Merge "mm, page_owner: don't grab zone->lock for init_pages_in_zone()"
diff --git a/.gitignore b/.gitignore
index c2ed4ec..d47ecbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,3 +114,6 @@
 
 # Kdevelop4
 *.kdev4
+
+# fetched Android config fragments
+kernel/configs/android-*.cfg
diff --git a/Documentation/devicetree/bindings/arm/arch_timer.txt b/Documentation/devicetree/bindings/arm/arch_timer.txt
index ad440a2..e926aea 100644
--- a/Documentation/devicetree/bindings/arm/arch_timer.txt
+++ b/Documentation/devicetree/bindings/arm/arch_timer.txt
@@ -31,6 +31,12 @@
   This also affects writes to the tval register, due to the implicit
   counter read.
 
+- hisilicon,erratum-161010101 : A boolean property. Indicates the
+  presence of Hisilicon erratum 161010101, which says that reading the
+  counters is unreliable in some cases, and reads may return a value 32
+  beyond the correct value. This also affects writes to the tval
+  registers, due to the implicit counter read.
+
 ** Optional properties:
 
 - arm,cpu-registers-not-fw-configured : Firmware does not initialize
diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt
index d4a352b..18386ab 100644
--- a/Documentation/devicetree/bindings/arm/coresight.txt
+++ b/Documentation/devicetree/bindings/arm/coresight.txt
@@ -39,6 +39,8 @@
 
 		- System Trace Macrocell:
 			"arm,coresight-stm", "arm,primecell"; [1]
+		- Trigger Generation Unit:
+			 "arm,primecell";
 
 	* reg: physical base address and length of the register
 	  set(s) of the component.
@@ -86,6 +88,16 @@
 
 	* qcom,dummy-sink: Configure the device as sink.
 
+* Additional required property for coresight-tgu devices:
+	* tgu-steps: must be present. Indicates number of steps supported
+	  by the TGU.
+	* tgu-conditions: must be present. Indicates the number of conditions
+	  supported by the TGU.
+	* tgu-regs: must be present. Indicates the number of regs supported
+	  by the TGU.
+	* tgu-timer-counters: must be present. Indicates the number of timers and
+	  counters available in the TGU to do a comparision.
+
 * Optional properties for all components:
 	* reg-names: names corresponding to each reg property value.
 
@@ -388,7 +400,7 @@
 		};
 	};
 
-4. CTIs
+5. CTIs
 	cti0: cti@6010000 {
 		compatible = "arm,coresight-cti", "arm,primecell";
 		reg = <0x6010000 0x1000>;
@@ -400,5 +412,21 @@
 		clock-names = "apb_pclk";
 	};
 
+6. TGUs
+	ipcb_tgu: tgu@6b0c000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b999>;
+		reg = <0x06B0C000 0x1000>;
+		reg-names = "tgu-base";
+		tgu-steps = <3>;
+		tgu-conditions = <4>;
+		tgu-regs = <4>;
+		tgu-timer-counters = <8>;
+
+		coresight-name = "coresight-tgu-ipcb";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
 [1]. There is currently two version of STM: STM32 and STM500.  Both
 have the same HW interface and as such don't need an explicit binding name.
diff --git a/Documentation/devicetree/bindings/arm/davinci.txt b/Documentation/devicetree/bindings/arm/davinci.txt
index f0841ce..715622c 100644
--- a/Documentation/devicetree/bindings/arm/davinci.txt
+++ b/Documentation/devicetree/bindings/arm/davinci.txt
@@ -13,6 +13,10 @@
 Required root node properties:
     - compatible = "enbw,cmc", "ti,da850;
 
+LEGO MINDSTORMS EV3 (AM1808 based)
+Required root node properties:
+    - compatible = "lego,ev3", "ti,da850";
+
 Generic DaVinci Boards
 ----------------------
 
diff --git a/Documentation/devicetree/bindings/arm/msm/glink_smem_native_xprt.txt b/Documentation/devicetree/bindings/arm/msm/glink_smem_native_xprt.txt
index f68c8e4..7011d5c 100644
--- a/Documentation/devicetree/bindings/arm/msm/glink_smem_native_xprt.txt
+++ b/Documentation/devicetree/bindings/arm/msm/glink_smem_native_xprt.txt
@@ -11,6 +11,7 @@
 -label : the name of the subsystem this link connects to
 
 Optional properties:
+-cpu-affinity: Cores to pin the interrupt and receiving work thread to.
 -qcom,qos-config: Reference to the qos configuration elements.It depends on
 		ramp-time.
 -qcom,ramp-time: Worst case time in microseconds to transition to this power
@@ -36,6 +37,7 @@
 		qcom,irq-mask = <0x1000>;
 		interrupts = <0 25 1>;
 		label = "lpass";
+		cpu-affinity = <1 2>;
 		qcom,qos-config = <&glink_qos_adsp>;
 		qcom,ramp-time = <0x10>,
 				     <0x20>,
diff --git a/Documentation/devicetree/bindings/arm/msm/imem.txt b/Documentation/devicetree/bindings/arm/msm/imem.txt
index 654f26e..440628d 100644
--- a/Documentation/devicetree/bindings/arm/msm/imem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/imem.txt
@@ -46,6 +46,12 @@
 -compatible: "qcom,msm-imem-restart_reason
 -reg: start address and size of restart_reason region in imem
 
+Download Mode Type:
+-------------------
+Required properties:
+-compatible: "qcom,msm-imem-dload-type"
+-reg: start address and size of dload type region in imem
+
 Download Mode:
 --------------
 Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
index c757233..55d06b2 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -77,6 +77,8 @@
 	- qcom,pm-cpu-levels: The different low power modes that a CPU could
 	enter. The following section explains the required properties of this
 	node.
+	-qcom,use-prediction: This optional property is used to indicate the
+	the LPM governor is to apply sleep prediction to this cluster.
 
 [Node bindings for qcom,pm-cpu-levels]
  Required properties:
diff --git a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
index a6537eb..105dcac 100644
--- a/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
+++ b/Documentation/devicetree/bindings/arm/msm/mdm-modem.txt
@@ -110,6 +110,10 @@
 			   on behalf of the subsystem driver.
 - qcom,mdm-link-info: a string indicating additional info about the physical link.
 			For example: "devID_domain.bus.slot" in case of PCIe.
+- qcom,mdm-auto-boot: Boolean. To indicate this instance of esoc boots independently.
+- qcom,mdm-statusline-not-a-powersource: Boolean. If set, status line to esoc device is not a
+		power source.
+- qcom,mdm-userspace-handle-shutdown: Boolean. If set, userspace handles shutdown requests.
 
 Example:
 	mdm0: qcom,mdm0 {
diff --git a/Documentation/devicetree/bindings/arm/msm/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index bc82bdc..b3d4d44 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -92,6 +92,12 @@
 - SDM670
   compatible = "qcom,sdm670"
 
+- QCS605
+  compatible = "qcom,qcs605"
+
+- SDA670
+  compatible = "qcom,sda670"
+
 - MSM8952
   compatible = "qcom,msm8952"
 
@@ -101,6 +107,9 @@
 - MSM8953
   compatible = "qcom,msm8953"
 
+- SDM450
+  compatible = "qcom,sdm450"
+
 - MSM8937
   compatible = "qcom,msm8937"
 
@@ -160,6 +169,11 @@
 - VR device:
   compatible = "qcom,qvr"
 
+- HDK device:
+  compatible = "qcom,hdk"
+
+- IPC device:
+  compatible = "qcom,ipc"
 
 Boards (SoC type + board variant):
 
@@ -190,6 +204,7 @@
 compatible = "qcom,apq8017-mtp"
 compatible = "qcom,apq8053-cdp"
 compatible = "qcom,apq8053-mtp"
+compatible = "qcom,apq8053-ipc"
 compatible = "qcom,mdm9630-cdp"
 compatible = "qcom,mdm9630-mtp"
 compatible = "qcom,mdm9630-sim"
@@ -273,9 +288,15 @@
 compatible = "qcom,sda845-cdp"
 compatible = "qcom,sda845-mtp"
 compatible = "qcom,sda845-qrd"
+compatible = "qcom,sda845-hdk"
 compatible = "qcom,sdm670-rumi"
 compatible = "qcom,sdm670-cdp"
 compatible = "qcom,sdm670-mtp"
+compatible = "qcom,sdm670-qrd"
+compatible = "qcom,qcs605-cdp"
+compatible = "qcom,qcs605-mtp"
+compatible = "qcom,sda670-cdp"
+compatible = "qcom,sda670-mtp"
 compatible = "qcom,msm8952-rumi"
 compatible = "qcom,msm8952-sim"
 compatible = "qcom,msm8952-qrd"
@@ -294,8 +315,12 @@
 compatible = "qcom,msm8953-sim"
 compatible = "qcom,msm8953-cdp"
 compatible = "qcom,msm8953-mtp"
+compatible = "qcom,msm8953-ipc"
 compatible = "qcom,msm8953-qrd"
 compatible = "qcom,msm8953-qrd-sku3"
+compatible = "qcom,sdm450-mtp"
+compatible = "qcom,sdm450-cdp"
+compatible = "qcom,sdm450-qrd"
 compatible = "qcom,mdm9640-cdp"
 compatible = "qcom,mdm9640-mtp"
 compatible = "qcom,mdm9640-rumi"
@@ -311,3 +336,5 @@
 compatible = "qcom,apq8009-cdp"
 compatible = "qcom,apq8009-mtp"
 compatible = "qcom,sdxpoorwills-rumi"
+compatible = "qcom,sdxpoorwills-mtp"
+compatible = "qcom,sdxpoorwills-cdp"
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
index b3f3431..0d955ed 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm_bus.txt
@@ -139,6 +139,7 @@
 			the command DB driver.
 qcom,drv-id:		The DRV id associated with the RSC, used to differentiate
 			between RSCS owned by different execution environments.
+qcom,defer-init-qos:	Flag to force defer initial QoS configuration at probe time.
 
 
 Example:
diff --git a/Documentation/devicetree/bindings/arm/msm/msm_core.txt b/Documentation/devicetree/bindings/arm/msm/msm_core.txt
deleted file mode 100644
index f385915..0000000
--- a/Documentation/devicetree/bindings/arm/msm/msm_core.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-MSM Core Energy Aware driver
-
-The Energy Aware driver provides per core power and temperature
-information to the scheduler for it to make more power efficient
-scheduling decision.
-
-The required properties for the Energy-aware driver are:
-
-- compatible:    "qcom,apss-core-ea"
-- reg:           Physical address mapped to this device
-
-Required nodes:
-- ea@X: Parent node that has the sensor mapping for each cpu.
-                 This node's phandle is provided within cpu node
-                 to invoke/probe energy-aware only for available cpus.
-                 There should be one such node present for each cpu.
-
-Optional properties:
-- qcom,low-hyst-temp: Degrees C below which the power numbers
-                 need to be recomputed for the cores and reset
-                 the threshold. If this is not present, the default
-                 value is 10C.
-- qcom,high-hyst-temp: Degrees C above which the power numbers
-                 need to be recomputed for the cores and reset
-                 the threshold. If this property is not present,
-                 the default value is 5C.
-- qcom,polling-interval: Interval for which the power numbers
-                 need to be recomputed for the cores if there
-                 is no change in threshold. If this property is not
-                 present, the power is recalculated only on
-                 temperature threshold notifications.
--qcom,throttling-temp: Temperature threshold for cpu frequency mitigation.
-                 The value should be set same as the threshold temperature
-                 in thermal module - 5 C, such that there is a bandwidth to
-                 control the cores before frequency mitigation happens.
-
-[Second level nodes]
-Require properties to define per core characteristics:
-- sensor:  Sensor phandle to map a particular sensor to the core.
-                If this property is not present, then the core is assumed
-                to be at 40C for all the power estimations. No sensor
-                threshold is set. This phandle's compatible property is
-                "qcom,sensor-information". This driver relies on the
-                sensor-type and scaling-factor information provided in this
-                phandle.
-
-Example
-
-qcom,msm-core@0xfc4b0000 {
-	compatible = "qcom,apss-core-ea";
-	reg = <0xfc4b0000 0x1000>;
-	qcom,low-hyst-temp = <10>;
-	qcom,high-hyst-temp = <5>;
-	qcom,polling-interval = <50>;
-
-	ea0: ea0 {
-		sensor = <&sensor_information0>;
-	};
-
-	ea1: ea1 {
-		sensor = <&sensor_information1>;
-	};
-
-};
-
-CPU0: cpu@0 {
-	device_type = "cpu";
-	compatible = "arm,cortex-a53";
-	reg = <0x0>;
-	qcom,ea = <&ea0>;
-};
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
index 628b2aa..90bc368 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,llcc.txt
@@ -31,6 +31,9 @@
 4. LLCC AMON Driver:
 Keeps track of the data progress within the internal channels of LLCC.
 
+5. LLCC Performance Monitor
+Used to monitor the events of LLCC sub blocks.
+
 == llcc device ==
 
 Require Properties:
@@ -107,6 +110,10 @@
 			compatible = "qcom,llcc-amon";
 			qcom,fg-cnt = <0x7>;
 		};
+
+		qcom,llcc-perfmon {
+			compatible = "qcom,llcc-perfmon";
+		};
 	};
 
 == Client ==
diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index cc4c3cc..7496f4d 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -9,397 +9,44 @@
 - compatible
 	Usage:      required
 	Value type: <string>
-	Definition: must be "qcom,clk-cpu-osm" or "qcom,clk-cpu-osm-v2".
+	Definition: must be "qcom,clk-cpu-osm", "qcom,clk-cpu-osm-v2" or
+		    "qcom,clk-cpu-osm-sdm670".
 
 - reg
 	Usage:      required
 	Value type: <prop-encoded-array>
-	Definition: Addresses and sizes for the memory of the OSM controller,
-		    cluster PLL management, and APCS common register regions.
-		    Optionally, the address of the efuse registers used to
-		    determine the pwrcl or perfcl speed-bins and/or the ACD
-		    register space to initialize prior to enabling OSM.
+	Definition: Addresses and sizes for the memory of the OSM controller.
 
 - reg-names
 	Usage:      required
 	Value type: <stringlist>
 	Definition: Address names. Must be "osm_l3_base", "osm_pwrcl_base",
-		    "osm_perfcl_base", "l3_pll", "pwrcl_pll", "perfcl_pll",
-		    "l3_sequencer", "pwrcl_sequencer", or "perfcl_sequencer".
-		    Optionally, "l3_efuse", "pwrcl_efuse", "perfcl_efuse",
-		    "pwrcl_acd", "perfcl_acd", "l3_acd".
+		    "osm_perfcl_base", and "cpr_rc".
 		    Must be specified in the same order as the corresponding
 		    addresses are specified in the reg property.
 
-- vdd-l3-supply
+- vdd_l3_mx_ao-supply
 	Usage:      required
 	Value type: <phandle>
-	Definition: phandle of the underlying regulator device that manages
-		    the voltage supply of the L3 cluster.
+	Definition: Phandle to the MX active-only regulator device.
 
-- vdd-pwrcl-supply
+- vdd_pwrcl_mx_ao-supply
 	Usage:      required
 	Value type: <phandle>
-	Definition: phandle of the underlying regulator device that manages
-		    the voltage supply of the Power cluster.
+	Definition: Phandle to the MX active-only regulator device.
 
-- vdd-perfcl-supply
+- qcom,mx-turbo-freq
 	Usage:      required
+	Value type: <array>
+	Definition: List of frequencies for the 3 clock domains (following the
+		    order of L3, power, and performance clusters) that denote
+		    the lowest rate that requires a TURBO vote on the MX rail.
+
+- l3-devs
+	Usage: optional
 	Value type: <phandle>
-	Definition: phandle of the underlying regulator device that manages
-		    the voltage supply of the Performance cluster.
-
-- interrupts
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: OSM interrupt specifier.
-
-- interrupt-names
-	Usage:      required
-	Value type: <stringlist>
-	Definition: Interrupt names. this list must match up 1-to-1 with the
-		    interrupts specified in the 'interrupts' property.
-		    "pwrcl-irq" and "perfcl-irq" must be specified.
-
-- qcom,l3-speedbinX-v0
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the frequency in Hertz, frequency,
-		    PLL override data, ACC level, and virtual corner used
-		    by the OSM hardware for each supported DCVS setpoint
-		    of the L3 cluster.
-
-- qcom,pwrcl-speedbinX-v0
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the frequency in Hertz, frequency,
-		    PLL override data, ACC level, and virtual corner used
-		    by the OSM hardware for each supported DCVS setpoint
-		    of the Power cluster.
-
-- qcom,perfcl-speedbinX-v0
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the frequency in Hertz, frequency,
-		    PLL override data, ACC level and virtual corner used
-		    by the OSM hardware for each supported DCVS setpoint
-		    of the Performance cluster.
-
-- qcom,osm-no-tz
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates that there is no programming
-		    of the OSM hardware performed by the secure world.
-
-- qcom,osm-pll-setup
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates that the PLL setup sequence
-		    must be executed for each clock domain managed by the OSM
-		    controller.
-
-- qcom,up-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the DCVS up timer value in nanoseconds
-		    for each of the three clock domains managed by the OSM
-		    controller.
-
-- qcom,down-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the DCVS down timer value in nanoseconds
-		    for each of the three clock domains managed by the OSM
-		    controller.
-
-- qcom,pc-override-index
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the OSM performance index to be used
-		    when each cluster enters certain low power modes.
-
-- qcom,set-ret-inactive
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if domains in retention must
-		    be treated as inactive.
-
-- qcom,enable-llm-freq-vote
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if Limits hardware frequency
-		    votes must be honored by OSM.
-
-- qcom,llm-freq-up-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the LLM frequency up timer value in
-		    nanoseconds for each of the three clock domains managed by
-		    the OSM controller.
-
-- qcom,llm-freq-down-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the LLM frequency down timer value in
-		    nanoseconds for each of the three clock domains managed by
-		    the OSM controller.
-
-- qcom,enable-llm-volt-vote
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if Limits hardware voltage
-		    votes must be honored by OSM.
-
-- qcom,llm-volt-up-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the LLM voltage up timer value in
-		    nanoseconds for each of the three clock domains managed by
-		    the OSM controller.
-
-- qcom,llm-volt-down-timer
-	Usage:      optional
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the LLM voltage down timer value in
-		    nanoseconds for each of the three clock domains managed by
-		    the OSM controller.
-
-- qcom,cc-reads
-	Usage:      optional
-	Value type: <integer>
-	Definition: Defines the number of times the cycle counters must be
-		    read to determine the performance level of each clock
-		    domain.
-
-- qcom,l-val-base
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the register addresses of the L_VAL
-		    control register for each of the three clock domains
-		    managed by the OSM controller.
-
-- qcom,apcs-pll-user-ctl
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the register addresses of the PLL
-		    user control register for each of the three clock domains
-		    managed by the OSM controller.
-
-- qcom,perfcl-apcs-apm-threshold-voltage
-	Usage:      required
-	Value type: <u32>
-	Definition: Specifies the APM threshold voltage in microvolts.  If the
-		    VDD_APCC supply voltage is above or at this level, then the
-		    APM is switched to use VDD_APCC.  If VDD_APCC is below
-		    this level, then the APM is switched to use VDD_MX.
-
-- qcom,apm-mode-ctl
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the register addresses of the APM
-		    control register for each of the two clusters managed
-		    by the OSM controller.
-
-- qcom,apm-status-ctrl
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the register addresses of the APM
-		    controller status register for each of the three clock
-		    domains managed by the OSM controller.
-
-- qcom,perfcl-isense-addr
-	Usage:      required
-	Value type: <u32>
-	Definition: Contains the ISENSE register address.
-
-- qcom,l3-mem-acc-addr
-	Usage:      required if qcom,osm-no-tz is specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the addresses of the mem-acc
-		    configuration registers for the L3 cluster.
-		    The array must contain exactly three elements.
-
-- qcom,pwrcl-mem-acc-addr
-	Usage:      required if qcom,osm-no-tz is specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the addresses of the mem-acc
-		    configuration registers for the Power cluster.
-		    The array must contain exactly three elements.
-
-- qcom,perfcl-mem-acc-addr
-	Usage:      required if qcom,osm-no-tz is specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the addresses of the mem-acc
-		    configuration registers for the Performance cluster.
-		    The array must contain exactly three elements.
-
-- qcom,perfcl-apcs-mem-acc-threshold-voltage
-	Usage:      optional
-	Value type: <u32>
-	Definition: Specifies the highest MEM ACC threshold voltage in
-		    microvolts for the Performance cluster.  This voltage is
-		    used to determine which MEM ACC setting is used for the
-		    highest frequencies.  If specified, the voltage must match
-		    the MEM ACC threshold voltage specified for the
-		    corresponding CPRh device.
-
-- qcom,l3-memacc-level-vc-binX
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the NOM and TURBO VCs for the L3 clock
-		    on that BIN part.
-		    The array must contain exactly two elements.
-
-- qcom,pwrcl-memacc-level-vc-binX
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the NOM and TURBO VCs for the Power
-		    cluster clock on that BIN part.
-		    The array must contain exactly two elements.
-
-- qcom,perfcl-memacc-level-vc-binX
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the NOM and TURBO VCs for the
-		    Performance cluster clock on that BIN part.
-		    The array must contain exactly two elements.
-
-- qcom,apcs-cbc-addr
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the addresses of the APCS_CBC_ADDR
-		    registers for all three clock domains.
-
-- qcom,apcs-ramp-ctl-addr
-	Usage:      required
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the addresses of the APCS_RAMP_CTL_ADDR
-		    registers for all three clock domains.
-
-- qcom,red-fsm-en
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the reduction FSM
-		    should be enabled.
-
-- qcom,boost-fsm-en
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the boost FSM should
-		    be enabled.
-
-- qcom,safe-fsm-en
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the safe FSM should
-		    be enabled.
-
-- qcom,ps-fsm-en
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the PS FSM should be
-		    enabled.
-
-- qcom,droop-fsm-en
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the droop FSM should
-		    be enabled.
-
-- qcom,set-c3-active
-	Usage:	    optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the cores in C3 are to
-		    be treated as active for core count calculations.
-
-- qcom,set-c2-active
-	Usage:      optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if the cores in C2 are to
-		    be treated as active for core count calculations.
-
-- qcom,disable-cc-dvcs
-	Usage:	    optional
-	Value type: <empty>
-	Definition: Boolean flag which indicates if core count based DCVS is
-		    to be disabled.
-
-- qcom,apcs-pll-min-freq
-	Usage:	    required
-	Value type: <u32>
-	Definition: Contains the addresses of the RAILx_CLKDOMy_PLL_MIN_FREQ
-		    registers for the three clock domains.
-
-- qcom,acdtd-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values to program to the ACD
-		    Tunable-Length Delay register for the L3, power and
-		    performance clusters.
-
-- qcom,acdcr-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD control register
-		    for the L3, power and performance clusters.
-
-- qcom,acdsscr-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD Soft Start Control
-		    register for the L3, power and performance clusters.
-
-- qcom,acdextint0-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the initial values for the ACD
-		    external interface configuration register for the L3, power
-		    and performance clusters.
-
-- qcom,acdextint1-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the final values for the ACD
-		    external interface configuration register for the L3, power
-		    and performance clusters.
-
-- qcom,acdautoxfer-val
-	Usage:      required if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD auto transfer
-		    control register for the L3, power and performance clusters.
-
-- qcom,acdavg-init
-	Usage:      optional if pwrcl_acd, perfcl_acd or l3_acd registers are
-		    specified
-	Value type: <prop-encoded-array>
-	Definition: Array which defines if the AVG feature for ACD should be
-		    initialized for the L3, power and performance clusters.
-		    Valid values are 0 or 1.
-
-- qcom,acdavgcfg0-val
-	Usage:      required if qcom,acdavg-init is true for an ACD clock domain
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD AVG CFG0
-		    registers for the L3, power and performance clusters.
-
-- qcom,acdavgcfg1-val
-	Usage:      required if qcom,acdavg-init is true for an ACD clock domain
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD AVG CFG1
-		    registers for the L3, power and performance clusters.
-
-- qcom,acdavgcfg2-val
-	Usage:      required if qcom,acdavg-init is true for an ACD clock domain
-	Value type: <prop-encoded-array>
-	Definition: Array which defines the values for the ACD AVG CFG2
-		    registers for the L3, power and performance clusters.
+	Definition: List of phandles to devices that the OPP tables with the L3
+		    frequency and voltage mappings are loaded for.
 
 - clock-names
 	Usage:      required
@@ -417,155 +64,16 @@
 		reg = <0x17d41000 0x1400>,
 			<0x17d43000 0x1400>,
 			<0x17d45800 0x1400>,
-			<0x178d0000 0x1000>,
-			<0x178c0000 0x1000>,
-			<0x178b0000 0x1000>,
-			<0x17d42400 0x0c00>,
-			<0x17d44400 0x0c00>,
-			<0x17d46c00 0x0c00>,
-			<0x17930000 0x10000>,
-			<0x17920000 0x10000>,
-			<0x17910000 0x10000>;
+			<0x784248 0x4>;
 		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
-			"l3_pll", "pwrcl_pll", "perfcl_pll",
-			"l3_sequencer", "pwrcl_sequencer",
-			"perfcl_sequencer", "l3_acd", "pwrcl_acd",
-			"perfcl_acd";
+							"cpr_rc";
+		vdd_l3_mx_ao-supply = <&pm8998_s6_level_ao>;
+		vdd_pwrcl_mx_ao-supply = <&pm8998_s6_level_ao>;
 
-		/* ACD configurations for L3, Silver, and Gold clusters */
-		qcom,acdtd-val = <0x0000b411 0x0000b411 0x0000b411>;
-		qcom,acdcr-val = <0x002c5ffd 0x002c5ffd 0x002c5ffd>;
-		qcom,acdsscr-val = <0x00000901 0x00000901 0x00000901>;
-		qcom,acdextint0-val = <0x2cf9ae8 0x2cf9ae8 0x2cf9ae8>;
-		qcom,acdextint1-val = <0x2cf9afe 0x2cf9afe 0x2cf9afe>;
-		qcom,acdautoxfer-val = <0x00000015 0x00000015 0x00000015>;
-		qcom,acdavgcfg2-val = <0x0 0x56a38822 0x56a38822>;
-		qcom,acdavgcfg1-val = <0x0 0x27104e20 0x27104e20>;
-		qcom,acdavgcfg0-val = <0x0 0xa08007a1 0xa08007a1>;
-		qcom,acdavg-init = <0 1 1>;
-
-		vdd-l3-supply = <&apc0_l3_vreg>;
-		vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
-		vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
-		qcom,l3-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x501c0422 0x00002020 0x1 5 >,
-			<   729600000 0x501c0526 0x00002020 0x1 6 >,
-			<   806400000 0x501c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072b 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x2 9 >;
-
-		qcom,pwrcl-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x501c0422 0x00002020 0x1 5 >,
-			<   748800000 0x501c0527 0x00002020 0x1 6 >,
-			<   825600000 0x401c062b 0x00002222 0x1 7 >,
-			<   902400000 0x4024072f 0x00002626 0x1 8 >,
-			<   979200000 0x40240833 0x00002929 0x1 9 >,
-			<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-			<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-			<  1209600000 0x402c0b3f 0x00003333 0x1 12 >,
-			<  1286400000 0x40340c43 0x00003636 0x1 13 >,
-			<  1363200000 0x40340d47 0x00003939 0x1 14 >,
-			<  1440000000 0x403c0e4b 0x00003c3c 0x1 15 >,
-			<  1516800000 0x403c0f4f 0x00004040 0x2 16 >,
-			<  1593600000 0x403c1053 0x00004343 0x2 17 >;
-
-		qcom,perfcl-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x501c0422 0x00002020 0x1 5 >,
-			<   729600000 0x501c0526 0x00002020 0x1 6 >,
-			<   806400000 0x501c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072b 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
-			<  1190400000 0x402c0b3e 0x00003232 0x1 12 >,
-			<  1267200000 0x40340c42 0x00003535 0x1 13 >,
-			<  1344000000 0x40340d46 0x00003838 0x1 14 >,
-			<  1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
-			<  1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
-			<  1574400000 0x403c1052 0x00004242 0x2 17 >,
-			<  1651200000 0x403c1156 0x00004545 0x2 18 >,
-			<  1728000000 0x4044125a 0x00004848 0x2 19 >,
-			<  1804800000 0x4044135e 0x00004b4b 0x2 20 >,
-			<  1881600000 0x404c1462 0x00004e4e 0x2 21 >,
-			<  1958400000 0x404c1566 0x00005252 0x3 22 >;
-
-		qcom,l3-memacc-level-vc-bin0 = <7 63>;
-		qcom,l3-memacc-level-vc-bin1 = <7 9>;
-		qcom,l3-memacc-level-vc-bin2 = <7 9>;
-
-		qcom,pwrcl-memacc-level-vc-bin0 = <12 63>;
-		qcom,pwrcl-memacc-level-vc-bin1 = <12 17>;
-		qcom,pwrcl-memacc-level-vc-bin2 = <12 17>;
-
-		qcom,perfcl-memacc-level-vc-bin0 = <12 18>;
-		qcom,perfcl-memacc-level-vc-bin1 = <12 18>;
-		qcom,perfcl-memacc-level-vc-bin2 = <12 18>;
-
-		qcom,up-timer =
-			<1000 1000 1000>;
-		qcom,down-timer =
-			<100000 100000 100000>;
-		qcom,pc-override-index =
-			<0 0 0>;
-		qcom,set-ret-inactive;
-		qcom,enable-llm-freq-vote;
-		qcom,llm-freq-up-timer =
-			<1000 1000 1000>;
-		qcom,llm-freq-down-timer =
-			<327675 327675 327675>;
-		qcom,enable-llm-volt-vote;
-		qcom,llm-volt-up-timer =
-			<1000 1000 1000>;
-		qcom,llm-volt-down-timer =
-			<327675 327675 327675>;
-		qcom,cc-reads = <10>;
-		qcom,cc-delay = <5>;
-		qcom,cc-factor = <100>;
-		qcom,osm-clk-rate = <100000000>;
-		qcom,xo-clk-rate = <19200000>;
-
-		qcom,l-val-base =
-			<0x178d0004 0x178c0004 0x178b0004>;
-		qcom,apcs-pll-user-ctl =
-			<0x178d000c 0x178c000c 0x178b000c>;
-		qcom,apcs-pll-min-freq =
-			<0x17d41094 0x17d43094 0x17d45894>;
-		qcom,apm-mode-ctl =
-			<0x0 0x0 0x17d20010>;
-		qcom,apm-status-ctrl =
-			<0x0 0x0 0x17d20000>;
-		qcom,perfcl-isense-addr = <0x17871480>;
-		qcom,l3-mem-acc-addr = <0x17990170 0x17990170 0x17990170>;
-		qcom,pwrcl-mem-acc-addr = <0x17990160 0x17990164 0x17990164>;
-		qcom,perfcl-mem-acc-addr = <0x17990168 0x1799016c 0x1799016c>;
-		qcom,cfg-gfmux-addr =<0x178d0084 0x178c0084 0x178b0084>;
-		qcom,apcs-cbc-addr = <0x178d008c 0x178c008c 0x178b008c>;
-		qcom,apcs-ramp-ctl-addr = <0x17840904 0x17840904 0x17830904>;
-
-		qcom,perfcl-apcs-apm-threshold-voltage = <800000>;
-		qcom,perfcl-apcs-mem-acc-threshold-voltage = <852000>;
-		qcom,boost-fsm-en;
-		qcom,safe-fsm-en;
-		qcom,ps-fsm-en;
-		qcom,droop-fsm-en;
-		qcom,osm-no-tz;
-		qcom,osm-pll-setup;
+		qcom,mx-turbo-freq = <1478400000 1689600000 3300000001>;
+		l3-devs = <&l3_cpu0 &l3_cpu4 &l3_cdsp>;
 
 		clock-names = "xo_ao";
 		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
 		#clock-cells = <1>;
-		#reset-cells = <1>;
 	};
diff --git a/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt b/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt
new file mode 100644
index 0000000..928a4f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/qdss_mhi.txt
@@ -0,0 +1,15 @@
+Qualcomm Technologies, Inc. QDSS bridge Driver
+
+This device will enable routing debug data from modem
+subsystem to APSS host.
+
+Required properties:
+-compatible : "qcom,qdss-mhi".
+-qcom,mhi : phandle of MHI Device to connect to.
+
+Example:
+	qcom,qdss-mhi {
+		compatible = "qcom,qdss-mhi";
+		qcom,mhi = <&mhi_0>;
+	};
+
diff --git a/Documentation/devicetree/bindings/batterydata/batterydata.txt b/Documentation/devicetree/bindings/batterydata/batterydata.txt
new file mode 100644
index 0000000..884b19c
--- /dev/null
+++ b/Documentation/devicetree/bindings/batterydata/batterydata.txt
@@ -0,0 +1,268 @@
+Battery Profile Data
+
+Battery Data is a collection of battery profile data made available to
+the QPNP Charger and BMS drivers via device tree.
+
+qcom,battery-data node required properties:
+- qcom,rpull-up-kohm : The vadc pullup resistor's resistance value in kOhms.
+- qcom,vref-batt-therm-uv : The vadc voltage used to make readings.
+			For Qualcomm Technologies, Inc. VADCs, this should be
+			1800000uV.
+
+qcom,battery-data node optional properties:
+- qcom,batt-id-range-pct : The area of variation between upper and lower bound
+			for which a given battery ID resistance is valid. This
+			value is expressed as a percentage of the specified kohm
+			resistance provided by qcom,batt-id-kohm.
+
+qcom,battery-data can also include any number of children nodes. These children
+nodes will be treated as battery profile data nodes.
+
+Profile data node required properties:
+- qcom,fcc-mah : Full charge count of the battery in milliamp-hours
+- qcom,default-rbatt-mohm : The nominal battery resistance value
+- qcom,rbatt-capacitive-mohm : The capacitive resistance of the battery.
+- qcom,flat-ocv-threshold-uv : The threshold under which the battery can be
+			considered to be in the flat portion of the discharge
+			curve.
+- qcom,max-voltage-uv : The maximum rated voltage of the battery
+- qcom,v-cutoff-uv : The cutoff voltage of the battery at which the device
+			should shutdown gracefully.
+- qcom,chg-term-ua : The termination charging current of the battery.
+- qcom,batt-id-kohm : The battery id resistance of the battery. It can be
+			used as an array which could support multiple IDs for one battery
+			module when the ID resistance of some battery modules goes across
+			several ranges.
+- qcom,battery-type : A string indicating the type of battery.
+- qcom,fg-profile-data : An array of hexadecimal values used to configure more
+			complex fuel gauge peripherals which have a large amount
+			of coefficients used in hardware state machines and thus
+			influencing the final output of the state of charge read
+			by software.
+
+Profile data node optional properties:
+- qcom,chg-rslow-comp-c1 : A constant for rslow compensation in the fuel gauge.
+			This will be provided by the profiling tool for
+			additional fuel gauge accuracy during charging.
+- qcom,chg-rslow-comp-c2 : A constant for rslow compensation in the fuel gauge.
+			This will be provided by the profiling tool for
+			additional fuel gauge accuracy during charging.
+- qcom,chg-rslow-comp-thr : A constant for rslow compensation in the fuel gauge.
+			This will be provided by the profiling tool for
+			additional fuel gauge accuracy during charging.
+- qcom,chg-rs-to-rslow: A constant for rslow compensation in the fuel gauge.
+			This will be provided by the profiling tool for
+			additional fuel gauge accuracy during charging.
+- qcom,fastchg-current-ma: Specifies the maximum fastcharge current.
+- qcom,fg-cc-cv-threshold-mv: Voltage threshold in mV for transition from constant
+			charge (CC) to constant voltage (CV). This value should
+			be 10 mV less than the float voltage.
+			This property should only be specified if
+			"qcom,autoadjust-vfloat" property is specified in the
+			charger driver to ensure a proper operation.
+- qcom,thermal-coefficients: Byte array of thermal coefficients for reading
+			battery thermistor. This should be exactly 6 bytes
+			in length.
+			Example: [01 02 03 04 05 06]
+- qcom,soc-based-step-chg: A bool property to indicate if the battery will
+			perform SoC (State of Charge) based step charging.
+			If yes, the low and high thresholds defined in
+			"qcom,step-chg-ranges" tuples should be assigned as
+			SoC values in percentage.
+- qcom,step-chg-ranges: Array of tuples in which a tuple describes a range
+			data of step charging setting.
+			A range contains following 3 integer elements:
+			[0]: the low threshold of battery votlage in uV
+			     or SoC (State of Charge) in percentage when
+			     SoC based step charge is used;
+			[1]: the high threshold of battery voltage in uV
+			     or SoC in percentage when SoC based step charge
+			     is used;
+			[2]: the FCC (full charging current) in uA when battery
+			     voltage or SoC falls between the low and high
+			     thresholds.
+			The threshold values in range should be in ascending
+			and shouldn't overlap. It support 8 ranges at max.
+- qcom,jeita-fcc-ranges: Array of tuples in which a tuple describes a range
+			data of sw-jeita FCC (full charging current) setting.
+			A range contains following 3 integer elements:
+			[0]: the low threshold of battery temperature in deci-degree;
+			[1]: the high threshold of battery temperature in deci-degree;
+			[2]: the FCC in uA when battery temperature falls between
+			     the low and high thresholds.
+			The threshold values in range should be in ascending
+			and shouldn't overlap. It support 8 ranges at max.
+- qcom,jeita-fv-ranges: Array of tuples in which a tuple describes a range
+			data of sw-jeita FV (float voltage) setting.
+			A range contains following 3 integer elements:
+			[0]: the low threshold of battery temperature in deci-degree;
+			[1]: the high threshold of battery temperature in deci-degree;
+			[3]: the FV in uV when battery temperature falls between
+			     the low and high thresholds.
+			The threshold values in range should be in ascending
+			and shouldn't overlap. It support 8 ranges at max.
+
+Profile data node required subnodes:
+- qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes
+			temperature to fcc lookup. The units for this lookup
+			table should be degrees celsius to milliamp-hours.
+- qcom,pc-temp-ocv-lut : A 2-dimensional lookup table node that encodes
+			temperature and percent charge to open circuit voltage
+			lookup. The units for this lookup table should be
+			degrees celsius and percent to millivolts.
+- qcom,rbatt-sf-lut : A 2-dimentional lookup table node that encodes
+			temperature and percent charge to battery internal
+			resistance lookup. The units for this lookup table
+			should be degrees celsius and percent to milliohms.
+
+Profile data node optional subnodes:
+- qcom,ibat-acc-luit: A 2-dimentional lookup table that encodes temperature
+			and battery current to battery ACC (apparent charge
+			capacity). The units for this lookup table should be
+			temperature in degrees celsius, ibat in milli-amps
+			and ACC in milli-ampere-hour.
+
+Lookup table required properties:
+- qcom,lut-col-legend : An array that encodes the legend of the lookup table's
+			columns. The length of this array will determine the
+			lookup table's width.
+- qcom,lut-data : An array that encodes the lookup table's data. The size of this
+			array should be equal to the size of qcom,lut-col-legend
+			multiplied by 1 if it's a 1-dimensional table, or
+			the size of qcom,lut-row-legend if it's a 2-dimensional
+			table. The data should be in a flattened row-major
+			representation.
+
+Lookup table optional properties:
+- qcom,lut-row-legend : An array that encodes the legend of the lookup table's rows.
+			If this property exists, then it is assumed that the
+			lookup table is a 2-dimensional table.
+
+Example:
+
+In msm8974-mtp.dtsi:
+
+mtp_batterydata: qcom,battery-data {
+	qcom,rpull-up-kohm = <100>;
+	qcom,vref-batt-therm-uv = <1800000>;
+
+	/include/ "batterydata-palladium.dtsi"
+	/include/ "batterydata-mtp-3000mah.dtsi"
+};
+
+&pm8941_bms {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
+In batterydata-palladium.dtsi:
+
+qcom,palladium-batterydata {
+	qcom,fcc-mah = <1500>;
+	qcom,default-rbatt-mohm = <236>;
+	qcom,rbatt-capacitive-mohm = <50>;
+	qcom,flat-ocv-threshold-uv = <3800000>;
+	qcom,max-voltage-uv = <4200000>;
+	qcom,v-cutoff-uv = <3400000>;
+	qcom,chg-term-ua = <100000>;
+	qcom,batt-id-kohm = <75>;
+	qcom,step-chg-ranges = <3600000 4000000 3000000
+				4001000 4200000 2800000
+				4201000 4400000 2000000>;
+	qcom,jeita-fcc-ranges = <0      100     600000
+				 101    200     2000000
+				 201    450     3000000
+				 451    550     600000>;
+	qcom,jeita-fv-ranges = <0      100     4200000
+				101    450     4350000
+				451    550     4200000>;
+	qcom,battery-type = "palladium_1500mah";
+
+	qcom,fcc-temp-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 65>;
+		qcom,lut-data = <1492 1492 1493 1483 1502>;
+	};
+
+	qcom,pc-temp-ocv-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 65>;
+		qcom,lut-row-legend = <100 95 90 85 80 75 70>,
+				<65 60 55 50 45 40 35>,
+				<30 25 20 15 10 9 8>,
+				<7 6 5 4 3 2 1 0>;
+		qcom,lut-data = <4173 4167 4163 4156 4154>,
+			<4104 4107 4108 4102 4104>,
+			<4057 4072 4069 4061 4060>,
+			<3973 4009 4019 4016 4020>,
+			<3932 3959 3981 3982 3983>,
+			<3899 3928 3954 3950 3950>,
+			<3868 3895 3925 3921 3920>,
+			<3837 3866 3898 3894 3892>,
+			<3812 3841 3853 3856 3862>,
+			<3794 3818 3825 3823 3822>,
+			<3780 3799 3804 3804 3803>,
+			<3768 3787 3790 3788 3788>,
+			<3757 3779 3778 3775 3776>,
+			<3747 3772 3771 3766 3765>,
+			<3736 3763 3766 3760 3746>,
+			<3725 3749 3756 3747 3729>,
+			<3714 3718 3734 3724 3706>,
+			<3701 3703 3696 3689 3668>,
+			<3675 3695 3682 3675 3662>,
+			<3670 3691 3680 3673 3661>,
+			<3661 3686 3679 3672 3656>,
+			<3649 3680 3676 3669 3641>,
+			<3633 3669 3667 3655 3606>,
+			<3610 3647 3640 3620 3560>,
+			<3580 3607 3596 3572 3501>,
+			<3533 3548 3537 3512 3425>,
+			<3457 3468 3459 3429 3324>,
+			<3328 3348 3340 3297 3172>,
+			<3000 3000 3000 3000 3000>;
+	};
+
+	qcom,rbatt-sf-lut {
+		qcom,lut-col-legend = <(-20) 0 25 40 65>;
+		qcom,lut-row-legend = <100 95 90 85 80 75 70>,
+				<65 60 55 50 45 40 35>,
+				<30 25 20 15 10 9 8>,
+				<7 6 5 4 3 2 1 0>;
+		qcom,lut-data = <357 187 100 91 91>,
+			<400 208 105 94 94>,
+			<390 204 106 95 96>,
+			<391 201 108 98 98>,
+			<391 202 110 98 100>,
+			<390 200 110 99 102>,
+			<389 200 110 99 102>,
+			<393 202 101 93 100>,
+			<407 205 99 89 94>,
+			<428 208 100 91 96>,
+			<455 212 102 92 98>,
+			<495 220 104 93 101>,
+			<561 232 107 95 102>,
+			<634 245 112 98 98>,
+			<714 258 114 98 98>,
+			<791 266 114 97 100>,
+			<871 289 108 95 97>,
+			<973 340 124 108 105>,
+			<489 241 109 96 99>,
+			<511 246 110 96 99>,
+			<534 252 111 95 98>,
+			<579 263 112 96 96>,
+			<636 276 111 95 97>,
+			<730 294 109 96 99>,
+			<868 328 112 98 104>,
+			<1089 374 119 101 115>,
+			<1559 457 128 105 213>,
+			<12886 1026 637 422 3269>,
+			<170899 127211 98968 88907 77102>;
+	};
+
+	qcom,ibat-acc-lut {
+		qcom,lut-col-legend = <(-20) 0 25>;
+		qcom,lut-row-legend = <0 250 500 1000>;
+		qcom,lut-data = <1470 1470 1473>,
+				<1406 1406 1430>,
+				<1247 1247 1414>,
+				<764 764 1338>;
+	};
+};
+
diff --git a/Documentation/devicetree/bindings/clock/qcom,a7-cpucc.txt b/Documentation/devicetree/bindings/clock/qcom,a7-cpucc.txt
new file mode 100644
index 0000000..2782b9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/qcom,a7-cpucc.txt
@@ -0,0 +1,48 @@
+Qualcomm Application A7 CPU clock driver
+-------------------------------------
+
+It is the clock controller driver which provides higher frequency
+clocks and allows A7 CPU frequency scaling on sdxpoorwills based platforms.
+
+Required properties:
+- compatible : shall contain only one of the following:
+		"qcom,cpu-sdxpoorwills",
+- clocks : Phandle to the clock device.
+- clock-names: Names of the used clocks.
+- qcom,a7cc-init-rate = Initial rate which needs to be set from cpu driver.
+- reg : shall contain base register offset and size.
+- reg-names : Names of the bases for the above registers.
+- vdd_dig_ao-supply : The regulator powering the APSS PLL.
+- cpu-vdd-supply : The regulator powering the APSS RCG.
+- qcom,rcg-reg-offset : Register offset for APSS RCG.
+- qcom,speedX-bin-vZ :	A table of CPU frequency (Hz) to regulator voltage (uV) mapping.
+			Format: <freq uV>
+			This represents the max frequency possible for each possible
+			power configuration for a CPU that's binned as speed bin X,
+			speed bin revision Z. Speed bin values can be between [0-7]
+			and the version can be between [0-3].
+- #clock-cells : shall contain 1.
+
+Optional properties :
+- reg-names: "efuse",
+
+Example:
+	clock_cpu: qcom,clock-a7@17808100 {
+		compatible = "qcom,cpu-sdxpoorwills";
+		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
+		clock-names = "xo_ao";
+		qcom,a7cc-init-rate = <1497600000>;
+		reg = <0x17808100 0x7F10>;
+		reg-names = "apcs_pll";
+
+		vdd_dig_ao-supply = <&pmxpoorwills_s5_level_ao>;
+		cpu-vdd-supply = <&pmxpoorwills_s5_level_ao>;
+		qcom,rcg-reg-offset = <0x7F08>;
+		qcom,speed0-bin-v0 =
+			<          0 RPMH_REGULATOR_LEVEL_OFF>,
+			<  345600000 RPMH_REGULATOR_LEVEL_LOW_SVS>,
+			<  576000000 RPMH_REGULATOR_LEVEL_SVS>,
+			< 1094400000 RPMH_REGULATOR_LEVEL_NOM>,
+			< 1497600000 RPMH_REGULATOR_LEVEL_TURBO>;
+		#clock-cells = <1>;
+	};
diff --git a/Documentation/devicetree/bindings/clock/qcom,camcc.txt b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
index 313e50f..daf8a539 100644
--- a/Documentation/devicetree/bindings/clock/qcom,camcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,camcc.txt
@@ -2,7 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,cam_cc-sdm845" or "qcom,cam_cc-sdm845-v2"
+- compatible : shall contain "qcom,cam_cc-sdm845", "qcom,cam_cc-sdm845-v2" or
+	       "qcom,cam_cc-sdm670"
 - reg : shall contain base register location and length
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
index 87af0f6..d169c31 100644
--- a/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,dispcc.txt
@@ -2,7 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,dispcc-sdm845" or "qcom,dispcc-sdm845-v2".
+- compatible : shall contain "qcom,dispcc-sdm845", "qcom,dispcc-sdm845-v2" or
+	       "qcom,dispcc-sdm670".
 - reg : shall contain base register location and length.
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index c280b92..7330db4 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -18,7 +18,10 @@
 			"qcom,gcc-mdm9615"
 			"qcom,gcc-sdm845"
 			"qcom,gcc-sdm845-v2"
+			"qcom,gcc-sdm845-v2.1"
+			"qcom,gcc-sdm670"
 			"qcom,debugcc-sdm845"
+			"qcom,gcc-sdxpoorwills"
 
 - reg : shall contain base register location and length
 - #clock-cells : shall contain 1
diff --git a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
index 12676b7..aa90bc4 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gpucc.txt
@@ -7,6 +7,8 @@
 		"qcom,gpucc-sdm845-v2",
 		"qcom,gfxcc-sdm845",
 		"qcom,gfxcc-sdm845-v2"
+		"qcom,gpucc-sdm670",
+		"qcom,gfxcc-sdm670"
 
 - reg : shall contain base register offset and size.
 - #clock-cells : shall contain 1.
diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
index c81a454..d57f61a 100644
--- a/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,rpmh.txt
@@ -2,7 +2,9 @@
 -------------------------------------------------------
 
 Required properties :
-- compatible : must be "qcom,rpmh-clk-sdm845"
+- compatible : shall contain "qcom,rpmh-clk-sdm845" or "qcom,rpmh-clk-sdm670"
+	       or "qcom,rpmh-clk-sdxpoorwills"
+
 - #clock-cells : must contain 1
 - mboxes : list of RPMh mailbox phandle and channel identifier tuples.
 - mbox-names : list of names to identify the RPMh mailboxes used.
diff --git a/Documentation/devicetree/bindings/clock/qcom,videocc.txt b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
index 6bd0f0b..9b53c65 100644
--- a/Documentation/devicetree/bindings/clock/qcom,videocc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,videocc.txt
@@ -2,8 +2,8 @@
 ----------------------------------------------------
 
 Required properties :
-- compatible : shall contain "qcom,video_cc-sdm845" or
-	       "qcom,video_cc-sdm845-v2".
+- compatible : shall contain "qcom,video_cc-sdm845", "qcom,video_cc-sdm845-v2"
+	       or "qcom,video_cc-sdm670".
 - reg : shall contain base register location and length.
 - reg-names: names of registers listed in the same order as in
 	     the reg property.
diff --git a/Documentation/devicetree/bindings/clock/qoriq-clock.txt b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
index 16a3ec4..1bd2c76 100644
--- a/Documentation/devicetree/bindings/clock/qoriq-clock.txt
+++ b/Documentation/devicetree/bindings/clock/qoriq-clock.txt
@@ -31,6 +31,7 @@
 	* "fsl,t4240-clockgen"
 	* "fsl,b4420-clockgen"
 	* "fsl,b4860-clockgen"
+	* "fsl,ls1012a-clockgen"
 	* "fsl,ls1021a-clockgen"
 	Chassis-version clock strings include:
 	* "fsl,qoriq-clockgen-1.0": for chassis 1.0 clocks
diff --git a/Documentation/devicetree/bindings/crypto/msm/ice.txt b/Documentation/devicetree/bindings/crypto/msm/ice.txt
index 2d0e580..fe8671f 100644
--- a/Documentation/devicetree/bindings/crypto/msm/ice.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/ice.txt
@@ -5,16 +5,22 @@
   - reg : <register mapping>
 
 Optional properties:
-  - interrupt-names     : name describing the interrupts for ICE IRQ
-  - interrupts          : <interrupt mapping for ICE IRQ>
-  - qcom,enable-ice-clk : should enable clocks for ICE HW
-  - clocks              : List of phandle and clock specifier pairs
-  - clock-names         : List of clock input name strings sorted in the same
-                          order as the clocks property.
-  - qocm,op-freq-hz     : max clock speed sorted in the same order as the clocks
-                          property.
-  - qcom,instance-type  : describe the storage type for which ICE node is defined
-			  currently, only "ufs" and "sdcc" are supported storage type
+  - interrupt-names     	: name describing the interrupts for ICE IRQ
+  - interrupts          	: <interrupt mapping for ICE IRQ>
+  - qcom,enable-ice-clk 	: should enable clocks for ICE HW
+  - clocks              	: List of phandle and clock specifier pairs
+  - clock-names         	: List of clock input name strings sorted in the same
+                          	  order as the clocks property.
+  - qocm,op-freq-hz     	: max clock speed sorted in the same order as the clocks
+                          	  property.
+  - qcom,instance-type  	: describe the storage type for which ICE node is defined
+			 	  currently, only "ufs" and "sdcc" are supported storage type
+  - vdd-hba-supply		: regulated supply to be used by ICE HW
+  - qcom,msm-bus,name		: bus for ICE transactions
+  - qcom,msm-bus,num-cases	: bus case mapping for ICE HW
+  - qcom,msm-bus,num-paths	: bus path mapping for iCE HW
+  - qcom,msm-bus,vectors-KBps	: bus bandwidth to be voted
+  - qcom,bus-vector-names	: bus vectors mapping
 
 Example:
         ufs_ice: ufsice@630000 {
@@ -30,3 +36,26 @@
 		qcom,instance-type = "ufs";
                 status = "disabled";
         };
+
+	ufs_card_ice: ufscardice@1db0000 {
+		compatible = "qcom,ice_card";
+		reg = <0x1db0000 0x8000>;
+		qcom,enable-ice-clk;
+		clock-names = "ufs_core_clk", "bus_clk",
+			      "iface_clk", "ice_core_clk";
+		clocks = <&clock_gcc GCC_UFS_CARD_AXI_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_CLKREF_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_AHB_CLK>,
+			 <&clock_gcc GCC_UFS_CARD_ICE_CORE_CLK>;
+		qcom,op-freq-hz = <0>, <0>, <0>, <300000000>;
+		vdd-hba-supply = <&ufs_card_gdsc>;
+		qcom,msm-bus,name = "ufs_card_ice_noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<1 650 0 0>,    /* No vote */
+				<1 650 1000 0>; /* Max. bandwidth */
+		qcom,bus-vector-names = "MIN",
+					"MAX";
+		qcom,instance-type = "ufs_card";
+	};
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
index 051b315..6f1d8e3 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcedev.txt
@@ -20,7 +20,7 @@
   - qcom,ce-hw-key : optional, indicates if the hardware supports use of HW KEY.
   - qcom,support-core-clk-only : optional, indicates if the HW supports single crypto core clk.
   - qcom,bsm-ee : optional, indicate the BAM EE value, changes from target to target. Default value is 1 if not specified.
-  - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+  - qcom,smmu-s1-enable : Boolean flag to enable SMMU stage 1 translation.
   - iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
 
 Example:
diff --git a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
index fa27198..231f31a 100644
--- a/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
+++ b/Documentation/devicetree/bindings/crypto/msm/qcrypto.txt
@@ -40,7 +40,7 @@
 	required. For other targets such as fsm, they do not perform
 	bus scaling. It is not required for those targets.
 
-  - qcom,smmu-s1-bypass : Boolean flag to bypass SMMU stage 1 translation.
+  - qcom,smmu-s1-enable : Boolean flag to bypass SMMU stage 1 translation.
   - iommus : A list of phandle and IOMMU specifier pairs that describe the IOMMU master interfaces of the device.
 
 Example:
diff --git a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
index 67dc991..3786412 100644
--- a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
+++ b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
@@ -4,7 +4,7 @@
 to measure the parameters for latency driven memory access patterns.
 
 Required properties:
-- compatible:			Must be "qcom,arm-memlat-mon"
+- compatible:			Must be "qcom,arm-memlat-mon" or "qcom,arm-cpu-mon"
 - qcom,cpulist:			List of CPU phandles to be monitored in a cluster
 - qcom,target-dev:		The DT device that corresponds to this master port
 - qcom,core-dev-table:		A mapping table of core frequency to a required bandwidth vote at the
@@ -15,6 +15,8 @@
 				Defaults to 0x17 if not specified.
 - qcom,inst-ev:			The instruction count event that this monitor is supposed to measure.
 				Defaults to 0x08 if not specified.
+- qcom,stall-cycle-ev:		The stall cycle count that this monitor is supposed to measure.
+				Assumes 100% stall if not specified.
 
 
 Example:
@@ -24,6 +26,7 @@
 		qcom,target-dev = <&memlat0>;
 		qcom,cachemiss-ev = <0x2A>;
 		qcom,inst-ev = <0x08>;
+		qcom,stall-cycle-ev = <0xE7>;
 		qcom,core-dev-table =
 			<  300000 1525>,
 			<  499200 3143>,
diff --git a/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt
new file mode 100644
index 0000000..6ec1a88
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ti,ths8135.txt
@@ -0,0 +1,46 @@
+THS8135 Video DAC
+-----------------
+
+This is the binding for Texas Instruments THS8135 Video DAC bridge.
+
+Required properties:
+
+- compatible: Must be "ti,ths8135"
+
+Required nodes:
+
+This device has two video ports. Their connections are modelled using the OF
+graph bindings specified in Documentation/devicetree/bindings/graph.txt.
+
+- Video port 0 for RGB input
+- Video port 1 for VGA output
+
+Example
+-------
+
+vga-bridge {
+	compatible = "ti,ths8135";
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+
+			vga_bridge_in: endpoint {
+				remote-endpoint = <&lcdc_out_vga>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+
+			vga_bridge_out: endpoint {
+				remote-endpoint = <&vga_con_in>;
+			};
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/display/msm/dsi.txt b/Documentation/devicetree/bindings/display/msm/dsi.txt
index 3534f04..fc95288 100644
--- a/Documentation/devicetree/bindings/display/msm/dsi.txt
+++ b/Documentation/devicetree/bindings/display/msm/dsi.txt
@@ -121,6 +121,12 @@
 					If ping pong split is enabled, this time should not be higher
 					than two times the dsi link rate time.
 					If the property is not specified, then the default value is 14000 us.
+- qcom,panel-allow-phy-poweroff:	A boolean property indicates that panel allows to turn off the phy power
+					supply during idle screen. A panel should be able to handle the dsi lanes
+					in floating state(not LP00 or LP11) to turn on this property. Software
+					turns off PHY pmic power supply, phy ldo and DSI Lane ldo during
+					idle screen (footswitch control off) when this property is enabled.
+- qcom,dsi-phy-regulator-min-datarate-bps:  Minimum per lane data rate (bps) to turn on PHY regulator.
 
 [1] Documentation/devicetree/bindings/clocks/clock-bindings.txt
 [2] Documentation/devicetree/bindings/graph.txt
@@ -229,4 +235,6 @@
 		vddio-supply = <&pma8084_l12>;
 
 		qcom,dsi-phy-regulator-ldo-mode;
+		qcom,panel-allow-phy-poweroff;
+		qcom,dsi-phy-regulator-min-datarate-bps = <1200000000>;
 	};
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index dd668cb..4b4c274 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -125,6 +125,11 @@
 				configuration value.
 - qcom,sde-ubwc-swizzle:	Property to specify the default UBWC swizzle
 				configuration value.
+- qcom,sde-smart-panel-align-mode: A u32 property to specify the align mode for
+				split display on smart panel. Possible values:
+				0x0 - no alignment
+				0xc - align at start of frame
+				0xd - align at start of line
 - qcom,sde-panic-per-pipe:	Boolean property to indicate if panic signal
 				control feature is available on each source pipe.
 - qcom,sde-has-src-split:	Boolean property to indicate if source split
@@ -135,6 +140,18 @@
 				power collapse feature available or not.
 - qcom,sde-has-mixer-gc:	Boolean property to indicate if mixer has gamma correction
 				feature available or not.
+- qcom,sde-has-dest-scaler: 	Boolean property to indicate if destination scaler
+				feature is available or not.
+- qcom,sde-max-dest-scaler-input-linewidth: A u32 value indicates the
+				maximum input line width to destination scaler.
+- qcom,sde-max-dest-scaler-output-linewidth: A u32 value indicates the
+				maximum output line width of destination scaler.
+- qcom,sde-dest-scaler-top-off: A u32 value provides the
+				offset from mdp base to destination scaler block.
+- qcom,sde-dest-scaler-top-size: A u32 value indicates the address range for ds top
+- qcom,sde-dest-scaler-off: 	Array of u32 offsets indicate the qseed3 scaler blocks
+				offset from destination scaler top offset.
+- qcom,sde-dest-scaler-size:    A u32 value indicates the address range for each scaler block
 - qcom,sde-sspp-clk-ctrl:	Array of offsets describing clk control
 				offsets for dynamic clock gating. 1st value
 				in the array represents offset of the control
@@ -281,6 +298,18 @@
 				core ib calculation.
 - qcom,sde-core-clk-ff:		A string entry indicating the fudge factor for
 				core clock calculation.
+- qcom,sde-min-core-ib-kbps:	This u32 value indicates the minimum mnoc ib
+				vote in Kbps that can be reduced without hitting underflow.
+				BW calculation logic will choose the IB bandwidth requirement
+				based on usecase if this floor value is not defined.
+- qcom,sde-min-llcc-ib-kbps:	This u32 value indicates the minimum llcc ib
+				vote in Kbps that can be reduced without hitting underflow.
+				BW calculation logic will choose the IB bandwidth requirement
+				based on usecase if this floor value is not defined.
+- qcom,sde-min-dram-ib-kbps:	This u32 value indicates the minimum dram ib
+				vote in Kbps that can be reduced without hitting underflow.
+				BW calculation logic will choose the IB bandwidth requirement
+				based on usecase if this floor value is not defined.
 - qcom,sde-comp-ratio-rt:	A string entry indicating the compression ratio
 				for each supported compressed format on realtime interface.
 				The string is composed of one or more of
@@ -307,9 +336,22 @@
 - qcom,sde-danger-lut:		A 4 cell property, with a format of <linear,
 				tile, nrt, cwb>,
 				indicating the danger luts on sspp.
-- qcom,sde-safe-lut:		A 4 cell property, with a format of <linear,
-				tile, nrt, cwb>,
-				indicating the safe luts on sspp.
+- qcom,sde-safe-lut-linear:	Array of 2 cell property, with a format of
+				<fill level, lut> in ascending fill level
+				indicating the safe luts for linear format on sspp.
+				Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-macrotile:	Array of 2 cell property, with a format of
+				<fill level, lut> in ascending fill level
+				indicating the safe luts for macrotile format on sspp.
+				Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-nrt:	Array of 2 cell property, with a format of
+				<fill level, lut> in ascending fill level
+				indicating the safe luts for nrt (e.g wfd) on sspp.
+				Zero fill level on the last entry identifies the default lut.
+- qcom,sde-safe-lut-cwb:	Array of 2 cell property, with a format of
+				<fill level, lut> in ascending fill level
+				indicating the safe luts for cwb on sspp.
+				Zero fill level on the last entry identifies the default lut.
 - qcom,sde-qos-lut-linear:	Array of 3 cell property, with a format of
 				<fill level, lut hi, lut lo> in ascending fill level
 				indicating the qos luts for linear format on sspp.
@@ -329,6 +371,8 @@
 - qcom,sde-cdp-setting:		Array of 2 cell property, with a format of
 				<read enable, write enable> for cdp use cases in
 				order of <real_time>, and <non_real_time>.
+- qcom,sde-qos-cpu-mask:	A u32 value indicating desired PM QoS CPU affine mask.
+- qcom,sde-qos-cpu-dma-latency:	A u32 value indicating desired PM QoS CPU DMA latency in usec.
 - qcom,sde-inline-rot-xin:	An integer array of xin-ids related to inline
 				rotation.
 - qcom,sde-inline-rot-xin-type:	A string array indicating the type of xin,
@@ -342,6 +386,15 @@
 				control register. Number of offsets defined should
 				match the number of xin-ids defined in
 				property: qcom,sde-inline-rot-xin
+- #power-domain-cells:		Number of cells in a power-domain specifier and should contain 0.
+- qcom,sde-mixer-display-pref:  A string array indicating the preferred display type
+				for the mixer block. Possible values:
+				"primary" - preferred for primary display
+				"none" - no preference on display
+- qcom,sde-ctl-display-pref:    A string array indicating the preferred display type
+                                for the ctl block. Possible values:
+				"primary" - preferred for primary display
+				"none" - no preference on display
 
 Bus Scaling Subnodes:
 - qcom,sde-reg-bus:		Property to provide Bus scaling for register access for
@@ -422,16 +475,23 @@
     interrupt-controller;
     #interrupt-cells = <1>;
     iommus = <&mdp_smmu 0>;
+    #power-domain-cells = <0>;
 
     qcom,sde-off = <0x1000>;
     qcom,sde-ctl-off = <0x00002000 0x00002200 0x00002400
 		     0x00002600 0x00002800>;
+    qcom,sde-ctl-display-pref = "primary", "none", "none",
+	             "none", "none";
     qcom,sde-mixer-off = <0x00045000 0x00046000
 			0x00047000 0x0004a000>;
+    qcom,sde-mixer-display-pref = "primary", "none",
+	                "none", "none";
     qcom,sde-dspp-top-off = <0x1300>;
     qcom,sde-dspp-off = <0x00055000 0x00057000>;
     qcom,sde-dspp-ad-off = <0x24000 0x22800>;
     qcom,sde-dspp-ad-version = <0x00030000>;
+    qcom,sde-dest-scaler-top-off = <0x00061000>;
+    qcom,sde-dest-scaler-off = <0x800 0x1000>;
     qcom,sde-wb-off = <0x00066000>;
     qcom,sde-wb-xin-id = <6>;
     qcom,sde-intf-off = <0x0006b000 0x0006b800
@@ -480,6 +540,7 @@
     qcom,sde-ubwc-version = <0x100>;
     qcom,sde-ubwc-static = <0x100>;
     qcom,sde-ubwc-swizzle = <0>;
+    qcom,sde-smart-panel-align-mode = <0xd>;
     qcom,sde-panic-per-pipe;
     qcom,sde-has-src-split;
     qcom,sde-has-dim-layer;
@@ -493,6 +554,8 @@
     qcom,sde-cdm-size = <0x100>;
     qcom,sde-pp-size = <0x100>;
     qcom,sde-wb-size = <0x100>;
+    qcom,sde-dest-scaler-top-size = <0xc>;
+    qcom,sde-dest-scaler-size = <0x800>;
     qcom,sde-len = <0x100>;
     qcom,sde-wb-linewidth = <2560>;
     qcom,sde-sspp-scale-size = <0x100>;
@@ -502,6 +565,9 @@
     qcom,sde-highest-bank-bit = <15>;
     qcom,sde-has-mixer-gc;
     qcom,sde-has-idle-pc;
+    qcom,sde-has-dest-scaler;
+    qcom,sde-max-dest-scaler-input-linewidth = <2048>;
+    qcom,sde-max-dest-scaler-output-linewidth = <2560>;
     qcom,sde-sspp-max-rects = <1 1 1 1
 				1 1 1 1
 				1 1
@@ -553,6 +619,9 @@
 
     qcom,sde-cdp-setting = <1 1>, <1 0>;
 
+    qcom,sde-qos-cpu-mask = <0x3>;
+    qcom,sde-qos-cpu-dma-latency = <300>;
+
     qcom,sde-vbif-off = <0 0>;
     qcom,sde-vbif-id = <0 1>;
     qcom,sde-vbif-default-ot-rd-limit = <32>;
@@ -572,6 +641,9 @@
 
     qcom,sde-core-ib-ff = "1.1";
     qcom,sde-core-clk-ff = "1.0";
+    qcom,sde-min-core-ib-kbps = <2400000>;
+    qcom,sde-min-llcc-ib-kbps = <800000>;
+    qcom,sde-min-dram-ib-kbps = <800000>;
     qcom,sde-comp-ratio-rt = "NV12/5/1/1.1 AB24/5/1/1.2 XB24/5/1/1.3";
     qcom,sde-comp-ratio-nrt = "NV12/5/1/1.1 AB24/5/1/1.2 XB24/5/1/1.3";
     qcom,sde-undersized-prefill-lines = <4>;
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
index 32c31af..806c458 100644
--- a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -340,13 +340,15 @@
 					"single_roi": default enable mode, only single roi is sent to panel
 					"dual_roi": two rois are merged into one big roi. Panel ddic should be able
 					to process two roi's along with the DCS command to send two rois.
-					disabled if property is not specified.
+					disabled if property is not specified. This property is specified
+					per timing node to support resolution restrictions.
 - qcom,mdss-dsi-horizontal-line-idle:	List of width ranges (EC - SC) in pixels indicating
 					additional idle time in dsi clock cycles that is needed
 					to compensate for smaller line width.
 - qcom,partial-update-roi-merge:	Boolean indicates roi combination is need
 					and function has been provided for dcs
-					2A/2B command.
+					2A/2B command. This property is specified per timing node to support
+					resolution restrictions.
 - qcom,dcs-cmd-by-left:			Boolean to indicate that dcs command are sent
 					through the left DSI controller only in a dual-dsi configuration
 - qcom,mdss-dsi-panel-hdr-enabled:      Boolean to indicate HDR support in panel.
@@ -383,7 +385,8 @@
 - qcom,suspend-ulps-enabled:		Boolean to enable support for ULPS mode for panels during suspend state.
 - qcom,panel-roi-alignment:		Specifies the panel ROI alignment restrictions on its
 					left, top, width, height alignments and minimum width and
-					height values
+					height values. This property is specified per timing node to support
+					resolution's alignment restrictions.
 - qcom,esd-check-enabled:		Boolean used to enable ESD recovery feature.
 - qcom,mdss-dsi-panel-status-command:	A byte stream formed by multiple dcs packets based on
 					qcom dsi controller protocol, to read the panel status.
@@ -654,7 +657,6 @@
 		qcom,mdss-tear-check-rd-ptr-trigger-intr = <1281>;
 		qcom,mdss-tear-check-frame-rate = <6000>;
 		qcom,mdss-dsi-reset-sequence = <1 2>, <0 10>, <1 10>;
-		qcom,partial-update-enabled = "single_roi";
 		qcom,dcs-cmd-by-left;
 		qcom,mdss-dsi-lp11-init;
 		qcom,mdss-dsi-init-delay-us = <100>;
@@ -662,7 +664,6 @@
 		mdss-dsi-tx-eot-append;
 		qcom,ulps-enabled;
 		qcom,suspend-ulps-enabled;
-		qcom,panel-roi-alignment = <4 4 2 2 20 20>;
 		qcom,esd-check-enabled;
 		qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0A 08];
 		qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
@@ -721,6 +722,8 @@
 				qcom,mdss-dsc-config-by-manufacture-cmd;
 				qcom,display-topology = <1 1 1>;
 				qcom,default-topology-index = <0>;
+				qcom,partial-update-enabled = "single_roi";
+				qcom,panel-roi-alignment = <4 4 2 2 20 20>;
 			};
 		};
 		qcom,panel-supply-entries {
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dp.txt b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
index e46fd5c..ada2eab 100644
--- a/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dp.txt
@@ -10,6 +10,7 @@
 			"dp_mmss_cc" - Display Clock Control memory region.
 			"qfprom_physical" - QFPROM Phys memory region.
 			"dp_pll" - USB3 DP combo PLL memory region.
+			"usb3_dp_com" - USB3 DP PHY combo memory region.
 			"hdcp_physical" - DP HDCP memory region.
 - cell-index:           Specifies the controller instance.
 - clocks:               Clocks required for Display Port operation.
diff --git a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
index 641cc26..a89b834 100644
--- a/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
+++ b/Documentation/devicetree/bindings/drm/msm/sde-dsi.txt
@@ -100,3 +100,4 @@
 					controller. This must be enabled for debugging purpose
 					only with simulator panel. It should not be enabled for
 					normal DSI panels.
+- - qcom,null-insertion-enabled:	A boolean to enable NULL packet insertion feature for DSI controller.
diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt
index 2e233a1..69174ca 100644
--- a/Documentation/devicetree/bindings/gpu/adreno.txt
+++ b/Documentation/devicetree/bindings/gpu/adreno.txt
@@ -5,14 +5,17 @@
 Required properties:
 - label:		A string used as a descriptive name for the device.
 - compatible:		Must be "qcom,kgsl-3d0" and "qcom,kgsl-3d"
-- reg:			Specifies the register base address and size. The second interval
-			specifies the shader memory base address and size.
+- reg:			Specifies the register base address and size, the shader memory
+			base address and size (if it exists), and the base address and size
+			of the CX_DBGC block (if it exists).
 - reg-names:		Resource names used for the physical address of device registers
 			and shader memory. "kgsl_3d0_reg_memory" gives the physical address
 			and length of device registers while "kgsl_3d0_shader_memory" gives
 			physical address and length of device shader memory.  If
 			specified, "qfprom_memory" gives the range for the efuse
-			registers used for various configuration options.
+			registers used for various configuration options. If specified,
+			"kgsl_3d0_cx_dbgc_memory" gives the physical address and length
+			of the CX DBGC block.
 - interrupts:		Interrupt mapping for GPU IRQ.
 - interrupt-names:	String property to describe the name of the interrupt.
 - qcom,id:		An integer used as an identification number for the device.
@@ -211,19 +214,22 @@
 				and pagetable walk.
 - cache-slices:			phandle to the system LLC driver, cache slice index.
 
+
+GPU coresight info:
 The following properties are optional as collecting data via coresight might
 not be supported for every chipset. The documentation for coresight
 properties can be found in:
 Documentation/devicetree/bindings/coresight/coresight.txt
 
-- coresight-id           Unique integer identifier for the bus.
-- coresight-name         Unique descriptive name of the bus.
-- coresight-nr-inports   Number of input ports on the bus.
-- coresight-outports     List of output port numbers on the bus.
-- coresight-child-list   List of phandles pointing to the children of this
+- qcom,gpu-coresights:	 Container for sets of GPU coresight sources.
+- coresight-id:          Unique integer identifier for the bus.
+- coresight-name:        Unique descriptive name of the bus.
+- coresight-nr-inports:  Number of input ports on the bus.
+- coresight-outports:    List of output port numbers on the bus.
+- coresight-child-list:  List of phandles pointing to the children of this
                          component.
-- coresight-child-ports  List of input port numbers of the children.
-- coresight-atid         The unique ATID value of the coresight device
+- coresight-child-ports: List of input port numbers of the children.
+- coresight-atid:        The unique ATID value of the coresight device
 
 Example of A330 GPU in MSM8916:
 
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index 62ba54b..b0c5b57 100644
--- a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -18,7 +18,6 @@
 		"high-thr-en-set" for high threshold interrupts and
 		"low-thr-en-set" for low threshold interrupts. High and low threshold
 		interrupts are to be enabled if VADC_USR needs to support recurring measurement.
-- qcom,adc-bit-resolution : Bit resolution of the ADC.
 - qcom,adc-vdd-reference : Voltage reference used by the ADC.
 
 Channel nodes
@@ -46,6 +45,12 @@
 		    0 : The calibration values used for measurement are from a timer.
 		    1 : Forces a fresh measurement for calibration values at the same time
 			measurement is taken.
+- qcom,adc-full-scale-code: Full scale code with offset removed.
+- pinctrl-names: should be "default" (see pinctrl binding [0]).
+- pinctrl-0: a phandle pointing to the pin settings for the
+  device (see pinctrl binding [0]).
+
+[0]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
 
 Client required property:
 - qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
diff --git a/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt
new file mode 100644
index 0000000..b362940
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/avia-hx711.txt
@@ -0,0 +1,18 @@
+* AVIA HX711 ADC chip for weight cells
+  Bit-banging driver
+
+Required properties:
+ - compatible:	Should be "avia,hx711"
+ - sck-gpios:	Definition of the GPIO for the clock
+ - dout-gpios:	Definition of the GPIO for data-out
+		See Documentation/devicetree/bindings/gpio/gpio.txt
+ - avdd-supply:	Definition of the regulator used as analog supply
+
+Example:
+weight@0 {
+	compatible = "avia,hx711";
+	sck-gpios = <&gpio3 10 GPIO_ACTIVE_HIGH>;
+	dout-gpios = <&gpio0 7 GPIO_ACTIVE_HIGH>;
+	avdd-suppy = <&avdd>;
+};
+
diff --git a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
index 26cd70e..07667a4 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/qti,pdc.txt
@@ -29,6 +29,7 @@
 	Definition: Should contain "qcom,pdc-<target>"
 
 	* "qcom,pdc-sdm845": For sdm845 pin data
+	* "qcom,pdc-sdm845-v2": For sdm845 v2 pin data
 	* "qcom,pdc-sdm670": For sdm670 pin data
 
 - reg:
diff --git a/Documentation/devicetree/bindings/iommu/arm,smmu.txt b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
index 0f8dc27..2a7e161 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -96,6 +96,19 @@
 		  retention. No cache invalidation operations involving asid
 		  may be used.
 
+- qcom,disable-atos:
+		  Some hardware may not have full support for atos debugging
+		  in tandem with other features like power collapse.
+
+- qcom,mmu500-errata-1:
+		  An array of <sid mask>.
+		  Indicates the SIDs for which the workaround is required.
+
+- qcom,actlr:
+		  An array of <sid mask actlr-setting>.
+		  Any sid X for which X&~mask==sid will be programmed with the
+		  given actlr-setting.
+
 - qcom,deferred-regulator-disable-delay : The time delay for deferred regulator
                   disable in ms. In case of unmap call, regulator is
                   enabled/disabled. This may introduce additional delay. For
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
index 54365b1..cd4d222 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cci.txt
@@ -54,6 +54,21 @@
 - hw-trdhld : should contain internal hold time for SDA
 - hw-tsp : should contain filtering of glitches
 
+* Qualcomm Technologies, Inc. MSM Camera Sensor Resource Manager
+
+MSM camera sensor resource manager node contains properties of shared camera
+sensor resource.
+
+Required properties:
+- compatible : should be manufacturer name followed by sensor name
+  - "qcom,cam-res-mgr"
+Optional properties:
+- shared-gpios : should contain the gpios which are used by two or more
+  cameras, and these cameras may be opened together.
+- pinctrl-names: List of names to assign the shared pin state defined in pinctrl device node
+- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
+  controller. These pin configurations are installed in the pinctrl device node.
+
 * Qualcomm Technologies, Inc. MSM Sensor
 
 MSM sensor node contains properties of camera sensor
@@ -165,6 +180,9 @@
   should contain phandle of respective ir-cut node
 - qcom,special-support-sensors: if only some special sensors are supported
   on this board, add sensor name in this property.
+- use-shared-clk : It is booloean property. This property is required
+  if the clk is shared clk between different sensor and ois, if this
+  device need to be opened together.
 - clock-rates: clock rate in Hz.
 - clock-cntl-level: says what all different cloc level node has.
 - clock-cntl-support: Says whether clock control support is present or not
@@ -233,6 +251,9 @@
   required from the regulators mentioned in the regulator-names property
   (in the same order).
 - cam_vaf-supply : should contain regulator from which ois voltage is supplied
+- use-shared-clk : It is booloean property. This property is required
+  if the clk is shared clk between different sensor and ois, if this
+  device need to be opened together.
 
 Example:
 
@@ -334,6 +355,15 @@
          rgltr-load-current = <100000>;
     };
 
+    qcom,cam-res-mgr {
+         compatible = "qcom,cam-res-mgr";
+         status = "ok";
+         shared-gpios = <18 19>;
+         pinctrl-names = "cam_res_mgr_default", "cam_res_mgr_suspend";
+         pinctrl-0 = <&cam_shared_clk_active &cam_res_mgr_active>;
+         pinctrl-1 = <&cam_shared_clk_suspend &cam_res_mgr_suspend>;
+    };
+
     qcom,cam-sensor@0 {
          cell-index = <0>;
          compatible = "qcom,camera";
@@ -350,7 +380,7 @@
          cam_vio-supply = <&pm845_lvs1>;
          cam_vana-supply = <&pmi8998_bob>;
          regulator-names = "cam_vdig", "cam_vio", "cam_vana";
-	 rgltr-cntrl-support;
+         rgltr-cntrl-support;
          rgltr-min-voltage = <0 3312000 1352000>;
          rgltr-max-voltage = <0 3312000 1352000>;
          rgltr-load-current = <0 80000 105000>;
@@ -374,6 +404,7 @@
          sensor-mode = <0>;
          cci-master = <0>;
          status = "ok";
+         use-shared-clk;
          clocks = <&clock_mmss clk_mclk0_clk_src>,
                <&clock_mmss clk_camss_mclk0_clk>;
          clock-names = "cam_src_clk", "cam_clk";
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index 99f3ba2..4b16103 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -66,6 +66,11 @@
   Value type: <u32>
   Definition: Interrupt associated with CAMNOC HW.
 
+- qcom,cpas-hw-ver
+  Usage: required
+  Value type: <u32>
+  Definition: CAM HW Version information.
+
 - regulator-names
   Usage: required
   Value type: <string>
@@ -181,6 +186,7 @@
 		reg-cam-base = <0x40000 0x42000>;
 		interrupt-names = "cpas_camnoc";
 		interrupts = <0 459 0>;
+		qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */
 		regulator-names = "camss-vdd";
 		camss-vdd-supply = <&titan_top_gdsc>;
 		clock-names = "gcc_ahb_clk",
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-eeprom.txt b/Documentation/devicetree/bindings/media/video/msm-cam-eeprom.txt
index 933ad85..83a58df 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-eeprom.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-eeprom.txt
@@ -278,6 +278,46 @@
   Value type: <u32>
   Definition: should specify the power on sequence delay time in ms.
 
+- spiop-read
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI read operation related data.
+
+- spiop-readseq
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI read sequence operation realted data.
+
+- spiop-queryid
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI query eeprom id operation related data.
+
+- spiop-pprog:
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI page program operation related data.
+
+- spiop-wenable
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI write enable operation related data.
+
+- spiop-readst
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI read destination operation related data.
+
+- spiop-erase
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides SPI erase operation related data.
+
+- eeprom-idx
+  Usage: required
+  Value type: <u32>
+  Definition: this array provides eeprom id realted data.
+
 - xxxx-supply
   Usage: required
   Value type: <phandle>
@@ -385,6 +425,10 @@
 		cell-index = <0>;
 		reg = <0x0>;
 		qcom,eeprom-name = "msm_eeprom";
+		eeprom-id0 = <0xF8 0x15>;
+		eeprom-id1 = <0xEF 0x15>;
+		eeprom-id2 = <0xC2 0x36>;
+		eeprom-id3 = <0xC8 0x15>;
 		compatible = "qcom,eeprom";
 		qcom,slave-addr = <0x60>;
 		qcom,num-blocks = <2>;
@@ -400,6 +444,13 @@
 		qcom,cmm-data-compressed;
 		qcom,cmm-data-offset = <0>;
 		qcom,cmm-data-size = <0>;
+		spiop-read = <0x03 3 0 0 0>;
+		spiop-readseq = <0x03 3 0 0 0>;
+		spiop-queryid = <0x90 3 0 0 0>;
+		spiop-pprog = <0x02 3 0 3 100>;
+		spiop-wenable = <0x06 0 0 0 0>;
+		spiop-readst = <0x05 0 0 0 0>;
+		spiop-erase = <0x20 3 0 10 100>;
 		qcom,cam-power-seq-type = "sensor_vreg",
 			"sensor_vreg", "sensor_clk",
 			"sensor_gpio", "sensor_gpio";
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
index 36dad1a..ffc0e96 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-icp.txt
@@ -131,6 +131,11 @@
   Value type: <string>
   Definition: Name of firmware image.
 
+- ubwc-cfg
+  Usage: required
+  Value type: <u32>
+  Definition: UBWC configuration.
+
 Examples:
 a5: qcom,a5@ac00000 {
 	cell-index = <0>;
@@ -169,6 +174,7 @@
 	clock-rates = <0 0 0 80000000 0 0 0 0 600000000 0 0>;
 	clock-cntl-level = "turbo";
 	fw_name = "CAMERA_ICP.elf";
+	ubwc-cfg = <0x7F 0x1FF>;
 };
 
 qcom,ipe0 {
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-lrme.txt b/Documentation/devicetree/bindings/media/video/msm-cam-lrme.txt
new file mode 100644
index 0000000..9a37922
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-lrme.txt
@@ -0,0 +1,149 @@
+* Qualcomm Technologies, Inc. MSM Camera LRME
+
+The MSM camera Low Resolution Motion Estimation device provides dependency
+definitions for enabling Camera LRME HW. MSM camera LRME is implemented in
+multiple device nodes. The root LRME device node has properties defined to
+hint the driver about the LRME HW nodes available during the probe sequence.
+Each node has multiple properties defined for interrupts, clocks and
+regulators.
+
+=======================
+Required Node Structure
+=======================
+LRME root interface node takes care of the handling LRME high level
+driver handling and controls underlying LRME hardware present.
+
+- compatible
+  Usage: required
+  Value type: <string>
+  Definition: Should be "qcom,cam-lrme"
+
+- compat-hw-name
+  Usage: required
+  Value type: <string>
+  Definition: Should be "qcom,lrme"
+
+- num-lrme
+  Usage: required
+  Value type: <u32>
+  Definition: Number of supported LRME HW blocks
+
+Example:
+	qcom,cam-lrme {
+		compatible = "qcom,cam-lrme";
+		compat-hw-name = "qcom,lrme";
+		num-lrme = <1>;
+	};
+
+=======================
+Required Node Structure
+=======================
+LRME Node provides interface for Low Resolution Motion Estimation hardware
+driver about the device register map, interrupt map, clocks, regulators.
+
+- cell-index
+  Usage: required
+  Value type: <u32>
+  Definition: Node instance number
+
+- compatible
+  Usage: required
+  Value type: <string>
+  Definition: Should be "qcom,lrme"
+
+- reg-names
+  Usage: optional
+  Value type: <string>
+  Definition: Name of the register resources
+
+- reg
+  Usage: optional
+  Value type: <u32>
+  Definition: Register values
+
+- reg-cam-base
+  Usage: optional
+  Value type: <u32>
+  Definition: Offset of the register space compared to
+              to Camera base register space
+
+- interrupt-names
+  Usage: optional
+  Value type: <string>
+  Definition: Name of the interrupt
+
+- interrupts
+  Usage: optional
+  Value type: <u32>
+  Definition: Interrupt line associated with LRME HW
+
+- regulator-names
+  Usage: required
+  Value type: <string>
+  Definition: Name of the regulator resources for LRME HW
+
+- camss-supply
+  Usage: required
+  Value type: <phandle>
+  Definition: Regulator reference corresponding to the names listed
+              in "regulator-names"
+
+- clock-names
+  Usage: required
+  Value type: <string>
+  Definition: List of clock names required for LRME HW
+
+- clocks
+  Usage: required
+  Value type: <phandle>
+  Definition: List of clocks required for LRME HW
+
+- clock-rates
+  Usage: required
+  Value type: <u32>
+  Definition: List of clocks rates
+
+- clock-cntl-level
+  Usage: required
+  Value type: <string>
+  Definition: List of strings corresponds clock-rates levels
+  Supported strings: minsvs, lowsvs, svs, svs_l1, nominal, turbo
+
+- src-clock-name
+  Usage: required
+  Value type: <string>
+  Definition: Source clock name
+
+Examples:
+	cam_lrme: qcom,lrme@ac6b000 {
+		cell-index = <0>;
+		compatible = "qcom,lrme";
+		reg-names = "lrme";
+		reg = <0xac6b000 0xa00>;
+		reg-cam-base = <0x6b000>;
+		interrupt-names = "lrme";
+		interrupts = <0 476 0>;
+		regulator-names = "camss";
+		camss-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"lrme_clk_src",
+			"lrme_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_LRME_CLK_SRC>,
+			<&clock_camcc CAM_CC_LRME_CLK>;
+		clock-rates = <0 0 0 0 0 0 0>,
+			<0 0 0 0 0 19200000 19200000>,
+			<0 0 0 0 0 19200000 19200000>,
+			<0 0 0 0 0 19200000 19200000>;
+		clock-cntl-level = "lowsvs", "svs", "svs_l1", "turbo";
+		src-clock-name = "lrme_core_clk_src";
+	};
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt b/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt
index 72e2ab4..728c5f9 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-smmu.txt
@@ -56,6 +56,11 @@
   Value type: <string>
   Definition: Should specify a string label to identify the context bank.
 
+- qcom,secure-cb
+  Usage: optional
+  Value type: boolean
+  Definition: Specifies if the context bank is a secure context bank.
+
 =============================================
 Third Level Node - CAM SMMU memory map device
 =============================================
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt b/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt
index 1c18228..99f2c7a 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-vfe.txt
@@ -12,6 +12,7 @@
 ======================================
 First Level Node - CAM VFE device
 ======================================
+Required properties:
 - compatible
   Usage: required
   Value type: <string>
@@ -74,6 +75,22 @@
   Value type: <string>
   Definition: Source clock name.
 
+Optional properties:
+- clock-names-option
+  Usage: optional
+  Value type: <string>
+  Definition: Optional clock names.
+
+- clocks-option
+  Usage: required if clock-names-option defined
+  Value type: <phandle>
+  Definition: List of optinal clocks used for VFE HW.
+
+- clock-rates-option
+  Usage: required if clock-names-option defined
+  Value type: <u32>
+  Definition: List of clocks rates for optional clocks.
+
 Example:
 	qcom,vfe0@acaf000 {
 		cell-index = <0>;
@@ -105,5 +122,8 @@
 			<&clock_camcc CAM_CC_IFE_0_AXI_CLK>,
 		clock-rates = <0 0 80000000 0 320000000 0 384000000 0 0 0>;
 		src-clock-name = "ife_clk_src";
+		clock-names-option = "ife_dsp_clk";
+		clocks-option = <&clock_camcc CAM_CC_IFE_0_DSP_CLK>;
+		clock-rates-option = <600000000>;
 		status = "ok";
 	};
diff --git a/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt b/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
index 46649af..db34047 100644
--- a/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-sde-rotator.txt
@@ -123,6 +123,7 @@
 				swizzle configuration value.
 - qcom,rot-reg-bus:		Property to provide Bus scaling for register
 				access for rotator blocks.
+- power-domains:		A phandle to respective power domain node.
 
 Subnode properties:
 - compatible:		Compatible name used in smmu v2.
@@ -150,6 +151,8 @@
 		interrupt-parent = <&mdss_mdp>;
 		interrupts = <2 0>;
 
+		power-domains = <&mdss_mdp>;
+
 		qcom,mdss-mdp-reg-offset = <0x00001000>;
 
 		rot-vdd-supply = <&gdsc_mdss>;
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
index d61606a..f0549cb 100644
--- a/Documentation/devicetree/bindings/media/video/msm-vidc.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -7,9 +7,11 @@
 - compatible : one of:
 	- "qcom,msm-vidc"
         - "qcom,sdm845-vidc" : Invokes driver specific data for SDM845.
+        - "qcom,sdm670-vidc" : Invokes driver specific data for SDM670.
 
 Optional properties:
 - reg : offset and length of the register set for the device.
+- sku-index : sku version of the hardware.
 - interrupts : should contain the vidc interrupt.
 - qcom,reg-presets : list of offset-value pairs for registers to be written.
   The offsets are from the base offset specified in 'reg'. This is mainly
diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index 24c75e2..6222881 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -85,7 +85,12 @@
 	  On certain chipsets, coming out of the CX Power Collapse event, the SDCC registers
 	  contents will not be retained. It is software responsibility to restore the
 	  SDCC registers before resuming to normal operation.
-
+	- qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device.
+	- qcom,ddr-config: Certain chipsets and platforms require particular settings for
+			   the RCLK delay DLL configuration register for HS400 mode to work.
+			   This value can vary between platforms and msms. If a msm/platform
+			   require a different DLL setting than the default/POR setting for
+			   HS400 mode, it can be specified using this field.
 In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
 	- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
 	- qcom,<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm).
diff --git a/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
index 094dc25..a98e4ae 100644
--- a/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
+++ b/Documentation/devicetree/bindings/mtd/msm_qpic_nand.txt
@@ -9,6 +9,8 @@
   defined.
 - qcom,reg-adjustment-offset : Specify the base adjustment offset value for the
   version registers
+- qcom,qpic-clk-rpmh: Indicates whether QPIC clock is RPMH controlled clock or
+  not.
 
 MTD flash partition layout for NAND devices -
 
@@ -53,6 +55,7 @@
 		qcom,msm-bus,num-cases = <1>;
 		qcom,msm-bus,num-paths = <1>;
 		qcom,msm-bus,vectors-KBps = <91 512 0 0>,
+		qcom,qpic-clk-rpmh;
 	};
 
        qcom,mtd-partitions {
diff --git a/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt b/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt
new file mode 100644
index 0000000..8e56180
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/qcom,emac-dwc-eqos.txt
@@ -0,0 +1,43 @@
+Qualcomm Technologies Inc. EMAC Gigabit Ethernet controller
+
+This network controller consists of the MAC and
+RGMII IO Macro for interfacing with PHY.
+
+Required properties:
+
+emac_hw node:
+- compatible: Should be "qcom,emac-dwc-eqos"
+- reg: Offset and length of the register regions for the mac and io-macro
+- interrupts: Interrupt number used by this controller
+- io-macro-info: Internal io-macro-info
+
+Internal io-macro-info:
+- io-macro-bypass-mode: <0 or 1> internal or external delay configuration
+- io-interface: <rgmii/mii/rmii> PHY interface used
+
+Example:
+
+soc {
+	emac_hw: qcom,emac@00020000 {
+			compatible = "qcom,emac-dwc-eqos";
+			reg = <0x20000 0x10000>,
+				  <0x36000 0x100>;
+			reg-names = "emac-base", "rgmii-base";
+			interrupts = <0 62 4>, <0 60 4>,
+					<0 49 4>, <0 50 4>,
+					<0 51 4>, <0 52 4>,
+					<0 53 4>, <0 54 4>,
+					<0 55 4>, <0 56 4>,
+					<0 57 4>;
+			interrupt-names = "sbd-intr", "lpi-intr",
+				"tx-ch0-intr", "tx-ch1-intr",
+				"tx-ch2-intr", "tx-ch3-intr",
+				"tx-ch4-intr", "rx-ch0-intr",
+				"rx-ch1-intr", "rx-ch2-intr",
+				"rx-ch3-intr";
+			io-macro-info {
+				io-macro-bypass-mode = <0>;
+				io-interface = "rgmii";
+			};
+		};
+}
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
index 2a5096f..6af2bac 100644
--- a/Documentation/devicetree/bindings/pci/msm_pcie.txt
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -95,6 +95,9 @@
     and assign for each endpoint.
   - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
     stable after power on, before de-assert the PERST to the endpoint.
+  - qcom,switch-latency: The time (unit: ms) to wait for the PCIe endpoint's link
+    training with switch downstream port after the link between switch upstream
+    port and RC is up.
   - qcom,wr-halt-size: With base 2, this exponent determines the size of the
     data that PCIe core will halt on for each write transaction.
   - qcom,slv-addr-space-size: The memory space size of PCIe Root Complex.
@@ -268,6 +271,7 @@
 		qcom,smmu-exist;
 		qcom,smmu-sid-base = <0x1480>;
 		qcom,ep-latency = <100>;
+		qcom,switch-latency = <100>;
 		qcom,wr-halt-size = <0xa>; /* 1KB */
 		qcom,slv-addr-space-size = <0x1000000>; /* 16MB */
 		qcom,cpl-timeout = <0x2>;
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index 271703f..abbc560 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -32,6 +32,7 @@
 		      This may be a shared regulator that is already voted
 		      on in the PIL proxy voting code (and also managed by the
 		      modem on its own), hence we mark it as as optional.
+- vdd_mss-uV:         Voltage to set for vdd_mss.
 - vdd_pll-supply:     Reference to the regulator that supplies the PLL's rail.
 - qcom,vdd_pll:       Voltage to be set for the PLL's rail.
 - reg-names:          "cxrail_bhs_reg" - control register for modem power
@@ -67,6 +68,9 @@
 			  service.
 - qcom,sysmon-id:	platform device id that sysmon is probed with for the subsystem.
 - qcom,override-acc: Boolean- Present if we need to override the default ACC settings
+- qcom,mss_pdc_offset: Integer- Mandatory if PDC register is specified. It is
+				used to specify which bit in the PDC register
+				corresponds to the modem.
 - qcom,ahb-clk-vote: Boolean- Present if we need to remove the vote for the mss_cfg_ahb
 		     clock after the modem boots up
 - qcom,pnoc-clk-vote: Boolean- Present if the modem needs the PNOC bus to be
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
new file mode 100644
index 0000000..4b483e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
@@ -0,0 +1,210 @@
+Qualcomm Technologies, Inc. MSM8953 TLMM block
+
+This binding describes the Top Level Mode Multiplexer block found in the
+MSM8953 platform.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be "qcom,msm8953-pinctrl"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: the base address and size of the TLMM register space.
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: should specify the TLMM summary IRQ.
+
+- interrupt-controller:
+	Usage: required
+	Value type: <none>
+	Definition: identifies this node as an interrupt controller
+
+- #interrupt-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 2. Specifying the pin number and flags, as defined
+		    in <dt-bindings/interrupt-controller/irq.h>
+
+- gpio-controller:
+	Usage: required
+	Value type: <none>
+	Definition: identifies this node as a gpio controller
+
+- #gpio-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 2. Specifying the pin number and flags, as defined
+		    in <dt-bindings/gpio/gpio.h>
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters, such as pull-up, drive strength, etc.
+
+
+PIN CONFIGURATION NODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+	Usage: required
+	Value type: <string-array>
+	Definition: List of gpio pins affected by the properties specified in
+		    this subnode.
+		    Valid pins are:
+		    gpio0-gpio141,
+		    sdc1_clk,
+		    sdc1_cmd,
+		    sdc1_data,
+		    sdc1_rclk,
+		    sdc2_clk,
+		    sdc2_cmd,
+		    sdc2_data,
+		    qdsd_cmd,
+		    qdsd_data0,
+		    qdsd_data1,
+		    qdsd_data2,
+		    qdsd_data3
+
+- function:
+	Usage: required
+	Value type: <string>
+	Definition: Specify the alternative function to be configured for the
+		    specified pins. Functions are only valid for gpio pins.
+		    Valid values are:
+	gpio, blsp_spi1, smb_int, adsp_ext, prng_rosc, blsp_i2c1,
+	qdss_cti_trig_out_b0, qdss_cti_trig_out_a1, blsp_spi2, blsp_uart2,
+	ldo_update, dac_calib0, ldo_en, blsp_i2c2, gcc_gp1_clk_b,
+	atest_gpsadc_dtest0_native, blsp_spi3, qdss_tracedata_b,
+	pwr_modem_enabled_b, blsp_i2c3, gcc_gp2_clk_b, gcc_gp3_clk_b, hall_int,
+	blsp_spi4, blsp_uart4, pwr_nav_enabled_b, dac_calib1, cap_int,
+	pwr_crypto_enabled_b, dac_calib2, blsp_i2c4, nfc_disable, blsp_spi5,
+	blsp_uart5, qdss_traceclk_a, atest_bbrx1, nfc_irq, m_voc,
+	qdss_cti_trig_in_a0, atest_bbrx0, blsp_i2c5, qdss_tracectl_a,
+	atest_gpsadc_dtest1_native, qdss_tracedata_a, blsp_spi6, blsp_uart6,
+	qdss_tracectl_b, dac_calib15, qdss_cti_trig_in_b0, dac_calib16, blsp_i2c6,
+	qdss_traceclk_b, atest_wlan0, atest_wlan1, mdp_vsync, pri_mi2s_mclk_a,
+	sec_mi2s_mclk_a, qdss_cti_trig_out_b1, cam_mclk, dac_calib3, cci_i2c,
+	pwr_modem_enabled_a, dac_calib4, dac_calib19, flash_strobe, cci_timer0,
+	cci_timer1, cam_irq, cci_timer2, blsp1_spi, pwr_nav_enabled_a, ois_sync,
+	cci_timer3, cci_timer4, blsp3_spi, qdss_cti_trig_out_a0, dac_calib7,
+	accel_int, gcc_gp1_clk_a, dac_calib8, alsp_int, gcc_gp2_clk_a, dac_calib9,
+	mag_int, gcc_gp3_clk_a, pwr_crypto_enabled_a, cci_async, cam1_standby,
+	dac_calib5, cam1_rst, dac_calib6, dac_calib10, gyro_int, dac_calib11,
+	pressure_int, dac_calib12, blsp6_spi, dac_calib13, fp_int,
+	qdss_cti_trig_in_b1, dac_calib14, uim_batt, cam0_ldo, sd_write, uim1_data,
+	uim1_clk, uim1_reset, uim1_present, uim2_data, uim2_clk, uim2_reset,
+	uim2_present, ts_xvdd, mipi_dsi0, nfc_dwl, us_euro, atest_char3, dbg_out,
+	bimc_dte0, ts_resout, ts_sample, sec_mi2s_mclk_b, pri_mi2s, codec_reset,
+	cdc_pdm0, atest_char1, ebi_cdc, dac_calib17, us_emitter, atest_char0,
+	pri_mi2s_mclk_b, lpass_slimbus, lpass_slimbus0, lpass_slimbus1, codec_int1,
+	codec_int2, wcss_bt, atest_char2, ebi_ch0, wcss_wlan2, wcss_wlan1,
+	wcss_wlan0, wcss_wlan, wcss_fm, ext_lpass, mss_lte, key_volp, pbs0,
+	cri_trng0, key_snapshot, pbs1, cri_trng1, key_focus, pbs2, cri_trng,
+	gcc_tlmm, key_home, pwr_down, dmic0_clk, blsp7_spi, hdmi_int, dmic0_data,
+	qdss_cti_trig_in_a1, pri_mi2s_ws, wsa_io, wsa_en, blsp_spi8, wsa_irq,
+	blsp_i2c8, gcc_plltest, nav_pps_in_a, pa_indicator, nav_pps_in_b, nav_pps,
+	modem_tsync, nav_tsync, ssbi_wtr1, gsm1_tx, dac_calib18, gsm0_tx,
+	atest_char, atest_tsens, bimc_dte1, dac_calib20, cam2_rst, ddr_bist,
+	dac_calib21, cam2_standby, dac_calib22, cam3_rst, dac_calib23,
+	cam3_standby, dac_calib24, sdcard_det, dac_calib25, cam1_ldo, sec_mi2s,
+	blsp_spi7, blsp_i2c7, ss_switch, tsens_max
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as no pull.
+
+- bias-pull-down:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as pull down.
+
+- bias-pull-up:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as pull up.
+
+- output-high:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    high.
+		    Not valid for sdc pins.
+
+- output-low:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    low.
+		    Not valid for sdc pins.
+
+- drive-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the drive strength for the specified pins, in mA.
+		    Valid values are: 2, 4, 6, 8, 10, 12, 14 and 16
+
+Example:
+
+	tlmm: pinctrl@1000000 {
+		compatible = "qcom,msm8953-pinctrl";
+		reg = <0x1000000 0x300000>;
+		interrupts = <0 208 0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+
+		pmx-uartconsole {
+			uart_console_active: uart_console_active {
+				mux {
+					pins = "gpio4", "gpio5";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			uart_console_sleep: uart_console_sleep {
+				mux {
+					pins = "gpio4", "gpio5";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+
+		};
+	};
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
index 9c26374..7e75d2c 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdm845-pinctrl
@@ -6,7 +6,7 @@
 - compatible:
 	Usage: required
 	Value type: <string>
-	Definition: must be "qcom,sdm845-pinctrl"
+	Definition: must be "qcom,sdm845-pinctrl" or "qcom,sdm845-pinctrl-v2"
 
 - reg:
 	Usage: required
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index e821feb..6b40b30 100644
--- a/Documentation/devicetree/bindings/platform/msm/ipa.txt
+++ b/Documentation/devicetree/bindings/platform/msm/ipa.txt
@@ -28,7 +28,6 @@
 - qcom,lan-rx-ring-size: size of LAN rx ring, default is 192
 - qcom,arm-smmu: SMMU is present and ARM SMMU driver is used
 - qcom,msm-smmu: SMMU is present and QSMMU driver is used
-- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass
 - qcom,smmu-fast-map: Boolean context flag to set SMMU to fastpath mode
 - ipa_smmu_ap: AP general purpose SMMU device
 	compatible "qcom,ipa-smmu-ap-cb"
@@ -80,6 +79,8 @@
 - qcom,rx-polling-sleep-ms:	Receive Polling Timeout in millisecond,
 				default is 1 millisecond.
 - qcom,ipa-polling-iteration:	IPA Polling Iteration Count,default is 40.
+- qcom,mhi-event-ring-id-limits: Two elements property. Start and End limits
+					for MHI event rings ids.
 - qcom,ipa-tz-unlock-reg:       Register start addresses and ranges which
                                 need to be unlocked by TZ.
 
@@ -122,6 +123,9 @@
 
 -compatible: "qcom,ipa-smmu-uc-cb" - represents IPA uC context bank (for uC
 					offload scenarios).
+
+- qcom,smmu-s1-bypass: Boolean context flag to set SMMU to S1 bypass.
+
 - iommus : the phandle and stream IDs for the SMMU used by this root
 
 - qcom,iova-mapping: specifies the start address and size of iova space.
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
index d0d878b..f50fd88 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg-gen3.txt
@@ -208,14 +208,14 @@
 	Value type: <u32>
 	Definition: Lower limit of battery temperature to start the capacity
 		    learning. If this is not specified, then the default value
-		    used will be 150. Unit is in decidegC.
+		    used will be 150 (15 C). Unit is in decidegC.
 
 - qcom,cl-max-temp
 	Usage:      optional
 	Value type: <u32>
 	Definition: Upper limit of battery temperature to start the capacity
 		    learning. If this is not specified, then the default value
-		    used will be 450 (45C). Unit is in decidegC.
+		    used will be 500 (50 C). Unit is in decidegC.
 
 - qcom,cl-max-increment
 	Usage:      optional
@@ -271,6 +271,14 @@
 	Definition: A boolean property that when defined holds SOC at 100% when
 		    the battery is full.
 
+- qcom,linearize-soc
+	Usage:      optional
+	Value type: <empty>
+	Definition: A boolean property that when defined linearizes SOC when
+		    the SOC drops after charge termination monotonically to
+		    improve the user experience. This is applicable only if
+		    "qcom,hold-soc-while-full" is specified.
+
 - qcom,ki-coeff-soc-dischg
 	Usage:      optional
 	Value type: <prop-encoded-array>
@@ -301,6 +309,13 @@
 		    is specified to make it fully functional. Value has no
 		    unit. Allowed range is 0 to 62200 in micro units.
 
+- qcom,ki-coeff-full-dischg
+	Usage:	    optional
+	Value type: <u32>
+	Definition: Ki coefficient full SOC value that will be applied during
+		    discharging. If not specified, a value of 0 will be set.
+		    Allowed range is from 245 to 62256.
+
 - qcom,fg-rconn-mohms
 	Usage:      optional
 	Value type: <u32>
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
new file mode 100644
index 0000000..f6a7a1b
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-fg.txt
@@ -0,0 +1,275 @@
+QTI's QPNP PMIC Fuel Gauge Device
+
+QPNP PMIC FG provides interface to clients to read properties related
+to the battery. Its main function is to retrieve the State of Charge (SOC),
+a 0-100 percentage representing the amount of charge left in the battery.
+
+There are two required peripherals in the FG driver, both implemented as
+subnodes in the example. These peripherals must not be disabled if the FG
+device is to enabled:
+
+- qcom,fg-soc : The main FG device. Supports battery fuel gauge controls and
+		sensors.
+- qcom,fg-batt : The FG battery device supports interrupts and controls with
+		respect to the state of the connected battery.For example: the
+		peripheral informs the driver if the battery has been identified
+		by the fuel gauge based on a given battery resistance range.
+
+Optionally ADC nodes can be added
+- qcom,revid-tp-rev: A subnode with a register address for the TP_REV register
+		in the REVID peripheral. This is used to apply workarounds that
+		may depend on the trim program.
+- qcom,fg-adc-vbat : A subnode with a register address for the FG_ADC_USR
+		peripheral which is used mainly for battery current limiting (BCL).
+		This node maps out the VBAT reading register which allows to have
+		a +/- 32 mV accurate reading of VBAT.
+- qcom,fg-adc-ibat : A subnode with a register address for the FG_ADC_USR
+		peripheral which is used mainly for battery current limiting (BCL).
+		This node maps out the IBAT current reading register which allows
+		to have a +/- 32 mA accurate reading of IBAT.
+
+Parent node required properties:
+- compatible : should be "qcom,qpnp-fg" for the FG driver.
+- qcom,pmic-revid :			Should specify the phandle of PMIC
+					revid module. This is used to identify
+					the PMIC subtype.
+
+Parent node optional properties:
+- qcom,warm-bat-decidegc:		Warm battery temperature in decidegC.
+- qcom,cool-bat-decidegc:		Cool battery temperature in decidegC.
+- qcom,hot-bat-decidegc:		Hot battery temperature in decidegC.
+- qcom,cold-bat-decidegc:		Cold battery temperature in decidegC.
+- qcom,cold-hot-jeita-hysteresis:	A tuple of 2. Index[0] is cold
+					hysteresis and index[1] is hot
+					hysterisis(in decidegC).
+- qcom,ext-sense-type:			Current sense channel used by the FG.
+					Set this to use external rsense.
+- qcom,thermal-coefficients:		Byte array of thermal coefficients for
+					reading battery thermistor. This should
+					be exactly 6 bytes in length.
+					Example: [01 02 03 04 05 06]
+- qcom,resume-soc:			soc to resume charging in percentage.
+- qcom,resume-soc-raw:			soc to resume charging in the scale of
+					[0-255]. This overrides qcom,resume-soc
+					if defined.
+- qcom,hold-soc-while-full:		A boolean property that when defined
+					holds SOC at 100% when the battery is
+					full.
+- qcom,bcl-lm-threshold-ma:		BCL LPM to MPM mode transition threshold
+					in milliAmpere.
+- qcom,bcl-mh-threshold-ma:		BCL MPM to HPM mode transition threshold
+					in milliAmpere.
+- qcom,use-otp-profile:			Specify this flag to avoid RAM loading
+					any battery profile.
+- qcom,sw-rbias-control:		Boolean property which defines whether
+					the Rbias needs to be controlled by
+					software. If this is not set, it will
+					be controlled by hardware (default).
+- qcom,fg-iterm-ma:			Battery current at which the fuel gauge
+					will try to scale 100% towards. When
+					the charge current goes above this, the
+					SoC should be at 100%.
+- qcom,fg-chg-iterm-ma:			Battery current at which the fuel gauge
+					will issue end of charge if the charger
+					is configured to use the fuel gauge
+					ADCs for end of charge detection. This
+					property is in milliamps and should be
+					positive (e.g. 100mA to terminate at
+					-100mA).
+- qcom,irq-volt-empty-mv:		The voltage threshold that the empty
+					soc interrupt will be triggered. When
+					the empty soc interrupt fires, battery
+					soc will be pulled to 0 and the
+					userspace will be notified via the
+					power supply framework. The userspace
+					will read 0% soc and immediately
+					shutdown.
+- qcom,fg-cutoff-voltage-mv:		The voltage where the fuel gauge will
+					steer the SOC to be zero. For example,
+					if the cutoff voltage is set to 3400mv,
+					the fuel gauge will try to count SoC so
+					that the battery SoC will be 0 when it
+					is 3400mV.
+- qcom,fg-vbat-estimate-diff-mv:	If the estimated voltage based on SoC
+					and battery current/resistance differs
+					from the actual voltage by more than
+					this amount, the fuel gauge will
+					redo the first SoC estimate when the
+					driver probes.
+- qcom,fg-delta-soc:			How many percent the monotonic SoC must
+					change before a new delta_soc interrupt
+					is asserted. If this value is raised
+					above 3-4, some period workarounds may
+					not function well, so it's best to
+					leave this at 1 or 2%.
+- qcom,fg-vbatt-low-threshold:		Voltage (in mV) which upon set will be
+					used for configuring the low battery
+					voltage threshold. Interrupt will be
+					asserted and handled based upon
+					this. If this property is not specified,
+					low battery voltage threshold will be
+					configured to 4200 mV.
+- qcom,cycle-counter-en:		Boolean property which enables the cycle
+					counter feature. If this property is
+					present, then the following properties
+					to specify low and high soc thresholds
+					should be defined.
+- qcom,capacity-learning-on:		A boolean property to have the fuel
+					gauge driver attempt to learn the
+					battery capacity when charging. Takes
+					precedence over capacity-estimation-on.
+- qcom,capacity-learning-feedback:	A boolean property to have the fuel
+					gauge driver to feedback the learned
+					capacity into the capacity learning
+					algorithm. This has to be used only if
+					the property "qcom,capacity-learning-on"
+					is specified.
+- qcom,cl-max-increment-deciperc:	The maximum percent that the capacity
+					can rise as the result of a single
+					charge cycle. This property corresponds
+					to .1% increments.
+- qcom,cl-max-decrement-deciperc:	The maximum percent that the capacity
+					can fall as the result of a single
+					charge cycle. This property corresponds
+					to .1% decrements.
+- qcom,cl-max-temp-decidegc:		Above this temperature, capacity
+					learning will be canceled.
+- qcom,cl-mix-temp-decidegc:		Below this temperature, capacity
+					learning will be canceled.
+- qcom,cl-max-start-soc:		The battery soc has to be below this
+					value at the start of a charge cycle
+					for capacity learning to be run.
+- qcom,cl-vbat-est-thr-uv:		The maximum difference between the
+					battery voltage shadow and the current
+					predicted voltage in uV to initiate
+					capacity learning.
+- qcom,capacity-estimation-on:		A boolean property to have the fuel
+					gauge driver attempt to estimate the
+					battery capacity using battery
+					resistance.
+- qcom,aging-eval-current-ma:		Current used to evaluate battery aging.
+					This value should be around the steady
+					state current drawn from the battery
+					when the phone is low on battery.
+- qcom,fg-cc-cv-threshold-mv:		Voltage threshold in mV for configuring
+					constant charge (CC) to constant
+					voltage (CV) setpoint in FG upon
+					which the battery EOC status will
+					be determined. This value should be
+					10 mV less than the float voltage
+					configured in the charger.
+					This property should only be specified
+					if "qcom,autoadjust-vfloat" property is
+					specified in the charger driver to
+					ensure a proper operation.
+- qcom,bad-battery-detection-enable:	A boolean property to enable the fuel
+					gauge driver to detect the damaged battery
+					when the safety-timer expires by using the
+					coulomb count.
+- qcom,fg-therm-delay-us:		The time in microseconds to delay battery
+					thermistor biasing.
+- qcom,esr-pulse-tuning-en:		A boolean property to enable ESR pulse
+					tuning feature. If this is enabled,
+					ESR pulse extraction will be disabled
+					when state of charge (SOC) is less than
+					2%. It will be enabled back when SOC
+					gets above 2%. In addition, for SOC
+					between 2% and 5%, ESR pulse timing
+					settings will be different from default.
+					Once SOC crosses 5%, ESR pulse timings
+					will be restored back to default.
+
+qcom,fg-soc node required properties:
+- reg : offset and length of the PMIC peripheral register map.
+- interrupts : the interrupt mappings.
+		The format should be
+		<slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped fg soc interrupts
+		The following interrupts are required:
+		0: high-soc
+		1: low-soc
+		2: full-soc
+		3: empty-soc
+		4: delta-soc
+		5: first-est-done
+		6: sw-fallbk-ocv
+		7: sw-fallbk-new-batt
+
+qcom,fg-memif node required properties:
+- reg : offset and length of the PMIC peripheral register map.
+- interrupts : the interrupt mappings.
+		The format should be
+		<slave-id peripheral-id interrupt-number>.
+- interrupt-names : names for the mapped fg adc interrupts
+		The following interrupts are required:
+		0: mem-avail
+
+Example:
+pmi8994_fg: qcom,fg {
+	compatible = "qcom,qpnp-fg";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	status = "disabled";
+	qcom,pmic-revid = <&pmi8994_revid>;
+
+	qcom,fg-soc@4000 {
+		reg = <0x4000 0x100>;
+		interrupts =	<0x2 0x40 0x0>,
+				<0x2 0x40 0x1>,
+				<0x2 0x40 0x2>,
+				<0x2 0x40 0x3>,
+				<0x2 0x40 0x4>,
+				<0x2 0x40 0x5>,
+				<0x2 0x40 0x6>,
+				<0x2 0x40 0x7>;
+
+		interrupt-names =	"high-soc",
+					"low-soc",
+					"full-soc",
+					"empty-soc",
+					"delta-soc",
+					"first-est-done",
+					"sw-fallbk-ocv",
+					"sw-fallbk-new-batt";
+	};
+
+	qcom,fg-batt@4100 {
+		reg = <0x4100 0x100>;
+		interrupts =	<0x2 0x41 0x0>,
+				<0x2 0x41 0x1>,
+				<0x2 0x41 0x2>,
+				<0x2 0x41 0x3>,
+				<0x2 0x41 0x4>,
+				<0x2 0x41 0x5>,
+				<0x2 0x41 0x6>,
+				<0x2 0x41 0x7>;
+
+		interrupt-names =	"soft-cold",
+					"soft-hot",
+					"vbatt-low",
+					"batt-ided",
+					"batt-id-req",
+					"batt-unknown",
+					"batt-missing",
+					"batt-match";
+	};
+
+	qcom,fg-adc-vbat@4254 {
+		reg = <0x4254 0x1>;
+	};
+
+	qcom,fg-adc-ibat@4255 {
+		reg = <0x4255 0x1>;
+	};
+
+	qcom,fg-memif@4400 {
+		reg = <0x4400 0x100>;
+		interrupts =	<0x2 0x44 0x0>,
+				<0x2 0x44 0x1>;
+
+		interrupt-names =	"mem-avail",
+					"data-rcvry-sug";
+
+		qcom,cold-hot-jeita-hysteresis = <30 50>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
index 05fa6e4..8795aff 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt
@@ -137,12 +137,6 @@
 		be based off battery voltage. For both SOC and battery voltage,
 		charger receives the signal from FG to resume charging.
 
-- qcom,micro-usb
-  Usage:      optional
-  Value type: <empty>
-  Definition: Boolean flag which indicates that the platform only support
-		micro usb port.
-
 - qcom,suspend-input-on-debug-batt
   Usage:      optional
   Value type: <empty>
@@ -183,6 +177,15 @@
   Value type: bool
   Definition: Boolean flag which when present enables sw compensation for jeita
 
+- qcom,battery-data
+  Usage:      optional
+  Value type: <u32>
+  Definition: Specifies the phandle of the node which contains the battery
+		profiles supported on the device. This is only specified
+		when step charging and sw-jeita configurations are desired
+		to be get from these properties defined in battery profile:
+		qcom,step-chg-ranges, qcom,jeita-fcc-ranges, qcom,jeita-fv-ranges.
+
 =============================================
 Second Level Nodes - SMB2 Charger Peripherals
 =============================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
new file mode 100644
index 0000000..efd64cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smbcharger.txt
@@ -0,0 +1,394 @@
+QPNP SMB Battery Charger
+
+QPNP SMB Charger is a single-cell switching mode battery charger. It can charge
+the battery and power the system via the USB and AC adapter input.
+
+The QPNP SMB Charger interfaces via the SPMI bus.
+
+There are six different peripherals adding the following functionality.
+Each of these peripherals are implemented as subnodes in the example at the
+end of this file.
+
+- qcom,chgr:		Supports charging control and status
+			reporting.
+- qcom,bat-if:		Battery status reporting such as presence,
+			temperature reporting and voltage collapse
+			protection.
+- qcom,usb-chgpth:	USB charge path detection and input current
+			limiting configuration.
+- qcom,dc-chgpth:	DC charge path detection and input current
+			limiting configuration.
+- qcom,chg-misc:	Miscellaneous features such as watchdog timers
+			and SYSOK pin control
+- qcom,chg-otg:		OTG configuration control.
+
+Parent node required properties:
+- compatible:		Must be "qcom,qpnp-smbcharger"
+- #address-cells:	Must be <1>
+- #size-cells:		Must be <1>
+- qcom,pmic-revid:      Should specify the phandle of PMIC
+                        revid module. This is used to identify
+                        the PMIC subtype.
+
+
+
+Sub node required properties:
+- reg:			The SPMI address for this peripheral
+- interrupts:		Specifies the interrupt associated with the peripheral.
+- interrupt-names:	Specifies the interrupt names for the peripheral. Every
+			available interrupt needs to have an associated name
+			with it to indentify its purpose.
+
+			The following lists each subnode and their corresponding
+			required interrupt names:
+
+			qcom,chgr:
+			 - chg-tcc-thr:		Triggers on charge completion.
+			 - chg-taper-thr:	Triggers on the taper charge
+						transtion.
+			 - chg-inhibit:		Notifies on battery voltage
+						being too high to resume
+						charging.
+			 - chg-p2f-thr:		Triggers on transitioning from
+						precharge to fastcharge.
+			 - chg-rechg-thr:	Triggers on battery voltage
+						falling below the resume
+						threshold.
+
+			qcom,bat-if:
+			 - batt-hot:		Triggers on battery temperature
+						hitting the hot threshold.
+						Charging stops.
+			 - batt-warm:		Triggers on battery temperature
+						hitting the warm threshold.
+						Charging current is reduced.
+			 - batt-cool:		Triggers on battery temperature
+						hitting the cool threshold.
+						Charging current is reduced
+			 - batt-cold:		Triggers on battery temperature
+						hitting the cold threshold.
+						Charging stops.
+			 - batt-missing:	Battery missing status
+						interrupt.
+			 - batt-low:		Triggers on battery voltage
+						falling across a low threshold.
+
+			qcom,usb-chgpth:
+			 - usbin-uv:		USB input voltage falls below a
+						valid threshold.
+			 - usbin-src-det:	USB automatic source detection
+						finishes.
+
+			qcom,dc-chgpth:
+			 - dcin-uv:		DC input voltage falls below a
+						valid threshold.
+
+			qcom,chgr-misc:
+			 - wdog-timeout-mins:	Charger watchdog timer
+						interrupt.
+			 - temp-shutdown:	Triggers when charger goes
+						overtemp and causes a shutdown.
+			 - power-ok:		Triggers when the charger
+						switcher turns on or off.
+
+Regulator Subnodes:
+- qcom,smbcharger-boost-otg	A subnode for a regulator device that turns on
+				the charger boost for OTG operation.
+- qcom,smbcharger-external-otg	A subnode for a regulator device that switches
+				off charging and the USB input charge path
+				in order to allow an external regulator to
+				operate. This can be used in place of the
+				qcom,smbcharger-boost-otg if an external boost
+				is available.
+
+Regulator Sub node required properties:
+- regulator-name		A name string for the regulator in question
+
+Optional Properties:
+- qcom,battery-psy-name		The name of the main battery power supply that
+				the charger will register. Failing to define
+				this property will default the name to
+				"battery".
+- qcom,bms-psy-name		The psy name to use for reporting battery
+				capacity. If left unspecified the capacity uses
+				a preprogrammed default value of 50.
+- qcom,float-voltage-mv		Float Voltage in mV - the maximum voltage up
+				to which the battery is charged. Supported
+				range 3600mV to 4500mV
+- qcom,float-voltage-comp	Specifies the JEITA float voltage compensation.
+				Value ranges from 0 to 63.
+- qcom,fastchg-current-ma	Specifies the fast charge current in mA. Supported
+				range is from 300mA to 3000mA.
+- qcom,fastchg-current-comp	Specifies the fast charge current compensation in
+				mA. Supported values are 250, 700, 900 and 1200mA.
+- qcom,charging-timeout-mins	Maximum duration in minutes that a single
+				charge cycle may last.  Supported values are:
+				0, 192, 384, 768, and 1536.  A value of 0
+				means that no charge cycle timeout is used and
+				charging can continue indefinitely.
+- qcom,precharging-timeout-mins	Maximum duration in minutes that a single
+				precharge cycle may last.  Supported values
+				are: 0, 24, 48, 96, 192.  A value of 0 means
+				that no precharge cycle timeout is used and
+				charging can continue indefinitely. Note that
+				the qcom,charging-timeout-mins property must
+				be specified in order for this to take effect.
+- qcom,dc-psy-type		The type of charger connected to the DC path.
+				Can be "Mains", "Wireless" or "Wipower"
+- qcom,dc-psy-ma		The current in mA dc path can support. Must be
+				specified if dc-psy-type is specified. Valid
+				range 300mA to 2000mA.
+- qcom,dcin-vadc		The phandle to pmi8994 voltage adc. The ADC is
+				used to get notifications when the DCIN voltage
+				crosses a programmed min/max threshold. This is
+				used to make configurations for optimized power
+				draw for Wipower.
+- qcom,wipower-div2-ilim-map
+- qcom,wipower-pt-ilim-map
+- qcom,wipower-default-ilim-map
+				Array of 5 elements to indicate the voltage ranges and their corresponding
+				current limits. The 5 elements with index [0..4] are:
+				[0] => voltage_low in uV
+				[1] => voltage_high in uV
+				[2] => current limit for pass through in mA
+				[3] => current limit for div2 mode dcin low voltage in mA
+				[4] => current limit for div2 mode dcin high voltage in mA
+				The div2 and pt tables indicate the current limits
+				to use when Wipower is operating in divide_by_2 mode
+				and pass through mode respectively.
+				The default table is used when the voltage ranges
+				are beyond the ones specified in the mapping table.
+				Note that if dcin-vadc or any of these mapping
+				tables are not specified, dynamic dcin input
+				is disabled.
+- qcom,charging-disabled	Set this if charging should be disabled in the
+				build by default.
+- qcom,resume-delta-mv		Specifies the minimum voltage drop in
+				millivolts below the float voltage that is
+				required in order to initiate a new charging
+				cycle. Supported values are: 50, 100, 200 and
+				300mV.
+- qcom,chg-inhibit-en		Boolean that indicates whether the charge inhibit
+				feature needs to be enabled. If this is not set,
+				charge inhibit feature is disabled by default.
+- qcom,chg-inhibit-fg		Indicates if the recharge threshold source has
+				to be Fuel gauge ADC. If this is not set, it
+				will be analog sensor by default.
+- qcom,bmd-algo-disabled	Indicates if the battery missing detection
+				algorithm is disabled. If this node is present
+				SMB uses the THERM pin for battery missing
+				detection.
+- qcom,charge-unknown-battery	Boolean that indicates whether an unknown
+				battery without a matching profile will be
+				charged. If this is not set, if the fuel gauge
+				does not recognize the battery based on its
+				battery ID, the charger will not start
+				charging.
+- qcom,bmd-pin-src		A string that indicates the source pin for the
+				battery missind detection. This can be either:
+				- "bpd_none"
+					battery is considered always present
+				- "bpd_id"
+					battery id pin is used
+				- "bpd_thm"
+					battery therm pin is used
+				- "bpd_thm_id"
+					both pins are used (battery is
+					considered missing if either pin is
+					floating).
+- qcom,iterm-ma			Specifies the termination current to indicate
+				end-of-charge. Possible values in mA:
+				50, 100, 150, 200, 250, 300, 500, 600.
+- qcom,iterm-disabled		Disables the termination current feature. This
+				is a boolean property.
+- otg-parent-supply		A phandle to an external boost regulator for
+				OTG if it exists.
+- qcom,thermal-mitigation:	Array of input current limit values for
+				different system thermal mitigation levels.
+				This should be a flat array that denotates the
+				maximum charge current in mA for each thermal
+				level.
+- qcom,rparasitics-uohm:	The parasitic resistance of the board following
+				the line from the battery connectors through
+				vph_power. This is used to calculate maximum
+				available current of the battery.
+- qcom,vled-max-uv:		The maximum input voltage of the flash leds.
+				This is used to calculate maximum available
+				current of the battery.
+- qcom,autoadjust-vfloat	A boolean property that when set, makes the
+				driver automatically readjust vfloat using the
+				fuel gauge ADC readings to make charging more
+				accurate.
+- qcom,jeita-temp-hard-limit	property when present will enable or disable
+				the jeita temperature hard limit based on the
+				value 1 or 0. Specify 0 if the jeita temp hard
+				limit needs to be disabled. If it is not present,
+				jeita temperature hard limit will be based on what
+				the bootloader had set earlier.
+- qcom,low-volt-dcin:		A boolean property which upon set will enable the
+				AICL deglitch configuration dynamically. This needs
+				to be set if the DCIN supply is going to be less
+				than or equal to 5V.
+- qcom,force-aicl-rerun:	A boolean property which upon set will enable the
+				AICL rerun by default along with the deglitch time
+				configured to long interval (20 ms). Also, specifying
+				this property will not adjust the AICL deglitch time
+				dynamically for handling the battery over-voltage
+				oscillations when the charger is headroom limited.
+- qcom,aicl-rerun-period-s	If force-aicl-rerun is on, this property dictates
+				how often aicl is reran in seconds. Possible values
+				are 45, 90, 180, and 360.
+- qcom,ibat-ocp-threshold-ua	Maximum current before the battery will trigger
+				overcurrent protection. Use the recommended
+				battery pack value minus some margin.
+- qcom,soft-vfloat-comp-disabled	Set this property when the battery is
+					powered via external source and could
+					go above the float voltage.
+- qcom,parallel-usb-min-current-ma	Minimum current drawn by the primary
+					charger before enabling the parallel
+					charger if one exists. Do not define
+					this property if no parallel chargers
+					exist.
+- qcom,parallel-usb-9v-min-current-ma	Minimum current drawn by the primary
+					charger before enabling the parallel
+					charger if one exists. This property
+					applies only for 9V chargers.
+- qcom,parallel-allowed-lowering-ma	Acceptable current drop from the initial limit
+					to keep parallel charger activated. If the
+					charger current reduces beyond this threshold
+					parallel charger is disabled. Must be specified
+					if parallel charger is used.
+- qcom,parallel-main-chg-fcc-percent	Percentage of the fast charge current allotted to the
+					main charger when parallel charging is enabled and
+					operational. If this property is not defined, the
+					driver defaults to a 50%/50% split between the main
+					and parallel charger.
+- qcom,parallel-main-chg-icl-percent	Percentage of the input current allotted to the
+					main charger when parallel charging is enabled and
+					operational. If this property is not defined, the
+					driver defaults to a 60%/40% split between the main
+					and parallel charger.
+- qcom,battery-data			Points to the phandle of node which
+					contains the battery-profiles supported
+					by the charger/FG.
+- qcom,chg-led-support		A bool property to support the charger led feature.
+- qcom,chg-led-sw-controls		A bool property to allow the software to control
+					the charger led without a valid charger.
+- qcom,skip-usb-notification	A boolean property to be used when usb gets present
+				and type from other means. Especially true on
+				liquid hardware, where usb presence is detected based on GPIO.
+- qcom,skip-usb-suspend-for-fake-battery	A boolean property to skip
+						suspending USB path for fake
+						battery.
+- qcom,vchg_sns-vadc		Phandle of the VADC node.
+- qcom,vchg-adc-channel-id	The ADC channel to which the VCHG is routed.
+
+Example:
+	qcom,qpnp-smbcharger {
+		compatible = "qcom,qpnp-smbcharger";
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		qcom,iterm-ma = <100>;
+		qcom,float-voltage-mv = <4200>;
+		qcom,resume-delta-mv = <100>;
+		qcom,bmd-pin-src = "bpd_thm_id";
+		qcom,dc-psy-type = "Mains";
+		qcom,dc-psy-ma = <1500>;
+		qcom,bms-psy-name = "bms";
+		qcom,battery-psy-name = "battery";
+		qcom,thermal-mitigation = <1500 700 600 325>;
+		qcom,vchg_sns-vadc = <&pmi8950_vadc>;
+		qcom,vchg-adc-channel-id = <3>;
+
+		qcom,chgr@1000 {
+			reg = <0x1000 0x100>;
+			interrupts =	<0x2 0x10 0x0>,
+					<0x2 0x10 0x1>,
+					<0x2 0x10 0x2>,
+					<0x2 0x10 0x3>,
+					<0x2 0x10 0x4>,
+					<0x2 0x10 0x5>,
+					<0x2 0x10 0x6>,
+					<0x2 0x10 0x7>;
+
+			interrupt-names =	"chg-error",
+						"chg-inhibit",
+						"chg-prechg-sft",
+						"chg-complete-chg-sft",
+						"chg-p2f-thr",
+						"chg-rechg-thr",
+						"chg-taper-thr",
+						"chg-tcc-thr";
+		};
+
+		qcom,otg@1100 {
+			reg = <0x1100 0x100>;
+		};
+
+		qcom,bat-if@1200 {
+			reg = <0x1200 0x100>;
+			interrupts =	<0x2 0x12 0x0>,
+					<0x2 0x12 0x1>,
+					<0x2 0x12 0x2>,
+					<0x2 0x12 0x3>,
+					<0x2 0x12 0x4>,
+					<0x2 0x12 0x5>,
+					<0x2 0x12 0x6>,
+					<0x2 0x12 0x7>;
+
+			interrupt-names =	"batt-hot",
+						"batt-warm",
+						"batt-cold",
+						"batt-cool",
+						"batt-ov",
+						"batt-low",
+						"batt-missing",
+						"batt-term-missing";
+		};
+
+		qcom,usb-chgpth@1300 {
+			reg = <0x1300 0x100>;
+			interrupts =	<0x2 0x13 0x0>,
+					<0x2 0x13 0x1>,
+					<0x2 0x13 0x2>,
+					<0x2 0x13 0x3>,
+					<0x2 0x13 0x4>,
+					<0x2 0x13 0x5>,
+					<0x2 0x13 0x6>;
+
+			interrupt-names =	"usbin-uv",
+						"usbin-ov",
+						"usbin-src-det",
+						"otg-fail",
+						"otg-oc",
+						"aicl-done",
+						"usbid-change";
+		};
+
+		qcom,dc-chgpth@1400 {
+			reg = <0x1400 0x100>;
+			interrupts =	<0x2 0x14 0x0>,
+					<0x2 0x14 0x1>;
+
+			interrupt-names =	"dcin-uv",
+						"dcin-ov";
+		};
+
+		qcom,chgr-misc@1600 {
+			reg = <0x1600 0x100>;
+			interrupts =	<0x2 0x16 0x0>,
+					<0x2 0x16 0x1>,
+					<0x2 0x16 0x2>,
+					<0x2 0x16 0x3>,
+					<0x2 0x16 0x4>,
+					<0x2 0x16 0x5>;
+
+			interrupt-names =	"power-ok",
+						"temp-shutdown",
+						"wdog-timeout",
+						"flash-fail",
+						"otst2",
+						"otst3";
+		};
+	};
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb1355-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb1355-charger.txt
index ca584e5..abbb981 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb1355-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb1355-charger.txt
@@ -29,6 +29,13 @@
   Definition: Should specify the phandle of SMB's revid module. This is used
 	      to identify the SMB subtype.
 
+- qcom,disable-ctm
+  Usage:      optional
+  Value type: <empty>
+  Definition: boolean flag. Usually a thermistor near usb/typeC connector is
+	      connected to AUX. Set this flag to indicate the thermistor
+	      doesn't exist.
+
 ================================================
 Second Level Nodes - SMB1355 Charger Peripherals
 ================================================
diff --git a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
index 92ef23c..fd2729f 100644
--- a/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
+++ b/Documentation/devicetree/bindings/power/supply/qcom/smb138x-charger.txt
@@ -43,6 +43,13 @@
   Definition: Boolean flag which indicates that the charger should not draw
 	      current from any of its input sources (USB, DC).
 
+- qcom,use-extcon
+  Usage:      optional
+  Value type: <empty>
+  Definition: Boolean flag which specify that smb138x will act as main charger
+	      to do extcon USB calls. If not defined, other charger driver can
+	      act as main charger to do extcon USB calls.
+
 - qcom,fcc-max-ua
   Usage:      optional
   Value type: <u32>
diff --git a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
index 29bb2d3..05792b0 100644
--- a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
@@ -165,7 +165,7 @@
 		    and Turbo.
 
 - qcom,cpr-fuse-combos
-	Usage:      required
+	Usage:      optional
 	Value type: <u32>
 	Definition: Specifies the number of fuse combinations being supported by
 		    the device.  This value is utilized by several other
@@ -178,6 +178,11 @@
 		    The last 8 fuse combos correspond to speed bin fuse value 7
 		    along with CPR revision fuse values 0 to 7.
 
+		    This property must be specified unless qcom,cpr-fuse-combo-map
+		    is present. In that case, qcom,cpr-fuse-combos is implicitly
+		    assumed to have a value equal to the number of tuple lists (rows)
+		    found in the qcom,cpr-fuse-combo-map property.
+
 - qcom,cpr-speed-bins
 	Usage:      optional
 	Value type: <u32>
@@ -368,6 +373,27 @@
 		    speed bins 1-to-1 or exactly 1 list which is used regardless
 		    of the speed bin found on a given chip.
 
+- qcom,cpr-fuse-combo-map
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists where each tuple list (row)
+		    defines a mapping from combinations of fuse parameter ranges
+		    to fuse combo ID (i.e., map row index). Each tuple defines the
+		    beginning and ending fuse parameter value that matches. The
+		    number of tuples in each row is equal to the number of selection
+		    fuse parameters used in fuse combo logic. For MSM8953, the fuse
+		    parameters are "speed-bin", "cpr fuse revision", and "foundry id".
+		    The tuples in each row correspond to the fuses in order:
+		    "speed-bin", "cpr fuse revision" and "foundry id". An example entry
+		    for speed-bin '3', cpr fuse revisions >= '2', and foundry '2' is
+		    as shown below:
+		         <3 3>, <2 7>, <2 2>
+
+		    The number of rows in the property is arbitrary but used to size
+		    other properties. qcom,cpr-fuse-combos must be set to the number
+		    of rows specified in this property. For msm8953, the maximum number
+		    of rows for this property is 512 (8 * 8 * 8).
+
 =======
 Example
 =======
@@ -414,6 +440,8 @@
 			"APCS_ALIAS0_APM_CTLER_STATUS",
 			"APCS0_CPR_CORE_ADJ_MODE_REG";
 
+	qcom,cpr-aging-ref-voltage = <990000>; /* Turbo corner */
+
 	thread@0 {
 		qcom,cpr-thread-id = <0>;
 		qcom,cpr-consecutive-up = <1>;
@@ -517,6 +545,14 @@
 				<(-20000) (-15000) (-10000) 0>;
 			qcom,allow-boost =
 				<1>;
+
+			qcom,cpr-aging-max-voltage-adjustment = <15000>;
+			qcom,cpr-aging-ref-corner = <6>; /* Turbo corner */
+			qcom,cpr-aging-ro-scaling-factor = <2800>;
+			qcom,cpr-aging-derate =
+				<1000 1000 1000 1000 1000
+				 1000 1000 1000>;
+			qcom,allow-aging-voltage-adjustment = <1>;
 		};
 	};
 };
diff --git a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
index 5515457..f4f549a 100644
--- a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
@@ -159,6 +159,21 @@
 				not specified, then "qcom,override-cornerX-reg-config" must contain a single
 				register configuration sequence list which is then applied unconditionally.
 				This property can only be specified if qcom,cornerX-reg-config property is already defined.
+- qcom,override-acc-range-fuse-list:	Array of tuples define the selection parameters used for selecting the override
+				mem-acc configuration. The fused values for these selection parameters are used by the
+				qcom,override-fuse-range-map to identify the correct set of override properties.
+				Each tuple contains 4 elements as defined below:
+				  [0] => the fuse row number of the selector
+				  [1] => LSB bit position of the bits
+				  [2] => number of bits
+				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
+- qcom,override-fuse-range-map:	Array of tuples where each tuple specifies the allowed range for all the selection parameters
+				defined in qcom,override-acc-range-fuse-list. The fused values of these selection parameters
+				are compared against their allowed range in each tuple starting from 0th tuple and use the
+				first matched tuple index to select the right tuples from the other override properties.
+				Either qcom,override-fuse-range-map or qcom,override-fuse-version-map is used to select
+				the override configuration. The qcom,override-fuse-range-map is used if both the
+				properties are specified.
 
 mem_acc_vreg_corner: regulator@fd4aa044 {
 	compatible = "qcom,mem-acc-regulator";
@@ -184,6 +199,13 @@
 	qcom,override-fuse-version-map = <0>,
 					 <2>,
 					 <(-1)>;
+	qcom,override-acc-range-fuse-list =
+					<37 40 3 0>,
+					<36 30 8 0>;
+	qcom,override-fuse-range-map =
+				<0 0>, <  0   0>, <49 63>,
+				<1 1>, <  0   0>, <50 63>,
+				<0 1>, < 95 255>, < 0 63>;
 	qcom,override-corner-acc-map =	<0 0 1>,
 					<0 1 2>,
 					<0 1 1>;
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index c9cfc88..795ee95 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -82,8 +82,10 @@
 - qcom,qpnp-lab-init-lcd-voltage: 	The default output voltage when LAB regulator
 					is configured in lcd mode.
 - qcom,qpnp-lab-ps-threshold:		The threshold in mA of Pulse Skip Mode for
-					LAB regulator. Supported values are 20, 30,
-					40 and 50.
+					LAB regulator. Supported values for
+					PMI8994/6 are 20, 30, 40 and 50.
+					Supported values for PMI8998/PM660A are
+					50, 60, 70 and 80.
 - interrupts:				Specify the interrupts as per the interrupt
 					encoding.
 					Currently "lab-vreg-ok" is required for
diff --git a/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt b/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
index 9deb7d4..7de891e 100644
--- a/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/rpmh-regulator.txt
@@ -1,12 +1,13 @@
 Qualcomm Technologies, Inc. RPMh Regulators
 
-rpmh-regulator devices support PMIC regulator management via the VRM and ARC
-RPMh accelerators.  The APPS processor communicates with these hardware blocks
-via an RSC using command packets.  The VRM allows changing four parameters for
-a given regulator: enable state, output voltage, operating mode, and minimum
-headroom voltage.  The ARC allows changing only a single parameter for a given
-regulator: its operating level.  This operating level is fed into CPR which then
-decides upon a final explicit voltage for the regulator.
+rpmh-regulator devices support PMIC regulator management via the VRM, ARC and
+XOB RPMh accelerators.  The APPS processor communicates with these hardware
+blocks via an RSC using command packets.  The VRM allows changing four
+parameters for a given regulator: enable state, output voltage, operating mode,
+and minimum headroom voltage.  The ARC allows changing only a single parameter
+for a given regulator: its operating level.  This operating level is fed into
+CPR which then decides upon a final explicit voltage for the regulator.  The XOB
+allows changing only a single parameter for a given regulator: its enable state.
 
 =======================
 Required Node Structure
@@ -24,9 +25,10 @@
 - compatible
 	Usage:      required
 	Value type: <string>
-	Definition: Must be "qcom,rpmh-vrm-regulator" or
-		    "qcom,rpmh-arc-regulator" depending upon the hardware type,
-		    VRM or ARC, of the RPMh managed regulator resource.
+	Definition: Must be "qcom,rpmh-vrm-regulator", "qcom,rpmh-arc-regulator"
+		    or "qcom,rpmh-xob-regulator" depending upon the hardware
+		    type, VRM, ARC or XOB, of the RPMh managed regulator
+		    resource.
 
 - mboxes
 	Usage:      required
@@ -116,8 +118,8 @@
  - regulator-enable-ramp-delay
 	Usage:      optional
 	Value type: <u32>
-	Definition: For VRM resources, the time in microseconds to delay after
-		    enabling a regulator.
+	Definition: For VRM and XOB resources, the time in microseconds to delay
+		    after enabling a regulator.
 
 - qcom,set
 	Usage:      required
@@ -130,7 +132,7 @@
 		    one of RPMH_REGULATOR_SET_* (i.e. 1, 2, or 3).
 
 - qcom,init-enable
-	Usage:      optional; VRM regulators only
+	Usage:      optional; VRM and XOB regulators only
 	Value type: <u32>
 	Definition: Specifies the initial enable state to request for a VRM
 		    regulator.  Supported values are 0 (regulator disabled) and
@@ -267,3 +269,15 @@
 		qcom,init-voltage = <1000000>;
 	};
 };
+
+rpmh-regulator-ldoc1 {
+	compatible = "qcom,rpmh-xob-regulator";
+	mboxes = <&apps_rsc 0>;
+	qcom,resource-name = "ldoc1";
+	pm855l_l1: regulator-pm855l-l1 {
+		regulator-name = "pm855l_l1";
+		qcom,set = <RPMH_REGULATOR_SET_ALL>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/serial/msm_serial_hs.txt b/Documentation/devicetree/bindings/serial/msm_serial_hs.txt
new file mode 100644
index 0000000..031be45
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/msm_serial_hs.txt
@@ -0,0 +1,121 @@
+* Qualcomm MSM HSUART
+
+Required properties:
+- compatible :
+	- "qcom,msm-hsuart-v14" to be used for UARTDM Core v1.4
+- reg : offset and length of the register set for both the device,
+	uart core and bam core
+- reg-names :
+	- "core_mem" to be used as name of the uart core
+	- "bam_mem" to be used as name of the bam core
+- interrupts : interrupts for both the device,uart core and bam core
+- interrupt-names :
+	- "core_irq" to be used as uart irq
+	- "bam irq" to be used as bam irq
+- #interrupt-cells: Specifies the number of cells needed to encode an interrupt
+		source. The type shall be a <u32> and the value shall be 1
+- #address-cells: Specifies the number of cells needed to encode an address.
+		The type shall be <u32> and the value shall be 0
+- interrupt-parent = It is needed for interrupt mapping
+- bam-tx-ep-pipe-index : BAM TX Endpoint Pipe Index for HSUART
+- bam-rx-ep-pipe-index : BAM RX Endpoint Pipe Index for HSUART
+
+BLSP has a static pipe allocation and assumes a pair-pipe for each uart core.
+Pipes [2*i : 2*i+1] are allocated for UART cores where i = [0 : 5].
+Hence, Minimum and Maximum permitted value of endpoint pipe index to be used
+with uart core is 0 and 11 respectively.
+
+There is one HSUART block used in MSM devices,
+"qcom,msm-hsuart-v14". The msm-serial-hs driver is
+able to handle this, and matches against the "qcom,msm-hsuart-v14"
+as the compatibility.
+
+The registers for the "qcom,msm-hsuart-v14" device need to specify both
+register blocks - uart core and bam core.
+
+Example:
+
+	uart7: uart@f995d000 {
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0xf995d000 0x1000>,
+		      <0xf9944000 0x5000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&uart7>;
+		interrupts = <0 1>;
+		#interrupt-cells = <1>;
+		interrupt-map = <0 &intc 0 113 0
+				1 &intc 0 239 0>
+		qcom,bam-tx-ep-pipe-index = <0>;
+		qcom,bam-rx-ep-pipe-index = <1>;
+	};
+
+Optional properties:
+- qcom,<gpio-name>-gpio : handle to the GPIO node, see "gpios property" in
+Documentation/devicetree/bindings/gpio/gpio.txt.
+"gpio-name" can be "tx", "rx", "cts" and "rfr" based on number of UART GPIOs
+need to configured.
+Gpio's are optional if it is required to be not configured by UART driver or
+case where there is nothing connected and we want to use internal loopback mode
+for uart.
+- qcom, wakeup_irq : UART RX GPIO IRQ line to be configured as wakeup source.
+- qcom,inject-rx-on-wakeup : inject_rx_on_wakeup enables feature where on
+receiving interrupt with UART RX GPIO IRQ line (i.e. above wakeup_irq property),
+HSUART driver injects provided character with property rx_to_inject.
+- qcom, rx-char-to-inject : The character to be inserted on wakeup.
+- qcom, no-suspend-delay : This decides system to go to suspend immediately
+or not
+
+- Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+below optional properties:
+    - qcom,msm_bus,name
+    - qcom,msm_bus,num_cases
+    - qcom,msm_bus,active_only
+    - qcom,msm_bus,num_paths
+    - qcom,msm_bus,vectors
+
+Aliases :
+An alias may be optionally used to bind the UART device to a TTY device
+(ttyHS<alias_num>) with a given alias number. Aliases are of the form
+uart<n> where <n> is an integer representing the alias number to use.
+On systems with multiple UART devices present, an alias may optionally be
+defined for such devices. The alias value should be from 0 to 255.
+
+Example:
+
+	aliases {
+		uart4 = &uart7; // This device will be enumerated as ttyHS4
+	};
+
+	uart7: uart@f995d000 {
+		compatible = "qcom,msm-hsuart-v14"
+		reg = <0x19c40000 0x1000">,
+		      <0xf9944000 0x5000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&uart7>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 113 0
+				1 &intc 0 239 0
+				2 &msmgpio 42 0>;
+		qcom,tx-gpio = <&msmgpio 41 0x00>;
+		qcom,rx-gpio = <&msmgpio 42 0x00>;
+		qcom,cts-gpio = <&msmgpio 43 0x00>;
+		qcom,rfr-gpio = <&msmgpio 44 0x00>;
+		qcom,inject-rx-on-wakeup = <1>;
+		qcom,rx-char-to-inject = <0xFD>;
+
+		qcom,bam-tx-ep-pipe-index = <0>;
+		qcom,bam-rx-ep-pipe-index = <1>;
+
+		qcom,msm-bus,name = "uart7";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<84 512 0 0>,
+				<84 512 500 800>;
+	};
diff --git a/Documentation/devicetree/bindings/soc/qcom/dcc.txt b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
index 8a9761c..5150459 100644
--- a/Documentation/devicetree/bindings/soc/qcom/dcc.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/dcc.txt
@@ -35,6 +35,14 @@
 		  "atb"	  : To send captured data over ATB to a trace sink
 		  "sram"  : To save captured data in dcc internal SRAM.
 
+- qcom,curr-link-list: int, To specify the link list to use for the default list.
+
+- qcom,link-list: The values to be programmed into the default link list.
+		  The enum values for DCC operations is defined in dt-bindings/soc/qcom,dcc_v2.h
+		  The following gives basic structure to be used for each operation:
+		  <DCC_operation addr val apb_bus>
+		  val is to be interpreted based on what operation is to be performed.
+
 Example:
 
 	dcc: dcc@4b3000 {
@@ -47,6 +55,13 @@
 		clocks = <&clock_gcc clk_gcc_dcc_ahb_clk>;
 		clock-names = "dcc_clk";
 
+		qcom,curr-link-list = <2>;
+		qcom,link-list = <DCC_READ 0x1740300 6 0>,
+				 <DCC_READ 0x1620500 4 0>,
+				 <DCC_READ 0x7840000 1 0>,
+				 <DCC_READ 0x7841010 12 0>,
+				 <DCC_READ 0x7842000 16 0>,
+				 <DCC_READ 0x7842500 2 0>;
 		qcom,save-reg;
 	};
 
diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
index 800508a..bf2a91a 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
@@ -10,6 +10,13 @@
  - interrupts:  Interrupt number
  - reg: Should be address and size of EUD register space
  - reg-names: Should be "eud_base"
+ - clocks: a list of phandles to the PHY clocks. Use as per
+   Documentation/devicetree/bindings/clock/clock-bindings.txt
+ - clock-names: Names of the clocks in 1-1 correspondence with
+   the "clocks" property.
+ - <supply-name>-supply: phandle to the regulator device tree node
+   Required "supply-name" examples are:
+	"vdda33" : 3.3v supply to eud.
 
 Driver notifies clients via extcon for VBUS spoof attach/detach
 and charger enable/disable events. Clients registered for these
@@ -23,6 +30,9 @@
 		interrupts = <GIC_SPI 492 IRQ_TYPE_LEVEL_HIGH>;
 		reg = <0x88e0000 0x4000>;
 		reg-names = "eud_base";
+		clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+		clock-names = "cfg_ahb_clk";
+		vdda33-supply = <&pm8998_l24>;
 	};
 
 An example for EUD extcon client:
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 4d05e50..34c2963 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -2016,6 +2016,66 @@
 		qcom,aux-codec = <&stub_codec>;
 	};
 
+* SDX ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,sdx-asoc-snd-tavil"
+- qcom,model : The user-visible name of this sound card.
+- qcom,prim_mi2s_aux_master : Handle to prim_master pinctrl configurations
+- qcom,prim_mi2s_aux_slave : Handle to prim_slave pinctrl configurations
+- qcom,sec_mi2s_aux_master : Handle to sec_master pinctrl configurations
+- qcom,sec_mi2s_aux_slave : Handle to sec_slave pinctrl configurations
+- asoc-platform: This is phandle list containing the references to platform device
+		nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		the platform names should match to that of the phandle order
+		given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+		that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		cpu dai names should match to that of the phandle order give
+		in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		where the id (%d) field represents the back-end AFE port id that
+		this CPU dai is associated with.
+
+Example:
+
+	sound-tavil {
+		compatible = "qcom,sdx-asoc-snd-tavil";
+		qcom,model = "sdx-tavil-i2s-snd-card";
+		qcom,prim_mi2s_aux_master = <&prim_master>;
+		qcom,prim_mi2s_aux_slave = <&prim_slave>;
+		qcom,sec_mi2s_aux_master = <&sec_master>;
+		qcom,sec_mi2s_aux_slave = <&sec_slave>;
+
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&hostless>, <&afe>, <&routing>,
+				<&pcm_dtmf>, <&host_pcm>, <&compress>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice",
+				"msm-pcm-loopback", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-pcm-routing",
+				"msm-pcm-dtmf", "msm-voice-host-pcm",
+				"msm-compress-dsp";
+		asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&mi2s_sec>,
+				<&dtmf_tx>,
+				<&rx_capture_tx>, <&rx_playback_rx>,
+				<&tx_capture_tx>, <&tx_playback_rx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>,
+				<&dai_sec_auxpcm>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-stub-dev.4", "msm-dai-stub-dev.5",
+				"msm-dai-stub-dev.6", "msm-dai-stub-dev.7",
+				"msm-dai-stub-dev.8", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-auxpcm.2";
+	};
+
 * APQ8096 Automotive ASoC Machine driver
 
 Required properties:
@@ -2459,6 +2519,10 @@
 		6-pole-jack : Jack on the hardware is 6-pole.
 - clock-names : clock name defined for external clock.
 - clocks : external clock defined for codec clock.
+- qcom,msm-mbhc-hs-mic-max-threshold-mv : headset detection threshold. When micbias is
+					  not set to 2.7v, need scale in driver.
+- qcom,msm-mbhc-hs-mic-min-threshold-mv : headhpone detection threshold. When micbias is
+					  not set to 2.7v, need scale in driver.
 - qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers.
 - qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset.
 - qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
@@ -2516,6 +2580,8 @@
 
 		qcom,msm-mbhc-hphl-swh = <0>;
 		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-mbhc-hs-mic-max-threshold-mv = <1700>;
+		qcom,msm-mbhc-hs-mic-min-threshold-mv = <50>;
 		qcom,hph-en0-gpio = <&tavil_hph_en0>;
 		qcom,hph-en1-gpio = <&tavil_hph_en1>;
 		qcom,tavil-mclk-clk-freq = <9600000>;
@@ -2634,6 +2700,11 @@
 - qcom,cdc-comp-gpios : phandle for compander gpios.
 - qcom,cdc-dmic-gpios : phandle for Digital mic clk and data gpios.
 - qcom,cdc-sdw-gpios : phandle for soundwire clk and data gpios.
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
 - qcom,msm-mbhc-moist-cfg: This property is used to set moisture detection
 		threshold values for different codecs. First parameter is V(voltage)
 		second one is i(current), third one is r (resistance). Depending on the
@@ -2740,6 +2811,11 @@
 - qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
 - qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
 - qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+- qcom,pri-mi2s-gpios : phandle for primary MI2S clk, word select and data gpios.
+- qcom,sec-mi2s-gpios : phandle for secondary MI2S clk, word select and data gpios.
+- qcom,tert-mi2s-gpios : phandle for tertiary MI2S clk, word select and data gpios.
+- qcom,quat-mi2s-gpios : phandle for quaternary MI2S clk, word select and data gpios.
+- qcom,quin-mi2s-gpios : phandle for quinary MI2S clk, word select and data gpios.
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/sound/wcd_codec.txt b/Documentation/devicetree/bindings/sound/wcd_codec.txt
index c848ab5..6d2ae5e 100644
--- a/Documentation/devicetree/bindings/sound/wcd_codec.txt
+++ b/Documentation/devicetree/bindings/sound/wcd_codec.txt
@@ -3,7 +3,7 @@
 Required properties:
 
  - compatible : "qcom,tasha-slim-pgd" or "qcom,tasha-i2c-pgd" for Tasha Codec
-		or "qcom,tavil-slim-pgd" for Tavil Codec
+		"qcom,tavil-slim-pgd" or "qcom,tavil-i2c-pgd" for Tavil Codec
  - elemental-addr: codec slimbus slave PGD enumeration address.(48 bits)
 
  - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
diff --git a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
index cd2d2ea..866d004 100644
--- a/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
+++ b/Documentation/devicetree/bindings/spi/qcom,spi-geni-qcom.txt
@@ -25,6 +25,10 @@
 		     Documentation/devicetree/bindings/spi/spi-bus.txt
 - qcom,wrapper-core: Wrapper QUPv3 core containing this SPI controller.
 
+Optional properties:
+- qcom,rt:	Specifies if the framework worker thread for this
+		controller device should have "real-time" priority.
+
 SPI slave nodes must be children of the SPI master node and can contain
 properties described in Documentation/devicetree/bindings/spi/spi-bus.txt
 
diff --git a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
index bef9193..c50d678 100644
--- a/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
+++ b/Documentation/devicetree/bindings/spmi/qcom,spmi-pmic-arb.txt
@@ -42,6 +42,11 @@
     cell 4: interrupt flags indicating level-sense information, as defined in
             dt-bindings/interrupt-controller/irq.h
 
+Optional properties:
+- qcom,enable-ahb-bus-workaround : Boolean flag which indicates that the AHB bus
+				   workaround sequence should be used for SPMI
+				   write transactions to avoid corruption
+
 Example V1 PMIC-Arbiter:
 
 	spmi {
diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index 4901fa0..7f79f40 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -23,6 +23,8 @@
                           with "phys" attribute, provides phandle to UFS PHY node
 - vdd-hba-supply        : phandle to UFS host controller supply regulator node
 - vcc-supply            : phandle to VCC supply regulator node
+- vcc-voltage-level     : specifies voltage levels for VCC supply.
+                          Should be specified in pairs (min, max), units uV.
 - vccq-supply           : phandle to VCCQ supply regulator node
 - vccq2-supply          : phandle to VCCQ2 supply regulator node
 - vcc-supply-1p8        : For embedded UFS devices, valid VCC range is 1.7-1.95V
@@ -71,6 +73,8 @@
 			  3: 52 MHz
 			  Defaults to 26 MHz if not specified.
 - extcon:       phandle to external connector (Refer Documentation/devicetree/bindings/extcon/extcon-gpio.txt for more details).
+- non-removable		: defines if the connected ufs device is not removable
+
 
 Note: If above properties are not defined it can be assumed that the supply
 regulators or clocks are always on.
diff --git a/Documentation/devicetree/bindings/uio/msm_sharedmem.txt b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
index 749c6e85..4c89846 100644
--- a/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
+++ b/Documentation/devicetree/bindings/uio/msm_sharedmem.txt
@@ -9,10 +9,18 @@
 - reg-names : Indicates various client-names.
 - qcom,client-id : The client id for the QMI clients.
 
+Optional properties:
+- qcom,guard-memory:	If this dtsi property is set, then the shared memory
+			region will be guarded by SZ_4K at the start and at the end.
+			This is needed to overcome the XPU limitation on few MSM HW,
+			so as to make this memory not contiguous with other allocations
+			that may possibly happen from other clients.
+
 Example:
 	qcom,msm_sharedmem@0dc80000 {
 		compatible = "qcom,sharedmem-uio";
 		reg = <0x0dc80000 0x00180000>,
 		reg-names = "rmtfs";
 		qcom,client-id = <0x00000001>;
+		qcom,guard-memory;
 	};
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 609d853..5d3b232 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -58,6 +58,9 @@
 	gating. Default it is enabled.
  - snps,xhci-imod-value: Interrupt moderation interval for host mode
 	(in increments of 250nsec).
+ - usb-core-id: Differentiates between different controllers present on a device.
+ - snps,bus-suspend-enable: If present then controller supports low power mode
+	during bus suspend.
 
 This is usually a subnode to DWC3 glue to which it is connected.
 
diff --git a/Documentation/devicetree/bindings/usb/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index 6109fad..9ee2cc6 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -159,6 +159,7 @@
    "efuse_addr": EFUSE address to read and update analog tune parameter.
    "emu_phy_base" : phy base address used for programming emulation target phy.
    "ref_clk_addr" : ref_clk bcr address used for on/off ref_clk before reset.
+   "refgen_north_bg_reg" : address used to read REFGEN status for overriding QUSB PHY register.
  - clocks: a list of phandles to the PHY clocks. Use as per
    Documentation/devicetree/bindings/clock/clock-bindings.txt
  - clock-names: Names of the clocks in 1-1 correspondence with the "clocks"
@@ -191,7 +192,8 @@
 			 0x210 /* QUSB2PHY_PWR_CTRL1 */
 			 0x230 /* QUSB2PHY_INTR_CTRL */
 			 0x0a8 /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE */
-			 0x254>; /* QUSB2PHY_TEST1 */
+			 0x254 /* QUSB2PHY_TEST1 */
+			 0x198>; /* QUSB2PHY_PLL_BIAS_CONTROL_2 */
 		qcom,efuse-bit-pos = <21>;
 		qcom,efuse-num-bits = <3>;
 
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 3bdc8967..86c9259 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -38,6 +38,7 @@
 auo	AU Optronics Corporation
 auvidea Auvidea GmbH
 avago	Avago Technologies
+avia	avia semiconductor
 avic	Shanghai AVIC Optoelectronics Co., Ltd.
 axis	Axis Communications AB
 boe	BOE Technology Group Co., Ltd.
@@ -154,6 +155,7 @@
 kyo	Kyocera Corporation
 lacie	LaCie
 lantiq	Lantiq Semiconductor
+lego	LEGO Systems A/S
 lenovo	Lenovo Group Ltd.
 lg	LG Corporation
 linux	Linux-specific binding
diff --git a/Makefile b/Makefile
index 3849d63..6f6262b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 4
 PATCHLEVEL = 9
-SUBLEVEL = 41
+SUBLEVEL = 62
 EXTRAVERSION =
 NAME = Roaring Lionus
 
diff --git a/arch/alpha/include/asm/types.h b/arch/alpha/include/asm/types.h
index 4cb4b6d..0bc66e1 100644
--- a/arch/alpha/include/asm/types.h
+++ b/arch/alpha/include/asm/types.h
@@ -1,6 +1,6 @@
 #ifndef _ALPHA_TYPES_H
 #define _ALPHA_TYPES_H
 
-#include <asm-generic/int-ll64.h>
+#include <uapi/asm/types.h>
 
 #endif /* _ALPHA_TYPES_H */
diff --git a/arch/alpha/include/uapi/asm/types.h b/arch/alpha/include/uapi/asm/types.h
index 9fd3cd4..8d1024d 100644
--- a/arch/alpha/include/uapi/asm/types.h
+++ b/arch/alpha/include/uapi/asm/types.h
@@ -9,8 +9,18 @@
  * need to be careful to avoid a name clashes.
  */
 
-#ifndef __KERNEL__
+/*
+ * This is here because we used to use l64 for alpha
+ * and we don't want to impact user mode with our change to ll64
+ * in the kernel.
+ *
+ * However, some user programs are fine with this.  They can
+ * flag __SANE_USERSPACE_TYPES__ to get int-ll64.h here.
+ */
+#if !defined(__SANE_USERSPACE_TYPES__) && !defined(__KERNEL__)
 #include <asm-generic/int-l64.h>
+#else
+#include <asm-generic/int-ll64.h>
 #endif
 
 #endif /* _UAPI_ALPHA_TYPES_H */
diff --git a/arch/arc/include/asm/cache.h b/arch/arc/include/asm/cache.h
index b3410ff..4fd6272 100644
--- a/arch/arc/include/asm/cache.h
+++ b/arch/arc/include/asm/cache.h
@@ -89,7 +89,9 @@
 #define ARC_REG_SLC_FLUSH	0x904
 #define ARC_REG_SLC_INVALIDATE	0x905
 #define ARC_REG_SLC_RGN_START	0x914
+#define ARC_REG_SLC_RGN_START1	0x915
 #define ARC_REG_SLC_RGN_END	0x916
+#define ARC_REG_SLC_RGN_END1	0x917
 
 /* Bit val in SLC_CONTROL */
 #define SLC_CTRL_IM		0x040
diff --git a/arch/arc/kernel/entry.S b/arch/arc/kernel/entry.S
index 1eea99b..85d9ea4 100644
--- a/arch/arc/kernel/entry.S
+++ b/arch/arc/kernel/entry.S
@@ -92,6 +92,12 @@
 	lr  r0, [efa]
 	mov r1, sp
 
+	; hardware auto-disables MMU, re-enable it to allow kernel vaddr
+	; access for say stack unwinding of modules for crash dumps
+	lr	r3, [ARC_REG_PID]
+	or	r3, r3, MMU_ENABLE
+	sr	r3, [ARC_REG_PID]
+
 	lsr  	r3, r2, 8
 	bmsk 	r3, r3, 7
 	brne    r3, ECR_C_MCHK_DUP_TLB, 1f
diff --git a/arch/arc/mm/cache.c b/arch/arc/mm/cache.c
index 8147583..bbdfeb3 100644
--- a/arch/arc/mm/cache.c
+++ b/arch/arc/mm/cache.c
@@ -562,6 +562,7 @@
 	static DEFINE_SPINLOCK(lock);
 	unsigned long flags;
 	unsigned int ctrl;
+	phys_addr_t end;
 
 	spin_lock_irqsave(&lock, flags);
 
@@ -591,8 +592,16 @@
 	 * END needs to be setup before START (latter triggers the operation)
 	 * END can't be same as START, so add (l2_line_sz - 1) to sz
 	 */
-	write_aux_reg(ARC_REG_SLC_RGN_END, (paddr + sz + l2_line_sz - 1));
-	write_aux_reg(ARC_REG_SLC_RGN_START, paddr);
+	end = paddr + sz + l2_line_sz - 1;
+	if (is_pae40_enabled())
+		write_aux_reg(ARC_REG_SLC_RGN_END1, upper_32_bits(end));
+
+	write_aux_reg(ARC_REG_SLC_RGN_END, lower_32_bits(end));
+
+	if (is_pae40_enabled())
+		write_aux_reg(ARC_REG_SLC_RGN_START1, upper_32_bits(paddr));
+
+	write_aux_reg(ARC_REG_SLC_RGN_START, lower_32_bits(paddr));
 
 	while (read_aux_reg(ARC_REG_SLC_CTRL) & SLC_CTRL_BUSY);
 
diff --git a/arch/arc/mm/tlb.c b/arch/arc/mm/tlb.c
index bdb295e..a4dc881 100644
--- a/arch/arc/mm/tlb.c
+++ b/arch/arc/mm/tlb.c
@@ -896,9 +896,6 @@
 
 	local_irq_save(flags);
 
-	/* re-enable the MMU */
-	write_aux_reg(ARC_REG_PID, MMU_ENABLE | read_aux_reg(ARC_REG_PID));
-
 	/* loop thru all sets of TLB */
 	for (set = 0; set < mmu->sets; set++) {
 
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 21c66eb..d8d8b82 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -55,6 +55,7 @@
 	select HAVE_FTRACE_MCOUNT_RECORD if (!XIP_KERNEL)
 	select HAVE_FUNCTION_GRAPH_TRACER if (!THUMB2_KERNEL)
 	select HAVE_FUNCTION_TRACER if (!XIP_KERNEL)
+	select HAVE_FUTEX_CMPXCHG if FUTEX
 	select HAVE_GCC_PLUGINS
 	select HAVE_GENERIC_DMA_COHERENT
 	select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7))
@@ -571,6 +572,7 @@
        select USE_OF
        select PINCTRL
        select ARCH_WANT_KMAP_ATOMIC_FLUSH
+       select SND_SOC_COMPRESS
        help
          Support for Qualcomm MSM/QSD based systems.  This runs on the
          apps processor of the MSM/QSD and depends on a shared memory
@@ -1486,6 +1488,7 @@
 
 config HOTPLUG_CPU
 	bool "Support for hot-pluggable CPUs"
+	select GENERIC_IRQ_MIGRATION
 	depends on SMP
 	help
 	  Say Y here to experiment with turning CPUs off and on.  CPUs
diff --git a/arch/arm/Kconfig-nommu b/arch/arm/Kconfig-nommu
index aed66d5..b757634 100644
--- a/arch/arm/Kconfig-nommu
+++ b/arch/arm/Kconfig-nommu
@@ -34,8 +34,7 @@
 	  used instead of the auto-probing which utilizes the register.
 
 config REMAP_VECTORS_TO_RAM
-	bool 'Install vectors to the beginning of RAM' if DRAM_BASE
-	depends on DRAM_BASE
+	bool 'Install vectors to the beginning of RAM'
 	help
 	  The kernel needs to change the hardware exception vectors.
 	  In nommu mode, the hardware exception vectors are normally
diff --git a/arch/arm/boot/dts/am335x-chilisom.dtsi b/arch/arm/boot/dts/am335x-chilisom.dtsi
index f9ee585..1b43ebd 100644
--- a/arch/arm/boot/dts/am335x-chilisom.dtsi
+++ b/arch/arm/boot/dts/am335x-chilisom.dtsi
@@ -124,6 +124,14 @@
 
 &rtc {
 	system-power-controller;
+
+	pinctrl-0 = <&ext_wakeup>;
+	pinctrl-names = "default";
+
+	ext_wakeup: ext-wakeup {
+		pins = "ext_wakeup0";
+		input-enable;
+	};
 };
 
 /* NAND Flash */
diff --git a/arch/arm/boot/dts/armada-375.dtsi b/arch/arm/boot/dts/armada-375.dtsi
index cc952cf..024f1b7 100644
--- a/arch/arm/boot/dts/armada-375.dtsi
+++ b/arch/arm/boot/dts/armada-375.dtsi
@@ -176,9 +176,9 @@
 				reg = <0x8000 0x1000>;
 				cache-unified;
 				cache-level = <2>;
-				arm,double-linefill-incr = <1>;
+				arm,double-linefill-incr = <0>;
 				arm,double-linefill-wrap = <0>;
-				arm,double-linefill = <1>;
+				arm,double-linefill = <0>;
 				prefetch-data = <1>;
 			};
 
diff --git a/arch/arm/boot/dts/armada-388-gp.dts b/arch/arm/boot/dts/armada-388-gp.dts
index 895fa6c..563901e 100644
--- a/arch/arm/boot/dts/armada-388-gp.dts
+++ b/arch/arm/boot/dts/armada-388-gp.dts
@@ -75,7 +75,7 @@
 					pinctrl-names = "default";
 					pinctrl-0 = <&pca0_pins>;
 					interrupt-parent = <&gpio0>;
-					interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
+					interrupts = <18 IRQ_TYPE_LEVEL_LOW>;
 					gpio-controller;
 					#gpio-cells = <2>;
 					interrupt-controller;
@@ -87,7 +87,7 @@
 					compatible = "nxp,pca9555";
 					pinctrl-names = "default";
 					interrupt-parent = <&gpio0>;
-					interrupts = <18 IRQ_TYPE_EDGE_FALLING>;
+					interrupts = <18 IRQ_TYPE_LEVEL_LOW>;
 					gpio-controller;
 					#gpio-cells = <2>;
 					interrupt-controller;
diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index 2d76688..c60cfe9 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -143,9 +143,9 @@
 				reg = <0x8000 0x1000>;
 				cache-unified;
 				cache-level = <2>;
-				arm,double-linefill-incr = <1>;
+				arm,double-linefill-incr = <0>;
 				arm,double-linefill-wrap = <0>;
-				arm,double-linefill = <1>;
+				arm,double-linefill = <0>;
 				prefetch-data = <1>;
 			};
 
diff --git a/arch/arm/boot/dts/armada-39x.dtsi b/arch/arm/boot/dts/armada-39x.dtsi
index 34cba87..aeecfa7 100644
--- a/arch/arm/boot/dts/armada-39x.dtsi
+++ b/arch/arm/boot/dts/armada-39x.dtsi
@@ -111,9 +111,9 @@
 				reg = <0x8000 0x1000>;
 				cache-unified;
 				cache-level = <2>;
-				arm,double-linefill-incr = <1>;
+				arm,double-linefill-incr = <0>;
 				arm,double-linefill-wrap = <0>;
-				arm,double-linefill = <1>;
+				arm,double-linefill = <0>;
 				prefetch-data = <1>;
 			};
 
diff --git a/arch/arm/boot/dts/bcm953012k.dts b/arch/arm/boot/dts/bcm953012k.dts
index 05a985a..6208e85 100644
--- a/arch/arm/boot/dts/bcm953012k.dts
+++ b/arch/arm/boot/dts/bcm953012k.dts
@@ -48,7 +48,7 @@
 	};
 
 	memory {
-		reg = <0x00000000 0x10000000>;
+		reg = <0x80000000 0x10000000>;
 	};
 };
 
diff --git a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
index 8aa19ba..5282d69 100644
--- a/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
+++ b/arch/arm/boot/dts/exynos4412-odroid-common.dtsi
@@ -97,11 +97,11 @@
 	thermal-zones {
 		cpu_thermal: cpu-thermal {
 			cooling-maps {
-				map0 {
+				cooling_map0: map0 {
 				     /* Corresponds to 800MHz at freq_table */
 				     cooling-device = <&cpu0 7 7>;
 				};
-				map1 {
+				cooling_map1: map1 {
 				     /* Corresponds to 200MHz at freq_table */
 				     cooling-device = <&cpu0 13 13>;
 			       };
diff --git a/arch/arm/boot/dts/exynos4412-odroidu3.dts b/arch/arm/boot/dts/exynos4412-odroidu3.dts
index 99634c5..7504a5a 100644
--- a/arch/arm/boot/dts/exynos4412-odroidu3.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidu3.dts
@@ -13,6 +13,7 @@
 
 /dts-v1/;
 #include "exynos4412-odroid-common.dtsi"
+#include "exynos4412-prime.dtsi"
 
 / {
 	model = "Hardkernel ODROID-U3 board based on Exynos4412";
@@ -47,11 +48,11 @@
 			cooling-maps {
 				map0 {
 				     trip = <&cpu_alert1>;
-				     cooling-device = <&cpu0 7 7>;
+				     cooling-device = <&cpu0 9 9>;
 				};
 				map1 {
 				     trip = <&cpu_alert2>;
-				     cooling-device = <&cpu0 13 13>;
+				     cooling-device = <&cpu0 15 15>;
 				};
 				map2 {
 				     trip = <&cpu_alert0>;
diff --git a/arch/arm/boot/dts/exynos4412-odroidx2.dts b/arch/arm/boot/dts/exynos4412-odroidx2.dts
index 4d22885..d6e92eb 100644
--- a/arch/arm/boot/dts/exynos4412-odroidx2.dts
+++ b/arch/arm/boot/dts/exynos4412-odroidx2.dts
@@ -12,6 +12,7 @@
 */
 
 #include "exynos4412-odroidx.dts"
+#include "exynos4412-prime.dtsi"
 
 / {
 	model = "Hardkernel ODROID-X2 board based on Exynos4412";
diff --git a/arch/arm/boot/dts/exynos4412-prime.dtsi b/arch/arm/boot/dts/exynos4412-prime.dtsi
new file mode 100644
index 0000000..e75bc17
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4412-prime.dtsi
@@ -0,0 +1,41 @@
+/*
+ * Samsung's Exynos4412 Prime SoC device tree source
+ *
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * Exynos4412 Prime SoC revision supports higher CPU frequencies than
+ * non-Prime version.  Therefore we need to update OPPs table and
+ * thermal maps accordingly.
+ */
+
+&cpu0_opp_1500 {
+	/delete-property/turbo-mode;
+};
+
+&cpu0_opp_table {
+	opp@1600000000 {
+		opp-hz = /bits/ 64 <1600000000>;
+		opp-microvolt = <1350000>;
+		clock-latency-ns = <200000>;
+	};
+	opp@1704000000 {
+		opp-hz = /bits/ 64 <1704000000>;
+		opp-microvolt = <1350000>;
+		clock-latency-ns = <200000>;
+	};
+};
+
+&cooling_map0 {
+	cooling-device = <&cpu0 9 9>;
+};
+
+&cooling_map1 {
+	cooling-device = <&cpu0 15 15>;
+};
diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi
index 40beede..3ebdf01 100644
--- a/arch/arm/boot/dts/exynos4412.dtsi
+++ b/arch/arm/boot/dts/exynos4412.dtsi
@@ -130,7 +130,7 @@
 			opp-microvolt = <1287500>;
 			clock-latency-ns = <200000>;
 		};
-		opp@1500000000 {
+		cpu0_opp_1500: opp@1500000000 {
 			opp-hz = /bits/ 64 <1500000000>;
 			opp-microvolt = <1350000>;
 			clock-latency-ns = <200000>;
diff --git a/arch/arm/boot/dts/imx53-qsb-common.dtsi b/arch/arm/boot/dts/imx53-qsb-common.dtsi
index c05e7cf..40b3e31 100644
--- a/arch/arm/boot/dts/imx53-qsb-common.dtsi
+++ b/arch/arm/boot/dts/imx53-qsb-common.dtsi
@@ -215,16 +215,16 @@
 
 		pinctrl_fec: fecgrp {
 			fsl,pins = <
-				MX53_PAD_FEC_MDC__FEC_MDC		0x80000000
-				MX53_PAD_FEC_MDIO__FEC_MDIO		0x80000000
-				MX53_PAD_FEC_REF_CLK__FEC_TX_CLK	0x80000000
-				MX53_PAD_FEC_RX_ER__FEC_RX_ER		0x80000000
-				MX53_PAD_FEC_CRS_DV__FEC_RX_DV		0x80000000
-				MX53_PAD_FEC_RXD1__FEC_RDATA_1		0x80000000
-				MX53_PAD_FEC_RXD0__FEC_RDATA_0		0x80000000
-				MX53_PAD_FEC_TX_EN__FEC_TX_EN		0x80000000
-				MX53_PAD_FEC_TXD1__FEC_TDATA_1		0x80000000
-				MX53_PAD_FEC_TXD0__FEC_TDATA_0		0x80000000
+				MX53_PAD_FEC_MDC__FEC_MDC		0x4
+				MX53_PAD_FEC_MDIO__FEC_MDIO		0x1fc
+				MX53_PAD_FEC_REF_CLK__FEC_TX_CLK	0x180
+				MX53_PAD_FEC_RX_ER__FEC_RX_ER		0x180
+				MX53_PAD_FEC_CRS_DV__FEC_RX_DV		0x180
+				MX53_PAD_FEC_RXD1__FEC_RDATA_1		0x180
+				MX53_PAD_FEC_RXD0__FEC_RDATA_0		0x180
+				MX53_PAD_FEC_TX_EN__FEC_TX_EN		0x4
+				MX53_PAD_FEC_TXD1__FEC_TDATA_1		0x4
+				MX53_PAD_FEC_TXD0__FEC_TDATA_0		0x4
 			>;
 		};
 
diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 18596a2..77c6b93 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -174,4 +174,40 @@
 		clocks = <&uart_clk>;
 		status = "disabled";
 	};
+
+	mmsys: syscon@14000000 {
+		compatible = "mediatek,mt2701-mmsys", "syscon";
+		reg = <0 0x14000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	imgsys: syscon@15000000 {
+		compatible = "mediatek,mt2701-imgsys", "syscon";
+		reg = <0 0x15000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	vdecsys: syscon@16000000 {
+		compatible = "mediatek,mt2701-vdecsys", "syscon";
+		reg = <0 0x16000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	hifsys: syscon@1a000000 {
+		compatible = "mediatek,mt2701-hifsys", "syscon";
+		reg = <0 0x1a000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	ethsys: syscon@1b000000 {
+		compatible = "mediatek,mt2701-ethsys", "syscon";
+		reg = <0 0x1b000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	bdpsys: syscon@1c000000 {
+		compatible = "mediatek,mt2701-bdpsys", "syscon";
+		reg = <0 0x1c000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
 };
diff --git a/arch/arm/boot/dts/qcom/Makefile b/arch/arm/boot/dts/qcom/Makefile
index 7eb0c7f..c51581d 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -1,17 +1,17 @@
 
-dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb
+dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb \
+	sdxpoorwills-cdp.dtb \
+	sdxpoorwills-mtp.dtb
 
-
-ifeq ($(CONFIG_ARM64),y)
-always          := $(dtb-y)
-subdir-y        := $(dts-dirs)
-else
 targets += dtbs
 targets += $(addprefix ../, $(dtb-y))
 
 $(obj)/../%.dtb: $(src)/%.dts FORCE
 	$(call if_changed_dep,dtc)
 
+include $(srctree)/arch/arm64/boot/dts/qcom/Makefile
+$(obj)/../%.dtb: $(src)/../../../../arm64/boot/dts/qcom/%.dts FORCE
+	$(call if_changed_dep,dtc)
+
 dtbs: $(addprefix $(obj)/../,$(dtb-y))
-endif
 clean-files := *.dtb
diff --git a/arch/arm/boot/dts/qcom/msm-smb138x.dtsi b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi
new file mode 100644
index 0000000..fa21dd7
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/msm-smb138x.dtsi
@@ -0,0 +1,137 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <dt-bindings/interrupt-controller/irq.h>
+
+&i2c_7 {
+	status = "okay";
+	smb138x: qcom,smb138x@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 = "smb138x";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+
+		smb138x_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		smb138x_tadc: qcom,tadc@3600 {
+			compatible = "qcom,tadc";
+			reg = <0x3600 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			#io-channel-cells = <1>;
+			interrupt-parent = <&smb138x>;
+			interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "eoc";
+
+			batt_temp@0 {
+				reg = <0>;
+				qcom,rbias = <68100>;
+				qcom,rtherm-at-25degc = <68000>;
+				qcom,beta-coefficient = <3450>;
+			};
+
+			skin_temp@1 {
+				reg = <1>;
+				qcom,rbias = <33000>;
+				qcom,rtherm-at-25degc = <68000>;
+				qcom,beta-coefficient = <3450>;
+			};
+
+			die_temp@2 {
+				reg = <2>;
+				qcom,scale = <(-1306)>;
+				qcom,offset = <397904>;
+			};
+
+			batt_i@3 {
+				reg = <3>;
+				qcom,channel = <3>;
+				qcom,scale = <(-20000000)>;
+			};
+
+			batt_v@4 {
+				reg = <4>;
+				qcom,scale = <5000000>;
+			};
+
+			input_i@5 {
+				reg = <5>;
+				qcom,scale = <14285714>;
+			};
+
+			input_v@6 {
+				reg = <6>;
+				qcom,scale = <25000000>;
+			};
+
+			otg_i@7 {
+				reg = <7>;
+				qcom,scale = <5714286>;
+			};
+		};
+
+		smb1381_charger: qcom,smb1381-charger@1000 {
+			compatible = "qcom,smb138x-parallel-slave";
+			qcom,pmic-revid = <&smb138x_revid>;
+			reg = <0x1000 0x700>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			interrupt-parent = <&smb138x>;
+			io-channels =
+				<&smb138x_tadc 1>,
+				<&smb138x_tadc 2>,
+				<&smb138x_tadc 3>,
+				<&smb138x_tadc 14>,
+				<&smb138x_tadc 15>,
+				<&smb138x_tadc 16>,
+				<&smb138x_tadc 17>;
+			io-channel-names =
+				"connector_temp",
+				"charger_temp",
+				"batt_i",
+				"connector_temp_thr1",
+				"connector_temp_thr2",
+				"connector_temp_thr3",
+				"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";
+			};
+		};
+	};
+};
+
+&smb1381_charger {
+	smb138x_vbus: qcom,smb138x-vbus {
+		status = "disabled";
+		regulator-name = "smb138x-vbus";
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/pm8950.dtsi b/arch/arm/boot/dts/qcom/pm8950.dtsi
new file mode 100644
index 0000000..f47872a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/pm8950.dtsi
@@ -0,0 +1,388 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&spmi_bus {
+	qcom,pm8950@0 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x0 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm8950_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		pm8950_temp_alarm: qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0>;
+			label = "pm8950_tz";
+			qcom,channel-num = <8>;
+			qcom,threshold-set = <0>;
+			qcom,temp_alarm-vadc = <&pm8950_vadc>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			interrupts = <0x0 0x8 0x0>,
+				<0x0 0x8 0x1>,
+				<0x0 0x8 0x4>,
+				<0x0 0x8 0x5>;
+			interrupt-names = "kpdpwr", "resin",
+				"resin-bark", "kpdpwr-resin-bark";
+			qcom,pon-dbc-delay = <15625>;
+			qcom,system-reset;
+
+			qcom,pon_1 {
+				qcom,pon-type = <0>;
+				qcom,pull-up = <1>;
+				linux,code = <116>;
+			};
+
+			qcom,pon_2 {
+				qcom,pon-type = <1>;
+				qcom,pull-up = <1>;
+				linux,code = <114>;
+			};
+		};
+
+		pm8950_coincell: qcom,coincell@2800 {
+			compatible = "qcom,qpnp-coincell";
+			reg = <0x2800 0x100>;
+		};
+
+		pm8950_mpps: mpps {
+			compatible = "qcom,qpnp-pin";
+			spmi-dev-container;
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8950-mpp";
+
+			mpp@a000 {
+				reg = <0xa000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			mpp@a100 {
+				/* MPP2 - PA_THERM config */
+				reg = <0xa100 0x100>;
+				qcom,pin-num = <2>;
+				qcom,mode = <4>; /* AIN input */
+				qcom,invert = <1>; /* Enable MPP */
+				qcom,ain-route = <1>; /* AMUX 6 */
+				qcom,master-en = <1>;
+				qcom,src-sel = <0>; /* Function constant */
+			};
+
+			mpp@a200 {
+				reg = <0xa200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			mpp@a300 {
+				/* MPP4 - CASE_THERM config */
+				reg = <0xa300 0x100>;
+				qcom,pin-num = <4>;
+				qcom,mode = <4>; /* AIN input */
+				qcom,invert = <1>; /* Enable MPP */
+				qcom,ain-route = <3>; /* AMUX 8 */
+				qcom,master-en = <1>;
+				qcom,src-sel = <0>; /* Function constant */
+			};
+		};
+
+		pm8950_gpios: gpios {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8950-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			gpio@c200 {
+				reg = <0xc200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			gpio@c300 {
+				reg = <0xc300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+
+			gpio@c400 {
+				reg = <0xc400 0x100>;
+				qcom,pin-num = <5>;
+				status = "disabled";
+			};
+
+			gpio@c500 {
+				reg = <0xc500 0x100>;
+				qcom,pin-num = <6>;
+				status = "disabled";
+			};
+
+			gpio@c600 {
+				reg = <0xc600 0x100>;
+				qcom,pin-num = <7>;
+				status = "disabled";
+			};
+
+			gpio@c700 {
+				reg = <0xc700 0x100>;
+				qcom,pin-num = <8>;
+				status = "disabled";
+			};
+		};
+
+		pm8950_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,vadc-poll-eoc;
+			qcom,pmic-revid = <&pm8950_revid>;
+
+			chan@5 {
+				label = "vcoin";
+				reg = <5>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@7 {
+				label = "vph_pwr";
+				reg = <7>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@8 {
+				label = "die_temp";
+				reg = <8>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@9 {
+				label = "ref_625mv";
+				reg = <9>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@a {
+				label = "ref_1250v";
+				reg = <0xa>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@c {
+				label = "ref_buf_625mv";
+				reg = <0xc>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@36 {
+				label = "pa_therm0";
+				reg = <0x36>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@11 {
+				label = "pa_therm1";
+				reg = <0x11>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@32 {
+				label = "xo_therm";
+				reg = <0x32>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@3c {
+				label = "xo_therm_buf";
+				reg = <0x3c>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@13 {
+				label = "case_therm";
+				reg = <0x13>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+		};
+
+		pm8950_adc_tm: vadc@3400 {
+			compatible = "qcom,qpnp-adc-tm";
+			reg = <0x3400 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts =	<0x0 0x34 0x0>,
+					<0x0 0x34 0x3>,
+					<0x0 0x34 0x4>;
+			interrupt-names =	"eoc-int-en-set",
+						"high-thr-en-set",
+						"low-thr-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,adc_tm-vadc = <&pm8950_vadc>;
+			qcom,pmic-revid = <&pm8950_revid>;
+
+			chan@36 {
+				label = "pa_therm0";
+				reg = <0x36>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x48>;
+				qcom,thermal-node;
+			};
+
+			chan@7 {
+				label = "vph_pwr";
+				reg = <0x7>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x68>;
+			};
+		};
+
+		pm8950_rtc: qcom,pm8950_rtc {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-rtc";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-rtc-write = <0>;
+			qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+			qcom,pm8950_rtc_rw@6000 {
+				reg = <0x6000 0x100>;
+			};
+
+			qcom,pm8950_rtc_alarm@6100 {
+				reg = <0x6100 0x100>;
+				interrupts = <0x0 0x61 0x1>;
+			};
+		};
+
+		qcom,leds@a300 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xa300 0x100>;
+			label = "mpp";
+		};
+	};
+
+	pm8950_1: qcom,pm8950@1 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x1 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm8950_pwm: pwm@bc00 {
+			status = "disabled";
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbc00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <0>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/pmi8950.dtsi b/arch/arm/boot/dts/qcom/pmi8950.dtsi
new file mode 100644
index 0000000..0ec1f0b
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/pmi8950.dtsi
@@ -0,0 +1,641 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+	qcom,pmi8950@2 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x2 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pmi8950_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			qcom,secondary-pon-reset;
+			qcom,hard-reset-poweroff-type =
+				<PON_POWER_OFF_SHUTDOWN>;
+
+			pon_perph_reg: qcom,pon_perph_reg {
+				regulator-name = "pon_spare_reg";
+				qcom,pon-spare-reg-addr = <0x8c>;
+				qcom,pon-spare-reg-bit = <1>;
+			};
+		};
+
+		pmi8950_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x2 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,vadc-poll-eoc;
+
+			chan@0 {
+				label = "usbin";
+				reg = <0>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <4>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@1 {
+				label = "dcin";
+				reg = <1>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <4>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@3 {
+				label = "vchg_sns";
+				reg = <3>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@9 {
+				label = "ref_625mv";
+				reg = <9>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@a {
+				label = "ref_1250v";
+				reg = <0xa>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@d {
+				label = "chg_temp";
+				reg = <0xd>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <16>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@43 {
+				label = "usb_dp";
+				reg = <0x43>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@44 {
+				label = "usb_dm";
+				reg = <0x44>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+		};
+
+		pmi8950_gpios: gpios {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pmi8950-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+		};
+
+		pmi8950_mpps: mpps {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pmi8950-mpp";
+
+			mpp@a000 {
+				reg = <0xa000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			mpp@a100 {
+				reg = <0xa100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			mpp@a200 {
+				reg = <0xa200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			mpp@a300 {
+				reg = <0xa300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+		};
+
+		pmi8950_charger: qcom,qpnp-smbcharger {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-smbcharger";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			qcom,iterm-ma = <100>;
+			qcom,float-voltage-mv = <4200>;
+			qcom,resume-delta-mv = <200>;
+			qcom,chg-inhibit-fg;
+			qcom,rparasitic-uohm = <100000>;
+			qcom,bms-psy-name = "bms";
+			qcom,thermal-mitigation = <1500 700 600 0>;
+			qcom,parallel-usb-min-current-ma = <1400>;
+			qcom,parallel-usb-9v-min-current-ma = <900>;
+			qcom,parallel-allowed-lowering-ma = <500>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+			qcom,force-aicl-rerun;
+			qcom,aicl-rerun-period-s = <180>;
+			qcom,autoadjust-vfloat;
+
+			qcom,chgr@1000 {
+				reg = <0x1000 0x100>;
+				interrupts =	<0x2 0x10 0x0>,
+						<0x2 0x10 0x1>,
+						<0x2 0x10 0x2>,
+						<0x2 0x10 0x3>,
+						<0x2 0x10 0x4>,
+						<0x2 0x10 0x5>,
+						<0x2 0x10 0x6>,
+						<0x2 0x10 0x7>;
+
+				interrupt-names =	"chg-error",
+							"chg-inhibit",
+							"chg-prechg-sft",
+							"chg-complete-chg-sft",
+							"chg-p2f-thr",
+							"chg-rechg-thr",
+							"chg-taper-thr",
+							"chg-tcc-thr";
+			};
+
+			qcom,otg@1100 {
+				reg = <0x1100 0x100>;
+				interrupts =	<0x2 0x11 0x0>,
+						<0x2 0x11 0x1>,
+						<0x2 0x11 0x3>;
+				interrupt-names =	"otg-fail",
+							"otg-oc",
+						"usbid-change";
+			};
+
+			qcom,bat-if@1200 {
+				reg = <0x1200 0x100>;
+				interrupts =	<0x2 0x12 0x0>,
+						<0x2 0x12 0x1>,
+						<0x2 0x12 0x2>,
+						<0x2 0x12 0x3>,
+					<0x2 0x12 0x4>,
+						<0x2 0x12 0x5>,
+						<0x2 0x12 0x6>,
+						<0x2 0x12 0x7>;
+
+				interrupt-names =	"batt-hot",
+							"batt-warm",
+							"batt-cold",
+							"batt-cool",
+						"batt-ov",
+							"batt-low",
+							"batt-missing",
+							"batt-term-missing";
+			};
+
+			qcom,usb-chgpth@1300 {
+				reg = <0x1300 0x100>;
+				interrupts =	<0x2 0x13 0x0>,
+						<0x2 0x13 0x1>,
+					<0x2 0x13 0x2>,
+						<0x2 0x13 0x5>;
+
+				interrupt-names =	"usbin-uv",
+						"usbin-ov",
+							"usbin-src-det",
+							"aicl-done";
+			};
+
+			qcom,dc-chgpth@1400 {
+				reg = <0x1400 0x100>;
+				interrupts =	<0x2 0x14 0x0>,
+						<0x2 0x14 0x1>;
+				interrupt-names =	"dcin-uv",
+							"dcin-ov";
+			};
+
+			qcom,chgr-misc@1600 {
+				reg = <0x1600 0x100>;
+				interrupts =	<0x2 0x16 0x0>,
+						<0x2 0x16 0x1>,
+						<0x2 0x16 0x2>,
+					<0x2 0x16 0x3>,
+						<0x2 0x16 0x4>,
+						<0x2 0x16 0x5>;
+
+				interrupt-names =	"power-ok",
+							"temp-shutdown",
+							"wdog-timeout",
+							"flash-fail",
+							"otst2",
+							"otst3";
+			};
+
+			smbcharger_charger_otg: qcom,smbcharger-boost-otg {
+				regulator-name = "smbcharger_charger_otg";
+			};
+		};
+
+		pmi8950_fg: qcom,fg {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-fg";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,resume-soc = <95>;
+			status = "okay";
+			qcom,bcl-lm-threshold-ma = <127>;
+			qcom,bcl-mh-threshold-ma = <405>;
+			qcom,fg-iterm-ma = <150>;
+			qcom,fg-chg-iterm-ma = <100>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+			qcom,fg-cutoff-voltage-mv = <3500>;
+			qcom,cycle-counter-en;
+			qcom,capacity-learning-on;
+
+			qcom,fg-soc@4000 {
+			status = "okay";
+				reg = <0x4000 0x100>;
+				interrupts =	<0x2 0x40 0x0>,
+						<0x2 0x40 0x1>,
+						<0x2 0x40 0x2>,
+						<0x2 0x40 0x3>,
+						<0x2 0x40 0x4>,
+						<0x2 0x40 0x5>,
+						<0x2 0x40 0x6>;
+
+				interrupt-names =	"high-soc",
+							"low-soc",
+							"full-soc",
+							"empty-soc",
+							"delta-soc",
+							"first-est-done",
+							"update-soc";
+			};
+
+			qcom,fg-batt@4100 {
+				reg = <0x4100 0x100>;
+				interrupts =	<0x2 0x41 0x0>,
+						<0x2 0x41 0x1>,
+					<0x2 0x41 0x2>,
+						<0x2 0x41 0x3>,
+						<0x2 0x41 0x4>,
+						<0x2 0x41 0x5>,
+						<0x2 0x41 0x6>,
+						<0x2 0x41 0x7>;
+
+				interrupt-names =	"soft-cold",
+							"soft-hot",
+							"vbatt-low",
+							"batt-ided",
+							"batt-id-req",
+							"batt-unknown",
+							"batt-missing",
+							"batt-match";
+			};
+
+			qcom,revid-tp-rev@1f1 {
+				reg = <0x1f1 0x1>;
+			};
+
+			qcom,fg-memif@4400 {
+				status = "okay";
+				reg = <0x4400 0x100>;
+				interrupts =	<0x2 0x44 0x0>,
+						<0x2 0x44 0x2>;
+
+				interrupt-names =	"mem-avail",
+							"data-rcvry-sug";
+			};
+		};
+
+		bcl@4200 {
+			compatible = "qcom,msm-bcl";
+			reg = <0x4200 0xFF 0x88E 0x2>;
+			reg-names = "fg_user_adc", "pon_spare";
+			interrupts = <0x2 0x42 0x0>,
+					<0x2 0x42 0x1>;
+			interrupt-names = "bcl-high-ibat-int",
+					"bcl-low-vbat-int";
+			qcom,vbat-scaling-factor = <39000>;
+			qcom,vbat-gain-numerator = <1>;
+			qcom,vbat-gain-denominator = <128>;
+			qcom,vbat-polling-delay-ms = <100>;
+			qcom,ibat-scaling-factor = <39000>;
+			qcom,ibat-gain-numerator = <1>;
+			qcom,ibat-gain-denominator = <128>;
+			qcom,ibat-offset-numerator = <1200>;
+			qcom,ibat-offset-denominator = <1>;
+			qcom,ibat-polling-delay-ms = <100>;
+			qcom,inhibit-derating-ua = <550000>;
+		};
+
+		qcom,leds@a100 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xa100 0x100>;
+			label = "mpp";
+		};
+	};
+
+	qcom,pmi8950@3 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x3 SPMI_USID>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		pmi8950_pwm: pwm@b000 {
+			status = "disabled";
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb000 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <0>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+		};
+
+		labibb: qpnp-labibb-regulator {
+			status = "disabled";
+			spmi-dev-container;
+			compatible = "qcom,qpnp-labibb-regulator";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+
+			ibb_regulator: qcom,ibb@dc00 {
+				reg = <0xdc00 0x100>;
+				reg-names = "ibb_reg";
+				regulator-name = "ibb_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-ibb-min-voltage = <1400000>;
+				qcom,qpnp-ibb-step-size = <100000>;
+				qcom,qpnp-ibb-slew-rate = <2000000>;
+				qcom,qpnp-ibb-use-default-voltage;
+				qcom,qpnp-ibb-init-voltage = <5500000>;
+				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+				qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-ibb-soft-start = <1000>;
+
+				qcom,qpnp-ibb-discharge-resistor = <32>;
+				qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
+				qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
+				qcom,qpnp-ibb-en-discharge;
+
+				qcom,qpnp-ibb-full-pull-down;
+				qcom,qpnp-ibb-pull-down-enable;
+				qcom,qpnp-ibb-switching-clock-frequency =
+									<1480>;
+				qcom,qpnp-ibb-limit-maximum-current = <1550>;
+				qcom,qpnp-ibb-debounce-cycle = <16>;
+				qcom,qpnp-ibb-limit-max-current-enable;
+				qcom,qpnp-ibb-ps-enable;
+			};
+
+			lab_regulator: qcom,lab@de00 {
+				reg = <0xde00 0x100>;
+				reg-names = "lab";
+				regulator-name = "lab_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-lab-min-voltage = <4600000>;
+				qcom,qpnp-lab-step-size = <100000>;
+				qcom,qpnp-lab-slew-rate = <5000>;
+				qcom,qpnp-lab-use-default-voltage;
+				qcom,qpnp-lab-init-voltage = <5500000>;
+				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+				qcom,qpnp-lab-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-lab-soft-start = <800>;
+
+				qcom,qpnp-lab-full-pull-down;
+				qcom,qpnp-lab-pull-down-enable;
+				qcom,qpnp-lab-switching-clock-frequency =
+									<1600>;
+				qcom,qpnp-lab-limit-maximum-current = <800>;
+				qcom,qpnp-lab-limit-max-current-enable;
+				qcom,qpnp-lab-ps-threshold = <40>;
+				qcom,qpnp-lab-ps-enable;
+				qcom,qpnp-lab-nfet-size = <100>;
+				qcom,qpnp-lab-pfet-size = <100>;
+				qcom,qpnp-lab-max-precharge-time = <500>;
+			};
+
+		};
+
+		wled: qcom,leds@d800 {
+			compatible = "qcom,qpnp-wled";
+			reg = <0xd800 0x100>,
+				<0xd900 0x100>,
+				<0xdc00 0x100>,
+				<0xde00 0x100>;
+			reg-names = "qpnp-wled-ctrl-base",
+					"qpnp-wled-sink-base",
+					"qpnp-wled-ibb-base",
+					"qpnp-wled-lab-base";
+			interrupts = <0x3 0xd8 0x2>;
+			interrupt-names = "sc-irq";
+			status = "okay";
+			linux,name = "wled";
+			linux,default-trigger = "bkl-trigger";
+			qcom,fdbk-output = "auto";
+			qcom,vref-mv = <350>;
+			qcom,switch-freq-khz = <800>;
+			qcom,ovp-mv = <29500>;
+			qcom,ilim-ma = <980>;
+			qcom,boost-duty-ns = <26>;
+			qcom,mod-freq-khz = <9600>;
+			qcom,dim-mode = "hybrid";
+			qcom,dim-method = "linear";
+			qcom,hyb-thres = <625>;
+			qcom,sync-dly-us = <800>;
+			qcom,fs-curr-ua = <20000>;
+			qcom,led-strings-list = [00 01];
+			qcom,en-ext-pfet-sc-pro;
+			qcom,cons-sync-write-delay-us = <1000>;
+		};
+
+		flash_led: qcom,leds@d300 {
+			compatible = "qcom,qpnp-flash-led";
+			status = "okay";
+			reg = <0xd300 0x100>;
+			label = "flash";
+			qcom,headroom = <500>;
+			qcom,startup-dly = <128>;
+			qcom,clamp-curr = <200>;
+			qcom,pmic-charger-support;
+			qcom,self-check-enabled;
+			qcom,thermal-derate-enabled;
+			qcom,thermal-derate-threshold = <100>;
+			qcom,thermal-derate-rate = "5_PERCENT";
+			qcom,current-ramp-enabled;
+			qcom,ramp_up_step = "6P7_US";
+			qcom,ramp_dn_step = "6P7_US";
+			qcom,vph-pwr-droop-enabled;
+			qcom,vph-pwr-droop-threshold = <3000>;
+			qcom,vph-pwr-droop-debounce-time = <10>;
+			qcom,headroom-sense-ch0-enabled;
+			qcom,headroom-sense-ch1-enabled;
+			qcom,pmic-revid = <&pmi8950_revid>;
+
+			pmi8950_flash0: qcom,flash_0 {
+				label = "flash";
+				qcom,led-name = "led:flash_0";
+				qcom,default-led-trigger =
+						"flash0_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <0>;
+				qcom,current = <625>;
+			};
+
+			pmi8950_flash1: qcom,flash_1 {
+				label = "flash";
+				qcom,led-name = "led:flash_1";
+				qcom,default-led-trigger =
+						"flash1_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <1>;
+				qcom,current = <625>;
+			};
+
+			pmi8950_torch0: qcom,torch_0 {
+				label = "torch";
+				qcom,led-name = "led:torch_0";
+				qcom,default-led-trigger =
+						"torch0_trigger";
+				qcom,max-current = <200>;
+				qcom,id = <0>;
+				qcom,current = <120>;
+			};
+
+			pmi8950_torch1: qcom,torch_1 {
+				label = "torch";
+				qcom,led-name = "led:torch_1";
+				qcom,default-led-trigger =
+						"torch1_trigger";
+				qcom,max-current = <200>;
+				qcom,id = <1>;
+				qcom,current = <120>;
+			};
+
+			pmi8950_switch: qcom,switch {
+				label = "switch";
+				qcom,led-name = "led:switch";
+				qcom,default-led-trigger =
+						"switch_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <2>;
+				qcom,current = <625>;
+				reg0 {
+					regulator-name = "pon_spare_reg";
+				};
+			};
+		};
+
+		pmi_haptic: qcom,haptic@c000 {
+			compatible = "qcom,qpnp-haptic";
+			reg = <0xc000 0x100>;
+			interrupts = <0x3 0xc0 0x0>,
+					<0x3 0xc0 0x1>;
+			interrupt-names = "sc-irq", "play-irq";
+			qcom,pmic-revid = <&pmi8950_revid>;
+			vcc_pon-supply = <&pon_perph_reg>;
+			qcom,play-mode = "direct";
+			qcom,wave-play-rate-us = <5263>;
+			qcom,actuator-type = "erm";
+			qcom,wave-shape = "square";
+			qcom,vmax-mv = <2000>;
+			qcom,ilim-ma = <800>;
+			qcom,sc-deb-cycles = <8>;
+			qcom,int-pwm-freq-khz = <505>;
+			qcom,en-brake;
+			qcom,brake-pattern = [03 03 00 00];
+			qcom,use-play-irq;
+			qcom,use-sc-irq;
+			qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
+			qcom,wave-rep-cnt = <1>;
+			qcom,wave-samp-rep-cnt = <1>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
index a3df1f4..2106759 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -51,6 +51,89 @@
 				linux,code = <114>;
 			};
 		};
+
+		pmxpoorwills_gpios: pinctrl@c000 {
+			compatible = "qcom,spmi-gpio";
+			reg = <0xc000 0x900>;
+			interrupts = <0x0 0xc1 0 IRQ_TYPE_NONE>,
+					<0x0 0xc2 0 IRQ_TYPE_NONE>,
+					<0x0 0xc3 0 IRQ_TYPE_NONE>,
+					<0x0 0xc4 0 IRQ_TYPE_NONE>,
+					<0x0 0xc5 0 IRQ_TYPE_NONE>;
+			interrupt-names = "pmxpoorwills_gpio2",
+					  "pmxpoorwills_gpio3",
+					  "pmxpoorwills_gpio4",
+					  "pmxpoorwills_gpio5",
+					  "pmxpoorwills_gpio6";
+			gpio-controller;
+			#gpio-cells = <2>;
+			qcom,gpios-disallowed = <1 7 8 9>;
+		};
+
+		pmxpoorwills_rtc: qcom,pmxpoorwills_rtc {
+			compatible = "qcom,qpnp-rtc";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-rtc-write = <0>;
+			qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+			qcom,pmxpoorwills_rtc_rw@6000 {
+				reg = <0x6000 0x100>;
+			};
+			qcom,pmxpoorwills_rtc_alarm@6100 {
+				reg = <0x6100 0x100>;
+				interrupts = <0x0 0x61 0x1 IRQ_TYPE_NONE>;
+			};
+		};
+
+		pmxpoorwills_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc-hc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-full-scale-code = <0x70e4>;
+			qcom,adc-vdd-reference = <1875>;
+			pinctrl-names = "default";
+			pinctrl-0 = <&ambient_therm_default>;
+
+			chan@6 {
+				label = "die_temp";
+				reg = <6>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+
+			chan@0 {
+				label = "ref_gnd";
+				reg = <0>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+
+			chan@1 {
+				label = "ref_1250v";
+				reg = <1>;
+				qcom,decimation = <2>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,cal-val = <0>;
+			};
+		};
 	};
 
 	qcom,pmxpoorwills@1 {
@@ -58,5 +141,45 @@
 		reg = <0x1 SPMI_USID>;
 		#address-cells = <2>;
 		#size-cells = <0>;
+
+		pmxpoorwills_pwm_1: pwm@bc00 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbc00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <0>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+			status = "disabled";
+		};
+
+		pmxpoorwills_pwm_2: pwm@bd00 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbd00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <1>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+			status = "disabled";
+		};
+
+		pmxpoorwills_pwm_3: pwm@be00 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbe00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <2>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+			status = "disabled";
+		};
+
+		pmxpoorwills_pwm_4: pwm@bf00 {
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbf00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <3>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+			status = "disabled";
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi b/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi
new file mode 100644
index 0000000..0fd3b34
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdx-audio-lpass.dtsi
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	qcom,msm-adsp-loader {
+		compatible = "qcom,adsp-loader";
+		qcom,adsp-state = <0>;
+		qcom,proc-img-to-load = "modem";
+	};
+
+	qcom,msm-audio-ion {
+		compatible = "qcom,msm-audio-ion";
+		qcom,scm-mp-enabled;
+		memory-region = <&audio_mem>;
+	};
+
+	pcm0: qcom,msm-pcm {
+		compatible = "qcom,msm-pcm-dsp";
+		qcom,msm-pcm-dsp-id = <0>;
+	};
+
+	routing: qcom,msm-pcm-routing {
+		compatible = "qcom,msm-pcm-routing";
+	};
+
+	pcm1: qcom,msm-pcm-low-latency {
+		compatible = "qcom,msm-pcm-dsp";
+		qcom,msm-pcm-dsp-id = <1>;
+		qcom,msm-pcm-low-latency;
+		qcom,latency-level = "ultra";
+	};
+
+	qcom,msm-compr-dsp {
+		compatible = "qcom,msm-compr-dsp";
+	};
+
+	voip: qcom,msm-voip-dsp {
+		compatible = "qcom,msm-voip-dsp";
+	};
+
+	voice: qcom,msm-pcm-voice {
+		compatible = "qcom,msm-pcm-voice";
+		qcom,destroy-cvd;
+	};
+
+	stub_codec: qcom,msm-stub-codec {
+		compatible = "qcom,msm-stub-codec";
+	};
+
+	qcom,msm-dai-fe {
+		compatible = "qcom,msm-dai-fe";
+	};
+
+	afe: qcom,msm-pcm-afe {
+		compatible = "qcom,msm-pcm-afe";
+	};
+
+	 hostless: qcom,msm-pcm-hostless {
+		compatible = "qcom,msm-pcm-hostless";
+	};
+
+	host_pcm: qcom,msm-voice-host-pcm {
+		compatible = "qcom,msm-voice-host-pcm";
+	};
+
+	loopback: qcom,msm-pcm-loopback {
+		compatible = "qcom,msm-pcm-loopback";
+	};
+
+	compress: qcom,msm-compress-dsp {
+		compatible = "qcom,msm-compress-dsp";
+		qcom,adsp-version = "MDSP 1.2";
+	};
+
+	qcom,msm-dai-stub {
+		compatible = "qcom,msm-dai-stub";
+		dtmf_tx: qcom,msm-dai-stub-dtmf-tx {
+			compatible = "qcom,msm-dai-stub-dev";
+			qcom,msm-dai-stub-dev-id = <4>;
+		};
+
+		rx_capture_tx: qcom,msm-dai-stub-host-rx-capture-tx {
+			compatible = "qcom,msm-dai-stub-dev";
+			qcom,msm-dai-stub-dev-id = <5>;
+		};
+
+		rx_playback_rx: qcom,msm-dai-stub-host-rx-playback-rx {
+			compatible = "qcom,msm-dai-stub-dev";
+			qcom,msm-dai-stub-dev-id = <6>;
+		};
+
+		tx_capture_tx: qcom,msm-dai-stub-host-tx-capture-tx {
+			compatible = "qcom,msm-dai-stub-dev";
+			qcom,msm-dai-stub-dev-id = <7>;
+		};
+
+		tx_playback_rx: qcom,msm-dai-stub-host-tx-playback-rx {
+			compatible = "qcom,msm-dai-stub-dev";
+			qcom,msm-dai-stub-dev-id = <8>;
+		};
+	};
+
+	qcom,msm-dai-q6 {
+		compatible = "qcom,msm-dai-q6";
+		afe_pcm_rx: qcom,msm-dai-q6-be-afe-pcm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <224>;
+		};
+
+		afe_pcm_tx: qcom,msm-dai-q6-be-afe-pcm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <225>;
+		};
+
+		afe_proxy_rx: qcom,msm-dai-q6-afe-proxy-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <241>;
+		};
+
+		afe_proxy_tx: qcom,msm-dai-q6-afe-proxy-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <240>;
+		};
+
+		incall_record_rx: qcom,msm-dai-q6-incall-record-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32771>;
+		};
+
+		incall_record_tx: qcom,msm-dai-q6-incall-record-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32772>;
+		};
+
+		incall_music_rx: qcom,msm-dai-q6-incall-music-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32773>;
+		};
+	};
+
+	pcm_dtmf: qcom,msm-pcm-dtmf {
+		compatible = "qcom,msm-pcm-dtmf";
+	};
+
+	cpu-pmu {
+		compatible = "arm,cortex-a7-pmu";
+		qcom,irq-is-percpu;
+		interrupts = <1 8 0x100>;
+	};
+
+	dai_pri_auxpcm: qcom,msm-pri-auxpcm {
+		compatible = "qcom,msm-auxpcm-dev";
+		qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+		qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+		qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+		qcom,msm-auxpcm-interface = "primary";
+		qcom,msm-cpudai-afe-clk-ver = <2>;
+	};
+
+	dai_sec_auxpcm: qcom,msm-sec-auxpcm {
+		compatible = "qcom,msm-auxpcm-dev";
+		qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+		qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+		qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+		qcom,msm-auxpcm-interface = "secondary";
+		qcom,msm-cpudai-afe-clk-ver = <2>;
+	};
+
+	qcom,msm-dai-mi2s {
+		compatible = "qcom,msm-dai-mi2s";
+		mi2s_prim: qcom,msm-dai-q6-mi2s-prim {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <0>;
+			qcom,msm-mi2s-rx-lines = <2>;
+			qcom,msm-mi2s-tx-lines = <1>;
+		};
+		mi2s_sec: qcom,msm-dai-q6-mi2s-sec {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <1>;
+			qcom,msm-mi2s-rx-lines = <2>;
+			qcom,msm-mi2s-tx-lines = <1>;
+		};
+
+	};
+
+	prim_master: prim_master_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&pri_ws_active_master
+				&pri_sck_active_master
+				&pri_dout_active
+				&pri_din_active>;
+		pinctrl-1 = <&pri_ws_sleep
+				&pri_sck_sleep
+				&pri_dout_sleep
+				&pri_din_sleep>;
+		qcom,mi2s-auxpcm-cdc-gpios;
+	};
+
+	prim_slave: prim_slave_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&pri_ws_active_slave
+				&pri_sck_active_slave
+				&pri_dout_active
+				&pri_din_active>;
+		pinctrl-1 = <&pri_ws_sleep
+				&pri_sck_sleep
+				&pri_dout_sleep
+				&pri_din_sleep>;
+		qcom,mi2s-auxpcm-cdc-gpios;
+	};
+
+	sec_master: sec_master_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&sec_ws_active_master
+				&sec_sck_active_master
+				&sec_dout_active
+				&sec_din_active>;
+		pinctrl-1 = <&sec_ws_sleep
+				&sec_sck_sleep
+				&sec_dout_sleep
+				&sec_din_sleep>;
+		qcom,mi2s-auxpcm-cdc-gpios;
+	};
+
+	sec_slave: sec_slave_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&sec_ws_active_slave
+				&sec_sck_active_slave
+				&sec_dout_active
+				&sec_din_active>;
+		pinctrl-1 = <&sec_ws_sleep
+				&sec_sck_sleep
+				&sec_dout_sleep
+				&sec_din_sleep>;
+		qcom,mi2s-auxpcm-cdc-gpios;
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdx-wsa881x.dtsi b/arch/arm/boot/dts/qcom/sdx-wsa881x.dtsi
new file mode 100644
index 0000000..a294e6c
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdx-wsa881x.dtsi
@@ -0,0 +1,45 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&i2c_3 {
+	tavil_codec {
+		swr_master {
+			compatible = "qcom,swr-wcd";
+			#address-cells = <2>;
+			#size-cells = <0>;
+
+			wsa881x_0211: wsa881x@20170211 {
+				compatible = "qcom,wsa881x";
+				reg = <0x00 0x20170211>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>;
+			};
+
+			wsa881x_0212: wsa881x@20170212 {
+				compatible = "qcom,wsa881x";
+				reg = <0x00 0x20170212>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>;
+			};
+
+			wsa881x_0213: wsa881x@21170213 {
+				compatible = "qcom,wsa881x";
+				reg = <0x00 0x21170213>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd1>;
+			};
+
+			wsa881x_0214: wsa881x@21170214 {
+				compatible = "qcom,wsa881x";
+				reg = <0x00 0x21170214>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_wcd_sd2>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-audio-overlay.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-audio-overlay.dtsi
new file mode 100644
index 0000000..f90bd7f
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-audio-overlay.dtsi
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdxpoorwills-wcd.dtsi"
+#include "sdx-wsa881x.dtsi"
+#include <dt-bindings/clock/qcom,audio-ext-clk.h>
+
+&snd_934x {
+	qcom,audio-routing =
+		"AIF4 VI", "MCLK",
+		"RX_BIAS", "MCLK",
+		"MADINPUT", "MCLK",
+		"AMIC2", "MIC BIAS2",
+		"MIC BIAS2", "Headset Mic",
+		"AMIC3", "MIC BIAS2",
+		"MIC BIAS2", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2",
+		"MIC BIAS2", "ANCLeft Headset Mic",
+		"AMIC5", "MIC BIAS3",
+		"MIC BIAS3", "Handset Mic",
+		"DMIC0", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic0",
+		"DMIC1", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic1",
+		"DMIC2", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic2",
+		"DMIC3", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic3",
+		"DMIC4", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic4",
+		"DMIC5", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic5",
+		"SpkrLeft IN", "SPK1 OUT",
+		"SpkrRight IN", "SPK2 OUT";
+
+	qcom,msm-mbhc-hphl-swh = <1>;
+	qcom,msm-mbhc-gnd-swh = <1>;
+	qcom,msm-mbhc-hs-mic-max-threshold-mv = <1700>;
+	qcom,msm-mbhc-hs-mic-min-threshold-mv = <50>;
+	qcom,tavil-mclk-clk-freq = <12288000>;
+
+	asoc-codec = <&stub_codec>;
+	asoc-codec-names = "msm-stub-codec.1";
+
+	qcom,wsa-max-devs = <2>;
+	qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>,
+			<&wsa881x_0213>, <&wsa881x_0214>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+				  "SpkrLeft", "SpkrRight";
+};
+
+&soc {
+	wcd9xxx_intc: wcd9xxx-irq {
+		status = "ok";
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&tlmm>;
+		qcom,gpio-connect = <&tlmm 71 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&wcd_intr_default>;
+	};
+
+	clock_audio_up: audio_ext_clk_up {
+		compatible = "qcom,audio-ref-clk";
+		qcom,codec-mclk-clk-freq = <12288000>;
+		pinctrl-names = "sleep", "active";
+		pinctrl-0 = <&i2s_mclk_sleep>;
+		pinctrl-1 = <&i2s_mclk_active>;
+		#clock-cells = <1>;
+	};
+
+	wcd_rst_gpio: msm_cdc_pinctrl@77 {
+		compatible = "qcom,msm-cdc-pinctrl";
+		qcom,cdc-rst-n-gpio = <&tlmm 77 0>;
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_reset_active>;
+		pinctrl-1 = <&cdc_reset_sleep>;
+	};
+};
+
+&i2c_3 {
+	wcd934x_cdc: tavil_codec {
+		compatible = "qcom,tavil-i2c-pgd";
+		elemental-addr = [00 01 50 02 17 02];
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			      17 18 19 20 21 22 23 24 25 26 27 28 29
+			      30 31>;
+
+		qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+
+		clock-names = "wcd_clk";
+		clocks = <&clock_audio_up AUDIO_LPASS_MCLK>;
+
+		cdc-vdd-buck-supply = <&pmxpoorwills_l6>;
+		qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-buck-current = <650000>;
+
+		cdc-buck-sido-supply = <&pmxpoorwills_l6>;
+		qcom,cdc-buck-sido-voltage = <1800000 1800000>;
+		qcom,cdc-buck-sido-current = <250000>;
+
+		cdc-vdd-tx-h-supply = <&pmxpoorwills_l6>;
+		qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-tx-h-current = <25000>;
+
+		cdc-vdd-rx-h-supply = <&pmxpoorwills_l6>;
+		qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-rx-h-current = <25000>;
+
+		cdc-vddpx-1-supply = <&pmxpoorwills_l6>;
+		qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+		qcom,cdc-vddpx-1-current = <10000>;
+
+		qcom,cdc-static-supplies = "cdc-vdd-buck",
+					   "cdc-buck-sido",
+					   "cdc-vdd-tx-h",
+					   "cdc-vdd-rx-h",
+					   "cdc-vddpx-1";
+
+		qcom,cdc-micbias1-mv = <1800>;
+		qcom,cdc-micbias2-mv = <1800>;
+		qcom,cdc-micbias3-mv = <1800>;
+		qcom,cdc-micbias4-mv = <1800>;
+
+		qcom,cdc-mclk-clk-rate = <12288000>;
+		qcom,cdc-dmic-sample-rate = <4800000>;
+
+		qcom,wdsp-cmpnt-dev-name = "tavil_codec";
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-audio.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-audio.dtsi
new file mode 100644
index 0000000..a3eba9a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-audio.dtsi
@@ -0,0 +1,51 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "sdx-audio-lpass.dtsi"
+
+&soc {
+	snd_934x: sound-tavil {
+		compatible = "qcom,sdx-asoc-snd-tavil";
+		qcom,model = "sdx-tavil-i2s-snd-card";
+		qcom,prim_mi2s_aux_master = <&prim_master>;
+		qcom,prim_mi2s_aux_slave = <&prim_slave>;
+		qcom,sec_mi2s_aux_master = <&sec_master>;
+		qcom,sec_mi2s_aux_slave = <&sec_slave>;
+
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&hostless>, <&afe>, <&routing>,
+				<&pcm_dtmf>, <&host_pcm>, <&compress>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice",
+				"msm-pcm-loopback", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-pcm-routing",
+				"msm-pcm-dtmf", "msm-voice-host-pcm",
+				"msm-compress-dsp";
+		asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&mi2s_sec>,
+				<&dtmf_tx>,
+				<&rx_capture_tx>, <&rx_playback_rx>,
+				<&tx_capture_tx>, <&tx_playback_rx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>,
+				<&dai_sec_auxpcm>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-stub-dev.4", "msm-dai-stub-dev.5",
+				"msm-dai-stub-dev.6", "msm-dai-stub-dev.7",
+				"msm-dai-stub-dev.8", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-auxpcm.2";
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi
new file mode 100644
index 0000000..4fe2d1e
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-blsp.dtsi
@@ -0,0 +1,573 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+	aliases {
+		spi1 = &spi_1;
+		spi2 = &spi_2;
+		spi3 = &spi_3;
+		spi4 = &spi_4;
+		i2c1 = &i2c_1;
+		i2c2 = &i2c_2;
+		i2c3 = &i2c_3;
+		i2c4 = &i2c_4;
+		i2c5 = &i2c_5;
+		i2c6 = &i2c_6;
+		i2c7 = &i2c_7;
+	};
+};
+
+
+&soc {
+	dma_blsp1: qcom,sps-dma@804000 { /* BLSP1 */
+		#dma-cells = <4>;
+		compatible = "qcom,sps-dma";
+		reg = <0x804000 0x23000>;
+		interrupts = <0 58 0>;
+		qcom,summing-threshold = <0x10>;
+	};
+
+	i2c_1: i2c@835000 { /* BLSP1 QUP1: GPIO: 2,3 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x835000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 31 0>;
+		dmas = <&dma_blsp1 8 64 0x20000020 0x20>,
+			<&dma_blsp1 9 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_1_active>;
+		pinctrl-1 = <&i2c_1_sleep>;
+		status = "disabled";
+	};
+
+	i2c_2: i2c@836000 { /* BLSP1 QUP2: GPIO: 6,7 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x836000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 32 0>;
+		dmas = <&dma_blsp1 10 64 0x20000020 0x20>,
+			<&dma_blsp1 11 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_2_active>;
+		pinctrl-1 = <&i2c_2_sleep>;
+		status = "disabled";
+	};
+
+	i2c_3: i2c@837000 { /* BLSP1 QUP3: GPIO: 10,11 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x837000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 33 0>;
+		dmas = <&dma_blsp1 12 64 0x20000020 0x20>,
+			<&dma_blsp1 13 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP3_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_3_active>;
+		pinctrl-1 = <&i2c_3_sleep>;
+		status = "disabled";
+	};
+
+	i2c_4: i2c@838000 { /* BLSP1 QUP4: GPIO: 76,77 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x838000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 34 0>;
+		dmas = <&dma_blsp1 14 64 0x20000020 0x20>,
+			<&dma_blsp1 15 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_4_active>;
+		pinctrl-1 = <&i2c_4_sleep>;
+		status = "disabled";
+	};
+
+	i2c_5: i2c@835000 { /* BLSP1 QUP1: GPIO: 74,75 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x835000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 31 0>;
+		dmas = <&dma_blsp1 8 64 0x20000020 0x20>,
+			<&dma_blsp1 9 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP1_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_5_active>;
+		pinctrl-1 = <&i2c_5_sleep>;
+		status = "disabled";
+	};
+
+	i2c_6: i2c@836000 { /* BLSP1 QUP2: GPIO: 65,66 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x836000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 32 0>;
+		dmas = <&dma_blsp1 10 64 0x20000020 0x20>,
+			<&dma_blsp1 11 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP2_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_6_active>;
+		pinctrl-1 = <&i2c_6_sleep>;
+		status = "disabled";
+	};
+
+	i2c_7: i2c@838000 { /* BLSP1 QUP4: GPIO: 18,19 */
+		compatible = "qcom,i2c-msm-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0x838000 0x600>;
+		reg-names = "qup_phys_addr";
+		interrupt-names = "qup_irq";
+		interrupts = <0 34 0>;
+		dmas = <&dma_blsp1 14 64 0x20000020 0x20>,
+			<&dma_blsp1 15 32 0x20000020 0x20>;
+		dma-names = "tx", "rx";
+		qcom,master-id = <86>;
+		qcom,clk-freq-out = <400000>;
+		qcom,clk-freq-in  = <19200000>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP4_I2C_APPS_CLK>;
+		pinctrl-names = "i2c_active", "i2c_sleep";
+		pinctrl-0 = <&i2c_7_active>;
+		pinctrl-1 = <&i2c_7_sleep>;
+		status = "disabled";
+	};
+
+	spi_1: spi@835000 { /* BLSP1 QUP1: GPIO: 72,73,74,75 */
+		compatible = "qcom,spi-qup-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "spi_physical", "spi_bam_physical";
+		reg = <0x835000 0x600>,
+		      <0x804000 0x23000>;
+		interrupt-names = "spi_irq", "spi_bam_irq";
+		interrupts = <0 31 0>, <0 58 0>;
+		spi-max-frequency = <50000000>;
+		qcom,use-bam;
+		qcom,ver-reg-exists;
+		qcom,bam-consumer-pipe-index = <8>;
+		qcom,bam-producer-pipe-index = <9>;
+		qcom,master-id = <86>;
+		qcom,use-pinctrl;
+		pinctrl-names = "spi_default", "spi_sleep";
+		pinctrl-0 = <&spi_1_active>;
+		pinctrl-1 = <&spi_1_sleep>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP1_SPI_APPS_CLK>;
+		status = "disabled";
+	};
+
+	spi_2: spi@836000 { /* BLSP1 QUP2: GPIO: 4,5,6,7 */
+		compatible = "qcom,spi-qup-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "spi_physical", "spi_bam_physical";
+		reg = <0x836000 0x600>,
+		      <0x804000 0x23000>;
+		interrupt-names = "spi_irq", "spi_bam_irq";
+		interrupts = <0 32 0>, <0 58 0>;
+		spi-max-frequency = <50000000>;
+		qcom,use-bam;
+		qcom,ver-reg-exists;
+		qcom,bam-consumer-pipe-index = <10>;
+		qcom,bam-producer-pipe-index = <11>;
+		qcom,master-id = <86>;
+		qcom,use-pinctrl;
+		pinctrl-names = "spi_default", "spi_sleep";
+		pinctrl-0 = <&spi_2_active>;
+		pinctrl-1 = <&spi_2_sleep>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP2_SPI_APPS_CLK>;
+		status = "disabled";
+	};
+
+	spi_3: spi@837000 { /* BLSP1 QUP3: GPIO: 8,9,10,11 */
+		compatible = "qcom,spi-qup-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "spi_physical", "spi_bam_physical";
+		reg = <0x837000 0x600>,
+		      <0x804000 0x23000>;
+		interrupt-names = "spi_irq", "spi_bam_irq";
+		interrupts = <0 33 0>, <0 58 0>;
+		spi-max-frequency = <50000000>;
+		qcom,use-bam;
+		qcom,ver-reg-exists;
+		qcom,bam-consumer-pipe-index = <12>;
+		qcom,bam-producer-pipe-index = <13>;
+		qcom,master-id = <86>;
+		qcom,use-pinctrl;
+		pinctrl-names = "spi_default", "spi_sleep";
+		pinctrl-0 = <&spi_3_active>;
+		pinctrl-1 = <&spi_3_sleep>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP3_SPI_APPS_CLK>;
+		status = "disabled";
+	};
+
+	spi_4: spi@838000 { /* BLSP1 QUP4: GPIO: 16,17,18,19 */
+		compatible = "qcom,spi-qup-v2";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg-names = "spi_physical", "spi_bam_physical";
+		reg = <0x838000 0x600>,
+		      <0x804000 0x23000>;
+		interrupt-names = "spi_irq", "spi_bam_irq";
+		interrupts = <0 34 0>, <0 58 0>;
+		spi-max-frequency = <50000000>;
+		qcom,use-bam;
+		qcom,ver-reg-exists;
+		qcom,bam-consumer-pipe-index = <14>;
+		qcom,bam-producer-pipe-index = <15>;
+		qcom,master-id = <86>;
+		qcom,use-pinctrl;
+		pinctrl-names = "spi_default", "spi_sleep";
+		pinctrl-0 = <&spi_4_active>;
+		pinctrl-1 = <&spi_4_sleep>;
+		clock-names = "iface_clk", "core_clk";
+		clocks = <&clock_gcc GCC_BLSP1_AHB_CLK>,
+			 <&clock_gcc GCC_BLSP1_QUP4_SPI_APPS_CLK>;
+		status = "disabled";
+	};
+
+	blsp1_uart1a_hs: uarta@82f000 { /* BLSP1 UART1: GPIO: 0,1,2,3 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x82f000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart1a_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 24 0
+			    1 &intc 0 58 0
+			    2 &tlmm 1 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <0>;
+		qcom,bam-rx-ep-pipe-index = <1>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART1_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart1a_tx_sleep>,
+		<&blsp1_uart1a_rxcts_sleep>, <&blsp1_uart1a_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart1a_tx_active>,
+		<&blsp1_uart1a_rxcts_active>, <&blsp1_uart1a_rfr_active>;
+
+		qcom,msm-bus,name = "buart1a";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart1b_hs: uartb@82f000 { /* BLSP1 UART1: GPIO: 20,21,22,23 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x82f000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart1b_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 24 0
+			    1 &intc 0 58 0
+			    2 &tlmm 21 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <0>;
+		qcom,bam-rx-ep-pipe-index = <1>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART1_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart1b_tx_sleep>,
+		<&blsp1_uart1b_rxcts_sleep>, <&blsp1_uart1b_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart1b_tx_active>,
+		<&blsp1_uart1b_rxcts_active>, <&blsp1_uart1b_rfr_active>;
+
+		qcom,msm-bus,name = "buart1b";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart2a_hs: uarta@830000 { /* BLSP1 UART2 : GPIO: 4,5,6,7 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x830000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart2a_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 25 0
+			    1 &intc 0 58 0
+			    2 &tlmm 5 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <2>;
+		qcom,bam-rx-ep-pipe-index = <3>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart2a_tx_sleep>,
+		<&blsp1_uart2a_rxcts_sleep>, <&blsp1_uart2a_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart2b_tx_active>,
+		<&blsp1_uart2b_rxcts_active>, <&blsp1_uart2b_rfr_active>;
+
+		qcom,msm-bus,name = "buart2a";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart2b_hs: uartb@830000 { /* BLSP1 UART2 : GPIO: 63,64,65,66 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x830000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart2b_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 25 0
+			    1 &intc 0 58 0
+			    2 &tlmm 64 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <2>;
+		qcom,bam-rx-ep-pipe-index = <3>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart2b_tx_sleep>,
+		<&blsp1_uart2b_rxcts_sleep>, <&blsp1_uart2b_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart2b_tx_active>,
+		<&blsp1_uart2b_rxcts_active>, <&blsp1_uart2b_rfr_active>;
+
+		qcom,msm-bus,name = "buart2b";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart3_hs: uart@831000 { /* BLSP1 UART3: GPIO: 8,9,10,11 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x831000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart3_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 26 0
+			    1 &intc 0 58 0
+			    2 &tlmm 9 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <4>;
+		qcom,bam-rx-ep-pipe-index = <5>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART3_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart3_tx_sleep>,
+		<&blsp1_uart3_rxcts_sleep>, <&blsp1_uart3_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart3_tx_active>,
+		<&blsp1_uart3_rxcts_active>, <&blsp1_uart3_rfr_active>;
+
+		qcom,msm-bus,name = "buart3";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart4a_hs: uarta@832000 {	/* BLSP1 UART4 : GPIO: 20,21,22,23 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x832000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart4a_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 27 0
+			    1 &intc 0 58 0
+			    2 &tlmm 21 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <6>;
+		qcom,bam-rx-ep-pipe-index = <7>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART4_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart4a_tx_active>,
+		<&blsp1_uart4a_rxcts_sleep>, <&blsp1_uart4a_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart4a_tx_active>,
+		<&blsp1_uart4a_rxcts_active>, <&blsp1_uart4a_rfr_active>;
+
+		qcom,msm-bus,name = "buart4a";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+
+	blsp1_uart4b_hs: uartb@832000 { /* BLSP1 UART4 : GPIO: 16,17,18,19 */
+		compatible = "qcom,msm-hsuart-v14";
+		reg = <0x832000 0x200>,
+		    <0x804000 0x23000>;
+		reg-names = "core_mem", "bam_mem";
+		interrupt-names = "core_irq", "bam_irq", "wakeup_irq";
+		#address-cells = <0>;
+		interrupt-parent = <&blsp1_uart4b_hs>;
+		interrupts = <0 1 2>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0 &intc 0 27 0
+			    1 &intc 0 58 0
+			    2 &tlmm 17 0>;
+
+		qcom,inject-rx-on-wakeup;
+		qcom,rx-char-to-inject = <0xfd>;
+
+		qcom,bam-tx-ep-pipe-index = <6>;
+		qcom,bam-rx-ep-pipe-index = <7>;
+		qcom,master-id = <86>;
+		clock-names = "core_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_BLSP1_UART4_APPS_CLK>,
+		    <&clock_gcc GCC_BLSP1_AHB_CLK>;
+		pinctrl-names = "sleep", "default";
+		pinctrl-0 = <&blsp1_uart4b_tx_sleep>,
+		<&blsp1_uart4b_rxcts_sleep>, <&blsp1_uart4b_rfr_sleep>;
+		pinctrl-1 = <&blsp1_uart4b_tx_active>,
+		<&blsp1_uart4b_rxcts_active>, <&blsp1_uart4b_rfr_active>;
+
+		qcom,msm-bus,name = "buart4b";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			    <86 512 0 0>,
+			    <86 512 500 800>;
+		status = "disabled";
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
new file mode 100644
index 0000000..d1d44ec
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-bus.dtsi
@@ -0,0 +1,804 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/msm/msm-bus-ids.h>
+
+&soc {
+	ad_hoc_bus: ad-hoc-bus {
+		compatible = "qcom,msm-bus-device";
+		reg = <0x1100000 0x400000>,
+			<0x1100000 0x400000>,
+			<0x1620000 0x400000>,
+			<0x1620000 0x400000>;
+
+		reg-names = "mc_virt-base", "mem_noc-base",
+			"system_noc-base", "ipa_virt-base";
+
+		/*RSCs*/
+		rsc_apps: rsc-apps {
+			cell-id = <MSM_BUS_RSC_APPS>;
+			label = "apps_rsc";
+			qcom,rsc-dev;
+			qcom,req-state = <2>;
+		};
+
+		/*BCMs*/
+		bcm_alc: bcm-alc {
+			cell-id = <MSM_BUS_BCM_ALC>;
+			label = "ALC";
+			qcom,bcm-name = "ALC";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_ce: bcm-ce {
+			cell-id = <MSM_BUS_BCM_CE>;
+			label = "CE";
+			qcom,bcm-name = "CE";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mc0: bcm-mc0 {
+			cell-id = <MSM_BUS_BCM_MC0>;
+			label = "MC0";
+			qcom,bcm-name = "MC0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_ip0: bcm-ip0 {
+			cell-id = <MSM_BUS_BCM_IP0>;
+			label = "IP0";
+			qcom,bcm-name = "CE";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh0: bcm-sh0 {
+			cell-id = <MSM_BUS_BCM_SH0>;
+			label = "SH0";
+			qcom,bcm-name = "SH0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_pn0: bcm-pn0 {
+			cell-id = <MSM_BUS_BCM_PN0>;
+			label = "PN0";
+			qcom,bcm-name = "PN0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh1: bcm-sh1 {
+			cell-id = <MSM_BUS_BCM_SH1>;
+			label = "SH1";
+			qcom,bcm-name = "SH1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh3: bcm-sh3 {
+			cell-id = <MSM_BUS_BCM_SH3>;
+			label = "SH3";
+			qcom,bcm-name = "SH3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh4: bcm-sh4 {
+			cell-id = <MSM_BUS_BCM_SH4>;
+			label = "SH4";
+			qcom,bcm-name = "SH4";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn0: bcm-sn0 {
+			cell-id = <MSM_BUS_BCM_SN0>;
+			label = "SN0";
+			qcom,bcm-name = "SN0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn1: bcm-sn1 {
+			cell-id = <MSM_BUS_BCM_SN1>;
+			label = "SN1";
+			qcom,bcm-name = "SN1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_pn1: bcm-pn1 {
+			cell-id = <MSM_BUS_BCM_PN1>;
+			label = "PN1";
+			qcom,bcm-name = "PN1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_pn2: bcm-pn2 {
+			cell-id = <MSM_BUS_BCM_PN2>;
+			label = "PN2";
+			qcom,bcm-name = "PN2";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn3: bcm-sn3 {
+			cell-id = <MSM_BUS_BCM_SN3>;
+			label = "SN3";
+			qcom,bcm-name = "SN3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_pn3: bcm-pn3 {
+			cell-id = <MSM_BUS_BCM_PN3>;
+			label = "PN3";
+			qcom,bcm-name = "PN3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_pn5: bcm-pn5 {
+			cell-id = <MSM_BUS_BCM_PN5>;
+			label = "PN5";
+			qcom,bcm-name = "PN5";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn6: bcm-sn6 {
+			cell-id = <MSM_BUS_BCM_SN6>;
+			label = "SN6";
+			qcom,bcm-name = "SN6";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn7: bcm-sn7 {
+			cell-id = <MSM_BUS_BCM_SN7>;
+			label = "SN7";
+			qcom,bcm-name = "SN7";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn8: bcm-sn8 {
+			cell-id = <MSM_BUS_BCM_SN8>;
+			label = "SN8";
+			qcom,bcm-name = "SN8";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn9: bcm-sn9 {
+			cell-id = <MSM_BUS_BCM_SN9>;
+			label = "SN9";
+			qcom,bcm-name = "SN9";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn11: bcm-sn11 {
+			cell-id = <MSM_BUS_BCM_SN11>;
+			label = "SN11";
+			qcom,bcm-name = "SN11";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		/*Buses*/
+		fab_ipa_virt: fab-ipa_virt{
+			cell-id = <MSM_BUS_FAB_IPA_VIRT>;
+			label = "fab-ipa_virt";
+			qcom,fab-dev;
+			qcom,base-name = "ipa_virt-base";
+			qcom,qos-off = <0>;
+			qcom,base-offset = <0>;
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_mc_virt: fab-mc_virt{
+			cell-id = <MSM_BUS_FAB_MC_VIRT>;
+			label = "fab-mc_virt";
+			qcom,fab-dev;
+			qcom,base-name = "mc_virt-base";
+			qcom,qos-off = <0>;
+			qcom,base-offset = <0>;
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_mem_noc: fab-mem_noc {
+			cell-id = <MSM_BUS_FAB_MEM_NOC>;
+			label = "fab-mem_noc";
+			qcom,fab-dev;
+			qcom,base-name = "mem_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <65536>;
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_system_noc: fab-system_noc {
+			cell-id = <MSM_BUS_FAB_SYS_NOC>;
+			label = "fab-system_noc";
+			qcom,fab-dev;
+			qcom,base-name = "system_noc-base";
+			qcom,qos-off = <0>;
+			qcom,base-offset = <0>;
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		/*Masters*/
+
+		mas_ipa_core_master: mas-ipa-core-master {
+			cell-id = <MSM_BUS_MASTER_IPA_CORE>;
+			label = "mas-ipa-core-master";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_ipa_core_slave>;
+			qcom,bus-dev = <&fab_ipa_virt>;
+		};
+
+		mas_llcc_mc: mas-llcc-mc {
+			cell-id = <MSM_BUS_MASTER_LLCC>;
+			label = "mas-llcc-mc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_ebi>;
+			qcom,bus-dev = <&fab_mc_virt>;
+		};
+
+		mas_acm_tcu: mas-acm-tcu {
+			cell-id = <MSM_BUS_MASTER_TCU_0>;
+			label = "mas-acm-tcu";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <0>;
+			qcom,connections = <&slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,bcms = <&bcm_sh1>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_qnm_snoc_gc: mas-qnm-snoc-gc {
+			cell-id = <MSM_BUS_MASTER_SNOC_GC_MEM_NOC>;
+			label = "mas-qnm-snoc-gc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <8>;
+			qcom,connections = <&slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_xm_apps_rdwr: mas-xm-apps-rdwr {
+			cell-id = <MSM_BUS_MASTER_AMPSS_M0>;
+			label = "mas-xm-apps-rdwr";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <3>;
+			qcom,connections = <&slv_qns_llcc &slv_qns_memnoc_snoc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,bcms = <&bcm_sh3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_qhm_audio: mas-qhm-audio {
+			cell-id = <MSM_BUS_MASTER_AUDIO>;
+			label = "mas-qhm-audio";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn2>;
+		};
+
+		mas_qhm_blsp1: mas-qhm-blsp1 {
+			cell-id = <MSM_BUS_MASTER_BLSP_1>;
+			label = "mas-qhm-blsp1";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn3>;
+		};
+
+		mas_qhm_qdss_bam: mas-qhm-qdss-bam {
+			cell-id = <MSM_BUS_MASTER_QDSS_BAM>;
+			label = "mas-qhm-qdss-bam";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_crypto_cfg
+				 &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+				 &slv_qhs_aoss &slv_qhs_spmi_fetcher
+				 &slv_qhs_pdm &slv_qns_snoc_memnoc
+				 &slv_qhs_tcsr &slv_qhs_qpic
+				 &slv_qxs_imem &slv_qhs_ipa
+				 &slv_qhs_usb3_phy &slv_qhs_aop
+				 &slv_qhs_blsp1 &slv_qhs_sdc1
+				 &slv_qhs_pcie_parf &slv_qhs_audio
+				 &slv_qhs_tlmm &slv_qhs_prng
+				 &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+				 &slv_qhs_usb3>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn8>;
+		};
+
+		mas_qhm_qpic: mas-qhm-qpic {
+			cell-id = <MSM_BUS_MASTER_QPIC>;
+			label = "mas-qhm-qpic";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn3>;
+		};
+
+		mas_qhm_snoc_cfg: mas-qhm-snoc-cfg {
+			cell-id = <MSM_BUS_MASTER_SNOC_CFG>;
+			label = "mas-qhm-snoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_snoc>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		mas_qhm_spmi_fetcher1: mas-qhm-spmi-fetcher1 {
+			cell-id = <MSM_BUS_MASTER_SPMI_FETCHER>;
+			label = "mas-qhm-spmi-fetcher1";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc &slv_qhs_aop>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn2>;
+		};
+
+		mas_qnm_aggre_noc: mas-qnm-aggre-noc {
+			cell-id = <MSM_BUS_MASTER_ANOC_SNOC>;
+			label = "mas-qnm-aggre-noc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_crypto_cfg
+				 &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+				 &slv_qhs_aoss &slv_qhs_spmi_fetcher
+				 &slv_qhs_pdm &slv_qns_snoc_memnoc
+				 &slv_qhs_tcsr &slv_xs_qdss_stm
+				 &slv_qhs_qpic &slv_qxs_imem
+				 &slv_qhs_ipa &slv_qhs_usb3_phy
+				 &slv_qhs_aop &slv_qhs_blsp1
+				 &slv_qhs_sdc1 &slv_qhs_pcie_parf
+				 &slv_qhs_audio &slv_qxs_pcie
+				 &slv_qhs_tlmm &slv_qhs_prng
+				 &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+				 &slv_qhs_usb3>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn7>;
+		};
+
+		mas_qnm_aggre_noc_ipa: mas-qnm-aggre-noc-ipa {
+			cell-id = <MSM_BUS_MASTER_ANOC_IPA>;
+			label = "mas-qnm-aggre-noc-ipa";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_crypto_cfg
+				 &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+				 &slv_qhs_aoss &slv_qhs_spmi_fetcher
+				 &slv_qhs_pdm &slv_qns_snoc_memnoc
+				 &slv_qhs_tcsr &slv_xs_qdss_stm
+				 &slv_qhs_qpic &slv_qxs_imem
+				 &slv_qhs_ipa &slv_qhs_usb3_phy
+				 &slv_qhs_aop &slv_qhs_blsp1
+				 &slv_qhs_sdc1 &slv_qhs_pcie_parf
+				 &slv_qhs_audio &slv_qhs_tlmm
+				 &slv_qhs_prng &slv_xs_sys_tcu_cfg
+				 &slv_qhs_clk_ctl &slv_qhs_usb3>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		mas_qnm_memnoc: mas-qnm-memnoc {
+			cell-id = <MSM_BUS_MASTER_MEM_NOC_SNOC>;
+			label = "mas-qnm-memnoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_crypto_cfg
+				 &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+				 &slv_qhs_aoss &slv_qhs_spmi_fetcher
+				 &slv_qhs_pdm &slv_qhs_tcsr
+				 &slv_xs_qdss_stm &slv_qhs_qpic
+				 &slv_qxs_imem &slv_qhs_ipa
+				 &slv_qhs_usb3_phy &slv_qhs_aop
+				 &slv_qhs_blsp1 &slv_qhs_sdc1
+				 &slv_qhs_pcie_parf &slv_qhs_audio
+				 &slv_qhs_tlmm &slv_qhs_prng
+				 &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+				 &slv_qhs_usb3>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn9>;
+		};
+
+		mas_qxm_crypto: mas-qxm-crypto {
+			cell-id = <MSM_BUS_MASTER_CRYPTO_CORE_0>;
+			label = "mas-qxm-crypto";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_ce>;
+		};
+
+		mas_qxm_ipa: mas-qxm-ipa {
+			cell-id = <MSM_BUS_MASTER_IPA>;
+			label = "mas-qxm-ipa";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc_ipa>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn11>;
+		};
+
+		mas_xm_emac: mas-xm-emac {
+			cell-id = <MSM_BUS_MASTER_EMAC>;
+			label = "mas-xm-emac";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		mas_xm_pcie: mas-xm-pcie {
+			cell-id = <MSM_BUS_MASTER_PCIE>;
+			label = "mas-xm-pcie";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		mas_xm_qdss_etr: mas-xm-qdss-etr {
+			cell-id = <MSM_BUS_MASTER_QDSS_ETR>;
+			label = "mas-xm-qdss-etr";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_crypto_cfg
+				 &slv_qhs_snoc_cfg &slv_qhs_emac_cfg
+				 &slv_qhs_aoss &slv_qhs_spmi_fetcher
+				 &slv_qhs_pdm &slv_qns_snoc_memnoc
+				 &slv_qhs_tcsr &slv_qhs_qpic
+				 &slv_qxs_imem &slv_qhs_ipa
+				 &slv_qhs_usb3_phy &slv_qhs_aop
+				 &slv_qhs_blsp1 &slv_qhs_sdc1
+				 &slv_qhs_pcie_parf &slv_qhs_audio
+				 &slv_qhs_tlmm &slv_qhs_prng
+				 &slv_xs_sys_tcu_cfg &slv_qhs_clk_ctl
+				 &slv_qhs_usb3>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn8>;
+		};
+
+		mas_xm_sdc1: mas-xm-sdc1 {
+			cell-id = <MSM_BUS_MASTER_SDCC_1>;
+			label = "mas-xm-sdc1";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_aoss &slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn1>;
+		};
+
+		mas_xm_usb3: mas-xm-usb3 {
+			cell-id = <MSM_BUS_MASTER_USB3>;
+			label = "mas-xm-usb3";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_aggre_noc>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		/*Internal nodes*/
+
+		/*Slaves*/
+
+		slv_ipa_core_slave:slv-ipa-core-slave {
+			cell-id = <MSM_BUS_SLAVE_IPA_CORE>;
+			label = "slv-ipa-core-slave";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_ipa_virt>;
+			qcom,bcms = <&bcm_ip0>;
+		};
+
+		slv_ebi:slv-ebi {
+			cell-id = <MSM_BUS_SLAVE_EBI_CH0>;
+			label = "slv-ebi";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mc_virt>;
+			qcom,bcms = <&bcm_mc0>;
+		};
+
+		slv_qns_llcc:slv-qns-llcc {
+			cell-id = <MSM_BUS_SLAVE_LLCC>;
+			label = "slv-qns-llcc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,connections = <&mas_llcc_mc>;
+			qcom,bcms = <&bcm_sh0>;
+		};
+
+		slv_qns_memnoc_snoc:slv-qns-memnoc-snoc {
+			cell-id = <MSM_BUS_SLAVE_MEM_NOC_SNOC>;
+			label = "slv-qns-memnoc-snoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,connections = <&mas_qnm_memnoc>;
+			qcom,bcms = <&bcm_sh4>;
+		};
+
+		slv_qhs_aop:slv-qhs-aop {
+			cell-id = <MSM_BUS_SLAVE_AOP>;
+			label = "slv-qhs-aop";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_aoss:slv-qhs-aoss {
+			cell-id = <MSM_BUS_SLAVE_AOSS>;
+			label = "slv-qhs-aoss";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_audio:slv-qhs-audio {
+			cell-id = <MSM_BUS_SLAVE_AUDIO>;
+			label = "slv-qhs-audio";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_blsp1:slv-qhs-blsp1 {
+			cell-id = <MSM_BUS_SLAVE_BLSP_1>;
+			label = "slv-qhs-blsp1";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_clk_ctl:slv-qhs-clk-ctl {
+			cell-id = <MSM_BUS_SLAVE_CLK_CTL>;
+			label = "slv-qhs-clk-ctl";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_crypto_cfg:slv-qhs-crypto-cfg {
+			cell-id = <MSM_BUS_SLAVE_CRYPTO_0_CFG>;
+			label = "slv-qhs-crypto-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_emac_cfg:slv-qhs-emac-cfg {
+			cell-id = <MSM_BUS_SLAVE_EMAC_CFG>;
+			label = "slv-qhs-emac-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_ipa:slv-qhs-ipa {
+			cell-id = <MSM_BUS_SLAVE_IPA_CFG>;
+			label = "slv-qhs-ipa";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_pcie_parf:slv-qhs-pcie-parf {
+			cell-id = <MSM_BUS_SLAVE_PCIE_PARF>;
+			label = "slv-qhs-pcie-parf";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_pdm:slv-qhs-pdm {
+			cell-id = <MSM_BUS_SLAVE_PDM>;
+			label = "slv-qhs-pdm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_prng:slv-qhs-prng {
+			cell-id = <MSM_BUS_SLAVE_PRNG>;
+			label = "slv-qhs-prng";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_qpic:slv-qhs-qpic {
+			cell-id = <MSM_BUS_SLAVE_QPIC>;
+			label = "slv-qhs-qpic";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_sdc1:slv-qhs-sdc1 {
+			cell-id = <MSM_BUS_SLAVE_SDCC_1>;
+			label = "slv-qhs-sdc1";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_snoc_cfg:slv-qhs-snoc-cfg {
+			cell-id = <MSM_BUS_SLAVE_SNOC_CFG>;
+			label = "slv-qhs-snoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qhm_snoc_cfg>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_spmi_fetcher:slv-qhs-spmi-fetcher {
+			cell-id = <MSM_BUS_SLAVE_SPMI_FETCHER>;
+			label = "slv-qhs-spmi-fetcher";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_tcsr:slv-qhs-tcsr {
+			cell-id = <MSM_BUS_SLAVE_TCSR>;
+			label = "slv-qhs-tcsr";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_tlmm:slv-qhs-tlmm {
+			cell-id = <MSM_BUS_SLAVE_TLMM>;
+			label = "slv-qhs-tlmm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_usb3:slv-qhs-usb3 {
+			cell-id = <MSM_BUS_SLAVE_USB3>;
+			label = "slv-qhs-usb3";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qhs_usb3_phy:slv-qhs-usb3-phy {
+			cell-id = <MSM_BUS_SLAVE_USB3_PHY_CFG>;
+			label = "slv-qhs-usb3-phy";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_pn0>;
+		};
+
+		slv_qns_aggre_noc:slv-qns-aggre-noc {
+			cell-id = <MSM_BUS_SLAVE_ANOC_SNOC>;
+			label = "slv-qns-aggre-noc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_aggre_noc>;
+		};
+
+		slv_qns_aggre_noc_ipa:slv-qns-aggre-noc-ipa {
+			cell-id = <MSM_BUS_SLAVE_ANOC_IPA>;
+			label = "slv-qns-aggre-noc-ipa";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_aggre_noc_ipa>;
+		};
+
+		slv_qns_snoc_memnoc:slv-qns-snoc-memnoc {
+			cell-id = <MSM_BUS_SLAVE_SNOC_MEM_NOC_GC>;
+			label = "slv-qns-snoc-memnoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_snoc_gc>;
+			qcom,bcms = <&bcm_sn0>;
+		};
+
+		slv_qxs_imem:slv-qxs-imem {
+			cell-id = <MSM_BUS_SLAVE_OCIMEM>;
+			label = "slv-qxs-imem";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn1>;
+		};
+
+		slv_qxs_pcie:slv-qxs-pcie {
+			cell-id = <MSM_BUS_SLAVE_PCIE_0>;
+			label = "slv-qxs-pcie";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn6>;
+		};
+
+		slv_srvc_snoc:slv-srvc-snoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_SNOC>;
+			label = "slv-srvc-snoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		slv_xs_qdss_stm:slv-xs-qdss-stm {
+			cell-id = <MSM_BUS_SLAVE_QDSS_STM>;
+			label = "slv-xs-qdss-stm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn3>;
+		};
+
+		slv_xs_sys_tcu_cfg:slv-xs-sys-tcu-cfg {
+			cell-id = <MSM_BUS_SLAVE_TCU>;
+			label = "slv-xs-sys-tcu-cfg";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-audio-overlay.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm/boot/dts/qcom/sdxpoorwills-cdp-audio-overlay.dtsi
index c06b806..a7943cd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp-audio-overlay.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,12 @@
  * GNU General Public License for more details.
  */
 
+#include "sdxpoorwills-audio-overlay.dtsi"
 
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&soc {
+	sound-tavil {
+		qcom,wsa-max-devs = <1>;
+		qcom,wsa-devs = <&wsa881x_0214>;
+		qcom,wsa-aux-dev-prefix = "SpkrRight";
+	};
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
new file mode 100644
index 0000000..15129c7
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
@@ -0,0 +1,107 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+
+#include "sdxpoorwills.dtsi"
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDXPOORWILLS CDP";
+	compatible = "qcom,sdxpoorwills-cdp",
+		"qcom,sdxpoorwills", "qcom,cdp";
+	qcom,board-id = <1 0x0>, <1 0x100>;
+};
+
+&blsp1_uart2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2_console_active>;
+	status = "ok";
+};
+
+&qnand_1 {
+	status = "ok";
+};
+
+&pmxpoorwills_vadc {
+	chan@83 {
+		label = "vph_pwr";
+		reg = <0x83>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <1>;
+		qcom,calibration-type = "absolute";
+		qcom,scale-function = <0>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@4c {
+		label = "xo_therm";
+		reg = <0x4c>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <4>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4d {
+		label = "pa_therm1";
+		reg = <0x4d>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4e {
+		label = "pa_therm2";
+		reg = <0x4e>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4f {
+		label = "mdm_case_therm";
+		reg = <0x4f>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@52 {
+		label = "ambient_therm";
+		reg = <0x52>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
new file mode 100644
index 0000000..8d7e377
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -0,0 +1,107 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+
+#include "sdxpoorwills.dtsi"
+#include "sdxpoorwills-pinctrl.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDXPOORWILLS MTP";
+	compatible = "qcom,sdxpoorwills-mtp",
+		"qcom,sdxpoorwills", "qcom,mtp";
+	qcom,board-id = <8 0x0>, <8 0x100>;
+};
+
+&blsp1_uart2 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2_console_active>;
+	status = "ok";
+};
+
+&qnand_1 {
+	status = "ok";
+};
+
+&pmxpoorwills_vadc {
+	chan@83 {
+		label = "vph_pwr";
+		reg = <0x83>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <1>;
+		qcom,calibration-type = "absolute";
+		qcom,scale-function = <0>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
+
+	chan@4c {
+		label = "xo_therm";
+		reg = <0x4c>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <4>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4d {
+		label = "pa_therm1";
+		reg = <0x4d>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4e {
+		label = "pa_therm2";
+		reg = <0x4e>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@4f {
+		label = "mdm_case_therm";
+		reg = <0x4f>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+
+	chan@52 {
+		label = "ambient_therm";
+		reg = <0x52>;
+		qcom,decimation = <2>;
+		qcom,pre-div-channel-scaling = <0>;
+		qcom,calibration-type = "ratiometric";
+		qcom,scale-function = <2>;
+		qcom,hw-settle-time = <2>;
+		qcom,fast-avg-setup = <0>;
+		qcom,vadc-thermal-node;
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
index ac02429..b6c04ec 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -31,5 +31,1257 @@
 				bias-disable;
 			};
 		};
+
+		/* I2C CONFIGURATION */
+		i2c_1 {
+			i2c_1_active: i2c_1_active {
+				mux {
+					pins = "gpio2", "gpio3";
+					function = "blsp_i2c1";
+				};
+
+				config {
+					pins = "gpio2", "gpio3";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_1_sleep: i2c_1_sleep {
+				mux {
+					pins = "gpio2", "gpio3";
+					function = "blsp_i2c1";
+				};
+
+				config {
+					pins = "gpio2", "gpio3";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_2 {
+			i2c_2_active: i2c_2_active {
+				mux {
+					pins = "gpio6", "gpio7";
+					function = "blsp_i2c2";
+				};
+
+				config {
+					pins = "gpio6", "gpio7";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_2_sleep: i2c_2_sleep {
+				mux {
+					pins = "gpio6", "gpio7";
+					function = "blsp_i2c2";
+				};
+
+				config {
+					pins = "gpio6", "gpio7";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_3 {
+			i2c_3_active: i2c_3_active {
+				mux {
+					pins = "gpio10", "gpio11";
+					function = "blsp_i2c3";
+				};
+
+				config {
+					pins = "gpio10", "gpio11";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_3_sleep: i2c_3_sleep {
+				mux {
+					pins = "gpio10", "gpio11";
+					function = "blsp_i2c3";
+				};
+
+				config {
+					pins = "gpio10", "gpio11";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_4 {
+			i2c_4_active: i2c_4_active {
+				mux {
+					pins = "gpio76", "gpio77";
+					function = "blsp_i2c4";
+				};
+
+				config {
+					pins = "gpio76", "gpio77";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_4_sleep: i2c_4_sleep {
+				mux {
+					pins = "gpio76", "gpio77";
+					function = "blsp_i2c4";
+				};
+
+				config {
+					pins = "gpio76", "gpio77";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_5 {
+			i2c_5_active: i2c_5_active {
+				mux {
+					pins = "gpio74", "gpio75";
+					function = "blsp_i2c1";
+				};
+
+				config {
+					pins = "gpio74", "gpio75";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_5_sleep: i2c_5_sleep {
+				mux {
+					pins = "gpio74", "gpio75";
+					function = "blsp_i2c1";
+				};
+
+				config {
+					pins = "gpio74", "gpio75";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_6 {
+			i2c_6_active: i2c_6_active {
+				mux {
+					pins = "gpio65", "gpio66";
+					function = "blsp_i2c2";
+				};
+
+				config {
+					pins = "gpio65", "gpio66";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_6_sleep: i2c_6_sleep {
+				mux {
+					pins = "gpio65", "gpio66";
+					function = "blsp_i2c2";
+				};
+
+				config {
+					pins = "gpio65", "gpio66";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		i2c_7 {
+			i2c_7_active: i2c_7_active {
+				mux {
+					pins = "gpio18", "gpio19";
+					function = "blsp_i2c4";
+				};
+
+				config {
+					pins = "gpio18", "gpio19";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_7_sleep: i2c_7_sleep {
+				mux {
+					pins = "gpio18", "gpio19";
+					function = "blsp_i2c4";
+				};
+
+				config {
+					pins = "gpio18", "gpio19";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		/* SPI CONFIGURATION */
+		spi_1 {
+			spi_1_active: spi_1_active {
+				mux {
+					pins = "gpio72", "gpio73",
+							"gpio74", "gpio75";
+					function = "blsp_spi1";
+				};
+
+				config {
+					pins = "gpio72", "gpio73",
+							"gpio74", "gpio75";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+
+			spi_1_sleep: spi_1_sleep {
+				mux {
+					pins = "gpio72", "gpio73",
+							"gpio74", "gpio75";
+					function = "blsp_spi1";
+				};
+
+				config {
+					pins = "gpio72", "gpio73",
+							"gpio74", "gpio75";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+		};
+
+		spi_2 {
+			spi_2_active: spi_2_active {
+				mux {
+					pins = "gpio4", "gpio5",
+							"gpio6", "gpio7";
+					function = "blsp_spi2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5",
+							"gpio6", "gpio7";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+
+			spi_2_sleep: spi_2_sleep {
+				mux {
+					pins = "gpio4", "gpio5",
+							"gpio6", "gpio7";
+					function = "blsp_spi2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5",
+							"gpio6", "gpio7";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+		};
+
+		spi_3 {
+			spi_3_active: spi_3_active {
+				mux {
+					pins = "gpio8", "gpio9",
+							"gpio10", "gpio11";
+					function = "blsp_spi3";
+				};
+
+				config {
+					pins = "gpio8", "gpio9",
+							"gpio10", "gpio11";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+
+			spi_3_sleep: spi_3_sleep {
+				mux {
+					pins = "gpio8", "gpio9",
+							"gpio10", "gpio11";
+					function = "blsp_spi3";
+				};
+
+				config {
+					pins = "gpio8", "gpio9",
+							"gpio10", "gpio11";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+		};
+
+		spi_4 {
+			spi_4_active: spi_4_active {
+				mux {
+					pins = "gpio16", "gpio17",
+							"gpio18", "gpio19";
+					function = "blsp_spi4";
+				};
+
+				config {
+					pins = "gpio16", "gpio17",
+							"gpio18", "gpio19";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+
+			spi_4_sleep: spi_4_sleep {
+				mux {
+					pins = "gpio16", "gpio17",
+							"gpio18", "gpio19";
+					function = "blsp_spi4";
+				};
+
+				config {
+					pins = "gpio16", "gpio17",
+							"gpio18", "gpio19";
+					drive-strength = <6>;
+					bias-disable;
+				};
+			};
+		};
+
+		/* HS UART CONFIGURATION */
+
+		blsp1_uart1a: blsp1_uart1a {
+			blsp1_uart1a_tx_active: blsp1_uart1a_tx_active {
+				mux {
+					pins = "gpio0";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio0";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1a_tx_sleep: blsp1_uart1a_tx_sleep {
+				mux {
+					pins = "gpio0";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio0";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart1a_rxcts_active: blsp1_uart1a_rxcts_active {
+				mux {
+					pins = "gpio1", "gpio2";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio1", "gpio2";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1a_rxcts_sleep: blsp1_uart1a_rxcts_sleep {
+				mux {
+					pins = "gpio1", "gpio2";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio1", "gpio2";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart1a_rfr_active: blsp1_uart1a_rfr_active {
+				mux {
+					pins = "gpio3";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio3";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1a_rfr_sleep: blsp1_uart1a_rfr_sleep {
+				mux {
+					pins = "gpio3";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio3";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart1b: blsp1_uart1b {
+			blsp1_uart1b_tx_active: blsp1_uart1b_tx_active {
+				mux {
+					pins = "gpio20";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio20";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1b_tx_sleep: blsp1_uart1b_tx_sleep {
+				mux {
+					pins = "gpio20";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio20";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart1b_rxcts_active: blsp1_uart1b_rxcts_active {
+				mux {
+					pins = "gpio21", "gpio22";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio21", "gpio22";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1b_rxcts_sleep: blsp1_uart1b_rxcts_sleep {
+				mux {
+					pins = "gpio21", "gpio22";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio21", "gpio22";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart1b_rfr_active: blsp1_uart1b_rfr_active {
+				mux {
+					pins = "gpio23";
+					function = "blsp_uart1";
+				};
+
+				config {
+					pins = "gpio23";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart1b_rfr_sleep: blsp1_uart1b_rfr_sleep {
+				mux {
+					pins = "gpio23";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio23";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart2a: blsp1_uart2a {
+			blsp1_uart2a_tx_active: blsp1_uart2a_tx_active {
+				mux {
+					pins = "gpio4";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio4";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2a_tx_sleep: blsp1_uart2a_tx_sleep {
+				mux {
+					pins = "gpio4";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio4";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart2a_rxcts_active: blsp1_uart2a_rxcts_active {
+				mux {
+					pins = "gpio5", "gpio6";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio5", "gpio6";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2a_rxcts_sleep: blsp1_uart2a_rxcts_sleep {
+				mux {
+					pins = "gpio5", "gpio6";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio1", "gpio2";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart2a_rfr_active: blsp1_uart2a_rfr_active {
+				mux {
+					pins = "gpio7";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio7";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2a_rfr_sleep: blsp1_uart2a_rfr_sleep {
+				mux {
+					pins = "gpio7";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio7";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart2b: blsp1_uart2b {
+			blsp1_uart2b_tx_active: blsp1_uart2b_tx_active {
+				mux {
+					pins = "gpio63";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio63";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2b_tx_sleep: blsp1_uart2b_tx_sleep {
+				mux {
+					pins = "gpio63";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio63";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart2b_rxcts_active: blsp1_uart2b_rxcts_active {
+				mux {
+					pins = "gpio64", "gpio65";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio64", "gpio65";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2b_rxcts_sleep: blsp1_uart2b_rxcts_sleep {
+				mux {
+					pins = "gpio64", "gpio65";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio64", "gpio65";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart2b_rfr_active: blsp1_uart2b_rfr_active {
+				mux {
+					pins = "gpio66";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio66";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart2b_rfr_sleep: blsp1_uart2b_rfr_sleep {
+				mux {
+					pins = "gpio66";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio66";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart3: blsp1_uart3 {
+			blsp1_uart3_tx_active: blsp1_uart3_tx_active {
+				mux {
+					pins = "gpio8";
+					function = "blsp_uart3";
+				};
+
+				config {
+					pins = "gpio8";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart3_tx_sleep: blsp1_uart3_tx_sleep {
+				mux {
+					pins = "gpio8";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio8";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart3_rxcts_active: blsp1_uart3_rxcts_active {
+				mux {
+					pins = "gpio9", "gpio10";
+					function = "blsp_uart3";
+				};
+
+				config {
+					pins = "gpio9", "gpio10";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart3_rxcts_sleep: blsp1_uart3_rxcts_sleep {
+				mux {
+					pins = "gpio9", "gpio10";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio9", "gpio10";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart3_rfr_active: blsp1_uart3_rfr_active {
+				mux {
+					pins = "gpio11";
+					function = "blsp_uart3";
+				};
+
+				config {
+					pins = "gpio11";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart3_rfr_sleep: blsp1_uart3_rfr_sleep {
+				mux {
+					pins = "gpio11";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio11";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart4a: blsp1_uart4a {
+			blsp1_uart4a_tx_active: blsp1_uart4a_tx_active {
+				mux {
+					pins = "gpio20";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio20";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4a_tx_sleep: blsp1_uart4a_tx_sleep {
+				mux {
+					pins = "gpio20";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio20";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart4a_rxcts_active: blsp1_uart4a_rxcts_active {
+				mux {
+					pins = "gpio21", "gpio22";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio21", "gpio22";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4a_rxcts_sleep: blsp1_uart4a_rxcts_sleep {
+				mux {
+					pins = "gpio21", "gpio22";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio21", "gpio22";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart4a_rfr_active: blsp1_uart4a_rfr_active {
+				mux {
+					pins = "gpio23";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio23";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4a_rfr_sleep: blsp1_uart4a_rfr_sleep {
+				mux {
+					pins = "gpio23";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio23";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		blsp1_uart4b: blsp1_uart4b {
+			blsp1_uart4b_tx_active: blsp1_uart4b_tx_active {
+				mux {
+					pins = "gpio16";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio16";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4b_tx_sleep: blsp1_uart4b_tx_sleep {
+				mux {
+					pins = "gpio16";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio16";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			blsp1_uart4b_rxcts_active: blsp1_uart4b_rxcts_active {
+				mux {
+					pins = "gpio17", "gpio18";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio17", "gpio18";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4b_rxcts_sleep: blsp1_uart4b_rxcts_sleep {
+				mux {
+					pins = "gpio17", "gpio18";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio17", "gpio18";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+
+			blsp1_uart4b_rfr_active: blsp1_uart4b_rfr_active {
+				mux {
+					pins = "gpio19";
+					function = "blsp_uart4";
+				};
+
+				config {
+					pins = "gpio19";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			blsp1_uart4b_rfr_sleep: blsp1_uart4b_rfr_sleep {
+				mux {
+					pins = "gpio19";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio19";
+					drive-strength = <2>;
+					bias-no-pull;
+				};
+			};
+		};
+
+		wcd9xxx_intr {
+			wcd_intr_default: wcd_intr_default{
+				mux {
+					pins = "gpio71";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio71";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down; /* pull down */
+					input-enable;
+				};
+			};
+		};
+
+		cdc_reset_ctrl {
+			cdc_reset_sleep: cdc_reset_sleep {
+				mux {
+					pins = "gpio77";
+					function = "gpio";
+				};
+				config {
+					pins = "gpio77";
+					drive-strength = <2>;
+					bias-disable;
+					output-low;
+				};
+			};
+
+			cdc_reset_active:cdc_reset_active {
+				mux {
+					pins = "gpio77";
+					function = "gpio";
+				};
+				config {
+					pins = "gpio77";
+					drive-strength = <8>;
+					bias-pull-down;
+					output-high;
+				};
+			};
+		};
+
+		i2s_mclk {
+			i2s_mclk_sleep: i2s_mclk_sleep {
+				mux {
+					pins = "gpio62";
+					function = "i2s_mclk";
+				};
+
+				config {
+					pins = "gpio62";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+				};
+			};
+
+			i2s_mclk_active: i2s_mclk_active {
+				mux {
+					pins = "gpio62";
+					function = "i2s_mclk";
+				};
+
+				config {
+					pins = "gpio62";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+		};
+
+		pmx_pri_mi2s_aux {
+			pri_ws_sleep: pri_ws_sleep {
+				mux {
+					pins = "gpio12";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio12";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			pri_sck_sleep: pri_sck_sleep {
+				mux {
+					pins = "gpio15";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio15";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			pri_dout_sleep: pri_dout_sleep {
+				mux {
+					pins = "gpio14";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio14";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			pri_ws_active_master: pri_ws_active_master {
+				mux {
+					pins = "gpio12";
+					function = "pri_mi2s_ws_a";
+				};
+
+				config {
+					pins = "gpio12";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+
+			pri_sck_active_master: pri_sck_active_master {
+				mux {
+					pins = "gpio15";
+					function = "pri_mi2s_sck_a";
+				};
+
+				config {
+					pins = "gpio15";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+
+			pri_ws_active_slave: pri_ws_active_slave {
+				mux {
+					pins = "gpio12";
+					function = "pri_mi2s_ws_a";
+				};
+
+				config {
+					pins = "gpio12";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+				};
+			};
+
+			pri_sck_active_slave: pri_sck_active_slave {
+				mux {
+					pins = "gpio15";
+					function = "pri_mi2s_sck_a";
+				};
+
+				config {
+					pins = "gpio15";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+				};
+			};
+
+			pri_dout_active: pri_dout_active {
+				mux {
+					pins = "gpio14";
+					function = "pri_mi2s_data1_a";
+				};
+
+				config {
+					pins = "gpio14";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+		};
+
+		pmx_pri_mi2s_aux_din {
+			pri_din_sleep: pri_din_sleep {
+				mux {
+					pins = "gpio13";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio13";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			pri_din_active: pri_din_active {
+				mux {
+					pins = "gpio13";
+					function = "pri_mi2s_data0_a";
+				};
+
+				config {
+					pins = "gpio13";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL */
+				};
+			};
+		};
+
+		pmx_sec_mi2s_aux {
+			sec_ws_sleep: sec_ws_sleep {
+				mux {
+					pins = "gpio16";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio16";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			sec_sck_sleep: sec_sck_sleep {
+				mux {
+					pins = "gpio19";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio19";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			sec_dout_sleep: sec_dout_sleep {
+				mux {
+					pins = "gpio18";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio18";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			sec_ws_active_master: sec_ws_active_master {
+				mux {
+					pins = "gpio16";
+					function = "sec_mi2s_ws_a";
+				};
+
+				config {
+					pins = "gpio16";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+
+			sec_sck_active_master: sec_sck_active_master {
+				mux {
+					pins = "gpio19";
+					function = "sec_mi2s_sck_a";
+				};
+
+				config {
+					pins = "gpio19";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+
+			sec_ws_active_slave: sec_ws_active_slave {
+				mux {
+					pins = "gpio16";
+					function = "sec_mi2s_ws_a";
+				};
+
+				config {
+					pins = "gpio16";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+				};
+			};
+
+			sec_sck_active_slave: sec_sck_active_slave {
+				mux {
+					pins = "gpio19";
+					function = "sec_mi2s_sck_a";
+				};
+
+				config {
+					pins = "gpio19";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+				};
+			};
+
+			sec_dout_active: sec_dout_active {
+				mux {
+					pins = "gpio18";
+					function = "sec_mi2s_data1_a";
+				};
+
+				config {
+					pins = "gpio18";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL*/
+					output-high;
+				};
+			};
+		};
+
+		pmx_sec_mi2s_aux_din {
+			sec_din_sleep: sec_din_sleep {
+				mux {
+					pins = "gpio17";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio17";
+					drive-strength = <2>;	/* 2 mA */
+					bias-pull-down;		/* PULL DOWN */
+					input-enable;
+				};
+			};
+
+			sec_din_active: sec_din_active {
+				mux {
+					pins = "gpio17";
+					function = "sec_mi2s_data0_a";
+				};
+
+				config {
+					pins = "gpio17";
+					drive-strength = <8>;	/* 8 mA */
+					bias-disable;		/* NO PULL */
+				};
+			};
+		};
+	};
+};
+
+&pmxpoorwills_gpios {
+	ambient_therm {
+		ambient_therm_default: ambient_therm_default {
+			pins = "gpio2";
+			bias-high-impedance;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
index cc126f6..9947594 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-regulator.dtsi
@@ -12,103 +12,324 @@
 
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 
-/* Stub regulators */
-/ {
-	pmxpoorwills_s1: regualtor-pmxpoorwills-s1 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_s1";
-		qcom,hpm-min-load = <100000>;
-		regulator-min-microvolt = <752000>;
-		regulator-max-microvolt = <752000>;
+&soc {
+	/* RPMh regulators */
+
+	/* pmxpoorwills S1 - VDD_MODEM supply */
+	rpmh-regulator-modemlvl {
+		compatible = "qcom,rpmh-arc-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "mss.lvl";
+		pmxpoorwills_s1_level: regualtor-pmxpoorwills-s1 {
+			regulator-name = "pmxpoorwills_s1_level";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+		};
 	};
 
-	/* VDD CX supply */
-	pmxpoorwills_s5_level: regualtor-pmxpoorwills-s5-level {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_s5_level";
-		qcom,hpm-min-load = <100000>;
-		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
-		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	rpmh-regulator-smpa4 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "smpa4";
+		pmxpoorwills_s4: regulator-pmxpoorwills-s4 {
+			 regulator-name = "pmxpoorwills_s4";
+			 qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			 regulator-min-microvolt = <1800000>;
+			 regulator-max-microvolt = <1800000>;
+			 qcom,init-voltage = <1800000>;
+		 };
 	};
 
-	pmxpoorwills_s5_level_ao: regualtor-pmxpoorwills-s5-level-ao {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_s5_level_ao";
-		qcom,hpm-min-load = <100000>;
-		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
-		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	/*  pmxpoorwills S5 - VDD_CX supply */
+	rpmh-regulator-cxlvl {
+		compatible = "qcom,rpmh-arc-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "cx.lvl";
+		pmxpoorwills_s5_level-parent-supply = <&pmxpoorwills_l9_level>;
+		pmxpoorwills_s5_level_ao-parent-supply =
+						<&pmxpoorwills_l9_level_ao>;
+		pmxpoorwills_s5_level: regualtor-pmxpoorwills-s5-level {
+			regulator-name = "pmxpoorwills_s5_level";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+			qcom,min-dropout-voltage-level = <(-1)>;
+		};
+
+		pmxpoorwills_s5_level_ao: regualtor-pmxpoorwills-s5-level-ao {
+			regulator-name = "pmxpoorwills_s5_level_ao";
+			qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
+			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+			qcom,min-dropout-voltage-level = <(-1)>;
+		};
 	};
 
-	pmxpoorwills_l1: regualtor-pmxpoorwills-11 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l1";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <1200000>;
-		regulator-max-microvolt = <1200000>;
+	rpmh-regulator-ldoa1 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa1";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l1: regualtor-pmxpoorwills-11 {
+			regulator-name = "pmxpoorwills_l1";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+			qcom,init-voltage = <1200000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	pmxpoorwills_l3: regualtor-pmxpoorwills-l3 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l3";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <800000>;
-		regulator-max-microvolt = <800000>;
+	rpmh-regulator-ldoa2 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa2";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l2: regualtor-pmxpoorwills-12 {
+			 regulator-name = "pmxpoorwills_l2";
+			 qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			 regulator-min-microvolt = <1128000>;
+			 regulator-max-microvolt = <1128000>;
+			 qcom,init-voltage = <1128000>;
+			 qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+			 regulator-always-on;
+		 };
 	};
 
-	pmxpoorwills_l4: regualtor-pmxpoorwills-l4 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l4";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <872000>;
-		regulator-max-microvolt = <872000>;
+	rpmh-regulator-ldoa3 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa3";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l3: regualtor-pmxpoorwills-l3 {
+			regulator-name = "pmxpoorwills_l3";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <800000>;
+			qcom,init-voltage = <800000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	pmxpoorwills_l5: regualtor-pmxpoorwills-l5 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l5";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <1800000>;
-		regulator-max-microvolt = <1800000>;
+	rpmh-regulator-ldoa4 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa4";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l4: regualtor-pmxpoorwills-l4 {
+			regulator-name = "pmxpoorwills_l4";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <872000>;
+			regulator-max-microvolt = <872000>;
+			qcom,init-voltage = <872000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	pmxpoorwills_l6: regualtor-pmxpoorwills-l6 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l6";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <1800000>;
-		regulator-max-microvolt = <1800000>;
+	rpmh-regulator-ldoa5 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa5";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l5: regualtor-pmxpoorwills-l5 {
+			regulator-name = "pmxpoorwills_l5";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <1704000>;
+			regulator-max-microvolt = <1704000>;
+			qcom,init-voltage = <1704000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	pmxpoorwills_l8: regualtor-pmxpoorwills-l8 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l8";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <800000>;
-		regulator-max-microvolt = <800000>;
+	rpmh-regulator-ldoa7 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa7";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l7: regualtor-pmxpoorwills-l7 {
+			regulator-name = "pmxpoorwills_l7";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <2952000>;
+			regulator-max-microvolt = <2952000>;
+			qcom,init-voltage = <2952000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	/* VDD MX supply */
-	pmxpoorwills_l9_level: regualtor-pmxpoorwills-l9-level {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l9_level";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
-		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	rpmh-regulator-ldoa8 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa8";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l8: regualtor-pmxpoorwills-l8 {
+			regulator-name = "pmxpoorwills_l8";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <800000>;
+			qcom,init-voltage = <800000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
 	};
 
-	pmxpoorwills_l9_level_ao: regualtor-pmxpoorwills-l9-level_ao {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l9_level_ao";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
-		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	/* pmxpoorwills L9 - VDD_MX supply */
+	rpmh-regulator-mxlvl {
+		compatible = "qcom,rpmh-arc-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "mx.lvl";
+		pmxpoorwills_l9_level: regualtor-pmxpoorwills-l9-level {
+			regulator-name = "pmxpoorwills_l9_level";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+		};
+
+		pmxpoorwills_l9_level_ao: regualtor-pmxpoorwills-l9-level-ao {
+			regulator-name = "pmxpoorwills_l9_level_ao";
+			qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
+			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+		};
 	};
 
-	pmxpoorwills_l10: regualtor-pmxpoorwills-l10 {
-		compatible = "qcom,stub-regulator";
-		regulator-name = "pmxpoorwills_l10";
-		qcom,hpm-min-load = <10000>;
-		regulator-min-microvolt = <3088000>;
-		regulator-max-microvolt = <3088000>;
+	rpmh-regulator-ldoa10 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa10";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l10: regualtor-pmxpoorwills-l10 {
+			regulator-name = "pmxpoorwills_l10";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			regulator-min-microvolt = <3088000>;
+			regulator-max-microvolt = <3088000>;
+			qcom,init-voltage = <3088000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		};
+	};
+
+	rpmh-regulator-ldoa11 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa11";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l11: regualtor-pmxpoorwills-l11 {
+			  regulator-name = "pmxpoorwills_l11";
+			  qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			  regulator-min-microvolt = <1808000>;
+			  regulator-max-microvolt = <1808000>;
+			  qcom,init-voltage = <1808000>;
+			  qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		  };
+	};
+
+	rpmh-regulator-ldoa12 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa12";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l12: regualtor-pmxpoorwills-l12 {
+			  regulator-name = "pmxpoorwills_l12";
+			  qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			  regulator-min-microvolt = <2704000>;
+			  regulator-max-microvolt = <2704000>;
+			  qcom,init-voltage = <2704000>;
+			  qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		  };
+	};
+
+	rpmh-regulator-ldoa13 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa13";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l13: regualtor-pmxpoorwills-l13 {
+			  regulator-name = "pmxpoorwills_l13";
+			  qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			  regulator-min-microvolt = <1808000>;
+			  regulator-max-microvolt = <1808000>;
+			  qcom,init-voltage = <1808000>;
+			  qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		  };
+	};
+
+	rpmh-regulator-ldoa14 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa14";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l14: regualtor-pmxpoorwills-l14 {
+			  regulator-name = "pmxpoorwills_l14";
+			  qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			  regulator-min-microvolt = <620000>;
+			  regulator-max-microvolt = <620000>;
+			  qcom,init-voltage = <620000>;
+			  qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+		  };
+	};
+
+	rpmh-regulator-ldoa16 {
+		compatible = "qcom,rpmh-vrm-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "ldoa16";
+		qcom,supported-modes =
+			<RPMH_REGULATOR_MODE_LDO_LPM
+			 RPMH_REGULATOR_MODE_LDO_HPM>;
+		qcom,mode-threshold-currents = <0 1>;
+		pmxpoorwills_l16: regualtor-pmxpoorwills-l16 {
+			  regulator-name = "pmxpoorwills_l16";
+			  qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			  regulator-min-microvolt = <752000>;
+			  regulator-max-microvolt = <752000>;
+			  qcom,init-voltage = <752000>;
+			  qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
+			  regulator-always-on;
+		  };
+	};
+
+	/* VREF_RGMII */
+	rpmh-regulator-rgmii {
+		compatible = "qcom,rpmh-xob-regulator";
+		mboxes = <&apps_rsc 0>;
+		qcom,resource-name = "vrefa2";
+		vreg_rgmii: regulator-rgmii {
+			regulator-name = "vreg_rgmii";
+			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
index b3103cd..aa9e7f2 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
@@ -23,6 +23,30 @@
 	qcom,board-id = <15 0>;
 };
 
+&soc {
+	/* Delete rpmh regulators */
+	/delete-node/ rpmh-regulator-modemlvl;
+	/delete-node/ rpmh-regulator-smpa4;
+	/delete-node/ rpmh-regulator-cxlvl;
+	/delete-node/ rpmh-regulator-ldoa1;
+	/delete-node/ rpmh-regulator-ldoa2;
+	/delete-node/ rpmh-regulator-ldoa3;
+	/delete-node/ rpmh-regulator-ldoa4;
+	/delete-node/ rpmh-regulator-ldoa5;
+	/delete-node/ rpmh-regulator-ldoa7;
+	/delete-node/ rpmh-regulator-ldoa8;
+	/delete-node/ rpmh-regulator-mxlvl;
+	/delete-node/ rpmh-regulator-ldoa10;
+	/delete-node/ rpmh-regulator-ldoa11;
+	/delete-node/ rpmh-regulator-ldoa12;
+	/delete-node/ rpmh-regulator-ldoa13;
+	/delete-node/ rpmh-regulator-ldoa14;
+	/delete-node/ rpmh-regulator-ldoa16;
+	/delete-node/ rpmh-regulator-rgmii;
+};
+
+#include "sdxpoorwills-stub-regulator.dtsi"
+
 &blsp1_uart2 {
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart2_console_active>;
@@ -68,3 +92,7 @@
 &usb3_qmp_phy {
 	status = "disabled";
 };
+
+&qnand_1 {
+	status = "ok";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-smp2p.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-smp2p.dtsi
new file mode 100644
index 0000000..f9ad6f4
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-smp2p.dtsi
@@ -0,0 +1,109 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+&soc {
+	qcom,smp2p-modem@17811008 {
+		compatible = "qcom,smp2p";
+		reg = <0x17811008 0x4>;
+		qcom,remote-pid = <1>;
+		qcom,irq-bitmask = <0x4000>;
+		interrupts = <GIC_SPI 113 IRQ_TYPE_EDGE_RISING>;
+	};
+
+	smp2pgpio_smp2p_15_in: qcom,smp2pgpio-smp2p-15-in {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "smp2p";
+		qcom,remote-pid = <15>;
+		qcom,is-inbound;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	qcom,smp2pgpio_test_smp2p_15_in {
+		compatible = "qcom,smp2pgpio_test_smp2p_15_in";
+		gpios = <&smp2pgpio_smp2p_15_in 0 0>;
+	};
+
+	smp2pgpio_smp2p_15_out: qcom,smp2pgpio-smp2p-15-out {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "smp2p";
+		qcom,remote-pid = <15>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	qcom,smp2pgpio_test_smp2p_15_out {
+		compatible = "qcom,smp2pgpio_test_smp2p_15_out";
+		gpios = <&smp2pgpio_smp2p_15_out 0 0>;
+	};
+
+	smp2pgpio_smp2p_1_in: qcom,smp2pgpio-smp2p-1-in {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "smp2p";
+		qcom,remote-pid = <1>;
+		qcom,is-inbound;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	qcom,smp2pgpio_test_smp2p_1_in {
+		compatible = "qcom,smp2pgpio_test_smp2p_1_in";
+		gpios = <&smp2pgpio_smp2p_1_in 0 0>;
+	};
+
+	smp2pgpio_smp2p_1_out: qcom,smp2pgpio-smp2p-1-out {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "smp2p";
+		qcom,remote-pid = <1>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	qcom,smp2pgpio_test_smp2p_1_out {
+		compatible = "qcom,smp2pgpio_test_smp2p_1_out";
+		gpios = <&smp2pgpio_smp2p_1_out 0 0>;
+	};
+
+	/* ssr - inbound entry from mss */
+	smp2pgpio_ssr_smp2p_1_in: qcom,smp2pgpio-ssr-smp2p-1-in {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "slave-kernel";
+		qcom,remote-pid = <1>;
+		qcom,is-inbound;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+
+	/* ssr - outbound entry to mss */
+	smp2pgpio_ssr_smp2p_1_out: qcom,smp2pgpio-ssr-smp2p-1-out {
+		compatible = "qcom,smp2pgpio";
+		qcom,entry-name = "master-kernel";
+		qcom,remote-pid = <1>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
+
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-stub-regulator.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-stub-regulator.dtsi
new file mode 100644
index 0000000..7c6b7b0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-stub-regulator.dtsi
@@ -0,0 +1,176 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
+
+/* Stub regulators */
+/ {
+	pmxpoorwills_s1: regualtor-pmxpoorwills-s1 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_s1";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <752000>;
+		regulator-max-microvolt = <752000>;
+	};
+
+	pmxpoorwills_s4: regualtor-pmxpoorwills-s4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_s4";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	/* VDD CX supply */
+	pmxpoorwills_s5_level: regualtor-pmxpoorwills-s5-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_s5_level";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pmxpoorwills_s5_level_ao: regualtor-pmxpoorwills-s5-level-ao {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_s5_level_ao";
+		qcom,hpm-min-load = <100000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pmxpoorwills_l1: regualtor-pmxpoorwills-11 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l1";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+	};
+
+	pmxpoorwills_l2: regualtor-pmxpoorwills-12 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l2";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1128000>;
+		regulator-max-microvolt = <1128000>;
+	};
+
+	pmxpoorwills_l3: regualtor-pmxpoorwills-l3 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l3";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <800000>;
+	};
+
+	pmxpoorwills_l4: regualtor-pmxpoorwills-l4 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l4";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <872000>;
+		regulator-max-microvolt = <872000>;
+	};
+
+	pmxpoorwills_l5: regualtor-pmxpoorwills-l5 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l5";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	pmxpoorwills_l7: regualtor-pmxpoorwills-l7 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l7";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <2950000>;
+	};
+
+	pmxpoorwills_l8: regualtor-pmxpoorwills-l8 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l8";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <800000>;
+		regulator-max-microvolt = <800000>;
+	};
+
+	/* VDD MX supply */
+	pmxpoorwills_l9_level: regualtor-pmxpoorwills-l9-level {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l9_level";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pmxpoorwills_l9_level_ao: regualtor-pmxpoorwills-l9-level_ao {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l9_level_ao";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
+		regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
+	};
+
+	pmxpoorwills_l10: regualtor-pmxpoorwills-l10 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l10";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <3088000>;
+		regulator-max-microvolt = <3088000>;
+	};
+
+	pmxpoorwills_l11: regualtor-pmxpoorwills-l11 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l11";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1808000>;
+		regulator-max-microvolt = <2848000>;
+	};
+
+	pmxpoorwills_l12: regualtor-pmxpoorwills-l12 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l12";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <2704000>;
+		regulator-max-microvolt = <2704000>;
+	};
+
+	pmxpoorwills_l13: regualtor-pmxpoorwills-l13 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l13";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <1808000>;
+		regulator-max-microvolt = <2848000>;
+	};
+
+	pmxpoorwills_l14: regualtor-pmxpoorwills-l14 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l14";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <620000>;
+		regulator-max-microvolt = <752000>;
+	};
+
+	pmxpoorwills_l16: regualtor-pmxpoorwills-l16 {
+		compatible = "qcom,stub-regulator";
+		regulator-name = "pmxpoorwills_l16";
+		qcom,hpm-min-load = <10000>;
+		regulator-min-microvolt = <752000>;
+		regulator-max-microvolt = <752000>;
+	};
+
+	 /* VREF_RGMII */
+	vreg_rgmii: rgmii-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vreg_rgmii";
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi
new file mode 100644
index 0000000..5a4810a
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-thermal.dtsi
@@ -0,0 +1,293 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/thermal/thermal.h>
+
+&soc {
+	qmi-tmd-devices {
+		compatible = "qcom,qmi_cooling_devices";
+
+		modem {
+			qcom,instance-id = <0x0>;
+
+			modem_pa: modem_pa {
+				qcom,qmi-dev-name = "pa";
+				#cooling-cells = <2>;
+			};
+
+			modem_proc: modem_proc {
+				qcom,qmi-dev-name = "modem";
+				#cooling-cells = <2>;
+			};
+
+			modem_current: modem_current {
+				qcom,qmi-dev-name = "modem_current";
+				#cooling-cells = <2>;
+			};
+
+			modem_skin: modem_skin {
+				qcom,qmi-dev-name = "modem_skin";
+				#cooling-cells = <2>;
+			};
+
+			modem_vdd: modem_vdd {
+				qcom,qmi-dev-name = "cpuv_restriction_cold";
+				#cooling-cells = <2>;
+			};
+		};
+
+		adsp {
+			qcom,instance-id = <0x1>;
+
+			adsp_vdd: adsp_vdd {
+				qcom,qmi-dev-name = "cpuv_restriction_cold";
+				#cooling-cells = <2>;
+			};
+		};
+	};
+};
+
+&thermal_zones {
+	aoss-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 0>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mdm-q6-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 1>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	ddrss-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 2>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 3>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mdm-core-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 4>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mdm-vpe-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 5>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	aoss-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 0>;
+		tracks-low;
+		trips {
+			aoss_trip: aoss-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&aoss_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&aoss_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mdm-q6-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 1>;
+		tracks-low;
+		trips {
+			mdm_q6_trip: mdm-q6-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&mdm_q6_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&mdm_q6_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+
+	ddrss-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 2>;
+		tracks-low;
+		trips {
+			ddrss_trip: ddrss-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&ddrss_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&ddrss_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 3>;
+		tracks-low;
+		trips {
+			cpu_trip: cpu-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&cpu_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mdm-core-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 4>;
+		tracks-low;
+		trips {
+			mdm_trip: mdm-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mdm-vpe-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 5>;
+		tracks-low;
+		trips {
+			mdm_vpe_trip: mdm-vpe-trip {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			modem_vdd_cdev {
+				trip = <&mdm_vpe_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&mdm_vpe_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-wcd.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-wcd.dtsi
new file mode 100644
index 0000000..9303ed1
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-wcd.dtsi
@@ -0,0 +1,80 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&i2c_3 {
+	tavil_codec {
+		wcd: wcd_pinctrl@5 {
+			compatible = "qcom,wcd-pinctrl";
+			qcom,num-gpios = <5>;
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			spkr_1_wcd_en_active: spkr_1_wcd_en_active {
+				mux {
+					pins = "gpio2";
+				};
+
+				config {
+					pins = "gpio2";
+					output-high;
+				};
+			};
+
+			spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep {
+				mux {
+					pins = "gpio2";
+				};
+
+				config {
+					pins = "gpio2";
+					input-enable;
+				};
+			};
+
+			spkr_2_wcd_en_active: spkr_2_sd_n_active {
+				mux {
+					pins = "gpio3";
+				};
+
+				config {
+					pins = "gpio3";
+					output-high;
+				};
+			};
+
+			spkr_2_wcd_en_sleep: spkr_2_sd_n_sleep {
+				mux {
+					pins = "gpio3";
+				};
+
+				config {
+					pins = "gpio3";
+					input-enable;
+				};
+			};
+		};
+
+		wsa_spkr_wcd_sd1: msm_cdc_pinctrll {
+		      compatible = "qcom,msm-cdc-pinctrl";
+		      pinctrl-names = "aud_active", "aud_sleep";
+		      pinctrl-0 = <&spkr_1_wcd_en_active>;
+		      pinctrl-1 = <&spkr_1_wcd_en_sleep>;
+		};
+
+		wsa_spkr_wcd_sd2: msm_cdc_pinctrlr {
+		      compatible = "qcom,msm-cdc-pinctrl";
+		      pinctrl-names = "aud_active", "aud_sleep";
+		      pinctrl-0 = <&spkr_2_wcd_en_active>;
+		      pinctrl-1 = <&spkr_2_wcd_en_sleep>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index 2ef277f..b0be698 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -10,12 +10,12 @@
  * GNU General Public License for more details.
  */
 
-
+#include <dt-bindings/soc/qcom,tcs-mbox.h>
 #include "skeleton.dtsi"
-
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 
 / {
 	model = "Qualcomm Technologies, Inc. SDX POORWILLS";
@@ -34,6 +34,19 @@
 			reg = <0x8fd00000 0x300000>;
 			label = "peripheral2_mem";
 		};
+
+		mss_mem: mss_region@87800000 {
+			compatible = "removed-dma-pool";
+			no-map;
+			reg = <0x87800000 0x8000000>;
+			label = "mss_mem";
+		};
+
+		audio_mem: audio_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0x400000>;
+		 };
 	};
 
 	cpus {
@@ -44,9 +57,14 @@
 			device-type = "cpu";
 			compatible = "arm,cortex-a7";
 			reg = <0x0>;
+			#cooling-cells = <2>;
 		};
 	};
 
+	aliases {
+		qpic_nand1 = &qnand_1;
+	};
+
 	soc: soc { };
 };
 
@@ -140,22 +158,40 @@
 	};
 
 	clock_gcc: qcom,gcc@100000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "gcc_clocks";
+		compatible = "qcom,gcc-sdxpoorwills";
+		reg = <0x100000 0x1f0000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pmxpoorwills_s5_level>;
+		vdd_cx_ao-supply = <&pmxpoorwills_s5_level_ao>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
-	clock_cpu: qcom,clock-a7@17810008 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "cpu_clocks";
+	clock_cpu: qcom,clock-a7@17808100 {
+		compatible = "qcom,cpu-sdxpoorwills";
+		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
+		clock-names = "xo_ao";
+		qcom,a7cc-init-rate = <1497600000>;
+		reg = <0x17808100 0x7F10>;
+		reg-names = "apcs_pll";
+		qcom,rcg-reg-offset = <0x7F08>;
+
+		vdd_dig_ao-supply = <&pmxpoorwills_s5_level_ao>;
+		cpu-vdd-supply = <&pmxpoorwills_s5_level_ao>;
+		qcom,speed0-bin-v0 =
+			<          0 RPMH_REGULATOR_LEVEL_OFF>,
+			<  345600000 RPMH_REGULATOR_LEVEL_LOW_SVS>,
+			<  576000000 RPMH_REGULATOR_LEVEL_SVS>,
+			< 1094400000 RPMH_REGULATOR_LEVEL_NOM>,
+			< 1497600000 RPMH_REGULATOR_LEVEL_TURBO>;
 		#clock-cells = <1>;
 	};
 
 	clock_rpmh: qcom,rpmhclk {
-		compatible = "qcom,dummycc";
-		clock-output-names = "rpmh_clocks";
+		compatible = "qcom,rpmh-clk-sdxpoorwills";
 		#clock-cells = <1>;
+		mboxes = <&apps_rsc 0>;
+		mbox-names = "apps";
 	};
 
 	blsp1_uart2: serial@831000 {
@@ -163,7 +199,7 @@
 		reg = <0x831000 0x200>;
 		interrupts = <0 26 0>;
 		status = "disabled";
-		clocks = <&clock_gcc GCC_BLSP1_UART2_APPS_CLK>,
+		clocks = <&clock_gcc GCC_BLSP1_UART3_APPS_CLK>,
 			<&clock_gcc GCC_BLSP1_AHB_CLK>;
 		clock-names = "core", "iface";
 	};
@@ -172,7 +208,6 @@
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_usb30";
 		reg = <0x0010b004 0x4>;
-		status = "ok";
 	};
 
 	qcom,sps {
@@ -184,7 +219,36 @@
 		compatible = "qcom,gdsc";
 		regulator-name = "gdsc_pcie";
 		reg = <0x00137004 0x4>;
-		status = "ok";
+	};
+
+	gdsc_emac: qcom,gdsc@147004 {
+		compatible = "qcom,gdsc";
+		regulator-name = "gdsc_emac";
+		reg = <0x00147004 0x4>;
+	};
+
+	qnand_1: nand@1b00000 {
+		compatible = "qcom,msm-nand";
+		reg = < 0x01b00000 0x10000>,
+			<0x01b04000 0x1a000>;
+		reg-names = "nand_phys",
+			"bam_phys";
+		qcom,reg-adjustment-offset = <0x4000>;
+		qcom,qpic-clk-rpmh;
+
+		interrupts = <0 135 0>;
+		interrupt-names = "bam_irq";
+
+		qcom,msm-bus,name = "qpic_nand";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+
+		qcom,msm-bus,vectors-KBps =
+			<91 512 0 0>,
+			/* Voting for max b/w on PNOC bus for now */
+			<91 512 400000 400000>;
+
+		status = "disabled";
 	};
 
 	qcom,msm-imem@8600000 {
@@ -228,77 +292,7 @@
 		#thermal-sensor-cells = <1>;
 	};
 
-	thermal_zones: thermal-zones {
-		mpm-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 0>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		q6-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 1>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		ctile-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 2>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 3>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		mdm-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 4>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-	};
+	thermal_zones: thermal-zones { };
 
 	qcom,ipa_fws {
 		compatible = "qcom,pil-tz-generic";
@@ -324,8 +318,197 @@
 		#interrupt-cells = <4>;
 		cell-index = <0>;
 	};
+
+	qcom,ipc-spinlock@1f40000 {
+		compatible = "qcom,ipc-spinlock-sfpb";
+		reg = <0x1f40000 0x8000>;
+		qcom,num-locks = <8>;
+	};
+
+	qcom,smem@8fe40000 {
+		compatible = "qcom,smem";
+		reg = <0x8fe40000 0xc0000>,
+			<0x17811008 0x4>,
+			<0x1fd4000 0x8>;
+		reg-names = "smem", "irq-reg-base",
+			"smem_targ_info_reg";
+		qcom,mpu-enabled;
+	};
+
+	qcom,glink-smem-native-xprt-modem@8fe40000 {
+		compatible = "qcom,glink-smem-native-xprt";
+		reg = <0x8fe40000 0xc0000>,
+			<0x17811008 0x4>;
+		reg-names = "smem", "irq-reg-base";
+		qcom,irq-mask = <0x8000>;
+		interrupts = <GIC_SPI 114 IRQ_TYPE_EDGE_RISING>;
+		label = "mpss";
+	};
+
+	qcom,ipc_router {
+		compatible = "qcom,ipc_router";
+		qcom,node-id = <1>;
+	};
+
+	qcom,ipc_router_modem_xprt {
+		compatible = "qcom,ipc_router_glink_xprt";
+		qcom,ch-name = "IPCRTR";
+		qcom,xprt-remote = "mpss";
+		qcom,glink-xprt = "smem";
+		qcom,xprt-linkid = <1>;
+		qcom,xprt-version = <1>;
+		qcom,fragmented-data;
+	};
+
+	qcom,glink_pkt {
+		compatible = "qcom,glinkpkt";
+
+		qcom,glinkpkt-at-mdm0 {
+			qcom,glinkpkt-transport = "smem";
+			qcom,glinkpkt-edge = "mpss";
+			qcom,glinkpkt-ch-name = "DS";
+			qcom,glinkpkt-dev-name = "at_mdm0";
+		};
+
+		qcom,glinkpkt-loopback_cntl {
+			qcom,glinkpkt-transport = "lloop";
+			qcom,glinkpkt-edge = "local";
+			qcom,glinkpkt-ch-name = "LOCAL_LOOPBACK_CLNT";
+			qcom,glinkpkt-dev-name = "glink_pkt_loopback_ctrl";
+		};
+
+		qcom,glinkpkt-loopback_data {
+			qcom,glinkpkt-transport = "lloop";
+			qcom,glinkpkt-edge = "local";
+			qcom,glinkpkt-ch-name = "glink_pkt_lloop_CLNT";
+			qcom,glinkpkt-dev-name = "glink_pkt_loopback";
+		};
+
+		qcom,glinkpkt-data40-cntl {
+			qcom,glinkpkt-transport = "smem";
+			qcom,glinkpkt-edge = "mpss";
+			qcom,glinkpkt-ch-name = "DATA40_CNTL";
+			qcom,glinkpkt-dev-name = "smdcntl8";
+		};
+
+		qcom,glinkpkt-data1 {
+			qcom,glinkpkt-transport = "smem";
+			qcom,glinkpkt-edge = "mpss";
+			qcom,glinkpkt-ch-name = "DATA1";
+			qcom,glinkpkt-dev-name = "smd7";
+		};
+
+		qcom,glinkpkt-data4 {
+			qcom,glinkpkt-transport = "smem";
+			qcom,glinkpkt-edge = "mpss";
+			qcom,glinkpkt-ch-name = "DATA4";
+			qcom,glinkpkt-dev-name = "smd8";
+		};
+
+		qcom,glinkpkt-data11 {
+			qcom,glinkpkt-transport = "smem";
+			qcom,glinkpkt-edge = "mpss";
+			qcom,glinkpkt-ch-name = "DATA11";
+			qcom,glinkpkt-dev-name = "smd11";
+		};
+	};
+
+	pil_modem: qcom,mss@4080000 {
+		compatible = "qcom,pil-tz-generic";
+		reg = <0x4080000 0x100>;
+		interrupts = <0 250 1>;
+
+		clocks = <&clock_rpmh RPMH_CXO_CLK>;
+		clock-names = "xo";
+		qcom,proxy-clock-names = "xo";
+
+		vdd_cx-supply = <&pmxpoorwills_s5_level>;
+		qcom,proxy-reg-names = "vdd_cx";
+
+		qcom,pas-id = <0>;
+		qcom,smem-id = <421>;
+		qcom,proxy-timeout-ms = <10000>;
+		qcom,sysmon-id = <0>;
+		qcom,ssctl-instance-id = <0x12>;
+		qcom,firmware-name = "modem";
+		memory-region = <&mss_mem>;
+		status = "ok";
+
+		/* GPIO inputs from mss */
+		qcom,gpio-err-fatal = <&smp2pgpio_ssr_smp2p_1_in 0 0>;
+		qcom,gpio-err-ready = <&smp2pgpio_ssr_smp2p_1_in 1 0>;
+		qcom,gpio-proxy-unvote = <&smp2pgpio_ssr_smp2p_1_in 2 0>;
+		qcom,gpio-stop-ack = <&smp2pgpio_ssr_smp2p_1_in 3 0>;
+
+		/* GPIO output to mss */
+		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
+	};
+
+	apps_rsc: mailbox@17840000 {
+		compatible = "qcom,tcs-drv";
+		label = "apps_rsc";
+		reg = <0x17840000 0x100>, <0x17840d00 0x3000>;
+		interrupts = <0 17 0>;
+		#mbox-cells = <1>;
+		qcom,drv-id = <1>;
+		qcom,tcs-config = <ACTIVE_TCS  2>,
+				<SLEEP_TCS     2>,
+				<WAKE_TCS      2>,
+				<CONTROL_TCS   1>;
+	};
+
+	cmd_db: qcom,cmd-db@ca0000c {
+		compatible = "qcom,cmd-db";
+		reg = <0xca0000c 8>;
+	};
+
+	system_pm {
+		compatible = "qcom,system-pm";
+		mboxes = <&apps_rsc 0>;
+	};
+
+	emac_hw: qcom,emac@00020000 {
+		compatible = "qcom,emac-dwc-eqos";
+		reg = <0x20000 0x10000>,
+		      <0x36000 0x100>;
+		reg-names = "emac-base", "rgmii-base";
+		interrupts = <0 62 4>, <0 60 4>,
+			<0 45 4>, <0 49 4>,
+			<0 50 4>, <0 51 4>,
+			<0 52 4>, <0 53 4>,
+			<0 54 4>, <0 55 4>,
+			<0 56 4>, <0 57 4>;
+		interrupt-names = "sbd-intr", "lpi-intr",
+			"wol-intr", "tx-ch0-intr",
+			"tx-ch1-intr", "tx-ch2-intr",
+			"tx-ch3-intr", "tx-ch4-intr",
+			"rx-ch0-intr", "rx-ch1-intr",
+			"rx-ch2-intr", "rx-ch3-intr";
+		io-macro-info {
+			io-macro-bypass-mode = <0>;
+			io-interface = "rgmii";
+		};
+	};
+
+	qmp_aop: qcom,qmp-aop@c300000 {
+		compatible = "qcom,qmp-mbox";
+		label = "aop";
+		reg = <0xc300000 0x400>,
+			<0x17811008 0x4>;
+		reg-names = "msgram", "irq-reg-base";
+		qcom,irq-mask = <0x1>;
+		interrupts = <GIC_SPI 221 IRQ_TYPE_EDGE_RISING>;
+		priority = <0>;
+		mbox-desc-offset = <0x0>;
+		#mbox-cells = <1>;
+	};
 };
 
 #include "pmxpoorwills.dtsi"
+#include "sdxpoorwills-blsp.dtsi"
 #include "sdxpoorwills-regulator.dtsi"
+#include "sdxpoorwills-smp2p.dtsi"
 #include "sdxpoorwills-usb.dtsi"
+#include "sdxpoorwills-bus.dtsi"
+#include "sdxpoorwills-thermal.dtsi"
+#include "sdxpoorwills-audio.dtsi"
diff --git a/arch/arm/boot/dts/r8a7790.dtsi b/arch/arm/boot/dts/r8a7790.dtsi
index 351fcc2..b6c6410 100644
--- a/arch/arm/boot/dts/r8a7790.dtsi
+++ b/arch/arm/boot/dts/r8a7790.dtsi
@@ -1493,7 +1493,8 @@
 	};
 
 	msiof0: spi@e6e20000 {
-		compatible = "renesas,msiof-r8a7790";
+		compatible = "renesas,msiof-r8a7790",
+			     "renesas,rcar-gen2-msiof";
 		reg = <0 0xe6e20000 0 0x0064>;
 		interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&mstp0_clks R8A7790_CLK_MSIOF0>;
@@ -1507,7 +1508,8 @@
 	};
 
 	msiof1: spi@e6e10000 {
-		compatible = "renesas,msiof-r8a7790";
+		compatible = "renesas,msiof-r8a7790",
+			     "renesas,rcar-gen2-msiof";
 		reg = <0 0xe6e10000 0 0x0064>;
 		interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&mstp2_clks R8A7790_CLK_MSIOF1>;
@@ -1521,7 +1523,8 @@
 	};
 
 	msiof2: spi@e6e00000 {
-		compatible = "renesas,msiof-r8a7790";
+		compatible = "renesas,msiof-r8a7790",
+			     "renesas,rcar-gen2-msiof";
 		reg = <0 0xe6e00000 0 0x0064>;
 		interrupts = <GIC_SPI 158 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&mstp2_clks R8A7790_CLK_MSIOF2>;
@@ -1535,7 +1538,8 @@
 	};
 
 	msiof3: spi@e6c90000 {
-		compatible = "renesas,msiof-r8a7790";
+		compatible = "renesas,msiof-r8a7790",
+			     "renesas,rcar-gen2-msiof";
 		reg = <0 0xe6c90000 0 0x0064>;
 		interrupts = <GIC_SPI 159 IRQ_TYPE_LEVEL_HIGH>;
 		clocks = <&mstp2_clks R8A7790_CLK_MSIOF3>;
diff --git a/arch/arm/boot/dts/stih410.dtsi b/arch/arm/boot/dts/stih410.dtsi
index a3ef734..4d329b2 100644
--- a/arch/arm/boot/dts/stih410.dtsi
+++ b/arch/arm/boot/dts/stih410.dtsi
@@ -131,7 +131,7 @@
 						 <&clk_s_d2_quadfs 0>;
 
 			assigned-clock-rates = <297000000>,
-					       <108000000>,
+					       <297000000>,
 					       <0>,
 					       <400000000>,
 					       <400000000>;
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2-emmc.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2-emmc.dts
index 5ea4915..10d3074 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2-emmc.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-lime2-emmc.dts
@@ -56,7 +56,7 @@
 };
 
 &pio {
-	mmc2_pins_nrst: mmc2@0 {
+	mmc2_pins_nrst: mmc2-rst-pin {
 		allwinner,pins = "PC16";
 		allwinner,function = "gpio_out";
 		allwinner,drive = <SUN4I_PINCTRL_10_MA>;
diff --git a/arch/arm/boot/dts/tango4-vantage-1172.dts b/arch/arm/boot/dts/tango4-vantage-1172.dts
index 4cab64c..e3a51e3 100644
--- a/arch/arm/boot/dts/tango4-vantage-1172.dts
+++ b/arch/arm/boot/dts/tango4-vantage-1172.dts
@@ -21,7 +21,7 @@
 };
 
 &eth0 {
-	phy-connection-type = "rgmii";
+	phy-connection-type = "rgmii-id";
 	phy-handle = <&eth0_phy>;
 	#address-cells = <1>;
 	#size-cells = <0>;
diff --git a/arch/arm/configs/omap2plus_defconfig b/arch/arm/configs/omap2plus_defconfig
index 53e1a88..66d7196 100644
--- a/arch/arm/configs/omap2plus_defconfig
+++ b/arch/arm/configs/omap2plus_defconfig
@@ -216,6 +216,7 @@
 CONFIG_SERIAL_8250=y
 CONFIG_SERIAL_8250_CONSOLE=y
 CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=6
 CONFIG_SERIAL_8250_EXTENDED=y
 CONFIG_SERIAL_8250_MANY_PORTS=y
 CONFIG_SERIAL_8250_SHARE_IRQ=y
diff --git a/arch/arm/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index ff8a523..28a0c38 100644
--- a/arch/arm/configs/sdxpoorwills-perf_defconfig
+++ b/arch/arm/configs/sdxpoorwills-perf_defconfig
@@ -28,6 +28,7 @@
 CONFIG_PREEMPT=y
 CONFIG_AEABI=y
 CONFIG_CMA=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
 CONFIG_CPU_FREQ=y
 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
 CONFIG_CPU_IDLE=y
@@ -191,29 +192,51 @@
 # CONFIG_INPUT_KEYBOARD is not set
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
 CONFIG_INPUT_UINPUT=y
 CONFIG_INPUT_GPIO=m
 CONFIG_SERIO_LIBPS2=y
 # CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
 CONFIG_SPI_SPIDEV=m
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
 CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
 CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_POWER_RESET=y
 CONFIG_POWER_RESET_QCOM=y
 CONFIG_QCOM_DLOAD_MODE=y
 CONFIG_POWER_SUPPLY=y
+CONFIG_SMB138X_CHARGER=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_THERMAL_QPNP=y
 CONFIG_THERMAL_TSENS=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_MFD_I2C_PMIC=y
+CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QPNP=y
 CONFIG_SOUND=y
 CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_USB_AUDIO_QMI=y
 CONFIG_SND_SOC=y
 CONFIG_UHID=y
 CONFIG_HID_APPLE=y
@@ -252,9 +275,11 @@
 CONFIG_USB_CONFIGFS_MASS_STORAGE=y
 CONFIG_USB_CONFIGFS_F_FS=y
 CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_UAC1=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_USB_CONFIGFS_F_GSI=y
+CONFIG_USB_CONFIGFS_F_QDSS=y
 CONFIG_MMC=y
 CONFIG_MMC_PARANOID_SD_INIT=y
 CONFIG_MMC_BLOCK_MINORS=32
@@ -263,9 +288,13 @@
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_SDHCI_MSM=y
 CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
 CONFIG_UIO=y
 CONFIG_STAGING=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
 CONFIG_GSI=y
 CONFIG_IPA3=y
 CONFIG_RMNET_IPA3=y
@@ -274,19 +303,30 @@
 CONFIG_IPA_UT=y
 CONFIG_SPS=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
 CONFIG_USB_BAM=y
+CONFIG_MDM_GCC_SDXPOORWILLS=y
+CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_MSM_QMP=y
 CONFIG_QCOM_SCM=y
 CONFIG_MSM_BOOT_STATS=y
 CONFIG_MSM_SMEM=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
+CONFIG_IIO=y
 CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
 CONFIG_QCOM_SHOW_RESUME_IRQ=y
 CONFIG_ANDROID=y
 CONFIG_EXT3_FS=y
diff --git a/arch/arm/configs/sdxpoorwills_defconfig b/arch/arm/configs/sdxpoorwills_defconfig
index 6965607..6c3ebc7 100644
--- a/arch/arm/configs/sdxpoorwills_defconfig
+++ b/arch/arm/configs/sdxpoorwills_defconfig
@@ -30,6 +30,7 @@
 CONFIG_PREEMPT=y
 CONFIG_AEABI=y
 CONFIG_CMA=y
+CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE=y
 CONFIG_CPU_FREQ=y
 CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
 CONFIG_CPU_IDLE=y
@@ -183,34 +184,57 @@
 # CONFIG_INPUT_KEYBOARD is not set
 # CONFIG_INPUT_MOUSE is not set
 CONFIG_INPUT_MISC=y
+CONFIG_INPUT_QPNP_POWER_ON=y
 CONFIG_INPUT_UINPUT=y
 CONFIG_INPUT_GPIO=m
 CONFIG_SERIO_LIBPS2=y
 # CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_MSM=y
 CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_SERIAL_MSM_HS=y
+CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MSM_V2=y
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
 CONFIG_SPI_SPIDEV=m
 CONFIG_SLIMBUS=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
 CONFIG_PINCTRL_SDXPOORWILLS=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
 CONFIG_POWER_RESET=y
 CONFIG_POWER_RESET_QCOM=y
 CONFIG_QCOM_DLOAD_MODE=y
 CONFIG_POWER_SUPPLY=y
+CONFIG_SMB138X_CHARGER=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_THERMAL_QPNP=y
 CONFIG_THERMAL_TSENS=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
+CONFIG_MFD_I2C_PMIC=y
+CONFIG_MFD_SPMI_PMIC=y
+CONFIG_MFD_SYSCON=y
 CONFIG_MSM_CDC_PINCTRL=y
 CONFIG_MSM_CDC_SUPPLY=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_QPNP=y
 CONFIG_REGULATOR_STUB=y
 CONFIG_FB=y
 CONFIG_SOUND=y
 CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_USB_AUDIO=y
+CONFIG_SND_USB_AUDIO_QMI=y
 CONFIG_SND_SOC=y
 CONFIG_UHID=y
 CONFIG_HID_APPLE=y
@@ -249,9 +273,11 @@
 CONFIG_USB_CONFIGFS_MASS_STORAGE=y
 CONFIG_USB_CONFIGFS_F_FS=y
 CONFIG_USB_CONFIGFS_UEVENT=y
+CONFIG_USB_CONFIGFS_F_UAC1=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
 CONFIG_USB_CONFIGFS_F_GSI=y
+CONFIG_USB_CONFIGFS_F_QDSS=y
 CONFIG_MMC=y
 CONFIG_MMC_PARANOID_SD_INIT=y
 CONFIG_MMC_BLOCK_MINORS=32
@@ -259,9 +285,13 @@
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
 CONFIG_UIO=y
 CONFIG_STAGING=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
 CONFIG_GSI=y
 CONFIG_IPA3=y
 CONFIG_RMNET_IPA3=y
@@ -270,18 +300,30 @@
 CONFIG_IPA_UT=y
 CONFIG_SPS=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
-CONFIG_HWSPINLOCK_QCOM=y
-CONFIG_QCOM_SMEM=y
-CONFIG_QCOM_SMD=y
+CONFIG_QPNP_REVID=y
+CONFIG_MDM_GCC_SDXPOORWILLS=y
+CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_MSM_QMP=y
 CONFIG_QCOM_SCM=y
 CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
 CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
 CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_IIO=y
 CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
 CONFIG_QCOM_SHOW_RESUME_IRQ=y
 CONFIG_ANDROID=y
 CONFIG_STM=y
diff --git a/arch/arm/include/asm/Kbuild b/arch/arm/include/asm/Kbuild
index 55e0e3e..bd12b98 100644
--- a/arch/arm/include/asm/Kbuild
+++ b/arch/arm/include/asm/Kbuild
@@ -37,4 +37,3 @@
 generic-y += termios.h
 generic-y += timex.h
 generic-y += trace_clock.h
-generic-y += unaligned.h
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h
index bfe2a2f..22b7311 100644
--- a/arch/arm/include/asm/ftrace.h
+++ b/arch/arm/include/asm/ftrace.h
@@ -54,6 +54,24 @@
 
 #define ftrace_return_address(n) return_address(n)
 
+#define ARCH_HAS_SYSCALL_MATCH_SYM_NAME
+
+static inline bool arch_syscall_match_sym_name(const char *sym,
+					       const char *name)
+{
+	if (!strcmp(sym, "sys_mmap2"))
+		sym = "sys_mmap_pgoff";
+	else if (!strcmp(sym, "sys_statfs64_wrapper"))
+		sym = "sys_statfs64";
+	else if (!strcmp(sym, "sys_fstatfs64_wrapper"))
+		sym = "sys_fstatfs64";
+	else if (!strcmp(sym, "sys_arm_fadvise64_64"))
+		sym = "sys_fadvise64_64";
+
+	/* Ignore case since sym may start with "SyS" instead of "sys" */
+	return !strcasecmp(sym, name);
+}
+
 #endif /* ifndef __ASSEMBLY__ */
 
 #endif /* _ASM_ARM_FTRACE */
diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h
index 9edea10..41e9107 100644
--- a/arch/arm/include/asm/topology.h
+++ b/arch/arm/include/asm/topology.h
@@ -32,6 +32,9 @@
 #define arch_scale_cpu_capacity scale_cpu_capacity
 extern unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu);
 
+#define arch_update_cpu_capacity update_cpu_power_capacity
+extern void update_cpu_power_capacity(int cpu);
+
 #else
 
 static inline void init_cpu_topology(void) { }
diff --git a/arch/arm/include/asm/unaligned.h b/arch/arm/include/asm/unaligned.h
new file mode 100644
index 0000000..ab905ff
--- /dev/null
+++ b/arch/arm/include/asm/unaligned.h
@@ -0,0 +1,27 @@
+#ifndef __ASM_ARM_UNALIGNED_H
+#define __ASM_ARM_UNALIGNED_H
+
+/*
+ * We generally want to set CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS on ARMv6+,
+ * but we don't want to use linux/unaligned/access_ok.h since that can lead
+ * to traps on unaligned stm/ldm or strd/ldrd.
+ */
+#include <asm/byteorder.h>
+
+#if defined(__LITTLE_ENDIAN)
+# include <linux/unaligned/le_struct.h>
+# include <linux/unaligned/be_byteshift.h>
+# include <linux/unaligned/generic.h>
+# define get_unaligned	__get_unaligned_le
+# define put_unaligned	__put_unaligned_le
+#elif defined(__BIG_ENDIAN)
+# include <linux/unaligned/be_struct.h>
+# include <linux/unaligned/le_byteshift.h>
+# include <linux/unaligned/generic.h>
+# define get_unaligned	__get_unaligned_be
+# define put_unaligned	__put_unaligned_be
+#else
+# error need to define endianess
+#endif
+
+#endif /* __ASM_ARM_UNALIGNED_H */
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index bbf60e3..ab509d6 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -466,17 +466,6 @@
 }
 
 static void (*__smp_cross_call)(const struct cpumask *, unsigned int);
-DEFINE_PER_CPU(bool, pending_ipi);
-static void smp_cross_call_common(const struct cpumask *cpumask,
-						unsigned int func)
-{
-	unsigned int cpu;
-
-	for_each_cpu(cpu, cpumask)
-		per_cpu(pending_ipi, cpu) = true;
-
-	__smp_cross_call(cpumask, func);
-}
 
 void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
 {
@@ -501,6 +490,18 @@
 	__smp_cross_call(target, ipinr);
 }
 
+DEFINE_PER_CPU(bool, pending_ipi);
+static void smp_cross_call_common(const struct cpumask *cpumask,
+						unsigned int func)
+{
+	unsigned int cpu;
+
+	for_each_cpu(cpu, cpumask)
+		per_cpu(pending_ipi, cpu) = true;
+
+	smp_cross_call(cpumask, func);
+}
+
 void show_ipi_list(struct seq_file *p, int prec)
 {
 	unsigned int cpu, i;
@@ -539,7 +540,7 @@
 
 void arch_send_call_function_single_ipi(int cpu)
 {
-	smp_cross_call_common(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+	smp_cross_call_common(cpumask_of(cpu), IPI_CALL_FUNC);
 }
 
 #ifdef CONFIG_IRQ_WORK
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 2b6c530..28dcd44 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -42,6 +42,16 @@
  */
 static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
 
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+	return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+	per_cpu(cpu_scale, cpu) = power;
+}
+
 unsigned long scale_cpu_capacity(struct sched_domain *sd, int cpu)
 {
 #ifdef CONFIG_CPU_FREQ
@@ -397,6 +407,23 @@
 	return &cpu_topology[cpu].thread_sibling;
 }
 
+static void update_cpu_power(unsigned int cpu)
+{
+	if (!cpu_capacity(cpu))
+		return;
+
+	set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+
+	pr_info("CPU%u: update cpu_power %lu\n",
+		cpu, arch_scale_freq_power(NULL, cpu));
+}
+
+void update_cpu_power_capacity(int cpu)
+{
+	update_cpu_power(cpu);
+	update_cpu_capacity(cpu);
+}
+
 static void update_siblings_masks(unsigned int cpuid)
 {
 	struct cputopo_arm *cpu_topo, *cpuid_topo = &cpu_topology[cpuid];
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 9688ec0..1b30489 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -152,30 +152,26 @@
 	set_fs(fs);
 }
 
-static void dump_instr(const char *lvl, struct pt_regs *regs)
+static void __dump_instr(const char *lvl, struct pt_regs *regs)
 {
 	unsigned long addr = instruction_pointer(regs);
 	const int thumb = thumb_mode(regs);
 	const int width = thumb ? 4 : 8;
-	mm_segment_t fs;
 	char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
 	int i;
 
 	/*
-	 * We need to switch to kernel mode so that we can use __get_user
-	 * to safely read from kernel space.  Note that we now dump the
-	 * code first, just in case the backtrace kills us.
+	 * Note that we now dump the code first, just in case the backtrace
+	 * kills us.
 	 */
-	fs = get_fs();
-	set_fs(KERNEL_DS);
 
 	for (i = -4; i < 1 + !!thumb; i++) {
 		unsigned int val, bad;
 
 		if (thumb)
-			bad = __get_user(val, &((u16 *)addr)[i]);
+			bad = get_user(val, &((u16 *)addr)[i]);
 		else
-			bad = __get_user(val, &((u32 *)addr)[i]);
+			bad = get_user(val, &((u32 *)addr)[i]);
 
 		if (!bad)
 			p += sprintf(p, i == 0 ? "(%0*x) " : "%0*x ",
@@ -186,8 +182,20 @@
 		}
 	}
 	printk("%sCode: %s\n", lvl, str);
+}
 
-	set_fs(fs);
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+	mm_segment_t fs;
+
+	if (!user_mode(regs)) {
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		__dump_instr(lvl, regs);
+		set_fs(fs);
+	} else {
+		__dump_instr(lvl, regs);
+	}
 }
 
 #ifdef CONFIG_ARM_UNWIND
diff --git a/arch/arm/kvm/emulate.c b/arch/arm/kvm/emulate.c
index 0064b86..30a13647 100644
--- a/arch/arm/kvm/emulate.c
+++ b/arch/arm/kvm/emulate.c
@@ -227,7 +227,7 @@
 	u32 return_offset = (is_thumb) ? 2 : 4;
 
 	kvm_update_psr(vcpu, UND_MODE);
-	*vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) - return_offset;
+	*vcpu_reg(vcpu, 14) = *vcpu_pc(vcpu) + return_offset;
 
 	/* Branch to exception vector */
 	*vcpu_pc(vcpu) = exc_vector_base(vcpu) + vect_offset;
@@ -239,10 +239,8 @@
  */
 static void inject_abt(struct kvm_vcpu *vcpu, bool is_pabt, unsigned long addr)
 {
-	unsigned long cpsr = *vcpu_cpsr(vcpu);
-	bool is_thumb = (cpsr & PSR_T_BIT);
 	u32 vect_offset;
-	u32 return_offset = (is_thumb) ? 4 : 0;
+	u32 return_offset = (is_pabt) ? 4 : 8;
 	bool is_lpae;
 
 	kvm_update_psr(vcpu, ABT_MODE);
diff --git a/arch/arm/kvm/hyp/Makefile b/arch/arm/kvm/hyp/Makefile
index 8679405..92eab1d 100644
--- a/arch/arm/kvm/hyp/Makefile
+++ b/arch/arm/kvm/hyp/Makefile
@@ -2,7 +2,7 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
 
 KVM=../../../../virt/kvm
 
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 332ce3b..2206e0e 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -829,22 +829,22 @@
  * Walks the level-1 page table pointed to by kvm->arch.pgd and frees all
  * underlying level-2 and level-3 tables before freeing the actual level-1 table
  * and setting the struct pointer to NULL.
- *
- * Note we don't need locking here as this is only called when the VM is
- * destroyed, which can only be done once.
  */
 void kvm_free_stage2_pgd(struct kvm *kvm)
 {
-	if (kvm->arch.pgd == NULL)
-		return;
+	void *pgd = NULL;
 
 	spin_lock(&kvm->mmu_lock);
-	unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
+	if (kvm->arch.pgd) {
+		unmap_stage2_range(kvm, 0, KVM_PHYS_SIZE);
+		pgd = READ_ONCE(kvm->arch.pgd);
+		kvm->arch.pgd = NULL;
+	}
 	spin_unlock(&kvm->mmu_lock);
 
 	/* Free the HW pgd, one page at a time */
-	free_pages_exact(kvm->arch.pgd, S2_PGD_SIZE);
-	kvm->arch.pgd = NULL;
+	if (pgd)
+		free_pages_exact(pgd, S2_PGD_SIZE);
 }
 
 static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
@@ -1664,12 +1664,16 @@
 
 int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end)
 {
+	if (!kvm->arch.pgd)
+		return 0;
 	trace_kvm_age_hva(start, end);
 	return handle_hva_to_gpa(kvm, start, end, kvm_age_hva_handler, NULL);
 }
 
 int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
 {
+	if (!kvm->arch.pgd)
+		return 0;
 	trace_kvm_test_age_hva(hva);
 	return handle_hva_to_gpa(kvm, hva, hva, kvm_test_age_hva_handler, NULL);
 }
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index 31dde8b..8ba0e2e 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -335,7 +335,7 @@
 		at91_ramc_write(1, AT91_SDRAMC_LPR, saved_lpr1);
 }
 
-static const struct of_device_id const ramc_ids[] __initconst = {
+static const struct of_device_id ramc_ids[] __initconst = {
 	{ .compatible = "atmel,at91rm9200-sdramc", .data = at91rm9200_standby },
 	{ .compatible = "atmel,at91sam9260-sdramc", .data = at91sam9_sdram_standby },
 	{ .compatible = "atmel,at91sam9g45-ddramc", .data = at91_ddr_standby },
diff --git a/arch/arm/mach-bcm/bcm_kona_smc.c b/arch/arm/mach-bcm/bcm_kona_smc.c
index cf3f865..a55a7ec 100644
--- a/arch/arm/mach-bcm/bcm_kona_smc.c
+++ b/arch/arm/mach-bcm/bcm_kona_smc.c
@@ -33,7 +33,7 @@
 	unsigned result;
 };
 
-static const struct of_device_id const bcm_kona_smc_ids[] __initconst = {
+static const struct of_device_id bcm_kona_smc_ids[] __initconst = {
 	{.compatible = "brcm,kona-smc"},
 	{.compatible = "bcm,kona-smc"}, /* deprecated name */
 	{},
diff --git a/arch/arm/mach-cns3xxx/core.c b/arch/arm/mach-cns3xxx/core.c
index 03da381..7d5a44a 100644
--- a/arch/arm/mach-cns3xxx/core.c
+++ b/arch/arm/mach-cns3xxx/core.c
@@ -346,7 +346,7 @@
 	.power_off	= csn3xxx_usb_power_off,
 };
 
-static const struct of_dev_auxdata const cns3xxx_auxdata[] __initconst = {
+static const struct of_dev_auxdata cns3xxx_auxdata[] __initconst = {
 	{ "intel,usb-ehci", CNS3XXX_USB_BASE, "ehci-platform", &cns3xxx_usb_ehci_pdata },
 	{ "intel,usb-ohci", CNS3XXX_USB_OHCI_BASE, "ohci-platform", &cns3xxx_usb_ohci_pdata },
 	{ "cavium,cns3420-ahci", CNS3XXX_SATA2_BASE, "ahci", NULL },
diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c
index 5b2f513..f1ca947 100644
--- a/arch/arm/mach-omap2/prm_common.c
+++ b/arch/arm/mach-omap2/prm_common.c
@@ -713,7 +713,7 @@
 };
 #endif
 
-static const struct of_device_id const omap_prcm_dt_match_table[] __initconst = {
+static const struct of_device_id omap_prcm_dt_match_table[] __initconst = {
 #ifdef CONFIG_SOC_AM33XX
 	{ .compatible = "ti,am3-prcm", .data = &am3_prm_data },
 #endif
diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c
index 2028167f..d76b1e5 100644
--- a/arch/arm/mach-omap2/vc.c
+++ b/arch/arm/mach-omap2/vc.c
@@ -559,7 +559,7 @@
 	u8 hsscll_12;
 };
 
-static const struct i2c_init_data const omap4_i2c_timing_data[] __initconst = {
+static const struct i2c_init_data omap4_i2c_timing_data[] __initconst = {
 	{
 		.load = 50,
 		.loadbits = 0x3,
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig
index f4d7965..4761bc5 100644
--- a/arch/arm/mach-qcom/Kconfig
+++ b/arch/arm/mach-qcom/Kconfig
@@ -51,5 +51,28 @@
 	select COMMON_CLK
 	select COMMON_CLK_QCOM
 	select QCOM_GDSC
+
+config ARCH_MSM8953
+	bool "Enable support for MSM8953"
+	select CPU_V7
+	select HAVE_ARM_ARCH_TIMER
+	select PINCTRL
+	select QCOM_SCM if SMP
+	select PM_DEVFREQ
+	select COMMON_CLK
+	select COMMON_CLK_QCOM
+	select QCOM_GDSC
+
+config ARCH_SDM450
+	bool "Enable support for SDM450"
+	select CPU_V7
+	select HAVE_ARM_ARCH_TIMER
+	select PINCTRL
+	select QCOM_SCM if SMP
+	select PM_DEVFREQ
+	select COMMON_CLK
+	select COMMON_CLK_QCOM
+	select QCOM_GDSC
+
 endmenu
 endif
diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile
index d893b27..828e9c9 100644
--- a/arch/arm/mach-qcom/Makefile
+++ b/arch/arm/mach-qcom/Makefile
@@ -1,3 +1,5 @@
 obj-$(CONFIG_USE_OF) += board-dt.o
 obj-$(CONFIG_SMP)	+= platsmp.o
 obj-$(CONFIG_ARCH_SDXPOORWILLS) += board-poorwills.o
+obj-$(CONFIG_ARCH_MSM8953) += board-msm8953.o
+obj-$(CONFIG_ARCH_SDM450) += board-sdm450.o
diff --git a/arch/arm/mach-qcom/board-msm8953.c b/arch/arm/mach-qcom/board-msm8953.c
new file mode 100644
index 0000000..cae3bf7
--- /dev/null
+++ b/arch/arm/mach-qcom/board-msm8953.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include "board-dt.h"
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+static const char *msm8953_dt_match[] __initconst = {
+	"qcom,msm8953",
+	NULL
+};
+
+static void __init msm8953_init(void)
+{
+	board_dt_populate(NULL);
+}
+
+DT_MACHINE_START(MSM8953_DT,
+	"Qualcomm Technologies, Inc. MSM8953 (Flattened Device Tree)")
+	.init_machine		= msm8953_init,
+	.dt_compat		= msm8953_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-qcom/board-sdm450.c b/arch/arm/mach-qcom/board-sdm450.c
new file mode 100644
index 0000000..5f68ede
--- /dev/null
+++ b/arch/arm/mach-qcom/board-sdm450.c
@@ -0,0 +1,32 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include "board-dt.h"
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+static const char *sdm450_dt_match[] __initconst = {
+	"qcom,sdm450",
+	NULL
+};
+
+static void __init sdm450_init(void)
+{
+	board_dt_populate(NULL);
+}
+
+DT_MACHINE_START(SDM450_DT,
+	"Qualcomm Technologies, Inc. SDM450 (Flattened Device Tree)")
+	.init_machine		= sdm450_init,
+	.dt_compat		= sdm450_dt_match,
+MACHINE_END
diff --git a/arch/arm/mach-spear/time.c b/arch/arm/mach-spear/time.c
index 9ccffc1..aaaa678 100644
--- a/arch/arm/mach-spear/time.c
+++ b/arch/arm/mach-spear/time.c
@@ -204,7 +204,7 @@
 	setup_irq(irq, &spear_timer_irq);
 }
 
-static const struct of_device_id const timer_of_match[] __initconst = {
+static const struct of_device_id timer_of_match[] __initconst = {
 	{ .compatible = "st,spear-timer", },
 	{ },
 };
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index aec74bf..a9ef54d 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -314,8 +314,11 @@
 	 * signal first. We do not need to release the mmap_sem because
 	 * it would already be released in __lock_page_or_retry in
 	 * mm/filemap.c. */
-	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
+		if (!user_mode(regs))
+			goto no_context;
 		return 0;
+	}
 
 	/*
 	 * Major/minor page fault accounting is only done on the
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c
index 5d73327..0bb7673 100644
--- a/arch/arm/mm/highmem.c
+++ b/arch/arm/mm/highmem.c
@@ -150,7 +150,7 @@
 }
 
 #ifdef CONFIG_ARCH_WANT_KMAP_ATOMIC_FLUSH
-static void kmap_remove_unused_cpu(int cpu)
+int kmap_remove_unused_cpu(unsigned int cpu)
 {
 	int start_idx, idx, type;
 
@@ -167,6 +167,7 @@
 			set_top_pte(vaddr, __pte(0));
 	}
 	pagefault_enable();
+	return 0;
 }
 
 static void kmap_remove_unused(void *unused)
@@ -179,27 +180,4 @@
 	on_each_cpu(kmap_remove_unused, NULL, 1);
 }
 
-static int hotplug_kmap_atomic_callback(struct notifier_block *nfb,
-					unsigned long action, void *hcpu)
-{
-	switch (action & (~CPU_TASKS_FROZEN)) {
-	case CPU_DYING:
-		kmap_remove_unused_cpu((int)hcpu);
-		break;
-	default:
-		break;
-	}
-
-	return NOTIFY_OK;
-}
-
-static struct notifier_block hotplug_kmap_atomic_notifier = {
-	.notifier_call = hotplug_kmap_atomic_callback,
-};
-
-static int __init init_kmap_atomic(void)
-{
-	return register_hotcpu_notifier(&hotplug_kmap_atomic_notifier);
-}
-early_initcall(init_kmap_atomic);
 #endif
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
index d062f08..4b24964 100644
--- a/arch/arm/xen/mm.c
+++ b/arch/arm/xen/mm.c
@@ -199,6 +199,7 @@
 	.unmap_page = xen_swiotlb_unmap_page,
 	.dma_supported = xen_swiotlb_dma_supported,
 	.set_dma_mask = xen_swiotlb_set_dma_mask,
+	.mmap = xen_swiotlb_dma_mmap,
 };
 
 int __init xen_mm_init(void)
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index f96fba6..8edfbf2 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -139,6 +139,24 @@
 	  This enables support for the SDM670 chipset. If you do not
 	  wish to build a kernel that runs on this chipset, say 'N' here.
 
+config ARCH_MSM8953
+	bool "Enable Support for Qualcomm Technologies Inc. MSM8953"
+	depends on ARCH_QCOM
+	select COMMON_CLK_QCOM
+	select QCOM_GDSC
+	help
+	  This enables support for the MSM8953 chipset. If you do not
+	  wish to build a kernel that runs on this chipset, say 'N' here.
+
+config ARCH_SDM450
+	bool "Enable Support for Qualcomm Technologies Inc. SDM450"
+	depends on ARCH_QCOM
+	select COMMON_CLK_QCOM
+	select QCOM_GDSC
+	help
+	  This enables support for the sdm450 chipset. If you do not
+	  wish to build a kernel that runs on this chipset, say 'N' here.
+
 config ARCH_ROCKCHIP
 	bool "Rockchip Platforms"
 	select ARCH_HAS_RESET_CONTROLLER
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index 49a5d8c..68e6f88 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -170,6 +170,7 @@
 				interrupt-controller;
 				reg = <0x1d00000 0x10000>, /* GICD */
 				      <0x1d40000 0x40000>; /* GICR */
+				interrupts = <GIC_PPI 9 IRQ_TYPE_LEVEL_HIGH>;
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 041b304..3df7439 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -7,46 +7,80 @@
 		sdm845-cdp-overlay.dtbo \
 		sdm845-mtp-overlay.dtbo \
 		sdm845-qrd-overlay.dtbo \
-		sdm845-qvr-overlay.dtbo \
-		sdm845-v2-cdp-overlay.dtbo \
-		sdm845-v2-mtp-overlay.dtbo \
-		sdm845-v2-qrd-overlay.dtbo \
 		sdm845-4k-panel-mtp-overlay.dtbo \
 		sdm845-4k-panel-cdp-overlay.dtbo \
 		sdm845-4k-panel-qrd-overlay.dtbo \
+		sdm845-v2-qvr-overlay.dtbo \
+		sdm845-v2-cdp-overlay.dtbo \
+		sdm845-v2-mtp-overlay.dtbo \
+		sdm845-v2-qrd-overlay.dtbo \
+		sdm845-v2-4k-panel-mtp-overlay.dtbo \
+		sdm845-v2-4k-panel-cdp-overlay.dtbo \
+		sdm845-v2-4k-panel-qrd-overlay.dtbo \
+		sdm845-v2.1-cdp-overlay.dtbo \
+		sdm845-v2.1-mtp-overlay.dtbo \
+		sdm845-v2.1-qrd-overlay.dtbo \
+		sdm845-v2.1-4k-panel-mtp-overlay.dtbo \
+		sdm845-v2.1-4k-panel-cdp-overlay.dtbo \
+		sdm845-v2.1-4k-panel-qrd-overlay.dtbo \
 		sda845-cdp-overlay.dtbo \
 		sda845-mtp-overlay.dtbo \
 		sda845-qrd-overlay.dtbo \
-		sda845-v2-cdp-overlay.dtbo \
-		sda845-v2-mtp-overlay.dtbo \
-		sda845-v2-qrd-overlay.dtbo \
 		sda845-4k-panel-mtp-overlay.dtbo \
 		sda845-4k-panel-cdp-overlay.dtbo \
 		sda845-4k-panel-qrd-overlay.dtbo \
-		sdm845-interposer-sdm670-cdp-overlay.dtbo \
-		sdm845-interposer-sdm670-mtp-overlay.dtbo
+		sda845-v2-cdp-overlay.dtbo \
+		sda845-v2-mtp-overlay.dtbo \
+		sda845-v2-qrd-overlay.dtbo \
+		sda845-v2-hdk-overlay.dtbo \
+		sda845-v2-4k-panel-mtp-overlay.dtbo \
+		sda845-v2-4k-panel-cdp-overlay.dtbo \
+		sda845-v2-4k-panel-qrd-overlay.dtbo \
+		sda845-v2.1-cdp-overlay.dtbo \
+		sda845-v2.1-mtp-overlay.dtbo \
+		sda845-v2.1-qrd-overlay.dtbo \
+		sda845-v2.1-4k-panel-cdp-overlay.dtbo \
+		sda845-v2.1-4k-panel-mtp-overlay.dtbo \
+		sda845-v2.1-4k-panel-qrd-overlay.dtbo
 
 sdm845-cdp-overlay.dtbo-base := sdm845.dtb
 sdm845-mtp-overlay.dtbo-base := sdm845.dtb
 sdm845-qrd-overlay.dtbo-base := sdm845.dtb
-sdm845-qvr-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-cdp-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-mtp-overlay.dtbo-base := sdm845-v2.dtb
-sdm845-v2-qrd-overlay.dtbo-base := sdm845-v2.dtb
 sdm845-4k-panel-mtp-overlay.dtbo-base := sdm845.dtb
 sdm845-4k-panel-cdp-overlay.dtbo-base := sdm845.dtb
 sdm845-4k-panel-qrd-overlay.dtbo-base := sdm845.dtb
+sdm845-v2-qvr-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-cdp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-mtp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-qrd-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-mtp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-cdp-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2-4k-panel-qrd-overlay.dtbo-base := sdm845-v2.dtb
+sdm845-v2.1-cdp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-mtp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-qrd-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-mtp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-cdp-overlay.dtbo-base := sdm845-v2.1.dtb
+sdm845-v2.1-4k-panel-qrd-overlay.dtbo-base := sdm845-v2.1.dtb
 sda845-cdp-overlay.dtbo-base := sda845.dtb
 sda845-mtp-overlay.dtbo-base := sda845.dtb
 sda845-qrd-overlay.dtbo-base := sda845.dtb
-sda845-v2-cdp-overlay.dtbo-base := sda845-v2.dtb
-sda845-v2-mtp-overlay.dtbo-base := sda845-v2.dtb
-sda845-v2-qrd-overlay.dtbo-base := sda845-v2.dtb
 sda845-4k-panel-mtp-overlay.dtbo-base := sda845.dtb
 sda845-4k-panel-cdp-overlay.dtbo-base := sda845.dtb
 sda845-4k-panel-qrd-overlay.dtbo-base := sda845.dtb
-sdm845-interposer-sdm670-cdp-overlay.dtbo-base := sdm845-interposer-sdm670.dtb
-sdm845-interposer-sdm670-mtp-overlay.dtbo-base := sdm845-interposer-sdm670.dtb
+sda845-v2-cdp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-mtp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-qrd-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-hdk-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-mtp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-cdp-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2-4k-panel-qrd-overlay.dtbo-base := sda845-v2.dtb
+sda845-v2.1-cdp-overlay.dtbo-base := sda845-v2.1.dtb
+sda845-v2.1-mtp-overlay.dtbo-base := sda845-v2.1.dtb
+sda845-v2.1-qrd-overlay.dtbo-base := sda845-v2.1.dtb
+sda845-v2.1-4k-panel-cdp-overlay.dtbo-base := sda845-v2.1.dtb
+sda845-v2.1-4k-panel-mtp-overlay.dtbo-base := sda845-v2.1.dtb
+sda845-v2.1-4k-panel-qrd-overlay.dtbo-base := sda845-v2.1.dtb
 else
 dtb-$(CONFIG_ARCH_SDM845) += sdm845-sim.dtb \
 	sdm845-rumi.dtb \
@@ -57,7 +91,7 @@
 	sdm845-v2-cdp.dtb \
 	sdm845-qrd.dtb \
 	sdm845-v2-qrd.dtb \
-	sdm845-qvr.dtb \
+	sdm845-v2-qvr.dtb \
 	sdm845-4k-panel-mtp.dtb \
 	sdm845-4k-panel-cdp.dtb \
 	sdm845-4k-panel-qrd.dtb \
@@ -69,15 +103,120 @@
 	dtbo-$(CONFIG_ARCH_SDM670) += \
 		sdm670-cdp-overlay.dtbo \
 		sdm670-mtp-overlay.dtbo \
-		sdm670-rumi-overlay.dtbo
+		sdm670-rumi-overlay.dtbo \
+		sdm670-qrd-overlay.dtbo \
+		sdm670-qrd-sku2-overlay.dtbo \
+		sdm670-pm660a-cdp-overlay.dtbo \
+		sdm670-pm660a-mtp-overlay.dtbo \
+		sdm670-external-codec-cdp-overlay.dtbo \
+		sdm670-external-codec-mtp-overlay.dtbo \
+		sdm670-external-codec-pm660a-cdp-overlay.dtbo \
+		sdm670-external-codec-pm660a-mtp-overlay.dtbo \
+		sdm670-usbc-cdp-overlay.dtbo \
+		sdm670-usbc-mtp-overlay.dtbo \
+		sdm670-usbc-pm660a-cdp-overlay.dtbo \
+		sdm670-usbc-pm660a-mtp-overlay.dtbo \
+		sdm670-usbc-external-codec-cdp-overlay.dtbo \
+		sdm670-usbc-external-codec-mtp-overlay.dtbo \
+		sdm670-usbc-external-codec-pm660a-cdp-overlay.dtbo \
+		sdm670-usbc-external-codec-pm660a-mtp-overlay.dtbo \
+		sda670-cdp-overlay.dtbo \
+		sda670-mtp-overlay.dtbo \
+		sda670-pm660a-cdp-overlay.dtbo \
+		sda670-pm660a-mtp-overlay.dtbo \
+		qcs605-cdp-overlay.dtbo \
+		qcs605-mtp-overlay.dtbo \
+		qcs605-external-codec-mtp-overlay.dtbo
 
 sdm670-cdp-overlay.dtbo-base := sdm670.dtb
 sdm670-mtp-overlay.dtbo-base := sdm670.dtb
 sdm670-rumi-overlay.dtbo-base := sdm670.dtb
+sdm670-qrd-overlay.dtbo-base := sdm670.dtb
+sdm670-qrd-sku2-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-external-codec-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-external-codec-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-external-codec-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-external-codec-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-external-codec-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-external-codec-mtp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-external-codec-pm660a-cdp-overlay.dtbo-base := sdm670.dtb
+sdm670-usbc-external-codec-pm660a-mtp-overlay.dtbo-base := sdm670.dtb
+sda670-cdp-overlay.dtbo-base := sda670.dtb
+sda670-mtp-overlay.dtbo-base := sda670.dtb
+sda670-pm660a-cdp-overlay.dtbo-base := sda670.dtb
+sda670-pm660a-mtp-overlay.dtbo-base := sda670.dtb
+qcs605-cdp-overlay.dtbo-base := qcs605.dtb
+qcs605-mtp-overlay.dtbo-base := qcs605.dtb
+qcs605-external-codec-mtp-overlay.dtbo-base := qcs605.dtb
+
 else
 dtb-$(CONFIG_ARCH_SDM670) += sdm670-rumi.dtb \
 	sdm670-mtp.dtb \
-	sdm670-cdp.dtb
+	sdm670-cdp.dtb \
+	sdm670-qrd.dtb \
+	sdm670-qrd-sku2.dtb \
+	sdm670-pm660a-mtp.dtb \
+	sdm670-pm660a-cdp.dtb \
+	sdm670-external-codec-cdp.dtb \
+	sdm670-external-codec-mtp.dtb \
+	sdm670-external-codec-pm660a-cdp.dtb \
+	sdm670-external-codec-pm660a-mtp.dtb \
+	sdm670-usbc-cdp.dtb \
+	sdm670-usbc-external-codec-cdp.dtb \
+	sdm670-usbc-external-codec-mtp.dtb \
+	sdm670-usbc-external-codec-pm660a-cdp.dtb \
+	sdm670-usbc-external-codec-pm660a-mtp.dtb \
+	sdm670-usbc-mtp.dtb \
+	sdm670-usbc-pm660a-cdp.dtb \
+	sdm670-usbc-pm660a-mtp.dtb \
+	sda670-mtp.dtb \
+	sda670-cdp.dtb \
+	sda670-pm660a-mtp.dtb \
+	sda670-pm660a-cdp.dtb \
+	qcs605-360camera.dtb \
+	qcs605-mtp.dtb \
+	qcs605-cdp.dtb \
+	qcs605-external-codec-mtp.dtb
+endif
+
+ifeq ($(CONFIG_BUILD_ARM64_DT_OVERLAY),y)
+else
+dtb-$(CONFIG_ARCH_MSM8953) += msm8953-cdp.dtb \
+	msm8953-mtp.dtb \
+	msm8953-ext-codec-mtp.dtb \
+	msm8953-qrd-sku3.dtb \
+	msm8953-rcm.dtb \
+	apq8053-rcm.dtb \
+	msm8953-ext-codec-rcm.dtb \
+	apq8053-cdp.dtb \
+	apq8053-ipc.dtb \
+	msm8953-ipc.dtb \
+	apq8053-mtp.dtb \
+	apq8053-ext-audio-mtp.dtb \
+	apq8053-ext-codec-rcm.dtb \
+	msm8953-cdp-1200p.dtb \
+	msm8953-iot-mtp.dtb \
+	apq8053-iot-mtp.dtb \
+	msm8953-pmi8940-cdp.dtb \
+	msm8953-pmi8940-mtp.dtb \
+	msm8953-pmi8937-cdp.dtb \
+	msm8953-pmi8937-mtp.dtb \
+	msm8953-pmi8940-ext-codec-mtp.dtb \
+	msm8953-pmi8937-ext-codec-mtp.dtb
+
+dtb-$(CONFIG_ARCH_SDM450) += sdm450-rcm.dtb \
+	sdm450-cdp.dtb \
+	sdm450-mtp.dtb \
+	sdm450-qrd.dtb \
+	sdm450-pmi8940-mtp.dtb \
+	sdm450-pmi8937-mtp.dtb \
+	sdm450-iot-mtp.dtb
 endif
 
 always		:= $(dtb-y)
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053-cdp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053-cdp.dts
index c06b806..5e89e4f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-cdp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "apq8053.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 CDP";
+	compatible = "qcom,apq8053-cdp", "qcom,apq8053", "qcom,cdp";
+	qcom,board-id= <1 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/apq8053-ext-audio-mtp.dts b/arch/arm64/boot/dts/qcom/apq8053-ext-audio-mtp.dts
new file mode 100644
index 0000000..2c7b228
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-ext-audio-mtp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053.dtsi"
+#include "msm8953-mtp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 Ext Codec MTP";
+	compatible = "qcom,apq8053-mtp", "qcom,apq8053", "qcom,mtp";
+	qcom,board-id= <8 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/apq8053-ext-codec-rcm.dts b/arch/arm64/boot/dts/qcom/apq8053-ext-codec-rcm.dts
new file mode 100644
index 0000000..d026734
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/apq8053-ext-codec-rcm.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "apq8053.dtsi"
+#include "msm8953-cdp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 Ext Codec RCM";
+	compatible = "qcom,apq8053-cdp", "qcom,apq8053", "qcom,cdp";
+	qcom,board-id= <21 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
index c06b806..177e105 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-iot-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "apq8053.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 IOT MTP";
+	compatible = "qcom,apq8053-mtp", "qcom,apq8053", "qcom,mtp";
+	qcom,board-id= <8 2>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053-ipc.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053-ipc.dts
index c06b806..3381b2a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-ipc.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "apq8053.dtsi"
+#include "msm8953-ipc.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 IPC";
+	compatible = "qcom,apq8053-ipc", "qcom,apq8053", "qcom,ipc";
+	qcom,board-id= <12 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053-mtp.dts
index c06b806..be544af 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "apq8053.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 MTP";
+	compatible = "qcom,apq8053-mtp", "qcom,apq8053", "qcom,mtp";
+	qcom,board-id= <8 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053-rcm.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053-rcm.dts
index c06b806..cc5bdaa 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053-rcm.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "apq8053.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ8053 + PMI8950 RCM";
+	compatible = "qcom,apq8053-cdp", "qcom,apq8053", "qcom,cdp";
+	qcom,board-id= <21 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/apq8053.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/apq8053.dtsi
index c06b806..15a1595 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/apq8053.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,15 +10,14 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
+#include "msm8953.dtsi"
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. APQ 8953";
+	compatible = "qcom,apq8053";
+	qcom,msm-id = <304 0x0>;
 };
+
+&secure_mem {
+	status = "disabled";
+};
+
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
new file mode 100644
index 0000000..78f933a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi
@@ -0,0 +1,112 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&mdss_mdp {
+	dsi_dual_nt35597_cmd: qcom,mdss_dsi_nt35597_wqxga_cmd{
+		qcom,mdss-dsi-panel-name =
+			"Dual nt35597 cmd mode dsi panel without DSC";
+		qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,adjust-timer-wakeup-ms = <1>;
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-pan-physical-width-dimension = <74>;
+		qcom,mdss-pan-physical-height-dimension = <131>;
+		qcom,mdss-dsi-t-clk-post = <0x0d>;
+		qcom,mdss-dsi-t-clk-pre = <0x2d>;
+		qcom,mdss-dsi-bl-max-level = <4095>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-wr-mem-start = <0x2c>;
+		qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,ulps-enabled;
+
+		qcom,mdss-dsi-display-timings {
+			timing@0{
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <100>;
+				qcom,mdss-dsi-h-back-porch = <32>;
+				qcom,mdss-dsi-h-pulse-width = <16>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <7>;
+				qcom,mdss-dsi-v-front-porch = <8>;
+				qcom,mdss-dsi-v-pulse-width = <1>;
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-on-command =
+					[15 01 00 00 10 00 02 ff 10
+					15 01 00 00 10 00 02 fb 01
+					15 01 00 00 10 00 02 ba 03
+					15 01 00 00 10 00 02 e5 01
+					15 01 00 00 10 00 02 35 00
+					15 01 00 00 10 00 02 bb 10
+					15 01 00 00 10 00 02 b0 03
+					15 01 00 00 10 00 02 ff e0
+					15 01 00 00 10 00 02 fb 01
+					15 01 00 00 10 00 02 6b 3d
+					15 01 00 00 10 00 02 6c 3d
+					15 01 00 00 10 00 02 6d 3d
+					15 01 00 00 10 00 02 6e 3d
+					15 01 00 00 10 00 02 6f 3d
+					15 01 00 00 10 00 02 35 02
+					15 01 00 00 10 00 02 36 72
+					15 01 00 00 10 00 02 37 10
+					15 01 00 00 10 00 02 08 c0
+					15 01 00 00 10 00 02 ff 24
+					15 01 00 00 10 00 02 fb 01
+					15 01 00 00 10 00 02 c6 06
+					15 01 00 00 10 00 02 ff 10
+					05 01 00 00 a0 00 02 11 00
+					05 01 00 00 a0 00 02 29 00];
+
+				qcom,mdss-dsi-off-command = [05 01 00 00 0a 00
+					02 28 00 05 01 00 00 3c 00 02 10 00];
+
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+				qcom,mdss-dsi-panel-timings = [cd 32 22 00 60
+					64 26 34 29 03 04 00];
+
+				qcom,config-select =
+					<&dsi_dual_nt35597_cmd_config0>;
+
+				dsi_dual_nt35597_cmd_config0: config0 {
+					qcom,split-mode = "dualctl-split";
+				};
+
+				dsi_dual_nt35597_cmd_config1: config1 {
+					qcom,split-mode = "pingpong-split";
+				};
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
new file mode 100644
index 0000000..9cd5815
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-dualmipi-wqxga-video.dtsi
@@ -0,0 +1,111 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&mdss_mdp {
+	dsi_dual_nt35597_video: qcom,mdss_dsi_nt35597_wqxga_video {
+		qcom,mdss-dsi-panel-name =
+			"Dual nt35597 video mode dsi panel without DSC";
+		qcom,mdss-dsi-panel-type = "dsi_video_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-underflow-color = <0x3ff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-panel-hdr-enabled;
+		qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000
+			17000 15500 30000 8000 3000>;
+		qcom,mdss-dsi-panel-peak-brightness = <4200000>;
+		qcom,mdss-dsi-panel-blackness-level = <3230>;
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-pan-physical-width-dimension = <74>;
+		qcom,mdss-pan-physical-height-dimension = <131>;
+		qcom,cmd-sync-wait-broadcast;
+		qcom,mdss-dsi-t-clk-post = <0x0d>;
+		qcom,mdss-dsi-t-clk-pre = <0x2d>;
+		qcom,mdss-dsi-min-refresh-rate = <55>;
+		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";
+
+		qcom,mdss-dsi-display-timings {
+			timing@0{
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <100>;
+				qcom,mdss-dsi-h-back-porch = <32>;
+				qcom,mdss-dsi-h-pulse-width = <16>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <7>;
+				qcom,mdss-dsi-v-front-porch = <8>;
+				qcom,mdss-dsi-v-pulse-width = <1>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-on-command =
+					[15 01 00 00 00 00 02 ff 10
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 ba 03
+					15 01 00 00 00 00 02 e5 01
+					15 01 00 00 00 00 02 35 00
+					15 01 00 00 00 00 02 bb 03
+					15 01 00 00 00 00 02 b0 03
+					39 01 00 00 00 00 06 3b 03
+						08 08 64 9a
+					15 01 00 00 00 00 02 ff e0
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 6b 3d
+					15 01 00 00 00 00 02 6c 3d
+					15 01 00 00 00 00 02 6d 3d
+					15 01 00 00 00 00 02 6e 3d
+					15 01 00 00 00 00 02 6f 3d
+					15 01 00 00 00 00 02 35 02
+					15 01 00 00 00 00 02 36 72
+					15 01 00 00 00 00 02 37 10
+					15 01 00 00 00 00 02 08 c0
+					15 01 00 00 00 00 02 ff 10
+					05 01 00 00 78 00 02 11 00
+					05 01 00 00 32 00 02 29 00];
+				qcom,mdss-dsi-off-command = [05 01 00 00
+					0a 00 02 28 00 05 01 00 00 3c 00
+					02 10 00];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+				qcom,mdss-dsi-panel-timings = [e2 36 24 00 66
+					6a 28 38 2a 03 04 00];
+
+				qcom,config-select =
+					<&dsi_dual_nt35597_video_config0>;
+
+				dsi_dual_nt35597_video_config0:
+					config0 {
+						qcom,split-mode =
+							"dualctl-split";
+				};
+
+				dsi_dual_nt35597_video_config1:
+					config1 {
+						qcom,split-mode =
+							"pingpong-split";
+				};
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
index 0ca1175..0e60a0c 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi
@@ -88,7 +88,7 @@
 					15 01 00 00 00 00 02 5d 81
 					15 01 00 00 00 00 02 5e 00
 					15 01 00 00 00 00 02 5f 01
-					15 01 00 00 00 00 02 72 31
+					15 01 00 00 00 00 02 72 11
 					15 01 00 00 00 00 02 68 03
 					/* CMD2_P4 */
 					15 01 00 00 00 00 02 ff 24
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
index ac8a956..2c54504 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi
@@ -74,7 +74,7 @@
 					15 01 00 00 00 00 02 5d 81
 					15 01 00 00 00 00 02 5e 00
 					15 01 00 00 00 00 02 5f 01
-					15 01 00 00 00 00 02 72 31
+					15 01 00 00 00 00 02 72 11
 					15 01 00 00 00 00 02 68 03
 					/* CMD2_P4 */
 					15 01 00 00 00 00 02 ff 24
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
index 87cabae..1a8ce91 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi
@@ -89,7 +89,7 @@
 					15 01 00 00 00 00 02 5D 81
 					15 01 00 00 00 00 02 5E 00
 					15 01 00 00 00 00 02 5F 01
-					15 01 00 00 00 00 02 72 31
+					15 01 00 00 00 00 02 72 11
 					15 01 00 00 00 00 02 68 03
 					/* CMD2_P4 */
 					15 01 00 00 00 00 02 ff 24
@@ -210,6 +210,11 @@
 					15 01 00 00 00 00 02 E5 01
 					/* CMD mode(10) VDO mode(03) */
 					15 01 00 00 00 00 02 BB 10
+					/* NVT SDC */
+					15 01 00 00 00 00 02 C0 00
+					/* GRAM Slide Parameter */
+					29 01 00 00 00 00 0C C9 01 01 70
+					00 0A 06 67 04 C5 12 18
 					/* Non Reload MTP */
 					15 01 00 00 00 00 02 FB 01
 					/* SlpOut + DispOn */
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
index 0d0e7f7..95e0e5a 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi
@@ -29,7 +29,6 @@
 		qcom,mdss-dsi-lane-1-state;
 		qcom,mdss-dsi-lane-2-state;
 		qcom,mdss-dsi-lane-3-state;
-		qcom,cmd-sync-wait-broadcast;
 		qcom,mdss-dsi-dma-trigger = "trigger_sw";
 		qcom,mdss-dsi-mdp-trigger = "none";
 		qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 50>;
@@ -76,7 +75,7 @@
 					15 01 00 00 00 00 02 5D 81
 					15 01 00 00 00 00 02 5E 00
 					15 01 00 00 00 00 02 5F 01
-					15 01 00 00 00 00 02 72 31
+					15 01 00 00 00 00 02 72 11
 					15 01 00 00 00 00 02 68 03
 					/* CMD2_P4 */
 					15 01 00 00 00 00 02 FF 24
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
new file mode 100644
index 0000000..5529ed1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-cmd.dtsi
@@ -0,0 +1,196 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_nt35695b_truly_fhd_cmd: qcom,mdss_dsi_nt35695b_truly_fhd_cmd {
+		qcom,mdss-dsi-panel-name =
+				"nt35695b truly fhd command mode dsi panel";
+		qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-traffic-mode = "burst_mode";
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-dsi-tx-eot-append;
+		qcom,mdss-dsi-post-init-delay = <1>;
+		qcom,ulps-enabled;
+		qcom,mdss-dsi-t-clk-post = <0x0d>;
+		qcom,mdss-dsi-t-clk-pre = <0x2f>;
+		qcom,mdss-dsi-bl-min-level = <1>;
+		qcom,mdss-dsi-bl-max-level = <4095>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+
+		qcom,mdss-dsi-display-timings {
+			timing@0{
+				qcom,mdss-dsi-panel-width = <1080>;
+				qcom,mdss-dsi-panel-height = <1920>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <60>;
+				qcom,mdss-dsi-h-pulse-width = <12>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <2>;
+				qcom,mdss-dsi-v-front-porch = <12>;
+				qcom,mdss-dsi-v-pulse-width = <2>;
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-on-command =
+					[15 01 00 00 10 00 02 ff 20
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 00 01
+					15 01 00 00 00 00 02 01 55
+					15 01 00 00 00 00 02 02 45
+					15 01 00 00 00 00 02 03 55
+					15 01 00 00 00 00 02 05 50
+					15 01 00 00 00 00 02 06 a8
+					15 01 00 00 00 00 02 07 ad
+					15 01 00 00 00 00 02 08 0c
+					15 01 00 00 00 00 02 0b aa
+					15 01 00 00 00 00 02 0c aa
+					15 01 00 00 00 00 02 0e b0
+					15 01 00 00 00 00 02 0f b3
+					15 01 00 00 00 00 02 11 28
+					15 01 00 00 00 00 02 12 10
+					15 01 00 00 00 00 02 13 01
+					15 01 00 00 00 00 02 14 4a
+					15 01 00 00 00 00 02 15 12
+					15 01 00 00 00 00 02 16 12
+					15 01 00 00 00 00 02 30 01
+					15 01 00 00 00 00 02 72 11
+					15 01 00 00 00 00 02 58 82
+					15 01 00 00 00 00 02 59 00
+					15 01 00 00 00 00 02 5a 02
+					15 01 00 00 00 00 02 5b 00
+					15 01 00 00 00 00 02 5c 82
+					15 01 00 00 00 00 02 5d 80
+					15 01 00 00 00 00 02 5e 02
+					15 01 00 00 00 00 02 5f 00
+					15 01 00 00 00 00 02 ff 24
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 00 01
+					15 01 00 00 00 00 02 01 0b
+					15 01 00 00 00 00 02 02 0c
+					15 01 00 00 00 00 02 03 89
+					15 01 00 00 00 00 02 04 8a
+					15 01 00 00 00 00 02 05 0f
+					15 01 00 00 00 00 02 06 10
+					15 01 00 00 00 00 02 07 10
+					15 01 00 00 00 00 02 08 1c
+					15 01 00 00 00 00 02 09 00
+					15 01 00 00 00 00 02 0a 00
+					15 01 00 00 00 00 02 0b 00
+					15 01 00 00 00 00 02 0c 00
+					15 01 00 00 00 00 02 0d 13
+					15 01 00 00 00 00 02 0e 15
+					15 01 00 00 00 00 02 0f 17
+					15 01 00 00 00 00 02 10 01
+					15 01 00 00 00 00 02 11 0b
+					15 01 00 00 00 00 02 12 0c
+					15 01 00 00 00 00 02 13 89
+					15 01 00 00 00 00 02 14 8a
+					15 01 00 00 00 00 02 15 0f
+					15 01 00 00 00 00 02 16 10
+					15 01 00 00 00 00 02 17 10
+					15 01 00 00 00 00 02 18 1c
+					15 01 00 00 00 00 02 19 00
+					15 01 00 00 00 00 02 1a 00
+					15 01 00 00 00 00 02 1b 00
+					15 01 00 00 00 00 02 1c 00
+					15 01 00 00 00 00 02 1d 13
+					15 01 00 00 00 00 02 1e 15
+					15 01 00 00 00 00 02 1f 17
+					15 01 00 00 00 00 02 20 00
+					15 01 00 00 00 00 02 21 01
+					15 01 00 00 00 00 02 22 00
+					15 01 00 00 00 00 02 23 40
+					15 01 00 00 00 00 02 24 40
+					15 01 00 00 00 00 02 25 6d
+					15 01 00 00 00 00 02 26 40
+					15 01 00 00 00 00 02 27 40
+					15 01 00 00 00 00 02 29 d8
+					15 01 00 00 00 00 02 2a 2a
+					15 01 00 00 00 00 02 4b 03
+					15 01 00 00 00 00 02 4c 11
+					15 01 00 00 00 00 02 4d 10
+					15 01 00 00 00 00 02 4e 01
+					15 01 00 00 00 00 02 4f 01
+					15 01 00 00 00 00 02 50 10
+					15 01 00 00 00 00 02 51 00
+					15 01 00 00 00 00 02 52 80
+					15 01 00 00 00 00 02 53 00
+					15 01 00 00 00 00 02 54 07
+					15 01 00 00 00 00 02 55 25
+					15 01 00 00 00 00 02 56 00
+					15 01 00 00 00 00 02 58 07
+					15 01 00 00 00 00 02 5b 43
+					15 01 00 00 00 00 02 5c 00
+					15 01 00 00 00 00 02 5f 73
+					15 01 00 00 00 00 02 60 73
+					15 01 00 00 00 00 02 63 22
+					15 01 00 00 00 00 02 64 00
+					15 01 00 00 00 00 02 67 08
+					15 01 00 00 00 00 02 68 04
+					15 01 00 00 00 00 02 7a 80
+					15 01 00 00 00 00 02 7b 91
+					15 01 00 00 00 00 02 7c d8
+					15 01 00 00 00 00 02 7d 60
+					15 01 00 00 00 00 02 93 06
+					15 01 00 00 00 00 02 94 06
+					15 01 00 00 00 00 02 8a 00
+					15 01 00 00 00 00 02 9b 0f
+					15 01 00 00 00 00 02 b3 c0
+					15 01 00 00 00 00 02 b4 00
+					15 01 00 00 00 00 02 b5 00
+					15 01 00 00 00 00 02 b6 21
+					15 01 00 00 00 00 02 b7 22
+					15 01 00 00 00 00 02 b8 07
+					15 01 00 00 00 00 02 b9 07
+					15 01 00 00 00 00 02 ba 22
+					15 01 00 00 00 00 02 bd 20
+					15 01 00 00 00 00 02 be 07
+					15 01 00 00 00 00 02 bf 07
+					15 01 00 00 00 00 02 c1 6d
+					15 01 00 00 00 00 02 c4 24
+					15 01 00 00 00 00 02 e3 00
+					15 01 00 00 00 00 02 ec 00
+					15 01 00 00 00 00 02 ff 10
+					15 01 00 00 00 00 02 bb 10
+					15 01 00 00 00 00 02 35 02
+					05 01 00 00 78 00 02 11 00
+					05 01 00 00 78 00 02 29 00];
+				qcom,mdss-dsi-off-command = [05 01 00 00 14
+					00 02 28 00 05 01 00 00 78 00 02 10 00];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-panel-timings = [e6 38 26 00 68 6e
+					2a 3c 44 03 04 00];
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
new file mode 100644
index 0000000..720bb1d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt35695b-truly-fhd-video.dtsi
@@ -0,0 +1,191 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_nt35695b_truly_fhd_video: qcom,mdss_dsi_nt35695b_truly_fhd_video {
+		qcom,mdss-dsi-panel-name =
+				"nt35695b truly fhd video mode dsi panel";
+		qcom,mdss-dsi-panel-type = "dsi_video_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-traffic-mode = "burst_mode";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-bl-min-level = <1>;
+		qcom,mdss-dsi-bl-max-level = <4095>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-dsi-tx-eot-append;
+		qcom,mdss-dsi-post-init-delay = <1>;
+		qcom,mdss-dsi-t-clk-post = <0x0d>;
+		qcom,mdss-dsi-t-clk-pre = <0x2f>;
+
+		qcom,mdss-dsi-display-timings {
+			timing@0{
+				qcom,mdss-dsi-panel-width = <1080>;
+				qcom,mdss-dsi-panel-height = <1920>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <60>;
+				qcom,mdss-dsi-h-pulse-width = <12>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+				qcom,mdss-dsi-v-back-porch = <2>;
+				qcom,mdss-dsi-v-front-porch = <12>;
+				qcom,mdss-dsi-v-pulse-width = <2>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-on-command =
+					[15 01 00 00 10 00 02 ff 20
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 00 01
+					15 01 00 00 00 00 02 01 55
+					15 01 00 00 00 00 02 02 45
+					15 01 00 00 00 00 02 03 55
+					15 01 00 00 00 00 02 05 50
+					15 01 00 00 00 00 02 06 a8
+					15 01 00 00 00 00 02 07 ad
+					15 01 00 00 00 00 02 08 0c
+					15 01 00 00 00 00 02 0b aa
+					15 01 00 00 00 00 02 0c aa
+					15 01 00 00 00 00 02 0e b0
+					15 01 00 00 00 00 02 0f b3
+					15 01 00 00 00 00 02 11 28
+					15 01 00 00 00 00 02 12 10
+					15 01 00 00 00 00 02 13 01
+					15 01 00 00 00 00 02 14 4a
+					15 01 00 00 00 00 02 15 12
+					15 01 00 00 00 00 02 16 12
+					15 01 00 00 00 00 02 30 01
+					15 01 00 00 00 00 02 72 11
+					15 01 00 00 00 00 02 58 82
+					15 01 00 00 00 00 02 59 00
+					15 01 00 00 00 00 02 5a 02
+					15 01 00 00 00 00 02 5b 00
+					15 01 00 00 00 00 02 5c 82
+					15 01 00 00 00 00 02 5d 80
+					15 01 00 00 00 00 02 5e 02
+					15 01 00 00 00 00 02 5f 00
+					15 01 00 00 00 00 02 ff 24
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 00 01
+					15 01 00 00 00 00 02 01 0b
+					15 01 00 00 00 00 02 02 0c
+					15 01 00 00 00 00 02 03 89
+					15 01 00 00 00 00 02 04 8a
+					15 01 00 00 00 00 02 05 0f
+					15 01 00 00 00 00 02 06 10
+					15 01 00 00 00 00 02 07 10
+					15 01 00 00 00 00 02 08 1c
+					15 01 00 00 00 00 02 09 00
+					15 01 00 00 00 00 02 0a 00
+					15 01 00 00 00 00 02 0b 00
+					15 01 00 00 00 00 02 0c 00
+					15 01 00 00 00 00 02 0d 13
+					15 01 00 00 00 00 02 0e 15
+					15 01 00 00 00 00 02 0f 17
+					15 01 00 00 00 00 02 10 01
+					15 01 00 00 00 00 02 11 0b
+					15 01 00 00 00 00 02 12 0c
+					15 01 00 00 00 00 02 13 89
+					15 01 00 00 00 00 02 14 8a
+					15 01 00 00 00 00 02 15 0f
+					15 01 00 00 00 00 02 16 10
+					15 01 00 00 00 00 02 17 10
+					15 01 00 00 00 00 02 18 1c
+					15 01 00 00 00 00 02 19 00
+					15 01 00 00 00 00 02 1a 00
+					15 01 00 00 00 00 02 1b 00
+					15 01 00 00 00 00 02 1c 00
+					15 01 00 00 00 00 02 1d 13
+					15 01 00 00 00 00 02 1e 15
+					15 01 00 00 00 00 02 1f 17
+					15 01 00 00 00 00 02 20 00
+					15 01 00 00 00 00 02 21 01
+					15 01 00 00 00 00 02 22 00
+					15 01 00 00 00 00 02 23 40
+					15 01 00 00 00 00 02 24 40
+					15 01 00 00 00 00 02 25 6d
+					15 01 00 00 00 00 02 26 40
+					15 01 00 00 00 00 02 27 40
+					15 01 00 00 00 00 02 29 d8
+					15 01 00 00 00 00 02 2a 2a
+					15 01 00 00 00 00 02 4b 03
+					15 01 00 00 00 00 02 4c 11
+					15 01 00 00 00 00 02 4d 10
+					15 01 00 00 00 00 02 4e 01
+					15 01 00 00 00 00 02 4f 01
+					15 01 00 00 00 00 02 50 10
+					15 01 00 00 00 00 02 51 00
+					15 01 00 00 00 00 02 52 80
+					15 01 00 00 00 00 02 53 00
+					15 01 00 00 00 00 02 54 07
+					15 01 00 00 00 00 02 55 25
+					15 01 00 00 00 00 02 56 00
+					15 01 00 00 00 00 02 58 07
+					15 01 00 00 00 00 02 5b 43
+					15 01 00 00 00 00 02 5c 00
+					15 01 00 00 00 00 02 5f 73
+					15 01 00 00 00 00 02 60 73
+					15 01 00 00 00 00 02 63 22
+					15 01 00 00 00 00 02 64 00
+					15 01 00 00 00 00 02 67 08
+					15 01 00 00 00 00 02 68 04
+					15 01 00 00 00 00 02 7a 80
+					15 01 00 00 00 00 02 7b 91
+					15 01 00 00 00 00 02 7c d8
+					15 01 00 00 00 00 02 7d 60
+					15 01 00 00 00 00 02 93 06
+					15 01 00 00 00 00 02 94 06
+					15 01 00 00 00 00 02 8a 00
+					15 01 00 00 00 00 02 9b 0f
+					15 01 00 00 00 00 02 b3 c0
+					15 01 00 00 00 00 02 b4 00
+					15 01 00 00 00 00 02 b5 00
+					15 01 00 00 00 00 02 b6 21
+					15 01 00 00 00 00 02 b7 22
+					15 01 00 00 00 00 02 b8 07
+					15 01 00 00 00 00 02 b9 07
+					15 01 00 00 00 00 02 ba 22
+					15 01 00 00 00 00 02 bd 20
+					15 01 00 00 00 00 02 be 07
+					15 01 00 00 00 00 02 bf 07
+					15 01 00 00 00 00 02 c1 6d
+					15 01 00 00 00 00 02 c4 24
+					15 01 00 00 00 00 02 e3 00
+					15 01 00 00 00 00 02 ec 00
+					15 01 00 00 00 00 02 ff 10
+					15 01 00 00 00 00 02 bb 03
+					05 01 00 00 78 00 02 11 00
+					05 01 00 00 78 00 02 29 00];
+				qcom,mdss-dsi-off-command = [05 01 00 00
+					14 00 02 28 00 05 01 00 00 78 00
+					02 10 00];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-panel-timings = [e6 38 26 00
+					68 6e 2a 3c 44 03 04 00];
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
new file mode 100644
index 0000000..c059443
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi
@@ -0,0 +1,92 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_dual_nt36850_truly_cmd: qcom,mdss_dsi_nt36850_truly_wqhd_cmd {
+		qcom,mdss-dsi-panel-name =
+			"Dual nt36850 cmd mode dsi truly panel without DSC";
+		qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+		qcom,mdss-dsi-lane-map = "lane_map_0123";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-tx-eot-append;
+		qcom,cmd-sync-wait-broadcast;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-wr-mem-start = <0x2c>;
+		qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,mdss-dsi-panel-timings =
+			[da 34 24 00 64 68 28 38 2a 03 04 00];
+		qcom,mdss-dsi-t-clk-pre = <0x29>;
+		qcom,mdss-dsi-t-clk-post = <0x03>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-lp11-init;
+		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+		qcom,mdss-dsi-bl-min-level = <1>;
+		qcom,mdss-dsi-bl-max-level = <4095>;
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-dsi-display-timings {
+			timing@0 {
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <140>;
+				qcom,mdss-dsi-h-pulse-width = <20>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <20>;
+				qcom,mdss-dsi-v-front-porch = <8>;
+				qcom,mdss-dsi-v-pulse-width = <4>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-on-command = [
+					15 01 00 00 00 00 02 ff 10
+					15 01 00 00 00 00 02 fb 01
+					15 01 00 00 00 00 02 36 00
+					15 01 00 00 00 00 02 35 00
+					39 01 00 00 00 00 03 44 03 e8
+					15 01 00 00 00 00 02 51 ff
+					15 01 00 00 00 00 02 53 2c
+					15 01 00 00 00 00 02 55 01
+					05 01 00 00 0a 00 02 20 00
+					15 01 00 00 00 00 02 bb 10
+					05 01 00 00 78 00 02 11 00
+					05 01 00 00 14 00 02 29 00
+				];
+				qcom,mdss-dsi-off-command = [
+					05 01 00 00 14 00 02
+					28 00 05 01 00 00 78 00 02 10 00
+				];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
new file mode 100644
index 0000000..a13486d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-rm67195-amoled-fhd-cmd.dtsi
@@ -0,0 +1,137 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_rm67195_amoled_fhd_cmd: qcom,mdss_dsi_rm67195_amoled_fhd_cmd{
+		qcom,mdss-dsi-panel-name =
+			"rm67195 amoled fhd cmd mode dsi panel";
+		qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_pulse";
+		qcom,mdss-dsi-lane-map = "lane_map_0123";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-pan-physical-width-dimension = <70>;
+		qcom,mdss-pan-physical-height-dimension = <125>;
+		qcom,mdss-dsi-reset-sequence = <1 20>, <0 20>, <1 20>;
+		qcom,mdss-dsi-panel-orientation = "180";
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-t-clk-post = <0x0d>;
+		qcom,mdss-dsi-t-clk-pre = <0x2f>;
+		qcom,mdss-dsi-wr-mem-start = <0x2c>;
+		qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,mdss-dsi-lp11-init;
+		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+		qcom,mdss-dsi-bl-min-level = <1>;
+		qcom,mdss-dsi-bl-max-level = <255>;
+
+		qcom,mdss-dsi-display-timings {
+			timing@0{
+				qcom,mdss-dsi-panel-width = <1080>;
+				qcom,mdss-dsi-panel-height = <1920>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <60>;
+				qcom,mdss-dsi-h-pulse-width = <12>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <12>;
+				qcom,mdss-dsi-v-front-porch = <8>;
+				qcom,mdss-dsi-v-pulse-width = <4>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-on-command = [
+					15 01 00 00 00 00 02 fe 0d
+					15 01 00 00 00 00 02 0b c0
+					15 01 00 00 00 00 02 42 00
+					15 01 00 00 00 00 02 18 08
+					15 01 00 00 00 00 02 08 41
+					15 01 00 00 00 00 02 46 02
+					15 01 00 00 00 00 02 1e 04
+					15 01 00 00 02 00 02 1e 00
+					15 01 00 00 00 00 02 fe 0a
+					15 01 00 00 00 00 02 24 17
+					15 01 00 00 00 00 02 04 07
+					15 01 00 00 00 00 02 1a 0c
+					15 01 00 00 02 00 02 0f 44
+					15 01 00 00 00 00 02 fe 0b
+					15 01 00 00 00 00 02 28 40
+					15 01 00 00 02 00 02 29 4f
+					15 01 00 00 00 00 02 fe 04
+					15 01 00 00 00 00 02 0a d8
+					15 01 00 00 00 00 02 0c e6
+					15 01 00 00 00 00 02 4e 20
+					15 01 00 00 00 00 02 4f 1b
+					15 01 00 00 00 00 02 50 2f
+					15 01 00 00 02 00 02 51 08
+					15 01 00 00 00 00 02 fe 09
+					15 01 00 00 00 00 02 00 08
+					15 01 00 00 00 00 02 01 08
+					15 01 00 00 00 00 02 02 00
+					15 01 00 00 00 00 02 03 00
+					15 01 00 00 00 00 02 04 10
+					15 01 00 00 00 00 02 05 00
+					15 01 00 00 00 00 02 06 08
+					15 01 00 00 00 00 02 07 08
+					15 01 00 00 00 00 02 08 00
+					15 01 00 00 00 00 02 12 24
+					15 01 00 00 00 00 02 13 49
+					15 01 00 00 00 00 02 14 92
+					15 01 00 00 00 00 02 15 49
+					15 01 00 00 00 00 02 16 92
+					15 01 00 00 00 00 02 17 24
+					15 01 00 00 00 00 02 18 24
+					15 01 00 00 00 00 02 19 49
+					15 01 00 00 00 00 02 1a 92
+					15 01 00 00 00 00 02 1b 49
+					15 01 00 00 00 00 02 1c 92
+					15 01 00 00 00 00 02 1d 24
+					15 01 00 00 00 00 02 1e 24
+					15 01 00 00 00 00 02 1f 49
+					15 01 00 00 00 00 02 20 92
+					15 01 00 00 00 00 02 21 49
+					15 01 00 00 00 00 02 22 92
+					15 01 00 00 00 00 02 23 24
+					15 01 00 00 00 00 02 9b 07
+					15 01 00 00 02 00 02 9c a5
+					15 01 00 00 00 00 02 fe 00
+					15 01 00 00 00 00 02 c2 08
+					15 01 00 00 02 00 02 35 00
+					39 01 00 00 00 00 03 44 03 e8
+					05 01 00 00 82 00 02 11 00
+					05 01 00 00 14 00 02 29 00];
+
+				qcom,mdss-dsi-off-command = [05 01 00 00 14
+					00 02 28 00 05 01 00 00 82 00 02 10 00];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+				qcom,mdss-dsi-h-sync-pulse = <0>;
+			};
+		};
+
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi
index 50da1bf..45ac042 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi
@@ -41,6 +41,7 @@
 		qcom,mdss-dsi-te-using-wd;
 		qcom,mdss-dsi-te-using-te-pin;
 		qcom,mdss-dsi-panel-hdr-enabled;
+		qcom,ulps-enabled;
 		qcom,mdss-dsi-panel-hdr-color-primaries = <14500 15500 32000
 			17000 15500 30000 8000 3000>;
 		qcom,mdss-dsi-panel-peak-brightness = <4200000>;
@@ -49,26 +50,22 @@
 
 		qcom,mdss-dsi-display-timings {
 			timing@0{
-				qcom,mdss-dsi-panel-width = <640>;
-				qcom,mdss-dsi-panel-height = <480>;
-				qcom,mdss-dsi-h-front-porch = <20>;
-				qcom,mdss-dsi-h-back-porch = <20>;
-				qcom,mdss-dsi-h-pulse-width = <16>;
+				qcom,mdss-dsi-panel-width = <1440>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <100>;
+				qcom,mdss-dsi-h-pulse-width = <40>;
 				qcom,mdss-dsi-h-sync-skew = <0>;
-				qcom,mdss-dsi-v-back-porch = <16>;
-				qcom,mdss-dsi-v-front-porch = <4>;
-				qcom,mdss-dsi-v-pulse-width = <1>;
+				qcom,mdss-dsi-v-back-porch = <100>;
+				qcom,mdss-dsi-v-front-porch = <100>;
+				qcom,mdss-dsi-v-pulse-width = <40>;
 				qcom,mdss-dsi-h-left-border = <0>;
 				qcom,mdss-dsi-h-right-border = <0>;
 				qcom,mdss-dsi-v-top-border = <0>;
 				qcom,mdss-dsi-v-bottom-border = <0>;
-				qcom,mdss-dsi-h-sync-pulse = <0>;
 				qcom,mdss-dsi-panel-framerate = <60>;
-				qcom,mdss-dsi-hor-line-idle = <0 40 256>,
-								<40 120 128>,
-								<120 240 64>;
 				qcom,mdss-dsi-panel-timings =
-					[cd 32 22 00 60 64 26 34 29 03 04 00];
+					[00 21 09 09 24 23 08 08 08 03 04 00];
 				qcom,mdss-dsi-on-command =
 					[29 01 00 00 00 00 02 b0 03
 					05 01 00 00 0a 00 01 00
@@ -98,6 +95,124 @@
 					[05 01 00 00 32 00 02 28 00
 					05 01 00 00 78 00 02 10 00];
 				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+
+				qcom,compression-mode = "dsc";
+				qcom,mdss-dsc-slice-height = <40>;
+				qcom,mdss-dsc-slice-width = <720>;
+				qcom,mdss-dsc-slice-per-pkt = <1>;
+				qcom,mdss-dsc-bit-per-component = <8>;
+				qcom,mdss-dsc-bit-per-pixel = <8>;
+				qcom,mdss-dsc-block-prediction-enable;
+			};
+			timing@1{
+				qcom,mdss-dsi-panel-width = <1080>;
+				qcom,mdss-dsi-panel-height = <1920>;
+				qcom,mdss-dsi-h-front-porch = <120>;
+				qcom,mdss-dsi-h-back-porch = <460>;
+				qcom,mdss-dsi-h-pulse-width = <40>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <100>;
+				qcom,mdss-dsi-v-front-porch = <740>;
+				qcom,mdss-dsi-v-pulse-width = <40>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-panel-timings =
+					[00 21 09 09 24 23 08 08 08 03 04 00];
+				qcom,mdss-dsi-on-command =
+					[29 01 00 00 00 00 02 b0 03
+					05 01 00 00 0a 00 01 00
+					/* Soft reset, wait 10ms */
+					15 01 00 00 0a 00 02 3a 77
+					/* Set Pixel format (24 bpp) */
+					39 01 00 00 0a 00 05 2a 00 00 04 ff
+					/* Set Column address */
+					39 01 00 00 0a 00 05 2b 00 00 05 9f
+					/* Set page address */
+					15 01 00 00 0a 00 02 35 00
+					/* Set tear on */
+					39 01 00 00 0a 00 03 44 00 00
+					/* Set tear scan line */
+					15 01 00 00 0a 00 02 51 ff
+					/* write display brightness */
+					15 01 00 00 0a 00 02 53 24
+					 /* write control brightness */
+					15 01 00 00 0a 00 02 55 00
+					/* CABC brightness */
+					05 01 00 00 78 00 01 11
+					/* exit sleep mode, wait 120ms */
+					05 01 00 00 10 00 01 29];
+					/* Set display on, wait 16ms */
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command =
+					[05 01 00 00 32 00 02 28 00
+					05 01 00 00 78 00 02 10 00];
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+
+				qcom,compression-mode = "dsc";
+				qcom,mdss-dsc-slice-height = <40>;
+				qcom,mdss-dsc-slice-width = <540>;
+				qcom,mdss-dsc-slice-per-pkt = <1>;
+				qcom,mdss-dsc-bit-per-component = <8>;
+				qcom,mdss-dsc-bit-per-pixel = <8>;
+				qcom,mdss-dsc-block-prediction-enable;
+			};
+			timing@2{
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <1280>;
+				qcom,mdss-dsi-h-front-porch = <100>;
+				qcom,mdss-dsi-h-back-porch = <840>;
+				qcom,mdss-dsi-h-pulse-width = <40>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <100>;
+				qcom,mdss-dsi-v-front-porch = <1380>;
+				qcom,mdss-dsi-v-pulse-width = <40>;
+				qcom,mdss-dsi-h-left-border = <0>;
+				qcom,mdss-dsi-h-right-border = <0>;
+				qcom,mdss-dsi-v-top-border = <0>;
+				qcom,mdss-dsi-v-bottom-border = <0>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-panel-timings =
+					[00 21 09 09 24 23 08 08 08 03 04 00];
+				qcom,mdss-dsi-on-command =
+					[29 01 00 00 00 00 02 b0 03
+					05 01 00 00 0a 00 01 00
+					/* Soft reset, wait 10ms */
+					15 01 00 00 0a 00 02 3a 77
+					/* Set Pixel format (24 bpp) */
+					39 01 00 00 0a 00 05 2a 00 00 04 ff
+					/* Set Column address */
+					39 01 00 00 0a 00 05 2b 00 00 05 9f
+					/* Set page address */
+					15 01 00 00 0a 00 02 35 00
+					/* Set tear on */
+					39 01 00 00 0a 00 03 44 00 00
+					/* Set tear scan line */
+					15 01 00 00 0a 00 02 51 ff
+					/* write display brightness */
+					15 01 00 00 0a 00 02 53 24
+					 /* write control brightness */
+					15 01 00 00 0a 00 02 55 00
+					/* CABC brightness */
+					05 01 00 00 78 00 01 11
+					/* exit sleep mode, wait 120ms */
+					05 01 00 00 10 00 01 29];
+					/* Set display on, wait 16ms */
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-off-command =
+					[05 01 00 00 32 00 02 28 00
+					05 01 00 00 78 00 02 10 00];
+				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+
+				qcom,compression-mode = "dsc";
+				qcom,mdss-dsc-slice-height = <40>;
+				qcom,mdss-dsc-slice-width = <360>;
+				qcom,mdss-dsc-slice-per-pkt = <1>;
+				qcom,mdss-dsc-bit-per-component = <8>;
+				qcom,mdss-dsc-bit-per-pixel = <8>;
+				qcom,mdss-dsc-block-prediction-enable;
 			};
 		};
 	};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi
index 895cbc5..9a4e318 100644
--- a/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-dualmipi-cmd.dtsi
@@ -71,20 +71,20 @@
 				qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
 			};
 			timing@1{
-				qcom,mdss-dsi-panel-width = <1280>;
-				qcom,mdss-dsi-panel-height = <1440>;
-				qcom,mdss-dsi-h-front-porch = <120>;
-				qcom,mdss-dsi-h-back-porch = <44>;
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <100>;
+				qcom,mdss-dsi-h-back-porch = <32>;
 				qcom,mdss-dsi-h-pulse-width = <16>;
 				qcom,mdss-dsi-h-sync-skew = <0>;
-				qcom,mdss-dsi-v-back-porch = <4>;
+				qcom,mdss-dsi-v-back-porch = <7>;
 				qcom,mdss-dsi-v-front-porch = <8>;
-				qcom,mdss-dsi-v-pulse-width = <4>;
+				qcom,mdss-dsi-v-pulse-width = <1>;
+				qcom,mdss-dsi-h-sync-pulse = <0>;
 				qcom,mdss-dsi-h-left-border = <0>;
 				qcom,mdss-dsi-h-right-border = <0>;
 				qcom,mdss-dsi-v-top-border = <0>;
 				qcom,mdss-dsi-v-bottom-border = <0>;
-				qcom,mdss-dsi-h-sync-pulse = <0>;
 				qcom,mdss-dsi-panel-framerate = <60>;
 				qcom,mdss-dsi-on-command =
 					[/* exit sleep mode, wait 0ms */
diff --git a/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp356477-2800mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp356477-2800mah.dtsi
index 98dbf1c..4720238 100644
--- a/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp356477-2800mah.dtsi
+++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp356477-2800mah.dtsi
@@ -14,9 +14,15 @@
 	/* #mlp356477_2800mah_averaged_MasterSlave_Aug14th2017*/
 	qcom,max-voltage-uv = <4400000>;
 	qcom,fg-cc-cv-threshold-mv = <4390>;
-	qcom,fastchg-current-ma = <2800>;
+	qcom,fastchg-current-ma = <4200>;
 	qcom,batt-id-kohm = <82>;
 	qcom,battery-beta = <4250>;
+	qcom,jeita-fcc-ranges = <0   150   560000
+				 151 450  4200000
+				 451 550  2380000>;
+	qcom,jeita-fv-ranges =  <0   150  4150000
+				 151 450  4400000
+				 451 550  4150000>;
 	qcom,battery-type =
 		"mlp356477_2800mah_averaged_masterslave_aug14th2017";
 	qcom,checksum = <0x71B8>;
diff --git a/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
new file mode 100644
index 0000000..75504d4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
@@ -0,0 +1,87 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+qcom,mlp446579_3800mah {
+	/* #MLP446579_3800mAh_averaged_MasterSlave_Oct9th2017*/
+	qcom,max-voltage-uv = <4400000>;
+	qcom,fg-cc-cv-threshold-mv = <4390>;
+	qcom,fastchg-current-ma = <3800>;
+	qcom,batt-id-kohm = <91>;
+	qcom,battery-beta = <4250>;
+	qcom,jeita-fcc-ranges = <0   150  760000
+				 151 450  3800000
+				 451 550  1900000>;
+	qcom,jeita-fv-ranges =  <0   150  4150000
+				 151 450  4400000
+				 451 550  4150000>;
+	qcom,battery-type = "mlp446579_3800mah_averaged_masterslave_oct9th2017";
+	qcom,checksum = <0x3F0A>;
+	qcom,gui-version = "PMI8998GUI - 2.0.0.58";
+	qcom,fg-profile-data = [
+		 64 1F B8 05
+		 8F 0A 0F 06
+		 A0 1D F3 FC
+		 41 13 70 1D
+		 1E 18 1E 23
+		 92 45 88 52
+		 52 00 00 00
+		 12 00 00 00
+		 00 00 04 00
+		 FD D4 68 C3
+		 23 00 08 00
+		 C3 D2 52 DC
+		 33 05 BB FB
+		 84 05 BE 13
+		 18 07 9E 2B
+		 21 06 09 20
+		 27 00 14 00
+		 B7 1F A2 05
+		 8E 0A 1C 06
+		 7F 1D D9 ED
+		 20 12 FE 1D
+		 F4 18 0A 23
+		 4C 45 49 53
+		 54 00 00 00
+		 0E 00 00 00
+		 00 00 F8 07
+		 03 00 27 B2
+		 1A 00 00 00
+		 FE E2 52 DC
+		 FE 05 79 FA
+		 56 05 D5 FA
+		 27 F5 5C 22
+		 BC 33 CC FF
+		 07 10 00 00
+		 F0 0E 66 46
+		 1A 00 40 00
+		 A7 01 0A FA
+		 FF 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+		 00 00 00 00
+	];
+};
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
index 8e5d854..ae22a36 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
@@ -21,6 +21,7 @@
 		#iommu-cells = <1>;
 		qcom,dynamic;
 		qcom,use-3-lvl-tables;
+		qcom,disable-atos;
 		#global-interrupts = <2>;
 		qcom,regulator-names = "vdd";
 		vdd-supply = <&gpu_cx_gdsc>;
@@ -34,14 +35,8 @@
 				<GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
 				<GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
 				<GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
-		clock-names = "gcc_ddrss_gpu_axi_clk",
-				"gcc_gpu_memnoc_gfx_clk",
-				"gpu_cc_ahb_clk",
-				"gpu_cc_cx_gmu_clk";
-		clocks = <&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
-			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
-			<&clock_gpucc GPU_CC_AHB_CLK>,
-			<&clock_gpucc GPU_CC_CX_GMU_CLK>;
+		clock-names = "gcc_gpu_memnoc_gfx_clk";
+		clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
 		attach-impl-defs =
 				<0x6000 0x2378>,
 				<0x6060 0x1055>,
@@ -65,6 +60,8 @@
 		#iommu-cells = <2>;
 		qcom,skip-init;
 		qcom,use-3-lvl-tables;
+		qcom,no-asid-retention;
+		qcom,disable-atos;
 		#global-interrupts = <1>;
 		#size-cells = <1>;
 		#address-cells = <1>;
@@ -295,6 +292,7 @@
 	};
 
 	kgsl_iommu_test_device {
+		status = "disabled";
 		compatible = "iommu-debug-test";
 		/*
 		 * 0x7 isn't a valid sid, but should pass the sid sanity check.
@@ -308,9 +306,19 @@
 	apps_iommu_test_device {
 		compatible = "iommu-debug-test";
 		/*
-		 * This SID belongs to QUP1-GSI. We can't use a fake SID for
+		 * This SID belongs to TSIF. We can't use a fake SID for
 		 * the apps_smmu device.
 		 */
-		iommus = <&apps_smmu 0x16 0x0>;
+		iommus = <&apps_smmu 0x20 0xf>;
+	};
+
+	apps_iommu_coherent_test_device {
+		compatible = "iommu-debug-test";
+		/*
+		 * This SID belongs to TSIF. We can't use a fake SID for
+		 * the apps_smmu device.
+		 */
+		iommus = <&apps_smmu 0x20 0xf>;
+		dma-coherent;
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
index 5de0e44..e4fe2e3 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -21,6 +21,7 @@
 		#iommu-cells = <1>;
 		qcom,dynamic;
 		qcom,use-3-lvl-tables;
+		qcom,disable-atos;
 		#global-interrupts = <2>;
 		qcom,regulator-names = "vdd";
 		vdd-supply = <&gpu_cx_gdsc>;
@@ -34,10 +35,8 @@
 				<GIC_SPI 369 IRQ_TYPE_EDGE_RISING>,
 				<GIC_SPI 370 IRQ_TYPE_EDGE_RISING>,
 				<GIC_SPI 371 IRQ_TYPE_EDGE_RISING>;
-		clock-names =	"gcc_gpu_memnoc_gfx_clk",
-				"gpu_cc_ahb_clk";
-		clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
-			<&clock_gpucc GPU_CC_AHB_CLK>;
+		clock-names =	"gcc_gpu_memnoc_gfx_clk";
+		clocks = <&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
 		attach-impl-defs =
 				<0x6000 0x2378>,
 				<0x6060 0x1055>,
@@ -62,6 +61,7 @@
 		qcom,skip-init;
 		qcom,use-3-lvl-tables;
 		qcom,no-asid-retention;
+		qcom,disable-atos;
 		#global-interrupts = <1>;
 		#size-cells = <1>;
 		#address-cells = <1>;
@@ -314,6 +314,7 @@
 	};
 
 	kgsl_iommu_test_device {
+		status = "disabled";
 		compatible = "iommu-debug-test";
 		/*
 		 * 0x7 isn't a valid sid, but should pass the sid sanity check.
@@ -345,12 +346,19 @@
 };
 
 &apps_smmu {
-	qcom,actlr =	<0x0000 0x3ff 0x3>,
-			<0x0400 0x3ff 0x3>,
-			<0x0800 0x3ff 0x103>,
-			<0x0c00 0x3ff 0x103>,
-			<0x1000 0x3ff 0x103>,
-			<0x1400 0x3ff 0x3>,
-			<0x1800 0x3ff 0x3>,
-			<0x1c00 0x3ff 0x3>;
+	qcom,actlr =	<0x0880 0x8 0x103>,
+			<0x0881 0x8 0x103>,
+			<0x0c80 0x8 0x103>,
+			<0x0c81 0x8 0x103>,
+			<0x1090 0x0 0x103>,
+			<0x1091 0x0 0x103>,
+			<0x10a0 0x8 0x103>,
+			<0x10b0 0x0 0x103>,
+			<0x10a1 0x8 0x103>,
+			<0x10a3 0x8 0x103>,
+			<0x10a4 0x8 0x103>,
+			<0x10b4 0x0 0x103>,
+			<0x10a5 0x8 0x103>;
+	qcom,mmu500-errata-1 =	<0x800 0x3ff>,
+				<0xc00 0x3ff>;
 };
diff --git a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
index 0d2f9e8..b20feef8 100644
--- a/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-audio-lpass.dtsi
@@ -117,7 +117,7 @@
 
 		dai_mi2s4: qcom,msm-dai-q6-mi2s-quin {
 			compatible = "qcom,msm-dai-q6-mi2s";
-			qcom,msm-dai-q6-mi2s-dev-id = <5>;
+			qcom,msm-dai-q6-mi2s-dev-id = <4>;
 			qcom,msm-mi2s-rx-lines = <1>;
 			qcom,msm-mi2s-tx-lines = <2>;
 		};
@@ -353,6 +353,20 @@
 		qcom,msm-cpudai-afe-clk-ver = <2>;
 	};
 
+	dai_quin_auxpcm: qcom,msm-quin-auxpcm {
+		compatible = "qcom,msm-auxpcm-dev";
+		qcom,msm-cpudai-auxpcm-mode = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+		qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+		qcom,msm-cpudai-auxpcm-num-slots = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-slot-mapping = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+		qcom,msm-auxpcm-interface = "quinary";
+		qcom,msm-cpudai-afe-clk-ver = <2>;
+	};
+
 	hdmi_dba: qcom,msm-hdmi-dba-codec-rx {
 		compatible = "qcom,msm-hdmi-dba-codec-rx";
 		qcom,dba-bridge-chip = "adv7533";
@@ -522,4 +536,42 @@
 			qcom,msm-cpudai-tdm-data-align = <0>;
 		};
 	};
+
+	qcom,msm-dai-tdm-quin-rx {
+		compatible = "qcom,msm-dai-tdm";
+		qcom,msm-cpudai-tdm-group-id = <37184>;
+		qcom,msm-cpudai-tdm-group-num-ports = <1>;
+		qcom,msm-cpudai-tdm-group-port-id = <36928>;
+		qcom,msm-cpudai-tdm-clk-rate = <1536000>;
+		qcom,msm-cpudai-tdm-clk-internal = <1>;
+		qcom,msm-cpudai-tdm-sync-mode = <1>;
+		qcom,msm-cpudai-tdm-sync-src = <1>;
+		qcom,msm-cpudai-tdm-data-out = <0>;
+		qcom,msm-cpudai-tdm-invert-sync = <1>;
+		qcom,msm-cpudai-tdm-data-delay = <1>;
+		dai_quin_tdm_rx_0: qcom,msm-dai-q6-tdm-quin-rx-0 {
+			compatible = "qcom,msm-dai-q6-tdm";
+			qcom,msm-cpudai-tdm-dev-id = <36928>;
+			qcom,msm-cpudai-tdm-data-align = <0>;
+		};
+	};
+
+	qcom,msm-dai-tdm-quin-tx {
+		compatible = "qcom,msm-dai-tdm";
+		qcom,msm-cpudai-tdm-group-id = <37185>;
+		qcom,msm-cpudai-tdm-group-num-ports = <1>;
+		qcom,msm-cpudai-tdm-group-port-id = <36929>;
+		qcom,msm-cpudai-tdm-clk-rate = <1536000>;
+		qcom,msm-cpudai-tdm-clk-internal = <1>;
+		qcom,msm-cpudai-tdm-sync-mode = <1>;
+		qcom,msm-cpudai-tdm-sync-src = <1>;
+		qcom,msm-cpudai-tdm-data-out = <0>;
+		qcom,msm-cpudai-tdm-invert-sync = <1>;
+		qcom,msm-cpudai-tdm-data-delay = <1>;
+		dai_quin_tdm_tx_0: qcom,msm-dai-q6-tdm-quin-tx-0 {
+			compatible = "qcom,msm-dai-q6-tdm";
+			qcom,msm-cpudai-tdm-dev-id = <36929>;
+			qcom,msm-cpudai-tdm-data-align = <0>;
+		};
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
index dcc646c93b..b43c876 100644
--- a/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-gdsc-sdm845.dtsi
@@ -181,6 +181,8 @@
 		qcom,poll-cfg-gdscr;
 		qcom,support-hw-trigger;
 		status = "disabled";
+		proxy-supply = <&mdss_core_gdsc>;
+		qcom,proxy-consumer-enable;
 	};
 
 	/* GDSCs in Graphics CC */
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cdp-1200p.dts b/arch/arm64/boot/dts/qcom/msm8953-cdp-1200p.dts
new file mode 100644
index 0000000..a685380
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp-1200p.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 CDP 1200P";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <1 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-cdp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-cdp.dts
index c06b806..1f78902 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 CDP";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <1 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
index c06b806..243aaf5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-cdp.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,8 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&blsp1_uart0 {
+	status = "ok";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart_console_active>;
 };
diff --git a/arch/arm64/boot/dts/qcom/msm8953-cpu.dtsi b/arch/arm64/boot/dts/qcom/msm8953-cpu.dtsi
new file mode 100644
index 0000000..1b78fdd
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-cpu.dtsi
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+/ {
+	psci {
+		compatible = "arm,psci-1.0";
+		method = "smc";
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		cpu-map {
+			cluster0 {
+				core0 {
+					cpu = <&CPU0>;
+				};
+				core1 {
+					cpu = <&CPU1>;
+				};
+				core2 {
+					cpu = <&CPU2>;
+				};
+				core3 {
+					cpu = <&CPU3>;
+				};
+			};
+
+			cluster1 {
+				core0 {
+					cpu = <&CPU4>;
+				};
+				core1 {
+					cpu = <&CPU5>;
+				};
+				core2 {
+					cpu = <&CPU6>;
+				};
+				core3 {
+					cpu = <&CPU7>;
+				};
+			};
+		};
+
+		CPU0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			reg = <0x0>;
+			enable-method = "psci";
+			efficiency = <1024>;
+			next-level-cache = <&L2_0>;
+			L2_0: l2-cache {
+			      compatible = "arm,arch-cache";
+			      cache-level = <2>;
+			      /* A53 L2 dump not supported */
+			      qcom,dump-size = <0x0>;
+			};
+			L1_I_0: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_0: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x1>;
+			efficiency = <1024>;
+			next-level-cache = <&L2_0>;
+			L1_I_1: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_1: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU2: cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x2>;
+			efficiency = <1024>;
+			next-level-cache = <&L2_0>;
+			L1_I_2: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_2: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU3: cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x3>;
+			efficiency = <1024>;
+			next-level-cache = <&L2_0>;
+			L1_I_3: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_3: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU4: cpu@100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x100>;
+			efficiency = <1126>;
+			next-level-cache = <&L2_1>;
+			L2_1: l2-cache {
+			      compatible = "arm,arch-cache";
+			      cache-level = <2>;
+			      /* A53 L2 dump not supported */
+			      qcom,dump-size = <0x0>;
+			};
+			L1_I_100: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_100: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU5: cpu@101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x101>;
+			efficiency = <1126>;
+			next-level-cache = <&L2_1>;
+			L1_I_101: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_101: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU6: cpu@102 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x102>;
+			efficiency = <1126>;
+			next-level-cache = <&L2_1>;
+			L1_I_102: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_102: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+
+		CPU7: cpu@103 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53","arm,armv8";
+			enable-method = "psci";
+			reg = <0x103>;
+			efficiency = <1126>;
+			next-level-cache = <&L2_1>;
+			L1_I_103: l1-icache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x8800>;
+			};
+			L1_D_103: l1-dcache {
+			      compatible = "arm,arch-cache";
+			      qcom,dump-size = <0x9000>;
+			};
+		};
+	};
+};
+
+&soc {
+	cpuss_dump {
+		compatible = "qcom,cpuss-dump";
+		qcom,l2_dump0 {
+			/* L2 cache dump for A53 cluster */
+			qcom,dump-node = <&L2_0>;
+			qcom,dump-id = <0xC0>;
+		};
+		qcom,l2_dump1 {
+			/* L2 cache dump for A53 cluster */
+			qcom,dump-node = <&L2_1>;
+			qcom,dump-id = <0xC1>;
+		};
+		qcom,l1_i_cache0 {
+			qcom,dump-node = <&L1_I_0>;
+			qcom,dump-id = <0x60>;
+		};
+		qcom,l1_i_cache1 {
+			qcom,dump-node = <&L1_I_1>;
+			qcom,dump-id = <0x61>;
+		};
+		qcom,l1_i_cache2 {
+			qcom,dump-node = <&L1_I_2>;
+			qcom,dump-id = <0x62>;
+		};
+		qcom,l1_i_cache3 {
+			qcom,dump-node = <&L1_I_3>;
+			qcom,dump-id = <0x63>;
+		};
+		qcom,l1_i_cache100 {
+			qcom,dump-node = <&L1_I_100>;
+			qcom,dump-id = <0x64>;
+		};
+		qcom,l1_i_cache101 {
+			qcom,dump-node = <&L1_I_101>;
+			qcom,dump-id = <0x65>;
+		};
+		qcom,l1_i_cache102 {
+			qcom,dump-node = <&L1_I_102>;
+			qcom,dump-id = <0x66>;
+		};
+		qcom,l1_i_cache103 {
+			qcom,dump-node = <&L1_I_103>;
+			qcom,dump-id = <0x67>;
+		};
+		qcom,l1_d_cache0 {
+			qcom,dump-node = <&L1_D_0>;
+			qcom,dump-id = <0x80>;
+		};
+		qcom,l1_d_cache1 {
+			qcom,dump-node = <&L1_D_1>;
+			qcom,dump-id = <0x81>;
+		};
+		qcom,l1_d_cache2 {
+			qcom,dump-node = <&L1_D_2>;
+			qcom,dump-id = <0x82>;
+		};
+		qcom,l1_d_cache3 {
+			qcom,dump-node = <&L1_D_3>;
+			qcom,dump-id = <0x83>;
+		};
+		qcom,l1_d_cache100 {
+			qcom,dump-node = <&L1_D_100>;
+			qcom,dump-id = <0x84>;
+		};
+		qcom,l1_d_cache101 {
+			qcom,dump-node = <&L1_D_101>;
+			qcom,dump-id = <0x85>;
+		};
+		qcom,l1_d_cache102 {
+			qcom,dump-node = <&L1_D_102>;
+			qcom,dump-id = <0x86>;
+		};
+		qcom,l1_d_cache103 {
+			qcom,dump-node = <&L1_D_103>;
+			qcom,dump-id = <0x87>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
new file mode 100644
index 0000000..3dfd848
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-mtp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 Ext Codec MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-ext-codec-rcm.dts b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-rcm.dts
new file mode 100644
index 0000000..a81e212
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-ext-codec-rcm.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 Ext Codec RCM";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <21 1>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
index c06b806..524e7ca 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-iot-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 IOT MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 2>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-ipc.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-ipc.dts
index c06b806..89a54af 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ipc.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-ipc.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 IPC";
+	compatible = "qcom,msm8953-ipc", "qcom,msm8953", "qcom,ipc";
+	qcom,board-id= <12 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
index c06b806..26f4338 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-ipc.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,8 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&blsp1_uart0 {
+	status = "ok";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart_console_active>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-mtp.dts
index c06b806..1e8b0f0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
index c06b806..243aaf5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-mtp.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,8 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&blsp1_uart0 {
+	status = "ok";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart_console_active>;
 };
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
new file mode 100644
index 0000000..e3ada39
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -0,0 +1,1506 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	tlmm: pinctrl@1000000 {
+		compatible = "qcom,msm8953-pinctrl";
+		reg = <0x1000000 0x300000>;
+		interrupts = <0 208 0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+
+		pmx-uartconsole {
+			uart_console_active: uart_console_active {
+				mux {
+					pins = "gpio4", "gpio5";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			uart_console_sleep: uart_console_sleep {
+				mux {
+					pins = "gpio4", "gpio5";
+					function = "blsp_uart2";
+				};
+
+				config {
+					pins = "gpio4", "gpio5";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+
+		};
+		cci {
+			cci0_active: cci0_active {
+				/* cci0 active state */
+				mux {
+					/* CLK, DATA */
+					pins = "gpio29", "gpio30";
+					function = "cci_i2c";
+				};
+
+				config {
+					pins = "gpio29", "gpio30";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable; /* No PULL */
+				};
+			};
+
+			cci0_suspend: cci0_suspend {
+				/* cci0 suspended state */
+				mux {
+					/* CLK, DATA */
+					pins = "gpio29", "gpio30";
+					function = "cci_i2c";
+				};
+
+				config {
+					pins = "gpio29", "gpio30";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable; /* No PULL */
+				};
+			};
+
+			cci1_active: cci1_active {
+				/* cci1 active state */
+				mux {
+					/* CLK, DATA */
+					pins = "gpio31", "gpio32";
+					function = "cci_i2c";
+				};
+
+				config {
+					pins = "gpio31", "gpio32";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable; /* No PULL */
+				};
+			};
+
+			cci1_suspend: cci1_suspend {
+				/* cci1 suspended state */
+				mux {
+					/* CLK, DATA */
+					pins = "gpio31", "gpio32";
+					function = "cci_i2c";
+				};
+
+				config {
+					pins = "gpio31", "gpio32";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable; /* No PULL */
+				};
+			};
+		};
+
+		/*sensors */
+		cam_sensor_mclk0_default: cam_sensor_mclk0_default {
+			/* MCLK0 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio26";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio26";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk0_sleep: cam_sensor_mclk0_sleep {
+			/* MCLK0 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio26";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio26";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_default: cam_sensor_rear_default {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio40", "gpio39";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio40","gpio39";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_sleep: cam_sensor_rear_sleep {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio40","gpio39";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio40","gpio39";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_vana: cam_sensor_rear_vdig {
+			/* VDIG */
+			mux {
+				pins = "gpio134";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio134";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_vana_sleep: cam_sensor_rear_vdig_sleep {
+			/* VDIG */
+			mux {
+				pins = "gpio134";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio134";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk1_default: cam_sensor_mclk1_default {
+			/* MCLK1 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio27";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio27";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk1_sleep: cam_sensor_mclk1_sleep {
+			/* MCLK1 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio27";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio27";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front_default: cam_sensor_front_default {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio131","gpio132";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio131","gpio132";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front_sleep: cam_sensor_front_sleep {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio131","gpio132";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio131","gpio132";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk2_default: cam_sensor_mclk2_default {
+			/* MCLK2 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio28";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio28";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk2_sleep: cam_sensor_mclk2_sleep {
+			/* MCLK2 */
+			mux {
+				/* CLK, DATA */
+				pins = "gpio28";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio28";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front1_default: cam_sensor_front1_default {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio129", "gpio130";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio129", "gpio130";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front1_sleep: cam_sensor_front1_sleep {
+			/* RESET, STANDBY */
+			mux {
+				pins = "gpio129", "gpio130";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio129", "gpio130";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		pmx_adv7533_int: pmx_adv7533_int {
+			adv7533_int_active: adv7533_int_active {
+				mux {
+					pins = "gpio90";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio90";
+					drive-strength = <16>;
+					bias-disable;
+				};
+			};
+
+			adv7533_int_suspend: adv7533_int_suspend {
+				mux {
+					pins = "gpio90";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio90";
+					drive-strength = <16>;
+					bias-disable;
+				};
+			};
+
+		};
+
+		pmx_mdss: pmx_mdss {
+			mdss_dsi_active: mdss_dsi_active {
+				mux {
+					pins = "gpio61", "gpio59";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio61", "gpio59";
+					drive-strength = <8>; /* 8 mA */
+					bias-disable = <0>; /* no pull */
+					output-high;
+				};
+			};
+
+			mdss_dsi_suspend: mdss_dsi_suspend {
+				mux {
+					pins = "gpio61", "gpio59";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio61", "gpio59";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down; /* pull down */
+				};
+			};
+		};
+
+		pmx_mdss_te {
+			mdss_te_active: mdss_te_active {
+				mux {
+					pins = "gpio24";
+					function = "mdp_vsync";
+				};
+				config {
+					pins = "gpio24";
+					drive-strength = <2>; /* 8 mA */
+					bias-pull-down; /* pull down*/
+				};
+			};
+
+			mdss_te_suspend: mdss_te_suspend {
+				mux {
+					pins = "gpio24";
+					function = "mdp_vsync";
+				};
+				config {
+					pins = "gpio24";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down; /* pull down */
+				};
+			};
+		};
+
+		hsuart_active: default {
+			mux {
+				pins = "gpio12", "gpio13", "gpio14", "gpio15";
+				function = "blsp_uart4";
+			};
+
+			config {
+				pins = "gpio12", "gpio13", "gpio14", "gpio15";
+				drive-strength = <16>;
+				bias-disable;
+			};
+		};
+
+		hsuart_sleep: sleep {
+			mux {
+				pins = "gpio12", "gpio13", "gpio14", "gpio15";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio12", "gpio13", "gpio14", "gpio15";
+				drive-strength = <2>;
+				bias-disable;
+			};
+		};
+
+		/* SDC pin type */
+		sdc1_clk_on: sdc1_clk_on {
+			config {
+				pins = "sdc1_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <16>;	/* 16 MA */
+			};
+		};
+
+		sdc1_clk_off: sdc1_clk_off {
+			config {
+				pins = "sdc1_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <2>;	/* 2 MA */
+			};
+		};
+
+		sdc1_cmd_on: sdc1_cmd_on {
+			config {
+				pins = "sdc1_cmd";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc1_cmd_off: sdc1_cmd_off {
+			config {
+				pins = "sdc1_cmd";
+				num-grp-pins = <1>;
+				bias-pull-up;		/* pull up */
+				drive-strength = <2>;	/* 2 MA */
+			};
+		};
+
+		sdc1_data_on: sdc1_data_on {
+			config {
+				pins = "sdc1_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc1_data_off: sdc1_data_off {
+			config {
+				pins = "sdc1_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <2>;	/* 2 MA */
+			};
+		};
+
+		sdc1_rclk_on: sdc1_rclk_on {
+			config {
+				pins = "sdc1_rclk";
+				bias-pull-down; /* pull down */
+			};
+		};
+
+		sdc1_rclk_off: sdc1_rclk_off {
+			config {
+				pins = "sdc1_rclk";
+				bias-pull-down; /* pull down */
+			};
+		};
+
+		sdc2_clk_on: sdc2_clk_on {
+			config {
+				pins = "sdc2_clk";
+				drive-strength = <16>; /* 16 MA */
+				bias-disable; /* NO pull */
+			};
+		};
+
+		sdc2_clk_off: sdc2_clk_off {
+			config {
+				pins = "sdc2_clk";
+				bias-disable; /* NO pull */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		sdc2_cmd_on: sdc2_cmd_on {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up; /* pull up */
+				drive-strength = <10>; /* 10 MA */
+			};
+		};
+
+		sdc2_cmd_off: sdc2_cmd_off {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up; /* pull up */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		sdc2_data_on: sdc2_data_on {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up; /* pull up */
+				drive-strength = <10>; /* 10 MA */
+			};
+		};
+
+		sdc2_data_off: sdc2_data_off {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up; /* pull up */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		sdc2_cd_on: cd_on {
+			mux {
+				pins = "gpio133";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio133";
+				drive-strength = <2>;
+				bias-pull-up;
+			};
+		};
+
+		sdc2_cd_off: cd_off {
+			mux {
+				pins = "gpio133";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio133";
+				drive-strength = <2>;
+				bias-disable;
+			};
+		};
+
+		i2c_2 {
+			i2c_2_active: i2c_2_active {
+				/* active state */
+				mux {
+					pins = "gpio6", "gpio7";
+					function = "blsp_i2c2";
+				};
+
+				config {
+					pins = "gpio6", "gpio7";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_2_sleep: i2c_2_sleep {
+				/* suspended state */
+				mux {
+					pins = "gpio6", "gpio7";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio6", "gpio7";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		i2c_3 {
+			i2c_3_active: i2c_3_active {
+				/* active state */
+				mux {
+					pins = "gpio10", "gpio11";
+					function = "blsp_i2c3";
+				};
+
+				config {
+					pins = "gpio10", "gpio11";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_3_sleep: i2c_3_sleep {
+				/* suspended state */
+				mux {
+					pins = "gpio10", "gpio11";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio10", "gpio11";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		i2c_5 {
+			i2c_5_active: i2c_5_active {
+				/* active state */
+				mux {
+					pins = "gpio18", "gpio19";
+					function = "blsp_i2c5";
+				};
+
+				config {
+					pins = "gpio18", "gpio19";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+
+			i2c_5_sleep: i2c_5_sleep {
+				/* suspended state */
+				mux {
+					pins = "gpio18", "gpio19";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio18", "gpio19";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		pmx_rd_nfc_int {
+			/*qcom,pins = <&gp 17>;*/
+			pins = "gpio17";
+			qcom,pin-func = <0>;
+			qcom,num-grp-pins = <1>;
+			label = "pmx_nfc_int";
+
+			nfc_int_active: active {
+				drive-strength = <6>;
+				bias-pull-up;
+			};
+
+			nfc_int_suspend: suspend {
+				drive-strength = <6>;
+				bias-pull-up;
+			};
+		};
+
+		pmx_nfc_reset {
+			/*qcom,pins = <&gp 16>;*/
+			pins = "gpio16";
+			qcom,pin-func = <0>;
+			qcom,num-grp-pins = <1>;
+			label = "pmx_nfc_disable";
+
+			nfc_disable_active: active {
+				drive-strength = <6>;
+				bias-pull-up;
+			};
+
+			nfc_disable_suspend: suspend {
+				drive-strength = <6>;
+				bias-disable;
+			};
+		};
+
+		wcnss_pmux_5wire {
+			/* Active configuration of bus pins */
+			wcnss_default: wcnss_default {
+				wcss_wlan2 {
+					pins = "gpio76";
+					function = "wcss_wlan2";
+				};
+				wcss_wlan1 {
+					pins = "gpio77";
+					function = "wcss_wlan1";
+				};
+				wcss_wlan0 {
+					pins = "gpio78";
+					function = "wcss_wlan0";
+				};
+				wcss_wlan {
+					pins = "gpio79", "gpio80";
+					function = "wcss_wlan";
+				};
+
+				config {
+					pins = "gpio76", "gpio77",
+						"gpio78", "gpio79",
+						"gpio80";
+					drive-strength = <6>; /* 6 MA */
+					bias-pull-up; /* PULL UP */
+				};
+			};
+
+			wcnss_sleep: wcnss_sleep {
+				wcss_wlan2 {
+					pins = "gpio76";
+					function = "wcss_wlan2";
+				};
+				wcss_wlan1 {
+					pins = "gpio77";
+					function = "wcss_wlan1";
+				};
+				wcss_wlan0 {
+					pins = "gpio78";
+					function = "wcss_wlan0";
+				};
+				wcss_wlan {
+					pins = "gpio79", "gpio80";
+					function = "wcss_wlan";
+				};
+
+				config {
+					pins = "gpio76", "gpio77",
+						"gpio78", "gpio79",
+						"gpio80";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-down; /* PULL Down */
+				};
+			};
+		};
+
+		wcnss_pmux_gpio: wcnss_pmux_gpio {
+			wcnss_gpio_default: wcnss_gpio_default {
+				/* Active configuration of bus pins */
+				mux {
+					/* Uses general purpose pins */
+					pins = "gpio76", "gpio77",
+					"gpio78", "gpio79",
+					"gpio80";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio76", "gpio77",
+						"gpio78", "gpio79",
+						"gpio80";
+					drive-strength = <6>; /* 6 MA */
+					bias-pull-up; /* PULL UP */
+				};
+			};
+		};
+
+		wcd9xxx_intr {
+			wcd_intr_default: wcd_intr_default{
+				mux {
+					pins = "gpio73";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio73";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down; /* pull down */
+					input-enable;
+				};
+			};
+		};
+
+		cdc_reset_ctrl {
+			cdc_reset_sleep: cdc_reset_sleep {
+				mux {
+					pins = "gpio67";
+					function = "gpio";
+				};
+				config {
+					pins = "gpio67";
+					drive-strength = <16>;
+					bias-disable;
+					output-low;
+				};
+			};
+			cdc_reset_active:cdc_reset_active {
+				mux {
+					pins = "gpio67";
+					function = "gpio";
+				};
+				config {
+					pins = "gpio67";
+					drive-strength = <16>;
+					bias-pull-down;
+					output-high;
+				};
+			};
+		};
+
+		cdc_mclk2_pin {
+			cdc_mclk2_sleep: cdc_mclk2_sleep {
+				mux {
+					pins = "gpio66";
+					function = "pri_mi2s";
+				};
+				config {
+					pins = "gpio66";
+					drive-strength = <2>; /* 2 mA */
+					bias-pull-down;       /* PULL DOWN */
+				};
+			};
+			cdc_mclk2_active: cdc_mclk2_active {
+				mux {
+					pins = "gpio66";
+					function = "pri_mi2s";
+				};
+				config {
+					pins = "gpio66";
+					drive-strength = <8>; /* 8 mA */
+					bias-disable;         /* NO PULL */
+				};
+			};
+		};
+
+		cdc-pdm-2-lines {
+			cdc_pdm_lines_2_act: pdm_lines_2_on {
+				mux {
+					pins = "gpio70", "gpio71", "gpio72";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio70", "gpio71", "gpio72";
+					drive-strength = <8>;
+				};
+			};
+
+			cdc_pdm_lines_2_sus: pdm_lines_2_off {
+				mux {
+					pins = "gpio70", "gpio71", "gpio72";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio70", "gpio71", "gpio72";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		cdc-pdm-lines {
+			cdc_pdm_lines_act: pdm_lines_on {
+				mux {
+					pins = "gpio69", "gpio73", "gpio74";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio69", "gpio73", "gpio74";
+					drive-strength = <8>;
+				};
+			};
+			cdc_pdm_lines_sus: pdm_lines_off {
+				mux {
+					pins = "gpio69", "gpio73", "gpio74";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio69", "gpio73", "gpio74";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		cdc-pdm-comp-lines {
+			cdc_pdm_comp_lines_act: pdm_comp_lines_on {
+				mux {
+					pins = "gpio67", "gpio68";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio67", "gpio68";
+					drive-strength = <8>;
+				};
+			};
+
+			cdc_pdm_comp_lines_sus: pdm_comp_lines_off {
+				mux {
+					pins = "gpio67", "gpio68";
+					function = "cdc_pdm0";
+				};
+
+				config {
+					pins = "gpio67", "gpio68";
+					drive-strength = <2>;
+					bias-disable;
+				};
+			};
+		};
+
+		cross-conn-det {
+			cross_conn_det_act: lines_on {
+				mux {
+					pins = "gpio63";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio63";
+					drive-strength = <8>;
+					output-low;
+					bias-pull-down;
+				};
+			};
+
+			cross_conn_det_sus: lines_off {
+				mux {
+					pins = "gpio63";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio63";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		/* WSA VI sense */
+		wsa-vi {
+			wsa_vi_on: wsa_vi_on {
+				mux {
+					pins = "gpio94", "gpio95";
+					function = "wsa_io";
+				};
+
+				config {
+					pins = "gpio94", "gpio95";
+					drive-strength = <8>; /* 8 MA */
+					bias-disable; /* NO pull */
+				};
+			};
+
+			wsa_vi_off: wsa_vi_off {
+				mux {
+					pins = "gpio94", "gpio95";
+					function = "wsa_io";
+				};
+
+				config {
+					pins = "gpio94", "gpio95";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-down;
+				};
+			};
+		};
+
+		/* WSA Reset */
+		wsa_reset {
+			wsa_reset_on: wsa_reset_on {
+				mux {
+					pins = "gpio96";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio96";
+					drive-strength = <2>; /* 2 MA */
+					output-high;
+				};
+			};
+
+			wsa_reset_off: wsa_reset_off {
+				mux {
+					pins = "gpio96";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio96";
+					drive-strength = <2>; /* 2 MA */
+					output-low;
+				};
+			};
+		};
+
+		/* WSA CLK */
+		wsa_clk {
+			wsa_clk_on: wsa_clk_on {
+				mux {
+					pins = "gpio25";
+					function = "pri_mi2s_mclk_a";
+				};
+
+				config {
+					pins = "gpio25";
+					drive-strength = <8>; /* 8 MA */
+					output-high;
+				};
+			};
+
+			wsa_clk_off: wsa_clk_off {
+				mux {
+					pins = "gpio25";
+					function = "pri_mi2s_mclk_a";
+				};
+
+				config {
+					pins = "gpio25";
+					drive-strength = <2>; /* 2 MA */
+					output-low;
+					bias-pull-down;
+				};
+			};
+		};
+
+		pri-tlmm-lines {
+			pri_tlmm_lines_act: pri_tlmm_lines_act {
+				mux {
+					pins = "gpio91", "gpio93";
+					function = "pri_mi2s";
+				};
+
+				config {
+					pins = "gpio91", "gpio93";
+					drive-strength = <8>;
+				};
+			};
+
+			pri_tlmm_lines_sus: pri_tlmm_lines_sus {
+				mux {
+					pins = "gpio91", "gpio93";
+					function = "pri_mi2s";
+				};
+
+				config {
+					pins = "gpio91", "gpio93";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		pri-tlmm-ws-lines {
+			pri_tlmm_ws_act: pri_tlmm_ws_act {
+				mux {
+					pins = "gpio92";
+					function = "pri_mi2s_ws";
+				};
+
+				config {
+					pins = "gpio92";
+					drive-strength = <8>;
+				};
+			};
+
+			pri_tlmm_ws_sus: pri_tlmm_ws_sus {
+				mux {
+					pins = "gpio92";
+					function = "pri_mi2s_ws";
+				};
+
+				config {
+					pins = "gpio92";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		spi3 {
+			spi3_default: spi3_default {
+				/* active state */
+				mux {
+					/* MOSI, MISO, CLK */
+					pins = "gpio8", "gpio9", "gpio11";
+					function = "blsp_spi3";
+				};
+
+				config {
+					pins = "gpio8", "gpio9", "gpio11";
+					drive-strength = <12>; /* 12 MA */
+					bias-disable = <0>; /* No PULL */
+				};
+			};
+
+			spi3_sleep: spi3_sleep {
+				/* suspended state */
+				mux {
+					/* MOSI, MISO, CLK */
+					pins = "gpio8", "gpio9", "gpio11";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio8", "gpio9", "gpio11";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-down; /* PULL Down */
+				};
+			};
+
+			spi3_cs0_active: cs0_active {
+				/* CS */
+				mux {
+					pins = "gpio10";
+					function = "blsp_spi3";
+				};
+
+				config {
+					pins = "gpio10";
+					drive-strength = <2>;
+					bias-disable = <0>;
+				};
+			};
+
+			spi3_cs0_sleep: cs0_sleep {
+				/* CS */
+				mux {
+					pins = "gpio10";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio10";
+					drive-strength = <2>;
+					bias-disable = <0>;
+				};
+			};
+		};
+
+		/* add pingrp for touchscreen */
+		pmx_ts_int_active {
+			ts_int_active: ts_int_active {
+				mux {
+					pins = "gpio65";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio65";
+					drive-strength = <8>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		pmx_ts_int_suspend {
+			ts_int_suspend: ts_int_suspend {
+				mux {
+					pins = "gpio65";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio65";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		pmx_ts_reset_active {
+			ts_reset_active: ts_reset_active {
+				mux {
+					pins = "gpio64";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio64";
+					drive-strength = <8>;
+					bias-pull-up;
+				};
+			};
+		};
+
+		pmx_ts_reset_suspend {
+			ts_reset_suspend: ts_reset_suspend {
+				mux {
+					pins = "gpio64";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio64";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		pmx_ts_release {
+			ts_release: ts_release {
+				mux {
+					pins = "gpio65", "gpio64";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio65", "gpio64";
+					drive-strength = <2>;
+					bias-pull-down;
+				};
+			};
+		};
+
+		tlmm_gpio_key {
+			gpio_key_active: gpio_key_active {
+				mux {
+					pins = "gpio85", "gpio86", "gpio87";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio85", "gpio86", "gpio87";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+
+			gpio_key_suspend: gpio_key_suspend {
+				mux {
+					pins = "gpio85", "gpio86", "gpio87";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio85", "gpio86", "gpio87";
+					drive-strength = <2>;
+					bias-pull-up;
+				};
+			};
+		};
+		pmx_qdsd_clk {
+			qdsd_clk_sdcard: clk_sdcard {
+				config {
+					pins = "qdsd_clk";
+					bias-disable;/* NO pull */
+					drive-strength = <16>; /* 16 MA */
+				};
+			};
+			qdsd_clk_trace: clk_trace {
+				config {
+					pins = "qdsd_clk";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_clk_swdtrc: clk_swdtrc {
+				config {
+					pins = "qdsd_clk";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_clk_spmi: clk_spmi {
+				config {
+					pins = "qdsd_clk";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+		};
+
+		pmx_qdsd_cmd {
+			qdsd_cmd_sdcard: cmd_sdcard {
+				config {
+					pins = "qdsd_cmd";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_cmd_trace: cmd_trace {
+				config {
+					pins = "qdsd_cmd";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_cmd_swduart: cmd_uart {
+				config {
+					pins = "qdsd_cmd";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_cmd_swdtrc: cmd_swdtrc {
+				config {
+					pins = "qdsd_cmd";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_cmd_jtag: cmd_jtag {
+				config {
+					pins = "qdsd_cmd";
+					bias-disable; /* NO pull */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_cmd_spmi: cmd_spmi {
+				config {
+					pins = "qdsd_cmd";
+					bias-pull-down; /* pull down */
+					drive-strength = <10>; /* 10 MA */
+				};
+			};
+		};
+
+		pmx_qdsd_data0 {
+			qdsd_data0_sdcard: data0_sdcard {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data0_trace: data0_trace {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data0_swduart: data0_uart {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data0_swdtrc: data0_swdtrc {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data0_jtag: data0_jtag {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data0_spmi: data0_spmi {
+				config {
+					pins = "qdsd_data0";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+		};
+
+		pmx_qdsd_data1 {
+			qdsd_data1_sdcard: data1_sdcard {
+				config {
+					pins = "qdsd_data1";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data1_trace: data1_trace {
+				config {
+					pins = "qdsd_data1";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data1_swduart: data1_uart {
+				config {
+					pins = "qdsd_data1";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data1_swdtrc: data1_swdtrc {
+				config {
+					pins = "qdsd_data1";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data1_jtag: data1_jtag {
+				config {
+					pins = "qdsd_data1";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+		};
+
+		pmx_qdsd_data2 {
+			qdsd_data2_sdcard: data2_sdcard {
+				config {
+					pins = "qdsd_data2";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data2_trace: data2_trace {
+				config {
+					pins = "qdsd_data2";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data2_swduart: data2_uart {
+				config {
+					pins = "qdsd_data2";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data2_swdtrc: data2_swdtrc {
+				config {
+					pins = "qdsd_data2";
+					bias-pull-down; /* pull down */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data2_jtag: data2_jtag {
+				config {
+					pins = "qdsd_data2";
+					bias-pull-up; /* pull up */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+		};
+
+		pmx_qdsd_data3 {
+			qdsd_data3_sdcard: data3_sdcard {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data3_trace: data3_trace {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+			qdsd_data3_swduart: data3_uart {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data3_swdtrc: data3_swdtrc {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data3_jtag: data3_jtag {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-up; /* pull up */
+					drive-strength = <2>; /* 2 MA */
+				};
+			};
+			qdsd_data3_spmi: data3_spmi {
+				config {
+					pins = "qdsd_data3";
+					bias-pull-down; /* pull down */
+					drive-strength = <8>; /* 8 MA */
+				};
+			};
+		};
+
+		typec_ssmux_config: typec_ssmux_config {
+			mux {
+				pins = "gpio139";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio139";
+				drive-strength = <2>;
+				bias-disable;
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-cdp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-pmi8937-cdp.dts
index c06b806..a751d5d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-cdp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8937 CDP";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <1 0>;
+	qcom,pmic-id = <0x010016 0x020037 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8937-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-ext-codec-mtp.dts
new file mode 100644
index 0000000..13aba62
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-ext-codec-mtp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8937 Ext Codec MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 1>;
+	qcom,pmic-id = <0x010016 0x020037 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-pmi8937-mtp.dts
index c06b806..9d6be47 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8937-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8937 MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 0>;
+	qcom,pmic-id = <0x010016 0x020037 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-cdp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-pmi8940-cdp.dts
index c06b806..d2bb465 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-cdp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8940 CDP";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <1 0>;
+	qcom,pmic-id = <0x010016 0x020040 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pmi8940-ext-codec-mtp.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-ext-codec-mtp.dts
new file mode 100644
index 0000000..dbbb6b8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-ext-codec-mtp.dts
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8940 Ext Codec MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 1>;
+	qcom,pmic-id = <0x010016 0x020040 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-pmi8940-mtp.dts
index c06b806..0fb793b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-pmi8940-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8940 MTP";
+	compatible = "qcom,msm8953-mtp", "qcom,msm8953", "qcom,mtp";
+	qcom,board-id= <8 0>;
+	qcom,pmic-id = <0x010016 0x020040 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dts b/arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dts
new file mode 100644
index 0000000..5d892fd
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+#include "msm8953.dtsi"
+#include "msm8953-qrd-sku3.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 QRD SKU3";
+	compatible = "qcom,msm8953-qrd-sku3",
+		   "qcom,msm8953-qrd", "qcom,msm8953", "qcom,qrd";
+	qcom,board-id= <0x2000b 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
+};
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dtsi
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dtsi
index c06b806..96e185b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd-sku3.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,5 @@
  * GNU General Public License for more details.
  */
 
+#include "msm8953-qrd.dtsi"
 
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
index c06b806..243aaf5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-qrd.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,8 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&blsp1_uart0 {
+	status = "ok";
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart_console_active>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/msm8953-rcm.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/msm8953-rcm.dts
index c06b806..a3117ed 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/msm8953-rcm.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,15 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 RCM";
+	compatible = "qcom,msm8953-cdp", "qcom,msm8953", "qcom,cdp";
+	qcom,board-id= <21 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
new file mode 100644
index 0000000..e90c30b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -0,0 +1,649 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 "skeleton64.dtsi"
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+	model = "Qualcomm Technologies, Inc. MSM 8953";
+	compatible = "qcom,msm8953";
+	qcom,msm-id = <293 0x0>;
+	interrupt-parent = <&intc>;
+
+	chosen {
+		bootargs = "sched_enable_hmp=1 sched_enable_power_aware=1";
+	};
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		other_ext_mem: other_ext_region@0 {
+			compatible = "removed-dma-pool";
+			no-map;
+			reg = <0x0 0x85b00000 0x0 0xd00000>;
+		};
+
+		modem_mem: modem_region@0 {
+			compatible = "removed-dma-pool";
+			no-map-fixup;
+			reg = <0x0 0x86c00000 0x0 0x6a00000>;
+		};
+
+		adsp_fw_mem: adsp_fw_region@0 {
+			compatible = "removed-dma-pool";
+			no-map;
+			reg = <0x0 0x8d600000 0x0 0x1100000>;
+		};
+
+		wcnss_fw_mem: wcnss_fw_region@0 {
+			compatible = "removed-dma-pool";
+			no-map;
+			reg = <0x0 0x8e700000 0x0 0x700000>;
+		};
+
+		venus_mem: venus_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			alloc-ranges = <0x0 0x80000000 0x0 0x10000000>;
+			alignment = <0 0x400000>;
+			size = <0 0x0800000>;
+		};
+
+		secure_mem: secure_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			alignment = <0 0x400000>;
+			size = <0 0x09800000>;
+		};
+
+		qseecom_mem: qseecom_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			alignment = <0 0x400000>;
+			size = <0 0x1000000>;
+		};
+
+		adsp_mem: adsp_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0 0x400000>;
+		};
+
+		dfps_data_mem: dfps_data_mem@90000000 {
+		       reg = <0 0x90000000 0 0x1000>;
+		       label = "dfps_data_mem";
+		};
+
+		cont_splash_mem: splash_region@0x90001000 {
+			reg = <0x0 0x90001000 0x0 0x13ff000>;
+			label = "cont_splash_mem";
+		};
+
+		gpu_mem: gpu_region@0 {
+			compatible = "shared-dma-pool";
+			reusable;
+			alloc-ranges = <0x0 0x80000000 0x0 0x10000000>;
+			alignment = <0 0x400000>;
+			size = <0 0x800000>;
+		};
+	};
+
+	aliases {
+		/* smdtty devices */
+		sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
+		sdhc2 = &sdhc_2; /* SDC2 for SD card */
+	};
+
+	soc: soc { };
+
+};
+
+#include "msm8953-pinctrl.dtsi"
+#include "msm8953-cpu.dtsi"
+
+
+&soc {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	ranges = <0 0 0 0xffffffff>;
+	compatible = "simple-bus";
+
+	apc_apm: apm@b111000 {
+		compatible = "qcom,msm8953-apm";
+		reg = <0xb111000 0x1000>;
+		reg-names = "pm-apcc-glb";
+		qcom,apm-post-halt-delay = <0x2>;
+		qcom,apm-halt-clk-delay = <0x11>;
+		qcom,apm-resume-clk-delay = <0x10>;
+		qcom,apm-sel-switch-delay = <0x01>;
+	};
+
+	intc: interrupt-controller@b000000 {
+		compatible = "qcom,msm-qgic2";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		reg = <0x0b000000 0x1000>,
+		      <0x0b002000 0x1000>;
+	};
+
+	qcom,msm-gladiator@b1c0000 {
+		compatible = "qcom,msm-gladiator";
+		reg = <0x0b1c0000 0x4000>;
+		reg-names = "gladiator_base";
+		interrupts = <0 22 0>;
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <1 2 0xff08>,
+			     <1 3 0xff08>,
+			     <1 4 0xff08>,
+			     <1 1 0xff08>;
+		clock-frequency = <19200000>;
+	};
+
+	timer@b120000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+		compatible = "arm,armv7-timer-mem";
+		reg = <0xb120000 0x1000>;
+		clock-frequency = <19200000>;
+
+		frame@b121000 {
+			frame-number = <0>;
+			interrupts = <0 8 0x4>,
+				     <0 7 0x4>;
+			reg = <0xb121000 0x1000>,
+			      <0xb122000 0x1000>;
+		};
+
+		frame@b123000 {
+			frame-number = <1>;
+			interrupts = <0 9 0x4>;
+			reg = <0xb123000 0x1000>;
+			status = "disabled";
+		};
+
+		frame@b124000 {
+			frame-number = <2>;
+			interrupts = <0 10 0x4>;
+			reg = <0xb124000 0x1000>;
+			status = "disabled";
+		};
+
+		frame@b125000 {
+			frame-number = <3>;
+			interrupts = <0 11 0x4>;
+			reg = <0xb125000 0x1000>;
+			status = "disabled";
+		};
+
+		frame@b126000 {
+			frame-number = <4>;
+			interrupts = <0 12 0x4>;
+			reg = <0xb126000 0x1000>;
+			status = "disabled";
+		};
+
+		frame@b127000 {
+			frame-number = <5>;
+			interrupts = <0 13 0x4>;
+			reg = <0xb127000 0x1000>;
+			status = "disabled";
+		};
+
+		frame@b128000 {
+			frame-number = <6>;
+			interrupts = <0 14 0x4>;
+			reg = <0xb128000 0x1000>;
+			status = "disabled";
+		};
+	};
+	qcom,rmtfs_sharedmem@00000000 {
+		compatible = "qcom,sharedmem-uio";
+		reg = <0x00000000 0x00180000>;
+		reg-names = "rmtfs";
+		qcom,client-id = <0x00000001>;
+	};
+
+	restart@4ab000 {
+		compatible = "qcom,pshold";
+		reg = <0x4ab000 0x4>,
+			<0x193d100 0x4>;
+		reg-names = "pshold-base", "tcsr-boot-misc-detect";
+	};
+
+	qcom,mpm2-sleep-counter@4a3000 {
+		compatible = "qcom,mpm2-sleep-counter";
+		reg = <0x4a3000 0x1000>;
+		clock-frequency = <32768>;
+	};
+
+	cpu-pmu {
+		compatible = "arm,armv8-pmuv3";
+		interrupts = <1 7 0xff00>;
+	};
+
+	qcom,sps {
+		compatible = "qcom,msm_sps_4k";
+		qcom,pipe-attr-ee;
+	};
+
+	blsp1_uart0: serial@78af000 {
+		compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm";
+		reg = <0x78af000 0x200>;
+		interrupts = <0 107 0>;
+		status = "disabled";
+	};
+
+	dma_blsp1: qcom,sps-dma@7884000 { /* BLSP1 */
+		#dma-cells = <4>;
+		compatible = "qcom,sps-dma";
+		reg = <0x7884000 0x1f000>;
+		interrupts = <0 238 0>;
+		qcom,summing-threshold = <10>;
+	};
+
+	dma_blsp2: qcom,sps-dma@7ac4000 { /* BLSP2 */
+		#dma-cells = <4>;
+		compatible = "qcom,sps-dma";
+		reg = <0x7ac4000 0x1f000>;
+		interrupts = <0 239 0>;
+		qcom,summing-threshold = <10>;
+	};
+
+	slim_msm: slim@c140000{
+		cell-index = <1>;
+		compatible = "qcom,slim-ngd";
+		reg = <0xc140000 0x2c000>,
+		      <0xc104000 0x2a000>;
+		reg-names = "slimbus_physical", "slimbus_bam_physical";
+		interrupts = <0 163 0>, <0 180 0>;
+		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
+		qcom,apps-ch-pipes = <0x600000>;
+		qcom,ea-pc = <0x200>;
+		status = "disabled";
+	};
+
+	cpubw: qcom,cpubw {
+		compatible = "qcom,devbw";
+		governor = "cpufreq";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			<   769 /*  100.8 MHz */ >,
+			<  1611 /*  211.2 MHz */ >,     /*Low SVS*/
+			<  2124 /*  278.4 MHz */ >,
+			<  2929 /*  384   MHz */ >,
+			<  3221 /*  422.4 MHz */ >,	/* SVS */
+			<  4248 /*  556.8 MHz */ >,
+			<  5126 /*  672   MHz */ >,
+			<  5859 /*  768   MHz */ >,     /* SVS+  */
+			<  6152 /*  806.4 MHz */ >,
+			<  6445 /*  844.8 MHz */ >,     /* NOM   */
+			<  7104 /*  931.2 MHz */ >;     /* TURBO */
+	};
+
+	mincpubw: qcom,mincpubw {
+		compatible = "qcom,devbw";
+		governor = "cpufreq";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			<   769 /*  100.8 MHz */ >,
+			<  1611 /*  211.2 MHz */ >,     /*Low SVS*/
+			<  2124 /*  278.4 MHz */ >,
+			<  2929 /*  384   MHz */ >,
+			<  3221 /*  422.4 MHz */ >,	/* SVS */
+			<  4248 /*  556.8 MHz */ >,
+			<  5126 /*  672   MHz */ >,
+			<  5859 /*  768   MHz */ >,     /* SVS+  */
+			<  6152 /*  806.4 MHz */ >,
+			<  6445 /*  844.8 MHz */ >,     /* NOM   */
+			<  7104 /*  931.2 MHz */ >;     /* TURBO */
+	};
+
+	qcom,cpu-bwmon {
+		compatible = "qcom,bimc-bwmon2";
+		reg = <0x408000 0x300>, <0x401000 0x200>;
+		reg-names = "base", "global_base";
+		interrupts = <0 183 4>;
+		qcom,mport = <0>;
+		qcom,target-dev = <&cpubw>;
+	};
+
+	devfreq-cpufreq {
+		cpubw-cpufreq {
+		target-dev = <&cpubw>;
+		cpu-to-dev-map =
+			 <  652800  1611>,
+			 < 1036800  3221>,
+			 < 1401600  5859>,
+			 < 1689600  6445>,
+			 < 1804800  7104>,
+			 < 1958400  7104>,
+			 < 2208000  7104>;
+		};
+
+		mincpubw-cpufreq {
+			target-dev = <&mincpubw>;
+			cpu-to-dev-map =
+				<  652800 1611 >,
+				< 1401600 3221 >,
+				< 2208000 5859 >;
+		};
+	};
+
+	cpubw_compute: qcom,cpubw-compute {
+		compatible = "qcom,arm-cpu-mon";
+		qcom,cpulist = < &CPU0 &CPU1 &CPU2 &CPU3
+				&CPU4 &CPU5 &CPU6 &CPU7 >;
+		qcom,target-dev = <&cpubw>;
+		qcom,core-dev-table =
+				 <  652800  1611>,
+				 < 1036800  3221>,
+				 < 1401600  5859>,
+				 < 1689600  6445>,
+				 < 1804800  7104>,
+				 < 1958400  7104>,
+				 < 2208000  7104>;
+	};
+
+	mincpubw_compute: qcom,mincpubw-compute {
+		compatible = "qcom,arm-cpu-mon";
+		qcom,cpulist = < &CPU0 &CPU1 &CPU2 &CPU3
+				&CPU4 &CPU5 &CPU6 &CPU7 >;
+		qcom,target-dev = <&mincpubw>;
+		qcom,core-dev-table =
+				<  652800 1611 >,
+				< 1401600 3221 >,
+				< 2208000 5859 >;
+	};
+
+	qcom,ipc-spinlock@1905000 {
+		compatible = "qcom,ipc-spinlock-sfpb";
+		reg = <0x1905000 0x8000>;
+		qcom,num-locks = <8>;
+	};
+
+	qcom,smem@86300000 {
+		compatible = "qcom,smem";
+		reg = <0x86300000 0x100000>,
+			<0x0b011008 0x4>,
+			<0x60000 0x8000>,
+			<0x193d000 0x8>;
+		reg-names = "smem", "irq-reg-base",
+				"aux-mem1", "smem_targ_info_reg";
+		qcom,mpu-enabled;
+
+		qcom,smd-modem {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <0>;
+			qcom,smd-irq-offset = <0x0>;
+			qcom,smd-irq-bitmask = <0x1000>;
+			interrupts = <0 25 1>;
+			label = "modem";
+			qcom,not-loadable;
+		};
+
+		qcom,smsm-modem {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <0>;
+			qcom,smsm-irq-offset = <0x0>;
+			qcom,smsm-irq-bitmask = <0x2000>;
+			interrupts = <0 26 1>;
+		};
+
+		qcom,smd-wcnss {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <6>;
+			qcom,smd-irq-offset = <0x0>;
+			qcom,smd-irq-bitmask = <0x20000>;
+			interrupts = <0 142 1>;
+			label = "wcnss";
+		};
+
+		qcom,smsm-wcnss {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <6>;
+			qcom,smsm-irq-offset = <0x0>;
+			qcom,smsm-irq-bitmask = <0x80000>;
+			interrupts = <0 144 1>;
+		};
+
+		qcom,smd-adsp {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <1>;
+			qcom,smd-irq-offset = <0x0>;
+			qcom,smd-irq-bitmask = <0x100>;
+			interrupts = <0 289 1>;
+			label = "adsp";
+		};
+
+		qcom,smsm-adsp {
+			compatible = "qcom,smsm";
+			qcom,smsm-edge = <1>;
+			qcom,smsm-irq-offset = <0x0>;
+			qcom,smsm-irq-bitmask = <0x200>;
+			interrupts = <0 290 1>;
+		};
+
+		qcom,smd-rpm {
+			compatible = "qcom,smd";
+			qcom,smd-edge = <15>;
+			qcom,smd-irq-offset = <0x0>;
+			qcom,smd-irq-bitmask = <0x1>;
+			interrupts = <0 168 1>;
+			label = "rpm";
+			qcom,irq-no-suspend;
+			qcom,not-loadable;
+		};
+	};
+
+	qcom,wdt@b017000 {
+		compatible = "qcom,msm-watchdog";
+		reg = <0xb017000 0x1000>;
+		reg-names = "wdt-base";
+		interrupts = <0 3 0>, <0 4 0>;
+		qcom,bark-time = <11000>;
+		qcom,pet-time = <10000>;
+		qcom,ipi-ping;
+		qcom,wakeup-enable;
+	};
+
+	qcom,chd {
+		compatible = "qcom,core-hang-detect";
+		qcom,threshold-arr = <0xb1880b0 0xb1980b0 0xb1a80b0
+			0xb1b80b0 0xb0880b0 0xb0980b0 0xb0a80b0 0xb0b80b0>;
+		qcom,config-arr = <0xb1880b8 0xb1980b8 0xb1a80b8
+			0xb1b80b8 0xb0880b8 0xb0980b8 0xb0a80b8 0xb0b80b8>;
+	};
+
+	qcom,msm-rtb {
+		compatible = "qcom,msm-rtb";
+		qcom,rtb-size = <0x100000>;
+	};
+
+	qcom,msm-imem@8600000 {
+		compatible = "qcom,msm-imem";
+		reg = <0x08600000 0x1000>;
+		ranges = <0x0 0x08600000 0x1000>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		mem_dump_table@10 {
+			compatible = "qcom,msm-imem-mem_dump_table";
+			reg = <0x10 8>;
+		};
+
+		restart_reason@65c {
+			compatible = "qcom,msm-imem-restart_reason";
+			reg = <0x65c 4>;
+		};
+
+		boot_stats@6b0 {
+			compatible = "qcom,msm-imem-boot_stats";
+			reg = <0x6b0 32>;
+		};
+
+		 pil@94c {
+			compatible = "qcom,msm-imem-pil";
+			reg = <0x94c 200>;
+
+		};
+	};
+
+	qcom,memshare {
+		compatible = "qcom,memshare";
+
+		qcom,client_1 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x200000>;
+			qcom,client-id = <0>;
+			qcom,allocate-boot-time;
+			label = "modem";
+		};
+
+		qcom,client_2 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x300000>;
+			qcom,client-id = <2>;
+			label = "modem";
+		};
+
+		mem_client_3_size: qcom,client_3 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x0>;
+			qcom,client-id = <1>;
+			label = "modem";
+		};
+	};
+		sdcc1_ice: sdcc1ice@7803000 {
+		compatible = "qcom,ice";
+		reg = <0x7803000 0x8000>;
+		interrupt-names = "sdcc_ice_nonsec_level_irq",
+				  "sdcc_ice_sec_level_irq";
+		interrupts = <0 312 0>, <0 313 0>;
+		qcom,enable-ice-clk;
+		qcom,op-freq-hz = <270000000>, <0>, <0>, <0>;
+		qcom,msm-bus,name = "sdcc_ice_noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<78 512 0 0>,    /* No vote */
+			<78 512 1000 0>; /* Max. bandwidth */
+		qcom,bus-vector-names = "MIN", "MAX";
+		qcom,instance-type = "sdcc";
+	};
+
+	sdhc_1: sdhci@7824900 {
+		compatible = "qcom,sdhci-msm";
+		reg = <0x7824900 0x500>, <0x7824000 0x800>, <0x7824e00 0x200>;
+		reg-names = "hc_mem", "core_mem", "cmdq_mem";
+
+		interrupts = <0 123 0>, <0 138 0>;
+		interrupt-names = "hc_irq", "pwr_irq";
+
+		sdhc-msm-crypto = <&sdcc1_ice>;
+		qcom,bus-width = <8>;
+
+		qcom,devfreq,freq-table = <50000000 200000000>;
+
+		qcom,pm-qos-irq-type = "affine_irq";
+		qcom,pm-qos-irq-latency = <2 213>;
+
+		qcom,pm-qos-cpu-groups = <0x0f 0xf0>;
+		qcom,pm-qos-cmdq-latency-us = <2 213>, <2 213>;
+
+		qcom,pm-qos-legacy-latency-us = <2 213>, <2 213>;
+
+		qcom,msm-bus,name = "sdhc1";
+		qcom,msm-bus,num-cases = <9>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <78 512 0 0>, /* No vote */
+			<78 512 1046 3200>,    /* 400 KB/s*/
+			<78 512 52286 160000>, /* 20 MB/s */
+			<78 512 65360 200000>, /* 25 MB/s */
+			<78 512 130718 400000>, /* 50 MB/s */
+			<78 512 130718 400000>, /* 100 MB/s */
+			<78 512 261438 800000>, /* 200 MB/s */
+			<78 512 261438 800000>, /* 400 MB/s */
+			<78 512 1338562 4096000>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+			100000000 200000000 400000000 4294967295>;
+
+		qcom,ice-clk-rates = <270000000 160000000>;
+		qcom,large-address-bus;
+
+		status = "disabled";
+	};
+
+	sdhc_2: sdhci@7864900 {
+		compatible = "qcom,sdhci-msm";
+		reg = <0x7864900 0x500>, <0x7864000 0x800>;
+		reg-names = "hc_mem", "core_mem";
+
+		interrupts = <0 125 0>, <0 221 0>;
+		interrupt-names = "hc_irq", "pwr_irq";
+
+		qcom,bus-width = <4>;
+
+		qcom,pm-qos-irq-type = "affine_irq";
+		qcom,pm-qos-irq-latency = <2 213>;
+
+		qcom,pm-qos-cpu-groups = <0x0f 0xf0>;
+		qcom,pm-qos-legacy-latency-us = <2 213>, <2 213>;
+
+		qcom,devfreq,freq-table = <50000000 200000000>;
+
+		qcom,msm-bus,name = "sdhc2";
+		qcom,msm-bus,num-cases = <8>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps = <81 512 0 0>, /* No vote */
+			<81 512 1046 3200>,    /* 400 KB/s*/
+			<81 512 52286 160000>, /* 20 MB/s */
+			<81 512 65360 200000>, /* 25 MB/s */
+			<81 512 130718 400000>, /* 50 MB/s */
+			<81 512 261438 800000>, /* 100 MB/s */
+			<81 512 261438 800000>, /* 200 MB/s */
+			<81 512 1338562 4096000>; /* Max. bandwidth */
+		qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+			100000000 200000000 4294967295>;
+
+		qcom,large-address-bus;
+		status = "disabled";
+	};
+
+	spmi_bus: qcom,spmi@200f000 {
+		compatible = "qcom,spmi-pmic-arb";
+		reg = <0x200f000 0x1000>,
+			<0x2400000 0x800000>,
+			<0x2c00000 0x800000>,
+			<0x3800000 0x200000>,
+			<0x200a000 0x2100>;
+		reg-names = "core", "chnls", "obsrvr", "intr", "cnfg";
+		interrupt-names = "periph_irq";
+		interrupts = <GIC_SPI 190 IRQ_TYPE_NONE>;
+		qcom,ee = <0>;
+		qcom,channel = <0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		cell-index = <0>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/mtp8953-ipc.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/mtp8953-ipc.dts
index c06b806..481e576 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/mtp8953-ipc.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
+#include "msm8953-ipc.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. MSM8953 + PMI8950 IPC";
+	compatible = "qcom,msm8953-ipc", "qcom,msm8953", "qcom,ipc";
+	qcom,board-id= <12 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 8075b9e..502b2fe 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -12,9 +12,10 @@
 
 #include <dt-bindings/spmi/spmi.h>
 #include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/thermal/thermal.h>
 
 &spmi_bus {
-	qcom,pm660@0 {
+	pm660_0: qcom,pm660@0 {
 		compatible ="qcom,spmi-pmic";
 		reg = <0x0 SPMI_USID>;
 		#address-cells = <2>;
@@ -58,13 +59,14 @@
 			};
 		};
 
-		qcom,temp-alarm@2400 {
+		pm660_tz: qcom,temp-alarm@2400 {
 			compatible = "qcom,qpnp-temp-alarm";
 			reg = <0x2400 0x100>;
 			interrupts = <0x0 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
 			label = "pm660_tz";
 			qcom,channel-num = <6>;
 			qcom,temp_alarm-vadc = <&pm660_vadc>;
+			#thermal-sensor-cells = <0>;
 		};
 
 		pm660_gpios: pinctrl@c000 {
@@ -114,7 +116,6 @@
 			#size-cells = <0>;
 			interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
 			interrupt-names = "eoc-int-en-set";
-			qcom,adc-bit-resolution = <15>;
 			qcom,adc-vdd-reference = <1875>;
 
 			chan@6 {
@@ -245,149 +246,12 @@
 			};
 		};
 
-		pm660_charger: qcom,qpnp-smb2 {
-			compatible = "qcom,qpnp-smb2";
-			#address-cells = <1>;
-			#size-cells = <1>;
-
-			qcom,pmic-revid = <&pm660_revid>;
-
-			io-channels = <&pm660_rradc 8>,
-				      <&pm660_rradc 10>,
-				      <&pm660_rradc 3>,
-				      <&pm660_rradc 4>;
-			io-channel-names = "charger_temp",
-					   "charger_temp_max",
-					   "usbin_i",
-					   "usbin_v";
-
-			qcom,wipower-max-uw = <5000000>;
-
-			/* Enable after the qusb_phy0 device node is added */
-			/* dpdm-supply = <&qusb_phy0>; */
-
-			qcom,thermal-mitigation
-					= <3000000 2500000 2000000 1500000
-						1000000 500000>;
-
-			qcom,chgr@1000 {
-				reg = <0x1000 0x100>;
-				interrupts =
-					<0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "chg-error",
-						  "chg-state-change",
-						  "step-chg-state-change",
-						  "step-chg-soc-update-fail",
-						  "step-chg-soc-update-request";
-			};
-
-			qcom,otg@1100 {
-				reg = <0x1100 0x100>;
-				interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "otg-fail",
-						  "otg-overcurrent",
-						  "otg-oc-dis-sw-sts",
-						  "testmode-change-detect";
-			};
-
-			qcom,bat-if@1200 {
-				reg = <0x1200 0x100>;
-				interrupts =
-					<0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "bat-temp",
-						  "bat-ocp",
-						  "bat-ov",
-						  "bat-low",
-						  "bat-therm-or-id-missing",
-						  "bat-terminal-missing";
-			};
-
-			qcom,usb-chgpth@1300 {
-				reg = <0x1300 0x100>;
-				interrupts =
-					<0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "usbin-collapse",
-						  "usbin-lt-3p6v",
-						  "usbin-uv",
-						  "usbin-ov",
-						  "usbin-plugin",
-						  "usbin-src-change",
-						  "usbin-icl-change",
-						  "type-c-change";
-			};
-
-			qcom,dc-chgpth@1400 {
-				reg = <0x1400 0x100>;
-				interrupts =
-					<0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
-
-				interrupt-names = "dcin-collapse",
-						  "dcin-lt-3p6v",
-						  "dcin-uv",
-						  "dcin-ov",
-						  "dcin-plugin",
-						  "div2-en-dg",
-						  "dcin-icl-change";
-			};
-
-			qcom,chgr-misc@1600 {
-				reg = <0x1600 0x100>;
-				interrupts =
-					<0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
-					<0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
-					<0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
-					<0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
-
-				interrupt-names = "wdog-snarl",
-						  "wdog-bark",
-						  "aicl-fail",
-						  "aicl-done",
-						  "high-duty-cycle",
-						  "input-current-limiting",
-						  "temperature-change",
-						  "switcher-power-ok";
-			};
-		};
-
 		pm660_pdphy: qcom,usb-pdphy@1700 {
 			compatible = "qcom,qpnp-pdphy";
 			reg = <0x1700 0x100>;
 			vdd-pdphy-supply = <&pm660l_l7>;
-			vbus-supply = <&smb2_vbus>;
-			vconn-supply = <&smb2_vconn>;
+			vbus-supply = <0>;
+			vconn-supply = <0>;
 			interrupts = <0x0 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
 				     <0x0 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
 				     <0x0 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
@@ -415,7 +279,6 @@
 			#size-cells = <0>;
 			interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
 			interrupt-names = "eoc-int-en-set";
-			qcom,adc-bit-resolution = <15>;
 			qcom,adc-vdd-reference = <1875>;
 			qcom,adc_tm-vadc = <&pm660_vadc>;
 			qcom,decimation = <0>;
@@ -473,7 +336,7 @@
 				qcom,scale-function = <2>;
 				qcom,hw-settle-time = <2>;
 				qcom,btm-channel-number = <0x80>;
-				qcom,vadc-thermal-node;
+				qcom,thermal-node;
 			};
 
 			chan@4f {
@@ -484,127 +347,45 @@
 				qcom,scale-function = <2>;
 				qcom,hw-settle-time = <2>;
 				qcom,btm-channel-number = <0x88>;
-				qcom,vadc-thermal-node;
+				qcom,thermal-node;
 			};
 		};
 
-		pm660_rradc: rradc@4500 {
-			compatible = "qcom,rradc";
-			reg = <0x4500 0x100>;
-			#address-cells = <1>;
-			#size-cells = <0>;
-			#io-channel-cells = <1>;
-			qcom,pmic-revid = <&pm660_revid>;
-		};
-
-		pm660_fg: qpnp,fg {
-			compatible = "qcom,fg-gen3";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			qcom,pmic-revid = <&pm660_revid>;
-			io-channels = <&pm660_rradc 0>,
-				      <&pm660_rradc 7>;
-			io-channel-names = "rradc_batt_id",
-					   "rradc_die_temp";
-			qcom,rradc-base = <0x4500>;
-			qcom,fg-esr-timer-awake = <96 96>;
-			qcom,fg-esr-timer-asleep = <256 256>;
-			qcom,fg-esr-timer-charging = <0 96>;
-			qcom,cycle-counter-en;
-			status = "okay";
-
-			qcom,fg-batt-soc@4000 {
-				status = "okay";
-				reg = <0x4000 0x100>;
-				interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x2
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x3
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x5
-							IRQ_TYPE_EDGE_RISING>,
-					     <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "soc-update",
-						  "soc-ready",
-						  "bsoc-delta",
-						  "msoc-delta",
-						  "msoc-low",
-						  "msoc-empty",
-						  "msoc-high",
-						  "msoc-full";
-			};
-
-			qcom,fg-batt-info@4100 {
-				status = "okay";
-				reg = <0x4100 0x100>;
-				interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "vbatt-pred-delta",
-						  "vbatt-low",
-						  "esr-delta",
-						  "batt-missing",
-						  "batt-temp-delta";
-			};
-
-			qcom,fg-memif@4400 {
-				status = "okay";
-				reg = <0x4400 0x100>;
-				interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
-				interrupt-names = "ima-rdy",
-						  "mem-xcp",
-						  "dma-grant";
-			};
-		};
-
-		bcl@4200 {
+		bcl_sensor: bcl@4200 {
 			compatible = "qcom,msm-bcl-lmh";
 			reg = <0x4200 0xff>,
 				<0x4300 0xff>;
 			reg-names = "fg_user_adc",
 					"fg_lmh";
 			interrupts = <0x0 0x42 0x0 IRQ_TYPE_NONE>,
-					<0x0 0x42 0x2 IRQ_TYPE_NONE>;
-			interrupt-names = "bcl-high-ibat-int",
-					"bcl-low-vbat-int";
-			qcom,vbat-polling-delay-ms = <100>;
-			qcom,ibat-polling-delay-ms = <100>;
+					<0x0 0x42 0x1 IRQ_TYPE_NONE>,
+					<0x0 0x42 0x2 IRQ_TYPE_NONE>,
+					<0x0 0x42 0x3 IRQ_TYPE_NONE>,
+					<0x0 0x42 0x4 IRQ_TYPE_NONE>;
+			interrupt-names = "bcl-high-ibat",
+						"bcl-very-high-ibat",
+						"bcl-low-vbat",
+						"bcl-very-low-vbat",
+						"bcl-crit-low-vbat";
+			#thermal-sensor-cells = <1>;
+		};
+
+		pm660_div_clk: qcom,clkdiv@5b00 {
+			compatible = "qcom,qpnp-clkdiv";
+			reg = <0x5b00 0x100>;
+			#clock-cells = <1>;
+			qcom,cxo-freq = <19200000>;
+			qcom,clkdiv-id = <1>;
+			qcom,clkdiv-init-freq = <19200000>;
+			status = "disabled";
 		};
 	};
 
-	qcom,pm660@1 {
+	pm660_1: qcom,pm660@1 {
 		compatible ="qcom,spmi-pmic";
 		reg = <0x1 SPMI_USID>;
 		#address-cells = <2>;
 		#size-cells = <0>;
-
-		pm660_haptics: qcom,haptics@c000 {
-			compatible = "qcom,qpnp-haptics";
-			reg = <0xc000 0x100>;
-			interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
-				     <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
-			interrupt-names = "hap-sc-irq", "hap-play-irq";
-			qcom,pmic-revid = <&pm660_revid>;
-			qcom,pmic-misc = <&pm660_misc>;
-			qcom,misc-clk-trim-error-reg = <0xf3>;
-			qcom,actuator-type = <0>;
-			qcom,play-mode = "direct";
-			qcom,vmax-mv = <3200>;
-			qcom,ilim-ma = <800>;
-			qcom,sc-dbc-cycles = <8>;
-			qcom,wave-play-rate-us = <6667>;
-			qcom,en-brake;
-			qcom,lra-high-z = "opt0";
-			qcom,lra-auto-res-mode = "qwd";
-			qcom,lra-res-cal-period = <4>;
-		};
 	};
 };
 
@@ -683,4 +464,243 @@
 			};
 		};
 	};
+
+	ibat-high {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "step_wise";
+		thermal-sensors = <&bcl_sensor 0>;
+
+		trips {
+			ibat-high {
+				temperature = <4200>;
+				hysteresis = <200>;
+				type = "passive";
+			};
+		};
+	};
+
+	ibat-vhigh {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "step_wise";
+		thermal-sensors = <&bcl_sensor 1>;
+
+		trips {
+			ibat-vhigh {
+				temperature = <4300>;
+				hysteresis = <100>;
+				type = "passive";
+			};
+		};
+	};
+
+	vbat_adc {
+		polling-delay-passive = <100>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_cap";
+		thermal-sensors = <&bcl_sensor 2>;
+		tracks-low;
+
+		trips {
+			pm660_vbat_adc: vbat-adc {
+				temperature = <3300>;
+				hysteresis = <100>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			vbat_map6 {
+				trip = <&pm660_vbat_adc>;
+				cooling-device =
+					<&CPU6 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			vbat_map7 {
+				trip = <&pm660_vbat_adc>;
+				cooling-device =
+					<&CPU7 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+		};
+	};
+
+	vbat_low {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_cap";
+		thermal-sensors = <&bcl_sensor 3>;
+		tracks-low;
+
+		trips {
+			vbat-low {
+				temperature = <3100>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+	};
+
+	vbat_too_low {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_cap";
+		thermal-sensors = <&bcl_sensor 4>;
+		tracks-low;
+
+		trips {
+			vbat-too-low {
+				temperature = <2900>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+	};
+
+	soc {
+		polling-delay-passive = <100>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_cap";
+		thermal-sensors = <&bcl_sensor 5>;
+		tracks-low;
+
+		trips {
+			pm660_low_soc: low-soc {
+				temperature = <10>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			soc_map6 {
+				trip = <&pm660_low_soc>;
+				cooling-device =
+					<&CPU6 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			soc_map7 {
+				trip = <&pm660_low_soc>;
+				cooling-device =
+					<&CPU7 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+		};
+	};
+
+	pm660_temp_alarm: pm660_tz {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "step_wise";
+		thermal-sensors = <&pm660_tz>;
+
+		trips {
+			pm660_trip0: pm660-trip0 {
+				temperature = <105000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+			pm660_trip1: pm660-trip1 {
+				temperature = <125000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+			pm660_trip2: pm660-trip2 {
+				temperature = <145000>;
+				hysteresis = <0>;
+				type = "critical";
+			};
+		};
+		cooling-maps {
+			trip0_cpu0 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU0 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu1 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU1 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu2 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU2 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu3 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU3 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu4 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU4 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu5 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU5 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu6 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU6 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip0_cpu7 {
+				trip = <&pm660_trip0>;
+				cooling-device =
+					<&CPU7 (THERMAL_MAX_LIMIT-1)
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			trip1_cpu1 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU1 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu2 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU2 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu3 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU3 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu4 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU4 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu5 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU5 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu6 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU6 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+			trip1_cpu7 {
+				trip = <&pm660_trip1>;
+				cooling-device =
+					<&CPU7 THERMAL_MAX_LIMIT
+						THERMAL_MAX_LIMIT>;
+			};
+		};
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
index 11101ffe..bcb5138 100644
--- a/arch/arm64/boot/dts/qcom/pm660l.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -39,34 +39,31 @@
 				<PON_POWER_OFF_SHUTDOWN>;
 		};
 
-		qcom,temp-alarm@2400 {
+		pm660l_tz: qcom,temp-alarm@2400 {
 			compatible = "qcom,qpnp-temp-alarm";
 			reg = <0x2400 0x100>;
 			interrupts = <0x2 0x24 0x0 IRQ_TYPE_EDGE_RISING>;
 			label = "pm660l_tz";
+			#thermal-sensor-cells = <0>;
 		};
 
 		pm660l_gpios: pinctrl@c000 {
 			compatible = "qcom,spmi-gpio";
 			reg = <0xc000 0xc00>;
-			interrupts = <0x2 0xc0 0 IRQ_TYPE_NONE>,
-					<0x2 0xc2 0 IRQ_TYPE_NONE>,
+			interrupts = <0x2 0xc2 0 IRQ_TYPE_NONE>,
 					<0x2 0xc3 0 IRQ_TYPE_NONE>,
 					<0x2 0xc4 0 IRQ_TYPE_NONE>,
 					<0x2 0xc5 0 IRQ_TYPE_NONE>,
 					<0x2 0xc6 0 IRQ_TYPE_NONE>,
 					<0x2 0xc7 0 IRQ_TYPE_NONE>,
-					<0x2 0xc8 0 IRQ_TYPE_NONE>,
-					<0x2 0xca 0 IRQ_TYPE_NONE>,
-					<0x2 0xcb 0 IRQ_TYPE_NONE>;
-			interrupt-names = "pm660l_gpio1", "pm660l_gpio3",
-					"pm660l_gpio4", "pm660l_gpio5",
-					"pm660l_gpio6", "pm660l_gpio7",
-					"pm660l_gpio8", "pm660l_gpio9",
-					"pm660l_gpio11", "pm660l_gpio12";
+					<0x2 0xc8 0 IRQ_TYPE_NONE>;
+			interrupt-names = "pm660l_gpio3", "pm660l_gpio4",
+					"pm660l_gpio5", "pm660l_gpio6",
+					"pm660l_gpio7", "pm660l_gpio8",
+					"pm660l_gpio9";
 			gpio-controller;
 			#gpio-cells = <2>;
-			qcom,gpios-disallowed = <2 10>;
+			qcom,gpios-disallowed = <1 2 10 11 12>;
 		};
 	};
 
@@ -185,35 +182,6 @@
 			};
 		};
 
-		pm660l_wled: qcom,leds@d800 {
-			compatible = "qcom,qpnp-wled";
-			reg = <0xd800 0x100>,
-				<0xd900 0x100>;
-			reg-names = "qpnp-wled-ctrl-base",
-					"qpnp-wled-sink-base";
-			interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "ovp-irq";
-			linux,name = "wled";
-			linux,default-trigger = "bkl-trigger";
-			qcom,fdbk-output = "auto";
-			qcom,vref-uv = <127500>;
-			qcom,switch-freq-khz = <800>;
-			qcom,ovp-mv = <29600>;
-			qcom,ilim-ma = <970>;
-			qcom,boost-duty-ns = <26>;
-			qcom,mod-freq-khz = <9600>;
-			qcom,dim-mode = "hybrid";
-			qcom,hyb-thres = <625>;
-			qcom,sync-dly-us = <800>;
-			qcom,fs-curr-ua = <25000>;
-			qcom,cons-sync-write-delay-us = <1000>;
-			qcom,led-strings-list = [00 01 02];
-			qcom,loop-auto-gm-en;
-			qcom,pmic-revid = <&pm660l_revid>;
-			qcom,auto-calibration-enable;
-			status = "ok";
-		};
-
 		flash_led: qcom,leds@d300 {
 			compatible = "qcom,qpnp-flash-led-v2";
 			reg = <0xd300 0x100>;
@@ -322,88 +290,30 @@
 				qcom,default-led-trigger = "switch1_trigger";
 			};
 		};
+	};
+};
 
-		pm660l_lcdb: qpnp-lcdb@ec00 {
-			compatible = "qcom,qpnp-lcdb-regulator";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			reg = <0xec00 0x100>;
-			interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
-			interrupt-names = "sc-irq";
+&thermal_zones {
+	pm660l_tz {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&pm660l_tz>;
 
-			qcom,pmic-revid = <&pm660l_revid>;
-
-			lcdb_ldo_vreg: ldo {
-				label = "ldo";
-				regulator-name = "lcdb_ldo";
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6000000>;
+		trips {
+			pm660l_trip0: pm660l-trip0 {
+				temperature = <105000>;
+				hysteresis = <0>;
+				type = "passive";
 			};
-
-			lcdb_ncp_vreg: ncp {
-				label = "ncp";
-				regulator-name = "lcdb_ncp";
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6000000>;
+			 pm660l_trip1: pm660l-trip1 {
+				temperature = <125000>;
+				hysteresis = <0>;
+				type = "passive";
 			};
-		};
-
-		pm660a_oledb: qpnp-oledb@e000 {
-		       compatible = "qcom,qpnp-oledb-regulator";
-		       #address-cells = <1>;
-		       #size-cells = <1>;
-		       qcom,pmic-revid = <&pm660l_revid>;
-		       reg = <0xe000 0x100>;
-		       qcom,pbs-client = <&pm660l_pbs>;
-
-		       label = "oledb";
-		       regulator-name = "regulator-oledb";
-		       regulator-min-microvolt = <5000000>;
-		       regulator-max-microvolt = <8100000>;
-
-		       qcom,swire-control;
-		       qcom,ext-pin-control;
-		       status = "disabled";
-		};
-
-		pm660a_labibb: qpnp-labibb-regulator {
-			compatible = "qcom,qpnp-labibb-regulator";
-			#address-cells = <1>;
-			#size-cells = <1>;
-			qcom,pmic-revid = <&pm660l_revid>;
-			qcom,swire-control;
-			status = "disabled";
-
-			ibb_regulator: qcom,ibb@dc00 {
-				reg = <0xdc00 0x100>;
-				reg-names = "ibb_reg";
-				regulator-name = "ibb_reg";
-
-				regulator-min-microvolt = <4000000>;
-				regulator-max-microvolt = <6300000>;
-
-				qcom,qpnp-ibb-min-voltage = <1400000>;
-				qcom,qpnp-ibb-step-size = <100000>;
-				qcom,qpnp-ibb-slew-rate = <2000000>;
-				qcom,qpnp-ibb-init-voltage = <4000000>;
-				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
-			};
-
-			lab_regulator: qcom,lab@de00 {
-				reg = <0xde00 0x100>;
-				reg-names = "lab";
-				regulator-name = "lab_reg";
-
-				regulator-min-microvolt = <4600000>;
-				regulator-max-microvolt = <6100000>;
-
-				qcom,qpnp-lab-min-voltage = <4600000>;
-				qcom,qpnp-lab-step-size = <100000>;
-				qcom,qpnp-lab-slew-rate = <5000>;
-				qcom,qpnp-lab-init-voltage = <4600000>;
-				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
-
-				qcom,notify-lab-vreg-ok-sts;
+			 pm660l_trip2: pm660l-trip2 {
+				temperature = <145000>;
+				hysteresis = <0>;
+				type = "critical";
 			};
 		};
 	};
diff --git a/arch/arm64/boot/dts/qcom/pm8950.dtsi b/arch/arm64/boot/dts/qcom/pm8950.dtsi
new file mode 100644
index 0000000..f47872a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pm8950.dtsi
@@ -0,0 +1,388 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+&spmi_bus {
+	qcom,pm8950@0 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x0 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm8950_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		pm8950_temp_alarm: qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0>;
+			label = "pm8950_tz";
+			qcom,channel-num = <8>;
+			qcom,threshold-set = <0>;
+			qcom,temp_alarm-vadc = <&pm8950_vadc>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			interrupts = <0x0 0x8 0x0>,
+				<0x0 0x8 0x1>,
+				<0x0 0x8 0x4>,
+				<0x0 0x8 0x5>;
+			interrupt-names = "kpdpwr", "resin",
+				"resin-bark", "kpdpwr-resin-bark";
+			qcom,pon-dbc-delay = <15625>;
+			qcom,system-reset;
+
+			qcom,pon_1 {
+				qcom,pon-type = <0>;
+				qcom,pull-up = <1>;
+				linux,code = <116>;
+			};
+
+			qcom,pon_2 {
+				qcom,pon-type = <1>;
+				qcom,pull-up = <1>;
+				linux,code = <114>;
+			};
+		};
+
+		pm8950_coincell: qcom,coincell@2800 {
+			compatible = "qcom,qpnp-coincell";
+			reg = <0x2800 0x100>;
+		};
+
+		pm8950_mpps: mpps {
+			compatible = "qcom,qpnp-pin";
+			spmi-dev-container;
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8950-mpp";
+
+			mpp@a000 {
+				reg = <0xa000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			mpp@a100 {
+				/* MPP2 - PA_THERM config */
+				reg = <0xa100 0x100>;
+				qcom,pin-num = <2>;
+				qcom,mode = <4>; /* AIN input */
+				qcom,invert = <1>; /* Enable MPP */
+				qcom,ain-route = <1>; /* AMUX 6 */
+				qcom,master-en = <1>;
+				qcom,src-sel = <0>; /* Function constant */
+			};
+
+			mpp@a200 {
+				reg = <0xa200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			mpp@a300 {
+				/* MPP4 - CASE_THERM config */
+				reg = <0xa300 0x100>;
+				qcom,pin-num = <4>;
+				qcom,mode = <4>; /* AIN input */
+				qcom,invert = <1>; /* Enable MPP */
+				qcom,ain-route = <3>; /* AMUX 8 */
+				qcom,master-en = <1>;
+				qcom,src-sel = <0>; /* Function constant */
+			};
+		};
+
+		pm8950_gpios: gpios {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pm8950-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			gpio@c200 {
+				reg = <0xc200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			gpio@c300 {
+				reg = <0xc300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+
+			gpio@c400 {
+				reg = <0xc400 0x100>;
+				qcom,pin-num = <5>;
+				status = "disabled";
+			};
+
+			gpio@c500 {
+				reg = <0xc500 0x100>;
+				qcom,pin-num = <6>;
+				status = "disabled";
+			};
+
+			gpio@c600 {
+				reg = <0xc600 0x100>;
+				qcom,pin-num = <7>;
+				status = "disabled";
+			};
+
+			gpio@c700 {
+				reg = <0xc700 0x100>;
+				qcom,pin-num = <8>;
+				status = "disabled";
+			};
+		};
+
+		pm8950_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,vadc-poll-eoc;
+			qcom,pmic-revid = <&pm8950_revid>;
+
+			chan@5 {
+				label = "vcoin";
+				reg = <5>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@7 {
+				label = "vph_pwr";
+				reg = <7>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@8 {
+				label = "die_temp";
+				reg = <8>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@9 {
+				label = "ref_625mv";
+				reg = <9>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@a {
+				label = "ref_1250v";
+				reg = <0xa>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@c {
+				label = "ref_buf_625mv";
+				reg = <0xc>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@36 {
+				label = "pa_therm0";
+				reg = <0x36>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@11 {
+				label = "pa_therm1";
+				reg = <0x11>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@32 {
+				label = "xo_therm";
+				reg = <0x32>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@3c {
+				label = "xo_therm_buf";
+				reg = <0x3c>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <4>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@13 {
+				label = "case_therm";
+				reg = <0x13>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+		};
+
+		pm8950_adc_tm: vadc@3400 {
+			compatible = "qcom,qpnp-adc-tm";
+			reg = <0x3400 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts =	<0x0 0x34 0x0>,
+					<0x0 0x34 0x3>,
+					<0x0 0x34 0x4>;
+			interrupt-names =	"eoc-int-en-set",
+						"high-thr-en-set",
+						"low-thr-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,adc_tm-vadc = <&pm8950_vadc>;
+			qcom,pmic-revid = <&pm8950_revid>;
+
+			chan@36 {
+				label = "pa_therm0";
+				reg = <0x36>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "ratiometric";
+				qcom,scale-function = <2>;
+				qcom,hw-settle-time = <2>;
+				qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x48>;
+				qcom,thermal-node;
+			};
+
+			chan@7 {
+				label = "vph_pwr";
+				reg = <0x7>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x68>;
+			};
+		};
+
+		pm8950_rtc: qcom,pm8950_rtc {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-rtc";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-rtc-write = <0>;
+			qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+			qcom,pm8950_rtc_rw@6000 {
+				reg = <0x6000 0x100>;
+			};
+
+			qcom,pm8950_rtc_alarm@6100 {
+				reg = <0x6100 0x100>;
+				interrupts = <0x0 0x61 0x1>;
+			};
+		};
+
+		qcom,leds@a300 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xa300 0x100>;
+			label = "mpp";
+		};
+	};
+
+	pm8950_1: qcom,pm8950@1 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x1 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pm8950_pwm: pwm@bc00 {
+			status = "disabled";
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xbc00 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <0>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/pm8998.dtsi b/arch/arm64/boot/dts/qcom/pm8998.dtsi
index 450295e..dc3ffda 100644
--- a/arch/arm64/boot/dts/qcom/pm8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm8998.dtsi
@@ -138,7 +138,6 @@
 			#size-cells = <0>;
 			interrupts = <0x0 0x31 0x0 IRQ_TYPE_EDGE_RISING>;
 			interrupt-names = "eoc-int-en-set";
-			qcom,adc-bit-resolution = <15>;
 			qcom,adc-vdd-reference = <1875>;
 
 			chan@6 {
@@ -185,13 +184,39 @@
 			#size-cells = <0>;
 			interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
 			interrupt-names = "eoc-int-en-set";
-			qcom,adc-bit-resolution = <15>;
 			qcom,adc-vdd-reference = <1875>;
 			qcom,adc_tm-vadc = <&pm8998_vadc>;
 			qcom,decimation = <0>;
 			qcom,fast-avg-setup = <0>;
 			#thermal-sensor-cells = <1>;
 		};
+
+		pm8998_div_clk1: qcom,clkdiv@5b00 {
+			compatible = "qcom,qpnp-clkdiv";
+			reg = <0x5b00 0x100>;
+			#clock-cells = <1>;
+			qcom,cxo-freq = <19200000>;
+			qcom,clkdiv-id = <1>;
+			qcom,clkdiv-init-freq = <19200000>;
+		};
+
+		pm8998_div_clk2: qcom,clkdiv@5c00 {
+			compatible = "qcom,qpnp-clkdiv";
+			reg = <0x5c00 0x100>;
+			#clock-cells = <1>;
+			qcom,cxo-freq = <19200000>;
+			qcom,clkdiv-id = <2>;
+			qcom,clkdiv-init-freq = <19200000>;
+		};
+
+		pm8998_div_clk3: qcom,clkdiv@5d00 {
+			compatible = "qcom,qpnp-clkdiv";
+			reg = <0x5d00 0x100>;
+			#clock-cells = <1>;
+			qcom,cxo-freq = <19200000>;
+			qcom,clkdiv-id = <3>;
+			qcom,clkdiv-init-freq = <19200000>;
+		};
 	};
 
 	qcom,pm8998@1 {
diff --git a/arch/arm64/boot/dts/qcom/pmi8950.dtsi b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
new file mode 100644
index 0000000..0ec1f0b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/pmi8950.dtsi
@@ -0,0 +1,641 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <dt-bindings/msm/power-on.h>
+
+&spmi_bus {
+	qcom,pmi8950@2 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x2 SPMI_USID>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		pmi8950_revid: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		qcom,power-on@800 {
+			compatible = "qcom,qpnp-power-on";
+			reg = <0x800 0x100>;
+			qcom,secondary-pon-reset;
+			qcom,hard-reset-poweroff-type =
+				<PON_POWER_OFF_SHUTDOWN>;
+
+			pon_perph_reg: qcom,pon_perph_reg {
+				regulator-name = "pon_spare_reg";
+				qcom,pon-spare-reg-addr = <0x8c>;
+				qcom,pon-spare-reg-bit = <1>;
+			};
+		};
+
+		pmi8950_vadc: vadc@3100 {
+			compatible = "qcom,qpnp-vadc";
+			reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x2 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1800>;
+			qcom,vadc-poll-eoc;
+
+			chan@0 {
+				label = "usbin";
+				reg = <0>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <4>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@1 {
+				label = "dcin";
+				reg = <1>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <4>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@3 {
+				label = "vchg_sns";
+				reg = <3>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@9 {
+				label = "ref_625mv";
+				reg = <9>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@a {
+				label = "ref_1250v";
+				reg = <0xa>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@d {
+				label = "chg_temp";
+				reg = <0xd>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <0>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <16>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,vadc-thermal-node;
+			};
+
+			chan@43 {
+				label = "usb_dp";
+				reg = <0x43>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+
+			chan@44 {
+				label = "usb_dm";
+				reg = <0x44>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <0>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+			};
+		};
+
+		pmi8950_gpios: gpios {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pmi8950-gpio";
+
+			gpio@c000 {
+				reg = <0xc000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			gpio@c100 {
+				reg = <0xc100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+		};
+
+		pmi8950_mpps: mpps {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-pin";
+			gpio-controller;
+			#gpio-cells = <2>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+			label = "pmi8950-mpp";
+
+			mpp@a000 {
+				reg = <0xa000 0x100>;
+				qcom,pin-num = <1>;
+				status = "disabled";
+			};
+
+			mpp@a100 {
+				reg = <0xa100 0x100>;
+				qcom,pin-num = <2>;
+				status = "disabled";
+			};
+
+			mpp@a200 {
+				reg = <0xa200 0x100>;
+				qcom,pin-num = <3>;
+				status = "disabled";
+			};
+
+			mpp@a300 {
+				reg = <0xa300 0x100>;
+				qcom,pin-num = <4>;
+				status = "disabled";
+			};
+		};
+
+		pmi8950_charger: qcom,qpnp-smbcharger {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-smbcharger";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			qcom,iterm-ma = <100>;
+			qcom,float-voltage-mv = <4200>;
+			qcom,resume-delta-mv = <200>;
+			qcom,chg-inhibit-fg;
+			qcom,rparasitic-uohm = <100000>;
+			qcom,bms-psy-name = "bms";
+			qcom,thermal-mitigation = <1500 700 600 0>;
+			qcom,parallel-usb-min-current-ma = <1400>;
+			qcom,parallel-usb-9v-min-current-ma = <900>;
+			qcom,parallel-allowed-lowering-ma = <500>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+			qcom,force-aicl-rerun;
+			qcom,aicl-rerun-period-s = <180>;
+			qcom,autoadjust-vfloat;
+
+			qcom,chgr@1000 {
+				reg = <0x1000 0x100>;
+				interrupts =	<0x2 0x10 0x0>,
+						<0x2 0x10 0x1>,
+						<0x2 0x10 0x2>,
+						<0x2 0x10 0x3>,
+						<0x2 0x10 0x4>,
+						<0x2 0x10 0x5>,
+						<0x2 0x10 0x6>,
+						<0x2 0x10 0x7>;
+
+				interrupt-names =	"chg-error",
+							"chg-inhibit",
+							"chg-prechg-sft",
+							"chg-complete-chg-sft",
+							"chg-p2f-thr",
+							"chg-rechg-thr",
+							"chg-taper-thr",
+							"chg-tcc-thr";
+			};
+
+			qcom,otg@1100 {
+				reg = <0x1100 0x100>;
+				interrupts =	<0x2 0x11 0x0>,
+						<0x2 0x11 0x1>,
+						<0x2 0x11 0x3>;
+				interrupt-names =	"otg-fail",
+							"otg-oc",
+						"usbid-change";
+			};
+
+			qcom,bat-if@1200 {
+				reg = <0x1200 0x100>;
+				interrupts =	<0x2 0x12 0x0>,
+						<0x2 0x12 0x1>,
+						<0x2 0x12 0x2>,
+						<0x2 0x12 0x3>,
+					<0x2 0x12 0x4>,
+						<0x2 0x12 0x5>,
+						<0x2 0x12 0x6>,
+						<0x2 0x12 0x7>;
+
+				interrupt-names =	"batt-hot",
+							"batt-warm",
+							"batt-cold",
+							"batt-cool",
+						"batt-ov",
+							"batt-low",
+							"batt-missing",
+							"batt-term-missing";
+			};
+
+			qcom,usb-chgpth@1300 {
+				reg = <0x1300 0x100>;
+				interrupts =	<0x2 0x13 0x0>,
+						<0x2 0x13 0x1>,
+					<0x2 0x13 0x2>,
+						<0x2 0x13 0x5>;
+
+				interrupt-names =	"usbin-uv",
+						"usbin-ov",
+							"usbin-src-det",
+							"aicl-done";
+			};
+
+			qcom,dc-chgpth@1400 {
+				reg = <0x1400 0x100>;
+				interrupts =	<0x2 0x14 0x0>,
+						<0x2 0x14 0x1>;
+				interrupt-names =	"dcin-uv",
+							"dcin-ov";
+			};
+
+			qcom,chgr-misc@1600 {
+				reg = <0x1600 0x100>;
+				interrupts =	<0x2 0x16 0x0>,
+						<0x2 0x16 0x1>,
+						<0x2 0x16 0x2>,
+					<0x2 0x16 0x3>,
+						<0x2 0x16 0x4>,
+						<0x2 0x16 0x5>;
+
+				interrupt-names =	"power-ok",
+							"temp-shutdown",
+							"wdog-timeout",
+							"flash-fail",
+							"otst2",
+							"otst3";
+			};
+
+			smbcharger_charger_otg: qcom,smbcharger-boost-otg {
+				regulator-name = "smbcharger_charger_otg";
+			};
+		};
+
+		pmi8950_fg: qcom,fg {
+			spmi-dev-container;
+			compatible = "qcom,qpnp-fg";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,resume-soc = <95>;
+			status = "okay";
+			qcom,bcl-lm-threshold-ma = <127>;
+			qcom,bcl-mh-threshold-ma = <405>;
+			qcom,fg-iterm-ma = <150>;
+			qcom,fg-chg-iterm-ma = <100>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+			qcom,fg-cutoff-voltage-mv = <3500>;
+			qcom,cycle-counter-en;
+			qcom,capacity-learning-on;
+
+			qcom,fg-soc@4000 {
+			status = "okay";
+				reg = <0x4000 0x100>;
+				interrupts =	<0x2 0x40 0x0>,
+						<0x2 0x40 0x1>,
+						<0x2 0x40 0x2>,
+						<0x2 0x40 0x3>,
+						<0x2 0x40 0x4>,
+						<0x2 0x40 0x5>,
+						<0x2 0x40 0x6>;
+
+				interrupt-names =	"high-soc",
+							"low-soc",
+							"full-soc",
+							"empty-soc",
+							"delta-soc",
+							"first-est-done",
+							"update-soc";
+			};
+
+			qcom,fg-batt@4100 {
+				reg = <0x4100 0x100>;
+				interrupts =	<0x2 0x41 0x0>,
+						<0x2 0x41 0x1>,
+					<0x2 0x41 0x2>,
+						<0x2 0x41 0x3>,
+						<0x2 0x41 0x4>,
+						<0x2 0x41 0x5>,
+						<0x2 0x41 0x6>,
+						<0x2 0x41 0x7>;
+
+				interrupt-names =	"soft-cold",
+							"soft-hot",
+							"vbatt-low",
+							"batt-ided",
+							"batt-id-req",
+							"batt-unknown",
+							"batt-missing",
+							"batt-match";
+			};
+
+			qcom,revid-tp-rev@1f1 {
+				reg = <0x1f1 0x1>;
+			};
+
+			qcom,fg-memif@4400 {
+				status = "okay";
+				reg = <0x4400 0x100>;
+				interrupts =	<0x2 0x44 0x0>,
+						<0x2 0x44 0x2>;
+
+				interrupt-names =	"mem-avail",
+							"data-rcvry-sug";
+			};
+		};
+
+		bcl@4200 {
+			compatible = "qcom,msm-bcl";
+			reg = <0x4200 0xFF 0x88E 0x2>;
+			reg-names = "fg_user_adc", "pon_spare";
+			interrupts = <0x2 0x42 0x0>,
+					<0x2 0x42 0x1>;
+			interrupt-names = "bcl-high-ibat-int",
+					"bcl-low-vbat-int";
+			qcom,vbat-scaling-factor = <39000>;
+			qcom,vbat-gain-numerator = <1>;
+			qcom,vbat-gain-denominator = <128>;
+			qcom,vbat-polling-delay-ms = <100>;
+			qcom,ibat-scaling-factor = <39000>;
+			qcom,ibat-gain-numerator = <1>;
+			qcom,ibat-gain-denominator = <128>;
+			qcom,ibat-offset-numerator = <1200>;
+			qcom,ibat-offset-denominator = <1>;
+			qcom,ibat-polling-delay-ms = <100>;
+			qcom,inhibit-derating-ua = <550000>;
+		};
+
+		qcom,leds@a100 {
+			compatible = "qcom,leds-qpnp";
+			reg = <0xa100 0x100>;
+			label = "mpp";
+		};
+	};
+
+	qcom,pmi8950@3 {
+		compatible ="qcom,spmi-pmic";
+		reg = <0x3 SPMI_USID>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		pmi8950_pwm: pwm@b000 {
+			status = "disabled";
+			compatible = "qcom,qpnp-pwm";
+			reg = <0xb000 0x100>;
+			reg-names = "qpnp-lpg-channel-base";
+			qcom,channel-id = <0>;
+			qcom,supported-sizes = <6>, <9>;
+			#pwm-cells = <2>;
+		};
+
+		labibb: qpnp-labibb-regulator {
+			status = "disabled";
+			spmi-dev-container;
+			compatible = "qcom,qpnp-labibb-regulator";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,pmic-revid = <&pmi8950_revid>;
+
+			ibb_regulator: qcom,ibb@dc00 {
+				reg = <0xdc00 0x100>;
+				reg-names = "ibb_reg";
+				regulator-name = "ibb_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-ibb-min-voltage = <1400000>;
+				qcom,qpnp-ibb-step-size = <100000>;
+				qcom,qpnp-ibb-slew-rate = <2000000>;
+				qcom,qpnp-ibb-use-default-voltage;
+				qcom,qpnp-ibb-init-voltage = <5500000>;
+				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+				qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-ibb-soft-start = <1000>;
+
+				qcom,qpnp-ibb-discharge-resistor = <32>;
+				qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
+				qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
+				qcom,qpnp-ibb-en-discharge;
+
+				qcom,qpnp-ibb-full-pull-down;
+				qcom,qpnp-ibb-pull-down-enable;
+				qcom,qpnp-ibb-switching-clock-frequency =
+									<1480>;
+				qcom,qpnp-ibb-limit-maximum-current = <1550>;
+				qcom,qpnp-ibb-debounce-cycle = <16>;
+				qcom,qpnp-ibb-limit-max-current-enable;
+				qcom,qpnp-ibb-ps-enable;
+			};
+
+			lab_regulator: qcom,lab@de00 {
+				reg = <0xde00 0x100>;
+				reg-names = "lab";
+				regulator-name = "lab_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-lab-min-voltage = <4600000>;
+				qcom,qpnp-lab-step-size = <100000>;
+				qcom,qpnp-lab-slew-rate = <5000>;
+				qcom,qpnp-lab-use-default-voltage;
+				qcom,qpnp-lab-init-voltage = <5500000>;
+				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+				qcom,qpnp-lab-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-lab-soft-start = <800>;
+
+				qcom,qpnp-lab-full-pull-down;
+				qcom,qpnp-lab-pull-down-enable;
+				qcom,qpnp-lab-switching-clock-frequency =
+									<1600>;
+				qcom,qpnp-lab-limit-maximum-current = <800>;
+				qcom,qpnp-lab-limit-max-current-enable;
+				qcom,qpnp-lab-ps-threshold = <40>;
+				qcom,qpnp-lab-ps-enable;
+				qcom,qpnp-lab-nfet-size = <100>;
+				qcom,qpnp-lab-pfet-size = <100>;
+				qcom,qpnp-lab-max-precharge-time = <500>;
+			};
+
+		};
+
+		wled: qcom,leds@d800 {
+			compatible = "qcom,qpnp-wled";
+			reg = <0xd800 0x100>,
+				<0xd900 0x100>,
+				<0xdc00 0x100>,
+				<0xde00 0x100>;
+			reg-names = "qpnp-wled-ctrl-base",
+					"qpnp-wled-sink-base",
+					"qpnp-wled-ibb-base",
+					"qpnp-wled-lab-base";
+			interrupts = <0x3 0xd8 0x2>;
+			interrupt-names = "sc-irq";
+			status = "okay";
+			linux,name = "wled";
+			linux,default-trigger = "bkl-trigger";
+			qcom,fdbk-output = "auto";
+			qcom,vref-mv = <350>;
+			qcom,switch-freq-khz = <800>;
+			qcom,ovp-mv = <29500>;
+			qcom,ilim-ma = <980>;
+			qcom,boost-duty-ns = <26>;
+			qcom,mod-freq-khz = <9600>;
+			qcom,dim-mode = "hybrid";
+			qcom,dim-method = "linear";
+			qcom,hyb-thres = <625>;
+			qcom,sync-dly-us = <800>;
+			qcom,fs-curr-ua = <20000>;
+			qcom,led-strings-list = [00 01];
+			qcom,en-ext-pfet-sc-pro;
+			qcom,cons-sync-write-delay-us = <1000>;
+		};
+
+		flash_led: qcom,leds@d300 {
+			compatible = "qcom,qpnp-flash-led";
+			status = "okay";
+			reg = <0xd300 0x100>;
+			label = "flash";
+			qcom,headroom = <500>;
+			qcom,startup-dly = <128>;
+			qcom,clamp-curr = <200>;
+			qcom,pmic-charger-support;
+			qcom,self-check-enabled;
+			qcom,thermal-derate-enabled;
+			qcom,thermal-derate-threshold = <100>;
+			qcom,thermal-derate-rate = "5_PERCENT";
+			qcom,current-ramp-enabled;
+			qcom,ramp_up_step = "6P7_US";
+			qcom,ramp_dn_step = "6P7_US";
+			qcom,vph-pwr-droop-enabled;
+			qcom,vph-pwr-droop-threshold = <3000>;
+			qcom,vph-pwr-droop-debounce-time = <10>;
+			qcom,headroom-sense-ch0-enabled;
+			qcom,headroom-sense-ch1-enabled;
+			qcom,pmic-revid = <&pmi8950_revid>;
+
+			pmi8950_flash0: qcom,flash_0 {
+				label = "flash";
+				qcom,led-name = "led:flash_0";
+				qcom,default-led-trigger =
+						"flash0_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <0>;
+				qcom,current = <625>;
+			};
+
+			pmi8950_flash1: qcom,flash_1 {
+				label = "flash";
+				qcom,led-name = "led:flash_1";
+				qcom,default-led-trigger =
+						"flash1_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <1>;
+				qcom,current = <625>;
+			};
+
+			pmi8950_torch0: qcom,torch_0 {
+				label = "torch";
+				qcom,led-name = "led:torch_0";
+				qcom,default-led-trigger =
+						"torch0_trigger";
+				qcom,max-current = <200>;
+				qcom,id = <0>;
+				qcom,current = <120>;
+			};
+
+			pmi8950_torch1: qcom,torch_1 {
+				label = "torch";
+				qcom,led-name = "led:torch_1";
+				qcom,default-led-trigger =
+						"torch1_trigger";
+				qcom,max-current = <200>;
+				qcom,id = <1>;
+				qcom,current = <120>;
+			};
+
+			pmi8950_switch: qcom,switch {
+				label = "switch";
+				qcom,led-name = "led:switch";
+				qcom,default-led-trigger =
+						"switch_trigger";
+				qcom,max-current = <1000>;
+				qcom,duration = <1280>;
+				qcom,id = <2>;
+				qcom,current = <625>;
+				reg0 {
+					regulator-name = "pon_spare_reg";
+				};
+			};
+		};
+
+		pmi_haptic: qcom,haptic@c000 {
+			compatible = "qcom,qpnp-haptic";
+			reg = <0xc000 0x100>;
+			interrupts = <0x3 0xc0 0x0>,
+					<0x3 0xc0 0x1>;
+			interrupt-names = "sc-irq", "play-irq";
+			qcom,pmic-revid = <&pmi8950_revid>;
+			vcc_pon-supply = <&pon_perph_reg>;
+			qcom,play-mode = "direct";
+			qcom,wave-play-rate-us = <5263>;
+			qcom,actuator-type = "erm";
+			qcom,wave-shape = "square";
+			qcom,vmax-mv = <2000>;
+			qcom,ilim-ma = <800>;
+			qcom,sc-deb-cycles = <8>;
+			qcom,int-pwm-freq-khz = <505>;
+			qcom,en-brake;
+			qcom,brake-pattern = [03 03 00 00];
+			qcom,use-play-irq;
+			qcom,use-sc-irq;
+			qcom,wave-samples = [3e 3e 3e 3e 3e 3e 3e 3e];
+			qcom,wave-rep-cnt = <1>;
+			qcom,wave-samp-rep-cnt = <1>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/pmi8998.dtsi b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
index a8b826a..c65430b1 100644
--- a/arch/arm64/boot/dts/qcom/pmi8998.dtsi
+++ b/arch/arm64/boot/dts/qcom/pmi8998.dtsi
@@ -71,7 +71,7 @@
 			qcom,gpios-disallowed = <4 7 13>;
 		};
 
-		qcom,qpnp-qnovo@1500 {
+		pmi8998_qnovo: qcom,qpnp-qnovo@1500 {
 			compatible = "qcom,qpnp-qnovo";
 			reg = <0x1500 0x100>;
 			interrupts = <0x2 0x15 0x0 IRQ_TYPE_NONE>;
@@ -102,6 +102,7 @@
 
 			qcom,thermal-mitigation
 					= <3000000 1500000 1000000 500000>;
+			qcom,auto-recharge-soc;
 
 			qcom,chgr@1000 {
 				reg = <0x1000 0x100>;
@@ -282,6 +283,9 @@
 			qcom,fg-esr-timer-asleep = <256 256>;
 			qcom,fg-esr-timer-charging = <0 96>;
 			qcom,cycle-counter-en;
+			qcom,hold-soc-while-full;
+			qcom,fg-auto-recharge-soc;
+			qcom,fg-recharge-soc-thr = <98>;
 			status = "okay";
 
 			qcom,fg-batt-soc@4000 {
@@ -328,7 +332,8 @@
 				reg = <0x4400 0x100>;
 				interrupts = <0x2 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
 					     <0x2 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
-					     <0x2 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+					     <0x2 0x44 0x2
+							IRQ_TYPE_EDGE_RISING>;
 				interrupt-names = "ima-rdy",
 						  "mem-xcp",
 						  "dma-grant";
@@ -529,7 +534,7 @@
 									<1600>;
 				qcom,qpnp-lab-limit-maximum-current = <1600>;
 				qcom,qpnp-lab-limit-max-current-enable;
-				qcom,qpnp-lab-ps-threshold = <20>;
+				qcom,qpnp-lab-ps-threshold = <70>;
 				qcom,qpnp-lab-ps-enable;
 				qcom,qpnp-lab-nfet-size = <100>;
 				qcom,qpnp-lab-pfet-size = <100>;
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dts b/arch/arm64/boot/dts/qcom/qcs605-360camera.dts
new file mode 100644
index 0000000..8caad4b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dts
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605.dtsi"
+#include "qcs605-360camera.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L 360camera";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,board-id = <0x0000000b 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
new file mode 100644
index 0000000..87e2e03
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-360camera.dtsi
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm670-mtp.dtsi"
+#include "sdm670-camera-sensor-360camera.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+&qupv3_se3_i2c {
+	status = "disabled";
+};
+
+&qupv3_se10_i2c {
+	status = "okay";
+};
+
+&qupv3_se12_2uart {
+	status = "okay";
+};
+
+&qupv3_se6_4uart {
+	status = "okay";
+};
+
+&qupv3_se13_i2c {
+	status = "disabled";
+};
+
+&qupv3_se13_spi {
+	status = "disabled";
+};
+
+&int_codec {
+	qcom,model = "sdm670-360cam-snd-card";
+	qcom,audio-routing =
+		"RX_BIAS", "INT_MCLK0",
+		"SPK_RX_BIAS", "INT_MCLK0",
+		"INT_LDO_H", "INT_MCLK0",
+		"DMIC1", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic1",
+		"DMIC2", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic2",
+		"DMIC3", "MIC BIAS External2",
+		"MIC BIAS External2", "Digital Mic3",
+		"DMIC4", "MIC BIAS External2",
+		"MIC BIAS External2", "Digital Mic4",
+		"PDM_IN_RX1", "PDM_OUT_RX1",
+		"PDM_IN_RX2", "PDM_OUT_RX2",
+		"PDM_IN_RX3", "PDM_OUT_RX3",
+		"ADC1_IN", "ADC1_OUT",
+		"ADC2_IN", "ADC2_OUT",
+		"ADC3_IN", "ADC3_OUT";
+	qcom,wsa-max-devs = <0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/qcs605-cdp-overlay.dts
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/qcs605-cdp-overlay.dts
index 2fac9e8..01471b6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/qcs605-cdp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L CDP";
+	compatible = "qcom,qcs605-cdp", "qcom,qcs605", "qcom,cdp";
+	qcom,msm-id = <347 0x0>;
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/qcs605-cdp.dts b/arch/arm64/boot/dts/qcom/qcs605-cdp.dts
new file mode 100644
index 0000000..ea10fa0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-cdp.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L CDP";
+	compatible = "qcom,qcs605-cdp", "qcom,qcs605", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp-overlay.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp-overlay.dts
index 2fac9e8..44fae6a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L Ext. Audio Codec MTP";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,msm-id = <347 0x0>;
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp.dts b/arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp.dts
new file mode 100644
index 0000000..abc3f2d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-external-codec-mtp.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L Ext. Audio Codec MTP";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/qcs605-mtp-overlay.dts
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/qcs605-mtp-overlay.dts
index 2fac9e8..7955242 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/qcs605-mtp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L MTP";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,msm-id = <347 0x0>;
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/qcs605-mtp.dts b/arch/arm64/boot/dts/qcom/qcs605-mtp.dts
new file mode 100644
index 0000000..dc3c7ce
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/qcs605-mtp.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "qcs605.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. QCS605 PM660 + PM660L MTP";
+	compatible = "qcom,qcs605-mtp", "qcom,qcs605", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/qcs605.dts
similarity index 64%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/qcs605.dts
index c06b806..28c417f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/qcs605.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,12 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "qcs605.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. QCS605 SoC";
+	compatible = "qcom,qcs605";
+	qcom,board-id = <0 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/qcs605.dtsi
similarity index 63%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/qcs605.dtsi
index c06b806..12da650 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/qcs605.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,9 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sda670.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. QCS605";
+	qcom,msm-id = <347 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda670-cdp-overlay.dts
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda670-cdp-overlay.dts
index 2fac9e8..12a130c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda670-cdp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660L CDP";
+	compatible = "qcom,sda670-cdp", "qcom,sda670", "qcom,cdp";
+	qcom,msm-id = <337 0x0>;
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda670-cdp.dts b/arch/arm64/boot/dts/qcom/sda670-cdp.dts
new file mode 100644
index 0000000..9cd9960
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-cdp.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660L CDP";
+	compatible = "qcom,sda670-cdp", "qcom,sda670", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda670-mtp-overlay.dts
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda670-mtp-overlay.dts
index 2fac9e8..b3f5a0b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda670-mtp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660L MTP";
+	compatible = "qcom,sda670-mtp", "qcom,sda670", "qcom,mtp";
+	qcom,msm-id = <337 0x0>;
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda670-mtp.dts b/arch/arm64/boot/dts/qcom/sda670-mtp.dts
new file mode 100644
index 0000000..253ec0c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-mtp.dts
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660L MTP";
+	compatible = "qcom,sda670-mtp", "qcom,sda670", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda670-pm660a-cdp-overlay.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda670-pm660a-cdp-overlay.dts
index 2fac9e8..7701c0b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda670-pm660a-cdp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660A CDP";
+	compatible = "qcom,sda670-cdp", "qcom,sda670", "qcom,cdp";
+	qcom,msm-id = <337 0x0>;
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda670-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sda670-pm660a-cdp.dts
new file mode 100644
index 0000000..e6f8d50
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-pm660a-cdp.dts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660A CDP";
+	compatible = "qcom,sda670-cdp", "qcom,sda670", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda670-pm660a-mtp-overlay.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda670-pm660a-mtp-overlay.dts
index 2fac9e8..0b355ab 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda670-pm660a-mtp-overlay.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -19,11 +20,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660A MTP";
+	compatible = "qcom,sda670-mtp", "qcom,sda670", "qcom,mtp";
+	qcom,msm-id = <337 0x0>;
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda670-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sda670-pm660a-mtp.dts
new file mode 100644
index 0000000..0d7e34a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda670-pm660a-mtp.dts
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sda670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA670 PM660 + PM660A MTP";
+	compatible = "qcom,sda670-mtp", "qcom,sda670", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda670.dts
similarity index 64%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda670.dts
index c06b806..8852e30 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda670.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,12 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sda670.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670 SoC";
+	compatible = "qcom,sda670";
+	qcom,board-id = <0 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda670.dtsi
similarity index 63%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda670.dtsi
index c06b806..d19aac3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda670.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,9 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm670.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA670";
+	qcom,msm-id = <337 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..9b7449e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel CDP";
+	compatible = "qcom,sda845-cdp", "qcom,sda845", "qcom,cdp";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..bb310d3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel MTP";
+	compatible = "qcom,sda845-mtp", "qcom,sda845", "qcom,mtp";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..ab61f2e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2 4K Display Panel QRD";
+	compatible = "qcom,sda845-qrd", "qcom,sda845", "qcom,qrd";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts
new file mode 100644
index 0000000..813c198
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts
@@ -0,0 +1,56 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sda845-v2-hdk.dtsi"
+#include "sdm845-hdk-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDA845 v2 HDK";
+	compatible = "qcom,sda845-hdk", "qcom,sda845", "qcom,hdk";
+	qcom,msm-id = <341 0x20000>;
+	qcom,board-id = <0x01001F 0x00>;
+};
+
+&dsi_dual_nt36850_truly_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_dual_nt36850_truly_cmd_display {
+	qcom,dsi-display-active;
+};
+
+&labibb {
+	status = "ok";
+	qcom,qpnp-labibb-mode = "lcd";
+};
+
+&pmi8998_wled {
+	status = "okay";
+	qcom,led-strings-list = [01 02];
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
similarity index 73%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
index c06b806..17f8324 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
@@ -13,11 +13,11 @@
 
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sda845-v2.dtsi"
+#include "sda845-v2-hdk.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA845 HDK";
+	compatible = "qcom,sda845-hdk", "qcom,sda845", "qcom,hdk";
+	qcom,board-id = <0x01001F 0x00>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
index c06b806..d212554 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
@@ -10,14 +10,15 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
 #include "sdm845-qvr.dtsi"
+#include "sdm845-camera-sensor-mtp.dtsi"
 
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&vendor {
+	qcom,battery-data {
+		#include "fg-gen3-batterydata-mlp356477-2800mah.dtsi"
+	};
+};
+
+&sdhc_2 {
+	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..d49fdb6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2.1 4K Display Panel CDP";
+	compatible = "qcom,sda845-cdp", "qcom,sda845", "qcom,cdp";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..c797492
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2.1 4K Display Panel MTP";
+	compatible = "qcom,sda845-mtp", "qcom,sda845", "qcom,mtp";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..221a1d7
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sda845 v2.1 4K Display Panel QRD";
+	compatible = "qcom,sda845-qrd", "qcom,sda845", "qcom,qrd";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-cdp-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2.1-cdp-overlay.dts
index 2fac9e8..64af617 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-cdp-overlay.dts
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+
 /dts-v1/;
 /plugin/;
 
@@ -19,11 +20,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. sda845 v2.1 CDP";
+	compatible = "qcom,sda845-cdp", "qcom,sda845", "qcom,cdp";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <1 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-mtp-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2.1-mtp-overlay.dts
index 2fac9e8..931f0e2 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-mtp-overlay.dts
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  */
 
+
 /dts-v1/;
 /plugin/;
 
@@ -19,11 +20,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. sda845 v2.1 MTP";
+	compatible = "qcom,sda845-mtp", "qcom,sda845", "qcom,mtp";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <8 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1-qrd-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2.1-qrd-overlay.dts
index 2fac9e8..d279fce 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1-qrd-overlay.dts
@@ -19,11 +19,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA845 v2.1 QRD";
+	compatible = "qcom,sda845-qrd", "qcom,sda845", "qcom,qrd";
+	qcom,msm-id = <341 0x20001>;
+	qcom,board-id = <11 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1.dts
similarity index 73%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2.1.dts
index c06b806..9706587 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1.dts
@@ -10,14 +10,12 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sda845-v2.1.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA845 v2.1 SoC";
+	compatible = "qcom,sda845";
+	qcom,board-id = <0 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sda845-v2.1.dtsi
similarity index 71%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sda845-v2.1.dtsi
index c06b806..fe70be1 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sda845-v2.1.dtsi
@@ -10,14 +10,9 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm845-v2.1.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDA 845 V2.1";
+	qcom,msm-id = <341 0x20001>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-cdp.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-cdp.dts
index c06b806..3e06872 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-cdp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 CDP";
+	compatible = "qcom,sdm450-cdp", "qcom,sdm450", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-iot-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-iot-mtp.dts
index c06b806..7fac030 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-iot-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 IOT MTP";
+	compatible = "qcom,sdm450-mtp", "qcom,sdm450", "qcom,mtp";
+	qcom,board-id = <8 2>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-mtp.dts
index c06b806..2524b80 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 MTP";
+	compatible = "qcom,sdm450-mtp", "qcom,sdm450", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi8937-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-pmi8937-mtp.dts
index c06b806..6a6a09e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi8937-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8937 MTP";
+	compatible = "qcom,sdm450-mtp", "qcom,sdm450", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x010016 0x020037 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-pmi8940-mtp.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-pmi8940-mtp.dts
index c06b806..3c4e802 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-pmi8940-mtp.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-mtp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8940 MTP";
+	compatible = "qcom,sdm450-mtp", "qcom,sdm450", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x010016 0x020040 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-qrd.dts
similarity index 60%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-qrd.dts
index c06b806..3c2e25b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-qrd.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-qrd.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 QRD";
+	compatible = "qcom,sdm450-qrd", "qcom,sdm450", "qcom,qrd";
+	qcom,board-id = <0x5000b 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450-rcm.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450-rcm.dts
index c06b806..4ab131a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450-rcm.dts
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,14 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm450.dtsi"
+#include "msm8953-cdp.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450 + PMI8950 RCM";
+	compatible = "qcom,sdm450-cdp", "qcom,sdm450", "qcom,cdp";
+	qcom,board-id = <21 0>;
+	qcom,pmic-id = <0x010016 0x010011 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm450.dtsi
similarity index 63%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm450.dtsi
index c06b806..8087399 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm450.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,11 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "msm8953.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM450";
+	compatible = "qcom,sdm450";
+	qcom,msm-id = <338 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
new file mode 100644
index 0000000..5dd5c0d
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio-overlay.dtsi
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-wcd.dtsi"
+#include "sdm670-wsa881x.dtsi"
+#include <dt-bindings/clock/qcom,audio-ext-clk.h>
+
+&tavil_snd {
+	qcom,msm-mi2s-master = <1>, <1>, <1>, <1>, <1>;
+	qcom,audio-routing =
+		"AIF4 VI", "MCLK",
+		"RX_BIAS", "MCLK",
+		"MADINPUT", "MCLK",
+		"hifi amp", "LINEOUT1",
+		"hifi amp", "LINEOUT2",
+		"AMIC2", "MIC BIAS2",
+		"MIC BIAS2", "Headset Mic",
+		"AMIC3", "MIC BIAS2",
+		"MIC BIAS2", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2",
+		"MIC BIAS2", "ANCLeft Headset Mic",
+		"AMIC5", "MIC BIAS3",
+		"MIC BIAS3", "Handset Mic",
+		"DMIC0", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic0",
+		"DMIC1", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic1",
+		"DMIC2", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic2",
+		"DMIC3", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic3",
+		"DMIC4", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic4",
+		"DMIC5", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic5",
+		"SpkrLeft IN", "SPK1 OUT",
+		"SpkrRight IN", "SPK2 OUT";
+
+	qcom,msm-mbhc-hphl-swh = <1>;
+	qcom,msm-mbhc-gnd-swh = <1>;
+	qcom,hph-en0-gpio = <&tavil_hph_en0>;
+	qcom,hph-en1-gpio = <&tavil_hph_en1>;
+	qcom,msm-mclk-freq = <9600000>;
+	asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
+	asoc-codec-names = "msm-stub-codec.1", "msm-ext-disp-audio-codec-rx";
+	qcom,wsa-max-devs = <2>;
+	qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>,
+		<&wsa881x_0213>, <&wsa881x_0214>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+		"SpkrLeft", "SpkrRight";
+};
+
+&int_codec {
+	qcom,audio-routing =
+		"RX_BIAS", "INT_MCLK0",
+		"SPK_RX_BIAS", "INT_MCLK0",
+		"INT_LDO_H", "INT_MCLK0",
+		"RX_I2S_CLK", "INT_MCLK0",
+		"TX_I2S_CLK", "INT_MCLK0",
+		"MIC BIAS External", "Handset Mic",
+		"MIC BIAS External2", "Headset Mic",
+		"MIC BIAS External", "Secondary Mic",
+		"AMIC1", "MIC BIAS External",
+		"AMIC2", "MIC BIAS External2",
+		"AMIC3", "MIC BIAS External",
+		"DMIC1", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic1",
+		"DMIC2", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic2",
+		"DMIC3", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic3",
+		"DMIC4", "MIC BIAS External",
+		"MIC BIAS External", "Digital Mic4",
+		"SpkrLeft IN", "SPK1 OUT",
+		"SpkrRight IN", "SPK2 OUT",
+		"PDM_IN_RX1", "PDM_OUT_RX1",
+		"PDM_IN_RX2", "PDM_OUT_RX2",
+		"PDM_IN_RX3", "PDM_OUT_RX3",
+		"ADC1_IN", "ADC1_OUT",
+		"ADC2_IN", "ADC2_OUT",
+		"ADC3_IN", "ADC3_OUT";
+
+	qcom,msm-mi2s-master = <1>, <1>, <1>, <1>, <1>;
+	qcom,msm-mclk-freq = <9600000>;
+	qcom,msm-mbhc-hphl-swh = <1>;
+	qcom,msm-mbhc-gnd-swh = <1>;
+	qcom,msm-micbias2-ext-cap;
+	qcom,msm-hs-micbias-type = "external";
+	qcom,cdc-pdm-gpios = <&cdc_pdm_gpios>;
+	qcom,cdc-comp-gpios = <&cdc_comp_gpios>;
+	qcom,cdc-dmic-gpios = <&cdc_dmic_gpios>;
+
+	asoc-codec = <&stub_codec>, <&msm_digital_codec>,
+		     <&pmic_analog_codec>, <&msm_sdw_codec>,
+		     <&ext_disp_audio_codec>;
+	asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec",
+			   "analog-codec", "msm_sdw_codec",
+			   "msm-ext-disp-audio-codec-rx";
+
+	qcom,wsa-max-devs = <2>;
+	qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_212_en>,
+			<&wsa881x_213_en>, <&wsa881x_214_en>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+				  "SpkrLeft", "SpkrRight";
+};
+
+&soc {
+	wcd_usbc_analog_en1_gpio: msm_cdc_pinctrl_usbc_audio_en1 {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&wcd_usbc_analog_en1_active>;
+		pinctrl-1 = <&wcd_usbc_analog_en1_idle>;
+	};
+
+	cdc_pdm_gpios: cdc_pdm_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_pdm_clk_active &cdc_pdm_sync_active
+			     &cdc_pdm_rx0_active &cdc_pdm_rx1_2_active
+			     &cdc_pdm_2_gpios_active>;
+		pinctrl-1 = <&cdc_pdm_clk_sleep &cdc_pdm_sync_sleep
+			     &cdc_pdm_rx0_sleep &cdc_pdm_rx1_2_sleep
+			     &cdc_pdm_2_gpios_sleep>;
+		qcom,lpi-gpios;
+	};
+
+	cdc_comp_gpios: cdc_comp_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_rx0_comp_active &cdc_rx1_comp_active>;
+		pinctrl-1 = <&cdc_rx0_comp_sleep &cdc_rx1_comp_sleep>;
+		qcom,lpi-gpios;
+	};
+
+	cdc_dmic_gpios: cdc_dmic_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_dmic12_gpios_active
+			     &cdc_dmic34_gpios_active>;
+		pinctrl-1 = <&cdc_dmic12_gpios_sleep
+			     &cdc_dmic34_gpios_sleep>;
+		qcom,lpi-gpios;
+	};
+
+	cdc_sdw_gpios: sdw_clk_data_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&sdw_clk_active &sdw_data_active>;
+		pinctrl-1 = <&sdw_clk_sleep &sdw_data_sleep>;
+	};
+
+	wsa_spkr_en1: wsa_spkr_en1_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&spkr_1_sd_n_active>;
+		pinctrl-1 = <&spkr_1_sd_n_sleep>;
+	};
+
+	wsa_spkr_en2: wsa_spkr_en2_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&spkr_2_sd_n_active>;
+		pinctrl-1 = <&spkr_2_sd_n_sleep>;
+	};
+
+	msm_sdw_codec: msm-sdw-codec@62ec1000 {
+		status = "okay";
+		compatible = "qcom,msm-sdw-codec";
+		reg = <0x62ec1000 0x0>;
+		interrupts = <0 88 0>;
+		interrupt-names = "swr_master_irq";
+		qcom,cdc-sdw-gpios = <&cdc_sdw_gpios>;
+
+		swr_master {
+			compatible = "qcom,swr-wcd";
+			#address-cells = <2>;
+			#size-cells = <0>;
+
+			wsa881x_211_en: wsa881x_en@20170211 {
+				compatible = "qcom,wsa881x";
+				reg = <0x0 0x20170211>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_en1>;
+			};
+
+			wsa881x_212_en: wsa881x_en@20170212 {
+				compatible = "qcom,wsa881x";
+				reg = <0x0 0x20170212>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_en2>;
+			};
+
+			wsa881x_213_en: wsa881x_en@21170213 {
+				compatible = "qcom,wsa881x";
+				reg = <0x0 0x21170213>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_en1>;
+			};
+
+			wsa881x_214_en: wsa881x_en@21170214 {
+				compatible = "qcom,wsa881x";
+				reg = <0x0 0x21170214>;
+				qcom,spkr-sd-n-node = <&wsa_spkr_en2>;
+			};
+		};
+	};
+
+	wcd9xxx_intc: wcd9xxx-irq {
+		status = "disabled";
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&tlmm>;
+		qcom,gpio-connect = <&tlmm 80 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&wcd_intr_default>;
+	};
+
+	clock_audio_lnbb: audio_ext_clk_lnbb {
+		status = "disabled";
+		compatible = "qcom,audio-ref-clk";
+		clock-names = "osr_clk";
+		clocks = <&clock_rpmh RPMH_LN_BB_CLK2>;
+		qcom,node_has_rpm_clock;
+		#clock-cells = <1>;
+	};
+
+	wcd_rst_gpio: msm_cdc_pinctrl@64 {
+		status = "disabled";
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&lpi_cdc_reset_active>;
+		pinctrl-1 = <&lpi_cdc_reset_sleep>;
+		qcom,lpi-gpios;
+	};
+
+	wdsp_mgr: qcom,wcd-dsp-mgr {
+		compatible = "qcom,wcd-dsp-mgr";
+		qcom,wdsp-components = <&wcd934x_cdc 0>,
+				       <&wcd_spi_0 1>,
+				       <&glink_spi_xprt_wdsp 2>;
+					qcom,img-filename = "cpe_9340";
+	};
+
+	wdsp_glink: qcom,wcd-dsp-glink {
+		compatible = "qcom,wcd-dsp-glink";
+	};
+};
+
+&slim_aud {
+	wcd934x_cdc: tavil_codec {
+		status = "disabled";
+		compatible = "qcom,tavil-slim-pgd";
+		elemental-addr = [00 01 50 02 17 02];
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+			      17 18 19 20 21 22 23 24 25 26 27 28 29
+			      30 31>;
+
+		qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
+
+		clock-names = "wcd_clk";
+		clocks = <&clock_audio_lnbb AUDIO_PMIC_LNBB_CLK>;
+
+		cdc-vdd-mic-bias-supply = <&pm660l_bob>;
+		qcom,cdc-vdd-mic-bias-voltage = <3312000 3312000>;
+		qcom,cdc-vdd-mic-bias-current = <30400>;
+
+		qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
+
+		qcom,cdc-micbias1-mv = <1800>;
+		qcom,cdc-micbias2-mv = <1800>;
+		qcom,cdc-micbias3-mv = <1800>;
+		qcom,cdc-micbias4-mv = <1800>;
+
+		qcom,cdc-mclk-clk-rate = <9600000>;
+		qcom,cdc-slim-ifd = "tavil-slim-ifd";
+		qcom,cdc-slim-ifd-elemental-addr = [00 00 50 02 17 02];
+		qcom,cdc-dmic-sample-rate = <4800000>;
+		qcom,cdc-mad-dmic-rate = <600000>;
+
+		qcom,wdsp-cmpnt-dev-name = "tavil_codec";
+
+		wcd_spi_0: wcd_spi {
+			compatible = "qcom,wcd-spi-v2";
+			qcom,master-bus-num = <0>;
+			qcom,chip-select = <0>;
+			qcom,max-frequency = <24000000>;
+			qcom,mem-base-addr = <0x100000>;
+		};
+	};
+};
+
+&pm660l_3 {
+	pmic_analog_codec: analog-codec@f000 {
+		status = "okay";
+		compatible = "qcom,pmic-analog-codec";
+		reg = <0xf000 0x200>;
+		#address-cells = <2>;
+		#size-cells = <0>;
+		interrupt-parent = <&spmi_bus>;
+		interrupts = <0x3 0xf0 0x0 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x1 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x2 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x3 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x4 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x5 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x6 IRQ_TYPE_NONE>,
+			     <0x3 0xf0 0x7 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x0 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x1 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x2 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x3 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x4 IRQ_TYPE_NONE>,
+			     <0x3 0xf1 0x5 IRQ_TYPE_NONE>;
+		interrupt-names = "spk_cnp_int",
+				   "spk_clip_int",
+				   "spk_ocp_int",
+				   "ins_rem_det1",
+				   "but_rel_det",
+				   "but_press_det",
+				   "ins_rem_det",
+				   "mbhc_int",
+				   "ear_ocp_int",
+				   "hphr_ocp_int",
+				   "hphl_ocp_det",
+				   "ear_cnp_int",
+				   "hphr_cnp_int",
+				   "hphl_cnp_int";
+
+		cdc-vdda-cp-supply = <&pm660_s4>;
+		qcom,cdc-vdda-cp-voltage = <1900000 2050000>;
+		qcom,cdc-vdda-cp-current = <50000>;
+
+		cdc-vdd-pa-supply = <&pm660_s4>;
+		qcom,cdc-vdd-pa-voltage = <2040000 2040000>;
+		qcom,cdc-vdd-pa-current = <260000>;
+
+		cdc-vdd-mic-bias-supply = <&pm660l_l7>;
+		qcom,cdc-vdd-mic-bias-voltage = <3088000 3088000>;
+		qcom,cdc-vdd-mic-bias-current = <5000>;
+
+		qcom,cdc-mclk-clk-rate = <9600000>;
+
+		qcom,cdc-static-supplies = "cdc-vdda-cp",
+					   "cdc-vdd-pa";
+
+		qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
+
+		/*
+		 * Not marking address @ as driver searches this child
+		 * with name msm-dig-codec
+		 */
+		msm_digital_codec: msm-dig-codec {
+			compatible = "qcom,msm-digital-codec";
+			reg = <0x62ec0000 0x0>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index 3bd0350..bda44cc 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
@@ -12,8 +12,8 @@
  */
 
 #include "msm-audio-lpass.dtsi"
-#include "sdm670-wsa881x.dtsi"
 #include "sdm670-wcd.dtsi"
+#include "sdm670-wsa881x.dtsi"
 #include "sdm670-lpi.dtsi"
 #include <dt-bindings/clock/qcom,audio-ext-clk.h>
 
@@ -39,42 +39,7 @@
 		qcom,wcn-btfm;
 		qcom,mi2s-audio-intf;
 		qcom,auxpcm-audio-intf;
-		qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
-		qcom,audio-routing =
-			"AIF4 VI", "MCLK",
-			"RX_BIAS", "MCLK",
-			"MADINPUT", "MCLK",
-			"hifi amp", "LINEOUT1",
-			"hifi amp", "LINEOUT2",
-			"AMIC2", "MIC BIAS2",
-			"MIC BIAS2", "Headset Mic",
-			"AMIC3", "MIC BIAS2",
-			"MIC BIAS2", "ANCRight Headset Mic",
-			"AMIC4", "MIC BIAS2",
-			"MIC BIAS2", "ANCLeft Headset Mic",
-			"AMIC5", "MIC BIAS3",
-			"MIC BIAS3", "Handset Mic",
-			"DMIC0", "MIC BIAS1",
-			"MIC BIAS1", "Digital Mic0",
-			"DMIC1", "MIC BIAS1",
-			"MIC BIAS1", "Digital Mic1",
-			"DMIC2", "MIC BIAS3",
-			"MIC BIAS3", "Digital Mic2",
-			"DMIC3", "MIC BIAS3",
-			"MIC BIAS3", "Digital Mic3",
-			"DMIC4", "MIC BIAS4",
-			"MIC BIAS4", "Digital Mic4",
-			"DMIC5", "MIC BIAS4",
-			"MIC BIAS4", "Digital Mic5",
-			"SpkrLeft IN", "SPK1 OUT",
-			"SpkrRight IN", "SPK2 OUT";
-
-		qcom,msm-mbhc-hphl-swh = <1>;
-		qcom,msm-mbhc-gnd-swh = <1>;
-		qcom,hph-en0-gpio = <&tavil_hph_en0>;
-		qcom,hph-en1-gpio = <&tavil_hph_en1>;
-		qcom,msm-mclk-freq = <9600000>;
-		qcom,usbc-analog-en1_gpio = <&wcd_usbc_analog_en1_gpio>;
+		qcom,ext-disp-audio-rx;
 		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
 			<&loopback>, <&compress>, <&hostless>,
 			<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
@@ -86,10 +51,11 @@
 			"msm-pcm-afe", "msm-lsm-client",
 			"msm-pcm-routing", "msm-cpe-lsm",
 			"msm-compr-dsp", "msm-pcm-dsp-noirq";
-		asoc-cpu = <&dai_mi2s0>, <&dai_mi2s1>,
-			<&dai_mi2s2>, <&dai_mi2s3>,
+		asoc-cpu = <&dai_dp>, <&dai_mi2s0>, <&dai_mi2s1>,
+			<&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s4>,
 			<&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
 			<&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
+			<&dai_quin_auxpcm>,
 			<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
 			<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
 			<&sb_4_rx>, <&sb_4_tx>, <&sb_5_rx>, <&sb_5_tx>,
@@ -103,11 +69,15 @@
 			<&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
 			<&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>,
 			<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>,
-			<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>;
-		asoc-cpu-names = "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+			<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>,
+			<&dai_quin_tdm_rx_0>, <&dai_quin_tdm_tx_0>;
+		asoc-cpu-names = "msm-dai-q6-dp.24608",
+			"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
 			"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+			"msm-dai-q6-mi2s.4",
 			"msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
 			"msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
+			"msm-dai-q6-auxpcm.5",
 			"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
 			"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
 			"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
@@ -125,59 +95,18 @@
 			"msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865",
 			"msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881",
 			"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897",
-			"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913";
-		asoc-codec = <&stub_codec>;
-		asoc-codec-names = "msm-stub-codec.1";
-		qcom,wsa-max-devs = <2>;
-		qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>,
-				<&wsa881x_0213>, <&wsa881x_0214>;
-		qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
-					"SpkrLeft", "SpkrRight";
+			"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913",
+			"msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
 	};
 
-int_codec: sound {
+	int_codec: sound {
 		status = "okay";
 		compatible = "qcom,sdm670-asoc-snd";
-		qcom,model = "sdm670-snd-card";
+		qcom,model = "sdm670-mtp-snd-card";
 		qcom,wcn-btfm;
+		qcom,ext-disp-audio-rx;
 		qcom,mi2s-audio-intf;
 		qcom,auxpcm-audio-intf;
-		qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
-		qcom,msm-mclk-freq = <9600000>;
-		qcom,msm-mbhc-hphl-swh = <1>;
-		qcom,msm-mbhc-gnd-swh = <1>;
-		qcom,msm-micbias2-ext-cap;
-		qcom,msm-hs-micbias-type = "external";
-		qcom,cdc-pdm-gpios = <&cdc_pdm_gpios>;
-		qcom,cdc-comp-gpios = <&cdc_comp_gpios>;
-		qcom,cdc-dmic-gpios = <&cdc_dmic_gpios>;
-		qcom,audio-routing =
-			"RX_BIAS", "INT_MCLK0",
-			"SPK_RX_BIAS", "INT_MCLK0",
-			"INT_LDO_H", "INT_MCLK0",
-			"MIC BIAS External", "Handset Mic",
-			"MIC BIAS External2", "Headset Mic",
-			"MIC BIAS External", "Secondary Mic",
-			"AMIC1", "MIC BIAS External",
-			"AMIC2", "MIC BIAS External2",
-			"AMIC3", "MIC BIAS External",
-			"DMIC1", "MIC BIAS External",
-			"MIC BIAS External", "Digital Mic1",
-			"DMIC2", "MIC BIAS External",
-			"MIC BIAS External", "Digital Mic2",
-			"DMIC3", "MIC BIAS External",
-			"MIC BIAS External", "Digital Mic3",
-			"DMIC4", "MIC BIAS External",
-			"MIC BIAS External", "Digital Mic4",
-			"SpkrLeft IN", "SPK1 OUT",
-			"SpkrRight IN", "SPK2 OUT",
-			"PDM_IN_RX1", "PDM_OUT_RX1",
-			"PDM_IN_RX2", "PDM_OUT_RX2",
-			"PDM_IN_RX3", "PDM_OUT_RX3",
-			"ADC1_IN", "ADC1_OUT",
-			"ADC2_IN", "ADC2_OUT",
-			"ADC3_IN", "ADC3_OUT";
-
 		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
 			<&loopback>, <&compress>, <&hostless>,
 			<&afe>, <&lsm>, <&routing>, <&compr>,
@@ -189,13 +118,14 @@
 			"msm-pcm-afe", "msm-lsm-client",
 			"msm-pcm-routing", "msm-compr-dsp",
 			"msm-pcm-dsp-noirq";
-		asoc-cpu = <&dai_mi2s0>, <&dai_mi2s1>,
-			<&dai_mi2s2>, <&dai_mi2s3>,
+		asoc-cpu = <&dai_dp>, <&dai_mi2s0>, <&dai_mi2s1>,
+			<&dai_mi2s2>, <&dai_mi2s3>, <&dai_mi2s4>,
 			<&dai_int_mi2s0>, <&dai_int_mi2s1>,
 			<&dai_int_mi2s2>, <&dai_int_mi2s3>,
 			<&dai_int_mi2s4>, <&dai_int_mi2s5>,
 			<&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
 			<&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
+			<&dai_quin_auxpcm>,
 			<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
 			<&afe_proxy_tx>, <&incall_record_rx>,
 			<&incall_record_tx>, <&incall_music_rx>,
@@ -205,14 +135,18 @@
 			<&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
 			<&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>,
 			<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>,
-			<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>;
-		asoc-cpu-names = "msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+			<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>,
+			<&dai_quin_tdm_rx_0>, <&dai_quin_tdm_tx_0>;
+		asoc-cpu-names = "msm-dai-q6-dp.24608",
+			"msm-dai-q6-mi2s.0","msm-dai-q6-mi2s.1",
 			"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+			"msm-dai-q6-mi2s.4",
 			"msm-dai-q6-mi2s.7", "msm-dai-q6-mi2s.8",
 			"msm-dai-q6-mi2s.9", "msm-dai-q6-mi2s.10",
 			"msm-dai-q6-mi2s.11", "msm-dai-q6-mi2s.12",
 			"msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
 			"msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
+			"msm-dai-q6-auxpcm.5",
 			"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
 			"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
 			"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
@@ -223,136 +157,8 @@
 			"msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865",
 			"msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881",
 			"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897",
-			"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913";
-		asoc-codec = <&stub_codec>, <&msm_digital_codec>,
-				<&pmic_analog_codec>, <&msm_sdw_codec>;
-		asoc-codec-names = "msm-stub-codec.1", "msm-dig-codec",
-				"analog-codec", "msm_sdw_codec";
-
-		qcom,wsa-max-devs = <2>;
-		qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_212_en>,
-				<&wsa881x_213_en>, <&wsa881x_214_en>;
-		qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
-					"SpkrLeft", "SpkrRight";
-	};
-
-	cdc_pdm_gpios: cdc_pdm_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&cdc_pdm_clk_active &cdc_pdm_sync_active
-			     &cdc_pdm_rx0_active &cdc_pdm_rx1_2_active
-			     &cdc_pdm_2_gpios_active>;
-		pinctrl-1 = <&cdc_pdm_clk_sleep &cdc_pdm_sync_sleep
-			     &cdc_pdm_rx0_sleep &cdc_pdm_rx1_2_sleep
-			     &cdc_pdm_2_gpios_sleep>;
-		qcom,lpi-gpios;
-	};
-
-	cdc_comp_gpios: cdc_comp_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&cdc_rx0_comp_active &cdc_rx1_comp_active>;
-		pinctrl-1 = <&cdc_rx0_comp_sleep &cdc_rx1_comp_sleep>;
-		qcom,lpi-gpios;
-	};
-
-	cdc_dmic_gpios: cdc_dmic_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&cdc_dmic12_gpios_active
-				&cdc_dmic34_gpios_active>;
-		pinctrl-1 = <&cdc_dmic12_gpios_sleep
-				&cdc_dmic34_gpios_sleep>;
-		qcom,lpi-gpios;
-	};
-
-	cdc_sdw_gpios: sdw_clk_data_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&sdw_clk_active &sdw_data_active>;
-		pinctrl-1 = <&sdw_clk_sleep &sdw_data_sleep>;
-	};
-
-	wsa_spkr_en1: wsa_spkr_en1_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&spkr_1_sd_n_active>;
-		pinctrl-1 = <&spkr_1_sd_n_sleep>;
-	};
-
-	wsa_spkr_en2: wsa_spkr_en2_pinctrl {
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&spkr_2_sd_n_active>;
-		pinctrl-1 = <&spkr_2_sd_n_sleep>;
-	};
-
-	msm_sdw_codec: msm-sdw-codec@62ec1000 {
-		status = "okay";
-		compatible = "qcom,msm-sdw-codec";
-		reg = <0x62ec1000 0x0>;
-		interrupts = <0 161 0>;
-		interrupt-names = "swr_master_irq";
-		qcom,cdc-sdw-gpios = <&cdc_sdw_gpios>;
-
-		swr_master {
-			compatible = "qcom,swr-wcd";
-			#address-cells = <2>;
-			#size-cells = <0>;
-
-			wsa881x_211_en: wsa881x_en@20170211 {
-				compatible = "qcom,wsa881x";
-				reg = <0x0 0x20170211>;
-				qcom,spkr-sd-n-node = <&wsa_spkr_en1>;
-			};
-
-			wsa881x_212_en: wsa881x_en@20170212 {
-				compatible = "qcom,wsa881x";
-				reg = <0x0 0x20170212>;
-				qcom,spkr-sd-n-node = <&wsa_spkr_en2>;
-			};
-
-			wsa881x_213_en: wsa881x_en@21170213 {
-				compatible = "qcom,wsa881x";
-				reg = <0x0 0x21170213>;
-				qcom,spkr-sd-n-node = <&wsa_spkr_en1>;
-			};
-
-			wsa881x_214_en: wsa881x_en@21170214 {
-				compatible = "qcom,wsa881x";
-				reg = <0x0 0x21170214>;
-				qcom,spkr-sd-n-node = <&wsa_spkr_en2>;
-			};
-		};
-	};
-
-	wcd9xxx_intc: wcd9xxx-irq {
-		status = "disabled";
-		compatible = "qcom,wcd9xxx-irq";
-		interrupt-controller;
-		#interrupt-cells = <1>;
-		interrupt-parent = <&tlmm>;
-		qcom,gpio-connect = <&tlmm 80 0>;
-		pinctrl-names = "default";
-		pinctrl-0 = <&wcd_intr_default>;
-	};
-
-	clock_audio_lnbb: audio_ext_clk_lnbb {
-		status = "disabled";
-		compatible = "qcom,audio-ref-clk";
-		clock-names = "osr_clk";
-		clocks = <&clock_rpmh RPMH_LN_BB_CLK2>;
-		qcom,node_has_rpm_clock;
-		#clock-cells = <1>;
-	};
-
-	wcd_rst_gpio: msm_cdc_pinctrl@64 {
-		status = "disabled";
-		compatible = "qcom,msm-cdc-pinctrl";
-		pinctrl-names = "aud_active", "aud_sleep";
-		pinctrl-0 = <&lpi_cdc_reset_active>;
-		pinctrl-1 = <&lpi_cdc_reset_sleep>;
-		qcom,lpi-gpios;
+			"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913",
+			"msm-dai-q6-tdm.36928", "msm-dai-q6-tdm.36929";
 	};
 
 	cpe: qcom,msm-cpe-lsm {
@@ -363,18 +169,6 @@
 		compatible = "qcom,msm-cpe-lsm";
 		qcom,msm-cpe-lsm-id = <3>;
 	};
-
-	wdsp_mgr: qcom,wcd-dsp-mgr {
-		compatible = "qcom,wcd-dsp-mgr";
-		qcom,wdsp-components = <&wcd934x_cdc 0>,
-				       <&wcd_spi_0 1>,
-				       <&glink_spi_xprt_wdsp 2>;
-		qcom,img-filename = "cpe_9340";
-	};
-
-	wdsp_glink: qcom,wcd-dsp-glink {
-		compatible = "qcom,wcd-dsp-glink";
-	};
 };
 
 &slim_aud {
@@ -384,56 +178,6 @@
 		compatible = "qcom,msm-dai-slim";
 		elemental-addr = [ff ff ff fe 17 02];
 	};
-
-	wcd934x_cdc: tavil_codec {
-		status = "disabled";
-		compatible = "qcom,tavil-slim-pgd";
-		elemental-addr = [00 01 50 02 17 02];
-
-		interrupt-parent = <&wcd9xxx_intc>;
-		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
-			      17 18 19 20 21 22 23 24 25 26 27 28 29
-			      30 31>;
-
-		qcom,wcd-rst-gpio-node = <&wcd_rst_gpio>;
-
-		clock-names = "wcd_clk";
-		clocks = <&clock_audio_lnbb AUDIO_PMIC_LNBB_CLK>;
-
-		cdc-vdd-mic-bias-supply = <&pm660l_bob>;
-		qcom,cdc-vdd-mic-bias-voltage = <3300000 3300000>;
-		qcom,cdc-vdd-mic-bias-current = <30400>;
-
-		qcom,cdc-static-supplies = "cdc-vdd-mic-bias";
-
-		qcom,cdc-micbias1-mv = <1800>;
-		qcom,cdc-micbias2-mv = <1800>;
-		qcom,cdc-micbias3-mv = <1800>;
-		qcom,cdc-micbias4-mv = <1800>;
-
-		qcom,cdc-mclk-clk-rate = <9600000>;
-		qcom,cdc-slim-ifd = "tavil-slim-ifd";
-		qcom,cdc-slim-ifd-elemental-addr = [00 00 50 02 17 02];
-		qcom,cdc-dmic-sample-rate = <4800000>;
-		qcom,cdc-mad-dmic-rate = <600000>;
-
-		qcom,wdsp-cmpnt-dev-name = "tavil_codec";
-
-		wcd_spi_0: wcd_spi {
-			compatible = "qcom,wcd-spi-v2";
-			qcom,master-bus-num = <8>;
-			qcom,chip-select = <0>;
-			qcom,max-frequency = <24000000>;
-			qcom,mem-base-addr = <0x100000>;
-		};
-
-		wcd_usbc_analog_en1_gpio: msm_cdc_pinctrl_usbc_audio_en1 {
-			compatible = "qcom,msm-cdc-pinctrl";
-			pinctrl-names = "aud_active", "aud_sleep";
-			pinctrl-0 = <&wcd_usbc_analog_en1_active>;
-			pinctrl-1 = <&wcd_usbc_analog_en1_idle>;
-		};
-	};
 };
 
 &msm_dai_mi2s {
@@ -486,83 +230,3 @@
 		qcom,msm-mi2s-tx-lines = <3>;
 	};
 };
-
-&pm660l_3 {
-	pmic_analog_codec: analog-codec@f000 {
-		status = "okay";
-		compatible = "qcom,pmic-analog-codec";
-		reg = <0xf000 0x200>;
-		#address-cells = <2>;
-		#size-cells = <0>;
-		interrupt-parent = <&spmi_bus>;
-		interrupts = <0x3 0xf0 0x0 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x1 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x2 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x3 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x4 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x5 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x6 IRQ_TYPE_NONE>,
-				<0x3 0xf0 0x7 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x0 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x1 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x2 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x3 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x4 IRQ_TYPE_NONE>,
-				<0x3 0xf1 0x5 IRQ_TYPE_NONE>;
-		interrupt-names = "spk_cnp_int",
-				  "spk_clip_int",
-				  "spk_ocp_int",
-				  "ins_rem_det1",
-				  "but_rel_det",
-				  "but_press_det",
-				  "ins_rem_det",
-				  "mbhc_int",
-				  "ear_ocp_int",
-				  "hphr_ocp_int",
-				  "hphl_ocp_det",
-				  "ear_cnp_int",
-				  "hphr_cnp_int",
-				  "hphl_cnp_int";
-
-
-		cdc-vdda-cp-supply = <&pm660_s4>;
-		qcom,cdc-vdda-cp-voltage = <1900000 2050000>;
-		qcom,cdc-vdda-cp-current = <50000>;
-
-		cdc-vdd-pa-supply = <&pm660_s4>;
-		qcom,cdc-vdd-pa-voltage = <2040000 2040000>;
-		qcom,cdc-vdd-pa-current = <260000>;
-
-		cdc-vdd-mic-bias-supply = <&pm660l_l7>;
-		qcom,cdc-vdd-mic-bias-voltage = <3088000 3088000>;
-		qcom,cdc-vdd-mic-bias-current = <5000>;
-
-		qcom,cdc-mclk-clk-rate = <9600000>;
-
-		qcom,cdc-static-supplies = "cdc-vdda-cp",
-					   "cdc-vdd-pa";
-
-		qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
-
-		/*
-		 * Not marking address @ as driver searches this child
-		 * with name msm-dig-codec
-		 */
-		msm_digital_codec: msm-dig-codec {
-			compatible = "qcom,msm-digital-codec";
-			reg = <0x62ec0000 0x0>;
-		};
-	};
-};
-
-&pm660_gpios {
-	gpio@c200 {
-		status = "ok";
-		qcom,mode = <1>;
-		qcom,pull = <4>;
-		qcom,vin-sel = <0>;
-		qcom,src-sel = <2>;
-		qcom,master-en = <1>;
-		qcom,out-strength = <2>;
-	};
-};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
new file mode 100644
index 0000000..4f5a9b1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
@@ -0,0 +1,1871 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/msm/msm-bus-ids.h>
+#include <dt-bindings/soc/qcom,tcs-mbox.h>
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+
+&soc {
+	ad_hoc_bus: ad-hoc-bus {
+		compatible = "qcom,msm-bus-device";
+		reg = <0x016E0000 0x40000>,
+			<0x1700000 0x40000>,
+			<0x1500000 0x40000>,
+			<0x14E0000 0x40000>,
+			<0x17900000 0x40000>,
+			<0x1380000 0x40000>,
+			<0x1380000 0x40000>,
+			<0x1740000 0x40000>,
+			<0x1620000 0x40000>,
+			<0x1620000 0x40000>,
+			<0x1620000 0x40000>;
+
+		reg-names = "aggre1_noc-base", "aggre2_noc-base",
+			"config_noc-base", "dc_noc-base",
+			"gladiator_noc-base", "mc_virt-base", "mem_noc-base",
+			"mmss_noc-base", "system_noc-base", "ipa_virt-base",
+			"camnoc_virt-base";
+
+		mbox-names = "apps_rsc", "disp_rsc";
+		mboxes = <&apps_rsc 0 &disp_rsc 0>;
+
+	/*RSCs*/
+		rsc_apps: rsc-apps {
+			cell-id = <MSM_BUS_RSC_APPS>;
+			label = "apps_rsc";
+			qcom,rsc-dev;
+			qcom,req_state = <2>;
+		};
+
+		rsc_disp: rsc-disp {
+			cell-id = <MSM_BUS_RSC_DISP>;
+			label = "disp_rsc";
+			qcom,rsc-dev;
+			qcom,req_state = <3>;
+		};
+
+	/*BCMs*/
+		bcm_acv: bcm-acv {
+			cell-id = <MSM_BUS_BCM_ACV>;
+			label = "ACV";
+			qcom,bcm-name = "ACV";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_alc: bcm-alc {
+			cell-id = <MSM_BUS_BCM_ALC>;
+			label = "ALC";
+			qcom,bcm-name = "ALC";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mc0: bcm-mc0 {
+			cell-id = <MSM_BUS_BCM_MC0>;
+			label = "MC0";
+			qcom,bcm-name = "MC0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh0: bcm-sh0 {
+			cell-id = <MSM_BUS_BCM_SH0>;
+			label = "SH0";
+			qcom,bcm-name = "SH0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm0: bcm-mm0 {
+			cell-id = <MSM_BUS_BCM_MM0>;
+			label = "MM0";
+			qcom,bcm-name = "MM0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh1: bcm-sh1 {
+			cell-id = <MSM_BUS_BCM_SH1>;
+			label = "SH1";
+			qcom,bcm-name = "SH1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm1: bcm-mm1 {
+			cell-id = <MSM_BUS_BCM_MM1>;
+			label = "MM1";
+			qcom,bcm-name = "MM1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh2: bcm-sh2 {
+			cell-id = <MSM_BUS_BCM_SH2>;
+			label = "SH2";
+			qcom,bcm-name = "SH2";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm2: bcm-mm2 {
+			cell-id = <MSM_BUS_BCM_MM2>;
+			label = "MM2";
+			qcom,bcm-name = "MM2";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh3: bcm-sh3 {
+			cell-id = <MSM_BUS_BCM_SH3>;
+			label = "SH3";
+			qcom,bcm-name = "SH3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm3: bcm-mm3 {
+			cell-id = <MSM_BUS_BCM_MM3>;
+			label = "MM3";
+			qcom,bcm-name = "MM3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh5: bcm-sh5 {
+			cell-id = <MSM_BUS_BCM_SH5>;
+			label = "SH5";
+			qcom,bcm-name = "SH5";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn0: bcm-sn0 {
+			cell-id = <MSM_BUS_BCM_SN0>;
+			label = "SN0";
+			qcom,bcm-name = "SN0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_ce0: bcm-ce0 {
+			cell-id = <MSM_BUS_BCM_CE0>;
+			label = "CE0";
+			qcom,bcm-name = "CE0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_ip0: bcm-ip0 {
+			cell-id = <MSM_BUS_BCM_IP0>;
+			label = "IP0";
+			qcom,bcm-name = "IP0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_cn0: bcm-cn0 {
+			cell-id = <MSM_BUS_BCM_CN0>;
+			label = "CN0";
+			qcom,bcm-name = "CN0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_qup0: bcm-qup0 {
+			cell-id = <MSM_BUS_BCM_QUP0>;
+			label = "QUP0";
+			qcom,bcm-name = "QUP0";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn1: bcm-sn1 {
+			cell-id = <MSM_BUS_BCM_SN1>;
+			label = "SN1";
+			qcom,bcm-name = "SN1";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn2: bcm-sn2 {
+			cell-id = <MSM_BUS_BCM_SN2>;
+			label = "SN2";
+			qcom,bcm-name = "SN2";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn3: bcm-sn3 {
+			cell-id = <MSM_BUS_BCM_SN3>;
+			label = "SN3";
+			qcom,bcm-name = "SN3";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn4: bcm-sn4 {
+			cell-id = <MSM_BUS_BCM_SN4>;
+			label = "SN4";
+			qcom,bcm-name = "SN4";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn5: bcm-sn5 {
+			cell-id = <MSM_BUS_BCM_SN5>;
+			label = "SN5";
+			qcom,bcm-name = "SN5";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn8: bcm-sn8 {
+			cell-id = <MSM_BUS_BCM_SN8>;
+			label = "SN8";
+			qcom,bcm-name = "SN8";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn10: bcm-sn10 {
+			cell-id = <MSM_BUS_BCM_SN10>;
+			label = "SN10";
+			qcom,bcm-name = "SN10";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn11: bcm-sn11 {
+			cell-id = <MSM_BUS_BCM_SN11>;
+			label = "SN11";
+			qcom,bcm-name = "SN11";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sn13: bcm-sn13 {
+			cell-id = <MSM_BUS_BCM_SN13>;
+			label = "SN13";
+			qcom,bcm-name = "SN13";
+			qcom,rscs = <&rsc_apps>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mc0_display: bcm-mc0_display {
+			cell-id = <MSM_BUS_BCM_MC0_DISPLAY>;
+			label = "MC0_DISPLAY";
+			qcom,bcm-name = "MC0";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		bcm_sh0_display: bcm-sh0_display {
+			cell-id = <MSM_BUS_BCM_SH0_DISPLAY>;
+			label = "SH0_DISPLAY";
+			qcom,bcm-name = "SH0";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm0_display: bcm-mm0_display {
+			cell-id = <MSM_BUS_BCM_MM0_DISPLAY>;
+			label = "MM0_DISPLAY";
+			qcom,bcm-name = "MM0";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm1_display: bcm-mm1_display {
+			cell-id = <MSM_BUS_BCM_MM1_DISPLAY>;
+			label = "MM1_DISPLAY";
+			qcom,bcm-name = "MM1";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm2_display: bcm-mm2_display {
+			cell-id = <MSM_BUS_BCM_MM2_DISPLAY>;
+			label = "MM2_DISPLAY";
+			qcom,bcm-name = "MM2";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		bcm_mm3_display: bcm-mm3_display {
+			cell-id = <MSM_BUS_BCM_MM3_DISPLAY>;
+			label = "MM3_DISPLAY";
+			qcom,bcm-name = "MM3";
+			qcom,rscs = <&rsc_disp>;
+			qcom,bcm-dev;
+		};
+
+		/*Buses*/
+		fab_aggre1_noc: fab-aggre1_noc {
+			cell-id = <MSM_BUS_FAB_A1_NOC>;
+			label = "fab-aggre1_noc";
+			qcom,fab-dev;
+			qcom,base-name = "aggre1_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <16384>;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_aggre2_noc: fab-aggre2_noc {
+			cell-id = <MSM_BUS_FAB_A2_NOC>;
+			label = "fab-aggre2_noc";
+			qcom,fab-dev;
+			qcom,base-name = "aggre2_noc-base";
+			qcom,qos-off = <2048>;
+			qcom,base-offset = <12288>;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_camnoc_virt: fab-camnoc_virt {
+			cell-id = <MSM_BUS_FAB_CAMNOC_VIRT>;
+			label = "fab-camnoc_virt";
+			qcom,fab-dev;
+			qcom,base-name = "camnoc_virt-base";
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_config_noc: fab-config_noc {
+			cell-id = <MSM_BUS_FAB_CONFIG_NOC>;
+			label = "fab-config_noc";
+			qcom,fab-dev;
+			qcom,base-name = "config_noc-base";
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_dc_noc: fab-dc_noc {
+			cell-id = <MSM_BUS_FAB_DC_NOC>;
+			label = "fab-dc_noc";
+			qcom,fab-dev;
+			qcom,base-name = "dc_noc-base";
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_gladiator_noc: fab-gladiator_noc {
+			cell-id = <MSM_BUS_FAB_GNOC>;
+			label = "fab-gladiator_noc";
+			qcom,fab-dev;
+			qcom,base-name = "gladiator_noc-base";
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_ipa_virt: fab-ipa_virt {
+			cell-id = <MSM_BUS_FAB_IPA_VIRT>;
+			label = "fab-ipa_virt";
+			qcom,fab-dev;
+			qcom,base-name = "ipa_virt-base";
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_mc_virt: fab-mc_virt {
+			cell-id = <MSM_BUS_FAB_MC_VIRT>;
+			label = "fab-mc_virt";
+			qcom,fab-dev;
+			qcom,base-name = "mc_virt-base";
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_mem_noc: fab-mem_noc {
+			cell-id = <MSM_BUS_FAB_MEM_NOC>;
+			label = "fab-mem_noc";
+			qcom,fab-dev;
+			qcom,base-name = "mem_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <65536>;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_mmss_noc: fab-mmss_noc {
+			cell-id = <MSM_BUS_FAB_MMSS_NOC>;
+			label = "fab-mmss_noc";
+			qcom,fab-dev;
+			qcom,base-name = "mmss_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <36864>;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_system_noc: fab-system_noc {
+			cell-id = <MSM_BUS_FAB_SYS_NOC>;
+			label = "fab-system_noc";
+			qcom,fab-dev;
+			qcom,base-name = "system_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <36864>;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_mc_virt_display: fab-mc_virt_display {
+			cell-id = <MSM_BUS_FAB_MC_VIRT_DISPLAY>;
+			label = "fab-mc_virt_display";
+			qcom,fab-dev;
+			qcom,base-name = "mc_virt-base";
+			qcom,bypass-qos-prg;
+			clocks = <>;
+		};
+
+		fab_mem_noc_display: fab-mem_noc_display {
+			cell-id = <MSM_BUS_FAB_MEM_NOC_DISPLAY>;
+			label = "fab-mem_noc_display";
+			qcom,fab-dev;
+			qcom,base-name = "mem_noc-base";
+			qcom,qos-off = <4096>;
+			qcom,base-offset = <65536>;
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		fab_mmss_noc_display: fab-mmss_noc_display {
+			cell-id = <MSM_BUS_FAB_MMSS_NOC_DISPLAY>;
+			label = "fab-mmss_noc_display";
+			qcom,fab-dev;
+			qcom,base-name = "mmss_noc-base";
+			qcom,bypass-qos-prg;
+			qcom,bus-type = <1>;
+			clocks = <>;
+		};
+
+		/*Masters*/
+
+		mas_qhm_a1noc_cfg: mas-qhm-a1noc-cfg {
+			cell-id = <MSM_BUS_MASTER_A1NOC_CFG>;
+			label = "mas-qhm-a1noc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_aggre1_noc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+		};
+
+		mas_qhm_qup1: mas-qhm-qup1 {
+			cell-id = <MSM_BUS_MASTER_BLSP_1>;
+			label = "mas-qhm-qup1";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,bcms = <&bcm_qup0>;
+		};
+
+		mas_qhm_tsif: mas-qhm-tsif {
+			cell-id = <MSM_BUS_MASTER_TSIF>;
+			label = "mas-qhm-tsif";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+		};
+
+		mas_xm_emmc: mas-xm-emmc {
+			cell-id = <MSM_BUS_MASTER_EMMC>;
+			label = "mas-xm-emmc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <3>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_xm_sdc2: mas-xm-sdc2 {
+			cell-id = <MSM_BUS_MASTER_SDCC_2>;
+			label = "mas-xm-sdc2";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <1>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,ap-owned;
+			qcom,prio = <1>;
+		};
+
+		mas_xm_sdc4: mas-xm-sdc4 {
+			cell-id = <MSM_BUS_MASTER_SDCC_4>;
+			label = "mas-xm-sdc4";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <2>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,ap-owned;
+			qcom,prio = <1>;
+		};
+
+		mas_xm_ufs_mem: mas-xm-ufs-mem {
+			cell-id = <MSM_BUS_MASTER_UFS_MEM>;
+			label = "mas-xm-ufs-mem";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <4>;
+			qcom,connections = <&slv_qns_a1noc_snoc>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+		};
+
+		mas_qhm_a2noc_cfg: mas-qhm-a2noc-cfg {
+			cell-id = <MSM_BUS_MASTER_A2NOC_CFG>;
+			label = "mas-qhm-a2noc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_aggre2_noc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+		};
+
+		mas_qhm_qdss_bam: mas-qhm-qdss-bam {
+			cell-id = <MSM_BUS_MASTER_QDSS_BAM>;
+			label = "mas-qhm-qdss-bam";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+		};
+
+		mas_qhm_qup2: mas-qhm-qup2 {
+			cell-id = <MSM_BUS_MASTER_BLSP_2>;
+			label = "mas-qhm-qup2";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,bcms = <&bcm_qup0>;
+		};
+
+		mas_qnm_cnoc: mas-qnm-cnoc {
+			cell-id = <MSM_BUS_MASTER_CNOC_A2NOC>;
+			label = "mas-qnm-cnoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <2>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
+			qcom,prio = <1>;
+		};
+
+		mas_qxm_crypto: mas-qxm-crypto {
+			cell-id = <MSM_BUS_MASTER_CRYPTO_CORE_0>;
+			label = "mas-qxm-crypto";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <4>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,bcms = <&bcm_ce0>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+		};
+
+		mas_qxm_ipa: mas-qxm-ipa {
+			cell-id = <MSM_BUS_MASTER_IPA>;
+			label = "mas-qxm-ipa";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <6>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+			qcom,defer-init-qos;
+			qcom,node-qos-bcms = <7035 0 1>;
+		};
+
+		mas_xm_qdss_etr: mas-xm-qdss-etr {
+			cell-id = <MSM_BUS_MASTER_QDSS_ETR>;
+			label = "mas-xm-qdss-etr";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <16>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+		};
+
+		mas_xm_usb3_0: mas-xm-usb3-0 {
+			cell-id = <MSM_BUS_MASTER_USB3>;
+			label = "mas-xm-usb3-0";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <22>;
+			qcom,connections = <&slv_qns_a2noc_snoc>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+			qcom,node-qos-clks {
+				clocks =
+				<&clock_gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>;
+				clock-names =
+				"clk-usb3-prim-axi-no-rate";
+			};
+		};
+
+		mas_qxm_camnoc_hf0_uncomp: mas-qxm-camnoc-hf0-uncomp {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_HF0_UNCOMP>;
+			label = "mas-qxm-camnoc-hf0-uncomp";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_camnoc_uncomp>;
+			qcom,bus-dev = <&fab_camnoc_virt>;
+			qcom,bcms = <&bcm_mm1>;
+		};
+
+		mas_qxm_camnoc_hf1_uncomp: mas-qxm-camnoc-hf1-uncomp {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_HF1_UNCOMP>;
+			label = "mas-qxm-camnoc-hf1-uncomp";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_camnoc_uncomp>;
+			qcom,bus-dev = <&fab_camnoc_virt>;
+			qcom,bcms = <&bcm_mm1>;
+		};
+
+		mas_qxm_camnoc_sf_uncomp: mas-qxm-camnoc-sf-uncomp {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_SF_UNCOMP>;
+			label = "mas-qxm-camnoc-sf-uncomp";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_camnoc_uncomp>;
+			qcom,bus-dev = <&fab_camnoc_virt>;
+			qcom,bcms = <&bcm_mm1>;
+		};
+
+		mas_qhm_spdm: mas-qhm-spdm {
+			cell-id = <MSM_BUS_MASTER_SPDM>;
+			label = "mas-qhm-spdm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qns_cnoc_a2noc>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		mas_qhm_tic: mas-qhm-tic {
+			cell-id = <MSM_BUS_MASTER_TIC>;
+			label = "mas-qhm-tic";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_tlmm_south
+				&slv_qhs_camera_cfg &slv_qhs_sdc4
+				&slv_qhs_sdc2 &slv_qhs_mnoc_cfg
+				&slv_qhs_ufs_mem_cfg &slv_qhs_glm
+				&slv_qhs_pdm &slv_qhs_a2_noc_cfg
+				&slv_qhs_qdss_cfg &slv_qhs_display_cfg
+				&slv_qhs_tcsr &slv_qhs_dcc_cfg
+				&slv_qhs_ddrss_cfg &slv_qns_cnoc_a2noc
+				&slv_qhs_snoc_cfg &slv_qhs_phy_refgen_south
+				&slv_qhs_gpuss_cfg &slv_qhs_venus_cfg
+				&slv_qhs_tsif &slv_qhs_compute_dsp_cfg
+				&slv_qhs_aop &slv_qhs_qupv3_north
+				&slv_srvc_cnoc &slv_qhs_usb3_0
+				&slv_qhs_ipa &slv_qhs_cpr_cx
+				&slv_qhs_a1_noc_cfg &slv_qhs_aoss
+				&slv_qhs_prng &slv_qhs_vsense_ctrl_cfg
+				&slv_qhs_emmc_cfg &slv_qhs_qupv3_south
+				&slv_qhs_spdm &slv_qhs_crypto0_cfg
+				&slv_qhs_pimem_cfg &slv_qhs_tlmm_north
+				&slv_qhs_clk_ctl &slv_qhs_imem_cfg>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		mas_qnm_snoc: mas-qnm-snoc {
+			cell-id = <MSM_BUS_SNOC_CNOC_MAS>;
+			label = "mas-qnm-snoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_tlmm_south
+				&slv_qhs_camera_cfg &slv_qhs_sdc4
+				&slv_qhs_sdc2 &slv_qhs_mnoc_cfg
+				&slv_qhs_ufs_mem_cfg &slv_qhs_glm
+				&slv_qhs_pdm &slv_qhs_a2_noc_cfg
+				&slv_qhs_qdss_cfg &slv_qhs_display_cfg
+				&slv_qhs_tcsr &slv_qhs_dcc_cfg
+				&slv_qhs_ddrss_cfg &slv_qhs_snoc_cfg
+				&slv_qhs_phy_refgen_south &slv_qhs_gpuss_cfg
+				&slv_qhs_venus_cfg &slv_qhs_tsif
+				&slv_qhs_compute_dsp_cfg &slv_qhs_aop
+				&slv_qhs_qupv3_north &slv_srvc_cnoc
+				&slv_qhs_usb3_0 &slv_qhs_ipa
+				&slv_qhs_cpr_cx &slv_qhs_a1_noc_cfg
+				&slv_qhs_aoss &slv_qhs_prng
+				&slv_qhs_vsense_ctrl_cfg &slv_qhs_emmc_cfg
+				&slv_qhs_qupv3_south &slv_qhs_spdm
+				&slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg
+				&slv_qhs_tlmm_north &slv_qhs_clk_ctl
+				&slv_qhs_imem_cfg>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		mas_xm_qdss_dap: mas-xm-qdss-dap {
+			cell-id = <MSM_BUS_MASTER_QDSS_DAP>;
+			label = "mas-xm-qdss-dap";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_tlmm_south
+				&slv_qhs_camera_cfg
+				&slv_qhs_sdc4
+				&slv_qhs_sdc2 &slv_qhs_mnoc_cfg
+				&slv_qhs_ufs_mem_cfg &slv_qhs_glm
+				&slv_qhs_pdm &slv_qhs_a2_noc_cfg
+				&slv_qhs_qdss_cfg &slv_qhs_display_cfg
+				&slv_qhs_tcsr &slv_qhs_dcc_cfg
+				&slv_qhs_ddrss_cfg &slv_qns_cnoc_a2noc
+				&slv_qhs_snoc_cfg &slv_qhs_phy_refgen_south
+				&slv_qhs_gpuss_cfg &slv_qhs_venus_cfg
+				&slv_qhs_tsif &slv_qhs_compute_dsp_cfg
+				&slv_qhs_aop &slv_qhs_qupv3_north
+				&slv_srvc_cnoc &slv_qhs_usb3_0
+				&slv_qhs_ipa &slv_qhs_cpr_cx
+				&slv_qhs_a1_noc_cfg &slv_qhs_aoss
+				&slv_qhs_prng &slv_qhs_vsense_ctrl_cfg
+				&slv_qhs_qupv3_south &slv_qhs_spdm
+				&slv_qhs_crypto0_cfg &slv_qhs_pimem_cfg
+				&slv_qhs_tlmm_north &slv_qhs_clk_ctl
+				&slv_qhs_imem_cfg>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		mas_qhm_cnoc: mas-qhm-cnoc {
+			cell-id = <MSM_BUS_MASTER_CNOC_DC_NOC>;
+			label = "mas-qhm-cnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qhs_memnoc &slv_qhs_llcc>;
+			qcom,bus-dev = <&fab_dc_noc>;
+		};
+
+		mas_acm_l3: mas-acm-l3 {
+			cell-id = <MSM_BUS_MASTER_AMPSS_M0>;
+			label = "mas-acm-l3";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_gnoc
+				&slv_qns_gladiator_sodv &slv_qns_gnoc_memnoc>;
+			qcom,bus-dev = <&fab_gladiator_noc>;
+		};
+
+		mas_pm_gnoc_cfg: mas-pm-gnoc-cfg {
+			cell-id = <MSM_BUS_MASTER_GNOC_CFG>;
+			label = "mas-pm-gnoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_gnoc>;
+			qcom,bus-dev = <&fab_gladiator_noc>;
+		};
+
+		mas_ipa_core_master: mas-ipa-core-master {
+			cell-id = <MSM_BUS_MASTER_IPA_CORE>;
+			label = "mas-ipa-core-master";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_ipa_core_slave>;
+			qcom,bus-dev = <&fab_ipa_virt>;
+		};
+
+		mas_llcc_mc: mas-llcc-mc {
+			cell-id = <MSM_BUS_MASTER_LLCC>;
+			label = "mas-llcc-mc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <2>;
+			qcom,connections = <&slv_ebi>;
+			qcom,bus-dev = <&fab_mc_virt>;
+		};
+
+		mas_acm_tcu: mas-acm-tcu {
+			cell-id = <MSM_BUS_MASTER_TCU_0>;
+			label = "mas-acm-tcu";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <0>;
+			qcom,connections = <&slv_qns_apps_io &slv_qns_llcc
+				&slv_qns_memnoc_snoc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,bcms = <&bcm_sh3>;
+			qcom,ap-owned;
+			qcom,prio = <7>;
+		};
+
+		mas_qhm_memnoc_cfg: mas-qhm-memnoc-cfg {
+			cell-id = <MSM_BUS_MASTER_MEM_NOC_CFG>;
+			label = "mas-qhm-memnoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_memnoc
+				&slv_qhs_mdsp_ms_mpu_cfg>;
+			qcom,bus-dev = <&fab_mem_noc>;
+		};
+
+		mas_qnm_apps: mas-qnm-apps {
+			cell-id = <MSM_BUS_MASTER_GNOC_MEM_NOC>;
+			label = "mas-qnm-apps";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,qport = <2 3>;
+			qcom,connections = <&slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,bcms = <&bcm_sh5>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_qnm_mnoc_hf: mas-qnm-mnoc-hf {
+			cell-id = <MSM_BUS_MASTER_MNOC_HF_MEM_NOC>;
+			label = "mas-qnm-mnoc-hf";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,qport = <4 5>;
+			qcom,connections = <&slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qnm_mnoc_sf: mas-qnm-mnoc-sf {
+			cell-id = <MSM_BUS_MASTER_MNOC_SF_MEM_NOC>;
+			label = "mas-qnm-mnoc-sf";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <7>;
+			qcom,connections = <&slv_qns_apps_io
+				 &slv_qns_llcc &slv_qns_memnoc_snoc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qnm_snoc_gc: mas-qnm-snoc-gc {
+			cell-id = <MSM_BUS_MASTER_SNOC_GC_MEM_NOC>;
+			label = "mas-qnm-snoc-gc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <8>;
+			qcom,connections = <&slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+		};
+
+		mas_qnm_snoc_sf: mas-qnm-snoc-sf {
+			cell-id = <MSM_BUS_MASTER_SNOC_SF_MEM_NOC>;
+			label = "mas-qnm-snoc-sf";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <9>;
+			qcom,connections = <&slv_qns_apps_io &slv_qns_llcc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+		};
+
+		mas_qxm_gpu: mas-qxm-gpu {
+			cell-id = <MSM_BUS_MASTER_GRAPHICS_3D>;
+			label = "mas-qxm-gpu";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,qport = <10 11>;
+			qcom,connections = <&slv_qns_apps_io
+				 &slv_qns_llcc &slv_qns_memnoc_snoc>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+		};
+
+		mas_qhm_mnoc_cfg: mas-qhm-mnoc-cfg {
+			cell-id = <MSM_BUS_MASTER_CNOC_MNOC_CFG>;
+			label = "mas-qhm-mnoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_mnoc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+		};
+
+		mas_qxm_camnoc_hf0: mas-qxm-camnoc-hf0 {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_HF0>;
+			label = "mas-qxm-camnoc-hf0";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <1>;
+			qcom,connections = <&slv_qns_mem_noc_hf>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm1>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_camnoc_hf1: mas-qxm-camnoc-hf1 {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_HF1>;
+			label = "mas-qxm-camnoc-hf1";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <2>;
+			qcom,connections = <&slv_qns_mem_noc_hf>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm1>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_camnoc_sf: mas-qxm-camnoc-sf {
+			cell-id = <MSM_BUS_MASTER_CAMNOC_SF>;
+			label = "mas-qxm-camnoc-sf";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <0>;
+			qcom,connections = <&slv_qns2_mem_noc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_mdp0: mas-qxm-mdp0 {
+			cell-id = <MSM_BUS_MASTER_MDP_PORT0>;
+			label = "mas-qxm-mdp0";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <3>;
+			qcom,connections = <&slv_qns_mem_noc_hf>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm1>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_mdp1: mas-qxm-mdp1 {
+			cell-id = <MSM_BUS_MASTER_MDP_PORT1>;
+			label = "mas-qxm-mdp1";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <4>;
+			qcom,connections = <&slv_qns_mem_noc_hf>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm1>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_rot: mas-qxm-rot {
+			cell-id = <MSM_BUS_MASTER_ROTATOR>;
+			label = "mas-qxm-rot";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <5>;
+			qcom,connections = <&slv_qns2_mem_noc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_venus0: mas-qxm-venus0 {
+			cell-id = <MSM_BUS_MASTER_VIDEO_P0>;
+			label = "mas-qxm-venus0";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <6>;
+			qcom,connections = <&slv_qns2_mem_noc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_venus1: mas-qxm-venus1 {
+			cell-id = <MSM_BUS_MASTER_VIDEO_P1>;
+			label = "mas-qxm-venus1";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <7>;
+			qcom,connections = <&slv_qns2_mem_noc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qxm_venus_arm9: mas-qxm-venus-arm9 {
+			cell-id = <MSM_BUS_MASTER_VIDEO_PROC>;
+			label = "mas-qxm-venus-arm9";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <8>;
+			qcom,connections = <&slv_qns2_mem_noc>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,bcms = <&bcm_mm3>;
+			qcom,ap-owned;
+			qcom,prio = <0>;
+			qcom,forwarding;
+			qcom,node-qos-bcms = <7012 0 1>;
+		};
+
+		mas_qhm_snoc_cfg: mas-qhm-snoc-cfg {
+			cell-id = <MSM_BUS_MASTER_SNOC_CFG>;
+			label = "mas-qhm-snoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_srvc_snoc>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		mas_qnm_aggre1_noc: mas-qnm-aggre1-noc {
+			cell-id = <MSM_BUS_A1NOC_SNOC_MAS>;
+			label = "mas-qnm-aggre1-noc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qxs_pimem
+				 &slv_qns_memnoc_sf &slv_qxs_imem
+				 &slv_qhs_apss &slv_qns_cnoc
+				 &slv_xs_qdss_stm>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn8>;
+		};
+
+		mas_qnm_aggre2_noc: mas-qnm-aggre2-noc {
+			cell-id = <MSM_BUS_A2NOC_SNOC_MAS>;
+			label = "mas-qnm-aggre2-noc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qxs_pimem
+				&slv_qns_memnoc_sf &slv_qxs_imem
+				&slv_qhs_apss &slv_qns_cnoc
+				&slv_xs_sys_tcu_cfg &slv_xs_qdss_stm>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn10>;
+		};
+
+		mas_qnm_gladiator_sodv: mas-qnm-gladiator-sodv {
+			cell-id = <MSM_BUS_MASTER_GNOC_SNOC>;
+			label = "mas-qnm-gladiator-sodv";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qxs_pimem
+				&slv_qxs_imem &slv_qhs_apss
+				&slv_qns_cnoc &slv_xs_sys_tcu_cfg
+				&slv_xs_qdss_stm>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn11>;
+		};
+
+		mas_qnm_memnoc: mas-qnm-memnoc {
+			cell-id = <MSM_BUS_MASTER_MEM_NOC_SNOC>;
+			label = "mas-qnm-memnoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,connections = <&slv_qxs_imem
+				&slv_qhs_apss &slv_qxs_pimem
+				&slv_qns_cnoc &slv_xs_qdss_stm>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn13>;
+		};
+
+		mas_qxm_pimem: mas-qxm-pimem {
+			cell-id = <MSM_BUS_MASTER_PIMEM>;
+			label = "mas-qxm-pimem";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <3>;
+			qcom,connections = <&slv_qxs_imem &slv_qns_memnoc_gc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn4>;
+			qcom,ap-owned;
+			qcom,prio = <2>;
+		};
+
+		mas_xm_gic: mas-xm-gic {
+			cell-id = <MSM_BUS_MASTER_GIC>;
+			label = "mas-xm-gic";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <0>;
+			qcom,connections = <&slv_qxs_imem &slv_qns_memnoc_gc>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn11>;
+			qcom,ap-owned;
+			qcom,prio = <1>;
+		};
+
+		mas_alc: mas-alc {
+			cell-id = <MSM_BUS_MASTER_ALC>;
+			label = "mas-alc";
+			qcom,buswidth = <1>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mc_virt>;
+			qcom,bcms = <&bcm_alc>;
+		};
+
+		mas_llcc_mc_display: mas-llcc-mc_display {
+			cell-id = <MSM_BUS_MASTER_LLCC_DISPLAY>;
+			label = "mas-llcc-mc_display";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <2>;
+			qcom,connections = <&slv_ebi_display>;
+			qcom,bus-dev = <&fab_mc_virt_display>;
+		};
+
+		mas_qnm_mnoc_hf_display: mas-qnm-mnoc-hf_display {
+			cell-id = <MSM_BUS_MASTER_MNOC_HF_MEM_NOC_DISPLAY>;
+			label = "mas-qnm-mnoc-hf_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,qport = <4 5>;
+			qcom,connections = <&slv_qns_llcc_display>;
+			qcom,bus-dev = <&fab_mem_noc_display>;
+		};
+
+		mas_qnm_mnoc_sf_display: mas-qnm-mnoc-sf_display {
+			cell-id = <MSM_BUS_MASTER_MNOC_SF_MEM_NOC_DISPLAY>;
+			label = "mas-qnm-mnoc-sf_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <7>;
+			qcom,connections = <&slv_qns_llcc_display>;
+			qcom,bus-dev = <&fab_mem_noc_display>;
+		};
+
+		mas_qxm_mdp0_display: mas-qxm-mdp0_display {
+			cell-id = <MSM_BUS_MASTER_MDP_PORT0_DISPLAY>;
+			label = "mas-qxm-mdp0_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <3>;
+			qcom,connections = <&slv_qns_mem_noc_hf_display>;
+			qcom,bus-dev = <&fab_mmss_noc_display>;
+			qcom,bcms = <&bcm_mm1_display>;
+		};
+
+		mas_qxm_mdp1_display: mas-qxm-mdp1_display {
+			cell-id = <MSM_BUS_MASTER_MDP_PORT1_DISPLAY>;
+			label = "mas-qxm-mdp1_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <4>;
+			qcom,connections = <&slv_qns_mem_noc_hf_display>;
+			qcom,bus-dev = <&fab_mmss_noc_display>;
+			qcom,bcms = <&bcm_mm1_display>;
+		};
+
+		mas_qxm_rot_display: mas-qxm-rot_display {
+			cell-id = <MSM_BUS_MASTER_ROTATOR_DISPLAY>;
+			label = "mas-qxm-rot_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,qport = <5>;
+			qcom,connections = <&slv_qns2_mem_noc_display>;
+			qcom,bus-dev = <&fab_mmss_noc_display>;
+			qcom,bcms = <&bcm_mm3_display>;
+		};
+
+		/*Internal nodes*/
+
+		/*Slaves*/
+
+		slv_qns_a1noc_snoc:slv-qns-a1noc-snoc {
+			cell-id = <MSM_BUS_A1NOC_SNOC_SLV>;
+			label = "slv-qns-a1noc-snoc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,connections = <&mas_qnm_aggre1_noc>;
+		};
+
+		slv_srvc_aggre1_noc:slv-srvc-aggre1-noc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_A1NOC>;
+			label = "slv-srvc-aggre1-noc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_aggre1_noc>;
+			qcom,bcms = <&bcm_sn8>;
+		};
+
+		slv_qns_a2noc_snoc:slv-qns-a2noc-snoc {
+			cell-id = <MSM_BUS_A2NOC_SNOC_SLV>;
+			label = "slv-qns-a2noc-snoc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,connections = <&mas_qnm_aggre2_noc>;
+		};
+
+		slv_srvc_aggre2_noc:slv-srvc-aggre2-noc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_A2NOC>;
+			label = "slv-srvc-aggre2-noc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,bcms = <&bcm_sn10>;
+		};
+
+		slv_qns_camnoc_uncomp:slv-qns-camnoc-uncomp {
+			cell-id = <MSM_BUS_SLAVE_CAMNOC_UNCOMP>;
+			label = "slv-qns-camnoc-uncomp";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_camnoc_virt>;
+		};
+
+		slv_qhs_a1_noc_cfg:slv-qhs-a1-noc-cfg {
+			cell-id = <MSM_BUS_SLAVE_A1NOC_CFG>;
+			label = "slv-qhs-a1-noc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qhm_a1noc_cfg>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_a2_noc_cfg:slv-qhs-a2-noc-cfg {
+			cell-id = <MSM_BUS_SLAVE_A2NOC_CFG>;
+			label = "slv-qhs-a2-noc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qhm_a2noc_cfg>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_aop:slv-qhs-aop {
+			cell-id = <MSM_BUS_SLAVE_AOP>;
+			label = "slv-qhs-aop";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_aoss:slv-qhs-aoss {
+			cell-id = <MSM_BUS_SLAVE_AOSS>;
+			label = "slv-qhs-aoss";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_camera_cfg:slv-qhs-camera-cfg {
+			cell-id = <MSM_BUS_SLAVE_CAMERA_CFG>;
+			label = "slv-qhs-camera-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_clk_ctl:slv-qhs-clk-ctl {
+			cell-id = <MSM_BUS_SLAVE_CLK_CTL>;
+			label = "slv-qhs-clk-ctl";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_compute_dsp_cfg:slv-qhs-compute-dsp-cfg {
+			cell-id = <MSM_BUS_SLAVE_CDSP_CFG>;
+			label = "slv-qhs-compute-dsp-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_cpr_cx:slv-qhs-cpr-cx {
+			cell-id = <MSM_BUS_SLAVE_RBCPR_CX_CFG>;
+			label = "slv-qhs-cpr-cx";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_crypto0_cfg:slv-qhs-crypto0-cfg {
+			cell-id = <MSM_BUS_SLAVE_CRYPTO_0_CFG>;
+			label = "slv-qhs-crypto0-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_dcc_cfg:slv-qhs-dcc-cfg {
+			cell-id = <MSM_BUS_SLAVE_DCC_CFG>;
+			label = "slv-qhs-dcc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qhm_cnoc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_ddrss_cfg:slv-qhs-ddrss-cfg {
+			cell-id = <MSM_BUS_SLAVE_CNOC_DDRSS>;
+			label = "slv-qhs-ddrss-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_display_cfg:slv-qhs-display-cfg {
+			cell-id = <MSM_BUS_SLAVE_DISPLAY_CFG>;
+			label = "slv-qhs-display-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_emmc_cfg:slv-qhs-emmc-cfg {
+			cell-id = <MSM_BUS_SLAVE_EMMC_CFG>;
+			label = "slv-qhs-emmc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_glm:slv-qhs-glm {
+			cell-id = <MSM_BUS_SLAVE_GLM>;
+			label = "slv-qhs-glm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_gpuss_cfg:slv-qhs-gpuss-cfg {
+			cell-id = <MSM_BUS_SLAVE_GRAPHICS_3D_CFG>;
+			label = "slv-qhs-gpuss-cfg";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_imem_cfg:slv-qhs-imem-cfg {
+			cell-id = <MSM_BUS_SLAVE_IMEM_CFG>;
+			label = "slv-qhs-imem-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_ipa:slv-qhs-ipa {
+			cell-id = <MSM_BUS_SLAVE_IPA_CFG>;
+			label = "slv-qhs-ipa";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_mnoc_cfg:slv-qhs-mnoc-cfg {
+			cell-id = <MSM_BUS_SLAVE_CNOC_MNOC_CFG>;
+			label = "slv-qhs-mnoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qhm_mnoc_cfg>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_pdm:slv-qhs-pdm {
+			cell-id = <MSM_BUS_SLAVE_PDM>;
+			label = "slv-qhs-pdm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_phy_refgen_south:slv-qhs-phy-refgen-south {
+			cell-id = <MSM_BUS_SLAVE_SOUTH_PHY_CFG>;
+			label = "slv-qhs-phy-refgen-south";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_pimem_cfg:slv-qhs-pimem-cfg {
+			cell-id = <MSM_BUS_SLAVE_PIMEM_CFG>;
+			label = "slv-qhs-pimem-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_prng:slv-qhs-prng {
+			cell-id = <MSM_BUS_SLAVE_PRNG>;
+			label = "slv-qhs-prng";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_qdss_cfg:slv-qhs-qdss-cfg {
+			cell-id = <MSM_BUS_SLAVE_QDSS_CFG>;
+			label = "slv-qhs-qdss-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_qupv3_north:slv-qhs-qupv3-north {
+			cell-id = <MSM_BUS_SLAVE_BLSP_2>;
+			label = "slv-qhs-qupv3-north";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_qupv3_south:slv-qhs-qupv3-south {
+			cell-id = <MSM_BUS_SLAVE_BLSP_1>;
+			label = "slv-qhs-qupv3-south";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_sdc2:slv-qhs-sdc2 {
+			cell-id = <MSM_BUS_SLAVE_SDCC_2>;
+			label = "slv-qhs-sdc2";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_sdc4:slv-qhs-sdc4 {
+			cell-id = <MSM_BUS_SLAVE_SDCC_4>;
+			label = "slv-qhs-sdc4";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_snoc_cfg:slv-qhs-snoc-cfg {
+			cell-id = <MSM_BUS_SLAVE_SNOC_CFG>;
+			label = "slv-qhs-snoc-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qhm_snoc_cfg>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_spdm:slv-qhs-spdm {
+			cell-id = <MSM_BUS_SLAVE_SPDM_WRAPPER>;
+			label = "slv-qhs-spdm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_tcsr:slv-qhs-tcsr {
+			cell-id = <MSM_BUS_SLAVE_TCSR>;
+			label = "slv-qhs-tcsr";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_tlmm_north:slv-qhs-tlmm-north {
+			cell-id = <MSM_BUS_SLAVE_TLMM_NORTH>;
+			label = "slv-qhs-tlmm-north";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_tlmm_south:slv-qhs-tlmm-south {
+			cell-id = <MSM_BUS_SLAVE_TLMM_SOUTH>;
+			label = "slv-qhs-tlmm-south";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_tsif:slv-qhs-tsif {
+			cell-id = <MSM_BUS_SLAVE_TSIF>;
+			label = "slv-qhs-tsif";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_ufs_mem_cfg:slv-qhs-ufs-mem-cfg {
+			cell-id = <MSM_BUS_SLAVE_UFS_MEM_CFG>;
+			label = "slv-qhs-ufs-mem-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_usb3_0:slv-qhs-usb3-0 {
+			cell-id = <MSM_BUS_SLAVE_USB3>;
+			label = "slv-qhs-usb3-0";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_venus_cfg:slv-qhs-venus-cfg {
+			cell-id = <MSM_BUS_SLAVE_VENUS_CFG>;
+			label = "slv-qhs-venus-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_vsense_ctrl_cfg:slv-qhs-vsense-ctrl-cfg {
+			cell-id = <MSM_BUS_SLAVE_VSENSE_CTRL_CFG>;
+			label = "slv-qhs-vsense-ctrl-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qns_cnoc_a2noc:slv-qns-cnoc-a2noc {
+			cell-id = <MSM_BUS_SLAVE_CNOC_A2NOC>;
+			label = "slv-qns-cnoc-a2noc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,connections = <&mas_qnm_cnoc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_srvc_cnoc:slv-srvc-cnoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_CNOC>;
+			label = "slv-srvc-cnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_config_noc>;
+			qcom,bcms = <&bcm_cn0>;
+		};
+
+		slv_qhs_llcc:slv-qhs-llcc {
+			cell-id = <MSM_BUS_SLAVE_LLCC_CFG>;
+			label = "slv-qhs-llcc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_dc_noc>;
+		};
+
+		slv_qhs_memnoc:slv-qhs-memnoc {
+			cell-id = <MSM_BUS_SLAVE_MEM_NOC_CFG>;
+			label = "slv-qhs-memnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_dc_noc>;
+			qcom,connections = <&mas_qhm_memnoc_cfg>;
+		};
+
+		slv_qns_gladiator_sodv:slv-qns-gladiator-sodv {
+			cell-id = <MSM_BUS_SLAVE_GNOC_SNOC>;
+			label = "slv-qns-gladiator-sodv";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_gladiator_noc>;
+			qcom,connections = <&mas_qnm_gladiator_sodv>;
+		};
+
+		slv_qns_gnoc_memnoc:slv-qns-gnoc-memnoc {
+			cell-id = <MSM_BUS_SLAVE_GNOC_MEM_NOC>;
+			label = "slv-qns-gnoc-memnoc";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_gladiator_noc>;
+			qcom,connections = <&mas_qnm_apps>;
+		};
+
+		slv_srvc_gnoc:slv-srvc-gnoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_GNOC>;
+			label = "slv-srvc-gnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_gladiator_noc>;
+		};
+
+		slv_ipa_core_slave:slv-ipa-core-slave {
+			cell-id = <MSM_BUS_SLAVE_IPA_CORE>;
+			label = "slv-ipa-core-slave";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_ipa_virt>;
+			qcom,bcms = <&bcm_ip0>;
+		};
+
+		slv_ebi:slv-ebi {
+			cell-id = <MSM_BUS_SLAVE_EBI_CH0>;
+			label = "slv-ebi";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mc_virt>;
+			qcom,bcms = <&bcm_mc0>, <&bcm_acv>;
+		};
+
+		slv_qhs_mdsp_ms_mpu_cfg:slv-qhs-mdsp-ms-mpu-cfg {
+			cell-id = <MSM_BUS_SLAVE_MSS_PROC_MS_MPU_CFG>;
+			label = "slv-qhs-mdsp-ms-mpu-cfg";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+		};
+
+		slv_qns_apps_io:slv-qns-apps-io {
+			cell-id = <MSM_BUS_SLAVE_MEM_NOC_GNOC>;
+			label = "slv-qns-apps-io";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,bcms = <&bcm_sh1>;
+		};
+
+		slv_qns_llcc:slv-qns-llcc {
+			cell-id = <MSM_BUS_SLAVE_LLCC>;
+			label = "slv-qns-llcc";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,connections = <&mas_llcc_mc>;
+			qcom,bcms = <&bcm_sh0>;
+		};
+
+		slv_qns_memnoc_snoc:slv-qns-memnoc-snoc {
+			cell-id = <MSM_BUS_SLAVE_MEM_NOC_SNOC>;
+			label = "slv-qns-memnoc-snoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+			qcom,connections = <&mas_qnm_memnoc>;
+			qcom,bcms = <&bcm_sh2>;
+		};
+
+		slv_srvc_memnoc:slv-srvc-memnoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_MEM_NOC>;
+			label = "slv-srvc-memnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mem_noc>;
+		};
+
+		slv_qns2_mem_noc:slv-qns2-mem-noc {
+			cell-id = <MSM_BUS_SLAVE_MNOC_SF_MEM_NOC>;
+			label = "slv-qns2-mem-noc";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,connections = <&mas_qnm_mnoc_sf>;
+			qcom,bcms = <&bcm_mm2>;
+		};
+
+		slv_qns_mem_noc_hf:slv-qns-mem-noc-hf {
+			cell-id = <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC>;
+			label = "slv-qns-mem-noc-hf";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+			qcom,connections = <&mas_qnm_mnoc_hf>;
+			qcom,bcms = <&bcm_mm0>;
+		};
+
+		slv_srvc_mnoc:slv-srvc-mnoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_MNOC>;
+			label = "slv-srvc-mnoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mmss_noc>;
+		};
+
+		slv_qhs_apss:slv-qhs-apss {
+			cell-id = <MSM_BUS_SLAVE_APPSS>;
+			label = "slv-qhs-apss";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		slv_qns_cnoc:slv-qns-cnoc {
+			cell-id = <MSM_BUS_SNOC_CNOC_SLV>;
+			label = "slv-qns-cnoc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_snoc>;
+			qcom,bcms = <&bcm_sn3>;
+		};
+
+		slv_qns_memnoc_gc:slv-qns-memnoc-gc {
+			cell-id = <MSM_BUS_SLAVE_SNOC_MEM_NOC_GC>;
+			label = "slv-qns-memnoc-gc";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_snoc_gc>;
+			qcom,bcms = <&bcm_sn2>;
+		};
+
+		slv_qns_memnoc_sf:slv-qns-memnoc-sf {
+			cell-id = <MSM_BUS_SLAVE_SNOC_MEM_NOC_SF>;
+			label = "slv-qns-memnoc-sf";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,connections = <&mas_qnm_snoc_sf>;
+			qcom,bcms = <&bcm_sn0>;
+		};
+
+		slv_qxs_imem:slv-qxs-imem {
+			cell-id = <MSM_BUS_SLAVE_OCIMEM>;
+			label = "slv-qxs-imem";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn1>;
+		};
+
+		slv_qxs_pimem:slv-qxs-pimem {
+			cell-id = <MSM_BUS_SLAVE_PIMEM>;
+			label = "slv-qxs-pimem";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn4>;
+		};
+
+		slv_srvc_snoc:slv-srvc-snoc {
+			cell-id = <MSM_BUS_SLAVE_SERVICE_SNOC>;
+			label = "slv-srvc-snoc";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		slv_xs_qdss_stm:slv-xs-qdss-stm {
+			cell-id = <MSM_BUS_SLAVE_QDSS_STM>;
+			label = "slv-xs-qdss-stm";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+			qcom,bcms = <&bcm_sn5>;
+		};
+
+		slv_xs_sys_tcu_cfg:slv-xs-sys-tcu-cfg {
+			cell-id = <MSM_BUS_SLAVE_TCU>;
+			label = "slv-xs-sys-tcu-cfg";
+			qcom,buswidth = <8>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_system_noc>;
+		};
+
+		slv_ebi_display:slv-ebi_display {
+			cell-id = <MSM_BUS_SLAVE_EBI_CH0_DISPLAY>;
+			label = "slv-ebi_display";
+			qcom,buswidth = <4>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mc_virt_display>;
+			qcom,bcms = <&bcm_mc0_display>;
+		};
+
+		slv_qns_llcc_display:slv-qns-llcc_display {
+			cell-id = <MSM_BUS_SLAVE_LLCC_DISPLAY>;
+			label = "slv-qns-llcc_display";
+			qcom,buswidth = <16>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mem_noc_display>;
+			qcom,connections = <&mas_llcc_mc_display>;
+			qcom,bcms = <&bcm_sh0_display>;
+		};
+
+		slv_qns2_mem_noc_display:slv-qns2-mem-noc_display {
+			cell-id = <MSM_BUS_SLAVE_MNOC_SF_MEM_NOC_DISPLAY>;
+			label = "slv-qns2-mem-noc_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <1>;
+			qcom,bus-dev = <&fab_mmss_noc_display>;
+			qcom,connections = <&mas_qnm_mnoc_sf_display>;
+			qcom,bcms = <&bcm_mm2_display>;
+		};
+
+		slv_qns_mem_noc_hf_display:slv-qns-mem-noc-hf_display {
+			cell-id = <MSM_BUS_SLAVE_MNOC_HF_MEM_NOC_DISPLAY>;
+			label = "slv-qns-mem-noc-hf_display";
+			qcom,buswidth = <32>;
+			qcom,agg-ports = <2>;
+			qcom,bus-dev = <&fab_mmss_noc_display>;
+			qcom,connections = <&mas_qnm_mnoc_hf_display>;
+			qcom,bcms = <&bcm_mm0_display>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi
new file mode 100644
index 0000000..18b0cd8
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-360camera.dtsi
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash0 &pm660l_flash1>;
+		torch-source = <&pm660l_torch0 &pm660l_torch1>;
+		switch-source = <&pm660l_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@1 {
+		cell-index = <1>;
+		reg = <0x01 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash2>;
+		torch-source = <&pm660l_torch2>;
+		switch-source = <&pm660l_switch1>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+	};
+
+	camera_ldo: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "camera_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_rear_ldo: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "camera_rear_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_rear_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_vio_ldo: gpio-regulator@3 {
+		compatible = "regulator-fixed";
+		reg = <0x03 0x00>;
+		regulator-name = "camera_vio_ldo";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 29 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vio>;
+		vin-supply = <&pm660_s4>;
+	};
+
+	camera_vana_ldo: gpio-regulator@4 {
+		compatible = "regulator-fixed";
+		reg = <0x04 0x00>;
+		regulator-name = "camera_vana_ldo";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 8 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vana>;
+		vin-supply = <&pm660l_bob>;
+	};
+};
+
+&cam_cci {
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_front: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	ois_rear: qcom,ois@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,ois";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+		status = "disabled";
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		ois-src = <&ois_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		eeprom-src = <&eeprom_rear_aux>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		actuator-src = <&actuator_front>;
+		led-flash-src = <&led_flash_front>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-cdp.dtsi
new file mode 100644
index 0000000..8b94ca2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-cdp.dtsi
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash0 &pm660l_flash1>;
+		torch-source = <&pm660l_torch0 &pm660l_torch1>;
+		switch-source = <&pm660l_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@1 {
+		cell-index = <1>;
+		reg = <0x01 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash2>;
+		torch-source = <&pm660l_torch2>;
+		switch-source = <&pm660l_switch1>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+	};
+
+	camera_ldo: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "camera_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 3 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_rear_ldo: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "camera_rear_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_rear_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_vio_ldo: gpio-regulator@3 {
+		compatible = "regulator-fixed";
+		reg = <0x03 0x00>;
+		regulator-name = "camera_vio_ldo";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 29 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vio>;
+		vin-supply = <&pm660_s4>;
+	};
+
+	camera_vana_ldo: gpio-regulator@4 {
+		compatible = "regulator-fixed";
+		reg = <0x04 0x00>;
+		regulator-name = "camera_vana_ldo";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 8 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vana>;
+		vin-supply = <&pm660l_bob>;
+	};
+};
+
+&cam_cci {
+	qcom,cam-res-mgr {
+		compatible = "qcom,cam-res-mgr";
+		status = "ok";
+	};
+
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_front: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	ois_rear: qcom,ois@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,ois";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+		status = "disabled";
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		ois-src = <&ois_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		eeprom-src = <&eeprom_rear_aux>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		actuator-src = <&actuator_front>;
+		led-flash-src = <&led_flash_front>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-mtp.dtsi
new file mode 100644
index 0000000..8b94ca2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-mtp.dtsi
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash0 &pm660l_flash1>;
+		torch-source = <&pm660l_torch0 &pm660l_torch1>;
+		switch-source = <&pm660l_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@1 {
+		cell-index = <1>;
+		reg = <0x01 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash2>;
+		torch-source = <&pm660l_torch2>;
+		switch-source = <&pm660l_switch1>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+	};
+
+	camera_ldo: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "camera_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 3 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_rear_ldo: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "camera_rear_ldo";
+		regulator-min-microvolt = <1352000>;
+		regulator-max-microvolt = <1352000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_rear_dvdd_en_default>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	camera_vio_ldo: gpio-regulator@3 {
+		compatible = "regulator-fixed";
+		reg = <0x03 0x00>;
+		regulator-name = "camera_vio_ldo";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 29 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vio>;
+		vin-supply = <&pm660_s4>;
+	};
+
+	camera_vana_ldo: gpio-regulator@4 {
+		compatible = "regulator-fixed";
+		reg = <0x04 0x00>;
+		regulator-name = "camera_vana_ldo";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 8 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&cam_sensor_rear_vana>;
+		vin-supply = <&pm660l_bob>;
+	};
+};
+
+&cam_cci {
+	qcom,cam-res-mgr {
+		compatible = "qcom,cam-res-mgr";
+		status = "ok";
+	};
+
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_front: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	ois_rear: qcom,ois@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,ois";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+		status = "disabled";
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk", "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		ois-src = <&ois_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		eeprom-src = <&eeprom_rear_aux>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1352000 1800000 2850000 0>;
+		rgltr-max-voltage = <1352000 1800000 2850000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		actuator-src = <&actuator_front>;
+		led-flash-src = <&led_flash_front>;
+		cam_vio-supply = <&camera_vio_ldo>;
+		cam_vana-supply = <&camera_vana_ldo>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1800000 2850000 1352000 0>;
+		rgltr-max-voltage = <1800000 2850000 1352000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi
new file mode 100644
index 0000000..6506f98
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera-sensor-qrd.dtsi
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pm660l_flash0 &pm660l_flash1>;
+		torch-source = <&pm660l_torch0 &pm660l_torch1>;
+		switch-source = <&pm660l_switch0>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+		vin-supply = <&pm660l_bob>;
+	};
+
+	cam_avdd_gpio_regulator: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "cam_avdd_gpio_regulator";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&tlmm 100 0>;
+		vin-supply = <&pm660l_bob>;
+	};
+
+	cam_dvdd_gpio_regulator: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "cam_dvdd_gpio_regulator";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 4 0>;
+		vin-supply = <&pm660_s6>;
+	};
+
+	cam_iovdd_gpio_regulator: gpio-regulator@3 {
+		compatible = "regulator-fixed";
+		reg = <0x03 0x00>;
+		regulator-name = "cam_iovdd_gpio_regulator";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 29 0>;
+		vin-supply = <&pm660_s4>;
+	};
+
+	cam_rear_avdd_gpio_regulator: gpio-regulator@4 {
+		compatible = "regulator-fixed";
+		reg = <0x04 0x00>;
+		regulator-name = "cam_rear_avdd_gpio_regulator";
+		regulator-min-microvolt = <2850000>;
+		regulator-max-microvolt = <2850000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&tlmm 8 0>;
+		vin-supply = <&pm660l_bob>;
+	};
+
+	cam_rear_dvdd_gpio_regulator: gpio-regulator@5 {
+		compatible = "regulator-fixed";
+		reg = <0x05 0x00>;
+		regulator-name = "cam_rear_dvdd_gpio_regulator";
+		regulator-min-microvolt = <1200000>;
+		regulator-max-microvolt = <1200000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm660l_gpios 3 0>;
+		vin-supply = <&pm660_s6>;
+	};
+};
+
+&tlmm {
+	cam_sensor_rear_active: cam_sensor_rear_active {
+		/* RESET */
+		mux {
+			pins = "gpio30";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio30";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+
+	cam_sensor_rear_suspend: cam_sensor_rear_suspend {
+		/* RESET */
+		mux {
+			pins = "gpio30";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio30";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+
+	cam_sensor_rear2_active: cam_sensor_rear2_active {
+		/* RESET */
+		mux {
+			pins = "gpio9";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio9";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+
+	cam_sensor_rear2_suspend: cam_sensor_rear2_suspend {
+		/* RESET */
+		mux {
+			pins = "gpio9";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio9";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+
+	cam_sensor_front_active: cam_sensor_front_active {
+		/* RESET */
+		mux {
+			pins = "gpio28";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio28";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+
+	cam_sensor_front_suspend: cam_sensor_front_suspend {
+		/* RESET */
+		mux {
+			pins = "gpio28";
+			function = "gpio";
+		};
+
+		config {
+			pins = "gpio28";
+			bias-disable; /* No PULL */
+			drive-strength = <2>; /* 2 MA */
+		};
+	};
+};
+
+&cam_cci {
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_rear_aux: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_front: qcom,actuator@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_rear_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_rear_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-position = <1>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_rear_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_rear_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 30 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear_aux>;
+		eeprom-src = <&eeprom_rear_aux>;
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				&cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				&cam_sensor_front_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		actuator-src = <&actuator_front>;
+		cam_vio-supply = <&cam_iovdd_gpio_regulator>;
+		cam_vana-supply = <&cam_avdd_gpio_regulator>;
+		cam_vdig-supply = <&cam_dvdd_gpio_regulator>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 0 0 0>;
+		rgltr-max-voltage = <0 0 0 0>;
+		rgltr-load-current = <0 0 0 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>;
+		gpio-reset = <1>;
+		gpio-req-tbl-num = <0 1>;
+		gpio-req-tbl-flags = <1 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
+
+&pm660l_gpios {
+	gpio@c300 { /* GPIO4 -CAMERA SENSOR 1/2 VDIG*/
+		qcom,mode = <1>;                /* Output */
+		qcom,pull = <5>;                /* No Pull */
+		qcom,vin-sel = <0>;             /* VIN1 GPIO_LV */
+		qcom,src-sel = <0>;             /* GPIO */
+		qcom,invert = <0>;              /* Invert */
+		qcom,master-en = <1>;           /* Enable GPIO */
+		status = "ok";
+	};
+
+	gpio@c200 { /* GPIO3 -CAMERA SENSOR 0 VDIG*/
+		qcom,mode = <1>;                /* Output */
+		qcom,pull = <5>;                /* No Pull */
+		qcom,vin-sel = <0>;             /* VIN1 GPIO_LV */
+		qcom,src-sel = <0>;             /* GPIO */
+		qcom,invert = <0>;              /* Invert */
+		qcom,master-en = <1>;           /* Enable GPIO */
+		status = "ok";
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
new file mode 100644
index 0000000..34b8740
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-camera.dtsi
@@ -0,0 +1,1065 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	qcom,cam-req-mgr {
+		compatible = "qcom,cam-req-mgr";
+		status = "ok";
+	};
+
+	cam_csiphy0: qcom,csiphy@ac65000 {
+		cell-index = <0>;
+		compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+		reg = <0x0ac65000 0x1000>;
+		reg-names = "csiphy";
+		reg-cam-base = <0x65000>;
+		interrupts = <0 477 0>;
+		interrupt-names = "csiphy";
+		regulator-names = "gdscr", "refgen";
+		gdscr-supply = <&titan_top_gdsc>;
+		refgen-supply = <&refgen>;
+		csi-vdd-voltage = <1200000>;
+		mipi-csi-vdd-supply = <&pm660_l1>;
+		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSIPHY0_CLK>,
+			<&clock_camcc CAM_CC_CSI0PHYTIMER_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSI0PHYTIMER_CLK>;
+		clock-names = "camnoc_axi_clk",
+			"soc_ahb_clk",
+			"slow_ahb_src_clk",
+			"cpas_ahb_clk",
+			"cphy_rx_clk_src",
+			"csiphy0_clk",
+			"csi0phytimer_clk_src",
+			"csi0phytimer_clk";
+		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 384000000 0 269333333 0>;
+		status = "ok";
+	};
+
+	cam_csiphy1: qcom,csiphy@ac66000{
+		cell-index = <1>;
+		compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+		reg = <0xac66000 0x1000>;
+		reg-names = "csiphy";
+		reg-cam-base = <0x66000>;
+		interrupts = <0 478 0>;
+		interrupt-names = "csiphy";
+		regulator-names = "gdscr", "refgen";
+		gdscr-supply = <&titan_top_gdsc>;
+		refgen-supply = <&refgen>;
+		csi-vdd-voltage = <1200000>;
+		mipi-csi-vdd-supply = <&pm660_l1>;
+		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSIPHY1_CLK>,
+			<&clock_camcc CAM_CC_CSI1PHYTIMER_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSI1PHYTIMER_CLK>;
+		clock-names = "camnoc_axi_clk",
+			"soc_ahb_clk",
+			"slow_ahb_src_clk",
+			"cpas_ahb_clk",
+			"cphy_rx_clk_src",
+			"csiphy1_clk",
+			"csi1phytimer_clk_src",
+			"csi1phytimer_clk";
+		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 384000000 0 269333333 0>;
+
+		status = "ok";
+	};
+
+	cam_csiphy2: qcom,csiphy@ac67000 {
+		cell-index = <2>;
+		compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
+		reg = <0xac67000 0x1000>;
+		reg-names = "csiphy";
+		reg-cam-base = <0x67000>;
+		interrupts = <0 479 0>;
+		interrupt-names = "csiphy";
+		regulator-names = "gdscr", "refgen";
+		gdscr-supply = <&titan_top_gdsc>;
+		refgen-supply = <&refgen>;
+		csi-vdd-voltage = <1200000>;
+		mipi-csi-vdd-supply = <&pm660_l1>;
+		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSIPHY2_CLK>,
+			<&clock_camcc CAM_CC_CSI2PHYTIMER_CLK_SRC>,
+			<&clock_camcc CAM_CC_CSI2PHYTIMER_CLK>;
+		clock-names = "camnoc_axi_clk",
+			"soc_ahb_clk",
+			"slow_ahb_src_clk",
+			"cpas_ahb_clk",
+			"cphy_rx_clk_src",
+			"csiphy2_clk",
+			"csi2phytimer_clk_src",
+			"csi2phytimer_clk";
+		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 384000000 0 269333333 0>;
+		status = "ok";
+	};
+
+	cam_cci: qcom,cci@ac4a000 {
+		cell-index = <0>;
+		compatible = "qcom,cci";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		reg = <0xac4a000 0x4000>;
+		reg-names = "cci";
+		reg-cam-base = <0x4a000>;
+		interrupt-names = "cci";
+		interrupts = <0 460 0>;
+		status = "ok";
+		gdscr-supply = <&titan_top_gdsc>;
+		regulator-names = "gdscr";
+		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CCI_CLK>,
+			<&clock_camcc CAM_CC_CCI_CLK_SRC>;
+		clock-names = "camnoc_axi_clk",
+			"soc_ahb_clk",
+			"slow_ahb_src_clk",
+			"cpas_ahb_clk",
+			"cci_clk",
+			"cci_clk_src";
+		src-clock-name = "cci_clk_src";
+		clock-cntl-level = "lowsvs";
+		clock-rates = <0 0 0 0 0 37500000>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cci0_active &cci1_active>;
+		pinctrl-1 = <&cci0_suspend &cci1_suspend>;
+		gpios = <&tlmm 17 0>,
+			<&tlmm 18 0>,
+			<&tlmm 19 0>,
+			<&tlmm 20 0>;
+		gpio-req-tbl-num = <0 1 2 3>;
+		gpio-req-tbl-flags = <1 1 1 1>;
+		gpio-req-tbl-label = "CCI_I2C_DATA0",
+					"CCI_I2C_CLK0",
+					"CCI_I2C_DATA1",
+					"CCI_I2C_CLK1";
+
+		i2c_freq_100Khz: qcom,i2c_standard_mode {
+			hw-thigh = <201>;
+			hw-tlow = <174>;
+			hw-tsu-sto = <204>;
+			hw-tsu-sta = <231>;
+			hw-thd-dat = <22>;
+			hw-thd-sta = <162>;
+			hw-tbuf = <227>;
+			hw-scl-stretch-en = <0>;
+			hw-trdhld = <6>;
+			hw-tsp = <3>;
+			cci-clk-src = <37500000>;
+			status = "ok";
+		};
+
+		i2c_freq_400Khz: qcom,i2c_fast_mode {
+			hw-thigh = <38>;
+			hw-tlow = <56>;
+			hw-tsu-sto = <40>;
+			hw-tsu-sta = <40>;
+			hw-thd-dat = <22>;
+			hw-thd-sta = <35>;
+			hw-tbuf = <62>;
+			hw-scl-stretch-en = <0>;
+			hw-trdhld = <6>;
+			hw-tsp = <3>;
+			cci-clk-src = <37500000>;
+			status = "ok";
+		};
+
+		i2c_freq_custom: qcom,i2c_custom_mode {
+			hw-thigh = <38>;
+			hw-tlow = <56>;
+			hw-tsu-sto = <40>;
+			hw-tsu-sta = <40>;
+			hw-thd-dat = <22>;
+			hw-thd-sta = <35>;
+			hw-tbuf = <62>;
+			hw-scl-stretch-en = <1>;
+			hw-trdhld = <6>;
+			hw-tsp = <3>;
+			cci-clk-src = <37500000>;
+			status = "ok";
+		};
+
+		i2c_freq_1Mhz: qcom,i2c_fast_plus_mode {
+			hw-thigh = <16>;
+			hw-tlow = <22>;
+			hw-tsu-sto = <17>;
+			hw-tsu-sta = <18>;
+			hw-thd-dat = <16>;
+			hw-thd-sta = <15>;
+			hw-tbuf = <24>;
+			hw-scl-stretch-en = <0>;
+			hw-trdhld = <3>;
+			hw-tsp = <3>;
+			cci-clk-src = <37500000>;
+			status = "ok";
+		};
+	};
+
+	qcom,cam_smmu {
+		compatible = "qcom,msm-cam-smmu";
+		status = "ok";
+
+		msm_cam_smmu_ife {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x808 0x0>,
+				<&apps_smmu 0x810 0x8>,
+				<&apps_smmu 0xc08 0x0>,
+				<&apps_smmu 0xc10 0x8>;
+			label = "ife";
+			ife_iova_mem_map: iova-mem-map {
+				/* IO region is approximately 3.4 GB */
+				iova-mem-region-io {
+					iova-region-name = "io";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0xd8c00000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+
+		msm_cam_smmu_jpeg {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x1060 0x8>,
+				<&apps_smmu 0x1068 0x8>;
+			label = "jpeg";
+			jpeg_iova_mem_map: iova-mem-map {
+				/* IO region is approximately 3.4 GB */
+				iova-mem-region-io {
+					iova-region-name = "io";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0xd8c00000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+
+		msm_cam_icp_fw {
+			compatible = "qcom,msm-cam-smmu-fw-dev";
+			label="icp";
+			memory-region = <&pil_camera_mem>;
+		};
+
+		msm_cam_smmu_icp {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x107A 0x0>,
+				<&apps_smmu 0x1020 0x8>,
+				<&apps_smmu 0x1040 0x8>,
+				<&apps_smmu 0x1030 0x0>,
+				<&apps_smmu 0x1050 0x0>;
+			label = "icp";
+			icp_iova_mem_map: iova-mem-map {
+				iova-mem-region-firmware {
+					/* Firmware region is 5MB */
+					iova-region-name = "firmware";
+					iova-region-start = <0x0>;
+					iova-region-len = <0x500000>;
+					iova-region-id = <0x0>;
+					status = "ok";
+				};
+
+				iova-mem-region-shared {
+					/* Shared region is 100MB long */
+					iova-region-name = "shared";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0x6400000>;
+					iova-region-id = <0x1>;
+					iova-granularity = <0x15>;
+					status = "ok";
+				};
+
+				iova-mem-region-secondary-heap {
+					/* Secondary heap region is 1MB long */
+					iova-region-name = "secheap";
+					iova-region-start = <0xd800000>;
+					iova-region-len = <0x100000>;
+					iova-region-id = <0x4>;
+					status = "ok";
+				};
+
+				iova-mem-region-io {
+					/* IO region is approximately 3.3 GB */
+					iova-region-name = "io";
+					iova-region-start = <0xd900000>;
+					iova-region-len = <0xd2700000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+
+		msm_cam_smmu_cpas_cdm {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x1000 0x0>;
+			label = "cpas-cdm0";
+			cpas_cdm_iova_mem_map: iova-mem-map {
+				iova-mem-region-io {
+					/* IO region is approximately 3.4 GB */
+					iova-region-name = "io";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0xd8c00000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+
+		msm_cam_smmu_secure {
+			compatible = "qcom,msm-cam-smmu-cb";
+			label = "cam-secure";
+			qcom,secure-cb;
+		};
+
+		msm_cam_smmu_fd {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x1070 0x0>;
+			label = "fd";
+			fd_iova_mem_map: iova-mem-map {
+				iova-mem-region-io {
+					/* IO region is approximately 3.4 GB */
+					iova-region-name = "io";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0xd8c00000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+	};
+
+	qcom,cam-cpas@ac40000 {
+		cell-index = <0>;
+		compatible = "qcom,cam-cpas";
+		label = "cpas";
+		arch-compat = "cpas_top";
+		status = "ok";
+		reg-names = "cam_cpas_top", "cam_camnoc";
+		reg = <0xac40000 0x1000>,
+			<0xac42000 0x5000>;
+		reg-cam-base = <0x40000 0x42000>;
+		interrupt-names = "cpas_camnoc";
+		interrupts = <0 459 0>;
+		qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */
+		regulator-names = "camss-vdd";
+		camss-vdd-supply = <&titan_top_gdsc>;
+		clock-names = "gcc_ahb_clk",
+			"gcc_axi_clk",
+			"soc_ahb_clk",
+			"slow_ahb_clk_src",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+		src-clock-name = "slow_ahb_clk_src";
+		clock-rates = <0 0 0 0 0 0>,
+			<0 0 0 19200000 0 0>,
+			<0 0 0 80000000 0 0>,
+			<0 0 0 80000000 0 0>,
+			<0 0 0 80000000 0 0>,
+			<0 0 0 80000000 0 0>,
+			<0 0 0 80000000 0 0>;
+		clock-cntl-level = "suspend", "minsvs", "lowsvs", "svs",
+			"svs_l1", "nominal", "turbo";
+		qcom,msm-bus,name = "cam_ahb";
+		qcom,msm-bus,num-cases = <7>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 0>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 76500>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 120000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>;
+		vdd-corners = <RPMH_REGULATOR_LEVEL_OFF
+			RPMH_REGULATOR_LEVEL_RETENTION
+			RPMH_REGULATOR_LEVEL_MIN_SVS
+			RPMH_REGULATOR_LEVEL_LOW_SVS
+			RPMH_REGULATOR_LEVEL_SVS
+			RPMH_REGULATOR_LEVEL_SVS_L1
+			RPMH_REGULATOR_LEVEL_NOM
+			RPMH_REGULATOR_LEVEL_NOM_L1
+			RPMH_REGULATOR_LEVEL_NOM_L2
+			RPMH_REGULATOR_LEVEL_TURBO
+			RPMH_REGULATOR_LEVEL_TURBO_L1>;
+		vdd-corner-ahb-mapping = "suspend", "suspend",
+			"minsvs", "lowsvs", "svs", "svs_l1",
+			"nominal", "nominal", "nominal",
+			"turbo", "turbo";
+		client-id-based;
+		client-names =
+			"csiphy0", "csiphy1", "csiphy2", "cci0",
+			"csid0", "csid1", "csid2",
+			"ife0", "ife1", "ife2", "ipe0",
+			"ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0",
+			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0";
+		client-axi-port-names =
+			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
+			"cam_hf_1", "cam_hf_2", "cam_hf_2",
+			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
+			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
+			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1";
+		client-bus-camnoc-based;
+		qcom,axi-port-list {
+			qcom,axi-port1 {
+				qcom,axi-port-name = "cam_hf_1";
+				qcom,axi-port-mnoc {
+					qcom,msm-bus,name = "cam_hf_1_mnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_HF0
+					MSM_BUS_SLAVE_EBI_CH0 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_HF0
+					MSM_BUS_SLAVE_EBI_CH0 0 0>;
+				};
+				qcom,axi-port-camnoc {
+					qcom,msm-bus,name = "cam_hf_1_camnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_HF0_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_HF0_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>;
+				};
+			};
+			qcom,axi-port2 {
+				qcom,axi-port-name = "cam_hf_2";
+				qcom,axi-port-mnoc {
+					qcom,msm-bus,name = "cam_hf_2_mnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_HF1
+					MSM_BUS_SLAVE_EBI_CH0 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_HF1
+					MSM_BUS_SLAVE_EBI_CH0 0 0>;
+				};
+				qcom,axi-port-camnoc {
+					qcom,msm-bus,name = "cam_hf_2_camnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_HF1_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_HF1_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>;
+				};
+			};
+			qcom,axi-port3 {
+				qcom,axi-port-name = "cam_sf_1";
+				qcom,axi-port-mnoc {
+					qcom,msm-bus,name = "cam_sf_1_mnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_SF
+					MSM_BUS_SLAVE_EBI_CH0 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_SF
+					MSM_BUS_SLAVE_EBI_CH0 0 0>;
+				};
+				qcom,axi-port-camnoc {
+					qcom,msm-bus,name = "cam_sf_1_camnoc";
+					qcom,msm-bus-vector-dyn-vote;
+					qcom,msm-bus,num-cases = <2>;
+					qcom,msm-bus,num-paths = <1>;
+					qcom,msm-bus,vectors-KBps =
+					<MSM_BUS_MASTER_CAMNOC_SF_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>,
+					<MSM_BUS_MASTER_CAMNOC_SF_UNCOMP
+					MSM_BUS_SLAVE_CAMNOC_UNCOMP 0 0>;
+				};
+			};
+		};
+	};
+
+	qcom,cam-cdm-intf {
+		compatible = "qcom,cam-cdm-intf";
+		cell-index = <0>;
+		label = "cam-cdm-intf";
+		num-hw-cdm = <1>;
+		cdm-client-names = "vfe",
+			"jpegdma",
+			"jpegenc",
+			"fd";
+		status = "ok";
+	};
+
+	qcom,cpas-cdm0@ac48000 {
+		cell-index = <0>;
+		compatible = "qcom,cam170-cpas-cdm0";
+		label = "cpas-cdm";
+		reg = <0xac48000 0x1000>;
+		reg-names = "cpas-cdm";
+		reg-cam-base = <0x48000>;
+		interrupts = <0 461 0>;
+		interrupt-names = "cpas-cdm";
+		regulator-names = "camss";
+		camss-supply = <&titan_top_gdsc>;
+		clock-names = "gcc_camera_ahb",
+			"gcc_camera_axi",
+			"cam_cc_soc_ahb_clk",
+			"cam_cc_cpas_ahb_clk",
+			"cam_cc_camnoc_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+		clock-rates = <0 0 0 0 0>;
+		clock-cntl-level = "svs";
+		cdm-client-names = "ife";
+		status = "ok";
+	};
+
+	qcom,cam-isp {
+		compatible = "qcom,cam-isp";
+		arch-compat = "ife";
+		status = "ok";
+	};
+
+	cam_csid0: qcom,csid0@acb3000 {
+		cell-index = <0>;
+		compatible = "qcom,csid170";
+		reg-names = "csid";
+		reg = <0xacb3000 0x1000>;
+		reg-cam-base = <0xb3000>;
+		interrupt-names = "csid";
+		interrupts = <0 464 0>;
+		regulator-names = "camss", "ife0";
+		camss-supply = <&titan_top_gdsc>;
+		ife0-supply = <&ife_0_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_csid_clk",
+			"ife_csid_clk_src",
+			"ife_cphy_rx_clk",
+			"cphy_rx_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk",
+			"ife_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_0_CSID_CLK>,
+			<&clock_camcc CAM_CC_IFE_0_CSID_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_0_CPHY_RX_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_0_CLK>,
+			<&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "turbo";
+		src-clock-name = "ife_csid_clk_src";
+		status = "ok";
+	};
+
+	cam_vfe0: qcom,vfe0@acaf000 {
+		cell-index = <0>;
+		compatible = "qcom,vfe170";
+		reg-names = "ife";
+		reg = <0xacaf000 0x4000>;
+		reg-cam-base = <0xaf000>;
+		interrupt-names = "ife";
+		interrupts = <0 465 0>;
+		regulator-names = "camss", "ife0";
+		camss-supply = <&titan_top_gdsc>;
+		ife0-supply = <&ife_0_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk",
+			"ife_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_0_CLK>,
+			<&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 480000000 0 0>,
+			<0 0 0 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
+		src-clock-name = "ife_clk_src";
+		clock-names-option =  "ife_dsp_clk";
+		clocks-option = <&clock_camcc CAM_CC_IFE_0_DSP_CLK>;
+		clock-rates-option = <600000000>;
+		status = "ok";
+	};
+
+	cam_csid1: qcom,csid1@acba000 {
+		cell-index = <1>;
+		compatible = "qcom,csid170";
+		reg-names = "csid";
+		reg = <0xacba000 0x1000>;
+		reg-cam-base = <0xba000>;
+		interrupt-names = "csid";
+		interrupts = <0 466 0>;
+		regulator-names = "camss", "ife1";
+		camss-supply = <&titan_top_gdsc>;
+		ife1-supply = <&ife_1_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_csid_clk",
+			"ife_csid_clk_src",
+			"ife_cphy_rx_clk",
+			"cphy_rx_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk",
+			"ife_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_1_CSID_CLK>,
+			<&clock_camcc CAM_CC_IFE_1_CSID_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_1_CPHY_RX_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_1_CLK>,
+			<&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "turbo";
+		src-clock-name = "ife_csid_clk_src";
+		status = "ok";
+	};
+
+	cam_vfe1: qcom,vfe1@acb6000 {
+		cell-index = <1>;
+		compatible = "qcom,vfe170";
+		reg-names = "ife";
+		reg = <0xacb6000 0x4000>;
+		reg-cam-base = <0xb6000>;
+		interrupt-names = "ife";
+		interrupts = <0 467 0>;
+		regulator-names = "camss", "ife1";
+		camss-supply = <&titan_top_gdsc>;
+		ife1-supply = <&ife_1_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk",
+			"ife_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_1_CLK>,
+			<&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 480000000 0 0>,
+			<0 0 0 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
+		src-clock-name = "ife_clk_src";
+		clock-names-option =  "ife_dsp_clk";
+		clocks-option = <&clock_camcc CAM_CC_IFE_1_DSP_CLK>;
+		clock-rates-option = <600000000>;
+		status = "ok";
+	};
+
+	cam_csid_lite: qcom,csid-lite@acc8000 {
+		cell-index = <2>;
+		compatible = "qcom,csid-lite170";
+		reg-names = "csid-lite";
+		reg = <0xacc8000 0x1000>;
+		reg-cam-base = <0xc8000>;
+		interrupt-names = "csid-lite";
+		interrupts = <0 468 0>;
+		regulator-names = "camss";
+		camss-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_csid_clk",
+			"ife_csid_clk_src",
+			"ife_cphy_rx_clk",
+			"cphy_rx_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_LITE_CSID_CLK>,
+			<&clock_camcc CAM_CC_IFE_LITE_CSID_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_LITE_CPHY_RX_CLK>,
+			<&clock_camcc CAM_CC_CPHY_RX_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_LITE_CLK>,
+			<&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0>;
+		clock-cntl-level = "svs", "turbo";
+		src-clock-name = "ife_csid_clk_src";
+		status = "ok";
+	};
+
+	cam_vfe_lite: qcom,vfe-lite@acc4000 {
+		cell-index = <2>;
+		compatible = "qcom,vfe-lite170";
+		reg-names = "ife-lite";
+		reg = <0xacc4000 0x4000>;
+		reg-cam-base = <0xc4000>;
+		interrupt-names = "ife-lite";
+		interrupts = <0 469 0>;
+		regulator-names = "camss";
+		camss-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"slow_ahb_clk_src",
+			"ife_clk",
+			"ife_clk_src",
+			"camnoc_axi_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
+			<&clock_camcc CAM_CC_IFE_LITE_CLK>,
+			<&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0>,
+			<0 0 0 0 0 0 480000000 0>,
+			<0 0 0 0 0 0 600000000 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
+		src-clock-name = "ife_clk_src";
+		status = "ok";
+	};
+
+	qcom,cam-icp {
+		compatible = "qcom,cam-icp";
+		compat-hw-name = "qcom,a5",
+			"qcom,ipe0",
+			"qcom,ipe1",
+			"qcom,bps";
+		num-a5 = <1>;
+		num-ipe = <2>;
+		num-bps = <1>;
+		status = "ok";
+	};
+
+	cam_a5: qcom,a5@ac00000 {
+		cell-index = <0>;
+		compatible = "qcom,cam-a5";
+		reg = <0xac00000 0x6000>,
+			<0xac10000 0x8000>,
+			<0xac18000 0x3000>;
+		reg-names = "a5_qgic", "a5_sierra", "a5_csr";
+		reg-cam-base = <0x00000 0x10000 0x18000>;
+		interrupts = <0 463 0>;
+		interrupt-names = "a5";
+		regulator-names = "camss-vdd";
+		camss-vdd-supply = <&titan_top_gdsc>;
+		clock-names = "gcc_cam_ahb_clk",
+			"gcc_cam_axi_clk",
+			"soc_fast_ahb",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"icp_apb_clk",
+			"icp_clk",
+			"icp_clk_src";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+				<&clock_gcc GCC_CAMERA_AXI_CLK>,
+				<&clock_camcc CAM_CC_FAST_AHB_CLK_SRC>,
+				<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+				<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+				<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+				<&clock_camcc CAM_CC_ICP_APB_CLK>,
+				<&clock_camcc CAM_CC_ICP_CLK>,
+				<&clock_camcc CAM_CC_ICP_CLK_SRC>;
+
+		clock-rates =
+			<0 0 200000000 0 0 0 0 400000000>,
+			<0 0 200000000 0 0 0 0 600000000>;
+		clock-cntl-level = "svs", "turbo";
+		fw_name = "CAMERA_ICP.elf";
+		ubwc-cfg = <0x77 0x1DF>;
+		status = "ok";
+	};
+
+	cam_ipe0: qcom,ipe0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-ipe";
+		regulator-names = "ipe0-vdd";
+		ipe0-vdd-supply = <&ipe_0_gdsc>;
+		clock-names = "ipe_0_ahb_clk",
+			"ipe_0_areg_clk",
+			"ipe_0_axi_clk",
+			"ipe_0_clk",
+			"ipe_0_clk_src";
+		src-clock-name = "ipe_0_clk_src";
+		clocks = <&clock_camcc CAM_CC_IPE_0_AHB_CLK>,
+				<&clock_camcc CAM_CC_IPE_0_AREG_CLK>,
+				<&clock_camcc CAM_CC_IPE_0_AXI_CLK>,
+				<&clock_camcc CAM_CC_IPE_0_CLK>,
+				<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
+
+		clock-rates = <0 0 0 0 240000000>,
+			<0 0 0 0 404000000>,
+			<0 0 0 0 480000000>,
+			<0 0 0 0 538000000>,
+			<0 0 0 0 600000000>;
+		clock-cntl-level = "lowsvs", "svs",
+			"svs_l1", "nominal", "turbo";
+		status = "ok";
+	};
+
+	cam_ipe1: qcom,ipe1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-ipe";
+		regulator-names = "ipe1-vdd";
+		ipe1-vdd-supply = <&ipe_1_gdsc>;
+		clock-names = "ipe_1_ahb_clk",
+			"ipe_1_areg_clk",
+			"ipe_1_axi_clk",
+			"ipe_1_clk",
+			"ipe_1_clk_src";
+		src-clock-name = "ipe_1_clk_src";
+		clocks = <&clock_camcc CAM_CC_IPE_1_AHB_CLK>,
+				<&clock_camcc CAM_CC_IPE_1_AREG_CLK>,
+				<&clock_camcc CAM_CC_IPE_1_AXI_CLK>,
+				<&clock_camcc CAM_CC_IPE_1_CLK>,
+				<&clock_camcc CAM_CC_IPE_1_CLK_SRC>;
+
+		clock-rates = <0 0 0 0 240000000>,
+			<0 0 0 0 404000000>,
+			<0 0 0 0 480000000>,
+			<0 0 0 0 538000000>,
+			<0 0 0 0 600000000>;
+		clock-cntl-level = "lowsvs", "svs",
+			"svs_l1", "nominal", "turbo";
+		status = "ok";
+	};
+
+	cam_bps: qcom,bps {
+		cell-index = <0>;
+		compatible = "qcom,cam-bps";
+		regulator-names = "bps-vdd";
+		bps-vdd-supply = <&bps_gdsc>;
+		clock-names = "bps_ahb_clk",
+			"bps_areg_clk",
+			"bps_axi_clk",
+			"bps_clk",
+			"bps_clk_src";
+		src-clock-name = "bps_clk_src";
+		clocks = <&clock_camcc CAM_CC_BPS_AHB_CLK>,
+				<&clock_camcc CAM_CC_BPS_AREG_CLK>,
+				<&clock_camcc CAM_CC_BPS_AXI_CLK>,
+				<&clock_camcc CAM_CC_BPS_CLK>,
+				<&clock_camcc CAM_CC_BPS_CLK_SRC>;
+
+		clock-rates = <0 0 0 0 200000000>,
+			<0 0 0 0 404000000>,
+			<0 0 0 0 480000000>,
+			<0 0 0 0 600000000>,
+			<0 0 0 0 600000000>;
+		clock-cntl-level = "lowsvs", "svs",
+			"svs_l1", "nominal", "turbo";
+		status = "ok";
+	};
+
+	qcom,cam-jpeg {
+		compatible = "qcom,cam-jpeg";
+		compat-hw-name = "qcom,jpegenc",
+			"qcom,jpegdma";
+		num-jpeg-enc = <1>;
+		num-jpeg-dma = <1>;
+		status = "ok";
+	};
+
+	cam_jpeg_enc: qcom,jpegenc@ac4e000 {
+		cell-index = <0>;
+		compatible = "qcom,cam_jpeg_enc";
+		reg-names = "jpege_hw";
+		reg = <0xac4e000 0x4000>;
+		reg-cam-base = <0x4e000>;
+		interrupt-names = "jpeg";
+		interrupts = <0 474 0>;
+		regulator-names = "camss-vdd";
+		camss-vdd-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"jpegenc_clk_src",
+			"jpegenc_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_JPEG_CLK_SRC>,
+			<&clock_camcc CAM_CC_JPEG_CLK>;
+
+		clock-rates = <0 0 0 0 0 600000000 0>;
+		src-clock-name = "jpegenc_clk_src";
+		clock-cntl-level = "nominal";
+		status = "ok";
+	};
+
+	cam_jpeg_dma: qcom,jpegdma@0xac52000{
+		cell-index = <0>;
+		compatible = "qcom,cam_jpeg_dma";
+		reg-names = "jpegdma_hw";
+		reg = <0xac52000 0x4000>;
+		reg-cam-base = <0x52000>;
+		interrupt-names = "jpegdma";
+		interrupts = <0 475 0>;
+		regulator-names = "camss-vdd";
+		camss-vdd-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"jpegdma_clk_src",
+			"jpegdma_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_JPEG_CLK_SRC>,
+			<&clock_camcc CAM_CC_JPEG_CLK>;
+
+		clock-rates = <0 0 0 0 0 600000000 0>;
+		src-clock-name = "jpegdma_clk_src";
+		clock-cntl-level = "nominal";
+		status = "ok";
+	};
+
+	qcom,cam-fd {
+		compatible = "qcom,cam-fd";
+		compat-hw-name = "qcom,fd";
+		num-fd = <1>;
+		status = "ok";
+	};
+
+	cam_fd: qcom,fd@ac5a000 {
+		cell-index = <0>;
+		compatible = "qcom,fd41";
+		reg-names = "fd_core", "fd_wrapper";
+		reg = <0xac5a000 0x1000>,
+			<0xac5b000 0x400>;
+		reg-cam-base = <0x5a000 0x5b000>;
+		interrupt-names = "fd";
+		interrupts = <0 462 0>;
+		regulator-names = "camss-vdd";
+		camss-vdd-supply = <&titan_top_gdsc>;
+		clock-names = "gcc_ahb_clk",
+			"gcc_axi_clk",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"fd_core_clk_src",
+			"fd_core_clk",
+			"fd_core_uar_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_FD_CORE_CLK_SRC>,
+			<&clock_camcc CAM_CC_FD_CORE_CLK>,
+			<&clock_camcc CAM_CC_FD_CORE_UAR_CLK>;
+		src-clock-name = "fd_core_clk_src";
+		clock-cntl-level = "svs", "svs_l1", "turbo";
+		clock-rates =
+			<0 0 0 0 0 400000000 0 0>,
+			<0 0 0 0 0 538000000 0 0>,
+			<0 0 0 0 0 600000000 0 0>;
+		status = "ok";
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
index f0c820f..2b5ed1a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp-overlay.dts
@@ -20,11 +20,15 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
 #include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 CDP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
 	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
 	qcom,msm-id = <336 0x0>;
 	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
index 7e5947b..5a1b945 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dts
@@ -15,9 +15,13 @@
 
 #include "sdm670.dtsi"
 #include "sdm670-cdp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 CDP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L CDP";
 	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
 	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index 0cf48a3..521b048 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -10,6 +10,43 @@
  * GNU General Public License for more details.
  */
 
+#include <dt-bindings/gpio/gpio.h>
+#include "sdm670-pmic-overlay.dtsi"
+#include "sdm670-sde-display.dtsi"
+#include "sdm670-camera-sensor-cdp.dtsi"
+
+&ufsphy_mem {
+	compatible = "qcom,ufs-phy-qmp-v3";
+
+	vdda-phy-supply = <&pm660l_l1>; /* 0.88v */
+	vdda-pll-supply = <&pm660_l1>; /* 1.2v */
+	vdda-phy-max-microamp = <62900>;
+	vdda-pll-max-microamp = <18300>;
+
+	status = "ok";
+};
+
+&ufshc_mem {
+	vdd-hba-supply = <&ufs_phy_gdsc>;
+	vdd-hba-fixed-regulator;
+	vcc-supply = <&pm660l_l4>;
+	vcc-voltage-level = <2960000 2960000>;
+	vccq2-supply = <&pm660_l8>;
+	vcc-max-microamp = <600000>;
+	vccq2-max-microamp = <600000>;
+
+	qcom,vddp-ref-clk-supply = <&pm660_l1>;
+	qcom,vddp-ref-clk-max-microamp = <100>;
+
+	status = "ok";
+};
+
+&pm660l_switch1 {
+	pinctrl-names = "led_enable", "led_disable";
+	pinctrl-0 = <&flash_led3_front_en>;
+	pinctrl-1 = <&flash_led3_front_dis>;
+};
+
 &qupv3_se9_2uart {
 	status = "disabled";
 };
@@ -23,7 +60,27 @@
 };
 
 &qupv3_se3_i2c {
-	status = "disabled";
+	status = "ok";
+	nq@28 {
+		compatible = "qcom,nq-nci";
+		reg = <0x28>;
+		qcom,nq-irq = <&tlmm 44 0x00>;
+		qcom,nq-ven = <&tlmm 12 0x00>;
+		qcom,nq-firm = <&tlmm 43 0x00>;
+		qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+		qcom,nq-esepwr = <&tlmm 116 0x00>;
+		interrupt-parent = <&tlmm>;
+		qcom,clk-src = "BBCLK3";
+		interrupts = <44 0>;
+		interrupt-names = "nfc_irq";
+		pinctrl-names = "nfc_active", "nfc_suspend";
+		pinctrl-0 = <&nfc_int_active
+			     &nfc_enable_active
+			     &nfc_clk_default>;
+		pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+		clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+		clock-names = "ref_clk";
+	};
 };
 
 &qupv3_se10_i2c {
@@ -31,5 +88,223 @@
 };
 
 &qupv3_se6_4uart {
-	status = "disabled";
+	status = "ok";
+};
+
+&sdhc_1 {
+	vdd-supply = <&pm660l_l4>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 570000>;
+
+	vdd-io-supply = <&pm660_l8>;
+	qcom,vdd-io-always-on;
+	qcom,vdd-io-lpm-sup;
+	qcom,vdd-io-voltage-level = <1800000 1800000>;
+	qcom,vdd-io-current-level = <200 325000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc1_clk_on  &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+	pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+	status = "ok";
+};
+
+&sdhc_2 {
+	vdd-supply = <&pm660l_l5>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 800000>;
+
+	vdd-io-supply = <&pm660l_l2>;
+	qcom,vdd-io-voltage-level = <1800000 2960000>;
+	qcom,vdd-io-current-level = <200 22000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+	cd-gpios = <&tlmm 96 0x1>;
+
+	status = "ok";
+};
+
+&pm660_charger {
+	qcom,batteryless-platform;
+};
+
+&soc {
+	gpio_keys {
+		compatible = "gpio-keys";
+		label = "gpio-keys";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&key_cam_snapshot_default
+			     &key_cam_focus_default
+			     &key_vol_up_default>;
+
+		cam_snapshot {
+			label = "cam_snapshot";
+			gpios = <&tlmm 91 0>;
+			linux,input-type = <1>;
+			linux,code = <766>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		cam_focus {
+			label = "cam_focus";
+			gpios = <&tlmm 92 0>;
+			linux,input-type = <1>;
+			linux,code = <528>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm660l_gpios 7 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+	};
+};
+
+&dsi_dual_nt35597_truly_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_truly_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35597_truly_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35597_truly_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_sim_vid {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_vid {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_sim_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_sim_dsc_375_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_dsc_375_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
+	qcom,dsi-display-active;
+};
+
+&pm660l_wled {
+	status = "okay";
+	qcom,led-strings-list = [01 02];
+};
+
+&mdss_mdp {
+	#cooling-cells = <2>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index 16efb4c..a885495 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
@@ -71,6 +71,197 @@
 		};
 	};
 
+	replicator_swao: replicator@6b0a000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b909>;
+
+		reg = <0x6b0a000 0x1000>;
+		reg-names = "replicator-base";
+
+		coresight-name = "coresight-replicator-swao";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				replicator_swao_in_tmc_etf_swao: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&tmc_etf_swao_out_replicator>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				replicator_swao_out_funnel_in2: endpoint {
+					remote-endpoint =
+					      <&funnel_in2_in_replicator_swao>;
+				};
+			};
+		};
+	};
+
+	tmc_etf_swao: tmc@6b09000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b961>;
+
+		reg = <0x6b09000 0x1000>;
+		reg-names = "tmc-base";
+
+		coresight-name = "coresight-tmc-etf-swao";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				tmc_etf_swao_out_replicator: endpoint {
+					remote-endpoint=
+					  <&replicator_swao_in_tmc_etf_swao>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				tmc_etf_swao_in_funnel_swao: endpoint {
+					slave-mode;
+					remote-endpoint=
+					  <&funnel_swao_out_tmc_etf_swao>;
+				};
+			};
+		};
+	};
+
+	funnel_swao:funnel@0x6b08000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b908>;
+
+		reg = <0x6b08000 0x1000>;
+		reg-names = "funnel-base";
+
+		coresight-name = "coresight-funnel-swao";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				funnel_swao_out_tmc_etf_swao: endpoint {
+					remote-endpoint =
+						<&tmc_etf_swao_in_funnel_swao>;
+				};
+			};
+
+			port@1 {
+				reg = <7>;
+				funnel_swao_in_tpda_swao: endpoint {
+					slave-mode;
+					remote-endpoint=
+						<&tpda_swao_out_funnel_swao>;
+				};
+			};
+		};
+	};
+
+	tpda_swao: tpda@6b01000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b969>;
+		reg = <0x6b01000 0x1000>;
+		reg-names = "tpda-base";
+
+		coresight-name = "coresight-tpda-swao";
+
+		qcom,tpda-atid = <71>;
+		qcom,dsb-elem-size = <1 32>;
+		qcom,cmb-elem-size = <0 64>;
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				tpda_swao_out_funnel_swao: endpoint {
+					remote-endpoint =
+						<&funnel_swao_in_tpda_swao>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				tpda_swao_in_tpdm_swao0: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&tpdm_swao0_out_tpda_swao>;
+				};
+			};
+
+			port@2 {
+				reg = <1>;
+				tpda_swao_in_tpdm_swao1: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&tpdm_swao1_out_tpda_swao>;
+				};
+			};
+		};
+	};
+
+	tpdm_swao0: tpdm@6b02000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b968>;
+
+		reg = <0x6b02000 0x1000>;
+		reg-names = "tpdm-base";
+
+		coresight-name = "coresight-tpdm-swao-0";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		port {
+			tpdm_swao0_out_tpda_swao: endpoint {
+			remote-endpoint = <&tpda_swao_in_tpdm_swao0>;
+			 };
+		};
+	};
+
+	tpdm_swao1: tpdm@6b03000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b968>;
+		reg = <0x6b03000 0x1000>;
+		reg-names = "tpdm-base";
+
+		coresight-name="coresight-tpdm-swao-1";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		qcom,msr-fix-req;
+
+		port {
+			tpdm_swao1_out_tpda_swao: endpoint {
+				remote-endpoint = <&tpda_swao_in_tpdm_swao1>;
+			};
+		};
+	};
+
 	tmc_etf: tmc@6047000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b961>;
@@ -277,6 +468,15 @@
 			};
 
 			port@1 {
+				reg = <0>;
+				funnel_in1_in_audio_etm0: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&audio_etm0_out_funnel_in1>;
+				};
+			};
+
+			port@2 {
 				reg = <3>;
 				funnel_in1_in_funnel_modem: endpoint {
 					slave-mode;
@@ -322,6 +522,14 @@
 			};
 
 			port@2 {
+				reg = <1>;
+				funnel_in2_in_replicator_swao: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&replicator_swao_out_funnel_in2>;
+				};
+			};
+			port@3 {
 				reg = <5>;
 				funnel_in2_in_funnel_apss_merg: endpoint {
 					slave-mode;
@@ -329,7 +537,58 @@
 					  <&funnel_apss_merg_out_funnel_in2>;
 				};
 			};
+			port@4 {
+				reg = <6>;
+				funnel_in2_in_funnel_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&funnel_gfx_out_funnel_in2>;
+				};
+			};
+		};
+	};
 
+	funnel_gfx: funnel@0x6943000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b908>;
+
+		reg = <0x6943000 0x1000>;
+		reg-names = "funnel-base";
+
+		coresight-name = "coresight-funnel-gfx";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				funnel_gfx_out_funnel_in2: endpoint {
+					remote-endpoint =
+					  <&funnel_in2_in_funnel_gfx>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				funnel_in2_in_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_out_funnel_in2>;
+				};
+			};
+
+			port@2 {
+				reg = <1>;
+				funnel_in2_in_gfx_cx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_cx_out_funnel_in2>;
+				};
+			};
 		};
 	};
 
@@ -355,6 +614,7 @@
 				     <13 32>;
 		qcom,cmb-elem-size = <3 64>,
 				     <7 64>,
+				     <9 64>,
 				     <13 64>;
 
 		clocks = <&clock_aop QDSS_CLK>;
@@ -400,15 +660,6 @@
 			};
 
 			port@4 {
-				reg = <5>;
-				tpda_in_funnel_lpass: endpoint {
-					slave-mode;
-					remote-endpoint =
-						<&funnel_lpass_out_tpda>;
-				};
-			};
-
-			port@5 {
 				reg = <6>;
 				tpda_in_funnel_turing: endpoint {
 					slave-mode;
@@ -417,7 +668,7 @@
 				};
 			};
 
-			port@6 {
+			port@5 {
 				reg = <7>;
 				tpda_in_tpdm_vsense: endpoint {
 					slave-mode;
@@ -426,21 +677,30 @@
 				};
 			};
 
-			port@7 {
-				reg = <10>;
-				tpda_in_tpdm_qm: endpoint {
+			port@6 {
+				reg = <9>;
+				tpda_in_tpdm_prng: endpoint {
 					slave-mode;
 					remote-endpoint =
-						<&tpdm_qm_out_tpda>;
+						<&tpdm_prng_out_tpda>;
+				};
+			};
+
+			port@7 {
+				reg = <11>;
+					tpda_in_tpdm_north: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&tpdm_north_out_tpda>;
 				};
 			};
 
 			port@8 {
-				reg = <11>;
-				tpda_in_tpdm_north: endpoint {
+				reg = <12>;
+				tpda_in_tpdm_qm: endpoint {
 					slave-mode;
 					remote-endpoint =
-						<&tpdm_north_out_tpda>;
+						<&tpdm_qm_out_tpda>;
 				};
 			};
 
@@ -545,63 +805,20 @@
 		};
 	};
 
-	funnel_lpass: funnel@6845000 {
+	tpdm_prng: tpdm@684c000 {
 		compatible = "arm,primecell";
-		arm,primecell-periphid = <0x0003b908>;
-
-		reg = <0x6845000 0x1000>;
-		reg-names = "funnel-base";
-
-		coresight-name = "coresight-funnel-lpass";
-
-		clocks = <&clock_aop QDSS_CLK>;
-		clock-names = "apb_pclk";
-
-		ports {
-			#address-cells = <1>;
-			#size-cells = <0>;
-
-			port@0 {
-				reg = <0>;
-				funnel_lpass_out_tpda: endpoint {
-					remote-endpoint =
-					    <&tpda_in_funnel_lpass>;
-				};
-			};
-
-			port@1 {
-				reg = <0>;
-				funnel_lpass_in_tpdm_lpass: endpoint {
-					slave-mode;
-					remote-endpoint =
-					    <&tpdm_lpass_out_funnel_lpass>;
-				};
-			};
-
-			port@2 {
-				reg = <1>;
-				funnel_lpass_in_audio_etm0: endpoint {
-					slave-mode;
-					remote-endpoint =
-					    <&audio_etm0_out_funnel_lpass>;
-				};
-			};
-		};
-	};
-
-	tpdm_lpass: tpdm@6844000 {
-		compatible = "arm,primecell";
-		reg = <0x6844000 0x1000>;
+		arm,primecell-periphid = <0x0003b968>;
+		reg = <0x684c000 0x1000>;
 		reg-names = "tpdm-base";
 
-		coresight-name = "coresight-tpdm-lpass";
+		coresight-name = "coresight-tpdm-prng";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
 		port {
-			tpdm_lpass_out_funnel_lpass: endpoint {
-				remote-endpoint = <&funnel_lpass_in_tpdm_lpass>;
+			tpdm_prng_out_tpda: endpoint {
+				remote-endpoint = <&tpda_in_tpdm_prng>;
 			};
 		};
 	};
@@ -614,6 +831,8 @@
 
 		coresight-name = "coresight-tpdm-center";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -630,6 +849,8 @@
 		reg = <0x6a24000 0x1000>;
 		reg-names = "tpdm-base";
 
+		qcom,msr-fix-req;
+
 		coresight-name = "coresight-tpdm-north";
 
 		clocks = <&clock_aop QDSS_CLK>;
@@ -714,6 +935,116 @@
 		};
 	};
 
+	tpda_llm_silver: tpda@78c0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b969>;
+		reg = <0x78c0000 0x1000>;
+		reg-names = "tpda-base";
+
+		coresight-name = "coresight-tpda-llm-silver";
+
+		qcom,tpda-atid = <72>;
+		qcom,cmb-elem-size = <0 32>;
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port@0 {
+				reg = <0>;
+				tpda_llm_silver_out_funnel_apss_merg: endpoint {
+				remote-endpoint =
+					<&funnel_apss_merg_in_tpda_llm_silver>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				tpda_llm_silver_in_tpdm_llm_silver: endpoint {
+				slave-mode;
+				remote-endpoint =
+					<&tpdm_llm_silver_out_tpda_llm_silver>;
+				};
+			};
+		};
+	};
+
+	tpdm_llm_silver: tpdm@78a0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b968>;
+		reg = <0x78a0000 0x1000>;
+		reg-names = "tpdm-base";
+
+		coresight-name = "coresight-tpdm-llm-silver";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		port {
+			tpdm_llm_silver_out_tpda_llm_silver: endpoint {
+				remote-endpoint =
+					<&tpda_llm_silver_in_tpdm_llm_silver>;
+			};
+		};
+	};
+
+	tpda_llm_gold: tpda@78d0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b969>;
+		reg = <0x78d0000 0x1000>;
+		reg-names = "tpda-base";
+
+		coresight-name = "coresight-tpda-llm-gold";
+
+		qcom,tpda-atid = <73>;
+		qcom,cmb-elem-size = <0 32>;
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port@0 {
+				reg = <0>;
+				tpda_llm_gold_out_funnel_apss_merg: endpoint {
+					remote-endpoint =
+					  <&funnel_apss_merg_in_tpda_llm_gold>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				tpda_llm_gold_in_tpdm_llm_gold: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&tpdm_llm_gold_out_tpda_llm_gold>;
+				};
+			};
+		};
+	};
+
+	tpdm_llm_gold: tpdm@78b0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b968>;
+		reg = <0x78b0000 0x1000>;
+		reg-names = "tpdm-base";
+
+		coresight-name = "coresight-tpdm-llm-gold";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		port {
+			tpdm_llm_gold_out_tpda_llm_gold: endpoint {
+				remote-endpoint =
+				    <&tpda_llm_gold_in_tpdm_llm_gold>;
+			};
+		};
+	};
+
 	funnel_dl_mm: funnel@6c0b000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
@@ -757,6 +1088,8 @@
 
 		coresight-name = "coresight-tpdm-mm";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -799,13 +1132,42 @@
 					    <&tpdm_turing_out_funnel_turing>;
 				};
 			};
+		};
+	};
 
-			port@2 {
+	funnel_turing_1: funnel_1@6861000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b908>;
+
+		reg = <0x6867000 0x10>,
+		      <0x6861000 0x1000>;
+		reg-names = "funnel-base-dummy", "funnel-base-real";
+
+		coresight-name = "coresight-funnel-turing-1";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		qcom,duplicate-funnel;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				funnel_turing_1_out_funnel_qatb: endpoint {
+					remote-endpoint =
+					    <&funnel_qatb_in_funnel_turing_1>;
+				};
+			};
+
+			port@1 {
 				reg = <1>;
-				funnel_turing_in_turing_etm0: endpoint {
+				funnel_turing_1_in_turing_etm0: endpoint {
 					slave-mode;
 					remote-endpoint =
-					    <&turing_etm0_out_funnel_turing>;
+					    <&turing_etm0_out_funnel_turing_1>;
 				};
 			};
 		};
@@ -817,6 +1179,8 @@
 		reg = <0x6860000 0x1000>;
 		reg-names = "tpdm-base";
 
+		qcom,msr-fix-req;
+
 		coresight-name = "coresight-tpdm-turing";
 
 		clocks = <&clock_aop QDSS_CLK>;
@@ -873,6 +1237,8 @@
 
 		coresight-name = "coresight-tpdm-ddr";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -1004,6 +1370,15 @@
 						<&tpda_out_funnel_qatb>;
 				};
 			};
+
+			port@2 {
+				reg = <7>;
+				funnel_qatb_in_funnel_turing_1: endpoint {
+					slave-mode;
+					remote-endpoint =
+					    <&funnel_turing_1_out_funnel_qatb>;
+				};
+			};
 		};
 	};
 
@@ -1013,7 +1388,7 @@
 		reg = <0x69e1000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-ddr0";
+		coresight-name = "coresight-cti-ddr_dl_0_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1025,7 +1400,7 @@
 		reg = <0x69e4000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-ddr1";
+		coresight-name = "coresight-cti-ddr_dl_1_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1037,7 +1412,7 @@
 		reg = <0x69e5000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-ddr1";
+		coresight-name = "coresight-cti-ddr_dl_1_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1049,7 +1424,7 @@
 		reg = <0x6c09000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-dlmm";
+		coresight-name = "coresight-cti-dlmm_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1061,7 +1436,7 @@
 		reg = <0x6c0a000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-dlmm";
+		coresight-name = "coresight-cti-dlmm_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1073,7 +1448,7 @@
 		reg = <0x6c29000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-dlct";
+		coresight-name = "coresight-cti-dlct_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1085,47 +1460,51 @@
 		reg = <0x6c2a000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-dlct";
+		coresight-name = "coresight-cti-dlct_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti0_wcss: cti@69a4000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x69a4000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-wcss";
+		coresight-name = "coresight-cti-wcss_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti1_wcss: cti@69a5000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x69a5000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-wcss";
+		coresight-name = "coresight-cti-wcss_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti2_wcss: cti@69a6000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x69a6000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti2-wcss";
+		coresight-name = "coresight-cti-wcss_cti2";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti_mss_q6: cti@683b000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x683b000 0x1000>;
 		reg-names = "cti-base";
 
@@ -1136,7 +1515,8 @@
 	};
 
 	cti_turing: cti@6867000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6867000 0x1000>;
 		reg-names = "cti-base";
 
@@ -1147,106 +1527,116 @@
 	};
 
 	cti2_ssc_sdc: cti@6b10000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b10000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti2-ssc_sdc";
+		coresight-name = "coresight-cti-ssc_sdc_cti2";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti1_ssc: cti@6b11000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b11000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-ssc";
+		coresight-name = "coresight-cti-ssc_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti0_ssc_q6: cti@6b1b000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b1b000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-ssc-q6";
+		coresight-name = "coresight-cti-ssc_q6_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti_ssc_noc: cti@6b1e000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b1e000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti-ssc-noc";
+		coresight-name = "coresight-cti-ssc_noc";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti6_ssc_noc: cti@6b1f000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b1f000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti6-ssc-noc";
+		coresight-name = "coresight-cti-ssc_noc_cti6";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti0_swao: cti@6b04000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b04000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-swao";
+		coresight-name = "coresight-cti-swao_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti1_swao: cti@6b05000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b05000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-swao";
+		coresight-name = "coresight-cti-swao_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti2_swao: cti@6b06000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b06000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti2-swao";
+		coresight-name = "coresight-cti-swao_cti2";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti3_swao: cti@6b07000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b07000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti3-swao";
+		coresight-name = "coresight-cti-swao_cti3";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti_aop_m3: cti@6b21000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6b21000 0x1000>;
 		reg-names = "cti-base";
 
@@ -1257,7 +1647,8 @@
 	};
 
 	cti_titan: cti@6c13000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6c13000 0x1000>;
 		reg-names = "cti-base";
 
@@ -1268,7 +1659,8 @@
 	};
 
 	cti_venus_arm9: cti@6c20000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x6c20000 0x1000>;
 		reg-names = "cti-base";
 
@@ -1279,33 +1671,36 @@
 	};
 
 	cti0_apss: cti@78e0000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x78e0000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-apss";
+		coresight-name = "coresight-cti-apss_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti1_apss: cti@78f0000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x78f0000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-apss";
+		coresight-name = "coresight-cti-apss_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 	};
 
 	cti2_apss: cti@7900000 {
-		compatible = "arm,coresight-cti";
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
 		reg = <0x7900000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti2-apss";
+		coresight-name = "coresight-cti-apss_cti2";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1631,9 +2026,9 @@
 		qcom,inst-id = <13>;
 
 		port{
-			turing_etm0_out_funnel_turing: endpoint {
+			turing_etm0_out_funnel_turing_1: endpoint {
 				remote-endpoint =
-					<&funnel_turing_in_turing_etm0>;
+					<&funnel_turing_1_in_turing_etm0>;
 			};
 		};
 	};
@@ -1659,8 +2054,9 @@
 		qcom,inst-id = <5>;
 
 		port {
-			audio_etm0_out_funnel_lpass: endpoint {
-				remote-endpoint = <&funnel_lpass_in_audio_etm0>;
+			audio_etm0_out_funnel_in1: endpoint {
+				remote-endpoint =
+					<&funnel_in1_in_audio_etm0>;
 			};
 		};
 	};
@@ -1699,7 +2095,7 @@
 			};
 
 			port@2 {
-				reg = <1>;
+				reg = <2>;
 				funnel_apss_merg_in_tpda_olc: endpoint {
 					slave-mode;
 					remote-endpoint =
@@ -1708,13 +2104,29 @@
 			};
 
 			port@3 {
-				reg = <3>;
+				reg = <4>;
 				funnel_apss_merg_in_tpda_apss: endpoint {
 					slave-mode;
 					remote-endpoint =
 					    <&tpda_apss_out_funnel_apss_merg>;
 				};
 			};
+			port@4 {
+				reg = <5>;
+				funnel_apss_merg_in_tpda_llm_silver: endpoint {
+					slave-mode;
+					remote-endpoint =
+					<&tpda_llm_silver_out_funnel_apss_merg>;
+				};
+			};
+			port@5 {
+				reg = <6>;
+				funnel_apss_merg_in_tpda_llm_gold: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&tpda_llm_gold_out_funnel_apss_merg>;
+				};
+			};
 		};
 	};
 
@@ -1870,6 +2282,22 @@
 		};
 	};
 
+	ipcb_tgu: tgu@6b0c000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b999>;
+		reg = <0x6b0c000 0x1000>;
+		reg-names = "tgu-base";
+		tgu-steps = <3>;
+		tgu-conditions = <4>;
+		tgu-regs = <4>;
+		tgu-timer-counters = <8>;
+
+		coresight-name = "coresight-tgu-ipcb";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
+
 	funnel_apss: funnel@7800000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm670-ext-cdc-usbc-audio.dtsi
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm670-ext-cdc-usbc-audio.dtsi
index c06b806..cd113b3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-ext-cdc-usbc-audio.dtsi
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,7 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&tavil_snd {
+	qcom,msm-mbhc-usbc-audio-supported = <1>;
+	qcom,usbc-analog-en1-gpio = <&wcd_usbc_analog_en1_gpio>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-ext-codec-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-ext-codec-audio-overlay.dtsi
new file mode 100644
index 0000000..14a3e93
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-ext-codec-audio-overlay.dtsi
@@ -0,0 +1,84 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-audio-overlay.dtsi"
+
+&pmic_analog_codec {
+	status = "disabled";
+};
+
+&msm_sdw_codec {
+	status = "disabled";
+};
+
+&cdc_pdm_gpios {
+	status = "disabled";
+};
+
+&cdc_comp_gpios {
+	status = "disabled";
+};
+
+&cdc_dmic_gpios {
+	status = "disabled";
+};
+
+&cdc_sdw_gpios {
+	status = "disabled";
+};
+
+&wsa_spkr_en1 {
+	status = "disabled";
+};
+
+&wsa_spkr_en2 {
+	status = "disabled";
+};
+
+&qupv3_se8_spi {
+	status = "okay";
+};
+
+&wcd9xxx_intc {
+	status = "okay";
+};
+
+&wdsp_mgr {
+	status = "okay";
+};
+
+&wdsp_glink {
+	status = "okay";
+};
+
+&slim_aud {
+	status = "okay";
+};
+
+&dai_slim {
+	status = "okay";
+};
+
+&wcd934x_cdc {
+	status = "okay";
+};
+
+&clock_audio_lnbb {
+	status = "okay";
+};
+
+&wcd_rst_gpio {
+	status = "okay";
+};
+
+&wcd9xxx_intc {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp-overlay.dts
similarity index 67%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp-overlay.dts
index 2fac9e8..32a8580 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp-overlay.dts
@@ -19,11 +19,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-external-codec.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp.dts
new file mode 100644
index 0000000..6a87d3a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-cdp.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-external-codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660 + PM660L Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp-overlay.dts
similarity index 67%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp-overlay.dts
index 2fac9e8..970209ca 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp-overlay.dts
@@ -19,11 +19,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp.dts
new file mode 100644
index 0000000..87ac190
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-mtp.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660 + PM660L Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp-overlay.dts
similarity index 65%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp-overlay.dts
index 2fac9e8..48a6066 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp-overlay.dts
@@ -19,11 +19,17 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 1>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp.dts
new file mode 100644
index 0000000..e64d13b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-cdp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660 + PM660A Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 1>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp-overlay.dts
similarity index 65%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp-overlay.dts
index 2fac9e8..8715ddc 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp-overlay.dts
@@ -19,11 +19,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp.dts
new file mode 100644
index 0000000..0beaddb3
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec-pm660a-mtp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660 + PM660A Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 1>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm670-external-codec.dtsi
similarity index 71%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm670-external-codec.dtsi
index c06b806..dbcc6bd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-external-codec.dtsi
@@ -10,14 +10,20 @@
  * GNU General Public License for more details.
  */
 
+#include "sdm670-ext-codec-audio-overlay.dtsi"
 
-/dts-v1/;
+&int_codec {
+	status = "disabled";
+};
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+&tavil_snd {
+	status = "okay";
+};
 
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&slim_aud {
+	status = "okay";
+};
+
+&dai_slim {
+	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
new file mode 100644
index 0000000..5dd1dec
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -0,0 +1,492 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+
+	pil_gpu: qcom,kgsl-hyp {
+		compatible = "qcom,pil-tz-generic";
+		qcom,pas-id = <13>;
+		qcom,firmware-name = "a615_zap";
+	};
+
+	msm_bus: qcom,kgsl-busmon{
+		label = "kgsl-busmon";
+		compatible = "qcom,kgsl-busmon";
+	};
+
+	gpubw: qcom,gpubw {
+		compatible = "qcom,devbw";
+		governor = "bw_vbif";
+		qcom,src-dst-ports = <26 512>;
+		qcom,bw-tbl =
+			<     0 /*  off     */ >,
+			<   381 /*  100  MHz */ >,
+			<   762 /*  200  MHz */ >,
+			<  1144 /*  300  MHz */ >,
+			<  1720 /*  451  MHz */ >,
+			<  2086 /*  547  MHz */ >,
+			<  2597 /*  681  MHz */ >,
+			<  3147 /*  825  MHz */ >,
+			<  3879 /*  1017 MHz */ >,
+			<  5161 /*  1353 MHz */ >,
+			<  5931 /*  1555 MHz */ >,
+			<  6881 /*  1804 MHz */ >;
+	};
+
+	msm_gpu: qcom,kgsl-3d0@5000000 {
+		label = "kgsl-3d0";
+		compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
+		status = "ok";
+		reg = <0x5000000 0x40000
+			0x780000 0x6300>;
+		reg-names = "kgsl_3d0_reg_memory", "qfprom_memory";
+		interrupts = <0 300 0>;
+		interrupt-names = "kgsl_3d0_irq";
+		qcom,id = <0>;
+
+		qcom,chipid = <0x06010500>;
+
+		qcom,initial-pwrlevel = <3>;
+
+		qcom,gpu-quirk-hfi-use-reg;
+
+		/* <HZ/12> */
+		qcom,idle-timeout = <80>;
+		qcom,no-nap;
+
+		qcom,highest-bank-bit = <14>;
+
+		qcom,min-access-length = <32>;
+
+		qcom,ubwc-mode = <2>;
+
+		/* size in bytes */
+		qcom,snapshot-size = <1048576>;
+
+		/* base addr, size */
+		qcom,gpu-qdss-stm = <0x161c0000 0x40000>;
+		#cooling-cells = <2>;
+
+		clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
+			<&clock_gpucc GPU_CC_CXO_CLK>,
+			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
+			<&clock_gpucc GPU_CC_CX_GMU_CLK>;
+
+		clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
+				"mem_iface_clk", "gmu_clk";
+
+		/* Bus Scale Settings */
+		qcom,gpubw-dev = <&gpubw>;
+		qcom,bus-control;
+		qcom,msm-bus,name = "grp3d";
+		qcom,bus-width = <32>;
+		qcom,msm-bus,num-cases = <12>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<26 512 0 0>,
+				<26 512 0 400000>,     /*  1 bus=100  */
+				<26 512 0 800000>,     /*  2 bus=200  */
+				<26 512 0 1200000>,    /*  3 bus=300  */
+				<26 512 0 1804000>,    /*  4 bus=451  */
+				<26 512 0 2188000>,    /*  5 bus=547  */
+				<26 512 0 2724000>,    /*  6 bus=681  */
+				<26 512 0 3300000>,    /*  7 bus=825  */
+				<26 512 0 4068000>,    /*  8 bus=1017 */
+				<26 512 0 5412000>,    /*  9 bus=1353 */
+				<26 512 0 6220000>,    /* 10 bus=1555 */
+				<26 512 0 7216000>;    /* 11 bus=1804 */
+
+		/* GDSC regulator names */
+		regulator-names = "vddcx", "vdd";
+		/* GDSC oxili regulators */
+		vddcx-supply = <&gpu_cx_gdsc>;
+		vdd-supply = <&gpu_gx_gdsc>;
+
+		/* GPU related llc slices */
+		cache-slice-names = "gpu", "gpuhtw";
+		cache-slices = <&llcc 12>, <&llcc 11>;
+
+		/* CPU latency parameter */
+		qcom,pm-qos-active-latency = <660>;
+		qcom,pm-qos-wakeup-latency = <460>;
+
+		/* Enable context aware freq. scaling */
+		qcom,enable-ca-jump;
+		/* Context aware jump busy penalty in us */
+		qcom,ca-busy-penalty = <12000>;
+		/* Context aware jump target power level */
+		qcom,ca-target-pwrlevel = <1>;
+
+		qcom,gpu-speed-bin = <0x41a0 0x1fe00000 21>;
+
+		qcom,gpu-coresights {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "qcom,gpu-coresight";
+
+			qcom,gpu-coresight@0 {
+				reg = <0>;
+				coresight-name = "coresight-gfx";
+				coresight-atid = <50>;
+				port {
+					gfx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx>;
+					};
+				};
+			};
+
+			qcom,gpu-coresight@1 {
+				reg = <1>;
+				coresight-name = "coresight-gfx-cx";
+				coresight-atid = <51>;
+				port {
+					gfx_cx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx_cx>;
+					};
+				};
+			};
+		};
+
+		/* GPU Mempools */
+		qcom,gpu-mempools {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "qcom,gpu-mempools";
+
+			/* 4K Page Pool configuration */
+			qcom,gpu-mempool@0 {
+				reg = <0>;
+				qcom,mempool-page-size = <4096>;
+				qcom,mempool-allocate;
+			};
+			/* 8K Page Pool configuration */
+			qcom,gpu-mempool@1 {
+				reg = <1>;
+				qcom,mempool-page-size = <8192>;
+				qcom,mempool-allocate;
+			};
+			/* 64K Page Pool configuration */
+			qcom,gpu-mempool@2 {
+				reg = <2>;
+				qcom,mempool-page-size = <65536>;
+				qcom,mempool-reserved = <256>;
+			};
+			/* 1M Page Pool configuration */
+			qcom,gpu-mempool@3 {
+				reg = <3>;
+				qcom,mempool-page-size = <1048576>;
+				qcom,mempool-reserved = <32>;
+			};
+		};
+
+		/*
+		 * Speed-bin zero is default speed bin.
+		 * For rest of the speed bins, speed-bin value
+		 * is calulated as FMAX/4.8 MHz round up to zero
+		 * decimal places.
+		 */
+		qcom,gpu-pwrlevel-bins {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			compatible="qcom,gpu-pwrlevel-bins";
+
+			qcom,gpu-pwrlevels-0 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				qcom,speed-bin = <0>;
+
+				qcom,initial-pwrlevel = <3>;
+
+				/* SVS_L1 */
+				qcom,gpu-pwrlevel@0 {
+					reg = <0>;
+					qcom,gpu-freq = <430000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* SVS */
+				qcom,gpu-pwrlevel@1 {
+					reg = <1>;
+					qcom,gpu-freq = <355000000>;
+					qcom,bus-freq = <8>;
+					qcom,bus-min = <5>;
+					qcom,bus-max = <9>;
+				};
+
+				/* LOW SVS */
+				qcom,gpu-pwrlevel@2 {
+					reg = <2>;
+					qcom,gpu-freq = <267000000>;
+					qcom,bus-freq = <6>;
+					qcom,bus-min = <4>;
+					qcom,bus-max = <8>;
+				};
+
+				/* MIN SVS */
+				qcom,gpu-pwrlevel@3 {
+					reg = <3>;
+					qcom,gpu-freq = <180000000>;
+					qcom,bus-freq = <4>;
+					qcom,bus-min = <3>;
+					qcom,bus-max = <4>;
+				};
+
+				/* XO */
+				qcom,gpu-pwrlevel@4 {
+					reg = <4>;
+					qcom,gpu-freq = <0>;
+					qcom,bus-freq = <0>;
+					qcom,bus-min = <0>;
+					qcom,bus-max = <0>;
+				};
+			};
+
+			qcom,gpu-pwrlevels-1 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				qcom,speed-bin = <90>;
+
+				qcom,initial-pwrlevel = <3>;
+
+				/* SVS_L1 */
+				qcom,gpu-pwrlevel@0 {
+					reg = <0>;
+					qcom,gpu-freq = <430000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* SVS */
+				qcom,gpu-pwrlevel@1 {
+					reg = <1>;
+					qcom,gpu-freq = <355000000>;
+					qcom,bus-freq = <8>;
+					qcom,bus-min = <5>;
+					qcom,bus-max = <9>;
+				};
+
+				/* LOW SVS */
+				qcom,gpu-pwrlevel@2 {
+					reg = <2>;
+					qcom,gpu-freq = <267000000>;
+					qcom,bus-freq = <6>;
+					qcom,bus-min = <4>;
+					qcom,bus-max = <8>;
+				};
+
+				/* MIN SVS */
+				qcom,gpu-pwrlevel@3 {
+					reg = <3>;
+					qcom,gpu-freq = <180000000>;
+					qcom,bus-freq = <4>;
+					qcom,bus-min = <3>;
+					qcom,bus-max = <4>;
+				};
+
+				/* XO */
+				qcom,gpu-pwrlevel@4 {
+					reg = <4>;
+					qcom,gpu-freq = <0>;
+					qcom,bus-freq = <0>;
+					qcom,bus-min = <0>;
+					qcom,bus-max = <0>;
+				};
+
+			};
+
+			qcom,gpu-pwrlevels-2 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				qcom,speed-bin = <146>;
+
+				qcom,initial-pwrlevel = <6>;
+
+				/* TURBO */
+				qcom,gpu-pwrlevel@0 {
+					reg = <0>;
+					qcom,gpu-freq = <700000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* NOM_L1 */
+				qcom,gpu-pwrlevel@1 {
+					reg = <1>;
+					qcom,gpu-freq = <650000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* NOM */
+				qcom,gpu-pwrlevel@2 {
+					reg = <2>;
+					qcom,gpu-freq = <565000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* SVS_L1 */
+				qcom,gpu-pwrlevel@3 {
+					reg = <3>;
+					qcom,gpu-freq = <430000000>;
+					qcom,bus-freq = <11>;
+					qcom,bus-min = <8>;
+					qcom,bus-max = <11>;
+				};
+
+				/* SVS */
+				qcom,gpu-pwrlevel@4 {
+					reg = <4>;
+					qcom,gpu-freq = <355000000>;
+					qcom,bus-freq = <8>;
+					qcom,bus-min = <5>;
+					qcom,bus-max = <9>;
+				};
+
+				/* LOW SVS */
+				qcom,gpu-pwrlevel@5 {
+					reg = <5>;
+					qcom,gpu-freq = <267000000>;
+					qcom,bus-freq = <6>;
+					qcom,bus-min = <4>;
+					qcom,bus-max = <8>;
+				};
+
+				/* MIN SVS */
+				qcom,gpu-pwrlevel@6 {
+					reg = <6>;
+					qcom,gpu-freq = <180000000>;
+					qcom,bus-freq = <4>;
+					qcom,bus-min = <3>;
+					qcom,bus-max = <4>;
+				};
+
+				/* XO */
+				qcom,gpu-pwrlevel@7 {
+					reg = <7>;
+					qcom,gpu-freq = <0>;
+					qcom,bus-freq = <0>;
+					qcom,bus-min = <0>;
+					qcom,bus-max = <0>;
+				};
+
+			};
+
+		};
+
+	};
+
+	kgsl_msm_iommu: qcom,kgsl-iommu {
+		compatible = "qcom,kgsl-smmu-v2";
+
+		reg = <0x05040000 0x10000>;
+		qcom,protect = <0x40000 0x10000>;
+		qcom,micro-mmu-control = <0x6000>;
+
+		clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
+			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
+
+		clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
+
+		qcom,secure_align_mask = <0xfff>;
+		qcom,retention;
+		qcom,hyp_secure_alloc;
+
+		gfx3d_user: gfx3d_user {
+			compatible = "qcom,smmu-kgsl-cb";
+			label = "gfx3d_user";
+			iommus = <&kgsl_smmu 0>;
+			qcom,gpu-offset = <0x48000>;
+		};
+
+		gfx3d_secure: gfx3d_secure {
+			compatible = "qcom,smmu-kgsl-cb";
+			iommus = <&kgsl_smmu 2>;
+		};
+	};
+
+	gmu: qcom,gmu {
+		label = "kgsl-gmu";
+		compatible = "qcom,gpu-gmu";
+
+		reg =
+			<0x506a000 0x31000>,
+			<0xb200000 0x300000>;
+		reg-names =
+			"kgsl_gmu_reg",
+			"kgsl_gmu_pdc_reg";
+
+		interrupts = <0 304 0>, <0 305 0>;
+		interrupt-names = "kgsl_hfi_irq", "kgsl_gmu_irq";
+
+		qcom,msm-bus,name = "cnoc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<26 10036 0 0>,      /* CNOC off */
+			<26 10036 0 100>;    /* CNOC on  */
+
+		regulator-names = "vddcx", "vdd";
+		vddcx-supply = <&gpu_cx_gdsc>;
+		vdd-supply = <&gpu_gx_gdsc>;
+
+
+		clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
+				<&clock_gpucc GPU_CC_CXO_CLK>,
+				<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
+				<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
+
+		clock-names = "gmu_clk", "cxo_clk", "axi_clk",
+				"memnoc_clk";
+
+		qcom,gmu-pwrlevels {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			compatible = "qcom,gmu-pwrlevels";
+
+			qcom,gmu-pwrlevel@0 {
+				reg = <0>;
+				qcom,gmu-freq = <0>;
+			};
+
+			qcom,gmu-pwrlevel@1 {
+				reg = <1>;
+				qcom,gmu-freq = <200000000>;
+			};
+		};
+
+		gmu_user: gmu_user {
+			compatible = "qcom,smmu-gmu-user-cb";
+			iommus = <&kgsl_smmu 4>;
+		};
+
+		gmu_kernel: gmu_kernel {
+			compatible = "qcom,smmu-gmu-kernel-cb";
+			iommus = <&kgsl_smmu 5>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-usbc-audio-overlay.dtsi
similarity index 70%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm670-int-cdc-usbc-audio-overlay.dtsi
index c06b806..df10e7d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-int-cdc-usbc-audio-overlay.dtsi
@@ -9,15 +9,9 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+#include "sdm670-audio-overlay.dtsi"
 
-
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+&int_codec {
+	qcom,msm-mbhc-usbc-audio-supported = <1>;
+	qcom,usbc-analog-en1-gpio = <&wcd_usbc_analog_en1_gpio>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
index 6e92f0e..c64ed2c 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-lpi.dtsi
@@ -26,8 +26,8 @@
 
 			config {
 				pins = "gpio18";
-				drive-strength = <8>;
-				output-high;
+				drive-strength = <2>;
+				output-low;
 			};
 		};
 
@@ -53,8 +53,8 @@
 
 			config {
 				pins = "gpio19";
-				drive-strength = <8>;
-				output-high;
+				drive-strength = <2>;
+				output-low;
 			};
 		};
 
@@ -80,8 +80,8 @@
 
 			config {
 				pins = "gpio21";
-				drive-strength = <8>;
-				output-high;
+				drive-strength = <2>;
+				output-low;
 			};
 		};
 
@@ -107,8 +107,8 @@
 
 			config {
 				pins = "gpio23", "gpio25";
-				drive-strength = <8>;
-				output-high;
+				drive-strength = <2>;
+				output-low;
 			};
 		};
 
@@ -159,7 +159,7 @@
 
 			config {
 				pins = "gpio22";
-				drive-strength = <8>;
+				drive-strength = <2>;
 			};
 		};
 
@@ -184,7 +184,7 @@
 
 			config {
 				pins = "gpio24";
-				drive-strength = <8>;
+				drive-strength = <2>;
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
index c8537bc..ac254fd 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp-overlay.dts
@@ -20,10 +20,14 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
 #include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 MTP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
 	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
 	qcom,msm-id = <336 0x0>;
 	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
index 1de40b7..1241a20 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dts
@@ -15,9 +15,13 @@
 
 #include "sdm670.dtsi"
 #include "sdm670-mtp.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM670 MTP";
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L MTP";
 	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
 	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 0cf48a3..ef1fc08 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -10,6 +10,44 @@
  * GNU General Public License for more details.
  */
 
+#include <dt-bindings/gpio/gpio.h>
+#include "sdm670-pmic-overlay.dtsi"
+#include "sdm670-sde-display.dtsi"
+#include "sdm670-camera-sensor-mtp.dtsi"
+#include "smb1355.dtsi"
+
+&ufsphy_mem {
+	compatible = "qcom,ufs-phy-qmp-v3";
+
+	vdda-phy-supply = <&pm660l_l1>; /* 0.88v */
+	vdda-pll-supply = <&pm660_l1>; /* 1.2v */
+	vdda-phy-max-microamp = <62900>;
+	vdda-pll-max-microamp = <18300>;
+
+	status = "ok";
+};
+
+&ufshc_mem {
+	vdd-hba-supply = <&ufs_phy_gdsc>;
+	vdd-hba-fixed-regulator;
+	vcc-supply = <&pm660l_l4>;
+	vcc-voltage-level = <2960000 2960000>;
+	vccq2-supply = <&pm660_l8>;
+	vcc-max-microamp = <600000>;
+	vccq2-max-microamp = <600000>;
+
+	qcom,vddp-ref-clk-supply = <&pm660_l1>;
+	qcom,vddp-ref-clk-max-microamp = <100>;
+
+	status = "ok";
+};
+
+&pm660l_switch1 {
+	pinctrl-names = "led_enable", "led_disable";
+	pinctrl-0 = <&flash_led3_front_en>;
+	pinctrl-1 = <&flash_led3_front_dis>;
+};
+
 &qupv3_se9_2uart {
 	status = "disabled";
 };
@@ -23,13 +61,305 @@
 };
 
 &qupv3_se3_i2c {
-	status = "disabled";
+	status = "ok";
+	nq@28 {
+		compatible = "qcom,nq-nci";
+		reg = <0x28>;
+		qcom,nq-irq = <&tlmm 44 0x00>;
+		qcom,nq-ven = <&tlmm 12 0x00>;
+		qcom,nq-firm = <&tlmm 43 0x00>;
+		qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+		qcom,nq-esepwr = <&tlmm 116 0x00>;
+		interrupt-parent = <&tlmm>;
+		qcom,clk-src = "BBCLK3";
+		interrupts = <44 0>;
+		interrupt-names = "nfc_irq";
+		pinctrl-names = "nfc_active", "nfc_suspend";
+		pinctrl-0 = <&nfc_int_active
+			     &nfc_enable_active
+			     &nfc_clk_default>;
+		pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+		clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+		clock-names = "ref_clk";
+	};
 };
 
 &qupv3_se10_i2c {
-	status = "disabled";
+	status = "ok";
 };
 
 &qupv3_se6_4uart {
-	status = "disabled";
+	status = "ok";
+};
+
+&sdhc_1 {
+	vdd-supply = <&pm660l_l4>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 570000>;
+
+	vdd-io-supply = <&pm660_l8>;
+	qcom,vdd-io-always-on;
+	qcom,vdd-io-lpm-sup;
+	qcom,vdd-io-voltage-level = <1800000 1800000>;
+	qcom,vdd-io-current-level = <200 325000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc1_clk_on  &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+	pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+	status = "ok";
+};
+
+&sdhc_2 {
+	vdd-supply = <&pm660l_l5>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 800000>;
+
+	vdd-io-supply = <&pm660l_l2>;
+	qcom,vdd-io-voltage-level = <1800000 2960000>;
+	qcom,vdd-io-current-level = <200 22000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+	cd-gpios = <&tlmm 96 0x1>;
+
+	status = "ok";
+};
+
+&vendor {
+	mtp_batterydata: qcom,battery-data {
+		qcom,batt-id-range-pct = <15>;
+		#include "fg-gen3-batterydata-itech-3000mah.dtsi"
+		#include "fg-gen3-batterydata-ascent-3450mah.dtsi"
+		#include "fg-gen3-batterydata-demo-6000mah.dtsi"
+	};
+};
+
+&pm660_fg {
+	qcom,battery-data = <&mtp_batterydata>;
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio54";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio54";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_shutdown_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		io-channels = <&pm660_rradc 2>,
+				<&pm660_rradc 12>;
+		io-channel-names = "charger_temp",
+				"charger_temp_max";
+		status ="ok";
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+			&smb_shutdown_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		io-channels = <&pm660_rradc 2>,
+				<&pm660_rradc 12>;
+		io-channel-names = "charger_temp",
+				"charger_temp_max";
+		status ="ok";
+	};
+};
+
+&soc {
+	gpio_keys {
+		compatible = "gpio-keys";
+		label = "gpio-keys";
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&key_cam_snapshot_default
+			     &key_cam_focus_default
+			     &key_vol_up_default>;
+
+		cam_snapshot {
+			label = "cam_snapshot";
+			gpios = <&tlmm 91 0>;
+			linux,input-type = <1>;
+			linux,code = <766>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		cam_focus {
+			label = "cam_focus";
+			gpios = <&tlmm 92 0>;
+			linux,input-type = <1>;
+			linux,code = <528>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm660l_gpios 7 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+	};
+};
+
+&dsi_dual_nt35597_truly_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_truly_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35597_truly_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35597_truly_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "single_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_sim_vid {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_vid {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_sim_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_sim_dsc_375_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_sim_dsc_375_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_dcs";
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply_labibb_amoled>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_nt35695b_truly_fhd_video {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
+	qcom,dsi-display-active;
+};
+
+&pm660l_wled {
+	status = "okay";
+	qcom,led-strings-list = [01 02];
+};
+
+&mdss_mdp {
+	#cooling-cells = <2>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index 73df253..d4953c1 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -908,7 +908,7 @@
 			qupv3_se12_2uart_active: qupv3_se12_2uart_active {
 				mux {
 					pins = "gpio51", "gpio52";
-					function = "qup9";
+					function = "qup12";
 				};
 
 				config {
@@ -1196,6 +1196,20 @@
 			};
 		};
 
+		sdc1_rclk_on: sdc1_rclk_on {
+			config {
+				pins = "sdc1_rclk";
+				bias-pull-down; /* pull down */
+			};
+		};
+
+		sdc1_rclk_off: sdc1_rclk_off {
+			config {
+				pins = "sdc1_rclk";
+				bias-pull-down; /* pull down */
+			};
+		};
+
 		sdc2_clk_on: sdc2_clk_on {
 			config {
 				pins = "sdc2_clk";
@@ -1244,6 +1258,32 @@
 			};
 		};
 
+		sdc2_cd_on: cd_on {
+			mux {
+				pins = "gpio96";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio96";
+				drive-strength = <2>;
+				bias-pull-up;
+			};
+		};
+
+		sdc2_cd_off: cd_off {
+			mux {
+				pins = "gpio96";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio96";
+				drive-strength = <2>;
+				bias-disable;
+			};
+		};
+
 		/* USB C analog configuration */
 		wcd_usbc_analog_en1 {
 			wcd_usbc_analog_en1_idle: wcd_usbc_ana_en1_idle {
@@ -1331,6 +1371,70 @@
 			};
 		};
 
+		nfc {
+			nfc_int_active: nfc_int_active {
+				/* active state */
+				mux {
+					/* GPIO 44 NFC Read Interrupt */
+					pins = "gpio44";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio44";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+			};
+
+			nfc_int_suspend: nfc_int_suspend {
+				/* sleep state */
+				mux {
+					/* GPIO 44 NFC Read Interrupt */
+					pins = "gpio44";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio44";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+			};
+
+			nfc_enable_active: nfc_enable_active {
+				/* active state */
+				mux {
+					/* 12: NFC ENABLE 43: FW DNLD */
+					/* 116: ESE Enable */
+					pins = "gpio12", "gpio43", "gpio116";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio12", "gpio43", "gpio116";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+			};
+
+			nfc_enable_suspend: nfc_enable_suspend {
+				/* sleep state */
+				mux {
+					/* 12: NFC ENABLE 43: FW DNLD */
+					/* 116: ESE Enable */
+					pins = "gpio12", "gpio43", "gpio116";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio12", "gpio43", "gpio116";
+					drive-strength = <2>; /* 2 MA */
+					bias-disable;
+				};
+			};
+		};
+
 		/* WSA speaker reset pins */
 		spkr_1_sd_n {
 			spkr_1_sd_n_sleep: spkr_1_sd_n_sleep {
@@ -1392,6 +1496,22 @@
 			};
 		};
 
+		wcd_buck_vsel {
+			wcd_buck_vsel_default: wcd_buck_vsel_default{
+				mux {
+					pins = "gpio94";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio94";
+					drive-strength = <8>; /* 8 mA */
+					bias-pull-down; /* pull down */
+					output-high;
+				};
+			};
+		};
+
 		wcd_gnd_mic_swap {
 			wcd_gnd_mic_swap_idle: wcd_gnd_mic_swap_idle {
 				mux {
@@ -1437,5 +1557,482 @@
 				};
 			};
 		};
+
+		flash_led3_front {
+			flash_led3_front_en: flash_led3_front_en {
+				mux {
+					pins = "gpio21";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio21";
+					drive_strength = <2>;
+					output-high;
+					bias-disable;
+				};
+			};
+
+			flash_led3_front_dis: flash_led3_front_dis {
+				mux {
+					pins = "gpio21";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio21";
+					drive_strength = <2>;
+					output-low;
+					bias-disable;
+				};
+			};
+		};
+
+		/* Pinctrl setting for CAMERA GPIO key */
+		key_cam_snapshot {
+			key_cam_snapshot_default: key_cam_snapshot_default {
+				pins = "gpio91";
+				function = "normal";
+				input-enable;
+				bias-pull-up;
+				power-source = <0>;
+			};
+		};
+
+		key_cam_focus {
+			key_cam_focus_default: key_cam_focus_default {
+				pins = "gpio92";
+				function = "normal";
+				input-enable;
+				bias-pull-up;
+				power-source = <0>;
+			};
+		};
+
+		pmx_sde: pmx_sde {
+			sde_dsi_active: sde_dsi_active {
+				mux {
+					pins = "gpio75", "gpio76";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio75", "gpio76";
+					drive-strength = <8>;   /* 8 mA */
+					bias-disable = <0>;   /* no pull */
+				};
+			};
+			sde_dsi_suspend: sde_dsi_suspend {
+				mux {
+					pins = "gpio75", "gpio76";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio75", "gpio76";
+					drive-strength = <2>;   /* 2 mA */
+					bias-pull-down;         /* PULL DOWN */
+				};
+			};
+		};
+
+		pmx_sde_te {
+			sde_te_active: sde_te_active {
+				mux {
+					pins = "gpio10";
+					function = "mdp_vsync";
+				};
+
+				config {
+					pins = "gpio10";
+					drive-strength = <2>;   /* 2 mA */
+					bias-pull-down;         /* PULL DOWN */
+				};
+			};
+
+			sde_te_suspend: sde_te_suspend {
+				mux {
+					pins = "gpio10";
+					function = "mdp_vsync";
+				};
+
+				config {
+					pins = "gpio10";
+					drive-strength = <2>;   /* 2 mA */
+					bias-pull-down;         /* PULL DOWN */
+				};
+			};
+		};
+
+		sde_dp_aux_active: sde_dp_aux_active {
+			mux {
+				pins = "gpio40", "gpio50";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio40", "gpio50";
+				bias-disable = <0>; /* no pull */
+				drive-strength = <8>;
+			};
+		};
+
+		sde_dp_aux_suspend: sde_dp_aux_suspend {
+			mux {
+				pins = "gpio40", "gpio50";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio40", "gpio50";
+				bias-pull-down;
+				drive-strength = <2>;
+			};
+		};
+
+		sde_dp_usbplug_cc_active: sde_dp_usbplug_cc_active {
+			mux {
+				pins = "gpio38";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio38";
+				bias-disable;
+				drive-strength = <16>;
+			};
+		};
+
+		sde_dp_usbplug_cc_suspend: sde_dp_usbplug_cc_suspend {
+			mux {
+				pins = "gpio38";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio38";
+				bias-pull-down;
+				drive-strength = <2>;
+			};
+		};
+
+		cci0_active: cci0_active {
+			mux {
+				/* CLK, DATA */
+				pins = "gpio17","gpio18";
+				function = "cci_i2c";
+			};
+
+			config {
+				pins = "gpio17","gpio18";
+				bias-pull-up; /* PULL UP*/
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cci0_suspend: cci0_suspend {
+			mux {
+				/* CLK, DATA */
+				pins = "gpio17","gpio18";
+				function = "cci_i2c";
+			};
+
+			config {
+				pins = "gpio17","gpio18";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cci1_active: cci1_active {
+			mux {
+				/* CLK, DATA */
+				pins = "gpio19","gpio20";
+				function = "cci_i2c";
+			};
+
+			config {
+				pins = "gpio19","gpio20";
+				bias-pull-up; /* PULL UP*/
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cci1_suspend: cci1_suspend {
+			mux {
+				/* CLK, DATA */
+				pins = "gpio19","gpio20";
+				function = "cci_i2c";
+			};
+
+			config {
+				pins = "gpio19","gpio20";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_active: cam_sensor_rear_active {
+			/* RESET */
+			mux {
+				pins = "gpio30";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio30";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_suspend: cam_sensor_rear_suspend {
+			/* RESET */
+			mux {
+				pins = "gpio30";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio30";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+				output-low;
+			};
+		};
+
+		cam_sensor_rear_vana: cam_sensor_rear_vana {
+			/*  AVDD LDO */
+			mux {
+				pins = "gpio8";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio8";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear_vio: cam_sensor_rear_vio {
+			/* DOVDD LDO */
+			mux {
+				pins = "gpio29";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio29";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk0_active: cam_sensor_mclk0_active {
+			/* MCLK0 */
+			mux {
+				pins = "gpio13";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio13";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk0_suspend: cam_sensor_mclk0_suspend {
+			/* MCLK0 */
+			mux {
+				pins = "gpio13";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio13";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front_active: cam_sensor_front_active {
+			/* RESET  */
+			mux {
+				pins = "gpio9";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_front_suspend: cam_sensor_front_suspend {
+			/* RESET */
+			mux {
+				pins = "gpio9";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+				output-low;
+			};
+		};
+
+		cam_sensor_rear2_active: cam_sensor_rear2_active {
+			/* RESET */
+			mux {
+				pins = "gpio28";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio28";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_rear2_suspend: cam_sensor_rear2_suspend {
+			/* RESET */
+			mux {
+				pins = "gpio28";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio28";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+				output-low;
+			};
+		};
+
+		cam_sensor_mclk1_active: cam_sensor_mclk1_active {
+			/* MCLK1 */
+			mux {
+				pins = "gpio14";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio14";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk1_suspend: cam_sensor_mclk1_suspend {
+			/* MCLK1 */
+			mux {
+				pins = "gpio14";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio14";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk2_active: cam_sensor_mclk2_active {
+			/* MCLK2 */
+			mux {
+				pins = "gpio15";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio15";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk2_suspend: cam_sensor_mclk2_suspend {
+			/* MCLK2 */
+			mux {
+				pins = "gpio15";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio15";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+	};
+};
+
+&pm660l_gpios {
+	camera_rear_dvdd_en {
+		camera_rear_dvdd_en_default: camera_rear_dvdd_en_default {
+			pins = "gpio4";
+			function = "normal";
+			power-source = <0>;
+			output-low;
+		};
+	};
+
+	camera_dvdd_en {
+		camera_dvdd_en_default: camera_dvdd_en_default {
+			pins = "gpio3";
+			function = "normal";
+			power-source = <0>;
+			output-low;
+		};
+	};
+};
+
+&pm660l_gpios {
+	camera0_dvdd_en_default: camera0_dvdd_en_default {
+		pins = "gpio3";
+		function = "normal";
+		power-source = <0>;
+		output-low;
+	};
+
+	camera1_dvdd_en_default: camera1_dvdd_en_default {
+		pins = "gpio4";
+		function = "normal";
+		power-source = <0>;
+		output-low;
+	};
+
+	key_vol_up {
+		key_vol_up_default: key_vol_up_default {
+			pins = "gpio7";
+			function = "normal";
+			input-enable;
+			bias-pull-up;
+			power-source = <0>;
+		};
+	};
+};
+
+&pm660_gpios {
+	nfc_clk {
+		nfc_clk_default: nfc_clk_default {
+			pins = "gpio4";
+			function = "normal";
+			input-enable;
+			power-source = <1>;
+		};
+	};
+	smb_shutdown_default: smb_shutdown_default {
+		pins = "gpio11";
+		function = "normal";
+		power-source = <0>;
+		qcom,drive-strength = <3>;
+		output-high;
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
new file mode 100644
index 0000000..0ea4b1f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp-overlay.dts
@@ -0,0 +1,42 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_rm67195_amoled_fhd_cmd_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
new file mode 100644
index 0000000..1cf52f5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-cdp.dts
@@ -0,0 +1,36 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
+
+&dsi_dual_nt35597_truly_video_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_rm67195_amoled_fhd_cmd_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
similarity index 66%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
index 2fac9e8..1550661 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp-overlay.dts
@@ -19,11 +19,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
new file mode 100644
index 0000000..14f48a0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pm660a-mtp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 0>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
new file mode 100644
index 0000000..220487a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -0,0 +1,384 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&pm660_0{
+	pm660_charger: qcom,qpnp-smb2 {
+		compatible = "qcom,qpnp-smb2";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		#cooling-cells = <2>;
+
+		qcom,pmic-revid = <&pm660_revid>;
+
+		io-channels = <&pm660_rradc 8>,
+			      <&pm660_rradc 10>,
+			      <&pm660_rradc 3>,
+			      <&pm660_rradc 4>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max",
+				   "usbin_i",
+				   "usbin_v";
+
+		qcom,wipower-max-uw = <5000000>;
+
+		dpdm-supply = <&qusb_phy0>;
+
+		qcom,thermal-mitigation
+				= <3000000 2500000 2000000 1500000
+					1000000 500000>;
+		qcom,auto-recharge-soc;
+
+		qcom,chgr@1000 {
+			reg = <0x1000 0x100>;
+			interrupts =
+				<0x0 0x10 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x1 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x2 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x3 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x10 0x4 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "chg-error",
+					  "chg-state-change",
+					  "step-chg-state-change",
+					  "step-chg-soc-update-fail",
+					  "step-chg-soc-update-request";
+		};
+
+		qcom,otg@1100 {
+			reg = <0x1100 0x100>;
+			interrupts = <0x0 0x11 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x2 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x11 0x3 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "otg-fail",
+					  "otg-overcurrent",
+					  "otg-oc-dis-sw-sts",
+					  "testmode-change-detect";
+		};
+
+		qcom,bat-if@1200 {
+			reg = <0x1200 0x100>;
+			interrupts =
+				<0x0 0x12 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x12 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x12 0x5 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "bat-temp",
+					  "bat-ocp",
+					  "bat-ov",
+					  "bat-low",
+					  "bat-therm-or-id-missing",
+					  "bat-terminal-missing";
+		};
+
+		qcom,usb-chgpth@1300 {
+			reg = <0x1300 0x100>;
+			interrupts =
+				<0x0 0x13 0x0 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x13 0x5 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x13 0x6 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x13 0x7 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "usbin-collapse",
+					  "usbin-lt-3p6v",
+					  "usbin-uv",
+					  "usbin-ov",
+					  "usbin-plugin",
+					  "usbin-src-change",
+					  "usbin-icl-change",
+					  "type-c-change";
+		};
+
+		qcom,dc-chgpth@1400 {
+			reg = <0x1400 0x100>;
+			interrupts =
+				<0x0 0x14 0x0 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x1 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x5 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x14 0x6 IRQ_TYPE_EDGE_RISING>;
+
+			interrupt-names = "dcin-collapse",
+					  "dcin-lt-3p6v",
+					  "dcin-uv",
+					  "dcin-ov",
+					  "dcin-plugin",
+					  "div2-en-dg",
+					  "dcin-icl-change";
+		};
+
+		qcom,chgr-misc@1600 {
+			reg = <0x1600 0x100>;
+			interrupts =
+				<0x0 0x16 0x0 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+				<0x0 0x16 0x2 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x3 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x4 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x5 IRQ_TYPE_EDGE_BOTH>,
+				<0x0 0x16 0x6 IRQ_TYPE_EDGE_FALLING>,
+				<0x0 0x16 0x7 IRQ_TYPE_EDGE_BOTH>;
+
+			interrupt-names = "wdog-snarl",
+					  "wdog-bark",
+					  "aicl-fail",
+					  "aicl-done",
+					  "high-duty-cycle",
+					  "input-current-limiting",
+					  "temperature-change",
+					  "switcher-power-ok";
+		};
+		smb2_vbus: qcom,smb2-vbus {
+			regulator-name = "smb2-vbus";
+		};
+
+		smb2_vconn: qcom,smb2-vconn {
+			regulator-name = "smb2-vconn";
+		};
+	};
+
+	pm660_rradc: rradc@4500 {
+		compatible = "qcom,rradc";
+		reg = <0x4500 0x100>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		#io-channel-cells = <1>;
+		qcom,pmic-revid = <&pm660_revid>;
+	};
+
+	pm660_fg: qpnp,fg {
+		compatible = "qcom,fg-gen3";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,pmic-revid = <&pm660_revid>;
+		io-channels = <&pm660_rradc 0>,
+			      <&pm660_rradc 7>;
+		io-channel-names = "rradc_batt_id",
+				   "rradc_die_temp";
+		qcom,rradc-base = <0x4500>;
+		qcom,fg-esr-timer-awake = <96 96>;
+		qcom,fg-esr-timer-asleep = <256 256>;
+		qcom,fg-esr-timer-charging = <0 96>;
+		qcom,cycle-counter-en;
+		qcom,hold-soc-while-full;
+		qcom,fg-auto-recharge-soc;
+		qcom,fg-recharge-soc-thr = <98>;
+		status = "okay";
+
+		qcom,fg-batt-soc@4000 {
+			status = "okay";
+			reg = <0x4000 0x100>;
+			interrupts = <0x0 0x40 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x2
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x3
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x4 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x5
+						IRQ_TYPE_EDGE_RISING>,
+				     <0x0 0x40 0x6 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x40 0x7 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "soc-update",
+					  "soc-ready",
+					  "bsoc-delta",
+					  "msoc-delta",
+					  "msoc-low",
+					  "msoc-empty",
+					  "msoc-high",
+					  "msoc-full";
+		};
+
+		qcom,fg-batt-info@4100 {
+			status = "okay";
+			reg = <0x4100 0x100>;
+			interrupts = <0x0 0x41 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x2 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x3 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x41 0x6 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "vbatt-pred-delta",
+					  "vbatt-low",
+					  "esr-delta",
+					  "batt-missing",
+					  "batt-temp-delta";
+		};
+
+		qcom,fg-memif@4400 {
+			status = "okay";
+			reg = <0x4400 0x100>;
+			interrupts = <0x0 0x44 0x0 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x44 0x1 IRQ_TYPE_EDGE_BOTH>,
+				     <0x0 0x44 0x2 IRQ_TYPE_EDGE_BOTH>;
+			interrupt-names = "ima-rdy",
+					  "mem-xcp",
+					  "dma-grant";
+		};
+	};
+};
+
+&pm660_1 {
+	pm660_haptics: qcom,haptics@c000 {
+		compatible = "qcom,qpnp-haptics";
+		reg = <0xc000 0x100>;
+		interrupts = <0x1 0xc0 0x0 IRQ_TYPE_EDGE_BOTH>,
+			     <0x1 0xc0 0x1 IRQ_TYPE_EDGE_BOTH>;
+		interrupt-names = "hap-sc-irq", "hap-play-irq";
+		qcom,pmic-revid = <&pm660_revid>;
+		qcom,pmic-misc = <&pm660_misc>;
+		qcom,misc-clk-trim-error-reg = <0xf3>;
+		qcom,actuator-type = <0>;
+		qcom,play-mode = "direct";
+		qcom,vmax-mv = <3200>;
+		qcom,ilim-ma = <800>;
+		qcom,sc-dbc-cycles = <8>;
+		qcom,wave-play-rate-us = <6667>;
+		qcom,en-brake;
+		qcom,lra-high-z = "opt0";
+		qcom,lra-auto-res-mode = "qwd";
+		qcom,lra-res-cal-period = <4>;
+	};
+};
+
+&pm660l_3 {
+	pm660l_wled: qcom,leds@d800 {
+		compatible = "qcom,qpnp-wled";
+		reg = <0xd800 0x100>,
+			<0xd900 0x100>;
+		reg-names = "qpnp-wled-ctrl-base",
+				"qpnp-wled-sink-base";
+		interrupts = <0x3 0xd8 0x1 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "ovp-irq";
+		linux,name = "wled";
+		linux,default-trigger = "bkl-trigger";
+		qcom,fdbk-output = "auto";
+		qcom,vref-uv = <127500>;
+		qcom,switch-freq-khz = <800>;
+		qcom,ovp-mv = <29600>;
+		qcom,ilim-ma = <970>;
+		qcom,boost-duty-ns = <26>;
+		qcom,mod-freq-khz = <9600>;
+		qcom,dim-mode = "hybrid";
+		qcom,hyb-thres = <625>;
+		qcom,sync-dly-us = <800>;
+		qcom,fs-curr-ua = <25000>;
+		qcom,cons-sync-write-delay-us = <1000>;
+		qcom,led-strings-list = [00 01 02];
+		qcom,loop-auto-gm-en;
+		qcom,pmic-revid = <&pm660l_revid>;
+		qcom,auto-calibration-enable;
+		status = "ok";
+	};
+
+	pm660l_lcdb: qpnp-lcdb@ec00 {
+		compatible = "qcom,qpnp-lcdb-regulator";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0xec00 0x100>;
+		interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "sc-irq";
+
+		qcom,pmic-revid = <&pm660l_revid>;
+
+		lcdb_ldo_vreg: ldo {
+			label = "ldo";
+			regulator-name = "lcdb_ldo";
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6000000>;
+		};
+
+		lcdb_ncp_vreg: ncp {
+			label = "ncp";
+			regulator-name = "lcdb_ncp";
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6000000>;
+		};
+	};
+
+	pm660a_oledb: qpnp-oledb@e000 {
+	       compatible = "qcom,qpnp-oledb-regulator";
+	       #address-cells = <1>;
+	       #size-cells = <1>;
+	       qcom,pmic-revid = <&pm660l_revid>;
+	       reg = <0xe000 0x100>;
+	       qcom,pbs-client = <&pm660l_pbs>;
+
+	       label = "oledb";
+	       regulator-name = "regulator-oledb";
+	       regulator-min-microvolt = <5000000>;
+	       regulator-max-microvolt = <8100000>;
+
+	       qcom,swire-control;
+	       qcom,ext-pin-control;
+	       status = "disabled";
+	};
+
+	pm660a_labibb: qpnp-labibb-regulator {
+		compatible = "qcom,qpnp-labibb-regulator";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,pmic-revid = <&pm660l_revid>;
+		qcom,swire-control;
+		status = "disabled";
+
+		ibb_regulator: qcom,ibb@dc00 {
+			reg = <0xdc00 0x100>;
+			reg-names = "ibb_reg";
+			regulator-name = "ibb_reg";
+
+			regulator-min-microvolt = <4000000>;
+			regulator-max-microvolt = <6300000>;
+
+			qcom,qpnp-ibb-min-voltage = <1400000>;
+			qcom,qpnp-ibb-step-size = <100000>;
+			qcom,qpnp-ibb-slew-rate = <2000000>;
+			qcom,qpnp-ibb-init-voltage = <4000000>;
+			qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+		};
+
+		lab_regulator: qcom,lab@de00 {
+			reg = <0xde00 0x100>;
+			reg-names = "lab";
+			regulator-name = "lab_reg";
+
+			regulator-min-microvolt = <4600000>;
+			regulator-max-microvolt = <6100000>;
+
+			qcom,qpnp-lab-min-voltage = <4600000>;
+			qcom,qpnp-lab-step-size = <100000>;
+			qcom,qpnp-lab-slew-rate = <5000>;
+			qcom,qpnp-lab-init-voltage = <4600000>;
+			qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+
+			qcom,notify-lab-vreg-ok-sts;
+		};
+	};
+};
+
+&pm660_pdphy {
+	vbus-supply = <&smb2_vbus>;
+	vconn-supply = <&smb2_vconn>;
+};
+
+&usb0 {
+	extcon = <&pm660_pdphy>, <&pm660_pdphy>, <&eud>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
similarity index 69%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
index 2fac9e8..36d485e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
@@ -19,11 +19,14 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-qrd.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
+	compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <0x0002000b 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
similarity index 69%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
index 2fac9e8..37eb4cd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
@@ -19,11 +19,14 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-qrd.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
+	compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <0x0012000b 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
index c06b806..dada4c6 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
@@ -13,11 +13,14 @@
 
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm670.dtsi"
+#include "sdm670-qrd.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD SKU2";
+	compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+	qcom,board-id = <0x0012000b 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm670-qrd.dts
index c06b806..c22afa4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
@@ -13,11 +13,14 @@
 
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm670.dtsi"
+#include "sdm670-qrd.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L QRD";
+	compatible = "qcom,sdm670-qrd", "qcom,sdm670", "qcom,qrd";
+	qcom,board-id = <0x0002000b 0>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
new file mode 100644
index 0000000..93e4c51
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -0,0 +1,287 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+#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_se9_2uart {
+	status = "disabled";
+};
+
+&qupv3_se12_2uart {
+	status = "ok";
+};
+
+&qupv3_se8_spi {
+	status = "disabled";
+};
+
+&qupv3_se3_i2c {
+	status = "ok";
+	nq@28 {
+		compatible = "qcom,nq-nci";
+		reg = <0x28>;
+		qcom,nq-irq = <&tlmm 44 0x00>;
+		qcom,nq-ven = <&tlmm 12 0x00>;
+		qcom,nq-firm = <&tlmm 43 0x00>;
+		qcom,nq-clkreq = <&pm660_gpios 4 0x00>;
+		qcom,nq-esepwr = <&tlmm 116 0x00>;
+		interrupt-parent = <&tlmm>;
+		qcom,clk-src = "BBCLK3";
+		interrupts = <44 0>;
+		interrupt-names = "nfc_irq";
+		pinctrl-names = "nfc_active", "nfc_suspend";
+		pinctrl-0 = <&nfc_int_active
+			     &nfc_enable_active
+			     &nfc_clk_default>;
+		pinctrl-1 = <&nfc_int_suspend &nfc_enable_suspend>;
+		clocks = <&clock_rpmh RPMH_LN_BB_CLK3>;
+		clock-names = "ref_clk";
+	};
+};
+
+&qupv3_se10_i2c {
+	status = "ok";
+};
+
+&qupv3_se6_4uart {
+	status = "ok";
+};
+
+&vendor {
+	qrd_batterydata: qcom,battery-data {
+		qcom,batt-id-range-pct = <15>;
+		#include "fg-gen3-batterydata-mlp356477-2800mah.dtsi"
+	};
+};
+
+&pm660_fg {
+	qcom,battery-data = <&qrd_batterydata>;
+	qcom,fg-bmd-en-delay-ms = <300>;
+};
+
+&pm660_charger {
+	qcom,battery-data = <&qrd_batterydata>;
+	qcom,sw-jeita-enable;
+};
+
+&tlmm {
+	smb_int_default: smb_int_default {
+		mux {
+			pins = "gpio54";
+			function = "gpio";
+		};
+		config {
+			pins = "gpio54";
+			drive-strength = <2>;
+			bias-pull-up;
+			input-enable;
+		};
+	};
+};
+
+&smb1355_0 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+		     &smb_shutdown_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_0: qcom,smb1355-charger@1000 {
+		io-channels = <&pm660_rradc 2>,
+				<&pm660_rradc 12>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max";
+		status = "ok";
+	};
+};
+
+&smb1355_1 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&smb_int_default
+		     &smb_shutdown_default>;
+	interrupt-parent = <&tlmm>;
+	interrupts = <54 IRQ_TYPE_LEVEL_LOW>;
+	smb1355_charger_1: qcom,smb1355-charger@1000 {
+		io-channels = <&pm660_rradc 2>,
+				<&pm660_rradc 12>;
+		io-channel-names = "charger_temp",
+				   "charger_temp_max";
+		status = "ok";
+	};
+};
+
+&soc {
+	gpio_keys {
+		compatible = "gpio-keys";
+		label = "gpio-keys";
+		pinctrl-names = "default";
+		pinctrl-0 = <&key_vol_up_default>;
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm660l_gpios 7 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+	};
+};
+
+&pm660_haptics {
+	qcom,vmax-mv = <1800>;
+	qcom,wave-play-rate-us = <4255>;
+	qcom,lra-auto-mode;
+	status = "okay";
+};
+
+&int_codec {
+	qcom,model = "sdm670-skuw-snd-card";
+	qcom,audio-routing =
+		"RX_BIAS", "INT_MCLK0",
+		"SPK_RX_BIAS", "INT_MCLK0",
+		"INT_LDO_H", "INT_MCLK0",
+		"MIC BIAS External2", "Headset Mic",
+		"AMIC2", "MIC BIAS External2",
+		"MIC BIAS External", "Handset Mic",
+		"AMIC1", "MIC BIAS External",
+		"MIC BIAS External", "Secondary Mic",
+		"AMIC3", "MIC BIAS External",
+		"SpkrLeft IN", "SPK1 OUT",
+		"PDM_IN_RX1", "PDM_OUT_RX1",
+		"PDM_IN_RX2", "PDM_OUT_RX2",
+		"PDM_IN_RX3", "PDM_OUT_RX3",
+		"ADC1_IN", "ADC1_OUT",
+		"ADC2_IN", "ADC2_OUT",
+		"ADC3_IN", "ADC3_OUT";
+	qcom,wsa-max-devs = <1>;
+	qcom,wsa-devs = <&wsa881x_211_en>, <&wsa881x_213_en>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
+
+&sdhc_1 {
+	vdd-supply = <&pm660l_l4>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 570000>;
+
+	vdd-io-supply = <&pm660_l8>;
+	qcom,vdd-io-always-on;
+	qcom,vdd-io-lpm-sup;
+	qcom,vdd-io-voltage-level = <1800000 1800000>;
+	qcom,vdd-io-current-level = <200 325000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc1_clk_on  &sdc1_cmd_on &sdc1_data_on &sdc1_rclk_on>;
+	pinctrl-1 = <&sdc1_clk_off &sdc1_cmd_off &sdc1_data_off &sdc1_rclk_off>;
+
+	status = "ok";
+};
+
+&sdhc_2 {
+	vdd-supply = <&pm660l_l5>;
+	qcom,vdd-voltage-level = <2960000 2960000>;
+	qcom,vdd-current-level = <200 800000>;
+
+	vdd-io-supply = <&pm660l_l2>;
+	qcom,vdd-io-voltage-level = <1800000 2960000>;
+	qcom,vdd-io-current-level = <200 22000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc2_clk_on &sdc2_cmd_on &sdc2_data_on &sdc2_cd_on>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+
+	cd-gpios = <&tlmm 96 0>;
+
+	status = "ok";
+};
+
+&tlmm {
+	pmx_ts_rst_active {
+		ts_rst_active: ts_rst_active {
+			mux {
+				pins = "gpio99";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio99";
+				drive-strength = <16>;
+				bias-pull-up;
+			};
+		};
+	};
+
+	pmx_ts_rst_suspend {
+		ts_rst_suspend: ts_rst_suspend {
+			mux {
+				pins = "gpio99";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio99";
+				drive-strength = <2>;
+				bias-pull-down;
+			};
+		};
+	};
+};
+
+&soc {
+	hbtp {
+		compatible = "qcom,hbtp-input";
+		pinctrl-names = "pmx_ts_active", "pmx_ts_suspend";
+		pinctrl-0 = <&ts_rst_active>;
+		pinctrl-1 = <&ts_rst_suspend>;
+		vcc_ana-supply = <&pm660l_l3>;
+		vcc_dig-supply = <&pm660_l13>;
+		qcom,afe-load = <20000>;
+		qcom,afe-vtg-min = <3000000>;
+		qcom,afe-vtg-max = <3000000>;
+		qcom,dig-load = <40000>;
+		qcom,dig-vtg-min = <1800000>;
+		qcom,dig-vtg-max = <1800000>;
+		qcom,fb-resume-delay-us = <1000>;
+		qcom,afe-force-power-on;
+		qcom,afe-power-on-delay-us = <6>;
+		qcom,afe-power-off-delay-us = <6>;
+	};
+};
+
+&dsi_dual_nt36850_truly_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,panel-mode-gpio = <&tlmm 76 0>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,platform-reset-gpio = <&tlmm 75 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
+&dsi_dual_nt36850_truly_cmd_display {
+	 qcom,dsi-display-active;
+};
+
+&pm660l_wled {
+	status = "okay";
+	qcom,led-strings-list = [01 02];
+};
+
+&mdss_mdp {
+	#cooling-cells = <2>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm670-qupv3.dtsi
index 657363f..c388f4a 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-qupv3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-qupv3.dtsi
@@ -78,6 +78,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 0 3 64 0>,
+			<&gpi_dma0 1 0 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se0_i2c_active>;
 		pinctrl-1 = <&qupv3_se0_i2c_sleep>;
@@ -95,6 +98,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 1 3 64 0>,
+			<&gpi_dma0 1 1 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se1_i2c_active>;
 		pinctrl-1 = <&qupv3_se1_i2c_sleep>;
@@ -112,6 +118,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 2 3 64 0>,
+			<&gpi_dma0 1 2 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se2_i2c_active>;
 		pinctrl-1 = <&qupv3_se2_i2c_sleep>;
@@ -129,6 +138,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 3 3 64 0>,
+			<&gpi_dma0 1 3 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se3_i2c_active>;
 		pinctrl-1 = <&qupv3_se3_i2c_sleep>;
@@ -146,6 +158,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S4_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 4 3 64 0>,
+			<&gpi_dma0 1 4 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se4_i2c_active>;
 		pinctrl-1 = <&qupv3_se4_i2c_sleep>;
@@ -163,6 +178,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 5 3 64 0>,
+			<&gpi_dma0 1 5 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se5_i2c_active>;
 		pinctrl-1 = <&qupv3_se5_i2c_sleep>;
@@ -180,6 +198,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 6 3 64 0>,
+			<&gpi_dma0 1 6 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se6_i2c_active>;
 		pinctrl-1 = <&qupv3_se6_i2c_sleep>;
@@ -197,6 +218,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 7 3 64 0>,
+			<&gpi_dma0 1 7 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se7_i2c_active>;
 		pinctrl-1 = <&qupv3_se7_i2c_sleep>;
@@ -435,6 +459,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 0 3 64 0>,
+			<&gpi_dma1 1 0 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se8_i2c_active>;
 		pinctrl-1 = <&qupv3_se8_i2c_sleep>;
@@ -452,6 +479,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 1 3 64 0>,
+			<&gpi_dma1 1 1 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se9_i2c_active>;
 		pinctrl-1 = <&qupv3_se9_i2c_sleep>;
@@ -469,6 +499,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 2 3 64 0>,
+			<&gpi_dma1 1 2 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se10_i2c_active>;
 		pinctrl-1 = <&qupv3_se10_i2c_sleep>;
@@ -486,6 +519,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 3 3 64 0>,
+			<&gpi_dma1 1 3 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se11_i2c_active>;
 		pinctrl-1 = <&qupv3_se11_i2c_sleep>;
@@ -503,6 +539,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S4_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 4 3 64 0>,
+			<&gpi_dma1 1 4 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se12_i2c_active>;
 		pinctrl-1 = <&qupv3_se12_i2c_sleep>;
@@ -520,6 +559,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S5_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 5 3 64 0>,
+			<&gpi_dma1 1 5 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se13_i2c_active>;
 		pinctrl-1 = <&qupv3_se13_i2c_sleep>;
@@ -537,6 +579,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S6_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 6 3 64 0>,
+			<&gpi_dma1 1 6 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se14_i2c_active>;
 		pinctrl-1 = <&qupv3_se14_i2c_sleep>;
@@ -554,6 +599,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S7_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 7 3 64 0>,
+			<&gpi_dma1 1 7 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se15_i2c_active>;
 		pinctrl-1 = <&qupv3_se15_i2c_sleep>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
index 0a8c49f..3c84314 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-regulator.dtsi
@@ -46,9 +46,9 @@
 		pm660_s4: regulator-pm660-s4 {
 			regulator-name = "pm660_s4";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
-			regulator-min-microvolt = <2040000>;
+			regulator-min-microvolt = <1808000>;
 			regulator-max-microvolt = <2040000>;
-			qcom,init-voltage = <2040000>;
+			qcom,init-voltage = <1808000>;
 		};
 	};
 
@@ -72,9 +72,9 @@
 		pm660_s6: regulator-pm660-s6 {
 			regulator-name = "pm660_s6";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
-			regulator-min-microvolt = <1352000>;
+			regulator-min-microvolt = <1224000>;
 			regulator-max-microvolt = <1352000>;
-			qcom,init-voltage = <1352000>;
+			qcom,init-voltage = <1224000>;
 		};
 	};
 
@@ -96,6 +96,14 @@
 			regulator-min-microvolt = <RPMH_REGULATOR_LEVEL_OFF>;
 			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
 		};
+
+		mx_cdev: mx-cdev-lvl {
+			compatible = "qcom,regulator-cooling-device";
+			regulator-cdev-supply = <&pm660l_s1_level>;
+			regulator-levels = <RPMH_REGULATOR_LEVEL_NOM
+					RPMH_REGULATOR_LEVEL_OFF>;
+			#cooling-cells = <2>;
+		};
 	};
 
 	/* pm660l S2 - VDD_GFX supply */
@@ -137,6 +145,13 @@
 			regulator-max-microvolt = <RPMH_REGULATOR_LEVEL_MAX>;
 			qcom,min-dropout-voltage-level = <(-1)>;
 		};
+
+		cx_cdev: regulator-cdev {
+			compatible = "qcom,rpmh-reg-cdev";
+			mboxes = <&qmp_aop 0>;
+			qcom,reg-resource-name = "cx";
+			#cooling-cells = <2>;
+		};
 	};
 
 	rpmh-regulator-ldoa1 {
@@ -147,11 +162,14 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 1>;
+		proxy-supply = <&pm660_l1>;
 		pm660_l1: regulator-pm660-l1 {
 			regulator-name = "pm660_l1";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <1200000>;
 			regulator-max-microvolt = <1250000>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <43600>;
 			qcom,init-voltage = <1200000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
@@ -222,9 +240,9 @@
 		pm660_l6: regulator-pm660-l6 {
 			regulator-name = "pm660_l6";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
-			regulator-min-microvolt = <1304000>;
+			regulator-min-microvolt = <1248000>;
 			regulator-max-microvolt = <1304000>;
-			qcom,init-voltage = <1304000>;
+			qcom,init-voltage = <1248000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
 	};
@@ -309,11 +327,14 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 1>;
+		proxy-supply = <&pm660_l11>;
 		pm660_l11: regulator-pm660-l11 {
 			regulator-name = "pm660_l11";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1800000>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <115000>;
 			qcom,init-voltage = <1800000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
@@ -453,11 +474,14 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 1>;
+		proxy-supply = <&pm660l_l1>;
 		pm660l_l1: regulator-pm660l-l1 {
 			regulator-name = "pm660l_l1";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <880000>;
 			regulator-max-microvolt = <900000>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <72000>;
 			qcom,init-voltage = <880000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
@@ -627,14 +651,14 @@
 			qcom,init-voltage = <3312000>;
 		};
 	};
-};
 
-&pm660_charger {
-	smb2_vbus: qcom,smb2-vbus {
-		regulator-name = "smb2-vbus";
-	};
-
-	smb2_vconn: qcom,smb2-vconn {
-		regulator-name = "smb2-vconn";
+	refgen: refgen-regulator@ff1000 {
+		compatible = "qcom,refgen-regulator";
+		reg = <0xff1000 0x60>;
+		regulator-name = "refgen";
+		regulator-enable-ramp-delay = <5>;
+		proxy-supply = <&refgen>;
+		qcom,proxy-consumer-enable;
+		regulator-always-on;
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
index a770b3c..4a24d87 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi-overlay.dts
@@ -13,6 +13,7 @@
 /dts-v1/;
 /plugin/;
 
+#include <dt-bindings/interrupt-controller/arm-gic.h>
 #include "sdm670-rumi.dtsi"
 
 / {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
index cc97e3e..a50d9b6 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-rumi.dtsi
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 
+#include "sdm670-pmic-overlay.dtsi"
+
 &soc {
 	/* Delete all regulators */
 	/delete-node/ rpmh-regulator-smpa4;
@@ -47,6 +49,7 @@
 	/delete-node/ rpmh-regulator-ldoa28;
 	/delete-node/ rpmh-regulator-bobb1;
 	/delete-node/ rpmh-regulator-gfxlvl;
+	/delete-node/ thermal-zones;
 };
 
 #include "sdm670-stub-regulator.dtsi"
@@ -125,6 +128,8 @@
 	qcom,clk-rates = <400000 20000000 25000000 50000000>;
 	qcom,bus-speed-mode = "HS200_1p8v", "DDR_1p8v";
 
+	/delete-property/qcom,devfreq,freq-table;
+
 	status = "ok";
 };
 
@@ -144,6 +149,8 @@
 	qcom,clk-rates = <400000 20000000 25000000 50000000>;
 	qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50";
 
+	/delete-property/qcom,devfreq,freq-table;
+
 	status = "ok";
 };
 
@@ -181,3 +188,31 @@
 &usb_qmp_dp_phy {
 	status = "disabled";
 };
+
+&mdss_mdp {
+	status = "disabled";
+};
+
+&sde_rscc {
+	status = "disabled";
+};
+
+&mdss_rotator {
+	status = "disabled";
+};
+
+&mdss_dsi0 {
+	status = "disabled";
+};
+
+&mdss_dsi1 {
+	status = "disabled";
+};
+
+&mdss_dsi_phy0 {
+	status = "disabled";
+};
+
+&mdss_dsi_phy1 {
+	status = "disabled";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
new file mode 100644
index 0000000..8dbd063
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -0,0 +1,820 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "dsi-panel-sim-video.dtsi"
+#include "dsi-panel-sim-cmd.dtsi"
+#include "dsi-panel-sim-dsc375-cmd.dtsi"
+#include "dsi-panel-sim-dualmipi-video.dtsi"
+#include "dsi-panel-sim-dualmipi-cmd.dtsi"
+#include "dsi-panel-sim-dualmipi-dsc375-cmd.dtsi"
+#include "dsi-panel-nt35597-truly-dualmipi-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-truly-dualmipi-wqxga-cmd.dtsi"
+#include "dsi-panel-nt35597-truly-dsc-wqxga-cmd.dtsi"
+#include "dsi-panel-nt35597-truly-dsc-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-video.dtsi"
+#include "dsi-panel-nt35695b-truly-fhd-cmd.dtsi"
+#include "dsi-panel-rm67195-amoled-fhd-cmd.dtsi"
+#include "dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi"
+#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
+
+&soc {
+	dsi_panel_pwr_supply: dsi_panel_pwr_supply {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,panel-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "vddio";
+			qcom,supply-min-voltage = <1800000>;
+			qcom,supply-max-voltage = <1800000>;
+			qcom,supply-enable-load = <62000>;
+			qcom,supply-disable-load = <80>;
+			qcom,supply-post-on-sleep = <20>;
+		};
+
+		qcom,panel-supply-entry@1 {
+			reg = <1>;
+			qcom,supply-name = "lab";
+			qcom,supply-min-voltage = <4600000>;
+			qcom,supply-max-voltage = <6000000>;
+			qcom,supply-enable-load = <100000>;
+			qcom,supply-disable-load = <100>;
+		};
+
+		qcom,panel-supply-entry@2 {
+			reg = <2>;
+			qcom,supply-name = "ibb";
+			qcom,supply-min-voltage = <4600000>;
+			qcom,supply-max-voltage = <6000000>;
+			qcom,supply-enable-load = <100000>;
+			qcom,supply-disable-load = <100>;
+			qcom,supply-post-on-sleep = <20>;
+		};
+	};
+
+	dsi_panel_pwr_supply_no_labibb: dsi_panel_pwr_supply_no_labibb {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,panel-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "vddio";
+			qcom,supply-min-voltage = <1800000>;
+			qcom,supply-max-voltage = <1800000>;
+			qcom,supply-enable-load = <62000>;
+			qcom,supply-disable-load = <80>;
+			qcom,supply-post-on-sleep = <20>;
+		};
+	};
+
+	dsi_panel_pwr_supply_vdd_no_labibb: dsi_panel_pwr_supply_vdd_no_labibb {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,panel-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "vddio";
+			qcom,supply-min-voltage = <1800000>;
+			qcom,supply-max-voltage = <1800000>;
+			qcom,supply-enable-load = <62000>;
+			qcom,supply-disable-load = <80>;
+			qcom,supply-post-on-sleep = <20>;
+		};
+
+		qcom,panel-supply-entry@1 {
+			reg = <1>;
+			qcom,supply-name = "vdd";
+			qcom,supply-min-voltage = <3000000>;
+			qcom,supply-max-voltage = <3000000>;
+			qcom,supply-enable-load = <857000>;
+			qcom,supply-disable-load = <0>;
+			qcom,supply-post-on-sleep = <0>;
+		};
+	};
+
+	dsi_panel_pwr_supply_labibb_amoled: dsi_panel_pwr_supply_labibb_amoled {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,panel-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "vddio";
+			qcom,supply-min-voltage = <1800000>;
+			qcom,supply-max-voltage = <1800000>;
+			qcom,supply-enable-load = <32000>;
+			qcom,supply-disable-load = <80>;
+		};
+
+		qcom,panel-supply-entry@1 {
+			reg = <1>;
+			qcom,supply-name = "vdda-3p3";
+			qcom,supply-min-voltage = <3300000>;
+			qcom,supply-max-voltage = <3300000>;
+			qcom,supply-enable-load = <13200>;
+			qcom,supply-disable-load = <80>;
+		};
+
+		qcom,panel-supply-entry@2 {
+			reg = <2>;
+			qcom,supply-name = "lab";
+			qcom,supply-min-voltage = <4600000>;
+			qcom,supply-max-voltage = <6100000>;
+			qcom,supply-enable-load = <100000>;
+			qcom,supply-disable-load = <100>;
+		};
+
+		qcom,panel-supply-entry@3 {
+			reg = <3>;
+			qcom,supply-name = "ibb";
+			qcom,supply-min-voltage = <4000000>;
+			qcom,supply-max-voltage = <6300000>;
+			qcom,supply-enable-load = <100000>;
+			qcom,supply-disable-load = <100>;
+		};
+
+		qcom,panel-supply-entry@4 {
+			reg = <4>;
+			qcom,supply-name = "oledb";
+			qcom,supply-min-voltage = <5000000>;
+			qcom,supply-max-voltage = <8100000>;
+			qcom,supply-enable-load = <100000>;
+			qcom,supply-disable-load = <100>;
+		};
+	};
+
+	dsi_dual_nt35597_truly_video_display: qcom,dsi-display@0 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_truly_video_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_truly_video>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_dual_nt35597_truly_cmd_display: qcom,dsi-display@1 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_truly_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_truly_cmd>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_nt35597_truly_dsc_cmd_display: qcom,dsi-display@2 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_nt35597_truly_dsc_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy1>;
+		clocks = <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>,
+			<&mdss_dsi1_pll PCLK_MUX_1_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_nt35597_truly_dsc_cmd>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_nt35597_truly_dsc_video_display: qcom,dsi-display@3 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_nt35597_truly_dsc_video_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy1>;
+		clocks = <&mdss_dsi1_pll BYTECLK_MUX_1_CLK>,
+			<&mdss_dsi1_pll PCLK_MUX_1_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_nt35597_truly_dsc_video>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_sim_vid_display: qcom,dsi-display@4 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_sim_vid_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_sim_vid>;
+	};
+
+	dsi_dual_sim_vid_display: qcom,dsi-display@5 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_sim_vid_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_dual_sim_vid>;
+	};
+
+	dsi_sim_cmd_display: qcom,dsi-display@6 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_sim_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_sim_cmd>;
+	};
+
+	dsi_dual_sim_cmd_display: qcom,dsi-display@7 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_sim_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_dual_sim_cmd>;
+	};
+
+	dsi_sim_dsc_375_cmd_display: qcom,dsi-display@8 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_sim_dsc_375_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_sim_dsc_375_cmd>;
+	};
+
+	dsi_dual_sim_dsc_375_cmd_display: qcom,dsi-display@9 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_sim_dsc_375_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+
+		qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>;
+	};
+
+	dsi_dual_nt35597_video_display: qcom,dsi-display@10 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_video_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_video>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_dual_nt35597_cmd_display: qcom,dsi-display@11 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+		qcom,panel-mode-gpio = <&tlmm 76 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_cmd>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_rm67195_amoled_fhd_cmd_display: qcom,dsi-display@12 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_rm67195_amoled_fhd_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+		qcom,dsi-panel = <&dsi_rm67195_amoled_fhd_cmd>;
+		vddio-supply = <&pm660_l11>;
+		vdda-3p3-supply = <&pm660l_l6>;
+		lab-supply = <&lab_regulator>;
+		ibb-supply = <&ibb_regulator>;
+		oledb-supply = <&pm660a_oledb>;
+	};
+
+	dsi_nt35695b_truly_fhd_video_display: qcom,dsi-display@13 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_nt35695b_truly_fhd_video_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+		qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_video>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_nt35695b_truly_fhd_cmd_display: qcom,dsi-display@14 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_nt35695b_truly_fhd_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0>;
+		qcom,dsi-phy = <&mdss_dsi_phy0>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+		qcom,dsi-panel = <&dsi_nt35695b_truly_fhd_cmd>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	dsi_dual_nt36850_truly_cmd_display: qcom,dsi-display@15 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt36850_truly_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 75 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt36850_truly_cmd>;
+		vddio-supply = <&pm660_l11>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	sde_wb: qcom,wb-display@0 {
+		compatible = "qcom,wb-display";
+		cell-index = <0>;
+		label = "wb_display";
+	};
+
+	ext_disp: qcom,msm-ext-disp {
+		compatible = "qcom,msm-ext-disp";
+
+		ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
+			compatible = "qcom,msm-ext-disp-audio-codec-rx";
+		};
+	};
+};
+
+&sde_dp {
+	qcom,dp-usbpd-detection = <&pm660_pdphy>;
+	qcom,ext-disp = <&ext_disp>;
+
+	pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
+	pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
+	pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
+	qcom,aux-en-gpio = <&tlmm 50 0>;
+	qcom,aux-sel-gpio = <&tlmm 40 0>;
+	qcom,usbplug-cc-gpio = <&tlmm 38 0>;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_dual_nt35597_truly_video {
+	qcom,mdss-dsi-t-clk-post = <0x0D>;
+	qcom,mdss-dsi-t-clk-pre = <0x2D>;
+	qcom,mdss-dsi-min-refresh-rate = <53>;
+	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";
+	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_hs_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9c>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+	qcom,mdss-dsi-panel-status-read-length = <1>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt35597_truly_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0D>;
+	qcom,mdss-dsi-t-clk-pre = <0x2D>;
+	qcom,ulps-enabled;
+	qcom,partial-update-enabled = "single_roi";
+	qcom,panel-roi-alignment = <720 128 720 128 1440 128>;
+	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_hs_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9c>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+	qcom,mdss-dsi-panel-status-read-length = <1>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_nt35597_truly_dsc_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0b>;
+	qcom,mdss-dsi-t-clk-pre = <0x23>;
+	qcom,ulps-enabled;
+	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_hs_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9c>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+	qcom,mdss-dsi-panel-status-read-length = <1>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
+				05 03 03 04 00];
+			qcom,display-topology = <1 1 1>,
+						<2 2 1>, /* dsc merge */
+						<2 1 1>; /* 3d mux */
+			qcom,default-topology-index = <1>;
+		};
+	};
+};
+
+&dsi_nt35597_truly_dsc_video {
+	qcom,mdss-dsi-t-clk-post = <0x0b>;
+	qcom,mdss-dsi-t-clk-pre = <0x23>;
+	qcom,mdss-dsi-min-refresh-rate = <53>;
+	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";
+	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_hs_mode";
+	qcom,mdss-dsi-panel-status-value = <0x9c>;
+	qcom,mdss-dsi-panel-on-check-value = <0x9c>;
+	qcom,mdss-dsi-panel-status-read-length = <1>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
+				04 03 03 04 00];
+			qcom,display-topology = <1 1 1>,
+						<2 2 1>, /* dsc merge */
+						<2 1 1>; /* 3d mux */
+			qcom,default-topology-index = <1>;
+		};
+	};
+};
+
+&dsi_sim_vid {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <1 0 1>,
+						<2 0 1>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_sim_vid {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_sim_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <1 0 1>,
+						<2 0 1>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_sim_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 24 09 09 26 24 09
+				09 06 03 04 00];
+			qcom,display-topology = <2 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+		timing@1{
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+		timing@2{
+			qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06
+				06 04 03 04 00];
+			qcom,display-topology = <2 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_sim_dsc_375_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0 { /* 1080p */
+			qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07
+				07 04 03 04 00];
+			qcom,display-topology = <1 1 1>;
+			qcom,default-topology-index = <0>;
+		};
+		timing@1 { /* qhd */
+			qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
+				05 03 03 04 00];
+			qcom,display-topology = <1 1 1>,
+						<2 2 1>, /* dsc merge */
+						<2 1 1>; /* 3d mux */
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_sim_dsc_375_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		timing@0 { /* qhd */
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
+				07 05 03 04 00];
+			qcom,display-topology = <2 2 2>;
+			qcom,default-topology-index = <0>;
+		};
+		timing@1 { /* 4k */
+			qcom,mdss-dsi-panel-phy-timings = [00 18 06 06 21 20 06
+				06 04 03 04 00];
+			qcom,display-topology = <2 2 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt35597_video {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		 timing@0 {
+			qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+				05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,ulps-enabled;
+	qcom,partial-update-enabled = "single_roi";
+	qcom,panel-roi-alignment = <720 128 720 128 1440 128>;
+	qcom,mdss-dsi-display-timings {
+		 timing@0 {
+			qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+				05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_rm67195_amoled_fhd_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x07>;
+	qcom,mdss-dsi-t-clk-pre = <0x1c>;
+	qcom,mdss-dsi-display-timings {
+		timing@0 {
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+				05 07 05 03 04 00];
+			qcom,display-topology = <1 0 1>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_nt35695b_truly_fhd_video {
+	qcom,mdss-dsi-t-clk-post = <0x07>;
+	qcom,mdss-dsi-t-clk-pre = <0x1c>;
+	qcom,mdss-dsi-display-timings {
+		timing@0 {
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+				05 07 05 03 04 00];
+			qcom,display-topology = <1 0 1>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_nt35695b_truly_fhd_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x07>;
+	qcom,mdss-dsi-t-clk-pre = <0x1c>;
+	qcom,ulps-enabled;
+	qcom,mdss-dsi-display-timings {
+		timing@0 {
+			qcom,mdss-dsi-panel-phy-timings = [00 1c 05 06 0b 0c
+				05 07 05 03 04 00];
+			qcom,display-topology = <1 0 1>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt36850_truly_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0E>;
+	qcom,mdss-dsi-t-clk-pre = <0x30>;
+	qcom,mdss-dsi-display-timings {
+		timing@0{
+			qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
+				08 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+				<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
new file mode 100644
index 0000000..72e3f5f
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
@@ -0,0 +1,109 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	mdss_dsi0_pll: qcom,mdss_dsi_pll@ae94a00 {
+		compatible = "qcom,mdss_dsi_pll_10nm";
+		label = "MDSS DSI 0 PLL";
+		cell-index = <0>;
+		#clock-cells = <1>;
+		reg = <0xae94a00 0x1e0>,
+		      <0xae94400 0x800>,
+		      <0xaf03000 0x8>;
+		reg-names = "pll_base", "phy_base", "gdsc_base";
+		clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
+		clock-names = "iface_clk";
+		clock-rate = <0>;
+		gdsc-supply = <&mdss_core_gdsc>;
+		qcom,platform-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			qcom,platform-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "gdsc";
+				qcom,supply-min-voltage = <0>;
+				qcom,supply-max-voltage = <0>;
+				qcom,supply-enable-load = <0>;
+				qcom,supply-disable-load = <0>;
+			};
+		};
+	};
+
+	mdss_dsi1_pll: qcom,mdss_dsi_pll@ae96a00 {
+		compatible = "qcom,mdss_dsi_pll_10nm";
+		label = "MDSS DSI 1 PLL";
+		cell-index = <1>;
+		#clock-cells = <1>;
+		reg = <0xae96a00 0x1e0>,
+		      <0xae96400 0x800>,
+		      <0xaf03000 0x8>;
+		reg-names = "pll_base", "phy_base", "gdsc_base";
+		clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
+		clock-names = "iface_clk";
+		clock-rate = <0>;
+		gdsc-supply = <&mdss_core_gdsc>;
+		qcom,platform-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			qcom,platform-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "gdsc";
+				qcom,supply-min-voltage = <0>;
+				qcom,supply-max-voltage = <0>;
+				qcom,supply-enable-load = <0>;
+				qcom,supply-disable-load = <0>;
+			};
+		};
+	};
+
+	mdss_dp_pll: qcom,mdss_dp_pll@88ea000 {
+		compatible = "qcom,mdss_dp_pll_10nm";
+		label = "MDSS DP PLL";
+		cell-index = <0>;
+		#clock-cells = <1>;
+
+		reg = <0x088ea000 0x200>,
+		      <0x088eaa00 0x200>,
+		      <0x088ea200 0x200>,
+		      <0x088ea600 0x200>,
+		      <0xaf03000 0x8>;
+		reg-names = "pll_base", "phy_base", "ln_tx0_base",
+			"ln_tx1_base", "gdsc_base";
+
+		gdsc-supply = <&mdss_core_gdsc>;
+
+		clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
+			 <&clock_rpmh RPMH_CXO_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			 <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+		clock-names = "iface_clk", "ref_clk_src", "ref_clk",
+			"cfg_ahb_clk", "pipe_clk";
+		clock-rate = <0>;
+
+		qcom,platform-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,platform-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "gdsc";
+				qcom,supply-min-voltage = <0>;
+				qcom,supply-max-voltage = <0>;
+				qcom,supply-enable-load = <0>;
+				qcom,supply-disable-load = <0>;
+			};
+
+		};
+	};
+
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
new file mode 100644
index 0000000..2b80c22
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
@@ -0,0 +1,652 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
+
+&soc {
+	mdss_mdp: qcom,mdss_mdp@ae00000 {
+		compatible = "qcom,sde-kms";
+		reg = <0x0ae00000 0x81d40>,
+		      <0x0aeb0000 0x2008>,
+		      <0x0aeac000 0xf0>;
+		reg-names = "mdp_phys",
+			"vbif_phys",
+			"regdma_phys";
+
+		clocks =
+			<&clock_gcc GCC_DISP_AHB_CLK>,
+			<&clock_gcc GCC_DISP_AXI_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_AXI_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_MDP_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_VSYNC_CLK>;
+		clock-names = "gcc_iface", "gcc_bus", "iface_clk",
+				"bus_clk", "core_clk", "vsync_clk";
+		clock-rate = <0 0 0 0 300000000 19200000 0>;
+		clock-max-rate = <0 0 0 0 412500000 19200000 0>;
+
+		sde-vdd-supply = <&mdss_core_gdsc>;
+
+		/* interrupt config */
+		interrupt-parent = <&pdc>;
+		interrupts = <0 83 0>;
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		iommus = <&apps_smmu 0x880 0x8>,
+			<&apps_smmu 0xc80 0x8>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		#power-domain-cells = <0>;
+
+		/* hw blocks */
+		qcom,sde-off = <0x1000>;
+		qcom,sde-len = <0x45C>;
+
+		qcom,sde-ctl-off = <0x2000 0x2200 0x2400
+				     0x2600 0x2800>;
+		qcom,sde-ctl-size = <0xE4>;
+		qcom,sde-ctl-display-pref = "primary", "primary", "none",
+						"none", "none";
+
+		qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0x0 0x0 0x4a000>;
+		qcom,sde-mixer-size = <0x320>;
+		qcom,sde-mixer-display-pref = "primary", "primary", "none",
+						"none", "none", "none";
+
+		qcom,sde-dspp-top-off = <0x1300>;
+		qcom,sde-dspp-top-size = <0xc>;
+
+		qcom,sde-dspp-off = <0x55000 0x57000>;
+		qcom,sde-dspp-size = <0x17e0>;
+
+		qcom,sde-dest-scaler-top-off = <0x00061000>;
+		qcom,sde-dest-scaler-top-size = <0xc>;
+		qcom,sde-dest-scaler-off = <0x800 0x1000>;
+		qcom,sde-dest-scaler-size = <0x800>;
+
+		qcom,sde-wb-off = <0x66000>;
+		qcom,sde-wb-size = <0x2c8>;
+		qcom,sde-wb-xin-id = <6>;
+		qcom,sde-wb-id = <2>;
+		qcom,sde-wb-clk-ctrl = <0x3b8 24>;
+
+		qcom,sde-intf-off = <0x6b000 0x6b800
+					0x6c000 0x6c800>;
+		qcom,sde-intf-size = <0x280>;
+
+		qcom,sde-intf-type = "dp", "dsi", "dsi", "dp";
+		qcom,sde-pp-off = <0x71000 0x71800
+					  0x72000 0x72800 0x73000>;
+		qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>;
+		qcom,sde-pp-size = <0xd4>;
+
+		qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>;
+		qcom,sde-cdm-off = <0x7a200>;
+		qcom,sde-cdm-size = <0x224>;
+
+		qcom,sde-dsc-off = <0x81000 0x81400>;
+		qcom,sde-dsc-size = <0x140>;
+
+		qcom,sde-dither-off = <0x30e0 0x30e0 0x30e0 0x30e0 0x0>;
+		qcom,sde-dither-version = <0x00010000>;
+		qcom,sde-dither-size = <0x20>;
+
+		qcom,sde-sspp-type = "vig", "vig",
+					"dma", "dma", "dma";
+
+		qcom,sde-sspp-off = <0x5000 0x7000 0x25000 0x27000 0x29000>;
+		qcom,sde-sspp-src-size = <0x1c8>;
+
+		qcom,sde-sspp-xin-id = <0 4 1 5 9>;
+		qcom,sde-sspp-excl-rect = <1 1 1 1 1>;
+		qcom,sde-sspp-smart-dma-priority = <4 5 1 2 3>;
+		qcom,sde-smart-dma-rev = "smart_dma_v2";
+
+		qcom,sde-mixer-pair-mask = <2 1 6 0 0 3>;
+
+		qcom,sde-mixer-blend-op-off = <0x20 0x38 0x50 0x68 0x80 0x98
+						0xb0 0xc8 0xe0 0xf8 0x110>;
+
+		/* offsets are relative to "mdp_phys + qcom,sde-off */
+		qcom,sde-sspp-clk-ctrl =
+				<0x2ac 0>, <0x2b4 0>,
+				 <0x2ac 8>, <0x2b4 8>, <0x2bc 8>;
+		qcom,sde-sspp-csc-off = <0x1a00>;
+		qcom,sde-csc-type = "csc-10bit";
+		qcom,sde-qseed-type = "qseedv3";
+		qcom,sde-sspp-qseed-off = <0xa00>;
+		qcom,sde-mixer-linewidth = <2560>;
+		qcom,sde-sspp-linewidth = <2560>;
+		qcom,sde-wb-linewidth = <4096>;
+		qcom,sde-mixer-blendstages = <0xb>;
+		qcom,sde-highest-bank-bit = <0x1>;
+		qcom,sde-ubwc-version = <0x200>;
+		qcom,sde-smart-panel-align-mode = <0xc>;
+		qcom,sde-panic-per-pipe;
+		qcom,sde-has-cdp;
+		qcom,sde-has-src-split;
+		qcom,sde-has-dim-layer;
+		qcom,sde-has-idle-pc;
+		qcom,sde-has-dest-scaler;
+		qcom,sde-max-dest-scaler-input-linewidth = <2048>;
+		qcom,sde-max-dest-scaler-output-linewidth = <2560>;
+		qcom,sde-max-bw-low-kbps = <9600000>;
+		qcom,sde-max-bw-high-kbps = <9600000>;
+		qcom,sde-dram-channels = <2>;
+		qcom,sde-num-nrt-paths = <0>;
+		qcom,sde-dspp-ad-version = <0x00040000>;
+		qcom,sde-dspp-ad-off = <0x28000 0x27000>;
+
+		qcom,sde-vbif-off = <0>;
+		qcom,sde-vbif-size = <0x1040>;
+		qcom,sde-vbif-id = <0>;
+		qcom,sde-vbif-memtype-0 = <3 3 3 3 3 3 3 3>;
+		qcom,sde-vbif-memtype-1 = <3 3 3 3 3 3>;
+
+		qcom,sde-vbif-qos-rt-remap = <3 3 4 4 5 5 6 6>;
+		qcom,sde-vbif-qos-nrt-remap = <3 3 3 3 3 3 3 3>;
+
+		qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000
+			0x00000000>;
+		qcom,sde-safe-lut-linear =
+			<4 0xfff8>,
+			<0 0xfff0>;
+		qcom,sde-safe-lut-macrotile =
+			<10 0xfe00>,
+			<11 0xfc00>,
+			<12 0xf800>,
+			<0 0xf000>;
+		qcom,sde-safe-lut-nrt =
+			<0 0xffff>;
+		qcom,sde-safe-lut-cwb =
+			<0 0xffff>;
+		qcom,sde-qos-lut-linear =
+			<4 0x00000000 0x00000357>,
+			<5 0x00000000 0x00003357>,
+			<6 0x00000000 0x00023357>,
+			<7 0x00000000 0x00223357>,
+			<8 0x00000000 0x02223357>,
+			<9 0x00000000 0x22223357>,
+			<10 0x00000002 0x22223357>,
+			<11 0x00000022 0x22223357>,
+			<12 0x00000222 0x22223357>,
+			<13 0x00002222 0x22223357>,
+			<14 0x00012222 0x22223357>,
+			<0 0x00112222 0x22223357>;
+		qcom,sde-qos-lut-macrotile =
+			<10 0x00000003 0x44556677>,
+			<11 0x00000033 0x44556677>,
+			<12 0x00000233 0x44556677>,
+			<13 0x00002233 0x44556677>,
+			<14 0x00012233 0x44556677>,
+			<0 0x00112233 0x44556677>;
+		qcom,sde-qos-lut-nrt =
+			<0 0x00000000 0x00000000>;
+		qcom,sde-qos-lut-cwb =
+			<0 0x75300000 0x00000000>;
+
+		qcom,sde-cdp-setting = <1 1>, <1 0>;
+
+		qcom,sde-inline-rotator = <&mdss_rotator 0>;
+		qcom,sde-inline-rot-xin = <10 11>;
+		qcom,sde-inline-rot-xin-type = "sspp", "wb";
+
+		/* offsets are relative to "mdp_phys + qcom,sde-off */
+		qcom,sde-inline-rot-clk-ctrl = <0x2bc 0x8>, <0x2bc 0xc>;
+
+		qcom,sde-reg-dma-off = <0>;
+		qcom,sde-reg-dma-version = <0x1>;
+		qcom,sde-reg-dma-trigger-off = <0x119c>;
+
+		qcom,sde-sspp-vig-blocks {
+			qcom,sde-vig-csc-off = <0x1a00>;
+			qcom,sde-vig-qseed-off = <0xa00>;
+			qcom,sde-vig-qseed-size = <0xa0>;
+		};
+
+		qcom,sde-dspp-blocks {
+			qcom,sde-dspp-igc = <0x0 0x00030001>;
+			qcom,sde-dspp-hsic = <0x800 0x00010007>;
+			qcom,sde-dspp-memcolor = <0x880 0x00010007>;
+			qcom,sde-dspp-sixzone= <0x900 0x00010007>;
+			qcom,sde-dspp-vlut = <0xa00 0x00010008>;
+			qcom,sde-dspp-gamut = <0x1000 0x00040000>;
+			qcom,sde-dspp-pcc = <0x1700 0x00040000>;
+			qcom,sde-dspp-gc = <0x17c0 0x00010008>;
+			qcom,sde-dspp-hist = <0x800 0x00010007>;
+		};
+
+		qcom,platform-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,platform-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "sde-vdd";
+				qcom,supply-min-voltage = <0>;
+				qcom,supply-max-voltage = <0>;
+				qcom,supply-enable-load = <0>;
+				qcom,supply-disable-load = <0>;
+			};
+		};
+
+		smmu_sde_sec: qcom,smmu_sde_sec_cb {
+			compatible = "qcom,smmu_sde_sec";
+			iommus = <&apps_smmu 0x881 0x8>,
+			       <&apps_smmu 0xc81 0x8>;
+		};
+
+		/* data and reg bus scale settings */
+		qcom,sde-data-bus {
+			qcom,msm-bus,name = "mdss_sde_mnoc";
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <2>;
+			qcom,msm-bus,vectors-KBps =
+			    <22 773 0 0>, <23 773 0 0>,
+			    <22 773 0 6400000>, <23 773 0 6400000>,
+			    <22 773 0 6400000>, <23 773 0 6400000>;
+		};
+
+		qcom,sde-llcc-bus {
+			qcom,msm-bus,name = "mdss_sde_llcc";
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,vectors-KBps =
+			    <132 770 0 0>,
+			    <132 770 0 6400000>,
+			    <132 770 0 6400000>;
+		};
+
+		qcom,sde-ebi-bus {
+			qcom,msm-bus,name = "mdss_sde_ebi";
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,vectors-KBps =
+			    <129 512 0 0>,
+			    <129 512 0 6400000>,
+			    <129 512 0 6400000>;
+		};
+
+		qcom,sde-reg-bus {
+			qcom,msm-bus,name = "mdss_reg";
+			qcom,msm-bus,num-cases = <4>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,active-only;
+			qcom,msm-bus,vectors-KBps =
+				<1 590 0 0>,
+				<1 590 0 76800>,
+				<1 590 0 150000>,
+				<1 590 0 300000>;
+		};
+	};
+
+	sde_rscc: qcom,sde_rscc@af20000 {
+		cell-index = <0>;
+		compatible = "qcom,sde-rsc";
+		reg = <0xaf20000 0x1c44>,
+			<0xaf30000 0x3fd4>;
+		reg-names = "drv", "wrapper";
+		qcom,sde-rsc-version = <1>;
+
+		vdd-supply = <&mdss_core_gdsc>;
+		clocks = <&clock_dispcc DISP_CC_MDSS_RSCC_VSYNC_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_RSCC_AHB_CLK>;
+		clock-names = "vsync_clk", "iface_clk";
+		clock-rate = <0 0>;
+
+		qcom,sde-dram-channels = <2>;
+
+		mboxes = <&disp_rsc 0>;
+		mbox-names = "disp_rsc";
+
+		/* data and reg bus scale settings */
+		qcom,sde-data-bus {
+			qcom,msm-bus,name = "disp_rsc_mnoc";
+			qcom,msm-bus,active-only;
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <2>;
+			qcom,msm-bus,vectors-KBps =
+			    <20003 20515 0 0>, <20004 20515 0 0>,
+			    <20003 20515 0 6400000>, <20004 20515 0 6400000>,
+			    <20003 20515 0 6400000>, <20004 20515 0 6400000>;
+		};
+
+		qcom,sde-llcc-bus {
+			qcom,msm-bus,name = "disp_rsc_llcc";
+			qcom,msm-bus,active-only;
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,vectors-KBps =
+			    <20001 20513 0 0>,
+			    <20001 20513 0 6400000>,
+			    <20001 20513 0 6400000>;
+		};
+
+		qcom,sde-ebi-bus {
+			qcom,msm-bus,name = "disp_rsc_ebi";
+			qcom,msm-bus,active-only;
+			qcom,msm-bus,num-cases = <3>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,vectors-KBps =
+			    <20000 20512 0 0>,
+			    <20000 20512 0 6400000>,
+			    <20000 20512 0 6400000>;
+		};
+	};
+
+	mdss_rotator: qcom,mdss_rotator@ae00000 {
+		compatible = "qcom,sde_rotator";
+		reg = <0x0ae00000 0xac000>,
+		      <0x0aeb8000 0x3000>;
+		reg-names = "mdp_phys",
+			"rot_vbif_phys";
+
+		#list-cells = <1>;
+
+		qcom,mdss-rot-mode = <1>;
+		qcom,mdss-highest-bank-bit = <0x1>;
+
+		/* Bus Scale Settings */
+		qcom,msm-bus,name = "mdss_rotator";
+		qcom,msm-bus,num-cases = <3>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<25 512 0 0>,
+			<25 512 0 6400000>,
+			<25 512 0 6400000>;
+
+		rot-vdd-supply = <&mdss_core_gdsc>;
+		qcom,supply-names = "rot-vdd";
+
+		clocks =
+			<&clock_gcc GCC_DISP_AHB_CLK>,
+			<&clock_gcc GCC_DISP_AXI_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_AHB_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_ROT_CLK>,
+			<&clock_dispcc DISP_CC_MDSS_AXI_CLK>;
+		clock-names = "gcc_iface", "gcc_bus",
+			"iface_clk", "rot_clk", "axi_clk";
+
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <2 0>;
+
+		 power-domains = <&mdss_mdp>;
+
+		/* Offline rotator QoS setting */
+		qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>;
+		qcom,mdss-rot-vbif-memtype = <3 3>;
+		qcom,mdss-rot-cdp-setting = <1 1>;
+		qcom,mdss-rot-qos-lut = <0x0 0x0 0x0 0x0>;
+		qcom,mdss-rot-danger-lut = <0x0 0x0>;
+		qcom,mdss-rot-safe-lut = <0x0000ffff 0x0000ffff>;
+
+		/* Inline rotator QoS Setting */
+		/* setting default register values for RD - qos/danger/safe */
+		qcom,mdss-inline-rot-qos-lut = <0x44556677 0x00112233
+							0x44556677 0x00112233>;
+		qcom,mdss-inline-rot-danger-lut = <0x0055aaff 0x0000ffff>;
+		qcom,mdss-inline-rot-safe-lut = <0x0000f000 0x0000ff00>;
+
+		qcom,mdss-default-ot-rd-limit = <32>;
+		qcom,mdss-default-ot-wr-limit = <32>;
+
+		qcom,mdss-sbuf-headroom = <20>;
+
+		cache-slice-names = "rotator";
+		cache-slices = <&llcc 4>;
+
+		/* reg bus scale settings */
+		rot_reg: qcom,rot-reg-bus {
+			qcom,msm-bus,name = "mdss_rot_reg";
+			qcom,msm-bus,num-cases = <2>;
+			qcom,msm-bus,num-paths = <1>;
+			qcom,msm-bus,active-only;
+			qcom,msm-bus,vectors-KBps =
+				<1 590 0 0>,
+				<1 590 0 76800>;
+		};
+
+		smmu_rot_unsec: qcom,smmu_rot_unsec_cb {
+			compatible = "qcom,smmu_sde_rot_unsec";
+			iommus = <&apps_smmu 0x1090 0x0>;
+		};
+
+		smmu_rot_sec: qcom,smmu_rot_sec_cb {
+			compatible = "qcom,smmu_sde_rot_sec";
+			iommus = <&apps_smmu 0x1091 0x0>;
+		};
+	};
+
+	mdss_dsi0: qcom,mdss_dsi_ctrl0@ae94000 {
+		compatible = "qcom,dsi-ctrl-hw-v2.2";
+		label = "dsi-ctrl-0";
+		cell-index = <0>;
+		reg =   <0xae94000 0x400>,
+			<0xaf08000 0x4>;
+		reg-names = "dsi_ctrl", "disp_cc_base";
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <4 0>;
+		vdda-1p2-supply = <&pm660_l1>;
+		clocks = <&clock_dispcc DISP_CC_MDSS_BYTE0_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_BYTE0_CLK_SRC>,
+		<&clock_dispcc DISP_CC_MDSS_BYTE0_INTF_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_PCLK0_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_PCLK0_CLK_SRC>,
+		<&clock_dispcc DISP_CC_MDSS_ESC0_CLK>;
+		clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk",
+					"pixel_clk", "pixel_clk_rcg",
+					"esc_clk";
+
+		qcom,null-insertion-enabled;
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+	};
+
+	mdss_dsi1: qcom,mdss_dsi_ctrl1@ae96000 {
+		compatible = "qcom,dsi-ctrl-hw-v2.2";
+		label = "dsi-ctrl-1";
+		cell-index = <1>;
+		reg =   <0xae96000 0x400>,
+			<0xaf08000 0x4>;
+		reg-names = "dsi_ctrl", "disp_cc_base";
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <5 0>;
+		vdda-1p2-supply = <&pm660_l1>;
+		clocks = <&clock_dispcc DISP_CC_MDSS_BYTE1_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_BYTE1_CLK_SRC>,
+		<&clock_dispcc DISP_CC_MDSS_BYTE1_INTF_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_PCLK1_CLK>,
+		<&clock_dispcc DISP_CC_MDSS_PCLK1_CLK_SRC>,
+		<&clock_dispcc DISP_CC_MDSS_ESC1_CLK>;
+		clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk",
+				"pixel_clk", "pixel_clk_rcg", "esc_clk";
+		qcom,null-insertion-enabled;
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+	};
+
+	mdss_dsi_phy0: qcom,mdss_dsi_phy0@ae94400 {
+		compatible = "qcom,dsi-phy-v3.0";
+		label = "dsi-phy-0";
+		cell-index = <0>;
+		reg = <0xae94400 0x7c0>;
+		reg-names = "dsi_phy";
+		gdsc-supply = <&mdss_core_gdsc>;
+		vdda-0p9-supply = <&pm660l_l1>;
+		qcom,platform-strength-ctrl = [55 03
+						55 03
+						55 03
+						55 03
+						55 00];
+		qcom,platform-lane-config = [00 00 00 00
+						00 00 00 00
+						00 00 00 00
+						00 00 00 00
+						00 00 00 80];
+		qcom,platform-regulator-settings = [1d 1d 1d 1d 1d];
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+	};
+
+	mdss_dsi_phy1: qcom,mdss_dsi_phy0@ae96400 {
+		compatible = "qcom,dsi-phy-v3.0";
+		label = "dsi-phy-1";
+		cell-index = <1>;
+		reg = <0xae96400 0x7c0>;
+		reg-names = "dsi_phy";
+		gdsc-supply = <&mdss_core_gdsc>;
+		vdda-0p9-supply = <&pm660l_l1>;
+		qcom,platform-strength-ctrl = [55 03
+						55 03
+						55 03
+						55 03
+						55 00];
+		qcom,platform-regulator-settings = [1d 1d 1d 1d 1d];
+		qcom,platform-lane-config = [00 00 00 00
+						00 00 00 00
+						00 00 00 00
+						00 00 00 00
+						00 00 00 80];
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+	};
+
+	sde_dp: qcom,dp_display@0{
+		cell-index = <0>;
+		compatible = "qcom,dp-display";
+
+		vdda-1p2-supply = <&pm660_l1>;
+		vdda-0p9-supply = <&pm660l_l1>;
+
+		reg =	<0xae90000 0x0dc>,
+			<0xae90200 0x0c0>,
+			<0xae90400 0x508>,
+			<0xae90a00 0x094>,
+			<0x88eaa00 0x200>,
+			<0x88ea200 0x200>,
+			<0x88ea600 0x200>,
+			<0xaf02000 0x1a0>,
+			<0x780000 0x621c>,
+			<0x88ea030 0x10>,
+			<0x88e8000 0x20>,
+			<0x0aee1000 0x034>;
+		/* dp_ctrl: dp_ahb, dp_aux, dp_link, dp_p0 */
+		reg-names = "dp_ahb", "dp_aux", "dp_link",
+			"dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+			"dp_mmss_cc", "qfprom_physical", "dp_pll",
+			"usb3_dp_com", "hdcp_physical";
+
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <12 0>;
+
+		clocks =  <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+			 <&clock_rpmh RPMH_CXO_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			 <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
+			 <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+		clock-names = "core_aux_clk", "core_usb_ref_clk_src",
+			"core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk",
+			"ctrl_link_iface_clk", "ctrl_pixel_clk",
+			"crypto_clk", "pixel_clk_rcg", "pixel_parent";
+
+		qcom,aux-cfg0-settings = [20 00];
+		qcom,aux-cfg1-settings = [24 13 23 1d];
+		qcom,aux-cfg2-settings = [28 24];
+		qcom,aux-cfg3-settings = [2c 00];
+		qcom,aux-cfg4-settings = [30 0a];
+		qcom,aux-cfg5-settings = [34 26];
+		qcom,aux-cfg6-settings = [38 0a];
+		qcom,aux-cfg7-settings = [3c 03];
+		qcom,aux-cfg8-settings = [40 bb];
+		qcom,aux-cfg9-settings = [44 03];
+
+		qcom,max-pclk-frequency-khz = <675000>;
+
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
new file mode 100644
index 0000000..1ce8dba
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
@@ -0,0 +1,1507 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/thermal/thermal.h>
+
+&clock_cpucc {
+	lmh_dcvs0: qcom,limits-dcvs@0 {
+		compatible = "qcom,msm-hw-limits";
+		interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+		qcom,affinity = <0>;
+		#thermal-sensor-cells = <0>;
+	};
+
+	lmh_dcvs1: qcom,limits-dcvs@1 {
+		compatible = "qcom,msm-hw-limits";
+		interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
+		qcom,affinity = <1>;
+		#thermal-sensor-cells = <0>;
+	};
+};
+
+&soc {
+	qmi-tmd-devices {
+		compatible = "qcom,qmi_cooling_devices";
+
+		modem {
+			qcom,instance-id = <0x0>;
+
+			modem_pa: modem_pa {
+				qcom,qmi-dev-name = "pa";
+				#cooling-cells = <2>;
+			};
+
+			modem_proc: modem_proc {
+				qcom,qmi-dev-name = "modem";
+				#cooling-cells = <2>;
+			};
+
+			modem_current: modem_current {
+				qcom,qmi-dev-name = "modem_current";
+				#cooling-cells = <2>;
+			};
+
+			modem_vdd: modem_vdd {
+				qcom,qmi-dev-name = "cpuv_restriction_cold";
+				#cooling-cells = <2>;
+			};
+		};
+
+		adsp {
+			qcom,instance-id = <0x1>;
+
+			adsp_vdd: adsp_vdd {
+				qcom,qmi-dev-name = "cpuv_restriction_cold";
+				#cooling-cells = <2>;
+			};
+		};
+
+		cdsp {
+			qcom,instance-id = <0x43>;
+
+			cdsp_vdd: cdsp_vdd {
+				qcom,qmi-dev-name = "cpuv_restriction_cold";
+				#cooling-cells = <2>;
+			};
+		};
+	};
+};
+
+&thermal_zones {
+	aoss0-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 0>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu0-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 1>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu1-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 2>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu2-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 3>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu3-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 4>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu4-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 5>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu5-silver-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 6>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	kryo-l3-0-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 7>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	kryo-l3-1-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 8>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu0-gold-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 9>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	cpu1-gold-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 10>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	gpu0-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens0 11>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	gpu1-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&tsens0 12>;
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	aoss1-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 0>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mdm-dsp-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 1>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	ddr-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 2>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	wlan-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 3>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	compute-hvx-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 4>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	camera-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 5>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mmss-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 6>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	mdm-core-usr {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 7>;
+		thermal-governor = "user_space";
+		trips {
+			active-config0 {
+				temperature = <125000>;
+				hysteresis = <1000>;
+				type = "passive";
+			};
+		};
+	};
+
+	gpu-virt-max-step {
+		polling-delay-passive = <10>;
+		polling-delay = <100>;
+		thermal-governor = "step_wise";
+		trips {
+			gpu_trip0: gpu-trip0 {
+				temperature = <95000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			gpu_cdev0 {
+				trip = <&gpu_trip0>;
+				cooling-device =
+					<&msm_gpu THERMAL_NO_LIMIT
+						THERMAL_NO_LIMIT>;
+			};
+		};
+	};
+
+	hexa-silv-max-step {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "step_wise";
+		trips {
+			silver-trip {
+				temperature = <120000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+	};
+
+	dual-gold-max-step {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "step_wise";
+		trips {
+			gold-trip {
+				temperature = <120000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+	};
+
+	pop-mem-step {
+		polling-delay-passive = <10>;
+		polling-delay = <0>;
+		thermal-sensors = <&tsens1 2>;
+		thermal-governor = "step_wise";
+		trips {
+			pop_trip: pop-trip {
+				temperature = <95000>;
+				hysteresis = <0>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			pop_cdev6 {
+				trip = <&pop_trip>;
+				cooling-device =
+					<&CPU6 THERMAL_NO_LIMIT
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+			pop_cdev7 {
+				trip = <&pop_trip>;
+				cooling-device =
+					<&CPU7 THERMAL_NO_LIMIT
+						(THERMAL_MAX_LIMIT-1)>;
+			};
+		};
+	};
+
+	aoss0-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 0>;
+		tracks-low;
+		trips {
+			aoss0_trip: aoss0-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&aoss0_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu0-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 1>;
+		tracks-low;
+		trips {
+			cpu0_trip: cpu0-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu0_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu1-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 2>;
+		tracks-low;
+		trips {
+			cpu1_trip: cpu1-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu1_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu2-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 3>;
+		tracks-low;
+		trips {
+			cpu2_trip: cpu2-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu2_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu3-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 4>;
+		tracks-low;
+		trips {
+			cpu3_trip: cpu3-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu3_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu4-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 5>;
+		tracks-low;
+		trips {
+			cpu4_trip: cpu4-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu4_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu5-silver-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 6>;
+		tracks-low;
+		trips {
+			cpu5_trip: cpu5-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpu5_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	kryo-l3-0-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 7>;
+		tracks-low;
+		trips {
+			l3_0_trip: l3-0-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&l3_0_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	kryo-l3-1-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 8>;
+		tracks-low;
+		trips {
+			l3_1_trip: l3-1-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&l3_1_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu0-gold-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 9>;
+		tracks-low;
+		trips {
+			cpug0_trip: cpug0-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpug0_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	cpu1-gold-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 10>;
+		tracks-low;
+		trips {
+			cpug1_trip: cpug1-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&cpug1_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	gpu0-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 11>;
+		tracks-low;
+		trips {
+			gpu0_trip_l: gpu0-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&gpu0_trip_l>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	gpu1-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens0 12>;
+		tracks-low;
+		trips {
+			gpu1_trip_l: gpu1-trip_l {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&gpu1_trip_l>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	aoss1-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 0>;
+		tracks-low;
+		trips {
+			aoss1_trip: aoss1-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&aoss1_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mdm-dsp-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 1>;
+		tracks-low;
+		trips {
+			dsp_trip: dsp-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&dsp_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	ddr-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 2>;
+		tracks-low;
+		trips {
+			ddr_trip: ddr-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&ddr_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	wlan-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 3>;
+		tracks-low;
+		trips {
+			wlan_trip: wlan-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&wlan_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	compute-hvx-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 4>;
+		tracks-low;
+		trips {
+			hvx_trip: hvx-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&hvx_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	camera-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 5>;
+		tracks-low;
+		trips {
+			camera_trip: camera-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&camera_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mmss-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 6>;
+		tracks-low;
+		trips {
+			mmss_trip: mmss-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&mmss_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	mdm-core-lowf {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "low_limits_floor";
+		thermal-sensors = <&tsens1 7>;
+		tracks-low;
+		trips {
+			mdm_trip: mdm-trip {
+				temperature = <5000>;
+				hysteresis = <5000>;
+				type = "passive";
+			};
+		};
+		cooling-maps {
+			cpu0_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&CPU0 4 4>;
+			};
+			cpu6_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&CPU6 9 9>;
+			};
+			gpu_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&msm_gpu 1 1>;
+			};
+			cx_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&cx_cdev 0 0>;
+			};
+			mx_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&mx_cdev 0 0>;
+			};
+			modem_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&modem_vdd 0 0>;
+			};
+			adsp_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&adsp_vdd 0 0>;
+			};
+			cdsp_vdd_cdev {
+				trip = <&mdm_trip>;
+				cooling-device = <&cdsp_vdd 0 0>;
+			};
+		};
+	};
+
+	lmh-dcvs-01 {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&lmh_dcvs1>;
+
+		trips {
+			active-config {
+				temperature = <95000>;
+				hysteresis = <30000>;
+				type = "passive";
+			};
+		};
+	};
+
+	lmh-dcvs-00 {
+		polling-delay-passive = <0>;
+		polling-delay = <0>;
+		thermal-governor = "user_space";
+		thermal-sensors = <&lmh_dcvs0>;
+
+		trips {
+			active-config {
+				temperature = <95000>;
+				hysteresis = <30000>;
+				type = "passive";
+			};
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
index bd35cf2..6a69e29 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-usb.dtsi
@@ -17,10 +17,19 @@
 	/delete-node/ ssusb@a800000;
 	/delete-node/ qusb@88e3000;
 	/delete-node/ ssphy@88eb000;
+	/delete-node/ usb_audio_qmi_dev;
+	usb_audio_qmi_dev {
+		compatible = "qcom,usb-audio-qmi-dev";
+		iommus = <&apps_smmu 0x180f 0x0>;
+		qcom,usb-audio-stream-id = <0xf>;
+		qcom,usb-audio-intr-num = <2>;
+	};
 };
 
 &usb0 {
-	extcon = <&pm660_pdphy>, <&pm660_pdphy>, <0> /* <&eud> */;
+	/delete-property/ iommus;
+	/delete-property/ qcom,smmu-s1-bypass;
+	extcon = <0>, <0>, <0> /* <&eud> */;
 };
 
 &qusb_phy0 {
@@ -30,6 +39,6 @@
 };
 
 &usb_qmp_dp_phy {
-	vdd-supply = <&pm660_l1>;
-	core-supply = <&pm660l_l1>;
+	vdd-supply = <&pm660l_l1>; /* 0.88v */
+	core-supply = <&pm660_l1>; /* 1.2v */
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-cdp-overlay.dts
similarity index 66%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-cdp-overlay.dts
index 2fac9e8..e4e1db5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-cdp-overlay.dts
@@ -19,11 +19,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L, USB-C Audio, CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 2>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-cdp.dts
new file mode 100644
index 0000000..80a8423
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-cdp.dts
@@ -0,0 +1,26 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L, USB-C Audio, CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 2>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp-overlay.dts
similarity index 64%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp-overlay.dts
index 2fac9e8..c5bab55 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp-overlay.dts
@@ -19,11 +19,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660+PM660L, USB-C Audio, Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 3>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp.dts
new file mode 100644
index 0000000..2c53334
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-cdp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660+PM660L, USB-C Audio, Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 3>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp-overlay.dts
similarity index 64%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp-overlay.dts
index 2fac9e8..09ba184 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp-overlay.dts
@@ -19,11 +19,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
-
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660+PM660L, USB-C Audio, Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 3>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp.dts
new file mode 100644
index 0000000..7a19819
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-mtp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660+PM660L, USB-C Audio, Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 3>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp-overlay.dts
similarity index 63%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp-overlay.dts
index 2fac9e8..71db0f7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp-overlay.dts
@@ -19,11 +19,18 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660+PM660A, USB-C Audio, Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 3>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp.dts
new file mode 100644
index 0000000..ff641e6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-cdp.dts
@@ -0,0 +1,29 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660+PM660A, USB-C Audio, Ext. Audio Codec CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 3>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp-overlay.dts
similarity index 63%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp-overlay.dts
index 2fac9e8..c2e6f58 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp-overlay.dts
@@ -19,11 +19,17 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660+PM660A, USB-C Audio, Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 3>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp.dts
new file mode 100644
index 0000000..2cd68f1
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-external-codec-pm660a-mtp.dts
@@ -0,0 +1,29 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-external-codec.dtsi"
+#include "sdm670-ext-cdc-usbc-audio.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM 670 PM660+PM660A, USB-C Audio, Ext. Audio Codec MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 3>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-mtp-overlay.dts
similarity index 66%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-mtp-overlay.dts
index 2fac9e8..3d5c04e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-mtp-overlay.dts
@@ -19,11 +19,15 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L, USB-C Audio, MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 2>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-mtp.dts
new file mode 100644
index 0000000..8449625
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-mtp.dts
@@ -0,0 +1,27 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660L, USB-C Audio, MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 2>;
+	qcom,pmic-id = <0x0001001b 0x0101011a 0x0 0x0>,
+		       <0x0001001b 0x0102001a 0x0 0x0>,
+		       <0x0001001b 0x0201011a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp-overlay.dts
similarity index 65%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp-overlay.dts
index 2fac9e8..6a26d95 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp-overlay.dts
@@ -19,11 +19,17 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A, USB-C Audio, CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <1 2>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp.dts
new file mode 100644
index 0000000..1871b45
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-cdp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-cdp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A, USB-C Audio, CDP";
+	compatible = "qcom,sdm670-cdp", "qcom,sdm670", "qcom,cdp";
+	qcom,board-id = <1 2>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp-overlay.dts
similarity index 65%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp-overlay.dts
index 2fac9e8..d565cdd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp-overlay.dts
@@ -19,11 +19,16 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A, USB-C Audio, MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,msm-id = <336 0x0>;
+	qcom,board-id = <8 2>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp.dts b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp.dts
new file mode 100644
index 0000000..b288569
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-usbc-pm660a-mtp.dts
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+
+#include "sdm670.dtsi"
+#include "sdm670-mtp.dtsi"
+#include "pm660a.dtsi"
+#include "sdm670-int-cdc-usbc-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. SDM670 PM660 + PM660A, USB-C Audio, MTP";
+	compatible = "qcom,sdm670-mtp", "qcom,sdm670", "qcom,mtp";
+	qcom,board-id = <8 2>;
+	qcom,pmic-id = <0x0001001b 0x0001011a 0x0 0x0>,
+		       <0x0001001b 0x0002001a 0x0 0x0>,
+		       <0x0001001b 0x0202001a 0x0 0x0>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
new file mode 100644
index 0000000..01d4057
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -0,0 +1,214 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/msm/msm-bus-ids.h>
+#include <dt-bindings/clock/qcom,videocc-sdm845.h>
+
+&soc {
+	msm_vidc0: qcom,vidc0 {
+		compatible = "qcom,msm-vidc", "qcom,sdm670-vidc";
+		status = "ok";
+		sku-index = <0>;
+		reg = <0xaa00000 0x200000>;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* Supply */
+		venus-supply = <&venus_gdsc>;
+		venus-core0-supply = <&vcodec0_gdsc>;
+		venus-core1-supply = <&vcodec1_gdsc>;
+
+		/* Clocks */
+		clock-names = "core_clk", "iface_clk", "bus_clk",
+			"core0_clk", "core0_bus_clk",
+			"core1_clk", "core1_bus_clk";
+		clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
+			<&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
+			<&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC1_CORE_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC1_AXI_CLK>;
+		qcom,proxy-clock-names = "core_clk", "iface_clk",
+			"bus_clk", "core0_clk", "core0_bus_clk",
+			"core1_clk", "core1_bus_clk";
+		qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0 0x1 0x0>;
+		qcom,allowed-clock-rates = <100000000 200000000 320000000
+			380000000 444000000 533000000>;
+
+		/* Buses */
+		bus_cnoc {
+			compatible = "qcom,msm-vidc,bus";
+			label = "cnoc";
+			qcom,bus-master = <MSM_BUS_MASTER_AMPSS_M0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_VENUS_CFG>;
+			qcom,bus-governor = "performance";
+			qcom,bus-range-kbps = <1000 1000>;
+		};
+
+		venus_bus_ddr {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-ddr";
+			qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+			qcom,bus-governor = "msm-vidc-ddr";
+			qcom,bus-range-kbps = <1000 3388000>;
+		};
+		arm9_bus_ddr {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-arm9-ddr";
+			qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+			qcom,bus-governor = "performance";
+			qcom,bus-range-kbps = <1000 1000>;
+		};
+
+		/* MMUs */
+		non_secure_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_ns";
+			iommus =
+				<&apps_smmu 0x10a0 0x8>,
+				<&apps_smmu 0x10b0 0x0>;
+			buffer-types = <0xfff>;
+			virtual-addr-pool = <0x70800000 0x6f800000>;
+		};
+
+		secure_bitstream_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_bitstream";
+			iommus =
+				<&apps_smmu 0x10a1 0x8>,
+				<&apps_smmu 0x10a5 0x8>;
+			buffer-types = <0x241>;
+			virtual-addr-pool = <0x4b000000 0x25800000>;
+			qcom,secure-context-bank;
+		};
+
+		secure_pixel_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_pixel";
+			iommus =
+				<&apps_smmu 0x10a3 0x8>;
+			buffer-types = <0x106>;
+			virtual-addr-pool = <0x25800000 0x25800000>;
+			qcom,secure-context-bank;
+		};
+
+		secure_non_pixel_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_non_pixel";
+			iommus =
+				<&apps_smmu 0x10a4 0x8>,
+				<&apps_smmu 0x10b4 0x0>;
+			buffer-types = <0x480>;
+			virtual-addr-pool = <0x1000000 0x24800000>;
+			qcom,secure-context-bank;
+		};
+	};
+
+	msm_vidc1: qcom,vidc1 {
+		compatible = "qcom,msm-vidc", "qcom,sdm670-vidc";
+		status = "ok";
+		sku-index = <1>;
+		reg = <0xaa00000 0x200000>;
+		interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* Supply */
+		venus-supply = <&venus_gdsc>;
+		venus-core0-supply = <&vcodec0_gdsc>;
+
+		/* Clocks */
+		clock-names = "core_clk", "iface_clk", "bus_clk",
+			"core0_clk", "core0_bus_clk";
+		clocks = <&clock_videocc VIDEO_CC_VENUS_CTL_CORE_CLK>,
+			<&clock_videocc VIDEO_CC_VENUS_AHB_CLK>,
+			<&clock_videocc VIDEO_CC_VENUS_CTL_AXI_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC0_CORE_CLK>,
+			<&clock_videocc VIDEO_CC_VCODEC0_AXI_CLK>;
+		qcom,proxy-clock-names = "core_clk", "iface_clk",
+			"bus_clk", "core0_clk", "core0_bus_clk";
+		qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
+		qcom,allowed-clock-rates = <100000000 200000000 320000000
+			364700000>;
+
+		/* Buses */
+		bus_cnoc {
+			compatible = "qcom,msm-vidc,bus";
+			label = "cnoc";
+			qcom,bus-master = <MSM_BUS_MASTER_AMPSS_M0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_VENUS_CFG>;
+			qcom,bus-governor = "performance";
+			qcom,bus-range-kbps = <1000 1000>;
+		};
+
+		venus_bus_ddr {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-ddr";
+			qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+			qcom,bus-governor = "msm-vidc-ddr";
+			qcom,bus-range-kbps = <1000 2128000>;
+		};
+		arm9_bus_ddr {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-arm9-ddr";
+			qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+			qcom,bus-governor = "performance";
+			qcom,bus-range-kbps = <1000 1000>;
+		};
+
+		/* MMUs */
+		non_secure_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_ns";
+			iommus =
+				<&apps_smmu 0x10a0 0x8>,
+				<&apps_smmu 0x10b0 0x0>;
+			buffer-types = <0xfff>;
+			virtual-addr-pool = <0x70800000 0x6f800000>;
+		};
+
+		secure_bitstream_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_bitstream";
+			iommus =
+				<&apps_smmu 0x10a1 0x8>,
+				<&apps_smmu 0x10a5 0x8>;
+			buffer-types = <0x241>;
+			virtual-addr-pool = <0x4b000000 0x25800000>;
+			qcom,secure-context-bank;
+		};
+
+		secure_pixel_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_pixel";
+			iommus =
+				<&apps_smmu 0x10a3 0x8>;
+			buffer-types = <0x106>;
+			virtual-addr-pool = <0x25800000 0x25800000>;
+			qcom,secure-context-bank;
+		};
+
+		secure_non_pixel_cb {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_sec_non_pixel";
+			iommus =
+				<&apps_smmu 0x10a4 0x8>,
+				<&apps_smmu 0x10b4 0x0>;
+			buffer-types = <0x480>;
+			virtual-addr-pool = <0x1000000 0x24800000>;
+			qcom,secure-context-bank;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index aeaa3ff..95f831b 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -23,6 +23,8 @@
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include <dt-bindings/clock/qcom,aop-qmp.h>
 
+#define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024))
+
 / {
 	model = "Qualcomm Technologies, Inc. SDM670";
 	compatible = "qcom,sdm670";
@@ -40,6 +42,10 @@
 		hsuart0 = &qupv3_se6_4uart;
 	};
 
+	chosen {
+		bootargs = "rcupdate.rcu_expedited=1 core_ctl_disable_cpumask=6-7";
+	};
+
 	cpus {
 		#address-cells = <2>;
 		#size-cells = <0>;
@@ -50,9 +56,12 @@
 			reg = <0x0 0x0>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_0>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_0: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -66,11 +75,14 @@
 			};
 			L1_I_0: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_0: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_0: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -80,9 +92,12 @@
 			reg = <0x0 0x100>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_100>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_100: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -91,11 +106,14 @@
 			};
 			L1_I_100: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_100: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_100: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -105,9 +123,12 @@
 			reg = <0x0 0x200>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_200>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_200: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -116,11 +137,14 @@
 			};
 			L1_I_200: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_200: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_200: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -130,9 +154,12 @@
 			reg = <0x0 0x300>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_300>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_300: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -141,11 +168,14 @@
 			};
 			L1_I_300: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_300: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_300: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -155,9 +185,12 @@
 			reg = <0x0 0x400>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_400>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_400: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -166,11 +199,14 @@
 			};
 			L1_I_400: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_400: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_400: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -180,9 +216,12 @@
 			reg = <0x0 0x500>;
 			enable-method = "psci";
 			efficiency = <1024>;
-			cache-size = <0x8000>;
+			cache-size = <0x10000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_500>;
+			sched-energy-costs = <&CPU_COST_0 &CLUSTER_COST_0>;
+			qcom,lmh-dcvs = <&lmh_dcvs0>;
+			#cooling-cells = <2>;
 			L2_500: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x20000>;
@@ -191,11 +230,14 @@
 			};
 			L1_I_500: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_500: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x9000>;
+				qcom,dump-size = <0xa000>;
+			};
+			L1_TLB_500: l1-tlb {
+				qcom,dump-size = <0x3000>;
 			};
 		};
 
@@ -205,9 +247,12 @@
 			reg = <0x0 0x600>;
 			enable-method = "psci";
 			efficiency = <1740>;
-			cache-size = <0x10000>;
+			cache-size = <0x20000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_600>;
+			sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
+			qcom,lmh-dcvs = <&lmh_dcvs1>;
+			#cooling-cells = <2>;
 			L2_600: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x40000>;
@@ -216,11 +261,14 @@
 			};
 			L1_I_600: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x12000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_600: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x12000>;
+				qcom,dump-size = <0x14000>;
+			};
+			L1_TLB_600: l1-tlb {
+				qcom,dump-size = <0x3c000>;
 			};
 		};
 
@@ -230,9 +278,12 @@
 			reg = <0x0 0x700>;
 			enable-method = "psci";
 			efficiency = <1740>;
-			cache-size = <0x10000>;
+			cache-size = <0x20000>;
 			cpu-release-addr = <0x0 0x90000000>;
 			next-level-cache = <&L2_700>;
+			sched-energy-costs = <&CPU_COST_1 &CLUSTER_COST_1>;
+			qcom,lmh-dcvs = <&lmh_dcvs1>;
+			#cooling-cells = <2>;
 			L2_700: l2-cache {
 				compatible = "arm,arch-cache";
 				cache-size = <0x40000>;
@@ -241,11 +292,14 @@
 			};
 			L1_I_700: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x12000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_700: l1-dcache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x12000>;
+				qcom,dump-size = <0x14000>;
+			};
+			L1_TLB_700: l1-tlb {
+				qcom,dump-size = <0x3c000>;
 			};
 		};
 
@@ -287,6 +341,93 @@
 		};
 	};
 
+	energy_costs: energy-costs {
+		compatible = "sched-energy";
+
+		CPU_COST_0: core-cost0 {
+			busy-cost-data = <
+				 300000    14
+				 576000    25
+				 748800    31
+				 998400    46
+				1209600    57
+				1324800    84
+				1516800    96
+				1612800   114
+				1708000   139
+			>;
+			idle-cost-data = <
+				12 10 8 6
+			>;
+		};
+		CPU_COST_1: core-cost1 {
+			busy-cost-data = <
+				 300000    256
+				 652800    307
+				 825600    332
+				 979200    382
+				1132800    408
+				1363200    448
+				1536000    586
+				1747200    641
+				1843200    659
+				1996800    696
+				2054400    876
+				2169600    900
+				2208000    924
+				2361600    948
+				2400000   1170
+				2457600   1200
+				2515200   1300
+				2611200   1400
+			>;
+			idle-cost-data = <
+				100 80 60 40
+			>;
+		};
+		CLUSTER_COST_0: cluster-cost0 {
+			busy-cost-data = <
+				 300000    5
+				 576000    7
+				 748800    8
+				 998400    9
+				1209600   10
+				1324800   13
+				1516800   15
+				1612800   16
+				1708000   19
+			>;
+			idle-cost-data = <
+				4 3 2 1
+			>;
+		};
+		CLUSTER_COST_1: cluster-cost1 {
+			busy-cost-data = <
+				 300000    25
+				 652800    30
+				 825600    33
+				 979200    38
+				1132800    40
+				1363200    44
+				1536000    58
+				1747200    64
+				1843200    65
+				1996800    69
+				2054400    87
+				2169600    90
+				2208000    92
+				2361600    94
+				2400000   117
+				2457600   120
+				2515200   130
+				2611200   140
+			>;
+			idle-cost-data = <
+				4 3 2 1
+			>;
+		};
+	};
+
 	psci {
 		compatible = "arm,psci-1.0";
 		method = "smc";
@@ -312,7 +453,7 @@
 					dev = "/dev/block/platform/soc/1d84000.ufshc/by-name/vendor";
 					type = "ext4";
 					mnt_flags = "ro,barrier=1,discard";
-					fsmgr_flags = "wait,slotselect";
+					fsmgr_flags = "wait,slotselect,avb";
 				};
 			};
 		};
@@ -365,24 +506,6 @@
 			reg = <0 0x93d00000 0 0x1e00000>;
 		};
 
-		pil_ipa_fw_mem: pil_ipa_fw_region@95b00000 {
-			compatible = "removed-dma-pool";
-			no-map;
-			reg = <0 0x95b00000 0 0x10000>;
-		};
-
-		pil_ipa_gsi_mem: pil_ipa_gsi_region@95b10000 {
-			compatible = "removed-dma-pool";
-			no-map;
-			reg = <0 0x95b10000 0 0x5000>;
-		};
-
-		pil_gpu_mem: pil_gpu_region@95b15000 {
-			compatible = "removed-dma-pool";
-			no-map;
-			reg = <0 0x95b15000 0 0x1000>;
-		};
-
 		adsp_mem: adsp_region {
 			compatible = "shared-dma-pool";
 			alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -394,7 +517,7 @@
 		qseecom_mem: qseecom_region {
 			compatible = "shared-dma-pool";
 			alloc-ranges = <0 0x00000000 0 0xffffffff>;
-			reusable;
+			no-map;
 			alignment = <0 0x400000>;
 			size = <0 0x1400000>;
 		};
@@ -415,6 +538,17 @@
 			size = <0 0x5c00000>;
 		};
 
+		cont_splash_memory: cont_splash_region@9d400000 {
+			reg = <0x0 0x9d400000 0x0 0x02400000>;
+			label = "cont_splash_region";
+		};
+
+		dump_mem: mem_dump_region {
+			compatible = "shared-dma-pool";
+			reusable;
+			size = <0 0x2400000>;
+		};
+
 		/* global autoconfigured region for contiguous allocations */
 		linux,cma {
 			compatible = "shared-dma-pool";
@@ -434,12 +568,107 @@
 #include "sdm670-qupv3.dtsi"
 
 #include "sdm670-coresight.dtsi"
+
+#include "sdm670-vidc.dtsi"
+
+#include "sdm670-sde-pll.dtsi"
+
+#include "sdm670-sde.dtsi"
+
 &soc {
 	#address-cells = <1>;
 	#size-cells = <1>;
 	ranges = <0 0 0 0xffffffff>;
 	compatible = "simple-bus";
 
+	jtag_mm0: jtagmm@7040000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7040000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU0>;
+	};
+
+	jtag_mm1: jtagmm@7140000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7140000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qom,coresight-jtagmm-cpu = <&CPU1>;
+	};
+
+	jtag_mm2: jtagmm@7240000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7240000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU2>;
+	};
+
+	jtag_mm3: jtagmm@7340000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7340000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU3>;
+	};
+
+	jtag_mm4: jtagmm@7440000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7440000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU4>;
+	};
+
+	jtag_mm5: jtagmm@7540000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7540000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU5>;
+	};
+
+	jtag_mm6: jtagmm@7640000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7640000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU6>;
+	};
+
+	jtag_mm7: jtagmm@7740000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7740000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU7>;
+	};
+
 	intc: interrupt-controller@17a00000 {
 		compatible = "arm,gic-v3";
 		#interrupt-cells = <3>;
@@ -461,307 +690,167 @@
 		clock-frequency = <19200000>;
 	};
 
+	qcom,memshare {
+		compatible = "qcom,memshare";
+
+		qcom,client_1 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x0>;
+			qcom,client-id = <0>;
+			qcom,allocate-boot-time;
+			label = "modem";
+		};
+
+		qcom,client_2 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x0>;
+			qcom,client-id = <2>;
+			label = "modem";
+		};
+
+		mem_client_3_size: qcom,client_3 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x500000>;
+			qcom,client-id = <1>;
+			label = "modem";
+		};
+	};
+
 	qcom,sps {
 		compatible = "qcom,msm_sps_4k";
 		qcom,pipe-attr-ee;
 	};
 
-	thermal_zones: thermal-zones {
-		aoss0-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 0>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu0-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 1>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu1-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 2>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu2-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 3>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu3-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 4>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu4-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 5>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu5-silver-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 6>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		kryo-l3-0-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 7>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		kryo-l3-1-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 8>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu0-gold-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 9>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		cpu1-gold-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 10>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		gpu0-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens0 11>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		gpu1-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-governor = "user_space";
-			thermal-sensors = <&tsens0 12>;
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		aoss1-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 0>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		mdm-dsp-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 1>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		ddr-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 2>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		wlan-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 3>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		compute-hvx-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 4>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		camera-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 5>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		mmss-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 6>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
-
-		mdm-core-usr {
-			polling-delay-passive = <0>;
-			polling-delay = <0>;
-			thermal-sensors = <&tsens1 7>;
-			thermal-governor = "user_space";
-			trips {
-				active-config0 {
-					temperature = <125000>;
-					hysteresis = <1000>;
-					type = "passive";
-				};
-			};
-		};
+	qcom_cedev: qcedev@1de0000 {
+		compatible = "qcom,qcedev";
+		reg = <0x1de0000 0x20000>,
+			<0x1dc4000 0x24000>;
+		reg-names = "crypto-base","crypto-bam-base";
+		interrupts = <0 272 0>;
+		qcom,bam-pipe-pair = <3>;
+		qcom,ce-hw-instance = <0>;
+		qcom,ce-device = <0>;
+		qcom,ce-hw-shared;
+		qcom,bam-ee = <0>;
+		qcom,msm-bus,name = "qcedev-noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<125 512 0 0>,
+				<125 512 393600 393600>;
+		clock-names = "core_clk_src", "core_clk",
+				"iface_clk", "bus_clk";
+		clocks = <&clock_gcc GCC_CE1_CLK>,
+				 <&clock_gcc GCC_CE1_CLK>,
+				 <&clock_gcc GCC_CE1_AHB_CLK>,
+				 <&clock_gcc GCC_CE1_AXI_CLK>;
+		qcom,ce-opp-freq = <171430000>;
+		qcom,request-bw-before-clk;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x706 0x1>,
+			 <&apps_smmu 0x716 0x1>;
 	};
 
+	qcom_crypto: qcrypto@1de0000 {
+		compatible = "qcom,qcrypto";
+		reg = <0x1de0000 0x20000>,
+			<0x1dc4000 0x24000>;
+		reg-names = "crypto-base","crypto-bam-base";
+		interrupts = <0 272 0>;
+		qcom,bam-pipe-pair = <2>;
+		qcom,ce-hw-instance = <0>;
+		qcom,ce-device = <0>;
+		qcom,bam-ee = <0>;
+		qcom,ce-hw-shared;
+		qcom,clk-mgmt-sus-res;
+		qcom,msm-bus,name = "qcrypto-noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<125 512 0 0>,
+			<125 512 393600 393600>;
+		clock-names = "core_clk_src", "core_clk",
+				"iface_clk", "bus_clk";
+		clocks = <&clock_gcc GCC_CE1_CLK>,
+				 <&clock_gcc GCC_CE1_CLK>,
+				 <&clock_gcc GCC_CE1_AHB_CLK>,
+				 <&clock_gcc GCC_CE1_AXI_CLK>;
+		qcom,ce-opp-freq = <171430000>;
+		qcom,request-bw-before-clk;
+		qcom,use-sw-aes-cbc-ecb-ctr-algo;
+		qcom,use-sw-aes-xts-algo;
+		qcom,use-sw-aes-ccm-algo;
+		qcom,use-sw-aead-algo;
+		qcom,use-sw-ahash-algo;
+		qcom,use-sw-hmac-algo;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x704 0x1>,
+			 <&apps_smmu 0x714 0x1>;
+	};
+
+	qcom,qbt1000 {
+		compatible = "qcom,qbt1000";
+		clock-names = "core", "iface";
+		clock-frequency = <25000000>;
+		qcom,ipc-gpio = <&tlmm 121 0>;
+		qcom,finger-detect-gpio = <&tlmm 122 0>;
+	};
+
+	qcom_seecom: qseecom@86d00000 {
+		compatible = "qcom,qseecom";
+		reg = <0x86d00000 0x2200000>;
+		reg-names = "secapp-region";
+		qcom,hlos-num-ce-hw-instances = <1>;
+		qcom,hlos-ce-hw-instance = <0>;
+		qcom,qsee-ce-hw-instance = <0>;
+		qcom,disk-encrypt-pipe-pair = <2>;
+		qcom,support-fde;
+		qcom,no-clock-support;
+		qcom,fde-key-size;
+		qcom,appsbl-qseecom-support;
+		qcom,msm-bus,name = "qseecom-noc";
+		qcom,msm-bus,num-cases = <4>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<125 512 0 0>,
+				<125 512 200000 400000>,
+				<125 512 300000 800000>,
+				<125 512 400000 1000000>;
+		clock-names = "core_clk_src", "core_clk",
+					 "iface_clk", "bus_clk";
+		clocks = <&clock_gcc GCC_CE1_CLK>,
+			 <&clock_gcc GCC_CE1_CLK>,
+			 <&clock_gcc GCC_CE1_AHB_CLK>,
+			 <&clock_gcc GCC_CE1_AXI_CLK>;
+		qcom,ce-opp-freq = <171430000>;
+		qcom,qsee-reentrancy-support = <2>;
+	};
+
+	qcom_tzlog: tz-log@146bf720 {
+		compatible = "qcom,tz-log";
+		reg = <0x146bf720 0x3000>;
+		qcom,hyplog-enabled;
+		hyplog-address-offset = <0x410>;
+		hyplog-size-offset = <0x414>;
+	};
+
+	qcom_rng: qrng@793000{
+		compatible = "qcom,msm-rng";
+		reg = <0x793000 0x1000>;
+		qcom,msm-rng-iface-clk;
+		qcom,no-qrng-config;
+		qcom,msm-bus,name = "msm-rng-noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<1 618 0 0>,    /* No vote */
+			<1 618 0 800>;  /* 100 KHz */
+		clocks = <&clock_gcc GCC_PRNG_AHB_CLK>;
+		clock-names = "iface_clk";
+	};
+
+	thermal_zones: thermal-zones {};
+
 	tsens0: tsens@c222000 {
 		compatible = "qcom,tsens24xx";
 		reg = <0xc222000 0x4>,
@@ -857,62 +946,111 @@
 	};
 
 	clock_rpmh: qcom,rpmhclk {
-		compatible = "qcom,dummycc";
-		clock-output-names = "rpmh_clocks";
+		compatible = "qcom,rpmh-clk-sdm670";
 		#clock-cells = <1>;
+		mboxes = <&apps_rsc 0>;
+		mbox-names = "apps";
 	};
 
 	clock_gcc: qcom,gcc@100000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "gcc_clocks";
+		compatible = "qcom,gcc-sdm670", "syscon";
+		reg = <0x100000 0x1f0000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pm660l_s3_level>;
+		vdd_cx_ao-supply = <&pm660l_s3_level_ao>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
 	clock_videocc: qcom,videocc@ab00000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "videocc_clocks";
+		compatible = "qcom,video_cc-sdm670", "syscon";
+		reg = <0xab00000 0x10000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pm660l_s3_level>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
 	clock_camcc: qcom,camcc@ad00000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "camcc_clocks";
+		compatible = "qcom,cam_cc-sdm670", "syscon";
+		reg = <0xad00000 0x10000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pm660l_s3_level>;
+		vdd_mx-supply = <&pm660l_s1_level>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
 	clock_dispcc: qcom,dispcc@af00000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "dispcc_clocks";
+		compatible = "qcom,dispcc-sdm670", "syscon";
+		reg = <0xaf00000 0x10000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pm660l_s3_level>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
 	clock_gpucc: qcom,gpucc@5090000 {
-		compatible = "qcom,dummycc";
-		clock-output-names = "gpucc_clocks";
+		compatible = "qcom,gpucc-sdm670", "syscon";
+		reg = <0x5090000 0x9000>;
+		reg-names = "cc_base";
+		vdd_cx-supply = <&pm660l_s3_level>;
+		vdd_mx-supply = <&pm660l_s1_level>;
+		qcom,gpu_cc_gmu_clk_src-opp-handle = <&gmu>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
 	clock_gfx: qcom,gfxcc@5090000  {
-		compatible = "qcom,dummycc";
-		clock-output-names = "gfxcc_clocks";
+		compatible = "qcom,gfxcc-sdm670";
+		reg = <0x5090000 0x9000>;
+		reg-names = "cc_base";
+		vdd_gfx-supply = <&pm660l_s2_level>;
+		qcom,gpu_cc_gx_gfx3d_clk_src-opp-handle = <&msm_gpu>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
 	};
 
-	clock_cpucc: qcom,cpucc {
-		compatible = "qcom,dummycc";
-		clock-output-names = "cpucc_clocks";
+	cpucc_debug: syscon@17970018 {
+		compatible = "syscon";
+		reg = <0x17970018 0x4>;
+	};
+
+	clock_debug: qcom,cc-debug {
+		compatible = "qcom,debugcc-sdm845";
+		qcom,cc-count = <5>;
+		qcom,gcc = <&clock_gcc>;
+		qcom,videocc = <&clock_videocc>;
+		qcom,camcc = <&clock_camcc>;
+		qcom,dispcc = <&clock_dispcc>;
+		qcom,gpucc = <&clock_gpucc>;
+		qcom,cpucc = <&cpucc_debug>;
+		clock-names = "xo_clk_src";
+		clocks = <&clock_rpmh RPMH_CXO_CLK>;
 		#clock-cells = <1>;
-		#reset-cells = <1>;
+	};
+
+	clock_cpucc: qcom,cpucc@0x17d41000 {
+		compatible = "qcom,clk-cpu-osm-sdm670";
+		reg = <0x17d41000 0x1400>,
+			<0x17d43000 0x1400>,
+			<0x17d45800 0x1400>,
+			<0x784248 0x4>;
+		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
+				"cpr_rc";
+		vdd_l3_mx_ao-supply = <&pm660l_s1_level_ao>;
+		vdd_pwrcl_mx_ao-supply = <&pm660l_s1_level_ao>;
+
+		qcom,mx-turbo-freq = <1478400000 1689600000 3300000001>;
+		l3-devs = <&l3_cpu0 &l3_cpu6>;
+
+		clock-names = "xo_ao";
+		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
+		#clock-cells = <1>;
 	};
 
 	clock_aop: qcom,aopclk {
-		compatible = "qcom,aop-qmp-clk-v2";
+		compatible = "qcom,aop-qmp-clk-v1";
 		#clock-cells = <1>;
 		mboxes = <&qmp_aop 0>;
 		mbox-names = "qdss_clk";
@@ -929,6 +1067,16 @@
 		qcom,apps-ch-pipes = <0x780000>;
 		qcom,ea-pc = <0x290>;
 		status = "disabled";
+		qcom,iommu-s1-bypass;
+
+		iommu_slim_aud_ctrl_cb: qcom,iommu_slim_ctrl_cb {
+			compatible = "qcom,iommu-slim-ctrl-cb";
+			iommus = <&apps_smmu 0x1826 0x0>,
+				 <&apps_smmu 0x182d 0x0>,
+				 <&apps_smmu 0x182e 0x1>,
+				 <&apps_smmu 0x1830 0x1>;
+		};
+
 	};
 
 	slim_qca: slim@62e40000 {
@@ -939,14 +1087,28 @@
 		reg-names = "slimbus_physical", "slimbus_bam_physical";
 		interrupts = <0 291 0>, <0 292 0>;
 		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
-		status = "disabled";
+		status = "ok";
+		qcom,iommu-s1-bypass;
+
+		iommu_slim_qca_ctrl_cb: qcom,iommu_slim_ctrl_cb {
+			compatible = "qcom,iommu-slim-ctrl-cb";
+			iommus = <&apps_smmu 0x1833 0x0>;
+		};
+
+		/* Slimbus Slave DT for WCN3990 */
+		btfmslim_codec: wcn3990 {
+			compatible = "qcom,btfmslim_slave";
+			elemental-addr = [00 01 20 02 17 02];
+			qcom,btfm-slim-ifd = "btfmslim_slave_ifd";
+			qcom,btfm-slim-ifd-elemental-addr = [00 00 20 02 17 02];
+		};
 	};
 
 	wdog: qcom,wdt@17980000{
 		compatible = "qcom,msm-watchdog";
 		reg = <0x17980000 0x1000>;
 		reg-names = "wdt-base";
-		interrupts = <0 3 0>, <0 4 0>;
+		interrupts = <0 0 0>, <0 1 0>;
 		qcom,bark-time = <11000>;
 		qcom,pet-time = <10000>;
 		qcom,ipi-ping;
@@ -958,6 +1120,12 @@
 		qcom,rtb-size = <0x100000>;
 	};
 
+	qcom,mpm2-sleep-counter@c221000 {
+		compatible = "qcom,mpm2-sleep-counter";
+		reg = <0x0c221000 0x1000>;
+		clock-frequency = <32768>;
+	};
+
 	qcom,msm-imem@146bf000 {
 		compatible = "qcom,msm-imem";
 		reg = <0x146bf000 0x1000>;
@@ -984,10 +1152,20 @@
 			compatible = "qcom,msm-imem-kaslr_offset";
 			reg = <0x6d0 12>;
 		};
+
+		boot_stats@6b0 {
+			compatible = "qcom,msm-imem-boot_stats";
+			reg = <0x6b0 0x20>;
+		};
+
+		diag_dload@c8 {
+			compatible = "qcom,msm-imem-diag-dload";
+			reg = <0xc8 0xc8>;
+		};
 	};
 
 	gpi_dma0: qcom,gpi-dma@0x800000 {
-		#dma-cells = <6>;
+		#dma-cells = <5>;
 		compatible = "qcom,gpi-dma";
 		reg = <0x800000 0x60000>;
 		reg-names = "gpi-top";
@@ -999,11 +1177,13 @@
 		qcom,gpii-mask = <0xfa>;
 		qcom,ev-factor = <2>;
 		iommus = <&apps_smmu 0x0016 0x0>;
+		qcom,smmu-cfg = <0x1>;
+		qcom,iova-range = <0x0 0x100000 0x0 0x100000>;
 		status = "ok";
 	};
 
 	gpi_dma1: qcom,gpi-dma@0xa00000 {
-		#dma-cells = <6>;
+		#dma-cells = <5>;
 		compatible = "qcom,gpi-dma";
 		reg = <0xa00000 0x60000>;
 		reg-names = "gpi-top";
@@ -1014,6 +1194,8 @@
 		qcom,max-num-gpii = <13>;
 		qcom,gpii-mask = <0xfa>;
 		qcom,ev-factor = <2>;
+		qcom,smmu-cfg = <0x1>;
+		qcom,iova-range = <0x0 0x100000 0x0 0x100000>;
 		iommus = <&apps_smmu 0x06d6 0x0>;
 		status = "ok";
 	};
@@ -1024,31 +1206,31 @@
 			qcom,dump-node = <&L1_I_0>;
 			qcom,dump-id = <0x60>;
 		};
-		qcom,l1_i_cache1 {
+		qcom,l1_i_cache100 {
 			qcom,dump-node = <&L1_I_100>;
 			qcom,dump-id = <0x61>;
 		};
-		qcom,l1_i_cache2 {
+		qcom,l1_i_cache200 {
 			qcom,dump-node = <&L1_I_200>;
 			qcom,dump-id = <0x62>;
 		};
-		qcom,l1_i_cache3 {
+		qcom,l1_i_cache300 {
 			qcom,dump-node = <&L1_I_300>;
 			qcom,dump-id = <0x63>;
 		};
-		qcom,l1_i_cache100 {
+		qcom,l1_i_cache400 {
 			qcom,dump-node = <&L1_I_400>;
 			qcom,dump-id = <0x64>;
 		};
-		qcom,l1_i_cache101 {
+		qcom,l1_i_cache500 {
 			qcom,dump-node = <&L1_I_500>;
 			qcom,dump-id = <0x65>;
 		};
-		qcom,l1_i_cache102 {
+		qcom,l1_i_cache600 {
 			qcom,dump-node = <&L1_I_600>;
 			qcom,dump-id = <0x66>;
 		};
-		qcom,l1_i_cache103 {
+		qcom,l1_i_cache700 {
 			qcom,dump-node = <&L1_I_700>;
 			qcom,dump-id = <0x67>;
 		};
@@ -1056,31 +1238,31 @@
 			qcom,dump-node = <&L1_D_0>;
 			qcom,dump-id = <0x80>;
 		};
-		qcom,l1_d_cache1 {
+		qcom,l1_d_cache100 {
 			qcom,dump-node = <&L1_D_100>;
 			qcom,dump-id = <0x81>;
 		};
-		qcom,l1_d_cache2 {
+		qcom,l1_d_cache200 {
 			qcom,dump-node = <&L1_D_200>;
 			qcom,dump-id = <0x82>;
 		};
-		qcom,l1_d_cache3 {
+		qcom,l1_d_cache300 {
 			qcom,dump-node = <&L1_D_300>;
 			qcom,dump-id = <0x83>;
 		};
-		qcom,l1_d_cache100 {
+		qcom,l1_d_cache400 {
 			qcom,dump-node = <&L1_D_400>;
 			qcom,dump-id = <0x84>;
 		};
-		qcom,l1_d_cache101 {
+		qcom,l1_d_cache500 {
 			qcom,dump-node = <&L1_D_500>;
 			qcom,dump-id = <0x85>;
 		};
-		qcom,l1_d_cache102 {
+		qcom,l1_d_cache600 {
 			qcom,dump-node = <&L1_D_600>;
 			qcom,dump-id = <0x86>;
 		};
-		qcom,l1_d_cache103 {
+		qcom,l1_d_cache700 {
 			qcom,dump-node = <&L1_D_700>;
 			qcom,dump-id = <0x87>;
 		};
@@ -1092,6 +1274,93 @@
 			qcom,dump-node = <&LLCC_2>;
 			qcom,dump-id = <0x141>;
 		};
+		qcom,l1_tlb_dump0 {
+			qcom,dump-node = <&L1_TLB_0>;
+			qcom,dump-id = <0x20>;
+		};
+		qcom,l1_tlb_dump100 {
+			qcom,dump-node = <&L1_TLB_100>;
+			qcom,dump-id = <0x21>;
+		};
+		qcom,l1_tlb_dump200 {
+			qcom,dump-node = <&L1_TLB_200>;
+			qcom,dump-id = <0x22>;
+		};
+		qcom,l1_tlb_dump300 {
+			qcom,dump-node = <&L1_TLB_300>;
+			qcom,dump-id = <0x23>;
+		};
+		qcom,l1_tlb_dump400 {
+			qcom,dump-node = <&L1_TLB_400>;
+			qcom,dump-id = <0x24>;
+		};
+		qcom,l1_tlb_dump500 {
+			qcom,dump-node = <&L1_TLB_500>;
+			qcom,dump-id = <0x25>;
+		};
+		qcom,l1_tlb_dump600 {
+			qcom,dump-node = <&L1_TLB_600>;
+			qcom,dump-id = <0x26>;
+		};
+		qcom,l1_tlb_dump700 {
+			qcom,dump-node = <&L1_TLB_700>;
+			qcom,dump-id = <0x27>;
+		};
+	};
+
+	mem_dump {
+		compatible = "qcom,mem-dump";
+		memory-region = <&dump_mem>;
+
+		rpmh_dump {
+			qcom,dump-size = <0x2000000>;
+			qcom,dump-id = <0xec>;
+		};
+
+		rpm_sw_dump {
+			qcom,dump-size = <0x28000>;
+			qcom,dump-id = <0xea>;
+		};
+
+		pmic_dump {
+			qcom,dump-size = <0x10000>;
+			qcom,dump-id = <0xe4>;
+		};
+
+		tmc_etf_dump {
+			qcom,dump-size = <0x10000>;
+			qcom,dump-id = <0xf0>;
+		};
+
+		tmc_etf_swao_dump {
+			qcom,dump-size = <0x8400>;
+			qcom,dump-id = <0xf1>;
+		};
+
+		tmc_etr_reg_dump {
+			qcom,dump-size = <0x1000>;
+			qcom,dump-id = <0x100>;
+		};
+
+		tmc_etf_reg_dump {
+			qcom,dump-size = <0x1000>;
+			qcom,dump-id = <0x101>;
+		};
+
+		tmc_etf_swao_reg_dump {
+			qcom,dump-size = <0x1000>;
+			qcom,dump-id = <0x102>;
+		};
+
+		misc_data_dump {
+			qcom,dump-size = <0x1000>;
+			qcom,dump-id = <0xe8>;
+		};
+
+		power_regs_data_dump {
+			qcom,dump-size = <0x100000>;
+			qcom,dump-id = <0xed>;
+		};
 	};
 
 	kryo3xx-erp {
@@ -1356,6 +1625,16 @@
 		interrupts = <0 17 0>;
 	};
 
+	eud: qcom,msm-eud@88e0000 {
+		compatible = "qcom,msm-eud";
+		interrupt-names = "eud_irq";
+		interrupts = <GIC_SPI 492 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0x88e0000 0x2000>;
+		reg-names = "eud_base";
+		clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+		clock-names = "cfg_ahb_clk";
+	};
+
 	qcom,llcc@1100000 {
 		compatible = "qcom,llcc-core", "syscon", "simple-mfd";
 		reg = <0x1100000 0x250000>;
@@ -1370,6 +1649,10 @@
 			qcom,dump-size = <0x80000>;
 		};
 
+		qcom,llcc-perfmon {
+			compatible = "qcom,llcc-perfmon";
+		};
+
 		qcom,llcc-erp {
 			compatible = "qcom,llcc-erp";
 			interrupt-names = "ecc_irq";
@@ -1508,6 +1791,45 @@
 			<0 0>,
 			<0 0>;
 
+		non-removable;
+		qcom,msm-bus,name = "ufshc_mem";
+		qcom,msm-bus,num-cases = <12>;
+		qcom,msm-bus,num-paths = <2>;
+		qcom,msm-bus,vectors-KBps =
+		/*
+		 * During HS G3 UFS runs at nominal voltage corner, vote
+		 * higher bandwidth to push other buses in the data path
+		 * to run at nominal to achieve max throughput.
+		 * 4GBps pushes BIMC to run at nominal.
+		 * 200MBps pushes CNOC to run at nominal.
+		 * Vote for half of this bandwidth for HS G3 1-lane.
+		 * For max bandwidth, vote high enough to push the buses
+		 * to run in turbo voltage corner.
+		 */
+		<123 512 0 0>, <1 757 0 0>,          /* No vote */
+		<123 512 922 0>, <1 757 1000 0>,     /* PWM G1 */
+		<123 512 1844 0>, <1 757 1000 0>,    /* PWM G2 */
+		<123 512 3688 0>, <1 757 1000 0>,    /* PWM G3 */
+		<123 512 7376 0>, <1 757 1000 0>,    /* PWM G4 */
+		<123 512 127796 0>, <1 757 1000 0>,  /* HS G1 RA */
+		<123 512 255591 0>, <1 757 1000 0>,  /* HS G2 RA */
+		<123 512 2097152 0>, <1 757 102400 0>,  /* HS G3 RA */
+		<123 512 149422 0>, <1 757 1000 0>,  /* HS G1 RB */
+		<123 512 298189 0>, <1 757 1000 0>,  /* HS G2 RB */
+		<123 512 2097152 0>, <1 757 102400 0>,  /* HS G3 RB */
+		<123 512 7643136 0>, <1 757 307200 0>; /* Max. bandwidth */
+
+		qcom,bus-vector-names = "MIN",
+		"PWM_G1_L1", "PWM_G2_L1", "PWM_G3_L1", "PWM_G4_L1",
+		"HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1",
+		"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
+		"MAX";
+
+		/* PM QoS */
+		qcom,pm-qos-cpu-groups = <0x3f 0xC0>;
+		qcom,pm-qos-cpu-group-latency-us = <70 70>;
+		qcom,pm-qos-default-cpu = <0>;
+
 		resets = <&clock_gcc GCC_UFS_PHY_BCR>;
 		reset-names = "core_reset";
 
@@ -1533,6 +1855,7 @@
 		qcom,sysmon-id = <1>;
 		qcom,ssctl-instance-id = <0x14>;
 		qcom,firmware-name = "adsp";
+		qcom,signal-aop;
 		memory-region = <&pil_adsp_mem>;
 
 		/* GPIO inputs from lpass */
@@ -1543,9 +1866,24 @@
 
 		/* GPIO output to lpass */
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "adsp-pil";
 		status = "ok";
 	};
 
+	qcom,rmtfs_sharedmem@0 {
+		compatible = "qcom,sharedmem-uio";
+		reg = <0x0 0x200000>;
+		reg-names = "rmtfs";
+		qcom,client-id = <0x00000001>;
+		qcom,guard-memory;
+	};
+
+	qcom,msm_gsi {
+		compatible = "qcom,msm_gsi";
+	};
+
 	qcom,rmnet-ipa {
 		compatible = "qcom,rmnet-ipa3";
 		qcom,rmnet-ipa-ssr;
@@ -1571,7 +1909,6 @@
 		qcom,ipa-wdi2;
 		qcom,use-64-bit-dma-mask;
 		qcom,arm-smmu;
-		qcom,smmu-s1-bypass;
 		qcom,bandwidth-vote-for-ipa;
 		qcom,msm-bus,name = "ipa";
 		qcom,msm-bus,num-cases = <4>;
@@ -1586,17 +1923,17 @@
 			<90 512 80000 640000>,
 			<90 585 80000 640000>,
 			<1 676 80000 80000>,
-			<143 777 0 150000>,
+			<143 777 0 150>, /* IB defined for IPA clk in MHz*/
 		/* NOMINAL */
 			<90 512 206000 960000>,
 			<90 585 206000 960000>,
 			<1 676 206000 160000>,
-			<143 777 0 300000>,
+			<143 777 0 300>, /* IB defined for IPA clk in MHz*/
 		/* TURBO */
 			<90 512 206000 3600000>,
 			<90 585 206000 3600000>,
 			<1 676 206000 300000>,
-			<143 777 0 355333>;
+			<143 777 0 355>; /* IB defined for IPA clk in MHz*/
 		qcom,bus-vector-names = "MIN", "SVS", "NOMINAL", "TURBO";
 
 		/* IPA RAM mmap */
@@ -1688,17 +2025,20 @@
 
 		ipa_smmu_ap: ipa_smmu_ap {
 			compatible = "qcom,ipa-smmu-ap-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x720 0x0>;
 			qcom,iova-mapping = <0x20000000 0x40000000>;
 		};
 
 		ipa_smmu_wlan: ipa_smmu_wlan {
 			compatible = "qcom,ipa-smmu-wlan-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x721 0x0>;
 		};
 
 		ipa_smmu_uc: ipa_smmu_uc {
 			compatible = "qcom,ipa-smmu-uc-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x722 0x0>;
 			qcom,iova-mapping = <0x40000000 0x20000000>;
 		};
@@ -1745,12 +2085,16 @@
 		vdd_cx-voltage = <RPMH_REGULATOR_LEVEL_TURBO>;
 		vdd_mx-supply = <&pm660l_s1_level>;
 		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
+		vdd_mss-supply = <&pm660_s5_level>;
+		vdd_mss-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
 		qcom,firmware-name = "modem";
 		qcom,pil-self-auth;
 		qcom,sysmon-id = <0>;
 		qcom,ssctl-instance-id = <0x12>;
 		qcom,override-acc;
+		qcom,signal-aop;
 		qcom,qdsp6v65-1-0;
+		qcom,mss_pdc_offset = <9>;
 		status = "ok";
 		memory-region = <&pil_modem_mem>;
 		qcom,mem-protect-id = <0xF>;
@@ -1764,6 +2108,9 @@
 
 		/* GPIO output to mss */
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "mss-pil";
 		qcom,mba-mem@0 {
 			compatible = "qcom,pil-mba-mem";
 			memory-region = <&pil_mba_mem>;
@@ -1815,6 +2162,7 @@
 		qcom,sysmon-id = <7>;
 		qcom,ssctl-instance-id = <0x17>;
 		qcom,firmware-name = "cdsp";
+		qcom,signal-aop;
 		memory-region = <&pil_cdsp_mem>;
 
 		/* GPIO inputs from turing */
@@ -1825,9 +2173,34 @@
 
 		/* GPIO output to turing*/
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_5_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "cdsp-pil";
 		status = "ok";
 	};
 
+	sdcc1_ice: sdcc1ice@7c8000 {
+		compatible = "qcom,ice";
+		reg = <0x7c8000 0x8000>;
+		qcom,enable-ice-clk;
+		clock-names = "ice_core_clk_src", "ice_core_clk",
+				"bus_clk", "iface_clk";
+		clocks = <&clock_gcc GCC_SDCC1_ICE_CORE_CLK_SRC>,
+			<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>,
+			<&clock_gcc GCC_SDCC1_APPS_CLK>,
+			<&clock_gcc GCC_SDCC1_AHB_CLK>;
+		qcom,op-freq-hz = <300000000>, <0>, <0>, <0>;
+		qcom,msm-bus,name = "sdcc_ice_noc";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+			<150 512 0 0>,    /* No vote */
+			<150 512 1000 0>; /* Max. bandwidth */
+		qcom,bus-vector-names = "MIN",
+					"MAX";
+		qcom,instance-type = "sdcc";
+	};
+
 	sdhc_1: sdhci@7c4000 {
 		compatible = "qcom,sdhci-msm-v5";
 		reg = <0x7C4000 0x1000>, <0x7C5000 0x1000>;
@@ -1838,10 +2211,64 @@
 
 		qcom,bus-width = <8>;
 		qcom,large-address-bus;
+		sdhc-msm-crypto = <&sdcc1_ice>;
+
+		qcom,clk-rates = <400000 20000000 25000000 50000000 100000000
+						192000000 384000000>;
+		qcom,bus-speed-mode = "HS400_1p8v", "HS200_1p8v", "DDR_1p8v";
+
+		qcom,devfreq,freq-table = <50000000 200000000>;
+
+		qcom,msm-bus,name = "sdhc1";
+		qcom,msm-bus,num-cases = <9>;
+		qcom,msm-bus,num-paths = <2>;
+		qcom,msm-bus,vectors-KBps =
+			/* No vote */
+			<150 512 0 0>, <1 782 0 0>,
+			/* 400 KB/s*/
+			<150 512 1046 1600>,
+			<1 782 1600 1600>,
+			/* 20 MB/s */
+			<150 512 52286 80000>,
+			<1 782 80000 80000>,
+			/* 25 MB/s */
+			<150 512 65360 100000>,
+			<1 782 100000 100000>,
+			/* 50 MB/s */
+			<150 512 130718 200000>,
+			<1 782 100000 100000>,
+			/* 100 MB/s */
+			<150 512 130718 200000>,
+			<1 782 130000 130000>,
+			/* 200 MB/s */
+			<150 512 261438 400000>,
+			<1 782 300000 300000>,
+			/* 400 MB/s */
+			<150 512 261438 400000>,
+			<1 782 300000 300000>,
+			/* Max. bandwidth */
+			<150 512 1338562 4096000>,
+			<1 782 1338562 4096000>;
+		qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+			100000000 200000000 400000000 4294967295>;
+
+		/* PM QoS */
+		qcom,pm-qos-irq-type = "affine_irq";
+		qcom,pm-qos-irq-latency = <70 70>;
+		qcom,pm-qos-cpu-groups = <0x3f 0xc0>;
+		qcom,pm-qos-cmdq-latency-us = <70 70>, <70 70>;
+		qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
 
 		clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
-			<&clock_gcc GCC_SDCC1_APPS_CLK>;
-		clock-names = "iface_clk", "core_clk";
+			<&clock_gcc GCC_SDCC1_APPS_CLK>,
+			<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>,
+			<&clock_gcc GCC_AGGRE_UFS_PHY_AXI_CLK>;
+		clock-names = "iface_clk", "core_clk", "ice_core_clk",
+			"bus_aggr_clk";
+
+		qcom,ice-clk-rates = <300000000 75000000>;
+
+		qcom,ddr-config = <0xC3040873>;
 
 		qcom,nonremovable;
 
@@ -1860,6 +2287,49 @@
 		qcom,bus-width = <4>;
 		qcom,large-address-bus;
 
+		qcom,clk-rates = <400000 20000000 25000000
+					50000000 100000000 201500000>;
+		qcom,bus-speed-mode = "SDR12", "SDR25", "SDR50", "DDR50",
+				      "SDR104";
+
+		qcom,devfreq,freq-table = <50000000 201500000>;
+
+		qcom,msm-bus,name = "sdhc2";
+		qcom,msm-bus,num-cases = <8>;
+		qcom,msm-bus,num-paths = <2>;
+		qcom,msm-bus,vectors-KBps =
+			/* No vote */
+			<81 512 0 0>, <1 608 0 0>,
+			/* 400 KB/s*/
+			<81 512 1046 1600>,
+			<1 608 1600 1600>,
+			/* 20 MB/s */
+			<81 512 52286 80000>,
+			<1 608 80000 80000>,
+			/* 25 MB/s */
+			<81 512 65360 100000>,
+			<1 608 100000 100000>,
+			/* 50 MB/s */
+			<81 512 130718 200000>,
+			<1 608 100000 100000>,
+			/* 100 MB/s */
+			<81 512 261438 200000>,
+			<1 608 130000 130000>,
+			/* 200 MB/s */
+			<81 512 261438 400000>,
+			<1 608 300000 300000>,
+			/* Max. bandwidth */
+			<81 512 1338562 4096000>,
+			<1 608 1338562 4096000>;
+		qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
+			100000000 200000000 4294967295>;
+
+		/* PM QoS */
+		qcom,pm-qos-irq-type = "affine_irq";
+		qcom,pm-qos-irq-latency = <70 70>;
+		qcom,pm-qos-cpu-groups = <0x3f 0xc0>;
+		qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
+
 		clocks = <&clock_gcc GCC_SDCC2_AHB_CLK>,
 			<&clock_gcc GCC_SDCC2_APPS_CLK>;
 		clock-names = "iface_clk", "core_clk";
@@ -1948,9 +2418,313 @@
 			iommus = <&apps_smmu 0x1805 0x0>;
 			dma-coherent;
 		};
+		qcom,msm_fastrpc_compute_cb12 {
+			compatible = "qcom,msm-fastrpc-compute-cb";
+			label = "adsprpc-smd";
+			iommus = <&apps_smmu 0x1806 0x0>;
+			dma-coherent;
+		};
+	};
+
+	bluetooth: bt_wcn3990 {
+		compatible = "qca,wcn3990";
+		qca,bt-vdd-core-supply = <&pm660_l9>;
+		qca,bt-vdd-pa-supply = <&pm660_l6>;
+		qca,bt-vdd-ldo-supply = <&pm660_l19>;
+
+		qca,bt-vdd-core-voltage-level = <1800000 1900000>;
+		qca,bt-vdd-pa-voltage-level = <1304000 1370000>;
+		qca,bt-vdd-ldo-voltage-level = <3312000 3400000>;
+
+		qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+	};
+
+	qcom,icnss@18800000 {
+		compatible = "qcom,icnss";
+		reg = <0x18800000 0x800000>,
+		      <0xa0000000 0x10000000>,
+		      <0xb0000000 0x10000>;
+		reg-names = "membase", "smmu_iova_base", "smmu_iova_ipa";
+		iommus = <&apps_smmu 0x0040 0x1>;
+		interrupts = <0 414 0 /* CE0 */ >,
+			     <0 415 0 /* CE1 */ >,
+			     <0 416 0 /* CE2 */ >,
+			     <0 417 0 /* CE3 */ >,
+			     <0 418 0 /* CE4 */ >,
+			     <0 419 0 /* CE5 */ >,
+			     <0 420 0 /* CE6 */ >,
+			     <0 421 0 /* CE7 */ >,
+			     <0 422 0 /* CE8 */ >,
+			     <0 423 0 /* CE9 */ >,
+			     <0 424 0 /* CE10 */ >,
+			     <0 425 0 /* CE11 */ >;
+		vdd-0.8-cx-mx-supply = <&pm660_l5>;
+		vdd-1.8-xo-supply = <&pm660_l9>;
+		vdd-1.3-rfa-supply = <&pm660_l6>;
+		vdd-3.3-ch0-supply = <&pm660_l19>;
+		qcom,wlan-msa-memory = <0x100000>;
+		qcom,smmu-s1-bypass;
+	};
+
+	cpubw: qcom,cpubw {
+		compatible = "qcom,devbw";
+		governor = "performance";
+		qcom,src-dst-ports =
+			<MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_EBI_CH0>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	bwmon: qcom,cpu-bwmon {
+		compatible = "qcom,bimc-bwmon4";
+		reg = <0x1436400 0x300>, <0x1436300 0x200>;
+		reg-names = "base", "global_base";
+		interrupts = <0 581 4>;
+		qcom,mport = <0>;
+		qcom,count-unit = <0x10000>;
+		qcom,hw-timer-hz = <19200000>;
+		qcom,target-dev = <&cpubw>;
+		qcom,byte-mid-mask = <0xe000>;
+		qcom,byte-mid-match = <0xe000>;
+	};
+
+	memlat_cpu0: qcom,memlat-cpu0 {
+		compatible = "qcom,devbw";
+		governor = "powersave";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	memlat_cpu6: qcom,memlat-cpu6 {
+		compatible = "qcom,devbw";
+		governor = "powersave";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		status = "ok";
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	devfreq_memlat_0: qcom,cpu0-memlat-mon {
+		compatible = "qcom,arm-memlat-mon";
+		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
+		qcom,target-dev = <&memlat_cpu0>;
+		qcom,cachemiss-ev = <0x2a>;
+		qcom,core-dev-table =
+			<  748800 MHZ_TO_MBPS( 300, 4) >,
+			<  998400 MHZ_TO_MBPS( 451, 4) >,
+			< 1209600 MHZ_TO_MBPS( 547, 4) >,
+			< 1516800 MHZ_TO_MBPS( 768, 4) >,
+			< 1708000 MHZ_TO_MBPS(1017, 4) >;
+	};
+
+	devfreq_memlat_6: qcom,cpu6-memlat-mon {
+		compatible = "qcom,arm-memlat-mon";
+		qcom,cpulist = <&CPU6 &CPU7>;
+		qcom,target-dev = <&memlat_cpu6>;
+		qcom,cachemiss-ev = <0x2a>;
+		qcom,core-dev-table =
+			<  825600 MHZ_TO_MBPS( 300, 4) >,
+			< 1132800 MHZ_TO_MBPS( 547, 4) >,
+			< 1363200 MHZ_TO_MBPS(1017, 4) >,
+			< 1996800 MHZ_TO_MBPS(1555, 4) >,
+			< 2457600 MHZ_TO_MBPS(1804, 4) >;
+	};
+
+	l3_cpu0: qcom,l3-cpu0 {
+		compatible = "devfreq-simple-dev";
+		clock-names = "devfreq_clk";
+		clocks = <&clock_cpucc L3_CLUSTER0_VOTE_CLK>;
+		governor = "performance";
+	};
+
+	l3_cpu6: qcom,l3-cpu6 {
+		compatible = "devfreq-simple-dev";
+		clock-names = "devfreq_clk";
+		clocks = <&clock_cpucc L3_CLUSTER1_VOTE_CLK>;
+		governor = "performance";
+	};
+
+	devfreq_l3lat_0: qcom,cpu0-l3lat-mon {
+		compatible = "qcom,arm-memlat-mon";
+		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
+		qcom,target-dev = <&l3_cpu0>;
+		qcom,cachemiss-ev = <0x17>;
+		qcom,core-dev-table =
+			<  576000  300000000 >,
+			<  748800  556800000 >,
+			<  998400  806400000 >,
+			< 1209660  940800000 >,
+			< 1516800 1190400000 >,
+			< 1612800 1382400000 >,
+			< 1708000 1440000000 >;
+	};
+
+	devfreq_l3lat_6: qcom,cpu6-l3lat-mon {
+		compatible = "qcom,arm-memlat-mon";
+		qcom,cpulist = <&CPU6 &CPU7>;
+		qcom,target-dev = <&l3_cpu6>;
+		qcom,cachemiss-ev = <0x17>;
+		qcom,core-dev-table =
+			< 1132800  556800000 >,
+			< 1363200  806400000 >,
+			< 1747200  940800000 >,
+			< 1996800 1190400000 >,
+			< 2457600 1440000000 >;
+	};
+
+	mincpubw: qcom,mincpubw {
+		compatible = "qcom,devbw";
+		governor = "powersave";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	devfreq-cpufreq {
+		mincpubw-cpufreq {
+			target-dev = <&mincpubw>;
+			cpu-to-dev-map-0 =
+				<  748800 MHZ_TO_MBPS( 300, 4) >,
+				< 1209660 MHZ_TO_MBPS( 451, 4) >,
+				< 1612800 MHZ_TO_MBPS( 547, 4) >,
+				< 1708000 MHZ_TO_MBPS( 768, 4) >;
+			cpu-to-dev-map-6 =
+				< 1132800 MHZ_TO_MBPS( 300, 4) >,
+				< 1363200 MHZ_TO_MBPS( 547, 4) >,
+				< 1747200 MHZ_TO_MBPS( 768, 4) >,
+				< 1996800 MHZ_TO_MBPS(1017, 4) >,
+				< 2457600 MHZ_TO_MBPS(1804, 4) >;
+		};
+	};
+
+	mincpu0bw: qcom,mincpu0bw {
+		compatible = "qcom,devbw";
+		governor = "powersave";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	mincpu6bw: qcom,mincpu6bw {
+		compatible = "qcom,devbw";
+		governor = "powersave";
+		qcom,src-dst-ports = <1 512>;
+		qcom,active-only;
+		qcom,bw-tbl =
+			< MHZ_TO_MBPS( 100, 4) >, /* 381 MB/s */
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1353, 4) >, /* 5161 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
+	};
+
+	devfreq_compute0: qcom,devfreq-compute0 {
+		compatible = "qcom,arm-cpu-mon";
+		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
+		qcom,target-dev = <&mincpu0bw>;
+		qcom,core-dev-table =
+				<  748800 MHZ_TO_MBPS( 300, 4) >,
+				< 1209660 MHZ_TO_MBPS( 451, 4) >,
+				< 1612800 MHZ_TO_MBPS( 547, 4) >,
+				< 1708000 MHZ_TO_MBPS( 768, 4) >;
+	};
+
+	devfreq_compute6: qcom,devfreq-compute6 {
+		compatible = "qcom,arm-cpu-mon";
+		qcom,cpulist = <&CPU6 &CPU7>;
+		qcom,target-dev = <&mincpu6bw>;
+		qcom,core-dev-table =
+				< 1132800 MHZ_TO_MBPS( 300, 4) >,
+				< 1363200 MHZ_TO_MBPS( 547, 4) >,
+				< 1747200 MHZ_TO_MBPS( 768, 4) >,
+				< 1996800 MHZ_TO_MBPS(1017, 4) >,
+				< 2457600 MHZ_TO_MBPS(1804, 4) >;
+	};
+
+	cpu_pmu: cpu-pmu {
+		compatible = "arm,armv8-pmuv3";
+		qcom,irq-is-percpu;
+		interrupts = <1 5 4>;
+	};
+
+	gpu_gx_domain_addr: syscon@0x5091508 {
+		compatible = "syscon";
+		reg = <0x5091508 0x4>;
+	};
+
+	gpu_gx_sw_reset: syscon@0x5091008 {
+		compatible = "syscon";
+		reg = <0x5091008 0x4>;
 	};
 };
 
+#include "pm660.dtsi"
+#include "pm660l.dtsi"
+#include "sdm670-regulator.dtsi"
 #include "sdm670-pinctrl.dtsi"
 #include "msm-arm-smmu-sdm670.dtsi"
 #include "msm-gdsc-sdm845.dtsi"
@@ -2014,6 +2788,8 @@
 
 &mdss_core_gdsc {
 	status = "ok";
+	proxy-supply = <&mdss_core_gdsc>;
+	qcom,proxy-consumer-enable;
 };
 
 &gpu_cx_gdsc {
@@ -2025,6 +2801,9 @@
 	clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK_SRC>;
 	qcom,force-enable-root-clk;
 	parent-supply = <&pm660l_s2_level>;
+	domain-addr = <&gpu_gx_domain_addr>;
+	sw-reset = <&gpu_gx_sw_reset>;
+	qcom,reset-aon-logic;
 	status = "ok";
 };
 
@@ -2042,8 +2821,61 @@
 	status = "ok";
 };
 
-#include "pm660.dtsi"
-#include "pm660l.dtsi"
-#include "sdm670-regulator.dtsi"
+&mdss_dsi0 {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
+};
+
+&mdss_dsi1 {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
+};
+
+&sde_dp {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
+};
+
 #include "sdm670-audio.dtsi"
 #include "sdm670-usb.dtsi"
+#include "sdm670-gpu.dtsi"
+#include "sdm670-camera.dtsi"
+#include "sdm670-thermal.dtsi"
+#include "sdm670-bus.dtsi"
+
+&pm660_div_clk {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-qrd-overlay.dts
index c6622d4..f9c6f65 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-4k-panel-qrd-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-4k-panel-qrd-overlay.dts
@@ -62,3 +62,7 @@
 &dsi_sharp_4k_dsc_video_display {
 	qcom,dsi-display-active;
 };
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi
index 9bd1d54..9d485b5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-audio-overlay.dtsi
@@ -47,6 +47,8 @@
 
 	qcom,msm-mbhc-hphl-swh = <1>;
 	qcom,msm-mbhc-gnd-swh = <1>;
+	qcom,msm-mbhc-hs-mic-max-threshold-mv = <1700>;
+	qcom,msm-mbhc-hs-mic-min-threshold-mv = <50>;
 	qcom,hph-en0-gpio = <&tavil_hph_en0>;
 	qcom,hph-en1-gpio = <&tavil_hph_en1>;
 	qcom,tavil-mclk-clk-freq = <9600000>;
@@ -175,7 +177,7 @@
 			compatible = "qcom,wcd-spi-v2";
 			qcom,master-bus-num = <0>;
 			qcom,chip-select = <0>;
-			qcom,max-frequency = <9600000>;
+			qcom,max-frequency = <24000000>;
 			qcom,mem-base-addr = <0x100000>;
 		};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
index 5fbb1db..cf7ccae 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-bus.dtsi
@@ -645,7 +645,10 @@
 			qcom,qport = <2>;
 			qcom,connections = <&slv_qns_a2noc_snoc>;
 			qcom,bus-dev = <&fab_aggre2_noc>;
+			qcom,ap-owned;
 			qcom,prio = <2>;
+			qcom,defer-init-qos;
+			qcom,node-qos-bcms = <7035 0 1>;
 		};
 
 		mas_xm_pcie3_1: mas-xm-pcie3-1 {
@@ -900,7 +903,7 @@
 			qcom,bus-dev = <&fab_mem_noc>;
 			qcom,bcms = <&bcm_sh3>;
 			qcom,ap-owned;
-			qcom,prio = <6>;
+			qcom,prio = <7>;
 		};
 
 		mas_qhm_memnoc_cfg: mas-qhm-memnoc-cfg {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
index 9d799cb..d8a6dc3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
@@ -22,10 +22,20 @@
 		status = "ok";
 	};
 
-	led_flash_front: qcom,camera-flash@1 {
+	led_flash_rear_aux: qcom,camera-flash@1 {
 		cell-index = <1>;
 		reg = <0x01 0x00>;
 		compatible = "qcom,camera-flash";
+		flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+		torch-source = <&pmi8998_torch0 &pmi8998_torch1 >;
+		switch-source = <&pmi8998_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@2 {
+		cell-index = <2>;
+		reg = <0x02 0x00>;
+		compatible = "qcom,camera-flash";
 		flash-source = <&pmi8998_flash2>;
 		torch-source = <&pmi8998_torch2>;
 		switch-source = <&pmi8998_switch1>;
@@ -74,6 +84,11 @@
 };
 
 &cam_cci {
+	qcom,cam-res-mgr {
+		compatible = "qcom,cam-res-mgr";
+		status = "ok";
+	};
+
 	actuator_rear: qcom,actuator@0 {
 		cell-index = <0>;
 		reg = <0x0>;
@@ -87,7 +102,7 @@
 		rgltr-load-current = <0>;
 	};
 
-	actuator_front: qcom,actuator@1 {
+	actuator_rear_aux: qcom,actuator@1 {
 		cell-index = <1>;
 		reg = <0x1>;
 		compatible = "qcom,actuator";
@@ -100,6 +115,19 @@
 		rgltr-load-current = <0>;
 	};
 
+	actuator_front: qcom,actuator@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
 	ois_rear: qcom,ois@0 {
 		cell-index = <0>;
 		reg = <0x0>;
@@ -111,7 +139,7 @@
 		rgltr-min-voltage = <2800000>;
 		rgltr-max-voltage = <2800000>;
 		rgltr-load-current = <0>;
-		status = "disabled";
+		status = "ok";
 	};
 
 	eeprom_rear: qcom,eeprom@0 {
@@ -122,12 +150,13 @@
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_vdig-supply = <&camera_rear_ldo>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <0 3312000 1050000 0>;
-		rgltr-max-voltage = <0 3600000 1050000 0>;
-		rgltr-load-current = <0 80000 105000 0>;
+		rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+		rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk0_active
@@ -136,17 +165,14 @@
 				&cam_sensor_rear_suspend>;
 		gpios = <&tlmm 13 0>,
 			<&tlmm 80 0>,
-			<&tlmm 79 0>,
-			<&tlmm 27 0>;
+			<&tlmm 79 0>;
 		gpio-reset = <1>;
 		gpio-vana = <2>;
-		gpio-vaf = <3>;
-		gpio-req-tbl-num = <0 1 2 3>;
-		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
 		gpio-req-tbl-label = "CAMIF_MCLK0",
 					"CAM_RESET0",
-					"CAM_VANA0",
-					"CAM_VAF";
+					"CAM_VANA0";
 		sensor-position = <0>;
 		sensor-mode = <0>;
 		cci-master = <0>;
@@ -165,12 +191,13 @@
 		cam_vio-supply = <&pm8998_lvs1>;
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <1050000 0 3312000 0>;
-		rgltr-max-voltage = <1050000 0 3600000 0>;
-		rgltr-load-current = <105000 0 80000 0>;
+		rgltr-min-voltage = <1050000 0 3312000 0 2800000>;
+		rgltr-max-voltage = <1050000 0 3600000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk2_active
@@ -205,12 +232,13 @@
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_vdig-supply = <&camera_ldo>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <0 3312000 1050000 0>;
-		rgltr-max-voltage = <0 3600000 1050000 0>;
-		rgltr-load-current = <0 80000 105000 0>;
+		rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+		rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk1_active
@@ -219,17 +247,14 @@
 				 &cam_sensor_front_suspend>;
 		gpios = <&tlmm 14 0>,
 			<&tlmm 28 0>,
-			<&tlmm 8 0>,
-			<&tlmm 27 0>;
+			<&tlmm 8 0>;
 		gpio-reset = <1>;
 		gpio-vana = <2>;
-		gpio-vaf = <3>;
-		gpio-req-tbl-num = <0 1 2 3>;
-		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
 		gpio-req-tbl-label = "CAMIF_MCLK2",
 					"CAM_RESET2",
-					"CAM_VANA2",
-					"CAM_VAF";
+					"CAM_VANA2";
 		sensor-position = <1>;
 		sensor-mode = <0>;
 		cci-master = <1>;
@@ -292,9 +317,11 @@
 		compatible = "qcom,cam-sensor";
 		reg = <0x1>;
 		csiphy-sd-index = <1>;
-		sensor-position-roll = <90>;
+		sensor-position-roll = <270>;
 		sensor-position-pitch = <0>;
 		sensor-position-yaw = <180>;
+		actuator-src = <&actuator_rear_aux>;
+		led-flash-src = <&led_flash_rear_aux>;
 		eeprom-src = <&eeprom_rear_aux>;
 		cam_vdig-supply = <&camera_ldo>;
 		cam_vio-supply = <&pm8998_lvs1>;
@@ -376,4 +403,47 @@
 		clock-cntl-level = "turbo";
 		clock-rates = <24000000>;
 	};
+
+	qcom,cam-sensor@3 {
+		cell-index = <3>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x03>;
+		csiphy-sd-index = <3>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk3_active
+				 &cam_sensor_iris_active>;
+		pinctrl-1 = <&cam_sensor_mclk3_suspend
+				 &cam_sensor_iris_suspend>;
+		gpios = <&tlmm 16 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK3",
+					"CAM_RESET3",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK3_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
index f18137c..952ba29 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
@@ -22,10 +22,20 @@
 		status = "ok";
 	};
 
-	led_flash_front: qcom,camera-flash@1 {
+	led_flash_rear_aux: qcom,camera-flash@1 {
 		cell-index = <1>;
 		reg = <0x01 0x00>;
 		compatible = "qcom,camera-flash";
+		flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+		torch-source = <&pmi8998_torch0 &pmi8998_torch1>;
+		switch-source = <&pmi8998_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@2 {
+		cell-index = <2>;
+		reg = <0x02 0x00>;
+		compatible = "qcom,camera-flash";
 		flash-source = <&pmi8998_flash2>;
 		torch-source = <&pmi8998_torch2>;
 		switch-source = <&pmi8998_switch1>;
@@ -74,6 +84,11 @@
 };
 
 &cam_cci {
+	qcom,cam-res-mgr {
+		compatible = "qcom,cam-res-mgr";
+		status = "ok";
+	};
+
 	actuator_rear: qcom,actuator@0 {
 		cell-index = <0>;
 		reg = <0x0>;
@@ -87,7 +102,7 @@
 		rgltr-load-current = <0>;
 	};
 
-	actuator_front: qcom,actuator@1 {
+	actuator_rear_aux: qcom,actuator@1 {
 		cell-index = <1>;
 		reg = <0x1>;
 		compatible = "qcom,actuator";
@@ -100,6 +115,19 @@
 		rgltr-load-current = <0>;
 	};
 
+	actuator_front: qcom,actuator@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
 	ois_rear: qcom,ois@0 {
 		cell-index = <0>;
 		reg = <0x0>;
@@ -111,7 +139,7 @@
 		rgltr-min-voltage = <2800000>;
 		rgltr-max-voltage = <2800000>;
 		rgltr-load-current = <0>;
-		status = "disabled";
+		status = "ok";
 	};
 
 	eeprom_rear: qcom,eeprom@0 {
@@ -122,12 +150,13 @@
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_vdig-supply = <&camera_rear_ldo>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <0 3312000 1050000 0>;
-		rgltr-max-voltage = <0 3600000 1050000 0>;
-		rgltr-load-current = <0 80000 105000 0>;
+		rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+		rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk0_active
@@ -136,17 +165,14 @@
 				&cam_sensor_rear_suspend>;
 		gpios = <&tlmm 13 0>,
 			<&tlmm 80 0>,
-			<&tlmm 79 0>,
-			<&tlmm 27 0>;
+			<&tlmm 79 0>;
 		gpio-reset = <1>;
 		gpio-vana = <2>;
-		gpio-vaf = <3>;
-		gpio-req-tbl-num = <0 1 2 3>;
-		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
 		gpio-req-tbl-label = "CAMIF_MCLK0",
 					"CAM_RESET0",
-					"CAM_VANA0",
-					"CAM_VAF";
+					"CAM_VANA0";
 		sensor-position = <0>;
 		sensor-mode = <0>;
 		cci-master = <0>;
@@ -165,12 +191,13 @@
 		cam_vio-supply = <&pm8998_lvs1>;
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <1050000 0 3312000 0>;
-		rgltr-max-voltage = <1050000 0 3600000 0>;
-		rgltr-load-current = <105000 0 80000 0>;
+		rgltr-min-voltage = <1050000 0 3312000 0 2800000>;
+		rgltr-max-voltage = <1050000 0 3600000 0 2800000>;
+		rgltr-load-current = <105000 0 80000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk2_active
@@ -205,12 +232,13 @@
 		cam_vana-supply = <&pmi8998_bob>;
 		cam_vdig-supply = <&camera_ldo>;
 		cam_clk-supply = <&titan_top_gdsc>;
+		cam_vaf-supply = <&actuator_regulator>;
 		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
-			"cam_clk";
+			"cam_clk", "cam_vaf";
 		rgltr-cntrl-support;
-		rgltr-min-voltage = <0 3312000 1050000 0>;
-		rgltr-max-voltage = <0 3600000 1050000 0>;
-		rgltr-load-current = <0 80000 105000 0>;
+		rgltr-min-voltage = <0 3312000 1050000 0 2800000>;
+		rgltr-max-voltage = <0 3600000 1050000 0 2800000>;
+		rgltr-load-current = <0 80000 105000 0 0>;
 		gpio-no-mux = <0>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cam_sensor_mclk1_active
@@ -219,17 +247,14 @@
 				 &cam_sensor_front_suspend>;
 		gpios = <&tlmm 14 0>,
 			<&tlmm 28 0>,
-			<&tlmm 8 0>,
-			<&tlmm 27 0>;
+			<&tlmm 8 0>;
 		gpio-reset = <1>;
 		gpio-vana = <2>;
-		gpio-vaf = <3>;
-		gpio-req-tbl-num = <0 1 2 3>;
-		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
 		gpio-req-tbl-label = "CAMIF_MCLK2",
 					"CAM_RESET2",
-					"CAM_VANA2",
-					"CAM_VAF";
+					"CAM_VANA2";
 		sensor-position = <1>;
 		sensor-mode = <0>;
 		cci-master = <1>;
@@ -292,9 +317,11 @@
 		compatible = "qcom,cam-sensor";
 		reg = <0x1>;
 		csiphy-sd-index = <1>;
-		sensor-position-roll = <90>;
+		sensor-position-roll = <270>;
 		sensor-position-pitch = <0>;
 		sensor-position-yaw = <180>;
+		actuator-src = <&actuator_rear_aux>;
+		led-flash-src = <&led_flash_rear_aux>;
 		eeprom-src = <&eeprom_rear_aux>;
 		cam_vdig-supply = <&camera_ldo>;
 		cam_vio-supply = <&pm8998_lvs1>;
@@ -376,4 +403,47 @@
 		clock-cntl-level = "turbo";
 		clock-rates = <24000000>;
 	};
+	qcom,cam-sensor@3 {
+		cell-index = <3>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x03>;
+		csiphy-sd-index = <3>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk3_active
+				 &cam_sensor_iris_active>;
+		pinctrl-1 = <&cam_sensor_mclk3_suspend
+				 &cam_sensor_iris_suspend>;
+		gpios = <&tlmm 16 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK3",
+					"CAM_RESET3",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK3_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-qvr.dtsi
new file mode 100644
index 0000000..8ad5f3c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-qvr.dtsi
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	led_flash_rear: qcom,camera-flash@0 {
+		cell-index = <0>;
+		reg = <0x00 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pmi8998_flash0 &pmi8998_flash1>;
+		torch-source = <&pmi8998_torch0 &pmi8998_torch1>;
+		switch-source = <&pmi8998_switch0>;
+		status = "ok";
+	};
+
+	led_flash_front: qcom,camera-flash@1 {
+		cell-index = <1>;
+		reg = <0x01 0x00>;
+		compatible = "qcom,camera-flash";
+		flash-source = <&pmi8998_flash2>;
+		torch-source = <&pmi8998_torch2>;
+		switch-source = <&pmi8998_switch1>;
+		status = "ok";
+	};
+
+	actuator_regulator: gpio-regulator@0 {
+		compatible = "regulator-fixed";
+		reg = <0x00 0x00>;
+		regulator-name = "actuator_regulator";
+		regulator-min-microvolt = <2800000>;
+		regulator-max-microvolt = <2800000>;
+		regulator-enable-ramp-delay = <100>;
+		enable-active-high;
+		gpio = <&tlmm 27 0>;
+		vin-supply = <&pmi8998_bob>;
+	};
+
+	camera_rear_ldo: gpio-regulator@1 {
+		compatible = "regulator-fixed";
+		reg = <0x01 0x00>;
+		regulator-name = "camera_rear_ldo";
+		regulator-min-microvolt = <1050000>;
+		regulator-max-microvolt = <1050000>;
+		regulator-enable-ramp-delay = <135>;
+		enable-active-high;
+		gpio = <&pm8998_gpios 12 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_rear_dvdd_en_default>;
+		vin-supply = <&pm8998_s3>;
+	};
+
+	camera_ldo: gpio-regulator@2 {
+		compatible = "regulator-fixed";
+		reg = <0x02 0x00>;
+		regulator-name = "camera_ldo";
+		regulator-min-microvolt = <1050000>;
+		regulator-max-microvolt = <1050000>;
+		regulator-enable-ramp-delay = <233>;
+		enable-active-high;
+		gpio = <&pm8998_gpios 9 0>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&camera_dvdd_en_default>;
+		vin-supply = <&pm8998_s3>;
+	};
+};
+
+&cam_cci {
+	actuator_rear: qcom,actuator@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,actuator";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	actuator_rear_aux: qcom,actuator@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,actuator";
+		cci-master = <1>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+	};
+
+	ois_rear: qcom,ois@0 {
+		cell-index = <0>;
+		reg = <0x0>;
+		compatible = "qcom,ois";
+		cci-master = <0>;
+		cam_vaf-supply = <&actuator_regulator>;
+		regulator-names = "cam_vaf";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <2800000>;
+		rgltr-max-voltage = <2800000>;
+		rgltr-load-current = <0>;
+		status = "disabled";
+	};
+
+	eeprom_rear: qcom,eeprom@0 {
+		cell-index = <0>;
+		reg = <0>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 80 0>,
+			<&tlmm 79 0>,
+			<&tlmm 27 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-vaf = <3>;
+		gpio-req-tbl-num = <0 1 2 3>;
+		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0",
+					"CAM_VANA0",
+					"CAM_VAF";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_rear_aux: qcom,eeprom@1 {
+		cell-index = <1>;
+		reg = <0x1>;
+		compatible = "qcom,eeprom";
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>,
+			<&tlmm 8 0>,
+			<&tlmm 27 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-vaf = <3>;
+		gpio-req-tbl-num = <0 1 2 3>;
+		gpio-req-tbl-flags = <1 0 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1",
+					"CAM_VANA1",
+					"CAM_VAF";
+		sensor-position = <0>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	eeprom_front: qcom,eeprom@2 {
+		cell-index = <2>;
+		reg = <0x2>;
+		compatible = "qcom,eeprom";
+		cam_vdig-supply = <&camera_ldo>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1050000 0 3312000 0>;
+		rgltr-max-voltage = <1050000 0 3600000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				 &cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				 &cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2",
+					"CAM_VANA2";
+		sensor-position = <1>;
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@0 {
+		cell-index = <0>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x0>;
+		csiphy-sd-index = <0>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		led-flash-src = <&led_flash_rear>;
+		actuator-src = <&actuator_rear>;
+		ois-src = <&ois_rear>;
+		eeprom-src = <&eeprom_rear>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_rear_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk0_active
+				&cam_sensor_rear_active>;
+		pinctrl-1 = <&cam_sensor_mclk0_suspend
+				&cam_sensor_rear_suspend>;
+		gpios = <&tlmm 13 0>,
+			<&tlmm 80 0>,
+			<&tlmm 79 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK0",
+					"CAM_RESET0",
+					"CAM_VANA";
+		sensor-mode = <0>;
+		cci-master = <0>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK0_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@1 {
+		cell-index = <1>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x1>;
+		csiphy-sd-index = <1>;
+		sensor-position-roll = <90>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <180>;
+		eeprom-src = <&eeprom_rear_aux>;
+		actuator-src = <&actuator_rear_aux>;
+		led-flash-src = <&led_flash_front>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vio", "cam_vana", "cam_vdig",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <0 3312000 1050000 0>;
+		rgltr-max-voltage = <0 3600000 1050000 0>;
+		rgltr-load-current = <0 80000 105000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk1_active
+				 &cam_sensor_front_active>;
+		pinctrl-1 = <&cam_sensor_mclk1_suspend
+				 &cam_sensor_front_suspend>;
+		gpios = <&tlmm 14 0>,
+			<&tlmm 28 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK2",
+					"CAM_RESET2",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK1_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+
+	qcom,cam-sensor@2 {
+		cell-index = <2>;
+		compatible = "qcom,cam-sensor";
+		reg = <0x02>;
+		csiphy-sd-index = <2>;
+		sensor-position-roll = <270>;
+		sensor-position-pitch = <0>;
+		sensor-position-yaw = <0>;
+		eeprom-src = <&eeprom_front>;
+		cam_vdig-supply = <&camera_ldo>;
+		cam_vio-supply = <&pm8998_lvs1>;
+		cam_vana-supply = <&pmi8998_bob>;
+		cam_clk-supply = <&titan_top_gdsc>;
+		regulator-names = "cam_vdig", "cam_vio", "cam_vana",
+			"cam_clk";
+		rgltr-cntrl-support;
+		rgltr-min-voltage = <1050000 0 3312000 0>;
+		rgltr-max-voltage = <1050000 0 3600000 0>;
+		rgltr-load-current = <105000 0 80000 0>;
+		gpio-no-mux = <0>;
+		pinctrl-names = "cam_default", "cam_suspend";
+		pinctrl-0 = <&cam_sensor_mclk2_active
+				&cam_sensor_rear2_active>;
+		pinctrl-1 = <&cam_sensor_mclk2_suspend
+				&cam_sensor_rear2_suspend>;
+		gpios = <&tlmm 15 0>,
+			<&tlmm 9 0>,
+			<&tlmm 8 0>;
+		gpio-reset = <1>;
+		gpio-vana = <2>;
+		gpio-req-tbl-num = <0 1 2>;
+		gpio-req-tbl-flags = <1 0 0>;
+		gpio-req-tbl-label = "CAMIF_MCLK1",
+					"CAM_RESET1",
+					"CAM_VANA1";
+		sensor-mode = <0>;
+		cci-master = <1>;
+		status = "ok";
+		clocks = <&clock_camcc CAM_CC_MCLK2_CLK>;
+		clock-names = "cam_clk";
+		clock-cntl-level = "turbo";
+		clock-rates = <24000000>;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 8df879a..35a7774 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
@@ -28,7 +28,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		regulator-names = "gdscr";
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -45,9 +45,10 @@
 			"csiphy0_clk",
 			"csi0phytimer_clk_src",
 			"csi0phytimer_clk";
-		clock-cntl-level = "turbo";
+		clock-cntl-level = "svs", "turbo";
 		clock-rates =
-			<0 0 0 0 320000000 0 269333333 0>;
+			<0 0 0 0 320000000 0 269333333 0>,
+			<0 0 0 0 384000000 0 269333333 0>;
 		status = "ok";
 	};
 
@@ -62,7 +63,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		regulator-names = "gdscr";
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -79,9 +80,10 @@
 			"csiphy1_clk",
 			"csi1phytimer_clk_src",
 			"csi1phytimer_clk";
-		clock-cntl-level = "turbo";
+		clock-cntl-level = "svs", "turbo";
 		clock-rates =
-			<0 0 0 0 320000000 0 269333333 0>;
+			<0 0 0 0 320000000 0 269333333 0>,
+			<0 0 0 0 384000000 0 269333333 0>;
 
 		status = "ok";
 	};
@@ -97,7 +99,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		regulator-names = "gdscr";
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -114,9 +116,10 @@
 			"csiphy2_clk",
 			"csi2phytimer_clk_src",
 			"csi2phytimer_clk";
-		clock-cntl-level = "turbo";
+		clock-cntl-level = "svs", "turbo";
 		clock-rates =
-			<0 0 0 0 320000000 0 269333333 0>;
+			<0 0 0 0 320000000 0 269333333 0>,
+			<0 0 0 0 384000000 0 269333333 0>;
 		status = "ok";
 	};
 
@@ -146,7 +149,7 @@
 			"cci_clk",
 			"cci_clk_src";
 		src-clock-name = "cci_clk_src";
-		clock-cntl-level = "turbo";
+		clock-cntl-level = "lowsvs";
 		clock-rates = <0 0 0 0 0 37500000>;
 		pinctrl-names = "cam_default", "cam_suspend";
 		pinctrl-0 = <&cci0_active &cci1_active>;
@@ -296,11 +299,20 @@
 					status = "ok";
 				};
 
+				iova-mem-region-secondary-heap {
+					/* Secondary heap region is 1MB long */
+					iova-region-name = "secheap";
+					iova-region-start = <0xd800000>;
+					iova-region-len = <0x100000>;
+					iova-region-id = <0x4>;
+					status = "ok";
+				};
+
 				iova-mem-region-io {
 					/* IO region is approximately 3.3 GB */
 					iova-region-name = "io";
-					iova-region-start = <0xd800000>;
-					iova-region-len = <0xd2800000>;
+					iova-region-start = <0xd900000>;
+					iova-region-len = <0xd2700000>;
 					iova-region-id = <0x3>;
 					status = "ok";
 				};
@@ -325,18 +337,8 @@
 
 		msm_cam_smmu_secure {
 			compatible = "qcom,msm-cam-smmu-cb";
-			iommus = <&apps_smmu 0x1001 0x0>;
 			label = "cam-secure";
-			cam_secure_iova_mem_map: iova-mem-map {
-				/* Secure IO region is approximately 3.4 GB */
-				iova-mem-region-io {
-					iova-region-name = "io";
-					iova-region-start = <0x7400000>;
-					iova-region-len = <0xd8c00000>;
-					iova-region-id = <0x3>;
-					status = "ok";
-				};
-			};
+			qcom,secure-cb;
 		};
 
 		msm_cam_smmu_fd {
@@ -368,6 +370,7 @@
 		reg-cam-base = <0x40000 0x42000>;
 		interrupt-names = "cpas_camnoc";
 		interrupts = <0 459 0>;
+		qcom,cpas-hw-ver = <0x170100>; /* Titan v170 v1.0.0 */
 		regulator-names = "camss-vdd";
 		camss-vdd-supply = <&titan_top_gdsc>;
 		clock-names = "gcc_ahb_clk",
@@ -399,17 +402,17 @@
 			<MSM_BUS_MASTER_AMPSS_M0
 			MSM_BUS_SLAVE_CAMERA_CFG 0 0>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 180000>,
+			MSM_BUS_SLAVE_CAMERA_CFG 0 76500>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 180000>,
+			MSM_BUS_SLAVE_CAMERA_CFG 0 76500>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
 			<MSM_BUS_MASTER_AMPSS_M0
 			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>,
-			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 640000>,
-			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 640000>;
+			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>;
 		vdd-corners = <RPMH_REGULATOR_LEVEL_OFF
 			RPMH_REGULATOR_LEVEL_RETENTION
 			RPMH_REGULATOR_LEVEL_MIN_SVS
@@ -431,13 +434,14 @@
 			"csid0", "csid1", "csid2",
 			"ife0", "ife1", "ife2", "ipe0",
 			"ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0",
-			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0";
+			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0", "lrmecpas";
 		client-axi-port-names =
 			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
 			"cam_hf_1", "cam_hf_2", "cam_hf_2",
 			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
 			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
-			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1";
+			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
+			"cam_sf_1";
 		client-bus-camnoc-based;
 		qcom,axi-port-list {
 			qcom,axi-port1 {
@@ -526,7 +530,8 @@
 		cdm-client-names = "vfe",
 			"jpegdma",
 			"jpegenc",
-			"fd";
+			"fd",
+			"lrmecdm";
 		status = "ok";
 	};
 
@@ -600,8 +605,10 @@
 			<&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 500000000 0 0 0 600000000 0 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "turbo";
 		src-clock-name = "ife_csid_clk_src";
 		status = "ok";
 	};
@@ -635,12 +642,15 @@
 			<&clock_camcc CAM_CC_IFE_0_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_IFE_0_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 600000000 0 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 480000000 0 0>,
+			<0 0 0 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
 		src-clock-name = "ife_clk_src";
 		clock-names-option =  "ife_dsp_clk";
 		clocks-option = <&clock_camcc CAM_CC_IFE_0_DSP_CLK>;
-		clock-rates-option = <404000000>;
+		clock-rates-option = <600000000>;
 		status = "ok";
 	};
 
@@ -681,8 +691,10 @@
 			<&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 500000000 0 0 0 600000000 0 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "turbo";
 		src-clock-name = "ife_csid_clk_src";
 		status = "ok";
 	};
@@ -716,12 +728,15 @@
 			<&clock_camcc CAM_CC_IFE_1_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_IFE_1_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 600000000 0 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0 0>,
+			<0 0 0 0 0 0 480000000 0 0>,
+			<0 0 0 0 0 0 600000000 0 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
 		src-clock-name = "ife_clk_src";
 		clock-names-option =  "ife_dsp_clk";
 		clocks-option = <&clock_camcc CAM_CC_IFE_1_DSP_CLK>;
-		clock-rates-option = <404000000>;
+		clock-rates-option = <600000000>;
 		status = "ok";
 	};
 
@@ -759,8 +774,10 @@
 			<&clock_camcc CAM_CC_IFE_LITE_CLK>,
 			<&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 384000000 0 0 0 404000000 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 384000000 0 0 0 404000000 0>,
+			<0 0 0 0 0 0 538000000 0 0 0 600000000 0>;
+		clock-cntl-level = "svs", "turbo";
 		src-clock-name = "ife_csid_clk_src";
 		status = "ok";
 	};
@@ -791,8 +808,11 @@
 			<&clock_camcc CAM_CC_IFE_LITE_CLK>,
 			<&clock_camcc CAM_CC_IFE_LITE_CLK_SRC>,
 			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>;
-		clock-rates = <0 0 0 0 0 0 404000000 0>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 0 0 0 0 404000000 0>,
+			<0 0 0 0 0 0 480000000 0>,
+			<0 0 0 0 0 0 600000000 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
 		src-clock-name = "ife_clk_src";
 		status = "ok";
 	};
@@ -827,7 +847,6 @@
 			"soc_ahb_clk",
 			"cpas_ahb_clk",
 			"camnoc_axi_clk",
-			"icp_apb_clk",
 			"icp_clk",
 			"icp_clk_src";
 		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
@@ -836,13 +855,15 @@
 				<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 				<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
 				<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
-				<&clock_camcc CAM_CC_ICP_APB_CLK>,
 				<&clock_camcc CAM_CC_ICP_CLK>,
 				<&clock_camcc CAM_CC_ICP_CLK_SRC>;
 
-		clock-rates = <0 0 400000000 0 0 0 0 0 600000000>;
-		clock-cntl-level = "turbo";
+		clock-rates =
+			<0 0 200000000 0 0 0 0 400000000>,
+			<0 0 200000000 0 0 0 0 600000000>;
+		clock-cntl-level = "svs", "turbo";
 		fw_name = "CAMERA_ICP.elf";
+		ubwc-cfg = <0x7F 0x1FF>;
 		status = "ok";
 	};
 
@@ -863,7 +884,8 @@
 				<&clock_camcc CAM_CC_IPE_0_CLK>,
 				<&clock_camcc CAM_CC_IPE_0_CLK_SRC>;
 
-		clock-rates = <0 0 0 0 240000000>,
+		clock-rates =
+			<0 0 0 0 240000000>,
 			<0 0 0 0 404000000>,
 			<0 0 0 0 480000000>,
 			<0 0 0 0 538000000>,
@@ -1033,8 +1055,11 @@
 			<&clock_camcc CAM_CC_FD_CORE_CLK>,
 			<&clock_camcc CAM_CC_FD_CORE_UAR_CLK>;
 		src-clock-name = "fd_core_clk_src";
-		clock-cntl-level = "svs";
-		clock-rates = <0 0 0 0 0 400000000 0 0>;
+		clock-cntl-level = "svs", "svs_l1", "turbo";
+		clock-rates =
+			<0 0 0 0 0 400000000 0 0>,
+			<0 0 0 0 0 538000000 0 0>,
+			<0 0 0 0 0 600000000 0 0>;
 		status = "ok";
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index dddf1fb..dffb5e0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -110,6 +110,7 @@
 	vdd-hba-supply = <&ufs_phy_gdsc>;
 	vdd-hba-fixed-regulator;
 	vcc-supply = <&pm8998_l20>;
+	vcc-voltage-level = <2950000 2960000>;
 	vccq2-supply = <&pm8998_s4>;
 	vcc-max-microamp = <600000>;
 	vccq2-max-microamp = <600000>;
@@ -120,44 +121,6 @@
 	status = "ok";
 };
 
-&extcon_storage_cd {
-	gpio = <&tlmm 126 GPIO_ACTIVE_LOW>;
-	debounce-ms = <200>;
-	irq-flags = <IRQ_TYPE_EDGE_BOTH>;
-
-	pinctrl-names = "default";
-	pinctrl-0 = <&storage_cd>;
-
-	status = "ok";
-};
-
-&ufsphy_card {
-	compatible = "qcom,ufs-phy-qmp-v3";
-
-	vdda-phy-supply = <&pm8998_l1>; /* 0.88v */
-	vdda-pll-supply = <&pm8998_l26>; /* 1.2v */
-	vdda-phy-max-microamp = <62900>;
-	vdda-pll-max-microamp = <18300>;
-
-	status = "ok";
-};
-
-&ufshc_card {
-	vdd-hba-supply = <&ufs_card_gdsc>;
-	vdd-hba-fixed-regulator;
-	vcc-supply = <&pm8998_l21>;
-	vccq2-supply = <&pm8998_s4>;
-	vcc-max-microamp = <300000>;
-	vccq2-max-microamp = <300000>;
-
-	qcom,vddp-ref-clk-supply = <&pm8998_l2>;
-	qcom,vddp-ref-clk-max-microamp = <100>;
-
-	extcon = <&extcon_storage_cd>;
-
-	status = "ok";
-};
-
 &sdhc_2 {
 	vdd-supply = <&pm8998_l21>;
 	qcom,vdd-voltage-level = <2950000 2960000>;
@@ -168,10 +131,10 @@
 	qcom,vdd-io-current-level = <200 22000>;
 
 	pinctrl-names = "active", "sleep";
-	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on>;
-	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
 
-	extcon = <&extcon_storage_cd>;
+	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 
 	status = "ok";
 };
@@ -306,6 +269,27 @@
 	qcom,platform-reset-gpio = <&tlmm 6 0>;
 };
 
+&dsi_dual_nt35597_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
 &dsi_nt35597_truly_dsc_cmd_display {
 	qcom,dsi-display-active;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
index c1fcb62..fcfab09 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-coresight.dtsi
@@ -12,6 +12,16 @@
 
 &soc {
 
+	csr: csr@6001000 {
+		compatible = "qcom,coresight-csr";
+		reg = <0x6001000 0x1000>;
+		reg-names = "csr-base";
+
+		coresight-name = "coresight-csr";
+
+		qcom,blk-size = <1>;
+	};
+
 	replicator_qdss: replicator@6046000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b909>;
@@ -271,6 +281,9 @@
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
+		interrupts = <GIC_SPI 270 IRQ_TYPE_EDGE_RISING>;
+		interrupt-names = "byte-cntr-irq";
+
 		port {
 			tmc_etr_in_replicator: endpoint {
 				slave-mode;
@@ -397,16 +410,6 @@
 		clock-names = "apb_pclk";
 	};
 
-	csr: csr@6001000 {
-		compatible = "qcom,coresight-csr";
-		reg = <0x6001000 0x1000>;
-		reg-names = "csr-base";
-
-		coresight-name = "coresight-csr";
-
-		qcom,blk-size = <1>;
-	};
-
 	funnel_in0: funnel@0x6041000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
@@ -522,6 +525,58 @@
 				};
 			};
 
+			port@5 {
+				reg = <6>;
+				funnel_in2_in_funnel_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&funnel_gfx_out_funnel_in2>;
+				};
+			};
+		};
+	};
+
+	funnel_gfx: funnel@0x6943000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b908>;
+
+		reg = <0x6943000 0x1000>;
+		reg-names = "funnel-base";
+
+		coresight-name = "coresight-funnel-gfx";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			port@0 {
+				reg = <0>;
+				funnel_gfx_out_funnel_in2: endpoint {
+					remote-endpoint =
+					  <&funnel_in2_in_funnel_gfx>;
+				};
+			};
+
+			port@1 {
+				reg = <0>;
+				funnel_in2_in_gfx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_out_funnel_in2>;
+				};
+			};
+
+			port@2 {
+				reg = <1>;
+				funnel_in2_in_gfx_cx: endpoint {
+					slave-mode;
+					remote-endpoint =
+					  <&gfx_cx_out_funnel_in2>;
+				};
+			};
 		};
 	};
 
@@ -547,6 +602,7 @@
 				     <13 32>;
 		qcom,cmb-elem-size = <3 64>,
 				     <7 64>,
+				     <9 64>,
 				     <13 64>;
 
 		clocks = <&clock_aop QDSS_CLK>;
@@ -619,6 +675,15 @@
 			};
 
 			port@7 {
+				reg = <9>;
+				tpda_in_tpdm_prng: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&tpdm_prng_out_tpda>;
+				};
+			};
+
+			port@8 {
 				reg = <10>;
 				tpda_in_tpdm_qm: endpoint {
 					slave-mode;
@@ -627,7 +692,7 @@
 				};
 			};
 
-			port@8 {
+			port@9 {
 				reg = <11>;
 				tpda_in_tpdm_north: endpoint {
 					slave-mode;
@@ -636,7 +701,7 @@
 				};
 			};
 
-			port@9 {
+			port@10 {
 				reg = <13>;
 				tpda_in_tpdm_pimem: endpoint {
 					slave-mode;
@@ -1274,6 +1339,24 @@
 		};
 	};
 
+	tpdm_prng: tpdm@684c000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b968>;
+		reg = <0x684c000 0x1000>;
+		reg-names = "tpdm-base";
+
+		coresight-name = "coresight-tpdm-prng";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+
+		port{
+			tpdm_prng_out_tpda: endpoint {
+				remote-endpoint = <&tpda_in_tpdm_prng>;
+			};
+		};
+	};
+
 	tpdm_vsense: tpdm@6840000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b968>;
@@ -1430,6 +1513,15 @@
 						<&tpda_spss_out_funnel_spss>;
 				};
 			};
+
+			port@2 {
+				reg = <1>;
+				funnel_spss_in_spss_etm0: endpoint {
+					slave-mode;
+					remote-endpoint =
+						<&spss_etm0_out_funnel_spss>;
+				};
+			};
 		};
 	};
 
@@ -1492,7 +1584,7 @@
 		reg = <0x69e1000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-ddr0";
+		coresight-name = "coresight-cti-ddr_dl_0_cti";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1504,7 +1596,7 @@
 		reg = <0x69e4000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-ddr1";
+		coresight-name = "coresight-cti-ddr_dl_1_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1516,7 +1608,7 @@
 		reg = <0x69e5000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-ddr1";
+		coresight-name = "coresight-cti-ddr_dl_1_cti1";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1528,7 +1620,7 @@
 		reg = <0x6c09000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-dlmm";
+		coresight-name = "coresight-cti-dlmm_cti0";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1540,7 +1632,43 @@
 		reg = <0x6c0a000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti1-dlmm";
+		coresight-name = "coresight-cti-dlmm_cti1";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
+
+	cti0_apss: cti@78e0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
+		reg = <0x78e0000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-apss_cti0";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
+
+	cti1_apss: cti@78f0000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
+		reg = <0x78f0000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-apss_cti1";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
+
+	cti2_apss: cti@7900000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b966>;
+		reg = <0x7900000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-apss_cti2";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1868,7 +1996,23 @@
 		reg = <0x6b04000 0x1000>;
 		reg-names = "cti-base";
 
-		coresight-name = "coresight-cti0-swao";
+		coresight-name = "coresight-cti-swao_cti0";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "apb_pclk";
+	};
+
+	 ipcb_tgu: tgu@6b0c000 {
+		compatible = "arm,primecell";
+		arm,primecell-periphid = <0x0003b999>;
+		reg = <0x06B0C000 0x1000>;
+		reg-names = "tgu-base";
+		tgu-steps = <3>;
+		tgu-conditions = <4>;
+		tgu-regs = <4>;
+		tgu-timer-counters = <8>;
+
+		coresight-name = "coresight-tgu-ipcb";
 
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
@@ -1931,6 +2075,20 @@
 		};
 	};
 
+	spss_etm0 {
+		compatible = "qcom,coresight-dummy";
+
+		coresight-name = "coresight-spss-etm0";
+
+		qcom,dummy-source;
+		port {
+			spss_etm0_out_funnel_spss: endpoint {
+				remote-endpoint =
+					<&funnel_spss_in_spss_etm0>;
+			};
+		};
+	};
+
 	funnel_apss_merg: funnel@7810000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index b34751b..ee0ad1f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -47,8 +47,8 @@
 		label = "kgsl-3d0";
 		compatible = "qcom,kgsl-3d0", "qcom,kgsl-3d";
 		status = "ok";
-		reg = <0x5000000 0x40000>;
-		reg-names = "kgsl_3d0_reg_memory";
+		reg = <0x5000000 0x40000>, <0x5061000 0x800>;
+		reg-names = "kgsl_3d0_reg_memory", "kgsl_3d0_cx_dbgc_memory";
 		interrupts = <0 300 0>;
 		interrupt-names = "kgsl_3d0_irq";
 		qcom,id = <0>;
@@ -58,6 +58,7 @@
 		qcom,initial-pwrlevel = <5>;
 
 		qcom,gpu-quirk-hfi-use-reg;
+		qcom,gpu-quirk-secvid-set-once;
 
 		qcom,idle-timeout = <80>; //msecs
 		qcom,no-nap;
@@ -75,15 +76,16 @@
 		qcom,tsens-name = "tsens_tz_sensor12";
 		#cooling-cells = <2>;
 
+		qcom,pm-qos-active-latency = <460>;
+
 		clocks = <&clock_gfx GPU_CC_GX_GFX3D_CLK>,
 			<&clock_gpucc GPU_CC_CXO_CLK>,
 			<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
 			<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
-			<&clock_gpucc GPU_CC_CX_GMU_CLK>,
-			<&clock_gpucc GPU_CC_AHB_CLK>;
+			<&clock_gpucc GPU_CC_CX_GMU_CLK>;
 
 		clock-names = "core_clk", "rbbmtimer_clk", "mem_clk",
-				"mem_iface_clk", "gmu_clk", "ahb_clk";
+				"mem_iface_clk", "gmu_clk";
 
 		qcom,isense-clk-on-level = <1>;
 
@@ -120,6 +122,36 @@
 		cache-slice-names = "gpu", "gpuhtw";
 		cache-slices = <&llcc 12>, <&llcc 11>;
 
+		qcom,gpu-coresights {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "qcom,gpu-coresight";
+
+			qcom,gpu-coresight@0 {
+				reg = <0>;
+				coresight-name = "coresight-gfx";
+				coresight-atid = <50>;
+				port {
+					gfx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx>;
+					};
+				};
+			};
+
+			qcom,gpu-coresight@1 {
+				reg = <1>;
+				coresight-name = "coresight-gfx-cx";
+				coresight-atid = <51>;
+				port {
+					gfx_cx_out_funnel_in2: endpoint {
+						remote-endpoint =
+						  <&funnel_in2_in_gfx_cx>;
+					};
+				};
+			};
+		};
+
 		/* GPU Mempools */
 		qcom,gpu-mempools {
 			#address-cells = <1>;
@@ -235,7 +267,8 @@
 		compatible = "qcom,kgsl-smmu-v2";
 
 		reg = <0x05040000 0x10000>;
-		qcom,protect = <0x40000 0x10000>;
+		/* CB5(ATOS) & CB5/6/7 are protected by HYP */
+		qcom,protect = <0x40000 0xc000>;
 		qcom,micro-mmu-control = <0x6000>;
 
 		clocks =<&clock_gcc GCC_GPU_CFG_AHB_CLK>,
@@ -245,6 +278,7 @@
 		clock-names = "iface_clk", "mem_clk", "mem_iface_clk";
 
 		qcom,secure_align_mask = <0xfff>;
+		qcom,retention;
 		qcom,hyp_secure_alloc;
 
 		gfx3d_user: gfx3d_user {
@@ -256,7 +290,7 @@
 
 		gfx3d_secure: gfx3d_secure {
 			compatible = "qcom,smmu-kgsl-cb";
-			iommus = <&kgsl_smmu 2>;
+			iommus = <&kgsl_smmu 2>, <&kgsl_smmu 1>;
 		};
 	};
 
@@ -285,11 +319,10 @@
 		clocks = <&clock_gpucc GPU_CC_CX_GMU_CLK>,
 				<&clock_gpucc GPU_CC_CXO_CLK>,
 				<&clock_gcc GCC_DDRSS_GPU_AXI_CLK>,
-				<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>,
-				<&clock_gpucc GPU_CC_AHB_CLK>;
+				<&clock_gcc GCC_GPU_MEMNOC_GFX_CLK>;
 
 		clock-names = "gmu_clk", "cxo_clk", "axi_clk",
-				"memnoc_clk", "ahb_clk";
+				"memnoc_clk";
 
 		qcom,gmu-pwrlevels {
 			#address-cells = <1>;
@@ -297,9 +330,10 @@
 
 			compatible = "qcom,gmu-pwrlevels";
 
+			/* GMU power levels must go from lowest to highest */
 			qcom,gmu-pwrlevel@0 {
 				reg = <0>;
-				qcom,gmu-freq = <400000000>;
+				qcom,gmu-freq = <0>;
 			};
 
 			qcom,gmu-pwrlevel@1 {
@@ -309,7 +343,7 @@
 
 			qcom,gmu-pwrlevel@2 {
 				reg = <2>;
-				qcom,gmu-freq = <0>;
+				qcom,gmu-freq = <400000000>;
 			};
 		};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845-hdk-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-hdk-audio-overlay.dtsi
new file mode 100644
index 0000000..492f07b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-hdk-audio-overlay.dtsi
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm845-audio-overlay.dtsi"
+
+&snd_934x {
+	qcom,model = "sdm845-tavil-hdk-snd-card";
+
+	qcom,audio-routing =
+		"AIF4 VI", "MCLK",
+		"RX_BIAS", "MCLK",
+		"MADINPUT", "MCLK",
+		"AMIC2", "MIC BIAS2",
+		"MIC BIAS2", "Headset Mic",
+		"DMIC0", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic0",
+		"DMIC1", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic1",
+		"DMIC2", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic2",
+		"DMIC5", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic5",
+		"SpkrLeft IN", "SPK1 OUT";
+
+	qcom,wsa-max-devs = <1>;
+	qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index 299f01b..10efa20 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -54,6 +54,24 @@
 	ibb-supply = <&lcdb_ncp_vreg>;
 };
 
+&dsi_dual_nt35597_video_display {
+	vddio-supply = <&pm660_l11>;
+	lab-supply = <&lcdb_ldo_vreg>;
+	ibb-supply = <&lcdb_ncp_vreg>;
+};
+
+&dsi_dual_nt35597_cmd_display {
+	vddio-supply = <&pm660_l11>;
+	lab-supply = <&lcdb_ldo_vreg>;
+	ibb-supply = <&lcdb_ncp_vreg>;
+};
+
+&dsi_dual_nt36850_truly_cmd_display {
+	vddio-supply = <&pm660_l11>;
+	lab-supply = <&lcdb_ldo_vreg>;
+	ibb-supply = <&lcdb_ncp_vreg>;
+};
+
 &sde_dp {
 	status = "disabled";
 	/delete-property/ vdda-1p2-supply;
@@ -84,17 +102,6 @@
 	/delete-property/ qcom,vddp-ref-clk-supply;
 };
 
-&ufsphy_card {
-	/delete-property/ vdda-phy-supply;
-	/delete-property/ vdda-pll-supply;
-};
-
-&ufshc_card {
-	/delete-property/ vcc-supply;
-	/delete-property/ vccq2-supply;
-	/delete-property/ qcom,vddp-ref-clk-supply;
-};
-
 &sdhc_2 {
 	/delete-property/ vdd-supply;
 	/delete-property/ vdd-io-supply;
@@ -153,6 +160,12 @@
 	/delete-property/ switch-source;
 };
 
+&led_flash_rear_aux {
+	/delete-property/ flash-source;
+	/delete-property/ torch-source;
+	/delete-property/ switch-source;
+};
+
 &led_flash_front {
 	/delete-property/ flash-source;
 	/delete-property/ torch-source;
@@ -194,6 +207,12 @@
 		/delete-property/ cam_vio-supply;
 		/delete-property/ cam_vana-supply;
 	};
+
+	qcom,cam-sensor@3 {
+		/delete-property/ cam_vio-supply;
+		/delete-property/ cam_vana-supply;
+	};
+
 };
 
 &clock_gcc {
@@ -216,11 +235,16 @@
 
 &clock_gpucc {
 	/delete-property/ vdd_cx-supply;
+	/delete-property/ vdd_mx-supply;
 };
 
 &clock_gfx {
 	/delete-property/ vdd_gfx-supply;
-	/delete-property/ vdd_mx-supply;
+};
+
+&clock_cpucc {
+	/delete-property/ vdd_l3_mx_ao-supply;
+	/delete-property/ vdd_pwrcl_mx_ao-supply;
 };
 
 &pil_modem {
@@ -235,12 +259,17 @@
 &soc {
 	/delete-node/ gpio_keys;
 
+	qcom,mss@4080000 {
+		/delete-property/ vdd_mss-supply;
+	};
+
 	qcom,lpass@17300000 {
 		/delete-property/ vdd_cx-supply;
 	};
 
 	qcom,ssc@5c00000 {
 		/delete-property/ vdd_cx-supply;
+		/delete-property/ vdd_mx-supply;
 	};
 
 	qcom,spss@1880000 {
@@ -330,8 +359,6 @@
 
 &soc {
 	/* Delete all regulators */
-	/delete-node/ cprh-ctrl@17dc0000;
-	/delete-node/ cprh-ctrl@17db0000;
 	/delete-node/ rpmh-regulator-ebilvl;
 	/delete-node/ rpmh-regulator-smpa2;
 	/delete-node/ rpmh-regulator-smpa3;
@@ -389,6 +416,7 @@
 #include "pm660.dtsi"
 #include "pm660l.dtsi"
 #include "sdm670-regulator.dtsi"
+#include "sdm670-pmic-overlay.dtsi"
 
 &soc {
 	/delete-node/ thermal-zones;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio-overlay.dtsi
new file mode 100644
index 0000000..54bd5201
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio-overlay.dtsi
@@ -0,0 +1,53 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "sdm670-audio-overlay.dtsi"
+
+&pm660l_3 {
+	/delete-node/analog-codec;
+};
+
+&soc {
+	/delete-node/msm-sdw-codec@62ec1000;
+	/delete-node/cdc_pdm_pinctrl;
+	/delete-node/wsa_spkr_en1_pinctrl;
+	/delete-node/wsa_spkr_en2_pinctrl;
+	/delete-node/sdw_clk_data_pinctrl;
+};
+
+&qupv3_se8_spi {
+	status = "okay";
+};
+
+&wcd9xxx_intc {
+	status = "okay";
+	qcom,gpio-connect = <&tlmm 54 0>;
+};
+
+&wdsp_mgr {
+	status = "okay";
+};
+
+&wdsp_glink {
+	status = "okay";
+};
+
+&wcd934x_cdc {
+	status = "okay";
+};
+
+&clock_audio_lnbb {
+	status = "okay";
+};
+
+&wcd_rst_gpio {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi
index f861ca3..88892d7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-audio.dtsi
@@ -10,44 +10,17 @@
  * GNU General Public License for more details.
  */
 
-#include "sdm670-audio.dtsi"
 
 &msm_audio_ion {
 	iommus = <&apps_smmu 0x1821 0x0>;
 	qcom,smmu-sid-mask = /bits/ 64 <0xf>;
 };
 
-&qupv3_se8_spi {
-	status = "okay";
-};
-
-&pm660l_3 {
-	/delete-node/analog-codec;
-};
-
 &soc {
-	/delete-node/msm-sdw-codec@62ec1000;
 	/delete-node/sound;
-	/delete-node/cdc_pdm_pinctrl;
-	/delete-node/wsa_spkr_en1_pinctrl;
-	/delete-node/wsa_spkr_en2_pinctrl;
-	/delete-node/sdw_clk_data_pinctrl;
 };
 
-&msm_audio_ion {
-	iommus = <&apps_smmu 0x1821 0x0>;
-};
-
-&wcd9xxx_intc {
-	status = "okay";
-	qcom,gpio-connect = <&tlmm 54 0>;
-};
-
-&wdsp_mgr {
-	status = "okay";
-};
-
-&wdsp_glink {
+&tavil_snd {
 	status = "okay";
 };
 
@@ -58,24 +31,3 @@
 &dai_slim {
 	status = "okay";
 };
-
-&wcd934x_cdc {
-	status = "okay";
-};
-
-&clock_audio_lnbb {
-	status = "okay";
-};
-
-&wcd_rst_gpio {
-	status = "okay";
-};
-
-&wcd9xxx_intc {
-	status = "okay";
-};
-
-&tavil_snd {
-	status = "okay";
-};
-
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp-overlay.dts
index da59bcf..9d61324 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp-overlay.dts
@@ -21,7 +21,7 @@
 
 #include "sdm845-sde-display.dtsi"
 #include "sdm845-interposer-sdm670-cdp.dtsi"
-
+#include "sdm845-interposer-sdm670-audio-overlay.dtsi"
 / {
 	model = "Qualcomm Technologies, Inc. SDM845 v1 Interposer SDM670 CDP";
 	compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dts b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dts
index ebb5e8f..597773e 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dts
@@ -16,6 +16,8 @@
 #include "sdm845-interposer-sdm670.dtsi"
 #include "sdm845-sde-display.dtsi"
 #include "sdm845-interposer-sdm670-cdp.dtsi"
+#include "sdm670-audio.dtsi"
+#include "sdm845-interposer-sdm670-audio.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. MSM sdm845 v1 Interposer SDM670 CDP";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
index ad15615..9313a75 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
@@ -12,4 +12,32 @@
 
 #include "sdm845-cdp.dtsi"
 #include "sdm845-interposer-pm660.dtsi"
-#include "sdm845-interposer-sdm670-audio.dtsi"
+
+&soc {
+	/delete-node/ ssusb@a800000;
+	/delete-node/ qusb@88e3000;
+	/delete-node/ ssphy@88eb000;
+};
+
+&usb0 {
+	extcon = <&pm660_pdphy>, <&pm660_pdphy>, <0> /* <&eud> */;
+};
+
+&qusb_phy0 {
+	vdd-supply = <&pm660l_l1>;
+	vdda18-supply = <&pm660_l10>;
+	vdda33-supply = <&pm660l_l7>;
+};
+
+&usb_qmp_dp_phy {
+	vdd-supply = <&pm660l_l1>; /* 0.88v */
+	core-supply = <&pm660_l1>; /* 1.2v */
+};
+
+&pcie0 {
+	status = "disabled";
+};
+
+&eud {
+	vdda33-supply = <&pm660l_l7>;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp-overlay.dts
index 3ca15b9..1690174 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp-overlay.dts
@@ -21,7 +21,7 @@
 
 #include "sdm845-sde-display.dtsi"
 #include "sdm845-interposer-sdm670-mtp.dtsi"
-
+#include "sdm845-interposer-sdm670-audio-overlay.dtsi"
 / {
 	model = "Qualcomm Technologies, Inc. SDM845 v1 Interposer SDM670 MTP";
 	compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dts
index 39664f1..52869da 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dts
@@ -16,6 +16,8 @@
 #include "sdm845-interposer-sdm670.dtsi"
 #include "sdm845-sde-display.dtsi"
 #include "sdm845-interposer-sdm670-mtp.dtsi"
+#include "sdm670-audio.dtsi"
+#include "sdm845-interposer-sdm670-audio.dtsi"
 
 / {
 	model = "Qualcomm Technologies, Inc. MSM sdm845 v1 Interposer SDM670 MTP";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
index c709770..e7ff910 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
@@ -12,8 +12,37 @@
 
 #include "sdm845-mtp.dtsi"
 #include "sdm845-interposer-pm660.dtsi"
-#include "sdm845-interposer-sdm670-audio.dtsi"
 
 &qupv3_se10_i2c {
 	/delete-node/ qcom,smb1355@8;
+	/delete-node/ qcom,smb1355@c;
+};
+
+&soc {
+	/delete-node/ ssusb@a800000;
+	/delete-node/ qusb@88e3000;
+	/delete-node/ ssphy@88eb000;
+};
+
+&usb0 {
+	extcon = <&pm660_pdphy>, <&pm660_pdphy>, <0> /* <&eud> */;
+};
+
+&qusb_phy0 {
+	vdd-supply = <&pm660l_l1>;
+	vdda18-supply = <&pm660_l10>;
+	vdda33-supply = <&pm660l_l7>;
+};
+
+&usb_qmp_dp_phy {
+	vdd-supply = <&pm660l_l1>; /* 0.88v */
+	core-supply = <&pm660_l1>; /* 1.2v */
+};
+
+&pcie0 {
+	status = "disabled";
+};
+
+&eud {
+	vdda33-supply = <&pm660l_l7>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
index 3d58dc1..d01149b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dtsi
@@ -164,6 +164,27 @@
 	qcom,platform-reset-gpio = <&tlmm 6 0>;
 };
 
+&dsi_dual_nt35597_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+};
+
 &dsi_nt35597_truly_dsc_cmd_display {
 	qcom,dsi-display-active;
 };
@@ -198,6 +219,7 @@
 	vdd-hba-supply = <&ufs_phy_gdsc>;
 	vdd-hba-fixed-regulator;
 	vcc-supply = <&pm8998_l20>;
+	vcc-voltage-level = <2950000 2960000>;
 	vccq2-supply = <&pm8998_s4>;
 	vcc-max-microamp = <600000>;
 	vccq2-max-microamp = <600000>;
@@ -208,44 +230,6 @@
 	status = "ok";
 };
 
-&extcon_storage_cd {
-	gpio = <&tlmm 126 GPIO_ACTIVE_LOW>;
-	debounce-ms = <200>;
-	irq-flags = <IRQ_TYPE_EDGE_BOTH>;
-
-	pinctrl-names = "default";
-	pinctrl-0 = <&storage_cd>;
-
-	status = "ok";
-};
-
-&ufsphy_card {
-	compatible = "qcom,ufs-phy-qmp-v3";
-
-	vdda-phy-supply = <&pm8998_l1>; /* 0.88v */
-	vdda-pll-supply = <&pm8998_l26>; /* 1.2v */
-	vdda-phy-max-microamp = <62900>;
-	vdda-pll-max-microamp = <18300>;
-
-	status = "ok";
-};
-
-&ufshc_card {
-	vdd-hba-supply = <&ufs_card_gdsc>;
-	vdd-hba-fixed-regulator;
-	vcc-supply = <&pm8998_l21>;
-	vccq2-supply = <&pm8998_s4>;
-	vcc-max-microamp = <300000>;
-	vccq2-max-microamp = <300000>;
-
-	qcom,vddp-ref-clk-supply = <&pm8998_l2>;
-	qcom,vddp-ref-clk-max-microamp = <100>;
-
-	extcon = <&extcon_storage_cd>;
-
-	status = "ok";
-};
-
 &sdhc_2 {
 	vdd-supply = <&pm8998_l21>;
 	qcom,vdd-voltage-level = <2950000 2960000>;
@@ -255,11 +239,20 @@
 	qcom,vdd-io-voltage-level = <1808000 2960000>;
 	qcom,vdd-io-current-level = <200 22000>;
 
-	pinctrl-names = "active", "sleep";
-	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on>;
-	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+	pinctrl-names = "active", "sleep", "ds_400KHz",
+			"ds_50MHz", "ds_100MHz", "ds_200MHz";
+	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
+	pinctrl-2 = <&sdc2_clk_ds_400KHz
+			&sdc2_cmd_ds_400KHz &sdc2_data_ds_400KHz>;
+	pinctrl-3 = <&sdc2_clk_ds_50MHz
+			&sdc2_cmd_ds_50MHz &sdc2_data_ds_50MHz>;
+	pinctrl-4 = <&sdc2_clk_ds_100MHz
+			&sdc2_cmd_ds_100MHz &sdc2_data_ds_100MHz>;
+	pinctrl-5 = <&sdc2_clk_ds_200MHz
+			&sdc2_cmd_ds_200MHz &sdc2_data_ds_200MHz>;
 
-	extcon = <&extcon_storage_cd>;
+	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 
 	status = "ok";
 };
@@ -291,8 +284,14 @@
 	qcom,battery-data = <&mtp_batterydata>;
 };
 
-&smb1355_charger {
+&smb1355_charger_0 {
 	status = "ok";
+	qcom,disable-ctm;
+};
+
+&smb1355_charger_1 {
+	status = "ok";
+	qcom,disable-ctm;
 };
 
 &qupv3_se9_2uart {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
index 322c5f5..daf5687 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pcie.dtsi
@@ -214,7 +214,8 @@
 
 		qcom,smmu-sid-base = <0x1c10>;
 
-		iommu-map = <0x100 &apps_smmu 0x1c11 0x1>,
+		iommu-map = <0x0 &apps_smmu 0x1c10 0x1>,
+			<0x100 &apps_smmu 0x1c11 0x1>,
 			<0x200 &apps_smmu 0x1c12 0x1>,
 			<0x300 &apps_smmu 0x1c13 0x1>,
 			<0x400 &apps_smmu 0x1c14 0x1>,
@@ -547,7 +548,8 @@
 
 		qcom,smmu-sid-base = <0x1c00>;
 
-		iommu-map = <0x100 &apps_smmu 0x1c01 0x1>,
+		iommu-map = <0x0 &apps_smmu 0x1c00 0x1>,
+			<0x100 &apps_smmu 0x1c01 0x1>,
 			<0x200 &apps_smmu 0x1c02 0x1>,
 			<0x300 &apps_smmu 0x1c03 0x1>,
 			<0x400 &apps_smmu 0x1c04 0x1>,
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
index 8dd75b69..67933c3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl-overlay.dtsi
@@ -20,6 +20,18 @@
 		};
 	};
 
+	qnovo_fet_ctrl {
+		qnovo_fet_ctrl_default: qnovo_fet_ctrl_default {
+			pins = "gpio6";
+			function = "func1";
+			output-low;
+			input-disable;
+			bias-disable;
+			power-source = <0>;
+			qcom,drive-strength = <1>;
+		};
+	};
+
 	usb2_vbus_det {
 		usb2_vbus_det_default: usb2_vbus_det_default {
 			pins = "gpio8";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index be76635..5035c9f 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -142,6 +142,38 @@
 			};
 		};
 
+		sdc2_clk_ds_400KHz: sdc2_clk_ds_400KHz {
+			config {
+				pins = "sdc2_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <16>;	/* 16 MA */
+			};
+		};
+
+		sdc2_clk_ds_50MHz: sdc2_clk_ds_50MHz {
+			config {
+				pins = "sdc2_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <16>;	/* 16 MA */
+			};
+		};
+
+		sdc2_clk_ds_100MHz: sdc2_clk_ds_100MHz {
+			config {
+				pins = "sdc2_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <16>;	/* 16 MA */
+			};
+		};
+
+		sdc2_clk_ds_200MHz: sdc2_clk_ds_200MHz {
+			config {
+				pins = "sdc2_clk";
+				bias-disable;		/* NO pull */
+				drive-strength = <16>;	/* 16 MA */
+			};
+		};
+
 		sdc2_cmd_on: sdc2_cmd_on {
 			config {
 				pins = "sdc2_cmd";
@@ -158,6 +190,38 @@
 			};
 		};
 
+		sdc2_cmd_ds_400KHz: sdc2_cmd_ds_400KHz {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_cmd_ds_50MHz: sdc2_cmd_ds_50MHz {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_cmd_ds_100MHz: sdc2_cmd_ds_100MHz {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_cmd_ds_200MHz: sdc2_cmd_ds_200MHz {
+			config {
+				pins = "sdc2_cmd";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
 		sdc2_data_on: sdc2_data_on {
 			config {
 				pins = "sdc2_data";
@@ -174,6 +238,38 @@
 			};
 		};
 
+		sdc2_data_ds_400KHz: sdc2_data_ds_400KHz {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_data_ds_50MHz: sdc2_data_ds_50MHz {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_data_ds_100MHz: sdc2_data_ds_100MHz {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
+		sdc2_data_ds_200MHz: sdc2_data_ds_200MHz {
+			config {
+				pins = "sdc2_data";
+				bias-pull-up;		/* pull up */
+				drive-strength = <10>;	/* 10 MA */
+			};
+		};
+
 		pcie0 {
 			pcie0_clkreq_default: pcie0_clkreq_default {
 				mux {
@@ -210,7 +306,7 @@
 				config {
 					pins = "gpio37";
 					drive-strength = <2>;
-					bias-pull-down;
+					bias-pull-up;
 				};
 			};
 		};
@@ -2826,6 +2922,35 @@
 			};
 		};
 
+		cam_sensor_mclk3_active: cam_sensor_mclk3_active {
+			/* MCLK3 */
+			mux {
+				pins = "gpio16";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio16";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_mclk3_suspend: cam_sensor_mclk3_suspend {
+			/* MCLK3 */
+			mux {
+				pins = "gpio16";
+				function = "cam_mclk";
+			};
+
+			config {
+				pins = "gpio16";
+				bias-pull-down; /* PULL DOWN */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+
 		cam_sensor_front_active: cam_sensor_front_active {
 			/* RESET  AVDD_LDO*/
 			mux {
@@ -2855,6 +2980,36 @@
 			};
 		};
 
+		cam_sensor_iris_active: cam_sensor_iris_active {
+			/* RESET  AVDD_LDO*/
+			mux {
+				pins = "gpio9", "gpio8";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9", "gpio8";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+			};
+		};
+
+		cam_sensor_iris_suspend: cam_sensor_iris_suspend {
+			/* RESET */
+			mux {
+				pins = "gpio9";
+				function = "gpio";
+			};
+
+			config {
+				pins = "gpio9";
+				bias-disable; /* No PULL */
+				drive-strength = <2>; /* 2 MA */
+				output-low;
+			};
+		};
+
+
 		cam_sensor_mclk2_active: cam_sensor_mclk2_active {
 			/* MCLK1 */
 			mux {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
index 73b0a32..8f1afe9 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pm.dtsi
@@ -34,51 +34,15 @@
 				qcom,time-overhead = <99>;
 			};
 
-			qcom,pm-cluster-level@1 { /* D2 */
+			qcom,pm-cluster-level@1 { /* LLCC off, AOSS sleep */
 				reg = <1>;
-				label = "l3-dyn-ret";
-				qcom,psci-mode = <0x2>;
-				qcom,latency-us = <659>;
-				qcom,ss-power = <434>;
-				qcom,energy-overhead = <465725>;
-				qcom,time-overhead = <976>;
-				qcom,min-child-idx = <1>;
-			};
-
-			qcom,pm-cluster-level@2 { /* D4, D3 is not supported */
-				reg = <2>;
-				label = "l3-pc";
-				qcom,psci-mode = <0x4>;
-				qcom,latency-us = <3201>;
-				qcom,ss-power = <408>;
-				qcom,energy-overhead = <2421840>;
-				qcom,time-overhead = <5376>;
-				qcom,min-child-idx = <2>;
-				qcom,is-reset;
-			};
-
-			qcom,pm-cluster-level@3 { /* Cx off */
-				reg = <3>;
-				label = "cx-off";
-				qcom,psci-mode = <0x224>;
-				qcom,latency-us = <5562>;
-				qcom,ss-power = <308>;
-				qcom,energy-overhead = <2521840>;
-				qcom,time-overhead = <6376>;
-				qcom,min-child-idx = <3>;
-				qcom,is-reset;
-				qcom,notify-rpm;
-			};
-
-			qcom,pm-cluster-level@4 { /* LLCC off, AOSS sleep */
-				reg = <4>;
 				label = "llcc-off";
 				qcom,psci-mode = <0xC24>;
 				qcom,latency-us = <6562>;
 				qcom,ss-power = <108>;
-				qcom,energy-overhead = <2621840>;
-				qcom,time-overhead = <7376>;
-				qcom,min-child-idx = <3>;
+				qcom,energy-overhead = <4000000>;
+				qcom,time-overhead = <5000>;
+				qcom,min-child-idx = <2>;
 				qcom,is-reset;
 				qcom,notify-rpm;
 			};
@@ -88,6 +52,7 @@
 				#size-cells = <0>;
 				qcom,psci-mode-shift = <0>;
 				qcom,psci-mode-mask = <0xf>;
+				qcom,use-prediction;
 				qcom,cpu = <&CPU0 &CPU1 &CPU2 &CPU3>;
 
 				qcom,pm-cpu-level@0 { /* C1 */
@@ -95,41 +60,31 @@
 					label = "wfi";
 					qcom,psci-cpu-mode = <0x1>;
 					qcom,latency-us = <43>;
-					qcom,ss-power = <454>;
-					qcom,energy-overhead = <38639>;
-					qcom,time-overhead = <83>;
+					qcom,ss-power = <150>;
+					qcom,energy-overhead = <10000>;
+					qcom,time-overhead = <100>;
 				};
 
-				qcom,pm-cpu-level@1 { /* C2D */
+				qcom,pm-cpu-level@1 {  /* C3 */
 					reg = <1>;
-					label = "ret";
-					qcom,psci-cpu-mode = <0x2>;
-					qcom,latency-us = <119>;
-					qcom,ss-power = <449>;
-					qcom,energy-overhead = <78456>;
-					qcom,time-overhead = <167>;
-				};
-
-				qcom,pm-cpu-level@2 {  /* C3 */
-					reg = <2>;
 					label = "pc";
 					qcom,psci-cpu-mode = <0x3>;
 					qcom,latency-us = <461>;
-					qcom,ss-power = <436>;
-					qcom,energy-overhead = <418225>;
-					qcom,time-overhead = <885>;
+					qcom,ss-power = <100>;
+					qcom,energy-overhead = <400000>;
+					qcom,time-overhead = <500>;
 					qcom,is-reset;
 					qcom,use-broadcast-timer;
 				};
 
-				qcom,pm-cpu-level@3 {  /* C4 */
-					reg = <3>;
+				qcom,pm-cpu-level@2 {  /* C4 */
+					reg = <2>;
 					label = "rail-pc";
 					qcom,psci-cpu-mode = <0x4>;
 					qcom,latency-us = <531>;
-					qcom,ss-power = <400>;
-					qcom,energy-overhead = <428225>;
-					qcom,time-overhead = <1000>;
+					qcom,ss-power = <73>;
+					qcom,energy-overhead = <500000>;
+					qcom,time-overhead = <600>;
 					qcom,is-reset;
 					qcom,use-broadcast-timer;
 				};
@@ -152,18 +107,8 @@
 					qcom,time-overhead = <83>;
 				};
 
-				qcom,pm-cpu-level@1 { /* C2D */
+				qcom,pm-cpu-level@1 {  /* C3 */
 					reg = <1>;
-					label = "ret";
-					qcom,psci-cpu-mode = <0x2>;
-					qcom,latency-us = <116>;
-					qcom,ss-power = <449>;
-					qcom,energy-overhead = <78456>;
-					qcom,time-overhead = <167>;
-				};
-
-				qcom,pm-cpu-level@2 {  /* C3 */
-					reg = <2>;
 					label = "pc";
 					qcom,psci-cpu-mode = <0x3>;
 					qcom,latency-us = <621>;
@@ -174,8 +119,8 @@
 					qcom,use-broadcast-timer;
 				};
 
-				qcom,pm-cpu-level@3 {  /* C4 */
-					reg = <3>;
+				qcom,pm-cpu-level@2 {  /* C4 */
+					reg = <2>;
 					label = "rail-pc";
 					qcom,psci-cpu-mode = <0x4>;
 					qcom,latency-us = <1061>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
index 000d65e7..48040a3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
@@ -32,6 +32,14 @@
 	smb2_vconn: qcom,smb2-vconn {
 		regulator-name = "smb2-vconn";
 	};
+	smb2_vbus: qcom,smb2-vbus {
+		regulator-name = "smb2-vbus";
+	};
+};
+
+&pmi8998_qnovo {
+	pinctrl-names = "default";
+	pinctrl-0 = <&qnovo_fet_ctrl_default>;
 };
 
 &usb0 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 6122eee..6dae069 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -88,7 +88,11 @@
 	qcom,fg-bmd-en-delay-ms = <300>;
 };
 
-&smb1355_charger {
+&smb1355_charger_0 {
+	status = "ok";
+};
+
+&smb1355_charger_1 {
 	status = "ok";
 };
 
@@ -111,6 +115,7 @@
 	vdd-hba-supply = <&ufs_phy_gdsc>;
 	vdd-hba-fixed-regulator;
 	vcc-supply = <&pm8998_l20>;
+	vcc-voltage-level = <2950000 2960000>;
 	vccq2-supply = <&pm8998_s4>;
 	vcc-max-microamp = <600000>;
 	vccq2-max-microamp = <600000>;
@@ -121,44 +126,6 @@
 	status = "ok";
 };
 
-&extcon_storage_cd {
-	gpio = <&tlmm 126 GPIO_ACTIVE_LOW>;
-	debounce-ms = <200>;
-	irq-flags = <IRQ_TYPE_EDGE_BOTH>;
-
-	pinctrl-names = "default";
-	pinctrl-0 = <&storage_cd>;
-
-	status = "ok";
-};
-
-&ufsphy_card {
-	compatible = "qcom,ufs-phy-qmp-v3";
-
-	vdda-phy-supply = <&pm8998_l1>; /* 0.88v */
-	vdda-pll-supply = <&pm8998_l26>; /* 1.2v */
-	vdda-phy-max-microamp = <62900>;
-	vdda-pll-max-microamp = <18300>;
-
-	status = "ok";
-};
-
-&ufshc_card {
-	vdd-hba-supply = <&ufs_card_gdsc>;
-	vdd-hba-fixed-regulator;
-	vcc-supply = <&pm8998_l21>;
-	vccq2-supply = <&pm8998_s4>;
-	vcc-max-microamp = <300000>;
-	vccq2-max-microamp = <300000>;
-
-	qcom,vddp-ref-clk-supply = <&pm8998_l2>;
-	qcom,vddp-ref-clk-max-microamp = <100>;
-
-	extcon = <&extcon_storage_cd>;
-
-	status = "ok";
-};
-
 &sdhc_2 {
 	vdd-supply = <&pm8998_l21>;
 	qcom,vdd-voltage-level = <2950000 2960000>;
@@ -169,10 +136,10 @@
 	qcom,vdd-io-current-level = <200 22000>;
 
 	pinctrl-names = "active", "sleep";
-	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on>;
-	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off>;
+	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &sdc2_data_on &storage_cd>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &storage_cd>;
 
-	extcon = <&extcon_storage_cd>;
+	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 
 	status = "ok";
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
index 1fa6e26..810afde 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
@@ -78,6 +78,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S0_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 0 3 64 0>,
+			<&gpi_dma0 1 0 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se0_i2c_active>;
 		pinctrl-1 = <&qupv3_se0_i2c_sleep>;
@@ -95,6 +98,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S1_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 1 3 64 0>,
+			<&gpi_dma0 1 1 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se1_i2c_active>;
 		pinctrl-1 = <&qupv3_se1_i2c_sleep>;
@@ -112,6 +118,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S2_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 2 3 64 0>,
+			<&gpi_dma0 1 2 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se2_i2c_active>;
 		pinctrl-1 = <&qupv3_se2_i2c_sleep>;
@@ -129,6 +138,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S3_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 3 3 64 0>,
+			<&gpi_dma0 1 3 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se3_i2c_active>;
 		pinctrl-1 = <&qupv3_se3_i2c_sleep>;
@@ -146,6 +158,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S4_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 4 3 64 0>,
+			<&gpi_dma0 1 4 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se4_i2c_active>;
 		pinctrl-1 = <&qupv3_se4_i2c_sleep>;
@@ -163,6 +178,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S5_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 5 3 64 0>,
+			<&gpi_dma0 1 5 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se5_i2c_active>;
 		pinctrl-1 = <&qupv3_se5_i2c_sleep>;
@@ -180,6 +198,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S6_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 6 3 64 0>,
+			<&gpi_dma0 1 6 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se6_i2c_active>;
 		pinctrl-1 = <&qupv3_se6_i2c_sleep>;
@@ -197,6 +218,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP0_S7_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_0_S_AHB_CLK>;
+		dmas = <&gpi_dma0 0 7 3 64 0>,
+			<&gpi_dma0 1 7 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se7_i2c_active>;
 		pinctrl-1 = <&qupv3_se7_i2c_sleep>;
@@ -221,6 +245,9 @@
 		interrupts = <GIC_SPI 601 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 0 1 64 0>,
+			<&gpi_dma0 1 0 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -240,6 +267,9 @@
 		interrupts = <GIC_SPI 602 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 1 1 64 0>,
+			<&gpi_dma0 1 1 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -259,6 +289,9 @@
 		interrupts = <GIC_SPI 603 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 2 1 64 0>,
+			<&gpi_dma0 1 2 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -278,6 +311,9 @@
 		interrupts = <GIC_SPI 604 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 3 1 64 0>,
+			<&gpi_dma0 1 3 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -297,6 +333,9 @@
 		interrupts = <GIC_SPI 605 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 4 1 64 0>,
+			<&gpi_dma0 1 4 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -316,6 +355,9 @@
 		interrupts = <GIC_SPI 606 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 5 1 64 0>,
+			<&gpi_dma0 1 5 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -335,6 +377,9 @@
 		interrupts = <GIC_SPI 607 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 6 1 64 0>,
+			<&gpi_dma0 1 6 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -354,6 +399,9 @@
 		interrupts = <GIC_SPI 608 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_0>;
+		dmas = <&gpi_dma0 0 7 1 64 0>,
+			<&gpi_dma0 1 7 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -418,6 +466,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S0_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 0 3 64 0>,
+			<&gpi_dma1 1 0 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se8_i2c_active>;
 		pinctrl-1 = <&qupv3_se8_i2c_sleep>;
@@ -435,6 +486,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S1_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 1 3 64 0>,
+			<&gpi_dma1 1 1 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se9_i2c_active>;
 		pinctrl-1 = <&qupv3_se9_i2c_sleep>;
@@ -452,6 +506,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S2_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 2 3 64 0>,
+			<&gpi_dma1 1 2 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se10_i2c_active>;
 		pinctrl-1 = <&qupv3_se10_i2c_sleep>;
@@ -469,6 +526,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S3_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 3 3 64 0>,
+			<&gpi_dma1 1 3 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se11_i2c_active>;
 		pinctrl-1 = <&qupv3_se11_i2c_sleep>;
@@ -486,6 +546,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S4_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 4 3 64 0>,
+			<&gpi_dma1 1 4 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se12_i2c_active>;
 		pinctrl-1 = <&qupv3_se12_i2c_sleep>;
@@ -503,6 +566,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S5_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 5 3 64 0>,
+			<&gpi_dma1 1 5 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se13_i2c_active>;
 		pinctrl-1 = <&qupv3_se13_i2c_sleep>;
@@ -520,6 +586,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S6_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 6 3 64 0>,
+			<&gpi_dma1 1 6 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se14_i2c_active>;
 		pinctrl-1 = <&qupv3_se14_i2c_sleep>;
@@ -537,6 +606,9 @@
 		clocks = <&clock_gcc GCC_QUPV3_WRAP1_S7_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_M_AHB_CLK>,
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
+		dmas = <&gpi_dma1 0 7 3 64 0>,
+			<&gpi_dma1 1 7 3 64 0>;
+		dma-names = "tx", "rx";
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se15_i2c_active>;
 		pinctrl-1 = <&qupv3_se15_i2c_sleep>;
@@ -557,10 +629,13 @@
 			<&clock_gcc GCC_QUPV3_WRAP_1_S_AHB_CLK>;
 		pinctrl-names = "default", "sleep";
 		pinctrl-0 = <&qupv3_se8_spi_active>;
-		pinctrl-1 = <&qupv3_se8_spi_sleep>;
+		pinctrl-1 = <&qupv3_se8_spi_active>;
 		interrupts = <GIC_SPI 353 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 0 1 64 0>,
+			<&gpi_dma1 1 0 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -580,6 +655,9 @@
 		interrupts = <GIC_SPI 354 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 1 1 64 0>,
+			<&gpi_dma1 1 1 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -599,6 +677,9 @@
 		interrupts = <GIC_SPI 355 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 2 1 64 0>,
+			<&gpi_dma1 1 2 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -618,6 +699,9 @@
 		interrupts = <GIC_SPI 356 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 3 1 64 0>,
+			<&gpi_dma1 1 3 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -637,6 +721,9 @@
 		interrupts = <GIC_SPI 357 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 4 1 64 0>,
+			<&gpi_dma1 1 4 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -656,6 +743,9 @@
 		interrupts = <GIC_SPI 358 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 5 1 64 0>,
+			<&gpi_dma1 1 5 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -675,6 +765,9 @@
 		interrupts = <GIC_SPI 359 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 6 1 64 0>,
+			<&gpi_dma1 1 6 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 
@@ -694,6 +787,9 @@
 		interrupts = <GIC_SPI 360 0>;
 		spi-max-frequency = <50000000>;
 		qcom,wrapper-core = <&qupv3_1>;
+		dmas = <&gpi_dma1 0 7 1 64 0>,
+			<&gpi_dma1 1 7 1 64 0>;
+		dma-names = "tx", "rx";
 		status = "disabled";
 	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi
new file mode 100644
index 0000000..77a89f0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr-audio-overlay.dtsi
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sdm845-audio-overlay.dtsi"
+
+&snd_934x {
+	qcom,model = "sdm845-qvr-tavil-snd-card";
+
+	qcom,audio-routing =
+		"AIF4 VI", "MCLK",
+		"RX_BIAS", "MCLK",
+		"MADINPUT", "MCLK",
+		"AMIC1", "Handset Mic",
+		"AMIC2", "MIC BIAS2",
+		"MIC BIAS2", "Headset Mic",
+		"DMIC0", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic0",
+		"DMIC1", "MIC BIAS1",
+		"MIC BIAS1", "Digital Mic1",
+		"DMIC2", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic2",
+		"DMIC3", "MIC BIAS3",
+		"MIC BIAS3", "Digital Mic3",
+		"DMIC4", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic4",
+		"DMIC5", "MIC BIAS4",
+		"MIC BIAS4", "Digital Mic5",
+		"SpkrLeft IN", "SPK1 OUT";
+
+	qcom,msm-mbhc-hphl-swh = <0>;
+	/delete-property/ qcom,hph-en0-gpio;
+	/delete-property/ qcom,hph-en1-gpio;
+	/delete-property/ qcom,usbc-analog-en1-gpio;
+	/delete-property/ qcom,usbc-analog-en2-gpio;
+	/delete-property/ pinctrl-names;
+	/delete-property/ pinctrl-0;
+	/delete-property/ pinctrl-1;
+
+	qcom,wsa-max-devs = <1>;
+	qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
+	qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrLeft";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index 6ea92ee..00f0650 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -9,3 +9,157 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+
+#include "sdm845-pmic-overlay.dtsi"
+#include "sdm845-pinctrl-overlay.dtsi"
+#include "smb1355.dtsi"
+
+&vendor {
+	bluetooth: bt_wcn3990 {
+		compatible = "qca,wcn3990";
+		qca,bt-vdd-io-supply = <&pm8998_s3>;
+		qca,bt-vdd-xtal-supply = <&pm8998_s5>;
+		qca,bt-vdd-core-supply = <&pm8998_l7>;
+		qca,bt-vdd-pa-supply = <&pm8998_l17>;
+		qca,bt-vdd-ldo-supply = <&pm8998_l25>;
+
+		qca,bt-vdd-io-voltage-level = <1352000 1352000>;
+		qca,bt-vdd-xtal-voltage-level = <2040000 2040000>;
+		qca,bt-vdd-core-voltage-level = <1800000 1800000>;
+		qca,bt-vdd-pa-voltage-level = <1304000 1304000>;
+		qca,bt-vdd-ldo-voltage-level = <3312000 3312000>;
+
+		qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+		qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+	};
+
+	qvr_batterydata: qcom,battery-data {
+		qcom,batt-id-range-pct = <15>;
+		#include "fg-gen3-batterydata-mlp446579-3800mah.dtsi"
+	};
+};
+
+&pmi8998_pdphy {
+	vbus-supply = <&smb2_vbus>;
+};
+
+&qupv3_se6_4uart {
+	status = "ok";
+};
+
+&pmi8998_fg {
+	qcom,battery-data = <&qvr_batterydata>;
+	qcom,fg-bmd-en-delay-ms = <300>;
+};
+
+&pmi8998_charger {
+	qcom,battery-data = <&qvr_batterydata>;
+	qcom,sw-jeita-enable;
+};
+
+&qupv3_se10_i2c {
+	status = "ok";
+};
+
+&smb1355_charger_0 {
+	status = "ok";
+};
+
+&smb1355_charger_1 {
+	status = "ok";
+};
+
+&soc {
+	qcom,qbt1000 {
+		status = "disabled";
+	};
+
+	gpio_keys {
+		compatible = "gpio-keys";
+		label = "gpio-keys";
+		pinctrl-names = "default";
+		pinctrl-0 = <&key_vol_up_default
+			     &key_home_default>;
+
+		vol_up {
+			label = "volume_up";
+			gpios = <&pm8998_gpios 6 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <115>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+
+		home {
+			label = "home";
+			gpios = <&pm8998_gpios 5 GPIO_ACTIVE_LOW>;
+			linux,input-type = <1>;
+			linux,code = <102>;
+			gpio-key,wakeup;
+			debounce-interval = <15>;
+			linux,can-disable;
+		};
+	};
+};
+
+&pmi8998_haptics {
+	qcom,vmax-mv = <1800>;
+	qcom,wave-play-rate-us = <4255>;
+	qcom,lra-auto-mode;
+	status = "okay";
+};
+
+&qupv3_se9_2uart {
+	status = "ok";
+};
+
+&ufsphy_mem {
+	compatible = "qcom,ufs-phy-qmp-v3";
+
+	vdda-phy-supply = <&pm8998_l1>; /* 0.88v */
+	vdda-pll-supply = <&pm8998_l26>; /* 1.2v */
+	vdda-phy-max-microamp = <62900>;
+	vdda-pll-max-microamp = <18300>;
+
+	status = "ok";
+};
+
+&ufshc_mem {
+	vdd-hba-supply = <&ufs_phy_gdsc>;
+	vdd-hba-fixed-regulator;
+	vcc-supply = <&pm8998_l20>;
+	vccq2-supply = <&pm8998_s4>;
+	vcc-max-microamp = <600000>;
+	vccq2-max-microamp = <600000>;
+
+	qcom,vddp-ref-clk-supply = <&pm8998_l2>;
+	qcom,vddp-ref-clk-max-microamp = <100>;
+
+	status = "ok";
+};
+
+&sdhc_2 {
+	vdd-supply = <&pm8998_l21>;
+	qcom,vdd-voltage-level = <2950000 2960000>;
+	qcom,vdd-current-level = <200 800000>;
+
+	vdd-io-supply = <&pm8998_l13>;
+	qcom,vdd-io-voltage-level = <1808000 2960000>;
+	qcom,vdd-io-current-level = <200 22000>;
+
+	pinctrl-names = "active", "sleep";
+	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on &storage_cd>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &storage_cd>;
+
+	cd-gpios = <&tlmm 126 GPIO_ACTIVE_HIGH>;
+
+	status = "ok";
+};
+
+&wil6210 {
+	status = "ok";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index f50df47..da4d41c 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -29,690 +29,7 @@
 	};
 };
 
-&spmi_bus {
-	qcom,pm8998@1 {
-		/* PM8998 S12 + S11 + S10 = VDD_APC1 supply */
-		pm8998_s12: regulator@3500 {
-			compatible = "qcom,qpnp-regulator";
-			reg = <0x3500 0x100>;
-			regulator-name = "pm8998_s12";
-			regulator-min-microvolt = <568000>;
-			regulator-max-microvolt = <1136000>;
-			qcom,enable-time = <500>;
-			regulator-always-on;
-		};
-
-		/* PM8998 S13 = VDD_APC0 supply */
-		pm8998_s13: regulator@3800 {
-			compatible = "qcom,qpnp-regulator";
-			reg = <0x3800 0x100>;
-			regulator-name = "pm8998_s13";
-			regulator-min-microvolt = <568000>;
-			regulator-max-microvolt = <996000>;
-			qcom,enable-time = <500>;
-			regulator-always-on;
-		};
-	};
-};
-
 &soc {
-	/* CPR controller regulators */
-	apc0_cpr: cprh-ctrl@17dc0000 {
-		compatible = "qcom,cprh-sdm845-v1-kbss-regulator";
-		reg =	<0x17dc0000 0x4000>,
-			<0x00784000 0x1000>,
-			<0x17840000 0x1000>;
-		reg-names = "cpr_ctrl", "fuse_base", "saw";
-		clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
-		clock-names = "core_clk";
-		qcom,cpr-ctrl-name = "apc0";
-		qcom,cpr-controller-id = <0>;
-
-		qcom,cpr-sensor-time = <1000>;
-		qcom,cpr-loop-time = <5000000>;
-		qcom,cpr-idle-cycles = <15>;
-		qcom,cpr-up-down-delay-time = <3000>;
-		qcom,cpr-step-quot-init-min = <11>;
-		qcom,cpr-step-quot-init-max = <12>;
-		qcom,cpr-count-mode = <0>;		/* All at once */
-		qcom,cpr-count-repeat = <20>;
-		qcom,cpr-down-error-step-limit = <1>;
-		qcom,cpr-up-error-step-limit = <1>;
-		qcom,cpr-corner-switch-delay-time = <1042>;
-		qcom,cpr-voltage-settling-time = <1760>;
-		qcom,cpr-reset-step-quot-loop-en;
-
-		qcom,voltage-step = <4000>;
-		qcom,voltage-base = <352000>;
-		qcom,cpr-saw-use-unit-mV;
-
-		qcom,saw-avs-ctrl = <0x101C031>;
-		qcom,saw-avs-limit = <0x3A003A0>;
-
-		qcom,cpr-enable;
-		qcom,cpr-hw-closed-loop;
-
-		qcom,cpr-panic-reg-addr-list =
-			<0x17dc3a84 0x17dc3a88 0x17840c18>;
-		qcom,cpr-panic-reg-name-list =
-			"APSS_SILVER_CPRH_STATUS_0",
-			"APSS_SILVER_CPRH_STATUS_1",
-			"SILVER_SAW4_PMIC_STS";
-
-		qcom,cpr-aging-ref-voltage = <996000>;
-		vdd-supply = <&pm8998_s13>;
-
-		thread@1 {
-			qcom,cpr-thread-id = <1>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc0_pwrcl_vreg: regulator {
-				regulator-name = "apc0_pwrcl_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <19>;
-
-				qcom,cpr-fuse-corners = <4>;
-				qcom,cpr-fuse-combos = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <19 19 19>;
-				qcom,cpr-corners = <19>;
-
-				qcom,cpr-corner-fmax-map = <6 12 17 19>;
-
-				qcom,cpr-voltage-ceiling =
-					<872000  872000  872000  872000  872000
-					 872000  872000  872000  872000  872000
-					 872000  872000  872000  872000  872000
-					 872000  928000  996000  996000>;
-
-				qcom,cpr-voltage-floor =
-					/* Speed bin 0 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  996000  996000>,
-					/* Speed bin 1 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000>,
-					/* Speed bin 2 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  40000  40000>;
-
-				qcom,corner-frequencies =
-					/* Speed bin 0 */
-					<300000000  422400000  499200000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1209600000
-					1286400000 1363200000 1440000000
-					1516800000 1593600000 1651200000
-					1708800000>,
-					/* Speed bin 1 */
-					<300000000  422400000  499200000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1209600000
-					1286400000 1363200000 1440000000
-					1516800000 1593600000 1651200000
-					1708800000>,
-					/* Speed bin 2 */
-					<300000000  422400000  499200000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1209600000
-					1286400000 1363200000 1440000000
-					1516800000 1593600000 1670400000
-					1747200000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2594 2795 2576 2761 2469 2673 2198
-					 2553 3188 3255 3191 2962 3055 2984
-					 2043 2947>,
-					<2594 2795 2576 2761 2469 2673 2198
-					 2553 3188 3255 3191 2962 3055 2984
-					 2043 2947>,
-					<2259 2389 2387 2531 2294 2464 2218
-					 2476 2525 2855 2817 2836 2740 2490
-					 1950 2632>,
-					<2259 2389 2387 2531 2294 2464 2218
-					 2476 2525 2855 2817 2836 2740 2490
-					 1950 2632>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					/* Speed bin 2 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					/* Speed bin 2 */
-					<100000 100000 100000 100000>,
-					<     0      0      0 100000>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>,
-					<     0      0      0      0>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <19>;
-				qcom,cpr-aging-ro-scaling-factor = <1620>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 2 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
-		};
-
-		thread@0 {
-			qcom,cpr-thread-id = <0>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc0_l3_vreg: regulator {
-				regulator-name = "apc0_l3_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <13>;
-
-				qcom,cpr-fuse-corners = <4>;
-				qcom,cpr-fuse-combos = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <11 11 13>;
-				qcom,cpr-corners =
-					/* Speed bin 0 */
-					<11 11 11 11 11 11 11 11>,
-					/* Speed bin 1 */
-					<11 11 11 11 11 11 11 11>,
-					/* Speed bin 2 */
-					<13 13 13 13 13 13 13 13>;
-
-				qcom,cpr-corner-fmax-map =
-					/* Speed bin 0 */
-					<4 7 9 11>,
-					/* Speed bin 1 */
-					<4 7 9 11>,
-					/* Speed bin 2 */
-					<4 7 9 13>;
-
-				qcom,cpr-voltage-ceiling =
-					/* Speed bin 0 */
-					<872000  872000  872000  872000  872000
-					 872000  872000  872000  928000  996000
-					 996000>,
-					/* Speed bin 1 */
-					<872000  872000  872000  872000  872000
-					 872000  872000  872000  928000  996000
-					 996000>,
-					/* Speed bin 2 */
-					<872000  872000  872000  872000  872000
-					 872000  872000  872000  928000  996000
-					 996000  996000  996000>;
-
-				qcom,cpr-voltage-floor =
-					/* Speed bin 0 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  996000
-					 996000>,
-					/* Speed bin 1 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000>,
-					/* Speed bin 2 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					/* Speed bin 0 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  40000
-					 40000>,
-					/* Speed bin 1 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  40000
-					 40000>,
-					/* Speed bin 2 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  40000
-					 40000  40000  40000>;
-
-				qcom,corner-frequencies =
-					/* Speed bin 0 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1094400000>,
-					/* Speed bin 1 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1094400000>,
-					/* Speed bin 2 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1113600000 1209600000
-					1305600000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2439 2577 2552 2667 2461 2577 2394
-					 2536 2132 2307 2191 2903 2838 2912
-					 2501 2095>,
-					<2439 2577 2552 2667 2461 2577 2394
-					 2536 2132 2307 2191 2903 2838 2912
-					 2501 2095>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000 100000>,
-					<     0  24000   4000 100000>,
-					<     0  24000   4000      0>,
-					<     0  24000   4000      0>,
-					<     0  24000   4000      0>,
-					<     0  24000   4000      0>,
-					<     0  24000   4000      0>,
-					<     0  24000   4000      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000 100000>,
-					<     0  24000   4000 100000>,
-					<     0  24000   4000  20000>,
-					<     0  24000   4000  20000>,
-					<     0  24000   4000  20000>,
-					<     0  24000   4000  20000>,
-					<     0  24000   4000  20000>,
-					<     0  24000   4000  20000>,
-					/* Speed bin 2 */
-					<100000 100000 100000 100000>,
-					<     0  24000   4000 100000>,
-					<     0  24000   4000  40000>,
-					<     0  24000   4000  40000>,
-					<     0  24000   4000  40000>,
-					<     0  24000   4000  40000>,
-					<     0  24000   4000  40000>,
-					<     0  24000   4000  40000>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000 100000>,
-					<     0  29000   6000 100000>,
-					<     0  29000   6000      0>,
-					<     0  29000   6000      0>,
-					<     0  29000   6000      0>,
-					<     0  29000   6000      0>,
-					<     0  29000   6000      0>,
-					<     0  29000   6000      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000 100000>,
-					<     0  29000   6000 100000>,
-					<     0  29000   6000  20000>,
-					<     0  29000   6000  20000>,
-					<     0  29000   6000  20000>,
-					<     0  29000   6000  20000>,
-					<     0  29000   6000  20000>,
-					<     0  29000   6000  20000>,
-					/* Speed bin 2 */
-					<100000 100000 100000 100000>,
-					<     0  29000   6000 100000>,
-					<     0  29000   6000  40000>,
-					<     0  29000   6000  40000>,
-					<     0  29000   6000  40000>,
-					<     0  29000   6000  40000>,
-					<     0  29000   6000  40000>,
-					<     0  29000   6000  40000>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <11 11 13>;
-				qcom,cpr-aging-ro-scaling-factor = <1620>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 2 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
-		};
-	};
-
-	apc1_cpr: cprh-ctrl@17db0000 {
-		compatible = "qcom,cprh-sdm845-v1-kbss-regulator";
-		reg =	<0x17db0000 0x4000>,
-			<0x00784000 0x1000>,
-			<0x17830000 0x1000>;
-		reg-names = "cpr_ctrl", "fuse_base", "saw";
-		clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
-		clock-names = "core_clk";
-		qcom,cpr-ctrl-name = "apc1";
-		qcom,cpr-controller-id = <1>;
-
-		qcom,cpr-sensor-time = <1000>;
-		qcom,cpr-loop-time = <5000000>;
-		qcom,cpr-idle-cycles = <15>;
-		qcom,cpr-up-down-delay-time = <3000>;
-		qcom,cpr-step-quot-init-min = <9>;
-		qcom,cpr-step-quot-init-max = <14>;
-		qcom,cpr-count-mode = <0>;		/* All at once */
-		qcom,cpr-count-repeat = <20>;
-		qcom,cpr-down-error-step-limit = <1>;
-		qcom,cpr-up-error-step-limit = <1>;
-		qcom,cpr-corner-switch-delay-time = <1042>;
-		qcom,cpr-voltage-settling-time = <1760>;
-		qcom,cpr-reset-step-quot-loop-en;
-
-		qcom,apm-threshold-voltage = <800000>;
-		qcom,apm-crossover-voltage = <880000>;
-		qcom,mem-acc-threshold-voltage = <852000>;
-		qcom,mem-acc-crossover-voltage = <852000>;
-
-		qcom,voltage-step = <4000>;
-		qcom,voltage-base = <352000>;
-		qcom,cpr-saw-use-unit-mV;
-
-		qcom,saw-avs-ctrl = <0x101C031>;
-		qcom,saw-avs-limit = <0x4200420>;
-
-		qcom,cpr-enable;
-		qcom,cpr-hw-closed-loop;
-
-		qcom,cpr-panic-reg-addr-list =
-			<0x17db3a84 0x17830c18>;
-		qcom,cpr-panic-reg-name-list =
-			"APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
-
-		qcom,cpr-aging-ref-voltage = <1136000>;
-		vdd-supply = <&pm8998_s12>;
-
-		thread@0 {
-			qcom,cpr-thread-id = <0>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc1_perfcl_vreg: regulator {
-				regulator-name = "apc1_perfcl_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <27>;
-
-				qcom,cpr-fuse-corners = <3>;
-				qcom,cpr-fuse-combos = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <22 24 25>;
-				qcom,cpr-corners =
-					/* Speed bin 0 */
-					<22 22 22 22 22 22 22 22>,
-					/* Speed bin 1 */
-					<24 24 24 24 24 24 24 24>,
-					/* Speed bin 2 */
-					<25 25 25 25 25 25 25 25>;
-
-				qcom,cpr-corner-fmax-map =
-					/* Speed bin 0 */
-					<10 17 22>,
-					/* Speed bin 1 */
-					<10 17 24>,
-					/* Speed bin 2 */
-					<10 17 25>;
-
-				qcom,cpr-voltage-ceiling =
-					/* Speed bin 0 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  884000  952000  952000
-					1136000 1136000>,
-					/* Speed bin 1 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  884000  952000  952000
-					1136000 1136000 1136000 1136000>,
-					/* Speed bin 2 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  884000  952000  952000
-					1136000 1136000 1136000 1136000
-					1136000>;
-
-				qcom,cpr-voltage-floor =
-					/* Speed bin 0 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000>,
-					/* Speed bin 1 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000>,
-					/* Speed bin 2 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000
-					 568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					/* Speed bin 0 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  40000  40000  40000
-					 40000  40000>,
-					/* Speed bin 1 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  40000  40000  40000
-					 40000  40000  40000  40000>,
-					/* Speed bin 2 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  40000  40000  40000
-					 40000  40000  40000  40000  40000>;
-
-				qcom,corner-frequencies =
-					/* Speed bin 0 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1113600000 1190400000
-					1267200000 1344000000 1420800000
-					1497600000 1574400000 1651200000
-					1728000000 1804800000 1881600000
-					1958400000>,
-					/* Speed bin 1 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1113600000 1190400000
-					1267200000 1344000000 1420800000
-					1497600000 1574400000 1651200000
-					1728000000 1804800000 1881600000
-					1958400000 2035200000 2092800000>,
-					/* Speed bin 2 */
-					<300000000  422400000  499200000
-					 576000000  652800000  729600000
-					 806400000  883200000  960000000
-					1036800000 1113600000 1190400000
-					1267200000 1344000000 1420800000
-					1497600000 1574400000 1651200000
-					1728000000 1804800000 1881600000
-					1958400000 2035200000 2112000000
-					2208000000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2086 2208 2273 2408 2203 2327 2213
-					 2340 1755 2039 2049 2474 2437 2618
-					 2003 1675>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					/* Speed bin 2 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					/* Speed bin 1 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					/* Speed bin 2 */
-					<100000 100000 100000>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>,
-					<     0      0      0>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <22 24 25>;
-				qcom,cpr-aging-ro-scaling-factor = <1700>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 2 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
-		};
-	};
-
 	/* RPMh regulators: */
 
 	/* PM8998 S1 = VDD_EBI supply */
@@ -854,11 +171,14 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 1>;
+		proxy-supply = <&pm8998_l1>;
 		pm8998_l1: regulator-l1 {
 			regulator-name = "pm8998_l1";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <880000>;
 			regulator-max-microvolt = <880000>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <72000>;
 			qcom,init-voltage = <880000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
@@ -1103,9 +423,12 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 10000>;
+		proxy-supply = <&pm8998_l14>;
 		pm8998_l14: regulator-l14 {
 			regulator-name = "pm8998_l14";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <115000>;
 			regulator-min-microvolt = <1800000>;
 			regulator-max-microvolt = <1880000>;
 			qcom,init-voltage = <1800000>;
@@ -1320,11 +643,14 @@
 			<RPMH_REGULATOR_MODE_LDO_LPM
 			 RPMH_REGULATOR_MODE_LDO_HPM>;
 		qcom,mode-threshold-currents = <0 1>;
+		proxy-supply = <&pm8998_l26>;
 		pm8998_l26: regulator-l26 {
 			regulator-name = "pm8998_l26";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <1200000>;
 			regulator-max-microvolt = <1200000>;
+			qcom,proxy-consumer-enable;
+			qcom,proxy-consumer-current = <43600>;
 			qcom,init-voltage = <1200000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LDO_LPM>;
 		};
@@ -1389,12 +715,24 @@
 		compatible = "qcom,rpmh-vrm-regulator";
 		mboxes = <&apps_rsc 0>;
 		qcom,resource-name = "bobb1";
+		qcom,send-defaults;
+
 		pmi8998_bob: regulator-bob {
 			regulator-name = "pmi8998_bob";
 			qcom,set = <RPMH_REGULATOR_SET_ALL>;
 			regulator-min-microvolt = <3312000>;
 			regulator-max-microvolt = <3600000>;
 			qcom,init-voltage = <3312000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_BOB_PASS>;
+		};
+
+		pmi8998_bob_ao: regulator-bob-ao {
+			regulator-name = "pmi8998_bob_ao";
+			qcom,set = <RPMH_REGULATOR_SET_ACTIVE>;
+			regulator-min-microvolt = <3312000>;
+			regulator-max-microvolt = <3600000>;
+			qcom,init-voltage = <3312000>;
+			qcom,init-mode = <RPMH_REGULATOR_MODE_BOB_AUTO>;
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index e7fd8a0..34de0a0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -33,6 +33,7 @@
 	vdd-hba-supply = <&ufs_phy_gdsc>;
 	vdd-hba-fixed-regulator;
 	vcc-supply = <&pm8998_l20>;
+	vcc-voltage-level = <2950000 2960000>;
 	vccq2-supply = <&pm8998_s4>;
 	vcc-max-microamp = <600000>;
 	vccq2-max-microamp = <600000>;
@@ -131,45 +132,6 @@
 	};
 };
 
-&apc0_cpr {
-	qcom,cpr-ignore-invalid-fuses;
-};
-
-&apc1_cpr {
-	qcom,cpr-ignore-invalid-fuses;
-};
-
-&ufsphy_card {
-	compatible = "qcom,ufs-phy-qrbtc-sdm845";
-
-	vdda-phy-supply = <&pm8998_l1>; /* 0.88v */
-	vdda-pll-supply = <&pm8998_l26>; /* 1.2v */
-	vdda-phy-max-microamp = <62900>;
-	vdda-pll-max-microamp = <18300>;
-
-	status = "ok";
-};
-
-&ufshc_card {
-	limit-tx-hs-gear = <1>;
-	limit-rx-hs-gear = <1>;
-
-	vdd-hba-supply = <&ufs_card_gdsc>;
-	vdd-hba-fixed-regulator;
-	vcc-supply = <&pm8998_l21>;
-	vccq2-supply = <&pm8998_s4>;
-	vcc-max-microamp = <300000>;
-	vccq2-max-microamp = <300000>;
-
-	qcom,vddp-ref-clk-supply = <&pm8998_l2>;
-	qcom,vddp-ref-clk-max-microamp = <100>;
-
-	qcom,disable-lpm;
-	rpm-level = <0>;
-	spm-level = <0>;
-	status = "ok";
-};
-
 &pmi8998_charger {
 	qcom,suspend-input;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
index db3d23c..4ecb49a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
@@ -25,6 +25,9 @@
 #include "dsi-panel-sharp-1080p-cmd.dtsi"
 #include "dsi-panel-sharp-dualmipi-1080p-120hz.dtsi"
 #include "dsi-panel-s6e3ha3-amoled-dualmipi-wqhd-cmd.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-video.dtsi"
+#include "dsi-panel-nt35597-dualmipi-wqxga-cmd.dtsi"
+#include "dsi-panel-nt36850-truly-dualmipi-wqhd-cmd.dtsi"
 #include <dt-bindings/clock/mdss-10nm-pll-clk.h>
 
 &soc {
@@ -401,6 +404,78 @@
 		qcom,dsi-panel = <&dsi_dual_sim_dsc_375_cmd>;
 	};
 
+	dsi_dual_nt35597_video_display: qcom,dsi-display@14 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_video_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-reset-gpio = <&tlmm 6 0>;
+		qcom,panel-mode-gpio = <&tlmm 52 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_video>;
+		vddio-supply = <&pm8998_l14>;
+		lab-supply = <&lab_regulator>;
+		ibb-supply = <&ibb_regulator>;
+	};
+
+
+	dsi_dual_nt35597_cmd_display: qcom,dsi-display@15 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt35597_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+		       <&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 6 0>;
+		qcom,panel-mode-gpio = <&tlmm 52 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt35597_cmd>;
+		vddio-supply = <&pm8998_l14>;
+		lab-supply = <&lab_regulator>;
+		ibb-supply = <&ibb_regulator>;
+	};
+
+	dsi_dual_nt36850_truly_cmd_display: qcom,dsi-display@16 {
+		compatible = "qcom,dsi-display";
+		label = "dsi_dual_nt36850_truly_cmd_display";
+		qcom,display-type = "primary";
+
+		qcom,dsi-ctrl = <&mdss_dsi0 &mdss_dsi1>;
+		qcom,dsi-phy = <&mdss_dsi_phy0 &mdss_dsi_phy1>;
+		clocks = <&mdss_dsi0_pll BYTECLK_MUX_0_CLK>,
+			<&mdss_dsi0_pll PCLK_MUX_0_CLK>;
+		clock-names = "src_byte_clk", "src_pixel_clk";
+
+		pinctrl-names = "panel_active", "panel_suspend";
+		pinctrl-0 = <&sde_dsi_active &sde_te_active>;
+		pinctrl-1 = <&sde_dsi_suspend &sde_te_suspend>;
+		qcom,platform-te-gpio = <&tlmm 10 0>;
+		qcom,platform-reset-gpio = <&tlmm 6 0>;
+		qcom,panel-mode-gpio = <&tlmm 52 0>;
+
+		qcom,dsi-panel = <&dsi_dual_nt36850_truly_cmd>;
+		vddio-supply = <&pm8998_l14>;
+		lab-supply = <&lab_regulator>;
+		ibb-supply = <&ibb_regulator>;
+	};
+
 	sde_wb: qcom,wb-display@0 {
 		compatible = "qcom,wb-display";
 		cell-index = <0>;
@@ -414,108 +489,12 @@
 			compatible = "qcom,msm-ext-disp-audio-codec-rx";
 		};
 	};
-
-	sde_dp: qcom,dp_display@0{
-		cell-index = <0>;
-		compatible = "qcom,dp-display";
-
-		gdsc-supply = <&mdss_core_gdsc>;
-		vdda-1p2-supply = <&pm8998_l26>;
-		vdda-0p9-supply = <&pm8998_l1>;
-
-		reg =	<0xae90000 0xa84>,
-			<0x88eaa00 0x200>,
-			<0x88ea200 0x200>,
-			<0x88ea600 0x200>,
-			<0xaf02000 0x1a0>,
-			<0x780000 0x621c>,
-			<0x88ea030 0x10>,
-			<0x0aee1000 0x034>;
-		reg-names = "dp_ctrl", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
-			"dp_mmss_cc", "qfprom_physical", "dp_pll",
-			"hdcp_physical";
-
-		interrupt-parent = <&mdss_mdp>;
-		interrupts = <12 0>;
-
-		clocks =  <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
-			 <&clock_rpmh RPMH_CXO_CLK>,
-			 <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
-			 <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
-			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
-			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
-			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
-			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
-			 <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
-			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
-			 <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
-		clock-names = "core_aux_clk", "core_usb_ref_clk_src",
-			"core_usb_ref_clk", "core_usb_cfg_ahb_clk",
-			"core_usb_pipe_clk", "ctrl_link_clk",
-			"ctrl_link_iface_clk", "ctrl_pixel_clk",
-			"crypto_clk", "pixel_clk_rcg", "pixel_parent";
-
-		qcom,dp-usbpd-detection = <&pmi8998_pdphy>;
-		qcom,ext-disp = <&ext_disp>;
-
-		qcom,aux-cfg0-settings = [20 00];
-		qcom,aux-cfg1-settings = [24 13 23 1d];
-		qcom,aux-cfg2-settings = [28 24];
-		qcom,aux-cfg3-settings = [2c 00];
-		qcom,aux-cfg4-settings = [30 0a];
-		qcom,aux-cfg5-settings = [34 26];
-		qcom,aux-cfg6-settings = [38 0a];
-		qcom,aux-cfg7-settings = [3c 03];
-		qcom,aux-cfg8-settings = [40 bb];
-		qcom,aux-cfg9-settings = [44 03];
-
-		qcom,max-pclk-frequency-khz = <576000>;
-
-		qcom,core-supply-entries {
-			#address-cells = <1>;
-			#size-cells = <0>;
-
-			qcom,core-supply-entry@0 {
-				reg = <0>;
-				qcom,supply-name = "gdsc";
-				qcom,supply-min-voltage = <0>;
-				qcom,supply-max-voltage = <0>;
-				qcom,supply-enable-load = <0>;
-				qcom,supply-disable-load = <0>;
-			};
-		};
-
-		qcom,ctrl-supply-entries {
-			#address-cells = <1>;
-			#size-cells = <0>;
-
-			qcom,ctrl-supply-entry@0 {
-				reg = <0>;
-				qcom,supply-name = "vdda-1p2";
-				qcom,supply-min-voltage = <1200000>;
-				qcom,supply-max-voltage = <1200000>;
-				qcom,supply-enable-load = <21800>;
-				qcom,supply-disable-load = <4>;
-			};
-		};
-
-		qcom,phy-supply-entries {
-			#address-cells = <1>;
-			#size-cells = <0>;
-
-			qcom,phy-supply-entry@0 {
-				reg = <0>;
-				qcom,supply-name = "vdda-0p9";
-				qcom,supply-min-voltage = <880000>;
-				qcom,supply-max-voltage = <880000>;
-				qcom,supply-enable-load = <36000>;
-				qcom,supply-disable-load = <32>;
-			};
-		};
-	};
 };
 
 &sde_dp {
+	qcom,dp-usbpd-detection = <&pmi8998_pdphy>;
+	qcom,ext-disp = <&ext_disp>;
+
 	pinctrl-names = "mdss_dp_active", "mdss_dp_sleep";
 	pinctrl-0 = <&sde_dp_aux_active &sde_dp_usbplug_cc_active>;
 	pinctrl-1 = <&sde_dp_aux_suspend &sde_dp_usbplug_cc_suspend>;
@@ -531,6 +510,11 @@
 &dsi_dual_nt35597_truly_video {
 	qcom,mdss-dsi-t-clk-post = <0x0D>;
 	qcom,mdss-dsi-t-clk-pre = <0x2D>;
+	qcom,mdss-dsi-min-refresh-rate = <53>;
+	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";
 	qcom,mdss-dsi-display-timings {
 		timing@0{
 			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
@@ -559,6 +543,7 @@
 &dsi_nt35597_truly_dsc_cmd {
 	qcom,mdss-dsi-t-clk-post = <0x0b>;
 	qcom,mdss-dsi-t-clk-pre = <0x23>;
+	qcom,ulps-enabled;
 	qcom,mdss-dsi-display-timings {
 		timing@0{
 			qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
@@ -574,6 +559,11 @@
 &dsi_nt35597_truly_dsc_video {
 	qcom,mdss-dsi-t-clk-post = <0x0b>;
 	qcom,mdss-dsi-t-clk-pre = <0x23>;
+	qcom,mdss-dsi-min-refresh-rate = <53>;
+	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";
 	qcom,mdss-dsi-display-timings {
 		timing@0{
 			qcom,mdss-dsi-panel-phy-timings = [00 15 05 05 20 1f 05
@@ -631,8 +621,9 @@
 	qcom,mdss-dsi-t-clk-pre = <0x29>;
 	qcom,mdss-dsi-display-timings {
 		timing@0{
-			qcom,mdss-dsi-panel-phy-timings = [00 1A 06 06 22 20 07
-				07 04 03 04 00];
+			qcom,mdss-dsi-panel-phy-timings = [00 1E 08 08 24 22 08
+				08 05 03 04 00];
+			qcom,mdss-dsi-panel-clockrate = <900000000>;
 			qcom,display-topology = <1 0 1>;
 			qcom,default-topology-index = <0>;
 		};
@@ -668,15 +659,35 @@
 };
 
 &dsi_sim_cmd {
-	qcom,mdss-dsi-t-clk-post = <0x0d>;
-	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-t-clk-post = <0x0c>;
+	qcom,mdss-dsi-t-clk-pre = <0x29>;
 	qcom,mdss-dsi-display-timings {
 		timing@0{
-			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
-				07 05 03 04 00];
 			qcom,display-topology = <1 0 1>,
-						<2 0 1>;
-			qcom,default-topology-index = <0>;
+						<2 2 1>;
+			qcom,default-topology-index = <1>;
+			qcom,panel-roi-alignment = <720 40 720 40 720 40>;
+			qcom,partial-update-enabled = "single_roi";
+			qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07
+				07 04 03 04 00];
+		};
+		timing@1{
+			qcom,display-topology = <1 0 1>,
+						<2 2 1>;
+			qcom,default-topology-index = <1>;
+			qcom,panel-roi-alignment = <540 40 540 40 540 40>;
+			qcom,partial-update-enabled = "single_roi";
+			qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07
+				07 04 03 04 00];
+		};
+		timing@2{
+			qcom,display-topology = <1 0 1>,
+						<2 2 1>;
+			qcom,default-topology-index = <1>;
+			qcom,panel-roi-alignment = <360 40 360 40 360 40>;
+			qcom,partial-update-enabled = "single_roi";
+			qcom,mdss-dsi-panel-phy-timings = [00 1a 06 06 22 20 07
+				07 04 03 04 00];
 		};
 	};
 };
@@ -692,8 +703,8 @@
 			qcom,default-topology-index = <0>;
 		};
 		timing@1{
-			qcom,mdss-dsi-panel-phy-timings = [00 1c 07 07 23 21 07
-				07 05 03 04 00];
+			qcom,mdss-dsi-panel-phy-timings = [00 30 0c 0d 2a 27 0c
+				0d 09 03 04 00];
 			qcom,display-topology = <2 0 2>,
 						<1 0 2>;
 			qcom,default-topology-index = <0>;
@@ -746,3 +757,45 @@
 		};
 	};
 };
+
+&dsi_dual_nt35597_video {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		 timing@0 {
+			qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+				05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt35597_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0d>;
+	qcom,mdss-dsi-t-clk-pre = <0x2d>;
+	qcom,mdss-dsi-display-timings {
+		 timing@0 {
+			qcom,mdss-dsi-panel-timings = [00 1c 08 07 23 22 07 07
+				05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+						<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
+
+&dsi_dual_nt36850_truly_cmd {
+	qcom,mdss-dsi-t-clk-post = <0x0E>;
+	qcom,mdss-dsi-t-clk-pre = <0x30>;
+	qcom,mdss-dsi-display-timings {
+		timing@0 {
+			qcom,mdss-dsi-panel-phy-timings = [00 1f 08 08 24 23 08
+				08 05 03 04 00];
+			qcom,display-topology = <2 0 2>,
+				<1 0 2>;
+			qcom,default-topology-index = <0>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
index b9eac3c..967865b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-pll.dtsi
@@ -23,6 +23,8 @@
 		clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
 		clock-names = "iface_clk";
 		clock-rate = <0>;
+		qcom,dsi-pll-ssc-en;
+		qcom,dsi-pll-ssc-mode = "down-spread";
 		gdsc-supply = <&mdss_core_gdsc>;
 		qcom,platform-supply-entries {
 			#address-cells = <1>;
@@ -50,6 +52,8 @@
 		clocks = <&clock_dispcc DISP_CC_MDSS_AHB_CLK>;
 		clock-names = "iface_clk";
 		clock-rate = <0>;
+		qcom,dsi-pll-ssc-en;
+		qcom,dsi-pll-ssc-mode = "down-spread";
 		gdsc-supply = <&mdss_core_gdsc>;
 		qcom,platform-supply-entries {
 			#address-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index 0618f92..4194e67 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -9,6 +9,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+#include <dt-bindings/clock/mdss-10nm-pll-clk.h>
 
 &soc {
 	mdss_mdp: qcom,mdss_mdp@ae00000 {
@@ -45,6 +46,8 @@
 		#address-cells = <1>;
 		#size-cells = <0>;
 
+		#power-domain-cells = <0>;
+
 		/* hw blocks */
 		qcom,sde-off = <0x1000>;
 		qcom,sde-len = <0x45C>;
@@ -52,10 +55,13 @@
 		qcom,sde-ctl-off = <0x2000 0x2200 0x2400
 				     0x2600 0x2800>;
 		qcom,sde-ctl-size = <0xE4>;
+		qcom,sde-ctl-display-pref = "primary", "primary", "none",
+					    "none", "none";
 
-		qcom,sde-mixer-off = <0x45000 0x46000 0x47000
-				      0x48000 0x49000 0x4a000>;
+		qcom,sde-mixer-off = <0x45000 0x46000 0x47000 0 0 0x4a000>;
 		qcom,sde-mixer-size = <0x320>;
+		qcom,sde-mixer-display-pref = "primary", "primary", "none",
+					      "none", "none", "none";
 
 		qcom,sde-dspp-top-off = <0x1300>;
 		qcom,sde-dspp-top-size = <0xc>;
@@ -63,6 +69,11 @@
 		qcom,sde-dspp-off = <0x55000 0x57000 0x59000 0x5b000>;
 		qcom,sde-dspp-size = <0x17e0>;
 
+		qcom,sde-dest-scaler-top-off = <0x00061000>;
+		qcom,sde-dest-scaler-top-size = <0xc>;
+		qcom,sde-dest-scaler-off = <0x800 0x1000>;
+		qcom,sde-dest-scaler-size = <0x800>;
+
 		qcom,sde-wb-off = <0x66000>;
 		qcom,sde-wb-size = <0x2c8>;
 		qcom,sde-wb-xin-id = <6>;
@@ -123,13 +134,20 @@
 		qcom,sde-mixer-blendstages = <0xb>;
 		qcom,sde-highest-bank-bit = <0x2>;
 		qcom,sde-ubwc-version = <0x200>;
+		qcom,sde-smart-panel-align-mode = <0xc>;
 		qcom,sde-panic-per-pipe;
 		qcom,sde-has-cdp;
 		qcom,sde-has-src-split;
 		qcom,sde-has-dim-layer;
 		qcom,sde-has-idle-pc;
-		qcom,sde-max-bw-low-kbps = <9600000>;
-		qcom,sde-max-bw-high-kbps = <9600000>;
+		qcom,sde-has-dest-scaler;
+		qcom,sde-max-dest-scaler-input-linewidth = <2048>;
+		qcom,sde-max-dest-scaler-output-linewidth = <2560>;
+		qcom,sde-max-bw-low-kbps = <6800000>;
+		qcom,sde-max-bw-high-kbps = <6800000>;
+		qcom,sde-min-core-ib-kbps = <2400000>;
+		qcom,sde-min-llcc-ib-kbps = <800000>;
+		qcom,sde-min-dram-ib-kbps = <800000>;
 		qcom,sde-dram-channels = <2>;
 		qcom,sde-num-nrt-paths = <0>;
 		qcom,sde-dspp-ad-version = <0x00040000>;
@@ -146,7 +164,18 @@
 
 		qcom,sde-danger-lut = <0x0000000f 0x0000ffff 0x00000000
 			0x00000000>;
-		qcom,sde-safe-lut = <0xfffc 0xff00 0xffff 0xffff>;
+		qcom,sde-safe-lut-linear =
+			<4 0xfff8>,
+			<0 0xfff0>;
+		qcom,sde-safe-lut-macrotile =
+			<10 0xfe00>,
+			<11 0xfc00>,
+			<12 0xf800>,
+			<0 0xf000>;
+		qcom,sde-safe-lut-nrt =
+			<0 0xffff>;
+		qcom,sde-safe-lut-cwb =
+			<0 0xffff>;
 		qcom,sde-qos-lut-linear =
 			<4 0x00000000 0x00000357>,
 			<5 0x00000000 0x00003357>,
@@ -174,6 +203,9 @@
 
 		qcom,sde-cdp-setting = <1 1>, <1 0>;
 
+		qcom,sde-qos-cpu-mask = <0x3>;
+		qcom,sde-qos-cpu-dma-latency = <300>;
+
 		qcom,sde-inline-rotator = <&mdss_rotator 0>;
 		qcom,sde-inline-rot-xin = <10 11>;
 		qcom,sde-inline-rot-xin-type = "sspp", "wb";
@@ -193,10 +225,14 @@
 
 		qcom,sde-dspp-blocks {
 			qcom,sde-dspp-igc = <0x0 0x00030001>;
+			qcom,sde-dspp-hsic = <0x800 0x00010007>;
+			qcom,sde-dspp-memcolor = <0x880 0x00010007>;
+			qcom,sde-dspp-sixzone= <0x900 0x00010007>;
 			qcom,sde-dspp-vlut = <0xa00 0x00010008>;
 			qcom,sde-dspp-gamut = <0x1000 0x00040000>;
 			qcom,sde-dspp-pcc = <0x1700 0x00040000>;
 			qcom,sde-dspp-gc = <0x17c0 0x00010008>;
+			qcom,sde-dspp-hist = <0x800 0x00010007>;
 		};
 
 		qcom,platform-supply-entries {
@@ -353,6 +389,8 @@
 		interrupt-parent = <&mdss_mdp>;
 		interrupts = <2 0>;
 
+		power-domains = <&mdss_mdp>;
+
 		/* Offline rotator QoS setting */
 		qcom,mdss-rot-vbif-qos-setting = <3 3 3 3 3 3 3 3>;
 		qcom,mdss-rot-vbif-memtype = <3 3>;
@@ -417,7 +455,7 @@
 		clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk",
 					"pixel_clk", "pixel_clk_rcg",
 					"esc_clk";
-
+		qcom,null-insertion-enabled;
 		qcom,ctrl-supply-entries {
 			#address-cells = <1>;
 			#size-cells = <0>;
@@ -451,6 +489,7 @@
 		<&clock_dispcc DISP_CC_MDSS_ESC1_CLK>;
 		clock-names = "byte_clk", "byte_clk_rcg", "byte_intf_clk",
 				"pixel_clk", "pixel_clk_rcg", "esc_clk";
+		qcom,null-insertion-enabled;
 		qcom,ctrl-supply-entries {
 			#address-cells = <1>;
 			#size-cells = <0>;
@@ -532,4 +571,91 @@
 		};
 	};
 
+	sde_dp: qcom,dp_display@0{
+		cell-index = <0>;
+		compatible = "qcom,dp-display";
+
+		gdsc-supply = <&mdss_core_gdsc>;
+		vdda-1p2-supply = <&pm8998_l26>;
+		vdda-0p9-supply = <&pm8998_l1>;
+
+		reg =	<0xae90000 0x0dc>,
+			<0xae90200 0x0c0>,
+			<0xae90400 0x508>,
+			<0xae90a00 0x094>,
+			<0x88eaa00 0x200>,
+			<0x88ea200 0x200>,
+			<0x88ea600 0x200>,
+			<0xaf02000 0x1a0>,
+			<0x780000 0x621c>,
+			<0x88ea030 0x10>,
+			<0x88e8000 0x20>,
+			<0x0aee1000 0x034>;
+		/* dp_ctrl: dp_ahb, dp_aux, dp_link, dp_p0 */
+		reg-names = "dp_ahb", "dp_aux", "dp_link",
+			"dp_p0", "dp_phy", "dp_ln_tx0", "dp_ln_tx1",
+			"dp_mmss_cc", "qfprom_physical", "dp_pll",
+			"usb3_dp_com", "hdcp_physical";
+
+		interrupt-parent = <&mdss_mdp>;
+		interrupts = <12 0>;
+
+		clocks =  <&clock_dispcc DISP_CC_MDSS_DP_AUX_CLK>,
+			 <&clock_rpmh RPMH_CXO_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_CLKREF_CLK>,
+			 <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
+			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
+			 <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>;
+		clock-names = "core_aux_clk", "core_usb_ref_clk_src",
+			"core_usb_ref_clk", "core_usb_cfg_ahb_clk",
+			"core_usb_pipe_clk", "ctrl_link_clk",
+			"ctrl_link_iface_clk", "ctrl_pixel_clk",
+			"crypto_clk", "pixel_clk_rcg", "pixel_parent";
+
+		qcom,aux-cfg0-settings = [20 00];
+		qcom,aux-cfg1-settings = [24 13 23 1d];
+		qcom,aux-cfg2-settings = [28 24];
+		qcom,aux-cfg3-settings = [2c 00];
+		qcom,aux-cfg4-settings = [30 0a];
+		qcom,aux-cfg5-settings = [34 26];
+		qcom,aux-cfg6-settings = [38 0a];
+		qcom,aux-cfg7-settings = [3c 03];
+		qcom,aux-cfg8-settings = [40 bb];
+		qcom,aux-cfg9-settings = [44 03];
+
+		qcom,max-pclk-frequency-khz = <675000>;
+
+		qcom,ctrl-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,ctrl-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-1p2";
+				qcom,supply-min-voltage = <1200000>;
+				qcom,supply-max-voltage = <1200000>;
+				qcom,supply-enable-load = <21800>;
+				qcom,supply-disable-load = <4>;
+			};
+		};
+
+		qcom,phy-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,phy-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdda-0p9";
+				qcom,supply-min-voltage = <880000>;
+				qcom,supply-max-voltage = <880000>;
+				qcom,supply-enable-load = <36000>;
+				qcom,supply-disable-load = <32>;
+			};
+		};
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 16d0988..b9eabcf 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -78,6 +78,8 @@
 			snps,disable-clk-gating;
 			snps,has-lpm-erratum;
 			snps,hird-threshold = /bits/ 8 <0x10>;
+			snps,usb3_lpm_capable;
+			usb-core-id = <0>;
 		};
 
 		qcom,usbbam@a704000 {
@@ -116,8 +118,10 @@
 	qusb_phy0: qusb@88e2000 {
 		compatible = "qcom,qusb2phy-v2";
 		reg = <0x088e2000 0x400>,
-			<0x007801e8 0x4>;
-		reg-names = "qusb_phy_base", "efuse_addr";
+			<0x007801e8 0x4>,
+			<0x088e7014 0x4>;
+		reg-names = "qusb_phy_base", "efuse_addr",
+				"refgen_north_bg_reg_addr";
 
 		qcom,efuse-bit-pos = <25>;
 		qcom,efuse-num-bits = <3>;
@@ -131,7 +135,10 @@
 			 0x210 /* QUSB2PHY_PWR_CTRL1 */
 			 0x230 /* QUSB2PHY_INTR_CTRL */
 			 0x0a8 /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE */
-			 0x254>; /* QUSB2PHY_TEST1 */
+			 0x254 /* QUSB2PHY_TEST1 */
+			 0x198 /* PLL_BIAS_CONTROL_2 */
+			 0x228 /* QUSB2PHY_SQ_CTRL1 */
+			 0x22c>; /* QUSB2PHY_SQ_CTRL2 */
 
 		qcom,qusb-phy-init-seq =
 			/* <value reg_offset> */
@@ -219,6 +226,8 @@
 			 0x14fc 0x80 0x00 /* RXA_RX_OFFSET_ADAPTOR_CNTRL2 */
 			 0x1504 0x03 0x00 /* RXA_SIGDET_CNTRL */
 			 0x150c 0x16 0x00 /* RXA_SIGDET_DEGLITCH_CNTRL */
+			 0x1564 0x05 0x00 /* RXA_RX_MODE_00 */
+			 0x14c0 0x03 0x00 /* RXA_VGA_CAL_CNTRL2 */
 			 0x1830 0x0b 0x00 /* RXB_UCDR_FASTLOCK_FO_GAIN */
 			 0x18d4 0x0f 0x00 /* RXB_RX_EQU_ADAPTOR_CNTRL2 */
 			 0x18d8 0x4e 0x00 /* RXB_RX_EQU_ADAPTOR_CNTRL3 */
@@ -227,6 +236,8 @@
 			 0x18fc 0x80 0x00 /* RXB_RX_OFFSET_ADAPTOR_CNTRL2 */
 			 0x1904 0x03 0x00 /* RXB_SIGDET_CNTRL */
 			 0x190c 0x16 0x00 /* RXB_SIGDET_DEGLITCH_CNTRL */
+			 0x1964 0x05 0x00 /* RXB_RX_MODE_00 */
+			 0x18c0 0x03 0x00 /* RXB_VGA_CAL_CNTRL2 */
 			 0x1260 0x10 0x00 /* TXA_HIGHZ_DRVR_EN */
 			 0x12a4 0x12 0x00 /* TXA_RCV_DETECT_LVL_2 */
 			 0x128c 0x16 0x00 /* TXA_LANE_MODE_1 */
@@ -267,6 +278,8 @@
 			 0x1c48 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_V4 */
 			 0x1c4c 0x15 0x00 /* PCS_TXDEEMPH_M6DB_LS */
 			 0x1c50 0x0d 0x00 /* PCS_TXDEEMPH_M3P5DB_LS */
+			 0x1e0c 0x21 0x00 /* PCS_REFGEN_REQ_CONFIG1 */
+			 0x1e10 0x60 0x00 /* PCS_REFGEN_REQ_CONFIG2 */
 			 0x1c5c 0x02 0x00 /* PCS_RATE_SLEW_CNTRL */
 			 0x1ca0 0x04 0x00 /* PCS_PWRUP_RESET_DLY_TIME_AUXCLK */
 			 0x1c8c 0x44 0x00 /* PCS_TSYNC_RSYNC_TIME */
@@ -277,6 +290,7 @@
 			 0x1cb8 0x75 0x00 /* PCS_RXEQTRAINING_WAIT_TIME */
 			 0x1cb0 0x86 0x00 /* PCS_LFPS_TX_ECSTART_EQTLOCK */
 			 0x1cbc 0x13 0x00 /* PCS_RXEQTRAINING_RUN_TIME */
+			 0x1cac 0x04 0x00 /* PCS_LFPS_DET_HIGH_COUNT_VAL */
 			 0xffffffff 0xffffffff 0x00>;
 
 		qcom,qmp-phy-reg-offset =
@@ -385,14 +399,18 @@
 			snps,disable-clk-gating;
 			snps,has-lpm-erratum;
 			snps,hird-threshold = /bits/ 8 <0x10>;
+			snps,usb3_lpm_capable;
+			usb-core-id = <1>;
 		};
 	};
 
 	/* Secondary USB port related QUSB2 PHY */
 	qusb_phy1: qusb@88e3000 {
 		compatible = "qcom,qusb2phy-v2";
-		reg = <0x088e3000 0x400>;
-		reg-names = "qusb_phy_base";
+		reg = <0x088e3000 0x400>,
+			<0x088e7014 0x4>;
+		reg-names = "qusb_phy_base",
+				"refgen_north_bg_reg_addr";
 
 		vdd-supply = <&pm8998_l1>;
 		vdda18-supply = <&pm8998_l12>;
@@ -404,7 +422,10 @@
 			 0x210 /* QUSB2PHY_PWR_CTRL1 */
 			 0x230 /* QUSB2PHY_INTR_CTRL */
 			 0x0a8 /* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE */
-			 0x254>; /* QUSB2PHY_TEST1 */
+			 0x254 /* QUSB2PHY_TEST1 */
+			 0x198 /* PLL_BIAS_CONTROL_2 */
+			 0x228 /* QUSB2PHY_SQ_CTRL1 */
+			 0x22c>; /* QUSB2PHY_SQ_CTRL2 */
 
 		qcom,qusb-phy-init-seq =
 			/* <value reg_offset> */
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..5b3e964
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel CDP";
+	compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..0d6c5e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel MTP";
+	compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..764c145
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2 4K Display Panel QRD";
+	compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+	qcom,msm-id = <321 0x20000>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index c42a7be..d2ee9eb 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -24,8 +24,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -60,8 +59,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -97,8 +95,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -124,7 +121,7 @@
 	cam_csiphy3: qcom,csiphy@ac68000 {
 		cell-index = <3>;
 		compatible = "qcom,csiphy-v1.0", "qcom,csiphy";
-		reg = <0xac67000 0x1000>;
+		reg = <0xac68000 0x1000>;
 		reg-names = "csiphy";
 		reg-cam-base = <0x68000>;
 		interrupts = <0 448 0>;
@@ -133,8 +130,7 @@
 		gdscr-supply = <&titan_top_gdsc>;
 		refgen-supply = <&refgen>;
 		csi-vdd-voltage = <1200000>;
-		mipi-csi-vdd-supply = <&pm8998_l26>;
-		mipi-dsi-vdd-supply = <&pm8998_l1>;
+		mipi-csi-vdd-supply = <&pm8998_l1>;
 		clocks = <&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
 			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
 			<&clock_camcc CAM_CC_SLOW_AHB_CLK_SRC>,
@@ -161,6 +157,33 @@
 		compatible = "qcom,msm-cam-smmu";
 		status = "ok";
 
+		msm_cam_smmu_lrme {
+			compatible = "qcom,msm-cam-smmu-cb";
+			iommus = <&apps_smmu 0x1038 0x0>,
+				<&apps_smmu 0x1058 0x0>,
+				<&apps_smmu 0x1039 0x0>,
+				<&apps_smmu 0x1059 0x0>;
+			label = "lrme";
+			lrme_iova_mem_map: iova-mem-map {
+				iova-mem-region-shared {
+					/* Shared region is 100MB long */
+					iova-region-name = "shared";
+					iova-region-start = <0x7400000>;
+					iova-region-len = <0x6400000>;
+					iova-region-id = <0x1>;
+					status = "ok";
+				};
+				/* IO region is approximately 3.3 GB */
+				iova-mem-region-io {
+					iova-region-name = "io";
+					iova-region-start = <0xd800000>;
+					iova-region-len = <0xd2800000>;
+					iova-region-id = <0x3>;
+					status = "ok";
+				};
+			};
+		};
+
 		msm_cam_smmu_ife {
 			compatible = "qcom,msm-cam-smmu-cb";
 			iommus = <&apps_smmu 0x808 0x0>,
@@ -214,11 +237,20 @@
 					status = "ok";
 				};
 
+				iova-mem-region-secondary-heap {
+					/* Secondary heap region is 1MB long */
+					iova-region-name = "secheap";
+					iova-region-start = <0xd800000>;
+					iova-region-len = <0x100000>;
+					iova-region-id = <0x4>;
+					status = "ok";
+				};
+
 				iova-mem-region-io {
 					/* IO region is approximately 3.3 GB */
 					iova-region-name = "io";
-					iova-region-start = <0xd800000>;
-					iova-region-len = <0xd2800000>;
+					iova-region-start = <0xd900000>;
+					iova-region-len = <0xd2700000>;
 					iova-region-id = <0x3>;
 					status = "ok";
 				};
@@ -243,18 +275,8 @@
 
 		msm_cam_smmu_secure {
 			compatible = "qcom,msm-cam-smmu-cb";
-			iommus = <&apps_smmu 0x1001 0x0>;
 			label = "cam-secure";
-			cam_secure_iova_mem_map: iova-mem-map {
-				/* Secure IO region is approximately 3.4 GB */
-				iova-mem-region-io {
-					iova-region-name = "io";
-					iova-region-start = <0x7400000>;
-					iova-region-len = <0xd8c00000>;
-					iova-region-id = <0x3>;
-					status = "ok";
-				};
-			};
+			qcom,secure-cb;
 		};
 	};
 
@@ -270,6 +292,7 @@
 		reg-cam-base = <0x40000 0x42000>;
 		interrupt-names = "cpas_camnoc";
 		interrupts = <0 459 0>;
+		qcom,cpas-hw-ver = <0x170110>; /* Titan v170 v1.1.0 */
 		regulator-names = "camss-vdd";
 		camss-vdd-supply = <&titan_top_gdsc>;
 		clock-names = "gcc_ahb_clk",
@@ -301,17 +324,17 @@
 			<MSM_BUS_MASTER_AMPSS_M0
 			MSM_BUS_SLAVE_CAMERA_CFG 0 0>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 153000>,
+			MSM_BUS_SLAVE_CAMERA_CFG 0 76500>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 153000>,
+			MSM_BUS_SLAVE_CAMERA_CFG 0 76500>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
+			<MSM_BUS_MASTER_AMPSS_M0
+			MSM_BUS_SLAVE_CAMERA_CFG 0 150000>,
 			<MSM_BUS_MASTER_AMPSS_M0
 			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>,
 			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>,
-			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 600000>,
-			<MSM_BUS_MASTER_AMPSS_M0
-			MSM_BUS_SLAVE_CAMERA_CFG 0 600000>;
+			MSM_BUS_SLAVE_CAMERA_CFG 0 300000>;
 		vdd-corners = <RPMH_REGULATOR_LEVEL_OFF
 			RPMH_REGULATOR_LEVEL_RETENTION
 			RPMH_REGULATOR_LEVEL_MIN_SVS
@@ -329,17 +352,18 @@
 			"turbo", "turbo";
 		client-id-based;
 		client-names =
-			"csiphy0", "csiphy1", "csiphy2", "cci0",
+			"csiphy0", "csiphy1", "csiphy2", "csiphy3", "cci0",
 			"csid0", "csid1", "csid2",
 			"ife0", "ife1", "ife2", "ipe0",
 			"ipe1", "cam-cdm-intf0", "cpas-cdm0", "bps0",
-			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0";
+			"icp0", "jpeg-dma0", "jpeg-enc0", "fd0", "lrmecpas0";
 		client-axi-port-names =
-			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
-			"cam_hf_1", "cam_hf_2", "cam_hf_2",
+			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_hf_2",
+			"cam_sf_1", "cam_hf_1", "cam_hf_2", "cam_hf_2",
 			"cam_hf_1", "cam_hf_2", "cam_hf_2", "cam_sf_1",
 			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
-			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1";
+			"cam_sf_1", "cam_sf_1", "cam_sf_1", "cam_sf_1",
+			"cam_sf_1";
 		client-bus-camnoc-based;
 		qcom,axi-port-list {
 			qcom,axi-port1 {
@@ -419,4 +443,44 @@
 			};
 		};
 	};
+
+	qcom,cam-lrme {
+		compatible = "qcom,cam-lrme";
+		arch-compat = "lrme";
+		status = "ok";
+	};
+
+	cam_lrme: qcom,lrme@ac6b000 {
+		cell-index = <0>;
+		compatible = "qcom,lrme";
+		reg-names = "lrme";
+		reg = <0xac6b000 0xa00>;
+		reg-cam-base = <0x6b000>;
+		interrupt-names = "lrme";
+		interrupts = <0 476 0>;
+		regulator-names = "camss";
+		camss-supply = <&titan_top_gdsc>;
+		clock-names = "camera_ahb",
+			"camera_axi",
+			"soc_ahb_clk",
+			"cpas_ahb_clk",
+			"camnoc_axi_clk",
+			"lrme_clk_src",
+			"lrme_clk";
+		clocks = <&clock_gcc GCC_CAMERA_AHB_CLK>,
+			<&clock_gcc GCC_CAMERA_AXI_CLK>,
+			<&clock_camcc CAM_CC_SOC_AHB_CLK>,
+			<&clock_camcc CAM_CC_CPAS_AHB_CLK>,
+			<&clock_camcc CAM_CC_CAMNOC_AXI_CLK>,
+			<&clock_camcc CAM_CC_LRME_CLK_SRC>,
+			<&clock_camcc CAM_CC_LRME_CLK>;
+		clock-rates = <0 0 0 0 0 200000000 200000000>,
+			<0 0 0 0 0 269000000 269000000>,
+			<0 0 0 0 0 320000000 320000000>,
+			<0 0 0 0 0 400000000 400000000>;
+
+		clock-cntl-level = "lowsvs", "svs", "svs_l1", "turbo";
+		src-clock-name = "lrme_clk_src";
+		status = "ok";
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-qvr-overlay.dts
similarity index 84%
rename from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
rename to arch/arm64/boot/dts/qcom/sdm845-v2-qvr-overlay.dts
index 2fac9e8..e1ec364 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-qvr-overlay.dts
@@ -19,10 +19,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
+#include "sdm845-sde-display.dtsi"
 #include "sdm845-qvr.dtsi"
+#include "sdm845-qvr-audio-overlay.dtsi"
+#include "sdm845-camera-sensor-qvr.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
+	model = "Qualcomm Technologies, Inc. SDM845 V2 QVR";
 	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
 	qcom,msm-id = <321 0x20000>;
 	qcom,board-id = <0x01000B 0x20>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm845-v2-qvr.dts
similarity index 87%
rename from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
rename to arch/arm64/boot/dts/qcom/sdm845-v2-qvr.dts
index c06b806..0a56c79 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-qvr.dts
@@ -15,9 +15,10 @@
 
 #include "sdm845-v2.dtsi"
 #include "sdm845-qvr.dtsi"
+#include "sdm845-camera-sensor-qvr.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
+	model = "Qualcomm Technologies, Inc. SDM845 V2 QVR";
 	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
 	qcom,board-id = <0x01000B 0x20>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts
new file mode 100644
index 0000000..b7bc3dc
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-cdp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel CDP";
+	compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <1 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts
new file mode 100644
index 0000000..95bd94b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-mtp-overlay.dts
@@ -0,0 +1,66 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel MTP";
+	compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <8 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&mdss_mdp {
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts
new file mode 100644
index 0000000..42ccbf5
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-4k-panel-qrd-overlay.dts
@@ -0,0 +1,64 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+/dts-v1/;
+/plugin/;
+
+#include <dt-bindings/clock/qcom,gcc-sdm845.h>
+#include <dt-bindings/clock/qcom,camcc-sdm845.h>
+#include <dt-bindings/clock/qcom,dispcc-sdm845.h>
+#include <dt-bindings/clock/qcom,rpmh.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
+
+/ {
+	model = "Qualcomm Technologies, Inc. sdm845 v2.1 4K Display Panel QRD";
+	compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <11 1>;
+};
+
+&dsi_nt35597_truly_dsc_cmd_display {
+	/delete-property/ qcom,dsi-display-active;
+};
+
+&dsi_sharp_4k_dsc_video {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_cmd {
+	qcom,panel-supply-entries = <&dsi_panel_pwr_supply>;
+	qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	qcom,mdss-dsi-mode-sel-gpio-state = "dual_port";
+	qcom,panel-mode-gpio = <&tlmm 52 0>;
+	qcom,platform-te-gpio = <&tlmm 10 0>;
+	qcom,platform-reset-gpio = <&tlmm 6 0>;
+	qcom,mdss-dsi-panel-orientation = "180";
+};
+
+&dsi_sharp_4k_dsc_video_display {
+	qcom,dsi-display-active;
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
index 2fac9e8..0825f4b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
@@ -19,11 +19,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-cdp.dtsi"
+#include "sdm845-cdp-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM845 v2.1 CDP";
+	compatible = "qcom,sdm845-cdp", "qcom,sdm845", "qcom,cdp";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <1 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
index 2fac9e8..7b3573a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
@@ -19,11 +19,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-mtp.dtsi"
+#include "sdm845-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM845 v2.1 MTP";
+	compatible = "qcom,sdm845-mtp", "qcom,sdm845", "qcom,mtp";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <8 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
similarity index 74%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
index 2fac9e8..15d79c2 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
@@ -19,11 +19,13 @@
 #include <dt-bindings/clock/qcom,rpmh.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 
-#include "sdm845-qvr.dtsi"
+#include "sdm845-sde-display.dtsi"
+#include "sdm845-qrd.dtsi"
+#include "sdm845-qrd-audio-overlay.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,msm-id = <321 0x20000>;
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM845 v2.1 QRD";
+	compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <11 0>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
similarity index 73%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
index c06b806..2902a60 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
@@ -10,14 +10,13 @@
  * GNU General Public License for more details.
  */
 
-
 /dts-v1/;
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm845-v2.1.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM845 v2.1 SoC";
+	compatible = "qcom,sdm845";
+	qcom,board-id = <0 0>;
 };
+
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
similarity index 64%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
index c06b806..b298272 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
@@ -10,14 +10,22 @@
  * GNU General Public License for more details.
  */
 
-
-/dts-v1/;
-
 #include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
+#include "sdm845-v2-camera.dtsi"
 
 / {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+	model = "Qualcomm Technologies, Inc. SDM845 V2.1";
+	qcom,msm-id = <321 0x20001>;
+};
+
+&spmi_bus {
+	/delete-property/ qcom,enable-ahb-bus-workaround;
+};
+
+&clock_gcc {
+	compatible = "qcom,gcc-sdm845-v2.1", "syscon";
+};
+
+&apps_smmu {
+	/delete-property/ qcom,no-asid-retention;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index 98ab8d4..0f6650d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -22,447 +22,36 @@
 	/delete-property/ qcom,sdr104-wa;
 };
 
-/delete-node/ &apc0_cpr;
-/delete-node/ &apc1_cpr;
+/delete-node/ &pdc;
+
+&tlmm {
+	compatible = "qcom,sdm845-pinctrl-v2";
+};
 
 &soc {
-	/* CPR controller regulators */
-	apc0_cpr: cprh-ctrl@17dc0000 {
-		compatible = "qcom,cprh-sdm845-v2-kbss-regulator";
-		reg =	<0x17dc0000 0x4000>,
-			<0x00784000 0x1000>,
-			<0x17840000 0x1000>;
-		reg-names = "cpr_ctrl", "fuse_base", "saw";
-		clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
-		clock-names = "core_clk";
-		qcom,cpr-ctrl-name = "apc0";
-		qcom,cpr-controller-id = <0>;
+	qcom,memshare {
+		compatible = "qcom,memshare";
 
-		qcom,cpr-sensor-time = <1000>;
-		qcom,cpr-loop-time = <5000000>;
-		qcom,cpr-idle-cycles = <15>;
-		qcom,cpr-up-down-delay-time = <3000>;
-		qcom,cpr-step-quot-init-min = <11>;
-		qcom,cpr-step-quot-init-max = <12>;
-		qcom,cpr-count-mode = <0>;		/* All at once */
-		qcom,cpr-count-repeat = <20>;
-		qcom,cpr-down-error-step-limit = <1>;
-		qcom,cpr-up-error-step-limit = <1>;
-		qcom,cpr-corner-switch-delay-time = <1042>;
-		qcom,cpr-voltage-settling-time = <1760>;
-		qcom,cpr-reset-step-quot-loop-en;
-
-		qcom,voltage-step = <4000>;
-		qcom,voltage-base = <352000>;
-		qcom,cpr-saw-use-unit-mV;
-
-		qcom,saw-avs-ctrl = <0x101C031>;
-		qcom,saw-avs-limit = <0x3B803B8>;
-
-		qcom,cpr-enable;
-		qcom,cpr-hw-closed-loop;
-
-		qcom,cpr-panic-reg-addr-list =
-			<0x17dc3a84 0x17dc3a88 0x17840c18>;
-		qcom,cpr-panic-reg-name-list =
-			"APSS_SILVER_CPRH_STATUS_0",
-			"APSS_SILVER_CPRH_STATUS_1",
-			"SILVER_SAW4_PMIC_STS";
-
-		qcom,cpr-aging-ref-voltage = <1000000>;
-		vdd-supply = <&pm8998_s13>;
-
-		thread@0 {
-			qcom,cpr-thread-id = <0>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc0_pwrcl_vreg: regulator {
-				regulator-name = "apc0_pwrcl_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <18>;
-
-				qcom,cpr-fuse-corners = <4>;
-				qcom,cpr-fuse-combos = <16>;
-				qcom,cpr-speed-bins = <2>;
-				qcom,cpr-speed-bin-corners = <18 18>;
-				qcom,cpr-corners = <18>;
-
-				qcom,cpr-corner-fmax-map = <6 12 15 18>;
-
-				qcom,cpr-voltage-ceiling =
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 932000 1000000 1000000>;
-
-				qcom,cpr-voltage-floor =
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  40000  40000>;
-
-				qcom,corner-frequencies =
-					<300000000  403200000  480000000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1228800000
-					1324800000 1420800000 1516800000
-					1612800000 1689600000 1766400000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2594 2795 2576 2761 2469 2673 2198
-					 2553 3188 3255 3191 2962 3055 2984
-					 2043 2947>,
-					<2594 2795 2576 2761 2469 2673 2198
-					 2553 3188 3255 3191 2962 3055 2984
-					 2043 2947>,
-					<2259 2389 2387 2531 2294 2464 2218
-					 2476 2525 2855 2817 2836 2740 2490
-					 1950 2632>,
-					<2259 2389 2387 2531 2294 2464 2218
-					 2476 2525 2855 2817 2836 2740 2490
-					 1950 2632>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					<     0      0  12000  12000>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					<     0      0  12000  10000>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <18>;
-				qcom,cpr-aging-ro-scaling-factor = <1620>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
+		qcom,client_1 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x0>;
+			qcom,client-id = <0>;
+			qcom,allocate-boot-time;
+			label = "modem";
 		};
 
-		thread@1 {
-			qcom,cpr-thread-id = <1>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc0_l3_vreg: regulator {
-				regulator-name = "apc0_l3_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <15>;
-
-				qcom,cpr-fuse-corners = <4>;
-				qcom,cpr-fuse-combos = <16>;
-				qcom,cpr-speed-bins = <2>;
-				qcom,cpr-speed-bin-corners = <14 15>;
-				qcom,cpr-corners =
-					/* Speed bin 0 */
-					<14 14 14 14 14 14 14 14>,
-					/* Speed bin 1 */
-					<15 15 15 15 15 15 15 15>;
-
-				qcom,cpr-corner-fmax-map =
-					/* Speed bin 0 */
-					<4 8 11 14>,
-					/* Speed bin 1 */
-					<4 8 11 15>;
-
-				qcom,cpr-voltage-ceiling =
-					/* Speed bin 0 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  932000  932000 1000000>,
-					/* Speed bin 1 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  932000  932000 1000000
-					1000000>;
-
-				qcom,cpr-voltage-floor =
-					/* Speed bin 0 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000>,
-					/* Speed bin 1 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000
-					 568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					/* Speed bin 0 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  40000>,
-					/* Speed bin 1 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  40000  40000>;
-
-				qcom,corner-frequencies =
-					/* Speed bin 0 */
-					<300000000  403200000  480000000
-					 576000000  652800000  748800000
-					 844800000  940800000 1036800000
-					1132800000 1209600000 1305600000
-					1401600000 1478400000>,
-					/* Speed bin 1 */
-					<300000000  403200000  480000000
-					 576000000  652800000  748800000
-					 844800000  940800000 1036800000
-					1132800000 1209600000 1305600000
-					1401600000 1497600000 1593600000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2439 2577 2552 2667 2461 2577 2394
-					 2536 2132 2307 2191 2903 2838 2912
-					 2501 2095>,
-					<2439 2577 2552 2667 2461 2577 2394
-					 2536 2132 2307 2191 2903 2838 2912
-					 2501 2095>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					<  8000  16000  16000  12000>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					<  6000  14000  16000  12000>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <14 15>;
-				qcom,cpr-aging-ro-scaling-factor = <1620>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
+		qcom,client_2 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x0>;
+			qcom,client-id = <2>;
+			label = "modem";
 		};
-	};
 
-	apc1_cpr: cprh-ctrl@17db0000 {
-		compatible = "qcom,cprh-sdm845-v2-kbss-regulator";
-		reg =	<0x17db0000 0x4000>,
-			<0x00784000 0x1000>,
-			<0x17830000 0x1000>;
-		reg-names = "cpr_ctrl", "fuse_base", "saw";
-		clocks = <&clock_gcc GCC_CPUSS_RBCPR_CLK>;
-		clock-names = "core_clk";
-		qcom,cpr-ctrl-name = "apc1";
-		qcom,cpr-controller-id = <1>;
-
-		qcom,cpr-sensor-time = <1000>;
-		qcom,cpr-loop-time = <5000000>;
-		qcom,cpr-idle-cycles = <15>;
-		qcom,cpr-up-down-delay-time = <3000>;
-		qcom,cpr-step-quot-init-min = <9>;
-		qcom,cpr-step-quot-init-max = <14>;
-		qcom,cpr-count-mode = <0>;		/* All at once */
-		qcom,cpr-count-repeat = <20>;
-		qcom,cpr-down-error-step-limit = <1>;
-		qcom,cpr-up-error-step-limit = <1>;
-		qcom,cpr-corner-switch-delay-time = <1042>;
-		qcom,cpr-voltage-settling-time = <1760>;
-		qcom,cpr-reset-step-quot-loop-en;
-
-		qcom,apm-threshold-voltage = <800000>;
-		qcom,apm-crossover-voltage = <880000>;
-		qcom,mem-acc-threshold-voltage = <852000>;
-		qcom,mem-acc-crossover-voltage = <852000>;
-
-		qcom,voltage-step = <4000>;
-		qcom,voltage-base = <352000>;
-		qcom,cpr-saw-use-unit-mV;
-
-		qcom,saw-avs-ctrl = <0x101C031>;
-		qcom,saw-avs-limit = <0x4700470>;
-
-		qcom,cpr-enable;
-		qcom,cpr-hw-closed-loop;
-
-		qcom,cpr-panic-reg-addr-list =
-			<0x17db3a84 0x17830c18>;
-		qcom,cpr-panic-reg-name-list =
-			"APSS_GOLD_CPRH_STATUS_0", "GOLD_SAW4_PMIC_STS";
-
-		qcom,cpr-aging-ref-voltage = <1136000>;
-		vdd-supply = <&pm8998_s12>;
-
-		thread@0 {
-			qcom,cpr-thread-id = <0>;
-			qcom,cpr-consecutive-up = <0>;
-			qcom,cpr-consecutive-down = <0>;
-			qcom,cpr-up-threshold = <2>;
-			qcom,cpr-down-threshold = <2>;
-
-			apc1_perfcl_vreg: regulator {
-				regulator-name = "apc1_perfcl_corner";
-				regulator-min-microvolt = <1>;
-				regulator-max-microvolt = <33>;
-
-				qcom,cpr-fuse-corners = <5>;
-				qcom,cpr-fuse-combos = <16>;
-				qcom,cpr-speed-bins = <2>;
-				qcom,cpr-speed-bin-corners = <28 31>;
-				qcom,cpr-corners =
-					/* Speed bin 0 */
-					<28 28 28 28 28 28 28 28>,
-					/* Speed bin 1 */
-					<31 31 31 31 31 31 31 31>;
-
-				qcom,cpr-corner-fmax-map =
-					/* Speed bin 0 */
-					<7 14 22 27 28>,
-					/* Speed bin 1 */
-					<7 14 22 27 31>;
-
-				qcom,cpr-voltage-ceiling =
-					/* Speed bin 0 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  932000  932000
-					 932000  932000 1104000 1104000 1104000
-					1104000 1136000 1136000>,
-					/* Speed bin 1 */
-					<828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  828000  828000
-					 828000  828000  828000  932000  932000
-					 932000  932000 1104000 1104000 1104000
-					1104000 1136000 1136000 1136000 1136000
-					1136000>;
-
-				qcom,cpr-voltage-floor =
-					/* Speed bin 0 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000>,
-					/* Speed bin 1 */
-					<568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000  568000  568000  568000  568000
-					 568000>;
-
-				qcom,cpr-floor-to-ceiling-max-range =
-					/* Speed bin 0 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000>,
-					/* Speed bin 1 */
-					<32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  32000  32000  32000
-					 32000  32000  40000  40000  40000
-					 40000>;
-
-				qcom,corner-frequencies =
-					/* Speed bin 0 */
-					<300000000  403200000  480000000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1209600000
-					1286400000 1363200000 1459200000
-					1536000000 1612800000 1689600000
-					1766400000 1843200000 1920000000
-					1996800000 2092800000 2169600000
-					2246400000 2323200000 2400000000
-					2400000000>,
-					/* Speed bin 1 */
-					<300000000  403200000  480000000
-					 576000000  652800000  748800000
-					 825600000  902400000  979200000
-					1056000000 1132800000 1209600000
-					1286400000 1363200000 1459200000
-					1536000000 1612800000 1689600000
-					1766400000 1843200000 1920000000
-					1996800000 2092800000 2169600000
-					2246400000 2323200000 2400000000
-					2476800000 2553600000 2649600000
-					2707200000>;
-
-				qcom,cpr-ro-scaling-factor =
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2857 3056 2828 2952 2699 2796 2447
-					 2631 2630 2579 2244 3343 3287 3137
-					 3164 2656>,
-					<2086 2208 2273 2408 2203 2327 2213
-					 2340 1755 2039 2049 2474 2437 2618
-					 2003 1675>,
-					<2086 2208 2273 2408 2203 2327 2213
-					 2340 1755 2039 2049 2474 2437 2618
-					 2003 1675>,
-					<2086 2208 2273 2408 2203 2327 2213
-					 2340 1755 2039 2049 2474 2437 2618
-					 2003 1675>;
-
-				qcom,cpr-open-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<  8000   8000   8000      0      0>,
-					/* Speed bin 1 */
-					<  8000   8000   8000      0  16000>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<  6000   6000   8000      0      0>,
-					/* Speed bin 1 */
-					<  6000   6000   8000      0  16000>;
-
-				qcom,allow-voltage-interpolation;
-				qcom,allow-quotient-interpolation;
-				qcom,cpr-scaled-open-loop-voltage-as-ceiling;
-
-				qcom,cpr-aging-max-voltage-adjustment = <15000>;
-				qcom,cpr-aging-ref-corner = <27 31>;
-				qcom,cpr-aging-ro-scaling-factor = <1700>;
-				qcom,allow-aging-voltage-adjustment =
-					/* Speed bin 0 */
-					<0 1 1 1 1 1 1 1>,
-					/* Speed bin 1 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
+		mem_client_3_size: qcom,client_3 {
+			compatible = "qcom,memshare-peripheral";
+			qcom,peripheral-size = <0x500000>;
+			qcom,client-id = <1>;
+			label = "modem";
 		};
 	};
 
@@ -475,170 +64,29 @@
 		compatible = "syscon";
 		reg = <0x5091008 0x4>;
 	};
+
+	pdc: interrupt-controller@0xb220000{
+		compatible = "qcom,pdc-sdm845-v2";
+		reg = <0xb220000 0x400>;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&intc>;
+		interrupt-controller;
+	};
+
 };
 
-/* VDD_APC0 */
-&pm8998_s13 {
-	regulator-min-microvolt = <568000>;
-	regulator-max-microvolt = <1000000>;
-};
-
-/* VDD_APC1 */
-&pm8998_s12 {
-	regulator-min-microvolt = <568000>;
-	regulator-max-microvolt = <1136000>;
+&pil_modem {
+	qcom,mss_pdc_offset = <9>;
 };
 
 &clock_cpucc {
 	compatible = "qcom,clk-cpu-osm-v2";
-
-	vdd-l3-supply = <&apc0_l3_vreg>;
-	vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
-	vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
-	qcom,l3-speedbin0-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   844800000 0x4024062c 0x00002323 0x2 7 >,
-		<   940800000 0x40240731 0x00002727 0x2 8 >,
-		<  1036800000 0x40240836 0x00002b2b 0x2 9 >,
-		<  1132800000 0x402c093b 0x00002f2f 0x2 10 >,
-		<  1209600000 0x402c0a3f 0x00003232 0x2 11 >,
-		<  1305600000 0x40340b44 0x00003636 0x2 12 >,
-		<  1401600000 0x40340c49 0x00003a3a 0x2 13 >,
-		<  1478400000 0x403c0d4d 0x00003e3e 0x2 14 >;
-
-	qcom,l3-speedbin1-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   844800000 0x4024062c 0x00002323 0x2 7 >,
-		<   940800000 0x40240731 0x00002727 0x2 8 >,
-		<  1036800000 0x40240836 0x00002b2b 0x2 9 >,
-		<  1132800000 0x402c093b 0x00002f2f 0x2 10 >,
-		<  1209600000 0x402c0a3f 0x00003232 0x2 11 >,
-		<  1305600000 0x40340b44 0x00003636 0x2 12 >,
-		<  1401600000 0x40340c49 0x00003a3a 0x2 13 >,
-		<  1497600000 0x403c0d4e 0x00003e3e 0x2 14 >,
-		<  1593600000 0x403c0e53 0x00004242 0x2 15 >;
-
-	qcom,pwrcl-speedbin0-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   825600000 0x401c062b 0x00002222 0x1 7 >,
-		<   902400000 0x4024072f 0x00002626 0x1 8 >,
-		<   979200000 0x40240833 0x00002929 0x1 9 >,
-		<  1056000000 0x402c0937 0x00002c2c 0x2 10 >,
-		<  1132800000 0x402c0a3b 0x00002f2f 0x2 11 >,
-		<  1228800000 0x402c0b40 0x00003333 0x2 12 >,
-		<  1324800000 0x40340c45 0x00003737 0x2 13 >,
-		<  1420800000 0x40340d4a 0x00003b3b 0x2 14 >,
-		<  1516800000 0x403c0e4f 0x00003f3f 0x2 15 >,
-		<  1612800000 0x403c0f54 0x00004343 0x2 16 >,
-		<  1689600000 0x40441058 0x00004646 0x2 17 >,
-		<  1766400000 0x4044115c 0x00004a4a 0x2 18 >;
-
-	qcom,pwrcl-speedbin1-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   825600000 0x401c062b 0x00002222 0x1 7 >,
-		<   902400000 0x4024072f 0x00002626 0x1 8 >,
-		<   979200000 0x40240833 0x00002929 0x1 9 >,
-		<  1056000000 0x402c0937 0x00002c2c 0x2 10 >,
-		<  1132800000 0x402c0a3b 0x00002f2f 0x2 11 >,
-		<  1228800000 0x402c0b40 0x00003333 0x2 12 >,
-		<  1324800000 0x40340c45 0x00003737 0x2 13 >,
-		<  1420800000 0x40340d4a 0x00003b3b 0x2 14 >,
-		<  1516800000 0x403c0e4f 0x00003f3f 0x2 15 >,
-		<  1612800000 0x403c0f54 0x00004343 0x2 16 >,
-		<  1689600000 0x40441058 0x00004646 0x2 17 >,
-		<  1766400000 0x4044115c 0x00004a4a 0x2 18 >;
-
-	qcom,perfcl-speedbin0-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   825600000 0x401c062b 0x00002222 0x1 7 >,
-		<   902400000 0x4024072f 0x00002626 0x1 8 >,
-		<   979200000 0x40240833 0x00002929 0x1 9 >,
-		<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-		<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-		<  1209600000 0x402c0b3f 0x00003232 0x2 12 >,
-		<  1286400000 0x40340c43 0x00003636 0x2 13 >,
-		<  1363200000 0x40340d47 0x00003939 0x2 14 >,
-		<  1459200000 0x403c0e4c 0x00003d3d 0x2 15 >,
-		<  1536000000 0x403c0f50 0x00004040 0x2 16 >,
-		<  1612800000 0x403c1054 0x00004343 0x2 17 >,
-		<  1689600000 0x40441158 0x00004646 0x2 18 >,
-		<  1766400000 0x4044125c 0x00004a4a 0x2 19 >,
-		<  1843200000 0x40441360 0x00004d4d 0x2 20 >,
-		<  1920000000 0x404c1464 0x00005050 0x2 21 >,
-		<  1996800000 0x404c1568 0x00005353 0x2 22 >,
-		<  2092800000 0x4054166d 0x00005757 0x2 23 >,
-		<  2169600000 0x40541771 0x00005a5a 0x2 24 >,
-		<  2246400000 0x40541875 0x00005e5e 0x2 25 >,
-		<  2323200000 0x40541979 0x00006161 0x2 26 >,
-		<  2400000000 0x40541a7d 0x00006464 0x2 27 >;
-
-	qcom,perfcl-speedbin1-v0 =
-		<   300000000 0x000c000f 0x00002020 0x1 1 >,
-		<   403200000 0x500c0115 0x00002020 0x1 2 >,
-		<   480000000 0x50140219 0x00002020 0x1 3 >,
-		<   576000000 0x5014031e 0x00002020 0x1 4 >,
-		<   652800000 0x401c0422 0x00002020 0x1 5 >,
-		<   748800000 0x401c0527 0x00002020 0x1 6 >,
-		<   825600000 0x401c062b 0x00002222 0x1 7 >,
-		<   902400000 0x4024072f 0x00002626 0x1 8 >,
-		<   979200000 0x40240833 0x00002929 0x1 9 >,
-		<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-		<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-		<  1209600000 0x402c0b3f 0x00003232 0x2 12 >,
-		<  1286400000 0x40340c43 0x00003636 0x2 13 >,
-		<  1363200000 0x40340d47 0x00003939 0x2 14 >,
-		<  1459200000 0x403c0e4c 0x00003d3d 0x2 15 >,
-		<  1536000000 0x403c0f50 0x00004040 0x2 16 >,
-		<  1612800000 0x403c1054 0x00004343 0x2 17 >,
-		<  1689600000 0x40441158 0x00004646 0x2 18 >,
-		<  1766400000 0x4044125c 0x00004a4a 0x2 19 >,
-		<  1843200000 0x40441360 0x00004d4d 0x2 20 >,
-		<  1920000000 0x404c1464 0x00005050 0x2 21 >,
-		<  1996800000 0x404c1568 0x00005353 0x2 22 >,
-		<  2092800000 0x4054166d 0x00005757 0x2 23 >,
-		<  2169600000 0x40541771 0x00005a5a 0x2 24 >,
-		<  2246400000 0x40541875 0x00005e5e 0x2 25 >,
-		<  2323200000 0x40541979 0x00006161 0x2 26 >,
-		<  2400000000 0x40541a7d 0x00006464 0x2 27 >,
-		<  2476800000 0x40541b81 0x00006767 0x2 28 >,
-		<  2553600000 0x40541c85 0x00006a6a 0x2 29 >,
-		<  2649600000 0x40541d8a 0x00006e6e 0x2 30 >,
-		<  2745600000 0x40511e8f 0x00007272 0x2 31 >;
-
-	qcom,l3-memacc-level-vc-bin0 = <8 13>;
-	qcom,l3-memacc-level-vc-bin1 = <8 13>;
-
-	qcom,pwrcl-memacc-level-vc-bin0 = <12 16>;
-	qcom,pwrcl-memacc-level-vc-bin1 = <12 16>;
-
-	qcom,perfcl-memacc-level-vc-bin0 = <14 22>;
-	qcom,perfcl-memacc-level-vc-bin1 = <14 22>;
+	reg = <0x17d41000 0x1400>,
+		<0x17d43000 0x1400>,
+		<0x17d45800 0x1400>,
+		<0x78425c 0x4>;
+	reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
+							"cpr_rc";
 };
 
 &pcie1 {
@@ -823,9 +271,8 @@
 		< 1324800 1036800000 >,
 		< 1420800 1132800000 >,
 		< 1516800 1209600000 >,
-		< 1612800 1401600000 >,
-		< 1689600 1497600000 >,
-		< 1766400 1593600000 >;
+		< 1689600 1305600000 >,
+		< 1766400 1401600000 >;
 };
 
 &devfreq_l3lat_4 {
@@ -835,20 +282,48 @@
 		< 1132800  748800000 >,
 		< 1363200  940800000 >,
 		< 1689600 1209600000 >,
-		< 1996800 1401600000 >,
-		< 2400000 1593600000 >;
+		< 1996800 1305600000 >,
+		< 2400000 1401600000 >,
+		< 2745600 1593600000 >;
 };
 
 &bwmon {
 	qcom,count-unit = <0x10000>;
 };
 
+&cpubw {
+	qcom,bw-tbl =
+		< MHZ_TO_MBPS(150, 16) >, /*  2288 MB/s */
+		< MHZ_TO_MBPS(300, 16) >, /*  4577 MB/s */
+		< MHZ_TO_MBPS(426, 16) >, /*  6500 MB/s */
+		< MHZ_TO_MBPS(533, 16) >, /*  8132 MB/s */
+		< MHZ_TO_MBPS(600, 16) >, /*  9155 MB/s */
+		< MHZ_TO_MBPS(806, 16) >, /* 12298 MB/s */
+		< MHZ_TO_MBPS(933, 16) >; /* 14236 MB/s */
+};
+
+&devfreq_cpufreq {
+	mincpubw-cpufreq {
+		cpu-to-dev-map-4 =
+			< 1881600 MHZ_TO_MBPS(200, 4) >,
+			< 2400000 MHZ_TO_MBPS(1017, 4) >;
+	};
+};
+
+&devfreq_compute {
+	qcom,core-dev-table =
+		< 1881600 MHZ_TO_MBPS( 200, 4) >,
+		< 2649600 MHZ_TO_MBPS(1017, 4) >,
+		< 2745600 MHZ_TO_MBPS(1804, 4) >;
+};
+
 &clock_gcc {
 	compatible = "qcom,gcc-sdm845-v2", "syscon";
 };
 
 &clock_camcc {
 	compatible = "qcom,cam_cc-sdm845-v2", "syscon";
+	qcom,cam_cc_csi3phytimer_clk_src-opp-handle = <&cam_csiphy3>;
 };
 
 &clock_dispcc {
@@ -874,7 +349,6 @@
 
 &refgen {
 	status = "ok";
-	regulator-always-on;
 };
 
 &spss_utils {
@@ -885,15 +359,66 @@
 
 &mdss_mdp {
 	clock-max-rate = <0 0 0 0 430000000 19200000 0>;
+	qcom,sde-min-core-ib-kbps = <4800000>;
+	qcom,sde-max-bw-low-kbps = <9600000>;
+	qcom,sde-max-bw-high-kbps = <9600000>;
+};
+
+&mdss_dsi0 {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
+};
+
+&mdss_dsi1 {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
+};
+
+&sde_dp {
+	qcom,core-supply-entries {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,core-supply-entry@0 {
+			reg = <0>;
+			qcom,supply-name = "refgen";
+			qcom,supply-min-voltage = <0>;
+			qcom,supply-max-voltage = <0>;
+			qcom,supply-enable-load = <0>;
+			qcom,supply-disable-load = <0>;
+		};
+	};
 };
 
 &energy_costs {
 	CPU_COST_0: core-cost0 {
 		busy-cost-data = <
-			 300000 11
+			 300000 12
 			 403200 17
 			 480000 21
-			 576000 26
+			 576000 27
 			 652800 31
 			 748800 37
 			 825600 42
@@ -901,13 +426,13 @@
 			 979200 52
 			1056000 57
 			1132800 62
-			1228800 69
+			1228800 70
 			1324800 78
 			1420800 89
 			1516800 103
 			1612800 122
-			1689600 140
-			1766400 159
+			1689600 141
+			1766400 160
 		>;
 		idle-cost-data = <
 			22 18 14 12
@@ -915,37 +440,40 @@
 	};
 	CPU_COST_1: core-cost1 {
 		busy-cost-data = <
-			 300000 130
-			 403200 480
-			 480000 730
-			 576000 1030
-			 652800 1260
-			 748800 1530
-			 825600 1740
-			 902400 1930
-			 979200 2110
-			1056000 2290
-			1132800 2460
-			1209600 2630
-			1286400 2800
-			1363200 2980
-			1459200 3240
-			1536000 3490
-			1612800 3780
-			1689600 4120
-			1766400 4530
-			1843200 5020
-			1920000 5590
-			1996800 6230
+			 300000 189
+			 403200 523
+			 480000 763
+			 576000 1052
+			 652800 1273
+			 748800 1536
+			 825600 1736
+			 902400 1926
+			 979200 2108
+			1056000 2284
+			1132800 2456
+			1209600 2628
+			1286400 2804
+			1363200 2992
+			1459200 3255
+			1536000 3499
+			1612800 3786
+			1689600 4128
+			1766400 4535
+			1843200 5019
+			1920000 5583
+			1996800 6226
 			2092800 7120
-			2169600 7870
-			2246400 8620
-			2323200 9330
+			2169600 7876
+			2246400 8628
+			2323200 9344
 			2400000 10030
-			2476800 10830
-			2553600 12080
-			2630400 14580
-			2707200 19960
+			2476800 10806
+			2553600 12045
+			2649600 15686
+			2745600 25586
+			2764800 30000
+			2784000 35000
+			2803200 40000
 		>;
 		idle-cost-data = <
 			100 80 60 40
@@ -1007,8 +535,11 @@
 			2400000 135
 			2476800 140
 			2553600 145
-			2630400 150
-			2707200 155
+			2649600 150
+			2745600 155
+			2764800 160
+			2784000 165
+			2803200 170
 		>;
 		idle-cost-data = <
 			4 3 2 1
@@ -1026,7 +557,7 @@
 &msm_gpu {
 	/* Updated chip ID */
 	qcom,chipid = <0x06030001>;
-	qcom,initial-pwrlevel = <5>;
+	qcom,initial-pwrlevel = <6>;
 
 	qcom,gpu-pwrlevels {
 		#address-cells = <1>;
@@ -1036,54 +567,62 @@
 
 		qcom,gpu-pwrlevel@0 {
 			reg = <0>;
+			qcom,gpu-freq = <710000000>;
+			qcom,bus-freq = <12>;
+			qcom,bus-min = <12>;
+			qcom,bus-max = <12>;
+		};
+
+		qcom,gpu-pwrlevel@1 {
+			reg = <1>;
 			qcom,gpu-freq = <675000000>;
 			qcom,bus-freq = <12>;
 			qcom,bus-min = <10>;
 			qcom,bus-max = <12>;
 		};
 
-		qcom,gpu-pwrlevel@1 {
-			reg = <1>;
+		qcom,gpu-pwrlevel@2 {
+			reg = <2>;
 			qcom,gpu-freq = <596000000>;
 			qcom,bus-freq = <10>;
 			qcom,bus-min = <9>;
-			qcom,bus-max = <11>;
+			qcom,bus-max = <12>;
 		};
 
-		qcom,gpu-pwrlevel@2 {
-			reg = <2>;
+		qcom,gpu-pwrlevel@3 {
+			reg = <3>;
 			qcom,gpu-freq = <520000000>;
 			qcom,bus-freq = <9>;
 			qcom,bus-min = <8>;
 			qcom,bus-max = <10>;
 		};
 
-		qcom,gpu-pwrlevel@3 {
-			reg = <3>;
+		qcom,gpu-pwrlevel@4 {
+			reg = <4>;
 			qcom,gpu-freq = <414000000>;
 			qcom,bus-freq = <8>;
 			qcom,bus-min = <7>;
 			qcom,bus-max = <9>;
 		};
 
-		qcom,gpu-pwrlevel@4 {
-			reg = <4>;
+		qcom,gpu-pwrlevel@5 {
+			reg = <5>;
 			qcom,gpu-freq = <342000000>;
 			qcom,bus-freq = <6>;
 			qcom,bus-min = <5>;
 			qcom,bus-max = <7>;
 		};
 
-		qcom,gpu-pwrlevel@5 {
-			reg = <5>;
+		qcom,gpu-pwrlevel@6 {
+			reg = <6>;
 			qcom,gpu-freq = <257000000>;
 			qcom,bus-freq = <4>;
 			qcom,bus-min = <3>;
 			qcom,bus-max = <5>;
 		};
 
-		qcom,gpu-pwrlevel@6 {
-			reg = <6>;
+		qcom,gpu-pwrlevel@7 {
+			reg = <7>;
 			qcom,gpu-freq = <0>;
 			qcom,bus-freq = <0>;
 			qcom,bus-min = <0>;
@@ -1099,9 +638,10 @@
 
 		compatible = "qcom,gmu-pwrlevels";
 
+		/* GMU power levels must go from lowest to highest */
 		qcom,gmu-pwrlevel@0 {
 			reg = <0>;
-			qcom,gmu-freq = <500000000>;
+			qcom,gmu-freq = <0>;
 		};
 
 		qcom,gmu-pwrlevel@1 {
@@ -1111,7 +651,30 @@
 
 		qcom,gmu-pwrlevel@2 {
 			reg = <2>;
-			qcom,gmu-freq = <0>;
+			qcom,gmu-freq = <500000000>;
 		};
 	};
 };
+
+&qusb_phy0 {
+		qcom,qusb-phy-init-seq =
+			/* <value reg_offset> */
+			   <0x23 0x210 /* PWR_CTRL1 */
+			    0x03 0x04  /* PLL_ANALOG_CONTROLS_TWO */
+			    0x7c 0x18c /* PLL_CLOCK_INVERTERS */
+			    0x80 0x2c  /* PLL_CMODE */
+			    0x0a 0x184 /* PLL_LOCK_DELAY */
+			    0x19 0xb4  /* PLL_DIGITAL_TIMERS_TWO */
+			    0x40 0x194 /* PLL_BIAS_CONTROL_1 */
+			    0x20 0x198 /* PLL_BIAS_CONTROL_2 */
+			    0x21 0x214 /* PWR_CTRL2 */
+			    0x08 0x220 /* IMP_CTRL1 */
+			    0x58 0x224 /* IMP_CTRL2 */
+			    0x45 0x240 /* TUNE1 */
+			    0x29 0x244 /* TUNE2 */
+			    0xca 0x248 /* TUNE3 */
+			    0x04 0x24c /* TUNE4 */
+			    0x03 0x250 /* TUNE5 */
+			    0x00 0x23c /* CHG_CTRL2 */
+			    0x22 0x210>; /* PWR_CTRL1 */
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 0b02e20..97904e3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -25,6 +25,7 @@
 #include <dt-bindings/spmi/spmi.h>
 #include <dt-bindings/thermal/thermal.h>
 #include <dt-bindings/msm/msm-bus-ids.h>
+#include <dt-bindings/soc/qcom,dcc_v2.h>
 
 #define MHZ_TO_MBPS(mhz, w) ((mhz * 1000000 * w) / (1024 * 1024))
 
@@ -36,7 +37,6 @@
 
 	aliases {
 		ufshc1 = &ufshc_mem; /* Embedded UFS slot */
-		ufshc2 = &ufshc_card; /* Removable UFS slot */
 		pci-domain0 = &pcie0;
 		pci-domain1 = &pcie1;
 		sdhc2 = &sdhc_2; /* SDC2 SD card slot */
@@ -80,7 +80,7 @@
 			};
 			L1_I_0: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_0: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -111,7 +111,7 @@
 			};
 			L1_I_100: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_100: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -142,7 +142,7 @@
 			};
 			L1_I_200: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_200: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -173,7 +173,7 @@
 			};
 			L1_I_300: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0xa000>;
+				qcom,dump-size = <0x12000>;
 			};
 			L1_D_300: l1-dcache {
 				compatible = "arm,arch-cache";
@@ -204,14 +204,14 @@
 			};
 			L1_I_400: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_400: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_400: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -235,14 +235,14 @@
 			};
 			L1_I_500: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_500: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_500: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -266,14 +266,14 @@
 			};
 			L1_I_600: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_600: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_600: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -297,14 +297,14 @@
 			};
 			L1_I_700: l1-icache {
 				compatible = "arm,arch-cache";
-				qcom,dump-size = <0x14000>;
+				qcom,dump-size = <0x24000>;
 			};
 			L1_D_700: l1-dcache {
 				compatible = "arm,arch-cache";
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_700: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -592,7 +592,7 @@
 			alloc-ranges = <0 0x00000000 0 0xffffffff>;
 			reusable;
 			alignment = <0 0x400000>;
-			size = <0 0xc00000>;
+			size = <0 0x1000000>;
 		};
 
 		qseecom_mem: qseecom_region {
@@ -611,6 +611,11 @@
 			size = <0 0x800000>;
 		};
 
+		cont_splash_memory: cont_splash_region@9d400000 {
+			reg = <0x0 0x9d400000 0x0 0x02400000>;
+			label = "cont_splash_region";
+		};
+
 		secure_display_memory: secure_display_region {
 			compatible = "shared-dma-pool";
 			alloc-ranges = <0 0x00000000 0 0xffffffff>;
@@ -649,6 +654,94 @@
 	ranges = <0 0 0 0xffffffff>;
 	compatible = "simple-bus";
 
+	jtag_mm0: jtagmm@7040000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7040000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU0>;
+	};
+
+	jtag_mm1: jtagmm@7140000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7140000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU1>;
+	};
+
+	jtag_mm2: jtagmm@7240000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7240000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU2>;
+	};
+
+	jtag_mm3: jtagmm@7340000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7340000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU3>;
+	};
+
+	jtag_mm4: jtagmm@7440000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7440000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU4>;
+	};
+
+	jtag_mm5: jtagmm@7540000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7540000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU5>;
+	};
+
+	jtag_mm6: jtagmm@7640000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7640000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU6>;
+	};
+
+	jtag_mm7: jtagmm@7740000 {
+		compatible = "qcom,jtagv8-mm";
+		reg = <0x7740000 0x1000>;
+		reg-names = "etm-base";
+
+		clocks = <&clock_aop QDSS_CLK>;
+		clock-names = "core_clk";
+
+		qcom,coresight-jtagmm-cpu = <&CPU7>;
+	};
+
 	intc: interrupt-controller@17a00000 {
 		compatible = "arm,gic-v3";
 		#interrupt-cells = <3>;
@@ -759,6 +852,7 @@
 		interrupt-controller;
 		#interrupt-cells = <4>;
 		cell-index = <0>;
+		qcom,enable-ahb-bus-workaround;
 	};
 
 	spmi_debug_bus: qcom,spmi-debug@6b22000 {
@@ -827,12 +921,12 @@
 			<MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_LLCC>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  2288 /* 150 MHz */ >,
-			<  4577 /* 300 MHz */ >,
-			<  6500 /* 426 MHz */ >,
-			<  8132 /* 533 MHz */ >,
-			<  9155 /* 600 MHz */ >,
-			< 10681 /* 700 MHz */ >;
+			< MHZ_TO_MBPS(150, 16) >, /*  2288 MB/s */
+			< MHZ_TO_MBPS(300, 16) >, /*  4577 MB/s */
+			< MHZ_TO_MBPS(426, 16) >, /*  6500 MB/s */
+			< MHZ_TO_MBPS(533, 16) >, /*  8132 MB/s */
+			< MHZ_TO_MBPS(600, 16) >, /*  9155 MB/s */
+			< MHZ_TO_MBPS(700, 16) >; /* 10681 MB/s */
 	};
 
 	bwmon: qcom,cpu-bwmon {
@@ -847,21 +941,21 @@
 
 	llccbw: qcom,llccbw {
 		compatible = "qcom,devbw";
-		governor = "powersave";
+		governor = "performance";
 		qcom,src-dst-ports =
 			<MSM_BUS_MASTER_LLCC MSM_BUS_SLAVE_EBI_CH0>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	llcc_bwmon: qcom,llcc-bwmon {
@@ -882,16 +976,16 @@
 		qcom,src-dst-ports = <1 512>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	memlat_cpu4: qcom,memlat-cpu4 {
@@ -901,16 +995,16 @@
 		qcom,active-only;
 		status = "ok";
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
 	snoc_cnoc_keepalive: qcom,snoc_cnoc_keepalive {
@@ -929,11 +1023,11 @@
 		qcom,target-dev = <&memlat_cpu0>;
 		qcom,cachemiss-ev = <0x2A>;
 		qcom,core-dev-table =
-			<  300000  762 >,
-			<  748800 1720 >,
-			< 1132800 2086 >,
-			< 1440000 2929 >,
-			< 1593600 3879 >;
+			<  300000 MHZ_TO_MBPS( 200, 4) >,
+			<  748800 MHZ_TO_MBPS( 451, 4) >,
+			< 1132800 MHZ_TO_MBPS( 547, 4) >,
+			< 1440000 MHZ_TO_MBPS( 768, 4) >,
+			< 1593600 MHZ_TO_MBPS(1017, 4) >;
 	};
 
 	devfreq_memlat_4: qcom,cpu4-memlat-mon {
@@ -942,14 +1036,14 @@
 		qcom,target-dev = <&memlat_cpu4>;
 		qcom,cachemiss-ev = <0x2A>;
 		qcom,core-dev-table =
-			<  300000  762 >,
-			<  499200 1720 >,
-			<  806400 2086 >,
-			< 1036800 2929 >,
-			< 1190400 3879 >,
-			< 1574400 4943 >,
-			< 1728000 5931 >,
-			< 1958400 6881 >;
+			<  300000 MHZ_TO_MBPS( 200, 4) >,
+			<  499200 MHZ_TO_MBPS( 451, 4) >,
+			<  806400 MHZ_TO_MBPS( 547, 4) >,
+			< 1036800 MHZ_TO_MBPS( 768, 4) >,
+			< 1190400 MHZ_TO_MBPS(1017, 4) >,
+			< 1574400 MHZ_TO_MBPS(1296, 4) >,
+			< 1728000 MHZ_TO_MBPS(1555, 4) >,
+			< 1958400 MHZ_TO_MBPS(1804, 4) >;
 	};
 
 	l3_cpu0: qcom,l3-cpu0 {
@@ -995,6 +1089,13 @@
 			< 1958400 1305600000 >;
 	};
 
+	l3_cdsp: qcom,l3-cdsp {
+		compatible = "devfreq-simple-dev";
+		clock-names = "devfreq_clk";
+		clocks = <&clock_cpucc L3_MISC_VOTE_CLK>;
+		governor = "powersave";
+	};
+
 	cpu_pmu: cpu-pmu {
 		compatible = "arm,armv8-pmuv3";
 		qcom,irq-is-percpu;
@@ -1007,29 +1108,38 @@
 		qcom,src-dst-ports = <1 512>;
 		qcom,active-only;
 		qcom,bw-tbl =
-			<  762 /*  200 MHz */ >,
-			< 1144 /*  300 MHz */ >,
-			< 1720 /*  451 MHz */ >,
-			< 2086 /*  547 MHz */ >,
-			< 2597 /*  681 MHz */ >,
-			< 2929 /*  768 MHz */ >,
-			< 3879 /* 1017 MHz */ >,
-			< 4943 /* 1296 MHz */ >,
-			< 5931 /* 1555 MHz */ >,
-			< 6881 /* 1804 MHz */ >;
+			< MHZ_TO_MBPS( 200, 4) >, /* 762 MB/s */
+			< MHZ_TO_MBPS( 300, 4) >, /* 1144 MB/s */
+			< MHZ_TO_MBPS( 451, 4) >, /* 1720 MB/s */
+			< MHZ_TO_MBPS( 547, 4) >, /* 2086 MB/s */
+			< MHZ_TO_MBPS( 681, 4) >, /* 2597 MB/s */
+			< MHZ_TO_MBPS( 768, 4) >, /* 2929 MB/s */
+			< MHZ_TO_MBPS(1017, 4) >, /* 3879 MB/s */
+			< MHZ_TO_MBPS(1296, 4) >, /* 4943 MB/s */
+			< MHZ_TO_MBPS(1555, 4) >, /* 5931 MB/s */
+			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
-	devfreq-cpufreq {
+	devfreq_cpufreq: devfreq-cpufreq {
 		mincpubw-cpufreq {
 			target-dev = <&mincpubw>;
 			cpu-to-dev-map-0 =
-				< 1708800  762 >;
+				< 1708800 MHZ_TO_MBPS(200, 4) >;
 			cpu-to-dev-map-4 =
-				< 1881600  762 >,
-				< 2208000 2597 >;
+				< 1881600 MHZ_TO_MBPS(200, 4) >,
+				< 2208000 MHZ_TO_MBPS(681, 4) >;
 		};
 	};
 
+	devfreq_compute: qcom,devfreq-compute {
+		compatible = "qcom,arm-cpu-mon";
+		qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>;
+		qcom,target-dev = <&mincpubw>;
+		qcom,core-dev-table =
+			< 1881600 MHZ_TO_MBPS(200, 4) >,
+			< 2208000 MHZ_TO_MBPS(681, 4) >;
+	};
+
 	clock_rpmh: qcom,rpmhclk {
 		compatible = "qcom,rpmh-clk-sdm845";
 		#clock-cells = <1>;
@@ -1094,6 +1204,7 @@
 		reg = <0x5090000 0x9000>;
 		reg-names = "cc_base";
 		vdd_cx-supply = <&pm8998_s9_level>;
+		vdd_mx-supply = <&pm8998_s6_level>;
 		qcom,gpu_cc_gmu_clk_src-opp-handle = <&gmu>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
@@ -1104,7 +1215,6 @@
 		reg = <0x5090000 0x9000>;
 		reg-names = "cc_base";
 		vdd_gfx-supply = <&pm8005_s1_level>;
-		vdd_mx-supply = <&pm8998_s6_level>;
 		qcom,gpu_cc_gx_gfx3d_clk_src-opp-handle = <&msm_gpu>;
 		#clock-cells = <1>;
 		#reset-cells = <1>;
@@ -1120,266 +1230,18 @@
 		reg = <0x17d41000 0x1400>,
 			<0x17d43000 0x1400>,
 			<0x17d45800 0x1400>,
-			<0x178d0000 0x1000>,
-			<0x178c0000 0x1000>,
-			<0x178b0000 0x1000>,
-			<0x17d42400 0x0c00>,
-			<0x17d44400 0x0c00>,
-			<0x17d46c00 0x0c00>,
-			<0x00784130 0x4>,
-			<0x00784130 0x4>,
-			<0x00784130 0x4>;
+			<0x784248 0x4>;
 		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base",
-			"l3_pll", "pwrcl_pll", "perfcl_pll", "l3_sequencer",
-			"pwrcl_sequencer", "perfcl_sequencer", "l3_efuse",
-			"pwrcl_efuse", "perfcl_efuse";
+							"cpr_rc";
+		vdd_l3_mx_ao-supply = <&pm8998_s6_level_ao>;
+		vdd_pwrcl_mx_ao-supply = <&pm8998_s6_level_ao>;
 
-		vdd-l3-supply = <&apc0_l3_vreg>;
-		vdd-pwrcl-supply = <&apc0_pwrcl_vreg>;
-		vdd-perfcl-supply = <&apc1_perfcl_vreg>;
-
-		l3-dev0 = <&l3_cpu0>;
-		l3-dev4 = <&l3_cpu4>;
-
-		qcom,l3-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >;
-
-		qcom,l3-speedbin1-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1094400000 0x402c0a39 0x00002e2e 0x1 11 >;
-
-		qcom,l3-speedbin2-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
-			<  1209600000 0x402c0b3f 0x00003232 0x1 12 >,
-			<  1305600000 0x40340c44 0x00003636 0x1 13 >;
-
-		qcom,pwrcl-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   748800000 0x401c0527 0x00002020 0x1 6 >,
-			<   825600000 0x401c062b 0x00002222 0x1 7 >,
-			<   902400000 0x4024072f 0x00002626 0x1 8 >,
-			<   979200000 0x40240833 0x00002929 0x1 9 >,
-			<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-			<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-			<  1209600000 0x402c0b3f 0x00003232 0x1 12 >,
-			<  1286400000 0x40340c43 0x00003636 0x1 13 >,
-			<  1363200000 0x40340d47 0x00003939 0x1 14 >,
-			<  1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
-			<  1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
-			<  1593600000 0x403c1053 0x00004242 0x1 17 >;
-
-		qcom,pwrcl-speedbin1-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   748800000 0x401c0527 0x00002020 0x1 6 >,
-			<   825600000 0x401c062b 0x00002222 0x1 7 >,
-			<   902400000 0x4024072f 0x00002626 0x1 8 >,
-			<   979200000 0x40240833 0x00002929 0x1 9 >,
-			<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-			<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-			<  1209600000 0x402c0b3f 0x00003232 0x1 12 >,
-			<  1286400000 0x40340c43 0x00003636 0x1 13 >,
-			<  1363200000 0x40340d47 0x00003939 0x1 14 >,
-			<  1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
-			<  1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
-			<  1593600000 0x403c1053 0x00004242 0x1 17 >,
-			<  1651200000 0x403c1156 0x00004545 0x1 18 >,
-			<  1708800000 0x40441259 0x00004747 0x1 19 >;
-
-		qcom,pwrcl-speedbin2-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   748800000 0x401c0527 0x00002020 0x1 6 >,
-			<   825600000 0x401c062b 0x00002222 0x1 7 >,
-			<   902400000 0x4024072f 0x00002626 0x1 8 >,
-			<   979200000 0x40240833 0x00002929 0x1 9 >,
-			<  1056000000 0x402c0937 0x00002c2c 0x1 10 >,
-			<  1132800000 0x402c0a3b 0x00002f2f 0x1 11 >,
-			<  1209600000 0x402c0b3f 0x00003232 0x1 12 >,
-			<  1286400000 0x40340c43 0x00003636 0x1 13 >,
-			<  1363200000 0x40340d47 0x00003939 0x1 14 >,
-			<  1440000000 0x40340e4b 0x00003c3c 0x1 15 >,
-			<  1516800000 0x403c0f4f 0x00003f3f 0x1 16 >,
-			<  1593600000 0x403c1053 0x00004242 0x1 17 >,
-			<  1670400000 0x40441157 0x00004646 0x1 18 >,
-			<  1747200000 0x4044125b 0x00004949 0x1 19 >;
-
-		qcom,perfcl-speedbin0-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
-			<  1190400000 0x402c0b3e 0x00003232 0x1 12 >,
-			<  1267200000 0x40340c42 0x00003535 0x1 13 >,
-			<  1344000000 0x40340d46 0x00003838 0x1 14 >,
-			<  1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
-			<  1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
-			<  1574400000 0x403c1052 0x00004242 0x1 17 >,
-			<  1651200000 0x403c1156 0x00004545 0x1 18 >,
-			<  1728000000 0x4044125a 0x00004848 0x1 19 >,
-			<  1804800000 0x4044135e 0x00004b4b 0x1 20 >,
-			<  1881600000 0x404c1462 0x00004e4e 0x1 21 >,
-			<  1958400000 0x404c1566 0x00005252 0x1 22 >;
-
-		qcom,perfcl-speedbin1-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
-			<  1190400000 0x402c0b3e 0x00003232 0x1 12 >,
-			<  1267200000 0x40340c42 0x00003535 0x1 13 >,
-			<  1344000000 0x40340d46 0x00003838 0x1 14 >,
-			<  1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
-			<  1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
-			<  1574400000 0x403c1052 0x00004242 0x1 17 >,
-			<  1651200000 0x403c1156 0x00004545 0x1 18 >,
-			<  1728000000 0x4044125a 0x00004848 0x1 19 >,
-			<  1804800000 0x4044135e 0x00004b4b 0x1 20 >,
-			<  1881600000 0x404c1462 0x00004e4e 0x1 21 >,
-			<  1958400000 0x404c1566 0x00005252 0x1 22 >,
-			<  2035200000 0x404c166a 0x00005555 0x1 23 >,
-			<  2092800000 0x4054176d 0x00005757 0x1 24 >;
-
-		qcom,perfcl-speedbin2-v0 =
-			<   300000000 0x000c000f 0x00002020 0x1 1 >,
-			<   422400000 0x50140116 0x00002020 0x1 2 >,
-			<   499200000 0x5014021a 0x00002020 0x1 3 >,
-			<   576000000 0x5014031e 0x00002020 0x1 4 >,
-			<   652800000 0x401c0422 0x00002020 0x1 5 >,
-			<   729600000 0x401c0526 0x00002020 0x1 6 >,
-			<   806400000 0x401c062a 0x00002222 0x1 7 >,
-			<   883200000 0x4024072e 0x00002525 0x1 8 >,
-			<   960000000 0x40240832 0x00002828 0x1 9 >,
-			<  1036800000 0x40240936 0x00002b2b 0x1 10 >,
-			<  1113600000 0x402c0a3a 0x00002e2e 0x1 11 >,
-			<  1190400000 0x402c0b3e 0x00003232 0x1 12 >,
-			<  1267200000 0x40340c42 0x00003535 0x1 13 >,
-			<  1344000000 0x40340d46 0x00003838 0x1 14 >,
-			<  1420800000 0x40340e4a 0x00003b3b 0x1 15 >,
-			<  1497600000 0x403c0f4e 0x00003e3e 0x1 16 >,
-			<  1574400000 0x403c1052 0x00004242 0x1 17 >,
-			<  1651200000 0x403c1156 0x00004545 0x1 18 >,
-			<  1728000000 0x4044125a 0x00004848 0x1 19 >,
-			<  1804800000 0x4044135e 0x00004b4b 0x1 20 >,
-			<  1881600000 0x404c1462 0x00004e4e 0x1 21 >,
-			<  1958400000 0x404c1566 0x00005252 0x1 22 >,
-			<  2035200000 0x404c166a 0x00005555 0x1 23 >,
-			<  2112000000 0x4054176e 0x00005858 0x1 24 >,
-			<  2208000000 0x40541873 0x00005c5c 0x1 25 >;
-
-		qcom,l3-memacc-level-vc-bin0 = <7 63>;
-		qcom,l3-memacc-level-vc-bin1 = <7 9>;
-		qcom,l3-memacc-level-vc-bin2 = <7 9>;
-
-		qcom,pwrcl-memacc-level-vc-bin0 = <12 63>;
-		qcom,pwrcl-memacc-level-vc-bin1 = <12 17>;
-		qcom,pwrcl-memacc-level-vc-bin2 = <12 17>;
-
-		qcom,perfcl-memacc-level-vc-bin0 = <12 18>;
-		qcom,perfcl-memacc-level-vc-bin1 = <12 18>;
-		qcom,perfcl-memacc-level-vc-bin2 = <12 18>;
-
-		qcom,up-timer =
-			<1000 1000 1000>;
-		qcom,down-timer =
-			<100000 100000 100000>;
-		qcom,set-ret-inactive;
-		qcom,enable-llm-freq-vote;
-		qcom,llm-freq-up-timer =
-			<1000 1000 1000>;
-		qcom,llm-freq-down-timer =
-			<327675 327675 327675>;
-		qcom,enable-llm-volt-vote;
-		qcom,llm-volt-up-timer =
-			<1000 1000 1000>;
-		qcom,llm-volt-down-timer =
-			<327675 327675 327675>;
-		qcom,cc-reads = <10>;
-		qcom,cc-delay = <5>;
-		qcom,cc-factor = <100>;
-		qcom,osm-clk-rate = <100000000>;
-		qcom,xo-clk-rate = <19200000>;
-
-		qcom,l-val-base =
-			<0x178d0004 0x178c0004 0x178b0004>;
-		qcom,apcs-pll-user-ctl =
-			<0x178d000c 0x178c000c 0x178b000c>;
-		qcom,apcs-pll-min-freq =
-			<0x17d41094 0x17d43094 0x17d45894>;
-		qcom,apm-mode-ctl =
-			<0x0 0x0 0x17d20010>;
-		qcom,apm-status-ctrl =
-			<0x0 0x0 0x17d20000>;
-		qcom,perfcl-isense-addr = <0x17871480>;
-		qcom,l3-mem-acc-addr = <0x17990170 0x17990170 0x17990170>;
-		qcom,pwrcl-mem-acc-addr = <0x17990160 0x17990164 0x17990164>;
-		qcom,perfcl-mem-acc-addr = <0x17990168 0x1799016c 0x1799016c>;
-		qcom,cfg-gfmux-addr =<0x178d0084 0x178c0084 0x178b0084>;
-		qcom,apcs-cbc-addr = <0x178d008c 0x178c008c 0x178b008c>;
-		qcom,apcs-ramp-ctl-addr = <0x17840904 0x17840904 0x17830904>;
-
-		qcom,perfcl-apcs-apm-threshold-voltage = <800000>;
-		qcom,perfcl-apcs-mem-acc-threshold-voltage = <852000>;
-		qcom,boost-fsm-en;
-		qcom,safe-fsm-en;
-		qcom,ps-fsm-en;
-		qcom,droop-fsm-en;
+		qcom,mx-turbo-freq = <1478400000 1689600000 3300000001>;
+		l3-devs = <&l3_cpu0 &l3_cpu4 &l3_cdsp>;
 
 		clock-names = "xo_ao";
 		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
 		#clock-cells = <1>;
-		#reset-cells = <1>;
 	};
 
 	clock_debug: qcom,cc-debug@100000 {
@@ -1485,6 +1347,7 @@
 			<0 0>,
 			<0 0>;
 
+		non-removable;
 		qcom,msm-bus,name = "ufshc_mem";
 		qcom,msm-bus,num-cases = <22>;
 		qcom,msm-bus,num-paths = <2>;
@@ -1519,7 +1382,13 @@
 		<123 512 2097152 0>, <1 757 102400 0>,  /* HS G3 RB */
 		<123 512 298189 0>, <1 757 1000 0>,  /* HS G1 RB L2 */
 		<123 512 596378 0>, <1 757 1000 0>,  /* HS G2 RB L2 */
-		<123 512 4194304 0>, <1 757 204800 0>, /* HS G3 RB L2 */
+		/* As UFS working in HS G3 RB L2 mode, aggregated
+		 * bandwidth (AB) should take care of providing
+		 * optimum throughput requested. However, as tested,
+		 * in order to scale up CNOC clock, instantaneous
+		 * bindwidth (IB) needs to be given a proper value too.
+		 */
+		<123 512 4194304 0>, <1 757 204800 409600>, /* HS G3 RB L2 */
 		<123 512 7643136 0>, <1 757 307200 0>; /* Max. bandwidth */
 
 		qcom,bus-vector-names = "MIN",
@@ -1546,102 +1415,6 @@
 		status = "disabled";
 	};
 
-	extcon_storage_cd: extcon_storage_cd {
-		compatible = "extcon-gpio";
-		extcon-id = <62>; /* EXTCON_MECHANICAL */
-		status = "disabled";
-	};
-
-	ufsphy_card: ufsphy_card@1da7000 {
-		reg = <0x1da7000 0xda8>; /* PHY regs */
-		reg-names = "phy_mem";
-		#phy-cells = <0>;
-
-		lanes-per-direction = <1>;
-
-		clock-names = "ref_clk_src",
-			"ref_clk",
-			"ref_aux_clk";
-		clocks = <&clock_rpmh RPMH_CXO_CLK>,
-			<&clock_gcc GCC_UFS_CARD_CLKREF_CLK>,
-			<&clock_gcc GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK>;
-
-		status = "disabled";
-	};
-
-	ufshc_card: ufshc_card@1da4000 {
-		compatible = "qcom,ufshc";
-		reg = <0x1da4000 0x2500>;
-		interrupts = <0 125 0>;
-		phys = <&ufsphy_card>;
-		phy-names = "ufsphy";
-
-		lanes-per-direction = <1>;
-		dev-ref-clk-freq = <0>; /* 19.2 MHz */
-
-		clock-names =
-			"core_clk",
-			"bus_aggr_clk",
-			"iface_clk",
-			"core_clk_unipro",
-			"core_clk_ice",
-			"ref_clk",
-			"tx_lane0_sync_clk",
-			"rx_lane0_sync_clk";
-		clocks =
-			<&clock_gcc GCC_UFS_CARD_AXI_HW_CTL_CLK>,
-			<&clock_gcc GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK>,
-			<&clock_gcc GCC_UFS_CARD_AHB_CLK>,
-			<&clock_gcc GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK>,
-			<&clock_gcc GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK>,
-			<&clock_rpmh RPMH_CXO_CLK>,
-			<&clock_gcc GCC_UFS_CARD_TX_SYMBOL_0_CLK>,
-			<&clock_gcc GCC_UFS_CARD_RX_SYMBOL_0_CLK>;
-		freq-table-hz =
-			<50000000 200000000>,
-			<0 0>,
-			<0 0>,
-			<37500000 150000000>,
-			<75000000 300000000>,
-			<0 0>,
-			<0 0>,
-			<0 0>;
-
-		qcom,msm-bus,name = "ufshc_card";
-		qcom,msm-bus,num-cases = <9>;
-		qcom,msm-bus,num-paths = <2>;
-		qcom,msm-bus,vectors-KBps =
-		<122 512 0 0>, <1 756 0 0>,          /* No vote */
-		<122 512 922 0>, <1 756 1000 0>,     /* PWM G1 */
-		<122 512 127796 0>, <1 756 1000 0>,  /* HS G1 RA */
-		<122 512 255591 0>, <1 756 1000 0>,  /* HS G2 RA */
-		<122 512 2097152 0>, <1 756 102400 0>,  /* HS G3 RA */
-		<122 512 149422 0>, <1 756 1000 0>,  /* HS G1 RB */
-		<122 512 298189 0>, <1 756 1000 0>,  /* HS G2 RB */
-		<122 512 2097152 0>, <1 756 102400 0>,  /* HS G3 RB */
-		<122 512 7643136 0>, <1 756 307200 0>; /* Max. bandwidth */
-		qcom,bus-vector-names = "MIN",
-		"PWM_G1_L1",
-		"HS_RA_G1_L1", "HS_RA_G2_L1", "HS_RA_G3_L1",
-		"HS_RB_G1_L1", "HS_RB_G2_L1", "HS_RB_G3_L1",
-		"MAX";
-
-		/* PM QoS */
-		qcom,pm-qos-cpu-groups = <0x0f 0xf0>;
-		qcom,pm-qos-cpu-group-latency-us = <70 70>;
-		qcom,pm-qos-default-cpu = <0>;
-
-		/*
-		 * Note: this instance doesn't have control over UFS device
-		 * reset
-		 */
-
-		resets = <&clock_gcc GCC_UFS_CARD_BCR>;
-		reset-names = "core_reset";
-
-		status = "disabled";
-	};
-
 	sdhc_2: sdhci@8804000 {
 		compatible = "qcom,sdhci-msm-v5";
 		reg = <0x8804000 0x1000>;
@@ -1697,6 +1470,12 @@
 			<&clock_gcc GCC_SDCC2_APPS_CLK>;
 		clock-names = "iface_clk", "core_clk";
 
+		/* PM QoS */
+		qcom,pm-qos-irq-type = "affine_irq";
+		qcom,pm-qos-irq-latency = <70 70>;
+		qcom,pm-qos-cpu-groups = <0x3f 0xc0>;
+		qcom,pm-qos-legacy-latency-us = <70 70>, <70 70>;
+
 		status = "disabled";
 	};
 
@@ -1735,12 +1514,16 @@
 		vdd_cx-voltage = <RPMH_REGULATOR_LEVEL_TURBO>;
 		vdd_mx-supply = <&pm8998_s6_level>;
 		vdd_mx-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
+		vdd_mss-supply = <&pm8005_s2_level>;
+		vdd_mss-uV = <RPMH_REGULATOR_LEVEL_TURBO>;
 		qcom,firmware-name = "modem";
 		qcom,pil-self-auth;
 		qcom,sysmon-id = <0>;
 		qcom,ssctl-instance-id = <0x12>;
 		qcom,override-acc;
+		qcom,signal-aop;
 		qcom,qdsp6v65-1-0;
+		qcom,mss_pdc_offset = <8>;
 		status = "ok";
 		memory-region = <&pil_modem_mem>;
 		qcom,mem-protect-id = <0xF>;
@@ -1754,6 +1537,9 @@
 
 		/* GPIO output to mss */
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_1_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "mss-pil";
 		qcom,mba-mem@0 {
 			compatible = "qcom,pil-mba-mem";
 			memory-region = <&pil_mba_mem>;
@@ -1780,6 +1566,7 @@
 		status = "ok";
 		qcom,ssctl-instance-id = <0x14>;
 		qcom,firmware-name = "adsp";
+		qcom,signal-aop;
 		memory-region = <&pil_adsp_mem>;
 
 		/* GPIO inputs from lpass */
@@ -1790,6 +1577,9 @@
 
 		/* GPIO output to lpass */
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_2_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "adsp-pil";
 	};
 
 	qcom,ssc@5c00000 {
@@ -1799,7 +1589,9 @@
 
 		vdd_cx-supply = <&pm8998_l27_level>;
 		qcom,vdd_cx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 0>;
-		qcom,proxy-reg-names = "vdd_cx";
+		vdd_mx-supply = <&pm8998_l4_level>;
+		qcom,vdd_mx-uV-uA = <RPMH_REGULATOR_LEVEL_TURBO 0>;
+		qcom,proxy-reg-names = "vdd_cx", "vdd_mx";
 		qcom,keep-proxy-regs-on;
 
 		clocks = <&clock_rpmh RPMH_CXO_CLK>;
@@ -1811,6 +1603,7 @@
 		qcom,smem-id = <424>;
 		qcom,sysmon-id = <3>;
 		qcom,ssctl-instance-id = <0x16>;
+		qcom,signal-aop;
 		qcom,firmware-name = "slpi";
 		status = "ok";
 		memory-region = <&pil_slpi_mem>;
@@ -1823,6 +1616,9 @@
 
 		/* GPIO output to ssc */
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_3_out 0 0>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "slpi-pil";
 	};
 
 	slim_aud: slim@171c0000 {
@@ -1877,6 +1673,9 @@
 		interrupts = <GIC_SPI 492 IRQ_TYPE_LEVEL_HIGH>;
 		reg = <0x88e0000 0x2000>;
 		reg-names = "eud_base";
+		clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+		clock-names = "cfg_ahb_clk";
+		vdda33-supply = <&pm8998_l24>;
 		status = "ok";
 	};
 
@@ -1905,9 +1704,13 @@
 
 		qcom,pas-id = <14>;
 		qcom,proxy-timeout-ms = <10000>;
+		qcom,signal-aop;
 		qcom,firmware-name = "spss";
 		memory-region = <&pil_spss_mem>;
 		qcom,spss-scsr-bits = <24 25>;
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "spss-pil";
 	};
 
 	wdog: qcom,wdt@17980000{
@@ -1940,6 +1743,7 @@
 		qcom,sysmon-id = <7>;
 		qcom,ssctl-instance-id = <0x17>;
 		qcom,firmware-name = "cdsp";
+		qcom,signal-aop;
 		memory-region = <&pil_cdsp_mem>;
 
 		/* GPIO inputs from turing */
@@ -1951,6 +1755,9 @@
 		/* GPIO output to turing*/
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_5_out 0 0>;
 		status = "ok";
+
+		mboxes = <&qmp_aop 0>;
+		mbox-names = "cdsp-pil";
 	};
 
 	qcom,msm-rtb {
@@ -2266,6 +2073,10 @@
 			max-slices = <32>;
 		};
 
+		qcom,llcc-perfmon {
+			compatible = "qcom,llcc-perfmon";
+		};
+
 		qcom,llcc-erp {
 			compatible = "qcom,llcc-erp";
 			interrupt-names = "ecc_irq";
@@ -2277,19 +2088,19 @@
 		};
 
 		LLCC_1: llcc_1_dcache {
-			qcom,dump-size = <0x114100>;
+			qcom,dump-size = <0x1141c0>;
 		};
 
 		LLCC_2: llcc_2_dcache {
-			qcom,dump-size = <0x114100>;
+			qcom,dump-size = <0x1141c0>;
 		};
 
 		LLCC_3: llcc_3_dcache {
-			qcom,dump-size = <0x114100>;
+			qcom,dump-size = <0x1141c0>;
 		};
 
 		LLCC_4: llcc_4_dcache {
-			qcom,dump-size = <0x114100>;
+			qcom,dump-size = <0x1141c0>;
 		};
 	};
 
@@ -2387,6 +2198,7 @@
 		qcom,irq-mask = <0x100>;
 		interrupts = <GIC_SPI 156 IRQ_TYPE_EDGE_RISING>;
 		label = "lpass";
+		cpu-affinity = <1 2>;
 		qcom,qos-config = <&glink_qos_adsp>;
 		qcom,ramp-time = <0xaf>;
 	};
@@ -2539,6 +2351,18 @@
 		qcom,fragmented-data;
 	};
 
+	qcom,qsee_ipc_irq_bridge {
+		compatible = "qcom,qsee-ipc-irq-bridge";
+
+		qcom,qsee-ipc-irq-spss {
+			qcom,rx-irq-clr = <0x1888008 0x4>;
+			qcom,rx-irq-clr-mask = <0x1>;
+			qcom,dev-name = "qsee_ipc_irq_spss";
+			interrupts = <0 349 4>;
+			label = "spss";
+		};
+	};
+
 	qcom,spcom {
 		compatible = "qcom,spcom";
 
@@ -2673,7 +2497,7 @@
 		qcom,msm-bus,num-paths = <1>;
 		qcom,msm-bus,vectors-KBps =
 			<1 618 0 0>,    /* No vote */
-			<1 618 0 800>;  /* 100 KHz */
+			<1 618 0 300000>;  /* 75 MHz */
 		clocks = <&clock_gcc GCC_PRNG_AHB_CLK>;
 		clock-names = "iface_clk";
 	};
@@ -2692,7 +2516,7 @@
 			<0x1dc4000 0x24000>;
 		reg-names = "crypto-base","crypto-bam-base";
 		interrupts = <0 272 0>;
-		qcom,bam-pipe-pair = <1>;
+		qcom,bam-pipe-pair = <3>;
 		qcom,ce-hw-instance = <0>;
 		qcom,ce-device = <0>;
 		qcom,ce-hw-shared;
@@ -2711,9 +2535,9 @@
 			 <&clock_gcc GCC_CE1_AXI_CLK>;
 		qcom,ce-opp-freq = <171430000>;
 		qcom,request-bw-before-clk;
-		qcom,smmu-s1-bypass;
-		iommus = <&apps_smmu 0x702 0x1>,
-			 <&apps_smmu 0x712 0x1>;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x706 0x1>,
+			 <&apps_smmu 0x716 0x1>;
 	};
 
 	qcom_msmhdcp: qcom,msm_hdcp {
@@ -2752,9 +2576,9 @@
 		qcom,use-sw-ahash-algo;
 		qcom,use-sw-aead-algo;
 		qcom,use-sw-hmac-algo;
-		qcom,smmu-s1-bypass;
-		iommus = <&apps_smmu 0x704 0x3>,
-			 <&apps_smmu 0x714 0x3>;
+		qcom,smmu-s1-enable;
+		iommus = <&apps_smmu 0x704 0x1>,
+			 <&apps_smmu 0x714 0x1>;
 	};
 
 	qcom,msm_gsi {
@@ -2766,6 +2590,7 @@
 		reg = <0x0 0x200000>;
 		reg-names = "rmtfs";
 		qcom,client-id = <0x00000001>;
+		qcom,guard-memory;
 	};
 
 	qcom,rmnet-ipa {
@@ -2793,7 +2618,6 @@
 		qcom,ipa-wdi2;
 		qcom,use-64-bit-dma-mask;
 		qcom,arm-smmu;
-		qcom,smmu-s1-bypass;
 		qcom,bandwidth-vote-for-ipa;
 		qcom,msm-bus,name = "ipa";
 		qcom,msm-bus,num-cases = <5>;
@@ -2916,17 +2740,26 @@
 
 		ipa_smmu_ap: ipa_smmu_ap {
 			compatible = "qcom,ipa-smmu-ap-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x720 0x0>;
 			qcom,iova-mapping = <0x20000000 0x40000000>;
+			qcom,additional-mapping =
+			/* modem tables in IMEM */
+			<0x146BD000 0x146BD000 0x2000>;
 		};
 
 		ipa_smmu_wlan: ipa_smmu_wlan {
 			compatible = "qcom,ipa-smmu-wlan-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x721 0x0>;
+			qcom,additional-mapping =
+			/* ipa-uc ram */
+			<0x1E60000 0x1E60000 0x80000>;
 		};
 
 		ipa_smmu_uc: ipa_smmu_uc {
 			compatible = "qcom,ipa-smmu-uc-cb";
+			qcom,smmu-s1-bypass;
 			iommus = <&apps_smmu 0x722 0x0>;
 			qcom,iova-mapping = <0x40000000 0x20000000>;
 		};
@@ -2936,6 +2769,7 @@
 		compatible = "qcom,pil-tz-generic";
 		qcom,pas-id = <0xf>;
 		qcom,firmware-name = "ipa_fws";
+		qcom,pil-force-shutdown;
 	};
 
 	qcom,chd_sliver {
@@ -2981,6 +2815,214 @@
 		reg-names = "dcc-base", "dcc-ram-base";
 
 		dcc-ram-offset = <0x6000>;
+
+		qcom,curr-link-list = <2>;
+		qcom,link-list = <DCC_READ 0x1740300 6 0>,
+				 <DCC_READ 0x1620500 4 0>,
+				 <DCC_READ 0x7840000 1 0>,
+				 <DCC_READ 0x7841010 12 0>,
+				 <DCC_READ 0x7842000 16 0>,
+				 <DCC_READ 0x7842500 2 0>,
+				 <DCC_LOOP 7 0 0>,
+				 <DCC_READ 0x7841000 1 0>,
+				 <DCC_LOOP 1 0 0>,
+				 <DCC_LOOP 165 0 0>,
+				 <DCC_READ 0x7841008 2 0>,
+				 <DCC_LOOP 1 0 0>,
+				 <DCC_READ 0x17dc3a84 2 0>,
+				 <DCC_READ 0x17db3a84 1 0>,
+				 <DCC_READ 0x1301000 2 0>,
+				 <DCC_READ 0x17990044 1 0>,
+				 <DCC_READ 0x17d45f00 1 0>,
+				 <DCC_READ 0x17d45f08 6 0>,
+				 <DCC_READ 0x17d45f80 1 0>,
+				 <DCC_READ 0x17d47418 1 0>,
+				 <DCC_READ 0x17d47570 1 0>,
+				 <DCC_READ 0x17d47588 1 0>,
+				 <DCC_READ 0x17d43700 1 0>,
+				 <DCC_READ 0x17d43708 6 0>,
+				 <DCC_READ 0x17d43780 1 0>,
+				 <DCC_READ 0x17d44c18 1 0>,
+				 <DCC_READ 0x17d44d70 1 0>,
+				 <DCC_READ 0x17d44d88 1 0>,
+				 <DCC_READ 0x17d41700 1 0>,
+				 <DCC_READ 0x17d41708 6 0>,
+				 <DCC_READ 0x17d41780 1 0>,
+				 <DCC_READ 0x17d42c18 1 0>,
+				 <DCC_READ 0x17d42d70 1 0>,
+				 <DCC_READ 0x17d42d88 1 0>,
+				 <DCC_WRITE 0x69ea00c 0x600007 1>,
+				 <DCC_WRITE 0x69ea01c 0x136800 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136810 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136820 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136830 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136840 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136850 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136860 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x69ea01c 0x136870 1>,
+				 <DCC_READ 0x69ea014 1 1>,
+				 <DCC_WRITE 0x069ea01C 0x0003e9a0 1>,
+				 <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003c0a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003d1a0 1>,
+				 <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003d2a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01C 0x0003d5a0 1>,
+				 <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01C 0x0003d6a0 1>,
+				 <DCC_WRITE 0x069ea01C 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003b1a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003b2a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003b5a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003b6a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003c2a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003c5a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x0003c6a0 1>,
+				 <DCC_WRITE 0x069ea01c 0x001368a0 1>,
+				 <DCC_READ 0x069ea014 1 1>,
+				 <DCC_WRITE 0x069ea01c 0x00f1e000 1>,
+				 <DCC_WRITE 0x069ea008 0x00000007 1>,
+				 <DCC_READ 0x013e7e00 31 0>,
+				 <DCC_READ 0x01132100 1 0>,
+				 <DCC_READ 0x01136044 4 0>,
+				 <DCC_READ 0x011360b0 1 0>,
+				 <DCC_READ 0x0113e030 2 0>,
+				 <DCC_READ 0x01141000 1 0>,
+				 <DCC_READ 0x01142028 1 0>,
+				 <DCC_READ 0x01148058 4 0>,
+				 <DCC_READ 0x01160410 3 0>,
+				 <DCC_READ 0x011604a0 1 0>,
+				 <DCC_READ 0x011604b8 1 0>,
+				 <DCC_READ 0x01165804 1 0>,
+				 <DCC_READ 0x01166418 1 0>,
+				 <DCC_READ 0x011b2100 1 0>,
+				 <DCC_READ 0x011b6044 4 0>,
+				 <DCC_READ 0x011be030 2 0>,
+				 <DCC_READ 0x011c1000 1 0>,
+				 <DCC_READ 0x011c2028 1 0>,
+				 <DCC_READ 0x011c8058 4 0>,
+				 <DCC_READ 0x011e0410 3 0>,
+				 <DCC_READ 0x011e04a0 1 0>,
+				 <DCC_READ 0x011e04b8 1 0>,
+				 <DCC_READ 0x011e5804 1 0>,
+				 <DCC_READ 0x011e6418 1 0>,
+				 <DCC_READ 0x01232100 1 0>,
+				 <DCC_READ 0x01236044 4 0>,
+				 <DCC_READ 0x012360B0 1 0>,
+				 <DCC_READ 0x0123E030 2 0>,
+				 <DCC_READ 0x01241000 1 0>,
+				 <DCC_READ 0x01242028 1 0>,
+				 <DCC_READ 0x01248058 4 0>,
+				 <DCC_READ 0x01260410 3 0>,
+				 <DCC_READ 0x012604a0 1 0>,
+				 <DCC_READ 0x012604b8 1 0>,
+				 <DCC_READ 0x01265804 1 0>,
+				 <DCC_READ 0x01266418 1 0>,
+				 <DCC_READ 0x012b2100 1 0>,
+				 <DCC_READ 0x012b6044 3 0>,
+				 <DCC_READ 0x012b6050 1 0>,
+				 <DCC_READ 0x012b60b0 1 0>,
+				 <DCC_READ 0x012be030 2 0>,
+				 <DCC_READ 0x012c1000 1 0>,
+				 <DCC_READ 0x012c2028 1 0>,
+				 <DCC_READ 0x012c8058 4 0>,
+				 <DCC_READ 0x012e0410 3 0>,
+				 <DCC_READ 0x012e04a0 1 0>,
+				 <DCC_READ 0x012e04b8 1 0>,
+				 <DCC_READ 0x012e5804 1 0>,
+				 <DCC_READ 0x012e6418 1 0>,
+				 <DCC_READ 0x01380900 8 0>,
+				 <DCC_READ 0x01380d00 5 0>,
+				 <DCC_READ 0x01350110 4 0>,
+				 <DCC_READ 0x01430280 1 0>,
+				 <DCC_READ 0x01430288 1 0>,
+				 <DCC_READ 0x0143028c 7 0>,
+				 <DCC_READ 0x01132100 1 0>,
+				 <DCC_READ 0x01136044 4 0>,
+				 <DCC_READ 0x011360b0 1 0>,
+				 <DCC_READ 0x0113e030 2 0>,
+				 <DCC_READ 0x01141000 1 0>,
+				 <DCC_READ 0x01142028 1 0>,
+				 <DCC_READ 0x01148058 4 0>,
+				 <DCC_READ 0x01160410 3 0>,
+				 <DCC_READ 0x011604a0 1 0>,
+				 <DCC_READ 0x011604b8 1 0>,
+				 <DCC_READ 0x01165804 1 0>,
+				 <DCC_READ 0x01166418 1 0>,
+				 <DCC_READ 0x011b2100 1 0>,
+				 <DCC_READ 0x011b6044 4 0>,
+				 <DCC_READ 0x011be030 2 0>,
+				 <DCC_READ 0x011c1000 1 0>,
+				 <DCC_READ 0x011c2028 1 0>,
+				 <DCC_READ 0x011c8058 4 0>,
+				 <DCC_READ 0x011e0410 3 0>,
+				 <DCC_READ 0x011e04a0 1 0>,
+				 <DCC_READ 0x011e04b8 1 0>,
+				 <DCC_READ 0x011e5804 1 0>,
+				 <DCC_READ 0x011e6418 1 0>,
+				 <DCC_READ 0x01232100 1 0>,
+				 <DCC_READ 0x01236044 4 0>,
+				 <DCC_READ 0x012360b0 1 0>,
+				 <DCC_READ 0x0123e030 2 0>,
+				 <DCC_READ 0x01241000 1 0>,
+				 <DCC_READ 0x01242028 1 0>,
+				 <DCC_READ 0x01248058 4 0>,
+				 <DCC_READ 0x01260410 3 0>,
+				 <DCC_READ 0x012604a0 1 0>,
+				 <DCC_READ 0x012604b8 1 0>,
+				 <DCC_READ 0x01265804 1 0>,
+				 <DCC_READ 0x01266418 1 0>,
+				 <DCC_READ 0x012b2100 1 0>,
+				 <DCC_READ 0x012b6044 3 0>,
+				 <DCC_READ 0x012b6050 1 0>,
+				 <DCC_READ 0x012b60b0 1 0>,
+				 <DCC_READ 0x012be030 2 0>,
+				 <DCC_READ 0x012C1000 1 0>,
+				 <DCC_READ 0x012C2028 1 0>,
+				 <DCC_READ 0x012C8058 4 0>,
+				 <DCC_READ 0x012e0410 3 0>,
+				 <DCC_READ 0x012e04a0 1 0>,
+				 <DCC_READ 0x012e04b8 1 0>,
+				 <DCC_READ 0x012e5804 1 0>,
+				 <DCC_READ 0x012e6418 1 0>,
+				 <DCC_READ 0x01380900 8 0>,
+				 <DCC_READ 0x01380d00 5 0>,
+				 <DCC_READ 0x01350110 4 0>,
+				 <DCC_READ 0x01430280 1 0>,
+				 <DCC_READ 0x01430288 1 0>,
+				 <DCC_READ 0x0143028c 7 0>,
+				 <DCC_READ 0x0c201244 1 0>,
+				 <DCC_READ 0x0c202244 1 0>;
 	};
 
 	qcom,msm-core@780000 {
@@ -3039,6 +3081,11 @@
 				#cooling-cells = <2>;
 			};
 
+			modem_skin: modem_skin {
+				qcom,qmi-dev-name = "modem_skin";
+				#cooling-cells = <2>;
+			};
+
 			modem_vdd: modem_vdd {
 				qcom,qmi-dev-name = "cpuv_restriction_cold";
 				#cooling-cells = <2>;
@@ -3456,6 +3503,182 @@
 			};
 		};
 
+		cpu0-silver-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 1>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config0: emerg-config0 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev0 {
+					trip = <&emerg_config0>;
+					cooling-device =
+						<&CPU0 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu1-silver-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 2>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config1: emerg-config1 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev1 {
+					trip = <&emerg_config1>;
+					cooling-device =
+						<&CPU1 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu2-silver-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 3>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config2: emerg-config2 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev2 {
+					trip = <&emerg_config2>;
+					cooling-device =
+						<&CPU2 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu3-silver-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 4>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config3: emerg-config3 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev3 {
+					trip = <&emerg_config3>;
+					cooling-device =
+						<&CPU3 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu0-gold-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 7>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config4: emerg-config4 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev4 {
+					trip = <&emerg_config4>;
+					cooling-device =
+						<&CPU4 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu1-gold-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 8>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config5: emerg-config5 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev5 {
+					trip = <&emerg_config5>;
+					cooling-device =
+						<&CPU5 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu2-gold-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 9>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config6: emerg-config6 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev6 {
+					trip = <&emerg_config6>;
+					cooling-device =
+						<&CPU6 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
+		cpu3-gold-step {
+			polling-delay-passive = <100>;
+			polling-delay = <0>;
+			thermal-sensors = <&tsens0 10>;
+			thermal-governor = "step_wise";
+			trips {
+				emerg_config7: emerg-config7 {
+					temperature = <110000>;
+					hysteresis = <10000>;
+					type = "passive";
+				};
+			};
+			cooling-maps {
+				emerg_cdev7 {
+					trip = <&emerg_config7>;
+					cooling-device =
+						<&CPU7 THERMAL_MAX_LIMIT
+							THERMAL_MAX_LIMIT>;
+				};
+			};
+		};
+
 		lmh-dcvs-01 {
 			polling-delay-passive = <0>;
 			polling-delay = <0>;
@@ -3519,6 +3742,11 @@
 			qcom,dump-id = <0xec>;
 		};
 
+		fcm_dump {
+			qcom,dump-size = <0x400>;
+			qcom,dump-id = <0xee>;
+		};
+
 		rpm_sw_dump {
 			qcom,dump-size = <0x28000>;
 			qcom,dump-id = <0xea>;
@@ -3558,6 +3786,11 @@
 			qcom,dump-size = <0x1000>;
 			qcom,dump-id = <0xe8>;
 		};
+
+		tpdm_swao_dump {
+			qcom,dump-size = <0x512>;
+			qcom,dump-id = <0xf2>;
+		};
 	};
 
 	gpi_dma0: qcom,gpi-dma@0x800000 {
@@ -3686,6 +3919,10 @@
 			 <&clock_rpmh RPMH_RF_CLK3_A>;
 		clock-names = "rf_clk3_clk", "rf_clk3_pin_clk";
 		qcom,smmu-support;
+		qcom,smmu-mapping = <0x20000000 0xe0000000>;
+		qcom,smmu-s1-en;
+		qcom,smmu-fast-map;
+		qcom,smmu-coherent;
 		qcom,keep-radio-on-during-sleep;
 		status = "disabled";
 	};
diff --git a/arch/arm64/boot/dts/qcom/smb1355.dtsi b/arch/arm64/boot/dts/qcom/smb1355.dtsi
index 6b7ebbd..3412b25d 100644
--- a/arch/arm64/boot/dts/qcom/smb1355.dtsi
+++ b/arch/arm64/boot/dts/qcom/smb1355.dtsi
@@ -13,30 +13,30 @@
 #include <dt-bindings/interrupt-controller/irq.h>
 
 &qupv3_se10_i2c {
-	smb1355: qcom,smb1355@8 {
+	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";
+		interrupt_names = "smb1355_0";
 		interrupt-controller;
 		#interrupt-cells = <3>;
 		qcom,periph-map = <0x10 0x12 0x13 0x16>;
 
-		smb1355_revid: qcom,revid@100 {
+		smb1355_revid_0: qcom,revid@100 {
 			compatible = "qcom,qpnp-revid";
 			reg = <0x100 0x100>;
 		};
 
-		smb1355_charger: qcom,smb1355-charger@1000 {
+		smb1355_charger_0: qcom,smb1355-charger@1000 {
 			compatible = "qcom,smb1355";
-			qcom,pmic-revid = <&smb1355_revid>;
+			qcom,pmic-revid = <&smb1355_revid_0>;
 			reg = <0x1000 0x700>;
 			#address-cells = <1>;
 			#size-cells = <1>;
-			interrupt-parent = <&smb1355>;
+			interrupt-parent = <&smb1355_0>;
 			status = "disabled";
 
 			io-channels = <&pmi8998_rradc 2>,
@@ -52,8 +52,57 @@
 
 			qcom,chgr-misc@1600 {
 				reg = <0x1600 0x100>;
-				interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>;
-				interrupt-names = "wdog-bark";
+				interrupts = <0x16 0x1 IRQ_TYPE_EDGE_RISING>,
+					     <0x16 0x6 IRQ_TYPE_EDGE_RISING>;
+				interrupt-names = "wdog-bark",
+						  "temperature-change";
+			};
+		};
+	};
+
+	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>;
+
+		smb1355_revid_1: qcom,revid@100 {
+			compatible = "qcom,qpnp-revid";
+			reg = <0x100 0x100>;
+		};
+
+		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/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
new file mode 100644
index 0000000..12365b3
--- /dev/null
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -0,0 +1,473 @@
+CONFIG_LOCALVERSION="-perf"
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_FHANDLE is not set
+CONFIG_AUDIT=y
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+# CONFIG_AIO is not set
+# CONFIG_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_SDM450=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT=y
+CONFIG_HZ_100=y
+CONFIG_CMA=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+# CONFIG_ARM64_VHE is not set
+CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_BOOST=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# CONFIG_CFG80211_CRDA_SUPPORT is not set
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_REQ_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8953=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_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_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_CPRH_KBSS=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_VIDC_GOVERNORS=y
+CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+CONFIG_QCOM_KGSL=y
+CONFIG_DRM=y
+CONFIG_DRM_SDE_EVTLOG_DEBUG=y
+CONFIG_DRM_SDE_RSC=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_DWC3=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
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_RMNET_IPA3=y
+CONFIG_RNDIS_IPA=y
+CONFIG_IPA_UT=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_QCOM_MDSS_PLL=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_QCOM_EUD=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+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
+CONFIG_MSM_PM=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_SCHEDSTATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_IPC_LOGGING=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_DEBUG_ALIGN_RODATA=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM64_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM64_CE=y
+CONFIG_CRYPTO_SHA2_ARM64_CE=y
+CONFIG_CRYPTO_GHASH_ARM64_CE=y
+CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
+CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
+CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
+CONFIG_CRYPTO_CRC32_ARM64=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
new file mode 100644
index 0000000..8757cc3
--- /dev/null
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -0,0 +1,543 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_FHANDLE is not set
+CONFIG_AUDIT=y
+# CONFIG_AUDITSYSCALL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IRQ_TIME_ACCOUNTING=y
+CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_RCU_EXPERT=y
+CONFIG_RCU_FAST_NO_HZ=y
+CONFIG_RCU_NOCB_CPU=y
+CONFIG_RCU_NOCB_CPU_ALL=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
+CONFIG_SCHED_CORE_CTL=y
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
+# CONFIG_AIO is not set
+# CONFIG_MEMBARRIER is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_MODULE_SIG=y
+CONFIG_MODULE_SIG_FORCE=y
+CONFIG_MODULE_SIG_SHA512=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_ARCH_QCOM=y
+CONFIG_ARCH_MSM8953=y
+CONFIG_ARCH_SDM450=y
+CONFIG_SCHED_MC=y
+CONFIG_NR_CPUS=8
+CONFIG_PREEMPT=y
+CONFIG_HZ_100=y
+CONFIG_CLEANCACHE=y
+CONFIG_CMA=y
+CONFIG_CMA_DEBUGFS=y
+CONFIG_ZSMALLOC=y
+CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_SECCOMP=y
+CONFIG_ARMV8_DEPRECATED=y
+CONFIG_SWP_EMULATION=y
+CONFIG_CP15_BARRIER_EMULATION=y
+CONFIG_SETEND_EMULATION=y
+# CONFIG_ARM64_VHE is not set
+CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_COMPAT=y
+CONFIG_PM_AUTOSLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_LIMIT=0
+# CONFIG_PM_WAKELOCKS_GC is not set
+CONFIG_PM_DEBUG=y
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_BOOST=y
+CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_INET_AH=y
+CONFIG_INET_ESP=y
+CONFIG_INET_IPCOMP=y
+CONFIG_INET_DIAG_DESTROY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_SUBTREES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_LOG=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=y
+CONFIG_NETFILTER_XT_TARGET_TEE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DSCP=y
+CONFIG_NETFILTER_XT_MATCH_ESP=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_RPFILTER=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_SECURITY=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_RPFILTER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_BROUTE=y
+CONFIG_L2TP=y
+CONFIG_L2TP_DEBUGFS=y
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=y
+CONFIG_L2TP_ETH=y
+CONFIG_BRIDGE=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_PRIO=y
+CONFIG_NET_SCH_MULTIQ=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_FW=y
+CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=y
+CONFIG_NET_EMATCH_NBYTE=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_EMATCH_META=y
+CONFIG_NET_EMATCH_TEXT=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_NET_ACT_SKBEDIT=y
+CONFIG_DNS_RESOLVER=y
+CONFIG_RMNET_DATA=y
+CONFIG_RMNET_DATA_FC=y
+CONFIG_RMNET_DATA_DEBUG_PKT=y
+CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_INTERNAL_REGDB=y
+# CONFIG_CFG80211_CRDA_SUPPORT is not set
+CONFIG_RFKILL=y
+CONFIG_NFC_NQ=y
+CONFIG_IPC_ROUTER=y
+CONFIG_IPC_ROUTER_SECURITY=y
+CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_DMA_CMA=y
+CONFIG_ZRAM=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
+CONFIG_MEMORY_STATE_TIME=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_UFSHCD=y
+CONFIG_SCSI_UFSHCD_PLATFORM=y
+CONFIG_SCSI_UFS_QCOM=y
+CONFIG_SCSI_UFS_QCOM_ICE=y
+CONFIG_SCSI_UFSHCD_CMD_LOGGING=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_REQ_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_VERITY=y
+CONFIG_DM_VERITY_FEC=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_TUN=y
+CONFIG_PPP=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
+CONFIG_WCNSS_MEM_PRE_ALLOC=y
+CONFIG_CLD_LL_CORE=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_HBTP_INPUT=y
+CONFIG_INPUT_UINPUT=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_MSM=y
+CONFIG_SERIAL_MSM_CONSOLE=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_MSM_LEGACY=y
+CONFIG_MSM_ADSPRPC=y
+CONFIG_MSM_RDBG=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_SPI=y
+CONFIG_SPI_QUP=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
+CONFIG_SPMI=y
+CONFIG_SPMI_MSM_PMIC_ARB_DEBUG=y
+CONFIG_PINCTRL_MSM8953=y
+CONFIG_PINCTRL_QCOM_SPMI_PMIC=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_POWER_RESET_QCOM=y
+CONFIG_QCOM_DLOAD_MODE=y
+CONFIG_POWER_RESET_XGENE=y
+CONFIG_POWER_RESET_SYSCON=y
+CONFIG_SENSORS_QPNP_ADC_VOLTAGE=y
+CONFIG_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_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_CPRH_KBSS=y
+CONFIG_REGULATOR_QPNP_LABIBB=y
+CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_STUB=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_CONTROLLER=y
+CONFIG_VIDEO_V4L2_SUBDEV_API=y
+CONFIG_V4L_PLATFORM_DRIVERS=y
+CONFIG_MSM_VIDC_V4L2=y
+CONFIG_MSM_VIDC_GOVERNORS=y
+CONFIG_MSM_SDE_ROTATOR=y
+CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+CONFIG_QCOM_KGSL=y
+CONFIG_DRM=y
+CONFIG_DRM_SDE_EVTLOG_DEBUG=y
+CONFIG_DRM_SDE_RSC=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SOC=y
+CONFIG_UHID=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_DWC3=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
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_MMC=y
+CONFIG_MMC_PERF_PROFILING=y
+CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_TEST=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_MSM=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_QPNP=y
+CONFIG_LEDS_QPNP_WLED=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_EDAC=y
+CONFIG_EDAC_MM_EDAC=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_QPNP=y
+CONFIG_DMADEVICES=y
+CONFIG_QCOM_SPS_DMA=y
+CONFIG_UIO=y
+CONFIG_UIO_MSM_SHAREDMEM=y
+CONFIG_STAGING=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ION=y
+CONFIG_ION_MSM=y
+CONFIG_GSI=y
+CONFIG_IPA3=y
+CONFIG_RMNET_IPA3=y
+CONFIG_RNDIS_IPA=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_COINCELL=y
+CONFIG_QPNP_REVID=y
+CONFIG_USB_BAM=y
+CONFIG_QCOM_MDSS_PLL=y
+CONFIG_REMOTE_SPINLOCK_MSM=y
+CONFIG_MAILBOX=y
+CONFIG_ARM_SMMU=y
+CONFIG_IOMMU_DEBUG=y
+CONFIG_IOMMU_DEBUG_TRACKING=y
+CONFIG_IOMMU_TESTS=y
+CONFIG_MSM_BOOT_STATS=y
+CONFIG_MSM_CORE_HANG_DETECT=y
+CONFIG_MSM_GLADIATOR_HANG_DETECT=y
+CONFIG_QCOM_EUD=y
+CONFIG_QCOM_WATCHDOG_V2=y
+CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
+CONFIG_MSM_SMEM=y
+CONFIG_MSM_GLINK=y
+CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
+CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT=y
+CONFIG_MSM_GLINK_SPI_XPRT=y
+CONFIG_TRACER_PKT=y
+CONFIG_MSM_SMP2P=y
+CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
+CONFIG_MSM_QMI_INTERFACE=y
+CONFIG_MSM_GLINK_PKT=y
+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
+CONFIG_MSM_PM=y
+CONFIG_QTI_RPM_STATS_LOG=y
+CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_PWM=y
+CONFIG_PWM_QPNP=y
+CONFIG_ARM_GIC_V3_ACL=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_SENSORS_SSC=y
+CONFIG_MSM_TZ_LOG=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_ECRYPT_FS=y
+CONFIG_ECRYPT_FS_MESSAGING=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_PAGE_OWNER=y
+CONFIG_PAGE_OWNER_ENABLE_DEFAULT=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_SLUB_DEBUG_PANIC_ON=y
+CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT=y
+CONFIG_PAGE_POISONING=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_SLUB_DEBUG_ON=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=4000
+CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_WQ_WATCHDOG=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_PANIC_ON_SCHED_BUG=y
+CONFIG_PANIC_ON_RT_THROTTLING=y
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_STACK_END_CHECK=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_LIST=y
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_IPC_LOGGING=y
+CONFIG_QCOM_RTB=y
+CONFIG_QCOM_RTB_SEPARATE_CPUS=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_IRQSOFF_TRACER=y
+CONFIG_PREEMPT_TRACER=y
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_CPU_FREQ_SWITCH_PROFILER=y
+CONFIG_LKDTM=y
+CONFIG_MEMTEST=y
+CONFIG_PANIC_ON_DATA_CORRUPTION=y
+CONFIG_ARM64_PTDUMP=y
+CONFIG_PID_IN_CONTEXTIDR=y
+CONFIG_CORESIGHT=y
+CONFIG_CORESIGHT_REMOTE_ETM=y
+CONFIG_CORESIGHT_REMOTE_ETM_DEFAULT_ENABLE=0
+CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_TPDA=y
+CONFIG_CORESIGHT_TPDM=y
+CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_HWEVENT=y
+CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
+CONFIG_SECURITY=y
+CONFIG_HARDENED_USERCOPY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_XCBC=y
+CONFIG_CRYPTO_MD4=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_ANSI_CPRNG=y
+CONFIG_CRYPTO_DEV_QCOM_MSM_QCE=y
+CONFIG_CRYPTO_DEV_QCRYPTO=y
+CONFIG_CRYPTO_DEV_QCEDEV=y
+CONFIG_CRYPTO_DEV_QCOM_ICE=y
+CONFIG_ARM64_CRYPTO=y
+CONFIG_CRYPTO_SHA1_ARM64_CE=y
+CONFIG_CRYPTO_SHA2_ARM64_CE=y
+CONFIG_CRYPTO_GHASH_ARM64_CE=y
+CONFIG_CRYPTO_AES_ARM64_CE_CCM=y
+CONFIG_CRYPTO_AES_ARM64_CE_BLK=y
+CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
+CONFIG_CRYPTO_CRC32_ARM64=y
+CONFIG_QMI_ENCDEC=y
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 59e795b..dca942b 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -7,6 +7,9 @@
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_IRQ_TIME_ACCOUNTING=y
 CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
 CONFIG_RCU_EXPERT=y
 CONFIG_RCU_FAST_NO_HZ=y
 CONFIG_RCU_NOCB_CPU=y
@@ -18,6 +21,9 @@
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_BLK_CGROUP=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
@@ -49,6 +55,7 @@
 CONFIG_MODULE_SIG_FORCE=y
 CONFIG_MODULE_SIG_SHA512=y
 CONFIG_PARTITION_ADVANCED=y
+CONFIG_CFQ_GROUP_IOSCHED=y
 CONFIG_ARCH_QCOM=y
 CONFIG_ARCH_SDM670=y
 CONFIG_PCI=y
@@ -60,12 +67,15 @@
 CONFIG_CMA=y
 CONFIG_ZSMALLOC=y
 CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
 CONFIG_SECCOMP=y
 CONFIG_ARMV8_DEPRECATED=y
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
 # CONFIG_ARM64_VHE is not set
+CONFIG_RANDOMIZE_BASE=y
+# CONFIG_EFI is not set
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_COMPAT=y
@@ -127,6 +137,7 @@
 CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
 CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
 CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
 CONFIG_NETFILTER_XT_TARGET_LOG=y
 CONFIG_NETFILTER_XT_TARGET_MARK=y
 CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -202,6 +213,8 @@
 CONFIG_NET_SCH_INGRESS=y
 CONFIG_NET_CLS_FW=y
 CONFIG_NET_CLS_U32=y
+CONFIG_CLS_U32_MARK=y
+CONFIG_NET_CLS_FLOW=y
 CONFIG_NET_EMATCH=y
 CONFIG_NET_EMATCH_CMP=y
 CONFIG_NET_EMATCH_NBYTE=y
@@ -224,12 +237,14 @@
 CONFIG_IPC_ROUTER=y
 CONFIG_IPC_ROUTER_SECURITY=y
 CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
 CONFIG_DMA_CMA=y
 CONFIG_ZRAM=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=8192
 CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
 CONFIG_MEMORY_STATE_TIME=y
 CONFIG_QPNP_MISC=y
 CONFIG_SCSI=y
@@ -261,14 +276,19 @@
 CONFIG_PPP=y
 CONFIG_PPP_BSDCOMP=y
 CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
 CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
 CONFIG_PPPOLAC=y
 CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
 CONFIG_USB_USBNET=y
-CONFIG_WIL6210=m
-# CONFIG_WIL6210_TRACING is not set
 CONFIG_WCNSS_MEM_PRE_ALLOC=y
 CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 # CONFIG_INPUT_MOUSE is not set
@@ -286,6 +306,7 @@
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
 CONFIG_MSM_ADSPRPC=y
 CONFIG_MSM_RDBG=m
 CONFIG_I2C_CHARDEV=y
@@ -321,18 +342,22 @@
 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_AOP_REG_COOLING_DEVICE=y
 CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
 CONFIG_REGULATOR_CPRH_KBSS=y
 CONFIG_REGULATOR_QPNP_LABIBB=y
 CONFIG_REGULATOR_QPNP_LCDB=y
 CONFIG_REGULATOR_QPNP_OLEDB=y
 CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_REFGEN=y
 CONFIG_REGULATOR_RPMH=y
 CONFIG_REGULATOR_STUB=y
 CONFIG_MEDIA_SUPPORT=y
@@ -350,8 +375,7 @@
 CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
 CONFIG_DVB_MPQ=m
 CONFIG_DVB_MPQ_DEMUX=m
-CONFIG_DVB_MPQ_TSPP1=y
-CONFIG_TSPP=m
+CONFIG_DVB_MPQ_SW=y
 CONFIG_QCOM_KGSL=y
 CONFIG_DRM=y
 CONFIG_DRM_SDE_EVTLOG_DEBUG=y
@@ -390,6 +414,7 @@
 CONFIG_USB_PD_POLICY=y
 CONFIG_QPNP_USB_PDPHY=y
 CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_LINK_LAYER_TEST=y
 CONFIG_NOP_USB_XCEIV=y
 CONFIG_DUAL_ROLE_USB_INTF=y
 CONFIG_USB_MSM_SSPHY_QMP=y
@@ -422,6 +447,8 @@
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_SDHCI_MSM_ICE=y
+CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_QPNP=y
@@ -453,9 +480,14 @@
 CONFIG_MSM_11AD=m
 CONFIG_SEEMP_CORE=y
 CONFIG_QCOM_GENI_SE=y
+CONFIG_MSM_GCC_SDM845=y
+CONFIG_MSM_VIDEOCC_SDM845=y
+CONFIG_MSM_CAMCC_SDM845=y
+CONFIG_MSM_DISPCC_SDM845=y
 CONFIG_CLOCK_QPNP_DIV=y
 CONFIG_MSM_CLK_RPMH=y
 CONFIG_CLOCK_CPU_OSM=y
+CONFIG_MSM_GPUCC_SDM845=y
 CONFIG_MSM_CLK_AOP_QMP=y
 CONFIG_QCOM_MDSS_PLL=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -469,6 +501,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM670_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -476,6 +509,8 @@
 CONFIG_QCOM_WATCHDOG_V2=y
 CONFIG_QPNP_PBS=y
 CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_BUS_CONFIG_RPMH=y
 CONFIG_QCOM_SECURE_BUFFER=y
 CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
@@ -488,24 +523,28 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
+CONFIG_MSM_SYSMON_GLINK_COMM=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
 CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_ICNSS=y
 CONFIG_QCOM_COMMAND_DB=y
 CONFIG_MSM_PERFORMANCE=y
 CONFIG_MSM_CDSP_LOADER=y
+CONFIG_QCOM_SMCINVOKE=y
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
 CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
+CONFIG_QCOM_DCC_V2=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
+CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_MSM_REMOTEQDSS=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_ARM_MEMLAT_MON=y
 CONFIG_QCOMCCI_HWMON=y
@@ -538,6 +577,7 @@
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_PRINTK_TIME=y
@@ -556,13 +596,16 @@
 CONFIG_CORESIGHT_TPDA=y
 CONFIG_CORESIGHT_TPDM=y
 CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
 CONFIG_CORESIGHT_HWEVENT=y
 CONFIG_CORESIGHT_DUMMY=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_CTR=y
 CONFIG_CRYPTO_XCBC=y
 CONFIG_CRYPTO_MD4=y
 CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index bdc27ea..4d7db53 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -22,24 +22,31 @@
 CONFIG_CPUSETS=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_CGROUP_SCHEDTUNE=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
+CONFIG_BLK_CGROUP=y
+CONFIG_DEBUG_BLK_CGROUP=y
 CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
 CONFIG_SCHED_AUTOGROUP=y
 CONFIG_SCHED_TUNE=y
+CONFIG_DEFAULT_USE_ENERGY_AWARE=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_RD_XZ is not set
 # CONFIG_RD_LZO is not set
 # CONFIG_RD_LZ4 is not set
 CONFIG_KALLSYMS_ALL=y
+CONFIG_BPF_SYSCALL=y
 # CONFIG_AIO is not set
 # CONFIG_MEMBARRIER is not set
 CONFIG_EMBEDDED=y
 # CONFIG_COMPAT_BRK is not set
 CONFIG_PROFILING=y
-CONFIG_CC_STACKPROTECTOR_REGULAR=y
+CONFIG_CC_STACKPROTECTOR_STRONG=y
 CONFIG_ARCH_MMAP_RND_COMPAT_BITS=16
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
@@ -51,6 +58,7 @@
 # CONFIG_BLK_DEV_BSG is not set
 CONFIG_PARTITION_ADVANCED=y
 # CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_CFQ_GROUP_IOSCHED=y
 CONFIG_ARCH_QCOM=y
 CONFIG_ARCH_SDM670=y
 CONFIG_PCI=y
@@ -64,12 +72,14 @@
 CONFIG_CMA_DEBUGFS=y
 CONFIG_ZSMALLOC=y
 CONFIG_BALANCE_ANON_FILE_RECLAIM=y
+CONFIG_PROCESS_RECLAIM=y
 CONFIG_SECCOMP=y
 CONFIG_ARMV8_DEPRECATED=y
 CONFIG_SWP_EMULATION=y
 CONFIG_CP15_BARRIER_EMULATION=y
 CONFIG_SETEND_EMULATION=y
 # CONFIG_ARM64_VHE is not set
+CONFIG_RANDOMIZE_BASE=y
 CONFIG_BUILD_ARM64_APPENDED_DTB_IMAGE=y
 # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 CONFIG_COMPAT=y
@@ -132,6 +142,7 @@
 CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
 CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
 CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
+CONFIG_NETFILTER_XT_TARGET_HARDIDLETIMER=y
 CONFIG_NETFILTER_XT_TARGET_LOG=y
 CONFIG_NETFILTER_XT_TARGET_MARK=y
 CONFIG_NETFILTER_XT_TARGET_NFLOG=y
@@ -159,6 +170,7 @@
 CONFIG_NETFILTER_XT_MATCH_MULTIPORT=y
 CONFIG_NETFILTER_XT_MATCH_POLICY=y
 CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
 CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
@@ -234,6 +246,7 @@
 CONFIG_IPC_ROUTER=y
 CONFIG_IPC_ROUTER_SECURITY=y
 CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
 CONFIG_DMA_CMA=y
 CONFIG_ZRAM=y
 CONFIG_BLK_DEV_LOOP=y
@@ -258,6 +271,7 @@
 CONFIG_MD=y
 CONFIG_BLK_DEV_DM=y
 CONFIG_DM_CRYPT=y
+CONFIG_DM_REQ_CRYPT=y
 CONFIG_DM_UEVENT=y
 CONFIG_DM_VERITY=y
 CONFIG_DM_VERITY_FEC=y
@@ -268,13 +282,19 @@
 CONFIG_PPP=y
 CONFIG_PPP_BSDCOMP=y
 CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_FILTER=y
 CONFIG_PPP_MPPE=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=y
+CONFIG_PPPOL2TP=y
 CONFIG_PPPOLAC=y
 CONFIG_PPPOPNS=y
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=y
 CONFIG_USB_USBNET=y
-CONFIG_WIL6210=m
 CONFIG_WCNSS_MEM_PRE_ALLOC=y
 CONFIG_CLD_LL_CORE=y
+CONFIG_CNSS_GENL=y
 CONFIG_INPUT_EVDEV=y
 CONFIG_KEYBOARD_GPIO=y
 # CONFIG_INPUT_MOUSE is not set
@@ -292,6 +312,7 @@
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
 CONFIG_MSM_ADSPRPC=y
 CONFIG_I2C_CHARDEV=y
 CONFIG_I2C_QCOM_GENI=y
@@ -330,18 +351,23 @@
 CONFIG_QTI_THERMAL_LIMITS_DCVS=y
 CONFIG_QTI_VIRTUAL_SENSOR=y
 CONFIG_QTI_AOP_REG_COOLING_DEVICE=y
+CONFIG_QTI_QMI_COOLING_DEVICE=y
+CONFIG_REGULATOR_COOLING_DEVICE=y
 CONFIG_MFD_I2C_PMIC=y
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_PROXY_CONSUMER=y
 CONFIG_REGULATOR_CPRH_KBSS=y
 CONFIG_REGULATOR_QPNP_LABIBB=y
 CONFIG_REGULATOR_QPNP_LCDB=y
 CONFIG_REGULATOR_QPNP_OLEDB=y
 CONFIG_REGULATOR_QPNP=y
+CONFIG_REGULATOR_REFGEN=y
 CONFIG_REGULATOR_RPMH=y
 CONFIG_REGULATOR_STUB=y
 CONFIG_MEDIA_SUPPORT=y
 CONFIG_MEDIA_CAMERA_SUPPORT=y
+CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
 CONFIG_MEDIA_CONTROLLER=y
 CONFIG_VIDEO_V4L2_SUBDEV_API=y
 CONFIG_VIDEO_ADV_DEBUG=y
@@ -352,6 +378,9 @@
 CONFIG_MSM_VIDC_GOVERNORS=y
 CONFIG_MSM_SDE_ROTATOR=y
 CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y
+CONFIG_DVB_MPQ=m
+CONFIG_DVB_MPQ_DEMUX=m
+CONFIG_DVB_MPQ_SW=y
 CONFIG_QCOM_KGSL=y
 CONFIG_DRM=y
 CONFIG_DRM_SDE_EVTLOG_DEBUG=y
@@ -370,7 +399,11 @@
 CONFIG_SND_SOC=y
 CONFIG_UHID=y
 CONFIG_HID_APPLE=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_MAGICMOUSE=y
 CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_PLANTRONICS=y
 CONFIG_USB=y
 CONFIG_USB_XHCI_HCD=y
 CONFIG_USB_EHCI_HCD=y
@@ -385,6 +418,7 @@
 CONFIG_USB_PD_POLICY=y
 CONFIG_QPNP_USB_PDPHY=y
 CONFIG_USB_EHSET_TEST_FIXTURE=y
+CONFIG_USB_LINK_LAYER_TEST=y
 CONFIG_NOP_USB_XCEIV=y
 CONFIG_DUAL_ROLE_USB_INTF=y
 CONFIG_USB_MSM_SSPHY_QMP=y
@@ -410,6 +444,7 @@
 CONFIG_MMC=y
 CONFIG_MMC_PERF_PROFILING=y
 CONFIG_MMC_RING_BUFFER=y
+CONFIG_MMC_PARANOID_SD_INIT=y
 CONFIG_MMC_CLKGATE=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
@@ -417,6 +452,8 @@
 CONFIG_MMC_SDHCI=y
 CONFIG_MMC_SDHCI_PLTFM=y
 CONFIG_MMC_SDHCI_MSM=y
+CONFIG_MMC_SDHCI_MSM_ICE=y
+CONFIG_MMC_CQ_HCI=y
 CONFIG_NEW_LEDS=y
 CONFIG_LEDS_CLASS=y
 CONFIG_LEDS_QPNP=y
@@ -427,9 +464,8 @@
 CONFIG_EDAC=y
 CONFIG_EDAC_MM_EDAC=y
 CONFIG_EDAC_KRYO3XX_ARM64=y
+CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_CE=y
 CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_UE=y
-CONFIG_EDAC_QCOM_LLCC=y
-CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
@@ -455,9 +491,14 @@
 CONFIG_MSM_11AD=m
 CONFIG_SEEMP_CORE=y
 CONFIG_QCOM_GENI_SE=y
+CONFIG_MSM_GCC_SDM845=y
+CONFIG_MSM_VIDEOCC_SDM845=y
+CONFIG_MSM_CAMCC_SDM845=y
+CONFIG_MSM_DISPCC_SDM845=y
 CONFIG_CLOCK_QPNP_DIV=y
 CONFIG_MSM_CLK_RPMH=y
 CONFIG_CLOCK_CPU_OSM=y
+CONFIG_MSM_GPUCC_SDM845=y
 CONFIG_MSM_CLK_AOP_QMP=y
 CONFIG_QCOM_MDSS_PLL=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -472,6 +513,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM670_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -482,6 +524,8 @@
 CONFIG_QCOM_WATCHDOG_V2=y
 CONFIG_QPNP_PBS=y
 CONFIG_QCOM_MEMORY_DUMP_V2=y
+CONFIG_QCOM_BUS_SCALING=y
+CONFIG_QCOM_BUS_CONFIG_RPMH=y
 CONFIG_QCOM_SECURE_BUFFER=y
 CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
@@ -494,12 +538,12 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
 CONFIG_MSM_SUBSYSTEM_RESTART=y
 CONFIG_MSM_PIL=y
+CONFIG_MSM_SYSMON_GLINK_COMM=y
 CONFIG_MSM_PIL_SSR_GENERIC=y
 CONFIG_MSM_PIL_MSS_QDSP6V5=y
 CONFIG_ICNSS=y
@@ -507,6 +551,7 @@
 CONFIG_QCOM_COMMAND_DB=y
 CONFIG_MSM_PERFORMANCE=y
 CONFIG_MSM_CDSP_LOADER=y
+CONFIG_QCOM_SMCINVOKE=y
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
 CONFIG_MSM_QBT1000=y
@@ -514,6 +559,8 @@
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
 CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_MSM_REMOTEQDSS=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_ARM_MEMLAT_MON=y
 CONFIG_QCOMCCI_HWMON=y
@@ -547,6 +594,7 @@
 CONFIG_EFIVAR_FS=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
@@ -579,7 +627,6 @@
 CONFIG_PANIC_ON_RT_THROTTLING=y
 CONFIG_SCHEDSTATS=y
 CONFIG_SCHED_STACK_END_CHECK=y
-# CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
 CONFIG_DEBUG_ATOMIC_SLEEP=y
@@ -611,13 +658,17 @@
 CONFIG_CORESIGHT_TPDA=y
 CONFIG_CORESIGHT_TPDM=y
 CONFIG_CORESIGHT_CTI=y
+CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_TGU=y
 CONFIG_CORESIGHT_HWEVENT=y
 CONFIG_CORESIGHT_DUMMY=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
+CONFIG_FORTIFY_SOURCE=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
+CONFIG_CRYPTO_CTR=y
 CONFIG_CRYPTO_XCBC=y
 CONFIG_CRYPTO_MD4=y
 CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index d2d2bf0..357a6b2 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -7,6 +7,9 @@
 CONFIG_HIGH_RES_TIMERS=y
 CONFIG_IRQ_TIME_ACCOUNTING=y
 CONFIG_SCHED_WALT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
 CONFIG_RCU_EXPERT=y
 CONFIG_RCU_FAST_NO_HZ=y
 CONFIG_RCU_NOCB_CPU=y
@@ -21,6 +24,7 @@
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
+CONFIG_SCHED_CORE_ROTATE=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
@@ -187,6 +191,7 @@
 CONFIG_IP_NF_ARP_MANGLE=y
 CONFIG_NF_CONNTRACK_IPV6=y
 CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
 CONFIG_IP6_NF_MATCH_RPFILTER=y
 CONFIG_IP6_NF_FILTER=y
 CONFIG_IP6_NF_TARGET_REJECT=y
@@ -228,12 +233,14 @@
 CONFIG_IPC_ROUTER=y
 CONFIG_IPC_ROUTER_SECURITY=y
 CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
 CONFIG_DMA_CMA=y
 CONFIG_ZRAM=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_RAM_SIZE=8192
 CONFIG_QSEECOM=y
+CONFIG_UID_SYS_STATS=y
 CONFIG_MEMORY_STATE_TIME=y
 CONFIG_SCSI=y
 CONFIG_BLK_DEV_SD=y
@@ -296,6 +303,7 @@
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
 CONFIG_MSM_ADSPRPC=y
 CONFIG_MSM_RDBG=m
 CONFIG_I2C_CHARDEV=y
@@ -341,7 +349,6 @@
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_PROXY_CONSUMER=y
-CONFIG_REGULATOR_CPRH_KBSS=y
 CONFIG_REGULATOR_QPNP_LABIBB=y
 CONFIG_REGULATOR_QPNP=y
 CONFIG_REGULATOR_REFGEN=y
@@ -487,6 +494,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -507,7 +515,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
@@ -524,10 +531,11 @@
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
 CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
 CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
+CONFIG_QSEE_IPC_IRQ_BRIDGE=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_ARM_MEMLAT_MON=y
 CONFIG_QCOMCCI_HWMON=y
@@ -551,6 +559,9 @@
 CONFIG_EXT2_FS_XATTR=y
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
 CONFIG_QUOTA=y
 CONFIG_QUOTA_NETLINK_INTERFACE=y
 CONFIG_QFMT_V2=y
@@ -560,6 +571,7 @@
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_PRINTK_TIME=y
@@ -581,13 +593,13 @@
 CONFIG_CORESIGHT_EVENT=y
 CONFIG_CORESIGHT_HWEVENT=y
 CONFIG_CORESIGHT_DUMMY=y
+CONFIG_PFK=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
 CONFIG_FORTIFY_SOURCE=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
 CONFIG_CRYPTO_XCBC=y
 CONFIG_CRYPTO_MD4=y
 CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index 4b347e6..d0a32e7 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -25,6 +25,7 @@
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_BPF=y
 CONFIG_SCHED_CORE_CTL=y
+CONFIG_SCHED_CORE_ROTATE=y
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
@@ -193,6 +194,7 @@
 CONFIG_IP_NF_ARP_MANGLE=y
 CONFIG_NF_CONNTRACK_IPV6=y
 CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_IPTABLES_128=y
 CONFIG_IP6_NF_MATCH_RPFILTER=y
 CONFIG_IP6_NF_FILTER=y
 CONFIG_IP6_NF_TARGET_REJECT=y
@@ -237,6 +239,7 @@
 CONFIG_IPC_ROUTER=y
 CONFIG_IPC_ROUTER_SECURITY=y
 CONFIG_FW_LOADER_USER_HELPER_FALLBACK=y
+CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS=y
 CONFIG_DMA_CMA=y
 CONFIG_ZRAM=y
 CONFIG_BLK_DEV_LOOP=y
@@ -303,6 +306,7 @@
 CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_HW_RANDOM_MSM_LEGACY=y
+# CONFIG_DEVPORT is not set
 CONFIG_MSM_ADSPRPC=y
 CONFIG_MSM_RDBG=m
 CONFIG_I2C_CHARDEV=y
@@ -348,7 +352,6 @@
 CONFIG_MFD_SPMI_PMIC=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_PROXY_CONSUMER=y
-CONFIG_REGULATOR_CPRH_KBSS=y
 CONFIG_REGULATOR_QPNP_LABIBB=y
 CONFIG_REGULATOR_QPNP=y
 CONFIG_REGULATOR_REFGEN=y
@@ -454,9 +457,6 @@
 CONFIG_EDAC_KRYO3XX_ARM64=y
 CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_CE=y
 CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_UE=y
-CONFIG_EDAC_QCOM_LLCC=y
-CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE=y
-CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_QPNP=y
 CONFIG_DMADEVICES=y
@@ -504,6 +504,7 @@
 CONFIG_QCOM_RUN_QUEUE_STATS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_SDM845_LLCC=y
+CONFIG_QCOM_LLCC_PERFMON=m
 CONFIG_MSM_SERVICE_LOCATOR=y
 CONFIG_MSM_SERVICE_NOTIFIER=y
 CONFIG_MSM_BOOT_STATS=y
@@ -526,7 +527,6 @@
 CONFIG_TRACER_PKT=y
 CONFIG_QTI_RPMH_API=y
 CONFIG_MSM_SMP2P=y
-CONFIG_MSM_SMP2P_TEST=y
 CONFIG_MSM_IPC_ROUTER_GLINK_XPRT=y
 CONFIG_MSM_QMI_INTERFACE=y
 CONFIG_MSM_GLINK_PKT=y
@@ -544,12 +544,13 @@
 CONFIG_MSM_EVENT_TIMER=y
 CONFIG_MSM_PM=y
 CONFIG_MSM_QBT1000=y
-CONFIG_APSS_CORE_EA=y
 CONFIG_QCOM_DCC_V2=y
 CONFIG_QTI_RPM_STATS_LOG=y
 CONFIG_QCOM_FORCE_WDOG_BITE_ON_PANIC=y
 CONFIG_QMP_DEBUGFS_CLIENT=y
+CONFIG_MEM_SHARE_QMI_SERVICE=y
 CONFIG_MSM_REMOTEQDSS=y
+CONFIG_QSEE_IPC_IRQ_BRIDGE=y
 CONFIG_QCOM_BIMC_BWMON=y
 CONFIG_ARM_MEMLAT_MON=y
 CONFIG_QCOMCCI_HWMON=y
@@ -574,6 +575,9 @@
 CONFIG_EXT2_FS_XATTR=y
 CONFIG_EXT3_FS=y
 CONFIG_EXT4_FS_SECURITY=y
+CONFIG_EXT4_ENCRYPTION=y
+CONFIG_EXT4_FS_ENCRYPTION=y
+CONFIG_EXT4_FS_ICE_ENCRYPTION=y
 CONFIG_QUOTA=y
 CONFIG_QUOTA_NETLINK_INTERFACE=y
 CONFIG_QFMT_V2=y
@@ -584,6 +588,7 @@
 CONFIG_EFIVAR_FS=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
+CONFIG_SDCARD_FS=y
 # CONFIG_NETWORK_FILESYSTEMS is not set
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
@@ -610,13 +615,13 @@
 CONFIG_DEBUG_STACK_USAGE=y
 CONFIG_DEBUG_MEMORY_INIT=y
 CONFIG_LOCKUP_DETECTOR=y
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
 CONFIG_WQ_WATCHDOG=y
 CONFIG_PANIC_TIMEOUT=5
 CONFIG_PANIC_ON_SCHED_BUG=y
 CONFIG_PANIC_ON_RT_THROTTLING=y
 CONFIG_SCHEDSTATS=y
 CONFIG_SCHED_STACK_END_CHECK=y
-# CONFIG_DEBUG_PREEMPT is not set
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
 CONFIG_DEBUG_ATOMIC_SLEEP=y
@@ -650,15 +655,16 @@
 CONFIG_CORESIGHT_TPDM=y
 CONFIG_CORESIGHT_CTI=y
 CONFIG_CORESIGHT_EVENT=y
+CONFIG_CORESIGHT_TGU=y
 CONFIG_CORESIGHT_HWEVENT=y
 CONFIG_CORESIGHT_DUMMY=y
+CONFIG_PFK=y
 CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
 CONFIG_SECURITY=y
 CONFIG_HARDENED_USERCOPY=y
 CONFIG_FORTIFY_SOURCE=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
-CONFIG_CRYPTO_CTR=y
 CONFIG_CRYPTO_XCBC=y
 CONFIG_CRYPTO_MD4=y
 CONFIG_CRYPTO_TWOFISH=y
diff --git a/arch/arm64/include/asm/arch_gicv3.h b/arch/arm64/include/asm/arch_gicv3.h
index 6ebd2c3..f1ace59 100644
--- a/arch/arm64/include/asm/arch_gicv3.h
+++ b/arch/arm64/include/asm/arch_gicv3.h
@@ -21,6 +21,7 @@
 #include <asm/sysreg.h>
 
 #define ICC_EOIR1_EL1			sys_reg(3, 0, 12, 12, 1)
+#define ICC_HPPIR1_EL1			sys_reg(3, 0, 12, 12, 2)
 #define ICC_DIR_EL1			sys_reg(3, 0, 12, 11, 1)
 #define ICC_IAR1_EL1			sys_reg(3, 0, 12, 12, 0)
 #define ICC_SGI1R_EL1			sys_reg(3, 0, 12, 11, 5)
diff --git a/arch/arm64/include/asm/dma-iommu.h b/arch/arm64/include/asm/dma-iommu.h
index ab0e5b2..cfd49b2 100644
--- a/arch/arm64/include/asm/dma-iommu.h
+++ b/arch/arm64/include/asm/dma-iommu.h
@@ -14,13 +14,17 @@
 struct dma_iommu_mapping {
 	/* iommu specific data */
 	struct iommu_domain	*domain;
+	bool			init;
+	struct kref		kref;
+	const struct dma_map_ops *ops;
 
+	/* Protects bitmap */
+	spinlock_t		lock;
 	void			*bitmap;
 	size_t			bits;
 	dma_addr_t		base;
-
-	spinlock_t		lock;
-	struct kref		kref;
+	u32			min_iova_align;
+	struct page		*guard_page;
 
 	struct dma_fast_smmu_mapping *fast;
 };
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
index afa23b0..1fb0230 100644
--- a/arch/arm64/include/asm/elf.h
+++ b/arch/arm64/include/asm/elf.h
@@ -114,10 +114,10 @@
 
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
  * space open for things that want to use the area for 32-bit pointers.
  */
-#define ELF_ET_DYN_BASE		0x100000000UL
+#define ELF_ET_DYN_BASE		(2 * TASK_SIZE_64 / 3)
 
 #ifndef __ASSEMBLY__
 
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index 5edb6ed..f3a142e 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -165,6 +165,11 @@
 /* the offset between the kernel virtual and physical mappings */
 extern u64			kimage_voffset;
 
+static inline unsigned long kaslr_offset(void)
+{
+	return kimage_vaddr - KIMAGE_VADDR;
+}
+
 /*
  * Allow all memory at the discovery stage. We will clip it later.
  */
diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
index 06ff7fd..b6c6fa2 100644
--- a/arch/arm64/include/asm/module.h
+++ b/arch/arm64/include/asm/module.h
@@ -22,14 +22,19 @@
 #define MODULE_ARCH_VERMAGIC	"aarch64"
 
 #ifdef CONFIG_ARM64_MODULE_PLTS
-struct mod_arch_specific {
+struct mod_plt_sec {
 	struct elf64_shdr	*plt;
 	int			plt_num_entries;
 	int			plt_max_entries;
 };
+
+struct mod_arch_specific {
+	struct mod_plt_sec	core;
+	struct mod_plt_sec	init;
+};
 #endif
 
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
 			  Elf64_Sym *sym);
 
 #ifdef CONFIG_RANDOMIZE_BASE
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 394c61d..1d5890f 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -157,9 +157,11 @@
 
 void fpsimd_flush_thread(void)
 {
+	preempt_disable();
 	memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
 	fpsimd_flush_task_state(current);
 	set_thread_flag(TIF_FOREIGN_FPSTATE);
+	preempt_enable();
 }
 
 /*
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
index 7ee6d74..c186586 100644
--- a/arch/arm64/kernel/head.S
+++ b/arch/arm64/kernel/head.S
@@ -487,6 +487,7 @@
  * booted in EL1 or EL2 respectively.
  */
 ENTRY(el2_setup)
+	msr	SPsel, #1			// We want to use SP_EL{1,2}
 	mrs	x0, CurrentEL
 	cmp	x0, #CurrentEL_EL2
 	b.ne	1f
diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
index 1ce90d8..d05dbe6 100644
--- a/arch/arm64/kernel/module-plts.c
+++ b/arch/arm64/kernel/module-plts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014-2016 Linaro Ltd. <ard.biesheuvel@linaro.org>
+ * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -26,35 +26,21 @@
 	__le32	br;	/* br	x16				*/
 };
 
-u64 module_emit_plt_entry(struct module *mod, const Elf64_Rela *rela,
+static bool in_init(const struct module *mod, void *loc)
+{
+	return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size;
+}
+
+u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela,
 			  Elf64_Sym *sym)
 {
-	struct plt_entry *plt = (struct plt_entry *)mod->arch.plt->sh_addr;
-	int i = mod->arch.plt_num_entries;
+	struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core :
+							  &mod->arch.init;
+	struct plt_entry *plt = (struct plt_entry *)pltsec->plt->sh_addr;
+	int i = pltsec->plt_num_entries;
 	u64 val = sym->st_value + rela->r_addend;
 
 	/*
-	 * We only emit PLT entries against undefined (SHN_UNDEF) symbols,
-	 * which are listed in the ELF symtab section, but without a type
-	 * or a size.
-	 * So, similar to how the module loader uses the Elf64_Sym::st_value
-	 * field to store the resolved addresses of undefined symbols, let's
-	 * borrow the Elf64_Sym::st_size field (whose value is never used by
-	 * the module loader, even for symbols that are defined) to record
-	 * the address of a symbol's associated PLT entry as we emit it for a
-	 * zero addend relocation (which is the only kind we have to deal with
-	 * in practice). This allows us to find duplicates without having to
-	 * go through the table every time.
-	 */
-	if (rela->r_addend == 0 && sym->st_size != 0) {
-		BUG_ON(sym->st_size < (u64)plt || sym->st_size >= (u64)&plt[i]);
-		return sym->st_size;
-	}
-
-	mod->arch.plt_num_entries++;
-	BUG_ON(mod->arch.plt_num_entries > mod->arch.plt_max_entries);
-
-	/*
 	 * MOVK/MOVN/MOVZ opcode:
 	 * +--------+------------+--------+-----------+-------------+---------+
 	 * | sf[31] | opc[30:29] | 100101 | hw[22:21] | imm16[20:5] | Rd[4:0] |
@@ -72,8 +58,19 @@
 		cpu_to_le32(0xd61f0200)
 	};
 
-	if (rela->r_addend == 0)
-		sym->st_size = (u64)&plt[i];
+	/*
+	 * Check if the entry we just created is a duplicate. Given that the
+	 * relocations are sorted, this will be the last entry we allocated.
+	 * (if one exists).
+	 */
+	if (i > 0 &&
+	    plt[i].mov0 == plt[i - 1].mov0 &&
+	    plt[i].mov1 == plt[i - 1].mov1 &&
+	    plt[i].mov2 == plt[i - 1].mov2)
+		return (u64)&plt[i - 1];
+
+	pltsec->plt_num_entries++;
+	BUG_ON(pltsec->plt_num_entries > pltsec->plt_max_entries);
 
 	return (u64)&plt[i];
 }
@@ -104,7 +101,8 @@
 	return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0;
 }
 
-static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num)
+static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num,
+			       Elf64_Word dstidx)
 {
 	unsigned int ret = 0;
 	Elf64_Sym *s;
@@ -116,13 +114,17 @@
 		case R_AARCH64_CALL26:
 			/*
 			 * We only have to consider branch targets that resolve
-			 * to undefined symbols. This is not simply a heuristic,
-			 * it is a fundamental limitation, since the PLT itself
-			 * is part of the module, and needs to be within 128 MB
-			 * as well, so modules can never grow beyond that limit.
+			 * to symbols that are defined in a different section.
+			 * This is not simply a heuristic, it is a fundamental
+			 * limitation, since there is no guaranteed way to emit
+			 * PLT entries sufficiently close to the branch if the
+			 * section size exceeds the range of a branch
+			 * instruction. So ignore relocations against defined
+			 * symbols if they live in the same section as the
+			 * relocation target.
 			 */
 			s = syms + ELF64_R_SYM(rela[i].r_info);
-			if (s->st_shndx != SHN_UNDEF)
+			if (s->st_shndx == dstidx)
 				break;
 
 			/*
@@ -149,7 +151,8 @@
 int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
 			      char *secstrings, struct module *mod)
 {
-	unsigned long plt_max_entries = 0;
+	unsigned long core_plts = 0;
+	unsigned long init_plts = 0;
 	Elf64_Sym *syms = NULL;
 	int i;
 
@@ -158,14 +161,16 @@
 	 * entries. Record the symtab address as well.
 	 */
 	for (i = 0; i < ehdr->e_shnum; i++) {
-		if (strcmp(".plt", secstrings + sechdrs[i].sh_name) == 0)
-			mod->arch.plt = sechdrs + i;
+		if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
+			mod->arch.core.plt = sechdrs + i;
+		else if (!strcmp(secstrings + sechdrs[i].sh_name, ".init.plt"))
+			mod->arch.init.plt = sechdrs + i;
 		else if (sechdrs[i].sh_type == SHT_SYMTAB)
 			syms = (Elf64_Sym *)sechdrs[i].sh_addr;
 	}
 
-	if (!mod->arch.plt) {
-		pr_err("%s: module PLT section missing\n", mod->name);
+	if (!mod->arch.core.plt || !mod->arch.init.plt) {
+		pr_err("%s: module PLT section(s) missing\n", mod->name);
 		return -ENOEXEC;
 	}
 	if (!syms) {
@@ -188,14 +193,27 @@
 		/* sort by type, symbol index and addend */
 		sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL);
 
-		plt_max_entries += count_plts(syms, rels, numrels);
+		if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0)
+			core_plts += count_plts(syms, rels, numrels,
+						sechdrs[i].sh_info);
+		else
+			init_plts += count_plts(syms, rels, numrels,
+						sechdrs[i].sh_info);
 	}
 
-	mod->arch.plt->sh_type = SHT_NOBITS;
-	mod->arch.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
-	mod->arch.plt->sh_addralign = L1_CACHE_BYTES;
-	mod->arch.plt->sh_size = plt_max_entries * sizeof(struct plt_entry);
-	mod->arch.plt_num_entries = 0;
-	mod->arch.plt_max_entries = plt_max_entries;
+	mod->arch.core.plt->sh_type = SHT_NOBITS;
+	mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+	mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES;
+	mod->arch.core.plt->sh_size = (core_plts  + 1) * sizeof(struct plt_entry);
+	mod->arch.core.plt_num_entries = 0;
+	mod->arch.core.plt_max_entries = core_plts;
+
+	mod->arch.init.plt->sh_type = SHT_NOBITS;
+	mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+	mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES;
+	mod->arch.init.plt->sh_size = (init_plts + 1) * sizeof(struct plt_entry);
+	mod->arch.init.plt_num_entries = 0;
+	mod->arch.init.plt_max_entries = init_plts;
+
 	return 0;
 }
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 7f31698..c9a2ab4 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -380,7 +380,7 @@
 
 			if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
 			    ovf == -ERANGE) {
-				val = module_emit_plt_entry(me, &rel[i], sym);
+				val = module_emit_plt_entry(me, loc, &rel[i], sym);
 				ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
 						     26, AARCH64_INSN_IMM_26);
 			}
diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
index 8949f6c..f7c9781 100644
--- a/arch/arm64/kernel/module.lds
+++ b/arch/arm64/kernel/module.lds
@@ -1,3 +1,4 @@
 SECTIONS {
 	.plt (NOLOAD) : { BYTE(0) }
+	.init.plt (NOLOAD) : { BYTE(0) }
 }
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
index b325f74..c1e932d 100644
--- a/arch/arm64/kernel/perf_event.c
+++ b/arch/arm64/kernel/perf_event.c
@@ -1161,8 +1161,8 @@
 {
 	int ret;
 
-	ret = cpuhp_setup_state_nocalls(CPUHP_AP_NOTIFY_ONLINE,
-				"PERF_EVENT/CPUHP_AP_NOTIFY_ONLINE",
+	ret = cpuhp_setup_state_nocalls(CPUHP_AP_NOTIFY_PERF_ONLINE,
+				"PERF_EVENT/CPUHP_AP_NOTIFY_PERF_ONLINE",
 				perf_event_hotplug_coming_up,
 				perf_event_hotplug_going_down);
 	if (ret)
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index ba29095..a58fb92 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -356,11 +356,11 @@
 static int dump_kernel_offset(struct notifier_block *self, unsigned long v,
 			      void *p)
 {
-	u64 const kaslr_offset = kimage_vaddr - KIMAGE_VADDR;
+	const unsigned long offset = kaslr_offset();
 
-	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_offset > 0) {
-		pr_emerg("Kernel Offset: 0x%llx from 0x%lx\n",
-			 kaslr_offset, KIMAGE_VADDR);
+	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && offset > 0) {
+		pr_emerg("Kernel Offset: 0x%lx from 0x%lx\n",
+			 offset, KIMAGE_VADDR);
 	} else {
 		pr_emerg("Kernel Offset: disabled\n");
 	}
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
index 900c1ec..f7ce3d2 100644
--- a/arch/arm64/kernel/stacktrace.c
+++ b/arch/arm64/kernel/stacktrace.c
@@ -176,7 +176,8 @@
 		trace->entries[trace->nr_entries++] = ULONG_MAX;
 }
 
-void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+static noinline void __save_stack_trace(struct task_struct *tsk,
+	struct stack_trace *trace, unsigned int nosched)
 {
 	struct stack_trace_data data;
 	struct stackframe frame;
@@ -186,17 +187,18 @@
 
 	data.trace = trace;
 	data.skip = trace->skip;
+	data.no_sched_functions = nosched;
 
 	if (tsk != current) {
-		data.no_sched_functions = 1;
 		frame.fp = thread_saved_fp(tsk);
 		frame.sp = thread_saved_sp(tsk);
 		frame.pc = thread_saved_pc(tsk);
 	} else {
-		data.no_sched_functions = 0;
+		/* We don't want this function nor the caller */
+		data.skip += 2;
 		frame.fp = (unsigned long)__builtin_frame_address(0);
 		frame.sp = current_stack_pointer;
-		frame.pc = (unsigned long)save_stack_trace_tsk;
+		frame.pc = (unsigned long)__save_stack_trace;
 	}
 #ifdef CONFIG_FUNCTION_GRAPH_TRACER
 	frame.graph = tsk->curr_ret_stack;
@@ -210,9 +212,15 @@
 }
 EXPORT_SYMBOL(save_stack_trace_tsk);
 
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+	__save_stack_trace(tsk, trace, 1);
+}
+
 void save_stack_trace(struct stack_trace *trace)
 {
-	save_stack_trace_tsk(current, trace);
+	__save_stack_trace(current, trace, 0);
 }
+
 EXPORT_SYMBOL_GPL(save_stack_trace);
 #endif
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 5620500..19f3515 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -114,7 +114,7 @@
 	for (i = -4; i < 1; i++) {
 		unsigned int val, bad;
 
-		bad = __get_user(val, &((u32 *)addr)[i]);
+		bad = get_user(val, &((u32 *)addr)[i]);
 
 		if (!bad)
 			p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
diff --git a/arch/arm64/kvm/hyp/Makefile b/arch/arm64/kvm/hyp/Makefile
index 14c4e3b..48b0354 100644
--- a/arch/arm64/kvm/hyp/Makefile
+++ b/arch/arm64/kvm/hyp/Makefile
@@ -2,7 +2,7 @@
 # Makefile for Kernel-based Virtual Machine module, HYP part
 #
 
-ccflags-y += -fno-stack-protector
+ccflags-y += -fno-stack-protector -DDISABLE_BRANCH_PROFILING
 
 KVM=../../../../virt/kvm
 
diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c
index da6a8cf..3556715 100644
--- a/arch/arm64/kvm/inject_fault.c
+++ b/arch/arm64/kvm/inject_fault.c
@@ -33,12 +33,26 @@
 #define LOWER_EL_AArch64_VECTOR		0x400
 #define LOWER_EL_AArch32_VECTOR		0x600
 
+/*
+ * Table taken from ARMv8 ARM DDI0487B-B, table G1-10.
+ */
+static const u8 return_offsets[8][2] = {
+	[0] = { 0, 0 },		/* Reset, unused */
+	[1] = { 4, 2 },		/* Undefined */
+	[2] = { 0, 0 },		/* SVC, unused */
+	[3] = { 4, 4 },		/* Prefetch abort */
+	[4] = { 8, 8 },		/* Data abort */
+	[5] = { 0, 0 },		/* HVC, unused */
+	[6] = { 4, 4 },		/* IRQ, unused */
+	[7] = { 4, 4 },		/* FIQ, unused */
+};
+
 static void prepare_fault32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset)
 {
 	unsigned long cpsr;
 	unsigned long new_spsr_value = *vcpu_cpsr(vcpu);
 	bool is_thumb = (new_spsr_value & COMPAT_PSR_T_BIT);
-	u32 return_offset = (is_thumb) ? 4 : 0;
+	u32 return_offset = return_offsets[vect_offset >> 2][is_thumb];
 	u32 sctlr = vcpu_cp15(vcpu, c1_SCTLR);
 
 	cpsr = mode | COMPAT_PSR_I_BIT;
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 7f90b7e..31d4684 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -31,13 +31,15 @@
 #include <linux/vmalloc.h>
 #include <linux/swiotlb.h>
 #include <linux/io.h>
+#include <linux/pci.h>
 
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 #include <asm/dma-iommu.h>
 #include <linux/dma-mapping-fast.h>
 #include <linux/msm_dma_iommu_mapping.h>
-
+#include <linux/arm-smmu-errata.h>
+#include <soc/qcom/secure_buffer.h>
 
 
 static int swiotlb __ro_after_init;
@@ -969,14 +971,21 @@
 	 * then the IOMMU core will have already configured a group for this
 	 * device, and allocated the default domain for that group.
 	 */
-	if (!domain || iommu_dma_init_domain(domain, dma_base, size, dev)) {
-		pr_debug("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
-			dev_name(dev));
-		return false;
+	if (!domain)
+		goto out_err;
+
+	if (domain->type == IOMMU_DOMAIN_DMA) {
+		if (iommu_dma_init_domain(domain, dma_base, size, dev))
+			goto out_err;
+
+		dev->archdata.dma_ops = &iommu_dma_ops;
 	}
 
-	dev->archdata.dma_ops = &iommu_dma_ops;
 	return true;
+out_err:
+	pr_debug("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
+		 dev_name(dev));
+	return false;
 }
 
 static void queue_iommu_attach(struct device *dev, const struct iommu_ops *ops,
@@ -1165,15 +1174,24 @@
 static inline dma_addr_t __alloc_iova(struct dma_iommu_mapping *mapping,
 				      size_t size)
 {
-	unsigned int order = get_order(size);
+	unsigned int order;
 	unsigned int align = 0;
 	unsigned int count, start;
 	unsigned long flags;
+	dma_addr_t iova;
+	size_t guard_len;
 
+	size = PAGE_ALIGN(size);
+	if (mapping->min_iova_align)
+		guard_len = ALIGN(size, mapping->min_iova_align) - size;
+	else
+		guard_len = 0;
+
+	order = get_order(size + guard_len);
 	if (order > CONFIG_ARM64_DMA_IOMMU_ALIGNMENT)
 		order = CONFIG_ARM64_DMA_IOMMU_ALIGNMENT;
 
-	count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+	count = PAGE_ALIGN(size + guard_len) >> PAGE_SHIFT;
 	align = (1 << order) - 1;
 
 	spin_lock_irqsave(&mapping->lock, flags);
@@ -1187,16 +1205,41 @@
 	bitmap_set(mapping->bitmap, start, count);
 	spin_unlock_irqrestore(&mapping->lock, flags);
 
-	return mapping->base + (start << PAGE_SHIFT);
+	iova = mapping->base + (start << PAGE_SHIFT);
+
+	if (guard_len &&
+		iommu_map(mapping->domain, iova + size,
+			page_to_phys(mapping->guard_page),
+			guard_len, ARM_SMMU_GUARD_PROT)) {
+
+		spin_lock_irqsave(&mapping->lock, flags);
+		bitmap_clear(mapping->bitmap, start, count);
+		spin_unlock_irqrestore(&mapping->lock, flags);
+		return DMA_ERROR_CODE;
+	}
+
+	return iova;
 }
 
 static inline void __free_iova(struct dma_iommu_mapping *mapping,
 			       dma_addr_t addr, size_t size)
 {
-	unsigned int start = (addr - mapping->base) >> PAGE_SHIFT;
-	unsigned int count = size >> PAGE_SHIFT;
+	unsigned int start;
+	unsigned int count;
 	unsigned long flags;
+	size_t guard_len;
 
+	addr = addr & PAGE_MASK;
+	size = PAGE_ALIGN(size);
+	if (mapping->min_iova_align) {
+		guard_len = ALIGN(size, mapping->min_iova_align) - size;
+		iommu_unmap(mapping->domain, addr + size, guard_len);
+	} else {
+		guard_len = 0;
+	}
+
+	start = (addr - mapping->base) >> PAGE_SHIFT;
+	count = (size + guard_len) >> PAGE_SHIFT;
 	spin_lock_irqsave(&mapping->lock, flags);
 	bitmap_clear(mapping->bitmap, start, count);
 	spin_unlock_irqrestore(&mapping->lock, flags);
@@ -1906,23 +1949,59 @@
  * IO address ranges, which is required to perform memory allocation and
  * mapping with IOMMU aware functions.
  *
- * The client device need to be attached to the mapping with
- * arm_iommu_attach_device function.
+ * Clients may use iommu_domain_set_attr() to set additional flags prior
+ * to calling arm_iommu_attach_device() to complete initialization.
  */
 struct dma_iommu_mapping *
 arm_iommu_create_mapping(struct bus_type *bus, dma_addr_t base, size_t size)
 {
 	unsigned int bits = size >> PAGE_SHIFT;
-	unsigned int bitmap_size = BITS_TO_LONGS(bits) * sizeof(long);
 	struct dma_iommu_mapping *mapping;
-	int err = -ENOMEM;
 
-	if (!bitmap_size)
+	if (!bits)
 		return ERR_PTR(-EINVAL);
 
 	mapping = kzalloc(sizeof(struct dma_iommu_mapping), GFP_KERNEL);
 	if (!mapping)
-		goto err;
+		return ERR_PTR(-ENOMEM);
+
+	mapping->base = base;
+	mapping->bits = bits;
+
+	mapping->domain = iommu_domain_alloc(bus);
+	if (!mapping->domain)
+		goto err_domain_alloc;
+
+	mapping->init = false;
+	return mapping;
+
+err_domain_alloc:
+	kfree(mapping);
+	return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL(arm_iommu_create_mapping);
+
+static int
+bitmap_iommu_init_mapping(struct device *dev, struct dma_iommu_mapping *mapping)
+{
+	unsigned int bitmap_size = BITS_TO_LONGS(mapping->bits) * sizeof(long);
+	int vmid = VMID_HLOS;
+	int min_iova_align = 0;
+
+	iommu_domain_get_attr(mapping->domain,
+			DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
+			&min_iova_align);
+	iommu_domain_get_attr(mapping->domain,
+			DOMAIN_ATTR_SECURE_VMID, &vmid);
+	if (vmid >= VMID_LAST || vmid < 0)
+		vmid = VMID_HLOS;
+
+	if (min_iova_align) {
+		mapping->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN;
+		mapping->guard_page = arm_smmu_errata_get_guard_page(vmid);
+		if (!mapping->guard_page)
+			return -ENOMEM;
+	}
 
 	mapping->bitmap = kzalloc(bitmap_size, GFP_KERNEL | __GFP_NOWARN |
 							__GFP_NORETRY);
@@ -1930,72 +2009,114 @@
 		mapping->bitmap = vzalloc(bitmap_size);
 
 	if (!mapping->bitmap)
-		goto err2;
+		return -ENOMEM;
 
-	mapping->base = base;
-	mapping->bits = bits;
 	spin_lock_init(&mapping->lock);
-
-	mapping->domain = iommu_domain_alloc(bus);
-	if (!mapping->domain)
-		goto err3;
-
-	kref_init(&mapping->kref);
-	return mapping;
-err3:
-	kvfree(mapping->bitmap);
-err2:
-	kfree(mapping);
-err:
-	return ERR_PTR(err);
+	mapping->ops = &iommu_ops;
+	return 0;
 }
-EXPORT_SYMBOL(arm_iommu_create_mapping);
 
-static void release_iommu_mapping(struct kref *kref)
+static void bitmap_iommu_release_mapping(struct kref *kref)
+{
+	struct dma_iommu_mapping *mapping =
+		container_of(kref, struct dma_iommu_mapping, kref);
+
+	kfree(mapping->bitmap);
+	iommu_domain_free(mapping->domain);
+	kfree(mapping);
+}
+
+static void bypass_iommu_release_mapping(struct kref *kref)
 {
 	struct dma_iommu_mapping *mapping =
 		container_of(kref, struct dma_iommu_mapping, kref);
 
 	iommu_domain_free(mapping->domain);
-	kvfree(mapping->bitmap);
 	kfree(mapping);
 }
 
+static int upstream_iommu_init_mapping(struct device *dev,
+					struct dma_iommu_mapping *mapping)
+{
+	struct iommu_domain *domain = mapping->domain;
+	dma_addr_t base = mapping->base;
+	u64 size = mapping->bits << PAGE_SHIFT;
+
+	if (iommu_get_dma_cookie(domain))
+		return -EINVAL;
+
+	if (iommu_dma_init_domain(domain, base, size, dev))
+		goto out_put_cookie;
+
+	mapping->ops = &iommu_dma_ops;
+	return 0;
+
+out_put_cookie:
+	iommu_put_dma_cookie(domain);
+	return -EINVAL;
+}
+
+static void upstream_iommu_release_mapping(struct kref *kref)
+{
+	struct dma_iommu_mapping *mapping =
+		container_of(kref, struct dma_iommu_mapping, kref);
+
+	iommu_put_dma_cookie(mapping->domain);
+	iommu_domain_free(mapping->domain);
+	kfree(mapping);
+}
+
+/*
+ * arm_iommu_release_mapping
+ * @mapping: allocted via arm_iommu_create_mapping()
+ *
+ * Frees all resources associated with the iommu mapping.
+ * The device associated with this mapping must be in the 'detached' state
+ */
 void arm_iommu_release_mapping(struct dma_iommu_mapping *mapping)
 {
-	if (mapping)
-		kref_put(&mapping->kref, release_iommu_mapping);
+	int s1_bypass = 0, is_fast = 0, is_upstream = 0;
+	void (*release)(struct kref *kref);
+
+	if (!mapping)
+		return;
+
+	if (!mapping->init) {
+		iommu_domain_free(mapping->domain);
+		kfree(mapping);
+		return;
+	}
+
+	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
+					&s1_bypass);
+	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast);
+	iommu_domain_get_attr(mapping->domain,
+				DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR,
+				&is_upstream);
+
+	if (s1_bypass)
+		release = bypass_iommu_release_mapping;
+	else if (is_fast)
+		release = fast_smmu_release_mapping;
+	else if (is_upstream)
+		release = upstream_iommu_release_mapping;
+	else
+		release = bitmap_iommu_release_mapping;
+
+	kref_put(&mapping->kref, release);
 }
 EXPORT_SYMBOL(arm_iommu_release_mapping);
 
-/**
- * arm_iommu_attach_device
- * @dev: valid struct device pointer
- * @mapping: io address space mapping structure (returned from
- *	arm_iommu_create_mapping)
- *
- * Attaches specified io address space mapping to the provided device,
- * this replaces the dma operations (dma_map_ops pointer) with the
- * IOMMU aware version. Only one device in an iommu_group may use this
- * function.
- */
-int arm_iommu_attach_device(struct device *dev,
+static int arm_iommu_init_mapping(struct device *dev,
 			    struct dma_iommu_mapping *mapping)
 {
-	int err;
-	int s1_bypass = 0, is_fast = 0;
-	struct iommu_group *group;
+	int err = -EINVAL;
+	int s1_bypass = 0, is_fast = 0, is_upstream = 0;
 	dma_addr_t iova_end;
 
-	group = dev->iommu_group;
-	if (!group) {
-		dev_err(dev, "No iommu associated with device\n");
-		return -ENODEV;
-	}
-
-	if (iommu_get_domain_for_dev(dev)) {
-		dev_err(dev, "Device already attached to other iommu_domain\n");
-		return -EINVAL;
+	if (mapping->init) {
+		kref_get(&mapping->kref);
+		return 0;
 	}
 
 	iova_end = mapping->base + (mapping->bits << PAGE_SHIFT) - 1;
@@ -2005,21 +2126,73 @@
 		return -EINVAL;
 	}
 
+	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
+					&s1_bypass);
 	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast);
-	if (is_fast)
-		return fast_smmu_attach_device(dev, mapping);
+	iommu_domain_get_attr(mapping->domain,
+				DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR,
+				&is_upstream);
 
-	err = iommu_attach_group(mapping->domain, group);
+	if (s1_bypass) {
+		mapping->ops = &swiotlb_dma_ops;
+		err = 0;
+	} else if (is_fast) {
+		err = fast_smmu_init_mapping(dev, mapping);
+	} else if (is_upstream) {
+		err = upstream_iommu_init_mapping(dev, mapping);
+	} else {
+		err = bitmap_iommu_init_mapping(dev, mapping);
+	}
+	if (!err) {
+		kref_init(&mapping->kref);
+		mapping->init = true;
+	}
+	return err;
+}
+
+/**
+ * arm_iommu_attach_device
+ * @dev: valid struct device pointer
+ * @mapping: io address space mapping structure (returned from
+ *	arm_iommu_create_mapping)
+ *
+ * Attaches specified io address space mapping to the provided device,
+ * this replaces the dma operations (dma_map_ops pointer) with the
+ * IOMMU aware version.
+ *
+ * Clients are expected to call arm_iommu_attach_device() prior to sharing
+ * the dma_iommu_mapping structure with another device. This ensures
+ * initialization is complete.
+ */
+int arm_iommu_attach_device(struct device *dev,
+			    struct dma_iommu_mapping *mapping)
+{
+	int err;
+	struct iommu_domain *domain = mapping->domain;
+	struct iommu_group *group = dev->iommu_group;
+
+	if (!group) {
+		dev_err(dev, "No iommu associated with device\n");
+		return -EINVAL;
+	}
+
+	if (iommu_get_domain_for_dev(dev)) {
+		dev_err(dev, "Device already attached to other iommu_domain\n");
+		return -EINVAL;
+	}
+
+	err = iommu_attach_group(domain, group);
 	if (err)
 		return err;
 
-	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
-					&s1_bypass);
+	err = arm_iommu_init_mapping(dev, mapping);
+	if (err) {
+		iommu_detach_group(domain, group);
+		return err;
+	}
 
-	kref_get(&mapping->kref);
 	dev->archdata.mapping = mapping;
-	if (!s1_bypass)
-		set_dma_ops(dev, &iommu_ops);
+	set_dma_ops(dev, mapping->ops);
 
 	pr_debug("Attached IOMMU controller to %s device.\n", dev_name(dev));
 	return 0;
@@ -2036,8 +2209,7 @@
 void arm_iommu_detach_device(struct device *dev)
 {
 	struct dma_iommu_mapping *mapping;
-	int is_fast, s1_bypass = 0;
-	struct iommu_group *group;
+	int s1_bypass = 0;
 
 	mapping = to_dma_iommu_mapping(dev);
 	if (!mapping) {
@@ -2045,26 +2217,22 @@
 		return;
 	}
 
-	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_FAST, &is_fast);
-	if (is_fast) {
-		fast_smmu_detach_device(dev, mapping);
+	if (!dev->iommu_group) {
+		dev_err(dev, "No iommu associated with device\n");
 		return;
 	}
 
 	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS,
 					&s1_bypass);
 
+	/*
+	 * ION defers dma_unmap calls. Ensure they have all completed prior to
+	 * setting dma_ops to NULL.
+	 */
 	if (msm_dma_unmap_all_for_dev(dev))
 		dev_warn(dev, "IOMMU detach with outstanding mappings\n");
 
-	group = dev->iommu_group;
-	if (!group) {
-		dev_err(dev, "No iommu associated with device\n");
-		return;
-	}
-
-	iommu_detach_group(mapping->domain, group);
-	kref_put(&mapping->kref, release_iommu_mapping);
+	iommu_detach_group(mapping->domain, dev->iommu_group);
 	dev->archdata.mapping = NULL;
 	if (!s1_bypass)
 		set_dma_ops(dev, NULL);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 7f9501a..b5d88f8 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -384,8 +384,11 @@
 	 * signal first. We do not need to release the mmap_sem because it
 	 * would already be released in __lock_page_or_retry in mm/filemap.c.
 	 */
-	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) {
+		if (!user_mode(regs))
+			goto no_context;
 		return 0;
+	}
 
 	/*
 	 * Major/minor page fault accounting is only done on the initial
@@ -534,7 +537,7 @@
 	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"level 0 translation fault"	},
 	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"level 1 translation fault"	},
 	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"level 2 translation fault"	},
-	{ do_page_fault,	SIGSEGV, SEGV_MAPERR,	"level 3 translation fault"	},
+	{ do_translation_fault,	SIGSEGV, SEGV_MAPERR,	"level 3 translation fault"	},
 	{ do_bad,		SIGBUS,  0,		"unknown 8"			},
 	{ do_page_fault,	SIGSEGV, SEGV_ACCERR,	"level 1 access flag fault"	},
 	{ do_page_fault,	SIGSEGV, SEGV_ACCERR,	"level 2 access flag fault"	},
diff --git a/arch/mips/ar7/platform.c b/arch/mips/ar7/platform.c
index 58fca9a..3446b6f 100644
--- a/arch/mips/ar7/platform.c
+++ b/arch/mips/ar7/platform.c
@@ -576,6 +576,7 @@
 	uart_port.type		= PORT_AR7;
 	uart_port.uartclk	= clk_get_rate(bus_clk) / 2;
 	uart_port.iotype	= UPIO_MEM32;
+	uart_port.flags		= UPF_FIXED_TYPE;
 	uart_port.regshift	= 2;
 
 	uart_port.line		= 0;
@@ -654,6 +655,10 @@
 	u32 val;
 	int res;
 
+	res = ar7_gpio_init();
+	if (res)
+		pr_warn("unable to register gpios: %d\n", res);
+
 	res = ar7_register_uarts();
 	if (res)
 		pr_err("unable to setup uart(s): %d\n", res);
diff --git a/arch/mips/ar7/prom.c b/arch/mips/ar7/prom.c
index a23adc4..36aabee 100644
--- a/arch/mips/ar7/prom.c
+++ b/arch/mips/ar7/prom.c
@@ -246,8 +246,6 @@
 	ar7_init_cmdline(fw_arg0, (char **)fw_arg1);
 	ar7_init_env((struct env_var *)fw_arg2);
 	console_config();
-
-	ar7_gpio_init();
 }
 
 #define PORT(offset) (KSEG1ADDR(AR7_REGS_UART0 + (offset * 4)))
diff --git a/arch/mips/ath79/clock.c b/arch/mips/ath79/clock.c
index cc3a1e3..7e2bb12 100644
--- a/arch/mips/ath79/clock.c
+++ b/arch/mips/ath79/clock.c
@@ -508,16 +508,19 @@
 		ar9330_clk_init(ref_clk, pll_base);
 	else {
 		pr_err("%s: could not find any appropriate clk_init()\n", dnfn);
-		goto err_clk;
+		goto err_iounmap;
 	}
 
 	if (of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data)) {
 		pr_err("%s: could not register clk provider\n", dnfn);
-		goto err_clk;
+		goto err_iounmap;
 	}
 
 	return;
 
+err_iounmap:
+	iounmap(pll_base);
+
 err_clk:
 	clk_put(ref_clk);
 
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
index 1910223..cea2bb1 100644
--- a/arch/mips/dec/int-handler.S
+++ b/arch/mips/dec/int-handler.S
@@ -147,23 +147,12 @@
 		 * Find irq with highest priority
 		 */
 		# open coded PTR_LA t1, cpu_mask_nr_tbl
-#if (_MIPS_SZPTR == 32)
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
 		# open coded la t1, cpu_mask_nr_tbl
 		lui	t1, %hi(cpu_mask_nr_tbl)
 		addiu	t1, %lo(cpu_mask_nr_tbl)
-
-#endif
-#if (_MIPS_SZPTR == 64)
-		# open coded dla t1, cpu_mask_nr_tbl
-		.set	push
-		.set	noat
-		lui	t1, %highest(cpu_mask_nr_tbl)
-		lui	AT, %hi(cpu_mask_nr_tbl)
-		daddiu	t1, t1, %higher(cpu_mask_nr_tbl)
-		daddiu	AT, AT, %lo(cpu_mask_nr_tbl)
-		dsll	t1, 32
-		daddu	t1, t1, AT
-		.set	pop
+#else
+#error GCC `-msym32' option required for 64-bit DECstation builds
 #endif
 1:		lw	t2,(t1)
 		nop
@@ -214,23 +203,12 @@
 		 * Find irq with highest priority
 		 */
 		# open coded PTR_LA t1,asic_mask_nr_tbl
-#if (_MIPS_SZPTR == 32)
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
 		# open coded la t1, asic_mask_nr_tbl
 		lui	t1, %hi(asic_mask_nr_tbl)
 		addiu	t1, %lo(asic_mask_nr_tbl)
-
-#endif
-#if (_MIPS_SZPTR == 64)
-		# open coded dla t1, asic_mask_nr_tbl
-		.set	push
-		.set	noat
-		lui	t1, %highest(asic_mask_nr_tbl)
-		lui	AT, %hi(asic_mask_nr_tbl)
-		daddiu	t1, t1, %higher(asic_mask_nr_tbl)
-		daddiu	AT, AT, %lo(asic_mask_nr_tbl)
-		dsll	t1, 32
-		daddu	t1, t1, AT
-		.set	pop
+#else
+#error GCC `-msym32' option required for 64-bit DECstation builds
 #endif
 2:		lw	t2,(t1)
 		nop
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 956db6e..c5d3517 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -18,9 +18,24 @@
 #include <irq.h>
 
 #define IRQ_STACK_SIZE			THREAD_SIZE
+#define IRQ_STACK_START			(IRQ_STACK_SIZE - 16)
 
 extern void *irq_stack[NR_CPUS];
 
+/*
+ * The highest address on the IRQ stack contains a dummy frame put down in
+ * genex.S (handle_int & except_vec_vi_handler) which is structured as follows:
+ *
+ *   top ------------
+ *       | task sp  | <- irq_stack[cpu] + IRQ_STACK_START
+ *       ------------
+ *       |          | <- First frame of IRQ context
+ *       ------------
+ *
+ * task sp holds a copy of the task stack pointer where the struct pt_regs
+ * from exception entry can be found.
+ */
+
 static inline bool on_irq_stack(int cpu, unsigned long sp)
 {
 	unsigned long low = (unsigned long)irq_stack[cpu];
diff --git a/arch/mips/include/asm/mips-cm.h b/arch/mips/include/asm/mips-cm.h
index 2e41807..b6845db 100644
--- a/arch/mips/include/asm/mips-cm.h
+++ b/arch/mips/include/asm/mips-cm.h
@@ -239,8 +239,8 @@
 #define CM_GCR_BASE_GCRBASE_MSK			(_ULCAST_(0x1ffff) << 15)
 #define CM_GCR_BASE_CMDEFTGT_SHF		0
 #define CM_GCR_BASE_CMDEFTGT_MSK		(_ULCAST_(0x3) << 0)
-#define  CM_GCR_BASE_CMDEFTGT_DISABLED		0
-#define  CM_GCR_BASE_CMDEFTGT_MEM		1
+#define  CM_GCR_BASE_CMDEFTGT_MEM		0
+#define  CM_GCR_BASE_CMDEFTGT_RESERVED		1
 #define  CM_GCR_BASE_CMDEFTGT_IOCU0		2
 #define  CM_GCR_BASE_CMDEFTGT_IOCU1		3
 
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 4be2763..bfff6ea 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -103,6 +103,7 @@
 	DEFINE(_THREAD_SIZE, THREAD_SIZE);
 	DEFINE(_THREAD_MASK, THREAD_MASK);
 	DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE);
+	DEFINE(_IRQ_STACK_START, IRQ_STACK_START);
 	BLANK();
 }
 
diff --git a/arch/mips/kernel/cps-vec.S b/arch/mips/kernel/cps-vec.S
index 59476a6..a00e87b 100644
--- a/arch/mips/kernel/cps-vec.S
+++ b/arch/mips/kernel/cps-vec.S
@@ -361,7 +361,7 @@
 	END(mips_cps_get_bootcfg)
 
 LEAF(mips_cps_boot_vpes)
-	PTR_L	ta2, COREBOOTCFG_VPEMASK(a0)
+	lw	ta2, COREBOOTCFG_VPEMASK(a0)
 	PTR_L	ta3, COREBOOTCFG_VPECONFIG(a0)
 
 #if defined(CONFIG_CPU_MIPSR6)
diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S
index 2ac6c26..ae810da 100644
--- a/arch/mips/kernel/genex.S
+++ b/arch/mips/kernel/genex.S
@@ -215,9 +215,11 @@
 	beq	t0, t1, 2f
 
 	/* Switch to IRQ stack */
-	li	t1, _IRQ_STACK_SIZE
+	li	t1, _IRQ_STACK_START
 	PTR_ADD sp, t0, t1
 
+	/* Save task's sp on IRQ stack so that unwinding can follow it */
+	LONG_S	s1, 0(sp)
 2:
 	jal	plat_irq_dispatch
 
@@ -325,9 +327,11 @@
 	beq	t0, t1, 2f
 
 	/* Switch to IRQ stack */
-	li	t1, _IRQ_STACK_SIZE
+	li	t1, _IRQ_STACK_START
 	PTR_ADD sp, t0, t1
 
+	/* Save task's sp on IRQ stack so that unwinding can follow it */
+	LONG_S	s1, 0(sp)
 2:
 	jalr	v0
 
diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c
index fbbf5fc..c558bce 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -50,9 +50,7 @@
 #ifdef CONFIG_HOTPLUG_CPU
 void arch_cpu_idle_dead(void)
 {
-	/* What the heck is this check doing ? */
-	if (!cpumask_test_cpu(smp_processor_id(), &cpu_callin_map))
-		play_dead();
+	play_dead();
 }
 #endif
 
@@ -487,31 +485,52 @@
 					      unsigned long pc,
 					      unsigned long *ra)
 {
+	unsigned long low, high, irq_stack_high;
 	struct mips_frame_info info;
 	unsigned long size, ofs;
+	struct pt_regs *regs;
 	int leaf;
-	extern void ret_from_irq(void);
-	extern void ret_from_exception(void);
 
 	if (!stack_page)
 		return 0;
 
 	/*
-	 * If we reached the bottom of interrupt context,
-	 * return saved pc in pt_regs.
+	 * IRQ stacks start at IRQ_STACK_START
+	 * task stacks at THREAD_SIZE - 32
 	 */
-	if (pc == (unsigned long)ret_from_irq ||
-	    pc == (unsigned long)ret_from_exception) {
-		struct pt_regs *regs;
-		if (*sp >= stack_page &&
-		    *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) {
-			regs = (struct pt_regs *)*sp;
-			pc = regs->cp0_epc;
-			if (!user_mode(regs) && __kernel_text_address(pc)) {
-				*sp = regs->regs[29];
-				*ra = regs->regs[31];
-				return pc;
-			}
+	low = stack_page;
+	if (!preemptible() && on_irq_stack(raw_smp_processor_id(), *sp)) {
+		high = stack_page + IRQ_STACK_START;
+		irq_stack_high = high;
+	} else {
+		high = stack_page + THREAD_SIZE - 32;
+		irq_stack_high = 0;
+	}
+
+	/*
+	 * If we reached the top of the interrupt stack, start unwinding
+	 * the interrupted task stack.
+	 */
+	if (unlikely(*sp == irq_stack_high)) {
+		unsigned long task_sp = *(unsigned long *)*sp;
+
+		/*
+		 * Check that the pointer saved in the IRQ stack head points to
+		 * something within the stack of the current task
+		 */
+		if (!object_is_on_stack((void *)task_sp))
+			return 0;
+
+		/*
+		 * Follow pointer to tasks kernel stack frame where interrupted
+		 * state was saved.
+		 */
+		regs = (struct pt_regs *)task_sp;
+		pc = regs->cp0_epc;
+		if (!user_mode(regs) && __kernel_text_address(pc)) {
+			*sp = regs->regs[29];
+			*ra = regs->regs[31];
+			return pc;
 		}
 		return 0;
 	}
@@ -532,8 +551,7 @@
 	if (leaf < 0)
 		return 0;
 
-	if (*sp < stack_page ||
-	    *sp + info.frame_size > stack_page + THREAD_SIZE - 32)
+	if (*sp < low || *sp + info.frame_size > high)
 		return 0;
 
 	if (leaf)
diff --git a/arch/mips/kernel/smp-bmips.c b/arch/mips/kernel/smp-bmips.c
index 6d0f132..47c9646 100644
--- a/arch/mips/kernel/smp-bmips.c
+++ b/arch/mips/kernel/smp-bmips.c
@@ -587,11 +587,11 @@
 
 		/* Flush and enable RAC */
 		cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG);
-		__raw_writel(cfg | 0x100, BMIPS_RAC_CONFIG);
+		__raw_writel(cfg | 0x100, cbr + BMIPS_RAC_CONFIG);
 		__raw_readl(cbr + BMIPS_RAC_CONFIG);
 
 		cfg = __raw_readl(cbr + BMIPS_RAC_CONFIG);
-		__raw_writel(cfg | 0xf, BMIPS_RAC_CONFIG);
+		__raw_writel(cfg | 0xf, cbr + BMIPS_RAC_CONFIG);
 		__raw_readl(cbr + BMIPS_RAC_CONFIG);
 
 		cfg = __raw_readl(cbr + BMIPS_RAC_ADDRESS_RANGE);
diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c
index 7ebb191..95ba427 100644
--- a/arch/mips/kernel/smp.c
+++ b/arch/mips/kernel/smp.c
@@ -68,6 +68,9 @@
 cpumask_t cpu_core_map[NR_CPUS] __read_mostly;
 EXPORT_SYMBOL(cpu_core_map);
 
+static DECLARE_COMPLETION(cpu_starting);
+static DECLARE_COMPLETION(cpu_running);
+
 /*
  * A logcal cpu mask containing only one VPE per core to
  * reduce the number of IPIs on large MT systems.
@@ -369,9 +372,12 @@
 	cpumask_set_cpu(cpu, &cpu_coherent_mask);
 	notify_cpu_starting(cpu);
 
-	cpumask_set_cpu(cpu, &cpu_callin_map);
+	/* Notify boot CPU that we're starting & ready to sync counters */
+	complete(&cpu_starting);
+
 	synchronise_count_slave(cpu);
 
+	/* The CPU is running and counters synchronised, now mark it online */
 	set_cpu_online(cpu, true);
 
 	set_cpu_sibling_map(cpu);
@@ -380,6 +386,12 @@
 	calculate_cpu_foreign_map();
 
 	/*
+	 * Notify boot CPU that we're up & online and it can safely return
+	 * from __cpu_up
+	 */
+	complete(&cpu_running);
+
+	/*
 	 * irq will be enabled in ->smp_finish(), enabling it too early
 	 * is dangerous.
 	 */
@@ -430,22 +442,23 @@
 {
 	set_cpu_possible(0, true);
 	set_cpu_online(0, true);
-	cpumask_set_cpu(0, &cpu_callin_map);
 }
 
 int __cpu_up(unsigned int cpu, struct task_struct *tidle)
 {
 	mp_ops->boot_secondary(cpu, tidle);
 
-	/*
-	 * Trust is futile.  We should really have timeouts ...
-	 */
-	while (!cpumask_test_cpu(cpu, &cpu_callin_map)) {
-		udelay(100);
-		schedule();
+	/* Wait for CPU to start and be ready to sync counters */
+	if (!wait_for_completion_timeout(&cpu_starting,
+					 msecs_to_jiffies(1000))) {
+		pr_crit("CPU%u: failed to start\n", cpu);
+		return -EIO;
 	}
 
 	synchronise_count_master(cpu);
+
+	/* Wait for CPU to finish startup & mark itself online before return */
+	wait_for_completion(&cpu_running);
 	return 0;
 }
 
diff --git a/arch/mips/kernel/vmlinux.lds.S b/arch/mips/kernel/vmlinux.lds.S
index d5de675..f0a0e6d 100644
--- a/arch/mips/kernel/vmlinux.lds.S
+++ b/arch/mips/kernel/vmlinux.lds.S
@@ -182,7 +182,7 @@
 	 * Force .bss to 64K alignment so that .bss..swapper_pg_dir
 	 * gets that alignment.	 .sbss should be empty, so there will be
 	 * no holes after __init_end. */
-	BSS_SECTION(0, 0x10000, 0)
+	BSS_SECTION(0, 0x10000, 8)
 
 	_end = . ;
 
diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c
index 9056547..95bec46 100644
--- a/arch/mips/lantiq/xway/sysctrl.c
+++ b/arch/mips/lantiq/xway/sysctrl.c
@@ -469,8 +469,8 @@
 			panic("Failed to load xbar nodes from devicetree");
 		if (of_address_to_resource(np_xbar, 0, &res_xbar))
 			panic("Failed to get xbar resources");
-		if (request_mem_region(res_xbar.start, resource_size(&res_xbar),
-			res_xbar.name) < 0)
+		if (!request_mem_region(res_xbar.start, resource_size(&res_xbar),
+			res_xbar.name))
 			panic("Failed to get xbar resources");
 
 		ltq_xbar_membase = ioremap_nocache(res_xbar.start,
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index e9385bc..9ade60c 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -2386,7 +2386,6 @@
 					break;
 				default:
 					/* Reserved R6 ops */
-					pr_err("Reserved MIPS R6 CMP.condn.S operation\n");
 					return SIGILL;
 				}
 			}
@@ -2460,7 +2459,6 @@
 					break;
 				default:
 					/* Reserved R6 ops */
-					pr_err("Reserved MIPS R6 CMP.condn.D operation\n");
 					return SIGILL;
 				}
 			}
diff --git a/arch/mips/math-emu/dp_fmax.c b/arch/mips/math-emu/dp_fmax.c
index fd71b8d..5bec64f 100644
--- a/arch/mips/math-emu/dp_fmax.c
+++ b/arch/mips/math-emu/dp_fmax.c
@@ -47,14 +47,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754dp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
 		return ys ? x : y;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754dp_zero(1);
+		return ieee754dp_zero(xs & ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		DPDNORMX;
@@ -106,16 +116,32 @@
 	else if (xs < ys)
 		return x;
 
-	/* Compare exponent */
-	if (xe > ye)
-		return x;
-	else if (xe < ye)
-		return y;
+	/* Signs of inputs are equal, let's compare exponents */
+	if (xs == 0) {
+		/* Inputs are both positive */
+		if (xe > ye)
+			return x;
+		else if (xe < ye)
+			return y;
+	} else {
+		/* Inputs are both negative */
+		if (xe > ye)
+			return y;
+		else if (xe < ye)
+			return x;
+	}
 
-	/* Compare mantissa */
+	/* Signs and exponents of inputs are equal, let's compare mantissas */
+	if (xs == 0) {
+		/* Inputs are both positive, with equal signs and exponents */
+		if (xm <= ym)
+			return y;
+		return x;
+	}
+	/* Inputs are both negative, with equal signs and exponents */
 	if (xm <= ym)
-		return y;
-	return x;
+		return x;
+	return y;
 }
 
 union ieee754dp ieee754dp_fmaxa(union ieee754dp x, union ieee754dp y)
@@ -147,14 +173,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754dp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,6 +202,9 @@
 	/*
 	 * Infinity and zero handling
 	 */
+	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+		return ieee754dp_inf(xs & ys);
+
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
@@ -171,7 +212,6 @@
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
@@ -180,9 +220,7 @@
 		return y;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754dp_zero(1);
+		return ieee754dp_zero(xs & ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		DPDNORMX;
@@ -207,7 +245,11 @@
 		return y;
 
 	/* Compare mantissa */
-	if (xm <= ym)
+	if (xm < ym)
 		return y;
-	return x;
+	else if (xm > ym)
+		return x;
+	else if (xs == 0)
+		return x;
+	return y;
 }
diff --git a/arch/mips/math-emu/dp_fmin.c b/arch/mips/math-emu/dp_fmin.c
index c1072b0..a287b23 100644
--- a/arch/mips/math-emu/dp_fmin.c
+++ b/arch/mips/math-emu/dp_fmin.c
@@ -47,14 +47,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754dp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
 		return ys ? y : x;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754dp_zero(1);
+		return ieee754dp_zero(xs | ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		DPDNORMX;
@@ -106,16 +116,32 @@
 	else if (xs < ys)
 		return y;
 
-	/* Compare exponent */
-	if (xe > ye)
-		return y;
-	else if (xe < ye)
-		return x;
+	/* Signs of inputs are the same, let's compare exponents */
+	if (xs == 0) {
+		/* Inputs are both positive */
+		if (xe > ye)
+			return y;
+		else if (xe < ye)
+			return x;
+	} else {
+		/* Inputs are both negative */
+		if (xe > ye)
+			return x;
+		else if (xe < ye)
+			return y;
+	}
 
-	/* Compare mantissa */
+	/* Signs and exponents of inputs are equal, let's compare mantissas */
+	if (xs == 0) {
+		/* Inputs are both positive, with equal signs and exponents */
+		if (xm <= ym)
+			return x;
+		return y;
+	}
+	/* Inputs are both negative, with equal signs and exponents */
 	if (xm <= ym)
-		return x;
-	return y;
+		return y;
+	return x;
 }
 
 union ieee754dp ieee754dp_fmina(union ieee754dp x, union ieee754dp y)
@@ -147,14 +173,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754dp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,25 +202,25 @@
 	/*
 	 * Infinity and zero handling
 	 */
+	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+		return ieee754dp_inf(xs | ys);
+
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
-		return x;
+		return y;
 
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_DNORM):
-		return y;
+		return x;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754dp_zero(1);
+		return ieee754dp_zero(xs | ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		DPDNORMX;
@@ -207,7 +245,11 @@
 		return x;
 
 	/* Compare mantissa */
-	if (xm <= ym)
+	if (xm < ym)
+		return x;
+	else if (xm > ym)
+		return y;
+	else if (xs == 1)
 		return x;
 	return y;
 }
diff --git a/arch/mips/math-emu/dp_maddf.c b/arch/mips/math-emu/dp_maddf.c
index 4a2d03c..e0d9be5 100644
--- a/arch/mips/math-emu/dp_maddf.c
+++ b/arch/mips/math-emu/dp_maddf.c
@@ -14,22 +14,45 @@
 
 #include "ieee754dp.h"
 
-enum maddf_flags {
-	maddf_negate_product	= 1 << 0,
-};
+
+/* 128 bits shift right logical with rounding. */
+void srl128(u64 *hptr, u64 *lptr, int count)
+{
+	u64 low;
+
+	if (count >= 128) {
+		*lptr = *hptr != 0 || *lptr != 0;
+		*hptr = 0;
+	} else if (count >= 64) {
+		if (count == 64) {
+			*lptr = *hptr | (*lptr != 0);
+		} else {
+			low = *lptr;
+			*lptr = *hptr >> (count - 64);
+			*lptr |= (*hptr << (128 - count)) != 0 || low != 0;
+		}
+		*hptr = 0;
+	} else {
+		low = *lptr;
+		*lptr = low >> count | *hptr << (64 - count);
+		*lptr |= (low << (64 - count)) != 0;
+		*hptr = *hptr >> count;
+	}
+}
 
 static union ieee754dp _dp_maddf(union ieee754dp z, union ieee754dp x,
 				 union ieee754dp y, enum maddf_flags flags)
 {
 	int re;
 	int rs;
-	u64 rm;
 	unsigned lxm;
 	unsigned hxm;
 	unsigned lym;
 	unsigned hym;
 	u64 lrm;
 	u64 hrm;
+	u64 lzm;
+	u64 hzm;
 	u64 t;
 	u64 at;
 	int s;
@@ -48,52 +71,34 @@
 
 	ieee754_clearcx();
 
-	switch (zc) {
-	case IEEE754_CLASS_SNAN:
-		ieee754_setcx(IEEE754_INVALID_OPERATION);
+	/*
+	 * Handle the cases when at least one of x, y or z is a NaN.
+	 * Order of precedence is sNaN, qNaN and z, x, y.
+	 */
+	if (zc == IEEE754_CLASS_SNAN)
 		return ieee754dp_nanxcpt(z);
-	case IEEE754_CLASS_DNORM:
-		DPDNORMZ;
-	/* QNAN is handled separately below */
-	}
-
-	switch (CLPAIR(xc, yc)) {
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN):
-		return ieee754dp_nanxcpt(y);
-
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
+	if (xc == IEEE754_CLASS_SNAN)
 		return ieee754dp_nanxcpt(x);
-
-	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
+	if (yc == IEEE754_CLASS_SNAN)
+		return ieee754dp_nanxcpt(y);
+	if (zc == IEEE754_CLASS_QNAN)
+		return z;
+	if (xc == IEEE754_CLASS_QNAN)
+		return x;
+	if (yc == IEEE754_CLASS_QNAN)
 		return y;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_INF):
-		return x;
+	if (zc == IEEE754_CLASS_DNORM)
+		DPDNORMZ;
+	/* ZERO z cases are handled separately below */
 
+	switch (CLPAIR(xc, yc)) {
 
 	/*
 	 * Infinity handling
 	 */
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
 		ieee754_setcx(IEEE754_INVALID_OPERATION);
 		return ieee754dp_indef();
 
@@ -102,9 +107,27 @@
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		return ieee754dp_inf(xs ^ ys);
+		if ((zc == IEEE754_CLASS_INF) &&
+		    ((!(flags & MADDF_NEGATE_PRODUCT) && (zs != (xs ^ ys))) ||
+		     ((flags & MADDF_NEGATE_PRODUCT) && (zs == (xs ^ ys))))) {
+			/*
+			 * Cases of addition of infinities with opposite signs
+			 * or subtraction of infinities with same signs.
+			 */
+			ieee754_setcx(IEEE754_INVALID_OPERATION);
+			return ieee754dp_indef();
+		}
+		/*
+		 * z is here either not an infinity, or an infinity having the
+		 * same sign as product (x*y) (in case of MADDF.D instruction)
+		 * or product -(x*y) (in MSUBF.D case). The result must be an
+		 * infinity, and its sign is determined only by the value of
+		 * (flags & MADDF_NEGATE_PRODUCT) and the signs of x and y.
+		 */
+		if (flags & MADDF_NEGATE_PRODUCT)
+			return ieee754dp_inf(1 ^ (xs ^ ys));
+		else
+			return ieee754dp_inf(xs ^ ys);
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
@@ -113,32 +136,42 @@
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
 		if (zc == IEEE754_CLASS_INF)
 			return ieee754dp_inf(zs);
-		/* Multiplication is 0 so just return z */
+		if (zc == IEEE754_CLASS_ZERO) {
+			/* Handle cases +0 + (-0) and similar ones. */
+			if ((!(flags & MADDF_NEGATE_PRODUCT)
+					&& (zs == (xs ^ ys))) ||
+			    ((flags & MADDF_NEGATE_PRODUCT)
+					&& (zs != (xs ^ ys))))
+				/*
+				 * Cases of addition of zeros of equal signs
+				 * or subtraction of zeroes of opposite signs.
+				 * The sign of the resulting zero is in any
+				 * such case determined only by the sign of z.
+				 */
+				return z;
+
+			return ieee754dp_zero(ieee754_csr.rm == FPU_CSR_RD);
+		}
+		/* x*y is here 0, and z is not 0, so just return z */
 		return z;
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		DPDNORMX;
 
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_DNORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754dp_inf(zs);
 		DPDNORMY;
 		break;
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_NORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754dp_inf(zs);
 		DPDNORMX;
 		break;
 
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_NORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754dp_inf(zs);
 		/* fall through to real computations */
 	}
@@ -157,7 +190,7 @@
 
 	re = xe + ye;
 	rs = xs ^ ys;
-	if (flags & maddf_negate_product)
+	if (flags & MADDF_NEGATE_PRODUCT)
 		rs ^= 1;
 
 	/* shunt to top of word */
@@ -165,7 +198,7 @@
 	ym <<= 64 - (DP_FBITS + 1);
 
 	/*
-	 * Multiply 64 bits xm, ym to give high 64 bits rm with stickness.
+	 * Multiply 64 bits xm and ym to give 128 bits result in hrm:lrm.
 	 */
 
 	/* 32 * 32 => 64 */
@@ -195,78 +228,110 @@
 
 	hrm = hrm + (t >> 32);
 
-	rm = hrm | (lrm != 0);
-
-	/*
-	 * Sticky shift down to normal rounding precision.
-	 */
-	if ((s64) rm < 0) {
-		rm = (rm >> (64 - (DP_FBITS + 1 + 3))) |
-		     ((rm << (DP_FBITS + 1 + 3)) != 0);
+	/* Put explicit bit at bit 126 if necessary */
+	if ((int64_t)hrm < 0) {
+		lrm = (hrm << 63) | (lrm >> 1);
+		hrm = hrm >> 1;
 		re++;
-	} else {
-		rm = (rm >> (64 - (DP_FBITS + 1 + 3 + 1))) |
-		     ((rm << (DP_FBITS + 1 + 3 + 1)) != 0);
 	}
-	assert(rm & (DP_HIDDEN_BIT << 3));
 
-	/* And now the addition */
-	assert(zm & DP_HIDDEN_BIT);
+	assert(hrm & (1 << 62));
 
-	/*
-	 * Provide guard,round and stick bit space.
-	 */
-	zm <<= 3;
+	if (zc == IEEE754_CLASS_ZERO) {
+		/*
+		 * Move explicit bit from bit 126 to bit 55 since the
+		 * ieee754dp_format code expects the mantissa to be
+		 * 56 bits wide (53 + 3 rounding bits).
+		 */
+		srl128(&hrm, &lrm, (126 - 55));
+		return ieee754dp_format(rs, re, lrm);
+	}
 
+	/* Move explicit bit from bit 52 to bit 126 */
+	lzm = 0;
+	hzm = zm << 10;
+	assert(hzm & (1 << 62));
+
+	/* Make the exponents the same */
 	if (ze > re) {
 		/*
 		 * Have to shift y fraction right to align.
 		 */
 		s = ze - re;
-		rm = XDPSRS(rm, s);
+		srl128(&hrm, &lrm, s);
 		re += s;
 	} else if (re > ze) {
 		/*
 		 * Have to shift x fraction right to align.
 		 */
 		s = re - ze;
-		zm = XDPSRS(zm, s);
+		srl128(&hzm, &lzm, s);
 		ze += s;
 	}
 	assert(ze == re);
 	assert(ze <= DP_EMAX);
 
+	/* Do the addition */
 	if (zs == rs) {
 		/*
-		 * Generate 28 bit result of adding two 27 bit numbers
-		 * leaving result in xm, xs and xe.
+		 * Generate 128 bit result by adding two 127 bit numbers
+		 * leaving result in hzm:lzm, zs and ze.
 		 */
-		zm = zm + rm;
-
-		if (zm >> (DP_FBITS + 1 + 3)) { /* carry out */
-			zm = XDPSRS1(zm);
+		hzm = hzm + hrm + (lzm > (lzm + lrm));
+		lzm = lzm + lrm;
+		if ((int64_t)hzm < 0) {        /* carry out */
+			srl128(&hzm, &lzm, 1);
 			ze++;
 		}
 	} else {
-		if (zm >= rm) {
-			zm = zm - rm;
+		if (hzm > hrm || (hzm == hrm && lzm >= lrm)) {
+			hzm = hzm - hrm - (lzm < lrm);
+			lzm = lzm - lrm;
 		} else {
-			zm = rm - zm;
+			hzm = hrm - hzm - (lrm < lzm);
+			lzm = lrm - lzm;
 			zs = rs;
 		}
-		if (zm == 0)
+		if (lzm == 0 && hzm == 0)
 			return ieee754dp_zero(ieee754_csr.rm == FPU_CSR_RD);
 
 		/*
-		 * Normalize to rounding precision.
+		 * Put explicit bit at bit 126 if necessary.
 		 */
-		while ((zm >> (DP_FBITS + 3)) == 0) {
-			zm <<= 1;
-			ze--;
+		if (hzm == 0) {
+			/* left shift by 63 or 64 bits */
+			if ((int64_t)lzm < 0) {
+				/* MSB of lzm is the explicit bit */
+				hzm = lzm >> 1;
+				lzm = lzm << 63;
+				ze -= 63;
+			} else {
+				hzm = lzm;
+				lzm = 0;
+				ze -= 64;
+			}
+		}
+
+		t = 0;
+		while ((hzm >> (62 - t)) == 0)
+			t++;
+
+		assert(t <= 62);
+		if (t) {
+			hzm = hzm << t | lzm >> (64 - t);
+			lzm = lzm << t;
+			ze -= t;
 		}
 	}
 
-	return ieee754dp_format(zs, ze, zm);
+	/*
+	 * Move explicit bit from bit 126 to bit 55 since the
+	 * ieee754dp_format code expects the mantissa to be
+	 * 56 bits wide (53 + 3 rounding bits).
+	 */
+	srl128(&hzm, &lzm, (126 - 55));
+
+	return ieee754dp_format(zs, ze, lzm);
 }
 
 union ieee754dp ieee754dp_maddf(union ieee754dp z, union ieee754dp x,
@@ -278,5 +343,5 @@
 union ieee754dp ieee754dp_msubf(union ieee754dp z, union ieee754dp x,
 				union ieee754dp y)
 {
-	return _dp_maddf(z, x, y, maddf_negate_product);
+	return _dp_maddf(z, x, y, MADDF_NEGATE_PRODUCT);
 }
diff --git a/arch/mips/math-emu/ieee754int.h b/arch/mips/math-emu/ieee754int.h
index 8bc2f69..dd2071f 100644
--- a/arch/mips/math-emu/ieee754int.h
+++ b/arch/mips/math-emu/ieee754int.h
@@ -26,6 +26,10 @@
 
 #define CLPAIR(x, y)	((x)*6+(y))
 
+enum maddf_flags {
+	MADDF_NEGATE_PRODUCT	= 1 << 0,
+};
+
 static inline void ieee754_clearcx(void)
 {
 	ieee754_csr.cx = 0;
diff --git a/arch/mips/math-emu/ieee754sp.h b/arch/mips/math-emu/ieee754sp.h
index 8476067..0f63e42 100644
--- a/arch/mips/math-emu/ieee754sp.h
+++ b/arch/mips/math-emu/ieee754sp.h
@@ -45,6 +45,10 @@
 	return SPBEXP(x) != SP_EMAX + 1 + SP_EBIAS;
 }
 
+/* 64 bit right shift with rounding */
+#define XSPSRS64(v, rs)						\
+	(((rs) >= 64) ? ((v) != 0) : ((v) >> (rs)) | ((v) << (64-(rs)) != 0))
+
 /* 3bit extended single precision sticky right shift */
 #define XSPSRS(v, rs)						\
 	((rs > (SP_FBITS+3))?1:((v) >> (rs)) | ((v) << (32-(rs)) != 0))
diff --git a/arch/mips/math-emu/sp_fmax.c b/arch/mips/math-emu/sp_fmax.c
index 4d00084..74a5a00 100644
--- a/arch/mips/math-emu/sp_fmax.c
+++ b/arch/mips/math-emu/sp_fmax.c
@@ -47,14 +47,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754sp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
 		return ys ? x : y;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754sp_zero(1);
+		return ieee754sp_zero(xs & ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		SPDNORMX;
@@ -106,16 +116,32 @@
 	else if (xs < ys)
 		return x;
 
-	/* Compare exponent */
-	if (xe > ye)
-		return x;
-	else if (xe < ye)
-		return y;
+	/* Signs of inputs are equal, let's compare exponents */
+	if (xs == 0) {
+		/* Inputs are both positive */
+		if (xe > ye)
+			return x;
+		else if (xe < ye)
+			return y;
+	} else {
+		/* Inputs are both negative */
+		if (xe > ye)
+			return y;
+		else if (xe < ye)
+			return x;
+	}
 
-	/* Compare mantissa */
+	/* Signs and exponents of inputs are equal, let's compare mantissas */
+	if (xs == 0) {
+		/* Inputs are both positive, with equal signs and exponents */
+		if (xm <= ym)
+			return y;
+		return x;
+	}
+	/* Inputs are both negative, with equal signs and exponents */
 	if (xm <= ym)
-		return y;
-	return x;
+		return x;
+	return y;
 }
 
 union ieee754sp ieee754sp_fmaxa(union ieee754sp x, union ieee754sp y)
@@ -147,14 +173,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754sp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,6 +202,9 @@
 	/*
 	 * Infinity and zero handling
 	 */
+	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+		return ieee754sp_inf(xs & ys);
+
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
@@ -171,7 +212,6 @@
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
@@ -180,9 +220,7 @@
 		return y;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754sp_zero(1);
+		return ieee754sp_zero(xs & ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		SPDNORMX;
@@ -207,7 +245,11 @@
 		return y;
 
 	/* Compare mantissa */
-	if (xm <= ym)
+	if (xm < ym)
 		return y;
-	return x;
+	else if (xm > ym)
+		return x;
+	else if (xs == 0)
+		return x;
+	return y;
 }
diff --git a/arch/mips/math-emu/sp_fmin.c b/arch/mips/math-emu/sp_fmin.c
index 4eb1bb9..c51385f 100644
--- a/arch/mips/math-emu/sp_fmin.c
+++ b/arch/mips/math-emu/sp_fmin.c
@@ -47,14 +47,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754sp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -80,9 +92,7 @@
 		return ys ? y : x;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754sp_zero(1);
+		return ieee754sp_zero(xs | ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		SPDNORMX;
@@ -106,16 +116,32 @@
 	else if (xs < ys)
 		return y;
 
-	/* Compare exponent */
-	if (xe > ye)
-		return y;
-	else if (xe < ye)
-		return x;
+	/* Signs of inputs are the same, let's compare exponents */
+	if (xs == 0) {
+		/* Inputs are both positive */
+		if (xe > ye)
+			return y;
+		else if (xe < ye)
+			return x;
+	} else {
+		/* Inputs are both negative */
+		if (xe > ye)
+			return x;
+		else if (xe < ye)
+			return y;
+	}
 
-	/* Compare mantissa */
+	/* Signs and exponents of inputs are equal, let's compare mantissas */
+	if (xs == 0) {
+		/* Inputs are both positive, with equal signs and exponents */
+		if (xm <= ym)
+			return x;
+		return y;
+	}
+	/* Inputs are both negative, with equal signs and exponents */
 	if (xm <= ym)
-		return x;
-	return y;
+		return y;
+	return x;
 }
 
 union ieee754sp ieee754sp_fmina(union ieee754sp x, union ieee754sp y)
@@ -147,14 +173,26 @@
 	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
 		return ieee754sp_nanxcpt(x);
 
-	/* numbers are preferred to NaNs */
+	/*
+	 * Quiet NaN handling
+	 */
+
+	/*
+	 *    The case of both inputs quiet NaNs
+	 */
+	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
+		return x;
+
+	/*
+	 *    The cases of exactly one input quiet NaN (numbers
+	 *    are here preferred as returned values to NaNs)
+	 */
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
 		return x;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
@@ -164,25 +202,25 @@
 	/*
 	 * Infinity and zero handling
 	 */
+	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
+		return ieee754sp_inf(xs | ys);
+
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
-		return x;
+		return y;
 
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_DNORM):
-		return y;
+		return x;
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
-		if (xs == ys)
-			return x;
-		return ieee754sp_zero(1);
+		return ieee754sp_zero(xs | ys);
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		SPDNORMX;
@@ -207,7 +245,11 @@
 		return x;
 
 	/* Compare mantissa */
-	if (xm <= ym)
+	if (xm < ym)
+		return x;
+	else if (xm > ym)
+		return y;
+	else if (xs == 1)
 		return x;
 	return y;
 }
diff --git a/arch/mips/math-emu/sp_maddf.c b/arch/mips/math-emu/sp_maddf.c
index a8cd8b4..7195fe7 100644
--- a/arch/mips/math-emu/sp_maddf.c
+++ b/arch/mips/math-emu/sp_maddf.c
@@ -14,9 +14,6 @@
 
 #include "ieee754sp.h"
 
-enum maddf_flags {
-	maddf_negate_product	= 1 << 0,
-};
 
 static union ieee754sp _sp_maddf(union ieee754sp z, union ieee754sp x,
 				 union ieee754sp y, enum maddf_flags flags)
@@ -24,14 +21,8 @@
 	int re;
 	int rs;
 	unsigned rm;
-	unsigned short lxm;
-	unsigned short hxm;
-	unsigned short lym;
-	unsigned short hym;
-	unsigned lrm;
-	unsigned hrm;
-	unsigned t;
-	unsigned at;
+	uint64_t rm64;
+	uint64_t zm64;
 	int s;
 
 	COMPXSP;
@@ -48,51 +39,35 @@
 
 	ieee754_clearcx();
 
-	switch (zc) {
-	case IEEE754_CLASS_SNAN:
-		ieee754_setcx(IEEE754_INVALID_OPERATION);
+	/*
+	 * Handle the cases when at least one of x, y or z is a NaN.
+	 * Order of precedence is sNaN, qNaN and z, x, y.
+	 */
+	if (zc == IEEE754_CLASS_SNAN)
 		return ieee754sp_nanxcpt(z);
-	case IEEE754_CLASS_DNORM:
-		SPDNORMZ;
-	/* QNAN is handled separately below */
-	}
-
-	switch (CLPAIR(xc, yc)) {
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_SNAN):
-		return ieee754sp_nanxcpt(y);
-
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_SNAN):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_ZERO):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_NORM):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_DNORM):
-	case CLPAIR(IEEE754_CLASS_SNAN, IEEE754_CLASS_INF):
+	if (xc == IEEE754_CLASS_SNAN)
 		return ieee754sp_nanxcpt(x);
-
-	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_QNAN):
+	if (yc == IEEE754_CLASS_SNAN)
+		return ieee754sp_nanxcpt(y);
+	if (zc == IEEE754_CLASS_QNAN)
+		return z;
+	if (xc == IEEE754_CLASS_QNAN)
+		return x;
+	if (yc == IEEE754_CLASS_QNAN)
 		return y;
 
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_QNAN):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_ZERO):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_NORM):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_DNORM):
-	case CLPAIR(IEEE754_CLASS_QNAN, IEEE754_CLASS_INF):
-		return x;
+	if (zc == IEEE754_CLASS_DNORM)
+		SPDNORMZ;
+	/* ZERO z cases are handled separately below */
+
+	switch (CLPAIR(xc, yc)) {
+
 
 	/*
 	 * Infinity handling
 	 */
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_INF):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
 		ieee754_setcx(IEEE754_INVALID_OPERATION);
 		return ieee754sp_indef();
 
@@ -101,9 +76,27 @@
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_NORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_DNORM):
 	case CLPAIR(IEEE754_CLASS_INF, IEEE754_CLASS_INF):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		return ieee754sp_inf(xs ^ ys);
+		if ((zc == IEEE754_CLASS_INF) &&
+		    ((!(flags & MADDF_NEGATE_PRODUCT) && (zs != (xs ^ ys))) ||
+		     ((flags & MADDF_NEGATE_PRODUCT) && (zs == (xs ^ ys))))) {
+			/*
+			 * Cases of addition of infinities with opposite signs
+			 * or subtraction of infinities with same signs.
+			 */
+			ieee754_setcx(IEEE754_INVALID_OPERATION);
+			return ieee754sp_indef();
+		}
+		/*
+		 * z is here either not an infinity, or an infinity having the
+		 * same sign as product (x*y) (in case of MADDF.D instruction)
+		 * or product -(x*y) (in MSUBF.D case). The result must be an
+		 * infinity, and its sign is determined only by the value of
+		 * (flags & MADDF_NEGATE_PRODUCT) and the signs of x and y.
+		 */
+		if (flags & MADDF_NEGATE_PRODUCT)
+			return ieee754sp_inf(1 ^ (xs ^ ys));
+		else
+			return ieee754sp_inf(xs ^ ys);
 
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_ZERO):
 	case CLPAIR(IEEE754_CLASS_ZERO, IEEE754_CLASS_NORM):
@@ -112,32 +105,42 @@
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_ZERO):
 		if (zc == IEEE754_CLASS_INF)
 			return ieee754sp_inf(zs);
-		/* Multiplication is 0 so just return z */
+		if (zc == IEEE754_CLASS_ZERO) {
+			/* Handle cases +0 + (-0) and similar ones. */
+			if ((!(flags & MADDF_NEGATE_PRODUCT)
+					&& (zs == (xs ^ ys))) ||
+			    ((flags & MADDF_NEGATE_PRODUCT)
+					&& (zs != (xs ^ ys))))
+				/*
+				 * Cases of addition of zeros of equal signs
+				 * or subtraction of zeroes of opposite signs.
+				 * The sign of the resulting zero is in any
+				 * such case determined only by the sign of z.
+				 */
+				return z;
+
+			return ieee754sp_zero(ieee754_csr.rm == FPU_CSR_RD);
+		}
+		/* x*y is here 0, and z is not 0, so just return z */
 		return z;
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_DNORM):
 		SPDNORMX;
 
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_DNORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754sp_inf(zs);
 		SPDNORMY;
 		break;
 
 	case CLPAIR(IEEE754_CLASS_DNORM, IEEE754_CLASS_NORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754sp_inf(zs);
 		SPDNORMX;
 		break;
 
 	case CLPAIR(IEEE754_CLASS_NORM, IEEE754_CLASS_NORM):
-		if (zc == IEEE754_CLASS_QNAN)
-			return z;
-		else if (zc == IEEE754_CLASS_INF)
+		if (zc == IEEE754_CLASS_INF)
 			return ieee754sp_inf(zs);
 		/* fall through to real computations */
 	}
@@ -158,108 +161,93 @@
 
 	re = xe + ye;
 	rs = xs ^ ys;
-	if (flags & maddf_negate_product)
+	if (flags & MADDF_NEGATE_PRODUCT)
 		rs ^= 1;
 
-	/* shunt to top of word */
-	xm <<= 32 - (SP_FBITS + 1);
-	ym <<= 32 - (SP_FBITS + 1);
+	/* Multiple 24 bit xm and ym to give 48 bit results */
+	rm64 = (uint64_t)xm * ym;
 
-	/*
-	 * Multiply 32 bits xm, ym to give high 32 bits rm with stickness.
-	 */
-	lxm = xm & 0xffff;
-	hxm = xm >> 16;
-	lym = ym & 0xffff;
-	hym = ym >> 16;
+	/* Shunt to top of word */
+	rm64 = rm64 << 16;
 
-	lrm = lxm * lym;	/* 16 * 16 => 32 */
-	hrm = hxm * hym;	/* 16 * 16 => 32 */
-
-	t = lxm * hym; /* 16 * 16 => 32 */
-	at = lrm + (t << 16);
-	hrm += at < lrm;
-	lrm = at;
-	hrm = hrm + (t >> 16);
-
-	t = hxm * lym; /* 16 * 16 => 32 */
-	at = lrm + (t << 16);
-	hrm += at < lrm;
-	lrm = at;
-	hrm = hrm + (t >> 16);
-
-	rm = hrm | (lrm != 0);
-
-	/*
-	 * Sticky shift down to normal rounding precision.
-	 */
-	if ((int) rm < 0) {
-		rm = (rm >> (32 - (SP_FBITS + 1 + 3))) |
-		    ((rm << (SP_FBITS + 1 + 3)) != 0);
+	/* Put explicit bit at bit 62 if necessary */
+	if ((int64_t) rm64 < 0) {
+		rm64 = rm64 >> 1;
 		re++;
-	} else {
-		rm = (rm >> (32 - (SP_FBITS + 1 + 3 + 1))) |
-		     ((rm << (SP_FBITS + 1 + 3 + 1)) != 0);
 	}
-	assert(rm & (SP_HIDDEN_BIT << 3));
 
-	/* And now the addition */
+	assert(rm64 & (1 << 62));
 
-	assert(zm & SP_HIDDEN_BIT);
+	if (zc == IEEE754_CLASS_ZERO) {
+		/*
+		 * Move explicit bit from bit 62 to bit 26 since the
+		 * ieee754sp_format code expects the mantissa to be
+		 * 27 bits wide (24 + 3 rounding bits).
+		 */
+		rm = XSPSRS64(rm64, (62 - 26));
+		return ieee754sp_format(rs, re, rm);
+	}
 
-	/*
-	 * Provide guard,round and stick bit space.
-	 */
-	zm <<= 3;
+	/* Move explicit bit from bit 23 to bit 62 */
+	zm64 = (uint64_t)zm << (62 - 23);
+	assert(zm64 & (1 << 62));
 
+	/* Make the exponents the same */
 	if (ze > re) {
 		/*
 		 * Have to shift r fraction right to align.
 		 */
 		s = ze - re;
-		rm = XSPSRS(rm, s);
+		rm64 = XSPSRS64(rm64, s);
 		re += s;
 	} else if (re > ze) {
 		/*
 		 * Have to shift z fraction right to align.
 		 */
 		s = re - ze;
-		zm = XSPSRS(zm, s);
+		zm64 = XSPSRS64(zm64, s);
 		ze += s;
 	}
 	assert(ze == re);
 	assert(ze <= SP_EMAX);
 
+	/* Do the addition */
 	if (zs == rs) {
 		/*
-		 * Generate 28 bit result of adding two 27 bit numbers
-		 * leaving result in zm, zs and ze.
+		 * Generate 64 bit result by adding two 63 bit numbers
+		 * leaving result in zm64, zs and ze.
 		 */
-		zm = zm + rm;
-
-		if (zm >> (SP_FBITS + 1 + 3)) { /* carry out */
-			zm = XSPSRS1(zm);
+		zm64 = zm64 + rm64;
+		if ((int64_t)zm64 < 0) {	/* carry out */
+			zm64 = XSPSRS1(zm64);
 			ze++;
 		}
 	} else {
-		if (zm >= rm) {
-			zm = zm - rm;
+		if (zm64 >= rm64) {
+			zm64 = zm64 - rm64;
 		} else {
-			zm = rm - zm;
+			zm64 = rm64 - zm64;
 			zs = rs;
 		}
-		if (zm == 0)
+		if (zm64 == 0)
 			return ieee754sp_zero(ieee754_csr.rm == FPU_CSR_RD);
 
 		/*
-		 * Normalize in extended single precision
+		 * Put explicit bit at bit 62 if necessary.
 		 */
-		while ((zm >> (SP_MBITS + 3)) == 0) {
-			zm <<= 1;
+		while ((zm64 >> 62) == 0) {
+			zm64 <<= 1;
 			ze--;
 		}
-
 	}
+
+	/*
+	 * Move explicit bit from bit 62 to bit 26 since the
+	 * ieee754sp_format code expects the mantissa to be
+	 * 27 bits wide (24 + 3 rounding bits).
+	 */
+	zm = XSPSRS64(zm64, (62 - 26));
+
 	return ieee754sp_format(zs, ze, zm);
 }
 
@@ -272,5 +260,5 @@
 union ieee754sp ieee754sp_msubf(union ieee754sp z, union ieee754sp x,
 				union ieee754sp y)
 {
-	return _sp_maddf(z, x, y, maddf_negate_product);
+	return _sp_maddf(z, x, y, MADDF_NEGATE_PRODUCT);
 }
diff --git a/arch/mips/mm/uasm-micromips.c b/arch/mips/mm/uasm-micromips.c
index 277cf52..6c17cba 100644
--- a/arch/mips/mm/uasm-micromips.c
+++ b/arch/mips/mm/uasm-micromips.c
@@ -80,7 +80,7 @@
 	{ insn_jr, M(mm_pool32a_op, 0, 0, 0, mm_jalr_op, mm_pool32axf_op), RS },
 	{ insn_lb, M(mm_lb32_op, 0, 0, 0, 0, 0), RT | RS | SIMM },
 	{ insn_ld, 0, 0 },
-	{ insn_lh, M(mm_lh32_op, 0, 0, 0, 0, 0), RS | RS | SIMM },
+	{ insn_lh, M(mm_lh32_op, 0, 0, 0, 0, 0), RT | RS | SIMM },
 	{ insn_ll, M(mm_pool32c_op, 0, 0, (mm_ll_func << 1), 0, 0), RS | RT | SIMM },
 	{ insn_lld, 0, 0 },
 	{ insn_lui, M(mm_pool32i_op, mm_lui_op, 0, 0, 0, 0), RS | SIMM },
diff --git a/arch/mips/ralink/mt7620.c b/arch/mips/ralink/mt7620.c
index 3c7c9bf..6f892c1 100644
--- a/arch/mips/ralink/mt7620.c
+++ b/arch/mips/ralink/mt7620.c
@@ -176,7 +176,7 @@
 
 static struct rt2880_pmx_func spis_grp_mt7628[] = {
 	FUNC("pwm_uart2", 3, 14, 4),
-	FUNC("util", 2, 14, 4),
+	FUNC("utif", 2, 14, 4),
 	FUNC("gpio", 1, 14, 4),
 	FUNC("spis", 0, 14, 4),
 };
@@ -190,28 +190,28 @@
 
 static struct rt2880_pmx_func p4led_kn_grp_mt7628[] = {
 	FUNC("jtag", 3, 30, 1),
-	FUNC("util", 2, 30, 1),
+	FUNC("utif", 2, 30, 1),
 	FUNC("gpio", 1, 30, 1),
 	FUNC("p4led_kn", 0, 30, 1),
 };
 
 static struct rt2880_pmx_func p3led_kn_grp_mt7628[] = {
 	FUNC("jtag", 3, 31, 1),
-	FUNC("util", 2, 31, 1),
+	FUNC("utif", 2, 31, 1),
 	FUNC("gpio", 1, 31, 1),
 	FUNC("p3led_kn", 0, 31, 1),
 };
 
 static struct rt2880_pmx_func p2led_kn_grp_mt7628[] = {
 	FUNC("jtag", 3, 32, 1),
-	FUNC("util", 2, 32, 1),
+	FUNC("utif", 2, 32, 1),
 	FUNC("gpio", 1, 32, 1),
 	FUNC("p2led_kn", 0, 32, 1),
 };
 
 static struct rt2880_pmx_func p1led_kn_grp_mt7628[] = {
 	FUNC("jtag", 3, 33, 1),
-	FUNC("util", 2, 33, 1),
+	FUNC("utif", 2, 33, 1),
 	FUNC("gpio", 1, 33, 1),
 	FUNC("p1led_kn", 0, 33, 1),
 };
@@ -232,28 +232,28 @@
 
 static struct rt2880_pmx_func p4led_an_grp_mt7628[] = {
 	FUNC("jtag", 3, 39, 1),
-	FUNC("util", 2, 39, 1),
+	FUNC("utif", 2, 39, 1),
 	FUNC("gpio", 1, 39, 1),
 	FUNC("p4led_an", 0, 39, 1),
 };
 
 static struct rt2880_pmx_func p3led_an_grp_mt7628[] = {
 	FUNC("jtag", 3, 40, 1),
-	FUNC("util", 2, 40, 1),
+	FUNC("utif", 2, 40, 1),
 	FUNC("gpio", 1, 40, 1),
 	FUNC("p3led_an", 0, 40, 1),
 };
 
 static struct rt2880_pmx_func p2led_an_grp_mt7628[] = {
 	FUNC("jtag", 3, 41, 1),
-	FUNC("util", 2, 41, 1),
+	FUNC("utif", 2, 41, 1),
 	FUNC("gpio", 1, 41, 1),
 	FUNC("p2led_an", 0, 41, 1),
 };
 
 static struct rt2880_pmx_func p1led_an_grp_mt7628[] = {
 	FUNC("jtag", 3, 42, 1),
-	FUNC("util", 2, 42, 1),
+	FUNC("utif", 2, 42, 1),
 	FUNC("gpio", 1, 42, 1),
 	FUNC("p1led_an", 0, 42, 1),
 };
diff --git a/arch/mips/ralink/rt3883.c b/arch/mips/ralink/rt3883.c
index 9e4631a..3e68e35 100644
--- a/arch/mips/ralink/rt3883.c
+++ b/arch/mips/ralink/rt3883.c
@@ -145,5 +145,5 @@
 
 	rt2880_pinmux_data = rt3883_pinmux_data;
 
-	ralink_soc == RT3883_SOC;
+	ralink_soc = RT3883_SOC;
 }
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index c721ea2..df757c9 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -604,13 +604,12 @@
 	if (parisc_requires_coherency())
 		flush_tlb_range(vma, start, end);
 
-	if ((end - start) >= parisc_cache_flush_threshold) {
+	if ((end - start) >= parisc_cache_flush_threshold
+	    || vma->vm_mm->context != mfsp(3)) {
 		flush_cache_all();
 		return;
 	}
 
-	BUG_ON(vma->vm_mm->context != mfsp(3));
-
 	flush_user_dcache_range_asm(start, end);
 	if (vma->vm_flags & VM_EXEC)
 		flush_user_icache_range_asm(start, end);
diff --git a/arch/parisc/kernel/perf.c b/arch/parisc/kernel/perf.c
index 518f4f5..d63d425 100644
--- a/arch/parisc/kernel/perf.c
+++ b/arch/parisc/kernel/perf.c
@@ -39,7 +39,7 @@
  *  the PDC INTRIGUE calls.  This is done to eliminate bugs introduced
  *  in various PDC revisions.  The code is much more maintainable
  *  and reliable this way vs having to debug on every version of PDC
- *  on every box. 
+ *  on every box.
  */
 
 #include <linux/capability.h>
@@ -195,8 +195,8 @@
 static int perf_release(struct inode *inode, struct file *file);
 static int perf_open(struct inode *inode, struct file *file);
 static ssize_t perf_read(struct file *file, char __user *buf, size_t cnt, loff_t *ppos);
-static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, 
-	loff_t *ppos);
+static ssize_t perf_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *ppos);
 static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 static void perf_start_counters(void);
 static int perf_stop_counters(uint32_t *raddr);
@@ -222,7 +222,7 @@
 /*
  * configure:
  *
- * Configure the cpu with a given data image.  First turn off the counters, 
+ * Configure the cpu with a given data image.  First turn off the counters,
  * then download the image, then turn the counters back on.
  */
 static int perf_config(uint32_t *image_ptr)
@@ -234,7 +234,7 @@
 	error = perf_stop_counters(raddr);
 	if (error != 0) {
 		printk("perf_config: perf_stop_counters = %ld\n", error);
-		return -EINVAL; 
+		return -EINVAL;
 	}
 
 printk("Preparing to write image\n");
@@ -242,7 +242,7 @@
 	error = perf_write_image((uint64_t *)image_ptr);
 	if (error != 0) {
 		printk("perf_config: DOWNLOAD = %ld\n", error);
-		return -EINVAL; 
+		return -EINVAL;
 	}
 
 printk("Preparing to start counters\n");
@@ -254,7 +254,7 @@
 }
 
 /*
- * Open the device and initialize all of its memory.  The device is only 
+ * Open the device and initialize all of its memory.  The device is only
  * opened once, but can be "queried" by multiple processes that know its
  * file descriptor.
  */
@@ -298,8 +298,8 @@
  * called on the processor that the download should happen
  * on.
  */
-static ssize_t perf_write(struct file *file, const char __user *buf, size_t count, 
-	loff_t *ppos)
+static ssize_t perf_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *ppos)
 {
 	int err;
 	size_t image_size;
@@ -307,11 +307,11 @@
 	uint32_t interface_type;
 	uint32_t test;
 
-	if (perf_processor_interface == ONYX_INTF) 
+	if (perf_processor_interface == ONYX_INTF)
 		image_size = PCXU_IMAGE_SIZE;
-	else if (perf_processor_interface == CUDA_INTF) 
+	else if (perf_processor_interface == CUDA_INTF)
 		image_size = PCXW_IMAGE_SIZE;
-	else 
+	else
 		return -EFAULT;
 
 	if (!capable(CAP_SYS_ADMIN))
@@ -331,22 +331,22 @@
 
 	/* First check the machine type is correct for
 	   the requested image */
-        if (((perf_processor_interface == CUDA_INTF) &&
-		       (interface_type != CUDA_INTF)) ||
-	    ((perf_processor_interface == ONYX_INTF) &&
-	               (interface_type != ONYX_INTF))) 
+	if (((perf_processor_interface == CUDA_INTF) &&
+			(interface_type != CUDA_INTF)) ||
+		((perf_processor_interface == ONYX_INTF) &&
+			(interface_type != ONYX_INTF)))
 		return -EINVAL;
 
 	/* Next check to make sure the requested image
 	   is valid */
-	if (((interface_type == CUDA_INTF) && 
+	if (((interface_type == CUDA_INTF) &&
 		       (test >= MAX_CUDA_IMAGES)) ||
-	    ((interface_type == ONYX_INTF) && 
-		       (test >= MAX_ONYX_IMAGES))) 
+	    ((interface_type == ONYX_INTF) &&
+		       (test >= MAX_ONYX_IMAGES)))
 		return -EINVAL;
 
 	/* Copy the image into the processor */
-	if (interface_type == CUDA_INTF) 
+	if (interface_type == CUDA_INTF)
 		return perf_config(cuda_images[test]);
 	else
 		return perf_config(onyx_images[test]);
@@ -360,7 +360,7 @@
 static void perf_patch_images(void)
 {
 #if 0 /* FIXME!! */
-/* 
+/*
  * NOTE:  this routine is VERY specific to the current TLB image.
  * If the image is changed, this routine might also need to be changed.
  */
@@ -368,9 +368,9 @@
 	extern void $i_dtlb_miss_2_0();
 	extern void PA2_0_iva();
 
-	/* 
+	/*
 	 * We can only use the lower 32-bits, the upper 32-bits should be 0
-	 * anyway given this is in the kernel 
+	 * anyway given this is in the kernel
 	 */
 	uint32_t itlb_addr  = (uint32_t)&($i_itlb_miss_2_0);
 	uint32_t dtlb_addr  = (uint32_t)&($i_dtlb_miss_2_0);
@@ -378,21 +378,21 @@
 
 	if (perf_processor_interface == ONYX_INTF) {
 		/* clear last 2 bytes */
-		onyx_images[TLBMISS][15] &= 0xffffff00;  
+		onyx_images[TLBMISS][15] &= 0xffffff00;
 		/* set 2 bytes */
 		onyx_images[TLBMISS][15] |= (0x000000ff&((dtlb_addr) >> 24));
 		onyx_images[TLBMISS][16] = (dtlb_addr << 8)&0xffffff00;
 		onyx_images[TLBMISS][17] = itlb_addr;
 
 		/* clear last 2 bytes */
-		onyx_images[TLBHANDMISS][15] &= 0xffffff00;  
+		onyx_images[TLBHANDMISS][15] &= 0xffffff00;
 		/* set 2 bytes */
 		onyx_images[TLBHANDMISS][15] |= (0x000000ff&((dtlb_addr) >> 24));
 		onyx_images[TLBHANDMISS][16] = (dtlb_addr << 8)&0xffffff00;
 		onyx_images[TLBHANDMISS][17] = itlb_addr;
 
 		/* clear last 2 bytes */
-		onyx_images[BIG_CPI][15] &= 0xffffff00;  
+		onyx_images[BIG_CPI][15] &= 0xffffff00;
 		/* set 2 bytes */
 		onyx_images[BIG_CPI][15] |= (0x000000ff&((dtlb_addr) >> 24));
 		onyx_images[BIG_CPI][16] = (dtlb_addr << 8)&0xffffff00;
@@ -405,24 +405,24 @@
 
 	} else if (perf_processor_interface == CUDA_INTF) {
 		/* Cuda interface */
-		cuda_images[TLBMISS][16] =  
+		cuda_images[TLBMISS][16] =
 			(cuda_images[TLBMISS][16]&0xffff0000) |
 			((dtlb_addr >> 8)&0x0000ffff);
-		cuda_images[TLBMISS][17] = 
+		cuda_images[TLBMISS][17] =
 			((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
 		cuda_images[TLBMISS][18] = (itlb_addr << 16)&0xffff0000;
 
-		cuda_images[TLBHANDMISS][16] = 
+		cuda_images[TLBHANDMISS][16] =
 			(cuda_images[TLBHANDMISS][16]&0xffff0000) |
 			((dtlb_addr >> 8)&0x0000ffff);
-		cuda_images[TLBHANDMISS][17] = 
+		cuda_images[TLBHANDMISS][17] =
 			((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
 		cuda_images[TLBHANDMISS][18] = (itlb_addr << 16)&0xffff0000;
 
-		cuda_images[BIG_CPI][16] = 
+		cuda_images[BIG_CPI][16] =
 			(cuda_images[BIG_CPI][16]&0xffff0000) |
 			((dtlb_addr >> 8)&0x0000ffff);
-		cuda_images[BIG_CPI][17] = 
+		cuda_images[BIG_CPI][17] =
 			((dtlb_addr << 24)&0xff000000) | ((itlb_addr >> 16)&0x000000ff);
 		cuda_images[BIG_CPI][18] = (itlb_addr << 16)&0xffff0000;
 	} else {
@@ -434,7 +434,7 @@
 
 /*
  * ioctl routine
- * All routines effect the processor that they are executed on.  Thus you 
+ * All routines effect the processor that they are executed on.  Thus you
  * must be running on the processor that you wish to change.
  */
 
@@ -460,7 +460,7 @@
 			}
 
 			/* copy out the Counters */
-			if (copy_to_user((void __user *)arg, raddr, 
+			if (copy_to_user((void __user *)arg, raddr,
 					sizeof (raddr)) != 0) {
 				error =  -EFAULT;
 				break;
@@ -488,7 +488,7 @@
 	.open = perf_open,
 	.release = perf_release
 };
-	
+
 static struct miscdevice perf_dev = {
 	MISC_DYNAMIC_MINOR,
 	PA_PERF_DEV,
@@ -596,7 +596,7 @@
 		/* OR sticky2 (bit 1496) to counter2 bit 32 */
 		tmp64 |= (userbuf[23] >> 8) & 0x0000000080000000;
 		raddr[2] = (uint32_t)tmp64;
-		
+
 		/* Counter3 is bits 1497 to 1528 */
 		tmp64 =  (userbuf[23] >> 7) & 0x00000000ffffffff;
 		/* OR sticky3 (bit 1529) to counter3 bit 32 */
@@ -618,7 +618,7 @@
 		userbuf[22] = 0;
 		userbuf[23] = 0;
 
-		/* 
+		/*
 		 * Write back the zeroed bytes + the image given
 		 * the read was destructive.
 		 */
@@ -626,13 +626,13 @@
 	} else {
 
 		/*
-		 * Read RDR-15 which contains the counters and sticky bits 
+		 * Read RDR-15 which contains the counters and sticky bits
 		 */
 		if (!perf_rdr_read_ubuf(15, userbuf)) {
 			return -13;
 		}
 
-		/* 
+		/*
 		 * Clear out the counters
 		 */
 		perf_rdr_clear(15);
@@ -645,7 +645,7 @@
 		raddr[2] = (uint32_t)((userbuf[1] >> 32) & 0x00000000ffffffffUL);
 		raddr[3] = (uint32_t)(userbuf[1] & 0x00000000ffffffffUL);
 	}
- 
+
 	return 0;
 }
 
@@ -683,7 +683,7 @@
 	i = tentry->num_words;
 	while (i--) {
 		buffer[i] = 0;
-	}	
+	}
 
 	/* Check for bits an even number of 64 */
 	if ((xbits = width & 0x03f) != 0) {
@@ -809,18 +809,22 @@
 	}
 
 	runway = ioremap_nocache(cpu_device->hpa.start, 4096);
+	if (!runway) {
+		pr_err("perf_write_image: ioremap failed!\n");
+		return -ENOMEM;
+	}
 
 	/* Merge intrigue bits into Runway STATUS 0 */
 	tmp64 = __raw_readq(runway + RUNWAY_STATUS) & 0xffecfffffffffffful;
-	__raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul), 
+	__raw_writeq(tmp64 | (*memaddr++ & 0x0013000000000000ul),
 		     runway + RUNWAY_STATUS);
-	
+
 	/* Write RUNWAY DEBUG registers */
 	for (i = 0; i < 8; i++) {
 		__raw_writeq(*memaddr++, runway + RUNWAY_DEBUG);
 	}
 
-	return 0; 
+	return 0;
 }
 
 /*
@@ -844,7 +848,7 @@
 			perf_rdr_shift_out_U(rdr_num, buffer[i]);
 		} else {
 			perf_rdr_shift_out_W(rdr_num, buffer[i]);
-		}	
+		}
 	}
 printk("perf_rdr_write done\n");
 }
diff --git a/arch/parisc/kernel/syscall.S b/arch/parisc/kernel/syscall.S
index 23de307..41e60a9 100644
--- a/arch/parisc/kernel/syscall.S
+++ b/arch/parisc/kernel/syscall.S
@@ -742,7 +742,7 @@
 10:	ldd	0(%r25), %r25
 11:	ldd	0(%r24), %r24
 #else
-	/* Load new value into r22/r23 - high/low */
+	/* Load old value into r22/r23 - high/low */
 10:	ldw	0(%r25), %r22
 11:	ldw	4(%r25), %r23
 	/* Load new value into fr4 for atomic store later */
@@ -834,11 +834,11 @@
 	copy	%r0, %r28
 #else
 	/* Compare first word */
-19:	ldw,ma	0(%r26), %r29
+19:	ldw	0(%r26), %r29
 	sub,=	%r29, %r22, %r0
 	b,n	cas2_end
 	/* Compare second word */
-20:	ldw,ma	4(%r26), %r29
+20:	ldw	4(%r26), %r29
 	sub,=	%r29, %r23, %r0
 	b,n	cas2_end
 	/* Perform the store */
diff --git a/arch/powerpc/boot/dts/fsl/kmcoge4.dts b/arch/powerpc/boot/dts/fsl/kmcoge4.dts
index ae70a24..e103c0f 100644
--- a/arch/powerpc/boot/dts/fsl/kmcoge4.dts
+++ b/arch/powerpc/boot/dts/fsl/kmcoge4.dts
@@ -83,6 +83,10 @@
 			};
 		};
 
+		sdhc@114000 {
+			status = "disabled";
+		};
+
 		i2c@119000 {
 			status = "disabled";
 		};
diff --git a/arch/powerpc/include/asm/mmu_context.h b/arch/powerpc/include/asm/mmu_context.h
index 0012f03..fe208b7 100644
--- a/arch/powerpc/include/asm/mmu_context.h
+++ b/arch/powerpc/include/asm/mmu_context.h
@@ -75,9 +75,27 @@
 				      struct task_struct *tsk)
 {
 	/* Mark this context has been used on the new CPU */
-	if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next)))
+	if (!cpumask_test_cpu(smp_processor_id(), mm_cpumask(next))) {
 		cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
 
+		/*
+		 * This full barrier orders the store to the cpumask above vs
+		 * a subsequent operation which allows this CPU to begin loading
+		 * translations for next.
+		 *
+		 * When using the radix MMU that operation is the load of the
+		 * MMU context id, which is then moved to SPRN_PID.
+		 *
+		 * For the hash MMU it is either the first load from slb_cache
+		 * in switch_slb(), and/or the store of paca->mm_ctx_id in
+		 * copy_mm_to_paca().
+		 *
+		 * On the read side the barrier is in pte_xchg(), which orders
+		 * the store to the PTE vs the load of mm_cpumask.
+		 */
+		smp_mb();
+	}
+
 	/* 32-bit keeps track of the current PGDIR in the thread struct */
 #ifdef CONFIG_PPC32
 	tsk->thread.pgdir = next->pgd;
diff --git a/arch/powerpc/include/asm/pgtable-be-types.h b/arch/powerpc/include/asm/pgtable-be-types.h
index 49c0a5a..68e087e 100644
--- a/arch/powerpc/include/asm/pgtable-be-types.h
+++ b/arch/powerpc/include/asm/pgtable-be-types.h
@@ -87,6 +87,7 @@
 	unsigned long *p = (unsigned long *)ptep;
 	__be64 prev;
 
+	/* See comment in switch_mm_irqs_off() */
 	prev = (__force __be64)__cmpxchg_u64(p, (__force unsigned long)pte_raw(old),
 					     (__force unsigned long)pte_raw(new));
 
diff --git a/arch/powerpc/include/asm/pgtable-types.h b/arch/powerpc/include/asm/pgtable-types.h
index e7f4f3e..41e9d0a 100644
--- a/arch/powerpc/include/asm/pgtable-types.h
+++ b/arch/powerpc/include/asm/pgtable-types.h
@@ -62,6 +62,7 @@
 {
 	unsigned long *p = (unsigned long *)ptep;
 
+	/* See comment in switch_mm_irqs_off() */
 	return pte_val(old) == __cmpxchg_u64(p, pte_val(old), pte_val(new));
 }
 #endif
diff --git a/arch/powerpc/kernel/align.c b/arch/powerpc/kernel/align.c
index b2da7c8..292458b 100644
--- a/arch/powerpc/kernel/align.c
+++ b/arch/powerpc/kernel/align.c
@@ -235,6 +235,28 @@
 
 #define SWIZ_PTR(p)		((unsigned char __user *)((p) ^ swiz))
 
+#define __get_user_or_set_dar(_regs, _dest, _addr)		\
+	({							\
+		int rc = 0;					\
+		typeof(_addr) __addr = (_addr);			\
+		if (__get_user_inatomic(_dest, __addr)) {	\
+			_regs->dar = (unsigned long)__addr;	\
+			rc = -EFAULT;				\
+		}						\
+		rc;						\
+	})
+
+#define __put_user_or_set_dar(_regs, _src, _addr)		\
+	({							\
+		int rc = 0;					\
+		typeof(_addr) __addr = (_addr);			\
+		if (__put_user_inatomic(_src, __addr)) {	\
+			_regs->dar = (unsigned long)__addr;	\
+			rc = -EFAULT;				\
+		}						\
+		rc;						\
+	})
+
 static int emulate_multiple(struct pt_regs *regs, unsigned char __user *addr,
 			    unsigned int reg, unsigned int nb,
 			    unsigned int flags, unsigned int instr,
@@ -263,9 +285,10 @@
 		} else {
 			unsigned long pc = regs->nip ^ (swiz & 4);
 
-			if (__get_user_inatomic(instr,
-						(unsigned int __user *)pc))
+			if (__get_user_or_set_dar(regs, instr,
+						  (unsigned int __user *)pc))
 				return -EFAULT;
+
 			if (swiz == 0 && (flags & SW))
 				instr = cpu_to_le32(instr);
 			nb = (instr >> 11) & 0x1f;
@@ -309,31 +332,31 @@
 			       ((nb0 + 3) / 4) * sizeof(unsigned long));
 
 		for (i = 0; i < nb; ++i, ++p)
-			if (__get_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
-						SWIZ_PTR(p)))
+			if (__get_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
+						  SWIZ_PTR(p)))
 				return -EFAULT;
 		if (nb0 > 0) {
 			rptr = &regs->gpr[0];
 			addr += nb;
 			for (i = 0; i < nb0; ++i, ++p)
-				if (__get_user_inatomic(REG_BYTE(rptr,
-								 i ^ bswiz),
-							SWIZ_PTR(p)))
+				if (__get_user_or_set_dar(regs,
+							  REG_BYTE(rptr, i ^ bswiz),
+							  SWIZ_PTR(p)))
 					return -EFAULT;
 		}
 
 	} else {
 		for (i = 0; i < nb; ++i, ++p)
-			if (__put_user_inatomic(REG_BYTE(rptr, i ^ bswiz),
-						SWIZ_PTR(p)))
+			if (__put_user_or_set_dar(regs, REG_BYTE(rptr, i ^ bswiz),
+						  SWIZ_PTR(p)))
 				return -EFAULT;
 		if (nb0 > 0) {
 			rptr = &regs->gpr[0];
 			addr += nb;
 			for (i = 0; i < nb0; ++i, ++p)
-				if (__put_user_inatomic(REG_BYTE(rptr,
-								 i ^ bswiz),
-							SWIZ_PTR(p)))
+				if (__put_user_or_set_dar(regs,
+							  REG_BYTE(rptr, i ^ bswiz),
+							  SWIZ_PTR(p)))
 					return -EFAULT;
 		}
 	}
@@ -345,29 +368,32 @@
  * Only POWER6 has these instructions, and it does true little-endian,
  * so we don't need the address swizzling.
  */
-static int emulate_fp_pair(unsigned char __user *addr, unsigned int reg,
-			   unsigned int flags)
+static int emulate_fp_pair(struct pt_regs *regs, unsigned char __user *addr,
+			   unsigned int reg, unsigned int flags)
 {
 	char *ptr0 = (char *) &current->thread.TS_FPR(reg);
 	char *ptr1 = (char *) &current->thread.TS_FPR(reg+1);
-	int i, ret, sw = 0;
+	int i, sw = 0;
 
 	if (reg & 1)
 		return 0;	/* invalid form: FRS/FRT must be even */
 	if (flags & SW)
 		sw = 7;
-	ret = 0;
+
 	for (i = 0; i < 8; ++i) {
 		if (!(flags & ST)) {
-			ret |= __get_user(ptr0[i^sw], addr + i);
-			ret |= __get_user(ptr1[i^sw], addr + i + 8);
+			if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+				return -EFAULT;
+			if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+				return -EFAULT;
 		} else {
-			ret |= __put_user(ptr0[i^sw], addr + i);
-			ret |= __put_user(ptr1[i^sw], addr + i + 8);
+			if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+				return -EFAULT;
+			if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+				return -EFAULT;
 		}
 	}
-	if (ret)
-		return -EFAULT;
+
 	return 1;	/* exception handled and fixed up */
 }
 
@@ -377,24 +403,27 @@
 {
 	char *ptr0 = (char *)&regs->gpr[reg];
 	char *ptr1 = (char *)&regs->gpr[reg+1];
-	int i, ret, sw = 0;
+	int i, sw = 0;
 
 	if (reg & 1)
 		return 0;	/* invalid form: GPR must be even */
 	if (flags & SW)
 		sw = 7;
-	ret = 0;
+
 	for (i = 0; i < 8; ++i) {
 		if (!(flags & ST)) {
-			ret |= __get_user(ptr0[i^sw], addr + i);
-			ret |= __get_user(ptr1[i^sw], addr + i + 8);
+			if (__get_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+				return -EFAULT;
+			if (__get_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+				return -EFAULT;
 		} else {
-			ret |= __put_user(ptr0[i^sw], addr + i);
-			ret |= __put_user(ptr1[i^sw], addr + i + 8);
+			if (__put_user_or_set_dar(regs, ptr0[i^sw], addr + i))
+				return -EFAULT;
+			if (__put_user_or_set_dar(regs, ptr1[i^sw], addr + i + 8))
+				return -EFAULT;
 		}
 	}
-	if (ret)
-		return -EFAULT;
+
 	return 1;	/* exception handled and fixed up */
 }
 #endif /* CONFIG_PPC64 */
@@ -687,9 +716,14 @@
 	for (j = 0; j < length; j += elsize) {
 		for (i = 0; i < elsize; ++i) {
 			if (flags & ST)
-				ret |= __put_user(ptr[i^sw], addr + i);
+				ret = __put_user_or_set_dar(regs, ptr[i^sw],
+							    addr + i);
 			else
-				ret |= __get_user(ptr[i^sw], addr + i);
+				ret = __get_user_or_set_dar(regs, ptr[i^sw],
+							    addr + i);
+
+			if (ret)
+				return ret;
 		}
 		ptr  += elsize;
 #ifdef __LITTLE_ENDIAN__
@@ -739,7 +773,7 @@
 	unsigned int dsisr;
 	unsigned char __user *addr;
 	unsigned long p, swiz;
-	int ret, i;
+	int i;
 	union data {
 		u64 ll;
 		double dd;
@@ -936,7 +970,7 @@
 		if (flags & F) {
 			/* Special case for 16-byte FP loads and stores */
 			PPC_WARN_ALIGNMENT(fp_pair, regs);
-			return emulate_fp_pair(addr, reg, flags);
+			return emulate_fp_pair(regs, addr, reg, flags);
 		} else {
 #ifdef CONFIG_PPC64
 			/* Special case for 16-byte loads and stores */
@@ -966,15 +1000,12 @@
 		}
 
 		data.ll = 0;
-		ret = 0;
 		p = (unsigned long)addr;
 
 		for (i = 0; i < nb; i++)
-			ret |= __get_user_inatomic(data.v[start + i],
-						   SWIZ_PTR(p++));
-
-		if (unlikely(ret))
-			return -EFAULT;
+			if (__get_user_or_set_dar(regs, data.v[start + i],
+						  SWIZ_PTR(p++)))
+				return -EFAULT;
 
 	} else if (flags & F) {
 		data.ll = current->thread.TS_FPR(reg);
@@ -1046,15 +1077,13 @@
 			break;
 		}
 
-		ret = 0;
 		p = (unsigned long)addr;
 
 		for (i = 0; i < nb; i++)
-			ret |= __put_user_inatomic(data.v[start + i],
-						   SWIZ_PTR(p++));
+			if (__put_user_or_set_dar(regs, data.v[start + i],
+						  SWIZ_PTR(p++)))
+				return -EFAULT;
 
-		if (unlikely(ret))
-			return -EFAULT;
 	} else if (flags & F)
 		current->thread.TS_FPR(reg) = data.ll;
 	else
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 767ef6d..caa6596 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -1235,10 +1235,14 @@
 	stdu	r1,-SWITCH_FRAME_SIZE(r1)
 
 	/* Save all gprs to pt_regs */
-	SAVE_8GPRS(0,r1)
-	SAVE_8GPRS(8,r1)
-	SAVE_8GPRS(16,r1)
-	SAVE_8GPRS(24,r1)
+	SAVE_GPR(0, r1)
+	SAVE_10GPRS(2, r1)
+	SAVE_10GPRS(12, r1)
+	SAVE_10GPRS(22, r1)
+
+	/* Save previous stack pointer (r1) */
+	addi	r8, r1, SWITCH_FRAME_SIZE
+	std	r8, GPR1(r1)
 
 	/* Load special regs for save below */
 	mfmsr   r8
@@ -1292,10 +1296,10 @@
 #endif
 
 	/* Restore gprs */
-	REST_8GPRS(0,r1)
-	REST_8GPRS(8,r1)
-	REST_8GPRS(16,r1)
-	REST_8GPRS(24,r1)
+	REST_GPR(0,r1)
+	REST_10GPRS(2,r1)
+	REST_10GPRS(12,r1)
+	REST_10GPRS(22,r1)
 
 	/* Restore callee's TOC */
 	ld	r2, 24(r1)
diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S
index 2e2fc1e..fd68e19 100644
--- a/arch/powerpc/kernel/exceptions-64s.S
+++ b/arch/powerpc/kernel/exceptions-64s.S
@@ -764,7 +764,29 @@
 EXC_VIRT(program_check, 0x4700, 0x4800, 0x700)
 TRAMP_KVM(PACA_EXGEN, 0x700)
 EXC_COMMON_BEGIN(program_check_common)
-	EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN)
+	/*
+	 * It's possible to receive a TM Bad Thing type program check with
+	 * userspace register values (in particular r1), but with SRR1 reporting
+	 * that we came from the kernel. Normally that would confuse the bad
+	 * stack logic, and we would report a bad kernel stack pointer. Instead
+	 * we switch to the emergency stack if we're taking a TM Bad Thing from
+	 * the kernel.
+	 */
+	li	r10,MSR_PR		/* Build a mask of MSR_PR ..	*/
+	oris	r10,r10,0x200000@h	/* .. and SRR1_PROGTM		*/
+	and	r10,r10,r12		/* Mask SRR1 with that.		*/
+	srdi	r10,r10,8		/* Shift it so we can compare	*/
+	cmpldi	r10,(0x200000 >> 8)	/* .. with an immediate.	*/
+	bne 1f				/* If != go to normal path.	*/
+
+	/* SRR1 had PR=0 and SRR1_PROGTM=1, so use the emergency stack	*/
+	andi.	r10,r12,MSR_PR;		/* Set CR0 correctly for label	*/
+					/* 3 in EXCEPTION_PROLOG_COMMON	*/
+	mr	r10,r1			/* Save r1			*/
+	ld	r1,PACAEMERGSP(r13)	/* Use emergency stack		*/
+	subi	r1,r1,INT_FRAME_SIZE	/* alloc stack frame		*/
+	b 3f				/* Jump into the macro !!	*/
+1:	EXCEPTION_PROLOG_COMMON(0x700, PACA_EXGEN)
 	bl	save_nvgprs
 	RECONCILE_IRQ_STATE(r10, r11)
 	addi	r3,r1,STACK_FRAME_OVERHEAD
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 3c05c31..028a22b 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -146,6 +146,19 @@
 
 	/* Clear bit 0 which we wouldn't clear otherwise */
 	local_paca->irq_happened &= ~PACA_IRQ_HARD_DIS;
+	if (happened & PACA_IRQ_HARD_DIS) {
+		/*
+		 * We may have missed a decrementer interrupt if hard disabled.
+		 * Check the decrementer register in case we had a rollover
+		 * while hard disabled.
+		 */
+		if (!(happened & PACA_IRQ_DEC)) {
+			if (decrementer_check_overflow()) {
+				local_paca->irq_happened |= PACA_IRQ_DEC;
+				happened |= PACA_IRQ_DEC;
+			}
+		}
+	}
 
 	/*
 	 * Force the delivery of pending soft-disabled interrupts on PS3.
@@ -171,7 +184,7 @@
 	 * in case we also had a rollover while hard disabled
 	 */
 	local_paca->irq_happened &= ~PACA_IRQ_DEC;
-	if ((happened & PACA_IRQ_DEC) || decrementer_check_overflow())
+	if (happened & PACA_IRQ_DEC)
 		return 0x900;
 
 	/* Finally check if an external interrupt happened */
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index b249c2f..1c141d5 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -359,7 +359,8 @@
 
 	cpumsr = msr_check_and_set(MSR_FP|MSR_VEC|MSR_VSX);
 
-	if (current->thread.regs && (current->thread.regs->msr & MSR_VSX)) {
+	if (current->thread.regs &&
+	    (current->thread.regs->msr & (MSR_VSX|MSR_VEC|MSR_FP))) {
 		check_if_tm_restore_required(current);
 		/*
 		 * If a thread has already been reclaimed then the
@@ -383,7 +384,7 @@
 {
 	if (tsk->thread.regs) {
 		preempt_disable();
-		if (tsk->thread.regs->msr & MSR_VSX) {
+		if (tsk->thread.regs->msr & (MSR_VSX|MSR_VEC|MSR_FP)) {
 			BUG_ON(tsk != current);
 			giveup_vsx(tsk);
 		}
diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c
index 5c8f12f..d973708 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -127,12 +127,19 @@
 	 * If task is not current, it will have been flushed already to
 	 * it's thread_struct during __switch_to().
 	 *
-	 * A reclaim flushes ALL the state.
+	 * A reclaim flushes ALL the state or if not in TM save TM SPRs
+	 * in the appropriate thread structures from live.
 	 */
 
-	if (tsk == current && MSR_TM_SUSPENDED(mfmsr()))
-		tm_reclaim_current(TM_CAUSE_SIGNAL);
+	if ((!cpu_has_feature(CPU_FTR_TM)) || (tsk != current))
+		return;
 
+	if (MSR_TM_SUSPENDED(mfmsr())) {
+		tm_reclaim_current(TM_CAUSE_SIGNAL);
+	} else {
+		tm_enable();
+		tm_save_sprs(&(tsk->thread));
+	}
 }
 #else
 static inline void flush_tmregs_to_thread(struct task_struct *tsk) { }
diff --git a/arch/powerpc/kernel/signal_64.c b/arch/powerpc/kernel/signal_64.c
index 96698fd..04e9225 100644
--- a/arch/powerpc/kernel/signal_64.c
+++ b/arch/powerpc/kernel/signal_64.c
@@ -452,9 +452,20 @@
 	if (MSR_TM_RESV(msr))
 		return -EINVAL;
 
-	/* pull in MSR TM from user context */
+	/* pull in MSR TS bits from user context */
 	regs->msr = (regs->msr & ~MSR_TS_MASK) | (msr & MSR_TS_MASK);
 
+	/*
+	 * Ensure that TM is enabled in regs->msr before we leave the signal
+	 * handler. It could be the case that (a) user disabled the TM bit
+	 * through the manipulation of the MSR bits in uc_mcontext or (b) the
+	 * TM bit was disabled because a sufficient number of context switches
+	 * happened whilst in the signal handler and load_tm overflowed,
+	 * disabling the TM bit. In either case we can end up with an illegal
+	 * TM state leading to a TM Bad Thing when we return to userspace.
+	 */
+	regs->msr |= MSR_TM;
+
 	/* pull in MSR LE from user context */
 	regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
 
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index bc3f7d0..f1d7e99 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -407,6 +407,7 @@
 	struct cpu_accounting_data *acct = get_accounting(current);
 
 	acct->starttime = get_accounting(prev)->starttime;
+	acct->startspurr = get_accounting(prev)->startspurr;
 	acct->system_time = 0;
 	acct->user_time = 0;
 }
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index c379ff5..da2a7ec 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -129,8 +129,11 @@
 static int kvm_spapr_tce_release(struct inode *inode, struct file *filp)
 {
 	struct kvmppc_spapr_tce_table *stt = filp->private_data;
+	struct kvm *kvm = stt->kvm;
 
+	mutex_lock(&kvm->lock);
 	list_del_rcu(&stt->list);
+	mutex_unlock(&kvm->lock);
 
 	kvm_put_kvm(stt->kvm);
 
@@ -150,6 +153,7 @@
 				   struct kvm_create_spapr_tce_64 *args)
 {
 	struct kvmppc_spapr_tce_table *stt = NULL;
+	struct kvmppc_spapr_tce_table *siter;
 	unsigned long npages, size;
 	int ret = -ENOMEM;
 	int i;
@@ -157,24 +161,16 @@
 	if (!args->size)
 		return -EINVAL;
 
-	/* Check this LIOBN hasn't been previously allocated */
-	list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) {
-		if (stt->liobn == args->liobn)
-			return -EBUSY;
-	}
-
 	size = args->size;
 	npages = kvmppc_tce_pages(size);
 	ret = kvmppc_account_memlimit(kvmppc_stt_pages(npages), true);
-	if (ret) {
-		stt = NULL;
-		goto fail;
-	}
+	if (ret)
+		return ret;
 
 	stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *),
 		      GFP_KERNEL);
 	if (!stt)
-		goto fail;
+		goto fail_acct;
 
 	stt->liobn = args->liobn;
 	stt->page_shift = args->page_shift;
@@ -188,24 +184,39 @@
 			goto fail;
 	}
 
-	kvm_get_kvm(kvm);
-
 	mutex_lock(&kvm->lock);
-	list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables);
+
+	/* Check this LIOBN hasn't been previously allocated */
+	ret = 0;
+	list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) {
+		if (siter->liobn == args->liobn) {
+			ret = -EBUSY;
+			break;
+		}
+	}
+
+	if (!ret)
+		ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
+				       stt, O_RDWR | O_CLOEXEC);
+
+	if (ret >= 0) {
+		list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables);
+		kvm_get_kvm(kvm);
+	}
 
 	mutex_unlock(&kvm->lock);
 
-	return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
-				stt, O_RDWR | O_CLOEXEC);
+	if (ret >= 0)
+		return ret;
 
-fail:
-	if (stt) {
-		for (i = 0; i < npages; i++)
-			if (stt->pages[i])
-				__free_page(stt->pages[i]);
+ fail:
+	for (i = 0; i < npages; i++)
+		if (stt->pages[i])
+			__free_page(stt->pages[i]);
 
-		kfree(stt);
-	}
+	kfree(stt);
+ fail_acct:
+	kvmppc_account_memlimit(kvmppc_stt_pages(npages), false);
 	return ret;
 }
 
diff --git a/arch/powerpc/kvm/book3s_hv_rm_xics.c b/arch/powerpc/kvm/book3s_hv_rm_xics.c
index a0ea63a..a8e3498 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_xics.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_xics.c
@@ -376,6 +376,7 @@
 		 */
 		if (reject && reject != XICS_IPI) {
 			arch_spin_unlock(&ics->lock);
+			icp->n_reject++;
 			new_irq = reject;
 			goto again;
 		}
@@ -707,10 +708,8 @@
 	state = &ics->irq_state[src];
 
 	/* Still asserted, resend it */
-	if (state->asserted) {
-		icp->n_reject++;
+	if (state->asserted)
 		icp_rm_deliver_irq(xics, icp, irq);
-	}
 
 	if (!hlist_empty(&vcpu->kvm->irq_ack_notifier_list)) {
 		icp->rm_action |= XICS_RM_NOTIFY_EOI;
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 70963c8..fc0df0f 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -601,8 +601,7 @@
 		break;
 #endif
 	case KVM_CAP_PPC_HTM:
-		r = cpu_has_feature(CPU_FTR_TM_COMP) &&
-		    is_kvmppc_hv_enabled(kvm);
+		r = cpu_has_feature(CPU_FTR_TM_COMP) && hv_enabled;
 		break;
 	default:
 		r = 0;
diff --git a/arch/powerpc/mm/init_64.c b/arch/powerpc/mm/init_64.c
index d5ce34d..1e28747 100644
--- a/arch/powerpc/mm/init_64.c
+++ b/arch/powerpc/mm/init_64.c
@@ -42,6 +42,8 @@
 #include <linux/memblock.h>
 #include <linux/hugetlb.h>
 #include <linux/slab.h>
+#include <linux/of_fdt.h>
+#include <linux/libfdt.h>
 
 #include <asm/pgalloc.h>
 #include <asm/page.h>
@@ -421,6 +423,28 @@
 }
 early_param("disable_radix", parse_disable_radix);
 
+/*
+ * If we're running under a hypervisor, we currently can't do radix
+ * since we don't have the code to do the H_REGISTER_PROC_TBL hcall.
+ * We tell that we're running under a hypervisor by looking for the
+ * /chosen/ibm,architecture-vec-5 property.
+ */
+static void early_check_vec5(void)
+{
+	unsigned long root, chosen;
+	int size;
+	const u8 *vec5;
+
+	root = of_get_flat_dt_root();
+	chosen = of_get_flat_dt_subnode_by_name(root, "chosen");
+	if (chosen == -FDT_ERR_NOTFOUND)
+		return;
+	vec5 = of_get_flat_dt_prop(chosen, "ibm,architecture-vec-5", &size);
+	if (!vec5)
+		return;
+	cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
+}
+
 void __init mmu_early_init_devtree(void)
 {
 	/* Disable radix mode based on kernel command line. */
@@ -428,6 +452,15 @@
 	if (disable_radix || !(mfmsr() & MSR_HV))
 		cur_cpu_spec->mmu_features &= ~MMU_FTR_TYPE_RADIX;
 
+	/*
+	 * Check /chosen/ibm,architecture-vec-5 if running as a guest.
+	 * When running bare-metal, we can use radix if we like
+	 * even though the ibm,architecture-vec-5 property created by
+	 * skiboot doesn't have the necessary bits set.
+	 */
+	if (early_radix_enabled() && !(mfmsr() & MSR_HV))
+		early_check_vec5();
+
 	if (early_radix_enabled())
 		radix__early_init_devtree();
 	else
diff --git a/arch/powerpc/perf/isa207-common.h b/arch/powerpc/perf/isa207-common.h
index 4d0a4e5..8e6dd17 100644
--- a/arch/powerpc/perf/isa207-common.h
+++ b/arch/powerpc/perf/isa207-common.h
@@ -201,6 +201,10 @@
 	CNST_PMC_VAL(1) | CNST_PMC_VAL(2) | CNST_PMC_VAL(3) | \
 	CNST_PMC_VAL(4) | CNST_PMC_VAL(5) | CNST_PMC_VAL(6) | CNST_NC_VAL
 
+/*
+ * Lets restrict use of PMC5 for instruction counting.
+ */
+#define P9_DD1_TEST_ADDER	(ISA207_TEST_ADDER | CNST_PMC_VAL(5))
 
 /* Bits in MMCR1 for PowerISA v2.07 */
 #define MMCR1_UNIT_SHIFT(pmc)		(60 - (4 * ((pmc) - 1)))
diff --git a/arch/powerpc/perf/power9-pmu.c b/arch/powerpc/perf/power9-pmu.c
index 8e9a819..9abcd8f 100644
--- a/arch/powerpc/perf/power9-pmu.c
+++ b/arch/powerpc/perf/power9-pmu.c
@@ -295,7 +295,7 @@
 	.name			= "POWER9",
 	.n_counter		= MAX_PMU_COUNTERS,
 	.add_fields		= ISA207_ADD_FIELDS,
-	.test_adder		= ISA207_TEST_ADDER,
+	.test_adder		= P9_DD1_TEST_ADDER,
 	.compute_mmcr		= isa207_compute_mmcr,
 	.config_bhrb		= power9_config_bhrb,
 	.bhrb_filter_map	= power9_bhrb_filter_map,
diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
index a560a98..6a5e746 100644
--- a/arch/powerpc/platforms/pseries/mobility.c
+++ b/arch/powerpc/platforms/pseries/mobility.c
@@ -225,8 +225,10 @@
 		return -ENOENT;
 
 	dn = dlpar_configure_connector(drc_index, parent_dn);
-	if (!dn)
+	if (!dn) {
+		of_node_put(parent_dn);
 		return -ENOENT;
+	}
 
 	rc = dlpar_attach_node(dn);
 	if (rc)
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index 303d28e..591cbdf6 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -28,6 +28,7 @@
 #include <linux/cpufeature.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
+#include <linux/fips.h>
 #include <crypto/xts.h>
 #include <asm/cpacf.h>
 
@@ -501,6 +502,12 @@
 	if (err)
 		return err;
 
+	/* In fips mode only 128 bit or 256 bit keys are valid */
+	if (fips_enabled && key_len != 32 && key_len != 64) {
+		tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN;
+		return -EINVAL;
+	}
+
 	/* Pick the correct function code based on the key length */
 	fc = (key_len == 32) ? CPACF_KM_XTS_128 :
 	     (key_len == 64) ? CPACF_KM_XTS_256 : 0;
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index 1113389..fe7368a 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -110,22 +110,30 @@
 
 /*** helper functions ***/
 
+/*
+ * generate_entropy:
+ * This algorithm produces 64 bytes of entropy data based on 1024
+ * individual stckf() invocations assuming that each stckf() value
+ * contributes 0.25 bits of entropy. So the caller gets 256 bit
+ * entropy per 64 byte or 4 bits entropy per byte.
+ */
 static int generate_entropy(u8 *ebuf, size_t nbytes)
 {
 	int n, ret = 0;
-	u8 *pg, *h, hash[32];
+	u8 *pg, *h, hash[64];
 
-	pg = (u8 *) __get_free_page(GFP_KERNEL);
+	/* allocate 2 pages */
+	pg = (u8 *) __get_free_pages(GFP_KERNEL, 1);
 	if (!pg) {
 		prng_errorflag = PRNG_GEN_ENTROPY_FAILED;
 		return -ENOMEM;
 	}
 
 	while (nbytes) {
-		/* fill page with urandom bytes */
-		get_random_bytes(pg, PAGE_SIZE);
-		/* exor page with stckf values */
-		for (n = 0; n < PAGE_SIZE / sizeof(u64); n++) {
+		/* fill pages with urandom bytes */
+		get_random_bytes(pg, 2*PAGE_SIZE);
+		/* exor pages with 1024 stckf values */
+		for (n = 0; n < 2 * PAGE_SIZE / sizeof(u64); n++) {
 			u64 *p = ((u64 *)pg) + n;
 			*p ^= get_tod_clock_fast();
 		}
@@ -134,8 +142,8 @@
 			h = hash;
 		else
 			h = ebuf;
-		/* generate sha256 from this page */
-		cpacf_kimd(CPACF_KIMD_SHA_256, h, pg, PAGE_SIZE);
+		/* hash over the filled pages */
+		cpacf_kimd(CPACF_KIMD_SHA_512, h, pg, 2*PAGE_SIZE);
 		if (n < sizeof(hash))
 			memcpy(ebuf, hash, n);
 		ret += n;
@@ -143,7 +151,7 @@
 		nbytes -= n;
 	}
 
-	free_page((unsigned long)pg);
+	free_pages((unsigned long)pg, 1);
 	return ret;
 }
 
@@ -334,7 +342,7 @@
 static int __init prng_sha512_instantiate(void)
 {
 	int ret, datalen;
-	u8 seed[64];
+	u8 seed[64 + 32 + 16];
 
 	pr_debug("prng runs in SHA-512 mode "
 		 "with chunksize=%d and reseed_limit=%u\n",
@@ -357,12 +365,12 @@
 	if (ret)
 		goto outfree;
 
-	/* generate initial seed bytestring, first 48 bytes of entropy */
-	ret = generate_entropy(seed, 48);
-	if (ret != 48)
+	/* generate initial seed bytestring, with 256 + 128 bits entropy */
+	ret = generate_entropy(seed, 64 + 32);
+	if (ret != 64 + 32)
 		goto outfree;
 	/* followed by 16 bytes of unique nonce */
-	get_tod_clock_ext(seed + 48);
+	get_tod_clock_ext(seed + 64 + 32);
 
 	/* initial seed of the ppno drng */
 	cpacf_ppno(CPACF_PPNO_SHA512_DRNG_SEED,
@@ -395,9 +403,9 @@
 static int prng_sha512_reseed(void)
 {
 	int ret;
-	u8 seed[32];
+	u8 seed[64];
 
-	/* generate 32 bytes of fresh entropy */
+	/* fetch 256 bits of fresh entropy */
 	ret = generate_entropy(seed, sizeof(seed));
 	if (ret != sizeof(seed))
 		return ret;
diff --git a/arch/s390/include/asm/mmu.h b/arch/s390/include/asm/mmu.h
index bea785d..af85d6b 100644
--- a/arch/s390/include/asm/mmu.h
+++ b/arch/s390/include/asm/mmu.h
@@ -5,6 +5,7 @@
 #include <linux/errno.h>
 
 typedef struct {
+	spinlock_t lock;
 	cpumask_t cpu_attach_mask;
 	atomic_t flush_count;
 	unsigned int flush_mm;
@@ -25,6 +26,7 @@
 } mm_context_t;
 
 #define INIT_MM_CONTEXT(name)						   \
+	.context.lock =	__SPIN_LOCK_UNLOCKED(name.context.lock),	   \
 	.context.pgtable_lock =						   \
 			__SPIN_LOCK_UNLOCKED(name.context.pgtable_lock),   \
 	.context.pgtable_list = LIST_HEAD_INIT(name.context.pgtable_list), \
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index 515fea5..f65a708 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -15,6 +15,7 @@
 static inline int init_new_context(struct task_struct *tsk,
 				   struct mm_struct *mm)
 {
+	spin_lock_init(&mm->context.lock);
 	spin_lock_init(&mm->context.pgtable_lock);
 	INIT_LIST_HEAD(&mm->context.pgtable_list);
 	spin_lock_init(&mm->context.gmap_lock);
@@ -93,7 +94,6 @@
 	if (prev == next)
 		return;
 	cpumask_set_cpu(cpu, &next->context.cpu_attach_mask);
-	cpumask_set_cpu(cpu, mm_cpumask(next));
 	/* Clear old ASCE by loading the kernel ASCE. */
 	__ctl_load(S390_lowcore.kernel_asce, 1, 1);
 	__ctl_load(S390_lowcore.kernel_asce, 7, 7);
@@ -111,9 +111,8 @@
 		preempt_disable();
 		while (atomic_read(&mm->context.flush_count))
 			cpu_relax();
-
-		if (mm->context.flush_mm)
-			__tlb_flush_mm(mm);
+		cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+		__tlb_flush_mm_lazy(mm);
 		preempt_enable();
 	}
 	set_fs(current->thread.mm_segment);
@@ -126,6 +125,7 @@
                                struct mm_struct *next)
 {
 	switch_mm(prev, next, current);
+	cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
 	set_user_asce(next);
 }
 
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h
index 0cea702..db74d39 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -480,7 +480,7 @@
  * In the case that a guest uses storage keys
  * faults should no longer be backed by zero pages
  */
-#define mm_forbids_zeropage mm_use_skey
+#define mm_forbids_zeropage mm_has_pgste
 static inline int mm_use_skey(struct mm_struct *mm)
 {
 #ifdef CONFIG_PGSTE
@@ -1359,7 +1359,9 @@
 static inline void pmdp_invalidate(struct vm_area_struct *vma,
 				   unsigned long addr, pmd_t *pmdp)
 {
-	pmdp_xchg_direct(vma->vm_mm, addr, pmdp, __pmd(_SEGMENT_ENTRY_INVALID));
+	pmd_t pmd = __pmd(pmd_val(*pmdp) | _SEGMENT_ENTRY_INVALID);
+
+	pmdp_xchg_direct(vma->vm_mm, addr, pmdp, pmd);
 }
 
 #define __HAVE_ARCH_PMDP_SET_WRPROTECT
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 3984610..eed927a 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -43,23 +43,6 @@
  * Flush TLB entries for a specific mm on all CPUs (in case gmap is used
  * this implicates multiple ASCEs!).
  */
-static inline void __tlb_flush_full(struct mm_struct *mm)
-{
-	preempt_disable();
-	atomic_inc(&mm->context.flush_count);
-	if (cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
-		/* Local TLB flush */
-		__tlb_flush_local();
-	} else {
-		/* Global TLB flush */
-		__tlb_flush_global();
-		/* Reset TLB flush mask */
-		cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
-	}
-	atomic_dec(&mm->context.flush_count);
-	preempt_enable();
-}
-
 static inline void __tlb_flush_mm(struct mm_struct *mm)
 {
 	unsigned long gmap_asce;
@@ -71,16 +54,18 @@
 	 */
 	preempt_disable();
 	atomic_inc(&mm->context.flush_count);
+	/* Reset TLB flush mask */
+	cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
+	barrier();
 	gmap_asce = READ_ONCE(mm->context.gmap_asce);
 	if (MACHINE_HAS_IDTE && gmap_asce != -1UL) {
 		if (gmap_asce)
 			__tlb_flush_idte(gmap_asce);
 		__tlb_flush_idte(mm->context.asce);
 	} else {
-		__tlb_flush_full(mm);
+		/* Global TLB flush */
+		__tlb_flush_global();
 	}
-	/* Reset TLB flush mask */
-	cpumask_copy(mm_cpumask(mm), &mm->context.cpu_attach_mask);
 	atomic_dec(&mm->context.flush_count);
 	preempt_enable();
 }
@@ -94,7 +79,6 @@
 }
 #else
 #define __tlb_flush_global()	__tlb_flush_local()
-#define __tlb_flush_full(mm)	__tlb_flush_local()
 
 /*
  * Flush TLB entries for a specific ASCE on all CPUs.
@@ -112,10 +96,12 @@
 
 static inline void __tlb_flush_mm_lazy(struct mm_struct * mm)
 {
+	spin_lock(&mm->context.lock);
 	if (mm->context.flush_mm) {
-		__tlb_flush_mm(mm);
 		mm->context.flush_mm = 0;
+		__tlb_flush_mm(mm);
 	}
+	spin_unlock(&mm->context.lock);
 }
 
 /*
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 2374c5b..0c19686 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -363,6 +363,18 @@
 #endif
 }
 
+static int __init topology_setup(char *str)
+{
+	bool enabled;
+	int rc;
+
+	rc = kstrtobool(str, &enabled);
+	if (!rc && !enabled)
+		S390_lowcore.machine_flags &= ~MACHINE_HAS_TOPOLOGY;
+	return rc;
+}
+early_param("topology", topology_setup);
+
 static int __init disable_vector_extension(char *str)
 {
 	S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX;
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 8705ee6..239f295 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -37,7 +37,6 @@
 static void topology_work_fn(struct work_struct *work);
 static struct sysinfo_15_1_x *tl_info;
 
-static bool topology_enabled = true;
 static DECLARE_WORK(topology_work, topology_work_fn);
 
 /*
@@ -56,7 +55,7 @@
 	cpumask_t mask;
 
 	cpumask_copy(&mask, cpumask_of(cpu));
-	if (!topology_enabled || !MACHINE_HAS_TOPOLOGY)
+	if (!MACHINE_HAS_TOPOLOGY)
 		return mask;
 	for (; info; info = info->next) {
 		if (cpumask_test_cpu(cpu, &info->mask))
@@ -71,7 +70,7 @@
 	int i;
 
 	cpumask_copy(&mask, cpumask_of(cpu));
-	if (!topology_enabled || !MACHINE_HAS_TOPOLOGY)
+	if (!MACHINE_HAS_TOPOLOGY)
 		return mask;
 	cpu -= cpu % (smp_cpu_mtid + 1);
 	for (i = 0; i <= smp_cpu_mtid; i++)
@@ -413,12 +412,6 @@
 	return &per_cpu(cpu_topology, cpu).drawer_mask;
 }
 
-static int __init early_parse_topology(char *p)
-{
-	return kstrtobool(p, &topology_enabled);
-}
-early_param("topology", early_parse_topology);
-
 static struct sched_domain_topology_level s390_topology[] = {
 	{ cpu_thread_mask, cpu_smt_flags, SD_INIT_NAME(SMT) },
 	{ cpu_coregroup_mask, cpu_core_flags, SD_INIT_NAME(MC) },
diff --git a/arch/s390/kvm/sthyi.c b/arch/s390/kvm/sthyi.c
index 05c98bb..2f04ad1 100644
--- a/arch/s390/kvm/sthyi.c
+++ b/arch/s390/kvm/sthyi.c
@@ -394,7 +394,7 @@
 		"srl     %[cc],28\n"
 		: [cc] "=d" (cc)
 		: [code] "d" (code), [addr] "a" (addr)
-		: "memory", "cc");
+		: "3", "memory", "cc");
 	return cc;
 }
 
@@ -422,7 +422,7 @@
 	VCPU_EVENT(vcpu, 3, "STHYI: fc: %llu addr: 0x%016llx", code, addr);
 	trace_kvm_s390_handle_sthyi(vcpu, code, addr);
 
-	if (reg1 == reg2 || reg1 & 1 || reg2 & 1 || addr & ~PAGE_MASK)
+	if (reg1 == reg2 || reg1 & 1 || reg2 & 1)
 		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
 
 	if (code & 0xffff) {
@@ -430,6 +430,9 @@
 		goto out;
 	}
 
+	if (addr & ~PAGE_MASK)
+		return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
+
 	/*
 	 * If the page has not yet been faulted in, we want to do that
 	 * now and not after all the expensive calculations.
diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c
index 3ba6227..cb2cd04 100644
--- a/arch/s390/mm/gmap.c
+++ b/arch/s390/mm/gmap.c
@@ -2125,6 +2125,37 @@
 }
 
 /*
+ * Remove all empty zero pages from the mapping for lazy refaulting
+ * - This must be called after mm->context.has_pgste is set, to avoid
+ *   future creation of zero pages
+ * - This must be called after THP was enabled
+ */
+static int __zap_zero_pages(pmd_t *pmd, unsigned long start,
+			   unsigned long end, struct mm_walk *walk)
+{
+	unsigned long addr;
+
+	for (addr = start; addr != end; addr += PAGE_SIZE) {
+		pte_t *ptep;
+		spinlock_t *ptl;
+
+		ptep = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
+		if (is_zero_pfn(pte_pfn(*ptep)))
+			ptep_xchg_direct(walk->mm, addr, ptep, __pte(_PAGE_INVALID));
+		pte_unmap_unlock(ptep, ptl);
+	}
+	return 0;
+}
+
+static inline void zap_zero_pages(struct mm_struct *mm)
+{
+	struct mm_walk walk = { .pmd_entry = __zap_zero_pages };
+
+	walk.mm = mm;
+	walk_page_range(0, TASK_SIZE, &walk);
+}
+
+/*
  * switch on pgstes for its userspace process (for kvm)
  */
 int s390_enable_sie(void)
@@ -2141,6 +2172,7 @@
 	mm->context.has_pgste = 1;
 	/* split thp mappings and disable thp for future mappings */
 	thp_split_mm(mm);
+	zap_zero_pages(mm);
 	up_write(&mm->mmap_sem);
 	return 0;
 }
@@ -2153,13 +2185,6 @@
 static int __s390_enable_skey(pte_t *pte, unsigned long addr,
 			      unsigned long next, struct mm_walk *walk)
 {
-	/*
-	 * Remove all zero page mappings,
-	 * after establishing a policy to forbid zero page mappings
-	 * following faults for that page will get fresh anonymous pages
-	 */
-	if (is_zero_pfn(pte_pfn(*pte)))
-		ptep_xchg_direct(walk->mm, addr, pte, __pte(_PAGE_INVALID));
 	/* Clear storage key */
 	ptep_zap_key(walk->mm, addr, pte);
 	return 0;
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 18d4107e..97fc449 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -56,13 +56,12 @@
 static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
 		unsigned long end, int write, struct page **pages, int *nr)
 {
-	unsigned long mask, result;
 	struct page *head, *page;
+	unsigned long mask;
 	int refs;
 
-	result = write ? 0 : _SEGMENT_ENTRY_PROTECT;
-	mask = result | _SEGMENT_ENTRY_INVALID;
-	if ((pmd_val(pmd) & mask) != result)
+	mask = (write ? _SEGMENT_ENTRY_PROTECT : 0) | _SEGMENT_ENTRY_INVALID;
+	if ((pmd_val(pmd) & mask) != 0)
 		return 0;
 	VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT));
 
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
index bee281f..e8dee62 100644
--- a/arch/s390/net/bpf_jit_comp.c
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -1252,7 +1252,8 @@
 		insn_count = bpf_jit_insn(jit, fp, i);
 		if (insn_count < 0)
 			return -1;
-		jit->addrs[i + 1] = jit->prg; /* Next instruction address */
+		/* Next instruction address */
+		jit->addrs[i + insn_count] = jit->prg;
 	}
 	bpf_jit_epilogue(jit);
 
diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
index 538c10d..8dc315b 100644
--- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c
+++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c
@@ -165,7 +165,6 @@
 	.scscr		= SCSCR_TE | SCSCR_RE,
 	.type		= PORT_IRDA,
 	.ops		= &sh770x_sci_port_ops,
-	.regshift	= 1,
 };
 
 static struct resource scif2_resources[] = {
diff --git a/arch/sparc/include/asm/mmu_context_64.h b/arch/sparc/include/asm/mmu_context_64.h
index 349dd23..0cdeb2b 100644
--- a/arch/sparc/include/asm/mmu_context_64.h
+++ b/arch/sparc/include/asm/mmu_context_64.h
@@ -25,9 +25,11 @@
 void __tsb_context_switch(unsigned long pgd_pa,
 			  struct tsb_config *tsb_base,
 			  struct tsb_config *tsb_huge,
-			  unsigned long tsb_descr_pa);
+			  unsigned long tsb_descr_pa,
+			  unsigned long secondary_ctx);
 
-static inline void tsb_context_switch(struct mm_struct *mm)
+static inline void tsb_context_switch_ctx(struct mm_struct *mm,
+					  unsigned long ctx)
 {
 	__tsb_context_switch(__pa(mm->pgd),
 			     &mm->context.tsb_block[0],
@@ -38,9 +40,12 @@
 #else
 			     NULL
 #endif
-			     , __pa(&mm->context.tsb_descr[0]));
+			     , __pa(&mm->context.tsb_descr[0]),
+			     ctx);
 }
 
+#define tsb_context_switch(X) tsb_context_switch_ctx(X, 0)
+
 void tsb_grow(struct mm_struct *mm,
 	      unsigned long tsb_index,
 	      unsigned long mm_rss);
@@ -110,8 +115,7 @@
 	 * cpu0 to update it's TSB because at that point the cpu_vm_mask
 	 * only had cpu1 set in it.
 	 */
-	load_secondary_context(mm);
-	tsb_context_switch(mm);
+	tsb_context_switch_ctx(mm, CTX_HWBITS(mm->context));
 
 	/* Any time a processor runs a context on an address space
 	 * for the first time, we must flush that context out of the
diff --git a/arch/sparc/include/asm/setup.h b/arch/sparc/include/asm/setup.h
index be0cc1b..3fae200 100644
--- a/arch/sparc/include/asm/setup.h
+++ b/arch/sparc/include/asm/setup.h
@@ -59,8 +59,11 @@
 extern atomic_t dcpage_flushes_xcall;
 
 extern int sysctl_tsb_ratio;
-#endif
 
+#ifdef CONFIG_SERIAL_SUNHV
+void sunhv_migrate_hvcons_irq(int cpu);
+#endif
+#endif
 void sun_do_break(void);
 extern int stop_a_enabled;
 extern int scons_pwroff;
diff --git a/arch/sparc/include/asm/trap_block.h b/arch/sparc/include/asm/trap_block.h
index ec9c04d..ff05992 100644
--- a/arch/sparc/include/asm/trap_block.h
+++ b/arch/sparc/include/asm/trap_block.h
@@ -54,6 +54,7 @@
 void init_cur_cpu_trap(struct thread_info *);
 void setup_tba(void);
 extern int ncpus_probed;
+extern u64 cpu_mondo_counter[NR_CPUS];
 
 unsigned long real_hard_smp_processor_id(void);
 
diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c
index 06981cc..d04111a 100644
--- a/arch/sparc/kernel/pci_sun4v.c
+++ b/arch/sparc/kernel/pci_sun4v.c
@@ -1240,8 +1240,6 @@
 			 * ATU group, but ATU hcalls won't be available.
 			 */
 			hv_atu = false;
-			pr_err(PFX "Could not register hvapi ATU err=%d\n",
-			       err);
 		} else {
 			pr_info(PFX "Registered hvapi ATU major[%lu] minor[%lu]\n",
 				vatu_major, vatu_minor);
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index d5807d2..ca7cb8e 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -621,22 +621,48 @@
 	}
 }
 
-/* Multi-cpu list version.  */
+#define	CPU_MONDO_COUNTER(cpuid)	(cpu_mondo_counter[cpuid])
+#define	MONDO_USEC_WAIT_MIN		2
+#define	MONDO_USEC_WAIT_MAX		100
+#define	MONDO_RETRY_LIMIT		500000
+
+/* Multi-cpu list version.
+ *
+ * Deliver xcalls to 'cnt' number of cpus in 'cpu_list'.
+ * Sometimes not all cpus receive the mondo, requiring us to re-send
+ * the mondo until all cpus have received, or cpus are truly stuck
+ * unable to receive mondo, and we timeout.
+ * Occasionally a target cpu strand is borrowed briefly by hypervisor to
+ * perform guest service, such as PCIe error handling. Consider the
+ * service time, 1 second overall wait is reasonable for 1 cpu.
+ * Here two in-between mondo check wait time are defined: 2 usec for
+ * single cpu quick turn around and up to 100usec for large cpu count.
+ * Deliver mondo to large number of cpus could take longer, we adjusts
+ * the retry count as long as target cpus are making forward progress.
+ */
 static void hypervisor_xcall_deliver(struct trap_per_cpu *tb, int cnt)
 {
-	int retries, this_cpu, prev_sent, i, saw_cpu_error;
+	int this_cpu, tot_cpus, prev_sent, i, rem;
+	int usec_wait, retries, tot_retries;
+	u16 first_cpu = 0xffff;
+	unsigned long xc_rcvd = 0;
 	unsigned long status;
+	int ecpuerror_id = 0;
+	int enocpu_id = 0;
 	u16 *cpu_list;
+	u16 cpu;
 
 	this_cpu = smp_processor_id();
-
 	cpu_list = __va(tb->cpu_list_pa);
-
-	saw_cpu_error = 0;
-	retries = 0;
+	usec_wait = cnt * MONDO_USEC_WAIT_MIN;
+	if (usec_wait > MONDO_USEC_WAIT_MAX)
+		usec_wait = MONDO_USEC_WAIT_MAX;
+	retries = tot_retries = 0;
+	tot_cpus = cnt;
 	prev_sent = 0;
+
 	do {
-		int forward_progress, n_sent;
+		int n_sent, mondo_delivered, target_cpu_busy;
 
 		status = sun4v_cpu_mondo_send(cnt,
 					      tb->cpu_list_pa,
@@ -644,94 +670,113 @@
 
 		/* HV_EOK means all cpus received the xcall, we're done.  */
 		if (likely(status == HV_EOK))
-			break;
+			goto xcall_done;
+
+		/* If not these non-fatal errors, panic */
+		if (unlikely((status != HV_EWOULDBLOCK) &&
+			(status != HV_ECPUERROR) &&
+			(status != HV_ENOCPU)))
+			goto fatal_errors;
 
 		/* First, see if we made any forward progress.
 		 *
+		 * Go through the cpu_list, count the target cpus that have
+		 * received our mondo (n_sent), and those that did not (rem).
+		 * Re-pack cpu_list with the cpus remain to be retried in the
+		 * front - this simplifies tracking the truly stalled cpus.
+		 *
 		 * The hypervisor indicates successful sends by setting
 		 * cpu list entries to the value 0xffff.
+		 *
+		 * EWOULDBLOCK means some target cpus did not receive the
+		 * mondo and retry usually helps.
+		 *
+		 * ECPUERROR means at least one target cpu is in error state,
+		 * it's usually safe to skip the faulty cpu and retry.
+		 *
+		 * ENOCPU means one of the target cpu doesn't belong to the
+		 * domain, perhaps offlined which is unexpected, but not
+		 * fatal and it's okay to skip the offlined cpu.
 		 */
+		rem = 0;
 		n_sent = 0;
 		for (i = 0; i < cnt; i++) {
-			if (likely(cpu_list[i] == 0xffff))
+			cpu = cpu_list[i];
+			if (likely(cpu == 0xffff)) {
 				n_sent++;
+			} else if ((status == HV_ECPUERROR) &&
+				(sun4v_cpu_state(cpu) == HV_CPU_STATE_ERROR)) {
+				ecpuerror_id = cpu + 1;
+			} else if (status == HV_ENOCPU && !cpu_online(cpu)) {
+				enocpu_id = cpu + 1;
+			} else {
+				cpu_list[rem++] = cpu;
+			}
 		}
 
-		forward_progress = 0;
-		if (n_sent > prev_sent)
-			forward_progress = 1;
+		/* No cpu remained, we're done. */
+		if (rem == 0)
+			break;
 
+		/* Otherwise, update the cpu count for retry. */
+		cnt = rem;
+
+		/* Record the overall number of mondos received by the
+		 * first of the remaining cpus.
+		 */
+		if (first_cpu != cpu_list[0]) {
+			first_cpu = cpu_list[0];
+			xc_rcvd = CPU_MONDO_COUNTER(first_cpu);
+		}
+
+		/* Was any mondo delivered successfully? */
+		mondo_delivered = (n_sent > prev_sent);
 		prev_sent = n_sent;
 
-		/* If we get a HV_ECPUERROR, then one or more of the cpus
-		 * in the list are in error state.  Use the cpu_state()
-		 * hypervisor call to find out which cpus are in error state.
+		/* or, was any target cpu busy processing other mondos? */
+		target_cpu_busy = (xc_rcvd < CPU_MONDO_COUNTER(first_cpu));
+		xc_rcvd = CPU_MONDO_COUNTER(first_cpu);
+
+		/* Retry count is for no progress. If we're making progress,
+		 * reset the retry count.
 		 */
-		if (unlikely(status == HV_ECPUERROR)) {
-			for (i = 0; i < cnt; i++) {
-				long err;
-				u16 cpu;
-
-				cpu = cpu_list[i];
-				if (cpu == 0xffff)
-					continue;
-
-				err = sun4v_cpu_state(cpu);
-				if (err == HV_CPU_STATE_ERROR) {
-					saw_cpu_error = (cpu + 1);
-					cpu_list[i] = 0xffff;
-				}
-			}
-		} else if (unlikely(status != HV_EWOULDBLOCK))
-			goto fatal_mondo_error;
-
-		/* Don't bother rewriting the CPU list, just leave the
-		 * 0xffff and non-0xffff entries in there and the
-		 * hypervisor will do the right thing.
-		 *
-		 * Only advance timeout state if we didn't make any
-		 * forward progress.
-		 */
-		if (unlikely(!forward_progress)) {
-			if (unlikely(++retries > 10000))
-				goto fatal_mondo_timeout;
-
-			/* Delay a little bit to let other cpus catch up
-			 * on their cpu mondo queue work.
-			 */
-			udelay(2 * cnt);
+		if (likely(mondo_delivered || target_cpu_busy)) {
+			tot_retries += retries;
+			retries = 0;
+		} else if (unlikely(retries > MONDO_RETRY_LIMIT)) {
+			goto fatal_mondo_timeout;
 		}
+
+		/* Delay a little bit to let other cpus catch up on
+		 * their cpu mondo queue work.
+		 */
+		if (!mondo_delivered)
+			udelay(usec_wait);
+
+		retries++;
 	} while (1);
 
-	if (unlikely(saw_cpu_error))
-		goto fatal_mondo_cpu_error;
-
+xcall_done:
+	if (unlikely(ecpuerror_id > 0)) {
+		pr_crit("CPU[%d]: SUN4V mondo cpu error, target cpu(%d) was in error state\n",
+		       this_cpu, ecpuerror_id - 1);
+	} else if (unlikely(enocpu_id > 0)) {
+		pr_crit("CPU[%d]: SUN4V mondo cpu error, target cpu(%d) does not belong to the domain\n",
+		       this_cpu, enocpu_id - 1);
+	}
 	return;
 
-fatal_mondo_cpu_error:
-	printk(KERN_CRIT "CPU[%d]: SUN4V mondo cpu error, some target cpus "
-	       "(including %d) were in error state\n",
-	       this_cpu, saw_cpu_error - 1);
-	return;
+fatal_errors:
+	/* fatal errors include bad alignment, etc */
+	pr_crit("CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) mondo_block_pa(%lx)\n",
+	       this_cpu, tot_cpus, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
+	panic("Unexpected SUN4V mondo error %lu\n", status);
 
 fatal_mondo_timeout:
-	printk(KERN_CRIT "CPU[%d]: SUN4V mondo timeout, no forward "
-	       " progress after %d retries.\n",
-	       this_cpu, retries);
-	goto dump_cpu_list_and_out;
-
-fatal_mondo_error:
-	printk(KERN_CRIT "CPU[%d]: Unexpected SUN4V mondo error %lu\n",
-	       this_cpu, status);
-	printk(KERN_CRIT "CPU[%d]: Args were cnt(%d) cpulist_pa(%lx) "
-	       "mondo_block_pa(%lx)\n",
-	       this_cpu, cnt, tb->cpu_list_pa, tb->cpu_mondo_block_pa);
-
-dump_cpu_list_and_out:
-	printk(KERN_CRIT "CPU[%d]: CPU list [ ", this_cpu);
-	for (i = 0; i < cnt; i++)
-		printk("%u ", cpu_list[i]);
-	printk("]\n");
+	/* some cpus being non-responsive to the cpu mondo */
+	pr_crit("CPU[%d]: SUN4V mondo timeout, cpu(%d) made no forward progress after %d retries. Total target cpus(%d).\n",
+	       this_cpu, first_cpu, (tot_retries + retries), tot_cpus);
+	panic("SUN4V mondo timeout panic\n");
 }
 
 static void (*xcall_deliver_impl)(struct trap_per_cpu *, int);
@@ -1420,8 +1465,12 @@
 	int cpu;
 
 	if (tlb_type == hypervisor) {
+		int this_cpu = smp_processor_id();
+#ifdef CONFIG_SERIAL_SUNHV
+		sunhv_migrate_hvcons_irq(this_cpu);
+#endif
 		for_each_online_cpu(cpu) {
-			if (cpu == smp_processor_id())
+			if (cpu == this_cpu)
 				continue;
 #ifdef CONFIG_SUN_LDOMS
 			if (ldom_domaining_enabled) {
diff --git a/arch/sparc/kernel/sun4v_ivec.S b/arch/sparc/kernel/sun4v_ivec.S
index 559bc5e..3463199 100644
--- a/arch/sparc/kernel/sun4v_ivec.S
+++ b/arch/sparc/kernel/sun4v_ivec.S
@@ -26,6 +26,21 @@
 	ldxa	[%g0] ASI_SCRATCHPAD, %g4
 	sub	%g4, TRAP_PER_CPU_FAULT_INFO, %g4
 
+	/* Get smp_processor_id() into %g3 */
+	sethi	%hi(trap_block), %g5
+	or	%g5, %lo(trap_block), %g5
+	sub	%g4, %g5, %g3
+	srlx	%g3, TRAP_BLOCK_SZ_SHIFT, %g3
+
+	/* Increment cpu_mondo_counter[smp_processor_id()] */
+	sethi	%hi(cpu_mondo_counter), %g5
+	or	%g5, %lo(cpu_mondo_counter), %g5
+	sllx	%g3, 3, %g3
+	add	%g5, %g3, %g5
+	ldx	[%g5], %g3
+	add	%g3, 1, %g3
+	stx	%g3, [%g5]
+
 	/* Get CPU mondo queue base phys address into %g7.  */
 	ldx	[%g4 + TRAP_PER_CPU_CPU_MONDO_PA], %g7
 
diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c
index d44fb80..32dafb92 100644
--- a/arch/sparc/kernel/traps_64.c
+++ b/arch/sparc/kernel/traps_64.c
@@ -2732,6 +2732,7 @@
 	}
 }
 
+u64 cpu_mondo_counter[NR_CPUS] = {0};
 struct trap_per_cpu trap_block[NR_CPUS];
 EXPORT_SYMBOL(trap_block);
 
diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S
index 395ec18..7d961f6 100644
--- a/arch/sparc/kernel/tsb.S
+++ b/arch/sparc/kernel/tsb.S
@@ -375,6 +375,7 @@
 	 * %o1:	TSB base config pointer
 	 * %o2:	TSB huge config pointer, or NULL if none
 	 * %o3:	Hypervisor TSB descriptor physical address
+	 * %o4: Secondary context to load, if non-zero
 	 *
 	 * We have to run this whole thing with interrupts
 	 * disabled so that the current cpu doesn't change
@@ -387,6 +388,17 @@
 	rdpr	%pstate, %g1
 	wrpr	%g1, PSTATE_IE, %pstate
 
+	brz,pn	%o4, 1f
+	 mov	SECONDARY_CONTEXT, %o5
+
+661:	stxa	%o4, [%o5] ASI_DMMU
+	.section .sun4v_1insn_patch, "ax"
+	.word	661b
+	stxa	%o4, [%o5] ASI_MMU
+	.previous
+	flush	%g6
+
+1:
 	TRAP_LOAD_TRAP_BLOCK(%g2, %g3)
 
 	stx	%o0, [%g2 + TRAP_PER_CPU_PGD_PADDR]
diff --git a/arch/sparc/lib/U3memcpy.S b/arch/sparc/lib/U3memcpy.S
index 54f9870..5a8cb37 100644
--- a/arch/sparc/lib/U3memcpy.S
+++ b/arch/sparc/lib/U3memcpy.S
@@ -145,13 +145,13 @@
 ENTRY(U3_retl_o2_and_7_plus_GS)
 	and	%o2, 7, %o2
 	retl
-	 add	%o2, GLOBAL_SPARE, %o2
+	 add	%o2, GLOBAL_SPARE, %o0
 ENDPROC(U3_retl_o2_and_7_plus_GS)
 ENTRY(U3_retl_o2_and_7_plus_GS_plus_8)
 	add	GLOBAL_SPARE, 8, GLOBAL_SPARE
 	and	%o2, 7, %o2
 	retl
-	 add	%o2, GLOBAL_SPARE, %o2
+	 add	%o2, GLOBAL_SPARE, %o0
 ENDPROC(U3_retl_o2_and_7_plus_GS_plus_8)
 #endif
 
diff --git a/arch/sparc/power/hibernate.c b/arch/sparc/power/hibernate.c
index 17bd2e1..df707a8 100644
--- a/arch/sparc/power/hibernate.c
+++ b/arch/sparc/power/hibernate.c
@@ -35,6 +35,5 @@
 {
 	struct mm_struct *mm = current->active_mm;
 
-	load_secondary_context(mm);
-	tsb_context_switch(mm);
+	tsb_context_switch_ctx(mm, CTX_HWBITS(mm->context));
 }
diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c
index cc3bd58..9e240fc 100644
--- a/arch/x86/boot/string.c
+++ b/arch/x86/boot/string.c
@@ -14,6 +14,7 @@
 
 #include <linux/types.h>
 #include "ctype.h"
+#include "string.h"
 
 int memcmp(const void *s1, const void *s2, size_t len)
 {
diff --git a/arch/x86/boot/string.h b/arch/x86/boot/string.h
index 725e820..113588d 100644
--- a/arch/x86/boot/string.h
+++ b/arch/x86/boot/string.h
@@ -18,4 +18,13 @@
 #define memset(d,c,l) __builtin_memset(d,c,l)
 #define memcmp	__builtin_memcmp
 
+extern int strcmp(const char *str1, const char *str2);
+extern int strncmp(const char *cs, const char *ct, size_t count);
+extern size_t strlen(const char *s);
+extern char *strstr(const char *s1, const char *s2);
+extern size_t strnlen(const char *s, size_t maxlen);
+extern unsigned int atou(const char *s);
+extern unsigned long long simple_strtoull(const char *cp, char **endp,
+					  unsigned int base);
+
 #endif /* BOOT_STRING_H */
diff --git a/arch/x86/crypto/sha1-mb/sha1_mb_mgr_flush_avx2.S b/arch/x86/crypto/sha1-mb/sha1_mb_mgr_flush_avx2.S
index 96df6a3..a2ae689 100644
--- a/arch/x86/crypto/sha1-mb/sha1_mb_mgr_flush_avx2.S
+++ b/arch/x86/crypto/sha1-mb/sha1_mb_mgr_flush_avx2.S
@@ -157,8 +157,8 @@
 .endr
 
 	# Find min length
-	vmovdqa _lens+0*16(state), %xmm0
-	vmovdqa _lens+1*16(state), %xmm1
+	vmovdqu _lens+0*16(state), %xmm0
+	vmovdqu _lens+1*16(state), %xmm1
 
 	vpminud %xmm1, %xmm0, %xmm2     # xmm2 has {D,C,B,A}
 	vpalignr $8, %xmm2, %xmm3, %xmm3   # xmm3 has {x,x,D,C}
@@ -178,8 +178,8 @@
 	vpsubd  %xmm2, %xmm0, %xmm0
 	vpsubd  %xmm2, %xmm1, %xmm1
 
-	vmovdqa %xmm0, _lens+0*16(state)
-	vmovdqa %xmm1, _lens+1*16(state)
+	vmovdqu %xmm0, _lens+0*16(state)
+	vmovdqu %xmm1, _lens+1*16(state)
 
 	# "state" and "args" are the same address, arg1
 	# len is arg2
@@ -235,8 +235,8 @@
 	jc      .return_null
 
 	# Find min length
-	vmovdqa _lens(state), %xmm0
-	vmovdqa _lens+1*16(state), %xmm1
+	vmovdqu _lens(state), %xmm0
+	vmovdqu _lens+1*16(state), %xmm1
 
 	vpminud %xmm1, %xmm0, %xmm2        # xmm2 has {D,C,B,A}
 	vpalignr $8, %xmm2, %xmm3, %xmm3   # xmm3 has {x,x,D,C}
diff --git a/arch/x86/crypto/sha1_avx2_x86_64_asm.S b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
index 1cd792d..1eab79c 100644
--- a/arch/x86/crypto/sha1_avx2_x86_64_asm.S
+++ b/arch/x86/crypto/sha1_avx2_x86_64_asm.S
@@ -117,11 +117,10 @@
 	.set T1, REG_T1
 .endm
 
-#define K_BASE		%r8
 #define HASH_PTR	%r9
+#define BLOCKS_CTR	%r8
 #define BUFFER_PTR	%r10
 #define BUFFER_PTR2	%r13
-#define BUFFER_END	%r11
 
 #define PRECALC_BUF	%r14
 #define WK_BUF		%r15
@@ -205,14 +204,14 @@
 		 * blended AVX2 and ALU instruction scheduling
 		 * 1 vector iteration per 8 rounds
 		 */
-		vmovdqu ((i * 2) + PRECALC_OFFSET)(BUFFER_PTR), W_TMP
+		vmovdqu (i * 2)(BUFFER_PTR), W_TMP
 	.elseif ((i & 7) == 1)
-		vinsertf128 $1, (((i-1) * 2)+PRECALC_OFFSET)(BUFFER_PTR2),\
+		vinsertf128 $1, ((i-1) * 2)(BUFFER_PTR2),\
 			 WY_TMP, WY_TMP
 	.elseif ((i & 7) == 2)
 		vpshufb YMM_SHUFB_BSWAP, WY_TMP, WY
 	.elseif ((i & 7) == 4)
-		vpaddd  K_XMM(K_BASE), WY, WY_TMP
+		vpaddd  K_XMM + K_XMM_AR(%rip), WY, WY_TMP
 	.elseif ((i & 7) == 7)
 		vmovdqu  WY_TMP, PRECALC_WK(i&~7)
 
@@ -255,7 +254,7 @@
 		vpxor	WY, WY_TMP, WY_TMP
 	.elseif ((i & 7) == 7)
 		vpxor	WY_TMP2, WY_TMP, WY
-		vpaddd	K_XMM(K_BASE), WY, WY_TMP
+		vpaddd  K_XMM + K_XMM_AR(%rip), WY, WY_TMP
 		vmovdqu	WY_TMP, PRECALC_WK(i&~7)
 
 		PRECALC_ROTATE_WY
@@ -291,7 +290,7 @@
 		vpsrld	$30, WY, WY
 		vpor	WY, WY_TMP, WY
 	.elseif ((i & 7) == 7)
-		vpaddd	K_XMM(K_BASE), WY, WY_TMP
+		vpaddd  K_XMM + K_XMM_AR(%rip), WY, WY_TMP
 		vmovdqu	WY_TMP, PRECALC_WK(i&~7)
 
 		PRECALC_ROTATE_WY
@@ -446,6 +445,16 @@
 
 .endm
 
+/* Add constant only if (%2 > %3) condition met (uses RTA as temp)
+ * %1 + %2 >= %3 ? %4 : 0
+ */
+.macro ADD_IF_GE a, b, c, d
+	mov     \a, RTA
+	add     $\d, RTA
+	cmp     $\c, \b
+	cmovge  RTA, \a
+.endm
+
 /*
  * macro implements 80 rounds of SHA-1, for multiple blocks with s/w pipelining
  */
@@ -463,13 +472,16 @@
 	lea	(2*4*80+32)(%rsp), WK_BUF
 
 	# Precalc WK for first 2 blocks
-	PRECALC_OFFSET = 0
+	ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 2, 64
 	.set i, 0
 	.rept    160
 		PRECALC i
 		.set i, i + 1
 	.endr
-	PRECALC_OFFSET = 128
+
+	/* Go to next block if needed */
+	ADD_IF_GE BUFFER_PTR, BLOCKS_CTR, 3, 128
+	ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 4, 128
 	xchg	WK_BUF, PRECALC_BUF
 
 	.align 32
@@ -479,8 +491,8 @@
 	 * we use K_BASE value as a signal of a last block,
 	 * it is set below by: cmovae BUFFER_PTR, K_BASE
 	 */
-	cmp	K_BASE, BUFFER_PTR
-	jne	_begin
+	test BLOCKS_CTR, BLOCKS_CTR
+	jnz _begin
 	.align 32
 	jmp	_end
 	.align 32
@@ -512,10 +524,10 @@
 		.set j, j+2
 	.endr
 
-	add	$(2*64), BUFFER_PTR       /* move to next odd-64-byte block */
-	cmp	BUFFER_END, BUFFER_PTR    /* is current block the last one? */
-	cmovae	K_BASE, BUFFER_PTR	/* signal the last iteration smartly */
-
+	/* Update Counter */
+	sub $1, BLOCKS_CTR
+	/* Move to the next block only if needed*/
+	ADD_IF_GE BUFFER_PTR, BLOCKS_CTR, 4, 128
 	/*
 	 * rounds
 	 * 60,62,64,66,68
@@ -532,8 +544,8 @@
 	UPDATE_HASH	12(HASH_PTR), D
 	UPDATE_HASH	16(HASH_PTR), E
 
-	cmp	K_BASE, BUFFER_PTR	/* is current block the last one? */
-	je	_loop
+	test	BLOCKS_CTR, BLOCKS_CTR
+	jz	_loop
 
 	mov	TB, B
 
@@ -575,10 +587,10 @@
 		.set j, j+2
 	.endr
 
-	add	$(2*64), BUFFER_PTR2      /* move to next even-64-byte block */
-
-	cmp	BUFFER_END, BUFFER_PTR2   /* is current block the last one */
-	cmovae	K_BASE, BUFFER_PTR       /* signal the last iteration smartly */
+	/* update counter */
+	sub     $1, BLOCKS_CTR
+	/* Move to the next block only if needed*/
+	ADD_IF_GE BUFFER_PTR2, BLOCKS_CTR, 4, 128
 
 	jmp	_loop3
 _loop3:
@@ -641,19 +653,12 @@
 
 	avx2_zeroupper
 
-	lea	K_XMM_AR(%rip), K_BASE
-
+	/* Setup initial values */
 	mov	CTX, HASH_PTR
 	mov	BUF, BUFFER_PTR
-	lea	64(BUF), BUFFER_PTR2
 
-	shl	$6, CNT			/* mul by 64 */
-	add	BUF, CNT
-	add	$64, CNT
-	mov	CNT, BUFFER_END
-
-	cmp	BUFFER_END, BUFFER_PTR2
-	cmovae	K_BASE, BUFFER_PTR2
+	mov	BUF, BUFFER_PTR2
+	mov	CNT, BLOCKS_CTR
 
 	xmm_mov	BSWAP_SHUFB_CTL(%rip), YMM_SHUFB_BSWAP
 
diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c
index f960a04..fc61739 100644
--- a/arch/x86/crypto/sha1_ssse3_glue.c
+++ b/arch/x86/crypto/sha1_ssse3_glue.c
@@ -201,7 +201,7 @@
 
 static bool avx2_usable(void)
 {
-	if (false && avx_usable() && boot_cpu_has(X86_FEATURE_AVX2)
+	if (avx_usable() && boot_cpu_has(X86_FEATURE_AVX2)
 		&& boot_cpu_has(X86_FEATURE_BMI1)
 		&& boot_cpu_has(X86_FEATURE_BMI2))
 		return true;
diff --git a/arch/x86/crypto/sha256-mb/sha256_mb_mgr_flush_avx2.S b/arch/x86/crypto/sha256-mb/sha256_mb_mgr_flush_avx2.S
index a78a069..ec9bee6 100644
--- a/arch/x86/crypto/sha256-mb/sha256_mb_mgr_flush_avx2.S
+++ b/arch/x86/crypto/sha256-mb/sha256_mb_mgr_flush_avx2.S
@@ -155,8 +155,8 @@
 .endr
 
 	# Find min length
-	vmovdqa _lens+0*16(state), %xmm0
-	vmovdqa _lens+1*16(state), %xmm1
+	vmovdqu _lens+0*16(state), %xmm0
+	vmovdqu _lens+1*16(state), %xmm1
 
 	vpminud %xmm1, %xmm0, %xmm2		# xmm2 has {D,C,B,A}
 	vpalignr $8, %xmm2, %xmm3, %xmm3	# xmm3 has {x,x,D,C}
@@ -176,8 +176,8 @@
 	vpsubd	%xmm2, %xmm0, %xmm0
 	vpsubd	%xmm2, %xmm1, %xmm1
 
-	vmovdqa	%xmm0, _lens+0*16(state)
-	vmovdqa	%xmm1, _lens+1*16(state)
+	vmovdqu	%xmm0, _lens+0*16(state)
+	vmovdqu	%xmm1, _lens+1*16(state)
 
 	# "state" and "args" are the same address, arg1
 	# len is arg2
@@ -234,8 +234,8 @@
 	jc	.return_null
 
 	# Find min length
-	vmovdqa	_lens(state), %xmm0
-	vmovdqa	_lens+1*16(state), %xmm1
+	vmovdqu	_lens(state), %xmm0
+	vmovdqu	_lens+1*16(state), %xmm1
 
 	vpminud	%xmm1, %xmm0, %xmm2		# xmm2 has {D,C,B,A}
 	vpalignr $8, %xmm2, %xmm3, %xmm3	# xmm3 has {x,x,D,C}
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index ef766a3..e7b0e7f 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -1215,6 +1215,8 @@
 	 * other IST entries.
 	 */
 
+	ASM_CLAC
+
 	/* Use %rdx as our temp variable throughout */
 	pushq	%rdx
 
diff --git a/arch/x86/events/intel/rapl.c b/arch/x86/events/intel/rapl.c
index 970c1de..4c1b7ea 100644
--- a/arch/x86/events/intel/rapl.c
+++ b/arch/x86/events/intel/rapl.c
@@ -161,7 +161,13 @@
 
 static inline struct rapl_pmu *cpu_to_rapl_pmu(unsigned int cpu)
 {
-	return rapl_pmus->pmus[topology_logical_package_id(cpu)];
+	unsigned int pkgid = topology_logical_package_id(cpu);
+
+	/*
+	 * The unsigned check also catches the '-1' return value for non
+	 * existent mappings in the topology map.
+	 */
+	return pkgid < rapl_pmus->maxpkg ? rapl_pmus->pmus[pkgid] : NULL;
 }
 
 static inline u64 rapl_read_counter(struct perf_event *event)
@@ -402,6 +408,8 @@
 
 	/* must be done before validate_group */
 	pmu = cpu_to_rapl_pmu(event->cpu);
+	if (!pmu)
+		return -EINVAL;
 	event->cpu = pmu->cpu;
 	event->pmu_private = pmu;
 	event->hw.event_base = msr;
@@ -585,6 +593,19 @@
 	struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu);
 	int target;
 
+	if (!pmu) {
+		pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
+		if (!pmu)
+			return -ENOMEM;
+
+		raw_spin_lock_init(&pmu->lock);
+		INIT_LIST_HEAD(&pmu->active_list);
+		pmu->pmu = &rapl_pmus->pmu;
+		pmu->timer_interval = ms_to_ktime(rapl_timer_ms);
+		rapl_hrtimer_init(pmu);
+
+		rapl_pmus->pmus[topology_logical_package_id(cpu)] = pmu;
+	}
 	/*
 	 * Check if there is an online cpu in the package which collects rapl
 	 * events already.
@@ -598,27 +619,6 @@
 	return 0;
 }
 
-static int rapl_cpu_prepare(unsigned int cpu)
-{
-	struct rapl_pmu *pmu = cpu_to_rapl_pmu(cpu);
-
-	if (pmu)
-		return 0;
-
-	pmu = kzalloc_node(sizeof(*pmu), GFP_KERNEL, cpu_to_node(cpu));
-	if (!pmu)
-		return -ENOMEM;
-
-	raw_spin_lock_init(&pmu->lock);
-	INIT_LIST_HEAD(&pmu->active_list);
-	pmu->pmu = &rapl_pmus->pmu;
-	pmu->timer_interval = ms_to_ktime(rapl_timer_ms);
-	pmu->cpu = -1;
-	rapl_hrtimer_init(pmu);
-	rapl_pmus->pmus[topology_logical_package_id(cpu)] = pmu;
-	return 0;
-}
-
 static int rapl_check_hw_unit(bool apply_quirk)
 {
 	u64 msr_rapl_power_unit_bits;
@@ -804,28 +804,21 @@
 	 * Install callbacks. Core will call them for each online cpu.
 	 */
 
-	ret = cpuhp_setup_state(CPUHP_PERF_X86_RAPL_PREP, "PERF_X86_RAPL_PREP",
-				rapl_cpu_prepare, NULL);
-	if (ret)
-		goto out;
-
 	ret = cpuhp_setup_state(CPUHP_AP_PERF_X86_RAPL_ONLINE,
 				"AP_PERF_X86_RAPL_ONLINE",
 				rapl_cpu_online, rapl_cpu_offline);
 	if (ret)
-		goto out1;
+		goto out;
 
 	ret = perf_pmu_register(&rapl_pmus->pmu, "power", -1);
 	if (ret)
-		goto out2;
+		goto out1;
 
 	rapl_advertise();
 	return 0;
 
-out2:
-	cpuhp_remove_state(CPUHP_AP_PERF_X86_RAPL_ONLINE);
 out1:
-	cpuhp_remove_state(CPUHP_PERF_X86_RAPL_PREP);
+	cpuhp_remove_state(CPUHP_AP_PERF_X86_RAPL_ONLINE);
 out:
 	pr_warn("Initialization failed (%d), disabled\n", ret);
 	cleanup_rapl_pmus();
@@ -836,7 +829,6 @@
 static void __exit intel_rapl_exit(void)
 {
 	cpuhp_remove_state_nocalls(CPUHP_AP_PERF_X86_RAPL_ONLINE);
-	cpuhp_remove_state_nocalls(CPUHP_PERF_X86_RAPL_PREP);
 	perf_pmu_unregister(&rapl_pmus->pmu);
 	cleanup_rapl_pmus();
 }
diff --git a/arch/x86/include/asm/alternative-asm.h b/arch/x86/include/asm/alternative-asm.h
index e7636ba..6c98821 100644
--- a/arch/x86/include/asm/alternative-asm.h
+++ b/arch/x86/include/asm/alternative-asm.h
@@ -62,8 +62,10 @@
 #define new_len2		145f-144f
 
 /*
- * max without conditionals. Idea adapted from:
+ * gas compatible max based on the idea from:
  * http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
+ *
+ * The additional "-" is needed because gas uses a "true" value of -1.
  */
 #define alt_max_short(a, b)	((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
 
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index 1b02038..d4aea31 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -103,12 +103,12 @@
 	alt_end_marker ":\n"
 
 /*
- * max without conditionals. Idea adapted from:
+ * gas compatible max based on the idea from:
  * http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
  *
- * The additional "-" is needed because gas works with s32s.
+ * The additional "-" is needed because gas uses a "true" value of -1.
  */
-#define alt_max_short(a, b)	"((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") - (" b ")))))"
+#define alt_max_short(a, b)	"((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))"
 
 /*
  * Pad the second replacement alternative with additional NOPs if it is
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index c152db2..7bcd138 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -204,6 +204,7 @@
 
 #define ELF_CORE_COPY_REGS(pr_reg, regs)			\
 do {								\
+	unsigned long base;					\
 	unsigned v;						\
 	(pr_reg)[0] = (regs)->r15;				\
 	(pr_reg)[1] = (regs)->r14;				\
@@ -226,8 +227,8 @@
 	(pr_reg)[18] = (regs)->flags;				\
 	(pr_reg)[19] = (regs)->sp;				\
 	(pr_reg)[20] = (regs)->ss;				\
-	(pr_reg)[21] = current->thread.fsbase;			\
-	(pr_reg)[22] = current->thread.gsbase;			\
+	rdmsrl(MSR_FS_BASE, base); (pr_reg)[21] = base;		\
+	rdmsrl(MSR_KERNEL_GS_BASE, base); (pr_reg)[22] = base;	\
 	asm("movl %%ds,%0" : "=r" (v)); (pr_reg)[23] = v;	\
 	asm("movl %%es,%0" : "=r" (v)); (pr_reg)[24] = v;	\
 	asm("movl %%fs,%0" : "=r" (v)); (pr_reg)[25] = v;	\
@@ -247,11 +248,11 @@
 
 /*
  * This is the base location for PIE (ET_DYN with INTERP) loads. On
- * 64-bit, this is raised to 4GB to leave the entire 32-bit address
+ * 64-bit, this is above 4GB to leave the entire 32-bit address
  * space open for things that want to use the area for 32-bit pointers.
  */
 #define ELF_ET_DYN_BASE		(mmap_is_ia32() ? 0x000400000UL : \
-						  0x100000000UL)
+						  (TASK_SIZE / 3 * 2))
 
 /* This yields a mask that user programs can use to figure out what
    instruction set this CPU supports.  This could be done in user space,
diff --git a/arch/x86/include/asm/io.h b/arch/x86/include/asm/io.h
index d34bd37..6c50201 100644
--- a/arch/x86/include/asm/io.h
+++ b/arch/x86/include/asm/io.h
@@ -304,13 +304,13 @@
 static inline void outs##bwl(int port, const void *addr, unsigned long count) \
 {									\
 	asm volatile("rep; outs" #bwl					\
-		     : "+S"(addr), "+c"(count) : "d"(port));		\
+		     : "+S"(addr), "+c"(count) : "d"(port) : "memory");	\
 }									\
 									\
 static inline void ins##bwl(int port, void *addr, unsigned long count)	\
 {									\
 	asm volatile("rep; ins" #bwl					\
-		     : "+D"(addr), "+c"(count) : "d"(port));		\
+		     : "+D"(addr), "+c"(count) : "d"(port) : "memory");	\
 }
 
 BUILDIO(b, b, char)
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index 19d14ac..fc3c7e4 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -296,6 +296,7 @@
 
 	bool perm_ok; /* do not check permissions if true */
 	bool ud;	/* inject an #UD if host doesn't support insn */
+	bool tf;	/* TF value before instruction (after for syscall/sysret) */
 
 	bool have_exception;
 	struct x86_exception exception;
diff --git a/arch/x86/include/asm/mmu_context.h b/arch/x86/include/asm/mmu_context.h
index 8e0a9fe..f9dd224 100644
--- a/arch/x86/include/asm/mmu_context.h
+++ b/arch/x86/include/asm/mmu_context.h
@@ -116,9 +116,7 @@
 		mm->context.execute_only_pkey = -1;
 	}
 	#endif
-	init_new_context_ldt(tsk, mm);
-
-	return 0;
+	return init_new_context_ldt(tsk, mm);
 }
 static inline void destroy_context(struct mm_struct *mm)
 {
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index a300aa1..dead0f3 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -68,6 +68,12 @@
 	__chk_range_not_ok((unsigned long __force)(addr), size, limit); \
 })
 
+#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
+# define WARN_ON_IN_IRQ()	WARN_ON_ONCE(!in_task())
+#else
+# define WARN_ON_IN_IRQ()
+#endif
+
 /**
  * access_ok: - Checks if a user space pointer is valid
  * @type: Type of access: %VERIFY_READ or %VERIFY_WRITE.  Note that
@@ -88,8 +94,11 @@
  * checks that the pointer is in the user space range - after calling
  * this function, memory access functions may still return -EFAULT.
  */
-#define access_ok(type, addr, size) \
-	likely(!__range_not_ok(addr, size, user_addr_max()))
+#define access_ok(type, addr, size)					\
+({									\
+	WARN_ON_IN_IRQ();						\
+	likely(!__range_not_ok(addr, size, user_addr_max()));		\
+})
 
 /*
  * These are the main single-value transfer routines.  They automatically
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index d3e0d04..b89bef9 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -176,10 +176,15 @@
 		return -EINVAL;
 	}
 
+	if (!enabled) {
+		++disabled_cpus;
+		return -EINVAL;
+	}
+
 	if (boot_cpu_physical_apicid != -1U)
 		ver = boot_cpu_apic_version;
 
-	cpu = __generic_processor_info(id, ver, enabled);
+	cpu = generic_processor_info(id, ver);
 	if (cpu >= 0)
 		early_per_cpu(x86_cpu_to_acpiid, cpu) = acpiid;
 
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index f223491..e2ead34 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2070,7 +2070,7 @@
 	return nr_logical_cpuids++;
 }
 
-int __generic_processor_info(int apicid, int version, bool enabled)
+int generic_processor_info(int apicid, int version)
 {
 	int cpu, max = nr_cpu_ids;
 	bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,
@@ -2128,11 +2128,9 @@
 	if (num_processors >= nr_cpu_ids) {
 		int thiscpu = max + disabled_cpus;
 
-		if (enabled) {
-			pr_warning("APIC: NR_CPUS/possible_cpus limit of %i "
-				   "reached. Processor %d/0x%x ignored.\n",
-				   max, thiscpu, apicid);
-		}
+		pr_warning("APIC: NR_CPUS/possible_cpus limit of %i "
+			   "reached. Processor %d/0x%x ignored.\n",
+			   max, thiscpu, apicid);
 
 		disabled_cpus++;
 		return -EINVAL;
@@ -2184,23 +2182,13 @@
 		apic->x86_32_early_logical_apicid(cpu);
 #endif
 	set_cpu_possible(cpu, true);
-
-	if (enabled) {
-		num_processors++;
-		physid_set(apicid, phys_cpu_present_map);
-		set_cpu_present(cpu, true);
-	} else {
-		disabled_cpus++;
-	}
+	physid_set(apicid, phys_cpu_present_map);
+	set_cpu_present(cpu, true);
+	num_processors++;
 
 	return cpu;
 }
 
-int generic_processor_info(int apicid, int version)
-{
-	return __generic_processor_info(apicid, version, true);
-}
-
 int hard_smp_processor_id(void)
 {
 	return read_apic_id();
diff --git a/arch/x86/kernel/cpu/microcode/intel.c b/arch/x86/kernel/cpu/microcode/intel.c
index cdc0dea..13dbcc0 100644
--- a/arch/x86/kernel/cpu/microcode/intel.c
+++ b/arch/x86/kernel/cpu/microcode/intel.c
@@ -34,6 +34,7 @@
 #include <linux/mm.h>
 
 #include <asm/microcode_intel.h>
+#include <asm/intel-family.h>
 #include <asm/processor.h>
 #include <asm/tlbflush.h>
 #include <asm/setup.h>
@@ -1046,6 +1047,18 @@
 	return 0;
 }
 
+static bool is_blacklisted(unsigned int cpu)
+{
+	struct cpuinfo_x86 *c = &cpu_data(cpu);
+
+	if (c->x86 == 6 && c->x86_model == INTEL_FAM6_BROADWELL_X) {
+		pr_err_once("late loading on model 79 is disabled.\n");
+		return true;
+	}
+
+	return false;
+}
+
 static enum ucode_state request_microcode_fw(int cpu, struct device *device,
 					     bool refresh_fw)
 {
@@ -1054,6 +1067,9 @@
 	const struct firmware *firmware;
 	enum ucode_state ret;
 
+	if (is_blacklisted(cpu))
+		return UCODE_NFOUND;
+
 	sprintf(name, "intel-ucode/%02x-%02x-%02x",
 		c->x86, c->x86_model, c->x86_mask);
 
@@ -1078,6 +1094,9 @@
 static enum ucode_state
 request_microcode_user(int cpu, const void __user *buf, size_t size)
 {
+	if (is_blacklisted(cpu))
+		return UCODE_NFOUND;
+
 	return generic_load_microcode(cpu, (void *)buf, size, &get_ucode_user);
 }
 
diff --git a/arch/x86/kernel/fpu/regset.c b/arch/x86/kernel/fpu/regset.c
index c114b13..7052d9a 100644
--- a/arch/x86/kernel/fpu/regset.c
+++ b/arch/x86/kernel/fpu/regset.c
@@ -130,11 +130,16 @@
 
 	fpu__activate_fpstate_write(fpu);
 
-	if (boot_cpu_has(X86_FEATURE_XSAVES))
+	if (boot_cpu_has(X86_FEATURE_XSAVES)) {
 		ret = copyin_to_xsaves(kbuf, ubuf, xsave);
-	else
+	} else {
 		ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, xsave, 0, -1);
 
+		/* xcomp_bv must be 0 when using uncompacted format */
+		if (!ret && xsave->header.xcomp_bv)
+			ret = -EINVAL;
+	}
+
 	/*
 	 * In case of failure, mark all states as init:
 	 */
diff --git a/arch/x86/kernel/fpu/signal.c b/arch/x86/kernel/fpu/signal.c
index a184c21..3ec0d2d 100644
--- a/arch/x86/kernel/fpu/signal.c
+++ b/arch/x86/kernel/fpu/signal.c
@@ -329,6 +329,10 @@
 		} else {
 			err = __copy_from_user(&fpu->state.xsave,
 					       buf_fx, state_size);
+
+			/* xcomp_bv must be 0 when using uncompacted format */
+			if (!err && state_size > offsetof(struct xregs_state, header) && fpu->state.xsave.header.xcomp_bv)
+				err = -EINVAL;
 		}
 
 		if (err || __copy_from_user(&env, buf, sizeof(env))) {
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 9cf697c..77f17cb 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -141,7 +141,8 @@
 
 	n.token = token;
 	n.cpu = smp_processor_id();
-	n.halted = is_idle_task(current) || preempt_count() > 1;
+	n.halted = is_idle_task(current) || preempt_count() > 1 ||
+		   rcu_preempt_depth();
 	init_swait_queue_head(&n.wq);
 	hlist_add_head(&n.link, &b->list);
 	raw_spin_unlock(&b->lock);
@@ -152,6 +153,8 @@
 		if (hlist_unhashed(&n.link))
 			break;
 
+		rcu_irq_exit();
+
 		if (!n.halted) {
 			local_irq_enable();
 			schedule();
@@ -160,11 +163,11 @@
 			/*
 			 * We cannot reschedule. So halt.
 			 */
-			rcu_irq_exit();
 			native_safe_halt();
 			local_irq_disable();
-			rcu_irq_enter();
 		}
+
+		rcu_irq_enter();
 	}
 	if (!n.halted)
 		finish_swait(&n.wq, &wait);
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index b3760b3..0887d2a 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -136,6 +136,123 @@
 	}
 }
 
+enum which_selector {
+	FS,
+	GS
+};
+
+/*
+ * Saves the FS or GS base for an outgoing thread if FSGSBASE extensions are
+ * not available.  The goal is to be reasonably fast on non-FSGSBASE systems.
+ * It's forcibly inlined because it'll generate better code and this function
+ * is hot.
+ */
+static __always_inline void save_base_legacy(struct task_struct *prev_p,
+					     unsigned short selector,
+					     enum which_selector which)
+{
+	if (likely(selector == 0)) {
+		/*
+		 * On Intel (without X86_BUG_NULL_SEG), the segment base could
+		 * be the pre-existing saved base or it could be zero.  On AMD
+		 * (with X86_BUG_NULL_SEG), the segment base could be almost
+		 * anything.
+		 *
+		 * This branch is very hot (it's hit twice on almost every
+		 * context switch between 64-bit programs), and avoiding
+		 * the RDMSR helps a lot, so we just assume that whatever
+		 * value is already saved is correct.  This matches historical
+		 * Linux behavior, so it won't break existing applications.
+		 *
+		 * To avoid leaking state, on non-X86_BUG_NULL_SEG CPUs, if we
+		 * report that the base is zero, it needs to actually be zero:
+		 * see the corresponding logic in load_seg_legacy.
+		 */
+	} else {
+		/*
+		 * If the selector is 1, 2, or 3, then the base is zero on
+		 * !X86_BUG_NULL_SEG CPUs and could be anything on
+		 * X86_BUG_NULL_SEG CPUs.  In the latter case, Linux
+		 * has never attempted to preserve the base across context
+		 * switches.
+		 *
+		 * If selector > 3, then it refers to a real segment, and
+		 * saving the base isn't necessary.
+		 */
+		if (which == FS)
+			prev_p->thread.fsbase = 0;
+		else
+			prev_p->thread.gsbase = 0;
+	}
+}
+
+static __always_inline void save_fsgs(struct task_struct *task)
+{
+	savesegment(fs, task->thread.fsindex);
+	savesegment(gs, task->thread.gsindex);
+	save_base_legacy(task, task->thread.fsindex, FS);
+	save_base_legacy(task, task->thread.gsindex, GS);
+}
+
+static __always_inline void loadseg(enum which_selector which,
+				    unsigned short sel)
+{
+	if (which == FS)
+		loadsegment(fs, sel);
+	else
+		load_gs_index(sel);
+}
+
+static __always_inline void load_seg_legacy(unsigned short prev_index,
+					    unsigned long prev_base,
+					    unsigned short next_index,
+					    unsigned long next_base,
+					    enum which_selector which)
+{
+	if (likely(next_index <= 3)) {
+		/*
+		 * The next task is using 64-bit TLS, is not using this
+		 * segment at all, or is having fun with arcane CPU features.
+		 */
+		if (next_base == 0) {
+			/*
+			 * Nasty case: on AMD CPUs, we need to forcibly zero
+			 * the base.
+			 */
+			if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
+				loadseg(which, __USER_DS);
+				loadseg(which, next_index);
+			} else {
+				/*
+				 * We could try to exhaustively detect cases
+				 * under which we can skip the segment load,
+				 * but there's really only one case that matters
+				 * for performance: if both the previous and
+				 * next states are fully zeroed, we can skip
+				 * the load.
+				 *
+				 * (This assumes that prev_base == 0 has no
+				 * false positives.  This is the case on
+				 * Intel-style CPUs.)
+				 */
+				if (likely(prev_index | next_index | prev_base))
+					loadseg(which, next_index);
+			}
+		} else {
+			if (prev_index != next_index)
+				loadseg(which, next_index);
+			wrmsrl(which == FS ? MSR_FS_BASE : MSR_KERNEL_GS_BASE,
+			       next_base);
+		}
+	} else {
+		/*
+		 * The next task is using a real segment.  Loading the selector
+		 * is sufficient.
+		 */
+		loadseg(which, next_index);
+	}
+}
+
 int copy_thread_tls(unsigned long clone_flags, unsigned long sp,
 		unsigned long arg, struct task_struct *p, unsigned long tls)
 {
@@ -216,10 +333,19 @@
 		    unsigned long new_sp,
 		    unsigned int _cs, unsigned int _ss, unsigned int _ds)
 {
+	WARN_ON_ONCE(regs != current_pt_regs());
+
+	if (static_cpu_has(X86_BUG_NULL_SEG)) {
+		/* Loading zero below won't clear the base. */
+		loadsegment(fs, __USER_DS);
+		load_gs_index(__USER_DS);
+	}
+
 	loadsegment(fs, 0);
 	loadsegment(es, _ds);
 	loadsegment(ds, _ds);
 	load_gs_index(0);
+
 	regs->ip		= new_ip;
 	regs->sp		= new_sp;
 	regs->cs		= _cs;
@@ -264,7 +390,6 @@
 	struct fpu *next_fpu = &next->fpu;
 	int cpu = smp_processor_id();
 	struct tss_struct *tss = &per_cpu(cpu_tss, cpu);
-	unsigned prev_fsindex, prev_gsindex;
 	fpu_switch_t fpu_switch;
 
 	fpu_switch = switch_fpu_prepare(prev_fpu, next_fpu, cpu);
@@ -274,8 +399,7 @@
 	 *
 	 * (e.g. xen_load_tls())
 	 */
-	savesegment(fs, prev_fsindex);
-	savesegment(gs, prev_gsindex);
+	save_fsgs(prev_p);
 
 	/*
 	 * Load TLS before restoring any segments so that segment loads
@@ -314,108 +438,10 @@
 	if (unlikely(next->ds | prev->ds))
 		loadsegment(ds, next->ds);
 
-	/*
-	 * Switch FS and GS.
-	 *
-	 * These are even more complicated than DS and ES: they have
-	 * 64-bit bases are that controlled by arch_prctl.  The bases
-	 * don't necessarily match the selectors, as user code can do
-	 * any number of things to cause them to be inconsistent.
-	 *
-	 * We don't promise to preserve the bases if the selectors are
-	 * nonzero.  We also don't promise to preserve the base if the
-	 * selector is zero and the base doesn't match whatever was
-	 * most recently passed to ARCH_SET_FS/GS.  (If/when the
-	 * FSGSBASE instructions are enabled, we'll need to offer
-	 * stronger guarantees.)
-	 *
-	 * As an invariant,
-	 * (fsbase != 0 && fsindex != 0) || (gsbase != 0 && gsindex != 0) is
-	 * impossible.
-	 */
-	if (next->fsindex) {
-		/* Loading a nonzero value into FS sets the index and base. */
-		loadsegment(fs, next->fsindex);
-	} else {
-		if (next->fsbase) {
-			/* Next index is zero but next base is nonzero. */
-			if (prev_fsindex)
-				loadsegment(fs, 0);
-			wrmsrl(MSR_FS_BASE, next->fsbase);
-		} else {
-			/* Next base and index are both zero. */
-			if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
-				/*
-				 * We don't know the previous base and can't
-				 * find out without RDMSR.  Forcibly clear it.
-				 */
-				loadsegment(fs, __USER_DS);
-				loadsegment(fs, 0);
-			} else {
-				/*
-				 * If the previous index is zero and ARCH_SET_FS
-				 * didn't change the base, then the base is
-				 * also zero and we don't need to do anything.
-				 */
-				if (prev->fsbase || prev_fsindex)
-					loadsegment(fs, 0);
-			}
-		}
-	}
-	/*
-	 * Save the old state and preserve the invariant.
-	 * NB: if prev_fsindex == 0, then we can't reliably learn the base
-	 * without RDMSR because Intel user code can zero it without telling
-	 * us and AMD user code can program any 32-bit value without telling
-	 * us.
-	 */
-	if (prev_fsindex)
-		prev->fsbase = 0;
-	prev->fsindex = prev_fsindex;
-
-	if (next->gsindex) {
-		/* Loading a nonzero value into GS sets the index and base. */
-		load_gs_index(next->gsindex);
-	} else {
-		if (next->gsbase) {
-			/* Next index is zero but next base is nonzero. */
-			if (prev_gsindex)
-				load_gs_index(0);
-			wrmsrl(MSR_KERNEL_GS_BASE, next->gsbase);
-		} else {
-			/* Next base and index are both zero. */
-			if (static_cpu_has_bug(X86_BUG_NULL_SEG)) {
-				/*
-				 * We don't know the previous base and can't
-				 * find out without RDMSR.  Forcibly clear it.
-				 *
-				 * This contains a pointless SWAPGS pair.
-				 * Fixing it would involve an explicit check
-				 * for Xen or a new pvop.
-				 */
-				load_gs_index(__USER_DS);
-				load_gs_index(0);
-			} else {
-				/*
-				 * If the previous index is zero and ARCH_SET_GS
-				 * didn't change the base, then the base is
-				 * also zero and we don't need to do anything.
-				 */
-				if (prev->gsbase || prev_gsindex)
-					load_gs_index(0);
-			}
-		}
-	}
-	/*
-	 * Save the old state and preserve the invariant.
-	 * NB: if prev_gsindex == 0, then we can't reliably learn the base
-	 * without RDMSR because Intel user code can zero it without telling
-	 * us and AMD user code can program any 32-bit value without telling
-	 * us.
-	 */
-	if (prev_gsindex)
-		prev->gsbase = 0;
-	prev->gsindex = prev_gsindex;
+	load_seg_legacy(prev->fsindex, prev->fsbase,
+			next->fsindex, next->fsbase, FS);
+	load_seg_legacy(prev->gsindex, prev->gsbase,
+			next->gsindex, next->gsbase, GS);
 
 	switch_fpu_finish(next_fpu, fpu_switch);
 
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 36171bc..9fe7b9e 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -181,6 +181,12 @@
 	smp_store_cpu_info(cpuid);
 
 	/*
+	 * The topology information must be up to date before
+	 * calibrate_delay() and notify_cpu_starting().
+	 */
+	set_cpu_sibling_map(raw_smp_processor_id());
+
+	/*
 	 * Get our bogomips.
 	 * Update loops_per_jiffy in cpu_data. Previous call to
 	 * smp_store_cpu_info() stored a value that is close but not as
@@ -190,11 +196,6 @@
 	cpu_data(cpuid).loops_per_jiffy = loops_per_jiffy;
 	pr_debug("Stack at about %p\n", &cpuid);
 
-	/*
-	 * This must be done before setting cpu_online_mask
-	 * or calling notify_cpu_starting.
-	 */
-	set_cpu_sibling_map(raw_smp_processor_id());
 	wmb();
 
 	notify_cpu_starting(cpuid);
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 6e57edf..44bf5cf 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -1382,12 +1382,10 @@
 unsigned long calibrate_delay_is_known(void)
 {
 	int sibling, cpu = smp_processor_id();
-	struct cpumask *mask = topology_core_cpumask(cpu);
+	int constant_tsc = cpu_has(&cpu_data(cpu), X86_FEATURE_CONSTANT_TSC);
+	const struct cpumask *mask = topology_core_cpumask(cpu);
 
-	if (!tsc_disabled && !cpu_has(&cpu_data(cpu), X86_FEATURE_CONSTANT_TSC))
-		return 0;
-
-	if (!mask)
+	if (tsc_disabled || !constant_tsc || !mask)
 		return 0;
 
 	sibling = cpumask_any_but(mask, cpu);
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 649d8f2..91af75e 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -456,7 +456,7 @@
 			entry->ecx &= kvm_cpuid_7_0_ecx_x86_features;
 			cpuid_mask(&entry->ecx, CPUID_7_ECX);
 			/* PKU is not yet implemented for shadow paging. */
-			if (!tdp_enabled)
+			if (!tdp_enabled || !boot_cpu_has(X86_FEATURE_OSPKE))
 				entry->ecx &= ~F(PKU);
 		} else {
 			entry->ebx = 0;
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index de36660..72b737b 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -2738,6 +2738,7 @@
 		ctxt->eflags &= ~(X86_EFLAGS_VM | X86_EFLAGS_IF);
 	}
 
+	ctxt->tf = (ctxt->eflags & X86_EFLAGS_TF) != 0;
 	return X86EMUL_CONTINUE;
 }
 
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 5f24127..d29c745 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -3649,19 +3649,19 @@
 				unsigned level, unsigned gpte)
 {
 	/*
-	 * PT_PAGE_TABLE_LEVEL always terminates.  The RHS has bit 7 set
-	 * iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
-	 * level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
-	 */
-	gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
-
-	/*
 	 * The RHS has bit 7 set iff level < mmu->last_nonleaf_level.
 	 * If it is clear, there are no large pages at this level, so clear
 	 * PT_PAGE_SIZE_MASK in gpte if that is the case.
 	 */
 	gpte &= level - mmu->last_nonleaf_level;
 
+	/*
+	 * PT_PAGE_TABLE_LEVEL always terminates.  The RHS has bit 7 set
+	 * iff level <= PT_PAGE_TABLE_LEVEL, which for our purpose means
+	 * level == PT_PAGE_TABLE_LEVEL; set PT_PAGE_SIZE_MASK in gpte then.
+	 */
+	gpte |= level - PT_PAGE_TABLE_LEVEL - 1;
+
 	return gpte & PT_PAGE_SIZE_MASK;
 }
 
@@ -4169,6 +4169,7 @@
 
 	update_permission_bitmask(vcpu, context, true);
 	update_pkru_bitmask(vcpu, context, true);
+	update_last_nonleaf_level(vcpu, context);
 	reset_rsvds_bits_mask_ept(vcpu, context, execonly);
 	reset_ept_shadow_zero_bits_mask(vcpu, context, execonly);
 }
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index a011054..3736390 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -324,10 +324,11 @@
 		--walker->level;
 
 		index = PT_INDEX(addr, walker->level);
-
 		table_gfn = gpte_to_gfn(pte);
 		offset    = index * sizeof(pt_element_t);
 		pte_gpa   = gfn_to_gpa(table_gfn) + offset;
+
+		BUG_ON(walker->level < 1);
 		walker->table_gfn[walker->level - 1] = table_gfn;
 		walker->pte_gpa[walker->level - 1] = pte_gpa;
 
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 3dc6d80..a8ae57a 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -2167,46 +2167,44 @@
 	struct pi_desc old, new;
 	unsigned int dest;
 
-	if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
-		!irq_remapping_cap(IRQ_POSTING_CAP)  ||
-		!kvm_vcpu_apicv_active(vcpu))
+	/*
+	 * In case of hot-plug or hot-unplug, we may have to undo
+	 * vmx_vcpu_pi_put even if there is no assigned device.  And we
+	 * always keep PI.NDST up to date for simplicity: it makes the
+	 * code easier, and CPU migration is not a fast path.
+	 */
+	if (!pi_test_sn(pi_desc) && vcpu->cpu == cpu)
 		return;
 
+	/*
+	 * First handle the simple case where no cmpxchg is necessary; just
+	 * allow posting non-urgent interrupts.
+	 *
+	 * If the 'nv' field is POSTED_INTR_WAKEUP_VECTOR, do not change
+	 * PI.NDST: pi_post_block will do it for us and the wakeup_handler
+	 * expects the VCPU to be on the blocked_vcpu_list that matches
+	 * PI.NDST.
+	 */
+	if (pi_desc->nv == POSTED_INTR_WAKEUP_VECTOR ||
+	    vcpu->cpu == cpu) {
+		pi_clear_sn(pi_desc);
+		return;
+	}
+
+	/* The full case.  */
 	do {
 		old.control = new.control = pi_desc->control;
 
-		/*
-		 * If 'nv' field is POSTED_INTR_WAKEUP_VECTOR, there
-		 * are two possible cases:
-		 * 1. After running 'pre_block', context switch
-		 *    happened. For this case, 'sn' was set in
-		 *    vmx_vcpu_put(), so we need to clear it here.
-		 * 2. After running 'pre_block', we were blocked,
-		 *    and woken up by some other guy. For this case,
-		 *    we don't need to do anything, 'pi_post_block'
-		 *    will do everything for us. However, we cannot
-		 *    check whether it is case #1 or case #2 here
-		 *    (maybe, not needed), so we also clear sn here,
-		 *    I think it is not a big deal.
-		 */
-		if (pi_desc->nv != POSTED_INTR_WAKEUP_VECTOR) {
-			if (vcpu->cpu != cpu) {
-				dest = cpu_physical_id(cpu);
+		dest = cpu_physical_id(cpu);
 
-				if (x2apic_enabled())
-					new.ndst = dest;
-				else
-					new.ndst = (dest << 8) & 0xFF00;
-			}
+		if (x2apic_enabled())
+			new.ndst = dest;
+		else
+			new.ndst = (dest << 8) & 0xFF00;
 
-			/* set 'NV' to 'notification vector' */
-			new.nv = POSTED_INTR_VECTOR;
-		}
-
-		/* Allow posting non-urgent interrupts */
 		new.sn = 0;
-	} while (cmpxchg(&pi_desc->control, old.control,
-			new.control) != old.control);
+	} while (cmpxchg64(&pi_desc->control, old.control,
+			   new.control) != old.control);
 }
 
 static void decache_tsc_multiplier(struct vcpu_vmx *vmx)
@@ -4761,21 +4759,30 @@
 {
 #ifdef CONFIG_SMP
 	if (vcpu->mode == IN_GUEST_MODE) {
-		struct vcpu_vmx *vmx = to_vmx(vcpu);
-
 		/*
-		 * Currently, we don't support urgent interrupt,
-		 * all interrupts are recognized as non-urgent
-		 * interrupt, so we cannot post interrupts when
-		 * 'SN' is set.
+		 * The vector of interrupt to be delivered to vcpu had
+		 * been set in PIR before this function.
 		 *
-		 * If the vcpu is in guest mode, it means it is
-		 * running instead of being scheduled out and
-		 * waiting in the run queue, and that's the only
-		 * case when 'SN' is set currently, warning if
-		 * 'SN' is set.
+		 * Following cases will be reached in this block, and
+		 * we always send a notification event in all cases as
+		 * explained below.
+		 *
+		 * Case 1: vcpu keeps in non-root mode. Sending a
+		 * notification event posts the interrupt to vcpu.
+		 *
+		 * Case 2: vcpu exits to root mode and is still
+		 * runnable. PIR will be synced to vIRR before the
+		 * next vcpu entry. Sending a notification event in
+		 * this case has no effect, as vcpu is not in root
+		 * mode.
+		 *
+		 * Case 3: vcpu exits to root mode and is blocked.
+		 * vcpu_block() has already synced PIR to vIRR and
+		 * never blocks vcpu if vIRR is not cleared. Therefore,
+		 * a blocked vcpu here does not wait for any requested
+		 * interrupts in PIR, and sending a notification event
+		 * which has no effect is safe here.
 		 */
-		WARN_ON_ONCE(pi_test_sn(&vmx->pi_desc));
 
 		apic->send_IPI_mask(get_cpu_mask(vcpu->cpu),
 				POSTED_INTR_VECTOR);
@@ -9187,6 +9194,13 @@
 
 	vmx->msr_ia32_feature_control_valid_bits = FEATURE_CONTROL_LOCKED;
 
+	/*
+	 * Enforce invariant: pi_desc.nv is always either POSTED_INTR_VECTOR
+	 * or POSTED_INTR_WAKEUP_VECTOR.
+	 */
+	vmx->pi_desc.nv = POSTED_INTR_VECTOR;
+	vmx->pi_desc.sn = 1;
+
 	return &vmx->vcpu;
 
 free_vmcs:
@@ -9996,6 +10010,11 @@
 		vmcs_write64(VIRTUAL_APIC_PAGE_ADDR,
 				page_to_phys(vmx->nested.virtual_apic_page));
 		vmcs_write32(TPR_THRESHOLD, vmcs12->tpr_threshold);
+	} else {
+#ifdef CONFIG_X86_64
+		exec_control |= CPU_BASED_CR8_LOAD_EXITING |
+				CPU_BASED_CR8_STORE_EXITING;
+#endif
 	}
 
 	if (cpu_has_vmx_msr_bitmap() &&
@@ -10671,7 +10690,7 @@
 	 * (KVM doesn't change it)- no reason to call set_cr4_guest_host_mask();
 	 */
 	vcpu->arch.cr4_guest_owned_bits = ~vmcs_readl(CR4_GUEST_HOST_MASK);
-	kvm_set_cr4(vcpu, vmcs12->host_cr4);
+	vmx_set_cr4(vcpu, vmcs12->host_cr4);
 
 	nested_ept_uninit_mmu_context(vcpu);
 
@@ -11000,6 +11019,37 @@
 	kvm_mmu_clear_dirty_pt_masked(kvm, memslot, offset, mask);
 }
 
+static void __pi_post_block(struct kvm_vcpu *vcpu)
+{
+	struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
+	struct pi_desc old, new;
+	unsigned int dest;
+
+	do {
+		old.control = new.control = pi_desc->control;
+		WARN(old.nv != POSTED_INTR_WAKEUP_VECTOR,
+		     "Wakeup handler not enabled while the VCPU is blocked\n");
+
+		dest = cpu_physical_id(vcpu->cpu);
+
+		if (x2apic_enabled())
+			new.ndst = dest;
+		else
+			new.ndst = (dest << 8) & 0xFF00;
+
+		/* set 'NV' to 'notification vector' */
+		new.nv = POSTED_INTR_VECTOR;
+	} while (cmpxchg64(&pi_desc->control, old.control,
+			   new.control) != old.control);
+
+	if (!WARN_ON_ONCE(vcpu->pre_pcpu == -1)) {
+		spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+		list_del(&vcpu->blocked_vcpu_list);
+		spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+		vcpu->pre_pcpu = -1;
+	}
+}
+
 /*
  * This routine does the following things for vCPU which is going
  * to be blocked if VT-d PI is enabled.
@@ -11015,7 +11065,6 @@
  */
 static int pi_pre_block(struct kvm_vcpu *vcpu)
 {
-	unsigned long flags;
 	unsigned int dest;
 	struct pi_desc old, new;
 	struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
@@ -11025,34 +11074,20 @@
 		!kvm_vcpu_apicv_active(vcpu))
 		return 0;
 
-	vcpu->pre_pcpu = vcpu->cpu;
-	spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
-			  vcpu->pre_pcpu), flags);
-	list_add_tail(&vcpu->blocked_vcpu_list,
-		      &per_cpu(blocked_vcpu_on_cpu,
-		      vcpu->pre_pcpu));
-	spin_unlock_irqrestore(&per_cpu(blocked_vcpu_on_cpu_lock,
-			       vcpu->pre_pcpu), flags);
+	WARN_ON(irqs_disabled());
+	local_irq_disable();
+	if (!WARN_ON_ONCE(vcpu->pre_pcpu != -1)) {
+		vcpu->pre_pcpu = vcpu->cpu;
+		spin_lock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+		list_add_tail(&vcpu->blocked_vcpu_list,
+			      &per_cpu(blocked_vcpu_on_cpu,
+				       vcpu->pre_pcpu));
+		spin_unlock(&per_cpu(blocked_vcpu_on_cpu_lock, vcpu->pre_pcpu));
+	}
 
 	do {
 		old.control = new.control = pi_desc->control;
 
-		/*
-		 * We should not block the vCPU if
-		 * an interrupt is posted for it.
-		 */
-		if (pi_test_on(pi_desc) == 1) {
-			spin_lock_irqsave(&per_cpu(blocked_vcpu_on_cpu_lock,
-					  vcpu->pre_pcpu), flags);
-			list_del(&vcpu->blocked_vcpu_list);
-			spin_unlock_irqrestore(
-					&per_cpu(blocked_vcpu_on_cpu_lock,
-					vcpu->pre_pcpu), flags);
-			vcpu->pre_pcpu = -1;
-
-			return 1;
-		}
-
 		WARN((pi_desc->sn == 1),
 		     "Warning: SN field of posted-interrupts "
 		     "is set before blocking\n");
@@ -11074,10 +11109,15 @@
 
 		/* set 'NV' to 'wakeup vector' */
 		new.nv = POSTED_INTR_WAKEUP_VECTOR;
-	} while (cmpxchg(&pi_desc->control, old.control,
-			new.control) != old.control);
+	} while (cmpxchg64(&pi_desc->control, old.control,
+			   new.control) != old.control);
 
-	return 0;
+	/* We should not block the vCPU if an interrupt is posted for it.  */
+	if (pi_test_on(pi_desc) == 1)
+		__pi_post_block(vcpu);
+
+	local_irq_enable();
+	return (vcpu->pre_pcpu == -1);
 }
 
 static int vmx_pre_block(struct kvm_vcpu *vcpu)
@@ -11093,44 +11133,13 @@
 
 static void pi_post_block(struct kvm_vcpu *vcpu)
 {
-	struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu);
-	struct pi_desc old, new;
-	unsigned int dest;
-	unsigned long flags;
-
-	if (!kvm_arch_has_assigned_device(vcpu->kvm) ||
-		!irq_remapping_cap(IRQ_POSTING_CAP)  ||
-		!kvm_vcpu_apicv_active(vcpu))
+	if (vcpu->pre_pcpu == -1)
 		return;
 
-	do {
-		old.control = new.control = pi_desc->control;
-
-		dest = cpu_physical_id(vcpu->cpu);
-
-		if (x2apic_enabled())
-			new.ndst = dest;
-		else
-			new.ndst = (dest << 8) & 0xFF00;
-
-		/* Allow posting non-urgent interrupts */
-		new.sn = 0;
-
-		/* set 'NV' to 'notification vector' */
-		new.nv = POSTED_INTR_VECTOR;
-	} while (cmpxchg(&pi_desc->control, old.control,
-			new.control) != old.control);
-
-	if(vcpu->pre_pcpu != -1) {
-		spin_lock_irqsave(
-			&per_cpu(blocked_vcpu_on_cpu_lock,
-			vcpu->pre_pcpu), flags);
-		list_del(&vcpu->blocked_vcpu_list);
-		spin_unlock_irqrestore(
-			&per_cpu(blocked_vcpu_on_cpu_lock,
-			vcpu->pre_pcpu), flags);
-		vcpu->pre_pcpu = -1;
-	}
+	WARN_ON(irqs_disabled());
+	local_irq_disable();
+	__pi_post_block(vcpu);
+	local_irq_enable();
 }
 
 static void vmx_post_block(struct kvm_vcpu *vcpu)
@@ -11158,7 +11167,7 @@
 	struct kvm_lapic_irq irq;
 	struct kvm_vcpu *vcpu;
 	struct vcpu_data vcpu_info;
-	int idx, ret = -EINVAL;
+	int idx, ret = 0;
 
 	if (!kvm_arch_has_assigned_device(kvm) ||
 		!irq_remapping_cap(IRQ_POSTING_CAP) ||
@@ -11167,7 +11176,12 @@
 
 	idx = srcu_read_lock(&kvm->irq_srcu);
 	irq_rt = srcu_dereference(kvm->irq_routing, &kvm->irq_srcu);
-	BUG_ON(guest_irq >= irq_rt->nr_rt_entries);
+	if (guest_irq >= irq_rt->nr_rt_entries ||
+	    hlist_empty(&irq_rt->map[guest_irq])) {
+		pr_warn_once("no route for guest_irq %u/%u (broken user space?)\n",
+			     guest_irq, irq_rt->nr_rt_entries);
+		goto out;
+	}
 
 	hlist_for_each_entry(e, &irq_rt->map[guest_irq], link) {
 		if (e->type != KVM_IRQ_ROUTING_MSI)
@@ -11210,12 +11224,8 @@
 
 		if (set)
 			ret = irq_set_vcpu_affinity(host_irq, &vcpu_info);
-		else {
-			/* suppress notification event before unposting */
-			pi_set_sn(vcpu_to_pi_desc(vcpu));
+		else
 			ret = irq_set_vcpu_affinity(host_irq, NULL);
-			pi_clear_sn(vcpu_to_pi_desc(vcpu));
-		}
 
 		if (ret < 0) {
 			printk(KERN_INFO "%s: failed to update PI IRTE\n",
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 3dbcb09..595f814 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5250,6 +5250,8 @@
 	kvm_x86_ops->get_cs_db_l_bits(vcpu, &cs_db, &cs_l);
 
 	ctxt->eflags = kvm_get_rflags(vcpu);
+	ctxt->tf = (ctxt->eflags & X86_EFLAGS_TF) != 0;
+
 	ctxt->eip = kvm_rip_read(vcpu);
 	ctxt->mode = (!is_protmode(vcpu))		? X86EMUL_MODE_REAL :
 		     (ctxt->eflags & X86_EFLAGS_VM)	? X86EMUL_MODE_VM86 :
@@ -5465,37 +5467,26 @@
 	return dr6;
 }
 
-static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflags, int *r)
+static void kvm_vcpu_do_singlestep(struct kvm_vcpu *vcpu, int *r)
 {
 	struct kvm_run *kvm_run = vcpu->run;
 
-	/*
-	 * rflags is the old, "raw" value of the flags.  The new value has
-	 * not been saved yet.
-	 *
-	 * This is correct even for TF set by the guest, because "the
-	 * processor will not generate this exception after the instruction
-	 * that sets the TF flag".
-	 */
-	if (unlikely(rflags & X86_EFLAGS_TF)) {
-		if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
-			kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1 |
-						  DR6_RTM;
-			kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip;
-			kvm_run->debug.arch.exception = DB_VECTOR;
-			kvm_run->exit_reason = KVM_EXIT_DEBUG;
-			*r = EMULATE_USER_EXIT;
-		} else {
-			vcpu->arch.emulate_ctxt.eflags &= ~X86_EFLAGS_TF;
-			/*
-			 * "Certain debug exceptions may clear bit 0-3.  The
-			 * remaining contents of the DR6 register are never
-			 * cleared by the processor".
-			 */
-			vcpu->arch.dr6 &= ~15;
-			vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
-			kvm_queue_exception(vcpu, DB_VECTOR);
-		}
+	if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
+		kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1 | DR6_RTM;
+		kvm_run->debug.arch.pc = vcpu->arch.singlestep_rip;
+		kvm_run->debug.arch.exception = DB_VECTOR;
+		kvm_run->exit_reason = KVM_EXIT_DEBUG;
+		*r = EMULATE_USER_EXIT;
+	} else {
+		vcpu->arch.emulate_ctxt.eflags &= ~X86_EFLAGS_TF;
+		/*
+		 * "Certain debug exceptions may clear bit 0-3.  The
+		 * remaining contents of the DR6 register are never
+		 * cleared by the processor".
+		 */
+		vcpu->arch.dr6 &= ~15;
+		vcpu->arch.dr6 |= DR6_BS | DR6_RTM;
+		kvm_queue_exception(vcpu, DB_VECTOR);
 	}
 }
 
@@ -5650,8 +5641,9 @@
 		toggle_interruptibility(vcpu, ctxt->interruptibility);
 		vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
 		kvm_rip_write(vcpu, ctxt->eip);
-		if (r == EMULATE_DONE)
-			kvm_vcpu_check_singlestep(vcpu, rflags, &r);
+		if (r == EMULATE_DONE &&
+		    (ctxt->tf || (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)))
+			kvm_vcpu_do_singlestep(vcpu, &r);
 		if (!ctxt->have_exception ||
 		    exception_type(ctxt->exception.vector) == EXCPT_TRAP)
 			__kvm_set_rflags(vcpu, ctxt->eflags);
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 9f72ca3..1dd7960 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -191,8 +191,7 @@
  * 6. T1   : reaches here, sees vma_pkey(vma)=5, when we really
  *	     faulted on a pte with its pkey=4.
  */
-static void fill_sig_info_pkey(int si_code, siginfo_t *info,
-		struct vm_area_struct *vma)
+static void fill_sig_info_pkey(int si_code, siginfo_t *info, u32 *pkey)
 {
 	/* This is effectively an #ifdef */
 	if (!boot_cpu_has(X86_FEATURE_OSPKE))
@@ -208,7 +207,7 @@
 	 * valid VMA, so we should never reach this without a
 	 * valid VMA.
 	 */
-	if (!vma) {
+	if (!pkey) {
 		WARN_ONCE(1, "PKU fault with no VMA passed in");
 		info->si_pkey = 0;
 		return;
@@ -218,13 +217,12 @@
 	 * absolutely guranteed to be 100% accurate because of
 	 * the race explained above.
 	 */
-	info->si_pkey = vma_pkey(vma);
+	info->si_pkey = *pkey;
 }
 
 static void
 force_sig_info_fault(int si_signo, int si_code, unsigned long address,
-		     struct task_struct *tsk, struct vm_area_struct *vma,
-		     int fault)
+		     struct task_struct *tsk, u32 *pkey, int fault)
 {
 	unsigned lsb = 0;
 	siginfo_t info;
@@ -239,7 +237,7 @@
 		lsb = PAGE_SHIFT;
 	info.si_addr_lsb = lsb;
 
-	fill_sig_info_pkey(si_code, &info, vma);
+	fill_sig_info_pkey(si_code, &info, pkey);
 
 	force_sig_info(si_signo, &info, tsk);
 }
@@ -718,8 +716,6 @@
 	struct task_struct *tsk = current;
 	unsigned long flags;
 	int sig;
-	/* No context means no VMA to pass down */
-	struct vm_area_struct *vma = NULL;
 
 	/* Are we prepared to handle this kernel fault? */
 	if (fixup_exception(regs, X86_TRAP_PF)) {
@@ -744,7 +740,7 @@
 
 			/* XXX: hwpoison faults will set the wrong code. */
 			force_sig_info_fault(signal, si_code, address,
-					     tsk, vma, 0);
+					     tsk, NULL, 0);
 		}
 
 		/*
@@ -853,8 +849,7 @@
 
 static void
 __bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
-		       unsigned long address, struct vm_area_struct *vma,
-		       int si_code)
+		       unsigned long address, u32 *pkey, int si_code)
 {
 	struct task_struct *tsk = current;
 
@@ -902,7 +897,7 @@
 		tsk->thread.error_code	= error_code;
 		tsk->thread.trap_nr	= X86_TRAP_PF;
 
-		force_sig_info_fault(SIGSEGV, si_code, address, tsk, vma, 0);
+		force_sig_info_fault(SIGSEGV, si_code, address, tsk, pkey, 0);
 
 		return;
 	}
@@ -915,9 +910,9 @@
 
 static noinline void
 bad_area_nosemaphore(struct pt_regs *regs, unsigned long error_code,
-		     unsigned long address, struct vm_area_struct *vma)
+		     unsigned long address, u32 *pkey)
 {
-	__bad_area_nosemaphore(regs, error_code, address, vma, SEGV_MAPERR);
+	__bad_area_nosemaphore(regs, error_code, address, pkey, SEGV_MAPERR);
 }
 
 static void
@@ -925,6 +920,10 @@
 	   unsigned long address,  struct vm_area_struct *vma, int si_code)
 {
 	struct mm_struct *mm = current->mm;
+	u32 pkey;
+
+	if (vma)
+		pkey = vma_pkey(vma);
 
 	/*
 	 * Something tried to access memory that isn't in our memory map..
@@ -932,7 +931,8 @@
 	 */
 	up_read(&mm->mmap_sem);
 
-	__bad_area_nosemaphore(regs, error_code, address, vma, si_code);
+	__bad_area_nosemaphore(regs, error_code, address,
+			       (vma) ? &pkey : NULL, si_code);
 }
 
 static noinline void
@@ -975,7 +975,7 @@
 
 static void
 do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address,
-	  struct vm_area_struct *vma, unsigned int fault)
+	  u32 *pkey, unsigned int fault)
 {
 	struct task_struct *tsk = current;
 	int code = BUS_ADRERR;
@@ -1002,13 +1002,12 @@
 		code = BUS_MCEERR_AR;
 	}
 #endif
-	force_sig_info_fault(SIGBUS, code, address, tsk, vma, fault);
+	force_sig_info_fault(SIGBUS, code, address, tsk, pkey, fault);
 }
 
 static noinline void
 mm_fault_error(struct pt_regs *regs, unsigned long error_code,
-	       unsigned long address, struct vm_area_struct *vma,
-	       unsigned int fault)
+	       unsigned long address, u32 *pkey, unsigned int fault)
 {
 	if (fatal_signal_pending(current) && !(error_code & PF_USER)) {
 		no_context(regs, error_code, address, 0, 0);
@@ -1032,9 +1031,9 @@
 	} else {
 		if (fault & (VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|
 			     VM_FAULT_HWPOISON_LARGE))
-			do_sigbus(regs, error_code, address, vma, fault);
+			do_sigbus(regs, error_code, address, pkey, fault);
 		else if (fault & VM_FAULT_SIGSEGV)
-			bad_area_nosemaphore(regs, error_code, address, vma);
+			bad_area_nosemaphore(regs, error_code, address, pkey);
 		else
 			BUG();
 	}
@@ -1220,6 +1219,7 @@
 	struct mm_struct *mm;
 	int fault, major = 0;
 	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+	u32 pkey;
 
 	tsk = current;
 	mm = tsk->mm;
@@ -1420,9 +1420,10 @@
 		return;
 	}
 
+	pkey = vma_pkey(vma);
 	up_read(&mm->mmap_sem);
 	if (unlikely(fault & VM_FAULT_ERROR)) {
-		mm_fault_error(regs, error_code, address, vma, fault);
+		mm_fault_error(regs, error_code, address, &pkey, fault);
 		return;
 	}
 
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 9a324fc..3e27ded 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -689,7 +689,7 @@
 	if (PageReserved(page)) {
 		__ClearPageReserved(page);
 
-		magic = (unsigned long)page->lru.next;
+		magic = (unsigned long)page->freelist;
 		if (magic == SECTION_INFO || magic == MIX_SECTION_INFO) {
 			while (nr_pages--)
 				put_page_bootmem(page++);
diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c
index 350f709..7913b69 100644
--- a/arch/x86/oprofile/op_model_ppro.c
+++ b/arch/x86/oprofile/op_model_ppro.c
@@ -212,8 +212,8 @@
 	eax.full = cpuid_eax(0xa);
 
 	/* Workaround for BIOS bugs in 6/15. Taken from perfmon2 */
-	if (eax.split.version_id == 0 && __this_cpu_read(cpu_info.x86) == 6 &&
-		__this_cpu_read(cpu_info.x86_model) == 15) {
+	if (eax.split.version_id == 0 && boot_cpu_data.x86 == 6 &&
+	    boot_cpu_data.x86_model == 15) {
 		eax.split.version_id = 2;
 		eax.split.num_counters = 2;
 		eax.split.bit_width = 40;
diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile
index 555b9fa..7dbdb78 100644
--- a/arch/x86/purgatory/Makefile
+++ b/arch/x86/purgatory/Makefile
@@ -8,6 +8,7 @@
 LDFLAGS_purgatory.ro := -e purgatory_start -r --no-undefined -nostdlib -z nodefaultlib
 targets += purgatory.ro
 
+KASAN_SANITIZE	:= n
 KCOV_INSTRUMENT := n
 
 # Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That
diff --git a/arch/xtensa/kernel/xtensa_ksyms.c b/arch/xtensa/kernel/xtensa_ksyms.c
index 4d2872f..a71d273 100644
--- a/arch/xtensa/kernel/xtensa_ksyms.c
+++ b/arch/xtensa/kernel/xtensa_ksyms.c
@@ -94,13 +94,11 @@
 }
 EXPORT_SYMBOL(__sync_fetch_and_or_4);
 
-#ifdef CONFIG_NET
 /*
  * Networking support
  */
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(csum_partial_copy_generic);
-#endif /* CONFIG_NET */
 
 /*
  * Architecture-specific symbols
diff --git a/arch/xtensa/mm/cache.c b/arch/xtensa/mm/cache.c
index 1a804a2..3c75c4e 100644
--- a/arch/xtensa/mm/cache.c
+++ b/arch/xtensa/mm/cache.c
@@ -103,6 +103,7 @@
 	clear_page_alias(kvaddr, paddr);
 	preempt_enable();
 }
+EXPORT_SYMBOL(clear_user_highpage);
 
 void copy_user_highpage(struct page *dst, struct page *src,
 			unsigned long vaddr, struct vm_area_struct *vma)
@@ -119,10 +120,7 @@
 	copy_page_alias(dst_vaddr, src_vaddr, dst_paddr, src_paddr);
 	preempt_enable();
 }
-
-#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
-
-#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
+EXPORT_SYMBOL(copy_user_highpage);
 
 /*
  * Any time the kernel writes to a user page cache page, or it is about to
@@ -176,7 +174,7 @@
 
 	/* There shouldn't be an entry in the cache for this page anymore. */
 }
-
+EXPORT_SYMBOL(flush_dcache_page);
 
 /*
  * For now, flush the whole cache. FIXME??
@@ -188,6 +186,7 @@
 	__flush_invalidate_dcache_all();
 	__invalidate_icache_all();
 }
+EXPORT_SYMBOL(local_flush_cache_range);
 
 /* 
  * Remove any entry in the cache for this page. 
@@ -207,8 +206,9 @@
 	__flush_invalidate_dcache_page_alias(virt, phys);
 	__invalidate_icache_page_alias(virt, phys);
 }
+EXPORT_SYMBOL(local_flush_cache_page);
 
-#endif
+#endif /* DCACHE_WAY_SIZE > PAGE_SIZE */
 
 void
 update_mmu_cache(struct vm_area_struct * vma, unsigned long addr, pte_t *ptep)
@@ -225,7 +225,7 @@
 
 	flush_tlb_page(vma, addr);
 
-#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
 
 	if (!PageReserved(page) && test_bit(PG_arch_1, &page->flags)) {
 		unsigned long phys = page_to_phys(page);
@@ -256,7 +256,7 @@
  * flush_dcache_page() on the page.
  */
 
-#if (DCACHE_WAY_SIZE > PAGE_SIZE) && XCHAL_DCACHE_IS_WRITEBACK
+#if (DCACHE_WAY_SIZE > PAGE_SIZE)
 
 void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
 		unsigned long vaddr, void *dst, const void *src,
diff --git a/block/bio.c b/block/bio.c
index 655c901..e14a897 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -589,7 +589,7 @@
 	bio->bi_opf = bio_src->bi_opf;
 	bio->bi_iter = bio_src->bi_iter;
 	bio->bi_io_vec = bio_src->bi_io_vec;
-
+	bio->bi_dio_inode = bio_src->bi_dio_inode;
 	bio_clone_blkcg_association(bio, bio_src);
 }
 EXPORT_SYMBOL(__bio_clone_fast);
@@ -1171,8 +1171,8 @@
 	 */
 	bmd->is_our_pages = map_data ? 0 : 1;
 	memcpy(bmd->iov, iter->iov, sizeof(struct iovec) * iter->nr_segs);
-	iov_iter_init(&bmd->iter, iter->type, bmd->iov,
-			iter->nr_segs, iter->count);
+	bmd->iter = *iter;
+	bmd->iter.iov = bmd->iov;
 
 	ret = -ENOMEM;
 	bio = bio_kmalloc(gfp_mask, nr_pages);
@@ -1266,6 +1266,7 @@
 	int ret, offset;
 	struct iov_iter i;
 	struct iovec iov;
+	struct bio_vec *bvec;
 
 	iov_for_each(iov, i, *iter) {
 		unsigned long uaddr = (unsigned long) iov.iov_base;
@@ -1310,7 +1311,12 @@
 		ret = get_user_pages_fast(uaddr, local_nr_pages,
 				(iter->type & WRITE) != WRITE,
 				&pages[cur_page]);
-		if (ret < local_nr_pages) {
+		if (unlikely(ret < local_nr_pages)) {
+			for (j = cur_page; j < page_limit; j++) {
+				if (!pages[j])
+					break;
+				put_page(pages[j]);
+			}
 			ret = -EFAULT;
 			goto out_unmap;
 		}
@@ -1318,6 +1324,7 @@
 		offset = offset_in_page(uaddr);
 		for (j = cur_page; j < page_limit; j++) {
 			unsigned int bytes = PAGE_SIZE - offset;
+			unsigned short prev_bi_vcnt = bio->bi_vcnt;
 
 			if (len <= 0)
 				break;
@@ -1332,6 +1339,13 @@
 					    bytes)
 				break;
 
+			/*
+			 * check if vector was merged with previous
+			 * drop page reference if needed
+			 */
+			if (bio->bi_vcnt == prev_bi_vcnt)
+				put_page(pages[j]);
+
 			len -= bytes;
 			offset = 0;
 		}
@@ -1364,10 +1378,8 @@
 	return bio;
 
  out_unmap:
-	for (j = 0; j < nr_pages; j++) {
-		if (!pages[j])
-			break;
-		put_page(pages[j]);
+	bio_for_each_segment_all(bvec, bio, j) {
+		put_page(bvec->bv_page);
 	}
  out:
 	kfree(pages);
diff --git a/block/blk-core.c b/block/blk-core.c
index d8fba67..9fc567c 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -235,7 +235,7 @@
  **/
 void blk_start_queue(struct request_queue *q)
 {
-	WARN_ON(!irqs_disabled());
+	WARN_ON(!in_interrupt() && !irqs_disabled());
 
 	queue_flag_clear(QUEUE_FLAG_STOPPED, q);
 	__blk_run_queue(q);
diff --git a/block/blk-merge.c b/block/blk-merge.c
index abde370..0272fac 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -6,7 +6,7 @@
 #include <linux/bio.h>
 #include <linux/blkdev.h>
 #include <linux/scatterlist.h>
-
+#include <linux/pfk.h>
 #include <trace/events/block.h>
 
 #include "blk.h"
@@ -725,6 +725,11 @@
 	}
 }
 
+static bool crypto_not_mergeable(const struct bio *bio, const struct bio *nxt)
+{
+	return (!pfk_allow_merge_bio(bio, nxt));
+}
+
 /*
  * Has to be called with the request spinlock acquired
  */
@@ -752,6 +757,8 @@
 	    !blk_write_same_mergeable(req->bio, next->bio))
 		return 0;
 
+	if (crypto_not_mergeable(req->bio, next->bio))
+		return 0;
 	/*
 	 * If we are allowed to merge, then append bio list
 	 * from next to rq and release next. merge_requests_fn
@@ -862,6 +869,8 @@
 	    !blk_write_same_mergeable(rq->bio, bio))
 		return false;
 
+	if (crypto_not_mergeable(rq->bio, bio))
+		return false;
 	return true;
 }
 
diff --git a/block/blk-mq-pci.c b/block/blk-mq-pci.c
index 966c216..ee9d3d9 100644
--- a/block/blk-mq-pci.c
+++ b/block/blk-mq-pci.c
@@ -36,12 +36,18 @@
 	for (queue = 0; queue < set->nr_hw_queues; queue++) {
 		mask = pci_irq_get_affinity(pdev, queue);
 		if (!mask)
-			return -EINVAL;
+			goto fallback;
 
 		for_each_cpu(cpu, mask)
 			set->mq_map[cpu] = queue;
 	}
 
 	return 0;
+
+fallback:
+	WARN_ON_ONCE(set->nr_hw_queues > 1);
+	for_each_possible_cpu(cpu)
+		set->mq_map[cpu] = 0;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(blk_mq_pci_map_queues);
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index c7c3d4e..4ac4910 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -2948,10 +2948,11 @@
 
 	/*
 	 * SSD device without seek penalty, disable idling. But only do so
-	 * for devices that support queuing, otherwise we still have a problem
-	 * with sync vs async workloads.
+	 * for devices that support queuing (and when group idle is 0),
+	 * otherwise we still have a problem with sync vs async workloads.
 	 */
-	if (blk_queue_nonrot(cfqd->queue) && cfqd->hw_tag)
+	if (blk_queue_nonrot(cfqd->queue) && cfqd->hw_tag &&
+		!cfqd->cfq_group_idle)
 		return;
 
 	WARN_ON(!RB_EMPTY_ROOT(&cfqq->sort_list));
diff --git a/block/partitions/efi.c b/block/partitions/efi.c
index bcd86e5..39f70d9 100644
--- a/block/partitions/efi.c
+++ b/block/partitions/efi.c
@@ -293,7 +293,7 @@
 	if (!gpt)
 		return NULL;
 
-	count = le32_to_cpu(gpt->num_partition_entries) *
+	count = (size_t)le32_to_cpu(gpt->num_partition_entries) *
                 le32_to_cpu(gpt->sizeof_partition_entry);
 	if (!count)
 		return NULL;
@@ -352,7 +352,7 @@
 			gpt_header **gpt, gpt_entry **ptes)
 {
 	u32 crc, origcrc;
-	u64 lastlba;
+	u64 lastlba, pt_size;
 
 	if (!ptes)
 		return 0;
@@ -434,13 +434,20 @@
 		goto fail;
 	}
 
+	/* Sanity check partition table size */
+	pt_size = (u64)le32_to_cpu((*gpt)->num_partition_entries) *
+		le32_to_cpu((*gpt)->sizeof_partition_entry);
+	if (pt_size > KMALLOC_MAX_SIZE) {
+		pr_debug("GUID Partition Table is too large: %llu > %lu bytes\n",
+			 (unsigned long long)pt_size, KMALLOC_MAX_SIZE);
+		goto fail;
+	}
+
 	if (!(*ptes = alloc_read_gpt_entries(state, *gpt)))
 		goto fail;
 
 	/* Check the GUID Partition Entry Array CRC */
-	crc = efi_crc32((const unsigned char *) (*ptes),
-			le32_to_cpu((*gpt)->num_partition_entries) *
-			le32_to_cpu((*gpt)->sizeof_partition_entry));
+	crc = efi_crc32((const unsigned char *) (*ptes), pt_size);
 
 	if (crc != le32_to_cpu((*gpt)->partition_entry_array_crc32)) {
 		pr_debug("GUID Partition Entry Array CRC check failed.\n");
diff --git a/crypto/Kconfig b/crypto/Kconfig
index 84d7148..fa98ad7 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -360,6 +360,7 @@
 	select CRYPTO_BLKCIPHER
 	select CRYPTO_MANAGER
 	select CRYPTO_GF128MUL
+	select CRYPTO_ECB
 	help
 	  XTS: IEEE1619/D16 narrow block cipher use with aes-xts-plain,
 	  key size 256, 384 or 512 bits. This implementation currently
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index 28556fc..aaf2f81 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -86,8 +86,13 @@
 	}
 	sgl = sreq->tsg;
 	n = sg_nents(sgl);
-	for_each_sg(sgl, sg, n, i)
-		put_page(sg_page(sg));
+	for_each_sg(sgl, sg, n, i) {
+		struct page *page = sg_page(sg);
+
+		/* some SGs may not have a page mapped */
+		if (page && page_ref_count(page))
+			put_page(page);
+	}
 
 	kfree(sreq->tsg);
 }
@@ -138,8 +143,10 @@
 		sg_init_table(sgl->sg, MAX_SGL_ENTS + 1);
 		sgl->cur = 0;
 
-		if (sg)
+		if (sg) {
 			sg_chain(sg, MAX_SGL_ENTS + 1, sgl->sg);
+			sg_unmark_end(sg + (MAX_SGL_ENTS - 1));
+		}
 
 		list_add_tail(&sgl->list, &ctx->tsgl);
 	}
diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c
index af4cd86..d140d8bb 100644
--- a/crypto/asymmetric_keys/pkcs7_parser.c
+++ b/crypto/asymmetric_keys/pkcs7_parser.c
@@ -88,6 +88,9 @@
 	bool want = false;
 
 	sinfo = msg->signed_infos;
+	if (!sinfo)
+		goto inconsistent;
+
 	if (sinfo->authattrs) {
 		want = true;
 		msg->have_authattrs = true;
diff --git a/crypto/ccm.c b/crypto/ccm.c
index 006d857..b3ace63 100644
--- a/crypto/ccm.c
+++ b/crypto/ccm.c
@@ -413,7 +413,7 @@
 	unsigned int cryptlen = req->cryptlen;
 	u8 *authtag = pctx->auth_tag;
 	u8 *odata = pctx->odata;
-	u8 *iv = req->iv;
+	u8 *iv = pctx->idata;
 	int err;
 
 	cryptlen -= authsize;
@@ -429,6 +429,8 @@
 	if (req->src != req->dst)
 		dst = pctx->dst;
 
+	memcpy(iv, req->iv, 16);
+
 	skcipher_request_set_tfm(skreq, ctx->ctr);
 	skcipher_request_set_callback(skreq, pctx->flags,
 				      crypto_ccm_decrypt_done, req);
diff --git a/crypto/drbg.c b/crypto/drbg.c
index 8cac3d3..942ddff 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -1133,10 +1133,10 @@
 {
 	if (!drbg)
 		return;
-	kzfree(drbg->V);
-	drbg->Vbuf = NULL;
-	kzfree(drbg->C);
-	drbg->Cbuf = NULL;
+	kzfree(drbg->Vbuf);
+	drbg->V = NULL;
+	kzfree(drbg->Cbuf);
+	drbg->C = NULL;
 	kzfree(drbg->scratchpadbuf);
 	drbg->scratchpadbuf = NULL;
 	drbg->reseed_ctr = 0;
diff --git a/crypto/shash.c b/crypto/shash.c
index a051541..4d8a671 100644
--- a/crypto/shash.c
+++ b/crypto/shash.c
@@ -274,12 +274,14 @@
 
 int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc)
 {
-	struct scatterlist *sg = req->src;
-	unsigned int offset = sg->offset;
 	unsigned int nbytes = req->nbytes;
+	struct scatterlist *sg;
+	unsigned int offset;
 	int err;
 
-	if (nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset)) {
+	if (nbytes &&
+	    (sg = req->src, offset = sg->offset,
+	     nbytes < min(sg->length, ((unsigned int)(PAGE_SIZE)) - offset))) {
 		void *data;
 
 		data = kmap_atomic(sg_page(sg));
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index e53bef6..0375c60 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -1072,6 +1072,7 @@
 		if (list_empty(&ghes_sci))
 			unregister_acpi_hed_notifier(&ghes_notifier_sci);
 		mutex_unlock(&ghes_list_mutex);
+		synchronize_rcu();
 		break;
 	case ACPI_HEST_NOTIFY_NMI:
 		ghes_nmi_remove(ghes);
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 79152db..5187469 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1728,7 +1728,7 @@
  * functioning ECDT EC first in order to handle the events.
  * https://bugzilla.kernel.org/show_bug.cgi?id=115021
  */
-int __init acpi_ec_ecdt_start(void)
+static int __init acpi_ec_ecdt_start(void)
 {
 	acpi_handle handle;
 
@@ -1959,20 +1959,17 @@
 int __init acpi_ec_init(void)
 {
 	int result;
+	int ecdt_fail, dsdt_fail;
 
 	/* register workqueue for _Qxx evaluations */
 	result = acpi_ec_query_init();
 	if (result)
-		goto err_exit;
-	/* Now register the driver for the EC */
-	result = acpi_bus_register_driver(&acpi_ec_driver);
-	if (result)
-		goto err_exit;
+		return result;
 
-err_exit:
-	if (result)
-		acpi_ec_query_exit();
-	return result;
+	/* Drivers must be started after acpi_ec_query_init() */
+	ecdt_fail = acpi_ec_ecdt_start();
+	dsdt_fail = acpi_bus_register_driver(&acpi_ec_driver);
+	return ecdt_fail && dsdt_fail ? -ENODEV : 0;
 }
 
 /* EC driver currently not unloadable */
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 219b90b..08b3ca0 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -185,7 +185,6 @@
 int acpi_ec_init(void);
 int acpi_ec_ecdt_probe(void);
 int acpi_ec_dsdt_probe(void);
-int acpi_ec_ecdt_start(void);
 void acpi_ec_block_transactions(void);
 void acpi_ec_unblock_transactions(void);
 int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit,
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c
index 6d7ce6e..5e18ccf 100644
--- a/drivers/acpi/ioapic.c
+++ b/drivers/acpi/ioapic.c
@@ -45,6 +45,12 @@
 	struct resource *res = data;
 	struct resource_win win;
 
+	/*
+	 * We might assign this to 'res' later, make sure all pointers are
+	 * cleared before the resource is added to the global list
+	 */
+	memset(&win, 0, sizeof(win));
+
 	res->flags = 0;
 	if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM))
 		return AE_OK;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index dd3786a..cf725d5 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2051,7 +2051,6 @@
 
 	acpi_gpe_apply_masked_gpes();
 	acpi_update_all_gpes();
-	acpi_ec_ecdt_start();
 
 	acpi_scan_initialized = true;
 
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index 4d4cdc1..01de42c 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -44,6 +44,16 @@
 
 	  Note that enabling this will break newer Android user-space.
 
+config ANDROID_BINDER_IPC_SELFTEST
+	bool "Android Binder IPC Driver Selftest"
+	depends on ANDROID_BINDER_IPC
+	---help---
+	  This feature allows binder selftest to run.
+
+	  Binder selftest checks the allocation and free of binder buffers
+	  exhaustively with combinations of various buffer sizes and
+	  alignments.
+
 endif # if ANDROID
 
 endmenu
diff --git a/drivers/android/Makefile b/drivers/android/Makefile
index 4b7c726..a01254c 100644
--- a/drivers/android/Makefile
+++ b/drivers/android/Makefile
@@ -1,3 +1,4 @@
 ccflags-y += -I$(src)			# needed for trace events
 
 obj-$(CONFIG_ANDROID_BINDER_IPC)	+= binder.o binder_alloc.o
+obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
index 88edacd..4e7e9a7 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -465,9 +465,8 @@
 };
 
 enum binder_deferred_state {
-	BINDER_DEFERRED_PUT_FILES    = 0x01,
-	BINDER_DEFERRED_FLUSH        = 0x02,
-	BINDER_DEFERRED_RELEASE      = 0x04,
+	BINDER_DEFERRED_FLUSH        = 0x01,
+	BINDER_DEFERRED_RELEASE      = 0x02,
 };
 
 /**
@@ -504,8 +503,6 @@
  *                        (invariant after initialized)
  * @tsk                   task_struct for group_leader of process
  *                        (invariant after initialized)
- * @files                 files_struct for process
- *                        (invariant after initialized)
  * @deferred_work_node:   element for binder_deferred_list
  *                        (protected by binder_deferred_lock)
  * @deferred_work:        bitmap of deferred work to perform
@@ -552,7 +549,6 @@
 	struct list_head waiting_threads;
 	int pid;
 	struct task_struct *tsk;
-	struct files_struct *files;
 	struct hlist_node deferred_work_node;
 	int deferred_work;
 	bool is_dead;
@@ -600,6 +596,8 @@
  *                        (protected by @proc->inner_lock)
  * @todo:                 list of work to do for this thread
  *                        (protected by @proc->inner_lock)
+ * @process_todo:         whether work in @todo should be processed
+ *                        (protected by @proc->inner_lock)
  * @return_error:         transaction errors reported by this thread
  *                        (only accessed by this thread)
  * @reply_error:          transaction errors reported by target thread
@@ -626,6 +624,7 @@
 	bool looper_need_return; /* can be written by other thread */
 	struct binder_transaction *transaction_stack;
 	struct list_head todo;
+	bool process_todo;
 	struct binder_error return_error;
 	struct binder_error reply_error;
 	wait_queue_head_t wait;
@@ -813,6 +812,16 @@
 	return ret;
 }
 
+/**
+ * binder_enqueue_work_ilocked() - Add an item to the work list
+ * @work:         struct binder_work to add to list
+ * @target_list:  list to add work to
+ *
+ * Adds the work to the specified list. Asserts that work
+ * is not already on a list.
+ *
+ * Requires the proc->inner_lock to be held.
+ */
 static void
 binder_enqueue_work_ilocked(struct binder_work *work,
 			   struct list_head *target_list)
@@ -823,22 +832,56 @@
 }
 
 /**
- * binder_enqueue_work() - Add an item to the work list
- * @proc:         binder_proc associated with list
+ * binder_enqueue_deferred_thread_work_ilocked() - Add deferred thread work
+ * @thread:       thread to queue work to
  * @work:         struct binder_work to add to list
- * @target_list:  list to add work to
  *
- * Adds the work to the specified list. Asserts that work
- * is not already on a list.
+ * Adds the work to the todo list of the thread. Doesn't set the process_todo
+ * flag, which means that (if it wasn't already set) the thread will go to
+ * sleep without handling this work when it calls read.
+ *
+ * Requires the proc->inner_lock to be held.
  */
 static void
-binder_enqueue_work(struct binder_proc *proc,
-		    struct binder_work *work,
-		    struct list_head *target_list)
+binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread,
+					    struct binder_work *work)
 {
-	binder_inner_proc_lock(proc);
-	binder_enqueue_work_ilocked(work, target_list);
-	binder_inner_proc_unlock(proc);
+	binder_enqueue_work_ilocked(work, &thread->todo);
+}
+
+/**
+ * binder_enqueue_thread_work_ilocked() - Add an item to the thread work list
+ * @thread:       thread to queue work to
+ * @work:         struct binder_work to add to list
+ *
+ * Adds the work to the todo list of the thread, and enables processing
+ * of the todo queue.
+ *
+ * Requires the proc->inner_lock to be held.
+ */
+static void
+binder_enqueue_thread_work_ilocked(struct binder_thread *thread,
+				   struct binder_work *work)
+{
+	binder_enqueue_work_ilocked(work, &thread->todo);
+	thread->process_todo = true;
+}
+
+/**
+ * binder_enqueue_thread_work() - Add an item to the thread work list
+ * @thread:       thread to queue work to
+ * @work:         struct binder_work to add to list
+ *
+ * Adds the work to the todo list of the thread, and enables processing
+ * of the todo queue.
+ */
+static void
+binder_enqueue_thread_work(struct binder_thread *thread,
+			   struct binder_work *work)
+{
+	binder_inner_proc_lock(thread->proc);
+	binder_enqueue_thread_work_ilocked(thread, work);
+	binder_inner_proc_unlock(thread->proc);
 }
 
 static void
@@ -901,22 +944,34 @@
 static void binder_free_proc(struct binder_proc *proc);
 static void binder_inc_node_tmpref_ilocked(struct binder_node *node);
 
+struct files_struct *binder_get_files_struct(struct binder_proc *proc)
+{
+	return get_files_struct(proc->tsk);
+}
+
 static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
 {
-	struct files_struct *files = proc->files;
+	struct files_struct *files;
 	unsigned long rlim_cur;
 	unsigned long irqs;
+	int ret;
 
+	files = binder_get_files_struct(proc);
 	if (files == NULL)
 		return -ESRCH;
 
-	if (!lock_task_sighand(proc->tsk, &irqs))
-		return -EMFILE;
+	if (!lock_task_sighand(proc->tsk, &irqs)) {
+		ret = -EMFILE;
+		goto err;
+	}
 
 	rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE);
 	unlock_task_sighand(proc->tsk, &irqs);
 
-	return __alloc_fd(files, 0, rlim_cur, flags);
+	ret = __alloc_fd(files, 0, rlim_cur, flags);
+err:
+	put_files_struct(files);
+	return ret;
 }
 
 /*
@@ -925,8 +980,12 @@
 static void task_fd_install(
 	struct binder_proc *proc, unsigned int fd, struct file *file)
 {
-	if (proc->files)
-		__fd_install(proc->files, fd, file);
+	struct files_struct *files = binder_get_files_struct(proc);
+
+	if (files) {
+		__fd_install(files, fd, file);
+		put_files_struct(files);
+	}
 }
 
 /*
@@ -934,18 +993,20 @@
  */
 static long task_close_fd(struct binder_proc *proc, unsigned int fd)
 {
+	struct files_struct *files = binder_get_files_struct(proc);
 	int retval;
 
-	if (proc->files == NULL)
+	if (files == NULL)
 		return -ESRCH;
 
-	retval = __close_fd(proc->files, fd);
+	retval = __close_fd(files, fd);
 	/* can't restart close syscall because file table entry was cleared */
 	if (unlikely(retval == -ERESTARTSYS ||
 		     retval == -ERESTARTNOINTR ||
 		     retval == -ERESTARTNOHAND ||
 		     retval == -ERESTART_RESTARTBLOCK))
 		retval = -EINTR;
+	put_files_struct(files);
 
 	return retval;
 }
@@ -953,7 +1014,7 @@
 static bool binder_has_work_ilocked(struct binder_thread *thread,
 				    bool do_proc_work)
 {
-	return !binder_worklist_empty_ilocked(&thread->todo) ||
+	return thread->process_todo ||
 		thread->looper_need_return ||
 		(do_proc_work &&
 		 !binder_worklist_empty_ilocked(&thread->proc->todo));
@@ -1153,6 +1214,10 @@
 			      task->pid, desired.prio,
 			      to_kernel_prio(policy, priority));
 
+	trace_binder_set_priority(task->tgid, task->pid, task->normal_prio,
+				  to_kernel_prio(policy, priority),
+				  desired.prio);
+
 	/* Set the actual priority */
 	if (task->policy != policy || is_rt_policy(policy)) {
 		struct sched_param params;
@@ -1184,7 +1249,7 @@
 					struct binder_priority node_prio,
 					bool inherit_rt)
 {
-	struct binder_priority desired_prio;
+	struct binder_priority desired_prio = t->priority;
 
 	if (t->set_priority_called)
 		return;
@@ -1196,9 +1261,6 @@
 	if (!inherit_rt && is_rt_policy(desired_prio.sched_policy)) {
 		desired_prio.prio = NICE_TO_PRIO(0);
 		desired_prio.sched_policy = SCHED_NORMAL;
-	} else {
-		desired_prio.prio = t->priority.prio;
-		desired_prio.sched_policy = t->priority.sched_policy;
 	}
 
 	if (node_prio.prio < t->priority.prio ||
@@ -1301,7 +1363,7 @@
 	node->cookie = cookie;
 	node->work.type = BINDER_WORK_NODE;
 	priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
-	node->sched_policy = (flags & FLAT_BINDER_FLAG_PRIORITY_MASK) >>
+	node->sched_policy = (flags & FLAT_BINDER_FLAG_SCHED_POLICY_MASK) >>
 		FLAT_BINDER_FLAG_SCHED_POLICY_SHIFT;
 	node->min_priority = to_kernel_prio(node->sched_policy, priority);
 	node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
@@ -1369,6 +1431,17 @@
 			node->local_strong_refs++;
 		if (!node->has_strong_ref && target_list) {
 			binder_dequeue_work_ilocked(&node->work);
+			/*
+			 * Note: this function is the only place where we queue
+			 * directly to a thread->todo without using the
+			 * corresponding binder_enqueue_thread_work() helper
+			 * functions; in this case it's ok to not set the
+			 * process_todo flag, since we know this node work will
+			 * always be followed by other work that starts queue
+			 * processing: in case of synchronous transactions, a
+			 * BR_REPLY or BR_ERROR; in case of oneway
+			 * transactions, a BR_TRANSACTION_COMPLETE.
+			 */
 			binder_enqueue_work_ilocked(&node->work, target_list);
 		}
 	} else {
@@ -1380,6 +1453,9 @@
 					node->debug_id);
 				return -EINVAL;
 			}
+			/*
+			 * See comment above
+			 */
 			binder_enqueue_work_ilocked(&node->work, target_list);
 		}
 	}
@@ -2069,9 +2145,9 @@
 			binder_pop_transaction_ilocked(target_thread, t);
 			if (target_thread->reply_error.cmd == BR_OK) {
 				target_thread->reply_error.cmd = error_code;
-				binder_enqueue_work_ilocked(
-					&target_thread->reply_error.work,
-					&target_thread->todo);
+				binder_enqueue_thread_work_ilocked(
+					target_thread,
+					&target_thread->reply_error.work);
 				wake_up_interruptible(&target_thread->wait);
 			} else {
 				WARN(1, "Unexpected reply error: %u\n",
@@ -2102,6 +2178,26 @@
 }
 
 /**
+ * binder_cleanup_transaction() - cleans up undelivered transaction
+ * @t:		transaction that needs to be cleaned up
+ * @reason:	reason the transaction wasn't delivered
+ * @error_code:	error to return to caller (if synchronous call)
+ */
+static void binder_cleanup_transaction(struct binder_transaction *t,
+				       const char *reason,
+				       uint32_t error_code)
+{
+	if (t->buffer->target_node && !(t->flags & TF_ONE_WAY)) {
+		binder_send_failed_reply(t, error_code);
+	} else {
+		binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
+			"undelivered transaction %d, %s\n",
+			t->debug_id, reason);
+		binder_free_transaction(t);
+	}
+}
+
+/**
  * binder_validate_object() - checks for a valid metadata object in a buffer.
  * @buffer:	binder_buffer that we're parsing.
  * @offset:	offset in the buffer at which to validate an object.
@@ -2480,7 +2576,6 @@
 			     (u64)node->ptr);
 		binder_node_unlock(node);
 	} else {
-		int ret;
 		struct binder_ref_data dest_rdata;
 
 		binder_node_unlock(node);
@@ -2691,11 +2786,10 @@
 				    struct binder_proc *proc,
 				    struct binder_thread *thread)
 {
-	struct list_head *target_list = NULL;
 	struct binder_node *node = t->buffer->target_node;
 	struct binder_priority node_prio;
 	bool oneway = !!(t->flags & TF_ONE_WAY);
-	bool wakeup = true;
+	bool pending_async = false;
 
 	BUG_ON(!node);
 	binder_node_lock(node);
@@ -2705,8 +2799,7 @@
 	if (oneway) {
 		BUG_ON(thread);
 		if (node->has_async_transaction) {
-			target_list = &node->async_todo;
-			wakeup = false;
+			pending_async = true;
 		} else {
 			node->has_async_transaction = 1;
 		}
@@ -2720,22 +2813,20 @@
 		return false;
 	}
 
-	if (!thread && !target_list)
+	if (!thread && !pending_async)
 		thread = binder_select_thread_ilocked(proc);
 
 	if (thread) {
-		target_list = &thread->todo;
 		binder_transaction_priority(thread->task, t, node_prio,
 					    node->inherit_rt);
-	} else if (!target_list) {
-		target_list = &proc->todo;
+		binder_enqueue_thread_work_ilocked(thread, &t->work);
+	} else if (!pending_async) {
+		binder_enqueue_work_ilocked(&t->work, &proc->todo);
 	} else {
-		BUG_ON(target_list != &node->async_todo);
+		binder_enqueue_work_ilocked(&t->work, &node->async_todo);
 	}
 
-	binder_enqueue_work_ilocked(&t->work, target_list);
-
-	if (wakeup)
+	if (!pending_async)
 		binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
 
 	binder_inner_proc_unlock(proc);
@@ -2744,6 +2835,48 @@
 	return true;
 }
 
+/**
+ * binder_get_node_refs_for_txn() - Get required refs on node for txn
+ * @node:         struct binder_node for which to get refs
+ * @proc:         returns @node->proc if valid
+ * @error:        if no @proc then returns BR_DEAD_REPLY
+ *
+ * User-space normally keeps the node alive when creating a transaction
+ * since it has a reference to the target. The local strong ref keeps it
+ * alive if the sending process dies before the target process processes
+ * the transaction. If the source process is malicious or has a reference
+ * counting bug, relying on the local strong ref can fail.
+ *
+ * Since user-space can cause the local strong ref to go away, we also take
+ * a tmpref on the node to ensure it survives while we are constructing
+ * the transaction. We also need a tmpref on the proc while we are
+ * constructing the transaction, so we take that here as well.
+ *
+ * Return: The target_node with refs taken or NULL if no @node->proc is NULL.
+ * Also sets @proc if valid. If the @node->proc is NULL indicating that the
+ * target proc has died, @error is set to BR_DEAD_REPLY
+ */
+static struct binder_node *binder_get_node_refs_for_txn(
+		struct binder_node *node,
+		struct binder_proc **procp,
+		uint32_t *error)
+{
+	struct binder_node *target_node = NULL;
+
+	binder_node_inner_lock(node);
+	if (node->proc) {
+		target_node = node;
+		binder_inc_node_nilocked(node, 1, 0, NULL);
+		binder_inc_node_tmpref_ilocked(node);
+		node->proc->tmp_ref++;
+		*procp = node->proc;
+	} else
+		*error = BR_DEAD_REPLY;
+	binder_node_inner_unlock(node);
+
+	return target_node;
+}
+
 static void binder_transaction(struct binder_proc *proc,
 			       struct binder_thread *thread,
 			       struct binder_transaction_data *tr, int reply,
@@ -2846,43 +2979,35 @@
 			ref = binder_get_ref_olocked(proc, tr->target.handle,
 						     true);
 			if (ref) {
-				binder_inc_node(ref->node, 1, 0, NULL);
-				target_node = ref->node;
+				target_node = binder_get_node_refs_for_txn(
+						ref->node, &target_proc,
+						&return_error);
+			} else {
+				binder_user_error("%d:%d got transaction to invalid handle\n",
+						  proc->pid, thread->pid);
+				return_error = BR_FAILED_REPLY;
 			}
 			binder_proc_unlock(proc);
-			if (target_node == NULL) {
-				binder_user_error("%d:%d got transaction to invalid handle\n",
-					proc->pid, thread->pid);
-				return_error = BR_FAILED_REPLY;
-				return_error_param = -EINVAL;
-				return_error_line = __LINE__;
-				goto err_invalid_target_handle;
-			}
 		} else {
 			mutex_lock(&context->context_mgr_node_lock);
 			target_node = context->binder_context_mgr_node;
-			if (target_node == NULL) {
+			if (target_node)
+				target_node = binder_get_node_refs_for_txn(
+						target_node, &target_proc,
+						&return_error);
+			else
 				return_error = BR_DEAD_REPLY;
-				mutex_unlock(&context->context_mgr_node_lock);
-				return_error_line = __LINE__;
-				goto err_no_context_mgr_node;
-			}
-			binder_inc_node(target_node, 1, 0, NULL);
 			mutex_unlock(&context->context_mgr_node_lock);
 		}
-		e->to_node = target_node->debug_id;
-		binder_node_lock(target_node);
-		target_proc = target_node->proc;
-		if (target_proc == NULL) {
-			binder_node_unlock(target_node);
-			return_error = BR_DEAD_REPLY;
+		if (!target_node) {
+			/*
+			 * return_error is set above
+			 */
+			return_error_param = -EINVAL;
 			return_error_line = __LINE__;
 			goto err_dead_binder;
 		}
-		binder_inner_proc_lock(target_proc);
-		target_proc->tmp_ref++;
-		binder_inner_proc_unlock(target_proc);
-		binder_node_unlock(target_node);
+		e->to_node = target_node->debug_id;
 		if (security_binder_transaction(proc->tsk,
 						target_proc->tsk) < 0) {
 			return_error = BR_FAILED_REPLY;
@@ -3203,10 +3328,10 @@
 		}
 	}
 	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
-	binder_enqueue_work(proc, tcomplete, &thread->todo);
 	t->work.type = BINDER_WORK_TRANSACTION;
 
 	if (reply) {
+		binder_enqueue_thread_work(thread, tcomplete);
 		binder_inner_proc_lock(target_proc);
 		if (target_thread->is_dead) {
 			binder_inner_proc_unlock(target_proc);
@@ -3214,7 +3339,7 @@
 		}
 		BUG_ON(t->buffer->async_transaction != 0);
 		binder_pop_transaction_ilocked(target_thread, in_reply_to);
-		binder_enqueue_work_ilocked(&t->work, &target_thread->todo);
+		binder_enqueue_thread_work_ilocked(target_thread, &t->work);
 		binder_inner_proc_unlock(target_proc);
 		wake_up_interruptible_sync(&target_thread->wait);
 		binder_restore_priority(current, in_reply_to->saved_priority);
@@ -3222,6 +3347,14 @@
 	} else if (!(t->flags & TF_ONE_WAY)) {
 		BUG_ON(t->buffer->async_transaction != 0);
 		binder_inner_proc_lock(proc);
+		/*
+		 * Defer the TRANSACTION_COMPLETE, so we don't return to
+		 * userspace immediately; this allows the target process to
+		 * immediately start processing this transaction, reducing
+		 * latency. We will then return the TRANSACTION_COMPLETE when
+		 * the target replies (or there is an error).
+		 */
+		binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
 		t->need_reply = 1;
 		t->from_parent = thread->transaction_stack;
 		thread->transaction_stack = t;
@@ -3235,12 +3368,15 @@
 	} else {
 		BUG_ON(target_node == NULL);
 		BUG_ON(t->buffer->async_transaction != 1);
+		binder_enqueue_thread_work(thread, tcomplete);
 		if (!binder_proc_transaction(t, target_proc, NULL))
 			goto err_dead_proc_or_thread;
 	}
 	if (target_thread)
 		binder_thread_dec_tmpref(target_thread);
 	binder_proc_dec_tmpref(target_proc);
+	if (target_node)
+		binder_dec_node_tmpref(target_node);
 	/*
 	 * write barrier to synchronize with initialization
 	 * of log entry
@@ -3252,6 +3388,7 @@
 err_dead_proc_or_thread:
 	return_error = BR_DEAD_REPLY;
 	return_error_line = __LINE__;
+	binder_dequeue_work(proc, tcomplete);
 err_translate_failed:
 err_bad_object_type:
 err_bad_offset:
@@ -3259,6 +3396,8 @@
 err_copy_data_failed:
 	trace_binder_transaction_failed_buffer_release(t->buffer);
 	binder_transaction_buffer_release(target_proc, t->buffer, offp);
+	if (target_node)
+		binder_dec_node_tmpref(target_node);
 	target_node = NULL;
 	t->buffer->transaction = NULL;
 	binder_alloc_free_buf(&target_proc->alloc, t->buffer);
@@ -3273,13 +3412,14 @@
 err_empty_call_stack:
 err_dead_binder:
 err_invalid_target_handle:
-err_no_context_mgr_node:
 	if (target_thread)
 		binder_thread_dec_tmpref(target_thread);
 	if (target_proc)
 		binder_proc_dec_tmpref(target_proc);
-	if (target_node)
+	if (target_node) {
 		binder_dec_node(target_node, 1, 0);
+		binder_dec_node_tmpref(target_node);
+	}
 
 	binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
 		     "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n",
@@ -3308,15 +3448,11 @@
 	if (in_reply_to) {
 		binder_restore_priority(current, in_reply_to->saved_priority);
 		thread->return_error.cmd = BR_TRANSACTION_COMPLETE;
-		binder_enqueue_work(thread->proc,
-				    &thread->return_error.work,
-				    &thread->todo);
+		binder_enqueue_thread_work(thread, &thread->return_error.work);
 		binder_send_failed_reply(in_reply_to, return_error);
 	} else {
 		thread->return_error.cmd = return_error;
-		binder_enqueue_work(thread->proc,
-				    &thread->return_error.work,
-				    &thread->todo);
+		binder_enqueue_thread_work(thread, &thread->return_error.work);
 	}
 }
 
@@ -3521,11 +3657,13 @@
 				BUG_ON(buf_node->proc != proc);
 				w = binder_dequeue_work_head_ilocked(
 						&buf_node->async_todo);
-				if (!w)
+				if (!w) {
 					buf_node->has_async_transaction = 0;
-				else
+				} else {
 					binder_enqueue_work_ilocked(
-							w, &thread->todo);
+							w, &proc->todo);
+					binder_wakeup_proc_ilocked(proc);
+				}
 				binder_node_inner_unlock(buf_node);
 			}
 			trace_binder_transaction_buffer_release(buffer);
@@ -3618,10 +3756,9 @@
 					WARN_ON(thread->return_error.cmd !=
 						BR_OK);
 					thread->return_error.cmd = BR_ERROR;
-					binder_enqueue_work(
-						thread->proc,
-						&thread->return_error.work,
-						&thread->todo);
+					binder_enqueue_thread_work(
+						thread,
+						&thread->return_error.work);
 					binder_debug(
 						BINDER_DEBUG_FAILED_TRANSACTION,
 						"%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
@@ -3669,22 +3806,12 @@
 				ref->death = death;
 				if (ref->node->proc == NULL) {
 					ref->death->work.type = BINDER_WORK_DEAD_BINDER;
-					if (thread->looper &
-					    (BINDER_LOOPER_STATE_REGISTERED |
-					     BINDER_LOOPER_STATE_ENTERED))
-						binder_enqueue_work(
-							proc,
-							&ref->death->work,
-							&thread->todo);
-					else {
-						binder_inner_proc_lock(proc);
-						binder_enqueue_work_ilocked(
-							&ref->death->work,
-							&proc->todo);
-						binder_wakeup_proc_ilocked(
-							proc);
-						binder_inner_proc_unlock(proc);
-					}
+
+					binder_inner_proc_lock(proc);
+					binder_enqueue_work_ilocked(
+						&ref->death->work, &proc->todo);
+					binder_wakeup_proc_ilocked(proc);
+					binder_inner_proc_unlock(proc);
 				}
 			} else {
 				if (ref->death == NULL) {
@@ -3711,9 +3838,9 @@
 					if (thread->looper &
 					    (BINDER_LOOPER_STATE_REGISTERED |
 					     BINDER_LOOPER_STATE_ENTERED))
-						binder_enqueue_work_ilocked(
-								&death->work,
-								&thread->todo);
+						binder_enqueue_thread_work_ilocked(
+								thread,
+								&death->work);
 					else {
 						binder_enqueue_work_ilocked(
 								&death->work,
@@ -3768,8 +3895,8 @@
 				if (thread->looper &
 					(BINDER_LOOPER_STATE_REGISTERED |
 					 BINDER_LOOPER_STATE_ENTERED))
-					binder_enqueue_work_ilocked(
-						&death->work, &thread->todo);
+					binder_enqueue_thread_work_ilocked(
+						thread, &death->work);
 				else {
 					binder_enqueue_work_ilocked(
 							&death->work,
@@ -3801,12 +3928,6 @@
 	}
 }
 
-static int binder_has_thread_work(struct binder_thread *thread)
-{
-	return !binder_worklist_empty(thread->proc, &thread->todo) ||
-		thread->looper_need_return;
-}
-
 static int binder_put_node_cmd(struct binder_proc *proc,
 			       struct binder_thread *thread,
 			       void __user **ptrp,
@@ -3949,6 +4070,8 @@
 			break;
 		}
 		w = binder_dequeue_work_head_ilocked(list);
+		if (binder_worklist_empty_ilocked(&thread->todo))
+			thread->process_todo = false;
 
 		switch (w->type) {
 		case BINDER_WORK_TRANSACTION: {
@@ -4158,12 +4281,20 @@
 		if (put_user(cmd, (uint32_t __user *)ptr)) {
 			if (t_from)
 				binder_thread_dec_tmpref(t_from);
+
+			binder_cleanup_transaction(t, "put_user failed",
+						   BR_FAILED_REPLY);
+
 			return -EFAULT;
 		}
 		ptr += sizeof(uint32_t);
 		if (copy_to_user(ptr, &tr, sizeof(tr))) {
 			if (t_from)
 				binder_thread_dec_tmpref(t_from);
+
+			binder_cleanup_transaction(t, "copy_to_user failed",
+						   BR_FAILED_REPLY);
+
 			return -EFAULT;
 		}
 		ptr += sizeof(tr);
@@ -4233,15 +4364,9 @@
 			struct binder_transaction *t;
 
 			t = container_of(w, struct binder_transaction, work);
-			if (t->buffer->target_node &&
-			    !(t->flags & TF_ONE_WAY)) {
-				binder_send_failed_reply(t, BR_DEAD_REPLY);
-			} else {
-				binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
-					"undelivered transaction %d\n",
-					t->debug_id);
-				binder_free_transaction(t);
-			}
+
+			binder_cleanup_transaction(t, "process died.",
+						   BR_DEAD_REPLY);
 		} break;
 		case BINDER_WORK_RETURN_ERROR: {
 			struct binder_error *e = container_of(
@@ -4437,12 +4562,9 @@
 
 	binder_inner_proc_unlock(thread->proc);
 
-	if (binder_has_work(thread, wait_for_proc_work))
-		return POLLIN;
-
 	poll_wait(filp, &thread->wait, wait);
 
-	if (binder_has_thread_work(thread))
+	if (binder_has_work(thread, wait_for_proc_work))
 		return POLLIN;
 
 	return 0;
@@ -4596,6 +4718,8 @@
 	/*pr_info("binder_ioctl: %d:%d %x %lx\n",
 			proc->pid, current->pid, cmd, arg);*/
 
+	binder_selftest_alloc(&proc->alloc);
+
 	trace_binder_ioctl(cmd, arg);
 
 	ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
@@ -4707,7 +4831,6 @@
 		     (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags,
 		     (unsigned long)pgprot_val(vma->vm_page_prot));
 	binder_alloc_vma_close(&proc->alloc);
-	binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES);
 }
 
 static int binder_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
@@ -4749,10 +4872,8 @@
 	vma->vm_private_data = proc;
 
 	ret = binder_alloc_mmap_handler(&proc->alloc, vma);
-	if (ret)
-		return ret;
-	proc->files = get_files_struct(current);
-	return 0;
+
+	return ret;
 
 err_bad_arg:
 	pr_err("binder_mmap: %d %lx-%lx %s failed %d\n",
@@ -4931,8 +5052,6 @@
 	struct rb_node *n;
 	int threads, nodes, incoming_refs, outgoing_refs, active_transactions;
 
-	BUG_ON(proc->files);
-
 	mutex_lock(&binder_procs_lock);
 	hlist_del(&proc->proc_node);
 	mutex_unlock(&binder_procs_lock);
@@ -5014,8 +5133,6 @@
 static void binder_deferred_func(struct work_struct *work)
 {
 	struct binder_proc *proc;
-	struct files_struct *files;
-
 	int defer;
 
 	do {
@@ -5032,21 +5149,11 @@
 		}
 		mutex_unlock(&binder_deferred_lock);
 
-		files = NULL;
-		if (defer & BINDER_DEFERRED_PUT_FILES) {
-			files = proc->files;
-			if (files)
-				proc->files = NULL;
-		}
-
 		if (defer & BINDER_DEFERRED_FLUSH)
 			binder_deferred_flush(proc);
 
 		if (defer & BINDER_DEFERRED_RELEASE)
 			binder_deferred_release(proc); /* frees proc */
-
-		if (files)
-			put_files_struct(files);
 	} while (proc);
 }
 static DECLARE_WORK(binder_deferred_work, binder_deferred_func);
@@ -5441,6 +5548,8 @@
 	count = binder_alloc_get_allocated_count(&proc->alloc);
 	seq_printf(m, "  buffers: %d\n", count);
 
+	binder_alloc_print_pages(m, &proc->alloc);
+
 	count = 0;
 	binder_inner_proc_lock(proc);
 	list_for_each_entry(w, &proc->todo, entry) {
@@ -5637,6 +5746,8 @@
 	struct binder_device *device;
 	struct hlist_node *tmp;
 
+	binder_alloc_shrinker_init();
+
 	atomic_set(&binder_transaction_log.cur, ~0U);
 	atomic_set(&binder_transaction_log_failed.cur, ~0U);
 
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index b90222a..3ad1bcf 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -27,9 +27,12 @@
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
+#include <linux/list_lru.h>
 #include "binder_alloc.h"
 #include "binder_trace.h"
 
+struct list_lru binder_alloc_lru;
+
 static DEFINE_MUTEX(binder_alloc_mmap_lock);
 
 enum {
@@ -48,14 +51,23 @@
 			pr_info(x); \
 	} while (0)
 
+static struct binder_buffer *binder_buffer_next(struct binder_buffer *buffer)
+{
+	return list_entry(buffer->entry.next, struct binder_buffer, entry);
+}
+
+static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer)
+{
+	return list_entry(buffer->entry.prev, struct binder_buffer, entry);
+}
+
 static size_t binder_alloc_buffer_size(struct binder_alloc *alloc,
 				       struct binder_buffer *buffer)
 {
 	if (list_is_last(&buffer->entry, &alloc->buffers))
-		return alloc->buffer +
-		       alloc->buffer_size - (void *)buffer->data;
-	return (size_t)list_entry(buffer->entry.next,
-			  struct binder_buffer, entry) - (size_t)buffer->data;
+		return (u8 *)alloc->buffer +
+			alloc->buffer_size - (u8 *)buffer->data;
+	return (u8 *)binder_buffer_next(buffer)->data - (u8 *)buffer->data;
 }
 
 static void binder_insert_free_buffer(struct binder_alloc *alloc,
@@ -105,9 +117,9 @@
 		buffer = rb_entry(parent, struct binder_buffer, rb_node);
 		BUG_ON(buffer->free);
 
-		if (new_buffer < buffer)
+		if (new_buffer->data < buffer->data)
 			p = &parent->rb_left;
-		else if (new_buffer > buffer)
+		else if (new_buffer->data > buffer->data)
 			p = &parent->rb_right;
 		else
 			BUG();
@@ -122,18 +134,17 @@
 {
 	struct rb_node *n = alloc->allocated_buffers.rb_node;
 	struct binder_buffer *buffer;
-	struct binder_buffer *kern_ptr;
+	void *kern_ptr;
 
-	kern_ptr = (struct binder_buffer *)(user_ptr - alloc->user_buffer_offset
-		- offsetof(struct binder_buffer, data));
+	kern_ptr = (void *)(user_ptr - alloc->user_buffer_offset);
 
 	while (n) {
 		buffer = rb_entry(n, struct binder_buffer, rb_node);
 		BUG_ON(buffer->free);
 
-		if (kern_ptr < buffer)
+		if (kern_ptr < buffer->data)
 			n = n->rb_left;
-		else if (kern_ptr > buffer)
+		else if (kern_ptr > buffer->data)
 			n = n->rb_right;
 		else {
 			/*
@@ -175,13 +186,14 @@
 }
 
 static int binder_update_page_range(struct binder_alloc *alloc, int allocate,
-				    void *start, void *end,
-				    struct vm_area_struct *vma)
+				    void *start, void *end)
 {
 	void *page_addr;
 	unsigned long user_page_addr;
-	struct page **page;
-	struct mm_struct *mm;
+	struct binder_lru_page *page;
+	struct vm_area_struct *vma = NULL;
+	struct mm_struct *mm = NULL;
+	bool need_mm = false;
 
 	binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
 		     "%d: %s pages %pK-%pK\n", alloc->pid,
@@ -192,25 +204,26 @@
 
 	trace_binder_update_page_range(alloc, allocate, start, end);
 
-	if (vma)
-		mm = NULL;
-	else
-		mm = get_task_mm(alloc->tsk);
+	if (allocate == 0)
+		goto free_range;
+
+	for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
+		page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+		if (!page->page_ptr) {
+			need_mm = true;
+			break;
+		}
+	}
+
+	if (need_mm && mmget_not_zero(alloc->vma_vm_mm))
+		mm = alloc->vma_vm_mm;
 
 	if (mm) {
 		down_write(&mm->mmap_sem);
 		vma = alloc->vma;
-		if (vma && mm != alloc->vma_vm_mm) {
-			pr_err("%d: vma mm and task mm mismatch\n",
-				alloc->pid);
-			vma = NULL;
-		}
 	}
 
-	if (allocate == 0)
-		goto free_range;
-
-	if (vma == NULL) {
+	if (!vma && need_mm) {
 		pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n",
 			alloc->pid);
 		goto err_no_vma;
@@ -218,18 +231,40 @@
 
 	for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
 		int ret;
+		bool on_lru;
+		size_t index;
 
-		page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
+		index = (page_addr - alloc->buffer) / PAGE_SIZE;
+		page = &alloc->pages[index];
 
-		BUG_ON(*page);
-		*page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
-		if (*page == NULL) {
+		if (page->page_ptr) {
+			trace_binder_alloc_lru_start(alloc, index);
+
+			on_lru = list_lru_del(&binder_alloc_lru, &page->lru);
+			WARN_ON(!on_lru);
+
+			trace_binder_alloc_lru_end(alloc, index);
+			continue;
+		}
+
+		if (WARN_ON(!vma))
+			goto err_page_ptr_cleared;
+
+		trace_binder_alloc_page_start(alloc, index);
+		page->page_ptr = alloc_page(GFP_KERNEL |
+					    __GFP_HIGHMEM |
+					    __GFP_ZERO);
+		if (!page->page_ptr) {
 			pr_err("%d: binder_alloc_buf failed for page at %pK\n",
 				alloc->pid, page_addr);
 			goto err_alloc_page_failed;
 		}
+		page->alloc = alloc;
+		INIT_LIST_HEAD(&page->lru);
+
 		ret = map_kernel_range_noflush((unsigned long)page_addr,
-					PAGE_SIZE, PAGE_KERNEL, page);
+					       PAGE_SIZE, PAGE_KERNEL,
+					       &page->page_ptr);
 		flush_cache_vmap((unsigned long)page_addr,
 				(unsigned long)page_addr + PAGE_SIZE);
 		if (ret != 1) {
@@ -239,12 +274,14 @@
 		}
 		user_page_addr =
 			(uintptr_t)page_addr + alloc->user_buffer_offset;
-		ret = vm_insert_page(vma, user_page_addr, page[0]);
+		ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr);
 		if (ret) {
 			pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n",
 			       alloc->pid, user_page_addr);
 			goto err_vm_insert_page_failed;
 		}
+
+		trace_binder_alloc_page_end(alloc, index);
 		/* vm_insert_page does not seem to increment the refcount */
 	}
 	if (mm) {
@@ -256,16 +293,27 @@
 free_range:
 	for (page_addr = end - PAGE_SIZE; page_addr >= start;
 	     page_addr -= PAGE_SIZE) {
-		page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE];
-		if (vma)
-			zap_page_range(vma, (uintptr_t)page_addr +
-				alloc->user_buffer_offset, PAGE_SIZE, NULL);
+		bool ret;
+		size_t index;
+
+		index = (page_addr - alloc->buffer) / PAGE_SIZE;
+		page = &alloc->pages[index];
+
+		trace_binder_free_lru_start(alloc, index);
+
+		ret = list_lru_add(&binder_alloc_lru, &page->lru);
+		WARN_ON(!ret);
+
+		trace_binder_free_lru_end(alloc, index);
+		continue;
+
 err_vm_insert_page_failed:
 		unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
 err_map_kernel_failed:
-		__free_page(*page);
-		*page = NULL;
+		__free_page(page->page_ptr);
+		page->page_ptr = NULL;
 err_alloc_page_failed:
+err_page_ptr_cleared:
 		;
 	}
 err_no_vma:
@@ -321,6 +369,9 @@
 		return ERR_PTR(-ENOSPC);
 	}
 
+	/* Pad 0-size buffers so they get assigned unique addresses */
+	size = max(size, sizeof(void *));
+
 	while (n) {
 		buffer = rb_entry(n, struct binder_buffer, rb_node);
 		BUG_ON(!buffer->free);
@@ -380,32 +431,35 @@
 
 	has_page_addr =
 		(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK);
-	if (n == NULL) {
-		if (size + sizeof(struct binder_buffer) + 4 >= buffer_size)
-			buffer_size = size; /* no room for other buffers */
-		else
-			buffer_size = size + sizeof(struct binder_buffer);
-	}
+	WARN_ON(n && buffer_size != size);
 	end_page_addr =
-		(void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size);
+		(void *)PAGE_ALIGN((uintptr_t)buffer->data + size);
 	if (end_page_addr > has_page_addr)
 		end_page_addr = has_page_addr;
 	ret = binder_update_page_range(alloc, 1,
-	    (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL);
+	    (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr);
 	if (ret)
 		return ERR_PTR(ret);
 
+	if (buffer_size != size) {
+		struct binder_buffer *new_buffer;
+
+		new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+		if (!new_buffer) {
+			pr_err("%s: %d failed to alloc new buffer struct\n",
+			       __func__, alloc->pid);
+			goto err_alloc_buf_struct_failed;
+		}
+		new_buffer->data = (u8 *)buffer->data + size;
+		list_add(&new_buffer->entry, &buffer->entry);
+		new_buffer->free = 1;
+		binder_insert_free_buffer(alloc, new_buffer);
+	}
+
 	rb_erase(best_fit, &alloc->free_buffers);
 	buffer->free = 0;
 	buffer->free_in_progress = 0;
 	binder_insert_allocated_buffer_locked(alloc, buffer);
-	if (buffer_size != size) {
-		struct binder_buffer *new_buffer = (void *)buffer->data + size;
-
-		list_add(&new_buffer->entry, &buffer->entry);
-		new_buffer->free = 1;
-		binder_insert_free_buffer(alloc, new_buffer);
-	}
 	binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
 		     "%d: binder_alloc_buf size %zd got %pK\n",
 		      alloc->pid, size, buffer);
@@ -420,6 +474,12 @@
 			      alloc->pid, size, alloc->free_async_space);
 	}
 	return buffer;
+
+err_alloc_buf_struct_failed:
+	binder_update_page_range(alloc, 0,
+				 (void *)PAGE_ALIGN((uintptr_t)buffer->data),
+				 end_page_addr);
+	return ERR_PTR(-ENOMEM);
 }
 
 /**
@@ -454,57 +514,58 @@
 
 static void *buffer_start_page(struct binder_buffer *buffer)
 {
-	return (void *)((uintptr_t)buffer & PAGE_MASK);
+	return (void *)((uintptr_t)buffer->data & PAGE_MASK);
 }
 
-static void *buffer_end_page(struct binder_buffer *buffer)
+static void *prev_buffer_end_page(struct binder_buffer *buffer)
 {
-	return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK);
+	return (void *)(((uintptr_t)(buffer->data) - 1) & PAGE_MASK);
 }
 
 static void binder_delete_free_buffer(struct binder_alloc *alloc,
 				      struct binder_buffer *buffer)
 {
 	struct binder_buffer *prev, *next = NULL;
-	int free_page_end = 1;
-	int free_page_start = 1;
-
+	bool to_free = true;
 	BUG_ON(alloc->buffers.next == &buffer->entry);
-	prev = list_entry(buffer->entry.prev, struct binder_buffer, entry);
+	prev = binder_buffer_prev(buffer);
 	BUG_ON(!prev->free);
-	if (buffer_end_page(prev) == buffer_start_page(buffer)) {
-		free_page_start = 0;
-		if (buffer_end_page(prev) == buffer_end_page(buffer))
-			free_page_end = 0;
+	if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) {
+		to_free = false;
 		binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
-			     "%d: merge free, buffer %pK share page with %pK\n",
-			      alloc->pid, buffer, prev);
+				   "%d: merge free, buffer %pK share page with %pK\n",
+				   alloc->pid, buffer->data, prev->data);
 	}
 
 	if (!list_is_last(&buffer->entry, &alloc->buffers)) {
-		next = list_entry(buffer->entry.next,
-				  struct binder_buffer, entry);
-		if (buffer_start_page(next) == buffer_end_page(buffer)) {
-			free_page_end = 0;
-			if (buffer_start_page(next) ==
-			    buffer_start_page(buffer))
-				free_page_start = 0;
+		next = binder_buffer_next(buffer);
+		if (buffer_start_page(next) == buffer_start_page(buffer)) {
+			to_free = false;
 			binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
-				     "%d: merge free, buffer %pK share page with %pK\n",
-				      alloc->pid, buffer, prev);
+					   "%d: merge free, buffer %pK share page with %pK\n",
+					   alloc->pid,
+					   buffer->data,
+					   next->data);
 		}
 	}
-	list_del(&buffer->entry);
-	if (free_page_start || free_page_end) {
+
+	if (PAGE_ALIGNED(buffer->data)) {
 		binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
-			     "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n",
-			     alloc->pid, buffer, free_page_start ? "" : " end",
-			     free_page_end ? "" : " start", prev, next);
-		binder_update_page_range(alloc, 0, free_page_start ?
-			buffer_start_page(buffer) : buffer_end_page(buffer),
-			(free_page_end ? buffer_end_page(buffer) :
-			buffer_start_page(buffer)) + PAGE_SIZE, NULL);
+				   "%d: merge free, buffer start %pK is page aligned\n",
+				   alloc->pid, buffer->data);
+		to_free = false;
 	}
+
+	if (to_free) {
+		binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
+				   "%d: merge free, buffer %pK do not share page with %pK or %pK\n",
+				   alloc->pid, buffer->data,
+				   prev->data, next ? next->data : NULL);
+		binder_update_page_range(alloc, 0, buffer_start_page(buffer),
+					 buffer_start_page(buffer) + PAGE_SIZE);
+	}
+	list_del(&buffer->entry);
+	kfree(buffer);
 }
 
 static void binder_free_buf_locked(struct binder_alloc *alloc,
@@ -525,8 +586,8 @@
 	BUG_ON(buffer->free);
 	BUG_ON(size > buffer_size);
 	BUG_ON(buffer->transaction != NULL);
-	BUG_ON((void *)buffer < alloc->buffer);
-	BUG_ON((void *)buffer > alloc->buffer + alloc->buffer_size);
+	BUG_ON(buffer->data < alloc->buffer);
+	BUG_ON(buffer->data > alloc->buffer + alloc->buffer_size);
 
 	if (buffer->async_transaction) {
 		alloc->free_async_space += size + sizeof(struct binder_buffer);
@@ -538,14 +599,12 @@
 
 	binder_update_page_range(alloc, 0,
 		(void *)PAGE_ALIGN((uintptr_t)buffer->data),
-		(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
-		NULL);
+		(void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK));
 
 	rb_erase(&buffer->rb_node, &alloc->allocated_buffers);
 	buffer->free = 1;
 	if (!list_is_last(&buffer->entry, &alloc->buffers)) {
-		struct binder_buffer *next = list_entry(buffer->entry.next,
-						struct binder_buffer, entry);
+		struct binder_buffer *next = binder_buffer_next(buffer);
 
 		if (next->free) {
 			rb_erase(&next->rb_node, &alloc->free_buffers);
@@ -553,8 +612,7 @@
 		}
 	}
 	if (alloc->buffers.next != &buffer->entry) {
-		struct binder_buffer *prev = list_entry(buffer->entry.prev,
-						struct binder_buffer, entry);
+		struct binder_buffer *prev = binder_buffer_prev(buffer);
 
 		if (prev->free) {
 			binder_delete_free_buffer(alloc, buffer);
@@ -640,14 +698,14 @@
 	}
 	alloc->buffer_size = vma->vm_end - vma->vm_start;
 
-	if (binder_update_page_range(alloc, 1, alloc->buffer,
-				     alloc->buffer + PAGE_SIZE, vma)) {
+	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+	if (!buffer) {
 		ret = -ENOMEM;
-		failure_string = "alloc small buf";
-		goto err_alloc_small_buf_failed;
+		failure_string = "alloc buffer struct";
+		goto err_alloc_buf_struct_failed;
 	}
-	buffer = alloc->buffer;
-	INIT_LIST_HEAD(&alloc->buffers);
+
+	buffer->data = alloc->buffer;
 	list_add(&buffer->entry, &alloc->buffers);
 	buffer->free = 1;
 	binder_insert_free_buffer(alloc, buffer);
@@ -655,10 +713,12 @@
 	barrier();
 	alloc->vma = vma;
 	alloc->vma_vm_mm = vma->vm_mm;
+	/* Same as mmgrab() in later kernel versions */
+	atomic_inc(&alloc->vma_vm_mm->mm_count);
 
 	return 0;
 
-err_alloc_small_buf_failed:
+err_alloc_buf_struct_failed:
 	kfree(alloc->pages);
 	alloc->pages = NULL;
 err_alloc_pages_failed:
@@ -678,14 +738,13 @@
 {
 	struct rb_node *n;
 	int buffers, page_count;
+	struct binder_buffer *buffer;
 
 	BUG_ON(alloc->vma);
 
 	buffers = 0;
 	mutex_lock(&alloc->mutex);
 	while ((n = rb_first(&alloc->allocated_buffers))) {
-		struct binder_buffer *buffer;
-
 		buffer = rb_entry(n, struct binder_buffer, rb_node);
 
 		/* Transaction should already have been freed */
@@ -695,28 +754,44 @@
 		buffers++;
 	}
 
+	while (!list_empty(&alloc->buffers)) {
+		buffer = list_first_entry(&alloc->buffers,
+					  struct binder_buffer, entry);
+		WARN_ON(!buffer->free);
+
+		list_del(&buffer->entry);
+		WARN_ON_ONCE(!list_empty(&alloc->buffers));
+		kfree(buffer);
+	}
+
 	page_count = 0;
 	if (alloc->pages) {
 		int i;
 
 		for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
 			void *page_addr;
+			bool on_lru;
 
-			if (!alloc->pages[i])
+			if (!alloc->pages[i].page_ptr)
 				continue;
 
+			on_lru = list_lru_del(&binder_alloc_lru,
+					      &alloc->pages[i].lru);
 			page_addr = alloc->buffer + i * PAGE_SIZE;
 			binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC,
-				     "%s: %d: page %d at %pK not freed\n",
-				     __func__, alloc->pid, i, page_addr);
+				     "%s: %d: page %d at %pK %s\n",
+				     __func__, alloc->pid, i, page_addr,
+				     on_lru ? "on lru" : "active");
 			unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
-			__free_page(alloc->pages[i]);
+			__free_page(alloc->pages[i].page_ptr);
 			page_count++;
 		}
 		kfree(alloc->pages);
 		vfree(alloc->buffer);
 	}
 	mutex_unlock(&alloc->mutex);
+	if (alloc->vma_vm_mm)
+		mmdrop(alloc->vma_vm_mm);
 
 	binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE,
 		     "%s: %d buffers %d, pages %d\n",
@@ -754,6 +829,34 @@
 }
 
 /**
+ * binder_alloc_print_pages() - print page usage
+ * @m:     seq_file for output via seq_printf()
+ * @alloc: binder_alloc for this proc
+ */
+void binder_alloc_print_pages(struct seq_file *m,
+			      struct binder_alloc *alloc)
+{
+	struct binder_lru_page *page;
+	int i;
+	int active = 0;
+	int lru = 0;
+	int free = 0;
+
+	mutex_lock(&alloc->mutex);
+	for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) {
+		page = &alloc->pages[i];
+		if (!page->page_ptr)
+			free++;
+		else if (list_empty(&page->lru))
+			active++;
+		else
+			lru++;
+	}
+	mutex_unlock(&alloc->mutex);
+	seq_printf(m, "  pages: %d:%d:%d\n", active, lru, free);
+}
+
+/**
  * binder_alloc_get_allocated_count() - return count of buffers
  * @alloc: binder_alloc for this proc
  *
@@ -783,10 +886,111 @@
 void binder_alloc_vma_close(struct binder_alloc *alloc)
 {
 	WRITE_ONCE(alloc->vma, NULL);
-	WRITE_ONCE(alloc->vma_vm_mm, NULL);
 }
 
 /**
+ * binder_alloc_free_page() - shrinker callback to free pages
+ * @item:   item to free
+ * @lock:   lock protecting the item
+ * @cb_arg: callback argument
+ *
+ * Called from list_lru_walk() in binder_shrink_scan() to free
+ * up pages when the system is under memory pressure.
+ */
+enum lru_status binder_alloc_free_page(struct list_head *item,
+				       struct list_lru_one *lru,
+				       spinlock_t *lock,
+				       void *cb_arg)
+{
+	struct mm_struct *mm = NULL;
+	struct binder_lru_page *page = container_of(item,
+						    struct binder_lru_page,
+						    lru);
+	struct binder_alloc *alloc;
+	uintptr_t page_addr;
+	size_t index;
+	struct vm_area_struct *vma;
+
+	alloc = page->alloc;
+	if (!mutex_trylock(&alloc->mutex))
+		goto err_get_alloc_mutex_failed;
+
+	if (!page->page_ptr)
+		goto err_page_already_freed;
+
+	index = page - alloc->pages;
+	page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE;
+	vma = alloc->vma;
+	if (vma) {
+		if (!mmget_not_zero(alloc->vma_vm_mm))
+			goto err_mmget;
+		mm = alloc->vma_vm_mm;
+		if (!down_write_trylock(&mm->mmap_sem))
+			goto err_down_write_mmap_sem_failed;
+	}
+
+	list_lru_isolate(lru, item);
+	spin_unlock(lock);
+
+	if (vma) {
+		trace_binder_unmap_user_start(alloc, index);
+
+		zap_page_range(vma,
+			       page_addr +
+			       alloc->user_buffer_offset,
+			       PAGE_SIZE, NULL);
+
+		trace_binder_unmap_user_end(alloc, index);
+
+		up_write(&mm->mmap_sem);
+		mmput(mm);
+	}
+
+	trace_binder_unmap_kernel_start(alloc, index);
+
+	unmap_kernel_range(page_addr, PAGE_SIZE);
+	__free_page(page->page_ptr);
+	page->page_ptr = NULL;
+
+	trace_binder_unmap_kernel_end(alloc, index);
+
+	spin_lock(lock);
+	mutex_unlock(&alloc->mutex);
+	return LRU_REMOVED_RETRY;
+
+err_down_write_mmap_sem_failed:
+	mmput_async(mm);
+err_mmget:
+err_page_already_freed:
+	mutex_unlock(&alloc->mutex);
+err_get_alloc_mutex_failed:
+	return LRU_SKIP;
+}
+
+static unsigned long
+binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
+{
+	unsigned long ret = list_lru_count(&binder_alloc_lru);
+	return ret;
+}
+
+static unsigned long
+binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
+{
+	unsigned long ret;
+
+	ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
+			    NULL, sc->nr_to_scan);
+	return ret;
+}
+
+static struct shrinker binder_shrinker = {
+	.count_objects = binder_shrink_count,
+	.scan_objects = binder_shrink_scan,
+	.seeks = DEFAULT_SEEKS,
+};
+
+/**
  * binder_alloc_init() - called by binder_open() for per-proc initialization
  * @alloc: binder_alloc for this proc
  *
@@ -795,8 +999,13 @@
  */
 void binder_alloc_init(struct binder_alloc *alloc)
 {
-	alloc->tsk = current->group_leader;
 	alloc->pid = current->group_leader->pid;
 	mutex_init(&alloc->mutex);
+	INIT_LIST_HEAD(&alloc->buffers);
 }
 
+void binder_alloc_shrinker_init(void)
+{
+	list_lru_init(&binder_alloc_lru);
+	register_shrinker(&binder_shrinker);
+}
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index 088e4ff..2dd33b6 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -21,7 +21,9 @@
 #include <linux/rtmutex.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
+#include <linux/list_lru.h>
 
+extern struct list_lru binder_alloc_lru;
 struct binder_transaction;
 
 /**
@@ -57,7 +59,19 @@
 	size_t data_size;
 	size_t offsets_size;
 	size_t extra_buffers_size;
-	uint8_t data[0];
+	void *data;
+};
+
+/**
+ * struct binder_lru_page - page object used for binder shrinker
+ * @page_ptr: pointer to physical page in mmap'd space
+ * @lru:      entry in binder_alloc_lru
+ * @alloc:    binder_alloc for a proc
+ */
+struct binder_lru_page {
+	struct list_head lru;
+	struct page *page_ptr;
+	struct binder_alloc *alloc;
 };
 
 /**
@@ -75,8 +89,7 @@
  * @allocated_buffers:  rb tree of allocated buffers sorted by address
  * @free_async_space:   VA space available for async buffers. This is
  *                      initialized at mmap time to 1/2 the full VA space
- * @pages:              array of physical page addresses for each
- *                      page of mmap'd space
+ * @pages:              array of binder_lru_page
  * @buffer_size:        size of address space specified via mmap
  * @pid:                pid for associated binder_proc (invariant after init)
  *
@@ -87,7 +100,6 @@
  */
 struct binder_alloc {
 	struct mutex mutex;
-	struct task_struct *tsk;
 	struct vm_area_struct *vma;
 	struct mm_struct *vma_vm_mm;
 	void *buffer;
@@ -96,18 +108,27 @@
 	struct rb_root free_buffers;
 	struct rb_root allocated_buffers;
 	size_t free_async_space;
-	struct page **pages;
+	struct binder_lru_page *pages;
 	size_t buffer_size;
 	uint32_t buffer_free;
 	int pid;
 };
 
+#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST
+void binder_selftest_alloc(struct binder_alloc *alloc);
+#else
+static inline void binder_selftest_alloc(struct binder_alloc *alloc) {}
+#endif
+enum lru_status binder_alloc_free_page(struct list_head *item,
+				       struct list_lru_one *lru,
+				       spinlock_t *lock, void *cb_arg);
 extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
 						  size_t data_size,
 						  size_t offsets_size,
 						  size_t extra_buffers_size,
 						  int is_async);
 extern void binder_alloc_init(struct binder_alloc *alloc);
+void binder_alloc_shrinker_init(void);
 extern void binder_alloc_vma_close(struct binder_alloc *alloc);
 extern struct binder_buffer *
 binder_alloc_prepare_to_free(struct binder_alloc *alloc,
@@ -120,6 +141,8 @@
 extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc);
 extern void binder_alloc_print_allocated(struct seq_file *m,
 					 struct binder_alloc *alloc);
+void binder_alloc_print_pages(struct seq_file *m,
+			      struct binder_alloc *alloc);
 
 /**
  * binder_alloc_get_free_async_space() - get free space available for async
diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c
new file mode 100644
index 0000000..8bd7bce
--- /dev/null
+++ b/drivers/android/binder_alloc_selftest.c
@@ -0,0 +1,310 @@
+/* binder_alloc_selftest.c
+ *
+ * Android IPC Subsystem
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/mm_types.h>
+#include <linux/err.h>
+#include "binder_alloc.h"
+
+#define BUFFER_NUM 5
+#define BUFFER_MIN_SIZE (PAGE_SIZE / 8)
+
+static bool binder_selftest_run = true;
+static int binder_selftest_failures;
+static DEFINE_MUTEX(binder_selftest_lock);
+
+/**
+ * enum buf_end_align_type - Page alignment of a buffer
+ * end with regard to the end of the previous buffer.
+ *
+ * In the pictures below, buf2 refers to the buffer we
+ * are aligning. buf1 refers to previous buffer by addr.
+ * Symbol [ means the start of a buffer, ] means the end
+ * of a buffer, and | means page boundaries.
+ */
+enum buf_end_align_type {
+	/**
+	 * @SAME_PAGE_UNALIGNED: The end of this buffer is on
+	 * the same page as the end of the previous buffer and
+	 * is not page aligned. Examples:
+	 * buf1 ][ buf2 ][ ...
+	 * buf1 ]|[ buf2 ][ ...
+	 */
+	SAME_PAGE_UNALIGNED = 0,
+	/**
+	 * @SAME_PAGE_ALIGNED: When the end of the previous buffer
+	 * is not page aligned, the end of this buffer is on the
+	 * same page as the end of the previous buffer and is page
+	 * aligned. When the previous buffer is page aligned, the
+	 * end of this buffer is aligned to the next page boundary.
+	 * Examples:
+	 * buf1 ][ buf2 ]| ...
+	 * buf1 ]|[ buf2 ]| ...
+	 */
+	SAME_PAGE_ALIGNED,
+	/**
+	 * @NEXT_PAGE_UNALIGNED: The end of this buffer is on
+	 * the page next to the end of the previous buffer and
+	 * is not page aligned. Examples:
+	 * buf1 ][ buf2 | buf2 ][ ...
+	 * buf1 ]|[ buf2 | buf2 ][ ...
+	 */
+	NEXT_PAGE_UNALIGNED,
+	/**
+	 * @NEXT_PAGE_ALIGNED: The end of this buffer is on
+	 * the page next to the end of the previous buffer and
+	 * is page aligned. Examples:
+	 * buf1 ][ buf2 | buf2 ]| ...
+	 * buf1 ]|[ buf2 | buf2 ]| ...
+	 */
+	NEXT_PAGE_ALIGNED,
+	/**
+	 * @NEXT_NEXT_UNALIGNED: The end of this buffer is on
+	 * the page that follows the page after the end of the
+	 * previous buffer and is not page aligned. Examples:
+	 * buf1 ][ buf2 | buf2 | buf2 ][ ...
+	 * buf1 ]|[ buf2 | buf2 | buf2 ][ ...
+	 */
+	NEXT_NEXT_UNALIGNED,
+	LOOP_END,
+};
+
+static void pr_err_size_seq(size_t *sizes, int *seq)
+{
+	int i;
+
+	pr_err("alloc sizes: ");
+	for (i = 0; i < BUFFER_NUM; i++)
+		pr_cont("[%zu]", sizes[i]);
+	pr_cont("\n");
+	pr_err("free seq: ");
+	for (i = 0; i < BUFFER_NUM; i++)
+		pr_cont("[%d]", seq[i]);
+	pr_cont("\n");
+}
+
+static bool check_buffer_pages_allocated(struct binder_alloc *alloc,
+					 struct binder_buffer *buffer,
+					 size_t size)
+{
+	void *page_addr, *end;
+	int page_index;
+
+	end = (void *)PAGE_ALIGN((uintptr_t)buffer->data + size);
+	page_addr = buffer->data;
+	for (; page_addr < end; page_addr += PAGE_SIZE) {
+		page_index = (page_addr - alloc->buffer) / PAGE_SIZE;
+		if (!alloc->pages[page_index].page_ptr ||
+		    !list_empty(&alloc->pages[page_index].lru)) {
+			pr_err("expect alloc but is %s at page index %d\n",
+			       alloc->pages[page_index].page_ptr ?
+			       "lru" : "free", page_index);
+			return false;
+		}
+	}
+	return true;
+}
+
+static void binder_selftest_alloc_buf(struct binder_alloc *alloc,
+				      struct binder_buffer *buffers[],
+				      size_t *sizes, int *seq)
+{
+	int i;
+
+	for (i = 0; i < BUFFER_NUM; i++) {
+		buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0);
+		if (IS_ERR(buffers[i]) ||
+		    !check_buffer_pages_allocated(alloc, buffers[i],
+						  sizes[i])) {
+			pr_err_size_seq(sizes, seq);
+			binder_selftest_failures++;
+		}
+	}
+}
+
+static void binder_selftest_free_buf(struct binder_alloc *alloc,
+				     struct binder_buffer *buffers[],
+				     size_t *sizes, int *seq, size_t end)
+{
+	int i;
+
+	for (i = 0; i < BUFFER_NUM; i++)
+		binder_alloc_free_buf(alloc, buffers[seq[i]]);
+
+	for (i = 0; i < end / PAGE_SIZE; i++) {
+		/**
+		 * Error message on a free page can be false positive
+		 * if binder shrinker ran during binder_alloc_free_buf
+		 * calls above.
+		 */
+		if (list_empty(&alloc->pages[i].lru)) {
+			pr_err_size_seq(sizes, seq);
+			pr_err("expect lru but is %s at page index %d\n",
+			       alloc->pages[i].page_ptr ? "alloc" : "free", i);
+			binder_selftest_failures++;
+		}
+	}
+}
+
+static void binder_selftest_free_page(struct binder_alloc *alloc)
+{
+	int i;
+	unsigned long count;
+
+	while ((count = list_lru_count(&binder_alloc_lru))) {
+		list_lru_walk(&binder_alloc_lru, binder_alloc_free_page,
+			      NULL, count);
+	}
+
+	for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) {
+		if (alloc->pages[i].page_ptr) {
+			pr_err("expect free but is %s at page index %d\n",
+			       list_empty(&alloc->pages[i].lru) ?
+			       "alloc" : "lru", i);
+			binder_selftest_failures++;
+		}
+	}
+}
+
+static void binder_selftest_alloc_free(struct binder_alloc *alloc,
+				       size_t *sizes, int *seq, size_t end)
+{
+	struct binder_buffer *buffers[BUFFER_NUM];
+
+	binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
+	binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
+
+	/* Allocate from lru. */
+	binder_selftest_alloc_buf(alloc, buffers, sizes, seq);
+	if (list_lru_count(&binder_alloc_lru))
+		pr_err("lru list should be empty but is not\n");
+
+	binder_selftest_free_buf(alloc, buffers, sizes, seq, end);
+	binder_selftest_free_page(alloc);
+}
+
+static bool is_dup(int *seq, int index, int val)
+{
+	int i;
+
+	for (i = 0; i < index; i++) {
+		if (seq[i] == val)
+			return true;
+	}
+	return false;
+}
+
+/* Generate BUFFER_NUM factorial free orders. */
+static void binder_selftest_free_seq(struct binder_alloc *alloc,
+				     size_t *sizes, int *seq,
+				     int index, size_t end)
+{
+	int i;
+
+	if (index == BUFFER_NUM) {
+		binder_selftest_alloc_free(alloc, sizes, seq, end);
+		return;
+	}
+	for (i = 0; i < BUFFER_NUM; i++) {
+		if (is_dup(seq, index, i))
+			continue;
+		seq[index] = i;
+		binder_selftest_free_seq(alloc, sizes, seq, index + 1, end);
+	}
+}
+
+static void binder_selftest_alloc_size(struct binder_alloc *alloc,
+				       size_t *end_offset)
+{
+	int i;
+	int seq[BUFFER_NUM] = {0};
+	size_t front_sizes[BUFFER_NUM];
+	size_t back_sizes[BUFFER_NUM];
+	size_t last_offset, offset = 0;
+
+	for (i = 0; i < BUFFER_NUM; i++) {
+		last_offset = offset;
+		offset = end_offset[i];
+		front_sizes[i] = offset - last_offset;
+		back_sizes[BUFFER_NUM - i - 1] = front_sizes[i];
+	}
+	/*
+	 * Buffers share the first or last few pages.
+	 * Only BUFFER_NUM - 1 buffer sizes are adjustable since
+	 * we need one giant buffer before getting to the last page.
+	 */
+	back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1];
+	binder_selftest_free_seq(alloc, front_sizes, seq, 0,
+				 end_offset[BUFFER_NUM - 1]);
+	binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size);
+}
+
+static void binder_selftest_alloc_offset(struct binder_alloc *alloc,
+					 size_t *end_offset, int index)
+{
+	int align;
+	size_t end, prev;
+
+	if (index == BUFFER_NUM) {
+		binder_selftest_alloc_size(alloc, end_offset);
+		return;
+	}
+	prev = index == 0 ? 0 : end_offset[index - 1];
+	end = prev;
+
+	BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE);
+
+	for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) {
+		if (align % 2)
+			end = ALIGN(end, PAGE_SIZE);
+		else
+			end += BUFFER_MIN_SIZE;
+		end_offset[index] = end;
+		binder_selftest_alloc_offset(alloc, end_offset, index + 1);
+	}
+}
+
+/**
+ * binder_selftest_alloc() - Test alloc and free of buffer pages.
+ * @alloc: Pointer to alloc struct.
+ *
+ * Allocate BUFFER_NUM buffers to cover all page alignment cases,
+ * then free them in all orders possible. Check that pages are
+ * correctly allocated, put onto lru when buffers are freed, and
+ * are freed when binder_alloc_free_page is called.
+ */
+void binder_selftest_alloc(struct binder_alloc *alloc)
+{
+	size_t end_offset[BUFFER_NUM];
+
+	if (!binder_selftest_run)
+		return;
+	mutex_lock(&binder_selftest_lock);
+	if (!binder_selftest_run || !alloc->vma)
+		goto done;
+	pr_info("STARTED\n");
+	binder_selftest_alloc_offset(alloc, end_offset, 0);
+	binder_selftest_run = false;
+	if (binder_selftest_failures > 0)
+		pr_info("%d tests FAILED\n", binder_selftest_failures);
+	else
+		pr_info("PASSED\n");
+
+done:
+	mutex_unlock(&binder_selftest_lock);
+}
diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h
index 7967db1..b11dffc 100644
--- a/drivers/android/binder_trace.h
+++ b/drivers/android/binder_trace.h
@@ -85,6 +85,30 @@
 DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_write_done);
 DEFINE_BINDER_FUNCTION_RETURN_EVENT(binder_read_done);
 
+TRACE_EVENT(binder_set_priority,
+	TP_PROTO(int proc, int thread, unsigned int old_prio,
+		 unsigned int desired_prio, unsigned int new_prio),
+	TP_ARGS(proc, thread, old_prio, new_prio, desired_prio),
+
+	TP_STRUCT__entry(
+		__field(int, proc)
+		__field(int, thread)
+		__field(unsigned int, old_prio)
+		__field(unsigned int, new_prio)
+		__field(unsigned int, desired_prio)
+	),
+	TP_fast_assign(
+		__entry->proc = proc;
+		__entry->thread = thread;
+		__entry->old_prio = old_prio;
+		__entry->new_prio = new_prio;
+		__entry->desired_prio = desired_prio;
+	),
+	TP_printk("proc=%d thread=%d old=%d => new=%d desired=%d",
+		  __entry->proc, __entry->thread, __entry->old_prio,
+		  __entry->new_prio, __entry->desired_prio)
+);
+
 TRACE_EVENT(binder_wait_for_work,
 	TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo),
 	TP_ARGS(proc_work, transaction_stack, thread_todo),
@@ -291,6 +315,61 @@
 		  __entry->offset, __entry->size)
 );
 
+DECLARE_EVENT_CLASS(binder_lru_page_class,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index),
+	TP_STRUCT__entry(
+		__field(int, proc)
+		__field(size_t, page_index)
+	),
+	TP_fast_assign(
+		__entry->proc = alloc->pid;
+		__entry->page_index = page_index;
+	),
+	TP_printk("proc=%d page_index=%zu",
+		  __entry->proc, __entry->page_index)
+);
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_start,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_end,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_free_lru_start,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_free_lru_end,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_start,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_end,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_start,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_end,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_start,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
+DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_end,
+	TP_PROTO(const struct binder_alloc *alloc, size_t page_index),
+	TP_ARGS(alloc, page_index));
+
 TRACE_EVENT(binder_command,
 	TP_PROTO(uint32_t cmd),
 	TP_ARGS(cmd),
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 8e575fb..e3e10e8 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -2971,10 +2971,12 @@
 static struct ata_device *ata_find_dev(struct ata_port *ap, int devno)
 {
 	if (!sata_pmp_attached(ap)) {
-		if (likely(devno < ata_link_max_devices(&ap->link)))
+		if (likely(devno >= 0 &&
+			   devno < ata_link_max_devices(&ap->link)))
 			return &ap->link.device[devno];
 	} else {
-		if (likely(devno < ap->nr_pmp_links))
+		if (likely(devno >= 0 &&
+			   devno < ap->nr_pmp_links))
 			return &ap->pmp_link[devno].device[0];
 	}
 
diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c
index 7ef16c0..20e2b7a 100644
--- a/drivers/ata/libata-transport.c
+++ b/drivers/ata/libata-transport.c
@@ -224,7 +224,6 @@
 
 static void ata_tport_release(struct device *dev)
 {
-	put_device(dev->parent);
 }
 
 /**
@@ -284,7 +283,7 @@
 	device_initialize(dev);
 	dev->type = &ata_port_type;
 
-	dev->parent = get_device(parent);
+	dev->parent = parent;
 	dev->release = ata_tport_release;
 	dev_set_name(dev, "ata%d", ap->print_id);
 	transport_setup_device(dev);
@@ -348,7 +347,6 @@
 
 static void ata_tlink_release(struct device *dev)
 {
-	put_device(dev->parent);
 }
 
 /**
@@ -410,7 +408,7 @@
 	int error;
 
 	device_initialize(dev);
-	dev->parent = get_device(&ap->tdev);
+	dev->parent = &ap->tdev;
 	dev->release = ata_tlink_release;
 	if (ata_is_host_link(link))
 		dev_set_name(dev, "link%d", ap->print_id);
@@ -589,7 +587,6 @@
 
 static void ata_tdev_release(struct device *dev)
 {
-	put_device(dev->parent);
 }
 
 /**
@@ -662,7 +659,7 @@
 	int error;
 
 	device_initialize(dev);
-	dev->parent = get_device(&link->tdev);
+	dev->parent = &link->tdev;
 	dev->release = ata_tdev_release;
 	if (ata_is_host_link(link))
 		dev_set_name(dev, "dev%d.%d", ap->print_id,ata_dev->devno);
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index 8d4d959..8706533 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -616,6 +616,7 @@
 	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP73_IDE),	8 },
 	{ PCI_VDEVICE(NVIDIA,	PCI_DEVICE_ID_NVIDIA_NFORCE_MCP77_IDE),	8 },
 	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_CS5536_IDE),		9 },
+	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_CS5536_DEV_IDE),	9 },
 
 	{ },
 };
diff --git a/drivers/ata/pata_cs5536.c b/drivers/ata/pata_cs5536.c
index 6c15a55..dc12552 100644
--- a/drivers/ata/pata_cs5536.c
+++ b/drivers/ata/pata_cs5536.c
@@ -289,6 +289,7 @@
 
 static const struct pci_device_id cs5536[] = {
 	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_CS5536_IDE), },
+	{ PCI_VDEVICE(AMD,	PCI_DEVICE_ID_AMD_CS5536_DEV_IDE), },
 	{ },
 };
 
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 0636d84..f3f538e 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -644,14 +644,16 @@
 		pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8);
 	}
 
-	/* enable IRQ on hotplug */
-	pci_read_config_byte(pdev, SVIA_MISC_3, &tmp8);
-	if ((tmp8 & SATA_HOTPLUG) != SATA_HOTPLUG) {
-		dev_dbg(&pdev->dev,
-			"enabling SATA hotplug (0x%x)\n",
-			(int) tmp8);
-		tmp8 |= SATA_HOTPLUG;
-		pci_write_config_byte(pdev, SVIA_MISC_3, tmp8);
+	if (board_id == vt6421) {
+		/* enable IRQ on hotplug */
+		pci_read_config_byte(pdev, SVIA_MISC_3, &tmp8);
+		if ((tmp8 & SATA_HOTPLUG) != SATA_HOTPLUG) {
+			dev_dbg(&pdev->dev,
+				"enabling SATA hotplug (0x%x)\n",
+				(int) tmp8);
+			tmp8 |= SATA_HOTPLUG;
+			pci_write_config_byte(pdev, SVIA_MISC_3, tmp8);
+		}
 	}
 
 	/*
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 6470eb8..e32a74e 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -736,7 +736,7 @@
 
 out_unregister:
 	kobject_put(&priv->kobj);
-	kfree(drv->p);
+	/* drv->p is freed in driver_release()  */
 	drv->p = NULL;
 out_put_bus:
 	bus_put(bus);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 35ab4d5..ac43d6f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1116,13 +1116,7 @@
 	error = dpm_sysfs_add(dev);
 	if (error)
 		goto DPMError;
-	if ((dev->pm_domain) || (dev->type && dev->type->pm)
-		|| (dev->class && (dev->class->pm || dev->class->resume))
-		|| (dev->bus && (dev->bus->pm || dev->bus->resume)) ||
-		(dev->driver && dev->driver->pm)) {
-		device_pm_add(dev);
-	}
-
+	device_pm_add(dev);
 
 	if (MAJOR(dev->devt)) {
 		error = device_create_file(dev, &dev_attr_dev);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 55687b8..e17ad53 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -208,6 +208,59 @@
 
 #endif
 
+static ssize_t show_sched_load_boost(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	ssize_t rc;
+	unsigned int boost;
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int cpuid = cpu->dev.id;
+
+	boost = per_cpu(sched_load_boost, cpuid);
+	rc = snprintf(buf, PAGE_SIZE-2, "%d\n", boost);
+
+	return rc;
+}
+
+static ssize_t __ref store_sched_load_boost(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	int err;
+	int boost;
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int cpuid = cpu->dev.id;
+
+	err = kstrtoint(strstrip((char *)buf), 0, &boost);
+	if (err)
+		return err;
+
+	/*
+	 * -100 is low enough to cancel out CPU's load and make it near zro.
+	 * 1000 is close to the maximum value that cpu_util_freq_{walt,pelt}
+	 * can take without overflow.
+	 */
+	if (boost < -100 || boost > 1000)
+		return -EINVAL;
+
+	per_cpu(sched_load_boost, cpuid) = boost;
+
+	return count;
+}
+
+static DEVICE_ATTR(sched_load_boost, 0644,
+		   show_sched_load_boost,
+		   store_sched_load_boost);
+
+static struct attribute *sched_cpu_attrs[] = {
+	&dev_attr_sched_load_boost.attr,
+	NULL
+};
+
+static struct attribute_group sched_cpu_attr_group = {
+	.attrs = sched_cpu_attrs,
+};
+
 static const struct attribute_group *common_cpu_attr_groups[] = {
 #ifdef CONFIG_KEXEC
 	&crash_note_cpu_attr_group,
@@ -215,6 +268,7 @@
 #ifdef CONFIG_HOTPLUG_CPU
 	&cpu_isolated_attr_group,
 #endif
+	&sched_cpu_attr_group,
 	NULL
 };
 
@@ -225,6 +279,7 @@
 #ifdef CONFIG_HOTPLUG_CPU
 	&cpu_isolated_attr_group,
 #endif
+	&sched_cpu_attr_group,
 	NULL
 };
 
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index b0beb52..914433f 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -958,7 +958,7 @@
 		timeout = MAX_JIFFY_OFFSET;
 	}
 
-	timeout = wait_for_completion_interruptible_timeout(&buf->completion,
+	timeout = wait_for_completion_killable_timeout(&buf->completion,
 			timeout);
 	if (timeout == -ERESTARTSYS || !timeout) {
 		retval = timeout;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 5eba478..14ff403 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -858,7 +858,8 @@
 	struct platform_device *pdev = to_platform_device(dev);
 	char *driver_override, *old, *cp;
 
-	if (count > PATH_MAX)
+	/* We need to keep extra room for a newline */
+	if (count >= (PAGE_SIZE - 1))
 		return -EINVAL;
 
 	driver_override = kstrndup(buf, count, GFP_KERNEL);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index c1e56c3..dc259d2 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -162,12 +162,6 @@
 	pr_debug("PM: Moving %s:%s before %s:%s\n",
 		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
 		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
-	if (!((devb->pm_domain) || (devb->type && devb->type->pm)
-		|| (devb->class && (devb->class->pm || devb->class->resume))
-		|| (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
-		(devb->driver && devb->driver->pm))) {
-		device_pm_add(devb);
-	}
 	/* Delete deva from dpm_list and reinsert before devb. */
 	list_move_tail(&deva->power.entry, &devb->power.entry);
 }
@@ -182,12 +176,6 @@
 	pr_debug("PM: Moving %s:%s after %s:%s\n",
 		 deva->bus ? deva->bus->name : "No Bus", dev_name(deva),
 		 devb->bus ? devb->bus->name : "No Bus", dev_name(devb));
-	if (!((devb->pm_domain) || (devb->type && devb->type->pm)
-		|| (devb->class && (devb->class->pm || devb->class->resume))
-		|| (devb->bus && (devb->bus->pm || devb->bus->resume)) ||
-		(devb->driver && devb->driver->pm))) {
-		device_pm_add(devb);
-	}
 	/* Delete deva from dpm_list and reinsert after devb. */
 	list_move(&deva->power.entry, &devb->power.entry);
 }
@@ -1774,10 +1762,13 @@
 {
 	spin_lock_irq(&dev->power.lock);
 	dev->power.no_pm_callbacks =
-		(!dev->bus || pm_ops_is_empty(dev->bus->pm)) &&
-		(!dev->class || pm_ops_is_empty(dev->class->pm)) &&
+		(!dev->bus || (pm_ops_is_empty(dev->bus->pm) &&
+		 !dev->bus->suspend && !dev->bus->resume)) &&
+		(!dev->class || (pm_ops_is_empty(dev->class->pm) &&
+		 !dev->class->suspend && !dev->class->resume)) &&
 		(!dev->type || pm_ops_is_empty(dev->type->pm)) &&
 		(!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) &&
-		(!dev->driver || pm_ops_is_empty(dev->driver->pm));
+		(!dev->driver || (pm_ops_is_empty(dev->driver->pm) &&
+		 !dev->driver->suspend && !dev->driver->resume));
 	spin_unlock_irq(&dev->power.lock);
 }
diff --git a/drivers/base/power/opp/of.c b/drivers/base/power/opp/of.c
index 5552211..b52c617 100644
--- a/drivers/base/power/opp/of.c
+++ b/drivers/base/power/opp/of.c
@@ -386,7 +386,7 @@
 {
 	const struct property *prop;
 	const __be32 *val;
-	int nr;
+	int nr, ret;
 
 	prop = of_find_property(dev->of_node, "operating-points", NULL);
 	if (!prop)
@@ -409,9 +409,13 @@
 		unsigned long freq = be32_to_cpup(val++) * 1000;
 		unsigned long volt = be32_to_cpup(val++);
 
-		if (_opp_add_v1(dev, freq, volt, false))
-			dev_warn(dev, "%s: Failed to add OPP %ld\n",
-				 __func__, freq);
+		ret = _opp_add_v1(dev, freq, volt, false);
+		if (ret) {
+			dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
+				__func__, freq, ret);
+			dev_pm_opp_of_remove_table(dev);
+			return ret;
+		}
 		nr -= 2;
 	}
 
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index ce68c1e..882f1c9 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -131,7 +131,11 @@
 /* drivers/base/power/main.c */
 extern struct list_head dpm_list;	/* The active device list */
 
+#ifdef CONFIG_QCOM_SHOW_RESUME_IRQ
 extern int msm_show_resume_irq_mask;
+#else
+#define msm_show_resume_irq_mask 0
+#endif
 
 static inline struct device *to_device(struct list_head *entry)
 {
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index 404d94c..feba1b2 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -141,6 +141,13 @@
 	struct wake_irq *wirq = _wirq;
 	int res;
 
+	/* Maybe abort suspend? */
+	if (irqd_is_wakeup_set(irq_get_irq_data(irq))) {
+		pm_wakeup_event(wirq->dev, 0);
+
+		return IRQ_HANDLED;
+	}
+
 	/* We don't want RPM_ASYNC or RPM_NOWAIT here */
 	res = pm_runtime_resume(wirq->dev);
 	if (res < 0)
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 43a36d6..7b313b5 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -20,6 +20,7 @@
 #include <linux/phy.h>
 
 struct property_set {
+	struct device *dev;
 	struct fwnode_handle fwnode;
 	struct property_entry *properties;
 };
@@ -182,11 +183,12 @@
 	return 0;
 }
 
-static inline struct fwnode_handle *dev_fwnode(struct device *dev)
+struct fwnode_handle *dev_fwnode(struct device *dev)
 {
 	return IS_ENABLED(CONFIG_OF) && dev->of_node ?
 		&dev->of_node->fwnode : dev->fwnode;
 }
+EXPORT_SYMBOL_GPL(dev_fwnode);
 
 /**
  * device_property_present - check if a property of a device is present
@@ -816,6 +818,7 @@
 void device_remove_properties(struct device *dev)
 {
 	struct fwnode_handle *fwnode;
+	struct property_set *pset;
 
 	fwnode = dev_fwnode(dev);
 	if (!fwnode)
@@ -825,16 +828,16 @@
 	 * the pset. If there is no real firmware node (ACPI/DT) primary
 	 * will hold the pset.
 	 */
-	if (is_pset_node(fwnode)) {
+	pset = to_pset_node(fwnode);
+	if (pset) {
 		set_primary_fwnode(dev, NULL);
-		pset_free_set(to_pset_node(fwnode));
 	} else {
-		fwnode = fwnode->secondary;
-		if (!IS_ERR(fwnode) && is_pset_node(fwnode)) {
+		pset = to_pset_node(fwnode->secondary);
+		if (pset && dev == pset->dev)
 			set_secondary_fwnode(dev, NULL);
-			pset_free_set(to_pset_node(fwnode));
-		}
 	}
+	if (pset && dev == pset->dev)
+		pset_free_set(pset);
 }
 EXPORT_SYMBOL_GPL(device_remove_properties);
 
@@ -862,6 +865,7 @@
 
 	p->fwnode.type = FWNODE_PDATA;
 	set_secondary_fwnode(dev, &p->fwnode);
+	p->dev = dev;
 	return 0;
 }
 EXPORT_SYMBOL_GPL(device_add_properties);
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index fc814a3..45fc564 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -32,3 +32,12 @@
 
 config REGMAP_SWR
 	tristate
+
+config REGMAP_ALLOW_WRITE_DEBUGFS
+	depends on REGMAP && DEBUG_FS
+	bool "Allow REGMAP debugfs write"
+	default n
+	help
+	  Say 'y' here to allow the regmap debugfs write. Regmap debugfs write
+	  could be risky when accessing some essential hardwares, so it is not
+	  recommended to enable this option on any production device.
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index b4c5224..1559070 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -269,8 +269,7 @@
 				   count, ppos);
 }
 
-#define REGMAP_ALLOW_WRITE_DEBUGFS
-#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
+#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS
 /*
  * This can be dangerous especially when we have clients such as
  * PMICs, therefore don't provide any real compile time configuration option
@@ -340,7 +339,7 @@
 			new_count, ppos);
 }
 
-#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
+#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS
 static ssize_t regmap_data_write_file(struct file *file,
 				     const char __user *user_buf,
 				     size_t count, loff_t *ppos)
@@ -633,7 +632,7 @@
 	if (map->max_register || regmap_readable(map, 0)) {
 		umode_t registers_mode;
 
-#if defined(REGMAP_ALLOW_WRITE_DEBUGFS)
+#ifdef CONFIG_REGMAP_ALLOW_WRITE_DEBUGFS
 		registers_mode = 0600;
 #else
 		registers_mode = 0400;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index c9441f9..98b767d 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -929,6 +929,7 @@
 		return -ENOMEM;
 
 	for (i = 0; i < nbds_max; i++) {
+		struct request_queue *q;
 		struct gendisk *disk = alloc_disk(1 << part_shift);
 		if (!disk)
 			goto out;
@@ -954,12 +955,13 @@
 		 * every gendisk to have its very own request_queue struct.
 		 * These structs are big so we dynamically allocate them.
 		 */
-		disk->queue = blk_mq_init_queue(&nbd_dev[i].tag_set);
-		if (!disk->queue) {
+		q = blk_mq_init_queue(&nbd_dev[i].tag_set);
+		if (IS_ERR(q)) {
 			blk_mq_free_tag_set(&nbd_dev[i].tag_set);
 			put_disk(disk);
 			goto out;
 		}
+		disk->queue = q;
 
 		/*
 		 * Tell the block layer that we are not a rotational device
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 7b274ff..24f4b54 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -2788,7 +2788,7 @@
 	 * from the parent.
 	 */
 	page_count = (u32)calc_pages_for(0, length);
-	pages = ceph_alloc_page_vector(page_count, GFP_KERNEL);
+	pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
 	if (IS_ERR(pages)) {
 		result = PTR_ERR(pages);
 		pages = NULL;
@@ -2922,7 +2922,7 @@
 	 */
 	size = sizeof (__le64) + sizeof (__le32) + sizeof (__le32);
 	page_count = (u32)calc_pages_for(0, size);
-	pages = ceph_alloc_page_vector(page_count, GFP_KERNEL);
+	pages = ceph_alloc_page_vector(page_count, GFP_NOIO);
 	if (IS_ERR(pages)) {
 		ret = PTR_ERR(pages);
 		goto fail_stat_request;
diff --git a/drivers/block/skd_main.c b/drivers/block/skd_main.c
index 3822eae..6f78cea7 100644
--- a/drivers/block/skd_main.c
+++ b/drivers/block/skd_main.c
@@ -2163,6 +2163,9 @@
 		 */
 		qcmd |= FIT_QCMD_MSGSIZE_64;
 
+	/* Make sure skd_msg_buf is written before the doorbell is triggered. */
+	smp_wmb();
+
 	SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND);
 
 }
@@ -2209,6 +2212,9 @@
 	qcmd = skspcl->mb_dma_address;
 	qcmd |= FIT_QCMD_QID_NORMAL + FIT_QCMD_MSGSIZE_128;
 
+	/* Make sure skd_msg_buf is written before the doorbell is triggered. */
+	smp_wmb();
+
 	SKD_WRITEQ(skdev, qcmd, FIT_Q_COMMAND);
 }
 
@@ -4622,15 +4628,16 @@
 {
 	struct gendisk *disk = skdev->disk;
 
-	if (disk != NULL) {
-		struct request_queue *q = disk->queue;
+	if (disk && (disk->flags & GENHD_FL_UP))
+		del_gendisk(disk);
 
-		if (disk->flags & GENHD_FL_UP)
-			del_gendisk(disk);
-		if (q)
-			blk_cleanup_queue(q);
-		put_disk(disk);
+	if (skdev->queue) {
+		blk_cleanup_queue(skdev->queue);
+		skdev->queue = NULL;
+		disk->queue = NULL;
 	}
+
+	put_disk(disk);
 	skdev->disk = NULL;
 }
 
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 3c3b8f6..10332c2 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -630,11 +630,12 @@
 	if (err)
 		goto out_put_disk;
 
-	q = vblk->disk->queue = blk_mq_init_queue(&vblk->tag_set);
+	q = blk_mq_init_queue(&vblk->tag_set);
 	if (IS_ERR(q)) {
 		err = -ENOMEM;
 		goto out_free_tags;
 	}
+	vblk->disk->queue = q;
 
 	q->queuedata = vblk;
 
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 9908597..f11d62d 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -2112,9 +2112,9 @@
 			/*
 			 * Get the bios in the request so we can re-queue them.
 			 */
-			if (req_op(shadow[i].request) == REQ_OP_FLUSH ||
-			    req_op(shadow[i].request) == REQ_OP_DISCARD ||
-			    req_op(shadow[i].request) == REQ_OP_SECURE_ERASE ||
+			if (req_op(shadow[j].request) == REQ_OP_FLUSH ||
+			    req_op(shadow[j].request) == REQ_OP_DISCARD ||
+			    req_op(shadow[j].request) == REQ_OP_SECURE_ERASE ||
 			    shadow[j].request->cmd_flags & REQ_FUA) {
 				/*
 				 * Flush operations don't contain bios, so
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 3868665..9701cc2 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -41,6 +41,12 @@
 static int zram_major;
 static const char *default_compressor = "lzo";
 
+/*
+ * We don't need to see memory allocation errors more than once every 1
+ * second to know that a problem is occurring.
+ */
+#define ALLOC_ERROR_LOG_RATE_MS 1000
+
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
 
@@ -668,6 +674,7 @@
 	struct zram_meta *meta = zram->meta;
 	struct zcomp_strm *zstrm = NULL;
 	unsigned long alloced_pages;
+	static unsigned long zram_rs_time;
 
 	page = bvec->bv_page;
 	if (is_partial_io(bvec)) {
@@ -761,8 +768,10 @@
 		if (handle)
 			goto compress_again;
 
-		pr_err("Error allocating memory for compressed page: %u, size=%u\n",
-			index, clen);
+		if (printk_timed_ratelimit(&zram_rs_time,
+					   ALLOC_ERROR_LOG_RATE_MS))
+			pr_err("Error allocating memory for compressed page: %u, size=%u\n",
+			       index, clen);
 		ret = -ENOMEM;
 		goto out;
 	}
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index b793853..3880c90 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -212,15 +212,28 @@
 				const struct firmware *firmware)
 {
 	u8 *send_buf;
-	int len = 0;
-	int err, pipe, size, sent = 0;
-	int count = firmware->size;
+	int err, pipe, len, size, sent = 0;
+	int count;
 
 	BT_DBG("udev %p", udev);
 
+	if (!firmware || !firmware->data || firmware->size <= 0) {
+		err = -EINVAL;
+		BT_ERR("Not a valid FW file");
+		return err;
+	}
+
+	count = firmware->size;
+
+	if (count < FW_HDR_SIZE) {
+		err = -EINVAL;
+		BT_ERR("ath3k loading invalid size of file");
+		return err;
+	}
+
 	pipe = usb_sndctrlpipe(udev, 0);
 
-	send_buf = kmalloc(BULK_SIZE, GFP_KERNEL);
+	send_buf = kzalloc(BULK_SIZE, GFP_KERNEL);
 	if (!send_buf) {
 		BT_ERR("Can't allocate memory chunk for firmware");
 		return -ENOMEM;
diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c
index d02f2c1..c738bae 100644
--- a/drivers/bluetooth/btmrvl_sdio.c
+++ b/drivers/bluetooth/btmrvl_sdio.c
@@ -1682,8 +1682,12 @@
 	/* Disable platform specific wakeup interrupt */
 	if (card->plt_wake_cfg && card->plt_wake_cfg->irq_bt >= 0) {
 		disable_irq_wake(card->plt_wake_cfg->irq_bt);
-		if (!card->plt_wake_cfg->wake_by_bt)
-			disable_irq(card->plt_wake_cfg->irq_bt);
+		disable_irq(card->plt_wake_cfg->irq_bt);
+		if (card->plt_wake_cfg->wake_by_bt)
+			/* Undo our disable, since interrupt handler already
+			 * did this.
+			 */
+			enable_irq(card->plt_wake_cfg->irq_bt);
 	}
 
 	return 0;
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index dd220fa..74e677a 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -342,6 +342,7 @@
 	{ USB_DEVICE(0x13d3, 0x3410), .driver_info = BTUSB_REALTEK },
 	{ USB_DEVICE(0x13d3, 0x3416), .driver_info = BTUSB_REALTEK },
 	{ USB_DEVICE(0x13d3, 0x3459), .driver_info = BTUSB_REALTEK },
+	{ USB_DEVICE(0x13d3, 0x3494), .driver_info = BTUSB_REALTEK },
 
 	/* Additional Realtek 8821AE Bluetooth devices */
 	{ USB_DEVICE(0x0b05, 0x17dc), .driver_info = BTUSB_REALTEK },
diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
index c7f3969..70db4d5 100644
--- a/drivers/bus/mvebu-mbus.c
+++ b/drivers/bus/mvebu-mbus.c
@@ -720,7 +720,7 @@
 			if (mbus->hw_io_coherency)
 				w->mbus_attr |= ATTR_HW_COHERENCY;
 			w->base = base & DDR_BASE_CS_LOW_MASK;
-			w->size = (size | ~DDR_SIZE_MASK) + 1;
+			w->size = (u64)(size | ~DDR_SIZE_MASK) + 1;
 		}
 	}
 	mvebu_mbus_dram_info.num_cs = cs;
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index 636c982..c92819c 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -42,13 +42,17 @@
 #include <linux/sort.h>
 #include <linux/msm_dma_iommu_mapping.h>
 #include <asm/dma-iommu.h>
+#include <soc/qcom/scm.h>
 #include "adsprpc_compat.h"
 #include "adsprpc_shared.h"
+#include <soc/qcom/ramdump.h>
 #include <linux/debugfs.h>
 #include <linux/pm_qos.h>
 #define TZ_PIL_PROTECT_MEM_SUBSYS_ID 0x0C
 #define TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID 0x0D
 #define TZ_PIL_AUTH_QDSP6_PROC 1
+#define ADSP_MMAP_HEAP_ADDR 4
+#define ADSP_MMAP_REMOTE_HEAP_ADDR 8
 #define FASTRPC_ENOSUCH 39
 #define VMID_SSC_Q6     5
 #define VMID_ADSP_Q6    6
@@ -60,6 +64,8 @@
 #define NUM_SESSIONS	9	/*8 compute, 1 cpz*/
 #define M_FDLIST	(16)
 #define M_CRCLIST	(64)
+#define SESSION_ID_INDEX (30)
+#define FASTRPC_CTX_MAGIC (0xbeeddeed)
 
 #define IS_CACHE_ALIGNED(x) (((x) & ((L1_CACHE_BYTES)-1)) == 0)
 
@@ -69,12 +75,17 @@
 #define FASTRPC_LINK_CONNECTING   (0x1)
 #define FASTRPC_LINK_CONNECTED    (0x3)
 #define FASTRPC_LINK_DISCONNECTING (0x7)
+#define FASTRPC_LINK_REMOTE_DISCONNECTING (0x8)
+#define FASTRPC_GLINK_INTENT_LEN  (64)
 
 #define PERF_KEYS "count:flush:map:copy:glink:getargs:putargs:invalidate:invoke"
 #define FASTRPC_STATIC_HANDLE_LISTENER (3)
 #define FASTRPC_STATIC_HANDLE_MAX (20)
 #define FASTRPC_LATENCY_CTRL_ENB  (1)
 
+#define INIT_FILELEN_MAX (2*1024*1024)
+#define INIT_MEMLEN_MAX  (8*1024*1024)
+
 #define PERF_END (void)0
 
 #define PERF(enb, cnt, ff) \
@@ -175,6 +186,7 @@
 	struct overlap **overps;
 	struct smq_msg msg;
 	uint32_t *crc;
+	unsigned int magic;
 };
 
 struct fastrpc_ctx_lst {
@@ -183,6 +195,7 @@
 };
 
 struct fastrpc_smmu {
+	struct device *dev;
 	struct dma_iommu_mapping *mapping;
 	int cb;
 	int enabled;
@@ -219,17 +232,21 @@
 	int ssrcount;
 	void *handle;
 	int prevssrcount;
+	int issubsystemup;
 	int vmid;
+	int ramdumpenabled;
+	void *remoteheap_ramdump_dev;
 	struct fastrpc_glink_info link;
+	struct mutex mut;
 };
 
 struct fastrpc_apps {
 	struct fastrpc_channel_ctx *channel;
 	struct cdev cdev;
 	struct class *class;
-	struct mutex smd_mutex;
 	struct smq_phy_page range;
 	struct hlist_head maps;
+	uint32_t staticpd_flags;
 	dev_t dev_no;
 	int compat;
 	struct hlist_head drivers;
@@ -251,7 +268,7 @@
 	struct ion_handle *handle;
 	uint64_t phys;
 	ssize_t size;
-	uintptr_t va;
+	uintptr_t __user va;
 	ssize_t len;
 	int refs;
 	uintptr_t raddr;
@@ -282,6 +299,7 @@
 	struct fastrpc_session_ctx *secsctx;
 	uint32_t mode;
 	uint32_t profile;
+	int sessionid;
 	int tgid;
 	int cid;
 	int ssrcount;
@@ -336,7 +354,7 @@
 
 static void fastrpc_buf_free(struct fastrpc_buf *buf, int cache)
 {
-	struct fastrpc_file *fl = buf == 0 ? 0 : buf->fl;
+	struct fastrpc_file *fl = buf == NULL ? NULL : buf->fl;
 	int vmid;
 
 	if (!fl)
@@ -360,7 +378,7 @@
 			hyp_assign_phys(buf->phys, buf_page_size(buf->size),
 				srcVM, 2, destVM, destVMperm, 1);
 		}
-		dma_free_coherent(fl->sctx->dev, buf->size, buf->virt,
+		dma_free_coherent(fl->sctx->smmu.dev, buf->size, buf->virt,
 					buf->phys);
 	}
 	kfree(buf);
@@ -373,7 +391,7 @@
 	do {
 		struct hlist_node *n;
 
-		free = 0;
+		free = NULL;
 		spin_lock(&fl->hlock);
 		hlist_for_each_entry_safe(buf, n, &fl->bufs, hn) {
 			hlist_del_init(&buf->hn);
@@ -388,31 +406,57 @@
 
 static void fastrpc_mmap_add(struct fastrpc_mmap *map)
 {
-	struct fastrpc_file *fl = map->fl;
+	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		struct fastrpc_apps *me = &gfa;
 
-	spin_lock(&fl->hlock);
-	hlist_add_head(&map->hn, &fl->maps);
-	spin_unlock(&fl->hlock);
+		spin_lock(&me->hlock);
+		hlist_add_head(&map->hn, &me->maps);
+		spin_unlock(&me->hlock);
+	} else {
+		struct fastrpc_file *fl = map->fl;
+
+		spin_lock(&fl->hlock);
+		hlist_add_head(&map->hn, &fl->maps);
+		spin_unlock(&fl->hlock);
+	}
 }
 
-static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd, uintptr_t va,
-		ssize_t len, int mflags, int refs, struct fastrpc_mmap **ppmap)
+static int fastrpc_mmap_find(struct fastrpc_file *fl, int fd,
+		uintptr_t __user va, ssize_t len, int mflags, int refs,
+		struct fastrpc_mmap **ppmap)
 {
-	struct fastrpc_mmap *match = 0, *map;
+	struct fastrpc_apps *me = &gfa;
+	struct fastrpc_mmap *match = NULL, *map = NULL;
 	struct hlist_node *n;
-
-	spin_lock(&fl->hlock);
-	hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
-		if (va >= map->va &&
-			va + len <= map->va + map->len &&
-			map->fd == fd) {
-			if (refs)
-				map->refs++;
-			match = map;
-			break;
+	if (mflags == ADSP_MMAP_HEAP_ADDR ||
+				 mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		spin_lock(&me->hlock);
+		hlist_for_each_entry_safe(map, n, &me->maps, hn) {
+			if (va >= map->va &&
+				va + len <= map->va + map->len &&
+				map->fd == fd) {
+				if (refs)
+					map->refs++;
+				match = map;
+				break;
+			}
 		}
+		spin_unlock(&me->hlock);
+	} else {
+		spin_lock(&fl->hlock);
+		hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
+			if (va >= map->va &&
+				va + len <= map->va + map->len &&
+				map->fd == fd) {
+				if (refs)
+					map->refs++;
+				match = map;
+				break;
+			}
+		}
+		spin_unlock(&fl->hlock);
 	}
-	spin_unlock(&fl->hlock);
 	if (match) {
 		*ppmap = match;
 		return 0;
@@ -420,10 +464,28 @@
 	return -ENOTTY;
 }
 
+static int dma_alloc_memory(phys_addr_t *region_start, ssize_t size)
+{
+	struct fastrpc_apps *me = &gfa;
+	void *vaddr = NULL;
+
+	if (me->dev == NULL) {
+		pr_err("device adsprpc-mem is not initialized\n");
+		return -ENODEV;
+	}
+	vaddr = dma_alloc_coherent(me->dev, size, region_start, GFP_KERNEL);
+	if (!vaddr) {
+		pr_err("ADSPRPC: Failed to allocate %x remote heap memory\n",
+						(unsigned int)size);
+		return -ENOMEM;
+	}
+	return 0;
+}
+
 static int fastrpc_mmap_remove(struct fastrpc_file *fl, uintptr_t va,
 			       ssize_t len, struct fastrpc_mmap **ppmap)
 {
-	struct fastrpc_mmap *match = 0, *map;
+	struct fastrpc_mmap *match = NULL, *map;
 	struct hlist_node *n;
 	struct fastrpc_apps *me = &gfa;
 
@@ -460,53 +522,79 @@
 	return -ENOTTY;
 }
 
-static void fastrpc_mmap_free(struct fastrpc_mmap *map)
+static void fastrpc_mmap_free(struct fastrpc_mmap *map, uint32_t flags)
 {
+	struct fastrpc_apps *me = &gfa;
 	struct fastrpc_file *fl;
 	int vmid;
 	struct fastrpc_session_ctx *sess;
-	int destVM[1] = {VMID_HLOS};
-	int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
 	if (!map)
 		return;
 	fl = map->fl;
-	spin_lock(&fl->hlock);
-	map->refs--;
-	if (!map->refs)
-		hlist_del_init(&map->hn);
-	spin_unlock(&fl->hlock);
-	if (map->refs > 0)
-		return;
-	if (map->secure)
-		sess = fl->secsctx;
-	else
-		sess = fl->sctx;
-
-	if (!IS_ERR_OR_NULL(map->handle))
-		ion_free(fl->apps->client, map->handle);
-	if (sess && sess->smmu.enabled) {
-		if (map->size || map->phys)
-			msm_dma_unmap_sg(sess->dev,
-				map->table->sgl,
-				map->table->nents, DMA_BIDIRECTIONAL,
-				map->buf);
+	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		spin_lock(&me->hlock);
+		map->refs--;
+		if (!map->refs)
+			hlist_del_init(&map->hn);
+		spin_unlock(&me->hlock);
+		if (map->refs > 0)
+			return;
+	} else {
+		spin_lock(&fl->hlock);
+		map->refs--;
+		if (!map->refs)
+			hlist_del_init(&map->hn);
+		spin_unlock(&fl->hlock);
+		if (map->refs > 0 && !flags)
+			return;
 	}
-	vmid = fl->apps->channel[fl->cid].vmid;
-	if (vmid && map->phys) {
-		int srcVM[2] = {VMID_HLOS, vmid};
+	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
 
-		hyp_assign_phys(map->phys, buf_page_size(map->size),
-			srcVM, 2, destVM, destVMperm, 1);
+		if (me->dev == NULL) {
+			pr_err("failed to free remote heap allocation\n");
+			return;
+		}
+		if (map->phys) {
+			dma_free_coherent(me->dev, map->size,
+					&(map->va), map->phys);
+		}
+	} else {
+		int destVM[1] = {VMID_HLOS};
+		int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+		if (map->secure)
+			sess = fl->secsctx;
+		else
+			sess = fl->sctx;
+
+		if (!IS_ERR_OR_NULL(map->handle))
+			ion_free(fl->apps->client, map->handle);
+		if (sess && sess->smmu.enabled) {
+			if (map->size || map->phys)
+				msm_dma_unmap_sg(sess->smmu.dev,
+					map->table->sgl,
+					map->table->nents, DMA_BIDIRECTIONAL,
+					map->buf);
+		}
+		vmid = fl->apps->channel[fl->cid].vmid;
+		if (vmid && map->phys) {
+			int srcVM[2] = {VMID_HLOS, vmid};
+
+			hyp_assign_phys(map->phys, buf_page_size(map->size),
+				srcVM, 2, destVM, destVMperm, 1);
+		}
+
+		if (!IS_ERR_OR_NULL(map->table))
+			dma_buf_unmap_attachment(map->attach, map->table,
+					DMA_BIDIRECTIONAL);
+		if (!IS_ERR_OR_NULL(map->attach))
+			dma_buf_detach(map->buf, map->attach);
+		if (!IS_ERR_OR_NULL(map->buf))
+			dma_buf_put(map->buf);
 	}
-
-	if (!IS_ERR_OR_NULL(map->table))
-		dma_buf_unmap_attachment(map->attach, map->table,
-				DMA_BIDIRECTIONAL);
-	if (!IS_ERR_OR_NULL(map->attach))
-		dma_buf_detach(map->buf, map->attach);
-	if (!IS_ERR_OR_NULL(map->buf))
-		dma_buf_put(map->buf);
 	kfree(map);
 }
 
@@ -514,15 +602,17 @@
 					struct fastrpc_session_ctx **session);
 
 static int fastrpc_mmap_create(struct fastrpc_file *fl, int fd,
-	unsigned int attr, uintptr_t va, ssize_t len, int mflags,
+	unsigned int attr, uintptr_t __user va, ssize_t len, int mflags,
 	struct fastrpc_mmap **ppmap)
 {
+	struct fastrpc_apps *me = &gfa;
 	struct fastrpc_session_ctx *sess;
 	struct fastrpc_apps *apps = fl->apps;
 	int cid = fl->cid;
 	struct fastrpc_channel_ctx *chan = &apps->channel[cid];
-	struct fastrpc_mmap *map = 0;
+	struct fastrpc_mmap *map = NULL;
 	unsigned long attrs;
+	phys_addr_t region_start = 0;
 	unsigned long flags;
 	int err = 0, vmid;
 
@@ -538,47 +628,63 @@
 	map->fl = fl;
 	map->fd = fd;
 	map->attr = attr;
-	VERIFY(err, !IS_ERR_OR_NULL(map->handle =
-			ion_import_dma_buf_fd(fl->apps->client, fd)));
-	if (err)
-		goto bail;
-	VERIFY(err, !ion_handle_get_flags(fl->apps->client, map->handle,
-					&flags));
-	if (err)
-		goto bail;
-
-	map->uncached = !ION_IS_CACHED(flags);
-	if (map->attr & FASTRPC_ATTR_NOVA)
-		map->uncached = 1;
-
-	map->secure = flags & ION_FLAG_SECURE;
-	if (map->secure) {
-		if (!fl->secsctx)
-			err = fastrpc_session_alloc(chan, 1,
-						&fl->secsctx);
+	if (mflags == ADSP_MMAP_HEAP_ADDR ||
+				mflags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		map->apps = me;
+		map->fl = NULL;
+		VERIFY(err, !dma_alloc_memory(&region_start, len));
 		if (err)
 			goto bail;
-	}
-	if (map->secure)
-		sess = fl->secsctx;
-	else
-		sess = fl->sctx;
-	VERIFY(err, !IS_ERR_OR_NULL(sess));
-	if (err)
-		goto bail;
-	VERIFY(err, !IS_ERR_OR_NULL(map->buf = dma_buf_get(fd)));
-	if (err)
-		goto bail;
-	VERIFY(err, !IS_ERR_OR_NULL(map->attach =
-			dma_buf_attach(map->buf, sess->dev)));
-	if (err)
-		goto bail;
-	VERIFY(err, !IS_ERR_OR_NULL(map->table =
-		dma_buf_map_attachment(map->attach,
-			DMA_BIDIRECTIONAL)));
-	if (err)
-		goto bail;
-	if (sess->smmu.enabled) {
+		map->phys = (uintptr_t)region_start;
+		map->size = len;
+		map->va = (uintptr_t __user)map->phys;
+	} else {
+		if (map->attr && (map->attr & FASTRPC_ATTR_KEEP_MAP)) {
+			pr_info("adsprpc: buffer mapped with persist attr %x\n",
+				(unsigned int)map->attr);
+			map->refs = 2;
+		}
+		VERIFY(err, !IS_ERR_OR_NULL(map->handle =
+				ion_import_dma_buf_fd(fl->apps->client, fd)));
+		if (err)
+			goto bail;
+		VERIFY(err, !ion_handle_get_flags(fl->apps->client, map->handle,
+						&flags));
+		if (err)
+			goto bail;
+
+		map->uncached = !ION_IS_CACHED(flags);
+		if (map->attr & FASTRPC_ATTR_NOVA)
+			map->uncached = 1;
+
+		map->secure = flags & ION_FLAG_SECURE;
+		if (map->secure) {
+			if (!fl->secsctx)
+				err = fastrpc_session_alloc(chan, 1,
+							&fl->secsctx);
+			if (err)
+				goto bail;
+		}
+		if (map->secure)
+			sess = fl->secsctx;
+		else
+			sess = fl->sctx;
+		VERIFY(err, !IS_ERR_OR_NULL(sess));
+		if (err)
+			goto bail;
+		VERIFY(err, !IS_ERR_OR_NULL(map->buf = dma_buf_get(fd)));
+		if (err)
+			goto bail;
+		VERIFY(err, !IS_ERR_OR_NULL(map->attach =
+				dma_buf_attach(map->buf, sess->smmu.dev)));
+		if (err)
+			goto bail;
+		VERIFY(err, !IS_ERR_OR_NULL(map->table =
+			dma_buf_map_attachment(map->attach,
+				DMA_BIDIRECTIONAL)));
+		if (err)
+			goto bail;
+		if (sess->smmu.enabled) {
 		attrs = DMA_ATTR_EXEC_MAPPING;
 
 		if (map->attr & FASTRPC_ATTR_NON_COHERENT ||
@@ -588,37 +694,38 @@
 			attrs |= DMA_ATTR_FORCE_COHERENT;
 
 		VERIFY(err, map->table->nents ==
-			msm_dma_map_sg_attrs(sess->dev,
+				msm_dma_map_sg_attrs(sess->smmu.dev,
 				map->table->sgl, map->table->nents,
 				DMA_BIDIRECTIONAL, map->buf, attrs));
-		if (err)
+			if (err)
+				goto bail;
+		} else {
+			VERIFY(err, map->table->nents == 1);
+			if (err)
 			goto bail;
-	} else {
-		VERIFY(err, map->table->nents == 1);
-		if (err)
-		goto bail;
-	}
-	map->phys = sg_dma_address(map->table->sgl);
-	if (sess->smmu.cb) {
-		map->phys += ((uint64_t)sess->smmu.cb << 32);
-		map->size = sg_dma_len(map->table->sgl);
-	} else {
-		map->size = buf_page_size(len);
-	}
-	vmid = fl->apps->channel[fl->cid].vmid;
-	if (vmid) {
-		int srcVM[1] = {VMID_HLOS};
-		int destVM[2] = {VMID_HLOS, vmid};
-		int destVMperm[2] = {PERM_READ | PERM_WRITE,
-				PERM_READ | PERM_WRITE | PERM_EXEC};
+		}
+		map->phys = sg_dma_address(map->table->sgl);
+		if (sess->smmu.cb) {
+			map->phys += ((uint64_t)sess->smmu.cb << 32);
+			map->size = sg_dma_len(map->table->sgl);
+		} else {
+			map->size = buf_page_size(len);
+		}
+		vmid = fl->apps->channel[fl->cid].vmid;
+		if (vmid) {
+			int srcVM[1] = {VMID_HLOS};
+			int destVM[2] = {VMID_HLOS, vmid};
+			int destVMperm[2] = {PERM_READ | PERM_WRITE,
+					PERM_READ | PERM_WRITE | PERM_EXEC};
 
-		VERIFY(err, !hyp_assign_phys(map->phys,
-				buf_page_size(map->size),
-				srcVM, 1, destVM, destVMperm, 2));
-		if (err)
-			goto bail;
+			VERIFY(err, !hyp_assign_phys(map->phys,
+					buf_page_size(map->size),
+					srcVM, 1, destVM, destVMperm, 2));
+			if (err)
+				goto bail;
+		}
+		map->va = va;
 	}
-	map->va = va;
 	map->len = len;
 
 	fastrpc_mmap_add(map);
@@ -626,7 +733,7 @@
 
 bail:
 	if (err && map)
-		fastrpc_mmap_free(map);
+		fastrpc_mmap_free(map, 0);
 	return err;
 }
 
@@ -634,7 +741,7 @@
 			     struct fastrpc_buf **obuf)
 {
 	int err = 0, vmid;
-	struct fastrpc_buf *buf = 0, *fr = 0;
+	struct fastrpc_buf *buf = NULL, *fr = NULL;
 	struct hlist_node *n;
 
 	VERIFY(err, size > 0);
@@ -654,21 +761,21 @@
 		*obuf = fr;
 		return 0;
 	}
-	buf = 0;
-	VERIFY(err, buf = kzalloc(sizeof(*buf), GFP_KERNEL));
+	buf = NULL;
+	VERIFY(err, NULL != (buf = kzalloc(sizeof(*buf), GFP_KERNEL)));
 	if (err)
 		goto bail;
 	INIT_HLIST_NODE(&buf->hn);
 	buf->fl = fl;
-	buf->virt = 0;
+	buf->virt = NULL;
 	buf->phys = 0;
 	buf->size = size;
-	buf->virt = dma_alloc_coherent(fl->sctx->dev, buf->size,
+	buf->virt = dma_alloc_coherent(fl->sctx->smmu.dev, buf->size,
 				       (void *)&buf->phys, GFP_KERNEL);
 	if (IS_ERR_OR_NULL(buf->virt)) {
 		/* free cache and retry */
 		fastrpc_buf_list_free(fl);
-		buf->virt = dma_alloc_coherent(fl->sctx->dev, buf->size,
+		buf->virt = dma_alloc_coherent(fl->sctx->smmu.dev, buf->size,
 					       (void *)&buf->phys, GFP_KERNEL);
 		VERIFY(err, !IS_ERR_OR_NULL(buf->virt));
 	}
@@ -702,7 +809,7 @@
 				       struct smq_invoke_ctx **po)
 {
 	int err = 0;
-	struct smq_invoke_ctx *ctx = 0, *ictx = 0;
+	struct smq_invoke_ctx *ctx = NULL, *ictx = NULL;
 	struct hlist_node *n;
 	struct fastrpc_ioctl_invoke *invoke = &inv->inv;
 
@@ -757,7 +864,7 @@
 		ctx->overs[i].raix = i;
 		ctx->overps[i] = &ctx->overs[i];
 	}
-	sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, 0);
+	sort(ctx->overps, nbufs, sizeof(*ctx->overps), overlap_ptr_cmp, NULL);
 	max.start = 0;
 	max.end = 0;
 	for (i = 0; i < nbufs; ++i) {
@@ -786,7 +893,8 @@
 #define K_COPY_FROM_USER(err, kernel, dst, src, size) \
 	do {\
 		if (!(kernel))\
-			VERIFY(err, 0 == copy_from_user((dst), (src),\
+			VERIFY(err, 0 == copy_from_user((dst),\
+			(void const __user *)(src),\
 							(size)));\
 		else\
 			memmove((dst), (src), (size));\
@@ -795,8 +903,8 @@
 #define K_COPY_TO_USER(err, kernel, dst, src, size) \
 	do {\
 		if (!(kernel))\
-			VERIFY(err, 0 == copy_to_user((dst), (src),\
-						      (size)));\
+			VERIFY(err, 0 == copy_to_user((void __user *)(dst), \
+						(src), (size)));\
 		else\
 			memmove((dst), (src), (size));\
 	} while (0)
@@ -809,7 +917,7 @@
 			 struct smq_invoke_ctx **po)
 {
 	int err = 0, bufs, size = 0;
-	struct smq_invoke_ctx *ctx = 0;
+	struct smq_invoke_ctx *ctx = NULL;
 	struct fastrpc_ctx_lst *clst = &fl->clst;
 	struct fastrpc_ioctl_invoke *invoke = &invokefd->inv;
 
@@ -820,7 +928,7 @@
 		sizeof(*ctx->overs) * (bufs) +
 		sizeof(*ctx->overps) * (bufs);
 
-	VERIFY(err, ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL));
+	VERIFY(err, NULL != (ctx = kzalloc(sizeof(*ctx) + size, GFP_KERNEL)));
 	if (err)
 		goto bail;
 
@@ -834,7 +942,7 @@
 	ctx->overs = (struct overlap *)(&ctx->attrs[bufs]);
 	ctx->overps = (struct overlap **)(&ctx->overs[bufs]);
 
-	K_COPY_FROM_USER(err, kernel, ctx->lpra, invoke->pra,
+	K_COPY_FROM_USER(err, kernel, (void *)ctx->lpra, invoke->pra,
 					bufs * sizeof(*ctx->lpra));
 	if (err)
 		goto bail;
@@ -860,8 +968,9 @@
 	}
 	ctx->retval = -1;
 	ctx->pid = current->pid;
-	ctx->tgid = current->tgid;
+	ctx->tgid = fl->tgid;
 	init_completion(&ctx->work);
+	ctx->magic = FASTRPC_CTX_MAGIC;
 
 	spin_lock(&fl->hlock);
 	hlist_add_head(&ctx->hn, &clst->pending);
@@ -895,8 +1004,9 @@
 	hlist_del_init(&ctx->hn);
 	spin_unlock(&ctx->fl->hlock);
 	for (i = 0; i < nbufs; ++i)
-		fastrpc_mmap_free(ctx->maps[i]);
+		fastrpc_mmap_free(ctx->maps[i], 0);
 	fastrpc_buf_free(ctx->buf, 1);
+	ctx->magic = 0;
 	kfree(ctx);
 }
 
@@ -945,11 +1055,11 @@
 static void fastrpc_context_list_dtor(struct fastrpc_file *fl)
 {
 	struct fastrpc_ctx_lst *clst = &fl->clst;
-	struct smq_invoke_ctx *ictx = 0, *ctxfree;
+	struct smq_invoke_ctx *ictx = NULL, *ctxfree;
 	struct hlist_node *n;
 
 	do {
-		ctxfree = 0;
+		ctxfree = NULL;
 		spin_lock(&fl->hlock);
 		hlist_for_each_entry_safe(ictx, n, &clst->interrupted, hn) {
 			hlist_del_init(&ictx->hn);
@@ -961,7 +1071,7 @@
 			context_free(ctxfree);
 	} while (ctxfree);
 	do {
-		ctxfree = 0;
+		ctxfree = NULL;
 		spin_lock(&fl->hlock);
 		hlist_for_each_entry_safe(ictx, n, &clst->pending, hn) {
 			hlist_del_init(&ictx->hn);
@@ -981,7 +1091,7 @@
 	struct hlist_node *n;
 
 	do {
-		free = 0;
+		free = NULL;
 		spin_lock(&me->hlock);
 		hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
 			hlist_del_init(&fl->hn);
@@ -1013,13 +1123,13 @@
 	uint32_t *crclist;
 
 	/* calculate size of the metadata */
-	rpra = 0;
+	rpra = NULL;
 	list = smq_invoke_buf_start(rpra, sc);
 	pages = smq_phy_page_start(sc, list);
 	ipage = pages;
 
 	for (i = 0; i < bufs; ++i) {
-		uintptr_t buf = (uintptr_t)lpra[i].buf.pv;
+		uintptr_t __user buf = (uintptr_t __user)lpra[i].buf.pv;
 		ssize_t len = lpra[i].buf.len;
 
 		if (ctx->fds[i] && (ctx->fds[i] != -1))
@@ -1087,9 +1197,10 @@
 		list[i].pgidx = ipage - pages;
 		ipage++;
 	}
+
 	/* map ion buffers */
 	PERF(ctx->fl->profile, ctx->fl->perf.map,
-	for (i = 0; i < inbufs + outbufs; ++i) {
+	for (i = 0; rpra && i < inbufs + outbufs; ++i) {
 		struct fastrpc_mmap *map = ctx->maps[i];
 		uint64_t buf = ptr_to_uint64(lpra[i].buf.pv);
 		ssize_t len = lpra[i].buf.len;
@@ -1197,7 +1308,7 @@
 			uint64_to_ptr(rpra[i].buf.pv + rpra[i].buf.len));
 	}
 	PERF_END);
-	for (i = bufs; i < bufs + handles; i++) {
+	for (i = bufs; rpra && i < bufs + handles; i++) {
 		rpra[i].dma.fd = ctx->fds[i];
 		rpra[i].dma.len = (uint32_t)lpra[i].buf.len;
 		rpra[i].dma.offset = (uint32_t)(uintptr_t)lpra[i].buf.pv;
@@ -1243,8 +1354,8 @@
 			if (err)
 				goto bail;
 		} else {
-			fastrpc_mmap_free(ctx->maps[i]);
-			ctx->maps[i] = 0;
+			fastrpc_mmap_free(ctx->maps[i], 0);
+			ctx->maps[i] = NULL;
 		}
 	}
 	if (inbufs + outbufs + handles) {
@@ -1253,11 +1364,11 @@
 				break;
 			if (!fastrpc_mmap_find(ctx->fl, (int)fdlist[i], 0, 0,
 						0, 0, &mmap))
-				fastrpc_mmap_free(mmap);
+				fastrpc_mmap_free(mmap, 0);
 		}
 	}
 	if (ctx->crc && crclist && rpra)
-		K_COPY_TO_USER(err, kernel, (void __user *)ctx->crc,
+		K_COPY_TO_USER(err, kernel, ctx->crc,
 			crclist, M_CRCLIST*sizeof(uint32_t));
 
  bail:
@@ -1348,11 +1459,13 @@
 	struct fastrpc_channel_ctx *channel_ctx = &fl->apps->channel[fl->cid];
 	int err = 0;
 
-	VERIFY(err, 0 != channel_ctx->chan);
+	VERIFY(err, NULL != channel_ctx->chan);
 	if (err)
 		goto bail;
-	msg->pid = current->tgid;
+	msg->pid = fl->tgid;
 	msg->tid = current->pid;
+	if (fl->sessionid)
+		msg->tid |= (1 << SESSION_ID_INDEX);
 	if (kernel)
 		msg->pid = 0;
 	msg->invoke.header.ctx = ptr_to_uint64(ctx) | fl->pd;
@@ -1382,12 +1495,12 @@
 
 	INIT_HLIST_HEAD(&me->drivers);
 	spin_lock_init(&me->hlock);
-	mutex_init(&me->smd_mutex);
 	me->channel = &gcinfo[0];
 	for (i = 0; i < NUM_CHANNELS; i++) {
 		init_completion(&me->channel[i].work);
 		init_completion(&me->channel[i].workport);
 		me->channel[i].sesscount = 0;
+		mutex_init(&me->channel[i].mut);
 	}
 }
 
@@ -1397,16 +1510,17 @@
 				   uint32_t kernel,
 				   struct fastrpc_ioctl_invoke_crc *inv)
 {
-	struct smq_invoke_ctx *ctx = 0;
+	struct smq_invoke_ctx *ctx = NULL;
 	struct fastrpc_ioctl_invoke *invoke = &inv->inv;
 	int cid = fl->cid;
 	int interrupted = 0;
 	int err = 0;
-	struct timespec invoket;
+	struct timespec invoket = {0};
 
 	if (fl->profile)
 		getnstimeofday(&invoket);
 
+
 	VERIFY(err, fl->sctx != NULL);
 	if (err)
 		goto bail;
@@ -1482,8 +1596,7 @@
 	if (fl->profile && !interrupted) {
 		if (invoke->handle != FASTRPC_STATIC_HANDLE_LISTENER)
 			fl->perf.invoke += getnstimediff(&invoket);
-		if (!(invoke->handle >= 0 &&
-			invoke->handle <= FASTRPC_STATIC_HANDLE_MAX))
+		if (invoke->handle > FASTRPC_STATIC_HANDLE_MAX)
 			fl->perf.count++;
 	}
 	return err;
@@ -1494,25 +1607,31 @@
 				struct fastrpc_ioctl_init_attrs *uproc)
 {
 	int err = 0;
+	struct fastrpc_apps *me = &gfa;
 	struct fastrpc_ioctl_invoke_crc ioctl;
 	struct fastrpc_ioctl_init *init = &uproc->init;
 	struct smq_phy_page pages[1];
-	struct fastrpc_mmap *file = 0, *mem = 0;
+	struct fastrpc_mmap *file = NULL, *mem = NULL;
+	char *proc_name = NULL;
+	int srcVM[1] = {VMID_HLOS};
+	int destVM[1] = {VMID_ADSP_Q6};
+	int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+	int hlosVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
 
 	VERIFY(err, !fastrpc_channel_open(fl));
 	if (err)
 		goto bail;
 	if (init->flags == FASTRPC_INIT_ATTACH) {
 		remote_arg_t ra[1];
-		int tgid = current->tgid;
+		int tgid = fl->tgid;
 
 		ra[0].buf.pv = (void *)&tgid;
 		ra[0].buf.len = sizeof(tgid);
 		ioctl.inv.handle = 1;
 		ioctl.inv.sc = REMOTE_SCALARS_MAKE(0, 1, 0);
 		ioctl.inv.pra = ra;
-		ioctl.fds = 0;
-		ioctl.attrs = 0;
+		ioctl.fds = NULL;
+		ioctl.attrs = NULL;
 		ioctl.crc = NULL;
 		fl->pd = 0;
 		VERIFY(err, !(err = fastrpc_internal_invoke(fl,
@@ -1532,7 +1651,7 @@
 			int siglen;
 		} inbuf;
 
-		inbuf.pgid = current->tgid;
+		inbuf.pgid = fl->tgid;
 		inbuf.namelen = strlen(current->comm) + 1;
 		inbuf.filelen = init->filelen;
 		fl->pd = 1;
@@ -1542,6 +1661,7 @@
 			if (err)
 				goto bail;
 		}
+
 		inbuf.pageslen = 1;
 		VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, 0,
 				init->mem, init->memlen, mflags, &mem));
@@ -1582,7 +1702,79 @@
 			ioctl.inv.sc = REMOTE_SCALARS_MAKE(7, 6, 0);
 		ioctl.inv.pra = ra;
 		ioctl.fds = fds;
-		ioctl.attrs = 0;
+		ioctl.attrs = NULL;
+		ioctl.crc = NULL;
+		VERIFY(err, !(err = fastrpc_internal_invoke(fl,
+			FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+		if (err)
+			goto bail;
+	} else if (init->flags == FASTRPC_INIT_CREATE_STATIC) {
+		remote_arg_t ra[3];
+		uint64_t phys = 0;
+		ssize_t size = 0;
+		int fds[3];
+		struct {
+			int pgid;
+			int namelen;
+			int pageslen;
+		} inbuf;
+
+		if (!init->filelen)
+			goto bail;
+
+		proc_name = kzalloc(init->filelen, GFP_KERNEL);
+		VERIFY(err, !IS_ERR_OR_NULL(proc_name));
+		if (err)
+			goto bail;
+		VERIFY(err, 0 == copy_from_user((void *)proc_name,
+			(void __user *)init->file, init->filelen));
+		if (err)
+			goto bail;
+
+		inbuf.pgid = current->tgid;
+		inbuf.namelen = init->filelen;
+		inbuf.pageslen = 0;
+		if (!me->staticpd_flags) {
+			inbuf.pageslen = 1;
+			VERIFY(err, !fastrpc_mmap_create(fl, -1, 0, init->mem,
+				 init->memlen, ADSP_MMAP_REMOTE_HEAP_ADDR,
+				 &mem));
+			if (err)
+				goto bail;
+			phys = mem->phys;
+			size = mem->size;
+			VERIFY(err, !hyp_assign_phys(phys, (uint64_t)size,
+					srcVM, 1, destVM, destVMperm, 1));
+			if (err) {
+				pr_err("ADSPRPC: hyp_assign_phys fail err %d",
+							 err);
+				pr_err("map->phys %llx, map->size %d\n",
+							 phys, (int)size);
+				goto bail;
+			}
+			me->staticpd_flags = 1;
+		}
+
+		ra[0].buf.pv = (void *)&inbuf;
+		ra[0].buf.len = sizeof(inbuf);
+		fds[0] = 0;
+
+		ra[1].buf.pv = (void *)proc_name;
+		ra[1].buf.len = inbuf.namelen;
+		fds[1] = 0;
+
+		pages[0].addr = phys;
+		pages[0].size = size;
+
+		ra[2].buf.pv = (void *)pages;
+		ra[2].buf.len = sizeof(*pages);
+		fds[2] = 0;
+		ioctl.inv.handle = 1;
+
+		ioctl.inv.sc = REMOTE_SCALARS_MAKE(8, 3, 0);
+		ioctl.inv.pra = ra;
+		ioctl.fds = NULL;
+		ioctl.attrs = NULL;
 		ioctl.crc = NULL;
 		VERIFY(err, !(err = fastrpc_internal_invoke(fl,
 			FASTRPC_MODE_PARALLEL, 1, &ioctl)));
@@ -1592,10 +1784,17 @@
 		err = -ENOTTY;
 	}
 bail:
-	if (mem && err)
-		fastrpc_mmap_free(mem);
+	kfree(proc_name);
+	if (err && (init->flags == FASTRPC_INIT_CREATE_STATIC))
+		me->staticpd_flags = 0;
+	if (mem && err) {
+		if (mem->flags == ADSP_MMAP_REMOTE_HEAP_ADDR)
+			hyp_assign_phys(mem->phys, (uint64_t)mem->size,
+					destVM, 1, srcVM, hlosVMperm, 1);
+		fastrpc_mmap_free(mem, 0);
+	}
 	if (file)
-		fastrpc_mmap_free(file);
+		fastrpc_mmap_free(file, 0);
 	return err;
 }
 
@@ -1609,7 +1808,7 @@
 	VERIFY(err, fl->cid >= 0 && fl->cid < NUM_CHANNELS);
 	if (err)
 		goto bail;
-	VERIFY(err, fl->apps->channel[fl->cid].chan != 0);
+	VERIFY(err, fl->apps->channel[fl->cid].chan != NULL);
 	if (err)
 		goto bail;
 	tgid = fl->tgid;
@@ -1618,8 +1817,8 @@
 	ioctl.inv.handle = 1;
 	ioctl.inv.sc = REMOTE_SCALARS_MAKE(1, 1, 0);
 	ioctl.inv.pra = ra;
-	ioctl.fds = 0;
-	ioctl.attrs = 0;
+	ioctl.fds = NULL;
+	ioctl.attrs = NULL;
 	ioctl.crc = NULL;
 	VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
 		FASTRPC_MODE_PARALLEL, 1, &ioctl)));
@@ -1645,7 +1844,7 @@
 		uintptr_t vaddrout;
 	} routargs;
 
-	inargs.pid = current->tgid;
+	inargs.pid = fl->tgid;
 	inargs.vaddrin = (uintptr_t)map->va;
 	inargs.flags = flags;
 	inargs.num = fl->apps->compat ? num * sizeof(page) : num;
@@ -1665,13 +1864,86 @@
 	else
 		ioctl.inv.sc = REMOTE_SCALARS_MAKE(2, 2, 1);
 	ioctl.inv.pra = ra;
-	ioctl.fds = 0;
-	ioctl.attrs = 0;
+	ioctl.fds = NULL;
+	ioctl.attrs = NULL;
 	ioctl.crc = NULL;
 	VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
 		FASTRPC_MODE_PARALLEL, 1, &ioctl)));
 	map->raddr = (uintptr_t)routargs.vaddrout;
+	if (err)
+		goto bail;
+	if (flags == ADSP_MMAP_HEAP_ADDR) {
+		struct scm_desc desc = {0};
 
+		desc.args[0] = TZ_PIL_AUTH_QDSP6_PROC;
+		desc.args[1] = map->phys;
+		desc.args[2] = map->size;
+		desc.arginfo = SCM_ARGS(3);
+		err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL,
+			TZ_PIL_PROTECT_MEM_SUBSYS_ID), &desc);
+	} else if (flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+
+		int srcVM[1] = {VMID_HLOS};
+		int destVM[1] = {VMID_ADSP_Q6};
+		int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+		VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
+				srcVM, 1, destVM, destVMperm, 1));
+		if (err)
+			goto bail;
+	}
+bail:
+	return err;
+}
+
+static int fastrpc_munmap_on_dsp_rh(struct fastrpc_file *fl,
+				 struct fastrpc_mmap *map)
+{
+	int err = 0;
+	int srcVM[1] = {VMID_ADSP_Q6};
+	int destVM[1] = {VMID_HLOS};
+	int destVMperm[1] = {PERM_READ | PERM_WRITE | PERM_EXEC};
+
+	if (map->flags == ADSP_MMAP_HEAP_ADDR) {
+		struct fastrpc_ioctl_invoke_crc ioctl;
+		struct scm_desc desc = {0};
+		remote_arg_t ra[1];
+		int err = 0;
+		struct {
+			uint8_t skey;
+		} routargs;
+
+		ra[0].buf.pv = (void *)&routargs;
+		ra[0].buf.len = sizeof(routargs);
+
+		ioctl.inv.handle = 1;
+		ioctl.inv.sc = REMOTE_SCALARS_MAKE(7, 0, 1);
+		ioctl.inv.pra = ra;
+		ioctl.fds = NULL;
+		ioctl.attrs = NULL;
+		ioctl.crc = NULL;
+		if (fl == NULL)
+			goto bail;
+
+		VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
+				FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+		if (err)
+			goto bail;
+		desc.args[0] = TZ_PIL_AUTH_QDSP6_PROC;
+		desc.args[1] = map->phys;
+		desc.args[2] = map->size;
+		desc.args[3] = routargs.skey;
+		desc.arginfo = SCM_ARGS(4);
+		err = scm_call2(SCM_SIP_FNID(SCM_SVC_PIL,
+			TZ_PIL_CLEAR_PROTECT_MEM_SUBSYS_ID), &desc);
+	} else if (map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		VERIFY(err, !hyp_assign_phys(map->phys, (uint64_t)map->size,
+					srcVM, 1, destVM, destVMperm, 1));
+		if (err)
+			goto bail;
+	}
+
+bail:
 	return err;
 }
 
@@ -1687,7 +1959,7 @@
 		ssize_t size;
 	} inargs;
 
-	inargs.pid = current->tgid;
+	inargs.pid = fl->tgid;
 	inargs.size = map->size;
 	inargs.vaddrout = map->raddr;
 	ra[0].buf.pv = (void *)&inargs;
@@ -1699,11 +1971,66 @@
 	else
 		ioctl.inv.sc = REMOTE_SCALARS_MAKE(3, 1, 0);
 	ioctl.inv.pra = ra;
-	ioctl.fds = 0;
-	ioctl.attrs = 0;
+	ioctl.fds = NULL;
+	ioctl.attrs = NULL;
 	ioctl.crc = NULL;
 	VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl,
 		FASTRPC_MODE_PARALLEL, 1, &ioctl)));
+	if (err)
+		goto bail;
+	if (map->flags == ADSP_MMAP_HEAP_ADDR ||
+				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
+		VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, map));
+		if (err)
+			goto bail;
+	}
+bail:
+	return err;
+}
+
+static int fastrpc_mmap_remove_ssr(struct fastrpc_file *fl)
+{
+	struct fastrpc_mmap *match = NULL, *map = NULL;
+	struct hlist_node *n = NULL;
+	int err = 0, ret = 0;
+	struct fastrpc_apps *me = &gfa;
+	struct ramdump_segment *ramdump_segments_rh = NULL;
+
+	do {
+		match = NULL;
+		spin_lock(&me->hlock);
+		hlist_for_each_entry_safe(map, n, &me->maps, hn) {
+			match = map;
+			hlist_del_init(&map->hn);
+			break;
+		}
+		spin_unlock(&me->hlock);
+
+		if (match) {
+			VERIFY(err, !fastrpc_munmap_on_dsp_rh(fl, match));
+			if (err)
+				goto bail;
+			if (me->channel[0].ramdumpenabled) {
+				ramdump_segments_rh = kcalloc(1,
+				sizeof(struct ramdump_segment), GFP_KERNEL);
+				if (ramdump_segments_rh) {
+					ramdump_segments_rh->address =
+					match->phys;
+					ramdump_segments_rh->size = match->size;
+					ret = do_elf_ramdump(
+					 me->channel[0].remoteheap_ramdump_dev,
+					 ramdump_segments_rh, 1);
+					if (ret < 0)
+						pr_err("ADSPRPC: unable to dump heap");
+					kfree(ramdump_segments_rh);
+				}
+			}
+			fastrpc_mmap_free(match, 0);
+		}
+	} while (match);
+bail:
+	if (err && match)
+		fastrpc_mmap_add(match);
 	return err;
 }
 
@@ -1716,7 +2043,7 @@
 				   struct fastrpc_ioctl_munmap *ud)
 {
 	int err = 0;
-	struct fastrpc_mmap *map = 0;
+	struct fastrpc_mmap *map = NULL;
 
 	VERIFY(err, !fastrpc_mmap_remove(fl, ud->vaddrout, ud->size, &map));
 	if (err)
@@ -1724,26 +2051,50 @@
 	VERIFY(err, !fastrpc_munmap_on_dsp(fl, map));
 	if (err)
 		goto bail;
-	fastrpc_mmap_free(map);
+	fastrpc_mmap_free(map, 0);
 bail:
 	if (err && map)
 		fastrpc_mmap_add(map);
 	return err;
 }
 
+static int fastrpc_internal_munmap_fd(struct fastrpc_file *fl,
+					struct fastrpc_ioctl_munmap_fd *ud) {
+	int err = 0;
+	struct fastrpc_mmap *map = NULL;
+
+	VERIFY(err, (fl && ud));
+	if (err)
+		goto bail;
+
+	if (!fastrpc_mmap_find(fl, ud->fd, ud->va, ud->len, 0, 0, &map)) {
+		pr_err("mapping not found to unamp %x va %llx %x\n",
+			ud->fd, (unsigned long long)ud->va,
+			(unsigned int)ud->len);
+		err = -1;
+		goto bail;
+	}
+	if (map)
+	fastrpc_mmap_free(map, 0);
+bail:
+	return err;
+}
+
+
 static int fastrpc_internal_mmap(struct fastrpc_file *fl,
 				 struct fastrpc_ioctl_mmap *ud)
 {
 
-	struct fastrpc_mmap *map = 0;
+	struct fastrpc_mmap *map = NULL;
 	int err = 0;
 
-	if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t)ud->vaddrin, ud->size,
-			       ud->flags, 1, &map))
+	if (!fastrpc_mmap_find(fl, ud->fd, (uintptr_t __user)ud->vaddrin,
+			 ud->size, ud->flags, 1, &map))
 		return 0;
 
 	VERIFY(err, !fastrpc_mmap_create(fl, ud->fd, 0,
-			(uintptr_t)ud->vaddrin, ud->size, ud->flags, &map));
+			(uintptr_t __user)ud->vaddrin, ud->size,
+			 ud->flags, &map));
 	if (err)
 		goto bail;
 	VERIFY(err, 0 == fastrpc_mmap_on_dsp(fl, ud->flags, map));
@@ -1752,7 +2103,7 @@
 	ud->vaddrout = map->raddr;
  bail:
 	if (err && map)
-		fastrpc_mmap_free(map);
+		fastrpc_mmap_free(map, 0);
 	return err;
 }
 
@@ -1765,10 +2116,10 @@
 	ctx = container_of(kref, struct fastrpc_channel_ctx, kref);
 	cid = ctx - &gcinfo[0];
 	fastrpc_glink_close(ctx->chan, cid);
-	ctx->chan = 0;
+	ctx->chan = NULL;
 	glink_unregister_link_state_cb(ctx->link.link_notify_handle);
 	ctx->link.link_notify_handle = NULL;
-	mutex_unlock(&me->smd_mutex);
+	mutex_unlock(&ctx->mut);
 	pr_info("'closed /dev/%s c %d %d'\n", gcinfo[cid].name,
 						MAJOR(me->dev_no), cid);
 }
@@ -1798,6 +2149,7 @@
 		if (err)
 			goto bail;
 		chan->session[0].dev = me->dev;
+		chan->session[0].smmu.dev = me->dev;
 	}
 
 	*session = &chan->session[idx];
@@ -1805,34 +2157,43 @@
 	return err;
 }
 
-bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv, size_t size)
+static bool fastrpc_glink_notify_rx_intent_req(void *h, const void *priv,
+						size_t size)
 {
 	if (glink_queue_rx_intent(h, NULL, size))
 		return false;
 	return true;
 }
 
-void fastrpc_glink_notify_tx_done(void *handle, const void *priv,
+static void fastrpc_glink_notify_tx_done(void *handle, const void *priv,
 		const void *pkt_priv, const void *ptr)
 {
 }
 
-void fastrpc_glink_notify_rx(void *handle, const void *priv,
+static void fastrpc_glink_notify_rx(void *handle, const void *priv,
 	const void *pkt_priv, const void *ptr, size_t size)
 {
 	struct smq_invoke_rsp *rsp = (struct smq_invoke_rsp *)ptr;
-	int len = size;
+	struct smq_invoke_ctx *ctx;
+	int err = 0;
 
-	while (len >= sizeof(*rsp) && rsp) {
-		rsp->ctx = rsp->ctx & ~1;
-		context_notify_user(uint64_to_ptr(rsp->ctx), rsp->retval);
-		rsp++;
-		len = len - sizeof(*rsp);
-	}
+	VERIFY(err, (rsp && size >= sizeof(*rsp)));
+	if (err)
+		goto bail;
+
+	ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp->ctx & ~1));
+	VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC));
+	if (err)
+		goto bail;
+
+	context_notify_user(ctx, rsp->retval);
+bail:
+	if (err)
+		pr_err("adsprpc: invalid response or context\n");
 	glink_rx_done(handle, ptr, true);
 }
 
-void fastrpc_glink_notify_state(void *handle, const void *priv,
+static void fastrpc_glink_notify_state(void *handle, const void *priv,
 				unsigned int event)
 {
 	struct fastrpc_apps *me = &gfa;
@@ -1851,10 +2212,15 @@
 		link->port_state = FASTRPC_LINK_DISCONNECTED;
 		break;
 	case GLINK_REMOTE_DISCONNECTED:
+		mutex_lock(&me->channel[cid].mut);
 		if (me->channel[cid].chan) {
+			link->port_state = FASTRPC_LINK_REMOTE_DISCONNECTING;
 			fastrpc_glink_close(me->channel[cid].chan, cid);
-			me->channel[cid].chan = 0;
+			me->channel[cid].chan = NULL;
+		} else {
+			link->port_state = FASTRPC_LINK_DISCONNECTED;
 		}
+		mutex_unlock(&me->channel[cid].mut);
 		break;
 	default:
 		break;
@@ -1865,35 +2231,34 @@
 					struct fastrpc_session_ctx **session)
 {
 	int err = 0;
-	struct fastrpc_apps *me = &gfa;
 
-	mutex_lock(&me->smd_mutex);
+	mutex_lock(&chan->mut);
 	if (!*session)
 		err = fastrpc_session_alloc_locked(chan, secure, session);
-	mutex_unlock(&me->smd_mutex);
+	mutex_unlock(&chan->mut);
 	return err;
 }
 
 static void fastrpc_session_free(struct fastrpc_channel_ctx *chan,
 				struct fastrpc_session_ctx *session)
 {
-	struct fastrpc_apps *me = &gfa;
-
-	mutex_lock(&me->smd_mutex);
+	mutex_lock(&chan->mut);
 	session->used = 0;
-	mutex_unlock(&me->smd_mutex);
+	mutex_unlock(&chan->mut);
 }
 
 static int fastrpc_file_free(struct fastrpc_file *fl)
 {
 	struct hlist_node *n;
-	struct fastrpc_mmap *map = 0;
+	struct fastrpc_mmap *map = NULL;
 	int cid;
 
 	if (!fl)
 		return 0;
 	cid = fl->cid;
 
+	(void)fastrpc_release_current_dsp_process(fl);
+
 	spin_lock(&fl->apps->hlock);
 	hlist_del_init(&fl->hn);
 	spin_unlock(&fl->apps->hlock);
@@ -1902,18 +2267,17 @@
 		kfree(fl);
 		return 0;
 	}
-	(void)fastrpc_release_current_dsp_process(fl);
 	spin_lock(&fl->hlock);
 	fl->file_close = 1;
 	spin_unlock(&fl->hlock);
 	fastrpc_context_list_dtor(fl);
 	fastrpc_buf_list_free(fl);
 	hlist_for_each_entry_safe(map, n, &fl->maps, hn) {
-		fastrpc_mmap_free(map);
+		fastrpc_mmap_free(map, 1);
 	}
 	if (fl->ssrcount == fl->apps->channel[cid].ssrcount)
 		kref_put_mutex(&fl->apps->channel[cid].kref,
-				fastrpc_channel_close, &fl->apps->smd_mutex);
+			fastrpc_channel_close, &fl->apps->channel[cid].mut);
 	if (fl->sctx)
 		fastrpc_session_free(&fl->apps->channel[cid], fl->sctx);
 	if (fl->secsctx)
@@ -1932,7 +2296,7 @@
 		if (fl->debugfs_file != NULL)
 			debugfs_remove(fl->debugfs_file);
 		fastrpc_file_free(fl);
-		file->private_data = 0;
+		file->private_data = NULL;
 	}
 	return 0;
 }
@@ -1990,6 +2354,20 @@
 	return err;
 }
 
+static void fastrpc_glink_stop(int cid)
+{
+	int err = 0;
+	struct fastrpc_glink_info *link;
+
+	VERIFY(err, (cid >= 0 && cid < NUM_CHANNELS));
+	if (err)
+		return;
+	link = &gfa.channel[cid].link;
+
+	if (link->port_state == FASTRPC_LINK_CONNECTED)
+		link->port_state = FASTRPC_LINK_REMOTE_DISCONNECTING;
+}
+
 static void fastrpc_glink_close(void *chan, int cid)
 {
 	int err = 0;
@@ -2000,7 +2378,8 @@
 		return;
 	link = &gfa.channel[cid].link;
 
-	if (link->port_state == FASTRPC_LINK_CONNECTED) {
+	if (link->port_state == FASTRPC_LINK_CONNECTED ||
+		link->port_state == FASTRPC_LINK_REMOTE_DISCONNECTING) {
 		link->port_state = FASTRPC_LINK_DISCONNECTING;
 		glink_close(chan);
 	}
@@ -2038,8 +2417,11 @@
 	cfg->notify_rx_intent_req = fastrpc_glink_notify_rx_intent_req;
 	handle = glink_open(cfg);
 	VERIFY(err, !IS_ERR_OR_NULL(handle));
-	if (err)
+	if (err) {
+		if (link->port_state == FASTRPC_LINK_CONNECTING)
+			link->port_state = FASTRPC_LINK_DISCONNECTED;
 		goto bail;
+	}
 	me->channel[cid].chan = handle;
 bail:
 	return err;
@@ -2056,9 +2438,9 @@
 {
 	struct fastrpc_file *fl = filp->private_data;
 	struct hlist_node *n;
-	struct fastrpc_buf *buf = 0;
-	struct fastrpc_mmap *map = 0;
-	struct smq_invoke_ctx *ictx = 0;
+	struct fastrpc_buf *buf = NULL;
+	struct fastrpc_mmap *map = NULL;
+	struct smq_invoke_ctx *ictx = NULL;
 	struct fastrpc_channel_ctx *chan;
 	struct fastrpc_session_ctx *sess;
 	unsigned int len = 0;
@@ -2163,18 +2545,25 @@
 	struct fastrpc_apps *me = &gfa;
 	int cid, err = 0;
 
-	mutex_lock(&me->smd_mutex);
-
 	VERIFY(err, fl && fl->sctx);
 	if (err)
-		goto bail;
+		return err;
 	cid = fl->cid;
 	VERIFY(err, cid >= 0 && cid < NUM_CHANNELS);
 	if (err)
 		goto bail;
+	mutex_lock(&me->channel[cid].mut);
+	if (me->channel[cid].ssrcount !=
+				 me->channel[cid].prevssrcount) {
+		if (!me->channel[cid].issubsystemup) {
+			VERIFY(err, 0);
+			if (err)
+				goto bail;
+		}
+	}
 	fl->ssrcount = me->channel[cid].ssrcount;
 	if ((kref_get_unless_zero(&me->channel[cid].kref) == 0) ||
-	    (me->channel[cid].chan == 0)) {
+	    (me->channel[cid].chan == NULL)) {
 		VERIFY(err, 0 == fastrpc_glink_register(cid, me));
 		if (err)
 			goto bail;
@@ -2182,30 +2571,36 @@
 		if (err)
 			goto bail;
 
+		mutex_unlock(&me->channel[cid].mut);
 		VERIFY(err,
 			 wait_for_completion_timeout(&me->channel[cid].workport,
 						RPC_TIMEOUT));
+		mutex_lock(&me->channel[cid].mut);
 		if (err) {
-			me->channel[cid].chan = 0;
+			me->channel[cid].chan = NULL;
 			goto bail;
 		}
 		kref_init(&me->channel[cid].kref);
 		pr_info("'opened /dev/%s c %d %d'\n", gcinfo[cid].name,
 						MAJOR(me->dev_no), cid);
-		err = glink_queue_rx_intent(me->channel[cid].chan, NULL, 16);
-		err |= glink_queue_rx_intent(me->channel[cid].chan, NULL, 64);
+		err = glink_queue_rx_intent(me->channel[cid].chan, NULL,
+			FASTRPC_GLINK_INTENT_LEN);
+		err |= glink_queue_rx_intent(me->channel[cid].chan, NULL,
+			FASTRPC_GLINK_INTENT_LEN);
 		if (err)
 			pr_warn("adsprpc: initial intent fail for %d err %d\n",
 					 cid, err);
 		if (me->channel[cid].ssrcount !=
 				 me->channel[cid].prevssrcount) {
+			if (fastrpc_mmap_remove_ssr(fl))
+				pr_err("ADSPRPC: SSR: Failed to unmap remote heap\n");
 			me->channel[cid].prevssrcount =
 						me->channel[cid].ssrcount;
 		}
 	}
 
 bail:
-	mutex_unlock(&me->smd_mutex);
+	mutex_unlock(&me->channel[cid].mut);
 	return err;
 }
 
@@ -2213,10 +2608,10 @@
 {
 	int err = 0;
 	struct dentry *debugfs_file;
-	struct fastrpc_file *fl = 0;
+	struct fastrpc_file *fl = NULL;
 	struct fastrpc_apps *me = &gfa;
 
-	VERIFY(err, fl = kzalloc(sizeof(*fl), GFP_KERNEL));
+	VERIFY(err, NULL != (fl = kzalloc(sizeof(*fl), GFP_KERNEL)));
 	if (err)
 		return err;
 	debugfs_file = debugfs_create_file(current->comm, 0644, debugfs_root,
@@ -2226,6 +2621,7 @@
 	INIT_HLIST_HEAD(&fl->maps);
 	INIT_HLIST_HEAD(&fl->bufs);
 	INIT_HLIST_NODE(&fl->hn);
+	fl->sessionid = 0;
 	fl->tgid = current->tgid;
 	fl->apps = me;
 	fl->mode = FASTRPC_MODE_SERIAL;
@@ -2246,7 +2642,7 @@
 	int err = 0;
 	uint32_t cid;
 
-	VERIFY(err, fl != 0);
+	VERIFY(err, fl != NULL);
 	if (err)
 		goto bail;
 	if (fl->cid == -1) {
@@ -2311,6 +2707,7 @@
 		struct fastrpc_ioctl_invoke_crc inv;
 		struct fastrpc_ioctl_mmap mmap;
 		struct fastrpc_ioctl_munmap munmap;
+		struct fastrpc_ioctl_munmap_fd munmap_fd;
 		struct fastrpc_ioctl_init_attrs init;
 		struct fastrpc_ioctl_perf perf;
 		struct fastrpc_ioctl_control cp;
@@ -2320,8 +2717,8 @@
 	int size = 0, err = 0;
 	uint32_t info;
 
-	p.inv.fds = 0;
-	p.inv.attrs = 0;
+	p.inv.fds = NULL;
+	p.inv.attrs = NULL;
 	p.inv.crc = NULL;
 	spin_lock(&fl->hlock);
 	if (fl->file_close == 1) {
@@ -2347,7 +2744,7 @@
 	case FASTRPC_IOCTL_INVOKE_CRC:
 		if (!size)
 			size = sizeof(struct fastrpc_ioctl_invoke_crc);
-		VERIFY(err, 0 == copy_from_user(&p.inv, param, size));
+		K_COPY_FROM_USER(err, 0, &p.inv, param, size);
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == (err = fastrpc_internal_invoke(fl, fl->mode,
@@ -2356,20 +2753,20 @@
 			goto bail;
 		break;
 	case FASTRPC_IOCTL_MMAP:
-		VERIFY(err, 0 == copy_from_user(&p.mmap, param,
-						sizeof(p.mmap)));
+		K_COPY_FROM_USER(err, 0, &p.mmap, param,
+						sizeof(p.mmap));
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == (err = fastrpc_internal_mmap(fl, &p.mmap)));
 		if (err)
 			goto bail;
-		VERIFY(err, 0 == copy_to_user(param, &p.mmap, sizeof(p.mmap)));
+		K_COPY_TO_USER(err, 0, param, &p.mmap, sizeof(p.mmap));
 		if (err)
 			goto bail;
 		break;
 	case FASTRPC_IOCTL_MUNMAP:
-		VERIFY(err, 0 == copy_from_user(&p.munmap, param,
-						sizeof(p.munmap)));
+		K_COPY_FROM_USER(err, 0, &p.munmap, param,
+						sizeof(p.munmap));
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == (err = fastrpc_internal_munmap(fl,
@@ -2377,6 +2774,16 @@
 		if (err)
 			goto bail;
 		break;
+	case FASTRPC_IOCTL_MUNMAP_FD:
+		K_COPY_FROM_USER(err, 0, &p.munmap_fd, param,
+			sizeof(p.munmap_fd));
+		if (err)
+			goto bail;
+		VERIFY(err, 0 == (err = fastrpc_internal_munmap_fd(fl,
+			&p.munmap_fd)));
+		if (err)
+			goto bail;
+		break;
 	case FASTRPC_IOCTL_SETMODE:
 		switch ((uint32_t)ioctl_param) {
 		case FASTRPC_MODE_PARALLEL:
@@ -2386,36 +2793,40 @@
 		case FASTRPC_MODE_PROFILE:
 			fl->profile = (uint32_t)ioctl_param;
 			break;
+		case FASTRPC_MODE_SESSION:
+			fl->sessionid = 1;
+			fl->tgid |= (1 << SESSION_ID_INDEX);
+			break;
 		default:
 			err = -ENOTTY;
 			break;
 		}
 		break;
 	case FASTRPC_IOCTL_GETPERF:
-		VERIFY(err, 0 == copy_from_user(&p.perf,
-					param, sizeof(p.perf)));
+		K_COPY_FROM_USER(err, 0, &p.perf,
+					param, sizeof(p.perf));
 		if (err)
 			goto bail;
 		p.perf.numkeys = sizeof(struct fastrpc_perf)/sizeof(int64_t);
 		if (p.perf.keys) {
 			char *keys = PERF_KEYS;
 
-			VERIFY(err, 0 == copy_to_user((char *)p.perf.keys,
-						 keys, strlen(keys)+1));
+			K_COPY_TO_USER(err, 0, (void *)p.perf.keys,
+						 keys, strlen(keys)+1);
 			if (err)
 				goto bail;
 		}
 		if (p.perf.data) {
-			VERIFY(err, 0 == copy_to_user((int64_t *)p.perf.data,
-						 &fl->perf, sizeof(fl->perf)));
+			K_COPY_TO_USER(err, 0, (void *)p.perf.data,
+						 &fl->perf, sizeof(fl->perf));
 		}
-		VERIFY(err, 0 == copy_to_user(param, &p.perf, sizeof(p.perf)));
+		K_COPY_TO_USER(err, 0, param, &p.perf, sizeof(p.perf));
 		if (err)
 			goto bail;
 		break;
 	case FASTRPC_IOCTL_CONTROL:
-		VERIFY(err, 0 == copy_from_user(&p.cp, (void __user *)param,
-				sizeof(p.cp)));
+		K_COPY_FROM_USER(err, 0, &p.cp, param,
+				sizeof(p.cp));
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == (err = fastrpc_internal_control(fl, &p.cp)));
@@ -2423,13 +2834,13 @@
 			goto bail;
 		break;
 	case FASTRPC_IOCTL_GETINFO:
-	    VERIFY(err, 0 == copy_from_user(&info, param, sizeof(info)));
+	    K_COPY_FROM_USER(err, 0, &info, param, sizeof(info));
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == (err = fastrpc_get_info(fl, &info)));
 		if (err)
 			goto bail;
-		VERIFY(err, 0 == copy_to_user(param, &info, sizeof(info)));
+		K_COPY_TO_USER(err, 0, param, &info, sizeof(info));
 		if (err)
 			goto bail;
 		break;
@@ -2441,11 +2852,15 @@
 	case FASTRPC_IOCTL_INIT_ATTRS:
 		if (!size)
 			size = sizeof(struct fastrpc_ioctl_init_attrs);
-		VERIFY(err, 0 == copy_from_user(&p.init, param, size));
+		K_COPY_FROM_USER(err, 0, &p.init, param, size);
 		if (err)
 			goto bail;
 		VERIFY(err, p.init.init.filelen >= 0 &&
-			p.init.init.memlen >= 0);
+			p.init.init.filelen < INIT_FILELEN_MAX);
+		if (err)
+			goto bail;
+		VERIFY(err, p.init.init.memlen >= 0 &&
+			p.init.init.memlen < INIT_MEMLEN_MAX);
 		if (err)
 			goto bail;
 		VERIFY(err, 0 == fastrpc_init_process(fl, &p.init));
@@ -2468,21 +2883,30 @@
 {
 	struct fastrpc_apps *me = &gfa;
 	struct fastrpc_channel_ctx *ctx;
+	struct notif_data *notifdata = data;
 	int cid;
 
 	ctx = container_of(nb, struct fastrpc_channel_ctx, nb);
 	cid = ctx - &me->channel[0];
 	if (code == SUBSYS_BEFORE_SHUTDOWN) {
-		mutex_lock(&me->smd_mutex);
+		mutex_lock(&ctx->mut);
 		ctx->ssrcount++;
-		if (ctx->chan) {
-			fastrpc_glink_close(ctx->chan, cid);
-			ctx->chan = 0;
-			pr_info("'restart notifier: closed /dev/%s c %d %d'\n",
-				 gcinfo[cid].name, MAJOR(me->dev_no), cid);
-		}
-		mutex_unlock(&me->smd_mutex);
+		ctx->issubsystemup = 0;
+		pr_info("'restart notifier: /dev/%s c %d %d'\n",
+			 gcinfo[cid].name, MAJOR(me->dev_no), cid);
+		if (ctx->chan)
+			fastrpc_glink_stop(cid);
+		mutex_unlock(&ctx->mut);
+		if (cid == 0)
+			me->staticpd_flags = 0;
 		fastrpc_notify_drivers(me, cid);
+	} else if (code == SUBSYS_RAMDUMP_NOTIFICATION) {
+		if (me->channel[0].remoteheap_ramdump_dev &&
+				notifdata->enable_ramdump) {
+			me->channel[0].ramdumpenabled = 1;
+		}
+	} else if (code == SUBSYS_AFTER_POWERUP) {
+		ctx->issubsystemup = 1;
 	}
 
 	return NOTIFY_DONE;
@@ -2513,7 +2937,8 @@
 	int err = 0, i;
 	int secure_vmid = VMID_CP_PIXEL;
 
-	VERIFY(err, 0 != (name = of_get_property(dev->of_node, "label", NULL)));
+	VERIFY(err, NULL != (name = of_get_property(dev->of_node,
+					 "label", NULL)));
 	if (err)
 		goto bail;
 	for (i = 0; i < NUM_CHANNELS; i++) {
@@ -2557,7 +2982,7 @@
 	VERIFY(err, !arm_iommu_attach_device(dev, sess->smmu.mapping));
 	if (err)
 		goto bail;
-	sess->dev = dev;
+	sess->smmu.dev = dev;
 	sess->smmu.enabled = 1;
 	chan->sesscount++;
 	debugfs_global_file = debugfs_create_file("global", 0644, debugfs_root,
@@ -2636,26 +3061,25 @@
 
 static void fastrpc_deinit(void)
 {
-	struct fastrpc_apps *me = &gfa;
 	struct fastrpc_channel_ctx *chan = gcinfo;
 	int i, j;
 
 	for (i = 0; i < NUM_CHANNELS; i++, chan++) {
 		if (chan->chan) {
 			kref_put_mutex(&chan->kref,
-				fastrpc_channel_close, &me->smd_mutex);
-			chan->chan = 0;
+				fastrpc_channel_close, &chan->mut);
+			chan->chan = NULL;
+			mutex_destroy(&chan->mut);
 		}
 		for (j = 0; j < NUM_SESSIONS; j++) {
 			struct fastrpc_session_ctx *sess = &chan->session[j];
-
-			if (sess->smmu.enabled) {
-				arm_iommu_detach_device(sess->dev);
-				sess->dev = 0;
+			if (sess->smmu.dev) {
+				arm_iommu_detach_device(sess->smmu.dev);
+				sess->smmu.dev = NULL;
 			}
 			if (sess->smmu.mapping) {
 				arm_iommu_release_mapping(sess->smmu.mapping);
-				sess->smmu.mapping = 0;
+				sess->smmu.mapping = NULL;
 			}
 		}
 	}
@@ -2673,7 +3097,7 @@
 static int __init fastrpc_device_init(void)
 {
 	struct fastrpc_apps *me = &gfa;
-	struct device *dev = 0;
+	struct device *dev = NULL;
 	int err = 0, i;
 
 	memset(me, 0, sizeof(*me));
@@ -2708,6 +3132,9 @@
 		me->channel[i].dev = dev;
 		me->channel[i].ssrcount = 0;
 		me->channel[i].prevssrcount = 0;
+		me->channel[i].issubsystemup = 1;
+		me->channel[i].ramdumpenabled = 0;
+		me->channel[i].remoteheap_ramdump_dev = NULL;
 		me->channel[i].nb.notifier_call = fastrpc_restart_notifier_cb;
 		me->channel[i].handle = subsys_notif_register_notifier(
 							gcinfo[i].subsys,
diff --git a/drivers/char/adsprpc_shared.h b/drivers/char/adsprpc_shared.h
index 9f964d2..e2f8983 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -29,6 +29,7 @@
 #define FASTRPC_IOCTL_INIT_ATTRS _IOWR('R', 10, struct fastrpc_ioctl_init_attrs)
 #define FASTRPC_IOCTL_INVOKE_CRC _IOWR('R', 11, struct fastrpc_ioctl_invoke_crc)
 #define FASTRPC_IOCTL_CONTROL   _IOWR('R', 12, struct fastrpc_ioctl_control)
+#define FASTRPC_IOCTL_MUNMAP_FD _IOWR('R', 13, struct fastrpc_ioctl_munmap_fd)
 
 #define FASTRPC_GLINK_GUID "fastrpcglink-apps-dsp"
 #define FASTRPC_SMD_GUID "fastrpcsmd-apps-dsp"
@@ -43,6 +44,9 @@
 /* Set for buffers that are dma coherent */
 #define FASTRPC_ATTR_COHERENT 0x4
 
+/* Fastrpc attribute for keeping the map persistent */
+#define FASTRPC_ATTR_KEEP_MAP	0x8
+
 /* Driver should operate in parallel with the co-processor */
 #define FASTRPC_MODE_PARALLEL    0
 
@@ -52,9 +56,13 @@
 /* Driver should operate in profile mode with the co-processor */
 #define FASTRPC_MODE_PROFILE     2
 
+/* Set FastRPC session ID to 1 */
+#define FASTRPC_MODE_SESSION     4
+
 /* INIT a new process or attach to guestos */
 #define FASTRPC_INIT_ATTACH      0
 #define FASTRPC_INIT_CREATE      1
+#define FASTRPC_INIT_CREATE_STATIC  2
 
 /* Retrives number of input buffers from the scalars parameter */
 #define REMOTE_SCALARS_INBUFS(sc)        (((sc) >> 16) & 0x0ff)
@@ -200,6 +208,13 @@
 	uintptr_t vaddrout;		/* dsps virtual address */
 };
 
+struct fastrpc_ioctl_munmap_fd {
+	int     fd;				/* fd */
+	uint32_t  flags;		/* control flags */
+	uintptr_t va;			/* va */
+	ssize_t  len;			/* length */
+};
+
 struct fastrpc_ioctl_perf {			/* kernel performance data */
 	uintptr_t __user data;
 	uint32_t numkeys;
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index e2d39e7..0aad08a 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -134,6 +134,35 @@
 void diag_dci_record_traffic(int read_bytes, uint8_t ch_type,
 			     uint8_t peripheral, uint8_t proc) { }
 #endif
+
+static int check_peripheral_dci_support(int peripheral_id, int dci_proc_id)
+{
+	int dci_peripheral_list = 0;
+
+	if (dci_proc_id < 0 || dci_proc_id >= NUM_DCI_PROC) {
+		pr_err("diag:In %s,not a supported DCI proc id\n", __func__);
+		return 0;
+	}
+	if (peripheral_id < 0 || peripheral_id >= NUM_PERIPHERALS) {
+		pr_err("diag:In %s,not a valid peripheral id\n", __func__);
+		return 0;
+	}
+	dci_peripheral_list = dci_ops_tbl[dci_proc_id].peripheral_status;
+
+	if (dci_peripheral_list <= 0 || dci_peripheral_list > DIAG_CON_ALL) {
+		pr_err("diag:In %s,not a valid dci peripheral mask\n",
+			 __func__);
+		return 0;
+	}
+	/* Remove APSS bit mask information */
+	dci_peripheral_list = dci_peripheral_list >> 1;
+
+	if ((1 << peripheral_id) & (dci_peripheral_list))
+		return 1;
+	else
+		return 0;
+}
+
 static void create_dci_log_mask_tbl(unsigned char *mask, uint8_t dirty)
 {
 	unsigned char *temp = mask;
@@ -1440,6 +1469,8 @@
 	struct siginfo info;
 	struct list_head *start, *temp;
 	struct diag_dci_client_tbl *entry = NULL;
+	struct pid *pid_struct = NULL;
+	struct task_struct *dci_task = NULL;
 
 	memset(&info, 0, sizeof(struct siginfo));
 	info.si_code = SI_QUEUE;
@@ -1457,20 +1488,32 @@
 			continue;
 		if (entry->client_info.notification_list & peripheral_mask) {
 			info.si_signo = entry->client_info.signal_type;
-			if (entry->client &&
-				entry->tgid == entry->client->tgid) {
-				DIAG_LOG(DIAG_DEBUG_DCI,
-					"entry tgid = %d, dci client tgid = %d\n",
-					entry->tgid, entry->client->tgid);
-				stat = send_sig_info(
-					entry->client_info.signal_type,
-					&info, entry->client);
-				if (stat)
-					pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
+			pid_struct = find_get_pid(entry->tgid);
+			if (pid_struct) {
+				dci_task = get_pid_task(pid_struct,
+						PIDTYPE_PID);
+				if (!dci_task) {
+					DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+						"diag: dci client with pid = %d Exited..\n",
+						entry->tgid);
+					mutex_unlock(&driver->dci_mutex);
+					return;
+				}
+				if (entry->client &&
+					entry->tgid == dci_task->tgid) {
+					DIAG_LOG(DIAG_DEBUG_DCI,
+						"entry tgid = %d, dci client tgid = %d\n",
+						entry->tgid, dci_task->tgid);
+					stat = send_sig_info(
+						entry->client_info.signal_type,
+						&info, dci_task);
+					if (stat)
+						pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
 							info.si_int, stat);
-			} else
-				pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n",
+				} else
+					pr_err("diag: client data is corrupted, signal data: 0x%x, stat: %d\n",
 						info.si_int, stat);
+			}
 		}
 	}
 	mutex_unlock(&driver->dci_mutex);
@@ -2396,10 +2439,12 @@
 		 * is down. It may also mean that the peripheral doesn't
 		 * support DCI.
 		 */
-		err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf,
-					  header_size + DCI_EVENT_MASK_SIZE);
-		if (err != DIAG_DCI_NO_ERROR)
-			ret = DIAG_DCI_SEND_DATA_FAIL;
+		if (check_peripheral_dci_support(i, DCI_LOCAL_PROC)) {
+			err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf,
+				  header_size + DCI_EVENT_MASK_SIZE);
+			if (err != DIAG_DCI_NO_ERROR)
+				ret = DIAG_DCI_SEND_DATA_FAIL;
+		}
 	}
 
 	mutex_unlock(&event_mask.lock);
@@ -2581,11 +2626,13 @@
 		}
 		write_len = dci_fill_log_mask(buf, log_mask_ptr);
 		for (j = 0; j < NUM_PERIPHERALS && write_len; j++) {
-			err = diag_dci_write_proc(j, DIAG_CNTL_TYPE, buf,
-						  write_len);
-			if (err != DIAG_DCI_NO_ERROR) {
-				updated = 0;
-				ret = DIAG_DCI_SEND_DATA_FAIL;
+			if (check_peripheral_dci_support(j, DCI_LOCAL_PROC)) {
+				err = diag_dci_write_proc(j, DIAG_CNTL_TYPE,
+					buf, write_len);
+				if (err != DIAG_DCI_NO_ERROR) {
+					updated = 0;
+					ret = DIAG_DCI_SEND_DATA_FAIL;
+				}
 			}
 		}
 		if (updated)
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 177bbdb..0a3faba 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -50,7 +50,7 @@
 static int diag_dbgfs_finished;
 static int diag_dbgfs_dci_data_index;
 static int diag_dbgfs_dci_finished;
-
+static struct mutex diag_dci_dbgfs_mutex;
 static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
 				      size_t count, loff_t *ppos)
 {
@@ -77,7 +77,8 @@
 		"Time Sync Enabled: %d\n"
 		"MD session mode: %d\n"
 		"MD session mask: %d\n"
-		"Uses Time API: %d\n",
+		"Uses Time API: %d\n"
+		"Supports PD buffering: %d\n",
 		chk_config_get_id(),
 		chk_polling_response(),
 		driver->polling_reg_flag,
@@ -92,11 +93,12 @@
 		driver->time_sync_enabled,
 		driver->md_session_mode,
 		driver->md_session_mask,
-		driver->uses_time_api);
+		driver->uses_time_api,
+		driver->supports_pd_buffering);
 
 	for (i = 0; i < NUM_PERIPHERALS; i++) {
 		ret += scnprintf(buf+ret, buf_size-ret,
-			"p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c%c|\n",
+			"p: %s Feature: %02x %02x |%c%c%c%c%c%c%c%c%c%c|\n",
 			PERIPHERAL_STRING(i),
 			driver->feature[i].feature_mask[0],
 			driver->feature[i].feature_mask[1],
@@ -105,6 +107,7 @@
 			driver->feature[i].encode_hdlc ? 'H':'h',
 			driver->feature[i].peripheral_buffering ? 'B':'b',
 			driver->feature[i].mask_centralization ? 'M':'m',
+			driver->feature[i].pd_buffering ? 'P':'p',
 			driver->feature[i].stm_support ? 'Q':'q',
 			driver->feature[i].sockets_enabled ? 'S':'s',
 			driver->feature[i].sent_feature_mask ? 'T':'t',
@@ -152,6 +155,7 @@
 	buf_size = ksize(buf);
 	bytes_remaining = buf_size;
 
+	mutex_lock(&diag_dci_dbgfs_mutex);
 	if (diag_dbgfs_dci_data_index == 0) {
 		bytes_written =
 			scnprintf(buf, buf_size,
@@ -207,8 +211,8 @@
 		}
 		temp_data++;
 	}
-
 	diag_dbgfs_dci_data_index = (i >= DIAG_DCI_DEBUG_CNT) ? 0 : i + 1;
+	mutex_unlock(&diag_dci_dbgfs_mutex);
 	bytes_written = simple_read_from_buffer(ubuf, count, ppos, buf,
 								bytes_in_buf);
 	kfree(buf);
@@ -1061,6 +1065,7 @@
 		pr_warn("diag: could not allocate memory for dci debug info\n");
 
 	mutex_init(&dci_stat_mutex);
+	mutex_init(&diag_dci_dbgfs_mutex);
 	return 0;
 err:
 	kfree(dci_traffic);
@@ -1074,6 +1079,7 @@
 	diag_dbgfs_dent = NULL;
 	kfree(dci_traffic);
 	mutex_destroy(&dci_stat_mutex);
+	mutex_destroy(&diag_dci_dbgfs_mutex);
 }
 #else
 int diag_debugfs_init(void) { return 0; }
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index d734e29..8e5d836 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -28,8 +28,7 @@
 #define DIAG_SET_FEATURE_MASK(x) (feature_bytes[(x)/8] |= (1 << (x & 0x7)))
 
 #define diag_check_update(x)	\
-	(!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x))) \
-	|| (info && (info->peripheral_mask & MD_PERIPHERAL_PD_MASK(x)))) \
+	(!info || (info && (info->peripheral_mask & MD_PERIPHERAL_MASK(x)))) \
 
 struct diag_mask_info msg_mask;
 struct diag_mask_info msg_bt_mask;
@@ -87,16 +86,15 @@
 
 static void diag_send_log_mask_update(uint8_t peripheral, int equip_id)
 {
-	int i;
-	int err = 0;
-	int send_once = 0;
+	int err = 0, send_once = 0, i;
 	int header_len = sizeof(struct diag_ctrl_log_mask);
 	uint8_t *buf = NULL, *temp = NULL;
 	uint8_t upd = 0;
-	uint32_t mask_size = 0;
+	uint32_t mask_size = 0, pd_mask = 0;
 	struct diag_ctrl_log_mask ctrl_pkt;
 	struct diag_mask_info *mask_info = NULL;
 	struct diag_log_mask_t *mask = NULL;
+	struct diagfwd_info *fwd_info = NULL;
 
 	if (peripheral >= NUM_PERIPHERALS)
 		return;
@@ -108,13 +106,14 @@
 		return;
 	}
 
+	MD_PERIPHERAL_PD_MASK(TYPE_CNTL, peripheral, pd_mask);
+
 	if (driver->md_session_mask != 0) {
 		if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
 			if (driver->md_session_map[peripheral])
 				mask_info =
 				driver->md_session_map[peripheral]->log_mask;
-		} else if (driver->md_session_mask &
-				MD_PERIPHERAL_PD_MASK(peripheral)) {
+		} else if (driver->md_session_mask & pd_mask) {
 			upd = diag_mask_to_pd_value(driver->md_session_mask);
 			if (upd && driver->md_session_map[upd])
 				mask_info =
@@ -213,12 +212,12 @@
 {
 	uint8_t *buf = NULL, *temp = NULL;
 	uint8_t upd = 0;
+	uint32_t pd_mask = 0;
+	int num_bytes = EVENT_COUNT_TO_BYTES(driver->last_event_id);
+	int write_len = 0, err = 0, i = 0, temp_len = 0;
 	struct diag_ctrl_event_mask header;
 	struct diag_mask_info *mask_info = NULL;
-	int num_bytes = EVENT_COUNT_TO_BYTES(driver->last_event_id);
-	int write_len = 0;
-	int err = 0;
-	int temp_len = 0;
+	struct diagfwd_info *fwd_info = NULL;
 
 	if (num_bytes <= 0 || num_bytes > driver->event_mask_size) {
 		pr_debug("diag: In %s, invalid event mask length %d\n",
@@ -236,13 +235,14 @@
 		return;
 	}
 
+	MD_PERIPHERAL_PD_MASK(TYPE_CNTL, peripheral, pd_mask);
+
 	if (driver->md_session_mask != 0) {
 		if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
 			if (driver->md_session_map[peripheral])
 				mask_info =
 				driver->md_session_map[peripheral]->event_mask;
-		} else if (driver->md_session_mask &
-				MD_PERIPHERAL_PD_MASK(peripheral)) {
+		} else if (driver->md_session_mask & pd_mask) {
 			upd = diag_mask_to_pd_value(driver->md_session_mask);
 			if (upd && driver->md_session_map[upd])
 				mask_info =
@@ -310,17 +310,16 @@
 
 static void diag_send_msg_mask_update(uint8_t peripheral, int first, int last)
 {
-	int i;
-	int err = 0;
+	int i, err = 0, temp_len = 0;
 	int header_len = sizeof(struct diag_ctrl_msg_mask);
-	int temp_len = 0;
 	uint8_t *buf = NULL, *temp = NULL;
 	uint8_t upd = 0;
-	uint32_t mask_size = 0;
+	uint8_t msg_mask_tbl_count_local;
+	uint32_t mask_size = 0, pd_mask = 0;
 	struct diag_mask_info *mask_info = NULL;
 	struct diag_msg_mask_t *mask = NULL;
 	struct diag_ctrl_msg_mask header;
-	uint8_t msg_mask_tbl_count_local;
+	struct diagfwd_info *fwd_info = NULL;
 
 	if (peripheral >= NUM_PERIPHERALS)
 		return;
@@ -332,13 +331,14 @@
 		return;
 	}
 
+	MD_PERIPHERAL_PD_MASK(TYPE_CNTL, peripheral, pd_mask);
+
 	if (driver->md_session_mask != 0) {
 		if (driver->md_session_mask & MD_PERIPHERAL_MASK(peripheral)) {
 			if (driver->md_session_map[peripheral])
 				mask_info =
 				driver->md_session_map[peripheral]->msg_mask;
-		} else if (driver->md_session_mask &
-				MD_PERIPHERAL_PD_MASK(peripheral)) {
+		} else if (driver->md_session_mask & pd_mask) {
 			upd = diag_mask_to_pd_value(driver->md_session_mask);
 			if (upd && driver->md_session_map[upd])
 				mask_info =
@@ -510,7 +510,7 @@
 	if (driver->supports_apps_hdlc_encoding)
 		DIAG_SET_FEATURE_MASK(F_DIAG_APPS_HDLC_ENCODE);
 	if (driver->supports_apps_header_untagging) {
-		if (peripheral == PERIPHERAL_MODEM) {
+		if (driver->feature[peripheral].untag_header) {
 			DIAG_SET_FEATURE_MASK(F_DIAG_PKT_HEADER_UNTAG);
 			driver->peripheral_untag[peripheral] =
 				ENABLE_PKT_HEADER_UNTAGGING;
@@ -554,6 +554,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	if (!diag_apps_responds())
 		return 0;
@@ -655,7 +660,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	if (!diag_apps_responds())
 		return 0;
 
@@ -668,6 +677,12 @@
 	rsp.status = MSG_STATUS_FAIL;
 	rsp.padding = 0;
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		if ((req->ssid_first < mask->ssid_first) ||
 		    (req->ssid_first > mask->ssid_last_tools)) {
@@ -692,18 +707,15 @@
 				 unsigned char *dest_buf, int dest_len,
 				 struct diag_md_session_t *info)
 {
-	int i;
-	int write_len = 0;
+	uint32_t mask_size = 0, offset = 0;
+	uint32_t *temp = NULL;
+	int write_len = 0, i = 0, found = 0, peripheral;
 	int header_len = sizeof(struct diag_msg_build_mask_t);
-	int found = 0;
-	uint32_t mask_size = 0;
-	uint32_t offset = 0;
 	struct diag_msg_mask_t *mask = NULL;
 	struct diag_msg_build_mask_t *req = NULL;
 	struct diag_msg_build_mask_t rsp;
 	struct diag_mask_info *mask_info = NULL;
 	struct diag_msg_mask_t *mask_next = NULL;
-	uint32_t *temp = NULL;
 
 	mask_info = (!info) ? &msg_mask : info->msg_mask;
 	if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -713,11 +725,23 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_msg_build_mask_t *)src_buf;
 	mutex_lock(&mask_info->lock);
 	mutex_lock(&driver->msg_mask_lock);
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		if (i < (driver->msg_mask_tbl_count - 1)) {
 			mask_next = mask;
@@ -799,11 +823,18 @@
 		mask_size = dest_len - write_len;
 	memcpy(dest_buf + write_len, src_buf + header_len, mask_size);
 	write_len += mask_size;
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_msg_mask_update(i, req->ssid_first, req->ssid_last);
+		diag_send_msg_mask_update(peripheral, req->ssid_first,
+			req->ssid_last);
 		mutex_unlock(&driver->md_session_lock);
 	}
 end:
@@ -814,8 +845,7 @@
 				     unsigned char *dest_buf, int dest_len,
 				     struct diag_md_session_t *info)
 {
-	int i;
-	int write_len = 0;
+	int i, write_len = 0, peripheral;
 	int header_len = sizeof(struct diag_msg_config_rsp_t);
 	struct diag_msg_config_rsp_t rsp;
 	struct diag_msg_config_rsp_t *req = NULL;
@@ -830,6 +860,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_msg_config_rsp_t *)src_buf;
 
@@ -837,6 +872,13 @@
 	mutex_lock(&driver->msg_mask_lock);
 
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	mask_info->status = (req->rt_mask) ? DIAG_CTRL_MASK_ALL_ENABLED :
 					   DIAG_CTRL_MASK_ALL_DISABLED;
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
@@ -863,11 +905,17 @@
 	memcpy(dest_buf, &rsp, header_len);
 	write_len += header_len;
 
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_msg_mask_update(i, ALL_SSID, ALL_SSID);
+		diag_send_msg_mask_update(peripheral, ALL_SSID, ALL_SSID);
 		mutex_unlock(&driver->md_session_lock);
 	}
 
@@ -914,9 +962,7 @@
 				      unsigned char *dest_buf, int dest_len,
 				      struct diag_md_session_t *info)
 {
-	int i;
-	int write_len = 0;
-	int mask_len = 0;
+	int i, write_len = 0, mask_len = 0, peripheral;
 	int header_len = sizeof(struct diag_event_mask_config_t);
 	struct diag_event_mask_config_t rsp;
 	struct diag_event_mask_config_t *req;
@@ -930,7 +976,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	req = (struct diag_event_mask_config_t *)src_buf;
 	mask_len = EVENT_COUNT_TO_BYTES(req->num_bits);
 	if (mask_len <= 0 || mask_len > event_mask.mask_len) {
@@ -959,11 +1009,17 @@
 	memcpy(dest_buf + write_len, mask_info->ptr, mask_len);
 	write_len += mask_len;
 
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_event_mask_update(i);
+		diag_send_event_mask_update(peripheral);
 		mutex_unlock(&driver->md_session_lock);
 	}
 
@@ -974,8 +1030,7 @@
 				  unsigned char *dest_buf, int dest_len,
 				  struct diag_md_session_t *info)
 {
-	int i;
-	int write_len = 0;
+	int write_len = 0, i, peripheral;
 	uint8_t toggle = 0;
 	struct diag_event_report_t header;
 	struct diag_mask_info *mask_info = NULL;
@@ -988,6 +1043,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	toggle = *(src_buf + 1);
 	mutex_lock(&mask_info->lock);
@@ -1008,11 +1068,17 @@
 	 */
 	header.cmd_code = DIAG_CMD_EVENT_TOGGLE;
 	header.padding = 0;
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_event_mask_update(i);
+		diag_send_event_mask_update(peripheral);
 		mutex_unlock(&driver->md_session_lock);
 	}
 	memcpy(dest_buf, &header, sizeof(header));
@@ -1045,6 +1111,11 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	if (!diag_apps_responds())
 		return 0;
@@ -1064,6 +1135,11 @@
 	write_len += rsp_header_len;
 
 	log_item = (struct diag_log_mask_t *)mask_info->ptr;
+	if (!log_item->ptr) {
+		pr_err("diag: Invalid input in %s, mask: %pK\n",
+			__func__, log_item);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, log_item++) {
 		if (log_item->equip_id != req->equip_id)
 			continue;
@@ -1149,19 +1225,17 @@
 				 unsigned char *dest_buf, int dest_len,
 				 struct diag_md_session_t *info)
 {
-	int i;
-	int write_len = 0;
+	int i, peripheral, write_len = 0;
 	int status = LOG_STATUS_SUCCESS;
-	int read_len = 0;
-	int payload_len = 0;
+	int read_len = 0, payload_len = 0;
 	int req_header_len = sizeof(struct diag_log_config_req_t);
 	int rsp_header_len = sizeof(struct diag_log_config_set_rsp_t);
 	uint32_t mask_size = 0;
 	struct diag_log_config_req_t *req;
 	struct diag_log_config_set_rsp_t rsp;
 	struct diag_log_mask_t *mask = NULL;
-	unsigned char *temp_buf = NULL;
 	struct diag_mask_info *mask_info = NULL;
+	unsigned char *temp_buf = NULL;
 
 	mask_info = (!info) ? &log_mask : info->log_mask;
 	if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -1171,11 +1245,20 @@
 		       mask_info);
 		return -EINVAL;
 	}
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 
 	req = (struct diag_log_config_req_t *)src_buf;
 	read_len += req_header_len;
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
-
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		return -EINVAL;
+	}
 	if (req->equip_id >= MAX_EQUIP_ID) {
 		pr_err("diag: In %s, Invalid logging mask request, equip_id: %d\n",
 		       __func__, req->equip_id);
@@ -1264,11 +1347,17 @@
 	memcpy(dest_buf + write_len, src_buf + read_len, payload_len);
 	write_len += payload_len;
 
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_log_mask_update(i, req->equip_id);
+		diag_send_log_mask_update(peripheral, req->equip_id);
 		mutex_unlock(&driver->md_session_lock);
 	}
 end:
@@ -1282,8 +1371,7 @@
 	struct diag_mask_info *mask_info = NULL;
 	struct diag_log_mask_t *mask = NULL;
 	struct diag_log_config_rsp_t header;
-	int write_len = 0;
-	int i;
+	int write_len = 0, i, peripheral;
 
 	mask_info = (!info) ? &log_mask : info->log_mask;
 	if (!src_buf || !dest_buf || src_len <= 0 || dest_len <= 0 ||
@@ -1293,9 +1381,17 @@
 		       mask_info);
 		return -EINVAL;
 	}
-
+	if (!mask_info->ptr) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK\n",
+			__func__, mask_info->ptr);
+		return -EINVAL;
+	}
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
-
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		mutex_lock(&mask->lock);
 		memset(mask->ptr, 0, mask->range);
@@ -1317,11 +1413,17 @@
 	header.status = LOG_STATUS_SUCCESS;
 	memcpy(dest_buf, &header, sizeof(struct diag_log_config_rsp_t));
 	write_len += sizeof(struct diag_log_config_rsp_t);
-	for (i = 0; i < NUM_PERIPHERALS; i++) {
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
+		if (i == APPS_DATA)
+			continue;
 		if (!diag_check_update(i))
 			continue;
+		if (i > NUM_PERIPHERALS)
+			peripheral = diag_search_peripheral_by_pd(i);
+		else
+			peripheral = i;
 		mutex_lock(&driver->md_session_lock);
-		diag_send_log_mask_update(i, ALL_EQUIP_ID);
+		diag_send_log_mask_update(peripheral, ALL_EQUIP_ID);
 		mutex_unlock(&driver->md_session_lock);
 	}
 
@@ -1355,8 +1457,7 @@
 
 static int diag_create_msg_mask_table(void)
 {
-	int i;
-	int err = 0;
+	int i, err = 0;
 	struct diag_msg_mask_t *mask = (struct diag_msg_mask_t *)msg_mask.ptr;
 	struct diag_ssid_range_t range;
 
@@ -1377,8 +1478,7 @@
 
 static int diag_create_build_time_mask(void)
 {
-	int i;
-	int err = 0;
+	int i, err = 0;
 	const uint32_t *tbl = NULL;
 	uint32_t tbl_size = 0;
 	struct diag_msg_mask_t *build_mask = NULL;
@@ -1561,7 +1661,7 @@
 
 static void __diag_mask_exit(struct diag_mask_info *mask_info)
 {
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 
 	mutex_lock(&mask_info->lock);
@@ -1574,8 +1674,7 @@
 
 int diag_log_mask_copy(struct diag_mask_info *dest, struct diag_mask_info *src)
 {
-	int i;
-	int err = 0;
+	int i, err = 0;
 	struct diag_log_mask_t *src_mask = NULL;
 	struct diag_log_mask_t *dest_mask = NULL;
 
@@ -1618,11 +1717,17 @@
 	int i;
 	struct diag_log_mask_t *mask = NULL;
 
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 
 	mutex_lock(&mask_info->lock);
 	mask = (struct diag_log_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&mask_info->lock);
+		return;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		kfree(mask->ptr);
 		mask->ptr = NULL;
@@ -1635,8 +1740,7 @@
 
 static int diag_msg_mask_init(void)
 {
-	int err = 0;
-	int i;
+	int err = 0, i;
 
 	err = __diag_mask_init(&msg_mask, MSG_MASK_SIZE, APPS_BUF_SIZE);
 	if (err)
@@ -1657,8 +1761,7 @@
 
 int diag_msg_mask_copy(struct diag_mask_info *dest, struct diag_mask_info *src)
 {
-	int i;
-	int err = 0;
+	int i, err = 0, mask_size = 0;
 	struct diag_msg_mask_t *src_mask = NULL;
 	struct diag_msg_mask_t *dest_mask = NULL;
 	struct diag_ssid_range_t range;
@@ -1682,8 +1785,11 @@
 		err = diag_create_msg_mask_table_entry(dest_mask, &range);
 		if (err)
 			break;
-		memcpy(dest_mask->ptr, src_mask->ptr,
-		       dest_mask->range * sizeof(uint32_t));
+		if (src_mask->range_tools < dest_mask->range)
+			mask_size = src_mask->range_tools * sizeof(uint32_t);
+		else
+			mask_size = dest_mask->range * sizeof(uint32_t);
+		memcpy(dest_mask->ptr, src_mask->ptr, mask_size);
 		src_mask++;
 		dest_mask++;
 	}
@@ -1697,11 +1803,18 @@
 	int i;
 	struct diag_msg_mask_t *mask = NULL;
 
-	if (!mask_info)
+	if (!mask_info || !mask_info->ptr)
 		return;
 	mutex_lock(&mask_info->lock);
 	mutex_lock(&driver->msg_mask_lock);
 	mask = (struct diag_msg_mask_t *)mask_info->ptr;
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		kfree(mask->ptr);
 		mask->ptr = NULL;
@@ -1763,8 +1876,7 @@
 
 static int diag_log_mask_init(void)
 {
-	int err = 0;
-	int i;
+	int err = 0, i;
 
 	err = __diag_mask_init(&log_mask, LOG_MASK_SIZE, APPS_BUF_SIZE);
 	if (err)
@@ -1797,8 +1909,7 @@
 
 static int diag_event_mask_init(void)
 {
-	int err = 0;
-	int i;
+	int err = 0, i;
 
 	err = __diag_mask_init(&event_mask, EVENT_MASK_SIZE, APPS_BUF_SIZE);
 	if (err)
@@ -1851,11 +1962,8 @@
 int diag_copy_to_user_msg_mask(char __user *buf, size_t count,
 			       struct diag_md_session_t *info)
 {
-	int i;
-	int err = 0;
-	int len = 0;
-	int copy_len = 0;
-	int total_len = 0;
+	int i, err = 0, len = 0;
+	int copy_len = 0, total_len = 0;
 	struct diag_msg_mask_userspace_t header;
 	struct diag_mask_info *mask_info = NULL;
 	struct diag_msg_mask_t *mask = NULL;
@@ -1868,6 +1976,11 @@
 	if (!mask_info)
 		return -EIO;
 
+	if (!mask_info->ptr || !mask_info->update_buf) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n",
+			__func__, mask_info->ptr, mask_info->update_buf);
+		return -EINVAL;
+	}
 	mutex_lock(&driver->diag_maskclear_mutex);
 	if (driver->mask_clear) {
 		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
@@ -1880,6 +1993,13 @@
 	mutex_lock(&driver->msg_mask_lock);
 
 	mask = (struct diag_msg_mask_t *)(mask_info->ptr);
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&driver->msg_mask_lock);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < driver->msg_mask_tbl_count; i++, mask++) {
 		ptr = mask_info->update_buf;
 		len = 0;
@@ -1923,11 +2043,8 @@
 int diag_copy_to_user_log_mask(char __user *buf, size_t count,
 			       struct diag_md_session_t *info)
 {
-	int i;
-	int err = 0;
-	int len = 0;
-	int copy_len = 0;
-	int total_len = 0;
+	int i, err = 0, len = 0;
+	int copy_len = 0, total_len = 0;
 	struct diag_log_mask_userspace_t header;
 	struct diag_log_mask_t *mask = NULL;
 	struct diag_mask_info *mask_info = NULL;
@@ -1940,8 +2057,20 @@
 	if (!mask_info)
 		return -EIO;
 
+	if (!mask_info->ptr || !mask_info->update_buf) {
+		pr_err("diag: In %s, invalid input mask_info->ptr: %pK, mask_info->update_buf: %pK\n",
+			__func__, mask_info->ptr, mask_info->update_buf);
+		return -EINVAL;
+	}
+
 	mutex_lock(&mask_info->lock);
 	mask = (struct diag_log_mask_t *)(mask_info->ptr);
+	if (!mask->ptr) {
+		pr_err("diag: Invalid input in %s, mask->ptr: %pK\n",
+			__func__, mask->ptr);
+		mutex_unlock(&mask_info->lock);
+		return -EINVAL;
+	}
 	for (i = 0; i < MAX_EQUIP_ID; i++, mask++) {
 		ptr = mask_info->update_buf;
 		len = 0;
@@ -1982,7 +2111,8 @@
 
 void diag_send_updates_peripheral(uint8_t peripheral)
 {
-	diag_send_feature_mask_update(peripheral);
+	if (!driver->feature[peripheral].sent_feature_mask)
+		diag_send_feature_mask_update(peripheral);
 	/*
 	 * Masks (F3, logs and events) will be sent to
 	 * peripheral immediately following feature mask update only
@@ -2009,8 +2139,7 @@
 int diag_process_apps_masks(unsigned char *buf, int len,
 			    struct diag_md_session_t *info)
 {
-	int size = 0;
-	int sub_cmd = 0;
+	int size = 0, sub_cmd = 0;
 	int (*hdlr)(unsigned char *src_buf, int src_len,
 		    unsigned char *dest_buf, int dest_len,
 		    struct diag_md_session_t *info) = NULL;
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 7e3fe90..6377677 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -152,15 +152,20 @@
 		return -EIO;
 
 	ch = &diag_md[id];
+	if (!ch)
+		return -EINVAL;
 
 	spin_lock_irqsave(&ch->lock, flags);
 	for (i = 0; i < ch->num_tbl_entries && !found; i++) {
 		if (ch->tbl[i].buf != buf)
 			continue;
 		found = 1;
-		pr_err_ratelimited("diag: trying to write the same buffer buf: %pK, ctxt: %d len: %d at i: %d back to the table, proc: %d, mode: %d\n",
-				   buf, ctx, ch->tbl[i].len,
-				   i, id, driver->logging_mode);
+		pr_err_ratelimited("diag: trying to write the same buffer buf: %pK, len: %d, back to the table for p: %d, t: %d, buf_num: %d, proc: %d, i: %d\n",
+				   buf, ch->tbl[i].len, GET_BUF_PERIPHERAL(ctx),
+				   GET_BUF_TYPE(ctx), GET_BUF_NUM(ctx), id, i);
+		ch->tbl[i].buf = NULL;
+		ch->tbl[i].len = 0;
+		ch->tbl[i].ctx = 0;
 	}
 	spin_unlock_irqrestore(&ch->lock, flags);
 
@@ -194,6 +199,7 @@
 
 		found = 1;
 		driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		pr_debug("diag: wake up logging process\n");
 		wake_up_interruptible(&driver->wait_q);
 	}
@@ -218,12 +224,13 @@
 	uint8_t drain_again = 0;
 	int peripheral = 0;
 	struct diag_md_session_t *session_info = NULL;
+	struct pid *pid_struct = NULL;
 
 	for (i = 0; i < NUM_DIAG_MD_DEV && !err; i++) {
 		ch = &diag_md[i];
 		for (j = 0; j < ch->num_tbl_entries && !err; j++) {
 			entry = &ch->tbl[j];
-			if (entry->len <= 0)
+			if (entry->len <= 0 || entry->buf == NULL)
 				continue;
 
 			peripheral = diag_md_get_peripheral(entry->ctx);
@@ -240,6 +247,14 @@
 			if ((info && (info->peripheral_mask &
 			    MD_PERIPHERAL_MASK(peripheral)) == 0))
 				goto drop_data;
+			pid_struct = find_get_pid(session_info->pid);
+			if (!pid_struct) {
+				err = -ESRCH;
+				DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+					"diag: No such md_session_map[%d] with pid = %d err=%d exists..\n",
+					peripheral, session_info->pid, err);
+				goto drop_data;
+			}
 			/*
 			 * If the data is from remote processor, copy the remote
 			 * token first
@@ -259,27 +274,35 @@
 			}
 			if (i > 0) {
 				remote_token = diag_get_remote(i);
-				err = copy_to_user(buf + ret, &remote_token,
-						   sizeof(int));
+				if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+					err = copy_to_user(buf + ret,
+							&remote_token,
+							sizeof(int));
+					if (err)
+						goto drop_data;
+					ret += sizeof(int);
+				}
+			}
+
+			/* Copy the length of data being passed */
+			if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+				err = copy_to_user(buf + ret,
+						(void *)&(entry->len),
+						sizeof(int));
 				if (err)
 					goto drop_data;
 				ret += sizeof(int);
 			}
 
-			/* Copy the length of data being passed */
-			err = copy_to_user(buf + ret, (void *)&(entry->len),
-					   sizeof(int));
-			if (err)
-				goto drop_data;
-			ret += sizeof(int);
-
 			/* Copy the actual data being passed */
-			err = copy_to_user(buf + ret, (void *)entry->buf,
-					   entry->len);
-			if (err)
-				goto drop_data;
-			ret += entry->len;
-
+			if (get_pid_task(pid_struct, PIDTYPE_PID)) {
+				err = copy_to_user(buf + ret,
+						(void *)entry->buf,
+						entry->len);
+				if (err)
+					goto drop_data;
+				ret += entry->len;
+			}
 			/*
 			 * The data is now copied to the user space client,
 			 * Notify that the write is complete and delete its
@@ -301,7 +324,11 @@
 	}
 
 	*pret = ret;
-	err = copy_to_user(buf + sizeof(int), (void *)&num_data, sizeof(int));
+	if (pid_struct && get_pid_task(pid_struct, PIDTYPE_PID)) {
+		err = copy_to_user(buf + sizeof(int),
+				(void *)&num_data,
+				sizeof(int));
+	}
 	diag_ws_on_copy_complete(DIAG_WS_MUX);
 	if (drain_again)
 		chk_logging_wakeup();
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index ac3c1fd..9de40b0 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -21,10 +21,12 @@
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/sched.h>
-#include <linux/wakelock.h>
+#include <linux/device.h>
 #include <linux/atomic.h>
 #include "diagfwd_bridge.h"
 
+#define THRESHOLD_CLIENT_LIMIT	50
+
 /* Size of the USB buffers used for read and write*/
 #define USB_MAX_OUT_BUF 4096
 #define APPS_BUF_SIZE	4096
@@ -33,7 +35,7 @@
 
 #define DIAG_MAX_REQ_SIZE	(16 * 1024)
 #define DIAG_MAX_RSP_SIZE	(16 * 1024)
-#define APF_DIAG_PADDING	256
+#define APF_DIAG_PADDING	0
 /*
  * In the worst case, the HDLC buffer can be atmost twice the size of the
  * original packet. Add 3 bytes for 16 bit CRC (2 bytes) and a delimiter
@@ -69,12 +71,17 @@
 #define DIAG_CON_CDSP		(0x0040)	/* Bit mask for CDSP */
 
 #define DIAG_CON_UPD_WLAN		(0x1000) /*Bit mask for WLAN PD*/
+#define DIAG_CON_UPD_AUDIO		(0x2000) /*Bit mask for AUDIO PD*/
+#define DIAG_CON_UPD_SENSORS	(0x4000) /*Bit mask for SENSORS PD*/
+
 #define DIAG_CON_NONE		(0x0000)	/* Bit mask for No SS*/
 #define DIAG_CON_ALL		(DIAG_CON_APSS | DIAG_CON_MPSS \
 				| DIAG_CON_LPASS | DIAG_CON_WCNSS \
 				| DIAG_CON_SENSORS | DIAG_CON_WDSP \
 				| DIAG_CON_CDSP)
-#define DIAG_CON_UPD_ALL	(DIAG_CON_UPD_WLAN)
+#define DIAG_CON_UPD_ALL	(DIAG_CON_UPD_WLAN \
+				| DIAG_CON_UPD_AUDIO \
+				| DIAG_CON_UPD_SENSORS)
 
 #define DIAG_STM_MODEM	0x01
 #define DIAG_STM_LPASS	0x02
@@ -214,16 +221,23 @@
 #define APPS_DATA		(NUM_PERIPHERALS)
 
 #define UPD_WLAN		7
-#define NUM_UPD			1
-#define MAX_PERIPHERAL_UPD			1
+#define UPD_AUDIO		8
+#define UPD_SENSORS		9
+#define NUM_UPD			3
+
+#define MAX_PERIPHERAL_UPD			2
 /* Number of sessions possible in Memory Device Mode. +1 for Apps data */
 #define NUM_MD_SESSIONS		(NUM_PERIPHERALS \
 					+ NUM_UPD + 1)
 
 #define MD_PERIPHERAL_MASK(x)	(1 << x)
 
-#define MD_PERIPHERAL_PD_MASK(x)					\
-	((x == PERIPHERAL_MODEM) ? (1 << UPD_WLAN) : 0)\
+#define MD_PERIPHERAL_PD_MASK(x, peripheral, pd_mask)	\
+do {						\
+fwd_info = &peripheral_info[x][peripheral];	\
+for (i = 0; i <= fwd_info->num_pd - 2; i++)	\
+	pd_mask |= (1 << fwd_info->upd_diag_id[i].pd);\
+} while (0)
 
 /*
  * Number of stm processors includes all the peripherals and
@@ -306,6 +320,8 @@
 struct diag_id_tbl_t {
 	struct list_head link;
 	uint8_t diag_id;
+	uint8_t pd_val;
+	uint8_t peripheral;
 	char *process_name;
 } __packed;
 struct diag_id_t {
@@ -452,6 +468,10 @@
 	uint32_t peripheral_mask;
 	uint32_t pd_mask;
 	uint8_t mode_param;
+	uint8_t diag_id;
+	uint8_t pd_val;
+	uint8_t reserved;
+	int peripheral;
 } __packed;
 
 struct diag_md_session_t {
@@ -499,6 +519,7 @@
 	uint8_t encode_hdlc;
 	uint8_t untag_header;
 	uint8_t peripheral_buffering;
+	uint8_t pd_buffering;
 	uint8_t mask_centralization;
 	uint8_t stm_support;
 	uint8_t sockets_enabled;
@@ -525,12 +546,14 @@
 	wait_queue_head_t wait_q;
 	struct diag_client_map *client_map;
 	int *data_ready;
+	atomic_t data_ready_notif[THRESHOLD_CLIENT_LIMIT];
 	int num_clients;
 	int polling_reg_flag;
 	int use_device_tree;
 	int supports_separate_cmdrsp;
 	int supports_apps_hdlc_encoding;
 	int supports_apps_header_untagging;
+	int supports_pd_buffering;
 	int peripheral_untag[NUM_PERIPHERALS];
 	int supports_sockets;
 	/* The state requested in the STM command */
@@ -584,8 +607,8 @@
 	struct diagfwd_info *diagfwd_cmd[NUM_PERIPHERALS];
 	struct diagfwd_info *diagfwd_dci_cmd[NUM_PERIPHERALS];
 	struct diag_feature_t feature[NUM_PERIPHERALS];
-	struct diag_buffering_mode_t buffering_mode[NUM_PERIPHERALS];
-	uint8_t buffering_flag[NUM_PERIPHERALS];
+	struct diag_buffering_mode_t buffering_mode[NUM_MD_SESSIONS];
+	uint8_t buffering_flag[NUM_MD_SESSIONS];
 	struct mutex mode_lock;
 	unsigned char *user_space_data_buf;
 	uint8_t user_space_data_busy;
@@ -693,7 +716,10 @@
 int diag_mask_param(void);
 void diag_clear_masks(struct diag_md_session_t *info);
 uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask);
-
+int diag_query_pd(char *process_name);
+int diag_search_peripheral_by_pd(uint8_t pd_val);
+uint8_t diag_search_diagid_by_pd(uint8_t pd_val,
+	uint8_t *diag_id, int *peripheral);
 void diag_record_stats(int type, int flag);
 
 struct diag_md_session_t *diag_md_session_get_pid(int pid);
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 18f941d..919ea0f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -136,7 +136,6 @@
 
 /* This is the max number of user-space clients supported at initialization*/
 static unsigned int max_clients = 15;
-static unsigned int threshold_client_limit = 50;
 module_param(max_clients, uint, 0000);
 
 /* Timer variables */
@@ -324,7 +323,7 @@
 		if (i < driver->num_clients) {
 			diag_add_client(i, file);
 		} else {
-			if (i < threshold_client_limit) {
+			if (i < THRESHOLD_CLIENT_LIMIT) {
 				driver->num_clients++;
 				temp = krealloc(driver->client_map
 					, (driver->num_clients) * sizeof(struct
@@ -354,11 +353,17 @@
 			}
 		}
 		driver->data_ready[i] = 0x0;
+		atomic_set(&driver->data_ready_notif[i], 0);
 		driver->data_ready[i] |= MSG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= EVENT_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= LOG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= DCI_LOG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= DCI_EVENT_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 
 		if (driver->ref_count == 0)
 			diag_mempool_init();
@@ -395,24 +400,22 @@
 		ret |= DIAG_CON_CDSP;
 	if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_WLAN))
 		ret |= DIAG_CON_UPD_WLAN;
+	if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_AUDIO))
+		ret |= DIAG_CON_UPD_AUDIO;
+	if (peripheral_mask & MD_PERIPHERAL_MASK(UPD_SENSORS))
+		ret |= DIAG_CON_UPD_SENSORS;
 	return ret;
 }
 
 uint8_t diag_mask_to_pd_value(uint32_t peripheral_mask)
 {
 	uint8_t upd = 0;
-	uint32_t pd_mask = 0;
 
-	pd_mask = diag_translate_kernel_to_user_mask(peripheral_mask);
-	switch (pd_mask) {
-	case DIAG_CON_UPD_WLAN:
-		upd = UPD_WLAN;
-		break;
-	default:
-		DIAG_LOG(DIAG_DEBUG_MASKS,
-		"asking for mask update with no pd mask set\n");
+	for (upd = UPD_WLAN; upd < NUM_MD_SESSIONS; upd++) {
+		if (peripheral_mask & (1 << upd))
+			return upd;
 	}
-	return upd;
+	return 0;
 }
 
 int diag_mask_param(void)
@@ -1304,11 +1307,9 @@
 		mutex_unlock(&driver->md_session_lock);
 		return -ENOMEM;
 	}
-
 	new_session->peripheral_mask = 0;
 	new_session->pid = current->tgid;
 	new_session->task = current;
-
 	new_session->log_mask = kzalloc(sizeof(struct diag_mask_info),
 					GFP_KERNEL);
 	if (!new_session->log_mask) {
@@ -1426,7 +1427,6 @@
 struct diag_md_session_t *diag_md_session_get_pid(int pid)
 {
 	int i;
-
 	for (i = 0; i < NUM_MD_SESSIONS; i++) {
 		if (driver->md_session_map[i] &&
 		    driver->md_session_map[i]->pid == pid)
@@ -1542,7 +1542,10 @@
 		 * If this session owns all the requested peripherals, then
 		 * call function to switch the modes/masks for the md_session
 		 */
+		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
+
 		if (!session_info) {
 			*change_mode = 1;
 			return 0;
@@ -1571,7 +1574,9 @@
 		 * owned by this md session
 		 */
 		change_mask = driver->md_session_mask & param->peripheral_mask;
+		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
 
 		if (session_info) {
 			if ((session_info->peripheral_mask & change_mask)
@@ -1617,18 +1622,19 @@
 		ret |= (1 << PERIPHERAL_CDSP);
 	if (peripheral_mask & DIAG_CON_UPD_WLAN)
 		ret |= (1 << UPD_WLAN);
-
+	if (peripheral_mask & DIAG_CON_UPD_AUDIO)
+		ret |= (1 << UPD_AUDIO);
+	if (peripheral_mask & DIAG_CON_UPD_SENSORS)
+		ret |= (1 << UPD_SENSORS);
 	return ret;
 }
 
 static int diag_switch_logging(struct diag_logging_mode_param_t *param)
 {
 	int new_mode, i = 0;
-	int curr_mode;
-	int err = 0;
-	uint8_t do_switch = 1;
-	uint32_t peripheral_mask = 0;
-	uint8_t peripheral, upd;
+	int curr_mode, err = 0;
+	uint8_t do_switch = 1, peripheral = 0;
+	uint32_t peripheral_mask = 0, pd_mask = 0;
 
 	if (!param)
 		return -EINVAL;
@@ -1640,30 +1646,42 @@
 	}
 
 	if (param->pd_mask) {
-		switch (param->pd_mask) {
-		case DIAG_CON_UPD_WLAN:
-			peripheral = PERIPHERAL_MODEM;
-			upd = UPD_WLAN;
-			break;
-		default:
+		pd_mask = diag_translate_mask(param->pd_mask);
+		for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
+			if (pd_mask & (1 << i)) {
+				if (diag_search_diagid_by_pd(i, &param->diag_id,
+					&param->peripheral)) {
+					param->pd_val = i;
+					break;
+				}
+			}
+		}
+		if (!param->diag_id) {
 			DIAG_LOG(DIAG_DEBUG_USERSPACE,
-			"asking for mode switch with no pd mask set\n");
+			"diag_id support is not present for the pd mask = %d\n",
+			param->pd_mask);
 			return -EINVAL;
 		}
 
+		DIAG_LOG(DIAG_DEBUG_USERSPACE,
+			"diag: pd_mask = %d, diag_id = %d, peripheral = %d, pd_val = %d\n",
+			param->pd_mask, param->diag_id,
+			param->peripheral, param->pd_val);
+
+		peripheral = param->peripheral;
 		if (driver->md_session_map[peripheral] &&
 			(MD_PERIPHERAL_MASK(peripheral) &
 			diag_mux->mux_mask)) {
 			DIAG_LOG(DIAG_DEBUG_USERSPACE,
 			"diag_fr: User PD is already logging onto active peripheral logging\n");
-			i = upd - UPD_WLAN;
+			i = param->pd_val - UPD_WLAN;
 			driver->pd_session_clear[i] = 0;
 			return -EINVAL;
 		}
 		peripheral_mask =
 			diag_translate_mask(param->pd_mask);
 		param->peripheral_mask = peripheral_mask;
-		i = upd - UPD_WLAN;
+		i = param->pd_val - UPD_WLAN;
 		if (!driver->pd_session_clear[i]) {
 			driver->pd_logging_mode[i] = 1;
 			driver->num_pd_session += 1;
@@ -1832,6 +1850,7 @@
 	}
 
 	driver->data_ready[i] |= DEINIT_TYPE;
+	atomic_inc(&driver->data_ready_notif[i]);
 	mutex_unlock(&driver->diagchar_mutex);
 	wake_up_interruptible(&driver->wait_q);
 
@@ -1941,12 +1960,33 @@
 static int diag_ioctl_set_buffering_mode(unsigned long ioarg)
 {
 	struct diag_buffering_mode_t params;
+	int peripheral = 0;
+	uint8_t diag_id = 0;
 
 	if (copy_from_user(&params, (void __user *)ioarg, sizeof(params)))
 		return -EFAULT;
 
-	if (params.peripheral >= NUM_PERIPHERALS)
-		return -EINVAL;
+	diag_map_pd_to_diagid(params.peripheral, &diag_id, &peripheral);
+
+	if ((peripheral < 0) ||
+		peripheral >= NUM_PERIPHERALS) {
+		pr_err("diag: In %s, invalid peripheral = %d\n", __func__,
+		       peripheral);
+		return -EIO;
+	}
+
+	if (params.peripheral > NUM_PERIPHERALS &&
+		!driver->feature[peripheral].pd_buffering) {
+		pr_err("diag: In %s, pd buffering not supported for peripheral:%d\n",
+			__func__, peripheral);
+		return -EIO;
+	}
+
+	if (!driver->feature[peripheral].peripheral_buffering) {
+		pr_err("diag: In %s, peripheral %d doesn't support buffering\n",
+		       __func__, peripheral);
+		return -EIO;
+	}
 
 	mutex_lock(&driver->mode_lock);
 	driver->buffering_flag[params.peripheral] = 1;
@@ -1957,24 +1997,29 @@
 
 static int diag_ioctl_peripheral_drain_immediate(unsigned long ioarg)
 {
-	uint8_t peripheral;
+	uint8_t pd, diag_id = 0;
+	int peripheral = 0;
 
-	if (copy_from_user(&peripheral, (void __user *)ioarg, sizeof(uint8_t)))
+	if (copy_from_user(&pd, (void __user *)ioarg, sizeof(uint8_t)))
 		return -EFAULT;
 
-	if (peripheral >= NUM_PERIPHERALS) {
+	diag_map_pd_to_diagid(pd, &diag_id, &peripheral);
+
+	if ((peripheral < 0) ||
+		peripheral >= NUM_PERIPHERALS) {
 		pr_err("diag: In %s, invalid peripheral %d\n", __func__,
 		       peripheral);
 		return -EINVAL;
 	}
 
-	if (!driver->feature[peripheral].peripheral_buffering) {
-		pr_err("diag: In %s, peripheral %d doesn't support buffering\n",
-		       __func__, peripheral);
+	if (pd > NUM_PERIPHERALS &&
+		!driver->feature[peripheral].pd_buffering) {
+		pr_err("diag: In %s, pd buffering not supported for peripheral:%d\n",
+			__func__, peripheral);
 		return -EIO;
 	}
 
-	return diag_send_peripheral_drain_immediate(peripheral);
+	return diag_send_peripheral_drain_immediate(pd, diag_id, peripheral);
 }
 
 static int diag_ioctl_dci_support(unsigned long ioarg)
@@ -1999,8 +2044,9 @@
 {
 	uint8_t hdlc_support;
 	struct diag_md_session_t *session_info = NULL;
-
+	mutex_lock(&driver->md_session_lock);
 	session_info = diag_md_session_get_pid(current->tgid);
+	mutex_unlock(&driver->md_session_lock);
 	if (copy_from_user(&hdlc_support, (void __user *)ioarg,
 				sizeof(uint8_t)))
 		return -EFAULT;
@@ -2017,11 +2063,126 @@
 	return 0;
 }
 
+/*
+ * diag_search_peripheral_by_pd(uint8_t pd_val)
+ *
+ * Function will return peripheral by searching pd in the
+ * diag_id table.
+ *
+ */
+
+int diag_search_peripheral_by_pd(uint8_t pd_val)
+{
+	struct list_head *start;
+	struct list_head *temp;
+	struct diag_id_tbl_t *item = NULL;
+
+	mutex_lock(&driver->diag_id_mutex);
+	list_for_each_safe(start, temp, &driver->diag_id_list) {
+		item = list_entry(start, struct diag_id_tbl_t, link);
+		if (pd_val == item->pd_val) {
+			mutex_unlock(&driver->diag_id_mutex);
+			return item->peripheral;
+		}
+	}
+	mutex_unlock(&driver->diag_id_mutex);
+	return -EINVAL;
+}
+
+/*
+ * diag_search_diagid_by_pd(uint8_t pd_val,
+ *	uint8_t *diag_id, int *peripheral)
+ *
+ * Function will update the peripheral and diag_id
+ * from the pd passed as an argument.
+ *
+ */
+
+uint8_t diag_search_diagid_by_pd(uint8_t pd_val,
+	uint8_t *diag_id, int *peripheral)
+{
+	struct list_head *start;
+	struct list_head *temp;
+	struct diag_id_tbl_t *item = NULL;
+
+	mutex_lock(&driver->diag_id_mutex);
+	list_for_each_safe(start, temp, &driver->diag_id_list) {
+		item = list_entry(start, struct diag_id_tbl_t, link);
+		if (pd_val == item->pd_val) {
+			*peripheral = item->peripheral;
+			*diag_id = item->diag_id;
+			mutex_unlock(&driver->diag_id_mutex);
+			return 1;
+		}
+	}
+	mutex_unlock(&driver->diag_id_mutex);
+	return 0;
+}
+
+/*
+ * diag_query_pd_name(char *process_name, char *search_str)
+ *
+ * The function searches the pd string in the control packet string
+ * from the peripheral
+ *
+ */
+
+static int diag_query_pd_name(char *process_name, char *search_str)
+{
+	if (!process_name)
+		return -EINVAL;
+
+	if (strnstr(process_name, search_str, strlen(process_name)))
+		return 1;
+
+	return 0;
+}
+
+/*
+ * diag_query_pd_name(char *process_name, char *search_str)
+ *
+ * The function returns the PD information based on the presence of
+ * the pd specific string in the control packet's string from peripheral.
+ *
+ */
+
+int diag_query_pd(char *process_name)
+{
+	if (!process_name)
+		return -EINVAL;
+
+	if (diag_query_pd_name(process_name, "modem/root_pd"))
+		return PERIPHERAL_MODEM;
+	if (diag_query_pd_name(process_name, "adsp/root_pd"))
+		return PERIPHERAL_LPASS;
+	if (diag_query_pd_name(process_name, "slpi/root_pd"))
+		return PERIPHERAL_SENSORS;
+	if (diag_query_pd_name(process_name, "cdsp/root_pd"))
+		return PERIPHERAL_CDSP;
+	if (diag_query_pd_name(process_name, "wlan_pd"))
+		return UPD_WLAN;
+	if (diag_query_pd_name(process_name, "audio_pd"))
+		return UPD_AUDIO;
+	if (diag_query_pd_name(process_name, "sensor_pd"))
+		return UPD_SENSORS;
+
+	return -EINVAL;
+}
+
+/*
+ * diag_ioctl_query_pd_logging(struct diag_logging_mode_param_t *param)
+ *
+ * IOCTL handler based on the parameter received will check on which peripheral
+ * the PD is present and validate if the peripheral supports the diag_id and
+ * tagging feature.
+ *
+ */
+
 static int diag_ioctl_query_pd_logging(struct diag_logging_mode_param_t *param)
 {
-	int ret = -EINVAL;
-	int peripheral;
-	char *p_str = NULL;
+	int ret = -EINVAL, i = 0;
+	int peripheral = -EINVAL;
+	uint32_t pd_mask = 0;
 
 	if (!param)
 		return -EINVAL;
@@ -2032,17 +2193,21 @@
 		return -EINVAL;
 	}
 
-	switch (param->pd_mask) {
-	case DIAG_CON_UPD_WLAN:
-		peripheral = PERIPHERAL_MODEM;
-		p_str = "MODEM";
-		break;
-	default:
-		DIAG_LOG(DIAG_DEBUG_USERSPACE,
-		"Invalid pd mask, returning EINVAL\n");
-		return -EINVAL;
+	if (param->pd_mask) {
+		pd_mask = diag_translate_mask(param->pd_mask);
+		for (i = UPD_WLAN; i < NUM_MD_SESSIONS; i++) {
+			if (pd_mask & (1 << i)) {
+				peripheral = diag_search_peripheral_by_pd(i);
+				break;
+			}
+		}
+		if (peripheral < 0) {
+			DIAG_LOG(DIAG_DEBUG_USERSPACE,
+			"diag_id support is not present for the pd mask = %d\n",
+			param->pd_mask);
+			return -EINVAL;
+		}
 	}
-
 	mutex_lock(&driver->diag_cntl_mutex);
 	DIAG_LOG(DIAG_DEBUG_USERSPACE,
 	"diag: %s: Untagging support on APPS is %s\n", __func__,
@@ -2050,8 +2215,8 @@
 	"present" : "absent"));
 
 	DIAG_LOG(DIAG_DEBUG_USERSPACE,
-	"diag: %s: Tagging support on %s is %s\n",
-	__func__, p_str,
+	"diag: %s: Tagging support on peripheral = %d is %s\n",
+	__func__, peripheral,
 	(driver->feature[peripheral].untag_header ?
 	"present" : "absent"));
 
@@ -2368,7 +2533,9 @@
 		mutex_unlock(&driver->dci_mutex);
 		break;
 	case DIAG_IOCTL_DCI_EVENT_STATUS:
+		mutex_lock(&driver->dci_mutex);
 		result = diag_ioctl_dci_event_status(ioarg);
+		mutex_unlock(&driver->dci_mutex);
 		break;
 	case DIAG_IOCTL_DCI_CLEAR_LOGS:
 		mutex_lock(&driver->dci_mutex);
@@ -2766,10 +2933,13 @@
 	} else {
 		wait_event_interruptible(driver->wait_q,
 					 (driver->in_busy_pktdata == 0));
+		mutex_lock(&driver->md_session_lock);
 		info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
 		ret = diag_process_apps_pkt(user_space_data, len, info);
 		if (ret == 1)
-			diag_send_error_rsp((void *)(user_space_data), len);
+			diag_send_error_rsp((void *)(user_space_data), len,
+						info);
 	}
 fail:
 	diagmem_free(driver, user_space_data, mempool);
@@ -2833,7 +3003,9 @@
 
 	/* send masks to local processor now */
 	if (!remote_proc) {
+		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
 		if (!session_info) {
 			pr_err("diag:In %s request came from invalid md session pid:%d",
 				__func__, current->tgid);
@@ -2956,16 +3128,6 @@
 	return 0;
 }
 
-static int check_data_ready(int index)
-{
-	int data_type = 0;
-
-	mutex_lock(&driver->diagchar_mutex);
-	data_type = driver->data_ready[index];
-	mutex_unlock(&driver->diagchar_mutex);
-	return data_type;
-}
-
 static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
 			  loff_t *ppos)
 {
@@ -2992,7 +3154,8 @@
 		pr_err("diag: bad address from user side\n");
 		return -EFAULT;
 	}
-	wait_event_interruptible(driver->wait_q, (check_data_ready(index)) > 0);
+	wait_event_interruptible(driver->wait_q,
+			atomic_read(&driver->data_ready_notif[index]) > 0);
 
 	mutex_lock(&driver->diagchar_mutex);
 
@@ -3003,12 +3166,15 @@
 		/*Copy the type of data being passed*/
 		data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
 		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int));
 		if (ret == -EFAULT)
 			goto exit;
 		/* place holder for number of data field */
 		ret += sizeof(int);
+		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
 		exit_stat = diag_md_copy_to_user(buf, &ret, count,
 						 session_info);
 		goto exit;
@@ -3017,16 +3183,20 @@
 		 * memory device any more, the condition needs to be cleared.
 		 */
 		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 	}
 
 	if (driver->data_ready[index] & HDLC_SUPPORT_TYPE) {
 		data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE;
 		driver->data_ready[index] ^= HDLC_SUPPORT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		COPY_USER_SPACE_OR_ERR(buf, data_type, sizeof(int));
 		if (ret == -EFAULT)
 			goto exit;
 
+		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
+		mutex_unlock(&driver->md_session_lock);
 		if (session_info) {
 			COPY_USER_SPACE_OR_ERR(buf+4,
 					session_info->hdlc_disabled,
@@ -3044,6 +3214,7 @@
 		if (ret == -EFAULT)
 			goto exit;
 		driver->data_ready[index] ^= DEINIT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		mutex_unlock(&driver->diagchar_mutex);
 		diag_remove_client_entry(file);
 		return ret;
@@ -3061,6 +3232,7 @@
 		if (write_len > 0)
 			ret += write_len;
 		driver->data_ready[index] ^= MSG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3087,6 +3259,7 @@
 				goto exit;
 		}
 		driver->data_ready[index] ^= EVENT_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3103,6 +3276,7 @@
 		if (write_len > 0)
 			ret += write_len;
 		driver->data_ready[index] ^= LOG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3119,6 +3293,7 @@
 		if (ret == -EFAULT)
 			goto exit;
 		driver->data_ready[index] ^= PKT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		driver->in_busy_pktdata = 0;
 		goto exit;
 	}
@@ -3136,6 +3311,7 @@
 			goto exit;
 
 		driver->data_ready[index] ^= DCI_PKT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		driver->in_busy_dcipktdata = 0;
 		goto exit;
 	}
@@ -3157,6 +3333,7 @@
 			goto exit;
 
 		driver->data_ready[index] ^= DCI_EVENT_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3176,6 +3353,7 @@
 		if (ret == -EFAULT)
 			goto exit;
 		driver->data_ready[index] ^= DCI_LOG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3207,6 +3385,7 @@
 			exit_stat = diag_copy_dci(buf, count, entry, &ret);
 			mutex_lock(&driver->diagchar_mutex);
 			driver->data_ready[index] ^= DCI_DATA_TYPE;
+			atomic_dec(&driver->data_ready_notif[index]);
 			mutex_unlock(&driver->diagchar_mutex);
 			if (exit_stat == 1) {
 				mutex_unlock(&driver->dci_mutex);
@@ -3728,7 +3907,7 @@
 		goto fail;
 	mutex_init(&driver->diag_id_mutex);
 	INIT_LIST_HEAD(&driver->diag_id_list);
-	diag_add_diag_id_to_list(DIAG_ID_APPS, "APPS");
+	diag_add_diag_id_to_list(DIAG_ID_APPS, "APPS", APPS_DATA, APPS_DATA);
 	pr_debug("diagchar initialized now");
 	ret = diagfwd_bridge_init();
 	if (ret)
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index b59f245..4195b40 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -226,6 +226,7 @@
 			 * situation.
 			 */
 			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+			atomic_inc(&driver->data_ready_notif[i]);
 			pr_debug("diag: Force wakeup of logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 			break;
@@ -240,10 +241,11 @@
 	}
 }
 
-static void pack_rsp_and_send(unsigned char *buf, int len)
+static void pack_rsp_and_send(unsigned char *buf, int len,
+				struct diag_md_session_t *info)
 {
 	int err;
-	int retry_count = 0;
+	int retry_count = 0, i, rsp_ctxt;
 	uint32_t write_len = 0;
 	unsigned long flags;
 	unsigned char *rsp_ptr = driver->encoded_rsp_buf;
@@ -259,6 +261,26 @@
 	}
 
 	/*
+	 * Explicitly check for the Peripheral Modem here
+	 * is necessary till a way to identify a peripheral
+	 * if its supporting qshrink4 feature.
+	 */
+	if (info && info->peripheral_mask) {
+		if (info->peripheral_mask == DIAG_CON_ALL ||
+			(info->peripheral_mask & (1 << APPS_DATA)) ||
+			(info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
+			rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
+		} else {
+			for (i = 0; i < NUM_MD_SESSIONS; i++) {
+				if (info->peripheral_mask & (1 << i))
+					break;
+			}
+		rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+		}
+	} else
+		rsp_ctxt = driver->rsp_buf_ctxt;
+
+	/*
 	 * Keep trying till we get the buffer back. It should probably
 	 * take one or two iterations. When this loops till UINT_MAX, it
 	 * means we did not get a write complete for the previous
@@ -299,8 +321,7 @@
 	*(uint8_t *)(rsp_ptr + write_len) = CONTROL_CHAR;
 	write_len += sizeof(uint8_t);
 
-	err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, write_len,
-			     driver->rsp_buf_ctxt);
+	err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, write_len, rsp_ctxt);
 	if (err) {
 		pr_err("diag: In %s, unable to write to mux, err: %d\n",
 		       __func__, err);
@@ -310,12 +331,13 @@
 	}
 }
 
-static void encode_rsp_and_send(unsigned char *buf, int len)
+static void encode_rsp_and_send(unsigned char *buf, int len,
+				struct diag_md_session_t *info)
 {
 	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
 	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
 	unsigned char *rsp_ptr = driver->encoded_rsp_buf;
-	int err, retry_count = 0;
+	int err, i, rsp_ctxt, retry_count = 0;
 	unsigned long flags;
 
 	if (!rsp_ptr || !buf)
@@ -328,6 +350,26 @@
 	}
 
 	/*
+	 * Explicitly check for the Peripheral Modem here
+	 * is necessary till a way to identify a peripheral
+	 * if its supporting qshrink4 feature.
+	 */
+	if (info && info->peripheral_mask) {
+		if (info->peripheral_mask == DIAG_CON_ALL ||
+			(info->peripheral_mask & (1 << APPS_DATA)) ||
+			(info->peripheral_mask & (1 << PERIPHERAL_MODEM))) {
+			rsp_ctxt = SET_BUF_CTXT(APPS_DATA, TYPE_CMD, 1);
+		} else {
+			for (i = 0; i < NUM_MD_SESSIONS; i++) {
+				if (info->peripheral_mask & (1 << i))
+					break;
+			}
+		rsp_ctxt = SET_BUF_CTXT(i, TYPE_CMD, 1);
+		}
+	} else
+		rsp_ctxt = driver->rsp_buf_ctxt;
+
+	/*
 	 * Keep trying till we get the buffer back. It should probably
 	 * take one or two iterations. When this loops till UINT_MAX, it
 	 * means we did not get a write complete for the previous
@@ -370,7 +412,7 @@
 	diag_hdlc_encode(&send, &enc);
 	driver->encoded_rsp_len = (int)(enc.dest - (void *)rsp_ptr);
 	err = diag_mux_write(DIAG_LOCAL_PROC, rsp_ptr, driver->encoded_rsp_len,
-			     driver->rsp_buf_ctxt);
+			     rsp_ctxt);
 	if (err) {
 		pr_err("diag: In %s, Unable to write to device, err: %d\n",
 			__func__, err);
@@ -381,21 +423,23 @@
 	memset(buf, '\0', DIAG_MAX_RSP_SIZE);
 }
 
-void diag_send_rsp(unsigned char *buf, int len)
+static void diag_send_rsp(unsigned char *buf, int len,
+	struct diag_md_session_t *info)
 {
 	struct diag_md_session_t *session_info = NULL;
 	uint8_t hdlc_disabled;
 
-	session_info = diag_md_session_get_peripheral(APPS_DATA);
+	session_info = (info) ? info :
+				diag_md_session_get_peripheral(APPS_DATA);
 	if (session_info)
 		hdlc_disabled = session_info->hdlc_disabled;
 	else
 		hdlc_disabled = driver->hdlc_disabled;
 
 	if (hdlc_disabled)
-		pack_rsp_and_send(buf, len);
+		pack_rsp_and_send(buf, len, session_info);
 	else
-		encode_rsp_and_send(buf, len);
+		encode_rsp_and_send(buf, len, session_info);
 }
 
 void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type)
@@ -448,8 +492,10 @@
 
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
-		if (driver->client_map[i].pid != 0)
+		if (driver->client_map[i].pid != 0) {
 			driver->data_ready[i] |= type;
+			atomic_inc(&driver->data_ready_notif[i]);
+		}
 	wake_up_interruptible(&driver->wait_q);
 	mutex_unlock(&driver->diagchar_mutex);
 }
@@ -466,6 +512,8 @@
 					driver->client_map[j].pid ==
 					driver->md_session_map[i]->pid) {
 					driver->data_ready[j] |= type;
+					atomic_inc(
+						&driver->data_ready_notif[j]);
 					break;
 				}
 			}
@@ -481,6 +529,7 @@
 	for (i = 0; i < driver->num_clients; i++)
 		if (driver->client_map[i].pid == process_id) {
 			driver->data_ready[i] |= data_type;
+			atomic_inc(&driver->data_ready_notif[i]);
 			break;
 		}
 	wake_up_interruptible(&driver->wait_q);
@@ -926,7 +975,8 @@
 	return write_len;
 }
 
-void diag_send_error_rsp(unsigned char *buf, int len)
+void diag_send_error_rsp(unsigned char *buf, int len,
+			struct diag_md_session_t *info)
 {
 	/* -1 to accommodate the first byte 0x13 */
 	if (len > (DIAG_MAX_RSP_SIZE - 1)) {
@@ -936,7 +986,7 @@
 
 	*(uint8_t *)driver->apps_rsp_buf = DIAG_CMD_ERROR;
 	memcpy((driver->apps_rsp_buf + sizeof(uint8_t)), buf, len);
-	diag_send_rsp(driver->apps_rsp_buf, len + 1);
+	diag_send_rsp(driver->apps_rsp_buf, len + 1, info);
 }
 
 int diag_process_apps_pkt(unsigned char *buf, int len,
@@ -949,6 +999,8 @@
 	struct diag_cmd_reg_entry_t entry;
 	struct diag_cmd_reg_entry_t *temp_entry = NULL;
 	struct diag_cmd_reg_t *reg_item = NULL;
+	struct diagfwd_info *fwd_info = NULL;
+	uint32_t pd_mask = 0;
 
 	if (!buf)
 		return -EIO;
@@ -956,7 +1008,7 @@
 	/* Check if the command is a supported mask command */
 	mask_ret = diag_process_apps_masks(buf, len, info);
 	if (mask_ret > 0) {
-		diag_send_rsp(driver->apps_rsp_buf, mask_ret);
+		diag_send_rsp(driver->apps_rsp_buf, mask_ret, info);
 		return 0;
 	}
 
@@ -978,7 +1030,7 @@
 						   driver->apps_rsp_buf,
 						   DIAG_MAX_RSP_SIZE);
 		if (write_len > 0)
-			diag_send_rsp(driver->apps_rsp_buf, write_len);
+			diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		return 0;
 	}
 
@@ -986,15 +1038,18 @@
 	temp_entry = diag_cmd_search(&entry, ALL_PROC);
 	if (temp_entry) {
 		reg_item = container_of(temp_entry, struct diag_cmd_reg_t,
-								entry);
+					entry);
 		if (info) {
-			if (MD_PERIPHERAL_MASK(reg_item->proc) &
-				info->peripheral_mask)
+			MD_PERIPHERAL_PD_MASK(TYPE_CMD, reg_item->proc,
+				pd_mask);
+			if ((MD_PERIPHERAL_MASK(reg_item->proc) &
+				info->peripheral_mask) ||
+				(pd_mask & info->peripheral_mask))
 				write_len = diag_send_data(reg_item, buf, len);
 		} else {
 			if (MD_PERIPHERAL_MASK(reg_item->proc) &
 				driver->logging_mask)
-				diag_send_error_rsp(buf, len);
+				diag_send_error_rsp(buf, len, info);
 			else
 				write_len = diag_send_data(reg_item, buf, len);
 		}
@@ -1010,13 +1065,13 @@
 		for (i = 0; i < 4; i++)
 			*(driver->apps_rsp_buf+i) = *(buf+i);
 		*(uint32_t *)(driver->apps_rsp_buf+4) = DIAG_MAX_REQ_SIZE;
-		diag_send_rsp(driver->apps_rsp_buf, 8);
+		diag_send_rsp(driver->apps_rsp_buf, 8, info);
 		return 0;
 	} else if ((*buf == 0x4b) && (*(buf+1) == 0x12) &&
 		(*(uint16_t *)(buf+2) == DIAG_DIAG_STM)) {
 		len = diag_process_stm_cmd(buf, driver->apps_rsp_buf);
 		if (len > 0) {
-			diag_send_rsp(driver->apps_rsp_buf, len);
+			diag_send_rsp(driver->apps_rsp_buf, len, info);
 			return 0;
 		}
 		return len;
@@ -1029,7 +1084,7 @@
 							driver->apps_rsp_buf,
 							DIAG_MAX_RSP_SIZE);
 		if (write_len > 0)
-			diag_send_rsp(driver->apps_rsp_buf, write_len);
+			diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		return 0;
 	}
 	/* Check for time sync switch command */
@@ -1040,7 +1095,7 @@
 							driver->apps_rsp_buf,
 							DIAG_MAX_RSP_SIZE);
 		if (write_len > 0)
-			diag_send_rsp(driver->apps_rsp_buf, write_len);
+			diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		return 0;
 	}
 	/* Check for diag id command */
@@ -1051,14 +1106,14 @@
 							driver->apps_rsp_buf,
 							DIAG_MAX_RSP_SIZE);
 		if (write_len > 0)
-			diag_send_rsp(driver->apps_rsp_buf, write_len);
+			diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		return 0;
 	}
 	/* Check for download command */
 	else if ((chk_apps_master()) && (*buf == 0x3A)) {
 		/* send response back */
 		driver->apps_rsp_buf[0] = *buf;
-		diag_send_rsp(driver->apps_rsp_buf, 1);
+		diag_send_rsp(driver->apps_rsp_buf, 1, info);
 		msleep(5000);
 		/* call download API */
 		msm_set_restart_mode(RESTART_DLOAD);
@@ -1078,7 +1133,7 @@
 			for (i = 0; i < 13; i++)
 				driver->apps_rsp_buf[i+3] = 0;
 
-			diag_send_rsp(driver->apps_rsp_buf, 16);
+			diag_send_rsp(driver->apps_rsp_buf, 16, info);
 			return 0;
 		}
 	}
@@ -1087,7 +1142,7 @@
 		(*(buf+2) == 0x04) && (*(buf+3) == 0x0)) {
 		memcpy(driver->apps_rsp_buf, buf, 4);
 		driver->apps_rsp_buf[4] = wrap_enabled;
-		diag_send_rsp(driver->apps_rsp_buf, 5);
+		diag_send_rsp(driver->apps_rsp_buf, 5, info);
 		return 0;
 	}
 	/* Wrap the Delayed Rsp ID */
@@ -1096,7 +1151,7 @@
 		wrap_enabled = true;
 		memcpy(driver->apps_rsp_buf, buf, 4);
 		driver->apps_rsp_buf[4] = wrap_count;
-		diag_send_rsp(driver->apps_rsp_buf, 6);
+		diag_send_rsp(driver->apps_rsp_buf, 6, info);
 		return 0;
 	}
 	/* Mobile ID Rsp */
@@ -1107,7 +1162,7 @@
 						   driver->apps_rsp_buf,
 						   DIAG_MAX_RSP_SIZE);
 		if (write_len > 0) {
-			diag_send_rsp(driver->apps_rsp_buf, write_len);
+			diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 			return 0;
 		}
 	}
@@ -1127,7 +1182,7 @@
 			for (i = 0; i < 55; i++)
 				driver->apps_rsp_buf[i] = 0;
 
-			diag_send_rsp(driver->apps_rsp_buf, 55);
+			diag_send_rsp(driver->apps_rsp_buf, 55, info);
 			return 0;
 		}
 		/* respond to 0x7c command */
@@ -1140,14 +1195,14 @@
 							 chk_config_get_id();
 			*(unsigned char *)(driver->apps_rsp_buf + 12) = '\0';
 			*(unsigned char *)(driver->apps_rsp_buf + 13) = '\0';
-			diag_send_rsp(driver->apps_rsp_buf, 14);
+			diag_send_rsp(driver->apps_rsp_buf, 14, info);
 			return 0;
 		}
 	}
 	write_len = diag_cmd_chk_stats(buf, len, driver->apps_rsp_buf,
 				       DIAG_MAX_RSP_SIZE);
 	if (write_len > 0) {
-		diag_send_rsp(driver->apps_rsp_buf, write_len);
+		diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		return 0;
 	}
 	write_len = diag_cmd_disable_hdlc(buf, len, driver->apps_rsp_buf,
@@ -1159,7 +1214,7 @@
 		 * before disabling HDLC encoding on Apps processor.
 		 */
 		mutex_lock(&driver->hdlc_disable_mutex);
-		diag_send_rsp(driver->apps_rsp_buf, write_len);
+		diag_send_rsp(driver->apps_rsp_buf, write_len, info);
 		/*
 		 * Set the value of hdlc_disabled after sending the response to
 		 * the tools. This is required since the tools is expecting a
@@ -1179,7 +1234,7 @@
 
 	/* We have now come to the end of the function. */
 	if (chk_apps_only())
-		diag_send_error_rsp(buf, len);
+		diag_send_error_rsp(buf, len, info);
 
 	return 0;
 }
@@ -1262,7 +1317,7 @@
 	 * recovery algorithm. Send an error response if the
 	 * packet is not in expected format.
 	 */
-	diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len);
+	diag_send_error_rsp(driver->hdlc_buf, driver->hdlc_buf_len, info);
 	driver->hdlc_buf_len = 0;
 end:
 	mutex_unlock(&driver->diag_hdlc_mutex);
@@ -1535,7 +1590,7 @@
 
 		if (actual_pkt->start != CONTROL_CHAR) {
 			diag_hdlc_start_recovery(buf, len, info);
-			diag_send_error_rsp(buf, len);
+			diag_send_error_rsp(buf, len, info);
 			goto end;
 		}
 		mutex_lock(&driver->hdlc_recovery_mutex);
@@ -1611,6 +1666,9 @@
 	switch (type) {
 	case TYPE_DATA:
 		if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) {
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Marking buffer as free after write done p: %d, t: %d, buf_num: %d\n",
+				peripheral, type, num);
 			diagfwd_write_done(peripheral, type, num);
 			diag_ws_on_copy(DIAG_WS_MUX);
 		} else if (peripheral == APPS_DATA) {
@@ -1625,15 +1683,17 @@
 	case TYPE_CMD:
 		if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) {
 			diagfwd_write_done(peripheral, type, num);
-		} else if (peripheral == APPS_DATA) {
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Marking buffer as free after write done p: %d, t: %d, buf_num: %d\n",
+				peripheral, type, num);
+		}
+		if (peripheral == APPS_DATA ||
+				ctxt == DIAG_MEMORY_DEVICE_MODE) {
 			spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags);
 			driver->rsp_buf_busy = 0;
 			driver->encoded_rsp_len = 0;
 			spin_unlock_irqrestore(&driver->rsp_buf_busy_lock,
 					       flags);
-		} else {
-			pr_err_ratelimited("diag: Invalid peripheral %d in %s, type: %d\n",
-					   peripheral, __func__, type);
 		}
 		break;
 	default:
@@ -1665,6 +1725,7 @@
 	driver->supports_separate_cmdrsp = 1;
 	driver->supports_apps_hdlc_encoding = 1;
 	driver->supports_apps_header_untagging = 1;
+	driver->supports_pd_buffering = 1;
 	for (i = 0; i < NUM_PERIPHERALS; i++)
 		driver->peripheral_untag[i] = 0;
 	mutex_init(&driver->diag_hdlc_mutex);
@@ -1695,6 +1756,7 @@
 		driver->feature[i].stm_support = DISABLE_STM;
 		driver->feature[i].rcvd_feature_mask = 0;
 		driver->feature[i].peripheral_buffering = 0;
+		driver->feature[i].pd_buffering = 0;
 		driver->feature[i].encode_hdlc = 0;
 		driver->feature[i].untag_header =
 			DISABLE_PKT_HEADER_UNTAGGING;
@@ -1702,6 +1764,9 @@
 		driver->feature[i].log_on_demand = 0;
 		driver->feature[i].sent_feature_mask = 0;
 		driver->feature[i].diag_id_support = 0;
+	}
+
+	for (i = 0; i < NUM_MD_SESSIONS; i++) {
 		driver->buffering_mode[i].peripheral = i;
 		driver->buffering_mode[i].mode = DIAG_BUFFERING_MODE_STREAMING;
 		driver->buffering_mode[i].high_wm_val = DEFAULT_HIGH_WM_VAL;
@@ -1742,6 +1807,9 @@
 	}
 	kmemleak_not_leak(driver->data_ready);
 
+	for (i = 0; i < THRESHOLD_CLIENT_LIMIT; i++)
+		atomic_set(&driver->data_ready_notif[i], 0);
+
 	if (driver->apps_req_buf == NULL) {
 		driver->apps_req_buf = kzalloc(DIAG_MAX_REQ_SIZE, GFP_KERNEL);
 		if (!driver->apps_req_buf)
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 677099f..0e0bf2d 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -47,7 +47,8 @@
 void diag_update_sleeping_process(int process_id, int data_type);
 int diag_process_apps_pkt(unsigned char *buf, int len,
 			  struct diag_md_session_t *info);
-void diag_send_error_rsp(unsigned char *buf, int len);
+void diag_send_error_rsp(unsigned char *buf, int len,
+			 struct diag_md_session_t *info);
 void diag_update_pkt_buffer(unsigned char *buf, uint32_t len, int type);
 int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf);
 void diag_md_hdlc_reset_timer_func(unsigned long pid);
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
index 3684b8d..5de7897 100644
--- a/drivers/char/diag/diagfwd_bridge.c
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -90,6 +90,18 @@
 {
 	if (id < 0 || id >= NUM_REMOTE_DEV)
 		return -EINVAL;
+
+	if ((mode == DIAG_USB_MODE &&
+		driver->logging_mode == DIAG_MEMORY_DEVICE_MODE) ||
+		(mode == DIAG_MEMORY_DEVICE_MODE &&
+		driver->logging_mode == DIAG_USB_MODE)) {
+		/*
+		 * Don't close the MHI channels when usb is disconnected
+		 * and a process is running in memory device mode.
+		 */
+		return 0;
+	}
+
 	if (bridge_info[id].dev_ops && bridge_info[id].dev_ops->close)
 		bridge_info[id].dev_ops->close(bridge_info[id].ctxt);
 	return 0;
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index d7e24fc..eaca17a 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -111,6 +111,8 @@
 {
 	int stat = 0;
 	struct siginfo info;
+	struct pid *pid_struct;
+	struct task_struct *result;
 
 	if (peripheral > NUM_PERIPHERALS)
 		return;
@@ -123,18 +125,38 @@
 	info.si_code = SI_QUEUE;
 	info.si_int = (PERIPHERAL_MASK(peripheral) | data);
 	info.si_signo = SIGCONT;
-	if (driver->md_session_map[peripheral] &&
-		driver->md_session_map[peripheral]->task) {
-		if (driver->md_session_map[peripheral]->pid ==
-			driver->md_session_map[peripheral]->task->tgid) {
+
+	if (!driver->md_session_map[peripheral] ||
+		driver->md_session_map[peripheral]->pid <= 0) {
+		pr_err("diag: md_session_map[%d] is invalid\n", peripheral);
+		mutex_unlock(&driver->md_session_lock);
+		return;
+	}
+
+	pid_struct = find_get_pid(
+			driver->md_session_map[peripheral]->pid);
+	DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"md_session_map[%d] pid = %d task = %pK\n",
+		peripheral,
+		driver->md_session_map[peripheral]->pid,
+		driver->md_session_map[peripheral]->task);
+
+	if (pid_struct) {
+		result = get_pid_task(pid_struct, PIDTYPE_PID);
+
+		if (!result) {
 			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
-				"md_session %d pid = %d, md_session %d task tgid = %d\n",
+				"diag: md_session_map[%d] with pid = %d Exited..\n",
 				peripheral,
-				driver->md_session_map[peripheral]->pid,
-				peripheral,
-				driver->md_session_map[peripheral]->task->tgid);
-			stat = send_sig_info(info.si_signo, &info,
-				driver->md_session_map[peripheral]->task);
+				driver->md_session_map[peripheral]->pid);
+			mutex_unlock(&driver->md_session_lock);
+			return;
+		}
+
+		if (driver->md_session_map[peripheral] &&
+			driver->md_session_map[peripheral]->task == result) {
+			stat = send_sig_info(info.si_signo,
+					&info, result);
 			if (stat)
 				pr_err("diag: Err sending signal to memory device client, signal data: 0x%x, stat: %d\n",
 					info.si_int, stat);
@@ -401,6 +423,8 @@
 			enable_socket_feature(peripheral);
 		if (FEATURE_SUPPORTED(F_DIAG_DIAGID_SUPPORT))
 			driver->feature[peripheral].diag_id_support = 1;
+		if (FEATURE_SUPPORTED(F_DIAG_PD_BUFFERING))
+			driver->feature[peripheral].pd_buffering = 1;
 	}
 
 	process_socket_feature(peripheral);
@@ -672,7 +696,8 @@
 	}
 }
 
-int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name)
+int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name,
+	uint8_t pd_val, uint8_t peripheral)
 {
 	struct diag_id_tbl_t *new_item = NULL;
 
@@ -691,6 +716,8 @@
 	}
 	kmemleak_not_leak(new_item->process_name);
 	new_item->diag_id = diag_id;
+	new_item->pd_val = pd_val;
+	new_item->peripheral = peripheral;
 	strlcpy(new_item->process_name, process_name, strlen(process_name) + 1);
 	INIT_LIST_HEAD(&new_item->link);
 	mutex_lock(&driver->diag_id_mutex);
@@ -725,47 +752,59 @@
 {
 	struct diag_ctrl_diagid *header = NULL;
 	struct diag_ctrl_diagid ctrl_pkt;
-	struct diagfwd_info *fwd_info_data = NULL;
-	struct diagfwd_info *fwd_info_cmd = NULL;
+	struct diagfwd_info *fwd_info = NULL;
 	char *process_name = NULL;
 	int err = 0;
+	int pd_val;
+	char *root_str = NULL;
 	uint8_t local_diag_id = 0;
-	uint8_t new_request = 0;
+	uint8_t new_request = 0, i = 0, ch_type = 0;
 
 	if (!buf || len == 0 || peripheral >= NUM_PERIPHERALS)
 		return;
 
-	fwd_info_data = &peripheral_info[TYPE_DATA][peripheral];
-	if (!fwd_info_data)
-		return;
-
-	fwd_info_cmd = &peripheral_info[TYPE_CMD][peripheral];
-	if (!fwd_info_cmd)
-		return;
-
 	header = (struct diag_ctrl_diagid *)buf;
 	process_name = (char *)&header->process_name;
 	if (diag_query_diag_id(process_name, &local_diag_id))
 		ctrl_pkt.diag_id = local_diag_id;
 	else {
 		diag_id++;
-		diag_add_diag_id_to_list(diag_id, process_name);
-		ctrl_pkt.diag_id = diag_id;
 		new_request = 1;
+		pd_val = diag_query_pd(process_name);
+		if (pd_val < 0)
+			return;
+		diag_add_diag_id_to_list(diag_id, process_name,
+			pd_val, peripheral);
+		ctrl_pkt.diag_id = diag_id;
 	}
+	root_str = strnstr(process_name, DIAG_ID_ROOT_STRING,
+		strlen(process_name));
 
 	if (new_request) {
-		fwd_info_data->num_pd++;
-		fwd_info_cmd->num_pd++;
-	}
+		for (ch_type = 0; ch_type < NUM_TYPES; ch_type++) {
+			if (ch_type == TYPE_DCI ||
+				ch_type == TYPE_DCI_CMD)
+				continue;
+			fwd_info = &peripheral_info[ch_type][peripheral];
+			fwd_info->num_pd++;
 
-	if (strnstr(process_name, DIAG_ID_ROOT_STRING, strlen(process_name))) {
-		fwd_info_cmd->diagid_root = diag_id;
-		fwd_info_data->diagid_root = diag_id;
-		driver->diag_id_sent[peripheral] = 0;
-	} else {
-		fwd_info_cmd->diagid_user[fwd_info_cmd->num_pd - 2] = diag_id;
-		fwd_info_data->diagid_user[fwd_info_data->num_pd - 2] = diag_id;
+			if (root_str) {
+				fwd_info->root_diag_id.diagid_val =
+					ctrl_pkt.diag_id;
+				fwd_info->root_diag_id.reg_str =
+					process_name;
+				fwd_info->root_diag_id.pd = pd_val;
+			} else {
+				i = fwd_info->num_pd - 2;
+				if (i >= 0 && i < MAX_PERIPHERAL_UPD) {
+					fwd_info->upd_diag_id[i].diagid_val =
+						ctrl_pkt.diag_id;
+					fwd_info->upd_diag_id[i].reg_str =
+						process_name;
+					fwd_info->upd_diag_id[i].pd = pd_val;
+				}
+			}
+		}
 	}
 
 	DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
@@ -784,18 +823,25 @@
 		pr_err("diag: Unable to send diag id ctrl packet to peripheral %d, err: %d\n",
 		       peripheral, err);
 	} else {
-	/*
-	 * Masks (F3, logs and events) will be sent to
-	 * peripheral immediately following feature mask update only
-	 * if diag_id support is not present or
-	 * diag_id support is present and diag_id has been sent to
-	 * peripheral.
-	 * With diag_id being sent now, mask will be updated
-	 * to peripherals.
-	 */
-		driver->diag_id_sent[peripheral] = 1;
-		diag_send_updates_peripheral(peripheral);
-		diagfwd_buffers_init(fwd_info_data);
+		/*
+		 * Masks (F3, logs and events) will be sent to
+		 * peripheral immediately following feature mask update only
+		 * if diag_id support is not present or
+		 * diag_id support is present and diag_id has been sent to
+		 * peripheral.
+		 * With diag_id being sent now, mask will be updated
+		 * to peripherals.
+		 */
+		if (root_str) {
+			driver->diag_id_sent[peripheral] = 1;
+			diag_send_updates_peripheral(peripheral);
+		}
+		fwd_info = &peripheral_info[TYPE_DATA][peripheral];
+		diagfwd_buffers_init(fwd_info);
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"diag: diag_id sent = %d to peripheral = %d with diag_id = %d for %s :\n",
+			driver->diag_id_sent[peripheral], peripheral,
+			ctrl_pkt.diag_id, process_name);
 	}
 }
 
@@ -903,32 +949,54 @@
 }
 
 static void diag_create_diag_mode_ctrl_pkt(unsigned char *dest_buf,
-					   int real_time)
+					   uint8_t diag_id, int real_time)
 {
 	struct diag_ctrl_msg_diagmode diagmode;
+	struct diag_ctrl_msg_diagmode_v2 diagmode_v2;
 	int msg_size = sizeof(struct diag_ctrl_msg_diagmode);
+	int msg_size_2 = sizeof(struct diag_ctrl_msg_diagmode_v2);
 
 	if (!dest_buf)
 		return;
 
-	diagmode.ctrl_pkt_id = DIAG_CTRL_MSG_DIAGMODE;
-	diagmode.ctrl_pkt_data_len = DIAG_MODE_PKT_LEN;
-	diagmode.version = 1;
-	diagmode.sleep_vote = real_time ? 1 : 0;
-	/*
-	 * 0 - Disables real-time logging (to prevent
-	 *     frequent APPS wake-ups, etc.).
-	 * 1 - Enable real-time logging
-	 */
-	diagmode.real_time = real_time;
-	diagmode.use_nrt_values = 0;
-	diagmode.commit_threshold = 0;
-	diagmode.sleep_threshold = 0;
-	diagmode.sleep_time = 0;
-	diagmode.drain_timer_val = 0;
-	diagmode.event_stale_timer_val = 0;
-
-	memcpy(dest_buf, &diagmode, msg_size);
+	if (diag_id) {
+		diagmode_v2.ctrl_pkt_id = DIAG_CTRL_MSG_DIAGMODE;
+		diagmode_v2.ctrl_pkt_data_len = DIAG_MODE_PKT_LEN_V2;
+		diagmode_v2.version = 2;
+		diagmode_v2.sleep_vote = real_time ? 1 : 0;
+		/*
+		 * 0 - Disables real-time logging (to prevent
+		 *	   frequent APPS wake-ups, etc.).
+		 * 1 - Enable real-time logging
+		 */
+		diagmode_v2.real_time = real_time;
+		diagmode_v2.use_nrt_values = 0;
+		diagmode_v2.commit_threshold = 0;
+		diagmode_v2.sleep_threshold = 0;
+		diagmode_v2.sleep_time = 0;
+		diagmode_v2.drain_timer_val = 0;
+		diagmode_v2.event_stale_timer_val = 0;
+		diagmode_v2.diag_id = diag_id;
+		memcpy(dest_buf, &diagmode_v2, msg_size_2);
+	} else {
+		diagmode.ctrl_pkt_id = DIAG_CTRL_MSG_DIAGMODE;
+		diagmode.ctrl_pkt_data_len = DIAG_MODE_PKT_LEN;
+		diagmode.version = 1;
+		diagmode.sleep_vote = real_time ? 1 : 0;
+		/*
+		 * 0 - Disables real-time logging (to prevent
+		 *     frequent APPS wake-ups, etc.).
+		 * 1 - Enable real-time logging
+		 */
+		diagmode.real_time = real_time;
+		diagmode.use_nrt_values = 0;
+		diagmode.commit_threshold = 0;
+		diagmode.sleep_threshold = 0;
+		diagmode.sleep_time = 0;
+		diagmode.drain_timer_val = 0;
+		diagmode.event_stale_timer_val = 0;
+		memcpy(dest_buf, &diagmode, msg_size);
+	}
 }
 
 void diag_update_proc_vote(uint16_t proc, uint8_t vote, int index)
@@ -1013,7 +1081,7 @@
 
 	memcpy(buf + write_len, &dci_header, dci_header_size);
 	write_len += dci_header_size;
-	diag_create_diag_mode_ctrl_pkt(buf + write_len, real_time);
+	diag_create_diag_mode_ctrl_pkt(buf + write_len, 0, real_time);
 	write_len += msg_size;
 	*(buf + write_len) = CONTROL_CHAR; /* End Terminator */
 	write_len += sizeof(uint8_t);
@@ -1119,14 +1187,18 @@
 }
 #endif
 
-static int __diag_send_real_time_update(uint8_t peripheral, int real_time)
+static int __diag_send_real_time_update(uint8_t peripheral, int real_time,
+						uint8_t diag_id)
 {
-	char buf[sizeof(struct diag_ctrl_msg_diagmode)];
-	int msg_size = sizeof(struct diag_ctrl_msg_diagmode);
+	char buf[sizeof(struct diag_ctrl_msg_diagmode_v2)];
+	int msg_size = 0;
 	int err = 0;
 
-	if (peripheral >= NUM_PERIPHERALS)
+	if (peripheral >= NUM_PERIPHERALS) {
+		pr_err("diag: In %s, invalid peripheral %d\n", __func__,
+		       peripheral);
 		return -EINVAL;
+	}
 
 	if (!driver->diagfwd_cntl[peripheral] ||
 	    !driver->diagfwd_cntl[peripheral]->ch_open) {
@@ -1141,12 +1213,17 @@
 		return -EINVAL;
 	}
 
-	diag_create_diag_mode_ctrl_pkt(buf, real_time);
+	msg_size = (diag_id ? sizeof(struct diag_ctrl_msg_diagmode_v2) :
+		sizeof(struct diag_ctrl_msg_diagmode));
+
+	diag_create_diag_mode_ctrl_pkt(buf, diag_id, real_time);
 
 	mutex_lock(&driver->diag_cntl_mutex);
+
 	err = diagfwd_write(peripheral, TYPE_CNTL, buf, msg_size);
+
 	if (err && err != -ENODEV) {
-		pr_err("diag: In %s, unable to write to socket, peripheral: %d, type: %d, len: %d, err: %d\n",
+		pr_err("diag: In %s, unable to write, peripheral: %d, type: %d, len: %d, err: %d\n",
 		       __func__, peripheral, TYPE_CNTL,
 		       msg_size, err);
 	} else {
@@ -1172,27 +1249,56 @@
 		return -EINVAL;
 	}
 
-	return __diag_send_real_time_update(peripheral, real_time);
+	return __diag_send_real_time_update(peripheral, real_time, 0);
+}
+
+void diag_map_pd_to_diagid(uint8_t pd, uint8_t *diag_id, int *peripheral)
+{
+	if (!diag_search_diagid_by_pd(pd, (void *)diag_id,
+		(void *)peripheral)) {
+		*diag_id = 0;
+		if ((pd >= 0) && pd < NUM_PERIPHERALS)
+			*peripheral = pd;
+		else
+			*peripheral = -EINVAL;
+	}
+
+	if (*peripheral >= 0)
+		if (!driver->feature[*peripheral].pd_buffering)
+			*diag_id = 0;
 }
 
 int diag_send_peripheral_buffering_mode(struct diag_buffering_mode_t *params)
 {
 	int err = 0;
 	int mode = MODE_REALTIME;
-	uint8_t peripheral = 0;
+	int peripheral = 0;
+	uint8_t diag_id = 0;
 
 	if (!params)
 		return -EIO;
 
-	peripheral = params->peripheral;
-	if (peripheral >= NUM_PERIPHERALS) {
+	diag_map_pd_to_diagid(params->peripheral,
+		&diag_id, &peripheral);
+
+	if ((peripheral < 0) ||
+		peripheral >= NUM_PERIPHERALS) {
 		pr_err("diag: In %s, invalid peripheral %d\n", __func__,
 		       peripheral);
 		return -EINVAL;
 	}
 
-	if (!driver->buffering_flag[peripheral])
+	if (!driver->buffering_flag[params->peripheral]) {
+		pr_err("diag: In %s, buffering flag not set for %d\n", __func__,
+		       params->peripheral);
 		return -EINVAL;
+	}
+
+	if (!driver->feature[peripheral].peripheral_buffering) {
+		pr_err("diag: In %s, peripheral %d doesn't support buffering\n",
+		       __func__, peripheral);
+		return -EIO;
+	}
 
 	switch (params->mode) {
 	case DIAG_BUFFERING_MODE_STREAMING:
@@ -1211,7 +1317,7 @@
 	if (!driver->feature[peripheral].peripheral_buffering) {
 		pr_debug("diag: In %s, peripheral %d doesn't support buffering\n",
 			 __func__, peripheral);
-		driver->buffering_flag[peripheral] = 0;
+		driver->buffering_flag[params->peripheral] = 0;
 		return -EIO;
 	}
 
@@ -1226,35 +1332,39 @@
 	     (params->low_wm_val != DIAG_MIN_WM_VAL))) {
 		pr_err("diag: In %s, invalid watermark values, high: %d, low: %d, peripheral: %d\n",
 		       __func__, params->high_wm_val, params->low_wm_val,
-		       peripheral);
+		       params->peripheral);
 		return -EINVAL;
 	}
 
 	mutex_lock(&driver->mode_lock);
-	err = diag_send_buffering_tx_mode_pkt(peripheral, params);
+	err = diag_send_buffering_tx_mode_pkt(peripheral, diag_id, params);
 	if (err) {
 		pr_err("diag: In %s, unable to send buffering mode packet to peripheral %d, err: %d\n",
 		       __func__, peripheral, err);
 		goto fail;
 	}
-	err = diag_send_buffering_wm_values(peripheral, params);
+	err = diag_send_buffering_wm_values(peripheral, diag_id, params);
 	if (err) {
 		pr_err("diag: In %s, unable to send buffering wm value packet to peripheral %d, err: %d\n",
 		       __func__, peripheral, err);
 		goto fail;
 	}
-	err = __diag_send_real_time_update(peripheral, mode);
+	err = __diag_send_real_time_update(peripheral, mode, diag_id);
 	if (err) {
 		pr_err("diag: In %s, unable to send mode update to peripheral %d, mode: %d, err: %d\n",
 		       __func__, peripheral, mode, err);
 		goto fail;
 	}
-	driver->buffering_mode[peripheral].peripheral = peripheral;
-	driver->buffering_mode[peripheral].mode = params->mode;
-	driver->buffering_mode[peripheral].low_wm_val = params->low_wm_val;
-	driver->buffering_mode[peripheral].high_wm_val = params->high_wm_val;
+	driver->buffering_mode[params->peripheral].peripheral =
+		params->peripheral;
+	driver->buffering_mode[params->peripheral].mode =
+		params->mode;
+	driver->buffering_mode[params->peripheral].low_wm_val =
+		params->low_wm_val;
+	driver->buffering_mode[params->peripheral].high_wm_val =
+		params->high_wm_val;
 	if (params->mode == DIAG_BUFFERING_MODE_STREAMING)
-		driver->buffering_flag[peripheral] = 0;
+		driver->buffering_flag[params->peripheral] = 0;
 fail:
 	mutex_unlock(&driver->mode_lock);
 	return err;
@@ -1293,10 +1403,12 @@
 	return err;
 }
 
-int diag_send_peripheral_drain_immediate(uint8_t peripheral)
+int diag_send_peripheral_drain_immediate(uint8_t pd,
+		uint8_t diag_id, int peripheral)
 {
 	int err = 0;
 	struct diag_ctrl_drain_immediate ctrl_pkt;
+	struct diag_ctrl_drain_immediate_v2 ctrl_pkt_v2;
 
 	if (!driver->feature[peripheral].peripheral_buffering) {
 		pr_debug("diag: In %s, peripheral  %d doesn't support buffering\n",
@@ -1311,32 +1423,57 @@
 		return -ENODEV;
 	}
 
-	ctrl_pkt.pkt_id = DIAG_CTRL_MSG_PERIPHERAL_BUF_DRAIN_IMM;
-	/* The length of the ctrl pkt is size of version and stream id */
-	ctrl_pkt.len = sizeof(uint32_t) + sizeof(uint8_t);
-	ctrl_pkt.version = 1;
-	ctrl_pkt.stream_id = 1;
-
-	err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt, sizeof(ctrl_pkt));
-	if (err && err != -ENODEV) {
-		pr_err("diag: Unable to send drain immediate ctrl packet to peripheral %d, err: %d\n",
-		       peripheral, err);
+	if (diag_id && driver->feature[peripheral].pd_buffering) {
+		ctrl_pkt_v2.pkt_id = DIAG_CTRL_MSG_PERIPHERAL_BUF_DRAIN_IMM;
+		/*
+		 * The length of the ctrl pkt is size of version,
+		 * diag_id and stream id
+		 */
+		ctrl_pkt_v2.len = sizeof(uint32_t) + (2 * sizeof(uint8_t));
+		ctrl_pkt_v2.version = 2;
+		ctrl_pkt_v2.diag_id = diag_id;
+		ctrl_pkt_v2.stream_id = 1;
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt_v2,
+				sizeof(ctrl_pkt_v2));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send drain immediate ctrl packet to peripheral %d, err: %d\n",
+			peripheral, err);
+		}
+	} else {
+		ctrl_pkt.pkt_id = DIAG_CTRL_MSG_PERIPHERAL_BUF_DRAIN_IMM;
+		/*
+		 * The length of the ctrl pkt is
+		 * size of version and stream id
+		 */
+		ctrl_pkt.len = sizeof(uint32_t) + sizeof(uint8_t);
+		ctrl_pkt.version = 1;
+		ctrl_pkt.stream_id = 1;
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt,
+				sizeof(ctrl_pkt));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send drain immediate ctrl packet to peripheral %d, err: %d\n",
+			peripheral, err);
+		}
 	}
 
 	return err;
 }
 
 int diag_send_buffering_tx_mode_pkt(uint8_t peripheral,
-				    struct diag_buffering_mode_t *params)
+		    uint8_t diag_id, struct diag_buffering_mode_t *params)
 {
 	int err = 0;
 	struct diag_ctrl_peripheral_tx_mode ctrl_pkt;
+	struct diag_ctrl_peripheral_tx_mode_v2 ctrl_pkt_v2;
 
 	if (!params)
 		return -EIO;
 
-	if (peripheral >= NUM_PERIPHERALS)
+	if (peripheral >= NUM_PERIPHERALS) {
+		pr_err("diag: In %s, invalid peripheral %d\n", __func__,
+		       peripheral);
 		return -EINVAL;
+	}
 
 	if (!driver->feature[peripheral].peripheral_buffering) {
 		pr_debug("diag: In %s, peripheral  %d doesn't support buffering\n",
@@ -1344,9 +1481,6 @@
 		return -EINVAL;
 	}
 
-	if (params->peripheral != peripheral)
-		return -EINVAL;
-
 	switch (params->mode) {
 	case DIAG_BUFFERING_MODE_STREAMING:
 	case DIAG_BUFFERING_MODE_THRESHOLD:
@@ -1358,36 +1492,67 @@
 		return -EINVAL;
 	}
 
-	ctrl_pkt.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_TX_MODE;
-	/* Control packet length is size of version, stream_id and tx_mode */
-	ctrl_pkt.len = sizeof(uint32_t) +  (2 * sizeof(uint8_t));
-	ctrl_pkt.version = 1;
-	ctrl_pkt.stream_id = 1;
-	ctrl_pkt.tx_mode = params->mode;
+	if (diag_id &&
+		driver->feature[peripheral].pd_buffering) {
 
-	err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt, sizeof(ctrl_pkt));
-	if (err && err != -ENODEV) {
-		pr_err("diag: Unable to send tx_mode ctrl packet to peripheral %d, err: %d\n",
-		       peripheral, err);
-		goto fail;
+		ctrl_pkt_v2.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_TX_MODE;
+		/*
+		 * Control packet length is size of version, diag_id,
+		 * stream_id and tx_mode
+		 */
+		ctrl_pkt_v2.len = sizeof(uint32_t) +  (3 * sizeof(uint8_t));
+		ctrl_pkt_v2.version = 2;
+		ctrl_pkt_v2.diag_id = diag_id;
+		ctrl_pkt_v2.stream_id = 1;
+		ctrl_pkt_v2.tx_mode = params->mode;
+
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt_v2,
+			sizeof(ctrl_pkt_v2));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send tx_mode ctrl packet to peripheral %d, err: %d\n",
+				   peripheral, err);
+			goto fail;
+		}
+	} else {
+		ctrl_pkt.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_TX_MODE;
+		/*
+		 * Control packet length is size of version,
+		 * stream_id and tx_mode
+		 */
+		ctrl_pkt.len = sizeof(uint32_t) +  (2 * sizeof(uint8_t));
+		ctrl_pkt.version = 1;
+		ctrl_pkt.stream_id = 1;
+		ctrl_pkt.tx_mode = params->mode;
+
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt,
+			sizeof(ctrl_pkt));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send tx_mode ctrl packet to peripheral %d, err: %d\n",
+			       peripheral, err);
+			goto fail;
+		}
 	}
-	driver->buffering_mode[peripheral].mode = params->mode;
+	driver->buffering_mode[params->peripheral].mode = params->mode;
 
 fail:
 	return err;
 }
 
 int diag_send_buffering_wm_values(uint8_t peripheral,
-				  struct diag_buffering_mode_t *params)
+		uint8_t diag_id, struct diag_buffering_mode_t *params)
 {
 	int err = 0;
 	struct diag_ctrl_set_wq_val ctrl_pkt;
+	struct diag_ctrl_set_wq_val_v2 ctrl_pkt_v2;
 
 	if (!params)
 		return -EIO;
 
-	if (peripheral >= NUM_PERIPHERALS)
+	if (peripheral >= NUM_PERIPHERALS) {
+		pr_err("diag: In %s, invalid peripheral %d\n", __func__,
+		       peripheral);
 		return -EINVAL;
+	}
 
 	if (!driver->feature[peripheral].peripheral_buffering) {
 		pr_debug("diag: In %s, peripheral  %d doesn't support buffering\n",
@@ -1402,9 +1567,6 @@
 		return -ENODEV;
 	}
 
-	if (params->peripheral != peripheral)
-		return -EINVAL;
-
 	switch (params->mode) {
 	case DIAG_BUFFERING_MODE_STREAMING:
 	case DIAG_BUFFERING_MODE_THRESHOLD:
@@ -1416,21 +1578,45 @@
 		return -EINVAL;
 	}
 
-	ctrl_pkt.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_WMQ_VAL;
-	/* Control packet length is size of version, stream_id and wmq values */
-	ctrl_pkt.len = sizeof(uint32_t) + (3 * sizeof(uint8_t));
-	ctrl_pkt.version = 1;
-	ctrl_pkt.stream_id = 1;
-	ctrl_pkt.high_wm_val = params->high_wm_val;
-	ctrl_pkt.low_wm_val = params->low_wm_val;
+	if (diag_id &&
+		driver->feature[peripheral].pd_buffering) {
+		ctrl_pkt_v2.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_WMQ_VAL;
+		/*
+		 * Control packet length is size of version, diag_id,
+		 * stream_id and wmq values
+		 */
+		ctrl_pkt_v2.len = sizeof(uint32_t) + (4 * sizeof(uint8_t));
+		ctrl_pkt_v2.version = 2;
+		ctrl_pkt_v2.diag_id = diag_id;
+		ctrl_pkt_v2.stream_id = 1;
+		ctrl_pkt_v2.high_wm_val = params->high_wm_val;
+		ctrl_pkt_v2.low_wm_val = params->low_wm_val;
 
-	err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt,
-			    sizeof(ctrl_pkt));
-	if (err && err != -ENODEV) {
-		pr_err("diag: Unable to send watermark values to peripheral %d, err: %d\n",
-		       peripheral, err);
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt_v2,
+					sizeof(ctrl_pkt_v2));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send watermark values to peripheral %d, err: %d\n",
+				   peripheral, err);
+		}
+	} else {
+		ctrl_pkt.pkt_id = DIAG_CTRL_MSG_CONFIG_PERIPHERAL_WMQ_VAL;
+		/*
+		 * Control packet length is size of version,
+		 * stream_id and wmq values
+		 */
+		ctrl_pkt.len = sizeof(uint32_t) + (3 * sizeof(uint8_t));
+		ctrl_pkt.version = 1;
+		ctrl_pkt.stream_id = 1;
+		ctrl_pkt.high_wm_val = params->high_wm_val;
+		ctrl_pkt.low_wm_val = params->low_wm_val;
+
+		err = diagfwd_write(peripheral, TYPE_CNTL, &ctrl_pkt,
+				    sizeof(ctrl_pkt));
+		if (err && err != -ENODEV) {
+			pr_err("diag: Unable to send watermark values to peripheral %d, err: %d\n",
+			       peripheral, err);
+		}
 	}
-
 	return err;
 }
 
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 8b22d7e..848ad87 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -69,6 +69,7 @@
 #define F_DIAG_DCI_EXTENDED_HEADER_SUPPORT	14
 #define F_DIAG_DIAGID_SUPPORT	15
 #define F_DIAG_PKT_HEADER_UNTAG			16
+#define F_DIAG_PD_BUFFERING		17
 
 #define ENABLE_SEPARATE_CMDRSP	1
 #define DISABLE_SEPARATE_CMDRSP	0
@@ -86,7 +87,8 @@
 #define ENABLE_PKT_HEADER_UNTAGGING		1
 #define DISABLE_PKT_HEADER_UNTAGGING	0
 
-#define DIAG_MODE_PKT_LEN	36
+#define DIAG_MODE_PKT_LEN		36
+#define DIAG_MODE_PKT_LEN_V2	37
 
 struct diag_ctrl_pkt_header_t {
 	uint32_t pkt_id;
@@ -172,6 +174,21 @@
 	uint32_t event_stale_timer_val;
 } __packed;
 
+struct diag_ctrl_msg_diagmode_v2 {
+	uint32_t ctrl_pkt_id;
+	uint32_t ctrl_pkt_data_len;
+	uint32_t version;
+	uint32_t sleep_vote;
+	uint32_t real_time;
+	uint32_t use_nrt_values;
+	uint32_t commit_threshold;
+	uint32_t sleep_threshold;
+	uint32_t sleep_time;
+	uint32_t drain_timer_val;
+	uint32_t event_stale_timer_val;
+	uint8_t diag_id;
+} __packed;
+
 struct diag_ctrl_msg_stm {
 	uint32_t ctrl_pkt_id;
 	uint32_t ctrl_pkt_data_len;
@@ -250,6 +267,15 @@
 	uint8_t tx_mode;
 } __packed;
 
+struct diag_ctrl_peripheral_tx_mode_v2 {
+	uint32_t pkt_id;
+	uint32_t len;
+	uint32_t version;
+	uint8_t diag_id;
+	uint8_t stream_id;
+	uint8_t tx_mode;
+} __packed;
+
 struct diag_ctrl_drain_immediate {
 	uint32_t pkt_id;
 	uint32_t len;
@@ -257,6 +283,14 @@
 	uint8_t stream_id;
 } __packed;
 
+struct diag_ctrl_drain_immediate_v2 {
+	uint32_t pkt_id;
+	uint32_t len;
+	uint32_t version;
+	uint8_t diag_id;
+	uint8_t stream_id;
+} __packed;
+
 struct diag_ctrl_set_wq_val {
 	uint32_t pkt_id;
 	uint32_t len;
@@ -266,6 +300,16 @@
 	uint8_t low_wm_val;
 } __packed;
 
+struct diag_ctrl_set_wq_val_v2 {
+	uint32_t pkt_id;
+	uint32_t len;
+	uint32_t version;
+	uint8_t diag_id;
+	uint8_t stream_id;
+	uint8_t high_wm_val;
+	uint8_t low_wm_val;
+} __packed;
+
 struct diag_ctrl_diagid {
 	uint32_t pkt_id;
 	uint32_t len;
@@ -275,7 +319,8 @@
 } __packed;
 
 int diagfwd_cntl_init(void);
-int diag_add_diag_id_to_list(uint8_t diag_id, char *process_name);
+int diag_add_diag_id_to_list(uint8_t diag_id,
+	char *process_name, uint8_t pd_val, uint8_t peripheral);
 void diagfwd_cntl_channel_init(void);
 void diagfwd_cntl_exit(void);
 void diag_cntl_channel_open(struct diagfwd_info *p_info);
@@ -289,9 +334,10 @@
 void diag_update_real_time_vote(uint16_t proc, uint8_t real_time, int index);
 void diag_real_time_work_fn(struct work_struct *work);
 int diag_send_stm_state(uint8_t peripheral, uint8_t stm_control_data);
-int diag_send_peripheral_drain_immediate(uint8_t peripheral);
+int diag_send_peripheral_drain_immediate(uint8_t pd,
+			uint8_t diag_id, int peripheral);
 int diag_send_buffering_tx_mode_pkt(uint8_t peripheral,
-				    struct diag_buffering_mode_t *params);
+		    uint8_t diag_id, struct diag_buffering_mode_t *params);
 int diag_send_buffering_wm_values(uint8_t peripheral,
-				  struct diag_buffering_mode_t *params);
+		    uint8_t diag_id, struct diag_buffering_mode_t *params);
 #endif
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index 13904fd..4d6ae23 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -30,6 +30,7 @@
 #include "diag_mux.h"
 #include "diag_ipc_logging.h"
 #include "diagfwd_glink.h"
+#include "diag_memorydevice.h"
 
 struct data_header {
 	uint8_t control_char;
@@ -187,8 +188,10 @@
 
 static int check_bufsize_for_encoding(struct diagfwd_buf_t *buf, uint32_t len)
 {
+	int i, ctx = 0;
 	uint32_t max_size = 0;
 	unsigned char *temp_buf = NULL;
+	struct diag_md_info *ch = NULL;
 
 	if (!buf || len == 0)
 		return -EINVAL;
@@ -202,11 +205,31 @@
 		}
 
 		if (buf->len < max_size) {
+			if (driver->logging_mode == DIAG_MEMORY_DEVICE_MODE) {
+				ch = &diag_md[DIAG_LOCAL_PROC];
+				for (i = 0; ch != NULL &&
+						i < ch->num_tbl_entries; i++) {
+					if (ch->tbl[i].buf == buf->data) {
+						ctx = ch->tbl[i].ctx;
+						ch->tbl[i].buf = NULL;
+						ch->tbl[i].len = 0;
+						ch->tbl[i].ctx = 0;
+						DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+						"Flushed mdlog table entries before reallocating data buffer, p:%d, t:%d\n",
+						GET_BUF_PERIPHERAL(ctx),
+						GET_BUF_TYPE(ctx));
+						break;
+					}
+				}
+			}
 			temp_buf = krealloc(buf->data, max_size +
 						APF_DIAG_PADDING,
 					    GFP_KERNEL);
 			if (!temp_buf)
 				return -ENOMEM;
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Reallocated data buffer: %pK with size: %d\n",
+			temp_buf, max_size);
 			buf->data = temp_buf;
 			buf->len = max_size;
 		}
@@ -215,19 +238,38 @@
 	return buf->len;
 }
 
+/*
+ * diag_md_get_peripheral(int ctxt)
+ *
+ * Context(ctxt) contains peripheral, channel type, buffer num and diag_id
+ * The function decodes the ctxt, checks for the active user pd session
+ * using diag_id and returns peripheral if not active or the PD if active.
+ *
+ */
 int diag_md_get_peripheral(int ctxt)
 {
-	int pd = 0, i = 0;
+	uint8_t diag_id = 0, i = 0, pd = 0;
 	int type = 0, peripheral = -EINVAL;
+	int index = 0;
 	struct diagfwd_info *fwd_info = NULL;
 
 	peripheral = GET_BUF_PERIPHERAL(ctxt);
-	if (peripheral < 0 || peripheral > NUM_PERIPHERALS)
+
+	/* Check for peripheral value within bounds
+	 * of peripherals and UPD combined.
+	 */
+	if (peripheral < 0 || peripheral > NUM_MD_SESSIONS)
 		return -EINVAL;
 
 	if (peripheral == APPS_DATA)
 		return peripheral;
 
+	/* With peripheral value bound checked
+	 * return user pd value.
+	 */
+	if (peripheral > NUM_PERIPHERALS)
+		return peripheral;
+
 	type = GET_BUF_TYPE(ctxt);
 	if (type < 0 || type >= NUM_TYPES)
 		return -EINVAL;
@@ -236,22 +278,22 @@
 	if (!fwd_info)
 		return -EINVAL;
 
-	pd = GET_PD_CTXT(ctxt);
+	diag_id = GET_PD_CTXT(ctxt);
 
-	if (driver->num_pd_session) {
-		if (pd == fwd_info->diagid_root) {
-			if (peripheral > NUM_PERIPHERALS)
+	if (driver->num_pd_session &&
+		driver->feature[peripheral].untag_header) {
+		if (diag_id == fwd_info->root_diag_id.diagid_val) {
+			if (peripheral != fwd_info->root_diag_id.pd)
 				peripheral = -EINVAL;
 		} else {
 			for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
-				if (pd == fwd_info->diagid_user[i]) {
-					switch (peripheral) {
-					case PERIPHERAL_MODEM:
-					if (driver->pd_logging_mode[0])
-						peripheral = UPD_WLAN;
-					break;
-					default:
-						peripheral = -EINVAL;
+				if (diag_id ==
+					fwd_info->upd_diag_id[i].diagid_val) {
+					pd = fwd_info->upd_diag_id[i].pd;
+					index = pd - UPD_WLAN;
+					if ((index >= 0 && index < NUM_UPD) &&
+					driver->pd_logging_mode[index]) {
+						peripheral = pd;
 						break;
 					}
 				}
@@ -367,12 +409,28 @@
 	mutex_unlock(&fwd_info->data_mutex);
 	mutex_unlock(&driver->hdlc_disable_mutex);
 	if (buf) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Marking buffer as free p: %d, t: %d, buf_num: %d\n",
+			fwd_info->peripheral, fwd_info->type,
+			GET_BUF_NUM(buf->ctxt));
 		diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
 				   GET_BUF_NUM(buf->ctxt));
 	}
 	diagfwd_queue_read(fwd_info);
 }
 
+/*
+ * diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
+ *			   unsigned char *buf, int len)
+ *
+ * Data received from the peripheral can contain data from core and user PD
+ * The function segregates the data depending on the diag_id in the header
+ * of the packet chunk and copies to PD specific buffers.
+ * Sets the context for the buffers using diag_id and process it later for
+ * splitting the stream based on active PD logging.
+ *
+ */
+
 static void diagfwd_data_read_untag_done(struct diagfwd_info *fwd_info,
 				   unsigned char *buf, int len)
 {
@@ -448,8 +506,10 @@
 				*(uint16_t *) (temp_buf_main + 2);
 			if (packet_len > PERIPHERAL_BUF_SZ)
 				goto end;
-			if ((*temp_buf_main) == fwd_info->diagid_root) {
-				ctxt_cpd = fwd_info->diagid_root;
+			if ((*temp_buf_main) ==
+				fwd_info->root_diag_id.diagid_val) {
+				ctxt_cpd =
+					fwd_info->root_diag_id.diagid_val;
 				len_cpd += packet_len;
 				if (temp_buf_cpd) {
 					memcpy(temp_buf_cpd,
@@ -457,33 +517,46 @@
 					temp_buf_cpd += packet_len;
 				}
 			} else {
-				for (i = 0; i <= (fwd_info->num_pd - 2); i++)
-					if ((*temp_buf_main) ==
-						fwd_info->diagid_user[i])
-						break;
-				ctxt_upd[i] = fwd_info->diagid_user[i];
-				if (temp_buf_upd[i]) {
-					memcpy(temp_buf_upd[i],
-					(temp_buf_main + 4), packet_len);
-					temp_buf_upd[i] += packet_len;
+				for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
+				if ((*temp_buf_main) ==
+					fwd_info->upd_diag_id[i].diagid_val) {
+					ctxt_upd[i] =
+					fwd_info->upd_diag_id[i].diagid_val;
+					if (temp_buf_upd[i]) {
+						memcpy(temp_buf_upd[i],
+							(temp_buf_main + 4),
+							packet_len);
+						temp_buf_upd[i] += packet_len;
+					}
+					len_upd[i] += packet_len;
+					}
 				}
-				len_upd[i] += packet_len;
 			}
 			len = len - 4;
 			temp_buf_main += (packet_len + 4);
 			processed += packet_len;
 		}
+
+		if (flag_buf_1) {
+			fwd_info->cpd_len_1 = len_cpd;
+			for (i = 0; i <= (fwd_info->num_pd - 2); i++)
+				if (fwd_info->type == TYPE_DATA)
+					fwd_info->upd_len[i][0] = len_upd[i];
+		} else if (flag_buf_2) {
+			fwd_info->cpd_len_2 = len_cpd;
+			for (i = 0; i <= (fwd_info->num_pd - 2); i++)
+				if (fwd_info->type == TYPE_DATA)
+					fwd_info->upd_len[i][1] = len_upd[i];
+		}
+
 		for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
 			if (fwd_info->type == TYPE_DATA && len_upd[i]) {
-				if (flag_buf_1) {
-					fwd_info->upd_len[i][0] = len_upd[i];
+				if (flag_buf_1)
 					temp_fwdinfo_upd =
 						fwd_info->buf_upd[i][0];
-				} else {
-					fwd_info->upd_len[i][1] = len_upd[i];
+				else
 					temp_fwdinfo_upd =
 						fwd_info->buf_upd[i][1];
-				}
 				temp_fwdinfo_upd->ctxt &= 0x00FFFFFF;
 				temp_fwdinfo_upd->ctxt |=
 					(SET_PD_CTXT(ctxt_upd[i]));
@@ -497,14 +570,10 @@
 					fwd_info->upd_len[i][1] = 0;
 			}
 		}
+
 		if (len_cpd) {
-			if (flag_buf_1)
-				fwd_info->cpd_len_1 = len_cpd;
-			else
-				fwd_info->cpd_len_2 = len_cpd;
 			temp_fwdinfo_cpd->ctxt &= 0x00FFFFFF;
-			temp_fwdinfo_cpd->ctxt |=
-				(SET_PD_CTXT(ctxt_cpd));
+			temp_fwdinfo_cpd->ctxt |= (SET_PD_CTXT(ctxt_cpd));
 			diagfwd_data_process_done(fwd_info,
 				temp_fwdinfo_cpd, len_cpd);
 		} else {
@@ -520,6 +589,10 @@
 end:
 	diag_ws_release();
 	if (temp_fwdinfo_cpd) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Marking buffer as free p: %d, t: %d, buf_num: %d\n",
+			fwd_info->peripheral, fwd_info->type,
+			GET_BUF_NUM(temp_fwdinfo_cpd->ctxt));
 		diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
 				   GET_BUF_NUM(temp_fwdinfo_cpd->ctxt));
 	}
@@ -640,6 +713,10 @@
 	mutex_unlock(&fwd_info->data_mutex);
 	mutex_unlock(&driver->hdlc_disable_mutex);
 	if (temp_buf) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Marking buffer as free p: %d, t: %d, buf_num: %d\n",
+			fwd_info->peripheral, fwd_info->type,
+			GET_BUF_NUM(temp_buf->ctxt));
 		diagfwd_write_done(fwd_info->peripheral, fwd_info->type,
 				   GET_BUF_NUM(temp_buf->ctxt));
 	}
@@ -719,6 +796,16 @@
 		else if (fwd_info->buf_2 && fwd_info->buf_2->data_raw == buf)
 			atomic_set(&fwd_info->buf_2->in_busy, 0);
 	}
+	if (fwd_info->buf_1 && !atomic_read(&(fwd_info->buf_1->in_busy))) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
+	}
+	if (fwd_info->buf_2 && !atomic_read(&(fwd_info->buf_2->in_busy))) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Buffer 2 for core PD is marked free, p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
+	}
 }
 
 int diagfwd_peripheral_init(void)
@@ -753,12 +840,13 @@
 			fwd_info->cpd_len_1 = 0;
 			fwd_info->cpd_len_2 = 0;
 			fwd_info->num_pd = 0;
+			fwd_info->root_diag_id.diagid_val = 0;
 			mutex_init(&fwd_info->buf_mutex);
 			mutex_init(&fwd_info->data_mutex);
 			spin_lock_init(&fwd_info->write_buf_lock);
 
 			for (i = 0; i < MAX_PERIPHERAL_UPD; i++) {
-				fwd_info->diagid_user[i] = 0;
+				fwd_info->upd_diag_id[i].diagid_val = 0;
 				fwd_info->upd_len[i][0] = 0;
 				fwd_info->upd_len[i][1] = 0;
 				fwd_info->buf_upd[i][0] = NULL;
@@ -780,12 +868,13 @@
 			fwd_info->num_pd = 0;
 			fwd_info->cpd_len_1 = 0;
 			fwd_info->cpd_len_2 = 0;
+			fwd_info->root_diag_id.diagid_val = 0;
 			spin_lock_init(&fwd_info->write_buf_lock);
 			mutex_init(&fwd_info->buf_mutex);
 			mutex_init(&fwd_info->data_mutex);
 
 			for (i = 0; i < MAX_PERIPHERAL_UPD; i++) {
-				fwd_info->diagid_user[i] = 0;
+				fwd_info->upd_diag_id[i].diagid_val = 0;
 				fwd_info->upd_len[i][0] = 0;
 				fwd_info->upd_len[i][1] = 0;
 				fwd_info->buf_upd[i][0] = NULL;
@@ -823,6 +912,7 @@
 	uint8_t peripheral;
 	uint8_t type;
 	struct diagfwd_info *fwd_info = NULL;
+	int transport = 0;
 
 	diag_socket_exit();
 
@@ -844,7 +934,10 @@
 		driver->diagfwd_dci_cmd[peripheral] = NULL;
 	}
 
-	kfree(early_init_info);
+	for (transport = 0; transport < NUM_TRANSPORT; transport++) {
+		kfree(early_init_info[transport]);
+		early_init_info[transport] = NULL;
+	}
 }
 
 int diagfwd_cntl_register(uint8_t transport, uint8_t peripheral, void *ctxt,
@@ -1058,8 +1151,8 @@
 		return -ENODEV;
 
 	if (type == TYPE_CMD) {
-		if (driver->feature[peripheral].untag_header)
-			if (!fwd_info->diagid_root ||
+		if (driver->feature[peripheral].diag_id_support)
+			if (!fwd_info->root_diag_id.diagid_val ||
 				(!driver->diag_id_sent[peripheral])) {
 			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
 				 "diag: diag_id is not assigned yet\n");
@@ -1110,10 +1203,28 @@
 	if (!fwd_info->inited)
 		return;
 
-	if (fwd_info->buf_1)
-		atomic_set(&fwd_info->buf_1->in_busy, 0);
-	if (fwd_info->buf_2)
-		atomic_set(&fwd_info->buf_2->in_busy, 0);
+	/*
+	 * Logging mode here is reflecting previous mode
+	 * status and will be updated to new mode later.
+	 *
+	 * Keeping the buffers busy for Memory Device Mode.
+	 */
+
+	if ((driver->logging_mode != DIAG_USB_MODE) ||
+		driver->usb_connected) {
+		if (fwd_info->buf_1) {
+			atomic_set(&fwd_info->buf_1->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
+		}
+		if (fwd_info->buf_2) {
+			atomic_set(&fwd_info->buf_2->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Buffer 2 for core PD is marked free, p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
+		}
+	}
 
 	if (fwd_info->p_ops && fwd_info->p_ops->open)
 		fwd_info->p_ops->open(fwd_info->ctxt);
@@ -1238,10 +1349,18 @@
 	if (fwd_info && fwd_info->c_ops && fwd_info->c_ops->close)
 		fwd_info->c_ops->close(fwd_info);
 
-	if (fwd_info->buf_1 && fwd_info->buf_1->data)
+	if (fwd_info->buf_1 && fwd_info->buf_1->data) {
 		atomic_set(&fwd_info->buf_1->in_busy, 0);
-	if (fwd_info->buf_2 && fwd_info->buf_2->data)
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Buffer 1 for core PD is marked free, p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
+	}
+	if (fwd_info->buf_2 && fwd_info->buf_2->data) {
 		atomic_set(&fwd_info->buf_2->in_busy, 0);
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Buffer 2 for core PD is marked free, p: %d, t: %d\n",
+				fwd_info->peripheral, fwd_info->type);
+	}
 
 	for (i = 0; i < NUM_WRITE_BUFFERS; i++) {
 		if (fwd_info->buf_ptr[i])
@@ -1267,6 +1386,9 @@
 	 * in_busy flags. No need to queue read in this case.
 	 */
 	if (len == 0) {
+		DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+		"Read Length is 0, resetting the diag buffers p: %d, t: %d\n",
+			fwd_info->peripheral, fwd_info->type);
 		diagfwd_reset_buffers(fwd_info, buf);
 		diag_ws_release();
 		return 0;
@@ -1279,9 +1401,9 @@
 	return 0;
 }
 
-void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt)
+void diagfwd_write_done(uint8_t peripheral, uint8_t type, int buf_num)
 {
-	int i = 0;
+	int i = 0, upd_valid_len = 0;
 	struct diagfwd_info *fwd_info = NULL;
 
 	if (peripheral >= NUM_PERIPHERALS || type >= NUM_TYPES)
@@ -1291,36 +1413,126 @@
 	if (!fwd_info)
 		return;
 
-	if (ctxt == 1 && fwd_info->buf_1) {
-		/* Buffer 1 for core PD is freed */
-		atomic_set(&fwd_info->buf_1->in_busy, 0);
+	if (buf_num == 1 && fwd_info->buf_1) {
+		/*
+		 * Core PD buffer data is processed and
+		 * length in the buffer is marked zero.
+		 *
+		 * Check if the user PD buffer contains any
+		 * data before freeing core PD buffer.
+		 */
 		fwd_info->cpd_len_1 = 0;
-	} else if (ctxt == 2 && fwd_info->buf_2) {
-		/* Buffer 2 for core PD is freed */
-		atomic_set(&fwd_info->buf_2->in_busy, 0);
-		fwd_info->cpd_len_2 = 0;
-	} else if (ctxt >= 3 && (ctxt % 2)) {
 		for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
-			if (fwd_info->buf_upd[i][0]) {
-				/* Buffer 1 for ith user PD is freed */
-			atomic_set(&fwd_info->buf_upd[i][0]->in_busy, 0);
-			fwd_info->upd_len[i][0] = 0;
+			if (fwd_info->upd_len[i][0]) {
+				upd_valid_len = 1;
+				break;
 			}
-		if (!fwd_info->cpd_len_1)
+		}
+		/*
+		 * Do not free the core pd buffer if valid data
+		 * is present in any user PD buffer.
+		 */
+		if (!upd_valid_len) {
 			atomic_set(&fwd_info->buf_1->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Buffer 1 for core PD is marked free, p: %d, t: %d, buf_num: %d\n",
+				fwd_info->peripheral, fwd_info->type, buf_num);
 		}
-	} else if (ctxt >= 4 && !(ctxt % 2)) {
+	} else if (buf_num == 2 && fwd_info->buf_2) {
+		/*
+		 * Core PD buffer data is processed and
+		 * length in the buffer is marked zero.
+		 *
+		 * Check if the user PD buffer contains any
+		 * data before freeing core PD buffer.
+		 */
+		fwd_info->cpd_len_2 = 0;
 		for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
-			if (fwd_info->buf_upd[i][1]) {
-				/* Buffer 2 for ith user PD is freed */
-			atomic_set(&fwd_info->buf_upd[i][0]->in_busy, 0);
-			fwd_info->upd_len[i][1] = 0;
+			if (fwd_info->upd_len[i][1]) {
+				upd_valid_len = 1;
+				break;
 			}
-		if (!fwd_info->cpd_len_2)
-			atomic_set(&fwd_info->buf_2->in_busy, 0);
 		}
+		/*
+		 * Do not free the core pd buffer if valid data
+		 * is present in any user PD buffer
+		 */
+		if (!upd_valid_len) {
+			atomic_set(&fwd_info->buf_2->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Buffer 2 for core PD is marked free, p: %d, t: %d, buf_num: %d\n",
+				fwd_info->peripheral, fwd_info->type, buf_num);
+		}
+	} else if (buf_num >= 3 && (buf_num % 2)) {
+		/*
+		 * Go through each User PD buffer, validate the
+		 * request for freeing the buffer by validating
+		 * the buffer number.
+		 *
+		 */
+		for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
+			if (fwd_info->buf_upd[i][0] &&
+				(buf_num == ((2 * i) + 3))) {
+				/* Buffer 1 for ith user PD is freed */
+				atomic_set(&fwd_info->buf_upd[i][0]->in_busy,
+					0);
+				fwd_info->upd_len[i][0] = 0;
+			}
+			/*
+			 * Check if there is any data in user PD buffer other
+			 * than buffer requested for freeing.
+			 *
+			 */
+			if (fwd_info->upd_len[i][0])
+				upd_valid_len = 1;
+		}
+		/*
+		 * Mark the core pd buffer free if there is no
+		 * data present in core PD buffer and other User PD buffer.
+		 *
+		 */
+		if (!upd_valid_len && !fwd_info->cpd_len_1) {
+			atomic_set(&fwd_info->buf_1->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+				"Buffer 1 for core PD is marked free, p: %d, t: %d, buf_num: %d\n",
+				fwd_info->peripheral, fwd_info->type, buf_num);
+		}
+	} else if (buf_num >= 4 && !(buf_num % 2)) {
+		/*
+		 * Go through each User PD buffer, validate the
+		 * request for freeing the buffer by validating
+		 * the buffer number.
+		 *
+		 */
+		for (i = 0; i <= (fwd_info->num_pd - 2); i++) {
+			if (fwd_info->buf_upd[i][1] &&
+				(buf_num == ((2 * i) + 4))) {
+				/* Buffer 2 for ith user PD is freed */
+				atomic_set(&fwd_info->buf_upd[i][1]->in_busy,
+					0);
+				fwd_info->upd_len[i][1] = 0;
+			}
+			/*
+			 * Check if there is any data in user PD buffer other
+			 * than buffer requested for freeing.
+			 *
+			 */
+			if (fwd_info->upd_len[i][1])
+				upd_valid_len = 1;
+		}
+		/*
+		 * Mark the core pd buffer free if there is no
+		 * data present in core PD buffer and other User PD buffer.
+		 *
+		 */
+		if (!upd_valid_len && !fwd_info->cpd_len_2) {
+			atomic_set(&fwd_info->buf_2->in_busy, 0);
+			DIAG_LOG(DIAG_DEBUG_PERIPHERALS,
+			"Buffer 2 for core PD is marked free, p: %d, t: %d, buf_num: %d\n",
+			fwd_info->peripheral, fwd_info->type, buf_num);
+			}
 	} else
-		pr_err("diag: In %s, invalid ctxt %d\n", __func__, ctxt);
+		pr_err("diag: In %s, invalid buf_num %d\n", __func__, buf_num);
 
 	diagfwd_queue_read(fwd_info);
 }
diff --git a/drivers/char/diag/diagfwd_peripheral.h b/drivers/char/diag/diagfwd_peripheral.h
index b16670e..6ddce32 100644
--- a/drivers/char/diag/diagfwd_peripheral.h
+++ b/drivers/char/diag/diagfwd_peripheral.h
@@ -62,6 +62,12 @@
 	void (*queue_read)(void *ctxt);
 };
 
+struct diag_id_info {
+	uint8_t diagid_val;
+	uint8_t pd;
+	char *reg_str;
+};
+
 struct diagfwd_info {
 	uint8_t peripheral;
 	uint8_t type;
@@ -69,8 +75,6 @@
 	uint8_t inited;
 	uint8_t ch_open;
 	uint8_t num_pd;
-	uint8_t diagid_root;
-	uint8_t diagid_user[MAX_PERIPHERAL_UPD];
 	int cpd_len_1;
 	int cpd_len_2;
 	int upd_len[MAX_PERIPHERAL_UPD][2];
@@ -81,6 +85,8 @@
 	struct mutex buf_mutex;
 	struct mutex data_mutex;
 	void *ctxt;
+	struct diag_id_info root_diag_id;
+	struct diag_id_info upd_diag_id[MAX_PERIPHERAL_UPD];
 	struct diagfwd_buf_t *buf_1;
 	struct diagfwd_buf_t *buf_2;
 	struct diagfwd_buf_t *buf_upd[MAX_PERIPHERAL_UPD][2];
@@ -113,7 +119,7 @@
 void diagfwd_deregister(uint8_t peripheral, uint8_t type, void *ctxt);
 
 int diagfwd_write(uint8_t peripheral, uint8_t type, void *buf, int len);
-void diagfwd_write_done(uint8_t peripheral, uint8_t type, int ctxt);
+void diagfwd_write_done(uint8_t peripheral, uint8_t type, int buf_num);
 void diagfwd_buffers_init(struct diagfwd_info *fwd_info);
 
 /*
diff --git a/drivers/char/hw_random/msm_rng.c b/drivers/char/hw_random/msm_rng.c
index 7641a6a..fdcef1d 100644
--- a/drivers/char/hw_random/msm_rng.c
+++ b/drivers/char/hw_random/msm_rng.c
@@ -53,6 +53,9 @@
 #define MAX_HW_FIFO_DEPTH 16                     /* FIFO is 16 words deep */
 #define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide  */
 
+#define RETRY_MAX_CNT		5	/* max retry times to read register */
+#define RETRY_DELAY_INTERVAL	440	/* retry delay interval in us */
+
 struct msm_rng_device {
 	struct platform_device *pdev;
 	void __iomem *base;
@@ -96,7 +99,7 @@
 	struct platform_device *pdev;
 	void __iomem *base;
 	size_t currsize = 0;
-	u32 val;
+	u32 val = 0;
 	u32 *retdata = data;
 	int ret;
 	int failed = 0;
@@ -113,41 +116,41 @@
 	if (msm_rng_dev->qrng_perf_client) {
 		ret = msm_bus_scale_client_update_request(
 				msm_rng_dev->qrng_perf_client, 1);
-		if (ret)
+		if (ret) {
 			pr_err("bus_scale_client_update_req failed!\n");
+			goto bus_err;
+		}
 	}
 	/* enable PRNG clock */
 	ret = clk_prepare_enable(msm_rng_dev->prng_clk);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to enable clock in callback\n");
+		pr_err("failed to enable prng clock\n");
 		goto err;
 	}
 	/* read random data from h/w */
 	do {
 		/* check status bit if data is available */
-		while (!(readl_relaxed(base + PRNG_STATUS_OFFSET)
+		if (!(readl_relaxed(base + PRNG_STATUS_OFFSET)
 				& 0x00000001)) {
-			if (failed == 10) {
-				pr_err("Data not available after retry\n");
+			if (failed++ == RETRY_MAX_CNT) {
+				if (currsize == 0)
+					pr_err("Data not available\n");
 				break;
 			}
-			pr_err("msm_rng:Data not available!\n");
-			msleep_interruptible(10);
-			failed++;
+			udelay(RETRY_DELAY_INTERVAL);
+		} else {
+
+			/* read FIFO */
+			val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
+
+			/* write data back to callers pointer */
+			*(retdata++) = val;
+			currsize += 4;
+			/* make sure we stay on 32bit boundary */
+			if ((max - currsize) < 4)
+				break;
 		}
 
-		/* read FIFO */
-		val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
-		if (!val)
-			break;	/* no data to read so just bail */
-
-		/* write data back to callers pointer */
-		*(retdata++) = val;
-		currsize += 4;
-		/* make sure we stay on 32bit boundary */
-		if ((max - currsize) < 4)
-			break;
-
 	} while (currsize < max);
 
 	/* vote to turn off clock */
@@ -159,6 +162,7 @@
 		if (ret)
 			pr_err("bus_scale_client_update_req failed!\n");
 	}
+bus_err:
 	mutex_unlock(&msm_rng_dev->rng_lock);
 
 	val = 0L;
diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c
index 411310d..02d3bcd 100644
--- a/drivers/clk/axs10x/i2s_pll_clock.c
+++ b/drivers/clk/axs10x/i2s_pll_clock.c
@@ -182,6 +182,7 @@
 	if (IS_ERR(pll_clk->base))
 		return PTR_ERR(pll_clk->base);
 
+	memset(&init, 0, sizeof(init));
 	clk_name = node->name;
 	init.name = clk_name;
 	init.ops = &i2s_pll_ops;
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 5638333..4f2fb77 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2712,9 +2712,10 @@
 	struct clk_core *core;
 	int cnt = 0;
 
-	clock_debug_output(s, 0, "Enabled clocks:\n");
+	if (!mutex_trylock(&clk_debug_lock))
+		return;
 
-	mutex_lock(&clk_debug_lock);
+	clock_debug_output(s, 0, "Enabled clocks:\n");
 
 	hlist_for_each_entry(core, &clk_debug_list, debug_node)
 		cnt += clock_debug_print_clock(core, s);
@@ -3037,14 +3038,19 @@
 
 /*
  * Print the names of all enabled clocks and their parents if
- * debug_suspend is set from debugfs.
+ * debug_suspend is set from debugfs along with print_parent flag set to 1.
+ * Otherwise if print_parent set to 0, print only enabled clocks
+ *
  */
-void clock_debug_print_enabled(void)
+void clock_debug_print_enabled(bool print_parent)
 {
 	if (likely(!debug_suspend))
 		return;
 
-	clock_debug_print_enabled_debug_suspend(NULL);
+	if (print_parent)
+		clock_debug_print_enabled_clocks(NULL);
+	else
+		clock_debug_print_enabled_debug_suspend(NULL);
 }
 EXPORT_SYMBOL_GPL(clock_debug_print_enabled);
 
diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h
index b52aa25..a7d0981 100644
--- a/drivers/clk/clk.h
+++ b/drivers/clk/clk.h
@@ -23,7 +23,7 @@
 void __clk_free_clk(struct clk *clk);
 
 /* Debugfs API to print the enabled clocks */
-void clock_debug_print_enabled(void);
+void clock_debug_print_enabled(bool print_parent);
 void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f);
 
 #else
diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c
index 02023ba..962e0c5 100644
--- a/drivers/clk/mvebu/ap806-system-controller.c
+++ b/drivers/clk/mvebu/ap806-system-controller.c
@@ -55,21 +55,39 @@
 
 	freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
 	switch (freq_mode) {
-	case 0x0 ... 0x5:
+	case 0x0:
+	case 0x1:
 		cpuclk_freq = 2000;
 		break;
-	case 0x6 ... 0xB:
+	case 0x6:
+	case 0x7:
 		cpuclk_freq = 1800;
 		break;
-	case 0xC ... 0x11:
+	case 0x4:
+	case 0xB:
+	case 0xD:
 		cpuclk_freq = 1600;
 		break;
-	case 0x12 ... 0x16:
+	case 0x1a:
 		cpuclk_freq = 1400;
 		break;
-	case 0x17 ... 0x19:
+	case 0x14:
+	case 0x17:
 		cpuclk_freq = 1300;
 		break;
+	case 0x19:
+		cpuclk_freq = 1200;
+		break;
+	case 0x13:
+	case 0x1d:
+		cpuclk_freq = 1000;
+		break;
+	case 0x1c:
+		cpuclk_freq = 800;
+		break;
+	case 0x1b:
+		cpuclk_freq = 600;
+		break;
 	default:
 		dev_err(&pdev->dev, "invalid SAR value\n");
 		return -EINVAL;
diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig
index d47b66e..87d067a 100644
--- a/drivers/clk/qcom/Kconfig
+++ b/drivers/clk/qcom/Kconfig
@@ -235,4 +235,21 @@
 	subsystems via QMP mailboxes.
 	Say Y to support the clocks managed by AOP on platforms such as sdm845.
 
+config MDM_GCC_SDXPOORWILLS
+	tristate "SDXPOORWILLS Global Clock Controller"
+	depends on COMMON_CLK_QCOM
+	help
+	  Support for the global clock controller on sdxpoorwills devices.
+	  Say Y if you want to use peripheral devices such as UART, SPI,
+	  i2c, USB, SD/eMMC, etc.
+
+config MDM_CLOCK_CPU_SDXPOORWILLS
+	tristate "SDXPOORWILLS CPU Clock Controller"
+	depends on COMMON_CLK_QCOM
+	help
+	Support for the cpu clock controller on sdxpoorwills
+	based devices.
+	Say Y if you want to support CPU clock scaling using
+	CPUfreq drivers for dyanmic power management.
+
 source "drivers/clk/qcom/mdss/Kconfig"
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 6a8c43b..8cb46a7 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -22,7 +22,9 @@
 obj-$(CONFIG_IPQ_GCC_4019) += gcc-ipq4019.o
 obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o
 obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o
+obj-$(CONFIG_MDM_CLOCK_CPU_SDXPOORWILLS) += clk-cpu-a7.o
 obj-$(CONFIG_MDM_GCC_9615) += gcc-mdm9615.o
+obj-$(CONFIG_MDM_GCC_SDXPOORWILLS) += gcc-sdxpoorwills.o
 obj-$(CONFIG_MDM_LCC_9615) += lcc-mdm9615.o
 obj-$(CONFIG_MSM_CAMCC_SDM845) += camcc-sdm845.o
 obj-$(CONFIG_MSM_CLK_AOP_QMP) += clk-aop-qmp.o
diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c
index 1984d4a..836c25c 100644
--- a/drivers/clk/qcom/camcc-sdm845.c
+++ b/drivers/clk/qcom/camcc-sdm845.c
@@ -442,7 +442,7 @@
 	.mnd_width = 0,
 	.hid_width = 5,
 	.parent_map = cam_cc_parent_map_0,
-	.freq_tbl = NULL,
+	.freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src,
 	.clkr.hw.init = &(struct clk_init_data){
 		.name = "cam_cc_csi3phytimer_clk_src",
 		.parent_names = cam_cc_parent_names_0,
@@ -1959,6 +1959,7 @@
 static const struct of_device_id cam_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,cam_cc-sdm845" },
 	{ .compatible = "qcom,cam_cc-sdm845-v2" },
+	{ .compatible = "qcom,cam_cc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, cam_cc_sdm845_match_table);
@@ -1970,8 +1971,90 @@
 	cam_cc_sdm845_clocks[CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr;
 	cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK_SRC] =
 		&cam_cc_csi3phytimer_clk_src.clkr;
+	cam_cc_bps_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_bps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_cci_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_cci_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_cphy_rx_clk_src.freq_tbl = ftbl_cam_cc_cphy_rx_clk_src_sdm845_v2;
+	cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 384000000;
+	cam_cc_csi0phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_csi0phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_csi1phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_csi1phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_csi2phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_csi2phytimer_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_fast_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_fast_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_fd_core_clk_src.freq_tbl = ftbl_cam_cc_fd_core_clk_src_sdm845_v2;
+	cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_icp_clk_src.freq_tbl = ftbl_cam_cc_icp_clk_src_sdm845_v2;
+	cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_icp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 600000000;
+	cam_cc_ife_0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_0_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
+		384000000;
+	cam_cc_ife_1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_1_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
+		384000000;
+	cam_cc_ife_lite_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_lite_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ife_lite_csid_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
+		384000000;
+	cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ipe_0_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000;
+	cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_ipe_1_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 600000000;
+	cam_cc_jpeg_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_jpeg_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_lrme_clk_src.freq_tbl = ftbl_cam_cc_lrme_clk_src_sdm845_v2;
+	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 269333333;
+	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 320000000;
+	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 400000000;
+	cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_mclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714;
+	cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_mclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714;
+	cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_mclk2_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714;
+	cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_mclk3_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 34285714;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_MIN] = 0;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 0;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 80000000;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		80000000;
+}
+
+static void cam_cc_sdm845_fixup_sdm670(void)
+{
+	cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK] =
+		&cam_cc_csi3phytimer_clk.clkr;
+	cam_cc_sdm845_clocks[CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr;
+	cam_cc_sdm845_clocks[CAM_CC_CSI3PHYTIMER_CLK_SRC] =
+		&cam_cc_csi3phytimer_clk_src.clkr;
 	cam_cc_cphy_rx_clk_src.freq_tbl = ftbl_cam_cc_cphy_rx_clk_src_sdm845_v2;
 	cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 384000000;
+	cam_cc_cphy_rx_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 384000000;
 	cam_cc_fd_core_clk_src.freq_tbl = ftbl_cam_cc_fd_core_clk_src_sdm845_v2;
 	cam_cc_fd_core_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 384000000;
 	cam_cc_icp_clk_src.freq_tbl = ftbl_cam_cc_icp_clk_src_sdm845_v2;
@@ -1984,6 +2067,9 @@
 	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] = 320000000;
 	cam_cc_lrme_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] = 400000000;
 	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 80000000;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 80000000;
+	cam_cc_slow_ahb_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		80000000;
 }
 
 static int cam_cc_sdm845_fixup(struct platform_device *pdev)
@@ -1997,6 +2083,8 @@
 
 	if (!strcmp(compat, "qcom,cam_cc-sdm845-v2"))
 		cam_cc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,cam_cc-sdm670"))
+		cam_cc_sdm845_fixup_sdm670();
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index e7d3ee4..bf9b99d 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -22,6 +22,8 @@
 #include "clk-alpha-pll.h"
 
 #define PLL_MODE		0x00
+#define PLL_STANDBY		0x0
+#define PLL_RUN			0x1
 # define PLL_OUTCTRL		BIT(0)
 # define PLL_BYPASSNL		BIT(1)
 # define PLL_RESET_N		BIT(2)
@@ -51,25 +53,40 @@
 #define PLL_TEST_CTL		0x1c
 #define PLL_TEST_CTL_U		0x20
 #define PLL_STATUS		0x24
+#define PLL_UPDATE		BIT(22)
+#define PLL_ACK_LATCH		BIT(29)
+#define PLL_CALIBRATION_MASK	(0x7<<3)
+#define PLL_CALIBRATION_CONTROL	2
+#define PLL_HW_UPDATE_LOGIC_BYPASS	BIT(23)
+#define ALPHA_16_BIT_PLL_RATE_MARGIN	500
 
 /*
  * Even though 40 bits are present, use only 32 for ease of calculation.
  */
 #define ALPHA_REG_BITWIDTH	40
 #define ALPHA_BITWIDTH		32
-#define FABIA_BITWIDTH		16
+#define SUPPORTS_16BIT_ALPHA	16
 
 #define FABIA_USER_CTL_LO	0xc
 #define FABIA_USER_CTL_HI	0x10
 #define FABIA_FRAC_VAL		0x38
 #define FABIA_OPMODE		0x2c
-#define FABIA_PLL_STANDBY	0x0
-#define FABIA_PLL_RUN		0x1
 #define FABIA_PLL_OUT_MASK	0x7
-#define FABIA_PLL_RATE_MARGIN	500
 #define FABIA_PLL_ACK_LATCH	BIT(29)
 #define FABIA_PLL_UPDATE	BIT(22)
-#define FABIA_PLL_HW_UPDATE_LOGIC_BYPASS	BIT(23)
+
+#define TRION_PLL_CAL_VAL	0x44
+#define TRION_PLL_CAL_L_VAL	0x8
+#define TRION_PLL_USER_CTL	0xc
+#define TRION_PLL_USER_CTL_U	0x10
+#define TRION_PLL_USER_CTL_U1	0x14
+#define TRION_PLL_CONFIG_CTL_U	0x1c
+#define TRION_PLL_CONFIG_CTL_U1	0x20
+#define TRION_PLL_OPMODE	0x38
+#define TRION_PLL_ALPHA_VAL	0x40
+
+#define TRION_PLL_OUT_MASK	0x7
+#define TRION_PLL_ENABLE_STATE_READ	BIT(4)
 
 #define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \
 					   struct clk_alpha_pll, clkr)
@@ -121,6 +138,10 @@
 	return wait_for_pll(pll, mask, 0, "offline");
 }
 
+static int wait_for_pll_latch_ack(struct clk_alpha_pll *pll, u32 mask)
+{
+	return wait_for_pll(pll, mask, 0, "latch_ack");
+}
 
 /* alpha pll with hwfsm support */
 
@@ -294,8 +315,8 @@
 {
 	int alpha_bw = ALPHA_BITWIDTH;
 
-	if (pll->type == FABIA_PLL)
-		alpha_bw = FABIA_BITWIDTH;
+	if (pll->type == FABIA_PLL || pll->type == TRION_PLL)
+		alpha_bw = SUPPORTS_16BIT_ALPHA;
 
 	return (prate * l) + ((prate * a) >> alpha_bw);
 }
@@ -326,9 +347,9 @@
 		return rate;
 	}
 
-	/* Fabia PLLs only have 16 bits to program the fractional divider */
-	if (pll->type == FABIA_PLL)
-		alpha_bw = FABIA_BITWIDTH;
+	/* Some PLLs only have 16 bits to program the fractional divider */
+	if (pll->type == FABIA_PLL || pll->type == TRION_PLL)
+		alpha_bw = SUPPORTS_16BIT_ALPHA;
 
 	/* Upper ALPHA_BITWIDTH bits of Alpha */
 	quotient = remainder << alpha_bw;
@@ -415,7 +436,8 @@
 	unsigned long min_freq, max_freq;
 
 	rate = alpha_pll_round_rate(pll, rate, *prate, &l, &a);
-	if (pll->type == FABIA_PLL || alpha_pll_find_vco(pll, rate))
+	if (pll->type == FABIA_PLL || pll->type == TRION_PLL ||
+		alpha_pll_find_vco(pll, rate))
 		return rate;
 
 	min_freq = pll->vco_table[0].min_freq;
@@ -459,6 +481,37 @@
 	}
 }
 
+static int clk_fabia_pll_latch_input(struct clk_alpha_pll *pll,
+					struct regmap *regmap)
+{
+	u32 regval;
+	int ret = 0;
+
+	/* Latch the PLL input */
+	ret = regmap_update_bits(regmap, pll->offset + PLL_MODE,
+			   FABIA_PLL_UPDATE, FABIA_PLL_UPDATE);
+	if (ret)
+		return ret;
+
+	/* Wait for 2 reference cycles before checking the ACK bit. */
+	udelay(1);
+	regmap_read(regmap, pll->offset + PLL_MODE, &regval);
+	if (!(regval & FABIA_PLL_ACK_LATCH)) {
+		WARN(1, "clk: PLL latch failed. Output may be unstable!\n");
+		return -EINVAL;
+	}
+
+	/* Return the latch input to 0 */
+	ret = regmap_update_bits(regmap, pll->offset + PLL_MODE,
+			   FABIA_PLL_UPDATE, 0);
+	if (ret)
+		return ret;
+
+	/* Wait for PLL output to stabilize */
+	udelay(100);
+	return ret;
+}
+
 void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 				const struct pll_config *config)
 {
@@ -471,6 +524,7 @@
 	if (config->frac)
 		regmap_write(regmap, pll->offset + FABIA_FRAC_VAL,
 						config->frac);
+
 	if (config->config_ctl_val)
 		regmap_write(regmap, pll->offset + PLL_CONFIG_CTL,
 				config->config_ctl_val);
@@ -482,9 +536,17 @@
 					mask, val);
 	}
 
+	/*
+	 * If the PLL has already been initialized, it would now be in a STANDBY
+	 * state. Any new updates to the PLL frequency will require setting the
+	 * PLL_UPDATE bit.
+	 */
+	if (pll->inited)
+		clk_fabia_pll_latch_input(pll, regmap);
+
 	regmap_update_bits(regmap, pll->offset + PLL_MODE,
-				 FABIA_PLL_HW_UPDATE_LOGIC_BYPASS,
-				 FABIA_PLL_HW_UPDATE_LOGIC_BYPASS);
+				 PLL_HW_UPDATE_LOGIC_BYPASS,
+				 PLL_HW_UPDATE_LOGIC_BYPASS);
 
 	regmap_update_bits(regmap, pll->offset + PLL_MODE,
 			   PLL_RESET_N, PLL_RESET_N);
@@ -520,7 +582,7 @@
 		return ret;
 
 	/* Set operation mode to STANDBY */
-	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, FABIA_PLL_STANDBY);
+	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, PLL_STANDBY);
 
 	/* PLL should be in STANDBY mode before continuing */
 	mb();
@@ -532,7 +594,7 @@
 		return ret;
 
 	/* Set operation mode to RUN */
-	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, FABIA_PLL_RUN);
+	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, PLL_RUN);
 
 	ret = wait_for_pll_enable(pll, PLL_LOCK_DET);
 	if (ret)
@@ -584,7 +646,7 @@
 		return;
 
 	/* Place the PLL mode in STANDBY */
-	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, FABIA_PLL_STANDBY);
+	regmap_write(pll->clkr.regmap, off + FABIA_OPMODE, PLL_STANDBY);
 }
 
 static unsigned long
@@ -619,7 +681,7 @@
 	 * Due to limited number of bits for fractional rate programming, the
 	 * rounded up rate could be marginally higher than the requested rate.
 	 */
-	if (rrate > (rate + FABIA_PLL_RATE_MARGIN) || rrate < rate) {
+	if (rrate > (rate + ALPHA_16_BIT_PLL_RATE_MARGIN) || rrate < rate) {
 		pr_err("Call set rate on the PLL with rounded rates!\n");
 		return -EINVAL;
 	}
@@ -627,29 +689,8 @@
 	regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
 	regmap_write(pll->clkr.regmap, off + FABIA_FRAC_VAL, a);
 
-	/* Latch the PLL input */
-	ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
-			   FABIA_PLL_UPDATE, FABIA_PLL_UPDATE);
-	if (ret)
-		return ret;
-
-	/* Wait for 2 reference cycles before checking the ACK bit. */
-	udelay(1);
-	regmap_read(pll->clkr.regmap, off + PLL_MODE, &regval);
-	if (!(regval & FABIA_PLL_ACK_LATCH)) {
-		WARN(1, "clk: PLL latch failed. Output may be unstable!\n");
-		return -EINVAL;
-	}
-
-	/* Return the latch input to 0 */
-	ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
-			   FABIA_PLL_UPDATE, 0);
-	if (ret)
-		return ret;
-
-	/* Wait for PLL output to stabilize */
-	udelay(100);
-	return 0;
+	ret = clk_fabia_pll_latch_input(pll, pll->clkr.regmap);
+	return ret;
 }
 
 static void clk_fabia_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
@@ -860,3 +901,436 @@
 	.set_rate = clk_generic_pll_postdiv_set_rate,
 };
 EXPORT_SYMBOL_GPL(clk_generic_pll_postdiv_ops);
+
+static int trion_pll_is_enabled(struct clk_alpha_pll *pll,
+					struct regmap *regmap)
+{
+	u32 mode_val, opmode_val, off = pll->offset;
+	int ret;
+
+	ret = regmap_read(regmap, off + PLL_MODE, &mode_val);
+	ret |= regmap_read(regmap, off + TRION_PLL_OPMODE, &opmode_val);
+	if (ret)
+		return 0;
+
+	return ((opmode_val & PLL_RUN) && (mode_val & PLL_OUTCTRL));
+}
+
+int clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
+				const struct pll_config *config)
+{
+	int ret = 0;
+
+	if (trion_pll_is_enabled(pll, regmap)) {
+		pr_debug("PLL is already enabled. Skipping configuration.\n");
+
+		/*
+		 * Set the PLL_HW_UPDATE_LOGIC_BYPASS bit to latch the input
+		 * before continuing.
+		 */
+		regmap_update_bits(regmap, pll->offset + PLL_MODE,
+				 PLL_HW_UPDATE_LOGIC_BYPASS,
+				 PLL_HW_UPDATE_LOGIC_BYPASS);
+
+		pll->inited = true;
+		return ret;
+	}
+
+	/*
+	 * Disable the PLL if it's already been initialized. Not doing so might
+	 * lead to the PLL running with the old frequency configuration.
+	 */
+	if (pll->inited) {
+		ret = regmap_update_bits(regmap, pll->offset + PLL_MODE,
+							PLL_RESET_N, 0);
+		if (ret)
+			return ret;
+	}
+
+	if (config->l)
+		regmap_write(regmap, pll->offset + PLL_L_VAL,
+							config->l);
+
+	regmap_write(regmap, pll->offset + TRION_PLL_CAL_L_VAL,
+						TRION_PLL_CAL_VAL);
+
+	if (config->frac)
+		regmap_write(regmap, pll->offset + TRION_PLL_ALPHA_VAL,
+						config->frac);
+
+	if (config->config_ctl_val)
+		regmap_write(regmap, pll->offset + PLL_CONFIG_CTL,
+				config->config_ctl_val);
+
+	if (config->config_ctl_hi_val)
+		regmap_write(regmap, pll->offset + TRION_PLL_CONFIG_CTL_U,
+				config->config_ctl_hi_val);
+
+	if (config->config_ctl_hi1_val)
+		regmap_write(regmap, pll->offset + TRION_PLL_CONFIG_CTL_U1,
+				config->config_ctl_hi1_val);
+
+	if (config->post_div_mask)
+		regmap_update_bits(regmap, pll->offset + TRION_PLL_USER_CTL,
+				config->post_div_mask, config->post_div_val);
+
+	/* Disable state read */
+	regmap_update_bits(regmap, pll->offset + TRION_PLL_USER_CTL_U,
+				TRION_PLL_ENABLE_STATE_READ, 0);
+
+	regmap_update_bits(regmap, pll->offset + PLL_MODE,
+				 PLL_HW_UPDATE_LOGIC_BYPASS,
+				 PLL_HW_UPDATE_LOGIC_BYPASS);
+
+	/* Set calibration control to Automatic */
+	regmap_update_bits(regmap, pll->offset + TRION_PLL_USER_CTL_U,
+			PLL_CALIBRATION_MASK, PLL_CALIBRATION_CONTROL);
+
+	/* Disable PLL output */
+	ret = regmap_update_bits(regmap, pll->offset + PLL_MODE,
+							PLL_OUTCTRL, 0);
+	if (ret)
+		return ret;
+
+	/* Set operation mode to OFF */
+	regmap_write(regmap, pll->offset + TRION_PLL_OPMODE, PLL_STANDBY);
+
+	/* PLL should be in OFF mode before continuing */
+	wmb();
+
+	/* Place the PLL in STANDBY mode */
+	ret = regmap_update_bits(regmap, pll->offset + PLL_MODE,
+						PLL_RESET_N, PLL_RESET_N);
+	if (ret)
+		return ret;
+
+	pll->inited = true;
+
+	return ret;
+}
+
+static int clk_alpha_pll_latch_l_val(struct clk_alpha_pll *pll)
+{
+	int ret;
+
+	/* Latch the input to the PLL */
+	ret = regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
+			PLL_UPDATE, PLL_UPDATE);
+	if (ret)
+		return ret;
+
+	/* Wait for 2 reference cycle before checking ACK bit */
+	udelay(1);
+
+	ret = wait_for_pll_latch_ack(pll, PLL_ACK_LATCH);
+	if (ret)
+		return ret;
+
+	/* Return latch input to 0 */
+	ret = regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_MODE,
+		PLL_UPDATE, (u32)~PLL_UPDATE);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int clk_trion_pll_enable(struct clk_hw *hw)
+{
+	int ret = 0;
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	u32 val, off = pll->offset;
+
+	ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+	if (ret)
+		return ret;
+
+	/* If in FSM mode, just vote for it */
+	if (val & PLL_VOTE_FSM_ENA) {
+		ret = clk_enable_regmap(hw);
+		if (ret)
+			return ret;
+		return wait_for_pll_enable(pll, PLL_ACTIVE_FLAG);
+	}
+
+	if (unlikely(!pll->inited)) {
+		ret = clk_trion_pll_configure(pll, pll->clkr.regmap,
+						pll->config);
+		if (ret) {
+			pr_err("Failed to configure %s\n", clk_hw_get_name(hw));
+			return ret;
+		}
+	}
+
+	/* Skip If PLL is already running */
+	if (trion_pll_is_enabled(pll, pll->clkr.regmap))
+		return ret;
+
+	/* Set operation mode to RUN */
+	regmap_write(pll->clkr.regmap, off + TRION_PLL_OPMODE, PLL_RUN);
+
+	ret = wait_for_pll_enable(pll, PLL_LOCK_DET);
+	if (ret)
+		return ret;
+
+	/* Enable PLL main output */
+	ret = regmap_update_bits(pll->clkr.regmap, off + TRION_PLL_USER_CTL,
+				 TRION_PLL_OUT_MASK, TRION_PLL_OUT_MASK);
+	if (ret)
+		return ret;
+
+	/* Enable Global PLL outputs */
+	ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+				 PLL_OUTCTRL, PLL_OUTCTRL);
+	if (ret)
+		return ret;
+
+	/* Ensure that the write above goes through before returning. */
+	mb();
+	return ret;
+}
+
+static void clk_trion_pll_disable(struct clk_hw *hw)
+{
+	int ret;
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	u32 val, off = pll->offset;
+
+	ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+	if (ret)
+		return;
+
+	/* If in FSM mode, just unvote it */
+	if (val & PLL_VOTE_FSM_ENA) {
+		clk_disable_regmap(hw);
+		return;
+	}
+
+	/* Disable Global PLL outputs */
+	ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+							PLL_OUTCTRL, 0);
+	if (ret)
+		return;
+
+	/* Disable the main PLL output */
+	ret = regmap_update_bits(pll->clkr.regmap, off + TRION_PLL_USER_CTL,
+			TRION_PLL_OUT_MASK, 0);
+	if (ret)
+		return;
+
+	/* Place the PLL into STANDBY mode */
+	regmap_write(pll->clkr.regmap, off + TRION_PLL_OPMODE, PLL_STANDBY);
+
+	regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+				 PLL_RESET_N, PLL_RESET_N);
+}
+
+static unsigned long
+clk_trion_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	u32 l, frac = 0;
+	u64 prate = parent_rate;
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	u32 off = pll->offset;
+
+	regmap_read(pll->clkr.regmap, off + PLL_L_VAL, &l);
+	regmap_read(pll->clkr.regmap, off + TRION_PLL_ALPHA_VAL, &frac);
+
+	return alpha_pll_calc_rate(pll, prate, l, frac);
+}
+
+static int clk_trion_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long prate)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	unsigned long rrate;
+	bool is_enabled;
+	int ret;
+	u32 l, val, off = pll->offset;
+	u64 a;
+
+	rrate = alpha_pll_round_rate(pll, rate, prate, &l, &a);
+	/*
+	 * Due to limited number of bits for fractional rate programming, the
+	 * rounded up rate could be marginally higher than the requested rate.
+	 */
+	if (rrate > (rate + ALPHA_16_BIT_PLL_RATE_MARGIN) || rrate < rate) {
+		pr_err("Trion_pll: Call clk_set_rate with rounded rates!\n");
+		return -EINVAL;
+	}
+
+	is_enabled = clk_hw_is_enabled(hw);
+
+	if (is_enabled)
+		hw->init->ops->disable(hw);
+
+	regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
+	regmap_write(pll->clkr.regmap, off + TRION_PLL_ALPHA_VAL, a);
+
+	ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+	if (ret)
+		return ret;
+
+	/*
+	 * If PLL is in Standby or RUN mode then only latch the L value
+	 * Else PLL is in OFF mode and just configure L register - as per
+	 * HPG no need to latch input.
+	 */
+	if (val & PLL_RESET_N)
+		clk_alpha_pll_latch_l_val(pll);
+
+	if (is_enabled)
+		hw->init->ops->enable(hw);
+
+	/* Wait for PLL output to stabilize */
+	udelay(100);
+
+	return ret;
+}
+
+static int clk_trion_pll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+
+	return trion_pll_is_enabled(pll, pll->clkr.regmap);
+}
+
+static void clk_trion_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
+{
+	struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+	int size, i, val;
+
+	static struct clk_register_data data[] = {
+		{"PLL_MODE", 0x0},
+		{"PLL_L_VAL", 0x4},
+		{"PLL_USER_CTL", 0xc},
+		{"PLL_USER_CTL_U", 0x10},
+		{"PLL_USER_CTL_U1", 0x14},
+		{"PLL_CONFIG_CTL", 0x18},
+		{"PLL_CONFIG_CTL_U", 0x1c},
+		{"PLL_CONFIG_CTL_U1", 0x20},
+		{"PLL_OPMODE", 0x38},
+	};
+
+	static struct clk_register_data data1[] = {
+		{"APSS_PLL_VOTE", 0x0},
+	};
+
+	size = ARRAY_SIZE(data);
+
+	for (i = 0; i < size; i++) {
+		regmap_read(pll->clkr.regmap, pll->offset + data[i].offset,
+					&val);
+		seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val);
+	}
+
+	regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val);
+
+	if (val & PLL_VOTE_FSM_ENA) {
+		regmap_read(pll->clkr.regmap, pll->clkr.enable_reg +
+					data1[0].offset, &val);
+		seq_printf(f, "%20s: 0x%.8x\n", data1[0].name, val);
+	}
+}
+
+const struct clk_ops clk_trion_pll_ops = {
+	.enable = clk_trion_pll_enable,
+	.disable = clk_trion_pll_disable,
+	.recalc_rate = clk_trion_pll_recalc_rate,
+	.round_rate = clk_alpha_pll_round_rate,
+	.set_rate = clk_trion_pll_set_rate,
+	.is_enabled = clk_trion_pll_is_enabled,
+	.list_registers = clk_trion_pll_list_registers,
+};
+EXPORT_SYMBOL(clk_trion_pll_ops);
+
+const struct clk_ops clk_trion_fixed_pll_ops = {
+	.enable = clk_trion_pll_enable,
+	.disable = clk_trion_pll_disable,
+	.recalc_rate = clk_trion_pll_recalc_rate,
+	.round_rate = clk_alpha_pll_round_rate,
+	.is_enabled = clk_trion_pll_is_enabled,
+	.list_registers = clk_trion_pll_list_registers,
+};
+EXPORT_SYMBOL(clk_trion_fixed_pll_ops);
+
+static unsigned long clk_trion_pll_postdiv_recalc_rate(struct clk_hw *hw,
+				unsigned long parent_rate)
+{
+	struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+	u32 i, cal_div = 1, val;
+
+	if (!pll->post_div_table) {
+		pr_err("Missing the post_div_table for the PLL\n");
+		return -EINVAL;
+	}
+
+	regmap_read(pll->clkr.regmap, pll->offset + TRION_PLL_USER_CTL, &val);
+
+	val >>= pll->post_div_shift;
+	val &= PLL_POST_DIV_MASK;
+
+	for (i = 0; i < pll->num_post_div; i++) {
+		if (pll->post_div_table[i].val == val) {
+			cal_div = pll->post_div_table[i].div;
+			break;
+		}
+	}
+
+	return (parent_rate / cal_div);
+}
+
+static long clk_trion_pll_postdiv_round_rate(struct clk_hw *hw,
+				unsigned long rate, unsigned long *prate)
+{
+	struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+
+	if (!pll->post_div_table)
+		return -EINVAL;
+
+	return divider_round_rate(hw, rate, prate, pll->post_div_table,
+					pll->width, CLK_DIVIDER_ROUND_CLOSEST);
+}
+
+static int clk_trion_pll_postdiv_set_rate(struct clk_hw *hw,
+				unsigned long rate, unsigned long parent_rate)
+{
+	struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+	int i, val = 0, cal_div, ret;
+
+	/*
+	 * If the PLL is in FSM mode, then treat the set_rate callback
+	 * as a no-operation.
+	 */
+	ret = regmap_read(pll->clkr.regmap, pll->offset + PLL_MODE, &val);
+	if (ret)
+		return ret;
+
+	if (val & PLL_VOTE_FSM_ENA)
+		return 0;
+
+	if (!pll->post_div_table) {
+		pr_err("Missing the post_div_table for the PLL\n");
+		return -EINVAL;
+	}
+
+	cal_div = DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+	for (i = 0; i < pll->num_post_div; i++) {
+		if (pll->post_div_table[i].div == cal_div) {
+			val = pll->post_div_table[i].val;
+			break;
+		}
+	}
+
+	return regmap_update_bits(pll->clkr.regmap,
+				pll->offset + TRION_PLL_USER_CTL,
+				PLL_POST_DIV_MASK << pll->post_div_shift,
+				val << pll->post_div_shift);
+}
+
+const struct clk_ops clk_trion_pll_postdiv_ops = {
+	.recalc_rate = clk_trion_pll_postdiv_recalc_rate,
+	.round_rate = clk_trion_pll_postdiv_round_rate,
+	.set_rate = clk_trion_pll_postdiv_set_rate,
+};
+EXPORT_SYMBOL(clk_trion_pll_postdiv_ops);
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 2656cd6..c5fecb1 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -27,6 +27,7 @@
 enum pll_type {
 	ALPHA_PLL,
 	FABIA_PLL,
+	TRION_PLL,
 };
 
 /**
@@ -35,7 +36,7 @@
  * @inited: flag that's set when the PLL is initialized
  * @vco_table: array of VCO settings
  * @clkr: regmap clock handle
- * @is_fabia: Set if the PLL type is FABIA
+ * @pll_type: Specify the type of PLL
  */
 struct clk_alpha_pll {
 	u32 offset;
@@ -79,10 +80,15 @@
 extern const struct clk_ops clk_fabia_pll_ops;
 extern const struct clk_ops clk_fabia_fixed_pll_ops;
 extern const struct clk_ops clk_generic_pll_postdiv_ops;
+extern const struct clk_ops clk_trion_pll_ops;
+extern const struct clk_ops clk_trion_fixed_pll_ops;
+extern const struct clk_ops clk_trion_pll_postdiv_ops;
 
 void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
 		const struct pll_config *config);
 void clk_fabia_pll_configure(struct clk_alpha_pll *pll,
 		struct regmap *regmap, const struct pll_config *config);
+int clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
+		const struct pll_config *config);
 
 #endif
diff --git a/drivers/clk/qcom/clk-aop-qmp.c b/drivers/clk/qcom/clk-aop-qmp.c
index ff229fb..e2bfe42 100644
--- a/drivers/clk/qcom/clk-aop-qmp.c
+++ b/drivers/clk/qcom/clk-aop-qmp.c
@@ -139,6 +139,12 @@
 	struct clk_aop_qmp *clk = to_aop_qmp_clk(hw);
 
 	mutex_lock(&clk_aop_lock);
+	/*
+	 * Return early if the clock has been enabled already. This
+	 * is to avoid issues with sending duplicate enable requests.
+	 */
+	if (clk->enabled)
+		goto err;
 
 	if (clk->level)
 		rate = clk->level;
@@ -179,6 +185,9 @@
 
 	mutex_lock(&clk_aop_lock);
 
+	if (!clk->enabled)
+		goto err;
+
 	rate = clk->disable_state;
 
 	snprintf(mbox_msg, MAX_LEN, "{class: %s, res: %s, val: %ld}",
diff --git a/drivers/clk/qcom/clk-cpu-a7.c b/drivers/clk/qcom/clk-cpu-a7.c
new file mode 100644
index 0000000..c0cc00f8
--- /dev/null
+++ b/drivers/clk/qcom/clk-cpu-a7.c
@@ -0,0 +1,718 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/pm_opp.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <dt-bindings/clock/qcom,cpu-a7.h>
+
+#include "clk-alpha-pll.h"
+#include "clk-debug.h"
+#include "clk-rcg.h"
+#include "clk-regmap-mux-div.h"
+#include "common.h"
+#include "vdd-level-sdm845.h"
+
+#define SYS_APC0_AUX_CLK_SRC	1
+
+#define PLL_MODE_REG		0x0
+#define PLL_OPMODE_RUN		0x1
+#define PLL_OPMODE_REG		0x38
+#define PLL_MODE_OUTCTRL	BIT(0)
+
+#define to_clk_regmap_mux_div(_hw) \
+	container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
+
+static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner);
+static DEFINE_VDD_REGS_INIT(vdd_cpu, 1);
+
+enum apcs_clk_parent_index {
+	XO_AO_INDEX,
+	SYS_APC0_AUX_CLK_INDEX,
+	APCS_CPU_PLL_INDEX,
+};
+
+enum {
+	P_SYS_APC0_AUX_CLK,
+	P_APCS_CPU_PLL,
+	P_BI_TCXO_AO,
+};
+
+static const struct parent_map apcs_clk_parent_map[] = {
+	[XO_AO_INDEX] = { P_BI_TCXO_AO, 0 },
+	[SYS_APC0_AUX_CLK_INDEX] = { P_SYS_APC0_AUX_CLK, 1 },
+	[APCS_CPU_PLL_INDEX] = { P_APCS_CPU_PLL, 5 },
+};
+
+static const char *const apcs_clk_parent_name[] = {
+	[XO_AO_INDEX] = "bi_tcxo_ao",
+	[SYS_APC0_AUX_CLK_INDEX] = "sys_apc0_aux_clk",
+	[APCS_CPU_PLL_INDEX] = "apcs_cpu_pll",
+};
+
+static int a7cc_clk_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
+						unsigned long prate, u8 index)
+{
+	struct clk_regmap_mux_div *cpuclk = to_clk_regmap_mux_div(hw);
+
+	return __mux_div_set_src_div(cpuclk, cpuclk->parent_map[index].cfg,
+					cpuclk->div);
+}
+
+static int a7cc_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+	/*
+	 * Since a7cc_clk_set_rate_and_parent() is defined and set_parent()
+	 * will never gets called from clk_change_rate() so return 0.
+	 */
+	return 0;
+}
+
+static int a7cc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+						unsigned long prate)
+{
+	struct clk_regmap_mux_div *cpuclk = to_clk_regmap_mux_div(hw);
+
+	/*
+	 * Parent is same as the last rate.
+	 * Here just configure new div.
+	 */
+	return __mux_div_set_src_div(cpuclk, cpuclk->src, cpuclk->div);
+}
+
+static int a7cc_clk_determine_rate(struct clk_hw *hw,
+					struct clk_rate_request *req)
+{
+	int ret;
+	u32 div = 1;
+	struct clk_hw *xo, *apc0_auxclk_hw, *apcs_cpu_pll_hw;
+	unsigned long apc0_auxclk_rate, rate = req->rate;
+	struct clk_rate_request parent_req = { };
+	struct clk_regmap_mux_div *cpuclk = to_clk_regmap_mux_div(hw);
+	unsigned long mask = BIT(cpuclk->hid_width) - 1;
+
+	xo = clk_hw_get_parent_by_index(hw, XO_AO_INDEX);
+	if (rate == clk_hw_get_rate(xo)) {
+		req->best_parent_hw = xo;
+		req->best_parent_rate = rate;
+		cpuclk->div = div;
+		cpuclk->src = cpuclk->parent_map[XO_AO_INDEX].cfg;
+		return 0;
+	}
+
+	apc0_auxclk_hw = clk_hw_get_parent_by_index(hw, SYS_APC0_AUX_CLK_INDEX);
+	apcs_cpu_pll_hw = clk_hw_get_parent_by_index(hw, APCS_CPU_PLL_INDEX);
+
+	apc0_auxclk_rate = clk_hw_get_rate(apc0_auxclk_hw);
+	if (rate <= apc0_auxclk_rate) {
+		req->best_parent_hw = apc0_auxclk_hw;
+		req->best_parent_rate = apc0_auxclk_rate;
+
+		div = DIV_ROUND_UP((2 * req->best_parent_rate), rate) - 1;
+		div = min_t(unsigned long, div, mask);
+
+		req->rate = clk_rcg2_calc_rate(req->best_parent_rate, 0,
+							0, 0, div);
+		cpuclk->src = cpuclk->parent_map[SYS_APC0_AUX_CLK_INDEX].cfg;
+	} else {
+		parent_req.rate = rate;
+		parent_req.best_parent_hw = apcs_cpu_pll_hw;
+
+		req->best_parent_hw = apcs_cpu_pll_hw;
+		ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
+		if (ret)
+			return ret;
+
+		req->best_parent_rate = parent_req.rate;
+		cpuclk->src = cpuclk->parent_map[APCS_CPU_PLL_INDEX].cfg;
+	}
+	cpuclk->div = div;
+
+	return 0;
+}
+
+static void a7cc_clk_list_registers(struct seq_file *f, struct clk_hw *hw)
+{
+	struct clk_regmap_mux_div *cpuclk = to_clk_regmap_mux_div(hw);
+	int i = 0, size = 0, val;
+
+	static struct clk_register_data data[] = {
+		{"CMD_RCGR", 0x0},
+		{"CFG_RCGR", 0x4},
+	};
+
+	size = ARRAY_SIZE(data);
+	for (i = 0; i < size; i++) {
+		regmap_read(cpuclk->clkr.regmap,
+				cpuclk->reg_offset + data[i].offset, &val);
+		seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val);
+	}
+}
+
+static unsigned long a7cc_clk_recalc_rate(struct clk_hw *hw,
+					unsigned long prate)
+{
+	struct clk_regmap_mux_div *cpuclk = to_clk_regmap_mux_div(hw);
+	const char *name = clk_hw_get_name(hw);
+	struct clk_hw *parent;
+	int ret = 0;
+	unsigned long parent_rate;
+	u32 i, div, src = 0;
+	u32 num_parents = clk_hw_get_num_parents(hw);
+
+	ret = mux_div_get_src_div(cpuclk, &src, &div);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < num_parents; i++) {
+		if (src == cpuclk->parent_map[i].cfg) {
+			parent = clk_hw_get_parent_by_index(hw, i);
+			parent_rate = clk_hw_get_rate(parent);
+			return clk_rcg2_calc_rate(parent_rate, 0, 0, 0, div);
+		}
+	}
+	pr_err("%s: Can't find parent %d\n", name, src);
+	return ret;
+}
+
+static int a7cc_clk_enable(struct clk_hw *hw)
+{
+	return clk_regmap_mux_div_ops.enable(hw);
+}
+
+static void a7cc_clk_disable(struct clk_hw *hw)
+{
+	clk_regmap_mux_div_ops.disable(hw);
+}
+
+static u8 a7cc_clk_get_parent(struct clk_hw *hw)
+{
+	return clk_regmap_mux_div_ops.get_parent(hw);
+}
+
+/*
+ * We use the notifier function for switching to a temporary safe configuration
+ * (mux and divider), while the APSS pll is reconfigured.
+ */
+static int a7cc_notifier_cb(struct notifier_block *nb, unsigned long event,
+			     void *data)
+{
+	int ret = 0;
+	struct clk_regmap_mux_div *cpuclk = container_of(nb,
+					struct clk_regmap_mux_div, clk_nb);
+
+	if (event == PRE_RATE_CHANGE)
+		/* set the mux to safe source(sys_apc0_aux_clk) & div */
+		ret = __mux_div_set_src_div(cpuclk, SYS_APC0_AUX_CLK_SRC, 1);
+
+	if (event == ABORT_RATE_CHANGE)
+		pr_err("Error in configuring PLL - stay at safe src only\n");
+
+	return notifier_from_errno(ret);
+}
+
+static const struct clk_ops a7cc_clk_ops = {
+	.enable = a7cc_clk_enable,
+	.disable = a7cc_clk_disable,
+	.get_parent = a7cc_clk_get_parent,
+	.set_rate = a7cc_clk_set_rate,
+	.set_parent = a7cc_clk_set_parent,
+	.set_rate_and_parent = a7cc_clk_set_rate_and_parent,
+	.determine_rate = a7cc_clk_determine_rate,
+	.recalc_rate = a7cc_clk_recalc_rate,
+	.debug_init = clk_debug_measure_add,
+	.list_registers = a7cc_clk_list_registers,
+};
+
+/*
+ * As per HW, sys_apc0_aux_clk runs at 300MHz and configured by BOOT
+ * So adding it as dummy clock.
+ */
+
+static struct clk_dummy sys_apc0_aux_clk = {
+	.rrate = 300000000,
+	.hw.init = &(struct clk_init_data){
+		.name = "sys_apc0_aux_clk",
+		.ops = &clk_dummy_ops,
+	},
+};
+
+/* Initial configuration for 1497.6MHz(Turbo) */
+static const struct pll_config apcs_cpu_pll_config = {
+	.l = 0x4E,
+};
+
+static struct pll_vco trion_vco[] = {
+	{ 249600000, 2000000000, 0 },
+};
+
+static struct clk_alpha_pll apcs_cpu_pll = {
+	.type = TRION_PLL,
+	.vco_table = trion_vco,
+	.num_vco = ARRAY_SIZE(trion_vco),
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "apcs_cpu_pll",
+		.parent_names = (const char *[]){ "bi_tcxo_ao" },
+		.num_parents = 1,
+		.ops = &clk_trion_pll_ops,
+		VDD_CX_FMAX_MAP4(LOWER, 345600000,
+				LOW, 576000000,
+				NOMINAL, 1094400000,
+				HIGH, 1497600000),
+	},
+};
+
+static struct clk_regmap_mux_div apcs_clk = {
+	.hid_width  = 5,
+	.hid_shift  = 0,
+	.src_width  = 3,
+	.src_shift  = 8,
+	.safe_src = 1,
+	.safe_div = 1,
+	.parent_map = apcs_clk_parent_map,
+	.clk_nb.notifier_call = a7cc_notifier_cb,
+	.clkr.hw.init = &(struct clk_init_data) {
+		.name = "apcs_clk",
+		.parent_names = apcs_clk_parent_name,
+		.num_parents = 3,
+		.vdd_class = &vdd_cpu,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &a7cc_clk_ops,
+	},
+};
+
+static const struct of_device_id match_table[] = {
+	{ .compatible = "qcom,cpu-sdxpoorwills" },
+	{}
+};
+
+static const struct regmap_config cpu_regmap_config = {
+	.reg_bits               = 32,
+	.reg_stride             = 4,
+	.val_bits               = 32,
+	.max_register           = 0x7F10,
+	.fast_io                = true,
+};
+
+static struct clk_hw *cpu_clks_hws[] = {
+	[SYS_APC0_AUX_CLK] = &sys_apc0_aux_clk.hw,
+	[APCS_CPU_PLL] = &apcs_cpu_pll.clkr.hw,
+	[APCS_CLK] = &apcs_clk.clkr.hw,
+};
+
+static void a7cc_clk_get_speed_bin(struct platform_device *pdev, int *bin,
+							int *version)
+{
+	struct resource *res;
+	void __iomem *base;
+	u32 pte_efuse, valid;
+
+	*bin = 0;
+	*version = 0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
+	if (!res) {
+		dev_info(&pdev->dev,
+			"No speed/PVS binning available. Defaulting to 0!\n");
+		return;
+	}
+
+	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (!base) {
+		dev_info(&pdev->dev,
+			"Unable to read efuse data. Defaulting to 0!\n");
+		return;
+	}
+
+	pte_efuse = readl_relaxed(base);
+	devm_iounmap(&pdev->dev, base);
+
+	*bin = pte_efuse & 0x7;
+	valid = (pte_efuse >> 3) & 0x1;
+	*version = (pte_efuse >> 4) & 0x3;
+
+	if (!valid) {
+		dev_info(&pdev->dev, "Speed bin not set. Defaulting to 0!\n");
+		*bin = 0;
+	} else {
+		dev_info(&pdev->dev, "Speed bin: %d\n", *bin);
+	}
+
+	dev_info(&pdev->dev, "PVS version: %d\n", *version);
+}
+
+static int a7cc_clk_get_fmax_vdd_class(struct platform_device *pdev,
+			struct clk_init_data *clk_intd, char *prop_name)
+{
+	struct device_node *of = pdev->dev.of_node;
+	int prop_len, i, j;
+	struct clk_vdd_class *vdd = clk_intd->vdd_class;
+	int num = vdd->num_regulators + 1;
+	u32 *array;
+
+	if (!of_find_property(of, prop_name, &prop_len)) {
+		dev_err(&pdev->dev, "missing %s\n", prop_name);
+		return -EINVAL;
+	}
+
+	prop_len /= sizeof(u32);
+	if (prop_len % num) {
+		dev_err(&pdev->dev, "bad length %d\n", prop_len);
+		return -EINVAL;
+	}
+
+	prop_len /= num;
+	vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
+					GFP_KERNEL);
+	if (!vdd->level_votes)
+		return -ENOMEM;
+
+	vdd->vdd_uv = devm_kzalloc(&pdev->dev,
+				prop_len * sizeof(int) * (num - 1), GFP_KERNEL);
+	if (!vdd->vdd_uv)
+		return -ENOMEM;
+
+	clk_intd->rate_max = devm_kzalloc(&pdev->dev,
+				prop_len * sizeof(unsigned long), GFP_KERNEL);
+	if (!clk_intd->rate_max)
+		return -ENOMEM;
+
+	array = devm_kzalloc(&pdev->dev,
+			prop_len * sizeof(u32) * num, GFP_KERNEL);
+	if (!array)
+		return -ENOMEM;
+
+	of_property_read_u32_array(of, prop_name, array, prop_len * num);
+	for (i = 0; i < prop_len; i++) {
+		clk_intd->rate_max[i] = array[num * i];
+		for (j = 1; j < num; j++) {
+			vdd->vdd_uv[(num - 1) * i + (j - 1)] =
+					array[num * i + j];
+		}
+	}
+
+	devm_kfree(&pdev->dev, array);
+	vdd->num_levels = prop_len;
+	vdd->cur_level = prop_len;
+	clk_intd->num_rate_max = prop_len;
+
+	return 0;
+}
+
+/*
+ *  Find the voltage level required for a given clock rate.
+ */
+static int find_vdd_level(struct clk_init_data *clk_intd, unsigned long rate)
+{
+	int level;
+
+	for (level = 0; level < clk_intd->num_rate_max; level++)
+		if (rate <= clk_intd->rate_max[level])
+			break;
+
+	if (level == clk_intd->num_rate_max) {
+		pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
+				clk_intd->name);
+		return -EINVAL;
+	}
+
+	return level;
+}
+
+static int
+a7cc_clk_add_opp(struct clk_hw *hw, struct device *dev, unsigned long max_rate)
+{
+	unsigned long rate = 0;
+	int level, uv, j = 1;
+	long ret;
+	struct clk_init_data *clk_intd =  (struct clk_init_data *)hw->init;
+	struct clk_vdd_class *vdd = clk_intd->vdd_class;
+
+	if (IS_ERR_OR_NULL(dev)) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	while (1) {
+		rate = clk_intd->rate_max[j++];
+		level = find_vdd_level(clk_intd, rate);
+		if (level <= 0) {
+			pr_warn("clock-cpu: no corner for %lu.\n", rate);
+			return -EINVAL;
+		}
+
+		uv = vdd->vdd_uv[level];
+		if (uv < 0) {
+			pr_warn("clock-cpu: no uv for %lu.\n", rate);
+			return -EINVAL;
+		}
+
+		ret = dev_pm_opp_add(dev, rate, uv);
+		if (ret) {
+			pr_warn("clock-cpu: failed to add OPP for %lu\n", rate);
+			return rate;
+		}
+
+		if (rate >= max_rate)
+			break;
+	}
+
+	return 0;
+}
+
+static void a7cc_clk_print_opp_table(int a7_cpu)
+{
+	struct dev_pm_opp *oppfmax, *oppfmin;
+	unsigned long apc_fmax, apc_fmin;
+	u32 max_a7ss_index = apcs_clk.clkr.hw.init->num_rate_max;
+
+	apc_fmax = apcs_clk.clkr.hw.init->rate_max[max_a7ss_index - 1];
+	apc_fmin = apcs_clk.clkr.hw.init->rate_max[1];
+
+	rcu_read_lock();
+
+	oppfmax = dev_pm_opp_find_freq_exact(get_cpu_device(a7_cpu),
+					apc_fmax, true);
+	oppfmin = dev_pm_opp_find_freq_exact(get_cpu_device(a7_cpu),
+					apc_fmin, true);
+	pr_info("Clock_cpu: OPP voltage for %lu: %ld\n", apc_fmin,
+		dev_pm_opp_get_voltage(oppfmin));
+	pr_info("Clock_cpu: OPP voltage for %lu: %ld\n", apc_fmax,
+		dev_pm_opp_get_voltage(oppfmax));
+
+	rcu_read_unlock();
+}
+
+static void a7cc_clk_populate_opp_table(struct platform_device *pdev)
+{
+	unsigned long apc_fmax;
+	int cpu, a7_cpu = 0;
+	u32 max_a7ss_index = apcs_clk.clkr.hw.init->num_rate_max;
+
+	apc_fmax = apcs_clk.clkr.hw.init->rate_max[max_a7ss_index - 1];
+
+	for_each_possible_cpu(cpu) {
+		a7_cpu = cpu;
+		WARN(a7cc_clk_add_opp(&apcs_clk.clkr.hw, get_cpu_device(cpu),
+				apc_fmax),
+				"Failed to add OPP levels for apcs_clk\n");
+	}
+	/* One time print during bootup */
+	dev_info(&pdev->dev, "OPP tables populated (cpu %d)\n", a7_cpu);
+
+	a7cc_clk_print_opp_table(a7_cpu);
+}
+
+static int a7cc_driver_probe(struct platform_device *pdev)
+{
+	struct clk *clk;
+	void __iomem *base;
+	u32 opmode_regval, mode_regval;
+	struct resource *res;
+	struct clk_onecell_data *data;
+	struct device *dev = &pdev->dev;
+	struct device_node *of = pdev->dev.of_node;
+	int i, ret, speed_bin, version, cpu;
+	int num_clks = ARRAY_SIZE(cpu_clks_hws);
+	u32 a7cc_clk_init_rate = 0;
+	char prop_name[] = "qcom,speedX-bin-vX";
+	struct clk *ext_xo_clk;
+
+	/* Require the RPMH-XO clock to be registered before */
+	ext_xo_clk = devm_clk_get(dev, "xo_ao");
+	if (IS_ERR(ext_xo_clk)) {
+		if (PTR_ERR(ext_xo_clk) != -EPROBE_DEFER)
+			dev_err(dev, "Unable to get xo clock\n");
+		return PTR_ERR(ext_xo_clk);
+	}
+
+	/* Get speed bin information */
+	a7cc_clk_get_speed_bin(pdev, &speed_bin, &version);
+
+	/* Rail Regulator for apcs_pll */
+	vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_dig_ao");
+	if (IS_ERR(vdd_cx.regulator[0])) {
+		if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER))
+			dev_err(&pdev->dev,
+				"Unable to get vdd_dig_ao regulator\n");
+		return PTR_ERR(vdd_cx.regulator[0]);
+	}
+
+	/* Rail Regulator for APSS a7ss mux */
+	vdd_cpu.regulator[0] = devm_regulator_get(&pdev->dev, "cpu-vdd");
+	if (IS_ERR(vdd_cpu.regulator[0])) {
+		if (!(PTR_ERR(vdd_cpu.regulator[0]) == -EPROBE_DEFER))
+			dev_err(&pdev->dev,
+				"Unable to get cpu-vdd regulator\n");
+		return PTR_ERR(vdd_cpu.regulator[0]);
+	}
+
+	snprintf(prop_name, ARRAY_SIZE(prop_name),
+			"qcom,speed%d-bin-v%d", speed_bin, version);
+
+	ret = a7cc_clk_get_fmax_vdd_class(pdev,
+		(struct clk_init_data *)apcs_clk.clkr.hw.init, prop_name);
+	if (ret) {
+		dev_err(&pdev->dev,
+		"Can't get speed bin for apcs_clk. Falling back to zero\n");
+		ret = a7cc_clk_get_fmax_vdd_class(pdev,
+				(struct clk_init_data *)apcs_clk.clkr.hw.init,
+				"qcom,speed0-bin-v0");
+		if (ret) {
+			dev_err(&pdev->dev,
+			"Unable to get speed bin for apcs_clk freq-corner mapping info\n");
+			return ret;
+		}
+	}
+
+	ret = of_property_read_u32(of, "qcom,a7cc-init-rate",
+						&a7cc_clk_init_rate);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"unable to find qcom,a7cc_clk_init_rate property,ret=%d\n",
+			ret);
+		return -EINVAL;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs_pll");
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to map apcs_cpu_pll register base\n");
+		return PTR_ERR(base);
+	}
+
+	apcs_cpu_pll.clkr.regmap = devm_regmap_init_mmio(dev, base,
+						&cpu_regmap_config);
+	if (IS_ERR(apcs_cpu_pll.clkr.regmap)) {
+		dev_err(&pdev->dev, "Couldn't get regmap for apcs_cpu_pll\n");
+		return PTR_ERR(apcs_cpu_pll.clkr.regmap);
+	}
+
+	ret = of_property_read_u32(of, "qcom,rcg-reg-offset",
+						&apcs_clk.reg_offset);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"unable to find qcom,rcg-reg-offset property,ret=%d\n",
+			ret);
+		return -EINVAL;
+	}
+
+	apcs_clk.clkr.regmap = apcs_cpu_pll.clkr.regmap;
+
+	/* Read PLLs OPMODE and mode register */
+	ret = regmap_read(apcs_cpu_pll.clkr.regmap, PLL_OPMODE_REG,
+							&opmode_regval);
+	if (ret)
+		return ret;
+
+	ret = regmap_read(apcs_cpu_pll.clkr.regmap, PLL_MODE_REG,
+							&mode_regval);
+	if (ret)
+		return ret;
+
+	/* Configure APSS PLL only if it is not enabled and running */
+	if (!(opmode_regval & PLL_OPMODE_RUN) &&
+				!(mode_regval & PLL_MODE_OUTCTRL))
+		clk_trion_pll_configure(&apcs_cpu_pll,
+			apcs_cpu_pll.clkr.regmap, &apcs_cpu_pll_config);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->clk_num = num_clks;
+
+	data->clks = devm_kzalloc(dev, num_clks * sizeof(struct clk *),
+					GFP_KERNEL);
+	if (!data->clks)
+		return -ENOMEM;
+
+	/* Register clocks with clock framework */
+	for (i = 0; i < num_clks; i++) {
+		clk = devm_clk_register(dev, cpu_clks_hws[i]);
+		if (IS_ERR(clk))
+			return PTR_ERR(clk);
+		data->clks[i] = clk;
+	}
+
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, data);
+	if (ret) {
+		dev_err(&pdev->dev, "CPU clock driver registeration failed\n");
+		return ret;
+	}
+
+	ret = clk_notifier_register(apcs_cpu_pll.clkr.hw.clk, &apcs_clk.clk_nb);
+	if (ret) {
+		dev_err(dev, "failed to register clock notifier: %d\n", ret);
+		return ret;
+	}
+
+	/* Put proxy vote for APSS PLL */
+	clk_prepare_enable(apcs_cpu_pll.clkr.hw.clk);
+
+	/* Set to TURBO boot frequency */
+	ret = clk_set_rate(apcs_clk.clkr.hw.clk, a7cc_clk_init_rate);
+	if (ret)
+		dev_err(&pdev->dev, "Unable to set init rate on apcs_clk\n");
+
+	/*
+	 * We don't want the CPU clocks to be turned off at late init
+	 * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
+	 * refcount of these clocks. Any cpufreq/hotplug manager can assume
+	 * that the clocks have already been prepared and enabled by the time
+	 * they take over.
+	 */
+
+	get_online_cpus();
+	for_each_online_cpu(cpu)
+		WARN(clk_prepare_enable(apcs_clk.clkr.hw.clk),
+			"Unable to turn on CPU clock\n");
+	put_online_cpus();
+
+	/* Remove proxy vote for APSS PLL */
+	clk_disable_unprepare(apcs_cpu_pll.clkr.hw.clk);
+
+	a7cc_clk_populate_opp_table(pdev);
+
+	dev_info(dev, "CPU clock Driver probed successfully\n");
+
+	return ret;
+}
+
+static struct platform_driver a7_clk_driver = {
+	.probe = a7cc_driver_probe,
+	.driver = {
+		.name = "qcom-cpu-sdxpoorwills",
+		.of_match_table = match_table,
+	},
+};
+
+static int __init a7_clk_init(void)
+{
+	return platform_driver_register(&a7_clk_driver);
+}
+subsys_initcall(a7_clk_init);
+
+static void __exit a7_clk_exit(void)
+{
+	platform_driver_unregister(&a7_clk_driver);
+}
+module_exit(a7_clk_exit);
+
+MODULE_ALIAS("platform:cpu");
+MODULE_DESCRIPTION("A7 CPU clock Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 258a6f2..7e665ca 100644
--- a/drivers/clk/qcom/clk-cpu-osm.c
+++ b/drivers/clk/qcom/clk-cpu-osm.c
@@ -18,7 +18,6 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/io.h>
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/clk.h>
@@ -27,20 +26,17 @@
 #include <linux/platform_device.h>
 #include <linux/of_platform.h>
 #include <linux/pm_opp.h>
-#include <linux/pm_qos.h>
 #include <linux/interrupt.h>
-#include <linux/regulator/driver.h>
-#include <linux/regmap.h>
 #include <linux/uaccess.h>
 #include <linux/sched.h>
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
-#include <soc/qcom/scm.h>
+#include <linux/regulator/consumer.h>
 #include <dt-bindings/clock/qcom,cpucc-sdm845.h>
+#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 
 #include "common.h"
 #include "clk-regmap.h"
-#include "clk-rcg.h"
 #include "clk-voter.h"
 #include "clk-debug.h"
 
@@ -50,454 +46,82 @@
 #define SINGLE_CORE			1
 #define MAX_CLUSTER_CNT			3
 #define MAX_MEM_ACC_VAL_PER_LEVEL	3
-#define MAX_CORE_COUNT			4
 #define CORE_COUNT_VAL(val)		((val & GENMASK(18, 16)) >> 16)
 
-#define OSM_CYCLE_COUNTER_CTRL_REG		0x760
-#define OSM_CYCLE_COUNTER_USE_XO_EDGE_EN	BIT(8)
-
 #define OSM_REG_SIZE			32
 
-#define L3_EFUSE_SHIFT			29
-#define L3_EFUSE_MASK			0x7
-#define PWRCL_EFUSE_SHIFT		29
-#define PWRCL_EFUSE_MASK		0x7
-#define PERFCL_EFUSE_SHIFT		29
-#define PERFCL_EFUSE_MASK		0x7
-
 #define ENABLE_REG			0x0
-#define ENABLE_OSM			BIT(0)
 #define FREQ_REG			0x110
 #define VOLT_REG			0x114
-#define OVERRIDE_REG			0x118
-#define SPM_CC_INC_HYSTERESIS		0x1c
-#define SPM_CC_DEC_HYSTERESIS		0x20
-#define SPM_CORE_INACTIVE_MAPPING	0x28
-#define CC_ZERO_BEHAV_CTRL		0xc
-#define ENABLE_OVERRIDE			BIT(0)
-#define SPM_CC_DCVS_DISABLE		0x24
-#define LLM_FREQ_VOTE_INC_HYSTERESIS	0x30
-#define LLM_FREQ_VOTE_DEC_HYSTERESIS	0x34
-#define LLM_INTF_DCVS_DISABLE		0x40
-#define LLM_VOLTAGE_VOTE_INC_HYSTERESIS	0x38
-#define LLM_VOLTAGE_VOTE_DEC_HYSTERESIS	0x3c
-#define VMIN_REDUCTION_ENABLE_REG	0x48
-#define VMIN_REDUCTION_TIMER_REG	0x4c
-#define PDN_FSM_CTRL_REG		0x54
-#define DELTA_DEX_VAL			BVAL(31, 23, 0xa)
-#define IGNORE_PLL_LOCK			BIT(15)
-#define CC_BOOST_FSM_EN			BIT(0)
-#define CC_BOOST_FSM_TIMERS_REG0	0x58
-#define CC_BOOST_FSM_TIMERS_REG1	0x5c
-#define CC_BOOST_FSM_TIMERS_REG2	0x60
-#define DCVS_BOOST_FSM_EN_MASK		BIT(2)
-#define DCVS_BOOST_FSM_TIMERS_REG0	0x64
-#define DCVS_BOOST_FSM_TIMERS_REG1	0x68
-#define DCVS_BOOST_FSM_TIMERS_REG2	0x6c
-#define PS_BOOST_FSM_EN_MASK		BIT(1)
-#define PS_BOOST_FSM_TIMERS_REG0	0x74
-#define PS_BOOST_FSM_TIMERS_REG1	0x78
-#define PS_BOOST_FSM_TIMERS_REG2	0x7c
-#define BOOST_PROG_SYNC_DELAY_REG	0x80
-#define DCVS_DROOP_FSM_EN_MASK		BIT(5)
-#define DROOP_PROG_SYNC_DELAY_REG	0x9c
-#define DROOP_RELEASE_TIMER_CTRL	0x88
-#define DROOP_CTRL_REG			0x84
-#define DCVS_DROOP_TIMER_CTRL		0x98
-#define PLL_SW_OVERRIDE_ENABLE		0xa0
-#define PLL_SW_OVERRIDE_DROOP_EN	BIT(0)
-#define SPM_CORE_COUNT_CTRL		0x2c
 #define CORE_DCVS_CTRL			0xbc
-#define OVERRIDE_CLUSTER_IDLE_ACK	0x800
-#define REQ_GEN_FSM_STATUS		0x70c
 
-#define PLL_MIN_LVAL			0x21
-#define PLL_MIN_FREQ_REG		0x94
-#define PLL_POST_DIV1			0x09
-#define PLL_POST_DIV2			0x109
-#define PLL_MODE			0x0
-#define PLL_L_VAL			0x4
-#define PLL_USER_CTRL			0xc
-#define PLL_CONFIG_CTL_LO		0x10
-#define PLL_CONFIG_CTL_HI		0x14
-#define MIN_VCO_VAL			0x2b
-
-#define MAX_VC				63
-#define MEM_ACC_LEVELS_LUT		2
-#define MAX_MEM_ACC_LEVELS		3
-#define MAX_MEM_ACC_VAL_PER_LEVEL	3
-#define MAX_MEM_ACC_VALUES		(MAX_MEM_ACC_LEVELS * \
-					MAX_MEM_ACC_VAL_PER_LEVEL)
-#define MEM_ACC_ADDRS			3
-
-#define ISENSE_ON_DATA			0xf
-#define ISENSE_OFF_DATA			0x0
-#define CONSTANT_32			0x20
-
-#define APM_MX_MODE			0x4100000
-#define APM_APC_MODE			0x4100002
-#define APM_READ_DATA_MASK		0xc
-#define APM_MX_MODE_VAL			0x4
-#define APM_APC_READ_VAL		0x8
-#define APM_MX_READ_VAL			0x4
-#define APM_CROSSOVER_VC		0xb0
-
-#define MEM_ACC_SEQ_CONST(n)		(n)
-#define MEM_ACC_APM_READ_MASK		0xff
-#define MEMACC_CROSSOVER_VC		0xb8
-
-#define PLL_WAIT_LOCK_TIME_US		10
-#define PLL_WAIT_LOCK_TIME_NS		(PLL_WAIT_LOCK_TIME_US * 1000)
-#define SAFE_FREQ_WAIT_NS		5000
-#define DEXT_DECREMENT_WAIT_NS		1000
-
-#define DATA_MEM(n)			(0x400 + (n) * 4)
+#define EFUSE_SHIFT(v1)			((v1) ? 3 : 2)
+#define EFUSE_MASK			0x7
 
 #define DCVS_PERF_STATE_DESIRED_REG_0_V1	0x780
 #define DCVS_PERF_STATE_DESIRED_REG_0_V2	0x920
-#define DCVS_PERF_STATE_DESIRED_REG(n, v2) \
-	(((v2) ? DCVS_PERF_STATE_DESIRED_REG_0_V2 \
-		: DCVS_PERF_STATE_DESIRED_REG_0_V1) + 4 * (n))
+#define DCVS_PERF_STATE_DESIRED_REG(n, v1) \
+	(((v1) ? DCVS_PERF_STATE_DESIRED_REG_0_V1 \
+		: DCVS_PERF_STATE_DESIRED_REG_0_V2) + 4 * (n))
 
 #define OSM_CYCLE_COUNTER_STATUS_REG_0_V1	0x7d0
 #define OSM_CYCLE_COUNTER_STATUS_REG_0_V2	0x9c0
-#define OSM_CYCLE_COUNTER_STATUS_REG(n, v2) \
-	(((v2) ? OSM_CYCLE_COUNTER_STATUS_REG_0_V2 \
-		: OSM_CYCLE_COUNTER_STATUS_REG_0_V1) + 4 * (n))
+#define OSM_CYCLE_COUNTER_STATUS_REG(n, v1) \
+	(((v1) ? OSM_CYCLE_COUNTER_STATUS_REG_0_V1 \
+		: OSM_CYCLE_COUNTER_STATUS_REG_0_V2) + 4 * (n))
 
-/* ACD registers */
-#define ACD_HW_VERSION		0x0
-#define ACDCR			0x4
-#define ACDTD			0x8
-#define ACDSSCR			0x28
-#define ACD_EXTINT_CFG		0x30
-#define ACD_DCVS_SW		0x34
-#define ACD_GFMUX_CFG		0x3c
-#define ACD_READOUT_CFG		0x48
-#define ACD_AVG_CFG_0		0x4c
-#define ACD_AVG_CFG_1		0x50
-#define ACD_AVG_CFG_2		0x54
-#define ACD_AUTOXFER_CFG	0x80
-#define ACD_AUTOXFER		0x84
-#define ACD_AUTOXFER_CTL	0x88
-#define ACD_AUTOXFER_STATUS	0x8c
-#define ACD_WRITE_CTL		0x90
-#define ACD_WRITE_STATUS	0x94
-#define ACD_READOUT		0x98
-
-#define ACD_MASTER_ONLY_REG_ADDR	0x80
-#define ACD_1P1_MAX_REG_OFFSET		0x100
-#define ACD_WRITE_CTL_UPDATE_EN		BIT(0)
-#define ACD_WRITE_CTL_SELECT_SHIFT	1
-#define ACD_GFMUX_CFG_SELECT		BIT(0)
-#define ACD_AUTOXFER_START_CLEAR	0
-#define ACD_AUTOXFER_START_SET		1
-#define AUTO_XFER_DONE_MASK		BIT(0)
-#define ACD_DCVS_SW_DCVS_IN_PRGR_SET	BIT(0)
-#define ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR	0
-#define ACD_LOCAL_TRANSFER_TIMEOUT_NS   500
-
-#define ACD_REG_RELATIVE_ADDR(addr) (addr / 4)
-#define ACD_REG_RELATIVE_ADDR_BITMASK(addr) \
-			(1 << (ACD_REG_RELATIVE_ADDR(addr)))
-
-static const struct regmap_config osm_qcom_regmap_config = {
-	.reg_bits       = 32,
-	.reg_stride     = 4,
-	.val_bits       = 32,
-	.fast_io	= true,
-};
-
-enum clk_osm_bases {
-	OSM_BASE,
-	PLL_BASE,
-	EFUSE_BASE,
-	SEQ_BASE,
-	ACD_BASE,
-	NUM_BASES,
-};
-
-enum clk_osm_lut_data {
-	FREQ,
-	FREQ_DATA,
-	PLL_OVERRIDES,
-	MEM_ACC_LEVEL,
-	VIRTUAL_CORNER,
-	NUM_FIELDS,
-};
+static DEFINE_VDD_REGS_INIT(vdd_l3_mx_ao, 1);
+static DEFINE_VDD_REGS_INIT(vdd_pwrcl_mx_ao, 1);
 
 struct osm_entry {
 	u16 virtual_corner;
 	u16 open_loop_volt;
-	u32 freq_data;
-	u32 override_data;
-	u32 mem_acc_level;
 	long frequency;
 };
 
-static struct dentry *osm_debugfs_base;
-
 struct clk_osm {
 	struct clk_hw hw;
 	struct osm_entry osm_table[OSM_TABLE_SIZE];
 	struct dentry *debugfs;
-	struct regulator *vdd_reg;
-	struct platform_device *vdd_dev;
-	void *vbases[NUM_BASES];
-	unsigned long pbases[NUM_BASES];
+	void __iomem *vbase;
+	phys_addr_t pbase;
 	spinlock_t lock;
-
+	bool per_core_dcvs;
 	u32 num_entries;
 	u32 cluster_num;
 	u32 core_num;
-	u32 apm_crossover_vc;
-	u32 apm_threshold_vc;
-	u32 mem_acc_crossover_vc;
-	u32 mem_acc_threshold_vc;
-	u32 min_cpr_vc;
-	u32 cycle_counter_reads;
-	u32 cycle_counter_delay;
-	u32 cycle_counter_factor;
 	u64 total_cycle_counter;
 	u32 prev_cycle_counter;
-	u32 l_val_base;
-	u32 apcs_pll_user_ctl;
-	u32 apcs_pll_min_freq;
-	u32 cfg_gfmux_addr;
-	u32 apcs_cbc_addr;
-	u32 speedbin;
-	u32 mem_acc_crossover_vc_addr;
-	u32 mem_acc_addr[MEM_ACC_ADDRS];
-	u32 mem_acc_level_vc[MEM_ACC_LEVELS_LUT];
-	u32 ramp_ctl_addr;
-	u32 apm_mode_ctl;
-	u32 apm_status_ctl;
-	u32 osm_clk_rate;
-	u32 xo_clk_rate;
-	bool secure_init;
-	bool per_core_dcvs;
-	bool red_fsm_en;
-	bool boost_fsm_en;
-	bool safe_fsm_en;
-	bool ps_fsm_en;
-	bool droop_fsm_en;
-
-	struct notifier_block panic_notifier;
-	u32 trace_periodic_timer;
-	bool trace_en;
-	bool wdog_trace_en;
-
-	bool acd_init;
-	u32 acd_td;
-	u32 acd_cr;
-	u32 acd_sscr;
-	u32 acd_extint0_cfg;
-	u32 acd_extint1_cfg;
-	u32 acd_autoxfer_ctl;
-	u32 acd_debugfs_addr;
-	bool acd_avg_init;
-	u32 acd_avg_cfg0;
-	u32 acd_avg_cfg1;
-	u32 acd_avg_cfg2;
+	u32 max_core_count;
+	u32 mx_turbo_freq;
+	unsigned int cpr_rc;
 };
 
-static struct regulator *vdd_l3;
-static struct regulator *vdd_pwrcl;
-static struct regulator *vdd_perfcl;
-
-static inline int clk_osm_acd_mb(struct clk_osm *c)
-{
-	return readl_relaxed_no_log((char *)c->vbases[ACD_BASE] +
-					ACD_HW_VERSION);
-}
-
-static int clk_osm_acd_local_read_reg(struct clk_osm *c, u32 offset)
-{
-	u32 reg = 0;
-	int timeout;
-
-	if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
-		pr_err("ACD register at offset=0x%x not locally readable\n",
-			offset);
-		return -EINVAL;
-	}
-
-	/* Set select field in read control register */
-	writel_relaxed(ACD_REG_RELATIVE_ADDR(offset),
-			(char *)c->vbases[ACD_BASE] + ACD_READOUT_CFG);
-
-	/* Clear write control register */
-	writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
-	/* Set select and update_en fields in write control register */
-	reg = (ACD_REG_RELATIVE_ADDR(ACD_READOUT_CFG)
-	       << ACD_WRITE_CTL_SELECT_SHIFT)
-		| ACD_WRITE_CTL_UPDATE_EN;
-	writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
-	/* Ensure writes complete before polling */
-	clk_osm_acd_mb(c);
-
-	/* Poll write status register */
-	for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
-	     timeout -= 100) {
-		reg = readl_relaxed((char *)c->vbases[ACD_BASE]
-					+ ACD_WRITE_STATUS);
-		if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(ACD_READOUT_CFG))))
-			break;
-		ndelay(100);
-	}
-
-	if (!timeout) {
-		pr_err("local read timed out, offset=0x%x status=0x%x\n",
-			offset, reg);
-		return -ETIMEDOUT;
-	}
-
-	reg = readl_relaxed((char *)c->vbases[ACD_BASE] + ACD_READOUT);
-	return reg;
-}
-
-static int clk_osm_acd_local_write_reg(struct clk_osm *c, u32 val, u32 offset)
-{
-	u32 reg = 0;
-	int timeout;
-
-	if (offset >= ACD_MASTER_ONLY_REG_ADDR) {
-		pr_err("ACD register at offset=0x%x not transferrable\n",
-			offset);
-		return -EINVAL;
-	}
-
-	/* Clear write control register */
-	writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
-	/* Set select and update_en fields in write control register */
-	reg = (ACD_REG_RELATIVE_ADDR(offset) << ACD_WRITE_CTL_SELECT_SHIFT)
-		| ACD_WRITE_CTL_UPDATE_EN;
-	writel_relaxed(reg, (char *)c->vbases[ACD_BASE] + ACD_WRITE_CTL);
-
-	/* Ensure writes complete before polling */
-	clk_osm_acd_mb(c);
-
-	/* Poll write status register */
-	for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS; timeout > 0;
-	     timeout -= 100) {
-		reg = readl_relaxed((char *)c->vbases[ACD_BASE]
-					+ ACD_WRITE_STATUS);
-		if ((reg & (ACD_REG_RELATIVE_ADDR_BITMASK(offset))))
-			break;
-		ndelay(100);
-	}
-
-	if (!timeout) {
-		pr_err("local write timed out, offset=0x%x val=0x%x status=0x%x\n",
-			offset, val, reg);
-		return -ETIMEDOUT;
-	}
-
-	return 0;
-}
-
-static int clk_osm_acd_master_write_through_reg(struct clk_osm *c,
-						u32 val, u32 offset)
-{
-	writel_relaxed(val, (char *)c->vbases[ACD_BASE] + offset);
-
-	/* Ensure writes complete before transfer to local copy */
-	clk_osm_acd_mb(c);
-
-	return clk_osm_acd_local_write_reg(c, val, offset);
-}
-
-static int clk_osm_acd_auto_local_write_reg(struct clk_osm *c, u32 mask)
-{
-	u32 numregs, bitmask = mask;
-	u32 reg = 0;
-	int timeout;
-
-	/* count number of bits set in register mask */
-	for (numregs = 0; bitmask; numregs++)
-		bitmask &= bitmask - 1;
-
-	/* Program auto-transfer mask */
-	writel_relaxed(mask, (char *)c->vbases[ACD_BASE] + ACD_AUTOXFER_CFG);
-
-	/* Clear start field in auto-transfer register */
-	writel_relaxed(ACD_AUTOXFER_START_CLEAR,
-			(char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
-
-	/* Set start field in auto-transfer register */
-	writel_relaxed(ACD_AUTOXFER_START_SET,
-			(char *)c->vbases[ACD_BASE] + ACD_AUTOXFER);
-
-	/* Ensure writes complete before polling */
-	clk_osm_acd_mb(c);
-
-	/* Poll auto-transfer status register */
-	for (timeout = ACD_LOCAL_TRANSFER_TIMEOUT_NS * numregs;
-	     timeout > 0; timeout -= 100) {
-		reg = readl_relaxed((char *)c->vbases[ACD_BASE]
-					+ ACD_AUTOXFER_STATUS);
-		if (reg & AUTO_XFER_DONE_MASK)
-			break;
-		ndelay(100);
-	}
-
-	if (!timeout) {
-		pr_err("local register auto-transfer timed out, mask=0x%x registers=%d status=0x%x\n",
-			mask, numregs, reg);
-		return -ETIMEDOUT;
-	}
-
-	return 0;
-}
-
-static bool is_v2;
-static bool osm_tz_enabled;
+static bool is_sdm845v1;
 
 static inline struct clk_osm *to_clk_osm(struct clk_hw *_hw)
 {
 	return container_of(_hw, struct clk_osm, hw);
 }
 
-static inline void clk_osm_masked_write_reg(struct clk_osm *c, u32 val,
-					    u32 offset, u32 mask)
+static inline void clk_osm_write_reg(struct clk_osm *c, u32 val, u32 offset)
 {
-	u32 val2, orig_val;
-
-	val2 = orig_val = readl_relaxed((char *)c->vbases[OSM_BASE] + offset);
-	val2 &= ~mask;
-	val2 |= val & mask;
-
-	if (val2 != orig_val)
-		writel_relaxed(val2, (char *)c->vbases[OSM_BASE] + offset);
-}
-
-static inline void clk_osm_write_seq_reg(struct clk_osm *c, u32 val, u32 offset)
-{
-	writel_relaxed(val, (char *)c->vbases[SEQ_BASE] + offset);
-}
-
-static inline void clk_osm_write_reg(struct clk_osm *c, u32 val, u32 offset,
-					int base)
-{
-	writel_relaxed(val, (char *)c->vbases[base] + offset);
+	writel_relaxed(val, c->vbase + offset);
 }
 
 static inline int clk_osm_read_reg(struct clk_osm *c, u32 offset)
 {
-	return readl_relaxed((char *)c->vbases[OSM_BASE] + offset);
+	return readl_relaxed(c->vbase + offset);
 }
 
 static inline int clk_osm_read_reg_no_log(struct clk_osm *c, u32 offset)
 {
-	return readl_relaxed_no_log((char *)c->vbases[OSM_BASE] + offset);
+	return readl_relaxed_no_log(c->vbase + offset);
 }
 
-static inline int clk_osm_mb(struct clk_osm *c, int base)
+static inline int clk_osm_mb(struct clk_osm *c)
 {
-	return readl_relaxed_no_log((char *)c->vbases[base] + ENABLE_REG);
+	return readl_relaxed_no_log(c->vbase + ENABLE_REG);
 }
 
 static long clk_osm_list_rate(struct clk_hw *hw, unsigned int n,
@@ -517,6 +141,18 @@
 	return (req <= new && new < best) || (best < req && best < new);
 }
 
+static int clk_osm_search_table(struct osm_entry *table, int entries, long rate)
+{
+	int index;
+
+	for (index = 0; index < entries; index++) {
+		if (rate == table[index].frequency)
+			return index;
+	}
+
+	return -EINVAL;
+}
+
 static long clk_osm_round_rate(struct clk_hw *hw, unsigned long rate,
 				unsigned long *parent_rate)
 {
@@ -547,40 +183,62 @@
 	return rrate;
 }
 
-static int clk_osm_search_table(struct osm_entry *table, int entries, long rate)
+static int clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
+						unsigned long parent_rate)
 {
+	struct clk_osm *c = to_clk_osm(hw);
+	struct clk_hw *p_hw = clk_hw_get_parent(hw);
+	struct clk_osm *parent = to_clk_osm(p_hw);
 	int index = 0;
 
-	for (index = 0; index < entries; index++) {
-		if (rate == table[index].frequency)
-			return index;
+	if (!c || !parent)
+		return -EINVAL;
+
+	index = clk_osm_search_table(parent->osm_table,
+					parent->num_entries, rate);
+	if (index < 0) {
+		pr_err("cannot set %s to %lu\n", clk_hw_get_name(hw), rate);
+		return -EINVAL;
 	}
 
-	return -EINVAL;
-}
-
-static int clk_osm_enable(struct clk_hw *hw)
-{
-	struct clk_osm *cpuclk = to_clk_osm(hw);
-
-	clk_osm_masked_write_reg(cpuclk, ENABLE_OSM, ENABLE_REG, ENABLE_OSM);
+	clk_osm_write_reg(parent, index,
+				DCVS_PERF_STATE_DESIRED_REG(c->core_num,
+							is_sdm845v1));
 
 	/* Make sure the write goes through before proceeding */
-	clk_osm_mb(cpuclk, OSM_BASE);
-
-	/* Wait for 5us for OSM hardware to enable */
-	udelay(5);
-
-	pr_debug("OSM clk enabled for cluster=%d\n", cpuclk->cluster_num);
+	clk_osm_mb(parent);
 
 	return 0;
 }
 
-const struct clk_ops clk_ops_cpu_osm = {
-	.round_rate = clk_osm_round_rate,
-	.list_rate = clk_osm_list_rate,
-	.debug_init = clk_debug_measure_add,
-};
+static unsigned long clk_cpu_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct clk_osm *c = to_clk_osm(hw);
+	struct clk_hw *p_hw = clk_hw_get_parent(hw);
+	struct clk_osm *parent = to_clk_osm(p_hw);
+	int index = 0;
+
+	if (!c || !parent)
+		return -EINVAL;
+
+	index = clk_osm_read_reg(parent,
+				DCVS_PERF_STATE_DESIRED_REG(c->core_num,
+							is_sdm845v1));
+	return parent->osm_table[index].frequency;
+}
+
+static long clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *parent_rate)
+{
+	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
+
+	if (!parent_hw)
+		return -EINVAL;
+
+	*parent_rate = rate;
+	return clk_hw_round_rate(parent_hw, rate);
+}
 
 static int l3_clk_set_rate(struct clk_hw *hw, unsigned long rate,
 				    unsigned long parent_rate)
@@ -608,11 +266,11 @@
 	}
 	pr_debug("rate: %lu --> index %d\n", rate, index);
 
-	clk_osm_write_reg(cpuclk, index, DCVS_PERF_STATE_DESIRED_REG(0, is_v2),
-						OSM_BASE);
+	clk_osm_write_reg(cpuclk, index,
+				DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1));
 
 	/* Make sure the write goes through before proceeding */
-	clk_osm_mb(cpuclk, OSM_BASE);
+	clk_osm_mb(cpuclk);
 
 	return 0;
 }
@@ -626,7 +284,8 @@
 	if (!cpuclk)
 		return -EINVAL;
 
-	index = clk_osm_read_reg(cpuclk, DCVS_PERF_STATE_DESIRED_REG(0, is_v2));
+	index = clk_osm_read_reg(cpuclk,
+				DCVS_PERF_STATE_DESIRED_REG(0, is_sdm845v1));
 
 	pr_debug("%s: Index %d, freq %ld\n", __func__, index,
 				cpuclk->osm_table[index].frequency);
@@ -635,9 +294,7 @@
 	return cpuclk->osm_table[index].frequency;
 }
 
-
 static struct clk_ops clk_ops_l3_osm = {
-	.enable = clk_osm_enable,
 	.round_rate = clk_osm_round_rate,
 	.list_rate = clk_osm_list_rate,
 	.recalc_rate = l3_clk_recalc_rate,
@@ -645,18 +302,23 @@
 	.debug_init = clk_debug_measure_add,
 };
 
+static struct clk_ops clk_ops_core;
+static struct clk_ops clk_ops_cpu_osm;
+
 static struct clk_init_data osm_clks_init[] = {
 	[0] = {
 		.name = "l3_clk",
 		.parent_names = (const char *[]){ "bi_tcxo_ao" },
 		.num_parents = 1,
 		.ops = &clk_ops_l3_osm,
+		.vdd_class = &vdd_l3_mx_ao,
 	},
 	[1] = {
 		.name = "pwrcl_clk",
 		.parent_names = (const char *[]){ "bi_tcxo_ao" },
 		.num_parents = 1,
 		.ops = &clk_ops_cpu_osm,
+		.vdd_class = &vdd_pwrcl_mx_ao,
 	},
 	[2] = {
 		.name = "perfcl_clk",
@@ -668,14 +330,17 @@
 
 static struct clk_osm l3_clk = {
 	.cluster_num = 0,
+	.max_core_count = 4,
 	.hw.init = &osm_clks_init[0],
 };
 
 static DEFINE_CLK_VOTER(l3_cluster0_vote_clk, l3_clk, 0);
 static DEFINE_CLK_VOTER(l3_cluster1_vote_clk, l3_clk, 0);
+static DEFINE_CLK_VOTER(l3_misc_vote_clk, l3_clk, 0);
 
 static struct clk_osm pwrcl_clk = {
 	.cluster_num = 1,
+	.max_core_count = 4,
 	.hw.init = &osm_clks_init[1],
 };
 
@@ -687,7 +352,8 @@
 		.name = "cpu0_pwrcl_clk",
 		.parent_names = (const char *[]){ "pwrcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -699,7 +365,8 @@
 		.name = "cpu1_pwrcl_clk",
 		.parent_names = (const char *[]){ "pwrcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -711,7 +378,8 @@
 		.name = "cpu2_pwrcl_clk",
 		.parent_names = (const char *[]){ "pwrcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -723,12 +391,40 @@
 		.name = "cpu3_pwrcl_clk",
 		.parent_names = (const char *[]){ "pwrcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
+	},
+};
+
+static struct clk_osm cpu4_pwrcl_clk = {
+	.core_num = 4,
+	.total_cycle_counter = 0,
+	.prev_cycle_counter = 0,
+	.hw.init = &(struct clk_init_data){
+		.name = "cpu4_pwrcl_clk",
+		.parent_names = (const char *[]){ "pwrcl_clk" },
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
+	},
+};
+
+static struct clk_osm cpu5_pwrcl_clk = {
+	.core_num = 5,
+	.total_cycle_counter = 0,
+	.prev_cycle_counter = 0,
+	.hw.init = &(struct clk_init_data){
+		.name = "cpu5_pwrcl_clk",
+		.parent_names = (const char *[]){ "pwrcl_clk" },
+		.num_parents = 1,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
 static struct clk_osm perfcl_clk = {
 	.cluster_num = 2,
+	.max_core_count = 4,
 	.hw.init = &osm_clks_init[2],
 };
 
@@ -741,7 +437,8 @@
 		.name = "cpu4_perfcl_clk",
 		.parent_names = (const char *[]){ "perfcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -753,7 +450,8 @@
 		.name = "cpu5_perfcl_clk",
 		.parent_names = (const char *[]){ "perfcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -765,7 +463,8 @@
 		.name = "cpu6_perfcl_clk",
 		.parent_names = (const char *[]){ "perfcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
@@ -777,20 +476,16 @@
 		.name = "cpu7_perfcl_clk",
 		.parent_names = (const char *[]){ "perfcl_clk" },
 		.num_parents = 1,
-		.ops = &clk_dummy_ops,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_ops_core,
 	},
 };
 
-/*
- * Use the cpu* clocks only for writing to the PERF_STATE_DESIRED registers.
- * Note that we are currently NOT programming the APSS_LMH_GFMUX_CFG &
- * APSS_OSM_GFMUX_CFG registers.
- */
-
 static struct clk_hw *osm_qcom_clk_hws[] = {
 	[L3_CLK] = &l3_clk.hw,
 	[L3_CLUSTER0_VOTE_CLK] = &l3_cluster0_vote_clk.hw,
 	[L3_CLUSTER1_VOTE_CLK] = &l3_cluster1_vote_clk.hw,
+	[L3_MISC_VOTE_CLK] = &l3_misc_vote_clk.hw,
 	[PWRCL_CLK] = &pwrcl_clk.hw,
 	[CPU0_PWRCL_CLK] = &cpu0_pwrcl_clk.hw,
 	[CPU1_PWRCL_CLK] = &cpu1_pwrcl_clk.hw,
@@ -801,6 +496,19 @@
 	[CPU5_PERFCL_CLK] = &cpu5_perfcl_clk.hw,
 	[CPU6_PERFCL_CLK] = &cpu6_perfcl_clk.hw,
 	[CPU7_PERFCL_CLK] = &cpu7_perfcl_clk.hw,
+	[CPU4_PWRCL_CLK] = NULL,
+	[CPU5_PWRCL_CLK] = NULL,
+};
+
+static struct clk_osm *clk_cpu_map[] = {
+	&cpu0_pwrcl_clk,
+	&cpu1_pwrcl_clk,
+	&cpu2_pwrcl_clk,
+	&cpu3_pwrcl_clk,
+	&cpu4_perfcl_clk,
+	&cpu5_perfcl_clk,
+	&cpu6_perfcl_clk,
+	&cpu7_perfcl_clk,
 };
 
 static struct clk_osm *logical_cpu_to_clk(int cpu)
@@ -809,16 +517,6 @@
 	const u32 *cell;
 	u64 hwid;
 	static struct clk_osm *cpu_clk_map[NR_CPUS];
-	struct clk_osm *clk_cpu_map[] = {
-		&cpu0_pwrcl_clk,
-		&cpu1_pwrcl_clk,
-		&cpu2_pwrcl_clk,
-		&cpu3_pwrcl_clk,
-		&cpu4_perfcl_clk,
-		&cpu5_perfcl_clk,
-		&cpu6_perfcl_clk,
-		&cpu7_perfcl_clk,
-	};
 
 	if (!cpu_clk_map[cpu]) {
 		cpu_node = of_get_cpu_node(cpu, NULL);
@@ -892,13 +590,23 @@
 }
 
 static void
-osm_set_index(struct clk_osm *c, unsigned int index, unsigned int num)
+osm_set_index(struct clk_osm *c, unsigned int index)
 {
-	clk_osm_write_reg(c, index, DCVS_PERF_STATE_DESIRED_REG(num, is_v2),
-							OSM_BASE);
+	struct clk_hw *p_hw = clk_hw_get_parent(&c->hw);
+	struct clk_osm *parent = to_clk_osm(p_hw);
+	unsigned long rate = 0;
 
-	/* Make sure the write goes through before proceeding */
-	clk_osm_mb(c, OSM_BASE);
+	if (index >= OSM_TABLE_SIZE) {
+		pr_err("Passing an index (%u) that's greater than max (%d)\n",
+					index, OSM_TABLE_SIZE - 1);
+		return;
+	}
+
+	rate = parent->osm_table[index].frequency;
+	if (!rate)
+		return;
+
+	clk_set_rate(c->hw.clk, clk_round_rate(c->hw.clk, rate));
 }
 
 static int
@@ -906,7 +614,7 @@
 {
 	struct clk_osm *c = policy->driver_data;
 
-	osm_set_index(c, index, c->core_num);
+	osm_set_index(c, index);
 	return 0;
 }
 
@@ -921,7 +629,7 @@
 
 	c = policy->driver_data;
 	index = clk_osm_read_reg(c,
-			DCVS_PERF_STATE_DESIRED_REG(c->core_num, is_v2));
+			DCVS_PERF_STATE_DESIRED_REG(c->core_num, is_sdm845v1));
 	return policy->freq_table[index].frequency;
 }
 
@@ -947,7 +655,7 @@
 	}
 
 	parent = to_clk_osm(p_hw);
-	c->vbases[OSM_BASE] = parent->vbases[OSM_BASE];
+	c->vbase = parent->vbase;
 
 	p_hw = clk_hw_get_parent(p_hw);
 	if (!p_hw) {
@@ -975,7 +683,7 @@
 			table[i].frequency = xo_kHz * lval;
 		table[i].driver_data = table[i].frequency;
 
-		if (core_count != MAX_CORE_COUNT)
+		if (core_count != parent->max_core_count)
 			table[i].frequency = CPUFREQ_ENTRY_INVALID;
 
 		/* Two of the same frequencies means end of table */
@@ -999,10 +707,6 @@
 	}
 
 	policy->driver_data = c;
-
-	clk_osm_enable(&parent->hw);
-	udelay(300);
-
 	return 0;
 
 err:
@@ -1036,704 +740,6 @@
 	.boost_enabled	= true,
 };
 
-static inline int clk_osm_count_ns(struct clk_osm *c, u64 nsec)
-{
-	u64 temp;
-
-	temp = (u64)c->osm_clk_rate * nsec;
-	do_div(temp, 1000000000);
-
-	return temp;
-}
-
-static void clk_osm_program_mem_acc_regs(struct clk_osm *c)
-{
-	if (c->secure_init) {
-		clk_osm_write_seq_reg(c,
-				c->pbases[OSM_BASE] + MEMACC_CROSSOVER_VC,
-				DATA_MEM(57));
-		clk_osm_write_seq_reg(c, c->mem_acc_addr[0], DATA_MEM(48));
-		clk_osm_write_seq_reg(c, c->mem_acc_addr[1], DATA_MEM(49));
-		clk_osm_write_seq_reg(c, c->mem_acc_addr[2], DATA_MEM(50));
-		clk_osm_write_seq_reg(c, c->mem_acc_crossover_vc,
-							DATA_MEM(78));
-		clk_osm_write_seq_reg(c, c->mem_acc_level_vc[0], DATA_MEM(79));
-		clk_osm_write_seq_reg(c, c->mem_acc_level_vc[1], DATA_MEM(80));
-		/*
-		 * Note that DATA_MEM[81] -> DATA_MEM[89] values will be
-		 * confirmed post-si. Use a value of 1 for DATA_MEM[89] and
-		 * leave the rest of them as 0.
-		 */
-		clk_osm_write_seq_reg(c, 1, DATA_MEM(89));
-	} else {
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(78),
-						c->mem_acc_crossover_vc);
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(79),
-						c->mem_acc_level_vc[0]);
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(80),
-						c->mem_acc_level_vc[1]);
-	}
-}
-
-static void clk_osm_program_apm_regs(struct clk_osm *c)
-{
-	if (c == &l3_clk || c == &pwrcl_clk)
-		return;
-
-	/*
-	 * Program address of the control register used to configure
-	 * the Array Power Mux controller
-	 */
-	clk_osm_write_seq_reg(c, c->apm_mode_ctl, DATA_MEM(41));
-
-	/* Program address of controller status register */
-	clk_osm_write_seq_reg(c, c->apm_status_ctl, DATA_MEM(43));
-
-	/* Program address of crossover register */
-	clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + APM_CROSSOVER_VC,
-						DATA_MEM(44));
-
-	/* Program mode value to switch APM to VDD_APC */
-	clk_osm_write_seq_reg(c, APM_APC_MODE, DATA_MEM(72));
-
-	/* Program mode value to switch APM to VDD_MX */
-	clk_osm_write_seq_reg(c, APM_MX_MODE, DATA_MEM(73));
-
-	/* Program mask used to move into read_mask port */
-	clk_osm_write_seq_reg(c, APM_READ_DATA_MASK, DATA_MEM(74));
-
-	/* Value used to move into read_exp port */
-	clk_osm_write_seq_reg(c, APM_APC_READ_VAL, DATA_MEM(75));
-	clk_osm_write_seq_reg(c, APM_MX_READ_VAL, DATA_MEM(76));
-}
-
-static void clk_osm_do_additional_setup(struct clk_osm *c,
-					struct platform_device *pdev)
-{
-	if (!c->secure_init)
-		return;
-
-	dev_info(&pdev->dev, "Performing additional OSM setup due to lack of TZ for cluster=%d\n",
-						 c->cluster_num);
-
-	/* PLL L_VAL & post-div programming */
-	clk_osm_write_seq_reg(c, c->apcs_pll_min_freq, DATA_MEM(32));
-	clk_osm_write_seq_reg(c, c->l_val_base, DATA_MEM(33));
-	clk_osm_write_seq_reg(c, c->apcs_pll_user_ctl, DATA_MEM(34));
-	clk_osm_write_seq_reg(c, PLL_POST_DIV1, DATA_MEM(35));
-	clk_osm_write_seq_reg(c, PLL_POST_DIV2, DATA_MEM(36));
-
-	/* APM Programming */
-	clk_osm_program_apm_regs(c);
-
-	/* GFMUX Programming */
-	clk_osm_write_seq_reg(c, c->cfg_gfmux_addr, DATA_MEM(37));
-	clk_osm_write_seq_reg(c, 0x1, DATA_MEM(65));
-	clk_osm_write_seq_reg(c, 0x2, DATA_MEM(66));
-	clk_osm_write_seq_reg(c, 0x3, DATA_MEM(67));
-	clk_osm_write_seq_reg(c, 0x40000000, DATA_MEM(68));
-	clk_osm_write_seq_reg(c, 0x20000000, DATA_MEM(69));
-	clk_osm_write_seq_reg(c, 0x10000000, DATA_MEM(70));
-	clk_osm_write_seq_reg(c, 0x70000000, DATA_MEM(71));
-
-	/* Override programming */
-	clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] +
-			OVERRIDE_CLUSTER_IDLE_ACK, DATA_MEM(54));
-	clk_osm_write_seq_reg(c, 0x3, DATA_MEM(55));
-	clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + PDN_FSM_CTRL_REG,
-					DATA_MEM(40));
-	clk_osm_write_seq_reg(c, c->pbases[OSM_BASE] + REQ_GEN_FSM_STATUS,
-					DATA_MEM(60));
-	clk_osm_write_seq_reg(c, 0x10, DATA_MEM(61));
-	clk_osm_write_seq_reg(c, 0x70, DATA_MEM(62));
-	clk_osm_write_seq_reg(c, c->apcs_cbc_addr, DATA_MEM(112));
-	clk_osm_write_seq_reg(c, 0x2, DATA_MEM(113));
-
-	if (c == &perfcl_clk) {
-		int rc;
-		u32 isense_addr;
-
-		/* Performance cluster isense programming */
-		rc = of_property_read_u32(pdev->dev.of_node,
-				"qcom,perfcl-isense-addr", &isense_addr);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,perfcl-isense-addr property, rc=%d\n",
-				rc);
-			return;
-		}
-		clk_osm_write_seq_reg(c, isense_addr, DATA_MEM(45));
-		clk_osm_write_seq_reg(c, ISENSE_ON_DATA, DATA_MEM(46));
-		clk_osm_write_seq_reg(c, ISENSE_OFF_DATA, DATA_MEM(47));
-	}
-
-	clk_osm_write_seq_reg(c, c->ramp_ctl_addr, DATA_MEM(105));
-	clk_osm_write_seq_reg(c, CONSTANT_32, DATA_MEM(92));
-
-	/* Enable/disable CPR ramp settings */
-	clk_osm_write_seq_reg(c, 0x101C031, DATA_MEM(106));
-	clk_osm_write_seq_reg(c, 0x1010031, DATA_MEM(107));
-}
-
-static void clk_osm_setup_fsms(struct clk_osm *c)
-{
-	u32 val;
-
-	/* Voltage Reduction FSM */
-	if (c->red_fsm_en) {
-		val = clk_osm_read_reg(c, VMIN_REDUCTION_ENABLE_REG) | BIT(0);
-		val |= BVAL(6, 1, c->min_cpr_vc);
-		clk_osm_write_reg(c, val, VMIN_REDUCTION_ENABLE_REG,
-					OSM_BASE);
-
-		clk_osm_write_reg(c, clk_osm_count_ns(c, 10000),
-				  VMIN_REDUCTION_TIMER_REG, OSM_BASE);
-	}
-
-	/* Boost FSM */
-	if (c->boost_fsm_en) {
-		val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
-		val |= DELTA_DEX_VAL | CC_BOOST_FSM_EN | IGNORE_PLL_LOCK;
-		clk_osm_write_reg(c, val, PDN_FSM_CTRL_REG, OSM_BASE);
-
-		val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG0);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
-		val |= BVAL(31, 16, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
-		clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
-		val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG1);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
-		val |= BVAL(31, 16, clk_osm_count_ns(c, PLL_WAIT_LOCK_TIME_NS));
-		clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
-		val = clk_osm_read_reg(c, CC_BOOST_FSM_TIMERS_REG2);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
-		clk_osm_write_reg(c, val, CC_BOOST_FSM_TIMERS_REG2, OSM_BASE);
-	}
-
-	/* Safe Freq FSM */
-	if (c->safe_fsm_en) {
-		val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
-		clk_osm_write_reg(c, val | DCVS_BOOST_FSM_EN_MASK,
-				  PDN_FSM_CTRL_REG, OSM_BASE);
-
-		val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG0);
-		val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
-		clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
-		val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG1);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
-		clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
-		val = clk_osm_read_reg(c, DCVS_BOOST_FSM_TIMERS_REG2);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
-		clk_osm_write_reg(c, val, DCVS_BOOST_FSM_TIMERS_REG2, OSM_BASE);
-
-	}
-
-	/* Pulse Swallowing FSM */
-	if (c->ps_fsm_en) {
-		val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
-		clk_osm_write_reg(c, val | PS_BOOST_FSM_EN_MASK,
-					PDN_FSM_CTRL_REG, OSM_BASE);
-
-		val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG0);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
-		val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
-		clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG0, OSM_BASE);
-
-		val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG1);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, SAFE_FREQ_WAIT_NS));
-		val |= BVAL(31, 16, clk_osm_count_ns(c, 1000));
-		clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG1, OSM_BASE);
-
-		val = clk_osm_read_reg(c, PS_BOOST_FSM_TIMERS_REG2);
-		val |= BVAL(15, 0, clk_osm_count_ns(c, DEXT_DECREMENT_WAIT_NS));
-		clk_osm_write_reg(c, val, PS_BOOST_FSM_TIMERS_REG2, OSM_BASE);
-	}
-
-	/* PLL signal timing control */
-	if (c->boost_fsm_en || c->safe_fsm_en || c->ps_fsm_en)
-		clk_osm_write_reg(c, 0x2, BOOST_PROG_SYNC_DELAY_REG, OSM_BASE);
-
-	/* DCVS droop FSM - only if RCGwRC is not used for di/dt control */
-	if (c->droop_fsm_en) {
-		val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
-		clk_osm_write_reg(c, val | DCVS_DROOP_FSM_EN_MASK,
-					PDN_FSM_CTRL_REG, OSM_BASE);
-	}
-
-	if (c->ps_fsm_en || c->droop_fsm_en) {
-		clk_osm_write_reg(c, 0x1, DROOP_PROG_SYNC_DELAY_REG, OSM_BASE);
-		clk_osm_write_reg(c, clk_osm_count_ns(c, 100),
-					DROOP_RELEASE_TIMER_CTRL, OSM_BASE);
-		clk_osm_write_reg(c, clk_osm_count_ns(c, 150),
-					DCVS_DROOP_TIMER_CTRL, OSM_BASE);
-		/*
-		 * TODO: Check if DCVS_DROOP_CODE used is correct. Also check
-		 * if RESYNC_CTRL should be set for L3.
-		 */
-		val = BIT(31) | BVAL(22, 16, 0x2) | BVAL(6, 0, 0x8);
-		clk_osm_write_reg(c, val, DROOP_CTRL_REG, OSM_BASE);
-	}
-}
-
-static int clk_osm_set_llm_volt_policy(struct platform_device *pdev)
-{
-	struct device_node *of = pdev->dev.of_node;
-	u32 *array;
-	int rc = 0, val, regval;
-
-	array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
-			     GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	/*
-	 * Setup Timer to control how long OSM should wait before performing
-	 * DCVS when a LLM up voltage request is received.
-	 * Time is specified in us.
-	 */
-	rc = of_property_read_u32_array(of, "qcom,llm-volt-up-timer",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No LLM voltage up timer value, rc=%d\n",
-			rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val,
-					LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-						array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val,
-					LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&perfcl_clk,
-						array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val,
-					LLM_VOLTAGE_VOTE_INC_HYSTERESIS,
-					OSM_BASE);
-	}
-
-	/*
-	 * Setup Timer to control how long OSM should wait before performing
-	 * DCVS when a LLM down voltage request is received.
-	 * Time is specified in us.
-	 */
-	rc = of_property_read_u32_array(of, "qcom,llm-volt-down-timer",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No LLM Voltage down timer value: %d\n",
-									rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val,
-					LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-					       array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val,
-					LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&perfcl_clk,
-					array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val,
-					LLM_VOLTAGE_VOTE_DEC_HYSTERESIS,
-					OSM_BASE);
-	}
-
-	/* Enable or disable honoring of LLM Voltage requests */
-	rc = of_property_read_bool(pdev->dev.of_node,
-					"qcom,enable-llm-volt-vote");
-	if (rc) {
-		dev_dbg(&pdev->dev, "Honoring LLM Voltage requests\n");
-		val = 0;
-	} else
-		val = 1;
-
-	/* Enable or disable LLM VOLT DVCS */
-	regval = val | clk_osm_read_reg(&l3_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&l3_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-	regval = val | clk_osm_read_reg(&pwrcl_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&pwrcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-	regval = val | clk_osm_read_reg(&perfcl_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-
-	/* Wait for the writes to complete */
-	clk_osm_mb(&perfcl_clk, OSM_BASE);
-
-	devm_kfree(&pdev->dev, array);
-	return 0;
-}
-
-static int clk_osm_set_llm_freq_policy(struct platform_device *pdev)
-{
-	struct device_node *of = pdev->dev.of_node;
-	u32 *array;
-	int rc = 0, val, regval;
-
-	array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
-			     GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	/*
-	 * Setup Timer to control how long OSM should wait before performing
-	 * DCVS when a LLM up frequency request is received.
-	 * Time is specified in us.
-	 */
-	rc = of_property_read_u32_array(of, "qcom,llm-freq-up-timer", array,
-					MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "Unable to get CC up timer value: %d\n",
-			rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val, LLM_FREQ_VOTE_INC_HYSTERESIS,
-			OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-						array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val,
-					LLM_FREQ_VOTE_INC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&perfcl_clk,
-						array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val,
-					LLM_FREQ_VOTE_INC_HYSTERESIS,
-					OSM_BASE);
-	}
-
-	/*
-	 * Setup Timer to control how long OSM should wait before performing
-	 * DCVS when a LLM down frequency request is received.
-	 * Time is specified in us.
-	 */
-	rc = of_property_read_u32_array(of, "qcom,llm-freq-down-timer",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No LLM Frequency down timer value: %d\n",
-			rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk, array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val, LLM_FREQ_VOTE_DEC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-					       array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val,
-					LLM_FREQ_VOTE_DEC_HYSTERESIS, OSM_BASE);
-
-		val = clk_osm_count_ns(&perfcl_clk,
-					       array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val,
-					LLM_FREQ_VOTE_DEC_HYSTERESIS, OSM_BASE);
-	}
-
-	/* Enable or disable honoring of LLM frequency requests */
-	rc = of_property_read_bool(pdev->dev.of_node,
-					"qcom,enable-llm-freq-vote");
-	if (rc) {
-		dev_dbg(&pdev->dev, "Honoring LLM Frequency requests\n");
-		val = 0;
-	} else
-		val = BIT(1);
-
-	/* Enable or disable LLM FREQ DVCS */
-	regval = val | clk_osm_read_reg(&l3_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&l3_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-	regval = val | clk_osm_read_reg(&pwrcl_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&pwrcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-	regval = val | clk_osm_read_reg(&perfcl_clk, LLM_INTF_DCVS_DISABLE);
-	clk_osm_write_reg(&perfcl_clk, regval, LLM_INTF_DCVS_DISABLE, OSM_BASE);
-
-	/* Wait for the write to complete */
-	clk_osm_mb(&perfcl_clk, OSM_BASE);
-
-	devm_kfree(&pdev->dev, array);
-	return 0;
-}
-
-static int clk_osm_set_cc_policy(struct platform_device *pdev)
-{
-	int rc = 0, val;
-	u32 *array;
-	struct device_node *of = pdev->dev.of_node;
-
-	array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
-			     GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	rc = of_property_read_u32_array(of, "qcom,up-timer", array,
-					MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No up timer value, rc=%d\n",
-			 rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk,
-					array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val, SPM_CC_INC_HYSTERESIS,
-			OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-					array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_INC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&perfcl_clk,
-					array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val, SPM_CC_INC_HYSTERESIS,
-					OSM_BASE);
-	}
-
-	rc = of_property_read_u32_array(of, "qcom,down-timer",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No down timer value, rc=%d\n", rc);
-	} else {
-		val = clk_osm_count_ns(&l3_clk,
-				       array[l3_clk.cluster_num]);
-		clk_osm_write_reg(&l3_clk, val, SPM_CC_DEC_HYSTERESIS,
-					OSM_BASE);
-
-		val = clk_osm_count_ns(&pwrcl_clk,
-				       array[pwrcl_clk.cluster_num]);
-		clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_DEC_HYSTERESIS,
-					OSM_BASE);
-
-		clk_osm_count_ns(&perfcl_clk,
-				       array[perfcl_clk.cluster_num]);
-		clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DEC_HYSTERESIS,
-					OSM_BASE);
-	}
-
-	/* OSM index override for cluster PC */
-	rc = of_property_read_u32_array(of, "qcom,pc-override-index",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_dbg(&pdev->dev, "No PC override index value, rc=%d\n",
-			rc);
-		clk_osm_write_reg(&pwrcl_clk, 0, CC_ZERO_BEHAV_CTRL, OSM_BASE);
-		clk_osm_write_reg(&perfcl_clk, 0, CC_ZERO_BEHAV_CTRL,
-					OSM_BASE);
-	} else {
-		val = BVAL(6, 1, array[pwrcl_clk.cluster_num])
-			| ENABLE_OVERRIDE;
-		clk_osm_write_reg(&pwrcl_clk, val, CC_ZERO_BEHAV_CTRL,
-					OSM_BASE);
-		val = BVAL(6, 1, array[perfcl_clk.cluster_num])
-			| ENABLE_OVERRIDE;
-		clk_osm_write_reg(&perfcl_clk, val, CC_ZERO_BEHAV_CTRL,
-					OSM_BASE);
-	}
-
-	/* Wait for the writes to complete */
-	clk_osm_mb(&perfcl_clk, OSM_BASE);
-
-	rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-c3-active");
-	if (rc) {
-		dev_dbg(&pdev->dev, "Treat cores in C3 as active\n");
-
-		val = clk_osm_read_reg(&l3_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(2);
-		clk_osm_write_reg(&l3_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-
-		val = clk_osm_read_reg(&pwrcl_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(2);
-		clk_osm_write_reg(&pwrcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-
-		val = clk_osm_read_reg(&perfcl_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(2);
-		clk_osm_write_reg(&perfcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-	}
-
-	rc = of_property_read_bool(pdev->dev.of_node, "qcom,set-c2-active");
-	if (rc) {
-		dev_dbg(&pdev->dev, "Treat cores in C2 as active\n");
-
-		val = clk_osm_read_reg(&l3_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(1);
-		clk_osm_write_reg(&l3_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-
-		val = clk_osm_read_reg(&pwrcl_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(1);
-		clk_osm_write_reg(&pwrcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-
-		val = clk_osm_read_reg(&perfcl_clk, SPM_CORE_INACTIVE_MAPPING);
-		val &= ~BIT(1);
-		clk_osm_write_reg(&perfcl_clk, val, SPM_CORE_INACTIVE_MAPPING,
-					OSM_BASE);
-	}
-
-	rc = of_property_read_bool(pdev->dev.of_node, "qcom,disable-cc-dvcs");
-	if (rc) {
-		dev_dbg(&pdev->dev, "Disabling CC based DCVS\n");
-		val = 1;
-	} else
-		val = 0;
-
-	clk_osm_write_reg(&l3_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
-	clk_osm_write_reg(&pwrcl_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
-	clk_osm_write_reg(&perfcl_clk, val, SPM_CC_DCVS_DISABLE, OSM_BASE);
-
-	/* Wait for the writes to complete */
-	clk_osm_mb(&perfcl_clk, OSM_BASE);
-
-	devm_kfree(&pdev->dev, array);
-	return 0;
-}
-
-static void clk_osm_setup_cluster_pll(struct clk_osm *c)
-{
-	writel_relaxed(0x0, c->vbases[PLL_BASE] + PLL_MODE);
-	writel_relaxed(0x26, c->vbases[PLL_BASE] + PLL_L_VAL);
-	writel_relaxed(0x8, c->vbases[PLL_BASE] +
-			PLL_USER_CTRL);
-	writel_relaxed(0x20000AA8, c->vbases[PLL_BASE] +
-			PLL_CONFIG_CTL_LO);
-	writel_relaxed(0x000003D2, c->vbases[PLL_BASE] +
-			PLL_CONFIG_CTL_HI);
-	writel_relaxed(0x2, c->vbases[PLL_BASE] +
-			PLL_MODE);
-
-	/* Ensure writes complete before delaying */
-	clk_osm_mb(c, PLL_BASE);
-
-	udelay(PLL_WAIT_LOCK_TIME_US);
-
-	writel_relaxed(0x6, c->vbases[PLL_BASE] + PLL_MODE);
-
-	/* Ensure write completes before delaying */
-	clk_osm_mb(c, PLL_BASE);
-
-	usleep_range(50, 75);
-
-	writel_relaxed(0x7, c->vbases[PLL_BASE] + PLL_MODE);
-}
-
-static void clk_osm_misc_programming(struct clk_osm *c)
-{
-	u32 lval = 0xFF, val;
-	int i;
-
-	clk_osm_write_reg(c, BVAL(23, 16, 0xF), SPM_CORE_COUNT_CTRL,
-				OSM_BASE);
-	clk_osm_write_reg(c, PLL_MIN_LVAL, PLL_MIN_FREQ_REG, OSM_BASE);
-
-	/* Pattern to set/clear PLL lock in PDN_FSM_CTRL_REG */
-	val = clk_osm_read_reg(c, PDN_FSM_CTRL_REG);
-	if (c->secure_init) {
-		val |= IGNORE_PLL_LOCK;
-		clk_osm_write_seq_reg(c, val, DATA_MEM(108));
-		val &= ~IGNORE_PLL_LOCK;
-		clk_osm_write_seq_reg(c, val, DATA_MEM(109));
-		clk_osm_write_seq_reg(c, MIN_VCO_VAL, DATA_MEM(110));
-	} else {
-		val |= IGNORE_PLL_LOCK;
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(108), val);
-		val &= ~IGNORE_PLL_LOCK;
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(109), val);
-	}
-
-	/* Program LVAL corresponding to first turbo VC */
-	for (i = 0; i < c->num_entries; i++) {
-		if (c->osm_table[i].virtual_corner ==
-					c->mem_acc_level_vc[1]) {
-			lval = c->osm_table[i].freq_data & GENMASK(7, 0);
-			break;
-		}
-	}
-
-	if (c->secure_init)
-		clk_osm_write_seq_reg(c, lval, DATA_MEM(114));
-	else
-		scm_io_write(c->pbases[SEQ_BASE] + DATA_MEM(114), lval);
-
-}
-
-static int clk_osm_setup_hw_table(struct clk_osm *c)
-{
-	struct osm_entry *entry = c->osm_table;
-	int i;
-	u32 freq_val = 0, volt_val = 0, override_val = 0;
-	u32 table_entry_offset, last_mem_acc_level, last_virtual_corner = 0;
-
-	for (i = 0; i < OSM_TABLE_SIZE; i++) {
-		if (i < c->num_entries) {
-			freq_val = entry[i].freq_data;
-			volt_val = BVAL(27, 24, entry[i].mem_acc_level)
-				| BVAL(21, 16, entry[i].virtual_corner)
-				| BVAL(11, 0, entry[i].open_loop_volt);
-			override_val = entry[i].override_data;
-
-			if (last_virtual_corner && last_virtual_corner ==
-			    entry[i].virtual_corner && last_mem_acc_level !=
-			    entry[i].mem_acc_level) {
-				pr_err("invalid LUT entry at row=%d virtual_corner=%d, mem_acc_level=%d\n",
-				       i, entry[i].virtual_corner,
-				       entry[i].mem_acc_level);
-				return -EINVAL;
-			}
-			last_virtual_corner = entry[i].virtual_corner;
-			last_mem_acc_level = entry[i].mem_acc_level;
-		}
-
-		table_entry_offset = i * OSM_REG_SIZE;
-		clk_osm_write_reg(c, freq_val, FREQ_REG + table_entry_offset,
-					OSM_BASE);
-		clk_osm_write_reg(c, volt_val, VOLT_REG + table_entry_offset,
-					OSM_BASE);
-		clk_osm_write_reg(c, override_val, OVERRIDE_REG +
-				  table_entry_offset, OSM_BASE);
-	}
-
-	/* Make sure all writes go through */
-	clk_osm_mb(c, OSM_BASE);
-
-	return 0;
-}
-
-static void clk_osm_print_osm_table(struct clk_osm *c)
-{
-	int i;
-	struct osm_entry *table = c->osm_table;
-	u32 pll_src, pll_div, lval, core_count;
-
-	pr_debug("Index, Frequency, VC, OLV (mv), Core Count, PLL Src, PLL Div, L-Val, ACC Level\n");
-	for (i = 0; i < c->num_entries; i++) {
-		pll_src = (table[i].freq_data & GENMASK(31, 30)) >> 30;
-		pll_div = (table[i].freq_data & GENMASK(29, 28)) >> 28;
-		lval = table[i].freq_data & GENMASK(7, 0);
-		core_count = (table[i].freq_data & GENMASK(18, 16)) >> 16;
-
-		pr_debug("%3d, %11lu, %2u, %5u, %2u, %6u, %8u, %7u, %5u\n",
-			i,
-			table[i].frequency,
-			table[i].virtual_corner,
-			table[i].open_loop_volt,
-			core_count,
-			pll_src,
-			pll_div,
-			lval,
-			table[i].mem_acc_level);
-	}
-	pr_debug("APM threshold corner=%d, crossover corner=%d\n",
-			c->apm_threshold_vc, c->apm_crossover_vc);
-	pr_debug("MEM-ACC threshold corner=%d, crossover corner=%d\n",
-			c->mem_acc_threshold_vc, c->mem_acc_crossover_vc);
-}
-
 static u32 find_voltage(struct clk_osm *c, unsigned long rate)
 {
 	struct osm_entry *table = c->osm_table;
@@ -1749,12 +755,12 @@
 	return -EINVAL;
 }
 
-static int add_opp(struct clk_osm *c, struct device *dev)
+static int add_opp(struct clk_osm *c, struct device **device_list, int count)
 {
 	unsigned long rate = 0;
 	u32 uv;
 	long rc;
-	int j = 0;
+	int i, j = 0;
 	unsigned long min_rate = c->hw.init->rate_max[0];
 	unsigned long max_rate =
 			c->hw.init->rate_max[c->hw.init->num_rate_max - 1];
@@ -1767,10 +773,12 @@
 			return -EINVAL;
 		}
 
-		rc = dev_pm_opp_add(dev, rate, uv);
-		if (rc) {
-			pr_warn("failed to add OPP for %lu\n", rate);
-			return rc;
+		for (i = 0; i < count; i++) {
+			rc = dev_pm_opp_add(device_list[i], rate, uv);
+			if (rc) {
+				pr_warn("failed to add OPP for %lu\n", rate);
+				return rc;
+			}
 		}
 
 		/*
@@ -1779,13 +787,18 @@
 		 * this information will be used by thermal mitigation and the
 		 * scheduler.
 		 */
-		if (rate == min_rate)
-			pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
-				rate, uv, dev_name(dev));
+		if (rate == min_rate) {
+			for (i = 0; i < count; i++) {
+				pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+					rate, uv, dev_name(device_list[i]));
+			}
+		}
 
 		if (rate == max_rate && max_rate != min_rate) {
-			pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
-				rate, uv, dev_name(dev));
+			for (i = 0; i < count; i++) {
+				pr_info("Set OPP pair (%lu Hz, %d uv) on %s\n",
+					rate, uv, dev_name(device_list[i]));
+			}
 			break;
 		}
 
@@ -1795,14 +808,70 @@
 	return 0;
 }
 
+static int derive_device_list(struct device **device_list,
+				struct device_node *np,
+				char *phandle_name, int count)
+{
+	int i;
+	struct platform_device *pdev;
+	struct device_node *dev_node;
+
+	for (i = 0; i < count; i++) {
+		dev_node = of_parse_phandle(np, phandle_name, i);
+		if (!dev_node) {
+			pr_err("Unable to get device_node pointer for opp-handle (%s)\n",
+					phandle_name);
+			return -ENODEV;
+		}
+
+		pdev = of_find_device_by_node(dev_node);
+		if (!pdev) {
+			pr_err("Unable to find platform_device node for opp-handle (%s)\n",
+						phandle_name);
+			return -ENODEV;
+		}
+		device_list[i] = &pdev->dev;
+	}
+	return 0;
+}
+
+static void populate_l3_opp_table(struct device_node *np, char *phandle_name)
+{
+	struct device **device_list;
+	int len, count, ret = 0;
+
+	if (of_find_property(np, phandle_name, &len)) {
+		count = len / sizeof(u32);
+
+		device_list = kcalloc(count, sizeof(struct device *),
+							GFP_KERNEL);
+		if (!device_list)
+			return;
+
+		ret = derive_device_list(device_list, np, phandle_name, count);
+		if (ret < 0) {
+			pr_err("Failed to fill device_list for %s\n",
+							phandle_name);
+			return;
+		}
+	} else {
+		pr_debug("Unable to find %s\n", phandle_name);
+		return;
+	}
+
+	if (add_opp(&l3_clk, device_list, count))
+		pr_err("Failed to add OPP levels for %s\n", phandle_name);
+
+	kfree(device_list);
+}
+
 static void populate_opp_table(struct platform_device *pdev)
 {
 	int cpu;
 	struct device *cpu_dev;
 	struct clk_osm *c, *parent;
 	struct clk_hw *hw_parent;
-	struct device_node *l3_node_0, *l3_node_4;
-	struct platform_device *l3_dev_0, *l3_dev_4;
+	struct device_node *np = pdev->dev.of_node;
 
 	for_each_possible_cpu(cpu) {
 		c = logical_cpu_to_clk(cpu);
@@ -1815,40 +884,12 @@
 		parent = to_clk_osm(hw_parent);
 		cpu_dev = get_cpu_device(cpu);
 		if (cpu_dev)
-			if (add_opp(parent, cpu_dev))
+			if (add_opp(parent, &cpu_dev, 1))
 				pr_err("Failed to add OPP levels for %s\n",
 					dev_name(cpu_dev));
 	}
 
-	l3_node_0 = of_parse_phandle(pdev->dev.of_node, "l3-dev0", 0);
-	if (!l3_node_0) {
-		pr_err("can't find the L3 cluster 0 dt node\n");
-		return;
-	}
-
-	l3_dev_0 = of_find_device_by_node(l3_node_0);
-	if (!l3_dev_0) {
-		pr_err("can't find the L3 cluster 0 dt device\n");
-		return;
-	}
-
-	if (add_opp(&l3_clk, &l3_dev_0->dev))
-		pr_err("Failed to add OPP levels for L3 cluster 0\n");
-
-	l3_node_4 = of_parse_phandle(pdev->dev.of_node, "l3-dev4", 0);
-	if (!l3_node_4) {
-		pr_err("can't find the L3 cluster 1 dt node\n");
-		return;
-	}
-
-	l3_dev_4 = of_find_device_by_node(l3_node_4);
-	if (!l3_dev_4) {
-		pr_err("can't find the L3 cluster 1 dt device\n");
-		return;
-	}
-
-	if (add_opp(&l3_clk, &l3_dev_4->dev))
-		pr_err("Failed to add OPP levels for L3 cluster 1\n");
+	populate_l3_opp_table(np, "l3-devs");
 }
 
 static u64 clk_osm_get_cpu_cycle_counter(int cpu)
@@ -1873,7 +914,7 @@
 	 */
 	core_num = parent->per_core_dcvs ? c->core_num : 0;
 	val = clk_osm_read_reg_no_log(parent,
-			OSM_CYCLE_COUNTER_STATUS_REG(core_num, is_v2));
+			OSM_CYCLE_COUNTER_STATUS_REG(core_num, is_sdm845v1));
 
 	if (val < c->prev_cycle_counter) {
 		/* Handle counter overflow */
@@ -1890,199 +931,10 @@
 	return cycle_counter_ret;
 }
 
-static void clk_osm_setup_cycle_counters(struct clk_osm *c)
-{
-	u32 ratio = c->osm_clk_rate;
-	u32 val = 0;
-
-	/* Enable cycle counter */
-	val = BIT(0);
-	/* Setup OSM clock to XO ratio */
-	do_div(ratio, c->xo_clk_rate);
-	val |= BVAL(5, 1, ratio - 1) | OSM_CYCLE_COUNTER_USE_XO_EDGE_EN;
-
-	clk_osm_write_reg(c, val, OSM_CYCLE_COUNTER_CTRL_REG, OSM_BASE);
-	pr_debug("OSM to XO clock ratio: %d\n", ratio);
-}
-
-static int clk_osm_resolve_crossover_corners(struct clk_osm *c,
-					struct platform_device *pdev)
-{
-	struct regulator *regulator = c->vdd_reg;
-	int count, vc, i, memacc_threshold, apm_threshold;
-	int rc = 0;
-	u32 corner_volt;
-
-	if (c == &l3_clk || c == &pwrcl_clk)
-		return rc;
-
-	rc = of_property_read_u32(pdev->dev.of_node,
-				  "qcom,perfcl-apcs-apm-threshold-voltage",
-				  &apm_threshold);
-	if (rc) {
-		pr_err("qcom,perfcl-apcs-apm-threshold-voltage property not specified\n");
-		return rc;
-	}
-
-	rc = of_property_read_u32(pdev->dev.of_node,
-				  "qcom,perfcl-apcs-mem-acc-threshold-voltage",
-				  &memacc_threshold);
-	if (rc) {
-		pr_err("qcom,perfcl-apcs-mem-acc-threshold-voltage property not specified\n");
-		return rc;
-	}
-
-	/*
-	 * Initialize VC settings in case none of them go above the voltage
-	 * limits
-	 */
-	c->apm_threshold_vc = c->apm_crossover_vc = c->mem_acc_crossover_vc =
-				c->mem_acc_threshold_vc = MAX_VC;
-
-	count = regulator_count_voltages(regulator);
-	if (count < 0) {
-		pr_err("Failed to get the number of virtual corners supported\n");
-		return count;
-	}
-
-	c->apm_crossover_vc = count - 2;
-	c->mem_acc_crossover_vc = count - 1;
-
-	for (i = 0; i < OSM_TABLE_SIZE; i++) {
-		vc = c->osm_table[i].virtual_corner + 1;
-		corner_volt = regulator_list_corner_voltage(regulator, vc);
-
-		if (c->apm_threshold_vc == MAX_VC &&
-				corner_volt >= apm_threshold)
-			c->apm_threshold_vc = c->osm_table[i].virtual_corner;
-
-		if (c->mem_acc_threshold_vc == MAX_VC &&
-				corner_volt >= memacc_threshold)
-			c->mem_acc_threshold_vc =
-				c->osm_table[i].virtual_corner;
-	}
-
-	return rc;
-}
-
-static int clk_osm_resolve_open_loop_voltages(struct clk_osm *c)
-{
-	struct regulator *regulator = c->vdd_reg;
-	u32 vc, mv;
-	int i;
-
-	for (i = 0; i < OSM_TABLE_SIZE; i++) {
-		vc = c->osm_table[i].virtual_corner + 1;
-		/* Voltage is in uv. Convert to mv */
-		mv = regulator_list_corner_voltage(regulator, vc) / 1000;
-		c->osm_table[i].open_loop_volt = mv;
-	}
-
-	return 0;
-}
-
-static int clk_osm_get_lut(struct platform_device *pdev,
-			   struct clk_osm *c, char *prop_name)
-{
-	struct device_node *of = pdev->dev.of_node;
-	int prop_len, total_elems, num_rows, i, j, k;
-	int rc = 0;
-	u32 *array;
-	u32 *fmax_temp;
-	u32 data;
-	unsigned long abs_fmax = 0;
-	bool last_entry = false;
-
-	if (!of_find_property(of, prop_name, &prop_len)) {
-		dev_err(&pdev->dev, "missing %s\n", prop_name);
-		return -EINVAL;
-	}
-
-	total_elems = prop_len / sizeof(u32);
-	if (total_elems % NUM_FIELDS) {
-		dev_err(&pdev->dev, "bad length %d\n", prop_len);
-		return -EINVAL;
-	}
-
-	num_rows = total_elems / NUM_FIELDS;
-
-	fmax_temp = devm_kzalloc(&pdev->dev, num_rows * sizeof(unsigned long),
-					GFP_KERNEL);
-	if (!fmax_temp)
-		return -ENOMEM;
-
-	array = devm_kzalloc(&pdev->dev, prop_len, GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	rc = of_property_read_u32_array(of, prop_name, array, total_elems);
-	if (rc) {
-		dev_err(&pdev->dev, "Unable to parse OSM table, rc=%d\n", rc);
-		goto exit;
-	}
-
-	pr_debug("%s: Entries in Table: %d\n", __func__, num_rows);
-	c->num_entries = num_rows;
-	if (c->num_entries > OSM_TABLE_SIZE) {
-		pr_err("LUT entries %d exceed maximum size %d\n",
-		       c->num_entries, OSM_TABLE_SIZE);
-		return -EINVAL;
-	}
-
-	for (i = 0, j = 0, k = 0; j < OSM_TABLE_SIZE; j++) {
-		c->osm_table[j].frequency = array[i + FREQ];
-		c->osm_table[j].freq_data = array[i + FREQ_DATA];
-		c->osm_table[j].override_data = array[i + PLL_OVERRIDES];
-		c->osm_table[j].mem_acc_level = array[i + MEM_ACC_LEVEL];
-		/* Voltage corners are 0 based in the OSM LUT */
-		c->osm_table[j].virtual_corner = array[i + VIRTUAL_CORNER] - 1;
-		pr_debug("index=%d freq=%ld virtual_corner=%d freq_data=0x%x override_data=0x%x mem_acc_level=0x%x\n",
-			 j, c->osm_table[j].frequency,
-			 c->osm_table[j].virtual_corner,
-			 c->osm_table[j].freq_data,
-			 c->osm_table[j].override_data,
-			 c->osm_table[j].mem_acc_level);
-
-		data = (array[i + FREQ_DATA] & GENMASK(29, 28)) >> 28;
-		if (j && !c->min_cpr_vc && !data)
-			c->min_cpr_vc = c->osm_table[j].virtual_corner;
-
-		data = (array[i + FREQ_DATA] & GENMASK(18, 16)) >> 16;
-		if (!last_entry && data == MAX_CORE_COUNT) {
-			fmax_temp[k] = array[i];
-			k++;
-		}
-
-		if (i < total_elems - NUM_FIELDS)
-			i += NUM_FIELDS;
-		else {
-			abs_fmax = array[i];
-			last_entry = true;
-		}
-	}
-	fmax_temp[k] = abs_fmax;
-
-	osm_clks_init[c->cluster_num].rate_max = devm_kzalloc(&pdev->dev,
-						 k * sizeof(unsigned long),
-						       GFP_KERNEL);
-	if (!osm_clks_init[c->cluster_num].rate_max) {
-		rc = -ENOMEM;
-		goto exit;
-	}
-
-	for (i = 0; i < k; i++)
-		osm_clks_init[c->cluster_num].rate_max[i] = fmax_temp[i];
-
-	osm_clks_init[c->cluster_num].num_rate_max = k;
-exit:
-	devm_kfree(&pdev->dev, fmax_temp);
-	devm_kfree(&pdev->dev, array);
-	return rc;
-}
-
 static int clk_osm_read_lut(struct platform_device *pdev, struct clk_osm *c)
 {
 	u32 data, src, lval, i, j = OSM_TABLE_SIZE;
+	struct clk_vdd_class *vdd = osm_clks_init[c->cluster_num].vdd_class;
 
 	for (i = 0; i < OSM_TABLE_SIZE; i++) {
 		data = clk_osm_read_reg(c, FREQ_REG + i * OSM_REG_SIZE);
@@ -2115,6 +967,29 @@
 	if (!osm_clks_init[c->cluster_num].rate_max)
 		return -ENOMEM;
 
+	if (vdd) {
+		vdd->level_votes = devm_kcalloc(&pdev->dev, j,
+					sizeof(*vdd->level_votes), GFP_KERNEL);
+		if (!vdd->level_votes)
+			return -ENOMEM;
+
+		vdd->vdd_uv = devm_kcalloc(&pdev->dev, j, sizeof(*vdd->vdd_uv),
+								GFP_KERNEL);
+		if (!vdd->vdd_uv)
+			return -ENOMEM;
+
+		for (i = 0; i < j; i++) {
+			if (c->osm_table[i].frequency < c->mx_turbo_freq ||
+								(c->cpr_rc > 1))
+				vdd->vdd_uv[i] = RPMH_REGULATOR_LEVEL_NOM;
+			else
+				vdd->vdd_uv[i] = RPMH_REGULATOR_LEVEL_TURBO;
+		}
+		vdd->num_levels = j;
+		vdd->cur_level = j;
+		vdd->use_max_uV = true;
+	}
+
 	for (i = 0; i < j; i++)
 		osm_clks_init[c->cluster_num].rate_max[i] =
 					c->osm_table[i].frequency;
@@ -2123,494 +998,9 @@
 	return 0;
 }
 
-static int clk_osm_parse_acd_dt_configs(struct platform_device *pdev)
-{
-	struct device_node *of = pdev->dev.of_node;
-	u32 *array;
-	int rc = 0;
-
-	array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
-				GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	l3_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-				"l3_acd") != NULL ? true : false;
-	pwrcl_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-				"pwrcl_acd") != NULL ? true : false;
-	perfcl_clk.acd_init = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-				"perfcl_acd") != NULL ? true : false;
-
-	if (pwrcl_clk.acd_init || perfcl_clk.acd_init || l3_clk.acd_init) {
-		rc = of_property_read_u32_array(of, "qcom,acdtd-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdtd-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_td = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_td = array[perfcl_clk.cluster_num];
-		l3_clk.acd_td = array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdcr-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdcr-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_cr = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_cr = array[perfcl_clk.cluster_num];
-		l3_clk.acd_cr = array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdsscr-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdsscr-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_sscr = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_sscr = array[perfcl_clk.cluster_num];
-		l3_clk.acd_sscr =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdextint0-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdextint0-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_extint0_cfg = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_extint0_cfg = array[perfcl_clk.cluster_num];
-		l3_clk.acd_extint0_cfg =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdextint1-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdextint1-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_extint1_cfg = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_extint1_cfg = array[perfcl_clk.cluster_num];
-		l3_clk.acd_extint1_cfg =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdautoxfer-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdautoxfer-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-
-		pwrcl_clk.acd_autoxfer_ctl = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_autoxfer_ctl = array[perfcl_clk.cluster_num];
-		l3_clk.acd_autoxfer_ctl =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdavg-init",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdavg-init property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-		pwrcl_clk.acd_avg_init = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_avg_init = array[perfcl_clk.cluster_num];
-		l3_clk.acd_avg_init =  array[l3_clk.cluster_num];
-	}
-
-	if (pwrcl_clk.acd_avg_init || perfcl_clk.acd_avg_init ||
-	    l3_clk.acd_avg_init) {
-		rc = of_property_read_u32_array(of, "qcom,acdavgcfg0-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdavgcfg0-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-		pwrcl_clk.acd_avg_cfg0 = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_avg_cfg0 = array[perfcl_clk.cluster_num];
-		l3_clk.acd_avg_cfg0 =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdavgcfg1-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdavgcfg1-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-		pwrcl_clk.acd_avg_cfg1 = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_avg_cfg1 = array[perfcl_clk.cluster_num];
-		l3_clk.acd_avg_cfg1 =  array[l3_clk.cluster_num];
-
-		rc = of_property_read_u32_array(of, "qcom,acdavgcfg2-val",
-						array, MAX_CLUSTER_CNT);
-		if (rc) {
-			dev_err(&pdev->dev, "unable to find qcom,acdavgcfg2-val property, rc=%d\n",
-				rc);
-			return -EINVAL;
-		}
-		pwrcl_clk.acd_avg_cfg2 = array[pwrcl_clk.cluster_num];
-		perfcl_clk.acd_avg_cfg2 = array[perfcl_clk.cluster_num];
-		l3_clk.acd_avg_cfg2 =  array[l3_clk.cluster_num];
-	}
-
-	devm_kfree(&pdev->dev, array);
-	return rc;
-}
-
-static int clk_osm_parse_dt_configs(struct platform_device *pdev)
-{
-	struct device_node *of = pdev->dev.of_node;
-	u32 *array;
-	char memacc_str[40];
-	int rc = 0;
-	struct resource *res;
-
-	array = devm_kzalloc(&pdev->dev, MAX_CLUSTER_CNT * sizeof(u32),
-			     GFP_KERNEL);
-	if (!array)
-		return -ENOMEM;
-
-	rc = of_property_read_u32_array(of, "qcom,l-val-base",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,l-val-base property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.l_val_base = array[l3_clk.cluster_num];
-	pwrcl_clk.l_val_base = array[pwrcl_clk.cluster_num];
-	perfcl_clk.l_val_base = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apcs-pll-user-ctl",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apcs-pll-user-ctl property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.apcs_pll_user_ctl = array[l3_clk.cluster_num];
-	pwrcl_clk.apcs_pll_user_ctl = array[pwrcl_clk.cluster_num];
-	perfcl_clk.apcs_pll_user_ctl = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apcs-pll-min-freq",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apcs-pll-min-freq property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.apcs_pll_min_freq = array[l3_clk.cluster_num];
-	pwrcl_clk.apcs_pll_min_freq = array[pwrcl_clk.cluster_num];
-	perfcl_clk.apcs_pll_min_freq = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apm-mode-ctl",
-				  array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apm-mode-ctl property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.apm_mode_ctl = array[l3_clk.cluster_num];
-	pwrcl_clk.apm_mode_ctl = array[pwrcl_clk.cluster_num];
-	perfcl_clk.apm_mode_ctl = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apm-status-ctrl",
-				  array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apm-status-ctrl property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.apm_status_ctl = array[l3_clk.cluster_num];
-	pwrcl_clk.apm_status_ctl = array[pwrcl_clk.cluster_num];
-	perfcl_clk.apm_status_ctl = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,cfg-gfmux-addr",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,cfg-gfmux-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.cfg_gfmux_addr = array[l3_clk.cluster_num];
-	pwrcl_clk.cfg_gfmux_addr = array[pwrcl_clk.cluster_num];
-	perfcl_clk.cfg_gfmux_addr = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apcs-cbc-addr",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apcs-cbc-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.apcs_cbc_addr = array[l3_clk.cluster_num];
-	pwrcl_clk.apcs_cbc_addr = array[pwrcl_clk.cluster_num];
-	perfcl_clk.apcs_cbc_addr = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32_array(of, "qcom,apcs-ramp-ctl-addr",
-					array, MAX_CLUSTER_CNT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,apcs-ramp-ctl-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.ramp_ctl_addr = array[l3_clk.cluster_num];
-	pwrcl_clk.ramp_ctl_addr = array[pwrcl_clk.cluster_num];
-	perfcl_clk.ramp_ctl_addr = array[perfcl_clk.cluster_num];
-
-	rc = of_property_read_u32(of, "qcom,xo-clk-rate",
-				  &pwrcl_clk.xo_clk_rate);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,xo-clk-rate property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	l3_clk.xo_clk_rate = perfcl_clk.xo_clk_rate = pwrcl_clk.xo_clk_rate;
-
-	rc = of_property_read_u32(of, "qcom,osm-clk-rate",
-				  &pwrcl_clk.osm_clk_rate);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,osm-clk-rate property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-	l3_clk.osm_clk_rate = perfcl_clk.osm_clk_rate = pwrcl_clk.osm_clk_rate;
-
-	rc = of_property_read_u32(of, "qcom,cc-reads",
-				  &pwrcl_clk.cycle_counter_reads);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,cc-reads property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-	l3_clk.cycle_counter_reads = perfcl_clk.cycle_counter_reads =
-			pwrcl_clk.cycle_counter_reads;
-
-	rc = of_property_read_u32(of, "qcom,cc-delay",
-				  &pwrcl_clk.cycle_counter_delay);
-	if (rc)
-		dev_dbg(&pdev->dev, "no delays between cycle counter reads\n");
-	else
-		l3_clk.cycle_counter_delay = perfcl_clk.cycle_counter_delay =
-			pwrcl_clk.cycle_counter_delay;
-
-	rc = of_property_read_u32(of, "qcom,cc-factor",
-				  &pwrcl_clk.cycle_counter_factor);
-	if (rc)
-		dev_dbg(&pdev->dev, "no factor specified for cycle counter estimation\n");
-	else
-		l3_clk.cycle_counter_factor = perfcl_clk.cycle_counter_factor =
-			pwrcl_clk.cycle_counter_factor;
-
-	l3_clk.red_fsm_en = perfcl_clk.red_fsm_en = pwrcl_clk.red_fsm_en =
-		of_property_read_bool(of, "qcom,red-fsm-en");
-
-	l3_clk.boost_fsm_en = perfcl_clk.boost_fsm_en =
-		pwrcl_clk.boost_fsm_en =
-		of_property_read_bool(of, "qcom,boost-fsm-en");
-
-	l3_clk.safe_fsm_en = perfcl_clk.safe_fsm_en = pwrcl_clk.safe_fsm_en =
-		of_property_read_bool(of, "qcom,safe-fsm-en");
-
-	l3_clk.ps_fsm_en = perfcl_clk.ps_fsm_en = pwrcl_clk.ps_fsm_en =
-		of_property_read_bool(of, "qcom,ps-fsm-en");
-
-	l3_clk.droop_fsm_en = perfcl_clk.droop_fsm_en =
-		pwrcl_clk.droop_fsm_en =
-		of_property_read_bool(of, "qcom,droop-fsm-en");
-
-	devm_kfree(&pdev->dev, array);
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"l3_sequencer");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for l3_sequencer\n");
-		return -ENOMEM;
-	}
-
-	l3_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
-	l3_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
-						  resource_size(res));
-
-	if (!l3_clk.vbases[SEQ_BASE]) {
-		dev_err(&pdev->dev, "Unable to map l3_sequencer base\n");
-		return -ENOMEM;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"pwrcl_sequencer");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for pwrcl_sequencer\n");
-		return -ENOMEM;
-	}
-
-	pwrcl_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
-	pwrcl_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
-						  resource_size(res));
-
-	if (!pwrcl_clk.vbases[SEQ_BASE]) {
-		dev_err(&pdev->dev, "Unable to map pwrcl_sequencer base\n");
-		return -ENOMEM;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"perfcl_sequencer");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for perfcl_sequencer\n");
-		return -ENOMEM;
-	}
-
-	perfcl_clk.pbases[SEQ_BASE] = (unsigned long)res->start;
-	perfcl_clk.vbases[SEQ_BASE] = devm_ioremap(&pdev->dev, res->start,
-						  resource_size(res));
-
-	if (!perfcl_clk.vbases[SEQ_BASE]) {
-		dev_err(&pdev->dev, "Unable to map perfcl_sequencer base\n");
-		return -ENOMEM;
-	}
-
-	snprintf(memacc_str, ARRAY_SIZE(memacc_str),
-			"qcom,l3-memacc-level-vc-bin%d", l3_clk.speedbin);
-	rc = of_property_read_u32_array(of, memacc_str, l3_clk.mem_acc_level_vc,
-			MEM_ACC_LEVELS_LUT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
-						memacc_str, rc);
-		return rc;
-	}
-
-	snprintf(memacc_str, ARRAY_SIZE(memacc_str),
-			"qcom,pwrcl-memacc-level-vc-bin%d", pwrcl_clk.speedbin);
-	rc = of_property_read_u32_array(of, memacc_str,
-			pwrcl_clk.mem_acc_level_vc, MEM_ACC_LEVELS_LUT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
-			memacc_str, rc);
-		return rc;
-	}
-
-	snprintf(memacc_str, ARRAY_SIZE(memacc_str),
-		"qcom,perfcl-memacc-level-vc-bin%d", pwrcl_clk.speedbin);
-	rc = of_property_read_u32_array(of, memacc_str,
-			perfcl_clk.mem_acc_level_vc, MEM_ACC_LEVELS_LUT);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find %s property, rc=%d\n",
-			memacc_str, rc);
-		return rc;
-	}
-
-	l3_clk.secure_init = perfcl_clk.secure_init = pwrcl_clk.secure_init =
-		of_property_read_bool(pdev->dev.of_node, "qcom,osm-no-tz");
-
-	if (!pwrcl_clk.secure_init)
-		return rc;
-
-	rc = of_property_read_u32_array(of, "qcom,l3-mem-acc-addr",
-					l3_clk.mem_acc_addr, MEM_ACC_ADDRS);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,l3-mem-acc-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	rc = of_property_read_u32_array(of, "qcom,pwrcl-mem-acc-addr",
-					pwrcl_clk.mem_acc_addr, MEM_ACC_ADDRS);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,pwrcl-mem-acc-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	rc = of_property_read_u32_array(of, "qcom,perfcl-mem-acc-addr",
-					perfcl_clk.mem_acc_addr, MEM_ACC_ADDRS);
-	if (rc) {
-		dev_err(&pdev->dev, "unable to find qcom,perfcl-mem-acc-addr property, rc=%d\n",
-			rc);
-		return -EINVAL;
-	}
-
-	return rc;
-}
-
-static int clk_osm_acd_resources_init(struct platform_device *pdev)
-{
-	struct resource *res;
-	unsigned long pbase;
-	void *vbase;
-	int rc = 0;
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"pwrcl_acd");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map pwrcl_acd base\n");
-			return -ENOMEM;
-		}
-		pwrcl_clk.pbases[ACD_BASE] = pbase;
-		pwrcl_clk.vbases[ACD_BASE] = vbase;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"perfcl_acd");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map perfcl_acd base\n");
-			return -ENOMEM;
-		}
-		perfcl_clk.pbases[ACD_BASE] = pbase;
-		perfcl_clk.vbases[ACD_BASE] = vbase;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-						"l3_acd");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-					resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map l3_acd base\n");
-			return -ENOMEM;
-		}
-		l3_clk.pbases[ACD_BASE] = pbase;
-		l3_clk.vbases[ACD_BASE] = vbase;
-	}
-	return rc;
-}
-
 static int clk_osm_resources_init(struct platform_device *pdev)
 {
-	struct device_node *node;
 	struct resource *res;
-	unsigned long pbase;
-	int rc = 0;
-	void *vbase;
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"osm_l3_base");
@@ -2620,21 +1010,14 @@
 		return -ENOMEM;
 	}
 
-	l3_clk.pbases[OSM_BASE] = (unsigned long)res->start;
-	l3_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
-						  resource_size(res));
+	l3_clk.pbase = (unsigned long)res->start;
+	l3_clk.vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 
-	if (!l3_clk.vbases[OSM_BASE]) {
+	if (!l3_clk.vbase) {
 		dev_err(&pdev->dev, "Unable to map osm_l3_base base\n");
 		return -ENOMEM;
 	}
 
-	/* Check if OSM has been enabled already by trustzone.  */
-	if (readl_relaxed(l3_clk.vbases[OSM_BASE] + ENABLE_REG) & ENABLE_OSM) {
-		dev_info(&pdev->dev, "OSM has been initialized and enabled by TZ software\n");
-		osm_tz_enabled = true;
-	}
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"osm_pwrcl_base");
 	if (!res) {
@@ -2643,10 +1026,10 @@
 		return -ENOMEM;
 	}
 
-	pwrcl_clk.pbases[OSM_BASE] = (unsigned long)res->start;
-	pwrcl_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
+	pwrcl_clk.pbase = (unsigned long)res->start;
+	pwrcl_clk.vbase = devm_ioremap(&pdev->dev, res->start,
 						  resource_size(res));
-	if (!pwrcl_clk.vbases[OSM_BASE]) {
+	if (!pwrcl_clk.vbase) {
 		dev_err(&pdev->dev, "Unable to map osm_pwrcl_base base\n");
 		return -ENOMEM;
 	}
@@ -2659,394 +1042,48 @@
 		return -ENOMEM;
 	}
 
-	perfcl_clk.pbases[OSM_BASE] = (unsigned long)res->start;
-	perfcl_clk.vbases[OSM_BASE] = devm_ioremap(&pdev->dev, res->start,
+	perfcl_clk.pbase = (unsigned long)res->start;
+	perfcl_clk.vbase = devm_ioremap(&pdev->dev, res->start,
 						  resource_size(res));
 
-	if (!perfcl_clk.vbases[OSM_BASE]) {
+	if (!perfcl_clk.vbase) {
 		dev_err(&pdev->dev, "Unable to map osm_perfcl_base base\n");
 		return -ENOMEM;
 	}
 
-	if (osm_tz_enabled)
-		return rc;
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "l3_pll");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for l3_pll\n");
-		return -ENOMEM;
-	}
-	pbase = (unsigned long)res->start;
-	vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
-	if (!vbase) {
-		dev_err(&pdev->dev, "Unable to map l3_pll base\n");
-		return -ENOMEM;
-	}
-
-	l3_clk.pbases[PLL_BASE] = pbase;
-	l3_clk.vbases[PLL_BASE] = vbase;
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrcl_pll");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for pwrcl_pll\n");
-		return -ENOMEM;
-	}
-	pbase = (unsigned long)res->start;
-	vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
-	if (!vbase) {
-		dev_err(&pdev->dev, "Unable to map pwrcl_pll base\n");
-		return -ENOMEM;
-	}
-
-	pwrcl_clk.pbases[PLL_BASE] = pbase;
-	pwrcl_clk.vbases[PLL_BASE] = vbase;
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "perfcl_pll");
-	if (!res) {
-		dev_err(&pdev->dev,
-			"Unable to get platform resource for perfcl_pll\n");
-		return -ENOMEM;
-	}
-	pbase = (unsigned long)res->start;
-	vbase = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-
-	if (!vbase) {
-		dev_err(&pdev->dev, "Unable to map perfcl_pll base\n");
-		return -ENOMEM;
-	}
-
-	perfcl_clk.pbases[PLL_BASE] = pbase;
-	perfcl_clk.vbases[PLL_BASE] = vbase;
-
-	/* efuse speed bin fuses are optional */
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					   "l3_efuse");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-				     resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map in l3_efuse base\n");
-			return -ENOMEM;
-		}
-		l3_clk.pbases[EFUSE_BASE] = pbase;
-		l3_clk.vbases[EFUSE_BASE] = vbase;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					   "pwrcl_efuse");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-				     resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map pwrcl_efuse base\n");
-			return -ENOMEM;
-		}
-		pwrcl_clk.pbases[EFUSE_BASE] = pbase;
-		pwrcl_clk.vbases[EFUSE_BASE] = vbase;
-	}
-
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					   "perfcl_efuse");
-	if (res) {
-		pbase = (unsigned long)res->start;
-		vbase = devm_ioremap(&pdev->dev, res->start,
-				     resource_size(res));
-		if (!vbase) {
-			dev_err(&pdev->dev, "Unable to map perfcl_efuse base\n");
-			return -ENOMEM;
-		}
-		perfcl_clk.pbases[EFUSE_BASE] = pbase;
-		perfcl_clk.vbases[EFUSE_BASE] = vbase;
-	}
-
-	vdd_l3 = devm_regulator_get(&pdev->dev, "vdd-l3");
-	if (IS_ERR(vdd_l3)) {
-		rc = PTR_ERR(vdd_l3);
-		if (rc != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Unable to get the l3 vreg, rc=%d\n",
-				rc);
-		return rc;
-	}
-	l3_clk.vdd_reg = vdd_l3;
-
-	vdd_pwrcl = devm_regulator_get(&pdev->dev, "vdd-pwrcl");
-	if (IS_ERR(vdd_pwrcl)) {
-		rc = PTR_ERR(vdd_pwrcl);
-		if (rc != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Unable to get the pwrcl vreg, rc=%d\n",
-				rc);
-		return rc;
-	}
-	pwrcl_clk.vdd_reg = vdd_pwrcl;
-
-	vdd_perfcl = devm_regulator_get(&pdev->dev, "vdd-perfcl");
-	if (IS_ERR(vdd_perfcl)) {
-		rc = PTR_ERR(vdd_perfcl);
-		if (rc != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Unable to get the perfcl vreg, rc=%d\n",
-				rc);
-		return rc;
-	}
-	perfcl_clk.vdd_reg = vdd_perfcl;
-
-	node = of_parse_phandle(pdev->dev.of_node, "vdd-l3-supply", 0);
-	if (!node) {
-		pr_err("Unable to find vdd-l3-supply\n");
-		return -EINVAL;
-	}
-
-	l3_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
-	if (!l3_clk.vdd_dev) {
-		pr_err("Unable to find device for vdd-l3-supply node\n");
-		return -EINVAL;
-	}
-
-	node = of_parse_phandle(pdev->dev.of_node, "vdd-pwrcl-supply", 0);
-	if (!node) {
-		pr_err("Unable to find vdd-pwrcl-supply\n");
-		return -EINVAL;
-	}
-
-	pwrcl_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
-	if (!pwrcl_clk.vdd_dev) {
-		pr_err("Unable to find device for vdd-pwrcl-supply node\n");
-		return -EINVAL;
-	}
-
-	node = of_parse_phandle(pdev->dev.of_node, "vdd-perfcl-supply", 0);
-	if (!node) {
-		pr_err("Unable to find vdd-perfcl-supply\n");
-		return -EINVAL;
-	}
-
-	perfcl_clk.vdd_dev = of_find_device_by_node(node->parent->parent);
-	if (!perfcl_clk.vdd_dev) {
-		pr_err("Unable to find device for vdd-perfcl-supply\n");
-		return -EINVAL;
-	}
-
 	return 0;
 }
 
-static int debugfs_get_debug_reg(void *data, u64 *val)
+static void clk_cpu_osm_driver_sdm670_fixup(void)
 {
-	struct clk_osm *c = data;
+	osm_qcom_clk_hws[CPU4_PERFCL_CLK] = NULL;
+	osm_qcom_clk_hws[CPU5_PERFCL_CLK] = NULL;
+	osm_qcom_clk_hws[CPU4_PWRCL_CLK] = &cpu4_pwrcl_clk.hw;
+	osm_qcom_clk_hws[CPU5_PWRCL_CLK] = &cpu5_pwrcl_clk.hw;
 
-	if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
-		*val = readl_relaxed((char *)c->vbases[ACD_BASE] +
-					c->acd_debugfs_addr);
-	else
-		*val = clk_osm_acd_local_read_reg(c, c->acd_debugfs_addr);
-	return 0;
-}
+	clk_cpu_map[4] = &cpu4_pwrcl_clk;
+	clk_cpu_map[5] = &cpu5_pwrcl_clk;
 
-static int debugfs_set_debug_reg(void *data, u64 val)
-{
-	struct clk_osm *c = data;
+	cpu6_perfcl_clk.core_num = 0;
+	cpu7_perfcl_clk.core_num = 1;
 
-	if (c->acd_debugfs_addr >= ACD_MASTER_ONLY_REG_ADDR)
-		clk_osm_write_reg(c, val, c->acd_debugfs_addr, ACD_BASE);
-	else
-		clk_osm_acd_master_write_through_reg(c, val,
-							c->acd_debugfs_addr);
-
-	return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_fops,
-			debugfs_get_debug_reg,
-			debugfs_set_debug_reg,
-			"0x%llx\n");
-
-static int debugfs_get_debug_reg_addr(void *data, u64 *val)
-{
-	struct clk_osm *c = data;
-
-	*val = c->acd_debugfs_addr;
-	return 0;
-}
-
-static int debugfs_set_debug_reg_addr(void *data, u64 val)
-{
-	struct clk_osm *c = data;
-
-	if (val > ACD_1P1_MAX_REG_OFFSET) {
-		pr_err("invalid ACD register address offset, must be between 0-0x%x\n",
-			ACD_1P1_MAX_REG_OFFSET);
-		return 0;
-	}
-
-	c->acd_debugfs_addr = val;
-	return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(debugfs_acd_debug_reg_addr_fops,
-			debugfs_get_debug_reg_addr,
-			debugfs_set_debug_reg_addr,
-			"%llu\n");
-
-static void populate_debugfs_dir(struct clk_osm *c)
-{
-	struct dentry *temp;
-
-	if (osm_debugfs_base == NULL) {
-		osm_debugfs_base = debugfs_create_dir("osm", NULL);
-		if (IS_ERR_OR_NULL(osm_debugfs_base)) {
-			pr_err("osm debugfs base directory creation failed\n");
-			osm_debugfs_base = NULL;
-			return;
-		}
-	}
-
-	c->debugfs = debugfs_create_dir(clk_hw_get_name(&c->hw),
-					osm_debugfs_base);
-	if (IS_ERR_OR_NULL(c->debugfs)) {
-		pr_err("osm debugfs directory creation failed\n");
-		return;
-	}
-
-	temp = debugfs_create_file("acd_debug_reg",
-			0644,
-			c->debugfs, c,
-			&debugfs_acd_debug_reg_fops);
-	if (IS_ERR_OR_NULL(temp)) {
-		pr_err("debugfs_acd_debug_reg_fops debugfs file creation failed\n");
-		goto exit;
-	}
-
-	temp = debugfs_create_file("acd_debug_reg_addr",
-			0644,
-			c->debugfs, c,
-			&debugfs_acd_debug_reg_addr_fops);
-	if (IS_ERR_OR_NULL(temp)) {
-		pr_err("debugfs_acd_debug_reg_addr_fops debugfs file creation failed\n");
-		goto exit;
-	}
-
-exit:
-	if (IS_ERR_OR_NULL(temp))
-		debugfs_remove_recursive(c->debugfs);
-}
-
-static int clk_osm_acd_init(struct clk_osm *c)
-{
-
-	int rc = 0;
-	u32 auto_xfer_mask = 0;
-
-	if (c->secure_init) {
-		clk_osm_write_reg(c, c->pbases[ACD_BASE] + ACDCR,
-					DATA_MEM(115), OSM_BASE);
-		clk_osm_write_reg(c, c->pbases[ACD_BASE] + ACD_WRITE_CTL,
-					DATA_MEM(116), OSM_BASE);
-	}
-
-	if (!c->acd_init)
-		return 0;
-
-	c->acd_debugfs_addr = ACD_HW_VERSION;
-
-	/* Program ACD tunable-length delay register */
-	clk_osm_write_reg(c, c->acd_td, ACDTD, ACD_BASE);
-	auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDTD);
-
-	/* Program ACD control register */
-	clk_osm_write_reg(c, c->acd_cr, ACDCR, ACD_BASE);
-	auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDCR);
-
-	/* Program ACD soft start control register */
-	clk_osm_write_reg(c, c->acd_sscr, ACDSSCR, ACD_BASE);
-	auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACDSSCR);
-
-	/* Program initial ACD external interface configuration register */
-	clk_osm_write_reg(c, c->acd_extint0_cfg, ACD_EXTINT_CFG, ACD_BASE);
-	auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_EXTINT_CFG);
-
-	/* Program ACD auto-register transfer control register */
-	clk_osm_write_reg(c, c->acd_autoxfer_ctl, ACD_AUTOXFER_CTL, ACD_BASE);
-
-	/* Ensure writes complete before transfers to local copy */
-	clk_osm_acd_mb(c);
-
-	/* Transfer master copies */
-	rc = clk_osm_acd_auto_local_write_reg(c, auto_xfer_mask);
-	if (rc)
-		return rc;
-
-	/* Switch CPUSS clock source to ACD clock */
-	auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_GFMUX_CFG);
-	rc = clk_osm_acd_master_write_through_reg(c, ACD_GFMUX_CFG_SELECT,
-							ACD_GFMUX_CFG);
-	if (rc)
-		return rc;
-
-	/* Program ACD_DCVS_SW */
-	rc = clk_osm_acd_master_write_through_reg(c,
-					ACD_DCVS_SW_DCVS_IN_PRGR_SET,
-					ACD_DCVS_SW);
-	if (rc)
-		return rc;
-
-	rc = clk_osm_acd_master_write_through_reg(c,
-					ACD_DCVS_SW_DCVS_IN_PRGR_CLEAR,
-					ACD_DCVS_SW);
-	if (rc)
-		return rc;
-
-	udelay(1);
-
-	/* Program final ACD external interface configuration register */
-	rc = clk_osm_acd_master_write_through_reg(c, c->acd_extint1_cfg,
-							ACD_EXTINT_CFG);
-	if (rc)
-		return rc;
-
-	if (c->acd_avg_init) {
-		auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_2);
-		rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg2,
-								ACD_AVG_CFG_2);
-		if (rc)
-			return rc;
-
-		auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_1);
-		rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg1,
-								ACD_AVG_CFG_1);
-		if (rc)
-			return rc;
-
-		auto_xfer_mask |= ACD_REG_RELATIVE_ADDR_BITMASK(ACD_AVG_CFG_0);
-		rc = clk_osm_acd_master_write_through_reg(c, c->acd_avg_cfg0,
-								ACD_AVG_CFG_0);
-		if (rc)
-			return rc;
-	}
-
-	/*
-	 * ACDCR, ACDTD, ACDSSCR, ACD_EXTINT_CFG, ACD_GFMUX_CFG
-	 * must be copied from master to local copy on PC exit.
-	 * Also, ACD_AVG_CFG0, ACF_AVG_CFG1, and ACD_AVG_CFG2 when
-	 * AVG is enabled.
-	 */
-	clk_osm_write_reg(c, auto_xfer_mask, ACD_AUTOXFER_CFG, ACD_BASE);
-	return 0;
+	pwrcl_clk.max_core_count = 6;
+	perfcl_clk.max_core_count = 2;
 }
 
 static int clk_cpu_osm_driver_probe(struct platform_device *pdev)
 {
-	int rc = 0, i;
-	int pvs_ver = 0;
-	u32 pte_efuse, val;
+	int rc = 0, i, cpu;
+	bool is_sdm670 = false;
+	u32 *array;
+	u32 val, pte_efuse;
+	void __iomem *vbase;
 	int num_clks = ARRAY_SIZE(osm_qcom_clk_hws);
 	struct clk *ext_xo_clk, *clk;
+	struct clk_osm *osm_clk;
 	struct device *dev = &pdev->dev;
 	struct clk_onecell_data *clk_data;
-	char l3speedbinstr[] = "qcom,l3-speedbin0-v0";
-	char perfclspeedbinstr[] = "qcom,perfcl-speedbin0-v0";
-	char pwrclspeedbinstr[] = "qcom,pwrcl-speedbin0-v0";
+	struct resource *res;
 	struct cpu_cycle_counter_cb cb = {
 		.get_cpu_cycle_counter = clk_osm_get_cpu_cycle_counter,
 	};
@@ -3062,8 +1099,72 @@
 		return PTR_ERR(ext_xo_clk);
 	}
 
-	is_v2 = of_device_is_compatible(pdev->dev.of_node,
-					"qcom,clk-cpu-osm-v2");
+	is_sdm845v1 = of_device_is_compatible(pdev->dev.of_node,
+					"qcom,clk-cpu-osm");
+
+	if (of_device_is_compatible(pdev->dev.of_node,
+					 "qcom,clk-cpu-osm-sdm670")) {
+		is_sdm670 = true;
+		clk_cpu_osm_driver_sdm670_fixup();
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_rc");
+	if (res) {
+		vbase = devm_ioremap(&pdev->dev, res->start,
+						resource_size(res));
+		if (!vbase) {
+			dev_err(&pdev->dev, "Unable to map in cpr_rc base\n");
+			return -ENOMEM;
+		}
+		pte_efuse = readl_relaxed(vbase);
+		l3_clk.cpr_rc = pwrcl_clk.cpr_rc = perfcl_clk.cpr_rc =
+			((pte_efuse >> EFUSE_SHIFT(is_sdm845v1 | is_sdm670))
+							& EFUSE_MASK);
+		pr_info("LOCAL_CPR_RC: %u\n", l3_clk.cpr_rc);
+		devm_iounmap(&pdev->dev, vbase);
+	} else {
+		dev_err(&pdev->dev,
+			"Unable to get platform resource for cpr_rc\n");
+		return -ENOMEM;
+	}
+
+	vdd_l3_mx_ao.regulator[0] = devm_regulator_get(&pdev->dev,
+						"vdd_l3_mx_ao");
+	if (IS_ERR(vdd_l3_mx_ao.regulator[0])) {
+		if (PTR_ERR(vdd_l3_mx_ao.regulator[0]) != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Unable to get vdd_l3_mx_ao regulator\n");
+		return PTR_ERR(vdd_l3_mx_ao.regulator[0]);
+	}
+
+	vdd_pwrcl_mx_ao.regulator[0] = devm_regulator_get(&pdev->dev,
+						"vdd_pwrcl_mx_ao");
+	if (IS_ERR(vdd_pwrcl_mx_ao.regulator[0])) {
+		if (PTR_ERR(vdd_pwrcl_mx_ao.regulator[0]) != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"Unable to get vdd_pwrcl_mx_ao regulator\n");
+		return PTR_ERR(vdd_pwrcl_mx_ao.regulator[0]);
+	}
+
+	array = devm_kcalloc(&pdev->dev, MAX_CLUSTER_CNT, sizeof(*array),
+							GFP_KERNEL);
+	if (!array)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(pdev->dev.of_node, "qcom,mx-turbo-freq",
+							array, MAX_CLUSTER_CNT);
+	if (rc) {
+		dev_err(&pdev->dev, "unable to find qcom,mx-turbo-freq property, rc=%d\n",
+							rc);
+		devm_kfree(&pdev->dev, array);
+		return rc;
+	}
+
+	l3_clk.mx_turbo_freq = array[l3_clk.cluster_num];
+	pwrcl_clk.mx_turbo_freq = array[pwrcl_clk.cluster_num];
+	perfcl_clk.mx_turbo_freq = array[perfcl_clk.cluster_num];
+
+	devm_kfree(&pdev->dev, array);
 
 	clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
 								GFP_KERNEL);
@@ -3085,290 +1186,55 @@
 		return rc;
 	}
 
-	if (!osm_tz_enabled) {
-		if (l3_clk.vbases[EFUSE_BASE]) {
-			/* Multiple speed-bins are supported */
-			pte_efuse = readl_relaxed(l3_clk.vbases[EFUSE_BASE]);
-			l3_clk.speedbin = ((pte_efuse >> L3_EFUSE_SHIFT) &
-							L3_EFUSE_MASK);
-			snprintf(l3speedbinstr, ARRAY_SIZE(l3speedbinstr),
-			 "qcom,l3-speedbin%d-v%d", l3_clk.speedbin, pvs_ver);
-		}
+	/* Check if per-core DCVS is enabled/not */
+	val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
+	if (val && BIT(0))
+		pwrcl_clk.per_core_dcvs = true;
 
-		dev_info(&pdev->dev, "using L3 speed bin %u and pvs_ver %d\n",
-					l3_clk.speedbin, pvs_ver);
+	val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
+	if (val && BIT(0))
+		perfcl_clk.per_core_dcvs = true;
 
-		rc = clk_osm_get_lut(pdev, &l3_clk, l3speedbinstr);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to get OSM LUT for L3, rc=%d\n",
-				rc);
-			return rc;
-		}
+	rc = clk_osm_read_lut(pdev, &l3_clk);
+	if (rc) {
+		dev_err(&pdev->dev, "Unable to read OSM LUT for L3, rc=%d\n",
+			rc);
+		return rc;
+	}
 
-		if (pwrcl_clk.vbases[EFUSE_BASE]) {
-			/* Multiple speed-bins are supported */
-			pte_efuse = readl_relaxed(pwrcl_clk.vbases[EFUSE_BASE]);
-			pwrcl_clk.speedbin = ((pte_efuse >> PWRCL_EFUSE_SHIFT) &
-							PWRCL_EFUSE_MASK);
-			snprintf(pwrclspeedbinstr, ARRAY_SIZE(pwrclspeedbinstr),
-			 "qcom,pwrcl-speedbin%d-v%d", pwrcl_clk.speedbin,
-							pvs_ver);
-		}
+	rc = clk_osm_read_lut(pdev, &pwrcl_clk);
+	if (rc) {
+		dev_err(&pdev->dev, "Unable to read OSM LUT for power cluster, rc=%d\n",
+			rc);
+		return rc;
+	}
 
-		dev_info(&pdev->dev, "using pwrcl speed bin %u and pvs_ver %d\n",
-					pwrcl_clk.speedbin, pvs_ver);
-
-		rc = clk_osm_get_lut(pdev, &pwrcl_clk, pwrclspeedbinstr);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to get OSM LUT for power cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		if (perfcl_clk.vbases[EFUSE_BASE]) {
-			/* Multiple speed-bins are supported */
-			pte_efuse =
-				readl_relaxed(perfcl_clk.vbases[EFUSE_BASE]);
-			perfcl_clk.speedbin = ((pte_efuse >> PERFCL_EFUSE_SHIFT)
-						& PERFCL_EFUSE_MASK);
-			snprintf(perfclspeedbinstr,
-				ARRAY_SIZE(perfclspeedbinstr),
-				"qcom,perfcl-speedbin%d-v%d",
-				perfcl_clk.speedbin, pvs_ver);
-		}
-
-		dev_info(&pdev->dev, "using perfcl speed bin %u and pvs_ver %d\n",
-					perfcl_clk.speedbin, pvs_ver);
-
-		rc = clk_osm_get_lut(pdev, &perfcl_clk, perfclspeedbinstr);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to get OSM LUT for perf cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		rc = clk_osm_parse_dt_configs(pdev);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to parse OSM device tree configurations\n");
-			return rc;
-		}
-
-		rc = clk_osm_parse_acd_dt_configs(pdev);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to parse ACD device tree configurations\n");
-			return rc;
-		}
-
-		rc = clk_osm_acd_resources_init(pdev);
-		if (rc) {
-			dev_err(&pdev->dev, "ACD resources init failed, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		rc = clk_osm_resolve_open_loop_voltages(&l3_clk);
-		if (rc) {
-			if (rc == -EPROBE_DEFER)
-				return rc;
-			dev_err(&pdev->dev, "Unable to determine open-loop voltages for L3, rc=%d\n",
-				rc);
-			return rc;
-		}
-		rc = clk_osm_resolve_open_loop_voltages(&pwrcl_clk);
-		if (rc) {
-			if (rc == -EPROBE_DEFER)
-				return rc;
-			dev_err(&pdev->dev, "Unable to determine open-loop voltages for power cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-		rc = clk_osm_resolve_open_loop_voltages(&perfcl_clk);
-		if (rc) {
-			if (rc == -EPROBE_DEFER)
-				return rc;
-			dev_err(&pdev->dev, "Unable to determine open-loop voltages for perf cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		rc = clk_osm_resolve_crossover_corners(&l3_clk, pdev);
-		if (rc)
-			dev_info(&pdev->dev,
-				"No APM crossover corner programmed for L3\n");
-		rc = clk_osm_resolve_crossover_corners(&pwrcl_clk, pdev);
-		if (rc)
-			dev_info(&pdev->dev,
-				"No APM crossover corner programmed for pwrcl_clk\n");
-		rc = clk_osm_resolve_crossover_corners(&perfcl_clk, pdev);
-		if (rc)
-			dev_info(&pdev->dev, "No MEM-ACC crossover corner programmed\n");
-
-		clk_osm_setup_cycle_counters(&l3_clk);
-		clk_osm_setup_cycle_counters(&pwrcl_clk);
-		clk_osm_setup_cycle_counters(&perfcl_clk);
-
-		clk_osm_print_osm_table(&l3_clk);
-		clk_osm_print_osm_table(&pwrcl_clk);
-		clk_osm_print_osm_table(&perfcl_clk);
-
-		rc = clk_osm_setup_hw_table(&l3_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "failed to setup l3 hardware table\n");
-			goto exit;
-		}
-		rc = clk_osm_setup_hw_table(&pwrcl_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "failed to setup power cluster hardware table\n");
-			goto exit;
-		}
-		rc = clk_osm_setup_hw_table(&perfcl_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "failed to setup perf cluster hardware table\n");
-			goto exit;
-		}
-
-		/* Policy tuning */
-		rc = clk_osm_set_cc_policy(pdev);
-		if (rc < 0) {
-			dev_err(&pdev->dev, "cc policy setup failed");
-			goto exit;
-		}
-
-		/* LLM Freq Policy Tuning */
-		rc = clk_osm_set_llm_freq_policy(pdev);
-		if (rc < 0) {
-			dev_err(&pdev->dev, "LLM Frequency Policy setup failed");
-			goto exit;
-		}
-
-		/* LLM Voltage Policy Tuning */
-		rc = clk_osm_set_llm_volt_policy(pdev);
-		if (rc < 0) {
-			dev_err(&pdev->dev, "Failed to set LLM voltage Policy");
-			goto exit;
-		}
-
-		clk_osm_setup_fsms(&l3_clk);
-		clk_osm_setup_fsms(&pwrcl_clk);
-		clk_osm_setup_fsms(&perfcl_clk);
-
-		/*
-		 * Program the VC at which the array power supply
-		 * needs to be switched.
-		 */
-		clk_osm_write_reg(&perfcl_clk, perfcl_clk.apm_threshold_vc,
-				APM_CROSSOVER_VC, OSM_BASE);
-		if (perfcl_clk.secure_init) {
-			clk_osm_write_seq_reg(&perfcl_clk,
-				perfcl_clk.apm_crossover_vc, DATA_MEM(77));
-			clk_osm_write_seq_reg(&perfcl_clk,
-				(0x39 | (perfcl_clk.apm_threshold_vc << 6)),
-				DATA_MEM(111));
-		} else {
-			scm_io_write(perfcl_clk.pbases[SEQ_BASE] + DATA_MEM(77),
-					perfcl_clk.apm_crossover_vc);
-			scm_io_write(perfcl_clk.pbases[SEQ_BASE] +
-								DATA_MEM(111),
-				(0x39 | (perfcl_clk.apm_threshold_vc << 6)));
-		}
-
-		/*
-		 * Perform typical secure-world HW initialization
-		 * as necessary.
-		 */
-		clk_osm_do_additional_setup(&l3_clk, pdev);
-		clk_osm_do_additional_setup(&pwrcl_clk, pdev);
-		clk_osm_do_additional_setup(&perfcl_clk, pdev);
-
-		/* MEM-ACC Programming */
-		clk_osm_program_mem_acc_regs(&l3_clk);
-		clk_osm_program_mem_acc_regs(&pwrcl_clk);
-		clk_osm_program_mem_acc_regs(&perfcl_clk);
-
-		if (of_property_read_bool(pdev->dev.of_node,
-					"qcom,osm-pll-setup")) {
-			clk_osm_setup_cluster_pll(&l3_clk);
-			clk_osm_setup_cluster_pll(&pwrcl_clk);
-			clk_osm_setup_cluster_pll(&perfcl_clk);
-		}
-
-		/* Misc programming */
-		clk_osm_misc_programming(&l3_clk);
-		clk_osm_misc_programming(&pwrcl_clk);
-		clk_osm_misc_programming(&perfcl_clk);
-
-		rc = clk_osm_acd_init(&l3_clk);
-		if (rc) {
-			pr_err("failed to initialize ACD for L3, rc=%d\n", rc);
-			goto exit;
-		}
-		rc = clk_osm_acd_init(&pwrcl_clk);
-		if (rc) {
-			pr_err("failed to initialize ACD for pwrcl, rc=%d\n",
-									rc);
-			goto exit;
-		}
-		rc = clk_osm_acd_init(&perfcl_clk);
-		if (rc) {
-			pr_err("failed to initialize ACD for perfcl, rc=%d\n",
-									rc);
-			goto exit;
-		}
-
-		pwrcl_clk.per_core_dcvs = perfcl_clk.per_core_dcvs =
-			of_property_read_bool(pdev->dev.of_node,
-				"qcom,enable-per-core-dcvs");
-		if (pwrcl_clk.per_core_dcvs) {
-			val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
-			val |= BIT(0);
-			clk_osm_write_reg(&pwrcl_clk, val, CORE_DCVS_CTRL,
-							OSM_BASE);
-			val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
-			val |= BIT(0);
-			clk_osm_write_reg(&perfcl_clk, val, CORE_DCVS_CTRL,
-							OSM_BASE);
-		}
-	} else {
-		/* OSM has been enabled already by trustzone */
-		rc = clk_osm_read_lut(pdev, &l3_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to read OSM LUT for L3, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		rc = clk_osm_read_lut(pdev, &pwrcl_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to read OSM LUT for power cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		rc = clk_osm_read_lut(pdev, &perfcl_clk);
-		if (rc) {
-			dev_err(&pdev->dev, "Unable to read OSM LUT for perf cluster, rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		/* Check if per-core DCVS is enabled/not */
-		val = clk_osm_read_reg(&pwrcl_clk, CORE_DCVS_CTRL);
-		if (val && BIT(0))
-			pwrcl_clk.per_core_dcvs = true;
-
-		val = clk_osm_read_reg(&perfcl_clk, CORE_DCVS_CTRL);
-		if (val && BIT(0))
-			perfcl_clk.per_core_dcvs = true;
-
-		clk_ops_l3_osm.enable = NULL;
+	rc = clk_osm_read_lut(pdev, &perfcl_clk);
+	if (rc) {
+		dev_err(&pdev->dev, "Unable to read OSM LUT for perf cluster, rc=%d\n",
+			rc);
+		return rc;
 	}
 
 	spin_lock_init(&l3_clk.lock);
 	spin_lock_init(&pwrcl_clk.lock);
 	spin_lock_init(&perfcl_clk.lock);
 
+	clk_ops_core = clk_dummy_ops;
+	clk_ops_core.set_rate = clk_cpu_set_rate;
+	clk_ops_core.round_rate = clk_cpu_round_rate;
+	clk_ops_core.recalc_rate = clk_cpu_recalc_rate;
+
+	clk_ops_cpu_osm = clk_dummy_ops;
+	clk_ops_cpu_osm.round_rate = clk_osm_round_rate;
+	clk_ops_cpu_osm.list_rate = clk_osm_list_rate;
+	clk_ops_cpu_osm.debug_init = clk_debug_measure_add;
+
 	/* Register OSM l3, pwr and perf clocks with Clock Framework */
 	for (i = 0; i < num_clks; i++) {
+		if (!osm_qcom_clk_hws[i])
+			continue;
+
 		clk = devm_clk_register(&pdev->dev, osm_qcom_clk_hws[i]);
 		if (IS_ERR(clk)) {
 			dev_err(&pdev->dev, "Unable to register CPU clock at index %d\n",
@@ -3387,35 +1253,23 @@
 
 	get_online_cpus();
 
-	if (!osm_tz_enabled) {
-		populate_debugfs_dir(&l3_clk);
-		populate_debugfs_dir(&pwrcl_clk);
-		populate_debugfs_dir(&perfcl_clk);
-
-		/* Configure default rate to lowest frequency */
-		for (i = 0; i < MAX_CORE_COUNT; i++) {
-			osm_set_index(&pwrcl_clk, 0, i);
-			osm_set_index(&perfcl_clk, 0, i);
-		}
-	}
-	/*
-	 * Set the L3 clock to run off GPLL0 and enable OSM for the domain.
-	 * In the case that trustzone has already enabled OSM, bring the L3
-	 * clock rate to a safe level until the devfreq driver comes up and
-	 * votes for its desired frequency.
-	 */
-	rc = clk_set_rate(l3_clk.hw.clk, OSM_INIT_RATE);
-	if (rc) {
-		dev_err(&pdev->dev, "Unable to set init rate on L3 cluster, rc=%d\n",
-			rc);
-		goto provider_err;
-	}
 	WARN(clk_prepare_enable(l3_cluster0_vote_clk.hw.clk),
 			"clk: Failed to enable cluster0 clock for L3\n");
 	WARN(clk_prepare_enable(l3_cluster1_vote_clk.hw.clk),
 			"clk: Failed to enable cluster1 clock for L3\n");
-	udelay(300);
+	WARN(clk_prepare_enable(l3_misc_vote_clk.hw.clk),
+			"clk: Failed to enable misc clock for L3\n");
 
+	/*
+	 * Call clk_prepare_enable for the silver clock explicitly in order to
+	 * place an implicit vote on MX
+	 */
+	for_each_online_cpu(cpu) {
+		osm_clk = logical_cpu_to_clk(cpu);
+		if (!osm_clk)
+			return -EINVAL;
+		clk_prepare_enable(osm_clk->hw.clk);
+	}
 	populate_opp_table(pdev);
 
 	of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
@@ -3443,6 +1297,7 @@
 static const struct of_device_id match_table[] = {
 	{ .compatible = "qcom,clk-cpu-osm" },
 	{ .compatible = "qcom,clk-cpu-osm-v2" },
+	{ .compatible = "qcom,clk-cpu-osm-sdm670" },
 	{}
 };
 
diff --git a/drivers/clk/qcom/clk-pll.h b/drivers/clk/qcom/clk-pll.h
index 9682799..70f7612 100644
--- a/drivers/clk/qcom/clk-pll.h
+++ b/drivers/clk/qcom/clk-pll.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -83,6 +83,8 @@
 	u32 aux2_output_mask;
 	u32 early_output_mask;
 	u32 config_ctl_val;
+	u32 config_ctl_hi_val;
+	u32 config_ctl_hi1_val;
 };
 
 void clk_pll_configure_sr(struct clk_pll *pll, struct regmap *regmap,
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 7f56fb6..aaf2324 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -23,8 +23,6 @@
 	u8 pre_div;
 	u16 m;
 	u16 n;
-	unsigned long src_freq;
-#define FIXED_FREQ_SRC   0
 };
 
 /**
@@ -190,4 +188,6 @@
 
 extern int clk_rcg2_get_dfs_clock_rate(struct clk_rcg2 *clk,
 				struct device *dev, u8 rcg_flags);
+extern unsigned long
+clk_rcg2_calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div);
 #endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 7382cfa..35bcf5a 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -223,8 +223,8 @@
  *   rate = ----------- x  ---
  *            hid_div       n
  */
-static unsigned long
-calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
+unsigned long
+clk_rcg2_calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
 {
 	if (hid_div) {
 		rate *= 2;
@@ -240,6 +240,7 @@
 
 	return rate;
 }
+EXPORT_SYMBOL(clk_rcg2_calc_rate);
 
 static unsigned long
 clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
@@ -274,17 +275,16 @@
 	hid_div = cfg >> CFG_SRC_DIV_SHIFT;
 	hid_div &= mask;
 
-	return calc_rate(parent_rate, m, n, mode, hid_div);
+	return clk_rcg2_calc_rate(parent_rate, m, n, mode, hid_div);
 }
 
 static int _freq_tbl_determine_rate(struct clk_hw *hw,
 		const struct freq_tbl *f, struct clk_rate_request *req)
 {
 	unsigned long clk_flags, rate = req->rate;
-	struct clk_rate_request parent_req = { };
 	struct clk_hw *p;
 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
-	int index, ret = 0;
+	int index;
 
 	f = qcom_find_freq(f, rate);
 	if (!f)
@@ -315,21 +315,6 @@
 	req->best_parent_rate = rate;
 	req->rate = f->freq;
 
-	if (f->src_freq != FIXED_FREQ_SRC) {
-		rate = parent_req.rate = f->src_freq;
-		parent_req.best_parent_hw = p;
-		ret = __clk_determine_rate(p, &parent_req);
-		if (ret)
-			return ret;
-
-		ret = clk_set_rate(p->clk, parent_req.rate);
-		if (ret) {
-			pr_err("Failed set rate(%lu) on parent for non-fixed source\n",
-							parent_req.rate);
-			return ret;
-		}
-	}
-
 	return 0;
 }
 
@@ -780,7 +765,7 @@
 		hid_div >>= CFG_SRC_DIV_SHIFT;
 		hid_div &= mask;
 
-		req->rate = calc_rate(req->best_parent_rate,
+		req->rate = clk_rcg2_calc_rate(req->best_parent_rate,
 				      frac->num, frac->den,
 				      !!frac->den, hid_div);
 		return 0;
@@ -820,7 +805,7 @@
 	div = DIV_ROUND_UP((2 * parent_rate), req->rate) - 1;
 	div = min_t(u32, div, mask);
 
-	req->rate = calc_rate(parent_rate, 0, 0, 0, div);
+	req->rate = clk_rcg2_calc_rate(parent_rate, 0, 0, 0, div);
 
 	return 0;
 }
@@ -878,7 +863,7 @@
 	div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
 	div = min_t(u32, div, mask);
 
-	req->rate = calc_rate(parent_rate, 0, 0, 0, div);
+	req->rate = clk_rcg2_calc_rate(parent_rate, 0, 0, 0, div);
 
 	return 0;
 }
@@ -1334,7 +1319,7 @@
 		dfs_freq_tbl[i].n = n;
 
 		/* calculate the final frequency */
-		calc_freq = calc_rate(prate, dfs_freq_tbl[i].m,
+		calc_freq = clk_rcg2_calc_rate(prate, dfs_freq_tbl[i].m,
 						dfs_freq_tbl[i].n, mode,
 						dfs_freq_tbl[i].pre_div);
 
diff --git a/drivers/clk/qcom/clk-regmap-mux-div.h b/drivers/clk/qcom/clk-regmap-mux-div.h
index 63a696a..6cd8d4f 100644
--- a/drivers/clk/qcom/clk-regmap-mux-div.h
+++ b/drivers/clk/qcom/clk-regmap-mux-div.h
@@ -42,6 +42,7 @@
  *		on and runs at only one rate.
  * @parent_map:	pointer to parent_map struct
  * @clkr:	handle between common and hardware-specific interfaces
+ * @clk_nb:	clock notifier registered for clock rate change
  */
 
 struct clk_regmap_mux_div {
@@ -57,6 +58,7 @@
 	unsigned long			safe_freq;
 	const struct parent_map		*parent_map;
 	struct clk_regmap		clkr;
+	struct notifier_block		clk_nb;
 };
 
 extern const struct clk_ops clk_regmap_mux_div_ops;
diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c
index e1cda90..1f90d46 100644
--- a/drivers/clk/qcom/clk-rpmh.c
+++ b/drivers/clk/qcom/clk-rpmh.c
@@ -317,10 +317,47 @@
 
 static const struct of_device_id clk_rpmh_match_table[] = {
 	{ .compatible = "qcom,rpmh-clk-sdm845", .data = &clk_rpmh_sdm845},
+	{ .compatible = "qcom,rpmh-clk-sdm670", .data = &clk_rpmh_sdm845},
+	{ .compatible = "qcom,rpmh-clk-sdxpoorwills", .data = &clk_rpmh_sdm845},
 	{ }
 };
 MODULE_DEVICE_TABLE(of, clk_rpmh_match_table);
 
+static void clk_rpmh_sdm670_fixup(void)
+{
+	sdm845_rpmh_clocks[RPMH_RF_CLK3] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK3_A] = NULL;
+}
+
+static void clk_rpmh_sdxpoorwills_fixup(void)
+{
+	sdm845_rpmh_clocks[RPMH_LN_BB_CLK2] = NULL;
+	sdm845_rpmh_clocks[RPMH_LN_BB_CLK2_A] = NULL;
+	sdm845_rpmh_clocks[RPMH_LN_BB_CLK3] = NULL;
+	sdm845_rpmh_clocks[RPMH_LN_BB_CLK3_A] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK2] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK2_A] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK3] = NULL;
+	sdm845_rpmh_clocks[RPMH_RF_CLK3_A] = NULL;
+}
+
+static int clk_rpmh_fixup(struct platform_device *pdev)
+{
+	const char *compat = NULL;
+	int compatlen = 0;
+
+	compat = of_get_property(pdev->dev.of_node, "compatible", &compatlen);
+	if (!compat || (compatlen <= 0))
+		return -EINVAL;
+
+	if (!strcmp(compat, "qcom,rpmh-clk-sdm670"))
+		clk_rpmh_sdm670_fixup();
+	else if (!strcmp(compat, "qcom,rpmh-clk-sdxpoorwills"))
+		clk_rpmh_sdxpoorwills_fixup();
+
+	return 0;
+}
+
 static int clk_rpmh_probe(struct platform_device *pdev)
 {
 	struct clk **clks;
@@ -388,6 +425,10 @@
 		goto err2;
 	}
 
+	ret = clk_rpmh_fixup(pdev);
+	if (ret)
+		return ret;
+
 	hw_clks = desc->clks;
 	num_clks = desc->num_clks;
 
@@ -404,6 +445,11 @@
 	data->clk_num = num_clks;
 
 	for (i = 0; i < num_clks; i++) {
+		if (!hw_clks[i]) {
+			clks[i] = ERR_PTR(-ENOENT);
+			continue;
+		}
+
 		rpmh_clk = to_clk_rpmh(hw_clks[i]);
 		rpmh_clk->res_addr = cmd_db_get_addr(rpmh_clk->res_name);
 		if (!rpmh_clk->res_addr) {
diff --git a/drivers/clk/qcom/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index cb0cadd..45bd556 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -117,6 +117,7 @@
 	"gcc_aggre_ufs_phy_axi_clk",
 	"gcc_aggre_usb3_prim_axi_clk",
 	"gcc_aggre_usb3_sec_axi_clk",
+	"gcc_apc_vs_clk",
 	"gcc_boot_rom_ahb_clk",
 	"gcc_camera_ahb_clk",
 	"gcc_camera_axi_clk",
@@ -144,12 +145,14 @@
 	"gcc_gpu_gpll0_div_clk_src",
 	"gcc_gpu_memnoc_gfx_clk",
 	"gcc_gpu_snoc_dvm_gfx_clk",
+	"gcc_gpu_vs_clk",
 	"gcc_mss_axis2_clk",
 	"gcc_mss_cfg_ahb_clk",
 	"gcc_mss_gpll0_div_clk_src",
 	"gcc_mss_mfab_axis_clk",
 	"gcc_mss_q6_memnoc_axi_clk",
 	"gcc_mss_snoc_axi_clk",
+	"gcc_mss_vs_clk",
 	"gcc_pcie_0_aux_clk",
 	"gcc_pcie_0_cfg_ahb_clk",
 	"gcc_pcie_0_mstr_axi_clk",
@@ -232,11 +235,18 @@
 	"gcc_usb3_sec_phy_com_aux_clk",
 	"gcc_usb3_sec_phy_pipe_clk",
 	"gcc_usb_phy_cfg_ahb2phy_clk",
+	"gcc_vdda_vs_clk",
+	"gcc_vddcx_vs_clk",
+	"gcc_vddmx_vs_clk",
 	"gcc_video_ahb_clk",
 	"gcc_video_axi_clk",
 	"gcc_video_xo_clk",
+	"gcc_vs_ctrl_ahb_clk",
+	"gcc_vs_ctrl_clk",
+	"gcc_sdcc1_ahb_clk",
+	"gcc_sdcc1_apps_clk",
+	"gcc_sdcc1_ice_core_clk",
 	"gpu_cc_acd_cxo_clk",
-	"gpu_cc_ahb_clk",
 	"gpu_cc_crc_ahb_clk",
 	"gpu_cc_cx_apb_clk",
 	"gpu_cc_cx_gfx3d_clk",
@@ -449,6 +459,8 @@
 			0x11B, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_aggre_usb3_sec_axi_clk", 0x11C, 4, GCC,
 			0x11C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_apc_vs_clk", 0x113, 4, GCC,
+			0x113, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_boot_rom_ahb_clk", 0x94, 4, GCC,
 			0x94, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_camera_ahb_clk", 0x3A, 4, GCC,
@@ -503,6 +515,8 @@
 			0x145, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_gpu_snoc_dvm_gfx_clk", 0x147, 4, GCC,
 			0x147, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_gpu_vs_clk", 0x112, 4, GCC,
+			0x112, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_mss_axis2_clk", 0x12F, 4, GCC,
 			0x12F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_mss_cfg_ahb_clk", 0x12D, 4, GCC,
@@ -515,6 +529,8 @@
 			0x135, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_mss_snoc_axi_clk", 0x134, 4, GCC,
 			0x134, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_mss_vs_clk", 0x111, 4, GCC,
+			0x111, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_pcie_0_aux_clk", 0xE5, 4, GCC,
 			0xE5, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_pcie_0_cfg_ahb_clk", 0xE4, 4, GCC,
@@ -679,16 +695,30 @@
 			0x6A, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_usb_phy_cfg_ahb2phy_clk", 0x6F, 4, GCC,
 			0x6F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_vdda_vs_clk", 0x10E, 4, GCC,
+			0x10E, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_vddcx_vs_clk", 0x10C, 4, GCC,
+			0x10C, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_vddmx_vs_clk", 0x10D, 4, GCC,
+			0x10D, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_video_ahb_clk", 0x39, 4, GCC,
 			0x39, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_video_axi_clk", 0x3F, 4, GCC,
 			0x3F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gcc_video_xo_clk", 0x42, 4, GCC,
 			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_vs_ctrl_ahb_clk", 0x110, 4, GCC,
+			0x110, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_vs_ctrl_clk", 0x10F, 4, GCC,
+			0x10F, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_ahb_clk", 0x15C, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_apps_clk", 0x15B, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
+		{ "gcc_sdcc1_ice_core_clk", 0x15D, 4, GCC,
+			0x42, 0x3FF, 0, 0xF, 0, 4, 0x62008, 0x62000, 0x62004 },
 		{ "gpu_cc_acd_cxo_clk", 0x144, 4, GPU_CC,
 			0x1F, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
-		{ "gpu_cc_ahb_clk", 0x144, 4, GPU_CC,
-			0x11, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
 		{ "gpu_cc_crc_ahb_clk", 0x144, 4, GPU_CC,
 			0x12, 0xFF, 0, 0x3, 0, 1, 0x1568, 0x10FC, 0x1100 },
 		{ "gpu_cc_cx_apb_clk", 0x144, 4, GPU_CC,
diff --git a/drivers/clk/qcom/dispcc-sdm845.c b/drivers/clk/qcom/dispcc-sdm845.c
index 53bfe77..d4f27d7 100644
--- a/drivers/clk/qcom/dispcc-sdm845.c
+++ b/drivers/clk/qcom/dispcc-sdm845.c
@@ -40,8 +40,6 @@
 #define DISP_CC_MISC_CMD	0x8000
 
 #define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
-#define F_SLEW(f, s, h, m, n, src_freq) { (f), (s), (2 * (h) - 1), (m), (n), \
-					(src_freq) }
 
 static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner);
 
@@ -385,6 +383,20 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_disp_cc_mdss_mdp_clk_src_sdm670[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(85714286, P_GPLL0_OUT_MAIN, 7, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+	F(171428571, P_GPLL0_OUT_MAIN, 3.5, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+	F(286666667, P_DISP_CC_PLL0_OUT_MAIN, 3, 0, 0),
+	F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+	F(344000000, P_DISP_CC_PLL0_OUT_MAIN, 2.5, 0, 0),
+	F(430000000, P_DISP_CC_PLL0_OUT_MAIN, 2, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 disp_cc_mdss_mdp_clk_src = {
 	.cmd_rcgr = 0x2088,
 	.mnd_width = 0,
@@ -1014,6 +1026,7 @@
 static const struct of_device_id disp_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,dispcc-sdm845" },
 	{ .compatible = "qcom,dispcc-sdm845-v2" },
+	{ .compatible = "qcom,dispcc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, disp_cc_sdm845_match_table);
@@ -1021,23 +1034,23 @@
 static void disp_cc_sdm845_fixup_sdm845v2(struct regmap *regmap)
 {
 	clk_fabia_pll_configure(&disp_cc_pll0, regmap,
-					&disp_cc_pll0_config_v2);
+		&disp_cc_pll0_config_v2);
 	disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
 		180000000;
 	disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
 		275000000;
 	disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
-		358000000;
+		328580000;
 	disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
 		180000000;
 	disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
 		275000000;
 	disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
-		358000000;
+		328580000;
 	disp_cc_mdss_dp_pixel1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
-		337500000;
+		337500;
 	disp_cc_mdss_dp_pixel_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
-		337500000;
+		337500;
 	disp_cc_mdss_mdp_clk_src.freq_tbl =
 		ftbl_disp_cc_mdss_mdp_clk_src_sdm845_v2;
 	disp_cc_mdss_mdp_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
@@ -1050,10 +1063,14 @@
 		280000000;
 	disp_cc_mdss_pclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
 		430000000;
+	disp_cc_mdss_pclk0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		430000000;
 	disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
 		280000000;
 	disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] =
 		430000000;
+	disp_cc_mdss_pclk1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		430000000;
 	disp_cc_mdss_rot_clk_src.freq_tbl =
 		ftbl_disp_cc_mdss_rot_clk_src_sdm845_v2;
 	disp_cc_mdss_rot_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] =
@@ -1064,6 +1081,18 @@
 		430000000;
 }
 
+static void disp_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+	disp_cc_sdm845_fixup_sdm845v2(regmap);
+
+	disp_cc_mdss_mdp_clk_src.freq_tbl =
+		ftbl_disp_cc_mdss_mdp_clk_src_sdm670;
+	disp_cc_mdss_byte0_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		358000000;
+	disp_cc_mdss_byte1_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		358000000;
+}
+
 static int disp_cc_sdm845_fixup(struct platform_device *pdev,
 						struct regmap *regmap)
 {
@@ -1076,6 +1105,8 @@
 
 	if (!strcmp(compat, "qcom,dispcc-sdm845-v2"))
 		disp_cc_sdm845_fixup_sdm845v2(regmap);
+	else if (!strcmp(compat, "qcom,dispcc-sdm670"))
+		disp_cc_sdm845_fixup_sdm670(regmap);
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 17b2403..555b8bd 100644
--- a/drivers/clk/qcom/gcc-sdm845.c
+++ b/drivers/clk/qcom/gcc-sdm845.c
@@ -36,9 +36,6 @@
 #include "clk-alpha-pll.h"
 #include "vdd-level-sdm845.h"
 
-#define GCC_APCS_CLOCK_SLEEP_ENA_VOTE_OFFSET	0x52008
-#define CPUSS_AHB_CLK_SLEEP_ENA			BIT(21)
-#define SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA		BIT(0)
 #define GCC_MMSS_MISC				0x09FFC
 #define GCC_GPU_MISC				0x71028
 
@@ -198,6 +195,22 @@
 	"core_bi_pll_test_se",
 };
 
+static const struct parent_map gcc_parent_map_7[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_GPLL0_OUT_MAIN, 1 },
+	{ P_GPLL6_OUT_MAIN, 2 },
+	{ P_GPLL0_OUT_EVEN, 6 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_11[] = {
+	"bi_tcxo",
+	"gpll0",
+	"gpll6",
+	"gpll0_out_even",
+	"core_bi_pll_test_se",
+};
+
 static struct clk_dummy measure_only_snoc_clk = {
 	.rrate = 1000,
 	.hw.init = &(struct clk_init_data){
@@ -301,6 +314,28 @@
 	},
 };
 
+static struct clk_alpha_pll gpll6 = {
+	.offset = 0x13000,
+	.vco_table = fabia_vco,
+	.num_vco = ARRAY_SIZE(fabia_vco),
+	.type = FABIA_PLL,
+	.clkr = {
+		.enable_reg = 0x52000,
+		.enable_mask = BIT(6),
+		.hw.init = &(struct clk_init_data){
+			.name = "gpll6",
+			.parent_names = (const char *[]){ "bi_tcxo" },
+			.num_parents = 1,
+			.ops = &clk_fabia_fixed_pll_ops,
+			VDD_CX_FMAX_MAP4(
+				MIN, 615000000,
+				LOW, 1066000000,
+				LOW_L1, 1600000000,
+				NOMINAL, 2000000000),
+		},
+	},
+};
+
 static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
 	F(19200000, P_BI_TCXO, 1, 0, 0),
 	{ }
@@ -330,6 +365,12 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src_sdm670[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = {
 	.cmd_rcgr = 0x4815c,
 	.mnd_width = 0,
@@ -862,6 +903,67 @@
 	},
 };
 
+static const struct freq_tbl ftbl_gcc_sdcc1_ice_core_clk_src[] = {
+	F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0),
+	F(150000000, P_GPLL0_OUT_MAIN, 4, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+	F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_sdcc1_ice_core_clk_src = {
+	.cmd_rcgr = 0x26010,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_sdcc1_ice_core_clk_src,
+	.enable_safe_config = true,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_sdcc1_ice_core_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 75000000,
+			LOW, 150000000,
+			NOMINAL, 300000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk_src[] = {
+	F(144000, P_BI_TCXO, 16, 3, 25),
+	F(400000, P_BI_TCXO, 12, 1, 4),
+	F(20000000, P_GPLL0_OUT_EVEN, 5, 1, 3),
+	F(25000000, P_GPLL0_OUT_EVEN, 6, 1, 2),
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(192000000, P_GPLL6_OUT_MAIN, 2, 0, 0),
+	F(384000000, P_GPLL6_OUT_MAIN, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_sdcc1_apps_clk_src = {
+	.cmd_rcgr = 0x26028,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_7,
+	.freq_tbl = ftbl_gcc_sdcc1_apps_clk_src,
+	.enable_safe_config = true,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_sdcc1_apps_clk_src",
+		.parent_names = gcc_parent_names_11,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 384000000),
+	},
+};
+
 static const struct freq_tbl ftbl_gcc_sdcc2_apps_clk_src[] = {
 	F(400000, P_BI_TCXO, 12, 1, 4),
 	F(9600000, P_BI_TCXO, 2, 0, 0),
@@ -904,17 +1006,28 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gcc_sdcc4_apps_clk_src_sdm670[] = {
+	F(400000, P_BI_TCXO, 12, 1, 4),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
+	F(33333333, P_GPLL0_OUT_EVEN, 9, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gcc_sdcc4_apps_clk_src = {
 	.cmd_rcgr = 0x1600c,
 	.mnd_width = 8,
 	.hid_width = 5,
-	.parent_map = gcc_parent_map_3,
+	.parent_map = gcc_parent_map_0,
 	.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src,
 	.enable_safe_config = true,
 	.clkr.hw.init = &(struct clk_init_data){
 		.name = "gcc_sdcc4_apps_clk_src",
-		.parent_names = gcc_parent_names_3,
-		.num_parents = 3,
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
 		.flags = CLK_SET_RATE_PARENT,
 		.ops = &clk_rcg2_ops,
 		VDD_CX_FMAX_MAP4(
@@ -2700,6 +2813,55 @@
 	},
 };
 
+static struct clk_branch gcc_sdcc1_ice_core_clk = {
+	.halt_reg = 0x2600c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2600c,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_ice_core_clk",
+			.parent_names = (const char *[]){
+				"gcc_sdcc1_ice_core_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+	.halt_reg = 0x26008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+	.halt_reg = 0x26004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x26004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_sdcc1_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
 static struct clk_branch gcc_sdcc2_ahb_clk = {
 	.halt_reg = 0x14008,
 	.halt_check = BRANCH_HALT,
@@ -3824,6 +3986,12 @@
 	[GPLL0] = &gpll0.clkr,
 	[GPLL0_OUT_EVEN] = &gpll0_out_even.clkr,
 	[GPLL4] = &gpll4.clkr,
+	[GCC_SDCC1_AHB_CLK] = NULL,
+	[GCC_SDCC1_APPS_CLK] = NULL,
+	[GCC_SDCC1_ICE_CORE_CLK] = NULL,
+	[GCC_SDCC1_APPS_CLK_SRC] = NULL,
+	[GCC_SDCC1_ICE_CORE_CLK_SRC] = NULL,
+	[GPLL6] = NULL,
 };
 
 static const struct qcom_reset_map gcc_sdm845_resets[] = {
@@ -3853,6 +4021,7 @@
 	[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0x6a000 },
 	[GCC_PCIE_0_PHY_BCR] = { 0x6c01c },
 	[GCC_PCIE_1_PHY_BCR] = { 0x8e01c },
+	[GCC_SDCC1_BCR] = { 0x26000 },
 };
 
 /* List of RCG clocks and corresponding flags requested for DFS Mode */
@@ -3899,6 +4068,8 @@
 static const struct of_device_id gcc_sdm845_match_table[] = {
 	{ .compatible = "qcom,gcc-sdm845" },
 	{ .compatible = "qcom,gcc-sdm845-v2" },
+	{ .compatible = "qcom,gcc-sdm845-v2.1" },
+	{ .compatible = "qcom,gcc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, gcc_sdm845_match_table);
@@ -4007,6 +4178,85 @@
 		240000000;
 	gcc_ufs_phy_axi_clk_src.freq_tbl =
 		ftbl_gcc_ufs_card_axi_clk_src_sdm845_v2;
+	gcc_vsensor_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 600000000;
+}
+
+static void gcc_sdm845_fixup_sdm670(void)
+{
+	gcc_sdm845_fixup_sdm845v2();
+
+	gcc_sdm845_clocks[GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK] =
+					&gcc_sdcc1_ice_core_clk.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_APPS_CLK_SRC] =
+					&gcc_sdcc1_apps_clk_src.clkr;
+	gcc_sdm845_clocks[GCC_SDCC1_ICE_CORE_CLK_SRC] =
+					&gcc_sdcc1_ice_core_clk_src.clkr;
+	gcc_sdm845_clocks[GPLL6] = &gpll6.clkr;
+	gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_USB3_SEC_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_AGGRE_NOC_PCIE_TBU_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_CFG_NOC_USB3_SEC_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_CFG_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_MSTR_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_PIPE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_SLV_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_0_SLV_Q2A_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_CFG_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_MSTR_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_PIPE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_SLV_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_1_SLV_Q2A_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_PCIE_PHY_REFGEN_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AHB_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_AXI_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_ICE_CORE_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_PHY_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_0_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_RX_SYMBOL_1_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_TX_SYMBOL_0_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_HW_CTL_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_UFS_PHY_RX_SYMBOL_1_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MASTER_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_MOCK_UTMI_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB30_SEC_SLEEP_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_CLKREF_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_AUX_CLK_SRC] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_COM_AUX_CLK] = NULL;
+	gcc_sdm845_clocks[GCC_USB3_SEC_PHY_PIPE_CLK] = NULL;
+
+	gcc_cpuss_rbcpr_clk_src.freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src_sdm670;
+	gcc_cpuss_rbcpr_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+		50000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 50000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		100000000;
+	gcc_sdcc2_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_NOMINAL] =
+		201500000;
+	gcc_sdcc4_apps_clk_src.freq_tbl = ftbl_gcc_sdcc4_apps_clk_src_sdm670;
+	gcc_sdcc4_apps_clk_src.clkr.hw.init->rate_max[VDD_CX_LOWER] = 33333333;
 }
 
 static int gcc_sdm845_fixup(struct platform_device *pdev)
@@ -4018,8 +4268,11 @@
 	if (!compat || (compatlen <= 0))
 		return -EINVAL;
 
-	if (!strcmp(compat, "qcom,gcc-sdm845-v2"))
+	if (!strcmp(compat, "qcom,gcc-sdm845-v2") ||
+			!strcmp(compat, "qcom,gcc-sdm845-v2.1"))
 		gcc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,gcc-sdm670"))
+		gcc_sdm845_fixup_sdm670();
 
 	return 0;
 }
@@ -4034,14 +4287,6 @@
 	if (IS_ERR(regmap))
 		return PTR_ERR(regmap);
 
-	/*
-	 * Set the *_SLEEP_ENA bits to allow certain cpuss* clocks to be
-	 * turned off by hardware during certain apps low power modes.
-	 */
-	regmap_update_bits(regmap, GCC_APCS_CLOCK_SLEEP_ENA_VOTE_OFFSET,
-		CPUSS_AHB_CLK_SLEEP_ENA | SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA,
-		CPUSS_AHB_CLK_SLEEP_ENA | SYS_NOC_CPUSS_AHB_CLK_SLEEP_ENA);
-
 	vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx");
 	if (IS_ERR(vdd_cx.regulator[0])) {
 		if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER))
@@ -4079,8 +4324,8 @@
 	regmap_update_bits(regmap, GCC_MMSS_MISC, 0x3, 0x3);
 	regmap_update_bits(regmap, GCC_GPU_MISC, 0x3, 0x3);
 
-	/* Keep this clock on all the time on SDM845 v1 */
-	if (of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdm845"))
+	/* Keep this clock on all the times except on SDM845 v2.1 */
+	if (!of_device_is_compatible(pdev->dev.of_node, "qcom,gcc-sdm845-v2.1"))
 		clk_prepare_enable(gcc_aggre_noc_pcie_tbu_clk.clkr.hw.clk);
 
 	/* DFS clock registration */
diff --git a/drivers/clk/qcom/gcc-sdxpoorwills.c b/drivers/clk/qcom/gcc-sdxpoorwills.c
new file mode 100644
index 0000000..1b5cf61
--- /dev/null
+++ b/drivers/clk/qcom/gcc-sdxpoorwills.c
@@ -0,0 +1,1916 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "clk: %s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#include <dt-bindings/clock/qcom,gcc-sdxpoorwills.h>
+
+#include "common.h"
+#include "clk-regmap.h"
+#include "clk-pll.h"
+#include "clk-rcg.h"
+#include "clk-branch.h"
+#include "reset.h"
+
+#include "clk-alpha-pll.h"
+#include "vdd-level-sdm845.h"
+
+#define F(f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
+
+static DEFINE_VDD_REGULATORS(vdd_cx, VDD_CX_NUM, 1, vdd_corner);
+
+enum {
+	P_BI_TCXO,
+	P_CORE_BI_PLL_TEST_SE,
+	P_GPLL0_OUT_EVEN,
+	P_GPLL0_OUT_MAIN,
+	P_GPLL4_OUT_EVEN,
+	P_SLEEP_CLK,
+};
+
+static const struct parent_map gcc_parent_map_0[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_GPLL0_OUT_MAIN, 1 },
+	{ P_GPLL0_OUT_EVEN, 6 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_0[] = {
+	"bi_tcxo",
+	"gpll0",
+	"gpll0_out_even",
+	"core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_1[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_1[] = {
+	"bi_tcxo",
+	"core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_2[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_GPLL0_OUT_MAIN, 1 },
+	{ P_SLEEP_CLK, 5 },
+	{ P_GPLL0_OUT_EVEN, 6 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_2[] = {
+	"bi_tcxo",
+	"gpll0",
+	"core_pi_sleep_clk",
+	"gpll0_out_even",
+	"core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_3[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_SLEEP_CLK, 5 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_3[] = {
+	"bi_tcxo",
+	"core_pi_sleep_clk",
+	"core_bi_pll_test_se",
+};
+
+static const struct parent_map gcc_parent_map_4[] = {
+	{ P_BI_TCXO, 0 },
+	{ P_GPLL0_OUT_MAIN, 1 },
+	{ P_GPLL4_OUT_EVEN, 2 },
+	{ P_GPLL0_OUT_EVEN, 6 },
+	{ P_CORE_BI_PLL_TEST_SE, 7 },
+};
+
+static const char * const gcc_parent_names_4[] = {
+	"bi_tcxo",
+	"gpll0",
+	"gpll4_out_even",
+	"gpll0_out_even",
+	"core_bi_pll_test_se",
+};
+
+static struct pll_vco trion_vco[] = {
+	{ 249600000, 2000000000, 0 },
+};
+
+static struct clk_alpha_pll gpll0 = {
+	.offset = 0x0,
+	.vco_table = trion_vco,
+	.num_vco = ARRAY_SIZE(trion_vco),
+	.type = TRION_PLL,
+	.clkr = {
+		.enable_reg = 0x6d000,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gpll0",
+			.parent_names = (const char *[]){ "bi_tcxo" },
+			.num_parents = 1,
+			.ops = &clk_trion_fixed_pll_ops,
+			VDD_CX_FMAX_MAP4(
+				MIN, 615000000,
+				LOW, 1066000000,
+				LOW_L1, 1600000000,
+				NOMINAL, 2000000000),
+		},
+	},
+};
+
+static const struct clk_div_table post_div_table_trion_even[] = {
+	{ 0x0, 1 },
+	{ 0x1, 2 },
+	{ 0x3, 4 },
+	{ 0x7, 8 },
+	{ }
+};
+
+static struct clk_alpha_pll_postdiv gpll0_out_even = {
+	.offset = 0x0,
+	.post_div_shift = 8,
+	.post_div_table = post_div_table_trion_even,
+	.num_post_div = ARRAY_SIZE(post_div_table_trion_even),
+	.width = 4,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gpll0_out_even",
+		.parent_names = (const char *[]){ "gpll0" },
+		.num_parents = 1,
+		.ops = &clk_trion_pll_postdiv_ops,
+	},
+};
+
+static struct clk_alpha_pll gpll4 = {
+	.offset = 0x76000,
+	.vco_table = trion_vco,
+	.num_vco = ARRAY_SIZE(trion_vco),
+	.type = TRION_PLL,
+	.clkr = {
+		.enable_reg = 0x6d000,
+		.enable_mask = BIT(4),
+		.hw.init = &(struct clk_init_data){
+			.name = "gpll4",
+			.parent_names = (const char *[]){ "bi_tcxo" },
+			.num_parents = 1,
+			.ops = &clk_trion_fixed_pll_ops,
+			VDD_CX_FMAX_MAP4(
+				MIN, 615000000,
+				LOW, 1066000000,
+				LOW_L1, 1600000000,
+				NOMINAL, 2000000000),
+		},
+	},
+};
+
+static struct clk_alpha_pll_postdiv gpll4_out_even = {
+	.offset = 0x76000,
+	.post_div_shift = 8,
+	.post_div_table = post_div_table_trion_even,
+	.num_post_div = ARRAY_SIZE(post_div_table_trion_even),
+	.width = 4,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gpll4_out_even",
+		.parent_names = (const char *[]){ "gpll4" },
+		.num_parents = 1,
+		.ops = &clk_trion_pll_postdiv_ops,
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_qup1_i2c_apps_clk_src[] = {
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_blsp1_qup1_i2c_apps_clk_src = {
+	.cmd_rcgr = 0x11024,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_i2c_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup1_i2c_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 50000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_qup1_spi_apps_clk_src[] = {
+	F(960000, P_BI_TCXO, 10, 1, 2),
+	F(4800000, P_BI_TCXO, 4, 0, 0),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(15000000, P_GPLL0_OUT_EVEN, 5, 1, 4),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(24000000, P_GPLL0_OUT_MAIN, 12.5, 1, 2),
+	F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_blsp1_qup1_spi_apps_clk_src = {
+	.cmd_rcgr = 0x1100c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_spi_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup1_spi_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 25000000,
+			NOMINAL, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup2_i2c_apps_clk_src = {
+	.cmd_rcgr = 0x13024,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_i2c_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup2_i2c_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup2_spi_apps_clk_src = {
+	.cmd_rcgr = 0x1300c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_spi_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup2_spi_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 25000000,
+			NOMINAL, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup3_i2c_apps_clk_src = {
+	.cmd_rcgr = 0x15024,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_i2c_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup3_i2c_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup3_spi_apps_clk_src = {
+	.cmd_rcgr = 0x1500c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_spi_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup3_spi_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 25000000,
+			NOMINAL, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup4_i2c_apps_clk_src = {
+	.cmd_rcgr = 0x17024,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_i2c_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup4_i2c_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 50000000),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_qup4_spi_apps_clk_src = {
+	.cmd_rcgr = 0x1700c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_qup1_spi_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_qup4_spi_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 25000000,
+			NOMINAL, 50000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_blsp1_uart1_apps_clk_src[] = {
+	F(3686400, P_GPLL0_OUT_EVEN, 1, 192, 15625),
+	F(7372800, P_GPLL0_OUT_EVEN, 1, 384, 15625),
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(14745600, P_GPLL0_OUT_EVEN, 1, 768, 15625),
+	F(16000000, P_GPLL0_OUT_EVEN, 1, 4, 75),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(19354839, P_GPLL0_OUT_MAIN, 15.5, 1, 2),
+	F(20000000, P_GPLL0_OUT_MAIN, 15, 1, 2),
+	F(20689655, P_GPLL0_OUT_MAIN, 14.5, 1, 2),
+	F(21428571, P_GPLL0_OUT_MAIN, 14, 1, 2),
+	F(22222222, P_GPLL0_OUT_MAIN, 13.5, 1, 2),
+	F(23076923, P_GPLL0_OUT_MAIN, 13, 1, 2),
+	F(24000000, P_GPLL0_OUT_MAIN, 5, 1, 5),
+	F(25000000, P_GPLL0_OUT_MAIN, 12, 1, 2),
+	F(26086957, P_GPLL0_OUT_MAIN, 11.5, 1, 2),
+	F(27272727, P_GPLL0_OUT_MAIN, 11, 1, 2),
+	F(28571429, P_GPLL0_OUT_MAIN, 10.5, 1, 2),
+	F(32000000, P_GPLL0_OUT_MAIN, 1, 4, 75),
+	F(40000000, P_GPLL0_OUT_MAIN, 15, 0, 0),
+	F(46400000, P_GPLL0_OUT_MAIN, 1, 29, 375),
+	F(48000000, P_GPLL0_OUT_MAIN, 12.5, 0, 0),
+	F(51200000, P_GPLL0_OUT_MAIN, 1, 32, 375),
+	F(56000000, P_GPLL0_OUT_MAIN, 1, 7, 75),
+	F(58982400, P_GPLL0_OUT_MAIN, 1, 1536, 15625),
+	F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+	F(63157895, P_GPLL0_OUT_MAIN, 9.5, 0, 0),
+	{ }
+};
+
+
+static struct clk_rcg2 gcc_blsp1_uart1_apps_clk_src = {
+	.cmd_rcgr = 0x1200c,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_uart1_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 48000000,
+			NOMINAL, 63157895),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_uart2_apps_clk_src = {
+	.cmd_rcgr = 0x1400c,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_uart2_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 48000000,
+			NOMINAL, 63157895),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_uart3_apps_clk_src = {
+	.cmd_rcgr = 0x1600c,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_uart3_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 48000000,
+			NOMINAL, 63157895),
+	},
+};
+
+static struct clk_rcg2 gcc_blsp1_uart4_apps_clk_src = {
+	.cmd_rcgr = 0x1800c,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_blsp1_uart1_apps_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_blsp1_uart4_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 48000000,
+			NOMINAL, 63157895),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_cpuss_ahb_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(133333333, P_GPLL0_OUT_MAIN, 4.5, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_cpuss_ahb_clk_src = {
+	.cmd_rcgr = 0x24010,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_cpuss_ahb_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_cpuss_ahb_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			NOMINAL, 100000000,
+			HIGH, 133333333),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_cpuss_rbcpr_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_MAIN, 12, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_cpuss_rbcpr_clk_src = {
+	.cmd_rcgr = 0x2402c,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_cpuss_rbcpr_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_cpuss_rbcpr_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP2(
+			MIN, 19200000,
+			NOMINAL, 50000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_emac_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(125000000, P_GPLL4_OUT_EVEN, 4, 0, 0),
+	F(250000000, P_GPLL4_OUT_EVEN, 2, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_emac_clk_src = {
+	.cmd_rcgr = 0x47020,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_4,
+	.freq_tbl = ftbl_gcc_emac_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_emac_clk_src",
+		.parent_names = gcc_parent_names_4,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 125000000,
+			NOMINAL, 250000000),
+	},
+};
+
+static struct clk_rcg2 gcc_emac_ptp_clk_src = {
+	.cmd_rcgr = 0x47038,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_4,
+	.freq_tbl = ftbl_gcc_emac_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_emac_ptp_clk_src",
+		.parent_names = gcc_parent_names_4,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 125000000,
+			NOMINAL, 250000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_gp1_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(25000000, P_GPLL0_OUT_EVEN, 12, 0, 0),
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_gp1_clk_src = {
+	.cmd_rcgr = 0x2b004,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_2,
+	.freq_tbl = ftbl_gcc_gp1_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_gp1_clk_src",
+		.parent_names = gcc_parent_names_2,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 200000000),
+	},
+};
+
+static struct clk_rcg2 gcc_gp2_clk_src = {
+	.cmd_rcgr = 0x2c004,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_2,
+	.freq_tbl = ftbl_gcc_gp1_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_gp2_clk_src",
+		.parent_names = gcc_parent_names_2,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 200000000),
+	},
+};
+
+static struct clk_rcg2 gcc_gp3_clk_src = {
+	.cmd_rcgr = 0x2d004,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_2,
+	.freq_tbl = ftbl_gcc_gp1_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_gp3_clk_src",
+		.parent_names = gcc_parent_names_2,
+		.num_parents = 5,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 200000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_pcie_aux_phy_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_pcie_aux_phy_clk_src = {
+	.cmd_rcgr = 0x37030,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_3,
+	.freq_tbl = ftbl_gcc_pcie_aux_phy_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_pcie_aux_phy_clk_src",
+		.parent_names = gcc_parent_names_3,
+		.num_parents = 3,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP1(
+			MIN, 19200000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_pcie_phy_refgen_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_pcie_phy_refgen_clk_src = {
+	.cmd_rcgr = 0x39010,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_pcie_phy_refgen_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_pcie_phy_refgen_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP2(
+			MIN, 19200000,
+			LOW, 100000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_pdm2_clk_src[] = {
+	F(9600000, P_BI_TCXO, 2, 0, 0),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_pdm2_clk_src = {
+	.cmd_rcgr = 0x19010,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_pdm2_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_pdm2_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 9600000,
+			LOWER, 19200000,
+			LOW, 60000000),
+	},
+};
+
+static struct clk_rcg2 gcc_sdcc1_apps_clk_src = {
+	.cmd_rcgr = 0xf00c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_gp1_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_sdcc1_apps_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP4(
+			MIN, 19200000,
+			LOWER, 50000000,
+			LOW, 100000000,
+			NOMINAL, 200000000),
+	},
+};
+
+static struct clk_rcg2 gcc_spmi_fetcher_clk_src = {
+	.cmd_rcgr = 0x3f00c,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_1,
+	.freq_tbl = ftbl_gcc_pcie_aux_phy_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_spmi_fetcher_clk_src",
+		.parent_names = gcc_parent_names_1,
+		.num_parents = 2,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP1(
+			MIN, 19200000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_usb30_master_clk_src[] = {
+	F(50000000, P_GPLL0_OUT_EVEN, 6, 0, 0),
+	F(75000000, P_GPLL0_OUT_EVEN, 4, 0, 0),
+	F(100000000, P_GPLL0_OUT_MAIN, 6, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN, 3, 0, 0),
+	F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_usb30_master_clk_src = {
+	.cmd_rcgr = 0xb01c,
+	.mnd_width = 8,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_usb30_master_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_usb30_master_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP5(
+			MIN, 50000000,
+			LOWER, 75000000,
+			LOW, 100000000,
+			NOMINAL, 200000000,
+			HIGH, 240000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_usb30_mock_utmi_clk_src[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(40000000, P_GPLL0_OUT_EVEN, 7.5, 0, 0),
+	F(60000000, P_GPLL0_OUT_MAIN, 10, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_usb30_mock_utmi_clk_src = {
+	.cmd_rcgr = 0xb034,
+	.mnd_width = 0,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_0,
+	.freq_tbl = ftbl_gcc_usb30_mock_utmi_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_usb30_mock_utmi_clk_src",
+		.parent_names = gcc_parent_names_0,
+		.num_parents = 4,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP3(
+			MIN, 19200000,
+			LOWER, 40000000,
+			LOW, 60000000),
+	},
+};
+
+static const struct freq_tbl ftbl_gcc_usb3_phy_aux_clk_src[] = {
+	F(1000000, P_BI_TCXO, 1, 5, 96),
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	{ }
+};
+
+static struct clk_rcg2 gcc_usb3_phy_aux_clk_src = {
+	.cmd_rcgr = 0xb05c,
+	.mnd_width = 16,
+	.hid_width = 5,
+	.parent_map = gcc_parent_map_3,
+	.freq_tbl = ftbl_gcc_usb3_phy_aux_clk_src,
+	.clkr.hw.init = &(struct clk_init_data){
+		.name = "gcc_usb3_phy_aux_clk_src",
+		.parent_names = gcc_parent_names_3,
+		.num_parents = 3,
+		.flags = CLK_SET_RATE_PARENT,
+		.ops = &clk_rcg2_ops,
+		VDD_CX_FMAX_MAP1(
+			MIN, 19200000),
+	},
+};
+
+static struct clk_branch gcc_blsp1_ahb_clk = {
+	.halt_reg = 0x10004,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(25),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup1_i2c_apps_clk = {
+	.halt_reg = 0x11008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x11008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup1_i2c_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup1_i2c_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup1_spi_apps_clk = {
+	.halt_reg = 0x11004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x11004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup1_spi_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup1_spi_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup2_i2c_apps_clk = {
+	.halt_reg = 0x13008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x13008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup2_i2c_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup2_i2c_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup2_spi_apps_clk = {
+	.halt_reg = 0x13004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x13004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup2_spi_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup2_spi_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup3_i2c_apps_clk = {
+	.halt_reg = 0x15008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x15008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup3_i2c_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup3_i2c_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup3_spi_apps_clk = {
+	.halt_reg = 0x15004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x15004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup3_spi_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup3_spi_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup4_i2c_apps_clk = {
+	.halt_reg = 0x17008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x17008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup4_i2c_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup4_i2c_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_qup4_spi_apps_clk = {
+	.halt_reg = 0x17004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x17004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_qup4_spi_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_qup4_spi_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_sleep_clk = {
+	.halt_reg = 0x10008,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(26),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_sleep_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_uart1_apps_clk = {
+	.halt_reg = 0x12004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x12004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_uart1_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_uart1_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_uart2_apps_clk = {
+	.halt_reg = 0x14004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x14004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_uart2_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_uart2_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_uart3_apps_clk = {
+	.halt_reg = 0x16004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x16004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_uart3_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_uart3_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_blsp1_uart4_apps_clk = {
+	.halt_reg = 0x18004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x18004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_blsp1_uart4_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_blsp1_uart4_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_boot_rom_ahb_clk = {
+	.halt_reg = 0x1c004,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x1c004,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(10),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_boot_rom_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_ce1_ahb_clk = {
+	.halt_reg = 0x2100c,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x2100c,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(3),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_ce1_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_ce1_axi_clk = {
+	.halt_reg = 0x21008,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(4),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_ce1_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_ce1_clk = {
+	.halt_reg = 0x21004,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(5),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_ce1_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_cpuss_ahb_clk = {
+	.halt_reg = 0x24000,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(21),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_cpuss_ahb_clk",
+			.parent_names = (const char *[]){
+				"gcc_cpuss_ahb_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_cpuss_gnoc_clk = {
+	.halt_reg = 0x24004,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x24004,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(22),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_cpuss_gnoc_clk",
+			.flags = CLK_IS_CRITICAL,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_cpuss_rbcpr_clk = {
+	.halt_reg = 0x24008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x24008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_cpuss_rbcpr_clk",
+			.parent_names = (const char *[]){
+				"gcc_cpuss_rbcpr_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_eth_axi_clk = {
+	.halt_reg = 0x4701c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x4701c,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_eth_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_eth_ptp_clk = {
+	.halt_reg = 0x47018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x47018,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_eth_ptp_clk",
+			.parent_names = (const char *[]){
+				"gcc_emac_ptp_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_eth_rgmii_clk = {
+	.halt_reg = 0x47010,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x47010,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_eth_rgmii_clk",
+			.parent_names = (const char *[]){
+				"gcc_emac_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_eth_slave_ahb_clk = {
+	.halt_reg = 0x47014,
+	.halt_check = BRANCH_HALT,
+	.hwcg_reg = 0x47014,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x47014,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_eth_slave_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_gp1_clk = {
+	.halt_reg = 0x2b000,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2b000,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_gp1_clk",
+			.parent_names = (const char *[]){
+				"gcc_gp1_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_gp2_clk = {
+	.halt_reg = 0x2c000,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2c000,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_gp2_clk",
+			.parent_names = (const char *[]){
+				"gcc_gp2_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_gp3_clk = {
+	.halt_reg = 0x2d000,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x2d000,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_gp3_clk",
+			.parent_names = (const char *[]){
+				"gcc_gp3_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_mss_cfg_ahb_clk = {
+	.halt_reg = 0x40000,
+	.halt_check = BRANCH_HALT,
+	.hwcg_reg = 0x40000,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x40000,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_mss_cfg_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_gate2 gcc_mss_gpll0_div_clk_src = {
+	.udelay = 500,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(17),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_mss_gpll0_div_clk_src",
+			.ops = &clk_gate2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_mss_snoc_axi_clk = {
+	.halt_reg = 0x40148,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x40148,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_mss_snoc_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_aux_clk = {
+	.halt_reg = 0x37020,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(3),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_aux_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_cfg_ahb_clk = {
+	.halt_reg = 0x3701c,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x3701c,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(2),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_cfg_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_mstr_axi_clk = {
+	.halt_reg = 0x37018,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(1),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_mstr_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_phy_refgen_clk = {
+	.halt_reg = 0x39028,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x39028,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_phy_refgen_clk",
+			.parent_names = (const char *[]){
+				"gcc_pcie_phy_refgen_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_pipe_clk = {
+	.halt_reg = 0x37028,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(4),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_pipe_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_sleep_clk = {
+	.halt_reg = 0x37024,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(6),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_sleep_clk",
+			.parent_names = (const char *[]){
+				"gcc_pcie_aux_phy_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_slv_axi_clk = {
+	.halt_reg = 0x37014,
+	.halt_check = BRANCH_HALT_VOTED,
+	.hwcg_reg = 0x37014,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_slv_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pcie_slv_q2a_axi_clk = {
+	.halt_reg = 0x37010,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d00c,
+		.enable_mask = BIT(5),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pcie_slv_q2a_axi_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pdm2_clk = {
+	.halt_reg = 0x1900c,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x1900c,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pdm2_clk",
+			.parent_names = (const char *[]){
+				"gcc_pdm2_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pdm_ahb_clk = {
+	.halt_reg = 0x19004,
+	.halt_check = BRANCH_HALT,
+	.hwcg_reg = 0x19004,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0x19004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pdm_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_pdm_xo4_clk = {
+	.halt_reg = 0x19008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x19008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_pdm_xo4_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_prng_ahb_clk = {
+	.halt_reg = 0x1a004,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(13),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_prng_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_ahb_clk = {
+	.halt_reg = 0xf008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xf008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sdcc1_apps_clk = {
+	.halt_reg = 0xf004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xf004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sdcc1_apps_clk",
+			.parent_names = (const char *[]){
+				"gcc_sdcc1_apps_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_spmi_fetcher_ahb_clk = {
+	.halt_reg = 0x3f008,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x3f008,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_spmi_fetcher_ahb_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_spmi_fetcher_clk = {
+	.halt_reg = 0x3f004,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x3f004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_spmi_fetcher_clk",
+			.parent_names = (const char *[]){
+				"gcc_spmi_fetcher_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sys_noc_cpuss_ahb_clk = {
+	.halt_reg = 0x400c,
+	.halt_check = BRANCH_HALT_VOTED,
+	.clkr = {
+		.enable_reg = 0x6d004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sys_noc_cpuss_ahb_clk",
+			.parent_names = (const char *[]){
+				"gcc_cpuss_ahb_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_sys_noc_usb3_clk = {
+	.halt_reg = 0x4018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0x4018,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_sys_noc_usb3_clk",
+			.parent_names = (const char *[]){
+				"gcc_usb30_master_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb30_master_clk = {
+	.halt_reg = 0xb010,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb010,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb30_master_clk",
+			.parent_names = (const char *[]){
+				"gcc_usb30_master_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb30_mock_utmi_clk = {
+	.halt_reg = 0xb018,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb018,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb30_mock_utmi_clk",
+			.parent_names = (const char *[]){
+				"gcc_usb30_mock_utmi_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb30_sleep_clk = {
+	.halt_reg = 0xb014,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb014,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb30_sleep_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb3_phy_aux_clk = {
+	.halt_reg = 0xb050,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb050,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb3_phy_aux_clk",
+			.parent_names = (const char *[]){
+				"gcc_usb3_phy_aux_clk_src",
+			},
+			.num_parents = 1,
+			.flags = CLK_SET_RATE_PARENT,
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb3_phy_pipe_clk = {
+	.halt_reg = 0xb054,
+	.halt_check = BRANCH_HALT,
+	.clkr = {
+		.enable_reg = 0xb054,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb3_phy_pipe_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_branch gcc_usb_phy_cfg_ahb2phy_clk = {
+	.halt_reg = 0xe004,
+	.halt_check = BRANCH_HALT,
+	.hwcg_reg = 0xe004,
+	.hwcg_bit = 1,
+	.clkr = {
+		.enable_reg = 0xe004,
+		.enable_mask = BIT(0),
+		.hw.init = &(struct clk_init_data){
+			.name = "gcc_usb_phy_cfg_ahb2phy_clk",
+			.ops = &clk_branch2_ops,
+		},
+	},
+};
+
+static struct clk_regmap *gcc_sdxpoorwills_clocks[] = {
+	[GCC_BLSP1_AHB_CLK] = &gcc_blsp1_ahb_clk.clkr,
+	[GCC_BLSP1_QUP1_I2C_APPS_CLK] = &gcc_blsp1_qup1_i2c_apps_clk.clkr,
+	[GCC_BLSP1_QUP1_I2C_APPS_CLK_SRC] =
+		&gcc_blsp1_qup1_i2c_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP1_SPI_APPS_CLK] = &gcc_blsp1_qup1_spi_apps_clk.clkr,
+	[GCC_BLSP1_QUP1_SPI_APPS_CLK_SRC] =
+		&gcc_blsp1_qup1_spi_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP2_I2C_APPS_CLK] = &gcc_blsp1_qup2_i2c_apps_clk.clkr,
+	[GCC_BLSP1_QUP2_I2C_APPS_CLK_SRC] =
+		&gcc_blsp1_qup2_i2c_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP2_SPI_APPS_CLK] = &gcc_blsp1_qup2_spi_apps_clk.clkr,
+	[GCC_BLSP1_QUP2_SPI_APPS_CLK_SRC] =
+		&gcc_blsp1_qup2_spi_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP3_I2C_APPS_CLK] = &gcc_blsp1_qup3_i2c_apps_clk.clkr,
+	[GCC_BLSP1_QUP3_I2C_APPS_CLK_SRC] =
+		&gcc_blsp1_qup3_i2c_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP3_SPI_APPS_CLK] = &gcc_blsp1_qup3_spi_apps_clk.clkr,
+	[GCC_BLSP1_QUP3_SPI_APPS_CLK_SRC] =
+		&gcc_blsp1_qup3_spi_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP4_I2C_APPS_CLK] = &gcc_blsp1_qup4_i2c_apps_clk.clkr,
+	[GCC_BLSP1_QUP4_I2C_APPS_CLK_SRC] =
+		&gcc_blsp1_qup4_i2c_apps_clk_src.clkr,
+	[GCC_BLSP1_QUP4_SPI_APPS_CLK] = &gcc_blsp1_qup4_spi_apps_clk.clkr,
+	[GCC_BLSP1_QUP4_SPI_APPS_CLK_SRC] =
+		&gcc_blsp1_qup4_spi_apps_clk_src.clkr,
+	[GCC_BLSP1_SLEEP_CLK] = &gcc_blsp1_sleep_clk.clkr,
+	[GCC_BLSP1_UART1_APPS_CLK] = &gcc_blsp1_uart1_apps_clk.clkr,
+	[GCC_BLSP1_UART1_APPS_CLK_SRC] = &gcc_blsp1_uart1_apps_clk_src.clkr,
+	[GCC_BLSP1_UART2_APPS_CLK] = &gcc_blsp1_uart2_apps_clk.clkr,
+	[GCC_BLSP1_UART2_APPS_CLK_SRC] = &gcc_blsp1_uart2_apps_clk_src.clkr,
+	[GCC_BLSP1_UART3_APPS_CLK] = &gcc_blsp1_uart3_apps_clk.clkr,
+	[GCC_BLSP1_UART3_APPS_CLK_SRC] = &gcc_blsp1_uart3_apps_clk_src.clkr,
+	[GCC_BLSP1_UART4_APPS_CLK] = &gcc_blsp1_uart4_apps_clk.clkr,
+	[GCC_BLSP1_UART4_APPS_CLK_SRC] = &gcc_blsp1_uart4_apps_clk_src.clkr,
+	[GCC_BOOT_ROM_AHB_CLK] = &gcc_boot_rom_ahb_clk.clkr,
+	[GCC_CE1_AHB_CLK] = &gcc_ce1_ahb_clk.clkr,
+	[GCC_CE1_AXI_CLK] = &gcc_ce1_axi_clk.clkr,
+	[GCC_CE1_CLK] = &gcc_ce1_clk.clkr,
+	[GCC_CPUSS_AHB_CLK] = &gcc_cpuss_ahb_clk.clkr,
+	[GCC_CPUSS_AHB_CLK_SRC] = &gcc_cpuss_ahb_clk_src.clkr,
+	[GCC_CPUSS_GNOC_CLK] = &gcc_cpuss_gnoc_clk.clkr,
+	[GCC_CPUSS_RBCPR_CLK] = &gcc_cpuss_rbcpr_clk.clkr,
+	[GCC_CPUSS_RBCPR_CLK_SRC] = &gcc_cpuss_rbcpr_clk_src.clkr,
+	[GCC_EMAC_CLK_SRC] = &gcc_emac_clk_src.clkr,
+	[GCC_EMAC_PTP_CLK_SRC] = &gcc_emac_ptp_clk_src.clkr,
+	[GCC_ETH_AXI_CLK] = &gcc_eth_axi_clk.clkr,
+	[GCC_ETH_PTP_CLK] = &gcc_eth_ptp_clk.clkr,
+	[GCC_ETH_RGMII_CLK] = &gcc_eth_rgmii_clk.clkr,
+	[GCC_ETH_SLAVE_AHB_CLK] = &gcc_eth_slave_ahb_clk.clkr,
+	[GCC_GP1_CLK] = &gcc_gp1_clk.clkr,
+	[GCC_GP1_CLK_SRC] = &gcc_gp1_clk_src.clkr,
+	[GCC_GP2_CLK] = &gcc_gp2_clk.clkr,
+	[GCC_GP2_CLK_SRC] = &gcc_gp2_clk_src.clkr,
+	[GCC_GP3_CLK] = &gcc_gp3_clk.clkr,
+	[GCC_GP3_CLK_SRC] = &gcc_gp3_clk_src.clkr,
+	[GCC_MSS_CFG_AHB_CLK] = &gcc_mss_cfg_ahb_clk.clkr,
+	[GCC_MSS_GPLL0_DIV_CLK_SRC] = &gcc_mss_gpll0_div_clk_src.clkr,
+	[GCC_MSS_SNOC_AXI_CLK] = &gcc_mss_snoc_axi_clk.clkr,
+	[GCC_PCIE_AUX_CLK] = &gcc_pcie_aux_clk.clkr,
+	[GCC_PCIE_AUX_PHY_CLK_SRC] = &gcc_pcie_aux_phy_clk_src.clkr,
+	[GCC_PCIE_CFG_AHB_CLK] = &gcc_pcie_cfg_ahb_clk.clkr,
+	[GCC_PCIE_MSTR_AXI_CLK] = &gcc_pcie_mstr_axi_clk.clkr,
+	[GCC_PCIE_PHY_REFGEN_CLK] = &gcc_pcie_phy_refgen_clk.clkr,
+	[GCC_PCIE_PHY_REFGEN_CLK_SRC] = &gcc_pcie_phy_refgen_clk_src.clkr,
+	[GCC_PCIE_PIPE_CLK] = &gcc_pcie_pipe_clk.clkr,
+	[GCC_PCIE_SLEEP_CLK] = &gcc_pcie_sleep_clk.clkr,
+	[GCC_PCIE_SLV_AXI_CLK] = &gcc_pcie_slv_axi_clk.clkr,
+	[GCC_PCIE_SLV_Q2A_AXI_CLK] = &gcc_pcie_slv_q2a_axi_clk.clkr,
+	[GCC_PDM2_CLK] = &gcc_pdm2_clk.clkr,
+	[GCC_PDM2_CLK_SRC] = &gcc_pdm2_clk_src.clkr,
+	[GCC_PDM_AHB_CLK] = &gcc_pdm_ahb_clk.clkr,
+	[GCC_PDM_XO4_CLK] = &gcc_pdm_xo4_clk.clkr,
+	[GCC_PRNG_AHB_CLK] = &gcc_prng_ahb_clk.clkr,
+	[GCC_SDCC1_AHB_CLK] = &gcc_sdcc1_ahb_clk.clkr,
+	[GCC_SDCC1_APPS_CLK] = &gcc_sdcc1_apps_clk.clkr,
+	[GCC_SDCC1_APPS_CLK_SRC] = &gcc_sdcc1_apps_clk_src.clkr,
+	[GCC_SPMI_FETCHER_AHB_CLK] = &gcc_spmi_fetcher_ahb_clk.clkr,
+	[GCC_SPMI_FETCHER_CLK] = &gcc_spmi_fetcher_clk.clkr,
+	[GCC_SPMI_FETCHER_CLK_SRC] = &gcc_spmi_fetcher_clk_src.clkr,
+	[GCC_SYS_NOC_CPUSS_AHB_CLK] = &gcc_sys_noc_cpuss_ahb_clk.clkr,
+	[GCC_SYS_NOC_USB3_CLK] = &gcc_sys_noc_usb3_clk.clkr,
+	[GCC_USB30_MASTER_CLK] = &gcc_usb30_master_clk.clkr,
+	[GCC_USB30_MASTER_CLK_SRC] = &gcc_usb30_master_clk_src.clkr,
+	[GCC_USB30_MOCK_UTMI_CLK] = &gcc_usb30_mock_utmi_clk.clkr,
+	[GCC_USB30_MOCK_UTMI_CLK_SRC] = &gcc_usb30_mock_utmi_clk_src.clkr,
+	[GCC_USB30_SLEEP_CLK] = &gcc_usb30_sleep_clk.clkr,
+	[GCC_USB3_PHY_AUX_CLK] = &gcc_usb3_phy_aux_clk.clkr,
+	[GCC_USB3_PHY_AUX_CLK_SRC] = &gcc_usb3_phy_aux_clk_src.clkr,
+	[GCC_USB3_PHY_PIPE_CLK] = &gcc_usb3_phy_pipe_clk.clkr,
+	[GCC_USB_PHY_CFG_AHB2PHY_CLK] = &gcc_usb_phy_cfg_ahb2phy_clk.clkr,
+	[GPLL0] = &gpll0.clkr,
+	[GPLL0_OUT_EVEN] = &gpll0_out_even.clkr,
+	[GPLL4] = &gpll4.clkr,
+	[GPLL4_OUT_EVEN] = &gpll4_out_even.clkr,
+};
+
+static const struct qcom_reset_map gcc_sdxpoorwills_resets[] = {
+	[GCC_BLSP1_QUP1_BCR] = { 0x11000 },
+	[GCC_BLSP1_QUP2_BCR] = { 0x13000 },
+	[GCC_BLSP1_QUP3_BCR] = { 0x15000 },
+	[GCC_BLSP1_QUP4_BCR] = { 0x17000 },
+	[GCC_BLSP1_UART2_BCR] = { 0x14000 },
+	[GCC_BLSP1_UART3_BCR] = { 0x16000 },
+	[GCC_BLSP1_UART4_BCR] = { 0x18000 },
+	[GCC_CE1_BCR] = { 0x21000 },
+	[GCC_EMAC_BCR] = { 0x47000 },
+	[GCC_PCIE_BCR] = { 0x37000 },
+	[GCC_PCIE_PHY_BCR] = { 0x39000 },
+	[GCC_PDM_BCR] = { 0x19000 },
+	[GCC_PRNG_BCR] = { 0x1a000 },
+	[GCC_SDCC1_BCR] = { 0xf000 },
+	[GCC_SPMI_FETCHER_BCR] = { 0x3f000 },
+	[GCC_USB30_BCR] = { 0xb000 },
+	[GCC_USB_PHY_CFG_AHB2PHY_BCR] = { 0xe000 },
+};
+
+
+static const struct regmap_config gcc_sdxpoorwills_regmap_config = {
+	.reg_bits	= 32,
+	.reg_stride	= 4,
+	.val_bits	= 32,
+	.max_register	= 0x9b040,
+	.fast_io	= true,
+};
+
+static const struct qcom_cc_desc gcc_sdxpoorwills_desc = {
+	.config = &gcc_sdxpoorwills_regmap_config,
+	.clks = gcc_sdxpoorwills_clocks,
+	.num_clks = ARRAY_SIZE(gcc_sdxpoorwills_clocks),
+	.resets = gcc_sdxpoorwills_resets,
+	.num_resets = ARRAY_SIZE(gcc_sdxpoorwills_resets),
+};
+
+static const struct of_device_id gcc_sdxpoorwills_match_table[] = {
+	{ .compatible = "qcom,gcc-sdxpoorwills" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, gcc_sdxpoorwills_match_table);
+
+static int gcc_sdxpoorwills_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct regmap *regmap;
+
+	regmap = qcom_cc_map(pdev, &gcc_sdxpoorwills_desc);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	vdd_cx.regulator[0] = devm_regulator_get(&pdev->dev, "vdd_cx");
+	if (IS_ERR(vdd_cx.regulator[0])) {
+		if (!(PTR_ERR(vdd_cx.regulator[0]) == -EPROBE_DEFER))
+			dev_err(&pdev->dev,
+				"Unable to get vdd_cx regulator\n");
+		return PTR_ERR(vdd_cx.regulator[0]);
+	}
+
+	ret = qcom_cc_really_probe(pdev, &gcc_sdxpoorwills_desc, regmap);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register GCC clocks\n");
+		return ret;
+	}
+
+	dev_info(&pdev->dev, "Registered GCC clocks\n");
+
+	return ret;
+}
+
+static struct platform_driver gcc_sdxpoorwills_driver = {
+	.probe		= gcc_sdxpoorwills_probe,
+	.driver		= {
+		.name	= "gcc-sdxpoorwills",
+		.of_match_table = gcc_sdxpoorwills_match_table,
+	},
+};
+
+static int __init gcc_sdxpoorwills_init(void)
+{
+	return platform_driver_register(&gcc_sdxpoorwills_driver);
+}
+core_initcall(gcc_sdxpoorwills_init);
+
+static void __exit gcc_sdxpoorwills_exit(void)
+{
+	platform_driver_unregister(&gcc_sdxpoorwills_driver);
+}
+module_exit(gcc_sdxpoorwills_exit);
+
+MODULE_DESCRIPTION("QTI GCC SDXPOORWILLS Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:gcc-sdxpoorwills");
diff --git a/drivers/clk/qcom/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index cf4a8a5..53446a2 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -224,6 +224,12 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src_sdm670[] = {
+	F(19200000, P_BI_TCXO, 1, 0, 0),
+	F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gpu_cc_gmu_clk_src = {
 	.cmd_rcgr = 0x1120,
 	.mnd_width = 0,
@@ -279,6 +285,19 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670[] = {
+	F(180000000, P_CRC_DIV,  1, 0, 0),
+	F(267000000, P_CRC_DIV,  1, 0, 0),
+	F(355000000, P_CRC_DIV,  1, 0, 0),
+	F(430000000, P_CRC_DIV,  1, 0, 0),
+	F(565000000, P_CRC_DIV,  1, 0, 0),
+	F(650000000, P_CRC_DIV,  1, 0, 0),
+	F(700000000, P_CRC_DIV,  1, 0, 0),
+	F(750000000, P_CRC_DIV,  1, 0, 0),
+	F(780000000, P_CRC_DIV,  1, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 gpu_cc_gx_gfx3d_clk_src = {
 	.cmd_rcgr = 0x101c,
 	.mnd_width = 0,
@@ -330,19 +349,6 @@
 	},
 };
 
-static struct clk_branch gpu_cc_ahb_clk = {
-	.halt_reg = 0x1078,
-	.halt_check = BRANCH_HALT,
-	.clkr = {
-		.enable_reg = 0x1078,
-		.enable_mask = BIT(0),
-		.hw.init = &(struct clk_init_data){
-			.name = "gpu_cc_ahb_clk",
-			.ops = &clk_branch2_ops,
-		},
-	},
-};
-
 static struct clk_branch gpu_cc_crc_ahb_clk = {
 	.halt_reg = 0x107c,
 	.halt_check = BRANCH_HALT,
@@ -527,7 +533,6 @@
 static struct clk_regmap *gpu_cc_sdm845_clocks[] = {
 	[GPU_CC_ACD_AHB_CLK] = &gpu_cc_acd_ahb_clk.clkr,
 	[GPU_CC_ACD_CXO_CLK] = &gpu_cc_acd_cxo_clk.clkr,
-	[GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr,
 	[GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr,
 	[GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr,
 	[GPU_CC_CX_GFX3D_CLK] = &gpu_cc_cx_gfx3d_clk.clkr,
@@ -585,6 +590,7 @@
 static const struct of_device_id gpu_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,gpucc-sdm845" },
 	{ .compatible = "qcom,gpucc-sdm845-v2" },
+	{ .compatible = "qcom,gpucc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, gpu_cc_sdm845_match_table);
@@ -592,6 +598,7 @@
 static const struct of_device_id gpu_cc_gfx_sdm845_match_table[] = {
 	{ .compatible = "qcom,gfxcc-sdm845" },
 	{ .compatible = "qcom,gfxcc-sdm845-v2" },
+	{ .compatible = "qcom,gfxcc-sdm670" },
 	{},
 };
 MODULE_DEVICE_TABLE(of, gpu_cc_gfx_sdm845_match_table);
@@ -605,23 +612,51 @@
 	gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 500000000;
 }
 
+static void gpu_cc_sdm845_fixup_sdm670(struct regmap *regmap)
+{
+	gpu_cc_sdm845_clocks[GPU_CC_PLL1] = &gpu_cc_pll1.clkr;
+	clk_fabia_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config);
+
+	gpu_cc_gmu_clk_src.freq_tbl = ftbl_gpu_cc_gmu_clk_src_sdm670;
+	gpu_cc_gmu_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 200000000;
+}
+
 static void gpu_cc_gfx_sdm845_fixup_sdm845v2(void)
 {
 	gpu_cc_gx_gfx3d_clk_src.freq_tbl =
 				ftbl_gpu_cc_gx_gfx3d_clk_src_sdm845_v2;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] = 180000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] =
-				257000000;
+		257000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] = 342000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] =
-				414000000;
+		414000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] =
-				520000000;
+		520000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] =
-				596000000;
+		596000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] = 675000000;
 	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] =
-				710000000;
+		710000000;
+}
+
+static void gpu_cc_gfx_sdm845_fixup_sdm670(void)
+{
+	gpu_cc_gx_gfx3d_clk_src.freq_tbl =
+				ftbl_gpu_cc_gx_gfx3d_clk_src_sdm670;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_MIN] = 180000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOWER] =
+		267000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW] = 355000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_LOW_L1] =
+		430000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL] =
+		565000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_NOMINAL_L1] =
+		650000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH] = 750000000;
+	gpu_cc_gx_gfx3d_clk_src.clkr.hw.init->rate_max[VDD_GX_HIGH_L1] =
+		780000000;
 }
 
 static int gpu_cc_gfx_sdm845_fixup(struct platform_device *pdev)
@@ -635,6 +670,8 @@
 
 	if (!strcmp(compat, "qcom,gfxcc-sdm845-v2"))
 		gpu_cc_gfx_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,gfxcc-sdm670"))
+		gpu_cc_gfx_sdm845_fixup_sdm670();
 
 	return 0;
 }
@@ -651,6 +688,8 @@
 
 	if (!strcmp(compat, "qcom,gpucc-sdm845-v2"))
 		gpu_cc_sdm845_fixup_sdm845v2(regmap);
+	else if (!strcmp(compat, "qcom,gpucc-sdm670"))
+		gpu_cc_sdm845_fixup_sdm670(regmap);
 
 	return 0;
 }
diff --git a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
index eb6c658..dd02a8f 100644
--- a/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
+++ b/drivers/clk/qcom/mdss/mdss-dsi-pll-10nm.c
@@ -289,6 +289,14 @@
 
 	(void)mdss_pll_resource_enable(rsc, false);
 
+	/*
+	 * cache the current parent index for cases where parent
+	 * is not changing but rate is changing. In that case
+	 * clock framework won't call parent_set and hence dsiclk_sel
+	 * bit won't be programmed. e.g. dfps update use case.
+	 */
+	rsc->cached_cfg1 = val;
+
 	return rc;
 }
 
@@ -633,6 +641,12 @@
 	if (rsc->slave)
 		dsi_pll_enable_pll_bias(rsc->slave);
 
+	phy_reg_update_bits_sub(rsc, PHY_CMN_CLK_CFG1, 0x03, rsc->cached_cfg1);
+	if (rsc->slave)
+		phy_reg_update_bits_sub(rsc->slave, PHY_CMN_CLK_CFG1,
+				0x03, rsc->cached_cfg1);
+	wmb(); /* ensure dsiclk_sel is always programmed before pll start */
+
 	/* Start PLL */
 	MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_PLL_CNTRL, 0x01);
 
@@ -665,7 +679,6 @@
 
 static void dsi_pll_disable_sub(struct mdss_pll_resources *rsc)
 {
-	dsi_pll_disable_global_clk(rsc);
 	MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_RBUF_CTRL, 0);
 	dsi_pll_disable_pll_bias(rsc);
 }
@@ -684,11 +697,20 @@
 
 	pr_debug("stop PLL (%d)\n", rsc->index);
 
+	/*
+	 * To avoid any stray glitches while
+	 * abruptly powering down the PLL
+	 * make sure to gate the clock using
+	 * the clock enable bit before powering
+	 * down the PLL
+	 */
+	dsi_pll_disable_global_clk(rsc);
 	MDSS_PLL_REG_W(rsc->phy_base, PHY_CMN_PLL_CNTRL, 0);
 	dsi_pll_disable_sub(rsc);
-	if (rsc->slave)
+	if (rsc->slave) {
+		dsi_pll_disable_global_clk(rsc->slave);
 		dsi_pll_disable_sub(rsc->slave);
-
+	}
 	/* flush, ensure all register writes are done*/
 	wmb();
 	rsc->pll_on = false;
@@ -720,7 +742,6 @@
 		return;
 	}
 	pll->cached_cfg0 = MDSS_PLL_REG_R(pll->phy_base, PHY_CMN_CLK_CFG0);
-	pll->cached_cfg1 = MDSS_PLL_REG_R(pll->phy_base, PHY_CMN_CLK_CFG1);
 	pll->cached_outdiv = MDSS_PLL_REG_R(pll->pll_base, PLL_PLL_OUTDIV_RATE);
 	pr_debug("cfg0=%d,cfg1=%d, outdiv=%d\n", pll->cached_cfg0,
 			pll->cached_cfg1, pll->cached_outdiv);
@@ -741,6 +762,10 @@
 		return -EINVAL;
 	}
 
+	/* Skip vco recalculation for continuous splash use case */
+	if (pll->handoff_resources == true)
+		return 0;
+
 	rc = mdss_pll_resource_enable(pll, true);
 	if (rc) {
 		pr_err("failed to enable pll (%d) resource, rc=%d\n",
@@ -762,8 +787,6 @@
 			pll->cached_cfg1);
 		MDSS_PLL_REG_W(pll->phy_base, PHY_CMN_CLK_CFG0,
 					pll->cached_cfg0);
-		MDSS_PLL_REG_W(pll->phy_base, PHY_CMN_CLK_CFG1,
-					pll->cached_cfg1);
 		MDSS_PLL_REG_W(pll->pll_base, PLL_PLL_OUTDIV_RATE,
 					pll->cached_outdiv);
 	}
@@ -795,6 +818,19 @@
 	if (!vco->priv)
 		pr_err("vco priv is null\n");
 
+	/*
+	 * Calculate the vco rate from HW registers only for handoff cases.
+	 * For other cases where a vco_10nm_set_rate() has already been
+	 * called, just return the rate that was set earlier. This is due
+	 * to the fact that recalculating VCO rate requires us to read the
+	 * correct value of the pll_out_div divider clock, which is only set
+	 * afterwards.
+	 */
+	if (pll->vco_current_rate != 0) {
+		pr_debug("returning vco rate = %lld\n", pll->vco_current_rate);
+		return pll->vco_current_rate;
+	}
+
 	rc = mdss_pll_resource_enable(pll, true);
 	if (rc) {
 		pr_err("failed to enable pll(%d) resource, rc=%d\n",
@@ -802,6 +838,9 @@
 		return 0;
 	}
 
+	if (!dsi_pll_10nm_lock_status(pll))
+		pll->handoff_resources = true;
+
 	dec = MDSS_PLL_REG_R(pll->pll_base, PLL_DECIMAL_DIV_START_1);
 	dec &= 0xFF;
 
diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c
index ba4e591..79cc5cf 100644
--- a/drivers/clk/qcom/videocc-sdm845.c
+++ b/drivers/clk/qcom/videocc-sdm845.c
@@ -114,6 +114,17 @@
 	{ }
 };
 
+static const struct freq_tbl ftbl_video_cc_venus_clk_src_sdm670[] = {
+	F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0),
+	F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
+	F(330000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
+	F(364700000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
+	F(404000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
+	F(444000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
+	F(533000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
+	{ }
+};
+
 static struct clk_rcg2 video_cc_venus_clk_src = {
 	.cmd_rcgr = 0x7f0,
 	.mnd_width = 0,
@@ -328,6 +339,7 @@
 static const struct of_device_id video_cc_sdm845_match_table[] = {
 	{ .compatible = "qcom,video_cc-sdm845" },
 	{ .compatible = "qcom,video_cc-sdm845-v2" },
+	{ .compatible = "qcom,video_cc-sdm670" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, video_cc_sdm845_match_table);
@@ -340,6 +352,14 @@
 		404000000;
 }
 
+static void video_cc_sdm845_fixup_sdm670(void)
+{
+	video_cc_venus_clk_src.freq_tbl = ftbl_video_cc_venus_clk_src_sdm670;
+	video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW] = 330000000;
+	video_cc_venus_clk_src.clkr.hw.init->rate_max[VDD_CX_LOW_L1] =
+		404000000;
+}
+
 static int video_cc_sdm845_fixup(struct platform_device *pdev)
 {
 	const char *compat = NULL;
@@ -351,6 +371,8 @@
 
 	if (!strcmp(compat, "qcom,video_cc-sdm845-v2"))
 		video_cc_sdm845_fixup_sdm845v2();
+	else if (!strcmp(compat, "qcom,video_cc-sdm670"))
+		video_cc_sdm845_fixup_sdm670();
 
 	return 0;
 }
diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c
index 8c8b495..cdc092a 100644
--- a/drivers/clk/samsung/clk-exynos5420.c
+++ b/drivers/clk/samsung/clk-exynos5420.c
@@ -586,7 +586,7 @@
 	GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam",
 				GATE_BUS_TOP, 24, 0, 0),
 	GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler",
-				GATE_BUS_TOP, 27, 0, 0),
+				GATE_BUS_TOP, 27, CLK_IS_CRITICAL, 0),
 };
 
 static const struct samsung_mux_clock exynos5420_mux_clks[] __initconst = {
@@ -956,20 +956,20 @@
 	GATE(CLK_SMMU_G2D, "smmu_g2d", "aclk333_g2d", GATE_IP_G2D, 7, 0, 0),
 
 	GATE(0, "aclk200_fsys", "mout_user_aclk200_fsys",
-			GATE_BUS_FSYS0, 9, CLK_IGNORE_UNUSED, 0),
+			GATE_BUS_FSYS0, 9, CLK_IS_CRITICAL, 0),
 	GATE(0, "aclk200_fsys2", "mout_user_aclk200_fsys2",
 			GATE_BUS_FSYS0, 10, CLK_IGNORE_UNUSED, 0),
 
 	GATE(0, "aclk333_g2d", "mout_user_aclk333_g2d",
 			GATE_BUS_TOP, 0, CLK_IGNORE_UNUSED, 0),
 	GATE(0, "aclk266_g2d", "mout_user_aclk266_g2d",
-			GATE_BUS_TOP, 1, CLK_IGNORE_UNUSED, 0),
+			GATE_BUS_TOP, 1, CLK_IS_CRITICAL, 0),
 	GATE(0, "aclk300_jpeg", "mout_user_aclk300_jpeg",
 			GATE_BUS_TOP, 4, CLK_IGNORE_UNUSED, 0),
 	GATE(0, "aclk333_432_isp0", "mout_user_aclk333_432_isp0",
 			GATE_BUS_TOP, 5, 0, 0),
 	GATE(0, "aclk300_gscl", "mout_user_aclk300_gscl",
-			GATE_BUS_TOP, 6, CLK_IGNORE_UNUSED, 0),
+			GATE_BUS_TOP, 6, CLK_IS_CRITICAL, 0),
 	GATE(0, "aclk333_432_gscl", "mout_user_aclk333_432_gscl",
 			GATE_BUS_TOP, 7, CLK_IGNORE_UNUSED, 0),
 	GATE(0, "aclk333_432_isp", "mout_user_aclk333_432_isp",
@@ -983,20 +983,20 @@
 	GATE(0, "aclk166", "mout_user_aclk166",
 			GATE_BUS_TOP, 14, CLK_IGNORE_UNUSED, 0),
 	GATE(CLK_ACLK333, "aclk333", "mout_user_aclk333",
-			GATE_BUS_TOP, 15, CLK_IGNORE_UNUSED, 0),
+			GATE_BUS_TOP, 15, CLK_IS_CRITICAL, 0),
 	GATE(0, "aclk400_isp", "mout_user_aclk400_isp",
 			GATE_BUS_TOP, 16, 0, 0),
 	GATE(0, "aclk400_mscl", "mout_user_aclk400_mscl",
 			GATE_BUS_TOP, 17, 0, 0),
 	GATE(0, "aclk200_disp1", "mout_user_aclk200_disp1",
-			GATE_BUS_TOP, 18, 0, 0),
+			GATE_BUS_TOP, 18, CLK_IS_CRITICAL, 0),
 	GATE(CLK_SCLK_MPHY_IXTAL24, "sclk_mphy_ixtal24", "mphy_refclk_ixtal24",
 			GATE_BUS_TOP, 28, 0, 0),
 	GATE(CLK_SCLK_HSIC_12M, "sclk_hsic_12m", "ff_hsic_12m",
 			GATE_BUS_TOP, 29, 0, 0),
 
 	GATE(0, "aclk300_disp1", "mout_user_aclk300_disp1",
-			SRC_MASK_TOP2, 24, 0, 0),
+			SRC_MASK_TOP2, 24, CLK_IS_CRITICAL, 0),
 
 	GATE(CLK_MAU_EPLL, "mau_epll", "mout_mau_epll_clk",
 			SRC_MASK_TOP7, 20, 0, 0),
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index ea16086..2fe0573 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -2559,8 +2559,10 @@
 	FRATE(0, "phyclk_mipidphy1_bitclkdiv8_phy", NULL, 0, 188000000),
 	FRATE(0, "phyclk_mipidphy1_rxclkesc0_phy", NULL, 0, 100000000),
 	/* PHY clocks from MIPI_DPHY0 */
-	FRATE(0, "phyclk_mipidphy0_bitclkdiv8_phy", NULL, 0, 188000000),
-	FRATE(0, "phyclk_mipidphy0_rxclkesc0_phy", NULL, 0, 100000000),
+	FRATE(CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8_PHY, "phyclk_mipidphy0_bitclkdiv8_phy",
+			NULL, 0, 188000000),
+	FRATE(CLK_PHYCLK_MIPIDPHY0_RXCLKESC0_PHY, "phyclk_mipidphy0_rxclkesc0_phy",
+			NULL, 0, 100000000),
 	/* PHY clocks from HDMI_PHY */
 	FRATE(CLK_PHYCLK_HDMIPHY_TMDS_CLKO_PHY, "phyclk_hdmiphy_tmds_clko_phy",
 			NULL, 0, 300000000),
diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
index 21c427d..a26c8a1 100644
--- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
+++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c
@@ -803,6 +803,13 @@
 	.num_resets	= ARRAY_SIZE(sun8i_h3_ccu_resets),
 };
 
+static struct ccu_mux_nb sun8i_h3_cpu_nb = {
+	.common		= &cpux_clk.common,
+	.cm		= &cpux_clk.mux,
+	.delay_us	= 1, /* > 8 clock cycles at 24 MHz */
+	.bypass_index	= 1, /* index of 24 MHz oscillator */
+};
+
 static void __init sun8i_h3_ccu_setup(struct device_node *node)
 {
 	void __iomem *reg;
@@ -821,6 +828,9 @@
 	writel(val | (3 << 16), reg + SUN8I_H3_PLL_AUDIO_REG);
 
 	sunxi_ccu_probe(node, reg, &sun8i_h3_ccu_desc);
+
+	ccu_mux_notifier_register(pll_cpux_clk.common.hw.clk,
+				  &sun8i_h3_cpu_nb);
 }
 CLK_OF_DECLARE(sun8i_h3_ccu, "allwinner,sun8i-h3-ccu",
 	       sun8i_h3_ccu_setup);
diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c
index 51d4bac..01d0594 100644
--- a/drivers/clk/sunxi-ng/ccu_common.c
+++ b/drivers/clk/sunxi-ng/ccu_common.c
@@ -70,6 +70,11 @@
 		goto err_clk_unreg;
 
 	reset = kzalloc(sizeof(*reset), GFP_KERNEL);
+	if (!reset) {
+		ret = -ENOMEM;
+		goto err_alloc_reset;
+	}
+
 	reset->rcdev.of_node = node;
 	reset->rcdev.ops = &ccu_reset_ops;
 	reset->rcdev.owner = THIS_MODULE;
@@ -85,6 +90,16 @@
 	return 0;
 
 err_of_clk_unreg:
+	kfree(reset);
+err_alloc_reset:
+	of_clk_del_provider(node);
 err_clk_unreg:
+	while (--i >= 0) {
+		struct clk_hw *hw = desc->hw_clks->hws[i];
+
+		if (!hw)
+			continue;
+		clk_hw_unregister(hw);
+	}
 	return ret;
 }
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index 9a7e37c..e1d7373 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -117,7 +117,8 @@
 	/* Turn off the clock (and clear the event) */
 	disable_timer(cs5535_event_clock);
 
-	if (clockevent_state_shutdown(&cs5535_clockevent))
+	if (clockevent_state_detached(&cs5535_clockevent) ||
+	    clockevent_state_shutdown(&cs5535_clockevent))
 		return IRQ_HANDLED;
 
 	/* Clear the counter */
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index e2023bd..fd5984f 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -244,7 +244,7 @@
 
 config ACPI_CPPC_CPUFREQ
 	tristate "CPUFreq driver based on the ACPI CPPC spec"
-	depends on ACPI
+	depends on ACPI_PROCESSOR
 	select ACPI_CPPC_LIB
 	default n
 	help
diff --git a/drivers/cpufreq/cpu-boost.c b/drivers/cpufreq/cpu-boost.c
index e67f12b..6a4008c 100644
--- a/drivers/cpufreq/cpu-boost.c
+++ b/drivers/cpufreq/cpu-boost.c
@@ -75,7 +75,7 @@
 	for (i = 0; i < ntokens; i += 2) {
 		if (sscanf(cp, "%u:%u", &cpu, &val) != 2)
 			return -EINVAL;
-		if (cpu > num_possible_cpus())
+		if (cpu >= num_possible_cpus())
 			return -EINVAL;
 
 		per_cpu(sync_info, cpu).input_boost_freq = val;
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 062d297..e8c7af52 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1245,8 +1245,6 @@
 	if (new_policy) {
 		/* related_cpus should at least include policy->cpus. */
 		cpumask_copy(policy->related_cpus, policy->cpus);
-		/* Clear mask of registered CPUs */
-		cpumask_clear(policy->real_cpus);
 	}
 
 	/*
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 80fa656..a59ae8e 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -609,6 +609,7 @@
 static int pid_param_set(void *data, u64 val)
 {
 	*(u32 *)data = val;
+	pid_params.sample_rate_ns = pid_params.sample_rate_ms * NSEC_PER_MSEC;
 	intel_pstate_reset_all_pid();
 	return 0;
 }
diff --git a/drivers/cpufreq/qcom-cpufreq.c b/drivers/cpufreq/qcom-cpufreq.c
index f968ffd9..d310380 100644
--- a/drivers/cpufreq/qcom-cpufreq.c
+++ b/drivers/cpufreq/qcom-cpufreq.c
@@ -42,6 +42,8 @@
 };
 
 static DEFINE_PER_CPU(struct cpufreq_suspend_t, suspend_data);
+static DEFINE_PER_CPU(int, cached_resolve_idx);
+static DEFINE_PER_CPU(unsigned int, cached_resolve_freq);
 
 static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq,
 			unsigned int index)
@@ -74,6 +76,7 @@
 	int ret = 0;
 	int index;
 	struct cpufreq_frequency_table *table;
+	int first_cpu = cpumask_first(policy->related_cpus);
 
 	mutex_lock(&per_cpu(suspend_data, policy->cpu).suspend_mutex);
 
@@ -88,13 +91,11 @@
 	}
 
 	table = policy->freq_table;
-	if (!table) {
-		pr_err("cpufreq: Failed to get frequency table for CPU%u\n",
-		       policy->cpu);
-		ret = -ENODEV;
-		goto done;
-	}
-	index = cpufreq_frequency_table_target(policy, target_freq, relation);
+	if (per_cpu(cached_resolve_freq, first_cpu) == target_freq)
+		index = per_cpu(cached_resolve_idx, first_cpu);
+	else
+		index = cpufreq_frequency_table_target(policy, target_freq,
+						       relation);
 
 	pr_debug("CPU[%d] target %d relation %d (%d-%d) selected %d\n",
 		policy->cpu, target_freq, relation,
@@ -107,6 +108,23 @@
 	return ret;
 }
 
+static unsigned int msm_cpufreq_resolve_freq(struct cpufreq_policy *policy,
+					     unsigned int target_freq)
+{
+	int index;
+	int first_cpu = cpumask_first(policy->related_cpus);
+	unsigned int freq;
+
+	index = cpufreq_frequency_table_target(policy, target_freq,
+					       CPUFREQ_RELATION_L);
+	freq = policy->freq_table[index].frequency;
+
+	per_cpu(cached_resolve_idx, first_cpu) = index;
+	per_cpu(cached_resolve_freq, first_cpu) = freq;
+
+	return freq;
+}
+
 static int msm_cpufreq_verify(struct cpufreq_policy *policy)
 {
 	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
@@ -296,6 +314,7 @@
 	.init		= msm_cpufreq_init,
 	.verify		= msm_cpufreq_verify,
 	.target		= msm_cpufreq_target,
+	.resolve_freq	= msm_cpufreq_resolve_freq,
 	.get		= msm_cpufreq_get_freq,
 	.name		= "msm",
 	.attr		= msm_freq_attr,
@@ -462,6 +481,7 @@
 	for_each_possible_cpu(cpu) {
 		mutex_init(&(per_cpu(suspend_data, cpu).suspend_mutex));
 		per_cpu(suspend_data, cpu).device_suspended = 0;
+		per_cpu(cached_resolve_freq, cpu) = UINT_MAX;
 	}
 
 	rc = platform_driver_register(&msm_cpufreq_plat_driver);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 78ab946..a3e1de0 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -613,16 +613,36 @@
 
 #ifdef CONFIG_SMP
 
+static void wake_up_idle_cpus(void *v)
+{
+	int cpu;
+	struct cpumask cpus;
+
+	preempt_disable();
+	if (v) {
+		cpumask_andnot(&cpus, v, cpu_isolated_mask);
+		cpumask_and(&cpus, &cpus, cpu_online_mask);
+	} else
+		cpumask_andnot(&cpus, cpu_online_mask, cpu_isolated_mask);
+
+	for_each_cpu(cpu, &cpus) {
+		if (cpu == smp_processor_id())
+			continue;
+		wake_up_if_idle(cpu);
+	}
+	preempt_enable();
+}
+
 /*
  * This function gets called when a part of the kernel has a new latency
- * requirement.  This means we need to get all processors out of their C-state,
- * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that
- * wakes them all right up.
+ * requirement.  This means we need to get only those processors out of their
+ * C-state for which qos requirement is changed, and then recalculate a new
+ * suitable C-state. Just do a cross-cpu IPI; that wakes them all right up.
  */
 static int cpuidle_latency_notify(struct notifier_block *b,
 		unsigned long l, void *v)
 {
-	wake_up_all_idle_cpus();
+	wake_up_idle_cpus(v);
 	return NOTIFY_OK;
 }
 
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index 0ab4c21..fec75b1 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -410,6 +410,9 @@
 {
 	struct lpm_level_avail *avail = cpu_level_available[cpu];
 
+	if (lpm_pdev && !index)
+		return 1;
+
 	if (!lpm_pdev || !avail)
 		return !from_idle;
 
@@ -710,6 +713,9 @@
 	if (ret)
 		goto failed_parse_params;
 
+	key = "qcom,use-prediction";
+	cpu->lpm_prediction = of_property_read_bool(node, key);
+
 	key = "parse_cpu";
 	ret = parse_cpu(node, cpu);
 	if (ret)
@@ -779,6 +785,7 @@
 	if (ret)
 		goto failed_parse_params;
 
+	INIT_LIST_HEAD(&c->list);
 	INIT_LIST_HEAD(&c->child);
 	INIT_LIST_HEAD(&c->cpu);
 	c->parent = parent;
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 5d2e918..fc1b4e4 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -84,12 +84,13 @@
 static bool lpm_prediction = true;
 module_param_named(lpm_prediction, lpm_prediction, bool, 0664);
 
-static uint32_t ref_stddev = 100;
+static uint32_t ref_stddev = 500;
 module_param_named(ref_stddev, ref_stddev, uint, 0664);
 
-static uint32_t tmr_add = 100;
+static uint32_t tmr_add = 1000;
 module_param_named(tmr_add, tmr_add, uint, 0664);
 
+static uint32_t ref_premature_cnt = 1;
 static uint32_t bias_hyst;
 module_param_named(bias_hyst, bias_hyst, uint, 0664);
 
@@ -436,8 +437,9 @@
 	int64_t thresh = LLONG_MAX;
 	struct lpm_history *history = &per_cpu(hist, dev->cpu);
 	uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
+	uint32_t *max_residency = get_per_cpu_max_residency(dev->cpu);
 
-	if (!lpm_prediction)
+	if (!lpm_prediction || !cpu->lpm_prediction)
 		return 0;
 
 	/*
@@ -522,9 +524,17 @@
 					total += history->resi[i];
 				}
 			}
-			if (failed > (MAXSAMPLES/2)) {
+			if (failed >= ref_premature_cnt) {
 				*idx_restrict = j;
 				do_div(total, failed);
+				for (i = 0; i < j; i++) {
+					if (total < max_residency[i]) {
+						*idx_restrict = i+1;
+						total = max_residency[i];
+						break;
+					}
+				}
+
 				*idx_restrict_time = total;
 				history->stime = ktime_to_us(ktime_get())
 						+ *idx_restrict_time;
@@ -615,7 +625,7 @@
 		struct power_params *pwr_params = &level->pwr;
 		bool allow;
 
-		allow = lpm_cpu_mode_allow(dev->cpu, i, true);
+		allow = i ? lpm_cpu_mode_allow(dev->cpu, i, true) : true;
 
 		if (!allow)
 			continue;
@@ -1006,9 +1016,13 @@
 		bool from_idle, int predicted)
 {
 	struct lpm_cluster_level *level = &cluster->levels[idx];
+	struct cpumask online_cpus;
+
+	cpumask_and(&online_cpus, &cluster->num_children_in_sync,
+					cpu_online_mask);
 
 	if (!cpumask_equal(&cluster->num_children_in_sync, &cluster->child_cpus)
-			|| is_IPI_pending(&cluster->num_children_in_sync)) {
+			|| is_IPI_pending(&online_cpus)) {
 		return -EPERM;
 	}
 
@@ -1342,7 +1356,8 @@
 	struct lpm_cpu *cpu = per_cpu(cpu_lpm, dev->cpu);
 	bool success = false;
 	const struct cpumask *cpumask = get_cpu_mask(dev->cpu);
-	int64_t start_time = ktime_to_ns(ktime_get()), end_time;
+	ktime_t start = ktime_get();
+	uint64_t start_time = ktime_to_ns(start), end_time;
 	struct power_params *pwr_params;
 
 	pwr_params = &cpu->levels[idx].pwr;
@@ -1367,9 +1382,7 @@
 	cluster_unprepare(cpu->parent, cpumask, idx, true, end_time);
 	cpu_unprepare(cpu, idx, true);
 	sched_set_cpu_cstate(smp_processor_id(), 0, 0, 0);
-	end_time = ktime_to_ns(ktime_get()) - start_time;
-	do_div(end_time, 1000);
-	dev->last_residency = end_time;
+	dev->last_residency = ktime_us_delta(ktime_get(), start);
 	update_history(dev, idx);
 	trace_cpu_idle_exit(idx, success);
 	local_irq_enable();
@@ -1460,7 +1473,8 @@
 			struct lpm_cpu_level *cpu_level = &lpm_cpu->levels[i];
 
 			snprintf(st->name, CPUIDLE_NAME_LEN, "C%u\n", i);
-			snprintf(st->desc, CPUIDLE_DESC_LEN, cpu_level->name);
+			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;
@@ -1606,7 +1620,7 @@
 	 * clocks that are enabled and preventing the system level
 	 * LPMs(XO and Vmin).
 	 */
-	clock_debug_print_enabled();
+	clock_debug_print_enabled(true);
 
 	psci_enter_sleep(lpm_cpu, idx, false);
 
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index a458475..71416f7 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -44,6 +44,7 @@
 	int nlevels;
 	unsigned int psci_mode_shift;
 	unsigned int psci_mode_mask;
+	bool lpm_prediction;
 	struct cpuidle_driver *drv;
 	struct lpm_cluster *parent;
 };
@@ -124,7 +125,7 @@
 uint32_t *get_per_cpu_min_residency(int cpu);
 extern struct lpm_cluster *lpm_root_node;
 
-#if CONFIG_SMP
+#if defined(CONFIG_SMP)
 extern DEFINE_PER_CPU(bool, pending_ipi);
 static inline bool is_IPI_pending(const struct cpumask *mask)
 {
diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
index 58a4244..3f26a41 100644
--- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c
+++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c
@@ -1,8 +1,9 @@
 /*
  * AMD Cryptographic Coprocessor (CCP) AES XTS crypto API support
  *
- * Copyright (C) 2013 Advanced Micro Devices, Inc.
+ * Copyright (C) 2013,2017 Advanced Micro Devices, Inc.
  *
+ * Author: Gary R Hook <gary.hook@amd.com>
  * Author: Tom Lendacky <thomas.lendacky@amd.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -164,6 +165,7 @@
 	memset(&rctx->cmd, 0, sizeof(rctx->cmd));
 	INIT_LIST_HEAD(&rctx->cmd.entry);
 	rctx->cmd.engine = CCP_ENGINE_XTS_AES_128;
+	rctx->cmd.u.xts.type = CCP_AES_TYPE_128;
 	rctx->cmd.u.xts.action = (encrypt) ? CCP_AES_ACTION_ENCRYPT
 					   : CCP_AES_ACTION_DECRYPT;
 	rctx->cmd.u.xts.unit_size = unit_size;
diff --git a/drivers/crypto/ccp/ccp-dev-v5.c b/drivers/crypto/ccp/ccp-dev-v5.c
index 2c0ce5f..71980c4 100644
--- a/drivers/crypto/ccp/ccp-dev-v5.c
+++ b/drivers/crypto/ccp/ccp-dev-v5.c
@@ -131,6 +131,7 @@
 #define	CCP_AES_MODE(p)		((p)->aes.mode)
 #define	CCP_AES_TYPE(p)		((p)->aes.type)
 #define	CCP_XTS_SIZE(p)		((p)->aes_xts.size)
+#define	CCP_XTS_TYPE(p)		((p)->aes_xts.type)
 #define	CCP_XTS_ENCRYPT(p)	((p)->aes_xts.encrypt)
 #define	CCP_SHA_TYPE(p)		((p)->sha.type)
 #define	CCP_RSA_SIZE(p)		((p)->rsa.size)
@@ -277,8 +278,7 @@
 	CCP_AES_ENCRYPT(&function) = op->u.aes.action;
 	CCP_AES_MODE(&function) = op->u.aes.mode;
 	CCP_AES_TYPE(&function) = op->u.aes.type;
-	if (op->u.aes.mode == CCP_AES_MODE_CFB)
-		CCP_AES_SIZE(&function) = 0x7f;
+	CCP_AES_SIZE(&function) = op->u.aes.size;
 
 	CCP5_CMD_FUNCTION(&desc) = function.raw;
 
@@ -318,6 +318,7 @@
 	CCP5_CMD_PROT(&desc) = 0;
 
 	function.raw = 0;
+	CCP_XTS_TYPE(&function) = op->u.xts.type;
 	CCP_XTS_ENCRYPT(&function) = op->u.xts.action;
 	CCP_XTS_SIZE(&function) = op->u.xts.unit_size;
 	CCP5_CMD_FUNCTION(&desc) = function.raw;
diff --git a/drivers/crypto/ccp/ccp-dev.h b/drivers/crypto/ccp/ccp-dev.h
index 8ac7ae1..347b771 100644
--- a/drivers/crypto/ccp/ccp-dev.h
+++ b/drivers/crypto/ccp/ccp-dev.h
@@ -187,6 +187,7 @@
 #define CCP_AES_CTX_SB_COUNT		1
 
 #define CCP_XTS_AES_KEY_SB_COUNT	1
+#define CCP5_XTS_AES_KEY_SB_COUNT	2
 #define CCP_XTS_AES_CTX_SB_COUNT	1
 
 #define CCP_SHA_SB_COUNT		1
@@ -469,9 +470,11 @@
 	enum ccp_aes_type type;
 	enum ccp_aes_mode mode;
 	enum ccp_aes_action action;
+	unsigned int size;
 };
 
 struct ccp_xts_aes_op {
+	enum ccp_aes_type type;
 	enum ccp_aes_action action;
 	enum ccp_xts_aes_unit_size unit_size;
 };
diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c
index 50fae44..7d4cd51 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -692,6 +692,14 @@
 			goto e_ctx;
 		}
 	}
+	switch (aes->mode) {
+	case CCP_AES_MODE_CFB: /* CFB128 only */
+	case CCP_AES_MODE_CTR:
+		op.u.aes.size = AES_BLOCK_SIZE * BITS_PER_BYTE - 1;
+		break;
+	default:
+		op.u.aes.size = 0;
+	}
 
 	/* Prepare the input and output data workareas. For in-place
 	 * operations we need to set the dma direction to BIDIRECTIONAL
@@ -779,6 +787,8 @@
 	struct ccp_op op;
 	unsigned int unit_size, dm_offset;
 	bool in_place = false;
+	unsigned int sb_count;
+	enum ccp_aes_type aestype;
 	int ret;
 
 	switch (xts->unit_size) {
@@ -802,7 +812,9 @@
 		return -EINVAL;
 	}
 
-	if (xts->key_len != AES_KEYSIZE_128)
+	if (xts->key_len == AES_KEYSIZE_128)
+		aestype = CCP_AES_TYPE_128;
+	else
 		return -EINVAL;
 
 	if (!xts->final && (xts->src_len & (AES_BLOCK_SIZE - 1)))
@@ -824,23 +836,44 @@
 	op.sb_key = cmd_q->sb_key;
 	op.sb_ctx = cmd_q->sb_ctx;
 	op.init = 1;
+	op.u.xts.type = aestype;
 	op.u.xts.action = xts->action;
 	op.u.xts.unit_size = xts->unit_size;
 
-	/* All supported key sizes fit in a single (32-byte) SB entry
-	 * and must be in little endian format. Use the 256-bit byte
-	 * swap passthru option to convert from big endian to little
-	 * endian.
+	/* A version 3 device only supports 128-bit keys, which fits into a
+	 * single SB entry. A version 5 device uses a 512-bit vector, so two
+	 * SB entries.
 	 */
+	if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0))
+		sb_count = CCP_XTS_AES_KEY_SB_COUNT;
+	else
+		sb_count = CCP5_XTS_AES_KEY_SB_COUNT;
 	ret = ccp_init_dm_workarea(&key, cmd_q,
-				   CCP_XTS_AES_KEY_SB_COUNT * CCP_SB_BYTES,
+				   sb_count * CCP_SB_BYTES,
 				   DMA_TO_DEVICE);
 	if (ret)
 		return ret;
 
-	dm_offset = CCP_SB_BYTES - AES_KEYSIZE_128;
-	ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
-	ccp_set_dm_area(&key, 0, xts->key, dm_offset, xts->key_len);
+	if (cmd_q->ccp->vdata->version == CCP_VERSION(3, 0)) {
+		/* All supported key sizes must be in little endian format.
+		 * Use the 256-bit byte swap passthru option to convert from
+		 * big endian to little endian.
+		 */
+		dm_offset = CCP_SB_BYTES - AES_KEYSIZE_128;
+		ccp_set_dm_area(&key, dm_offset, xts->key, 0, xts->key_len);
+		ccp_set_dm_area(&key, 0, xts->key, xts->key_len, xts->key_len);
+	} else {
+		/* Version 5 CCPs use a 512-bit space for the key: each portion
+		 * occupies 256 bits, or one entire slot, and is zero-padded.
+		 */
+		unsigned int pad;
+
+		dm_offset = CCP_SB_BYTES;
+		pad = dm_offset - xts->key_len;
+		ccp_set_dm_area(&key, pad, xts->key, 0, xts->key_len);
+		ccp_set_dm_area(&key, dm_offset + pad, xts->key, xts->key_len,
+				xts->key_len);
+	}
 	ret = ccp_copy_to_sb(cmd_q, &key, op.jobid, op.sb_key,
 			     CCP_PASSTHRU_BYTESWAP_256BIT);
 	if (ret) {
diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c
index 7868765..b54af97 100644
--- a/drivers/crypto/ixp4xx_crypto.c
+++ b/drivers/crypto/ixp4xx_crypto.c
@@ -1074,7 +1074,7 @@
 		req_ctx->hmac_virt = dma_pool_alloc(buffer_pool, flags,
 				&crypt->icv_rev_aes);
 		if (unlikely(!req_ctx->hmac_virt))
-			goto free_buf_src;
+			goto free_buf_dst;
 		if (!encrypt) {
 			scatterwalk_map_and_copy(req_ctx->hmac_virt,
 				req->src, cryptlen, authsize, 0);
@@ -1089,10 +1089,10 @@
 	BUG_ON(qmgr_stat_overflow(SEND_QID));
 	return -EINPROGRESS;
 
-free_buf_src:
-	free_buf_chain(dev, req_ctx->src, crypt->src_buf);
 free_buf_dst:
 	free_buf_chain(dev, req_ctx->dst, crypt->dst_buf);
+free_buf_src:
+	free_buf_chain(dev, req_ctx->src, crypt->src_buf);
 	crypt->ctl_flags = CTL_FLAG_UNUSED;
 	return -ENOMEM;
 }
diff --git a/drivers/crypto/msm/ice.c b/drivers/crypto/msm/ice.c
index 6ed82ef..182097c 100644
--- a/drivers/crypto/msm/ice.c
+++ b/drivers/crypto/msm/ice.c
@@ -25,26 +25,8 @@
 #include <soc/qcom/scm.h>
 #include <soc/qcom/qseecomi.h>
 #include "iceregs.h"
-
-#ifdef CONFIG_PFK
 #include <linux/pfk.h>
-#else
-#include <linux/bio.h>
-static inline int pfk_load_key_start(const struct bio *bio,
-	struct ice_crypto_setting *ice_setting, bool *is_pfe, bool async)
-{
-	return 0;
-}
 
-static inline int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
-{
-	return 0;
-}
-
-static inline void pfk_clear_on_reset(void)
-{
-}
-#endif
 
 #define TZ_SYSCALL_CREATE_SMC_ID(o, s, f) \
 	((uint32_t)((((o & 0x3f) << 24) | (s & 0xff) << 8) | (f & 0xff)))
@@ -144,6 +126,9 @@
 		return -EPERM;
 	}
 
+	if (!setting)
+		return -EINVAL;
+
 	if ((short)(crypto_data->key_index) >= 0) {
 
 		memcpy(&setting->crypto_data, crypto_data,
@@ -888,7 +873,7 @@
 static int qcom_ice_init_clocks(struct ice_device *ice)
 {
 	int ret = -EINVAL;
-	struct ice_clk_info *clki;
+	struct ice_clk_info *clki = NULL;
 	struct device *dev = ice->pdev;
 	struct list_head *head = &ice->clk_list_head;
 
@@ -932,7 +917,7 @@
 static int qcom_ice_enable_clocks(struct ice_device *ice, bool enable)
 {
 	int ret = 0;
-	struct ice_clk_info *clki;
+	struct ice_clk_info *clki = NULL;
 	struct device *dev = ice->pdev;
 	struct list_head *head = &ice->clk_list_head;
 
@@ -1451,7 +1436,7 @@
 	int ret = 0;
 	bool is_pfe = false;
 
-	if (!pdev || !req || !setting) {
+	if (!pdev || !req) {
 		pr_err("%s: Invalid params passed\n", __func__);
 		return -EINVAL;
 	}
@@ -1470,6 +1455,7 @@
 		/* It is not an error to have a request with no  bio */
 		return 0;
 	}
+    //pr_err("%s bio is %pK\n", __func__, req->bio);
 
 	ret = pfk_load_key_start(req->bio, &pfk_crypto_data, &is_pfe, async);
 	if (is_pfe) {
@@ -1608,12 +1594,14 @@
 		if (ice_dev->pdev->of_node == node) {
 			pr_info("%s: found ice device %pK\n", __func__,
 			ice_dev);
+			ice_pdev = to_platform_device(ice_dev->pdev);
 			break;
 		}
 	}
 
-	ice_pdev = to_platform_device(ice_dev->pdev);
-	pr_info("%s: matching platform device %pK\n", __func__, ice_pdev);
+	if (ice_pdev)
+		pr_info("%s: matching platform device %pK\n", __func__,
+			ice_pdev);
 out:
 	return ice_pdev;
 }
@@ -1631,12 +1619,12 @@
 
 	list_for_each_entry(ice_dev, &ice_devices, list) {
 		if (!strcmp(ice_dev->ice_instance_type, storage_type)) {
-			pr_info("%s: found ice device %p\n", __func__, ice_dev);
-			break;
+			pr_debug("%s: ice device %pK\n", __func__, ice_dev);
+			return ice_dev;
 		}
 	}
 out:
-	return ice_dev;
+	return NULL;
 }
 
 static int enable_ice_setup(struct ice_device *ice_dev)
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index 35d7542..4426bc7 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -162,7 +162,7 @@
 	bool cadence_flag;
 	uint8_t *dummyreq_in_buf;
 	struct dma_iommu_mapping *smmu_mapping;
-	bool bypass_s1_smmu;
+	bool enable_s1_smmu;
 };
 
 static void print_notify_debug(struct sps_event_notify *notify);
@@ -5716,8 +5716,8 @@
 		pce_dev->ce_opp_freq_hz = CE_CLK_100MHZ;
 	}
 
-	if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-bypass"))
-		pce_dev->bypass_s1_smmu = true;
+	if (of_property_read_bool((&pdev->dev)->of_node, "qcom,smmu-s1-enable"))
+		pce_dev->enable_s1_smmu = true;
 
 	pce_dev->ce_bam_info.dest_pipe_index	=
 			2 * pce_dev->ce_bam_info.pipe_pair_index;
@@ -5963,7 +5963,7 @@
 static int qce_smmu_init(struct qce_device *pce_dev)
 {
 	struct dma_iommu_mapping *mapping;
-	int s1_bypass = 1;
+	int attr = 1;
 	int ret = 0;
 
 	mapping = arm_iommu_create_mapping(&platform_bus_type,
@@ -5975,9 +5975,16 @@
 	}
 
 	ret = iommu_domain_set_attr(mapping->domain,
-				DOMAIN_ATTR_S1_BYPASS, &s1_bypass);
+				DOMAIN_ATTR_ATOMIC, &attr);
 	if (ret < 0) {
-		pr_err("Set s1_bypass attribute failed, err = %d\n", ret);
+		pr_err("Set ATOMIC attr failed, err = %d\n", ret);
+		goto ext_fail_set_attr;
+	}
+
+	ret = iommu_domain_set_attr(mapping->domain,
+				DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR, &attr);
+	if (ret < 0) {
+		pr_err("Set UPSTREAM_IOVA_ALLOCATOR failed, err = %d\n", ret);
 		goto ext_fail_set_attr;
 	}
 
@@ -6020,6 +6027,13 @@
 		goto err_pce_dev;
 	}
 
+	if (pce_dev->enable_s1_smmu) {
+		if (qce_smmu_init(pce_dev)) {
+			*rc = -EIO;
+			goto err_pce_dev;
+		}
+	}
+
 	for (i = 0; i < MAX_QCE_ALLOC_BAM_REQ; i++)
 		atomic_set(&pce_dev->ce_request_info[i].in_use, false);
 	pce_dev->ce_request_index = 0;
@@ -6051,13 +6065,6 @@
 	if (*rc)
 		goto err_enable_clk;
 
-	if (pce_dev->bypass_s1_smmu) {
-		if (qce_smmu_init(pce_dev)) {
-			*rc = -EIO;
-			goto err_smmu;
-		}
-	}
-
 	if (_probe_ce_engine(pce_dev)) {
 		*rc = -ENXIO;
 		goto err;
@@ -6084,9 +6091,6 @@
 	mutex_unlock(&qce_iomap_mutex);
 	return pce_dev;
 err:
-	if (pce_dev->bypass_s1_smmu)
-		qce_iommu_release_iomapping(pce_dev);
-err_smmu:
 	qce_disable_clk(pce_dev);
 
 err_enable_clk:
@@ -6099,6 +6103,9 @@
 		dma_free_coherent(pce_dev->pdev, pce_dev->memsize,
 			pce_dev->coh_vmem, pce_dev->coh_pmem);
 err_iobase:
+	if (pce_dev->enable_s1_smmu)
+		qce_iommu_release_iomapping(pce_dev);
+
 	if (pce_dev->iobase)
 		iounmap(pce_dev->iobase);
 err_pce_dev:
@@ -6128,7 +6135,7 @@
 	kfree(pce_dev->dummyreq_in_buf);
 	kfree(pce_dev->iovec_vmem);
 
-	if (pce_dev->bypass_s1_smmu)
+	if (pce_dev->enable_s1_smmu)
 		qce_iommu_release_iomapping(pce_dev);
 
 	qce_disable_clk(pce_dev);
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 2cf303e..958fb91 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -340,8 +340,6 @@
 	if (authdata) {
 		handle->sha_ctxt.auth_data[0] = auth32[0];
 		handle->sha_ctxt.auth_data[1] = auth32[1];
-		handle->sha_ctxt.auth_data[2] = auth32[2];
-		handle->sha_ctxt.auth_data[3] = auth32[3];
 	}
 
 	tasklet_schedule(&pdev->done_tasklet);
@@ -905,6 +903,7 @@
 	memset(&handle->sha_ctxt.trailing_buf[0], 0, 64);
 
 	kzfree(k_buf_src);
+	qcedev_areq->sha_req.sreq.src = NULL;
 	return err;
 }
 
@@ -1068,6 +1067,7 @@
 	handle->sha_ctxt.first_blk = 0;
 
 	kzfree(k_src);
+	qcedev_areq->sha_req.sreq.src = NULL;
 	return err;
 }
 
@@ -1222,8 +1222,10 @@
 			if (err == 0 && copy_to_user(
 				(void __user *)creq->vbuf.dst[dst_i].vaddr,
 					(k_align_dst + byteoffset),
-					creq->vbuf.dst[dst_i].len))
-				return -EFAULT;
+					creq->vbuf.dst[dst_i].len)) {
+				err = -EFAULT;
+				goto exit;
+			}
 
 			k_align_dst += creq->vbuf.dst[dst_i].len +
 						byteoffset;
@@ -1233,8 +1235,10 @@
 			if (err == 0 && copy_to_user(
 				(void __user *)creq->vbuf.dst[dst_i].vaddr,
 					(k_align_dst + byteoffset),
-					creq->data_len))
-				return -EFAULT;
+				creq->data_len)) {
+				err = -EFAULT;
+				goto exit;
+			}
 
 			k_align_dst += creq->data_len;
 			creq->vbuf.dst[dst_i].len -= creq->data_len;
@@ -1243,7 +1247,9 @@
 		}
 	}
 	*di = dst_i;
-
+exit:
+	areq->cipher_req.creq.src = NULL;
+	areq->cipher_req.creq.dst = NULL;
 	return err;
 };
 
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index b3269a6..fd42575 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -3964,7 +3964,7 @@
 	struct qcrypto_sha_ctx *sha_ctx = crypto_tfm_ctx(&tfm->base);
 	uint8_t	*in_buf;
 	int ret = 0;
-	struct scatterlist sg;
+	struct scatterlist sg = {0};
 	struct ahash_request *ahash_req;
 	struct completion ahash_req_complete;
 
diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c
index 571de2f..e2d323f 100644
--- a/drivers/crypto/talitos.c
+++ b/drivers/crypto/talitos.c
@@ -1756,9 +1756,9 @@
 		req_ctx->swinit = 0;
 	} else {
 		desc->ptr[1] = zero_entry;
-		/* Indicate next op is not the first. */
-		req_ctx->first = 0;
 	}
+	/* Indicate next op is not the first. */
+	req_ctx->first = 0;
 
 	/* HMAC key */
 	if (ctx->keylen)
@@ -1769,7 +1769,7 @@
 
 	sg_count = edesc->src_nents ?: 1;
 	if (is_sec1 && sg_count > 1)
-		sg_copy_to_buffer(areq->src, sg_count, edesc->buf, length);
+		sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length);
 	else
 		sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count,
 				      DMA_TO_DEVICE);
@@ -3057,7 +3057,8 @@
 		t_alg->algt.alg.hash.final = ahash_final;
 		t_alg->algt.alg.hash.finup = ahash_finup;
 		t_alg->algt.alg.hash.digest = ahash_digest;
-		t_alg->algt.alg.hash.setkey = ahash_setkey;
+		if (!strncmp(alg->cra_name, "hmac", 4))
+			t_alg->algt.alg.hash.setkey = ahash_setkey;
 		t_alg->algt.alg.hash.import = ahash_import;
 		t_alg->algt.alg.hash.export = ahash_export;
 
diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c
index 38ed10d..7cf6d31 100644
--- a/drivers/crypto/vmx/aes_ctr.c
+++ b/drivers/crypto/vmx/aes_ctr.c
@@ -80,11 +80,13 @@
 	int ret;
 	struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm);
 
+	preempt_disable();
 	pagefault_disable();
 	enable_kernel_vsx();
 	ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key);
 	disable_kernel_vsx();
 	pagefault_enable();
+	preempt_enable();
 
 	ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen);
 	return ret;
@@ -99,11 +101,13 @@
 	u8 *dst = walk->dst.virt.addr;
 	unsigned int nbytes = walk->nbytes;
 
+	preempt_disable();
 	pagefault_disable();
 	enable_kernel_vsx();
 	aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key);
 	disable_kernel_vsx();
 	pagefault_enable();
+	preempt_enable();
 
 	crypto_xor(keystream, src, nbytes);
 	memcpy(dst, keystream, nbytes);
@@ -132,6 +136,7 @@
 		blkcipher_walk_init(&walk, dst, src, nbytes);
 		ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE);
 		while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
+			preempt_disable();
 			pagefault_disable();
 			enable_kernel_vsx();
 			aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr,
@@ -143,6 +148,7 @@
 						    walk.iv);
 			disable_kernel_vsx();
 			pagefault_enable();
+			preempt_enable();
 
 			/* We need to update IV mostly for last bytes/round */
 			inc = (nbytes & AES_BLOCK_MASK) / AES_BLOCK_SIZE;
diff --git a/drivers/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index 5802c21..1dca479 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -31,11 +31,13 @@
 #include "governor.h"
 #include "governor_memlat.h"
 #include <linux/perf_event.h>
+#include <linux/of_device.h>
 
 enum ev_index {
 	INST_IDX,
 	CM_IDX,
 	CYC_IDX,
+	STALL_CYC_IDX,
 	NUM_EVENTS
 };
 #define INST_EV		0x08
@@ -55,14 +57,17 @@
 struct cpu_grp_info {
 	cpumask_t cpus;
 	cpumask_t inited_cpus;
-	unsigned long cache_miss_event;
-	unsigned long inst_event;
+	unsigned int event_ids[NUM_EVENTS];
 	struct cpu_pmu_stats *cpustats;
 	struct memlat_hwmon hw;
 	struct notifier_block arm_memlat_cpu_notif;
 	struct list_head mon_list;
 };
 
+struct memlat_mon_spec {
+	bool is_compute;
+};
+
 #define to_cpustats(cpu_grp, cpu) \
 	(&cpu_grp->cpustats[cpu - cpumask_first(&cpu_grp->cpus)])
 #define to_devstats(cpu_grp, cpu) \
@@ -96,14 +101,12 @@
 	unsigned long ev_count;
 	u64 total, enabled, running;
 
+	if (!event->pevent)
+		return 0;
+
 	total = perf_event_read_value(event->pevent, &enabled, &running);
-	if (total >= event->prev_count)
-		ev_count = total - event->prev_count;
-	else
-		ev_count = (MAX_COUNT_LIM - event->prev_count) + total;
-
+	ev_count = total - event->prev_count;
 	event->prev_count = total;
-
 	return ev_count;
 }
 
@@ -111,12 +114,19 @@
 {
 	struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
 	struct dev_stats *devstats = to_devstats(cpu_grp, cpu);
-	unsigned long cyc_cnt;
+	unsigned long cyc_cnt, stall_cnt;
 
 	devstats->inst_count = read_event(&cpustats->events[INST_IDX]);
 	devstats->mem_count = read_event(&cpustats->events[CM_IDX]);
 	cyc_cnt = read_event(&cpustats->events[CYC_IDX]);
 	devstats->freq = compute_freq(cpustats, cyc_cnt);
+	if (cpustats->events[STALL_CYC_IDX].pevent) {
+		stall_cnt = read_event(&cpustats->events[STALL_CYC_IDX]);
+		stall_cnt = min(stall_cnt, cyc_cnt);
+		devstats->stall_pct = mult_frac(100, stall_cnt, cyc_cnt);
+	} else {
+		devstats->stall_pct = 100;
+	}
 }
 
 static unsigned long get_cnt(struct memlat_hwmon *hw)
@@ -134,9 +144,12 @@
 {
 	int i;
 
-	for (i = 0; i < NUM_EVENTS; i++) {
+	for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
 		cpustats->events[i].prev_count = 0;
-		perf_event_release_kernel(cpustats->events[i].pevent);
+		if (cpustats->events[i].pevent) {
+			perf_event_release_kernel(cpustats->events[i].pevent);
+			cpustats->events[i].pevent = NULL;
+		}
 	}
 }
 
@@ -155,6 +168,7 @@
 		devstats->inst_count = 0;
 		devstats->mem_count = 0;
 		devstats->freq = 0;
+		devstats->stall_pct = 0;
 	}
 	mutex_lock(&list_lock);
 	if (!cpumask_equal(&cpu_grp->cpus, &cpu_grp->inited_cpus))
@@ -187,7 +201,8 @@
 {
 	struct perf_event *pevent;
 	struct perf_event_attr *attr;
-	int err;
+	int err, i;
+	unsigned int event_id;
 	struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
 
 	/* Allocate an attribute for event initialization */
@@ -195,26 +210,19 @@
 	if (!attr)
 		return -ENOMEM;
 
-	attr->config = cpu_grp->inst_event;
-	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
-	if (IS_ERR(pevent))
-		goto err_out;
-	cpustats->events[INST_IDX].pevent = pevent;
-	perf_event_enable(cpustats->events[INST_IDX].pevent);
+	for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
+		event_id = cpu_grp->event_ids[i];
+		if (!event_id)
+			continue;
 
-	attr->config = cpu_grp->cache_miss_event;
-	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
-	if (IS_ERR(pevent))
-		goto err_out;
-	cpustats->events[CM_IDX].pevent = pevent;
-	perf_event_enable(cpustats->events[CM_IDX].pevent);
-
-	attr->config = CYC_EV;
-	pevent = perf_event_create_kernel_counter(attr, cpu, NULL, NULL, NULL);
-	if (IS_ERR(pevent))
-		goto err_out;
-	cpustats->events[CYC_IDX].pevent = pevent;
-	perf_event_enable(cpustats->events[CYC_IDX].pevent);
+		attr->config = event_id;
+		pevent = perf_event_create_kernel_counter(attr, cpu, NULL,
+							  NULL, NULL);
+		if (IS_ERR(pevent))
+			goto err_out;
+		cpustats->events[i].pevent = pevent;
+		perf_event_enable(pevent);
+	}
 
 	kfree(attr);
 	return 0;
@@ -314,8 +322,9 @@
 	struct device *dev = &pdev->dev;
 	struct memlat_hwmon *hw;
 	struct cpu_grp_info *cpu_grp;
+	const struct memlat_mon_spec *spec;
 	int cpu, ret;
-	u32 cachemiss_ev, inst_ev;
+	u32 event_id;
 
 	cpu_grp = devm_kzalloc(dev, sizeof(*cpu_grp), GFP_KERNEL);
 	if (!cpu_grp)
@@ -346,22 +355,7 @@
 	if (!cpu_grp->cpustats)
 		return -ENOMEM;
 
-	ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
-			&cachemiss_ev);
-	if (ret) {
-		dev_dbg(dev, "Cache Miss event not specified. Using def:0x%x\n",
-				L2DM_EV);
-		cachemiss_ev = L2DM_EV;
-	}
-	cpu_grp->cache_miss_event = cachemiss_ev;
-
-	ret = of_property_read_u32(dev->of_node, "qcom,inst-ev", &inst_ev);
-	if (ret) {
-		dev_dbg(dev, "Inst event not specified. Using def:0x%x\n",
-				INST_EV);
-		inst_ev = INST_EV;
-	}
-	cpu_grp->inst_event = inst_ev;
+	cpu_grp->event_ids[CYC_IDX] = CYC_EV;
 
 	for_each_cpu(cpu, &cpu_grp->cpus)
 		to_devstats(cpu_grp, cpu)->id = cpu;
@@ -370,17 +364,54 @@
 	hw->stop_hwmon = &stop_hwmon;
 	hw->get_cnt = &get_cnt;
 
-	ret = register_memlat(dev, hw);
-	if (ret) {
-		pr_err("Mem Latency Gov registration failed\n");
+	spec = of_device_get_match_data(dev);
+	if (spec && spec->is_compute) {
+		ret = register_compute(dev, hw);
+		if (ret)
+			pr_err("Compute Gov registration failed\n");
+
 		return ret;
 	}
 
-	return 0;
+	ret = of_property_read_u32(dev->of_node, "qcom,cachemiss-ev",
+				   &event_id);
+	if (ret) {
+		dev_dbg(dev, "Cache Miss event not specified. Using def:0x%x\n",
+			L2DM_EV);
+		event_id = L2DM_EV;
+	}
+	cpu_grp->event_ids[CM_IDX] = event_id;
+
+	ret = of_property_read_u32(dev->of_node, "qcom,inst-ev", &event_id);
+	if (ret) {
+		dev_dbg(dev, "Inst event not specified. Using def:0x%x\n",
+			INST_EV);
+		event_id = INST_EV;
+	}
+	cpu_grp->event_ids[INST_IDX] = event_id;
+
+	ret = of_property_read_u32(dev->of_node, "qcom,stall-cycle-ev",
+				   &event_id);
+	if (ret)
+		dev_dbg(dev, "Stall cycle event not specified. Event ignored.\n");
+	else
+		cpu_grp->event_ids[STALL_CYC_IDX] = event_id;
+
+	ret = register_memlat(dev, hw);
+	if (ret)
+		pr_err("Mem Latency Gov registration failed\n");
+
+	return ret;
 }
 
+static const struct memlat_mon_spec spec[] = {
+	[0] = { false },
+	[1] = { true },
+};
+
 static const struct of_device_id memlat_match_table[] = {
-	{ .compatible = "qcom,arm-memlat-mon" },
+	{ .compatible = "qcom,arm-memlat-mon", .data = &spec[0] },
+	{ .compatible = "qcom,arm-cpu-mon", .data = &spec[1] },
 	{}
 };
 
diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c
index ffe60de..f9b758f 100644
--- a/drivers/devfreq/bimc-bwmon.c
+++ b/drivers/devfreq/bimc-bwmon.c
@@ -575,7 +575,9 @@
 		count = readl_relaxed(MON2_ZONE_MAX(m, zone)) + 1;
 		break;
 	case MON3:
-		count = readl_relaxed(MON3_ZONE_MAX(m, zone)) + 1;
+		count = readl_relaxed(MON3_ZONE_MAX(m, zone));
+		if (count)
+			count++;
 		break;
 	}
 
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index 8f582f6..b263696 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -598,7 +598,7 @@
 	err = device_register(&devfreq->dev);
 	if (err) {
 		mutex_unlock(&devfreq->lock);
-		goto err_out;
+		goto err_dev;
 	}
 
 	devfreq->trans_table =	devm_kzalloc(&devfreq->dev, sizeof(unsigned int) *
@@ -642,6 +642,9 @@
 	mutex_unlock(&devfreq_list_lock);
 
 	device_unregister(&devfreq->dev);
+err_dev:
+	if (devfreq)
+		kfree(devfreq);
 err_out:
 	return ERR_PTR(err);
 }
diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c
index 53c0f8a..3026bc2 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -48,9 +48,6 @@
 	unsigned int hyst_trigger_count;
 	unsigned int hyst_length;
 	unsigned int idle_mbps;
-	unsigned int low_power_ceil_mbps;
-	unsigned int low_power_io_percent;
-	unsigned int low_power_delay;
 	unsigned int mbps_zones[NUM_MBPS_ZONES];
 
 	unsigned long prev_ab;
@@ -65,7 +62,6 @@
 	unsigned long hyst_mbps;
 	unsigned long hyst_trig_win;
 	unsigned long hyst_en;
-	unsigned long above_low_power;
 	unsigned long prev_req;
 	unsigned int wake;
 	unsigned int down_cnt;
@@ -317,7 +313,7 @@
 	unsigned long meas_mbps_zone;
 	unsigned long hist_lo_tol, hyst_lo_tol;
 	struct bw_hwmon *hw = node->hw;
-	unsigned int new_bw, io_percent;
+	unsigned int new_bw, io_percent = node->io_percent;
 	ktime_t ts;
 	unsigned int ms = 0;
 
@@ -353,17 +349,6 @@
 			node->hist_mem--;
 	}
 
-	/* Keep track of whether we are in low power mode consistently. */
-	if (meas_mbps > node->low_power_ceil_mbps)
-		node->above_low_power = node->low_power_delay;
-	if (node->above_low_power)
-		node->above_low_power--;
-
-	if (node->above_low_power)
-		io_percent = node->io_percent;
-	else
-		io_percent = node->low_power_io_percent;
-
 	/*
 	 * The AB value that corresponds to the lowest mbps zone greater than
 	 * or equal to the "frequency" the current measurement will pick.
@@ -785,9 +770,6 @@
 gov_attr(hyst_trigger_count, 0U, 90U);
 gov_attr(hyst_length, 0U, 90U);
 gov_attr(idle_mbps, 0U, 2000U);
-gov_attr(low_power_ceil_mbps, 0U, 2500U);
-gov_attr(low_power_io_percent, 1U, 100U);
-gov_attr(low_power_delay, 1U, 60U);
 gov_list_attr(mbps_zones, NUM_MBPS_ZONES, 0U, UINT_MAX);
 
 static struct attribute *dev_attr[] = {
@@ -804,9 +786,6 @@
 	&dev_attr_hyst_trigger_count.attr,
 	&dev_attr_hyst_length.attr,
 	&dev_attr_idle_mbps.attr,
-	&dev_attr_low_power_ceil_mbps.attr,
-	&dev_attr_low_power_io_percent.attr,
-	&dev_attr_low_power_delay.attr,
 	&dev_attr_mbps_zones.attr,
 	&dev_attr_throttle_adj.attr,
 	NULL,
@@ -820,11 +799,13 @@
 static int devfreq_bw_hwmon_ev_handler(struct devfreq *df,
 					unsigned int event, void *data)
 {
-	int ret;
+	int ret = 0;
 	unsigned int sample_ms;
 	struct hwmon_node *node;
 	struct bw_hwmon *hw;
 
+	mutex_lock(&state_lock);
+
 	switch (event) {
 	case DEVFREQ_GOV_START:
 		sample_ms = df->profile->polling_ms;
@@ -834,7 +815,7 @@
 
 		ret = gov_start(df);
 		if (ret)
-			return ret;
+			goto out;
 
 		dev_dbg(df->dev.parent,
 			"Enabled dev BW HW monitor governor\n");
@@ -864,7 +845,7 @@
 		if (ret) {
 			dev_err(df->dev.parent,
 				"Unable to resume HW monitor (%d)\n", ret);
-			return ret;
+			goto out;
 		}
 		break;
 
@@ -874,7 +855,7 @@
 			dev_err(df->dev.parent,
 				"Unable to suspend BW HW mon governor (%d)\n",
 				ret);
-			return ret;
+			goto out;
 		}
 
 		dev_dbg(df->dev.parent, "Suspended BW HW mon governor\n");
@@ -886,14 +867,17 @@
 			dev_err(df->dev.parent,
 				"Unable to resume BW HW mon governor (%d)\n",
 				ret);
-			return ret;
+			goto out;
 		}
 
 		dev_dbg(df->dev.parent, "Resumed BW HW mon governor\n");
 		break;
 	}
 
-	return 0;
+out:
+	mutex_unlock(&state_lock);
+
+	return ret;
 }
 
 static struct devfreq_governor devfreq_gov_bw_hwmon = {
@@ -935,9 +919,6 @@
 	node->guard_band_mbps = 100;
 	node->decay_rate = 90;
 	node->io_percent = 16;
-	node->low_power_ceil_mbps = 0;
-	node->low_power_io_percent = 16;
-	node->low_power_delay = 60;
 	node->bw_step = 190;
 	node->sample_ms = 50;
 	node->up_scale = 0;
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index e1afa60..12a90d4 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -35,7 +35,9 @@
 
 struct memlat_node {
 	unsigned int ratio_ceil;
+	unsigned int stall_floor;
 	bool mon_started;
+	bool already_zero;
 	struct list_head list;
 	void *orig_data;
 	struct memlat_hwmon *hw;
@@ -46,7 +48,8 @@
 static LIST_HEAD(memlat_list);
 static DEFINE_MUTEX(list_lock);
 
-static int use_cnt;
+static int memlat_use_cnt;
+static int compute_use_cnt;
 static DEFINE_MUTEX(state_lock);
 
 #define show_attr(name) \
@@ -224,7 +227,7 @@
 static int devfreq_memlat_get_freq(struct devfreq *df,
 					unsigned long *freq)
 {
-	int i, lat_dev;
+	int i, lat_dev = 0;
 	struct memlat_node *node = df->data;
 	struct memlat_hwmon *hw = node->hw;
 	unsigned long max_freq = 0;
@@ -238,21 +241,28 @@
 		if (hw->core_stats[i].mem_count)
 			ratio /= hw->core_stats[i].mem_count;
 
+		if (!hw->core_stats[i].freq)
+			continue;
+
 		trace_memlat_dev_meas(dev_name(df->dev.parent),
 					hw->core_stats[i].id,
 					hw->core_stats[i].inst_count,
 					hw->core_stats[i].mem_count,
-					hw->core_stats[i].freq, ratio);
+					hw->core_stats[i].freq,
+					hw->core_stats[i].stall_pct, ratio);
 
-		if (ratio && ratio <= node->ratio_ceil
+		if (ratio <= node->ratio_ceil
+		    && hw->core_stats[i].stall_pct >= node->stall_floor
 		    && hw->core_stats[i].freq > max_freq) {
 			lat_dev = i;
 			max_freq = hw->core_stats[i].freq;
 		}
 	}
 
-	if (max_freq) {
+	if (max_freq)
 		max_freq = core_to_dev_freq(node, max_freq);
+
+	if (max_freq || !node->already_zero) {
 		trace_memlat_dev_update(dev_name(df->dev.parent),
 					hw->core_stats[lat_dev].id,
 					hw->core_stats[lat_dev].inst_count,
@@ -261,21 +271,35 @@
 					max_freq);
 	}
 
+	node->already_zero = !max_freq;
+
 	*freq = max_freq;
 	return 0;
 }
 
 gov_attr(ratio_ceil, 1U, 10000U);
+gov_attr(stall_floor, 0U, 100U);
 
-static struct attribute *dev_attr[] = {
+static struct attribute *memlat_dev_attr[] = {
 	&dev_attr_ratio_ceil.attr,
+	&dev_attr_stall_floor.attr,
 	&dev_attr_freq_map.attr,
 	NULL,
 };
 
-static struct attribute_group dev_attr_group = {
+static struct attribute *compute_dev_attr[] = {
+	&dev_attr_freq_map.attr,
+	NULL,
+};
+
+static struct attribute_group memlat_dev_attr_group = {
 	.name = "mem_latency",
-	.attrs = dev_attr,
+	.attrs = memlat_dev_attr,
+};
+
+static struct attribute_group compute_dev_attr_group = {
+	.name = "compute",
+	.attrs = compute_dev_attr,
 };
 
 #define MIN_MS	10U
@@ -324,6 +348,12 @@
 	.event_handler = devfreq_memlat_ev_handler,
 };
 
+static struct devfreq_governor devfreq_gov_compute = {
+	.name = "compute",
+	.get_target_freq = devfreq_memlat_get_freq,
+	.event_handler = devfreq_memlat_ev_handler,
+};
+
 #define NUM_COLS	2
 static struct core_dev_map *init_core_dev_map(struct device *dev,
 		char *prop_name)
@@ -366,20 +396,17 @@
 	return tbl;
 }
 
-int register_memlat(struct device *dev, struct memlat_hwmon *hw)
+static struct memlat_node *register_common(struct device *dev,
+					   struct memlat_hwmon *hw)
 {
-	int ret = 0;
 	struct memlat_node *node;
 
 	if (!hw->dev && !hw->of_node)
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 
 	node = devm_kzalloc(dev, sizeof(*node), GFP_KERNEL);
 	if (!node)
-		return -ENOMEM;
-
-	node->gov = &devfreq_gov_memlat;
-	node->attr_grp = &dev_attr_group;
+		return ERR_PTR(-ENOMEM);
 
 	node->ratio_ceil = 10;
 	node->hw = hw;
@@ -387,20 +414,68 @@
 	hw->freq_map = init_core_dev_map(dev, "qcom,core-dev-table");
 	if (!hw->freq_map) {
 		dev_err(dev, "Couldn't find the core-dev freq table!\n");
-		return -EINVAL;
+		return ERR_PTR(-EINVAL);
 	}
 
 	mutex_lock(&list_lock);
 	list_add_tail(&node->list, &memlat_list);
 	mutex_unlock(&list_lock);
 
+	return node;
+}
+
+int register_compute(struct device *dev, struct memlat_hwmon *hw)
+{
+	struct memlat_node *node;
+	int ret = 0;
+
+	node = register_common(dev, hw);
+	if (IS_ERR(node)) {
+		ret = PTR_ERR(node);
+		goto out;
+	}
+
 	mutex_lock(&state_lock);
-	if (!use_cnt)
-		ret = devfreq_add_governor(&devfreq_gov_memlat);
+	node->gov = &devfreq_gov_compute;
+	node->attr_grp = &compute_dev_attr_group;
+
+	if (!compute_use_cnt)
+		ret = devfreq_add_governor(&devfreq_gov_compute);
 	if (!ret)
-		use_cnt++;
+		compute_use_cnt++;
 	mutex_unlock(&state_lock);
 
+out:
+	if (!ret)
+		dev_info(dev, "Compute governor registered.\n");
+	else
+		dev_err(dev, "Compute governor registration failed!\n");
+
+	return ret;
+}
+
+int register_memlat(struct device *dev, struct memlat_hwmon *hw)
+{
+	struct memlat_node *node;
+	int ret = 0;
+
+	node = register_common(dev, hw);
+	if (IS_ERR(node)) {
+		ret = PTR_ERR(node);
+		goto out;
+	}
+
+	mutex_lock(&state_lock);
+	node->gov = &devfreq_gov_memlat;
+	node->attr_grp = &memlat_dev_attr_group;
+
+	if (!memlat_use_cnt)
+		ret = devfreq_add_governor(&devfreq_gov_memlat);
+	if (!ret)
+		memlat_use_cnt++;
+	mutex_unlock(&state_lock);
+
+out:
 	if (!ret)
 		dev_info(dev, "Memory Latency governor registered.\n");
 	else
diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h
index 8c533ee..6491c6c 100644
--- a/drivers/devfreq/governor_memlat.h
+++ b/drivers/devfreq/governor_memlat.h
@@ -29,6 +29,7 @@
 	unsigned long inst_count;
 	unsigned long mem_count;
 	unsigned long freq;
+	unsigned long stall_pct;
 };
 
 struct core_dev_map {
@@ -73,10 +74,16 @@
 
 #ifdef CONFIG_DEVFREQ_GOV_MEMLAT
 int register_memlat(struct device *dev, struct memlat_hwmon *hw);
+int register_compute(struct device *dev, struct memlat_hwmon *hw);
 int update_memlat(struct memlat_hwmon *hw);
 #else
 static inline int register_memlat(struct device *dev,
-					struct memlat_hwmon *hw)
+				  struct memlat_hwmon *hw)
+{
+	return 0;
+}
+static inline int register_compute(struct device *dev,
+				   struct memlat_hwmon *hw)
 {
 	return 0;
 }
diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
index 5a9166a..2f34a01 100644
--- a/drivers/dma-buf/sync_file.c
+++ b/drivers/dma-buf/sync_file.c
@@ -285,7 +285,7 @@
 	struct sync_file *sync_file = container_of(kref, struct sync_file,
 						     kref);
 
-	if (test_bit(POLL_ENABLED, &sync_file->fence->flags))
+	if (test_bit(POLL_ENABLED, &sync_file->flags))
 		fence_remove_callback(sync_file->fence, &sync_file->cb);
 	fence_put(sync_file->fence);
 	kfree(sync_file);
@@ -305,7 +305,7 @@
 
 	poll_wait(file, &sync_file->wq, wait);
 
-	if (!test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) {
+	if (!test_and_set_bit(POLL_ENABLED, &sync_file->flags)) {
 		if (fence_add_callback(sync_file->fence, &sync_file->cb,
 					   fence_check_cb_func) < 0)
 			wake_up_all(&sync_file->wq);
diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c
index 77242b3..57962bf 100644
--- a/drivers/dma/edma.c
+++ b/drivers/dma/edma.c
@@ -1143,11 +1143,24 @@
 	struct edma_desc *edesc;
 	struct device *dev = chan->device->dev;
 	struct edma_chan *echan = to_edma_chan(chan);
-	unsigned int width, pset_len;
+	unsigned int width, pset_len, array_size;
 
 	if (unlikely(!echan || !len))
 		return NULL;
 
+	/* Align the array size (acnt block) with the transfer properties */
+	switch (__ffs((src | dest | len))) {
+	case 0:
+		array_size = SZ_32K - 1;
+		break;
+	case 1:
+		array_size = SZ_32K - 2;
+		break;
+	default:
+		array_size = SZ_32K - 4;
+		break;
+	}
+
 	if (len < SZ_64K) {
 		/*
 		 * Transfer size less than 64K can be handled with one paRAM
@@ -1169,7 +1182,7 @@
 		 * When the full_length is multibple of 32767 one slot can be
 		 * used to complete the transfer.
 		 */
-		width = SZ_32K - 1;
+		width = array_size;
 		pset_len = rounddown(len, width);
 		/* One slot is enough for lengths multiple of (SZ_32K -1) */
 		if (unlikely(pset_len == len))
@@ -1217,7 +1230,7 @@
 		}
 		dest += pset_len;
 		src += pset_len;
-		pset_len = width = len % (SZ_32K - 1);
+		pset_len = width = len % array_size;
 
 		ret = edma_config_pset(chan, &edesc->pset[1], src, dest, 1,
 				       width, pset_len, DMA_MEM_TO_MEM);
diff --git a/drivers/dma/ti-dma-crossbar.c b/drivers/dma/ti-dma-crossbar.c
index 2403475..88a00d0 100644
--- a/drivers/dma/ti-dma-crossbar.c
+++ b/drivers/dma/ti-dma-crossbar.c
@@ -262,13 +262,14 @@
 	mutex_lock(&xbar->mutex);
 	map->xbar_out = find_first_zero_bit(xbar->dma_inuse,
 					    xbar->dma_requests);
-	mutex_unlock(&xbar->mutex);
 	if (map->xbar_out == xbar->dma_requests) {
+		mutex_unlock(&xbar->mutex);
 		dev_err(&pdev->dev, "Run out of free DMA requests\n");
 		kfree(map);
 		return ERR_PTR(-ENOMEM);
 	}
 	set_bit(map->xbar_out, xbar->dma_inuse);
+	mutex_unlock(&xbar->mutex);
 
 	map->xbar_in = (u16)dma_spec->args[0];
 
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index ee181c5..6e197c1 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -2984,8 +2984,11 @@
 	int err = -ENODEV;
 	int i;
 
+	if (!x86_match_cpu(amd64_cpuids))
+		return -ENODEV;
+
 	if (amd_cache_northbridges() < 0)
-		goto err_ret;
+		return -ENODEV;
 
 	opstate_init();
 
@@ -2998,14 +3001,16 @@
 	if (!msrs)
 		goto err_free;
 
-	for (i = 0; i < amd_nb_num(); i++)
-		if (probe_one_instance(i)) {
+	for (i = 0; i < amd_nb_num(); i++) {
+		err = probe_one_instance(i);
+		if (err) {
 			/* unwind properly */
 			while (--i >= 0)
 				remove_one_instance(i);
 
 			goto err_pci;
 		}
+	}
 
 	setup_pci_device();
 
@@ -3025,7 +3030,6 @@
 	kfree(ecc_stngs);
 	ecc_stngs = NULL;
 
-err_ret:
 	return err;
 }
 
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index c088704..dcb5f94 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/mmzone.h>
 #include <linux/edac.h>
+#include <asm/cpu_device_id.h>
 #include <asm/msr.h>
 #include "edac_core.h"
 #include "mce_amd.h"
diff --git a/drivers/edac/kryo3xx_arm64_edac.c b/drivers/edac/kryo3xx_arm64_edac.c
index 5ca93a6..cf3fdde 100644
--- a/drivers/edac/kryo3xx_arm64_edac.c
+++ b/drivers/edac/kryo3xx_arm64_edac.c
@@ -30,10 +30,11 @@
 #endif
 
 #ifdef CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_CE
-#define ARM64_ERP_PANIC_ON_CE 1
+static bool panic_on_ce = 1;
 #else
-#define ARM64_ERP_PANIC_ON_CE 0
+static bool panic_on_ce;
 #endif
+module_param_named(panic_on_ce, panic_on_ce, bool, 0664);
 
 #ifdef CONFIG_EDAC_KRYO3XX_ARM64_PANIC_ON_UE
 #define ARM64_ERP_PANIC_ON_UE 1
@@ -238,6 +239,8 @@
 	else
 		edac_printk(KERN_CRIT, EDAC_CPU,
 			"Way: %d\n", (int) KRYO3XX_ERRXMISC_WAY(errxmisc) >> 2);
+
+	edev_ctl->panic_on_ce = panic_on_ce;
 	errors[errorcode].func(edev_ctl, smp_processor_id(),
 				level, errors[errorcode].msg);
 }
@@ -360,11 +363,34 @@
 	return IRQ_HANDLED;
 }
 
+static void initialize_registers(void *info)
+{
+	set_errxctlr_el1();
+	set_errxmisc_overflow();
+}
+
+static void init_regs_on_cpu(bool all_cpus)
+{
+	int cpu;
+
+	write_errselr_el1(0);
+	if (all_cpus) {
+		for_each_possible_cpu(cpu)
+			smp_call_function_single(cpu, initialize_registers,
+						NULL, 1);
+	} else
+		initialize_registers(NULL);
+
+	write_errselr_el1(1);
+	initialize_registers(NULL);
+}
+
 static int kryo3xx_pmu_cpu_pm_notify(struct notifier_block *self,
 				unsigned long action, void *v)
 {
 	switch (action) {
 	case CPU_PM_EXIT:
+		init_regs_on_cpu(false);
 		kryo3xx_check_l3_scu_error(panic_handler_drvdata->edev_ctl);
 		kryo3xx_check_l1_l2_ecc(panic_handler_drvdata->edev_ctl);
 		break;
@@ -373,23 +399,14 @@
 	return NOTIFY_OK;
 }
 
-static void initialize_registers(void *info)
-{
-	set_errxctlr_el1();
-	set_errxmisc_overflow();
-}
-
 static int kryo3xx_cpu_erp_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct erp_drvdata *drv;
 	int rc = 0;
 	int fail = 0;
-	int cpu;
 
-	for_each_possible_cpu(cpu)
-		smp_call_function_single(cpu, initialize_registers, NULL, 1);
-
+	init_regs_on_cpu(true);
 
 	drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
 
@@ -413,7 +430,7 @@
 	drv->edev_ctl->mod_name = dev_name(dev);
 	drv->edev_ctl->dev_name = dev_name(dev);
 	drv->edev_ctl->ctl_name = "cache";
-	drv->edev_ctl->panic_on_ce = ARM64_ERP_PANIC_ON_CE;
+	drv->edev_ctl->panic_on_ce = panic_on_ce;
 	drv->edev_ctl->panic_on_ue = ARM64_ERP_PANIC_ON_UE;
 	drv->nb_pm.notifier_call = kryo3xx_pmu_cpu_pm_notify;
 	platform_set_drvdata(pdev, drv);
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index daaac2c..7db692e 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -981,20 +981,19 @@
 	pr_cont("]: 0x%016llx\n", m->status);
 
 	if (m->status & MCI_STATUS_ADDRV)
-		pr_emerg(HW_ERR "Error Addr: 0x%016llx", m->addr);
+		pr_emerg(HW_ERR "Error Addr: 0x%016llx\n", m->addr);
 
 	if (boot_cpu_has(X86_FEATURE_SMCA)) {
+		pr_emerg(HW_ERR "IPID: 0x%016llx", m->ipid);
+
 		if (m->status & MCI_STATUS_SYNDV)
 			pr_cont(", Syndrome: 0x%016llx", m->synd);
 
-		pr_cont(", IPID: 0x%016llx", m->ipid);
-
 		pr_cont("\n");
 
 		decode_smca_errors(m);
 		goto err_code;
-	} else
-		pr_cont("\n");
+	}
 
 	if (!fam_ops)
 		goto err_code;
diff --git a/drivers/edac/qcom_llcc_edac.c b/drivers/edac/qcom_llcc_edac.c
index 4b89cbf..038e89c 100644
--- a/drivers/edac/qcom_llcc_edac.c
+++ b/drivers/edac/qcom_llcc_edac.c
@@ -397,21 +397,6 @@
 	if (rc)
 		goto out_mem;
 
-	if (interrupt_mode) {
-		drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
-		if (!drv->ecc_irq) {
-			rc = -ENODEV;
-			goto out_dev;
-		}
-
-		rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
-				IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
-		if (rc) {
-			dev_err(dev, "failed to request ecc irq\n");
-			goto out_dev;
-		}
-	}
-
 	drv->llcc_banks = devm_kzalloc(&pdev->dev,
 		sizeof(u32) * drv->num_banks, GFP_KERNEL);
 
@@ -437,6 +422,21 @@
 
 	platform_set_drvdata(pdev, edev_ctl);
 
+	if (interrupt_mode) {
+		drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
+		if (!drv->ecc_irq) {
+			rc = -ENODEV;
+			goto out_dev;
+		}
+
+		rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
+				IRQF_TRIGGER_HIGH, "llcc_ecc", edev_ctl);
+		if (rc) {
+			dev_err(dev, "failed to request ecc irq\n");
+			goto out_dev;
+		}
+	}
+
 	return 0;
 
 out_dev:
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index a56c7e0..3c65f69 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -38,7 +38,7 @@
 	  allow logging of different esoc driver traces.
 
 config ESOC_MDM_4x
-	bool "Add support for external mdm9x25/mdm9x35/mdm9x45/mdm9x55"
+	bool "Add support for external mdm9x25/mdm9x35/mdm9x55"
 	help
 	  In some Qualcomm Technologies, Inc. boards, an external modem such as
 	  mdm9x25 or mdm9x35 is connected to a primary msm. The primary soc can
@@ -49,7 +49,7 @@
 	tristate "Command engine for 4x series external modems"
 	help
 	  Provides a command engine to control the behavior of an external modem
-	  such as mdm9x25/mdm9x35/mdm9x45/mdm9x55/QSC. Allows the primary soc to put the
+	  such as mdm9x25/mdm9x35/mdm9x55/QSC. Allows the primary soc to put the
 	  external modem in a specific mode. Also listens for events on the
 	  external modem.
 
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 334278b..677e21d 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -88,12 +88,10 @@
 		return;
 	if (mdm->irq_mask & IRQ_ERRFATAL) {
 		enable_irq(mdm->errfatal_irq);
-		irq_set_irq_wake(mdm->errfatal_irq, 1);
 		mdm->irq_mask &= ~IRQ_ERRFATAL;
 	}
 	if (mdm->irq_mask & IRQ_STATUS) {
 		enable_irq(mdm->status_irq);
-		irq_set_irq_wake(mdm->status_irq, 1);
 		mdm->irq_mask &= ~IRQ_STATUS;
 	}
 	if (mdm->irq_mask & IRQ_PBLRDY) {
@@ -107,12 +105,10 @@
 	if (!mdm)
 		return;
 	if (!(mdm->irq_mask & IRQ_ERRFATAL)) {
-		irq_set_irq_wake(mdm->errfatal_irq, 0);
 		disable_irq_nosync(mdm->errfatal_irq);
 		mdm->irq_mask |= IRQ_ERRFATAL;
 	}
 	if (!(mdm->irq_mask & IRQ_STATUS)) {
-		irq_set_irq_wake(mdm->status_irq, 0);
 		disable_irq_nosync(mdm->status_irq);
 		mdm->irq_mask |= IRQ_STATUS;
 	}
@@ -179,26 +175,48 @@
 	struct device *dev = mdm->dev;
 	int ret;
 	bool graceful_shutdown = false;
+	u32 status, err_fatal;
 
 	switch (cmd) {
 	case ESOC_PWR_ON:
+		if (esoc->auto_boot) {
+			/*
+			 * If esoc has already booted, we would have missed
+			 * status change interrupt. Read status and err_fatal
+			 * signals to arrive at the state of esoc.
+			 */
+			esoc->clink_ops->get_status(&status, esoc);
+			esoc->clink_ops->get_err_fatal(&err_fatal, esoc);
+			if (err_fatal)
+				return -EIO;
+			if (status && !mdm->ready) {
+				mdm->ready = true;
+				esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
+			}
+		}
 		gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
-		mdm_enable_irqs(mdm);
 		mdm->init = 1;
 		mdm_do_first_power_on(mdm);
+		mdm_enable_irqs(mdm);
 		break;
 	case ESOC_PWR_OFF:
 		mdm_disable_irqs(mdm);
 		mdm->debug = 0;
 		mdm->ready = false;
 		mdm->trig_cnt = 0;
+		if (esoc->primary)
+			break;
 		graceful_shutdown = true;
-		ret = sysmon_send_shutdown(&esoc->subsys);
-		if (ret) {
-			dev_err(mdm->dev, "sysmon shutdown fail, ret = %d\n",
-									ret);
-			graceful_shutdown = false;
-			goto force_poff;
+		if (!esoc->userspace_handle_shutdown) {
+			ret = sysmon_send_shutdown(&esoc->subsys);
+			if (ret) {
+				dev_err(mdm->dev,
+				 "sysmon shutdown fail, ret = %d\n", ret);
+				graceful_shutdown = false;
+				goto force_poff;
+			}
+		} else {
+			esoc_clink_queue_request(ESOC_REQ_SEND_SHUTDOWN, esoc);
 		}
 		dev_dbg(mdm->dev, "Waiting for status gpio go low\n");
 		status_down = false;
@@ -229,12 +247,17 @@
 				esoc->subsys.sysmon_shutdown_ret);
 		}
 
+		if (esoc->primary)
+			break;
 		/*
 		 * Force a shutdown of the mdm. This is required in order
 		 * to prevent the mdm from immediately powering back on
-		 * after the shutdown
+		 * after the shutdown. Avoid setting status to 0, if line is
+		 * monitored by multiple mdms(might be wrongly interpreted as
+		 * a primary crash).
 		 */
-		gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+		if (esoc->statusline_not_a_powersource == false)
+			gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
 		esoc_clink_queue_request(ESOC_REQ_SHUTDOWN, esoc);
 		mdm_power_down(mdm);
 		mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
@@ -250,9 +273,12 @@
 		 */
 		mdm->ready = false;
 		cancel_delayed_work(&mdm->mdm2ap_status_check_work);
-		gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
-		dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
-		msleep(mdm->ramdump_delay_ms);
+		if (!mdm->esoc->auto_boot) {
+			gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+			dev_dbg(mdm->dev,
+				"set ap2mdm errfatal to force reset\n");
+			msleep(mdm->ramdump_delay_ms);
+		}
 		break;
 	case ESOC_EXE_DEBUG:
 		mdm->debug = 1;
@@ -380,6 +406,8 @@
 		status_down = false;
 		dev_dbg(dev, "signal apq err fatal for graceful restart\n");
 		gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+		if (esoc->primary)
+			break;
 		timeout = local_clock();
 		do_div(timeout, NSEC_PER_MSEC);
 		timeout += MDM_MODEM_TIMEOUT;
@@ -421,7 +449,8 @@
 		goto mdm_pwroff_irq;
 	esoc = mdm->esoc;
 	dev_err(dev, "%s: mdm sent errfatal interrupt\n",
-					 __func__);
+					__func__);
+	subsys_set_crash_status(esoc->subsys_dev, true);
 	/* disable irq ?*/
 	esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc);
 	return IRQ_HANDLED;
@@ -442,11 +471,26 @@
 		return IRQ_HANDLED;
 	dev = mdm->dev;
 	esoc = mdm->esoc;
+	/*
+	 * On auto boot devices, there is a possibility of receiving
+	 * status change interrupt before esoc_clink structure is
+	 * initialized. Ignore them.
+	 */
+	if (!esoc)
+		return IRQ_HANDLED;
 	value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
 	if (value == 0 && mdm->ready) {
 		dev_err(dev, "unexpected reset external modem\n");
+		subsys_set_crash_status(esoc->subsys_dev, true);
 		esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
 	} else if (value == 1) {
+		/*
+		 * In auto_boot cases, bailout early if mdm
+		 * is up already.
+		 */
+		if (esoc->auto_boot && mdm->ready)
+			return IRQ_HANDLED;
+
 		cancel_delayed_work(&mdm->mdm2ap_status_check_work);
 		dev_dbg(dev, "status = 1: mdm is now ready\n");
 		mdm->ready = true;
@@ -454,6 +498,8 @@
 		queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
 		if (mdm->get_restart_reason)
 			queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
+		if (esoc->auto_boot)
+			esoc->clink_ops->notify(ESOC_BOOT_DONE, esoc);
 	}
 	return IRQ_HANDLED;
 }
@@ -482,7 +528,7 @@
 	return IRQ_HANDLED;
 }
 
-static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
+static void mdm_get_status(u32 *status, struct esoc_clink *esoc)
 {
 	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
 
@@ -490,7 +536,16 @@
 		*status = 0;
 	else
 		*status = 1;
-	return 0;
+}
+
+static void mdm_get_err_fatal(u32 *status, struct esoc_clink *esoc)
+{
+	struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+
+	if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_ERRFATAL)) == 0)
+		*status = 0;
+	else
+		*status = 1;
 }
 
 static void mdm_configure_debug(struct mdm_ctrl *mdm)
@@ -573,13 +628,21 @@
 						&mdm->ramdump_delay_ms);
 	if (ret)
 		mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
-	/* Multilple gpio_request calls are allowed */
+	/*
+	 * In certain scenarios, multiple esoc devices are monitoring
+	 * same AP2MDM_STATUS line. But only one of them will have a
+	 * successful gpio_request call. Initialize gpio only if request
+	 * succeeds.
+	 */
 	if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
 		dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
-	/* Multilple gpio_request calls are allowed */
+	else
+		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
 	if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
 		dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
 			   __func__);
+	else
+		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
 	if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
 		dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
 			   __func__);
@@ -612,9 +675,6 @@
 		}
 	}
 
-	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
-	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
-
 	if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
 		gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);
 
@@ -637,6 +697,7 @@
 		goto errfatal_err;
 	}
 	mdm->errfatal_irq = irq;
+	irq_set_irq_wake(mdm->errfatal_irq, 1);
 
 errfatal_err:
 	 /* status irq */
@@ -655,6 +716,7 @@
 		goto status_err;
 	}
 	mdm->status_irq = irq;
+	irq_set_irq_wake(mdm->status_irq, 1);
 status_err:
 	if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) {
 		irq =  platform_get_irq_byname(pdev, "plbrdy_irq");
@@ -748,6 +810,7 @@
 		dev_err(mdm->dev, "cannot allocate esoc device\n");
 		return PTR_ERR(esoc);
 	}
+	esoc->pdev = pdev;
 	mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
 	if (!mdm->mdm_queue) {
 		dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -818,6 +881,7 @@
 		dev_err(mdm->dev, "cannot allocate esoc device\n");
 		return PTR_ERR(esoc);
 	}
+	esoc->pdev = pdev;
 	mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
 	if (!mdm->mdm_queue) {
 		dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -906,6 +970,7 @@
 		dev_err(mdm->dev, "cannot allocate esoc device\n");
 		return PTR_ERR(esoc);
 	}
+	esoc->pdev = pdev;
 	mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
 	if (!mdm->mdm_queue) {
 		dev_err(mdm->dev, "could not create mdm_queue\n");
@@ -966,6 +1031,7 @@
 static struct esoc_clink_ops mdm_cops = {
 	.cmd_exe = mdm_cmd_exe,
 	.get_status = mdm_get_status,
+	.get_err_fatal = mdm_get_err_fatal,
 	.notify = mdm_notify,
 };
 
diff --git a/drivers/esoc/esoc-mdm-dbg-eng.c b/drivers/esoc/esoc-mdm-dbg-eng.c
index 309c820..a61588a 100644
--- a/drivers/esoc/esoc-mdm-dbg-eng.c
+++ b/drivers/esoc/esoc-mdm-dbg-eng.c
@@ -269,7 +269,7 @@
 {
 	unsigned int i;
 	unsigned long flags;
-	size_t count;
+	size_t count = 0;
 
 	spin_lock_irqsave(&req_lock, flags);
 	for (i = 0; i < ARRAY_SIZE(req_to_str); i++) {
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
index 31cd8c4..77ae84b 100644
--- a/drivers/esoc/esoc-mdm-drv.c
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/reboot.h>
+#include <linux/of.h>
 #include "esoc.h"
 #include "mdm-dbg.h"
 
@@ -74,7 +75,14 @@
 		break;
 	case ESOC_UNEXPECTED_RESET:
 	case ESOC_ERR_FATAL:
-		if (mdm_drv->mode == CRASH)
+		/*
+		 * Modem can crash while we are waiting for boot_done during
+		 * a subsystem_get(). Setting mode to CRASH will prevent a
+		 * subsequent subsystem_get() from entering poweron ops. Avoid
+		 * this by seting mode to CRASH only if device was up and
+		 * running.
+		 */
+		if (mdm_drv->mode == CRASH || mdm_drv->mode != RUN)
 			return;
 		mdm_drv->mode = CRASH;
 		queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
@@ -164,8 +172,9 @@
 								subsys);
 	struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
 	const struct esoc_clink_ops * const clink_ops = esoc_clink->clink_ops;
+	int timeout = INT_MAX;
 
-	if (!esoc_req_eng_enabled(esoc_clink)) {
+	if (!esoc_clink->auto_boot && !esoc_req_eng_enabled(esoc_clink)) {
 		dev_dbg(&esoc_clink->dev, "Wait for req eng registration\n");
 		wait_for_completion(&mdm_drv->req_eng_wait);
 	}
@@ -190,8 +199,17 @@
 			return ret;
 		}
 	}
-	wait_for_completion(&mdm_drv->boot_done);
-	if (mdm_drv->boot_fail) {
+
+	/*
+	 * In autoboot case, it is possible that we can forever wait for
+	 * boot completion, when esoc fails to boot. This is because there
+	 * is no helper application which can alert esoc driver about boot
+	 * failure. Prevent going to wait forever in such case.
+	 */
+	if (esoc_clink->auto_boot)
+		timeout = 10 * HZ;
+	ret = wait_for_completion_timeout(&mdm_drv->boot_done, timeout);
+	if (mdm_drv->boot_fail || ret <= 0) {
 		dev_err(&esoc_clink->dev, "booting failed\n");
 		return -EIO;
 	}
@@ -219,10 +237,12 @@
 
 static int mdm_register_ssr(struct esoc_clink *esoc_clink)
 {
-	esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
-	esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
-	esoc_clink->subsys.powerup = mdm_subsys_powerup;
-	esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
+	struct subsys_desc *subsys = &esoc_clink->subsys;
+
+	subsys->shutdown = mdm_subsys_shutdown;
+	subsys->ramdump = mdm_subsys_ramdumps;
+	subsys->powerup = mdm_subsys_powerup;
+	subsys->crash_shutdown = mdm_crash_shutdown;
 	return esoc_clink_register_ssr(esoc_clink);
 }
 
diff --git a/drivers/esoc/esoc-mdm-pon.c b/drivers/esoc/esoc-mdm-pon.c
index 47d54db..0e85776 100644
--- a/drivers/esoc/esoc-mdm-pon.c
+++ b/drivers/esoc/esoc-mdm-pon.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -68,6 +68,9 @@
 	struct device *dev = mdm->dev;
 
 	dev_dbg(dev, "Powering on modem for the first time\n");
+	if (mdm->esoc->auto_boot)
+		return 0;
+
 	mdm_toggle_soft_reset(mdm, false);
 	/* Add a delay to allow PON sequence to complete*/
 	mdelay(50);
@@ -134,6 +137,9 @@
 
 static void mdm4x_cold_reset(struct mdm_ctrl *mdm)
 {
+	if (!gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET)))
+		return;
+
 	dev_dbg(mdm->dev, "Triggering mdm cold reset");
 	gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
 			!!mdm->soft_reset_inverted);
@@ -201,15 +207,6 @@
 	.setup = mdm4x_pon_setup,
 };
 
-struct mdm_pon_ops mdm9x45_pon_ops = {
-	.pon = mdm4x_do_first_power_on,
-	.soft_reset = mdm4x_toggle_soft_reset,
-	.poff_force = mdm4x_power_down,
-	.cold_reset = mdm4x_cold_reset,
-	.dt_init = mdm4x_pon_dt_init,
-	.setup = mdm4x_pon_setup,
-};
-
 struct mdm_pon_ops mdm9x55_pon_ops = {
 	.pon = mdm4x_do_first_power_on,
 	.soft_reset = mdm9x55_toggle_soft_reset,
diff --git a/drivers/esoc/esoc-mdm.h b/drivers/esoc/esoc-mdm.h
index fa3a576..621d913 100644
--- a/drivers/esoc/esoc-mdm.h
+++ b/drivers/esoc/esoc-mdm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -33,8 +33,6 @@
 #define MDM9x35_PCIE			"PCIe"
 #define MDM9x35_DUAL_LINK		"HSIC+PCIe"
 #define MDM9x35_HSIC			"HSIC"
-#define MDM9x45_LABEL			"MDM9x45"
-#define MDM9x45_PCIE			"PCIe"
 #define MDM9x55_LABEL			"MDM9x55"
 #define MDM9x55_PCIE			"PCIe"
 #define MDM2AP_STATUS_TIMEOUT_MS	120000L
@@ -151,6 +149,5 @@
 
 extern struct mdm_pon_ops mdm9x25_pon_ops;
 extern struct mdm_pon_ops mdm9x35_pon_ops;
-extern struct mdm_pon_ops mdm9x45_pon_ops;
 extern struct mdm_pon_ops mdm9x55_pon_ops;
 #endif
diff --git a/drivers/esoc/esoc.h b/drivers/esoc/esoc.h
index 9fc3192..df3c9df 100644
--- a/drivers/esoc/esoc.h
+++ b/drivers/esoc/esoc.h
@@ -49,6 +49,7 @@
  * @link_info: additional info about the physical link.
  * @parent: parent device.
  * @dev: device for userspace interface.
+ * @pdev: platform device to interface with SSR driver.
  * @id: id of the external device.
  * @owner: owner of the device.
  * @clink_ops: control operations for the control link
@@ -59,6 +60,12 @@
  * @subsys_desc: descriptor for subsystem restart
  * @subsys_dev: ssr device handle.
  * @np: device tree node for esoc_clink.
+ * @auto_boot: boots independently.
+ * @primary: primary esoc controls(reset/poweroff) all secondary
+ *	 esocs, but not	otherway around.
+ * @statusline_not_a_powersource: True if status line to esoc is not a
+ *				power source.
+ * @userspace_handle_shutdown: True if user space handles shutdown requests.
  */
 struct esoc_clink {
 	const char *name;
@@ -66,6 +73,7 @@
 	const char *link_info;
 	struct device *parent;
 	struct device dev;
+	struct platform_device *pdev;
 	unsigned int id;
 	struct module *owner;
 	const struct esoc_clink_ops *clink_ops;
@@ -77,17 +85,23 @@
 	struct subsys_desc subsys;
 	struct subsys_device *subsys_dev;
 	struct device_node *np;
+	bool auto_boot;
+	bool primary;
+	bool statusline_not_a_powersource;
+	bool userspace_handle_shutdown;
 };
 
 /**
  * struct esoc_clink_ops: Operations to control external soc
  * @cmd_exe: Execute control command
  * @get_status: Get current status, or response to previous command
+ * @get_err_fatal: Get status of err fatal signal
  * @notify_esoc: notify external soc of events
  */
 struct esoc_clink_ops {
 	int (*cmd_exe)(enum esoc_cmd cmd, struct esoc_clink *dev);
-	int (*get_status)(u32 *status, struct esoc_clink *dev);
+	void (*get_status)(u32 *status, struct esoc_clink *dev);
+	void (*get_err_fatal)(u32 *status, struct esoc_clink *dev);
 	void (*notify)(enum esoc_notify notify, struct esoc_clink *dev);
 };
 
diff --git a/drivers/esoc/esoc_bus.c b/drivers/esoc/esoc_bus.c
index cef570b..d9ab993 100644
--- a/drivers/esoc/esoc_bus.c
+++ b/drivers/esoc/esoc_bus.c
@@ -189,7 +189,7 @@
 	snprintf(subsys_name, len, "esoc%d", esoc_clink->id);
 	esoc_clink->subsys.name = subsys_name;
 	esoc_clink->dev.of_node = esoc_clink->np;
-	esoc_clink->subsys.dev = &esoc_clink->dev;
+	esoc_clink->subsys.dev = &esoc_clink->pdev->dev;
 	esoc_clink->subsys_dev = subsys_register(&esoc_clink->subsys);
 	if (IS_ERR_OR_NULL(esoc_clink->subsys_dev)) {
 		dev_err(&esoc_clink->dev, "failed to register ssr node\n");
diff --git a/drivers/esoc/esoc_dev.c b/drivers/esoc/esoc_dev.c
index 0c9e428..1d9e623 100644
--- a/drivers/esoc/esoc_dev.c
+++ b/drivers/esoc/esoc_dev.c
@@ -224,9 +224,11 @@
 		clink_ops->notify(esoc_cmd, esoc_clink);
 		break;
 	case ESOC_GET_STATUS:
-		err = clink_ops->get_status(&status, esoc_clink);
-		if (err)
-			return err;
+		clink_ops->get_status(&status, esoc_clink);
+		put_user(status, (unsigned int __user *)uarg);
+		break;
+	case ESOC_GET_ERR_FATAL:
+		clink_ops->get_err_fatal(&status, esoc_clink);
 		put_user(status, (unsigned int __user *)uarg);
 		break;
 	case ESOC_WAIT_FOR_CRASH:
@@ -336,7 +338,6 @@
 	esoc_udev = esoc_udev_get_by_minor(esoc_clink->id);
 	if (!esoc_udev)
 		return 0;
-	return_esoc_udev(esoc_udev);
 	device_destroy(esoc_class, MKDEV(esoc_major, esoc_clink->id));
 	return_esoc_udev(esoc_udev);
 	return 0;
diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c
index 42f41e8..27f67c2 100644
--- a/drivers/extcon/extcon-axp288.c
+++ b/drivers/extcon/extcon-axp288.c
@@ -168,7 +168,7 @@
 		return ret;
 	}
 
-	vbus_attach = (pwr_stat & PS_STAT_VBUS_PRESENT);
+	vbus_attach = (pwr_stat & PS_STAT_VBUS_VALID);
 	if (!vbus_attach)
 		goto notify_otg;
 
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9338ff7..642fa03 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1206,6 +1206,8 @@
 	tristate "Microchip MCP23xxx I/O expander"
 	depends on OF_GPIO
 	select GPIOLIB_IRQCHIP
+	select REGMAP_I2C if I2C
+	select REGMAP if SPI_MASTER
 	help
 	  SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
 	  I/O expanders.
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index f2bb512..063d176 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -703,24 +703,23 @@
 {
 	struct lineevent_state *le = p;
 	struct gpioevent_data ge;
-	int ret;
+	int ret, level;
 
 	ge.timestamp = ktime_get_real_ns();
+	level = gpiod_get_value_cansleep(le->desc);
 
 	if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
 	    && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
-		int level = gpiod_get_value_cansleep(le->desc);
-
 		if (level)
 			/* Emit low-to-high event */
 			ge.id = GPIOEVENT_EVENT_RISING_EDGE;
 		else
 			/* Emit high-to-low event */
 			ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
-	} else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) {
+	} else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && level) {
 		/* Emit low-to-high event */
 		ge.id = GPIOEVENT_EVENT_RISING_EDGE;
-	} else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) {
+	} else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE && !level) {
 		/* Emit high-to-low event */
 		ge.id = GPIOEVENT_EVENT_FALLING_EDGE;
 	} else {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 264899d..05ff98b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -491,6 +491,9 @@
 	case TTM_PL_TT:
 		break;
 	case TTM_PL_VRAM:
+		if (mem->start == AMDGPU_BO_INVALID_OFFSET)
+			return -EINVAL;
+
 		mem->bus.offset = mem->start << PAGE_SHIFT;
 		/* check if it's visible */
 		if ((mem->bus.offset + mem->bus.size) > adev->mc.visible_vram_size)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index 7fe8fd8..743a12d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -315,6 +315,10 @@
 			amdgpu_dpm_enable_vce(adev, false);
 		} else {
 			amdgpu_asic_set_vce_clocks(adev, 0, 0);
+			amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+							    AMD_PG_STATE_GATE);
+			amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+							    AMD_CG_STATE_GATE);
 		}
 	} else {
 		schedule_delayed_work(&adev->vce.idle_work, VCE_IDLE_TIMEOUT);
@@ -340,6 +344,11 @@
 			amdgpu_dpm_enable_vce(adev, true);
 		} else {
 			amdgpu_asic_set_vce_clocks(adev, 53300, 40000);
+			amdgpu_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+							    AMD_CG_STATE_UNGATE);
+			amdgpu_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_VCE,
+							    AMD_PG_STATE_UNGATE);
+
 		}
 	}
 	mutex_unlock(&adev->vce.idle_mutex);
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index dc9511c..327bdf1 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -1301,6 +1301,7 @@
 		amdgpu_program_register_sequence(adev,
 						 pitcairn_mgcg_cgcg_init,
 						 (const u32)ARRAY_SIZE(pitcairn_mgcg_cgcg_init));
+		break;
 	case CHIP_VERDE:
 		amdgpu_program_register_sequence(adev,
 						 verde_golden_registers,
@@ -1325,6 +1326,7 @@
 		amdgpu_program_register_sequence(adev,
 						 oland_mgcg_cgcg_init,
 						 (const u32)ARRAY_SIZE(oland_mgcg_cgcg_init));
+		break;
 	case CHIP_HAINAN:
 		amdgpu_program_register_sequence(adev,
 						 hainan_golden_registers,
diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
index ab3df6d..3f445df91 100644
--- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c
@@ -89,6 +89,10 @@
 {
 	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
 
+	if (!(adev->flags & AMD_IS_APU) &&
+	    (RREG32_SMC(ixCC_HARVEST_FUSES) & CC_HARVEST_FUSES__UVD_DISABLE_MASK))
+		return -ENOENT;
+
 	uvd_v6_0_set_ring_funcs(adev);
 	uvd_v6_0_set_irq_funcs(adev);
 
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
index a6a4b2b..6a3470f 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c
@@ -739,8 +739,10 @@
 		struct kfd_event_data event_data;
 
 		if (copy_from_user(&event_data, &events[i],
-				sizeof(struct kfd_event_data)))
+				sizeof(struct kfd_event_data))) {
+			ret = -EFAULT;
 			goto fail;
+		}
 
 		ret = init_event_waiter(p, &event_waiters[i],
 				event_data.event_id, i);
diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
index 08cd0bd..3907439 100644
--- a/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
+++ b/drivers/gpu/drm/amd/powerplay/hwmgr/smu7_hwmgr.c
@@ -825,7 +825,7 @@
 {
 	uint32_t reference_clock, tmp;
 	struct cgs_display_info info = {0};
-	struct cgs_mode_info mode_info;
+	struct cgs_mode_info mode_info = {0};
 
 	info.mode_info = &mode_info;
 
@@ -3718,10 +3718,9 @@
 	uint32_t ref_clock;
 	uint32_t refresh_rate = 0;
 	struct cgs_display_info info = {0};
-	struct cgs_mode_info mode_info;
+	struct cgs_mode_info mode_info = {0};
 
 	info.mode_info = &mode_info;
-
 	cgs_get_active_displays_info(hwmgr->device, &info);
 	num_active_displays = info.display_count;
 
@@ -3737,6 +3736,7 @@
 	frame_time_in_us = 1000000 / refresh_rate;
 
 	pre_vbi_time_in_us = frame_time_in_us - 200 - mode_info.vblank_time_us;
+
 	data->frame_time_x2 = frame_time_in_us * 2 / 100;
 
 	display_gap2 = pre_vbi_time_in_us * (ref_clock / 100);
diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 82c193e..8b009b5 100644
--- a/drivers/gpu/drm/arm/malidp_planes.c
+++ b/drivers/gpu/drm/arm/malidp_planes.c
@@ -150,13 +150,8 @@
 	/* convert src values from Q16 fixed point to integer */
 	src_w = plane->state->src_w >> 16;
 	src_h = plane->state->src_h >> 16;
-	if (plane->state->rotation & MALIDP_ROTATED_MASK) {
-		dest_w = plane->state->crtc_h;
-		dest_h = plane->state->crtc_w;
-	} else {
-		dest_w = plane->state->crtc_w;
-		dest_h = plane->state->crtc_h;
-	}
+	dest_w = plane->state->crtc_w;
+	dest_h = plane->state->crtc_h;
 
 	malidp_hw_write(mp->hwdev, format_id, mp->layer->base);
 
@@ -187,11 +182,12 @@
 
 	/* setup the rotation and axis flip bits */
 	if (plane->state->rotation & DRM_ROTATE_MASK)
-		val = ilog2(plane->state->rotation & DRM_ROTATE_MASK) << LAYER_ROT_OFFSET;
+		val |= ilog2(plane->state->rotation & DRM_ROTATE_MASK) <<
+		       LAYER_ROT_OFFSET;
 	if (plane->state->rotation & DRM_REFLECT_X)
-		val |= LAYER_V_FLIP;
-	if (plane->state->rotation & DRM_REFLECT_Y)
 		val |= LAYER_H_FLIP;
+	if (plane->state->rotation & DRM_REFLECT_Y)
+		val |= LAYER_V_FLIP;
 
 	/* set the 'enable layer' bit */
 	val |= LAYER_ENABLE;
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 161c923..3e74e1a 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -315,6 +315,8 @@
 	bool edid_read;
 
 	wait_queue_head_t wq;
+	struct work_struct hpd_work;
+
 	struct drm_bridge bridge;
 	struct drm_connector connector;
 
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 8ed3906..a68f94d 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -325,7 +325,7 @@
 	adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
 }
 
-static void adv7511_power_on(struct adv7511 *adv7511)
+static void __adv7511_power_on(struct adv7511 *adv7511)
 {
 	adv7511->current_edid_segment = -1;
 
@@ -354,6 +354,11 @@
 	regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER2,
 			   ADV7511_REG_POWER2_HPD_SRC_MASK,
 			   ADV7511_REG_POWER2_HPD_SRC_NONE);
+}
+
+static void adv7511_power_on(struct adv7511 *adv7511)
+{
+	__adv7511_power_on(adv7511);
 
 	/*
 	 * Most of the registers are reset during power down or when HPD is low.
@@ -362,21 +367,23 @@
 
 	if (adv7511->type == ADV7533)
 		adv7533_dsi_power_on(adv7511);
-
 	adv7511->powered = true;
 }
 
-static void adv7511_power_off(struct adv7511 *adv7511)
+static void __adv7511_power_off(struct adv7511 *adv7511)
 {
 	/* TODO: setup additional power down modes */
 	regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
 			   ADV7511_POWER_POWER_DOWN,
 			   ADV7511_POWER_POWER_DOWN);
 	regcache_mark_dirty(adv7511->regmap);
+}
 
+static void adv7511_power_off(struct adv7511 *adv7511)
+{
+	__adv7511_power_off(adv7511);
 	if (adv7511->type == ADV7533)
 		adv7533_dsi_power_off(adv7511);
-
 	adv7511->powered = false;
 }
 
@@ -402,6 +409,27 @@
 	return false;
 }
 
+static void adv7511_hpd_work(struct work_struct *work)
+{
+	struct adv7511 *adv7511 = container_of(work, struct adv7511, hpd_work);
+	enum drm_connector_status status;
+	unsigned int val;
+	int ret;
+
+	ret = regmap_read(adv7511->regmap, ADV7511_REG_STATUS, &val);
+	if (ret < 0)
+		status = connector_status_disconnected;
+	else if (val & ADV7511_STATUS_HPD)
+		status = connector_status_connected;
+	else
+		status = connector_status_disconnected;
+
+	if (adv7511->connector.status != status) {
+		adv7511->connector.status = status;
+		drm_kms_helper_hotplug_event(adv7511->connector.dev);
+	}
+}
+
 static int adv7511_irq_process(struct adv7511 *adv7511, bool process_hpd)
 {
 	unsigned int irq0, irq1;
@@ -419,7 +447,7 @@
 	regmap_write(adv7511->regmap, ADV7511_REG_INT(1), irq1);
 
 	if (process_hpd && irq0 & ADV7511_INT0_HPD && adv7511->bridge.encoder)
-		drm_helper_hpd_irq_event(adv7511->connector.dev);
+		schedule_work(&adv7511->hpd_work);
 
 	if (irq0 & ADV7511_INT0_EDID_READY || irq1 & ADV7511_INT1_DDC_ERROR) {
 		adv7511->edid_read = true;
@@ -546,23 +574,20 @@
 
 	/* Reading the EDID only works if the device is powered */
 	if (!adv7511->powered) {
-		regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
-				   ADV7511_POWER_POWER_DOWN, 0);
-		if (adv7511->i2c_main->irq) {
-			regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(0),
-				     ADV7511_INT0_EDID_READY);
-			regmap_write(adv7511->regmap, ADV7511_REG_INT_ENABLE(1),
-				     ADV7511_INT1_DDC_ERROR);
-		}
-		adv7511->current_edid_segment = -1;
+		unsigned int edid_i2c_addr =
+					(adv7511->i2c_main->addr << 1) + 4;
+
+		__adv7511_power_on(adv7511);
+
+		/* Reset the EDID_I2C_ADDR register as it might be cleared */
+		regmap_write(adv7511->regmap, ADV7511_REG_EDID_I2C_ADDR,
+			     edid_i2c_addr);
 	}
 
 	edid = drm_do_get_edid(connector, adv7511_get_edid_block, adv7511);
 
 	if (!adv7511->powered)
-		regmap_update_bits(adv7511->regmap, ADV7511_REG_POWER,
-				   ADV7511_POWER_POWER_DOWN,
-				   ADV7511_POWER_POWER_DOWN);
+		__adv7511_power_off(adv7511);
 
 	kfree(adv7511->edid);
 	adv7511->edid = edid;
@@ -1006,6 +1031,8 @@
 			goto err_i2c_unregister_edid;
 	}
 
+	INIT_WORK(&adv7511->hpd_work, adv7511_hpd_work);
+
 	if (i2c->irq) {
 		init_waitqueue_head(&adv7511->wq);
 
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 99011621..33778bf 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1459,6 +1459,9 @@
 	if (config->funcs->atomic_check)
 		ret = config->funcs->atomic_check(state->dev, state);
 
+	if (ret)
+		return ret;
+
 	if (!state->allow_modeset) {
 		for_each_crtc_in_state(state, crtc, crtc_state, i) {
 			if (drm_atomic_crtc_needs_modeset(crtc_state)) {
@@ -1469,7 +1472,7 @@
 		}
 	}
 
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL(drm_atomic_check_only);
 
@@ -1868,10 +1871,10 @@
 	struct drm_atomic_state *state;
 	struct drm_modeset_acquire_ctx ctx;
 	struct drm_plane *plane;
-	struct drm_out_fence_state *fence_state = NULL;
+	struct drm_out_fence_state *fence_state;
 	unsigned plane_mask;
 	int ret = 0;
-	unsigned int i, j, num_fences = 0;
+	unsigned int i, j, num_fences;
 
 	/* disallow for drivers not supporting atomic: */
 	if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
@@ -1912,6 +1915,8 @@
 	plane_mask = 0;
 	copied_objs = 0;
 	copied_props = 0;
+	fence_state = NULL;
+	num_fences = 0;
 
 	for (i = 0; i < arg->count_objs; i++) {
 		uint32_t obj_id, count_props;
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index ac5437e..6394109 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -358,20 +358,13 @@
  */
 int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
 {
-	u8 value;
+	u8 value = DP_SET_POWER_D0;
 	int err;
 
 	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
 	if (link->revision < 0x11)
 		return 0;
 
-	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
-	if (err < 0)
-		return err;
-
-	value &= ~DP_SET_POWER_MASK;
-	value |= DP_SET_POWER_D0;
-
 	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
 	if (err < 0)
 		return err;
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 362b8cd..80a903b 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -218,7 +218,7 @@
 	ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root);
 	if (ret) {
 		DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n");
-		return ret;
+		goto err_debugfs;
 	}
 
 	ret = device_add(minor->kdev);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 465bacd..48e99ab 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -255,13 +255,13 @@
 	struct drm_gem_object *obj = ptr;
 	struct drm_device *dev = obj->dev;
 
+	if (dev->driver->gem_close_object)
+		dev->driver->gem_close_object(obj, file_priv);
+
 	if (drm_core_check_feature(dev, DRIVER_PRIME))
 		drm_gem_remove_prime_handles(obj, file_priv);
 	drm_vma_node_revoke(&obj->vma_node, file_priv);
 
-	if (dev->driver->gem_close_object)
-		dev->driver->gem_close_object(obj, file_priv);
-
 	drm_gem_object_handle_unreference_unlocked(obj);
 
 	return 0;
diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index 76a1e43..d9a5762 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -360,6 +360,7 @@
 
 	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
 		msg->flags |= MIPI_DSI_MSG_USE_LPM;
+	msg->flags |= MIPI_DSI_MSG_LASTCOMMAND;
 
 	return ops->transfer(dsi->host, msg);
 }
diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c
index a4d81cf..ef80ec6 100644
--- a/drivers/gpu/drm/drm_property.c
+++ b/drivers/gpu/drm/drm_property.c
@@ -530,7 +530,7 @@
 
 	drm_mode_object_unregister(blob->dev, &blob->base);
 
-	kfree(blob);
+	vfree(blob);
 }
 
 /**
@@ -557,7 +557,7 @@
 	if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob))
 		return ERR_PTR(-EINVAL);
 
-	blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL);
+	blob = vmalloc(sizeof(struct drm_property_blob)+length);
 	if (!blob)
 		return ERR_PTR(-ENOMEM);
 
@@ -573,7 +573,7 @@
 	ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB,
 				      true, drm_property_free_blob);
 	if (ret) {
-		kfree(blob);
+		vfree(blob);
 		return ERR_PTR(-EINVAL);
 	}
 
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem.c b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
index 0370b84..82dd57d 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem.c
@@ -549,12 +549,15 @@
 void etnaviv_gem_free_object(struct drm_gem_object *obj)
 {
 	struct etnaviv_gem_object *etnaviv_obj = to_etnaviv_bo(obj);
+	struct etnaviv_drm_private *priv = obj->dev->dev_private;
 	struct etnaviv_vram_mapping *mapping, *tmp;
 
 	/* object should not be active */
 	WARN_ON(is_active(etnaviv_obj));
 
+	mutex_lock(&priv->gem_lock);
 	list_del(&etnaviv_obj->gem_node);
+	mutex_unlock(&priv->gem_lock);
 
 	list_for_each_entry_safe(mapping, tmp, &etnaviv_obj->vram_list,
 				 obj_node) {
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
index afdd55d..1ac9a95 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c
@@ -264,8 +264,8 @@
 		if (ret)
 			return ret;
 
-		if (r->reloc_offset >= bo->obj->base.size - sizeof(*ptr)) {
-			DRM_ERROR("relocation %u outside object", i);
+		if (r->reloc_offset > bo->obj->base.size - sizeof(*ptr)) {
+			DRM_ERROR("relocation %u outside object\n", i);
 			return -EINVAL;
 		}
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index fbd13fa..603d842 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -1193,6 +1193,17 @@
 	if (!node)
 		return -ENOMEM;
 
+	/*
+	 * To avoid an integer overflow for the later size computations, we
+	 * enforce a maximum number of submitted commands here. This limit is
+	 * sufficient for all conceivable usage cases of the G2D.
+	 */
+	if (req->cmd_nr > G2D_CMDLIST_DATA_NUM ||
+	    req->cmd_buf_nr > G2D_CMDLIST_DATA_NUM) {
+		dev_err(dev, "number of submitted G2D commands exceeds limit\n");
+		return -EINVAL;
+	}
+
 	node->event = NULL;
 
 	if (req->event_type != G2D_EVENT_NOT) {
@@ -1250,7 +1261,11 @@
 		cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF;
 	}
 
-	/* Check size of cmdlist: last 2 is about G2D_BITBLT_START */
+	/*
+	 * Check the size of cmdlist. The 2 that is added last comes from
+	 * the implicit G2D_BITBLT_START that is appended once we have
+	 * checked all the submitted commands.
+	 */
 	size = cmdlist->last + req->cmd_nr * 2 + req->cmd_buf_nr * 2 + 2;
 	if (size > G2D_CMDLIST_DATA_NUM) {
 		dev_err(dev, "cmdlist size is too big\n");
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
index 3194e54..faacc81 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
@@ -89,9 +89,13 @@
 		goto err_node_put;
 	}
 
-	of_node_put(np);
-	clk_prepare_enable(tcon->ipg_clk);
+	ret = clk_prepare_enable(tcon->ipg_clk);
+	if (ret) {
+		dev_err(dev, "Couldn't enable the TCON clock\n");
+		goto err_node_put;
+	}
 
+	of_node_put(np);
 	dev_info(dev, "Using TCON in bypass mode\n");
 
 	return tcon;
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 4ac36e3..80c5cc5 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -1152,6 +1152,13 @@
 	is_hdmi = is_dvi && (child->common.device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0;
 	is_edp = is_dp && (child->common.device_type & DEVICE_TYPE_INTERNAL_CONNECTOR);
 
+	if (port == PORT_A && is_dvi) {
+		DRM_DEBUG_KMS("VBT claims port A supports DVI%s, ignoring\n",
+			      is_hdmi ? "/HDMI" : "");
+		is_dvi = false;
+		is_hdmi = false;
+	}
+
 	info->supports_dvi = is_dvi;
 	info->supports_hdmi = is_hdmi;
 	info->supports_dp = is_dp;
@@ -1212,7 +1219,7 @@
 {
 	enum port port;
 
-	if (!HAS_DDI(dev_priv))
+	if (!HAS_DDI(dev_priv) && !IS_CHERRYVIEW(dev_priv))
 		return;
 
 	if (!dev_priv->vbt.child_dev_num)
diff --git a/drivers/gpu/drm/i915/intel_color.c b/drivers/gpu/drm/i915/intel_color.c
index 95a7277..89a7774 100644
--- a/drivers/gpu/drm/i915/intel_color.c
+++ b/drivers/gpu/drm/i915/intel_color.c
@@ -394,6 +394,7 @@
 		}
 
 		/* Program the max register to clamp values > 1.0. */
+		i = lut_size - 1;
 		I915_WRITE(PREC_PAL_GC_MAX(pipe, 0),
 			   drm_color_lut_extract(lut[i].red, 16));
 		I915_WRITE(PREC_PAL_GC_MAX(pipe, 1),
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f8efd20..ce32303 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11471,13 +11471,10 @@
 {
 	struct drm_i915_private *dev_priv = to_i915(dev);
 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-	enum transcoder cpu_transcoder = intel_crtc->config->cpu_transcoder;
+	enum transcoder cpu_transcoder;
 	struct drm_display_mode *mode;
 	struct intel_crtc_state *pipe_config;
-	int htot = I915_READ(HTOTAL(cpu_transcoder));
-	int hsync = I915_READ(HSYNC(cpu_transcoder));
-	int vtot = I915_READ(VTOTAL(cpu_transcoder));
-	int vsync = I915_READ(VSYNC(cpu_transcoder));
+	u32 htot, hsync, vtot, vsync;
 	enum pipe pipe = intel_crtc->pipe;
 
 	mode = kzalloc(sizeof(*mode), GFP_KERNEL);
@@ -11505,6 +11502,13 @@
 	i9xx_crtc_clock_get(intel_crtc, pipe_config);
 
 	mode->clock = pipe_config->port_clock / pipe_config->pixel_multiplier;
+
+	cpu_transcoder = pipe_config->cpu_transcoder;
+	htot = I915_READ(HTOTAL(cpu_transcoder));
+	hsync = I915_READ(HSYNC(cpu_transcoder));
+	vtot = I915_READ(VTOTAL(cpu_transcoder));
+	vsync = I915_READ(VSYNC(cpu_transcoder));
+
 	mode->hdisplay = (htot & 0xffff) + 1;
 	mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
 	mode->hsync_start = (hsync & 0xffff) + 1;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 7b06280..7fdc42e 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2193,8 +2193,8 @@
 	I915_WRITE(pp_ctrl_reg, pp);
 	POSTING_READ(pp_ctrl_reg);
 
-	intel_dp->panel_power_off_time = ktime_get_boottime();
 	wait_panel_off(intel_dp);
+	intel_dp->panel_power_off_time = ktime_get_boottime();
 
 	/* We got a reference when we enabled the VDD. */
 	power_domain = intel_display_port_aux_power_domain(intel_encoder);
@@ -3558,9 +3558,16 @@
 			      dev_priv->psr.psr2_support ? "supported" : "not supported");
 	}
 
-	/* Read the eDP Display control capabilities registers */
-	if ((intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] & DP_DPCD_DISPLAY_CONTROL_CAPABLE) &&
-	    drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
+	/*
+	 * Read the eDP display control registers.
+	 *
+	 * Do this independent of DP_DPCD_DISPLAY_CONTROL_CAPABLE bit in
+	 * DP_EDP_CONFIGURATION_CAP, because some buggy displays do not have it
+	 * set, but require eDP 1.4+ detection (e.g. for supported link rates
+	 * method). The display control registers should read zero if they're
+	 * not supported anyway.
+	 */
+	if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_DPCD_REV,
 			     intel_dp->edp_dpcd, sizeof(intel_dp->edp_dpcd)) ==
 			     sizeof(intel_dp->edp_dpcd))
 		DRM_DEBUG_KMS("EDP DPCD : %*ph\n", (int) sizeof(intel_dp->edp_dpcd),
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a19ec06..3ce9ba3 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -457,7 +457,6 @@
 
 struct intel_pipe_wm {
 	struct intel_wm_level wm[5];
-	struct intel_wm_level raw_wm[5];
 	uint32_t linetime;
 	bool fbc_wm_enabled;
 	bool pipe_enabled;
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index a2655cd..8ab6f30 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -272,8 +272,30 @@
 	return intel_overlay_do_wait_request(overlay, req, NULL);
 }
 
+static void intel_overlay_flip_prepare(struct intel_overlay *overlay,
+				       struct i915_vma *vma)
+{
+	enum pipe pipe = overlay->crtc->pipe;
+
+	WARN_ON(overlay->old_vma);
+
+	i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL,
+			  vma ? vma->obj : NULL,
+			  INTEL_FRONTBUFFER_OVERLAY(pipe));
+
+	intel_frontbuffer_flip_prepare(overlay->i915,
+				       INTEL_FRONTBUFFER_OVERLAY(pipe));
+
+	overlay->old_vma = overlay->vma;
+	if (vma)
+		overlay->vma = i915_vma_get(vma);
+	else
+		overlay->vma = NULL;
+}
+
 /* overlay needs to be enabled in OCMD reg */
 static int intel_overlay_continue(struct intel_overlay *overlay,
+				  struct i915_vma *vma,
 				  bool load_polyphase_filter)
 {
 	struct drm_i915_private *dev_priv = overlay->i915;
@@ -308,27 +330,35 @@
 	intel_ring_emit(ring, flip_addr);
 	intel_ring_advance(ring);
 
+	intel_overlay_flip_prepare(overlay, vma);
+
 	intel_overlay_submit_request(overlay, req, NULL);
 
 	return 0;
 }
 
+static void intel_overlay_release_old_vma(struct intel_overlay *overlay)
+{
+	struct i915_vma *vma;
+
+	vma = fetch_and_zero(&overlay->old_vma);
+	if (WARN_ON(!vma))
+		return;
+
+	intel_frontbuffer_flip_complete(overlay->i915,
+					INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
+
+	i915_gem_object_unpin_from_display_plane(vma);
+	i915_vma_put(vma);
+}
+
 static void intel_overlay_release_old_vid_tail(struct i915_gem_active *active,
 					       struct drm_i915_gem_request *req)
 {
 	struct intel_overlay *overlay =
 		container_of(active, typeof(*overlay), last_flip);
-	struct i915_vma *vma;
 
-	vma = fetch_and_zero(&overlay->old_vma);
-	if (WARN_ON(!vma))
-		return;
-
-	i915_gem_track_fb(vma->obj, NULL,
-			  INTEL_FRONTBUFFER_OVERLAY(overlay->crtc->pipe));
-
-	i915_gem_object_unpin_from_display_plane(vma);
-	i915_vma_put(vma);
+	intel_overlay_release_old_vma(overlay);
 }
 
 static void intel_overlay_off_tail(struct i915_gem_active *active,
@@ -336,15 +366,8 @@
 {
 	struct intel_overlay *overlay =
 		container_of(active, typeof(*overlay), last_flip);
-	struct i915_vma *vma;
 
-	/* never have the overlay hw on without showing a frame */
-	vma = fetch_and_zero(&overlay->vma);
-	if (WARN_ON(!vma))
-		return;
-
-	i915_gem_object_unpin_from_display_plane(vma);
-	i915_vma_put(vma);
+	intel_overlay_release_old_vma(overlay);
 
 	overlay->crtc->overlay = NULL;
 	overlay->crtc = NULL;
@@ -398,6 +421,8 @@
 	}
 	intel_ring_advance(ring);
 
+	intel_overlay_flip_prepare(overlay, NULL);
+
 	return intel_overlay_do_wait_request(overlay, req,
 					     intel_overlay_off_tail);
 }
@@ -836,18 +861,10 @@
 
 	intel_overlay_unmap_regs(overlay, regs);
 
-	ret = intel_overlay_continue(overlay, scale_changed);
+	ret = intel_overlay_continue(overlay, vma, scale_changed);
 	if (ret)
 		goto out_unpin;
 
-	i915_gem_track_fb(overlay->vma ? overlay->vma->obj : NULL,
-			  vma->obj, INTEL_FRONTBUFFER_OVERLAY(pipe));
-
-	overlay->old_vma = overlay->vma;
-	overlay->vma = vma;
-
-	intel_frontbuffer_flip(dev_priv, INTEL_FRONTBUFFER_OVERLAY(pipe));
-
 	return 0;
 
 out_unpin:
@@ -1215,6 +1232,7 @@
 
 	mutex_unlock(&dev->struct_mutex);
 	drm_modeset_unlock_all(dev);
+	i915_gem_object_put(new_bo);
 
 	kfree(params);
 
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 49de476..277a802 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -27,6 +27,7 @@
 
 #include <linux/cpufreq.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_atomic_helper.h>
 #include "i915_drv.h"
 #include "intel_drv.h"
 #include "../../../platform/x86/intel_ips.h"
@@ -2017,9 +2018,9 @@
 				 const struct intel_crtc *intel_crtc,
 				 int level,
 				 struct intel_crtc_state *cstate,
-				 struct intel_plane_state *pristate,
-				 struct intel_plane_state *sprstate,
-				 struct intel_plane_state *curstate,
+				 const struct intel_plane_state *pristate,
+				 const struct intel_plane_state *sprstate,
+				 const struct intel_plane_state *curstate,
 				 struct intel_wm_level *result)
 {
 	uint16_t pri_latency = dev_priv->wm.pri_latency[level];
@@ -2341,28 +2342,24 @@
 	struct intel_pipe_wm *pipe_wm;
 	struct drm_device *dev = state->dev;
 	const struct drm_i915_private *dev_priv = to_i915(dev);
-	struct intel_plane *intel_plane;
-	struct intel_plane_state *pristate = NULL;
-	struct intel_plane_state *sprstate = NULL;
-	struct intel_plane_state *curstate = NULL;
+	struct drm_plane *plane;
+	const struct drm_plane_state *plane_state;
+	const struct intel_plane_state *pristate = NULL;
+	const struct intel_plane_state *sprstate = NULL;
+	const struct intel_plane_state *curstate = NULL;
 	int level, max_level = ilk_wm_max_level(dev), usable_level;
 	struct ilk_wm_maximums max;
 
 	pipe_wm = &cstate->wm.ilk.optimal;
 
-	for_each_intel_plane_on_crtc(dev, intel_crtc, intel_plane) {
-		struct intel_plane_state *ps;
+	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, &cstate->base) {
+		const struct intel_plane_state *ps = to_intel_plane_state(plane_state);
 
-		ps = intel_atomic_get_existing_plane_state(state,
-							   intel_plane);
-		if (!ps)
-			continue;
-
-		if (intel_plane->base.type == DRM_PLANE_TYPE_PRIMARY)
+		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
 			pristate = ps;
-		else if (intel_plane->base.type == DRM_PLANE_TYPE_OVERLAY)
+		else if (plane->type == DRM_PLANE_TYPE_OVERLAY)
 			sprstate = ps;
-		else if (intel_plane->base.type == DRM_PLANE_TYPE_CURSOR)
+		else if (plane->type == DRM_PLANE_TYPE_CURSOR)
 			curstate = ps;
 	}
 
@@ -2384,11 +2381,9 @@
 	if (pipe_wm->sprites_scaled)
 		usable_level = 0;
 
-	ilk_compute_wm_level(dev_priv, intel_crtc, 0, cstate,
-			     pristate, sprstate, curstate, &pipe_wm->raw_wm[0]);
-
 	memset(&pipe_wm->wm, 0, sizeof(pipe_wm->wm));
-	pipe_wm->wm[0] = pipe_wm->raw_wm[0];
+	ilk_compute_wm_level(dev_priv, intel_crtc, 0, cstate,
+			     pristate, sprstate, curstate, &pipe_wm->wm[0]);
 
 	if (IS_HASWELL(dev) || IS_BROADWELL(dev))
 		pipe_wm->linetime = hsw_compute_linetime_wm(cstate);
@@ -2398,8 +2393,8 @@
 
 	ilk_compute_wm_reg_maximums(dev, 1, &max);
 
-	for (level = 1; level <= max_level; level++) {
-		struct intel_wm_level *wm = &pipe_wm->raw_wm[level];
+	for (level = 1; level <= usable_level; level++) {
+		struct intel_wm_level *wm = &pipe_wm->wm[level];
 
 		ilk_compute_wm_level(dev_priv, intel_crtc, level, cstate,
 				     pristate, sprstate, curstate, wm);
@@ -2409,13 +2404,10 @@
 		 * register maximums since such watermarks are
 		 * always invalid.
 		 */
-		if (level > usable_level)
-			continue;
-
-		if (ilk_validate_wm_level(level, &max, wm))
-			pipe_wm->wm[level] = *wm;
-		else
-			usable_level = level;
+		if (!ilk_validate_wm_level(level, &max, wm)) {
+			memset(wm, 0, sizeof(*wm));
+			break;
+		}
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
index 9b307ce..dff4784 100644
--- a/drivers/gpu/drm/i915/intel_psr.c
+++ b/drivers/gpu/drm/i915/intel_psr.c
@@ -387,6 +387,13 @@
 		return false;
 	}
 
+	/* PSR2 is restricted to work with panel resolutions upto 3200x2000 */
+	if (intel_crtc->config->pipe_src_w > 3200 ||
+				intel_crtc->config->pipe_src_h > 2000) {
+		dev_priv->psr.psr2_support = false;
+		return false;
+	}
+
 	dev_priv->psr.source_ok = true;
 	return true;
 }
@@ -425,7 +432,6 @@
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = intel_dig_port->base.base.dev;
 	struct drm_i915_private *dev_priv = to_i915(dev);
-	struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
 
 	if (!HAS_PSR(dev)) {
 		DRM_DEBUG_KMS("PSR not supported on this platform\n");
@@ -452,12 +458,7 @@
 		hsw_psr_setup_vsc(intel_dp);
 
 		if (dev_priv->psr.psr2_support) {
-			/* PSR2 is restricted to work with panel resolutions upto 3200x2000 */
-			if (crtc->config->pipe_src_w > 3200 ||
-				crtc->config->pipe_src_h > 2000)
-				dev_priv->psr.psr2_support = false;
-			else
-				skl_psr_setup_su_vsc(intel_dp);
+			skl_psr_setup_su_vsc(intel_dp);
 		}
 
 		/*
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4015832..c81832a 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -161,7 +161,8 @@
 	sde/sde_hw_color_processing_v1_7.o \
 	sde/sde_reg_dma.o \
 	sde/sde_hw_reg_dma_v1.o \
-	sde/sde_hw_dsc.o
+	sde/sde_hw_dsc.o \
+	sde/sde_hw_ds.o
 
 msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
 	sde/sde_encoder_phys_wb.o
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c
index c263115..51cb6c5 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.c
+++ b/drivers/gpu/drm/msm/dp/dp_audio.c
@@ -23,13 +23,6 @@
 #include "dp_audio.h"
 #include "dp_panel.h"
 
-#define HEADER_BYTE_2_BIT	 0
-#define PARITY_BYTE_2_BIT	 8
-#define HEADER_BYTE_1_BIT	16
-#define PARITY_BYTE_1_BIT	24
-#define HEADER_BYTE_3_BIT	16
-#define PARITY_BYTE_3_BIT	24
-
 struct dp_audio_private {
 	struct platform_device *ext_pdev;
 	struct platform_device *pdev;
@@ -44,75 +37,12 @@
 	u32 channels;
 
 	struct completion hpd_comp;
+	struct workqueue_struct *notify_workqueue;
+	struct delayed_work notify_delayed_work;
 
 	struct dp_audio dp_audio;
 };
 
-static u8 dp_audio_get_g0_value(u8 data)
-{
-	u8 c[4];
-	u8 g[4];
-	u8 ret_data = 0;
-	u8 i;
-
-	for (i = 0; i < 4; i++)
-		c[i] = (data >> i) & 0x01;
-
-	g[0] = c[3];
-	g[1] = c[0] ^ c[3];
-	g[2] = c[1];
-	g[3] = c[2];
-
-	for (i = 0; i < 4; i++)
-		ret_data = ((g[i] & 0x01) << i) | ret_data;
-
-	return ret_data;
-}
-
-static u8 dp_audio_get_g1_value(u8 data)
-{
-	u8 c[4];
-	u8 g[4];
-	u8 ret_data = 0;
-	u8 i;
-
-	for (i = 0; i < 4; i++)
-		c[i] = (data >> i) & 0x01;
-
-	g[0] = c[0] ^ c[3];
-	g[1] = c[0] ^ c[1] ^ c[3];
-	g[2] = c[1] ^ c[2];
-	g[3] = c[2] ^ c[3];
-
-	for (i = 0; i < 4; i++)
-		ret_data = ((g[i] & 0x01) << i) | ret_data;
-
-	return ret_data;
-}
-
-static u8 dp_audio_calculate_parity(u32 data)
-{
-	u8 x0 = 0;
-	u8 x1 = 0;
-	u8 ci = 0;
-	u8 iData = 0;
-	u8 i = 0;
-	u8 parity_byte;
-	u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2;
-
-	for (i = 0; i < num_byte; i++) {
-		iData = (data >> i*4) & 0xF;
-
-		ci = iData ^ x1;
-		x1 = x0 ^ dp_audio_get_g1_value(ci);
-		x0 = dp_audio_get_g0_value(ci);
-	}
-
-	parity_byte = x1 | (x0 << 4);
-
-	return parity_byte;
-}
-
 static u32 dp_audio_get_header(struct dp_catalog_audio *catalog,
 		enum dp_catalog_audio_sdp_type sdp,
 		enum dp_catalog_audio_header_type header)
@@ -146,7 +76,7 @@
 			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1);
 
 	new_value = 0x02;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_1_BIT)
 			| (parity_byte << PARITY_BYTE_1_BIT));
 	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -158,7 +88,7 @@
 	value = dp_audio_get_header(catalog,
 			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2);
 	new_value = value;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_2_BIT)
 			| (parity_byte << PARITY_BYTE_2_BIT));
 	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -172,7 +102,7 @@
 			DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3);
 
 	new_value = audio->channels - 1;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_3_BIT)
 			| (parity_byte << PARITY_BYTE_3_BIT));
 	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -193,7 +123,7 @@
 			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1);
 
 	new_value = 0x1;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_1_BIT)
 			| (parity_byte << PARITY_BYTE_1_BIT));
 	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -206,7 +136,7 @@
 			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2);
 
 	new_value = 0x17;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_2_BIT)
 			| (parity_byte << PARITY_BYTE_2_BIT));
 	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -219,7 +149,7 @@
 			DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3);
 
 	new_value = (0x0 | (0x11 << 2));
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_3_BIT)
 			| (parity_byte << PARITY_BYTE_3_BIT));
 	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -239,7 +169,7 @@
 			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1);
 
 	new_value = 0x84;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_1_BIT)
 			| (parity_byte << PARITY_BYTE_1_BIT));
 	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -252,7 +182,7 @@
 			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2);
 
 	new_value = 0x1b;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_2_BIT)
 			| (parity_byte << PARITY_BYTE_2_BIT));
 	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -265,7 +195,7 @@
 			DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3);
 
 	new_value = (0x0 | (0x11 << 2));
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_3_BIT)
 			| (parity_byte << PARITY_BYTE_3_BIT));
 	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -285,7 +215,7 @@
 			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1);
 
 	new_value = 0x05;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_1_BIT)
 			| (parity_byte << PARITY_BYTE_1_BIT));
 	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -298,7 +228,7 @@
 			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2);
 
 	new_value = 0x0F;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_2_BIT)
 			| (parity_byte << PARITY_BYTE_2_BIT));
 	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -311,7 +241,7 @@
 			DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3);
 
 	new_value = 0x0;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_3_BIT)
 			| (parity_byte << PARITY_BYTE_3_BIT));
 	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
@@ -331,7 +261,7 @@
 			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1);
 
 	new_value = 0x06;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_1_BIT)
 			| (parity_byte << PARITY_BYTE_1_BIT));
 	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
@@ -344,7 +274,7 @@
 			DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2);
 
 	new_value = 0x0F;
-	parity_byte = dp_audio_calculate_parity(new_value);
+	parity_byte = dp_header_get_parity(new_value);
 	value |= ((new_value << HEADER_BYTE_2_BIT)
 			| (parity_byte << PARITY_BYTE_2_BIT));
 	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
@@ -379,7 +309,7 @@
 	case DP_LINK_BW_5_4:
 		select = 2;
 		break;
-	case DP_LINK_RATE_810:
+	case DP_LINK_BW_8_1:
 		select = 3;
 		break;
 	default:
@@ -428,7 +358,7 @@
 	audio->engine_on = enable;
 }
 
-static struct dp_audio_private *get_audio_get_data(struct platform_device *pdev)
+static struct dp_audio_private *dp_audio_get_data(struct platform_device *pdev)
 {
 	struct msm_ext_disp_data *ext_data;
 	struct dp_audio *dp_audio;
@@ -459,18 +389,22 @@
 	int rc = 0;
 	struct dp_audio_private *audio;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio)) {
 		rc = PTR_ERR(audio);
 		goto end;
 	}
 
+	mutex_lock(&audio->dp_audio.ops_lock);
+
 	audio->channels = params->num_of_channels;
 
 	dp_audio_setup_sdp(audio);
 	dp_audio_setup_acr(audio);
 	dp_audio_safe_to_exit_level(audio);
 	dp_audio_enable(audio, true);
+
+	mutex_unlock(&audio->dp_audio.ops_lock);
 end:
 	return rc;
 }
@@ -482,7 +416,7 @@
 	struct dp_audio_private *audio;
 	struct sde_edid_ctrl *edid;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio)) {
 		rc = PTR_ERR(audio);
 		goto end;
@@ -510,18 +444,12 @@
 	int rc = 0;
 	struct dp_audio_private *audio;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio)) {
 		rc = PTR_ERR(audio);
 		goto end;
 	}
 
-	if (!audio->panel) {
-		pr_err("invalid panel data\n");
-		rc = -EINVAL;
-		goto end;
-	}
-
 	return audio->session_on;
 end:
 	return rc;
@@ -532,7 +460,7 @@
 	int rc = 0;
 	struct dp_audio_private *audio;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio)) {
 		rc = PTR_ERR(audio);
 		goto end;
@@ -547,16 +475,13 @@
 {
 	struct dp_audio_private *audio;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio))
 		return;
 
-	if (!audio->panel) {
-		pr_err("invalid panel data\n");
-		return;
-	}
-
+	mutex_lock(&audio->dp_audio.ops_lock);
 	dp_audio_enable(audio, false);
+	mutex_unlock(&audio->dp_audio.ops_lock);
 
 	complete_all(&audio->hpd_comp);
 
@@ -568,7 +493,7 @@
 	int rc = 0, ack_hpd;
 	struct dp_audio_private *audio;
 
-	audio = get_audio_get_data(pdev);
+	audio = dp_audio_get_data(pdev);
 	if (IS_ERR(audio)) {
 		rc = PTR_ERR(audio);
 		goto end;
@@ -596,6 +521,24 @@
 	return rc;
 }
 
+static int dp_audio_codec_ready(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct dp_audio_private *audio;
+
+	audio = dp_audio_get_data(pdev);
+	if (IS_ERR(audio)) {
+		pr_err("invalid input\n");
+		rc = PTR_ERR(audio);
+		goto end;
+	}
+
+	queue_delayed_work(audio->notify_workqueue,
+			&audio->notify_delayed_work, HZ/4);
+end:
+	return rc;
+}
+
 static int dp_audio_init_ext_disp(struct dp_audio_private *audio)
 {
 	int rc = 0;
@@ -617,6 +560,7 @@
 	ops->get_intf_id        = dp_audio_get_intf_id;
 	ops->teardown_done      = dp_audio_teardown_done;
 	ops->acknowledge        = dp_audio_ack_done;
+	ops->ready              = dp_audio_codec_ready;
 
 	if (!audio->pdev->dev.of_node) {
 		pr_err("cannot find audio dev.of_node\n");
@@ -648,6 +592,31 @@
 	return rc;
 }
 
+static int dp_audio_notify(struct dp_audio_private *audio, u32 state)
+{
+	int rc = 0;
+	struct msm_ext_disp_init_data *ext = &audio->ext_audio_data;
+
+	rc = ext->intf_ops.audio_notify(audio->ext_pdev,
+			EXT_DISPLAY_TYPE_DP, state);
+	if (rc) {
+		pr_err("failed to notify audio. state=%d err=%d\n", state, rc);
+		goto end;
+	}
+
+	reinit_completion(&audio->hpd_comp);
+	rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 5);
+	if (!rc) {
+		pr_err("timeout. state=%d err=%d\n", state, rc);
+		rc = -ETIMEDOUT;
+		goto end;
+	}
+
+	pr_debug("success\n");
+end:
+	return rc;
+}
+
 static int dp_audio_on(struct dp_audio *dp_audio)
 {
 	int rc = 0;
@@ -656,11 +625,14 @@
 
 	if (!dp_audio) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto end;
+		return -EINVAL;
 	}
 
 	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
+	if (IS_ERR(audio)) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
 
 	ext = &audio->ext_audio_data;
 
@@ -674,21 +646,9 @@
 		goto end;
 	}
 
-	rc = ext->intf_ops.audio_notify(audio->ext_pdev,
-			EXT_DISPLAY_TYPE_DP,
-			EXT_DISPLAY_CABLE_CONNECT);
-	if (rc) {
-		pr_err("failed to notify audio, err=%d\n", rc);
+	rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
+	if (rc)
 		goto end;
-	}
-
-	reinit_completion(&audio->hpd_comp);
-	rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 5);
-	if (!rc) {
-		pr_err("timeout\n");
-		rc = -ETIMEDOUT;
-		goto end;
-	}
 
 	pr_debug("success\n");
 end:
@@ -700,6 +660,7 @@
 	int rc = 0;
 	struct dp_audio_private *audio;
 	struct msm_ext_disp_init_data *ext;
+	bool work_pending = false;
 
 	if (!dp_audio) {
 		pr_err("invalid input\n");
@@ -709,21 +670,13 @@
 	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
 	ext = &audio->ext_audio_data;
 
-	rc = ext->intf_ops.audio_notify(audio->ext_pdev,
-			EXT_DISPLAY_TYPE_DP,
-			EXT_DISPLAY_CABLE_DISCONNECT);
-	if (rc) {
-		pr_err("failed to notify audio, err=%d\n", rc);
-		goto end;
-	}
+	work_pending = cancel_delayed_work_sync(&audio->notify_delayed_work);
+	if (work_pending)
+		pr_debug("pending notification work completed\n");
 
-	reinit_completion(&audio->hpd_comp);
-	rc = wait_for_completion_timeout(&audio->hpd_comp, HZ * 5);
-	if (!rc) {
-		pr_err("timeout\n");
-		rc = -ETIMEDOUT;
+	rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT);
+	if (rc)
 		goto end;
-	}
 
 	pr_debug("success\n");
 end:
@@ -739,6 +692,35 @@
 	return rc;
 }
 
+static void dp_audio_notify_work_fn(struct work_struct *work)
+{
+	struct dp_audio_private *audio;
+	struct delayed_work *dw = to_delayed_work(work);
+
+	audio = container_of(dw, struct dp_audio_private, notify_delayed_work);
+
+	dp_audio_notify(audio, EXT_DISPLAY_CABLE_CONNECT);
+}
+
+static int dp_audio_create_notify_workqueue(struct dp_audio_private *audio)
+{
+	audio->notify_workqueue = create_workqueue("sdm_dp_audio_notify");
+	if (IS_ERR_OR_NULL(audio->notify_workqueue)) {
+		pr_err("Error creating notify_workqueue\n");
+		return -EPERM;
+	}
+
+	INIT_DELAYED_WORK(&audio->notify_delayed_work, dp_audio_notify_work_fn);
+
+	return 0;
+}
+
+static void dp_audio_destroy_notify_workqueue(struct dp_audio_private *audio)
+{
+	if (audio->notify_workqueue)
+		destroy_workqueue(audio->notify_workqueue);
+}
+
 struct dp_audio *dp_audio_get(struct platform_device *pdev,
 			struct dp_panel *panel,
 			struct dp_catalog_audio *catalog)
@@ -759,6 +741,10 @@
 		goto error;
 	}
 
+	rc = dp_audio_create_notify_workqueue(audio);
+	if (rc)
+		goto error_notify_workqueue;
+
 	init_completion(&audio->hpd_comp);
 
 	audio->pdev = pdev;
@@ -767,16 +753,23 @@
 
 	dp_audio = &audio->dp_audio;
 
+	mutex_init(&dp_audio->ops_lock);
+
 	dp_audio->on  = dp_audio_on;
 	dp_audio->off = dp_audio_off;
 
 	rc = dp_audio_init_ext_disp(audio);
-	if (rc)
-		goto error;
+	if (rc) {
+		goto error_ext_disp;
+	}
 
 	catalog->init(catalog);
 
 	return dp_audio;
+error_ext_disp:
+	dp_audio_destroy_notify_workqueue(audio);
+error_notify_workqueue:
+	devm_kfree(&pdev->dev, audio);
 error:
 	return ERR_PTR(rc);
 }
@@ -789,6 +782,9 @@
 		return;
 
 	audio = container_of(dp_audio, struct dp_audio_private, dp_audio);
+	mutex_destroy(&dp_audio->ops_lock);
 
-	kzfree(audio);
+	dp_audio_destroy_notify_workqueue(audio);
+
+	devm_kfree(&audio->pdev->dev, audio);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.h b/drivers/gpu/drm/msm/dp/dp_audio.h
index d6e6b74..807444b 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.h
+++ b/drivers/gpu/drm/msm/dp/dp_audio.h
@@ -29,6 +29,8 @@
 	u32 lane_count;
 	u32 bw_code;
 
+	struct mutex ops_lock;
+
 	/**
 	 * on()
 	 *
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c
index 9106027..2d76d13 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.c
+++ b/drivers/gpu/drm/msm/dp/dp_aux.c
@@ -38,6 +38,11 @@
 	bool cmd_busy;
 	bool native;
 	bool read;
+	bool no_send_addr;
+	bool no_send_stop;
+	u32 offset;
+	u32 segment;
+	atomic_t aborted;
 
 	struct drm_dp_aux drm_aux;
 };
@@ -102,9 +107,19 @@
 		aux->catalog->write_data(aux->catalog);
 	}
 
+	aux->catalog->clear_trans(aux->catalog, false);
+	aux->catalog->clear_hw_interrupts(aux->catalog);
+
 	reg = 0; /* Transaction number == 1 */
-	if (!aux->native) /* i2c */
-		reg |= (BIT(8) | BIT(10) | BIT(11));
+	if (!aux->native) { /* i2c */
+		reg |= BIT(8);
+
+		if (aux->no_send_addr)
+			reg |= BIT(10);
+
+		if (aux->no_send_stop)
+			reg |= BIT(11);
+	}
 
 	reg |= BIT(9);
 	aux->catalog->data = reg;
@@ -150,9 +165,11 @@
 {
 	u32 data;
 	u8 *dp;
-	u32 i;
+	u32 i, actual_i;
 	u32 len = msg->size;
 
+	aux->catalog->clear_trans(aux->catalog, true);
+
 	data = 0;
 	data |= DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
 	data |= BIT(0);  /* read */
@@ -168,6 +185,11 @@
 	for (i = 0; i < len; i++) {
 		data = aux->catalog->read_data(aux->catalog);
 		*dp++ = (u8)((data >> 8) & 0xff);
+
+		actual_i = (data >> 16) & 0xFF;
+		if (i != actual_i)
+			pr_warn("Index mismatch: expected %d, found %d\n",
+				i, actual_i);
 	}
 }
 
@@ -183,6 +205,10 @@
 		aux->aux_error_num = DP_AUX_ERR_TOUT;
 	if (isr & DP_INTR_NACK_DEFER)
 		aux->aux_error_num = DP_AUX_ERR_NACK;
+	if (isr & DP_INTR_AUX_ERROR) {
+		aux->aux_error_num = DP_AUX_ERR_PHY;
+		aux->catalog->clear_hw_interrupts(aux->catalog);
+	}
 
 	complete(&aux->comp);
 }
@@ -207,6 +233,10 @@
 			aux->aux_error_num = DP_AUX_ERR_NACK;
 		if (isr & DP_INTR_I2C_DEFER)
 			aux->aux_error_num = DP_AUX_ERR_DEFER;
+		if (isr & DP_INTR_AUX_ERROR) {
+			aux->aux_error_num = DP_AUX_ERR_PHY;
+			aux->catalog->clear_hw_interrupts(aux->catalog);
+		}
 	}
 
 	complete(&aux->comp);
@@ -250,6 +280,103 @@
 	aux->catalog->reset(aux->catalog);
 }
 
+static void dp_aux_abort_transaction(struct dp_aux *dp_aux)
+{
+	struct dp_aux_private *aux;
+
+	if (!dp_aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
+
+	atomic_set(&aux->aborted, 1);
+}
+
+static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *input_msg)
+{
+	u32 const edid_address = 0x50;
+	u32 const segment_address = 0x30;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	u8 *data = NULL;
+
+	if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
+		(input_msg->address != segment_address)))
+		return;
+
+
+	data = input_msg->buffer;
+	if (input_msg->address == segment_address)
+		aux->segment = *data;
+	else
+		aux->offset = *data;
+}
+
+/**
+ * dp_aux_transfer_helper() - helper function for EDID read transactions
+ *
+ * @aux: DP AUX private structure
+ * @input_msg: input message from DRM upstream APIs
+ *
+ * return: void
+ *
+ * This helper function is used to fix EDID reads for non-compliant
+ * sinks that do not handle the i2c middle-of-transaction flag correctly.
+ */
+static void dp_aux_transfer_helper(struct dp_aux_private *aux,
+		struct drm_dp_aux_msg *input_msg)
+{
+	struct drm_dp_aux_msg helper_msg;
+	u32 const message_size = 0x10;
+	u32 const segment_address = 0x30;
+	bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
+	bool i2c_read = input_msg->request &
+		(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+	if (!i2c_mot || !i2c_read || (input_msg->size == 0))
+		return;
+
+	aux->read = false;
+	aux->cmd_busy = true;
+	aux->no_send_addr = true;
+	aux->no_send_stop = true;
+
+	/*
+	 * Send the segment address for i2c reads for segment > 0 and for which
+	 * the middle-of-transaction flag is set. This is required to support
+	 * EDID reads of more than 2 blocks as the segment address is reset to 0
+	 * since we are overriding the middle-of-transaction flag for read
+	 * transactions.
+	 */
+	if (aux->segment) {
+		memset(&helper_msg, 0, sizeof(helper_msg));
+		helper_msg.address = segment_address;
+		helper_msg.buffer = &aux->segment;
+		helper_msg.size = 1;
+		dp_aux_cmd_fifo_tx(aux, &helper_msg);
+	}
+
+	/*
+	 * Send the offset address for every i2c read in which the
+	 * middle-of-transaction flag is set. This will ensure that the sink
+	 * will update its read pointer and return the correct portion of the
+	 * EDID buffer in the subsequent i2c read trasntion triggered in the
+	 * native AUX transfer function.
+	 */
+	memset(&helper_msg, 0, sizeof(helper_msg));
+	helper_msg.address = input_msg->address;
+	helper_msg.buffer = &aux->offset;
+	helper_msg.size = 1;
+	dp_aux_cmd_fifo_tx(aux, &helper_msg);
+	aux->offset += message_size;
+
+	if (aux->offset == 0x80 || aux->offset == 0x100)
+		aux->segment = 0x0; /* reset segment at end of block */
+}
+
 /*
  * This function does the real job to process an AUX transaction.
  * It will call aux_reset() function to reset the AUX channel,
@@ -267,9 +394,12 @@
 
 	mutex_lock(&aux->mutex);
 
+	if (atomic_read(&aux->aborted)) {
+		ret = -ETIMEDOUT;
+		goto unlock_exit;
+	}
+
 	aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
-	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
-	aux->cmd_busy = true;
 
 	/* Ignore address only message */
 	if ((msg->size == 0) || (msg->buffer == NULL)) {
@@ -288,8 +418,22 @@
 		goto unlock_exit;
 	}
 
+	dp_aux_update_offset_and_segment(aux, msg);
+	dp_aux_transfer_helper(aux, msg);
+
+	aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+	aux->cmd_busy = true;
+
+	if (aux->read) {
+		aux->no_send_addr = true;
+		aux->no_send_stop = false;
+	} else {
+		aux->no_send_addr = true;
+		aux->no_send_stop = true;
+	}
+
 	ret = dp_aux_cmd_fifo_tx(aux, msg);
-	if ((ret < 0) && aux->native) {
+	if ((ret < 0) && aux->native && !atomic_read(&aux->aborted)) {
 		aux->retry_cnt++;
 		if (!(aux->retry_cnt % retry_count))
 			aux->catalog->update_aux_cfg(aux->catalog,
@@ -341,11 +485,12 @@
 
 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
 
-	aux->catalog->reset(aux->catalog);
-	aux->catalog->enable(aux->catalog, true);
-	aux->retry_cnt = 0;
 	dp_aux_reset_phy_config_indices(aux_cfg);
 	aux->catalog->setup(aux->catalog, aux_cfg);
+	aux->catalog->reset(aux->catalog);
+	aux->catalog->enable(aux->catalog, true);
+	atomic_set(&aux->aborted, 0);
+	aux->retry_cnt = 0;
 }
 
 static void dp_aux_deinit(struct dp_aux *dp_aux)
@@ -359,6 +504,7 @@
 
 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
 
+	atomic_set(&aux->aborted, 1);
 	aux->catalog->enable(aux->catalog, false);
 }
 
@@ -436,6 +582,7 @@
 	dp_aux->drm_aux_register = dp_aux_register;
 	dp_aux->drm_aux_deregister = dp_aux_deregister;
 	dp_aux->reconfig = dp_aux_reconfig;
+	dp_aux->abort = dp_aux_abort_transaction;
 
 	return dp_aux;
 error:
@@ -451,5 +598,7 @@
 
 	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
 
+	mutex_destroy(&aux->mutex);
+
 	devm_kfree(aux->dev, aux);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h
index 5d96fd9..e8cb1cc 100644
--- a/drivers/gpu/drm/msm/dp/dp_aux.h
+++ b/drivers/gpu/drm/msm/dp/dp_aux.h
@@ -25,6 +25,7 @@
 	DP_AUX_ERR_NACK	= -3,
 	DP_AUX_ERR_DEFER	= -4,
 	DP_AUX_ERR_NACK_DEFER	= -5,
+	DP_AUX_ERR_PHY	= -6,
 };
 
 struct dp_aux {
@@ -35,6 +36,7 @@
 	void (*init)(struct dp_aux *aux, struct dp_aux_cfg *aux_cfg);
 	void (*deinit)(struct dp_aux *aux);
 	void (*reconfig)(struct dp_aux *aux);
+	void (*abort)(struct dp_aux *aux);
 };
 
 struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 94bd199..c237a23 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -15,10 +15,14 @@
 #define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
 
 #include <linux/delay.h>
+#include <drm/drm_dp_helper.h>
 
 #include "dp_catalog.h"
 #include "dp_reg.h"
 
+#define DP_GET_MSB(x)	(x >> 8)
+#define DP_GET_LSB(x)	(x & 0xff)
+
 #define dp_read(offset) readl_relaxed((offset))
 #define dp_write(offset, data) writel_relaxed((data), (offset))
 
@@ -80,7 +84,7 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	return dp_read(base + DP_AUX_DATA);
 end:
@@ -100,7 +104,7 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	dp_write(base + DP_AUX_DATA, aux->data);
 end:
@@ -120,13 +124,65 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	dp_write(base + DP_AUX_TRANS_CTRL, aux->data);
 end:
 	return rc;
 }
 
+static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
+{
+	int rc = 0;
+	u32 data = 0;
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	dp_catalog_get_priv(aux);
+	base = catalog->io->dp_aux.base;
+
+	if (read) {
+		data = dp_read(base + DP_AUX_TRANS_CTRL);
+		data &= ~BIT(9);
+		dp_write(base + DP_AUX_TRANS_CTRL, data);
+	} else {
+		dp_write(base + DP_AUX_TRANS_CTRL, 0);
+	}
+end:
+	return rc;
+}
+
+static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *phy_base;
+	u32 data = 0;
+
+	if (!aux) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(aux);
+	phy_base = catalog->io->phy_io.base;
+
+	data = dp_read(phy_base + DP_PHY_AUX_INTERRUPT_STATUS);
+	pr_debug("PHY_AUX_INTERRUPT_STATUS=0x%08x\n", data);
+
+	dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
+	wmb(); /* make sure 0x1f is written before next write */
+	dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
+	wmb(); /* make sure 0x9f is written before next write */
+	dp_write(phy_base + DP_PHY_AUX_INTERRUPT_CLEAR, 0);
+	wmb(); /* make sure register is cleared */
+}
+
 static void dp_catalog_aux_reset(struct dp_catalog_aux *aux)
 {
 	u32 aux_ctrl;
@@ -139,7 +195,7 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	aux_ctrl = dp_read(base + DP_AUX_CTRL);
 
@@ -149,6 +205,7 @@
 
 	aux_ctrl &= ~BIT(1);
 	dp_write(base + DP_AUX_CTRL, aux_ctrl);
+	wmb(); /* make sure AUX reset is done here */
 }
 
 static void dp_catalog_aux_enable(struct dp_catalog_aux *aux, bool enable)
@@ -163,19 +220,20 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	aux_ctrl = dp_read(base + DP_AUX_CTRL);
 
 	if (enable) {
+		aux_ctrl |= BIT(0);
+		dp_write(base + DP_AUX_CTRL, aux_ctrl);
+		wmb(); /* make sure AUX module is enabled */
 		dp_write(base + DP_TIMEOUT_COUNT, 0xffff);
 		dp_write(base + DP_AUX_LIMITS, 0xffff);
-		aux_ctrl |= BIT(0);
 	} else {
 		aux_ctrl &= ~BIT(0);
+		dp_write(base + DP_AUX_CTRL, aux_ctrl);
 	}
-
-	dp_write(base + DP_AUX_CTRL, aux_ctrl);
 }
 
 static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
@@ -215,13 +273,12 @@
 
 	dp_catalog_get_priv(aux);
 
-	dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x02);
+	dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x65);
 	wmb(); /* make sure PD programming happened */
-	dp_write(catalog->io->phy_io.base + DP_PHY_PD_CTL, 0x7d);
 
 	/* Turn on BIAS current for PHY/PLL */
 	dp_write(catalog->io->dp_pll_io.base +
-		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
+		QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
 
 	/* DP AUX CFG register programming */
 	for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
@@ -233,13 +290,14 @@
 	}
 
 	dp_write(catalog->io->phy_io.base + DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
+	wmb(); /* make sure AUX configuration is done before enabling it */
 }
 
 static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
 {
 	u32 ack;
 	struct dp_catalog_private *catalog;
-	void __iomem *base;
+	void __iomem *ahb_base;
 
 	if (!aux) {
 		pr_err("invalid input\n");
@@ -247,17 +305,14 @@
 	}
 
 	dp_catalog_get_priv(aux);
-	base = catalog->io->ctrl_io.base;
+	ahb_base = catalog->io->dp_ahb.base;
 
-	if (cmd_busy)
-		dp_write(base + DP_AUX_TRANS_CTRL, 0x0);
-
-	aux->isr = dp_read(base + DP_INTR_STATUS);
+	aux->isr = dp_read(ahb_base + DP_INTR_STATUS);
 	aux->isr &= ~DP_INTR_MASK1;
 	ack = aux->isr & DP_INTERRUPT_STATUS1;
 	ack <<= 1;
 	ack |= DP_INTR_MASK1;
-	dp_write(base + DP_INTR_STATUS, ack);
+	dp_write(ahb_base + DP_INTR_STATUS, ack);
 }
 
 /* controller related catalog functions */
@@ -272,11 +327,277 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_ahb.base;
 
 	return dp_read(base + DP_HDCP_STATUS);
 }
 
+static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	struct drm_msm_ext_hdr_metadata *hdr;
+	void __iomem *base;
+	u32 header, parity, data;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	hdr = &panel->hdr_data.hdr_meta;
+	base = catalog->io->dp_link.base;
+
+	/* HEADER BYTE 1 */
+	header = panel->hdr_data.vscext_header_byte1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(base + MMSS_DP_VSCEXT_0, data);
+	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_VSCEXT_0: 0x%x\n", data);
+
+	/* HEADER BYTE 2 */
+	header = panel->hdr_data.vscext_header_byte2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(base + MMSS_DP_VSCEXT_1, data);
+	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_VSCEXT_1: 0x%x\n", data);
+
+	/* HEADER BYTE 3 */
+	header = panel->hdr_data.vscext_header_byte3;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_3_BIT)
+			| (parity << PARITY_BYTE_3_BIT));
+	data |= dp_read(base + MMSS_DP_VSCEXT_1);
+	dp_write(base + MMSS_DP_VSCEXT_1, data);
+	pr_debug("Header#3: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_VSCEXT_1: 0x%x\n", data);
+
+	data = panel->hdr_data.version;
+	data |= panel->hdr_data.length << 8;
+	data |= hdr->eotf << 16;
+	pr_debug("DP_VSCEXT_2: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_2, data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[0]) |
+		(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
+	pr_debug("DP_VSCEXT_3: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_3, data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[1]) |
+		(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
+	pr_debug("DP_VSCEXT_4: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_4, data);
+
+	data = (DP_GET_LSB(hdr->display_primaries_x[2]) |
+		(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
+		(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
+		(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
+	pr_debug("DP_VSCEXT_5: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_5, data);
+
+	data = (DP_GET_LSB(hdr->white_point_x) |
+		(DP_GET_MSB(hdr->white_point_x) << 8) |
+		(DP_GET_LSB(hdr->white_point_y) << 16) |
+		(DP_GET_MSB(hdr->white_point_y) << 24));
+	pr_debug("DP_VSCEXT_6: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_6, data);
+
+	data = (DP_GET_LSB(hdr->max_luminance) |
+		(DP_GET_MSB(hdr->max_luminance) << 8) |
+		(DP_GET_LSB(hdr->min_luminance) << 16) |
+		(DP_GET_MSB(hdr->min_luminance) << 24));
+	pr_debug("DP_VSCEXT_7: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_7, data);
+
+	data = (DP_GET_LSB(hdr->max_content_light_level) |
+		(DP_GET_MSB(hdr->max_content_light_level) << 8) |
+		(DP_GET_LSB(hdr->max_average_light_level) << 16) |
+		(DP_GET_MSB(hdr->max_average_light_level) << 24));
+	pr_debug("DP_VSCEXT_8: 0x%x\n", data);
+	dp_write(base + MMSS_DP_VSCEXT_8, data);
+
+	dp_write(base + MMSS_DP_VSCEXT_9, 0x00);
+}
+
+static void dp_catalog_panel_setup_ext_sdp(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 header, parity, data;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_link.base;
+
+	/* HEADER BYTE 1 */
+	header = panel->hdr_data.ext_header_byte1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(base + MMSS_DP_EXTENSION_0, data);
+	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_EXTENSION_0: 0x%x\n", data);
+
+	/* HEADER BYTE 2 */
+	header = panel->hdr_data.ext_header_byte2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(base + MMSS_DP_EXTENSION_1, data);
+	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_EXTENSION_1: 0x%x\n", data);
+
+	dp_write(base + MMSS_DP_EXTENSION_1, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_2, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_3, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_4, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_5, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_6, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_7, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_8, 0x5AA55AA5);
+	dp_write(base + MMSS_DP_EXTENSION_9, 0x5AA55AA5);
+}
+
+static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 header, parity, data;
+	u8 bpc;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->ctrl_io.base;
+
+	/* HEADER BYTE 1 */
+	header = panel->hdr_data.vsc_header_byte1;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_1_BIT)
+			| (parity << PARITY_BYTE_1_BIT));
+	dp_write(base + MMSS_DP_GENERIC0_0, data);
+	pr_debug("Header#1: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_GENERIC0_0: 0x%x\n", data);
+
+	/* HEADER BYTE 2 */
+	header = panel->hdr_data.vsc_header_byte2;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_2_BIT)
+			| (parity << PARITY_BYTE_2_BIT));
+	dp_write(base + MMSS_DP_GENERIC0_1, data);
+	pr_debug("Header#2: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_GENERIC0_1: 0x%x\n", data);
+
+	/* HEADER BYTE 3 */
+	header = panel->hdr_data.vsc_header_byte3;
+	parity = dp_header_get_parity(header);
+	data   = ((header << HEADER_BYTE_3_BIT)
+			| (parity << PARITY_BYTE_3_BIT));
+	data |= dp_read(base + MMSS_DP_GENERIC0_1);
+	dp_write(base + MMSS_DP_GENERIC0_1, data);
+	pr_debug("Header#3: 0x%x, parity = 0x%x\n", header, parity);
+	pr_debug("DP_GENERIC0_1: 0x%x\n", data);
+
+	dp_write(base + MMSS_DP_GENERIC0_2, 0x00);
+	dp_write(base + MMSS_DP_GENERIC0_3, 0x00);
+	dp_write(base + MMSS_DP_GENERIC0_4, 0x00);
+	dp_write(base + MMSS_DP_GENERIC0_5, 0x00);
+
+
+	switch (panel->hdr_data.bpc) {
+	default:
+	case 10:
+		bpc = BIT(1);
+		break;
+	case 8:
+		bpc = BIT(0);
+		break;
+	case 6:
+		bpc = 0;
+		break;
+	}
+
+	data = (panel->hdr_data.colorimetry & 0xF) |
+		((panel->hdr_data.pixel_encoding & 0xF) << 4) |
+		(bpc << 8) |
+		((panel->hdr_data.dynamic_range & 0x1) << 15) |
+		((panel->hdr_data.content_type & 0x7) << 16);
+
+	pr_debug("DP_GENERIC0_6: 0x%x\n", data);
+	dp_write(base + MMSS_DP_GENERIC0_6, data);
+	dp_write(base + MMSS_DP_GENERIC0_7, 0x00);
+	dp_write(base + MMSS_DP_GENERIC0_8, 0x00);
+	dp_write(base + MMSS_DP_GENERIC0_9, 0x00);
+}
+
+static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 cfg, cfg2;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_link.base;
+
+	cfg = dp_read(base + MMSS_DP_SDP_CFG);
+	/* EXTENSION_SDP_EN */
+	cfg |= BIT(4);
+
+	/* VSCEXT_SDP_EN */
+	cfg |= BIT(16);
+
+	/* GEN0_SDP_EN */
+	cfg |= BIT(17);
+
+	/* GEN1_SDP_EN */
+	cfg |= BIT(18);
+	dp_write(base + MMSS_DP_SDP_CFG, cfg);
+
+	cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
+	/* EXTN_SDPSIZE */
+	cfg2 |= BIT(15);
+
+	/* GENERIC0_SDPSIZE */
+	cfg |= BIT(16);
+
+	/* GENERIC1_SDPSIZE */
+	cfg |= BIT(17);
+	dp_write(base + MMSS_DP_SDP_CFG2, cfg2);
+
+	dp_catalog_panel_setup_ext_sdp(panel);
+	dp_catalog_panel_setup_vsc_sdp(panel);
+	dp_catalog_panel_setup_infoframe_sdp(panel);
+
+	cfg = dp_read(base + DP_MISC1_MISC0);
+	/* Indicates presence of VSC */
+	cfg |= BIT(6) << 8;
+
+	dp_write(base + DP_MISC1_MISC0, cfg);
+
+	dp_write(base + MMSS_DP_SDP_CFG3, 0x01);
+	dp_write(base + MMSS_DP_SDP_CFG3, 0x00);
+}
+
 static void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog_ctrl *ctrl)
 {
 	struct dp_catalog_private *catalog;
@@ -288,7 +609,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	dp_write(base + DP_VALID_BOUNDARY, ctrl->valid_boundary);
 	dp_write(base + DP_TU, ctrl->dp_tu);
@@ -306,7 +627,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	dp_write(base + DP_STATE_CTRL, state);
 }
@@ -314,7 +635,7 @@
 static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u32 cfg)
 {
 	struct dp_catalog_private *catalog;
-	void __iomem *base;
+	void __iomem *link_base;
 
 	if (!ctrl) {
 		pr_err("invalid input\n");
@@ -322,13 +643,11 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	link_base = catalog->io->dp_link.base;
 
 	pr_debug("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
 
-	dp_write(base + DP_CONFIGURATION_CTRL, cfg);
-	dp_write(base + DP_MAINLINK_LEVELS, 0xa08);
-	dp_write(base + MMSS_DP_ASYNC_FIFO_CONFIG, 0x1);
+	dp_write(link_base + DP_CONFIGURATION_CTRL, cfg);
 }
 
 static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl)
@@ -342,9 +661,9 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
-	dp_write(base + DP_LOGICAL2PHYSCIAL_LANE_MAPPING, 0xe4);
+	dp_write(base + DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
 }
 
 static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
@@ -360,7 +679,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	if (enable) {
 		dp_write(base + DP_MAINLINK_CTRL, 0x02000000);
@@ -391,7 +710,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	misc_val |= (tb << 5);
 	misc_val |= BIT(0); /* Configure clock to synchronous mode */
@@ -408,7 +727,8 @@
 	u32 mvid, nvid;
 	u64 mvid_calc;
 	u32 const nvid_fixed = 0x8000;
-	u32 const link_rate = 540000;
+	u32 const link_rate_hbr2 = 540000;
+	u32 const link_rate_hbr3 = 810000;
 	struct dp_catalog_private *catalog;
 	void __iomem *base_cc, *base_ctrl;
 
@@ -449,11 +769,14 @@
 
 		pr_debug("rate = %d\n", rate);
 
-		if (link_rate == rate)
+		if (link_rate_hbr2 == rate)
 			nvid *= 2;
+
+		if (link_rate_hbr3 == rate)
+			nvid *= 3;
 	}
 
-	base_ctrl = catalog->io->ctrl_io.base;
+	base_ctrl = catalog->io->dp_link.base;
 	pr_debug("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
 	dp_write(base_ctrl + DP_SOFTWARE_MVID, mvid);
 	dp_write(base_ctrl + DP_SOFTWARE_NVID, nvid);
@@ -473,7 +796,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	bit = 1;
 	bit <<= (pattern - 1);
@@ -493,6 +816,93 @@
 		pr_err("set link_train=%d failed\n", pattern);
 }
 
+static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->usb3_dp_com.base;
+
+	dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
+	dp_write(base + USB3_DP_COM_PHY_MODE_CTRL, 0x02);
+	dp_write(base + USB3_DP_COM_SW_RESET, 0x01);
+	/* make sure usb3 com phy software reset is done */
+	wmb();
+
+	if (!flip) /* CC1 */
+		dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x02);
+	else /* CC2 */
+		dp_write(base + USB3_DP_COM_TYPEC_CTRL, 0x03);
+
+	dp_write(base + USB3_DP_COM_SWI_CTRL, 0x00);
+	dp_write(base + USB3_DP_COM_SW_RESET, 0x00);
+	/* make sure the software reset is done */
+	wmb();
+
+	dp_write(base + USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
+	dp_write(base + USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
+	/* make sure phy is brought out of reset */
+	wmb();
+}
+
+static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel,
+	bool enable)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+
+	if (!panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_p0.base;
+
+	if (!enable) {
+		dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x0);
+		dp_write(base + MMSS_DP_BIST_ENABLE, 0x0);
+		dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x0);
+		wmb(); /* ensure Timing generator is turned off */
+		return;
+	}
+
+	dp_write(base + MMSS_DP_INTF_CONFIG, 0x0);
+	dp_write(base + MMSS_DP_INTF_HSYNC_CTL, panel->hsync_ctl);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F0, panel->vsync_period *
+			panel->hsync_period);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, panel->v_sync_width *
+			panel->hsync_period);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
+	dp_write(base + MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_HCTL, panel->display_hctl);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_HCTL, 0);
+	dp_write(base + MMSS_INTF_DISPLAY_V_START_F0, panel->display_v_start);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F0, panel->display_v_end);
+	dp_write(base + MMSS_INTF_DISPLAY_V_START_F1, 0);
+	dp_write(base + MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
+	dp_write(base + MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
+	dp_write(base + MMSS_DP_INTF_POLARITY_CTL, 0);
+	wmb(); /* ensure TPG registers are programmed */
+
+	dp_write(base + MMSS_DP_TPG_MAIN_CONTROL, 0x100);
+	dp_write(base + MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
+	wmb(); /* ensure TPG config is programmed */
+	dp_write(base + MMSS_DP_BIST_ENABLE, 0x1);
+	dp_write(base + MMSS_DP_TIMING_ENGINE_EN, 0x1);
+	wmb(); /* ensure Timing generator is turned on */
+}
+
 static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl)
 {
 	u32 sw_reset;
@@ -505,7 +915,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_ahb.base;
 
 	sw_reset = dp_read(base + DP_SW_RESET);
 
@@ -530,7 +940,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	while (--cnt) {
 		/* DP_MAINLINK_READY */
@@ -557,7 +967,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_ahb.base;
 
 	if (enable) {
 		dp_write(base + DP_INTR_STATUS, DP_INTR_MASK1);
@@ -579,7 +989,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_aux.base;
 
 	if (en) {
 		u32 reftimer = dp_read(base + DP_DP_HPD_REFTIMER);
@@ -610,7 +1020,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_ahb.base;
 
 	ctrl->isr = dp_read(base + DP_INTR_STATUS2);
 	ctrl->isr &= ~DP_INTR_MASK2;
@@ -631,7 +1041,7 @@
 	}
 
 	dp_catalog_get_priv(ctrl);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_ahb.base;
 
 	dp_write(base + DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
 	usleep_range(1000, 1010); /* h/w recommended delay */
@@ -706,6 +1116,86 @@
 	}
 }
 
+static void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog_ctrl *ctrl,
+			u32 pattern)
+{
+	struct dp_catalog_private *catalog;
+	u32 value = 0x0;
+	void __iomem *base = NULL;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->dp_link.base;
+
+	dp_write(base + DP_STATE_CTRL, 0x0);
+
+	switch (pattern) {
+	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
+		dp_write(base + DP_STATE_CTRL, 0x1);
+		break;
+	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+		value &= ~(1 << 16);
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(base + DP_MAINLINK_LEVELS, 0x2);
+		dp_write(base + DP_STATE_CTRL, 0x10);
+		break;
+	case DP_TEST_PHY_PATTERN_PRBS7:
+		dp_write(base + DP_STATE_CTRL, 0x20);
+		break;
+	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
+		dp_write(base + DP_STATE_CTRL, 0x40);
+		/* 00111110000011111000001111100000 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG0, 0x3E0F83E0);
+		/* 00001111100000111110000011111000 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG1, 0x0F83E0F8);
+		/* 1111100000111110 */
+		dp_write(base + DP_TEST_80BIT_CUSTOM_PATTERN_REG2, 0x0000F83E);
+		break;
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_1:
+		value = BIT(16);
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		value |= 0xFC;
+		dp_write(base + DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
+		dp_write(base + DP_MAINLINK_LEVELS, 0x2);
+		dp_write(base + DP_STATE_CTRL, 0x10);
+		break;
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_3:
+		dp_write(base + DP_MAINLINK_CTRL, 0x11);
+		dp_write(base + DP_STATE_CTRL, 0x8);
+		break;
+	default:
+		pr_debug("No valid test pattern requested: 0x%x\n", pattern);
+		return;
+	}
+
+	/* Make sure the test pattern is programmed in the hardware */
+	wmb();
+}
+
+static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base = NULL;
+
+	if (!ctrl) {
+		pr_err("invalid input\n");
+		return 0;
+	}
+
+	dp_catalog_get_priv(ctrl);
+
+	base = catalog->io->dp_link.base;
+
+	return dp_read(base + DP_MAINLINK_READY);
+}
+
 /* panel related catalog functions */
 static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
 {
@@ -718,7 +1208,7 @@
 	}
 
 	dp_catalog_get_priv(panel);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
 
 	dp_write(base + DP_TOTAL_HOR_VER, panel->total);
 	dp_write(base + DP_START_HOR_VER_FROM_SYNC, panel->sync_start);
@@ -778,7 +1268,9 @@
 		return;
 
 	dp_catalog_get_priv(audio);
-	base = catalog->io->ctrl_io.base;
+	base = catalog->io->dp_link.base;
+
+	sdp_cfg = dp_read(base + MMSS_DP_SDP_CFG);
 
 	/* AUDIO_TIMESTAMP_SDP_EN */
 	sdp_cfg |= BIT(1);
@@ -817,7 +1309,7 @@
 
 	dp_catalog_get_priv(audio);
 
-	base    = catalog->io->ctrl_io.base;
+	base    = catalog->io->dp_link.base;
 	sdp_map = catalog->audio_map;
 	sdp     = audio->sdp_type;
 	header  = audio->sdp_header;
@@ -839,7 +1331,7 @@
 
 	dp_catalog_get_priv(audio);
 
-	base    = catalog->io->ctrl_io.base;
+	base    = catalog->io->dp_link.base;
 	sdp_map = catalog->audio_map;
 	sdp     = audio->sdp_type;
 	header  = audio->sdp_header;
@@ -857,7 +1349,7 @@
 	dp_catalog_get_priv(audio);
 
 	select = audio->data;
-	base   = catalog->io->ctrl_io.base;
+	base   = catalog->io->dp_link.base;
 
 	acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
 
@@ -874,7 +1366,7 @@
 
 	dp_catalog_get_priv(audio);
 
-	base   = catalog->io->ctrl_io.base;
+	base   = catalog->io->dp_link.base;
 	safe_to_exit_level = audio->data;
 
 	mainlink_levels = dp_read(base + DP_MAINLINK_LEVELS);
@@ -896,7 +1388,7 @@
 
 	dp_catalog_get_priv(audio);
 
-	base   = catalog->io->ctrl_io.base;
+	base   = catalog->io->dp_link.base;
 	enable = !!audio->data;
 
 	audio_ctrl = dp_read(base + MMSS_DP_AUDIO_CFG);
@@ -913,6 +1405,131 @@
 	wmb();
 }
 
+static void dp_catalog_config_spd_header(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 value, new_value;
+	u8 parity_byte;
+
+	if (!panel)
+		return;
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_link.base;
+
+	/* Config header and parity byte 1 */
+	value = dp_read(base + MMSS_DP_GENERIC1_0);
+
+	new_value = 0x83;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_1_BIT)
+			| (parity_byte << PARITY_BYTE_1_BIT));
+	pr_debug("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_write(base + MMSS_DP_GENERIC1_0, value);
+
+	/* Config header and parity byte 2 */
+	value = dp_read(base + MMSS_DP_GENERIC1_1);
+
+	new_value = 0x1b;
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_2_BIT)
+			| (parity_byte << PARITY_BYTE_2_BIT));
+	pr_debug("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
+			value, parity_byte);
+	dp_write(base + MMSS_DP_GENERIC1_1, value);
+
+	/* Config header and parity byte 3 */
+	value = dp_read(base + MMSS_DP_GENERIC1_1);
+
+	new_value = (0x0 | (0x12 << 2));
+	parity_byte = dp_header_get_parity(new_value);
+	value |= ((new_value << HEADER_BYTE_3_BIT)
+			| (parity_byte << PARITY_BYTE_3_BIT));
+	pr_debug("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
+			new_value, parity_byte);
+	dp_write(base + MMSS_DP_GENERIC1_1, value);
+}
+
+static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
+{
+	struct dp_catalog_private *catalog;
+	void __iomem *base;
+	u32 spd_cfg = 0, spd_cfg2 = 0;
+	u8 *vendor = NULL, *product = NULL;
+	/*
+	 * Source Device Information
+	 * 00h unknown
+	 * 01h Digital STB
+	 * 02h DVD
+	 * 03h D-VHS
+	 * 04h HDD Video
+	 * 05h DVC
+	 * 06h DSC
+	 * 07h Video CD
+	 * 08h Game
+	 * 09h PC general
+	 * 0ah Bluray-Disc
+	 * 0bh Super Audio CD
+	 * 0ch HD DVD
+	 * 0dh PMP
+	 * 0eh-ffh reserved
+	 */
+	u32 device_type = 0;
+
+	if (!panel)
+		return;
+
+	dp_catalog_get_priv(panel);
+	base = catalog->io->dp_link.base;
+
+	dp_catalog_config_spd_header(panel);
+
+	vendor = panel->spd_vendor_name;
+	product = panel->spd_product_description;
+
+	dp_write(base + MMSS_DP_GENERIC1_2, ((vendor[0] & 0x7f) |
+				((vendor[1] & 0x7f) << 8) |
+				((vendor[2] & 0x7f) << 16) |
+				((vendor[3] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_3, ((vendor[4] & 0x7f) |
+				((vendor[5] & 0x7f) << 8) |
+				((vendor[6] & 0x7f) << 16) |
+				((vendor[7] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_4, ((product[0] & 0x7f) |
+			((product[1] & 0x7f) << 8) |
+			((product[2] & 0x7f) << 16) |
+			((product[3] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_5, ((product[4] & 0x7f) |
+			((product[5] & 0x7f) << 8) |
+			((product[6] & 0x7f) << 16) |
+			((product[7] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_6, ((product[8] & 0x7f) |
+			((product[9] & 0x7f) << 8) |
+			((product[10] & 0x7f) << 16) |
+			((product[11] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_7, ((product[12] & 0x7f) |
+			((product[13] & 0x7f) << 8) |
+			((product[14] & 0x7f) << 16) |
+			((product[15] & 0x7f) << 24)));
+	dp_write(base + MMSS_DP_GENERIC1_8, device_type);
+	dp_write(base + MMSS_DP_GENERIC1_9, 0x00);
+
+	spd_cfg = dp_read(base + MMSS_DP_SDP_CFG);
+	/* GENERIC1_SDP for SPD Infoframe */
+	spd_cfg |= BIT(18);
+	dp_write(base + MMSS_DP_SDP_CFG, spd_cfg);
+
+	spd_cfg2 = dp_read(base + MMSS_DP_SDP_CFG2);
+	/* 28 data bytes for SPD Infoframe with GENERIC1 set */
+	spd_cfg2 |= BIT(17);
+	dp_write(base + MMSS_DP_SDP_CFG2, spd_cfg2);
+
+	dp_write(base + MMSS_DP_SDP_CFG3, 0x1);
+	dp_write(base + MMSS_DP_SDP_CFG3, 0x0);
+}
+
 struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
 {
 	int rc = 0;
@@ -922,11 +1539,13 @@
 		.read_data     = dp_catalog_aux_read_data,
 		.write_data    = dp_catalog_aux_write_data,
 		.write_trans   = dp_catalog_aux_write_trans,
+		.clear_trans   = dp_catalog_aux_clear_trans,
 		.reset         = dp_catalog_aux_reset,
 		.update_aux_cfg = dp_catalog_aux_update_cfg,
 		.enable        = dp_catalog_aux_enable,
 		.setup         = dp_catalog_aux_setup,
 		.get_irq       = dp_catalog_aux_get_irq,
+		.clear_hw_interrupts = dp_catalog_aux_clear_hw_interrupts,
 	};
 	struct dp_catalog_ctrl ctrl = {
 		.state_ctrl     = dp_catalog_ctrl_state_ctrl,
@@ -937,6 +1556,7 @@
 		.config_msa     = dp_catalog_ctrl_config_msa,
 		.set_pattern    = dp_catalog_ctrl_set_pattern,
 		.reset          = dp_catalog_ctrl_reset,
+		.usb_reset      = dp_catalog_ctrl_usb_reset,
 		.mainlink_ready = dp_catalog_ctrl_mainlink_ready,
 		.enable_irq     = dp_catalog_ctrl_enable_irq,
 		.hpd_config     = dp_catalog_ctrl_hpd_config,
@@ -946,6 +1566,8 @@
 		.get_interrupt  = dp_catalog_ctrl_get_interrupt,
 		.update_transfer_unit = dp_catalog_ctrl_update_transfer_unit,
 		.read_hdcp_status     = dp_catalog_ctrl_read_hdcp_status,
+		.send_phy_pattern    = dp_catalog_ctrl_send_phy_pattern,
+		.read_phy_pattern = dp_catalog_ctrl_read_phy_pattern,
 	};
 	struct dp_catalog_audio audio = {
 		.init       = dp_catalog_audio_init,
@@ -958,6 +1580,9 @@
 	};
 	struct dp_catalog_panel panel = {
 		.timing_cfg = dp_catalog_panel_timing_cfg,
+		.config_hdr = dp_catalog_panel_config_hdr,
+		.tpg_config = dp_catalog_panel_tpg_cfg,
+		.config_spd = dp_catalog_panel_config_spd,
 	};
 
 	if (!io) {
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 47c10bf..b270545 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -15,6 +15,8 @@
 #ifndef _DP_CATALOG_H_
 #define _DP_CATALOG_H_
 
+#include <drm/msm_drm.h>
+
 #include "dp_parser.h"
 
 /* interrupts */
@@ -34,6 +36,34 @@
 #define DP_INTR_FRAME_END		BIT(6)
 #define DP_INTR_CRC_UPDATED		BIT(9)
 
+struct dp_catalog_hdr_data {
+	u32 ext_header_byte0;
+	u32 ext_header_byte1;
+	u32 ext_header_byte2;
+	u32 ext_header_byte3;
+
+	u32 vsc_header_byte0;
+	u32 vsc_header_byte1;
+	u32 vsc_header_byte2;
+	u32 vsc_header_byte3;
+
+	u32 vscext_header_byte0;
+	u32 vscext_header_byte1;
+	u32 vscext_header_byte2;
+	u32 vscext_header_byte3;
+
+	u32 bpc;
+
+	u32 version;
+	u32 length;
+	u32 pixel_encoding;
+	u32 colorimetry;
+	u32 dynamic_range;
+	u32 content_type;
+
+	struct drm_msm_ext_hdr_metadata hdr_meta;
+};
+
 struct dp_catalog_aux {
 	u32 data;
 	u32 isr;
@@ -41,6 +71,7 @@
 	u32 (*read_data)(struct dp_catalog_aux *aux);
 	int (*write_data)(struct dp_catalog_aux *aux);
 	int (*write_trans)(struct dp_catalog_aux *aux);
+	int (*clear_trans)(struct dp_catalog_aux *aux, bool read);
 	void (*reset)(struct dp_catalog_aux *aux);
 	void (*enable)(struct dp_catalog_aux *aux, bool enable);
 	void (*update_aux_cfg)(struct dp_catalog_aux *aux,
@@ -48,6 +79,7 @@
 	void (*setup)(struct dp_catalog_aux *aux,
 			struct dp_aux_cfg *aux_cfg);
 	void (*get_irq)(struct dp_catalog_aux *aux, bool cmd_busy);
+	void (*clear_hw_interrupts)(struct dp_catalog_aux *aux);
 };
 
 struct dp_catalog_ctrl {
@@ -65,6 +97,7 @@
 				u32 stream_rate_khz, bool fixed_nvid);
 	void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
 	void (*reset)(struct dp_catalog_ctrl *ctrl);
+	void (*usb_reset)(struct dp_catalog_ctrl *ctrl, bool flip);
 	bool (*mainlink_ready)(struct dp_catalog_ctrl *ctrl);
 	void (*enable_irq)(struct dp_catalog_ctrl *ctrl, bool enable);
 	void (*hpd_config)(struct dp_catalog_ctrl *ctrl, bool enable);
@@ -76,8 +109,18 @@
 	void (*get_interrupt)(struct dp_catalog_ctrl *ctrl);
 	void (*update_transfer_unit)(struct dp_catalog_ctrl *ctrl);
 	u32 (*read_hdcp_status)(struct dp_catalog_ctrl *ctrl);
+	void (*send_phy_pattern)(struct dp_catalog_ctrl *ctrl,
+			u32 pattern);
+	u32 (*read_phy_pattern)(struct dp_catalog_ctrl *ctrl);
 };
 
+#define HEADER_BYTE_2_BIT	 0
+#define PARITY_BYTE_2_BIT	 8
+#define HEADER_BYTE_1_BIT	16
+#define PARITY_BYTE_1_BIT	24
+#define HEADER_BYTE_3_BIT	16
+#define PARITY_BYTE_3_BIT	24
+
 enum dp_catalog_audio_sdp_type {
 	DP_AUDIO_SDP_STREAM,
 	DP_AUDIO_SDP_TIMESTAMP,
@@ -113,8 +156,24 @@
 	u32 sync_start;
 	u32 width_blanking;
 	u32 dp_active;
+	u8 *spd_vendor_name;
+	u8 *spd_product_description;
+
+	struct dp_catalog_hdr_data hdr_data;
+
+	/* TPG */
+	u32 hsync_period;
+	u32 vsync_period;
+	u32 display_v_start;
+	u32 display_v_end;
+	u32 v_sync_width;
+	u32 hsync_ctl;
+	u32 display_hctl;
 
 	int (*timing_cfg)(struct dp_catalog_panel *panel);
+	void (*config_hdr)(struct dp_catalog_panel *panel);
+	void (*tpg_config)(struct dp_catalog_panel *panel, bool enable);
+	void (*config_spd)(struct dp_catalog_panel *panel);
 };
 
 struct dp_catalog {
@@ -124,6 +183,71 @@
 	struct dp_catalog_panel panel;
 };
 
+static inline u8 dp_ecc_get_g0_value(u8 data)
+{
+	u8 c[4];
+	u8 g[4];
+	u8 ret_data = 0;
+	u8 i;
+
+	for (i = 0; i < 4; i++)
+		c[i] = (data >> i) & 0x01;
+
+	g[0] = c[3];
+	g[1] = c[0] ^ c[3];
+	g[2] = c[1];
+	g[3] = c[2];
+
+	for (i = 0; i < 4; i++)
+		ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+	return ret_data;
+}
+
+static inline u8 dp_ecc_get_g1_value(u8 data)
+{
+	u8 c[4];
+	u8 g[4];
+	u8 ret_data = 0;
+	u8 i;
+
+	for (i = 0; i < 4; i++)
+		c[i] = (data >> i) & 0x01;
+
+	g[0] = c[0] ^ c[3];
+	g[1] = c[0] ^ c[1] ^ c[3];
+	g[2] = c[1] ^ c[2];
+	g[3] = c[2] ^ c[3];
+
+	for (i = 0; i < 4; i++)
+		ret_data = ((g[i] & 0x01) << i) | ret_data;
+
+	return ret_data;
+}
+
+static inline u8 dp_header_get_parity(u32 data)
+{
+	u8 x0 = 0;
+	u8 x1 = 0;
+	u8 ci = 0;
+	u8 iData = 0;
+	u8 i = 0;
+	u8 parity_byte;
+	u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2;
+
+	for (i = 0; i < num_byte; i++) {
+		iData = (data >> i*4) & 0xF;
+
+		ci = iData ^ x1;
+		x1 = x0 ^ dp_ecc_get_g1_value(ci);
+		x0 = dp_ecc_get_g0_value(ci);
+	}
+
+	parity_byte = x1 | (x0 << 4);
+
+	return parity_byte;
+}
+
 struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
 void dp_catalog_put(struct dp_catalog *catalog);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index 51abcf5..576ed52 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -36,6 +36,12 @@
 #define ST_SEND_VIDEO			BIT(7)
 #define ST_PUSH_IDLE			BIT(8)
 
+#define MR_LINK_TRAINING1  0x8
+#define MR_LINK_SYMBOL_ERM 0x80
+#define MR_LINK_PRBS7 0x100
+#define MR_LINK_CUSTOM80 0x200
+#define MR_LINK_TRAINING4  0x40
+
 struct dp_vc_tu_mapping_table {
 	u32 vic;
 	u8 lanes;
@@ -63,9 +69,7 @@
 
 	struct completion idle_comp;
 	struct completion video_comp;
-	struct completion irq_comp;
 
-	bool psm_enabled;
 	bool orientation;
 	atomic_t aborted;
 
@@ -155,7 +159,7 @@
 	config |= tbd << 8;
 
 	/* Num of Lanes */
-	config |= ((ctrl->link->lane_count - 1) << 4);
+	config |= ((ctrl->link->link_params.lane_count - 1) << 4);
 
 	if (drm_dp_enhanced_frame_cap(dpcd))
 		config |= 0x40;
@@ -303,7 +307,7 @@
 	u64 brute_force_threshold = 10;
 	u64 diff_abs;
 
-	ln_cnt =  ctrl->link->lane_count;
+	ln_cnt =  ctrl->link->link_params.lane_count;
 
 	bpp = pinfo->bpp;
 	lwidth = pinfo->h_active;
@@ -322,7 +326,8 @@
 	even_distribution = 0;
 	min_hblank = 0;
 
-	lclk = drm_dp_bw_code_to_link_rate(ctrl->link->bw_code) * DP_KHZ_TO_HZ;
+	lclk = drm_dp_bw_code_to_link_rate(
+		ctrl->link->link_params.bw_code) * DP_KHZ_TO_HZ;
 
 	pr_debug("pclk=%lld, active_width=%d, h_blank=%d\n",
 						pclk, lwidth, h_blank);
@@ -756,17 +761,18 @@
 	return drm_dp_dpcd_write(ctrl->aux->drm_aux, 0x103, buf, 4);
 }
 
-static void dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
+static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
 {
 	struct dp_link *link = ctrl->link;
 
 	ctrl->catalog->update_vx_px(ctrl->catalog,
-			link->v_level, link->p_level);
+		link->phy_params.v_level, link->phy_params.p_level);
 
-	dp_ctrl_update_sink_vx_px(ctrl, link->v_level, link->p_level);
+	return dp_ctrl_update_sink_vx_px(ctrl, link->phy_params.v_level,
+		link->phy_params.p_level);
 }
 
-static void dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
+static int dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
 		u8 pattern)
 {
 	u8 buf[4];
@@ -774,7 +780,8 @@
 	pr_debug("sink: pattern=%x\n", pattern);
 
 	buf[0] = pattern;
-	drm_dp_dpcd_write(ctrl->aux->drm_aux, DP_TRAINING_PATTERN_SET, buf, 1);
+	return drm_dp_dpcd_write(ctrl->aux->drm_aux,
+		DP_TRAINING_PATTERN_SET, buf, 1);
 }
 
 static int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
@@ -811,12 +818,21 @@
 	wmb();
 
 	ctrl->catalog->set_pattern(ctrl->catalog, 0x01);
-	dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
+	ret = dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
 		DP_LINK_SCRAMBLING_DISABLE); /* train_1 */
-	dp_ctrl_update_vx_px(ctrl);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		return ret;
+	}
+
+	ret = dp_ctrl_update_vx_px(ctrl);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		return ret;
+	}
 
 	tries = 0;
-	old_v_level = ctrl->link->v_level;
+	old_v_level = ctrl->link->phy_params.v_level;
 	while (1) {
 		drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);
 
@@ -825,32 +841,36 @@
 			break;
 
 		if (drm_dp_clock_recovery_ok(link_status,
-			ctrl->link->lane_count)) {
+			ctrl->link->link_params.lane_count)) {
 			break;
 		}
 
-		if (ctrl->link->v_level == DP_LINK_VOLTAGE_MAX) {
+		if (ctrl->link->phy_params.v_level == DP_LINK_VOLTAGE_MAX) {
 			pr_err_ratelimited("max v_level reached\n");
 			ret = -EAGAIN;
 			break;
 		}
 
-		if (old_v_level == ctrl->link->v_level) {
+		if (old_v_level == ctrl->link->phy_params.v_level) {
 			tries++;
 			if (tries >= maximum_retries) {
 				pr_err("max tries reached\n");
-				ret = -EAGAIN;
+				ret = -ETIMEDOUT;
 				break;
 			}
 		} else {
 			tries = 0;
-			old_v_level = ctrl->link->v_level;
+			old_v_level = ctrl->link->phy_params.v_level;
 		}
 
 		pr_debug("clock recovery not done, adjusting vx px\n");
 
 		ctrl->link->adjust_levels(ctrl->link, link_status);
-		dp_ctrl_update_vx_px(ctrl);
+		ret = dp_ctrl_update_vx_px(ctrl);
+		if (ret <= 0) {
+			ret = -EINVAL;
+			break;
+		}
 	}
 
 	return ret;
@@ -859,41 +879,25 @@
 static int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
 {
 	int ret = 0;
-	u32 min_req_link_rate_khz;
-	u32 new_proposed_link_bw_code;
-	u32 new_proposed_link_rate_khz;
 
 	if (!ctrl)
 		return -EINVAL;
 
-	min_req_link_rate_khz = ctrl->panel->get_min_req_link_rate(ctrl->panel);
-
-	switch (ctrl->link->bw_code) {
-	case DP_LINK_RATE_810:
-		new_proposed_link_bw_code = DP_LINK_BW_5_4;
+	switch (ctrl->link->link_params.bw_code) {
+	case DP_LINK_BW_8_1:
+		ctrl->link->link_params.bw_code = DP_LINK_BW_5_4;
 		break;
 	case DP_LINK_BW_5_4:
-		new_proposed_link_bw_code = DP_LINK_BW_2_7;
+		ctrl->link->link_params.bw_code = DP_LINK_BW_2_7;
 		break;
 	case DP_LINK_BW_2_7:
 	case DP_LINK_BW_1_62:
 	default:
-		new_proposed_link_bw_code = DP_LINK_BW_1_62;
+		ctrl->link->link_params.bw_code = DP_LINK_BW_1_62;
 		break;
 	};
 
-	new_proposed_link_rate_khz = drm_dp_bw_code_to_link_rate(
-						new_proposed_link_bw_code);
-
-	pr_debug("new proposed link rate=%d khz\n", new_proposed_link_rate_khz);
-	pr_debug("min required link rate=%d khz\n", min_req_link_rate_khz);
-
-	if (new_proposed_link_rate_khz >= min_req_link_rate_khz)
-		ctrl->link->bw_code = new_proposed_link_bw_code;
-	else
-		pr_debug("can't go below min required link rate\n");
-
-	pr_debug("new bw code=0x%x\n", ctrl->link->bw_code);
+	pr_debug("new bw code=0x%x\n", ctrl->link->link_params.bw_code);
 
 	return ret;
 }
@@ -920,9 +924,18 @@
 	else
 		pattern = DP_TRAINING_PATTERN_2;
 
-	dp_ctrl_update_vx_px(ctrl);
+	ret = dp_ctrl_update_vx_px(ctrl);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		return ret;
+	}
 	ctrl->catalog->set_pattern(ctrl->catalog, pattern);
-	dp_ctrl_train_pattern_set(ctrl, pattern | DP_RECOVERED_CLOCK_OUT_EN);
+	ret = dp_ctrl_train_pattern_set(ctrl,
+		pattern | DP_RECOVERED_CLOCK_OUT_EN);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		return ret;
+	}
 
 	do  {
 		drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);
@@ -931,17 +944,22 @@
 		if (ret)
 			break;
 
-		if (drm_dp_channel_eq_ok(link_status, ctrl->link->lane_count))
+		if (drm_dp_channel_eq_ok(link_status,
+			ctrl->link->link_params.lane_count))
 			break;
 
 		if (tries > maximum_retries) {
-			ret = -EAGAIN;
+			ret = -ETIMEDOUT;
 			break;
 		}
 		tries++;
 
 		ctrl->link->adjust_levels(ctrl->link, link_status);
-		dp_ctrl_update_vx_px(ctrl);
+		ret = dp_ctrl_update_vx_px(ctrl);
+		if (ret <= 0) {
+			ret = -EINVAL;
+			break;
+		}
 	} while (1);
 
 	return ret;
@@ -953,18 +971,26 @@
 	u8 encoding = 0x1;
 	struct drm_dp_link link_info = {0};
 
-	ctrl->link->p_level = 0;
-	ctrl->link->v_level = 0;
+	ctrl->link->phy_params.p_level = 0;
+	ctrl->link->phy_params.v_level = 0;
 
 	dp_ctrl_config_ctrl(ctrl);
 
-	link_info.num_lanes = ctrl->link->lane_count;
-	link_info.rate = drm_dp_bw_code_to_link_rate(ctrl->link->bw_code);
+	link_info.num_lanes = ctrl->link->link_params.lane_count;
+	link_info.rate = drm_dp_bw_code_to_link_rate(
+		ctrl->link->link_params.bw_code);
 	link_info.capabilities = ctrl->panel->link_info.capabilities;
 
-	drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
-	drm_dp_dpcd_write(ctrl->aux->drm_aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
-				&encoding, 1);
+	ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
+	if (ret)
+		goto end;
+
+	ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
+		DP_MAIN_LINK_CHANNEL_CODING_SET, &encoding, 1);
+	if (ret <= 0) {
+		ret = -EINVAL;
+		goto end;
+	}
 
 	ret = dp_ctrl_link_train_1(ctrl);
 	if (ret) {
@@ -982,7 +1008,7 @@
 	}
 
 	/* print success info as this is a result of user initiated action */
-	pr_debug("link training #2 successful\n");
+	pr_info("link training #2 successful\n");
 
 end:
 	dp_ctrl_state_ctrl(ctrl, 0);
@@ -1000,9 +1026,7 @@
 
 	ctrl->catalog->mainlink_ctrl(ctrl->catalog, true);
 
-	drm_dp_link_power_up(ctrl->aux->drm_aux, &ctrl->panel->link_info);
-
-	if (ctrl->link->phy_pattern_requested(ctrl->link))
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
 		goto end;
 
 	if (!train)
@@ -1060,7 +1084,7 @@
 	ctrl->power->set_pixel_clk_parent(ctrl->power);
 
 	dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
-		drm_dp_bw_code_to_link_rate(ctrl->link->bw_code));
+		drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code));
 
 	dp_ctrl_set_clock_rate(ctrl, "ctrl_pixel_clk", ctrl->pixel_rate);
 
@@ -1078,7 +1102,8 @@
 	return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
 }
 
-static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip)
+static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl,
+	bool flip, bool multi_func)
 {
 	struct dp_ctrl_private *ctrl;
 	struct dp_catalog_ctrl *catalog;
@@ -1093,8 +1118,10 @@
 	ctrl->orientation = flip;
 	catalog = ctrl->catalog;
 
-	catalog->reset(ctrl->catalog);
-	catalog->phy_reset(ctrl->catalog);
+	if (!multi_func) {
+		catalog->usb_reset(ctrl->catalog, flip);
+		catalog->phy_reset(ctrl->catalog);
+	}
 	catalog->enable_irq(ctrl->catalog, true);
 
 	return 0;
@@ -1119,12 +1146,6 @@
 	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
 
 	ctrl->catalog->enable_irq(ctrl->catalog, false);
-	ctrl->catalog->reset(ctrl->catalog);
-
-	/* Make sure DP is disabled before clk disable */
-	wmb();
-
-	dp_ctrl_disable_mainlink_clocks(ctrl);
 
 	pr_debug("Host deinitialized successfully\n");
 }
@@ -1147,95 +1168,240 @@
 	return false;
 }
 
-static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)
+static int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
 {
 	int ret = 0;
 
+	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
+	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
+
+	ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+
 	do {
-		if (ret == -EAGAIN)
+		if (ret == -EAGAIN) {
+			/* try with lower link rate */
+			dp_ctrl_link_rate_down_shift(ctrl);
+
 			ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+		}
 
 		ctrl->catalog->phy_lane_cfg(ctrl->catalog,
-			ctrl->orientation, ctrl->link->lane_count);
+			ctrl->orientation, ctrl->link->link_params.lane_count);
 
-		if (lt_needed) {
-			/*
-			 * Diasable and re-enable the mainlink clock since the
-			 * link clock might have been adjusted as part of the
-			 * link maintenance.
-			 */
-			if (!ctrl->link->phy_pattern_requested(
-					ctrl->link))
-				dp_ctrl_disable_mainlink_clocks(ctrl);
+		/*
+		 * Disable and re-enable the mainlink clock since the
+		 * link clock might have been adjusted as part of the
+		 * link maintenance.
+		 */
+		dp_ctrl_disable_mainlink_clocks(ctrl);
 
-			ret = dp_ctrl_enable_mainlink_clocks(ctrl);
-			if (ret)
-				continue;
-		}
+		ret = dp_ctrl_enable_mainlink_clocks(ctrl);
+		if (ret)
+			continue;
 
 		dp_ctrl_configure_source_params(ctrl);
 
 		ctrl->catalog->config_msa(ctrl->catalog,
-			drm_dp_bw_code_to_link_rate(ctrl->link->bw_code),
+			drm_dp_bw_code_to_link_rate(
+			ctrl->link->link_params.bw_code),
 			ctrl->pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
 
 		reinit_completion(&ctrl->idle_comp);
 
-		if (ctrl->psm_enabled) {
-			ret = ctrl->link->send_psm_request(ctrl->link, false);
-			if (ret) {
-				pr_err("failed to exit low power mode, rc=%d\n",
-					ret);
-				continue;
-			}
-		}
-
-		ret = dp_ctrl_setup_main_link(ctrl, lt_needed);
+		ret = dp_ctrl_setup_main_link(ctrl, true);
 	} while (ret == -EAGAIN);
 
 	return ret;
 }
 
-static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)
+static void dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
 {
 	int ret = 0;
-	u32 rate = ctrl->panel->link_info.rate;
+
+	if (!ctrl->link->phy_params.phy_test_pattern_sel) {
+		pr_debug("no test pattern selected by sink\n");
+		return;
+	}
+
+	pr_debug("start\n");
+
+	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl);
+	/*
+	 * The global reset will need DP link ralated clocks to be
+	 * running. Add the global reset just before disabling the
+	 * link clocks and core clocks.
+	 */
+	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
+	ctrl->dp_ctrl.off(&ctrl->dp_ctrl);
+
+	ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl);
+	if (ret)
+		pr_err("failed to enable DP controller\n");
+
+	pr_debug("end\n");
+}
+
+static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
+{
+	bool success = false;
+	u32 pattern_sent = 0x0;
+	u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
+
+	ctrl->catalog->update_vx_px(ctrl->catalog,
+			ctrl->link->phy_params.v_level,
+			ctrl->link->phy_params.p_level);
+	ctrl->catalog->send_phy_pattern(ctrl->catalog, pattern_requested);
+	ctrl->link->send_test_response(ctrl->link);
+
+	pattern_sent = ctrl->catalog->read_phy_pattern(ctrl->catalog);
+	pr_debug("pattern_request: %s. pattern_sent: 0x%x\n",
+			dp_link_get_phy_test_pattern(pattern_requested),
+			pattern_sent);
+
+	switch (pattern_sent) {
+	case MR_LINK_TRAINING1:
+		if (pattern_requested ==
+				DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING)
+			success = true;
+		break;
+	case MR_LINK_SYMBOL_ERM:
+		if ((pattern_requested ==
+				DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT)
+			|| (pattern_requested ==
+				DP_TEST_PHY_PATTERN_CP2520_PATTERN_1))
+			success = true;
+		break;
+	case MR_LINK_PRBS7:
+		if (pattern_requested == DP_TEST_PHY_PATTERN_PRBS7)
+			success = true;
+		break;
+	case MR_LINK_CUSTOM80:
+		if (pattern_requested ==
+				DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN)
+			success = true;
+		break;
+	case MR_LINK_TRAINING4:
+		if (pattern_requested ==
+				DP_TEST_PHY_PATTERN_CP2520_PATTERN_3)
+			success = true;
+		break;
+	default:
+		success = false;
+		break;
+	}
+
+	pr_debug("%s: %s\n", success ? "success" : "failed",
+			dp_link_get_phy_test_pattern(pattern_requested));
+}
+
+static bool dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+	u32 sink_request = 0x0;
+	bool req_handled = false;
+
+	if (!dp_ctrl) {
+		pr_err("invalid input\n");
+		goto end;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+	sink_request = ctrl->link->sink_request;
+
+	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		pr_info("PHY_TEST_PATTERN\n");
+		dp_ctrl_process_phy_test_request(ctrl);
+
+		req_handled = true;
+	}
+
+	if (sink_request & DP_LINK_STATUS_UPDATED) {
+		pr_info("DP_LINK_STATUS_UPDATED\n");
+		dp_ctrl_link_maintenance(ctrl);
+
+		req_handled = true;
+	}
+
+	if (sink_request & DP_TEST_LINK_TRAINING) {
+		pr_info("DP_TEST_LINK_TRAINING\n");
+		ctrl->link->send_test_response(ctrl->link);
+		dp_ctrl_link_maintenance(ctrl);
+
+		req_handled = true;
+	}
+end:
+	return req_handled;
+}
+
+static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl)
+{
+	struct dp_ctrl_private *ctrl;
+
+	if (!dp_ctrl) {
+		pr_err("invalid params\n");
+		return;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
+	ctrl->catalog->reset(ctrl->catalog);
+}
+
+static int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
+{
+	int rc = 0;
+	struct dp_ctrl_private *ctrl;
+	u32 rate = 0;
 	u32 link_train_max_retries = 100;
+	u32 const phy_cts_pixel_clk_khz = 148500;
+
+	if (!dp_ctrl) {
+		rc = -EINVAL;
+		goto end;
+	}
+
+	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
 
 	atomic_set(&ctrl->aborted, 0);
+	rate = ctrl->panel->link_info.rate;
 
 	ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
 	ctrl->catalog->hpd_config(ctrl->catalog, true);
 
-	ctrl->link->bw_code  = drm_dp_link_rate_to_bw_code(rate);
-	ctrl->link->lane_count = ctrl->panel->link_info.num_lanes;
-	ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
+		pr_debug("using phy test link parameters\n");
+		if (!ctrl->panel->pinfo.pixel_clk_khz)
+			ctrl->pixel_rate = phy_cts_pixel_clk_khz;
+	} else {
+		ctrl->link->link_params.bw_code =
+			drm_dp_link_rate_to_bw_code(rate);
+		ctrl->link->link_params.lane_count =
+			ctrl->panel->link_info.num_lanes;
+		ctrl->pixel_rate = ctrl->panel->pinfo.pixel_clk_khz;
+	}
 
 	pr_debug("bw_code=%d, lane_count=%d, pixel_rate=%d\n",
-		ctrl->link->bw_code, ctrl->link->lane_count,
-		ctrl->pixel_rate);
+		ctrl->link->link_params.bw_code,
+		ctrl->link->link_params.lane_count, ctrl->pixel_rate);
 
 	ctrl->catalog->phy_lane_cfg(ctrl->catalog,
-			ctrl->orientation, ctrl->link->lane_count);
+			ctrl->orientation, ctrl->link->link_params.lane_count);
 
-	ret = dp_ctrl_enable_mainlink_clocks(ctrl);
-	if (ret)
-		goto exit;
+	rc = dp_ctrl_enable_mainlink_clocks(ctrl);
+	if (rc)
+		goto end;
 
 	reinit_completion(&ctrl->idle_comp);
 
 	dp_ctrl_configure_source_params(ctrl);
 
-	if (ctrl->psm_enabled)
-		ret = ctrl->link->send_psm_request(ctrl->link, false);
-
 	while (--link_train_max_retries && !atomic_read(&ctrl->aborted)) {
 		ctrl->catalog->config_msa(ctrl->catalog,
-			drm_dp_bw_code_to_link_rate(ctrl->link->bw_code),
+			drm_dp_bw_code_to_link_rate(
+			ctrl->link->link_params.bw_code),
 			ctrl->pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));
 
-		ret = dp_ctrl_setup_main_link(ctrl, true);
-		if (!ret)
+		rc = dp_ctrl_setup_main_link(ctrl, true);
+		if (!rc)
 			break;
 
 		/* try with lower link rate */
@@ -1250,50 +1416,16 @@
 		dp_ctrl_enable_mainlink_clocks(ctrl);
 	}
 
+	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
+		dp_ctrl_send_phy_test_pattern(ctrl);
+
 	pr_debug("End-\n");
 
-exit:
-	return ret;
-}
-
-static void dp_ctrl_off_irq(struct dp_ctrl_private *ctrl)
-{
-	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
-
-	/* Make sure DP mainlink and audio engines are disabled */
-	wmb();
-
-	complete_all(&ctrl->irq_comp);
-	pr_debug("end\n");
-}
-
-static void dp_ctrl_off_hpd(struct dp_ctrl_private *ctrl)
-{
-	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
-	pr_debug("DP off done\n");
-}
-
-static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool hpd_irq)
-{
-	int rc = 0;
-	struct dp_ctrl_private *ctrl;
-
-	if (!dp_ctrl) {
-		rc = -EINVAL;
-		goto end;
-	}
-
-	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
-
-	if (hpd_irq)
-		rc = dp_ctrl_on_irq(ctrl, false);
-	else
-		rc = dp_ctrl_on_hpd(ctrl);
 end:
 	return rc;
 }
 
-static void dp_ctrl_off(struct dp_ctrl *dp_ctrl, bool hpd_irq)
+static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
 {
 	struct dp_ctrl_private *ctrl;
 
@@ -1302,10 +1434,15 @@
 
 	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
 
-	if (hpd_irq)
-		dp_ctrl_off_irq(ctrl);
-	else
-		dp_ctrl_off_hpd(ctrl);
+	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
+	ctrl->catalog->reset(ctrl->catalog);
+
+	/* Make sure DP is disabled before clk disable */
+	wmb();
+
+	dp_ctrl_disable_mainlink_clocks(ctrl);
+
+	pr_debug("DP off done\n");
 }
 
 static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
@@ -1347,7 +1484,6 @@
 
 	init_completion(&ctrl->idle_comp);
 	init_completion(&ctrl->video_comp);
-	init_completion(&ctrl->irq_comp);
 
 	/* in parameters */
 	ctrl->parser   = in->parser;
@@ -1356,6 +1492,7 @@
 	ctrl->aux      = in->aux;
 	ctrl->link     = in->link;
 	ctrl->catalog  = in->catalog;
+	ctrl->dev  = in->dev;
 
 	dp_ctrl = &ctrl->dp_ctrl;
 
@@ -1367,6 +1504,8 @@
 	dp_ctrl->push_idle = dp_ctrl_push_idle;
 	dp_ctrl->abort     = dp_ctrl_abort;
 	dp_ctrl->isr       = dp_ctrl_isr;
+	dp_ctrl->reset	   = dp_ctrl_reset;
+	dp_ctrl->handle_sink_request = dp_ctrl_handle_sink_request;
 
 	return dp_ctrl;
 error:
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h
index 2ecfa0d..aaac0ab 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.h
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h
@@ -23,13 +23,15 @@
 #include "dp_catalog.h"
 
 struct dp_ctrl {
-	int (*init)(struct dp_ctrl *dp_ctrl, bool flip);
+	int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool multi_func);
 	void (*deinit)(struct dp_ctrl *dp_ctrl);
-	int (*on)(struct dp_ctrl *dp_ctrl, bool hpd_irq);
-	void (*off)(struct dp_ctrl *dp_ctrl, bool hpd_irq);
+	int (*on)(struct dp_ctrl *dp_ctrl);
+	void (*off)(struct dp_ctrl *dp_ctrl);
+	void (*reset)(struct dp_ctrl *dp_ctrl);
 	void (*push_idle)(struct dp_ctrl *dp_ctrl);
 	void (*abort)(struct dp_ctrl *dp_ctrl);
 	void (*isr)(struct dp_ctrl *dp_ctrl);
+	bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
 };
 
 struct dp_ctrl_in {
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c
index 82e34df..d00f159 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.c
+++ b/drivers/gpu/drm/msm/dp/dp_debug.c
@@ -29,6 +29,11 @@
 
 struct dp_debug_private {
 	struct dentry *root;
+	u8 *edid;
+	u32 edid_size;
+
+	u8 *dpcd;
+	u32 dpcd_size;
 
 	struct dp_usbpd *usbpd;
 	struct dp_link *link;
@@ -39,6 +44,138 @@
 	struct dp_debug dp_debug;
 };
 
+static ssize_t dp_debug_write_edid(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	u8 *buf = NULL, *buf_t = NULL, *edid = NULL;
+	const int char_to_nib = 2;
+	size_t edid_size = 0;
+	size_t size = 0, edid_buf_index = 0;
+	ssize_t rc = count;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		goto bail;
+
+	size = min_t(size_t, count, SZ_1K);
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	if (copy_from_user(buf, user_buff, size))
+		goto bail;
+
+	edid_size = size / char_to_nib;
+	buf_t = buf;
+
+	memset(debug->edid, 0, debug->edid_size);
+
+	if (edid_size != debug->edid_size) {
+		pr_debug("clearing debug edid\n");
+		goto bail;
+	}
+
+	while (edid_size--) {
+		char t[3];
+		int d;
+
+		memcpy(t, buf_t, sizeof(char) * char_to_nib);
+		t[char_to_nib] = '\0';
+
+		if (kstrtoint(t, 16, &d)) {
+			pr_err("kstrtoint error\n");
+			goto bail;
+		}
+
+		if (edid_buf_index < debug->edid_size)
+			debug->edid[edid_buf_index++] = d;
+
+		buf_t += char_to_nib;
+	}
+
+	print_hex_dump(KERN_DEBUG, "DEBUG EDID: ", DUMP_PREFIX_NONE,
+		16, 1, debug->edid, debug->edid_size, false);
+
+	edid = debug->edid;
+bail:
+	kfree(buf);
+	debug->panel->set_edid(debug->panel, edid);
+	return rc;
+}
+
+static ssize_t dp_debug_write_dpcd(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	u8 *buf = NULL, *buf_t = NULL, *dpcd = NULL;
+	const int char_to_nib = 2;
+	size_t dpcd_size = 0;
+	size_t size = 0, dpcd_buf_index = 0;
+	ssize_t rc = count;
+
+	pr_debug("count=%zu\n", count);
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		goto bail;
+
+	size = min_t(size_t, count, SZ_32);
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (!buf) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	if (copy_from_user(buf, user_buff, size))
+		goto bail;
+
+	dpcd_size = size / char_to_nib;
+	buf_t = buf;
+
+	memset(debug->dpcd, 0, debug->dpcd_size);
+
+	if (dpcd_size != debug->dpcd_size) {
+		pr_debug("clearing debug dpcd\n");
+		goto bail;
+	}
+
+	while (dpcd_size--) {
+		char t[3];
+		int d;
+
+		memcpy(t, buf_t, sizeof(char) * char_to_nib);
+		t[char_to_nib] = '\0';
+
+		if (kstrtoint(t, 16, &d)) {
+			pr_err("kstrtoint error\n");
+			goto bail;
+		}
+
+		if (dpcd_buf_index < debug->dpcd_size)
+			debug->dpcd[dpcd_buf_index++] = d;
+
+		buf_t += char_to_nib;
+	}
+
+	print_hex_dump(KERN_DEBUG, "DEBUG DPCD: ", DUMP_PREFIX_NONE,
+		8, 1, debug->dpcd, debug->dpcd_size, false);
+
+	dpcd = debug->dpcd;
+bail:
+	kfree(buf);
+	debug->panel->set_dpcd(debug->panel, dpcd);
+	return rc;
+}
+
 static ssize_t dp_debug_write_hpd(struct file *file,
 		const char __user *user_buff, size_t count, loff_t *ppos)
 {
@@ -63,9 +200,13 @@
 	if (kstrtoint(buf, 10, &hpd) != 0)
 		goto end;
 
-	debug->usbpd->connect(debug->usbpd, hpd);
+	hpd &= 0x3;
+
+	debug->dp_debug.psm_enabled = !!(hpd & BIT(1));
+
+	debug->usbpd->simulate_connect(debug->usbpd, !!(hpd & BIT(0)));
 end:
-	return -len;
+	return len;
 }
 
 static ssize_t dp_debug_write_edid_modes(struct file *file,
@@ -74,7 +215,7 @@
 	struct dp_debug_private *debug = file->private_data;
 	char buf[SZ_32];
 	size_t len = 0;
-	int hdisplay = 0, vdisplay = 0, vrefresh = 0;
+	int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio;
 
 	if (!debug)
 		return -ENODEV;
@@ -89,7 +230,8 @@
 
 	buf[len] = '\0';
 
-	if (sscanf(buf, "%d %d %d", &hdisplay, &vdisplay, &vrefresh) != 3)
+	if (sscanf(buf, "%d %d %d %d", &hdisplay, &vdisplay, &vrefresh,
+				&aspect_ratio) != 4)
 		goto clear;
 
 	if (!hdisplay || !vdisplay || !vrefresh)
@@ -99,6 +241,7 @@
 	debug->dp_debug.hdisplay = hdisplay;
 	debug->dp_debug.vdisplay = vdisplay;
 	debug->dp_debug.vrefresh = vrefresh;
+	debug->dp_debug.aspect_ratio = aspect_ratio;
 	goto end;
 clear:
 	pr_debug("clearing debug modes\n");
@@ -107,6 +250,78 @@
 	return len;
 }
 
+static ssize_t dp_debug_bw_code_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 max_bw_code = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		return 0;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &max_bw_code) != 0)
+		return 0;
+
+	if (!is_link_rate_valid(max_bw_code)) {
+		pr_err("Unsupported bw code %d\n", max_bw_code);
+		return len;
+	}
+	debug->panel->max_bw_code = max_bw_code;
+	pr_debug("max_bw_code: %d\n", max_bw_code);
+
+	return len;
+}
+
+static ssize_t dp_debug_tpg_write(struct file *file,
+		const char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	size_t len = 0;
+	u32 tpg_state = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	/* Leave room for termination char */
+	len = min_t(size_t, count, SZ_8 - 1);
+	if (copy_from_user(buf, user_buff, len))
+		goto bail;
+
+	buf[len] = '\0';
+
+	if (kstrtoint(buf, 10, &tpg_state) != 0)
+		goto bail;
+
+	tpg_state &= 0x1;
+	pr_debug("tpg_state: %d\n", tpg_state);
+
+	if (tpg_state == debug->dp_debug.tpg_state)
+		goto bail;
+
+	if (debug->panel)
+		debug->panel->tpg_config(debug->panel, tpg_state);
+
+	debug->dp_debug.tpg_state = tpg_state;
+bail:
+	return len;
+}
+
 static ssize_t dp_debug_read_connected(struct file *file,
 		char __user *user_buff, size_t count, loff_t *ppos)
 {
@@ -162,14 +377,16 @@
 		goto error;
 	}
 
+	mutex_lock(&connector->dev->mode_config.mutex);
 	list_for_each_entry(mode, &connector->modes, head) {
 		len += snprintf(buf + len, SZ_4K - len,
-		"%s %d %d %d %d %d %d %d %d %d 0x%x\n",
-		mode->name, mode->vrefresh, mode->hdisplay,
-		mode->hsync_start, mode->hsync_end, mode->htotal,
-		mode->vdisplay, mode->vsync_start, mode->vsync_end,
-		mode->vtotal, mode->flags);
+		"%s %d %d %d %d %d %d %d %d %d %d 0x%x\n",
+		mode->name, mode->vrefresh, mode->picture_aspect_ratio,
+		mode->hdisplay, mode->hsync_start, mode->hsync_end,
+		mode->htotal, mode->vdisplay, mode->vsync_start,
+		mode->vsync_end, mode->vtotal, mode->flags);
 	}
+	mutex_unlock(&connector->dev->mode_config.mutex);
 
 	if (copy_to_user(user_buff, buf, len)) {
 		kfree(buf);
@@ -306,33 +523,34 @@
 	/* Link Information */
 	rc = snprintf(buf + len, max_size,
 		"\tdp_link:\n\t\ttest_requested = %d\n",
-		debug->link->test_requested);
+		debug->link->sink_request);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
 	rc = snprintf(buf + len, max_size,
-		"\t\tlane_count = %d\n", debug->link->lane_count);
+		"\t\tlane_count = %d\n", debug->link->link_params.lane_count);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
 	rc = snprintf(buf + len, max_size,
-		"\t\tbw_code = %d\n", debug->link->bw_code);
+		"\t\tbw_code = %d\n", debug->link->link_params.bw_code);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
-	lclk = drm_dp_bw_code_to_link_rate(debug->link->bw_code) * 1000;
+	lclk = drm_dp_bw_code_to_link_rate(
+			debug->link->link_params.bw_code) * 1000;
 	rc = snprintf(buf + len, max_size,
 		"\t\tlclk = %lld\n", lclk);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
 	rc = snprintf(buf + len, max_size,
-		"\t\tv_level = %d\n", debug->link->v_level);
+		"\t\tv_level = %d\n", debug->link->phy_params.v_level);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
 	rc = snprintf(buf + len, max_size,
-		"\t\tp_level = %d\n", debug->link->p_level);
+		"\t\tp_level = %d\n", debug->link->phy_params.p_level);
 	if (dp_debug_check_buffer_overflow(rc, &max_size, &len))
 		goto error;
 
@@ -348,6 +566,58 @@
 	return -EINVAL;
 }
 
+static ssize_t dp_debug_bw_code_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char *buf;
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	len += snprintf(buf + len, (SZ_4K - len),
+			"max_bw_code = %d\n", debug->panel->max_bw_code);
+
+	if (copy_to_user(user_buff, buf, len)) {
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	*ppos += len;
+	kfree(buf);
+	return len;
+}
+
+static ssize_t dp_debug_tpg_read(struct file *file,
+	char __user *user_buff, size_t count, loff_t *ppos)
+{
+	struct dp_debug_private *debug = file->private_data;
+	char buf[SZ_8];
+	u32 len = 0;
+
+	if (!debug)
+		return -ENODEV;
+
+	if (*ppos)
+		return 0;
+
+	len += snprintf(buf, SZ_8, "%d\n", debug->dp_debug.tpg_state);
+
+	if (copy_to_user(user_buff, buf, len))
+		return -EFAULT;
+
+	*ppos += len;
+	return len;
+}
+
 static const struct file_operations dp_debug_fops = {
 	.open = simple_open,
 	.read = dp_debug_read_info,
@@ -364,28 +634,53 @@
 	.write = dp_debug_write_hpd,
 };
 
+static const struct file_operations edid_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_edid,
+};
+
+static const struct file_operations dpcd_fops = {
+	.open = simple_open,
+	.write = dp_debug_write_dpcd,
+};
+
 static const struct file_operations connected_fops = {
 	.open = simple_open,
 	.read = dp_debug_read_connected,
 };
 
+static const struct file_operations bw_code_fops = {
+	.open = simple_open,
+	.read = dp_debug_bw_code_read,
+	.write = dp_debug_bw_code_write,
+};
+
+static const struct file_operations tpg_fops = {
+	.open = simple_open,
+	.read = dp_debug_tpg_read,
+	.write = dp_debug_tpg_write,
+};
+
 static int dp_debug_init(struct dp_debug *dp_debug)
 {
 	int rc = 0;
 	struct dp_debug_private *debug = container_of(dp_debug,
 		struct dp_debug_private, dp_debug);
-	struct dentry *dir, *file, *edid_modes;
-	struct dentry *hpd, *connected;
-	struct dentry *root = debug->root;
+	struct dentry *dir, *file;
 
 	dir = debugfs_create_dir(DEBUG_NAME, NULL);
 	if (IS_ERR_OR_NULL(dir)) {
-		rc = PTR_ERR(dir);
+		if (!dir)
+			rc = -EINVAL;
+		else
+			rc = PTR_ERR(dir);
 		pr_err("[%s] debugfs create dir failed, rc = %d\n",
 		       DEBUG_NAME, rc);
 		goto error;
 	}
 
+	debug->root = dir;
+
 	file = debugfs_create_file("dp_debug", 0444, dir,
 				debug, &dp_debug_fops);
 	if (IS_ERR_OR_NULL(file)) {
@@ -395,37 +690,75 @@
 		goto error_remove_dir;
 	}
 
-	edid_modes = debugfs_create_file("edid_modes", 0644, dir,
+	file = debugfs_create_file("edid_modes", 0644, dir,
 					debug, &edid_modes_fops);
-	if (IS_ERR_OR_NULL(edid_modes)) {
-		rc = PTR_ERR(edid_modes);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
 		pr_err("[%s] debugfs create edid_modes failed, rc=%d\n",
 		       DEBUG_NAME, rc);
 		goto error_remove_dir;
 	}
 
-	hpd = debugfs_create_file("hpd", 0644, dir,
+	file = debugfs_create_file("hpd", 0644, dir,
 					debug, &hpd_fops);
-	if (IS_ERR_OR_NULL(hpd)) {
-		rc = PTR_ERR(hpd);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
 		pr_err("[%s] debugfs hpd failed, rc=%d\n",
 			DEBUG_NAME, rc);
 		goto error_remove_dir;
 	}
 
-	connected = debugfs_create_file("connected", 0444, dir,
+	file = debugfs_create_file("connected", 0444, dir,
 					debug, &connected_fops);
-	if (IS_ERR_OR_NULL(connected)) {
-		rc = PTR_ERR(connected);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
 		pr_err("[%s] debugfs connected failed, rc=%d\n",
 			DEBUG_NAME, rc);
 		goto error_remove_dir;
 	}
 
-	root = dir;
-	return rc;
+	file = debugfs_create_file("max_bw_code", 0644, dir,
+			debug, &bw_code_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs max_bw_code failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	file = debugfs_create_file("edid", 0644, dir,
+					debug, &edid_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs edid failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	file = debugfs_create_file("dpcd", 0644, dir,
+					debug, &dpcd_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs dpcd failed, rc=%d\n",
+			DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	file = debugfs_create_file("tpg_ctrl", 0644, dir,
+			debug, &tpg_fops);
+	if (IS_ERR_OR_NULL(file)) {
+		rc = PTR_ERR(file);
+		pr_err("[%s] debugfs tpg failed, rc=%d\n",
+		       DEBUG_NAME, rc);
+		goto error_remove_dir;
+	}
+
+	return 0;
+
 error_remove_dir:
-	debugfs_remove(dir);
+	if (!file)
+		rc = -EINVAL;
+	debugfs_remove_recursive(dir);
 error:
 	return rc;
 }
@@ -450,6 +783,24 @@
 		goto error;
 	}
 
+	debug->edid = devm_kzalloc(dev, SZ_256, GFP_KERNEL);
+	if (!debug->edid) {
+		rc = -ENOMEM;
+		kfree(debug);
+		goto error;
+	}
+
+	debug->edid_size = SZ_256;
+
+	debug->dpcd = devm_kzalloc(dev, SZ_16, GFP_KERNEL);
+	if (!debug->dpcd) {
+		rc = -ENOMEM;
+		kfree(debug);
+		goto error;
+	}
+
+	debug->dpcd_size = SZ_16;
+
 	debug->dp_debug.debug_en = false;
 	debug->usbpd = usbpd;
 	debug->link = link;
@@ -462,7 +813,11 @@
 	dp_debug->hdisplay = 0;
 	dp_debug->vrefresh = 0;
 
-	dp_debug_init(dp_debug);
+	rc = dp_debug_init(dp_debug);
+	if (rc) {
+		devm_kfree(dev, debug);
+		goto error;
+	}
 
 	return dp_debug;
 error:
@@ -478,7 +833,7 @@
 
 	debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
 
-	debugfs_remove(debug->root);
+	debugfs_remove_recursive(debug->root);
 
 	return 0;
 }
@@ -494,5 +849,7 @@
 
 	dp_debug_deinit(dp_debug);
 
-	kzfree(debug);
+	devm_kfree(debug->dev, debug->edid);
+	devm_kfree(debug->dev, debug->dpcd);
+	devm_kfree(debug->dev, debug);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_debug.h b/drivers/gpu/drm/msm/dp/dp_debug.h
index 7fd5330..3b2d23e 100644
--- a/drivers/gpu/drm/msm/dp/dp_debug.h
+++ b/drivers/gpu/drm/msm/dp/dp_debug.h
@@ -25,12 +25,16 @@
  * @vdisplay: used to filter out vdisplay value
  * @hdisplay: used to filter out hdisplay value
  * @vrefresh: used to filter out vrefresh value
+ * @tpg_state: specifies whether tpg feature is enabled
  */
 struct dp_debug {
 	bool debug_en;
+	bool psm_enabled;
+	int aspect_ratio;
 	int vdisplay;
 	int hdisplay;
 	int vrefresh;
+	bool tpg_state;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 7e40cc6..01a2a9c 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -37,6 +37,7 @@
 #include "dp_debug.h"
 
 static struct dp_display *g_dp_display;
+#define HPD_STRING_SIZE 30
 
 struct dp_hdcp {
 	void *data;
@@ -60,7 +61,6 @@
 	/* state variables */
 	bool core_initialized;
 	bool power_on;
-	bool hpd_irq_on;
 	bool audio_supported;
 
 	struct platform_device *pdev;
@@ -84,9 +84,12 @@
 	struct dp_display_mode mode;
 	struct dp_display dp_display;
 
-	struct workqueue_struct *hdcp_workqueue;
+	struct workqueue_struct *wq;
 	struct delayed_work hdcp_cb_work;
+	struct work_struct connect_work;
+	struct work_struct attention_work;
 	struct mutex hdcp_mutex;
+	struct mutex session_lock;
 	int hdcp_status;
 };
 
@@ -189,26 +192,13 @@
 	dp->hdcp_status = status;
 
 	if (dp->dp_display.is_connected)
-		queue_delayed_work(dp->hdcp_workqueue, &dp->hdcp_cb_work, HZ/4);
-}
-
-static int dp_display_create_hdcp_workqueue(struct dp_display_private *dp)
-{
-	dp->hdcp_workqueue = create_workqueue("sdm_dp_hdcp");
-	if (IS_ERR_OR_NULL(dp->hdcp_workqueue)) {
-		pr_err("Error creating hdcp_workqueue\n");
-		return -EPERM;
-	}
-
-	INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
-
-	return 0;
+		queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ/4);
 }
 
 static void dp_display_destroy_hdcp_workqueue(struct dp_display_private *dp)
 {
-	if (dp->hdcp_workqueue)
-		destroy_workqueue(dp->hdcp_workqueue);
+	if (dp->wq)
+		destroy_workqueue(dp->wq);
 }
 
 static void dp_display_update_hdcp_info(struct dp_display_private *dp)
@@ -275,7 +265,6 @@
 static int dp_display_initialize_hdcp(struct dp_display_private *dp)
 {
 	struct sde_hdcp_init_data hdcp_init_data;
-	struct resource *res;
 	int rc = 0;
 
 	if (!dp) {
@@ -285,29 +274,18 @@
 
 	mutex_init(&dp->hdcp_mutex);
 
-	rc = dp_display_create_hdcp_workqueue(dp);
-	if (rc) {
-		pr_err("Failed to create HDCP workqueue\n");
-		goto error;
-	}
-
-	res = platform_get_resource_byname(dp->pdev,
-		IORESOURCE_MEM, "dp_ctrl");
-	if (!res) {
-		pr_err("Error getting dp ctrl resource\n");
-		rc = -EINVAL;
-		goto error;
-	}
-
-	hdcp_init_data.phy_addr      = res->start;
 	hdcp_init_data.client_id     = HDCP_CLIENT_DP;
 	hdcp_init_data.drm_aux       = dp->aux->drm_aux;
 	hdcp_init_data.cb_data       = (void *)dp;
-	hdcp_init_data.workq         = dp->hdcp_workqueue;
+	hdcp_init_data.workq         = dp->wq;
 	hdcp_init_data.mutex         = &dp->hdcp_mutex;
 	hdcp_init_data.sec_access    = true;
 	hdcp_init_data.notify_status = dp_display_notify_hdcp_status_cb;
 	hdcp_init_data.core_io       = &dp->parser->io.ctrl_io;
+	hdcp_init_data.dp_ahb        = &dp->parser->io.dp_ahb;
+	hdcp_init_data.dp_aux        = &dp->parser->io.dp_aux;
+	hdcp_init_data.dp_link       = &dp->parser->io.dp_link;
+	hdcp_init_data.dp_p0         = &dp->parser->io.dp_p0;
 	hdcp_init_data.qfprom_io     = &dp->parser->io.qfprom_io;
 	hdcp_init_data.hdcp_io       = &dp->parser->io.hdcp_io;
 	hdcp_init_data.revision      = &dp->panel->link_info.revision;
@@ -361,24 +339,12 @@
 	dp->dp_display.drm_dev = drm;
 	priv = drm->dev_private;
 
-	rc = dp->parser->parse(dp->parser);
-	if (rc) {
-		pr_err("device tree parsing failed\n");
-		goto end;
-	}
-
 	rc = dp->aux->drm_aux_register(dp->aux);
 	if (rc) {
 		pr_err("DRM DP AUX register failed\n");
 		goto end;
 	}
 
-	rc = dp->panel->sde_edid_register(dp->panel);
-	if (rc) {
-		pr_err("DRM DP EDID register failed\n");
-		goto end;
-	}
-
 	rc = dp->power->power_client_init(dp->power, &priv->phandle);
 	if (rc) {
 		pr_err("Power client create failed\n");
@@ -412,7 +378,6 @@
 	}
 
 	(void)dp->power->power_client_deinit(dp->power);
-	(void)dp->panel->sde_edid_deregister(dp->panel);
 	(void)dp->aux->drm_aux_deregister(dp->aux);
 	dp_display_deinitialize_hdcp(dp);
 }
@@ -422,33 +387,124 @@
 	.unbind = dp_display_unbind,
 };
 
+static bool dp_display_is_ds_bridge(struct dp_panel *panel)
+{
+	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
+		DP_DWN_STRM_PORT_PRESENT);
+}
+
+static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
+{
+	return dp_display_is_ds_bridge(dp->panel) &&
+		(dp->link->sink_count.count == 0);
+}
+
+static void dp_display_send_hpd_event(struct dp_display *dp_display)
+{
+	struct drm_device *dev = NULL;
+	struct dp_display_private *dp;
+	struct drm_connector *connector;
+	char name[HPD_STRING_SIZE], status[HPD_STRING_SIZE],
+		bpp[HPD_STRING_SIZE], pattern[HPD_STRING_SIZE];
+	char *envp[5];
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+	if (!dp) {
+		pr_err("invalid params\n");
+		return;
+	}
+	connector = dp->dp_display.connector;
+	dev = dp_display->connector->dev;
+
+	connector->status = connector->funcs->detect(connector, false);
+	pr_debug("[%s] status updated to %s\n",
+			      connector->name,
+			      drm_get_connector_status_name(connector->status));
+	snprintf(name, HPD_STRING_SIZE, "name=%s", connector->name);
+	snprintf(status, HPD_STRING_SIZE, "status=%s",
+		drm_get_connector_status_name(connector->status));
+	snprintf(bpp, HPD_STRING_SIZE, "bpp=%d",
+		dp_link_bit_depth_to_bpp(
+		dp->link->test_video.test_bit_depth));
+	snprintf(pattern, HPD_STRING_SIZE, "pattern=%d",
+		dp->link->test_video.test_video_pattern);
+
+	pr_debug("generating hotplug event [%s]:[%s] [%s] [%s]\n",
+		name, status, bpp, pattern);
+	envp[0] = name;
+	envp[1] = status;
+	envp[2] = bpp;
+	envp[3] = pattern;
+	envp[4] = NULL;
+	kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
+			envp);
+}
+
+static int dp_display_send_hpd_notification(struct dp_display_private *dp,
+		bool hpd)
+{
+	dp->dp_display.is_connected = hpd;
+	reinit_completion(&dp->notification_comp);
+	dp_display_send_hpd_event(&dp->dp_display);
+
+	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 5)) {
+		pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
+		/* cancel any pending request */
+		dp->ctrl->abort(dp->ctrl);
+		dp->aux->abort(dp->aux);
+
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int dp_display_process_hpd_high(struct dp_display_private *dp)
 {
 	int rc = 0;
-	u32 max_pclk_from_edid = 0;
 	struct edid *edid;
 
+	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+	if (dp->debug->psm_enabled) {
+		dp->link->psm_config(dp->link, &dp->panel->link_info, false);
+		dp->debug->psm_enabled = false;
+	}
+
 	rc = dp->panel->read_sink_caps(dp->panel, dp->dp_display.connector);
-	if (rc)
-		return rc;
+	if (rc) {
+		if (rc == -ETIMEDOUT) {
+			pr_err("Sink cap read failed, skip notification\n");
+			goto end;
+		} else {
+			goto notify;
+		}
+	}
+
+	dp->link->process_request(dp->link);
+
+	if (dp_display_is_sink_count_zero(dp)) {
+		pr_debug("no downstream devices connected\n");
+		rc = -EINVAL;
+		goto end;
+	}
 
 	edid = dp->panel->edid_ctrl->edid;
 
 	dp->audio_supported = drm_detect_monitor_audio(edid);
 
-	max_pclk_from_edid = dp->panel->get_max_pclk(dp->panel);
+	dp->panel->handle_sink_request(dp->panel);
 
-	dp->dp_display.max_pclk_khz = min(max_pclk_from_edid,
-		dp->parser->max_pclk_khz);
+	dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;
+notify:
+	dp_display_send_hpd_notification(dp, true);
 
-	dp->dp_display.is_connected = true;
-
-	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);
-
-	reinit_completion(&dp->notification_comp);
-	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
-		pr_warn("timeout\n");
-
+end:
 	return rc;
 }
 
@@ -465,8 +521,7 @@
 		flip = true;
 
 	dp->power->init(dp->power, flip);
-	dp->ctrl->init(dp->ctrl, flip);
-	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+	dp->ctrl->init(dp->ctrl, flip, dp->usbpd->multi_func);
 	enable_irq(dp->irq);
 	dp->core_initialized = true;
 }
@@ -478,32 +533,34 @@
 		return;
 	}
 
-	dp->aux->deinit(dp->aux);
 	dp->ctrl->deinit(dp->ctrl);
 	dp->power->deinit(dp->power);
 	disable_irq(dp->irq);
 	dp->core_initialized = false;
 }
 
-static void dp_display_process_hpd_low(struct dp_display_private *dp)
+static int dp_display_process_hpd_low(struct dp_display_private *dp)
 {
-	/* cancel any pending request */
-	dp->ctrl->abort(dp->ctrl);
+	int rc = 0;
 
-	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
-		cancel_delayed_work_sync(&dp->hdcp_cb_work);
-		dp->hdcp.ops->off(dp->hdcp.data);
+	if (!dp->dp_display.is_connected) {
+		pr_debug("HPD already off\n");
+		return 0;
 	}
 
+	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->off)
+		dp->hdcp.ops->off(dp->hdcp.data);
+
 	if (dp->audio_supported)
 		dp->audio->off(dp->audio);
 
-	dp->dp_display.is_connected = false;
-	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);
+	rc = dp_display_send_hpd_notification(dp, false);
 
-	reinit_completion(&dp->notification_comp);
-	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
-		pr_warn("timeout\n");
+	dp->aux->deinit(dp->aux);
+
+	dp->panel->video_test = false;
+
+	return rc;
 }
 
 static int dp_display_usbpd_configure_cb(struct device *dev)
@@ -527,7 +584,7 @@
 	dp_display_host_init(dp);
 
 	if (dp->usbpd->hpd_high)
-		dp_display_process_hpd_high(dp);
+		queue_work(dp->wq, &dp->connect_work);
 end:
 	return rc;
 }
@@ -543,7 +600,26 @@
 	}
 
 	dp->ctrl->push_idle(dp->ctrl);
-	dp->ctrl->off(dp->ctrl, false);
+	dp->ctrl->off(dp->ctrl);
+	dp->power_on = false;
+}
+
+static int dp_display_handle_disconnect(struct dp_display_private *dp)
+{
+	int rc;
+
+	rc = dp_display_process_hpd_low(dp);
+
+	mutex_lock(&dp->session_lock);
+	if (rc && dp->power_on)
+		dp_display_clean(dp);
+
+	if (!dp->usbpd->alt_mode_cfg_done)
+		dp_display_host_deinit(dp);
+
+	mutex_unlock(&dp->session_lock);
+
+	return rc;
 }
 
 static int dp_display_usbpd_disconnect_cb(struct device *dev)
@@ -564,31 +640,70 @@
 		goto end;
 	}
 
+	if (dp->debug->psm_enabled)
+		dp->link->psm_config(dp->link, &dp->panel->link_info, true);
+
 	/* cancel any pending request */
 	dp->ctrl->abort(dp->ctrl);
+	dp->aux->abort(dp->aux);
 
-	if (dp->audio_supported)
-		dp->audio->off(dp->audio);
+	/* wait for idle state */
+	flush_workqueue(dp->wq);
 
-	dp->dp_display.is_connected = false;
-	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);
-
-	reinit_completion(&dp->notification_comp);
-	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) {
-		pr_warn("timeout\n");
-
-		if (dp->power_on)
-			dp_display_clean(dp);
-	}
-
-	dp_display_host_deinit(dp);
+	dp_display_handle_disconnect(dp);
 end:
 	return rc;
 }
 
+static void dp_display_attention_work(struct work_struct *work)
+{
+	bool req_handled;
+	struct dp_display_private *dp = container_of(work,
+			struct dp_display_private, attention_work);
+
+	if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) {
+		if (!dp->hdcp.ops->cp_irq(dp->hdcp.data))
+			return;
+	}
+
+	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
+		dp_display_handle_disconnect(dp);
+
+		if (dp_display_is_sink_count_zero(dp)) {
+			pr_debug("sink count is zero, nothing to do\n");
+			return;
+		}
+
+		queue_work(dp->wq, &dp->connect_work);
+		return;
+	}
+
+	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) {
+		dp_display_handle_disconnect(dp);
+
+		dp->panel->video_test = true;
+		dp_display_send_hpd_notification(dp, true);
+		dp->link->send_test_response(dp->link);
+
+		return;
+	}
+
+	mutex_lock(&dp->audio->ops_lock);
+	req_handled = dp->ctrl->handle_sink_request(dp->ctrl);
+	mutex_unlock(&dp->audio->ops_lock);
+
+	/*
+	 * reconfigure audio if test was executed
+	 * which could have changed the contoller's state
+	 */
+	if (req_handled && dp->audio_supported) {
+		dp->audio->off(dp->audio);
+		dp->audio->on(dp->audio);
+	}
+}
+
 static int dp_display_usbpd_attention_cb(struct device *dev)
 {
-	int rc = 0;
 	struct dp_display_private *dp;
 
 	if (!dev) {
@@ -602,29 +717,51 @@
 		return -ENODEV;
 	}
 
-	if (dp->usbpd->hpd_irq) {
-		dp->hpd_irq_on = true;
+	if (dp->usbpd->hpd_irq && dp->usbpd->hpd_high) {
+		dp->link->process_request(dp->link);
+		queue_work(dp->wq, &dp->attention_work);
+	} else if (dp->usbpd->hpd_high) {
+		queue_work(dp->wq, &dp->connect_work);
+	} else {
+		/* cancel any pending request */
+		dp->ctrl->abort(dp->ctrl);
+		dp->aux->abort(dp->aux);
 
-		if (dp_display_is_hdcp_enabled(dp) && dp->hdcp.ops->cp_irq) {
-			if (!dp->hdcp.ops->cp_irq(dp->hdcp.data))
-				goto end;
-		}
+		/* wait for idle state */
+		flush_workqueue(dp->wq);
 
-		rc = dp->link->process_request(dp->link);
-		dp->hpd_irq_on = false;
-		if (!rc)
-			goto end;
+		dp_display_handle_disconnect(dp);
 	}
 
-	if (!dp->usbpd->hpd_high) {
-		dp_display_process_hpd_low(dp);
-		goto end;
+	return 0;
+}
+
+static void dp_display_connect_work(struct work_struct *work)
+{
+	struct dp_display_private *dp = container_of(work,
+			struct dp_display_private, connect_work);
+
+	if (dp->dp_display.is_connected) {
+		pr_debug("HPD already on\n");
+		return;
 	}
 
-	if (dp->usbpd->alt_mode_cfg_done)
-		dp_display_process_hpd_high(dp);
-end:
-	return rc;
+	dp_display_process_hpd_high(dp);
+}
+
+static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
+{
+	dp_audio_put(dp->audio);
+	dp_ctrl_put(dp->ctrl);
+	dp_link_put(dp->link);
+	dp_panel_put(dp->panel);
+	dp_aux_put(dp->aux);
+	dp_power_put(dp->power);
+	dp_catalog_put(dp->catalog);
+	dp_parser_put(dp->parser);
+	dp_usbpd_put(dp->usbpd);
+	mutex_destroy(&dp->session_lock);
+	dp_debug_put(dp->debug);
 }
 
 static int dp_init_sub_modules(struct dp_display_private *dp)
@@ -635,6 +772,9 @@
 	struct dp_ctrl_in ctrl_in = {
 		.dev = dev,
 	};
+	struct dp_panel_in panel_in = {
+		.dev = dev,
+	};
 
 	cb->configure  = dp_display_usbpd_configure_cb;
 	cb->disconnect = dp_display_usbpd_disconnect_cb;
@@ -644,49 +784,68 @@
 	if (IS_ERR(dp->usbpd)) {
 		rc = PTR_ERR(dp->usbpd);
 		pr_err("failed to initialize usbpd, rc = %d\n", rc);
-		goto err;
+		dp->usbpd = NULL;
+		goto error;
 	}
 
+	mutex_init(&dp->session_lock);
+
 	dp->parser = dp_parser_get(dp->pdev);
 	if (IS_ERR(dp->parser)) {
 		rc = PTR_ERR(dp->parser);
 		pr_err("failed to initialize parser, rc = %d\n", rc);
-		goto err;
+		dp->parser = NULL;
+		goto error_parser;
+	}
+
+	rc = dp->parser->parse(dp->parser);
+	if (rc) {
+		pr_err("device tree parsing failed\n");
+		goto error_catalog;
 	}
 
 	dp->catalog = dp_catalog_get(dev, &dp->parser->io);
 	if (IS_ERR(dp->catalog)) {
 		rc = PTR_ERR(dp->catalog);
 		pr_err("failed to initialize catalog, rc = %d\n", rc);
-		goto err;
+		dp->catalog = NULL;
+		goto error_catalog;
 	}
 
 	dp->power = dp_power_get(dp->parser);
 	if (IS_ERR(dp->power)) {
 		rc = PTR_ERR(dp->power);
 		pr_err("failed to initialize power, rc = %d\n", rc);
-		goto err;
+		dp->power = NULL;
+		goto error_power;
 	}
 
 	dp->aux = dp_aux_get(dev, &dp->catalog->aux, dp->parser->aux_cfg);
 	if (IS_ERR(dp->aux)) {
 		rc = PTR_ERR(dp->aux);
 		pr_err("failed to initialize aux, rc = %d\n", rc);
-		goto err;
-	}
-
-	dp->panel = dp_panel_get(dev, dp->aux, &dp->catalog->panel);
-	if (IS_ERR(dp->panel)) {
-		rc = PTR_ERR(dp->panel);
-		pr_err("failed to initialize panel, rc = %d\n", rc);
-		goto err;
+		dp->aux = NULL;
+		goto error_aux;
 	}
 
 	dp->link = dp_link_get(dev, dp->aux);
 	if (IS_ERR(dp->link)) {
 		rc = PTR_ERR(dp->link);
 		pr_err("failed to initialize link, rc = %d\n", rc);
-		goto err;
+		dp->link = NULL;
+		goto error_link;
+	}
+
+	panel_in.aux = dp->aux;
+	panel_in.catalog = &dp->catalog->panel;
+	panel_in.link = dp->link;
+
+	dp->panel = dp_panel_get(&panel_in);
+	if (IS_ERR(dp->panel)) {
+		rc = PTR_ERR(dp->panel);
+		pr_err("failed to initialize panel, rc = %d\n", rc);
+		dp->panel = NULL;
+		goto error_panel;
 	}
 
 	ctrl_in.link = dp->link;
@@ -700,13 +859,16 @@
 	if (IS_ERR(dp->ctrl)) {
 		rc = PTR_ERR(dp->ctrl);
 		pr_err("failed to initialize ctrl, rc = %d\n", rc);
-		goto err;
+		dp->ctrl = NULL;
+		goto error_ctrl;
 	}
 
 	dp->audio = dp_audio_get(dp->pdev, dp->panel, &dp->catalog->audio);
 	if (IS_ERR(dp->audio)) {
 		rc = PTR_ERR(dp->audio);
 		pr_err("failed to initialize audio, rc = %d\n", rc);
+		dp->audio = NULL;
+		goto error_audio;
 	}
 
 	dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd,
@@ -714,29 +876,60 @@
 	if (IS_ERR(dp->debug)) {
 		rc = PTR_ERR(dp->debug);
 		pr_err("failed to initialize debug, rc = %d\n", rc);
-		goto err;
+		dp->debug = NULL;
+		goto error_debug;
 	}
-err:
+
+	return rc;
+error_debug:
+	dp_audio_put(dp->audio);
+error_audio:
+	dp_ctrl_put(dp->ctrl);
+error_ctrl:
+	dp_panel_put(dp->panel);
+error_panel:
+	dp_link_put(dp->link);
+error_link:
+	dp_aux_put(dp->aux);
+error_aux:
+	dp_power_put(dp->power);
+error_power:
+	dp_catalog_put(dp->catalog);
+error_catalog:
+	dp_parser_put(dp->parser);
+error_parser:
+	dp_usbpd_put(dp->usbpd);
+	mutex_destroy(&dp->session_lock);
+error:
 	return rc;
 }
 
 static int dp_display_set_mode(struct dp_display *dp_display,
 		struct dp_display_mode *mode)
 {
-	int rc = 0;
+	const u32 num_components = 3, default_bpp = 24;
 	struct dp_display_private *dp;
 
 	if (!dp_display) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto error;
+		return -EINVAL;
 	}
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
+	mutex_lock(&dp->session_lock);
+	mode->timing.bpp =
+		dp_display->connector->display_info.bpc * num_components;
+	if (!mode->timing.bpp)
+		mode->timing.bpp = default_bpp;
+
+	mode->timing.bpp = dp->panel->get_mode_bpp(dp->panel,
+			mode->timing.bpp, mode->timing.pixel_clk_khz);
+
 	dp->panel->pinfo = mode->timing;
-	dp->panel->init_info(dp->panel);
-error:
-	return rc;
+	dp->panel->init(dp->panel);
+	mutex_unlock(&dp->session_lock);
+
+	return 0;
 }
 
 static int dp_display_prepare(struct dp_display *dp)
@@ -751,39 +944,57 @@
 
 	if (!dp_display) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto error;
+		return -EINVAL;
 	}
 
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
-	rc = dp->ctrl->on(dp->ctrl, dp->hpd_irq_on);
+	mutex_lock(&dp->session_lock);
+
+	if (dp->power_on) {
+		pr_debug("Link already setup, return\n");
+		goto end;
+	}
+
+	dp->aux->init(dp->aux, dp->parser->aux_cfg);
+
+	rc = dp->ctrl->on(dp->ctrl);
+
+	if (dp->debug->tpg_state)
+		dp->panel->tpg_config(dp->panel, true);
+
 	if (!rc)
 		dp->power_on = true;
-error:
+end:
+	mutex_unlock(&dp->session_lock);
 	return rc;
 }
 
 static int dp_display_post_enable(struct dp_display *dp_display)
 {
-	int rc = 0;
 	struct dp_display_private *dp;
 
 	if (!dp_display) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto end;
+		return -EINVAL;
 	}
 
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
-	if (dp->audio_supported) {
-		dp->audio->bw_code = dp->link->bw_code;
-		dp->audio->lane_count = dp->link->lane_count;
-		dp->audio->on(dp->audio);
+	mutex_lock(&dp->session_lock);
+
+	if (!dp->power_on) {
+		pr_debug("Link not setup, return\n");
+		goto end;
 	}
 
-	complete_all(&dp->notification_comp);
+	dp->panel->spd_config(dp->panel);
+
+	if (dp->audio_supported) {
+		dp->audio->bw_code = dp->link->link_params.bw_code;
+		dp->audio->lane_count = dp->link->link_params.lane_count;
+		dp->audio->on(dp->audio);
+	}
 
 	dp_display_update_hdcp_info(dp);
 
@@ -791,26 +1002,35 @@
 		cancel_delayed_work_sync(&dp->hdcp_cb_work);
 
 		dp->hdcp_status = HDCP_STATE_AUTHENTICATING;
-		queue_delayed_work(dp->hdcp_workqueue,
-				&dp->hdcp_cb_work, HZ / 2);
+		queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ / 2);
 	}
 end:
-	return rc;
+	/* clear framework event notifier */
+	dp_display->send_hpd_event = NULL;
+
+	complete_all(&dp->notification_comp);
+	mutex_unlock(&dp->session_lock);
+	return 0;
 }
 
 static int dp_display_pre_disable(struct dp_display *dp_display)
 {
-	int rc = 0;
 	struct dp_display_private *dp;
 
 	if (!dp_display) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto error;
+		return -EINVAL;
 	}
 
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
+	mutex_lock(&dp->session_lock);
+
+	if (!dp->power_on) {
+		pr_debug("Link already powered off, return\n");
+		goto end;
+	}
+
 	if (dp_display_is_hdcp_enabled(dp)) {
 		dp->hdcp_status = HDCP_STATE_INACTIVE;
 
@@ -820,33 +1040,38 @@
 	}
 
 	dp->ctrl->push_idle(dp->ctrl);
-error:
-	return rc;
+end:
+	mutex_unlock(&dp->session_lock);
+	return 0;
 }
 
 static int dp_display_disable(struct dp_display *dp_display)
 {
-	int rc = 0;
 	struct dp_display_private *dp;
 
 	if (!dp_display) {
 		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto error;
+		return -EINVAL;
 	}
 
 	dp = container_of(dp_display, struct dp_display_private, dp_display);
 
-	if (!dp->power_on || !dp->core_initialized)
-		goto error;
+	mutex_lock(&dp->session_lock);
 
-	dp->ctrl->off(dp->ctrl, dp->hpd_irq_on);
+	if (!dp->power_on || !dp->core_initialized) {
+		pr_debug("Link already powered off, return\n");
+		goto end;
+	}
+
+	dp->ctrl->off(dp->ctrl);
+	dp->panel->deinit(dp->panel);
 
 	dp->power_on = false;
 
+end:
 	complete_all(&dp->notification_comp);
-error:
-	return rc;
+	mutex_unlock(&dp->session_lock);
+	return 0;
 }
 
 static int dp_request_irq(struct dp_display *dp_display)
@@ -899,25 +1124,88 @@
 	return 0;
 }
 
-static int dp_display_validate_mode(struct dp_display *dp,
-	struct dp_display_mode *mode)
+static int dp_display_validate_mode(struct dp_display *dp, u32 mode_pclk_khz)
 {
-	return 0;
+	const u32 num_components = 3, default_bpp = 24;
+	struct dp_display_private *dp_display;
+	struct drm_dp_link *link_info;
+	u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
+
+	if (!dp || !mode_pclk_khz) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	dp_display = container_of(dp, struct dp_display_private, dp_display);
+	link_info = &dp_display->panel->link_info;
+
+	mode_bpp = dp->connector->display_info.bpc * num_components;
+	if (!mode_bpp)
+		mode_bpp = default_bpp;
+
+	mode_bpp = dp_display->panel->get_mode_bpp(dp_display->panel,
+			mode_bpp, mode_pclk_khz);
+
+	mode_rate_khz = mode_pclk_khz * mode_bpp;
+	supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+	if (mode_rate_khz > supported_rate_khz)
+		return MODE_BAD;
+
+	return MODE_OK;
 }
 
-static int dp_display_get_modes(struct dp_display *dp)
+static int dp_display_get_modes(struct dp_display *dp,
+	struct dp_display_mode *dp_mode)
 {
-	int ret = 0;
 	struct dp_display_private *dp_display;
+	int ret = 0;
+
+	if (!dp) {
+		pr_err("invalid params\n");
+		return 0;
+	}
 
 	dp_display = container_of(dp, struct dp_display_private, dp_display);
 
-	ret = _sde_edid_update_modes(dp->connector,
-		dp_display->panel->edid_ctrl);
-
+	ret = dp_display->panel->get_modes(dp_display->panel,
+		dp->connector, dp_mode);
+	if (dp_mode->timing.pixel_clk_khz)
+		dp->max_pclk_khz = dp_mode->timing.pixel_clk_khz;
 	return ret;
 }
 
+
+static int dp_display_pre_kickoff(struct dp_display *dp_display,
+			struct drm_msm_ext_hdr_metadata *hdr)
+{
+	struct dp_display_private *dp;
+
+	if (!dp_display) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp = container_of(dp_display, struct dp_display_private, dp_display);
+
+	return dp->panel->setup_hdr(dp->panel, hdr);
+}
+
+static int dp_display_create_workqueue(struct dp_display_private *dp)
+{
+	dp->wq = create_singlethread_workqueue("drm_dp");
+	if (IS_ERR_OR_NULL(dp->wq)) {
+		pr_err("Error creating wq\n");
+		return -EPERM;
+	}
+
+	INIT_DELAYED_WORK(&dp->hdcp_cb_work, dp_display_hdcp_cb_work);
+	INIT_WORK(&dp->connect_work, dp_display_connect_work);
+	INIT_WORK(&dp->attention_work, dp_display_attention_work);
+
+	return 0;
+}
+
 static int dp_display_probe(struct platform_device *pdev)
 {
 	int rc = 0;
@@ -925,12 +1213,15 @@
 
 	if (!pdev || !pdev->dev.of_node) {
 		pr_err("pdev not found\n");
-		return -ENODEV;
+		rc = -ENODEV;
+		goto bail;
 	}
 
 	dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
-	if (!dp)
-		return -ENOMEM;
+	if (!dp) {
+		rc = -ENOMEM;
+		goto bail;
+	}
 
 	init_completion(&dp->notification_comp);
 
@@ -939,8 +1230,14 @@
 
 	rc = dp_init_sub_modules(dp);
 	if (rc) {
-		devm_kfree(&pdev->dev, dp);
-		return -EPROBE_DEFER;
+		rc = -EPROBE_DEFER;
+		goto err_dev;
+	}
+
+	rc = dp_display_create_workqueue(dp);
+	if (rc) {
+		pr_err("Failed to create workqueue\n");
+		goto err_sub_mod;
 	}
 
 	platform_set_drvdata(pdev, dp);
@@ -958,11 +1255,22 @@
 	g_dp_display->unprepare     = dp_display_unprepare;
 	g_dp_display->request_irq   = dp_request_irq;
 	g_dp_display->get_debug     = dp_get_debug;
+	g_dp_display->send_hpd_event    = dp_display_send_hpd_event;
+	g_dp_display->pre_kickoff   = dp_display_pre_kickoff;
 
 	rc = component_add(&pdev->dev, &dp_display_comp_ops);
-	if (rc)
+	if (rc) {
 		pr_err("component add failed, rc=%d\n", rc);
+		goto err_sub_mod;
+	}
 
+	return 0;
+
+err_sub_mod:
+	dp_display_deinit_sub_modules(dp);
+err_dev:
+	devm_kfree(&pdev->dev, dp);
+bail:
 	return rc;
 }
 
@@ -987,20 +1295,6 @@
 	return 1;
 }
 
-static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
-{
-	dp_audio_put(dp->audio);
-	dp_ctrl_put(dp->ctrl);
-	dp_link_put(dp->link);
-	dp_panel_put(dp->panel);
-	dp_aux_put(dp->aux);
-	dp_power_put(dp->power);
-	dp_catalog_put(dp->catalog);
-	dp_parser_put(dp->parser);
-	dp_usbpd_put(dp->usbpd);
-	dp_debug_put(dp->debug);
-}
-
 static int dp_display_remove(struct platform_device *pdev)
 {
 	struct dp_display_private *dp;
@@ -1039,7 +1333,7 @@
 
 	return ret;
 }
-module_init(dp_display_init);
+late_initcall(dp_display_init);
 
 static void __exit dp_display_cleanup(void)
 {
diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
index 5943629..2d314c7 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.h
+++ b/drivers/gpu/drm/msm/dp/dp_display.h
@@ -16,14 +16,10 @@
 #define _DP_DISPLAY_H_
 
 #include <drm/drmP.h>
+#include <drm/msm_drm.h>
 
 #include "dp_panel.h"
 
-struct dp_display_mode {
-	struct dp_panel_info timing;
-	u32 capabilities;
-};
-
 struct dp_display {
 	struct drm_device *drm_dev;
 	struct dp_bridge *bridge;
@@ -39,13 +35,16 @@
 
 	int (*set_mode)(struct dp_display *dp_display,
 			struct dp_display_mode *mode);
-	int (*validate_mode)(struct dp_display *dp_display,
-			struct dp_display_mode *mode);
-	int (*get_modes)(struct dp_display *dp_display);
+	int (*validate_mode)(struct dp_display *dp_display, u32 mode_pclk_khz);
+	int (*get_modes)(struct dp_display *dp_display,
+		struct dp_display_mode *dp_mode);
 	int (*prepare)(struct dp_display *dp_display);
 	int (*unprepare)(struct dp_display *dp_display);
 	int (*request_irq)(struct dp_display *dp_display);
 	struct dp_debug *(*get_debug)(struct dp_display *dp_display);
+	void (*send_hpd_event)(struct dp_display *dp_display);
+	int (*pre_kickoff)(struct dp_display *dp_display,
+				struct drm_msm_ext_hdr_metadata *hdr_meta);
 };
 
 int dp_display_get_num_of_displays(void);
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 802f295..2c29ad2 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -29,8 +29,6 @@
 static void convert_to_dp_mode(const struct drm_display_mode *drm_mode,
 			struct dp_display_mode *dp_mode, struct dp_display *dp)
 {
-	const u32 num_components = 3;
-
 	memset(dp_mode, 0, sizeof(*dp_mode));
 
 	dp_mode->timing.h_active = drm_mode->hdisplay;
@@ -48,7 +46,6 @@
 
 	dp_mode->timing.v_front_porch = drm_mode->vsync_start -
 					 drm_mode->vdisplay;
-	dp_mode->timing.bpp = dp->connector->display_info.bpc * num_components;
 
 	dp_mode->timing.refresh_rate = drm_mode->vrefresh;
 
@@ -246,7 +243,6 @@
 				  const struct drm_display_mode *mode,
 				  struct drm_display_mode *adjusted_mode)
 {
-	int rc = 0;
 	bool ret = true;
 	struct dp_display_mode dp_mode;
 	struct dp_bridge *bridge;
@@ -262,14 +258,7 @@
 	dp = bridge->display;
 
 	convert_to_dp_mode(mode, &dp_mode, dp);
-
-	rc = dp->validate_mode(dp, &dp_mode);
-	if (rc) {
-		pr_err("[%d] mode is not valid, rc=%d\n", bridge->id, rc);
-		ret = false;
-	} else {
-		convert_to_drm_mode(&dp_mode, adjusted_mode);
-	}
+	convert_to_drm_mode(&dp_mode, adjusted_mode);
 end:
 	return ret;
 }
@@ -284,13 +273,25 @@
 	.mode_set     = dp_bridge_mode_set,
 };
 
-int dp_connector_post_init(struct drm_connector *connector,
-		void *info,
-		void *display)
+int dp_connector_pre_kickoff(struct drm_connector *connector,
+		void *display,
+		struct msm_display_kickoff_params *params)
+{
+	struct dp_display *dp = display;
+
+	if (!connector || !display || !params) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	return dp->pre_kickoff(dp, params->hdr_meta);
+}
+
+int dp_connector_post_init(struct drm_connector *connector, void *display)
 {
 	struct dp_display *dp_display = display;
 
-	if (!info || !dp_display)
+	if (!dp_display)
 		return -EINVAL;
 
 	dp_display->connector = connector;
@@ -298,7 +299,7 @@
 }
 
 int dp_connector_get_mode_info(const struct drm_display_mode *drm_mode,
-	struct msm_mode_info *mode_info, u32 max_mixer_width)
+	struct msm_mode_info *mode_info, u32 max_mixer_width, void *display)
 {
 	const u32 dual_lm = 2;
 	const u32 single_lm = 1;
@@ -375,26 +376,65 @@
 	return status;
 }
 
+void dp_connector_send_hpd_event(void *display)
+{
+	struct dp_display *dp;
+
+	if (!display) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	dp = display;
+
+	if (dp->send_hpd_event)
+		dp->send_hpd_event(dp);
+}
+
 int dp_connector_get_modes(struct drm_connector *connector,
 		void *display)
 {
 	int rc = 0;
 	struct dp_display *dp;
+	struct dp_display_mode *dp_mode = NULL;
+	struct drm_display_mode *m, drm_mode;
 
 	if (!connector || !display)
-		return -EINVAL;
+		return 0;
 
 	dp = display;
+
+	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
+	if (!dp_mode)
+		return 0;
+
 	/* pluggable case assumes EDID is read when HPD */
 	if (dp->is_connected) {
-		rc = dp->get_modes(dp);
+		rc = dp->get_modes(dp, dp_mode);
 		if (!rc)
 			pr_err("failed to get DP sink modes, rc=%d\n", rc);
+
+		if (dp_mode->timing.pixel_clk_khz) { /* valid DP mode */
+			memset(&drm_mode, 0x0, sizeof(drm_mode));
+			convert_to_drm_mode(dp_mode, &drm_mode);
+			m = drm_mode_duplicate(connector->dev, &drm_mode);
+			if (!m) {
+				pr_err("failed to add mode %ux%u\n",
+				       drm_mode.hdisplay,
+				       drm_mode.vdisplay);
+				kfree(dp_mode);
+				return 0;
+			}
+			m->width_mm = connector->display_info.width_mm;
+			m->height_mm = connector->display_info.height_mm;
+			drm_mode_probed_add(connector, m);
+		}
 	} else {
 		pr_err("No sink connected\n");
 	}
+	kfree(dp_mode);
 
-	return 0;
+	return rc;
 }
 
 int dp_drm_bridge_init(void *data, struct drm_encoder *encoder)
@@ -467,18 +507,16 @@
 	dp_disp = display;
 	debug = dp_disp->get_debug(dp_disp);
 
-	if (debug->debug_en) {
-		if (mode->hdisplay == debug->hdisplay &&
-				mode->vdisplay == debug->vdisplay &&
-				mode->vrefresh == debug->vrefresh &&
-				mode->clock <= dp_disp->max_pclk_khz)
-			return MODE_OK;
-		else
-			return MODE_ERROR;
-	} else {
-		if (mode->clock > dp_disp->max_pclk_khz)
-			return MODE_BAD;
-		else
-			return MODE_OK;
-	}
+	mode->vrefresh = drm_mode_vrefresh(mode);
+
+	if (mode->clock > dp_disp->max_pclk_khz)
+		return MODE_BAD;
+
+	if (debug->debug_en && (mode->hdisplay != debug->hdisplay ||
+			mode->vdisplay != debug->vdisplay ||
+			mode->vrefresh != debug->vrefresh ||
+			mode->picture_aspect_ratio != debug->aspect_ratio))
+		return MODE_BAD;
+
+	return dp_disp->validate_mode(dp_disp, mode->clock);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 5918df1..1673212 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -32,15 +32,23 @@
 };
 
 /**
+ * dp_connector_pre_kickoff - callback to perform pre kickoff initialization
+ * @connector: Pointer to drm connector structure
+ * @display: Pointer to private display handle
+ * @params: Pointer to kickoff parameters
+ * Returns: Zero on success
+ */
+int dp_connector_pre_kickoff(struct drm_connector *connector,
+		void *display,
+		struct msm_display_kickoff_params *params);
+
+/**
  * dp_connector_post_init - callback to perform additional initialization steps
  * @connector: Pointer to drm connector structure
- * @info: Pointer to sde connector info structure
  * @display: Pointer to private display handle
  * Returns: Zero on success
  */
-int dp_connector_post_init(struct drm_connector *connector,
-		void *info,
-		void *display);
+int dp_connector_post_init(struct drm_connector *connector, void *display);
 
 /**
  * dp_connector_detect - callback to determine if connector is connected
@@ -78,14 +86,17 @@
  * @drm_mode: Display mode set for the display
  * @mode_info: Out parameter. Information of the mode
  * @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
  * Returns: zero on success
  */
 int dp_connector_get_mode_info(const struct drm_display_mode *drm_mode,
 		struct msm_mode_info *mode_info,
-		u32 max_mixer_width);
+		u32 max_mixer_width, void *display);
 
 int dp_connector_get_info(struct msm_display_info *info, void *display);
 
+void dp_connector_send_hpd_event(void *display);
+
 int dp_drm_bridge_init(void *display,
 	struct drm_encoder *encoder);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
index 061acee..0e1490f 100644
--- a/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
+++ b/drivers/gpu/drm/msm/dp/dp_hdcp2p2.c
@@ -27,6 +27,7 @@
 #define DP_INTR_STATUS3				(0x00000028)
 #define dp_read(offset) readl_relaxed((offset))
 #define dp_write(offset, data) writel_relaxed((data), (offset))
+#define DP_HDCP_RXCAPS_LENGTH 3
 
 enum dp_hdcp2p2_sink_status {
 	SINK_DISCONNECTED,
@@ -233,7 +234,7 @@
 
 static void dp_hdcp2p2_set_interrupts(struct dp_hdcp2p2_ctrl *ctrl, bool enable)
 {
-	void __iomem *base = ctrl->init_data.core_io->base;
+	void __iomem *base = ctrl->init_data.dp_ahb->base;
 	struct dp_hdcp2p2_interrupts *intr = ctrl->intr;
 
 	while (intr && intr->reg) {
@@ -739,13 +740,13 @@
 	struct dp_hdcp2p2_interrupts *intr;
 	u32 hdcp_int_val = 0;
 
-	if (!ctrl || !ctrl->init_data.core_io) {
+	if (!ctrl || !ctrl->init_data.dp_ahb) {
 		pr_err("invalid input\n");
 		rc = -EINVAL;
 		goto end;
 	}
 
-	io = ctrl->init_data.core_io;
+	io = ctrl->init_data.dp_ahb;
 	intr = ctrl->intr;
 
 	while (intr && intr->reg) {
@@ -893,21 +894,22 @@
 static bool dp_hdcp2p2_supported(struct dp_hdcp2p2_ctrl *ctrl)
 {
 	u32 const rxcaps_dpcd_offset = 0x6921d;
-	ssize_t const bytes_to_read = 1;
 	ssize_t bytes_read = 0;
-	u8 buf = 0;
+	u8 buf[DP_HDCP_RXCAPS_LENGTH];
 
 	bytes_read = drm_dp_dpcd_read(ctrl->init_data.drm_aux,
-			rxcaps_dpcd_offset, &buf, bytes_to_read);
-	if (bytes_read != bytes_to_read) {
+			rxcaps_dpcd_offset, &buf, DP_HDCP_RXCAPS_LENGTH);
+	if (bytes_read != DP_HDCP_RXCAPS_LENGTH) {
 		pr_err("RxCaps read failed\n");
 		goto error;
 	}
 
-	pr_debug("rxcaps 0x%x\n", buf);
+	pr_debug("HDCP_CAPABLE=%lu\n", (buf[2] & BIT(1)) >> 1);
+	pr_debug("VERSION=%d\n", buf[0]);
 
-	if (buf & BIT(1))
+	if ((buf[2] & BIT(1)) && (buf[0] == 0x2))
 		return true;
+
 error:
 	return false;
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_link.c b/drivers/gpu/drm/msm/dp/dp_link.c
index 48e6c8f..3ca247c 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.c
+++ b/drivers/gpu/drm/msm/dp/dp_link.c
@@ -17,14 +17,6 @@
 #include "dp_link.h"
 #include "dp_panel.h"
 
-#define DP_LINK_ENUM_STR(x)		#x
-
-enum dp_lane_count {
-	DP_LANE_COUNT_1	= 1,
-	DP_LANE_COUNT_2	= 2,
-	DP_LANE_COUNT_4	= 4,
-};
-
 enum dynamic_range {
 	DP_DYNAMIC_RANGE_RGB_VESA = 0x00,
 	DP_DYNAMIC_RANGE_RGB_CEA = 0x01,
@@ -50,111 +42,18 @@
 	u32 test_requested;
 	u32 test_link_rate;
 	u32 test_lane_count;
-	u32 phy_test_pattern_sel;
-	u32 test_video_pattern;
-	u32 test_bit_depth;
-	u32 test_dyn_range;
-	u32 test_h_total;
-	u32 test_v_total;
-	u32 test_h_start;
-	u32 test_v_start;
-	u32 test_hsync_pol;
-	u32 test_hsync_width;
-	u32 test_vsync_pol;
-	u32 test_vsync_width;
-	u32 test_h_width;
-	u32 test_v_height;
-	u32 test_rr_d;
-	u32 test_rr_n;
-	u32 test_audio_sampling_rate;
-	u32 test_audio_channel_count;
-	u32 test_audio_pattern_type;
-	u32 test_audio_period_ch_1;
-	u32 test_audio_period_ch_2;
-	u32 test_audio_period_ch_3;
-	u32 test_audio_period_ch_4;
-	u32 test_audio_period_ch_5;
-	u32 test_audio_period_ch_6;
-	u32 test_audio_period_ch_7;
-	u32 test_audio_period_ch_8;
-	u32 response;
-};
-
-struct dp_link_sink_count {
-	u32 count;
-	bool cp_ready;
 };
 
 struct dp_link_private {
+	u32 prev_sink_count;
 	struct device *dev;
 	struct dp_aux *aux;
 	struct dp_link dp_link;
 
 	struct dp_link_request request;
-	struct dp_link_sink_count sink_count;
 	u8 link_status[DP_LINK_STATUS_SIZE];
 };
 
-/**
- * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
- * @tbd: test bit depth
- *
- * Returns the bits per pixel (bpp) to be used corresponding to the
- * git bit depth value. This function assumes that bit depth has
- * already been validated.
- */
-static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
-{
-	u32 bpp;
-
-	/*
-	 * Few simplistic rules and assumptions made here:
-	 *    1. Bit depth is per color component
-	 *    2. If bit depth is unknown return 0
-	 *    3. Assume 3 color components
-	 */
-	switch (tbd) {
-	case DP_TEST_BIT_DEPTH_6:
-		bpp = 18;
-		break;
-	case DP_TEST_BIT_DEPTH_8:
-		bpp = 24;
-		break;
-	case DP_TEST_BIT_DEPTH_10:
-		bpp = 30;
-		break;
-	case DP_TEST_BIT_DEPTH_UNKNOWN:
-	default:
-		bpp = 0;
-	}
-
-	return bpp;
-}
-
-static char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
-{
-	switch (phy_test_pattern_sel) {
-	case DP_TEST_PHY_PATTERN_NONE:
-		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_NONE);
-	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
-		return DP_LINK_ENUM_STR(
-			DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING);
-	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
-		return DP_LINK_ENUM_STR(
-			DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
-	case DP_TEST_PHY_PATTERN_PRBS7:
-		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_PRBS7);
-	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
-		return DP_LINK_ENUM_STR(
-			DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN);
-	case DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN:
-		return DP_LINK_ENUM_STR(
-			DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN);
-	default:
-		return "unknown";
-	}
-}
-
 static char *dp_link_get_audio_test_pattern(u32 pattern)
 {
 	switch (pattern) {
@@ -223,7 +122,7 @@
 static int dp_link_parse_audio_channel_period(struct dp_link_private *link)
 {
 	int ret = 0;
-	struct dp_link_request *req = &link->request;
+	struct dp_link_test_audio *req = &link->dp_link.test_audio;
 
 	ret = dp_link_get_period(link, DP_TEST_AUDIO_PERIOD_CH1);
 	if (ret == -EINVAL)
@@ -310,7 +209,7 @@
 		goto exit;
 	}
 
-	link->request.test_audio_pattern_type = data;
+	link->dp_link.test_audio.test_audio_pattern_type = data;
 	pr_debug("audio pattern type = %s\n",
 			dp_link_get_audio_test_pattern(data));
 exit:
@@ -356,8 +255,8 @@
 		goto exit;
 	}
 
-	link->request.test_audio_sampling_rate = sampling_rate;
-	link->request.test_audio_channel_count = channel_count;
+	link->dp_link.test_audio.test_audio_sampling_rate = sampling_rate;
+	link->dp_link.test_audio.test_audio_channel_count = channel_count;
 	pr_debug("sampling_rate = %s, channel_count = 0x%x\n",
 		dp_link_get_audio_sample_rate(sampling_rate), channel_count);
 exit:
@@ -578,11 +477,11 @@
 		goto exit;
 	}
 
-	link->request.test_video_pattern = data;
+	link->dp_link.test_video.test_video_pattern = data;
 	pr_debug("link video pattern = 0x%x (%s)\n",
-		link->request.test_video_pattern,
+		link->dp_link.test_video.test_video_pattern,
 		dp_link_video_pattern_to_string(
-			link->request.test_video_pattern));
+			link->dp_link.test_video.test_video_pattern));
 
 	/* Read the requested color bit depth and dynamic range (Byte 0x232) */
 	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_TEST_MISC0,
@@ -601,11 +500,11 @@
 		ret = -EINVAL;
 		goto exit;
 	}
-	link->request.test_dyn_range = dyn_range;
+	link->dp_link.test_video.test_dyn_range = dyn_range;
 	pr_debug("link dynamic range = 0x%x (%s)\n",
-		link->request.test_dyn_range,
+		link->dp_link.test_video.test_dyn_range,
 		dp_link_dynamic_range_to_string(
-			link->request.test_dyn_range));
+			link->dp_link.test_video.test_dyn_range));
 
 	/* Color bit depth */
 	data &= DP_TEST_BIT_DEPTH_MASK;
@@ -615,128 +514,109 @@
 		goto exit;
 	}
 
-	link->request.test_bit_depth = data;
+	link->dp_link.test_video.test_bit_depth = data;
 	pr_debug("link bit depth = 0x%x (%s)\n",
-		link->request.test_bit_depth,
-		dp_link_bit_depth_to_string(link->request.test_bit_depth));
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_string(
+		link->dp_link.test_video.test_bit_depth));
 
 	/* resolution timing params */
 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_TOTAL_HI, 2,
-			&link->request.test_h_total);
+			&link->dp_link.test_video.test_h_total);
 	if (ret) {
 		pr_err("failed to parse test_h_total (DP_TEST_H_TOTAL_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_H_TOTAL = %d\n", link->request.test_h_total);
+	pr_debug("TEST_H_TOTAL = %d\n", link->dp_link.test_video.test_h_total);
 
 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_TOTAL_HI, 2,
-			&link->request.test_v_total);
+			&link->dp_link.test_video.test_v_total);
 	if (ret) {
 		pr_err("failed to parse test_v_total (DP_TEST_V_TOTAL_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_V_TOTAL = %d\n", link->request.test_v_total);
+	pr_debug("TEST_V_TOTAL = %d\n", link->dp_link.test_video.test_v_total);
 
 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_START_HI, 2,
-			&link->request.test_h_start);
+			&link->dp_link.test_video.test_h_start);
 	if (ret) {
 		pr_err("failed to parse test_h_start (DP_TEST_H_START_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_H_START = %d\n", link->request.test_h_start);
+	pr_debug("TEST_H_START = %d\n", link->dp_link.test_video.test_h_start);
 
 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_START_HI, 2,
-			&link->request.test_v_start);
+			&link->dp_link.test_video.test_v_start);
 	if (ret) {
 		pr_err("failed to parse test_v_start (DP_TEST_V_START_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_V_START = %d\n", link->request.test_v_start);
+	pr_debug("TEST_V_START = %d\n", link->dp_link.test_video.test_v_start);
 
 	ret = dp_link_parse_timing_params2(link, DP_TEST_HSYNC_HI, 2,
-			&link->request.test_hsync_pol,
-			&link->request.test_hsync_width);
+			&link->dp_link.test_video.test_hsync_pol,
+			&link->dp_link.test_video.test_hsync_width);
 	if (ret) {
 		pr_err("failed to parse (DP_TEST_HSYNC_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_HSYNC_POL = %d\n", link->request.test_hsync_pol);
-	pr_debug("TEST_HSYNC_WIDTH = %d\n", link->request.test_hsync_width);
+	pr_debug("TEST_HSYNC_POL = %d\n",
+		link->dp_link.test_video.test_hsync_pol);
+	pr_debug("TEST_HSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_hsync_width);
 
 	ret = dp_link_parse_timing_params2(link, DP_TEST_VSYNC_HI, 2,
-			&link->request.test_vsync_pol,
-			&link->request.test_vsync_width);
+			&link->dp_link.test_video.test_vsync_pol,
+			&link->dp_link.test_video.test_vsync_width);
 	if (ret) {
 		pr_err("failed to parse (DP_TEST_VSYNC_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_VSYNC_POL = %d\n", link->request.test_vsync_pol);
-	pr_debug("TEST_VSYNC_WIDTH = %d\n", link->request.test_vsync_width);
+	pr_debug("TEST_VSYNC_POL = %d\n",
+		link->dp_link.test_video.test_vsync_pol);
+	pr_debug("TEST_VSYNC_WIDTH = %d\n",
+		link->dp_link.test_video.test_vsync_width);
 
 	ret = dp_link_parse_timing_params1(link, DP_TEST_H_WIDTH_HI, 2,
-			&link->request.test_h_width);
+			&link->dp_link.test_video.test_h_width);
 	if (ret) {
 		pr_err("failed to parse test_h_width (DP_TEST_H_WIDTH_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_H_WIDTH = %d\n", link->request.test_h_width);
+	pr_debug("TEST_H_WIDTH = %d\n", link->dp_link.test_video.test_h_width);
 
 	ret = dp_link_parse_timing_params1(link, DP_TEST_V_HEIGHT_HI, 2,
-			&link->request.test_v_height);
+			&link->dp_link.test_video.test_v_height);
 	if (ret) {
 		pr_err("failed to parse test_v_height (DP_TEST_V_HEIGHT_HI)\n");
 		goto exit;
 	}
-	pr_debug("TEST_V_HEIGHT = %d\n", link->request.test_v_height);
+	pr_debug("TEST_V_HEIGHT = %d\n",
+		link->dp_link.test_video.test_v_height);
 
 	ret = dp_link_parse_timing_params3(link, DP_TEST_MISC1,
-		&link->request.test_rr_d);
-	link->request.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR;
+		&link->dp_link.test_video.test_rr_d);
+	link->dp_link.test_video.test_rr_d &= DP_TEST_REFRESH_DENOMINATOR;
 	if (ret) {
 		pr_err("failed to parse test_rr_d (DP_TEST_MISC1)\n");
 		goto exit;
 	}
-	pr_debug("TEST_REFRESH_DENOMINATOR = %d\n", link->request.test_rr_d);
+	pr_debug("TEST_REFRESH_DENOMINATOR = %d\n",
+		link->dp_link.test_video.test_rr_d);
 
 	ret = dp_link_parse_timing_params3(link, DP_TEST_REFRESH_RATE_NUMERATOR,
-		&link->request.test_rr_n);
+		&link->dp_link.test_video.test_rr_n);
 	if (ret) {
 		pr_err("failed to parse test_rr_n (DP_TEST_REFRESH_RATE_NUMERATOR)\n");
 		goto exit;
 	}
-	pr_debug("TEST_REFRESH_NUMERATOR = %d\n", link->request.test_rr_n);
+	pr_debug("TEST_REFRESH_NUMERATOR = %d\n",
+		link->dp_link.test_video.test_rr_n);
 exit:
 	return ret;
 }
 
 /**
- * dp_link_is_link_rate_valid() - validates the link rate
- * @lane_rate: link rate requested by the sink
- *
- * Returns true if the requested link rate is supported.
- */
-static bool dp_link_is_link_rate_valid(u32 bw_code)
-{
-	return ((bw_code == DP_LINK_BW_1_62) ||
-		(bw_code == DP_LINK_BW_2_7) ||
-		(bw_code == DP_LINK_BW_5_4) ||
-		(bw_code == DP_LINK_RATE_810));
-}
-
-/**
- * dp_link_is_lane_count_valid() - validates the lane count
- * @lane_count: lane count requested by the sink
- *
- * Returns true if the requested lane count is supported.
- */
-static bool dp_link_is_lane_count_valid(u32 lane_count)
-{
-	return (lane_count == DP_LANE_COUNT_1) ||
-		(lane_count == DP_LANE_COUNT_2) ||
-		(lane_count == DP_LANE_COUNT_4);
-}
-
-/**
  * dp_link_parse_link_training_params() - parses link training parameters from
  * DPCD
  * @link: Display Port Driver data
@@ -761,7 +641,7 @@
 	}
 	data = bp;
 
-	if (!dp_link_is_link_rate_valid(data)) {
+	if (!is_link_rate_valid(data)) {
 		pr_err("invalid link rate = 0x%x\n", data);
 		ret = -EINVAL;
 		goto exit;
@@ -780,7 +660,7 @@
 	data = bp;
 	data &= 0x1F;
 
-	if (!dp_link_is_lane_count_valid(data)) {
+	if (!is_lane_count_valid(data)) {
 		pr_err("invalid lane count = 0x%x\n", data);
 		ret = -EINVAL;
 		goto exit;
@@ -800,7 +680,8 @@
 	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
 	case DP_TEST_PHY_PATTERN_PRBS7:
 	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
-	case DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN:
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_1:
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_3:
 		return true;
 	default:
 		return false;
@@ -832,7 +713,7 @@
 
 	data = bp;
 
-	link->request.phy_test_pattern_sel = data;
+	link->dp_link.phy_params.phy_test_pattern_sel = data;
 
 	pr_debug("phy_test_pattern_sel = %s\n",
 			dp_link_get_phy_test_pattern(data));
@@ -891,6 +772,11 @@
 		dp_link_is_video_audio_test_requested(test_requested);
 }
 
+static bool dp_link_is_test_edid_read(struct dp_link_private *link)
+{
+	return (link->request.test_requested == DP_TEST_LINK_EDID_READ);
+}
+
 /**
  * dp_sink_parse_test_request() - parses link request parameters from sink
  * @link: Display Port Driver data
@@ -972,10 +858,15 @@
 	 * Send a DP_TEST_ACK if all link parameters are valid, otherwise send
 	 * a DP_TEST_NAK.
 	 */
-	if (ret)
-		link->request.response = DP_TEST_NAK;
-	else
-		link->request.response = DP_TEST_ACK;
+	if (ret) {
+		link->dp_link.test_response = DP_TEST_NAK;
+	} else {
+		if (!dp_link_is_test_edid_read(link))
+			link->dp_link.test_response = DP_TEST_ACK;
+		else
+			link->dp_link.test_response =
+				DP_TEST_EDID_CHECKSUM_WRITE;
+	}
 
 	return ret;
 }
@@ -987,46 +878,49 @@
  * (Byte 0x200), and whether all the sink devices connected have Content
  * Protection enabled.
  */
-static void dp_link_parse_sink_count(struct dp_link_private *link)
+static int dp_link_parse_sink_count(struct dp_link *dp_link)
 {
-	u8 bp;
-	u8 data;
 	int rlen;
 	int const param_len = 0x1;
+	struct dp_link_private *link = container_of(dp_link,
+			struct dp_link_private, dp_link);
 
 	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_SINK_COUNT,
-			&bp, param_len);
+			&link->dp_link.sink_count.count, param_len);
 	if (rlen < param_len) {
 		pr_err("failed to read sink count\n");
-		return;
+		return -EINVAL;
 	}
 
-	data = bp;
-
+	link->dp_link.sink_count.cp_ready =
+		link->dp_link.sink_count.count & DP_SINK_CP_READY;
 	/* BIT 7, BIT 5:0 */
-	link->sink_count.count = (data & BIT(7)) << 6 | (data & 0x63);
-	/* BIT 6*/
-	link->sink_count.cp_ready = data & BIT(6);
+	link->dp_link.sink_count.count =
+		DP_GET_SINK_COUNT(link->dp_link.sink_count.count);
 
 	pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n",
-		link->sink_count.count, link->sink_count.cp_ready);
+		link->dp_link.sink_count.count,
+		link->dp_link.sink_count.cp_ready);
+	return 0;
 }
 
 static void dp_link_parse_sink_status_field(struct dp_link_private *link)
 {
 	int len = 0;
 
-	dp_link_parse_sink_count(link);
-	dp_link_parse_request(link);
+	link->prev_sink_count = link->dp_link.sink_count.count;
+	dp_link_parse_sink_count(&link->dp_link);
+
 	len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
 		link->link_status);
 	if (len < DP_LINK_STATUS_SIZE)
 		pr_err("DP link status read failed\n");
+	dp_link_parse_request(link);
 }
 
 static bool dp_link_is_link_training_requested(struct dp_link_private *link)
 {
-	return (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN);
+	return (link->request.test_requested == DP_TEST_LINK_TRAINING);
 }
 
 /**
@@ -1046,22 +940,71 @@
 		return -EINVAL;
 
 	pr_debug("%s link rate = 0x%x, lane count = 0x%x\n",
-			dp_link_get_test_name(DP_TEST_LINK_PHY_TEST_PATTERN),
+			dp_link_get_test_name(DP_TEST_LINK_TRAINING),
 			link->request.test_link_rate,
 			link->request.test_lane_count);
 
-	link->dp_link.lane_count = link->request.test_lane_count;
-	link->dp_link.bw_code = link->request.test_link_rate;
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
 
 	return 0;
 }
 
-static bool dp_link_phy_pattern_requested(struct dp_link *dp_link)
+static void dp_link_send_test_response(struct dp_link *dp_link)
 {
-	struct dp_link_private *link = container_of(dp_link,
-			struct dp_link_private, dp_link);
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
 
-	return (link->request.test_requested == DP_TEST_LINK_PHY_TEST_PATTERN);
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_RESPONSE,
+			&dp_link->test_response, response_len);
+}
+
+static int dp_link_psm_config(struct dp_link *dp_link,
+	struct drm_dp_link *link_info, bool enable)
+{
+	struct dp_link_private *link = NULL;
+	int ret = 0;
+
+	if (!dp_link) {
+		pr_err("invalid params\n");
+		return -EINVAL;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	if (enable)
+		ret = drm_dp_link_power_down(link->aux->drm_aux, link_info);
+	else
+		ret = drm_dp_link_power_up(link->aux->drm_aux, link_info);
+
+	if (ret)
+		pr_err("Failed to %s low power mode\n",
+			(enable ? "enter" : "exit"));
+
+	return ret;
+}
+
+static void dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum)
+{
+	struct dp_link_private *link = NULL;
+	u32 const response_len = 0x1;
+
+	if (!dp_link) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	link = container_of(dp_link, struct dp_link_private, dp_link);
+
+	drm_dp_dpcd_write(link->aux->drm_aux, DP_TEST_EDID_CHECKSUM,
+			&checksum, response_len);
 }
 
 static int dp_link_parse_vx_px(struct dp_link_private *link)
@@ -1127,10 +1070,11 @@
 	 * vector.
 	 */
 	pr_debug("Current: v_level = 0x%x, p_level = 0x%x\n",
-			link->dp_link.v_level, link->dp_link.p_level);
+			link->dp_link.phy_params.v_level,
+			link->dp_link.phy_params.p_level);
 	pr_debug("Requested: v_level = 0x%x, p_level = 0x%x\n", v0, p0);
-	link->dp_link.v_level = v0;
-	link->dp_link.p_level = p0;
+	link->dp_link.phy_params.v_level = v0;
+	link->dp_link.phy_params.p_level = p0;
 
 	pr_debug("Success\n");
 end:
@@ -1150,14 +1094,16 @@
 {
 	u32 test_link_rate = 0, test_lane_count = 0;
 
-	if (!dp_link_phy_pattern_requested(&link->dp_link))
+	if (!(link->request.test_requested & DP_TEST_LINK_PHY_TEST_PATTERN)) {
+		pr_debug("no phy test\n");
 		return -EINVAL;
+	}
 
 	test_link_rate = link->request.test_link_rate;
 	test_lane_count = link->request.test_lane_count;
 
-	if (!dp_link_is_link_rate_valid(test_link_rate) ||
-		!dp_link_is_lane_count_valid(test_lane_count)) {
+	if (!is_link_rate_valid(test_link_rate) ||
+		!is_lane_count_valid(test_lane_count)) {
 		pr_err("Invalid params: link rate = 0x%x, lane count = 0x%x\n",
 				test_link_rate, test_lane_count);
 		return -EINVAL;
@@ -1165,8 +1111,15 @@
 
 	pr_debug("start\n");
 
-	link->dp_link.lane_count = link->request.test_lane_count;
-	link->dp_link.bw_code = link->request.test_link_rate;
+	pr_info("Current: bw_code = 0x%x, lane count = 0x%x\n",
+			link->dp_link.link_params.bw_code,
+			link->dp_link.link_params.lane_count);
+
+	pr_info("Requested: bw_code = 0x%x, lane count = 0x%x\n",
+			test_link_rate, test_lane_count);
+
+	link->dp_link.link_params.lane_count = link->request.test_lane_count;
+	link->dp_link.link_params.bw_code = link->request.test_link_rate;
 
 	dp_link_parse_vx_px(link);
 
@@ -1196,16 +1149,16 @@
 	if (!(get_link_status(link->link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
 		DP_LINK_STATUS_UPDATED) || /* link status updated */
 		(drm_dp_clock_recovery_ok(link->link_status,
-			link->dp_link.lane_count) &&
+			link->dp_link.link_params.lane_count) &&
 	     drm_dp_channel_eq_ok(link->link_status,
-			link->dp_link.lane_count)))
+			link->dp_link.link_params.lane_count)))
 		return -EINVAL;
 
 	pr_debug("channel_eq_done = %d, clock_recovery_done = %d\n",
 			drm_dp_clock_recovery_ok(link->link_status,
-			link->dp_link.lane_count),
+			link->dp_link.link_params.lane_count),
 			drm_dp_clock_recovery_ok(link->link_status,
-			link->dp_link.lane_count));
+			link->dp_link.link_params.lane_count));
 
 	return 0;
 }
@@ -1216,6 +1169,9 @@
 		DP_DOWNSTREAM_PORT_STATUS_CHANGED) /* port status changed */
 		return true;
 
+	if (link->prev_sink_count != link->dp_link.sink_count.count)
+		return true;
+
 	return false;
 }
 
@@ -1235,6 +1191,9 @@
 	if (!dp_link_is_ds_port_status_changed(link))
 		return -EINVAL;
 
+	/* reset prev_sink_count */
+	link->prev_sink_count = link->dp_link.sink_count.count;
+
 	return 0;
 }
 
@@ -1267,10 +1226,11 @@
 
 	pr_debug("%s: bit depth=%d(%d bpp) pattern=%s\n",
 		dp_link_get_test_name(DP_TEST_LINK_VIDEO_PATTERN),
-		link->request.test_bit_depth,
-		dp_link_bit_depth_to_bpp(link->request.test_bit_depth),
+		link->dp_link.test_video.test_bit_depth,
+		dp_link_bit_depth_to_bpp(
+		link->dp_link.test_video.test_bit_depth),
 		dp_link_video_pattern_to_string(
-			link->request.test_video_pattern));
+			link->dp_link.test_video.test_video_pattern));
 
 	return 0;
 end:
@@ -1293,22 +1253,22 @@
 
 	pr_debug("sampling_rate=%s, channel_count=%d, pattern_type=%s\n",
 		dp_link_get_audio_sample_rate(
-			link->request.test_audio_sampling_rate),
-		link->request.test_audio_channel_count,
+			link->dp_link.test_audio.test_audio_sampling_rate),
+		link->dp_link.test_audio.test_audio_channel_count,
 		dp_link_get_audio_test_pattern(
-			link->request.test_audio_pattern_type));
+			link->dp_link.test_audio.test_audio_pattern_type));
 
 	pr_debug("audio_period: ch1=0x%x, ch2=0x%x, ch3=0x%x, ch4=0x%x\n",
-		link->request.test_audio_period_ch_1,
-		link->request.test_audio_period_ch_2,
-		link->request.test_audio_period_ch_3,
-		link->request.test_audio_period_ch_4);
+		link->dp_link.test_audio.test_audio_period_ch_1,
+		link->dp_link.test_audio.test_audio_period_ch_2,
+		link->dp_link.test_audio.test_audio_period_ch_3,
+		link->dp_link.test_audio.test_audio_period_ch_4);
 
 	pr_debug("audio_period: ch5=0x%x, ch6=0x%x, ch7=0x%x, ch8=0x%x\n",
-		link->request.test_audio_period_ch_5,
-		link->request.test_audio_period_ch_6,
-		link->request.test_audio_period_ch_7,
-		link->request.test_audio_period_ch_8);
+		link->dp_link.test_audio.test_audio_period_ch_5,
+		link->dp_link.test_audio.test_audio_period_ch_6,
+		link->dp_link.test_audio.test_audio_period_ch_7,
+		link->dp_link.test_audio.test_audio_period_ch_8);
 
 	return 0;
 }
@@ -1316,8 +1276,12 @@
 static void dp_link_reset_data(struct dp_link_private *link)
 {
 	link->request = (const struct dp_link_request){ 0 };
-	link->request.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
-	link->dp_link.test_requested = 0;
+	link->dp_link.test_video = (const struct dp_link_test_video){ 0 };
+	link->dp_link.test_video.test_bit_depth = DP_TEST_BIT_DEPTH_UNKNOWN;
+	link->dp_link.test_audio = (const struct dp_link_test_audio){ 0 };
+	link->dp_link.phy_params.phy_test_pattern_sel = 0;
+	link->dp_link.sink_request = 0;
+	link->dp_link.test_response = 0;
 }
 
 /**
@@ -1346,39 +1310,44 @@
 
 	dp_link_parse_sink_status_field(link);
 
-	ret = dp_link_process_link_training_request(link);
-	if (!ret) {
-		dp_link->test_requested |= DP_TEST_LINK_TRAINING;
-		goto exit;
-	}
-
-	ret = dp_link_process_phy_test_pattern_request(link);
-	if (!ret) {
-		dp_link->test_requested |= DP_TEST_LINK_PHY_TEST_PATTERN;
-		goto exit;
-	}
-
-	ret = dp_link_process_link_status_update(link);
-	if (!ret) {
-		dp_link->test_requested |= DP_LINK_STATUS_UPDATED;
+	if (dp_link_is_test_edid_read(link)) {
+		dp_link->sink_request |= DP_TEST_LINK_EDID_READ;
 		goto exit;
 	}
 
 	ret = dp_link_process_ds_port_status_change(link);
 	if (!ret) {
-		dp_link->test_requested |= DS_PORT_STATUS_CHANGED;
+		dp_link->sink_request |= DS_PORT_STATUS_CHANGED;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_training_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_TRAINING;
+		goto exit;
+	}
+
+	ret = dp_link_process_phy_test_pattern_request(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_TEST_LINK_PHY_TEST_PATTERN;
+		goto exit;
+	}
+
+	ret = dp_link_process_link_status_update(link);
+	if (!ret) {
+		dp_link->sink_request |= DP_LINK_STATUS_UPDATED;
 		goto exit;
 	}
 
 	ret = dp_link_process_video_pattern_request(link);
 	if (!ret) {
-		dp_link->test_requested |= DP_TEST_LINK_VIDEO_PATTERN;
+		dp_link->sink_request |= DP_TEST_LINK_VIDEO_PATTERN;
 		goto exit;
 	}
 
 	ret = dp_link_process_audio_pattern_request(link);
 	if (!ret) {
-		dp_link->test_requested |= DP_TEST_LINK_AUDIO_PATTERN;
+		dp_link->sink_request |= DP_TEST_LINK_AUDIO_PATTERN;
 		goto exit;
 	}
 
@@ -1402,7 +1371,7 @@
 
 	/* unless a video pattern CTS test is ongoing, use CEA_VESA */
 	if (dp_link_is_video_pattern_requested(link))
-		dr = link->request.test_dyn_range;
+		dr = link->dp_link.test_video.test_dyn_range;
 	else
 		dr = DP_DYNAMIC_RANGE_RGB_VESA;
 
@@ -1434,51 +1403,52 @@
 	link = container_of(dp_link, struct dp_link_private, dp_link);
 
 	/* use the max level across lanes */
-	for (i = 0; i < dp_link->lane_count; i++) {
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
 		data = drm_dp_get_adjust_request_voltage(link_status, i);
 		pr_debug("lane=%d req_voltage_swing=%d\n", i, data);
 		if (max < data)
 			max = data;
 	}
 
-	dp_link->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	dp_link->phy_params.v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
 
 	/* use the max level across lanes */
 	max = 0;
-	for (i = 0; i < dp_link->lane_count; i++) {
+	for (i = 0; i < dp_link->link_params.lane_count; i++) {
 		data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
 		pr_debug("lane=%d req_pre_emphasis=%d\n", i, data);
 		if (max < data)
 			max = data;
 	}
 
-	dp_link->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+	dp_link->phy_params.p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
 
 	/**
 	 * Adjust the voltage swing and pre-emphasis level combination to within
 	 * the allowable range.
 	 */
-	if (dp_link->v_level > DP_LINK_VOLTAGE_MAX) {
+	if (dp_link->phy_params.v_level > DP_LINK_VOLTAGE_MAX) {
 		pr_debug("Requested vSwingLevel=%d, change to %d\n",
-				dp_link->v_level, DP_LINK_VOLTAGE_MAX);
-		dp_link->v_level = DP_LINK_VOLTAGE_MAX;
+			dp_link->phy_params.v_level, DP_LINK_VOLTAGE_MAX);
+		dp_link->phy_params.v_level = DP_LINK_VOLTAGE_MAX;
 	}
 
-	if (dp_link->p_level > DP_LINK_PRE_EMPHASIS_MAX) {
+	if (dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_MAX) {
 		pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
-				dp_link->p_level, DP_LINK_PRE_EMPHASIS_MAX);
-		dp_link->p_level = DP_LINK_PRE_EMPHASIS_MAX;
+			dp_link->phy_params.p_level, DP_LINK_PRE_EMPHASIS_MAX);
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_MAX;
 	}
 
-	if ((dp_link->p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
-			&& (dp_link->v_level == DP_LINK_VOLTAGE_LEVEL_2)) {
+	if ((dp_link->phy_params.p_level > DP_LINK_PRE_EMPHASIS_LEVEL_1)
+		&& (dp_link->phy_params.v_level == DP_LINK_VOLTAGE_LEVEL_2)) {
 		pr_debug("Requested preEmphasisLevel=%d, change to %d\n",
-				dp_link->p_level, DP_LINK_PRE_EMPHASIS_LEVEL_1);
-		dp_link->p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
+			dp_link->phy_params.p_level,
+			DP_LINK_PRE_EMPHASIS_LEVEL_1);
+		dp_link->phy_params.p_level = DP_LINK_PRE_EMPHASIS_LEVEL_1;
 	}
 
 	pr_debug("adjusted: v_level=%d, p_level=%d\n",
-		dp_link->v_level, dp_link->p_level);
+		dp_link->phy_params.v_level, dp_link->phy_params.p_level);
 
 	return 0;
 }
@@ -1555,7 +1525,9 @@
 	dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
 	dp_link->adjust_levels          = dp_link_adjust_levels;
 	dp_link->send_psm_request       = dp_link_send_psm_request;
-	dp_link->phy_pattern_requested  = dp_link_phy_pattern_requested;
+	dp_link->send_test_response     = dp_link_send_test_response;
+	dp_link->psm_config             = dp_link_psm_config;
+	dp_link->send_edid_checksum     = dp_link_send_edid_checksum;
 
 	return dp_link;
 error:
diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h
index 419186f..6f79b6a 100644
--- a/drivers/gpu/drm/msm/dp/dp_link.h
+++ b/drivers/gpu/drm/msm/dp/dp_link.h
@@ -17,6 +17,10 @@
 
 #include "dp_aux.h"
 
+#define DS_PORT_STATUS_CHANGED 0x200
+#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
+#define DP_LINK_ENUM_STR(x)		#x
+
 enum dp_link_voltage_level {
 	DP_LINK_VOLTAGE_LEVEL_0	= 0,
 	DP_LINK_VOLTAGE_LEVEL_1	= 1,
@@ -31,25 +35,138 @@
 	DP_LINK_PRE_EMPHASIS_MAX	= DP_LINK_PRE_EMPHASIS_LEVEL_2,
 };
 
-#define DS_PORT_STATUS_CHANGED 0x200
-#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
+struct dp_link_sink_count {
+	u32 count;
+	bool cp_ready;
+};
 
-struct dp_link {
-	u32 test_requested;
+struct dp_link_test_video {
+	u32 test_video_pattern;
+	u32 test_bit_depth;
+	u32 test_dyn_range;
+	u32 test_h_total;
+	u32 test_v_total;
+	u32 test_h_start;
+	u32 test_v_start;
+	u32 test_hsync_pol;
+	u32 test_hsync_width;
+	u32 test_vsync_pol;
+	u32 test_vsync_width;
+	u32 test_h_width;
+	u32 test_v_height;
+	u32 test_rr_d;
+	u32 test_rr_n;
+};
 
+struct dp_link_test_audio {
+	u32 test_audio_sampling_rate;
+	u32 test_audio_channel_count;
+	u32 test_audio_pattern_type;
+	u32 test_audio_period_ch_1;
+	u32 test_audio_period_ch_2;
+	u32 test_audio_period_ch_3;
+	u32 test_audio_period_ch_4;
+	u32 test_audio_period_ch_5;
+	u32 test_audio_period_ch_6;
+	u32 test_audio_period_ch_7;
+	u32 test_audio_period_ch_8;
+};
+
+struct dp_link_phy_params {
+	u32 phy_test_pattern_sel;
+	u8 v_level;
+	u8 p_level;
+};
+
+struct dp_link_params {
 	u32 lane_count;
 	u32 bw_code;
-	u32 v_level;
-	u32 p_level;
+};
+
+struct dp_link {
+	u32 sink_request;
+	u32 test_response;
+
+	struct dp_link_sink_count sink_count;
+	struct dp_link_test_video test_video;
+	struct dp_link_test_audio test_audio;
+	struct dp_link_phy_params phy_params;
+	struct dp_link_params link_params;
 
 	u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
 	int (*process_request)(struct dp_link *dp_link);
 	int (*get_colorimetry_config)(struct dp_link *dp_link);
 	int (*adjust_levels)(struct dp_link *dp_link, u8 *link_status);
 	int (*send_psm_request)(struct dp_link *dp_link, bool req);
-	bool (*phy_pattern_requested)(struct dp_link *dp_link);
+	void (*send_test_response)(struct dp_link *dp_link);
+	int (*psm_config)(struct dp_link *dp_link,
+		struct drm_dp_link *link_info, bool enable);
+	void (*send_edid_checksum)(struct dp_link *dp_link, u8 checksum);
 };
 
+static inline char *dp_link_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+	switch (phy_test_pattern_sel) {
+	case DP_TEST_PHY_PATTERN_NONE:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_NONE);
+	case DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_D10_2_NO_SCRAMBLING);
+	case DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
+	case DP_TEST_PHY_PATTERN_PRBS7:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_PRBS7);
+	case DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN:
+		return DP_LINK_ENUM_STR(
+			DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN);
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_1:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_1);
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_2:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_2);
+	case DP_TEST_PHY_PATTERN_CP2520_PATTERN_3:
+		return DP_LINK_ENUM_STR(DP_TEST_PHY_PATTERN_CP2520_PATTERN_3);
+	default:
+		return "unknown";
+	}
+}
+
+/**
+ * mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
+ * @tbd: test bit depth
+ *
+ * Returns the bits per pixel (bpp) to be used corresponding to the
+ * git bit depth value. This function assumes that bit depth has
+ * already been validated.
+ */
+static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
+{
+	u32 bpp;
+
+	/*
+	 * Few simplistic rules and assumptions made here:
+	 *    1. Bit depth is per color component
+	 *    2. If bit depth is unknown return 0
+	 *    3. Assume 3 color components
+	 */
+	switch (tbd) {
+	case DP_TEST_BIT_DEPTH_6:
+		bpp = 18;
+		break;
+	case DP_TEST_BIT_DEPTH_8:
+		bpp = 24;
+		break;
+	case DP_TEST_BIT_DEPTH_10:
+		bpp = 30;
+		break;
+	case DP_TEST_BIT_DEPTH_UNKNOWN:
+	default:
+		bpp = 0;
+	}
+
+	return bpp;
+}
+
 /**
  * dp_link_get() - get the functionalities of dp test module
  *
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index 2e21033..96f9d3a 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.c
+++ b/drivers/gpu/drm/msm/dp/dp_panel.c
@@ -17,6 +17,7 @@
 #include "dp_panel.h"
 
 #define DP_PANEL_DEFAULT_BPP 24
+#define DP_MAX_DS_PORT_COUNT 1
 
 enum {
 	DP_LINK_RATE_MULTIPLIER = 27000000,
@@ -26,17 +27,46 @@
 	struct device *dev;
 	struct dp_panel dp_panel;
 	struct dp_aux *aux;
+	struct dp_link *link;
 	struct dp_catalog_panel *catalog;
-	bool lane_switch_supported;
-	bool aux_cfg_update_done;
+	bool custom_edid;
+	bool custom_dpcd;
+	bool panel_on;
+	u8 spd_vendor_name[8];
+	u8 spd_product_description[16];
 };
 
+static const struct dp_panel_info fail_safe = {
+	.h_active = 640,
+	.v_active = 480,
+	.h_back_porch = 48,
+	.h_front_porch = 16,
+	.h_sync_width = 96,
+	.h_active_low = 0,
+	.v_back_porch = 33,
+	.v_front_porch = 10,
+	.v_sync_width = 2,
+	.v_active_low = 0,
+	.h_skew = 0,
+	.refresh_rate = 60,
+	.pixel_clk_khz = 25200,
+	.bpp = 24,
+};
+
+/* OEM NAME */
+static const u8 vendor_name[8] = {81, 117, 97, 108, 99, 111, 109, 109};
+
+/* MODEL NAME */
+static const u8 product_desc[16] = {83, 110, 97, 112, 100, 114, 97, 103,
+	111, 110, 0, 0, 0, 0, 0, 0};
+
 static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
 {
 	int rlen, rc = 0;
 	struct dp_panel_private *panel;
 	struct drm_dp_link *link_info;
-	u8 major = 0, minor = 0;
+	u8 *dpcd, major = 0, minor = 0;
+	u32 dfp_count = 0;
 	unsigned long caps = DP_LINK_CAP_ENHANCED_FRAMING;
 
 	if (!dp_panel) {
@@ -45,15 +75,26 @@
 		goto end;
 	}
 
+	dpcd = dp_panel->dpcd;
+
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 	link_info = &dp_panel->link_info;
 
-	rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
-		dp_panel->dpcd, (DP_RECEIVER_CAP_SIZE + 1));
-	if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
-		pr_err("dpcd read failed, rlen=%d\n", rlen);
-		rc = -EINVAL;
-		goto end;
+	if (!panel->custom_dpcd) {
+		rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
+			dp_panel->dpcd, (DP_RECEIVER_CAP_SIZE + 1));
+		if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
+			pr_err("dpcd read failed, rlen=%d\n", rlen);
+			if (rlen == -ETIMEDOUT)
+				rc = rlen;
+			else
+				rc = -EINVAL;
+
+			goto end;
+		}
+
+		print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ",
+			DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
 	}
 
 	link_info->revision = dp_panel->dpcd[DP_DPCD_REV];
@@ -66,27 +107,58 @@
 		drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]);
 	pr_debug("link_rate=%d\n", link_info->rate);
 
-	if (panel->lane_switch_supported)
-		link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] &
-			DP_MAX_LANE_COUNT_MASK;
-	else
-		link_info->num_lanes = 2;
+	link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] &
+				DP_MAX_LANE_COUNT_MASK;
 
 	pr_debug("lane_count=%d\n", link_info->num_lanes);
 
-	if (dp_panel->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)
+	if (drm_dp_enhanced_frame_cap(dpcd))
 		link_info->capabilities |= caps;
 
+	dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] &
+						DP_DOWN_STREAM_PORT_COUNT;
+
+	if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)
+		&& (dpcd[DP_DPCD_REV] > 0x10)) {
+		rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
+			DP_DOWNSTREAM_PORT_0, dp_panel->ds_ports,
+			DP_MAX_DOWNSTREAM_PORTS);
+		if (rlen < DP_MAX_DOWNSTREAM_PORTS) {
+			pr_err("ds port status failed, rlen=%d\n", rlen);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+
+	if (dfp_count > DP_MAX_DS_PORT_COUNT)
+		pr_debug("DS port count %d greater that max (%d) supported\n",
+			dfp_count, DP_MAX_DS_PORT_COUNT);
+
 end:
 	return rc;
 }
 
-
-static int dp_panel_read_edid(struct dp_panel *dp_panel,
-	struct drm_connector *connector)
+static int dp_panel_set_default_link_params(struct dp_panel *dp_panel)
 {
-	int retry_cnt = 0;
-	const int max_retry = 10;
+	struct drm_dp_link *link_info;
+	const int default_bw_code = 162000;
+	const int default_num_lanes = 1;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+	link_info = &dp_panel->link_info;
+	link_info->rate = default_bw_code;
+	link_info->num_lanes = default_num_lanes;
+	pr_debug("link_rate=%d num_lanes=%d\n",
+		link_info->rate, link_info->num_lanes);
+
+	return 0;
+}
+
+static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid)
+{
 	struct dp_panel_private *panel;
 
 	if (!dp_panel) {
@@ -96,18 +168,70 @@
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 
-	do {
-		sde_get_edid(connector, &panel->aux->drm_aux->ddc,
-			(void **)&dp_panel->edid_ctrl);
-		if (!dp_panel->edid_ctrl->edid) {
-			pr_err("EDID read failed\n");
-			retry_cnt++;
-			panel->aux->reconfig(panel->aux);
-			panel->aux_cfg_update_done = true;
-		} else {
-			return 0;
-		}
-	} while (retry_cnt < max_retry);
+	if (edid) {
+		dp_panel->edid_ctrl->edid = (struct edid *)edid;
+		panel->custom_edid = true;
+	} else {
+		panel->custom_edid = false;
+	}
+
+	return 0;
+}
+
+static int dp_panel_set_dpcd(struct dp_panel *dp_panel, u8 *dpcd)
+{
+	struct dp_panel_private *panel;
+	u8 *dp_dpcd;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	dp_dpcd = dp_panel->dpcd;
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (dpcd) {
+		memcpy(dp_dpcd, dpcd, DP_RECEIVER_CAP_SIZE + 1);
+		panel->custom_dpcd = true;
+	} else {
+		panel->custom_dpcd = false;
+	}
+
+	return 0;
+}
+
+static int dp_panel_read_edid(struct dp_panel *dp_panel,
+	struct drm_connector *connector)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (panel->custom_edid) {
+		pr_debug("skip edid read in debug mode\n");
+		return 0;
+	}
+
+	sde_get_edid(connector, &panel->aux->drm_aux->ddc,
+		(void **)&dp_panel->edid_ctrl);
+	if (!dp_panel->edid_ctrl->edid) {
+		pr_err("EDID read failed\n");
+	} else {
+		u8 *buf = (u8 *)dp_panel->edid_ctrl->edid;
+		u32 size = buf[0x7E] ? 256 : 128;
+
+		print_hex_dump(KERN_DEBUG, "[drm-dp] SINK EDID: ",
+			DUMP_PREFIX_NONE, 16, 1, buf, size, false);
+
+		return 0;
+	}
 
 	return -EINVAL;
 }
@@ -126,52 +250,210 @@
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 
 	rc = dp_panel_read_dpcd(dp_panel);
-	if (rc) {
-		pr_err("panel dpcd read failed\n");
-		return rc;
+	if (rc || !is_link_rate_valid(drm_dp_link_rate_to_bw_code(
+		dp_panel->link_info.rate)) || !is_lane_count_valid(
+		dp_panel->link_info.num_lanes) ||
+		((drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate)) >
+		dp_panel->max_bw_code)) {
+		if ((rc == -ETIMEDOUT) || (rc == -ENODEV)) {
+			pr_err("DPCD read failed, return early\n");
+			return rc;
+		}
+		pr_err("panel dpcd read failed/incorrect, set default params\n");
+		dp_panel_set_default_link_params(dp_panel);
 	}
 
 	rc = dp_panel_read_edid(dp_panel, connector);
 	if (rc) {
-		pr_err("panel edid read failed\n");
+		pr_err("panel edid read failed, set failsafe mode\n");
 		return rc;
 	}
 
-	if (panel->aux_cfg_update_done) {
-		pr_debug("read DPCD with updated AUX config\n");
-		dp_panel_read_dpcd(dp_panel);
-		panel->aux_cfg_update_done = false;
-	}
-
 	return 0;
 }
 
-static u32 dp_panel_get_max_pclk(struct dp_panel *dp_panel)
+static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
+		u32 mode_edid_bpp, u32 mode_pclk_khz)
 {
 	struct drm_dp_link *link_info;
-	const u8 num_components = 3;
-	u32 bpc = 0, bpp = 0, max_data_rate_khz = 0, max_pclk_rate_khz = 0;
+	const u32 max_supported_bpp = 30, min_supported_bpp = 18;
+	u32 bpp = 0, data_rate_khz = 0;
 
-	if (!dp_panel) {
+	bpp = min_t(u32, mode_edid_bpp, max_supported_bpp);
+
+	link_info = &dp_panel->link_info;
+	data_rate_khz = link_info->num_lanes * link_info->rate * 8;
+
+	while (bpp > min_supported_bpp) {
+		if (mode_pclk_khz * bpp <= data_rate_khz)
+			break;
+		bpp -= 6;
+	}
+
+	return bpp;
+}
+
+static u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
+		u32 mode_edid_bpp, u32 mode_pclk_khz)
+{
+	struct dp_panel_private *panel;
+	u32 bpp = mode_edid_bpp;
+
+	if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) {
 		pr_err("invalid input\n");
 		return 0;
 	}
 
-	link_info = &dp_panel->link_info;
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 
-	bpc = sde_get_sink_bpc(dp_panel->edid_ctrl);
-	bpp = bpc * num_components;
-	if (!bpp)
-		bpp = DP_PANEL_DEFAULT_BPP;
+	if (dp_panel->video_test)
+		bpp = dp_link_bit_depth_to_bpp(
+				panel->link->test_video.test_bit_depth);
+	else
+		bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp,
+				mode_pclk_khz);
 
-	max_data_rate_khz = (link_info->num_lanes * link_info->rate * 8);
-	max_pclk_rate_khz = max_data_rate_khz / bpp;
+	return bpp;
+}
 
-	pr_debug("bpp=%d, max_lane_cnt=%d\n", bpp, link_info->num_lanes);
-	pr_debug("max_data_rate=%dKHz, max_pclk_rate=%dKHz\n",
-		max_data_rate_khz, max_pclk_rate_khz);
+static void dp_panel_set_test_mode(struct dp_panel_private *panel,
+		struct dp_display_mode *mode)
+{
+	struct dp_panel_info *pinfo = NULL;
+	struct dp_link_test_video *test_info = NULL;
 
-	return max_pclk_rate_khz;
+	if (!panel) {
+		pr_err("invalid params\n");
+		return;
+	}
+
+	pinfo = &mode->timing;
+	test_info = &panel->link->test_video;
+
+	pinfo->h_active = test_info->test_h_width;
+	pinfo->h_sync_width = test_info->test_hsync_width;
+	pinfo->h_back_porch = test_info->test_h_start -
+		test_info->test_hsync_width;
+	pinfo->h_front_porch = test_info->test_h_total -
+		(test_info->test_h_start + test_info->test_h_width);
+
+	pinfo->v_active = test_info->test_v_height;
+	pinfo->v_sync_width = test_info->test_vsync_width;
+	pinfo->v_back_porch = test_info->test_v_start -
+		test_info->test_vsync_width;
+	pinfo->v_front_porch = test_info->test_v_total -
+		(test_info->test_v_start + test_info->test_v_height);
+
+	pinfo->bpp = dp_link_bit_depth_to_bpp(test_info->test_bit_depth);
+	pinfo->h_active_low = test_info->test_hsync_pol;
+	pinfo->v_active_low = test_info->test_vsync_pol;
+
+	pinfo->refresh_rate = test_info->test_rr_n;
+	pinfo->pixel_clk_khz = test_info->test_h_total *
+		test_info->test_v_total * pinfo->refresh_rate;
+
+	if (test_info->test_rr_d == 0)
+		pinfo->pixel_clk_khz /= 1000;
+	else
+		pinfo->pixel_clk_khz /= 1001;
+
+	if (test_info->test_h_width == 640)
+		pinfo->pixel_clk_khz = 25170;
+}
+
+static int dp_panel_get_modes(struct dp_panel *dp_panel,
+	struct drm_connector *connector, struct dp_display_mode *mode)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (dp_panel->video_test) {
+		dp_panel_set_test_mode(panel, mode);
+		return 1;
+	} else if (dp_panel->edid_ctrl->edid) {
+		return _sde_edid_update_modes(connector, dp_panel->edid_ctrl);
+	} else { /* fail-safe mode */
+		memcpy(&mode->timing, &fail_safe,
+			sizeof(fail_safe));
+		return 1;
+	}
+}
+
+static void dp_panel_handle_sink_request(struct dp_panel *dp_panel)
+{
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
+		u8 checksum = sde_get_edid_checksum(dp_panel->edid_ctrl);
+
+		panel->link->send_edid_checksum(panel->link, checksum);
+		panel->link->send_test_response(panel->link);
+	}
+}
+
+static void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
+{
+	u32 hsync_start_x, hsync_end_x;
+	struct dp_catalog_panel *catalog;
+	struct dp_panel_private *panel;
+	struct dp_panel_info *pinfo;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	catalog = panel->catalog;
+	pinfo = &panel->dp_panel.pinfo;
+
+	if (!panel->panel_on) {
+		pr_debug("DP panel not enabled, handle TPG on next panel on\n");
+		return;
+	}
+
+	if (!enable) {
+		panel->catalog->tpg_config(catalog, false);
+		return;
+	}
+
+	/* TPG config */
+	catalog->hsync_period = pinfo->h_sync_width + pinfo->h_back_porch +
+			pinfo->h_active + pinfo->h_front_porch;
+	catalog->vsync_period = pinfo->v_sync_width + pinfo->v_back_porch +
+			pinfo->v_active + pinfo->v_front_porch;
+
+	catalog->display_v_start = ((pinfo->v_sync_width +
+			pinfo->v_back_porch) * catalog->hsync_period);
+	catalog->display_v_end = ((catalog->vsync_period -
+			pinfo->v_front_porch) * catalog->hsync_period) - 1;
+
+	catalog->display_v_start += pinfo->h_sync_width + pinfo->h_back_porch;
+	catalog->display_v_end -= pinfo->h_front_porch;
+
+	hsync_start_x = pinfo->h_back_porch + pinfo->h_sync_width;
+	hsync_end_x = catalog->hsync_period - pinfo->h_front_porch - 1;
+
+	catalog->v_sync_width = pinfo->v_sync_width;
+
+	catalog->hsync_ctl = (catalog->hsync_period << 16) |
+			pinfo->h_sync_width;
+	catalog->display_hctl = (hsync_end_x << 16) | hsync_start_x;
+
+	panel->catalog->tpg_config(catalog, true);
 }
 
 static int dp_panel_timing_cfg(struct dp_panel *dp_panel)
@@ -233,38 +515,27 @@
 	catalog->dp_active = data;
 
 	panel->catalog->timing_cfg(catalog);
+	panel->panel_on = true;
 end:
 	return rc;
 }
 
-static int dp_panel_edid_register(struct dp_panel *dp_panel)
+static int dp_panel_edid_register(struct dp_panel_private *panel)
 {
 	int rc = 0;
 
-	if (!dp_panel) {
-		pr_err("invalid input\n");
-		rc = -EINVAL;
-		goto end;
-	}
-
-	dp_panel->edid_ctrl = sde_edid_init();
-	if (!dp_panel->edid_ctrl) {
+	panel->dp_panel.edid_ctrl = sde_edid_init();
+	if (!panel->dp_panel.edid_ctrl) {
 		pr_err("sde edid init for DP failed\n");
 		rc = -ENOMEM;
-		goto end;
 	}
-end:
+
 	return rc;
 }
 
-static void dp_panel_edid_deregister(struct dp_panel *dp_panel)
+static void dp_panel_edid_deregister(struct dp_panel_private *panel)
 {
-	if (!dp_panel) {
-		pr_err("invalid input\n");
-		return;
-	}
-
-	sde_edid_deinit((void **)&dp_panel->edid_ctrl);
+	sde_edid_deinit((void **)&panel->dp_panel.edid_ctrl);
 }
 
 static int dp_panel_init_panel_info(struct dp_panel *dp_panel)
@@ -299,13 +570,31 @@
 	pr_info("bpp = %d\n", pinfo->bpp);
 	pr_info("active low (h|v)=(%d|%d)\n", pinfo->h_active_low,
 		pinfo->v_active_low);
-
-	pinfo->bpp = max_t(u32, 18, min_t(u32, pinfo->bpp, 30));
-	pr_info("updated bpp = %d\n", pinfo->bpp);
 end:
 	return rc;
 }
 
+static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		return -EINVAL;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	if (!panel->custom_edid)
+		sde_free_edid((void **)&dp_panel->edid_ctrl);
+
+	memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
+	panel->panel_on = false;
+
+	return rc;
+}
+
 static u32 dp_panel_get_min_req_link_rate(struct dp_panel *dp_panel)
 {
 	const u32 encoding_factx10 = 8;
@@ -332,39 +621,164 @@
 	return min_link_rate_khz;
 }
 
-struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
-				struct dp_catalog_panel *catalog)
+enum dp_panel_hdr_pixel_encoding {
+	RGB,
+	YCbCr444,
+	YCbCr422,
+	YCbCr420,
+	YONLY,
+	RAW,
+};
+
+enum dp_panel_hdr_rgb_colorimetry {
+	sRGB,
+	RGB_WIDE_GAMUT_FIXED_POINT,
+	RGB_WIDE_GAMUT_FLOATING_POINT,
+	ADOBERGB,
+	DCI_P3,
+	CUSTOM_COLOR_PROFILE,
+	ITU_R_BT_2020_RGB,
+};
+
+enum dp_panel_hdr_dynamic_range {
+	VESA,
+	CEA,
+};
+
+enum dp_panel_hdr_content_type {
+	NOT_DEFINED,
+	GRAPHICS,
+	PHOTO,
+	VIDEO,
+	GAME,
+};
+
+static int dp_panel_setup_hdr(struct dp_panel *dp_panel,
+		struct drm_msm_ext_hdr_metadata *hdr_meta)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+	struct dp_catalog_hdr_data *hdr;
+
+	if (!hdr_meta || !hdr_meta->hdr_state)
+		goto end;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+	hdr = &panel->catalog->hdr_data;
+
+	hdr->ext_header_byte0 = 0x00;
+	hdr->ext_header_byte1 = 0x04;
+	hdr->ext_header_byte2 = 0x1F;
+	hdr->ext_header_byte3 = 0x00;
+
+	hdr->vsc_header_byte0 = 0x00;
+	hdr->vsc_header_byte1 = 0x07;
+	hdr->vsc_header_byte2 = 0x05;
+	hdr->vsc_header_byte3 = 0x13;
+
+	/* VSC SDP Payload for DB16 */
+	hdr->pixel_encoding = RGB;
+	hdr->colorimetry = ITU_R_BT_2020_RGB;
+
+	/* VSC SDP Payload for DB17 */
+	hdr->dynamic_range = CEA;
+
+	/* VSC SDP Payload for DB18 */
+	hdr->content_type = GRAPHICS;
+
+	hdr->bpc = dp_panel->pinfo.bpp / 3;
+
+	hdr->vscext_header_byte0 = 0x00;
+	hdr->vscext_header_byte1 = 0x87;
+	hdr->vscext_header_byte2 = 0x1D;
+	hdr->vscext_header_byte3 = 0x13 << 2;
+
+	hdr->version = 0x01;
+	hdr->length = 0x1A;
+
+	memcpy(&hdr->hdr_meta, hdr_meta, sizeof(hdr->hdr_meta));
+
+	panel->catalog->config_hdr(panel->catalog);
+end:
+	return rc;
+}
+
+static int dp_panel_spd_config(struct dp_panel *dp_panel)
+{
+	int rc = 0;
+	struct dp_panel_private *panel;
+
+	if (!dp_panel) {
+		pr_err("invalid input\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (!dp_panel->spd_enabled) {
+		pr_debug("SPD Infoframe not enabled\n");
+		goto end;
+	}
+
+	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+
+	panel->catalog->spd_vendor_name = panel->spd_vendor_name;
+	panel->catalog->spd_product_description =
+		panel->spd_product_description;
+	panel->catalog->config_spd(panel->catalog);
+end:
+	return rc;
+}
+
+struct dp_panel *dp_panel_get(struct dp_panel_in *in)
 {
 	int rc = 0;
 	struct dp_panel_private *panel;
 	struct dp_panel *dp_panel;
 
-	if (!dev || !aux || !catalog) {
+	if (!in->dev || !in->catalog || !in->aux || !in->link) {
 		pr_err("invalid input\n");
 		rc = -EINVAL;
 		goto error;
 	}
 
-	panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL);
+	panel = devm_kzalloc(in->dev, sizeof(*panel), GFP_KERNEL);
 	if (!panel) {
 		rc = -ENOMEM;
 		goto error;
 	}
 
-	panel->dev = dev;
-	panel->aux = aux;
-	panel->catalog = catalog;
+	panel->dev = in->dev;
+	panel->aux = in->aux;
+	panel->catalog = in->catalog;
+	panel->link = in->link;
 
 	dp_panel = &panel->dp_panel;
-	panel->aux_cfg_update_done = false;
+	dp_panel->max_bw_code = DP_LINK_BW_8_1;
+	dp_panel->spd_enabled = true;
+	memcpy(panel->spd_vendor_name, vendor_name, (sizeof(u8) * 8));
+	memcpy(panel->spd_product_description, product_desc, (sizeof(u8) * 16));
 
-	dp_panel->sde_edid_register = dp_panel_edid_register;
-	dp_panel->sde_edid_deregister = dp_panel_edid_deregister;
-	dp_panel->init_info = dp_panel_init_panel_info;
+	dp_panel->init = dp_panel_init_panel_info;
+	dp_panel->deinit = dp_panel_deinit_panel_info;
 	dp_panel->timing_cfg = dp_panel_timing_cfg;
 	dp_panel->read_sink_caps = dp_panel_read_sink_caps;
 	dp_panel->get_min_req_link_rate = dp_panel_get_min_req_link_rate;
-	dp_panel->get_max_pclk = dp_panel_get_max_pclk;
+	dp_panel->get_mode_bpp = dp_panel_get_mode_bpp;
+	dp_panel->get_modes = dp_panel_get_modes;
+	dp_panel->handle_sink_request = dp_panel_handle_sink_request;
+	dp_panel->set_edid = dp_panel_set_edid;
+	dp_panel->set_dpcd = dp_panel_set_dpcd;
+	dp_panel->tpg_config = dp_panel_tpg_config;
+	dp_panel->spd_config = dp_panel_spd_config;
+	dp_panel->setup_hdr = dp_panel_setup_hdr;
+
+	dp_panel_edid_register(panel);
 
 	return dp_panel;
 error:
@@ -380,5 +794,6 @@
 
 	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
 
+	dp_panel_edid_deregister(panel);
 	devm_kfree(panel->dev, panel);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h
index ab9a451..128f694 100644
--- a/drivers/gpu/drm/msm/dp/dp_panel.h
+++ b/drivers/gpu/drm/msm/dp/dp_panel.h
@@ -15,10 +15,20 @@
 #ifndef _DP_PANEL_H_
 #define _DP_PANEL_H_
 
+#include <drm/msm_drm.h>
+
 #include "dp_aux.h"
+#include "dp_link.h"
+#include "dp_usbpd.h"
 #include "sde_edid_parser.h"
 
-#define DP_LINK_RATE_810	30	/* 8.10G = 270M * 30 */
+enum dp_lane_count {
+	DP_LANE_COUNT_1	= 1,
+	DP_LANE_COUNT_2	= 2,
+	DP_LANE_COUNT_4	= 4,
+};
+
+#define DP_MAX_DOWNSTREAM_PORTS 0x10
 
 struct dp_panel_info {
 	u32 h_active;
@@ -37,29 +47,81 @@
 	u32 bpp;
 };
 
+struct dp_display_mode {
+	struct dp_panel_info timing;
+	u32 capabilities;
+};
+
+struct dp_panel_in {
+	struct device *dev;
+	struct dp_aux *aux;
+	struct dp_link *link;
+	struct dp_catalog_panel *catalog;
+};
+
 struct dp_panel {
 	/* dpcd raw data */
-	u8 dpcd[DP_RECEIVER_CAP_SIZE];
-	struct drm_dp_link link_info;
+	u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
+	u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS];
 
+	struct drm_dp_link link_info;
 	struct sde_edid_ctrl *edid_ctrl;
-	struct drm_connector *connector;
 	struct dp_panel_info pinfo;
+	bool video_test;
+	bool spd_enabled;
 
 	u32 vic;
 	u32 max_pclk_khz;
 
-	int (*sde_edid_register)(struct dp_panel *dp_panel);
-	void (*sde_edid_deregister)(struct dp_panel *dp_panel);
-	int (*init_info)(struct dp_panel *dp_panel);
+	/* debug */
+	u32 max_bw_code;
+
+	int (*init)(struct dp_panel *dp_panel);
+	int (*deinit)(struct dp_panel *dp_panel);
 	int (*timing_cfg)(struct dp_panel *dp_panel);
 	int (*read_sink_caps)(struct dp_panel *dp_panel,
 		struct drm_connector *connector);
 	u32 (*get_min_req_link_rate)(struct dp_panel *dp_panel);
-	u32 (*get_max_pclk)(struct dp_panel *dp_panel);
+	u32 (*get_mode_bpp)(struct dp_panel *dp_panel, u32 mode_max_bpp,
+			u32 mode_pclk_khz);
+	int (*get_modes)(struct dp_panel *dp_panel,
+		struct drm_connector *connector, struct dp_display_mode *mode);
+	void (*handle_sink_request)(struct dp_panel *dp_panel);
+	int (*set_edid)(struct dp_panel *dp_panel, u8 *edid);
+	int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd);
+	int (*setup_hdr)(struct dp_panel *dp_panel,
+		struct drm_msm_ext_hdr_metadata *hdr_meta);
+	void (*tpg_config)(struct dp_panel *dp_panel, bool enable);
+	int (*spd_config)(struct dp_panel *dp_panel);
 };
 
-struct dp_panel *dp_panel_get(struct device *dev, struct dp_aux *aux,
-				struct dp_catalog_panel *catalog);
+/**
+ * is_link_rate_valid() - validates the link rate
+ * @lane_rate: link rate requested by the sink
+ *
+ * Returns true if the requested link rate is supported.
+ */
+static inline bool is_link_rate_valid(u32 bw_code)
+{
+	return ((bw_code == DP_LINK_BW_1_62) ||
+		(bw_code == DP_LINK_BW_2_7) ||
+		(bw_code == DP_LINK_BW_5_4) ||
+		(bw_code == DP_LINK_BW_8_1));
+}
+
+/**
+ * dp_link_is_lane_count_valid() - validates the lane count
+ * @lane_count: lane count requested by the sink
+ *
+ * Returns true if the requested lane count is supported.
+ */
+static inline bool is_lane_count_valid(u32 lane_count)
+{
+	return (lane_count == DP_LANE_COUNT_1) ||
+		(lane_count == DP_LANE_COUNT_2) ||
+		(lane_count == DP_LANE_COUNT_4);
+}
+
+struct dp_panel *dp_panel_get(struct dp_panel_in *in);
 void dp_panel_put(struct dp_panel *dp_panel);
 #endif /* _DP_PANEL_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index c85c2a2..c112cdc 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -22,22 +22,18 @@
 {
 	struct dp_io *io = &parser->io;
 
-	if (&io->ctrl_io)
-		msm_dss_iounmap(&io->ctrl_io);
-	if (&io->phy_io)
-		msm_dss_iounmap(&io->phy_io);
-	if (&io->ln_tx0_io)
-		msm_dss_iounmap(&io->ln_tx0_io);
-	if (&io->ln_tx1_io)
-		msm_dss_iounmap(&io->ln_tx0_io);
-	if (&io->dp_pll_io)
-		msm_dss_iounmap(&io->dp_pll_io);
-	if (&io->dp_cc_io)
-		msm_dss_iounmap(&io->dp_cc_io);
-	if (&io->qfprom_io)
-		msm_dss_iounmap(&io->qfprom_io);
-	if (&io->hdcp_io)
-		msm_dss_iounmap(&io->hdcp_io);
+	msm_dss_iounmap(&io->dp_ahb);
+	msm_dss_iounmap(&io->dp_aux);
+	msm_dss_iounmap(&io->dp_link);
+	msm_dss_iounmap(&io->dp_p0);
+	msm_dss_iounmap(&io->phy_io);
+	msm_dss_iounmap(&io->ln_tx0_io);
+	msm_dss_iounmap(&io->ln_tx0_io);
+	msm_dss_iounmap(&io->dp_pll_io);
+	msm_dss_iounmap(&io->dp_cc_io);
+	msm_dss_iounmap(&io->usb3_dp_com);
+	msm_dss_iounmap(&io->qfprom_io);
+	msm_dss_iounmap(&io->hdcp_io);
 }
 
 static int dp_parser_ctrl_res(struct dp_parser *parser)
@@ -54,7 +50,25 @@
 		goto err;
 	}
 
-	rc = msm_dss_ioremap_byname(pdev, &io->ctrl_io, "dp_ctrl");
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_ahb, "dp_ahb");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_aux, "dp_aux");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_link, "dp_link");
+	if (rc) {
+		pr_err("unable to remap dp io resources\n");
+		goto err;
+	}
+
+	rc = msm_dss_ioremap_byname(pdev, &io->dp_p0, "dp_p0");
 	if (rc) {
 		pr_err("unable to remap dp io resources\n");
 		goto err;
@@ -84,6 +98,12 @@
 		goto err;
 	}
 
+	rc = msm_dss_ioremap_byname(pdev, &io->usb3_dp_com, "usb3_dp_com");
+	if (rc) {
+		pr_err("unable to remap USB3 DP com resources\n");
+		goto err;
+	}
+
 	if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
 		pr_err("unable to remap dp MMSS_CC resources\n");
 		goto err;
@@ -241,6 +261,9 @@
 
 	mp->gpio_config = devm_kzalloc(dev,
 		sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
+	if (!mp->gpio_config)
+		return -ENOMEM;
+
 	mp->num_gpio = ARRAY_SIZE(dp_gpios);
 
 	for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
@@ -439,6 +462,22 @@
 	mp->num_clk = 0;
 }
 
+static void dp_parser_put_gpio_data(struct device *dev,
+	struct dss_module_power *mp)
+{
+	if (!mp) {
+		DEV_ERR("%s: invalid input\n", __func__);
+		return;
+	}
+
+	if (mp->gpio_config) {
+		devm_kfree(dev, mp->gpio_config);
+		mp->gpio_config = NULL;
+	}
+
+	mp->num_gpio = 0;
+}
+
 static int dp_parser_init_clk_data(struct dp_parser *parser)
 {
 	int num_clk = 0, i = 0, rc = 0;
@@ -635,11 +674,9 @@
 	power = parser->mp;
 
 	for (i = 0; i < DP_MAX_PM; i++) {
-		struct dss_module_power *mp = &power[i];
-
-		devm_kfree(&parser->pdev->dev, mp->clk_config);
-		devm_kfree(&parser->pdev->dev, mp->vreg_config);
-		devm_kfree(&parser->pdev->dev, mp->gpio_config);
+		dp_parser_put_clk_data(&parser->pdev->dev, &power[i]);
+		dp_parser_put_vreg_data(&parser->pdev->dev, &power[i]);
+		dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
 	}
 
 	devm_kfree(&parser->pdev->dev, parser);
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
index 7794da5..72da381 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.h
+++ b/drivers/gpu/drm/msm/dp/dp_parser.h
@@ -58,23 +58,32 @@
 /**
  * struct dp_ctrl_resource - controller's IO related data
  *
- * @ctrl_io: controller's mapped memory address
+ * @dp_ahb: controller's ahb mapped memory address
+ * @dp_aux: controller's aux mapped memory address
+ * @dp_link: controller's link mapped memory address
+ * @dp_p0: controller's p0 mapped memory address
  * @phy_io: phy's mapped memory address
  * @ln_tx0_io: USB-DP lane TX0's mapped memory address
  * @ln_tx1_io: USB-DP lane TX1's mapped memory address
  * @dp_cc_io: DP cc's mapped memory address
  * @qfprom_io: qfprom's mapped memory address
  * @dp_pll_io: DP PLL mapped memory address
+ * @usb3_dp_com: USB3 DP PHY combo mapped memory address
  * @hdcp_io: hdcp's mapped memory address
  */
 struct dp_io {
 	struct dss_io_data ctrl_io;
+	struct dss_io_data dp_ahb;
+	struct dss_io_data dp_aux;
+	struct dss_io_data dp_link;
+	struct dss_io_data dp_p0;
 	struct dss_io_data phy_io;
 	struct dss_io_data ln_tx0_io;
 	struct dss_io_data ln_tx1_io;
 	struct dss_io_data dp_cc_io;
 	struct dss_io_data qfprom_io;
 	struct dss_io_data dp_pll_io;
+	struct dss_io_data usb3_dp_com;
 	struct dss_io_data hdcp_io;
 };
 
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
index 60c8966..d793365 100644
--- a/drivers/gpu/drm/msm/dp/dp_power.c
+++ b/drivers/gpu/drm/msm/dp/dp_power.c
@@ -562,6 +562,15 @@
 	power = container_of(dp_power, struct dp_power_private, dp_power);
 
 	dp_power_clk_enable(dp_power, DP_CORE_PM, false);
+	/*
+	 * If the display power on event was not successful, for example if
+	 * there was a link training failure, then the link clocks could
+	 * possibly still be on. In this scenario, we need to turn off the
+	 * link clocks as soon as the cable is disconnected so that the clock
+	 * state is cleaned up before subsequent connection events.
+	 */
+	if (power->link_clks_on)
+		dp_power_clk_enable(dp_power, DP_CTRL_PM, false);
 	rc = sde_power_resource_enable(power->phandle,
 			power->dp_core_client, false);
 	if (rc) {
@@ -612,8 +621,12 @@
 
 void dp_power_put(struct dp_power *dp_power)
 {
-	struct dp_power_private *power = container_of(dp_power,
-			struct dp_power_private, dp_power);
+	struct dp_power_private *power = NULL;
+
+	if (!dp_power)
+		return;
+
+	power = container_of(dp_power, struct dp_power_private, dp_power);
 
 	devm_kfree(&power->pdev->dev, power);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 30377a0..4e2194e 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -25,124 +25,158 @@
 #define DP_INTR_STATUS2				(0x00000024)
 #define DP_INTR_STATUS3				(0x00000028)
 
-#define DP_DP_HPD_CTRL				(0x00000200)
-#define DP_DP_HPD_INT_STATUS			(0x00000204)
-#define DP_DP_HPD_INT_ACK			(0x00000208)
-#define DP_DP_HPD_INT_MASK			(0x0000020C)
-#define DP_DP_HPD_REFTIMER			(0x00000218)
-#define DP_DP_HPD_EVENT_TIME_0			(0x0000021C)
-#define DP_DP_HPD_EVENT_TIME_1			(0x00000220)
-#define DP_AUX_CTRL				(0x00000230)
-#define DP_AUX_DATA				(0x00000234)
-#define DP_AUX_TRANS_CTRL			(0x00000238)
-#define DP_TIMEOUT_COUNT			(0x0000023C)
-#define DP_AUX_LIMITS				(0x00000240)
-#define DP_AUX_STATUS				(0x00000244)
+#define DP_DP_HPD_CTRL				(0x00000000)
+#define DP_DP_HPD_INT_STATUS			(0x00000004)
+#define DP_DP_HPD_INT_ACK			(0x00000008)
+#define DP_DP_HPD_INT_MASK			(0x0000000C)
+#define DP_DP_HPD_REFTIMER			(0x00000018)
+#define DP_DP_HPD_EVENT_TIME_0			(0x0000001C)
+#define DP_DP_HPD_EVENT_TIME_1			(0x00000020)
+#define DP_AUX_CTRL				(0x00000030)
+#define DP_AUX_DATA				(0x00000034)
+#define DP_AUX_TRANS_CTRL			(0x00000038)
+#define DP_TIMEOUT_COUNT			(0x0000003C)
+#define DP_AUX_LIMITS				(0x00000040)
+#define DP_AUX_STATUS				(0x00000044)
 
 #define DP_DPCD_CP_IRQ				(0x201)
 #define DP_DPCD_RXSTATUS			(0x69493)
 
-#define DP_INTERRUPT_TRANS_NUM			(0x000002A0)
+#define DP_INTERRUPT_TRANS_NUM			(0x000000A0)
 
-#define DP_MAINLINK_CTRL			(0x00000400)
-#define DP_STATE_CTRL				(0x00000404)
-#define DP_CONFIGURATION_CTRL			(0x00000408)
-#define DP_SOFTWARE_MVID			(0x00000410)
-#define DP_SOFTWARE_NVID			(0x00000418)
-#define DP_TOTAL_HOR_VER			(0x0000041C)
-#define DP_START_HOR_VER_FROM_SYNC		(0x00000420)
-#define DP_HSYNC_VSYNC_WIDTH_POLARITY		(0x00000424)
-#define DP_ACTIVE_HOR_VER			(0x00000428)
-#define DP_MISC1_MISC0				(0x0000042C)
-#define DP_VALID_BOUNDARY			(0x00000430)
-#define DP_VALID_BOUNDARY_2			(0x00000434)
-#define DP_LOGICAL2PHYSCIAL_LANE_MAPPING	(0x00000438)
+#define DP_MAINLINK_CTRL			(0x00000000)
+#define DP_STATE_CTRL				(0x00000004)
+#define DP_CONFIGURATION_CTRL			(0x00000008)
+#define DP_SOFTWARE_MVID			(0x00000010)
+#define DP_SOFTWARE_NVID			(0x00000018)
+#define DP_TOTAL_HOR_VER			(0x0000001C)
+#define DP_START_HOR_VER_FROM_SYNC		(0x00000020)
+#define DP_HSYNC_VSYNC_WIDTH_POLARITY		(0x00000024)
+#define DP_ACTIVE_HOR_VER			(0x00000028)
+#define DP_MISC1_MISC0				(0x0000002C)
+#define DP_VALID_BOUNDARY			(0x00000030)
+#define DP_VALID_BOUNDARY_2			(0x00000034)
+#define DP_LOGICAL2PHYSICAL_LANE_MAPPING	(0x00000038)
 
-#define DP_MAINLINK_READY			(0x00000440)
-#define DP_MAINLINK_LEVELS			(0x00000444)
-#define DP_TU					(0x0000044C)
+#define DP_MAINLINK_READY			(0x00000040)
+#define DP_MAINLINK_LEVELS			(0x00000044)
+#define DP_TU					(0x0000004C)
 
-#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET	(0x00000454)
-#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0	(0x000004C0)
-#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1	(0x000004C4)
-#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2	(0x000004C8)
+#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET	(0x00000054)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0	(0x000000C0)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1	(0x000000C4)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2	(0x000000C8)
 
-#define MMSS_DP_MISC1_MISC0			(0x0000042C)
-#define MMSS_DP_AUDIO_TIMING_GEN		(0x00000480)
-#define MMSS_DP_AUDIO_TIMING_RBR_32		(0x00000484)
-#define MMSS_DP_AUDIO_TIMING_HBR_32		(0x00000488)
-#define MMSS_DP_AUDIO_TIMING_RBR_44		(0x0000048C)
-#define MMSS_DP_AUDIO_TIMING_HBR_44		(0x00000490)
-#define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000494)
-#define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000498)
+#define MMSS_DP_MISC1_MISC0			(0x0000002C)
+#define MMSS_DP_AUDIO_TIMING_GEN		(0x00000080)
+#define MMSS_DP_AUDIO_TIMING_RBR_32		(0x00000084)
+#define MMSS_DP_AUDIO_TIMING_HBR_32		(0x00000088)
+#define MMSS_DP_AUDIO_TIMING_RBR_44		(0x0000008C)
+#define MMSS_DP_AUDIO_TIMING_HBR_44		(0x00000090)
+#define MMSS_DP_AUDIO_TIMING_RBR_48		(0x00000094)
+#define MMSS_DP_AUDIO_TIMING_HBR_48		(0x00000098)
 
-#define MMSS_DP_PSR_CRC_RG			(0x00000554)
-#define MMSS_DP_PSR_CRC_B			(0x00000558)
+#define MMSS_DP_PSR_CRC_RG			(0x00000154)
+#define MMSS_DP_PSR_CRC_B			(0x00000158)
 
-#define MMSS_DP_AUDIO_CFG			(0x00000600)
-#define MMSS_DP_AUDIO_STATUS			(0x00000604)
-#define MMSS_DP_AUDIO_PKT_CTRL			(0x00000608)
-#define MMSS_DP_AUDIO_PKT_CTRL2			(0x0000060C)
-#define MMSS_DP_AUDIO_ACR_CTRL			(0x00000610)
-#define MMSS_DP_AUDIO_CTRL_RESET		(0x00000614)
+#define DP_COMPRESSION_MODE_CTRL		(0x00000180)
 
-#define MMSS_DP_SDP_CFG				(0x00000628)
-#define MMSS_DP_SDP_CFG2			(0x0000062C)
-#define MMSS_DP_AUDIO_TIMESTAMP_0		(0x00000630)
-#define MMSS_DP_AUDIO_TIMESTAMP_1		(0x00000634)
+#define MMSS_DP_AUDIO_CFG			(0x00000200)
+#define MMSS_DP_AUDIO_STATUS			(0x00000204)
+#define MMSS_DP_AUDIO_PKT_CTRL			(0x00000208)
+#define MMSS_DP_AUDIO_PKT_CTRL2			(0x0000020C)
+#define MMSS_DP_AUDIO_ACR_CTRL			(0x00000210)
+#define MMSS_DP_AUDIO_CTRL_RESET		(0x00000214)
 
-#define MMSS_DP_AUDIO_STREAM_0			(0x00000640)
-#define MMSS_DP_AUDIO_STREAM_1			(0x00000644)
+#define MMSS_DP_SDP_CFG				(0x00000228)
+#define MMSS_DP_SDP_CFG2			(0x0000022C)
+#define MMSS_DP_SDP_CFG3			(0x0000024C)
+#define MMSS_DP_AUDIO_TIMESTAMP_0		(0x00000230)
+#define MMSS_DP_AUDIO_TIMESTAMP_1		(0x00000234)
 
-#define MMSS_DP_EXTENSION_0			(0x00000650)
-#define MMSS_DP_EXTENSION_1			(0x00000654)
-#define MMSS_DP_EXTENSION_2			(0x00000658)
-#define MMSS_DP_EXTENSION_3			(0x0000065C)
-#define MMSS_DP_EXTENSION_4			(0x00000660)
-#define MMSS_DP_EXTENSION_5			(0x00000664)
-#define MMSS_DP_EXTENSION_6			(0x00000668)
-#define MMSS_DP_EXTENSION_7			(0x0000066C)
-#define MMSS_DP_EXTENSION_8			(0x00000670)
-#define MMSS_DP_EXTENSION_9			(0x00000674)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_0		(0x00000678)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_1		(0x0000067C)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_2		(0x00000680)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_3		(0x00000684)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_4		(0x00000688)
-#define MMSS_DP_AUDIO_COPYMANAGEMENT_5		(0x0000068C)
-#define MMSS_DP_AUDIO_ISRC_0			(0x00000690)
-#define MMSS_DP_AUDIO_ISRC_1			(0x00000694)
-#define MMSS_DP_AUDIO_ISRC_2			(0x00000698)
-#define MMSS_DP_AUDIO_ISRC_3			(0x0000069C)
-#define MMSS_DP_AUDIO_ISRC_4			(0x000006A0)
-#define MMSS_DP_AUDIO_ISRC_5			(0x000006A4)
-#define MMSS_DP_AUDIO_INFOFRAME_0		(0x000006A8)
-#define MMSS_DP_AUDIO_INFOFRAME_1		(0x000006AC)
-#define MMSS_DP_AUDIO_INFOFRAME_2		(0x000006B0)
+#define MMSS_DP_AUDIO_STREAM_0			(0x00000240)
+#define MMSS_DP_AUDIO_STREAM_1			(0x00000244)
 
-#define MMSS_DP_GENERIC0_0			(0x00000700)
-#define MMSS_DP_GENERIC0_1			(0x00000704)
-#define MMSS_DP_GENERIC0_2			(0x00000708)
-#define MMSS_DP_GENERIC0_3			(0x0000070C)
-#define MMSS_DP_GENERIC0_4			(0x00000710)
-#define MMSS_DP_GENERIC0_5			(0x00000714)
-#define MMSS_DP_GENERIC0_6			(0x00000718)
-#define MMSS_DP_GENERIC0_7			(0x0000071C)
-#define MMSS_DP_GENERIC0_8			(0x00000720)
-#define MMSS_DP_GENERIC0_9			(0x00000724)
-#define MMSS_DP_GENERIC1_0			(0x00000728)
-#define MMSS_DP_GENERIC1_1			(0x0000072C)
-#define MMSS_DP_GENERIC1_2			(0x00000730)
-#define MMSS_DP_GENERIC1_3			(0x00000734)
-#define MMSS_DP_GENERIC1_4			(0x00000738)
-#define MMSS_DP_GENERIC1_5			(0x0000073C)
-#define MMSS_DP_GENERIC1_6			(0x00000740)
-#define MMSS_DP_GENERIC1_7			(0x00000744)
-#define MMSS_DP_GENERIC1_8			(0x00000748)
-#define MMSS_DP_GENERIC1_9			(0x0000074C)
+#define MMSS_DP_EXTENSION_0			(0x00000250)
+#define MMSS_DP_EXTENSION_1			(0x00000254)
+#define MMSS_DP_EXTENSION_2			(0x00000258)
+#define MMSS_DP_EXTENSION_3			(0x0000025C)
+#define MMSS_DP_EXTENSION_4			(0x00000260)
+#define MMSS_DP_EXTENSION_5			(0x00000264)
+#define MMSS_DP_EXTENSION_6			(0x00000268)
+#define MMSS_DP_EXTENSION_7			(0x0000026C)
+#define MMSS_DP_EXTENSION_8			(0x00000270)
+#define MMSS_DP_EXTENSION_9			(0x00000274)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_0		(0x00000278)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_1		(0x0000027C)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_2		(0x00000280)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_3		(0x00000284)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_4		(0x00000288)
+#define MMSS_DP_AUDIO_COPYMANAGEMENT_5		(0x0000028C)
+#define MMSS_DP_AUDIO_ISRC_0			(0x00000290)
+#define MMSS_DP_AUDIO_ISRC_1			(0x00000294)
+#define MMSS_DP_AUDIO_ISRC_2			(0x00000298)
+#define MMSS_DP_AUDIO_ISRC_3			(0x0000029C)
+#define MMSS_DP_AUDIO_ISRC_4			(0x000002A0)
+#define MMSS_DP_AUDIO_ISRC_5			(0x000002A4)
+#define MMSS_DP_AUDIO_INFOFRAME_0		(0x000002A8)
+#define MMSS_DP_AUDIO_INFOFRAME_1		(0x000002AC)
+#define MMSS_DP_AUDIO_INFOFRAME_2		(0x000002B0)
 
-#define MMSS_DP_TIMING_ENGINE_EN		(0x00000A10)
-#define MMSS_DP_ASYNC_FIFO_CONFIG		(0x00000A88)
+#define MMSS_DP_GENERIC0_0			(0x00000300)
+#define MMSS_DP_GENERIC0_1			(0x00000304)
+#define MMSS_DP_GENERIC0_2			(0x00000308)
+#define MMSS_DP_GENERIC0_3			(0x0000030C)
+#define MMSS_DP_GENERIC0_4			(0x00000310)
+#define MMSS_DP_GENERIC0_5			(0x00000314)
+#define MMSS_DP_GENERIC0_6			(0x00000318)
+#define MMSS_DP_GENERIC0_7			(0x0000031C)
+#define MMSS_DP_GENERIC0_8			(0x00000320)
+#define MMSS_DP_GENERIC0_9			(0x00000324)
+#define MMSS_DP_GENERIC1_0			(0x00000328)
+#define MMSS_DP_GENERIC1_1			(0x0000032C)
+#define MMSS_DP_GENERIC1_2			(0x00000330)
+#define MMSS_DP_GENERIC1_3			(0x00000334)
+#define MMSS_DP_GENERIC1_4			(0x00000338)
+#define MMSS_DP_GENERIC1_5			(0x0000033C)
+#define MMSS_DP_GENERIC1_6			(0x00000340)
+#define MMSS_DP_GENERIC1_7			(0x00000344)
+#define MMSS_DP_GENERIC1_8			(0x00000348)
+#define MMSS_DP_GENERIC1_9			(0x0000034C)
+
+#define MMSS_DP_VSCEXT_0			(0x000002D0)
+#define MMSS_DP_VSCEXT_1			(0x000002D4)
+#define MMSS_DP_VSCEXT_2			(0x000002D8)
+#define MMSS_DP_VSCEXT_3			(0x000002DC)
+#define MMSS_DP_VSCEXT_4			(0x000002E0)
+#define MMSS_DP_VSCEXT_5			(0x000002E4)
+#define MMSS_DP_VSCEXT_6			(0x000002E8)
+#define MMSS_DP_VSCEXT_7			(0x000002EC)
+#define MMSS_DP_VSCEXT_8			(0x000002F0)
+#define MMSS_DP_VSCEXT_9			(0x000002F4)
+
+#define MMSS_DP_BIST_ENABLE			(0x00000000)
+#define MMSS_DP_TIMING_ENGINE_EN		(0x00000010)
+#define MMSS_DP_INTF_CONFIG			(0x00000014)
+#define MMSS_DP_INTF_HSYNC_CTL			(0x00000018)
+#define MMSS_DP_INTF_VSYNC_PERIOD_F0		(0x0000001C)
+#define MMSS_DP_INTF_VSYNC_PERIOD_F1		(0x00000020)
+#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0	(0x00000024)
+#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1	(0x00000028)
+#define MMSS_INTF_DISPLAY_V_START_F0		(0x0000002C)
+#define MMSS_INTF_DISPLAY_V_START_F1		(0x00000030)
+#define MMSS_DP_INTF_DISPLAY_V_END_F0		(0x00000034)
+#define MMSS_DP_INTF_DISPLAY_V_END_F1		(0x00000038)
+#define MMSS_DP_INTF_ACTIVE_V_START_F0		(0x0000003C)
+#define MMSS_DP_INTF_ACTIVE_V_START_F1		(0x00000040)
+#define MMSS_DP_INTF_ACTIVE_V_END_F0		(0x00000044)
+#define MMSS_DP_INTF_ACTIVE_V_END_F1		(0x00000048)
+#define MMSS_DP_INTF_DISPLAY_HCTL		(0x0000004C)
+#define MMSS_DP_INTF_ACTIVE_HCTL		(0x00000050)
+#define MMSS_DP_INTF_POLARITY_CTL		(0x00000058)
+#define MMSS_DP_TPG_MAIN_CONTROL		(0x00000060)
+#define MMSS_DP_TPG_VIDEO_CONFIG		(0x00000064)
+#define MMSS_DP_ASYNC_FIFO_CONFIG		(0x00000088)
 
 /*DP PHY Register offsets */
 #define DP_PHY_REVISION_ID0                     (0x00000000)
@@ -166,6 +200,7 @@
 #define DP_PHY_AUX_CFG9                         (0x00000044)
 #define DP_PHY_AUX_INTERRUPT_MASK               (0x00000048)
 #define DP_PHY_AUX_INTERRUPT_CLEAR              (0x0000004C)
+#define DP_PHY_AUX_INTERRUPT_STATUS             (0x000000BC)
 
 #define DP_PHY_SPARE0				(0x00AC)
 
@@ -183,14 +218,14 @@
 /* DP HDCP 1.3 registers */
 #define DP_HDCP_CTRL                                   (0x0A0)
 #define DP_HDCP_STATUS                                 (0x0A4)
-#define DP_HDCP_SW_UPPER_AKSV                          (0x298)
-#define DP_HDCP_SW_LOWER_AKSV                          (0x29C)
-#define DP_HDCP_ENTROPY_CTRL0                          (0x750)
-#define DP_HDCP_ENTROPY_CTRL1                          (0x75C)
+#define DP_HDCP_SW_UPPER_AKSV                          (0x098)
+#define DP_HDCP_SW_LOWER_AKSV                          (0x09C)
+#define DP_HDCP_ENTROPY_CTRL0                          (0x350)
+#define DP_HDCP_ENTROPY_CTRL1                          (0x35C)
 #define DP_HDCP_SHA_STATUS                             (0x0C8)
 #define DP_HDCP_RCVPORT_DATA2_0                        (0x0B0)
-#define DP_HDCP_RCVPORT_DATA3                          (0x2A4)
-#define DP_HDCP_RCVPORT_DATA4                          (0x2A8)
+#define DP_HDCP_RCVPORT_DATA3                          (0x0A4)
+#define DP_HDCP_RCVPORT_DATA4                          (0x0A8)
 #define DP_HDCP_RCVPORT_DATA5                          (0x0C0)
 #define DP_HDCP_RCVPORT_DATA6                          (0x0C4)
 
@@ -205,4 +240,14 @@
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11     (0x01C)
 #define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12     (0x020)
 
+/* USB3 DP COM registers */
+#define USB3_DP_COM_RESET_OVRD_CTRL (0x1C)
+#define USB3_DP_COM_PHY_MODE_CTRL   (0x00)
+#define USB3_DP_COM_SW_RESET        (0x04)
+#define USB3_DP_COM_TYPEC_CTRL      (0x10)
+#define USB3_DP_COM_SWI_CTRL        (0x0c)
+#define USB3_DP_COM_POWER_DOWN_CTRL (0x08)
+
+
+
 #endif /* _DP_REG_H_ */
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.c b/drivers/gpu/drm/msm/dp/dp_usbpd.c
index df43267..3ddc499 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.c
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.c
@@ -64,6 +64,7 @@
 };
 
 struct dp_usbpd_private {
+	bool forced_disconnect;
 	u32 vdo;
 	struct device *dev;
 	struct usbpd *pd;
@@ -73,8 +74,6 @@
 	struct dp_usbpd dp_usbpd;
 	enum dp_usbpd_alt_mode alt_mode;
 	u32 dp_usbpd_config;
-
-	bool forced_disconnect;
 };
 
 static const char *dp_usbpd_pin_name(u8 pin)
@@ -178,7 +177,10 @@
 	u32 config = 0;
 	const u32 ufp_d_config = 0x2, dp_ver = 0x1;
 
-	pin_cfg = pd->cap.dlink_pin_config;
+	if (pd->cap.receptacle_state)
+		pin_cfg = pd->cap.ulink_pin_config;
+	else
+		pin_cfg = pd->cap.dlink_pin_config;
 
 	for (pin = DP_USBPD_PIN_A; pin < DP_USBPD_PIN_MAX; pin++) {
 		if (pin_cfg & BIT(pin)) {
@@ -374,6 +376,18 @@
 
 		pd->dp_usbpd.orientation = usbpd_get_plug_orientation(pd->pd);
 
+		/*
+		 * By default, USB reserves two lanes for Super Speed.
+		 * Which means DP has remaining two lanes to operate on.
+		 * If multi-function is not supported, request USB to
+		 * release the Super Speed lanes so that DP can use
+		 * all four lanes in case DPCD indicates support for
+		 * four lanes.
+		 */
+		if (!pd->dp_usbpd.multi_func)
+			pd->svid_handler.request_usb_ss_lane(pd->pd,
+				&pd->svid_handler);
+
 		if (pd->dp_cb && pd->dp_cb->configure)
 			pd->dp_cb->configure(pd->dev);
 		break;
@@ -383,7 +397,7 @@
 	}
 }
 
-static int dp_usbpd_connect(struct dp_usbpd *dp_usbpd, bool hpd)
+static int dp_usbpd_simulate_connect(struct dp_usbpd *dp_usbpd, bool hpd)
 {
 	int rc = 0;
 	struct dp_usbpd_private *pd;
@@ -451,12 +465,12 @@
 	if (rc) {
 		pr_err("pd registration failed\n");
 		rc = -ENODEV;
-		kfree(usbpd);
+		devm_kfree(dev, usbpd);
 		goto error;
 	}
 
 	dp_usbpd = &usbpd->dp_usbpd;
-	dp_usbpd->connect = dp_usbpd_connect;
+	dp_usbpd->simulate_connect = dp_usbpd_simulate_connect;
 
 	return dp_usbpd;
 error:
@@ -472,5 +486,7 @@
 
 	usbpd = container_of(dp_usbpd, struct dp_usbpd_private, dp_usbpd);
 
-	kfree(usbpd);
+	usbpd_unregister_svid(usbpd->pd, &usbpd->svid_handler);
+
+	devm_kfree(usbpd->dev, usbpd);
 }
diff --git a/drivers/gpu/drm/msm/dp/dp_usbpd.h b/drivers/gpu/drm/msm/dp/dp_usbpd.h
index 2682e98..e70ad7d 100644
--- a/drivers/gpu/drm/msm/dp/dp_usbpd.h
+++ b/drivers/gpu/drm/msm/dp/dp_usbpd.h
@@ -49,7 +49,7 @@
  * @hpd_irq: Change in the status since last message
  * @alt_mode_cfg_done: bool to specify alt mode status
  * @debug_en: bool to specify debug mode
- * @connect: simulate disconnect or connect for debug mode
+ * @simulate_connect: simulate disconnect or connect for debug mode
  */
 struct dp_usbpd {
 	enum dp_usbpd_port port;
@@ -64,7 +64,7 @@
 	bool alt_mode_cfg_done;
 	bool debug_en;
 
-	int (*connect)(struct dp_usbpd *dp_usbpd, bool hpd);
+	int (*simulate_connect)(struct dp_usbpd *dp_usbpd, bool hpd);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index e9dabb1..0ddb47f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -28,6 +28,7 @@
 	ctrl->ops.video_engine_en        = dsi_ctrl_hw_cmn_video_engine_en;
 	ctrl->ops.video_engine_setup     = dsi_ctrl_hw_cmn_video_engine_setup;
 	ctrl->ops.set_video_timing       = dsi_ctrl_hw_cmn_set_video_timing;
+	ctrl->ops.set_timing_db          = dsi_ctrl_hw_cmn_set_timing_db;
 	ctrl->ops.cmd_engine_setup       = dsi_ctrl_hw_cmn_cmd_engine_setup;
 	ctrl->ops.setup_cmd_stream       = dsi_ctrl_hw_cmn_setup_cmd_stream;
 	ctrl->ops.ctrl_en                = dsi_ctrl_hw_cmn_ctrl_en;
@@ -58,6 +59,14 @@
 	ctrl->ops.phy_reset_config = dsi_ctrl_hw_cmn_phy_reset_config;
 	ctrl->ops.setup_misr = dsi_ctrl_hw_cmn_setup_misr;
 	ctrl->ops.collect_misr = dsi_ctrl_hw_cmn_collect_misr;
+	ctrl->ops.debug_bus = dsi_ctrl_hw_cmn_debug_bus;
+	ctrl->ops.get_cmd_read_data = dsi_ctrl_hw_cmn_get_cmd_read_data;
+	ctrl->ops.clear_rdbk_register = dsi_ctrl_hw_cmn_clear_rdbk_reg;
+	ctrl->ops.ctrl_reset = dsi_ctrl_hw_cmn_ctrl_reset;
+	ctrl->ops.mask_error_intr = dsi_ctrl_hw_cmn_mask_error_intr;
+	ctrl->ops.error_intr_ctrl = dsi_ctrl_hw_cmn_error_intr_ctrl;
+	ctrl->ops.get_error_mask = dsi_ctrl_hw_cmn_get_error_mask;
+	ctrl->ops.get_hw_version = dsi_ctrl_hw_cmn_get_hw_version;
 
 	switch (version) {
 	case DSI_CTRL_VERSION_1_4:
@@ -72,6 +81,8 @@
 		ctrl->ops.clamp_disable = dsi_ctrl_hw_14_clamp_disable;
 		ctrl->ops.reg_dump_to_buffer =
 			dsi_ctrl_hw_14_reg_dump_to_buffer;
+		ctrl->ops.schedule_dma_cmd = NULL;
+		ctrl->ops.get_cont_splash_status = NULL;
 		break;
 	case DSI_CTRL_VERSION_2_0:
 		ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
@@ -84,9 +95,13 @@
 		ctrl->ops.ulps_ops.get_lanes_in_ulps = NULL;
 		ctrl->ops.clamp_enable = NULL;
 		ctrl->ops.clamp_disable = NULL;
+		ctrl->ops.schedule_dma_cmd = NULL;
+		ctrl->ops.get_cont_splash_status = NULL;
 		break;
 	case DSI_CTRL_VERSION_2_2:
 		ctrl->ops.phy_reset_config = dsi_ctrl_hw_22_phy_reset_config;
+		ctrl->ops.get_cont_splash_status =
+			dsi_ctrl_hw_22_get_cont_splash_status;
 		ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map;
 		ctrl->ops.wait_for_lane_idle =
 			dsi_ctrl_hw_20_wait_for_lane_idle;
@@ -97,6 +112,7 @@
 		ctrl->ops.ulps_ops.get_lanes_in_ulps = NULL;
 		ctrl->ops.clamp_enable = NULL;
 		ctrl->ops.clamp_disable = NULL;
+		ctrl->ops.schedule_dma_cmd = dsi_ctrl_hw_22_schedule_dma_cmd;
 		break;
 	default:
 		break;
@@ -109,6 +125,7 @@
  * @version:     DSI controller version.
  * @index:       DSI controller instance ID.
  * @phy_isolation_enabled:       DSI controller works isolated from phy.
+ * @null_insertion_enabled:      DSI controller inserts null packet.
  *
  * This function setups the catalog information in the dsi_ctrl_hw object.
  *
@@ -116,7 +133,7 @@
  */
 int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 		   enum dsi_ctrl_version version, u32 index,
-		   bool phy_isolation_enabled)
+		   bool phy_isolation_enabled, bool null_insertion_enabled)
 {
 	int rc = 0;
 
@@ -127,6 +144,7 @@
 	}
 
 	ctrl->index = index;
+	ctrl->null_insertion_enabled = null_insertion_enabled;
 	set_bit(DSI_CTRL_VIDEO_TPG, ctrl->feature_map);
 	set_bit(DSI_CTRL_CMD_TPG, ctrl->feature_map);
 	set_bit(DSI_CTRL_VARIABLE_REFRESH_RATE, ctrl->feature_map);
@@ -190,6 +208,7 @@
 	phy->ops.ulps_ops.is_lanes_in_ulps =
 		dsi_phy_hw_v3_0_is_lanes_in_ulps;
 	phy->ops.phy_timing_val = dsi_phy_hw_timing_val_v3_0;
+	phy->ops.phy_lane_reset = dsi_phy_hw_v3_0_lane_reset;
 }
 
 /**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index e8d594c..735f61f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -24,6 +24,7 @@
  * @version:     DSI controller version.
  * @index:       DSI controller instance ID.
  * @phy_isolation_enabled:       DSI controller works isolated from phy.
+ * @null_insertion_enabled:      DSI controller inserts null packet.
  *
  * This function setups the catalog information in the dsi_ctrl_hw object.
  *
@@ -31,7 +32,7 @@
  */
 int dsi_catalog_ctrl_setup(struct dsi_ctrl_hw *ctrl,
 		   enum dsi_ctrl_version version, u32 index,
-		   bool phy_isolation_enabled);
+		   bool phy_isolation_enabled, bool null_insertion_enabled);
 
 /**
  * dsi_catalog_phy_setup() - return catalog info for dsi phy hardware
@@ -101,9 +102,11 @@
 bool dsi_phy_hw_v3_0_is_lanes_in_ulps(u32 lanes, u32 ulps_lanes);
 int dsi_phy_hw_timing_val_v3_0(struct dsi_phy_per_lane_cfgs *timing_cfg,
 		u32 *timing_val, u32 size);
+int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy);
 
 /* DSI controller common ops */
 u32 dsi_ctrl_hw_cmn_get_interrupt_status(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl);
 void dsi_ctrl_hw_cmn_clear_interrupt_status(struct dsi_ctrl_hw *ctrl, u32 ints);
 void dsi_ctrl_hw_cmn_enable_status_interrupts(struct dsi_ctrl_hw *ctrl,
 					     u32 ints);
@@ -132,7 +135,8 @@
 				       struct dsi_video_engine_cfg *cfg);
 void dsi_ctrl_hw_cmn_set_video_timing(struct dsi_ctrl_hw *ctrl,
 			 struct dsi_mode_info *mode);
-
+void dsi_ctrl_hw_cmn_set_timing_db(struct dsi_ctrl_hw *ctrl,
+				     bool enable);
 void dsi_ctrl_hw_cmn_cmd_engine_setup(struct dsi_ctrl_hw *ctrl,
 				     struct dsi_host_common_cfg *common_cfg,
 				     struct dsi_cmd_engine_cfg *cfg);
@@ -168,6 +172,20 @@
 			bool enable);
 void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
 			bool enable);
+u32 dsi_ctrl_hw_cmn_get_cmd_read_data(struct dsi_ctrl_hw *ctrl,
+				     u8 *rd_buf,
+				     u32 read_offset,
+				     u32 rx_byte,
+				     u32 pkt_size, u32 *hw_read_cnt);
+void dsi_ctrl_hw_cmn_clear_rdbk_reg(struct dsi_ctrl_hw *ctrl);
+void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_on);
+int dsi_ctrl_hw_cmn_ctrl_reset(struct dsi_ctrl_hw *ctrl,
+			int mask);
+void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx,
+			bool en);
+void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en);
+u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl);
+u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl);
 
 /* Definitions specific to 1.4 DSI controller hardware */
 int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
@@ -196,4 +214,7 @@
 					  char *buf,
 					  u32 size);
 
+/* Definitions specific to 2.2 DSI controller hardware */
+bool dsi_ctrl_hw_22_get_cont_splash_status(struct dsi_ctrl_hw *ctrl);
+
 #endif /* _DSI_CATALOG_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
index 2a84a2d..1fd10d9 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
@@ -198,6 +198,13 @@
 };
 
 /**
+ * dsi_display_clk_mngr_update_splash_status() - Update splash stattus
+ * @clk_mngr:     Structure containing DSI clock information
+ * @status:     Splash status
+ */
+void dsi_display_clk_mngr_update_splash_status(void *clk_mgr, bool status);
+
+/**
  * dsi_display_clk_mgr_register() - Register DSI clock manager
  * @info:     Structure containing DSI clock information
  */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
index 560964e..61406fe 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
@@ -46,6 +46,7 @@
 	post_clockon_cb post_clkon_cb;
 	pre_clockon_cb pre_clkon_cb;
 
+	bool is_cont_splash_enabled;
 	void *priv_data;
 };
 
@@ -287,7 +288,18 @@
 static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
 {
 	int rc = 0;
+	struct dsi_clk_mngr *mngr;
 
+	mngr = container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+
+	if (mngr->is_cont_splash_enabled)
+		return 0;
+	/*
+	 * In an ideal world, cont_splash_enabled should not be required inside
+	 * the clock manager. But, in the current driver cont_splash_enabled
+	 * flag is set inside mdp driver and there is no interface event
+	 * associated with this flag setting.
+	 */
 	rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->freq.esc_clk_rate);
 	if (rc) {
 		pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
@@ -1143,6 +1155,19 @@
 	return rc;
 }
 
+void dsi_display_clk_mngr_update_splash_status(void *clk_mgr, bool status)
+{
+	struct dsi_clk_mngr *mngr;
+
+	if (!clk_mgr) {
+		pr_err("Invalid params\n");
+		return;
+	}
+
+	mngr = (struct dsi_clk_mngr *)clk_mgr;
+	mngr->is_cont_splash_enabled = status;
+}
+
 void *dsi_display_clk_mngr_register(struct dsi_clk_info *info)
 {
 	struct dsi_clk_mngr *mngr;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index dcde566..1f10e3c 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -38,6 +38,8 @@
 #define DSI_CTRL_TX_TO_MS     200
 
 #define TO_ON_OFF(x) ((x) ? "ON" : "OFF")
+
+#define CEIL(x, y)              (((x) + ((y)-1)) / (y))
 /**
  * enum dsi_ctrl_driver_ops - controller driver ops
  */
@@ -783,10 +785,14 @@
 	if (host_cfg->data_lanes & DSI_DATA_LANE_3)
 		num_of_lanes++;
 
-	h_period = DSI_H_TOTAL_DSC(timing);
-	v_period = DSI_V_TOTAL(timing);
+	if (config->bit_clk_rate_hz == 0) {
+		h_period = DSI_H_TOTAL_DSC(timing);
+		v_period = DSI_V_TOTAL(timing);
+		bit_rate = h_period * v_period * timing->refresh_rate * bpp * 8;
+	} else {
+		bit_rate = config->bit_clk_rate_hz * num_of_lanes;
+	}
 
-	bit_rate = h_period * v_period * timing->refresh_rate * bpp * 8;
 	bit_rate_per_lane = bit_rate;
 	do_div(bit_rate_per_lane, num_of_lanes);
 	pclk_rate = bit_rate;
@@ -883,13 +889,57 @@
 	if (packet->payload_length > 0)
 		buf[3] |= BIT(6);
 
-	buf[3] |= BIT(7);
+
+	/* send embedded BTA for read commands */
+	if ((buf[2] & 0x3f) == MIPI_DSI_DCS_READ)
+		buf[3] |= BIT(5);
+
 	*buffer = buf;
 	*size = len;
 
 	return rc;
 }
 
+static void dsi_ctrl_wait_for_video_done(struct dsi_ctrl *dsi_ctrl)
+{
+	u32 v_total = 0, v_blank = 0, sleep_ms = 0, fps = 0, ret;
+	struct dsi_mode_info *timing;
+
+	/**
+	 * No need to wait if the panel is not video mode or
+	 * if DSI controller supports command DMA scheduling or
+	 * if we are sending init commands.
+	 */
+	if ((dsi_ctrl->host_config.panel_mode != DSI_OP_VIDEO_MODE) ||
+		(dsi_ctrl->version >= DSI_CTRL_VERSION_2_2) ||
+		(dsi_ctrl->current_state.vid_engine_state !=
+					DSI_CTRL_ENGINE_ON))
+		return;
+
+	dsi_ctrl->hw.ops.clear_interrupt_status(&dsi_ctrl->hw,
+				DSI_VIDEO_MODE_FRAME_DONE);
+
+	dsi_ctrl_enable_status_interrupt(dsi_ctrl,
+				DSI_SINT_VIDEO_MODE_FRAME_DONE, NULL);
+	reinit_completion(&dsi_ctrl->irq_info.vid_frame_done);
+	ret = wait_for_completion_timeout(
+			&dsi_ctrl->irq_info.vid_frame_done,
+			msecs_to_jiffies(DSI_CTRL_TX_TO_MS));
+	if (ret <= 0)
+		pr_debug("wait for video done failed\n");
+	dsi_ctrl_disable_status_interrupt(dsi_ctrl,
+				DSI_SINT_VIDEO_MODE_FRAME_DONE);
+
+	timing = &(dsi_ctrl->host_config.video_timing);
+	v_total = timing->v_sync_width + timing->v_back_porch +
+			timing->v_front_porch + timing->v_active;
+	v_blank = timing->v_sync_width + timing->v_back_porch;
+	fps = timing->refresh_rate;
+
+	sleep_ms = CEIL((v_blank * 1000), (v_total * fps)) + 1;
+	udelay(sleep_ms * 1000);
+}
+
 static int dsi_message_tx(struct dsi_ctrl *dsi_ctrl,
 			  const struct mipi_dsi_msg *msg,
 			  u32 flags)
@@ -901,8 +951,17 @@
 	u32 hw_flags = 0;
 	u32 length = 0;
 	u8 *buffer = NULL;
-	u32 cnt = 0;
+	u32 cnt = 0, line_no = 0x1;
 	u8 *cmdbuf;
+	struct dsi_mode_info *timing;
+
+	/* override cmd fetch mode during secure session */
+	if (dsi_ctrl->secure_mode) {
+		flags &= ~DSI_CTRL_CMD_FETCH_MEMORY;
+		flags |= DSI_CTRL_CMD_FIFO_STORE;
+		pr_debug("[%s] override to TPG during secure session\n",
+				dsi_ctrl->name);
+	}
 
 	rc = mipi_dsi_create_packet(&packet, msg);
 	if (rc) {
@@ -910,20 +969,31 @@
 		goto error;
 	}
 
+	/* fail cmds more than the supported size in TPG mode */
+	if ((flags & DSI_CTRL_CMD_FIFO_STORE) &&
+			(msg->tx_len > DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE)) {
+		pr_err("[%s] TPG cmd size:%zd not supported, secure:%d\n",
+				dsi_ctrl->name, msg->tx_len,
+				dsi_ctrl->secure_mode);
+		rc = -ENOTSUPP;
+		goto error;
+	}
+
+	rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
+			&packet,
+			&buffer,
+			&length);
+	if (rc) {
+		pr_err("[%s] failed to copy message, rc=%d\n",
+				dsi_ctrl->name, rc);
+		goto error;
+	}
+
+	if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND))
+		buffer[3] |= BIT(7);//set the last cmd bit in header.
+
 	if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
-		rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
-				&packet,
-				&buffer,
-				&length);
-
-		if (rc) {
-			pr_err("[%s] failed to copy message, rc=%d\n",
-					dsi_ctrl->name, rc);
-			goto error;
-		}
-
 		cmd_mem.offset = dsi_ctrl->cmd_buffer_iova;
-		cmd_mem.length = length;
 		cmd_mem.en_broadcast = (flags & DSI_CTRL_CMD_BROADCAST) ?
 			true : false;
 		cmd_mem.is_master = (flags & DSI_CTRL_CMD_BROADCAST_MASTER) ?
@@ -932,19 +1002,20 @@
 			true : false;
 
 		cmdbuf = (u8 *)(dsi_ctrl->vaddr);
+
 		for (cnt = 0; cnt < length; cnt++)
-			cmdbuf[cnt] = buffer[cnt];
+			cmdbuf[dsi_ctrl->cmd_len + cnt] = buffer[cnt];
+
+		dsi_ctrl->cmd_len += length;
+
+		if (!(msg->flags & MIPI_DSI_MSG_LASTCOMMAND)) {
+			goto error;
+		} else {
+			cmd_mem.length = dsi_ctrl->cmd_len;
+			dsi_ctrl->cmd_len = 0;
+		}
 
 	} else if (flags & DSI_CTRL_CMD_FIFO_STORE) {
-		rc = dsi_ctrl_copy_and_pad_cmd(dsi_ctrl,
-					       &packet,
-					       &buffer,
-					       &length);
-		if (rc) {
-			pr_err("[%s] failed to copy message, rc=%d\n",
-			       dsi_ctrl->name, rc);
-			goto error;
-		}
 		cmd.command =  (u32 *)buffer;
 		cmd.size = length;
 		cmd.en_broadcast = (flags & DSI_CTRL_CMD_BROADCAST) ?
@@ -955,9 +1026,23 @@
 				  true : false;
 	}
 
+	timing = &(dsi_ctrl->host_config.video_timing);
+	if (timing)
+		line_no += timing->v_back_porch + timing->v_sync_width +
+				timing->v_active;
+	if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) &&
+		dsi_ctrl->hw.ops.schedule_dma_cmd &&
+		(dsi_ctrl->current_state.vid_engine_state ==
+					DSI_CTRL_ENGINE_ON))
+		dsi_ctrl->hw.ops.schedule_dma_cmd(&dsi_ctrl->hw,
+				line_no);
+
 	hw_flags |= (flags & DSI_CTRL_CMD_DEFER_TRIGGER) ?
 			DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER : 0;
 
+	if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND))
+		hw_flags |= DSI_CTRL_CMD_LAST_COMMAND;
+
 	if (flags & DSI_CTRL_CMD_DEFER_TRIGGER) {
 		if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
 			dsi_ctrl->hw.ops.kickoff_command(&dsi_ctrl->hw,
@@ -971,8 +1056,12 @@
 	}
 
 	if (!(flags & DSI_CTRL_CMD_DEFER_TRIGGER)) {
+		dsi_ctrl_wait_for_video_done(dsi_ctrl);
 		dsi_ctrl_enable_status_interrupt(dsi_ctrl,
 					DSI_SINT_CMD_MODE_DMA_DONE, NULL);
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), true);
 		reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done);
 
 		if (flags & DSI_CTRL_CMD_FETCH_MEMORY) {
@@ -990,7 +1079,8 @@
 				msecs_to_jiffies(DSI_CTRL_TX_TO_MS));
 
 		if (ret == 0) {
-			u32 status = 0;
+			u32 status = dsi_ctrl->hw.ops.get_interrupt_status(
+								&dsi_ctrl->hw);
 			u32 mask = DSI_CMD_MODE_DMA_DONE;
 
 			if (status & mask) {
@@ -1012,6 +1102,9 @@
 			}
 		}
 
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), false);
 		dsi_ctrl->hw.ops.reset_cmd_fifo(&dsi_ctrl->hw);
 	}
 error:
@@ -1026,6 +1119,7 @@
 {
 	int rc = 0;
 	u8 tx[2] = { (u8)(size & 0xFF), (u8)(size >> 8) };
+	u32 flags = DSI_CTRL_CMD_FETCH_MEMORY;
 	struct mipi_dsi_msg msg = {
 		.channel = rx_msg->channel,
 		.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
@@ -1033,24 +1127,77 @@
 		.tx_buf = tx,
 	};
 
-	rc = dsi_message_tx(dsi_ctrl, &msg, 0x0);
+	rc = dsi_message_tx(dsi_ctrl, &msg, flags);
 	if (rc)
 		pr_err("failed to send max return size packet, rc=%d\n", rc);
 
 	return rc;
 }
 
+/* Helper functions to support DCS read operation */
+static int dsi_parse_short_read1_resp(const struct mipi_dsi_msg *msg,
+		unsigned char *buff)
+{
+	u8 *data = msg->rx_buf;
+	int read_len = 1;
+
+	if (!data)
+		return 0;
+
+	/* remove dcs type */
+	if (msg->rx_len >= 1)
+		data[0] = buff[1];
+	else
+		read_len = 0;
+
+	return read_len;
+}
+
+static int dsi_parse_short_read2_resp(const struct mipi_dsi_msg *msg,
+		unsigned char *buff)
+{
+	u8 *data = msg->rx_buf;
+	int read_len = 2;
+
+	if (!data)
+		return 0;
+
+	/* remove dcs type */
+	if (msg->rx_len >= 2) {
+		data[0] = buff[1];
+		data[1] = buff[2];
+	} else {
+		read_len = 0;
+	}
+
+	return read_len;
+}
+
+static int dsi_parse_long_read_resp(const struct mipi_dsi_msg *msg,
+		unsigned char *buff)
+{
+	if (!msg->rx_buf)
+		return 0;
+
+	/* remove dcs type */
+	if (msg->rx_buf && msg->rx_len)
+		memcpy(msg->rx_buf, buff + 4, msg->rx_len);
+
+	return msg->rx_len;
+}
+
 static int dsi_message_rx(struct dsi_ctrl *dsi_ctrl,
 			  const struct mipi_dsi_msg *msg,
 			  u32 flags)
 {
 	int rc = 0;
-	u32 rd_pkt_size;
-	u32 total_read_len;
-	u32 bytes_read = 0, tot_bytes_read = 0;
-	u32 current_read_len;
+	u32 rd_pkt_size, total_read_len, hw_read_cnt;
+	u32 current_read_len = 0, total_bytes_read = 0;
 	bool short_resp = false;
 	bool read_done = false;
+	u32 dlen, diff, rlen = msg->rx_len;
+	unsigned char *buff;
+	char cmd;
 
 	if (msg->rx_len <= 2) {
 		short_resp = true;
@@ -1066,6 +1213,7 @@
 
 		total_read_len = current_read_len + 6;
 	}
+	buff = msg->rx_buf;
 
 	while (!read_done) {
 		rc = dsi_set_max_return_size(dsi_ctrl, msg, rd_pkt_size);
@@ -1075,24 +1223,79 @@
 			goto error;
 		}
 
+		/* clear RDBK_DATA registers before proceeding */
+		dsi_ctrl->hw.ops.clear_rdbk_register(&dsi_ctrl->hw);
+
 		rc = dsi_message_tx(dsi_ctrl, msg, flags);
 		if (rc) {
 			pr_err("Message transmission failed, rc=%d\n", rc);
 			goto error;
 		}
 
+		dlen = dsi_ctrl->hw.ops.get_cmd_read_data(&dsi_ctrl->hw,
+					buff, total_bytes_read,
+					total_read_len, rd_pkt_size,
+					&hw_read_cnt);
+		if (!dlen)
+			goto error;
 
-		tot_bytes_read += bytes_read;
 		if (short_resp)
+			break;
+
+		if (rlen <= current_read_len) {
+			diff = current_read_len - rlen;
 			read_done = true;
-		else if (msg->rx_len <= tot_bytes_read)
-			read_done = true;
+		} else {
+			diff = 0;
+			rlen -= current_read_len;
+		}
+
+		dlen -= 2; /* 2 bytes of CRC */
+		dlen -= diff;
+		buff += dlen;
+		total_bytes_read += dlen;
+		if (!read_done) {
+			current_read_len = 14; /* Not first read */
+			if (rlen < current_read_len)
+				rd_pkt_size += rlen;
+			else
+				rd_pkt_size += current_read_len;
+		}
 	}
+
+	if (hw_read_cnt < 16 && !short_resp)
+		buff = msg->rx_buf + (16 - hw_read_cnt);
+	else
+		buff = msg->rx_buf;
+
+	/* parse the data read from panel */
+	cmd = buff[0];
+	switch (cmd) {
+	case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
+		pr_err("Rx ACK_ERROR\n");
+		rc = 0;
+		break;
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
+		rc = dsi_parse_short_read1_resp(msg, buff);
+		break;
+	case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
+	case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
+		rc = dsi_parse_short_read2_resp(msg, buff);
+		break;
+	case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
+	case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
+		rc = dsi_parse_long_read_resp(msg, buff);
+		break;
+	default:
+		pr_warn("Invalid response\n");
+		rc = 0;
+	}
+
 error:
 	return rc;
 }
 
-
 static int dsi_enable_ulps(struct dsi_ctrl *dsi_ctrl)
 {
 	int rc = 0;
@@ -1193,7 +1396,9 @@
 
 		msm_gem_put_iova(dsi_ctrl->tx_cmd_buf, aspace);
 
+		mutex_lock(&dsi_ctrl->drm_dev->struct_mutex);
 		msm_gem_free_object(dsi_ctrl->tx_cmd_buf);
+		mutex_unlock(&dsi_ctrl->drm_dev->struct_mutex);
 		dsi_ctrl->tx_cmd_buf = NULL;
 	}
 
@@ -1288,6 +1493,9 @@
 	dsi_ctrl->phy_isolation_enabled = of_property_read_bool(of_node,
 				    "qcom,dsi-phy-isolation-enabled");
 
+	dsi_ctrl->null_insertion_enabled = of_property_read_bool(of_node,
+					"qcom,null-insertion-enabled");
+
 	return 0;
 }
 
@@ -1345,7 +1553,8 @@
 	}
 
 	rc = dsi_catalog_ctrl_setup(&dsi_ctrl->hw, dsi_ctrl->version,
-		    dsi_ctrl->cell_index, dsi_ctrl->phy_isolation_enabled);
+		dsi_ctrl->cell_index, dsi_ctrl->phy_isolation_enabled,
+		dsi_ctrl->null_insertion_enabled);
 	if (rc) {
 		pr_err("Catalog does not support version (%d)\n",
 		       dsi_ctrl->version);
@@ -1363,6 +1572,7 @@
 	mutex_unlock(&dsi_ctrl_list_lock);
 
 	mutex_init(&dsi_ctrl->ctrl_lock);
+	dsi_ctrl->secure_mode = false;
 
 	dsi_ctrl->pdev = pdev;
 	platform_set_drvdata(pdev, dsi_ctrl);
@@ -1429,6 +1639,26 @@
 	},
 };
 
+#if defined(CONFIG_DEBUG_FS)
+
+void dsi_ctrl_debug_dump(void)
+{
+	struct list_head *pos, *tmp;
+	struct dsi_ctrl *ctrl = NULL;
+
+	mutex_lock(&dsi_ctrl_list_lock);
+	list_for_each_safe(pos, tmp, &dsi_ctrl_list) {
+		struct dsi_ctrl_list_item *n;
+
+		n = list_entry(pos, struct dsi_ctrl_list_item, list);
+		ctrl = n->ctrl;
+		pr_err("dsi ctrl:%d\n", ctrl->cell_index);
+		ctrl->hw.ops.debug_bus(&ctrl->hw);
+	}
+	mutex_unlock(&dsi_ctrl_list_lock);
+}
+
+#endif
 /**
  * dsi_ctrl_get() - get a dsi_ctrl handle from an of_node
  * @of_node:    of_node of the DSI controller.
@@ -1465,7 +1695,9 @@
 	mutex_lock(&ctrl->ctrl_lock);
 	if (ctrl->refcount == 1) {
 		pr_err("[%s] Device in use\n", ctrl->name);
+		mutex_unlock(&ctrl->ctrl_lock);
 		ctrl = ERR_PTR(-EBUSY);
+		return ctrl;
 	} else {
 		ctrl->refcount++;
 	}
@@ -1645,7 +1877,7 @@
 
 	host_mode = &dsi_ctrl->host_config.video_timing;
 	memcpy(host_mode, timing, sizeof(*host_mode));
-
+	dsi_ctrl->hw.ops.set_timing_db(&dsi_ctrl->hw, true);
 	dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw, host_mode);
 
 exit:
@@ -1653,6 +1885,53 @@
 	return rc;
 }
 
+/**
+ * dsi_ctrl_timing_db_update() - update only controller Timing DB
+ * @dsi_ctrl:          DSI controller handle.
+ * @enable:            Enable/disable Timing DB register
+ *
+ *  Update timing db register value during dfps usecases
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_timing_db_update(struct dsi_ctrl *dsi_ctrl,
+		bool enable)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl) {
+		pr_err("Invalid dsi_ctrl\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
+	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_ASYNC_TIMING,
+			DSI_CTRL_ENGINE_ON);
+	if (rc) {
+		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
+		       dsi_ctrl->cell_index, rc);
+		goto exit;
+	}
+
+	/*
+	 * Add HW recommended delay for dfps feature.
+	 * When prefetch is enabled, MDSS HW works on 2 vsync
+	 * boundaries i.e. mdp_vsync and panel_vsync.
+	 * In the current implementation we are only waiting
+	 * for mdp_vsync. We need to make sure that interface
+	 * flush is after panel_vsync. So, added the recommended
+	 * delays after dfps update.
+	 */
+	usleep_range(2000, 2010);
+
+	dsi_ctrl->hw.ops.set_timing_db(&dsi_ctrl->hw, enable);
+
+exit:
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+	return rc;
+}
+
 int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
 {
 	int rc = 0;
@@ -1691,7 +1970,7 @@
 	}
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
 	dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
@@ -1742,33 +2021,77 @@
 static void dsi_ctrl_handle_error_status(struct dsi_ctrl *dsi_ctrl,
 				unsigned long int error)
 {
-	pr_err("%s: %lu\n", __func__, error);
+	struct dsi_event_cb_info cb_info;
+
+	cb_info = dsi_ctrl->irq_info.irq_err_cb;
+
+	/* disable error interrupts */
+	if (dsi_ctrl->hw.ops.error_intr_ctrl)
+		dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, false);
+
+	/* clear error interrupts first */
+	if (dsi_ctrl->hw.ops.clear_error_status)
+		dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
+					error);
 
 	/* DTLN PHY error */
-	if (error & 0x3000e00)
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0x3000e00);
+	if (error & 0x3000E00)
+		pr_err("dsi PHY contention error: 0x%lx\n", error);
+
+	/* TX timeout error */
+	if (error & 0xE0) {
+		if (error & 0xA0) {
+			if (cb_info.event_cb) {
+				cb_info.event_idx = DSI_LP_Rx_TIMEOUT;
+				(void)cb_info.event_cb(cb_info.event_usr_ptr,
+							cb_info.event_idx,
+							dsi_ctrl->cell_index,
+							0, 0, 0, 0);
+			}
+		}
+		pr_err("tx timeout error: 0x%lx\n", error);
+	}
 
 	/* DSI FIFO OVERFLOW error */
-	if (error & 0xf0000) {
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0xf0000);
+	if (error & 0xF0000) {
+		u32 mask = 0;
+
+		if (dsi_ctrl->hw.ops.get_error_mask)
+			mask = dsi_ctrl->hw.ops.get_error_mask(&dsi_ctrl->hw);
+		/* no need to report FIFO overflow if already masked */
+		if (cb_info.event_cb && !(mask & 0xf0000)) {
+			cb_info.event_idx = DSI_FIFO_OVERFLOW;
+			(void)cb_info.event_cb(cb_info.event_usr_ptr,
+						cb_info.event_idx,
+						dsi_ctrl->cell_index,
+						0, 0, 0, 0);
+			pr_err("dsi FIFO OVERFLOW error: 0x%lx\n", error);
+		}
 	}
 
 	/* DSI FIFO UNDERFLOW error */
-	if (error & 0xf00000) {
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					0xf00000);
+	if (error & 0xF00000) {
+		if (cb_info.event_cb) {
+			cb_info.event_idx = DSI_FIFO_UNDERFLOW;
+			(void)cb_info.event_cb(cb_info.event_usr_ptr,
+						cb_info.event_idx,
+						dsi_ctrl->cell_index,
+						0, 0, 0, 0);
+		}
+		pr_err("dsi FIFO UNDERFLOW error: 0x%lx\n", error);
 	}
 
 	/* DSI PLL UNLOCK error */
 	if (error & BIT(8))
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-					BIT(8));
+		pr_err("dsi PLL unlock error: 0x%lx\n", error);
+
+	/* ACK error */
+	if (error & 0xF)
+		pr_err("ack error: 0x%lx\n", error);
+
+	/* enable back DSI interrupts */
+	if (dsi_ctrl->hw.ops.error_intr_ctrl)
+		dsi_ctrl->hw.ops.error_intr_ctrl(&dsi_ctrl->hw, true);
 }
 
 /**
@@ -1782,39 +2105,28 @@
 	struct dsi_ctrl *dsi_ctrl;
 	struct dsi_event_cb_info cb_info;
 	unsigned long flags;
-	uint32_t cell_index, status, i;
-	uint64_t errors;
+	uint32_t status = 0x0, i;
+	uint64_t errors = 0x0;
 
 	if (!ptr)
 		return IRQ_NONE;
 	dsi_ctrl = ptr;
 
-	/* clear status interrupts */
+	/* check status interrupts */
 	if (dsi_ctrl->hw.ops.get_interrupt_status)
 		status = dsi_ctrl->hw.ops.get_interrupt_status(&dsi_ctrl->hw);
-	else
-		status = 0x0;
 
-	if (dsi_ctrl->hw.ops.clear_interrupt_status)
-		dsi_ctrl->hw.ops.clear_interrupt_status(&dsi_ctrl->hw, status);
-
-	spin_lock_irqsave(&dsi_ctrl->irq_info.irq_lock, flags);
-	cell_index = dsi_ctrl->cell_index;
-	spin_unlock_irqrestore(&dsi_ctrl->irq_info.irq_lock, flags);
-
-	/* clear error interrupts */
+	/* check error interrupts */
 	if (dsi_ctrl->hw.ops.get_error_status)
 		errors = dsi_ctrl->hw.ops.get_error_status(&dsi_ctrl->hw);
-	else
-		errors = 0x0;
 
-	if (errors) {
-		/* handle DSI error recovery */
+	/* clear interrupts */
+	if (dsi_ctrl->hw.ops.clear_interrupt_status)
+		dsi_ctrl->hw.ops.clear_interrupt_status(&dsi_ctrl->hw, 0x0);
+
+	/* handle DSI error recovery */
+	if (status & DSI_ERROR)
 		dsi_ctrl_handle_error_status(dsi_ctrl, errors);
-		if (dsi_ctrl->hw.ops.clear_error_status)
-			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
-							errors);
-	}
 
 	if (status & DSI_CMD_MODE_DMA_DONE) {
 		dsi_ctrl_disable_status_interrupt(dsi_ctrl,
@@ -1835,9 +2147,16 @@
 	}
 
 	if (status & DSI_BTA_DONE) {
+		u32 fifo_overflow_mask = (DSI_DLN0_HS_FIFO_OVERFLOW |
+					DSI_DLN1_HS_FIFO_OVERFLOW |
+					DSI_DLN2_HS_FIFO_OVERFLOW |
+					DSI_DLN3_HS_FIFO_OVERFLOW);
 		dsi_ctrl_disable_status_interrupt(dsi_ctrl,
 					DSI_SINT_BTA_DONE);
 		complete_all(&dsi_ctrl->irq_info.bta_done);
+		if (dsi_ctrl->hw.ops.clear_error_status)
+			dsi_ctrl->hw.ops.clear_error_status(&dsi_ctrl->hw,
+					fifo_overflow_mask);
 	}
 
 	for (i = 0; status && i < DSI_STATUS_INTERRUPT_COUNT; ++i) {
@@ -1850,7 +2169,8 @@
 			if (cb_info.event_cb)
 				(void)cb_info.event_cb(cb_info.event_usr_ptr,
 						cb_info.event_idx,
-						cell_index, irq, 0, 0, 0);
+						dsi_ctrl->cell_index,
+						irq, 0, 0, 0);
 		}
 		status >>= 1;
 	}
@@ -2002,6 +2322,7 @@
 /**
  * dsi_ctrl_host_init() - Initialize DSI host hardware.
  * @dsi_ctrl:        DSI controller handle.
+ * @is_splash_enabled:        boolean signifying splash status.
  *
  * Initializes DSI controller hardware with host configuration provided by
  * dsi_ctrl_update_host_config(). Initialization can be performed only during
@@ -2010,7 +2331,7 @@
  *
  * Return: error code.
  */
-int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl)
+int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool is_splash_enabled)
 {
 	int rc = 0;
 
@@ -2027,37 +2348,42 @@
 		goto error;
 	}
 
-	dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
+	/* For Splash usecases we omit hw operations as bootloader
+	 * already takes care of them
+	 */
+	if (!is_splash_enabled) {
+		dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
 					&dsi_ctrl->host_config.lane_map);
 
-	dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw,
+		dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw,
 				    &dsi_ctrl->host_config.common_config);
 
-	if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) {
-		dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw,
+		if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) {
+			dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw,
 					&dsi_ctrl->host_config.common_config,
 					&dsi_ctrl->host_config.u.cmd_engine);
 
-		dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
+			dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
 				&dsi_ctrl->host_config.video_timing,
 				dsi_ctrl->host_config.video_timing.h_active * 3,
 				0x0,
 				NULL);
-	} else {
-		dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
+		} else {
+			dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
 					&dsi_ctrl->host_config.common_config,
 					&dsi_ctrl->host_config.u.video_engine);
-		dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw,
+			dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw,
 					  &dsi_ctrl->host_config.video_timing);
+		}
 	}
 
 	dsi_ctrl_setup_isr(dsi_ctrl);
 
 	dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
-	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+	dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0xFF00E0);
 
-	pr_debug("[DSI_%d]Host initialization complete\n",
-		dsi_ctrl->cell_index);
+	pr_debug("[DSI_%d]Host initialization complete, continuous splash status:%d\n",
+		dsi_ctrl->cell_index, is_splash_enabled);
 	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_HOST_INIT, 0x1);
 error:
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
@@ -2077,6 +2403,48 @@
 	return 0;
 }
 
+int dsi_ctrl_reset(struct dsi_ctrl *dsi_ctrl, int mask)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	rc = dsi_ctrl->hw.ops.ctrl_reset(&dsi_ctrl->hw, mask);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
+int dsi_ctrl_get_hw_version(struct dsi_ctrl *dsi_ctrl)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	rc = dsi_ctrl->hw.ops.get_hw_version(&dsi_ctrl->hw);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
+int dsi_ctrl_vid_engine_en(struct dsi_ctrl *dsi_ctrl, bool on)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl)
+		return -EINVAL;
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+	dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, on);
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+
+	return rc;
+}
+
 /**
  * dsi_ctrl_host_deinit() - De-Initialize DSI host hardware.
  * @dsi_ctrl:        DSI controller handle.
@@ -2146,7 +2514,7 @@
 		goto error;
 	}
 
-	if (!(flags & DSI_MODE_FLAG_SEAMLESS)) {
+	if (!(flags & (DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR))) {
 		rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle);
 		if (rc) {
 			pr_err("[%s] failed to update link frequencies, rc=%d\n",
@@ -2231,8 +2599,8 @@
 
 	if (flags & DSI_CTRL_CMD_READ) {
 		rc = dsi_message_rx(dsi_ctrl, msg, flags);
-		if (rc)
-			pr_err("read message failed, rc=%d\n", rc);
+		if (rc <= 0)
+			pr_err("read message failed read length, rc=%d\n", rc);
 	} else {
 		rc = dsi_message_tx(dsi_ctrl, msg, flags);
 		if (rc)
@@ -2264,6 +2632,10 @@
 		return -EINVAL;
 	}
 
+	/* Dont trigger the command if this is not the last ocmmand */
+	if (!(flags & DSI_CTRL_CMD_LAST_COMMAND))
+		return rc;
+
 	mutex_lock(&dsi_ctrl->ctrl_lock);
 
 	if (!(flags & DSI_CTRL_CMD_BROADCAST_MASTER))
@@ -2271,8 +2643,12 @@
 
 	if ((flags & DSI_CTRL_CMD_BROADCAST) &&
 		(flags & DSI_CTRL_CMD_BROADCAST_MASTER)) {
+		dsi_ctrl_wait_for_video_done(dsi_ctrl);
 		dsi_ctrl_enable_status_interrupt(dsi_ctrl,
 					DSI_SINT_CMD_MODE_DMA_DONE, NULL);
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), true);
 		reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done);
 
 		/* trigger command */
@@ -2303,6 +2679,9 @@
 						dsi_ctrl->cell_index);
 			}
 		}
+		if (dsi_ctrl->hw.ops.mask_error_intr)
+			dsi_ctrl->hw.ops.mask_error_intr(&dsi_ctrl->hw,
+					BIT(DSI_FIFO_OVERFLOW), false);
 	}
 
 	mutex_unlock(&dsi_ctrl->ctrl_lock);
@@ -2332,6 +2711,43 @@
 }
 
 /**
+ * dsi_ctrl_update_host_engine_state_for_cont_splash() -
+ *            set engine state for dsi controller during continuous splash
+ * @dsi_ctrl:          DSI controller handle.
+ * @state:             Engine state.
+ *
+ * Set host engine state for DSI controller during continuous splash.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_update_host_engine_state_for_cont_splash(struct dsi_ctrl *dsi_ctrl,
+					enum dsi_engine_state state)
+{
+	int rc = 0;
+
+	if (!dsi_ctrl || (state >= DSI_CTRL_ENGINE_MAX)) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&dsi_ctrl->ctrl_lock);
+
+	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_HOST_ENGINE, state);
+	if (rc) {
+		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
+		       dsi_ctrl->cell_index, rc);
+		goto error;
+	}
+
+	pr_debug("[DSI_%d] Set host engine state = %d\n", dsi_ctrl->cell_index,
+		 state);
+	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_HOST_ENGINE, state);
+error:
+	mutex_unlock(&dsi_ctrl->ctrl_lock);
+	return rc;
+}
+
+/**
  * dsi_ctrl_set_power_state() - set power state for dsi controller
  * @dsi_ctrl:          DSI controller handle.
  * @state:             Power state.
@@ -2501,8 +2917,6 @@
 		return -EINVAL;
 	}
 
-	mutex_lock(&dsi_ctrl->ctrl_lock);
-
 	rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CMD_ENGINE, state);
 	if (rc) {
 		pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
@@ -2519,7 +2933,6 @@
 		 state);
 	dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_ENGINE, state);
 error:
-	mutex_unlock(&dsi_ctrl->ctrl_lock);
 	return rc;
 }
 
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index f10c7a6..f5b08a0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -35,6 +35,8 @@
  *				   reading data from memory.
  * @DSI_CTRL_CMD_FETCH_MEMORY:     Fetch command from memory through AXI bus
  *				   and transfer it.
+ * @DSI_CTRL_CMD_LAST_COMMAND:     Trigger the DMA cmd transfer if this is last
+ *				   command in the batch.
  */
 #define DSI_CTRL_CMD_READ             0x1
 #define DSI_CTRL_CMD_BROADCAST        0x2
@@ -42,6 +44,10 @@
 #define DSI_CTRL_CMD_DEFER_TRIGGER    0x8
 #define DSI_CTRL_CMD_FIFO_STORE       0x10
 #define DSI_CTRL_CMD_FETCH_MEMORY     0x20
+#define DSI_CTRL_CMD_LAST_COMMAND     0x40
+
+/* max size supported for dsi cmd transfer using TPG */
+#define DSI_CTRL_MAX_CMD_FIFO_STORE_SIZE 64
 
 /**
  * enum dsi_power_state - defines power states for dsi controller.
@@ -143,6 +149,7 @@
  * @irq_stat_mask:       Hardware mask of currently enabled interrupts.
  * @irq_stat_refcount:   Number of times each interrupt has been requested.
  * @irq_stat_cb:         Status IRQ callback definitions.
+ * @irq_err_cb:          IRQ callback definition to handle DSI ERRORs.
  * @cmd_dma_done:          Completion signal for DSI_CMD_MODE_DMA_DONE interrupt
  * @vid_frame_done:        Completion signal for DSI_VIDEO_MODE_FRAME_DONE int.
  * @cmd_frame_done:        Completion signal for DSI_CMD_FRAME_DONE interrupt.
@@ -153,6 +160,7 @@
 	uint32_t irq_stat_mask;
 	int irq_stat_refcount[DSI_STATUS_INTERRUPT_COUNT];
 	struct dsi_event_cb_info irq_stat_cb[DSI_STATUS_INTERRUPT_COUNT];
+	struct dsi_event_cb_info irq_err_cb;
 
 	struct completion cmd_dma_done;
 	struct completion vid_frame_done;
@@ -174,6 +182,7 @@
  * @current_state:       Current driver and hardware state.
  * @clk_cb:		 Callback for DSI clock control.
  * @irq_info:            Interrupt information.
+ * @recovery_cb:         Recovery call back to SDE.
  * @clk_info:            Clock information.
  * @clk_freq:            DSi Link clock frequency information.
  * @pwr_info:            Power information.
@@ -185,13 +194,16 @@
  *                       Origin is top left of this CTRL.
  * @tx_cmd_buf:          Tx command buffer.
  * @cmd_buffer_iova:     cmd buffer mapped address.
- * @vaddr:		 CPU virtual address of cmd buffer.
  * @cmd_buffer_size:     Size of command buffer.
+ * @vaddr:               CPU virtual address of cmd buffer.
+ * @secure_mode:         Indicates if secure-session is in progress
  * @debugfs_root:        Root for debugfs entries.
  * @misr_enable:         Frame MISR enable/disable
  * @misr_cache:          Cached Frame MISR value
  * @phy_isolation_enabled:    A boolean property allows to isolate the phy from
  *                          dsi controller and run only dsi controller.
+ * @null_insertion_enabled:  A boolean property to allow dsi controller to
+ *                           insert null packet.
  */
 struct dsi_ctrl {
 	struct platform_device *pdev;
@@ -210,6 +222,7 @@
 	struct clk_ctrl_cb clk_cb;
 
 	struct dsi_ctrl_interrupts irq_info;
+	struct dsi_event_cb_info recovery_cb;
 
 	/* Clock and power states */
 	struct dsi_ctrl_clk_info clk_info;
@@ -225,7 +238,9 @@
 	struct drm_gem_object *tx_cmd_buf;
 	u32 cmd_buffer_size;
 	u32 cmd_buffer_iova;
+	u32 cmd_len;
 	void *vaddr;
+	u32 secure_mode;
 
 	/* Debug Information */
 	struct dentry *debugfs_root;
@@ -235,6 +250,7 @@
 	u32 misr_cache;
 
 	bool phy_isolation_enabled;
+	bool null_insertion_enabled;
 };
 
 /**
@@ -311,6 +327,18 @@
 				int flags, void *clk_handle);
 
 /**
+ * dsi_ctrl_timing_db_update() - update only controller Timing DB
+ * @dsi_ctrl:          DSI controller handle.
+ * @enable:            Enable/disable Timing DB register
+ *
+ * Update timing db register value during dfps usecases
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_timing_db_update(struct dsi_ctrl *dsi_ctrl,
+		bool enable);
+
+/**
  * dsi_ctrl_async_timing_update() - update only controller timing
  * @dsi_ctrl:          DSI controller handle.
  * @timing:            New DSI timing info
@@ -376,6 +404,7 @@
 /**
  * dsi_ctrl_host_init() - Initialize DSI host hardware.
  * @dsi_ctrl:        DSI controller handle.
+ * @is_splash_enabled:       boolean signifying splash status.
  *
  * Initializes DSI controller hardware with host configuration provided by
  * dsi_ctrl_update_host_config(). Initialization can be performed only during
@@ -384,7 +413,7 @@
  *
  * Return: error code.
  */
-int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl);
+int dsi_ctrl_host_init(struct dsi_ctrl *dsi_ctrl, bool is_splash_enabled);
 
 /**
  * dsi_ctrl_host_deinit() - De-Initialize DSI host hardware.
@@ -474,6 +503,17 @@
 int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags);
 
 /**
+ * dsi_ctrl_update_host_engine_state_for_cont_splash() - update engine
+ *                                 states for cont splash usecase
+ * @dsi_ctrl:              DSI controller handle.
+ * @state:                 DSI engine state
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_update_host_engine_state_for_cont_splash(struct dsi_ctrl *dsi_ctrl,
+				enum dsi_engine_state state);
+
+/**
  * dsi_ctrl_set_power_state() - set power state for dsi controller
  * @dsi_ctrl:          DSI controller handle.
  * @state:             Power state.
@@ -620,4 +660,24 @@
  */
 void dsi_ctrl_drv_unregister(void);
 
+/**
+ * dsi_ctrl_reset() - Reset DSI PHY CLK/DATA lane
+ * @dsi_ctrl:        DSI controller handle.
+ * @mask:	     Mask to indicate if CLK and/or DATA lane needs reset.
+ */
+int dsi_ctrl_reset(struct dsi_ctrl *dsi_ctrl, int mask);
+
+/**
+ * dsi_ctrl_get_hw_version() - read dsi controller hw revision
+ * @dsi_ctrl:        DSI controller handle.
+ */
+int dsi_ctrl_get_hw_version(struct dsi_ctrl *dsi_ctrl);
+
+/**
+ * dsi_ctrl_vid_engine_en() - Control DSI video engine HW state
+ * @dsi_ctrl:        DSI controller handle.
+ * @on:		variable to control video engine ON/OFF.
+ */
+int dsi_ctrl_vid_engine_en(struct dsi_ctrl *dsi_ctrl, bool on);
+
 #endif /* _DSI_CTRL_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index 57f9bcd..c77065c 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -96,6 +96,7 @@
  * @DSI_SINT_DESKEW_DONE:              The deskew calibration operation done.
  * @DSI_SINT_DYN_BLANK_DMA_DONE:       The dynamic blankin DMA operation has
  *                                     completed.
+ * @DSI_SINT_ERROR:                    DSI error has happened.
  */
 enum dsi_status_int_index {
 	DSI_SINT_CMD_MODE_DMA_DONE = 0,
@@ -108,6 +109,7 @@
 	DSI_SINT_DYN_REFRESH_DONE = 7,
 	DSI_SINT_DESKEW_DONE = 8,
 	DSI_SINT_DYN_BLANK_DMA_DONE = 9,
+	DSI_SINT_ERROR = 10,
 
 	DSI_STATUS_INTERRUPT_COUNT
 };
@@ -126,6 +128,7 @@
  * @DSI_DESKEW_DONE:              The deskew calibration operation has completed
  * @DSI_DYN_BLANK_DMA_DONE:       The dynamic blankin DMA operation has
  *                                completed.
+ * @DSI_ERROR:                    DSI error has happened.
  */
 enum dsi_status_int_type {
 	DSI_CMD_MODE_DMA_DONE = BIT(DSI_SINT_CMD_MODE_DMA_DONE),
@@ -137,7 +140,8 @@
 	DSI_CMD_FRAME_DONE = BIT(DSI_SINT_CMD_FRAME_DONE),
 	DSI_DYN_REFRESH_DONE = BIT(DSI_SINT_DYN_REFRESH_DONE),
 	DSI_DESKEW_DONE = BIT(DSI_SINT_DESKEW_DONE),
-	DSI_DYN_BLANK_DMA_DONE = BIT(DSI_SINT_DYN_BLANK_DMA_DONE)
+	DSI_DYN_BLANK_DMA_DONE = BIT(DSI_SINT_DYN_BLANK_DMA_DONE),
+	DSI_ERROR = BIT(DSI_SINT_ERROR)
 };
 
 /**
@@ -175,6 +179,7 @@
  * @DSI_EINT_DLN1_LP1_CONTENTION:        PHY level contention while lane 1 high.
  * @DSI_EINT_DLN2_LP1_CONTENTION:        PHY level contention while lane 2 high.
  * @DSI_EINT_DLN3_LP1_CONTENTION:        PHY level contention while lane 3 high.
+ * @DSI_EINT_PANEL_SPECIFIC_ERR:         DSI Protocol violation error.
  */
 enum dsi_error_int_index {
 	DSI_EINT_RDBK_SINGLE_ECC_ERR = 0,
@@ -209,6 +214,7 @@
 	DSI_EINT_DLN1_LP1_CONTENTION = 29,
 	DSI_EINT_DLN2_LP1_CONTENTION = 30,
 	DSI_EINT_DLN3_LP1_CONTENTION = 31,
+	DSI_EINT_PANEL_SPECIFIC_ERR = 32,
 
 	DSI_ERROR_INTERRUPT_COUNT
 };
@@ -248,6 +254,7 @@
  * @DSI_DLN1_LP1_CONTENTION:        PHY level contention while lane 1 is high.
  * @DSI_DLN2_LP1_CONTENTION:        PHY level contention while lane 2 is high.
  * @DSI_DLN3_LP1_CONTENTION:        PHY level contention while lane 3 is high.
+ * @DSI_PANEL_SPECIFIC_ERR:         DSI Protocol violation.
  */
 enum dsi_error_int_type {
 	DSI_RDBK_SINGLE_ECC_ERR = BIT(DSI_EINT_RDBK_SINGLE_ECC_ERR),
@@ -282,6 +289,7 @@
 	DSI_DLN1_LP1_CONTENTION = BIT(DSI_EINT_DLN1_LP1_CONTENTION),
 	DSI_DLN2_LP1_CONTENTION = BIT(DSI_EINT_DLN2_LP1_CONTENTION),
 	DSI_DLN3_LP1_CONTENTION = BIT(DSI_EINT_DLN3_LP1_CONTENTION),
+	DSI_PANEL_SPECIFIC_ERR = BIT(DSI_EINT_PANEL_SPECIFIC_ERR),
 };
 
 /**
@@ -445,6 +453,12 @@
 	void (*phy_sw_reset)(struct dsi_ctrl_hw *ctrl);
 
 	/**
+	 * debug_bus() - get dsi debug bus status.
+	 * @ctrl:        Pointer to the controller host hardware.
+	 */
+	void (*debug_bus)(struct dsi_ctrl_hw *ctrl);
+
+	/**
 	 * soft_reset() - perform a soft reset on DSI controller
 	 * @ctrl:          Pointer to the controller host hardware.
 	 *
@@ -514,11 +528,23 @@
 	 * get_cmd_read_data() - get data read from the peripheral
 	 * @ctrl:           Pointer to the controller host hardware.
 	 * @rd_buf:         Buffer where data will be read into.
-	 * @total_read_len: Number of bytes to read.
+	 * @read_offset:    Offset from where to read.
+	 * @rx_byte:        Number of bytes to be read.
+	 * @pkt_size:        Size of response expected.
+	 * @hw_read_cnt:    Actual number of bytes read by HW.
 	 */
 	u32 (*get_cmd_read_data)(struct dsi_ctrl_hw *ctrl,
 				 u8 *rd_buf,
-				 u32 total_read_len);
+				 u32 read_offset,
+				 u32 rx_byte,
+				 u32 pkt_size,
+				 u32 *hw_read_cnt);
+
+	/**
+	 * get_cont_splash_status() - get continuous splash status
+	 * @ctrl:           Pointer to the controller host hardware.
+	 */
+	bool (*get_cont_splash_status)(struct dsi_ctrl_hw *ctrl);
 
 	/**
 	 * wait_for_lane_idle() - wait for DSI lanes to go to idle state
@@ -695,6 +721,62 @@
 	u32 (*collect_misr)(struct dsi_ctrl_hw *ctrl,
 			    enum dsi_op_mode panel_mode);
 
+	/**
+	 * set_timing_db() - enable/disable Timing DB register
+	 * @ctrl:          Pointer to controller host hardware.
+	 * @enable:        Enable/Disable flag.
+	 *
+	 * Enable or Disabe the Timing DB register.
+	 */
+	void (*set_timing_db)(struct dsi_ctrl_hw *ctrl,
+				 bool enable);
+	/**
+	 * clear_rdbk_register() - Clear and reset read back register
+	 * @ctrl:         Pointer to the controller host hardware.
+	 */
+	void (*clear_rdbk_register)(struct dsi_ctrl_hw *ctrl);
+
+	/** schedule_dma_cmd() - Schdeule DMA command transfer on a
+	 *                       particular blanking line.
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @line_no:      Blanking line number on whihch DMA command
+	 *                needs to be sent.
+	 */
+	void (*schedule_dma_cmd)(struct dsi_ctrl_hw *ctrl, int line_no);
+
+	/**
+	 * ctrl_reset() - Reset DSI lanes to recover from DSI errors
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @mask:         Indicates the error type.
+	 */
+	int (*ctrl_reset)(struct dsi_ctrl_hw *ctrl, int mask);
+
+	/**
+	 * mask_error_int() - Mask/Unmask particular DSI error interrupts
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @idx:	  Indicates the errors to be masked.
+	 * @en:		  Bool for mask or unmask of the error
+	 */
+	void (*mask_error_intr)(struct dsi_ctrl_hw *ctrl, u32 idx, bool en);
+
+	/**
+	 * error_intr_ctrl() - Mask/Unmask master DSI error interrupt
+	 * @ctrl:         Pointer to the controller host hardware.
+	 * @en:		  Bool for mask or unmask of DSI error
+	 */
+	void (*error_intr_ctrl)(struct dsi_ctrl_hw *ctrl, bool en);
+
+	/**
+	 * get_error_mask() - get DSI error interrupt mask status
+	 * @ctrl:         Pointer to the controller host hardware.
+	 */
+	u32 (*get_error_mask)(struct dsi_ctrl_hw *ctrl);
+
+	/**
+	 * get_hw_version() - get DSI controller hw version
+	 * @ctrl:         Pointer to the controller host hardware.
+	 */
+	u32 (*get_hw_version)(struct dsi_ctrl_hw *ctrl);
 };
 
 /*
@@ -713,6 +795,8 @@
  * @supported_errors:       Number of supported errors.
  * @phy_isolation_enabled:    A boolean property allows to isolate the phy from
  *                          dsi controller and run only dsi controller.
+ * @null_insertion_enabled:  A boolean property to allow dsi controller to
+ *                           insert null packet.
  */
 struct dsi_ctrl_hw {
 	void __iomem *base;
@@ -732,6 +816,7 @@
 	u64 supported_errors;
 
 	bool phy_isolation_enabled;
+	bool null_insertion_enabled;
 };
 
 #endif /* _DSI_CTRL_HW_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c
index 1b1e811..650c2e0 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_2_2.c
@@ -17,10 +17,14 @@
 #include "dsi_ctrl_hw.h"
 #include "dsi_ctrl_reg.h"
 #include "dsi_hw.h"
+#include "dsi_catalog.h"
 
 /* Equivalent to register DISP_CC_MISC_CMD */
 #define DISP_CC_CLAMP_REG_OFF 0x00
 
+/* register to configure DMA scheduling */
+#define DSI_DMA_SCHEDULE_CTRL 0x100
+
 /**
  * dsi_ctrl_hw_22_phy_reset_config() - to configure clamp control during ulps
  * @ctrl:          Pointer to the controller host hardware.
@@ -40,3 +44,38 @@
 		reg |= BIT(ctrl->index);
 	DSI_DISP_CC_W32(ctrl, DISP_CC_CLAMP_REG_OFF, reg);
 }
+
+/**
+ * dsi_ctrl_hw_22_schedule_dma_cmd() - to schedule DMA command transfer
+ * @ctrl:         Pointer to the controller host hardware.
+ * @line_no:      Line number at which command needs to be sent.
+ */
+void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_no)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, DSI_DMA_SCHEDULE_CTRL);
+	reg |= BIT(28);
+	reg |= (line_no & 0xffff);
+
+	DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL, reg);
+}
+
+/*
+ * dsi_ctrl_hw_22_get_cont_splash_status() - to verify whether continuous
+ *                                           splash is enabled or not
+ * @ctrl:          Pointer to the controller host hardware.
+ *
+ * Return:         Return Continuous splash status
+ */
+bool dsi_ctrl_hw_22_get_cont_splash_status(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	/**
+	 * DSI scratch register 1 is used to notify whether continuous
+	 * splash is enabled or not by bootloader
+	 */
+	reg = DSI_R32(ctrl, DSI_SCRATCH_REGISTER_1);
+	return reg == 0x1 ? true : false;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
index 3fec296..c2c8f57 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_cmn.c
@@ -16,11 +16,13 @@
 #include <linux/delay.h>
 #include <linux/iopoll.h>
 
+#include "dsi_catalog.h"
 #include "dsi_ctrl_hw.h"
 #include "dsi_ctrl_reg.h"
 #include "dsi_hw.h"
 #include "dsi_panel.h"
 #include "dsi_catalog.h"
+#include "sde_dbg.h"
 
 #define MMSS_MISC_CLAMP_REG_OFF           0x0014
 #define DSI_CTRL_DYNAMIC_FORCE_ON         (0x23F|BIT(8)|BIT(9)|BIT(11)|BIT(21))
@@ -218,6 +220,27 @@
 }
 
 /**
+* set_timing_db() - enable/disable Timing DB register
+* @ctrl:          Pointer to controller host hardware.
+* @enable:        Enable/Disable flag.
+*
+* Enable or Disabe the Timing DB register.
+*/
+void dsi_ctrl_hw_cmn_set_timing_db(struct dsi_ctrl_hw *ctrl,
+				     bool enable)
+{
+	if (enable)
+		DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x1);
+	else
+		DSI_W32(ctrl, DSI_DSI_TIMING_DB_MODE, 0x0);
+
+	wmb(); /* make sure timing db registers are set */
+	pr_debug("[DSI_%d] ctrl timing DB set:%d\n", ctrl->index,
+				enable);
+	SDE_EVT32(ctrl->index, enable);
+}
+
+/**
  * set_video_timing() - set up the timing for video frame
  * @ctrl:          Pointer to controller host hardware.
  * @mode:          Video mode information.
@@ -290,6 +313,7 @@
 	DSI_W32(ctrl, DSI_MISR_VIDEO_CTRL, 0x10100);
 	DSI_W32(ctrl, DSI_DSI_TIMING_FLUSH, 0x1);
 	pr_debug("[DSI_%d] ctrl video parameters updated\n", ctrl->index);
+	SDE_EVT32(v_total, h_total);
 }
 
 /**
@@ -310,7 +334,7 @@
 	u32 width_final, stride_final;
 	u32 height_final;
 	u32 stream_total = 0, stream_ctrl = 0;
-	u32 reg_ctrl = 0, reg_ctrl2 = 0;
+	u32 reg_ctrl = 0, reg_ctrl2 = 0, data = 0;
 
 	if (roi && (!roi->w || !roi->h))
 		return;
@@ -354,8 +378,6 @@
 		reg_ctrl |= (reg << offset);
 		reg_ctrl2 &= ~(0xFFFF << offset);
 		reg_ctrl2 |= (dsc.bytes_in_slice << offset);
-		DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
-		DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
 
 		pr_debug("ctrl %d reg_ctrl 0x%x reg_ctrl2 0x%x\n", ctrl->index,
 				reg_ctrl, reg_ctrl2);
@@ -369,10 +391,16 @@
 		height_final = mode->v_active;
 	}
 
+	/* HS Timer value */
+	DSI_W32(ctrl, DSI_HS_TIMER_CTRL, 0x3FD08);
+
 	stream_ctrl = (stride_final + 1) << 16;
 	stream_ctrl |= (vc_id & 0x3) << 8;
 	stream_ctrl |= 0x39; /* packet data type */
 
+	DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl);
+	DSI_W32(ctrl, DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2);
+
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_CTRL, stream_ctrl);
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_CTRL, stream_ctrl);
 
@@ -380,6 +408,14 @@
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM0_TOTAL, stream_total);
 	DSI_W32(ctrl, DSI_COMMAND_MODE_MDP_STREAM1_TOTAL, stream_total);
 
+	if (ctrl->null_insertion_enabled) {
+		/* enable null packet insertion */
+		data = (vc_id << 1);
+		data |= 0 << 16;
+		data |= 0x1;
+		DSI_W32(ctrl, DSI_COMMAND_MODE_NULL_INSERTION_CTRL, data);
+	}
+
 	pr_debug("ctrl %d stream_ctrl 0x%x stream_total 0x%x\n", ctrl->index,
 			stream_ctrl, stream_total);
 }
@@ -423,6 +459,18 @@
 	pr_debug("[DSI_%d] Video engine setup done\n", ctrl->index);
 }
 
+void dsi_ctrl_hw_cmn_debug_bus(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	DSI_W32(ctrl, DSI_DEBUG_BUS_CTL, 0x181);
+
+	/* make sure that debug test point is enabled */
+	wmb();
+	reg = DSI_R32(ctrl, DSI_DEBUG_BUS_STATUS);
+
+	pr_err("[DSI_%d] debug bus status:0x%x\n", ctrl->index, reg);
+}
 /**
  * cmd_engine_setup() - setup dsi host controller for command mode
  * @ctrl:          Pointer to the controller host hardware.
@@ -673,6 +721,22 @@
 }
 
 /**
+ * clear_rdbk_reg() - clear previously read panel data.
+ * @ctrl:          Pointer to the controller host hardware.
+ *
+ * This function is called before sending DSI Rx command to
+ * panel in order to clear if any stale data remaining from
+ * previous read operation.
+ */
+void dsi_ctrl_hw_cmn_clear_rdbk_reg(struct dsi_ctrl_hw *ctrl)
+{
+	DSI_W32(ctrl, DSI_RDBK_DATA_CTRL, 0x1);
+	wmb(); /* ensure read back register is reset */
+	DSI_W32(ctrl, DSI_RDBK_DATA_CTRL, 0x0);
+	wmb(); /* ensure read back register is cleared */
+}
+
+/**
  * get_cmd_read_data() - get data read from the peripheral
  * @ctrl:           Pointer to the controller host hardware.
  * @rd_buf:         Buffer where data will be read into.
@@ -683,16 +747,16 @@
 u32 dsi_ctrl_hw_cmn_get_cmd_read_data(struct dsi_ctrl_hw *ctrl,
 				     u8 *rd_buf,
 				     u32 read_offset,
-				     u32 total_read_len)
+				     u32 rx_byte,
+				     u32 pkt_size,
+				     u32 *hw_read_cnt)
 {
 	u32 *lp, *temp, data;
-	int i, j = 0, cnt;
+	int i, j = 0, cnt, off;
 	u32 read_cnt;
-	u32 rx_byte = 0;
 	u32 repeated_bytes = 0;
 	u8 reg[16] = {0};
-	u32 pkt_size = 0;
-	int buf_offset = read_offset;
+	bool ack_err = false;
 
 	lp = (u32 *)rd_buf;
 	temp = (u32 *)reg;
@@ -701,28 +765,49 @@
 	if (cnt > 4)
 		cnt = 4;
 
-	if (rx_byte == 4)
-		read_cnt = 4;
-	else
-		read_cnt = pkt_size + 6;
+	read_cnt = (DSI_R32(ctrl, DSI_RDBK_DATA_CTRL) >> 16);
+	ack_err = (rx_byte == 4) ? (read_cnt == 8) :
+			((read_cnt - 4) == (pkt_size + 6));
+
+	if (ack_err)
+		read_cnt -= 4;
+	if (!read_cnt) {
+		pr_err("Panel detected error, no data read\n");
+		return 0;
+	}
 
 	if (read_cnt > 16) {
-		int bytes_shifted;
+		int bytes_shifted, data_lost = 0, rem_header = 0;
 
-		bytes_shifted = read_cnt - 16;
-		repeated_bytes = buf_offset - bytes_shifted;
+		bytes_shifted = read_cnt - rx_byte;
+		if (bytes_shifted >= 4)
+			data_lost = bytes_shifted - 4; /* remove DCS header */
+		else
+			rem_header = 4 - bytes_shifted; /* remaining header */
+
+		repeated_bytes = (read_offset - 4) - data_lost + rem_header;
 	}
 
-	for (i = cnt - 1; i >= 0; i--) {
-		data = DSI_R32(ctrl, DSI_RDBK_DATA0 + i*4);
-		*temp++ = ntohl(data);
+	off = DSI_RDBK_DATA0;
+	off += ((cnt - 1) * 4);
+
+	for (i = 0; i < cnt; i++) {
+		data = DSI_R32(ctrl, off);
+		if (!repeated_bytes)
+			*lp++ = ntohl(data);
+		else
+			*temp++ = ntohl(data);
+		off -= 4;
 	}
 
-	for (i = repeated_bytes; i < 16; i++)
-		rd_buf[j++] = reg[i];
+	if (repeated_bytes) {
+		for (i = repeated_bytes; i < 16; i++)
+			rd_buf[j++] = reg[i];
+	}
 
-	pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, j);
-	return j;
+	*hw_read_cnt = read_cnt;
+	pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, rx_byte);
+	return rx_byte;
 }
 
 /**
@@ -760,6 +845,8 @@
 		ints |= DSI_DYN_REFRESH_DONE;
 	if (reg & BIT(30))
 		ints |= DSI_DESKEW_DONE;
+	if (reg & BIT(24))
+		ints |= DSI_ERROR;
 
 	pr_debug("[DSI_%d] Interrupt status = 0x%x, INT_CTRL=0x%x\n",
 		 ctrl->index, ints, reg);
@@ -796,6 +883,12 @@
 	if (ints & DSI_DESKEW_DONE)
 		reg |= BIT(30);
 
+	/*
+	 * Do not clear error status.
+	 * It will be cleared as part of
+	 * error handler function.
+	 */
+	reg &= ~BIT(24);
 	DSI_W32(ctrl, DSI_INT_CTRL, reg);
 
 	pr_debug("[DSI_%d] Clear interrupts, ints = 0x%x, INT_CTRL=0x%x\n",
@@ -862,7 +955,7 @@
 	u32 timeout_errors;
 	u32 clk_error;
 	u32 dsi_status;
-	u64 errors = 0;
+	u64 errors = 0, shift = 0x1;
 
 	dln0_phy_err = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
 	if (dln0_phy_err & BIT(0))
@@ -909,6 +1002,8 @@
 		errors |= DSI_RDBK_INCOMPLETE_PKT;
 	if (ack_error & BIT(24))
 		errors |= DSI_PERIPH_ERROR_PKT;
+	if (ack_error & BIT(15))
+		errors |= (shift << DSI_EINT_PANEL_SPECIFIC_ERR);
 
 	timeout_errors = DSI_R32(ctrl, DSI_TIMEOUT_STATUS);
 	if (timeout_errors & BIT(0))
@@ -946,7 +1041,6 @@
 	u32 timeout_error = 0;
 	u32 clk_error = 0;
 	u32 dsi_status = 0;
-	u32 int_ctrl = 0;
 
 	if (errors & DSI_RDBK_SINGLE_ECC_ERR)
 		ack_error |= BIT(16);
@@ -958,6 +1052,8 @@
 		ack_error |= BIT(23);
 	if (errors & DSI_PERIPH_ERROR_PKT)
 		ack_error |= BIT(24);
+	if (errors & DSI_PANEL_SPECIFIC_ERR)
+		ack_error |= BIT(15);
 
 	if (errors & DSI_LP_RX_TIMEOUT)
 		timeout_error |= BIT(4);
@@ -1006,14 +1102,14 @@
 
 	DSI_W32(ctrl, DSI_DLN0_PHY_ERR, dln0_phy_err);
 	DSI_W32(ctrl, DSI_FIFO_STATUS, fifo_status);
+	/* Writing of an extra 0 is needed to clear ack error bits */
 	DSI_W32(ctrl, DSI_ACK_ERR_STATUS, ack_error);
+	wmb(); /* make sure register is committed */
+	DSI_W32(ctrl, DSI_ACK_ERR_STATUS, 0x0);
 	DSI_W32(ctrl, DSI_TIMEOUT_STATUS, timeout_error);
 	DSI_W32(ctrl, DSI_CLK_STATUS, clk_error);
 	DSI_W32(ctrl, DSI_STATUS, dsi_status);
 
-	int_ctrl = DSI_R32(ctrl, DSI_INT_CTRL);
-	int_ctrl |= BIT(24);
-	DSI_W32(ctrl, DSI_INT_CTRL, int_ctrl);
 	pr_debug("[DSI_%d] clear errors = 0x%llx, phy=0x%x, fifo=0x%x",
 		 ctrl->index, errors, dln0_phy_err, fifo_status);
 	pr_debug("[DSI_%d] ack=0x%x, timeout=0x%x, clk=0x%x, dsi=0x%x\n",
@@ -1274,3 +1370,102 @@
 	DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
 }
 
+int dsi_ctrl_hw_cmn_ctrl_reset(struct dsi_ctrl_hw *ctrl,
+		int mask)
+{
+	int rc = 0;
+	u32 data;
+
+	pr_debug("DSI CTRL and PHY reset. ctrl-num = %d %d\n",
+			ctrl->index, mask);
+
+	data = DSI_R32(ctrl, 0x0004);
+	/* Disable DSI video mode */
+	DSI_W32(ctrl, 0x004, (data & ~BIT(1)));
+	wmb(); /* ensure register committed */
+	/* Disable DSI controller */
+	DSI_W32(ctrl, 0x004, (data & ~(BIT(0) | BIT(1))));
+	wmb(); /* ensure register committed */
+	/* "Force On" all dynamic clocks */
+	DSI_W32(ctrl, 0x11c, 0x100a00);
+
+	/* DSI_SW_RESET */
+	DSI_W32(ctrl, 0x118, 0x1);
+	wmb(); /* ensure register is committed */
+	DSI_W32(ctrl, 0x118, 0x0);
+	wmb(); /* ensure register is committed */
+
+	/* Remove "Force On" all dynamic clocks */
+	DSI_W32(ctrl, 0x11c, 0x00);
+	/* Enable DSI controller */
+	DSI_W32(ctrl, 0x004, (data & ~BIT(1)));
+	wmb(); /* ensure register committed */
+
+	return rc;
+}
+
+void dsi_ctrl_hw_cmn_mask_error_intr(struct dsi_ctrl_hw *ctrl, u32 idx, bool en)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x10c);
+
+	if (idx & BIT(DSI_FIFO_OVERFLOW)) {
+		if (en)
+			reg |= (0xf << 16);
+		else
+			reg &= ~(0xf << 16);
+	}
+
+	if (idx & BIT(DSI_FIFO_UNDERFLOW)) {
+		if (en)
+			reg |= (0xf << 26);
+		else
+			reg &= ~(0xf << 26);
+	}
+
+	if (idx & BIT(DSI_LP_Rx_TIMEOUT)) {
+		if (en)
+			reg |= (0x7 << 23);
+		else
+			reg &= ~(0x7 << 23);
+	}
+
+	DSI_W32(ctrl, 0x10c, reg);
+	wmb(); /* ensure error is masked */
+}
+
+void dsi_ctrl_hw_cmn_error_intr_ctrl(struct dsi_ctrl_hw *ctrl, bool en)
+{
+	u32 reg = 0;
+	u32 dsi_total_mask = 0x2222AA02;
+
+	reg = DSI_R32(ctrl, 0x110);
+	reg &= dsi_total_mask;
+
+	if (en)
+		reg |= (BIT(24) | BIT(25));
+	else
+		reg &= ~BIT(25);
+
+	DSI_W32(ctrl, 0x110, reg);
+	wmb(); /* ensure error is masked */
+}
+
+u32 dsi_ctrl_hw_cmn_get_error_mask(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x10c);
+
+	return reg;
+}
+
+u32 dsi_ctrl_hw_cmn_get_hw_version(struct dsi_ctrl_hw *ctrl)
+{
+	u32 reg = 0;
+
+	reg = DSI_R32(ctrl, 0x0);
+
+	return reg;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
index f8f7e13..39ac021 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_reg.h
@@ -82,6 +82,8 @@
 #define DSI_SOFT_RESET                             (0x0118)
 #define DSI_CLK_CTRL                               (0x011C)
 #define DSI_CLK_STATUS                             (0x0120)
+#define DSI_DEBUG_BUS_CTL                          (0x0124)
+#define DSI_DEBUG_BUS_STATUS                       (0x0128)
 #define DSI_PHY_SW_RESET                           (0x012C)
 #define DSI_AXI2AHB_CTRL                           (0x0130)
 #define DSI_MISR_CMD_MDP0_32BIT                    (0x0134)
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index fcc59ef..d45f849 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -77,12 +77,15 @@
  * @DSI_MODE_FLAG_DFPS:		Seamless transition is DynamicFPS
  * @DSI_MODE_FLAG_VBLANK_PRE_MODESET:	Transition needs VBLANK before Modeset
  * @DSI_MODE_FLAG_DMS: Seamless transition is dynamic mode switch
+ * @DSI_MODE_FLAG_VRR: Seamless transition is DynamicFPS.
+ *                     New timing values are sent from DAL.
  */
 enum dsi_mode_flags {
 	DSI_MODE_FLAG_SEAMLESS			= BIT(0),
 	DSI_MODE_FLAG_DFPS			= BIT(1),
 	DSI_MODE_FLAG_VBLANK_PRE_MODESET	= BIT(2),
 	DSI_MODE_FLAG_DMS			= BIT(3),
+	DSI_MODE_FLAG_VRR			= BIT(4),
 };
 
 /**
@@ -352,8 +355,10 @@
  * @v_front_porch:    Vertical front porch in lines.
  * @v_sync_polarity:  Polarity of VSYNC (false is active low).
  * @refresh_rate:     Refresh rate in Hz.
+ * @clk_rate_hz:      DSI bit clock rate per lane in Hz.
  * @dsc_enabled:      DSC compression enabled.
  * @dsc:              DSC compression configuration.
+ * @roi_caps:         Panel ROI capabilities.
  */
 struct dsi_mode_info {
 	u32 h_active;
@@ -370,9 +375,10 @@
 	bool v_sync_polarity;
 
 	u32 refresh_rate;
-
+	u64 clk_rate_hz;
 	bool dsc_enabled;
 	struct msm_display_dsc_info *dsc;
+	struct msm_roi_caps roi_caps;
 };
 
 /**
@@ -497,9 +503,11 @@
  * @phy_timing_len:       Phy timing array length
  * @panel_jitter:         Panel jitter for RSC backoff
  * @panel_prefill_lines:  Panel prefill lines for RSC
+ * @clk_rate_hz:          DSI bit clock per lane in hz.
  * @topology:             Topology selected for the panel
  * @dsc:                  DSC compression info
  * @dsc_enabled:          DSC compression enabled
+ * @roi_caps:		  Panel ROI capabilities
  */
 struct dsi_display_mode_priv_info {
 	struct dsi_panel_cmd_set cmd_sets[DSI_CMD_SET_MAX];
@@ -510,10 +518,12 @@
 	u32 panel_jitter_numer;
 	u32 panel_jitter_denom;
 	u32 panel_prefill_lines;
+	u64 clk_rate_hz;
 
 	struct msm_display_topology topology;
 	struct msm_display_dsc_info dsc;
 	bool dsc_enabled;
+	struct msm_roi_caps roi_caps;
 };
 
 /**
@@ -575,4 +585,16 @@
 		uint32_t data2, uint32_t data3);
 };
 
+/**
+ * enum dsi_error_status - various dsi errors
+ * @DSI_FIFO_OVERFLOW:     DSI FIFO Overflow error
+ * @DSI_FIFO_UNDERFLOW:    DSI FIFO Underflow error
+ * @DSI_LP_Rx_TIMEOUT:     DSI LP/RX Timeout error
+ */
+enum dsi_error_status {
+	DSI_FIFO_OVERFLOW = 1,
+	DSI_FIFO_UNDERFLOW,
+	DSI_LP_Rx_TIMEOUT,
+};
+
 #endif /* _DSI_DEFS_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 4e76aa5..982d16e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -28,6 +28,7 @@
 #include "dsi_drm.h"
 #include "dsi_clk.h"
 #include "dsi_pwr.h"
+#include "sde_dbg.h"
 
 #define to_dsi_display(x) container_of(x, struct dsi_display, host)
 #define INT_BASE_10 10
@@ -35,6 +36,8 @@
 
 #define MISR_BUFF_SIZE	256
 
+#define MAX_NAME_SIZE	64
+
 static DEFINE_MUTEX(dsi_display_list_lock);
 static LIST_HEAD(dsi_display_list);
 static char dsi_display_primary[MAX_CMDLINE_PARAM_LEN];
@@ -84,6 +87,10 @@
 		return -EINVAL;
 
 	panel = dsi_display->panel;
+
+	if (!dsi_panel_initialized(panel))
+		return -EINVAL;
+
 	panel->bl_config.bl_level = bl_lvl;
 
 	/* scale backlight */
@@ -95,10 +102,328 @@
 
 	pr_debug("bl_scale = %u, bl_scale_ad = %u, bl_lvl = %u\n",
 		bl_scale, bl_scale_ad, (u32)bl_temp);
+
+	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_ON);
+	if (rc) {
+		pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
+		       dsi_display->name, rc);
+		goto error;
+	}
+
 	rc = dsi_panel_set_backlight(panel, (u32)bl_temp);
 	if (rc)
 		pr_err("unable to set backlight\n");
 
+	rc = dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+			DSI_CORE_CLK, DSI_CLK_OFF);
+	if (rc) {
+		pr_err("[%s] failed to disable DSI core clocks, rc=%d\n",
+		       dsi_display->name, rc);
+		goto error;
+	}
+
+error:
+	return rc;
+}
+
+static int dsi_display_cmd_engine_enable(struct dsi_display *display)
+{
+	int rc = 0;
+	int i;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	mutex_lock(&m_ctrl->ctrl->ctrl_lock);
+
+	if (display->cmd_engine_refcount > 0) {
+		display->cmd_engine_refcount++;
+		goto done;
+	}
+
+	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
+	if (rc) {
+		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+		       display->name, rc);
+		goto done;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
+						   DSI_CTRL_ENGINE_ON);
+		if (rc) {
+			pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+			       display->name, rc);
+			goto error_disable_master;
+		}
+	}
+
+	display->cmd_engine_refcount++;
+	goto done;
+error_disable_master:
+	(void)dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+done:
+	mutex_unlock(&m_ctrl->ctrl->ctrl_lock);
+	return rc;
+}
+
+static int dsi_display_cmd_engine_disable(struct dsi_display *display)
+{
+	int rc = 0;
+	int i;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+	mutex_lock(&m_ctrl->ctrl->ctrl_lock);
+
+	if (display->cmd_engine_refcount == 0) {
+		pr_err("[%s] Invalid refcount\n", display->name);
+		goto done;
+	} else if (display->cmd_engine_refcount > 1) {
+		display->cmd_engine_refcount--;
+		goto done;
+	}
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl || (ctrl == m_ctrl))
+			continue;
+
+		rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
+						   DSI_CTRL_ENGINE_OFF);
+		if (rc)
+			pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+			       display->name, rc);
+	}
+
+	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
+	if (rc) {
+		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+		       display->name, rc);
+		goto error;
+	}
+
+error:
+	display->cmd_engine_refcount = 0;
+done:
+	mutex_unlock(&m_ctrl->ctrl->ctrl_lock);
+	return rc;
+}
+
+static bool dsi_display_validate_reg_read(struct dsi_panel *panel)
+{
+	int i, j = 0;
+	int len = 0, *lenp;
+	int group = 0, count = 0;
+	struct dsi_display_mode *mode;
+	struct drm_panel_esd_config *config;
+
+	if (!panel)
+		return false;
+
+	config = &(panel->esd_config);
+
+	lenp = config->status_valid_params ?: config->status_cmds_rlen;
+	mode = panel->cur_mode;
+	count = mode->priv_info->cmd_sets[DSI_CMD_SET_PANEL_STATUS].count;
+
+	for (i = 0; i < count; i++)
+		len += lenp[i];
+
+	for (i = 0; i < len; i++)
+		j += len;
+
+	for (j = 0; j < config->groups; ++j) {
+		for (i = 0; i < len; ++i) {
+			if (config->return_buf[i] !=
+				config->status_value[group + i])
+				break;
+		}
+
+		if (i == len)
+			return true;
+		group += len;
+	}
+
+	return false;
+}
+
+static int dsi_display_read_status(struct dsi_display_ctrl *ctrl,
+		struct dsi_panel *panel)
+{
+	int i, rc = 0, count = 0, start = 0, *lenp;
+	struct drm_panel_esd_config *config;
+	struct dsi_cmd_desc *cmds;
+	u32 flags = 0;
+
+	if (!panel)
+		return -EINVAL;
+
+	/* acquire panel_lock to make sure no commands are in progress */
+	dsi_panel_acquire_panel_lock(panel);
+
+	config = &(panel->esd_config);
+	lenp = config->status_valid_params ?: config->status_cmds_rlen;
+	count = config->status_cmd.count;
+	cmds = config->status_cmd.cmds;
+	if (cmds->last_command) {
+		cmds->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND;
+		flags |= DSI_CTRL_CMD_LAST_COMMAND;
+	}
+	flags |= (DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_READ);
+
+	for (i = 0; i < count; ++i) {
+		memset(config->status_buf, 0x0, SZ_4K);
+		cmds[i].msg.rx_buf = config->status_buf;
+		cmds[i].msg.rx_len = config->status_cmds_rlen[i];
+		rc = dsi_ctrl_cmd_transfer(ctrl->ctrl, &cmds[i].msg, flags);
+		if (rc <= 0) {
+			pr_err("rx cmd transfer failed rc=%d\n", rc);
+			goto error;
+		}
+
+		memcpy(config->return_buf + start,
+			config->status_buf, lenp[i]);
+		start += lenp[i];
+	}
+
+error:
+	/* release panel_lock */
+	dsi_panel_release_panel_lock(panel);
+	return rc;
+}
+
+static int dsi_display_validate_status(struct dsi_display_ctrl *ctrl,
+		struct dsi_panel *panel)
+{
+	int rc = 0;
+
+	rc = dsi_display_read_status(ctrl, panel);
+	if (rc <= 0) {
+		goto exit;
+	} else {
+		/*
+		 * panel status read successfully.
+		 * check for validity of the data read back.
+		 */
+		rc = dsi_display_validate_reg_read(panel);
+		if (!rc) {
+			rc = -EINVAL;
+			goto exit;
+		}
+	}
+
+exit:
+	return rc;
+}
+
+static int dsi_display_status_reg_read(struct dsi_display *display)
+{
+	int rc = 0, i;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+	pr_debug(" ++\n");
+
+	m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+	rc = dsi_display_cmd_engine_enable(display);
+	if (rc) {
+		pr_err("cmd engine enable failed\n");
+		return -EPERM;
+	}
+
+	rc = dsi_display_validate_status(m_ctrl, display->panel);
+	if (rc <= 0) {
+		pr_err("[%s] read status failed on master,rc=%d\n",
+		       display->name, rc);
+		goto exit;
+	}
+
+	if (!display->panel->sync_broadcast_en)
+		goto exit;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (ctrl == m_ctrl)
+			continue;
+
+		rc = dsi_display_validate_status(ctrl, display->panel);
+		if (rc <= 0) {
+			pr_err("[%s] read status failed on master,rc=%d\n",
+			       display->name, rc);
+			goto exit;
+		}
+	}
+
+exit:
+	dsi_display_cmd_engine_disable(display);
+	return rc;
+}
+
+static int dsi_display_status_bta_request(struct dsi_display *display)
+{
+	int rc = 0;
+
+	pr_debug(" ++\n");
+	/* TODO: trigger SW BTA and wait for acknowledgment */
+
+	return rc;
+}
+
+static int dsi_display_status_check_te(struct dsi_display *display)
+{
+	int rc = 0;
+
+	pr_debug(" ++\n");
+	/* TODO: wait for TE interrupt from panel */
+
+	return rc;
+}
+
+int dsi_display_check_status(void *display)
+{
+	struct dsi_display *dsi_display = display;
+	struct dsi_panel *panel;
+	u32 status_mode;
+	int rc = 0x1;
+
+	if (dsi_display == NULL)
+		return -EINVAL;
+
+	panel = dsi_display->panel;
+
+	status_mode = panel->esd_config.status_mode;
+
+	mutex_lock(&dsi_display->display_lock);
+
+	if (!panel->panel_initialized) {
+		pr_debug("Panel not initialized\n");
+		mutex_unlock(&dsi_display->display_lock);
+		return rc;
+	}
+
+	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+		DSI_ALL_CLKS, DSI_CLK_ON);
+
+	if (status_mode == ESD_MODE_REG_READ) {
+		rc = dsi_display_status_reg_read(dsi_display);
+	} else if (status_mode == ESD_MODE_SW_BTA) {
+		rc = dsi_display_status_bta_request(dsi_display);
+	} else if (status_mode == ESD_MODE_PANEL_TE) {
+		rc = dsi_display_status_check_te(dsi_display);
+	} else {
+		pr_warn("unsupported check status mode\n");
+		panel->esd_config.esd_enabled = false;
+	}
+
+	dsi_display_clk_ctrl(dsi_display->dsi_clk_handle,
+		DSI_ALL_CLKS, DSI_CLK_OFF);
+	mutex_unlock(&dsi_display->display_lock);
+
 	return rc;
 }
 
@@ -154,6 +479,32 @@
 	}
 }
 
+/**
+ * dsi_display_get_cont_splash_status - Get continuous splash status.
+ * @dsi_display:         DSI display handle.
+ *
+ * Return: boolean to signify whether continuous splash is enabled.
+ */
+static bool dsi_display_get_cont_splash_status(struct dsi_display *display)
+{
+	u32 val = 0;
+	int i;
+	struct dsi_display_ctrl *ctrl;
+	struct dsi_ctrl_hw *hw;
+
+	for (i = 0; i < display->ctrl_count ; i++) {
+		ctrl = &(display->ctrl[i]);
+		if (!ctrl || !ctrl->ctrl)
+			continue;
+
+		hw = &(ctrl->ctrl->hw);
+		val = hw->ops.get_cont_splash_status(hw);
+		if (!val)
+			return false;
+	}
+	return true;
+}
+
 int dsi_display_set_power(struct drm_connector *connector,
 		int power_mode, void *disp)
 {
@@ -375,6 +726,8 @@
 {
 	int rc = 0;
 	struct dentry *dir, *dump_file, *misr_data;
+	char name[MAX_NAME_SIZE];
+	int i;
 
 	dir = debugfs_create_dir(display->name, NULL);
 	if (IS_ERR_OR_NULL(dir)) {
@@ -408,6 +761,35 @@
 		goto error_remove_dir;
 	}
 
+	for (i = 0; i < display->ctrl_count; i++) {
+		struct msm_dsi_phy *phy = display->ctrl[i].phy;
+
+		if (!phy || !phy->name)
+			continue;
+
+		snprintf(name, ARRAY_SIZE(name),
+				"%s_allow_phy_power_off", phy->name);
+		dump_file = debugfs_create_bool(name, 0600, dir,
+				&phy->allow_phy_power_off);
+		if (IS_ERR_OR_NULL(dump_file)) {
+			rc = PTR_ERR(dump_file);
+			pr_err("[%s] debugfs create %s failed, rc=%d\n",
+			       display->name, name, rc);
+			goto error_remove_dir;
+		}
+
+		snprintf(name, ARRAY_SIZE(name),
+				"%s_regulator_min_datarate_bps", phy->name);
+		dump_file = debugfs_create_u32(name, 0600, dir,
+				&phy->regulator_min_datarate_bps);
+		if (IS_ERR_OR_NULL(dump_file)) {
+			rc = PTR_ERR(dump_file);
+			pr_err("[%s] debugfs create %s failed, rc=%d\n",
+			       display->name, name, rc);
+			goto error_remove_dir;
+		}
+	}
+
 	display->root = dir;
 	return rc;
 error_remove_dir:
@@ -625,7 +1007,7 @@
 /**
  * dsi_display_phy_idle_on() - enable DSI PHY while coming out of idle screen.
  * @dsi_display:         DSI display handle.
- * @enable:           enable/disable DSI PHY.
+ * @mmss_clamp:          True if clamp is enabled.
  *
  * Return: error code.
  */
@@ -672,7 +1054,6 @@
 /**
  * dsi_display_phy_idle_off() - disable DSI PHY while going to idle screen.
  * @dsi_display:         DSI display handle.
- * @enable:           enable/disable DSI PHY.
  *
  * Return: error code.
  */
@@ -687,9 +1068,16 @@
 		return -EINVAL;
 	}
 
-	if (!display->panel->allow_phy_power_off) {
-		pr_debug("panel doesn't support this feature\n");
-		return 0;
+	for (i = 0; i < display->ctrl_count; i++) {
+		struct msm_dsi_phy *phy = display->ctrl[i].phy;
+
+		if (!phy)
+			continue;
+
+		if (!phy->allow_phy_power_off) {
+			pr_debug("phy doesn't support this feature\n");
+			return 0;
+		}
 	}
 
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
@@ -738,6 +1126,13 @@
 	case SDE_CONN_EVENT_CMD_DONE:
 		irq_status_idx = DSI_SINT_CMD_FRAME_DONE;
 		break;
+	case SDE_CONN_EVENT_VID_FIFO_OVERFLOW:
+	case SDE_CONN_EVENT_CMD_FIFO_UNDERFLOW:
+		if (event_info) {
+			for (i = 0; i < display->ctrl_count; i++)
+				display->ctrl[i].ctrl->recovery_cb =
+							*event_info;
+		}
 	default:
 		/* nothing to do */
 		pr_debug("[%s] unhandled event %d\n", display->name, event_idx);
@@ -756,6 +1151,30 @@
 	}
 }
 
+/**
+ * dsi_config_host_engine_state_for_cont_splash()- update host engine state
+ *                                                 during continuous splash.
+ * @display: Handle to dsi display
+ *
+ */
+static void dsi_config_host_engine_state_for_cont_splash
+					(struct dsi_display *display)
+{
+	int i;
+	struct dsi_display_ctrl *ctrl;
+	enum dsi_engine_state host_state = DSI_CTRL_ENGINE_ON;
+
+	/* Sequence does not matter for split dsi usecases */
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		if (!ctrl->ctrl)
+			continue;
+
+		dsi_ctrl_update_host_engine_state_for_cont_splash(ctrl->ctrl,
+							host_state);
+	}
+}
+
 static int dsi_display_ctrl_power_on(struct dsi_display *display)
 {
 	int rc = 0;
@@ -1153,7 +1572,8 @@
 
 	for (i = 0 ; i < display->ctrl_count; i++) {
 		ctrl = &display->ctrl[i];
-		rc = dsi_ctrl_host_init(ctrl->ctrl);
+		rc = dsi_ctrl_host_init(ctrl->ctrl,
+				display->is_cont_splash_enabled);
 		if (rc) {
 			pr_err("[%s] failed to init host_%d, rc=%d\n",
 			       display->name, i, rc);
@@ -1188,93 +1608,20 @@
 	return rc;
 }
 
-static int dsi_display_cmd_engine_enable(struct dsi_display *display)
-{
-	int rc = 0;
-	int i;
-	struct dsi_display_ctrl *m_ctrl, *ctrl;
-
-	if (display->cmd_engine_refcount > 0) {
-		display->cmd_engine_refcount++;
-		return 0;
-	}
-
-	m_ctrl = &display->ctrl[display->cmd_master_idx];
-
-	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
-	if (rc) {
-		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
-		       display->name, rc);
-		goto error;
-	}
-
-	for (i = 0; i < display->ctrl_count; i++) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || (ctrl == m_ctrl))
-			continue;
-
-		rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
-						   DSI_CTRL_ENGINE_ON);
-		if (rc) {
-			pr_err("[%s] failed to enable cmd engine, rc=%d\n",
-			       display->name, rc);
-			goto error_disable_master;
-		}
-	}
-
-	display->cmd_engine_refcount++;
-	return rc;
-error_disable_master:
-	(void)dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
-error:
-	return rc;
-}
-
-static int dsi_display_cmd_engine_disable(struct dsi_display *display)
-{
-	int rc = 0;
-	int i;
-	struct dsi_display_ctrl *m_ctrl, *ctrl;
-
-	if (display->cmd_engine_refcount == 0) {
-		pr_err("[%s] Invalid refcount\n", display->name);
-		return 0;
-	} else if (display->cmd_engine_refcount > 1) {
-		display->cmd_engine_refcount--;
-		return 0;
-	}
-
-	m_ctrl = &display->ctrl[display->cmd_master_idx];
-	for (i = 0; i < display->ctrl_count; i++) {
-		ctrl = &display->ctrl[i];
-		if (!ctrl->ctrl || (ctrl == m_ctrl))
-			continue;
-
-		rc = dsi_ctrl_set_cmd_engine_state(ctrl->ctrl,
-						   DSI_CTRL_ENGINE_OFF);
-		if (rc)
-			pr_err("[%s] failed to enable cmd engine, rc=%d\n",
-			       display->name, rc);
-	}
-
-	rc = dsi_ctrl_set_cmd_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_OFF);
-	if (rc) {
-		pr_err("[%s] failed to enable cmd engine, rc=%d\n",
-		       display->name, rc);
-		goto error;
-	}
-
-error:
-	display->cmd_engine_refcount = 0;
-	return rc;
-}
-
 static int dsi_display_ctrl_host_enable(struct dsi_display *display)
 {
 	int rc = 0;
 	int i;
 	struct dsi_display_ctrl *m_ctrl, *ctrl;
 
+	/* Host engine states are already taken care for
+	 * continuous splash case
+	 */
+	if (display->is_cont_splash_enabled) {
+		pr_debug("cont splash enabled, host enable not required\n");
+		return 0;
+	}
+
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
 
 	rc = dsi_ctrl_set_host_engine_state(m_ctrl->ctrl, DSI_CTRL_ENGINE_ON);
@@ -1413,7 +1760,8 @@
 	rc = dsi_phy_enable(m_ctrl->phy,
 			    &display->config,
 			    m_src,
-			    true);
+			    true,
+			    display->is_cont_splash_enabled);
 	if (rc) {
 		pr_err("[%s] failed to enable DSI PHY, rc=%d\n",
 		       display->name, rc);
@@ -1428,7 +1776,8 @@
 		rc = dsi_phy_enable(ctrl->phy,
 				    &display->config,
 				    DSI_PLL_SOURCE_NON_NATIVE,
-				    true);
+				    true,
+				    display->is_cont_splash_enabled);
 		if (rc) {
 			pr_err("[%s] failed to enable DSI PHY, rc=%d\n",
 			       display->name, rc);
@@ -1489,6 +1838,10 @@
 	flags = (DSI_CTRL_CMD_BROADCAST | DSI_CTRL_CMD_DEFER_TRIGGER |
 		 DSI_CTRL_CMD_FETCH_MEMORY);
 
+	if ((msg->flags & MIPI_DSI_MSG_LASTCOMMAND)) {
+		flags |= DSI_CTRL_CMD_LAST_COMMAND;
+		m_flags |= DSI_CTRL_CMD_LAST_COMMAND;
+	}
 	/*
 	 * 1. Setup commands in FIFO
 	 * 2. Trigger commands
@@ -1538,6 +1891,14 @@
 	int i;
 	struct dsi_display_ctrl *m_ctrl, *ctrl;
 
+	/* For continuous splash use case ctrl states are updated
+	 * separately and hence we do an early return
+	 */
+	if (display->is_cont_splash_enabled) {
+		pr_debug("cont splash enabled, phy sw reset not required\n");
+		return 0;
+	}
+
 	m_ctrl = &display->ctrl[display->cmd_master_idx];
 
 	rc = dsi_ctrl_phy_sw_reset(m_ctrl->ctrl);
@@ -1563,6 +1924,62 @@
 	return rc;
 }
 
+static void dsi_display_aspace_cb_locked(void *cb_data, bool is_detach)
+{
+	struct dsi_display *display;
+	struct dsi_display_ctrl *display_ctrl;
+	int rc, cnt;
+
+	if (!cb_data) {
+		pr_err("aspace cb called with invalid cb_data\n");
+		return;
+	}
+	display = (struct dsi_display *)cb_data;
+
+	/*
+	 * acquire panel_lock to make sure no commands are in-progress
+	 * while detaching the non-secure context banks
+	 */
+	dsi_panel_acquire_panel_lock(display->panel);
+
+	if (is_detach) {
+		/* invalidate the stored iova */
+		display->cmd_buffer_iova = 0;
+
+		/* return the virtual address mapping */
+		msm_gem_put_vaddr_locked(display->tx_cmd_buf);
+		msm_gem_vunmap(display->tx_cmd_buf);
+
+	} else {
+		rc = msm_gem_get_iova_locked(display->tx_cmd_buf,
+				display->aspace, &(display->cmd_buffer_iova));
+		if (rc) {
+			pr_err("failed to get the iova rc %d\n", rc);
+			goto end;
+		}
+
+		display->vaddr =
+			(void *) msm_gem_get_vaddr_locked(display->tx_cmd_buf);
+
+		if (IS_ERR_OR_NULL(display->vaddr)) {
+			pr_err("failed to get va rc %d\n", rc);
+			goto end;
+		}
+	}
+
+	for (cnt = 0; cnt < display->ctrl_count; cnt++) {
+		display_ctrl = &display->ctrl[cnt];
+		display_ctrl->ctrl->cmd_buffer_size = display->cmd_buffer_size;
+		display_ctrl->ctrl->cmd_buffer_iova = display->cmd_buffer_iova;
+		display_ctrl->ctrl->vaddr = display->vaddr;
+		display_ctrl->ctrl->secure_mode = is_detach ? true : false;
+	}
+
+end:
+	/* release panel_lock */
+	dsi_panel_release_panel_lock(display->panel);
+}
+
 static int dsi_host_attach(struct mipi_dsi_host *host,
 			   struct mipi_dsi_device *dsi)
 {
@@ -1580,7 +1997,6 @@
 {
 	struct dsi_display *display = to_dsi_display(host);
 	struct dsi_display_ctrl *display_ctrl;
-	struct msm_gem_address_space *aspace = NULL;
 	int rc = 0, cnt = 0;
 
 	if (!host || !msg) {
@@ -1624,19 +2040,27 @@
 			goto error_disable_cmd_engine;
 		}
 
-		aspace = msm_gem_smmu_address_space_get(display->drm_dev,
-				MSM_SMMU_DOMAIN_UNSECURE);
-		if (!aspace) {
+		display->aspace = msm_gem_smmu_address_space_get(
+				display->drm_dev, MSM_SMMU_DOMAIN_UNSECURE);
+		if (!display->aspace) {
 			pr_err("failed to get aspace\n");
 			rc = -EINVAL;
 			goto free_gem;
 		}
 
-		rc = msm_gem_get_iova(display->tx_cmd_buf, aspace,
+		/* register to aspace */
+		rc = msm_gem_address_space_register_cb(display->aspace,
+				dsi_display_aspace_cb_locked, (void *)display);
+		if (rc) {
+			pr_err("failed to register callback %d", rc);
+			goto free_gem;
+		}
+
+		rc = msm_gem_get_iova(display->tx_cmd_buf, display->aspace,
 					&(display->cmd_buffer_iova));
 		if (rc) {
 			pr_err("failed to get the iova rc %d\n", rc);
-			goto free_gem;
+			goto free_aspace_cb;
 		}
 
 		display->vaddr =
@@ -1688,9 +2112,14 @@
 	}
 	return rc;
 put_iova:
-	msm_gem_put_iova(display->tx_cmd_buf, aspace);
+	msm_gem_put_iova(display->tx_cmd_buf, display->aspace);
+free_aspace_cb:
+	msm_gem_address_space_unregister_cb(display->aspace,
+			dsi_display_aspace_cb_locked, display);
 free_gem:
+	mutex_lock(&display->drm_dev->struct_mutex);
 	msm_gem_free_object(display->tx_cmd_buf);
+	mutex_unlock(&display->drm_dev->struct_mutex);
 error:
 	return rc;
 }
@@ -2075,6 +2504,7 @@
 		 *     not be changed during static screen.
 		 */
 
+	  pr_debug("updating power states for ctrl and phy\n");
 		rc = dsi_display_ctrl_power_on(display);
 		if (rc) {
 			pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
@@ -2533,7 +2963,7 @@
 }
 
 static int dsi_display_dfps_calc_front_porch(
-		u64 clk_hz,
+		u32 old_fps,
 		u32 new_fps,
 		u32 a_total,
 		u32 b_total,
@@ -2541,6 +2971,7 @@
 		u32 *b_fp_out)
 {
 	s32 b_fp_new;
+	int add_porches, diff;
 
 	if (!b_fp_out) {
 		pr_err("Invalid params");
@@ -2552,15 +2983,22 @@
 		return -EINVAL;
 	}
 
-	/**
+	/*
 	 * Keep clock, other porches constant, use new fps, calc front porch
-	 * clk = (hor * ver * fps)
-	 * hfront = clk / (vtotal * fps)) - hactive - hback - hsync
+	 * new_vtotal = old_vtotal * (old_fps / new_fps )
+	 * new_vfp - old_vfp = new_vtotal - old_vtotal
+	 * new_vfp = old_vfp + old_vtotal * ((old_fps - new_fps)/ new_fps)
 	 */
-	b_fp_new = (clk_hz / (a_total * new_fps)) - (b_total - b_fp);
+	diff = abs(old_fps - new_fps);
+	add_porches = mult_frac(b_total, diff, new_fps);
 
-	pr_debug("clk %llu fps %u a %u b %u b_fp %u new_fp %d\n",
-			clk_hz, new_fps, a_total, b_total, b_fp, b_fp_new);
+	if (old_fps > new_fps)
+		b_fp_new = b_fp + add_porches;
+	else
+		b_fp_new = b_fp - add_porches;
+
+	pr_debug("fps %u a %u b %u b_fp %u new_fp %d\n",
+			new_fps, a_total, b_total, b_fp, b_fp_new);
 
 	if (b_fp_new < 0) {
 		pr_err("Invalid new_hfp calcluated%d\n", b_fp_new);
@@ -2576,14 +3014,25 @@
 	return 0;
 }
 
+/**
+ * dsi_display_get_dfps_timing() - Get the new dfps values.
+ * @display:         DSI display handle.
+ * @adj_mode:        Mode value structure to be changed.
+ *                   It contains old timing values and latest fps value.
+ *                   New timing values are updated based on new fps.
+ * @curr_refresh_rate:  Current fps rate.
+ *                      If zero , current fps rate is taken from
+ *                      display->panel->cur_mode.
+ * Return: error code.
+ */
 static int dsi_display_get_dfps_timing(struct dsi_display *display,
-				       struct dsi_display_mode *adj_mode)
+			struct dsi_display_mode *adj_mode,
+				u32 curr_refresh_rate)
 {
 	struct dsi_dfps_capabilities dfps_caps;
 	struct dsi_display_mode per_ctrl_mode;
 	struct dsi_mode_info *timing;
 	struct dsi_ctrl *m_ctrl;
-	u64 clk_hz;
 
 	int rc = 0;
 
@@ -2602,20 +3051,27 @@
 	per_ctrl_mode = *adj_mode;
 	adjust_timing_by_ctrl_count(display, &per_ctrl_mode);
 
-	if (!dsi_display_is_seamless_dfps_possible(display,
-			&per_ctrl_mode, dfps_caps.type)) {
-		pr_err("seamless dynamic fps not supported for mode\n");
-		return -EINVAL;
+	if (!curr_refresh_rate) {
+		if (!dsi_display_is_seamless_dfps_possible(display,
+				&per_ctrl_mode, dfps_caps.type)) {
+			pr_err("seamless dynamic fps not supported for mode\n");
+			return -EINVAL;
+		}
+		if (display->panel->cur_mode) {
+			curr_refresh_rate =
+				display->panel->cur_mode->timing.refresh_rate;
+		} else {
+			pr_err("cur_mode is not initialized\n");
+			return -EINVAL;
+		}
 	}
-
 	/* TODO: Remove this direct reference to the dsi_ctrl */
-	clk_hz = m_ctrl->clk_freq.pix_clk_rate;
 	timing = &per_ctrl_mode.timing;
 
 	switch (dfps_caps.type) {
 	case DSI_DFPS_IMMEDIATE_VFP:
 		rc = dsi_display_dfps_calc_front_porch(
-				clk_hz,
+				curr_refresh_rate,
 				timing->refresh_rate,
 				DSI_H_TOTAL(timing),
 				DSI_V_TOTAL(timing),
@@ -2625,7 +3081,7 @@
 
 	case DSI_DFPS_IMMEDIATE_HFP:
 		rc = dsi_display_dfps_calc_front_porch(
-				clk_hz,
+				curr_refresh_rate,
 				timing->refresh_rate,
 				DSI_V_TOTAL(timing),
 				DSI_H_TOTAL(timing),
@@ -2654,7 +3110,7 @@
 	}
 
 	/* Currently the only seamless transition is dynamic fps */
-	rc = dsi_display_get_dfps_timing(display, adj_mode);
+	rc = dsi_display_get_dfps_timing(display, adj_mode, 0);
 	if (rc) {
 		pr_debug("Dynamic FPS not supported for seamless\n");
 	} else {
@@ -2694,7 +3150,8 @@
 	memcpy(&display->config.lane_map, &display->lane_map,
 	       sizeof(display->lane_map));
 
-	if (mode->dsi_mode_flags & DSI_MODE_FLAG_DFPS) {
+	if (mode->dsi_mode_flags &
+			(DSI_MODE_FLAG_DFPS | DSI_MODE_FLAG_VRR)) {
 		rc = dsi_display_dfps_update(display, mode);
 		if (rc) {
 			pr_err("[%s]DSI dfps update failed, rc=%d\n",
@@ -2714,6 +3171,20 @@
 		}
 	}
 
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+
+		if (!ctrl->phy || !ctrl->ctrl)
+			continue;
+
+		rc = dsi_phy_set_clk_freq(ctrl->phy, &ctrl->ctrl->clk_freq);
+		if (rc) {
+			pr_err("[%s] failed to set phy clk freq, rc=%d\n",
+			       display->name, rc);
+			goto error;
+		}
+	}
+
 	if (priv_info->phy_timing_len) {
 		for (i = 0; i < display->ctrl_count; i++) {
 			ctrl = &display->ctrl[i];
@@ -2791,6 +3262,110 @@
 }
 
 /**
+ * dsi_display_splash_res_init() - Initialize resources for continuous splash
+ * @display:    Pointer to dsi display
+ * Returns:     Zero on success
+ */
+static int dsi_display_splash_res_init(struct  dsi_display *display)
+{
+	int rc = 0;
+
+	/* Vote for gdsc required to read register address space */
+
+	display->cont_splash_client = sde_power_client_create(display->phandle,
+						"cont_splash_client");
+	rc = sde_power_resource_enable(display->phandle,
+			display->cont_splash_client, true);
+	if (rc) {
+		pr_err("failed to vote gdsc for continuous splash, rc=%d\n",
+							rc);
+		return -EINVAL;
+	}
+
+	/* Verify whether continuous splash is enabled or not */
+	display->is_cont_splash_enabled =
+		dsi_display_get_cont_splash_status(display);
+	if (!display->is_cont_splash_enabled) {
+		pr_err("Continuous splash is not enabled\n");
+		goto splash_disabled;
+	}
+
+	/* Update splash status for clock manager */
+	dsi_display_clk_mngr_update_splash_status(display->clk_mngr,
+				display->is_cont_splash_enabled);
+
+	/* Vote for Core clk and link clk. Votes on ctrl and phy
+	 * regulator are inplicit from  pre clk on callback
+	 */
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+	if (rc) {
+		pr_err("[%s] failed to enable DSI link clocks, rc=%d\n",
+		       display->name, rc);
+		goto clk_manager_update;
+	}
+
+	/* Vote on panel regulator will be removed during suspend path */
+	rc = dsi_pwr_enable_regulator(&display->panel->power_info, true);
+	if (rc) {
+		pr_err("[%s] failed to enable vregs, rc=%d\n",
+				display->panel->name, rc);
+		goto clks_disabled;
+	}
+
+	dsi_config_host_engine_state_for_cont_splash(display);
+
+	return rc;
+
+clks_disabled:
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+
+clk_manager_update:
+	/* Update splash status for clock manager */
+	dsi_display_clk_mngr_update_splash_status(display->clk_mngr,
+				false);
+
+splash_disabled:
+	(void)sde_power_resource_enable(display->phandle,
+			display->cont_splash_client, false);
+	display->is_cont_splash_enabled = false;
+	return rc;
+}
+
+/**
+ * dsi_display_splash_res_cleanup() - cleanup for continuous splash
+ * @display:    Pointer to dsi display
+ * Returns:     Zero on success
+ */
+int dsi_display_splash_res_cleanup(struct  dsi_display *display)
+{
+	int rc = 0;
+
+	if (!display->is_cont_splash_enabled)
+		return 0;
+
+	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+	if (rc)
+		pr_err("[%s] failed to disable DSI link clocks, rc=%d\n",
+		       display->name, rc);
+
+	rc = sde_power_resource_enable(display->phandle,
+			display->cont_splash_client, false);
+	if (rc)
+		pr_err("failed to remove vote on gdsc for continuous splash, rc=%d\n",
+				rc);
+
+	display->is_cont_splash_enabled = false;
+	/* Update splash status for clock manager */
+	dsi_display_clk_mngr_update_splash_status(display->clk_mngr,
+				display->is_cont_splash_enabled);
+
+	return rc;
+}
+
+/**
  * dsi_display_bind - bind dsi device with controlling device
  * @dev:        Pointer to base of platform device
  * @master:     Pointer to container of drm device
@@ -2876,6 +3451,7 @@
 		}
 	}
 
+	display->phandle = &priv->phandle;
 	info.pre_clkoff_cb = dsi_pre_clkoff_cb;
 	info.pre_clkon_cb = dsi_pre_clkon_cb;
 	info.post_clkoff_cb = dsi_post_clkoff_cb;
@@ -2952,6 +3528,12 @@
 
 	pr_info("Successfully bind display panel '%s'\n", display->name);
 	display->drm_dev = drm;
+
+	/* Initialize resources for continuous splash */
+	rc = dsi_display_splash_res_init(display);
+	if (rc)
+		pr_err("Continuous splash resource init failed, rc=%d\n", rc);
+
 	goto error;
 
 error_host_deinit:
@@ -3060,6 +3642,8 @@
 		return -ENOMEM;
 
 	display->name = of_get_property(pdev->dev.of_node, "label", NULL);
+	if (!display->name)
+		display->name = "unknown";
 
 	if (!boot_displays_parsed) {
 		boot_displays[DSI_PRIMARY].boot_disp_en = false;
@@ -3366,8 +3950,8 @@
 		break;
 	}
 
-	memcpy(&info->roi_caps, &display->panel->roi_caps,
-			sizeof(info->roi_caps));
+	if (display->panel->esd_config.esd_enabled)
+		info->capabilities |= MSM_DISPLAY_ESD_ENABLED;
 
 error:
 	mutex_unlock(&display->display_lock);
@@ -3472,6 +4056,7 @@
 
 		for (i = 0; i < num_dfps_rates; i++) {
 			struct dsi_display_mode *sub_mode = &modes[array_idx];
+			u32 curr_refresh_rate;
 
 			if (!sub_mode) {
 				pr_err("invalid mode data\n");
@@ -3481,9 +4066,15 @@
 			memcpy(sub_mode, &panel_mode, sizeof(panel_mode));
 
 			if (dfps_caps.dfps_support) {
+				curr_refresh_rate =
+					sub_mode->timing.refresh_rate;
 				sub_mode->timing.refresh_rate =
 					dfps_caps.min_refresh_rate +
 					(i % num_dfps_rates);
+
+				dsi_display_get_dfps_timing(display,
+					sub_mode, curr_refresh_rate);
+
 				sub_mode->pixel_clk_khz =
 					(DSI_H_TOTAL(&sub_mode->timing) *
 					DSI_V_TOTAL(&sub_mode->timing) *
@@ -3498,6 +4089,102 @@
 	return rc;
 }
 
+/**
+ * dsi_display_validate_mode_vrr() - Validate if varaible refresh case.
+ * @display:     DSI display handle.
+ * @cur_dsi_mode:   Current DSI mode.
+ * @mode:        Mode value structure to be validated.
+ *               MSM_MODE_FLAG_SEAMLESS_VRR flag is set if there
+ *               is change in fps but vactive and hactive are same.
+ * Return: error code.
+ */
+int dsi_display_validate_mode_vrr(struct dsi_display *display,
+			struct dsi_display_mode *cur_dsi_mode,
+			struct dsi_display_mode *mode)
+{
+	int rc = 0;
+	struct dsi_display_mode adj_mode, cur_mode;
+	struct dsi_dfps_capabilities dfps_caps;
+	u32 curr_refresh_rate;
+
+	if (!display || !mode) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	if (!display->panel || !display->panel->cur_mode) {
+		pr_debug("Current panel mode not set\n");
+		return rc;
+	}
+
+	mutex_lock(&display->display_lock);
+
+	adj_mode = *mode;
+	cur_mode = *cur_dsi_mode;
+
+	if ((cur_mode.timing.refresh_rate != adj_mode.timing.refresh_rate) &&
+		(cur_mode.timing.v_active == adj_mode.timing.v_active) &&
+		(cur_mode.timing.h_active == adj_mode.timing.h_active)) {
+
+		curr_refresh_rate = cur_mode.timing.refresh_rate;
+		rc = dsi_panel_get_dfps_caps(display->panel, &dfps_caps);
+		if (rc) {
+			pr_err("[%s] failed to get dfps caps from panel\n",
+					display->name);
+			goto error;
+		}
+
+		cur_mode.timing.refresh_rate =
+			adj_mode.timing.refresh_rate;
+
+		rc = dsi_display_get_dfps_timing(display,
+			&cur_mode, curr_refresh_rate);
+		if (rc) {
+			pr_err("[%s] seamless vrr not possible rc=%d\n",
+			display->name, rc);
+			goto error;
+		}
+		switch (dfps_caps.type) {
+		/*
+		 * Ignore any round off factors in porch calculation.
+		 * Worse case is set to 5.
+		 */
+		case DSI_DFPS_IMMEDIATE_VFP:
+			if (abs(DSI_V_TOTAL(&cur_mode.timing) -
+				DSI_V_TOTAL(&adj_mode.timing)) > 5)
+				pr_err("Mismatch vfp fps:%d new:%d given:%d\n",
+				adj_mode.timing.refresh_rate,
+				cur_mode.timing.v_front_porch,
+				adj_mode.timing.v_front_porch);
+			break;
+
+		case DSI_DFPS_IMMEDIATE_HFP:
+			if (abs(DSI_H_TOTAL(&cur_mode.timing) -
+				DSI_H_TOTAL(&adj_mode.timing)) > 5)
+				pr_err("Mismatch hfp fps:%d new:%d given:%d\n",
+				adj_mode.timing.refresh_rate,
+				cur_mode.timing.h_front_porch,
+				adj_mode.timing.h_front_porch);
+			break;
+
+		default:
+			pr_err("Unsupported DFPS mode %d\n",
+				dfps_caps.type);
+			rc = -ENOTSUPP;
+		}
+
+		pr_debug("Mode switch is seamless variable refresh\n");
+		mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
+		SDE_EVT32(curr_refresh_rate, adj_mode.timing.refresh_rate,
+				cur_mode.timing.h_front_porch,
+				adj_mode.timing.h_front_porch);
+	}
+
+error:
+	mutex_unlock(&display->display_lock);
+	return rc;
+}
+
 int dsi_display_validate_mode(struct dsi_display *display,
 			      struct dsi_display_mode *mode,
 			      u32 flags)
@@ -3671,6 +4358,232 @@
 	return rc;
 }
 
+static void dsi_display_handle_fifo_underflow(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+
+	display =  container_of(work, struct dsi_display, fifo_underflow_work);
+	if (!display)
+		return;
+	pr_debug("handle DSI FIFO underflow error\n");
+
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+	dsi_display_soft_reset(display);
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static void dsi_display_handle_fifo_overflow(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+	struct dsi_display_ctrl *ctrl;
+	int i, rc;
+	int mask = BIT(20); /* clock lane */
+	int (*cb_func)(void *event_usr_ptr,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3);
+	void *data;
+	u32 version = 0;
+
+	display =  container_of(work, struct dsi_display, fifo_overflow_work);
+	if (!display || !display->panel ||
+			(display->panel->panel_mode != DSI_OP_VIDEO_MODE))
+		return;
+
+	pr_debug("handle DSI FIFO overflow error\n");
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+
+	/*
+	 * below recovery sequence is not applicable to
+	 * hw version 2.0.0, 2.1.0 and 2.2.0, so return early.
+	 */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	version = dsi_ctrl_get_hw_version(ctrl->ctrl);
+	if (!version || (version < 0x20020001))
+		goto end;
+
+	/* reset ctrl and lanes */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		rc = dsi_ctrl_reset(ctrl->ctrl, mask);
+		rc = dsi_phy_lane_reset(ctrl->phy);
+	}
+
+	/* wait for display line count to be in active area */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	if (ctrl->ctrl->recovery_cb.event_cb) {
+		cb_func = ctrl->ctrl->recovery_cb.event_cb;
+		data = ctrl->ctrl->recovery_cb.event_usr_ptr;
+		rc = cb_func(data, SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+				display->clk_master_idx, 0, 0, 0, 0);
+		if (rc < 0) {
+			pr_debug("sde callback failed\n");
+			goto end;
+		}
+	}
+
+	/* Enable Video mode for DSI controller */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		dsi_ctrl_vid_engine_en(ctrl->ctrl, true);
+	}
+	/*
+	 * Add sufficient delay to make sure
+	 * pixel transmission has started
+	 */
+	udelay(200);
+end:
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static void dsi_display_handle_lp_rx_timeout(struct work_struct *work)
+{
+	struct dsi_display *display = NULL;
+	struct dsi_display_ctrl *ctrl;
+	int i, rc;
+	int mask = (BIT(20) | (0xF << 16)); /* clock lane and 4 data lane */
+	int (*cb_func)(void *event_usr_ptr,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3);
+	void *data;
+	u32 version = 0;
+
+	display =  container_of(work, struct dsi_display, fifo_overflow_work);
+	if (!display || (display->panel->panel_mode != DSI_OP_VIDEO_MODE))
+		return;
+	pr_debug("handle DSI LP RX Timeout error\n");
+
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_ON);
+
+	/*
+	 * below recovery sequence is not applicable to
+	 * hw version 2.0.0, 2.1.0 and 2.2.0, so return early.
+	 */
+	ctrl = &display->ctrl[display->clk_master_idx];
+	version = dsi_ctrl_get_hw_version(ctrl->ctrl);
+	if (!version || (version < 0x20020001))
+		goto end;
+
+	/* reset ctrl and lanes */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		rc = dsi_ctrl_reset(ctrl->ctrl, mask);
+		rc = dsi_phy_lane_reset(ctrl->phy);
+	}
+
+	ctrl = &display->ctrl[display->clk_master_idx];
+	if (ctrl->ctrl->recovery_cb.event_cb) {
+		cb_func = ctrl->ctrl->recovery_cb.event_cb;
+		data = ctrl->ctrl->recovery_cb.event_usr_ptr;
+		rc = cb_func(data, SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+				display->clk_master_idx, 0, 0, 0, 0);
+		if (rc < 0) {
+			pr_debug("Target is in suspend/shutdown\n");
+			goto end;
+		}
+	}
+
+	/* Enable Video mode for DSI controller */
+	for (i = 0 ; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		dsi_ctrl_vid_engine_en(ctrl->ctrl, true);
+	}
+
+	/*
+	 * Add sufficient delay to make sure
+	 * pixel transmission as started
+	 */
+	udelay(200);
+end:
+	dsi_display_clk_ctrl(display->dsi_clk_handle,
+			DSI_ALL_CLKS, DSI_CLK_OFF);
+}
+
+static int dsi_display_cb_error_handler(void *data,
+		uint32_t event_idx, uint32_t instance_idx,
+		uint32_t data0, uint32_t data1,
+		uint32_t data2, uint32_t data3)
+{
+	struct dsi_display *display =  data;
+
+	if (!display)
+		return -EINVAL;
+
+	switch (event_idx) {
+	case DSI_FIFO_UNDERFLOW:
+		queue_work(display->err_workq, &display->fifo_underflow_work);
+		break;
+	case DSI_FIFO_OVERFLOW:
+		queue_work(display->err_workq, &display->fifo_overflow_work);
+		break;
+	case DSI_LP_Rx_TIMEOUT:
+		queue_work(display->err_workq, &display->lp_rx_timeout_work);
+		break;
+	default:
+		pr_warn("unhandled error interrupt: %d\n", event_idx);
+		break;
+	}
+
+	return 0;
+}
+
+static void dsi_display_register_error_handler(struct dsi_display *display)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+	struct dsi_event_cb_info event_info;
+
+	if (!display)
+		return;
+
+	display->err_workq = create_singlethread_workqueue("dsi_err_workq");
+	if (!display->err_workq) {
+		pr_err("failed to create dsi workq!\n");
+		return;
+	}
+
+	INIT_WORK(&display->fifo_underflow_work,
+				dsi_display_handle_fifo_underflow);
+	INIT_WORK(&display->fifo_overflow_work,
+				dsi_display_handle_fifo_overflow);
+	INIT_WORK(&display->lp_rx_timeout_work,
+				dsi_display_handle_lp_rx_timeout);
+
+	memset(&event_info, 0, sizeof(event_info));
+
+	event_info.event_cb = dsi_display_cb_error_handler;
+	event_info.event_usr_ptr = display;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		ctrl->ctrl->irq_info.irq_err_cb = event_info;
+	}
+}
+
+static void dsi_display_unregister_error_handler(struct dsi_display *display)
+{
+	int i = 0;
+	struct dsi_display_ctrl *ctrl;
+
+	if (!display)
+		return;
+
+	for (i = 0; i < display->ctrl_count; i++) {
+		ctrl = &display->ctrl[i];
+		memset(&ctrl->ctrl->irq_info.irq_err_cb,
+				0, sizeof(struct dsi_event_cb_info));
+	}
+
+	if (display->err_workq)
+		destroy_workqueue(display->err_workq);
+}
+
 int dsi_display_prepare(struct dsi_display *display)
 {
 	int rc = 0;
@@ -3700,11 +4613,18 @@
 		goto error;
 	}
 
-	rc = dsi_panel_pre_prepare(display->panel);
-	if (rc) {
-		pr_err("[%s] panel pre-prepare failed, rc=%d\n",
-		       display->name, rc);
-		goto error;
+	if (!display->is_cont_splash_enabled) {
+		/*
+		 * For continuous splash usecase we skip panel
+		 * pre prepare since the regulator vote is already
+		 * taken care in splash resource init
+		 */
+		rc = dsi_panel_pre_prepare(display->panel);
+		if (rc) {
+			pr_err("[%s] panel pre-prepare failed, rc=%d\n",
+					display->name, rc);
+			goto error;
+		}
 	}
 
 	rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
@@ -3741,6 +4661,8 @@
 		       display->name, rc);
 		goto error_phy_disable;
 	}
+	/* Set up DSI ERROR event callback */
+	dsi_display_register_error_handler(display);
 
 	rc = dsi_display_ctrl_host_enable(display);
 	if (rc) {
@@ -3763,12 +4685,19 @@
 		goto error_ctrl_link_off;
 	}
 
-	rc = dsi_panel_prepare(display->panel);
-	if (rc) {
-		pr_err("[%s] panel prepare failed, rc=%d\n", display->name, rc);
-		goto error_ctrl_link_off;
+	if (!display->is_cont_splash_enabled) {
+		/*
+		 * For continuous splash usecase we skip panel
+		 * prepare since the pnael is already in
+		 * active state and panel on commands are not needed
+		 */
+		rc = dsi_panel_prepare(display->panel);
+		if (rc) {
+			pr_err("[%s] panel prepare failed, rc=%d\n",
+					display->name, rc);
+			goto error_ctrl_link_off;
+		}
 	}
-
 	goto error;
 
 error_ctrl_link_off:
@@ -3796,13 +4725,20 @@
 		struct dsi_rect *out_roi)
 {
 	const struct dsi_rect *bounds = &ctrl->ctrl->mode_bounds;
+	struct dsi_display_mode *cur_mode;
+	struct msm_roi_caps *roi_caps;
 	struct dsi_rect req_roi = { 0 };
 	int rc = 0;
 
-	if (req_rois->num_rects > display->panel->roi_caps.num_roi) {
+	cur_mode = display->panel->cur_mode;
+	if (!cur_mode)
+		return 0;
+
+	roi_caps = &cur_mode->priv_info->roi_caps;
+	if (req_rois->num_rects > roi_caps->num_roi) {
 		pr_err("request for %d rois greater than max %d\n",
 				req_rois->num_rects,
-				display->panel->roi_caps.num_roi);
+				roi_caps->num_roi);
 		rc = -EINVAL;
 		goto exit;
 	}
@@ -3839,13 +4775,20 @@
 static int dsi_display_set_roi(struct dsi_display *display,
 		struct msm_roi_list *rois)
 {
+	struct dsi_display_mode *cur_mode;
+	struct msm_roi_caps *roi_caps;
 	int rc = 0;
 	int i;
 
 	if (!display || !rois || !display->panel)
 		return -EINVAL;
 
-	if (!display->panel->roi_caps.enabled)
+	cur_mode = display->panel->cur_mode;
+	if (!cur_mode)
+		return 0;
+
+	roi_caps = &cur_mode->priv_info->roi_caps;
+	if (!roi_caps->enabled)
 		return 0;
 
 	for (i = 0; i < display->ctrl_count; i++) {
@@ -3900,6 +4843,46 @@
 	return rc;
 }
 
+int dsi_display_config_ctrl_for_cont_splash(struct dsi_display *display)
+{
+	int rc = 0;
+
+	if (!display || !display->panel) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	if (!display->panel->cur_mode) {
+		pr_err("no valid mode set for the display");
+		return -EINVAL;
+	}
+
+	if (!display->is_cont_splash_enabled)
+		return 0;
+
+	if (display->config.panel_mode == DSI_OP_VIDEO_MODE) {
+		rc = dsi_display_vid_engine_enable(display);
+		if (rc) {
+			pr_err("[%s]failed to enable DSI video engine, rc=%d\n",
+			       display->name, rc);
+			goto error_out;
+		}
+	} else if (display->config.panel_mode == DSI_OP_CMD_MODE) {
+		rc = dsi_display_cmd_engine_enable(display);
+		if (rc) {
+			pr_err("[%s]failed to enable DSI cmd engine, rc=%d\n",
+			       display->name, rc);
+			goto error_out;
+		}
+	} else {
+		pr_err("[%s] Invalid configuration\n", display->name);
+		rc = -EINVAL;
+	}
+
+error_out:
+	return rc;
+}
+
 int dsi_display_enable(struct dsi_display *display)
 {
 	int rc = 0;
@@ -3915,6 +4898,25 @@
 		return -EINVAL;
 	}
 
+	/* Engine states and panel states are populated during splash
+	 * resource init and hence we return early
+	 */
+	if (display->is_cont_splash_enabled) {
+
+		dsi_display_config_ctrl_for_cont_splash(display);
+
+		rc = dsi_display_splash_res_cleanup(display);
+		if (rc) {
+			pr_err("Continuous splash res cleanup failed, rc=%d\n",
+				rc);
+			return -EINVAL;
+		}
+
+		display->panel->panel_initialized = true;
+		pr_debug("cont splash enabled, display enable not required\n");
+		return 0;
+	}
+
 	mutex_lock(&display->display_lock);
 
 	mode = display->panel->cur_mode;
@@ -4122,6 +5124,9 @@
 		pr_err("[%s] failed to disable Link clocks, rc=%d\n",
 		       display->name, rc);
 
+	/* Free up DSI ERROR event callback */
+	dsi_display_unregister_error_handler(display);
+
 	rc = dsi_display_ctrl_deinit(display);
 	if (rc)
 		pr_err("[%s] failed to deinit controller, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index 1c30b9c..886641b 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation.All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -128,6 +128,7 @@
  * @display_type:     Display type as defined in device tree.
  * @list:             List pointer.
  * @is_active:        Is display active.
+ * @is_cont_splash_enabled:  Is continuous splash enabled
  * @display_lock:     Mutex for dsi_display interface.
  * @ctrl_count:       Number of DSI interfaces required by panel.
  * @ctrl:             Controller information for DSI display.
@@ -165,6 +166,7 @@
 	const char *display_type;
 	struct list_head list;
 	bool is_active;
+	bool is_cont_splash_enabled;
 	struct mutex display_lock;
 
 	u32 ctrl_count;
@@ -192,11 +194,15 @@
 	u32 cmd_buffer_size;
 	u32 cmd_buffer_iova;
 	void *vaddr;
+	struct msm_gem_address_space *aspace;
 
 	struct mipi_dsi_host host;
 	struct dsi_bridge    *bridge;
 	u32 cmd_engine_refcount;
 
+	struct sde_power_handle *phandle;
+	struct sde_power_client *cont_splash_client;
+
 	void *clk_mngr;
 	void *dsi_clk_handle;
 	void *mdp_clk_handle;
@@ -206,6 +212,11 @@
 
 	bool misr_enable;
 	u32 misr_frame_count;
+	/* multiple dsi error handlers */
+	struct workqueue_struct *err_workq;
+	struct work_struct fifo_underflow_work;
+	struct work_struct fifo_overflow_work;
+	struct work_struct lp_rx_timeout_work;
 };
 
 int dsi_display_dev_probe(struct platform_device *pdev);
@@ -324,6 +335,17 @@
 			      u32 flags);
 
 /**
+ * dsi_display_validate_mode_vrr() - validates mode if variable refresh case
+ * @display:             Handle to display.
+ * @mode:                Mode to be validated..
+ *
+ * Return: 0 if  error code.
+ */
+int dsi_display_validate_mode_vrr(struct dsi_display *display,
+			struct dsi_display_mode *cur_dsi_mode,
+			struct dsi_display_mode *mode);
+
+/**
  * dsi_display_set_mode() - Set mode on the display.
  * @display:           Handle to display.
  * @mode:              mode to be set.
@@ -348,6 +370,22 @@
 int dsi_display_prepare(struct dsi_display *display);
 
 /**
+ * dsi_display_splash_res_cleanup() - cleanup for continuous splash
+ * @display:    Pointer to dsi display
+ * Returns:     Zero on success
+ */
+int dsi_display_splash_res_cleanup(struct  dsi_display *display);
+
+/**
+ * dsi_display_config_ctrl_for_cont_splash()- Enable engine modes for DSI
+ *                                     controller during continuous splash
+ * @display: Handle to DSI display
+ *
+ * Return:        returns error code
+ */
+int dsi_display_config_ctrl_for_cont_splash(struct dsi_display *display);
+
+/**
  * dsi_display_enable() - enable display
  * @display:            Handle to display.
  *
@@ -478,6 +516,12 @@
 int dsi_display_set_backlight(void *display, u32 bl_lvl);
 
 /**
+ * dsi_display_check_status() - check if panel is dead or alive
+ * @display:            Handle to display.
+ */
+int dsi_display_check_status(void *display);
+
+/**
  * dsi_display_soft_reset() - perform a soft reset on DSI controller
  * @display:         Handle to display
  *
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index 30e5f02..b0a06e1 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -61,9 +61,11 @@
 		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VBLANK_PRE_MODESET;
 	if (msm_is_mode_seamless_dms(drm_mode))
 		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS;
+	if (msm_is_mode_seamless_vrr(drm_mode))
+		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
 }
 
-static void convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
+void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
 				struct drm_display_mode *drm_mode)
 {
 	memset(drm_mode, 0, sizeof(*drm_mode));
@@ -96,6 +98,8 @@
 		drm_mode->private_flags |= MSM_MODE_FLAG_VBLANK_PRE_MODESET;
 	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_DMS)
 		drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DMS;
+	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR)
+		drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR;
 
 	drm_mode_set_name(drm_mode);
 }
@@ -125,6 +129,9 @@
 		return;
 	}
 
+	if (!c_bridge || !c_bridge->display)
+		pr_err("Incorrect bridge details\n");
+
 	/* By this point mode should have been validated through mode_fixup */
 	rc = dsi_display_set_mode(c_bridge->display,
 			&(c_bridge->dsi_mode), 0x0);
@@ -134,7 +141,8 @@
 		return;
 	}
 
-	if (c_bridge->dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS) {
+	if (c_bridge->dsi_mode.dsi_mode_flags &
+		(DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) {
 		pr_debug("[%d] seamless pre-enable\n", c_bridge->id);
 		return;
 	}
@@ -152,11 +160,16 @@
 	rc = dsi_display_enable(c_bridge->display);
 	if (rc) {
 		pr_err("[%d] DSI display enable failed, rc=%d\n",
-		       c_bridge->id, rc);
+				c_bridge->id, rc);
 		(void)dsi_display_unprepare(c_bridge->display);
 	}
 	SDE_ATRACE_END("dsi_display_enable");
 	SDE_ATRACE_END("dsi_bridge_pre_enable");
+
+	rc = dsi_display_splash_res_cleanup(c_bridge->display);
+	if (rc)
+		pr_err("Continuous splash pipeline cleanup failed, rc=%d\n",
+									rc);
 }
 
 static void dsi_bridge_enable(struct drm_bridge *bridge)
@@ -169,7 +182,8 @@
 		return;
 	}
 
-	if (c_bridge->dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_SEAMLESS) {
+	if (c_bridge->dsi_mode.dsi_mode_flags &
+			(DSI_MODE_FLAG_SEAMLESS | DSI_MODE_FLAG_VRR)) {
 		pr_debug("[%d] seamless enable\n", c_bridge->id);
 		return;
 	}
@@ -249,7 +263,7 @@
 {
 	int rc = 0;
 	struct dsi_bridge *c_bridge = to_dsi_bridge(bridge);
-	struct dsi_display_mode dsi_mode;
+	struct dsi_display_mode dsi_mode, cur_dsi_mode;
 	struct drm_display_mode cur_mode;
 
 	if (!bridge || !mode || !adjusted_mode) {
@@ -267,20 +281,31 @@
 	}
 
 	if (bridge->encoder && bridge->encoder->crtc) {
+
+		convert_to_dsi_mode(&bridge->encoder->crtc->state->mode,
+							&cur_dsi_mode);
+		rc = dsi_display_validate_mode_vrr(c_bridge->display,
+					&cur_dsi_mode, &dsi_mode);
+		if (rc)
+			pr_debug("[%s] vrr mode mismatch failure rc=%d\n",
+				c_bridge->display->name, rc);
+
 		cur_mode = bridge->encoder->crtc->mode;
 
-		if (!drm_mode_equal(&cur_mode, adjusted_mode))
+		if (!drm_mode_equal(&cur_mode, adjusted_mode) &&
+			(!(dsi_mode.dsi_mode_flags &
+				DSI_MODE_FLAG_VRR)))
 			dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS;
 	}
 
-	convert_to_drm_mode(&dsi_mode, adjusted_mode);
+	dsi_convert_to_drm_mode(&dsi_mode, adjusted_mode);
 
 	return true;
 }
 
 int dsi_conn_get_mode_info(const struct drm_display_mode *drm_mode,
 	struct msm_mode_info *mode_info,
-	u32 max_mixer_width)
+	u32 max_mixer_width, void *display)
 {
 	struct dsi_display_mode dsi_mode;
 	struct dsi_mode_info *timing;
@@ -301,6 +326,7 @@
 	mode_info->prefill_lines = dsi_mode.priv_info->panel_prefill_lines;
 	mode_info->jitter_numer = dsi_mode.priv_info->panel_jitter_numer;
 	mode_info->jitter_denom = dsi_mode.priv_info->panel_jitter_denom;
+	mode_info->clk_rate = dsi_mode.priv_info->clk_rate_hz;
 
 	memcpy(&mode_info->topology, &dsi_mode.priv_info->topology,
 			sizeof(struct msm_display_topology));
@@ -312,6 +338,11 @@
 			sizeof(dsi_mode.priv_info->dsc));
 	}
 
+	if (dsi_mode.priv_info->roi_caps.enabled) {
+		memcpy(&mode_info->roi_caps, &dsi_mode.priv_info->roi_caps,
+			sizeof(dsi_mode.priv_info->roi_caps));
+	}
+
 	return 0;
 }
 
@@ -325,9 +356,8 @@
 	.mode_set     = dsi_bridge_mode_set,
 };
 
-int dsi_conn_post_init(struct drm_connector *connector,
-		void *info,
-		void *display)
+int dsi_conn_set_info_blob(struct drm_connector *connector,
+		void *info, void *display, struct msm_mode_info *mode_info)
 {
 	struct dsi_display *dsi_display = display;
 	struct dsi_panel *panel;
@@ -383,6 +413,13 @@
 	sde_kms_info_add_keystr(info, "dfps support",
 			panel->dfps_caps.dfps_support ? "true" : "false");
 
+	if (panel->dfps_caps.dfps_support) {
+		sde_kms_info_add_keyint(info, "min_fps",
+			panel->dfps_caps.min_refresh_rate);
+		sde_kms_info_add_keyint(info, "max_fps",
+			panel->dfps_caps.max_refresh_rate);
+	}
+
 	switch (panel->phy_props.rotation) {
 	case DSI_PANEL_ROTATE_NONE:
 		sde_kms_info_add_keystr(info, "panel orientation", "none");
@@ -419,23 +456,23 @@
 		break;
 	}
 
-	if (panel->roi_caps.enabled) {
+	if (mode_info && mode_info->roi_caps.enabled) {
 		sde_kms_info_add_keyint(info, "partial_update_num_roi",
-				panel->roi_caps.num_roi);
+				mode_info->roi_caps.num_roi);
 		sde_kms_info_add_keyint(info, "partial_update_xstart",
-				panel->roi_caps.align.xstart_pix_align);
+				mode_info->roi_caps.align.xstart_pix_align);
 		sde_kms_info_add_keyint(info, "partial_update_walign",
-				panel->roi_caps.align.width_pix_align);
+				mode_info->roi_caps.align.width_pix_align);
 		sde_kms_info_add_keyint(info, "partial_update_wmin",
-				panel->roi_caps.align.min_width);
+				mode_info->roi_caps.align.min_width);
 		sde_kms_info_add_keyint(info, "partial_update_ystart",
-				panel->roi_caps.align.ystart_pix_align);
+				mode_info->roi_caps.align.ystart_pix_align);
 		sde_kms_info_add_keyint(info, "partial_update_halign",
-				panel->roi_caps.align.height_pix_align);
+				mode_info->roi_caps.align.height_pix_align);
 		sde_kms_info_add_keyint(info, "partial_update_hmin",
-				panel->roi_caps.align.min_height);
+				mode_info->roi_caps.align.min_height);
 		sde_kms_info_add_keyint(info, "partial_update_roimerge",
-				panel->roi_caps.merge_rois);
+				mode_info->roi_caps.merge_rois);
 	}
 
 end:
@@ -529,7 +566,7 @@
 		struct drm_display_mode *m;
 
 		memset(&drm_mode, 0x0, sizeof(drm_mode));
-		convert_to_drm_mode(&modes[i], &drm_mode);
+		dsi_convert_to_drm_mode(&modes[i], &drm_mode);
 		m = drm_mode_duplicate(connector->dev, &drm_mode);
 		if (!m) {
 			pr_err("failed to add mode %ux%u\n",
@@ -598,6 +635,59 @@
 	dsi_display_enable_event(display, event_idx, &event_info, enable);
 }
 
+int dsi_conn_post_kickoff(struct drm_connector *connector)
+{
+	struct drm_encoder *encoder;
+	struct dsi_bridge *c_bridge;
+	struct dsi_display_mode adj_mode;
+	struct dsi_display *display;
+	struct dsi_display_ctrl *m_ctrl, *ctrl;
+	int i, rc = 0;
+
+	if (!connector || !connector->state) {
+		pr_err("invalid connector or connector state");
+		return -EINVAL;
+	}
+
+	encoder = connector->state->best_encoder;
+	if (!encoder) {
+		pr_debug("best encoder is not available");
+		return 0;
+	}
+
+	c_bridge = to_dsi_bridge(encoder->bridge);
+	adj_mode = c_bridge->dsi_mode;
+	display = c_bridge->display;
+
+	if (adj_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR) {
+		m_ctrl = &display->ctrl[display->clk_master_idx];
+		rc = dsi_ctrl_timing_db_update(m_ctrl->ctrl, false);
+		if (rc) {
+			pr_err("[%s] failed to dfps update  rc=%d\n",
+				display->name, rc);
+			return -EINVAL;
+		}
+
+		/* Update the rest of the controllers */
+		for (i = 0; i < display->ctrl_count; i++) {
+			ctrl = &display->ctrl[i];
+			if (!ctrl->ctrl || (ctrl == m_ctrl))
+				continue;
+
+			rc = dsi_ctrl_timing_db_update(ctrl->ctrl, false);
+			if (rc) {
+				pr_err("[%s] failed to dfps update rc=%d\n",
+					display->name,  rc);
+				return -EINVAL;
+			}
+		}
+
+		c_bridge->dsi_mode.dsi_mode_flags &= ~DSI_MODE_FLAG_VRR;
+	}
+
+	return 0;
+}
+
 struct dsi_bridge *dsi_drm_bridge_init(struct dsi_display *display,
 				       struct drm_device *dev,
 				       struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
index 793f8f1..ec58479 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
@@ -33,15 +33,17 @@
 };
 
 /**
- * dsi_conn_post_init - callback to perform additional initialization steps
+ * dsi_conn_set_info_blob - callback to perform info blob initialization
  * @connector: Pointer to drm connector structure
  * @info: Pointer to sde connector info structure
  * @display: Pointer to private display handle
+ * @mode_info: Pointer to mode info structure
  * Returns: Zero on success
  */
-int dsi_conn_post_init(struct drm_connector *connector,
+int dsi_conn_set_info_blob(struct drm_connector *connector,
 		void *info,
-		void *display);
+		void *display,
+		struct msm_mode_info *mode_info);
 
 /**
  * dsi_conn_detect - callback to determine if connector is connected
@@ -76,10 +78,12 @@
  * @drm_mode: Display mode set for the display
  * @mode_info: Out parameter. information of the mode.
  * @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
  * Returns: Zero on success
  */
 int dsi_conn_get_mode_info(const struct drm_display_mode *drm_mode,
-	struct msm_mode_info *mode_info, u32 max_mixer_width);
+	struct msm_mode_info *mode_info, u32 max_mixer_width,
+	void *display);
 
 /**
  * dsi_conn_mode_valid - callback to determine if specified mode is valid
@@ -119,4 +123,19 @@
 		void *display,
 		struct msm_display_kickoff_params *params);
 
+/**
+ * dsi_display_post_kickoff - program post kickoff-time features
+ * @connector: Pointer to drm connector structure
+ * Returns: Zero on success
+ */
+int dsi_conn_post_kickoff(struct drm_connector *connector);
+
+/**
+ * dsi_convert_to_drm_mode - Update drm mode with dsi mode information
+ * @dsi_mode: input parameter. structure having dsi mode information.
+ * @drm_mode: output parameter. DRM mode set for the display
+ */
+void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
+				struct drm_display_mode *drm_mode);
+
 #endif /* _DSI_DRM_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 693295f..d0cb51b 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -493,10 +493,12 @@
 	}
 
 	for (i = 0; i < count; i++) {
-		/* TODO:  handle last command */
 		if (state == DSI_CMD_SET_STATE_LP)
 			cmds->msg.flags |= MIPI_DSI_MSG_USE_LPM;
 
+		if (cmds->last_command)
+			cmds->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND;
+
 		len = ops->transfer(panel->host, &cmds->msg);
 		if (len < 0) {
 			rc = len;
@@ -683,6 +685,21 @@
 				  struct device_node *of_node)
 {
 	int rc = 0;
+	u64 tmp64;
+	struct dsi_display_mode *display_mode;
+
+	display_mode = container_of(mode, struct dsi_display_mode, timing);
+
+	rc = of_property_read_u64(of_node,
+			"qcom,mdss-dsi-panel-clockrate", &tmp64);
+	if (rc == -EOVERFLOW) {
+		tmp64 = 0;
+		rc = of_property_read_u32(of_node,
+			"qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64);
+	}
+
+	mode->clk_rate_hz = !rc ? tmp64 : 0;
+	display_mode->priv_info->clk_rate_hz = mode->clk_rate_hz;
 
 	rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-framerate",
 				  &mode->refresh_rate);
@@ -728,6 +745,10 @@
 	if (rc)
 		pr_err("qcom,mdss-dsi-h-sync-skew is not defined, rc=%d\n", rc);
 
+	pr_debug("panel horz active:%d front_portch:%d back_porch:%d sync_skew:%d\n",
+		mode->h_active, mode->h_front_porch, mode->h_back_porch,
+		mode->h_sync_width);
+
 	rc = of_property_read_u32(of_node, "qcom,mdss-dsi-panel-height",
 				  &mode->v_active);
 	if (rc) {
@@ -759,6 +780,9 @@
 		       rc);
 		goto error;
 	}
+	pr_debug("panel vert active:%d front_portch:%d back_porch:%d pulse_width:%d\n",
+		mode->v_active, mode->v_front_porch, mode->v_back_porch,
+		mode->v_sync_width);
 
 error:
 	return rc;
@@ -2158,7 +2182,9 @@
 
 	intf_width = mode->timing.h_active;
 	if (intf_width % priv_info->dsc.slice_width) {
-		pr_err("invalid slice width for the panel\n");
+		pr_err("invalid slice width for the intf width:%d slice width:%d\n",
+			intf_width, priv_info->dsc.slice_width);
+		rc = -EINVAL;
 		goto error;
 	}
 
@@ -2377,21 +2403,37 @@
 	return rc;
 }
 
-static int dsi_panel_parse_partial_update_caps(struct dsi_panel *panel,
-					       struct device_node *of_node)
+static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode,
+				struct device_node *of_node)
 {
-	struct msm_roi_caps *roi_caps = &panel->roi_caps;
+	struct msm_roi_caps *roi_caps = NULL;
 	const char *data;
 	int rc = 0;
 
+	if (!mode || !mode->priv_info) {
+		pr_err("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	roi_caps = &mode->priv_info->roi_caps;
+
 	memset(roi_caps, 0, sizeof(*roi_caps));
 
 	data = of_get_property(of_node, "qcom,partial-update-enabled", NULL);
 	if (data) {
 		if (!strcmp(data, "dual_roi"))
 			roi_caps->num_roi = 2;
-		else
+		else if (!strcmp(data, "single_roi"))
 			roi_caps->num_roi = 1;
+		else {
+			pr_info(
+			"invalid value for qcom,partial-update-enabled: %s\n",
+			data);
+			return 0;
+		}
+	} else {
+		pr_info("partial update disabled as the property is not set\n");
+		return 0;
 	}
 
 	roi_caps->merge_rois = of_property_read_bool(of_node,
@@ -2404,7 +2446,7 @@
 
 	if (roi_caps->enabled)
 		rc = dsi_panel_parse_roi_alignment(of_node,
-				&panel->roi_caps.align);
+				&roi_caps->align);
 
 	if (rc)
 		memset(roi_caps, 0, sizeof(*roi_caps));
@@ -2495,6 +2537,7 @@
 
 static void dsi_panel_esd_config_deinit(struct drm_panel_esd_config *esd_config)
 {
+	kfree(esd_config->status_buf);
 	kfree(esd_config->return_buf);
 	kfree(esd_config->status_value);
 	kfree(esd_config->status_valid_params);
@@ -2511,8 +2554,10 @@
 	struct property *data;
 	const char *string;
 	struct drm_panel_esd_config *esd_config;
+	u8 *esd_mode = NULL;
 
 	esd_config = &panel->esd_config;
+	esd_config->status_mode = ESD_MODE_MAX;
 	esd_config->esd_enabled = of_property_read_bool(of_node,
 		"qcom,esd-check-enabled");
 
@@ -2621,6 +2666,10 @@
 		goto error3;
 	}
 
+	esd_config->status_buf = kzalloc(SZ_4K, GFP_KERNEL);
+	if (!esd_config->status_buf)
+		goto error4;
+
 	rc = of_property_read_u32_array(of_node,
 		"qcom,mdss-dsi-panel-status-value",
 		esd_config->status_value, esd_config->groups * status_len);
@@ -2630,8 +2679,19 @@
 				esd_config->groups * status_len);
 	}
 
+	if (panel->esd_config.status_mode == ESD_MODE_REG_READ)
+		esd_mode = "register_read";
+	else if (panel->esd_config.status_mode == ESD_MODE_SW_BTA)
+		esd_mode = "bta_trigger";
+	else if (panel->esd_config.status_mode ==  ESD_MODE_PANEL_TE)
+		esd_mode = "te_check";
+
+	pr_info("ESD enabled with mode: %s\n", esd_mode);
+
 	return 0;
 
+error4:
+	kfree(esd_config->return_buf);
 error3:
 	kfree(esd_config->status_value);
 error2:
@@ -2703,10 +2763,6 @@
 	if (rc)
 		pr_err("failed to parse hdr config, rc=%d\n", rc);
 
-	rc = dsi_panel_parse_partial_update_caps(panel, of_node);
-	if (rc)
-		pr_debug("failed to partial update caps, rc=%d\n", rc);
-
 	rc = dsi_panel_get_mode_count(panel, of_node);
 	if (rc) {
 		pr_err("failed to get mode count, rc=%d\n", rc);
@@ -2718,20 +2774,8 @@
 		pr_debug("failed to get dms info, rc=%d\n", rc);
 
 	rc = dsi_panel_parse_esd_config(panel, of_node);
-	if (rc) {
+	if (rc)
 		pr_debug("failed to parse esd config, rc=%d\n", rc);
-	} else {
-		u8 *esd_mode = NULL;
-
-		if (panel->esd_config.status_mode == ESD_MODE_REG_READ)
-			esd_mode = "register_read";
-		else if (panel->esd_config.status_mode == ESD_MODE_SW_BTA)
-			esd_mode = "bta_trigger";
-		else if (panel->esd_config.status_mode ==  ESD_MODE_PANEL_TE)
-			esd_mode = "te_check";
-
-		pr_info("ESD enabled with mode: %s\n", esd_mode);
-	}
 
 	panel->panel_of_node = of_node;
 	drm_panel_init(&panel->drm_panel);
@@ -3027,6 +3071,10 @@
 			"failed to parse panel phy timings, rc=%d\n", rc);
 			goto parse_fail;
 		}
+
+		rc = dsi_panel_parse_partial_update_caps(mode, child_np);
+		if (rc)
+			pr_err("failed to partial update caps, rc=%d\n", rc);
 	}
 	goto done;
 
@@ -3068,6 +3116,7 @@
 	config->video_timing.dsc_enabled = mode->priv_info->dsc_enabled;
 	config->video_timing.dsc = &mode->priv_info->dsc;
 
+	config->bit_clk_rate_hz = mode->priv_info->clk_rate_hz;
 	config->esc_clk_rate_hz = 19200000;
 	mutex_unlock(&panel->panel_lock);
 	return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index f80dea2..ea67f45 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -135,7 +135,8 @@
 	u32 *status_cmds_rlen;
 	u32 *status_valid_params;
 	u32 *status_value;
-	unsigned char *return_buf;
+	u8 *return_buf;
+	u8 *status_buf;
 	u32 groups;
 };
 
@@ -155,8 +156,6 @@
 	enum dsi_op_mode panel_mode;
 
 	struct dsi_dfps_capabilities dfps_caps;
-	struct msm_roi_caps roi_caps;
-
 	struct dsi_panel_phy_props phy_props;
 
 	struct dsi_display_mode *cur_mode;
@@ -192,6 +191,16 @@
 	return panel->panel_initialized;
 }
 
+static inline void dsi_panel_acquire_panel_lock(struct dsi_panel *panel)
+{
+	mutex_lock(&panel->panel_lock);
+}
+
+static inline void dsi_panel_release_panel_lock(struct dsi_panel *panel)
+{
+	mutex_unlock(&panel->panel_lock);
+}
+
 struct dsi_panel *dsi_panel_get(struct device *parent,
 				struct device_node *of_node,
 				int topology_override);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index a91dba8..197d448 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -33,6 +33,8 @@
 
 #define DSI_PHY_DEFAULT_LABEL "MDSS PHY CTRL"
 
+#define BITS_PER_BYTE	8
+
 struct dsi_phy_list_item {
 	struct msm_dsi_phy *phy;
 	struct list_head list;
@@ -290,6 +292,14 @@
 
 	/* Actual timing values are dependent on panel */
 	timing->count_per_lane = phy->ver_info->timing_cfg_count;
+
+	phy->allow_phy_power_off = of_property_read_bool(pdev->dev.of_node,
+			"qcom,panel-allow-phy-poweroff");
+
+	of_property_read_u32(pdev->dev.of_node,
+			"qcom,dsi-phy-regulator-min-datarate-bps",
+			&phy->regulator_min_datarate_bps);
+
 	return 0;
 err:
 	lane->count_per_lane = 0;
@@ -641,7 +651,8 @@
 			goto error;
 		}
 
-		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF &&
+				dsi_phy->regulator_required) {
 			rc = dsi_pwr_enable_regulator(
 				&dsi_phy->pwr_info.phy_pwr, true);
 			if (rc) {
@@ -652,7 +663,8 @@
 			}
 		}
 	} else {
-		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+		if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF &&
+				dsi_phy->regulator_required) {
 			rc = dsi_pwr_enable_regulator(
 				&dsi_phy->pwr_info.phy_pwr, false);
 			if (rc) {
@@ -787,6 +799,7 @@
  * @config:             DSI host configuration.
  * @pll_source:         Source PLL for PHY clock.
  * @skip_validation:    Validation will not be performed on parameters.
+ * @is_cont_splash_enabled:    check whether continuous splash enabled.
  *
  * Validates and enables DSI PHY.
  *
@@ -795,7 +808,8 @@
 int dsi_phy_enable(struct msm_dsi_phy *phy,
 		   struct dsi_host_config *config,
 		   enum dsi_phy_pll_source pll_source,
-		   bool skip_validation)
+		   bool skip_validation,
+		   bool is_cont_splash_enabled)
 {
 	int rc = 0;
 
@@ -829,7 +843,10 @@
 		goto error;
 	}
 
-	dsi_phy_enable_hw(phy);
+	if (!is_cont_splash_enabled) {
+		dsi_phy_enable_hw(phy);
+		pr_debug("cont splash not enabled, phy enable required\n");
+	}
 	phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
 
 error:
@@ -838,6 +855,21 @@
 	return rc;
 }
 
+int dsi_phy_lane_reset(struct msm_dsi_phy *phy)
+{
+	int ret = 0;
+
+	if (!phy)
+		return ret;
+
+	mutex_lock(&phy->phy_lock);
+	if (phy->hw.ops.phy_lane_reset)
+		ret = phy->hw.ops.phy_lane_reset(&phy->hw);
+	mutex_unlock(&phy->phy_lock);
+
+	return ret;
+}
+
 /**
  * dsi_phy_disable() - disable DSI PHY hardware.
  * @phy:        DSI PHY handle.
@@ -876,6 +908,8 @@
 		return -EINVAL;
 	}
 
+	pr_debug("[%s] enable=%d\n", phy->name, enable);
+
 	mutex_lock(&phy->phy_lock);
 	if (enable) {
 		if (phy->hw.ops.phy_idle_on)
@@ -884,7 +918,17 @@
 		if (phy->hw.ops.regulator_enable)
 			phy->hw.ops.regulator_enable(&phy->hw,
 				&phy->cfg.regulators);
+
+		if (phy->hw.ops.enable)
+			phy->hw.ops.enable(&phy->hw, &phy->cfg);
+
+		phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
 	} else {
+		phy->dsi_phy_state = DSI_PHY_ENGINE_OFF;
+
+		if (phy->hw.ops.disable)
+			phy->hw.ops.disable(&phy->hw, &phy->cfg);
+
 		if (phy->hw.ops.phy_idle_off)
 			phy->hw.ops.phy_idle_off(&phy->hw);
 	}
@@ -894,6 +938,33 @@
 }
 
 /**
+ * dsi_phy_set_clk_freq() - set DSI PHY clock frequency setting
+ * @phy:          DSI PHY handle
+ * @clk_freq:     link clock frequency
+ *
+ * Return: error code.
+ */
+int dsi_phy_set_clk_freq(struct msm_dsi_phy *phy,
+		struct link_clk_freq *clk_freq)
+{
+	if (!phy || !clk_freq) {
+		pr_err("Invalid params\n");
+		return -EINVAL;
+	}
+
+	phy->regulator_required = clk_freq->byte_clk_rate >
+		(phy->regulator_min_datarate_bps / BITS_PER_BYTE);
+
+	pr_debug("[%s] lane_datarate=%u min_datarate=%u required=%d\n",
+			phy->name,
+			clk_freq->byte_clk_rate * BITS_PER_BYTE,
+			phy->regulator_min_datarate_bps,
+			phy->regulator_required);
+
+	return 0;
+}
+
+/**
  * dsi_phy_set_timing_params() - timing parameters for the panel
  * @phy:          DSI PHY handle
  * @timing:       array holding timing params.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index e721486..a158812 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -67,6 +67,9 @@
  * @mode:              Current mode.
  * @data_lanes:        Number of data lanes used.
  * @dst_format:        Destination format.
+ * @allow_phy_power_off: True if PHY is allowed to power off when idle
+ * @regulator_min_datarate_bps: Minimum per lane data rate to turn on regulator
+ * @regulator_required: True if phy regulator is required
  */
 struct msm_dsi_phy {
 	struct platform_device *pdev;
@@ -88,6 +91,10 @@
 	struct dsi_mode_info mode;
 	enum dsi_data_lanes data_lanes;
 	enum dsi_pixel_format dst_format;
+
+	bool allow_phy_power_off;
+	u32 regulator_min_datarate_bps;
+	bool regulator_required;
 };
 
 /**
@@ -159,6 +166,7 @@
  * @config:             DSI host configuration.
  * @pll_source:         Source PLL for PHY clock.
  * @skip_validation:    Validation will not be performed on parameters.
+ * @is_cont_splash_enabled:    check whether continuous splash enabled.
  *
  * Validates and enables DSI PHY.
  *
@@ -167,7 +175,8 @@
 int dsi_phy_enable(struct msm_dsi_phy *dsi_phy,
 		   struct dsi_host_config *config,
 		   enum dsi_phy_pll_source pll_source,
-		   bool skip_validation);
+		   bool skip_validation,
+		   bool is_cont_splash_enabled);
 
 /**
  * dsi_phy_disable() - disable DSI PHY hardware.
@@ -209,6 +218,16 @@
 int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable);
 
 /**
+ * dsi_phy_set_clk_freq() - set DSI PHY clock frequency setting
+ * @phy:          DSI PHY handle
+ * @clk_freq:     link clock frequency
+ *
+ * Return: error code.
+ */
+int dsi_phy_set_clk_freq(struct msm_dsi_phy *phy,
+		struct link_clk_freq *clk_freq);
+
+/**
  * dsi_phy_set_timing_params() - timing parameters for the panel
  * @phy:          DSI PHY handle
  * @timing:       array holding timing params.
@@ -223,6 +242,14 @@
 			      u32 *timing, u32 size);
 
 /**
+ * dsi_phy_lane_reset() - Reset DSI PHY lanes in case of error
+ * @phy:	DSI PHY handle
+ *
+ * Return: error code.
+ */
+int dsi_phy_lane_reset(struct msm_dsi_phy *phy);
+
+/**
  * dsi_phy_drv_register() - register platform driver for dsi phy
  */
 void dsi_phy_drv_register(void);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index 51c2f46..efebd99 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -233,6 +233,13 @@
 	int (*phy_timing_val)(struct dsi_phy_per_lane_cfgs *timing_val,
 				u32 *timing, u32 size);
 
+	/**
+	 * phy_lane_reset() - Reset dsi phy lanes in case of error.
+	 * @phy:      Pointer to DSI PHY hardware object.
+	 * Return:    error code.
+	 */
+	int (*phy_lane_reset)(struct dsi_phy_hw *phy);
+
 	void *timing_ops;
 	struct phy_ulps_config_ops ulps_ops;
 };
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
index 371239d..8d91141 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v3_0.c
@@ -17,6 +17,7 @@
 #include <linux/iopoll.h>
 #include "dsi_hw.h"
 #include "dsi_phy_hw.h"
+#include "dsi_catalog.h"
 
 #define DSIPHY_CMN_CLK_CFG0						0x010
 #define DSIPHY_CMN_CLK_CFG1						0x014
@@ -373,6 +374,29 @@
 		 lanes);
 }
 
+int dsi_phy_hw_v3_0_lane_reset(struct dsi_phy_hw *phy)
+{
+	int ret = 0, loop = 10, u_dly = 200;
+	u32 ln_status = 0;
+
+	while ((ln_status != 0x1f) && loop) {
+		DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, 0x1f);
+		wmb(); /* ensure register is committed */
+		loop--;
+		udelay(u_dly);
+		ln_status = DSI_R32(phy, DSIPHY_CMN_LANE_STATUS1);
+		pr_debug("trial no: %d\n", loop);
+	}
+
+	if (!loop)
+		pr_debug("could not reset phy lanes\n");
+
+	DSI_W32(phy, DSIPHY_CMN_LANE_CTRL3, 0x0);
+	wmb(); /* ensure register is committed */
+
+	return ret;
+}
+
 void dsi_phy_hw_v3_0_ulps_exit(struct dsi_phy_hw *phy,
 			struct dsi_phy_cfg *cfg, u32 lanes)
 {
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index 0c1bcf6..95bdc36 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -16,6 +16,9 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/msm_drm_notify.h>
+#include <linux/notifier.h>
+
 #include "msm_drv.h"
 #include "msm_kms.h"
 #include "msm_gem.h"
@@ -30,6 +33,47 @@
 	struct kthread_work commit_work;
 };
 
+static BLOCKING_NOTIFIER_HEAD(msm_drm_notifier_list);
+
+/**
+ * msm_drm_register_client - register a client notifier
+ * @nb: notifier block to callback on events
+ *
+ * This function registers a notifier callback function
+ * to msm_drm_notifier_list, which would be called when
+ * received unblank/power down event.
+ */
+int msm_drm_register_client(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_register(&msm_drm_notifier_list,
+						nb);
+}
+
+/**
+ * msm_drm_unregister_client - unregister a client notifier
+ * @nb: notifier block to callback on events
+ *
+ * This function unregisters the callback function from
+ * msm_drm_notifier_list.
+ */
+int msm_drm_unregister_client(struct notifier_block *nb)
+{
+	return blocking_notifier_chain_unregister(&msm_drm_notifier_list,
+						  nb);
+}
+
+/**
+ * msm_drm_notifier_call_chain - notify clients of drm_events
+ * @val: event MSM_DRM_EARLY_EVENT_BLANK or MSM_DRM_EVENT_BLANK
+ * @v: notifier data, inculde display id and display blank
+ *     event(unblank or power down).
+ */
+static int msm_drm_notifier_call_chain(unsigned long val, void *v)
+{
+	return blocking_notifier_call_chain(&msm_drm_notifier_list, val,
+					    v);
+}
+
 /* block until specified crtcs are no longer pending update, and
  * atomically mark them as pending update
  */
@@ -97,7 +141,8 @@
 	struct drm_connector_state *old_conn_state;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
-	int i;
+	struct msm_drm_notifier notifier_data;
+	int i, blank;
 
 	SDE_ATRACE_BEGIN("msm_disable");
 	for_each_connector_in_state(old_state, connector, old_conn_state, i) {
@@ -130,11 +175,13 @@
 			continue;
 
 		if (msm_is_mode_seamless(
-				&connector->encoder->crtc->state->mode))
+			&connector->encoder->crtc->state->mode) ||
+			msm_is_mode_seamless_vrr(
+			&connector->encoder->crtc->state->adjusted_mode))
 			continue;
 
 		if (msm_is_mode_seamless_dms(
-			       &connector->encoder->crtc->state->adjusted_mode))
+			&connector->encoder->crtc->state->adjusted_mode))
 			continue;
 
 		funcs = encoder->helper_private;
@@ -142,6 +189,11 @@
 		DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n",
 				 encoder->base.id, encoder->name);
 
+		blank = MSM_DRM_BLANK_POWERDOWN;
+		notifier_data.data = &blank;
+		notifier_data.id = crtc_idx;
+		msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
+					     &notifier_data);
 		/*
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call disable hooks twice.
@@ -157,6 +209,8 @@
 			funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
 
 		drm_bridge_post_disable(encoder->bridge);
+		msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
+					    &notifier_data);
 	}
 
 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -169,7 +223,8 @@
 		if (!old_crtc_state->active)
 			continue;
 
-		if (msm_is_mode_seamless(&crtc->state->mode))
+		if (msm_is_mode_seamless(&crtc->state->mode) ||
+			msm_is_mode_seamless_vrr(&crtc->state->adjusted_mode))
 			continue;
 
 		if (msm_is_mode_seamless_dms(&crtc->state->adjusted_mode))
@@ -293,10 +348,11 @@
 	struct drm_crtc_state *old_crtc_state;
 	struct drm_connector *connector;
 	struct drm_connector_state *old_conn_state;
+	struct msm_drm_notifier notifier_data;
 	struct msm_drm_private *priv = dev->dev_private;
 	struct msm_kms *kms = priv->kms;
 	int bridge_enable_count = 0;
-	int i;
+	int i, blank;
 
 	SDE_ATRACE_BEGIN("msm_enable");
 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
@@ -309,7 +365,8 @@
 		if (!crtc->state->active)
 			continue;
 
-		if (msm_is_mode_seamless(&crtc->state->mode))
+		if (msm_is_mode_seamless(&crtc->state->mode) ||
+			msm_is_mode_seamless_vrr(&crtc->state->adjusted_mode))
 			continue;
 
 		funcs = crtc->helper_private;
@@ -346,6 +403,12 @@
 		DRM_DEBUG_ATOMIC("enabling [ENCODER:%d:%s]\n",
 				 encoder->base.id, encoder->name);
 
+		blank = MSM_DRM_BLANK_UNBLANK;
+		notifier_data.data = &blank;
+		notifier_data.id =
+			connector->state->crtc->index;
+		msm_drm_notifier_call_chain(MSM_DRM_EARLY_EVENT_BLANK,
+					    &notifier_data);
 		/*
 		 * Each encoder has at most one connector (since we always steal
 		 * it away), so we won't call enable hooks twice.
@@ -387,6 +450,8 @@
 				 encoder->base.id, encoder->name);
 
 		drm_bridge_enable(encoder->bridge);
+		msm_drm_notifier_call_chain(MSM_DRM_EVENT_BLANK,
+					    &notifier_data);
 	}
 	SDE_ATRACE_END("msm_enable");
 }
@@ -476,6 +541,10 @@
 	struct drm_crtc *crtc = NULL;
 	struct drm_crtc_state *crtc_state = NULL;
 	int ret = -EINVAL, i = 0, j = 0;
+	bool nonblock;
+
+	/* cache since work will kfree commit in non-blocking case */
+	nonblock = commit->nonblock;
 
 	for_each_crtc_in_state(state, crtc, crtc_state, i) {
 		for (j = 0; j < priv->num_crtcs; j++) {
@@ -515,10 +584,13 @@
 		 */
 		DRM_ERROR("failed to dispatch commit to any CRTC\n");
 		complete_commit(commit);
-	} else if (!commit->nonblock) {
+	} else if (!nonblock) {
 		kthread_flush_work(&commit->commit_work);
-		kfree(commit);
 	}
+
+	/* free nonblocking commits in this context, after processing */
+	if (!nonblock)
+		kfree(commit);
 }
 
 /**
@@ -544,6 +616,11 @@
 	struct drm_plane_state *plane_state;
 	int i, ret;
 
+	if (!priv || priv->shutdown_in_progress) {
+		DRM_ERROR("priv is null or shutdwon is in-progress\n");
+		return -EINVAL;
+	}
+
 	SDE_ATRACE_BEGIN("atomic_commit");
 	ret = drm_atomic_helper_prepare_planes(dev, state);
 	if (ret) {
@@ -599,8 +676,7 @@
 	 * are dispatched to work queues, so that the fence preparation is
 	 * finished before the .atomic_commit returns.
 	 */
-	if (priv && priv->kms && priv->kms->funcs &&
-			priv->kms->funcs->prepare_fence)
+	if (priv->kms && priv->kms->funcs && priv->kms->funcs->prepare_fence)
 		priv->kms->funcs->prepare_fence(priv->kms, state);
 
 	/*
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 5e47daf..33778f8e 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -57,53 +57,6 @@
 #define MSM_VERSION_MINOR	2
 #define MSM_VERSION_PATCHLEVEL	0
 
-#define TEARDOWN_DEADLOCK_RETRY_MAX 5
-#define HPD_STRING_SIZE 30
-
-static void msm_drm_helper_hotplug_event(struct drm_device *dev)
-{
-	struct drm_connector *connector;
-	char name[HPD_STRING_SIZE], status[HPD_STRING_SIZE];
-	char const *connector_name;
-	char *envp[3];
-
-	if (!dev) {
-		DRM_ERROR("hotplug_event failed, invalid input\n");
-		return;
-	}
-
-	if (!dev->mode_config.poll_enabled)
-		return;
-
-	mutex_lock(&dev->mode_config.mutex);
-	drm_for_each_connector(connector, dev) {
-		/* Only handle HPD capable connectors. */
-		if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
-			continue;
-
-		connector->status = connector->funcs->detect(connector, false);
-
-		if (connector->name)
-			connector_name = connector->name;
-		else
-			connector_name = "unknown";
-
-		snprintf(name, HPD_STRING_SIZE, "name=%s", connector_name);
-
-		snprintf(status, HPD_STRING_SIZE, "status=%s",
-			drm_get_connector_status_name(connector->status));
-
-		DRM_DEBUG("generating hotplug event [%s]: [%s]\n",
-			name, status);
-		envp[0] = name;
-		envp[1] = status;
-		envp[2] = NULL;
-		kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE,
-				envp);
-	}
-	mutex_unlock(&dev->mode_config.mutex);
-}
-
 static void msm_fb_output_poll_changed(struct drm_device *dev)
 {
 	struct msm_drm_private *priv = NULL;
@@ -117,8 +70,6 @@
 
 	if (priv->fbdev)
 		drm_fb_helper_hotplug_event(priv->fbdev);
-	else
-		msm_drm_helper_hotplug_event(dev);
 }
 
 /**
@@ -264,12 +215,16 @@
 	struct msm_kms *kms = priv->kms;
 	struct vblank_event *vbl_ev, *tmp;
 	unsigned long flags;
+	LIST_HEAD(tmp_head);
 
 	spin_lock_irqsave(&vbl_ctrl->lock, flags);
 	list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
 		list_del(&vbl_ev->node);
-		spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+		list_add_tail(&vbl_ev->node, &tmp_head);
+	}
+	spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
 
+	list_for_each_entry_safe(vbl_ev, tmp, &tmp_head, node) {
 		if (vbl_ev->enable)
 			kms->funcs->enable_vblank(kms,
 						priv->crtcs[vbl_ev->crtc_id]);
@@ -278,11 +233,7 @@
 						priv->crtcs[vbl_ev->crtc_id]);
 
 		kfree(vbl_ev);
-
-		spin_lock_irqsave(&vbl_ctrl->lock, flags);
 	}
-
-	spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
 }
 
 static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
@@ -740,6 +691,14 @@
 
 	drm_mode_config_reset(ddev);
 
+	if (kms && kms->funcs && kms->funcs->cont_splash_config) {
+		ret = kms->funcs->cont_splash_config(kms);
+		if (ret) {
+			dev_err(dev, "kms cont_splash config failed.\n");
+			goto fail;
+		}
+	}
+
 #ifdef CONFIG_DRM_FBDEV_EMULATION
 	if (fbdev)
 		priv->fbdev = msm_fbdev_init(ddev);
@@ -986,9 +945,9 @@
 	} else {
 		drm_modeset_lock_all(dev);
 		msm_disable_all_modes(dev);
-		drm_modeset_unlock_all(dev);
 		if (kms && kms->funcs && kms->funcs->lastclose)
 			kms->funcs->lastclose(kms);
+		drm_modeset_unlock_all(dev);
 	}
 }
 
@@ -1414,9 +1373,11 @@
 		if (node->event.type != event->type ||
 			obj->id != node->info.object_id)
 			continue;
-		len = event->length + sizeof(struct drm_msm_event_resp);
+		len = event->length + sizeof(struct msm_drm_event);
 		if (node->base.file_priv->event_space < len) {
-			DRM_ERROR("Insufficient space to notify\n");
+			DRM_ERROR("Insufficient space %d for event %x len %d\n",
+				node->base.file_priv->event_space, event->type,
+				len);
 			continue;
 		}
 		notify = kzalloc(len, GFP_ATOMIC);
@@ -1426,7 +1387,8 @@
 		notify->base.event = &notify->event;
 		notify->base.pid = node->base.pid;
 		notify->event.type = node->event.type;
-		notify->event.length = len;
+		notify->event.length = event->length +
+					sizeof(struct drm_msm_event_resp);
 		memcpy(&notify->info, &node->info, sizeof(notify->info));
 		memcpy(notify->data, payload, event->length);
 		ret = drm_event_reserve_init_locked(dev, node->base.file_priv,
@@ -1813,6 +1775,14 @@
 		struct device_node *np = dev->of_node;
 		unsigned int i;
 
+		for (i = 0; ; i++) {
+			node = of_parse_phandle(np, "connectors", i);
+			if (!node)
+				break;
+
+			component_match_add(dev, matchptr, compare_of, node);
+		}
+
 		for (i = 0; i < MAX_DSI_ACTIVE_DISPLAY; i++) {
 			node = dsi_display_get_boot_display(i);
 
@@ -1824,13 +1794,6 @@
 			}
 		}
 
-		for (i = 0; ; i++) {
-			node = of_parse_phandle(np, "connectors", i);
-			if (!node)
-				break;
-
-			component_match_add(dev, matchptr, compare_of, node);
-		}
 		return 0;
 	}
 
@@ -1975,6 +1938,28 @@
 	return 0;
 }
 
+static void msm_pdev_shutdown(struct platform_device *pdev)
+{
+	struct drm_device *ddev = platform_get_drvdata(pdev);
+	struct msm_drm_private *priv = NULL;
+
+	if (!ddev) {
+		DRM_ERROR("invalid drm device node\n");
+		return;
+	}
+
+	priv = ddev->dev_private;
+	if (!priv) {
+		DRM_ERROR("invalid msm drm private node\n");
+		return;
+	}
+
+	msm_lastclose(ddev);
+
+	/* set this after lastclose to allow kickoff from lastclose */
+	priv->shutdown_in_progress = true;
+}
+
 static const struct of_device_id dt_match[] = {
 	{ .compatible = "qcom,mdp4", .data = (void *)4 },	/* MDP4 */
 	{ .compatible = "qcom,mdss", .data = (void *)5 },	/* MDP5 MDSS */
@@ -1986,6 +1971,7 @@
 static struct platform_driver msm_platform_driver = {
 	.probe      = msm_pdev_probe,
 	.remove     = msm_pdev_remove,
+	.shutdown   = msm_pdev_shutdown,
 	.driver     = {
 		.name   = "msm_drm",
 		.of_match_table = dt_match,
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 12d2d15..e5c3082 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -71,6 +71,8 @@
 #define MAX_BRIDGES    8
 #define MAX_CONNECTORS 8
 
+#define TEARDOWN_DEADLOCK_RETRY_MAX 5
+
 struct msm_file_private {
 	/* currently we don't do anything useful with this.. but when
 	 * per-context address spaces are supported we'd keep track of
@@ -81,8 +83,6 @@
 
 enum msm_mdp_plane_property {
 	/* blob properties, always put these first */
-	PLANE_PROP_SCALER_V1,
-	PLANE_PROP_SCALER_V2,
 	PLANE_PROP_CSC_V1,
 	PLANE_PROP_INFO,
 	PLANE_PROP_SCALER_LUT_ED,
@@ -114,12 +114,15 @@
 	PLANE_PROP_ROT_DST_H,
 	PLANE_PROP_PREFILL_SIZE,
 	PLANE_PROP_PREFILL_TIME,
+	PLANE_PROP_SCALER_V1,
+	PLANE_PROP_SCALER_V2,
 
 	/* enum/bitmask properties */
 	PLANE_PROP_ROTATION,
 	PLANE_PROP_BLEND_OP,
 	PLANE_PROP_SRC_CONFIG,
 	PLANE_PROP_FB_TRANSLATION_MODE,
+	PLANE_PROP_MULTIRECT_MODE,
 
 	/* total # of properties */
 	PLANE_PROP_COUNT
@@ -127,6 +130,9 @@
 
 enum msm_mdp_crtc_property {
 	CRTC_PROP_INFO,
+	CRTC_PROP_DEST_SCALER_LUT_ED,
+	CRTC_PROP_DEST_SCALER_LUT_CIR,
+	CRTC_PROP_DEST_SCALER_LUT_SEP,
 
 	/* # of blob properties */
 	CRTC_PROP_BLOBCOUNT,
@@ -148,6 +154,7 @@
 	CRTC_PROP_ROI_V1,
 	CRTC_PROP_SECURITY_LEVEL,
 	CRTC_PROP_IDLE_TIMEOUT,
+	CRTC_PROP_DEST_SCALER,
 
 	/* total # of properties */
 	CRTC_PROP_COUNT
@@ -156,8 +163,11 @@
 enum msm_mdp_conn_property {
 	/* blob properties, always put these first */
 	CONNECTOR_PROP_SDE_INFO,
+	CONNECTOR_PROP_MODE_INFO,
 	CONNECTOR_PROP_HDR_INFO,
+	CONNECTOR_PROP_EXT_HDR_INFO,
 	CONNECTOR_PROP_PP_DITHER,
+	CONNECTOR_PROP_HDR_METADATA,
 
 	/* # of blob properties */
 	CONNECTOR_PROP_BLOBCOUNT,
@@ -208,12 +218,14 @@
  * @MSM_DISPLAY_CAP_CMD_MODE:           Command mode supported
  * @MSM_DISPLAY_CAP_HOT_PLUG:           Hot plug detection supported
  * @MSM_DISPLAY_CAP_EDID:               EDID supported
+ * @MSM_DISPLAY_ESD_ENABLED:            ESD feature enabled
  */
 enum msm_display_caps {
 	MSM_DISPLAY_CAP_VID_MODE	= BIT(0),
 	MSM_DISPLAY_CAP_CMD_MODE	= BIT(1),
 	MSM_DISPLAY_CAP_HOT_PLUG	= BIT(2),
 	MSM_DISPLAY_CAP_EDID		= BIT(3),
+	MSM_DISPLAY_ESD_ENABLED		= BIT(4),
 };
 
 /**
@@ -221,11 +233,13 @@
  * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW
  * @MSM_ENC_TX_COMPLETE - wait for the HW to transfer the frame to panel
  * @MSM_ENC_VBLANK - wait for the HW VBLANK event (for driver-internal waiters)
+ * @MSM_ENC_ACTIVE_REGION - wait for the TG to be in active pixel region
  */
 enum msm_event_wait {
 	MSM_ENC_COMMIT_DONE = 0,
 	MSM_ENC_TX_COMPLETE,
 	MSM_ENC_VBLANK,
+	MSM_ENC_ACTIVE_REGION,
 };
 
 /**
@@ -398,8 +412,10 @@
  * @prefill_lines:   prefill lines based on porches.
  * @jitter_numer:	display panel jitter numerator configuration
  * @jitter_denom:	display panel jitter denominator configuration
+ * @clk_rate:	     DSI bit clock per lane in HZ.
  * @topology:        supported topology for the mode
  * @comp_info:       compression info supported
+ * @roi_caps:        panel roi capabilities
  */
 struct msm_mode_info {
 	uint32_t frame_rate;
@@ -407,8 +423,10 @@
 	uint32_t prefill_lines;
 	uint32_t jitter_numer;
 	uint32_t jitter_denom;
+	uint64_t clk_rate;
 	struct msm_display_topology topology;
 	struct msm_compression_info comp_info;
+	struct msm_roi_caps roi_caps;
 };
 
 /**
@@ -425,6 +443,7 @@
  *                      this is max width supported by controller
  * @max_height:         Max height of display. In case of hot pluggable display
  *                      this is max height supported by controller
+ * @clk_rate:           DSI bit clock per lane in HZ.
  * @is_primary:         Set to true if display is primary display
  * @is_te_using_watchdog_timer:  Boolean to indicate watchdog TE is
  *				 used instead of panel TE in cmd mode panels
@@ -444,6 +463,7 @@
 
 	uint32_t max_width;
 	uint32_t max_height;
+	uint64_t clk_rate;
 
 	bool is_primary;
 	bool is_te_using_watchdog_timer;
@@ -468,6 +488,7 @@
  */
 struct msm_display_kickoff_params {
 	struct msm_roi_list *rois;
+	struct drm_msm_ext_hdr_metadata *hdr_meta;
 };
 
 /**
@@ -603,6 +624,9 @@
 
 	/* msm drv debug root node */
 	struct dentry *debug_root;
+
+	/* update the flag when msm driver receives shutdown notification */
+	bool shutdown_in_progress;
 };
 
 /* get struct msm_kms * from drm_device * */
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index f5cdf64..e8bf244 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -42,15 +42,31 @@
 		struct drm_file *file_priv,
 		unsigned int *handle)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+	struct msm_framebuffer *msm_fb;
+
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return -EINVAL;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+
 	return drm_gem_handle_create(file_priv,
 			msm_fb->planes[0], handle);
 }
 
 static void msm_framebuffer_destroy(struct drm_framebuffer *fb)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int i, n;
+
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 
 	DBG("destroy: FB ID: %d (%p)", fb->base.id, fb);
 
@@ -73,9 +89,16 @@
 #ifdef CONFIG_DEBUG_FS
 void msm_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int i, n;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 	seq_printf(m, "fb: %dx%d@%4.4s (%2d, ID:%d)\n",
 			fb->width, fb->height, (char *)&fb->pixel_format,
 			drm_framebuffer_read_refcount(fb), fb->base.id);
@@ -90,8 +113,14 @@
 
 void msm_framebuffer_set_kmap(struct drm_framebuffer *fb, bool enable)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+	struct msm_framebuffer *msm_fb;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
 	if (enable)
 		msm_fb->flags |= MSM_FRAMEBUFFER_FLAG_KMAP;
 	else
@@ -100,10 +129,17 @@
 
 static int msm_framebuffer_kmap(struct drm_framebuffer *fb)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int i, n;
 	struct drm_gem_object *bo;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return -EINVAL;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 	if (atomic_inc_return(&msm_fb->kmap_count) > 1)
 		return 0;
 
@@ -124,10 +160,17 @@
 
 static void msm_framebuffer_kunmap(struct drm_framebuffer *fb)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int i, n;
 	struct drm_gem_object *bo;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 	if (atomic_dec_return(&msm_fb->kmap_count) > 0)
 		return;
 
@@ -151,10 +194,17 @@
 int msm_framebuffer_prepare(struct drm_framebuffer *fb,
 		struct msm_gem_address_space *aspace)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int ret, i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int ret, i, n;
 	uint32_t iova;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return -EINVAL;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 	for (i = 0; i < n; i++) {
 		ret = msm_gem_get_iova(msm_fb->planes[i], aspace, &iova);
 		DBG("FB[%u]: iova[%d]: %08x (%d)", fb->base.id, i, iova, ret);
@@ -171,8 +221,16 @@
 void msm_framebuffer_cleanup(struct drm_framebuffer *fb,
 		struct msm_gem_address_space *aspace)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
-	int i, n = drm_format_num_planes(fb->pixel_format);
+	struct msm_framebuffer *msm_fb;
+	int i, n;
+
+	if (fb == NULL) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
+	n = drm_format_num_planes(fb->pixel_format);
 
 	if (msm_fb->flags & MSM_FRAMEBUFFER_FLAG_KMAP)
 		msm_framebuffer_kunmap(fb);
@@ -184,7 +242,14 @@
 uint32_t msm_framebuffer_iova(struct drm_framebuffer *fb,
 		struct msm_gem_address_space *aspace, int plane)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+	struct msm_framebuffer *msm_fb;
+
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return -EINVAL;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
 	if (!msm_fb->planes[plane])
 		return 0;
 	return msm_gem_iova(msm_fb->planes[plane], aspace) + fb->offsets[plane];
@@ -193,9 +258,15 @@
 uint32_t msm_framebuffer_phys(struct drm_framebuffer *fb,
 		int plane)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+	struct msm_framebuffer *msm_fb;
 	dma_addr_t phys_addr;
 
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return -EINVAL;
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
 	if (!msm_fb->planes[plane])
 		return 0;
 
@@ -208,7 +279,14 @@
 
 struct drm_gem_object *msm_framebuffer_bo(struct drm_framebuffer *fb, int plane)
 {
-	struct msm_framebuffer *msm_fb = to_msm_framebuffer(fb);
+	struct msm_framebuffer *msm_fb;
+
+	if (!fb) {
+		DRM_ERROR("from:%pS null fb\n", __builtin_return_address(0));
+		return ERR_PTR(-EINVAL);
+	}
+
+	msm_fb = to_msm_framebuffer(fb);
 	return msm_fb->planes[plane];
 }
 
diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c
index b829460..f8c3df5 100644
--- a/drivers/gpu/drm/msm/msm_gem.c
+++ b/drivers/gpu/drm/msm/msm_gem.c
@@ -311,8 +311,7 @@
 	}
 }
 
-static void
-put_iova(struct drm_gem_object *obj)
+static void put_iova(struct drm_gem_object *obj)
 {
 	struct drm_device *dev = obj->dev;
 	struct msm_gem_object *msm_obj = to_msm_bo(obj);
@@ -324,12 +323,14 @@
 		if (iommu_present(&platform_bus_type)) {
 			msm_gem_unmap_vma(domain->aspace, domain,
 				msm_obj->sgt, get_dmabuf_ptr(obj));
-
-			msm_gem_remove_obj_from_aspace_active_list(
-					domain->aspace,
-					obj);
 		}
 
+		/*
+		 * put_iova removes the domain connected to the obj which makes
+		 * the aspace inaccessible. Store the aspace, as it is used to
+		 * update the active_list during gem_free_obj and gem_purege.
+		 */
+		msm_obj->aspace = domain->aspace;
 		obj_remove_domain(domain);
 	}
 }
@@ -409,7 +410,8 @@
 
 	if (!ret && domain) {
 		*iova = domain->iova;
-		msm_gem_add_obj_to_aspace_active_list(aspace, obj);
+		if (aspace && aspace->domain_attached)
+			msm_gem_add_obj_to_aspace_active_list(aspace, obj);
 	} else {
 		obj_remove_domain(domain);
 	}
@@ -485,24 +487,21 @@
 		/**
 		 * Unmap active buffers,
 		 * typically clients should do this when the callback is called,
-		 * but this needs to be done for the framebuffers which are not
-		 * attached to any planes. (background apps)
+		 * but this needs to be done for the buffers which are not
+		 * attached to any planes.
 		 */
 		list_for_each_entry(msm_obj, &aspace->active_list, iova_list) {
 			obj = &msm_obj->base;
-			if (obj->import_attach) {
+			if (obj->import_attach)
 				put_iova(obj);
-				put_pages(obj);
-			}
 		}
 	} else {
 		/* map active buffers */
-		list_for_each_entry(msm_obj, &aspace->active_list,
-				iova_list) {
+		list_for_each_entry(msm_obj, &aspace->active_list, iova_list) {
 			obj = &msm_obj->base;
 			ret = msm_gem_get_iova_locked(obj, aspace, &iova);
 			if (ret) {
-				mutex_unlock(&obj->dev->struct_mutex);
+				mutex_unlock(&aspace->dev->struct_mutex);
 				return;
 			}
 		}
@@ -613,6 +612,7 @@
 	WARN_ON(obj->import_attach);
 
 	put_iova(obj);
+	msm_gem_remove_obj_from_aspace_active_list(msm_obj->aspace, obj);
 
 	msm_gem_vunmap(obj);
 
@@ -841,6 +841,7 @@
 	list_del(&msm_obj->mm_list);
 
 	put_iova(obj);
+	msm_gem_remove_obj_from_aspace_active_list(msm_obj->aspace, obj);
 
 	if (obj->import_attach) {
 		if (msm_obj->vaddr)
@@ -913,6 +914,8 @@
 		return -EINVAL;
 	}
 
+	WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
 	if (!iommu_present(&platform_bus_type))
 		use_vram = true;
 	else if ((flags & MSM_BO_STOLEN) && priv->vram.size)
@@ -946,6 +949,7 @@
 	INIT_LIST_HEAD(&msm_obj->submit_entry);
 	INIT_LIST_HEAD(&msm_obj->domains);
 	INIT_LIST_HEAD(&msm_obj->iova_list);
+	msm_obj->aspace = NULL;
 
 	list_add_tail(&msm_obj->mm_list, &priv->inactive_list);
 
@@ -987,7 +991,7 @@
 		struct dma_buf *dmabuf, struct sg_table *sgt)
 {
 	struct msm_gem_object *msm_obj;
-	struct drm_gem_object *obj;
+	struct drm_gem_object *obj = NULL;
 	uint32_t size;
 	int ret, npages;
 
@@ -999,7 +1003,12 @@
 
 	size = PAGE_ALIGN(dmabuf->size);
 
+	ret = mutex_lock_interruptible(&dev->struct_mutex);
+	if (ret)
+		return ERR_PTR(ret);
+
 	ret = msm_gem_new_impl(dev, size, MSM_BO_WC, dmabuf->resv, &obj);
+	mutex_unlock(&dev->struct_mutex);
 	if (ret)
 		goto fail;
 
diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h
index c50c453..8521bea 100644
--- a/drivers/gpu/drm/msm/msm_gem.h
+++ b/drivers/gpu/drm/msm/msm_gem.h
@@ -122,6 +122,8 @@
 	 */
 	struct drm_mm_node *vram_node;
 	struct list_head iova_list;
+
+	struct msm_gem_address_space *aspace;
 };
 #define to_msm_bo(x) container_of(x, struct msm_gem_object, base)
 
diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c
index cc75fb5..2581caf 100644
--- a/drivers/gpu/drm/msm/msm_gem_submit.c
+++ b/drivers/gpu/drm/msm/msm_gem_submit.c
@@ -34,8 +34,8 @@
 		struct msm_gpu *gpu, uint32_t nr_bos, uint32_t nr_cmds)
 {
 	struct msm_gem_submit *submit;
-	uint64_t sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) +
-		(nr_cmds * sizeof(submit->cmd[0]));
+	uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) +
+		((u64)nr_cmds * sizeof(submit->cmd[0]));
 
 	if (sz > SIZE_MAX)
 		return NULL;
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 76523c7..db9e7ee 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -36,6 +36,8 @@
 #define MSM_MODE_FLAG_VBLANK_PRE_MODESET	(1<<1)
 /* Request to switch the connector mode */
 #define MSM_MODE_FLAG_SEAMLESS_DMS			(1<<2)
+/* Request to switch the fps */
+#define MSM_MODE_FLAG_SEAMLESS_VRR			(1<<3)
 
 /* As there are different display controller blocks depending on the
  * snapdragon version, the kms support is split out and the appropriate
@@ -102,6 +104,8 @@
 	struct msm_gem_address_space *(*get_address_space)(
 			struct msm_kms *kms,
 			unsigned int domain);
+	/* handle continuous splash  */
+	int (*cont_splash_config)(struct msm_kms *kms);
 };
 
 struct msm_kms {
@@ -163,6 +167,12 @@
 		(mode->private_flags & MSM_MODE_FLAG_SEAMLESS_DYNAMIC_FPS));
 }
 
+static inline bool msm_is_mode_seamless_vrr(const struct drm_display_mode *mode)
+{
+	return mode ? (mode->private_flags & MSM_MODE_FLAG_SEAMLESS_VRR)
+		: false;
+}
+
 static inline bool msm_needs_vblank_pre_modeset(
 		const struct drm_display_mode *mode)
 {
diff --git a/drivers/gpu/drm/msm/msm_prop.c b/drivers/gpu/drm/msm/msm_prop.c
index d1991a4..ce84b7a 100644
--- a/drivers/gpu/drm/msm/msm_prop.c
+++ b/drivers/gpu/drm/msm/msm_prop.c
@@ -269,9 +269,16 @@
 		info->property_data[property_idx].default_value = 0;
 		info->property_data[property_idx].force_dirty = false;
 
+		/* select first defined value for enums */
+		if (!is_bitmask)
+			info->property_data[property_idx].default_value =
+				values->type;
+
 		/* always attach property, if created */
 		if (*prop) {
-			drm_object_attach_property(info->base, *prop, 0);
+			drm_object_attach_property(info->base, *prop,
+					info->property_data
+					[property_idx].default_value);
 			++info->install_count;
 		}
 	}
@@ -386,20 +393,25 @@
 	struct drm_property_blob *blob;
 	int property_idx, rc = -EINVAL;
 
+	if (!info || !property_state) {
+		DRM_ERROR("invalid argument(s)\n");
+		return -EINVAL;
+	}
+
 	property_idx = msm_property_index(info, property);
-	if (!info || !property_state ||
-			(property_idx == -EINVAL) || !property_state->values) {
-		DRM_DEBUG("invalid argument(s)\n");
+	if ((property_idx == -EINVAL) || !property_state->values) {
+		DRM_ERROR("invalid argument(s)\n");
 	} else {
 		/* extra handling for incoming properties */
 		mutex_lock(&info->property_lock);
-		if ((property->flags & DRM_MODE_PROP_BLOB) &&
+		if (val && (property->flags & DRM_MODE_PROP_BLOB) &&
 			(property_idx < info->blob_count)) {
 			/* DRM lookup also takes a reference */
 			blob = drm_property_lookup_blob(info->dev,
 				(uint32_t)val);
 			if (!blob) {
-				DRM_ERROR("blob not found\n");
+				DRM_ERROR("prop %d blob id 0x%llx not found\n",
+						property_idx, val);
 				val = 0;
 			} else {
 				DBG("Blob %u saved", blob->base.id);
diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c
index 730fc06..7c879651 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -33,6 +33,10 @@
 #define SZ_4G	(((size_t) SZ_1G) * 4)
 #endif
 
+#ifndef SZ_2G
+#define SZ_2G	(((size_t) SZ_1G) * 2)
+#endif
+
 struct msm_smmu_client {
 	struct device *dev;
 	struct dma_iommu_mapping *mmu_mapping;
@@ -300,26 +304,26 @@
 static struct msm_smmu_domain msm_smmu_domains[MSM_SMMU_DOMAIN_MAX] = {
 	[MSM_SMMU_DOMAIN_UNSECURE] = {
 		.label = "mdp_ns",
-		.va_start = SZ_128K,
-		.va_size = SZ_4G - SZ_128K,
+		.va_start = SZ_2G,
+		.va_size = SZ_4G - SZ_2G,
 		.secure = false,
 	},
 	[MSM_SMMU_DOMAIN_SECURE] = {
 		.label = "mdp_s",
-		.va_start = SZ_128K,
-		.va_size = SZ_4G - SZ_128K,
+		.va_start = SZ_2G,
+		.va_size = SZ_4G - SZ_2G,
 		.secure = true,
 	},
 	[MSM_SMMU_DOMAIN_NRT_UNSECURE] = {
 		.label = "rot_ns",
-		.va_start = SZ_128K,
-		.va_size = SZ_4G - SZ_128K,
+		.va_start = SZ_2G,
+		.va_size = SZ_4G - SZ_2G,
 		.secure = false,
 	},
 	[MSM_SMMU_DOMAIN_NRT_SECURE] = {
 		.label = "rot_s",
-		.va_start = SZ_128K,
-		.va_size = SZ_4G - SZ_128K,
+		.va_start = SZ_2G,
+		.va_size = SZ_4G - SZ_2G,
 		.secure = true,
 	},
 };
@@ -455,6 +459,7 @@
 	const struct msm_smmu_domain *domain)
 {
 	int rc;
+	int mdphtw_llc_enable = 1;
 
 	client->mmu_mapping = arm_iommu_create_mapping(&platform_bus_type,
 			domain->va_start, domain->va_size);
@@ -465,6 +470,14 @@
 		return PTR_ERR(client->mmu_mapping);
 	}
 
+	rc = iommu_domain_set_attr(client->mmu_mapping->domain,
+			DOMAIN_ATTR_USE_UPSTREAM_HINT, &mdphtw_llc_enable);
+	if (rc) {
+		dev_err(client->dev, "couldn't enable mdp pagetable walks: %d\n",
+			rc);
+		goto error;
+	}
+
 	if (domain->secure) {
 		int secure_vmid = VMID_CP_PIXEL;
 
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 9409066..c2419dc 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -52,6 +52,10 @@
 
 static void dspp_hsic_install_property(struct drm_crtc *crtc);
 
+static void dspp_memcolor_install_property(struct drm_crtc *crtc);
+
+static void dspp_sixzone_install_property(struct drm_crtc *crtc);
+
 static void dspp_ad_install_property(struct drm_crtc *crtc);
 
 static void dspp_vlut_install_property(struct drm_crtc *crtc);
@@ -62,6 +66,8 @@
 
 static void dspp_igc_install_property(struct drm_crtc *crtc);
 
+static void dspp_hist_install_property(struct drm_crtc *crtc);
+
 typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);
 
 static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -77,15 +83,20 @@
 static void sde_cp_ad_set_prop(struct sde_crtc *sde_crtc,
 		enum ad_property ad_prop);
 
+static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg);
+
 #define setup_dspp_prop_install_funcs(func) \
 do { \
 	func[SDE_DSPP_PCC] = dspp_pcc_install_property; \
 	func[SDE_DSPP_HSIC] = dspp_hsic_install_property; \
+	func[SDE_DSPP_MEMCOLOR] = dspp_memcolor_install_property; \
+	func[SDE_DSPP_SIXZONE] = dspp_sixzone_install_property; \
 	func[SDE_DSPP_AD] = dspp_ad_install_property; \
 	func[SDE_DSPP_VLUT] = dspp_vlut_install_property; \
 	func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
 	func[SDE_DSPP_GC] = dspp_gc_install_property; \
 	func[SDE_DSPP_IGC] = dspp_igc_install_property; \
+	func[SDE_DSPP_HIST] = dspp_hist_install_property; \
 } while (0)
 
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -103,15 +114,16 @@
 	SDE_CP_CRTC_DSPP_IGC,
 	SDE_CP_CRTC_DSPP_PCC,
 	SDE_CP_CRTC_DSPP_GC,
-	SDE_CP_CRTC_DSPP_HUE,
-	SDE_CP_CRTC_DSPP_SAT,
-	SDE_CP_CRTC_DSPP_VAL,
-	SDE_CP_CRTC_DSPP_CONT,
-	SDE_CP_CRTC_DSPP_MEMCOLOR,
+	SDE_CP_CRTC_DSPP_HSIC,
+	SDE_CP_CRTC_DSPP_MEMCOL_SKIN,
+	SDE_CP_CRTC_DSPP_MEMCOL_SKY,
+	SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE,
+	SDE_CP_CRTC_DSPP_MEMCOL_PROT,
 	SDE_CP_CRTC_DSPP_SIXZONE,
 	SDE_CP_CRTC_DSPP_GAMUT,
 	SDE_CP_CRTC_DSPP_DITHER,
-	SDE_CP_CRTC_DSPP_HIST,
+	SDE_CP_CRTC_DSPP_HIST_CTRL,
+	SDE_CP_CRTC_DSPP_HIST_IRQ,
 	SDE_CP_CRTC_DSPP_AD,
 	SDE_CP_CRTC_DSPP_VLUT,
 	SDE_CP_CRTC_DSPP_AD_MODE,
@@ -365,6 +377,13 @@
 		return;
 	}
 
+	/* create blob to store histogram data */
+	sde_crtc->hist_blob = drm_property_create_blob(crtc->dev,
+				sizeof(struct drm_msm_hist), NULL);
+	if (IS_ERR(sde_crtc->hist_blob))
+		sde_crtc->hist_blob = NULL;
+
+	mutex_init(&sde_crtc->crtc_cp_lock);
 	INIT_LIST_HEAD(&sde_crtc->active_list);
 	INIT_LIST_HEAD(&sde_crtc->dirty_list);
 	INIT_LIST_HEAD(&sde_crtc->feature_list);
@@ -531,6 +550,77 @@
 	sde_cp_crtc_prop_attach(&prop_attach);
 }
 
+static struct sde_crtc_irq_info *_sde_cp_get_intr_node(u32 event,
+				struct sde_crtc *sde_crtc)
+{
+	bool found = false;
+	struct sde_crtc_irq_info *node = NULL;
+
+	list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+		if (node->event == event) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found)
+		node = NULL;
+
+	return node;
+}
+
+static void _sde_cp_crtc_enable_hist_irq(struct sde_crtc *sde_crtc)
+{
+	struct drm_crtc *crtc_drm = &sde_crtc->base;
+	struct sde_kms *kms = NULL;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc_irq_info *node = NULL;
+	int i, irq_idx, ret = 0;
+	unsigned long flags;
+
+	if (!crtc_drm) {
+		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
+		return;
+	}
+
+	kms = get_kms(crtc_drm);
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		hw_lm = sde_crtc->mixers[i].hw_lm;
+		hw_dspp = sde_crtc->mixers[i].hw_dspp;
+		if (!hw_lm->cfg.right_mixer)
+			break;
+	}
+
+	if (!hw_dspp) {
+		DRM_ERROR("invalid dspp\n");
+		return;
+	}
+
+	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
+					hw_dspp->idx);
+	if (irq_idx < 0) {
+		DRM_ERROR("failed to get irq idx\n");
+		return;
+	}
+
+	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, sde_crtc);
+	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
+	if (!node)
+		return;
+
+	if (node->state == IRQ_DISABLED) {
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret)
+			DRM_ERROR("failed to enable irq %d\n", irq_idx);
+		else
+			node->state = IRQ_ENABLED;
+	}
+}
+
 static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
 				   struct sde_crtc *sde_crtc)
 {
@@ -583,40 +673,40 @@
 			}
 			hw_dspp->ops.setup_gc(hw_dspp, &hw_cfg);
 			break;
-		case SDE_CP_CRTC_DSPP_HUE:
-			if (!hw_dspp || !hw_dspp->ops.setup_hue) {
+		case SDE_CP_CRTC_DSPP_HSIC:
+			if (!hw_dspp || !hw_dspp->ops.setup_pa_hsic) {
 				ret = -EINVAL;
 				continue;
 			}
-			hw_dspp->ops.setup_hue(hw_dspp, &hw_cfg);
+			hw_dspp->ops.setup_pa_hsic(hw_dspp, &hw_cfg);
 			break;
-		case SDE_CP_CRTC_DSPP_SAT:
-			if (!hw_dspp || !hw_dspp->ops.setup_sat) {
+		case SDE_CP_CRTC_DSPP_MEMCOL_SKIN:
+			if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_skin) {
 				ret = -EINVAL;
 				continue;
 			}
-			hw_dspp->ops.setup_sat(hw_dspp, &hw_cfg);
+			hw_dspp->ops.setup_pa_memcol_skin(hw_dspp, &hw_cfg);
 			break;
-		case SDE_CP_CRTC_DSPP_VAL:
-			if (!hw_dspp || !hw_dspp->ops.setup_val) {
+		case SDE_CP_CRTC_DSPP_MEMCOL_SKY:
+			if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_sky) {
 				ret = -EINVAL;
 				continue;
 			}
-			hw_dspp->ops.setup_val(hw_dspp, &hw_cfg);
+			hw_dspp->ops.setup_pa_memcol_sky(hw_dspp, &hw_cfg);
 			break;
-		case SDE_CP_CRTC_DSPP_CONT:
-			if (!hw_dspp || !hw_dspp->ops.setup_cont) {
+		case SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE:
+			if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_foliage) {
 				ret = -EINVAL;
 				continue;
 			}
-			hw_dspp->ops.setup_cont(hw_dspp, &hw_cfg);
+			hw_dspp->ops.setup_pa_memcol_foliage(hw_dspp, &hw_cfg);
 			break;
-		case SDE_CP_CRTC_DSPP_MEMCOLOR:
-			if (!hw_dspp || !hw_dspp->ops.setup_pa_memcolor) {
+		case SDE_CP_CRTC_DSPP_MEMCOL_PROT:
+			if (!hw_dspp || !hw_dspp->ops.setup_pa_memcol_prot) {
 				ret = -EINVAL;
 				continue;
 			}
-			hw_dspp->ops.setup_pa_memcolor(hw_dspp, &hw_cfg);
+			hw_dspp->ops.setup_pa_memcol_prot(hw_dspp, &hw_cfg);
 			break;
 		case SDE_CP_CRTC_DSPP_SIXZONE:
 			if (!hw_dspp || !hw_dspp->ops.setup_sixzone) {
@@ -639,6 +729,21 @@
 			}
 			hw_lm->ops.setup_gc(hw_lm, &hw_cfg);
 			break;
+		case SDE_CP_CRTC_DSPP_HIST_CTRL:
+			if (!hw_dspp || !hw_dspp->ops.setup_histogram) {
+				ret = -EINVAL;
+				continue;
+			}
+			hw_dspp->ops.setup_histogram(hw_dspp, &feature_enabled);
+			break;
+		case SDE_CP_CRTC_DSPP_HIST_IRQ:
+			if (!hw_dspp || !hw_lm) {
+				ret = -EINVAL;
+				continue;
+			}
+			if (!hw_lm->cfg.right_mixer)
+				_sde_cp_crtc_enable_hist_irq(sde_crtc);
+			break;
 		case SDE_CP_CRTC_DSPP_AD_MODE:
 			if (!hw_dspp || !hw_dspp->ops.setup_ad) {
 				ret = -EINVAL;
@@ -746,6 +851,8 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
+
 	/* Check if dirty lists are empty and ad features are disabled for
 	 * early return. If ad properties are active then we need to issue
 	 * dspp flush.
@@ -754,7 +861,7 @@
 		list_empty(&sde_crtc->ad_dirty)) {
 		if (list_empty(&sde_crtc->ad_active)) {
 			DRM_DEBUG_DRIVER("Dirty list is empty\n");
-			return;
+			goto exit;
 		}
 		sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESET);
 		set_dspp_flush = true;
@@ -794,6 +901,8 @@
 			ctl->ops.update_pending_flush(ctl, flush_mask);
 		}
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 }
 
 void sde_cp_crtc_install_properties(struct drm_crtc *crtc)
@@ -824,13 +933,15 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
+
 	/**
 	 * Function can be called during the atomic_check with test_only flag
 	 * and actual commit. Allocate properties only if feature list is
 	 * empty during the atomic_check with test_only flag.
 	 */
 	if (!list_empty(&sde_crtc->feature_list))
-		return;
+		goto exit;
 
 	catalog = kms->catalog;
 	priv = crtc->dev->dev_private;
@@ -846,7 +957,7 @@
 		setup_lm_prop_install_funcs(lm_prop_install_func);
 	}
 	if (!priv->cp_property)
-		return;
+		goto exit;
 
 	if (!catalog->dspp_count)
 		goto lm_property;
@@ -862,7 +973,7 @@
 
 lm_property:
 	if (!catalog->mixer_count)
-		return;
+		goto exit;
 
 	/* Check for all the LM properties and attach it to CRTC */
 	features = catalog->mixer[0].features;
@@ -872,6 +983,9 @@
 		if (lm_prop_install_func[i])
 			lm_prop_install_func[i](crtc);
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
+
 }
 
 int sde_cp_crtc_set_property(struct drm_crtc *crtc,
@@ -894,6 +1008,7 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
 		if (property->base.id == prop_node->property_id) {
 			found = 1;
@@ -901,8 +1016,11 @@
 		}
 	}
 
-	if (!found)
-		return 0;
+	if (!found) {
+		ret = -ENOENT;
+		goto exit;
+	}
+
 	/**
 	 * sde_crtc is virtual ensure that hardware has been attached to the
 	 * crtc. Check LM and dspp counts based on whether feature is a
@@ -912,7 +1030,8 @@
 	    sde_crtc->num_mixers > ARRAY_SIZE(sde_crtc->mixers)) {
 		DRM_ERROR("Invalid mixer config act cnt %d max cnt %ld\n",
 			sde_crtc->num_mixers, ARRAY_SIZE(sde_crtc->mixers));
-		return -EINVAL;
+		ret = -EPERM;
+		goto exit;
 	}
 
 	dspp_cnt = 0;
@@ -927,17 +1046,19 @@
 	if (prop_node->is_dspp_feature && dspp_cnt < sde_crtc->num_mixers) {
 		DRM_ERROR("invalid dspp cnt %d mixer cnt %d\n", dspp_cnt,
 			sde_crtc->num_mixers);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit;
 	} else if (lm_cnt < sde_crtc->num_mixers) {
 		DRM_ERROR("invalid lm cnt %d mixer cnt %d\n", lm_cnt,
 			sde_crtc->num_mixers);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto exit;
 	}
 
 	ret = sde_cp_ad_validate_prop(prop_node, sde_crtc);
 	if (ret) {
 		DRM_ERROR("ad property validation failed ret %d\n", ret);
-		return ret;
+		goto exit;
 	}
 
 	/* remove the property from dirty list */
@@ -955,6 +1076,8 @@
 		/* Mark the feature as dirty */
 		sde_cp_update_list(prop_node, sde_crtc, true);
 	}
+exit:
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 	return ret;
 }
 
@@ -977,12 +1100,14 @@
 	}
 	/* Return 0 if property is not supported */
 	*val = 0;
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry(prop_node, &sde_crtc->feature_list, feature_list) {
 		if (property->base.id == prop_node->property_id) {
 			*val = prop_node->prop_val;
 			break;
 		}
 	}
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 	return 0;
 }
 
@@ -1015,6 +1140,10 @@
 		kfree(prop_node);
 	}
 
+	if (sde_crtc->hist_blob)
+		drm_property_unreference_blob(sde_crtc->hist_blob);
+
+	mutex_destroy(&sde_crtc->crtc_cp_lock);
 	INIT_LIST_HEAD(&sde_crtc->active_list);
 	INIT_LIST_HEAD(&sde_crtc->dirty_list);
 	INIT_LIST_HEAD(&sde_crtc->feature_list);
@@ -1035,6 +1164,7 @@
 		return;
 	}
 
+	mutex_lock(&sde_crtc->crtc_cp_lock);
 	list_for_each_entry_safe(prop_node, n, &sde_crtc->active_list,
 				 active_list) {
 		sde_cp_update_list(prop_node, sde_crtc, true);
@@ -1046,6 +1176,7 @@
 		sde_cp_update_list(prop_node, sde_crtc, true);
 		list_del_init(&prop_node->active_list);
 	}
+	mutex_unlock(&sde_crtc->crtc_cp_lock);
 }
 
 void sde_cp_crtc_resume(struct drm_crtc *crtc)
@@ -1091,9 +1222,72 @@
 	switch (version) {
 	case 1:
 		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
-			"SDE_DSPP_HUE_V", version);
-		sde_cp_crtc_install_range_property(crtc, feature_name,
-			SDE_CP_CRTC_DSPP_HUE, 0, U32_MAX, 0);
+			"SDE_DSPP_PA_HSIC_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_HSIC, sizeof(struct drm_msm_pa_hsic));
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
+static void dspp_memcolor_install_property(struct drm_crtc *crtc)
+{
+	char feature_name[256];
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+	version = catalog->dspp[0].sblk->memcolor.version >> 16;
+	switch (version) {
+	case 1:
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+			"SDE_DSPP_PA_MEMCOL_SKIN_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_MEMCOL_SKIN,
+			sizeof(struct drm_msm_memcol));
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+			"SDE_DSPP_PA_MEMCOL_SKY_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_MEMCOL_SKY,
+			sizeof(struct drm_msm_memcol));
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+			"SDE_DSPP_PA_MEMCOL_FOLIAGE_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_MEMCOL_FOLIAGE,
+			sizeof(struct drm_msm_memcol));
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+			"SDE_DSPP_PA_MEMCOL_PROT_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_MEMCOL_PROT,
+			sizeof(struct drm_msm_memcol));
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
+static void dspp_sixzone_install_property(struct drm_crtc *crtc)
+{
+	char feature_name[256];
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+	version = catalog->dspp[0].sblk->sixzone.version >> 16;
+	switch (version) {
+	case 1:
+		snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+			"SDE_DSPP_PA_SIXZONE_V", version);
+		sde_cp_crtc_install_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_SIXZONE,
+			sizeof(struct drm_msm_sixzone));
 		break;
 	default:
 		DRM_ERROR("version %d not supported\n", version);
@@ -1273,6 +1467,30 @@
 	}
 }
 
+static void dspp_hist_install_property(struct drm_crtc *crtc)
+{
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+
+	version = catalog->dspp[0].sblk->hist.version >> 16;
+	switch (version) {
+	case 1:
+		sde_cp_crtc_install_enum_property(crtc,
+			SDE_CP_CRTC_DSPP_HIST_CTRL, sde_hist_modes,
+			ARRAY_SIZE(sde_hist_modes), "SDE_DSPP_HIST_CTRL_V1");
+		sde_cp_crtc_install_range_property(crtc, "SDE_DSPP_HIST_IRQ_V1",
+			SDE_CP_CRTC_DSPP_HIST_IRQ, 0, U16_MAX, 0);
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
 static void sde_cp_update_list(struct sde_cp_node *prop_node,
 		struct sde_crtc *crtc, bool dirty_list)
 {
@@ -1394,6 +1612,7 @@
 	int i;
 	int irq_idx, ret;
 	struct sde_cp_node prop_node;
+	struct sde_crtc_irq_info *node = NULL;
 
 	if (!crtc_drm || !ad_irq) {
 		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, ad_irq);
@@ -1438,14 +1657,28 @@
 		goto exit;
 	}
 
+	node = _sde_cp_get_intr_node(DRM_EVENT_AD_BACKLIGHT, crtc);
+
 	if (!en) {
-		sde_core_irq_disable(kms, &irq_idx, 1);
+		if (node) {
+			if (node->state == IRQ_ENABLED) {
+				ret = sde_core_irq_disable(kms, &irq_idx, 1);
+				if (ret)
+					DRM_ERROR("disable irq %d error %d\n",
+						irq_idx, ret);
+				else
+					node->state = IRQ_NOINIT;
+			} else {
+				node->state = IRQ_NOINIT;
+			}
+		} else {
+			DRM_ERROR("failed to get node from crtc event list\n");
+		}
 		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
 		ret = 0;
 		goto exit;
 	}
 
-	INIT_LIST_HEAD(&ad_irq->list);
 	ad_irq->arg = crtc;
 	ad_irq->func = sde_cp_ad_interrupt_cb;
 	ret = sde_core_irq_register_callback(kms, irq_idx, ad_irq);
@@ -1453,10 +1686,30 @@
 		DRM_ERROR("failed to register the callback ret %d\n", ret);
 		goto exit;
 	}
-	ret = sde_core_irq_enable(kms, &irq_idx, 1);
-	if (ret) {
-		DRM_ERROR("failed to enable irq ret %d\n", ret);
-		sde_core_irq_unregister_callback(kms, irq_idx, ad_irq);
+
+	if (node) {
+		/* device resume or resume from IPC cases */
+		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
+			ret = sde_core_irq_enable(kms, &irq_idx, 1);
+			if (ret) {
+				DRM_ERROR("enable irq %d error %d\n",
+					irq_idx, ret);
+				sde_core_irq_unregister_callback(kms,
+					irq_idx, ad_irq);
+			} else {
+				node->state = IRQ_ENABLED;
+			}
+		}
+	} else {
+		/* request from userspace to register the event
+		 * in this case, node has not been added into the event list
+		 */
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret) {
+			DRM_ERROR("failed to enable irq ret %d\n", ret);
+			sde_core_irq_unregister_callback(kms,
+				irq_idx, ad_irq);
+		}
 	}
 exit:
 	return ret;
@@ -1519,3 +1772,201 @@
 
 	sde_cp_ad_set_prop(sde_crtc, AD_IPC_RESUME);
 }
+
+static void sde_cp_hist_interrupt_cb(void *arg, int irq_idx)
+{
+	struct sde_crtc *crtc = arg;
+	struct drm_crtc *crtc_drm = &crtc->base;
+	struct sde_hw_dspp *hw_dspp;
+	struct sde_kms *kms;
+	struct sde_crtc_irq_info *node = NULL;
+	u32 i;
+	int ret = 0;
+	unsigned long flags;
+
+	/* disable histogram irq */
+	kms = get_kms(crtc_drm);
+	spin_lock_irqsave(&crtc->spin_lock, flags);
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);
+	spin_unlock_irqrestore(&crtc->spin_lock, flags);
+
+	if (!node) {
+		DRM_ERROR("cannot find histogram event node in crtc\n");
+		return;
+	}
+
+	if (node->state == IRQ_ENABLED) {
+		if (sde_core_irq_disable_nolock(kms, irq_idx)) {
+			DRM_ERROR("failed to disable irq %d, ret %d\n",
+				irq_idx, ret);
+			return;
+		}
+		node->state = IRQ_DISABLED;
+	}
+
+	/* lock histogram buffer */
+	for (i = 0; i < crtc->num_mixers; i++) {
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (hw_dspp && hw_dspp->ops.lock_histogram)
+			hw_dspp->ops.lock_histogram(hw_dspp, NULL);
+	}
+
+	/* notify histogram event */
+	sde_crtc_event_queue(crtc_drm, sde_cp_notify_hist_event, NULL);
+}
+
+static void sde_cp_notify_hist_event(struct drm_crtc *crtc_drm, void *arg)
+{
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc *crtc;
+	struct drm_event event;
+	struct drm_msm_hist *hist_data;
+	struct drm_msm_hist tmp_hist_data;
+	u32 i, j;
+
+	if (!crtc_drm) {
+		DRM_ERROR("invalid crtc %pK\n", crtc_drm);
+		return;
+	}
+
+	crtc = to_sde_crtc(crtc_drm);
+	if (!crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
+		return;
+	}
+
+	if (!crtc->hist_blob)
+		return;
+
+	/* read histogram data into blob */
+	hist_data = (struct drm_msm_hist *)crtc->hist_blob->data;
+	for (i = 0; i < crtc->num_mixers; i++) {
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (!hw_dspp || !hw_dspp->ops.read_histogram) {
+			DRM_ERROR("invalid dspp %pK or read_histogram func\n",
+				hw_dspp);
+			return;
+		}
+		if (!i) {
+			hw_dspp->ops.read_histogram(hw_dspp, hist_data);
+		} else {
+			/* Merge hist data for DSPP0 and DSPP1 */
+			hw_dspp->ops.read_histogram(hw_dspp, &tmp_hist_data);
+			for (j = 0; j < HIST_V_SIZE; j++)
+				hist_data->data[j] += tmp_hist_data.data[j];
+		}
+	}
+
+	/* send histogram event with blob id */
+	event.length = sizeof(u32);
+	event.type = DRM_EVENT_HISTOGRAM;
+	msm_mode_object_event_notify(&crtc_drm->base, crtc_drm->dev,
+			&event, (u8 *)(&crtc->hist_blob->base.id));
+}
+
+int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
+	struct sde_irq_callback *hist_irq)
+{
+	struct sde_kms *kms = NULL;
+	u32 num_mixers;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_dspp *hw_dspp = NULL;
+	struct sde_crtc *crtc;
+	struct sde_crtc_irq_info *node = NULL;
+	int i, irq_idx, ret = 0;
+
+	if (!crtc_drm || !hist_irq) {
+		DRM_ERROR("invalid crtc %pK irq %pK\n", crtc_drm, hist_irq);
+		return -EINVAL;
+	}
+
+	crtc = to_sde_crtc(crtc_drm);
+	if (!crtc) {
+		DRM_ERROR("invalid sde_crtc %pK\n", crtc);
+		return -EINVAL;
+	}
+
+	kms = get_kms(crtc_drm);
+	num_mixers = crtc->num_mixers;
+
+	for (i = 0; i < num_mixers; i++) {
+		hw_lm = crtc->mixers[i].hw_lm;
+		hw_dspp = crtc->mixers[i].hw_dspp;
+		if (!hw_lm->cfg.right_mixer)
+			break;
+	}
+
+	if (!hw_dspp) {
+		DRM_ERROR("invalid dspp\n");
+		ret = -EPERM;
+		goto exit;
+	}
+
+	irq_idx = sde_core_irq_idx_lookup(kms, SDE_IRQ_TYPE_HIST_DSPP_DONE,
+			hw_dspp->idx);
+	if (irq_idx < 0) {
+		DRM_ERROR("failed to get the irq idx ret %d\n", irq_idx);
+		ret = irq_idx;
+		goto exit;
+	}
+
+	node = _sde_cp_get_intr_node(DRM_EVENT_HISTOGRAM, crtc);
+
+	/* deregister histogram irq */
+	if (!en) {
+		if (node) {
+			/* device suspend case or suspend to IPC cases */
+			if (node->state == IRQ_ENABLED) {
+				ret = sde_core_irq_disable(kms, &irq_idx, 1);
+				if (ret)
+					DRM_ERROR("disable irq %d error %d\n",
+						irq_idx, ret);
+				else
+					node->state = IRQ_NOINIT;
+			} else {
+				node->state = IRQ_NOINIT;
+			}
+		} else {
+			DRM_ERROR("failed to get node from crtc event list\n");
+		}
+
+		sde_core_irq_unregister_callback(kms, irq_idx, hist_irq);
+		goto exit;
+	}
+
+	/* register histogram irq */
+	hist_irq->arg = crtc;
+	hist_irq->func = sde_cp_hist_interrupt_cb;
+	ret = sde_core_irq_register_callback(kms, irq_idx, hist_irq);
+	if (ret) {
+		DRM_ERROR("failed to register the callback ret %d\n", ret);
+		goto exit;
+	}
+
+	if (node) {
+		/* device resume or resume from IPC cases */
+		if (node->state == IRQ_DISABLED || node->state == IRQ_NOINIT) {
+			ret = sde_core_irq_enable(kms, &irq_idx, 1);
+			if (ret) {
+				DRM_ERROR("enable irq %d error %d\n",
+					irq_idx, ret);
+				sde_core_irq_unregister_callback(kms,
+					irq_idx, hist_irq);
+			} else {
+				node->state = IRQ_ENABLED;
+			}
+		}
+	} else {
+		/* request from userspace to register the event
+		 * in this case, node has not been added into the event list
+		 */
+		ret = sde_core_irq_enable(kms, &irq_idx, 1);
+		if (ret) {
+			DRM_ERROR("failed to enable irq ret %d\n", ret);
+			sde_core_irq_unregister_callback(kms,
+				irq_idx, hist_irq);
+		}
+	}
+exit:
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index 08e345d..7eb1738 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.h
@@ -26,7 +26,27 @@
 enum sde_memcolor_type {
 	MEMCOLOR_SKIN = 0,
 	MEMCOLOR_SKY,
-	MEMCOLOR_FOLIAGE
+	MEMCOLOR_FOLIAGE,
+	MEMCOLOR_MAX
+};
+
+/*
+ * PA HISTOGRAM modes
+ * @HIST_DISABLED          Histogram disabled
+ * @HIST_ENABLED           Histogram enabled
+ */
+enum sde_hist_modes {
+	HIST_DISABLED,
+	HIST_ENABLED
+};
+
+/**
+ * struct drm_prop_enum_list - drm structure for creating enum property and
+ *                             enumerating values
+ */
+static const struct drm_prop_enum_list sde_hist_modes[] = {
+	{HIST_DISABLED, "hist_off"},
+	{HIST_ENABLED, "hist_on"},
 };
 
 /**
@@ -117,4 +137,13 @@
  * @crtc: Pointer to crtc.
  */
 void sde_cp_crtc_post_ipc(struct drm_crtc *crtc);
+
+/**
+ * sde_cp_hist_interrupt: Api to enable/disable histogram interrupt
+ * @crtc: Pointer to crtc.
+ * @en: Variable to enable/disable interrupt.
+ * @irq: Pointer to irq callback
+ */
+int sde_cp_hist_interrupt(struct drm_crtc *crtc_drm, bool en,
+	struct sde_irq_callback *hist_irq);
 #endif /*_SDE_COLOR_PROCESSING_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index 9d70525..fb1f578 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -12,12 +12,16 @@
 
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
 #include "msm_drv.h"
+#include "sde_dbg.h"
 
 #include "sde_kms.h"
 #include "sde_connector.h"
+#include "sde_encoder.h"
 #include <linux/backlight.h>
 #include "dsi_drm.h"
 #include "dsi_display.h"
+#include "sde_crtc.h"
+#include "sde_rm.h"
 
 #define BL_NODE_NAME_SIZE 32
 
@@ -107,7 +111,6 @@
 static int sde_backlight_setup(struct sde_connector *c_conn,
 					struct drm_device *dev)
 {
-	struct backlight_device *bl_device;
 	struct backlight_properties props;
 	struct dsi_display *display;
 	struct dsi_backlight_config *bl_config;
@@ -131,11 +134,12 @@
 	props.brightness = bl_config->brightness_max_level;
 	snprintf(bl_node_name, BL_NODE_NAME_SIZE, "panel%u-backlight",
 							display_count);
-	bl_device = backlight_device_register(bl_node_name, dev->dev,
+	c_conn->bl_device = backlight_device_register(bl_node_name, dev->dev,
 			c_conn, &sde_backlight_device_ops, &props);
-	if (IS_ERR_OR_NULL(bl_device)) {
+	if (IS_ERR_OR_NULL(c_conn->bl_device)) {
 		SDE_ERROR("Failed to register backlight: %ld\n",
-				    PTR_ERR(bl_device));
+				    PTR_ERR(c_conn->bl_device));
+		c_conn->bl_device = NULL;
 		return -ENODEV;
 	}
 	display_count++;
@@ -150,7 +154,7 @@
 {
 	struct sde_connector *c_conn;
 	unsigned long irq_flags;
-	void (*cb_func)(uint32_t event_idx,
+	int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3);
@@ -173,7 +177,7 @@
 	spin_unlock_irqrestore(&c_conn->event_lock, irq_flags);
 
 	if (cb_func)
-		cb_func(event_idx, instance_idx, usr,
+		rc = cb_func(event_idx, instance_idx, usr,
 			data0, data1, data2, data3);
 	else
 		rc = -EAGAIN;
@@ -183,7 +187,7 @@
 
 int sde_connector_register_event(struct drm_connector *connector,
 		uint32_t event_idx,
-		void (*cb_func)(uint32_t event_idx,
+		int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3),
@@ -343,6 +347,39 @@
 	return 0;
 }
 
+int sde_connector_get_mode_info(struct drm_connector_state *conn_state,
+	struct msm_mode_info *mode_info)
+{
+	struct sde_connector_state *sde_conn_state = NULL;
+
+	if (!conn_state || !mode_info) {
+		SDE_ERROR("Invalid arguments\n");
+		return -EINVAL;
+	}
+
+	sde_conn_state = to_sde_connector_state(conn_state);
+	memcpy(mode_info, &sde_conn_state->mode_info,
+		sizeof(sde_conn_state->mode_info));
+
+	return 0;
+}
+
+static int sde_connector_handle_disp_recovery(uint32_t event_idx,
+			uint32_t instance_idx, void *usr,
+			uint32_t data0, uint32_t data1,
+			uint32_t data2, uint32_t data3)
+{
+	struct sde_connector *c_conn = usr;
+	int rc = 0;
+
+	if (!c_conn)
+		return -EINVAL;
+
+	rc = sde_kms_handle_recovery(c_conn->encoder);
+
+	return rc;
+}
+
 int sde_connector_get_info(struct drm_connector *connector,
 		struct msm_display_info *info)
 {
@@ -365,6 +402,29 @@
 	return c_conn->ops.get_info(info, c_conn->display);
 }
 
+void sde_connector_schedule_status_work(struct drm_connector *connector,
+		bool en)
+{
+	struct sde_connector *c_conn;
+	struct msm_display_info info;
+
+	c_conn = to_sde_connector(connector);
+	if (!c_conn)
+		return;
+
+	sde_connector_get_info(connector, &info);
+	if (c_conn->ops.check_status &&
+		(info.capabilities & MSM_DISPLAY_ESD_ENABLED)) {
+		if (en)
+			/* Schedule ESD status check */
+			schedule_delayed_work(&c_conn->status_work,
+				msecs_to_jiffies(STATUS_CHECK_INTERVAL_MS));
+		else
+			/* Cancel any pending ESD status check */
+			cancel_delayed_work_sync(&c_conn->status_work);
+	}
+}
+
 static int _sde_connector_update_power_locked(struct sde_connector *c_conn)
 {
 	struct drm_connector *connector;
@@ -410,6 +470,57 @@
 	}
 	c_conn->last_panel_power_mode = mode;
 
+	if (mode != SDE_MODE_DPMS_ON)
+		sde_connector_schedule_status_work(connector, false);
+
+	return rc;
+}
+
+static int _sde_connector_update_bl_scale(struct sde_connector *c_conn, int idx)
+{
+	struct drm_connector conn;
+	struct dsi_display *dsi_display;
+	struct dsi_backlight_config *bl_config;
+	uint64_t value;
+	int rc = 0;
+
+	if (!c_conn) {
+		SDE_ERROR("Invalid params sde_connector null\n");
+		return -EINVAL;
+	}
+
+	conn = c_conn->base;
+	dsi_display = c_conn->display;
+	if (!dsi_display || !dsi_display->panel) {
+		SDE_ERROR("Invalid params(s) dsi_display %pK, panel %pK\n",
+			dsi_display,
+			((dsi_display) ? dsi_display->panel : NULL));
+		return -EINVAL;
+	}
+
+	bl_config = &dsi_display->panel->bl_config;
+	value = sde_connector_get_property(conn.state, idx);
+
+	if (idx == CONNECTOR_PROP_BL_SCALE) {
+		if (value > MAX_BL_SCALE_LEVEL)
+			bl_config->bl_scale = MAX_BL_SCALE_LEVEL;
+		else
+			bl_config->bl_scale = (u32)value;
+	} else if (idx == CONNECTOR_PROP_AD_BL_SCALE) {
+		if (value > MAX_AD_BL_SCALE_LEVEL)
+			bl_config->bl_scale_ad = MAX_AD_BL_SCALE_LEVEL;
+		else
+			bl_config->bl_scale_ad = (u32)value;
+	} else {
+		SDE_DEBUG("invalid idx %d\n", idx);
+		return 0;
+	}
+
+	SDE_DEBUG("bl_scale = %u, bl_scale_ad = %u, bl_level = %u\n",
+		bl_config->bl_scale, bl_config->bl_scale_ad,
+		bl_config->bl_level);
+	rc = c_conn->ops.set_backlight(dsi_display, bl_config->bl_level);
+
 	return rc;
 }
 
@@ -443,6 +554,10 @@
 			_sde_connector_update_power_locked(c_conn);
 			mutex_unlock(&c_conn->lock);
 			break;
+		case CONNECTOR_PROP_BL_SCALE:
+		case CONNECTOR_PROP_AD_BL_SCALE:
+			_sde_connector_update_bl_scale(c_conn, idx);
+			break;
 		default:
 			/* nothing to do for most properties */
 			break;
@@ -453,6 +568,7 @@
 		return 0;
 
 	params.rois = &c_state->rois;
+	params.hdr_meta = &c_state->hdr_meta;
 
 	SDE_EVT32_VERBOSE(connector->base.id);
 
@@ -461,23 +577,26 @@
 	return rc;
 }
 
-void sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
+int sde_connector_clk_ctrl(struct drm_connector *connector, bool enable)
 {
 	struct sde_connector *c_conn;
 	struct dsi_display *display;
 	u32 state = enable ? DSI_CLK_ON : DSI_CLK_OFF;
+	int rc = 0;
 
 	if (!connector) {
 		SDE_ERROR("invalid connector\n");
-		return;
+		return -EINVAL;
 	}
 
 	c_conn = to_sde_connector(connector);
 	display = (struct dsi_display *) c_conn->display;
 
 	if (display && c_conn->ops.clk_ctrl)
-		c_conn->ops.clk_ctrl(display->mdp_clk_handle,
+		rc = c_conn->ops.clk_ctrl(display->mdp_clk_handle,
 				DSI_ALL_CLKS, state);
+
+	return rc;
 }
 
 static void sde_connector_destroy(struct drm_connector *connector)
@@ -491,6 +610,9 @@
 
 	c_conn = to_sde_connector(connector);
 
+	/* cancel if any pending esd work */
+	sde_connector_schedule_status_work(connector, false);
+
 	if (c_conn->ops.put_modes)
 		c_conn->ops.put_modes(connector, c_conn->display);
 
@@ -500,8 +622,14 @@
 		drm_property_unreference_blob(c_conn->blob_hdr);
 	if (c_conn->blob_dither)
 		drm_property_unreference_blob(c_conn->blob_dither);
+	if (c_conn->blob_mode_info)
+		drm_property_unreference_blob(c_conn->blob_mode_info);
+	if (c_conn->blob_ext_hdr)
+		drm_property_unreference_blob(c_conn->blob_ext_hdr);
 	msm_property_destroy(&c_conn->property_info);
 
+	if (c_conn->bl_device)
+		backlight_device_unregister(c_conn->bl_device);
 	drm_connector_unregister(connector);
 	mutex_destroy(&c_conn->lock);
 	sde_fence_deinit(&c_conn->retire_fence);
@@ -556,6 +684,8 @@
 	if (c_state->out_fb)
 		_sde_connector_destroy_fb(c_conn, c_state);
 
+	__drm_atomic_helper_connector_destroy_state(&c_state->base);
+
 	if (!c_conn) {
 		kfree(c_state);
 	} else {
@@ -577,6 +707,12 @@
 
 	c_conn = to_sde_connector(connector);
 
+	if (connector->state &&
+			!sde_crtc_is_reset_required(connector->state->crtc)) {
+		SDE_DEBUG_CONN(c_conn, "avoid reset for connector\n");
+		return;
+	}
+
 	if (connector->state) {
 		sde_connector_atomic_destroy_state(connector, connector->state);
 		connector->state = 0;
@@ -593,8 +729,7 @@
 			&c_state->property_state,
 			c_state->property_values);
 
-	c_state->base.connector = connector;
-	connector->state = &c_state->base;
+	__drm_atomic_helper_connector_reset(connector, &c_state->base);
 }
 
 static struct drm_connector_state *
@@ -621,6 +756,9 @@
 			c_oldstate, c_state,
 			&c_state->property_state, c_state->property_values);
 
+	__drm_atomic_helper_connector_duplicate_state(connector,
+			&c_state->base);
+
 	/* additional handling for drm framebuffer objects */
 	if (c_state->out_fb)
 		drm_framebuffer_reference(c_state->out_fb);
@@ -628,44 +766,81 @@
 	return &c_state->base;
 }
 
-static int _sde_connector_roi_v1_check_roi(
-		struct sde_connector *c_conn,
-		struct drm_clip_rect *roi_conn,
-		const struct msm_roi_caps *caps)
+int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state)
 {
-	const struct msm_roi_alignment *align = &caps->align;
-	int w = roi_conn->x2 - roi_conn->x1;
-	int h = roi_conn->y2 - roi_conn->y1;
+	const struct msm_roi_alignment *align = NULL;
+	struct sde_connector *c_conn = NULL;
+	struct msm_mode_info mode_info;
+	struct sde_connector_state *c_state;
+	int i, w, h;
 
-	if (w <= 0 || h <= 0) {
-		SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n", w, h);
+	if (!conn_state)
 		return -EINVAL;
-	}
 
-	if (w < align->min_width || w % align->width_pix_align) {
-		SDE_ERROR_CONN(c_conn,
-				"invalid conn roi width %d min %d align %d\n",
-				w, align->min_width, align->width_pix_align);
-		return -EINVAL;
-	}
+	memset(&mode_info, 0, sizeof(mode_info));
 
-	if (h < align->min_height || h % align->height_pix_align) {
-		SDE_ERROR_CONN(c_conn,
-				"invalid conn roi height %d min %d align %d\n",
-				h, align->min_height, align->height_pix_align);
-		return -EINVAL;
-	}
+	c_state = to_sde_connector_state(conn_state);
+	c_conn = to_sde_connector(conn_state->connector);
 
-	if (roi_conn->x1 % align->xstart_pix_align) {
-		SDE_ERROR_CONN(c_conn, "invalid conn roi x1 %d align %d\n",
-				roi_conn->x1, align->xstart_pix_align);
-		return -EINVAL;
-	}
+	memcpy(&mode_info, &c_state->mode_info, sizeof(c_state->mode_info));
 
-	if (roi_conn->y1 % align->ystart_pix_align) {
-		SDE_ERROR_CONN(c_conn, "invalid conn roi y1 %d align %d\n",
-				roi_conn->y1, align->ystart_pix_align);
-		return -EINVAL;
+	if (!mode_info.roi_caps.enabled)
+		return 0;
+
+	if (c_state->rois.num_rects > mode_info.roi_caps.num_roi) {
+		SDE_ERROR_CONN(c_conn, "too many rects specified: %d > %d\n",
+				c_state->rois.num_rects,
+				mode_info.roi_caps.num_roi);
+		return -E2BIG;
+	};
+
+	align = &mode_info.roi_caps.align;
+	for (i = 0; i < c_state->rois.num_rects; ++i) {
+		struct drm_clip_rect *roi_conn;
+
+		roi_conn = &c_state->rois.roi[i];
+		w = roi_conn->x2 - roi_conn->x1;
+		h = roi_conn->y2 - roi_conn->y1;
+
+		SDE_EVT32_VERBOSE(DRMID(&c_conn->base),
+				roi_conn->x1, roi_conn->y1,
+				roi_conn->x2, roi_conn->y2);
+
+		if (w <= 0 || h <= 0) {
+			SDE_ERROR_CONN(c_conn, "invalid conn roi w %d h %d\n",
+					w, h);
+			return -EINVAL;
+		}
+
+		if (w < align->min_width || w % align->width_pix_align) {
+			SDE_ERROR_CONN(c_conn,
+					"invalid conn roi width %d min %d align %d\n",
+					w, align->min_width,
+					align->width_pix_align);
+			return -EINVAL;
+		}
+
+		if (h < align->min_height || h % align->height_pix_align) {
+			SDE_ERROR_CONN(c_conn,
+					"invalid conn roi height %d min %d align %d\n",
+					h, align->min_height,
+					align->height_pix_align);
+			return -EINVAL;
+		}
+
+		if (roi_conn->x1 % align->xstart_pix_align) {
+			SDE_ERROR_CONN(c_conn,
+					"invalid conn roi x1 %d align %d\n",
+					roi_conn->x1, align->xstart_pix_align);
+			return -EINVAL;
+		}
+
+		if (roi_conn->y1 % align->ystart_pix_align) {
+			SDE_ERROR_CONN(c_conn,
+					"invalid conn roi y1 %d align %d\n",
+					roi_conn->y1, align->ystart_pix_align);
+			return -EINVAL;
+		}
 	}
 
 	return 0;
@@ -677,27 +852,13 @@
 		void *usr_ptr)
 {
 	struct sde_drm_roi_v1 roi_v1;
-	struct msm_display_info display_info;
-	struct msm_roi_caps *caps;
-	int i, rc;
+	int i;
 
 	if (!c_conn || !c_state) {
 		SDE_ERROR("invalid args\n");
 		return -EINVAL;
 	}
 
-	rc = sde_connector_get_info(&c_conn->base, &display_info);
-	if (rc) {
-		SDE_ERROR_CONN(c_conn, "display get info error: %d\n", rc);
-		return rc;
-	}
-
-	caps = &display_info.roi_caps;
-	if (!caps->enabled) {
-		SDE_ERROR_CONN(c_conn, "display roi capability is disabled\n");
-		return -ENOTSUPP;
-	}
-
 	memset(&c_state->rois, 0, sizeof(c_state->rois));
 
 	if (!usr_ptr) {
@@ -717,22 +878,14 @@
 		return 0;
 	}
 
-	if (roi_v1.num_rects > SDE_MAX_ROI_V1 ||
-			roi_v1.num_rects > caps->num_roi) {
-		SDE_ERROR_CONN(c_conn, "too many rects specified: %d\n",
+	if (roi_v1.num_rects > SDE_MAX_ROI_V1) {
+		SDE_ERROR_CONN(c_conn, "num roi rects more than supported: %d",
 				roi_v1.num_rects);
 		return -EINVAL;
 	}
 
 	c_state->rois.num_rects = roi_v1.num_rects;
 	for (i = 0; i < roi_v1.num_rects; ++i) {
-		int rc;
-
-		rc = _sde_connector_roi_v1_check_roi(c_conn, &roi_v1.roi[i],
-				caps);
-		if (rc)
-			return rc;
-
 		c_state->rois.roi[i] = roi_v1.roi[i];
 		SDE_DEBUG_CONN(c_conn, "roi%d: roi (%d,%d) (%d,%d)\n", i,
 				c_state->rois.roi[i].x1,
@@ -744,40 +897,62 @@
 	return 0;
 }
 
-static int _sde_connector_update_bl_scale(struct sde_connector *c_conn,
-		int idx,
-		uint64_t value)
+static int _sde_connector_set_ext_hdr_info(
+	struct sde_connector *c_conn,
+	struct sde_connector_state *c_state,
+	void *usr_ptr)
 {
-	struct dsi_display *dsi_display = c_conn->display;
-	struct dsi_backlight_config *bl_config;
-	int rc = 0;
+	struct drm_connector *connector;
+	struct drm_msm_ext_hdr_metadata *hdr_meta;
+	int i;
 
-	if (!dsi_display || !dsi_display->panel) {
-		pr_err("Invalid params(s) dsi_display %pK, panel %pK\n",
-			dsi_display,
-			((dsi_display) ? dsi_display->panel : NULL));
+	if (!c_conn || !c_state) {
+		SDE_ERROR_CONN(c_conn, "invalid args\n");
 		return -EINVAL;
 	}
 
-	bl_config = &dsi_display->panel->bl_config;
-	if (idx == CONNECTOR_PROP_BL_SCALE) {
-		bl_config->bl_scale = value;
-		if (value > MAX_BL_SCALE_LEVEL)
-			bl_config->bl_scale = MAX_BL_SCALE_LEVEL;
-		SDE_DEBUG("set to panel: bl_scale = %u, bl_level = %u\n",
-			bl_config->bl_scale, bl_config->bl_level);
-		rc = c_conn->ops.set_backlight(dsi_display,
-					       bl_config->bl_level);
-	} else if (idx == CONNECTOR_PROP_AD_BL_SCALE) {
-		bl_config->bl_scale_ad = value;
-		if (value > MAX_AD_BL_SCALE_LEVEL)
-			bl_config->bl_scale_ad = MAX_AD_BL_SCALE_LEVEL;
-		SDE_DEBUG("set to panel: bl_scale_ad = %u, bl_level = %u\n",
-			bl_config->bl_scale_ad, bl_config->bl_level);
-		rc = c_conn->ops.set_backlight(dsi_display,
-					       bl_config->bl_level);
+	connector = &c_conn->base;
+
+	if (!connector->hdr_supported) {
+		SDE_ERROR_CONN(c_conn, "sink doesn't support HDR\n");
+		return -ENOTSUPP;
 	}
-	return rc;
+
+	memset(&c_state->hdr_meta, 0, sizeof(c_state->hdr_meta));
+
+	if (!usr_ptr) {
+		SDE_DEBUG_CONN(c_conn, "hdr metadata cleared\n");
+		return 0;
+	}
+
+	if (copy_from_user(&c_state->hdr_meta,
+		(void __user *)usr_ptr,
+			sizeof(*hdr_meta))) {
+		SDE_ERROR_CONN(c_conn, "failed to copy hdr metadata\n");
+		return -EFAULT;
+	}
+
+	hdr_meta = &c_state->hdr_meta;
+
+	SDE_DEBUG_CONN(c_conn, "hdr_state %d\n", hdr_meta->hdr_state);
+	SDE_DEBUG_CONN(c_conn, "hdr_supported %d\n", hdr_meta->hdr_supported);
+	SDE_DEBUG_CONN(c_conn, "eotf %d\n", hdr_meta->eotf);
+	SDE_DEBUG_CONN(c_conn, "white_point_x %d\n", hdr_meta->white_point_x);
+	SDE_DEBUG_CONN(c_conn, "white_point_y %d\n", hdr_meta->white_point_y);
+	SDE_DEBUG_CONN(c_conn, "max_luminance %d\n", hdr_meta->max_luminance);
+	SDE_DEBUG_CONN(c_conn, "max_content_light_level %d\n",
+				hdr_meta->max_content_light_level);
+	SDE_DEBUG_CONN(c_conn, "max_average_light_level %d\n",
+				hdr_meta->max_average_light_level);
+
+	for (i = 0; i < HDR_PRIMARIES_COUNT; i++) {
+		SDE_DEBUG_CONN(c_conn, "display_primaries_x [%d]\n",
+				   hdr_meta->display_primaries_x[i]);
+		SDE_DEBUG_CONN(c_conn, "display_primaries_y [%d]\n",
+				   hdr_meta->display_primaries_y[i]);
+	}
+
+	return 0;
 }
 
 static int sde_connector_atomic_set_property(struct drm_connector *connector,
@@ -788,6 +963,7 @@
 	struct sde_connector *c_conn;
 	struct sde_connector_state *c_state;
 	int idx, rc;
+	uint64_t fence_fd;
 
 	if (!connector || !state || !property) {
 		SDE_ERROR("invalid argument(s), conn %pK, state %pK, prp %pK\n",
@@ -826,18 +1002,40 @@
 					c_conn->fb_kmap);
 		}
 		break;
-	case CONNECTOR_PROP_BL_SCALE:
-	case CONNECTOR_PROP_AD_BL_SCALE:
-		rc = _sde_connector_update_bl_scale(c_conn, idx, val);
+	case CONNECTOR_PROP_RETIRE_FENCE:
+		if (!val)
+			goto end;
+
+		rc = sde_fence_create(&c_conn->retire_fence, &fence_fd, 0);
+		if (rc) {
+			SDE_ERROR("fence create failed rc:%d\n", rc);
+			goto end;
+		}
+
+		rc = copy_to_user((uint64_t __user *)val, &fence_fd,
+			sizeof(uint64_t));
+		if (rc) {
+			SDE_ERROR("copy to user failed rc:%d\n", rc);
+			/* fence will be released with timeline update */
+			put_unused_fd(fence_fd);
+			rc = -EFAULT;
+			goto end;
+		}
+		break;
+	case CONNECTOR_PROP_ROI_V1:
+		rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val);
+		if (rc)
+			SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc);
 		break;
 	default:
 		break;
 	}
 
-	if (idx == CONNECTOR_PROP_ROI_V1) {
-		rc = _sde_connector_set_roi_v1(c_conn, c_state, (void *)val);
+	if (idx == CONNECTOR_PROP_HDR_METADATA) {
+		rc = _sde_connector_set_ext_hdr_info(c_conn,
+			c_state, (void *)val);
 		if (rc)
-			SDE_ERROR_CONN(c_conn, "invalid roi_v1, rc: %d\n", rc);
+			SDE_ERROR_CONN(c_conn, "cannot set hdr info %d\n", rc);
 	}
 
 	/* check for custom property handling */
@@ -905,6 +1103,19 @@
 	return rc;
 }
 
+void sde_conn_timeline_status(struct drm_connector *conn)
+{
+	struct sde_connector *c_conn;
+
+	if (!conn) {
+		SDE_ERROR("invalid connector\n");
+		return;
+	}
+
+	c_conn = to_sde_connector(conn);
+	sde_fence_timeline_status(&c_conn->retire_fence, &conn->base);
+}
+
 void sde_connector_prepare_fence(struct drm_connector *connector)
 {
 	if (!connector) {
@@ -938,6 +1149,29 @@
 	sde_fence_signal(&to_sde_connector(connector)->retire_fence, ts, true);
 }
 
+static void sde_connector_update_hdr_props(struct drm_connector *connector)
+{
+	struct sde_connector *c_conn = to_sde_connector(connector);
+	struct drm_msm_ext_hdr_properties hdr = {};
+
+	hdr.hdr_supported = connector->hdr_supported;
+
+	if (hdr.hdr_supported) {
+		hdr.hdr_eotf = connector->hdr_eotf;
+		hdr.hdr_metadata_type_one = connector->hdr_metadata_type_one;
+		hdr.hdr_max_luminance = connector->hdr_max_luminance;
+		hdr.hdr_avg_luminance = connector->hdr_avg_luminance;
+		hdr.hdr_min_luminance = connector->hdr_min_luminance;
+
+		msm_property_set_blob(&c_conn->property_info,
+			      &c_conn->blob_ext_hdr,
+			      &hdr,
+			      sizeof(hdr),
+			      CONNECTOR_PROP_EXT_HDR_INFO);
+
+	}
+}
+
 static enum drm_connector_status
 sde_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -1045,6 +1279,58 @@
 			connector, state, property, value);
 }
 
+int sde_connector_helper_reset_custom_properties(
+		struct drm_connector *connector,
+		struct drm_connector_state *connector_state)
+{
+	struct sde_connector *c_conn;
+	struct sde_connector_state *c_state;
+	struct drm_property *drm_prop;
+	enum msm_mdp_conn_property prop_idx;
+
+	if (!connector || !connector_state) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	c_state = to_sde_connector_state(connector_state);
+
+	for (prop_idx = 0; prop_idx < CONNECTOR_PROP_COUNT; prop_idx++) {
+		uint64_t val = c_state->property_values[prop_idx].value;
+		uint64_t def;
+		int ret;
+
+		drm_prop = msm_property_index_to_drm_property(
+				&c_conn->property_info, prop_idx);
+		if (!drm_prop) {
+			/* not all props will be installed, based on caps */
+			SDE_DEBUG_CONN(c_conn, "invalid property index %d\n",
+					prop_idx);
+			continue;
+		}
+
+		def = msm_property_get_default(&c_conn->property_info,
+				prop_idx);
+		if (val == def)
+			continue;
+
+		SDE_DEBUG_CONN(c_conn, "set prop %s idx %d from %llu to %llu\n",
+				drm_prop->name, prop_idx, val, def);
+
+		ret = drm_atomic_connector_set_property(connector,
+				connector_state, drm_prop, def);
+		if (ret) {
+			SDE_ERROR_CONN(c_conn,
+					"set property failed, idx %d ret %d\n",
+					prop_idx, ret);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_DEBUG_FS
 /**
  * sde_connector_init_debugfs - initialize connector debugfs
@@ -1053,6 +1339,7 @@
 static int sde_connector_init_debugfs(struct drm_connector *connector)
 {
 	struct sde_connector *sde_connector;
+	struct msm_display_info info;
 
 	if (!connector || !connector->debugfs_entry) {
 		SDE_ERROR("invalid connector\n");
@@ -1061,6 +1348,13 @@
 
 	sde_connector = to_sde_connector(connector);
 
+	sde_connector_get_info(connector, &info);
+	if (sde_connector->ops.check_status &&
+		(info.capabilities & MSM_DISPLAY_ESD_ENABLED))
+		debugfs_create_u32("force_panel_dead", 0600,
+				connector->debugfs_entry,
+				&sde_connector->force_panel_dead);
+
 	if (!debugfs_create_bool("fb_kmap", 0600, connector->debugfs_entry,
 			&sde_connector->fb_kmap)) {
 		SDE_ERROR("failed to create connector fb_kmap\n");
@@ -1086,12 +1380,39 @@
 	/* debugfs under connector->debugfs are deleted by drm_debugfs */
 }
 
+static int sde_connector_fill_modes(struct drm_connector *connector,
+		uint32_t max_width, uint32_t max_height)
+{
+	int rc, mode_count = 0;
+	struct sde_connector *sde_conn = NULL;
+
+	sde_conn = to_sde_connector(connector);
+	if (!sde_conn) {
+		SDE_ERROR("invalid arguments\n");
+		return 0;
+	}
+
+	mode_count = drm_helper_probe_single_connector_modes(connector,
+			max_width, max_height);
+
+	rc = sde_connector_set_blob_data(connector,
+				connector->state,
+				CONNECTOR_PROP_MODE_INFO);
+	if (rc) {
+		SDE_ERROR_CONN(sde_conn,
+			"failed to setup mode info prop, rc = %d\n", rc);
+		return 0;
+	}
+
+	return mode_count;
+}
+
 static const struct drm_connector_funcs sde_connector_ops = {
 	.dpms =                   sde_connector_dpms,
 	.reset =                  sde_connector_atomic_reset,
 	.detect =                 sde_connector_detect,
 	.destroy =                sde_connector_destroy,
-	.fill_modes =             drm_helper_probe_single_connector_modes,
+	.fill_modes =             sde_connector_fill_modes,
 	.atomic_duplicate_state = sde_connector_atomic_duplicate_state,
 	.atomic_destroy_state =   sde_connector_atomic_destroy_state,
 	.atomic_set_property =    sde_connector_atomic_set_property,
@@ -1104,6 +1425,7 @@
 static int sde_connector_get_modes(struct drm_connector *connector)
 {
 	struct sde_connector *c_conn;
+	int mode_count = 0;
 
 	if (!connector) {
 		SDE_ERROR("invalid connector\n");
@@ -1116,7 +1438,15 @@
 		return 0;
 	}
 
-	return c_conn->ops.get_modes(connector, c_conn->display);
+	mode_count = c_conn->ops.get_modes(connector, c_conn->display);
+	if (!mode_count) {
+		SDE_ERROR_CONN(c_conn, "failed to get modes\n");
+		return 0;
+	}
+
+	sde_connector_update_hdr_props(connector);
+
+	return mode_count;
 }
 
 static enum drm_mode_status
@@ -1157,12 +1487,217 @@
 	return c_conn->encoder;
 }
 
+static void sde_connector_check_status_work(struct work_struct *work)
+{
+	struct sde_connector *conn;
+	struct drm_event event;
+	int rc = 0;
+	bool panel_dead = false;
+
+	conn = container_of(to_delayed_work(work),
+			struct sde_connector, status_work);
+	if (!conn) {
+		SDE_ERROR("not able to get connector object\n");
+		return;
+	}
+
+	mutex_lock(&conn->lock);
+	if (!conn->ops.check_status ||
+			(conn->dpms_mode != DRM_MODE_DPMS_ON)) {
+		SDE_DEBUG("dpms mode: %d\n", conn->dpms_mode);
+		mutex_unlock(&conn->lock);
+		return;
+	}
+
+	rc = conn->ops.check_status(conn->display);
+	mutex_unlock(&conn->lock);
+
+	if (conn->force_panel_dead) {
+		conn->force_panel_dead--;
+		if (!conn->force_panel_dead)
+			goto status_dead;
+	}
+
+	if (rc > 0) {
+		SDE_DEBUG("esd check status success conn_id: %d enc_id: %d\n",
+				conn->base.base.id, conn->encoder->base.id);
+		schedule_delayed_work(&conn->status_work,
+			msecs_to_jiffies(STATUS_CHECK_INTERVAL_MS));
+		return;
+	}
+
+status_dead:
+	SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+	SDE_ERROR("esd check failed report PANEL_DEAD conn_id: %d enc_id: %d\n",
+			conn->base.base.id, conn->encoder->base.id);
+	panel_dead = true;
+	event.type = DRM_EVENT_PANEL_DEAD;
+	event.length = sizeof(u32);
+	msm_mode_object_event_notify(&conn->base.base,
+		conn->base.dev, &event, (u8 *)&panel_dead);
+}
+
 static const struct drm_connector_helper_funcs sde_connector_helper_ops = {
 	.get_modes =    sde_connector_get_modes,
 	.mode_valid =   sde_connector_mode_valid,
 	.best_encoder = sde_connector_best_encoder,
 };
 
+static int sde_connector_populate_mode_info(struct drm_connector *conn,
+	struct sde_kms_info *info)
+{
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	struct sde_connector *c_conn = NULL;
+	struct drm_display_mode *mode;
+	struct msm_mode_info mode_info;
+	int rc = 0;
+
+	if (!conn || !conn->dev || !conn->dev->dev_private) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	priv = conn->dev->dev_private;
+	sde_kms = to_sde_kms(priv->kms);
+
+	c_conn = to_sde_connector(conn);
+	if (!c_conn->ops.get_mode_info) {
+		SDE_ERROR_CONN(c_conn, "get_mode_info not defined\n");
+		return -EINVAL;
+	}
+
+	list_for_each_entry(mode, &conn->modes, head) {
+		int topology_idx = 0;
+
+		memset(&mode_info, 0, sizeof(mode_info));
+
+		rc = c_conn->ops.get_mode_info(mode, &mode_info,
+			sde_kms->catalog->max_mixer_width,
+			c_conn->display);
+		if (rc) {
+			SDE_ERROR_CONN(c_conn,
+				"failed to get mode info for mode %s\n",
+				mode->name);
+			continue;
+		}
+
+		sde_kms_info_add_keystr(info, "mode_name", mode->name);
+
+		topology_idx = (int)sde_rm_get_topology_name(
+							mode_info.topology);
+		if (topology_idx < SDE_RM_TOPOLOGY_MAX) {
+			sde_kms_info_add_keystr(info, "topology",
+					e_topology_name[topology_idx].name);
+		} else {
+			SDE_ERROR_CONN(c_conn, "invalid topology\n");
+			continue;
+		}
+
+		if (!mode_info.roi_caps.num_roi)
+			continue;
+
+		sde_kms_info_add_keyint(info, "partial_update_num_roi",
+			mode_info.roi_caps.num_roi);
+		sde_kms_info_add_keyint(info, "partial_update_xstart",
+			mode_info.roi_caps.align.xstart_pix_align);
+		sde_kms_info_add_keyint(info, "partial_update_walign",
+			mode_info.roi_caps.align.width_pix_align);
+		sde_kms_info_add_keyint(info, "partial_update_wmin",
+			mode_info.roi_caps.align.min_width);
+		sde_kms_info_add_keyint(info, "partial_update_ystart",
+			mode_info.roi_caps.align.ystart_pix_align);
+		sde_kms_info_add_keyint(info, "partial_update_halign",
+			mode_info.roi_caps.align.height_pix_align);
+		sde_kms_info_add_keyint(info, "partial_update_hmin",
+			mode_info.roi_caps.align.min_height);
+		sde_kms_info_add_keyint(info, "partial_update_roimerge",
+			mode_info.roi_caps.merge_rois);
+	}
+
+	return rc;
+}
+
+int sde_connector_set_blob_data(struct drm_connector *conn,
+		struct drm_connector_state *state,
+		enum msm_mdp_conn_property prop_id)
+{
+	struct sde_kms_info *info;
+	struct sde_connector *c_conn = NULL;
+	struct sde_connector_state *sde_conn_state = NULL;
+	struct msm_mode_info mode_info;
+	struct drm_property_blob *blob = NULL;
+	int rc = 0;
+
+	c_conn = to_sde_connector(conn);
+	if (!c_conn) {
+		SDE_ERROR("invalid argument\n");
+		return -EINVAL;
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	sde_kms_info_reset(info);
+
+	switch (prop_id) {
+	case CONNECTOR_PROP_SDE_INFO:
+		memset(&mode_info, 0, sizeof(mode_info));
+
+		if (state) {
+			sde_conn_state = to_sde_connector_state(state);
+			memcpy(&mode_info, &sde_conn_state->mode_info,
+					sizeof(sde_conn_state->mode_info));
+		} else {
+			/**
+			 * connector state is assigned only on first
+			 * atomic_commit. But this function is allowed to be
+			 * invoked during probe/init sequence. So not throwing
+			 * an error.
+			 */
+			SDE_DEBUG_CONN(c_conn, "invalid connector state\n");
+		}
+
+		if (c_conn->ops.set_info_blob) {
+			rc = c_conn->ops.set_info_blob(conn, info,
+					c_conn->display, &mode_info);
+			if (rc) {
+				SDE_ERROR_CONN(c_conn,
+						"set_info_blob failed, %d\n",
+						rc);
+				goto exit;
+			}
+		}
+
+		blob = c_conn->blob_caps;
+	break;
+	case CONNECTOR_PROP_MODE_INFO:
+		rc = sde_connector_populate_mode_info(conn, info);
+		if (rc) {
+			SDE_ERROR_CONN(c_conn,
+					"mode info population failed, %d\n",
+					rc);
+			goto exit;
+		}
+		blob = c_conn->blob_mode_info;
+	break;
+	default:
+		SDE_ERROR_CONN(c_conn, "invalid prop_id: %d\n", prop_id);
+		goto exit;
+	};
+
+	msm_property_set_blob(&c_conn->property_info,
+			&blob,
+			SDE_KMS_INFO_DATA(info),
+			SDE_KMS_INFO_DATALEN(info),
+			prop_id);
+exit:
+	kfree(info);
+
+	return rc;
+}
+
 struct drm_connector *sde_connector_init(struct drm_device *dev,
 		struct drm_encoder *encoder,
 		struct drm_panel *panel,
@@ -1173,7 +1708,6 @@
 {
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
-	struct sde_kms_info *info;
 	struct sde_connector *c_conn = NULL;
 	struct dsi_display *dsi_display;
 	struct msm_display_info display_info;
@@ -1197,6 +1731,8 @@
 		return ERR_PTR(-ENOMEM);
 	}
 
+	memset(&display_info, 0, sizeof(display_info));
+
 	rc = drm_connector_init(dev,
 			&c_conn->base,
 			&sde_connector_ops,
@@ -1269,34 +1805,32 @@
 			sizeof(struct sde_connector_state));
 
 	if (c_conn->ops.post_init) {
-		info = kmalloc(sizeof(*info), GFP_KERNEL);
-		if (!info) {
-			SDE_ERROR("failed to allocate info buffer\n");
-			rc = -ENOMEM;
-			goto error_cleanup_fence;
-		}
-
-		sde_kms_info_reset(info);
-		rc = c_conn->ops.post_init(&c_conn->base, info, display);
+		rc = c_conn->ops.post_init(&c_conn->base, display);
 		if (rc) {
 			SDE_ERROR("post-init failed, %d\n", rc);
-			kfree(info);
 			goto error_cleanup_fence;
 		}
-
-		msm_property_install_blob(&c_conn->property_info,
-				"capabilities",
-				DRM_MODE_PROP_IMMUTABLE,
-				CONNECTOR_PROP_SDE_INFO);
-
-		msm_property_set_blob(&c_conn->property_info,
-				&c_conn->blob_caps,
-				SDE_KMS_INFO_DATA(info),
-				SDE_KMS_INFO_DATALEN(info),
-				CONNECTOR_PROP_SDE_INFO);
-		kfree(info);
 	}
 
+	msm_property_install_blob(&c_conn->property_info,
+			"capabilities",
+			DRM_MODE_PROP_IMMUTABLE,
+			CONNECTOR_PROP_SDE_INFO);
+
+	rc = sde_connector_set_blob_data(&c_conn->base,
+			NULL,
+			CONNECTOR_PROP_SDE_INFO);
+	if (rc) {
+		SDE_ERROR_CONN(c_conn,
+			"failed to setup connector info, rc = %d\n", rc);
+		goto error_cleanup_fence;
+	}
+
+	msm_property_install_blob(&c_conn->property_info,
+			"mode_properties",
+			DRM_MODE_PROP_IMMUTABLE,
+			CONNECTOR_PROP_MODE_INFO);
+
 	if (connector_type == DRM_MODE_CONNECTOR_DSI) {
 		dsi_display = (struct dsi_display *)(display);
 		if (dsi_display && dsi_display->panel &&
@@ -1315,16 +1849,41 @@
 	}
 
 	rc = sde_connector_get_info(&c_conn->base, &display_info);
-	if (!rc && display_info.roi_caps.enabled) {
-		msm_property_install_volatile_range(
-				&c_conn->property_info, "sde_drm_roi_v1", 0x0,
-				0, ~0, 0, CONNECTOR_PROP_ROI_V1);
-	}
+	if (!rc && (connector_type == DRM_MODE_CONNECTOR_DSI) &&
+			(display_info.capabilities & MSM_DISPLAY_CAP_VID_MODE))
+		sde_connector_register_event(&c_conn->base,
+			SDE_CONN_EVENT_VID_FIFO_OVERFLOW,
+			sde_connector_handle_disp_recovery,
+			c_conn);
+
+	msm_property_install_volatile_range(
+			&c_conn->property_info, "sde_drm_roi_v1", 0x0,
+			0, ~0, 0, CONNECTOR_PROP_ROI_V1);
+
 	/* install PP_DITHER properties */
 	_sde_connector_install_dither_property(dev, sde_kms, c_conn);
 
-	msm_property_install_range(&c_conn->property_info, "RETIRE_FENCE",
-			0x0, 0, INR_OPEN_MAX, 0, CONNECTOR_PROP_RETIRE_FENCE);
+	if (connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+		struct drm_msm_ext_hdr_properties hdr = {0};
+
+		msm_property_install_blob(&c_conn->property_info,
+				"ext_hdr_properties",
+				DRM_MODE_PROP_IMMUTABLE,
+				CONNECTOR_PROP_EXT_HDR_INFO);
+
+		/* set default values to avoid reading uninitialized data */
+		msm_property_set_blob(&c_conn->property_info,
+			      &c_conn->blob_ext_hdr,
+			      &hdr,
+			      sizeof(hdr),
+			      CONNECTOR_PROP_EXT_HDR_INFO);
+	}
+
+	msm_property_install_volatile_range(&c_conn->property_info,
+		"hdr_metadata", 0x0, 0, ~0, 0, CONNECTOR_PROP_HDR_METADATA);
+
+	msm_property_install_volatile_range(&c_conn->property_info,
+		"RETIRE_FENCE", 0x0, 0, ~0, 0, CONNECTOR_PROP_RETIRE_FENCE);
 
 	msm_property_install_range(&c_conn->property_info, "autorefresh",
 			0x0, 0, AUTOREFRESH_MAX_FRAME_CNT, 0,
@@ -1363,6 +1922,9 @@
 
 	priv->connectors[priv->num_connectors++] = &c_conn->base;
 
+	INIT_DELAYED_WORK(&c_conn->status_work,
+			sde_connector_check_status_work);
+
 	return &c_conn->base;
 
 error_destroy_property:
@@ -1372,6 +1934,10 @@
 		drm_property_unreference_blob(c_conn->blob_hdr);
 	if (c_conn->blob_dither)
 		drm_property_unreference_blob(c_conn->blob_dither);
+	if (c_conn->blob_mode_info)
+		drm_property_unreference_blob(c_conn->blob_mode_info);
+	if (c_conn->blob_ext_hdr)
+		drm_property_unreference_blob(c_conn->blob_ext_hdr);
 
 	msm_property_destroy(&c_conn->property_info);
 error_cleanup_fence:
@@ -1394,6 +1960,9 @@
 	case DRM_EVENT_SYS_BACKLIGHT:
 		ret = 0;
 		break;
+	case DRM_EVENT_PANEL_DEAD:
+		ret = 0;
+		break;
 	default:
 		break;
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index b44bfae..a7bad7c 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -36,15 +36,26 @@
 	/**
 	 * post_init - perform additional initialization steps
 	 * @connector: Pointer to drm connector structure
-	 * @info: Pointer to sde connector info structure
 	 * @display: Pointer to private display handle
 	 * Returns: Zero on success
 	 */
 	int (*post_init)(struct drm_connector *connector,
-			void *info,
 			void *display);
 
 	/**
+	 * set_info_blob - initialize given info blob
+	 * @connector: Pointer to drm connector structure
+	 * @info: Pointer to sde connector info structure
+	 * @display: Pointer to private display handle
+	 * @mode_info: Pointer to mode info structure
+	 * Returns: Zero on success
+	 */
+	int (*set_info_blob)(struct drm_connector *connector,
+			void *info,
+			void *display,
+			struct msm_mode_info *mode_info);
+
+	/**
 	 * detect - determine if connector is connected
 	 * @connector: Pointer to drm connector structure
 	 * @force: Force detect setting from drm framework
@@ -134,11 +145,12 @@
 	 * @drm_mode: Display mode set for the display
 	 * @mode_info: Out parameter. information of the display mode
 	 * @max_mixer_width: max width supported by HW layer mixer
+	 * @display: Pointer to private display structure
 	 * Returns: Zero on success
 	 */
 	int (*get_mode_info)(const struct drm_display_mode *drm_mode,
 			struct msm_mode_info *mode_info,
-			u32 max_mixer_width);
+			u32 max_mixer_width, void *display);
 
 	/**
 	 * enable_event - notify display of event registration/unregistration
@@ -199,6 +211,26 @@
 	 * Returns: dst_format of display
 	 */
 	enum dsi_pixel_format (*get_dst_format)(void *display);
+
+	/**
+	 * post_kickoff - display to program post kickoff-time features
+	 * @connector: Pointer to drm connector structure
+	 * Returns: Zero on success
+	 */
+	int (*post_kickoff)(struct drm_connector *connector);
+
+	/**
+	 * send_hpd_event - send HPD uevent notification to userspace
+	 * @display: Pointer to private display structure
+	 */
+	void (*send_hpd_event)(void *display);
+
+	/**
+	 * check_status - check status of connected display panel
+	 * @display: Pointer to private display handle
+	 * Returns: positive value for success, negetive or zero for failure
+	 */
+	int (*check_status)(void *display);
 };
 
 /**
@@ -207,6 +239,8 @@
 enum sde_connector_events {
 	SDE_CONN_EVENT_VID_DONE, /* video mode frame done */
 	SDE_CONN_EVENT_CMD_DONE, /* command mode frame done */
+	SDE_CONN_EVENT_VID_FIFO_OVERFLOW, /* dsi fifo overflow error */
+	SDE_CONN_EVENT_CMD_FIFO_UNDERFLOW, /* dsi fifo underflow error */
 	SDE_CONN_EVENT_COUNT,
 };
 
@@ -214,9 +248,10 @@
  * struct sde_connector_evt - local event registration entry structure
  * @cb_func: Pointer to desired callback function
  * @usr: User pointer to pass to callback on event trigger
+ * Returns: Zero success, negetive for failure
  */
 struct sde_connector_evt {
-	void (*cb_func)(uint32_t event_idx,
+	int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3);
@@ -243,10 +278,15 @@
  * @property_data: Array of private data for generic property handling
  * @blob_caps: Pointer to blob structure for 'capabilities' property
  * @blob_hdr: Pointer to blob structure for 'hdr_properties' property
+ * @blob_ext_hdr: Pointer to blob structure for 'ext_hdr_properties' property
  * @blob_dither: Pointer to blob structure for default dither config
+ * @blob_mode_info: Pointer to blob structure for mode info
  * @fb_kmap: true if kernel mapping of framebuffer is requested
  * @event_table: Array of registered events
  * @event_lock: Lock object for event_table
+ * @bl_device: backlight device node
+ * @status_work: work object to perform status checks
+ * @force_panel_dead: variable to trigger forced ESD recovery
  */
 struct sde_connector {
 	struct drm_connector base;
@@ -272,11 +312,17 @@
 	struct msm_property_data property_data[CONNECTOR_PROP_COUNT];
 	struct drm_property_blob *blob_caps;
 	struct drm_property_blob *blob_hdr;
+	struct drm_property_blob *blob_ext_hdr;
 	struct drm_property_blob *blob_dither;
+	struct drm_property_blob *blob_mode_info;
 
 	bool fb_kmap;
 	struct sde_connector_evt event_table[SDE_CONN_EVENT_COUNT];
 	spinlock_t event_lock;
+
+	struct backlight_device *bl_device;
+	struct delayed_work status_work;
+	u32 force_panel_dead;
 };
 
 /**
@@ -292,7 +338,7 @@
  * Returns: Pointer to associated private display structure
  */
 #define sde_connector_get_display(C) \
-	((C) ? to_sde_connector((C))->display : 0)
+	((C) ? to_sde_connector((C))->display : NULL)
 
 /**
  * sde_connector_get_panel - get sde connector's private panel pointer
@@ -308,7 +354,7 @@
  * Returns: Pointer to associated private encoder structure
  */
 #define sde_connector_get_encoder(C) \
-	((C) ? to_sde_connector((C))->encoder : 0)
+	((C) ? to_sde_connector((C))->encoder : NULL)
 
 /**
  * sde_connector_get_propinfo - get sde connector's property info pointer
@@ -316,7 +362,7 @@
  * Returns: Pointer to associated private property info structure
  */
 #define sde_connector_get_propinfo(C) \
-	((C) ? &to_sde_connector((C))->property_info : 0)
+	((C) ? &to_sde_connector((C))->property_info : NULL)
 
 /**
  * struct sde_connector_state - private connector status structure
@@ -326,6 +372,10 @@
  * @property_values: Local cache of current connector property values
  * @rois: Regions of interest structure for mapping CRTC to Connector output
  * @property_blobs: blob properties
+ * @mode_info: local copy of msm_mode_info struct
+ * @hdr_meta: HDR metadata info passed from userspace
+ * @old_topology_name: topology of previous atomic state. remove this in later
+ *	kernel versions which provide drm_atomic_state old_state pointers
  */
 struct sde_connector_state {
 	struct drm_connector_state base;
@@ -335,6 +385,9 @@
 
 	struct msm_roi_list rois;
 	struct drm_property_blob *property_blobs[CONNECTOR_PROP_BLOBCOUNT];
+	struct msm_mode_info mode_info;
+	struct drm_msm_ext_hdr_metadata hdr_meta;
+	enum sde_rm_topology_name old_topology_name;
 };
 
 /**
@@ -387,6 +440,43 @@
 }
 
 /**
+ * sde_connector_get_old_topology_name - helper accessor to retrieve
+ *	topology_name for the previous mode
+ * @connector: pointer to drm connector state
+ * Returns: cached value of the previous topology, or SDE_RM_TOPOLOGY_NONE
+ */
+static inline enum sde_rm_topology_name sde_connector_get_old_topology_name(
+		struct drm_connector_state *state)
+{
+	struct sde_connector_state *c_state = to_sde_connector_state(state);
+
+	if (!state)
+		return SDE_RM_TOPOLOGY_NONE;
+
+	return c_state->old_topology_name;
+}
+
+/**
+ * sde_connector_set_old_topology_name - helper to cache value of previous
+ *	mode's topology
+ * @connector: pointer to drm connector state
+ * Returns: 0 on success, negative errno on failure
+ */
+static inline int sde_connector_set_old_topology_name(
+		struct drm_connector_state *state,
+		enum sde_rm_topology_name top)
+{
+	struct sde_connector_state *c_state = to_sde_connector_state(state);
+
+	if (!state)
+		return -EINVAL;
+
+	c_state->old_topology_name = top;
+
+	return 0;
+}
+
+/**
  * sde_connector_get_lp - helper accessor to retrieve LP state
  * @connector: pointer to drm connector
  * Returns: value of the CONNECTOR_PROP_LP property or 0
@@ -466,8 +556,9 @@
  * sde_connector_clk_ctrl - enables/disables the connector clks
  * @connector: Pointer to drm connector object
  * @enable: true/false to enable/disable
+ * Returns: Zero on success
  */
-void sde_connector_clk_ctrl(struct drm_connector *connector, bool enable);
+int sde_connector_clk_ctrl(struct drm_connector *connector, bool enable);
 
 /**
  * sde_connector_get_dpms - query dpms setting
@@ -504,7 +595,7 @@
  */
 int sde_connector_register_event(struct drm_connector *connector,
 		uint32_t event_idx,
-		void (*cb_func)(uint32_t event_idx,
+		int (*cb_func)(uint32_t event_idx,
 			uint32_t instance_idx, void *usr,
 			uint32_t data0, uint32_t data1,
 			uint32_t data2, uint32_t data3),
@@ -564,4 +655,54 @@
 int sde_connector_get_dither_cfg(struct drm_connector *conn,
 		struct drm_connector_state *state, void **cfg, size_t *len);
 
+/**
+ * sde_connector_set_blob_data - set connector blob property data
+ * @conn: Pointer to drm_connector struct
+ * @state: Pointer to the drm_connector_state struct
+ * @prop_id: property id to be populated
+ * Returns: Zero on success
+ */
+int sde_connector_set_blob_data(struct drm_connector *conn,
+		struct drm_connector_state *state,
+		enum msm_mdp_conn_property prop_id);
+
+/**
+ * sde_connector_roi_v1_check_roi - validate connector ROI
+ * @conn_state: Pointer to drm_connector_state struct
+ * Returns: Zero on success
+ */
+int sde_connector_roi_v1_check_roi(struct drm_connector_state *conn_state);
+
+/**
+ * sde_connector_schedule_status_work - manage ESD thread
+ * conn: Pointer to drm_connector struct
+ * @en: flag to start/stop ESD thread
+ */
+void sde_connector_schedule_status_work(struct drm_connector *conn, bool en);
+
+/**
+ * sde_connector_helper_reset_properties - reset properties to default values in
+ *	the given DRM connector state object
+ * @connector: Pointer to DRM connector object
+ * @connector_state: Pointer to DRM connector state object
+ * Returns: 0 on success, negative errno on failure
+ */
+int sde_connector_helper_reset_custom_properties(
+		struct drm_connector *connector,
+		struct drm_connector_state *connector_state);
+
+/**
+ * sde_connector_get_mode_info - get information of the current mode in the
+ *                               given connector state.
+ * conn_state: Pointer to the DRM connector state object
+ * mode_info: Pointer to the mode info structure
+ */
+int sde_connector_get_mode_info(struct drm_connector_state *conn_state,
+	struct msm_mode_info *mode_info);
+
+/**
+ * sde_conn_timeline_status - current buffer timeline status
+ * conn: Pointer to drm_connector struct
+ */
+void sde_conn_timeline_status(struct drm_connector *conn);
 #endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index 1402fdd..a6f22c9 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -31,14 +31,17 @@
 	struct sde_irq *irq_obj = &sde_kms->irq_obj;
 	struct sde_irq_callback *cb;
 	unsigned long irq_flags;
+	bool cb_tbl_error = false;
+	int enable_counts = 0;
 
 	pr_debug("irq_idx=%d\n", irq_idx);
 
+	spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
 	if (list_empty(&irq_obj->irq_cb_tbl[irq_idx])) {
-		SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx);
-		SDE_EVT32_IRQ(irq_idx, atomic_read(
-				&sde_kms->irq_obj.enable_counts[irq_idx]),
-				SDE_EVTLOG_ERROR);
+		/* print error outside lock */
+		cb_tbl_error = true;
+		enable_counts = atomic_read(
+				&sde_kms->irq_obj.enable_counts[irq_idx]);
 	}
 
 	atomic_inc(&irq_obj->irq_counts[irq_idx]);
@@ -46,12 +49,30 @@
 	/*
 	 * Perform registered function callback
 	 */
-	spin_lock_irqsave(&sde_kms->irq_obj.cb_lock, irq_flags);
 	list_for_each_entry(cb, &irq_obj->irq_cb_tbl[irq_idx], list)
 		if (cb->func)
 			cb->func(cb->arg, irq_idx);
 	spin_unlock_irqrestore(&sde_kms->irq_obj.cb_lock, irq_flags);
 
+	if (cb_tbl_error) {
+		/*
+		 * If enable count is zero and callback list is empty, then it's
+		 * not a fatal issue. Log this case as debug. If the enable
+		 * count is nonzero and callback list is empty, then its a real
+		 * issue. Log this case as error to ensure we don't have silent
+		 * IRQs running.
+		 */
+		if (!enable_counts) {
+			SDE_DEBUG("irq has no callback, idx %d enables %d\n",
+					irq_idx, enable_counts);
+			SDE_EVT32_IRQ(irq_idx, enable_counts);
+		} else {
+			SDE_ERROR("irq has no callback, idx %d enables %d\n",
+					irq_idx, enable_counts);
+			SDE_EVT32_IRQ(irq_idx, enable_counts, SDE_EVTLOG_ERROR);
+		}
+	}
+
 	/*
 	 * Clear pending interrupt status in HW.
 	 * NOTE: sde_core_irq_callback_handler is protected by top-level
@@ -439,6 +460,7 @@
 {
 	struct msm_drm_private *priv;
 	int i;
+	int rc;
 
 	if (!sde_kms) {
 		SDE_ERROR("invalid sde_kms\n");
@@ -452,7 +474,14 @@
 	}
 	priv = sde_kms->dev->dev_private;
 
-	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+	rc = sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+			true);
+	if (rc) {
+		SDE_ERROR("failed to enable power resource %d\n", rc);
+		SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+		return;
+	}
+
 	sde_clear_all_irqs(sde_kms);
 	sde_disable_all_irqs(sde_kms);
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
@@ -483,6 +512,7 @@
 {
 	struct msm_drm_private *priv;
 	int i;
+	int rc;
 
 	if (!sde_kms) {
 		SDE_ERROR("invalid sde_kms\n");
@@ -496,7 +526,14 @@
 	}
 	priv = sde_kms->dev->dev_private;
 
-	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+	rc = sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+			true);
+	if (rc) {
+		SDE_ERROR("failed to enable power resource %d\n", rc);
+		SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+		return;
+	}
+
 	for (i = 0; i < sde_kms->irq_obj.total_irqs; i++)
 		if (atomic_read(&sde_kms->irq_obj.enable_counts[i]) ||
 				!list_empty(&sde_kms->irq_obj.irq_cb_tbl[i]))
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 7243fe2..6b36745 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_perf.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_perf.c
@@ -169,6 +169,7 @@
 		perf->core_clk_rate = kms->perf.fix_core_clk_rate;
 	}
 
+	SDE_EVT32(crtc->base.id, perf->core_clk_rate);
 	SDE_DEBUG(
 		"crtc=%d clk_rate=%llu core_ib=%llu core_ab=%llu llcc_ib=%llu llcc_ab=%llu mem_ib=%llu mem_ab=%llu\n",
 			crtc->base.id, perf->core_clk_rate,
@@ -293,7 +294,8 @@
 }
 
 static void _sde_core_perf_crtc_update_bus(struct sde_kms *kms,
-		struct drm_crtc *crtc, u32 bus_id)
+		struct drm_crtc *crtc, u32 bus_id,
+		struct sde_core_perf_params *crtc_perf)
 {
 	u64 bw_sum_of_intfs = 0, bus_ab_quota, bus_ib_quota;
 	struct sde_core_perf_params perf = { { 0 } };
@@ -303,21 +305,36 @@
 	struct sde_crtc_state *sde_cstate;
 	struct msm_drm_private *priv = kms->dev->dev_private;
 
+	u64 tmp_max_per_pipe_ib;
+	u64 tmp_bw_ctl;
+
 	drm_for_each_crtc(tmp_crtc, crtc->dev) {
 		if (_sde_core_perf_crtc_is_power_on(tmp_crtc) &&
 		    _is_crtc_client_type_matches(tmp_crtc, curr_client_type,
 								&kms->perf)) {
-			sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+
+			if (crtc->base.id == tmp_crtc->base.id) {
+				/* for current crtc use the cached values */
+				tmp_max_per_pipe_ib =
+					crtc_perf->max_per_pipe_ib[bus_id];
+				tmp_bw_ctl = crtc_perf->bw_ctl[bus_id];
+			} else {
+				sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+				tmp_max_per_pipe_ib =
+				  sde_cstate->new_perf.max_per_pipe_ib[bus_id];
+				tmp_bw_ctl =
+				  sde_cstate->new_perf.bw_ctl[bus_id];
+			}
 
 			perf.max_per_pipe_ib[bus_id] =
 				max(perf.max_per_pipe_ib[bus_id],
-				sde_cstate->new_perf.max_per_pipe_ib[bus_id]);
+				tmp_max_per_pipe_ib);
 
-			bw_sum_of_intfs += sde_cstate->new_perf.bw_ctl[bus_id];
+			bw_sum_of_intfs += tmp_bw_ctl;
 
-			SDE_DEBUG("crtc=%d bus_id=%d bw=%llu\n",
+			SDE_DEBUG("crtc=%d bus_id=%d bw=%llu perf_pipe:%llu\n",
 				tmp_crtc->base.id, bus_id,
-				sde_cstate->new_perf.bw_ctl[bus_id]);
+				tmp_bw_ctl, tmp_max_per_pipe_ib);
 		}
 	}
 
@@ -442,22 +459,32 @@
 		SDE_DEBUG("Release BW crtc=%d\n", crtc->base.id);
 		for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
 			sde_crtc->cur_perf.bw_ctl[i] = 0;
-			_sde_core_perf_crtc_update_bus(kms, crtc, i);
+			_sde_core_perf_crtc_update_bus(kms, crtc, i,
+				&sde_crtc->cur_perf);
 		}
 	}
 }
 
-static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms)
+static u64 _sde_core_perf_get_core_clk_rate(struct sde_kms *kms,
+	struct sde_core_perf_params *crct_perf, struct drm_crtc *crtc)
 {
 	u64 clk_rate = kms->perf.perf_tune.min_core_clk;
-	struct drm_crtc *crtc;
+	struct drm_crtc *tmp_crtc;
 	struct sde_crtc_state *sde_cstate;
+	u64 tmp_rate;
 
-	drm_for_each_crtc(crtc, kms->dev) {
-		if (_sde_core_perf_crtc_is_power_on(crtc)) {
-			sde_cstate = to_sde_crtc_state(crtc->state);
-			clk_rate = max(sde_cstate->new_perf.core_clk_rate,
-							clk_rate);
+	drm_for_each_crtc(tmp_crtc, kms->dev) {
+		if (_sde_core_perf_crtc_is_power_on(tmp_crtc)) {
+
+			if (crtc->base.id == tmp_crtc->base.id) {
+				/* for current CRTC, use the cached value */
+				tmp_rate = crct_perf->core_clk_rate;
+			} else {
+				sde_cstate = to_sde_crtc_state(tmp_crtc->state);
+				tmp_rate = sde_cstate->new_perf.core_clk_rate;
+			}
+			clk_rate = max(tmp_rate, clk_rate);
+
 			clk_rate = clk_round_rate(kms->perf.core_clk, clk_rate);
 		}
 	}
@@ -504,8 +531,17 @@
 	SDE_DEBUG("crtc:%d stop_req:%d core_clk:%llu\n",
 			crtc->base.id, stop_req, kms->perf.core_clk_rate);
 
+	/*
+	 * cache the performance numbers in the crtc prior to the
+	 * crtc kickoff, so the same numbers are used during the
+	 * perf update that happens post kickoff.
+	 */
+	if (params_changed)
+		memcpy(&sde_crtc->new_perf, &sde_cstate->new_perf,
+			sizeof(struct sde_core_perf_params));
+
 	old = &sde_crtc->cur_perf;
-	new = &sde_cstate->new_perf;
+	new = &sde_crtc->new_perf;
 
 	if (_sde_core_perf_crtc_is_power_on(crtc) && !stop_req) {
 		for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
@@ -580,7 +616,7 @@
 
 	for (i = 0; i < SDE_POWER_HANDLE_DBUS_ID_MAX; i++) {
 		if (update_bus & BIT(i))
-			_sde_core_perf_crtc_update_bus(kms, crtc, i);
+			_sde_core_perf_crtc_update_bus(kms, crtc, i, old);
 	}
 
 	/*
@@ -588,9 +624,10 @@
 	 * bandwidth is available before clock rate is increased.
 	 */
 	if (update_clk) {
-		clk_rate = _sde_core_perf_get_core_clk_rate(kms);
+		clk_rate = _sde_core_perf_get_core_clk_rate(kms, old, crtc);
 
-		SDE_EVT32(kms->dev, stop_req, clk_rate);
+		SDE_EVT32(kms->dev, stop_req, clk_rate, params_changed,
+			old->core_clk_rate, new->core_clk_rate);
 		ret = sde_power_clk_set_rate(&priv->phandle,
 				kms->perf.clk_name, clk_rate);
 		if (ret) {
@@ -722,6 +759,12 @@
 			(u32 *)&catalog->perf.max_bw_low);
 	debugfs_create_u32("threshold_high", 0600, perf->debugfs_root,
 			(u32 *)&catalog->perf.max_bw_high);
+	debugfs_create_u32("min_core_ib", 0600, perf->debugfs_root,
+			(u32 *)&catalog->perf.min_core_ib);
+	debugfs_create_u32("min_llcc_ib", 0600, perf->debugfs_root,
+			(u32 *)&catalog->perf.min_llcc_ib);
+	debugfs_create_u32("min_dram_ib", 0600, perf->debugfs_root,
+			(u32 *)&catalog->perf.min_dram_ib);
 	debugfs_create_file("perf_mode", 0600, perf->debugfs_root,
 			(u32 *)perf, &sde_core_perf_mode_fops);
 	debugfs_create_u32("bw_vote_mode", 0600, perf->debugfs_root,
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index cac7893..f68f64d 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -48,13 +48,8 @@
 #define MEM_PROTECT_SD_CTRL_SWITCH 0x18
 #define MDP_DEVICE_ID            0x1A
 
-struct sde_crtc_irq_info {
-	struct sde_irq_callback irq;
-	u32 event;
-	int (*func)(struct drm_crtc *crtc, bool en,
-			struct sde_irq_callback *irq);
-	struct list_head list;
-};
+#define SDE_PSTATES_MAX (SDE_STAGE_MAX * 4)
+#define SDE_MULTIRECT_PLANE_MAX (SDE_STAGE_MAX * 2)
 
 struct sde_crtc_custom_events {
 	u32 event;
@@ -66,11 +61,15 @@
 	bool en, struct sde_irq_callback *ad_irq);
 static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm,
 	bool en, struct sde_irq_callback *idle_irq);
+static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en,
+		struct sde_irq_callback *noirq);
 
 static struct sde_crtc_custom_events custom_events[] = {
 	{DRM_EVENT_AD_BACKLIGHT, sde_cp_ad_interrupt},
 	{DRM_EVENT_CRTC_POWER, sde_crtc_power_interrupt_handler},
-	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler}
+	{DRM_EVENT_IDLE_NOTIFY, sde_crtc_idle_interrupt_handler},
+	{DRM_EVENT_HISTOGRAM, sde_cp_hist_interrupt},
+	{DRM_EVENT_SDE_POWER, sde_crtc_pm_event_handler},
 };
 
 /* default input fence timeout, in ms */
@@ -639,8 +638,9 @@
 {
 	SDE_DEBUG("\n");
 
-	if (msm_is_mode_seamless(adjusted_mode) &&
-		(!crtc->enabled || crtc->state->active_changed)) {
+	if ((msm_is_mode_seamless(adjusted_mode) ||
+			msm_is_mode_seamless_vrr(adjusted_mode)) &&
+		(!crtc->enabled)) {
 		SDE_ERROR("crtc state prevents seamless transition\n");
 		return false;
 	}
@@ -790,7 +790,7 @@
 }
 
 static int _sde_crtc_set_roi_v1(struct drm_crtc_state *state,
-		void *usr_ptr)
+		void __user *usr_ptr)
 {
 	struct drm_crtc *crtc;
 	struct sde_crtc_state *cstate;
@@ -839,6 +839,11 @@
 				cstate->user_roi_list.roi[i].y1,
 				cstate->user_roi_list.roi[i].x2,
 				cstate->user_roi_list.roi[i].y2);
+		SDE_EVT32_VERBOSE(DRMID(crtc),
+				cstate->user_roi_list.roi[i].x1,
+				cstate->user_roi_list.roi[i].y1,
+				cstate->user_roi_list.roi[i].x2,
+				cstate->user_roi_list.roi[i].y2);
 	}
 
 	return 0;
@@ -882,6 +887,7 @@
 
 	for_each_connector_in_state(state->state, conn, conn_state, i) {
 		struct sde_connector_state *sde_conn_state;
+		struct sde_rect conn_roi;
 
 		if (!conn_state || conn_state->crtc != crtc)
 			continue;
@@ -908,29 +914,19 @@
 					sde_crtc->name);
 			return -EINVAL;
 		}
+
+		sde_kms_rect_merge_rectangles(&sde_conn_state->rois, &conn_roi);
+		SDE_EVT32_VERBOSE(DRMID(crtc), DRMID(conn),
+				conn_roi.x, conn_roi.y,
+				conn_roi.w, conn_roi.h);
 	}
 
 	sde_kms_rect_merge_rectangles(&crtc_state->user_roi_list, crtc_roi);
 
-	/*
-	 * for 3dmux dsc, make sure is full ROI, since current driver doesn't
-	 * support partial update for this configuration.
-	 */
-	if (!sde_kms_rect_is_null(crtc_roi) &&
-		_sde_crtc_setup_is_3dmux_dsc(state)) {
-		struct drm_display_mode *adj_mode = &state->adjusted_mode;
-
-		if (crtc_roi->w != adj_mode->hdisplay ||
-			crtc_roi->h != adj_mode->vdisplay) {
-			SDE_ERROR("%s: unsupported top roi[%d %d] wxh[%d %d]\n",
-				sde_crtc->name, crtc_roi->w, crtc_roi->h,
-				adj_mode->hdisplay, adj_mode->vdisplay);
-			return -EINVAL;
-		}
-	}
-
 	SDE_DEBUG("%s: crtc roi (%d,%d,%d,%d)\n", sde_crtc->name,
 			crtc_roi->x, crtc_roi->y, crtc_roi->w, crtc_roi->h);
+	SDE_EVT32_VERBOSE(DRMID(crtc), crtc_roi->x, crtc_roi->y, crtc_roi->w,
+			crtc_roi->h);
 
 	return 0;
 }
@@ -999,6 +995,18 @@
 	SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx,
 			lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);
 
+	/*
+	 * partial update is not supported with 3dmux dsc or dest scaler.
+	 * hence, crtc roi must match the mixer dimensions.
+	 */
+	if (crtc_state->num_ds_enabled ||
+		_sde_crtc_setup_is_3dmux_dsc(state)) {
+		if (memcmp(lm_roi, lm_bounds, sizeof(struct sde_rect))) {
+			SDE_ERROR("Unsupported: Dest scaler/3d mux DSC + PU\n");
+			return -EINVAL;
+		}
+	}
+
 	/* if any dimension is zero, clear all dimensions for clarity */
 	if (sde_kms_rect_is_null(lm_roi))
 		memset(lm_roi, 0, sizeof(*lm_roi));
@@ -1122,7 +1130,7 @@
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *crtc_state;
 	const struct sde_rect *crtc_roi;
-	struct drm_plane_state *pstate;
+	const struct drm_plane_state *pstate;
 	struct drm_plane *plane;
 
 	if (!crtc || !state)
@@ -1141,10 +1149,9 @@
 	if (sde_kms_rect_is_null(crtc_roi))
 		return 0;
 
-	drm_atomic_crtc_state_for_each_plane(plane, state) {
+	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
 		struct sde_rect plane_roi, intersection;
 
-		pstate = drm_atomic_get_plane_state(state->state, plane);
 		if (IS_ERR_OR_NULL(pstate)) {
 			int rc = PTR_ERR(pstate);
 
@@ -1177,14 +1184,56 @@
 		struct drm_crtc_state *state)
 {
 	struct sde_crtc *sde_crtc;
-	int lm_idx;
-	int rc;
+	struct sde_crtc_state *sde_crtc_state;
+	struct msm_mode_info mode_info;
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	int rc, lm_idx, i;
 
 	if (!crtc || !state)
 		return -EINVAL;
 
+	memset(&mode_info, 0, sizeof(mode_info));
+
 	sde_crtc = to_sde_crtc(crtc);
 
+	if (hweight_long(state->connector_mask) != 1) {
+		SDE_ERROR("invalid connector count(%d) for crtc: %d\n",
+			(int)hweight_long(state->connector_mask),
+			crtc->base.id);
+		return -EINVAL;
+	}
+
+	for_each_connector_in_state(state->state, conn, conn_state, i) {
+		rc = sde_connector_get_mode_info(conn_state, &mode_info);
+		if (rc) {
+			SDE_ERROR("failed to get mode info\n");
+			return -EINVAL;
+		}
+		break;
+	}
+
+	if (!mode_info.roi_caps.enabled)
+		return 0;
+
+	sde_crtc_state = to_sde_crtc_state(state);
+	if (sde_crtc_state->user_roi_list.num_rects >
+					mode_info.roi_caps.num_roi) {
+		SDE_ERROR("roi count is more than supported limit, %d > %d\n",
+				sde_crtc_state->user_roi_list.num_rects,
+				mode_info.roi_caps.num_roi);
+		return -E2BIG;
+	}
+
+	/**
+	 * TODO: Need to check against ROI alignment restrictions if partial
+	 * update support is added for destination scalar configurations
+	 */
+	if (sde_crtc_state->num_ds_enabled) {
+		SDE_ERROR("DS and PU concurrency is not supported\n");
+		return -EINVAL;
+	}
+
 	rc = _sde_crtc_set_crtc_roi(crtc, state);
 	if (rc)
 		return rc;
@@ -1263,7 +1312,7 @@
 	struct sde_hw_stage_cfg *stage_cfg;
 	struct sde_rect plane_crtc_roi;
 
-	u32 flush_mask, flush_sbuf, flush_tmp;
+	u32 flush_mask, flush_sbuf;
 	uint32_t stage_idx, lm_idx;
 	int zpos_cnt[SDE_STAGE_MAX + 1] = { 0 };
 	int i;
@@ -1279,10 +1328,9 @@
 	lm = mixer->hw_lm;
 	stage_cfg = &sde_crtc->stage_cfg;
 	cstate = to_sde_crtc_state(crtc->state);
-	flush_sbuf = 0x0;
 
-	cstate->sbuf_cfg.rot_op_mode = SDE_CTL_ROT_OP_MODE_OFFLINE;
 	cstate->sbuf_prefill_line = 0;
+	sde_crtc->sbuf_flush_mask = 0x0;
 
 	drm_atomic_crtc_for_each_plane(plane, crtc) {
 		state = plane->state;
@@ -1297,17 +1345,14 @@
 		pstate = to_sde_plane_state(state);
 		fb = state->fb;
 
-		if (sde_plane_is_sbuf_mode(plane, &prefill))
-			cstate->sbuf_cfg.rot_op_mode =
-					SDE_CTL_ROT_OP_MODE_INLINE_SYNC;
+		prefill = sde_plane_rot_calc_prefill(plane);
 		if (prefill > cstate->sbuf_prefill_line)
 			cstate->sbuf_prefill_line = prefill;
 
-		sde_plane_get_ctl_flush(plane, ctl, &flush_mask, &flush_tmp);
+		sde_plane_get_ctl_flush(plane, ctl, &flush_mask, &flush_sbuf);
 
-		/* persist rotator flush bit(s) for one more commit */
-		flush_mask |= cstate->sbuf_flush_mask | flush_tmp;
-		flush_sbuf |= flush_tmp;
+		/* save sbuf flush value for later */
+		sde_crtc->sbuf_flush_mask |= flush_sbuf;
 
 		SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n",
 				crtc->base.id,
@@ -1331,8 +1376,7 @@
 				state->src_w >> 16, state->src_h >> 16,
 				state->crtc_x, state->crtc_y,
 				state->crtc_w, state->crtc_h,
-				flush_tmp ? cstate->sbuf_cfg.rot_op_mode :
-				SDE_CTL_ROT_OP_MODE_OFFLINE);
+				flush_sbuf != 0);
 
 		stage_idx = zpos_cnt[pstate->stage]++;
 		stage_cfg->stage[pstate->stage][stage_idx] =
@@ -1359,8 +1403,6 @@
 		}
 	}
 
-	cstate->sbuf_flush_mask = flush_sbuf;
-
 	if (lm && lm->ops.setup_dim_layer) {
 		cstate = to_sde_crtc_state(crtc->state);
 		for (i = 0; i < cstate->num_dim_layers; i++)
@@ -1438,7 +1480,7 @@
  * _sde_crtc_blend_setup - configure crtc mixers
  * @crtc: Pointer to drm crtc structure
  */
-static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
+static void _sde_crtc_blend_setup(struct drm_crtc *crtc, bool add_planes)
 {
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *sde_crtc_state;
@@ -1484,7 +1526,8 @@
 	/* initialize stage cfg */
 	memset(&sde_crtc->stage_cfg, 0, sizeof(struct sde_hw_stage_cfg));
 
-	_sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);
+	if (add_planes)
+		_sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);
 
 	for (i = 0; i < sde_crtc->num_mixers; i++) {
 		const struct sde_rect *lm_roi = &sde_crtc_state->lm_roi[i];
@@ -1502,6 +1545,7 @@
 
 		lm->ops.setup_alpha_out(lm, mixer[i].mixer_op_mode);
 
+		mixer[i].pipe_mask = mixer[i].flush_mask;
 		mixer[i].flush_mask |= ctl->ops.get_bitmask_mixer(ctl,
 			mixer[i].hw_lm->idx);
 
@@ -1524,7 +1568,6 @@
 static int _sde_crtc_find_plane_fb_modes(struct drm_crtc_state *state,
 		uint32_t *fb_ns,
 		uint32_t *fb_sec,
-		uint32_t *fb_ns_dir,
 		uint32_t *fb_sec_dir)
 {
 	struct drm_plane *plane;
@@ -1540,7 +1583,6 @@
 
 	*fb_ns = 0;
 	*fb_sec = 0;
-	*fb_ns_dir = 0;
 	*fb_sec_dir = 0;
 	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
 		if (IS_ERR_OR_NULL(pstate)) {
@@ -1560,16 +1602,12 @@
 		case SDE_DRM_FB_SEC:
 			(*fb_sec)++;
 			break;
-		case SDE_DRM_FB_NON_SEC_DIR_TRANS:
-			(*fb_ns_dir)++;
-			break;
 		case SDE_DRM_FB_SEC_DIR_TRANS:
 			(*fb_sec_dir)++;
 			break;
 		default:
 			SDE_ERROR("Error: Plane[%d], fb_trans_mode:%d",
-					plane->base.id,
-					mode);
+					plane->base.id, mode);
 			return -EINVAL;
 		}
 	}
@@ -1593,7 +1631,7 @@
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *cstate;
 	struct sde_crtc_smmu_state_data *smmu_state;
-	uint32_t translation_mode = 0;
+	uint32_t translation_mode = 0, secure_level;
 	int ops  = 0;
 	bool post_commit = false;
 
@@ -1605,11 +1643,13 @@
 	sde_crtc = to_sde_crtc(crtc);
 	cstate = to_sde_crtc_state(crtc->state);
 	smmu_state = &sde_crtc->smmu_state;
+	secure_level = sde_crtc_get_secure_level(crtc, crtc->state);
 
-	SDE_DEBUG("crtc%d, secure_level%d\n",
-			crtc->base.id,
-			sde_crtc_get_secure_level(crtc, crtc->state));
+	SDE_DEBUG("crtc%d, secure_level%d old_valid_fb%d\n",
+			crtc->base.id, secure_level, old_valid_fb);
 
+	SDE_EVT32_VERBOSE(DRMID(crtc), secure_level, smmu_state->state,
+			old_valid_fb, SDE_EVTLOG_FUNC_ENTRY);
 	/**
 	 * SMMU operations need to be delayed in case of
 	 * video mode panels when switching back to non_secure
@@ -1619,7 +1659,7 @@
 		if (encoder->crtc != crtc)
 			continue;
 
-		post_commit &= sde_encoder_check_mode(encoder,
+		post_commit |= sde_encoder_check_mode(encoder,
 						MSM_DISPLAY_CAP_VID_MODE);
 	}
 
@@ -1632,8 +1672,7 @@
 				PLANE_PROP_FB_TRANSLATION_MODE);
 		if (translation_mode > SDE_DRM_FB_SEC_DIR_TRANS) {
 			SDE_ERROR("crtc%d, invalid translation_mode%d\n",
-					crtc->base.id,
-					translation_mode);
+					crtc->base.id, translation_mode);
 			return -EINVAL;
 		}
 
@@ -1641,14 +1680,15 @@
 		 * we can break if we find sec_fir or non_sec_dir
 		 * plane
 		 */
-		if ((translation_mode == SDE_DRM_FB_NON_SEC_DIR_TRANS) ||
-			(translation_mode == SDE_DRM_FB_SEC_DIR_TRANS))
+		if (translation_mode == SDE_DRM_FB_SEC_DIR_TRANS)
 			break;
 	}
 
 	switch (translation_mode) {
-	case SDE_DRM_FB_NON_SEC_DIR_TRANS:
-		if (smmu_state->state == ATTACHED) {
+	case SDE_DRM_FB_SEC_DIR_TRANS:
+		/* secure display usecase */
+		if ((smmu_state->state == ATTACHED) &&
+				(secure_level == SDE_DRM_SEC_ONLY)) {
 			smmu_state->state = DETACH_ALL_REQ;
 			smmu_state->transition_type = PRE_COMMIT;
 			ops |= SDE_KMS_OPS_CRTC_SECURE_STATE_CHANGE;
@@ -1656,10 +1696,8 @@
 				ops |= (SDE_KMS_OPS_WAIT_FOR_TX_DONE  |
 					SDE_KMS_OPS_CLEANUP_PLANE_FB);
 			}
-		}
-		break;
-	case SDE_DRM_FB_SEC_DIR_TRANS:
-		if (smmu_state->state == ATTACHED) {
+		/* secure camera usecase */
+		} else if (smmu_state->state == ATTACHED) {
 			smmu_state->state = DETACH_SEC_REQ;
 			smmu_state->transition_type = PRE_COMMIT;
 			ops |= SDE_KMS_OPS_CRTC_SECURE_STATE_CHANGE;
@@ -1667,34 +1705,38 @@
 		break;
 	case SDE_DRM_FB_SEC:
 	case SDE_DRM_FB_NON_SEC:
-		if (smmu_state->state == DETACHED_SEC) {
+		if ((smmu_state->state == DETACHED_SEC) ||
+			(smmu_state->state == DETACH_SEC_REQ)) {
 			smmu_state->state = ATTACH_SEC_REQ;
 			smmu_state->transition_type = post_commit ?
 				POST_COMMIT : PRE_COMMIT;
 			ops |= SDE_KMS_OPS_CRTC_SECURE_STATE_CHANGE;
-			if (translation_mode == SDE_DRM_FB_SEC)
-				ops |= SDE_KMS_OPS_PREPARE_PLANE_FB;
-		} else if (smmu_state->state == DETACHED) {
+			if (old_valid_fb)
+				ops |= SDE_KMS_OPS_WAIT_FOR_TX_DONE;
+		} else if ((smmu_state->state == DETACHED) ||
+				(smmu_state->state == DETACH_ALL_REQ)) {
 			smmu_state->state = ATTACH_ALL_REQ;
 			smmu_state->transition_type = post_commit ?
 				POST_COMMIT : PRE_COMMIT;
-			ops |= SDE_KMS_OPS_CRTC_SECURE_STATE_CHANGE |
-				SDE_KMS_OPS_PREPARE_PLANE_FB;
+			ops |= SDE_KMS_OPS_CRTC_SECURE_STATE_CHANGE;
 			if (old_valid_fb)
 				ops |= (SDE_KMS_OPS_WAIT_FOR_TX_DONE |
 				 SDE_KMS_OPS_CLEANUP_PLANE_FB);
 		}
 		break;
 	default:
-		SDE_ERROR("invalid plane fb_mode:%d\n",
-				translation_mode);
+		SDE_ERROR("invalid plane fb_mode:%d\n", translation_mode);
 		ops = 0;
 		return -EINVAL;
 	}
 
 	SDE_DEBUG("SMMU State:%d, type:%d ops:%x\n", smmu_state->state,
-			smmu_state->transition_type,
-			ops);
+			smmu_state->transition_type, ops);
+	/* log only during actual transition times */
+	if (ops)
+		SDE_EVT32(DRMID(crtc), secure_level, translation_mode,
+				smmu_state->state, smmu_state->transition_type,
+				ops, old_valid_fb, SDE_EVTLOG_FUNC_EXIT);
 	return ops;
 }
 
@@ -1722,13 +1764,13 @@
 	 */
 	sec_sid[0] = SEC_SID_MASK_0;
 	sec_sid[1] = SEC_SID_MASK_1;
-	dmac_flush_range(&sec_sid, &sec_sid + num_sids);
+	dmac_flush_range(sec_sid, sec_sid + num_sids);
 
 	SDE_DEBUG("calling scm_call for vmid %d", vmid);
 
 	desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
 	desc.args[0] = MDP_DEVICE_ID;
-	desc.args[1] = SCM_BUFFER_PHYS(&sec_sid);
+	desc.args[1] = SCM_BUFFER_PHYS(sec_sid);
 	desc.args[2] = sizeof(uint32_t) * num_sids;
 	desc.args[3] =  vmid;
 
@@ -1736,15 +1778,87 @@
 				mem_protect_sd_ctrl_id), &desc);
 	if (ret) {
 		SDE_ERROR("Error:scm_call2, vmid (%lld): ret%d\n",
-				desc.args[3],
-				ret);
+				desc.args[3], ret);
 	}
+	SDE_EVT32(mem_protect_sd_ctrl_id,
+			desc.args[0], desc.args[3], num_sids,
+			sec_sid[0], sec_sid[1], ret);
 
 	kfree(sec_sid);
 	return ret;
 }
 
 /**
+ * _sde_crtc_setup_scaler3_lut - Set up scaler lut
+ * LUTs are configured only once during boot
+ * @sde_crtc: Pointer to sde crtc
+ * @cstate: Pointer to sde crtc state
+ */
+static int _sde_crtc_set_dest_scaler_lut(struct sde_crtc *sde_crtc,
+		struct sde_crtc_state *cstate, uint32_t lut_idx)
+{
+	struct sde_hw_scaler3_lut_cfg *cfg;
+	u32 *lut_data = NULL;
+	size_t len = 0;
+	int ret = 0;
+
+	if (!sde_crtc || !cstate) {
+		SDE_ERROR("invalid args\n");
+		return -EINVAL;
+	}
+
+	lut_data = msm_property_get_blob(&sde_crtc->property_info,
+			&cstate->property_state, &len, lut_idx);
+	if (!lut_data || !len) {
+		SDE_DEBUG("%s: lut(%d): cleared: %pK, %zu\n", sde_crtc->name,
+				lut_idx, lut_data, len);
+		lut_data = NULL;
+		len = 0;
+	}
+
+	cfg = &cstate->scl3_lut_cfg;
+
+	switch (lut_idx) {
+	case CRTC_PROP_DEST_SCALER_LUT_ED:
+		cfg->dir_lut = lut_data;
+		cfg->dir_len = len;
+		break;
+	case CRTC_PROP_DEST_SCALER_LUT_CIR:
+		cfg->cir_lut = lut_data;
+		cfg->cir_len = len;
+		break;
+	case CRTC_PROP_DEST_SCALER_LUT_SEP:
+		cfg->sep_lut = lut_data;
+		cfg->sep_len = len;
+		break;
+	default:
+		ret = -EINVAL;
+		SDE_ERROR("%s:invalid LUT idx(%d)\n", sde_crtc->name, lut_idx);
+		SDE_EVT32(DRMID(&sde_crtc->base), lut_idx, SDE_EVTLOG_ERROR);
+		break;
+	}
+
+	cfg->is_configured = cfg->dir_lut && cfg->cir_lut && cfg->sep_lut;
+
+	SDE_EVT32_VERBOSE(DRMID(&sde_crtc->base), ret, lut_idx, len,
+			cfg->is_configured);
+	return ret;
+}
+
+void sde_crtc_timeline_status(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+
+	if (!crtc) {
+		SDE_ERROR("invalid crtc\n");
+		return;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	sde_fence_timeline_status(&sde_crtc->output_fence, &crtc->base);
+}
+
+/**
  * sde_crtc_secure_ctrl - Initiates the operations to swtich  between secure
  *                       and non-secure mode
  * @crtc: Pointer to crtc
@@ -1775,12 +1889,14 @@
 	smmu_state = &sde_crtc->smmu_state;
 	old_smmu_state = smmu_state->state;
 
+	SDE_EVT32(DRMID(crtc), smmu_state->state, smmu_state->transition_type,
+			post_commit, SDE_EVTLOG_FUNC_ENTRY);
+
 	if ((!smmu_state->transition_type) ||
 	    ((smmu_state->transition_type == POST_COMMIT) && !post_commit))
 		/* Bail out */
 		return 0;
 
-
 	/* Secure UI use case enable */
 	switch (smmu_state->state) {
 	case DETACH_ALL_REQ:
@@ -1856,14 +1972,155 @@
 	SDE_DEBUG("crtc: %d, old_state %d new_state %d\n", crtc->base.id,
 			old_smmu_state,
 			smmu_state->state);
-	smmu_state->transition_error = false;
 	smmu_state->transition_type = NONE;
 
 error:
-	smmu_state->transition_error = true;
+	smmu_state->transition_error = ret ? true : false;
+	SDE_EVT32(DRMID(crtc), smmu_state->state, smmu_state->transition_type,
+			smmu_state->transition_error, ret,
+			SDE_EVTLOG_FUNC_EXIT);
 	return ret;
 }
 
+static int _sde_validate_hw_resources(struct sde_crtc *sde_crtc)
+{
+	int i;
+
+	/**
+	 * Check if sufficient hw resources are
+	 * available as per target caps & topology
+	 */
+	if (!sde_crtc) {
+		SDE_ERROR("invalid argument\n");
+		return -EINVAL;
+	}
+
+	if (!sde_crtc->num_mixers ||
+		sde_crtc->num_mixers > CRTC_DUAL_MIXERS) {
+		SDE_ERROR("%s: invalid number mixers: %d\n",
+			sde_crtc->name, sde_crtc->num_mixers);
+		SDE_EVT32(DRMID(&sde_crtc->base), sde_crtc->num_mixers,
+			SDE_EVTLOG_ERROR);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		if (!sde_crtc->mixers[i].hw_lm || !sde_crtc->mixers[i].hw_ctl
+			|| !sde_crtc->mixers[i].hw_ds) {
+			SDE_ERROR("%s:insufficient resources for mixer(%d)\n",
+				sde_crtc->name, i);
+			SDE_EVT32(DRMID(&sde_crtc->base), sde_crtc->num_mixers,
+				i, sde_crtc->mixers[i].hw_lm,
+				sde_crtc->mixers[i].hw_ctl,
+				sde_crtc->mixers[i].hw_ds, SDE_EVTLOG_ERROR);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * _sde_crtc_dest_scaler_setup - Set up dest scaler block
+ * @crtc: Pointer to drm crtc
+ */
+static void _sde_crtc_dest_scaler_setup(struct drm_crtc *crtc)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	struct sde_hw_mixer *hw_lm;
+	struct sde_hw_ctl *hw_ctl;
+	struct sde_hw_ds *hw_ds;
+	struct sde_hw_ds_cfg *cfg;
+	struct sde_kms *kms;
+	u32 flush_mask = 0, op_mode = 0;
+	u32 lm_idx = 0, num_mixers = 0;
+	int i, count = 0;
+	bool ds_dirty = false;
+
+	if (!crtc)
+		return;
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc->state);
+	kms = _sde_crtc_get_kms(crtc);
+	num_mixers = sde_crtc->num_mixers;
+	count = cstate->num_ds;
+
+	SDE_DEBUG("crtc%d\n", crtc->base.id);
+	SDE_EVT32(DRMID(crtc), num_mixers, count, cstate->ds_dirty,
+		sde_crtc->ds_reconfig, cstate->num_ds_enabled);
+
+	/**
+	 * destination scaler configuration will be done either
+	 * or on set property or on power collapse (idle/suspend)
+	 */
+	ds_dirty = (cstate->ds_dirty || sde_crtc->ds_reconfig);
+	if (sde_crtc->ds_reconfig) {
+		SDE_DEBUG("reconfigure dest scaler block\n");
+		sde_crtc->ds_reconfig = false;
+	}
+
+	if (!ds_dirty) {
+		SDE_DEBUG("no change in settings, skip commit\n");
+	} else if (!kms || !kms->catalog) {
+		SDE_ERROR("crtc%d:invalid parameters\n", crtc->base.id);
+	} else if (!kms->catalog->mdp[0].has_dest_scaler) {
+		SDE_DEBUG("dest scaler feature not supported\n");
+	} else if (_sde_validate_hw_resources(sde_crtc)) {
+		//do nothing
+	} else if (!cstate->scl3_lut_cfg.is_configured) {
+		SDE_ERROR("crtc%d:no LUT data available\n", crtc->base.id);
+	} else {
+		for (i = 0; i < count; i++) {
+			cfg = &cstate->ds_cfg[i];
+
+			if (!cfg->flags)
+				continue;
+
+			lm_idx = cfg->idx;
+			hw_lm  = sde_crtc->mixers[lm_idx].hw_lm;
+			hw_ctl = sde_crtc->mixers[lm_idx].hw_ctl;
+			hw_ds  = sde_crtc->mixers[lm_idx].hw_ds;
+
+			/* Setup op mode - Dual/single */
+			if (cfg->flags & SDE_DRM_DESTSCALER_ENABLE)
+				op_mode |= BIT(hw_ds->idx - DS_0);
+
+			if ((i == count-1) && hw_ds->ops.setup_opmode) {
+				op_mode |= (cstate->num_ds_enabled ==
+					CRTC_DUAL_MIXERS) ?
+					SDE_DS_OP_MODE_DUAL : 0;
+				hw_ds->ops.setup_opmode(hw_ds, op_mode);
+				SDE_EVT32_VERBOSE(DRMID(crtc), op_mode);
+			}
+
+			/* Setup scaler */
+			if ((cfg->flags & SDE_DRM_DESTSCALER_SCALE_UPDATE) ||
+				(cfg->flags &
+					SDE_DRM_DESTSCALER_ENHANCER_UPDATE)) {
+				if (hw_ds->ops.setup_scaler)
+					hw_ds->ops.setup_scaler(hw_ds,
+						&cfg->scl3_cfg,
+						&cstate->scl3_lut_cfg);
+
+			}
+
+			/*
+			 * Dest scaler shares the flush bit of the LM in control
+			 */
+			if (hw_ctl->ops.get_bitmask_mixer) {
+				flush_mask = hw_ctl->ops.get_bitmask_mixer(
+						hw_ctl, hw_lm->idx);
+				SDE_DEBUG("Set lm[%d] flush = %d",
+					hw_lm->idx, flush_mask);
+				hw_ctl->ops.update_pending_flush(hw_ctl,
+							flush_mask);
+			}
+		}
+	}
+}
+
 void sde_crtc_prepare_commit(struct drm_crtc *crtc,
 		struct drm_crtc_state *old_state)
 {
@@ -2022,47 +2279,6 @@
 	SDE_ATRACE_END("signal_retire_fence");
 }
 
-/* _sde_crtc_idle_notify - signal idle timeout to client */
-static void _sde_crtc_idle_notify(struct sde_crtc *sde_crtc)
-{
-	struct drm_crtc *crtc;
-	struct drm_event event;
-	int ret = 0;
-
-	if (!sde_crtc) {
-		SDE_ERROR("invalid sde crtc\n");
-		return;
-	}
-
-	crtc = &sde_crtc->base;
-	event.type = DRM_EVENT_IDLE_NOTIFY;
-	event.length = sizeof(u32);
-	msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
-								(u8 *)&ret);
-
-	SDE_DEBUG("crtc:%d idle timeout notified\n", crtc->base.id);
-}
-
-/*
- * sde_crtc_handle_event - crtc frame event handle.
- * This API must manage only non-IRQ context events.
- */
-static bool _sde_crtc_handle_event(struct sde_crtc *sde_crtc, u32 event)
-{
-	bool event_processed = false;
-
-	/**
-	 * idle events are originated from commit thread and can be processed
-	 * in same context
-	 */
-	if (event & SDE_ENCODER_FRAME_EVENT_IDLE) {
-		_sde_crtc_idle_notify(sde_crtc);
-		event_processed = true;
-	}
-
-	return event_processed;
-}
-
 static void sde_crtc_frame_event_work(struct kthread_work *work)
 {
 	struct msm_drm_private *priv;
@@ -2156,15 +2372,6 @@
 	SDE_ATRACE_END("crtc_frame_event");
 }
 
-/*
- * sde_crtc_frame_event_cb - crtc frame event callback API. CRTC module
- * registers this API to encoder for all frame event callbacks like
- * release_fence, retire_fence, frame_error, frame_done, idle_timeout,
- * etc. Encoder may call different events from different context - IRQ,
- * user thread, commit_thread, etc. Each event should be carefully
- * reviewed and should be processed in proper task context to avoid scheduling
- * delay or properly manage the irq context's bottom half processing.
- */
 static void sde_crtc_frame_event_cb(void *data, u32 event)
 {
 	struct drm_crtc *crtc = (struct drm_crtc *)data;
@@ -2173,7 +2380,6 @@
 	struct sde_crtc_frame_event *fevent;
 	unsigned long flags;
 	u32 crtc_id;
-	bool event_processed = false;
 
 	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
 		SDE_ERROR("invalid parameters\n");
@@ -2186,11 +2392,6 @@
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 	SDE_EVT32_VERBOSE(DRMID(crtc), event);
 
-	/* try to process the event in caller context */
-	event_processed = _sde_crtc_handle_event(sde_crtc, event);
-	if (event_processed)
-		return;
-
 	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
 	fevent = list_first_entry_or_null(&sde_crtc->frame_event_list,
 			struct sde_crtc_frame_event, list);
@@ -2231,24 +2432,6 @@
 		sde_crtc_secure_ctrl(crtc, true);
 }
 
-/* _sde_crtc_set_idle_timeout - update idle timeout wait duration */
-static void _sde_crtc_set_idle_timeout(struct drm_crtc *crtc, u64 val)
-{
-	struct drm_encoder *encoder;
-
-	if (!crtc) {
-		SDE_ERROR("invalid crtc\n");
-		return;
-	}
-
-	drm_for_each_encoder(encoder, crtc->dev) {
-		if (encoder->crtc != crtc)
-			continue;
-
-		sde_encoder_set_idle_timeout(encoder, (u32) val);
-	}
-}
-
 /**
  * _sde_crtc_set_input_fence_timeout - update ns version of in fence timeout
  * @cstate: Pointer to sde crtc state
@@ -2270,7 +2453,7 @@
  * @user_ptr:    User ptr for sde_drm_dim_layer_v1 struct
  */
 static void _sde_crtc_set_dim_layer_v1(struct sde_crtc_state *cstate,
-		void *usr_ptr)
+		void __user *usr_ptr)
 {
 	struct sde_drm_dim_layer_v1 dim_layer_v1;
 	struct sde_drm_dim_layer_cfg *user_cfg;
@@ -2332,6 +2515,333 @@
 }
 
 /**
+ * _sde_crtc_set_dest_scaler - copy dest scaler settings from userspace
+ * @sde_crtc   :  Pointer to sde crtc
+ * @cstate :  Pointer to sde crtc state
+ * @usr_ptr:  User ptr for sde_drm_dest_scaler_data struct
+ */
+static int _sde_crtc_set_dest_scaler(struct sde_crtc *sde_crtc,
+				struct sde_crtc_state *cstate,
+				void __user *usr_ptr)
+{
+	struct sde_drm_dest_scaler_data ds_data;
+	struct sde_drm_dest_scaler_cfg *ds_cfg_usr;
+	struct sde_drm_scaler_v2 scaler_v2;
+	void __user *scaler_v2_usr;
+	int i, count;
+
+	if (!sde_crtc || !cstate) {
+		SDE_ERROR("invalid sde_crtc/state\n");
+		return -EINVAL;
+	}
+
+	SDE_DEBUG("crtc %s\n", sde_crtc->name);
+
+	if (!usr_ptr) {
+		SDE_DEBUG("ds data removed\n");
+		return 0;
+	}
+
+	if (copy_from_user(&ds_data, usr_ptr, sizeof(ds_data))) {
+		SDE_ERROR("%s:failed to copy dest scaler data from user\n",
+			sde_crtc->name);
+		return -EINVAL;
+	}
+
+	count = ds_data.num_dest_scaler;
+	if (!count) {
+		SDE_DEBUG("no ds data available\n");
+		return 0;
+	}
+
+	if (count > SDE_MAX_DS_COUNT) {
+		SDE_ERROR("%s: invalid config: num_ds(%d) max(%d)\n",
+			sde_crtc->name, count, SDE_MAX_DS_COUNT);
+		SDE_EVT32(DRMID(&sde_crtc->base), count, SDE_EVTLOG_ERROR);
+		return -EINVAL;
+	}
+
+	/* Populate from user space */
+	for (i = 0; i < count; i++) {
+		ds_cfg_usr = &ds_data.ds_cfg[i];
+
+		cstate->ds_cfg[i].idx = ds_cfg_usr->index;
+		cstate->ds_cfg[i].flags = ds_cfg_usr->flags;
+		cstate->ds_cfg[i].lm_width = ds_cfg_usr->lm_width;
+		cstate->ds_cfg[i].lm_height = ds_cfg_usr->lm_height;
+		memset(&scaler_v2, 0, sizeof(scaler_v2));
+
+		if (ds_cfg_usr->scaler_cfg) {
+			scaler_v2_usr =
+			(void __user *)((uintptr_t)ds_cfg_usr->scaler_cfg);
+
+			if (copy_from_user(&scaler_v2, scaler_v2_usr,
+					sizeof(scaler_v2))) {
+				SDE_ERROR("%s:scaler: copy from user failed\n",
+					sde_crtc->name);
+				return -EINVAL;
+			}
+		}
+
+		sde_set_scaler_v2(&cstate->ds_cfg[i].scl3_cfg, &scaler_v2);
+
+		SDE_DEBUG("en(%d)dir(%d)de(%d) src(%dx%d) dst(%dx%d)\n",
+			scaler_v2.enable, scaler_v2.dir_en, scaler_v2.de.enable,
+			scaler_v2.src_width[0], scaler_v2.src_height[0],
+			scaler_v2.dst_width, scaler_v2.dst_height);
+		SDE_EVT32_VERBOSE(DRMID(&sde_crtc->base),
+			scaler_v2.enable, scaler_v2.dir_en, scaler_v2.de.enable,
+			scaler_v2.src_width[0], scaler_v2.src_height[0],
+			scaler_v2.dst_width, scaler_v2.dst_height);
+
+		SDE_DEBUG("ds cfg[%d]-ndx(%d) flags(%d) lm(%dx%d)\n",
+			i, ds_cfg_usr->index, ds_cfg_usr->flags,
+			ds_cfg_usr->lm_width, ds_cfg_usr->lm_height);
+		SDE_EVT32_VERBOSE(DRMID(&sde_crtc->base), i, ds_cfg_usr->index,
+			ds_cfg_usr->flags, ds_cfg_usr->lm_width,
+			ds_cfg_usr->lm_height);
+	}
+
+	cstate->num_ds = count;
+	cstate->ds_dirty = true;
+	SDE_EVT32_VERBOSE(DRMID(&sde_crtc->base), count, cstate->ds_dirty);
+
+	return 0;
+}
+
+/**
+ * _sde_crtc_check_dest_scaler_data - validate the dest scaler data
+ * @crtc  :  Pointer to drm crtc
+ * @state :  Pointer to drm crtc state
+ */
+static int _sde_crtc_check_dest_scaler_data(struct drm_crtc *crtc,
+				struct drm_crtc_state *state)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	struct drm_display_mode *mode;
+	struct sde_kms *kms;
+	struct sde_hw_ds *hw_ds;
+	struct sde_hw_ds_cfg *cfg;
+	u32 i, ret = 0, lm_idx;
+	u32 num_ds_enable = 0, hdisplay = 0;
+	u32 max_in_width = 0, max_out_width = 0;
+	u32 prev_lm_width = 0, prev_lm_height = 0;
+
+	if (!crtc || !state)
+		return -EINVAL;
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(state);
+	kms = _sde_crtc_get_kms(crtc);
+	mode = &state->adjusted_mode;
+
+	SDE_DEBUG("crtc%d\n", crtc->base.id);
+
+	if (!cstate->ds_dirty) {
+		SDE_DEBUG("dest scaler property not set, skip validation\n");
+		return 0;
+	}
+
+	if (!kms || !kms->catalog) {
+		SDE_ERROR("crtc%d: invalid parameters\n", crtc->base.id);
+		return -EINVAL;
+	}
+
+	if (!kms->catalog->mdp[0].has_dest_scaler) {
+		SDE_DEBUG("dest scaler feature not supported\n");
+		return 0;
+	}
+
+	if (!sde_crtc->num_mixers) {
+		SDE_DEBUG("mixers not allocated\n");
+		return 0;
+	}
+
+	ret = _sde_validate_hw_resources(sde_crtc);
+	if (ret)
+		goto err;
+
+	/**
+	 * No of dest scalers shouldn't exceed hw ds block count and
+	 * also, match the num of mixers unless it is partial update
+	 * left only/right only use case - currently PU + DS is not supported
+	 */
+	if (cstate->num_ds > kms->catalog->ds_count ||
+		((cstate->num_ds != sde_crtc->num_mixers) &&
+		!(cstate->ds_cfg[0].flags & SDE_DRM_DESTSCALER_PU_ENABLE))) {
+		SDE_ERROR("crtc%d: num_ds(%d), hw_ds_cnt(%d) flags(%d)\n",
+			crtc->base.id, cstate->num_ds, kms->catalog->ds_count,
+			cstate->ds_cfg[0].flags);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/**
+	 * Check if DS needs to be enabled or disabled
+	 * In case of enable, validate the data
+	 */
+	if (!(cstate->ds_cfg[0].flags & SDE_DRM_DESTSCALER_ENABLE)) {
+		SDE_DEBUG("disable dest scaler, num(%d) flags(%d)\n",
+			cstate->num_ds, cstate->ds_cfg[0].flags);
+		goto disable;
+	}
+
+	/* Display resolution */
+	hdisplay = mode->hdisplay/sde_crtc->num_mixers;
+
+	/* Validate the DS data */
+	for (i = 0; i < cstate->num_ds; i++) {
+		cfg = &cstate->ds_cfg[i];
+		lm_idx = cfg->idx;
+
+		/**
+		 * Validate against topology
+		 * No of dest scalers should match the num of mixers
+		 * unless it is partial update left only/right only use case
+		 */
+		if (lm_idx >= sde_crtc->num_mixers || (i != lm_idx &&
+			!(cfg->flags & SDE_DRM_DESTSCALER_PU_ENABLE))) {
+			SDE_ERROR("crtc%d: ds_cfg id(%d):idx(%d), flags(%d)\n",
+				crtc->base.id, i, lm_idx, cfg->flags);
+			SDE_EVT32(DRMID(crtc), i, lm_idx, cfg->flags,
+				SDE_EVTLOG_ERROR);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		hw_ds = sde_crtc->mixers[lm_idx].hw_ds;
+
+		if (!max_in_width && !max_out_width) {
+			max_in_width = hw_ds->scl->top->maxinputwidth;
+			max_out_width = hw_ds->scl->top->maxoutputwidth;
+
+			if (cstate->num_ds == CRTC_DUAL_MIXERS)
+				max_in_width -= SDE_DS_OVERFETCH_SIZE;
+
+			SDE_DEBUG("max DS width [%d,%d] for num_ds = %d\n",
+				max_in_width, max_out_width, cstate->num_ds);
+		}
+
+		/* Check LM width and height */
+		if (cfg->lm_width > hdisplay || cfg->lm_height > mode->vdisplay
+			|| !cfg->lm_width || !cfg->lm_height) {
+			SDE_ERROR("crtc%d: lm size[%d,%d] display [%d,%d]\n",
+				crtc->base.id, cfg->lm_width, cfg->lm_height,
+				hdisplay, mode->vdisplay);
+			SDE_EVT32(DRMID(crtc),  cfg->lm_width, cfg->lm_height,
+				hdisplay, mode->vdisplay, SDE_EVTLOG_ERROR);
+			ret = -E2BIG;
+			goto err;
+		}
+
+		if (!prev_lm_width && !prev_lm_height) {
+			prev_lm_width = cfg->lm_width;
+			prev_lm_height = cfg->lm_height;
+		} else {
+			if (cfg->lm_width != prev_lm_width ||
+				cfg->lm_height != prev_lm_height) {
+				SDE_ERROR("crtc%d:lm left[%d,%d]right[%d %d]\n",
+					crtc->base.id, cfg->lm_width,
+					cfg->lm_height, prev_lm_width,
+					prev_lm_height);
+				SDE_EVT32(DRMID(crtc), cfg->lm_width,
+					cfg->lm_height, prev_lm_width,
+					prev_lm_height, SDE_EVTLOG_ERROR);
+				ret = -EINVAL;
+				goto err;
+			}
+		}
+
+		/* Check scaler data */
+		if (cfg->flags & SDE_DRM_DESTSCALER_SCALE_UPDATE ||
+			cfg->flags & SDE_DRM_DESTSCALER_ENHANCER_UPDATE) {
+
+			/**
+			 * Scaler src and dst width shouldn't exceed the maximum
+			 * width limitation. Also, if there is no partial update
+			 * dst width and height must match display resolution.
+			 */
+			if (cfg->scl3_cfg.src_width[0] > max_in_width ||
+				cfg->scl3_cfg.dst_width > max_out_width ||
+				!cfg->scl3_cfg.src_width[0] ||
+				!cfg->scl3_cfg.dst_width ||
+				(!(cfg->flags & SDE_DRM_DESTSCALER_PU_ENABLE)
+				 && (cfg->scl3_cfg.dst_width != hdisplay ||
+				 cfg->scl3_cfg.dst_height != mode->vdisplay))) {
+				SDE_ERROR("crtc%d: ", crtc->base.id);
+				SDE_ERROR("src_w(%d) dst(%dx%d) display(%dx%d)",
+					cfg->scl3_cfg.src_width[0],
+					cfg->scl3_cfg.dst_width,
+					cfg->scl3_cfg.dst_height,
+					hdisplay, mode->vdisplay);
+				SDE_ERROR("num_mixers(%d) flags(%d) ds-%d:\n",
+					sde_crtc->num_mixers, cfg->flags,
+					hw_ds->idx - DS_0);
+				SDE_ERROR("scale_en = %d, DE_en =%d\n",
+					cfg->scl3_cfg.enable,
+					cfg->scl3_cfg.de.enable);
+
+				SDE_EVT32(DRMID(crtc), cfg->scl3_cfg.enable,
+					cfg->scl3_cfg.de.enable, cfg->flags,
+					max_in_width, max_out_width,
+					cfg->scl3_cfg.src_width[0],
+					cfg->scl3_cfg.dst_width,
+					cfg->scl3_cfg.dst_height, hdisplay,
+					mode->vdisplay, sde_crtc->num_mixers,
+					SDE_EVTLOG_ERROR);
+
+				cfg->flags &=
+					~SDE_DRM_DESTSCALER_SCALE_UPDATE;
+				cfg->flags &=
+					~SDE_DRM_DESTSCALER_ENHANCER_UPDATE;
+
+				ret = -EINVAL;
+				goto err;
+			}
+		}
+
+		if (cfg->flags & SDE_DRM_DESTSCALER_ENABLE)
+			num_ds_enable++;
+
+		SDE_DEBUG("ds[%d]: flags[0x%X]\n",
+			hw_ds->idx - DS_0, cfg->flags);
+		SDE_EVT32_VERBOSE(DRMID(crtc), hw_ds->idx - DS_0, cfg->flags);
+	}
+
+disable:
+	SDE_DEBUG("dest scaler status : %d -> %d\n",
+		cstate->num_ds_enabled,	num_ds_enable);
+	SDE_EVT32_VERBOSE(DRMID(crtc), cstate->num_ds_enabled, num_ds_enable,
+			cstate->num_ds, cstate->ds_dirty);
+
+	if (cstate->num_ds_enabled != num_ds_enable) {
+		/* Disabling destination scaler */
+		if (!num_ds_enable) {
+			for (i = 0; i < cstate->num_ds; i++) {
+				cfg = &cstate->ds_cfg[i];
+				cfg->idx = i;
+				/* Update scaler settings in disable case */
+				cfg->flags = SDE_DRM_DESTSCALER_SCALE_UPDATE;
+				cfg->scl3_cfg.enable = 0;
+				cfg->scl3_cfg.de.enable = 0;
+			}
+		}
+		cstate->num_ds_enabled = num_ds_enable;
+		cstate->ds_dirty = true;
+	} else {
+		if (!cstate->num_ds_enabled)
+			cstate->ds_dirty = false;
+	}
+
+	return 0;
+
+err:
+	cstate->ds_dirty = false;
+	return ret;
+}
+
+/**
  * _sde_crtc_wait_for_fences - wait for incoming framebuffer sync fences
  * @crtc: Pointer to CRTC object
  */
@@ -2388,11 +2898,12 @@
 	struct sde_crtc_mixer *mixer;
 	struct sde_hw_ctl *last_valid_ctl = NULL;
 	int i;
-	struct sde_rm_hw_iter lm_iter, ctl_iter, dspp_iter;
+	struct sde_rm_hw_iter lm_iter, ctl_iter, dspp_iter, ds_iter;
 
 	sde_rm_init_hw_iter(&lm_iter, enc->base.id, SDE_HW_BLK_LM);
 	sde_rm_init_hw_iter(&ctl_iter, enc->base.id, SDE_HW_BLK_CTL);
 	sde_rm_init_hw_iter(&dspp_iter, enc->base.id, SDE_HW_BLK_DSPP);
+	sde_rm_init_hw_iter(&ds_iter, enc->base.id, SDE_HW_BLK_DS);
 
 	/* Set up all the mixers and ctls reserved by this encoder */
 	for (i = sde_crtc->num_mixers; i < ARRAY_SIZE(sde_crtc->mixers); i++) {
@@ -2423,6 +2934,10 @@
 		(void) sde_rm_get_hw(rm, &dspp_iter);
 		mixer->hw_dspp = (struct sde_hw_dspp *)dspp_iter.hw;
 
+		/* DS may be null */
+		(void) sde_rm_get_hw(rm, &ds_iter);
+		mixer->hw_ds = (struct sde_hw_ds *)ds_iter.hw;
+
 		mixer->encoder = enc;
 
 		sde_crtc->num_mixers++;
@@ -2430,6 +2945,9 @@
 				i, mixer->hw_lm->idx - LM_0);
 		SDE_DEBUG("setup mixer %d: ctl %d\n",
 				i, mixer->hw_ctl->idx - CTL_0);
+		if (mixer->hw_ds)
+			SDE_DEBUG("setup mixer %d: ds %d\n",
+				i, mixer->hw_ds->idx - DS_0);
 	}
 }
 
@@ -2439,6 +2957,7 @@
 	struct drm_encoder *enc;
 
 	sde_crtc->num_mixers = 0;
+	sde_crtc->mixers_swapped = false;
 	memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
 
 	mutex_lock(&sde_crtc->crtc_lock);
@@ -2488,13 +3007,14 @@
 	cstate = to_sde_crtc_state(state);
 
 	adj_mode = &state->adjusted_mode;
-	crtc_split_width = sde_crtc_mixer_width(sde_crtc, adj_mode);
+	crtc_split_width = sde_crtc_get_mixer_width(sde_crtc, cstate, adj_mode);
 
 	for (i = 0; i < sde_crtc->num_mixers; i++) {
 		cstate->lm_bounds[i].x = crtc_split_width * i;
 		cstate->lm_bounds[i].y = 0;
 		cstate->lm_bounds[i].w = crtc_split_width;
-		cstate->lm_bounds[i].h = adj_mode->vdisplay;
+		cstate->lm_bounds[i].h =
+			sde_crtc_get_mixer_height(sde_crtc, cstate, adj_mode);
 		memcpy(&cstate->lm_roi[i], &cstate->lm_bounds[i],
 				sizeof(cstate->lm_roi[i]));
 		SDE_EVT32_VERBOSE(DRMID(crtc), i,
@@ -2528,6 +3048,11 @@
 		return;
 	}
 
+	if (!sde_kms_power_resource_is_enabled(crtc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 
 	sde_crtc = to_sde_crtc(crtc);
@@ -2564,7 +3089,14 @@
 	if (unlikely(!sde_crtc->num_mixers))
 		return;
 
-	_sde_crtc_blend_setup(crtc);
+	_sde_crtc_blend_setup(crtc, true);
+	_sde_crtc_dest_scaler_setup(crtc);
+
+	/* cancel the idle notify delayed work */
+	if (sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
+					MSM_DISPLAY_CAP_VID_MODE) &&
+		kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work))
+		SDE_DEBUG("idle notify work cancelled\n");
 
 	/*
 	 * Since CP properties use AXI buffer to program the
@@ -2573,7 +3105,7 @@
 	 * apply color processing properties only if
 	 * smmu state is attached,
 	 */
-	if ((smmu_state->state != DETACHED) ||
+	if ((smmu_state->state != DETACHED) &&
 			(smmu_state->state != DETACH_ALL_REQ))
 		sde_cp_crtc_apply_properties(crtc);
 
@@ -2597,6 +3129,7 @@
 	struct msm_drm_thread *event_thread;
 	unsigned long flags;
 	struct sde_crtc_state *cstate;
+	int idle_time = 0;
 
 	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
 		SDE_ERROR("invalid crtc\n");
@@ -2609,6 +3142,11 @@
 		return;
 	}
 
+	if (!sde_kms_power_resource_is_enabled(crtc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 
 	sde_crtc = to_sde_crtc(crtc);
@@ -2622,6 +3160,7 @@
 	}
 
 	event_thread = &priv->event_thread[crtc->index];
+	idle_time = sde_crtc_get_property(cstate, CRTC_PROP_IDLE_TIMEOUT);
 
 	if (sde_crtc->event) {
 		SDE_DEBUG("already received sde_crtc->event\n");
@@ -2652,6 +3191,15 @@
 	/* wait for acquire fences before anything else is done */
 	_sde_crtc_wait_for_fences(crtc);
 
+	/* schedule the idle notify delayed work */
+	if (idle_time && sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
+						MSM_DISPLAY_CAP_VID_MODE)) {
+		kthread_queue_delayed_work(&event_thread->worker,
+					&sde_crtc->idle_notify_work,
+					msecs_to_jiffies(idle_time));
+		SDE_DEBUG("schedule idle notify work in %dms\n", idle_time);
+	}
+
 	if (!cstate->rsc_update) {
 		drm_for_each_encoder(encoder, dev) {
 			if (encoder->crtc != crtc)
@@ -2713,7 +3261,7 @@
 static int _sde_crtc_wait_for_frame_done(struct drm_crtc *crtc)
 {
 	struct sde_crtc *sde_crtc;
-	int ret, rc = 0;
+	int ret, rc = 0, i;
 
 	if (!crtc) {
 		SDE_ERROR("invalid argument\n");
@@ -2726,7 +3274,17 @@
 		return 0;
 	}
 
-	SDE_EVT32_VERBOSE(DRMID(crtc), SDE_EVTLOG_FUNC_ENTRY);
+	SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_ENTRY);
+
+	/*
+	 * flush all the event thread work to make sure all the
+	 * FRAME_EVENTS from encoder are propagated to crtc
+	 */
+	for (i = 0; i < ARRAY_SIZE(sde_crtc->frame_events); i++) {
+		if (list_empty(&sde_crtc->frame_events[i].list))
+			kthread_flush_work(&sde_crtc->frame_events[i].work);
+	}
+
 	ret = wait_for_completion_timeout(&sde_crtc->frame_done_comp,
 			msecs_to_jiffies(SDE_FRAME_DONE_TIMEOUT));
 	if (!ret) {
@@ -2740,17 +3298,288 @@
 	return rc;
 }
 
-void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
+static int _sde_crtc_commit_kickoff_rot(struct drm_crtc *crtc,
+		struct sde_crtc_state *cstate)
 {
 	struct drm_plane *plane;
+	struct sde_crtc *sde_crtc;
+	struct sde_hw_ctl *ctl, *master_ctl;
+	u32 flush_mask;
+	int i, rc = 0;
+
+	if (!crtc || !cstate)
+		return -EINVAL;
+
+	sde_crtc = to_sde_crtc(crtc);
+
+	/*
+	 * Update sbuf configuration and flush bits if a flush
+	 * mask has been defined for either the current or
+	 * previous commit.
+	 *
+	 * Updates are also required for the first commit after
+	 * sbuf_flush_mask becomes 0x0, to properly transition
+	 * the hardware out of sbuf mode.
+	 */
+	if (!sde_crtc->sbuf_flush_mask_old && !sde_crtc->sbuf_flush_mask)
+		return 0;
+
+	flush_mask = sde_crtc->sbuf_flush_mask_old | sde_crtc->sbuf_flush_mask;
+	sde_crtc->sbuf_flush_mask_old = sde_crtc->sbuf_flush_mask;
+
+	SDE_ATRACE_BEGIN("crtc_kickoff_rot");
+
+	if (cstate->sbuf_cfg.rot_op_mode != SDE_CTL_ROT_OP_MODE_OFFLINE) {
+		drm_atomic_crtc_for_each_plane(plane, crtc) {
+			rc = sde_plane_kickoff_rot(plane);
+			if (rc) {
+				SDE_ERROR("crtc%d cancelling inline rotation\n",
+						crtc->base.id);
+				SDE_EVT32(DRMID(crtc), SDE_EVTLOG_ERROR);
+
+				/* revert to offline on errors */
+				cstate->sbuf_cfg.rot_op_mode =
+					SDE_CTL_ROT_OP_MODE_OFFLINE;
+				break;
+			}
+		}
+	}
+
+	master_ctl = NULL;
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		ctl = sde_crtc->mixers[i].hw_ctl;
+		if (!ctl)
+			continue;
+
+		if (!master_ctl || master_ctl->idx > ctl->idx)
+			master_ctl = ctl;
+	}
+
+	/* only update sbuf_cfg and flush for master ctl */
+	if (master_ctl && master_ctl->ops.setup_sbuf_cfg &&
+			master_ctl->ops.update_pending_flush) {
+		master_ctl->ops.setup_sbuf_cfg(master_ctl, &cstate->sbuf_cfg);
+		master_ctl->ops.update_pending_flush(master_ctl, flush_mask);
+
+		/* explicitly trigger rotator for async modes */
+		if (cstate->sbuf_cfg.rot_op_mode ==
+				SDE_CTL_ROT_OP_MODE_INLINE_ASYNC &&
+				master_ctl->ops.trigger_rot_start) {
+			master_ctl->ops.trigger_rot_start(master_ctl);
+			SDE_EVT32(DRMID(crtc), master_ctl->idx - CTL_0);
+		}
+	}
+
+	SDE_ATRACE_END("crtc_kickoff_rot");
+	return rc;
+}
+
+/**
+ * _sde_crtc_remove_pipe_flush - remove staged pipes from flush mask
+ * @sde_crtc: Pointer to sde crtc structure
+ */
+static void _sde_crtc_remove_pipe_flush(struct sde_crtc *sde_crtc)
+{
+	struct sde_crtc_mixer *mixer;
+	struct sde_hw_ctl *ctl;
+	u32 i, flush_mask;
+
+	if (!sde_crtc)
+		return;
+
+	mixer = sde_crtc->mixers;
+	for (i = 0; i < sde_crtc->num_mixers; i++) {
+		ctl = mixer[i].hw_ctl;
+		if (!ctl || !ctl->ops.get_pending_flush ||
+				!ctl->ops.clear_pending_flush ||
+				!ctl->ops.update_pending_flush)
+			continue;
+
+		flush_mask = ctl->ops.get_pending_flush(ctl);
+		flush_mask &= ~mixer[i].pipe_mask;
+		ctl->ops.clear_pending_flush(ctl);
+		ctl->ops.update_pending_flush(ctl, flush_mask);
+	}
+}
+
+/**
+ * _sde_crtc_reset_hw - attempt hardware reset on errors
+ * @crtc: Pointer to DRM crtc instance
+ * @old_state: Pointer to crtc state for previous commit
+ * @dump_status: Whether or not to dump debug status before reset
+ * Returns: Zero if current commit should still be attempted
+ */
+static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state, bool dump_status)
+{
+	struct drm_plane *plane_halt[MAX_PLANES];
+	struct drm_plane *plane;
+	const struct drm_plane_state *pstate;
+	struct sde_crtc *sde_crtc;
+	struct sde_hw_ctl *ctl;
+	enum sde_ctl_rot_op_mode old_rot_op_mode;
+	signed int i, plane_count;
+	int rc;
+
+	if (!crtc || !old_state)
+		return -EINVAL;
+	sde_crtc = to_sde_crtc(crtc);
+
+	old_rot_op_mode = to_sde_crtc_state(old_state)->sbuf_cfg.rot_op_mode;
+	SDE_EVT32(DRMID(crtc), old_rot_op_mode,
+			dump_status, SDE_EVTLOG_FUNC_ENTRY);
+
+	if (dump_status)
+		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus");
+
+	for (i = 0; i < sde_crtc->num_mixers; ++i) {
+		ctl = sde_crtc->mixers[i].hw_ctl;
+		if (!ctl || !ctl->ops.reset)
+			continue;
+
+		rc = ctl->ops.reset(ctl);
+		if (rc) {
+			SDE_DEBUG("crtc%d: ctl%d reset failure\n",
+					crtc->base.id, ctl->idx - CTL_0);
+			SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0,
+					SDE_EVTLOG_ERROR);
+			break;
+		}
+	}
+
+	/*
+	 * Early out if simple ctl reset succeeded and previous commit
+	 * did not involve the rotator.
+	 *
+	 * If the previous commit had rotation enabled, then the ctl
+	 * reset would also have reset the rotator h/w. The rotator
+	 * programming for the current commit may need to be repeated,
+	 * depending on the rotation mode; don't handle this for now
+	 * and just force a hard reset in those cases.
+	 */
+	if (i == sde_crtc->num_mixers &&
+			old_rot_op_mode == SDE_CTL_ROT_OP_MODE_OFFLINE)
+		return false;
+
+	SDE_DEBUG("crtc%d: issuing hard reset\n", DRMID(crtc));
+
+	/* force all components in the system into reset at the same time */
+	for (i = 0; i < sde_crtc->num_mixers; ++i) {
+		ctl = sde_crtc->mixers[i].hw_ctl;
+		if (!ctl || !ctl->ops.hard_reset)
+			continue;
+
+		SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0);
+		ctl->ops.hard_reset(ctl, true);
+	}
+
+	plane_count = 0;
+	drm_atomic_crtc_state_for_each_plane(plane, old_state) {
+		if (plane_count >= ARRAY_SIZE(plane_halt))
+			break;
+
+		plane_halt[plane_count++] = plane;
+		sde_plane_halt_requests(plane, true);
+		sde_plane_set_revalidate(plane, true);
+	}
+
+	/* reset both previous... */
+	for_each_plane_in_state(old_state->state, plane, pstate, i) {
+		if (pstate->crtc != crtc)
+			continue;
+
+		sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
+	}
+
+	/* ...and current rotation attempts, if applicable */
+	drm_atomic_crtc_for_each_plane(plane, crtc) {
+		pstate = plane->state;
+		if (!pstate)
+			continue;
+
+		sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
+	}
+
+	/* take h/w components out of reset */
+	for (i = plane_count - 1; i >= 0; --i)
+		sde_plane_halt_requests(plane_halt[i], false);
+
+	for (i = 0; i < sde_crtc->num_mixers; ++i) {
+		ctl = sde_crtc->mixers[i].hw_ctl;
+		if (!ctl || !ctl->ops.hard_reset)
+			continue;
+
+		ctl->ops.hard_reset(ctl, false);
+	}
+
+	return -EAGAIN;
+}
+
+/**
+ * _sde_crtc_prepare_for_kickoff_rot - rotator related kickoff preparation
+ * @dev: Pointer to drm device
+ * @crtc: Pointer to crtc structure
+ * Returns: true on preparation errors
+ */
+static bool _sde_crtc_prepare_for_kickoff_rot(struct drm_device *dev,
+		struct drm_crtc *crtc)
+{
+	struct drm_encoder *encoder;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+
+	if (!crtc || !dev) {
+		SDE_ERROR("invalid argument(s)\n");
+		return false;
+	}
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc->state);
+
+	/* default to ASYNC mode for inline rotation */
+	cstate->sbuf_cfg.rot_op_mode = sde_crtc->sbuf_flush_mask ?
+		SDE_CTL_ROT_OP_MODE_INLINE_ASYNC : SDE_CTL_ROT_OP_MODE_OFFLINE;
+
+	if (cstate->sbuf_cfg.rot_op_mode == SDE_CTL_ROT_OP_MODE_OFFLINE)
+		return false;
+
+	/* extra steps needed for inline ASYNC modes */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		if (encoder->crtc != crtc)
+			continue;
+
+		/*
+		 * For inline ASYNC modes, the flush bits are not written
+		 * to hardware atomically, so avoid using it if a video
+		 * mode encoder is active on this CRTC.
+		 */
+		if (sde_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO) {
+			cstate->sbuf_cfg.rot_op_mode =
+				SDE_CTL_ROT_OP_MODE_INLINE_SYNC;
+			return false;
+		}
+	}
+
+	/*
+	 * For ASYNC inline modes, kick off the rotator now so that the H/W
+	 * can start as soon as it's ready.
+	 */
+	if (_sde_crtc_commit_kickoff_rot(crtc, cstate))
+		return true;
+
+	return false;
+}
+
+void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state)
+{
 	struct drm_encoder *encoder;
 	struct drm_device *dev;
 	struct sde_crtc *sde_crtc;
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
 	struct sde_crtc_state *cstate;
-	struct sde_hw_ctl *ctl;
-	int ret, i;
+	bool is_error, reset_req;
+	int ret;
 
 	if (!crtc) {
 		SDE_ERROR("invalid argument\n");
@@ -2759,6 +3588,7 @@
 	dev = crtc->dev;
 	sde_crtc = to_sde_crtc(crtc);
 	sde_kms = _sde_crtc_get_kms(crtc);
+	reset_req = false;
 
 	if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) {
 		SDE_ERROR("invalid argument\n");
@@ -2777,6 +3607,9 @@
 		return;
 
 	SDE_ATRACE_BEGIN("crtc_commit");
+
+	is_error = _sde_crtc_prepare_for_kickoff_rot(dev, crtc);
+
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		struct sde_encoder_kickoff_params params = { 0 };
 
@@ -2790,9 +3623,26 @@
 		params.inline_rotate_prefill = cstate->sbuf_prefill_line;
 		params.affected_displays = _sde_crtc_get_displays_affected(crtc,
 				crtc->state);
-		sde_encoder_prepare_for_kickoff(encoder, &params);
+		if (sde_encoder_prepare_for_kickoff(encoder, &params))
+			reset_req = true;
 	}
 
+	/*
+	 * Optionally attempt h/w recovery if any errors were detected while
+	 * preparing for the kickoff
+	 */
+	if (reset_req) {
+		if (_sde_crtc_reset_hw(crtc, old_state,
+					!sde_crtc->reset_request))
+			is_error = true;
+
+		/* force offline rotation mode since the commit has no pipes */
+		if (is_error)
+			cstate->sbuf_cfg.rot_op_mode =
+				SDE_CTL_ROT_OP_MODE_OFFLINE;
+	}
+	sde_crtc->reset_request = reset_req;
+
 	/* wait for frame_event_done completion */
 	SDE_ATRACE_BEGIN("wait_for_frame_done_event");
 	ret = _sde_crtc_wait_for_frame_done(crtc);
@@ -2801,39 +3651,50 @@
 		SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
 				crtc->base.id,
 				atomic_read(&sde_crtc->frame_pending));
-		goto end;
+
+		is_error = true;
+
+		/* force offline rotation mode since the commit has no pipes */
+		cstate->sbuf_cfg.rot_op_mode = SDE_CTL_ROT_OP_MODE_OFFLINE;
 	}
 
 	if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
 		/* acquire bandwidth and other resources */
 		SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
-		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_CASE1);
+		SDE_EVT32(DRMID(crtc), cstate->sbuf_cfg.rot_op_mode,
+				SDE_EVTLOG_FUNC_CASE1);
 	} else {
 		SDE_DEBUG("crtc%d commit\n", crtc->base.id);
-		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_CASE2);
+		SDE_EVT32(DRMID(crtc), cstate->sbuf_cfg.rot_op_mode,
+				SDE_EVTLOG_FUNC_CASE2);
 	}
 	sde_crtc->play_count++;
 
-	if (cstate->sbuf_cfg.rot_op_mode != SDE_CTL_ROT_OP_MODE_OFFLINE)
-		drm_atomic_crtc_for_each_plane(plane, crtc)
-			sde_plane_kickoff(plane);
-
-	for (i = 0; i < sde_crtc->num_mixers; i++) {
-		ctl = sde_crtc->mixers[i].hw_ctl;
-		if (ctl && ctl->ops.setup_sbuf_cfg)
-			ctl->ops.setup_sbuf_cfg(ctl, &cstate->sbuf_cfg);
-	}
+	/*
+	 * For SYNC inline modes, delay the kick off until after the
+	 * wait for frame done in case the wait times out.
+	 *
+	 * Also perform a final kickoff when transitioning back to
+	 * offline mode.
+	 */
+	if (cstate->sbuf_cfg.rot_op_mode != SDE_CTL_ROT_OP_MODE_INLINE_ASYNC)
+		if (_sde_crtc_commit_kickoff_rot(crtc, cstate))
+			is_error = true;
 
 	sde_vbif_clear_errors(sde_kms);
 
+	if (is_error) {
+		_sde_crtc_remove_pipe_flush(sde_crtc);
+		_sde_crtc_blend_setup(crtc, false);
+	}
+
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc != crtc)
 			continue;
 
-		sde_encoder_kickoff(encoder);
+		sde_encoder_kickoff(encoder, false);
 	}
 
-end:
 	reinit_completion(&sde_crtc->frame_done_comp);
 	SDE_ATRACE_END("crtc_commit");
 	return;
@@ -2983,6 +3844,9 @@
 			old_cstate, cstate,
 			&cstate->property_state, cstate->property_values);
 
+	/* clear destination scaler dirty bit */
+	cstate->ds_dirty = false;
+
 	/* duplicate base helper */
 	__drm_atomic_helper_crtc_duplicate_state(crtc, &cstate->base);
 
@@ -3008,9 +3872,16 @@
 	}
 
 	/* revert suspend actions, if necessary */
-	if (sde_kms_is_suspend_state(crtc->dev))
+	if (sde_kms_is_suspend_state(crtc->dev)) {
 		_sde_crtc_set_suspend(crtc, false);
 
+		if (!sde_crtc_is_reset_required(crtc)) {
+			SDE_DEBUG("avoiding reset for crtc:%d\n",
+					crtc->base.id);
+			return;
+		}
+	}
+
 	/* remove previous state, if present */
 	if (crtc->state) {
 		sde_crtc_destroy_state(crtc, crtc->state);
@@ -3042,16 +3913,22 @@
 {
 	struct drm_crtc *crtc = arg;
 	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
 	struct drm_plane *plane;
 	struct drm_encoder *encoder;
 	struct sde_crtc_mixer *m;
-	u32 i, misr_status;
+	u32 i, misr_status, power_on;
+	unsigned long flags;
+	struct sde_crtc_irq_info *node = NULL;
+	int ret = 0;
+	struct drm_event event;
 
 	if (!crtc) {
 		SDE_ERROR("invalid crtc\n");
 		return;
 	}
 	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc->state);
 
 	mutex_lock(&sde_crtc->crtc_lock);
 
@@ -3066,8 +3943,26 @@
 
 			sde_encoder_virt_restore(encoder);
 		}
+
+		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+		list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+			ret = 0;
+			if (node->func)
+				ret = node->func(crtc, true, &node->irq);
+			if (ret)
+				SDE_ERROR("%s failed to enable event %x\n",
+						sde_crtc->name, node->event);
+		}
+		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
 		sde_cp_crtc_post_ipc(crtc);
 
+		event.type = DRM_EVENT_SDE_POWER;
+		event.length = sizeof(power_on);
+		power_on = 1;
+		msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
+				(u8 *)&power_on);
+
 		for (i = 0; i < sde_crtc->num_mixers; ++i) {
 			m = &sde_crtc->mixers[i];
 			if (!m->hw_lm || !m->hw_lm->ops.setup_misr ||
@@ -3079,6 +3974,21 @@
 		}
 		break;
 	case SDE_POWER_EVENT_PRE_DISABLE:
+		drm_for_each_encoder(encoder, crtc->dev) {
+			if (encoder->crtc != crtc)
+				continue;
+			/*
+			 * disable the vsync source after updating the
+			 * rsc state. rsc state update might have vsync wait
+			 * and vsync source must be disabled after it.
+			 * It will avoid generating any vsync from this point
+			 * till mode-2 entry. It is SW workaround for HW
+			 * limitation and should not be removed without
+			 * checking the updated design.
+			 */
+			sde_encoder_control_te(encoder, false);
+		}
+
 		for (i = 0; i < sde_crtc->num_mixers; ++i) {
 			m = &sde_crtc->mixers[i];
 			if (!m->hw_lm || !m->hw_lm->ops.collect_misr ||
@@ -3089,6 +3999,19 @@
 			sde_crtc->misr_data[i] = misr_status ? misr_status :
 							sde_crtc->misr_data[i];
 		}
+
+		spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+		node = NULL;
+		list_for_each_entry(node, &sde_crtc->user_event_list, list) {
+			ret = 0;
+			if (node->func)
+				ret = node->func(crtc, false, &node->irq);
+			if (ret)
+				SDE_ERROR("%s failed to disable event %x\n",
+						sde_crtc->name, node->event);
+		}
+		spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
 		sde_cp_crtc_pre_ipc(crtc);
 		break;
 	case SDE_POWER_EVENT_POST_DISABLE:
@@ -3100,6 +4023,19 @@
 			sde_plane_set_revalidate(plane, true);
 
 		sde_cp_crtc_suspend(crtc);
+
+		/**
+		 * destination scaler if enabled should be reconfigured
+		 * in the next frame update
+		 */
+		if (cstate->num_ds_enabled)
+			sde_crtc->ds_reconfig = true;
+
+		event.type = DRM_EVENT_SDE_POWER;
+		event.length = sizeof(power_on);
+		power_on = 0;
+		msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
+				(u8 *)&power_on);
 		break;
 	default:
 		SDE_DEBUG("event:%d not handled\n", event_type);
@@ -3125,6 +4061,12 @@
 		SDE_ERROR("invalid crtc\n");
 		return;
 	}
+
+	if (!sde_kms_power_resource_is_enabled(crtc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	sde_crtc = to_sde_crtc(crtc);
 	cstate = to_sde_crtc_state(crtc->state);
 	priv = crtc->dev->dev_private;
@@ -3145,6 +4087,10 @@
 	msm_mode_object_event_notify(&crtc->base, crtc->dev, &event,
 			(u8 *)&power_on);
 
+	/* destination scaler if enabled should be reconfigured on resume */
+	if (cstate->num_ds_enabled)
+		sde_crtc->ds_reconfig = true;
+
 	/* wait for frame_event_done completion */
 	if (_sde_crtc_wait_for_frame_done(crtc))
 		SDE_ERROR("crtc%d wait for frame done failed;frame_pending%d\n",
@@ -3152,7 +4098,8 @@
 				atomic_read(&sde_crtc->frame_pending));
 
 	SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
-			sde_crtc->vblank_requested);
+			sde_crtc->vblank_requested,
+			crtc->state->active, crtc->state->enable);
 	if (sde_crtc->enabled && !sde_crtc->suspend &&
 			sde_crtc->vblank_requested) {
 		ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, false);
@@ -3197,14 +4144,18 @@
 	/**
 	 * All callbacks are unregistered and frame done waits are complete
 	 * at this point. No buffers are accessed by hardware.
-	 * reset the fence timeline if there is any issue.
+	 * reset the fence timeline if crtc will not be enabled for this commit
 	 */
-	sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
-	for (i = 0; i < cstate->num_connectors; ++i)
-		sde_connector_commit_reset(cstate->connectors[i], ktime_get());
+	if (!crtc->state->active || !crtc->state->enable) {
+		sde_fence_signal(&sde_crtc->output_fence, ktime_get(), true);
+		for (i = 0; i < cstate->num_connectors; ++i)
+			sde_connector_commit_reset(cstate->connectors[i],
+					ktime_get());
+	}
 
 	memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
 	sde_crtc->num_mixers = 0;
+	sde_crtc->mixers_swapped = false;
 
 	/* disable clk & bw control until clk & bw properties are set */
 	cstate->bw_control = false;
@@ -3230,10 +4181,31 @@
 	}
 	priv = crtc->dev->dev_private;
 
+	if (!sde_kms_power_resource_is_enabled(crtc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 	SDE_EVT32_VERBOSE(DRMID(crtc));
 	sde_crtc = to_sde_crtc(crtc);
 
+	mutex_lock(&sde_crtc->crtc_lock);
+	SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
+			sde_crtc->vblank_requested);
+
+	/* return early if crtc is already enabled */
+	if (sde_crtc->enabled) {
+		if (msm_is_mode_seamless_dms(&crtc->state->adjusted_mode))
+			SDE_DEBUG("%s extra crtc enable expected during DMS\n",
+					sde_crtc->name);
+		else
+			WARN(1, "%s unexpected crtc enable\n", sde_crtc->name);
+
+		mutex_unlock(&sde_crtc->crtc_lock);
+		return;
+	}
+
 	drm_for_each_encoder(encoder, crtc->dev) {
 		if (encoder->crtc != crtc)
 			continue;
@@ -3241,9 +4213,6 @@
 				sde_crtc_frame_event_cb, (void *)crtc);
 	}
 
-	mutex_lock(&sde_crtc->crtc_lock);
-	SDE_EVT32(DRMID(crtc), sde_crtc->enabled, sde_crtc->suspend,
-			sde_crtc->vblank_requested);
 	if (!sde_crtc->enabled && !sde_crtc->suspend &&
 			sde_crtc->vblank_requested) {
 		ret = _sde_crtc_vblank_enable_no_lock(sde_crtc, true);
@@ -3383,13 +4352,14 @@
 }
 
 static int _sde_crtc_check_secure_state(struct drm_crtc *crtc,
-		struct drm_crtc_state *state)
+		struct drm_crtc_state *state, struct plane_state pstates[],
+		int cnt)
 {
 	struct drm_encoder *encoder;
 	struct sde_crtc_state *cstate;
 	uint32_t secure;
-	uint32_t fb_ns = 0, fb_sec = 0, fb_ns_dir = 0, fb_sec_dir = 0;
-	int encoder_cnt = 0;
+	uint32_t fb_ns = 0, fb_sec = 0, fb_sec_dir = 0;
+	int encoder_cnt = 0, i;
 	int rc;
 
 	if (!crtc || !state) {
@@ -3399,55 +4369,57 @@
 
 	cstate = to_sde_crtc_state(state);
 
-	secure = sde_crtc_get_property(cstate,
-			CRTC_PROP_SECURITY_LEVEL);
+	secure = sde_crtc_get_property(cstate, CRTC_PROP_SECURITY_LEVEL);
 
-	rc = _sde_crtc_find_plane_fb_modes(state,
-			&fb_ns,
-			&fb_sec,
-			&fb_ns_dir,
-			&fb_sec_dir);
+	rc = _sde_crtc_find_plane_fb_modes(state, &fb_ns, &fb_sec, &fb_sec_dir);
 	if (rc)
 		return rc;
 
-	/**
-	 * validate planes
-	 * fb_ns_dir is for  secure display use case,
-	 * fb_sec_dir is for secure camera preview use case,
-	 * fb_sec is for secure video playback,
-	 * fb_ns is for normal non secure use cases.
-	 */
-	if (((secure == SDE_DRM_SEC_ONLY) &&
-				(fb_ns || fb_sec || fb_sec_dir)) ||
-			(fb_sec && fb_sec_dir)) {
-		SDE_ERROR(
-			"crtc%d: invalid planes fb_modes Sec:%d, NS:%d, Sec_Dir:%d, NS_Dir%d\n",
-				crtc->base.id,
-				fb_sec, fb_ns, fb_sec_dir,
-				fb_ns_dir);
-		return -EINVAL;
+	if (secure == SDE_DRM_SEC_ONLY) {
+		/*
+		 * validate planes - only fb_sec_dir is allowed during sec_crtc
+		 * - fb_sec_dir is for secure camera preview and
+		 * secure display use case
+		 * - fb_sec is for secure video playback
+		 * - fb_ns is for normal non secure use cases
+		 */
+		if (fb_ns || fb_sec) {
+			SDE_ERROR(
+			 "crtc%d: invalid fb_modes Sec:%d, NS:%d, Sec_Dir:%d\n",
+				crtc->base.id, fb_sec, fb_ns, fb_sec_dir);
+			return -EINVAL;
+		}
+
+		/* only one blending stage is allowed in sec_crtc */
+		for (i = 1; i < cnt; i++) {
+			if (pstates[i].stage != pstates[i-1].stage) {
+				SDE_ERROR(
+				  "crtc%d: invalid blend stages %d:%d, %d:%d\n",
+				  crtc->base.id, i, pstates[i].stage,
+				  i-1, pstates[i-1].stage);
+				return -EINVAL;
+			}
+		}
 	}
 
-	/**
+	/*
 	 * secure_crtc is not allowed in a shared toppolgy
 	 * across different encoders.
 	 */
-	if (fb_ns_dir || fb_sec_dir) {
+	if (fb_sec_dir) {
 		drm_for_each_encoder(encoder, crtc->dev)
 			if (encoder->crtc ==  crtc)
 				encoder_cnt++;
 
-		if (encoder_cnt >
-			MAX_ALLOWED_ENCODER_CNT_PER_SECURE_CRTC) {
-			SDE_ERROR(
-				"crtc%d, invalid virtual encoder crtc%d\n",
-				crtc->base.id,
-				encoder_cnt);
+		if (encoder_cnt > MAX_ALLOWED_ENCODER_CNT_PER_SECURE_CRTC) {
+			SDE_ERROR("crtc%d, invalid virtual encoder crtc%d\n",
+				crtc->base.id, encoder_cnt);
 			return -EINVAL;
 
 		}
 	}
 	SDE_DEBUG("crtc:%d Secure validation successful\n", crtc->base.id);
+
 	return 0;
 }
 
@@ -3455,7 +4427,7 @@
 		struct drm_crtc_state *state)
 {
 	struct sde_crtc *sde_crtc;
-	struct plane_state pstates[SDE_STAGE_MAX * 4];
+	struct plane_state *pstates = NULL;
 	struct sde_crtc_state *cstate;
 
 	const struct drm_plane_state *pstate;
@@ -3464,7 +4436,7 @@
 
 	int cnt = 0, rc = 0, mixer_width, i, z_pos;
 
-	struct sde_multirect_plane_states multirect_plane[SDE_STAGE_MAX * 2];
+	struct sde_multirect_plane_states *multirect_plane = NULL;
 	int multirect_count = 0;
 	const struct drm_plane_state *pipe_staged[SSPP_MAX];
 	int left_zpos_cnt = 0, right_zpos_cnt = 0;
@@ -3483,6 +4455,17 @@
 		goto end;
 	}
 
+	pstates = kzalloc(SDE_PSTATES_MAX *
+			sizeof(struct plane_state), GFP_KERNEL);
+
+	multirect_plane = kzalloc(SDE_MULTIRECT_PLANE_MAX *
+		sizeof(struct sde_multirect_plane_states), GFP_KERNEL);
+
+	if (!pstates || !multirect_plane) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
 	mode = &state->adjusted_mode;
 	SDE_DEBUG("%s: check", sde_crtc->name);
 
@@ -3492,15 +4475,18 @@
 
 	memset(pipe_staged, 0, sizeof(pipe_staged));
 
-	mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
+	rc = _sde_crtc_check_dest_scaler_data(crtc, state);
+	if (rc) {
+		SDE_ERROR("crtc%d failed dest scaler check %d\n",
+			crtc->base.id, rc);
+		goto end;
+	}
+
+	mixer_width = sde_crtc_get_mixer_width(sde_crtc, cstate, mode);
 
 	_sde_crtc_setup_is_ppsplit(state);
 	_sde_crtc_setup_lm_bounds(crtc, state);
 
-	rc = _sde_crtc_check_secure_state(crtc, state);
-	if (rc)
-		return rc;
-
 	 /* get plane state for all drm planes associated with crtc state */
 	drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) {
 		if (IS_ERR_OR_NULL(pstate)) {
@@ -3509,7 +4495,7 @@
 					sde_crtc->name, plane->base.id, rc);
 			goto end;
 		}
-		if (cnt >= ARRAY_SIZE(pstates))
+		if (cnt >= SDE_PSTATES_MAX)
 			continue;
 
 		pstates[cnt].sde_pstate = to_sde_plane_state(pstate);
@@ -3574,6 +4560,10 @@
 	/* assign mixer stages based on sorted zpos property */
 	sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
 
+	rc = _sde_crtc_check_secure_state(crtc, state, pstates, cnt);
+	if (rc)
+		goto end;
+
 	rc = _sde_crtc_excl_dim_layer_check(state, pstates, cnt);
 	if (rc)
 		goto end;
@@ -3719,6 +4709,8 @@
 	}
 
 end:
+	kfree(pstates);
+	kfree(multirect_plane);
 	_sde_crtc_rp_free_unused(&cstate->rp);
 	return rc;
 }
@@ -3757,6 +4749,57 @@
 	_sde_crtc_complete_flip(crtc, file);
 }
 
+int sde_crtc_helper_reset_custom_properties(struct drm_crtc *crtc,
+		struct drm_crtc_state *crtc_state)
+{
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	struct drm_property *drm_prop;
+	enum msm_mdp_crtc_property prop_idx;
+
+	if (!crtc || !crtc_state) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(crtc_state);
+
+	for (prop_idx = 0; prop_idx < CRTC_PROP_COUNT; prop_idx++) {
+		uint64_t val = cstate->property_values[prop_idx].value;
+		uint64_t def;
+		int ret;
+
+		drm_prop = msm_property_index_to_drm_property(
+				&sde_crtc->property_info, prop_idx);
+		if (!drm_prop) {
+			/* not all props will be installed, based on caps */
+			SDE_DEBUG("%s: invalid property index %d\n",
+					sde_crtc->name, prop_idx);
+			continue;
+		}
+
+		def = msm_property_get_default(&sde_crtc->property_info,
+				prop_idx);
+		if (val == def)
+			continue;
+
+		SDE_DEBUG("%s: set prop %s idx %d from %llu to %llu\n",
+				sde_crtc->name, drm_prop->name, prop_idx, val,
+				def);
+
+		ret = drm_atomic_crtc_set_property(crtc, crtc_state, drm_prop,
+				def);
+		if (ret) {
+			SDE_ERROR("%s: set property failed, idx %d ret %d\n",
+					sde_crtc->name, prop_idx, ret);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
 /**
  * sde_crtc_install_properties - install all drm properties for crtc
  * @crtc: Pointer to drm crtc structure
@@ -3800,8 +4843,8 @@
 		"input_fence_timeout", 0x0, 0, SDE_CRTC_MAX_INPUT_FENCE_TIMEOUT,
 		SDE_CRTC_INPUT_FENCE_TIMEOUT, CRTC_PROP_INPUT_FENCE_TIMEOUT);
 
-	msm_property_install_range(&sde_crtc->property_info, "output_fence",
-			0x0, 0, INR_OPEN_MAX, 0x0, CRTC_PROP_OUTPUT_FENCE);
+	msm_property_install_volatile_range(&sde_crtc->property_info,
+		"output_fence", 0x0, 0, ~0, 0, CRTC_PROP_OUTPUT_FENCE);
 
 	msm_property_install_range(&sde_crtc->property_info,
 			"output_fence_offset", 0x0, 0, 1, 0,
@@ -3845,7 +4888,7 @@
 			CRTC_PROP_ROT_CLK);
 
 	msm_property_install_range(&sde_crtc->property_info,
-		"idle_timeout", IDLE_TIMEOUT, 0, U64_MAX, 0,
+		"idle_time", 0, 0, U64_MAX, 0,
 		CRTC_PROP_IDLE_TIMEOUT);
 
 	msm_property_install_blob(&sde_crtc->property_info, "capabilities",
@@ -3887,13 +4930,56 @@
 					"smart_dma_rev", "smart_dma_v2");
 	}
 
+	if (catalog->mdp[0].has_dest_scaler) {
+		sde_kms_info_add_keyint(info, "has_dest_scaler",
+				catalog->mdp[0].has_dest_scaler);
+		sde_kms_info_add_keyint(info, "dest_scaler_count",
+					catalog->ds_count);
+
+		if (catalog->ds[0].top) {
+			sde_kms_info_add_keyint(info,
+					"max_dest_scaler_input_width",
+					catalog->ds[0].top->maxinputwidth);
+			sde_kms_info_add_keyint(info,
+					"max_dest_scaler_output_width",
+					catalog->ds[0].top->maxinputwidth);
+			sde_kms_info_add_keyint(info, "max_dest_scale_up",
+					catalog->ds[0].top->maxupscale);
+		}
+
+		if (catalog->ds[0].features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+			msm_property_install_volatile_range(
+					&sde_crtc->property_info, "dest_scaler",
+					0x0, 0, ~0, 0, CRTC_PROP_DEST_SCALER);
+			msm_property_install_blob(&sde_crtc->property_info,
+					"ds_lut_ed", 0,
+					CRTC_PROP_DEST_SCALER_LUT_ED);
+			msm_property_install_blob(&sde_crtc->property_info,
+					"ds_lut_cir", 0,
+					CRTC_PROP_DEST_SCALER_LUT_CIR);
+			msm_property_install_blob(&sde_crtc->property_info,
+					"ds_lut_sep", 0,
+					CRTC_PROP_DEST_SCALER_LUT_SEP);
+		}
+	}
+
 	sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
+	sde_kms_info_add_keyint(info, "has_hdr", catalog->has_hdr);
 	if (catalog->perf.max_bw_low)
 		sde_kms_info_add_keyint(info, "max_bandwidth_low",
 				catalog->perf.max_bw_low * 1000LL);
 	if (catalog->perf.max_bw_high)
 		sde_kms_info_add_keyint(info, "max_bandwidth_high",
 				catalog->perf.max_bw_high * 1000LL);
+	if (catalog->perf.min_core_ib)
+		sde_kms_info_add_keyint(info, "min_core_ib",
+				catalog->perf.min_core_ib * 1000LL);
+	if (catalog->perf.min_llcc_ib)
+		sde_kms_info_add_keyint(info, "min_llcc_ib",
+				catalog->perf.min_llcc_ib * 1000LL);
+	if (catalog->perf.min_dram_ib)
+		sde_kms_info_add_keyint(info, "min_dram_ib",
+				catalog->perf.min_dram_ib * 1000LL);
 	if (sde_kms->perf.max_core_clk_rate)
 		sde_kms_info_add_keyint(info, "max_mdp_clk",
 				sde_kms->perf.max_core_clk_rate);
@@ -3930,6 +5016,47 @@
 	kfree(info);
 }
 
+static int _sde_crtc_get_output_fence(struct drm_crtc *crtc,
+	const struct drm_crtc_state *state, uint64_t *val)
+{
+	struct drm_encoder *encoder;
+	struct sde_crtc *sde_crtc;
+	struct sde_crtc_state *cstate;
+	uint32_t offset, i;
+	bool conn_offset = 0, is_cmd = true;
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(state);
+
+	for (i = 0; i < cstate->num_connectors; ++i) {
+		conn_offset = sde_connector_needs_offset(cstate->connectors[i]);
+		if (conn_offset)
+			break;
+	}
+
+	/**
+	 * set the cmd flag only when all the encoders attached
+	 * to the crtc are in cmd mode. Consider all other cases
+	 * as video mode.
+	 */
+	drm_for_each_encoder(encoder, crtc->dev) {
+		if (encoder->crtc == crtc)
+			is_cmd = sde_encoder_check_mode(encoder,
+					MSM_DISPLAY_CAP_CMD_MODE);
+	}
+
+	offset = sde_crtc_get_property(cstate, CRTC_PROP_OUTPUT_FENCE_OFFSET);
+
+	/**
+	 * set the offset to 0 only for cmd mode panels, so
+	 * the release fence for the current frame can be
+	 * triggered right after PP_DONE interrupt.
+	 */
+	offset = is_cmd ? 0 : (offset + conn_offset);
+
+	return sde_fence_create(&sde_crtc->output_fence, val, offset);
+}
+
 /**
  * sde_crtc_atomic_set_property - atomically set a crtc drm property
  * @crtc: Pointer to drm crtc structure
@@ -3945,57 +5072,92 @@
 {
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *cstate;
-	int idx, ret = -EINVAL;
+	int idx, ret;
+	uint64_t fence_fd;
 
 	if (!crtc || !state || !property) {
 		SDE_ERROR("invalid argument(s)\n");
-	} else {
-		sde_crtc = to_sde_crtc(crtc);
-		cstate = to_sde_crtc_state(state);
-		ret = msm_property_atomic_set(&sde_crtc->property_info,
-				&cstate->property_state, property, val);
-		if (!ret) {
-			idx = msm_property_index(&sde_crtc->property_info,
-					property);
-			switch (idx) {
-			case CRTC_PROP_INPUT_FENCE_TIMEOUT:
-				_sde_crtc_set_input_fence_timeout(cstate);
-				break;
-			case CRTC_PROP_DIM_LAYER_V1:
-				_sde_crtc_set_dim_layer_v1(cstate, (void *)val);
-				break;
-			case CRTC_PROP_ROI_V1:
-				ret = _sde_crtc_set_roi_v1(state, (void *)val);
-				break;
-			case CRTC_PROP_CORE_CLK:
-			case CRTC_PROP_CORE_AB:
-			case CRTC_PROP_CORE_IB:
-				cstate->bw_control = true;
-				break;
-			case CRTC_PROP_LLCC_AB:
-			case CRTC_PROP_LLCC_IB:
-			case CRTC_PROP_DRAM_AB:
-			case CRTC_PROP_DRAM_IB:
-				cstate->bw_control = true;
-				cstate->bw_split_vote = true;
-				break;
-			case CRTC_PROP_IDLE_TIMEOUT:
-				_sde_crtc_set_idle_timeout(crtc, val);
-			default:
-				/* nothing to do */
-				break;
-			}
-		} else {
-			ret = sde_cp_crtc_set_property(crtc,
-					property, val);
-		}
-		if (ret)
-			DRM_ERROR("failed to set the property\n");
-
-		SDE_DEBUG("crtc%d %s[%d] <= 0x%llx ret=%d\n", crtc->base.id,
-				property->name, property->base.id, val, ret);
+		return -EINVAL;
 	}
 
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(state);
+
+	/* check with cp property system first */
+	ret = sde_cp_crtc_set_property(crtc, property, val);
+	if (ret != -ENOENT)
+		goto exit;
+
+	/* if not handled by cp, check msm_property system */
+	ret = msm_property_atomic_set(&sde_crtc->property_info,
+			&cstate->property_state, property, val);
+	if (ret)
+		goto exit;
+
+	idx = msm_property_index(&sde_crtc->property_info, property);
+	switch (idx) {
+	case CRTC_PROP_INPUT_FENCE_TIMEOUT:
+		_sde_crtc_set_input_fence_timeout(cstate);
+		break;
+	case CRTC_PROP_DIM_LAYER_V1:
+		_sde_crtc_set_dim_layer_v1(cstate, (void __user *)val);
+		break;
+	case CRTC_PROP_ROI_V1:
+		ret = _sde_crtc_set_roi_v1(state, (void __user *)val);
+		break;
+	case CRTC_PROP_DEST_SCALER:
+		ret = _sde_crtc_set_dest_scaler(sde_crtc, cstate,
+				(void __user *)val);
+		break;
+	case CRTC_PROP_DEST_SCALER_LUT_ED:
+	case CRTC_PROP_DEST_SCALER_LUT_CIR:
+	case CRTC_PROP_DEST_SCALER_LUT_SEP:
+		ret = _sde_crtc_set_dest_scaler_lut(sde_crtc, cstate, idx);
+		break;
+	case CRTC_PROP_CORE_CLK:
+	case CRTC_PROP_CORE_AB:
+	case CRTC_PROP_CORE_IB:
+		cstate->bw_control = true;
+		break;
+	case CRTC_PROP_LLCC_AB:
+	case CRTC_PROP_LLCC_IB:
+	case CRTC_PROP_DRAM_AB:
+	case CRTC_PROP_DRAM_IB:
+		cstate->bw_control = true;
+		cstate->bw_split_vote = true;
+		break;
+	case CRTC_PROP_OUTPUT_FENCE:
+		if (!val)
+			goto exit;
+
+		ret = _sde_crtc_get_output_fence(crtc, state, &fence_fd);
+		if (ret) {
+			SDE_ERROR("fence create failed rc:%d\n", ret);
+			goto exit;
+		}
+
+		ret = copy_to_user((uint64_t __user *)val, &fence_fd,
+				sizeof(uint64_t));
+		if (ret) {
+			SDE_ERROR("copy to user failed rc:%d\n", ret);
+			put_unused_fd(fence_fd);
+			ret = -EFAULT;
+			goto exit;
+		}
+		break;
+	default:
+		/* nothing to do */
+		break;
+	}
+
+exit:
+	if (ret)
+		SDE_ERROR("%s: failed to set property%d %s: %d\n", crtc->name,
+				DRMID(property), property->name, ret);
+	else
+		SDE_DEBUG("%s: %s[%d] <= 0x%llx\n", crtc->name, property->name,
+				property->base.id, val);
+
 	return ret;
 }
 
@@ -4029,62 +5191,29 @@
 {
 	struct sde_crtc *sde_crtc;
 	struct sde_crtc_state *cstate;
-	struct drm_encoder *encoder;
-	int i, ret = -EINVAL;
-	bool conn_offset = 0;
-	bool is_cmd = true;
+	int ret = -EINVAL, i;
 
 	if (!crtc || !state) {
 		SDE_ERROR("invalid argument(s)\n");
-	} else {
-		sde_crtc = to_sde_crtc(crtc);
-		cstate = to_sde_crtc_state(state);
-
-		for (i = 0; i < cstate->num_connectors; ++i) {
-			conn_offset = sde_connector_needs_offset(
-						cstate->connectors[i]);
-			if (conn_offset)
-				break;
-		}
-
-		/**
-		 * set the cmd flag only when all the encoders attached
-		 * to the crtc are in cmd mode. Consider all other cases
-		 * as video mode.
-		 */
-		drm_for_each_encoder(encoder, crtc->dev) {
-			if (encoder->crtc == crtc)
-				is_cmd = sde_encoder_check_mode(encoder,
-						MSM_DISPLAY_CAP_CMD_MODE);
-		}
-
-		i = msm_property_index(&sde_crtc->property_info, property);
-		if (i == CRTC_PROP_OUTPUT_FENCE) {
-			uint32_t offset = sde_crtc_get_property(cstate,
-					CRTC_PROP_OUTPUT_FENCE_OFFSET);
-
-			/**
-			 * set the offset to 0 only for cmd mode panels, so
-			 * the release fence for the current frame can be
-			 * triggered right after PP_DONE interrupt.
-			 */
-			offset = is_cmd ? 0 : (offset + conn_offset);
-
-			ret = sde_fence_create(&sde_crtc->output_fence, val,
-								offset);
-			if (ret)
-				SDE_ERROR("fence create failed\n");
-		} else {
-			ret = msm_property_atomic_get(&sde_crtc->property_info,
-					&cstate->property_state,
-					property, val);
-			if (ret)
-				ret = sde_cp_crtc_get_property(crtc,
-					property, val);
-		}
-		if (ret)
-			DRM_ERROR("get property failed\n");
+		goto end;
 	}
+
+	sde_crtc = to_sde_crtc(crtc);
+	cstate = to_sde_crtc_state(state);
+
+	i = msm_property_index(&sde_crtc->property_info, property);
+	if (i == CRTC_PROP_OUTPUT_FENCE) {
+		ret = _sde_crtc_get_output_fence(crtc, state, val);
+	} else {
+		ret = msm_property_atomic_get(&sde_crtc->property_info,
+			&cstate->property_state, property, val);
+		if (ret)
+			ret = sde_cp_crtc_get_property(crtc, property, val);
+	}
+	if (ret)
+		DRM_ERROR("get property failed\n");
+
+end:
 	return ret;
 }
 
@@ -4113,7 +5242,7 @@
 
 	mutex_lock(&sde_crtc->crtc_lock);
 	mode = &crtc->state->adjusted_mode;
-	out_width = sde_crtc_mixer_width(sde_crtc, mode);
+	out_width = sde_crtc_get_mixer_width(sde_crtc, cstate, mode);
 
 	seq_printf(s, "crtc:%d width:%d height:%d\n", crtc->base.id,
 				mode->hdisplay, mode->vdisplay);
@@ -4582,6 +5711,30 @@
 	return rc;
 }
 
+/*
+ * __sde_crtc_idle_notify_work - signal idle timeout to user space
+ */
+static void __sde_crtc_idle_notify_work(struct kthread_work *work)
+{
+	struct sde_crtc *sde_crtc = container_of(work, struct sde_crtc,
+				idle_notify_work.work);
+	struct drm_crtc *crtc;
+	struct drm_event event;
+	int ret = 0;
+
+	if (!sde_crtc) {
+		SDE_ERROR("invalid sde crtc\n");
+	} else {
+		crtc = &sde_crtc->base;
+		event.type = DRM_EVENT_IDLE_NOTIFY;
+		event.length = sizeof(u32);
+		msm_mode_object_event_notify(&crtc->base, crtc->dev,
+				&event, (u8 *)&ret);
+
+		SDE_DEBUG("crtc[%d]: idle timeout notified\n", crtc->base.id);
+	}
+}
+
 /* initialize crtc */
 struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 {
@@ -4609,6 +5762,7 @@
 	INIT_LIST_HEAD(&sde_crtc->rp_head);
 
 	init_completion(&sde_crtc->frame_done_comp);
+	sde_crtc->enabled = false;
 
 	INIT_LIST_HEAD(&sde_crtc->frame_event_list);
 	INIT_LIST_HEAD(&sde_crtc->user_event_list);
@@ -4652,6 +5806,9 @@
 	sde_cp_crtc_init(crtc);
 	sde_cp_crtc_install_properties(crtc);
 
+	kthread_init_delayed_work(&sde_crtc->idle_notify_work,
+					__sde_crtc_idle_notify_work);
+
 	SDE_DEBUG("%s: successfully initialized crtc\n", sde_crtc->name);
 	return crtc;
 }
@@ -4703,8 +5860,16 @@
 	priv = kms->dev->dev_private;
 	ret = 0;
 	if (crtc_drm->enabled) {
-		sde_power_resource_enable(&priv->phandle, kms->core_client,
-				true);
+		ret = sde_power_resource_enable(&priv->phandle,
+				kms->core_client, true);
+		if (ret) {
+			SDE_ERROR("failed to enable power resource %d\n", ret);
+			SDE_EVT32(ret, SDE_EVTLOG_ERROR);
+			kfree(node);
+			return ret;
+		}
+
+		INIT_LIST_HEAD(&node->irq.list);
 		ret = node->func(crtc_drm, true, &node->irq);
 		sde_power_resource_enable(&priv->phandle, kms->core_client,
 				false);
@@ -4712,6 +5877,8 @@
 
 	if (!ret) {
 		spin_lock_irqsave(&crtc->spin_lock, flags);
+		/* irq is regiestered and enabled and set the state */
+		node->state = IRQ_ENABLED;
 		list_add_tail(&node->list, &crtc->user_event_list);
 		spin_unlock_irqrestore(&crtc->spin_lock, flags);
 	} else {
@@ -4735,7 +5902,6 @@
 	spin_lock_irqsave(&crtc->spin_lock, flags);
 	list_for_each_entry(node, &crtc->user_event_list, list) {
 		if (node->event == event) {
-			list_del(&node->list);
 			found = true;
 			break;
 		}
@@ -4751,12 +5917,23 @@
 	 * no need to disable/de-register.
 	 */
 	if (!crtc_drm->enabled) {
+		list_del(&node->list);
 		kfree(node);
 		return 0;
 	}
 	priv = kms->dev->dev_private;
-	sde_power_resource_enable(&priv->phandle, kms->core_client, true);
+	ret = sde_power_resource_enable(&priv->phandle, kms->core_client, true);
+	if (ret) {
+		SDE_ERROR("failed to enable power resource %d\n", ret);
+		SDE_EVT32(ret, SDE_EVTLOG_ERROR);
+		list_del(&node->list);
+		kfree(node);
+		return ret;
+	}
+
 	ret = node->func(crtc_drm, false, &node->irq);
+	list_del(&node->list);
+	kfree(node);
 	sde_power_resource_enable(&priv->phandle, kms->core_client, false);
 	return ret;
 }
@@ -4788,8 +5965,30 @@
 	return 0;
 }
 
+static int sde_crtc_pm_event_handler(struct drm_crtc *crtc, bool en,
+		struct sde_irq_callback *noirq)
+{
+	/*
+	 * IRQ object noirq is not being used here since there is
+	 * no crtc irq from pm event.
+	 */
+	return 0;
+}
+
 static int sde_crtc_idle_interrupt_handler(struct drm_crtc *crtc_drm,
 	bool en, struct sde_irq_callback *irq)
 {
 	return 0;
 }
+
+/**
+ * sde_crtc_update_cont_splash_mixer_settings - update mixer settings
+ *	during device bootup for cont_splash use case
+ * @crtc: Pointer to drm crtc structure
+ */
+void sde_crtc_update_cont_splash_mixer_settings(
+		struct drm_crtc *crtc)
+{
+	_sde_crtc_setup_mixers(crtc);
+	crtc->enabled = true;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index ea606b3..c6b4afa 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -26,6 +26,7 @@
 #include "sde_kms.h"
 #include "sde_core_perf.h"
 #include "sde_hw_blk.h"
+#include "sde_hw_ds.h"
 
 #define SDE_CRTC_NAME_SIZE	12
 
@@ -106,17 +107,21 @@
  * @hw_lm:	LM HW Driver context
  * @hw_ctl:	CTL Path HW driver context
  * @hw_dspp:	DSPP HW driver context
+ * @hw_ds:	DS HW driver context
  * @encoder:	Encoder attached to this lm & ctl
- * @mixer_op_mode: mixer blending operation mode
+ * @mixer_op_mode:	mixer blending operation mode
  * @flush_mask:	mixer flush mask for ctl, mixer and pipe
+ * @pipe_mask:	mixer flush mask for pipe
  */
 struct sde_crtc_mixer {
 	struct sde_hw_mixer *hw_lm;
 	struct sde_hw_ctl *hw_ctl;
-	struct sde_hw_dspp  *hw_dspp;
+	struct sde_hw_dspp *hw_dspp;
+	struct sde_hw_ds *hw_ds;
 	struct drm_encoder *encoder;
 	u32 mixer_op_mode;
 	u32 flush_mask;
+	u32 pipe_mask;
 };
 
 /**
@@ -183,6 +188,9 @@
  * @enabled       : whether the SDE CRTC is currently enabled. updated in the
  *                  commit-thread, not state-swap time which is earlier, so
  *                  safe to make decisions on during VBLANK on/off work
+ * @reset_request : whether or not a h/w request was requested for the previous
+ *                  frame
+ * @ds_reconfig   : force reconfiguration of the destination scaler block
  * @feature_list  : list of color processing features supported on a crtc
  * @active_list   : list of color processing features are active
  * @dirty_list    : list of color processing features are dirty
@@ -204,6 +212,9 @@
  * @misr_enable   : boolean entry indicates misr enable/disable status.
  * @misr_frame_count  : misr frame count provided by client
  * @misr_data     : store misr data before turning off the clocks.
+ * @sbuf_flush_mask: flush mask for inline rotator
+ * @sbuf_flush_mask_old: inline rotator flush mask for previous commit
+ * @idle_notify_work: delayed worker to notify idle timeout to user space
  * @power_event   : registered power event handle
  * @cur_perf      : current performance committed to clock/bandwidth driver
  * @rp_lock       : serialization lock for resource pool
@@ -238,7 +249,9 @@
 	bool vblank_requested;
 	bool suspend;
 	bool enabled;
+	bool reset_request;
 
+	bool ds_reconfig;
 	struct list_head feature_list;
 	struct list_head active_list;
 	struct list_head dirty_list;
@@ -247,6 +260,7 @@
 	struct list_head user_event_list;
 
 	struct mutex crtc_lock;
+	struct mutex crtc_cp_lock;
 
 	atomic_t frame_pending;
 	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
@@ -264,14 +278,22 @@
 	u32 misr_frame_count;
 	u32 misr_data[CRTC_DUAL_MIXERS];
 
+	u32 sbuf_flush_mask;
+	u32 sbuf_flush_mask_old;
+	struct kthread_delayed_work idle_notify_work;
+
 	struct sde_power_event *power_event;
 
 	struct sde_core_perf_params cur_perf;
+	struct sde_core_perf_params new_perf;
 
 	struct mutex rp_lock;
 	struct list_head rp_head;
 
 	struct sde_crtc_smmu_state_data smmu_state;
+
+	/* blob for histogram data */
+	struct drm_property_blob *hist_blob;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
@@ -337,7 +359,6 @@
  * @base: Base drm crtc state structure
  * @connectors    : Currently associated drm connectors
  * @num_connectors: Number of associated drm connectors
- * @intf_mode     : Interface mode of the primary connector
  * @rsc_client    : sde rsc client when mode is valid
  * @is_ppsplit    : Whether current topology requires PPSplit special handling
  * @bw_control    : true if bw/clk controlled by core bw/clk properties
@@ -354,17 +375,20 @@
  * @input_fence_timeout_ns : Cached input fence timeout, in ns
  * @num_dim_layers: Number of dim layers
  * @dim_layer: Dim layer configs
+ * @num_ds: Number of destination scalers to be configured
+ * @num_ds_enabled: Number of destination scalers enabled
+ * @ds_dirty: Boolean to indicate if dirty or not
+ * @ds_cfg: Destination scaler config
+ * @scl3_lut_cfg: QSEED3 lut config
  * @new_perf: new performance state being requested
  * @sbuf_cfg: stream buffer configuration
  * @sbuf_prefill_line: number of line for inline rotator prefetch
- * @sbuf_flush_mask: flush mask for inline rotator
  */
 struct sde_crtc_state {
 	struct drm_crtc_state base;
 
 	struct drm_connector *connectors[MAX_CONNECTORS];
 	int num_connectors;
-	enum sde_intf_mode intf_mode;
 	struct sde_rsc_client *rsc_client;
 	bool rsc_update;
 	bool bw_control;
@@ -381,15 +405,42 @@
 	uint64_t input_fence_timeout_ns;
 	uint32_t num_dim_layers;
 	struct sde_hw_dim_layer dim_layer[SDE_MAX_DIM_LAYERS];
+	uint32_t num_ds;
+	uint32_t num_ds_enabled;
+	bool ds_dirty;
+	struct sde_hw_ds_cfg ds_cfg[SDE_MAX_DS_COUNT];
+	struct sde_hw_scaler3_lut_cfg scl3_lut_cfg;
 
 	struct sde_core_perf_params new_perf;
 	struct sde_ctl_sbuf_cfg sbuf_cfg;
 	u32 sbuf_prefill_line;
-	u32 sbuf_flush_mask;
 
 	struct sde_crtc_respool rp;
 };
 
+enum sde_crtc_irq_state {
+	IRQ_NOINIT,
+	IRQ_ENABLED,
+	IRQ_DISABLED,
+};
+
+/**
+ * sde_crtc_irq_info - crtc interrupt info
+ * @irq: interrupt callback
+ * @event: event type of the interrupt
+ * @func: function pointer to enable/disable the interrupt
+ * @list: list of user customized event in crtc
+ * @ref_count: reference count for the interrupt
+ */
+struct sde_crtc_irq_info {
+	struct sde_irq_callback irq;
+	u32 event;
+	int (*func)(struct drm_crtc *crtc, bool en,
+			struct sde_irq_callback *irq);
+	struct list_head list;
+	enum sde_crtc_irq_state state;
+};
+
 #define to_sde_crtc_state(x) \
 	container_of(x, struct sde_crtc_state, base)
 
@@ -402,14 +453,41 @@
 #define sde_crtc_get_property(S, X) \
 	((S) && ((X) < CRTC_PROP_COUNT) ? ((S)->property_values[(X)].value) : 0)
 
-static inline int sde_crtc_mixer_width(struct sde_crtc *sde_crtc,
-	struct drm_display_mode *mode)
+/**
+ * sde_crtc_get_mixer_width - get the mixer width
+ * Mixer width will be same as panel width(/2 for split)
+ * unless destination scaler feature is enabled
+ */
+static inline int sde_crtc_get_mixer_width(struct sde_crtc *sde_crtc,
+	struct sde_crtc_state *cstate, struct drm_display_mode *mode)
 {
-	if (!sde_crtc || !mode)
+	u32 mixer_width;
+
+	if (!sde_crtc || !cstate || !mode)
 		return 0;
 
-	return  sde_crtc->num_mixers == CRTC_DUAL_MIXERS ?
-		mode->hdisplay / CRTC_DUAL_MIXERS : mode->hdisplay;
+	if (cstate->num_ds_enabled)
+		mixer_width = cstate->ds_cfg[0].lm_width;
+	else
+		mixer_width = (sde_crtc->num_mixers == CRTC_DUAL_MIXERS ?
+			mode->hdisplay / CRTC_DUAL_MIXERS : mode->hdisplay);
+
+	return mixer_width;
+}
+
+/**
+ * sde_crtc_get_mixer_height - get the mixer height
+ * Mixer height will be same as panel height unless
+ * destination scaler feature is enabled
+ */
+static inline int sde_crtc_get_mixer_height(struct sde_crtc *sde_crtc,
+		struct sde_crtc_state *cstate, struct drm_display_mode *mode)
+{
+	if (!sde_crtc || !cstate || !mode)
+		return 0;
+
+	return (cstate->num_ds_enabled ?
+			cstate->ds_cfg[0].lm_height : mode->vdisplay);
 }
 
 /**
@@ -437,8 +515,10 @@
 /**
  * sde_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
  * @crtc: Pointer to drm crtc object
+ * @old_state: Pointer to drm crtc old state object
  */
-void sde_crtc_commit_kickoff(struct drm_crtc *crtc);
+void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
+		struct drm_crtc_state *old_state);
 
 /**
  * sde_crtc_prepare_commit - callback to prepare for output fences
@@ -500,8 +580,8 @@
 	if (!cstate)
 		return NRT_CLIENT;
 
-	return cstate->rsc_client ? RT_RSC_CLIENT :
-	    (cstate->intf_mode == INTF_MODE_WB_LINE ? NRT_CLIENT : RT_CLIENT);
+	return sde_crtc_get_intf_mode(crtc) == INTF_MODE_WB_LINE ? NRT_CLIENT :
+			(cstate->rsc_client ? RT_RSC_CLIENT : RT_CLIENT);
 }
 
 /**
@@ -531,6 +611,30 @@
 }
 
 /**
+ * sde_crtc_is_reset_required - validate the reset request based on the
+ *	pm_suspend and crtc's active status. crtc's are left active
+ *	on pm_suspend during LP1/LP2 states, as the display is still
+ *	left ON. Avoid reset for the subsequent pm_resume in such cases.
+ * @crtc: Pointer to crtc
+ * return: false if in suspend state and crtc active, true otherwise
+ */
+static inline bool sde_crtc_is_reset_required(struct drm_crtc *crtc)
+{
+	/*
+	 * reset is required even when there is no crtc_state as it is required
+	 * to create the initial state object
+	 */
+	if (!crtc || !crtc->state)
+		return true;
+
+	/* reset not required if crtc is active during suspend state */
+	if (sde_kms_is_suspend_state(crtc->dev) && crtc->state->active)
+		return false;
+
+	return true;
+}
+
+/**
  * sde_crtc_event_queue - request event callback
  * @crtc: Pointer to drm crtc structure
  * @func: Pointer to callback function
@@ -617,4 +721,28 @@
  */
 int sde_crtc_secure_ctrl(struct drm_crtc *crtc, bool post_commit);
 
+/**
+ * sde_crtc_helper_reset_properties - reset properties to default values in the
+ *	given DRM CRTC state object
+ * @crtc: Pointer to DRM crtc object
+ * @crtc_state: Pointer to DRM crtc state object
+ * Returns: 0 on success, negative errno on failure
+ */
+int sde_crtc_helper_reset_custom_properties(struct drm_crtc *crtc,
+		struct drm_crtc_state *crtc_state);
+
+/**
+ * sde_crtc_timeline_status - current buffer timeline status
+ * @crtc: Pointer to crtc
+ */
+void sde_crtc_timeline_status(struct drm_crtc *crtc);
+
+/**
+ * sde_crtc_update_cont_splash_mixer_settings - update mixer settings
+ *	during device bootup for cont_splash use case
+ * @crtc: Pointer to drm crtc structure
+ */
+void sde_crtc_update_cont_splash_mixer_settings(
+		struct drm_crtc *crtc);
+
 #endif /* _SDE_CRTC_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 8a46d66..a7dffba 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -73,6 +73,10 @@
 
 #define IDLE_SHORT_TIMEOUT	1
 
+#define FAULT_TOLERENCE_DELTA_IN_MS 2
+
+#define FAULT_TOLERENCE_WAIT_IN_MS 5
+
 /* Maximum number of VSYNC wait attempts for RSC state transition */
 #define MAX_RSC_WAIT	5
 
@@ -88,7 +92,7 @@
  *	This event happens at INTERRUPT level.
  *	Event signals the end of the data transfer after the PP FRAME_DONE
  *	event. At the end of this event, a delayed work is scheduled to go to
- *	IDLE_PC state after IDLE_TIMEOUT time.
+ *	IDLE_PC state after IDLE_POWERCOLLAPSE_DURATION time.
  * @SDE_ENC_RC_EVENT_PRE_STOP:
  *	This event happens at NORMAL priority.
  *	This event, when received during the ON state, set RSC to IDLE, and
@@ -114,9 +118,9 @@
  *	with new vtotal.
  * @SDE_ENC_RC_EVENT_ENTER_IDLE:
  *	This event happens at NORMAL priority from a work item.
- *	Event signals that there were no frame updates for IDLE_TIMEOUT time.
- *	This would disable MDP/DSI core clocks and request RSC with IDLE state
- *	and change the resource state to IDLE.
+ *	Event signals that there were no frame updates for
+ *	IDLE_POWERCOLLAPSE_DURATION time. This would disable MDP/DSI core clocks
+ *      and request RSC with IDLE state and change the resource state to IDLE.
  */
 enum sde_enc_rc_events {
 	SDE_ENC_RC_EVENT_KICKOFF = 1,
@@ -177,12 +181,10 @@
  *				Bit0 = phys_encs[0] etc.
  * @crtc_frame_event_cb:	callback handler for frame event
  * @crtc_frame_event_cb_data:	callback handler private data
- * @frame_done_timeout:		frame done timeout in Hz
- * @frame_done_timer:		watchdog timer for frame done event
+ * @vsync_event_timer:		vsync timer
  * @rsc_client:			rsc client pointer
  * @rsc_state_init:		boolean to indicate rsc config init
  * @disp_info:			local copy of msm_display_info struct
- * @mode_info:			local copy of msm_mode_info struct
  * @misr_enable:		misr enable/disable status
  * @misr_frame_count:		misr frame count before start capturing the data
  * @idle_pc_supported:		indicate if idle power collaps is supported
@@ -191,12 +193,12 @@
  * @rc_state:			resource controller state
  * @delayed_off_work:		delayed worker to schedule disabling of
  *				clks and resources after IDLE_TIMEOUT time.
+ * @vsync_event_work:		worker to handle vsync event for autorefresh
  * @topology:                   topology of the display
- * @mode_set_complete:          flag to indicate modeset completion
+ * @vblank_enabled:		boolean to track userspace vblank vote
  * @rsc_config:			rsc configuration for display vtotal, fps, etc.
  * @cur_conn_roi:		current connector roi
  * @prv_conn_roi:		previous connector roi to optimize if unchanged
- * @idle_timeout:		idle timeout duration in milliseconds
  */
 struct sde_encoder_virt {
 	struct drm_encoder base;
@@ -222,13 +224,11 @@
 	void (*crtc_frame_event_cb)(void *, u32 event);
 	void *crtc_frame_event_cb_data;
 
-	atomic_t frame_done_timeout;
-	struct timer_list frame_done_timer;
+	struct timer_list vsync_event_timer;
 
 	struct sde_rsc_client *rsc_client;
 	bool rsc_state_init;
 	struct msm_display_info disp_info;
-	struct msm_mode_info mode_info;
 	bool misr_enable;
 	u32 misr_frame_count;
 
@@ -236,44 +236,146 @@
 	struct mutex rc_lock;
 	enum sde_enc_rc_states rc_state;
 	struct kthread_delayed_work delayed_off_work;
+	struct kthread_work vsync_event_work;
 	struct msm_display_topology topology;
-	bool mode_set_complete;
+	bool vblank_enabled;
 
 	struct sde_rsc_cmd_config rsc_config;
 	struct sde_rect cur_conn_roi;
 	struct sde_rect prv_conn_roi;
-
-	u32 idle_timeout;
 };
 
 #define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
 
-bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
-
+static void _sde_encoder_pm_qos_add_request(struct drm_encoder *drm_enc)
 {
-	struct sde_encoder_virt *sde_enc;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	struct pm_qos_request *req;
+	u32 cpu_mask;
+	u32 cpu_dma_latency;
+	int cpu;
+
+	if (!drm_enc->dev || !drm_enc->dev->dev_private) {
+		SDE_ERROR("drm device invalid\n");
+		return;
+	}
+
+	priv = drm_enc->dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms\n");
+		return;
+	}
+
+	sde_kms = to_sde_kms(priv->kms);
+	if (!sde_kms || !sde_kms->catalog)
+		return;
+
+	cpu_mask = sde_kms->catalog->perf.cpu_mask;
+	cpu_dma_latency = sde_kms->catalog->perf.cpu_dma_latency;
+	if (!cpu_mask)
+		return;
+
+	req = &sde_kms->pm_qos_cpu_req;
+	req->type = PM_QOS_REQ_AFFINE_CORES;
+	cpumask_empty(&req->cpus_affine);
+	for_each_possible_cpu(cpu) {
+		if ((1 << cpu) & cpu_mask)
+			cpumask_set_cpu(cpu, &req->cpus_affine);
+	}
+	pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, cpu_dma_latency);
+
+	SDE_EVT32_VERBOSE(DRMID(drm_enc), cpu_mask, cpu_dma_latency);
+}
+
+static void _sde_encoder_pm_qos_remove_request(struct drm_encoder *drm_enc)
+{
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+
+	if (!drm_enc->dev || !drm_enc->dev->dev_private) {
+		SDE_ERROR("drm device invalid\n");
+		return;
+	}
+
+	priv = drm_enc->dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms\n");
+		return;
+	}
+
+	sde_kms = to_sde_kms(priv->kms);
+	if (!sde_kms || !sde_kms->catalog || !sde_kms->catalog->perf.cpu_mask)
+		return;
+
+	pm_qos_remove_request(&sde_kms->pm_qos_cpu_req);
+}
+
+static struct drm_connector_state *_sde_encoder_get_conn_state(
+		struct drm_encoder *drm_enc)
+{
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	struct list_head *connector_list;
+	struct drm_connector *conn_iter;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid argument\n");
+		return NULL;
+	}
+
+	priv = drm_enc->dev->dev_private;
+	sde_kms = to_sde_kms(priv->kms);
+	connector_list = &sde_kms->dev->mode_config.connector_list;
+
+	list_for_each_entry(conn_iter, connector_list, head)
+		if (conn_iter->encoder == drm_enc)
+			return conn_iter->state;
+
+	return NULL;
+}
+
+static int _sde_encoder_get_mode_info(struct drm_encoder *drm_enc,
+		struct msm_mode_info *mode_info)
+{
+	struct drm_connector_state *conn_state;
+
+	if (!drm_enc || !mode_info) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	conn_state = _sde_encoder_get_conn_state(drm_enc);
+	if (!conn_state) {
+		SDE_ERROR("invalid connector state for the encoder: %d\n",
+			drm_enc->base.id);
+		return -EINVAL;
+	}
+
+	return sde_connector_get_mode_info(conn_state, mode_info);
+}
+
+static bool _sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc)
+{
 	struct msm_compression_info *comp_info;
+	struct msm_mode_info mode_info;
+	int rc = 0;
 
 	if (!drm_enc)
 		return false;
 
-	sde_enc = to_sde_encoder_virt(drm_enc);
-	comp_info = &sde_enc->mode_info.comp_info;
+	rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+	if (rc) {
+		SDE_ERROR("failed to get mode info, enc: %d\n",
+			drm_enc->base.id);
+		return false;
+	}
+
+	comp_info = &mode_info.comp_info;
 
 	return (comp_info->comp_type == MSM_DISPLAY_COMPRESSION_DSC);
 }
 
-void sde_encoder_set_idle_timeout(struct drm_encoder *drm_enc, u32 idle_timeout)
-{
-	struct sde_encoder_virt *sde_enc;
-
-	if (!drm_enc)
-		return;
-
-	sde_enc = to_sde_encoder_virt(drm_enc);
-	sde_enc->idle_timeout = idle_timeout;
-}
-
 bool sde_encoder_is_dsc_merge(struct drm_encoder *drm_enc)
 {
 	enum sde_rm_topology_name topology;
@@ -438,7 +540,7 @@
 	irq = &phys_enc->irq[intr_idx];
 
 	if (irq->irq_idx >= 0) {
-		SDE_ERROR_PHYS(phys_enc,
+		SDE_DEBUG_PHYS(phys_enc,
 				"skipping already registered irq %s type %d\n",
 				irq->name, irq->intr_type);
 		return 0;
@@ -532,7 +634,8 @@
 		struct drm_connector_state *conn_state)
 {
 	struct sde_encoder_virt *sde_enc = NULL;
-	int i = 0;
+	struct msm_mode_info mode_info;
+	int rc, i = 0;
 
 	if (!hw_res || !drm_enc || !conn_state) {
 		SDE_ERROR("invalid argument(s), drm_enc %d, res %d, state %d\n",
@@ -554,7 +657,19 @@
 			phys->ops.get_hw_resources(phys, hw_res, conn_state);
 	}
 
-	hw_res->topology = sde_enc->mode_info.topology;
+	/**
+	 * NOTE: Do not use sde_encoder_get_mode_info here as this function is
+	 * called from atomic_check phase. Use the below API to get mode
+	 * information of the temporary conn_state passed.
+	 */
+	rc = sde_connector_get_mode_info(conn_state, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+		return;
+	}
+
+	hw_res->topology = mode_info.topology;
+	hw_res->is_primary = sde_enc->disp_info.is_primary;
 }
 
 void sde_encoder_destroy(struct drm_encoder *drm_enc)
@@ -693,6 +808,9 @@
 	struct sde_kms *sde_kms;
 	const struct drm_display_mode *mode;
 	struct drm_display_mode *adj_mode;
+	struct sde_connector *sde_conn = NULL;
+	struct sde_connector_state *sde_conn_state = NULL;
+	struct sde_crtc_state *sde_crtc_state = NULL;
 	int i = 0;
 	int ret = 0;
 
@@ -709,7 +827,11 @@
 	sde_kms = to_sde_kms(priv->kms);
 	mode = &crtc_state->mode;
 	adj_mode = &crtc_state->adjusted_mode;
-	SDE_EVT32(DRMID(drm_enc));
+	sde_conn = to_sde_connector(conn_state->connector);
+	sde_conn_state = to_sde_connector_state(conn_state);
+	sde_crtc_state = to_sde_crtc_state(crtc_state);
+
+	SDE_EVT32(DRMID(drm_enc), drm_atomic_crtc_needs_modeset(crtc_state));
 
 	/*
 	 * display drivers may populate private fields of the drm display mode
@@ -738,18 +860,109 @@
 		}
 	}
 
-	/* Reserve dynamic resources now. Indicating AtomicTest phase */
-	if (!ret) {
-		/*
-		 * Avoid reserving resources when mode set is pending. Topology
-		 * info may not be available to complete reservation.
-		 */
-		if (drm_atomic_crtc_needs_modeset(crtc_state)
-				&& sde_enc->mode_set_complete) {
-			ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state,
-				conn_state, true);
-			sde_enc->mode_set_complete = false;
+	if (!ret && drm_atomic_crtc_needs_modeset(crtc_state)) {
+		struct sde_rect mode_roi, roi;
+
+		mode_roi.x = 0;
+		mode_roi.y = 0;
+		mode_roi.w = crtc_state->adjusted_mode.hdisplay;
+		mode_roi.h = crtc_state->adjusted_mode.vdisplay;
+
+		if (sde_conn_state->rois.num_rects) {
+			sde_kms_rect_merge_rectangles(
+					&sde_conn_state->rois, &roi);
+			if (!sde_kms_rect_is_equal(&mode_roi, &roi)) {
+				SDE_ERROR_ENC(sde_enc,
+					"roi (%d,%d,%d,%d) on connector invalid during modeset\n",
+					roi.x, roi.y, roi.w, roi.h);
+				ret = -EINVAL;
+			}
 		}
+
+		if (sde_crtc_state->user_roi_list.num_rects) {
+			sde_kms_rect_merge_rectangles(
+					&sde_crtc_state->user_roi_list, &roi);
+			if (!sde_kms_rect_is_equal(&mode_roi, &roi)) {
+				SDE_ERROR_ENC(sde_enc,
+					"roi (%d,%d,%d,%d) on crtc invalid during modeset\n",
+					roi.x, roi.y, roi.w, roi.h);
+				ret = -EINVAL;
+			}
+		}
+
+		if (ret)
+			return ret;
+	}
+
+	if (!ret) {
+		/**
+		 * record topology in previous atomic state to be able to handle
+		 * topology transitions correctly.
+		 */
+		enum sde_rm_topology_name old_top;
+
+		old_top  = sde_connector_get_property(conn_state,
+				CONNECTOR_PROP_TOPOLOGY_NAME);
+		ret = sde_connector_set_old_topology_name(conn_state, old_top);
+		if (ret)
+			return ret;
+	}
+
+	if (!ret && sde_conn && drm_atomic_crtc_needs_modeset(crtc_state)) {
+		struct msm_display_topology *topology = NULL;
+
+		ret = sde_conn->ops.get_mode_info(adj_mode,
+				&sde_conn_state->mode_info,
+				sde_kms->catalog->max_mixer_width,
+				sde_conn->display);
+		if (ret) {
+			SDE_ERROR_ENC(sde_enc,
+				"failed to get mode info, rc = %d\n", ret);
+			return ret;
+		}
+
+		/* Reserve dynamic resources, indicating atomic_check phase */
+		ret = sde_rm_reserve(&sde_kms->rm, drm_enc, crtc_state,
+			conn_state, true);
+		if (ret) {
+			SDE_ERROR_ENC(sde_enc,
+				"RM failed to reserve resources, rc = %d\n",
+				ret);
+			return ret;
+		}
+
+		/**
+		 * Update connector state with the topology selected for the
+		 * resource set validated. Reset the topology if we are
+		 * de-activating crtc.
+		 */
+		if (crtc_state->active)
+			topology = &sde_conn_state->mode_info.topology;
+
+		ret = sde_rm_update_topology(conn_state, topology);
+		if (ret) {
+			SDE_ERROR_ENC(sde_enc,
+				"RM failed to update topology, rc: %d\n", ret);
+			return ret;
+		}
+
+		ret = sde_connector_set_blob_data(conn_state->connector,
+				conn_state,
+				CONNECTOR_PROP_SDE_INFO);
+		if (ret) {
+			SDE_ERROR_ENC(sde_enc,
+				"connector failed to update info, rc: %d\n",
+				ret);
+			return ret;
+		}
+
+	}
+
+	ret = sde_connector_roi_v1_check_roi(conn_state);
+	if (ret) {
+		SDE_ERROR_ENC(sde_enc, "connector roi check failed, rc: %d",
+				ret);
+		return ret;
 	}
 
 	if (!ret)
@@ -915,14 +1128,23 @@
 	struct sde_hw_dsc *hw_dsc = sde_enc->hw_dsc[0];
 	struct sde_encoder_phys *enc_master = sde_enc->cur_master;
 	const struct sde_rect *roi = &sde_enc->cur_conn_roi;
-	struct msm_display_dsc_info *dsc =
-		&sde_enc->mode_info.comp_info.dsc_info;
+	struct msm_mode_info mode_info;
+	struct msm_display_dsc_info *dsc = NULL;
+	int rc;
 
-	if (dsc == NULL || hw_dsc == NULL || hw_pp == NULL || !enc_master) {
+	if (hw_dsc == NULL || hw_pp == NULL || !enc_master) {
 		SDE_ERROR_ENC(sde_enc, "invalid params for DSC\n");
 		return -EINVAL;
 	}
 
+	rc = _sde_encoder_get_mode_info(&sde_enc->base, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+		return -EINVAL;
+	}
+
+	dsc = &mode_info.comp_info.dsc_info;
+
 	_sde_encoder_dsc_update_pic_dim(dsc, roi->w, roi->h);
 
 	this_frame_slices = roi->w / dsc->slice_width;
@@ -959,8 +1181,9 @@
 	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
 	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
 	struct msm_display_dsc_info dsc[MAX_CHANNELS_PER_ENC];
+	struct msm_mode_info mode_info;
 	bool half_panel_partial_update;
-	int i;
+	int i, rc;
 
 	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
 		hw_pp[i] = sde_enc->hw_pp[i];
@@ -972,6 +1195,12 @@
 		}
 	}
 
+	rc = _sde_encoder_get_mode_info(&sde_enc->base, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+		return -EINVAL;
+	}
+
 	half_panel_partial_update =
 			hweight_long(params->affected_displays) == 1;
 
@@ -981,8 +1210,8 @@
 	if (enc_master->intf_mode == INTF_MODE_VIDEO)
 		dsc_common_mode |= DSC_MODE_VIDEO;
 
-	memcpy(&dsc[0], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[0]));
-	memcpy(&dsc[1], &sde_enc->mode_info.comp_info.dsc_info, sizeof(dsc[1]));
+	memcpy(&dsc[0], &mode_info.comp_info.dsc_info, sizeof(dsc[0]));
+	memcpy(&dsc[1], &mode_info.comp_info.dsc_info, sizeof(dsc[1]));
 
 	/*
 	 * Since both DSC use same pic dimension, set same pic dimension
@@ -1045,10 +1274,10 @@
 	const struct sde_rect *roi = &sde_enc->cur_conn_roi;
 	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
 	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
-	struct msm_display_dsc_info *dsc =
-		&sde_enc->mode_info.comp_info.dsc_info;
+	struct msm_display_dsc_info *dsc = NULL;
+	struct msm_mode_info mode_info;
 	bool half_panel_partial_update;
-	int i;
+	int i, rc;
 
 	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
 		hw_pp[i] = sde_enc->hw_pp[i];
@@ -1060,6 +1289,14 @@
 		}
 	}
 
+	rc = _sde_encoder_get_mode_info(&sde_enc->base, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+		return -EINVAL;
+	}
+
+	dsc = &mode_info.comp_info.dsc_info;
+
 	half_panel_partial_update =
 			hweight_long(params->affected_displays) == 1;
 
@@ -1148,7 +1385,17 @@
 	}
 
 	SDE_DEBUG_ENC(sde_enc, "topology:%d\n", topology);
-	SDE_EVT32(DRMID(&sde_enc->base));
+	SDE_EVT32(DRMID(&sde_enc->base), topology,
+			sde_enc->cur_conn_roi.x,
+			sde_enc->cur_conn_roi.y,
+			sde_enc->cur_conn_roi.w,
+			sde_enc->cur_conn_roi.h,
+			sde_enc->prv_conn_roi.x,
+			sde_enc->prv_conn_roi.y,
+			sde_enc->prv_conn_roi.w,
+			sde_enc->prv_conn_roi.h,
+			sde_enc->base.crtc->state->adjusted_mode.hdisplay,
+			sde_enc->base.crtc->state->adjusted_mode.vdisplay);
 
 	if (sde_kms_rect_is_equal(&sde_enc->cur_conn_roi,
 			&sde_enc->prv_conn_roi))
@@ -1182,8 +1429,8 @@
 	struct sde_kms *sde_kms;
 	struct sde_hw_mdp *hw_mdptop;
 	struct drm_encoder *drm_enc;
-	struct msm_mode_info *mode_info;
-	int i;
+	struct msm_mode_info mode_info;
+	int i, rc = 0;
 
 	if (!sde_enc || !disp_info) {
 		SDE_ERROR("invalid param sde_enc:%d or disp_info:%d\n",
@@ -1212,9 +1459,9 @@
 		return;
 	}
 
-	mode_info = &sde_enc->mode_info;
-	if (!mode_info) {
-		SDE_ERROR("invalid mode info\n");
+	rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
 		return;
 	}
 
@@ -1224,7 +1471,7 @@
 			vsync_cfg.ppnumber[i] = sde_enc->hw_pp[i]->idx;
 
 		vsync_cfg.pp_count = sde_enc->num_phys_encs;
-		vsync_cfg.frame_rate = mode_info->frame_rate;
+		vsync_cfg.frame_rate = mode_info.frame_rate;
 		if (is_dummy)
 			vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_1;
 		else if (disp_info->is_te_using_watchdog_timer)
@@ -1237,6 +1484,34 @@
 	}
 }
 
+static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
+{
+	int i, ret = 0;
+	struct sde_hw_pingpong *hw_pp = NULL;
+	struct sde_hw_dsc *hw_dsc = NULL;
+
+	if (!sde_enc || !sde_enc->phys_encs[0] ||
+			!sde_enc->phys_encs[0]->connector) {
+		SDE_ERROR("invalid params %d %d\n",
+			!sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1);
+		return -EINVAL;
+	}
+
+	/* Disable DSC for all the pp's present in this topology */
+	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+		hw_pp = sde_enc->hw_pp[i];
+		hw_dsc = sde_enc->hw_dsc[i];
+
+		if (hw_pp && hw_pp->ops.disable_dsc)
+			hw_pp->ops.disable_dsc(hw_pp);
+
+		if (hw_dsc && hw_dsc->ops.dsc_disable)
+			hw_dsc->ops.dsc_disable(hw_dsc);
+	}
+
+	return ret;
+}
+
 static int _sde_encoder_update_rsc_client(
 		struct drm_encoder *drm_enc,
 		struct sde_encoder_rsc_config *config, bool enable)
@@ -1247,11 +1522,12 @@
 	struct sde_rsc_cmd_config *rsc_config;
 	int ret, prefill_lines;
 	struct msm_display_info *disp_info;
-	struct msm_mode_info *mode_info;
+	struct msm_mode_info mode_info;
 	int wait_vblank_crtc_id = SDE_RSC_INVALID_CRTC_ID;
 	int wait_count = 0;
 	struct drm_crtc *primary_crtc;
 	int pipe = -1;
+	int rc = 0;
 
 	if (!drm_enc || !drm_enc->crtc || !drm_enc->dev) {
 		SDE_ERROR("invalid arguments\n");
@@ -1261,7 +1537,6 @@
 	sde_enc = to_sde_encoder_virt(drm_enc);
 	crtc = drm_enc->crtc;
 	disp_info = &sde_enc->disp_info;
-	mode_info = &sde_enc->mode_info;
 	rsc_config = &sde_enc->rsc_config;
 
 	if (!sde_enc->rsc_client) {
@@ -1269,6 +1544,12 @@
 		return 0;
 	}
 
+	rc = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "failed to mode info\n");
+		return 0;
+	}
+
 	/**
 	 * only primary command mode panel can request CMD state.
 	 * all other panels/displays can request for VID state including
@@ -1278,20 +1559,20 @@
 		(((disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) &&
 		  disp_info->is_primary) ? SDE_RSC_CMD_STATE :
 		SDE_RSC_VID_STATE) : SDE_RSC_IDLE_STATE;
-	prefill_lines = config ? mode_info->prefill_lines +
-		config->inline_rotate_prefill : mode_info->prefill_lines;
+	prefill_lines = config ? mode_info.prefill_lines +
+		config->inline_rotate_prefill : mode_info.prefill_lines;
 
 	/* compare specific items and reconfigure the rsc */
-	if ((rsc_config->fps != mode_info->frame_rate) ||
-	    (rsc_config->vtotal != mode_info->vtotal) ||
+	if ((rsc_config->fps != mode_info.frame_rate) ||
+	    (rsc_config->vtotal != mode_info.vtotal) ||
 	    (rsc_config->prefill_lines != prefill_lines) ||
-	    (rsc_config->jitter_numer != mode_info->jitter_numer) ||
-	    (rsc_config->jitter_denom != mode_info->jitter_denom)) {
-		rsc_config->fps = mode_info->frame_rate;
-		rsc_config->vtotal = mode_info->vtotal;
+	    (rsc_config->jitter_numer != mode_info.jitter_numer) ||
+	    (rsc_config->jitter_denom != mode_info.jitter_denom)) {
+		rsc_config->fps = mode_info.frame_rate;
+		rsc_config->vtotal = mode_info.vtotal;
 		rsc_config->prefill_lines = prefill_lines;
-		rsc_config->jitter_numer = mode_info->jitter_numer;
-		rsc_config->jitter_denom = mode_info->jitter_denom;
+		rsc_config->jitter_numer = mode_info.jitter_numer;
+		rsc_config->jitter_denom = mode_info.jitter_denom;
 		sde_enc->rsc_state_init = false;
 	}
 
@@ -1391,6 +1672,44 @@
 
 }
 
+/* keep track of the userspace vblank during modeset */
+static void _sde_encoder_modeset_helper_locked(struct drm_encoder *drm_enc,
+		u32 sw_event)
+{
+	struct sde_encoder_virt *sde_enc;
+	bool enable;
+	int i;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	SDE_DEBUG_ENC(sde_enc, "sw_event:%d, vblank_enabled:%d\n",
+			sw_event, sde_enc->vblank_enabled);
+
+	/* nothing to do if vblank not enabled by userspace */
+	if (!sde_enc->vblank_enabled)
+		return;
+
+	/* disable vblank on pre_modeset */
+	if (sw_event == SDE_ENC_RC_EVENT_PRE_MODESET)
+		enable = false;
+	/* enable vblank on post_modeset */
+	else if (sw_event == SDE_ENC_RC_EVENT_POST_MODESET)
+		enable = true;
+	else
+		return;
+
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+		if (phys && phys->ops.control_vblank_irq)
+			phys->ops.control_vblank_irq(phys, enable);
+	}
+}
+
 struct sde_rsc_client *sde_encoder_get_rsc_client(struct drm_encoder *drm_enc)
 {
 	struct sde_encoder_virt *sde_enc;
@@ -1404,65 +1723,73 @@
 static void _sde_encoder_resource_control_rsc_update(
 		struct drm_encoder *drm_enc, bool enable)
 {
-	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
 	struct sde_encoder_rsc_config rsc_cfg = { 0 };
 
 	if (enable) {
 		rsc_cfg.inline_rotate_prefill =
 				sde_crtc_get_inline_prefill(drm_enc->crtc);
 
-		/* connect the TE source to actual TE GPIO to drive RSC */
-		_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info,
-				false);
-
 		_sde_encoder_update_rsc_client(drm_enc, &rsc_cfg, true);
 	} else {
 		_sde_encoder_update_rsc_client(drm_enc, NULL, false);
-
-		/**
-		 * disconnect the TE source from the actual TE GPIO for RSC
-		 *
-		 * this call is for hardware workaround on sdm845 and should
-		 * not be removed without considering the design changes for
-		 * sde rsc + command mode concurrency. It may lead to pp
-		 * timeout due to vsync from panel for command mode panel.
-		 */
-		_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info,
-				true);
 	}
 }
 
-static void _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
+static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
 		bool enable)
 {
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
 	struct sde_encoder_virt *sde_enc;
+	int rc;
+	bool is_cmd_mode, is_primary;
 
 	sde_enc = to_sde_encoder_virt(drm_enc);
 	priv = drm_enc->dev->dev_private;
 	sde_kms = to_sde_kms(priv->kms);
 
+	is_cmd_mode = sde_enc->disp_info.capabilities &
+			MSM_DISPLAY_CAP_CMD_MODE;
+	is_primary = sde_enc->disp_info.is_primary;
+
 	SDE_DEBUG_ENC(sde_enc, "enable:%d\n", enable);
 	SDE_EVT32(DRMID(drm_enc), enable);
 
 	if (!sde_enc->cur_master) {
 		SDE_ERROR("encoder master not set\n");
-		return;
+		return -EINVAL;
 	}
 
 	if (enable) {
 		/* enable SDE core clks */
-		sde_power_resource_enable(&priv->phandle,
+		rc = sde_power_resource_enable(&priv->phandle,
 				sde_kms->core_client, true);
+		if (rc) {
+			SDE_ERROR("failed to enable power resource %d\n", rc);
+			SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+			return rc;
+		}
 
 		/* enable DSI clks */
-		sde_connector_clk_ctrl(sde_enc->cur_master->connector, true);
+		rc = sde_connector_clk_ctrl(sde_enc->cur_master->connector,
+				true);
+		if (rc) {
+			SDE_ERROR("failed to enable clk control %d\n", rc);
+			sde_power_resource_enable(&priv->phandle,
+					sde_kms->core_client, false);
+			return rc;
+		}
 
 		/* enable all the irq */
 		_sde_encoder_irq_control(drm_enc, true);
 
+		if (is_cmd_mode && is_primary)
+			_sde_encoder_pm_qos_add_request(drm_enc);
+
 	} else {
+		if (is_cmd_mode && is_primary)
+			_sde_encoder_pm_qos_remove_request(drm_enc);
+
 		/* disable all the irq */
 		_sde_encoder_irq_control(drm_enc, false);
 
@@ -1474,13 +1801,14 @@
 				sde_kms->core_client, false);
 	}
 
+	return 0;
 }
 
 static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
 		u32 sw_event)
 {
 	bool autorefresh_enabled = false;
-	unsigned int lp, idle_timeout;
+	unsigned int lp, idle_pc_duration;
 	struct sde_encoder_virt *sde_enc;
 	struct msm_drm_private *priv;
 	struct msm_drm_thread *disp_thread;
@@ -1552,7 +1880,19 @@
 			_sde_encoder_irq_control(drm_enc, true);
 		} else {
 			/* enable all the clks and resources */
-			_sde_encoder_resource_control_helper(drm_enc, true);
+			ret = _sde_encoder_resource_control_helper(drm_enc,
+					true);
+			if (ret) {
+				SDE_ERROR_ENC(sde_enc,
+						"sw_event:%d, rc in state %d\n",
+						sw_event, sde_enc->rc_state);
+				SDE_EVT32(DRMID(drm_enc), sw_event,
+						sde_enc->rc_state,
+						SDE_EVTLOG_ERROR);
+				mutex_unlock(&sde_enc->rc_lock);
+				return ret;
+			}
+
 			_sde_encoder_resource_control_rsc_update(drm_enc, true);
 		}
 
@@ -1604,18 +1944,18 @@
 			lp = SDE_MODE_DPMS_ON;
 
 		if (lp == SDE_MODE_DPMS_LP2)
-			idle_timeout = IDLE_SHORT_TIMEOUT;
+			idle_pc_duration = IDLE_SHORT_TIMEOUT;
 		else
-			idle_timeout = sde_enc->idle_timeout;
+			idle_pc_duration = IDLE_POWERCOLLAPSE_DURATION;
 
 		if (!autorefresh_enabled)
 			kthread_queue_delayed_work(
 				&disp_thread->worker,
 				&sde_enc->delayed_off_work,
-				msecs_to_jiffies(idle_timeout));
+				msecs_to_jiffies(idle_pc_duration));
 		SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
 				autorefresh_enabled,
-				idle_timeout, SDE_EVTLOG_FUNC_CASE2);
+				idle_pc_duration, SDE_EVTLOG_FUNC_CASE2);
 		SDE_DEBUG_ENC(sde_enc, "sw_event:%d, work scheduled\n",
 				sw_event);
 		break;
@@ -1660,8 +2000,11 @@
 		break;
 
 	case SDE_ENC_RC_EVENT_STOP:
-		mutex_lock(&sde_enc->rc_lock);
+		/* cancel vsync event work and timer */
+		kthread_cancel_work_sync(&sde_enc->vsync_event_work);
+		del_timer_sync(&sde_enc->vsync_event_timer);
 
+		mutex_lock(&sde_enc->rc_lock);
 		/* return if the resource control is already in OFF state */
 		if (sde_enc->rc_state == SDE_ENC_RC_STATE_OFF) {
 			SDE_DEBUG_ENC(sde_enc, "sw_event:%d, rc in OFF state\n",
@@ -1707,7 +2050,18 @@
 		/* return if the resource control is already in ON state */
 		if (sde_enc->rc_state != SDE_ENC_RC_STATE_ON) {
 			/* enable all the clks and resources */
-			_sde_encoder_resource_control_helper(drm_enc, true);
+			ret = _sde_encoder_resource_control_helper(drm_enc,
+					true);
+			if (ret) {
+				SDE_ERROR_ENC(sde_enc,
+						"sw_event:%d, rc in state %d\n",
+						sw_event, sde_enc->rc_state);
+				SDE_EVT32(DRMID(drm_enc), sw_event,
+						sde_enc->rc_state,
+						SDE_EVTLOG_ERROR);
+				mutex_unlock(&sde_enc->rc_lock);
+				return ret;
+			}
 
 			_sde_encoder_resource_control_rsc_update(drm_enc, true);
 
@@ -1728,6 +2082,7 @@
 		}
 
 		_sde_encoder_irq_control(drm_enc, false);
+		_sde_encoder_modeset_helper_locked(drm_enc, sw_event);
 
 		SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
 			SDE_ENC_RC_STATE_MODESET, SDE_EVTLOG_FUNC_CASE5);
@@ -1750,6 +2105,7 @@
 			return -EINVAL;
 		}
 
+		_sde_encoder_modeset_helper_locked(drm_enc, sw_event);
 		_sde_encoder_irq_control(drm_enc, true);
 
 		_sde_encoder_update_rsc_client(drm_enc, NULL, true);
@@ -1824,6 +2180,7 @@
 	struct sde_kms *sde_kms;
 	struct list_head *connector_list;
 	struct drm_connector *conn = NULL, *conn_iter;
+	struct sde_connector_state *sde_conn_state = NULL;
 	struct sde_connector *sde_conn = NULL;
 	struct sde_rm_hw_iter dsc_iter, pp_iter;
 	int i = 0, ret;
@@ -1833,6 +2190,11 @@
 		return;
 	}
 
+	if (!sde_kms_power_resource_is_enabled(drm_enc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	sde_enc = to_sde_encoder_virt(drm_enc);
 	SDE_DEBUG_ENC(sde_enc, "\n");
 
@@ -1855,12 +2217,15 @@
 	}
 
 	sde_conn = to_sde_connector(conn);
-	if (sde_conn) {
-		ret = sde_conn->ops.get_mode_info(adj_mode, &sde_enc->mode_info,
-				sde_kms->catalog->max_mixer_width);
+	sde_conn_state = to_sde_connector_state(conn->state);
+	if (sde_conn && sde_conn_state) {
+		ret = sde_conn->ops.get_mode_info(adj_mode,
+				&sde_conn_state->mode_info,
+				sde_kms->catalog->max_mixer_width,
+				sde_conn->display);
 		if (ret) {
 			SDE_ERROR_ENC(sde_enc,
-				"invalid topology for the mode\n");
+				"failed to get mode info from the display\n");
 			return;
 		}
 	}
@@ -1876,6 +2241,12 @@
 					ret);
 			return;
 		}
+
+		/*
+		 * Disable dsc before switch the mode and after pre_modeset,
+		 * to guarantee that previous kickoff finished.
+		 */
+		_sde_encoder_dsc_disable(sde_enc);
 	}
 
 	/* Reserve dynamic resources now. Indicating non-AtomicTest phase */
@@ -1923,8 +2294,30 @@
 	if (msm_is_mode_seamless_dms(adj_mode))
 		sde_encoder_resource_control(&sde_enc->base,
 						SDE_ENC_RC_EVENT_POST_MODESET);
+}
 
-	sde_enc->mode_set_complete = true;
+void sde_encoder_control_te(struct drm_encoder *drm_enc, bool enable)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct sde_encoder_phys *phys;
+	int i;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid parameters\n");
+		return;
+	}
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	if (!sde_enc) {
+		SDE_ERROR("invalid sde encoder\n");
+		return;
+	}
+
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		phys = sde_enc->phys_encs[i];
+		if (phys && phys->ops.control_te)
+			phys->ops.control_te(phys, enable);
+	}
 }
 
 static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
@@ -1964,6 +2357,7 @@
 				sde_kms->catalog);
 
 	_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info, false);
+	sde_encoder_control_te(drm_enc, true);
 
 	memset(&sde_enc->prv_conn_roi, 0, sizeof(sde_enc->prv_conn_roi));
 	memset(&sde_enc->cur_conn_roi, 0, sizeof(sde_enc->cur_conn_roi));
@@ -1999,13 +2393,27 @@
 	int i, ret = 0;
 	struct msm_compression_info *comp_info = NULL;
 	struct drm_display_mode *cur_mode = NULL;
+	struct msm_mode_info mode_info;
+	struct drm_connector *drm_conn = NULL;
 
 	if (!drm_enc) {
 		SDE_ERROR("invalid encoder\n");
 		return;
 	}
 	sde_enc = to_sde_encoder_virt(drm_enc);
-	comp_info = &sde_enc->mode_info.comp_info;
+
+	if (!sde_kms_power_resource_is_enabled(drm_enc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
+	ret = _sde_encoder_get_mode_info(drm_enc, &mode_info);
+	if (ret) {
+		SDE_ERROR_ENC(sde_enc, "failed to get mode info\n");
+		return;
+	}
+
+	comp_info = &mode_info.comp_info;
 	cur_mode = &sde_enc->base.crtc->state->adjusted_mode;
 
 	SDE_DEBUG_ENC(sde_enc, "\n");
@@ -2067,6 +2475,10 @@
 		sde_enc->cur_master->ops.enable(sde_enc->cur_master);
 
 	_sde_encoder_virt_enable_helper(drm_enc);
+
+	/* Enable ESD thread */
+	drm_conn = sde_enc->cur_master->connector;
+	sde_connector_schedule_status_work(drm_conn, true);
 }
 
 static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
@@ -2074,6 +2486,8 @@
 	struct sde_encoder_virt *sde_enc = NULL;
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
+	struct drm_connector *drm_conn = NULL;
+	enum sde_intf_mode intf_mode;
 	int i = 0;
 
 	if (!drm_enc) {
@@ -2087,39 +2501,72 @@
 		return;
 	}
 
+	if (!sde_kms_power_resource_is_enabled(drm_enc->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	sde_enc = to_sde_encoder_virt(drm_enc);
 	SDE_DEBUG_ENC(sde_enc, "\n");
 
 	priv = drm_enc->dev->dev_private;
 	sde_kms = to_sde_kms(priv->kms);
+	intf_mode = sde_encoder_get_intf_mode(drm_enc);
 
 	SDE_EVT32(DRMID(drm_enc));
 
+	/* Disable ESD thread */
+	drm_conn = sde_enc->cur_master->connector;
+	sde_connector_schedule_status_work(drm_conn, false);
+
 	/* wait for idle */
 	sde_encoder_wait_for_event(drm_enc, MSM_ENC_TX_COMPLETE);
 
-	sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_PRE_STOP);
+	/*
+	 * For primary command mode encoders, execute the resource control
+	 * pre-stop operations before the physical encoders are disabled, to
+	 * allow the rsc to transition its states properly.
+	 *
+	 * For other encoder types, rsc should not be enabled until after
+	 * they have been fully disabled, so delay the pre-stop operations
+	 * until after the physical disable calls have returned.
+	 */
+	if (sde_enc->disp_info.is_primary && intf_mode == INTF_MODE_CMD) {
+		sde_encoder_resource_control(drm_enc,
+				SDE_ENC_RC_EVENT_PRE_STOP);
+		for (i = 0; i < sde_enc->num_phys_encs; i++) {
+			struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
-	for (i = 0; i < sde_enc->num_phys_encs; i++) {
-		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+			if (phys && phys->ops.disable)
+				phys->ops.disable(phys);
+		}
+	} else {
+		for (i = 0; i < sde_enc->num_phys_encs; i++) {
+			struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
-		if (phys && phys->ops.disable)
-			phys->ops.disable(phys);
+			if (phys && phys->ops.disable)
+				phys->ops.disable(phys);
+		}
+		sde_encoder_resource_control(drm_enc,
+				SDE_ENC_RC_EVENT_PRE_STOP);
 	}
 
-	/* after phys waits for frame-done, should be no more frames pending */
-	if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
-		SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
-		del_timer_sync(&sde_enc->frame_done_timer);
-	}
+	/*
+	 * disable dsc after the transfer is complete (for command mode)
+	 * and after physical encoder is disabled, to make sure timing
+	 * engine is already disabled (for video mode).
+	 */
+	_sde_encoder_dsc_disable(sde_enc);
 
 	sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_STOP);
 
-	if (sde_enc->cur_master) {
-		sde_enc->cur_master->connector = NULL;
-		sde_enc->cur_master = NULL;
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		if (sde_enc->phys_encs[i])
+			sde_enc->phys_encs[i]->connector = NULL;
 	}
 
+	sde_enc->cur_master = NULL;
+
 	SDE_DEBUG_ENC(sde_enc, "encoder disabled\n");
 
 	sde_rm_release(&sde_kms->rm, drm_enc);
@@ -2210,6 +2657,7 @@
 		if (phys && phys->ops.control_vblank_irq)
 			phys->ops.control_vblank_irq(phys, enable);
 	}
+	sde_enc->vblank_enabled = enable;
 }
 
 void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
@@ -2265,9 +2713,6 @@
 		}
 
 		if (!sde_enc->frame_busy_mask[0]) {
-			atomic_set(&sde_enc->frame_done_timeout, 0);
-			del_timer(&sde_enc->frame_done_timer);
-
 			sde_encoder_resource_control(drm_enc,
 					SDE_ENC_RC_EVENT_FRAME_DONE);
 
@@ -2295,9 +2740,6 @@
 
 	sde_encoder_resource_control(&sde_enc->base,
 						SDE_ENC_RC_EVENT_ENTER_IDLE);
-
-	sde_encoder_frame_done_callback(&sde_enc->base, NULL,
-				SDE_ENCODER_FRAME_EVENT_IDLE);
 }
 
 /**
@@ -2324,8 +2766,8 @@
 	}
 
 	ctl = phys->hw_ctl;
-	if (!ctl || !ctl->ops.trigger_flush) {
-		SDE_ERROR("missing trigger cb\n");
+	if (!ctl || !phys->ops.trigger_flush) {
+		SDE_ERROR("missing ctl/trigger cb\n");
 		return;
 	}
 
@@ -2345,14 +2787,15 @@
 	if (extra_flush_bits && ctl->ops.update_pending_flush)
 		ctl->ops.update_pending_flush(ctl, extra_flush_bits);
 
-	ctl->ops.trigger_flush(ctl);
+	phys->ops.trigger_flush(phys);
 
 	if (ctl->ops.get_pending_flush)
-		SDE_EVT32(DRMID(drm_enc), phys->intf_idx, pending_kickoff_cnt,
-			ctl->idx, ctl->ops.get_pending_flush(ctl));
+		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
+			pending_kickoff_cnt, ctl->idx - CTL_0,
+			ctl->ops.get_pending_flush(ctl));
 	else
-		SDE_EVT32(DRMID(drm_enc), phys->intf_idx, ctl->idx,
-						pending_kickoff_cnt);
+		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
+			ctl->idx - CTL_0, pending_kickoff_cnt);
 }
 
 /**
@@ -2385,6 +2828,20 @@
 		phys->ops.trigger_start(phys);
 }
 
+void sde_encoder_helper_trigger_flush(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_hw_ctl *ctl;
+
+	if (!phys_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+
+	ctl = phys_enc->hw_ctl;
+	if (ctl && ctl->ops.trigger_flush)
+		ctl->ops.trigger_flush(ctl);
+}
+
 void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_hw_ctl *ctl;
@@ -2397,30 +2854,50 @@
 	ctl = phys_enc->hw_ctl;
 	if (ctl && ctl->ops.trigger_start) {
 		ctl->ops.trigger_start(ctl);
-		SDE_EVT32(DRMID(phys_enc->parent), ctl->idx);
+		SDE_EVT32(DRMID(phys_enc->parent), ctl->idx - CTL_0);
 	}
 }
 
-int sde_encoder_helper_wait_event_timeout(
-		int32_t drm_id,
-		int32_t hw_id,
-		struct sde_encoder_wait_info *info)
+static int _sde_encoder_wait_timeout(int32_t drm_id, int32_t hw_id,
+	s64 timeout_ms, struct sde_encoder_wait_info *info)
 {
 	int rc = 0;
-	s64 expected_time = ktime_to_ms(ktime_get()) + info->timeout_ms;
-	s64 jiffies = msecs_to_jiffies(info->timeout_ms);
-	s64 time;
+	s64 wait_time_jiffies = msecs_to_jiffies(timeout_ms);
+	ktime_t cur_ktime;
+	ktime_t exp_ktime = ktime_add_ms(ktime_get(), timeout_ms);
 
 	do {
 		rc = wait_event_timeout(*(info->wq),
-				atomic_read(info->atomic_cnt) == 0, jiffies);
-		time = ktime_to_ms(ktime_get());
+			atomic_read(info->atomic_cnt) == 0, wait_time_jiffies);
+		cur_ktime = ktime_get();
 
-		SDE_EVT32_VERBOSE(drm_id, hw_id, rc, time, expected_time,
-				atomic_read(info->atomic_cnt));
+		SDE_EVT32(drm_id, hw_id, rc, ktime_to_ms(cur_ktime),
+			timeout_ms, atomic_read(info->atomic_cnt));
 	/* If we timed out, counter is valid and time is less, wait again */
 	} while (atomic_read(info->atomic_cnt) && (rc == 0) &&
-			(time < expected_time));
+			(ktime_compare_safe(exp_ktime, cur_ktime) > 0));
+
+	return rc;
+}
+
+int sde_encoder_helper_wait_event_timeout(int32_t drm_id, int32_t hw_id,
+	struct sde_encoder_wait_info *info)
+{
+	int rc;
+	ktime_t exp_ktime = ktime_add_ms(ktime_get(), info->timeout_ms);
+
+	rc = _sde_encoder_wait_timeout(drm_id, hw_id, info->timeout_ms, info);
+
+	/**
+	 * handle disabled irq case where timer irq is also delayed.
+	 * wait for additional timeout of FAULT_TOLERENCE_WAIT_IN_MS
+	 * if it event_timeout expired late detected.
+	 */
+	if (atomic_read(info->atomic_cnt) && (!rc) &&
+	    (ktime_compare_safe(ktime_get(), ktime_add_ms(exp_ktime,
+	     FAULT_TOLERENCE_DELTA_IN_MS)) > 0))
+		rc = _sde_encoder_wait_timeout(drm_id, hw_id,
+			FAULT_TOLERENCE_WAIT_IN_MS, info);
 
 	return rc;
 }
@@ -2462,12 +2939,6 @@
 		}
 	}
 
-	rc = ctl->ops.reset(ctl);
-	if (rc) {
-		SDE_ERROR_ENC(sde_enc, "ctl %d reset failure\n",  ctl->idx);
-		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
-	}
-
 	phys_enc->enable_state = SDE_ENC_ENABLED;
 }
 
@@ -2493,6 +2964,29 @@
 
 	pending_flush = 0x0;
 
+	/*
+	 * Trigger LUT DMA flush, this might need a wait, so we need
+	 * to do this outside of the atomic context
+	 */
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+		bool wait_for_dma = false;
+
+		if (!phys || phys->enable_state == SDE_ENC_DISABLED)
+			continue;
+
+		ctl = phys->hw_ctl;
+		if (!ctl)
+			continue;
+
+		if (phys->ops.wait_dma_trigger)
+			wait_for_dma = phys->ops.wait_dma_trigger(phys);
+
+		if (phys->hw_ctl->ops.reg_dma_flush)
+			phys->hw_ctl->ops.reg_dma_flush(phys->hw_ctl,
+					wait_for_dma);
+	}
+
 	/* update pending counts and trigger kickoff ctl flush atomically */
 	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
 
@@ -2520,8 +3014,7 @@
 				phys->split_role == ENC_ROLE_SLAVE) &&
 				phys->split_role != ENC_ROLE_SKIP)
 			set_bit(i, sde_enc->frame_busy_mask);
-		if (phys->hw_ctl->ops.reg_dma_flush)
-			phys->hw_ctl->ops.reg_dma_flush(phys->hw_ctl);
+
 		if (!phys->ops.needs_single_flush ||
 				!phys->ops.needs_single_flush(phys))
 			_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
@@ -2622,6 +3115,8 @@
 
 	SDE_DEBUG_ENC(sde_enc, "affected_displays 0x%lx num_active_phys %d\n",
 			params->affected_displays, num_active_phys);
+	SDE_EVT32_VERBOSE(DRMID(drm_enc), params->affected_displays,
+			num_active_phys);
 
 	/* for left/right only update, ppsplit master switches interface */
 	_sde_encoder_ppsplit_swap_intf_for_right_only_update(drm_enc,
@@ -2731,31 +3226,209 @@
 		phys->hw_pp->ops.setup_dither(phys->hw_pp, dither_cfg, len);
 }
 
-void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
+static u32 _sde_encoder_calculate_linetime(struct sde_encoder_virt *sde_enc,
+		struct drm_display_mode *mode)
+{
+	u64 pclk_rate;
+	u32 pclk_period;
+	u32 line_time;
+
+	/*
+	 * For linetime calculation, only operate on master encoder.
+	 */
+	if (!sde_enc->cur_master)
+		return 0;
+
+	if (!sde_enc->cur_master->ops.get_line_count) {
+		SDE_ERROR("get_line_count function not defined\n");
+		return 0;
+	}
+
+	pclk_rate = mode->clock; /* pixel clock in kHz */
+	if (pclk_rate == 0) {
+		SDE_ERROR("pclk is 0, cannot calculate line time\n");
+		return 0;
+	}
+
+	pclk_period = DIV_ROUND_UP_ULL(1000000000ull, pclk_rate);
+	if (pclk_period == 0) {
+		SDE_ERROR("pclk period is 0\n");
+		return 0;
+	}
+
+	/*
+	 * Line time calculation based on Pixel clock and HTOTAL.
+	 * Final unit is in ns.
+	 */
+	line_time = (pclk_period * mode->htotal) / 1000;
+	if (line_time == 0) {
+		SDE_ERROR("line time calculation is 0\n");
+		return 0;
+	}
+
+	SDE_DEBUG_ENC(sde_enc,
+			"clk_rate=%lldkHz, clk_period=%d, linetime=%dns\n",
+			pclk_rate, pclk_period, line_time);
+
+	return line_time;
+}
+
+static int _sde_encoder_wakeup_time(struct drm_encoder *drm_enc,
+		ktime_t *wakeup_time)
+{
+	struct drm_display_mode *mode;
+	struct sde_encoder_virt *sde_enc;
+	u32 cur_line;
+	u32 line_time;
+	u32 vtotal, time_to_vsync;
+	ktime_t cur_time;
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+
+	if (!drm_enc->crtc || !drm_enc->crtc->state) {
+		SDE_ERROR("crtc/crtc state object is NULL\n");
+		return -EINVAL;
+	}
+	mode = &drm_enc->crtc->state->adjusted_mode;
+
+	line_time = _sde_encoder_calculate_linetime(sde_enc, mode);
+	if (!line_time)
+		return -EINVAL;
+
+	cur_line = sde_enc->cur_master->ops.get_line_count(sde_enc->cur_master);
+
+	vtotal = mode->vtotal;
+	if (cur_line >= vtotal)
+		time_to_vsync = line_time * vtotal;
+	else
+		time_to_vsync = line_time * (vtotal - cur_line);
+
+	if (time_to_vsync == 0) {
+		SDE_ERROR("time to vsync should not be zero, vtotal=%d\n",
+				vtotal);
+		return -EINVAL;
+	}
+
+	cur_time = ktime_get();
+	*wakeup_time = ktime_add_ns(cur_time, time_to_vsync);
+
+	SDE_DEBUG_ENC(sde_enc,
+			"cur_line=%u vtotal=%u time_to_vsync=%u, cur_time=%lld, wakeup_time=%lld\n",
+			cur_line, vtotal, time_to_vsync,
+			ktime_to_ms(cur_time),
+			ktime_to_ms(*wakeup_time));
+	return 0;
+}
+
+static void sde_encoder_vsync_event_handler(unsigned long data)
+{
+	struct drm_encoder *drm_enc = (struct drm_encoder *) data;
+	struct sde_encoder_virt *sde_enc;
+	struct msm_drm_private *priv;
+	struct msm_drm_thread *event_thread;
+
+	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private ||
+			!drm_enc->crtc) {
+		SDE_ERROR("invalid parameters\n");
+		return;
+	}
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+	priv = drm_enc->dev->dev_private;
+
+	if (drm_enc->crtc->index >= ARRAY_SIZE(priv->event_thread)) {
+		SDE_ERROR("invalid crtc index\n");
+		return;
+	}
+	event_thread = &priv->event_thread[drm_enc->crtc->index];
+	if (!event_thread) {
+		SDE_ERROR("event_thread not found for crtc:%d\n",
+				drm_enc->crtc->index);
+		return;
+	}
+
+	kthread_queue_work(&event_thread->worker,
+				&sde_enc->vsync_event_work);
+}
+
+static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
+{
+	struct sde_encoder_virt *sde_enc = container_of(work,
+			struct sde_encoder_virt, vsync_event_work);
+	bool autorefresh_enabled = false;
+	int rc = 0;
+	ktime_t wakeup_time;
+
+	if (!sde_enc) {
+		SDE_ERROR("invalid sde encoder\n");
+		return;
+	}
+
+	rc = _sde_encoder_power_enable(sde_enc, true);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "sde enc power enabled failed:%d\n", rc);
+		return;
+	}
+
+	if (sde_enc->cur_master &&
+		sde_enc->cur_master->ops.is_autorefresh_enabled)
+		autorefresh_enabled =
+			sde_enc->cur_master->ops.is_autorefresh_enabled(
+						sde_enc->cur_master);
+
+	/* Update timer if autorefresh is enabled else return */
+	if (!autorefresh_enabled)
+		goto exit;
+
+	rc = _sde_encoder_wakeup_time(&sde_enc->base, &wakeup_time);
+	if (rc)
+		goto exit;
+
+	SDE_EVT32_VERBOSE(ktime_to_ms(wakeup_time));
+	mod_timer(&sde_enc->vsync_event_timer,
+			nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
+
+exit:
+	_sde_encoder_power_enable(sde_enc, false);
+}
+
+int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
 		struct sde_encoder_kickoff_params *params)
 {
 	struct sde_encoder_virt *sde_enc;
 	struct sde_encoder_phys *phys;
 	bool needs_hw_reset = false;
+	uint32_t ln_cnt1, ln_cnt2;
 	unsigned int i;
-	int rc;
+	int rc, ret = 0;
 
 	if (!drm_enc || !params) {
 		SDE_ERROR("invalid args\n");
-		return;
+		return -EINVAL;
 	}
 	sde_enc = to_sde_encoder_virt(drm_enc);
 
 	SDE_DEBUG_ENC(sde_enc, "\n");
 	SDE_EVT32(DRMID(drm_enc));
 
+	/* save this for later, in case of errors */
+	if (sde_enc->cur_master && sde_enc->cur_master->ops.get_wr_line_count)
+		ln_cnt1 = sde_enc->cur_master->ops.get_wr_line_count(
+				sde_enc->cur_master);
+	else
+		ln_cnt1 = -EINVAL;
+
 	/* prepare for next kickoff, may include waiting on previous kickoff */
 	SDE_ATRACE_BEGIN("enc_prepare_for_kickoff");
 	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		phys = sde_enc->phys_encs[i];
 		if (phys) {
-			if (phys->ops.prepare_for_kickoff)
-				phys->ops.prepare_for_kickoff(phys, params);
+			if (phys->ops.prepare_for_kickoff) {
+				rc = phys->ops.prepare_for_kickoff(
+						phys, params);
+				if (rc)
+					ret = rc;
+			}
 			if (phys->enable_state == SDE_ENC_ERR_NEEDS_HW_RESET)
 				needs_hw_reset = true;
 			_sde_encoder_setup_dither(phys);
@@ -2763,11 +3436,24 @@
 	}
 	SDE_ATRACE_END("enc_prepare_for_kickoff");
 
-	sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_KICKOFF);
+	rc = sde_encoder_resource_control(drm_enc, SDE_ENC_RC_EVENT_KICKOFF);
+	if (rc) {
+		SDE_ERROR_ENC(sde_enc, "resource kickoff failed rc %d\n", rc);
+		return rc;
+	}
 
 	/* if any phys needs reset, reset all phys, in-order */
 	if (needs_hw_reset) {
-		SDE_EVT32(DRMID(drm_enc), SDE_EVTLOG_FUNC_CASE1);
+		/* query line count before cur_master is updated */
+		if (sde_enc->cur_master &&
+				sde_enc->cur_master->ops.get_wr_line_count)
+			ln_cnt2 = sde_enc->cur_master->ops.get_wr_line_count(
+					sde_enc->cur_master);
+		else
+			ln_cnt2 = -EINVAL;
+
+		SDE_EVT32(DRMID(drm_enc), ln_cnt1, ln_cnt2,
+				SDE_EVTLOG_FUNC_CASE1);
 		for (i = 0; i < sde_enc->num_phys_encs; i++) {
 			phys = sde_enc->phys_encs[i];
 			if (phys && phys->ops.hw_reset)
@@ -2781,24 +3467,70 @@
 
 	if (sde_enc->cur_master && sde_enc->cur_master->connector) {
 		rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
-		if (rc)
+		if (rc) {
 			SDE_ERROR_ENC(sde_enc, "kickoff conn%d failed rc %d\n",
 					sde_enc->cur_master->connector->base.id,
 					rc);
+			ret = rc;
+		}
 	}
 
-	if (sde_encoder_is_dsc_enabled(drm_enc)) {
+	if (_sde_encoder_is_dsc_enabled(drm_enc)) {
 		rc = _sde_encoder_dsc_setup(sde_enc, params);
-		if (rc)
+		if (rc) {
 			SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc);
+			ret = rc;
+		}
 	}
+
+	return ret;
 }
 
-void sde_encoder_kickoff(struct drm_encoder *drm_enc)
+/**
+ * _sde_encoder_reset_ctl_hw - reset h/w configuration for all ctl's associated
+ *	with the specified encoder, and unstage all pipes from it
+ * @encoder:	encoder pointer
+ * Returns: 0 on success
+ */
+static int _sde_encoder_reset_ctl_hw(struct drm_encoder *drm_enc)
 {
 	struct sde_encoder_virt *sde_enc;
 	struct sde_encoder_phys *phys;
 	unsigned int i;
+	int rc = 0;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return -EINVAL;
+	}
+
+	sde_enc = to_sde_encoder_virt(drm_enc);
+
+	SDE_ATRACE_BEGIN("encoder_release_lm");
+	SDE_DEBUG_ENC(sde_enc, "\n");
+
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		phys = sde_enc->phys_encs[i];
+		if (!phys)
+			continue;
+
+		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0);
+
+		rc = sde_encoder_helper_reset_mixers(phys, NULL);
+		if (rc)
+			SDE_EVT32(DRMID(drm_enc), rc, SDE_EVTLOG_ERROR);
+	}
+
+	SDE_ATRACE_END("encoder_release_lm");
+	return rc;
+}
+
+void sde_encoder_kickoff(struct drm_encoder *drm_enc, bool is_error)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct sde_encoder_phys *phys;
+	ktime_t wakeup_time;
+	unsigned int i;
 
 	if (!drm_enc) {
 		SDE_ERROR("invalid encoder\n");
@@ -2809,11 +3541,9 @@
 
 	SDE_DEBUG_ENC(sde_enc, "\n");
 
-	atomic_set(&sde_enc->frame_done_timeout,
-			SDE_FRAME_DONE_TIMEOUT * 1000 /
-			drm_enc->crtc->state->adjusted_mode.vrefresh);
-	mod_timer(&sde_enc->frame_done_timer, jiffies +
-		((atomic_read(&sde_enc->frame_done_timeout) * HZ) / 1000));
+	/* create a 'no pipes' commit to release buffers on errors */
+	if (is_error)
+		_sde_encoder_reset_ctl_hw(drm_enc);
 
 	/* All phys encs are ready to go, trigger the kickoff */
 	_sde_encoder_kickoff_phys(sde_enc);
@@ -2824,10 +3554,18 @@
 		if (phys && phys->ops.handle_post_kickoff)
 			phys->ops.handle_post_kickoff(phys);
 	}
+
+	if (sde_enc->disp_info.intf_type == DRM_MODE_CONNECTOR_DSI &&
+			!_sde_encoder_wakeup_time(drm_enc, &wakeup_time)) {
+		SDE_EVT32_VERBOSE(ktime_to_ms(wakeup_time));
+		mod_timer(&sde_enc->vsync_event_timer,
+				nsecs_to_jiffies(ktime_to_ns(wakeup_time)));
+	}
+
 	SDE_ATRACE_END("encoder_kickoff");
 }
 
-int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
+int sde_encoder_helper_reset_mixers(struct sde_encoder_phys *phys_enc,
 		struct drm_framebuffer *fb)
 {
 	struct drm_encoder *drm_enc;
@@ -2844,8 +3582,6 @@
 	memset(&mixer, 0, sizeof(mixer));
 
 	/* reset associated CTL/LMs */
-	if (phys_enc->hw_ctl->ops.clear_pending_flush)
-		phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
 	if (phys_enc->hw_ctl->ops.clear_all_blendstages)
 		phys_enc->hw_ctl->ops.clear_all_blendstages(phys_enc->hw_ctl);
 
@@ -2885,7 +3621,7 @@
 	}
 
 	if (!lm_valid) {
-		SDE_DEBUG_ENC(to_sde_encoder_virt(drm_enc), "lm not found\n");
+		SDE_ERROR_ENC(to_sde_encoder_virt(drm_enc), "lm not found\n");
 		return -EFAULT;
 	}
 	return 0;
@@ -3365,36 +4101,6 @@
 	return ret;
 }
 
-static void sde_encoder_frame_done_timeout(unsigned long data)
-{
-	struct drm_encoder *drm_enc = (struct drm_encoder *) data;
-	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
-	struct msm_drm_private *priv;
-	u32 event;
-
-	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
-		SDE_ERROR("invalid parameters\n");
-		return;
-	}
-	priv = drm_enc->dev->dev_private;
-
-	if (!sde_enc->frame_busy_mask[0] || !sde_enc->crtc_frame_event_cb) {
-		SDE_DEBUG_ENC(sde_enc, "invalid timeout\n");
-		SDE_EVT32(DRMID(drm_enc), sde_enc->frame_busy_mask[0], 0);
-		return;
-	} else if (!atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
-		SDE_ERROR_ENC(sde_enc, "invalid timeout\n");
-		SDE_EVT32(DRMID(drm_enc), 0, 1);
-		return;
-	}
-
-	SDE_ERROR_ENC(sde_enc, "frame done timeout\n");
-
-	event = SDE_ENCODER_FRAME_EVENT_ERROR;
-	SDE_EVT32(DRMID(drm_enc), event);
-	sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data, event);
-}
-
 static const struct drm_encoder_helper_funcs sde_encoder_helper_funcs = {
 	.mode_set = sde_encoder_virt_mode_set,
 	.disable = sde_encoder_virt_disable,
@@ -3438,9 +4144,11 @@
 	drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
 	drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
 
-	atomic_set(&sde_enc->frame_done_timeout, 0);
-	setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
-			(unsigned long) sde_enc);
+	if ((disp_info->intf_type == DRM_MODE_CONNECTOR_DSI) &&
+			disp_info->is_primary)
+		setup_timer(&sde_enc->vsync_event_timer,
+				sde_encoder_vsync_event_handler,
+				(unsigned long)sde_enc);
 
 	snprintf(name, SDE_NAME_SIZE, "rsc_enc%u", drm_enc->base.id);
 	sde_enc->rsc_client = sde_rsc_client_create(SDE_RSC_INDEX, name,
@@ -3454,7 +4162,11 @@
 	mutex_init(&sde_enc->rc_lock);
 	kthread_init_delayed_work(&sde_enc->delayed_off_work,
 			sde_encoder_off_work);
-	sde_enc->idle_timeout = IDLE_TIMEOUT;
+	sde_enc->vblank_enabled = false;
+
+	kthread_init_work(&sde_enc->vsync_event_work,
+			sde_encoder_vsync_event_work_handler);
+
 	memcpy(&sde_enc->disp_info, disp_info, sizeof(*disp_info));
 
 	SDE_DEBUG_ENC(sde_enc, "created\n");
@@ -3496,6 +4208,9 @@
 		case MSM_ENC_VBLANK:
 			fn_wait = phys->ops.wait_for_vblank;
 			break;
+		case MSM_ENC_ACTIVE_REGION:
+			fn_wait = phys->ops.wait_for_active;
+			break;
 		default:
 			SDE_ERROR_ENC(sde_enc, "unknown wait event %d\n",
 					event);
@@ -3537,3 +4252,176 @@
 
 	return INTF_MODE_NONE;
 }
+
+/**
+ * sde_encoder_update_caps_for_cont_splash - update encoder settings during
+ *	device bootup when cont_splash is enabled
+ * @drm_enc:    Pointer to drm encoder structure
+ * @Return:	true if successful in updating the encoder structure
+ */
+int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	struct drm_connector *conn = NULL;
+	struct sde_connector *sde_conn = NULL;
+	struct sde_connector_state *sde_conn_state = NULL;
+	struct drm_display_mode *drm_mode = NULL;
+	struct sde_rm_hw_iter dsc_iter, pp_iter, ctl_iter;
+	int ret = 0, i;
+
+	if (!encoder) {
+		SDE_ERROR("invalid drm enc\n");
+		return -EINVAL;
+	}
+
+	if (!encoder->dev || !encoder->dev->dev_private) {
+		SDE_ERROR("drm device invalid\n");
+		return -EINVAL;
+	}
+
+	priv = encoder->dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+
+	sde_kms = to_sde_kms(priv->kms);
+	sde_enc = to_sde_encoder_virt(encoder);
+	if (!priv->num_connectors) {
+		SDE_ERROR_ENC(sde_enc, "No connectors registered\n");
+		return -EINVAL;
+	}
+	SDE_DEBUG_ENC(sde_enc,
+			"num of connectors: %d\n", priv->num_connectors);
+
+	for (i = 0; i < priv->num_connectors; i++) {
+		SDE_DEBUG_ENC(sde_enc, "connector id: %d\n",
+				priv->connectors[i]->base.id);
+		sde_conn = to_sde_connector(priv->connectors[i]);
+		if (!sde_conn->encoder) {
+			SDE_DEBUG_ENC(sde_enc,
+				"encoder not attached to connector\n");
+			continue;
+		}
+		if (sde_conn->encoder->base.id
+				== encoder->base.id) {
+			conn = (priv->connectors[i]);
+			break;
+		}
+	}
+
+	if (!conn || !conn->state) {
+		SDE_ERROR_ENC(sde_enc, "connector not found\n");
+		return -EINVAL;
+	}
+
+	sde_conn_state = to_sde_connector_state(conn->state);
+
+	if (!sde_conn->ops.get_mode_info) {
+		SDE_ERROR_ENC(sde_enc, "conn: get_mode_info ops not found\n");
+		return -EINVAL;
+	}
+
+	ret = sde_conn->ops.get_mode_info(&encoder->crtc->state->adjusted_mode,
+				&sde_conn_state->mode_info,
+				sde_kms->catalog->max_mixer_width,
+				sde_conn->display);
+	if (ret) {
+		SDE_ERROR_ENC(sde_enc,
+			"conn: ->get_mode_info failed. ret=%d\n", ret);
+		return ret;
+	}
+
+	ret = sde_rm_reserve(&sde_kms->rm, encoder, encoder->crtc->state,
+			conn->state, false);
+	if (ret) {
+		SDE_ERROR_ENC(sde_enc,
+			"failed to reserve hw resources, %d\n", ret);
+		return ret;
+	}
+
+	if (conn->encoder) {
+		conn->state->best_encoder = conn->encoder;
+		SDE_DEBUG_ENC(sde_enc,
+			"configured cstate->best_encoder to ID = %d\n",
+			conn->state->best_encoder->base.id);
+	} else {
+		SDE_ERROR_ENC(sde_enc, "No encoder mapped to connector=%d\n",
+				conn->base.id);
+	}
+
+	SDE_DEBUG_ENC(sde_enc, "connector topology = %llu\n",
+			sde_connector_get_topology_name(conn));
+	drm_mode = &encoder->crtc->state->adjusted_mode;
+	SDE_DEBUG_ENC(sde_enc, "hdisplay = %d, vdisplay = %d\n",
+			drm_mode->hdisplay, drm_mode->vdisplay);
+	drm_set_preferred_mode(conn, drm_mode->hdisplay, drm_mode->vdisplay);
+
+	if (encoder->bridge) {
+		SDE_DEBUG_ENC(sde_enc, "Bridge mapped to encoder\n");
+		/*
+		 * For cont-splash use case, we update the mode
+		 * configurations manually. This will skip the
+		 * usually mode set call when actual frame is
+		 * pushed from framework. The bridge needs to
+		 * be updated with the current drm mode by
+		 * calling the bridge mode set ops.
+		 */
+		if (encoder->bridge->funcs) {
+			SDE_DEBUG_ENC(sde_enc, "calling mode_set\n");
+			encoder->bridge->funcs->mode_set(encoder->bridge,
+						drm_mode, drm_mode);
+		}
+	} else {
+		SDE_ERROR_ENC(sde_enc, "No bridge attached to encoder\n");
+	}
+
+	sde_rm_init_hw_iter(&pp_iter, encoder->base.id, SDE_HW_BLK_PINGPONG);
+	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+		sde_enc->hw_pp[i] = NULL;
+		if (!sde_rm_get_hw(&sde_kms->rm, &pp_iter))
+			break;
+		sde_enc->hw_pp[i] = (struct sde_hw_pingpong *) pp_iter.hw;
+	}
+
+	sde_rm_init_hw_iter(&dsc_iter, encoder->base.id, SDE_HW_BLK_DSC);
+	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
+		sde_enc->hw_dsc[i] = NULL;
+		if (!sde_rm_get_hw(&sde_kms->rm, &dsc_iter))
+			break;
+		sde_enc->hw_dsc[i] = (struct sde_hw_dsc *) dsc_iter.hw;
+	}
+
+	sde_rm_init_hw_iter(&ctl_iter, encoder->base.id, SDE_HW_BLK_CTL);
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+		phys->hw_ctl = NULL;
+		if (!sde_rm_get_hw(&sde_kms->rm, &ctl_iter))
+			break;
+		phys->hw_ctl = (struct sde_hw_ctl *) ctl_iter.hw;
+	}
+
+	for (i = 0; i < sde_enc->num_phys_encs; i++) {
+		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
+
+		if (!phys) {
+			SDE_ERROR_ENC(sde_enc,
+				"phys encoders not initialized\n");
+			return -EINVAL;
+		}
+
+		phys->hw_pp = sde_enc->hw_pp[i];
+		if (phys->ops.cont_splash_mode_set)
+			phys->ops.cont_splash_mode_set(phys, drm_mode);
+
+		if (phys->ops.is_master && phys->ops.is_master(phys)) {
+			phys->connector = conn;
+			sde_enc->cur_master = phys;
+		}
+	}
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index bb7f31d..f8a3cf3 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -29,9 +29,8 @@
 #define SDE_ENCODER_FRAME_EVENT_PANEL_DEAD		BIT(2)
 #define SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE	BIT(3)
 #define SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE	BIT(4)
-#define SDE_ENCODER_FRAME_EVENT_IDLE			BIT(5)
 
-#define IDLE_TIMEOUT	(66 - 16/2)
+#define IDLE_POWERCOLLAPSE_DURATION	(66 - 16/2)
 
 /**
  * Encoder functions and data types
@@ -40,6 +39,7 @@
  * @needs_cdm:	Encoder requests a CDM based on pixel format conversion needs
  * @display_num_of_h_tiles: Number of horizontal tiles in case of split
  *                          interface
+ * @is_primary: set to true if the display is primary display
  * @topology:   Topology of the display
  */
 struct sde_encoder_hw_resources {
@@ -47,6 +47,7 @@
 	enum sde_intf_mode wbs[WB_MAX];
 	bool needs_cdm;
 	u32 display_num_of_h_tiles;
+	bool is_primary;
 	struct msm_display_topology topology;
 };
 
@@ -113,8 +114,9 @@
  *	Delayed: Block until next trigger can be issued.
  * @encoder:	encoder pointer
  * @params:	kickoff time parameters
+ * @Returns:	Zero on success, last detected error otherwise
  */
-void sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder,
+int sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder,
 		struct sde_encoder_kickoff_params *params);
 
 /**
@@ -128,8 +130,10 @@
  * sde_encoder_kickoff - trigger a double buffer flip of the ctl path
  *	(i.e. ctl flush and start) immediately.
  * @encoder:	encoder pointer
+ * @is_error:	whether the current commit needs to be aborted and replaced
+ *		with a 'safe' commit
  */
-void sde_encoder_kickoff(struct drm_encoder *encoder);
+void sde_encoder_kickoff(struct drm_encoder *encoder, bool is_error);
 
 /**
  * sde_encoder_wait_for_event - Waits for encoder events
@@ -158,19 +162,19 @@
 enum sde_intf_mode sde_encoder_get_intf_mode(struct drm_encoder *encoder);
 
 /**
+ * sde_encoder_control_te - control enabling/disabling VSYNC_IN_EN
+ * @encoder:	encoder pointer
+ * @enable:	boolean to indicate enable/disable
+ */
+void sde_encoder_control_te(struct drm_encoder *encoder, bool enable);
+
+/**
  * sde_encoder_virt_restore - restore the encoder configs
  * @encoder:	encoder pointer
  */
 void sde_encoder_virt_restore(struct drm_encoder *encoder);
 
 /**
- * sde_encoder_is_dsc_enabled - check if encoder is in DSC mode
- * @drm_enc: Pointer to drm encoder object
- * @Return: true if encoder is in DSC mode
- */
-bool sde_encoder_is_dsc_enabled(struct drm_encoder *drm_enc);
-
-/**
  * sde_encoder_is_dsc_merge - check if encoder is in DSC merge mode
  * @drm_enc: Pointer to drm encoder object
  * @Return: true if encoder is in DSC merge mode
@@ -209,12 +213,11 @@
 void sde_encoder_prepare_commit(struct drm_encoder *drm_enc);
 
 /**
- * sde_encoder_set_idle_timeout - set the idle timeout for video
- *                    and command mode encoders.
- * @drm_enc:    Pointer to previously created drm encoder structure
- * @idle_timeout:    idle timeout duration in milliseconds
+ * sde_encoder_update_caps_for_cont_splash - update encoder settings during
+ *	device bootup when cont_splash is enabled
+ * @drm_enc:    Pointer to drm encoder structure
+ * @Return:     true if successful in updating the encoder structure
  */
-void sde_encoder_set_idle_timeout(struct drm_encoder *drm_enc,
-							u32 idle_timeout);
+int sde_encoder_update_caps_for_cont_splash(struct drm_encoder *encoder);
 
 #endif /* __SDE_ENCODER_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 5a78e4d..cfe2126 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -99,6 +99,8 @@
  *				encoder. Can be switched at enable time. Based
  *				on split_role and current mode (CMD/VID).
  * @mode_fixup:			DRM Call. Fixup a DRM mode.
+ * @cont_splash_mode_set:	mode set with specific HW resources during
+ *                              cont splash enabled state.
  * @mode_set:			DRM Call. Set a DRM mode.
  *				This likely caches the mode, for use at enable.
  * @enable:			DRM Call. Enable a DRM mode.
@@ -117,6 +119,7 @@
  * @prepare_for_kickoff:	Do any work necessary prior to a kickoff
  *				For CMD encoder, may wait for previous tx done
  * @handle_post_kickoff:	Do any work necessary post-kickoff work
+ * @trigger_flush:		Process flush event on physical encoder
  * @trigger_start:		Process start event on physical encoder
  * @needs_single_flush:		Whether encoder slaves need to be flushed
  * @setup_misr:		Sets up MISR, enable and disables based on sysfs
@@ -125,9 +128,15 @@
  *				SDE_ENC_ERR_NEEDS_HW_RESET state
  * @irq_control:		Handler to enable/disable all the encoder IRQs
  * @update_split_role:		Update the split role of the phys enc
+ * @control_te:			Interface to control the vsync_enable status
  * @restore:			Restore all the encoder configs.
  * @is_autorefresh_enabled:	provides the autorefresh current
  *                              enable/disable state.
+ * @get_line_count:		Obtain current internal vertical line count
+ * @get_wr_line_count:		Obtain current output vertical line count
+ * @wait_dma_trigger:		Returns true if lut dma has to trigger and wait
+ *                              unitl transaction is complete.
+ * @wait_for_active:		Wait for display scan line to be in active area
  */
 
 struct sde_encoder_phys_ops {
@@ -141,6 +150,8 @@
 	void (*mode_set)(struct sde_encoder_phys *encoder,
 			struct drm_display_mode *mode,
 			struct drm_display_mode *adjusted_mode);
+	void (*cont_splash_mode_set)(struct sde_encoder_phys *encoder,
+			struct drm_display_mode *adjusted_mode);
 	void (*enable)(struct sde_encoder_phys *encoder);
 	void (*disable)(struct sde_encoder_phys *encoder);
 	int (*atomic_check)(struct sde_encoder_phys *encoder,
@@ -154,9 +165,10 @@
 	int (*wait_for_commit_done)(struct sde_encoder_phys *phys_enc);
 	int (*wait_for_tx_complete)(struct sde_encoder_phys *phys_enc);
 	int (*wait_for_vblank)(struct sde_encoder_phys *phys_enc);
-	void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
+	int (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc,
 			struct sde_encoder_kickoff_params *params);
 	void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
+	void (*trigger_flush)(struct sde_encoder_phys *phys_enc);
 	void (*trigger_start)(struct sde_encoder_phys *phys_enc);
 	bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc);
 
@@ -167,8 +179,13 @@
 	void (*irq_control)(struct sde_encoder_phys *phys, bool enable);
 	void (*update_split_role)(struct sde_encoder_phys *phys_enc,
 			enum sde_enc_split_role role);
+	void (*control_te)(struct sde_encoder_phys *phys_enc, bool enable);
 	void (*restore)(struct sde_encoder_phys *phys);
 	bool (*is_autorefresh_enabled)(struct sde_encoder_phys *phys);
+	int (*get_line_count)(struct sde_encoder_phys *phys);
+	int (*get_wr_line_count)(struct sde_encoder_phys *phys);
+	bool (*wait_dma_trigger)(struct sde_encoder_phys *phys);
+	int (*wait_for_active)(struct sde_encoder_phys *phys);
 };
 
 /**
@@ -286,6 +303,7 @@
  * @hw_intf:	Hardware interface to the intf registers
  * @timing_params: Current timing parameter
  * @rot_fetch:	Prefill for inline rotation
+ * @error_count: Number of consecutive kickoffs that experienced an error
  * @rot_fetch_valid: true if rot_fetch is updated (reset in enc enable)
  */
 struct sde_encoder_phys_vid {
@@ -293,6 +311,7 @@
 	struct sde_hw_intf *hw_intf;
 	struct intf_timing_params timing_params;
 	struct intf_prog_fetch rot_fetch;
+	int error_count;
 	bool rot_fetch_valid;
 };
 
@@ -458,6 +477,14 @@
 		struct sde_rect *wb_roi);
 
 /**
+ * sde_encoder_helper_trigger_flush - control flush helper function
+ *	This helper function may be optionally specified by physical
+ *	encoders if they require ctl_flush triggering.
+ * @phys_enc: Pointer to physical encoder structure
+ */
+void sde_encoder_helper_trigger_flush(struct sde_encoder_phys *phys_enc);
+
+/**
  * sde_encoder_helper_trigger_start - control start helper function
  *	This helper function may be optionally specified by physical
  *	encoders if they require ctl_start triggering.
@@ -517,12 +544,12 @@
 		enum sde_intf interface);
 
 /**
- * sde_encoder_helper_hw_release - prepare for h/w reset during disable
+ * sde_encoder_helper_reset_mixers - reset mixers associated with phys enc
  * @phys_enc: Pointer to physical encoder structure
  * @fb: Optional fb for specifying new mixer output resolution, may be NULL
  * Return: Zero on success
  */
-int sde_encoder_helper_hw_release(struct sde_encoder_phys *phys_enc,
+int sde_encoder_helper_reset_mixers(struct sde_encoder_phys *phys_enc,
 		struct drm_framebuffer *fb);
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 70a25dd..d7cbfbe 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -32,10 +32,7 @@
 #define to_sde_encoder_phys_cmd(x) \
 	container_of(x, struct sde_encoder_phys_cmd, base)
 
-#define PP_TIMEOUT_MAX_TRIALS	10
-
-/* wait for 2 vyncs only */
-#define CTL_START_TIMEOUT_MS	32
+#define PP_TIMEOUT_MAX_TRIALS	2
 
 /*
  * Tearcheck sync start and continue thresholds are empirically found
@@ -180,6 +177,13 @@
 		return;
 
 	SDE_ATRACE_BEGIN("pp_done_irq");
+
+	/* handle rare cases where the ctl_start_irq is not received */
+	if (sde_encoder_phys_cmd_is_master(phys_enc)
+	    && atomic_add_unless(&phys_enc->pending_retire_fence_cnt, -1, 0))
+		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+			phys_enc, SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE);
+
 	/* notify all synchronous clients first, then asynchronous clients */
 	if (phys_enc->parent_ops.handle_frame_done)
 		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
@@ -355,6 +359,21 @@
 	irq->irq_idx = -EINVAL;
 }
 
+static void sde_encoder_phys_cmd_cont_splash_mode_set(
+		struct sde_encoder_phys *phys_enc,
+		struct drm_display_mode *adj_mode)
+{
+	if (!phys_enc || !adj_mode) {
+		SDE_ERROR("invalid args\n");
+		return;
+	}
+
+	phys_enc->cached_mode = *adj_mode;
+	phys_enc->enable_state = SDE_ENC_ENABLED;
+
+	_sde_encoder_phys_cmd_setup_irq_hw_idx(phys_enc);
+}
+
 static void sde_encoder_phys_cmd_mode_set(
 		struct sde_encoder_phys *phys_enc,
 		struct drm_display_mode *mode,
@@ -414,26 +433,24 @@
 			to_sde_encoder_phys_cmd(phys_enc);
 	u32 frame_event = SDE_ENCODER_FRAME_EVENT_ERROR
 				| SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
-	bool do_log = false;
 
 	if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_ctl)
 		return -EINVAL;
 
 	cmd_enc->pp_timeout_report_cnt++;
-	if (cmd_enc->pp_timeout_report_cnt == PP_TIMEOUT_MAX_TRIALS) {
-		frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
-		do_log = true;
-	} else if (cmd_enc->pp_timeout_report_cnt == 1) {
-		do_log = true;
-	}
 
 	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
 			cmd_enc->pp_timeout_report_cnt,
 			atomic_read(&phys_enc->pending_kickoff_cnt),
 			frame_event);
 
-	/* to avoid flooding, only log first time, and "dead" time */
-	if (do_log) {
+	if (cmd_enc->pp_timeout_report_cnt >= PP_TIMEOUT_MAX_TRIALS) {
+		cmd_enc->pp_timeout_report_cnt = PP_TIMEOUT_MAX_TRIALS;
+		frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
+
+		SDE_DBG_DUMP("panic");
+	} else if (cmd_enc->pp_timeout_report_cnt == 1) {
+		/* to avoid flooding, only log first time, and "dead" time */
 		SDE_ERROR_CMDENC(cmd_enc,
 				"pp:%d kickoff timed out ctl %d cnt %d koff_cnt %d\n",
 				phys_enc->hw_pp->idx - PINGPONG_0,
@@ -442,9 +459,6 @@
 				atomic_read(&phys_enc->pending_kickoff_cnt));
 
 		SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);
-
-		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
-		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
 	}
 
 	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
@@ -469,6 +483,21 @@
 			phys_enc->split_role == ENC_ROLE_SLAVE;
 }
 
+static bool _sde_encoder_phys_is_disabling_ppsplit_slave(
+		struct sde_encoder_phys *phys_enc)
+{
+	enum sde_rm_topology_name old_top;
+
+	if (!phys_enc || !phys_enc->connector ||
+			phys_enc->split_role != ENC_ROLE_SLAVE)
+		return false;
+
+	old_top = sde_connector_get_old_topology_name(
+			phys_enc->connector->state);
+
+	return old_top == SDE_RM_TOPOLOGY_PPSPLIT;
+}
+
 static int _sde_encoder_phys_cmd_poll_write_pointer_started(
 		struct sde_encoder_phys *phys_enc)
 {
@@ -658,7 +687,15 @@
 {
 	struct sde_encoder_phys_cmd *cmd_enc;
 
-	if (!phys_enc || _sde_encoder_phys_is_ppsplit_slave(phys_enc))
+	if (!phys_enc)
+		return;
+
+	/**
+	 * pingpong split slaves do not register for IRQs
+	 * check old and new topologies
+	 */
+	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc) ||
+			_sde_encoder_phys_is_disabling_ppsplit_slave(phys_enc))
 		return;
 
 	cmd_enc = to_sde_encoder_phys_cmd(phys_enc);
@@ -886,7 +923,7 @@
 	return cfg.enable;
 }
 
-static void _sde_encoder_phys_cmd_connect_te(
+static void sde_encoder_phys_cmd_connect_te(
 		struct sde_encoder_phys *phys_enc, bool enable)
 {
 	if (!phys_enc || !phys_enc->hw_pp ||
@@ -897,6 +934,46 @@
 	phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
 }
 
+static int sde_encoder_phys_cmd_get_line_count(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_hw_pingpong *hw_pp;
+
+	if (!phys_enc || !phys_enc->hw_pp)
+		return -EINVAL;
+
+	if (!sde_encoder_phys_cmd_is_master(phys_enc))
+		return -EINVAL;
+
+	hw_pp = phys_enc->hw_pp;
+	if (!hw_pp->ops.get_line_count)
+		return -EINVAL;
+
+	return hw_pp->ops.get_line_count(hw_pp);
+}
+
+static int sde_encoder_phys_cmd_get_write_line_count(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_hw_pingpong *hw_pp;
+	struct sde_hw_pp_vsync_info info;
+
+	if (!phys_enc || !phys_enc->hw_pp)
+		return -EINVAL;
+
+	if (!sde_encoder_phys_cmd_is_master(phys_enc))
+		return -EINVAL;
+
+	hw_pp = phys_enc->hw_pp;
+	if (!hw_pp->ops.get_vsync_info)
+		return -EINVAL;
+
+	if (hw_pp->ops.get_vsync_info(hw_pp, &info))
+		return -EINVAL;
+
+	return (int)info.wr_ptr_line_count;
+}
+
 static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_phys_cmd *cmd_enc =
@@ -956,7 +1033,7 @@
 	hw_res->intfs[phys_enc->intf_idx - INTF_0] = INTF_MODE_CMD;
 }
 
-static void sde_encoder_phys_cmd_prepare_for_kickoff(
+static int sde_encoder_phys_cmd_prepare_for_kickoff(
 		struct sde_encoder_phys *phys_enc,
 		struct sde_encoder_kickoff_params *params)
 {
@@ -966,7 +1043,7 @@
 
 	if (!phys_enc || !phys_enc->hw_pp) {
 		SDE_ERROR("invalid encoder\n");
-		return;
+		return -EINVAL;
 	}
 	SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);
 
@@ -990,6 +1067,7 @@
 	SDE_DEBUG_CMDENC(cmd_enc, "pp:%d pending_cnt %d\n",
 			phys_enc->hw_pp->idx - PINGPONG_0,
 			atomic_read(&phys_enc->pending_kickoff_cnt));
+	return ret;
 }
 
 static int _sde_encoder_phys_cmd_wait_for_ctl_start(
@@ -999,6 +1077,7 @@
 			to_sde_encoder_phys_cmd(phys_enc);
 	struct sde_encoder_wait_info wait_info;
 	int ret;
+	bool frame_pending = true;
 
 	if (!phys_enc || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid argument(s)\n");
@@ -1007,7 +1086,7 @@
 
 	wait_info.wq = &phys_enc->pending_kickoff_wq;
 	wait_info.atomic_cnt = &phys_enc->pending_ctlstart_cnt;
-	wait_info.timeout_ms = CTL_START_TIMEOUT_MS;
+	wait_info.timeout_ms = KICKOFF_TIMEOUT_MS;
 
 	/* slave encoder doesn't enable for ppsplit */
 	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
@@ -1016,10 +1095,17 @@
 	ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_CTL_START,
 			&wait_info);
 	if (ret == -ETIMEDOUT) {
-		SDE_ERROR_CMDENC(cmd_enc, "ctl start interrupt wait failed\n");
-		ret = -EINVAL;
-	} else if (!ret)
-		ret = 0;
+		struct sde_hw_ctl *ctl = phys_enc->hw_ctl;
+
+		if (ctl && ctl->ops.get_start_state)
+			frame_pending = ctl->ops.get_start_state(ctl);
+
+		if (frame_pending)
+			SDE_ERROR_CMDENC(cmd_enc,
+					"ctl start interrupt wait failed\n");
+		else
+			ret = 0;
+	}
 
 	return ret;
 }
@@ -1184,19 +1270,6 @@
 	SDE_DEBUG_CMDENC(cmd_enc, "disabled autorefresh\n");
 }
 
-static void sde_encoder_phys_cmd_handle_post_kickoff(
-		struct sde_encoder_phys *phys_enc)
-{
-	if (!phys_enc)
-		return;
-
-	/**
-	 * re-enable external TE, either for the first time after enabling
-	 * or if disabled for Autorefresh
-	 */
-	_sde_encoder_phys_cmd_connect_te(phys_enc, true);
-}
-
 static void sde_encoder_phys_cmd_trigger_start(
 		struct sde_encoder_phys *phys_enc)
 {
@@ -1223,6 +1296,7 @@
 	ops->prepare_commit = sde_encoder_phys_cmd_prepare_commit;
 	ops->is_master = sde_encoder_phys_cmd_is_master;
 	ops->mode_set = sde_encoder_phys_cmd_mode_set;
+	ops->cont_splash_mode_set = sde_encoder_phys_cmd_cont_splash_mode_set;
 	ops->mode_fixup = sde_encoder_phys_cmd_mode_fixup;
 	ops->enable = sde_encoder_phys_cmd_enable;
 	ops->disable = sde_encoder_phys_cmd_disable;
@@ -1233,15 +1307,19 @@
 	ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
 	ops->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete;
 	ops->wait_for_vblank = sde_encoder_phys_cmd_wait_for_vblank;
+	ops->trigger_flush = sde_encoder_helper_trigger_flush;
 	ops->trigger_start = sde_encoder_phys_cmd_trigger_start;
 	ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
 	ops->hw_reset = sde_encoder_helper_hw_reset;
 	ops->irq_control = sde_encoder_phys_cmd_irq_control;
 	ops->update_split_role = sde_encoder_phys_cmd_update_split_role;
 	ops->restore = sde_encoder_phys_cmd_enable_helper;
+	ops->control_te = sde_encoder_phys_cmd_connect_te;
 	ops->is_autorefresh_enabled =
 			sde_encoder_phys_cmd_is_autorefresh_enabled;
-	ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff;
+	ops->get_line_count = sde_encoder_phys_cmd_get_line_count;
+	ops->get_wr_line_count = sde_encoder_phys_cmd_get_write_line_count;
+	ops->wait_for_active = NULL;
 }
 
 struct sde_encoder_phys *sde_encoder_phys_cmd_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 61e761d..aaf50f6 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -33,6 +33,13 @@
 #define to_sde_encoder_phys_vid(x) \
 	container_of(x, struct sde_encoder_phys_vid, base)
 
+/* maximum number of consecutive kickoff errors */
+#define KICKOFF_MAX_ERRORS	2
+
+/* Poll time to do recovery during active region */
+#define POLL_TIME_USEC_FOR_LN_CNT 500
+#define MAX_POLL_CNT 10
+
 static bool sde_encoder_phys_vid_is_master(
 		struct sde_encoder_phys *phys_enc)
 {
@@ -157,15 +164,6 @@
 	u32 needed_vfp_lines = worst_case_needed_lines - start_of_frame_lines;
 	u32 actual_vfp_lines = 0;
 
-	if (worst_case_needed_lines < start_of_frame_lines) {
-		needed_vfp_lines = 0;
-		SDE_ERROR("invalid params - needed_lines:%d, frame_lines:%d\n",
-				worst_case_needed_lines, start_of_frame_lines);
-	} else {
-		needed_vfp_lines = worst_case_needed_lines
-					- start_of_frame_lines;
-	}
-
 	/* Fetch must be outside active lines, otherwise undefined. */
 	if (start_of_frame_lines >= worst_case_needed_lines) {
 		SDE_DEBUG_VIDENC(vid_enc,
@@ -288,8 +286,9 @@
 		return;
 
 	SDE_DEBUG_VIDENC(vid_enc,
-		"rot_fetch_lines %u rot_fetch_start_vsync_counter %u\n",
-		rot_fetch_lines, rot_fetch_start_vsync_counter);
+		"rot_fetch_lines %u vfp_fetch_lines %u rot_fetch_start_vsync_counter %u\n",
+		rot_fetch_lines, vfp_fetch_lines,
+		rot_fetch_start_vsync_counter);
 
 	phys_enc->hw_ctl->ops.get_bitmask_intf(
 			phys_enc->hw_ctl, &flush_mask, vid_enc->hw_intf->idx);
@@ -464,10 +463,26 @@
 	return false;
 }
 
+static bool _sde_encoder_phys_is_dual_ctl(struct sde_encoder_phys *phys_enc)
+{
+	enum sde_rm_topology_name topology;
+
+	if (!phys_enc)
+		return false;
+
+	topology = sde_connector_get_topology_name(phys_enc->connector);
+	if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_DSC) ||
+		(topology == SDE_RM_TOPOLOGY_DUALPIPE))
+		return true;
+
+	return false;
+}
+
 static bool sde_encoder_phys_vid_needs_single_flush(
 		struct sde_encoder_phys *phys_enc)
 {
-	return phys_enc && _sde_encoder_phys_is_ppsplit(phys_enc);
+	return phys_enc && (_sde_encoder_phys_is_ppsplit(phys_enc) ||
+		_sde_encoder_phys_is_dual_ctl(phys_enc));
 }
 
 static void _sde_encoder_phys_vid_setup_irq_hw_idx(
@@ -475,13 +490,34 @@
 {
 	struct sde_encoder_irq *irq;
 
+	/*
+	 * Initialize irq->hw_idx only when irq is not registered.
+	 * Prevent invalidating irq->irq_idx as modeset may be
+	 * called many times during dfps.
+	 */
+
 	irq = &phys_enc->irq[INTR_IDX_VSYNC];
-	irq->hw_idx = phys_enc->intf_idx;
-	irq->irq_idx = -EINVAL;
+	if (irq->irq_idx < 0)
+		irq->hw_idx = phys_enc->intf_idx;
 
 	irq = &phys_enc->irq[INTR_IDX_UNDERRUN];
-	irq->hw_idx = phys_enc->intf_idx;
-	irq->irq_idx = -EINVAL;
+	if (irq->irq_idx < 0)
+		irq->hw_idx = phys_enc->intf_idx;
+}
+
+static void sde_encoder_phys_vid_cont_splash_mode_set(
+		struct sde_encoder_phys *phys_enc,
+		struct drm_display_mode *adj_mode)
+{
+	if (!phys_enc || !adj_mode) {
+		SDE_ERROR("invalid args\n");
+		return;
+	}
+
+	phys_enc->cached_mode = *adj_mode;
+	phys_enc->enable_state = SDE_ENC_ENABLED;
+
+	_sde_encoder_phys_vid_setup_irq_hw_idx(phys_enc);
 }
 
 static void sde_encoder_phys_vid_mode_set(
@@ -577,6 +613,37 @@
 	return ret;
 }
 
+static bool sde_encoder_phys_vid_wait_dma_trigger(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_phys_vid *vid_enc;
+	struct sde_hw_intf *intf;
+	struct sde_hw_ctl *ctl;
+	struct intf_status status;
+
+	if (!phys_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return false;
+	}
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	intf = vid_enc->hw_intf;
+	ctl = phys_enc->hw_ctl;
+	if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
+		SDE_ERROR("invalid hw_intf %d hw_ctl %d\n",
+			vid_enc->hw_intf != NULL, phys_enc->hw_ctl != NULL);
+		return false;
+	}
+
+	if (!intf->ops.get_status)
+		return false;
+
+	intf->ops.get_status(intf, &status);
+
+	/* if interface is not enabled, return true to wait for dma trigger */
+	return status.is_en ? false : true;
+}
+
 static void sde_encoder_phys_vid_enable(struct sde_encoder_phys *phys_enc)
 {
 	struct msm_drm_private *priv;
@@ -614,10 +681,11 @@
 	sde_encoder_phys_vid_setup_timing_engine(phys_enc);
 
 	/*
-	 * For pp-split, skip setting the flush bit for the slave intf, since
-	 * both intfs use same ctl and HW will only flush the master.
+	 * For single flush cases (dual-ctl or pp-split), skip setting the
+	 * flush bit for the slave intf, since both intfs use same ctl
+	 * and HW will only flush the master.
 	 */
-	if (_sde_encoder_phys_is_ppsplit(phys_enc) &&
+	if (sde_encoder_phys_vid_needs_single_flush(phys_enc) &&
 		!sde_encoder_phys_vid_is_master(phys_enc))
 		goto skip_flush;
 
@@ -676,7 +744,8 @@
 		struct sde_encoder_phys *phys_enc, bool notify)
 {
 	struct sde_encoder_wait_info wait_info;
-	int ret;
+	int ret = 0;
+	u32 event = 0;
 
 	if (!phys_enc) {
 		pr_err("invalid encoder\n");
@@ -689,11 +758,10 @@
 
 	if (!sde_encoder_phys_vid_is_master(phys_enc)) {
 		/* signal done for slave video encoder, unless it is pp-split */
-		if (!_sde_encoder_phys_is_ppsplit(phys_enc) &&
-			notify && phys_enc->parent_ops.handle_frame_done)
-			phys_enc->parent_ops.handle_frame_done(
-					phys_enc->parent, phys_enc,
-					SDE_ENCODER_FRAME_EVENT_DONE);
+		if (!_sde_encoder_phys_is_ppsplit(phys_enc) && notify) {
+			event = SDE_ENCODER_FRAME_EVENT_DONE;
+			goto end;
+		}
 		return 0;
 	}
 
@@ -701,13 +769,20 @@
 	ret = sde_encoder_helper_wait_for_irq(phys_enc, INTR_IDX_VSYNC,
 			&wait_info);
 
-	if (ret == -ETIMEDOUT) {
-		sde_encoder_helper_report_irq_timeout(phys_enc, INTR_IDX_VSYNC);
-	} else if (!ret && notify && phys_enc->parent_ops.handle_frame_done)
+	if (ret == -ETIMEDOUT)
+		event = SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE
+				| SDE_ENCODER_FRAME_EVENT_SIGNAL_RETIRE_FENCE
+				| SDE_ENCODER_FRAME_EVENT_ERROR;
+	else if (!ret && notify)
+		event = SDE_ENCODER_FRAME_EVENT_DONE;
+
+end:
+	SDE_EVT32(DRMID(phys_enc->parent), event, notify, ret,
+			ret ? SDE_EVTLOG_FATAL : 0);
+	if (phys_enc->parent_ops.handle_frame_done && event)
 		phys_enc->parent_ops.handle_frame_done(
 				phys_enc->parent, phys_enc,
 				SDE_ENCODER_FRAME_EVENT_DONE);
-
 	return ret;
 }
 
@@ -717,7 +792,7 @@
 	return _sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
 }
 
-static void sde_encoder_phys_vid_prepare_for_kickoff(
+static int sde_encoder_phys_vid_prepare_for_kickoff(
 		struct sde_encoder_phys *phys_enc,
 		struct sde_encoder_kickoff_params *params)
 {
@@ -725,15 +800,15 @@
 	struct sde_hw_ctl *ctl;
 	int rc;
 
-	if (!phys_enc || !params) {
+	if (!phys_enc || !params || !phys_enc->hw_ctl) {
 		SDE_ERROR("invalid encoder/parameters\n");
-		return;
+		return -EINVAL;
 	}
 	vid_enc = to_sde_encoder_phys_vid(phys_enc);
 
 	ctl = phys_enc->hw_ctl;
-	if (!ctl || !ctl->ops.wait_reset_status)
-		return;
+	if (!ctl->ops.wait_reset_status)
+		return 0;
 
 	/*
 	 * hw supports hardware initiated ctl reset, so before we kickoff a new
@@ -743,11 +818,25 @@
 	if (rc) {
 		SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n",
 				ctl->idx, rc);
-		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_VSYNC);
-		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
+
+		++vid_enc->error_count;
+		if (vid_enc->error_count >= KICKOFF_MAX_ERRORS) {
+			vid_enc->error_count = KICKOFF_MAX_ERRORS;
+
+			SDE_DBG_DUMP("panic");
+		} else if (vid_enc->error_count == 1) {
+			SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);
+		}
+
+		/* request a ctl reset before the next flush */
+		phys_enc->enable_state = SDE_ENC_ERR_NEEDS_HW_RESET;
+	} else {
+		vid_enc->error_count = 0;
 	}
 
 	programmable_rot_fetch_config(phys_enc, params->inline_rotate_prefill);
+
+	return rc;
 }
 
 static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
@@ -787,6 +876,9 @@
 		sde_encoder_phys_inc_pending(phys_enc);
 	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
 
+	if (!sde_encoder_phys_vid_is_master(phys_enc))
+		goto exit;
+
 	/*
 	 * Wait for a vsync so we know the ENABLE=0 latched before
 	 * the (connector) source of the vsync's gets disabled,
@@ -795,7 +887,16 @@
 	 * the settings changes for the new modeset (like new
 	 * scanout buffer) don't latch properly..
 	 */
-	if (sde_encoder_phys_vid_is_master(phys_enc)) {
+	ret = sde_encoder_phys_vid_control_vblank_irq(phys_enc, true);
+	if (ret) {
+		SDE_ERROR_VIDENC(vid_enc,
+				"failed to enable vblank irq: %d\n",
+				ret);
+		SDE_EVT32(DRMID(phys_enc->parent),
+				vid_enc->hw_intf->idx - INTF_0, ret,
+				SDE_EVTLOG_FUNC_CASE1,
+				SDE_EVTLOG_ERROR);
+	} else {
 		ret = _sde_encoder_phys_vid_wait_for_vblank(phys_enc, false);
 		if (ret) {
 			atomic_set(&phys_enc->pending_kickoff_cnt, 0);
@@ -803,10 +904,13 @@
 					"failure waiting for disable: %d\n",
 					ret);
 			SDE_EVT32(DRMID(phys_enc->parent),
-					vid_enc->hw_intf->idx - INTF_0, ret);
+					vid_enc->hw_intf->idx - INTF_0, ret,
+					SDE_EVTLOG_FUNC_CASE2,
+					SDE_EVTLOG_ERROR);
 		}
+		sde_encoder_phys_vid_control_vblank_irq(phys_enc, false);
 	}
-
+exit:
 	phys_enc->enable_state = SDE_ENC_DISABLED;
 }
 
@@ -890,10 +994,95 @@
 		vid_enc->hw_intf->ops.collect_misr(vid_enc->hw_intf) : 0;
 }
 
+static int sde_encoder_phys_vid_get_line_count(
+		struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_phys_vid *vid_enc;
+
+	if (!phys_enc)
+		return -EINVAL;
+
+	if (!sde_encoder_phys_vid_is_master(phys_enc))
+		return -EINVAL;
+
+	vid_enc = to_sde_encoder_phys_vid(phys_enc);
+	if (!vid_enc->hw_intf || !vid_enc->hw_intf->ops.get_line_count)
+		return -EINVAL;
+
+	return vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf);
+}
+
+static int sde_encoder_phys_vid_wait_for_active(
+			struct sde_encoder_phys *phys_enc)
+{
+	struct drm_display_mode mode;
+	struct sde_encoder_phys_vid *vid_enc;
+	u32 ln_cnt, min_ln_cnt, active_lns_cnt;
+	u32 clk_period, time_of_line;
+	u32 delay, retry = MAX_POLL_CNT;
+
+	vid_enc =  to_sde_encoder_phys_vid(phys_enc);
+
+	if (!vid_enc->hw_intf || !vid_enc->hw_intf->ops.get_line_count) {
+		SDE_ERROR_VIDENC(vid_enc, "invalid vid_enc params\n");
+		return -EINVAL;
+	}
+
+	mode = phys_enc->cached_mode;
+
+	/*
+	 * calculate clk_period as pico second to maintain good
+	 * accuracy with high pclk rate and this number is in 17 bit
+	 * range.
+	 */
+	clk_period = DIV_ROUND_UP_ULL(1000000000, mode.clock);
+	if (!clk_period) {
+		SDE_ERROR_VIDENC(vid_enc, "Unable to calculate clock period\n");
+		return -EINVAL;
+	}
+
+	min_ln_cnt = (mode.vtotal - mode.vsync_start) +
+		(mode.vsync_end - mode.vsync_start);
+	active_lns_cnt = mode.vdisplay;
+	time_of_line = mode.htotal * clk_period;
+
+	/* delay in micro seconds */
+	delay = (time_of_line * (min_ln_cnt +
+		(mode.vsync_start - mode.vdisplay))) / 1000000;
+
+	/*
+	 * Wait for max delay before
+	 * polling to check active region
+	 */
+	if (delay > POLL_TIME_USEC_FOR_LN_CNT)
+		delay = POLL_TIME_USEC_FOR_LN_CNT;
+
+	while (retry) {
+		ln_cnt = vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf);
+
+		if ((ln_cnt >= min_ln_cnt) &&
+			(ln_cnt < (active_lns_cnt + min_ln_cnt))) {
+			SDE_DEBUG_VIDENC(vid_enc,
+					"Needed lines left line_cnt=%d\n",
+					ln_cnt);
+			return 0;
+		}
+
+		SDE_ERROR_VIDENC(vid_enc, "line count is less. line_cnt = %d\n",
+				ln_cnt);
+		/* Add delay so that line count is in active region */
+		udelay(delay);
+		retry--;
+	}
+
+	return -EINVAL;
+}
+
 static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
 {
 	ops->is_master = sde_encoder_phys_vid_is_master;
 	ops->mode_set = sde_encoder_phys_vid_mode_set;
+	ops->cont_splash_mode_set = sde_encoder_phys_vid_cont_splash_mode_set;
 	ops->mode_fixup = sde_encoder_phys_vid_mode_fixup;
 	ops->enable = sde_encoder_phys_vid_enable;
 	ops->disable = sde_encoder_phys_vid_disable;
@@ -909,7 +1098,12 @@
 	ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
 	ops->setup_misr = sde_encoder_phys_vid_setup_misr;
 	ops->collect_misr = sde_encoder_phys_vid_collect_misr;
+	ops->trigger_flush = sde_encoder_helper_trigger_flush;
 	ops->hw_reset = sde_encoder_helper_hw_reset;
+	ops->get_line_count = sde_encoder_phys_vid_get_line_count;
+	ops->get_wr_line_count = sde_encoder_phys_vid_get_line_count;
+	ops->wait_dma_trigger = sde_encoder_phys_vid_wait_dma_trigger;
+	ops->wait_for_active = sde_encoder_phys_vid_wait_for_active;
 }
 
 struct sde_encoder_phys *sde_encoder_phys_vid_init(
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 240e521..42cf015 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -27,7 +27,8 @@
 #define to_sde_encoder_phys_wb(x) \
 	container_of(x, struct sde_encoder_phys_wb, base)
 
-#define WBID(wb_enc) ((wb_enc) ? wb_enc->wb_dev->wb_idx : -1)
+#define WBID(wb_enc) \
+	((wb_enc && wb_enc->wb_dev) ? wb_enc->wb_dev->wb_idx - WB_0 : -1)
 
 #define TO_S15D16(_x_)	((_x_) << 7)
 
@@ -458,10 +459,11 @@
 	SDE_DEBUG("[roi:%u,%u,%u,%u]\n", wb_roi.x, wb_roi.y,
 			wb_roi.w, wb_roi.h);
 
+	/* bypass check if commit with no framebuffer */
 	fb = sde_wb_connector_state_get_output_fb(conn_state);
 	if (!fb) {
-		SDE_ERROR("no output framebuffer\n");
-		return -EINVAL;
+		SDE_DEBUG("no output framebuffer\n");
+		return 0;
 	}
 
 	SDE_DEBUG("[fb_id:%u][fb:%u,%u]\n", fb->base.id,
@@ -847,7 +849,14 @@
 		return -EWOULDBLOCK;
 	}
 
-	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count);
+	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->frame_count,
+			!!wb_enc->wb_fb);
+
+	/* signal completion if commit with no framebuffer */
+	if (!wb_enc->wb_fb) {
+		SDE_DEBUG("no output framebuffer\n");
+		sde_encoder_phys_wb_done_irq(wb_enc, wb_enc->irq_idx);
+	}
 
 	ret = wait_for_completion_timeout(&wb_enc->wbdone_complete,
 			msecs_to_jiffies(timeout));
@@ -859,11 +868,11 @@
 				wb_enc->irq_idx, true);
 		if (irq_status) {
 			SDE_DEBUG("wb:%d done but irq not triggered\n",
-					wb_enc->wb_dev->wb_idx - WB_0);
+					WBID(wb_enc));
 			sde_encoder_phys_wb_done_irq(wb_enc, wb_enc->irq_idx);
 		} else {
 			SDE_ERROR("wb:%d kickoff timed out\n",
-					wb_enc->wb_dev->wb_idx - WB_0);
+					WBID(wb_enc));
 			atomic_add_unless(
 				&phys_enc->pending_retire_fence_cnt, -1, 0);
 
@@ -896,8 +905,7 @@
 	if (!rc) {
 		wb_time = (u64)ktime_to_us(wb_enc->end_time) -
 				(u64)ktime_to_us(wb_enc->start_time);
-		SDE_DEBUG("wb:%d took %llu us\n",
-			wb_enc->wb_dev->wb_idx - WB_0, wb_time);
+		SDE_DEBUG("wb:%d took %llu us\n", WBID(wb_enc), wb_time);
 	}
 
 	/* cleanup writeback framebuffer */
@@ -917,8 +925,9 @@
  * sde_encoder_phys_wb_prepare_for_kickoff - pre-kickoff processing
  * @phys_enc:	Pointer to physical encoder
  * @params:	kickoff parameters
+ * Returns:	Zero on success
  */
-static void sde_encoder_phys_wb_prepare_for_kickoff(
+static int sde_encoder_phys_wb_prepare_for_kickoff(
 		struct sde_encoder_phys *phys_enc,
 		struct sde_encoder_kickoff_params *params)
 {
@@ -933,7 +942,7 @@
 	ret = sde_encoder_phys_wb_register_irq(phys_enc);
 	if (ret) {
 		SDE_ERROR("failed to register irq %d\n", ret);
-		return;
+		return ret;
 	}
 
 	wb_enc->kickoff_count++;
@@ -947,6 +956,31 @@
 	wb_enc->start_time = ktime_get();
 
 	SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), wb_enc->kickoff_count);
+	return 0;
+}
+
+/**
+ * sde_encoder_phys_wb_trigger_flush - trigger flush processing
+ * @phys_enc:	Pointer to physical encoder
+ */
+static void sde_encoder_phys_wb_trigger_flush(struct sde_encoder_phys *phys_enc)
+{
+	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
+
+	if (!phys_enc || !wb_enc->hw_wb) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+
+	SDE_DEBUG("[wb:%d]\n", wb_enc->hw_wb->idx - WB_0);
+
+	/* clear pending flush if commit with no framebuffer */
+	if (!wb_enc->wb_fb) {
+		SDE_DEBUG("no output framebuffer\n");
+		return;
+	}
+
+	sde_encoder_helper_trigger_flush(phys_enc);
 }
 
 /**
@@ -1139,7 +1173,9 @@
 	}
 
 	/* reset h/w before final flush */
-	if (sde_encoder_helper_hw_release(phys_enc, wb_enc->fb_disable))
+	if (phys_enc->hw_ctl->ops.clear_pending_flush)
+		phys_enc->hw_ctl->ops.clear_pending_flush(phys_enc->hw_ctl);
+	if (sde_encoder_helper_reset_mixers(phys_enc, wb_enc->fb_disable))
 		goto exit;
 
 	phys_enc->enable_state = SDE_ENC_DISABLING;
@@ -1165,7 +1201,7 @@
 	struct sde_encoder_phys_wb *wb_enc = to_sde_encoder_phys_wb(phys_enc);
 	struct sde_hw_wb *hw_wb;
 	struct drm_framebuffer *fb;
-	const struct sde_format *fmt;
+	const struct sde_format *fmt = NULL;
 
 	if (!phys_enc) {
 		SDE_ERROR("invalid encoder\n");
@@ -1173,22 +1209,19 @@
 	}
 
 	fb = sde_wb_connector_state_get_output_fb(conn_state);
-	if (!fb) {
-		SDE_ERROR("no output framebuffer\n");
-		return;
-	}
-
-	fmt = sde_get_sde_format_ext(fb->pixel_format, fb->modifier,
-			drm_format_num_planes(fb->pixel_format));
-	if (!fmt) {
-		SDE_ERROR("unsupported output pixel format:%d\n",
-				fb->pixel_format);
-		return;
+	if (fb) {
+		fmt = sde_get_sde_format_ext(fb->pixel_format, fb->modifier,
+				drm_format_num_planes(fb->pixel_format));
+		if (!fmt) {
+			SDE_ERROR("unsupported output pixel format:%d\n",
+					fb->pixel_format);
+			return;
+		}
 	}
 
 	hw_wb = wb_enc->hw_wb;
 	hw_res->wbs[hw_wb->idx - WB_0] = phys_enc->intf_mode;
-	hw_res->needs_cdm = SDE_FORMAT_IS_YUV(fmt);
+	hw_res->needs_cdm = fmt ? SDE_FORMAT_IS_YUV(fmt) : false;
 	SDE_DEBUG("[wb:%d] intf_mode=%d needs_cdm=%d\n", hw_wb->idx - WB_0,
 			hw_res->wbs[hw_wb->idx - WB_0],
 			hw_res->needs_cdm);
@@ -1272,6 +1305,7 @@
 	ops->wait_for_commit_done = sde_encoder_phys_wb_wait_for_commit_done;
 	ops->prepare_for_kickoff = sde_encoder_phys_wb_prepare_for_kickoff;
 	ops->handle_post_kickoff = sde_encoder_phys_wb_handle_post_kickoff;
+	ops->trigger_flush = sde_encoder_phys_wb_trigger_flush;
 	ops->trigger_start = sde_encoder_helper_trigger_start;
 	ops->hw_reset = sde_encoder_helper_hw_reset;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.c b/drivers/gpu/drm/msm/sde/sde_fence.c
index ad390af..686c640 100644
--- a/drivers/gpu/drm/msm/sde/sde_fence.c
+++ b/drivers/gpu/drm/msm/sde/sde_fence.c
@@ -136,31 +136,12 @@
 
 static void sde_fence_release(struct fence *fence)
 {
-	struct sde_fence *f = to_sde_fence(fence);
-	struct sde_fence *fc, *next;
-	struct sde_fence_context *ctx;
-	bool release_kref = false;
+	struct sde_fence *f;
 
-	if (!fence || !f->ctx)
-		return;
-
-	ctx = f->ctx;
-
-	spin_lock(&ctx->list_lock);
-	list_for_each_entry_safe(fc, next, &ctx->fence_list_head, fence_list) {
-		/* fence release called before signal */
-		if (f == fc) {
-			list_del_init(&fc->fence_list);
-			release_kref = true;
-			break;
-		}
+	if (fence) {
+		f = to_sde_fence(fence);
+		kfree(f);
 	}
-	spin_unlock(&ctx->list_lock);
-
-	/* keep kput outside spin_lock because it may release ctx */
-	if (release_kref)
-		kref_put(&ctx->kref, sde_fence_destroy);
-	kfree(f);
 }
 
 static void sde_fence_value_str(struct fence *fence, char *str, int size)
@@ -225,9 +206,9 @@
 	/* create fd */
 	fd = get_unused_fd_flags(0);
 	if (fd < 0) {
-		fence_put(&sde_fence->base);
 		SDE_ERROR("failed to get_unused_fd_flags(), %s\n",
 							sde_fence->name);
+		fence_put(&sde_fence->base);
 		goto exit;
 	}
 
@@ -236,8 +217,8 @@
 	if (sync_file == NULL) {
 		put_unused_fd(fd);
 		fd = -EINVAL;
-		fence_put(&sde_fence->base);
 		SDE_ERROR("couldn't create fence, %s\n", sde_fence->name);
+		fence_put(&sde_fence->base);
 		goto exit;
 	}
 
@@ -324,6 +305,8 @@
 		spin_unlock_irqrestore(&ctx->lock, flags);
 
 		if (is_signaled) {
+			list_del_init(&fc->fence_list);
+			fence_put(&fc->base);
 			kref_put(&ctx->kref, sde_fence_destroy);
 		} else {
 			spin_lock(&ctx->list_lock);
@@ -419,3 +402,30 @@
 
 	_sde_fence_trigger(ctx, ts);
 }
+
+void sde_fence_timeline_status(struct sde_fence_context *ctx,
+					struct drm_mode_object *drm_obj)
+{
+	char *obj_name;
+
+	if (!ctx || !drm_obj) {
+		SDE_ERROR("invalid input params\n");
+		return;
+	}
+
+	switch (drm_obj->type) {
+	case DRM_MODE_OBJECT_CRTC:
+		obj_name = "crtc";
+		break;
+	case DRM_MODE_OBJECT_CONNECTOR:
+		obj_name = "connector";
+		break;
+	default:
+		obj_name = "unknown";
+		break;
+	}
+
+	SDE_ERROR("drm obj:%s id:%d type:0x%x done_count:%d commit_count:%d\n",
+		obj_name, drm_obj->id, drm_obj->type, ctx->done_count,
+		ctx->commit_count);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_fence.h b/drivers/gpu/drm/msm/sde/sde_fence.h
index 029175b..29d2ec7 100644
--- a/drivers/gpu/drm/msm/sde/sde_fence.h
+++ b/drivers/gpu/drm/msm/sde/sde_fence.h
@@ -132,6 +132,15 @@
  */
 void sde_fence_signal(struct sde_fence_context *fence, ktime_t ts,
 		bool reset_timeline);
+
+/**
+ * sde_fence_timeline_status - prints fence timeline status
+ * @fence: Pointer fence container
+ * @drm_obj Pointer to drm object associated with fence timeline
+ */
+void sde_fence_timeline_status(struct sde_fence_context *ctx,
+					struct drm_mode_object *drm_obj);
+
 #else
 static inline void *sde_sync_get(uint64_t fd)
 {
@@ -185,6 +194,12 @@
 {
 	return 0;
 }
+
+static inline void sde_fence_timeline_status(struct sde_fence_context *ctx,
+					struct drm_mode_object *drm_obj);
+{
+	/* do nothing */
+}
 #endif /* IS_ENABLED(CONFIG_SW_SYNC) */
 
 #endif /* _SDE_FENCE_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_formats.c b/drivers/gpu/drm/msm/sde/sde_formats.c
index 6880f7b..d09054e 100644
--- a/drivers/gpu/drm/msm/sde/sde_formats.c
+++ b/drivers/gpu/drm/msm/sde/sde_formats.c
@@ -263,109 +263,109 @@
 
 	INTERLEAVED_RGB_FMT(RGB565,
 		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGR565,
 		0, COLOR_5BIT, COLOR_6BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, 0, 3,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, 0, 3,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ARGB1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ABGR1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBA5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRA5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XRGB1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XBGR1555,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBX5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRX5551,
 		COLOR_ALPHA_1BIT, COLOR_5BIT, COLOR_5BIT, COLOR_5BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ARGB4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(ABGR4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBA4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRA4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		true, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XRGB4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
+		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(XBGR4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C2_R_Cr, C0_G_Y, C1_B_Cb, C3_ALPHA, 4,
+		C1_B_Cb, C0_G_Y, C2_R_Cr, C3_ALPHA, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(RGBX4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
+		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
 	INTERLEAVED_RGB_FMT(BGRX4444,
 		COLOR_ALPHA_4BIT, COLOR_4BIT, COLOR_4BIT, COLOR_4BIT,
-		C3_ALPHA, C2_R_Cr, C0_G_Y, C1_B_Cb, 4,
+		C3_ALPHA, C1_B_Cb, C0_G_Y, C2_R_Cr, 4,
 		false, 2, 0,
 		SDE_FETCH_LINEAR, 1),
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 0e1ab51..ddff6ee 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -15,6 +15,7 @@
 #include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/pm_qos.h>
 
 #include "sde_hw_mdss.h"
 #include "sde_hw_catalog.h"
@@ -36,9 +37,12 @@
 /* each entry will have register address and bit offset in that register */
 #define MAX_BIT_OFFSET 2
 
-/* default line width for sspp */
+/* default line width for sspp, mixer, ds (input), wb */
 #define DEFAULT_SDE_LINE_WIDTH 2048
 
+/* default output line width for ds */
+#define DEFAULT_SDE_OUTPUT_LINE_WIDTH 2560
+
 /* max mixer blend stages */
 #define DEFAULT_SDE_MIXER_BLENDSTAGES 7
 
@@ -60,8 +64,8 @@
 /* total number of intf - dp, dsi, hdmi */
 #define INTF_COUNT			3
 
-#define MAX_SSPP_UPSCALE		20
-#define MAX_SSPP_DOWNSCALE		4
+#define MAX_UPSCALE_RATIO		20
+#define MAX_DOWNSCALE_RATIO		4
 #define SSPP_UNITY_SCALE		1
 
 #define MAX_HORZ_DECIMATION		4
@@ -112,6 +116,8 @@
 		"NV12/5/1/1.25 AB24/5/1/1.25 XB24/5/1/1.25"
 #define DEFAULT_MAX_PER_PIPE_BW			2400000
 #define DEFAULT_AMORTIZABLE_THRESHOLD		25
+#define DEFAULT_CPU_MASK			0
+#define DEFAULT_CPU_DMA_LATENCY			PM_QOS_DEFAULT_VALUE
 
 /*************************************************************
  *  DTSI PROPERTY INDEX
@@ -119,6 +125,7 @@
 enum {
 	HW_OFF,
 	HW_LEN,
+	HW_DISP,
 	HW_PROP_MAX,
 };
 
@@ -140,12 +147,17 @@
 	DIM_LAYER,
 	SMART_DMA_REV,
 	IDLE_PC,
+	DEST_SCALER,
+	SMART_PANEL_ALIGN_MODE,
 	SDE_PROP_MAX,
 };
 
 enum {
 	PERF_MAX_BW_LOW,
 	PERF_MAX_BW_HIGH,
+	PERF_MIN_CORE_IB,
+	PERF_MIN_LLCC_IB,
+	PERF_MIN_DRAM_IB,
 	PERF_CORE_IB_FF,
 	PERF_CORE_CLK_FF,
 	PERF_COMP_RATIO_RT,
@@ -159,12 +171,17 @@
 	PERF_XTRA_PREFILL_LINES,
 	PERF_AMORTIZABLE_THRESHOLD,
 	PERF_DANGER_LUT,
-	PERF_SAFE_LUT,
+	PERF_SAFE_LUT_LINEAR,
+	PERF_SAFE_LUT_MACROTILE,
+	PERF_SAFE_LUT_NRT,
+	PERF_SAFE_LUT_CWB,
 	PERF_QOS_LUT_LINEAR,
 	PERF_QOS_LUT_MACROTILE,
 	PERF_QOS_LUT_NRT,
 	PERF_QOS_LUT_CWB,
 	PERF_CDP_SETTING,
+	PERF_CPU_MASK,
+	PERF_CPU_DMA_LATENCY,
 	PERF_PROP_MAX,
 };
 
@@ -230,6 +247,20 @@
 };
 
 enum {
+	DS_TOP_OFF,
+	DS_TOP_LEN,
+	DS_TOP_INPUT_LINEWIDTH,
+	DS_TOP_OUTPUT_LINEWIDTH,
+	DS_TOP_PROP_MAX,
+};
+
+enum {
+	DS_OFF,
+	DS_LEN,
+	DS_PROP_MAX,
+};
+
+enum {
 	DSPP_TOP_OFF,
 	DSPP_TOP_SIZE,
 	DSPP_TOP_PROP_MAX,
@@ -267,6 +298,7 @@
 	MIXER_LEN,
 	MIXER_PAIR_MASK,
 	MIXER_BLOCKS,
+	MIXER_DISP,
 	MIXER_PROP_MAX,
 };
 
@@ -371,11 +403,17 @@
 	{DIM_LAYER, "qcom,sde-has-dim-layer", false, PROP_TYPE_BOOL},
 	{SMART_DMA_REV, "qcom,sde-smart-dma-rev", false, PROP_TYPE_STRING},
 	{IDLE_PC, "qcom,sde-has-idle-pc", false, PROP_TYPE_BOOL},
+	{DEST_SCALER, "qcom,sde-has-dest-scaler", false, PROP_TYPE_BOOL},
+	{SMART_PANEL_ALIGN_MODE, "qcom,sde-smart-panel-align-mode",
+			false, PROP_TYPE_U32},
 };
 
 static struct sde_prop_type sde_perf_prop[] = {
 	{PERF_MAX_BW_LOW, "qcom,sde-max-bw-low-kbps", false, PROP_TYPE_U32},
 	{PERF_MAX_BW_HIGH, "qcom,sde-max-bw-high-kbps", false, PROP_TYPE_U32},
+	{PERF_MIN_CORE_IB, "qcom,sde-min-core-ib-kbps", false, PROP_TYPE_U32},
+	{PERF_MIN_LLCC_IB, "qcom,sde-min-llcc-ib-kbps", false, PROP_TYPE_U32},
+	{PERF_MIN_DRAM_IB, "qcom,sde-min-dram-ib-kbps", false, PROP_TYPE_U32},
 	{PERF_CORE_IB_FF, "qcom,sde-core-ib-ff", false, PROP_TYPE_STRING},
 	{PERF_CORE_CLK_FF, "qcom,sde-core-clk-ff", false, PROP_TYPE_STRING},
 	{PERF_COMP_RATIO_RT, "qcom,sde-comp-ratio-rt", false,
@@ -399,7 +437,14 @@
 	{PERF_AMORTIZABLE_THRESHOLD, "qcom,sde-amortizable-threshold",
 			false, PROP_TYPE_U32},
 	{PERF_DANGER_LUT, "qcom,sde-danger-lut", false, PROP_TYPE_U32_ARRAY},
-	{PERF_SAFE_LUT, "qcom,sde-safe-lut", false, PROP_TYPE_U32_ARRAY},
+	{PERF_SAFE_LUT_LINEAR, "qcom,sde-safe-lut-linear", false,
+			PROP_TYPE_U32_ARRAY},
+	{PERF_SAFE_LUT_MACROTILE, "qcom,sde-safe-lut-macrotile", false,
+			PROP_TYPE_U32_ARRAY},
+	{PERF_SAFE_LUT_NRT, "qcom,sde-safe-lut-nrt", false,
+			PROP_TYPE_U32_ARRAY},
+	{PERF_SAFE_LUT_CWB, "qcom,sde-safe-lut-cwb", false,
+			PROP_TYPE_U32_ARRAY},
 	{PERF_QOS_LUT_LINEAR, "qcom,sde-qos-lut-linear", false,
 			PROP_TYPE_U32_ARRAY},
 	{PERF_QOS_LUT_MACROTILE, "qcom,sde-qos-lut-macrotile", false,
@@ -408,8 +453,12 @@
 			PROP_TYPE_U32_ARRAY},
 	{PERF_QOS_LUT_CWB, "qcom,sde-qos-lut-cwb", false,
 			PROP_TYPE_U32_ARRAY},
+
 	{PERF_CDP_SETTING, "qcom,sde-cdp-setting", false,
 			PROP_TYPE_U32_ARRAY},
+	{PERF_CPU_MASK, "qcom,sde-qos-cpu-mask", false, PROP_TYPE_U32},
+	{PERF_CPU_DMA_LATENCY, "qcom,sde-qos-cpu-dma-latency", false,
+			PROP_TYPE_U32},
 };
 
 static struct sde_prop_type sspp_prop[] = {
@@ -450,6 +499,7 @@
 static struct sde_prop_type ctl_prop[] = {
 	{HW_OFF, "qcom,sde-ctl-off", true, PROP_TYPE_U32_ARRAY},
 	{HW_LEN, "qcom,sde-ctl-size", false, PROP_TYPE_U32},
+	{HW_DISP, "qcom,sde-ctl-display-pref", false, PROP_TYPE_STRING_ARRAY},
 };
 
 struct sde_prop_type mixer_blend_prop[] = {
@@ -463,6 +513,8 @@
 	{MIXER_PAIR_MASK, "qcom,sde-mixer-pair-mask", true,
 		PROP_TYPE_U32_ARRAY},
 	{MIXER_BLOCKS, "qcom,sde-mixer-blocks", false, PROP_TYPE_NODE},
+	{MIXER_DISP, "qcom,sde-mixer-display-pref", false,
+		PROP_TYPE_STRING_ARRAY},
 };
 
 static struct sde_prop_type mixer_blocks_prop[] = {
@@ -500,6 +552,20 @@
 	{AD_VERSION, "qcom,sde-dspp-ad-version", false, PROP_TYPE_U32},
 };
 
+static struct sde_prop_type ds_top_prop[] = {
+	{DS_TOP_OFF, "qcom,sde-dest-scaler-top-off", false, PROP_TYPE_U32},
+	{DS_TOP_LEN, "qcom,sde-dest-scaler-top-size", false, PROP_TYPE_U32},
+	{DS_TOP_INPUT_LINEWIDTH, "qcom,sde-max-dest-scaler-input-linewidth",
+		false, PROP_TYPE_U32},
+	{DS_TOP_OUTPUT_LINEWIDTH, "qcom,sde-max-dest-scaler-output-linewidth",
+		false, PROP_TYPE_U32},
+};
+
+static struct sde_prop_type ds_prop[] = {
+	{DS_OFF, "qcom,sde-dest-scaler-off", false, PROP_TYPE_U32_ARRAY},
+	{DS_LEN, "qcom,sde-dest-scaler-size", false, PROP_TYPE_U32},
+};
+
 static struct sde_prop_type pp_prop[] = {
 	{PP_OFF, "qcom,sde-pp-off", true, PROP_TYPE_U32_ARRAY},
 	{PP_LEN, "qcom,sde-pp-size", false, PROP_TYPE_U32},
@@ -857,8 +923,8 @@
 	struct sde_sspp_cfg *sspp, struct sde_sspp_sub_blks *sblk,
 	bool *prop_exists, struct sde_prop_value *prop_value, u32 *vig_count)
 {
-	sblk->maxupscale = MAX_SSPP_UPSCALE;
-	sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
+	sblk->maxupscale = MAX_UPSCALE_RATIO;
+	sblk->maxdwnscale = MAX_DOWNSCALE_RATIO;
 	sspp->id = SSPP_VIG0 + *vig_count;
 	snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
 			sspp->id - SSPP_VIG0);
@@ -952,8 +1018,8 @@
 	struct sde_sspp_cfg *sspp, struct sde_sspp_sub_blks *sblk,
 	bool *prop_exists, struct sde_prop_value *prop_value, u32 *rgb_count)
 {
-	sblk->maxupscale = MAX_SSPP_UPSCALE;
-	sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
+	sblk->maxupscale = MAX_UPSCALE_RATIO;
+	sblk->maxdwnscale = MAX_DOWNSCALE_RATIO;
 	sspp->id = SSPP_RGB0 + *rgb_count;
 	snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
 			sspp->id - SSPP_VIG0);
@@ -1253,6 +1319,8 @@
 		goto end;
 
 	for (i = 0; i < off_count; i++) {
+		const char *disp_pref = NULL;
+
 		ctl = sde_cfg->ctl + i;
 		ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
 		ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
@@ -1260,6 +1328,10 @@
 		snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u",
 				ctl->id - CTL_0);
 
+		of_property_read_string_index(np,
+				ctl_prop[HW_DISP].prop_name, i, &disp_pref);
+		if (disp_pref && !strcmp(disp_pref, "primary"))
+			set_bit(SDE_CTL_PRIMARY_PREF, &ctl->features);
 		if (i < MAX_SPLIT_DISPLAY_CTL)
 			set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
 		if (i < MAX_PP_SPLIT_DISPLAY_CTL)
@@ -1267,7 +1339,6 @@
 		if (sde_cfg->has_sbuf)
 			set_bit(SDE_CTL_SBUF, &ctl->features);
 	}
-
 end:
 	kfree(prop_value);
 	return rc;
@@ -1287,8 +1358,9 @@
 	u32 off_count, blend_off_count, max_blendstages, lm_pair_mask;
 	struct sde_lm_cfg *mixer;
 	struct sde_lm_sub_blks *sblk;
-	int pp_count, dspp_count;
-	u32 pp_idx, dspp_idx;
+	int pp_count, dspp_count, ds_count, mixer_count;
+	u32 pp_idx, dspp_idx, ds_idx;
+	u32 mixer_base;
 	struct device_node *snp = NULL;
 
 	if (!sde_cfg) {
@@ -1310,8 +1382,6 @@
 	if (rc)
 		goto end;
 
-	sde_cfg->mixer_count = off_count;
-
 	rc = _read_dt_entry(np, mixer_prop, ARRAY_SIZE(mixer_prop), prop_count,
 		prop_exists, prop_value);
 	if (rc)
@@ -1319,6 +1389,7 @@
 
 	pp_count = sde_cfg->pingpong_count;
 	dspp_count = sde_cfg->dspp_count;
+	ds_count = sde_cfg->ds_count;
 
 	/* get mixer feature dt properties if they exist */
 	snp = of_get_child_by_name(np, mixer_prop[MIXER_BLOCKS].prop_name);
@@ -1358,8 +1429,16 @@
 	if (rc)
 		goto end;
 
-	for (i = 0, pp_idx = 0, dspp_idx = 0; i < off_count; i++) {
-		mixer = sde_cfg->mixer + i;
+	for (i = 0, mixer_count = 0, pp_idx = 0, dspp_idx = 0,
+			ds_idx = 0; i < off_count; i++) {
+		const char *disp_pref = NULL;
+
+		mixer_base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
+		if (!mixer_base)
+			continue;
+
+		mixer = sde_cfg->mixer + mixer_count;
+
 		sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
 		if (!sblk) {
 			rc = -ENOMEM;
@@ -1368,7 +1447,7 @@
 		}
 		mixer->sblk = sblk;
 
-		mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
+		mixer->base = mixer_base;
 		mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0);
 		mixer->id = LM_0 + i;
 		snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u",
@@ -1395,19 +1474,24 @@
 		if (sde_cfg->has_dim_layer)
 			set_bit(SDE_DIM_LAYER, &mixer->features);
 
-		if ((i < ROT_LM_OFFSET) || (i >= LINE_LM_OFFSET)) {
-			mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
-								: PINGPONG_MAX;
-			mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
-								: DSPP_MAX;
-			pp_count--;
-			dspp_count--;
-			pp_idx++;
-			dspp_idx++;
-		} else {
-			mixer->pingpong = PINGPONG_MAX;
-			mixer->dspp = DSPP_MAX;
-		}
+		of_property_read_string_index(np,
+			mixer_prop[MIXER_DISP].prop_name, i, &disp_pref);
+		if (disp_pref && !strcmp(disp_pref, "primary"))
+			set_bit(SDE_DISP_PRIMARY_PREF, &mixer->features);
+
+		mixer->pingpong = pp_count > 0 ? pp_idx + PINGPONG_0
+							: PINGPONG_MAX;
+		mixer->dspp = dspp_count > 0 ? dspp_idx + DSPP_0
+							: DSPP_MAX;
+		mixer->ds = ds_count > 0 ? ds_idx + DS_0 : DS_MAX;
+		pp_count--;
+		dspp_count--;
+		ds_count--;
+		pp_idx++;
+		dspp_idx++;
+		ds_idx++;
+
+		mixer_count++;
 
 		sblk->gc.id = SDE_MIXER_GC;
 		if (blocks_prop_value && blocks_prop_exists[MIXER_GC_PROP]) {
@@ -1419,6 +1503,7 @@
 			set_bit(SDE_MIXER_GC, &mixer->features);
 		}
 	}
+	sde_cfg->mixer_count = mixer_count;
 
 end:
 	kfree(prop_value);
@@ -2038,6 +2123,116 @@
 	return rc;
 }
 
+static int sde_ds_parse_dt(struct device_node *np,
+			struct sde_mdss_cfg *sde_cfg)
+{
+	int rc, prop_count[DS_PROP_MAX], top_prop_count[DS_TOP_PROP_MAX], i;
+	struct sde_prop_value *prop_value = NULL, *top_prop_value = NULL;
+	bool prop_exists[DS_PROP_MAX], top_prop_exists[DS_TOP_PROP_MAX];
+	u32 off_count = 0, top_off_count = 0;
+	struct sde_ds_cfg *ds;
+	struct sde_ds_top_cfg *ds_top = NULL;
+
+	if (!sde_cfg) {
+		SDE_ERROR("invalid argument\n");
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (!sde_cfg->mdp[0].has_dest_scaler) {
+		SDE_DEBUG("dest scaler feature not supported\n");
+		rc = 0;
+		goto end;
+	}
+
+	/* Parse the dest scaler top register offset and capabilities */
+	top_prop_value = kzalloc(DS_TOP_PROP_MAX *
+			sizeof(struct sde_prop_value), GFP_KERNEL);
+	if (!top_prop_value) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	rc = _validate_dt_entry(np, ds_top_prop,
+				ARRAY_SIZE(ds_top_prop),
+				top_prop_count, &top_off_count);
+	if (rc)
+		goto end;
+
+	rc = _read_dt_entry(np, ds_top_prop,
+			ARRAY_SIZE(ds_top_prop), top_prop_count,
+			top_prop_exists, top_prop_value);
+	if (rc)
+		goto end;
+
+	/* Parse the offset of each dest scaler block */
+	prop_value = kzalloc(DS_PROP_MAX *
+			sizeof(struct sde_prop_value), GFP_KERNEL);
+	if (!prop_value) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	rc = _validate_dt_entry(np, ds_prop, ARRAY_SIZE(ds_prop), prop_count,
+		&off_count);
+	if (rc)
+		goto end;
+
+	sde_cfg->ds_count = off_count;
+
+	rc = _read_dt_entry(np, ds_prop, ARRAY_SIZE(ds_prop), prop_count,
+		prop_exists, prop_value);
+	if (rc)
+		goto end;
+
+	if (!off_count)
+		goto end;
+
+	ds_top = kzalloc(sizeof(struct sde_ds_top_cfg), GFP_KERNEL);
+	if (!ds_top) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	ds_top->id = DS_TOP;
+	snprintf(ds_top->name, SDE_HW_BLK_NAME_LEN, "ds_top_%u",
+		ds_top->id - DS_TOP);
+	ds_top->base = PROP_VALUE_ACCESS(top_prop_value, DS_TOP_OFF, 0);
+	ds_top->len = PROP_VALUE_ACCESS(top_prop_value, DS_TOP_LEN, 0);
+	ds_top->maxupscale = MAX_UPSCALE_RATIO;
+
+	ds_top->maxinputwidth = PROP_VALUE_ACCESS(top_prop_value,
+			DS_TOP_INPUT_LINEWIDTH, 0);
+	if (!top_prop_exists[DS_TOP_INPUT_LINEWIDTH])
+		ds_top->maxinputwidth = DEFAULT_SDE_LINE_WIDTH;
+
+	ds_top->maxoutputwidth = PROP_VALUE_ACCESS(top_prop_value,
+			DS_TOP_OUTPUT_LINEWIDTH, 0);
+	if (!top_prop_exists[DS_TOP_OUTPUT_LINEWIDTH])
+		ds_top->maxoutputwidth = DEFAULT_SDE_OUTPUT_LINE_WIDTH;
+
+	for (i = 0; i < off_count; i++) {
+		ds = sde_cfg->ds + i;
+		ds->top = ds_top;
+		ds->base = PROP_VALUE_ACCESS(prop_value, DS_OFF, i);
+		ds->id = DS_0 + i;
+		ds->len = PROP_VALUE_ACCESS(prop_value, DS_LEN, 0);
+		snprintf(ds->name, SDE_HW_BLK_NAME_LEN, "ds_%u",
+			ds->id - DS_0);
+
+		if (!prop_exists[DS_LEN])
+			ds->len = DEFAULT_SDE_HW_BLOCK_LEN;
+
+		if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3)
+			set_bit(SDE_SSPP_SCALER_QSEED3, &ds->features);
+	}
+
+end:
+	kfree(top_prop_value);
+	kfree(prop_value);
+	return rc;
+};
+
 static int sde_dsc_parse_dt(struct device_node *np,
 			struct sde_mdss_cfg *sde_cfg)
 {
@@ -2542,6 +2737,12 @@
 	if (!prop_exists[UBWC_SWIZZLE])
 		cfg->mdp[0].ubwc_swizzle = DEFAULT_SDE_UBWC_SWIZZLE;
 
+	cfg->mdp[0].has_dest_scaler =
+		PROP_VALUE_ACCESS(prop_value, DEST_SCALER, 0);
+
+	cfg->mdp[0].smart_panel_align_mode =
+		PROP_VALUE_ACCESS(prop_value, SMART_PANEL_ALIGN_MODE, 0);
+
 	rc = of_property_read_string(np, sde_prop[QSEED_TYPE].prop_name, &type);
 	if (!rc && !strcmp(type, "qseedv3")) {
 		cfg->qseed_type = SDE_SSPP_SCALER_QSEED3;
@@ -2648,8 +2849,23 @@
 	if (rc)
 		goto freeprop;
 
-	rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT], 1,
-			&prop_count[PERF_SAFE_LUT], NULL);
+	rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_LINEAR], 1,
+			&prop_count[PERF_SAFE_LUT_LINEAR], NULL);
+	if (rc)
+		goto freeprop;
+
+	rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_MACROTILE], 1,
+			&prop_count[PERF_SAFE_LUT_MACROTILE], NULL);
+	if (rc)
+		goto freeprop;
+
+	rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_NRT], 1,
+			&prop_count[PERF_SAFE_LUT_NRT], NULL);
+	if (rc)
+		goto freeprop;
+
+	rc = _validate_dt_entry(np, &sde_perf_prop[PERF_SAFE_LUT_CWB], 1,
+			&prop_count[PERF_SAFE_LUT_CWB], NULL);
 	if (rc)
 		goto freeprop;
 
@@ -2691,6 +2907,18 @@
 			prop_exists[PERF_MAX_BW_HIGH] ?
 			PROP_VALUE_ACCESS(prop_value, PERF_MAX_BW_HIGH, 0) :
 			DEFAULT_MAX_BW_HIGH;
+	cfg->perf.min_core_ib =
+			prop_exists[PERF_MIN_CORE_IB] ?
+			PROP_VALUE_ACCESS(prop_value, PERF_MIN_CORE_IB, 0) :
+			DEFAULT_MAX_BW_LOW;
+	cfg->perf.min_llcc_ib =
+			prop_exists[PERF_MIN_LLCC_IB] ?
+			PROP_VALUE_ACCESS(prop_value, PERF_MIN_LLCC_IB, 0) :
+			DEFAULT_MAX_BW_LOW;
+	cfg->perf.min_dram_ib =
+			prop_exists[PERF_MIN_DRAM_IB] ?
+			PROP_VALUE_ACCESS(prop_value, PERF_MIN_DRAM_IB, 0) :
+			DEFAULT_MAX_BW_LOW;
 
 	/*
 	 * The following performance parameters (e.g. core_ib_ff) are
@@ -2762,15 +2990,46 @@
 		}
 	}
 
-	if (prop_exists[PERF_SAFE_LUT] && prop_count[PERF_SAFE_LUT] <=
-			SDE_QOS_LUT_USAGE_MAX) {
-		for (j = 0; j < prop_count[PERF_SAFE_LUT]; j++) {
-			cfg->perf.safe_lut_tbl[j] =
-					PROP_VALUE_ACCESS(prop_value,
-						PERF_SAFE_LUT, j);
-			SDE_DEBUG("safe usage:%d lut:0x%x\n",
-					j, cfg->perf.safe_lut_tbl[j]);
+	for (j = 0; j < SDE_QOS_LUT_USAGE_MAX; j++) {
+		static const u32 safe_key[SDE_QOS_LUT_USAGE_MAX] = {
+			[SDE_QOS_LUT_USAGE_LINEAR] =
+					PERF_SAFE_LUT_LINEAR,
+			[SDE_QOS_LUT_USAGE_MACROTILE] =
+					PERF_SAFE_LUT_MACROTILE,
+			[SDE_QOS_LUT_USAGE_NRT] =
+					PERF_SAFE_LUT_NRT,
+			[SDE_QOS_LUT_USAGE_CWB] =
+					PERF_SAFE_LUT_CWB,
+		};
+		const u32 entry_size = 2;
+		int m, count;
+		int key = safe_key[j];
+
+		if (!prop_exists[key])
+			continue;
+
+		count = prop_count[key] / entry_size;
+
+		cfg->perf.sfe_lut_tbl[j].entries = kcalloc(count,
+			sizeof(struct sde_qos_lut_entry), GFP_KERNEL);
+		if (!cfg->perf.sfe_lut_tbl[j].entries) {
+			rc = -ENOMEM;
+			goto freeprop;
 		}
+
+		for (k = 0, m = 0; k < count; k++, m += entry_size) {
+			u64 lut_lo;
+
+			cfg->perf.sfe_lut_tbl[j].entries[k].fl =
+					PROP_VALUE_ACCESS(prop_value, key, m);
+			lut_lo = PROP_VALUE_ACCESS(prop_value, key, m + 1);
+			cfg->perf.sfe_lut_tbl[j].entries[k].lut = lut_lo;
+			SDE_DEBUG("safe usage:%d.%d fl:%d lut:0x%llx\n",
+				j, k,
+				cfg->perf.sfe_lut_tbl[j].entries[k].fl,
+				cfg->perf.sfe_lut_tbl[j].entries[k].lut);
+		}
+		cfg->perf.sfe_lut_tbl[j].nentry = count;
 	}
 
 	for (j = 0; j < SDE_QOS_LUT_USAGE_MAX; j++) {
@@ -2838,6 +3097,15 @@
 		cfg->has_cdp = true;
 	}
 
+	cfg->perf.cpu_mask =
+			prop_exists[PERF_CPU_MASK] ?
+			PROP_VALUE_ACCESS(prop_value, PERF_CPU_MASK, 0) :
+			DEFAULT_CPU_MASK;
+	cfg->perf.cpu_dma_latency =
+			prop_exists[PERF_CPU_DMA_LATENCY] ?
+			PROP_VALUE_ACCESS(prop_value, PERF_CPU_DMA_LATENCY, 0) :
+			DEFAULT_CPU_DMA_LATENCY;
+
 freeprop:
 	kfree(prop_value);
 end:
@@ -2873,7 +3141,8 @@
 	vig_list_size += ARRAY_SIZE(rgb_10bit_formats)
 		+ ARRAY_SIZE(tp10_ubwc_formats)
 		+ ARRAY_SIZE(p010_formats);
-	if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400))
+	if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) ||
+		(IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_410)))
 		vig_list_size += ARRAY_SIZE(p010_ubwc_formats);
 
 	wb2_list_size += ARRAY_SIZE(rgb_10bit_formats)
@@ -2901,6 +3170,12 @@
 		goto end;
 	}
 
+	if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_300) ||
+	    IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_301) ||
+	    IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) ||
+	    IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_401))
+		sde_cfg->has_hdr = true;
+
 	index = sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
 		0, plane_formats, ARRAY_SIZE(plane_formats));
 	index += sde_copy_formats(sde_cfg->dma_formats, dma_list_size,
@@ -2914,7 +3189,8 @@
 		ARRAY_SIZE(rgb_10bit_formats));
 	index += sde_copy_formats(sde_cfg->vig_formats, vig_list_size,
 		index, p010_formats, ARRAY_SIZE(p010_formats));
-	if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400))
+	if (IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_400) ||
+		(IS_SDE_MAJOR_MINOR_SAME((hw_rev), SDE_HW_VER_410)))
 		index += sde_copy_formats(sde_cfg->vig_formats,
 			vig_list_size, index, p010_ubwc_formats,
 			ARRAY_SIZE(p010_ubwc_formats));
@@ -2944,33 +3220,25 @@
 
 	rc = sde_hardware_format_caps(sde_cfg, hw_rev);
 
-	switch (hw_rev) {
-	case SDE_HW_VER_170:
-	case SDE_HW_VER_171:
-	case SDE_HW_VER_172:
+	if (IS_MSM8996_TARGET(hw_rev)) {
 		/* update msm8996 target here */
 		sde_cfg->perf.min_prefill_lines = 21;
-		break;
-	case SDE_HW_VER_300:
-	case SDE_HW_VER_301:
+	} else if (IS_MSM8998_TARGET(hw_rev)) {
 		/* update msm8998 target here */
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->perf.min_prefill_lines = 25;
 		sde_cfg->vbif_qos_nlvl = 4;
 		sde_cfg->ts_prefill_rev = 1;
-		sde_cfg->perf.min_prefill_lines = 25;
-		break;
-	case SDE_HW_VER_400:
+	} else if (IS_SDM845_TARGET(hw_rev) || IS_SDM670_TARGET(hw_rev)) {
 		/* update sdm845 target here */
 		sde_cfg->has_wb_ubwc = true;
 		sde_cfg->perf.min_prefill_lines = 24;
 		sde_cfg->vbif_qos_nlvl = 8;
 		sde_cfg->ts_prefill_rev = 2;
-		sde_cfg->perf.min_prefill_lines = 24;
-		break;
-	default:
+	} else {
+		SDE_ERROR("unsupported chipset id:%X\n", hw_rev);
 		sde_cfg->perf.min_prefill_lines = 0xffff;
-		break;
+		rc = -ENODEV;
 	}
 
 	return rc;
@@ -2995,6 +3263,9 @@
 	for (i = 0; i < sde_cfg->dspp_count; i++)
 		kfree(sde_cfg->dspp[i].sblk);
 
+	if (sde_cfg->ds_count)
+		kfree(sde_cfg->ds[0].top);
+
 	for (i = 0; i < sde_cfg->pingpong_count; i++)
 		kfree(sde_cfg->pingpong[i].sblk);
 
@@ -3063,6 +3334,10 @@
 	if (rc)
 		goto end;
 
+	rc = sde_ds_parse_dt(np, sde_cfg);
+	if (rc)
+		goto end;
+
 	rc = sde_dsc_parse_dt(np, sde_cfg);
 	if (rc)
 		goto end;
@@ -3071,7 +3346,9 @@
 	if (rc)
 		goto end;
 
-	/* mixer parsing should be done after dspp and pp for mapping setup */
+	/* mixer parsing should be done after dspp,
+	 * ds and pp for mapping setup
+	 */
 	rc = sde_mixer_parse_dt(np, sde_cfg);
 	if (rc)
 		goto end;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index fa10a88..1cd65ea 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -45,8 +45,13 @@
 #define SDE_HW_VER_300	SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
 #define SDE_HW_VER_301	SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
 #define SDE_HW_VER_400	SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
+#define SDE_HW_VER_401	SDE_HW_VER(4, 0, 1) /* sdm845 v2.0 */
+#define SDE_HW_VER_410	SDE_HW_VER(4, 1, 0) /* sdm670 v1.0 */
 
+#define IS_MSM8996_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_170)
+#define IS_MSM8998_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_300)
 #define IS_SDM845_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
+#define IS_SDM670_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_410)
 
 #define SDE_HW_BLK_NAME_LEN	16
 
@@ -147,6 +152,7 @@
  * @SDE_MIXER_SOURCESPLIT     Layer mixer supports source-split configuration
  * @SDE_MIXER_GC              Gamma correction block
  * @SDE_DIM_LAYER             Layer mixer supports dim layer
+ * @SDE_DISP_PRIMARY_PREF     Layer mixer preferred for primary display
  * @SDE_MIXER_MAX             maximum value
  */
 enum {
@@ -154,6 +160,7 @@
 	SDE_MIXER_SOURCESPLIT,
 	SDE_MIXER_GC,
 	SDE_DIM_LAYER,
+	SDE_DISP_PRIMARY_PREF,
 	SDE_MIXER_MAX
 };
 
@@ -212,12 +219,14 @@
  * @SDE_CTL_SPLIT_DISPLAY       CTL supports video mode split display
  * @SDE_CTL_PINGPONG_SPLIT      CTL supports pingpong split
  * @SDE_CTL_SBUF                CTL supports inline stream buffer
+ * @SDE_CTL_PRIMARY_PREF        CTL preferred for primary display
  * @SDE_CTL_MAX
  */
 enum {
 	SDE_CTL_SPLIT_DISPLAY = 0x1,
 	SDE_CTL_PINGPONG_SPLIT,
 	SDE_CTL_SBUF,
+	SDE_CTL_PRIMARY_PREF,
 	SDE_CTL_MAX
 };
 
@@ -514,6 +523,8 @@
  * @highest_bank_bit:  UBWC parameter
  * @ubwc_static:       ubwc static configuration
  * @ubwc_swizzle:      ubwc default swizzle setting
+ * @has_dest_scaler:   indicates support of destination scaler
+ * @smart_panel_align_mode: split display smart panel align modes
  * @clk_ctrls          clock control register definition
  */
 struct sde_mdp_cfg {
@@ -521,6 +532,8 @@
 	u32 highest_bank_bit;
 	u32 ubwc_static;
 	u32 ubwc_swizzle;
+	bool has_dest_scaler;
+	u32 smart_panel_align_mode;
 	struct sde_clk_ctrl_reg clk_ctrls[SDE_CLK_CTRL_MAX];
 };
 
@@ -559,6 +572,7 @@
  * @sblk:              LM Sub-blocks information
  * @dspp:              ID of connected DSPP, DSPP_MAX if unsupported
  * @pingpong:          ID of connected PingPong, PINGPONG_MAX if unsupported
+ * @ds:                ID of connected DS, DS_MAX if unsupported
  * @lm_pair_mask:      Bitmask of LMs that can be controlled by same CTL
  */
 struct sde_lm_cfg {
@@ -566,6 +580,7 @@
 	const struct sde_lm_sub_blks *sblk;
 	u32 dspp;
 	u32 pingpong;
+	u32 ds;
 	unsigned long lm_pair_mask;
 };
 
@@ -594,6 +609,38 @@
 };
 
 /**
+ * struct sde_ds_top_cfg - information of dest scaler top
+ * @id               enum identifying this block
+ * @base             register offset of this block
+ * @features         bit mask identifying features
+ * @version          hw version of dest scaler
+ * @maxinputwidth    maximum input line width
+ * @maxoutputwidth   maximum output line width
+ * @maxupscale       maximum upscale ratio
+ */
+struct sde_ds_top_cfg {
+	SDE_HW_BLK_INFO;
+	u32 version;
+	u32 maxinputwidth;
+	u32 maxoutputwidth;
+	u32 maxupscale;
+};
+
+/**
+ * struct sde_ds_cfg - information of dest scaler blocks
+ * @id          enum identifying this block
+ * @base        register offset wrt DS top offset
+ * @features    bit mask identifying features
+ * @version     hw version of the qseed block
+ * @top         DS top information
+ */
+struct sde_ds_cfg {
+	SDE_HW_BLK_INFO;
+	u32 version;
+	const struct sde_ds_top_cfg *top;
+};
+
+/**
  * struct sde_pingpong_cfg - information of PING-PONG blocks
  * @id                 enum identifying this block
  * @base               register offset of this block
@@ -800,6 +847,10 @@
  * struct sde_perf_cfg - performance control settings
  * @max_bw_low         low threshold of maximum bandwidth (kbps)
  * @max_bw_high        high threshold of maximum bandwidth (kbps)
+ * @min_core_ib        minimum bandwidth for core (kbps)
+ * @min_core_ib        minimum mnoc ib vote in kbps
+ * @min_llcc_ib        minimum llcc ib vote in kbps
+ * @min_dram_ib        minimum dram ib vote in kbps
  * @core_ib_ff         core instantaneous bandwidth fudge factor
  * @core_clk_ff        core clock fudge factor
  * @comp_ratio_rt      string of 0 or more of <fourcc>/<ven>/<mod>/<comp ratio>
@@ -813,14 +864,19 @@
  * @downscaling_prefill_lines  downscaling latency in lines
  * @amortizable_theshold minimum y position for traffic shaping prefill
  * @min_prefill_lines  minimum pipeline latency in lines
- * @safe_lut_tbl: LUT tables for safe signals
  * @danger_lut_tbl: LUT tables for danger signals
+ * @sfe_lut_tbl: LUT tables for safe signals
  * @qos_lut_tbl: LUT tables for QoS signals
  * @cdp_cfg            cdp use case configurations
+ * @cpu_mask:          pm_qos cpu mask value
+ * @cpu_dma_latency:   pm_qos cpu dma latency value
  */
 struct sde_perf_cfg {
 	u32 max_bw_low;
 	u32 max_bw_high;
+	u32 min_core_ib;
+	u32 min_llcc_ib;
+	u32 min_dram_ib;
 	const char *core_ib_ff;
 	const char *core_clk_ff;
 	const char *comp_ratio_rt;
@@ -834,10 +890,12 @@
 	u32 downscaling_prefill_lines;
 	u32 amortizable_threshold;
 	u32 min_prefill_lines;
-	u32 safe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
 	u32 danger_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
+	struct sde_qos_lut_tbl sfe_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
 	struct sde_qos_lut_tbl qos_lut_tbl[SDE_QOS_LUT_USAGE_MAX];
 	struct sde_perf_cdp_cfg cdp_cfg[SDE_PERF_CDP_USAGE_MAX];
+	u32 cpu_mask;
+	u32 cpu_dma_latency;
 };
 
 /**
@@ -861,6 +919,7 @@
  * @has_sbuf           indicate if stream buffer is available
  * @sbuf_headroom      stream buffer headroom in lines
  * @has_idle_pc        indicate if idle power collapse feature is supported
+ * @has_hdr            HDR feature support
  * @dma_formats        Supported formats for dma pipe
  * @cursor_formats     Supported formats for cursor pipe
  * @vig_formats        Supported formats for vig pipe
@@ -889,6 +948,7 @@
 	u32 vbif_qos_nlvl;
 	u32 ts_prefill_rev;
 
+	bool has_hdr;
 	u32 mdss_count;
 	struct sde_mdss_base_cfg mdss[MAX_BLOCKS];
 
@@ -909,6 +969,9 @@
 	u32 dspp_count;
 	struct sde_dspp_cfg dspp[MAX_BLOCKS];
 
+	u32 ds_count;
+	struct sde_ds_cfg ds[MAX_BLOCKS];
+
 	u32 pingpong_count;
 	struct sde_pingpong_cfg pingpong[MAX_BLOCKS];
 
@@ -961,6 +1024,7 @@
 #define BLK_CURSOR(s) ((s)->cursor)
 #define BLK_MIXER(s) ((s)->mixer)
 #define BLK_DSPP(s) ((s)->dspp)
+#define BLK_DS(s) ((s)->ds)
 #define BLK_PINGPONG(s) ((s)->pingpong)
 #define BLK_CDM(s) ((s)->cdm)
 #define BLK_INTF(s) ((s)->intf)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 304106d..7a391ae 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -81,6 +81,7 @@
 		if (cdm == m->cdm[i].id) {
 			b->base_off = addr;
 			b->blk_off = m->cdm[i].base;
+			b->length = m->cdm[i].len;
 			b->hwversion = m->hwversion;
 			b->log_mask = SDE_DBG_MASK_CDM;
 			return &m->cdm[i];
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
index 6896ba7..3d2c0a5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
@@ -66,4 +66,42 @@
 #define PCC_GG_OFF 0x70
 #define PCC_BB_OFF 0x7c
 
+#define PA_EN BIT(20)
+#define PA_HUE_EN BIT(25)
+#define PA_SAT_EN BIT(26)
+#define PA_VAL_EN BIT(27)
+#define PA_CONT_EN BIT(28)
+
+#define PA_SIXZONE_HUE_EN BIT(29)
+#define PA_SIXZONE_SAT_EN BIT(30)
+#define PA_SIXZONE_VAL_EN BIT(31)
+
+#define PA_HIST_EN BIT(16)
+
+#define PA_SKIN_EN BIT(7)
+#define PA_FOL_EN BIT(6)
+#define PA_SKY_EN BIT(5)
+
+#define PA_HUE_MASK (BIT(12) - 1)
+#define PA_SAT_MASK (BIT(16) - 1)
+#define PA_VAL_MASK (BIT(8) - 1)
+#define PA_CONT_MASK (BIT(8) - 1)
+
+#define PA_HUE_OFF 0x1c
+#define PA_SAT_OFF 0x20
+#define PA_VAL_OFF 0x24
+#define PA_CONT_OFF 0x28
+#define PA_PWL_HOLD_OFF 0x40
+
+#define PA_DISABLE_REQUIRED(x) \
+	!((x) & (PA_SKIN_EN | PA_SKY_EN | \
+	PA_FOL_EN | PA_HUE_EN | \
+	PA_SAT_EN | PA_VAL_EN | \
+	PA_CONT_EN | PA_HIST_EN | \
+	PA_SIXZONE_HUE_EN | PA_SIXZONE_SAT_EN | \
+	PA_SIXZONE_VAL_EN))
+
+#define SIXZONE_ADJ_CURVE_P1_OFF 0x4
+#define SIXZONE_THRESHOLDS_OFF 0x8
+
 #endif /* _SDE_HW_COLOR_PROC_COMMON_V4_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index 4191367..d32459a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -12,20 +12,29 @@
 
 #include <drm/msm_drm_pp.h>
 #include "sde_hw_color_processing_v1_7.h"
+#include "sde_hw_ctl.h"
+
+#define REG_MASK_SHIFT(n, shift) ((REG_MASK(n)) << (shift))
 
 #define PA_HUE_VIG_OFF		0x110
 #define PA_SAT_VIG_OFF		0x114
 #define PA_VAL_VIG_OFF		0x118
 #define PA_CONT_VIG_OFF		0x11C
 
-#define PA_HUE_DSPP_OFF		0x238
-#define PA_SAT_DSPP_OFF		0x23C
-#define PA_VAL_DSPP_OFF		0x240
-#define PA_CONT_DSPP_OFF	0x244
+#define PA_HUE_DSPP_OFF		0x1c
+#define PA_SAT_DSPP_OFF		0x20
+#define PA_VAL_DSPP_OFF		0x24
+#define PA_CONT_DSPP_OFF	0x28
+
+#define PA_HIST_CTRL_DSPP_OFF	0x4
+#define PA_HIST_DATA_DSPP_OFF	0x400
 
 #define PA_LUTV_DSPP_OFF	0x1400
 #define PA_LUT_SWAP_OFF		0x234
 
+#define PA_LUTV_DSPP_CTRL_OFF	0x4c
+#define PA_LUTV_DSPP_SWAP_OFF	0x18
+
 #define PA_HUE_MASK		0xFFF
 #define PA_SAT_MASK		0xFFFF
 #define PA_VAL_MASK		0xFF
@@ -66,22 +75,49 @@
 #define DSPP_OP_PA_CONT_EN	BIT(28)
 #define DSPP_OP_PA_EN		BIT(20)
 #define DSPP_OP_PA_LUTV_EN	BIT(19)
+#define DSPP_OP_PA_HIST_EN	BIT(16)
 #define DSPP_OP_PA_SKIN_EN	BIT(5)
 #define DSPP_OP_PA_FOL_EN	BIT(6)
 #define DSPP_OP_PA_SKY_EN	BIT(7)
 
+#define DSPP_SZ_ADJ_CURVE_P1_OFF	0x4
+#define DSPP_SZ_THRESHOLDS_OFF	0x8
+#define DSPP_PA_PWL_HOLD_OFF	0x40
+
+#define DSPP_MEMCOL_SIZE0	0x14
+#define DSPP_MEMCOL_SIZE1	0x8
+#define DSPP_MEMCOL_PWL0_OFF	0x0
+#define DSPP_MEMCOL_PWL2_OFF	0x3C
+#define DSPP_MEMCOL_HOLD_SIZE	0x4
+
+#define DSPP_MEMCOL_PROT_VAL_EN BIT(24)
+#define DSPP_MEMCOL_PROT_SAT_EN BIT(23)
+#define DSPP_MEMCOL_PROT_HUE_EN BIT(22)
+#define DSPP_MEMCOL_PROT_CONT_EN BIT(18)
+#define DSPP_MEMCOL_PROT_SIXZONE_EN BIT(17)
+#define DSPP_MEMCOL_PROT_BLEND_EN BIT(3)
+
+#define DSPP_MEMCOL_MASK \
+	(DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_SKY_EN | DSPP_OP_PA_FOL_EN)
+
+#define DSPP_MEMCOL_PROT_MASK \
+	(DSPP_MEMCOL_PROT_HUE_EN | DSPP_MEMCOL_PROT_SAT_EN | \
+	DSPP_MEMCOL_PROT_VAL_EN | DSPP_MEMCOL_PROT_CONT_EN | \
+	DSPP_MEMCOL_PROT_SIXZONE_EN | DSPP_MEMCOL_PROT_BLEND_EN)
+
 #define PA_VIG_DISABLE_REQUIRED(x) \
 			!((x) & (VIG_OP_PA_SKIN_EN | VIG_OP_PA_SKY_EN | \
 			VIG_OP_PA_FOL_EN | VIG_OP_PA_HUE_EN | \
 			VIG_OP_PA_SAT_EN | VIG_OP_PA_VAL_EN | \
 			VIG_OP_PA_CONT_EN))
 
-
 #define PA_DSPP_DISABLE_REQUIRED(x) \
 			!((x) & (DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_SKY_EN | \
 			DSPP_OP_PA_FOL_EN | DSPP_OP_PA_HUE_EN | \
 			DSPP_OP_PA_SAT_EN | DSPP_OP_PA_VAL_EN | \
-			DSPP_OP_PA_CONT_EN | DSPP_OP_PA_LUTV_EN))
+			DSPP_OP_PA_CONT_EN | DSPP_OP_PA_HIST_EN | \
+			DSPP_OP_SZ_HUE_EN | DSPP_OP_SZ_SAT_EN | \
+			DSPP_OP_SZ_VAL_EN))
 
 #define DSPP_OP_PCC_ENABLE	BIT(0)
 #define PCC_OP_MODE_OFF		0
@@ -108,30 +144,27 @@
 
 
 static void __setup_pa_hue(struct sde_hw_blk_reg_map *hw,
-			const struct sde_pp_blk *blk, uint32_t hue,
-			int location)
+		const struct sde_pp_blk *blk, u32 hue, int loc)
 {
 	u32 base = blk->base;
-	u32 offset = (location == DSPP) ? PA_HUE_DSPP_OFF : PA_HUE_VIG_OFF;
-	u32 op_hue_en = (location == DSPP) ? DSPP_OP_PA_HUE_EN :
-					VIG_OP_PA_HUE_EN;
-	u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+	u32 offset = (loc == DSPP) ? PA_HUE_DSPP_OFF : PA_HUE_VIG_OFF;
+	u32 op_hue_en = (loc == DSPP) ? DSPP_OP_PA_HUE_EN : VIG_OP_PA_HUE_EN;
+	u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
 	u32 disable_req;
 	u32 opmode;
 
-	SDE_REG_WRITE(hw, base + offset, hue & PA_HUE_MASK);
-
 	opmode = SDE_REG_READ(hw, base);
+	SDE_REG_WRITE(hw, base + offset, hue & PA_HUE_MASK);
 
 	if (!hue) {
 		opmode &= ~op_hue_en;
-		disable_req = (location == DSPP) ?
+		disable_req = (loc == DSPP) ?
 			PA_DSPP_DISABLE_REQUIRED(opmode) :
 			PA_VIG_DISABLE_REQUIRED(opmode);
 		if (disable_req)
 			opmode &= ~op_pa_en;
 	} else {
-		opmode |= op_hue_en | op_pa_en;
+		opmode |= (op_hue_en | op_pa_en);
 	}
 
 	SDE_REG_WRITE(hw, base, opmode);
@@ -144,38 +177,28 @@
 	__setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic_blk, hue, SSPP);
 }
 
-void sde_setup_dspp_pa_hue_v1_7(struct sde_hw_dspp *ctx, void *cfg)
-{
-	uint32_t hue = *((uint32_t *)cfg);
-
-	__setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP);
-}
-
 static void __setup_pa_sat(struct sde_hw_blk_reg_map *hw,
-			const struct sde_pp_blk *blk, uint32_t sat,
-			int location)
+		const struct sde_pp_blk *blk, u32 sat, int loc)
 {
 	u32 base = blk->base;
-	u32 offset = (location == DSPP) ? PA_SAT_DSPP_OFF : PA_SAT_VIG_OFF;
-	u32 op_sat_en = (location == DSPP) ?
-			DSPP_OP_PA_SAT_EN : VIG_OP_PA_SAT_EN;
-	u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+	u32 offset = (loc == DSPP) ? PA_SAT_DSPP_OFF : PA_SAT_VIG_OFF;
+	u32 op_sat_en = (loc == DSPP) ? DSPP_OP_PA_SAT_EN : VIG_OP_PA_SAT_EN;
+	u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
 	u32 disable_req;
 	u32 opmode;
 
-	SDE_REG_WRITE(hw, base + offset, sat & PA_SAT_MASK);
-
 	opmode = SDE_REG_READ(hw, base);
+	SDE_REG_WRITE(hw, base + offset, sat & PA_SAT_MASK);
 
 	if (!sat) {
 		opmode &= ~op_sat_en;
-		disable_req = (location == DSPP) ?
+		disable_req = (loc == DSPP) ?
 			PA_DSPP_DISABLE_REQUIRED(opmode) :
 			PA_VIG_DISABLE_REQUIRED(opmode);
 		if (disable_req)
 			opmode &= ~op_pa_en;
 	} else {
-		opmode |= op_sat_en | op_pa_en;
+		opmode |= (op_sat_en | op_pa_en);
 	}
 
 	SDE_REG_WRITE(hw, base, opmode);
@@ -189,30 +212,27 @@
 }
 
 static void __setup_pa_val(struct sde_hw_blk_reg_map *hw,
-			const struct sde_pp_blk *blk, uint32_t value,
-			int location)
+		const struct sde_pp_blk *blk, u32 value, int loc)
 {
 	u32 base = blk->base;
-	u32 offset = (location == DSPP) ? PA_VAL_DSPP_OFF : PA_VAL_VIG_OFF;
-	u32 op_val_en = (location == DSPP) ?
-			DSPP_OP_PA_VAL_EN : VIG_OP_PA_VAL_EN;
-	u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+	u32 offset = (loc == DSPP) ? PA_VAL_DSPP_OFF : PA_VAL_VIG_OFF;
+	u32 op_val_en = (loc == DSPP) ? DSPP_OP_PA_VAL_EN : VIG_OP_PA_VAL_EN;
+	u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
 	u32 disable_req;
 	u32 opmode;
 
-	SDE_REG_WRITE(hw, base + offset, value & PA_VAL_MASK);
-
 	opmode = SDE_REG_READ(hw, base);
+	SDE_REG_WRITE(hw, base + offset, value & PA_VAL_MASK);
 
 	if (!value) {
 		opmode &= ~op_val_en;
-		disable_req = (location == DSPP) ?
+		disable_req = (loc == DSPP) ?
 			PA_DSPP_DISABLE_REQUIRED(opmode) :
 			PA_VIG_DISABLE_REQUIRED(opmode);
 		if (disable_req)
 			opmode &= ~op_pa_en;
 	} else {
-		opmode |= op_val_en | op_pa_en;
+		opmode |= (op_val_en | op_pa_en);
 	}
 
 	SDE_REG_WRITE(hw, base, opmode);
@@ -226,30 +246,28 @@
 }
 
 static void __setup_pa_cont(struct sde_hw_blk_reg_map *hw,
-			const struct sde_pp_blk *blk, uint32_t contrast,
-			int location)
+		const struct sde_pp_blk *blk, u32 contrast, int loc)
 {
 	u32 base = blk->base;
-	u32 offset = (location == DSPP) ? PA_CONT_DSPP_OFF : PA_CONT_VIG_OFF;
-	u32 op_cont_en = (location == DSPP) ? DSPP_OP_PA_CONT_EN :
-					VIG_OP_PA_CONT_EN;
-	u32 op_pa_en = (location == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
+	u32 offset = (loc == DSPP) ? PA_CONT_DSPP_OFF : PA_CONT_VIG_OFF;
+	u32 op_cont_en = (loc == DSPP) ?
+		DSPP_OP_PA_CONT_EN : VIG_OP_PA_CONT_EN;
+	u32 op_pa_en = (loc == DSPP) ? DSPP_OP_PA_EN : VIG_OP_PA_EN;
 	u32 disable_req;
 	u32 opmode;
 
-	SDE_REG_WRITE(hw, base + offset, contrast & PA_CONT_MASK);
-
 	opmode = SDE_REG_READ(hw, base);
+	SDE_REG_WRITE(hw, base + offset, contrast & PA_CONT_MASK);
 
 	if (!contrast) {
 		opmode &= ~op_cont_en;
-		disable_req = (location == DSPP) ?
+		disable_req = (loc == DSPP) ?
 			PA_DSPP_DISABLE_REQUIRED(opmode) :
 			PA_VIG_DISABLE_REQUIRED(opmode);
 		if (disable_req)
 			opmode &= ~op_pa_en;
 	} else {
-		opmode |= op_cont_en | op_pa_en;
+		opmode |= (op_cont_en | op_pa_en);
 	}
 
 	SDE_REG_WRITE(hw, base, opmode);
@@ -262,6 +280,120 @@
 	__setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic_blk, contrast, SSPP);
 }
 
+void sde_setup_dspp_pa_hsic_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_pa_hsic *hsic_cfg;
+	u32 hue = 0;
+	u32 sat = 0;
+	u32 val = 0;
+	u32 cont = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	if (hw_cfg->payload &&
+		(hw_cfg->len != sizeof(struct drm_msm_pa_hsic))) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_pa_hsic));
+		return;
+	}
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable pa hsic feature\n");
+	} else {
+		hsic_cfg = hw_cfg->payload;
+		if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE)
+			hue = hsic_cfg->hue;
+		if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE)
+			sat = hsic_cfg->saturation;
+		if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE)
+			val = hsic_cfg->value;
+		if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE)
+			cont = hsic_cfg->contrast;
+	}
+
+	__setup_pa_hue(&ctx->hw, &ctx->cap->sblk->hsic, hue, DSPP);
+	__setup_pa_sat(&ctx->hw, &ctx->cap->sblk->hsic, sat, DSPP);
+	__setup_pa_val(&ctx->hw, &ctx->cap->sblk->hsic, val, DSPP);
+	__setup_pa_cont(&ctx->hw, &ctx->cap->sblk->hsic, cont, DSPP);
+}
+
+void sde_setup_dspp_sixzone_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_sixzone *sixzone;
+	u32 opcode = 0, local_opcode = 0;
+	u32 reg = 0, hold = 0, local_hold = 0;
+	u32 addr = 0;
+	int i = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable sixzone feature\n");
+		opcode &= ~(DSPP_OP_SZ_HUE_EN | DSPP_OP_SZ_SAT_EN |
+			DSPP_OP_SZ_VAL_EN);
+		if (PA_DSPP_DISABLE_REQUIRED(opcode))
+			opcode &= ~DSPP_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_sixzone)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_sixzone));
+		return;
+	}
+
+	sixzone = hw_cfg->payload;
+
+	reg = BIT(26);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->sixzone.base, reg);
+
+	addr = ctx->cap->sblk->sixzone.base + DSPP_SZ_ADJ_CURVE_P1_OFF;
+	for (i = 0; i < SIXZONE_LUT_SIZE; i++) {
+		SDE_REG_WRITE(&ctx->hw, addr, sixzone->curve[i].p1);
+		SDE_REG_WRITE(&ctx->hw, (addr - 4), sixzone->curve[i].p0);
+	}
+
+	addr = ctx->cap->sblk->sixzone.base + DSPP_SZ_THRESHOLDS_OFF;
+	SDE_REG_WRITE(&ctx->hw, addr, sixzone->threshold);
+	SDE_REG_WRITE(&ctx->hw, (addr + 4), sixzone->adjust_p0);
+	SDE_REG_WRITE(&ctx->hw, (addr + 8), sixzone->adjust_p1);
+
+	hold = SDE_REG_READ(&ctx->hw,
+		(ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF));
+	local_hold = ((sixzone->sat_hold & REG_MASK(2)) << 12);
+	local_hold |= ((sixzone->val_hold & REG_MASK(2)) << 14);
+	hold &= ~REG_MASK_SHIFT(4, 12);
+	hold |= local_hold;
+	SDE_REG_WRITE(&ctx->hw,
+		(ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF),
+		hold);
+
+	if (sixzone->flags & SIXZONE_HUE_ENABLE)
+		local_opcode |= DSPP_OP_SZ_HUE_EN;
+	if (sixzone->flags & SIXZONE_SAT_ENABLE)
+		local_opcode |= DSPP_OP_SZ_SAT_EN;
+	if (sixzone->flags & SIXZONE_VAL_ENABLE)
+		local_opcode |= DSPP_OP_SZ_VAL_EN;
+
+	if (local_opcode)
+		local_opcode |= DSPP_OP_PA_EN;
+
+	opcode &= ~REG_MASK_SHIFT(3, 29);
+	opcode |= local_opcode;
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
 void sde_setup_pipe_pa_memcol_v1_7(struct sde_hw_pipe *ctx,
 				   enum sde_memcolor_type type,
 				   void *cfg)
@@ -325,6 +457,220 @@
 	SDE_REG_WRITE(&ctx->hw, base, op);
 }
 
+static void __setup_dspp_memcol(struct sde_hw_dspp *ctx,
+		enum sde_memcolor_type type,
+		struct drm_msm_memcol *memcolor)
+{
+	u32 addr = 0, offset = 0, idx = 0;
+	u32 hold = 0, local_hold = 0, hold_shift = 0;
+
+	switch (type) {
+	case MEMCOLOR_SKIN:
+		idx = 0;
+		break;
+	case MEMCOLOR_SKY:
+		idx = 1;
+		break;
+	case MEMCOLOR_FOLIAGE:
+		idx = 2;
+		break;
+	default:
+		DRM_ERROR("Invalid memory color type %d\n", type);
+		return;
+	}
+
+	offset = DSPP_MEMCOL_PWL0_OFF + (idx * DSPP_MEMCOL_SIZE0);
+	addr = ctx->cap->sblk->memcolor.base + offset;
+	hold_shift = idx * DSPP_MEMCOL_HOLD_SIZE;
+
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p0);
+	addr += 4;
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p1);
+	addr += 4;
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->hue_region);
+	addr += 4;
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->sat_region);
+	addr += 4;
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->val_region);
+
+	offset = DSPP_MEMCOL_PWL2_OFF + (idx * DSPP_MEMCOL_SIZE1);
+	addr = ctx->cap->sblk->memcolor.base + offset;
+
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->color_adjust_p2);
+	addr += 4;
+	SDE_REG_WRITE(&ctx->hw, addr, memcolor->blend_gain);
+
+	addr = ctx->cap->sblk->hsic.base + DSPP_PA_PWL_HOLD_OFF;
+	hold = SDE_REG_READ(&ctx->hw, addr);
+	local_hold = ((memcolor->sat_hold & REG_MASK(2)) << hold_shift);
+	local_hold |=
+		((memcolor->val_hold & REG_MASK(2)) << (hold_shift + 2));
+	hold &= ~REG_MASK_SHIFT(4, hold_shift);
+	hold |= local_hold;
+	SDE_REG_WRITE(&ctx->hw, addr, hold);
+}
+
+void sde_setup_dspp_memcol_skin_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_memcol *memcolor;
+	u32 opcode = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable memcolor skin feature\n");
+		opcode &= ~(DSPP_OP_PA_SKIN_EN);
+		if (PA_DSPP_DISABLE_REQUIRED(opcode))
+			opcode &= ~DSPP_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_memcol));
+		return;
+	}
+
+	memcolor = hw_cfg->payload;
+
+	__setup_dspp_memcol(ctx, MEMCOLOR_SKIN, memcolor);
+
+	opcode |= (DSPP_OP_PA_SKIN_EN | DSPP_OP_PA_EN);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_sky_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_memcol *memcolor;
+	u32 opcode = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable memcolor sky feature\n");
+		opcode &= ~(DSPP_OP_PA_SKY_EN);
+		if (PA_DSPP_DISABLE_REQUIRED(opcode))
+			opcode &= ~DSPP_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_memcol));
+		return;
+	}
+
+	memcolor = hw_cfg->payload;
+
+	__setup_dspp_memcol(ctx, MEMCOLOR_SKY, memcolor);
+
+	opcode |= (DSPP_OP_PA_SKY_EN | DSPP_OP_PA_EN);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_foliage_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_memcol *memcolor;
+	u32 opcode = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable memcolor foliage feature\n");
+		opcode &= ~(DSPP_OP_PA_FOL_EN);
+		if (PA_DSPP_DISABLE_REQUIRED(opcode))
+			opcode &= ~DSPP_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_memcol));
+		return;
+	}
+
+	memcolor = hw_cfg->payload;
+
+	__setup_dspp_memcol(ctx, MEMCOLOR_FOLIAGE, memcolor);
+
+	opcode |= (DSPP_OP_PA_FOL_EN | DSPP_OP_PA_EN);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void sde_setup_dspp_memcol_prot_v17(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct drm_msm_memcol *memcolor;
+	u32 opcode = 0, local_opcode = 0;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable memcolor prot feature\n");
+		opcode &= ~(DSPP_MEMCOL_PROT_MASK);
+		if (PA_DSPP_DISABLE_REQUIRED(opcode))
+			opcode &= ~DSPP_OP_PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_memcol)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_memcol));
+		return;
+	}
+
+	memcolor = hw_cfg->payload;
+
+	if (memcolor->prot_flags) {
+		if (memcolor->prot_flags & MEMCOL_PROT_HUE)
+			local_opcode |= DSPP_MEMCOL_PROT_HUE_EN;
+		if (memcolor->prot_flags & MEMCOL_PROT_SAT)
+			local_opcode |= DSPP_MEMCOL_PROT_SAT_EN;
+		if (memcolor->prot_flags & MEMCOL_PROT_VAL)
+			local_opcode |= DSPP_MEMCOL_PROT_VAL_EN;
+		if (memcolor->prot_flags & MEMCOL_PROT_CONT)
+			local_opcode |= DSPP_MEMCOL_PROT_CONT_EN;
+		if (memcolor->prot_flags & MEMCOL_PROT_SIXZONE)
+			local_opcode |= DSPP_MEMCOL_PROT_SIXZONE_EN;
+		if (memcolor->prot_flags & MEMCOL_PROT_BLEND)
+			local_opcode |= DSPP_MEMCOL_PROT_BLEND_EN;
+	}
+
+	if (local_opcode) {
+		local_opcode |= DSPP_OP_PA_EN;
+		opcode &= ~(DSPP_MEMCOL_PROT_MASK);
+		opcode |= local_opcode;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+	}
+}
+
 void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg)
 {
 	struct sde_hw_cp_cfg *hw_cfg = cfg;
@@ -458,6 +804,62 @@
 	SDE_REG_WRITE(&ctx->hw, base, op_mode);
 }
 
+void sde_setup_dspp_pa_vlut_v1_8(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_pa_vlut *payload = NULL;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct sde_hw_ctl *ctl = NULL;
+	u32 vlut_base, pa_hist_base;
+	u32 ctrl_off, swap_off;
+	u32 tmp = 0;
+	int i = 0, j = 0;
+	u32 flush_mask = 0;
+
+	if (!ctx) {
+		DRM_ERROR("invalid input parameter NULL ctx\n");
+		return;
+	}
+
+	if (!hw_cfg || (hw_cfg->payload && hw_cfg->len !=
+			sizeof(struct drm_msm_pa_vlut))) {
+		DRM_ERROR("hw %pK payload %pK payloadsize %d exp size %zd\n",
+			  hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL),
+			  ((hw_cfg) ? hw_cfg->len : 0),
+			  sizeof(struct drm_msm_pa_vlut));
+		return;
+	}
+
+	ctl = hw_cfg->ctl;
+	vlut_base = ctx->cap->sblk->vlut.base;
+	pa_hist_base = ctx->cap->sblk->hist.base;
+	ctrl_off = pa_hist_base + PA_LUTV_DSPP_CTRL_OFF;
+	swap_off = pa_hist_base + PA_LUTV_DSPP_SWAP_OFF;
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("Disable vlut feature\n");
+		SDE_REG_WRITE(&ctx->hw, ctrl_off, 0);
+		goto exit;
+	}
+
+	payload = hw_cfg->payload;
+	DRM_DEBUG_DRIVER("Enable vlut feature flags %llx\n", payload->flags);
+	for (i = 0, j = 0; i < ARRAY_SIZE(payload->val); i += 2, j += 4) {
+		tmp = (payload->val[i] & REG_MASK(10)) |
+			((payload->val[i + 1] & REG_MASK(10)) << 16);
+		SDE_REG_WRITE(&ctx->hw, (vlut_base + j), tmp);
+	}
+	SDE_REG_WRITE(&ctx->hw, ctrl_off, 1);
+	SDE_REG_WRITE(&ctx->hw, swap_off, 1);
+
+exit:
+	/* update flush bit */
+	if (ctl && ctl->ops.get_bitmask_dspp_pavlut) {
+		ctl->ops.get_bitmask_dspp_pavlut(ctl, &flush_mask, ctx->idx);
+		if (ctl->ops.update_pending_flush)
+			ctl->ops.update_pending_flush(ctl, flush_mask);
+	}
+}
+
 void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg)
 {
 	struct drm_msm_pgc_lut *payload = NULL;
@@ -503,3 +905,69 @@
 	i = BIT(0) | ((payload->flags & PGC_8B_ROUND) ? BIT(1) : 0);
 	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, i);
 }
+
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 base, offset;
+	u32 op_mode;
+	bool feature_enabled;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	feature_enabled = *(bool *)cfg;
+	base = ctx->cap->sblk->hist.base;
+	offset = base + PA_HIST_CTRL_DSPP_OFF;
+
+	op_mode = SDE_REG_READ(&ctx->hw, base);
+	if (!feature_enabled) {
+		op_mode &= ~DSPP_OP_PA_HIST_EN;
+		if (PA_DSPP_DISABLE_REQUIRED(op_mode))
+			op_mode &= ~DSPP_OP_PA_EN;
+	} else {
+		op_mode |= DSPP_OP_PA_HIST_EN | DSPP_OP_PA_EN;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, offset, 0);
+	SDE_REG_WRITE(&ctx->hw, base, op_mode);
+}
+
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_hist *hist_data;
+	u32 offset, offset_ctl;
+	u32 i;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid parameters ctx %pK cfg %pK", ctx, cfg);
+		return;
+	}
+
+	hist_data = (struct drm_msm_hist *)cfg;
+	offset = ctx->cap->sblk->hist.base + PA_HIST_DATA_DSPP_OFF;
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	for (i = 0; i < HIST_V_SIZE; i++)
+		hist_data->data[i] = SDE_REG_READ(&ctx->hw, offset + i * 4) &
+					REG_MASK(24);
+
+	/* unlock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 0);
+}
+
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	u32 offset_ctl;
+
+	if (!ctx) {
+		DRM_ERROR("invalid parameters ctx %pK", ctx);
+		return;
+	}
+
+	offset_ctl = ctx->cap->sblk->hist.base + PA_HIST_CTRL_DSPP_OFF;
+
+	/* lock hist buffer */
+	SDE_REG_WRITE(&ctx->hw, offset_ctl, 1);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 25e446b7..3c783ee 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -62,11 +62,46 @@
 void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
- * sde_setup_dspp_pa_hue_v1_7 - setup DSPP hue feature in v1.7 hardware
+ * sde_setup_dspp_pa_hsic_v17 - setup DSPP hsic feature in v1.7 hardware
  * @ctx: Pointer to DSPP context
- * @cfg: Pointer to hue data
+ * @cfg: Pointer to hsic data
  */
-void sde_setup_dspp_pa_hue_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+void sde_setup_dspp_pa_hsic_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_skin_v17 - setup DSPP memcol skin in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_skin_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_sky_v17 - setup DSPP memcol sky in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_sky_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_foliage_v17 - setup DSPP memcol fol in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_foliage_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_memcol_prot_v17 - setup DSPP memcol prot in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to memcolor config data
+ */
+void sde_setup_dspp_memcol_prot_v17(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_setup_dspp_sixzone_v17 - setup DSPP sixzone feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to sixzone data
+ */
+void sde_setup_dspp_sixzone_v17(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
  * sde_setup_dspp_pa_vlut_v1_7 - setup DSPP PA vLUT feature in v1.7 hardware
@@ -76,10 +111,36 @@
 void sde_setup_dspp_pa_vlut_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
+ * sde_setup_dspp_pa_vlut_v1_8 - setup DSPP PA vLUT feature in v1.8 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to vLUT data
+ */
+void sde_setup_dspp_pa_vlut_v1_8(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
  * sde_setup_dspp_gc_v1_7 - setup DSPP gc feature in v1.7 hardware
  * @ctx: Pointer to DSPP context
  * @cfg: Pointer to gc data
  */
 void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
+/**
+ * sde_setup_dspp_hist_v1_7 - setup DSPP histogram feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram control data
+ */
+void sde_setup_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_read_dspp_hist_v1_7 - read DSPP histogram data in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to histogram data
+ */
+void sde_read_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * sde_lock_dspp_hist_v1_7 - lock DSPP histogram buffer in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ */
+void sde_lock_dspp_hist_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 6b1f33d..426ecf1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -30,6 +30,7 @@
 #define   CTL_START                     0x01C
 #define   CTL_PREPARE                   0x0d0
 #define   CTL_SW_RESET                  0x030
+#define   CTL_SW_RESET_OVERRIDE         0x060
 #define   CTL_LAYER_EXTN_OFFSET         0x40
 #define   CTL_ROT_TOP                   0x0C0
 #define   CTL_ROT_FLUSH                 0x0C4
@@ -39,7 +40,48 @@
 #define CTL_FLUSH_MASK_ROT              BIT(27)
 #define CTL_FLUSH_MASK_CTL              BIT(17)
 
-#define SDE_REG_RESET_TIMEOUT_COUNT    20
+#define SDE_REG_RESET_TIMEOUT_US        2000
+
+#define MDP_CTL_FLUSH(n) ((0x2000) + (0x200*n) + CTL_FLUSH)
+#define CTL_FLUSH_LM_BIT(n) (6 + n)
+#define CTL_TOP_LM_OFFSET(index, lm) (0x2000 + (0x200 * index) + (lm * 0x4))
+
+int sde_unstage_pipe_for_cont_splash(struct sde_splash_data *data,
+		void __iomem *mmio)
+{
+	int i, j;
+	u32 op_mode;
+
+	if (!data) {
+		pr_err("invalid splash data\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < data->ctl_top_cnt; i++) {
+		struct ctl_top *top = &data->top[i];
+		u8 ctl_id = data->ctl_ids[i] - CTL_0;
+		u32 regval = 0;
+
+		op_mode = readl_relaxed(mmio + MDP_CTL_FLUSH(ctl_id));
+
+		/* Set border fill*/
+		regval |= CTL_MIXER_BORDER_OUT;
+
+		for (j = 0; j < top->ctl_lm_cnt; j++) {
+			u8 lm_id = top->lm[j].lm_id - LM_0;
+
+			writel_relaxed(regval,
+			mmio + CTL_TOP_LM_OFFSET(ctl_id, lm_id));
+
+			op_mode |= BIT(CTL_FLUSH_LM_BIT(lm_id));
+		}
+		op_mode |= CTL_FLUSH_MASK_CTL;
+
+		writel_relaxed(op_mode, mmio + MDP_CTL_FLUSH(ctl_id));
+	}
+	return 0;
+
+}
 
 static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl,
 		struct sde_mdss_cfg *m,
@@ -82,6 +124,11 @@
 	SDE_REG_WRITE(&ctx->hw, CTL_START, 0x1);
 }
 
+static inline int sde_hw_ctl_get_start_state(struct sde_hw_ctl *ctx)
+{
+	return SDE_REG_READ(&ctx->hw, CTL_START);
+}
+
 static inline void sde_hw_ctl_trigger_pending(struct sde_hw_ctl *ctx)
 {
 	SDE_REG_WRITE(&ctx->hw, CTL_PREPARE, 0x1);
@@ -89,6 +136,11 @@
 
 static inline void sde_hw_ctl_trigger_rot_start(struct sde_hw_ctl *ctx)
 {
+	/* ROT flush bit is latched during ROT start, so set it first */
+	if (CTL_FLUSH_MASK_ROT & ctx->pending_flush_mask) {
+		ctx->pending_flush_mask &= ~CTL_FLUSH_MASK_ROT;
+		SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, CTL_FLUSH_MASK_ROT);
+	}
 	SDE_REG_WRITE(&ctx->hw, CTL_ROT_START, BIT(0));
 }
 
@@ -235,6 +287,22 @@
 	return 0;
 }
 
+static inline int sde_hw_ctl_get_bitmask_dspp_pavlut(struct sde_hw_ctl *ctx,
+		u32 *flushbits, enum sde_dspp dspp)
+{
+	switch (dspp) {
+	case DSPP_0:
+		*flushbits |= BIT(3);
+		break;
+	case DSPP_1:
+		*flushbits |= BIT(4);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static inline int sde_hw_ctl_get_bitmask_intf(struct sde_hw_ctl *ctx,
 		u32 *flushbits, enum sde_intf intf)
 {
@@ -298,14 +366,13 @@
 	return 0;
 }
 
-static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 timeout_us)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
+	ktime_t timeout;
 	u32 status;
 
-	/* protect to do at least one iteration */
-	if (!count)
-		count = 1;
+	timeout = ktime_add_us(ktime_get(), timeout_us);
 
 	/*
 	 * it takes around 30us to have mdp finish resetting its ctl path
@@ -313,10 +380,10 @@
 	 */
 	do {
 		status = SDE_REG_READ(c, CTL_SW_RESET);
-		status &= 0x01;
+		status &= 0x1;
 		if (status)
 			usleep_range(20, 50);
-	} while (status && --count > 0);
+	} while (status && ktime_compare_safe(ktime_get(), timeout) < 0);
 
 	return status;
 }
@@ -325,14 +392,23 @@
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
 
-	pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
+	pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx - CTL_0);
 	SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
-	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US))
 		return -EINVAL;
 
 	return 0;
 }
 
+static void sde_hw_ctl_hard_reset(struct sde_hw_ctl *ctx, bool enable)
+{
+	struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+	pr_debug("hw ctl hard reset for ctl:%d, %d\n",
+			ctx->idx - CTL_0, enable);
+	SDE_REG_WRITE(c, CTL_SW_RESET_OVERRIDE, enable);
+}
+
 static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
@@ -344,7 +420,7 @@
 		return 0;
 
 	pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
-	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+	if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_US)) {
 		pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
 		return -EINVAL;
 	}
@@ -358,10 +434,12 @@
 	int i;
 
 	for (i = 0; i < ctx->mixer_count; i++) {
-		SDE_REG_WRITE(c, CTL_LAYER(LM_0 + i), 0);
-		SDE_REG_WRITE(c, CTL_LAYER_EXT(LM_0 + i), 0);
-		SDE_REG_WRITE(c, CTL_LAYER_EXT2(LM_0 + i), 0);
-		SDE_REG_WRITE(c, CTL_LAYER_EXT3(LM_0 + i), 0);
+		int mixer_id = ctx->mixer_hw_caps[i].id;
+
+		SDE_REG_WRITE(c, CTL_LAYER(mixer_id), 0);
+		SDE_REG_WRITE(c, CTL_LAYER_EXT(mixer_id), 0);
+		SDE_REG_WRITE(c, CTL_LAYER_EXT2(mixer_id), 0);
+		SDE_REG_WRITE(c, CTL_LAYER_EXT3(mixer_id), 0);
 	}
 }
 
@@ -543,12 +621,13 @@
 	SDE_REG_WRITE(c, CTL_ROT_TOP, val);
 }
 
-static void sde_hw_reg_dma_flush(struct sde_hw_ctl *ctx)
+static void sde_hw_reg_dma_flush(struct sde_hw_ctl *ctx, bool blocking)
 {
 	struct sde_hw_reg_dma_ops *ops = sde_reg_dma_get_ops();
 
 	if (ops && ops->last_command)
-		ops->last_command(ctx, DMA_CTL_QUEUE0);
+		ops->last_command(ctx, DMA_CTL_QUEUE0,
+		    (blocking ? REG_DMA_WAIT4_COMP : REG_DMA_NOWAIT));
 }
 
 static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
@@ -563,16 +642,19 @@
 	ops->trigger_pending = sde_hw_ctl_trigger_pending;
 	ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
 	ops->reset = sde_hw_ctl_reset_control;
+	ops->hard_reset = sde_hw_ctl_hard_reset;
 	ops->wait_reset_status = sde_hw_ctl_wait_reset_status;
 	ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages;
 	ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
 	ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp;
 	ops->get_bitmask_mixer = sde_hw_ctl_get_bitmask_mixer;
 	ops->get_bitmask_dspp = sde_hw_ctl_get_bitmask_dspp;
+	ops->get_bitmask_dspp_pavlut = sde_hw_ctl_get_bitmask_dspp_pavlut;
 	ops->get_bitmask_intf = sde_hw_ctl_get_bitmask_intf;
 	ops->get_bitmask_cdm = sde_hw_ctl_get_bitmask_cdm;
 	ops->get_bitmask_wb = sde_hw_ctl_get_bitmask_wb;
 	ops->reg_dma_flush = sde_hw_reg_dma_flush;
+	ops->get_start_state = sde_hw_ctl_get_start_state;
 
 	if (cap & BIT(SDE_CTL_SBUF)) {
 		ops->get_bitmask_rot = sde_hw_ctl_get_bitmask_rot;
@@ -581,6 +663,27 @@
 	}
 };
 
+#define CTL_BASE_OFFSET	0x2000
+#define CTL_TOP_OFFSET(index) (CTL_BASE_OFFSET + (0x200 * (index)) + CTL_TOP)
+
+void sde_get_ctl_top_for_cont_splash(void __iomem *mmio,
+		struct ctl_top *top, int index)
+{
+	if (!mmio || !top) {
+		SDE_ERROR("invalid input parameters\n");
+		return;
+	}
+
+	top->value = readl_relaxed(mmio + CTL_TOP_OFFSET(index));
+	top->intf_sel = (top->value >> 4) & 0xf;
+	top->pp_sel = (top->value >> 8) & 0x7;
+	top->dspp_sel = (top->value >> 11) & 0x3;
+	top->mode_sel = (top->value >> 17) & 0x1;
+
+	SDE_DEBUG("ctl[%d]_top->0x%x,pp_sel=0x%x,dspp_sel=0x%x,intf_sel=0x%x\n",
+	       index, top->value, top->pp_sel, top->dspp_sel, top->intf_sel);
+}
+
 static struct sde_hw_blk_ops sde_hw_ops = {
 	.start = NULL,
 	.stop = NULL,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 5d3ced3..f8594da 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -152,6 +152,13 @@
 
 	int (*reset)(struct sde_hw_ctl *c);
 
+	/**
+	 * hard_reset - force reset on ctl_path
+	 * @ctx    : ctl path ctx pointer
+	 * @enable : whether to enable/disable hard reset
+	 */
+	void (*hard_reset)(struct sde_hw_ctl *c, bool enable);
+
 	/*
 	 * wait_reset_status - checks ctl reset status
 	 * @ctx       : ctl path ctx pointer
@@ -173,6 +180,10 @@
 		u32 *flushbits,
 		enum sde_dspp blk);
 
+	int (*get_bitmask_dspp_pavlut)(struct sde_hw_ctl *ctx,
+		u32 *flushbits,
+		enum sde_dspp blk);
+
 	int (*get_bitmask_intf)(struct sde_hw_ctl *ctx,
 		u32 *flushbits,
 		enum sde_intf blk);
@@ -210,9 +221,16 @@
 	/**
 	 * Flush the reg dma by sending last command.
 	 * @ctx       : ctl path ctx pointer
+	 * @blocking  : if set to true api will block until flush is done
 	 */
-	void (*reg_dma_flush)(struct sde_hw_ctl *ctx);
+	void (*reg_dma_flush)(struct sde_hw_ctl *ctx, bool blocking);
 
+	/**
+	 * check if ctl start trigger state to confirm the frame pending
+	 * status
+	 * @ctx       : ctl path ctx pointer
+	 */
+	int (*get_start_state)(struct sde_hw_ctl *ctx);
 };
 
 /**
@@ -242,6 +260,24 @@
 };
 
 /**
+ * sde_unstage_pipe_for_cont_splash - Unstage pipes for continuous splash
+ * @data: pointer to sde splash data
+ * @mmio: mapped register io address of MDP
+ * @return: error code
+ */
+int sde_unstage_pipe_for_cont_splash(struct sde_splash_data *data,
+		void __iomem *mmio);
+
+/**
+ * sde_get_ctl_top_for_cont_splash - retrieve the current LM blocks
+ * @mmio: mapped register io address of MDP
+ * @top: pointer to the current "ctl_top" structure thats needs update
+ * @index: ctl_top index
+ */
+void sde_get_ctl_top_for_cont_splash(void __iomem *mmio,
+		struct ctl_top *top, int index);
+
+/**
  * sde_hw_ctl - convert base object sde_hw_base to container
  * @hw: Pointer to base hardware block
  * return: Pointer to hardware block container
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ds.c b/drivers/gpu/drm/msm/sde/sde_hw_ds.c
new file mode 100644
index 0000000..e37a7d6
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ds.c
@@ -0,0 +1,149 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sde_hw_ds.h"
+#include "sde_formats.h"
+#include "sde_dbg.h"
+#include "sde_kms.h"
+
+/* Destination scaler TOP registers */
+#define DEST_SCALER_OP_MODE     0x00
+#define DEST_SCALER_HW_VERSION  0x10
+
+static void sde_hw_ds_setup_opmode(struct sde_hw_ds *hw_ds,
+				u32 op_mode)
+{
+	struct sde_hw_blk_reg_map *hw = &hw_ds->hw;
+
+	SDE_REG_WRITE(hw, DEST_SCALER_OP_MODE, op_mode);
+}
+
+static void sde_hw_ds_setup_scaler3(struct sde_hw_ds *hw_ds,
+			void *scaler_cfg, void *scaler_lut_cfg)
+{
+	struct sde_hw_scaler3_cfg *scl3_cfg = scaler_cfg;
+	struct sde_hw_scaler3_lut_cfg *scl3_lut_cfg = scaler_lut_cfg;
+
+	if (!hw_ds || !hw_ds->scl || !scl3_cfg || !scl3_lut_cfg)
+		return;
+
+	/*
+	 * copy LUT values to scaler structure
+	 */
+	if (scl3_lut_cfg->is_configured) {
+		scl3_cfg->dir_lut = scl3_lut_cfg->dir_lut;
+		scl3_cfg->dir_len = scl3_lut_cfg->dir_len;
+		scl3_cfg->cir_lut = scl3_lut_cfg->cir_lut;
+		scl3_cfg->cir_len = scl3_lut_cfg->cir_len;
+		scl3_cfg->sep_lut = scl3_lut_cfg->sep_lut;
+		scl3_cfg->sep_len = scl3_lut_cfg->sep_len;
+	}
+
+	sde_hw_setup_scaler3(&hw_ds->hw, scl3_cfg,
+			 hw_ds->scl->base,
+			 hw_ds->scl->version,
+			 sde_get_sde_format(DRM_FORMAT_XBGR2101010));
+}
+
+static void _setup_ds_ops(struct sde_hw_ds_ops *ops, unsigned long features)
+{
+	ops->setup_opmode = sde_hw_ds_setup_opmode;
+
+	if (test_bit(SDE_SSPP_SCALER_QSEED3, &features))
+		ops->setup_scaler = sde_hw_ds_setup_scaler3;
+}
+
+static struct sde_ds_cfg *_ds_offset(enum sde_ds ds,
+		struct sde_mdss_cfg *m,
+		void __iomem *addr,
+		struct sde_hw_blk_reg_map *b)
+{
+	int i;
+
+	if (!m || !addr || !b)
+		return ERR_PTR(-EINVAL);
+
+	for (i = 0; i < m->ds_count; i++) {
+		if ((ds == m->ds[i].id) &&
+			 (m->ds[i].top)) {
+			b->base_off = addr;
+			b->blk_off = m->ds[i].top->base;
+			b->length = m->ds[i].top->len;
+			b->hwversion = m->hwversion;
+			b->log_mask = SDE_DBG_MASK_DS;
+			return &m->ds[i];
+		}
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+static struct sde_hw_blk_ops sde_hw_ops = {
+	.start = NULL,
+	.stop = NULL,
+};
+
+struct sde_hw_ds *sde_hw_ds_init(enum sde_ds idx,
+			void __iomem *addr,
+			struct sde_mdss_cfg *m)
+{
+	struct sde_hw_ds *hw_ds;
+	struct sde_ds_cfg *cfg;
+	int rc;
+
+	if (!addr || !m)
+		return ERR_PTR(-EINVAL);
+
+	hw_ds = kzalloc(sizeof(*hw_ds), GFP_KERNEL);
+	if (!hw_ds)
+		return ERR_PTR(-ENOMEM);
+
+	cfg = _ds_offset(idx, m, addr, &hw_ds->hw);
+	if (IS_ERR_OR_NULL(cfg)) {
+		SDE_ERROR("failed to get ds cfg\n");
+		kfree(hw_ds);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Assign ops */
+	hw_ds->idx = idx;
+	hw_ds->scl = cfg;
+	_setup_ds_ops(&hw_ds->ops, hw_ds->scl->features);
+
+	rc = sde_hw_blk_init(&hw_ds->base, SDE_HW_BLK_DS, idx, &sde_hw_ops);
+	if (rc) {
+		SDE_ERROR("failed to init hw blk %d\n", rc);
+		goto blk_init_error;
+	}
+
+	if (cfg->len) {
+		sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+				hw_ds->hw.blk_off + cfg->base,
+				hw_ds->hw.blk_off + cfg->base + cfg->len,
+				hw_ds->hw.xin_id);
+	}
+
+	return hw_ds;
+
+blk_init_error:
+	kzfree(hw_ds);
+
+	return ERR_PTR(rc);
+
+}
+
+void sde_hw_ds_destroy(struct sde_hw_ds *hw_ds)
+{
+	if (hw_ds)
+		sde_hw_blk_destroy(&hw_ds->base);
+	kfree(hw_ds);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ds.h b/drivers/gpu/drm/msm/sde/sde_hw_ds.h
new file mode 100644
index 0000000..6e97c5d
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ds.h
@@ -0,0 +1,109 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SDE_HW_DS_H
+#define _SDE_HW_DS_H
+
+#include "sde_hw_mdss.h"
+#include "sde_hw_util.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_blk.h"
+
+struct sde_hw_ds;
+
+/* Destination Scaler DUAL mode overfetch pixel count */
+#define SDE_DS_OVERFETCH_SIZE 5
+
+/* Destination scaler DUAL mode operation bit */
+#define SDE_DS_OP_MODE_DUAL BIT(16)
+
+/* struct sde_hw_ds_cfg - destination scaler config
+ * @idx          : DS selection index
+ * @flags        : Flag to switch between mode for DS
+ * @lm_width     : Layer mixer width configuration
+ * @lm_heigh     : Layer mixer height configuration
+ * @scl3_cfg     : Configuration data for scaler
+ */
+struct sde_hw_ds_cfg {
+	u32 idx;
+	int flags;
+	u32 lm_width;
+	u32 lm_height;
+	struct sde_hw_scaler3_cfg scl3_cfg;
+};
+
+/**
+ * struct sde_hw_ds_ops - interface to the destination scaler
+ * hardware driver functions
+ * Caller must call the init function to get the ds context for each ds
+ * Assumption is these functions will be called after clocks are enabled
+ */
+struct sde_hw_ds_ops {
+	/**
+	 * setup_opmode - destination scaler op mode setup
+	 * @hw_ds   : Pointer to ds context
+	 * @op_mode : Op mode configuration
+	 */
+	void (*setup_opmode)(struct sde_hw_ds *hw_ds,
+				u32 op_mode);
+
+	/**
+	 * setup_scaler - destination scaler block setup
+	 * @hw_ds          : Pointer to ds context
+	 * @scaler_cfg     : Pointer to scaler data
+	 * @scaler_lut_cfg : Pointer to scaler lut
+	 */
+	void (*setup_scaler)(struct sde_hw_ds *hw_ds,
+				void *scaler_cfg,
+				void *scaler_lut_cfg);
+
+};
+
+/**
+ * struct sde_hw_ds - destination scaler description
+ * @base : Hardware block base structure
+ * @hw   : Block hardware details
+ * @idx  : Destination scaler index
+ * @scl  : Pointer to
+ *          - scaler offset relative to top offset
+ *          - capabilities
+ * @ops  : Pointer to operations for this DS
+ */
+struct sde_hw_ds {
+	struct sde_hw_blk base;
+	struct sde_hw_blk_reg_map hw;
+	enum sde_ds idx;
+	const struct sde_ds_cfg *scl;
+	struct sde_hw_ds_ops ops;
+};
+
+/**
+ * sde_hw_ds_init - initializes the destination scaler
+ * hw driver object and should be called once before
+ * accessing every destination scaler
+ * @idx : DS index for which driver object is required
+ * @addr: Mapped register io address of MDP
+ * @m   : MDSS catalog information
+ * @Return: pointer to structure or ERR_PTR
+ */
+struct sde_hw_ds *sde_hw_ds_init(enum sde_ds idx,
+			void __iomem *addr,
+			struct sde_mdss_cfg *m);
+
+/**
+ * sde_hw_ds_destroy - destroys destination scaler
+ * driver context
+ * @hw_ds:   Pointer to DS context
+ */
+void sde_hw_ds_destroy(struct sde_hw_ds *hw_ds);
+
+#endif /*_SDE_HW_DS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 5b3f51e..b268e8f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -71,8 +71,28 @@
 			break;
 		case SDE_DSPP_HSIC:
 			if (c->cap->sblk->hsic.version ==
-				(SDE_COLOR_PROCESS_VER(0x1, 0x7)))
-				c->ops.setup_hue = sde_setup_dspp_pa_hue_v1_7;
+				SDE_COLOR_PROCESS_VER(0x1, 0x7))
+				c->ops.setup_pa_hsic =
+					sde_setup_dspp_pa_hsic_v17;
+			break;
+		case SDE_DSPP_MEMCOLOR:
+			if (c->cap->sblk->memcolor.version ==
+				SDE_COLOR_PROCESS_VER(0x1, 0x7)) {
+				c->ops.setup_pa_memcol_skin =
+					sde_setup_dspp_memcol_skin_v17;
+				c->ops.setup_pa_memcol_sky =
+					sde_setup_dspp_memcol_sky_v17;
+				c->ops.setup_pa_memcol_foliage =
+					sde_setup_dspp_memcol_foliage_v17;
+				c->ops.setup_pa_memcol_prot =
+					sde_setup_dspp_memcol_prot_v17;
+			}
+			break;
+		case SDE_DSPP_SIXZONE:
+			if (c->cap->sblk->sixzone.version ==
+				SDE_COLOR_PROCESS_VER(0x1, 0x7))
+				c->ops.setup_sixzone =
+					sde_setup_dspp_sixzone_v17;
 			break;
 		case SDE_DSPP_VLUT:
 			if (c->cap->sblk->vlut.version ==
@@ -82,9 +102,23 @@
 			} else if (c->cap->sblk->vlut.version ==
 					(SDE_COLOR_PROCESS_VER(0x1, 0x8))) {
 				ret = reg_dmav1_init_dspp_op_v4(i, c->idx);
-				if (ret)
+				if (!ret)
 					c->ops.setup_vlut =
 					reg_dmav1_setup_dspp_vlutv18;
+				else
+					c->ops.setup_vlut =
+					sde_setup_dspp_pa_vlut_v1_8;
+			}
+			break;
+		case SDE_DSPP_HIST:
+			if (c->cap->sblk->hist.version ==
+				(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
+				c->ops.setup_histogram =
+				    sde_setup_dspp_hist_v1_7;
+				c->ops.read_histogram =
+				    sde_read_dspp_hist_v1_7;
+				c->ops.lock_histogram =
+				    sde_lock_dspp_hist_v1_7;
 			}
 			break;
 		case SDE_DSPP_GAMUT:
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 44b3831..2b64165 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -38,6 +38,13 @@
 	void (*read_histogram)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
+	 * lock_histogram - lock dspp histogram buffer
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*lock_histogram)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
 	 * setup_igc - update dspp igc
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
@@ -45,11 +52,11 @@
 	void (*setup_igc)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
-	 * setup_pa - setup dspp pa
+	 * setup_pa_hsic - setup dspp pa hsic
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
 	 */
-	void (*setup_pa)(struct sde_hw_dspp *dspp, void *cfg);
+	void (*setup_pa_hsic)(struct sde_hw_dspp *dspp, void *cfg);
 
 	/**
 	 * setup_pcc - setup dspp pcc
@@ -66,11 +73,32 @@
 	void (*setup_sharpening)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
-	 * setup_pa_memcolor - setup dspp memcolor
+	 * setup_pa_memcol_skin - setup dspp memcolor skin
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
 	 */
-	void (*setup_pa_memcolor)(struct sde_hw_dspp *ctx, void *cfg);
+	void (*setup_pa_memcol_skin)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_pa_memcol_sky - setup dspp memcolor sky
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_pa_memcol_sky)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_pa_memcol_foliage - setup dspp memcolor foliage
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_pa_memcol_foliage)(struct sde_hw_dspp *ctx, void *cfg);
+
+	/**
+	 * setup_pa_memcol_prot - setup dspp memcolor protection
+	 * @ctx: Pointer to dspp context
+	 * @cfg: Pointer to configuration
+	 */
+	void (*setup_pa_memcol_prot)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
 	 * setup_sixzone - setup dspp six zone
@@ -94,34 +122,6 @@
 	void (*setup_dither)(struct sde_hw_dspp *ctx, void *cfg);
 
 	/**
-	 * setup_hue - setup dspp PA hue
-	 * @ctx: Pointer to dspp context
-	 * @cfg: Pointer to configuration
-	 */
-	void (*setup_hue)(struct sde_hw_dspp *ctx, void *cfg);
-
-	/**
-	 * setup_sat - setup dspp PA saturation
-	 * @ctx: Pointer to dspp context
-	 * @cfg: Pointer to configuration
-	 */
-	void (*setup_sat)(struct sde_hw_dspp *ctx, void *cfg);
-
-	/**
-	 * setup_val - setup dspp PA value
-	 * @ctx: Pointer to dspp context
-	 * @cfg: Pointer to configuration
-	 */
-	void (*setup_val)(struct sde_hw_dspp *ctx, void *cfg);
-
-	/**
-	 * setup_cont - setup dspp PA contrast
-	 * @ctx: Pointer to dspp context
-	 * @cfg: Pointer to configuration
-	 */
-	void (*setup_cont)(struct sde_hw_dspp *ctx, void *cfg);
-
-	/**
 	 * setup_vlut - setup dspp PA VLUT
 	 * @ctx: Pointer to dspp context
 	 * @cfg: Pointer to configuration
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 35f1800..fd06c12 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -289,6 +289,18 @@
 	return SDE_REG_READ(c, INTF_MISR_SIGNATURE);
 }
 
+static u32 sde_hw_intf_get_line_count(struct sde_hw_intf *intf)
+{
+	struct sde_hw_blk_reg_map *c;
+
+	if (!intf)
+		return 0;
+
+	c = &intf->hw;
+
+	return SDE_REG_READ(c, INTF_LINE_COUNT);
+}
+
 static void _setup_intf_ops(struct sde_hw_intf_ops *ops,
 		unsigned long cap)
 {
@@ -298,6 +310,7 @@
 	ops->enable_timing = sde_hw_intf_enable_timing_engine;
 	ops->setup_misr = sde_hw_intf_setup_misr;
 	ops->collect_misr = sde_hw_intf_collect_misr;
+	ops->get_line_count = sde_hw_intf_get_line_count;
 	if (cap & BIT(SDE_INTF_ROT_START))
 		ops->setup_rot_start = sde_hw_intf_setup_rot_start;
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.h b/drivers/gpu/drm/msm/sde/sde_hw_intf.h
index 83e206d..89068bc 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.h
@@ -62,6 +62,7 @@
  * @ get_status: returns if timing engine is enabled or not
  * @ setup_misr: enables/disables MISR in HW register
  * @ collect_misr: reads and stores MISR data from HW register
+ * @ get_line_count: reads current vertical line counter
  */
 struct sde_hw_intf_ops {
 	void (*setup_timing_gen)(struct sde_hw_intf *intf,
@@ -84,6 +85,8 @@
 			bool enable, u32 frame_count);
 
 	u32 (*collect_misr)(struct sde_hw_intf *intf);
+
+	u32 (*get_line_count)(struct sde_hw_intf *intf);
 };
 
 struct sde_hw_intf {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 3d282ee..134db51 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -262,7 +262,7 @@
 		unsigned long features)
 {
 	ops->setup_mixer_out = sde_hw_lm_setup_out;
-	if (IS_SDM845_TARGET(m->hwversion))
+	if (IS_SDM845_TARGET(m->hwversion) || IS_SDM670_TARGET(m->hwversion))
 		ops->setup_blend_config = sde_hw_lm_setup_blend_config_sdm845;
 	else
 		ops->setup_blend_config = sde_hw_lm_setup_blend_config;
@@ -278,6 +278,43 @@
 	}
 };
 
+#define CTL_BASE_OFFSET	0x2000
+#define CTL_TOP_LM_OFFSET(index, lm)	\
+	(CTL_BASE_OFFSET + (0x200 * index) + (lm * 0x4))
+
+int sde_get_ctl_lm_for_cont_splash(void __iomem *mmio, int max_lm_cnt,
+		u8 lm_cnt, u8 *lm_ids, struct ctl_top *top, int index)
+{
+	int j;
+	struct sde_splash_lm_hw *lm;
+
+	if (!mmio || !top || !lm_ids) {
+		SDE_ERROR("invalid input parameters\n");
+		return 0;
+	}
+
+	lm = top->lm;
+	for (j = 0; j < max_lm_cnt; j++) {
+		lm[top->ctl_lm_cnt].lm_reg_value = readl_relaxed(mmio
+			      + CTL_TOP_LM_OFFSET(index, j));
+		SDE_DEBUG("ctl[%d]_top --> lm[%d]=0x%x, j=%d\n",
+			index, top->ctl_lm_cnt,
+			lm[top->ctl_lm_cnt].lm_reg_value, j);
+		SDE_DEBUG("lm_cnt = %d\n", lm_cnt);
+		if (lm[top->ctl_lm_cnt].lm_reg_value) {
+			lm[top->ctl_lm_cnt].ctl_id = index;
+			lm_ids[lm_cnt++] = j + LM_0;
+			lm[top->ctl_lm_cnt].lm_id = j + LM_0;
+			SDE_DEBUG("ctl_id=%d, lm[%d].lm_id = %d\n",
+				lm[top->ctl_lm_cnt].ctl_id,
+				top->ctl_lm_cnt,
+				lm[top->ctl_lm_cnt].lm_id);
+			top->ctl_lm_cnt++;
+		}
+	}
+	return top->ctl_lm_cnt;
+}
+
 static struct sde_hw_blk_ops sde_hw_ops = {
 	.start = NULL,
 	.stop = NULL,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.h b/drivers/gpu/drm/msm/sde/sde_hw_lm.h
index 8a146bd..a2307ec 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.h
@@ -117,6 +117,19 @@
 }
 
 /**
+ * sde_get_ctl_lm_for_cont_splash - retrieve the current LM blocks
+ * @mmio: mapped register io address of MDP
+ * @max_lm_cnt: number of LM blocks supported in the hw
+ * @lm_cnt: number of LM blocks already active
+ * @lm_ids: pointer to store the active LM block IDs
+ * @top: pointer to the current "ctl_top" structure
+ * @index: ctl_top index
+ * return: number of active LM blocks for this CTL block
+ */
+int sde_get_ctl_lm_for_cont_splash(void __iomem *mmio, int max_lm_cnt,
+		u8 lm_cnt, u8 *lm_ids, struct ctl_top *top, int index);
+
+/**
  * sde_hw_lm_init(): Initializes the mixer hw driver object.
  * should be called once before accessing every mixer.
  * @idx:  mixer index for which driver object is required
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index f07f5ed..3125ebf 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -100,6 +100,7 @@
 	SDE_HW_BLK_SSPP,
 	SDE_HW_BLK_LM,
 	SDE_HW_BLK_DSPP,
+	SDE_HW_BLK_DS,
 	SDE_HW_BLK_CTL,
 	SDE_HW_BLK_CDM,
 	SDE_HW_BLK_PINGPONG,
@@ -176,6 +177,13 @@
 	DSPP_MAX
 };
 
+enum sde_ds {
+	DS_TOP,
+	DS_0,
+	DS_1,
+	DS_MAX
+};
+
 enum sde_ctl {
 	CTL_0 = 1,
 	CTL_1,
@@ -489,6 +497,7 @@
 #define SDE_DBG_MASK_VBIF     (1 << 10)
 #define SDE_DBG_MASK_DSC      (1 << 11)
 #define SDE_DBG_MASK_ROT      (1 << 12)
+#define SDE_DBG_MASK_DS       (1 << 13)
 
 /**
  * struct sde_hw_cp_cfg: hardware dspp/lm feature payload.
@@ -526,4 +535,64 @@
 	struct sde_rect rect;
 };
 
+/**
+ * struct sde_splash_lm_hw - Struct contains LM block properties
+ * @lm_id:	stores the current LM ID
+ * @ctl_id:	stores the current CTL ID associated with the LM.
+ * @lm_reg_value:Store the LM block register value
+ */
+struct sde_splash_lm_hw {
+	u8 lm_id;
+	u8 ctl_id;
+	u32 lm_reg_value;
+};
+
+/**
+ * struct ctl_top - Struct contains CTL block properties
+ * @value:	Store the CTL block register value
+ * @mode_sel:	stores the mode selected in the CTL block
+ * @dspp_sel:	stores the dspp selected in the CTL block
+ * @pp_sel:	stores the pp selected in the CTL block
+ * @intf_sel:	stores the intf selected in the CTL block
+ * @lm:		Pointer to store the list of LMs in the CTL block
+ * @ctl_lm_cnt:	stores the active number of MDSS "LM" blocks in the CTL block
+ */
+struct ctl_top {
+	u32 value;
+	u8 mode_sel;
+	u8 dspp_sel;
+	u8 pp_sel;
+	u8 intf_sel;
+	struct sde_splash_lm_hw lm[LM_MAX - LM_0];
+	u8 ctl_lm_cnt;
+};
+
+/**
+ * struct sde_splash_data - Struct contains details of continuous splash
+ *	memory region and initial pipeline configuration.
+ * @smmu_handoff_pending:boolean to notify handoff from splash memory to smmu
+ * @splash_base:	Base address of continuous splash region reserved
+ *                      by bootloader
+ * @splash_size:	Size of continuous splash region
+ * @top:	struct ctl_top objects
+ * @ctl_ids:	stores the valid MDSS ctl block ids for the current mode
+ * @lm_ids:	stores the valid MDSS layer mixer block ids for the current mode
+ * @dsc_ids:	stores the valid MDSS DSC block ids for the current mode
+ * @ctl_top_cnt:stores the active number of MDSS "top" blks of the current mode
+ * @lm_cnt:	stores the active number of MDSS "LM" blks for the current mode
+ * @dsc_cnt:	stores the active number of MDSS "dsc" blks for the current mode
+ */
+struct sde_splash_data {
+	bool smmu_handoff_pending;
+	unsigned long splash_base;
+	u32 splash_size;
+	struct ctl_top top[CTL_MAX - CTL_0];
+	u8 ctl_ids[CTL_MAX - CTL_0];
+	u8 lm_ids[LM_MAX - LM_0];
+	u8 dsc_ids[DSC_MAX - DSC_0];
+	u8 ctl_top_cnt;
+	u8 lm_cnt;
+	u8 dsc_cnt;
+};
+
 #endif  /* _SDE_HW_MDSS_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index ff796f7..050e21b 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -165,11 +165,16 @@
 static void sde_hw_pp_dsc_disable(struct sde_hw_pingpong *pp)
 {
 	struct sde_hw_blk_reg_map *c;
+	u32 data;
 
 	if (!pp)
 		return;
 	c = &pp->hw;
 
+	data = SDE_REG_READ(c, PP_DCE_DATA_OUT_SWAP);
+	data &= ~BIT(18); /* disable endian flip */
+	SDE_REG_WRITE(c, PP_DCE_DATA_OUT_SWAP, data);
+
 	SDE_REG_WRITE(c, PP_DSC_MODE, 0);
 }
 
@@ -297,6 +302,33 @@
 	return 0;
 }
 
+static u32 sde_hw_pp_get_line_count(struct sde_hw_pingpong *pp)
+{
+	struct sde_hw_blk_reg_map *c = &pp->hw;
+	u32 height, init;
+	u32 line = 0xFFFF;
+
+	if (!pp)
+		return 0;
+	c = &pp->hw;
+
+	init = SDE_REG_READ(c, PP_VSYNC_INIT_VAL) & 0xFFFF;
+	height = SDE_REG_READ(c, PP_SYNC_CONFIG_HEIGHT) & 0xFFFF;
+
+	if (height < init)
+		goto line_count_exit;
+
+	line = SDE_REG_READ(c, PP_INT_COUNT_VAL) & 0xFFFF;
+
+	if (line < init)
+		line += (0xFFFF - init);
+	else
+		line -= init;
+
+line_count_exit:
+	return line;
+}
+
 static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops,
 	const struct sde_pingpong_cfg *hw_cap)
 {
@@ -312,6 +344,7 @@
 	ops->disable_dsc = sde_hw_pp_dsc_disable;
 	ops->get_autorefresh = sde_hw_pp_get_autorefresh_config;
 	ops->poll_timeout_wr_ptr = sde_hw_pp_poll_timeout_wr_ptr;
+	ops->get_line_count = sde_hw_pp_get_line_count;
 
 	version = SDE_COLOR_PROCESS_MAJOR(hw_cap->sblk->dither.version);
 	switch (version) {
@@ -324,6 +357,49 @@
 	}
 };
 
+#define MDP_PP_DSC_OFFSET(index) (0x71000 + (0x800 * index) + 0x0a0)
+#define MDP_PP_AUTOREFRESH_OFFSET(index) (0x71000 + (0x800 * index) + 0x030)
+
+int sde_get_pp_dsc_for_cont_splash(void __iomem *mmio,
+			int max_dsc_cnt, u8 *dsc_ids)
+{
+	int index;
+	int value, dsc_cnt = 0;
+
+	if (!mmio || !dsc_ids) {
+		SDE_ERROR("invalid input parameters\n");
+		return 0;
+	}
+
+	SDE_DEBUG("max_dsc_cnt = %d\n", max_dsc_cnt);
+	for (index = 0; index < max_dsc_cnt; index++) {
+		value = readl_relaxed(mmio
+			      + MDP_PP_DSC_OFFSET(index));
+		SDE_DEBUG("DSC[%d]=0x%x\n",
+					index, value);
+		SDE_DEBUG("dsc_cnt = %d\n", dsc_cnt);
+		if (value) {
+			dsc_ids[dsc_cnt] = index + DSC_0;
+			dsc_cnt++;
+		}
+		value = readl_relaxed(mmio
+			      + MDP_PP_AUTOREFRESH_OFFSET(index));
+		SDE_DEBUG("AUTOREFRESH[%d]=0x%x\n",
+					index, value);
+		if (value) {
+			SDE_DEBUG("Disabling autoreferesh\n");
+			writel_relaxed(0x0, mmio
+				+ MDP_PP_AUTOREFRESH_OFFSET(index));
+			/*
+			 * Wait for one frame update so that auto refresh
+			 * disable is through
+			 */
+			usleep_range(16000, 20000);
+		}
+	}
+	return dsc_cnt;
+}
+
 static struct sde_hw_blk_ops sde_hw_ops = {
 	.start = NULL,
 	.stop = NULL,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.h b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.h
index f0a2054..fef49f4 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.h
@@ -64,6 +64,7 @@
  *  @enable_dsc : enables DSC encoder
  *  @disable_dsc : disables DSC encoder
  *  @setup_dither : function to program the dither hw block
+ *  @get_line_count: obtain current vertical line counter
  */
 struct sde_hw_pingpong_ops {
 	/**
@@ -130,6 +131,11 @@
 	 * Program the dither hw block
 	 */
 	int (*setup_dither)(struct sde_hw_pingpong *pp, void *cfg, size_t len);
+
+	/**
+	 * Obtain current vertical line counter
+	 */
+	u32 (*get_line_count)(struct sde_hw_pingpong *pp);
 };
 
 struct sde_hw_pingpong {
@@ -155,6 +161,16 @@
 }
 
 /**
+ * sde_get_pp_dsc_for_cont_splash - retrieve the current dsc enabled blocks
+ * @mmio: mapped register io address of MDP
+ * @max_dsc_cnt: number of DSC blocks supported in the hw
+ * @dsc_ids: pointer to store the active DSC block IDs
+ * return: number of active DSC blocks
+ */
+int sde_get_pp_dsc_for_cont_splash(void __iomem *mmio,
+		int max_dsc_cnt, u8 *dsc_ids);
+
+/**
  * sde_hw_pingpong_init - initializes the pingpong driver for the passed
  *	pingpong idx.
  * @idx:  Pingpong index for which driver object is required
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
index 4487d78..cf65784 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
@@ -9,11 +9,13 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+#include <linux/iopoll.h>
 #include "sde_hw_mdss.h"
 #include "sde_hw_ctl.h"
 #include "sde_hw_reg_dma_v1.h"
 #include "msm_drv.h"
 #include "msm_mmu.h"
+#include "sde_dbg.h"
 
 #define GUARD_BYTES (BIT(8) - 1)
 #define ALIGNED_OFFSET (U32_MAX & ~(GUARD_BYTES))
@@ -93,6 +95,20 @@
 	[PCC] = GRP_DSPP_HW_BLK_SELECT,
 };
 
+static u32 ctl_trigger_done_mask[CTL_MAX][DMA_CTL_QUEUE_MAX] = {
+	[CTL_0][0] = BIT(16),
+	[CTL_0][1] = BIT(21),
+	[CTL_1][0] = BIT(17),
+	[CTL_1][1] = BIT(22),
+	[CTL_2][0] = BIT(18),
+	[CTL_2][1] = BIT(23),
+	[CTL_3][0] = BIT(19),
+	[CTL_3][1] = BIT(24),
+};
+
+static int reg_dma_int_status_off;
+static int reg_dma_clear_status_off;
+
 static int validate_dma_cfg(struct sde_reg_dma_setup_ops_cfg *cfg);
 static int validate_write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg);
 static int validate_write_reg(struct sde_reg_dma_setup_ops_cfg *cfg);
@@ -110,7 +126,8 @@
 static int setup_payload_v1(struct sde_reg_dma_setup_ops_cfg *cfg);
 static int kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg);
 static int reset_v1(struct sde_hw_ctl *ctl);
-static int last_cmd_v1(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q);
+static int last_cmd_v1(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_last_cmd_mode mode);
 static struct sde_reg_dma_buffer *alloc_reg_dma_buf_v1(u32 size);
 static int dealloc_reg_dma_v1(struct sde_reg_dma_buffer *lut_buf);
 
@@ -130,7 +147,7 @@
 	[REG_BLK_WRITE_MULTIPLE] = validate_write_multi_lut_reg,
 };
 
-static struct sde_reg_dma_buffer *last_cmd_buf;
+static struct sde_reg_dma_buffer *last_cmd_buf[CTL_MAX];
 
 static void get_decode_sel(unsigned long blk, u32 *decode_sel)
 {
@@ -466,6 +483,8 @@
 
 	SET_UP_REG_DMA_REG(hw, reg_dma);
 	SDE_REG_WRITE(&hw, REG_DMA_OP_MODE_OFF, BIT(0));
+	SDE_REG_WRITE(&hw, reg_dma_clear_status_off,
+		ctl_trigger_done_mask[cfg->ctl->idx][cfg->queue_select]);
 	SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx],
 			cfg->dma_buf->iova);
 	SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx] + 0x4,
@@ -479,17 +498,37 @@
 
 int init_v1(struct sde_hw_reg_dma *cfg)
 {
-	int i = 0;
+	int i = 0, rc = 0;
 
 	if (!cfg)
 		return -EINVAL;
 
 	reg_dma = cfg;
-	if (!last_cmd_buf) {
-		last_cmd_buf = alloc_reg_dma_buf_v1(REG_DMA_HEADERS_BUFFER_SZ);
-		if (IS_ERR_OR_NULL(last_cmd_buf))
-			return -EINVAL;
+	for (i = CTL_0; i < CTL_MAX; i++) {
+		if (!last_cmd_buf[i]) {
+			last_cmd_buf[i] =
+			    alloc_reg_dma_buf_v1(REG_DMA_HEADERS_BUFFER_SZ);
+			if (IS_ERR_OR_NULL(last_cmd_buf[i])) {
+				/*
+				 * This will allow reg dma to fall back to
+				 * AHB domain
+				 */
+				pr_info("Failed to allocate reg dma, ret:%lu\n",
+						PTR_ERR(last_cmd_buf[i]));
+				return 0;
+			}
+		}
 	}
+	if (rc) {
+		for (i = 0; i < CTL_MAX; i++) {
+			if (!last_cmd_buf[i])
+				continue;
+			dealloc_reg_dma_v1(last_cmd_buf[i]);
+			last_cmd_buf[i] = NULL;
+		}
+		return rc;
+	}
+
 	reg_dma->ops.check_support = check_support_v1;
 	reg_dma->ops.setup_payload = setup_payload_v1;
 	reg_dma->ops.kick_off = kick_off_v1;
@@ -503,6 +542,8 @@
 	for (i = CTL_1; i < ARRAY_SIZE(reg_dma_ctl_queue_off); i++)
 		reg_dma_ctl_queue_off[i] = reg_dma_ctl_queue_off[i - 1] +
 			(sizeof(u32) * 4);
+	reg_dma_int_status_off = 0x90;
+	reg_dma_clear_status_off = 0xa0;
 
 	return 0;
 }
@@ -582,7 +623,7 @@
 	return 0;
 }
 
-static void sde_reg_dma_aspace_cb(void *cb_data, bool attach)
+static void sde_reg_dma_aspace_cb_locked(void *cb_data, bool is_detach)
 {
 	struct sde_reg_dma_buffer *dma_buf = NULL;
 	struct msm_gem_address_space *aspace = NULL;
@@ -597,14 +638,23 @@
 	dma_buf = (struct sde_reg_dma_buffer *)cb_data;
 	aspace = dma_buf->aspace;
 
-	if (attach) {
-		rc = msm_gem_get_iova(dma_buf->buf, aspace, &dma_buf->iova);
+	if (is_detach) {
+		/* invalidate the stored iova */
+		dma_buf->iova = 0;
+
+		/* return the virtual address mapping */
+		msm_gem_put_vaddr_locked(dma_buf->buf);
+		msm_gem_vunmap(dma_buf->buf);
+
+	} else {
+		rc = msm_gem_get_iova_locked(dma_buf->buf, aspace,
+				&dma_buf->iova);
 		if (rc) {
 			DRM_ERROR("failed to get the iova rc %d\n", rc);
 			return;
 		}
 
-		dma_buf->vaddr = msm_gem_get_vaddr(dma_buf->buf);
+		dma_buf->vaddr = msm_gem_get_vaddr_locked(dma_buf->buf);
 		if (IS_ERR_OR_NULL(dma_buf->vaddr)) {
 			DRM_ERROR("failed to get va rc %d\n", rc);
 			return;
@@ -615,13 +665,6 @@
 		dma_buf->iova = dma_buf->iova + offset;
 		dma_buf->vaddr = (void *)(((u8 *)dma_buf->vaddr) + offset);
 		dma_buf->next_op_allowed = DECODE_SEL_OP;
-	} else {
-		/* invalidate the stored iova */
-		dma_buf->iova = 0;
-
-		/* return the virtual address mapping */
-		msm_gem_put_vaddr(dma_buf->buf);
-		msm_gem_vunmap(dma_buf->buf);
 	}
 }
 
@@ -661,7 +704,7 @@
 
 	/* register to aspace */
 	rc = msm_gem_address_space_register_cb(aspace,
-			sde_reg_dma_aspace_cb,
+			sde_reg_dma_aspace_cb_locked,
 			(void *)dma_buf);
 	if (rc) {
 		DRM_ERROR("failed to register callback %d", rc);
@@ -694,10 +737,12 @@
 put_iova:
 	msm_gem_put_iova(dma_buf->buf, aspace);
 free_aspace_cb:
-	msm_gem_address_space_unregister_cb(aspace, sde_reg_dma_aspace_cb,
-			dma_buf);
+	msm_gem_address_space_unregister_cb(aspace,
+			sde_reg_dma_aspace_cb_locked, dma_buf);
 free_gem:
+	mutex_lock(&reg_dma->drm_dev->struct_mutex);
 	msm_gem_free_object(dma_buf->buf);
+	mutex_unlock(&reg_dma->drm_dev->struct_mutex);
 fail:
 	kfree(dma_buf);
 	return ERR_PTR(rc);
@@ -713,7 +758,7 @@
 	if (dma_buf->buf) {
 		msm_gem_put_iova(dma_buf->buf, 0);
 		msm_gem_address_space_unregister_cb(dma_buf->aspace,
-				sde_reg_dma_aspace_cb, dma_buf);
+				sde_reg_dma_aspace_cb_locked, dma_buf);
 		mutex_lock(&reg_dma->drm_dev->struct_mutex);
 		msm_gem_free_object(dma_buf->buf);
 		mutex_unlock(&reg_dma->drm_dev->struct_mutex);
@@ -763,19 +808,28 @@
 	return 0;
 }
 
-static int last_cmd_v1(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q)
+static int last_cmd_v1(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_last_cmd_mode mode)
 {
 	struct sde_reg_dma_setup_ops_cfg cfg;
 	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_blk_reg_map hw;
+	u32 val;
+	int rc;
 
-	if (!last_cmd_buf || !ctl || q >= DMA_CTL_QUEUE_MAX) {
-		DRM_ERROR("invalid param buf %pK ctl %pK q %d\n", last_cmd_buf,
-				ctl, q);
+	if (!ctl || ctl->idx >= CTL_MAX || q >= DMA_CTL_QUEUE_MAX) {
+		DRM_ERROR("ctl %pK q %d index %d\n", ctl, q,
+				((ctl) ? ctl->idx : -1));
 		return -EINVAL;
 	}
 
-	cfg.dma_buf = last_cmd_buf;
-	reset_reg_dma_buffer_v1(last_cmd_buf);
+	if (!last_cmd_buf[ctl->idx] || !last_cmd_buf[ctl->idx]->iova) {
+		DRM_DEBUG("invalid last cmd buf for idx %d\n", ctl->idx);
+		return 0;
+	}
+
+	cfg.dma_buf = last_cmd_buf[ctl->idx];
+	reset_reg_dma_buffer_v1(last_cmd_buf[ctl->idx]);
 	if (validate_last_cmd(&cfg)) {
 		DRM_ERROR("validate buf failed\n");
 		return -EINVAL;
@@ -791,18 +845,37 @@
 	kick_off.trigger_mode = WRITE_IMMEDIATE;
 	kick_off.last_command = 1;
 	kick_off.op = REG_DMA_WRITE;
-	kick_off.dma_buf = last_cmd_buf;
+	kick_off.dma_buf = last_cmd_buf[ctl->idx];
 	if (kick_off_v1(&kick_off)) {
 		DRM_ERROR("kick off last cmd failed\n");
 		return -EINVAL;
 	}
 
+	memset(&hw, 0, sizeof(hw));
+	SET_UP_REG_DMA_REG(hw, reg_dma);
+
+	SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY, mode);
+	if (mode == REG_DMA_WAIT4_COMP) {
+		rc = readl_poll_timeout(hw.base_off + hw.blk_off +
+			reg_dma_int_status_off, val,
+			(val & ctl_trigger_done_mask[ctl->idx][q]),
+			10, 20000);
+		if (rc)
+			DRM_ERROR("poll wait failed %d val %x mask %x\n",
+			    rc, val, ctl_trigger_done_mask[ctl->idx][q]);
+		SDE_EVT32(SDE_EVTLOG_FUNC_EXIT, mode);
+	}
+
 	return 0;
 }
 
 void deinit_v1(void)
 {
-	if (last_cmd_buf)
-		dealloc_reg_dma_v1(last_cmd_buf);
-	last_cmd_buf = NULL;
+	int i = 0;
+
+	for (i = CTL_0; i < CTL_MAX; i++) {
+		if (last_cmd_buf[i])
+			dealloc_reg_dma_v1(last_cmd_buf[i]);
+		last_cmd_buf[i] = NULL;
+	}
 }
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
index 55cb260..0dc3fed 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
@@ -39,19 +39,26 @@
 #define PCC_MEM_SIZE (PCC_LEN + \
 		REG_DMA_HEADERS_BUFFER_SZ)
 
+#define HSIC_MEM_SIZE ((sizeof(struct drm_msm_pa_hsic)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+
+#define SIXZONE_MEM_SIZE ((sizeof(struct drm_msm_sixzone)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+
 #define REG_MASK(n) ((BIT(n)) - 1)
+#define REG_MASK_SHIFT(n, shift) ((REG_MASK(n)) << (shift))
 
 static struct sde_reg_dma_buffer *dspp_buf[REG_DMA_FEATURES_MAX][DSPP_MAX];
 
 static u32 feature_map[SDE_DSPP_MAX] = {
-	[SDE_DSPP_VLUT] = VLUT,
+	[SDE_DSPP_VLUT] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_GAMUT] = GAMUT,
 	[SDE_DSPP_IGC] = IGC,
 	[SDE_DSPP_PCC] = PCC,
 	[SDE_DSPP_GC] = GC,
-	[SDE_DSPP_HSIC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_HSIC] = HSIC,
 	[SDE_DSPP_MEMCOLOR] = REG_DMA_FEATURES_MAX,
-	[SDE_DSPP_SIXZONE] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_SIXZONE] = SIX_ZONE,
 	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
 	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
@@ -63,6 +70,8 @@
 	[SDE_DSPP_GC] = GC_LUT_MEM_SIZE,
 	[SDE_DSPP_IGC] = IGC_LUT_MEM_SIZE,
 	[SDE_DSPP_PCC] = PCC_MEM_SIZE,
+	[SDE_DSPP_HSIC] = HSIC_MEM_SIZE,
+	[SDE_DSPP_SIXZONE] = SIXZONE_MEM_SIZE,
 };
 
 static u32 dspp_mapping[DSPP_MAX] = {
@@ -295,8 +304,8 @@
 	if (!hw_cfg->payload) {
 		DRM_DEBUG_DRIVER("Disable vlut feature\n");
 		SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, 0);
-		if (op_mode & (~(BIT(20))))
-			op_mode = 0;
+		if (PA_DISABLE_REQUIRED(op_mode))
+			op_mode &= ~PA_EN;
 		SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode);
 		return;
 	}
@@ -919,6 +928,233 @@
 	kfree(data);
 }
 
+void reg_dmav1_setup_dspp_pa_hsicv18(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	struct drm_msm_pa_hsic *hsic_cfg;
+	u32 reg = 0, opcode = 0, local_opcode = 0;
+	int rc;
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	rc = reg_dma_dspp_check(ctx, cfg, HSIC);
+	if (rc)
+		return;
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable pa hsic feature\n");
+		opcode &= ~(PA_HUE_EN | PA_SAT_EN | PA_VAL_EN | PA_CONT_EN);
+		if (PA_DISABLE_REQUIRED(opcode))
+			opcode &= ~PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_pa_hsic)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+				hw_cfg->len, sizeof(struct drm_msm_pa_hsic));
+		return;
+	}
+
+	hsic_cfg = hw_cfg->payload;
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dspp_buf[HSIC][ctx->idx]);
+
+	REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx],
+		HSIC, dspp_buf[HSIC][ctx->idx]);
+
+	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+
+	if (hsic_cfg->flags & PA_HSIC_HUE_ENABLE) {
+		reg = hsic_cfg->hue & PA_HUE_MASK;
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->hsic.base + PA_HUE_OFF,
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("hsic hue write failed ret %d\n", rc);
+			return;
+		}
+		local_opcode |= PA_HUE_EN;
+	} else if (opcode & PA_HUE_EN)
+		opcode &= ~PA_HUE_EN;
+
+	if (hsic_cfg->flags & PA_HSIC_SAT_ENABLE) {
+		reg = hsic_cfg->saturation & PA_SAT_MASK;
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->hsic.base + PA_SAT_OFF,
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("hsic saturation write failed ret %d\n", rc);
+			return;
+		}
+		local_opcode |= PA_SAT_EN;
+	} else if (opcode & PA_SAT_EN)
+		opcode &= ~PA_SAT_EN;
+
+	if (hsic_cfg->flags & PA_HSIC_VAL_ENABLE) {
+		reg = hsic_cfg->value & PA_VAL_MASK;
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->hsic.base + PA_VAL_OFF,
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("hsic value write failed ret %d\n", rc);
+			return;
+		}
+		local_opcode |= PA_VAL_EN;
+	} else if (opcode & PA_VAL_EN)
+		opcode &= ~PA_VAL_EN;
+
+	if (hsic_cfg->flags & PA_HSIC_CONT_ENABLE) {
+		reg = hsic_cfg->contrast & PA_CONT_MASK;
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->hsic.base + PA_CONT_OFF,
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("hsic contrast write failed ret %d\n", rc);
+			return;
+		}
+		local_opcode |= PA_CONT_EN;
+	} else if (opcode & PA_CONT_EN)
+		opcode &= ~PA_CONT_EN;
+
+	if (local_opcode)
+		opcode |= (local_opcode | PA_EN);
+	else {
+		DRM_ERROR("Invalid hsic config\n");
+		return;
+	}
+
+	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[HSIC][ctx->idx],
+			REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc)
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
+void reg_dmav1_setup_dspp_sixzonev18(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	struct drm_msm_sixzone *sixzone;
+	u32 reg = 0, hold = 0, local_hold = 0;
+	u32 opcode = 0, local_opcode = 0;
+	int rc;
+
+	opcode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->hsic.base);
+
+	rc = reg_dma_dspp_check(ctx, cfg, SIX_ZONE);
+	if (rc)
+		return;
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable sixzone feature\n");
+		opcode &= ~(PA_SIXZONE_HUE_EN | PA_SIXZONE_SAT_EN |
+			PA_SIXZONE_VAL_EN);
+		if (PA_DISABLE_REQUIRED(opcode))
+			opcode &= ~PA_EN;
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_sixzone)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+			hw_cfg->len, sizeof(struct drm_msm_sixzone));
+		return;
+	}
+
+	sixzone = hw_cfg->payload;
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dspp_buf[SIX_ZONE][ctx->idx]);
+
+	REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx],
+		SIX_ZONE, dspp_buf[SIX_ZONE][ctx->idx]);
+
+	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+
+	reg = BIT(26);
+	REG_DMA_SETUP_OPS(dma_write_cfg,
+		ctx->cap->sblk->sixzone.base,
+		&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("setting lut index failed ret %d\n", rc);
+		return;
+	}
+
+	REG_DMA_SETUP_OPS(dma_write_cfg,
+	    (ctx->cap->sblk->sixzone.base + SIXZONE_ADJ_CURVE_P1_OFF),
+		&sixzone->curve[0].p1, (SIXZONE_LUT_SIZE * sizeof(u32) * 2),
+	    REG_BLK_WRITE_MULTIPLE, 2, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write sixzone lut failed ret %d\n", rc);
+		return;
+	}
+
+	REG_DMA_SETUP_OPS(dma_write_cfg,
+		ctx->cap->sblk->sixzone.base + SIXZONE_THRESHOLDS_OFF,
+		&sixzone->threshold, 3 * sizeof(u32),
+		REG_BLK_WRITE_SINGLE, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write sixzone threshold failed ret %d\n", rc);
+		return;
+	}
+
+	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl,
+		dspp_buf[SIX_ZONE][ctx->idx],
+		REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc)
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+
+	hold = SDE_REG_READ(&ctx->hw,
+			(ctx->cap->sblk->hsic.base + PA_PWL_HOLD_OFF));
+	local_hold = ((sixzone->sat_hold & REG_MASK(2)) << 12);
+	local_hold |= ((sixzone->val_hold & REG_MASK(2)) << 14);
+	hold &= ~REG_MASK_SHIFT(4, 12);
+	hold |= local_hold;
+	SDE_REG_WRITE(&ctx->hw,
+			(ctx->cap->sblk->hsic.base + PA_PWL_HOLD_OFF), hold);
+
+	if (sixzone->flags & SIXZONE_HUE_ENABLE)
+		local_opcode |= PA_SIXZONE_HUE_EN;
+	if (sixzone->flags & SIXZONE_SAT_ENABLE)
+		local_opcode |= PA_SIXZONE_SAT_EN;
+	if (sixzone->flags & SIXZONE_VAL_ENABLE)
+		local_opcode |= PA_SIXZONE_VAL_EN;
+
+	if (local_opcode)
+		local_opcode |= PA_EN;
+
+	opcode &= ~REG_MASK_SHIFT(3, 29);
+	opcode |= local_opcode;
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->hsic.base, opcode);
+}
+
 int reg_dmav1_deinit_dspp_ops(enum sde_dspp idx)
 {
 	int i;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
index bb72c8f..a8115d6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
@@ -67,6 +67,20 @@
 void reg_dmav1_setup_dspp_pccv4(struct sde_hw_dspp *ctx, void *cfg);
 
 /**
+ * reg_dmav1_setup_dspp_pa_hsicv18() - pa hsic v18 impl using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_pa_hsicv18(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * reg_dmav1_setup_dspp_sixzonev18() - sixzone v18 impl using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_sixzonev18(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
  * reg_dmav1_deinit_dspp_ops() - deinitialize the dspp feature op for sde v4
  *                               which were initialized.
  * @idx: dspp idx
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.c b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
index 1a00517..8d386a8 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.c
@@ -588,6 +588,10 @@
 		cmd_type = SDE_ROTATOR_INLINE_CMD_CLEANUP;
 		priv_handle = data->priv_handle;
 		break;
+	case SDE_HW_ROT_CMD_RESET:
+		cmd_type = SDE_ROTATOR_INLINE_CMD_ABORT;
+		priv_handle = data->priv_handle;
+		break;
 	default:
 		SDE_ERROR("invalid hw rotator command %d\n", hw_cmd);
 		return -EINVAL;
@@ -656,7 +660,7 @@
 			SDE_ERROR("failed to get dst format\n");
 			return -EINVAL;
 		}
-	} else if (hw_cmd == SDE_HW_ROT_CMD_COMMIT) {
+	} else {
 		rc = sde_hw_rot_to_v4l2_pixfmt(data->dst_pixel_format,
 				data->dst_modifier, &rot_cmd.dst_pixfmt);
 		if (rc) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_rot.h b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
index 1237858..ea88d05 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_rot.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_rot.h
@@ -28,12 +28,14 @@
  * @SDE_HW_ROT_CMD_COMMIT: commit/execute rotator command
  * @SDE_HW_ROT_CMD_START: mdp is ready to start
  * @SDE_HW_ROT_CMD_CLEANUP: cleanup rotator command after it is done
+ * @SDE_HW_ROT_CMD_RESET: request rotator h/w reset
  */
 enum sde_hw_rot_cmd_type {
 	SDE_HW_ROT_CMD_VALIDATE,
 	SDE_HW_ROT_CMD_COMMIT,
 	SDE_HW_ROT_CMD_START,
 	SDE_HW_ROT_CMD_CLEANUP,
+	SDE_HW_ROT_CMD_RESET,
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index a4564c1..e7aa6ea 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -85,6 +85,7 @@
 #define SSPP_TRAFFIC_SHAPER                0x130
 #define SSPP_CDP_CNTL                      0x134
 #define SSPP_UBWC_ERROR_STATUS             0x138
+#define SSPP_CDP_CNTL_REC1                 0x13c
 #define SSPP_TRAFFIC_SHAPER_PREFILL        0x150
 #define SSPP_TRAFFIC_SHAPER_REC1_PREFILL   0x154
 #define SSPP_TRAFFIC_SHAPER_REC1           0x158
@@ -119,57 +120,6 @@
 #define COMP1_2_INIT_PHASE_Y               0x2C
 #define VIG_0_QSEED2_SHARP                 0x30
 
-/* SDE_SSPP_SCALER_QSEED3 */
-#define QSEED3_HW_VERSION                  0x00
-#define QSEED3_OP_MODE                     0x04
-#define QSEED3_RGB2Y_COEFF                 0x08
-#define QSEED3_PHASE_INIT                  0x0C
-#define QSEED3_PHASE_STEP_Y_H              0x10
-#define QSEED3_PHASE_STEP_Y_V              0x14
-#define QSEED3_PHASE_STEP_UV_H             0x18
-#define QSEED3_PHASE_STEP_UV_V             0x1C
-#define QSEED3_PRELOAD                     0x20
-#define QSEED3_DE_SHARPEN                  0x24
-#define QSEED3_DE_SHARPEN_CTL              0x28
-#define QSEED3_DE_SHAPE_CTL                0x2C
-#define QSEED3_DE_THRESHOLD                0x30
-#define QSEED3_DE_ADJUST_DATA_0            0x34
-#define QSEED3_DE_ADJUST_DATA_1            0x38
-#define QSEED3_DE_ADJUST_DATA_2            0x3C
-#define QSEED3_SRC_SIZE_Y_RGB_A            0x40
-#define QSEED3_SRC_SIZE_UV                 0x44
-#define QSEED3_DST_SIZE                    0x48
-#define QSEED3_COEF_LUT_CTRL               0x4C
-#define QSEED3_COEF_LUT_SWAP_BIT           0
-#define QSEED3_COEF_LUT_DIR_BIT            1
-#define QSEED3_COEF_LUT_Y_CIR_BIT          2
-#define QSEED3_COEF_LUT_UV_CIR_BIT         3
-#define QSEED3_COEF_LUT_Y_SEP_BIT          4
-#define QSEED3_COEF_LUT_UV_SEP_BIT         5
-#define QSEED3_BUFFER_CTRL                 0x50
-#define QSEED3_CLK_CTRL0                   0x54
-#define QSEED3_CLK_CTRL1                   0x58
-#define QSEED3_CLK_STATUS                  0x5C
-#define QSEED3_MISR_CTRL                   0x70
-#define QSEED3_MISR_SIGNATURE_0            0x74
-#define QSEED3_MISR_SIGNATURE_1            0x78
-#define QSEED3_PHASE_INIT_Y_H              0x90
-#define QSEED3_PHASE_INIT_Y_V              0x94
-#define QSEED3_PHASE_INIT_UV_H             0x98
-#define QSEED3_PHASE_INIT_UV_V             0x9C
-#define QSEED3_COEF_LUT                    0x100
-#define QSEED3_FILTERS                     5
-#define QSEED3_LUT_REGIONS                 4
-#define QSEED3_CIRCULAR_LUTS               9
-#define QSEED3_SEPARABLE_LUTS              10
-#define QSEED3_LUT_SIZE                    60
-#define QSEED3_ENABLE                      2
-#define QSEED3_DIR_LUT_SIZE                (200 * sizeof(u32))
-#define QSEED3_CIR_LUT_SIZE \
-	(QSEED3_LUT_SIZE * QSEED3_CIRCULAR_LUTS * sizeof(u32))
-#define QSEED3_SEP_LUT_SIZE \
-	(QSEED3_LUT_SIZE * QSEED3_SEPARABLE_LUTS * sizeof(u32))
-
 /*
  * Definitions for ViG op modes
  */
@@ -309,7 +259,8 @@
  * Setup source pixel format, flip,
  */
 static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx,
-		const struct sde_format *fmt, u32 flags,
+		const struct sde_format *fmt,
+		bool blend_enabled, u32 flags,
 		enum sde_sspp_multirect_index rect_mode)
 {
 	struct sde_hw_blk_reg_map *c;
@@ -378,7 +329,8 @@
 			SDE_FETCH_CONFIG_RESET_VALUE |
 			ctx->mdp->highest_bank_bit << 18);
 		if (IS_UBWC_20_SUPPORTED(ctx->catalog->ubwc_version)) {
-			fast_clear = fmt->alpha_enable ? BIT(31) : 0;
+			fast_clear = (fmt->alpha_enable && blend_enabled) ?
+				BIT(31) : 0;
 			SDE_REG_WRITE(c, SSPP_UBWC_STATIC_CTRL,
 					fast_clear | (ctx->mdp->ubwc_swizzle) |
 					(ctx->mdp->highest_bank_bit << 4));
@@ -422,19 +374,19 @@
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
-	if (rect_mode == SDE_SSPP_RECT_SOLO || rect_mode == SDE_SSPP_RECT_0)
-		secure_bit_mask = (rect_mode == SDE_SSPP_RECT_SOLO) ? 0xF :
-			0x5;
-	else
-		secure_bit_mask = 0xA;
-
 	c = &ctx->hw;
 
-	secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx);
-	if (enable)
+	if (enable) {
+		if ((rect_mode == SDE_SSPP_RECT_SOLO)
+				|| (rect_mode == SDE_SSPP_RECT_0))
+			secure_bit_mask =
+				(rect_mode == SDE_SSPP_RECT_SOLO) ? 0xF : 0x5;
+		else
+			secure_bit_mask = 0xA;
+
+		secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx);
 		secure |= secure_bit_mask;
-	else
-		secure &= ~secure_bit_mask;
+	}
 
 	SDE_REG_WRITE(c, SSPP_SRC_ADDR_SW_STATUS + idx, secure);
 }
@@ -556,144 +508,12 @@
 		pe->phase_step_y[SDE_SSPP_COMP_1_2]);
 }
 
-static void _sde_hw_sspp_setup_scaler3_lut(struct sde_hw_pipe *ctx,
-		struct sde_hw_scaler3_cfg *scaler3_cfg)
-{
-	u32 idx;
-	int i, j, filter;
-	int config_lut = 0x0;
-	unsigned long lut_flags;
-	u32 lut_addr, lut_offset, lut_len;
-	u32 *lut[QSEED3_FILTERS] = {NULL, NULL, NULL, NULL, NULL};
-	static const uint32_t offset[QSEED3_FILTERS][QSEED3_LUT_REGIONS][2] = {
-		{{18, 0x000}, {12, 0x120}, {12, 0x1E0}, {8, 0x2A0} },
-		{{6, 0x320}, {3, 0x3E0}, {3, 0x440}, {3, 0x4A0} },
-		{{6, 0x500}, {3, 0x5c0}, {3, 0x620}, {3, 0x680} },
-		{{6, 0x380}, {3, 0x410}, {3, 0x470}, {3, 0x4d0} },
-		{{6, 0x560}, {3, 0x5f0}, {3, 0x650}, {3, 0x6b0} },
-	};
-
-	if (_sspp_subblk_offset(ctx, SDE_SSPP_SCALER_QSEED3, &idx) ||
-		!scaler3_cfg)
-		return;
-
-	lut_flags = (unsigned long) scaler3_cfg->lut_flag;
-	if (test_bit(QSEED3_COEF_LUT_DIR_BIT, &lut_flags) &&
-		(scaler3_cfg->dir_len == QSEED3_DIR_LUT_SIZE)) {
-		lut[0] = scaler3_cfg->dir_lut;
-		config_lut = 1;
-	}
-	if (test_bit(QSEED3_COEF_LUT_Y_CIR_BIT, &lut_flags) &&
-		(scaler3_cfg->y_rgb_cir_lut_idx < QSEED3_CIRCULAR_LUTS) &&
-		(scaler3_cfg->cir_len == QSEED3_CIR_LUT_SIZE)) {
-		lut[1] = scaler3_cfg->cir_lut +
-			scaler3_cfg->y_rgb_cir_lut_idx * QSEED3_LUT_SIZE;
-		config_lut = 1;
-	}
-	if (test_bit(QSEED3_COEF_LUT_UV_CIR_BIT, &lut_flags) &&
-		(scaler3_cfg->uv_cir_lut_idx < QSEED3_CIRCULAR_LUTS) &&
-		(scaler3_cfg->cir_len == QSEED3_CIR_LUT_SIZE)) {
-		lut[2] = scaler3_cfg->cir_lut +
-			scaler3_cfg->uv_cir_lut_idx * QSEED3_LUT_SIZE;
-		config_lut = 1;
-	}
-	if (test_bit(QSEED3_COEF_LUT_Y_SEP_BIT, &lut_flags) &&
-		(scaler3_cfg->y_rgb_sep_lut_idx < QSEED3_SEPARABLE_LUTS) &&
-		(scaler3_cfg->sep_len == QSEED3_SEP_LUT_SIZE)) {
-		lut[3] = scaler3_cfg->sep_lut +
-			scaler3_cfg->y_rgb_sep_lut_idx * QSEED3_LUT_SIZE;
-		config_lut = 1;
-	}
-	if (test_bit(QSEED3_COEF_LUT_UV_SEP_BIT, &lut_flags) &&
-		(scaler3_cfg->uv_sep_lut_idx < QSEED3_SEPARABLE_LUTS) &&
-		(scaler3_cfg->sep_len == QSEED3_SEP_LUT_SIZE)) {
-		lut[4] = scaler3_cfg->sep_lut +
-			scaler3_cfg->uv_sep_lut_idx * QSEED3_LUT_SIZE;
-		config_lut = 1;
-	}
-
-	if (config_lut) {
-		for (filter = 0; filter < QSEED3_FILTERS; filter++) {
-			if (!lut[filter])
-				continue;
-			lut_offset = 0;
-			for (i = 0; i < QSEED3_LUT_REGIONS; i++) {
-				lut_addr = QSEED3_COEF_LUT + idx
-					+ offset[filter][i][1];
-				lut_len = offset[filter][i][0] << 2;
-				for (j = 0; j < lut_len; j++) {
-					SDE_REG_WRITE(&ctx->hw,
-						lut_addr,
-						(lut[filter])[lut_offset++]);
-					lut_addr += 4;
-				}
-			}
-		}
-	}
-
-	if (test_bit(QSEED3_COEF_LUT_SWAP_BIT, &lut_flags))
-		SDE_REG_WRITE(&ctx->hw, QSEED3_COEF_LUT_CTRL + idx, BIT(0));
-
-}
-
-static void _sde_hw_sspp_setup_scaler3_de(struct sde_hw_pipe *ctx,
-		struct sde_hw_scaler3_de_cfg *de_cfg)
-{
-	u32 idx;
-	u32 sharp_lvl, sharp_ctl, shape_ctl, de_thr;
-	u32 adjust_a, adjust_b, adjust_c;
-	struct sde_hw_blk_reg_map *hw;
-
-	if (_sspp_subblk_offset(ctx, SDE_SSPP_SCALER_QSEED3, &idx) || !de_cfg)
-		return;
-
-	if (!de_cfg->enable)
-		return;
-
-	hw = &ctx->hw;
-	sharp_lvl = (de_cfg->sharpen_level1 & 0x1FF) |
-		((de_cfg->sharpen_level2 & 0x1FF) << 16);
-
-	sharp_ctl = ((de_cfg->limit & 0xF) << 9) |
-		((de_cfg->prec_shift & 0x7) << 13) |
-		((de_cfg->clip & 0x7) << 16);
-
-	shape_ctl = (de_cfg->thr_quiet & 0xFF) |
-		((de_cfg->thr_dieout & 0x3FF) << 16);
-
-	de_thr = (de_cfg->thr_low & 0x3FF) |
-		((de_cfg->thr_high & 0x3FF) << 16);
-
-	adjust_a = (de_cfg->adjust_a[0] & 0x3FF) |
-		((de_cfg->adjust_a[1] & 0x3FF) << 10) |
-		((de_cfg->adjust_a[2] & 0x3FF) << 20);
-
-	adjust_b = (de_cfg->adjust_b[0] & 0x3FF) |
-		((de_cfg->adjust_b[1] & 0x3FF) << 10) |
-		((de_cfg->adjust_b[2] & 0x3FF) << 20);
-
-	adjust_c = (de_cfg->adjust_c[0] & 0x3FF) |
-		((de_cfg->adjust_c[1] & 0x3FF) << 10) |
-		((de_cfg->adjust_c[2] & 0x3FF) << 20);
-
-	SDE_REG_WRITE(hw, QSEED3_DE_SHARPEN + idx, sharp_lvl);
-	SDE_REG_WRITE(hw, QSEED3_DE_SHARPEN_CTL + idx, sharp_ctl);
-	SDE_REG_WRITE(hw, QSEED3_DE_SHAPE_CTL + idx, shape_ctl);
-	SDE_REG_WRITE(hw, QSEED3_DE_THRESHOLD + idx, de_thr);
-	SDE_REG_WRITE(hw, QSEED3_DE_ADJUST_DATA_0 + idx, adjust_a);
-	SDE_REG_WRITE(hw, QSEED3_DE_ADJUST_DATA_1 + idx, adjust_b);
-	SDE_REG_WRITE(hw, QSEED3_DE_ADJUST_DATA_2 + idx, adjust_c);
-
-}
-
 static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 		struct sde_hw_pipe_cfg *sspp,
 		struct sde_hw_pixel_ext *pe,
 		void *scaler_cfg)
 {
 	u32 idx;
-	u32 op_mode = 0;
-	u32 phase_init, preload, src_y_rgb, src_uv, dst;
 	struct sde_hw_scaler3_cfg *scaler3_cfg = scaler_cfg;
 
 	(void)pe;
@@ -701,93 +521,9 @@
 		|| !scaler3_cfg || !ctx || !ctx->cap || !ctx->cap->sblk)
 		return;
 
-	if (!scaler3_cfg->enable)
-		goto end;
-
-	op_mode |= BIT(0);
-	op_mode |= (scaler3_cfg->y_rgb_filter_cfg & 0x3) << 16;
-
-	if (SDE_FORMAT_IS_YUV(sspp->layout.format)) {
-		op_mode |= BIT(12);
-		op_mode |= (scaler3_cfg->uv_filter_cfg & 0x3) << 24;
-	}
-
-	op_mode |= (scaler3_cfg->blend_cfg & 1) << 31;
-	op_mode |= (scaler3_cfg->dir_en) ? BIT(4) : 0;
-
-	preload =
-		((scaler3_cfg->preload_x[0] & 0x7F) << 0) |
-		((scaler3_cfg->preload_y[0] & 0x7F) << 8) |
-		((scaler3_cfg->preload_x[1] & 0x7F) << 16) |
-		((scaler3_cfg->preload_y[1] & 0x7F) << 24);
-
-	src_y_rgb = (scaler3_cfg->src_width[0] & 0x1FFFF) |
-		((scaler3_cfg->src_height[0] & 0x1FFFF) << 16);
-
-	src_uv = (scaler3_cfg->src_width[1] & 0x1FFFF) |
-		((scaler3_cfg->src_height[1] & 0x1FFFF) << 16);
-
-	dst = (scaler3_cfg->dst_width & 0x1FFFF) |
-		((scaler3_cfg->dst_height & 0x1FFFF) << 16);
-
-	if (scaler3_cfg->de.enable) {
-		_sde_hw_sspp_setup_scaler3_de(ctx, &scaler3_cfg->de);
-		op_mode |= BIT(8);
-	}
-
-	if (scaler3_cfg->lut_flag)
-		_sde_hw_sspp_setup_scaler3_lut(ctx, scaler3_cfg);
-
-	if (ctx->cap->sblk->scaler_blk.version == 0x1002) {
-		phase_init =
-			((scaler3_cfg->init_phase_x[0] & 0x3F) << 0) |
-			((scaler3_cfg->init_phase_y[0] & 0x3F) << 8) |
-			((scaler3_cfg->init_phase_x[1] & 0x3F) << 16) |
-			((scaler3_cfg->init_phase_y[1] & 0x3F) << 24);
-		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT + idx, phase_init);
-	} else {
-		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_H + idx,
-			scaler3_cfg->init_phase_x[0] & 0x1FFFFF);
-		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_V + idx,
-			scaler3_cfg->init_phase_y[0] & 0x1FFFFF);
-		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_UV_H + idx,
-			scaler3_cfg->init_phase_x[1] & 0x1FFFFF);
-		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_UV_V + idx,
-			scaler3_cfg->init_phase_y[1] & 0x1FFFFF);
-	}
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_STEP_Y_H + idx,
-		scaler3_cfg->phase_step_x[0] & 0xFFFFFF);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_STEP_Y_V + idx,
-		scaler3_cfg->phase_step_y[0] & 0xFFFFFF);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_STEP_UV_H + idx,
-		scaler3_cfg->phase_step_x[1] & 0xFFFFFF);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_STEP_UV_V + idx,
-		scaler3_cfg->phase_step_y[1] & 0xFFFFFF);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_PRELOAD + idx, preload);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_SRC_SIZE_Y_RGB_A + idx, src_y_rgb);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_SRC_SIZE_UV + idx, src_uv);
-
-	SDE_REG_WRITE(&ctx->hw, QSEED3_DST_SIZE + idx, dst);
-
-end:
-	if (!SDE_FORMAT_IS_DX(sspp->layout.format))
-		op_mode |= BIT(14);
-
-	if (sspp->layout.format->alpha_enable) {
-		op_mode |= BIT(10);
-		if (ctx->cap->sblk->scaler_blk.version == 0x1002)
-			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30;
-		else
-			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29;
-	}
-	SDE_REG_WRITE(&ctx->hw, QSEED3_OP_MODE + idx, op_mode);
+	sde_hw_setup_scaler3(&ctx->hw, scaler3_cfg, idx,
+			ctx->cap->sblk->scaler_blk.version,
+			sspp->layout.format);
 }
 
 static u32 _sde_hw_sspp_get_scaler3_ver(struct sde_hw_pipe *ctx)
@@ -797,7 +533,7 @@
 	if (!ctx || _sspp_subblk_offset(ctx, SDE_SSPP_SCALER_QSEED3, &idx))
 		return 0;
 
-	return SDE_REG_READ(&ctx->hw, QSEED3_HW_VERSION + idx);
+	return sde_hw_get_scaler3_ver(&ctx->hw, idx);
 }
 
 /**
@@ -1140,10 +876,12 @@
 }
 
 static void sde_hw_sspp_setup_cdp(struct sde_hw_pipe *ctx,
-		struct sde_hw_pipe_cdp_cfg *cfg)
+		struct sde_hw_pipe_cdp_cfg *cfg,
+		enum sde_sspp_multirect_index index)
 {
 	u32 idx;
 	u32 cdp_cntl = 0;
+	u32 cdp_cntl_offset = 0;
 
 	if (!ctx || !cfg)
 		return;
@@ -1151,6 +889,13 @@
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
+	if (index == SDE_SSPP_RECT_0)
+		cdp_cntl_offset = SSPP_CDP_CNTL;
+	else if (index == SDE_SSPP_RECT_1)
+		cdp_cntl_offset = SSPP_CDP_CNTL_REC1;
+	else
+		return;
+
 	if (cfg->enable)
 		cdp_cntl |= BIT(0);
 	if (cfg->ubwc_meta_enable)
@@ -1160,7 +905,7 @@
 	if (cfg->preload_ahead == SDE_SSPP_CDP_PRELOAD_AHEAD_64)
 		cdp_cntl |= BIT(3);
 
-	SDE_REG_WRITE(&ctx->hw, SSPP_CDP_CNTL, cdp_cntl);
+	SDE_REG_WRITE(&ctx->hw, cdp_cntl_offset, cdp_cntl);
 }
 
 static void _setup_layer_ops(struct sde_hw_pipe *c,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index 8700627..fdf215f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -156,114 +156,6 @@
 };
 
 /**
- * struct sde_hw_scaler3_de_cfg : QSEEDv3 detail enhancer configuration
- * @enable:         detail enhancer enable/disable
- * @sharpen_level1: sharpening strength for noise
- * @sharpen_level2: sharpening strength for signal
- * @ clip:          clip shift
- * @ limit:         limit value
- * @ thr_quiet:     quiet threshold
- * @ thr_dieout:    dieout threshold
- * @ thr_high:      low threshold
- * @ thr_high:      high threshold
- * @ prec_shift:    precision shift
- * @ adjust_a:      A-coefficients for mapping curve
- * @ adjust_b:      B-coefficients for mapping curve
- * @ adjust_c:      C-coefficients for mapping curve
- */
-struct sde_hw_scaler3_de_cfg {
-	u32 enable;
-	int16_t sharpen_level1;
-	int16_t sharpen_level2;
-	uint16_t clip;
-	uint16_t limit;
-	uint16_t thr_quiet;
-	uint16_t thr_dieout;
-	uint16_t thr_low;
-	uint16_t thr_high;
-	uint16_t prec_shift;
-	int16_t adjust_a[SDE_MAX_DE_CURVES];
-	int16_t adjust_b[SDE_MAX_DE_CURVES];
-	int16_t adjust_c[SDE_MAX_DE_CURVES];
-};
-
-/**
- * struct sde_hw_scaler3_cfg : QSEEDv3 configuration
- * @enable:        scaler enable
- * @dir_en:        direction detection block enable
- * @ init_phase_x: horizontal initial phase
- * @ phase_step_x: horizontal phase step
- * @ init_phase_y: vertical initial phase
- * @ phase_step_y: vertical phase step
- * @ preload_x:    horizontal preload value
- * @ preload_y:    vertical preload value
- * @ src_width:    source width
- * @ src_height:   source height
- * @ dst_width:    destination width
- * @ dst_height:   destination height
- * @ y_rgb_filter_cfg: y/rgb plane filter configuration
- * @ uv_filter_cfg: uv plane filter configuration
- * @ alpha_filter_cfg: alpha filter configuration
- * @ blend_cfg:    blend coefficients configuration
- * @ lut_flag:     scaler LUT update flags
- *                 0x1 swap LUT bank
- *                 0x2 update 2D filter LUT
- *                 0x4 update y circular filter LUT
- *                 0x8 update uv circular filter LUT
- *                 0x10 update y separable filter LUT
- *                 0x20 update uv separable filter LUT
- * @ dir_lut_idx:  2D filter LUT index
- * @ y_rgb_cir_lut_idx: y circular filter LUT index
- * @ uv_cir_lut_idx: uv circular filter LUT index
- * @ y_rgb_sep_lut_idx: y circular filter LUT index
- * @ uv_sep_lut_idx: uv separable filter LUT index
- * @ dir_lut:      pointer to 2D LUT
- * @ cir_lut:      pointer to circular filter LUT
- * @ sep_lut:      pointer to separable filter LUT
- * @ de: detail enhancer configuration
- */
-struct sde_hw_scaler3_cfg {
-	u32 enable;
-	u32 dir_en;
-	int32_t init_phase_x[SDE_MAX_PLANES];
-	int32_t phase_step_x[SDE_MAX_PLANES];
-	int32_t init_phase_y[SDE_MAX_PLANES];
-	int32_t phase_step_y[SDE_MAX_PLANES];
-
-	u32 preload_x[SDE_MAX_PLANES];
-	u32 preload_y[SDE_MAX_PLANES];
-	u32 src_width[SDE_MAX_PLANES];
-	u32 src_height[SDE_MAX_PLANES];
-
-	u32 dst_width;
-	u32 dst_height;
-
-	u32 y_rgb_filter_cfg;
-	u32 uv_filter_cfg;
-	u32 alpha_filter_cfg;
-	u32 blend_cfg;
-
-	u32 lut_flag;
-	u32 dir_lut_idx;
-
-	u32 y_rgb_cir_lut_idx;
-	u32 uv_cir_lut_idx;
-	u32 y_rgb_sep_lut_idx;
-	u32 uv_sep_lut_idx;
-	u32 *dir_lut;
-	size_t dir_len;
-	u32 *cir_lut;
-	size_t cir_len;
-	u32 *sep_lut;
-	size_t sep_len;
-
-	/*
-	 * Detail enhancer settings
-	 */
-	struct sde_hw_scaler3_de_cfg de;
-};
-
-/**
  * struct sde_hw_pipe_cfg : Pipe description
  * @layout:    format layout information for programming buffer to hardware
  * @src_rect:  src ROI, caller takes into account the different operations
@@ -401,12 +293,14 @@
 	/**
 	 * setup_format - setup pixel format cropping rectangle, flip
 	 * @ctx: Pointer to pipe context
-	 * @cfg: Pointer to pipe config structure
+	 * @fmt: Pointer to sde_format structure
+	 * @blend_enabled: flag indicating blend enabled or disabled on plane
 	 * @flags: Extra flags for format config
 	 * @index: rectangle index in multirect
 	 */
 	void (*setup_format)(struct sde_hw_pipe *ctx,
-			const struct sde_format *fmt, u32 flags,
+			const struct sde_format *fmt,
+			bool blend_enabled, u32 flags,
 			enum sde_sspp_multirect_index index);
 
 	/**
@@ -610,9 +504,11 @@
 	 * setup_cdp - setup client driven prefetch
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to cdp configuration
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_cdp)(struct sde_hw_pipe *ctx,
-			struct sde_hw_pipe_cdp_cfg *cfg);
+			struct sde_hw_pipe_cdp_cfg *cfg,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_secure_address - setup secureity status of the source address
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index ecb445d..51e4ba2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -86,6 +86,9 @@
 				lower_pipe = FLD_SMART_PANEL_FREE_RUN;
 
 			upper_pipe = lower_pipe;
+
+			/* smart panel align mode */
+			lower_pipe |= BIT(mdp->caps->smart_panel_align_mode);
 		} else {
 			if (cfg->intf == INTF_2) {
 				lower_pipe = FLD_INTF_1_SW_TRG_MUX;
@@ -176,6 +179,7 @@
 		new_val = reg_val & ~BIT(bit_off);
 
 	SDE_REG_WRITE(c, reg_off, new_val);
+	wmb(); /* ensure write finished before progressing */
 
 	clk_forced_on = !(reg_val & BIT(bit_off));
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.c b/drivers/gpu/drm/msm/sde/sde_hw_util.c
index 7df5736..08fe5e1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.c
@@ -10,6 +10,8 @@
  * GNU General Public License for more details.
  */
 #define pr_fmt(fmt)	"[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <uapi/drm/sde_drm.h>
 #include "msm_drv.h"
 #include "sde_kms.h"
 #include "sde_hw_mdss.h"
@@ -18,6 +20,57 @@
 /* using a file static variables for debugfs access */
 static u32 sde_hw_util_log_mask = SDE_DBG_MASK_NONE;
 
+/* SDE_SCALER_QSEED3 */
+#define QSEED3_HW_VERSION                  0x00
+#define QSEED3_OP_MODE                     0x04
+#define QSEED3_RGB2Y_COEFF                 0x08
+#define QSEED3_PHASE_INIT                  0x0C
+#define QSEED3_PHASE_STEP_Y_H              0x10
+#define QSEED3_PHASE_STEP_Y_V              0x14
+#define QSEED3_PHASE_STEP_UV_H             0x18
+#define QSEED3_PHASE_STEP_UV_V             0x1C
+#define QSEED3_PRELOAD                     0x20
+#define QSEED3_DE_SHARPEN                  0x24
+#define QSEED3_DE_SHARPEN_CTL              0x28
+#define QSEED3_DE_SHAPE_CTL                0x2C
+#define QSEED3_DE_THRESHOLD                0x30
+#define QSEED3_DE_ADJUST_DATA_0            0x34
+#define QSEED3_DE_ADJUST_DATA_1            0x38
+#define QSEED3_DE_ADJUST_DATA_2            0x3C
+#define QSEED3_SRC_SIZE_Y_RGB_A            0x40
+#define QSEED3_SRC_SIZE_UV                 0x44
+#define QSEED3_DST_SIZE                    0x48
+#define QSEED3_COEF_LUT_CTRL               0x4C
+#define QSEED3_COEF_LUT_SWAP_BIT           0
+#define QSEED3_COEF_LUT_DIR_BIT            1
+#define QSEED3_COEF_LUT_Y_CIR_BIT          2
+#define QSEED3_COEF_LUT_UV_CIR_BIT         3
+#define QSEED3_COEF_LUT_Y_SEP_BIT          4
+#define QSEED3_COEF_LUT_UV_SEP_BIT         5
+#define QSEED3_BUFFER_CTRL                 0x50
+#define QSEED3_CLK_CTRL0                   0x54
+#define QSEED3_CLK_CTRL1                   0x58
+#define QSEED3_CLK_STATUS                  0x5C
+#define QSEED3_MISR_CTRL                   0x70
+#define QSEED3_MISR_SIGNATURE_0            0x74
+#define QSEED3_MISR_SIGNATURE_1            0x78
+#define QSEED3_PHASE_INIT_Y_H              0x90
+#define QSEED3_PHASE_INIT_Y_V              0x94
+#define QSEED3_PHASE_INIT_UV_H             0x98
+#define QSEED3_PHASE_INIT_UV_V             0x9C
+#define QSEED3_COEF_LUT                    0x100
+#define QSEED3_FILTERS                     5
+#define QSEED3_LUT_REGIONS                 4
+#define QSEED3_CIRCULAR_LUTS               9
+#define QSEED3_SEPARABLE_LUTS              10
+#define QSEED3_LUT_SIZE                    60
+#define QSEED3_ENABLE                      2
+#define QSEED3_DIR_LUT_SIZE                (200 * sizeof(u32))
+#define QSEED3_CIR_LUT_SIZE \
+	(QSEED3_LUT_SIZE * QSEED3_CIRCULAR_LUTS * sizeof(u32))
+#define QSEED3_SEP_LUT_SIZE \
+	(QSEED3_LUT_SIZE * QSEED3_SEPARABLE_LUTS * sizeof(u32))
+
 void sde_reg_write(struct sde_hw_blk_reg_map *c,
 		u32 reg_off,
 		u32 val,
@@ -40,6 +93,283 @@
 	return &sde_hw_util_log_mask;
 }
 
+void sde_set_scaler_v2(struct sde_hw_scaler3_cfg *cfg,
+		const struct sde_drm_scaler_v2 *scale_v2)
+{
+	int i;
+
+	cfg->enable = scale_v2->enable;
+	cfg->dir_en = scale_v2->dir_en;
+
+	for (i = 0; i < SDE_MAX_PLANES; i++) {
+		cfg->init_phase_x[i] = scale_v2->init_phase_x[i];
+		cfg->phase_step_x[i] = scale_v2->phase_step_x[i];
+		cfg->init_phase_y[i] = scale_v2->init_phase_y[i];
+		cfg->phase_step_y[i] = scale_v2->phase_step_y[i];
+
+		cfg->preload_x[i] = scale_v2->preload_x[i];
+		cfg->preload_y[i] = scale_v2->preload_y[i];
+		cfg->src_width[i] = scale_v2->src_width[i];
+		cfg->src_height[i] = scale_v2->src_height[i];
+	}
+
+	cfg->dst_width = scale_v2->dst_width;
+	cfg->dst_height = scale_v2->dst_height;
+
+	cfg->y_rgb_filter_cfg = scale_v2->y_rgb_filter_cfg;
+	cfg->uv_filter_cfg = scale_v2->uv_filter_cfg;
+	cfg->alpha_filter_cfg = scale_v2->alpha_filter_cfg;
+	cfg->blend_cfg = scale_v2->blend_cfg;
+
+	cfg->lut_flag = scale_v2->lut_flag;
+	cfg->dir_lut_idx = scale_v2->dir_lut_idx;
+	cfg->y_rgb_cir_lut_idx = scale_v2->y_rgb_cir_lut_idx;
+	cfg->uv_cir_lut_idx = scale_v2->uv_cir_lut_idx;
+	cfg->y_rgb_sep_lut_idx = scale_v2->y_rgb_sep_lut_idx;
+	cfg->uv_sep_lut_idx = scale_v2->uv_sep_lut_idx;
+
+	cfg->de.enable = scale_v2->de.enable;
+	cfg->de.sharpen_level1 = scale_v2->de.sharpen_level1;
+	cfg->de.sharpen_level2 = scale_v2->de.sharpen_level2;
+	cfg->de.clip = scale_v2->de.clip;
+	cfg->de.limit = scale_v2->de.limit;
+	cfg->de.thr_quiet = scale_v2->de.thr_quiet;
+	cfg->de.thr_dieout = scale_v2->de.thr_dieout;
+	cfg->de.thr_low = scale_v2->de.thr_low;
+	cfg->de.thr_high = scale_v2->de.thr_high;
+	cfg->de.prec_shift = scale_v2->de.prec_shift;
+
+	for (i = 0; i < SDE_MAX_DE_CURVES; i++) {
+		cfg->de.adjust_a[i] = scale_v2->de.adjust_a[i];
+		cfg->de.adjust_b[i] = scale_v2->de.adjust_b[i];
+		cfg->de.adjust_c[i] = scale_v2->de.adjust_c[i];
+	}
+}
+
+static void _sde_hw_setup_scaler3_lut(struct sde_hw_blk_reg_map *c,
+		struct sde_hw_scaler3_cfg *scaler3_cfg, u32 offset)
+{
+	int i, j, filter;
+	int config_lut = 0x0;
+	unsigned long lut_flags;
+	u32 lut_addr, lut_offset, lut_len;
+	u32 *lut[QSEED3_FILTERS] = {NULL, NULL, NULL, NULL, NULL};
+	static const uint32_t off_tbl[QSEED3_FILTERS][QSEED3_LUT_REGIONS][2] = {
+		{{18, 0x000}, {12, 0x120}, {12, 0x1E0}, {8, 0x2A0} },
+		{{6, 0x320}, {3, 0x3E0}, {3, 0x440}, {3, 0x4A0} },
+		{{6, 0x500}, {3, 0x5c0}, {3, 0x620}, {3, 0x680} },
+		{{6, 0x380}, {3, 0x410}, {3, 0x470}, {3, 0x4d0} },
+		{{6, 0x560}, {3, 0x5f0}, {3, 0x650}, {3, 0x6b0} },
+	};
+
+	lut_flags = (unsigned long) scaler3_cfg->lut_flag;
+	if (test_bit(QSEED3_COEF_LUT_DIR_BIT, &lut_flags) &&
+		(scaler3_cfg->dir_len == QSEED3_DIR_LUT_SIZE)) {
+		lut[0] = scaler3_cfg->dir_lut;
+		config_lut = 1;
+	}
+	if (test_bit(QSEED3_COEF_LUT_Y_CIR_BIT, &lut_flags) &&
+		(scaler3_cfg->y_rgb_cir_lut_idx < QSEED3_CIRCULAR_LUTS) &&
+		(scaler3_cfg->cir_len == QSEED3_CIR_LUT_SIZE)) {
+		lut[1] = scaler3_cfg->cir_lut +
+			scaler3_cfg->y_rgb_cir_lut_idx * QSEED3_LUT_SIZE;
+		config_lut = 1;
+	}
+	if (test_bit(QSEED3_COEF_LUT_UV_CIR_BIT, &lut_flags) &&
+		(scaler3_cfg->uv_cir_lut_idx < QSEED3_CIRCULAR_LUTS) &&
+		(scaler3_cfg->cir_len == QSEED3_CIR_LUT_SIZE)) {
+		lut[2] = scaler3_cfg->cir_lut +
+			scaler3_cfg->uv_cir_lut_idx * QSEED3_LUT_SIZE;
+		config_lut = 1;
+	}
+	if (test_bit(QSEED3_COEF_LUT_Y_SEP_BIT, &lut_flags) &&
+		(scaler3_cfg->y_rgb_sep_lut_idx < QSEED3_SEPARABLE_LUTS) &&
+		(scaler3_cfg->sep_len == QSEED3_SEP_LUT_SIZE)) {
+		lut[3] = scaler3_cfg->sep_lut +
+			scaler3_cfg->y_rgb_sep_lut_idx * QSEED3_LUT_SIZE;
+		config_lut = 1;
+	}
+	if (test_bit(QSEED3_COEF_LUT_UV_SEP_BIT, &lut_flags) &&
+		(scaler3_cfg->uv_sep_lut_idx < QSEED3_SEPARABLE_LUTS) &&
+		(scaler3_cfg->sep_len == QSEED3_SEP_LUT_SIZE)) {
+		lut[4] = scaler3_cfg->sep_lut +
+			scaler3_cfg->uv_sep_lut_idx * QSEED3_LUT_SIZE;
+		config_lut = 1;
+	}
+
+	if (config_lut) {
+		for (filter = 0; filter < QSEED3_FILTERS; filter++) {
+			if (!lut[filter])
+				continue;
+			lut_offset = 0;
+			for (i = 0; i < QSEED3_LUT_REGIONS; i++) {
+				lut_addr = QSEED3_COEF_LUT + offset
+					+ off_tbl[filter][i][1];
+				lut_len = off_tbl[filter][i][0] << 2;
+				for (j = 0; j < lut_len; j++) {
+					SDE_REG_WRITE(c,
+						lut_addr,
+						(lut[filter])[lut_offset++]);
+					lut_addr += 4;
+				}
+			}
+		}
+	}
+
+	if (test_bit(QSEED3_COEF_LUT_SWAP_BIT, &lut_flags))
+		SDE_REG_WRITE(c, QSEED3_COEF_LUT_CTRL + offset, BIT(0));
+
+}
+
+static void _sde_hw_setup_scaler3_de(struct sde_hw_blk_reg_map *c,
+		struct sde_hw_scaler3_de_cfg *de_cfg, u32 offset)
+{
+	u32 sharp_lvl, sharp_ctl, shape_ctl, de_thr;
+	u32 adjust_a, adjust_b, adjust_c;
+
+	if (!de_cfg->enable)
+		return;
+
+	sharp_lvl = (de_cfg->sharpen_level1 & 0x1FF) |
+		((de_cfg->sharpen_level2 & 0x1FF) << 16);
+
+	sharp_ctl = ((de_cfg->limit & 0xF) << 9) |
+		((de_cfg->prec_shift & 0x7) << 13) |
+		((de_cfg->clip & 0x7) << 16);
+
+	shape_ctl = (de_cfg->thr_quiet & 0xFF) |
+		((de_cfg->thr_dieout & 0x3FF) << 16);
+
+	de_thr = (de_cfg->thr_low & 0x3FF) |
+		((de_cfg->thr_high & 0x3FF) << 16);
+
+	adjust_a = (de_cfg->adjust_a[0] & 0x3FF) |
+		((de_cfg->adjust_a[1] & 0x3FF) << 10) |
+		((de_cfg->adjust_a[2] & 0x3FF) << 20);
+
+	adjust_b = (de_cfg->adjust_b[0] & 0x3FF) |
+		((de_cfg->adjust_b[1] & 0x3FF) << 10) |
+		((de_cfg->adjust_b[2] & 0x3FF) << 20);
+
+	adjust_c = (de_cfg->adjust_c[0] & 0x3FF) |
+		((de_cfg->adjust_c[1] & 0x3FF) << 10) |
+		((de_cfg->adjust_c[2] & 0x3FF) << 20);
+
+	SDE_REG_WRITE(c, QSEED3_DE_SHARPEN + offset, sharp_lvl);
+	SDE_REG_WRITE(c, QSEED3_DE_SHARPEN_CTL + offset, sharp_ctl);
+	SDE_REG_WRITE(c, QSEED3_DE_SHAPE_CTL + offset, shape_ctl);
+	SDE_REG_WRITE(c, QSEED3_DE_THRESHOLD + offset, de_thr);
+	SDE_REG_WRITE(c, QSEED3_DE_ADJUST_DATA_0 + offset, adjust_a);
+	SDE_REG_WRITE(c, QSEED3_DE_ADJUST_DATA_1 + offset, adjust_b);
+	SDE_REG_WRITE(c, QSEED3_DE_ADJUST_DATA_2 + offset, adjust_c);
+
+}
+
+void sde_hw_setup_scaler3(struct sde_hw_blk_reg_map *c,
+		struct sde_hw_scaler3_cfg *scaler3_cfg,
+		u32 scaler_offset, u32 scaler_version,
+		const struct sde_format *format)
+{
+	u32 op_mode = 0;
+	u32 phase_init, preload, src_y_rgb, src_uv, dst;
+
+	if (!scaler3_cfg->enable)
+		goto end;
+
+	op_mode |= BIT(0);
+	op_mode |= (scaler3_cfg->y_rgb_filter_cfg & 0x3) << 16;
+
+	if (format && SDE_FORMAT_IS_YUV(format)) {
+		op_mode |= BIT(12);
+		op_mode |= (scaler3_cfg->uv_filter_cfg & 0x3) << 24;
+	}
+
+	op_mode |= (scaler3_cfg->blend_cfg & 1) << 31;
+	op_mode |= (scaler3_cfg->dir_en) ? BIT(4) : 0;
+
+	preload =
+		((scaler3_cfg->preload_x[0] & 0x7F) << 0) |
+		((scaler3_cfg->preload_y[0] & 0x7F) << 8) |
+		((scaler3_cfg->preload_x[1] & 0x7F) << 16) |
+		((scaler3_cfg->preload_y[1] & 0x7F) << 24);
+
+	src_y_rgb = (scaler3_cfg->src_width[0] & 0x1FFFF) |
+		((scaler3_cfg->src_height[0] & 0x1FFFF) << 16);
+
+	src_uv = (scaler3_cfg->src_width[1] & 0x1FFFF) |
+		((scaler3_cfg->src_height[1] & 0x1FFFF) << 16);
+
+	dst = (scaler3_cfg->dst_width & 0x1FFFF) |
+		((scaler3_cfg->dst_height & 0x1FFFF) << 16);
+
+	if (scaler3_cfg->de.enable) {
+		_sde_hw_setup_scaler3_de(c, &scaler3_cfg->de, scaler_offset);
+		op_mode |= BIT(8);
+	}
+
+	if (scaler3_cfg->lut_flag)
+		_sde_hw_setup_scaler3_lut(c, scaler3_cfg,
+								scaler_offset);
+
+	if (scaler_version == 0x1002) {
+		phase_init =
+			((scaler3_cfg->init_phase_x[0] & 0x3F) << 0) |
+			((scaler3_cfg->init_phase_y[0] & 0x3F) << 8) |
+			((scaler3_cfg->init_phase_x[1] & 0x3F) << 16) |
+			((scaler3_cfg->init_phase_y[1] & 0x3F) << 24);
+		SDE_REG_WRITE(c, QSEED3_PHASE_INIT + scaler_offset, phase_init);
+	} else {
+		SDE_REG_WRITE(c, QSEED3_PHASE_INIT_Y_H + scaler_offset,
+			scaler3_cfg->init_phase_x[0] & 0x1FFFFF);
+		SDE_REG_WRITE(c, QSEED3_PHASE_INIT_Y_V + scaler_offset,
+			scaler3_cfg->init_phase_y[0] & 0x1FFFFF);
+		SDE_REG_WRITE(c, QSEED3_PHASE_INIT_UV_H + scaler_offset,
+			scaler3_cfg->init_phase_x[1] & 0x1FFFFF);
+		SDE_REG_WRITE(c, QSEED3_PHASE_INIT_UV_V + scaler_offset,
+			scaler3_cfg->init_phase_y[1] & 0x1FFFFF);
+	}
+
+	SDE_REG_WRITE(c, QSEED3_PHASE_STEP_Y_H + scaler_offset,
+		scaler3_cfg->phase_step_x[0] & 0xFFFFFF);
+
+	SDE_REG_WRITE(c, QSEED3_PHASE_STEP_Y_V + scaler_offset,
+		scaler3_cfg->phase_step_y[0] & 0xFFFFFF);
+
+	SDE_REG_WRITE(c, QSEED3_PHASE_STEP_UV_H + scaler_offset,
+		scaler3_cfg->phase_step_x[1] & 0xFFFFFF);
+
+	SDE_REG_WRITE(c, QSEED3_PHASE_STEP_UV_V + scaler_offset,
+		scaler3_cfg->phase_step_y[1] & 0xFFFFFF);
+
+	SDE_REG_WRITE(c, QSEED3_PRELOAD + scaler_offset, preload);
+
+	SDE_REG_WRITE(c, QSEED3_SRC_SIZE_Y_RGB_A + scaler_offset, src_y_rgb);
+
+	SDE_REG_WRITE(c, QSEED3_SRC_SIZE_UV + scaler_offset, src_uv);
+
+	SDE_REG_WRITE(c, QSEED3_DST_SIZE + scaler_offset, dst);
+
+end:
+	if (format && !SDE_FORMAT_IS_DX(format))
+		op_mode |= BIT(14);
+
+	if (format && format->alpha_enable) {
+		op_mode |= BIT(10);
+		if (scaler_version == 0x1002)
+			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30;
+		else
+			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29;
+	}
+
+	SDE_REG_WRITE(c, QSEED3_OP_MODE + scaler_offset, op_mode);
+}
+
+u32 sde_hw_get_scaler3_ver(struct sde_hw_blk_reg_map *c,
+			u32 scaler_offset)
+{
+	return SDE_REG_READ(c, QSEED3_HW_VERSION + scaler_offset);
+}
+
 void sde_hw_csc_setup(struct sde_hw_blk_reg_map *c,
 		u32 csc_reg_off,
 		struct sde_csc_cfg *data, bool csc10)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h
index aa3d5b9..720e113 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h
@@ -39,6 +39,125 @@
 	u32 log_mask;
 };
 
+/**
+ * struct sde_hw_scaler3_de_cfg : QSEEDv3 detail enhancer configuration
+ * @enable:         detail enhancer enable/disable
+ * @sharpen_level1: sharpening strength for noise
+ * @sharpen_level2: sharpening strength for signal
+ * @ clip:          clip shift
+ * @ limit:         limit value
+ * @ thr_quiet:     quiet threshold
+ * @ thr_dieout:    dieout threshold
+ * @ thr_high:      low threshold
+ * @ thr_high:      high threshold
+ * @ prec_shift:    precision shift
+ * @ adjust_a:      A-coefficients for mapping curve
+ * @ adjust_b:      B-coefficients for mapping curve
+ * @ adjust_c:      C-coefficients for mapping curve
+ */
+struct sde_hw_scaler3_de_cfg {
+	u32 enable;
+	int16_t sharpen_level1;
+	int16_t sharpen_level2;
+	uint16_t clip;
+	uint16_t limit;
+	uint16_t thr_quiet;
+	uint16_t thr_dieout;
+	uint16_t thr_low;
+	uint16_t thr_high;
+	uint16_t prec_shift;
+	int16_t adjust_a[SDE_MAX_DE_CURVES];
+	int16_t adjust_b[SDE_MAX_DE_CURVES];
+	int16_t adjust_c[SDE_MAX_DE_CURVES];
+};
+
+
+/**
+ * struct sde_hw_scaler3_cfg : QSEEDv3 configuration
+ * @enable:        scaler enable
+ * @dir_en:        direction detection block enable
+ * @ init_phase_x: horizontal initial phase
+ * @ phase_step_x: horizontal phase step
+ * @ init_phase_y: vertical initial phase
+ * @ phase_step_y: vertical phase step
+ * @ preload_x:    horizontal preload value
+ * @ preload_y:    vertical preload value
+ * @ src_width:    source width
+ * @ src_height:   source height
+ * @ dst_width:    destination width
+ * @ dst_height:   destination height
+ * @ y_rgb_filter_cfg: y/rgb plane filter configuration
+ * @ uv_filter_cfg: uv plane filter configuration
+ * @ alpha_filter_cfg: alpha filter configuration
+ * @ blend_cfg:    blend coefficients configuration
+ * @ lut_flag:     scaler LUT update flags
+ *                 0x1 swap LUT bank
+ *                 0x2 update 2D filter LUT
+ *                 0x4 update y circular filter LUT
+ *                 0x8 update uv circular filter LUT
+ *                 0x10 update y separable filter LUT
+ *                 0x20 update uv separable filter LUT
+ * @ dir_lut_idx:  2D filter LUT index
+ * @ y_rgb_cir_lut_idx: y circular filter LUT index
+ * @ uv_cir_lut_idx: uv circular filter LUT index
+ * @ y_rgb_sep_lut_idx: y circular filter LUT index
+ * @ uv_sep_lut_idx: uv separable filter LUT index
+ * @ dir_lut:      pointer to 2D LUT
+ * @ cir_lut:      pointer to circular filter LUT
+ * @ sep_lut:      pointer to separable filter LUT
+ * @ de: detail enhancer configuration
+ */
+struct sde_hw_scaler3_cfg {
+	u32 enable;
+	u32 dir_en;
+	int32_t init_phase_x[SDE_MAX_PLANES];
+	int32_t phase_step_x[SDE_MAX_PLANES];
+	int32_t init_phase_y[SDE_MAX_PLANES];
+	int32_t phase_step_y[SDE_MAX_PLANES];
+
+	u32 preload_x[SDE_MAX_PLANES];
+	u32 preload_y[SDE_MAX_PLANES];
+	u32 src_width[SDE_MAX_PLANES];
+	u32 src_height[SDE_MAX_PLANES];
+
+	u32 dst_width;
+	u32 dst_height;
+
+	u32 y_rgb_filter_cfg;
+	u32 uv_filter_cfg;
+	u32 alpha_filter_cfg;
+	u32 blend_cfg;
+
+	u32 lut_flag;
+	u32 dir_lut_idx;
+
+	u32 y_rgb_cir_lut_idx;
+	u32 uv_cir_lut_idx;
+	u32 y_rgb_sep_lut_idx;
+	u32 uv_sep_lut_idx;
+	u32 *dir_lut;
+	size_t dir_len;
+	u32 *cir_lut;
+	size_t cir_len;
+	u32 *sep_lut;
+	size_t sep_len;
+
+	/*
+	 * Detail enhancer settings
+	 */
+	struct sde_hw_scaler3_de_cfg de;
+};
+
+struct sde_hw_scaler3_lut_cfg {
+	bool is_configured;
+	u32 *dir_lut;
+	size_t dir_len;
+	u32 *cir_lut;
+	size_t cir_len;
+	u32 *sep_lut;
+	size_t sep_len;
+};
+
 u32 *sde_hw_util_get_log_mask_ptr(void);
 
 void sde_reg_write(struct sde_hw_blk_reg_map *c,
@@ -58,6 +177,17 @@
 
 void *sde_hw_util_get_dir(void);
 
+void sde_set_scaler_v2(struct sde_hw_scaler3_cfg *cfg,
+		const struct sde_drm_scaler_v2 *scale_v2);
+
+void sde_hw_setup_scaler3(struct sde_hw_blk_reg_map *c,
+		struct sde_hw_scaler3_cfg *scaler3_cfg,
+		u32 scaler_offset, u32 scaler_version,
+		const struct sde_format *format);
+
+u32 sde_hw_get_scaler3_ver(struct sde_hw_blk_reg_map *c,
+		u32 scaler_offset);
+
 void sde_hw_csc_setup(struct sde_hw_blk_reg_map  *c,
 		u32 csc_reg_off,
 		struct sde_csc_cfg *data, bool csc10);
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index eeb7a00..10435da 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -19,6 +19,23 @@
 #include "sde_irq.h"
 #include "sde_core_irq.h"
 
+static uint32_t g_sde_irq_status;
+
+void sde_irq_update(struct msm_kms *msm_kms, bool enable)
+{
+	struct sde_kms *sde_kms = to_sde_kms(msm_kms);
+
+	if (!msm_kms || !sde_kms) {
+		SDE_ERROR("invalid kms arguments\n");
+		return;
+	}
+
+	if (enable)
+		enable_irq(sde_kms->irq_num);
+	else
+		disable_irq(sde_kms->irq_num);
+}
+
 irqreturn_t sde_irq(struct msm_kms *kms)
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
@@ -27,6 +44,9 @@
 	sde_kms->hw_intr->ops.get_interrupt_sources(sde_kms->hw_intr,
 			&interrupts);
 
+	/* store irq status in case of irq-storm debugging */
+	g_sde_irq_status = interrupts;
+
 	/*
 	 * Taking care of MDP interrupt
 	 */
@@ -40,13 +60,30 @@
 	 */
 	while (interrupts) {
 		irq_hw_number_t hwirq = fls(interrupts) - 1;
+		unsigned int mapping;
+		int rc;
 
-		generic_handle_irq(irq_find_mapping(
-				sde_kms->irq_controller.domain, hwirq));
+		mapping = irq_find_mapping(sde_kms->irq_controller.domain,
+				hwirq);
+		if (mapping == 0) {
+			SDE_EVT32(hwirq, SDE_EVTLOG_ERROR);
+			goto error;
+		}
+
+		rc = generic_handle_irq(mapping);
+		if (rc < 0) {
+			SDE_EVT32(hwirq, mapping, rc, SDE_EVTLOG_ERROR);
+			goto error;
+		}
+
 		interrupts &= ~(1 << hwirq);
 	}
 
 	return IRQ_HANDLED;
+
+error:
+	/* bad situation, inform irq system, it may disable overall MDSS irq */
+	return IRQ_NONE;
 }
 
 void sde_irq_preinstall(struct msm_kms *kms)
@@ -59,6 +96,16 @@
 	}
 
 	sde_core_irq_preinstall(sde_kms);
+
+	sde_kms->irq_num = platform_get_irq(sde_kms->dev->platformdev, 0);
+	if (sde_kms->irq_num < 0) {
+		SDE_ERROR("invalid irq number %d\n", sde_kms->irq_num);
+		return;
+	}
+
+	/* disable irq until power event enables it */
+	if (!sde_kms->cont_splash_en)
+		irq_set_status_flags(sde_kms->irq_num, IRQ_NOAUTOEN);
 }
 
 int sde_irq_postinstall(struct msm_kms *kms)
diff --git a/drivers/gpu/drm/msm/sde/sde_irq.h b/drivers/gpu/drm/msm/sde/sde_irq.h
index e1090071..5bb299a 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.h
+++ b/drivers/gpu/drm/msm/sde/sde_irq.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -56,4 +56,11 @@
  */
 irqreturn_t sde_irq(struct msm_kms *kms);
 
+/**
+ * sde_irq_update - enable/disable IRQ line
+ * @kms:		pointer to kms context
+ * @enable:		enable:true, disable:false
+ */
+void sde_irq_update(struct msm_kms *kms, bool enable);
+
 #endif /* __SDE_IRQ_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index 1a6585a..5d359be 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -20,8 +20,11 @@
 
 #include <drm/drm_crtc.h>
 #include <linux/debugfs.h>
+#include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/dma-buf.h>
+#include <linux/memblock.h>
+#include <linux/bootmem.h>
 
 #include "msm_drv.h"
 #include "msm_mmu.h"
@@ -84,6 +87,7 @@
 
 static int sde_kms_hw_init(struct msm_kms *kms);
 static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms);
+static int _sde_kms_mmu_init(struct sde_kms *sde_kms);
 static int _sde_kms_register_events(struct msm_kms *kms,
 		struct drm_mode_object *obj, u32 event, bool en);
 bool sde_is_custom_client(void)
@@ -99,6 +103,7 @@
 	struct msm_drm_private *priv;
 	struct sde_danger_safe_status status;
 	int i;
+	int rc;
 
 	if (!kms || !kms->dev || !kms->dev->dev_private || !kms->hw_mdp) {
 		SDE_ERROR("invalid arg(s)\n");
@@ -108,7 +113,13 @@
 	priv = kms->dev->dev_private;
 	memset(&status, 0, sizeof(struct sde_danger_safe_status));
 
-	sde_power_resource_enable(&priv->phandle, kms->core_client, true);
+	rc = sde_power_resource_enable(&priv->phandle, kms->core_client, true);
+	if (rc) {
+		SDE_ERROR("failed to enable power resource %d\n", rc);
+		SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+		return rc;
+	}
+
 	if (danger_status) {
 		seq_puts(s, "\nDanger signal status:\n");
 		if (kms->hw_mdp->ops.get_danger_status)
@@ -433,8 +444,7 @@
 		 * secure transition can be initiated.
 		 */
 		ops = sde_crtc_get_secure_transition_ops(crtc,
-				old_crtc_state,
-				old_valid_fb);
+				old_crtc_state, old_valid_fb);
 		if (ops < 0) {
 			SDE_ERROR("invalid secure operations %x\n", ops);
 			return ops;
@@ -444,9 +454,8 @@
 			goto no_ops;
 
 		SDE_DEBUG("%d:secure operations(%x) started on state:%pK\n",
-				crtc->base.id,
-				ops,
-				crtc->state);
+				crtc->base.id, ops, crtc->state);
+		SDE_EVT32(DRMID(crtc), ops, crtc->state, old_valid_fb);
 
 		/* 3. Perform operations needed for secure transition */
 		if  (ops & SDE_KMS_OPS_WAIT_FOR_TX_DONE) {
@@ -485,6 +494,7 @@
 				}
 			}
 		}
+		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
 		SDE_DEBUG("secure operations completed\n");
 	}
 
@@ -492,6 +502,30 @@
 	return 0;
 }
 
+static int _sde_kms_release_splash_buffer(unsigned int mem_addr,
+							unsigned int size)
+{
+	unsigned long pfn_start, pfn_end, pfn_idx;
+	int ret = 0;
+
+	if (!mem_addr || !size)
+		SDE_ERROR("invalid params\n");
+
+	pfn_start = mem_addr >> PAGE_SHIFT;
+	pfn_end = (mem_addr + size) >> PAGE_SHIFT;
+
+	ret = memblock_free(mem_addr, size);
+	if (ret) {
+		SDE_ERROR("continuous splash memory free failed:%d\n", ret);
+		return ret;
+	}
+	for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
+		free_reserved_page(pfn_to_page(pfn_idx));
+
+	return ret;
+
+}
+
 static void sde_kms_prepare_commit(struct msm_kms *kms,
 		struct drm_atomic_state *state)
 {
@@ -499,6 +533,11 @@
 	struct msm_drm_private *priv;
 	struct drm_device *dev;
 	struct drm_encoder *encoder;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	int i, rc = 0;
+	struct drm_plane *plane;
+	bool commit_no_planes = true;
 
 	if (!kms)
 		return;
@@ -509,11 +548,46 @@
 		return;
 	priv = dev->dev_private;
 
-	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+	rc = sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+			true);
+	if (rc) {
+		SDE_ERROR("failed to enable power resource %d\n", rc);
+		SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+		return;
+	}
 
-	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
-		if (encoder->crtc != NULL)
+	for_each_crtc_in_state(state, crtc, crtc_state, i) {
+		list_for_each_entry(encoder, &dev->mode_config.encoder_list,
+				head) {
+			if (encoder->crtc != crtc)
+				continue;
+
 			sde_encoder_prepare_commit(encoder);
+		}
+	}
+
+	if (sde_kms->splash_data.smmu_handoff_pending) {
+		list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+			if (plane->state != NULL &&
+					plane->state->crtc != NULL)
+				commit_no_planes = false;
+	}
+
+	if (sde_kms->splash_data.smmu_handoff_pending && commit_no_planes) {
+
+		rc = sde_unstage_pipe_for_cont_splash(&sde_kms->splash_data,
+						sde_kms->mmio);
+		if (rc)
+			SDE_ERROR("pipe staging failed: %d\n", rc);
+
+		rc = _sde_kms_release_splash_buffer(
+				sde_kms->splash_data.splash_base,
+				sde_kms->splash_data.splash_size);
+		if (rc)
+			SDE_ERROR("release of splash memory failed %d\n", rc);
+
+		sde_kms->splash_data.smmu_handoff_pending = false;
+	}
 
 	/*
 	 * NOTE: for secure use cases we want to apply the new HW
@@ -526,14 +600,24 @@
 static void sde_kms_commit(struct msm_kms *kms,
 		struct drm_atomic_state *old_state)
 {
+	struct sde_kms *sde_kms;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
 	int i;
 
+	if (!kms || !old_state)
+		return;
+	sde_kms = to_sde_kms(kms);
+
+	if (!sde_kms_power_resource_is_enabled(sde_kms->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
 		if (crtc->state->active) {
 			SDE_EVT32(DRMID(crtc));
-			sde_crtc_commit_kickoff(crtc);
+			sde_crtc_commit_kickoff(crtc, old_crtc_state);
 		}
 	}
 }
@@ -545,7 +629,9 @@
 	struct msm_drm_private *priv;
 	struct drm_crtc *crtc;
 	struct drm_crtc_state *old_crtc_state;
-	int i;
+	struct drm_connector *connector;
+	struct drm_connector_state *old_conn_state;
+	int i, rc = 0;
 
 	if (!kms || !old_state)
 		return;
@@ -555,12 +641,38 @@
 		return;
 	priv = sde_kms->dev->dev_private;
 
+	if (!sde_kms_power_resource_is_enabled(sde_kms->dev)) {
+		SDE_ERROR("power resource is not enabled\n");
+		return;
+	}
+
 	for_each_crtc_in_state(old_state, crtc, old_crtc_state, i)
 		sde_crtc_complete_commit(crtc, old_crtc_state);
 
+	for_each_connector_in_state(old_state, connector, old_conn_state, i) {
+		struct sde_connector *c_conn;
+
+		c_conn = to_sde_connector(connector);
+		if (!c_conn->ops.post_kickoff)
+			continue;
+		rc = c_conn->ops.post_kickoff(connector);
+		if (rc) {
+			pr_err("Connector Post kickoff failed rc=%d\n",
+					 rc);
+		}
+	}
+
 	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
 
 	SDE_EVT32_VERBOSE(SDE_EVTLOG_FUNC_EXIT);
+
+	if (sde_kms->cont_splash_en) {
+		SDE_DEBUG("Disabling cont_splash feature\n");
+		sde_kms->cont_splash_en = false;
+		sde_power_resource_enable(&priv->phandle,
+				sde_kms->core_client, false);
+		SDE_DEBUG("removing Vote for MDP Resources\n");
+	}
 }
 
 static void sde_kms_wait_for_commit_done(struct msm_kms *kms,
@@ -745,7 +857,7 @@
 		struct sde_kms *sde_kms)
 {
 	static const struct sde_connector_ops dsi_ops = {
-		.post_init =  dsi_conn_post_init,
+		.set_info_blob = dsi_conn_set_info_blob,
 		.detect =     dsi_conn_detect,
 		.get_modes =  dsi_connector_get_modes,
 		.put_modes =  dsi_connector_put_modes,
@@ -758,16 +870,21 @@
 		.set_power = dsi_display_set_power,
 		.get_mode_info = dsi_conn_get_mode_info,
 		.get_dst_format = dsi_display_get_dst_format,
+		.post_kickoff = dsi_conn_post_kickoff,
+		.check_status = dsi_display_check_status,
+		.enable_event = dsi_conn_enable_event
 	};
 	static const struct sde_connector_ops wb_ops = {
 		.post_init =    sde_wb_connector_post_init,
+		.set_info_blob = sde_wb_connector_set_info_blob,
 		.detect =       sde_wb_connector_detect,
 		.get_modes =    sde_wb_connector_get_modes,
 		.set_property = sde_wb_connector_set_property,
 		.get_info =     sde_wb_get_info,
 		.soft_reset =   NULL,
 		.get_mode_info = sde_wb_get_mode_info,
-		.get_dst_format = NULL
+		.get_dst_format = NULL,
+		.check_status = NULL,
 	};
 	static const struct sde_connector_ops dp_ops = {
 		.post_init  = dp_connector_post_init,
@@ -776,6 +893,9 @@
 		.mode_valid = dp_connector_mode_valid,
 		.get_info   = dp_connector_get_info,
 		.get_mode_info  = dp_connector_get_mode_info,
+		.send_hpd_event = dp_connector_send_hpd_event,
+		.check_status = NULL,
+		.pre_kickoff  = dp_connector_pre_kickoff,
 	};
 	struct msm_display_info info;
 	struct drm_encoder *encoder;
@@ -1070,6 +1190,30 @@
 }
 
 /**
+ * sde_kms_timeline_status - provides current timeline status
+ *    This API should be called without mode config lock.
+ * @dev: Pointer to drm device
+ */
+void sde_kms_timeline_status(struct drm_device *dev)
+{
+	struct drm_crtc *crtc;
+	struct drm_connector *conn;
+
+	if (!dev) {
+		SDE_ERROR("invalid drm device node\n");
+		return;
+	}
+
+	drm_for_each_crtc(crtc, dev)
+		sde_crtc_timeline_status(crtc);
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_for_each_connector(conn, dev)
+		sde_conn_timeline_status(conn);
+	mutex_unlock(&dev->mode_config.mutex);
+}
+
+/**
  * struct sde_kms_fbo_fb - framebuffer creation list
  * @list: list of framebuffer attached to framebuffer object
  * @fb: Pointer to framebuffer attached to framebuffer object
@@ -1403,6 +1547,12 @@
 	if (!priv)
 		return;
 
+	if (sde_kms->genpd_init) {
+		sde_kms->genpd_init = false;
+		pm_genpd_remove(&sde_kms->genpd);
+		of_genpd_del_provider(pdev->dev.of_node);
+	}
+
 	if (sde_kms->hw_intr)
 		sde_hw_intr_destroy(sde_kms->hw_intr);
 	sde_kms->hw_intr = NULL;
@@ -1543,15 +1693,245 @@
 	kfree(sde_kms);
 }
 
+static void _sde_kms_plane_force_remove(struct drm_plane *plane,
+			struct drm_atomic_state *state)
+{
+	struct drm_plane_state *plane_state;
+	int ret = 0;
+
+	if (!plane->crtc)
+		return;
+
+	plane_state = drm_atomic_get_plane_state(state, plane);
+	if (IS_ERR(plane_state)) {
+		ret = PTR_ERR(plane_state);
+		SDE_ERROR("error %d getting plane %d state\n",
+				ret, plane->base.id);
+		return;
+	}
+
+	plane->old_fb = plane->fb;
+
+	SDE_DEBUG("disabling plane %d\n", plane->base.id);
+
+	ret = __drm_atomic_helper_disable_plane(plane, plane_state);
+	if (ret != 0)
+		SDE_ERROR("error %d disabling plane %d\n", ret,
+				plane->base.id);
+}
+
+static int _sde_kms_remove_fbs(struct sde_kms *sde_kms, struct drm_file *file,
+		struct drm_atomic_state *state)
+{
+	struct drm_device *dev = sde_kms->dev;
+	struct drm_framebuffer *fb, *tfb;
+	struct list_head fbs;
+	struct drm_plane *plane;
+	int ret = 0;
+	u32 plane_mask = 0;
+
+	INIT_LIST_HEAD(&fbs);
+
+	list_for_each_entry_safe(fb, tfb, &file->fbs, filp_head) {
+		if (drm_framebuffer_read_refcount(fb) > 1) {
+			list_move_tail(&fb->filp_head, &fbs);
+
+			drm_for_each_plane(plane, dev) {
+				if (plane->fb == fb) {
+					plane_mask |=
+						1 << drm_plane_index(plane);
+					 _sde_kms_plane_force_remove(
+								plane, state);
+				}
+			}
+		} else {
+			list_del_init(&fb->filp_head);
+			drm_framebuffer_unreference(fb);
+		}
+	}
+
+	if (list_empty(&fbs)) {
+		SDE_DEBUG("skip commit as no fb(s)\n");
+		drm_atomic_state_free(state);
+		return 0;
+	}
+
+	SDE_DEBUG("committing after removing all the pipes\n");
+	ret = drm_atomic_commit(state);
+
+	if (ret) {
+		/*
+		 * move the fbs back to original list, so it would be
+		 * handled during drm_release
+		 */
+		list_for_each_entry_safe(fb, tfb, &fbs, filp_head)
+			list_move_tail(&fb->filp_head, &file->fbs);
+
+		SDE_ERROR("atomic commit failed in preclose, ret:%d\n", ret);
+		goto end;
+	}
+
+	while (!list_empty(&fbs)) {
+		fb = list_first_entry(&fbs, typeof(*fb), filp_head);
+
+		list_del_init(&fb->filp_head);
+		drm_framebuffer_unreference(fb);
+	}
+
+end:
+	drm_atomic_clean_old_fb(dev, plane_mask, ret);
+
+	return ret;
+}
+
 static void sde_kms_preclose(struct msm_kms *kms, struct drm_file *file)
 {
 	struct sde_kms *sde_kms = to_sde_kms(kms);
 	struct drm_device *dev = sde_kms->dev;
 	struct msm_drm_private *priv = dev->dev_private;
 	unsigned int i;
+	struct drm_atomic_state *state = NULL;
+	int ret = 0;
 
 	for (i = 0; i < priv->num_crtcs; i++)
 		sde_crtc_cancel_pending_flip(priv->crtcs[i], file);
+
+	drm_modeset_lock_all(dev);
+	state = drm_atomic_state_alloc(dev);
+	if (!state) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	state->acquire_ctx = dev->mode_config.acquire_ctx;
+
+	for (i = 0; i < TEARDOWN_DEADLOCK_RETRY_MAX; i++) {
+		ret = _sde_kms_remove_fbs(sde_kms, file, state);
+		if (ret != -EDEADLK)
+			break;
+		drm_atomic_state_clear(state);
+		drm_atomic_legacy_backoff(state);
+	}
+
+end:
+	if ((ret != 0) && state)
+		drm_atomic_state_free(state);
+
+	SDE_DEBUG("sde preclose done, ret:%d\n", ret);
+	drm_modeset_unlock_all(dev);
+}
+
+static int _sde_kms_helper_reset_custom_properties(struct sde_kms *sde_kms,
+		struct drm_atomic_state *state)
+{
+	struct drm_device *dev = sde_kms->dev;
+	struct drm_plane *plane;
+	struct drm_plane_state *plane_state;
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *crtc_state;
+	struct drm_connector *conn;
+	struct drm_connector_state *conn_state;
+	int ret = 0;
+
+	drm_for_each_plane(plane, dev) {
+		plane_state = drm_atomic_get_plane_state(state, plane);
+		if (IS_ERR(plane_state)) {
+			ret = PTR_ERR(plane_state);
+			SDE_ERROR("error %d getting plane %d state\n",
+					ret, DRMID(plane));
+			return ret;
+		}
+
+		ret = sde_plane_helper_reset_custom_properties(plane,
+				plane_state);
+		if (ret) {
+			SDE_ERROR("error %d resetting plane props %d\n",
+					ret, DRMID(plane));
+			return ret;
+		}
+	}
+	drm_for_each_crtc(crtc, dev) {
+		crtc_state = drm_atomic_get_crtc_state(state, crtc);
+		if (IS_ERR(crtc_state)) {
+			ret = PTR_ERR(crtc_state);
+			SDE_ERROR("error %d getting crtc %d state\n",
+					ret, DRMID(crtc));
+			return ret;
+		}
+
+		ret = sde_crtc_helper_reset_custom_properties(crtc, crtc_state);
+		if (ret) {
+			SDE_ERROR("error %d resetting crtc props %d\n",
+					ret, DRMID(crtc));
+			return ret;
+		}
+	}
+
+	drm_for_each_connector(conn, dev) {
+		conn_state = drm_atomic_get_connector_state(state, conn);
+		if (IS_ERR(conn_state)) {
+			ret = PTR_ERR(conn_state);
+			SDE_ERROR("error %d getting connector %d state\n",
+					ret, DRMID(conn));
+			return ret;
+		}
+
+		ret = sde_connector_helper_reset_custom_properties(conn,
+				conn_state);
+		if (ret) {
+			SDE_ERROR("error %d resetting connector props %d\n",
+					ret, DRMID(conn));
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void sde_kms_lastclose(struct msm_kms *kms)
+{
+	struct sde_kms *sde_kms;
+	struct drm_device *dev;
+	struct drm_atomic_state *state;
+	int ret, i;
+
+	if (!kms) {
+		SDE_ERROR("invalid argument\n");
+		return;
+	}
+
+	sde_kms = to_sde_kms(kms);
+	dev = sde_kms->dev;
+
+	state = drm_atomic_state_alloc(dev);
+	if (!state)
+		return;
+
+	state->acquire_ctx = dev->mode_config.acquire_ctx;
+
+	for (i = 0; i < TEARDOWN_DEADLOCK_RETRY_MAX; i++) {
+		/* add reset of custom properties to the state */
+		ret = _sde_kms_helper_reset_custom_properties(sde_kms, state);
+		if (ret)
+			break;
+
+		ret = drm_atomic_commit(state);
+		if (ret != -EDEADLK)
+			break;
+
+		drm_atomic_state_clear(state);
+		drm_atomic_legacy_backoff(state);
+		SDE_DEBUG("deadlock backoff on attempt %d\n", i);
+	}
+
+	if (ret) {
+		/**
+		 * on success, atomic state object ownership transfers to
+		 * framework, otherwise, free it here
+		 */
+		drm_atomic_state_free(state);
+		SDE_ERROR("failed to run last close: %d\n", ret);
+	}
 }
 
 static int sde_kms_check_secure_transition(struct msm_kms *kms,
@@ -1560,10 +1940,10 @@
 	struct sde_kms *sde_kms;
 	struct drm_device *dev;
 	struct drm_crtc *crtc;
-	struct drm_crtc *sec_crtc = NULL, *temp_crtc = NULL;
+	struct drm_crtc *cur_crtc = NULL, *global_crtc = NULL;
 	struct drm_crtc_state *crtc_state;
-	int secure_crtc_cnt = 0, active_crtc_cnt = 0;
-	int secure_global_crtc_cnt = 0, active_mode_crtc_cnt = 0;
+	int active_crtc_cnt = 0, global_active_crtc_cnt = 0;
+	bool sec_session = false, global_sec_session = false;
 	int i;
 
 	if (!kms || !state) {
@@ -1571,56 +1951,64 @@
 		SDE_ERROR("invalid arguments\n");
 	}
 
-	/* iterate state object for active and secure crtc */
+	sde_kms = to_sde_kms(kms);
+	dev = sde_kms->dev;
+
+	/* iterate state object for active secure/non-secure crtc */
 	for_each_crtc_in_state(state, crtc, crtc_state, i) {
 		if (!crtc_state->active)
 			continue;
+
 		active_crtc_cnt++;
 		if (sde_crtc_get_secure_level(crtc, crtc_state) ==
-				SDE_DRM_SEC_ONLY) {
-			sec_crtc = crtc;
-			secure_crtc_cnt++;
-		}
+				SDE_DRM_SEC_ONLY)
+			sec_session = true;
+
+		cur_crtc = crtc;
 	}
 
-	/* bail out from further validation if no secure ctrc */
-	if (!secure_crtc_cnt)
-		return 0;
-
-	if ((secure_crtc_cnt > MAX_ALLOWED_SECURE_CLIENT_CNT) ||
-		(secure_crtc_cnt &&
-		 (active_crtc_cnt > MAX_ALLOWED_CRTC_CNT_DURING_SECURE))) {
-		SDE_ERROR("Secure check failed active:%d, secure:%d\n",
-				active_crtc_cnt, secure_crtc_cnt);
-		return -EPERM;
-	}
-
-	sde_kms = to_sde_kms(kms);
-	dev = sde_kms->dev;
 	/* iterate global list for active and secure crtc */
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-
 		if (!crtc->state->active)
 			continue;
 
-		active_mode_crtc_cnt++;
-
+		global_active_crtc_cnt++;
 		if (sde_crtc_get_secure_level(crtc, crtc->state) ==
-				SDE_DRM_SEC_ONLY) {
-			secure_global_crtc_cnt++;
-			temp_crtc = crtc;
-		}
+				SDE_DRM_SEC_ONLY)
+			global_sec_session = true;
+
+		global_crtc = crtc;
 	}
 
-	/**
-	 * if more than one crtc is active fail
-	 * check if the previous and current commit secure
-	 * are same
+	/*
+	 * - fail secure crtc commit, if any other crtc session is already
+	 *   in progress
+	 * - fail non-secure crtc commit, if any secure crtc session is already
+	 *   in progress
 	 */
-	if (secure_crtc_cnt && ((active_mode_crtc_cnt > 1) ||
-			(secure_global_crtc_cnt && (temp_crtc != sec_crtc))))
-		SDE_ERROR("Secure check failed active:%d crtc_id:%d\n",
-				active_mode_crtc_cnt, temp_crtc->base.id);
+	if (global_sec_session || sec_session) {
+		if ((global_active_crtc_cnt >
+					MAX_ALLOWED_CRTC_CNT_DURING_SECURE) ||
+		    (active_crtc_cnt > MAX_ALLOWED_CRTC_CNT_DURING_SECURE)) {
+			SDE_ERROR(
+			"Secure check failed global_active:%d active:%d\n",
+				global_active_crtc_cnt, active_crtc_cnt);
+			return -EPERM;
+
+		/*
+		 * As only one crtc is allowed during secure session, the crtc
+		 * in this commit should match with the global crtc, if it
+		 * exists
+		 */
+		} else if (global_crtc && (global_crtc != cur_crtc)) {
+			SDE_ERROR(
+			    "crtc%d-sec%d not allowed during crtc%d-sec%d\n",
+				cur_crtc ? cur_crtc->base.id : -1, sec_session,
+				global_crtc->base.id, global_sec_session);
+			return -EPERM;
+		}
+
+	}
 
 	return 0;
 }
@@ -1685,6 +2073,8 @@
 {
 	struct drm_device *dev = NULL;
 	struct sde_kms *sde_kms = NULL;
+	struct drm_connector *connector = NULL;
+	struct sde_connector *sde_conn = NULL;
 
 	if (!kms) {
 		SDE_ERROR("invalid kms\n");
@@ -1699,8 +2089,158 @@
 		return;
 	}
 
-	if (dev->mode_config.funcs->output_poll_changed)
-		dev->mode_config.funcs->output_poll_changed(dev);
+	if (!dev->mode_config.poll_enabled)
+		return;
+
+	mutex_lock(&dev->mode_config.mutex);
+	drm_for_each_connector(connector, dev) {
+		/* Only handle HPD capable connectors. */
+		if (!(connector->polled & DRM_CONNECTOR_POLL_HPD))
+			continue;
+
+		sde_conn = to_sde_connector(connector);
+
+		if (sde_conn->ops.send_hpd_event)
+			sde_conn->ops.send_hpd_event(sde_conn->display);
+	}
+	mutex_unlock(&dev->mode_config.mutex);
+
+}
+
+static int _sde_kms_gen_drm_mode(struct sde_kms *sde_kms,
+				void *display,
+				struct drm_display_mode *drm_mode)
+{
+	struct dsi_display_mode *modes = NULL;
+	u32 count = 0;
+	u32 size = 0;
+	int rc = 0;
+
+	rc = dsi_display_get_mode_count(display, &count);
+	if (rc) {
+		SDE_ERROR("failed to get num of modes, rc=%d\n", rc);
+		return rc;
+	}
+
+	SDE_DEBUG("num of modes = %d\n", count);
+	size = count * sizeof(*modes);
+	modes = kzalloc(size,  GFP_KERNEL);
+	if (!modes) {
+		count = 0;
+		goto end;
+	}
+
+	rc = dsi_display_get_modes(display, modes);
+	if (rc) {
+		SDE_ERROR("failed to get modes, rc=%d\n", rc);
+		count = 0;
+		goto error;
+	}
+
+	/* TODO; currently consider modes[0] as the preferred mode */
+	dsi_convert_to_drm_mode(&modes[0], drm_mode);
+
+	SDE_DEBUG("hdisplay = %d, vdisplay = %d\n",
+		drm_mode->hdisplay, drm_mode->vdisplay);
+	drm_mode_set_name(drm_mode);
+	drm_mode_set_crtcinfo(drm_mode, 0);
+error:
+	kfree(modes);
+end:
+	return rc;
+}
+
+static int sde_kms_cont_splash_config(struct msm_kms *kms)
+{
+	void *display;
+	struct dsi_display *dsi_display;
+	struct msm_display_info info;
+	struct drm_encoder *encoder = NULL;
+	struct drm_crtc *crtc = NULL;
+	int i, rc = 0;
+	struct drm_display_mode *drm_mode = NULL;
+	struct drm_device *dev;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+
+	if (!kms) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+
+	sde_kms = to_sde_kms(kms);
+	dev = sde_kms->dev;
+	if (!dev || !dev->platformdev) {
+		SDE_ERROR("invalid device\n");
+		return -EINVAL;
+	}
+
+	if (!sde_kms->cont_splash_en) {
+		DRM_INFO("cont_splash feature not enabled\n");
+		return rc;
+	}
+
+	/* Currently, we only support one dsi display configuration */
+	/* dsi */
+	for (i = 0; i < sde_kms->dsi_display_count; ++i) {
+		display = sde_kms->dsi_displays[i];
+		dsi_display = (struct dsi_display *)display;
+		SDE_DEBUG("display->name = %s\n", dsi_display->name);
+
+		if (dsi_display->bridge->base.encoder) {
+			encoder = dsi_display->bridge->base.encoder;
+			SDE_DEBUG("encoder name = %s\n", encoder->name);
+		}
+		memset(&info, 0x0, sizeof(info));
+		rc = dsi_display_get_info(&info, display);
+		if (rc) {
+			SDE_ERROR("dsi get_info %d failed\n", i);
+			encoder = NULL;
+			continue;
+		}
+		SDE_DEBUG("info.is_connected = %s, info.is_primary = %s\n",
+			((info.is_connected) ? "true" : "false"),
+			((info.is_primary) ? "true" : "false"));
+		break;
+	}
+
+	if (!encoder) {
+		SDE_ERROR("encoder not initialized\n");
+		return -EINVAL;
+	}
+
+	priv = sde_kms->dev->dev_private;
+	encoder->crtc = priv->crtcs[0];
+	crtc = encoder->crtc;
+	SDE_DEBUG("crtc id = %d\n", crtc->base.id);
+
+	crtc->state->encoder_mask = (1 << drm_encoder_index(encoder));
+	drm_mode = drm_mode_create(encoder->dev);
+	if (!drm_mode) {
+		SDE_ERROR("drm_mode create failed\n");
+		return -EINVAL;
+	}
+	_sde_kms_gen_drm_mode(sde_kms, display, drm_mode);
+	SDE_DEBUG("drm_mode->name = %s, id=%d, type=0x%x, flags=0x%x\n",
+			drm_mode->name, drm_mode->base.id,
+			drm_mode->type, drm_mode->flags);
+
+	/* Update CRTC drm structure */
+	crtc->state->active = true;
+	rc = drm_atomic_set_mode_for_crtc(crtc->state, drm_mode);
+	if (rc) {
+		SDE_ERROR("Failed: set mode for crtc. rc = %d\n", rc);
+		return rc;
+	}
+	drm_mode_copy(&crtc->state->adjusted_mode, drm_mode);
+	drm_mode_copy(&crtc->mode, drm_mode);
+
+	/* Update encoder structure */
+	sde_encoder_update_caps_for_cont_splash(encoder);
+
+	sde_crtc_update_cont_splash_mixer_settings(crtc);
+
+	return rc;
 }
 
 static int sde_kms_pm_suspend(struct device *dev)
@@ -1867,6 +2407,7 @@
 	.irq_uninstall   = sde_irq_uninstall,
 	.irq             = sde_irq,
 	.preclose        = sde_kms_preclose,
+	.lastclose       = sde_kms_lastclose,
 	.prepare_fence   = sde_kms_prepare_fence,
 	.prepare_commit  = sde_kms_prepare_commit,
 	.commit          = sde_kms_commit,
@@ -1882,6 +2423,7 @@
 	.pm_suspend      = sde_kms_pm_suspend,
 	.pm_resume       = sde_kms_pm_resume,
 	.destroy         = sde_kms_destroy,
+	.cont_splash_config = sde_kms_cont_splash_config,
 	.register_events = _sde_kms_register_events,
 	.get_address_space = _sde_kms_get_address_space,
 	.postopen = _sde_kms_post_open,
@@ -1957,15 +2499,173 @@
 	return ret;
 }
 
+/* the caller api needs to turn on clock before calling this function */
+static int _sde_kms_cont_splash_res_init(struct sde_kms *sde_kms)
+{
+	struct sde_mdss_cfg *cat;
+	struct drm_device *dev;
+	struct msm_drm_private *priv;
+	struct sde_splash_data *splash_data;
+	int i;
+	int ctl_top_cnt;
+
+	if (!sde_kms || !sde_kms->catalog) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+	cat = sde_kms->catalog;
+	dev = sde_kms->dev;
+	priv = dev->dev_private;
+	splash_data = &sde_kms->splash_data;
+	SDE_DEBUG("mixer_count=%d, ctl_count=%d, dsc_count=%d\n",
+			cat->mixer_count,
+			cat->ctl_count,
+			cat->dsc_count);
+
+	ctl_top_cnt = cat->ctl_count;
+
+	if (ctl_top_cnt > ARRAY_SIZE(splash_data->top)) {
+		SDE_ERROR("Mismatch in ctl_top array size\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < ctl_top_cnt; i++) {
+		sde_get_ctl_top_for_cont_splash(sde_kms->mmio,
+				&splash_data->top[i], i);
+		if (splash_data->top[i].intf_sel) {
+			splash_data->lm_cnt +=
+				sde_get_ctl_lm_for_cont_splash
+					(sde_kms->mmio,
+					sde_kms->catalog->mixer_count,
+					splash_data->lm_cnt,
+					splash_data->lm_ids,
+					&splash_data->top[i], i);
+			splash_data->ctl_ids[splash_data->ctl_top_cnt]
+							= i + CTL_0;
+			splash_data->ctl_top_cnt++;
+			sde_kms->cont_splash_en = true;
+		}
+	}
+
+	/* Skip DSC blk reads if cont_splash is disabled */
+	if (!sde_kms->cont_splash_en)
+		return 0;
+
+	splash_data->dsc_cnt =
+		sde_get_pp_dsc_for_cont_splash(sde_kms->mmio,
+				sde_kms->catalog->dsc_count,
+				splash_data->dsc_ids);
+	SDE_DEBUG("splash_data: ctl_top_cnt=%d, lm_cnt=%d, dsc_cnt=%d\n",
+		splash_data->ctl_top_cnt, splash_data->lm_cnt,
+		splash_data->dsc_cnt);
+
+	return 0;
+}
+
 static void sde_kms_handle_power_event(u32 event_type, void *usr)
 {
 	struct sde_kms *sde_kms = usr;
+	struct msm_kms *msm_kms;
 
+	msm_kms = &sde_kms->base;
 	if (!sde_kms)
 		return;
 
-	if (event_type == SDE_POWER_EVENT_POST_ENABLE)
+	SDE_DEBUG("event_type:%d\n", event_type);
+	SDE_EVT32_VERBOSE(event_type);
+
+	if (event_type == SDE_POWER_EVENT_POST_ENABLE) {
+		sde_irq_update(msm_kms, true);
 		sde_vbif_init_memtypes(sde_kms);
+	} else if (event_type == SDE_POWER_EVENT_PRE_DISABLE) {
+		sde_irq_update(msm_kms, false);
+	}
+}
+
+#define genpd_to_sde_kms(domain) container_of(domain, struct sde_kms, genpd)
+
+static int sde_kms_pd_enable(struct generic_pm_domain *genpd)
+{
+	struct sde_kms *sde_kms = genpd_to_sde_kms(genpd);
+	struct drm_device *dev;
+	struct msm_drm_private *priv;
+	int rc;
+
+	SDE_DEBUG("\n");
+
+	dev = sde_kms->dev;
+	if (!dev)
+		return -EINVAL;
+
+	priv = dev->dev_private;
+	if (!priv)
+		return -EINVAL;
+
+	SDE_EVT32(genpd->device_count);
+
+	rc = sde_power_resource_enable(&priv->phandle, priv->pclient, true);
+
+	return rc;
+}
+
+static int sde_kms_pd_disable(struct generic_pm_domain *genpd)
+{
+	struct sde_kms *sde_kms = genpd_to_sde_kms(genpd);
+	struct drm_device *dev;
+	struct msm_drm_private *priv;
+	int rc;
+
+	SDE_DEBUG("\n");
+
+	dev = sde_kms->dev;
+	if (!dev)
+		return -EINVAL;
+
+	priv = dev->dev_private;
+	if (!priv)
+		return -EINVAL;
+
+	SDE_EVT32(genpd->device_count);
+
+	rc = sde_power_resource_enable(&priv->phandle, priv->pclient, false);
+
+	return rc;
+}
+
+static int _sde_kms_get_splash_data(struct sde_splash_data *data)
+{
+	int ret = 0;
+	struct device_node *parent, *node;
+	struct resource r;
+
+	if (!data)
+		return -EINVAL;
+
+	parent = of_find_node_by_path("/reserved-memory");
+	if (!parent) {
+		SDE_ERROR("failed to find reserved-memory node\n");
+		return -EINVAL;
+	}
+
+	node = of_find_node_by_name(parent, "cont_splash_region");
+	if (!node) {
+		SDE_ERROR("failed to find splash memory reservation\n");
+		return -EINVAL;
+	}
+
+	if (of_address_to_resource(node, 0, &r)) {
+		SDE_ERROR("failed to find data for  splash memory\n");
+		return -EINVAL;
+	}
+
+	data->splash_base = (unsigned long)r.start;
+	data->splash_size = (r.end - r.start) + 1;
+
+	pr_info("found continuous splash base address:%lx size:%x\n",
+						data->splash_base,
+						data->splash_size);
+	data->smmu_handoff_pending = true;
+
+	return ret;
 }
 
 static int sde_kms_hw_init(struct msm_kms *kms)
@@ -1973,6 +2673,7 @@
 	struct sde_kms *sde_kms;
 	struct drm_device *dev;
 	struct msm_drm_private *priv;
+	bool splash_mem_found = false;
 	int i, rc = -EINVAL;
 
 	if (!kms) {
@@ -2065,6 +2766,14 @@
 		goto error;
 	}
 
+	rc = _sde_kms_get_splash_data(&sde_kms->splash_data);
+	if (rc) {
+		SDE_DEBUG("sde splash data fetch failed: %d\n", rc);
+		splash_mem_found = false;
+	} else {
+		splash_mem_found = true;
+	}
+
 	rc = sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
 		true);
 	if (rc) {
@@ -2089,14 +2798,11 @@
 	sde_dbg_init_dbg_buses(sde_kms->core_rev);
 
 	/*
-	 * Now we need to read the HW catalog and initialize resources such as
-	 * clocks, regulators, GDSC/MMAGIC, ioremap the register ranges etc
+	 * Attempt continuous splash handoff only if reserved
+	 * splash memory is found.
 	 */
-	rc = _sde_kms_mmu_init(sde_kms);
-	if (rc) {
-		SDE_ERROR("sde_kms_mmu_init failed: %d\n", rc);
-		goto power_error;
-	}
+	if (splash_mem_found)
+		_sde_kms_cont_splash_res_init(sde_kms);
 
 	/* Initialize reg dma block which is a singleton */
 	rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
@@ -2106,6 +2812,12 @@
 		goto power_error;
 	}
 
+	rc = _sde_kms_mmu_init(sde_kms);
+	if (rc) {
+		SDE_ERROR("sde_kms_mmu_init failed: %d\n", rc);
+		goto power_error;
+	}
+
 	rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio,
 			sde_kms->dev);
 	if (rc) {
@@ -2193,12 +2905,45 @@
 	 */
 	sde_kms_handle_power_event(SDE_POWER_EVENT_POST_ENABLE, sde_kms);
 	sde_kms->power_event = sde_power_handle_register_event(&priv->phandle,
-			SDE_POWER_EVENT_POST_ENABLE,
+			SDE_POWER_EVENT_POST_ENABLE |
+			SDE_POWER_EVENT_PRE_DISABLE,
 			sde_kms_handle_power_event, sde_kms, "kms");
 
-	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, false);
+	/* initialize power domain if defined */
+	if (of_find_property(dev->dev->of_node, "#power-domain-cells", NULL)) {
+		sde_kms->genpd.name = dev->unique;
+		sde_kms->genpd.power_off = sde_kms_pd_disable;
+		sde_kms->genpd.power_on = sde_kms_pd_enable;
+
+		rc = pm_genpd_init(&sde_kms->genpd, NULL, true);
+		if (rc < 0) {
+			SDE_ERROR("failed to init genpd provider %s: %d\n",
+					sde_kms->genpd.name, rc);
+			goto genpd_err;
+		}
+
+		rc = of_genpd_add_provider_simple(dev->dev->of_node,
+				&sde_kms->genpd);
+		if (rc < 0) {
+			SDE_ERROR("failed to add genpd provider %s: %d\n",
+					sde_kms->genpd.name, rc);
+			pm_genpd_remove(&sde_kms->genpd);
+			goto genpd_err;
+		}
+
+		sde_kms->genpd_init = true;
+		SDE_DEBUG("added genpd provider %s\n", sde_kms->genpd.name);
+	}
+
+	if (sde_kms->cont_splash_en)
+		SDE_DEBUG("Skipping MDP Resources disable\n");
+	else
+		sde_power_resource_enable(&priv->phandle,
+						sde_kms->core_client, false);
+
 	return 0;
 
+genpd_err:
 drm_obj_init_err:
 	sde_core_perf_destroy(&sde_kms->perf);
 hw_intr_init_err:
@@ -2263,3 +3008,9 @@
 
 	return ret;
 }
+
+int sde_kms_handle_recovery(struct drm_encoder *encoder)
+{
+	SDE_EVT32(DRMID(encoder), MSM_ENC_ACTIVE_REGION);
+	return sde_encoder_wait_for_event(encoder, MSM_ENC_ACTIVE_REGION);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 0ddfb30..501797b 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -20,6 +20,8 @@
 #define __SDE_KMS_H__
 
 #include <linux/msm_ion.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_qos.h>
 
 #include "msm_drv.h"
 #include "msm_kms.h"
@@ -29,6 +31,7 @@
 #include "sde_hw_catalog.h"
 #include "sde_hw_ctl.h"
 #include "sde_hw_lm.h"
+#include "sde_hw_pingpong.h"
 #include "sde_hw_interrupts.h"
 #include "sde_hw_wb.h"
 #include "sde_hw_top.h"
@@ -107,6 +110,9 @@
 #define SDE_KMS_OPS_CLEANUP_PLANE_FB                       BIT(2)
 #define SDE_KMS_OPS_PREPARE_PLANE_FB                       BIT(3)
 
+/* ESD status check interval in miliseconds */
+#define STATUS_CHECK_INTERVAL_MS 5000
+
 /*
  * struct sde_irq_callback - IRQ callback handlers
  * @list: list to callback
@@ -173,8 +179,12 @@
 	int core_rev;
 	struct sde_mdss_cfg *catalog;
 
+	struct generic_pm_domain genpd;
+	bool genpd_init;
+
 	struct msm_gem_address_space *aspace[MSM_SMMU_DOMAIN_MAX];
 	struct sde_power_client *core_client;
+	struct pm_qos_request pm_qos_cpu_req;
 
 	struct ion_client *iclient;
 	struct sde_power_event *power_event;
@@ -195,6 +205,7 @@
 
 	struct sde_hw_intr *hw_intr;
 	struct sde_irq irq_obj;
+	int irq_num;	/* mdss irq number */
 
 	struct sde_core_perf perf;
 
@@ -204,7 +215,8 @@
 
 	struct sde_rm rm;
 	bool rm_init;
-
+	struct sde_splash_data splash_data;
+	bool cont_splash_en;
 	struct sde_hw_vbif *hw_vbif[VBIF_MAX];
 	struct sde_hw_mdp *hw_mdp;
 	int dsi_display_count;
@@ -232,6 +244,23 @@
 bool sde_is_custom_client(void);
 
 /**
+ * sde_kms_power_resource_is_enabled - whether or not power resource is enabled
+ * @dev: Pointer to drm device
+ * Return: true if power resource is enabled; false otherwise
+ */
+static inline bool sde_kms_power_resource_is_enabled(struct drm_device *dev)
+{
+	struct msm_drm_private *priv;
+
+	if (!dev || !dev->dev_private)
+		return false;
+
+	priv = dev->dev_private;
+
+	return sde_power_resource_is_enabled(&priv->phandle);
+}
+
+/**
  * sde_kms_is_suspend_state - whether or not the system is pm suspended
  * @dev: Pointer to drm device
  * Return: Suspend status
@@ -358,7 +387,8 @@
  * @S: Pointer to sde_kms_info structure
  * Returns: Pointer to byte data
  */
-#define SDE_KMS_INFO_DATA(S)    ((S) ? ((struct sde_kms_info *)(S))->data : 0)
+#define SDE_KMS_INFO_DATA(S)    ((S) ? ((struct sde_kms_info *)(S))->data \
+							: NULL)
 
 /**
  * SDE_KMS_INFO_DATALEN - Macro for accessing sde_kms_info data length
@@ -554,4 +584,17 @@
 int sde_kms_mmu_attach(struct sde_kms *sde_kms, bool secure_only);
 int sde_kms_mmu_detach(struct sde_kms *sde_kms, bool secure_only);
 
+/**
+ * sde_kms_timeline_status - provides current timeline status
+ * @dev: Pointer to drm device
+ */
+void sde_kms_timeline_status(struct drm_device *dev);
+
+/**
+ * sde_kms_handle_recovery - handler function for FIFO overflow issue
+ * @encoder: pointer to drm encoder structure
+ * return: 0 on success; error code otherwise
+ */
+int sde_kms_handle_recovery(struct drm_encoder *encoder);
+
 #endif /* __sde_kms_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 40dad76..ab48c4a 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -38,14 +38,6 @@
 #include "sde_color_processing.h"
 #include "sde_hw_rot.h"
 
-static bool suspend_blank = true;
-module_param(suspend_blank, bool, 0400);
-MODULE_PARM_DESC(suspend_blank,
-		"If set, active planes will force their outputs to black,\n"
-		"by temporarily enabling the color fill, when recovering\n"
-		"from a system resume instead of attempting to display the\n"
-		"last provided frame buffer.");
-
 #define SDE_DEBUG_PLANE(pl, fmt, ...) SDE_DEBUG("plane%d " fmt,\
 		(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
 
@@ -66,6 +58,9 @@
 
 #define SDE_PLANE_COLOR_FILL_FLAG	BIT(31)
 
+#define TIME_MULTIPLEX_RECT(r0, r1, buffer_lines) \
+	 ((r0).y >= ((r1).y + (r1).h + buffer_lines))
+
 /* multirect rect index */
 enum {
 	R0,
@@ -103,6 +98,7 @@
  * @sbuf_mode: force stream buffer mode if set
  * @sbuf_writeback: force stream buffer writeback if set
  * @revalidate: force revalidation of all the plane properties
+ * @xin_halt_forced_clk: whether or not clocks were forced on for xin halt
  * @blob_rot_caps: Pointer to rotator capability blob
  */
 struct sde_plane {
@@ -128,6 +124,7 @@
 	u32 sbuf_mode;
 	u32 sbuf_writeback;
 	bool revalidate;
+	bool xin_halt_forced_clk;
 
 	struct sde_csc_cfg csc_cfg;
 	struct sde_csc_cfg *csc_usr_ptr;
@@ -223,6 +220,7 @@
 	u32 fixed_buff_size;
 	u32 total_fl;
 	u32 hflip_bytes;
+	u32 unused_space;
 
 	if (!plane || !fmt || !plane->state || !src_width || !fmt->bpp) {
 		SDE_ERROR("invalid arguments\n");
@@ -250,29 +248,36 @@
 		hflip_bytes = 0;
 
 	if (fmt->fetch_planes == SDE_PLANE_PSEUDO_PLANAR) {
+
+		unused_space = 23 * 128;
 		if (fmt->chroma_sample == SDE_CHROMA_420) {
 			/* NV12 */
-			total_fl = (fixed_buff_size / 2 - hflip_bytes) /
-				((src_width + 32) * fmt->bpp);
+			total_fl = (fixed_buff_size / 2 - hflip_bytes -
+				unused_space) / ((src_width + 32) * fmt->bpp);
 		} else {
 			/* non NV12 */
-			total_fl = (fixed_buff_size / 2 - hflip_bytes) * 2 /
-				((src_width + 32) * fmt->bpp);
+			total_fl = (fixed_buff_size / 2 - hflip_bytes -
+				unused_space) * 2 / ((src_width + 32) *
+				fmt->bpp);
 		}
 	} else {
+
+		unused_space = 6 * 128;
 		if (pstate->multirect_mode == SDE_SSPP_MULTIRECT_PARALLEL) {
-			total_fl = (fixed_buff_size / 2 - hflip_bytes) * 2 /
-				((src_width + 32) * fmt->bpp);
+			total_fl = (fixed_buff_size / 2 - hflip_bytes -
+				unused_space) * 2 / ((src_width + 32) *
+				fmt->bpp);
 		} else {
-			total_fl = (fixed_buff_size - hflip_bytes) * 2 /
-				((src_width + 32) * fmt->bpp);
+			total_fl = (fixed_buff_size - hflip_bytes -
+				unused_space) * 2 / ((src_width + 32) *
+				fmt->bpp);
 		}
 	}
 
-	SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u hf:%d fl:%u\n",
+	SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u hf:%d us:%d fl:%u\n",
 			plane->base.id, psde->pipe - SSPP_VIG0,
 			(char *)&fmt->base.pixel_format,
-			src_width, hflip_bytes, total_fl);
+			src_width, hflip_bytes, unused_space, total_fl);
 
 	return total_fl;
 }
@@ -375,6 +380,7 @@
 	struct sde_plane *psde;
 	const struct sde_format *fmt = NULL;
 	u32 danger_lut, safe_lut;
+	u32 total_fl = 0, lut_usage;
 
 	if (!plane || !fb) {
 		SDE_ERROR("invalid arguments\n");
@@ -393,27 +399,29 @@
 	if (!psde->is_rt_pipe) {
 		danger_lut = psde->catalog->perf.danger_lut_tbl
 				[SDE_QOS_LUT_USAGE_NRT];
-		safe_lut = psde->catalog->perf.safe_lut_tbl
-				[SDE_QOS_LUT_USAGE_NRT];
+		lut_usage = SDE_QOS_LUT_USAGE_NRT;
 	} else {
 		fmt = sde_get_sde_format_ext(
 				fb->pixel_format,
 				fb->modifier,
 				drm_format_num_planes(fb->pixel_format));
+		total_fl = _sde_plane_calc_fill_level(plane, fmt,
+				psde->pipe_cfg.src_rect.w);
 
 		if (fmt && SDE_FORMAT_IS_LINEAR(fmt)) {
 			danger_lut = psde->catalog->perf.danger_lut_tbl
 					[SDE_QOS_LUT_USAGE_LINEAR];
-			safe_lut = psde->catalog->perf.safe_lut_tbl
-					[SDE_QOS_LUT_USAGE_LINEAR];
+			lut_usage = SDE_QOS_LUT_USAGE_LINEAR;
 		} else {
 			danger_lut = psde->catalog->perf.danger_lut_tbl
 					[SDE_QOS_LUT_USAGE_MACROTILE];
-			safe_lut = psde->catalog->perf.safe_lut_tbl
-					[SDE_QOS_LUT_USAGE_MACROTILE];
+			lut_usage = SDE_QOS_LUT_USAGE_MACROTILE;
 		}
 	}
 
+	safe_lut = (u32) _sde_plane_get_qos_lut(
+			&psde->catalog->perf.sfe_lut_tbl[lut_usage], total_fl);
+
 	psde->pipe_qos_cfg.danger_lut = danger_lut;
 	psde->pipe_qos_cfg.safe_lut = safe_lut;
 
@@ -423,11 +431,11 @@
 			psde->pipe_qos_cfg.danger_lut,
 			psde->pipe_qos_cfg.safe_lut);
 
-	SDE_DEBUG("plane%u: pnum:%d fmt: %4.4s mode:%d luts[0x%x, 0x%x]\n",
+	SDE_DEBUG("plane%u: pnum:%d fmt:%4.4s mode:%d fl:%d luts[0x%x,0x%x]\n",
 		plane->base.id,
 		psde->pipe - SSPP_VIG0,
 		fmt ? (char *)&fmt->base.pixel_format : NULL,
-		fmt ? fmt->fetch_mode : -1,
+		fmt ? fmt->fetch_mode : -1, total_fl,
 		psde->pipe_qos_cfg.danger_lut,
 		psde->pipe_qos_cfg.safe_lut);
 
@@ -510,6 +518,7 @@
 	struct sde_plane *psde;
 	struct msm_drm_private *priv;
 	struct sde_kms *sde_kms;
+	int rc;
 
 	if (!plane || !plane->dev) {
 		SDE_ERROR("invalid arguments\n");
@@ -528,7 +537,13 @@
 	if (!psde->is_rt_pipe)
 		goto end;
 
-	sde_power_resource_enable(&priv->phandle, sde_kms->core_client, true);
+	rc = sde_power_resource_enable(&priv->phandle, sde_kms->core_client,
+			true);
+	if (rc) {
+		SDE_ERROR("failed to enable power resource %d\n", rc);
+		SDE_EVT32(rc, SDE_EVTLOG_ERROR);
+		return rc;
+	}
 
 	_sde_plane_set_qos_ctrl(plane, enable, SDE_PLANE_QOS_PANIC_CTRL);
 
@@ -693,7 +708,10 @@
 		sde_sync_put(pstate->input_fence);
 
 	/* get fence pointer for later */
-	pstate->input_fence = sde_sync_get(fd);
+	if (fd == 0)
+		pstate->input_fence = NULL;
+	else
+		pstate->input_fence = sde_sync_get(fd);
 
 	SDE_DEBUG_PLANE(psde, "0x%llX\n", fd);
 }
@@ -810,6 +828,7 @@
 				SDE_ERROR_PLANE(psde, "%ums timeout on %08X\n",
 						wait_ms, prefix);
 				psde->is_error = true;
+				sde_kms_timeline_status(plane->dev);
 				ret = -ETIMEDOUT;
 				break;
 			case -ERESTARTSYS:
@@ -878,7 +897,6 @@
 			return -EINVAL;
 		break;
 	case SDE_DRM_FB_SEC_DIR_TRANS:
-	case SDE_DRM_FB_NON_SEC_DIR_TRANS:
 		*aspace = NULL;
 		break;
 	default:
@@ -896,7 +914,8 @@
 {
 	struct sde_plane *psde;
 	struct msm_gem_address_space *aspace = NULL;
-	int ret;
+	int ret, mode;
+	bool secure = false;
 
 	if (!plane || !pstate || !pipe_cfg || !fb) {
 		SDE_ERROR(
@@ -917,6 +936,26 @@
 		return;
 	}
 
+	/*
+	 * framebuffer prepare is deferred for prepare_fb calls that
+	 * happen during the transition from secure to non-secure.
+	 * Handle the prepare at this point for such cases. This can be
+	 * expected for one or two frames during the transition.
+	 */
+	if (aspace && pstate->defer_prepare_fb) {
+		SDE_EVT32(DRMID(plane), psde->pipe, aspace->domain_attached);
+		ret = msm_framebuffer_prepare(fb, pstate->aspace);
+		if (ret) {
+			SDE_ERROR_PLANE(psde,
+				"failed to prepare framebuffer %d\n", ret);
+			return;
+		}
+		pstate->defer_prepare_fb = false;
+	}
+	mode = sde_plane_get_property(pstate, PLANE_PROP_FB_TRANSLATION_MODE);
+	if ((mode == SDE_DRM_FB_SEC) || (mode == SDE_DRM_FB_SEC_DIR_TRANS))
+		secure = true;
+
 	ret = sde_format_populate_layout(aspace, fb, &pipe_cfg->layout);
 	if (ret == -EAGAIN)
 		SDE_DEBUG_PLANE(psde, "not updating same src addrs\n");
@@ -934,7 +973,8 @@
 				pipe_cfg->layout.plane_size[2],
 				pipe_cfg->layout.plane_addr[3],
 				pipe_cfg->layout.plane_size[3],
-				pstate->multirect_index);
+				pstate->multirect_index,
+				secure);
 		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg,
 						pstate->multirect_index);
 	}
@@ -1421,6 +1461,7 @@
 	const struct sde_format *fmt;
 	const struct drm_plane *plane;
 	struct sde_plane_state *pstate;
+	bool blend_enable = true;
 
 	if (!psde || !psde->base.state) {
 		SDE_ERROR("invalid plane\n");
@@ -1443,6 +1484,9 @@
 	 */
 	fmt = sde_get_sde_format(DRM_FORMAT_ABGR8888);
 
+	blend_enable = (SDE_DRM_BLEND_OP_OPAQUE !=
+			sde_plane_get_property(pstate, PLANE_PROP_BLEND_OP));
+
 	/* update sspp */
 	if (fmt && psde->pipe_hw->ops.setup_solidfill) {
 		psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw,
@@ -1458,7 +1502,7 @@
 
 		if (psde->pipe_hw->ops.setup_format)
 			psde->pipe_hw->ops.setup_format(psde->pipe_hw,
-					fmt, SDE_SSPP_SOLID_FILL,
+					fmt, blend_enable, SDE_SSPP_SOLID_FILL,
 					pstate->multirect_index);
 
 		if (psde->pipe_hw->ops.setup_rects)
@@ -1469,6 +1513,12 @@
 		if (psde->pipe_hw->ops.setup_pe)
 			psde->pipe_hw->ops.setup_pe(psde->pipe_hw,
 					&pstate->pixel_ext);
+
+		if (psde->pipe_hw->ops.setup_scaler &&
+				pstate->multirect_index != SDE_SSPP_RECT_1)
+			psde->pipe_hw->ops.setup_scaler(psde->pipe_hw,
+					&psde->pipe_cfg, &pstate->pixel_ext,
+					&pstate->scaler3_cfg);
 	}
 
 	return 0;
@@ -1513,7 +1563,7 @@
  * @plane: Pointer to drm plane
  * return: prefill time in line
  */
-static u32 sde_plane_rot_calc_prefill(struct drm_plane *plane)
+u32 sde_plane_rot_calc_prefill(struct drm_plane *plane)
 {
 	struct drm_plane_state *state;
 	struct sde_plane_state *pstate;
@@ -1542,33 +1592,19 @@
 				&blocksize, &blocksize);
 
 	prefill_line = blocksize + sde_kms->catalog->sbuf_headroom;
-
-	SDE_DEBUG("plane%d prefill:%u\n", plane->base.id, prefill_line);
+	prefill_line = mult_frac(prefill_line, rstate->out_src_h >> 16,
+			state->crtc_h);
+	SDE_DEBUG(
+		"plane%d.%d blk:%u head:%u vdst/vsrc:%u/%u prefill:%u\n",
+			plane->base.id, rstate->sequence_id,
+			blocksize, sde_kms->catalog->sbuf_headroom,
+			state->crtc_h, rstate->out_src_h >> 16,
+			prefill_line);
 
 	return prefill_line;
 }
 
 /**
- * sde_plane_is_sbuf_mode - check if sspp of given plane is in streaming
- *	buffer mode
- * @plane: Pointer to drm plane
- * @prefill: Pointer to prefill line count
- * return: true if sspp is in stream buffer mode
- */
-bool sde_plane_is_sbuf_mode(struct drm_plane *plane, u32 *prefill)
-{
-	struct sde_plane_state *pstate = plane && plane->state ?
-			to_sde_plane_state(plane->state) : NULL;
-	struct sde_plane_rot_state *rstate = pstate ? &pstate->rot : NULL;
-	bool sbuf_mode = rstate ? rstate->out_sbuf : false;
-
-	if (prefill)
-		*prefill = sde_plane_rot_calc_prefill(plane);
-
-	return sbuf_mode;
-}
-
-/**
  * sde_plane_rot_calc_cfg - calculate rotator/sspp configuration by
  *	enumerating over all planes attached to the same rotator
  * @plane: Pointer to drm plane
@@ -2126,6 +2162,14 @@
 		}
 	}
 
+	if (new_pstate->defer_prepare_fb) {
+		SDE_EVT32(DRMID(plane));
+		SDE_DEBUG(
+		    "plane%d, domain not attached, prepare fb handled later\n",
+		    plane->base.id);
+		return 0;
+	}
+
 	/* prepare rotator input buffer */
 	ret = msm_framebuffer_prepare(new_state->fb, new_pstate->aspace);
 	if (ret) {
@@ -2395,6 +2439,7 @@
 	struct drm_plane_state *state;
 	struct sde_plane_state *pstate;
 	struct sde_plane_rot_state *rstate;
+	int ret = 0;
 
 	if (!plane || !plane->state) {
 		SDE_ERROR("invalid plane/state\n");
@@ -2416,24 +2461,163 @@
 	if (!rstate->out_sbuf || !rstate->rot_hw)
 		return;
 
+	/*
+	 * framebuffer prepare is deferred for prepare_fb calls that
+	 * happen during the transition from secure to non-secure.
+	 * Handle the prepare at this point for rotator in such cases.
+	 * This can be expected for one or two frames during the transition.
+	 */
+	if (pstate->aspace && pstate->defer_prepare_fb) {
+		SDE_EVT32(DRMID(plane), pstate->aspace->domain_attached);
+		/* prepare rotator input buffer */
+		ret = msm_framebuffer_prepare(state->fb, pstate->aspace);
+		if (ret) {
+			SDE_ERROR("p%d failed to prepare input fb %d\n",
+							plane->base.id, ret);
+			return;
+		}
+
+		/* prepare rotator output buffer */
+		if (sde_plane_enabled(state) && rstate->out_fb) {
+			ret = msm_framebuffer_prepare(rstate->out_fb,
+						pstate->aspace);
+			if (ret) {
+				SDE_ERROR(
+				  "p%d failed to prepare inline fb %d\n",
+				  plane->base.id, ret);
+				goto error_prepare_output_buffer;
+			}
+		}
+	}
+
 	sde_plane_rot_submit_command(plane, state, SDE_HW_ROT_CMD_COMMIT);
+
+	return;
+
+error_prepare_output_buffer:
+	msm_framebuffer_cleanup(state->fb, pstate->aspace);
 }
 
-void sde_plane_kickoff(struct drm_plane *plane)
+static bool _sde_plane_halt_requests(struct drm_plane *plane,
+		uint32_t xin_id, bool halt_forced_clk, bool enable)
+{
+	struct sde_plane *psde;
+	struct msm_drm_private *priv;
+	struct sde_vbif_set_xin_halt_params halt_params;
+
+	if (!plane || !plane->dev) {
+		SDE_ERROR("invalid arguments\n");
+		return false;
+	}
+
+	psde = to_sde_plane(plane);
+	if (!psde->pipe_hw || !psde->pipe_hw->cap) {
+		SDE_ERROR("invalid pipe reference\n");
+		return false;
+	}
+
+	priv = plane->dev->dev_private;
+	if (!priv || !priv->kms) {
+		SDE_ERROR("invalid KMS reference\n");
+		return false;
+	}
+
+	memset(&halt_params, 0, sizeof(halt_params));
+	halt_params.vbif_idx = VBIF_RT;
+	halt_params.xin_id = xin_id;
+	halt_params.clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+	halt_params.forced_on = halt_forced_clk;
+	halt_params.enable = enable;
+
+	return sde_vbif_set_xin_halt(to_sde_kms(priv->kms), &halt_params);
+}
+
+void sde_plane_halt_requests(struct drm_plane *plane, bool enable)
+{
+	struct sde_plane *psde;
+
+	if (!plane) {
+		SDE_ERROR("invalid plane\n");
+		return;
+	}
+
+	psde = to_sde_plane(plane);
+	if (!psde->pipe_hw || !psde->pipe_hw->cap) {
+		SDE_ERROR("invalid pipe reference\n");
+		return;
+	}
+
+	SDE_EVT32(DRMID(plane), psde->xin_halt_forced_clk, enable);
+
+	psde->xin_halt_forced_clk =
+		_sde_plane_halt_requests(plane, psde->pipe_hw->cap->xin_id,
+				psde->xin_halt_forced_clk, enable);
+}
+
+int sde_plane_reset_rot(struct drm_plane *plane, struct drm_plane_state *state)
+{
+	struct sde_plane *psde;
+	struct sde_plane_state *pstate;
+	struct sde_plane_rot_state *rstate;
+	bool halt_ret[MAX_BLOCKS] = {false};
+	signed int i, count;
+
+	if (!plane || !state) {
+		SDE_ERROR("invalid plane\n");
+		return -EINVAL;
+	}
+
+	psde = to_sde_plane(plane);
+	pstate = to_sde_plane_state(state);
+	rstate = &pstate->rot;
+
+	/* do nothing if not master rotator plane */
+	if (!rstate->out_sbuf || !rstate->rot_hw ||
+			!rstate->rot_hw->caps || (rstate->out_xpos != 0))
+		return 0;
+
+	count = (signed int)rstate->rot_hw->caps->xin_count;
+	if (count > ARRAY_SIZE(halt_ret))
+		count = ARRAY_SIZE(halt_ret);
+
+	SDE_DEBUG_PLANE(psde, "issuing reset for rotator\n");
+	SDE_EVT32(DRMID(plane), count);
+
+	for (i = 0; i < count; i++) {
+		const struct sde_rot_vbif_cfg *cfg =
+				&rstate->rot_hw->caps->vbif_cfg[i];
+
+		halt_ret[i] = _sde_plane_halt_requests(plane, cfg->xin_id,
+				false, true);
+	}
+
+	sde_plane_rot_submit_command(plane, state, SDE_HW_ROT_CMD_RESET);
+
+	for (i = count - 1; i >= 0; --i) {
+		const struct sde_rot_vbif_cfg *cfg =
+				&rstate->rot_hw->caps->vbif_cfg[i];
+
+		_sde_plane_halt_requests(plane, cfg->xin_id,
+				halt_ret[i], false);
+	}
+	return 0;
+}
+
+int sde_plane_kickoff_rot(struct drm_plane *plane)
 {
 	struct sde_plane_state *pstate;
 
 	if (!plane || !plane->state) {
 		SDE_ERROR("invalid plane\n");
-		return;
+		return -EINVAL;
 	}
 
 	pstate = to_sde_plane_state(plane->state);
 
 	if (!pstate->rot.rot_hw || !pstate->rot.rot_hw->ops.commit)
-		return;
+		return 0;
 
-	pstate->rot.rot_hw->ops.commit(pstate->rot.rot_hw,
+	return pstate->rot.rot_hw->ops.commit(pstate->rot.rot_hw,
 			&pstate->rot.rot_cmd,
 			SDE_HW_ROT_CMD_START);
 }
@@ -2606,6 +2790,12 @@
 	pstate->multirect_mode = SDE_SSPP_MULTIRECT_NONE;
 }
 
+/**
+ * multi_rect validate API allows to validate only R0 and R1 RECT
+ * passing for each plane. Client of this API must not pass multiple
+ * plane which are not sharing same XIN client. Such calls will fail
+ * even though kernel client is passing valid multirect configuration.
+ */
 int sde_plane_validate_multirect_v2(struct sde_multirect_plane_states *plane)
 {
 	struct sde_plane_state *pstate[R_MAX];
@@ -2613,37 +2803,44 @@
 	struct sde_rect src[R_MAX], dst[R_MAX];
 	struct sde_plane *sde_plane[R_MAX];
 	const struct sde_format *fmt[R_MAX];
+	int xin_id[R_MAX];
 	bool q16_data = true;
-	int i, buffer_lines;
+	int i, j, buffer_lines, width_threshold[R_MAX];
 	unsigned int max_tile_height = 1;
 	bool parallel_fetch_qualified = true;
-	bool has_tiled_rect = false;
+	enum sde_sspp_multirect_mode mode = SDE_SSPP_MULTIRECT_NONE;
+	const struct msm_format *msm_fmt;
 
 	for (i = 0; i < R_MAX; i++) {
-		const struct msm_format *msm_fmt;
-
 		drm_state[i] = i ? plane->r1 : plane->r0;
-		msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
-		fmt[i] = to_sde_format(msm_fmt);
-
-		if (SDE_FORMAT_IS_UBWC(fmt[i])) {
-			has_tiled_rect = true;
-			if (fmt[i]->tile_height > max_tile_height)
-				max_tile_height = fmt[i]->tile_height;
+		if (!drm_state[i]) {
+			SDE_ERROR("drm plane state is NULL\n");
+			return -EINVAL;
 		}
-	}
-
-	for (i = 0; i < R_MAX; i++) {
-		int width_threshold;
 
 		pstate[i] = to_sde_plane_state(drm_state[i]);
 		sde_plane[i] = to_sde_plane(drm_state[i]->plane);
+		xin_id[i] = sde_plane[i]->pipe_hw->cap->xin_id;
 
-		if (pstate[i] == NULL) {
-			SDE_ERROR("SDE plane state of plane id %d is NULL\n",
-				drm_state[i]->plane->base.id);
+		for (j = 0; j < i; j++) {
+			if (xin_id[i] != xin_id[j]) {
+				SDE_ERROR_PLANE(sde_plane[i],
+					"invalid multirect validate call base:%d xin_id:%d curr:%d xin:%d\n",
+					j, xin_id[j], i, xin_id[i]);
+				return -EINVAL;
+			}
+		}
+
+		msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
+		if (!msm_fmt) {
+			SDE_ERROR_PLANE(sde_plane[i], "null fb\n");
 			return -EINVAL;
 		}
+		fmt[i] = to_sde_format(msm_fmt);
+
+		if (SDE_FORMAT_IS_UBWC(fmt[i]) &&
+		    (fmt[i]->tile_height > max_tile_height))
+			max_tile_height = fmt[i]->tile_height;
 
 		POPULATE_RECT(&src[i], drm_state[i]->src_x, drm_state[i]->src_y,
 			drm_state[i]->src_w, drm_state[i]->src_h, q16_data);
@@ -2670,41 +2867,81 @@
 		 * So we cannot support more than half of the supported SSPP
 		 * width for tiled formats.
 		 */
-		width_threshold = sde_plane[i]->pipe_sblk->maxlinewidth;
-		if (has_tiled_rect)
-			width_threshold /= 2;
+		width_threshold[i] = sde_plane[i]->pipe_sblk->maxlinewidth;
+		if (SDE_FORMAT_IS_UBWC(fmt[i]))
+			width_threshold[i] /= 2;
 
-		if (parallel_fetch_qualified && src[i].w > width_threshold)
+		if (parallel_fetch_qualified && src[i].w > width_threshold[i])
 			parallel_fetch_qualified = false;
 
+		if (sde_plane[i]->is_virtual)
+			mode = sde_plane_get_property(pstate[i],
+					PLANE_PROP_MULTIRECT_MODE);
 	}
 
-	/* Validate RECT's and set the mode */
-
-	/* Prefer PARALLEL FETCH Mode over TIME_MX Mode */
-	if (parallel_fetch_qualified) {
-		pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
-		pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
-
-		goto done;
-	}
-
-	/* TIME_MX Mode */
 	buffer_lines = 2 * max_tile_height;
 
-	if ((dst[R1].y >= dst[R0].y + dst[R0].h + buffer_lines) ||
-		(dst[R0].y >= dst[R1].y + dst[R1].h + buffer_lines)) {
-		pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
-		pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
-	} else {
-		SDE_ERROR(
-			"No multirect mode possible for the planes (%d - %d)\n",
-			drm_state[R0]->plane->base.id,
-			drm_state[R1]->plane->base.id);
-		return -EINVAL;
+	/**
+	 * fallback to driver mode selection logic if client is using
+	 * multirect plane without setting property.
+	 *
+	 * validate multirect mode configuration based on rectangle
+	 */
+	switch (mode) {
+	case SDE_SSPP_MULTIRECT_NONE:
+		if (parallel_fetch_qualified)
+			mode = SDE_SSPP_MULTIRECT_PARALLEL;
+		else if (TIME_MULTIPLEX_RECT(dst[R1], dst[R0], buffer_lines) ||
+			 TIME_MULTIPLEX_RECT(dst[R0], dst[R1], buffer_lines))
+			mode = SDE_SSPP_MULTIRECT_TIME_MX;
+		else
+			SDE_ERROR(
+				"planes(%d - %d) multirect mode selection fail\n",
+				drm_state[R0]->plane->base.id,
+				drm_state[R1]->plane->base.id);
+		break;
+
+	case SDE_SSPP_MULTIRECT_PARALLEL:
+		if (!parallel_fetch_qualified) {
+			SDE_ERROR("R0 plane:%d width_threshold:%d src_w:%d\n",
+				drm_state[R0]->plane->base.id,
+				width_threshold[R0],  src[R0].w);
+			SDE_ERROR("R1 plane:%d width_threshold:%d src_w:%d\n",
+				drm_state[R1]->plane->base.id,
+				width_threshold[R1],  src[R1].w);
+			SDE_ERROR("parallel fetch not qualified\n");
+			mode = SDE_SSPP_MULTIRECT_NONE;
+		}
+		break;
+
+	case SDE_SSPP_MULTIRECT_TIME_MX:
+		if (!TIME_MULTIPLEX_RECT(dst[R1], dst[R0], buffer_lines) &&
+		    !TIME_MULTIPLEX_RECT(dst[R0], dst[R1], buffer_lines)) {
+			SDE_ERROR(
+				"buffer_lines:%d R0 plane:%d dst_y:%d dst_h:%d\n",
+				buffer_lines, drm_state[R0]->plane->base.id,
+				dst[R0].y, dst[R0].h);
+			SDE_ERROR(
+				"buffer_lines:%d R1 plane:%d dst_y:%d dst_h:%d\n",
+				buffer_lines, drm_state[R1]->plane->base.id,
+				dst[R1].y, dst[R1].h);
+			SDE_ERROR("time multiplexed fetch not qualified\n");
+			mode = SDE_SSPP_MULTIRECT_NONE;
+		}
+		break;
+
+	default:
+		SDE_ERROR("bad mode:%d selection\n", mode);
+		mode = SDE_SSPP_MULTIRECT_NONE;
+		break;
 	}
 
-done:
+	for (i = 0; i < R_MAX; i++)
+		pstate[i]->multirect_mode = mode;
+
+	if (mode == SDE_SSPP_MULTIRECT_NONE)
+		return -EINVAL;
+
 	if (sde_plane[R0]->is_virtual) {
 		pstate[R0]->multirect_index = SDE_SSPP_RECT_1;
 		pstate[R1]->multirect_index = SDE_SSPP_RECT_0;
@@ -2717,6 +2954,7 @@
 		pstate[R0]->multirect_mode, pstate[R0]->multirect_index);
 	SDE_DEBUG_PLANE(sde_plane[R1], "R1: %d - %d\n",
 		pstate[R1]->multirect_mode, pstate[R1]->multirect_index);
+
 	return 0;
 }
 
@@ -2747,7 +2985,7 @@
 		return;
 
 	*flush_rot = 0x0;
-	if (sde_plane_is_sbuf_mode(plane, NULL) && rstate->rot_hw &&
+	if (rstate && rstate->out_sbuf && rstate->rot_hw &&
 			ctl->ops.get_bitmask_rot)
 		ctl->ops.get_bitmask_rot(ctl, flush_rot, rstate->rot_hw->idx);
 }
@@ -2774,14 +3012,31 @@
 		return ret;
 	}
 
-	/*cache aspace */
+	/* cache aspace */
 	pstate->aspace = aspace;
+
+	/*
+	 * when transitioning from secure to non-secure,
+	 * plane->prepare_fb happens before the commit. In such case,
+	 * defer the prepare_fb and handled it late, during the commit
+	 * after attaching the domains as part of the transition
+	 */
+	pstate->defer_prepare_fb = (aspace && !aspace->domain_attached) ?
+							true : false;
+
 	ret = sde_plane_rot_prepare_fb(plane, new_state);
 	if (ret) {
 		SDE_ERROR("failed to prepare rot framebuffer\n");
 		return ret;
 	}
 
+	if (pstate->defer_prepare_fb) {
+		SDE_EVT32(DRMID(plane), psde->pipe);
+		SDE_DEBUG_PLANE(psde,
+		    "domain not attached, prepare_fb handled later\n");
+		return 0;
+	}
+
 	new_rstate = &to_sde_plane_state(new_state)->rot;
 
 	if (pstate->aspace) {
@@ -2804,20 +3059,76 @@
 	return 0;
 }
 
+/**
+ * _sde_plane_fetch_halt - halts vbif transactions for a plane
+ * @plane: Pointer to plane
+ * Returns: 0 on success
+ */
+static int _sde_plane_fetch_halt(struct drm_plane *plane)
+{
+	struct sde_plane *psde;
+	int xin_id;
+	enum sde_clk_ctrl_type clk_ctrl;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+
+	psde = to_sde_plane(plane);
+	if (!plane || !plane->dev || !psde->pipe_hw) {
+		SDE_ERROR("invalid arguments\n");
+		return -EINVAL;
+	}
+
+	priv = plane->dev->dev_private;
+	if (!priv || !priv->kms) {
+		SDE_ERROR("invalid KMS reference\n");
+		return -EINVAL;
+	}
+
+	sde_kms = to_sde_kms(priv->kms);
+	clk_ctrl = psde->pipe_hw->cap->clk_ctrl;
+	xin_id = psde->pipe_hw->cap->xin_id;
+	SDE_DEBUG_PLANE(psde, "pipe:%d xin_id:%d clk_ctrl:%d\n",
+			psde->pipe - SSPP_VIG0, xin_id, clk_ctrl);
+	SDE_EVT32_VERBOSE(psde, psde->pipe - SSPP_VIG0, xin_id, clk_ctrl);
+
+	return sde_vbif_halt_plane_xin(sde_kms, xin_id, clk_ctrl);
+}
+
 static void sde_plane_cleanup_fb(struct drm_plane *plane,
 		struct drm_plane_state *old_state)
 {
 	struct sde_plane *psde = to_sde_plane(plane);
 	struct sde_plane_state *old_pstate;
 	struct sde_plane_rot_state *old_rstate;
+	int ret;
 
-	if (!old_state || !old_state->fb)
+	if (!old_state || !old_state->fb || !plane || !plane->state)
 		return;
 
 	old_pstate = to_sde_plane_state(old_state);
 
 	SDE_DEBUG_PLANE(psde, "FB[%u]\n", old_state->fb->base.id);
 
+	/*
+	 * plane->state gets populated for next frame after swap_state. If
+	 * plane->state->crtc pointer is not populated then it is not used in
+	 * the next frame, hence making it an unused plane.
+	 */
+	if ((plane->state->crtc == NULL) && !psde->is_virtual) {
+		SDE_DEBUG_PLANE(psde, "unused pipe:%u\n",
+			       psde->pipe - SSPP_VIG0);
+
+		/* halt this plane now */
+		ret = _sde_plane_fetch_halt(plane);
+		if (ret) {
+			SDE_ERROR_PLANE(psde,
+				       "unused pipe %u halt failed\n",
+				       psde->pipe - SSPP_VIG0);
+			SDE_EVT32(DRMID(plane), psde->pipe - SSPP_VIG0,
+				       ret, SDE_EVTLOG_ERROR);
+		}
+	}
+
 	old_rstate = &old_pstate->rot;
 
 	msm_framebuffer_cleanup(old_rstate->out_fb, old_pstate->aspace);
@@ -2937,8 +3248,9 @@
 		return -EINVAL;
 	}
 
-	/* don't run checks unless scaler data was changed */
-	if (pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2_CHECK)
+	if (psde->debugfs_default_scale ||
+	   (pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2 &&
+	    pstate->scaler_check_state != SDE_PLANE_SCLCHECK_SCALER_V2_CHECK))
 		return 0;
 
 	pstate->scaler_check_state = SDE_PLANE_SCLCHECK_INVALID;
@@ -3248,10 +3560,6 @@
 	else if (psde->pipe_hw && psde->csc_ptr && psde->pipe_hw->ops.setup_csc)
 		psde->pipe_hw->ops.setup_csc(psde->pipe_hw, psde->csc_ptr);
 
-	/* force black color fill during suspend */
-	if (sde_kms_is_suspend_state(plane->dev) && suspend_blank)
-		_sde_plane_color_fill(psde, 0x0, 0x0);
-
 	/* flag h/w flush complete */
 	if (plane->state)
 		pstate->pending = false;
@@ -3287,6 +3595,7 @@
 	struct sde_rect src, dst;
 	const struct sde_rect *crtc_roi;
 	bool q16_data = true;
+	bool blend_enabled = true;
 	int idx;
 
 	if (!plane) {
@@ -3357,6 +3666,7 @@
 		case PLANE_PROP_CSC_V1:
 			pstate->dirty |= SDE_PLANE_DIRTY_FORMAT;
 			break;
+		case PLANE_PROP_MULTIRECT_MODE:
 		case PLANE_PROP_COLOR_FILL:
 			/* potentially need to refresh everything */
 			pstate->dirty = SDE_PLANE_DIRTY_ALL;
@@ -3521,8 +3831,12 @@
 		if (rstate->out_rotation & DRM_REFLECT_Y)
 			src_flags |= SDE_SSPP_FLIP_UD;
 
+		blend_enabled = (SDE_DRM_BLEND_OP_OPAQUE !=
+			sde_plane_get_property(pstate, PLANE_PROP_BLEND_OP));
+
 		/* update format */
-		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags,
+		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt,
+				blend_enabled, src_flags,
 				pstate->multirect_index);
 
 		if (psde->pipe_hw->ops.setup_cdp) {
@@ -3539,7 +3853,8 @@
 					SDE_FORMAT_IS_TILE(fmt);
 			cdp_cfg->preload_ahead = SDE_WB_CDP_PRELOAD_AHEAD_64;
 
-			psde->pipe_hw->ops.setup_cdp(psde->pipe_hw, cdp_cfg);
+			psde->pipe_hw->ops.setup_cdp(psde->pipe_hw, cdp_cfg,
+					pstate->multirect_index);
 		}
 
 		if (psde->pipe_hw->ops.setup_sys_cache) {
@@ -3703,6 +4018,56 @@
 	sde_plane_atomic_update(plane, plane->state);
 }
 
+int sde_plane_helper_reset_custom_properties(struct drm_plane *plane,
+		struct drm_plane_state *plane_state)
+{
+	struct sde_plane *psde;
+	struct sde_plane_state *pstate;
+	struct drm_property *drm_prop;
+	enum msm_mdp_plane_property prop_idx;
+
+	if (!plane || !plane_state) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+
+	psde = to_sde_plane(plane);
+	pstate = to_sde_plane_state(plane_state);
+
+	for (prop_idx = 0; prop_idx < PLANE_PROP_COUNT; prop_idx++) {
+		uint64_t val = pstate->property_values[prop_idx].value;
+		uint64_t def;
+		int ret;
+
+		drm_prop = msm_property_index_to_drm_property(
+				&psde->property_info, prop_idx);
+		if (!drm_prop) {
+			/* not all props will be installed, based on caps */
+			SDE_DEBUG_PLANE(psde, "invalid property index %d\n",
+					prop_idx);
+			continue;
+		}
+
+		def = msm_property_get_default(&psde->property_info, prop_idx);
+		if (val == def)
+			continue;
+
+		SDE_DEBUG_PLANE(psde, "set prop %s idx %d from %llu to %llu\n",
+				drm_prop->name, prop_idx, val, def);
+
+		ret = drm_atomic_plane_set_property(plane, plane_state,
+				drm_prop, def);
+		if (ret) {
+			SDE_ERROR_PLANE(psde,
+					"set property failed, idx %d ret %d\n",
+					prop_idx, ret);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
 /* helper to install properties which are common to planes and crtcs */
 static void _sde_plane_install_properties(struct drm_plane *plane,
 	struct sde_mdss_cfg *catalog, u32 master_plane_id)
@@ -3722,6 +4087,11 @@
 		{SDE_DRM_FB_NON_SEC_DIR_TRANS, "non_sec_direct_translation"},
 		{SDE_DRM_FB_SEC_DIR_TRANS, "sec_direct_translation"},
 	};
+	static const struct drm_prop_enum_list e_multirect_mode[] = {
+		{SDE_SSPP_MULTIRECT_NONE, "none"},
+		{SDE_SSPP_MULTIRECT_PARALLEL, "parallel"},
+		{SDE_SSPP_MULTIRECT_TIME_MX,  "serial"},
+	};
 	const struct sde_format_extended *format_list;
 	struct sde_kms_info *info;
 	struct sde_plane *psde = to_sde_plane(plane);
@@ -3871,6 +4241,10 @@
 		format_list = psde->pipe_sblk->virt_format_list;
 		sde_kms_info_add_keyint(info, "primary_smart_plane_id",
 						master_plane_id);
+		msm_property_install_enum(&psde->property_info,
+			"multirect_mode", 0x0, 0, e_multirect_mode,
+			ARRAY_SIZE(e_multirect_mode),
+			PLANE_PROP_MULTIRECT_MODE);
 	}
 
 	if (format_list) {
@@ -4061,51 +4435,11 @@
 			&pstate->property_state, PLANE_PROP_SCALER_V2);
 
 	/* populate from user space */
+	sde_set_scaler_v2(cfg, &scale_v2);
+
 	pe = &pstate->pixel_ext;
 	memset(pe, 0, sizeof(struct sde_hw_pixel_ext));
-	cfg->enable = scale_v2.enable;
-	cfg->dir_en = scale_v2.dir_en;
-	for (i = 0; i < SDE_MAX_PLANES; i++) {
-		cfg->init_phase_x[i] = scale_v2.init_phase_x[i];
-		cfg->phase_step_x[i] = scale_v2.phase_step_x[i];
-		cfg->init_phase_y[i] = scale_v2.init_phase_y[i];
-		cfg->phase_step_y[i] = scale_v2.phase_step_y[i];
 
-		cfg->preload_x[i] = scale_v2.preload_x[i];
-		cfg->preload_y[i] = scale_v2.preload_y[i];
-		cfg->src_width[i] = scale_v2.src_width[i];
-		cfg->src_height[i] = scale_v2.src_height[i];
-	}
-	cfg->dst_width = scale_v2.dst_width;
-	cfg->dst_height = scale_v2.dst_height;
-
-	cfg->y_rgb_filter_cfg = scale_v2.y_rgb_filter_cfg;
-	cfg->uv_filter_cfg = scale_v2.uv_filter_cfg;
-	cfg->alpha_filter_cfg = scale_v2.alpha_filter_cfg;
-	cfg->blend_cfg = scale_v2.blend_cfg;
-
-	cfg->lut_flag = scale_v2.lut_flag;
-	cfg->dir_lut_idx = scale_v2.dir_lut_idx;
-	cfg->y_rgb_cir_lut_idx = scale_v2.y_rgb_cir_lut_idx;
-	cfg->uv_cir_lut_idx = scale_v2.uv_cir_lut_idx;
-	cfg->y_rgb_sep_lut_idx = scale_v2.y_rgb_sep_lut_idx;
-	cfg->uv_sep_lut_idx = scale_v2.uv_sep_lut_idx;
-
-	cfg->de.enable = scale_v2.de.enable;
-	cfg->de.sharpen_level1 = scale_v2.de.sharpen_level1;
-	cfg->de.sharpen_level2 = scale_v2.de.sharpen_level2;
-	cfg->de.clip = scale_v2.de.clip;
-	cfg->de.limit = scale_v2.de.limit;
-	cfg->de.thr_quiet = scale_v2.de.thr_quiet;
-	cfg->de.thr_dieout = scale_v2.de.thr_dieout;
-	cfg->de.thr_low = scale_v2.de.thr_low;
-	cfg->de.thr_high = scale_v2.de.thr_high;
-	cfg->de.prec_shift = scale_v2.de.prec_shift;
-	for (i = 0; i < SDE_MAX_DE_CURVES; i++) {
-		cfg->de.adjust_a[i] = scale_v2.de.adjust_a[i];
-		cfg->de.adjust_b[i] = scale_v2.de.adjust_b[i];
-		cfg->de.adjust_c[i] = scale_v2.de.adjust_c[i];
-	}
 	for (i = 0; i < SDE_MAX_PLANES; i++) {
 		pe->left_ftch[i] = scale_v2.pe.left_ftch[i];
 		pe->right_ftch[i] = scale_v2.pe.right_ftch[i];
@@ -4211,15 +4545,6 @@
 	return ret;
 }
 
-static int sde_plane_set_property(struct drm_plane *plane,
-		struct drm_property *property, uint64_t val)
-{
-	SDE_DEBUG("\n");
-
-	return sde_plane_atomic_set_property(plane,
-			plane->state, property, val);
-}
-
 static int sde_plane_atomic_get_property(struct drm_plane *plane,
 		const struct drm_plane_state *state,
 		struct drm_property *property, uint64_t *val)
@@ -4362,6 +4687,11 @@
 	psde = to_sde_plane(plane);
 	SDE_DEBUG_PLANE(psde, "\n");
 
+	if (plane->state && !sde_crtc_is_reset_required(plane->state->crtc)) {
+		SDE_DEBUG_PLANE(psde, "avoid reset for plane\n");
+		return;
+	}
+
 	/* remove previous state, if present */
 	if (plane->state) {
 		sde_plane_destroy_state(plane, plane->state);
@@ -4618,7 +4948,7 @@
 		.update_plane = drm_atomic_helper_update_plane,
 		.disable_plane = drm_atomic_helper_disable_plane,
 		.destroy = sde_plane_destroy,
-		.set_property = sde_plane_set_property,
+		.set_property = drm_atomic_helper_plane_set_property,
 		.atomic_set_property = sde_plane_atomic_set_property,
 		.atomic_get_property = sde_plane_atomic_get_property,
 		.reset = sde_plane_reset,
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index 913647f..5c1fff1 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -129,6 +129,7 @@
  * @multirect_index: index of the rectangle of SSPP
  * @multirect_mode: parallel or time multiplex multirect mode
  * @pending:	whether the current update is still pending
+ * @defer_prepare_fb:	indicate if prepare_fb call was deferred
  * @scaler3_cfg: configuration data for scaler3
  * @pixel_ext: configuration data for pixel extensions
  * @scaler_check_state: indicates status of user provided pixel extension data
@@ -146,6 +147,7 @@
 	uint32_t multirect_index;
 	uint32_t multirect_mode;
 	bool pending;
+	bool defer_prepare_fb;
 
 	/* scaler configuration */
 	struct sde_hw_scaler3_cfg scaler3_cfg;
@@ -207,12 +209,11 @@
 		u32 *flush_sspp, u32 *flush_rot);
 
 /**
- * sde_plane_is_sbuf_mode - return status of stream buffer mode
- * @plane:   Pointer to DRM plane object
- * @prefill: Pointer to updated prefill in stream buffer mode (optional)
- * Returns: true if plane is in stream buffer mode
+ * sde_plane_rot_calc_prefill - calculate rotator start prefill
+ * @plane: Pointer to drm plane
+ * return: prefill time in line
  */
-bool sde_plane_is_sbuf_mode(struct drm_plane *plane, u32 *prefill);
+u32 sde_plane_rot_calc_prefill(struct drm_plane *plane);
 
 /**
  * sde_plane_restore - restore hw state if previously power collapsed
@@ -227,10 +228,28 @@
 void sde_plane_flush(struct drm_plane *plane);
 
 /**
- * sde_plane_kickoff - final plane operations before commit kickoff
+ * sde_plane_halt_requests - control halting of vbif transactions for this plane
+ *	This function isn't thread safe. Plane halt enable/disable requests
+ *	should always be made from the same commit cycle.
  * @plane: Pointer to drm plane structure
+ * @enable: Whether to enable/disable halting of vbif transactions
  */
-void sde_plane_kickoff(struct drm_plane *plane);
+void sde_plane_halt_requests(struct drm_plane *plane, bool enable);
+
+/**
+ * sde_plane_reset_rot - reset rotator operations before commit kickoff
+ * @plane: Pointer to drm plane structure
+ * @state: Pointer to plane state associated with reset request
+ * Returns: Zero on success
+ */
+int sde_plane_reset_rot(struct drm_plane *plane, struct drm_plane_state *state);
+
+/**
+ * sde_plane_kickoff_rot - final plane rotator operations before commit kickoff
+ * @plane: Pointer to drm plane structure
+ * Returns: Zero on success
+ */
+int sde_plane_kickoff_rot(struct drm_plane *plane);
 
 /**
  * sde_plane_set_error: enable/disable error condition
@@ -292,4 +311,14 @@
  */
 void sde_plane_set_revalidate(struct drm_plane *plane, bool enable);
 
+/**
+ * sde_plane_helper_reset_properties - reset properties to default values in the
+ *	given DRM plane state object
+ * @plane: Pointer to DRM plane object
+ * @plane_state: Pointer to DRM plane state object
+ * Returns: 0 on success, negative errno on failure
+ */
+int sde_plane_helper_reset_custom_properties(struct drm_plane *plane,
+		struct drm_plane_state *plane_state);
+
 #endif /* _SDE_PLANE_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.c b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
index e38524f..1bef4b8 100644
--- a/drivers/gpu/drm/msm/sde/sde_reg_dma.c
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
@@ -64,7 +64,7 @@
 }
 
 static int default_last_command(struct sde_hw_ctl *ctl,
-		enum sde_reg_dma_queue q)
+		enum sde_reg_dma_queue q, enum sde_reg_dma_last_cmd_mode mode)
 {
 	return 0;
 }
@@ -99,9 +99,6 @@
 		rc = init_v1(&reg_dma);
 		if (rc)
 			DRM_DEBUG("init v1 dma ops failed\n");
-		else
-			sde_dbg_reg_register_base("reg_dma", addr,
-					reg_dma.caps->len);
 		break;
 	default:
 		break;
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.h b/drivers/gpu/drm/msm/sde/sde_reg_dma.h
index b9d7843..41a292a 100644
--- a/drivers/gpu/drm/msm/sde/sde_reg_dma.h
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.h
@@ -169,6 +169,18 @@
 };
 
 /**
+ * enum sde_reg_dma_last_cmd_mode - defines enums for kick off mode.
+ * @REG_DMA_WAIT4_COMP: last_command api will wait for max of 1 msec allowing
+ *			reg dma trigger to complete.
+ * @REG_DMA_NOWAIT: last_command api will not wait for reg dma trigger
+ *		    completion.
+ */
+enum sde_reg_dma_last_cmd_mode {
+	REG_DMA_WAIT4_COMP,
+	REG_DMA_NOWAIT,
+};
+
+/**
  * struct sde_reg_dma_buffer - defines reg dma buffer structure.
  * @drm_gem_object *buf: drm gem handle for the buffer
  * @asapce : pointer to address space
@@ -265,7 +277,8 @@
 	struct sde_reg_dma_buffer* (*alloc_reg_dma_buf)(u32 size);
 	int (*dealloc_reg_dma)(struct sde_reg_dma_buffer *lut_buf);
 	int (*reset_reg_dma_buf)(struct sde_reg_dma_buffer *buf);
-	int (*last_command)(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q);
+	int (*last_command)(struct sde_hw_ctl *ctl, enum sde_reg_dma_queue q,
+			enum sde_reg_dma_last_cmd_mode mode);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.c b/drivers/gpu/drm/msm/sde/sde_rm.c
index be3a8af..c2c1f75 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -18,6 +18,7 @@
 #include "sde_hw_ctl.h"
 #include "sde_hw_cdm.h"
 #include "sde_hw_dspp.h"
+#include "sde_hw_ds.h"
 #include "sde_hw_pingpong.h"
 #include "sde_hw_intf.h"
 #include "sde_hw_wb.h"
@@ -32,6 +33,7 @@
 #define RM_RQ_LOCK(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_LOCK))
 #define RM_RQ_CLEAR(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_RESERVE_CLEAR))
 #define RM_RQ_DSPP(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DSPP))
+#define RM_RQ_DS(r) ((r)->top_ctrl & BIT(SDE_RM_TOPCTL_DS))
 #define RM_IS_TOPOLOGY_MATCH(t, r) ((t).num_lm == (r).num_lm && \
 				(t).num_comp_enc == (r).num_enc && \
 				(t).num_intf == (r).num_intf)
@@ -173,6 +175,18 @@
 	iter->type = type;
 }
 
+enum sde_rm_topology_name sde_rm_get_topology_name(
+	struct msm_display_topology topology)
+{
+	int i;
+
+	for (i = 0; i < SDE_RM_TOPOLOGY_MAX; i++)
+		if (RM_IS_TOPOLOGY_MATCH(g_top_table[i], topology))
+			return g_top_table[i].top_name;
+
+	return SDE_RM_TOPOLOGY_NONE;
+}
+
 static bool _sde_rm_get_hw_locked(struct sde_rm *rm, struct sde_rm_hw_iter *i)
 {
 	struct list_head *blk_list;
@@ -234,6 +248,9 @@
 	case SDE_HW_BLK_DSPP:
 		sde_hw_dspp_destroy(hw);
 		break;
+	case SDE_HW_BLK_DS:
+		sde_hw_ds_destroy(hw);
+		break;
 	case SDE_HW_BLK_CTL:
 		sde_hw_ctl_destroy(hw);
 		break;
@@ -304,7 +321,7 @@
 static int _sde_rm_hw_blk_create(
 		struct sde_rm *rm,
 		struct sde_mdss_cfg *cat,
-		void *mmio,
+		void __iomem *mmio,
 		enum sde_hw_blk_type type,
 		uint32_t id,
 		void *hw_catalog_info)
@@ -322,6 +339,9 @@
 	case SDE_HW_BLK_DSPP:
 		hw = sde_hw_dspp_init(id, mmio, cat);
 		break;
+	case SDE_HW_BLK_DS:
+		hw = sde_hw_ds_init(id, mmio, cat);
+		break;
 	case SDE_HW_BLK_CTL:
 		hw = sde_hw_ctl_init(id, mmio, cat);
 		break;
@@ -375,7 +395,7 @@
 
 int sde_rm_init(struct sde_rm *rm,
 		struct sde_mdss_cfg *cat,
-		void *mmio,
+		void __iomem *mmio,
 		struct drm_device *dev)
 {
 	int rc, i;
@@ -411,8 +431,8 @@
 		struct sde_lm_cfg *lm = &cat->mixer[i];
 
 		if (lm->pingpong == PINGPONG_MAX) {
-			SDE_DEBUG("skip mixer %d without pingpong\n", lm->id);
-			continue;
+			SDE_ERROR("mixer %d without pingpong\n", lm->id);
+			goto fail;
 		}
 
 		rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_LM,
@@ -444,6 +464,17 @@
 		}
 	}
 
+	if (cat->mdp[0].has_dest_scaler) {
+		for (i = 0; i < cat->ds_count; i++) {
+			rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_DS,
+					cat->ds[i].id, &cat->ds[i]);
+			if (rc) {
+				SDE_ERROR("failed: ds hw not available\n");
+				goto fail;
+			}
+		}
+	}
+
 	for (i = 0; i < cat->pingpong_count; i++) {
 		rc = _sde_rm_hw_blk_create(rm, cat, mmio, SDE_HW_BLK_PINGPONG,
 				cat->pingpong[i].id, &cat->pingpong[i]);
@@ -543,18 +574,24 @@
 		struct sde_rm_requirements *reqs,
 		struct sde_rm_hw_blk *lm,
 		struct sde_rm_hw_blk **dspp,
+		struct sde_rm_hw_blk **ds,
 		struct sde_rm_hw_blk **pp,
 		struct sde_rm_hw_blk *primary_lm)
 {
 	const struct sde_lm_cfg *lm_cfg = to_sde_hw_mixer(lm->hw)->cap;
 	const struct sde_pingpong_cfg *pp_cfg;
 	struct sde_rm_hw_iter iter;
+	bool is_valid_dspp, is_valid_ds, ret;
+	u32 display_pref;
 
 	*dspp = NULL;
+	*ds = NULL;
 	*pp = NULL;
+	display_pref = lm_cfg->features & BIT(SDE_DISP_PRIMARY_PREF);
 
-	SDE_DEBUG("check lm %d: dspp %d pp %d\n", lm_cfg->id, lm_cfg->dspp,
-			lm_cfg->pingpong);
+	SDE_DEBUG("check lm %d: dspp %d ds %d pp %d display_pref: %d\n",
+		lm_cfg->id, lm_cfg->dspp, lm_cfg->ds,
+		lm_cfg->pingpong, display_pref);
 
 	/* Check if this layer mixer is a peer of the proposed primary LM */
 	if (primary_lm) {
@@ -568,12 +605,38 @@
 		}
 	}
 
-	/* Matches user requirements? */
-	if ((RM_RQ_DSPP(reqs) && lm_cfg->dspp == DSPP_MAX) ||
-			(!RM_RQ_DSPP(reqs) && lm_cfg->dspp != DSPP_MAX)) {
-		SDE_DEBUG("dspp req mismatch lm %d reqdspp %d, lm->dspp %d\n",
+	/* bypass rest of the checks if LM for primary display is found */
+	if (!display_pref) {
+		is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false;
+		is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false;
+
+		/**
+		 * RM_RQ_X: specification of which LMs to choose
+		 * is_valid_X: indicates whether LM is tied with block X
+		 * ret: true if given LM matches the user requirement,
+		 *      false otherwise
+		 */
+		if (RM_RQ_DSPP(reqs) && RM_RQ_DS(reqs))
+			ret = (is_valid_dspp && is_valid_ds);
+		else if (RM_RQ_DSPP(reqs))
+			ret = is_valid_dspp;
+		else if (RM_RQ_DS(reqs))
+			ret = is_valid_ds;
+		else
+			ret = !(is_valid_dspp || is_valid_ds);
+
+		if (!ret) {
+			SDE_DEBUG(
+				"fail:lm(%d)req_dspp(%d)dspp(%d)req_ds(%d)ds(%d)\n",
 				lm_cfg->id, (bool)(RM_RQ_DSPP(reqs)),
-				lm_cfg->dspp);
+				lm_cfg->dspp, (bool)(RM_RQ_DS(reqs)),
+				lm_cfg->ds);
+			return ret;
+		}
+	} else if (!(reqs->hw_res.is_primary && display_pref)) {
+		SDE_DEBUG(
+			"display preference is not met. is_primary: %d display_pref: %d\n",
+			(int)reqs->hw_res.is_primary, (int)display_pref);
 		return false;
 	}
 
@@ -605,6 +668,28 @@
 		}
 	}
 
+	if (lm_cfg->ds != DS_MAX) {
+		sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_DS);
+		while (_sde_rm_get_hw_locked(rm, &iter)) {
+			if (iter.blk->id == lm_cfg->ds) {
+				*ds = iter.blk;
+				break;
+			}
+		}
+
+		if (!*ds) {
+			SDE_DEBUG("lm %d failed to retrieve ds %d\n", lm->id,
+					lm_cfg->ds);
+			return false;
+		}
+
+		if (RESERVED_BY_OTHER(*ds, rsvp)) {
+			SDE_DEBUG("lm %d ds %d already reserved\n",
+					lm->id, (*ds)->id);
+			return false;
+		}
+	}
+
 	sde_rm_init_hw_iter(&iter, 0, SDE_HW_BLK_PINGPONG);
 	while (_sde_rm_get_hw_locked(rm, &iter)) {
 		if (iter.blk->id == lm_cfg->pingpong) {
@@ -622,6 +707,7 @@
 		SDE_DEBUG("lm %d pp %d already reserved\n", lm->id,
 				(*pp)->id);
 		*dspp = NULL;
+		*ds = NULL;
 		return false;
 	}
 
@@ -630,6 +716,7 @@
 			!(test_bit(SDE_PINGPONG_SPLIT, &pp_cfg->features))) {
 		SDE_DEBUG("pp %d doesn't support ppsplit\n", pp_cfg->id);
 		*dspp = NULL;
+		*ds = NULL;
 		return false;
 	}
 
@@ -639,11 +726,13 @@
 static int _sde_rm_reserve_lms(
 		struct sde_rm *rm,
 		struct sde_rm_rsvp *rsvp,
-		struct sde_rm_requirements *reqs)
+		struct sde_rm_requirements *reqs,
+		u8 *_lm_ids)
 
 {
 	struct sde_rm_hw_blk *lm[MAX_BLOCKS];
 	struct sde_rm_hw_blk *dspp[MAX_BLOCKS];
+	struct sde_rm_hw_blk *ds[MAX_BLOCKS];
 	struct sde_rm_hw_blk *pp[MAX_BLOCKS];
 	struct sde_rm_hw_iter iter_i, iter_j;
 	int lm_count = 0;
@@ -660,14 +749,24 @@
 			_sde_rm_get_hw_locked(rm, &iter_i)) {
 		memset(&lm, 0, sizeof(lm));
 		memset(&dspp, 0, sizeof(dspp));
+		memset(&ds, 0, sizeof(ds));
 		memset(&pp, 0, sizeof(pp));
 
 		lm_count = 0;
 		lm[lm_count] = iter_i.blk;
 
-		if (!_sde_rm_check_lm_and_get_connected_blks(rm, rsvp, reqs,
-				lm[lm_count], &dspp[lm_count], &pp[lm_count],
-				NULL))
+		SDE_DEBUG("blk id = %d, _lm_ids[%d] = %d\n",
+			iter_i.blk->id,
+			lm_count,
+			_lm_ids ? _lm_ids[lm_count] : -1);
+
+		if (_lm_ids && (lm[lm_count])->id != _lm_ids[lm_count])
+			continue;
+
+		if (!_sde_rm_check_lm_and_get_connected_blks(
+				rm, rsvp, reqs, lm[lm_count],
+				&dspp[lm_count], &ds[lm_count],
+				&pp[lm_count], NULL))
 			continue;
 
 		++lm_count;
@@ -680,12 +779,21 @@
 			if (iter_i.blk == iter_j.blk)
 				continue;
 
-			if (!_sde_rm_check_lm_and_get_connected_blks(rm, rsvp,
-					reqs, iter_j.blk, &dspp[lm_count],
+			if (!_sde_rm_check_lm_and_get_connected_blks(
+					rm, rsvp, reqs, iter_j.blk,
+					&dspp[lm_count], &ds[lm_count],
 					&pp[lm_count], iter_i.blk))
 				continue;
 
 			lm[lm_count] = iter_j.blk;
+			SDE_DEBUG("blk id = %d, _lm_ids[%d] = %d\n",
+				iter_i.blk->id,
+				lm_count,
+				_lm_ids ? _lm_ids[lm_count] : -1);
+
+			if (_lm_ids && (lm[lm_count])->id != _lm_ids[lm_count])
+				continue;
+
 			++lm_count;
 		}
 	}
@@ -704,8 +812,12 @@
 		if (dspp[i])
 			dspp[i]->rsvp_nxt = rsvp;
 
+		if (ds[i])
+			ds[i]->rsvp_nxt = rsvp;
+
 		SDE_EVT32(lm[i]->type, rsvp->enc_id, lm[i]->id, pp[i]->id,
-				dspp[i] ? dspp[i]->id : 0);
+				dspp[i] ? dspp[i]->id : 0,
+				ds[i] ? ds[i]->id : 0);
 	}
 
 	if (reqs->topology->top_name == SDE_RM_TOPOLOGY_PPSPLIT) {
@@ -734,7 +846,9 @@
 static int _sde_rm_reserve_ctls(
 		struct sde_rm *rm,
 		struct sde_rm_rsvp *rsvp,
-		const struct sde_rm_topology_def *top)
+		struct sde_rm_requirements *reqs,
+		const struct sde_rm_topology_def *top,
+		u8 *_ctl_ids)
 {
 	struct sde_rm_hw_blk *ctls[MAX_BLOCKS];
 	struct sde_rm_hw_iter iter;
@@ -746,23 +860,44 @@
 	while (_sde_rm_get_hw_locked(rm, &iter)) {
 		const struct sde_hw_ctl *ctl = to_sde_hw_ctl(iter.blk->hw);
 		unsigned long features = ctl->caps->features;
-		bool has_split_display, has_ppsplit;
+		bool has_split_display, has_ppsplit, primary_pref;
 
 		if (RESERVED_BY_OTHER(iter.blk, rsvp))
 			continue;
 
 		has_split_display = BIT(SDE_CTL_SPLIT_DISPLAY) & features;
 		has_ppsplit = BIT(SDE_CTL_PINGPONG_SPLIT) & features;
+		primary_pref = BIT(SDE_CTL_PRIMARY_PREF) & features;
 
 		SDE_DEBUG("ctl %d caps 0x%lX\n", iter.blk->id, features);
 
-		if (top->needs_split_display != has_split_display)
-			continue;
+		/*
+		 * bypass rest feature checks on finding CTL preferred
+		 * for primary displays.
+		 */
+		if (!primary_pref && !_ctl_ids) {
+			if (top->needs_split_display != has_split_display)
+				continue;
 
-		if (top->top_name == SDE_RM_TOPOLOGY_PPSPLIT && !has_ppsplit)
+			if (top->top_name == SDE_RM_TOPOLOGY_PPSPLIT &&
+					!has_ppsplit)
+				continue;
+		} else if (!(reqs->hw_res.is_primary && primary_pref)) {
+			SDE_DEBUG(
+				"display pref not met. is_primary: %d primary_pref: %d\n",
+				reqs->hw_res.is_primary, primary_pref);
 			continue;
+		}
 
 		ctls[i] = iter.blk;
+
+		SDE_DEBUG("blk id = %d, _ctl_ids[%d] = %d\n",
+			iter.blk->id, i,
+			_ctl_ids ? _ctl_ids[i] : -1);
+
+		if (_ctl_ids && (ctls[i]->id != _ctl_ids[i]))
+			continue;
+
 		SDE_DEBUG("ctl %d match\n", iter.blk->id);
 
 		if (++i == top->num_ctl)
@@ -783,7 +918,8 @@
 static int _sde_rm_reserve_dsc(
 		struct sde_rm *rm,
 		struct sde_rm_rsvp *rsvp,
-		const struct sde_rm_topology_def *top)
+		const struct sde_rm_topology_def *top,
+		u8 *_dsc_ids)
 {
 	struct sde_rm_hw_iter iter;
 	int alloc_count = 0;
@@ -798,6 +934,14 @@
 		if (RESERVED_BY_OTHER(iter.blk, rsvp))
 			continue;
 
+		SDE_DEBUG("blk id = %d, _dsc_ids[%d] = %d\n",
+			iter.blk->id,
+			alloc_count,
+			_dsc_ids ? _dsc_ids[alloc_count] : -1);
+
+		if (_dsc_ids && (iter.blk->id != _dsc_ids[alloc_count]))
+			continue;
+
 		iter.blk->rsvp_nxt = rsvp;
 		SDE_EVT32(iter.blk->type, rsvp->enc_id, iter.blk->id);
 
@@ -946,10 +1090,10 @@
 	 * - Check mixers without DSPPs
 	 * - Only then allow to grab from mixers with DSPP capability
 	 */
-	ret = _sde_rm_reserve_lms(rm, rsvp, reqs);
+	ret = _sde_rm_reserve_lms(rm, rsvp, reqs, NULL);
 	if (ret && !RM_RQ_DSPP(reqs)) {
 		reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP);
-		ret = _sde_rm_reserve_lms(rm, rsvp, reqs);
+		ret = _sde_rm_reserve_lms(rm, rsvp, reqs, NULL);
 	}
 
 	if (ret) {
@@ -962,11 +1106,11 @@
 	 * - Check mixers without Split Display
 	 * - Only then allow to grab from CTLs with split display capability
 	 */
-	_sde_rm_reserve_ctls(rm, rsvp, reqs->topology);
+	_sde_rm_reserve_ctls(rm, rsvp, reqs, reqs->topology, NULL);
 	if (ret && !reqs->topology->needs_split_display) {
 		memcpy(&topology, reqs->topology, sizeof(topology));
 		topology.needs_split_display = true;
-		_sde_rm_reserve_ctls(rm, rsvp, &topology);
+		_sde_rm_reserve_ctls(rm, rsvp, reqs, &topology, NULL);
 	}
 	if (ret) {
 		SDE_ERROR("unable to find appropriate CTL\n");
@@ -978,7 +1122,104 @@
 	if (ret)
 		return ret;
 
-	ret = _sde_rm_reserve_dsc(rm, rsvp, reqs->topology);
+	ret = _sde_rm_reserve_dsc(rm, rsvp, reqs->topology, NULL);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static int _sde_rm_make_next_rsvp_for_cont_splash(
+		struct sde_rm *rm,
+		struct drm_encoder *enc,
+		struct drm_crtc_state *crtc_state,
+		struct drm_connector_state *conn_state,
+		struct sde_rm_rsvp *rsvp,
+		struct sde_rm_requirements *reqs)
+{
+	int ret;
+	struct sde_rm_topology_def topology;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
+	int i;
+
+	if (!enc->dev || !enc->dev->dev_private) {
+		SDE_ERROR("drm device invalid\n");
+		return -EINVAL;
+	}
+	priv = enc->dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+	sde_kms = to_sde_kms(priv->kms);
+
+	for (i = 0; i < sde_kms->splash_data.lm_cnt; i++)
+		SDE_DEBUG("splash_data.lm_ids[%d] = %d\n",
+			i, sde_kms->splash_data.lm_ids[i]);
+
+	if (sde_kms->splash_data.lm_cnt !=
+			reqs->topology->num_lm)
+		SDE_DEBUG("Configured splash screen LMs != needed LM cnt\n");
+
+	/* Create reservation info, tag reserved blocks with it as we go */
+	rsvp->seq = ++rm->rsvp_next_seq;
+	rsvp->enc_id = enc->base.id;
+	rsvp->topology = reqs->topology->top_name;
+	list_add_tail(&rsvp->list, &rm->rsvps);
+
+	/*
+	 * Assign LMs and blocks whose usage is tied to them: DSPP & Pingpong.
+	 * Do assignment preferring to give away low-resource mixers first:
+	 * - Check mixers without DSPPs
+	 * - Only then allow to grab from mixers with DSPP capability
+	 */
+	ret = _sde_rm_reserve_lms(rm, rsvp, reqs,
+				sde_kms->splash_data.lm_ids);
+	if (ret && !RM_RQ_DSPP(reqs)) {
+		reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP);
+		ret = _sde_rm_reserve_lms(rm, rsvp, reqs,
+					sde_kms->splash_data.lm_ids);
+	}
+
+	if (ret) {
+		SDE_ERROR("unable to find appropriate mixers\n");
+		return ret;
+	}
+
+	/*
+	 * Do assignment preferring to give away low-resource CTLs first:
+	 * - Check mixers without Split Display
+	 * - Only then allow to grab from CTLs with split display capability
+	 */
+	for (i = 0; i < sde_kms->splash_data.ctl_top_cnt; i++)
+		SDE_DEBUG("splash_data.ctl_ids[%d] = %d\n",
+			i, sde_kms->splash_data.ctl_ids[i]);
+
+	_sde_rm_reserve_ctls(rm, rsvp, reqs, reqs->topology,
+			sde_kms->splash_data.ctl_ids);
+	if (ret && !reqs->topology->needs_split_display) {
+		memcpy(&topology, reqs->topology, sizeof(topology));
+		topology.needs_split_display = true;
+		_sde_rm_reserve_ctls(rm, rsvp, reqs, &topology,
+				sde_kms->splash_data.ctl_ids);
+	}
+	if (ret) {
+		SDE_ERROR("unable to find appropriate CTL\n");
+		return ret;
+	}
+
+	/* Assign INTFs, WBs, and blks whose usage is tied to them: CTL & CDM */
+	ret = _sde_rm_reserve_intf_related_hw(rm, rsvp, &reqs->hw_res);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < sde_kms->splash_data.dsc_cnt; i++)
+		SDE_DEBUG("splash_data.dsc_ids[%d] = %d\n",
+			i, sde_kms->splash_data.dsc_ids[i]);
+
+	ret = _sde_rm_reserve_dsc(rm, rsvp, reqs->topology,
+				sde_kms->splash_data.dsc_ids);
 	if (ret)
 		return ret;
 
@@ -1014,9 +1255,14 @@
 		return -EINVAL;
 	}
 
-	/* DSC blocks are hardwired for control path 0 and 1 */
-	if (reqs->topology->num_comp_enc)
-		reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DSPP);
+	/**
+	 * Set the requirement based on caps if not set from user space
+	 * This will ensure to select LM tied with DS blocks
+	 * Currently, DS blocks are tied with LM 0 and LM 1 (primary display)
+	 */
+	if (!RM_RQ_DS(reqs) && rm->hw_mdp->caps->has_dest_scaler &&
+		conn_state->connector->connector_type == DRM_MODE_CONNECTOR_DSI)
+		reqs->top_ctrl |= BIT(SDE_RM_TOPCTL_DS);
 
 	SDE_DEBUG("top_ctrl: 0x%llX num_h_tiles: %d\n", reqs->top_ctrl,
 			reqs->hw_res.display_num_of_h_tiles);
@@ -1066,6 +1312,33 @@
 	return NULL;
 }
 
+int sde_rm_update_topology(struct drm_connector_state *conn_state,
+	struct msm_display_topology *topology)
+{
+	int i, ret = 0;
+	struct msm_display_topology top;
+	enum sde_rm_topology_name top_name = SDE_RM_TOPOLOGY_NONE;
+
+	if (!conn_state)
+		return -EINVAL;
+
+	if (topology) {
+		top = *topology;
+		for (i = 0; i < SDE_RM_TOPOLOGY_MAX; i++)
+			if (RM_IS_TOPOLOGY_MATCH(g_top_table[i], top)) {
+				top_name = g_top_table[i].top_name;
+				break;
+			}
+	}
+
+	ret = msm_property_set_property(
+			sde_connector_get_propinfo(conn_state->connector),
+			sde_connector_get_property_state(conn_state),
+			CONNECTOR_PROP_TOPOLOGY_NAME, top_name);
+
+	return ret;
+}
+
 /**
  * _sde_rm_release_rsvp - release resources and release a reservation
  * @rm:	KMS handle
@@ -1147,12 +1420,6 @@
 		SDE_DEBUG("release rsvp[s%de%d]\n", rsvp->seq,
 				rsvp->enc_id);
 		_sde_rm_release_rsvp(rm, rsvp, conn);
-
-		(void) msm_property_set_property(
-				sde_connector_get_propinfo(conn),
-				sde_connector_get_property_state(conn->state),
-				CONNECTOR_PROP_TOPOLOGY_NAME,
-				SDE_RM_TOPOLOGY_NONE);
 	}
 
 end:
@@ -1168,18 +1435,6 @@
 	enum sde_hw_blk_type type;
 	int ret = 0;
 
-	ret = msm_property_set_property(
-			sde_connector_get_propinfo(conn_state->connector),
-			sde_connector_get_property_state(conn_state),
-			CONNECTOR_PROP_TOPOLOGY_NAME,
-			rsvp->topology);
-	if (ret) {
-		SDE_ERROR("failed to set topology name property, ret %d\n",
-				ret);
-		_sde_rm_release_rsvp(rm, rsvp, conn_state->connector);
-		return ret;
-	}
-
 	/* Swap next rsvp to be the active */
 	for (type = 0; type < SDE_HW_BLK_MAX; type++) {
 		list_for_each_entry(blk, &rm->hw_blks[type], list) {
@@ -1208,6 +1463,8 @@
 {
 	struct sde_rm_rsvp *rsvp_cur, *rsvp_nxt;
 	struct sde_rm_requirements reqs;
+	struct msm_drm_private *priv;
+	struct sde_kms *sde_kms;
 	int ret;
 
 	if (!rm || !enc || !crtc_state || !conn_state) {
@@ -1215,8 +1472,20 @@
 		return -EINVAL;
 	}
 
+	if (!enc->dev || !enc->dev->dev_private) {
+		SDE_ERROR("drm device invalid\n");
+		return -EINVAL;
+	}
+	priv = enc->dev->dev_private;
+	if (!priv->kms) {
+		SDE_ERROR("invalid kms\n");
+		return -EINVAL;
+	}
+	sde_kms = to_sde_kms(priv->kms);
+
 	/* Check if this is just a page-flip */
-	if (!drm_atomic_crtc_needs_modeset(crtc_state))
+	if (!sde_kms->cont_splash_en &&
+			!drm_atomic_crtc_needs_modeset(crtc_state))
 		return 0;
 
 	SDE_DEBUG("reserving hw for conn %d enc %d crtc %d test_only %d\n",
@@ -1264,17 +1533,17 @@
 		_sde_rm_release_rsvp(rm, rsvp_cur, conn_state->connector);
 		rsvp_cur = NULL;
 		_sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_CLEAR);
-		(void) msm_property_set_property(
-				sde_connector_get_propinfo(
-						conn_state->connector),
-				sde_connector_get_property_state(conn_state),
-				CONNECTOR_PROP_TOPOLOGY_NAME,
-				SDE_RM_TOPOLOGY_NONE);
 	}
 
 	/* Check the proposed reservation, store it in hw's "next" field */
-	ret = _sde_rm_make_next_rsvp(rm, enc, crtc_state, conn_state,
+	if (sde_kms->cont_splash_en) {
+		SDE_DEBUG("cont_splash feature enabled\n");
+		ret = _sde_rm_make_next_rsvp_for_cont_splash
+			(rm, enc, crtc_state, conn_state, rsvp_nxt, &reqs);
+	} else {
+		ret = _sde_rm_make_next_rsvp(rm, enc, crtc_state, conn_state,
 			rsvp_nxt, &reqs);
+	}
 
 	_sde_rm_print_rsvps(rm, SDE_RM_STAGE_AFTER_RSVPNEXT);
 
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h
index b4a801a..0545609 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.h
+++ b/drivers/gpu/drm/msm/sde/sde_rm.h
@@ -56,11 +56,13 @@
  *                               Normal behavior would not impact the
  *                               reservation list during the AtomicTest phase.
  * @SDE_RM_TOPCTL_DSPP: Require layer mixers with DSPP capabilities
+ * @SDE_RM_TOPCTL_DS  : Require layer mixers with DS capabilities
  */
 enum sde_rm_topology_control {
 	SDE_RM_TOPCTL_RESERVE_LOCK,
 	SDE_RM_TOPCTL_RESERVE_CLEAR,
 	SDE_RM_TOPCTL_DSPP,
+	SDE_RM_TOPCTL_DS,
 };
 
 /**
@@ -105,6 +107,15 @@
 };
 
 /**
+ * sde_rm_get_topology_name - get the name of the given topology config
+ * @topology: msm_display_topology topology config
+ * @Return: name of the given topology
+ */
+enum sde_rm_topology_name sde_rm_get_topology_name(
+	struct msm_display_topology topology);
+
+
+/**
  * sde_rm_init - Read hardware catalog and create reservation tracking objects
  *	for all HW blocks.
  * @rm: SDE Resource Manager handle
@@ -195,4 +206,13 @@
  */
 int sde_rm_check_property_topctl(uint64_t val);
 
+/**
+ * sde_rm_update_topology - sets topology property of the connector
+ * @conn_state: drm state of the connector
+ * @topology: topology selected for the display
+ * @return: 0 on success or error
+ */
+int sde_rm_update_topology(struct drm_connector_state *conn_state,
+	struct msm_display_topology *topology);
+
 #endif /* __SDE_RM_H__ */
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.c b/drivers/gpu/drm/msm/sde/sde_vbif.c
index 7fcd03e..0dbc027 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -60,6 +60,58 @@
 	return rc;
 }
 
+int sde_vbif_halt_plane_xin(struct sde_kms *sde_kms, u32 xin_id, u32 clk_ctrl)
+{
+	struct sde_hw_vbif *vbif = NULL;
+	struct sde_hw_mdp *mdp;
+	bool forced_on = false;
+	bool status;
+	int rc = 0;
+
+	if (!sde_kms) {
+		SDE_ERROR("invalid argument\n");
+		return -EINVAL;
+	}
+
+	vbif = sde_kms->hw_vbif[VBIF_RT];
+	mdp = sde_kms->hw_mdp;
+	if (!vbif || !mdp || !vbif->ops.get_halt_ctrl ||
+		       !vbif->ops.set_halt_ctrl ||
+		       !mdp->ops.setup_clk_force_ctrl) {
+		SDE_ERROR("invalid vbif or mdp arguments\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * If status is 0, then make sure client clock is not gated
+	 * while halting by forcing it ON only if it was not previously
+	 * forced on. If status is 1 then its already halted.
+	 */
+	status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+	if (status == 0)
+		forced_on = mdp->ops.setup_clk_force_ctrl(mdp, clk_ctrl, true);
+	else
+		return 0;
+
+	/* send halt request for unused plane's xin client */
+	vbif->ops.set_halt_ctrl(vbif, xin_id, true);
+
+	rc = _sde_vbif_wait_for_xin_halt(vbif, xin_id);
+	if (rc) {
+		SDE_ERROR(
+		"wait failed for pipe halt:xin_id %u, clk_ctrl %u, rc %u\n",
+			xin_id, clk_ctrl, rc);
+		SDE_EVT32(xin_id, clk_ctrl, rc, SDE_EVTLOG_ERROR);
+	}
+
+	/* open xin client to enable transactions */
+	vbif->ops.set_halt_ctrl(vbif, xin_id, false);
+	if (forced_on)
+		mdp->ops.setup_clk_force_ctrl(mdp, clk_ctrl, false);
+
+	return rc;
+}
+
 /**
  * _sde_vbif_apply_dynamic_ot_limit - determine OT based on usecase parameters
  * @vbif:	Pointer to hardware vbif driver
@@ -169,13 +221,15 @@
 
 	for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
 		if (sde_kms->hw_vbif[i] &&
-				sde_kms->hw_vbif[i]->idx == params->vbif_idx)
+				sde_kms->hw_vbif[i]->idx == params->vbif_idx) {
 			vbif = sde_kms->hw_vbif[i];
+			break;
+		}
 	}
 
 	if (!vbif || !mdp) {
 		SDE_DEBUG("invalid arguments vbif %d mdp %d\n",
-				vbif != 0, mdp != 0);
+				vbif != NULL, mdp != NULL);
 		return;
 	}
 
@@ -214,6 +268,58 @@
 	return;
 }
 
+bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms,
+		struct sde_vbif_set_xin_halt_params *params)
+{
+	struct sde_hw_vbif *vbif = NULL;
+	struct sde_hw_mdp *mdp;
+	bool forced_on = false;
+	int ret, i;
+
+	if (!sde_kms || !params) {
+		SDE_ERROR("invalid arguments\n");
+		return false;
+	}
+	mdp = sde_kms->hw_mdp;
+
+	for (i = 0; i < ARRAY_SIZE(sde_kms->hw_vbif); i++) {
+		if (sde_kms->hw_vbif[i] &&
+				sde_kms->hw_vbif[i]->idx == params->vbif_idx) {
+			vbif = sde_kms->hw_vbif[i];
+			break;
+		}
+	}
+
+	if (!vbif || !mdp) {
+		SDE_DEBUG("invalid arguments vbif %d mdp %d\n",
+				vbif != NULL, mdp != NULL);
+		return false;
+	}
+
+	if (!mdp->ops.setup_clk_force_ctrl ||
+			!vbif->ops.set_halt_ctrl)
+		return false;
+
+	if (params->enable) {
+		forced_on = mdp->ops.setup_clk_force_ctrl(mdp,
+				params->clk_ctrl, true);
+
+		vbif->ops.set_halt_ctrl(vbif, params->xin_id, true);
+
+		ret = _sde_vbif_wait_for_xin_halt(vbif, params->xin_id);
+		if (ret)
+			SDE_EVT32(vbif->idx, params->xin_id, SDE_EVTLOG_ERROR);
+	} else {
+		vbif->ops.set_halt_ctrl(vbif, params->xin_id, false);
+
+		if (params->forced_on)
+			mdp->ops.setup_clk_force_ctrl(mdp,
+					params->clk_ctrl, false);
+	}
+
+	return forced_on;
+}
+
 void sde_vbif_set_qos_remap(struct sde_kms *sde_kms,
 		struct sde_vbif_set_qos_params *params)
 {
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.h b/drivers/gpu/drm/msm/sde/sde_vbif.h
index a4830a0..0edc1a6 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -35,6 +35,23 @@
 };
 
 /**
+ * struct sde_vbif_set_xin_halt_params - xin halt parameters
+ * @vbif_idx: vbif identifier
+ * @xin_id: client interface identifier
+ * @clk_ctrl: clock control identifier of the xin
+ * @forced_on: whether or not previous call to xin halt forced the clocks on,
+ *	only applicable to xin halt disable calls
+ * @enable: whether to enable/disable xin halts
+ */
+struct sde_vbif_set_xin_halt_params {
+	u32 vbif_idx;
+	u32 xin_id;
+	u32 clk_ctrl;
+	bool forced_on;
+	bool enable;
+};
+
+/**
  * struct sde_vbif_set_qos_params - QoS remapper parameter
  * @vbif_idx: vbif identifier
  * @xin_id: client interface identifier
@@ -59,6 +76,16 @@
 		struct sde_vbif_set_ot_params *params);
 
 /**
+ * sde_vbif_set_xin_halt - halt one of the xin ports
+ *	This function isn't thread safe.
+ * @sde_kms:	SDE handler
+ * @params:	Pointer to halt configuration parameters
+ * Returns:	Whether or not VBIF clocks were forced on
+ */
+bool sde_vbif_set_xin_halt(struct sde_kms *sde_kms,
+		struct sde_vbif_set_xin_halt_params *params);
+
+/**
  * sde_vbif_set_qos_remap - set QoS priority level remap
  * @sde_kms:	SDE handler
  * @params:	Pointer to QoS configuration parameters
@@ -78,6 +105,20 @@
  */
 void sde_vbif_init_memtypes(struct sde_kms *sde_kms);
 
+/**
+ * sde_vbif_halt_plane_xin - halts the xin client for the unused plane
+ * On unused plane, check if the vbif for this plane is idle or not.
+ * If not then first force_on the planes clock and then send the
+ * halt request. Wait for some time then check for the vbif idle
+ * or not again.
+ * @sde_kms:	SDE handler
+ * @xin_id:	xin id of the unused plane
+ * @clk_ctrl:	clk ctrl type for the unused plane
+ * Returns:	0 on success, error code otherwise
+ */
+int sde_vbif_halt_plane_xin(struct sde_kms *sde_kms, u32 xin_id,
+	       u32 clk_ctrl);
+
 #ifdef CONFIG_DEBUG_FS
 int sde_debugfs_vbif_init(struct sde_kms *sde_kms, struct dentry *debugfs_root);
 void sde_debugfs_vbif_destroy(struct sde_kms *sde_kms);
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.c b/drivers/gpu/drm/msm/sde/sde_wb.c
index 576a8f3..71c8b63 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_wb.c
@@ -162,7 +162,9 @@
 		u32 count_modes, struct drm_mode_modeinfo __user *modes,
 		bool connected)
 {
+	struct drm_mode_modeinfo *modeinfo = NULL;
 	int ret = 0;
+	int i;
 
 	if (!wb_dev || !wb_dev->connector ||
 			(wb_dev->connector->connector_type !=
@@ -176,6 +178,56 @@
 	if (connected) {
 		SDE_DEBUG("connect\n");
 
+		if (count_modes && modes) {
+			modeinfo = kcalloc(count_modes,
+					sizeof(struct drm_mode_modeinfo),
+					GFP_KERNEL);
+			if (!modeinfo) {
+				SDE_ERROR("invalid params\n");
+				ret = -ENOMEM;
+				goto error;
+			}
+
+			if (copy_from_user(modeinfo, modes,
+					count_modes *
+					sizeof(struct drm_mode_modeinfo))) {
+				SDE_ERROR("failed to copy modes\n");
+				kfree(modeinfo);
+				ret = -EFAULT;
+				goto error;
+			}
+
+			for (i = 0; i < count_modes; i++) {
+				struct drm_display_mode dispmode;
+
+				memset(&dispmode, 0, sizeof(dispmode));
+				ret = drm_mode_convert_umode(&dispmode,
+						&modeinfo[i]);
+				if (ret) {
+					SDE_ERROR(
+						"failed to convert mode %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x status:%d rc:%d\n",
+						i,
+						modeinfo[i].name,
+						modeinfo[i].vrefresh,
+						modeinfo[i].clock,
+						modeinfo[i].hdisplay,
+						modeinfo[i].hsync_start,
+						modeinfo[i].hsync_end,
+						modeinfo[i].htotal,
+						modeinfo[i].vdisplay,
+						modeinfo[i].vsync_start,
+						modeinfo[i].vsync_end,
+						modeinfo[i].vtotal,
+						modeinfo[i].type,
+						modeinfo[i].flags,
+						dispmode.status,
+						ret);
+					kfree(modeinfo);
+					goto error;
+				}
+			}
+		}
+
 		if (wb_dev->modes) {
 			wb_dev->count_modes = 0;
 
@@ -183,29 +235,8 @@
 			wb_dev->modes = NULL;
 		}
 
-		if (count_modes && modes) {
-			wb_dev->modes = kcalloc(count_modes,
-					sizeof(struct drm_mode_modeinfo),
-					GFP_KERNEL);
-			if (!wb_dev->modes) {
-				SDE_ERROR("invalid params\n");
-				ret = -ENOMEM;
-				goto error;
-			}
-
-			if (copy_from_user(wb_dev->modes, modes,
-					count_modes *
-					sizeof(struct drm_mode_modeinfo))) {
-				SDE_ERROR("failed to copy modes\n");
-				kfree(wb_dev->modes);
-				wb_dev->modes = NULL;
-				ret = -EFAULT;
-				goto error;
-			}
-
-			wb_dev->count_modes = count_modes;
-		}
-
+		wb_dev->count_modes = count_modes;
+		wb_dev->modes = modeinfo;
 		wb_dev->detect_status = connector_status_connected;
 	} else {
 		SDE_DEBUG("disconnect\n");
@@ -289,22 +320,30 @@
 }
 
 int sde_wb_get_mode_info(const struct drm_display_mode *drm_mode,
-	struct msm_mode_info *mode_info, u32 max_mixer_width)
+	struct msm_mode_info *mode_info, u32 max_mixer_width, void *display)
 {
 	const u32 dual_lm = 2;
 	const u32 single_lm = 1;
 	const u32 single_intf = 1;
 	const u32 no_enc = 0;
 	struct msm_display_topology *topology;
+	struct sde_wb_device *wb_dev = display;
+	u16 hdisplay;
+	int i;
 
-	if (!drm_mode || !mode_info || !max_mixer_width) {
+	if (!drm_mode || !mode_info || !max_mixer_width || !display) {
 		pr_err("invalid params\n");
 		return -EINVAL;
 	}
 
+	hdisplay = drm_mode->hdisplay;
+
+	/* find maximum display width to support */
+	for (i = 0; i < wb_dev->count_modes; i++)
+		hdisplay = max(hdisplay, wb_dev->modes[i].hdisplay);
+
 	topology = &mode_info->topology;
-	topology->num_lm = (max_mixer_width <= drm_mode->hdisplay) ?
-							dual_lm : single_lm;
+	topology->num_lm = (max_mixer_width <= hdisplay) ? dual_lm : single_lm;
 	topology->num_enc = no_enc;
 	topology->num_intf = single_intf;
 
@@ -313,49 +352,20 @@
 	return 0;
 }
 
-int sde_wb_connector_post_init(struct drm_connector *connector,
-		void *info,
-		void *display)
+int sde_wb_connector_set_info_blob(struct drm_connector *connector,
+		void *info, void *display, struct msm_mode_info *mode_info)
 {
-	struct sde_connector *c_conn;
 	struct sde_wb_device *wb_dev = display;
 	const struct sde_format_extended *format_list;
-	static const struct drm_prop_enum_list e_fb_translation_mode[] = {
-		{SDE_DRM_FB_NON_SEC, "non_sec"},
-		{SDE_DRM_FB_SEC, "sec"},
-	};
 
 	if (!connector || !info || !display || !wb_dev->wb_cfg) {
 		SDE_ERROR("invalid params\n");
 		return -EINVAL;
 	}
 
-	c_conn = to_sde_connector(connector);
-	wb_dev->connector = connector;
-	wb_dev->detect_status = connector_status_connected;
 	format_list = wb_dev->wb_cfg->format_list;
 
 	/*
-	 * Add extra connector properties
-	 */
-	msm_property_install_range(&c_conn->property_info, "FB_ID",
-			0x0, 0, ~0, ~0, CONNECTOR_PROP_OUT_FB);
-	msm_property_install_range(&c_conn->property_info, "DST_X",
-			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_X);
-	msm_property_install_range(&c_conn->property_info, "DST_Y",
-			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_Y);
-	msm_property_install_range(&c_conn->property_info, "DST_W",
-			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_W);
-	msm_property_install_range(&c_conn->property_info, "DST_H",
-			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_H);
-	msm_property_install_enum(&c_conn->property_info,
-			"fb_translation_mode",
-			0x0,
-			0, e_fb_translation_mode,
-			ARRAY_SIZE(e_fb_translation_mode),
-			CONNECTOR_PROP_FB_TRANSLATION_MODE);
-
-	/*
 	 * Populate info buffer
 	 */
 	if (format_list) {
@@ -385,6 +395,47 @@
 	return 0;
 }
 
+int sde_wb_connector_post_init(struct drm_connector *connector, void *display)
+{
+	struct sde_connector *c_conn;
+	struct sde_wb_device *wb_dev = display;
+	static const struct drm_prop_enum_list e_fb_translation_mode[] = {
+		{SDE_DRM_FB_NON_SEC, "non_sec"},
+		{SDE_DRM_FB_SEC, "sec"},
+	};
+
+	if (!connector || !display || !wb_dev->wb_cfg) {
+		SDE_ERROR("invalid params\n");
+		return -EINVAL;
+	}
+
+	c_conn = to_sde_connector(connector);
+	wb_dev->connector = connector;
+	wb_dev->detect_status = connector_status_connected;
+
+	/*
+	 * Add extra connector properties
+	 */
+	msm_property_install_range(&c_conn->property_info, "FB_ID",
+			0x0, 0, ~0, 0, CONNECTOR_PROP_OUT_FB);
+	msm_property_install_range(&c_conn->property_info, "DST_X",
+			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_X);
+	msm_property_install_range(&c_conn->property_info, "DST_Y",
+			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_Y);
+	msm_property_install_range(&c_conn->property_info, "DST_W",
+			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_W);
+	msm_property_install_range(&c_conn->property_info, "DST_H",
+			0x0, 0, UINT_MAX, 0, CONNECTOR_PROP_DST_H);
+	msm_property_install_enum(&c_conn->property_info,
+			"fb_translation_mode",
+			0x0,
+			0, e_fb_translation_mode,
+			ARRAY_SIZE(e_fb_translation_mode),
+			CONNECTOR_PROP_FB_TRANSLATION_MODE);
+
+	return 0;
+}
+
 struct drm_framebuffer *sde_wb_get_output_fb(struct sde_wb_device *wb_dev)
 {
 	struct drm_framebuffer *fb;
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.h b/drivers/gpu/drm/msm/sde/sde_wb.h
index aa57d3e..d414bd0 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_wb.h
@@ -131,13 +131,23 @@
 /**
  * sde_wb_connector_post_init - perform writeback specific initialization
  * @connector: Pointer to drm connector structure
- * @info: Pointer to connector info
  * @display: Pointer to private display structure
  * Returns: Zero on success
  */
-int sde_wb_connector_post_init(struct drm_connector *connector,
+int sde_wb_connector_post_init(struct drm_connector *connector, void *display);
+
+/**
+ * sde_wb_connector_set_info_blob - perform writeback info blob initialization
+ * @connector: Pointer to drm connector structure
+ * @info: Pointer to connector info
+ * @display: Pointer to private display structure
+ * @mode_info: Pointer to the mode info structure
+ * Returns: Zero on success
+ */
+int sde_wb_connector_set_info_blob(struct drm_connector *connector,
 		void *info,
-		void *display);
+		void *display,
+		struct msm_mode_info *mode_info);
 
 /**
  * sde_wb_connector_detect - perform writeback connection status detection
@@ -190,10 +200,12 @@
  * @drm_mode: Display mode set for the display
  * @mode_info: Out parameter. information of the mode.
  * @max_mixer_width: max width supported by HW layer mixer
+ * @display: Pointer to private display structure
  * Returns: zero on success
  */
 int sde_wb_get_mode_info(const struct drm_display_mode *drm_mode,
-		struct msm_mode_info *mode_info, u32 max_mixer_width);
+		struct msm_mode_info *mode_info, u32 max_mixer_width,
+		void *display);
 
 /**
  * sde_wb_connector_get_wb - retrieve writeback device of the given connector
@@ -278,7 +290,8 @@
 static inline
 int sde_wb_connector_post_init(struct drm_connector *connector,
 		void *info,
-		void *display)
+		void *display,
+		struct msm_mode_info *mode_info)
 {
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index 7e58c2f..6b5be3b 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -44,6 +44,7 @@
 
 /* offsets from sde top address for the debug buses */
 #define DBGBUS_SSPP0	0x188
+#define DBGBUS_AXI_INTF	0x194
 #define DBGBUS_SSPP1	0x298
 #define DBGBUS_DSPP	0x348
 #define DBGBUS_PERIPH	0x418
@@ -66,6 +67,7 @@
 /* print debug ranges in groups of 4 u32s */
 #define REG_DUMP_ALIGN		16
 
+#define RSC_DEBUG_MUX_SEL_SDM845 9
 /**
  * struct sde_dbg_reg_offset - tracking for start and end of region
  * @start: start offset
@@ -128,7 +130,8 @@
 	u32 wr_addr;
 	u32 block_id;
 	u32 test_id;
-	void (*analyzer)(struct sde_debug_bus_entry *entry, u32 val);
+	void (*analyzer)(void __iomem *mem_base,
+				struct sde_debug_bus_entry *entry, u32 val);
 };
 
 struct vbif_debug_bus_entry {
@@ -165,6 +168,7 @@
  * @evtlog: event log instance
  * @reg_base_list: list of register dumping regions
  * @dev: device pointer
+ * @mutex: mutex to serialize access to serialze dumps, debugfs access
  * @power_ctrl: callback structure for enabling power for reading hw registers
  * @req_dump_blks: list of blocks requested for dumping
  * @panic_on_err: whether to kernel panic after triggering dump via debugfs
@@ -174,11 +178,13 @@
  * @dbgbus_sde: debug bus structure for the sde
  * @dbgbus_vbif_rt: debug bus structure for the realtime vbif
  * @dump_all: dump all entries in register dump
+ * @dsi_dbg_bus: dump dsi debug bus register
  */
 static struct sde_dbg_base {
 	struct sde_dbg_evtlog *evtlog;
 	struct list_head reg_base_list;
 	struct device *dev;
+	struct mutex mutex;
 	struct sde_dbg_power_ctrl power_ctrl;
 
 	struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX];
@@ -191,20 +197,21 @@
 	struct sde_dbg_sde_debug_bus dbgbus_sde;
 	struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt;
 	bool dump_all;
+	bool dsi_dbg_bus;
 } sde_dbg_base;
 
 /* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */
 struct sde_dbg_evtlog *sde_dbg_base_evtlog;
 
-static void _sde_debug_bus_xbar_dump(struct sde_debug_bus_entry *entry,
-		u32 val)
+static void _sde_debug_bus_xbar_dump(void __iomem *mem_base,
+		struct sde_debug_bus_entry *entry, u32 val)
 {
 	dev_err(sde_dbg_base.dev, "xbar 0x%x %d %d 0x%x\n",
 			entry->wr_addr, entry->block_id, entry->test_id, val);
 }
 
-static void _sde_debug_bus_lm_dump(struct sde_debug_bus_entry *entry,
-		u32 val)
+static void _sde_debug_bus_lm_dump(void __iomem *mem_base,
+		struct sde_debug_bus_entry *entry, u32 val)
 {
 	if (!(val & 0xFFF000))
 		return;
@@ -213,8 +220,8 @@
 			entry->wr_addr, entry->block_id, entry->test_id, val);
 }
 
-static void _sde_debug_bus_ppb0_dump(struct sde_debug_bus_entry *entry,
-		u32 val)
+static void _sde_debug_bus_ppb0_dump(void __iomem *mem_base,
+		struct sde_debug_bus_entry *entry, u32 val)
 {
 	if (!(val & BIT(15)))
 		return;
@@ -223,8 +230,8 @@
 			entry->wr_addr, entry->block_id, entry->test_id, val);
 }
 
-static void _sde_debug_bus_ppb1_dump(struct sde_debug_bus_entry *entry,
-		u32 val)
+static void _sde_debug_bus_ppb1_dump(void __iomem *mem_base,
+		struct sde_debug_bus_entry *entry, u32 val)
 {
 	if (!(val & BIT(15)))
 		return;
@@ -233,6 +240,29 @@
 			entry->wr_addr, entry->block_id, entry->test_id, val);
 }
 
+static void _sde_debug_bus_axi_dump_sdm845(void __iomem *mem_base,
+		struct sde_debug_bus_entry *entry, u32 val)
+{
+	u32 status, i;
+
+	if (!mem_base || !entry)
+		return;
+
+	for (i = 0; i <= RSC_DEBUG_MUX_SEL_SDM845; i++) {
+		sde_rsc_debug_dump(i);
+
+		/* make sure that mux_sel updated */
+		wmb();
+
+		/* read status again after rsc routes the debug bus */
+		status = readl_relaxed(mem_base + DBGBUS_DSPP_STATUS);
+
+		dev_err(sde_dbg_base.dev, "rsc mux_sel:%d 0x%x %d %d 0x%x\n",
+			i, entry->wr_addr, entry->block_id,
+			entry->test_id, status);
+	}
+}
+
 static struct sde_debug_bus_entry dbg_bus_sde_8998[] = {
 
 	/* Unpack 0 sspp 0*/
@@ -1986,6 +2016,9 @@
 	{ DBGBUS_PERIPH, 71, 3},
 	{ DBGBUS_PERIPH, 71, 4},
 	{ DBGBUS_PERIPH, 71, 5},
+
+	/* axi - should be last entry */
+	{ DBGBUS_AXI_INTF, 62, 0, _sde_debug_bus_axi_dump_sdm845},
 };
 
 static struct vbif_debug_bus_entry vbif_dbg_bus_msm8998[] = {
@@ -2001,12 +2034,13 @@
 /**
  * _sde_dbg_enable_power - use callback to turn power on for hw register access
  * @enable: whether to turn power on or off
+ * Return: zero if success; error code otherwise
  */
-static inline void _sde_dbg_enable_power(int enable)
+static inline int _sde_dbg_enable_power(int enable)
 {
 	if (!sde_dbg_base.power_ctrl.enable_fn)
-		return;
-	sde_dbg_base.power_ctrl.enable_fn(
+		return -EINVAL;
+	return sde_dbg_base.power_ctrl.enable_fn(
 			sde_dbg_base.power_ctrl.handle,
 			sde_dbg_base.power_ctrl.client,
 			enable);
@@ -2030,6 +2064,7 @@
 	u32 *dump_addr = NULL;
 	char *end_addr;
 	int i;
+	int rc;
 
 	if (!len_bytes)
 		return;
@@ -2070,8 +2105,13 @@
 		}
 	}
 
-	if (!from_isr)
-		_sde_dbg_enable_power(true);
+	if (!from_isr) {
+		rc = _sde_dbg_enable_power(true);
+		if (rc) {
+			pr_err("failed to enable power %d\n", rc);
+			return;
+		}
+	}
 
 	for (i = 0; i < len_align; i++) {
 		u32 x0, x4, x8, xc;
@@ -2255,6 +2295,7 @@
 	u32 offset;
 	void __iomem *mem_base = NULL;
 	struct sde_dbg_reg_base *reg_base;
+	int rc;
 
 	if (!bus || !bus->cmn.entries_size)
 		return;
@@ -2300,7 +2341,12 @@
 		}
 	}
 
-	_sde_dbg_enable_power(true);
+	rc = _sde_dbg_enable_power(true);
+	if (rc) {
+		pr_err("failed to enable power %d\n", rc);
+		return;
+	}
+
 	for (i = 0; i < bus->cmn.entries_size; i++) {
 		head = bus->entries + i;
 		writel_relaxed(TEST_MASK(head->block_id, head->test_id),
@@ -2332,7 +2378,7 @@
 		}
 
 		if (head->analyzer)
-			head->analyzer(head, status);
+			head->analyzer(mem_base, head, status);
 
 		/* Disable debug bus once we are done */
 		writel_relaxed(0, mem_base + head->wr_addr);
@@ -2394,6 +2440,7 @@
 	struct vbif_debug_bus_entry *dbg_bus;
 	u32 bus_size;
 	struct sde_dbg_reg_base *reg_base;
+	int rc;
 
 	if (!bus || !bus->cmn.entries_size)
 		return;
@@ -2451,7 +2498,11 @@
 		}
 	}
 
-	_sde_dbg_enable_power(true);
+	rc = _sde_dbg_enable_power(true);
+	if (rc) {
+		pr_err("failed to enable power %d\n", rc);
+		return;
+	}
 
 	value = readl_relaxed(mem_base + MMSS_VBIF_CLKON);
 	writel_relaxed(value | BIT(1), mem_base + MMSS_VBIF_CLKON);
@@ -2523,7 +2574,10 @@
 {
 	int i;
 
-	sde_evtlog_dump_all(sde_dbg_base.evtlog);
+	mutex_lock(&sde_dbg_base.mutex);
+
+	if (dump_all)
+		sde_evtlog_dump_all(sde_dbg_base.evtlog);
 
 	if (dump_all || !blk_arr || !len) {
 		_sde_dump_reg_all();
@@ -2541,8 +2595,13 @@
 	if (dump_dbgbus_vbif_rt)
 		_sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
 
+	if (sde_dbg_base.dsi_dbg_bus || dump_all)
+		dsi_ctrl_debug_dump();
+
 	if (do_panic && sde_dbg_base.panic_on_err)
 		panic(name);
+
+	mutex_unlock(&sde_dbg_base.mutex);
 }
 
 /**
@@ -2615,6 +2674,9 @@
 		if (!strcmp(blk_name, "vbif_dbg_bus"))
 			dump_dbgbus_vbif_rt = true;
 
+		if (!strcmp(blk_name, "dsi_dbg_bus"))
+			sde_dbg_base.dsi_dbg_bus = true;
+
 		if (!strcmp(blk_name, "panic"))
 			do_panic = true;
 	}
@@ -2627,6 +2689,7 @@
 				dump_dbgbus_sde;
 		sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work =
 				dump_dbgbus_vbif_rt;
+		sde_dbg_base.dump_all = dump_all;
 		schedule_work(&sde_dbg_base.dump_work);
 	} else {
 		_sde_dump_array(blk_arr, blk_len, do_panic, name,
@@ -2641,6 +2704,9 @@
  */
 static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
 {
+	if (!inode || !file)
+		return -EINVAL;
+
 	/* non-seekable */
 	file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
 	file->private_data = inode->i_private;
@@ -2660,8 +2726,11 @@
 	ssize_t len = 0;
 	char evtlog_buf[SDE_EVTLOG_BUF_MAX];
 
+	if (!buff || !ppos)
+		return -EINVAL;
+
 	len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
-			SDE_EVTLOG_BUF_MAX);
+			SDE_EVTLOG_BUF_MAX, true);
 	if (copy_to_user(buff, evtlog_buf, len))
 		return -EFAULT;
 	*ppos += len;
@@ -2721,7 +2790,7 @@
  */
 static int sde_evtlog_filter_open(struct inode *inode, struct file *file)
 {
-	if (!file)
+	if (!inode || !file)
 		return -EINVAL;
 
 	return single_open(file, sde_evtlog_filter_show, inode->i_private);
@@ -2740,6 +2809,9 @@
 	char *tmp_filter = NULL;
 	ssize_t rc = 0;
 
+	if (!user_buf)
+		return -EINVAL;
+
 	if (count > 0) {
 		/* copy user provided string and null terminate it */
 		tmp_filter = kzalloc(count + 1, GFP_KERNEL);
@@ -2775,13 +2847,23 @@
  */
 static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
 {
-	struct sde_dbg_reg_base *dbg = file->private_data;
+	struct sde_dbg_reg_base *dbg;
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
+	if (!dbg)
+		return -ENODEV;
+
+	mutex_lock(&sde_dbg_base.mutex);
 	if (dbg && dbg->buf) {
 		kfree(dbg->buf);
 		dbg->buf_len = 0;
 		dbg->buf = NULL;
 	}
+	mutex_unlock(&sde_dbg_base.mutex);
+
 	return 0;
 }
 
@@ -2796,11 +2878,15 @@
 static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
 		const char __user *user_buf, size_t count, loff_t *ppos)
 {
-	struct sde_dbg_reg_base *dbg = file->private_data;
+	struct sde_dbg_reg_base *dbg;
 	u32 off = 0;
 	u32 cnt = DEFAULT_BASE_REG_CNT;
 	char buf[24];
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
 	if (!dbg)
 		return -ENODEV;
 
@@ -2824,8 +2910,13 @@
 	if (cnt > (dbg->max_offset - off))
 		cnt = dbg->max_offset - off;
 
+	if (cnt == 0)
+		return -EINVAL;
+
+	mutex_lock(&sde_dbg_base.mutex);
 	dbg->off = off;
 	dbg->cnt = cnt;
+	mutex_unlock(&sde_dbg_base.mutex);
 
 	pr_debug("offset=%x cnt=%x\n", off, cnt);
 
@@ -2842,27 +2933,42 @@
 static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
 			char __user *buff, size_t count, loff_t *ppos)
 {
-	struct sde_dbg_reg_base *dbg = file->private_data;
+	struct sde_dbg_reg_base *dbg;
 	int len = 0;
 	char buf[24] = {'\0'};
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
 	if (!dbg)
 		return -ENODEV;
 
+	if (!ppos)
+		return -EINVAL;
+
 	if (*ppos)
 		return 0;	/* the end */
 
-	if (dbg->off % sizeof(u32))
+	mutex_lock(&sde_dbg_base.mutex);
+	if (dbg->off % sizeof(u32)) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		return -EFAULT;
+	}
 
 	len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
-	if (len < 0 || len >= sizeof(buf))
+	if (len < 0 || len >= sizeof(buf)) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		return 0;
+	}
 
-	if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+	if ((count < sizeof(buf)) || copy_to_user(buff, buf, len)) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		return -EFAULT;
+	}
 
 	*ppos += len;	/* increase offset */
+	mutex_unlock(&sde_dbg_base.mutex);
 
 	return len;
 }
@@ -2877,11 +2983,16 @@
 static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
 		const char __user *user_buf, size_t count, loff_t *ppos)
 {
-	struct sde_dbg_reg_base *dbg = file->private_data;
+	struct sde_dbg_reg_base *dbg;
 	size_t off;
 	u32 data, cnt;
 	char buf[24];
+	int rc;
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
 	if (!dbg)
 		return -ENODEV;
 
@@ -2901,15 +3012,25 @@
 	if (off % sizeof(u32))
 		return -EFAULT;
 
-	if (off >= dbg->max_offset)
+	mutex_lock(&sde_dbg_base.mutex);
+	if (off >= dbg->max_offset) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		return -EFAULT;
+	}
 
-	_sde_dbg_enable_power(true);
+	rc = _sde_dbg_enable_power(true);
+	if (rc) {
+		mutex_unlock(&sde_dbg_base.mutex);
+		pr_err("failed to enable power %d\n", rc);
+		return rc;
+	}
 
 	writel_relaxed(data, dbg->base + off);
 
 	_sde_dbg_enable_power(false);
 
+	mutex_unlock(&sde_dbg_base.mutex);
+
 	pr_debug("addr=%zx data=%x\n", off, data);
 
 	return count;
@@ -2925,14 +3046,23 @@
 static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
 			char __user *user_buf, size_t count, loff_t *ppos)
 {
-	struct sde_dbg_reg_base *dbg = file->private_data;
+	struct sde_dbg_reg_base *dbg;
 	size_t len;
+	int rc;
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
 	if (!dbg) {
 		pr_err("invalid handle\n");
 		return -ENODEV;
 	}
 
+	if (!ppos)
+		return -EINVAL;
+
+	mutex_lock(&sde_dbg_base.mutex);
 	if (!dbg->buf) {
 		char dump_buf[64];
 		char *ptr;
@@ -2942,16 +3072,25 @@
 			DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
 		dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
 
-		if (!dbg->buf)
+		if (!dbg->buf) {
+			mutex_unlock(&sde_dbg_base.mutex);
 			return -ENOMEM;
+		}
 
-		if (dbg->off % sizeof(u32))
+		if (dbg->off % sizeof(u32)) {
+			mutex_unlock(&sde_dbg_base.mutex);
 			return -EFAULT;
+		}
 
 		ptr = dbg->base + dbg->off;
 		tot = 0;
 
-		_sde_dbg_enable_power(true);
+		rc = _sde_dbg_enable_power(true);
+		if (rc) {
+			mutex_unlock(&sde_dbg_base.mutex);
+			pr_err("failed to enable power %d\n", rc);
+			return rc;
+		}
 
 		for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
 			hex_dump_to_buffer(ptr, min(cnt, ROW_BYTES),
@@ -2974,16 +3113,20 @@
 		dbg->buf_len = tot;
 	}
 
-	if (*ppos >= dbg->buf_len)
+	if (*ppos >= dbg->buf_len) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		return 0; /* done reading */
+	}
 
 	len = min(count, dbg->buf_len - (size_t) *ppos);
 	if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+		mutex_unlock(&sde_dbg_base.mutex);
 		pr_err("failed to copy to user\n");
 		return -EFAULT;
 	}
 
 	*ppos += len; /* increase offset */
+	mutex_unlock(&sde_dbg_base.mutex);
 
 	return len;
 }
@@ -3067,9 +3210,7 @@
 	memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde));
 	memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
 
-	switch (hwversion) {
-	case SDE_HW_VER_300:
-	case SDE_HW_VER_301:
+	if (IS_MSM8998_TARGET(hwversion)) {
 		dbg->dbgbus_sde.entries = dbg_bus_sde_8998;
 		dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998);
 		dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
@@ -3077,9 +3218,7 @@
 		dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
 		dbg->dbgbus_vbif_rt.cmn.entries_size =
 				ARRAY_SIZE(vbif_dbg_bus_msm8998);
-		break;
-
-	case SDE_HW_VER_400:
+	} else if (IS_SDM845_TARGET(hwversion) || IS_SDM670_TARGET(hwversion)) {
 		dbg->dbgbus_sde.entries = dbg_bus_sde_sdm845;
 		dbg->dbgbus_sde.cmn.entries_size =
 				ARRAY_SIZE(dbg_bus_sde_sdm845);
@@ -3089,10 +3228,8 @@
 		dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
 		dbg->dbgbus_vbif_rt.cmn.entries_size =
 				ARRAY_SIZE(vbif_dbg_bus_msm8998);
-		break;
-	default:
-		pr_err("unsupported chipset id %u\n", hwversion);
-		break;
+	} else {
+		pr_err("unsupported chipset id %X\n", hwversion);
 	}
 }
 
@@ -3103,6 +3240,7 @@
 		return -EINVAL;
 	}
 
+	mutex_init(&sde_dbg_base.mutex);
 	INIT_LIST_HEAD(&sde_dbg_base.reg_base_list);
 	sde_dbg_base.dev = dev;
 	sde_dbg_base.power_ctrl = *power_ctrl;
@@ -3155,6 +3293,7 @@
 	sde_evtlog_destroy(sde_dbg_base.evtlog);
 	sde_dbg_base.evtlog = NULL;
 	sde_dbg_reg_base_destroy();
+	mutex_destroy(&sde_dbg_base.mutex);
 }
 
 int sde_dbg_reg_register_base(const char *name, void __iomem *base,
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index a266574..7b1b4c6 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.h
@@ -17,7 +17,8 @@
 #include <linux/debugfs.h>
 #include <linux/list.h>
 
-#define SDE_EVTLOG_DATA_LIMITER	(-1)
+/* select an uncommon hex value for the limiter */
+#define SDE_EVTLOG_DATA_LIMITER	(0xC0DEBEEF)
 #define SDE_EVTLOG_FUNC_ENTRY	0x1111
 #define SDE_EVTLOG_FUNC_EXIT	0x2222
 #define SDE_EVTLOG_FUNC_CASE1	0x3333
@@ -66,7 +67,7 @@
  * number must be greater than print entry to prevent out of bound evtlog
  * entry array access.
  */
-#define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_EVTLOG_ENTRY	(SDE_EVTLOG_PRINT_ENTRY * 8)
 #define SDE_EVTLOG_MAX_DATA 15
 #define SDE_EVTLOG_BUF_MAX 512
 #define SDE_EVTLOG_BUF_ALIGN 32
@@ -87,12 +88,14 @@
 };
 
 /**
+ * @last_dump: Index of last entry to be output during evtlog dumps
  * @filter_list: Linked list of currently active filter strings
  */
 struct sde_dbg_evtlog {
 	struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
 	u32 first;
 	u32 last;
+	u32 last_dump;
 	u32 curr;
 	u32 next;
 	u32 enable;
@@ -197,10 +200,12 @@
  * @evtlog:		pointer to evtlog
  * @evtlog_buf:		target buffer to print into
  * @evtlog_buf_size:	size of target buffer
+ * @update_last_entry:	whether or not to stop at most recent entry
  * Returns:		number of bytes written to buffer
  */
 ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
-		char *evtlog_buf, ssize_t evtlog_buf_size);
+		char *evtlog_buf, ssize_t evtlog_buf_size,
+		bool update_last_entry);
 
 /**
  * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset
@@ -315,6 +320,17 @@
 int sde_evtlog_get_filter(struct sde_dbg_evtlog *evtlog, int index,
 		char *buf, size_t bufsz);
 
+/**
+ * sde_rsc_debug_dump - sde rsc debug dump status
+ * @mux_sel:	select mux on rsc debug bus
+ */
+void sde_rsc_debug_dump(u32 mux_sel);
+
+/**
+ * dsi_ctrl_debug_dump - dump dsi debug dump status
+ */
+void dsi_ctrl_debug_dump(void);
+
 #else
 static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
 {
@@ -341,7 +357,8 @@
 }
 
 static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
-		char *evtlog_buf, ssize_t evtlog_buf_size)
+		char *evtlog_buf, ssize_t evtlog_buf_size,
+		bool update_last_entry)
 {
 	return 0;
 }
@@ -396,6 +413,14 @@
 	return -EINVAL;
 }
 
+static inline void sde_rsc_debug_dump(u32 mux_sel)
+{
+}
+
+static inline void dsi_ctrl_debug_dump(void)
+{
+}
+
 #endif /* defined(CONFIG_DEBUG_FS) */
 
 
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 67c664f..9a75179 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -105,27 +105,32 @@
 }
 
 /* always dump the last entries which are not dumped yet */
-static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
+static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog,
+		bool update_last_entry)
 {
 	if (!evtlog)
 		return false;
 
 	evtlog->first = evtlog->next;
 
-	if (evtlog->last == evtlog->first)
+	if (update_last_entry)
+		evtlog->last_dump = evtlog->last;
+
+	if (evtlog->last_dump == evtlog->first)
 		return false;
 
-	if (evtlog->last < evtlog->first) {
+	if (evtlog->last_dump < evtlog->first) {
 		evtlog->first %= SDE_EVTLOG_ENTRY;
-		if (evtlog->last < evtlog->first)
-			evtlog->last += SDE_EVTLOG_ENTRY;
+		if (evtlog->last_dump < evtlog->first)
+			evtlog->last_dump += SDE_EVTLOG_ENTRY;
 	}
 
-	if ((evtlog->last - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) {
+	if ((evtlog->last_dump - evtlog->first) > SDE_EVTLOG_PRINT_ENTRY) {
 		pr_info("evtlog skipping %d entries, last=%d\n",
-			evtlog->last - evtlog->first - SDE_EVTLOG_PRINT_ENTRY,
-			evtlog->last - 1);
-		evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
+			evtlog->last_dump - evtlog->first -
+			SDE_EVTLOG_PRINT_ENTRY,
+			evtlog->last_dump - 1);
+		evtlog->first = evtlog->last_dump - SDE_EVTLOG_PRINT_ENTRY;
 	}
 	evtlog->next = evtlog->first + 1;
 
@@ -133,7 +138,8 @@
 }
 
 ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
-		char *evtlog_buf, ssize_t evtlog_buf_size)
+		char *evtlog_buf, ssize_t evtlog_buf_size,
+		bool update_last_entry)
 {
 	int i;
 	ssize_t off = 0;
@@ -146,7 +152,7 @@
 	spin_lock_irqsave(&evtlog->spin_lock, flags);
 
 	/* update markers, exit if nothing to print */
-	if (!_sde_evtlog_dump_calc_range(evtlog))
+	if (!_sde_evtlog_dump_calc_range(evtlog, update_last_entry))
 		goto exit;
 
 	log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];
@@ -179,12 +185,16 @@
 void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
 {
 	char buf[SDE_EVTLOG_BUF_MAX];
+	bool update_last_entry = true;
 
 	if (!evtlog)
 		return;
 
-	while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf)))
+	while (sde_evtlog_dump_to_buffer(
+				evtlog, buf, sizeof(buf), update_last_entry)) {
 		pr_info("%s", buf);
+		update_last_entry = false;
+	}
 }
 
 struct sde_dbg_evtlog *sde_evtlog_init(void)
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.c b/drivers/gpu/drm/msm/sde_edid_parser.c
index 3d6c2ea..c2ba3b97 100644
--- a/drivers/gpu/drm/msm/sde_edid_parser.c
+++ b/drivers/gpu/drm/msm/sde_edid_parser.c
@@ -566,43 +566,28 @@
 	return rc;
 }
 
-u32 sde_get_sink_bpc(void *input)
+u8 sde_get_edid_checksum(void *input)
 {
 	struct sde_edid_ctrl *edid_ctrl = (struct sde_edid_ctrl *)(input);
-	struct edid *edid = edid_ctrl->edid;
+	struct edid *edid = NULL, *last_block = NULL;
+	u8 *raw_edid = NULL;
 
-	if (!edid) {
+	if (!edid_ctrl || !edid_ctrl->edid) {
 		SDE_ERROR("invalid edid input\n");
 		return 0;
 	}
 
-	if ((edid->revision < 3) || !(edid->input & DRM_EDID_INPUT_DIGITAL))
-		return 0;
+	edid = edid_ctrl->edid;
 
-	if (edid->revision < 4) {
-		if (edid->input & DRM_EDID_DIGITAL_TYPE_DVI)
-			return 8;
-		else
-			return 0;
-	}
+	raw_edid = (u8 *)edid;
+	raw_edid += (edid->extensions * EDID_LENGTH);
+	last_block = (struct edid *)raw_edid;
 
-	switch (edid->input & DRM_EDID_DIGITAL_DEPTH_MASK) {
-	case DRM_EDID_DIGITAL_DEPTH_6:
-		return 6;
-	case DRM_EDID_DIGITAL_DEPTH_8:
-		return 8;
-	case DRM_EDID_DIGITAL_DEPTH_10:
-		return 10;
-	case DRM_EDID_DIGITAL_DEPTH_12:
-		return 12;
-	case DRM_EDID_DIGITAL_DEPTH_14:
-		return 14;
-	case DRM_EDID_DIGITAL_DEPTH_16:
-		return 16;
-	case DRM_EDID_DIGITAL_DEPTH_UNDEF:
-	default:
-		return 0;
-	}
+	if (last_block)
+		return last_block->checksum;
+
+	SDE_ERROR("Invalid block, no checksum\n");
+	return 0;
 }
 
 bool sde_detect_hdmi_monitor(void *input)
diff --git a/drivers/gpu/drm/msm/sde_edid_parser.h b/drivers/gpu/drm/msm/sde_edid_parser.h
index b58b322..fd56116 100644
--- a/drivers/gpu/drm/msm/sde_edid_parser.h
+++ b/drivers/gpu/drm/msm/sde_edid_parser.h
@@ -138,12 +138,12 @@
 bool sde_detect_hdmi_monitor(void *edid_ctrl);
 
 /**
- * sde_get_sink_bpc() - return the bpc of sink device.
- * @edid_ctrl:     Handle to the edid_ctrl structure.
+ * sde_get_edid_checksum() - return the checksum of last block of EDID.
+ * @input:     Handle to the edid_ctrl structure.
  *
- * Return: bpc supported by the sink.
+ * Return: checksum of the last EDID block.
  */
-u32 sde_get_sink_bpc(void *edid_ctrl);
+u8 sde_get_edid_checksum(void *input);
 
 /**
  * _sde_edid_update_modes() - populate EDID modes.
diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
index 05d290b..6c44260 100644
--- a/drivers/gpu/drm/msm/sde_hdcp.h
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -42,6 +42,10 @@
 
 struct sde_hdcp_init_data {
 	struct dss_io_data *core_io;
+	struct dss_io_data *dp_ahb;
+	struct dss_io_data *dp_aux;
+	struct dss_io_data *dp_link;
+	struct dss_io_data *dp_p0;
 	struct dss_io_data *qfprom_io;
 	struct dss_io_data *hdcp_io;
 	struct drm_dp_aux *drm_aux;
diff --git a/drivers/gpu/drm/msm/sde_hdcp_1x.c b/drivers/gpu/drm/msm/sde_hdcp_1x.c
index 7951c23..c012f9d 100644
--- a/drivers/gpu/drm/msm/sde_hdcp_1x.c
+++ b/drivers/gpu/drm/msm/sde_hdcp_1x.c
@@ -256,12 +256,15 @@
 	u32 ksv_lsb_addr, ksv_msb_addr;
 	u32 aksv_lsb, aksv_msb;
 	u8 aksv[5];
-	struct dss_io_data *io;
+	struct dss_io_data *dp_ahb;
+	struct dss_io_data *dp_aux;
+	struct dss_io_data *dp_link;
 	struct dss_io_data *qfprom_io;
 	struct sde_hdcp_1x *hdcp = input;
 	struct sde_hdcp_reg_set *reg_set;
 
-	if (!hdcp || !hdcp->init_data.core_io ||
+	if (!hdcp || !hdcp->init_data.dp_ahb ||
+		!hdcp->init_data.dp_aux ||
 		!hdcp->init_data.qfprom_io) {
 		pr_err("invalid input\n");
 		rc = -EINVAL;
@@ -276,7 +279,9 @@
 		goto end;
 	}
 
-	io = hdcp->init_data.core_io;
+	dp_ahb = hdcp->init_data.dp_ahb;
+	dp_aux = hdcp->init_data.dp_aux;
+	dp_link = hdcp->init_data.dp_link;
 	qfprom_io = hdcp->init_data.qfprom_io;
 	reg_set = &hdcp->reg_set;
 
@@ -327,18 +332,18 @@
 		goto end;
 	}
 
-	DSS_REG_W(io, reg_set->aksv_lsb, aksv_lsb);
-	DSS_REG_W(io, reg_set->aksv_msb, aksv_msb);
+	DSS_REG_W(dp_aux, reg_set->aksv_lsb, aksv_lsb);
+	DSS_REG_W(dp_aux, reg_set->aksv_msb, aksv_msb);
 
 	/* Setup seed values for random number An */
-	DSS_REG_W(io, reg_set->entropy_ctrl0, 0xB1FFB0FF);
-	DSS_REG_W(io, reg_set->entropy_ctrl1, 0xF00DFACE);
+	DSS_REG_W(dp_link, reg_set->entropy_ctrl0, 0xB1FFB0FF);
+	DSS_REG_W(dp_link, reg_set->entropy_ctrl1, 0xF00DFACE);
 
 	/* make sure hw is programmed */
 	wmb();
 
 	/* enable hdcp engine */
-	DSS_REG_W(io, reg_set->ctrl, 0x1);
+	DSS_REG_W(dp_ahb, reg_set->ctrl, 0x1);
 
 	hdcp->hdcp_state = HDCP_STATE_AUTHENTICATING;
 end:
@@ -363,12 +368,15 @@
 			if (bytes_read != read_size) {
 				pr_err("fail: offset(0x%x), size(0x%x), rc(0x%x)\n",
 					offset, read_size, bytes_read);
+				rc = -EIO;
 				break;
 			}
 
 			buf += read_size;
-			offset += read_size;
 			size -= read_size;
+
+			if (!realign)
+				offset += read_size;
 		} while (size > 0);
 	}
 
@@ -393,6 +401,7 @@
 			if (bytes_written != write_size) {
 				pr_err("fail: offset(0x%x), size(0x%x), rc(0x%x)\n",
 					offset, write_size, bytes_written);
+				rc = -EIO;
 				break;
 			}
 
@@ -411,7 +420,7 @@
 	struct dss_io_data *io;
 	struct sde_hdcp_int_set *isr;
 
-	io = hdcp->init_data.core_io;
+	io = hdcp->init_data.dp_ahb;
 	isr = &hdcp->int_set;
 
 	intr_reg = DSS_REG_R(io, isr->int_reg);
@@ -458,7 +467,8 @@
 	int rc;
 	u32 link0_status;
 	struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
-	struct dss_io_data *io = hdcp->init_data.core_io;
+	struct dss_io_data *dp_ahb = hdcp->init_data.dp_ahb;
+	struct dss_io_data *dp_aux = hdcp->init_data.dp_aux;
 
 	if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
 		pr_err("invalid state\n");
@@ -466,7 +476,7 @@
 	}
 
 	/* Wait for HDCP keys to be checked and validated */
-	rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+	rc = readl_poll_timeout(dp_ahb->base + reg_set->status, link0_status,
 				((link0_status >> reg_set->keys_offset) & 0x7)
 					== HDCP_KEYS_STATE_VALID ||
 				!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
@@ -480,10 +490,10 @@
 	 * 1.1_Features turned off by default.
 	 * No need to write AInfo since 1.1_Features is disabled.
 	 */
-	DSS_REG_W(io, reg_set->data4, 0);
+	DSS_REG_W(dp_aux, reg_set->data4, 0);
 
 	/* Wait for An0 and An1 bit to be ready */
-	rc = readl_poll_timeout(io->base + reg_set->status, link0_status,
+	rc = readl_poll_timeout(dp_ahb->base + reg_set->status, link0_status,
 				(link0_status & (BIT(8) | BIT(9))) ||
 				!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING),
 				HDCP_POLL_SLEEP_US, HDCP_POLL_TIMEOUT_US);
@@ -550,7 +560,8 @@
 
 static int sde_hdcp_1x_read_an_aksv_from_hw(struct sde_hdcp_1x *hdcp)
 {
-	struct dss_io_data *io = hdcp->init_data.core_io;
+	struct dss_io_data *dp_ahb = hdcp->init_data.dp_ahb;
+	struct dss_io_data *dp_aux = hdcp->init_data.dp_aux;
 	struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
 
 	if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
@@ -558,21 +569,21 @@
 		return -EINVAL;
 	}
 
-	hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+	hdcp->an_0 = DSS_REG_R(dp_ahb, reg_set->data5);
 	if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
 		udelay(1);
-		hdcp->an_0 = DSS_REG_R(io, reg_set->data5);
+		hdcp->an_0 = DSS_REG_R(dp_ahb, reg_set->data5);
 	}
 
-	hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+	hdcp->an_1 = DSS_REG_R(dp_ahb, reg_set->data6);
 	if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
 		udelay(1);
-		hdcp->an_1 = DSS_REG_R(io, reg_set->data6);
+		hdcp->an_1 = DSS_REG_R(dp_ahb, reg_set->data6);
 	}
 
 	/* Read AKSV */
-	hdcp->aksv_0 = DSS_REG_R(io, reg_set->data3);
-	hdcp->aksv_1 = DSS_REG_R(io, reg_set->data4);
+	hdcp->aksv_0 = DSS_REG_R(dp_aux, reg_set->data3);
+	hdcp->aksv_1 = DSS_REG_R(dp_aux, reg_set->data4);
 
 	return 0;
 }
@@ -645,7 +656,7 @@
 	u32 const r0_read_delay_us = 1;
 	u32 const r0_read_timeout_us = r0_read_delay_us * 10;
 	struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
-	struct dss_io_data *io = hdcp->init_data.core_io;
+	struct dss_io_data *io = hdcp->init_data.dp_ahb;
 
 	if (!sde_hdcp_1x_state(HDCP_STATE_AUTHENTICATING)) {
 		pr_err("invalid state\n");
@@ -906,7 +917,7 @@
 	int i, rc = 0;
 	u8 *ksv_fifo = hdcp->current_tp.ksv_list;
 	u32 ksv_bytes = hdcp->sink_addr.ksv_fifo.len;
-	struct dss_io_data *io = hdcp->init_data.core_io;
+	struct dss_io_data *io = hdcp->init_data.dp_ahb;
 	struct dss_io_data *sec_io = hdcp->init_data.hdcp_io;
 	struct sde_hdcp_reg_set *reg_set = &hdcp->reg_set;
 	u32 sha_status = 0, status;
@@ -1083,7 +1094,8 @@
 
 static void sde_hdcp_1x_cache_topology(struct sde_hdcp_1x *hdcp)
 {
-	if (!hdcp || !hdcp->init_data.core_io) {
+	if (!hdcp || !hdcp->init_data.dp_ahb || !hdcp->init_data.dp_aux ||
+		!hdcp->init_data.dp_link || !hdcp->init_data.dp_p0) {
 		pr_err("invalid input\n");
 		return;
 	}
@@ -1142,6 +1154,7 @@
 		DSS_REG_W_ND(io, REG_HDMI_DDC_ARBITRATION, DSS_REG_R(io,
 				REG_HDMI_DDC_ARBITRATION) & ~(BIT(4)));
 	else if (hdcp->init_data.client_id == HDCP_CLIENT_DP) {
+		io = hdcp->init_data.dp_aux;
 		DSS_REG_W(io, DP_DP_HPD_REFTIMER, 0x10013);
 	}
 
@@ -1220,12 +1233,12 @@
 	struct sde_hdcp_int_set *isr;
 	u32 ret = 0, reg;
 
-	if (!hdcp || !hdcp->init_data.core_io) {
+	if (!hdcp || !hdcp->init_data.dp_ahb) {
 		pr_err("invalid input\n");
 		return -EINVAL;
 	}
 
-	io = hdcp->init_data.core_io;
+	io = hdcp->init_data.dp_ahb;
 	reg_set = &hdcp->reg_set;
 	isr = &hdcp->int_set;
 
@@ -1260,12 +1273,12 @@
 	int rc = 0;
 	u32 reg;
 
-	if (!hdcp || !hdcp->init_data.core_io) {
+	if (!hdcp || !hdcp->init_data.dp_ahb) {
 		pr_err("invalid input\n");
 		return;
 	}
 
-	io = hdcp->init_data.core_io;
+	io = hdcp->init_data.dp_ahb;
 	reg_set = &hdcp->reg_set;
 	isr = &hdcp->int_set;
 
@@ -1323,13 +1336,13 @@
 	struct sde_hdcp_reg_set *reg_set;
 	struct sde_hdcp_int_set *isr;
 
-	if (!hdcp || !hdcp->init_data.core_io) {
+	if (!hdcp || !hdcp->init_data.dp_ahb) {
 		pr_err("invalid input\n");
 		rc = -EINVAL;
 		goto error;
 	}
 
-	io = hdcp->init_data.core_io;
+	io = hdcp->init_data.dp_ahb;
 	reg_set = &hdcp->reg_set;
 	isr = &hdcp->int_set;
 
@@ -1527,8 +1540,7 @@
 		.off = sde_hdcp_1x_off
 	};
 
-	if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
-		!init_data->mutex || !init_data->notify_status ||
+	if (!init_data || !init_data->mutex || !init_data->notify_status ||
 		!init_data->workq || !init_data->cb_data) {
 		pr_err("invalid input\n");
 		goto error;
diff --git a/drivers/gpu/drm/msm/sde_power_handle.c b/drivers/gpu/drm/msm/sde_power_handle.c
index 7a0da3d..34a826d 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.c
+++ b/drivers/gpu/drm/msm/sde_power_handle.c
@@ -360,7 +360,7 @@
 		ab_quota_nrt = max_t(u64, ab_quota_nrt,
 				SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA);
 		ib_quota_nrt = max_t(u64, ib_quota_nrt,
-				SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA);
+				SDE_POWER_HANDLE_ENABLE_NRT_BUS_IB_QUOTA);
 	} else {
 		ab_quota_rt = min_t(u64, ab_quota_rt,
 				SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA);
@@ -379,30 +379,30 @@
 		struct msm_bus_vectors *vect = NULL;
 		struct msm_bus_scale_pdata *bw_table =
 			pdbus->data_bus_scale_table;
-		u32 nrt_axi_port_cnt = pdbus->nrt_axi_port_cnt;
-		u32 total_axi_port_cnt = pdbus->axi_port_cnt;
-		u32 rt_axi_port_cnt = total_axi_port_cnt - nrt_axi_port_cnt;
+		u32 nrt_data_paths_cnt = pdbus->nrt_data_paths_cnt;
+		u32 total_data_paths_cnt = pdbus->data_paths_cnt;
+		u32 rt_data_paths_cnt = total_data_paths_cnt -
+			nrt_data_paths_cnt;
 
-		if (!bw_table || !total_axi_port_cnt ||
-		    total_axi_port_cnt > MAX_AXI_PORT_COUNT) {
+		if (!bw_table || !total_data_paths_cnt ||
+		    total_data_paths_cnt > MAX_AXI_PORT_COUNT) {
 			pr_err("invalid input\n");
 			return -EINVAL;
 		}
 
-		if (pdbus->bus_channels) {
+		if (nrt_data_paths_cnt) {
+
+			ab_quota_rt = div_u64(ab_quota_rt, rt_data_paths_cnt);
+			ab_quota_nrt = div_u64(ab_quota_nrt,
+						nrt_data_paths_cnt);
+
 			ib_quota_rt = div_u64(ib_quota_rt,
-						pdbus->bus_channels);
+						rt_data_paths_cnt);
 			ib_quota_nrt = div_u64(ib_quota_nrt,
-						pdbus->bus_channels);
-		}
+						nrt_data_paths_cnt);
 
-		if (nrt_axi_port_cnt) {
-
-			ab_quota_rt = div_u64(ab_quota_rt, rt_axi_port_cnt);
-			ab_quota_nrt = div_u64(ab_quota_nrt, nrt_axi_port_cnt);
-
-			for (i = 0; i < total_axi_port_cnt; i++) {
-				if (i < rt_axi_port_cnt) {
+			for (i = 0; i < total_data_paths_cnt; i++) {
+				if (i < rt_data_paths_cnt) {
 					ab_quota[i] = ab_quota_rt;
 					ib_quota[i] = ib_quota_rt;
 				} else {
@@ -412,10 +412,11 @@
 			}
 		} else {
 			ab_quota[0] = div_u64(ab_quota_rt + ab_quota_nrt,
-					total_axi_port_cnt);
-			ib_quota[0] = ib_quota_rt + ib_quota_nrt;
+					total_data_paths_cnt);
+			ib_quota[0] = div_u64(ib_quota_rt + ib_quota_nrt,
+					total_data_paths_cnt);
 
-			for (i = 1; i < total_axi_port_cnt; i++) {
+			for (i = 1; i < total_data_paths_cnt; i++) {
 				ab_quota[i] = ab_quota[0];
 				ib_quota[i] = ib_quota[0];
 			}
@@ -424,7 +425,7 @@
 		new_uc_idx = (pdbus->curr_bw_uc_idx %
 			(bw_table->num_usecases - 1)) + 1;
 
-		for (i = 0; i < total_axi_port_cnt; i++) {
+		for (i = 0; i < total_data_paths_cnt; i++) {
 			vect = &bw_table->usecase[new_uc_idx].vectors[i];
 			vect->ab = ab_quota[i];
 			vect->ib = ib_quota[i];
@@ -432,8 +433,8 @@
 			pr_debug(
 				"%s uc_idx=%d %s path idx=%d ab=%llu ib=%llu\n",
 				bw_table->name,
-				new_uc_idx, (i < rt_axi_port_cnt) ? "rt" : "nrt"
-				, i, vect->ab, vect->ib);
+				new_uc_idx, (i < rt_data_paths_cnt) ?
+				"rt" : "nrt", i, vect->ab, vect->ib);
 		}
 	}
 	pdbus->curr_bw_uc_idx = new_uc_idx;
@@ -518,10 +519,10 @@
 		rc = 0;
 	}
 
-	pdbus->nrt_axi_port_cnt = 0;
+	pdbus->nrt_data_paths_cnt = 0;
 	rc = of_property_read_u32(pdev->dev.of_node,
 			"qcom,sde-num-nrt-paths",
-			&pdbus->nrt_axi_port_cnt);
+			&pdbus->nrt_data_paths_cnt);
 	if (rc) {
 		pr_debug("number of axi port property not specified\n");
 		rc = 0;
@@ -535,7 +536,7 @@
 			pr_err("Error. qcom,msm-bus,num-paths not found\n");
 			return rc;
 		}
-		pdbus->axi_port_cnt = paths;
+		pdbus->data_paths_cnt = paths;
 
 		pdbus->data_bus_scale_table =
 				msm_bus_pdata_from_node(pdev, node);
@@ -982,6 +983,16 @@
 	return rc;
 }
 
+int sde_power_resource_is_enabled(struct sde_power_handle *phandle)
+{
+	if (!phandle) {
+		pr_err("invalid input argument\n");
+		return false;
+	}
+
+	return phandle->current_usecase_ndx != VOTE_INDEX_DISABLE;
+}
+
 int sde_power_clk_set_rate(struct sde_power_handle *phandle, char *clock_name,
 	u64 rate)
 {
diff --git a/drivers/gpu/drm/msm/sde_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 18777fd..72975e7 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -18,7 +18,8 @@
 
 #define SDE_POWER_HANDLE_ENABLE_BUS_AB_QUOTA	0
 #define SDE_POWER_HANDLE_DISABLE_BUS_AB_QUOTA	0
-#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA	1600000000
+#define SDE_POWER_HANDLE_ENABLE_BUS_IB_QUOTA		400000000
+#define SDE_POWER_HANDLE_ENABLE_NRT_BUS_IB_QUOTA	0
 #define SDE_POWER_HANDLE_DISABLE_BUS_IB_QUOTA	0
 
 #include <linux/sde_io_util.h>
@@ -101,8 +102,8 @@
  * struct sde_power_data_handle: power handle struct for data bus
  * @data_bus_scale_table: pointer to bus scaling table
  * @data_bus_hdl: current data bus handle
- * @axi_port_cnt: number of rt axi ports
- * @nrt_axi_port_cnt: number of nrt axi ports
+ * @data_paths_cnt: number of rt data path ports
+ * @nrt_data_paths_cnt: number of nrt data path ports
  * @bus_channels: number of memory bus channels
  * @curr_bw_uc_idx: current use case index of data bus
  * @ao_bw_uc_idx: active only use case index of data bus
@@ -115,8 +116,8 @@
 struct sde_power_data_bus_handle {
 	struct msm_bus_scale_pdata *data_bus_scale_table;
 	u32 data_bus_hdl;
-	u32 axi_port_cnt;
-	u32 nrt_axi_port_cnt;
+	u32 data_paths_cnt;
+	u32 nrt_data_paths_cnt;
 	u32 bus_channels;
 	u32 curr_bw_uc_idx;
 	u32 ao_bw_uc_idx;
@@ -224,6 +225,14 @@
 	struct sde_power_client *pclient, bool enable);
 
 /**
+ * sde_power_resource_is_enabled() - return true if power resource is enabled
+ * @pdata:  power handle containing the resources
+ *
+ * Return: true if enabled; false otherwise
+ */
+int sde_power_resource_is_enabled(struct sde_power_handle *pdata);
+
+/**
  * sde_power_data_bus_state_update() - update data bus state
  * @pdata:  power handle containing the resources
  * @enable: take enable vs disable path
diff --git a/drivers/gpu/drm/msm/sde_rsc.c b/drivers/gpu/drm/msm/sde_rsc.c
index b654b26..82b1199 100644
--- a/drivers/gpu/drm/msm/sde_rsc.c
+++ b/drivers/gpu/drm/msm/sde_rsc.c
@@ -456,7 +456,7 @@
 	} else if (rsc->hw_ops.state_update) {
 		rc = rsc->hw_ops.state_update(rsc, SDE_RSC_IDLE_STATE);
 		if (!rc)
-			rpmh_mode_solver_set(rsc->disp_rsc, false);
+			rpmh_mode_solver_set(rsc->disp_rsc, true);
 	}
 
 	return rc;
@@ -837,6 +837,21 @@
 }
 EXPORT_SYMBOL(sde_rsc_client_vote);
 
+#if defined(CONFIG_DEBUG_FS)
+void sde_rsc_debug_dump(u32 mux_sel)
+{
+	struct sde_rsc_priv *rsc;
+
+	rsc = rsc_prv_list[SDE_RSC_INDEX];
+	if (!rsc)
+		return;
+
+	/* this must be called with rsc clocks enabled */
+	if (rsc->hw_ops.debug_dump)
+		rsc->hw_ops.debug_dump(rsc, mux_sel);
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
 static int _sde_debugfs_status_show(struct seq_file *s, void *data)
 {
 	struct sde_rsc_priv *rsc;
diff --git a/drivers/gpu/drm/msm/sde_rsc_hw.c b/drivers/gpu/drm/msm/sde_rsc_hw.c
index b474d21..a0d1245 100644
--- a/drivers/gpu/drm/msm/sde_rsc_hw.c
+++ b/drivers/gpu/drm/msm/sde_rsc_hw.c
@@ -187,32 +187,32 @@
 						0x39e038a8, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x10,
 						0x888babec, rsc->debug_mode);
-	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x14,
-						0xa806a020, rsc->debug_mode);
 
 	/* Mode - 2 sequence */
+	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x14,
+						0xaaa8a020, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x18,
-						0xa138ebaa, rsc->debug_mode);
+						0xe1a138eb, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x1c,
-						0xaca581e1, rsc->debug_mode);
+						0xe0aca581, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x20,
-						0xe2a2ede0, rsc->debug_mode);
+						0x82e2a2ed, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x24,
-						0xea8a3982, rsc->debug_mode);
+						0x8cea8a39, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x28,
-						0xa920888c, rsc->debug_mode);
+						0xe9a92088, rsc->debug_mode);
 
-	/* tcs sleep sequence */
+	/* tcs sleep & wake sequence */
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x2c,
-						0x89e6a6e9, rsc->debug_mode);
+						0x89e686a6, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x30,
 						0xa7e9a920, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_MEM_0_DRV0 + 0x34,
-						0x002089e7, rsc->debug_mode);
+						0x2089e787, rsc->debug_mode);
 
 	/* branch address */
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_0_DRV0,
-						0x2b, rsc->debug_mode);
+						0x2a, rsc->debug_mode);
 	dss_reg_w(&rsc->drv_io, SDE_RSCC_SEQ_CFG_BR_ADDR_1_DRV0,
 						0x31, rsc->debug_mode);
 
@@ -515,7 +515,7 @@
 		reg = dss_reg_r(&rsc->wrapper_io,
 			SDE_RSCC_WRAPPER_OVERRIDE_CTRL, rsc->debug_mode);
 		reg |= (BIT(0) | BIT(8));
-		reg &= ~(BIT(1) | BIT(2) | BIT(3) | BIT(6) | BIT(7));
+		reg &= ~(BIT(1) | BIT(2) | BIT(3) | BIT(6) | BIT(7) | BIT(9));
 		dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_OVERRIDE_CTRL,
 							reg, rsc->debug_mode);
 		/* make sure that solver is enabled */
@@ -746,6 +746,12 @@
 	return blen;
 }
 
+static void rsc_hw_debug_dump(struct sde_rsc_priv *rsc, u32 mux_sel)
+{
+	dss_reg_w(&rsc->wrapper_io, SDE_RSCC_WRAPPER_DEBUG_BUS,
+		((mux_sel & 0xf) << 1) | BIT(0), rsc->debug_mode);
+}
+
 bool rsc_hw_is_amc_mode(struct sde_rsc_priv *rsc)
 {
 	return dss_reg_r(&rsc->drv_io, SDE_RSCC_TCS_DRV0_CONTROL,
@@ -806,6 +812,7 @@
 	rsc->hw_ops.state_update = sde_rsc_state_update;
 	rsc->hw_ops.debug_show = sde_rsc_debug_show;
 	rsc->hw_ops.mode_ctrl = rsc_hw_mode_ctrl;
+	rsc->hw_ops.debug_dump = rsc_hw_debug_dump;
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/msm/sde_rsc_priv.h b/drivers/gpu/drm/msm/sde_rsc_priv.h
index c96ce75..64b0216 100644
--- a/drivers/gpu/drm/msm/sde_rsc_priv.h
+++ b/drivers/gpu/drm/msm/sde_rsc_priv.h
@@ -73,6 +73,7 @@
  * @hw_vsync:			Enables the vsync on RSC block.
  * @tcs_use_ok:			set TCS set to high to allow RSC to use it.
  * @is_amc_mode:		Check current amc mode status
+ * @debug_dump:			dump debug bus registers or enable debug bus
  * @state_update:		Enable/override the solver based on rsc state
  *                              status (command/video)
  * @mode_show:			shows current mode status, mode0/1/2
@@ -87,6 +88,7 @@
 		char *buffer, int buffer_size, u32 mode);
 	int (*tcs_use_ok)(struct sde_rsc_priv *rsc);
 	bool (*is_amc_mode)(struct sde_rsc_priv *rsc);
+	void (*debug_dump)(struct sde_rsc_priv *rsc, u32 mux_sel);
 	int (*state_update)(struct sde_rsc_priv *rsc, enum sde_rsc_state state);
 	int (*debug_show)(struct seq_file *s, struct sde_rsc_priv *rsc);
 	int (*mode_ctrl)(struct sde_rsc_priv *rsc, enum rsc_mode_req request,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c
index 8e2e24a..44e116f 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/bsp/g84.c
@@ -39,5 +39,5 @@
 g84_bsp_new(struct nvkm_device *device, int index, struct nvkm_engine **pengine)
 {
 	return nvkm_xtensa_new_(&g84_bsp, device, index,
-				true, 0x103000, pengine);
+				device->chipset != 0x92, 0x103000, pengine);
 }
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
index 6584d50..133f896 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c
@@ -1129,7 +1129,7 @@
 	if (trap & 0x00000008) {
 		u32 stat = nvkm_rd32(device, 0x408030);
 
-		nvkm_snprintbf(error, sizeof(error), gf100_m2mf_error,
+		nvkm_snprintbf(error, sizeof(error), gf100_ccache_error,
 			       stat & 0x3fffffff);
 		nvkm_error(subdev, "CCACHE %08x [%s]\n", stat, error);
 		nvkm_wr32(device, 0x408030, 0xc0000000);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
index 5df9669..240872a 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/base.c
@@ -240,6 +240,8 @@
 			mmu->func->map_pgt(vpgd->obj, pde, vpgt->mem);
 		}
 
+		mmu->func->flush(vm);
+
 		nvkm_memory_del(&pgt);
 	}
 }
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
index eb9b278..a4cb824 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/pci/base.c
@@ -192,6 +192,10 @@
 		}
 	}
 
+#ifdef __BIG_ENDIAN
+	pci->msi = false;
+#endif
+
 	pci->msi = nvkm_boolopt(device->cfgopt, "NvMSI", pci->msi);
 	if (pci->msi && func->msi_rearm) {
 		pci->msi = pci_enable_msi(pci->pdev) == 0;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 3b21ca5..82b0112 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1674,7 +1674,7 @@
 	radeon_agp_suspend(rdev);
 
 	pci_save_state(dev->pdev);
-	if (freeze && rdev->family >= CHIP_CEDAR) {
+	if (freeze && rdev->family >= CHIP_CEDAR && !(rdev->flags & RADEON_IS_IGP)) {
 		rdev->asic->asic_reset(rdev, true);
 		pci_restore_state(dev->pdev);
 	} else if (suspend) {
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 7316fc7..a2ec6d8 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -149,8 +149,8 @@
 	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? OTAR2 : OTAR, 0);
 
 	/* Signal polarities */
-	value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? 0 : DSMR_VSL)
-	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? 0 : DSMR_HSL)
+	value = ((mode->flags & DRM_MODE_FLAG_PVSYNC) ? DSMR_VSL : 0)
+	      | ((mode->flags & DRM_MODE_FLAG_PHSYNC) ? DSMR_HSL : 0)
 	      | DSMR_DIPM_DISP | DSMR_CSPM;
 	rcar_du_crtc_write(rcrtc, DSMR, value);
 
@@ -172,7 +172,7 @@
 					mode->crtc_vsync_start - 1);
 	rcar_du_crtc_write(rcrtc, VCR,  mode->crtc_vtotal - 1);
 
-	rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start);
+	rcar_du_crtc_write(rcrtc, DESR,  mode->htotal - mode->hsync_start - 1);
 	rcar_du_crtc_write(rcrtc, DEWR,  mode->hdisplay);
 }
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
index cfc302c..c58602b 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c
@@ -453,13 +453,13 @@
 	}
 
 	ret = rcar_du_encoder_init(rcdu, enc_type, output, encoder, connector);
-	of_node_put(encoder);
-	of_node_put(connector);
-
 	if (ret && ret != -EPROBE_DEFER)
 		dev_warn(rcdu->dev,
-			 "failed to initialize encoder %s (%d), skipping\n",
-			 encoder->full_name, ret);
+			 "failed to initialize encoder %s on output %u (%d), skipping\n",
+			 of_node_full_name(encoder), output, ret);
+
+	of_node_put(encoder);
+	of_node_put(connector);
 
 	return ret;
 }
diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c
index c3b2186..1feec34 100644
--- a/drivers/gpu/drm/sun4i/sun4i_drv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_drv.c
@@ -47,6 +47,13 @@
 	sun4i_tcon_enable_vblank(tcon, false);
 }
 
+static void sun4i_drv_lastclose(struct drm_device *dev)
+{
+	struct sun4i_drv *drv = dev->dev_private;
+
+	drm_fbdev_cma_restore_mode(drv->fbdev);
+}
+
 static const struct file_operations sun4i_drv_fops = {
 	.owner		= THIS_MODULE,
 	.open		= drm_open,
@@ -65,6 +72,7 @@
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
 
 	/* Generic Operations */
+	.lastclose		= sun4i_drv_lastclose,
 	.fops			= &sun4i_drv_fops,
 	.name			= "sun4i-drm",
 	.desc			= "Allwinner sun4i Display Engine",
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index a37de5d..ddd6badd 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -612,7 +612,7 @@
 		} else {
 			pr_err("Failed to fill pool (%p)\n", pool);
 			/* If we have any pages left put them to the pool. */
-			list_for_each_entry(p, &pool->list, lru) {
+			list_for_each_entry(p, &new_pages, lru) {
 				++cpages;
 			}
 			list_splice(&new_pages, &pool->list);
diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c
index 2242a80..dc2976c 100644
--- a/drivers/gpu/drm/virtio/virtgpu_fb.c
+++ b/drivers/gpu/drm/virtio/virtgpu_fb.c
@@ -337,7 +337,7 @@
 	info->fbops = &virtio_gpufb_ops;
 	info->pixmap.flags = FB_PIXMAP_SYSTEM;
 
-	info->screen_base = obj->vmap;
+	info->screen_buffer = obj->vmap;
 	info->screen_size = obj->gem_base.size;
 	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
 	drm_fb_helper_fill_var(info, &vfbdev->helper,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 36005bd..29abd28 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -721,7 +721,7 @@
 		 * allocation taken by fbdev
 		 */
 		if (!(dev_priv->capabilities & SVGA_CAP_3D))
-			mem_size *= 2;
+			mem_size *= 3;
 
 		dev_priv->max_mob_pages = mem_size * 1024 / PAGE_SIZE;
 		dev_priv->prim_bb_mem =
diff --git a/drivers/gpu/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index e112fd1..ee696e2 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -68,6 +68,7 @@
 #define A6XX_CP_MEM_POOL_SIZE            0x8C3
 #define A6XX_CP_CHICKEN_DBG              0x841
 #define A6XX_CP_ADDR_MODE_CNTL           0x842
+#define A6XX_CP_DBG_ECO_CNTL             0x843
 #define A6XX_CP_PROTECT_CNTL             0x84F
 #define A6XX_CP_PROTECT_REG              0x850
 #define A6XX_CP_CONTEXT_SWITCH_CNTL      0x8A0
@@ -676,6 +677,7 @@
 #define A6XX_UCHE_PERFCTR_UCHE_SEL_9        0xE25
 #define A6XX_UCHE_PERFCTR_UCHE_SEL_10       0xE26
 #define A6XX_UCHE_PERFCTR_UCHE_SEL_11       0xE27
+#define A6XX_UCHE_GBIF_GX_CONFIG            0xE3A
 
 /* SP registers */
 #define A6XX_SP_ADDR_MODE_CNTL              0xAE01
@@ -764,6 +766,35 @@
 #define A6XX_VBIF_PERF_PWR_CNT_HIGH1            0x3119
 #define A6XX_VBIF_PERF_PWR_CNT_HIGH2            0x311a
 
+/* GBIF countables */
+#define GBIF_AXI0_READ_DATA_TOTAL_BEATS    34
+#define GBIF_AXI1_READ_DATA_TOTAL_BEATS    35
+#define GBIF_AXI0_WRITE_DATA_TOTAL_BEATS   46
+#define GBIF_AXI1_WRITE_DATA_TOTAL_BEATS   47
+
+/* GBIF registers */
+#define A6XX_GBIF_HALT                    0x3c45
+#define A6XX_GBIF_HALT_ACK                0x3c46
+#define A6XX_GBIF_HALT_MASK               0x1
+
+#define A6XX_GBIF_PERF_PWR_CNT_EN         0x3cc0
+#define A6XX_GBIF_PERF_CNT_SEL            0x3cc2
+#define A6XX_GBIF_PERF_CNT_LOW0           0x3cc4
+#define A6XX_GBIF_PERF_CNT_LOW1           0x3cc5
+#define A6XX_GBIF_PERF_CNT_LOW2           0x3cc6
+#define A6XX_GBIF_PERF_CNT_LOW3           0x3cc7
+#define A6XX_GBIF_PERF_CNT_HIGH0          0x3cc8
+#define A6XX_GBIF_PERF_CNT_HIGH1          0x3cc9
+#define A6XX_GBIF_PERF_CNT_HIGH2          0x3cca
+#define A6XX_GBIF_PERF_CNT_HIGH3          0x3ccb
+#define A6XX_GBIF_PWR_CNT_LOW0            0x3ccc
+#define A6XX_GBIF_PWR_CNT_LOW1            0x3ccd
+#define A6XX_GBIF_PWR_CNT_LOW2            0x3cce
+#define A6XX_GBIF_PWR_CNT_HIGH0           0x3ccf
+#define A6XX_GBIF_PWR_CNT_HIGH1           0x3cd0
+#define A6XX_GBIF_PWR_CNT_HIGH2           0x3cd1
+
+
 /* CX_DBGC_CFG registers */
 #define A6XX_CX_DBGC_CFG_DBGBUS_SEL_A                   0x18400
 #define A6XX_CX_DBGC_CFG_DBGBUS_SEL_B                   0x18401
@@ -935,6 +966,7 @@
 #define A6XX_GMU_AO_SPARE_CNTL			0x23B16
 
 /* GMU RSC control registers */
+#define A6XX_GPU_RSCC_RSC_STATUS0_DRV0		0x23404
 #define A6XX_GMU_RSCC_CONTROL_REQ		0x23B07
 #define A6XX_GMU_RSCC_CONTROL_ACK		0x23B08
 
@@ -980,6 +1012,18 @@
 #define PDC_GPU_TCS1_CMD0_MSGID			0x21575
 #define PDC_GPU_TCS1_CMD0_ADDR			0x21576
 #define PDC_GPU_TCS1_CMD0_DATA			0x21577
+#define PDC_GPU_TCS2_CONTROL			0x215A4
+#define PDC_GPU_TCS2_CMD_ENABLE_BANK		0x215A5
+#define PDC_GPU_TCS2_CMD_WAIT_FOR_CMPL_BANK	0x215A6
+#define PDC_GPU_TCS2_CMD0_MSGID			0x215A7
+#define PDC_GPU_TCS2_CMD0_ADDR			0x215A8
+#define PDC_GPU_TCS2_CMD0_DATA			0x215A9
+#define PDC_GPU_TCS3_CONTROL			0x215D6
+#define PDC_GPU_TCS3_CMD_ENABLE_BANK		0x215D7
+#define PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK	0x215D8
+#define PDC_GPU_TCS3_CMD0_MSGID			0x215D9
+#define PDC_GPU_TCS3_CMD0_ADDR			0x215DA
+#define PDC_GPU_TCS3_CMD0_DATA			0x215DB
 #define PDC_GPU_SEQ_MEM_0			0xA0000
 
 #endif /* _A6XX_REG_H */
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index b451750..9a9e1fc 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -325,7 +325,7 @@
 		.core = 6,
 		.major = 3,
 		.minor = 0,
-		.patchid = ANY_ID,
+		.patchid = 0,
 		.features = ADRENO_64BIT | ADRENO_RPMH |
 			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
 		.sqefw_name = "a630_sqe.fw",
@@ -341,6 +341,26 @@
 		.max_power = 5448,
 	},
 	{
+		.gpurev = ADRENO_REV_A630,
+		.core = 6,
+		.major = 3,
+		.minor = 0,
+		.patchid = ANY_ID,
+		.features = ADRENO_64BIT | ADRENO_RPMH | ADRENO_IFPC |
+			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
+		.sqefw_name = "a630_sqe.fw",
+		.zap_name = "a630_zap",
+		.gpudev = &adreno_a6xx_gpudev,
+		.gmem_size = SZ_1M,
+		.num_protected_regs = 0x20,
+		.busy_mask = 0xFFFFFFFE,
+		.gpmufw_name = "a630_gmu.bin",
+		.gpmu_major = 0x1,
+		.gpmu_minor = 0x001,
+		.gpmu_tsens = 0x000C000D,
+		.max_power = 5448,
+	},
+	{
 		.gpurev = ADRENO_REV_A615,
 		.core = 6,
 		.major = 1,
@@ -355,7 +375,7 @@
 		.num_protected_regs = 0x20,
 		.busy_mask = 0xFFFFFFFE,
 		.gpmufw_name = "a630_gmu.bin",
-		.gpmu_major = 0x0,
-		.gpmu_minor = 0x005,
+		.gpmu_major = 0x1,
+		.gpmu_minor = 0x001,
 	},
 };
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d16e42d..373264a 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -17,6 +17,7 @@
 #include <linux/of_device.h>
 #include <linux/delay.h>
 #include <linux/input.h>
+#include <linux/io.h>
 #include <soc/qcom/scm.h>
 
 #include <linux/msm-bus-board.h>
@@ -36,6 +37,7 @@
 #include "adreno_trace.h"
 
 #include "a3xx_reg.h"
+#include "a6xx_reg.h"
 #include "adreno_snapshot.h"
 
 /* Include the master list of GPU cores that are supported */
@@ -112,6 +114,11 @@
 	.active_list_lock = __SPIN_LOCK_UNLOCKED(device_3d0.active_list_lock),
 	.gpu_llc_slice_enable = true,
 	.gpuhtw_llc_slice_enable = true,
+	.preempt = {
+		.preempt_level = 1,
+		.skipsaverestore = 1,
+		.usesgmem = 1,
+	},
 };
 
 /* Ptr to array for the current set of fault detect registers */
@@ -554,7 +561,13 @@
 	return 0;
 }
 
-
+/**
+ * adreno_irqctrl() - Enables/disables the RBBM interrupt mask
+ * @adreno_dev: Pointer to an adreno_device
+ * @state: 1 for masked or 0 for unmasked
+ * Power: The caller of this function must make sure to use OOBs
+ * so that we know that the GPU is powered on
+ */
 void adreno_irqctrl(struct adreno_device *adreno_dev, int state)
 {
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
@@ -599,7 +612,8 @@
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	struct adreno_irq *irq_params = gpudev->irq;
 	irqreturn_t ret = IRQ_NONE;
-	unsigned int status = 0, tmp, int_bit;
+	unsigned int status = 0, fence = 0, fence_retries = 0, tmp, int_bit;
+	unsigned int status_retries = 0;
 	int i;
 
 	atomic_inc(&adreno_dev->pending_irq_refcnt);
@@ -614,9 +628,57 @@
 	if (gpudev->gpu_keepalive)
 		gpudev->gpu_keepalive(adreno_dev, true);
 
+	/*
+	 * If the AHB fence is not in ALLOW mode when we receive an RBBM
+	 * interrupt, something went wrong. This means that we cannot proceed
+	 * since the IRQ status and clear registers are not accessible.
+	 * This is usually harmless because the GMU will abort power collapse
+	 * and change the fence back to ALLOW. Poll so that this can happen.
+	 */
+	if (kgsl_gmu_isenabled(device)) {
+		do {
+			adreno_readreg(adreno_dev,
+					ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
+					&fence);
+
+			if (fence_retries == FENCE_RETRY_MAX) {
+				KGSL_DRV_CRIT_RATELIMIT(device,
+						"AHB fence stuck in ISR\n");
+				return ret;
+			}
+			fence_retries++;
+		} while (fence != 0);
+	}
+
 	adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
 
 	/*
+	 * Read status again to make sure the bits aren't transitory.
+	 * Transitory bits mean that they are spurious interrupts and are
+	 * seen while preemption is on going. Empirical experiments have
+	 * shown that the transitory bits are a timing thing and they
+	 * go away in the small time window between two or three consecutive
+	 * reads. If they don't go away, log the message and return.
+	 */
+	while (status_retries < STATUS_RETRY_MAX) {
+		unsigned int new_status;
+
+		adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS,
+			&new_status);
+
+		if (status == new_status)
+			break;
+
+		status = new_status;
+		status_retries++;
+	}
+
+	if (status_retries == STATUS_RETRY_MAX) {
+		KGSL_DRV_CRIT_RATELIMIT(device, "STATUS bits are not stable\n");
+			return ret;
+	}
+
+	/*
 	 * Clear all the interrupt bits but ADRENO_INT_RBBM_AHB_ERROR. Because
 	 * even if we clear it here, it will stay high until it is cleared
 	 * in its respective handler. Otherwise, the interrupt handler will
@@ -835,13 +897,13 @@
 		struct device_node *parent)
 {
 	struct device_node *node, *child;
+	unsigned int bin = 0;
 
 	node = of_find_node_by_name(parent, "qcom,gpu-pwrlevel-bins");
 	if (node == NULL)
 		return adreno_of_get_legacy_pwrlevels(adreno_dev, parent);
 
 	for_each_child_of_node(node, child) {
-		unsigned int bin;
 
 		if (of_property_read_u32(child, "qcom,speed-bin", &bin))
 			continue;
@@ -857,6 +919,8 @@
 		}
 	}
 
+	KGSL_CORE_ERR("GPU speed_bin:%d mismatch for efused bin:%d\n",
+			adreno_dev->speed_bin, bin);
 	return -ENODEV;
 }
 
@@ -881,6 +945,7 @@
 	 { ADRENO_QUIRK_DISABLE_LMLOADKILL,
 			"qcom,gpu-quirk-lmloadkill-disable" },
 	{ ADRENO_QUIRK_HFI_USE_REG, "qcom,gpu-quirk-hfi-use-reg" },
+	{ ADRENO_QUIRK_SECVID_SET_ONCE, "qcom,gpu-quirk-secvid-set-once" },
 };
 
 static int adreno_of_get_power(struct adreno_device *adreno_dev,
@@ -997,6 +1062,28 @@
 }
 #endif
 
+static void adreno_cx_dbgc_probe(struct kgsl_device *device)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct resource *res;
+
+	res = platform_get_resource_byname(device->pdev, IORESOURCE_MEM,
+					   "kgsl_3d0_cx_dbgc_memory");
+
+	if (res == NULL)
+		return;
+
+	adreno_dev->cx_dbgc_base = res->start - device->reg_phys;
+	adreno_dev->cx_dbgc_len = resource_size(res);
+	adreno_dev->cx_dbgc_virt = devm_ioremap(device->dev,
+					device->reg_phys +
+						adreno_dev->cx_dbgc_base,
+					adreno_dev->cx_dbgc_len);
+
+	if (adreno_dev->cx_dbgc_virt == NULL)
+		KGSL_DRV_WARN(device, "cx_dbgc ioremap failed\n");
+}
+
 static int adreno_probe(struct platform_device *pdev)
 {
 	struct kgsl_device *device;
@@ -1047,6 +1134,9 @@
 		return status;
 	}
 
+	/* Probe for the optional CX_DBGC block */
+	adreno_cx_dbgc_probe(device);
+
 	/*
 	 * qcom,iommu-secure-id is used to identify MMUs that can handle secure
 	 * content but that is only part of the story - the GPU also has to be
@@ -1249,6 +1339,10 @@
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	int ret;
 
+	if (!adreno_is_a3xx(adreno_dev))
+		kgsl_sharedmem_set(device, &device->scratch, 0, 0,
+				device->scratch.size);
+
 	ret = kgsl_pwrctrl_change_state(device, KGSL_STATE_INIT);
 	if (ret)
 		return ret;
@@ -1372,9 +1466,10 @@
 static void _set_secvid(struct kgsl_device *device)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	static bool set;
 
 	/* Program GPU contect protection init values */
-	if (device->mmu.secured) {
+	if (device->mmu.secured && !set) {
 		if (adreno_is_a4xx(adreno_dev))
 			adreno_writereg(adreno_dev,
 				ADRENO_REG_RBBM_SECVID_TRUST_CONFIG, 0x2);
@@ -1388,6 +1483,8 @@
 		adreno_writereg(adreno_dev,
 			ADRENO_REG_RBBM_SECVID_TSB_TRUSTED_SIZE,
 			KGSL_IOMMU_SECURE_SIZE);
+		if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_SECVID_SET_ONCE))
+			set = true;
 	}
 }
 
@@ -1438,6 +1535,22 @@
 	return ret;
 }
 
+static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
+{
+	int i;
+	struct adreno_ringbuffer *rb;
+
+	FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
+		if (rb->drawctxt_active)
+			kgsl_context_put(&(rb->drawctxt_active->base));
+		rb->drawctxt_active = NULL;
+
+		kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev),
+			&rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname),
+			0);
+	}
+}
+
 /**
  * _adreno_start - Power up the GPU and prepare to accept commands
  * @adreno_dev: Pointer to an adreno_device structure
@@ -1475,6 +1588,9 @@
 	if (status)
 		goto error_pwr_off;
 
+	/* Set any stale active contexts to NULL */
+	adreno_set_active_ctxs_null(adreno_dev);
+
 	/* Set the bit to indicate that we've just powered on */
 	set_bit(ADRENO_DEVICE_PWRON, &adreno_dev->priv);
 
@@ -1498,9 +1614,9 @@
 
 	/* Send OOB request to turn on the GX */
 	if (gpudev->oob_set) {
-		status = gpudev->oob_set(adreno_dev, OOB_GPUSTART_SET_MASK,
-				OOB_GPUSTART_CHECK_MASK,
-				OOB_GPUSTART_CLEAR_MASK);
+		status = gpudev->oob_set(adreno_dev, OOB_GPU_SET_MASK,
+				OOB_GPU_CHECK_MASK,
+				OOB_GPU_CLEAR_MASK);
 		if (status)
 			goto error_mmu_off;
 	}
@@ -1538,26 +1654,104 @@
 			}
 		}
 
-		/* VBIF DDR cycles */
-		if (adreno_dev->ram_cycles_lo == 0) {
-			ret = adreno_perfcounter_get(adreno_dev,
-				KGSL_PERFCOUNTER_GROUP_VBIF,
-				VBIF_AXI_TOTAL_BEATS,
-				&adreno_dev->ram_cycles_lo, NULL,
-				PERFCOUNTER_FLAG_KERNEL);
+		if (adreno_has_gbif(adreno_dev)) {
+			if (adreno_dev->starved_ram_lo_ch1 == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF_PWR, 1,
+					&adreno_dev->starved_ram_lo_ch1, NULL,
+					PERFCOUNTER_FLAG_KERNEL);
 
-			if (ret) {
-				KGSL_DRV_ERR(device,
-					"Unable to get perf counters for bus DCVS\n");
-				adreno_dev->ram_cycles_lo = 0;
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->starved_ram_lo_ch1 = 0;
+				}
+			}
+
+			if (adreno_dev->ram_cycles_lo == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF,
+					GBIF_AXI0_READ_DATA_TOTAL_BEATS,
+					&adreno_dev->ram_cycles_lo, NULL,
+					PERFCOUNTER_FLAG_KERNEL);
+
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->ram_cycles_lo = 0;
+				}
+			}
+
+			if (adreno_dev->ram_cycles_lo_ch1_read == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF,
+					GBIF_AXI1_READ_DATA_TOTAL_BEATS,
+					&adreno_dev->ram_cycles_lo_ch1_read,
+					NULL,
+					PERFCOUNTER_FLAG_KERNEL);
+
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->ram_cycles_lo_ch1_read = 0;
+				}
+			}
+
+			if (adreno_dev->ram_cycles_lo_ch0_write == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF,
+					GBIF_AXI0_WRITE_DATA_TOTAL_BEATS,
+					&adreno_dev->ram_cycles_lo_ch0_write,
+					NULL,
+					PERFCOUNTER_FLAG_KERNEL);
+
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->ram_cycles_lo_ch0_write = 0;
+				}
+			}
+
+			if (adreno_dev->ram_cycles_lo_ch1_write == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF,
+					GBIF_AXI1_WRITE_DATA_TOTAL_BEATS,
+					&adreno_dev->ram_cycles_lo_ch1_write,
+					NULL,
+					PERFCOUNTER_FLAG_KERNEL);
+
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->ram_cycles_lo_ch1_write = 0;
+				}
+			}
+		} else {
+			/* VBIF DDR cycles */
+			if (adreno_dev->ram_cycles_lo == 0) {
+				ret = adreno_perfcounter_get(adreno_dev,
+					KGSL_PERFCOUNTER_GROUP_VBIF,
+					VBIF_AXI_TOTAL_BEATS,
+					&adreno_dev->ram_cycles_lo, NULL,
+					PERFCOUNTER_FLAG_KERNEL);
+
+				if (ret) {
+					KGSL_DRV_ERR(device,
+						"Unable to get perf counters for bus DCVS\n");
+					adreno_dev->ram_cycles_lo = 0;
+				}
 			}
 		}
 	}
 
 	/* Clear the busy_data stats - we're starting over from scratch */
 	adreno_dev->busy_data.gpu_busy = 0;
-	adreno_dev->busy_data.vbif_ram_cycles = 0;
-	adreno_dev->busy_data.vbif_starved_ram = 0;
+	adreno_dev->busy_data.bif_ram_cycles = 0;
+	adreno_dev->busy_data.bif_ram_cycles_read_ch1 = 0;
+	adreno_dev->busy_data.bif_ram_cycles_write_ch0 = 0;
+	adreno_dev->busy_data.bif_ram_cycles_write_ch1 = 0;
+	adreno_dev->busy_data.bif_starved_ram = 0;
+	adreno_dev->busy_data.bif_starved_ram_ch1 = 0;
 
 	/* Restore performance counter registers with saved values */
 	adreno_perfcounter_restore(adreno_dev);
@@ -1599,17 +1793,28 @@
 				pmqos_active_vote);
 
 	/* Send OOB request to allow IFPC */
-	if (gpudev->oob_clear)
-		gpudev->oob_clear(adreno_dev, OOB_GPUSTART_CLEAR_MASK);
+	if (gpudev->oob_clear) {
+		gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
+
+		/* If we made it this far, the BOOT OOB was sent to the GMU */
+		if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
+			gpudev->oob_clear(adreno_dev,
+					OOB_BOOT_SLUMBER_CLEAR_MASK);
+	}
 
 	return 0;
 
 error_oob_clear:
 	if (gpudev->oob_clear)
-		gpudev->oob_clear(adreno_dev, OOB_GPUSTART_CLEAR_MASK);
+		gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
 
 error_mmu_off:
 	kgsl_mmu_stop(&device->mmu);
+	if (gpudev->oob_clear &&
+			ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) {
+		gpudev->oob_clear(adreno_dev,
+				OOB_BOOT_SLUMBER_CLEAR_MASK);
+	}
 
 error_pwr_off:
 	/* set the state back to original state */
@@ -1648,30 +1853,25 @@
 	return ret;
 }
 
-static void adreno_set_active_ctxs_null(struct adreno_device *adreno_dev)
-{
-	int i;
-	struct adreno_ringbuffer *rb;
-
-	FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
-		if (rb->drawctxt_active)
-			kgsl_context_put(&(rb->drawctxt_active->base));
-		rb->drawctxt_active = NULL;
-
-		kgsl_sharedmem_writel(KGSL_DEVICE(adreno_dev),
-			&rb->pagetable_desc, PT_INFO_OFFSET(current_rb_ptname),
-			0);
-	}
-}
-
 static int adreno_stop(struct kgsl_device *device)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+	int error = 0;
 
 	if (!test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv))
 		return 0;
 
-	adreno_set_active_ctxs_null(adreno_dev);
+	/* Turn the power on one last time before stopping */
+	if (gpudev->oob_set) {
+		error = gpudev->oob_set(adreno_dev, OOB_GPU_SET_MASK,
+				OOB_GPU_CHECK_MASK,
+				OOB_GPU_CLEAR_MASK);
+		if (error) {
+			gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
+			return error;
+		}
+	}
 
 	adreno_dispatcher_stop(adreno_dev);
 
@@ -1694,13 +1894,43 @@
 	/* Save physical performance counter values before GPU power down*/
 	adreno_perfcounter_save(adreno_dev);
 
+	if (gpudev->oob_clear)
+		gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
+
+	/*
+	 * Saving perfcounters will use an OOB to put the GMU into
+	 * active state. Before continuing, we should wait for the
+	 * GMU to return to the lowest idle level. This is
+	 * because some idle level transitions require VBIF and MMU.
+	 */
+	if (gpudev->wait_for_lowest_idle &&
+			gpudev->wait_for_lowest_idle(adreno_dev)) {
+		struct gmu_device *gmu = &device->gmu;
+
+		set_bit(GMU_FAULT, &gmu->flags);
+		gmu_snapshot(device);
+		/*
+		 * Assume GMU hang after 10ms without responding.
+		 * It shall be relative safe to clear vbif and stop
+		 * MMU later. Early return in adreno_stop function
+		 * will result in kernel panic in adreno_start
+		 */
+		error = -EINVAL;
+	}
+
 	adreno_vbif_clear_pending_transactions(device);
 
 	kgsl_mmu_stop(&device->mmu);
 
+	/*
+	 * At this point, MMU is turned off so we can safely
+	 * destroy any pending contexts and their pagetables
+	 */
+	adreno_set_active_ctxs_null(adreno_dev);
+
 	clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
 
-	return 0;
+	return error;
 }
 
 static inline bool adreno_try_soft_reset(struct kgsl_device *device, int fault)
@@ -2323,9 +2553,9 @@
 	int ret;
 
 	if (gpudev->oob_set) {
-		ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
-				OOB_CPINIT_CHECK_MASK,
-				OOB_CPINIT_CLEAR_MASK);
+		ret = gpudev->oob_set(adreno_dev, OOB_GPU_SET_MASK,
+				OOB_GPU_CHECK_MASK,
+				OOB_GPU_CLEAR_MASK);
 		if (ret)
 			return ret;
 	}
@@ -2349,14 +2579,18 @@
 		ret = _soft_reset(adreno_dev);
 	if (ret) {
 		if (gpudev->oob_clear)
-			gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+			gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
 		return ret;
 	}
 
 	/* Clear the busy_data stats - we're starting over from scratch */
 	adreno_dev->busy_data.gpu_busy = 0;
-	adreno_dev->busy_data.vbif_ram_cycles = 0;
-	adreno_dev->busy_data.vbif_starved_ram = 0;
+	adreno_dev->busy_data.bif_ram_cycles = 0;
+	adreno_dev->busy_data.bif_ram_cycles_read_ch1 = 0;
+	adreno_dev->busy_data.bif_ram_cycles_write_ch0 = 0;
+	adreno_dev->busy_data.bif_ram_cycles_write_ch1 = 0;
+	adreno_dev->busy_data.bif_starved_ram = 0;
+	adreno_dev->busy_data.bif_starved_ram_ch1 = 0;
 
 	/* Set the page table back to the default page table */
 	adreno_ringbuffer_set_global(adreno_dev, 0);
@@ -2399,7 +2633,7 @@
 	adreno_perfcounter_restore(adreno_dev);
 
 	if (gpudev->oob_clear)
-		gpudev->oob_clear(adreno_dev, OOB_CPINIT_CLEAR_MASK);
+		gpudev->oob_clear(adreno_dev, OOB_GPU_CLEAR_MASK);
 
 	return ret;
 }
@@ -2464,7 +2698,7 @@
 
 	dev_err(device->dev, " hwfault=%8.8X\n", hwfault);
 
-	kgsl_device_snapshot(device, NULL);
+	kgsl_device_snapshot(device, NULL, adreno_gmu_gpu_fault(adreno_dev));
 }
 
 /**
@@ -2694,6 +2928,56 @@
 	rmb();
 }
 
+bool adreno_is_cx_dbgc_register(struct kgsl_device *device,
+		unsigned int offsetwords)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+	return adreno_dev->cx_dbgc_virt &&
+		(offsetwords >= (adreno_dev->cx_dbgc_base >> 2)) &&
+		(offsetwords < (adreno_dev->cx_dbgc_base +
+				adreno_dev->cx_dbgc_len) >> 2);
+}
+
+void adreno_cx_dbgc_regread(struct kgsl_device *device,
+	unsigned int offsetwords, unsigned int *value)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	unsigned int cx_dbgc_offset;
+
+	if (!adreno_is_cx_dbgc_register(device, offsetwords))
+		return;
+
+	cx_dbgc_offset = (offsetwords << 2) - adreno_dev->cx_dbgc_base;
+	*value = __raw_readl(adreno_dev->cx_dbgc_virt + cx_dbgc_offset);
+
+	/*
+	 * ensure this read finishes before the next one.
+	 * i.e. act like normal readl()
+	 */
+	rmb();
+}
+
+void adreno_cx_dbgc_regwrite(struct kgsl_device *device,
+	unsigned int offsetwords, unsigned int value)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	unsigned int cx_dbgc_offset;
+
+	if (!adreno_is_cx_dbgc_register(device, offsetwords))
+		return;
+
+	cx_dbgc_offset = (offsetwords << 2) - adreno_dev->cx_dbgc_base;
+	trace_kgsl_regwrite(device, offsetwords, value);
+
+	/*
+	 * ensure previous writes post before this one,
+	 * i.e. act like normal writel()
+	 */
+	wmb();
+	__raw_writel(value, adreno_dev->cx_dbgc_virt + cx_dbgc_offset);
+}
+
 /**
  * adreno_waittimestamp - sleep while waiting for the specified timestamp
  * @device - pointer to a KGSL device structure
@@ -2870,9 +3154,10 @@
 			gpu_busy += adj;
 		}
 
-		if (kgsl_gmu_isenabled(device)) {
+		if (adreno_is_a6xx(adreno_dev)) {
 			/* clock sourced from XO */
-			stats->busy_time = gpu_busy * 10 / 192;
+			stats->busy_time = gpu_busy * 10;
+			do_div(stats->busy_time, 192);
 		} else {
 			/* clock sourced from GFX3D */
 			stats->busy_time = adreno_ticks_to_us(gpu_busy,
@@ -2886,12 +3171,36 @@
 		if (adreno_dev->ram_cycles_lo != 0)
 			ram_cycles = counter_delta(device,
 				adreno_dev->ram_cycles_lo,
-				&busy->vbif_ram_cycles);
+				&busy->bif_ram_cycles);
+
+		if (adreno_has_gbif(adreno_dev)) {
+			if (adreno_dev->ram_cycles_lo_ch1_read != 0)
+				ram_cycles += counter_delta(device,
+					adreno_dev->ram_cycles_lo_ch1_read,
+					&busy->bif_ram_cycles_read_ch1);
+
+			if (adreno_dev->ram_cycles_lo_ch0_write != 0)
+				ram_cycles += counter_delta(device,
+					adreno_dev->ram_cycles_lo_ch0_write,
+					&busy->bif_ram_cycles_write_ch0);
+
+			if (adreno_dev->ram_cycles_lo_ch1_write != 0)
+				ram_cycles += counter_delta(device,
+					adreno_dev->ram_cycles_lo_ch1_write,
+					&busy->bif_ram_cycles_write_ch1);
+		}
 
 		if (adreno_dev->starved_ram_lo != 0)
 			starved_ram = counter_delta(device,
 				adreno_dev->starved_ram_lo,
-				&busy->vbif_starved_ram);
+				&busy->bif_starved_ram);
+
+		if (adreno_has_gbif(adreno_dev)) {
+			if (adreno_dev->starved_ram_lo_ch1 != 0)
+				starved_ram += counter_delta(device,
+					adreno_dev->starved_ram_lo_ch1,
+					&busy->bif_starved_ram_ch1);
+		}
 
 		stats->ram_time = ram_cycles;
 		stats->ram_wait = starved_ram;
@@ -3091,6 +3400,7 @@
 	.irq_handler = adreno_irq_handler,
 	.drain = adreno_drain,
 	/* Optional functions */
+	.snapshot_gmu = adreno_snapshot_gmu,
 	.drawctxt_create = adreno_drawctxt_create,
 	.drawctxt_detach = adreno_drawctxt_detach,
 	.drawctxt_destroy = adreno_drawctxt_destroy,
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 36bd656..6f5fb6b 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -140,6 +140,8 @@
 #define ADRENO_QUIRK_DISABLE_LMLOADKILL BIT(5)
 /* Allow HFI to use registers to send message to GMU */
 #define ADRENO_QUIRK_HFI_USE_REG BIT(6)
+/* Only set protected SECVID registers once */
+#define ADRENO_QUIRK_SECVID_SET_ONCE BIT(7)
 
 /* Flags to control command packet settings */
 #define KGSL_CMD_FLAGS_NONE             0
@@ -162,6 +164,12 @@
 /* Number of times to try hard reset */
 #define NUM_TIMES_RESET_RETRY 5
 
+/* Number of times to poll the AHB fence in ISR */
+#define FENCE_RETRY_MAX 100
+
+/* Number of times to see if INT_0_STATUS changed or not */
+#define STATUS_RETRY_MAX 3
+
 /* One cannot wait forever for the core to idle, so set an upper limit to the
  * amount of time to wait for the core to go idle
  */
@@ -208,6 +216,7 @@
 #define ADRENO_IOMMU_PAGE_FAULT BIT(3)
 #define ADRENO_PREEMPT_FAULT BIT(4)
 #define ADRENO_GMU_FAULT BIT(5)
+#define ADRENO_CTX_DETATCH_TIMEOUT_FAULT BIT(6)
 
 #define ADRENO_SPTP_PC_CTRL 0
 #define ADRENO_PPD_CTRL     1
@@ -258,6 +267,10 @@
  * @work: A work struct for the preemption worker (for 5XX)
  * @token_submit: Indicates if a preempt token has been submitted in
  * current ringbuffer (for 4XX)
+ * preempt_level: The level of preemption (for 6XX)
+ * skipsaverestore: To skip saverestore during L1 preemption (for 6XX)
+ * usesgmem: enable GMEM save/restore across preemption (for 6XX)
+ * count: Track the number of preemptions triggered
  */
 struct adreno_preemption {
 	atomic_t state;
@@ -265,13 +278,21 @@
 	struct timer_list timer;
 	struct work_struct work;
 	bool token_submit;
+	unsigned int preempt_level;
+	bool skipsaverestore;
+	bool usesgmem;
+	unsigned int count;
 };
 
 
 struct adreno_busy_data {
 	unsigned int gpu_busy;
-	unsigned int vbif_ram_cycles;
-	unsigned int vbif_starved_ram;
+	unsigned int bif_ram_cycles;
+	unsigned int bif_ram_cycles_read_ch1;
+	unsigned int bif_ram_cycles_write_ch0;
+	unsigned int bif_ram_cycles_write_ch1;
+	unsigned int bif_starved_ram;
+	unsigned int bif_starved_ram_ch1;
 	unsigned int throttle_cycles[ADRENO_GPMU_THROTTLE_COUNTERS];
 };
 
@@ -388,8 +409,18 @@
  * @pwron_fixup_dwords: Number of dwords in the command buffer
  * @input_work: Work struct for turning on the GPU after a touch event
  * @busy_data: Struct holding GPU VBIF busy stats
- * @ram_cycles_lo: Number of DDR clock cycles for the monitor session
- * @perfctr_pwr_lo: Number of cycles VBIF is stalled by DDR
+ * @ram_cycles_lo: Number of DDR clock cycles for the monitor session (Only
+ * DDR channel 0 read cycles in case of GBIF)
+ * @ram_cycles_lo_ch1_read: Number of DDR channel 1 Read clock cycles for
+ * the monitor session
+ * @ram_cycles_lo_ch0_write: Number of DDR channel 0 Write clock cycles for
+ * the monitor session
+ * @ram_cycles_lo_ch1_write: Number of DDR channel 0 Write clock cycles for
+ * the monitor session
+ * @starved_ram_lo: Number of cycles VBIF/GBIF is stalled by DDR (Only channel 0
+ * stall cycles in case of GBIF)
+ * @starved_ram_lo_ch1: Number of cycles GBIF is stalled by DDR channel 1
+ * @perfctr_pwr_lo: GPU busy cycles
  * @halt: Atomic variable to check whether the GPU is currently halted
  * @pending_irq_refcnt: Atomic variable to keep track of running IRQ handlers
  * @ctx_d_debugfs: Context debugfs node
@@ -425,6 +456,9 @@
 	unsigned int chipid;
 	unsigned long gmem_base;
 	unsigned long gmem_size;
+	unsigned long cx_dbgc_base;
+	unsigned int cx_dbgc_len;
+	void __iomem *cx_dbgc_virt;
 	const struct adreno_gpu_core *gpucore;
 	struct adreno_firmware fw[2];
 	size_t gpmu_cmds_size;
@@ -446,7 +480,11 @@
 	struct work_struct input_work;
 	struct adreno_busy_data busy_data;
 	unsigned int ram_cycles_lo;
+	unsigned int ram_cycles_lo_ch1_read;
+	unsigned int ram_cycles_lo_ch0_write;
+	unsigned int ram_cycles_lo_ch1_write;
 	unsigned int starved_ram_lo;
+	unsigned int starved_ram_lo_ch1;
 	unsigned int perfctr_pwr_lo;
 	atomic_t halt;
 	atomic_t pending_irq_refcnt;
@@ -483,10 +521,6 @@
 	void *gpuhtw_llc_slice;
 	bool gpuhtw_llc_slice_enable;
 	unsigned int zap_loaded;
-	unsigned int preempt_level;
-	bool usesgmem;
-	bool skipsaverestore;
-
 };
 
 /**
@@ -596,6 +630,12 @@
 	ADRENO_REG_CP_PROTECT_REG_0,
 	ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_LO,
 	ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI,
+	ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_LO,
+	ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_HI,
+	ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_LO,
+	ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_HI,
+	ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_LO,
+	ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_HI,
 	ADRENO_REG_RBBM_STATUS,
 	ADRENO_REG_RBBM_STATUS3,
 	ADRENO_REG_RBBM_PERFCTR_CTL,
@@ -621,6 +661,8 @@
 	ADRENO_REG_RBBM_RBBM_CTL,
 	ADRENO_REG_UCHE_INVALIDATE0,
 	ADRENO_REG_UCHE_INVALIDATE1,
+	ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+	ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
 	ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
 	ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
 	ADRENO_REG_RBBM_SECVID_TRUST_CONTROL,
@@ -634,6 +676,9 @@
 	ADRENO_REG_VBIF_XIN_HALT_CTRL0,
 	ADRENO_REG_VBIF_XIN_HALT_CTRL1,
 	ADRENO_REG_VBIF_VERSION,
+	ADRENO_REG_GBIF_HALT,
+	ADRENO_REG_GBIF_HALT_ACK,
+	ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
 	ADRENO_REG_GMU_AO_INTERRUPT_EN,
 	ADRENO_REG_GMU_AO_HOST_INTERRUPT_CLR,
 	ADRENO_REG_GMU_AO_HOST_INTERRUPT_STATUS,
@@ -810,6 +855,13 @@
 	struct adreno_snapshot_sizes *sect_sizes;
 };
 
+enum adreno_cp_marker_type {
+	IFPC_DISABLE,
+	IFPC_ENABLE,
+	IB1LIST_START,
+	IB1LIST_END,
+};
+
 struct adreno_gpudev {
 	/*
 	 * These registers are in a different location on different devices,
@@ -832,6 +884,7 @@
 	/* GPU specific function hooks */
 	void (*irq_trace)(struct adreno_device *, unsigned int status);
 	void (*snapshot)(struct adreno_device *, struct kgsl_snapshot *);
+	void (*snapshot_gmu)(struct adreno_device *, struct kgsl_snapshot *);
 	void (*platform_setup)(struct adreno_device *);
 	void (*init)(struct adreno_device *);
 	void (*remove)(struct adreno_device *);
@@ -856,7 +909,8 @@
 				unsigned int *cmds,
 				struct kgsl_context *context);
 	int (*preemption_yield_enable)(unsigned int *);
-	unsigned int (*set_marker)(unsigned int *cmds, int start);
+	unsigned int (*set_marker)(unsigned int *cmds,
+				enum adreno_cp_marker_type type);
 	unsigned int (*preemption_post_ibsubmit)(
 				struct adreno_device *adreno_dev,
 				unsigned int *cmds);
@@ -881,6 +935,7 @@
 	int (*rpmh_gpu_pwrctrl)(struct adreno_device *, unsigned int ops,
 				unsigned int arg1, unsigned int arg2);
 	bool (*hw_isidle)(struct adreno_device *);
+	int (*wait_for_lowest_idle)(struct adreno_device *);
 	int (*wait_for_gmu_idle)(struct adreno_device *);
 	const char *(*iommu_fault_block)(struct adreno_device *adreno_dev,
 				unsigned int fsynr1);
@@ -888,6 +943,11 @@
 	int (*soft_reset)(struct adreno_device *);
 	bool (*gx_is_on)(struct adreno_device *);
 	bool (*sptprac_is_on)(struct adreno_device *);
+	unsigned int (*ccu_invalidate)(struct adreno_device *adreno_dev,
+				unsigned int *cmds);
+	int (*perfcounter_update)(struct adreno_device *adreno_dev,
+				struct adreno_perfcount_register *reg,
+				bool update_reg);
 };
 
 /**
@@ -1006,6 +1066,9 @@
 		struct kgsl_snapshot *snapshot,
 		struct kgsl_context *context);
 
+void adreno_snapshot_gmu(struct kgsl_device *device,
+		struct kgsl_snapshot *snapshot);
+
 int adreno_reset(struct kgsl_device *device, int fault);
 
 void adreno_fault_skipcmd_detached(struct adreno_device *adreno_dev,
@@ -1043,6 +1106,13 @@
 		unsigned int *val);
 void adreno_efuse_unmap(struct adreno_device *adreno_dev);
 
+bool adreno_is_cx_dbgc_register(struct kgsl_device *device,
+		unsigned int offset);
+void adreno_cx_dbgc_regread(struct kgsl_device *adreno_device,
+		unsigned int offsetwords, unsigned int *value);
+void adreno_cx_dbgc_regwrite(struct kgsl_device *device,
+		unsigned int offsetwords, unsigned int value);
+
 #define ADRENO_TARGET(_name, _id) \
 static inline int adreno_is_##_name(struct adreno_device *adreno_dev) \
 { \
@@ -1166,6 +1236,12 @@
 		(ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 0);
 }
 
+static inline int adreno_is_a630v2(struct adreno_device *adreno_dev)
+{
+	return (ADRENO_GPUREV(adreno_dev) == ADRENO_REV_A630) &&
+		(ADRENO_CHIPID_PATCH(adreno_dev->chipid) == 1);
+}
+
 /*
  * adreno_checkreg_off() - Checks the validity of a register enum
  * @adreno_dev:		Pointer to adreno device
@@ -1329,6 +1405,10 @@
 	smp_wmb();
 }
 
+static inline bool adreno_gmu_gpu_fault(struct adreno_device *adreno_dev)
+{
+	return adreno_gpu_fault(adreno_dev) & ADRENO_GMU_FAULT;
+}
 
 /**
  * adreno_clear_gpu_fault() - Clear the GPU fault register
@@ -1693,21 +1773,60 @@
 	spin_unlock_irqrestore(&rb->preempt_lock, flags);
 }
 
+static inline bool is_power_counter_overflow(struct adreno_device *adreno_dev,
+	unsigned int reg, unsigned int prev_val, unsigned int *perfctr_pwr_hi)
+{
+	unsigned int val;
+	bool ret = false;
+
+	/*
+	 * If prev_val is zero, it is first read after perf counter reset.
+	 * So set perfctr_pwr_hi register to zero.
+	 */
+	if (prev_val == 0) {
+		*perfctr_pwr_hi = 0;
+		return ret;
+	}
+	adreno_readreg(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI, &val);
+	if (val != *perfctr_pwr_hi) {
+		*perfctr_pwr_hi = val;
+		ret = true;
+	}
+	return ret;
+}
+
 static inline unsigned int counter_delta(struct kgsl_device *device,
 			unsigned int reg, unsigned int *counter)
 {
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	unsigned int val;
 	unsigned int ret = 0;
+	bool overflow = true;
+	static unsigned int perfctr_pwr_hi;
 
 	/* Read the value */
 	kgsl_regread(device, reg, &val);
 
+	if (adreno_is_a5xx(adreno_dev) && reg == adreno_getreg
+		(adreno_dev, ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO))
+		overflow = is_power_counter_overflow(adreno_dev, reg,
+				*counter, &perfctr_pwr_hi);
+
 	/* Return 0 for the first read */
 	if (*counter != 0) {
-		if (val < *counter)
-			ret = (0xFFFFFFFF - *counter) + val;
-		else
+		if (val >= *counter) {
 			ret = val - *counter;
+		} else if (overflow == true) {
+			ret = (0xFFFFFFFF - *counter) + val;
+		} else {
+			/*
+			 * Since KGSL got abnormal value from the counter,
+			 * We will drop the value from being accumulated.
+			 */
+			pr_warn_once("KGSL: Abnormal value :0x%x (0x%x) from perf counter : 0x%x\n",
+					val, *counter, reg);
+			return 0;
+		}
 	}
 
 	*counter = val;
@@ -1746,6 +1865,47 @@
 	kgsl_active_count_put(KGSL_DEVICE(adreno_dev));
 }
 
+static inline bool adreno_has_gbif(struct adreno_device *adreno_dev)
+{
+	if (adreno_is_a615(adreno_dev))
+		return true;
+	else
+		return false;
+}
+
+/**
+ * adreno_wait_for_vbif_halt_ack() - wait for VBIF acknowledgment
+ * for given HALT request.
+ * @ack_reg: register offset to wait for acknowledge
+ */
+static inline int adreno_wait_for_vbif_halt_ack(struct kgsl_device *device,
+	int ack_reg)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+	unsigned long wait_for_vbif;
+	unsigned int mask = gpudev->vbif_xin_halt_ctrl0_mask;
+	unsigned int val;
+	int ret = 0;
+
+	/* wait for the transactions to clear */
+	wait_for_vbif = jiffies + msecs_to_jiffies(100);
+	while (1) {
+		adreno_readreg(adreno_dev, ack_reg,
+			&val);
+		if ((val & mask) == mask)
+			break;
+		if (time_after(jiffies, wait_for_vbif)) {
+			KGSL_DRV_ERR(device,
+				"Wait limit reached for VBIF XIN Halt\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
+	}
+
+	return ret;
+}
+
 /**
  * adreno_vbif_clear_pending_transactions() - Clear transactions in VBIF pipe
  * @device: Pointer to the device whose VBIF pipe is to be cleared
@@ -1756,27 +1916,24 @@
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	unsigned int mask = gpudev->vbif_xin_halt_ctrl0_mask;
-	unsigned int val;
-	unsigned long wait_for_vbif;
 	int ret = 0;
 
-	adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, mask);
-	/* wait for the transactions to clear */
-	wait_for_vbif = jiffies + msecs_to_jiffies(100);
-	while (1) {
-		adreno_readreg(adreno_dev,
-			ADRENO_REG_VBIF_XIN_HALT_CTRL1, &val);
-		if ((val & mask) == mask)
-			break;
-		if (time_after(jiffies, wait_for_vbif)) {
-			KGSL_DRV_ERR(device,
-				"Wait limit reached for VBIF XIN Halt\n");
-			ret = -ETIMEDOUT;
-			break;
-		}
+	if (adreno_has_gbif(adreno_dev)) {
+		adreno_writereg(adreno_dev, ADRENO_REG_GBIF_HALT, mask);
+		ret = adreno_wait_for_vbif_halt_ack(device,
+				ADRENO_REG_GBIF_HALT_ACK);
+		adreno_writereg(adreno_dev, ADRENO_REG_GBIF_HALT, 0);
+	} else {
+		adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0,
+			mask);
+		ret = adreno_wait_for_vbif_halt_ack(device,
+				ADRENO_REG_VBIF_XIN_HALT_CTRL1);
+		adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, 0);
 	}
-	adreno_writereg(adreno_dev, ADRENO_REG_VBIF_XIN_HALT_CTRL0, 0);
 	return ret;
 }
 
+void adreno_gmu_fenced_write(struct adreno_device *adreno_dev,
+	enum adreno_regs offset, unsigned int val,
+	unsigned int fence_mask);
 #endif /*__ADRENO_H */
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 5d7fb21..b2cdf56 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -685,7 +685,7 @@
 		struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 
 		dev_err(device->dev, "CP initialization failed to idle\n");
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, NULL, false);
 	}
 
 	return ret;
@@ -1533,6 +1533,10 @@
 			A3XX_UCHE_CACHE_INVALIDATE0_REG),
 	ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1,
 			A3XX_UCHE_CACHE_INVALIDATE1_REG),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+			A3XX_RBBM_PERFCTR_RBBM_0_LO),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+			A3XX_RBBM_PERFCTR_RBBM_0_HI),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
 				A3XX_RBBM_PERFCTR_LOAD_VALUE_LO),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
@@ -1823,7 +1827,7 @@
 
 	if (ret) {
 		KGSL_DRV_ERR(device, "microcode bootstrap failed to idle\n");
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, NULL, false);
 	}
 
 	/* Clear the chicken bit for speed up on A430 and its derivatives */
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index c807b67..80ceabd 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -808,6 +808,10 @@
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_SW_RESET_CMD, A4XX_RBBM_SW_RESET_CMD),
 	ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A4XX_UCHE_INVALIDATE0),
 	ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE1, A4XX_UCHE_INVALIDATE1),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+				A4XX_RBBM_PERFCTR_RBBM_0_LO),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+				A4XX_RBBM_PERFCTR_RBBM_0_HI),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
 				A4XX_RBBM_PERFCTR_LOAD_VALUE_LO),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
@@ -1535,7 +1539,7 @@
 		struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 
 		dev_err(device->dev, "CP initialization failed to idle\n");
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, NULL, false);
 	}
 
 	return ret;
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 6cddd08..768a4bb 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -61,8 +61,8 @@
 };
 
 static void a5xx_irq_storm_worker(struct work_struct *work);
-static int _read_fw2_block_header(uint32_t *header, uint32_t id,
-	uint32_t major, uint32_t minor);
+static int _read_fw2_block_header(uint32_t *header, uint32_t remain,
+	uint32_t id, uint32_t major, uint32_t minor);
 static void a5xx_gpmu_reset(struct work_struct *work);
 static int a5xx_gpmu_init(struct adreno_device *adreno_dev);
 
@@ -193,6 +193,8 @@
 	kgsl_free_global(&adreno_dev->dev, &crit_pkts_refbuf2);
 	kgsl_free_global(&adreno_dev->dev, &crit_pkts_refbuf3);
 
+	kgsl_iommu_unmap_global_secure_pt_entry(KGSL_DEVICE(adreno_dev),
+			&crit_pkts_refbuf0);
 	kgsl_sharedmem_free(&crit_pkts_refbuf0);
 
 }
@@ -231,8 +233,10 @@
 	if (ret)
 		return ret;
 
-	kgsl_add_global_secure_entry(&adreno_dev->dev,
+	ret = kgsl_iommu_map_global_secure_pt_entry(&adreno_dev->dev,
 					&crit_pkts_refbuf0);
+	if (ret)
+		return ret;
 
 	ret = kgsl_allocate_global(&adreno_dev->dev,
 					&crit_pkts_refbuf1,
@@ -293,8 +297,13 @@
 
 	INIT_WORK(&adreno_dev->irq_storm_work, a5xx_irq_storm_worker);
 
-	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_CRITICAL_PACKETS))
-		a5xx_critical_packet_construct(adreno_dev);
+	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_CRITICAL_PACKETS)) {
+		int ret;
+
+		ret = a5xx_critical_packet_construct(adreno_dev);
+		if (ret)
+			a5xx_critical_packet_destroy(adreno_dev);
+	}
 
 	a5xx_crashdump_init(adreno_dev);
 }
@@ -678,6 +687,7 @@
 	if (data[1] != GPMU_FIRMWARE_ID)
 		goto err;
 	ret = _read_fw2_block_header(&data[2],
+		data[0] - 2,
 		GPMU_FIRMWARE_ID,
 		adreno_dev->gpucore->gpmu_major,
 		adreno_dev->gpucore->gpmu_minor);
@@ -1200,8 +1210,8 @@
 	kgsl_regwrite(device, A5XX_RBBM_ISDB_CNT, on ? 0x00000182 : 0x00000180);
 }
 
-static int _read_fw2_block_header(uint32_t *header, uint32_t id,
-				uint32_t major, uint32_t minor)
+static int _read_fw2_block_header(uint32_t *header, uint32_t remain,
+			uint32_t id, uint32_t major, uint32_t minor)
 {
 	uint32_t header_size;
 	int i = 1;
@@ -1211,7 +1221,8 @@
 
 	header_size = header[0];
 	/* Headers have limited size and always occur as pairs of words */
-	if (header_size > MAX_HEADER_SIZE || header_size % 2)
+	if (header_size >  MAX_HEADER_SIZE || header_size >= remain ||
+				header_size % 2 || header_size == 0)
 		return -EINVAL;
 	/* Sequences must have an identifying id first thing in their header */
 	if (id == GPMU_SEQUENCE_ID) {
@@ -1275,8 +1286,8 @@
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	const struct firmware *fw;
-	uint32_t block_size = 0, block_total = 0, fw_size;
-	uint32_t *block;
+	uint64_t block_size = 0, block_total = 0;
+	uint32_t fw_size, *block;
 	int ret = -EINVAL;
 
 	if (!adreno_dev->gpucore->regfw_name)
@@ -1298,7 +1309,8 @@
 	/* All offset numbers calculated from file description */
 	while (block_total < fw_size) {
 		block_size = block[0];
-		if (block_size >= fw_size || block_size < 2)
+		if (((block_total + block_size) >= fw_size)
+				|| block_size < 5)
 			goto err;
 		if (block[1] != GPMU_SEQUENCE_ID)
 			goto err;
@@ -1306,6 +1318,7 @@
 		/* For now ignore blocks other than the LM sequence */
 		if (block[4] == LM_SEQUENCE_ID) {
 			ret = _read_fw2_block_header(&block[2],
+				block_size - 2,
 				GPMU_SEQUENCE_ID,
 				adreno_dev->gpucore->lm_major,
 				adreno_dev->gpucore->lm_minor);
@@ -1313,6 +1326,9 @@
 				goto err;
 
 			adreno_dev->lm_fw = fw;
+
+			if (block[2] > (block_size - 2))
+				goto err;
 			adreno_dev->lm_sequence = block + block[2] + 3;
 			adreno_dev->lm_size = block_size - block[2] - 2;
 		}
@@ -1325,7 +1341,7 @@
 err:
 	release_firmware(fw);
 	KGSL_PWR_ERR(device,
-		"Register file failed to load sz=%d bsz=%d header=%d\n",
+		"Register file failed to load sz=%d bsz=%llu header=%d\n",
 		fw_size, block_size, ret);
 }
 
@@ -2998,6 +3014,10 @@
 		ADRENO_REG_DEFINE(ADRENO_REG_RBBM_BLOCK_SW_RESET_CMD2,
 					  A5XX_RBBM_BLOCK_SW_RESET_CMD2),
 	ADRENO_REG_DEFINE(ADRENO_REG_UCHE_INVALIDATE0, A5XX_UCHE_INVALIDATE0),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_LO,
+				A5XX_RBBM_PERFCTR_RBBM_0_LO),
+	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_RBBM_0_HI,
+				A5XX_RBBM_PERFCTR_RBBM_0_HI),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_LO,
 				A5XX_RBBM_PERFCTR_LOAD_VALUE_LO),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_LOAD_VALUE_HI,
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 78b56bc..d1a6005 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -621,7 +621,8 @@
 	header->index = info->bank;
 	header->size = block->sz;
 
-	memcpy(data, registers.hostptr + info->offset, block->sz);
+	memcpy(data, registers.hostptr + info->offset,
+		block->sz * sizeof(unsigned int));
 
 	return SHADER_SECTION_SZ(block->sz);
 }
@@ -767,6 +768,8 @@
 
 	crash_dump_valid = false;
 
+	if (!device->snapshot_crashdumper)
+		return;
 	if (capturescript.gpuaddr == 0 || registers.gpuaddr == 0)
 		return;
 
@@ -872,8 +875,7 @@
 		ARRAY_SIZE(a5xx_vbif_snapshot_registers));
 
 	/* Try to run the crash dumper */
-	if (device->snapshot_crashdumper)
-		_a5xx_do_crashdump(device);
+	_a5xx_do_crashdump(device);
 
 	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
 		snapshot, a5xx_snapshot_registers, NULL);
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 12824b0..0b18a34 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -13,6 +13,7 @@
 #include <linux/firmware.h>
 #include <soc/qcom/subsystem_restart.h>
 #include <linux/pm_opp.h>
+#include <linux/jiffies.h>
 
 #include "adreno.h"
 #include "a6xx_reg.h"
@@ -50,32 +51,38 @@
 	{0, 0},
 };
 
-static const struct adreno_vbif_platform a6xx_vbif_platforms[] = {
-	{ adreno_is_a630, a630_vbif },
+static const struct adreno_vbif_data a615_gbif[] = {
+	{A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x3},
+	{A6XX_UCHE_GBIF_GX_CONFIG, 0x10200F9},
+	{0, 0},
 };
 
+static const struct adreno_vbif_platform a6xx_vbif_platforms[] = {
+	{ adreno_is_a630, a630_vbif },
+	{ adreno_is_a615, a615_gbif },
+};
 
 struct kgsl_hwcg_reg {
 	unsigned int off;
 	unsigned int val;
 };
 static const struct kgsl_hwcg_reg a630_hwcg_regs[] = {
-	{A6XX_RBBM_CLOCK_CNTL_SP0, 0x22222222},
-	{A6XX_RBBM_CLOCK_CNTL_SP1, 0x22222222},
-	{A6XX_RBBM_CLOCK_CNTL_SP2, 0x22222222},
-	{A6XX_RBBM_CLOCK_CNTL_SP3, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL_SP0, 0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL_SP1, 0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL_SP2, 0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL_SP3, 0x02222222},
 	{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02022220},
 	{A6XX_RBBM_CLOCK_CNTL2_SP1, 0x02022220},
 	{A6XX_RBBM_CLOCK_CNTL2_SP2, 0x02022220},
 	{A6XX_RBBM_CLOCK_CNTL2_SP3, 0x02022220},
-	{A6XX_RBBM_CLOCK_DELAY_SP0, 0x0000F3CF},
-	{A6XX_RBBM_CLOCK_DELAY_SP1, 0x0000F3CF},
-	{A6XX_RBBM_CLOCK_DELAY_SP2, 0x0000F3CF},
-	{A6XX_RBBM_CLOCK_DELAY_SP3, 0x0000F3CF},
-	{A6XX_RBBM_CLOCK_HYST_SP0, 0x00000080},
-	{A6XX_RBBM_CLOCK_HYST_SP1, 0x00000080},
-	{A6XX_RBBM_CLOCK_HYST_SP2, 0x00000080},
-	{A6XX_RBBM_CLOCK_HYST_SP3, 0x00000080},
+	{A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000080},
+	{A6XX_RBBM_CLOCK_DELAY_SP1, 0x00000080},
+	{A6XX_RBBM_CLOCK_DELAY_SP2, 0x00000080},
+	{A6XX_RBBM_CLOCK_DELAY_SP3, 0x00000080},
+	{A6XX_RBBM_CLOCK_HYST_SP0, 0x0000F3CF},
+	{A6XX_RBBM_CLOCK_HYST_SP1, 0x0000F3CF},
+	{A6XX_RBBM_CLOCK_HYST_SP2, 0x0000F3CF},
+	{A6XX_RBBM_CLOCK_HYST_SP3, 0x0000F3CF},
 	{A6XX_RBBM_CLOCK_CNTL_TP0, 0x02222222},
 	{A6XX_RBBM_CLOCK_CNTL_TP1, 0x02222222},
 	{A6XX_RBBM_CLOCK_CNTL_TP2, 0x02222222},
@@ -100,10 +107,10 @@
 	{A6XX_RBBM_CLOCK_HYST2_TP1, 0x77777777},
 	{A6XX_RBBM_CLOCK_HYST2_TP2, 0x77777777},
 	{A6XX_RBBM_CLOCK_HYST2_TP3, 0x77777777},
-	{A6XX_RBBM_CLOCK_HYST3_TP0, 0x07777777},
-	{A6XX_RBBM_CLOCK_HYST3_TP1, 0x07777777},
-	{A6XX_RBBM_CLOCK_HYST3_TP2, 0x07777777},
-	{A6XX_RBBM_CLOCK_HYST3_TP3, 0x07777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP0, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP1, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP2, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP3, 0x77777777},
 	{A6XX_RBBM_CLOCK_HYST4_TP0, 0x00077777},
 	{A6XX_RBBM_CLOCK_HYST4_TP1, 0x00077777},
 	{A6XX_RBBM_CLOCK_HYST4_TP2, 0x00077777},
@@ -148,10 +155,76 @@
 	{A6XX_RBBM_CLOCK_HYST_RB_CCU3, 0x00040F00},
 	{A6XX_RBBM_CLOCK_CNTL_RAC, 0x05022022},
 	{A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005555},
-	{A6XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+	{A6XX_RBBM_CLOCK_DELAY_RAC, 0x00000011},
 	{A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
 	{A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222222},
-	{A6XX_RBBM_CLOCK_MODE_GPC, 0x02222222},
+	{A6XX_RBBM_CLOCK_MODE_GPC, 0x00222222},
+	{A6XX_RBBM_CLOCK_MODE_VFD, 0x00002222},
+	{A6XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00000000},
+	{A6XX_RBBM_CLOCK_HYST_GPC, 0x04104004},
+	{A6XX_RBBM_CLOCK_HYST_VFD, 0x00000000},
+	{A6XX_RBBM_CLOCK_DELAY_HLSQ, 0x00000000},
+	{A6XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00004000},
+	{A6XX_RBBM_CLOCK_DELAY_GPC, 0x00000200},
+	{A6XX_RBBM_CLOCK_DELAY_VFD, 0x00002222},
+	{A6XX_RBBM_CLOCK_DELAY_HLSQ_2, 0x00000002},
+	{A6XX_RBBM_CLOCK_MODE_HLSQ, 0x00002222},
+	{A6XX_RBBM_CLOCK_CNTL_GMU_GX, 0x00000222},
+	{A6XX_RBBM_CLOCK_DELAY_GMU_GX, 0x00000111},
+	{A6XX_RBBM_CLOCK_HYST_GMU_GX, 0x00000555}
+};
+
+static const struct kgsl_hwcg_reg a615_hwcg_regs[] = {
+	{A6XX_RBBM_CLOCK_CNTL_SP0,  0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
+	{A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000080},
+	{A6XX_RBBM_CLOCK_HYST_SP0,  0x0000F3CF},
+	{A6XX_RBBM_CLOCK_CNTL_TP0,  0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL_TP1,  0x02222222},
+	{A6XX_RBBM_CLOCK_CNTL2_TP0, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL2_TP1, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL3_TP0, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL3_TP1, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL4_TP0, 0x00022222},
+	{A6XX_RBBM_CLOCK_CNTL4_TP1, 0x00022222},
+	{A6XX_RBBM_CLOCK_HYST_TP0,  0x77777777},
+	{A6XX_RBBM_CLOCK_HYST_TP1,  0x77777777},
+	{A6XX_RBBM_CLOCK_HYST2_TP0, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST2_TP1, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP0, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST3_TP1, 0x77777777},
+	{A6XX_RBBM_CLOCK_HYST4_TP0, 0x00077777},
+	{A6XX_RBBM_CLOCK_HYST4_TP1, 0x00077777},
+	{A6XX_RBBM_CLOCK_DELAY_TP0, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY_TP1, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY2_TP0, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY2_TP1, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY3_TP0, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY3_TP1, 0x11111111},
+	{A6XX_RBBM_CLOCK_DELAY4_TP0, 0x00011111},
+	{A6XX_RBBM_CLOCK_DELAY4_TP1, 0x00011111},
+	{A6XX_RBBM_CLOCK_CNTL_UCHE,  0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL2_UCHE, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL3_UCHE, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL4_UCHE, 0x00222222},
+	{A6XX_RBBM_CLOCK_HYST_UCHE,  0x00000004},
+	{A6XX_RBBM_CLOCK_DELAY_UCHE, 0x00000002},
+	{A6XX_RBBM_CLOCK_CNTL_RB0, 0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL2_RB0, 0x00002222},
+	{A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00002220},
+	{A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00002220},
+	{A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00002220},
+	{A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00002220},
+	{A6XX_RBBM_CLOCK_HYST_RB_CCU0, 0x00040F00},
+	{A6XX_RBBM_CLOCK_HYST_RB_CCU1, 0x00040F00},
+	{A6XX_RBBM_CLOCK_HYST_RB_CCU2, 0x00040F00},
+	{A6XX_RBBM_CLOCK_HYST_RB_CCU3, 0x00040F00},
+	{A6XX_RBBM_CLOCK_CNTL_RAC, 0x05022022},
+	{A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005555},
+	{A6XX_RBBM_CLOCK_DELAY_RAC, 0x00000011},
+	{A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
+	{A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222222},
+	{A6XX_RBBM_CLOCK_MODE_GPC, 0x00222222},
 	{A6XX_RBBM_CLOCK_MODE_VFD, 0x00002222},
 	{A6XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00000000},
 	{A6XX_RBBM_CLOCK_HYST_GPC, 0x04104004},
@@ -173,7 +246,7 @@
 	unsigned int count;
 } a6xx_hwcg_registers[] = {
 	{adreno_is_a630, a630_hwcg_regs, ARRAY_SIZE(a630_hwcg_regs)},
-	{adreno_is_a615, a630_hwcg_regs, ARRAY_SIZE(a630_hwcg_regs)},
+	{adreno_is_a615, a615_hwcg_regs, ARRAY_SIZE(a615_hwcg_regs)},
 };
 
 static struct a6xx_protected_regs {
@@ -210,6 +283,7 @@
 	{ 0xA630, 0x0, 1 },
 };
 
+/* IFPC & Preemption static powerup restore list */
 static struct reg_list_pair {
 	uint32_t offset;
 	uint32_t val;
@@ -244,17 +318,47 @@
 	{ A6XX_RB_CONTEXT_SWITCH_GMEM_SAVE_RESTORE, 0x0 },
 };
 
-static void a6xx_platform_setup(struct adreno_device *adreno_dev)
-{
-	uint64_t addr;
-	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-
-	/* Calculate SP local and private mem addresses */
-	addr = ALIGN(ADRENO_UCHE_GMEM_BASE + adreno_dev->gmem_size, SZ_64K);
-	adreno_dev->sp_local_gpuaddr = addr;
-	adreno_dev->sp_pvt_gpuaddr = addr + SZ_64K;
-	gpudev->vbif_xin_halt_ctrl0_mask = A6XX_VBIF_XIN_HALT_CTRL0_MASK;
-}
+/* IFPC only static powerup restore list */
+static struct reg_list_pair a6xx_ifpc_pwrup_reglist[] = {
+	{ A6XX_RBBM_VBIF_CLIENT_QOS_CNTL, 0x0 },
+	{ A6XX_CP_CHICKEN_DBG, 0x0 },
+	{ A6XX_CP_ADDR_MODE_CNTL, 0x0 },
+	{ A6XX_CP_DBG_ECO_CNTL, 0x0 },
+	{ A6XX_CP_PROTECT_CNTL, 0x0 },
+	{ A6XX_CP_PROTECT_REG, 0x0 },
+	{ A6XX_CP_PROTECT_REG+1, 0x0 },
+	{ A6XX_CP_PROTECT_REG+2, 0x0 },
+	{ A6XX_CP_PROTECT_REG+3, 0x0 },
+	{ A6XX_CP_PROTECT_REG+4, 0x0 },
+	{ A6XX_CP_PROTECT_REG+5, 0x0 },
+	{ A6XX_CP_PROTECT_REG+6, 0x0 },
+	{ A6XX_CP_PROTECT_REG+7, 0x0 },
+	{ A6XX_CP_PROTECT_REG+8, 0x0 },
+	{ A6XX_CP_PROTECT_REG+9, 0x0 },
+	{ A6XX_CP_PROTECT_REG+10, 0x0 },
+	{ A6XX_CP_PROTECT_REG+11, 0x0 },
+	{ A6XX_CP_PROTECT_REG+12, 0x0 },
+	{ A6XX_CP_PROTECT_REG+13, 0x0 },
+	{ A6XX_CP_PROTECT_REG+14, 0x0 },
+	{ A6XX_CP_PROTECT_REG+15, 0x0 },
+	{ A6XX_CP_PROTECT_REG+16, 0x0 },
+	{ A6XX_CP_PROTECT_REG+17, 0x0 },
+	{ A6XX_CP_PROTECT_REG+18, 0x0 },
+	{ A6XX_CP_PROTECT_REG+19, 0x0 },
+	{ A6XX_CP_PROTECT_REG+20, 0x0 },
+	{ A6XX_CP_PROTECT_REG+21, 0x0 },
+	{ A6XX_CP_PROTECT_REG+22, 0x0 },
+	{ A6XX_CP_PROTECT_REG+23, 0x0 },
+	{ A6XX_CP_PROTECT_REG+24, 0x0 },
+	{ A6XX_CP_PROTECT_REG+25, 0x0 },
+	{ A6XX_CP_PROTECT_REG+26, 0x0 },
+	{ A6XX_CP_PROTECT_REG+27, 0x0 },
+	{ A6XX_CP_PROTECT_REG+28, 0x0 },
+	{ A6XX_CP_PROTECT_REG+29, 0x0 },
+	{ A6XX_CP_PROTECT_REG+30, 0x0 },
+	{ A6XX_CP_PROTECT_REG+31, 0x0 },
+	{ A6XX_CP_AHB_CNTL, 0x0 },
+};
 
 static void _update_always_on_regs(struct adreno_device *adreno_dev)
 {
@@ -272,7 +376,7 @@
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 
 	if (kgsl_allocate_global(device, &adreno_dev->pwrup_reglist,
-		PAGE_SIZE, 0, KGSL_MEMDESC_PRIVILEGED,
+		PAGE_SIZE, 0, KGSL_MEMDESC_CONTIG | KGSL_MEMDESC_PRIVILEGED,
 		"powerup_register_list")) {
 		adreno_dev->pwrup_reglist.gpuaddr = 0;
 		return;
@@ -369,14 +473,67 @@
 	kgsl_regwrite(device, A6XX_RBBM_SECVID_TSB_ADDR_MODE_CNTL, 0x1);
 }
 
+static inline unsigned int
+__get_rbbm_clock_cntl_on(struct adreno_device *adreno_dev)
+{
+	if (adreno_is_a615(adreno_dev))
+		return 0x8AA8AA82;
+	else
+		return 0x8AA8AA02;
+}
+
+static inline unsigned int
+__get_gmu_ao_cgc_mode_cntl(struct adreno_device *adreno_dev)
+{
+	if (adreno_is_a615(adreno_dev))
+		return 0x00000222;
+	else
+		return 0x00020222;
+}
+
+static inline unsigned int
+__get_gmu_ao_cgc_delay_cntl(struct adreno_device *adreno_dev)
+{
+	if (adreno_is_a615(adreno_dev))
+		return 0x00000111;
+	else
+		return 0x00010111;
+}
+
+static inline unsigned int
+__get_gmu_ao_cgc_hyst_cntl(struct adreno_device *adreno_dev)
+{
+	if (adreno_is_a615(adreno_dev))
+		return 0x00000555;
+	else
+		return 0x00005555;
+}
 
 static void a6xx_hwcg_set(struct adreno_device *adreno_dev, bool on)
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	const struct kgsl_hwcg_reg *regs;
+	unsigned int value;
 	int i, j;
 
 	if (!test_bit(ADRENO_HWCG_CTRL, &adreno_dev->pwrctrl_flag))
+		on = false;
+
+	if (kgsl_gmu_isenabled(device)) {
+		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL,
+			on ? __get_gmu_ao_cgc_mode_cntl(adreno_dev) : 0);
+		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL,
+			on ? __get_gmu_ao_cgc_delay_cntl(adreno_dev) : 0);
+		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL,
+			on ? __get_gmu_ao_cgc_hyst_cntl(adreno_dev) : 0);
+	}
+
+	kgsl_regread(device, A6XX_RBBM_CLOCK_CNTL, &value);
+
+	if (value == __get_rbbm_clock_cntl_on(adreno_dev) && on)
+		return;
+
+	if (value == 0 && !on)
 		return;
 
 	for (i = 0; i < ARRAY_SIZE(a6xx_hwcg_registers); i++) {
@@ -395,19 +552,12 @@
 	for (j = 0; j < a6xx_hwcg_registers[i].count; j++)
 		kgsl_regwrite(device, regs[j].off, on ? regs[j].val : 0);
 
-	if (kgsl_gmu_isenabled(device)) {
-		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_MODE_CNTL,
-			0x00020222);
-		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_DELAY_CNTL,
-			0x00010111);
-		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_AO_GMU_CGC_HYST_CNTL,
-			0x00050555);
-	}
 	/* Enable SP clock */
 	kgsl_gmu_regrmw(device, A6XX_GPU_GMU_GX_SPTPRAC_CLOCK_CONTROL, 0, 1);
 
 	/* enable top level HWCG */
-	kgsl_regwrite(device, A6XX_RBBM_CLOCK_CNTL, on ? 0x8AA8AA02 : 0);
+	kgsl_regwrite(device, A6XX_RBBM_CLOCK_CNTL,
+		on ? __get_rbbm_clock_cntl_on(adreno_dev) : 0);
 }
 
 #define LM_DEFAULT_LIMIT	6000
@@ -429,17 +579,46 @@
 static void a6xx_patch_pwrup_reglist(struct adreno_device *adreno_dev)
 {
 	uint32_t i;
+	struct cpu_gpu_lock *lock;
+	struct reg_list_pair *r;
 
 	/* Set up the register values */
-	for (i = 0; i < ARRAY_SIZE(a6xx_pwrup_reglist); i++) {
-		struct reg_list_pair *r = &a6xx_pwrup_reglist[i];
-
+	for (i = 0; i < ARRAY_SIZE(a6xx_ifpc_pwrup_reglist); i++) {
+		r = &a6xx_ifpc_pwrup_reglist[i];
 		kgsl_regread(KGSL_DEVICE(adreno_dev), r->offset, &r->val);
 	}
 
-	/* Copy Preemption register/data pairs */
-	memcpy(adreno_dev->pwrup_reglist.hostptr, &a6xx_pwrup_reglist,
-		sizeof(a6xx_pwrup_reglist));
+	for (i = 0; i < ARRAY_SIZE(a6xx_pwrup_reglist); i++) {
+		r = &a6xx_pwrup_reglist[i];
+		kgsl_regread(KGSL_DEVICE(adreno_dev), r->offset, &r->val);
+	}
+
+	lock = (struct cpu_gpu_lock *) adreno_dev->pwrup_reglist.hostptr;
+	lock->flag_ucode = 0;
+	lock->flag_kmd = 0;
+	lock->turn = 0;
+
+	/*
+	 * The overall register list is composed of
+	 * 1. Static IFPC-only registers
+	 * 2. Static IFPC + preemption registers
+	 * 2. Dynamic IFPC + preemption registers (ex: perfcounter selects)
+	 *
+	 * The CP views the second and third entries as one dynamic list
+	 * starting from list_offset. Thus, list_length should be the sum
+	 * of all three lists above (of which the third list will start off
+	 * empty). And list_offset should be specified as the size in dwords
+	 * of the static IFPC-only register list.
+	 */
+	lock->list_length = (sizeof(a6xx_ifpc_pwrup_reglist) +
+			sizeof(a6xx_pwrup_reglist)) >> 2;
+	lock->list_offset = sizeof(a6xx_ifpc_pwrup_reglist) >> 2;
+
+	memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock),
+		a6xx_ifpc_pwrup_reglist, sizeof(a6xx_ifpc_pwrup_reglist));
+	memcpy(adreno_dev->pwrup_reglist.hostptr + sizeof(*lock)
+		+ sizeof(a6xx_ifpc_pwrup_reglist),
+		a6xx_pwrup_reglist, sizeof(a6xx_pwrup_reglist));
 }
 
 /*
@@ -646,13 +825,16 @@
 /* Register initialization list */
 #define CP_INIT_REGISTER_INIT_LIST BIT(7)
 
+/* Register initialization list with spinlock */
+#define CP_INIT_REGISTER_INIT_LIST_WITH_SPINLOCK BIT(8)
+
 #define CP_INIT_MASK (CP_INIT_MAX_CONTEXT | \
 		CP_INIT_ERROR_DETECTION_CONTROL | \
 		CP_INIT_HEADER_DUMP | \
 		CP_INIT_DEFAULT_RESET_STATE | \
 		CP_INIT_UCODE_WORKAROUND_MASK | \
 		CP_INIT_OPERATION_MODE_MASK | \
-		CP_INIT_REGISTER_INIT_LIST)
+		CP_INIT_REGISTER_INIT_LIST_WITH_SPINLOCK)
 
 static void _set_ordinals(struct adreno_device *adreno_dev,
 		unsigned int *cmds, unsigned int count)
@@ -688,13 +870,21 @@
 	if (CP_INIT_MASK & CP_INIT_OPERATION_MODE_MASK)
 		*cmds++ = 0x00000002;
 
-	if (CP_INIT_MASK & CP_INIT_REGISTER_INIT_LIST) {
+	if (CP_INIT_MASK & CP_INIT_REGISTER_INIT_LIST_WITH_SPINLOCK) {
+		uint64_t gpuaddr = adreno_dev->pwrup_reglist.gpuaddr;
+
+		*cmds++ = lower_32_bits(gpuaddr);
+		*cmds++ = upper_32_bits(gpuaddr);
+		*cmds++ =  0;
+
+	} else if (CP_INIT_MASK & CP_INIT_REGISTER_INIT_LIST) {
 		uint64_t gpuaddr = adreno_dev->pwrup_reglist.gpuaddr;
 
 		*cmds++ = lower_32_bits(gpuaddr);
 		*cmds++ = upper_32_bits(gpuaddr);
 		/* Size is in dwords */
-		*cmds++ = sizeof(a6xx_pwrup_reglist) >> 2;
+		*cmds++ = (sizeof(a6xx_ifpc_pwrup_reglist) +
+			sizeof(a6xx_pwrup_reglist)) >> 2;
 	}
 
 	/* Pad rest of the cmds with 0's */
@@ -751,7 +941,8 @@
 			rb->preemption_desc.gpuaddr);
 
 	*cmds++ = 2;
-	cmds += cp_gpuaddr(adreno_dev, cmds, 0);
+	cmds += cp_gpuaddr(adreno_dev, cmds,
+			rb->secure_preemption_desc.gpuaddr);
 
 	/* Turn CP protection ON */
 	*cmds++ = cp_type7_packet(CP_SET_PROTECTED_MODE, 1);
@@ -842,6 +1033,38 @@
 	return a6xx_post_start(adreno_dev);
 }
 
+unsigned int a6xx_set_marker(
+		unsigned int *cmds, enum adreno_cp_marker_type type)
+{
+	unsigned int cmd = 0;
+
+	*cmds++ = cp_type7_packet(CP_SET_MARKER, 1);
+
+	/*
+	 * Indicate the beginning and end of the IB1 list with a SET_MARKER.
+	 * Among other things, this will implicitly enable and disable
+	 * preemption respectively. IFPC can also be disabled and enabled
+	 * with a SET_MARKER. Bit 8 tells the CP the marker is for IFPC.
+	 */
+	switch (type) {
+	case IFPC_DISABLE:
+		cmd = 0x101;
+		break;
+	case IFPC_ENABLE:
+		cmd = 0x100;
+		break;
+	case IB1LIST_START:
+		cmd = 0xD;
+		break;
+	case IB1LIST_END:
+		cmd = 0xE;
+		break;
+	}
+
+	*cmds++ = cmd;
+	return 2;
+}
+
 static int _load_firmware(struct kgsl_device *device, const char *fwfile,
 			  struct adreno_firmware *firmware)
 {
@@ -888,8 +1111,12 @@
  */
 static void _load_gmu_rpmh_ucode(struct kgsl_device *device)
 {
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct gmu_device *gmu = &device->gmu;
 
+	/* Disable SDE clock gating */
+	kgsl_gmu_regwrite(device, A6XX_GPU_RSCC_RSC_STATUS0_DRV0, BIT(24));
+
 	/* Setup RSC PDC handshake for sleep and wakeup */
 	kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_SLAVE_ID_DRV0, 1);
 	kgsl_gmu_regwrite(device, A6XX_RSCC_HIDDEN_TCS_CMD0_DATA, 0);
@@ -909,8 +1136,9 @@
 	kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_LO, 0x4510);
 	kgsl_gmu_regwrite(device, A6XX_RSCC_PDC_MATCH_VALUE_HI, 0x4514);
 
-	/* Enable timestamp event */
-	kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
+	/* Enable timestamp event for v1 only */
+	if (adreno_is_a630v1(adreno_dev))
+		kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
 
 	/* Load RSC sequencer uCode for sleep and wakeup */
 	kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0, 0xA7A506A0);
@@ -920,49 +1148,49 @@
 	kgsl_gmu_regwrite(device, A6XX_RSCC_SEQ_MEM_0_DRV0 + 4, 0x0020E8A8);
 
 	/* Load PDC sequencer uCode for power up and power down sequence */
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0, 0xFFBFA1E1);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 1, 0xE0A4A3A2);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 2, 0xE2848382);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 3, 0xFDBDE4E3);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 4, 0x00002081);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0, 0xFEBEA1E1);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 1, 0xA5A4A3A2);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 2, 0x8382A6E0);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 3, 0xBCE3E284);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_MEM_0 + 4, 0x002081FC);
 
 	/* Set TCS commands used by PDC sequence for low power modes */
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD_ENABLE_BANK, 7);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD_WAIT_FOR_CMPL_BANK, 0);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CONTROL, 0);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_MSGID, 0x10108);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_ADDR, 0x30010);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS0_CMD0_DATA, 1);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_DATA + PDC_CMD_OFFSET, 0x0);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
-	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS0_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0);
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_ENABLE_BANK, 7);
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD_WAIT_FOR_CMPL_BANK, 0);
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CONTROL, 0);
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_MSGID, 0x10108);
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_ADDR, 0x30010);
-	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 2);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS1_CMD0_DATA, 1);
 	_regwrite(gmu->pdc_reg_virt,
 			PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
 	_regwrite(gmu->pdc_reg_virt,
 			PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
 	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x3);
+			PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET, 0x0);
 	_regwrite(gmu->pdc_reg_virt,
 			PDC_GPU_TCS1_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
 	_regwrite(gmu->pdc_reg_virt,
 			PDC_GPU_TCS1_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
 	_regwrite(gmu->pdc_reg_virt,
-			PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3);
+			PDC_GPU_TCS1_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x0);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_ENABLE_BANK, 7);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD_WAIT_FOR_CMPL_BANK, 0);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CONTROL, 0);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_MSGID, 0x10108);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_ADDR, 0x30010);
+	_regwrite(gmu->pdc_reg_virt, PDC_GPU_TCS3_CMD0_DATA, 2);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET, 0x10108);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET, 0x30000);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET, 0x3);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_MSGID + PDC_CMD_OFFSET * 2, 0x10108);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_ADDR + PDC_CMD_OFFSET * 2, 0x30080);
+	_regwrite(gmu->pdc_reg_virt,
+			PDC_GPU_TCS3_CMD0_DATA + PDC_CMD_OFFSET * 2, 0x3);
 
 	/* Setup GPU PDC */
 	_regwrite(gmu->pdc_reg_virt, PDC_GPU_SEQ_START_ADDR, 0);
@@ -1003,14 +1231,27 @@
 		if ((value & mask) == expected_ret)
 			return 0;
 		/* Wait 100us to reduce unnecessary AHB bus traffic */
-		udelay(100);
-		cond_resched();
+		usleep_range(10, 100);
 	} while (!time_after(jiffies, t));
 
+	/* Double check one last time */
+	kgsl_gmu_regread(device, offset, &value);
+	if ((value & mask) == expected_ret)
+		return 0;
+
 	return -EINVAL;
 }
 
 /*
+ * The lowest 16 bits of this value are the number of XO clock cycles
+ * for main hysteresis. This is the first hysteresis. Here we set it
+ * to 0x1680 cycles, or 300 us. The highest 16 bits of this value are
+ * the number of XO clock cycles for short hysteresis. This happens
+ * after main hysteresis. Here we set it to 0xA cycles, or 0.5 us.
+ */
+#define GMU_PWR_COL_HYST 0x000A1680
+
+/*
  * a6xx_gmu_power_config() - Configure and enable GMU's low power mode
  * setting based on ADRENO feature flags.
  * @device: Pointer to KGSL device
@@ -1041,13 +1282,13 @@
 		/* fall through */
 	case GPU_HW_IFPC:
 		kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_INTER_FRAME_HYST,
-				0x000A0080);
+				GMU_PWR_COL_HYST);
 		kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0,
 				IFPC_ENABLE_MASK);
 		/* fall through */
 	case GPU_HW_SPTP_PC:
 		kgsl_gmu_regwrite(device, A6XX_GMU_PWR_COL_SPTPRAC_HYST,
-				0x000A0080);
+				GMU_PWR_COL_HYST);
 		kgsl_gmu_regrmw(device, A6XX_GMU_PWR_COL_INTER_FRAME_CTRL, 0,
 				SPTP_ENABLE_MASK);
 		/* fall through */
@@ -1136,7 +1377,6 @@
 		unsigned int clear_mask)
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
-	struct gmu_device *gmu = &device->gmu;
 	int ret = 0;
 
 	if (!kgsl_gmu_isenabled(device))
@@ -1150,9 +1390,7 @@
 			GPU_START_TIMEOUT,
 			check_mask)) {
 		ret = -ETIMEDOUT;
-		dev_err(&gmu->pdev->dev,
-			"OOB set timed out, mask %x\n", set_mask);
-		WARN_ON(true);
+		WARN(1, "OOB set timed out, mask %x\n", set_mask);
 	}
 
 	kgsl_gmu_regwrite(device, A6XX_GMU_GMU2HOST_INTR_CLR, clear_mask);
@@ -1266,21 +1504,12 @@
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	unsigned int val;
-	bool state;
 
 	if (!kgsl_gmu_isenabled(device))
 		return true;
 
 	kgsl_gmu_regread(device, A6XX_GMU_SPTPRAC_PWR_CLK_STATUS, &val);
-	state = !(val & (GX_GDSC_POWER_OFF | GX_CLK_OFF));
-
-	/* If GMU is holding on to the fence then we cannot dump any GX stuff */
-	kgsl_gmu_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val);
-	if (val)
-		return false;
-
-	return state;
-
+	return !(val & (GX_GDSC_POWER_OFF | GX_CLK_OFF));
 }
 
 /*
@@ -1351,12 +1580,13 @@
 	/* Disable the power counter so that the GMU is not busy */
 	kgsl_gmu_regwrite(device, A6XX_GMU_CX_GMU_POWER_COUNTER_ENABLE, 0);
 
-	/* Turn off SPTPRAC before GMU turns off GX */
-	a6xx_sptprac_disable(adreno_dev);
+	/* Turn off SPTPRAC if we own it */
+	if (gmu->idle_level < GPU_HW_SPTP_PC)
+		a6xx_sptprac_disable(adreno_dev);
 
 	if (!ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) {
 		ret = hfi_notify_slumber(gmu, perf_idx, bus_level);
-		return ret;
+		goto out;
 	}
 
 	kgsl_gmu_regwrite(device, A6XX_GMU_BOOT_SLUMBER_OPTION,
@@ -1382,6 +1612,9 @@
 		}
 	}
 
+out:
+	/* Make sure the fence is in ALLOW mode */
+	kgsl_gmu_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
 	return ret;
 }
 
@@ -1392,7 +1625,9 @@
 	int val;
 
 	kgsl_gmu_regread(device, A6XX_GPU_CC_GX_DOMAIN_MISC, &val);
-	WARN_ON(!(val & 0x1));
+	if (!(val & 0x1))
+		dev_err_ratelimited(&gmu->pdev->dev,
+			"GMEM CLAMP IO not set while GFX rail off\n");
 
 	/* RSC wake sequence */
 	kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, BIT(1));
@@ -1430,36 +1665,50 @@
 static int a6xx_rpmh_power_off_gpu(struct kgsl_device *device)
 {
 	struct gmu_device *gmu = &device->gmu;
-	const struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-	int val;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	int ret;
 
-	/* RSC sleep sequence */
-	kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
+	/* RSC sleep sequence is different on v1 */
+	if (adreno_is_a630v1(adreno_dev))
+		kgsl_gmu_regwrite(device, A6XX_RSCC_TIMESTAMP_UNIT1_EN_DRV0, 1);
+
 	kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 1);
 	wmb();
 
-	if (timed_poll_check(device,
-			A6XX_RSCC_TIMESTAMP_UNIT1_OUTPUT_DRV0,
-			BIT(0),
-			GPU_START_TIMEOUT,
-			BIT(0))) {
+	if (adreno_is_a630v1(adreno_dev))
+		ret = timed_poll_check(device,
+				A6XX_RSCC_TIMESTAMP_UNIT1_OUTPUT_DRV0,
+				BIT(0),
+				GPU_START_TIMEOUT,
+				BIT(0));
+	else
+		ret = timed_poll_check(device,
+				A6XX_GPU_RSCC_RSC_STATUS0_DRV0,
+				BIT(16),
+				GPU_START_TIMEOUT,
+				BIT(16));
+
+	if (ret) {
 		dev_err(&gmu->pdev->dev, "GPU RSC power off fail\n");
-		return -EINVAL;
+		return -ETIMEDOUT;
 	}
 
-	/* Read to clear the timestamp */
-	kgsl_gmu_regread(device, A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_L_DRV0,
-			&val);
-	kgsl_gmu_regread(device, A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_H_DRV0,
-			&val);
+	/* Read to clear the timestamp valid signal. Don't care what we read. */
+	if (adreno_is_a630v1(adreno_dev)) {
+		kgsl_gmu_regread(device,
+				A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_L_DRV0,
+				&ret);
+		kgsl_gmu_regread(device,
+				A6XX_RSCC_TIMESTAMP_UNIT0_TIMESTAMP_H_DRV0,
+				&ret);
+	}
+
 	kgsl_gmu_regwrite(device, A6XX_GMU_RSCC_CONTROL_REQ, 0);
 
 	if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) &&
-		test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag))
+			test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag))
 		kgsl_gmu_regwrite(device, A6XX_GMU_AO_SPARE_CNTL, 0);
 
-	/* FIXME: v2 has different procedure to trigger sequence */
-
 	return 0;
 }
 
@@ -1535,6 +1784,8 @@
 	kgsl_gmu_regwrite(device, A6XX_GPU_CS_AMP_CALIBRATION_DONE, 1);
 
 }
+
+#define GPU_LIMIT_THRESHOLD_ENABLE	BIT(31)
 /*
  * a6xx_gmu_fw_start() - set up GMU and start FW
  * @device: Pointer to KGSL device
@@ -1618,7 +1869,7 @@
 	if (ADRENO_FEATURE(adreno_dev, ADRENO_LM) &&
 		test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag)) {
 		kgsl_gmu_regwrite(device, A6XX_GPU_GMU_CX_GMU_PWR_THRESHOLD,
-			lm_limit(adreno_dev));
+			GPU_LIMIT_THRESHOLD_ENABLE | lm_limit(adreno_dev));
 		isense_cold_trimm(device);
 	}
 
@@ -1715,6 +1966,51 @@
 	return true;
 }
 
+static int a6xx_wait_for_lowest_idle(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+	unsigned int reg;
+	unsigned long t;
+
+	if (!kgsl_gmu_isenabled(device))
+		return 0;
+
+	t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT);
+	while (!time_after(jiffies, t)) {
+		adreno_read_gmureg(ADRENO_DEVICE(device),
+				ADRENO_REG_GMU_RPMH_POWER_STATE, &reg);
+
+		/* SPTPRAC PC has the same idle level as IFPC */
+		if ((reg == gmu->idle_level) ||
+				(gmu->idle_level == GPU_HW_SPTP_PC &&
+				reg == GPU_HW_IFPC)) {
+			/* IFPC is not complete until GX is off */
+			if (gmu->idle_level != GPU_HW_IFPC ||
+					!gpudev->gx_is_on(adreno_dev))
+				return 0;
+		}
+
+		/* Wait 100us to reduce unnecessary AHB bus traffic */
+		usleep_range(10, 100);
+	}
+
+	/* Check one last time */
+	adreno_read_gmureg(ADRENO_DEVICE(device),
+			ADRENO_REG_GMU_RPMH_POWER_STATE, &reg);
+	if ((reg == gmu->idle_level) ||
+			(gmu->idle_level == GPU_HW_SPTP_PC &&
+			reg == GPU_HW_IFPC)) {
+		if (gmu->idle_level != GPU_HW_IFPC ||
+				!gpudev->gx_is_on(adreno_dev))
+			return 0;
+	}
+
+	WARN(1, "Timeout waiting for lowest idle level: %d\n", reg);
+	return -ETIMEDOUT;
+}
+
 static int a6xx_wait_for_gmu_idle(struct adreno_device *adreno_dev)
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -1943,9 +2239,7 @@
 	/* If SPTP_RAC is on, turn off SPTP_RAC HS */
 	a6xx_sptprac_disable(adreno_dev);
 
-	/* Disconnect GPU from BUS. Clear and reconnected after reset */
-	adreno_vbif_clear_pending_transactions(device);
-	/* Unnecessary: a6xx_soft_reset(adreno_dev); */
+	/* Disconnect GPU from BUS is not needed if CX GDSC goes off later */
 
 	/* Check no outstanding RPMh voting */
 	a6xx_complete_rpmh_votes(device);
@@ -2687,6 +2981,27 @@
 		A6XX_VBIF_PERF_PWR_CNT_HIGH2, -1, A6XX_VBIF_PERF_PWR_CNT_EN2 },
 };
 
+
+static struct adreno_perfcount_register a6xx_perfcounters_gbif[] = {
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PERF_CNT_LOW0,
+		A6XX_GBIF_PERF_CNT_HIGH0, -1, A6XX_GBIF_PERF_CNT_SEL },
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PERF_CNT_LOW1,
+		A6XX_GBIF_PERF_CNT_HIGH1, -1, A6XX_GBIF_PERF_CNT_SEL },
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PERF_CNT_LOW2,
+		A6XX_GBIF_PERF_CNT_HIGH2, -1, A6XX_GBIF_PERF_CNT_SEL },
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PERF_CNT_LOW3,
+		A6XX_GBIF_PERF_CNT_HIGH3, -1, A6XX_GBIF_PERF_CNT_SEL },
+};
+
+static struct adreno_perfcount_register a6xx_perfcounters_gbif_pwr[] = {
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PWR_CNT_LOW0,
+		A6XX_GBIF_PWR_CNT_HIGH0, -1, A6XX_GBIF_PERF_PWR_CNT_EN },
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PWR_CNT_LOW1,
+		A6XX_GBIF_PWR_CNT_HIGH1, -1, A6XX_GBIF_PERF_PWR_CNT_EN },
+	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0, A6XX_GBIF_PWR_CNT_LOW2,
+		A6XX_GBIF_PWR_CNT_HIGH2, -1, A6XX_GBIF_PERF_PWR_CNT_EN },
+};
+
 static struct adreno_perfcount_register a6xx_perfcounters_pwr[] = {
 	{ KGSL_PERFCOUNTER_BROKEN, 0, 0, 0, 0, -1, 0 },
 	{ KGSL_PERFCOUNTER_NOT_USED, 0, 0,
@@ -2731,8 +3046,16 @@
 		A6XX_GMU_CX_GMU_POWER_COUNTER_SELECT_1, },
 };
 
+/*
+ * ADRENO_PERFCOUNTER_GROUP_RESTORE flag is enabled by default
+ * because most of the perfcounter groups need to be restored
+ * as part of preemption and IFPC. Perfcounter groups that are
+ * not restored as part of preemption and IFPC should be defined
+ * using A6XX_PERFCOUNTER_GROUP_FLAGS macro
+ */
 #define A6XX_PERFCOUNTER_GROUP(offset, name) \
-	ADRENO_PERFCOUNTER_GROUP(a6xx, offset, name)
+	ADRENO_PERFCOUNTER_GROUP_FLAGS(a6xx, offset, name, \
+	ADRENO_PERFCOUNTER_GROUP_RESTORE)
 
 #define A6XX_PERFCOUNTER_GROUP_FLAGS(offset, name, flags) \
 	ADRENO_PERFCOUNTER_GROUP_FLAGS(a6xx, offset, name, flags)
@@ -2743,7 +3066,7 @@
 static struct adreno_perfcount_group a6xx_perfcounter_groups
 				[KGSL_PERFCOUNTER_GROUP_MAX] = {
 	A6XX_PERFCOUNTER_GROUP(CP, cp),
-	A6XX_PERFCOUNTER_GROUP(RBBM, rbbm),
+	A6XX_PERFCOUNTER_GROUP_FLAGS(RBBM, rbbm, 0),
 	A6XX_PERFCOUNTER_GROUP(PC, pc),
 	A6XX_PERFCOUNTER_GROUP(VFD, vfd),
 	A6XX_PERFCOUNTER_GROUP(HLSQ, hlsq),
@@ -2758,7 +3081,7 @@
 	A6XX_PERFCOUNTER_GROUP(SP, sp),
 	A6XX_PERFCOUNTER_GROUP(RB, rb),
 	A6XX_PERFCOUNTER_GROUP(VSC, vsc),
-	A6XX_PERFCOUNTER_GROUP(VBIF, vbif),
+	A6XX_PERFCOUNTER_GROUP_FLAGS(VBIF, vbif, 0),
 	A6XX_PERFCOUNTER_GROUP_FLAGS(VBIF_PWR, vbif_pwr,
 		ADRENO_PERFCOUNTER_GROUP_FIXED),
 	A6XX_PERFCOUNTER_GROUP_FLAGS(PWR, pwr,
@@ -2797,6 +3120,88 @@
 	return 0;
 }
 
+static void a6xx_efuse_speed_bin(struct adreno_device *adreno_dev)
+{
+	unsigned int val;
+	unsigned int speed_bin[3];
+	struct kgsl_device *device = &adreno_dev->dev;
+
+	if (of_property_read_u32_array(device->pdev->dev.of_node,
+		"qcom,gpu-speed-bin", speed_bin, 3))
+		return;
+
+	adreno_efuse_read_u32(adreno_dev, speed_bin[0], &val);
+
+	adreno_dev->speed_bin = (val & speed_bin[1]) >> speed_bin[2];
+}
+
+static const struct {
+	int (*check)(struct adreno_device *adreno_dev);
+	void (*func)(struct adreno_device *adreno_dev);
+} a6xx_efuse_funcs[] = {
+	{ adreno_is_a615, a6xx_efuse_speed_bin },
+};
+
+static void a6xx_check_features(struct adreno_device *adreno_dev)
+{
+	unsigned int i;
+
+	if (adreno_efuse_map(adreno_dev))
+		return;
+	for (i = 0; i < ARRAY_SIZE(a6xx_efuse_funcs); i++) {
+		if (a6xx_efuse_funcs[i].check(adreno_dev))
+			a6xx_efuse_funcs[i].func(adreno_dev);
+	}
+
+	adreno_efuse_unmap(adreno_dev);
+}
+static void a6xx_platform_setup(struct adreno_device *adreno_dev)
+{
+	uint64_t addr;
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+
+	/* Calculate SP local and private mem addresses */
+	addr = ALIGN(ADRENO_UCHE_GMEM_BASE + adreno_dev->gmem_size, SZ_64K);
+	adreno_dev->sp_local_gpuaddr = addr;
+	adreno_dev->sp_pvt_gpuaddr = addr + SZ_64K;
+
+	if (adreno_has_gbif(adreno_dev)) {
+		a6xx_perfcounter_groups[KGSL_PERFCOUNTER_GROUP_VBIF].regs =
+				a6xx_perfcounters_gbif;
+		a6xx_perfcounter_groups[KGSL_PERFCOUNTER_GROUP_VBIF].reg_count
+				= ARRAY_SIZE(a6xx_perfcounters_gbif);
+
+		a6xx_perfcounter_groups[KGSL_PERFCOUNTER_GROUP_VBIF_PWR].regs =
+				a6xx_perfcounters_gbif_pwr;
+		a6xx_perfcounter_groups[
+			KGSL_PERFCOUNTER_GROUP_VBIF_PWR].reg_count
+				= ARRAY_SIZE(a6xx_perfcounters_gbif_pwr);
+
+		gpudev->vbif_xin_halt_ctrl0_mask =
+				A6XX_GBIF_HALT_MASK;
+	} else
+		gpudev->vbif_xin_halt_ctrl0_mask =
+				A6XX_VBIF_XIN_HALT_CTRL0_MASK;
+
+	/* Check efuse bits for various capabilties */
+	a6xx_check_features(adreno_dev);
+}
+
+
+static unsigned int a6xx_ccu_invalidate(struct adreno_device *adreno_dev,
+	unsigned int *cmds)
+{
+	/* CCU_INVALIDATE_DEPTH */
+	*cmds++ = cp_packet(adreno_dev, CP_EVENT_WRITE, 1);
+	*cmds++ = 24;
+
+	/* CCU_INVALIDATE_COLOR */
+	*cmds++ = cp_packet(adreno_dev, CP_EVENT_WRITE, 1);
+	*cmds++ = 25;
+
+	return 4;
+}
+
 /* Register offset defines for A6XX, in order of enum adreno_regs */
 static unsigned int a6xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
 
@@ -2825,6 +3230,22 @@
 			A6XX_CP_CONTEXT_SWITCH_SMMU_INFO_LO),
 	ADRENO_REG_DEFINE(ADRENO_REG_CP_CONTEXT_SWITCH_SMMU_INFO_HI,
 			A6XX_CP_CONTEXT_SWITCH_SMMU_INFO_HI),
+	ADRENO_REG_DEFINE(
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_LO,
+			A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_LO),
+	ADRENO_REG_DEFINE(
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_HI,
+			A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_HI),
+	ADRENO_REG_DEFINE(
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_LO,
+			A6XX_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_LO),
+	ADRENO_REG_DEFINE(
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_HI,
+			A6XX_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_HI),
+	ADRENO_REG_DEFINE(ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_LO,
+			A6XX_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_LO),
+	ADRENO_REG_DEFINE(ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_HI,
+			A6XX_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_HI),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS, A6XX_RBBM_STATUS),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_STATUS3, A6XX_RBBM_STATUS3),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_PERFCTR_CTL, A6XX_RBBM_PERFCTR_CNTL),
@@ -2856,10 +3277,14 @@
 				A6XX_VBIF_XIN_HALT_CTRL0),
 	ADRENO_REG_DEFINE(ADRENO_REG_VBIF_XIN_HALT_CTRL1,
 				A6XX_VBIF_XIN_HALT_CTRL1),
+	ADRENO_REG_DEFINE(ADRENO_REG_GBIF_HALT, A6XX_GBIF_HALT),
+	ADRENO_REG_DEFINE(ADRENO_REG_GBIF_HALT_ACK, A6XX_GBIF_HALT_ACK),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_ALWAYSON_COUNTER_LO,
 				A6XX_GMU_ALWAYS_ON_COUNTER_L),
 	ADRENO_REG_DEFINE(ADRENO_REG_RBBM_ALWAYSON_COUNTER_HI,
 				A6XX_GMU_ALWAYS_ON_COUNTER_H),
+	ADRENO_REG_DEFINE(ADRENO_REG_GMU_AO_AHB_FENCE_CTRL,
+				A6XX_GMU_AO_AHB_FENCE_CTRL),
 	ADRENO_REG_DEFINE(ADRENO_REG_GMU_AO_INTERRUPT_EN,
 				A6XX_GMU_AO_INTERRUPT_EN),
 	ADRENO_REG_DEFINE(ADRENO_REG_GMU_AO_HOST_INTERRUPT_CLR,
@@ -2913,10 +3338,74 @@
 	.offset_0 = ADRENO_REG_REGISTER_MAX,
 };
 
+static int a6xx_perfcounter_update(struct adreno_device *adreno_dev,
+	struct adreno_perfcount_register *reg, bool update_reg)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct cpu_gpu_lock *lock = adreno_dev->pwrup_reglist.hostptr;
+	struct reg_list_pair *reg_pair = (struct reg_list_pair *)(lock + 1);
+	unsigned int i;
+	unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+	int ret = 0;
+
+	lock->flag_kmd = 1;
+	/* Write flag_kmd before turn */
+	wmb();
+	lock->turn = 0;
+	/* Write these fields before looping */
+	mb();
+
+	/*
+	 * Spin here while GPU ucode holds the lock, lock->flag_ucode will
+	 * be set to 0 after GPU ucode releases the lock. Minimum wait time
+	 * is 1 second and this should be enough for GPU to release the lock
+	 */
+	while (lock->flag_ucode == 1 && lock->turn == 0) {
+		cpu_relax();
+		/* Get the latest updates from GPU */
+		rmb();
+		/*
+		 * Make sure we wait at least 1sec for the lock,
+		 * if we did not get it after 1sec return an error.
+		 */
+		if (time_after(jiffies, timeout) &&
+			(lock->flag_ucode == 1 && lock->turn == 0)) {
+			ret = -EBUSY;
+			goto unlock;
+		}
+	}
+
+	/* Read flag_ucode and turn before list_length */
+	rmb();
+	/*
+	 * If the perfcounter select register is already present in reglist
+	 * update it, otherwise append the <select register, value> pair to
+	 * the end of the list.
+	 */
+	for (i = 0; i < lock->list_length >> 1; i++)
+		if (reg_pair[i].offset == reg->select)
+			break;
+
+	reg_pair[i].offset = reg->select;
+	reg_pair[i].val = reg->countable;
+	if (i == lock->list_length >> 1)
+		lock->list_length += 2;
+
+	if (update_reg)
+		kgsl_regwrite(device, reg->select, reg->countable);
+
+unlock:
+	/* All writes done before releasing the lock */
+	wmb();
+	lock->flag_kmd = 0;
+	return ret;
+}
+
 struct adreno_gpudev adreno_a6xx_gpudev = {
 	.reg_offsets = &a6xx_reg_offsets,
 	.start = a6xx_start,
 	.snapshot = a6xx_snapshot,
+	.snapshot_gmu = a6xx_snapshot_gmu,
 	.irq = &a6xx_irq,
 	.snapshot_data = &a6xx_snapshot_data,
 	.irq_trace = trace_kgsl_a5xx_irq_status,
@@ -2939,6 +3428,7 @@
 	.gpu_keepalive = a6xx_gpu_keepalive,
 	.rpmh_gpu_pwrctrl = a6xx_rpmh_gpu_pwrctrl,
 	.hw_isidle = a6xx_hw_isidle, /* Replaced by NULL if GMU is disabled */
+	.wait_for_lowest_idle = a6xx_wait_for_lowest_idle,
 	.wait_for_gmu_idle = a6xx_wait_for_gmu_idle,
 	.iommu_fault_block = a6xx_iommu_fault_block,
 	.reset = a6xx_reset,
@@ -2952,4 +3442,6 @@
 	.preemption_context_destroy = a6xx_preemption_context_destroy,
 	.gx_is_on = a6xx_gx_is_on,
 	.sptprac_is_on = a6xx_sptprac_is_on,
+	.ccu_invalidate = a6xx_ccu_invalidate,
+	.perfcounter_update = a6xx_perfcounter_update,
 };
diff --git a/drivers/gpu/msm/adreno_a6xx.h b/drivers/gpu/msm/adreno_a6xx.h
index ee2fd71..bf1111c 100644
--- a/drivers/gpu/msm/adreno_a6xx.h
+++ b/drivers/gpu/msm/adreno_a6xx.h
@@ -75,6 +75,24 @@
 
 #define A6XX_CP_SMMU_INFO_MAGIC_REF     0x241350D5UL
 
+/**
+ * struct cpu_gpu_spinlock - CP spinlock structure for power up list
+ * @flag_ucode: flag value set by CP
+ * @flag_kmd: flag value set by KMD
+ * @turn: turn variable set by both CP and KMD
+ * @list_length: this tells CP the last dword in the list:
+ * 16 + (4 * (List_Length - 1))
+ * @list_offset: this tells CP the start of preemption only list:
+ * 16 + (4 * List_Offset)
+ */
+struct cpu_gpu_lock {
+	uint32_t flag_ucode;
+	uint32_t flag_kmd;
+	uint32_t turn;
+	uint16_t list_length;
+	uint16_t list_offset;
+};
+
 #define A6XX_CP_CTXRECORD_MAGIC_REF     0xAE399D6EUL
 /* Size of each CP preemption record */
 #define A6XX_CP_CTXRECORD_SIZE_IN_BYTES     (2112 * 1024)
@@ -100,7 +118,8 @@
 		struct adreno_ringbuffer *rb,
 		unsigned int *cmds, struct kgsl_context *context);
 
-unsigned int a6xx_set_marker(unsigned int *cmds, int start);
+unsigned int a6xx_set_marker(unsigned int *cmds,
+		enum adreno_cp_marker_type type);
 
 void a6xx_preemption_callback(struct adreno_device *adreno_dev, int bit);
 
@@ -110,6 +129,8 @@
 
 void a6xx_snapshot(struct adreno_device *adreno_dev,
 		struct kgsl_snapshot *snapshot);
+void a6xx_snapshot_gmu(struct adreno_device *adreno_dev,
+		struct kgsl_snapshot *snapshot);
 
 void a6xx_crashdump_init(struct adreno_device *adreno_dev);
 #endif
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index 1d5f4a5..d92d1e0 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -35,6 +35,25 @@
 	struct adreno_ringbuffer *rb = adreno_dev->cur_rb;
 	unsigned int wptr;
 	unsigned long flags;
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+
+	/*
+	 * Need to make sure GPU is up before we read the
+	 * WPTR as fence doesn't wake GPU on read operation.
+	 */
+	if (in_interrupt() == 0) {
+		int status;
+
+		if (gpudev->oob_set) {
+			status = gpudev->oob_set(adreno_dev,
+				OOB_PREEMPTION_SET_MASK,
+				OOB_PREEMPTION_CHECK_MASK,
+				OOB_PREEMPTION_CLEAR_MASK);
+			if (status)
+				return;
+		}
+	}
+
 
 	spin_lock_irqsave(&rb->preempt_lock, flags);
 
@@ -55,6 +74,12 @@
 			msecs_to_jiffies(adreno_drawobj_timeout);
 
 	spin_unlock_irqrestore(&rb->preempt_lock, flags);
+
+	if (in_interrupt() == 0) {
+		if (gpudev->oob_clear)
+			gpudev->oob_clear(adreno_dev,
+				OOB_PREEMPTION_CLEAR_MASK);
+	}
 }
 
 static inline bool adreno_move_preempt_state(struct adreno_device *adreno_dev,
@@ -204,14 +229,15 @@
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	struct kgsl_iommu *iommu = KGSL_IOMMU_PRIV(device);
 	struct adreno_ringbuffer *next;
-	uint64_t ttbr0;
+	uint64_t ttbr0, gpuaddr;
 	unsigned int contextidr;
 	unsigned long flags;
 	uint32_t preempt_level, usesgmem, skipsaverestore;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
 
-	preempt_level = adreno_dev->preempt_level;
-	usesgmem = adreno_dev->usesgmem;
-	skipsaverestore = adreno_dev->skipsaverestore;
+	preempt_level = preempt->preempt_level;
+	usesgmem = preempt->usesgmem;
+	skipsaverestore = preempt->skipsaverestore;
 
 	/* Put ourselves into a possible trigger state */
 	if (!adreno_move_preempt_state(adreno_dev,
@@ -266,6 +292,8 @@
 	kgsl_sharedmem_writel(device, &next->preemption_desc,
 		PREEMPT_RECORD(wptr), next->wptr);
 
+	preempt->count++;
+
 	spin_unlock_irqrestore(&next->preempt_lock, flags);
 
 	/* And write it to the smmu info */
@@ -274,24 +302,57 @@
 	kgsl_sharedmem_writel(device, &iommu->smmu_info,
 		PREEMPT_SMMU_RECORD(context_idr), contextidr);
 
-	kgsl_regwrite(device,
-		A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_LO,
-		lower_32_bits(next->preemption_desc.gpuaddr));
-	kgsl_regwrite(device,
-		A6XX_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_HI,
-		upper_32_bits(next->preemption_desc.gpuaddr));
+	kgsl_sharedmem_readq(&device->scratch, &gpuaddr,
+		SCRATCH_PREEMPTION_CTXT_RESTORE_ADDR_OFFSET(next->id));
 
-	if (next->drawctxt_active) {
-		struct kgsl_context *context = &next->drawctxt_active->base;
-		uint64_t gpuaddr = context->user_ctxt_record->memdesc.gpuaddr;
+	/*
+	 * Set a keepalive bit before the first preemption register write.
+	 * This is required since while each individual write to the context
+	 * switch registers will wake the GPU from collapse, it will not in
+	 * itself cause GPU activity. Thus, the GPU could technically be
+	 * re-collapsed between subsequent register writes leading to a
+	 * prolonged preemption sequence. The keepalive bit prevents any
+	 * further power collapse while it is set.
+	 * It is more efficient to use a keepalive+wake-on-fence approach here
+	 * rather than an OOB. Both keepalive and the fence are effectively
+	 * free when the GPU is already powered on, whereas an OOB requires an
+	 * unconditional handshake with the GMU.
+	 */
+	kgsl_gmu_regrmw(device, A6XX_GMU_AO_SPARE_CNTL, 0x0, 0x2);
 
-		kgsl_regwrite(device,
-			A6XX_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_LO,
-			lower_32_bits(gpuaddr));
-		kgsl_regwrite(device,
-			A6XX_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_HI,
-			upper_32_bits(gpuaddr));
-	}
+	/*
+	 * Fenced writes on this path will make sure the GPU is woken up
+	 * in case it was power collapsed by the GMU.
+	 */
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_LO,
+		lower_32_bits(next->preemption_desc.gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_NON_SECURE_RESTORE_ADDR_HI,
+		upper_32_bits(next->preemption_desc.gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_LO,
+		lower_32_bits(next->secure_preemption_desc.gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_PRIV_SECURE_RESTORE_ADDR_HI,
+		upper_32_bits(next->secure_preemption_desc.gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_LO,
+		lower_32_bits(gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_CONTEXT_SWITCH_NON_PRIV_RESTORE_ADDR_HI,
+		upper_32_bits(gpuaddr),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
 
 	adreno_dev->next_rb = next;
 
@@ -304,10 +365,20 @@
 	adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED);
 
 	/* Trigger the preemption */
-	adreno_writereg(adreno_dev, ADRENO_REG_CP_PREEMPT,
-			((preempt_level << 6) & 0xC0) |
-			((skipsaverestore << 9) & 0x200) |
-			((usesgmem << 8) & 0x100) | 0x1);
+	adreno_gmu_fenced_write(adreno_dev,
+		ADRENO_REG_CP_PREEMPT,
+		(((preempt_level << 6) & 0xC0) |
+		((skipsaverestore << 9) & 0x200) |
+		((usesgmem << 8) & 0x100) | 0x1),
+		FENCE_STATUS_WRITEDROPPED1_MASK);
+
+	/*
+	 * Once preemption has been requested with the final register write,
+	 * the preemption process starts and the GPU is considered busy.
+	 * We can now safely clear the preemption keepalive bit, allowing
+	 * power collapse to resume its regular activity.
+	 */
+	kgsl_gmu_regrmw(device, A6XX_GMU_AO_SPARE_CNTL, 0x2, 0x0);
 }
 
 void a6xx_preemption_callback(struct adreno_device *adreno_dev, int bit)
@@ -373,34 +444,20 @@
 	mutex_unlock(&device->mutex);
 }
 
-unsigned int a6xx_set_marker(unsigned int *cmds, int start)
-{
-	*cmds++ = cp_type7_packet(CP_SET_MARKER, 1);
-
-	/*
-	 * Indicate the beginning  and end of the IB1 list with a SET_MARKER.
-	 * Among other things, this will implicitly enable and disable
-	 * preemption respectively.
-	 */
-	if (start)
-		*cmds++ = 0xD;
-	else
-		*cmds++ = 0xE;
-
-	return 2;
-}
-
 unsigned int a6xx_preemption_pre_ibsubmit(
 		struct adreno_device *adreno_dev,
 		struct adreno_ringbuffer *rb,
 		unsigned int *cmds, struct kgsl_context *context)
 {
 	unsigned int *cmds_orig = cmds;
+	uint64_t gpuaddr = 0;
 
-	if (context)
+	if (context) {
+		gpuaddr = context->user_ctxt_record->memdesc.gpuaddr;
 		*cmds++ = cp_type7_packet(CP_SET_PSEUDO_REGISTER, 15);
-	else
+	} else {
 		*cmds++ = cp_type7_packet(CP_SET_PSEUDO_REGISTER, 12);
+	}
 
 	/* NULL SMMU_INFO buffer - we track in KMD */
 	*cmds++ = SET_PSEUDO_REGISTER_SAVE_REGISTER_SMMU_INFO;
@@ -410,10 +467,10 @@
 	cmds += cp_gpuaddr(adreno_dev, cmds, rb->preemption_desc.gpuaddr);
 
 	*cmds++ = SET_PSEUDO_REGISTER_SAVE_REGISTER_PRIV_SECURE_SAVE_ADDR;
-	cmds += cp_gpuaddr(adreno_dev, cmds, 0);
+	cmds += cp_gpuaddr(adreno_dev, cmds,
+			rb->secure_preemption_desc.gpuaddr);
 
 	if (context) {
-		uint64_t gpuaddr = context->user_ctxt_record->memdesc.gpuaddr;
 
 		*cmds++ = SET_PSEUDO_REGISTER_SAVE_REGISTER_NON_PRIV_SAVE_ADDR;
 		cmds += cp_gpuaddr(adreno_dev, cmds, gpuaddr);
@@ -430,6 +487,20 @@
 	cmds += cp_gpuaddr(adreno_dev, cmds,
 			rb->perfcounter_save_restore_desc.gpuaddr);
 
+	if (context) {
+		struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+		struct adreno_context *drawctxt = ADRENO_CONTEXT(context);
+		struct adreno_ringbuffer *rb = drawctxt->rb;
+		uint64_t dest =
+			SCRATCH_PREEMPTION_CTXT_RESTORE_GPU_ADDR(device,
+			rb->id);
+
+		*cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 2);
+		cmds += cp_gpuaddr(adreno_dev, cmds, dest);
+		*cmds++ = lower_32_bits(gpuaddr);
+		*cmds++ = upper_32_bits(gpuaddr);
+	}
+
 	return (unsigned int) (cmds - cmds_orig);
 }
 
@@ -437,6 +508,18 @@
 	unsigned int *cmds)
 {
 	unsigned int *cmds_orig = cmds;
+	struct adreno_ringbuffer *rb = adreno_dev->cur_rb;
+
+	if (rb) {
+		struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+		uint64_t dest = SCRATCH_PREEMPTION_CTXT_RESTORE_GPU_ADDR(device,
+			rb->id);
+
+		*cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 2);
+		cmds += cp_gpuaddr(adreno_dev, cmds, dest);
+		*cmds++ = 0;
+		*cmds++ = 0;
+	}
 
 	*cmds++ = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4);
 	cmds += cp_gpuaddr(adreno_dev, cmds, 0x0);
@@ -504,6 +587,17 @@
 	if (ret)
 		return ret;
 
+	ret = kgsl_allocate_user(device, &rb->secure_preemption_desc,
+		A6XX_CP_CTXRECORD_SIZE_IN_BYTES,
+		KGSL_MEMFLAGS_SECURE | KGSL_MEMDESC_PRIVILEGED);
+	if (ret)
+		return ret;
+
+	ret = kgsl_iommu_map_global_secure_pt_entry(device,
+				&rb->secure_preemption_desc);
+	if (ret)
+		return ret;
+
 	ret = kgsl_allocate_global(device, &rb->perfcounter_save_restore_desc,
 		A6XX_CP_PERFCOUNTER_SAVE_RESTORE_SIZE, 0,
 		KGSL_MEMDESC_PRIVILEGED, "perfcounter_save_restore_desc");
@@ -577,6 +671,9 @@
 	FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
 		kgsl_free_global(device, &rb->preemption_desc);
 		kgsl_free_global(device, &rb->perfcounter_save_restore_desc);
+		kgsl_iommu_unmap_global_secure_pt_entry(device,
+				&rb->secure_preemption_desc);
+		kgsl_sharedmem_free(&rb->secure_preemption_desc);
 	}
 }
 
@@ -635,18 +732,29 @@
 		return;
 
 	gpumem_free_entry(context->user_ctxt_record);
+
+	/* Put the extra ref from gpumem_alloc_entry() */
+	kgsl_mem_entry_put(context->user_ctxt_record);
 }
 
 int a6xx_preemption_context_init(struct kgsl_context *context)
 {
 	struct kgsl_device *device = context->device;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	uint64_t flags = 0;
 
 	if (!adreno_is_preemption_setup_enabled(adreno_dev))
 		return 0;
 
+	if (context->flags & KGSL_CONTEXT_SECURE)
+		flags |= KGSL_MEMFLAGS_SECURE;
+
+	/*
+	 * gpumem_alloc_entry takes an extra refcount. Put it only when
+	 * destroying the context to keep the context record valid
+	 */
 	context->user_ctxt_record = gpumem_alloc_entry(context->dev_priv,
-			A6XX_CP_CTXRECORD_USER_RESTORE_SIZE, 0);
+			A6XX_CP_CTXRECORD_USER_RESTORE_SIZE, flags);
 	if (IS_ERR(context->user_ctxt_record)) {
 		int ret = PTR_ERR(context->user_ctxt_record);
 
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index 80c9a61..b9a2f8d 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -195,35 +195,6 @@
 	unsigned int ctxt_id;
 };
 
-static const unsigned int a6xx_hlsq_non_ctx_registers[] = {
-	0xBE00, 0xBE01, 0xBE04, 0xBE05, 0xBE08, 0xBE09, 0xBE10, 0xBE15,
-	0xBE20, 0xBE23,
-};
-
-static const unsigned int a6xx_sp_non_ctx_registers[] = {
-	0xAE00, 0xAE04, 0xAE0C, 0xAE0C, 0xAE0F, 0xAE2B, 0xAE30, 0xAE32,
-	0xAE35, 0xAE35, 0xAE3A, 0xAE3F, 0xAE50, 0xAE52,
-};
-
-static const unsigned int a6xx_tp_non_ctx_registers[] = {
-	0xB600, 0xB601, 0xB604, 0xB605, 0xB610, 0xB61B, 0xB620, 0xB623,
-};
-
-static struct a6xx_non_ctx_dbgahb_registers {
-	unsigned int regbase;
-	unsigned int statetype;
-	const unsigned int *regs;
-	unsigned int num_sets;
-	unsigned int offset;
-} a6xx_non_ctx_dbgahb[] = {
-	{ 0x0002F800, 0x40, a6xx_hlsq_non_ctx_registers,
-		ARRAY_SIZE(a6xx_hlsq_non_ctx_registers) / 2 },
-	{ 0x0002B800, 0x20, a6xx_sp_non_ctx_registers,
-		ARRAY_SIZE(a6xx_sp_non_ctx_registers) / 2 },
-	{ 0x0002D800, 0x0, a6xx_tp_non_ctx_registers,
-		ARRAY_SIZE(a6xx_tp_non_ctx_registers) / 2 },
-};
-
 static const unsigned int a6xx_vbif_ver_20xxxxxx_registers[] = {
 	/* VBIF */
 	0x3000, 0x3007, 0x300C, 0x3014, 0x3018, 0x302D, 0x3030, 0x3031,
@@ -332,6 +303,15 @@
 	/* VFD */
 	0xA600, 0xA601, 0xA603, 0xA603, 0xA60A, 0xA60A, 0xA610, 0xA617,
 	0xA630, 0xA630,
+	/* SP */
+	0xAE00, 0xAE04, 0xAE0C, 0xAE0C, 0xAE0F, 0xAE2B, 0xAE30, 0xAE32,
+	0xAE35, 0xAE35, 0xAE3A, 0xAE3F, 0xAE50, 0xAE52,
+	/* TP */
+	0xB600, 0xB601, 0xB604, 0xB605, 0xB610, 0xB61B, 0xB620, 0xB623,
+	/* HLSQ */
+	0xBE00, 0xBE01, 0xBE04, 0xBE05, 0xBE08, 0xBE09, 0xBE10, 0xBE15,
+	0xBE20, 0xBE23,
+
 };
 
 /*
@@ -437,7 +417,6 @@
 	A6XX_DBGBUS_VBIF, 0x100,
 };
 
-static void __iomem *a6xx_cx_dbgc;
 static const struct adreno_debugbus_block a6xx_cx_dbgc_debugbus_blocks[] = {
 	{ A6XX_DBGBUS_GMU_CX, 0x100, },
 	{ A6XX_DBGBUS_CX, 0x100, },
@@ -557,6 +536,7 @@
 	const unsigned int *regs;
 	unsigned int count;
 	const struct sel_reg *sel;
+	uint64_t offset;
 } a6xx_reg_list[] = {
 	{ a6xx_registers, ARRAY_SIZE(a6xx_registers) / 2, NULL },
 	{ a6xx_rb_rac_registers, ARRAY_SIZE(a6xx_rb_rac_registers) / 2,
@@ -569,64 +549,56 @@
 	(((_a)[(2 * (_i)) + 1] - (_a)[2 * (_i)]) + 1)
 
 static size_t a6xx_legacy_snapshot_registers(struct kgsl_device *device,
-		u8 *buf, size_t remain)
+		u8 *buf, size_t remain, struct reg_list *regs)
 {
-	unsigned int i;
-	size_t used = 0;
+	struct kgsl_snapshot_registers snapshot_regs = {
+		.regs = regs->regs,
+		.count = regs->count,
+	};
 
-	for (i = 0; i < ARRAY_SIZE(a6xx_reg_list); i++) {
-		struct reg_list *regs = &a6xx_reg_list[i];
-		struct kgsl_snapshot_registers snapshot_regs = {
-			.regs = regs->regs,
-			.count = regs->count,
-		};
+	if (regs->sel)
+		kgsl_regwrite(device, regs->sel->host_reg, regs->sel->val);
 
-		if (regs->sel)
-			kgsl_regwrite(device, regs->sel->host_reg,
-				regs->sel->val);
-		used += kgsl_snapshot_dump_registers(device, buf + used,
-						remain - used, &snapshot_regs);
-	}
-	return used;
+	return kgsl_snapshot_dump_registers(device, buf, remain,
+		&snapshot_regs);
 }
 
 static size_t a6xx_snapshot_registers(struct kgsl_device *device, u8 *buf,
 		size_t remain, void *priv)
 {
 	struct kgsl_snapshot_regs *header = (struct kgsl_snapshot_regs *)buf;
+	struct reg_list *regs = (struct reg_list *)priv;
 	unsigned int *data = (unsigned int *)(buf + sizeof(*header));
-	unsigned int *src = (unsigned int *)a6xx_crashdump_registers.hostptr;
-	unsigned int i, j, k;
+	unsigned int *src;
+	unsigned int j, k;
 	unsigned int count = 0;
 
 	if (crash_dump_valid == false)
-		return a6xx_legacy_snapshot_registers(device, buf, remain);
+		return a6xx_legacy_snapshot_registers(device, buf, remain,
+			regs);
 
 	if (remain < sizeof(*header)) {
 		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
 		return 0;
 	}
 
+	src = (unsigned int *)(a6xx_crashdump_registers.hostptr + regs->offset);
 	remain -= sizeof(*header);
 
-	for (i = 0; i < ARRAY_SIZE(a6xx_reg_list); i++) {
-		struct reg_list *regs = &a6xx_reg_list[i];
+	for (j = 0; j < regs->count; j++) {
+		unsigned int start = regs->regs[2 * j];
+		unsigned int end = regs->regs[(2 * j) + 1];
 
-		for (j = 0; j < regs->count; j++) {
-			unsigned int start = regs->regs[2 * j];
-			unsigned int end = regs->regs[(2 * j) + 1];
+		if (remain < ((end - start) + 1) * 8) {
+			SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
+			goto out;
+		}
 
-			if (remain < ((end - start) + 1) * 8) {
-				SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
-				goto out;
-			}
+		remain -= ((end - start) + 1) * 8;
 
-			remain -= ((end - start) + 1) * 8;
-
-			for (k = start; k <= end; k++, count++) {
-				*data++ = k;
-				*data++ = *src++;
-			}
+		for (k = start; k <= end; k++, count++) {
+			*data++ = k;
+			*data++ = *src++;
 		}
 	}
 
@@ -668,7 +640,7 @@
 	header->size = block->sz;
 
 	memcpy(data, a6xx_crashdump_registers.hostptr + info->offset,
-		block->sz);
+		block->sz * sizeof(unsigned int));
 
 	return SHADER_SECTION_SZ(block->sz);
 }
@@ -855,106 +827,6 @@
 	return data_size + sizeof(*header);
 }
 
-static size_t a6xx_legacy_snapshot_non_ctx_dbgahb(struct kgsl_device *device,
-				u8 *buf, size_t remain, void *priv)
-{
-	struct kgsl_snapshot_regs *header =
-				(struct kgsl_snapshot_regs *)buf;
-	struct a6xx_non_ctx_dbgahb_registers *regs =
-				(struct a6xx_non_ctx_dbgahb_registers *)priv;
-	unsigned int *data = (unsigned int *)(buf + sizeof(*header));
-	int count = 0;
-	unsigned int read_sel;
-	int i, j;
-
-	if (!device->snapshot_legacy)
-		return 0;
-
-	/* Figure out how many registers we are going to dump */
-	for (i = 0; i < regs->num_sets; i++) {
-		int start = regs->regs[i * 2];
-		int end = regs->regs[i * 2 + 1];
-
-		count += (end - start + 1);
-	}
-
-	if (remain < (count * 8) + sizeof(*header)) {
-		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
-		return 0;
-	}
-
-	header->count = count;
-
-	read_sel = (regs->statetype & 0xff) << 8;
-	kgsl_regwrite(device, A6XX_HLSQ_DBG_READ_SEL, read_sel);
-
-	for (i = 0; i < regs->num_sets; i++) {
-		unsigned int start = regs->regs[2 * i];
-		unsigned int end = regs->regs[2 * i + 1];
-
-		for (j = start; j <= end; j++) {
-			unsigned int val;
-
-			val = a6xx_read_dbgahb(device, regs->regbase, j);
-			*data++ = j;
-			*data++ = val;
-
-		}
-	}
-	return (count * 8) + sizeof(*header);
-}
-
-static size_t a6xx_snapshot_non_ctx_dbgahb(struct kgsl_device *device, u8 *buf,
-				size_t remain, void *priv)
-{
-	struct kgsl_snapshot_regs *header =
-				(struct kgsl_snapshot_regs *)buf;
-	struct a6xx_non_ctx_dbgahb_registers *regs =
-				(struct a6xx_non_ctx_dbgahb_registers *)priv;
-	unsigned int count = 0;
-	unsigned int *data = (unsigned int *)(buf + sizeof(*header));
-	unsigned int i, k;
-	unsigned int *src;
-
-	if (crash_dump_valid == false)
-		return a6xx_legacy_snapshot_non_ctx_dbgahb(device, buf, remain,
-				regs);
-
-	if (remain < sizeof(*header)) {
-		SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
-		return 0;
-	}
-
-	remain -= sizeof(*header);
-
-	src = (unsigned int *)(a6xx_crashdump_registers.hostptr + regs->offset);
-
-	for (i = 0; i < regs->num_sets; i++) {
-		unsigned int start;
-		unsigned int end;
-
-		start = regs->regs[2 * i];
-		end = regs->regs[(2 * i) + 1];
-
-		if (remain < (end - start + 1) * 8) {
-			SNAPSHOT_ERR_NOMEM(device, "REGISTERS");
-			goto out;
-		}
-
-		remain -= ((end - start) + 1) * 8;
-
-		for (k = start; k <= end; k++, count++) {
-			*data++ = k;
-			*data++ = *src++;
-		}
-	}
-out:
-	header->count = count;
-
-	/* Return the size of the section */
-	return (count * 8) + sizeof(*header);
-}
-
 static void a6xx_snapshot_dbgahb_regs(struct kgsl_device *device,
 				struct kgsl_snapshot *snapshot)
 {
@@ -974,12 +846,6 @@
 				a6xx_snapshot_cluster_dbgahb, &info);
 		}
 	}
-
-	for (i = 0; i < ARRAY_SIZE(a6xx_non_ctx_dbgahb); i++) {
-		kgsl_snapshot_add_section(device,
-			KGSL_SNAPSHOT_SECTION_REGS, snapshot,
-			a6xx_snapshot_non_ctx_dbgahb, &a6xx_non_ctx_dbgahb[i]);
-	}
 }
 
 static size_t a6xx_legacy_snapshot_mvc(struct kgsl_device *device, u8 *buf,
@@ -1261,46 +1127,6 @@
 	return size;
 }
 
-static void _cx_dbgc_regread(unsigned int offsetwords, unsigned int *value)
-{
-	void __iomem *reg;
-
-	if (WARN((offsetwords < A6XX_CX_DBGC_CFG_DBGBUS_SEL_A) ||
-		(offsetwords > A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2),
-		"Read beyond CX_DBGC block: 0x%x\n", offsetwords))
-		return;
-
-	reg = a6xx_cx_dbgc +
-		((offsetwords - A6XX_CX_DBGC_CFG_DBGBUS_SEL_A) << 2);
-	*value = __raw_readl(reg);
-
-	/*
-	 * ensure this read finishes before the next one.
-	 * i.e. act like normal readl()
-	 */
-	rmb();
-}
-
-static void _cx_dbgc_regwrite(unsigned int offsetwords, unsigned int value)
-{
-	void __iomem *reg;
-
-	if (WARN((offsetwords < A6XX_CX_DBGC_CFG_DBGBUS_SEL_A) ||
-		(offsetwords > A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2),
-		"Write beyond CX_DBGC block: 0x%x\n", offsetwords))
-		return;
-
-	reg = a6xx_cx_dbgc +
-		((offsetwords - A6XX_CX_DBGC_CFG_DBGBUS_SEL_A) << 2);
-
-	/*
-	 * ensure previous writes post before this one,
-	 * i.e. act like normal writel()
-	 */
-	wmb();
-	__raw_writel(value, reg);
-}
-
 /* a6xx_cx_dbgc_debug_bus_read() - Read data from trace bus */
 static void a6xx_cx_debug_bus_read(struct kgsl_device *device,
 	unsigned int block_id, unsigned int index, unsigned int *val)
@@ -1310,10 +1136,10 @@
 	reg = (block_id << A6XX_CX_DBGC_CFG_DBGBUS_SEL_PING_BLK_SEL_SHIFT) |
 			(index << A6XX_CX_DBGC_CFG_DBGBUS_SEL_PING_INDEX_SHIFT);
 
-	_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_SEL_A, reg);
-	_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_SEL_B, reg);
-	_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_SEL_C, reg);
-	_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_SEL_D, reg);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_SEL_A, reg);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_SEL_B, reg);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_SEL_C, reg);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_SEL_D, reg);
 
 	/*
 	 * There needs to be a delay of 1 us to ensure enough time for correct
@@ -1321,9 +1147,9 @@
 	 */
 	udelay(1);
 
-	_cx_dbgc_regread(A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2, val);
+	adreno_cx_dbgc_regread(device, A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2, val);
 	val++;
-	_cx_dbgc_regread(A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF1, val);
+	adreno_cx_dbgc_regread(device, A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF1, val);
 }
 
 /*
@@ -1366,6 +1192,7 @@
 		struct kgsl_snapshot *snapshot)
 {
 	int i;
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 
 	kgsl_regwrite(device, A6XX_DBGC_CFG_DBGBUS_CNTLT,
 		(0xf << A6XX_DBGC_CFG_DBGBUS_CNTLT_SEGT_SHIFT) |
@@ -1404,50 +1231,42 @@
 	kgsl_regwrite(device, A6XX_DBGC_CFG_DBGBUS_MASKL_2, 0);
 	kgsl_regwrite(device, A6XX_DBGC_CFG_DBGBUS_MASKL_3, 0);
 
-	a6xx_cx_dbgc = ioremap(device->reg_phys +
-			(A6XX_CX_DBGC_CFG_DBGBUS_SEL_A << 2),
-			(A6XX_CX_DBGC_CFG_DBGBUS_TRACE_BUF2 -
-				A6XX_CX_DBGC_CFG_DBGBUS_SEL_A + 1) << 2);
-
-	if (a6xx_cx_dbgc) {
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_CNTLT,
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_CNTLT,
 		(0xf << A6XX_DBGC_CFG_DBGBUS_CNTLT_SEGT_SHIFT) |
 		(0x0 << A6XX_DBGC_CFG_DBGBUS_CNTLT_GRANU_SHIFT) |
 		(0x0 << A6XX_DBGC_CFG_DBGBUS_CNTLT_TRACEEN_SHIFT));
 
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_CNTLM,
-			0xf << A6XX_CX_DBGC_CFG_DBGBUS_CNTLM_ENABLE_SHIFT);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_CNTLM,
+		0xf << A6XX_CX_DBGC_CFG_DBGBUS_CNTLM_ENABLE_SHIFT);
 
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_IVTL_0, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_IVTL_1, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_IVTL_2, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_IVTL_3, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_IVTL_0, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_IVTL_1, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_IVTL_2, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_IVTL_3, 0);
 
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_0,
-			(0 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL0_SHIFT) |
-			(1 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL1_SHIFT) |
-			(2 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL2_SHIFT) |
-			(3 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL3_SHIFT) |
-			(4 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL4_SHIFT) |
-			(5 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL5_SHIFT) |
-			(6 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL6_SHIFT) |
-			(7 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL7_SHIFT));
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_1,
-			(8 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL8_SHIFT) |
-			(9 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL9_SHIFT) |
-			(10 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL10_SHIFT) |
-			(11 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL11_SHIFT) |
-			(12 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL12_SHIFT) |
-			(13 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL13_SHIFT) |
-			(14 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL14_SHIFT) |
-			(15 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL15_SHIFT));
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_0,
+		(0 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL0_SHIFT) |
+		(1 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL1_SHIFT) |
+		(2 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL2_SHIFT) |
+		(3 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL3_SHIFT) |
+		(4 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL4_SHIFT) |
+		(5 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL5_SHIFT) |
+		(6 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL6_SHIFT) |
+		(7 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL7_SHIFT));
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_BYTEL_1,
+		(8 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL8_SHIFT) |
+		(9 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL9_SHIFT) |
+		(10 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL10_SHIFT) |
+		(11 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL11_SHIFT) |
+		(12 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL12_SHIFT) |
+		(13 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL13_SHIFT) |
+		(14 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL14_SHIFT) |
+		(15 << A6XX_CX_DBGC_CFG_DBGBUS_BYTEL15_SHIFT));
 
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_MASKL_0, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_MASKL_1, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_MASKL_2, 0);
-		_cx_dbgc_regwrite(A6XX_CX_DBGC_CFG_DBGBUS_MASKL_3, 0);
-	} else
-		KGSL_DRV_ERR(device, "Unable to ioremap CX_DBGC_CFG block\n");
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_MASKL_0, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_MASKL_1, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_MASKL_2, 0);
+	adreno_cx_dbgc_regwrite(device, A6XX_CX_DBGC_CFG_DBGBUS_MASKL_3, 0);
 
 	for (i = 0; i < ARRAY_SIZE(a6xx_dbgc_debugbus_blocks); i++) {
 		kgsl_snapshot_add_section(device,
@@ -1456,26 +1275,38 @@
 			(void *) &a6xx_dbgc_debugbus_blocks[i]);
 	}
 
-	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_DEBUGBUS,
-			snapshot, a6xx_snapshot_vbif_debugbus_block,
-			(void *) &a6xx_vbif_debugbus_blocks);
+	/* Skip if GPU has GBIF */
+	if (!adreno_has_gbif(adreno_dev))
+		kgsl_snapshot_add_section(device,
+				KGSL_SNAPSHOT_SECTION_DEBUGBUS,
+				snapshot, a6xx_snapshot_vbif_debugbus_block,
+				(void *) &a6xx_vbif_debugbus_blocks);
 
-	if (a6xx_cx_dbgc) {
+	/* Dump the CX debugbus data if the block exists */
+	if (adreno_is_cx_dbgc_register(device, A6XX_CX_DBGC_CFG_DBGBUS_SEL_A)) {
 		for (i = 0; i < ARRAY_SIZE(a6xx_cx_dbgc_debugbus_blocks); i++) {
 			kgsl_snapshot_add_section(device,
 				KGSL_SNAPSHOT_SECTION_DEBUGBUS,
 				snapshot, a6xx_snapshot_cx_dbgc_debugbus_block,
 				(void *) &a6xx_cx_dbgc_debugbus_blocks[i]);
 		}
-		iounmap(a6xx_cx_dbgc);
 	}
 }
 
-static void a6xx_snapshot_gmu(struct kgsl_device *device,
+/*
+ * a6xx_snapshot_gmu() - A6XX GMU snapshot function
+ * @adreno_dev: Device being snapshotted
+ * @snapshot: Pointer to the snapshot instance
+ *
+ * This is where all of the A6XX GMU specific bits and pieces are grabbed
+ * into the snapshot memory
+ */
+void a6xx_snapshot_gmu(struct adreno_device *adreno_dev,
 		struct kgsl_snapshot *snapshot)
 {
-	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+	unsigned int val;
 
 	if (!kgsl_gmu_isenabled(device))
 		return;
@@ -1483,10 +1314,16 @@
 	adreno_snapshot_registers(device, snapshot, a6xx_gmu_registers,
 					ARRAY_SIZE(a6xx_gmu_registers) / 2);
 
-	if (gpudev->gx_is_on(adreno_dev))
+	if (gpudev->gx_is_on(adreno_dev)) {
+		/* Set fence to ALLOW mode so registers can be read */
+		kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
+		kgsl_regread(device, A6XX_GMU_AO_AHB_FENCE_CTRL, &val);
+
+		KGSL_DRV_ERR(device, "set FENCE to ALLOW mode:%x\n", val);
 		adreno_snapshot_registers(device, snapshot,
 				a6xx_gmu_gx_registers,
 				ARRAY_SIZE(a6xx_gmu_gx_registers) / 2);
+	}
 }
 
 /* a6xx_snapshot_sqe() - Dump SQE data in snapshot */
@@ -1519,6 +1356,8 @@
 
 	crash_dump_valid = false;
 
+	if (!device->snapshot_crashdumper)
+		return;
 	if (a6xx_capturescript.gpuaddr == 0 ||
 		a6xx_crashdump_registers.gpuaddr == 0)
 		return;
@@ -1570,33 +1409,35 @@
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	struct adreno_snapshot_data *snap_data = gpudev->snapshot_data;
 	bool sptprac_on;
+	unsigned int i;
 
 	/* GMU TCM data dumped through AHB */
-	a6xx_snapshot_gmu(device, snapshot);
+	a6xx_snapshot_gmu(adreno_dev, snapshot);
 
 	sptprac_on = gpudev->sptprac_is_on(adreno_dev);
 
 	/* Return if the GX is off */
-	if (!gpudev->gx_is_on(adreno_dev)) {
-		pr_err("GX is off. Only dumping GMU data in snapshot\n");
+	if (!gpudev->gx_is_on(adreno_dev))
 		return;
-	}
 
 	/* Dump the registers which get affected by crash dumper trigger */
 	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
 		snapshot, a6xx_snapshot_pre_crashdump_regs, NULL);
 
 	/* Dump vbif registers as well which get affected by crash dumper */
-	adreno_snapshot_vbif_registers(device, snapshot,
-		a6xx_vbif_snapshot_registers,
-		ARRAY_SIZE(a6xx_vbif_snapshot_registers));
+	if (!adreno_has_gbif(adreno_dev))
+		adreno_snapshot_vbif_registers(device, snapshot,
+			a6xx_vbif_snapshot_registers,
+			ARRAY_SIZE(a6xx_vbif_snapshot_registers));
 
 	/* Try to run the crash dumper */
 	if (sptprac_on)
 		_a6xx_do_crashdump(device);
 
-	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
-		snapshot, a6xx_snapshot_registers, NULL);
+	for (i = 0; i < ARRAY_SIZE(a6xx_reg_list); i++) {
+		kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_REGS,
+			snapshot, a6xx_snapshot_registers, &a6xx_reg_list[i]);
+	}
 
 	/* CP_SQE indexed registers */
 	kgsl_snapshot_indexed_registers(device, snapshot,
@@ -1753,40 +1594,6 @@
 	return qwords;
 }
 
-static int _a6xx_crashdump_init_non_ctx_dbgahb(uint64_t *ptr, uint64_t *offset)
-{
-	int qwords = 0;
-	unsigned int i, k;
-	unsigned int count;
-
-	for (i = 0; i < ARRAY_SIZE(a6xx_non_ctx_dbgahb); i++) {
-		struct a6xx_non_ctx_dbgahb_registers *regs =
-				&a6xx_non_ctx_dbgahb[i];
-
-		regs->offset = *offset;
-
-		/* Program the aperture */
-		ptr[qwords++] = (regs->statetype & 0xff) << 8;
-		ptr[qwords++] =	(((uint64_t)A6XX_HLSQ_DBG_READ_SEL << 44)) |
-					(1 << 21) | 1;
-
-		for (k = 0; k < regs->num_sets; k++) {
-			unsigned int start = regs->regs[2 * k];
-
-			count = REG_PAIR_COUNT(regs->regs, k);
-			ptr[qwords++] =
-				a6xx_crashdump_registers.gpuaddr + *offset;
-			ptr[qwords++] =
-				(((uint64_t)(A6XX_HLSQ_DBG_AHB_READ_APERTURE +
-					start - regs->regbase / 4) << 44)) |
-							count;
-
-			*offset += count * sizeof(unsigned int);
-		}
-	}
-	return qwords;
-}
-
 void a6xx_crashdump_init(struct adreno_device *adreno_dev)
 {
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
@@ -1878,26 +1685,6 @@
 		}
 	}
 
-	/*
-	 * Calculate the script and data size for non context debug
-	 * AHB registers
-	 */
-	for (i = 0; i < ARRAY_SIZE(a6xx_non_ctx_dbgahb); i++) {
-		struct a6xx_non_ctx_dbgahb_registers *regs =
-				&a6xx_non_ctx_dbgahb[i];
-
-		/* 16 bytes for programming the aperture */
-		script_size += 16;
-
-		/* Reading each pair of registers takes 16 bytes */
-		script_size += 16 * regs->num_sets;
-
-		/* A dword per register read from the cluster list */
-		for (k = 0; k < regs->num_sets; k++)
-			data_size += REG_PAIR_COUNT(regs->regs, k) *
-				sizeof(unsigned int);
-	}
-
 	/* Now allocate the script and data buffers */
 
 	/* The script buffers needs 2 extra qwords on the end */
@@ -1920,6 +1707,8 @@
 	for (i = 0; i < ARRAY_SIZE(a6xx_reg_list); i++) {
 		struct reg_list *regs = &a6xx_reg_list[i];
 
+		regs->offset = offset;
+
 		/* Program the SEL_CNTL_CD register appropriately */
 		if (regs->sel) {
 			*ptr++ = regs->sel->val;
@@ -1946,8 +1735,6 @@
 
 	ptr += _a6xx_crashdump_init_ctx_dbgahb(ptr, &offset);
 
-	ptr += _a6xx_crashdump_init_non_ctx_dbgahb(ptr, &offset);
-
 	*ptr++ = 0;
 	*ptr++ = 0;
 }
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 25aeaef..0caf55b 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -681,7 +681,7 @@
 	 * then set up the timer.  If this misses, then preemption is indeed a
 	 * thing and the timer will be set up in due time
 	 */
-	if (!adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) {
+	if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE)) {
 		if (drawqueue_is_current(dispatch_q))
 			mod_timer(&dispatcher->timer, dispatch_q->expires);
 	}
@@ -1836,7 +1836,8 @@
 	 * because we won't see this cmdobj again
 	 */
 
-	if (fault & ADRENO_TIMEOUT_FAULT)
+	if ((fault & ADRENO_TIMEOUT_FAULT) ||
+				(fault & ADRENO_CTX_DETATCH_TIMEOUT_FAULT))
 		bitmap_zero(&cmdobj->fault_policy, BITS_PER_LONG);
 
 	/*
@@ -2046,7 +2047,7 @@
 	kfree(replay);
 }
 
-static void do_header_and_snapshot(struct kgsl_device *device,
+static void do_header_and_snapshot(struct kgsl_device *device, int fault,
 		struct adreno_ringbuffer *rb, struct kgsl_drawobj_cmd *cmdobj)
 {
 	struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
@@ -2054,7 +2055,7 @@
 	/* Always dump the snapshot on a non-drawobj failure */
 	if (cmdobj == NULL) {
 		adreno_fault_header(device, rb, NULL);
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, NULL, fault & ADRENO_GMU_FAULT);
 		return;
 	}
 
@@ -2066,7 +2067,8 @@
 	adreno_fault_header(device, rb, cmdobj);
 
 	if (!(drawobj->context->flags & KGSL_CONTEXT_NO_SNAPSHOT))
-		kgsl_device_snapshot(device, drawobj->context);
+		kgsl_device_snapshot(device, drawobj->context,
+					fault & ADRENO_GMU_FAULT);
 }
 
 static int dispatcher_do_fault(struct adreno_device *adreno_dev)
@@ -2078,7 +2080,7 @@
 	struct adreno_ringbuffer *rb;
 	struct adreno_ringbuffer *hung_rb = NULL;
 	unsigned int reg;
-	uint64_t base;
+	uint64_t base = 0;
 	struct kgsl_drawobj_cmd *cmdobj = NULL;
 	int ret, i;
 	int fault;
@@ -2192,7 +2194,7 @@
 		adreno_readreg64(adreno_dev, ADRENO_REG_CP_IB1_BASE,
 			ADRENO_REG_CP_IB1_BASE_HI, &base);
 
-	do_header_and_snapshot(device, hung_rb, cmdobj);
+	do_header_and_snapshot(device, fault, hung_rb, cmdobj);
 
 	/* Turn off the KEEPALIVE vote from the ISR for hard fault */
 	if (gpudev->gpu_keepalive && fault & ADRENO_HARD_FAULT)
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 0882447..b81be8f 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -304,6 +304,7 @@
 	/* Give the bad news to everybody waiting around */
 	wake_up_all(&drawctxt->waiting);
 	wake_up_all(&drawctxt->wq);
+	wake_up_all(&drawctxt->timeout);
 }
 
 /*
@@ -398,6 +399,7 @@
 	spin_lock_init(&drawctxt->lock);
 	init_waitqueue_head(&drawctxt->wq);
 	init_waitqueue_head(&drawctxt->waiting);
+	init_waitqueue_head(&drawctxt->timeout);
 
 	/* Set the context priority */
 	_set_context_priority(drawctxt);
@@ -503,6 +505,8 @@
 		kgsl_drawobj_destroy(list[i]);
 	}
 
+	debugfs_remove_recursive(drawctxt->debug_root);
+
 	/*
 	 * internal_timestamp is set in adreno_ringbuffer_addcmds,
 	 * which holds the device mutex.
@@ -520,20 +524,32 @@
 		drawctxt->internal_timestamp, 30 * 1000);
 
 	/*
-	 * If the wait for global fails due to timeout then nothing after this
-	 * point is likely to work very well - Get GPU snapshot and BUG_ON()
-	 * so we can take advantage of the debug tools to figure out what the
-	 * h - e - double hockey sticks happened. If EAGAIN error is returned
+	 * If the wait for global fails due to timeout then mark it as
+	 * context detach timeout fault and schedule dispatcher to kick
+	 * in GPU recovery. For a ADRENO_CTX_DETATCH_TIMEOUT_FAULT we clear
+	 * the policy and invalidate the context. If EAGAIN error is returned
 	 * then recovery will kick in and there will be no more commands in the
-	 * RB pipe from this context which is waht we are waiting for, so ignore
-	 * -EAGAIN error
+	 * RB pipe from this context which is what we are waiting for, so ignore
+	 * -EAGAIN error.
 	 */
 	if (ret && ret != -EAGAIN) {
-		KGSL_DRV_ERR(device, "Wait for global ts=%d type=%d error=%d\n",
-				drawctxt->internal_timestamp,
+		KGSL_DRV_ERR(device,
+				"Wait for global ctx=%d ts=%d type=%d error=%d\n",
+				drawctxt->base.id, drawctxt->internal_timestamp,
 				drawctxt->type, ret);
-		device->force_panic = 1;
-		kgsl_device_snapshot(device, context);
+
+		adreno_set_gpu_fault(adreno_dev,
+				ADRENO_CTX_DETATCH_TIMEOUT_FAULT);
+		mutex_unlock(&device->mutex);
+
+		/* Schedule dispatcher to kick in recovery */
+		adreno_dispatcher_schedule(device);
+
+		/* Wait for context to be invalidated and release context */
+		wait_event_interruptible_timeout(drawctxt->timeout,
+					kgsl_context_invalid(&drawctxt->base),
+					msecs_to_jiffies(5000));
+		return;
 	}
 
 	kgsl_sharedmem_writel(device, &device->memstore,
@@ -548,8 +564,6 @@
 
 	mutex_unlock(&device->mutex);
 
-	debugfs_remove_recursive(drawctxt->debug_root);
-
 	/* wake threads waiting to submit commands from this context */
 	wake_up_all(&drawctxt->waiting);
 	wake_up_all(&drawctxt->wq);
diff --git a/drivers/gpu/msm/adreno_drawctxt.h b/drivers/gpu/msm/adreno_drawctxt.h
index aeacf09..eef506f 100644
--- a/drivers/gpu/msm/adreno_drawctxt.h
+++ b/drivers/gpu/msm/adreno_drawctxt.h
@@ -40,6 +40,7 @@
  * @pending: Priority list node for the dispatcher list of pending contexts
  * @wq: Workqueue structure for contexts to sleep pending room in the queue
  * @waiting: Workqueue structure for contexts waiting for a timestamp or event
+ * @timeout: Workqueue structure for contexts waiting to invalidate
  * @queued: Number of commands queued in the drawqueue
  * @fault_policy: GFT fault policy set in _skip_cmd();
  * @debug_root: debugfs entry for this context.
@@ -68,6 +69,7 @@
 	struct plist_node pending;
 	wait_queue_head_t wq;
 	wait_queue_head_t waiting;
+	wait_queue_head_t timeout;
 
 	int queued;
 	unsigned int fault_policy;
diff --git a/drivers/gpu/msm/adreno_ioctl.c b/drivers/gpu/msm/adreno_ioctl.c
index 8b283ae..13d71982 100644
--- a/drivers/gpu/msm/adreno_ioctl.c
+++ b/drivers/gpu/msm/adreno_ioctl.c
@@ -143,7 +143,7 @@
 
 	if (WARN_ON(_IOC_SIZE(cmds[i].cmd) > sizeof(data))) {
 		if (__ratelimit(&_rs))
-			WARN(1, "data too big for ioctl 0x%08X: %d/%ld\n",
+			WARN(1, "data too big for ioctl 0x%08X: %d/%zu\n",
 				cmd, _IOC_SIZE(cmds[i].cmd), sizeof(data));
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/msm/adreno_perfcounter.c b/drivers/gpu/msm/adreno_perfcounter.c
index 0da4da9..94fdbc2 100644
--- a/drivers/gpu/msm/adreno_perfcounter.c
+++ b/drivers/gpu/msm/adreno_perfcounter.c
@@ -28,6 +28,20 @@
 /* offset of enable register from select register */
 #define VBIF2_PERF_EN_REG_SEL_OFF 16
 
+/* offset of clear register from select register for GBIF */
+#define GBIF_PERF_CLR_REG_SEL_OFF 1
+
+/* offset of enable register from select register for GBIF*/
+#define GBIF_PERF_EN_REG_SEL_OFF  2
+
+/* offset of clear register from the power enable register for GBIF*/
+#define GBIF_PWR_CLR_REG_EN_OFF    1
+
+/* */
+#define GBIF_PERF_RMW_MASK   0xFF
+/* */
+#define GBIF_PWR_RMW_MASK    0x10000
+
 /* offset of clear register from the enable register */
 #define VBIF2_PERF_PWR_CLR_REG_EN_OFF 8
 
@@ -157,13 +171,24 @@
  */
 inline void adreno_perfcounter_save(struct adreno_device *adreno_dev)
 {
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	struct adreno_perfcounters *counters = ADRENO_PERFCOUNTERS(adreno_dev);
 	struct adreno_perfcount_group *group;
 	unsigned int counter, groupid;
+	int ret = 0;
 
 	if (counters == NULL)
 		return;
 
+	if (gpudev->oob_set)
+		ret = gpudev->oob_set(adreno_dev, OOB_PERFCNTR_SET_MASK,
+				OOB_PERFCNTR_CHECK_MASK,
+				OOB_PERFCNTR_CLEAR_MASK);
+
+	/* if oob_set timeout, clear the mask and return */
+	if (ret)
+		goto done;
+
 	for (groupid = 0; groupid < counters->group_count; groupid++) {
 		group = &(counters->groups[groupid]);
 
@@ -183,6 +208,10 @@
 								counter);
 		}
 	}
+
+done:
+	if (gpudev->oob_clear)
+		gpudev->oob_clear(adreno_dev, OOB_PERFCNTR_CLEAR_MASK);
 }
 
 static int adreno_perfcounter_enable(struct adreno_device *adreno_dev,
@@ -614,12 +643,40 @@
 	struct adreno_perfcount_register *reg;
 
 	reg = &counters->groups[KGSL_PERFCOUNTER_GROUP_VBIF].regs[counter];
-	/* Write 1, followed by 0 to CLR register for clearing the counter */
-	kgsl_regwrite(device, reg->select - VBIF2_PERF_CLR_REG_SEL_OFF, 1);
-	kgsl_regwrite(device, reg->select - VBIF2_PERF_CLR_REG_SEL_OFF, 0);
-	kgsl_regwrite(device, reg->select, countable & VBIF2_PERF_CNT_SEL_MASK);
-	/* enable reg is 8 DWORDS before select reg */
-	kgsl_regwrite(device, reg->select - VBIF2_PERF_EN_REG_SEL_OFF, 1);
+
+	if (adreno_has_gbif(adreno_dev)) {
+		unsigned int shift = counter << 3;
+		unsigned int perfctr_mask = 1 << counter;
+		/*
+		 * Write 1, followed by 0 to CLR register for
+		 * clearing the counter
+		 */
+		kgsl_regrmw(device, reg->select - GBIF_PERF_CLR_REG_SEL_OFF,
+			perfctr_mask, perfctr_mask);
+		kgsl_regrmw(device, reg->select - GBIF_PERF_CLR_REG_SEL_OFF,
+			perfctr_mask, 0);
+		/* select the desired countable */
+		kgsl_regrmw(device, reg->select,
+			GBIF_PERF_RMW_MASK << shift, countable << shift);
+		/* enable counter */
+		kgsl_regrmw(device, reg->select - GBIF_PERF_EN_REG_SEL_OFF,
+			perfctr_mask, perfctr_mask);
+
+	} else {
+		/*
+		 * Write 1, followed by 0 to CLR register for
+		 * clearing the counter
+		 */
+		kgsl_regwrite(device,
+			reg->select - VBIF2_PERF_CLR_REG_SEL_OFF, 1);
+		kgsl_regwrite(device,
+			reg->select - VBIF2_PERF_CLR_REG_SEL_OFF, 0);
+		kgsl_regwrite(device,
+			reg->select, countable & VBIF2_PERF_CNT_SEL_MASK);
+		/* enable reg is 8 DWORDS before select reg */
+		kgsl_regwrite(device,
+			reg->select - VBIF2_PERF_EN_REG_SEL_OFF, 1);
+	}
 	reg->value = 0;
 }
 
@@ -630,10 +687,30 @@
 	struct adreno_perfcount_register *reg;
 
 	reg = &counters->groups[KGSL_PERFCOUNTER_GROUP_VBIF_PWR].regs[counter];
-	/* Write 1, followed by 0 to CLR register for clearing the counter */
-	kgsl_regwrite(device, reg->select + VBIF2_PERF_PWR_CLR_REG_EN_OFF, 1);
-	kgsl_regwrite(device, reg->select + VBIF2_PERF_PWR_CLR_REG_EN_OFF, 0);
-	kgsl_regwrite(device, reg->select, 1);
+
+	if (adreno_has_gbif(adreno_dev)) {
+		unsigned int perfctr_mask = GBIF_PWR_RMW_MASK << counter;
+		/*
+		 * Write 1, followed by 0 to CLR register for
+		 * clearing the counter
+		 */
+		kgsl_regrmw(device, reg->select + GBIF_PWR_CLR_REG_EN_OFF,
+			perfctr_mask, perfctr_mask);
+		kgsl_regrmw(device, reg->select + GBIF_PWR_CLR_REG_EN_OFF,
+			perfctr_mask, 0);
+		/* Enable the counter */
+		kgsl_regrmw(device, reg->select, perfctr_mask, perfctr_mask);
+	} else {
+		/*
+		 * Write 1, followed by 0 to CLR register for
+		 * clearing the counter
+		 */
+		kgsl_regwrite(device, reg->select +
+			VBIF2_PERF_PWR_CLR_REG_EN_OFF, 1);
+		kgsl_regwrite(device, reg->select +
+			VBIF2_PERF_PWR_CLR_REG_EN_OFF, 0);
+		kgsl_regwrite(device, reg->select, 1);
+	}
 	reg->value = 0;
 }
 
@@ -691,6 +768,21 @@
 	reg->value = 0;
 }
 
+static inline bool _perfcounter_inline_update(
+	struct adreno_device *adreno_dev, unsigned int group)
+{
+	if (adreno_is_a6xx(adreno_dev)) {
+		if ((group == KGSL_PERFCOUNTER_GROUP_HLSQ) ||
+			(group == KGSL_PERFCOUNTER_GROUP_SP) ||
+			(group == KGSL_PERFCOUNTER_GROUP_TP))
+			return true;
+		else
+			return false;
+	}
+
+	return true;
+}
+
 static int _perfcounter_enable_default(struct adreno_device *adreno_dev,
 		struct adreno_perfcounters *counters, unsigned int group,
 		unsigned int counter, unsigned int countable)
@@ -698,6 +790,7 @@
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
 	struct adreno_perfcount_register *reg;
+	struct adreno_perfcount_group *grp;
 	int i;
 	int ret = 0;
 
@@ -712,15 +805,20 @@
 			if (countable == invalid_countable.countables[i])
 				return -EACCES;
 	}
-	reg = &(counters->groups[group].regs[counter]);
+	grp = &(counters->groups[group]);
+	reg = &(grp->regs[counter]);
 
-	if (!adreno_is_a6xx(adreno_dev) &&
-			test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) {
+	if (_perfcounter_inline_update(adreno_dev, group) &&
+		test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) {
 		struct adreno_ringbuffer *rb = &adreno_dev->ringbuffers[0];
 		unsigned int buf[4];
 		unsigned int *cmds = buf;
 		int ret;
 
+		if (gpudev->perfcounter_update && (grp->flags &
+				ADRENO_PERFCOUNTER_GROUP_RESTORE))
+			gpudev->perfcounter_update(adreno_dev, reg, false);
+
 		cmds += cp_wait_for_idle(adreno_dev, cmds);
 		*cmds++ = cp_register(adreno_dev, reg->select, 1);
 		*cmds++ = countable;
@@ -757,12 +855,16 @@
 		}
 	} else {
 		/* Select the desired perfcounter */
-		kgsl_regwrite(device, reg->select, countable);
+		if (gpudev->perfcounter_update && (grp->flags &
+				ADRENO_PERFCOUNTER_GROUP_RESTORE))
+			ret = gpudev->perfcounter_update(adreno_dev, reg, true);
+		else
+			kgsl_regwrite(device, reg->select, countable);
 	}
 
 	if (!ret)
 		reg->value = 0;
-	return 0;
+	return ret;
 }
 
 /**
diff --git a/drivers/gpu/msm/adreno_perfcounter.h b/drivers/gpu/msm/adreno_perfcounter.h
index 8c4db38..bcbc738 100644
--- a/drivers/gpu/msm/adreno_perfcounter.h
+++ b/drivers/gpu/msm/adreno_perfcounter.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2015, 2017 The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -70,6 +70,13 @@
 
 #define ADRENO_PERFCOUNTER_GROUP_FIXED BIT(0)
 
+/*
+ * ADRENO_PERFCOUNTER_GROUP_RESTORE indicates CP needs to restore the select
+ * registers of this perfcounter group as part of preemption and IFPC
+ */
+#define ADRENO_PERFCOUNTER_GROUP_RESTORE BIT(1)
+
+
 /**
  * adreno_perfcounts: all available perfcounter groups
  * @groups: available groups for this device
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 8b87ee2..01d9f71 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -25,6 +25,7 @@
 #include "adreno_iommu.h"
 #include "adreno_pm4types.h"
 #include "adreno_ringbuffer.h"
+#include "adreno_trace.h"
 
 #include "a3xx_reg.h"
 #include "adreno_a5xx.h"
@@ -37,6 +38,7 @@
 	((_rb)->buffer_desc.gpuaddr + ((_pos) * sizeof(unsigned int)))
 
 static void adreno_get_submit_time(struct adreno_device *adreno_dev,
+		struct adreno_ringbuffer *rb,
 		struct adreno_submit_time *time)
 {
 	unsigned long flags;
@@ -66,6 +68,9 @@
 	} else
 		time->ticks = 0;
 
+	/* Trace the GPU time to create a mapping to ftrace time */
+	trace_adreno_cmdbatch_sync(rb->drawctxt_active, time->ticks);
+
 	/* Get the kernel clock for time since boot */
 	time->ktime = local_clock();
 
@@ -75,42 +80,6 @@
 	local_irq_restore(flags);
 }
 
-/*
- * Wait time before trying to write the register again.
- * Hopefully the GMU has finished waking up during this delay.
- */
-#define GMU_WAKEUP_DELAY 50
-/* Max amount of tries to wake up the GMU. */
-#define GMU_WAKEUP_RETRY_MAX 20
-
-/*
- * Check the WRITEDROPPED0 bit in the
- * FENCE_STATUS regsiter to check if the write went
- * through. If it didn't then we retry the write.
- */
-static inline void _gmu_wptr_update_if_dropped(struct adreno_device *adreno_dev,
-		struct adreno_ringbuffer *rb)
-{
-	unsigned int val, i;
-
-	for (i = 0; i < GMU_WAKEUP_RETRY_MAX; i++) {
-		adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_AHB_FENCE_STATUS,
-				&val);
-
-		/* If !writedropped, then wptr update was successful */
-		if (!(val & 0x1))
-			return;
-
-		/* Wait a small amount of time before trying again */
-		udelay(GMU_WAKEUP_DELAY);
-
-		/* Try to write WPTR again */
-		adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR, rb->_wptr);
-	}
-
-	dev_err(adreno_dev->dev.dev, "GMU WPTR update timed out\n");
-}
-
 static void adreno_ringbuffer_wptr(struct adreno_device *adreno_dev,
 		struct adreno_ringbuffer *rb)
 {
@@ -125,15 +94,14 @@
 			 * been submitted.
 			 */
 			kgsl_pwrscale_busy(KGSL_DEVICE(adreno_dev));
-			adreno_writereg(adreno_dev, ADRENO_REG_CP_RB_WPTR,
-				rb->_wptr);
 
 			/*
-			 * If GMU, ensure the write posted after a possible
+			 * Ensure the write posted after a possible
 			 * GMU wakeup (write could have dropped during wakeup)
 			 */
-			if (kgsl_gmu_isenabled(KGSL_DEVICE(adreno_dev)))
-				_gmu_wptr_update_if_dropped(adreno_dev, rb);
+			adreno_gmu_fenced_write(adreno_dev,
+				ADRENO_REG_CP_RB_WPTR, rb->_wptr,
+				FENCE_STATUS_WRITEDROPPED0_MASK);
 
 		}
 	}
@@ -148,7 +116,7 @@
 	struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb);
 
 	if (time != NULL)
-		adreno_get_submit_time(adreno_dev, time);
+		adreno_get_submit_time(adreno_dev, rb, time);
 
 	adreno_ringbuffer_wptr(adreno_dev, rb);
 }
@@ -218,8 +186,9 @@
 	FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
 		kgsl_sharedmem_set(device, &(rb->buffer_desc),
 				0, 0xAA, KGSL_RB_SIZE);
-		kgsl_sharedmem_writel(device, &device->scratch,
-				SCRATCH_RPTR_OFFSET(rb->id), 0);
+		if (!adreno_is_a3xx(adreno_dev))
+			kgsl_sharedmem_writel(device, &device->scratch,
+					SCRATCH_RPTR_OFFSET(rb->id), 0);
 		rb->wptr = 0;
 		rb->_wptr = 0;
 		rb->wptr_preempt_end = 0xFFFFFFFF;
@@ -280,9 +249,16 @@
 
 int adreno_ringbuffer_probe(struct adreno_device *adreno_dev, bool nopreempt)
 {
-	int status = 0;
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-	int i;
+	int i, status;
+
+	if (!adreno_is_a3xx(adreno_dev)) {
+		status = kgsl_allocate_global(device, &device->scratch,
+				PAGE_SIZE, 0, 0, "scratch");
+		if (status != 0)
+			return status;
+	}
 
 	if (nopreempt == false)
 		adreno_dev->num_ringbuffers = gpudev->num_prio_levels;
@@ -318,9 +294,13 @@
 
 void adreno_ringbuffer_close(struct adreno_device *adreno_dev)
 {
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 	struct adreno_ringbuffer *rb;
 	int i;
 
+	if (!adreno_is_a3xx(adreno_dev))
+		kgsl_free_global(device, &device->scratch);
+
 	FOR_EACH_RINGBUFFER(adreno_dev, rb, i)
 		_adreno_ringbuffer_close(adreno_dev, rb);
 }
@@ -406,6 +386,7 @@
 	struct kgsl_context *context = NULL;
 	bool secured_ctxt = false;
 	static unsigned int _seq_cnt;
+	struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
 
 	if (drawctxt != NULL && kgsl_context_detached(&drawctxt->base) &&
 		!(flags & KGSL_CMD_FLAGS_INTERNAL_ISSUE))
@@ -462,7 +443,8 @@
 	total_sizedwords += (secured_ctxt) ? 26 : 0;
 
 	/* _seq mem write for each submission */
-	total_sizedwords += 4;
+	if (adreno_is_a5xx(adreno_dev))
+		total_sizedwords += 4;
 
 	/* context rollover */
 	if (adreno_is_a3xx(adreno_dev))
@@ -474,11 +456,11 @@
 
 	if (gpudev->preemption_pre_ibsubmit &&
 			adreno_is_preemption_execution_enabled(adreno_dev))
-		total_sizedwords += 22;
+		total_sizedwords += 27;
 
 	if (gpudev->preemption_post_ibsubmit &&
 			adreno_is_preemption_execution_enabled(adreno_dev))
-		total_sizedwords += 5;
+		total_sizedwords += 10;
 
 	/*
 	 * a5xx uses 64 bit memory address. pm4 commands that involve read/write
@@ -505,14 +487,20 @@
 	if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP)
 		total_sizedwords += 9;
 
-	/*
-	 * WAIT_MEM_WRITES - needed in the stall on fault case
-	 * to prevent out of order CP operations that can result
-	 * in a CACHE_FLUSH_TS interrupt storm
-	 */
-	if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
+	/* Don't insert any commands if stall on fault is not supported. */
+	if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) {
+		/*
+		 * WAIT_MEM_WRITES - needed in the stall on fault case
+		 * to prevent out of order CP operations that can result
+		 * in a CACHE_FLUSH_TS interrupt storm
+		 */
+		if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
 				&adreno_dev->ft_pf_policy))
-		total_sizedwords += 1;
+			total_sizedwords += 1;
+	}
+
+	if (gpudev->set_marker)
+		total_sizedwords += 4;
 
 	ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords);
 	if (IS_ERR(ringcmds))
@@ -533,6 +521,14 @@
 		*ringcmds++ = KGSL_CMD_INTERNAL_IDENTIFIER;
 	}
 
+	if (gpudev->set_marker) {
+		/* Firmware versions before 1.49 do not support IFPC markers */
+		if (adreno_is_a6xx(adreno_dev) && (fw->version & 0xFFF) < 0x149)
+			ringcmds += gpudev->set_marker(ringcmds, IB1LIST_START);
+		else
+			ringcmds += gpudev->set_marker(ringcmds, IFPC_DISABLE);
+	}
+
 	if (flags & KGSL_CMD_FLAGS_PWRON_FIXUP) {
 		/* Disable protected mode for the fixup */
 		*ringcmds++ = cp_packet(adreno_dev, CP_SET_PROTECTED_MODE, 1);
@@ -601,23 +597,29 @@
 	if (profile_ready)
 		adreno_profile_postib_processing(adreno_dev, &flags, &ringcmds);
 
-	/*
-	 * WAIT_MEM_WRITES - needed in the stall on fault case to prevent
-	 * out of order CP operations that can result in a CACHE_FLUSH_TS
-	 * interrupt storm
-	 */
-	if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
+	/* Don't insert any commands if stall on fault is not supported. */
+	if ((ADRENO_GPUREV(adreno_dev) > 500) && !adreno_is_a510(adreno_dev)) {
+		/*
+		 * WAIT_MEM_WRITES - needed in the stall on fault case
+		 * to prevent out of order CP operations that can result
+		 * in a CACHE_FLUSH_TS interrupt storm
+		 */
+		if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE,
 				&adreno_dev->ft_pf_policy))
-		*ringcmds++ = cp_packet(adreno_dev, CP_WAIT_MEM_WRITES, 0);
+			*ringcmds++ = cp_packet(adreno_dev,
+						CP_WAIT_MEM_WRITES, 0);
+	}
 
 	/*
 	 * Do a unique memory write from the GPU. This can be used in
 	 * early detection of timestamp interrupt storms to stave
 	 * off system collapse.
 	 */
-	ringcmds += cp_mem_write(adreno_dev, ringcmds,
-		MEMSTORE_ID_GPU_ADDR(device, KGSL_MEMSTORE_GLOBAL,
-			ref_wait_ts), ++_seq_cnt);
+	if (adreno_is_a5xx(adreno_dev))
+		ringcmds += cp_mem_write(adreno_dev, ringcmds,
+				MEMSTORE_ID_GPU_ADDR(device,
+				KGSL_MEMSTORE_GLOBAL,
+				ref_wait_ts), ++_seq_cnt);
 
 	/*
 	 * end-of-pipeline timestamp.  If per context timestamps is not
@@ -645,6 +647,13 @@
 		*ringcmds++ = timestamp;
 	}
 
+	if (gpudev->set_marker) {
+		if (adreno_is_a6xx(adreno_dev) && (fw->version & 0xFFF) < 0x149)
+			ringcmds += gpudev->set_marker(ringcmds, IB1LIST_END);
+		else
+			ringcmds += gpudev->set_marker(ringcmds, IFPC_ENABLE);
+	}
+
 	if (adreno_is_a3xx(adreno_dev)) {
 		/* Dummy set-constant to trigger context rollover */
 		*ringcmds++ = cp_packet(adreno_dev, CP_SET_CONSTANT, 2);
@@ -758,8 +767,9 @@
 	struct kgsl_drawobj_profiling_buffer *profile_buffer = NULL;
 	unsigned int dwords = 0;
 	struct adreno_submit_time local;
-
 	struct kgsl_mem_entry *entry = cmdobj->profiling_buf_entry;
+	struct adreno_firmware *fw = ADRENO_FW(adreno_dev, ADRENO_FW_SQE);
+	bool set_ib1list_marker = false;
 
 	if (entry)
 		profile_buffer = kgsl_gpuaddr_to_vaddr(&entry->memdesc,
@@ -869,7 +879,18 @@
 			dwords += 8;
 	}
 
-	if (gpudev->set_marker)
+	/*
+	 * Prior to SQE FW version 1.49, there was only one marker for
+	 * both preemption and IFPC. Only include the IB1LIST markers if
+	 * we are using a firmware that supports them.
+	 */
+	if (gpudev->set_marker && numibs && adreno_is_a6xx(adreno_dev) &&
+			((fw->version & 0xFFF) >= 0x149)) {
+		set_ib1list_marker = true;
+		dwords += 4;
+	}
+
+	if (gpudev->ccu_invalidate)
 		dwords += 4;
 
 	link = kcalloc(dwords, sizeof(unsigned int), GFP_KERNEL);
@@ -901,10 +922,10 @@
 			gpu_ticks_submitted));
 	}
 
-	if (gpudev->set_marker)
-		cmds += gpudev->set_marker(cmds, 1);
-
 	if (numibs) {
+		if (set_ib1list_marker)
+			cmds += gpudev->set_marker(cmds, IB1LIST_START);
+
 		list_for_each_entry(ib, &cmdobj->cmdlist, node) {
 			/*
 			 * Skip 0 sized IBs - these are presumed to have been
@@ -923,10 +944,13 @@
 			/* preamble is required on only for first command */
 			use_preamble = false;
 		}
+
+		if (set_ib1list_marker)
+			cmds += gpudev->set_marker(cmds, IB1LIST_END);
 	}
 
-	if (gpudev->set_marker)
-		cmds += gpudev->set_marker(cmds, 0);
+	if (gpudev->ccu_invalidate)
+		cmds += gpudev->ccu_invalidate(adreno_dev, cmds);
 
 	if (adreno_is_preemption_execution_enabled(adreno_dev)) {
 		if (gpudev->preemption_yield_enable)
diff --git a/drivers/gpu/msm/adreno_ringbuffer.h b/drivers/gpu/msm/adreno_ringbuffer.h
index 72fc5bf3..fbee627 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.h
+++ b/drivers/gpu/msm/adreno_ringbuffer.h
@@ -92,6 +92,8 @@
  * @drawctxt_active: The last pagetable that this ringbuffer is set to
  * @preemption_desc: The memory descriptor containing
  * preemption info written/read by CP
+ * @secure_preemption_desc: The memory descriptor containing
+ * preemption info written/read by CP for secure contexts
  * @perfcounter_save_restore_desc: Used by CP to save/restore the perfcounter
  * values across preemption
  * @pagetable_desc: Memory to hold information about the pagetables being used
@@ -120,6 +122,7 @@
 	struct kgsl_event_group events;
 	struct adreno_context *drawctxt_active;
 	struct kgsl_memdesc preemption_desc;
+	struct kgsl_memdesc secure_preemption_desc;
 	struct kgsl_memdesc perfcounter_save_restore_desc;
 	struct kgsl_memdesc pagetable_desc;
 	struct adreno_dispatcher_drawqueue dispatch_q;
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 0840aba..b5999e6 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -945,6 +945,24 @@
 
 }
 
+/* adreno_snapshot_gmu - Snapshot the Adreno GMU state
+ * @device - KGSL device to snapshot
+ * @snapshot - Pointer to the snapshot instance
+ * This is a hook function called by kgsl_snapshot to snapshot the
+ * Adreno specific information for the GMU snapshot.  In turn, this function
+ * calls the GMU specific snapshot function to get core specific information.
+ */
+void adreno_snapshot_gmu(struct kgsl_device *device,
+		struct kgsl_snapshot *snapshot)
+{
+	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+
+	/* Add GMU specific sections */
+	if (gpudev->snapshot_gmu)
+		gpudev->snapshot_gmu(adreno_dev, snapshot);
+}
+
 /*
  * adreno_snapshot_cp_roq - Dump CP merciu data in snapshot
  * @device: Device being snapshotted
diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index b06aa98..e309ab0 100644
--- a/drivers/gpu/msm/adreno_sysfs.c
+++ b/drivers/gpu/msm/adreno_sysfs.c
@@ -29,6 +29,13 @@
 	.store = _ ## _name ## _store, \
 }
 
+#define _ADRENO_SYSFS_ATTR_RO(_name, __show) \
+struct adreno_sysfs_attribute adreno_attr_##_name = { \
+	.attr = __ATTR(_name, 0644, __show, NULL), \
+	.show = _ ## _name ## _show, \
+	.store = NULL, \
+}
+
 #define ADRENO_SYSFS_ATTR(_a) \
 	container_of((_a), struct adreno_sysfs_attribute, attr)
 
@@ -54,38 +61,50 @@
 static int _preempt_level_store(struct adreno_device *adreno_dev,
 		unsigned int val)
 {
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
 	if (val <= 2)
-		adreno_dev->preempt_level = val;
+		preempt->preempt_level = val;
 	return 0;
 }
 
 static unsigned int _preempt_level_show(struct adreno_device *adreno_dev)
 {
-	return adreno_dev->preempt_level;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	return preempt->preempt_level;
 }
 
 static int _usesgmem_store(struct adreno_device *adreno_dev,
 		unsigned int val)
 {
-	adreno_dev->usesgmem = val ? 1 : 0;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	preempt->usesgmem = val ? 1 : 0;
 	return 0;
 }
 
 static unsigned int _usesgmem_show(struct adreno_device *adreno_dev)
 {
-	return adreno_dev->usesgmem;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	return preempt->usesgmem;
 }
 
 static int _skipsaverestore_store(struct adreno_device *adreno_dev,
 		unsigned int val)
 {
-	adreno_dev->skipsaverestore = val ? 1 : 0;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	preempt->skipsaverestore = val ? 1 : 0;
 	return 0;
 }
 
 static unsigned int _skipsaverestore_show(struct adreno_device *adreno_dev)
 {
-	return adreno_dev->skipsaverestore;
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	return preempt->skipsaverestore;
 }
 
 static int _ft_pagefault_policy_store(struct adreno_device *adreno_dev,
@@ -165,7 +184,6 @@
 
 	if (test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv)) {
 		kgsl_pwrctrl_change_state(device, KGSL_STATE_ACTIVE);
-		adreno_irqctrl(adreno_dev, 1);
 	} else if (device->state == KGSL_STATE_INIT) {
 		ret = -EACCES;
 		change_bit(ADRENO_DEVICE_HANG_INTR, &adreno_dev->priv);
@@ -221,6 +239,23 @@
 	return 0;
 }
 
+static int _gmu_idle_level_store(struct adreno_device *adreno_dev,
+		unsigned int val)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+
+	mutex_lock(&device->mutex);
+
+	/* Power down the GPU before changing the idle level */
+	kgsl_pwrctrl_change_state(device, KGSL_STATE_SUSPEND);
+	gmu->idle_level = val;
+	kgsl_pwrctrl_change_state(device, KGSL_STATE_SLUMBER);
+
+	mutex_unlock(&device->mutex);
+	return 0;
+}
+
 static unsigned int _preemption_show(struct adreno_device *adreno_dev)
 {
 	return adreno_is_preemption_execution_enabled(adreno_dev);
@@ -269,6 +304,47 @@
 	return test_bit(ADRENO_LM_CTRL, &adreno_dev->pwrctrl_flag);
 }
 
+static int _ifpc_store(struct adreno_device *adreno_dev, unsigned int val)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+	unsigned int requested_idle_level;
+
+	if (!kgsl_gmu_isenabled(device) ||
+			!ADRENO_FEATURE(adreno_dev, ADRENO_IFPC))
+		return -EINVAL;
+
+	if ((val && gmu->idle_level >= GPU_HW_IFPC) ||
+			(!val && gmu->idle_level < GPU_HW_IFPC))
+		return 0;
+
+	if (val)
+		requested_idle_level = GPU_HW_IFPC;
+	else {
+		if (ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC))
+			requested_idle_level = GPU_HW_SPTP_PC;
+		else
+			requested_idle_level = GPU_HW_ACTIVE;
+	}
+
+	return _gmu_idle_level_store(adreno_dev, requested_idle_level);
+}
+
+static unsigned int _ifpc_show(struct adreno_device *adreno_dev)
+{
+	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
+	struct gmu_device *gmu = &device->gmu;
+
+	return kgsl_gmu_isenabled(device) && gmu->idle_level >= GPU_HW_IFPC;
+}
+
+static unsigned int _preempt_count_show(struct adreno_device *adreno_dev)
+{
+	struct adreno_preemption *preempt = &adreno_dev->preempt;
+
+	return preempt->count;
+}
+
 static ssize_t _sysfs_store_u32(struct device *dev,
 		struct device_attribute *attr,
 		const char *buf, size_t count)
@@ -349,9 +425,13 @@
 #define ADRENO_SYSFS_U32(_name) \
 	_ADRENO_SYSFS_ATTR(_name, _sysfs_show_u32, _sysfs_store_u32)
 
+#define ADRENO_SYSFS_RO_U32(_name) \
+	_ADRENO_SYSFS_ATTR_RO(_name, _sysfs_show_u32)
+
 static ADRENO_SYSFS_U32(ft_policy);
 static ADRENO_SYSFS_U32(ft_pagefault_policy);
 static ADRENO_SYSFS_U32(preempt_level);
+static ADRENO_SYSFS_RO_U32(preempt_count);
 static ADRENO_SYSFS_BOOL(usesgmem);
 static ADRENO_SYSFS_BOOL(skipsaverestore);
 static ADRENO_SYSFS_BOOL(ft_long_ib_detect);
@@ -367,6 +447,7 @@
 static ADRENO_SYSFS_BOOL(preemption);
 static ADRENO_SYSFS_BOOL(hwcg);
 static ADRENO_SYSFS_BOOL(throttling);
+static ADRENO_SYSFS_BOOL(ifpc);
 
 
 
@@ -387,6 +468,8 @@
 	&adreno_attr_preempt_level.attr,
 	&adreno_attr_usesgmem.attr,
 	&adreno_attr_skipsaverestore.attr,
+	&adreno_attr_ifpc.attr,
+	&adreno_attr_preempt_count.attr,
 	NULL,
 };
 
diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h
index 7bc4c93..e33060a 100644
--- a/drivers/gpu/msm/adreno_trace.h
+++ b/drivers/gpu/msm/adreno_trace.h
@@ -148,6 +148,29 @@
 	)
 );
 
+TRACE_EVENT(adreno_cmdbatch_sync,
+	TP_PROTO(struct adreno_context *drawctxt,
+		uint64_t ticks),
+	TP_ARGS(drawctxt, ticks),
+	TP_STRUCT__entry(
+		__field(unsigned int, id)
+		__field(unsigned int, timestamp)
+		__field(uint64_t, ticks)
+		__field(int, prio)
+	),
+	TP_fast_assign(
+		__entry->id = drawctxt->base.id;
+		__entry->timestamp = drawctxt->timestamp;
+		__entry->ticks = ticks;
+		__entry->prio = drawctxt->base.priority;
+	),
+	TP_printk(
+		"ctx=%u ctx_prio=%d ts=%u ticks=%lld",
+			__entry->id, __entry->prio, __entry->timestamp,
+			__entry->ticks
+	)
+);
+
 TRACE_EVENT(adreno_cmdbatch_fault,
 	TP_PROTO(struct kgsl_drawobj_cmd *cmdobj, unsigned int fault),
 	TP_ARGS(cmdobj, fault),
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 9968d8c..a5e3804 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1131,8 +1131,6 @@
 		atomic_inc(&device->active_cnt);
 		kgsl_sharedmem_set(device, &device->memstore, 0, 0,
 				device->memstore.size);
-		kgsl_sharedmem_set(device, &device->scratch, 0, 0,
-				device->scratch.size);
 
 		result = device->ftbl->init(device);
 		if (result)
@@ -1807,18 +1805,15 @@
 
 long gpumem_free_entry(struct kgsl_mem_entry *entry)
 {
-	pid_t ptname = 0;
-
 	if (!kgsl_mem_entry_set_pend(entry))
 		return -EBUSY;
 
 	trace_kgsl_mem_free(entry);
-
-	if (entry->memdesc.pagetable != NULL)
-		ptname = entry->memdesc.pagetable->name;
-
-	kgsl_memfree_add(entry->priv->pid, ptname, entry->memdesc.gpuaddr,
-		entry->memdesc.size, entry->memdesc.flags);
+	kgsl_memfree_add(entry->priv->pid,
+			entry->memdesc.pagetable ?
+				entry->memdesc.pagetable->name : 0,
+			entry->memdesc.gpuaddr, entry->memdesc.size,
+			entry->memdesc.flags);
 
 	kgsl_mem_entry_put(entry);
 
@@ -1837,6 +1832,12 @@
 	/* Free the memory for all event types */
 	trace_kgsl_mem_timestamp_free(device, entry, KGSL_CONTEXT_ID(context),
 		timestamp, 0);
+	kgsl_memfree_add(entry->priv->pid,
+			entry->memdesc.pagetable ?
+				entry->memdesc.pagetable->name : 0,
+			entry->memdesc.gpuaddr, entry->memdesc.size,
+			entry->memdesc.flags);
+
 	kgsl_mem_entry_put(entry);
 }
 
@@ -1930,6 +1931,13 @@
 {
 	struct kgsl_mem_entry *entry = priv;
 
+	trace_kgsl_mem_free(entry);
+	kgsl_memfree_add(entry->priv->pid,
+			entry->memdesc.pagetable ?
+				entry->memdesc.pagetable->name : 0,
+			entry->memdesc.gpuaddr, entry->memdesc.size,
+			entry->memdesc.flags);
+
 	INIT_WORK(&entry->work, _deferred_put);
 	queue_work(kgsl_driver.mem_workqueue, &entry->work);
 	return true;
@@ -1962,15 +1970,15 @@
 	handle = kgsl_sync_fence_async_wait(event.fd,
 		gpuobj_free_fence_func, entry, NULL, 0);
 
-	/* if handle is NULL the fence has already signaled */
-	if (handle == NULL)
-		return gpumem_free_entry(entry);
-
 	if (IS_ERR(handle)) {
 		kgsl_mem_entry_unset_pend(entry);
 		return PTR_ERR(handle);
 	}
 
+	/* if handle is NULL the fence has already signaled */
+	if (handle == NULL)
+		gpuobj_free_fence_func(entry);
+
 	return 0;
 }
 
@@ -2280,7 +2288,7 @@
 		struct kgsl_mem_entry *entry,
 		struct kgsl_gpuobj_import *param)
 {
-	struct kgsl_gpuobj_import_useraddr useraddr;
+	struct kgsl_gpuobj_import_useraddr useraddr = {0};
 	int ret;
 
 	param->flags &= KGSL_MEMFLAGS_GPUREADONLY
@@ -2763,6 +2771,10 @@
 	int cacheop;
 	int mode;
 
+	 /* Cache ops are not allowed on secure memory */
+	if (entry->memdesc.flags & KGSL_MEMFLAGS_SECURE)
+		return 0;
+
 	/*
 	 * Flush is defined as (clean | invalidate).  If both bits are set, then
 	 * do a flush, otherwise check for the individual bits and clean or inv
@@ -2844,10 +2856,8 @@
 	bool ret = (kgsl_driver.full_cache_threshold != 0) &&
 		(size >= kgsl_driver.full_cache_threshold) &&
 		(op == KGSL_GPUMEM_CACHE_FLUSH);
-	if (ret) {
-		trace_kgsl_mem_sync_full_cache(actual_count, op_size);
+	if (ret)
 		flush_cache_all();
-	}
 	return ret;
 }
 #endif
@@ -2911,8 +2921,10 @@
 		entries[actual_count++] = entry;
 
 		full_flush  = check_full_flush(op_size, param->op);
-		if (full_flush)
+		if (full_flush) {
+			trace_kgsl_mem_sync_full_cache(actual_count, op_size);
 			break;
+		}
 
 		last_id = id;
 	}
@@ -3000,8 +3012,10 @@
 			size += (entries[i]->memdesc.size - objs[i].offset);
 
 		full_flush = check_full_flush(size, objs[i].op);
-		if (full_flush)
+		if (full_flush) {
+			trace_kgsl_mem_sync_full_cache(i, size);
 			break;
+		}
 
 		ptr += sizeof(*objs);
 	}
@@ -3461,6 +3475,7 @@
 	return 0;
 }
 
+/* entry->bind_lock must be held by the caller */
 static int _sparse_add_to_bind_tree(struct kgsl_mem_entry *entry,
 		uint64_t v_offset,
 		struct kgsl_memdesc *memdesc,
@@ -3471,7 +3486,7 @@
 	struct sparse_bind_object *new;
 	struct rb_node **node, *parent = NULL;
 
-	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	new = kzalloc(sizeof(*new), GFP_ATOMIC);
 	if (new == NULL)
 		return -ENOMEM;
 
@@ -3489,10 +3504,16 @@
 		parent = *node;
 		this = rb_entry(parent, struct sparse_bind_object, node);
 
-		if (new->v_off < this->v_off)
+		if ((new->v_off < this->v_off) &&
+			((new->v_off + new->size) <= this->v_off))
 			node = &parent->rb_left;
-		else if (new->v_off > this->v_off)
+		else if ((new->v_off > this->v_off) &&
+			(new->v_off >= (this->v_off + this->size)))
 			node = &parent->rb_right;
+		else {
+			kfree(new);
+			return -EADDRINUSE;
+		}
 	}
 
 	rb_link_node(&new->node, parent, node);
@@ -3505,7 +3526,6 @@
 		struct sparse_bind_object *obj,
 		uint64_t v_offset, uint64_t size)
 {
-	spin_lock(&entry->bind_lock);
 	if (v_offset == obj->v_off && size >= obj->size) {
 		/*
 		 * We are all encompassing, remove the entry and free
@@ -3538,7 +3558,6 @@
 
 		obj->size = v_offset - obj->v_off;
 
-		spin_unlock(&entry->bind_lock);
 		ret = _sparse_add_to_bind_tree(entry, v_offset + size,
 				obj->p_memdesc,
 				obj->p_off + (v_offset - obj->v_off) + size,
@@ -3548,11 +3567,10 @@
 		return ret;
 	}
 
-	spin_unlock(&entry->bind_lock);
-
 	return 0;
 }
 
+/* entry->bind_lock must be held by the caller */
 static struct sparse_bind_object *_find_containing_bind_obj(
 		struct kgsl_mem_entry *entry,
 		uint64_t offset, uint64_t size)
@@ -3560,8 +3578,6 @@
 	struct sparse_bind_object *obj = NULL;
 	struct rb_node *node = entry->bind_tree.rb_node;
 
-	spin_lock(&entry->bind_lock);
-
 	while (node != NULL) {
 		obj = rb_entry(node, struct sparse_bind_object, node);
 
@@ -3580,33 +3596,16 @@
 		}
 	}
 
-	spin_unlock(&entry->bind_lock);
-
 	return obj;
 }
 
+/* entry->bind_lock must be held by the caller */
 static int _sparse_unbind(struct kgsl_mem_entry *entry,
 		struct sparse_bind_object *bind_obj,
 		uint64_t offset, uint64_t size)
 {
-	struct kgsl_memdesc *memdesc = bind_obj->p_memdesc;
-	struct kgsl_pagetable *pt = memdesc->pagetable;
 	int ret;
 
-	if (memdesc->cur_bindings < (size / PAGE_SIZE))
-		return -EINVAL;
-
-	memdesc->cur_bindings -= size / PAGE_SIZE;
-
-	ret = kgsl_mmu_unmap_offset(pt, memdesc,
-			entry->memdesc.gpuaddr, offset, size);
-	if (ret)
-		return ret;
-
-	ret = kgsl_mmu_sparse_dummy_map(pt, &entry->memdesc, offset, size);
-	if (ret)
-		return ret;
-
 	ret = _sparse_rm_from_bind_tree(entry, bind_obj, offset, size);
 	if (ret == 0) {
 		atomic_long_sub(size, &kgsl_driver.stats.mapped);
@@ -3620,6 +3619,8 @@
 	struct kgsl_mem_entry *virt_entry)
 {
 	struct sparse_bind_object *bind_obj;
+	struct kgsl_memdesc *memdesc;
+	struct kgsl_pagetable *pt;
 	int ret = 0;
 	uint64_t size = obj->size;
 	uint64_t tmp_size = obj->size;
@@ -3627,9 +3628,14 @@
 
 	while (size > 0 && ret == 0) {
 		tmp_size = size;
+
+		spin_lock(&virt_entry->bind_lock);
 		bind_obj = _find_containing_bind_obj(virt_entry, offset, size);
-		if (bind_obj == NULL)
+
+		if (bind_obj == NULL) {
+			spin_unlock(&virt_entry->bind_lock);
 			return 0;
+		}
 
 		if (bind_obj->v_off > offset) {
 			tmp_size = size - bind_obj->v_off - offset;
@@ -3646,7 +3652,28 @@
 				tmp_size = bind_obj->size;
 		}
 
+		memdesc = bind_obj->p_memdesc;
+		pt = memdesc->pagetable;
+
+		if (memdesc->cur_bindings < (tmp_size / PAGE_SIZE)) {
+			spin_unlock(&virt_entry->bind_lock);
+			return -EINVAL;
+		}
+
+		memdesc->cur_bindings -= tmp_size / PAGE_SIZE;
+
 		ret = _sparse_unbind(virt_entry, bind_obj, offset, tmp_size);
+		spin_unlock(&virt_entry->bind_lock);
+
+		ret = kgsl_mmu_unmap_offset(pt, memdesc,
+				virt_entry->memdesc.gpuaddr, offset, tmp_size);
+		if (ret)
+			return ret;
+
+		ret = kgsl_mmu_sparse_dummy_map(pt, memdesc, offset, tmp_size);
+		if (ret)
+			return ret;
+
 		if (ret == 0) {
 			offset += tmp_size;
 			size -= tmp_size;
@@ -3707,8 +3734,11 @@
 		return ret;
 	}
 
+	spin_lock(&virt_entry->bind_lock);
 	ret = _sparse_add_to_bind_tree(virt_entry, v_offset, memdesc,
 			p_offset, size, flags);
+	spin_unlock(&virt_entry->bind_lock);
+
 	if (ret == 0)
 		memdesc->cur_bindings += size / PAGE_SIZE;
 
@@ -4362,13 +4392,13 @@
 	if (!kgsl_memdesc_use_cpu_map(&entry->memdesc)) {
 		val = get_unmapped_area(NULL, addr, len, 0, flags);
 		if (IS_ERR_VALUE(val))
-			KGSL_MEM_ERR(device,
+			KGSL_DRV_ERR_RATELIMIT(device,
 				"get_unmapped_area: pid %d addr %lx pgoff %lx len %ld failed error %d\n",
 				private->pid, addr, pgoff, len, (int) val);
 	} else {
 		val = _get_svm_area(private, entry, addr, len, flags);
 		if (IS_ERR_VALUE(val))
-			KGSL_MEM_ERR(device,
+			KGSL_DRV_ERR_RATELIMIT(device,
 				"_get_svm_area: pid %d mmap_base %lx addr %lx pgoff %lx len %ld failed error %d\n",
 				private->pid, current->mm->mmap_base, addr,
 				pgoff, len, (int) val);
@@ -4669,11 +4699,6 @@
 	if (status != 0)
 		goto error_close_mmu;
 
-	status = kgsl_allocate_global(device, &device->scratch,
-		PAGE_SIZE, 0, 0, "scratch");
-	if (status != 0)
-		goto error_free_memstore;
-
 	/*
 	 * The default request type PM_QOS_REQ_ALL_CORES is
 	 * applicable to all CPU cores that are online and
@@ -4719,8 +4744,6 @@
 
 	return 0;
 
-error_free_memstore:
-	kgsl_free_global(device, &device->memstore);
 error_close_mmu:
 	kgsl_mmu_close(device);
 error_pwrctrl_close:
@@ -4748,8 +4771,6 @@
 
 	idr_destroy(&device->context_idr);
 
-	kgsl_free_global(device, &device->scratch);
-
 	kgsl_free_global(device, &device->memstore);
 
 	kgsl_mmu_close(device);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index f80da79..023e63e 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -75,6 +75,7 @@
  * Used Data:
  * Offset: Length(bytes): What
  * 0x0: 4 * KGSL_PRIORITY_MAX_RB_LEVELS: RB0 RPTR
+ * 0x10: 8 * KGSL_PRIORITY_MAX_RB_LEVELS: RB0 CTXT RESTORE ADDR
  */
 
 /* Shadow global helpers */
@@ -82,6 +83,13 @@
 #define SCRATCH_RPTR_GPU_ADDR(dev, id) \
 	((dev)->scratch.gpuaddr + SCRATCH_RPTR_OFFSET(id))
 
+#define SCRATCH_PREEMPTION_CTXT_RESTORE_ADDR_OFFSET(id) \
+	(SCRATCH_RPTR_OFFSET(KGSL_PRIORITY_MAX_RB_LEVELS) + \
+	((id) * sizeof(uint64_t)))
+#define SCRATCH_PREEMPTION_CTXT_RESTORE_GPU_ADDR(dev, id) \
+	((dev)->scratch.gpuaddr + \
+	SCRATCH_PREEMPTION_CTXT_RESTORE_ADDR_OFFSET(id))
+
 /* Timestamp window used to detect rollovers (half of integer range) */
 #define KGSL_TIMESTAMP_WINDOW 0x80000000
 
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index e339a08..834706a 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -303,6 +303,7 @@
 	if (!(m->flags & KGSL_MEMFLAGS_SPARSE_VIRT))
 		return 0;
 
+	spin_lock(&entry->bind_lock);
 	node = rb_first(&entry->bind_tree);
 
 	while (node != NULL) {
@@ -313,6 +314,7 @@
 				obj->v_off, obj->size, obj->p_off);
 		node = rb_next(node);
 	}
+	spin_unlock(&entry->bind_lock);
 
 	seq_putc(s, '\n');
 
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 2cc1869..0ab775a 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -151,6 +151,8 @@
 	unsigned int (*gpuid)(struct kgsl_device *device, unsigned int *chipid);
 	void (*snapshot)(struct kgsl_device *device,
 		struct kgsl_snapshot *snapshot, struct kgsl_context *context);
+	void (*snapshot_gmu)(struct kgsl_device *device,
+		struct kgsl_snapshot *snapshot);
 	irqreturn_t (*irq_handler)(struct kgsl_device *device);
 	int (*drain)(struct kgsl_device *device);
 	/*
@@ -493,7 +495,9 @@
  * @work: worker to dump the frozen memory
  * @dump_gate: completion gate signaled by worker when it is finished.
  * @process: the process that caused the hang, if known.
- * @sysfs_read: An atomic for concurrent snapshot reads via syfs.
+ * @sysfs_read: Count of current reads via sysfs
+ * @first_read: True until the snapshot read is started
+ * @gmu_fault: Snapshot collected when GMU fault happened
  */
 struct kgsl_snapshot {
 	uint64_t ib1base;
@@ -514,7 +518,9 @@
 	struct work_struct work;
 	struct completion dump_gate;
 	struct kgsl_process_private *process;
-	atomic_t sysfs_read;
+	unsigned int sysfs_read;
+	bool first_read;
+	bool gmu_fault;
 };
 
 /**
@@ -700,9 +706,8 @@
 
 int kgsl_device_snapshot_init(struct kgsl_device *device);
 void kgsl_device_snapshot(struct kgsl_device *device,
-			struct kgsl_context *context);
+			struct kgsl_context *context, bool gmu_fault);
 void kgsl_device_snapshot_close(struct kgsl_device *device);
-void kgsl_snapshot_save_frozen_objs(struct work_struct *work);
 
 void kgsl_events_init(void);
 void kgsl_events_exit(void);
diff --git a/drivers/gpu/msm/kgsl_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 2ddb082..0a7424a 100644
--- a/drivers/gpu/msm/kgsl_gmu.c
+++ b/drivers/gpu/msm/kgsl_gmu.c
@@ -10,6 +10,7 @@
  * GNU General Public License for more details.
  *
  */
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/device.h>
 #include <linux/iommu.h>
@@ -17,6 +18,7 @@
 #include <linux/msm-bus.h>
 #include <linux/msm-bus-board.h>
 #include <linux/pm_opp.h>
+#include <linux/io.h>
 #include <soc/qcom/cmd-db.h>
 
 #include "kgsl_device.h"
@@ -25,6 +27,13 @@
 #include "a6xx_reg.h"
 #include "adreno.h"
 
+#undef MODULE_PARAM_PREFIX
+#define MODULE_PARAM_PREFIX "kgsl_gmu."
+
+static bool nogmu;
+module_param(nogmu, bool, 0444);
+MODULE_PARM_DESC(nogmu, "Disable the GMU");
+
 #define GMU_CONTEXT_USER		0
 #define GMU_CONTEXT_KERNEL		1
 #define GMU_KERNEL_ENTRIES		8
@@ -51,8 +60,6 @@
 	unsigned int image_start;
 };
 
-static void gmu_snapshot(struct kgsl_device *device);
-
 struct gmu_iommu_context {
 	const char *name;
 	struct device *dev;
@@ -109,9 +116,9 @@
 	struct gmu_device *gmu = &device->gmu;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 
-	if (gmu->pdev && ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
+	if (!nogmu && gmu->pdev &&
+		ADRENO_FEATURE(adreno_dev, ADRENO_GPMU))
 		return true;
-
 	return false;
 }
 
@@ -175,8 +182,8 @@
 
 	if (ret) {
 		dev_err(&gmu->pdev->dev,
-				"gmu map err: gaddr=0x%016llX, paddr=0x%016llX\n",
-				md->gmuaddr, md->physaddr);
+				"gmu map err: gaddr=0x%016llX, paddr=0x%pa\n",
+				md->gmuaddr, &(md->physaddr));
 		free_gmu_mem(gmu, md);
 	}
 
@@ -456,11 +463,12 @@
 			GMU_DCVS_NOHFI, perf_idx, bw_idx);
 
 		if (ret) {
-			dev_err(&gmu->pdev->dev,
+			dev_err_ratelimited(&gmu->pdev->dev,
 				"Failed to set GPU perf idx %d, bw idx %d\n",
 				perf_idx, bw_idx);
 
-			gmu_snapshot(device);
+			adreno_set_gpu_fault(adreno_dev, ADRENO_GMU_FAULT);
+			adreno_dispatcher_schedule(device);
 		}
 
 		return ret;
@@ -504,9 +512,10 @@
 	unsigned int len;
 
 	len = cmd_db_get_aux_data_len(res_id);
+	if (len == 0)
+		return -EINVAL;
 
 	if (len > (MAX_GX_LEVELS << 1)) {
-		/* CmdDB VLVL table size in bytes is too large */
 		dev_err(&gmu->pdev->dev,
 			"gfx cmddb size %d larger than alloc buf %d of %s\n",
 			len, (MAX_GX_LEVELS << 1), res_id);
@@ -514,9 +523,16 @@
 	}
 
 	cmd_db_get_aux_data(res_id, (uint8_t *)arc->val, len);
-	for (arc->num = 1; arc->num <= MAX_GX_LEVELS; arc->num++) {
-		if (arc->num == MAX_GX_LEVELS ||
-				arc->val[arc->num - 1] >= arc->val[arc->num])
+
+	/*
+	 * cmd_db_get_aux_data() gives us a zero-padded table of
+	 * size len that contains the arc values. To determine the
+	 * number of arc values, we loop through the table and count
+	 * them until we get to the end of the buffer or hit the
+	 * zero padding.
+	 */
+	for (arc->num = 1; arc->num < (len >> 1); arc->num++) {
+		if (arc->val[arc->num - 1] >= arc->val[arc->num])
 			break;
 	}
 
@@ -789,6 +805,15 @@
 	if (status & GMU_INT_HOST_AHB_BUS_ERR)
 		dev_err_ratelimited(&gmu->pdev->dev,
 				"AHB bus error interrupt received\n");
+	if (status & GMU_INT_FENCE_ERR) {
+		unsigned int fence_status;
+
+		adreno_read_gmureg(ADRENO_DEVICE(device),
+			ADRENO_REG_GMU_AHB_FENCE_STATUS, &fence_status);
+		dev_err_ratelimited(&gmu->pdev->dev,
+			"FENCE error interrupt received %x\n", fence_status);
+	}
+
 	if (status & ~GMU_AO_INT_MASK)
 		dev_err_ratelimited(&gmu->pdev->dev,
 				"Unhandled GMU interrupts 0x%lx\n",
@@ -1148,7 +1173,6 @@
 		goto error;
 
 	gmu->num_gpupwrlevels = pwr->num_pwrlevels;
-	gmu->wakeup_pwrlevel = pwr->default_pwrlevel;
 
 	for (i = 0; i < gmu->num_gpupwrlevels; i++) {
 		int j = gmu->num_gpupwrlevels - 1 - i;
@@ -1257,7 +1281,7 @@
 	return ret;
 }
 
-#define CX_GDSC_TIMEOUT	500	/* ms */
+#define CX_GDSC_TIMEOUT	5000	/* ms */
 static int gmu_disable_gdsc(struct gmu_device *gmu)
 {
 	int ret;
@@ -1276,7 +1300,7 @@
 	/*
 	 * After GX GDSC is off, CX GDSC must be off
 	 * Voting off alone from GPU driver cannot
-	 * Guarantee CX GDSC off. Polling with 10ms
+	 * Guarantee CX GDSC off. Polling with 5s
 	 * timeout to ensure
 	 */
 	t = jiffies + msecs_to_jiffies(CX_GDSC_TIMEOUT);
@@ -1291,37 +1315,6 @@
 	return -ETIMEDOUT;
 }
 
-static int gmu_fast_boot(struct kgsl_device *device)
-{
-	int ret;
-	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
-	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-	struct gmu_device *gmu = &device->gmu;
-
-	hfi_stop(gmu);
-	clear_bit(GMU_HFI_ON, &gmu->flags);
-
-	ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
-		GMU_RESET, 0);
-	if (ret)
-		return ret;
-
-	/*FIXME: enabling WD interrupt*/
-
-	ret = hfi_start(gmu, GMU_WARM_BOOT);
-	if (ret)
-		return ret;
-
-	ret = gpudev->oob_set(adreno_dev, OOB_CPINIT_SET_MASK,
-			OOB_CPINIT_CHECK_MASK, OOB_CPINIT_CLEAR_MASK);
-
-	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
-		gpudev->oob_clear(adreno_dev,
-				OOB_BOOT_SLUMBER_CLEAR_MASK);
-
-	return ret;
-}
-
 static int gmu_suspend(struct kgsl_device *device)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
@@ -1341,15 +1334,16 @@
 
 	gmu_disable_clks(gmu);
 	gmu_disable_gdsc(gmu);
+	dev_err(&gmu->pdev->dev, "Suspended GMU\n");
 	return 0;
 }
 
-static void gmu_snapshot(struct kgsl_device *device)
+void gmu_snapshot(struct kgsl_device *device)
 {
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct gmu_device *gmu = &device->gmu;
 
-	if (!test_and_set_bit(GMU_FAULT, &gmu->flags)) {
+	if (!gmu->fault_count) {
 		/* Mask so there's no interrupt caused by NMI */
 		adreno_write_gmureg(adreno_dev,
 				ADRENO_REG_GMU_GMU2HOST_INTR_MASK, 0xFFFFFFFF);
@@ -1364,7 +1358,7 @@
 		/* Wait for the NMI to be handled */
 		wmb();
 		udelay(100);
-		kgsl_device_snapshot(device, NULL);
+		kgsl_device_snapshot(device, NULL, true);
 
 		adreno_write_gmureg(adreno_dev,
 				ADRENO_REG_GMU_GMU2HOST_INTR_CLR, 0xFFFFFFFF);
@@ -1372,6 +1366,33 @@
 				ADRENO_REG_GMU_GMU2HOST_INTR_MASK,
 				(unsigned int) ~HFI_IRQ_MASK);
 	}
+
+	gmu->fault_count++;
+}
+
+static void gmu_change_gpu_pwrlevel(struct kgsl_device *device,
+	unsigned int new_level) {
+
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	unsigned int old_level = pwr->active_pwrlevel;
+
+	/*
+	 * Update the level according to any thermal,
+	 * max/min, or power constraints.
+	 */
+	new_level = kgsl_pwrctrl_adjust_pwrlevel(device, new_level);
+
+	/*
+	 * If thermal cycling is required and the new level hits the
+	 * thermal limit, kick off the cycling.
+	 */
+	kgsl_pwrctrl_set_thermal_cycle(device, new_level);
+
+	pwr->active_pwrlevel = new_level;
+	pwr->previous_pwrlevel = old_level;
+
+	/* Request adjusted DCVS level */
+	kgsl_clk_set_rate(device, pwr->active_pwrlevel);
 }
 
 /* To be called to power on both GPU and GMU */
@@ -1389,33 +1410,26 @@
 		WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
 		gmu_enable_gdsc(gmu);
 		gmu_enable_clks(gmu);
+		gmu_irq_enable(device);
 
 		/* Vote for 300MHz DDR for GMU to init */
 		ret = msm_bus_scale_client_update_request(gmu->pcl,
 				pwr->pwrlevels[pwr->default_pwrlevel].bus_freq);
-		if (ret) {
+		if (ret)
 			dev_err(&gmu->pdev->dev,
-					"Failed to allocate gmu b/w\n");
-			goto error_clks;
-		}
+				"Failed to allocate gmu b/w: %d\n", ret);
 
 		ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
 				GMU_COLD_BOOT, 0);
 		if (ret)
-			goto error_bus;
-
-		gmu_irq_enable(device);
+			goto error_gmu;
 
 		ret = hfi_start(gmu, GMU_COLD_BOOT);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
-		/* Send default DCVS level */
-		ret = gmu_dcvs_set(gmu, pwr->default_pwrlevel,
-				pwr->pwrlevels[pwr->default_pwrlevel].bus_freq);
-		if (ret)
-			goto error_gpu;
-
+		/* Request default DCVS level */
+		gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
 		msm_bus_scale_client_update_request(gmu->pcl, 0);
 		break;
 
@@ -1423,119 +1437,92 @@
 		WARN_ON(test_bit(GMU_CLK_ON, &gmu->flags));
 		gmu_enable_gdsc(gmu);
 		gmu_enable_clks(gmu);
+		gmu_irq_enable(device);
 
 		ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
 				GMU_WARM_BOOT, 0);
 		if (ret)
-			goto error_clks;
-
-		gmu_irq_enable(device);
+			goto error_gmu;
 
 		ret = hfi_start(gmu, GMU_WARM_BOOT);
 		if (ret)
-			goto error_gpu;
+			goto error_gmu;
 
-		ret = gmu_dcvs_set(gmu, gmu->wakeup_pwrlevel,
-				pwr->pwrlevels[gmu->wakeup_pwrlevel].bus_freq);
-		if (ret)
-			goto error_gpu;
-
-		gmu->wakeup_pwrlevel = pwr->default_pwrlevel;
+		gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
 		break;
 
 	case KGSL_STATE_RESET:
-		if (test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv)) {
+		if (test_bit(ADRENO_DEVICE_HARD_RESET, &adreno_dev->priv) ||
+			test_bit(GMU_FAULT, &gmu->flags)) {
 			gmu_suspend(device);
 			gmu_enable_gdsc(gmu);
 			gmu_enable_clks(gmu);
+			gmu_irq_enable(device);
 
 			ret = gpudev->rpmh_gpu_pwrctrl(
 				adreno_dev, GMU_FW_START, GMU_RESET, 0);
 			if (ret)
-				goto error_clks;
+				goto error_gmu;
 
-			gmu_irq_enable(device);
 
 			ret = hfi_start(gmu, GMU_COLD_BOOT);
 			if (ret)
-				goto error_gpu;
+				goto error_gmu;
 
 			/* Send DCVS level prior to reset*/
-			ret = gmu_dcvs_set(gmu, pwr->active_pwrlevel,
-					pwr->pwrlevels[pwr->active_pwrlevel]
-					.bus_freq);
+			gmu_change_gpu_pwrlevel(device,
+				pwr->default_pwrlevel);
+		} else {
+			/* GMU fast boot */
+			hfi_stop(gmu);
+
+			ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_START,
+					GMU_RESET, 0);
 			if (ret)
-				goto error_gpu;
+				goto error_gmu;
 
-			ret = gpudev->oob_set(adreno_dev,
-				OOB_CPINIT_SET_MASK,
-				OOB_CPINIT_CHECK_MASK,
-				OOB_CPINIT_CLEAR_MASK);
-
-		} else
-			gmu_fast_boot(device);
+			ret = hfi_start(gmu, GMU_WARM_BOOT);
+			if (ret)
+				goto error_gmu;
+		}
 		break;
 	default:
 		break;
 	}
 
-	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
-		gpudev->oob_clear(adreno_dev,
-				OOB_BOOT_SLUMBER_CLEAR_MASK);
-
 	return ret;
 
-error_gpu:
-	gmu_snapshot(device);
-	hfi_stop(gmu);
-	gmu_irq_disable(device);
+error_gmu:
 	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG))
 		gpudev->oob_clear(adreno_dev,
 				OOB_BOOT_SLUMBER_CLEAR_MASK);
-	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
-error_bus:
-	msm_bus_scale_client_update_request(gmu->pcl, 0);
-error_clks:
 	gmu_snapshot(device);
-	gmu_disable_clks(gmu);
-	gmu_disable_gdsc(gmu);
 	return ret;
 }
 
-#define GMU_IDLE_TIMEOUT	10 /* ms */
-
 /* Caller shall ensure GPU is ready for SLUMBER */
 void gmu_stop(struct kgsl_device *device)
 {
 	struct gmu_device *gmu = &device->gmu;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-	unsigned long t;
-	bool idle = false;
-	unsigned int reg;
+	int ret = 0;
 
 	if (!test_bit(GMU_CLK_ON, &gmu->flags))
 		return;
 
-	t = jiffies + msecs_to_jiffies(GMU_IDLE_TIMEOUT);
-	while (!time_after(jiffies, t)) {
-		adreno_read_gmureg(ADRENO_DEVICE(device),
-			ADRENO_REG_GMU_RPMH_POWER_STATE, &reg);
-		if (reg == device->gmu.idle_level) {
-			idle = true;
-			break;
-		}
-		/* Wait 100us to reduce unnecessary AHB bus traffic */
-		udelay(100);
-		cond_resched();
-	}
+	/* Wait for the lowest idle level we requested */
+	if (gpudev->wait_for_lowest_idle &&
+			gpudev->wait_for_lowest_idle(adreno_dev))
+		goto error;
 
-	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
+	ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
+	if (ret)
+		goto error;
 
-	if (!idle || (gpudev->wait_for_gmu_idle &&
-			gpudev->wait_for_gmu_idle(adreno_dev))) {
-		dev_err(&gmu->pdev->dev, "Stopping GMU before it is idle\n");
-	}
+	if (gpudev->wait_for_gmu_idle &&
+			gpudev->wait_for_gmu_idle(adreno_dev))
+		goto error;
 
 	/* Pending message in all queues are abandoned */
 	hfi_stop(gmu);
@@ -1546,9 +1533,18 @@
 	gmu_disable_clks(gmu);
 	gmu_disable_gdsc(gmu);
 
-	/* TODO: Vote CX, MX retention off */
-
 	msm_bus_scale_client_update_request(gmu->pcl, 0);
+	return;
+
+error:
+	/*
+	 * The power controller will change state to SLUMBER anyway
+	 * Set GMU_FAULT flag to indicate to power contrller
+	 * that hang recovery is needed to power on GPU
+	 */
+	set_bit(GMU_FAULT, &gmu->flags);
+	dev_err(&gmu->pdev->dev, "Failed to stop GMU\n");
+	gmu_snapshot(device);
 }
 
 void gmu_remove(struct kgsl_device *device)
@@ -1624,3 +1620,46 @@
 
 	device->gmu.pdev = NULL;
 }
+
+/*
+ * adreno_gmu_fenced_write() - Check if there is a GMU and it is enabled
+ * @adreno_dev: Pointer to the Adreno device device that owns the GMU
+ * @offset: 32bit register enum that is to be written
+ * @val: The value to be written to the register
+ * @fence_mask: The value to poll the fence status register
+ *
+ * Check the WRITEDROPPED0/1 bit in the FENCE_STATUS regsiter to check if
+ * the write to the fenced register went through. If it didn't then we retry
+ * the write until it goes through or we time out.
+ */
+void adreno_gmu_fenced_write(struct adreno_device *adreno_dev,
+		enum adreno_regs offset, unsigned int val,
+		unsigned int fence_mask)
+{
+	unsigned int status, i;
+
+	adreno_writereg(adreno_dev, offset, val);
+
+	if (!kgsl_gmu_isenabled(KGSL_DEVICE(adreno_dev)))
+		return;
+
+	for (i = 0; i < GMU_WAKEUP_RETRY_MAX; i++) {
+		adreno_read_gmureg(adreno_dev, ADRENO_REG_GMU_AHB_FENCE_STATUS,
+			&status);
+
+		/*
+		 * If !writedropped0/1, then the write to fenced register
+		 * was successful
+		 */
+		if (!(status & fence_mask))
+			return;
+		/* Wait a small amount of time before trying again */
+		udelay(GMU_WAKEUP_DELAY_US);
+
+		/* Try to write the fenced register again */
+		adreno_writereg(adreno_dev, offset, val);
+	}
+
+	dev_err(adreno_dev->dev.dev,
+		"GMU fenced register write timed out: reg %x\n", offset);
+}
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index 63ca028..90e87e4 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -22,14 +22,19 @@
 
 #define GMU_INT_WDOG_BITE		BIT(0)
 #define GMU_INT_RSCC_COMP		BIT(1)
+#define GMU_INT_FENCE_ERR		BIT(3)
 #define GMU_INT_DBD_WAKEUP		BIT(4)
 #define GMU_INT_HOST_AHB_BUS_ERR	BIT(5)
 #define GMU_AO_INT_MASK		\
 		(GMU_INT_WDOG_BITE |	\
-		GMU_INT_HOST_AHB_BUS_ERR)
+		GMU_INT_HOST_AHB_BUS_ERR |	\
+		GMU_INT_FENCE_ERR)
 
-#define MAX_GMUFW_SIZE	0x2000	/* in dwords */
-#define FENCE_RANGE_MASK	((0x1 << 31) | (0x0A << 18) | (0x8A0))
+#define MAX_GMUFW_SIZE	0x2000	/* in bytes */
+#define FENCE_RANGE_MASK	((0x1 << 31) | ((0xA << 2) << 18) | (0x8A0))
+
+#define FENCE_STATUS_WRITEDROPPED0_MASK 0x1
+#define FENCE_STATUS_WRITEDROPPED1_MASK 0x2
 
 /* Bitmask for GPU low power mode enabling and hysterisis*/
 #define SPTP_ENABLE_MASK (BIT(2) | BIT(0))
@@ -56,6 +61,9 @@
 #define GPUBUSYIGNAHB		BIT(23)
 #define CXGXCPUBUSYIGNAHB	BIT(30)
 
+/* GMU timeouts */
+#define GMU_IDLE_TIMEOUT        10 /* ms */
+
 /* Constants for GMU OOBs */
 #define OOB_BOOT_OPTION         0
 #define OOB_SLUMBER_OPTION      1
@@ -67,22 +75,33 @@
 #define OOB_DCVS_SET_MASK		BIT(23)
 #define OOB_DCVS_CHECK_MASK		BIT(31)
 #define OOB_DCVS_CLEAR_MASK		BIT(31)
-#define OOB_CPINIT_SET_MASK		BIT(16)
-#define OOB_CPINIT_CHECK_MASK		BIT(24)
-#define OOB_CPINIT_CLEAR_MASK		BIT(24)
+#define OOB_GPU_SET_MASK		BIT(16)
+#define OOB_GPU_CHECK_MASK		BIT(24)
+#define OOB_GPU_CLEAR_MASK		BIT(24)
 #define OOB_PERFCNTR_SET_MASK		BIT(17)
 #define OOB_PERFCNTR_CHECK_MASK		BIT(25)
 #define OOB_PERFCNTR_CLEAR_MASK		BIT(25)
-#define OOB_GPUSTART_SET_MASK		BIT(18)
-#define OOB_GPUSTART_CHECK_MASK		BIT(26)
-#define OOB_GPUSTART_CLEAR_MASK		BIT(26)
+#define OOB_PREEMPTION_SET_MASK		BIT(18)
+#define OOB_PREEMPTION_CHECK_MASK	BIT(26)
+#define OOB_PREEMPTION_CLEAR_MASK	BIT(26)
+
+/*
+ * Wait time before trying to write the register again.
+ * Hopefully the GMU has finished waking up during this delay.
+ * This delay must be less than the IFPC main hysteresis or
+ * the GMU will start shutting down before we try again.
+ */
+#define GMU_WAKEUP_DELAY_US 10
+/* Max amount of tries to wake up the GMU. */
+#define GMU_WAKEUP_RETRY_MAX 60
 
 /* Bits for the flags field in the gmu structure */
 enum gmu_flags {
 	GMU_BOOT_INIT_DONE = 0,
 	GMU_CLK_ON = 1,
 	GMU_HFI_ON = 2,
-	GMU_FAULT = 3
+	GMU_FAULT = 3,
+	GMU_DCVS_REPLAY = 4,
 };
 
 /**
@@ -193,6 +212,7 @@
  * @pcl: GPU BW scaling client
  * @ccl: CNOC BW scaling client
  * @idle_level: Minimal GPU idle power level
+ * @fault_count: GMU fault count
  */
 struct gmu_device {
 	unsigned int ver;
@@ -226,8 +246,10 @@
 	unsigned int pcl;
 	unsigned int ccl;
 	unsigned int idle_level;
+	unsigned int fault_count;
 };
 
+void gmu_snapshot(struct kgsl_device *device);
 bool kgsl_gmu_isenabled(struct kgsl_device *device);
 int gmu_probe(struct kgsl_device *device);
 void gmu_remove(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl_hfi.c b/drivers/gpu/msm/kgsl_hfi.c
index 68e0f3a..eef5f45 100644
--- a/drivers/gpu/msm/kgsl_hfi.c
+++ b/drivers/gpu/msm/kgsl_hfi.c
@@ -278,8 +278,7 @@
 	int rc = 0;
 	struct pending_msg msg;
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *)&init_msg,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &init_msg.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -309,8 +308,7 @@
 	int rc = 0;
 	struct pending_msg msg;
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *)&fw_ver,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &fw_ver.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -346,8 +344,7 @@
 		lmconfig.lm_enable_bitmask =
 			(1 << (gmu->lm_dcvs_level + 1)) - 1;
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *) &lmconfig,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &lmconfig.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -388,8 +385,7 @@
 
 	}
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *)&dcvstbl,
-			msg_size, &msg);
+	rc = hfi_send_msg(gmu, &dcvstbl.hdr, msg_size, &msg);
 	if (rc)
 		return rc;
 
@@ -441,8 +437,7 @@
 					gmu->rpmh_votes.cnoc_votes.
 					cmd_data[i][j];
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *) &bwtbl,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &bwtbl.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -454,6 +449,22 @@
 	return rc;
 }
 
+static int hfi_send_test(struct gmu_device *gmu)
+{
+	struct hfi_test_cmd test_msg = {
+		.hdr = {
+			.id = H2F_MSG_TEST,
+			.size = sizeof(test_msg) >> 2,
+			.type = HFI_MSG_CMD,
+		},
+	};
+	uint32_t msg_size_dwords = (sizeof(test_msg)) >> 2;
+	struct pending_msg msg;
+
+	return hfi_send_msg(gmu, (struct hfi_msg_hdr *)&test_msg.hdr,
+			msg_size_dwords, &msg);
+}
+
 int hfi_send_dcvs_vote(struct gmu_device *gmu, uint32_t perf_idx,
 		uint32_t bw_idx, enum rpm_ack_type ack_type)
 {
@@ -478,8 +489,7 @@
 	int rc = 0;
 	struct pending_msg msg;
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *)&dcvs_cmd,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &dcvs_cmd.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -511,8 +521,7 @@
 	if (init_perf_idx >= MAX_GX_LEVELS || init_bw_idx >= MAX_GX_LEVELS)
 		return -EINVAL;
 
-	rc = hfi_send_msg(gmu, (struct hfi_msg_hdr *) &slumber_cmd,
-			msg_size_dwords, &msg);
+	rc = hfi_send_msg(gmu, &slumber_cmd.hdr, msg_size_dwords, &msg);
 	if (rc)
 		return rc;
 
@@ -585,12 +594,12 @@
 
 	gmu->ver = ver;
 	if (major != FW_VER_MAJOR(ver))
-		dev_err(dev, "FW version major %d error (expect %d)\n",
+		WARN_ONCE(1, "FW version major %d error (expect %d)\n",
 				FW_VER_MAJOR(ver),
 				adreno_dev->gpucore->gpmu_major);
 
 	if (minor > FW_VER_MINOR(ver))
-		dev_err(dev, "FW version minor %d error (expect %d)\n",
+		WARN_ONCE(1, "FW version minor %d error (expect %d)\n",
 				FW_VER_MINOR(ver),
 				adreno_dev->gpucore->gpmu_minor);
 
@@ -614,12 +623,19 @@
 
 		result = hfi_send_lmconfig(gmu);
 		if (result) {
-			dev_err(dev, "Failire enabling limits management (%d)\n",
-				result);
+			dev_err(dev, "Failure enabling LM (%d)\n",
+					result);
 			return result;
 		}
 	}
 
+	/* Tell the GMU we are sending no more HFIs until the next boot */
+	if (ADRENO_QUIRK(adreno_dev, ADRENO_QUIRK_HFI_USE_REG)) {
+		result = hfi_send_test(gmu);
+		if (result)
+			return result;
+	}
+
 	set_bit(GMU_HFI_ON, &gmu->flags);
 	return 0;
 }
diff --git a/drivers/gpu/msm/kgsl_hfi.h b/drivers/gpu/msm/kgsl_hfi.h
index 47d07d9..105599c 100644
--- a/drivers/gpu/msm/kgsl_hfi.h
+++ b/drivers/gpu/msm/kgsl_hfi.h
@@ -115,7 +115,7 @@
 	HFI_F2H_QPRI_DEBUG = 40,
 };
 
-#define HFI_RSP_TIMEOUT 500 /* msec */
+#define HFI_RSP_TIMEOUT 5000 /* msec */
 #define HFI_H2F_CMD_IRQ_MASK BIT(0)
 
 enum hfi_msg_type {
@@ -228,6 +228,10 @@
 	uint32_t ddr_cmd_data[MAX_GX_LEVELS][MAX_BW_CMDS];
 };
 
+struct hfi_test_cmd {
+	struct hfi_msg_hdr hdr;
+};
+
 struct arc_vote_desc {
 	/* In case of GPU freq vote, primary is GX, secondary is MX
 	 * in case of GMU freq vote, primary is CX, secondary is MX
diff --git a/drivers/gpu/msm/kgsl_ioctl.c b/drivers/gpu/msm/kgsl_ioctl.c
index bfce4d4..9b02e19 100644
--- a/drivers/gpu/msm/kgsl_ioctl.c
+++ b/drivers/gpu/msm/kgsl_ioctl.c
@@ -145,7 +145,7 @@
 
 	if (_IOC_SIZE(cmds[nr].cmd) > sizeof(data)) {
 		if (__ratelimit(&_rs))
-			WARN(1, "data too big for ioctl 0x%08X: %d/%ld\n",
+			WARN(1, "data too big for ioctl 0x%08X: %d/%zu\n",
 				cmd, _IOC_SIZE(cmds[nr].cmd), sizeof(data));
 		return -EINVAL;
 	}
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index c02046a..8fc2c82 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -110,7 +110,7 @@
 };
 
 static struct global_pt_entry global_pt_entries[GLOBAL_PT_ENTRIES];
-static struct kgsl_memdesc *kgsl_global_secure_pt_entry;
+static int secure_global_size;
 static int global_pt_count;
 uint64_t global_pt_alloc;
 static struct kgsl_memdesc gpu_qdss_desc;
@@ -126,9 +126,11 @@
 		if (memdesc == NULL)
 			continue;
 
-		seq_printf(s, "0x%16.16llX-0x%16.16llX %16llu %s\n",
-			memdesc->gpuaddr, memdesc->gpuaddr + memdesc->size - 1,
-			memdesc->size, global_pt_entries[i].name);
+		seq_printf(s, "0x%pK-0x%pK %16llu %s\n",
+			(uint64_t *)(uintptr_t) memdesc->gpuaddr,
+			(uint64_t *)(uintptr_t) (memdesc->gpuaddr +
+			memdesc->size - 1), memdesc->size,
+			global_pt_entries[i].name);
 	}
 }
 
@@ -160,24 +162,33 @@
 	return 0;
 }
 
-static void kgsl_iommu_unmap_global_secure_pt_entry(struct kgsl_pagetable
-								*pagetable)
+void kgsl_iommu_unmap_global_secure_pt_entry(struct kgsl_device *device,
+				struct kgsl_memdesc *entry)
 {
-	struct kgsl_memdesc *entry = kgsl_global_secure_pt_entry;
+	if (!kgsl_mmu_is_secured(&device->mmu))
+		return;
 
-	if (entry != NULL)
-		kgsl_mmu_unmap(pagetable, entry);
+	if (entry != NULL && entry->pagetable->name == KGSL_MMU_SECURE_PT)
+		kgsl_mmu_unmap(entry->pagetable, entry);
 
 }
 
-static int kgsl_map_global_secure_pt_entry(struct kgsl_pagetable *pagetable)
+int kgsl_iommu_map_global_secure_pt_entry(struct kgsl_device *device,
+				struct kgsl_memdesc *entry)
 {
 	int ret = 0;
-	struct kgsl_memdesc *entry = kgsl_global_secure_pt_entry;
+
+	if (!kgsl_mmu_is_secured(&device->mmu))
+		return -ENOTSUPP;
 
 	if (entry != NULL) {
+		struct kgsl_pagetable *pagetable = device->mmu.securepagetable;
 		entry->pagetable = pagetable;
+		entry->gpuaddr = KGSL_IOMMU_SECURE_BASE + secure_global_size;
+
 		ret = kgsl_mmu_map(pagetable, entry);
+		if (ret == 0)
+			secure_global_size += entry->size;
 	}
 	return ret;
 }
@@ -222,13 +233,6 @@
 	global_pt_count++;
 }
 
-void kgsl_add_global_secure_entry(struct kgsl_device *device,
-					struct kgsl_memdesc *memdesc)
-{
-	memdesc->gpuaddr = KGSL_IOMMU_SECURE_BASE;
-	kgsl_global_secure_pt_entry = memdesc;
-}
-
 struct kgsl_memdesc *kgsl_iommu_get_qdss_global_entry(void)
 {
 	return &gpu_qdss_desc;
@@ -1066,7 +1070,6 @@
 
 	if (pt->name == KGSL_MMU_SECURE_PT) {
 		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_SECURE];
-		kgsl_iommu_unmap_global_secure_pt_entry(pt);
 	} else {
 		ctx = &iommu->ctx[KGSL_IOMMU_CONTEXT_USER];
 		kgsl_iommu_unmap_globals(pt);
@@ -1087,13 +1090,10 @@
 		struct kgsl_pagetable *pagetable,
 		struct kgsl_iommu_pt *pt)
 {
-	unsigned int secure_global_size = kgsl_global_secure_pt_entry != NULL ?
-					kgsl_global_secure_pt_entry->size : 0;
 	if (mmu->secured && pagetable->name == KGSL_MMU_SECURE_PT) {
-		pt->compat_va_start = KGSL_IOMMU_SECURE_BASE +
-						secure_global_size;
+		pt->compat_va_start = KGSL_IOMMU_SECURE_BASE;
 		pt->compat_va_end = KGSL_IOMMU_SECURE_END;
-		pt->va_start = KGSL_IOMMU_SECURE_BASE + secure_global_size;
+		pt->va_start = KGSL_IOMMU_SECURE_BASE;
 		pt->va_end = KGSL_IOMMU_SECURE_END;
 	} else {
 		pt->compat_va_start = KGSL_IOMMU_SVM_BASE32;
@@ -1118,20 +1118,15 @@
 		struct kgsl_pagetable *pagetable,
 		struct kgsl_iommu_pt *pt)
 {
-	unsigned int secure_global_size = kgsl_global_secure_pt_entry != NULL ?
-					kgsl_global_secure_pt_entry->size : 0;
 	if (mmu->secured) {
 		if (pagetable->name == KGSL_MMU_SECURE_PT) {
-			pt->compat_va_start = KGSL_IOMMU_SECURE_BASE +
-						secure_global_size;
+			pt->compat_va_start = KGSL_IOMMU_SECURE_BASE;
 			pt->compat_va_end = KGSL_IOMMU_SECURE_END;
-			pt->va_start = KGSL_IOMMU_SECURE_BASE +
-						secure_global_size;
+			pt->va_start = KGSL_IOMMU_SECURE_BASE;
 			pt->va_end = KGSL_IOMMU_SECURE_END;
 		} else {
 			pt->va_start = KGSL_IOMMU_SVM_BASE32;
-			pt->va_end = KGSL_IOMMU_SECURE_BASE +
-						secure_global_size;
+			pt->va_end = KGSL_IOMMU_SECURE_BASE;
 			pt->compat_va_start = pt->va_start;
 			pt->compat_va_end = pt->va_end;
 		}
@@ -1361,8 +1356,6 @@
 	ctx->regbase = iommu->regbase + KGSL_IOMMU_CB0_OFFSET
 			+ (cb_num << KGSL_IOMMU_CB_SHIFT);
 
-	ret = kgsl_map_global_secure_pt_entry(pt);
-
 done:
 	if (ret)
 		_free_pt(ctx, pt);
@@ -1606,6 +1599,18 @@
 	kgsl_setup_qdss_desc(device);
 	kgsl_setup_qtimer_desc(device);
 
+	if (!mmu->secured)
+		goto done;
+
+	mmu->securepagetable = kgsl_mmu_getpagetable(mmu,
+				KGSL_MMU_SECURE_PT);
+	if (IS_ERR(mmu->securepagetable)) {
+		status = PTR_ERR(mmu->securepagetable);
+		mmu->securepagetable = NULL;
+	} else if (mmu->securepagetable == NULL) {
+		status = -ENOMEM;
+	}
+
 done:
 	if (status)
 		kgsl_iommu_close(mmu);
@@ -1687,17 +1692,9 @@
 	if (ctx->dev == NULL || !mmu->secured)
 		return 0;
 
-	if (mmu->securepagetable == NULL) {
-		mmu->securepagetable = kgsl_mmu_getpagetable(mmu,
-						KGSL_MMU_SECURE_PT);
-		if (IS_ERR(mmu->securepagetable)) {
-			ret = PTR_ERR(mmu->securepagetable);
-			mmu->securepagetable = NULL;
-			return ret;
-		} else if (mmu->securepagetable == NULL) {
-			return -ENOMEM;
-		}
-	}
+	if (mmu->securepagetable == NULL)
+		return -ENOMEM;
+
 	iommu_pt = mmu->securepagetable->priv;
 
 	ret = _attach_pt(iommu_pt, ctx);
@@ -2500,6 +2497,13 @@
 		end = pt->va_end;
 	}
 
+	/*
+	 * When mapping secure buffers, adjust the start of the va range
+	 * to the end of secure global buffers.
+	 */
+	if (kgsl_memdesc_is_secured(memdesc))
+		start += secure_global_size;
+
 	spin_lock(&pagetable->lock);
 
 	addr = _get_unmapped_area(pagetable, start, end, size, align);
diff --git a/drivers/gpu/msm/kgsl_log.h b/drivers/gpu/msm/kgsl_log.h
index d79a410..4f1241b 100644
--- a/drivers/gpu/msm/kgsl_log.h
+++ b/drivers/gpu/msm/kgsl_log.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2008-2011,2013-2014,2016 The Linux Foundation.
+/* Copyright (c) 2002,2008-2011,2013-2014,2016-2017 The Linux Foundation.
  * All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
@@ -67,6 +67,13 @@
 					__func__, ##args);\
 	} while (0)
 
+#define KGSL_LOG_ERR_RATELIMITED(dev, lvl, fmt, args...) \
+	do { \
+		if ((lvl) >= 3) \
+			dev_err_ratelimited(dev, "|%s| " fmt, \
+					__func__, ##args);\
+	} while (0)
+
 #define KGSL_DRV_INFO(_dev, fmt, args...) \
 KGSL_LOG_INFO(_dev->dev, _dev->drv_log, fmt, ##args)
 #define KGSL_DRV_WARN(_dev, fmt, args...) \
@@ -77,6 +84,8 @@
 KGSL_LOG_CRIT(_dev->dev, _dev->drv_log, fmt, ##args)
 #define KGSL_DRV_CRIT_RATELIMIT(_dev, fmt, args...) \
 KGSL_LOG_CRIT_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
+#define KGSL_DRV_ERR_RATELIMIT(_dev, fmt, args...) \
+KGSL_LOG_ERR_RATELIMITED(_dev->dev, _dev->drv_log, fmt, ##args)
 #define KGSL_DRV_FATAL(_dev, fmt, args...) \
 KGSL_LOG_FATAL((_dev)->dev, (_dev)->drv_log, fmt, ##args)
 
diff --git a/drivers/gpu/msm/kgsl_mmu.c b/drivers/gpu/msm/kgsl_mmu.c
index 8ea4492..a0fd3ec 100644
--- a/drivers/gpu/msm/kgsl_mmu.c
+++ b/drivers/gpu/msm/kgsl_mmu.c
@@ -26,6 +26,17 @@
 
 static void pagetable_remove_sysfs_objects(struct kgsl_pagetable *pagetable);
 
+static void _deferred_destroy(struct work_struct *ws)
+{
+	struct kgsl_pagetable *pagetable = container_of(ws,
+					struct kgsl_pagetable, destroy_ws);
+
+	if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
+		pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
+
+	kfree(pagetable);
+}
+
 static void kgsl_destroy_pagetable(struct kref *kref)
 {
 	struct kgsl_pagetable *pagetable = container_of(kref,
@@ -33,10 +44,7 @@
 
 	kgsl_mmu_detach_pagetable(pagetable);
 
-	if (PT_OP_VALID(pagetable, mmu_destroy_pagetable))
-		pagetable->pt_ops->mmu_destroy_pagetable(pagetable);
-
-	kfree(pagetable);
+	kgsl_schedule_work(&pagetable->destroy_ws);
 }
 
 static inline void kgsl_put_pagetable(struct kgsl_pagetable *pagetable)
@@ -299,6 +307,7 @@
 	kref_init(&pagetable->refcount);
 
 	spin_lock_init(&pagetable->lock);
+	INIT_WORK(&pagetable->destroy_ws, _deferred_destroy);
 
 	pagetable->mmu = mmu;
 	pagetable->name = name;
diff --git a/drivers/gpu/msm/kgsl_mmu.h b/drivers/gpu/msm/kgsl_mmu.h
index 56bb317..2ea904e 100644
--- a/drivers/gpu/msm/kgsl_mmu.h
+++ b/drivers/gpu/msm/kgsl_mmu.h
@@ -42,6 +42,7 @@
 	struct list_head list;
 	unsigned int name;
 	struct kobject *kobj;
+	struct work_struct destroy_ws;
 
 	struct {
 		atomic_t entries;
@@ -173,7 +174,9 @@
 struct kgsl_pagetable *kgsl_mmu_getpagetable_ptbase(struct kgsl_mmu *mmu,
 						u64 ptbase);
 
-void kgsl_add_global_secure_entry(struct kgsl_device *device,
+int kgsl_iommu_map_global_secure_pt_entry(struct kgsl_device *device,
+					struct kgsl_memdesc *memdesc);
+void kgsl_iommu_unmap_global_secure_pt_entry(struct kgsl_device *device,
 					struct kgsl_memdesc *memdesc);
 void kgsl_print_global_pt_entries(struct seq_file *s);
 void kgsl_mmu_putpagetable(struct kgsl_pagetable *pagetable);
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index 685ce3e..5da8c1d 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -65,19 +65,26 @@
 
 /* Map the page into kernel and zero it out */
 static void
-_kgsl_pool_zero_page(struct page *p)
+_kgsl_pool_zero_page(struct page *p, unsigned int pool_order)
 {
-	void *addr = kmap_atomic(p);
+	int i;
 
-	memset(addr, 0, PAGE_SIZE);
-	dmac_flush_range(addr, addr + PAGE_SIZE);
-	kunmap_atomic(addr);
+	for (i = 0; i < (1 << pool_order); i++) {
+		struct page *page = nth_page(p, i);
+		void *addr = kmap_atomic(page);
+
+		memset(addr, 0, PAGE_SIZE);
+		dmac_flush_range(addr, addr + PAGE_SIZE);
+		kunmap_atomic(addr);
+	}
 }
 
 /* Add a page to specified pool */
 static void
 _kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p)
 {
+	_kgsl_pool_zero_page(p, pool->pool_order);
+
 	spin_lock(&pool->list_lock);
 	list_add_tail(&p->lru, &pool->page_list);
 	pool->page_count++;
@@ -322,6 +329,7 @@
 			} else
 				return -ENOMEM;
 		}
+		_kgsl_pool_zero_page(page, order);
 		goto done;
 	}
 
@@ -341,6 +349,7 @@
 			page = alloc_pages(gfp_mask, order);
 			if (page == NULL)
 				return -ENOMEM;
+			_kgsl_pool_zero_page(page, order);
 			goto done;
 		}
 	}
@@ -370,12 +379,13 @@
 			} else
 				return -ENOMEM;
 		}
+
+		_kgsl_pool_zero_page(page, order);
 	}
 
 done:
 	for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) {
 		p = nth_page(page, j);
-		_kgsl_pool_zero_page(p);
 		pages[pcount] = p;
 		pcount++;
 	}
@@ -412,6 +422,24 @@
 	__free_pages(page, page_order);
 }
 
+/*
+ * Return true if the pool of specified page size is supported
+ * or no pools are supported otherwise return false.
+ */
+bool kgsl_pool_avaialable(int page_size)
+{
+	int i;
+
+	if (!kgsl_num_pools)
+		return true;
+
+	for (i = 0; i < kgsl_num_pools; i++)
+		if (ilog2(page_size >> PAGE_SHIFT) == kgsl_pools[i].pool_order)
+			return true;
+
+	return false;
+}
+
 static void kgsl_pool_reserve_pages(void)
 {
 	int i, j;
diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h
index d55e1ad..8091afb 100644
--- a/drivers/gpu/msm/kgsl_pool.h
+++ b/drivers/gpu/msm/kgsl_pool.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -40,5 +40,6 @@
 int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 			unsigned int pages_len, unsigned int *align);
 void kgsl_pool_free_page(struct page *p);
+bool kgsl_pool_avaialable(int size);
 #endif /* __KGSL_POOL_H */
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 6e55395..940a741f 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -223,7 +223,7 @@
 	}
 
 	if (ret)
-		KGSL_PWR_ERR(device, "GPU BW scaling failure\n");
+		KGSL_PWR_ERR(device, "GPU BW scaling failure: %d\n", ret);
 
 	return ret;
 }
@@ -233,7 +233,7 @@
  * @device: Pointer to the kgsl_device struct
  * @pwrlevel: power level in pwrlevels[] table
  */
-static int kgsl_clk_set_rate(struct kgsl_device *device,
+int kgsl_clk_set_rate(struct kgsl_device *device,
 		unsigned int pwrlevel)
 {
 	struct gmu_device *gmu = &device->gmu;
@@ -244,12 +244,9 @@
 	/* GMU scales GPU freq */
 	if (kgsl_gmu_isenabled(device)) {
 		/* If GMU has not been started, save it */
-		if (!(gmu->flags & GMU_HFI_ON)) {
-			/* In slumber the clock is off so we are done */
-			if (pwrlevel == (gmu->num_gpupwrlevels - 1))
-				return 0;
-
-			gmu->wakeup_pwrlevel = pwrlevel;
+		if (!test_bit(GMU_HFI_ON, &gmu->flags)) {
+			/* store clock change request */
+			set_bit(GMU_DCVS_REPLAY, &gmu->flags);
 			return 0;
 		}
 
@@ -259,12 +256,14 @@
 			return -EINVAL;
 		}
 		ret = gmu_dcvs_set(gmu, pwrlevel, INVALID_DCVS_IDX);
+		/* indicate actual clock  change */
+		clear_bit(GMU_DCVS_REPLAY, &gmu->flags);
 	} else
 		/* Linux clock driver scales GPU freq */
 		ret = clk_set_rate(pwr->grp_clks[0], pl->gpu_freq);
 
 	if (ret)
-		KGSL_PWR_ERR(device, "GPU clk freq set failure\n");
+		KGSL_PWR_ERR(device, "GPU clk freq set failure: %d\n", ret);
 
 	return ret;
 }
@@ -345,12 +344,14 @@
 
 /**
  * kgsl_pwrctrl_set_thermal_cycle() - set the thermal cycle if required
- * @pwr: Pointer to the kgsl_pwrctrl struct
+ * @device: Pointer to the kgsl_device struct
  * @new_level: the level to transition to
  */
-static void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_pwrctrl *pwr,
+void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_device *device,
 						unsigned int new_level)
 {
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+
 	if ((new_level != pwr->thermal_pwrlevel) || !pwr->sysfs_pwr_limit)
 		return;
 	if (pwr->thermal_pwrlevel == pwr->sysfs_pwr_limit->level) {
@@ -371,21 +372,15 @@
 }
 
 /**
- * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels
+ * kgsl_pwrctrl_adjust_pwrlevel() - Adjust the power level if
+ * required by thermal, max/min, constraints, etc
  * @device: Pointer to the kgsl_device struct
  * @new_level: Requested powerlevel, an index into the pwrlevel array
- *
- * Check that any power level constraints are still valid.  Update the
- * requested level according to any thermal, max/min, or power constraints.
- * If a new GPU level is going to be set, update the bus to that level's
- * default value.  Do not change the bus if a constraint keeps the new
- * level at the current level.  Set the new GPU frequency.
  */
-void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+unsigned int kgsl_pwrctrl_adjust_pwrlevel(struct kgsl_device *device,
 				unsigned int new_level)
 {
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-	struct kgsl_pwrlevel *pwrlevel;
 	unsigned int old_level = pwr->active_pwrlevel;
 
 	/* If a pwr constraint is expired, remove it */
@@ -403,16 +398,38 @@
 	 * Adjust the power level if required by thermal, max/min,
 	 * constraints, etc
 	 */
-	new_level = _adjust_pwrlevel(pwr, new_level, &pwr->constraint,
+	return _adjust_pwrlevel(pwr, new_level, &pwr->constraint,
 					device->pwrscale.popp_level);
+}
+
+/**
+ * kgsl_pwrctrl_pwrlevel_change() - Validate and change power levels
+ * @device: Pointer to the kgsl_device struct
+ * @new_level: Requested powerlevel, an index into the pwrlevel array
+ *
+ * Check that any power level constraints are still valid.  Update the
+ * requested level according to any thermal, max/min, or power constraints.
+ * If a new GPU level is going to be set, update the bus to that level's
+ * default value.  Do not change the bus if a constraint keeps the new
+ * level at the current level.  Set the new GPU frequency.
+ */
+void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
+				unsigned int new_level)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	struct kgsl_pwrlevel *pwrlevel;
+	unsigned int old_level = pwr->active_pwrlevel;
+
+	new_level = kgsl_pwrctrl_adjust_pwrlevel(device, new_level);
 
 	/*
 	 * If thermal cycling is required and the new level hits the
 	 * thermal limit, kick off the cycling.
 	 */
-	kgsl_pwrctrl_set_thermal_cycle(pwr, new_level);
+	kgsl_pwrctrl_set_thermal_cycle(device, new_level);
 
-	if (new_level == old_level)
+	if (new_level == old_level &&
+		!test_bit(GMU_DCVS_REPLAY, &device->gmu.flags))
 		return;
 
 	kgsl_pwrscale_update_stats(device);
@@ -722,7 +739,7 @@
 {
 	int i;
 
-	for (i = pwr->num_pwrlevels - 1; i >= 0; i--) {
+	for (i = pwr->num_pwrlevels - 2; i >= 0; i--) {
 		if (abs(pwr->pwrlevels[i].gpu_freq - clock) < 5000000)
 			return i;
 	}
@@ -2629,6 +2646,7 @@
 _aware(struct kgsl_device *device)
 {
 	int status = 0;
+	struct gmu_device *gmu = &device->gmu;
 
 	switch (device->state) {
 	case KGSL_STATE_RESET:
@@ -2648,15 +2666,42 @@
 		kgsl_pwrscale_midframe_timer_cancel(device);
 		break;
 	case KGSL_STATE_SLUMBER:
+		/* if GMU already in FAULT */
+		if (kgsl_gmu_isenabled(device) &&
+			test_bit(GMU_FAULT, &gmu->flags)) {
+			status = -EINVAL;
+			break;
+		}
+
 		status = kgsl_pwrctrl_enable(device);
 		break;
 	default:
 		status = -EINVAL;
 	}
-	if (status)
+
+	if (status) {
+		if (kgsl_gmu_isenabled(device)) {
+			/* GMU hang recovery */
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_RESET);
+			set_bit(GMU_FAULT, &gmu->flags);
+			status = kgsl_pwrctrl_enable(device);
+			if (status) {
+				/*
+				 * Cannot recover GMU failure
+				 * GPU will not be powered on
+				 */
+				WARN_ONCE(1, "Failed to recover GMU\n");
+			}
+
+			clear_bit(GMU_FAULT, &gmu->flags);
+			kgsl_pwrctrl_set_state(device, KGSL_STATE_AWARE);
+			return status;
+		}
+
 		kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
-	else
+	} else {
 		kgsl_pwrctrl_set_state(device, KGSL_STATE_AWARE);
+	}
 	return status;
 }
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 85bab11..2a45de7 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -237,6 +237,12 @@
 int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
 void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device);
 int kgsl_pwrctrl_change_state(struct kgsl_device *device, int state);
+int kgsl_clk_set_rate(struct kgsl_device *device,
+	unsigned int pwrlevel);
+unsigned int kgsl_pwrctrl_adjust_pwrlevel(struct kgsl_device *device,
+	unsigned int new_level);
+void kgsl_pwrctrl_set_thermal_cycle(struct kgsl_device *device,
+	unsigned int new_level);
 
 static inline unsigned long kgsl_get_clkrate(struct clk *clk)
 {
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index 20590ea..32fac88 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -372,7 +372,7 @@
 		}
 		if (nap_time && go_time) {
 			percent_nap = 100 * nap_time;
-			do_div(percent_nap, nap_time + go_time);
+			div64_s64(percent_nap, nap_time + go_time);
 		}
 		trace_kgsl_popp_nap(device, (int)nap_time / 1000, nap,
 				percent_nap);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 5061f6a..de5df54 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -27,7 +27,6 @@
 #include "kgsl_device.h"
 #include "kgsl_log.h"
 #include "kgsl_mmu.h"
-#include "kgsl_pool.h"
 
 /*
  * The user can set this from debugfs to force failed memory allocations to
@@ -1057,7 +1056,7 @@
 	else if (type < ARRAY_SIZE(memtype_str) && memtype_str[type] != NULL)
 		strlcpy(name, memtype_str[type], name_size);
 	else
-		snprintf(name, name_size, "unknown(%3d)", type);
+		snprintf(name, name_size, "VK/others(%3d)", type);
 }
 EXPORT_SYMBOL(kgsl_get_memory_usage);
 
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 5466a49..55bb34f 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -368,6 +368,8 @@
 	}
 }
 
+#include "kgsl_pool.h"
+
 /**
  * kgsl_get_page_size() - Get supported pagesize
  * @size: Size of the page
@@ -378,11 +380,14 @@
 #ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
 static inline int kgsl_get_page_size(size_t size, unsigned int align)
 {
-	if (align >= ilog2(SZ_1M) && size >= SZ_1M)
+	if (align >= ilog2(SZ_1M) && size >= SZ_1M &&
+		kgsl_pool_avaialable(SZ_1M))
 		return SZ_1M;
-	else if (align >= ilog2(SZ_64K) && size >= SZ_64K)
+	else if (align >= ilog2(SZ_64K) && size >= SZ_64K &&
+		kgsl_pool_avaialable(SZ_64K))
 		return SZ_64K;
-	else if (align >= ilog2(SZ_8K) && size >= SZ_8K)
+	else if (align >= ilog2(SZ_8K) && size >= SZ_8K &&
+		kgsl_pool_avaialable(SZ_8K))
 		return SZ_8K;
 	else
 		return PAGE_SIZE;
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 7cbda72..f710d8f 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -24,6 +24,8 @@
 #include "kgsl_snapshot.h"
 #include "adreno_cp_parser.h"
 
+static void kgsl_snapshot_save_frozen_objs(struct work_struct *work);
+
 /* Placeholder for list of ib objects that contain all objects in that IB */
 
 struct kgsl_snapshot_cp_obj {
@@ -182,7 +184,7 @@
 	context = kgsl_context_get(device, header->current_context);
 
 	/* Get the current PT base */
-	 header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
+	header->ptbase = kgsl_mmu_get_current_ttbr0(&device->mmu);
 
 	/* And the PID for the task leader */
 	if (context) {
@@ -206,6 +208,44 @@
 	return size;
 }
 
+/* Snapshot the Linux specific information */
+static size_t snapshot_os_no_ctxt(struct kgsl_device *device,
+	u8 *buf, size_t remain, void *priv)
+{
+	struct kgsl_snapshot_linux_v2 *header =
+		(struct kgsl_snapshot_linux_v2 *)buf;
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	size_t size = sizeof(*header);
+
+	/* Make sure there is enough room for the data */
+	if (remain < size) {
+		SNAPSHOT_ERR_NOMEM(device, "OS");
+		return 0;
+	}
+
+	memset(header, 0, sizeof(*header));
+
+	header->osid = KGSL_SNAPSHOT_OS_LINUX_V3;
+
+	/* Get the kernel build information */
+	strlcpy(header->release, init_utsname()->release,
+			sizeof(header->release));
+	strlcpy(header->version, init_utsname()->version,
+			sizeof(header->version));
+
+	/* Get the Unix time for the timestamp */
+	header->seconds = get_seconds();
+
+	/* Remember the power information */
+	header->power_flags = pwr->power_flags;
+	header->power_level = pwr->active_pwrlevel;
+	header->power_interval_timeout = pwr->interval_timeout;
+	header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
+
+	/* Return the size of the data segment */
+	return size;
+}
+
 static void kgsl_snapshot_put_object(struct kgsl_snapshot_object *obj)
 {
 	list_del(&obj->node);
@@ -572,16 +612,34 @@
 	snapshot->size += header->size;
 }
 
+static void kgsl_free_snapshot(struct kgsl_snapshot *snapshot)
+{
+	struct kgsl_snapshot_object *obj, *tmp;
+
+	wait_for_completion(&snapshot->dump_gate);
+
+	list_for_each_entry_safe(obj, tmp,
+				&snapshot->obj_list, node)
+		kgsl_snapshot_put_object(obj);
+
+	if (snapshot->mempool)
+		vfree(snapshot->mempool);
+
+	kfree(snapshot);
+	KGSL_CORE_ERR("snapshot: objects released\n");
+}
+
 /**
  * kgsl_snapshot() - construct a device snapshot
  * @device: device to snapshot
  * @context: the context that is hung, might be NULL if unknown.
+ * @gmu_fault: whether this snapshot is triggered by a GMU fault.
  *
  * Given a device, construct a binary snapshot dump of the current device state
  * and store it in the device snapshot memory.
  */
 void kgsl_device_snapshot(struct kgsl_device *device,
-		struct kgsl_context *context)
+		struct kgsl_context *context, bool gmu_fault)
 {
 	struct kgsl_snapshot_header *header = device->snapshot_memory.ptr;
 	struct kgsl_snapshot *snapshot;
@@ -602,11 +660,20 @@
 	device->snapshot_faultcount++;
 
 	/*
-	 * The first hang is always the one we are interested in. Don't capture
-	 * a new snapshot instance if the old one hasn't been grabbed yet
+	 * Overwrite a non-GMU fault snapshot if a GMU fault occurs.
 	 */
-	if (device->snapshot != NULL)
-		return;
+	if (device->snapshot != NULL) {
+		if (!gmu_fault || device->snapshot->gmu_fault)
+			return;
+
+		/*
+		 * If another thread is currently reading it, that thread
+		 * will free it, otherwise free it now.
+		 */
+		if (!device->snapshot->sysfs_read)
+			kgsl_free_snapshot(device->snapshot);
+		device->snapshot = NULL;
+	}
 
 	/* Allocate memory for the snapshot instance */
 	snapshot = kzalloc(sizeof(*snapshot), GFP_KERNEL);
@@ -621,7 +688,9 @@
 	snapshot->start = device->snapshot_memory.ptr;
 	snapshot->ptr = device->snapshot_memory.ptr;
 	snapshot->remain = device->snapshot_memory.size;
-	atomic_set(&snapshot->sysfs_read, 0);
+	snapshot->gmu_fault = gmu_fault;
+	snapshot->first_read = true;
+	snapshot->sysfs_read = 0;
 
 	header = (struct kgsl_snapshot_header *) snapshot->ptr;
 
@@ -633,12 +702,24 @@
 	snapshot->size += sizeof(*header);
 
 	/* Build the Linux specific header */
-	kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
-			snapshot, snapshot_os, NULL);
+	/* We either want to only dump GMU, or we want to dump GPU and GMU */
+	if (gmu_fault) {
+		/* Dump only the GMU */
+		kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
+				snapshot, snapshot_os_no_ctxt, NULL);
 
-	/* Get the device specific sections */
-	if (device->ftbl->snapshot)
-		device->ftbl->snapshot(device, snapshot, context);
+		if (device->ftbl->snapshot_gmu)
+			device->ftbl->snapshot_gmu(device, snapshot);
+	} else {
+		/* Dump GPU and GMU */
+		kgsl_snapshot_add_section(device, KGSL_SNAPSHOT_SECTION_OS,
+				snapshot, snapshot_os, NULL);
+
+		if (device->ftbl->snapshot)
+			device->ftbl->snapshot(device, snapshot, context);
+		if (device->ftbl->snapshot_gmu)
+			device->ftbl->snapshot_gmu(device, snapshot);
+	}
 
 	/*
 	 * The timestamp is the seconds since boot so it is easier to match to
@@ -708,6 +789,30 @@
 #define kobj_to_device(a) \
 container_of(a, struct kgsl_device, snapshot_kobj)
 
+static int snapshot_release(struct kgsl_device *device,
+	struct kgsl_snapshot *snapshot)
+{
+	bool snapshot_free = false;
+	int ret = 0;
+
+	mutex_lock(&device->mutex);
+	snapshot->sysfs_read--;
+
+	/*
+	 * If someone's replaced the snapshot, return an error and free
+	 * the snapshot if this is the last thread to read it.
+	 */
+	if (device->snapshot != snapshot) {
+		ret = -EIO;
+		if (!snapshot->sysfs_read)
+			snapshot_free = true;
+	}
+	mutex_unlock(&device->mutex);
+	if (snapshot_free)
+		kgsl_free_snapshot(snapshot);
+	return ret;
+}
+
 /* Dump the sysfs binary data to the user */
 static ssize_t snapshot_show(struct file *filep, struct kobject *kobj,
 	struct bin_attribute *attr, char *buf, loff_t off,
@@ -715,20 +820,35 @@
 {
 	struct kgsl_device *device = kobj_to_device(kobj);
 	struct kgsl_snapshot *snapshot;
-	struct kgsl_snapshot_object *obj, *tmp;
 	struct kgsl_snapshot_section_header head;
 	struct snapshot_obj_itr itr;
-	int ret;
+	int ret = 0;
 
 	if (device == NULL)
 		return 0;
 
 	mutex_lock(&device->mutex);
 	snapshot = device->snapshot;
-	if (snapshot != NULL)
-		atomic_inc(&snapshot->sysfs_read);
+	if (snapshot != NULL) {
+		/*
+		 * If we're reading at a non-zero offset from a new snapshot,
+		 * that means we want to read from the previous snapshot (which
+		 * was overwritten), so return an error
+		 */
+		if (snapshot->first_read) {
+			if (off)
+				ret = -EIO;
+			else
+				snapshot->first_read = false;
+		}
+		if (!ret)
+			snapshot->sysfs_read++;
+	}
 	mutex_unlock(&device->mutex);
 
+	if (ret)
+		return ret;
+
 	/* Return nothing if we haven't taken a snapshot yet */
 	if (snapshot == NULL)
 		return 0;
@@ -739,7 +859,7 @@
 	 */
 	ret = wait_for_completion_interruptible(&snapshot->dump_gate);
 	if (ret) {
-		atomic_dec(&snapshot->sysfs_read);
+		snapshot_release(device, snapshot);
 		return ret;
 	}
 
@@ -776,29 +896,21 @@
 		bool snapshot_free = false;
 
 		mutex_lock(&device->mutex);
-		if (atomic_dec_and_test(&snapshot->sysfs_read)) {
-			device->snapshot = NULL;
+		if (--snapshot->sysfs_read == 0) {
+			if (device->snapshot == snapshot)
+				device->snapshot = NULL;
 			snapshot_free = true;
 		}
 		mutex_unlock(&device->mutex);
 
-		if (snapshot_free) {
-			list_for_each_entry_safe(obj, tmp,
-						&snapshot->obj_list, node)
-				kgsl_snapshot_put_object(obj);
-
-			if (snapshot->mempool)
-				vfree(snapshot->mempool);
-
-			kfree(snapshot);
-			KGSL_CORE_ERR("snapshot: objects released\n");
-		}
+		if (snapshot_free)
+			kgsl_free_snapshot(snapshot);
 		return 0;
 	}
 
 done:
-	atomic_dec(&snapshot->sysfs_read);
-	return itr.write;
+	ret = snapshot_release(device, snapshot);
+	return (ret < 0) ? ret : itr.write;
 }
 
 /* Show the total number of hangs since device boot */
@@ -869,9 +981,11 @@
 /* Show the timestamp of the last collected snapshot */
 static ssize_t timestamp_show(struct kgsl_device *device, char *buf)
 {
-	unsigned long timestamp =
-		device->snapshot ? device->snapshot->timestamp : 0;
+	unsigned long timestamp;
 
+	mutex_lock(&device->mutex);
+	timestamp = device->snapshot ? device->snapshot->timestamp : 0;
+	mutex_unlock(&device->mutex);
 	return snprintf(buf, PAGE_SIZE, "%lu\n", timestamp);
 }
 
@@ -1128,7 +1242,7 @@
  * is taken
  * @work: The work item that scheduled this work
  */
-void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
+static void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
 {
 	struct kgsl_snapshot *snapshot = container_of(work,
 				struct kgsl_snapshot, work);
@@ -1140,6 +1254,9 @@
 	if (IS_ERR_OR_NULL(device))
 		return;
 
+	if (snapshot->gmu_fault)
+		goto gmu_only;
+
 	kgsl_snapshot_process_ib_obj_list(snapshot);
 
 	list_for_each_entry(obj, &snapshot->obj_list, node) {
@@ -1186,6 +1303,7 @@
 			       "snapshot: Active IB2:%016llx not dumped\n",
 				snapshot->ib2base);
 
+gmu_only:
 	complete_all(&snapshot->dump_gate);
 	BUG_ON(device->force_panic);
 }
diff --git a/drivers/gpu/msm/kgsl_sync.c b/drivers/gpu/msm/kgsl_sync.c
index 015d07f..d4165b3 100644
--- a/drivers/gpu/msm/kgsl_sync.c
+++ b/drivers/gpu/msm/kgsl_sync.c
@@ -339,10 +339,10 @@
 		return -ENOENT;
 
 	snprintf(ktimeline_name, sizeof(ktimeline_name),
-		"%s_%.15s(%d)-%.15s(%d)-%d",
-		context->device->name,
+		"%s_%d-%.15s(%d)-%.15s(%d)",
+		context->device->name, context->id,
 		current->group_leader->comm, current->group_leader->pid,
-		current->comm, current->pid, context->id);
+		current->comm, current->pid);
 
 	ktimeline = kzalloc(sizeof(*ktimeline), GFP_KERNEL);
 	if (ktimeline == NULL) {
diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c
index 8008e06..865e7c2 100644
--- a/drivers/hid/i2c-hid/i2c-hid.c
+++ b/drivers/hid/i2c-hid/i2c-hid.c
@@ -604,7 +604,8 @@
 {
 	/* the worst case is computed from the set_report command with a
 	 * reportID > 15 and the maximum report length */
-	int args_len = sizeof(__u8) + /* optional ReportID byte */
+	int args_len = sizeof(__u8) + /* ReportID */
+		       sizeof(__u8) + /* optional ReportID byte */
 		       sizeof(__u16) + /* data register */
 		       sizeof(__u16) + /* size of the report */
 		       report_size; /* report */
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index ae83af6..7838343 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -971,6 +971,8 @@
 	unsigned int rsize = 0;
 	char *rdesc;
 	int ret, n;
+	int num_descriptors;
+	size_t offset = offsetof(struct hid_descriptor, desc);
 
 	quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
 			le16_to_cpu(dev->descriptor.idProduct));
@@ -993,10 +995,18 @@
 		return -ENODEV;
 	}
 
+	if (hdesc->bLength < sizeof(struct hid_descriptor)) {
+		dbg_hid("hid descriptor is too short\n");
+		return -EINVAL;
+	}
+
 	hid->version = le16_to_cpu(hdesc->bcdHID);
 	hid->country = hdesc->bCountryCode;
 
-	for (n = 0; n < hdesc->bNumDescriptors; n++)
+	num_descriptors = min_t(int, hdesc->bNumDescriptors,
+	       (hdesc->bLength - offset) / sizeof(struct hid_class_descriptor));
+
+	for (n = 0; n < num_descriptors; n++)
 		if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
 			rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
 
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 0c535d0..d72dfb2 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -611,8 +611,10 @@
 
 	/* Try to find an already-probed interface from the same device */
 	list_for_each_entry(data, &wacom_udev_list, list) {
-		if (compare_device_paths(hdev, data->dev, '/'))
+		if (compare_device_paths(hdev, data->dev, '/')) {
+			kref_get(&data->kref);
 			return data;
+		}
 	}
 
 	/* Fallback to finding devices that appear to be "siblings" */
@@ -712,6 +714,9 @@
 	if (!wacom->led.groups)
 		return -ENOTSUPP;
 
+	if (wacom->wacom_wac.features.type == REMOTE)
+		return -ENOTSUPP;
+
 	if (wacom->wacom_wac.pid) { /* wireless connected */
 		report_id = WAC_CMD_WL_LED_CONTROL;
 		buf_size = 13;
@@ -2433,6 +2438,8 @@
 	if (hdev->bus == BUS_BLUETOOTH)
 		device_remove_file(&hdev->dev, &dev_attr_speed);
 
+	wacom_release_resources(wacom);
+
 	hid_set_drvdata(hdev, NULL);
 }
 
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index c6a922e..db951c4 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -559,8 +559,8 @@
 				keys = data[9] & 0x07;
 			}
 		} else {
-			buttons = ((data[6] & 0x10) << 10) |
-			          ((data[5] & 0x10) << 9)  |
+			buttons = ((data[6] & 0x10) << 5)  |
+			          ((data[5] & 0x10) << 4)  |
 			          ((data[6] & 0x0F) << 4)  |
 			          (data[5] & 0x0F);
 		}
diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c
index e47d8c9..75126e4 100644
--- a/drivers/hv/hv_fcopy.c
+++ b/drivers/hv/hv_fcopy.c
@@ -161,6 +161,10 @@
 		out_src = smsg_out;
 		break;
 
+	case WRITE_TO_FILE:
+		out_src = fcopy_transaction.fcopy_msg;
+		out_len = sizeof(struct hv_do_fcopy);
+		break;
 	default:
 		out_src = fcopy_transaction.fcopy_msg;
 		out_len = fcopy_transaction.recv_len;
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index dee93ec..84e0994 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -208,11 +208,13 @@
 }
 static DEVICE_ATTR(cpu0_vid, S_IRUGO, get_cpu_vid, NULL);
 
-#define VDD_FROM_REG(val) (((val) * 95 + 2) / 4)
-#define VDD_TO_REG(val) clamp_val((((val) * 4 + 47) / 95), 0, 255)
+#define VDD_FROM_REG(val)	DIV_ROUND_CLOSEST((val) * 95, 4)
+#define VDD_CLAMP(val)		clamp_val(val, 0, 255 * 95 / 4)
+#define VDD_TO_REG(val)		DIV_ROUND_CLOSEST(VDD_CLAMP(val) * 4, 95)
 
-#define IN_FROM_REG(val) ((val) * 19)
-#define IN_TO_REG(val) clamp_val((((val) + 9) / 19), 0, 255)
+#define IN_FROM_REG(val)	((val) * 19)
+#define IN_CLAMP(val)		clamp_val(val, 0, 255 * 19)
+#define IN_TO_REG(val)		DIV_ROUND_CLOSEST(IN_CLAMP(val), 19)
 
 static ssize_t get_in_input(struct device *dev, struct device_attribute *attr,
 			    char *buf)
@@ -349,8 +351,13 @@
 
 #define DIV_FROM_REG(val) (1 << (val))
 #define FAN_FROM_REG(val, div) ((val) == 0 ? 0 : (480000 / ((val) << (div))))
-#define FAN_TO_REG(val, div) ((val) <= 0 ? 0 : \
-	clamp_val((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255))
+
+#define FAN_BASE(div)		(480000 >> (div))
+#define FAN_CLAMP(val, div)	clamp_val(val, FAN_BASE(div) / 255, \
+					  FAN_BASE(div))
+#define FAN_TO_REG(val, div)	((val) == 0 ? 0 : \
+				 DIV_ROUND_CLOSEST(480000, \
+						FAN_CLAMP(val, div) << (div)))
 
 static ssize_t get_fan_input(struct device *dev, struct device_attribute *attr,
 			     char *buf)
@@ -513,9 +520,9 @@
 static DEVICE_ATTR(fan1_off, S_IRUGO | S_IWUSR,
 		get_fan_off, set_fan_off);
 
-#define TEMP_FROM_REG(val) (((val) - 130) * 1000)
-#define TEMP_TO_REG(val) clamp_val(((((val) < 0 ? \
-			(val) - 500 : (val) + 500) / 1000) + 130), 0, 255)
+#define TEMP_FROM_REG(val)	(((val) - 130) * 1000)
+#define TEMP_CLAMP(val)		clamp_val(val, -130000, 125000)
+#define TEMP_TO_REG(val)	(DIV_ROUND_CLOSEST(TEMP_CLAMP(val), 1000) + 130)
 
 static ssize_t get_temp_input(struct device *dev, struct device_attribute *attr,
 			      char *buf)
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index d883483..16a3e7d 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -757,14 +757,14 @@
 		return -EINVAL;
 
 	if (adc_properties->adc_hc) {
-		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		/* (ADC code * vref_vadc (1.875V)) / scale_code */
 		if (adc_code > QPNP_VADC_HC_MAX_CODE)
 			adc_code = 0;
 		pmic_voltage = (int64_t) adc_code;
 		pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference
 							* 1000);
 		pmic_voltage = div64_s64(pmic_voltage,
-					QPNP_VADC_HC_VREF_CODE);
+					adc_properties->full_scale_code);
 	} else {
 		if (!chan_properties->adc_graph[CALIB_ABSOLUTE].dy)
 			return -EINVAL;
@@ -777,7 +777,8 @@
 		adc_chan_result->measurement = pmic_voltage*
 			chan_properties->offset_gain_denominator;
 
-		do_div(adc_chan_result->measurement,
+		adc_chan_result->measurement =
+			div64_s64(adc_chan_result->measurement,
 			chan_properties->offset_gain_numerator * 2);
 	} else
 		adc_chan_result->measurement = 0;
@@ -804,10 +805,12 @@
 	high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
 
 	if (param->adc_tm_hc) {
-		low_output *= QPNP_VADC_HC_VREF_CODE;
-		do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
-		high_output *= QPNP_VADC_HC_VREF_CODE;
-		do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		low_output *= param->full_scale_code;
+		low_output = div64_s64(low_output,
+				(QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		high_output *= param->full_scale_code;
+		high_output = div64_s64(high_output,
+				(QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
 	} else {
 		rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param,
 							CALIB_ABSOLUTE);
@@ -822,7 +825,7 @@
 			sign = 1;
 			low_output = -low_output;
 		}
-		do_div(low_output, QPNP_ADC_625_UV);
+		low_output = div64_s64(low_output, QPNP_ADC_625_UV);
 		if (sign)
 			low_output = -low_output;
 		low_output += btm_param.adc_gnd;
@@ -834,7 +837,7 @@
 			sign = 1;
 			high_output = -high_output;
 		}
-		do_div(high_output, QPNP_ADC_625_UV);
+		high_output = div64_s64(high_output, QPNP_ADC_625_UV);
 		if (sign)
 			high_output = -high_output;
 		high_output += btm_param.adc_gnd;
@@ -869,14 +872,14 @@
 		return -EINVAL;
 
 	if (adc_properties->adc_hc) {
-		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+		/* (code * vref_vadc (1.875V) * 1000) / (scale_code * 1000) */
 		if (adc_code > QPNP_VADC_HC_MAX_CODE)
 			adc_code = 0;
 		xo_thm_voltage = (int64_t) adc_code;
 		xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
 							* 1000);
 		xo_thm_voltage = div64_s64(xo_thm_voltage,
-					QPNP_VADC_HC_VREF_CODE * 1000);
+				adc_properties->full_scale_code * 1000);
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
 			xo_thm_voltage, &adc_chan_result->physical);
@@ -885,7 +888,7 @@
 			adc_properties, chan_properties, &xo_thm_voltage);
 
 		if (chan_properties->calib_type == CALIB_ABSOLUTE)
-			do_div(xo_thm_voltage, 1000);
+			xo_thm_voltage = div64_s64(xo_thm_voltage, 1000);
 
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1068,14 +1071,14 @@
 		return -EINVAL;
 
 	if (adc_properties->adc_hc) {
-		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+		/* (code * vref_vadc (1.875V) * 1000) / (scale code * 1000) */
 		if (adc_code > QPNP_VADC_HC_MAX_CODE)
 			adc_code = 0;
 		therm_voltage = (int64_t) adc_code;
 		therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
 							* 1000);
 		therm_voltage = div64_s64(therm_voltage,
-					(QPNP_VADC_HC_VREF_CODE * 1000));
+				(adc_properties->full_scale_code * 1000));
 
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
@@ -1085,7 +1088,7 @@
 			adc_properties, chan_properties, &therm_voltage);
 
 		if (chan_properties->calib_type == CALIB_ABSOLUTE)
-			do_div(therm_voltage, 1000);
+			therm_voltage = div64_s64(therm_voltage, 1000);
 
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1105,13 +1108,13 @@
 	int negative_offset = 0;
 
 	if (adc_properties->adc_hc) {
-		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
 		if (reg > QPNP_VADC_HC_MAX_CODE)
 			reg = 0;
 		adc_voltage = (int64_t) reg;
 		adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV;
 		adc_voltage = div64_s64(adc_voltage,
-					QPNP_VADC_HC_VREF_CODE);
+				adc_properties->full_scale_code);
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
 			adc_voltage, result);
@@ -1124,7 +1127,7 @@
 			adc_voltage = -adc_voltage;
 		}
 
-		do_div(adc_voltage, param1.dy);
+		adc_voltage = div64_s64(adc_voltage, param1.dy);
 
 		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
 			ARRAY_SIZE(adcmap_100k_104ef_104fb),
@@ -1151,8 +1154,9 @@
 			param->low_thr_temp, &param->low_thr_voltage);
 		if (rc)
 			return rc;
-		param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
-		do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+		param->low_thr_voltage *= adc_properties->full_scale_code;
+		param->low_thr_voltage = div64_s64(param->low_thr_voltage,
+						QPNP_VADC_HC_VDD_REFERENCE_MV);
 
 		rc = qpnp_adc_map_temp_voltage(
 			adcmap_100k_104ef_104fb_1875_vref,
@@ -1160,8 +1164,9 @@
 			param->high_thr_temp, &param->high_thr_voltage);
 		if (rc)
 			return rc;
-		param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
-		do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+		param->high_thr_voltage *= adc_properties->full_scale_code;
+		param->high_thr_voltage = div64_s64(param->high_thr_voltage,
+						QPNP_VADC_HC_VDD_REFERENCE_MV);
 	} else {
 		qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
 
@@ -1172,7 +1177,8 @@
 			return rc;
 
 		param->low_thr_voltage *= param1.dy;
-		do_div(param->low_thr_voltage, param1.adc_vref);
+		param->low_thr_voltage = div64_s64(param->low_thr_voltage,
+							param1.adc_vref);
 		param->low_thr_voltage += param1.adc_gnd;
 
 		rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
@@ -1182,7 +1188,8 @@
 			return rc;
 
 		param->high_thr_voltage *= param1.dy;
-		do_div(param->high_thr_voltage, param1.adc_vref);
+		param->high_thr_voltage = div64_s64(param->high_thr_voltage,
+							param1.adc_vref);
 		param->high_thr_voltage += param1.adc_gnd;
 	}
 
@@ -1241,13 +1248,13 @@
 		return -EINVAL;
 
 	if (adc_properties->adc_hc) {
-		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		/* (ADC code * vref_vadc (1.875V)) / full_scale_code */
 		if (adc_code > QPNP_VADC_HC_MAX_CODE)
 			adc_code = 0;
 		scale_voltage = (int64_t) adc_code;
 		scale_voltage *= (adc_properties->adc_vdd_reference * 1000);
 		scale_voltage = div64_s64(scale_voltage,
-						QPNP_VADC_HC_VREF_CODE);
+				adc_properties->full_scale_code);
 	} else {
 		qpnp_adc_scale_with_calib_param(adc_code, adc_properties,
 					chan_properties, &scale_voltage);
@@ -1281,11 +1288,11 @@
 	qpnp_get_vadc_gain_and_offset(chip, &usb_param, CALIB_RATIOMETRIC);
 
 	*low_threshold = param->low_thr * usb_param.dy;
-	do_div(*low_threshold, usb_param.adc_vref);
+	*low_threshold = div64_s64(*low_threshold, usb_param.adc_vref);
 	*low_threshold += usb_param.adc_gnd;
 
 	*high_threshold = param->high_thr * usb_param.dy;
-	do_div(*high_threshold, usb_param.adc_vref);
+	*high_threshold = div64_s64(*high_threshold, usb_param.adc_vref);
 	*high_threshold += usb_param.adc_gnd;
 
 	pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
@@ -1305,14 +1312,16 @@
 	if (param->adc_tm_hc) {
 		low_thr = (param->low_thr/param->gain_den);
 		low_thr *= param->gain_num;
-		low_thr *= QPNP_VADC_HC_VREF_CODE;
-		do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		low_thr *= param->full_scale_code;
+		low_thr = div64_s64(low_thr,
+				(QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
 		*low_threshold = low_thr;
 
 		high_thr = (param->high_thr/param->gain_den);
 		high_thr *= param->gain_num;
-		high_thr *= QPNP_VADC_HC_VREF_CODE;
-		do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		high_thr *= param->full_scale_code;
+		high_thr = div64_s64(high_thr,
+				(QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
 		*high_threshold = high_thr;
 	} else {
 		rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param,
@@ -1327,7 +1336,7 @@
 			low_thr = -low_thr;
 		}
 		low_thr = low_thr * param->gain_num;
-		do_div(low_thr, QPNP_ADC_625_UV);
+		low_thr = div64_s64(low_thr, QPNP_ADC_625_UV);
 		if (sign)
 			low_thr = -low_thr;
 		*low_threshold = low_thr + vbatt_param.adc_gnd;
@@ -1340,7 +1349,7 @@
 			high_thr = -high_thr;
 		}
 		high_thr = high_thr * param->gain_num;
-		do_div(high_thr, QPNP_ADC_625_UV);
+		high_thr = div64_s64(high_thr, QPNP_ADC_625_UV);
 		if (sign)
 			high_thr = -high_thr;
 		*high_threshold = high_thr + vbatt_param.adc_gnd;
@@ -1387,7 +1396,7 @@
 		low_thr = -low_thr;
 	}
 	low_thr = low_thr * chan_prop->offset_gain_numerator;
-	do_div(low_thr, QPNP_ADC_625_UV);
+	low_thr = div64_s64(low_thr, QPNP_ADC_625_UV);
 	if (sign)
 		low_thr = -low_thr;
 	*low_threshold = low_thr + vbatt_param.adc_gnd;
@@ -1400,7 +1409,7 @@
 		high_thr = -high_thr;
 	}
 	high_thr = high_thr * chan_prop->offset_gain_numerator;
-	do_div(high_thr, QPNP_ADC_625_UV);
+	high_thr = div64_s64(high_thr, QPNP_ADC_625_UV);
 	if (sign)
 		high_thr = -high_thr;
 	*high_threshold = high_thr + vbatt_param.adc_gnd;
@@ -1442,7 +1451,7 @@
 
 	pr_debug("low_output:%lld\n", low_output);
 	low_output *= btm_param.dy;
-	do_div(low_output, btm_param.adc_vref);
+	low_output = div64_s64(low_output, btm_param.adc_vref);
 	low_output += btm_param.adc_gnd;
 
 	rc = qpnp_adc_map_voltage_temp(
@@ -1457,7 +1466,7 @@
 
 	pr_debug("high_output:%lld\n", high_output);
 	high_output *= btm_param.dy;
-	do_div(high_output, btm_param.adc_vref);
+	high_output = div64_s64(high_output, btm_param.adc_vref);
 	high_output += btm_param.adc_gnd;
 
 	/* btm low temperature correspondes to high voltage threshold */
@@ -1500,7 +1509,7 @@
 
 	pr_debug("low_output:%lld\n", low_output);
 	low_output *= btm_param.dy;
-	do_div(low_output, btm_param.adc_vref);
+	low_output = div64_s64(low_output, btm_param.adc_vref);
 	low_output += btm_param.adc_gnd;
 
 	rc = qpnp_adc_map_voltage_temp(
@@ -1515,7 +1524,7 @@
 
 	pr_debug("high_output:%lld\n", high_output);
 	high_output *= btm_param.dy;
-	do_div(high_output, btm_param.adc_vref);
+	high_output = div64_s64(high_output, btm_param.adc_vref);
 	high_output += btm_param.adc_gnd;
 
 	/* btm low temperature correspondes to high voltage threshold */
@@ -1558,7 +1567,7 @@
 
 	pr_debug("low_output:%lld\n", low_output);
 	low_output *= btm_param.dy;
-	do_div(low_output, btm_param.adc_vref);
+	low_output = div64_s64(low_output, btm_param.adc_vref);
 	low_output += btm_param.adc_gnd;
 
 	rc = qpnp_adc_map_voltage_temp(
@@ -1573,7 +1582,7 @@
 
 	pr_debug("high_output:%lld\n", high_output);
 	high_output *= btm_param.dy;
-	do_div(high_output, btm_param.adc_vref);
+	high_output = div64_s64(high_output, btm_param.adc_vref);
 	high_output += btm_param.adc_gnd;
 
 	/* btm low temperature correspondes to high voltage threshold */
@@ -1616,7 +1625,7 @@
 
 	pr_debug("low_output:%lld\n", low_output);
 	low_output *= btm_param.dy;
-	do_div(low_output, btm_param.adc_vref);
+	low_output = div64_s64(low_output, btm_param.adc_vref);
 	low_output += btm_param.adc_gnd;
 
 	rc = qpnp_adc_map_voltage_temp(
@@ -1631,7 +1640,7 @@
 
 	pr_debug("high_output:%lld\n", high_output);
 	high_output *= btm_param.dy;
-	do_div(high_output, btm_param.adc_vref);
+	high_output = div64_s64(high_output, btm_param.adc_vref);
 	high_output += btm_param.adc_gnd;
 
 	/* btm low temperature correspondes to high voltage threshold */
@@ -2027,11 +2036,11 @@
 		pr_err("Invalid adc vdd reference property\n");
 		return -EINVAL;
 	}
-	rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
-			&adc_prop->bitresolution);
+	rc = of_property_read_u32(node, "qcom,adc-full-scale-code",
+			&adc_prop->full_scale_code);
 	if (rc) {
-		pr_err("Invalid adc bit resolution property\n");
-		return -EINVAL;
+		pr_debug("Use default value of 0x4000 for full scale\n");
+		adc_prop->full_scale_code = QPNP_VADC_HC_VREF_CODE;
 	}
 	adc_qpnp->adc_prop = adc_prop;
 
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index a597ef9..c4da780 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -27,17 +27,11 @@
 #include <linux/spmi.h>
 #include <linux/platform_device.h>
 #include <linux/of_irq.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
 #include <linux/interrupt.h>
 #include <linux/completion.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/platform_device.h>
-#ifdef CONFIG_WAKELOCK
-#include <linux/wakelock.h>
-#endif
 
 /* QPNP IADC register definition */
 #define QPNP_IADC_REVISION1				0x0
diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index ae494d7..8d242f8 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -156,6 +156,17 @@
           occurrence. These events can be controlled by using module
           parameters.
 
+config CORESIGHT_TGU
+	bool "CoreSight Trigger Generation Unit driver"
+	help
+	  This driver provides support for Trigger Generation Unit that is
+	  used to detect patterns or sequences on a given set of signals.
+	  TGU is used to monitor a particular bus within a given region to
+	  detect illegal transaction sequences or slave responses. It is also
+	  used to monitor a data stream to detect protocol violations and to
+	  provide a trigger point for centering data around a specific event
+	  within the trace data buffer.
+
 config CORESIGHT_CSR
 	bool "CoreSight Slave Register driver"
 	help
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index 0b5e434..cb47ecd 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -5,7 +5,8 @@
 obj-$(CONFIG_OF) += of_coresight.o
 obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \
 					     coresight-tmc-etf.o \
-					     coresight-tmc-etr.o
+					     coresight-tmc-etr.o \
+					     coresight-byte-cntr.o
 obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
 obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
 obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
@@ -21,6 +22,7 @@
 obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
 obj-$(CONFIG_CORESIGHT_EVENT) += coresight-event.o
 obj-$(CONFIG_CORESIGHT_CTI) += coresight-cti.o
+obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o
 obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
 obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
 obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c
new file mode 100644
index 0000000..7ef2710
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c
@@ -0,0 +1,373 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/of_irq.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+
+#include "coresight-byte-cntr.h"
+#include "coresight-priv.h"
+#include "coresight-tmc.h"
+
+static struct tmc_drvdata *tmcdrvdata;
+
+static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos,
+			       size_t bytes, size_t *len)
+{
+	if (*len >= bytes) {
+		atomic_dec(&byte_cntr_data->irq_cnt);
+		*len = bytes;
+	} else {
+		if (((uint32_t)*ppos % bytes) + *len > bytes)
+			*len = bytes - ((uint32_t)*ppos % bytes);
+		if ((*len + (uint32_t)*ppos) % bytes == 0)
+			atomic_dec(&byte_cntr_data->irq_cnt);
+	}
+}
+
+static void tmc_etr_sg_read_pos(loff_t *ppos,
+				size_t bytes, bool noirq, size_t *len,
+				char **bufpp)
+{
+	uint32_t rwp, i = 0;
+	uint32_t blk_num, sg_tbl_num, blk_num_loc, read_off;
+	uint32_t *virt_pte, *virt_st_tbl;
+	void *virt_blk;
+	phys_addr_t phys_pte;
+	int total_ents = DIV_ROUND_UP(tmcdrvdata->size, PAGE_SIZE);
+	int ents_per_pg = PAGE_SIZE/sizeof(uint32_t);
+
+	if (*len == 0)
+		return;
+
+	blk_num = *ppos / PAGE_SIZE;
+	read_off = *ppos % PAGE_SIZE;
+
+	virt_st_tbl = (uint32_t *)tmcdrvdata->vaddr;
+
+	/* Compute table index and block entry index within that table */
+	if (blk_num && (blk_num == (total_ents - 1)) &&
+	    !(blk_num % (ents_per_pg - 1))) {
+		sg_tbl_num = blk_num / ents_per_pg;
+		blk_num_loc = ents_per_pg - 1;
+	} else {
+		sg_tbl_num = blk_num / (ents_per_pg - 1);
+		blk_num_loc = blk_num % (ents_per_pg - 1);
+	}
+
+	for (i = 0; i < sg_tbl_num; i++) {
+		virt_pte = virt_st_tbl + (ents_per_pg - 1);
+		phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+		virt_st_tbl = (uint32_t *)phys_to_virt(phys_pte);
+	}
+
+	virt_pte = virt_st_tbl + blk_num_loc;
+	phys_pte = TMC_ETR_SG_ENT_TO_BLK(*virt_pte);
+	virt_blk = phys_to_virt(phys_pte);
+
+	*bufpp = (char *)(virt_blk + read_off);
+
+	if (noirq) {
+		rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP);
+		tmc_etr_sg_rwp_pos(tmcdrvdata, rwp);
+		if (tmcdrvdata->sg_blk_num == blk_num &&
+		    rwp >= (phys_pte + read_off))
+			*len = rwp - phys_pte - read_off;
+		else if (tmcdrvdata->sg_blk_num > blk_num)
+			*len = PAGE_SIZE - read_off;
+		else
+			*len = bytes;
+	} else {
+
+		if (*len > (PAGE_SIZE - read_off))
+			*len = PAGE_SIZE - read_off;
+
+		if (*len >= (bytes - ((uint32_t)*ppos % bytes)))
+			*len = bytes - ((uint32_t)*ppos % bytes);
+
+		if ((*len + (uint32_t)*ppos) % bytes == 0)
+			atomic_dec(&tmcdrvdata->byte_cntr->irq_cnt);
+	}
+
+	/*
+	 * Invalidate cache range before reading. This will make sure that CPU
+	 * reads latest contents from DDR
+	 */
+	dmac_inv_range((void *)(*bufpp), (void *)(*bufpp) + *len);
+}
+
+static irqreturn_t etr_handler(int irq, void *data)
+{
+	struct byte_cntr *byte_cntr_data = data;
+
+	atomic_inc(&byte_cntr_data->irq_cnt);
+
+	wake_up(&byte_cntr_data->wq);
+
+	return IRQ_HANDLED;
+}
+
+static void tmc_etr_flush_bytes(loff_t *ppos, size_t bytes, size_t *len)
+{
+	uint32_t rwp = 0;
+
+	rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP);
+
+	if (rwp >= (tmcdrvdata->paddr + *ppos)) {
+		if (bytes > (rwp - tmcdrvdata->paddr - *ppos))
+			*len = rwp - tmcdrvdata->paddr - *ppos;
+	}
+}
+
+static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data,
+			       size_t len, loff_t *ppos)
+{
+	struct byte_cntr *byte_cntr_data = fp->private_data;
+	char *bufp;
+
+	if (!data)
+		return -EINVAL;
+
+	mutex_lock(&byte_cntr_data->byte_cntr_lock);
+	if (!byte_cntr_data->read_active)
+		goto err0;
+
+	if (byte_cntr_data->enable) {
+		if (!atomic_read(&byte_cntr_data->irq_cnt)) {
+			mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+			if (wait_event_interruptible(byte_cntr_data->wq,
+				atomic_read(&byte_cntr_data->irq_cnt) > 0))
+				return -ERESTARTSYS;
+			mutex_lock(&byte_cntr_data->byte_cntr_lock);
+			if (!byte_cntr_data->read_active)
+				goto err0;
+		}
+		bufp = (char *)(tmcdrvdata->vaddr + *ppos);
+
+		if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG)
+			tmc_etr_read_bytes(byte_cntr_data, ppos,
+					   byte_cntr_data->block_size, &len);
+		else
+			tmc_etr_sg_read_pos(ppos, byte_cntr_data->block_size, 0,
+					    &len, &bufp);
+
+	} else {
+		if (!atomic_read(&byte_cntr_data->irq_cnt)) {
+			if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG)
+				tmc_etr_flush_bytes(ppos,
+						    byte_cntr_data->block_size,
+						    &len);
+			else
+				tmc_etr_sg_read_pos(ppos,
+						    byte_cntr_data->block_size,
+						    1,
+						    &len, &bufp);
+			if (!len)
+				goto err0;
+		} else {
+			if (tmcdrvdata->mem_type == TMC_ETR_MEM_TYPE_CONTIG)
+				tmc_etr_read_bytes(byte_cntr_data, ppos,
+						   byte_cntr_data->block_size,
+						   &len);
+			else
+				tmc_etr_sg_read_pos(ppos,
+						    byte_cntr_data->block_size,
+						    1,
+						    &len, &bufp);
+		}
+	}
+
+	if (copy_to_user(data, bufp, len)) {
+		mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+		dev_dbg(tmcdrvdata->dev, "%s: copy_to_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	if (*ppos + len >= tmcdrvdata->size)
+		*ppos = 0;
+	else
+		*ppos += len;
+err0:
+	mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+
+	return len;
+}
+
+void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data)
+{
+	if (!byte_cntr_data)
+		return;
+
+	mutex_lock(&byte_cntr_data->byte_cntr_lock);
+
+	if (byte_cntr_data->block_size == 0) {
+		mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+		return;
+	}
+
+	atomic_set(&byte_cntr_data->irq_cnt, 0);
+	byte_cntr_data->enable = true;
+	mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+}
+EXPORT_SYMBOL(tmc_etr_byte_cntr_start);
+
+void tmc_etr_byte_cntr_stop(struct byte_cntr *byte_cntr_data)
+{
+	if (!byte_cntr_data)
+		return;
+
+	mutex_lock(&byte_cntr_data->byte_cntr_lock);
+	byte_cntr_data->enable = false;
+	coresight_csr_set_byte_cntr(0);
+	mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+
+}
+EXPORT_SYMBOL(tmc_etr_byte_cntr_stop);
+
+
+static int tmc_etr_byte_cntr_release(struct inode *in, struct file *fp)
+{
+	struct byte_cntr *byte_cntr_data = fp->private_data;
+
+	mutex_lock(&byte_cntr_data->byte_cntr_lock);
+	byte_cntr_data->read_active = false;
+
+	coresight_csr_set_byte_cntr(0);
+	mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+
+	return 0;
+}
+
+static int tmc_etr_byte_cntr_open(struct inode *in, struct file *fp)
+{
+	struct byte_cntr *byte_cntr_data =
+			container_of(in->i_cdev, struct byte_cntr, dev);
+
+	mutex_lock(&byte_cntr_data->byte_cntr_lock);
+
+	if (!tmcdrvdata->enable || !byte_cntr_data->block_size) {
+		mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+		return -EINVAL;
+	}
+
+	coresight_csr_set_byte_cntr(byte_cntr_data->block_size);
+	fp->private_data = byte_cntr_data;
+	nonseekable_open(in, fp);
+	byte_cntr_data->enable = true;
+	byte_cntr_data->read_active = true;
+	mutex_unlock(&byte_cntr_data->byte_cntr_lock);
+
+	return 0;
+}
+
+static const struct file_operations byte_cntr_fops = {
+	.owner		= THIS_MODULE,
+	.open		= tmc_etr_byte_cntr_open,
+	.read		= tmc_etr_byte_cntr_read,
+	.release	= tmc_etr_byte_cntr_release,
+	.llseek		= no_llseek,
+};
+
+static int byte_cntr_register_chardev(struct byte_cntr *byte_cntr_data)
+{
+	int ret;
+	unsigned int baseminor = 0;
+	unsigned int count = 1;
+	struct device *device;
+	dev_t dev;
+
+	ret = alloc_chrdev_region(&dev, baseminor, count, "byte-cntr");
+	if (ret < 0) {
+		pr_err("alloc_chrdev_region failed %d\n", ret);
+		return ret;
+	}
+	cdev_init(&byte_cntr_data->dev, &byte_cntr_fops);
+
+	byte_cntr_data->dev.owner = THIS_MODULE;
+	byte_cntr_data->dev.ops = &byte_cntr_fops;
+
+	ret = cdev_add(&byte_cntr_data->dev, dev, 1);
+	if (ret)
+		goto exit_unreg_chrdev_region;
+
+	byte_cntr_data->driver_class = class_create(THIS_MODULE,
+						   "coresight-tmc-etr-stream");
+	if (IS_ERR(byte_cntr_data->driver_class)) {
+		ret = -ENOMEM;
+		pr_err("class_create failed %d\n", ret);
+		goto exit_unreg_chrdev_region;
+	}
+
+	device = device_create(byte_cntr_data->driver_class, NULL,
+			       byte_cntr_data->dev.dev, byte_cntr_data,
+			       "byte-cntr");
+
+	if (IS_ERR(device)) {
+		pr_err("class_device_create failed %d\n", ret);
+		ret = -ENOMEM;
+		goto exit_destroy_class;
+	}
+
+	return 0;
+
+exit_destroy_class:
+	class_destroy(byte_cntr_data->driver_class);
+exit_unreg_chrdev_region:
+	unregister_chrdev_region(byte_cntr_data->dev.dev, 1);
+	return ret;
+}
+
+struct byte_cntr *byte_cntr_init(struct amba_device *adev,
+				 struct tmc_drvdata *drvdata)
+{
+	struct device *dev = &adev->dev;
+	struct device_node *np = adev->dev.of_node;
+	int byte_cntr_irq;
+	int ret;
+	struct byte_cntr *byte_cntr_data;
+
+	byte_cntr_irq = of_irq_get_byname(np, "byte-cntr-irq");
+	if (byte_cntr_irq < 0)
+		return NULL;
+
+	byte_cntr_data = devm_kzalloc(dev, sizeof(*byte_cntr_data), GFP_KERNEL);
+	if (!byte_cntr_data)
+		return NULL;
+
+	ret = devm_request_irq(dev, byte_cntr_irq, etr_handler,
+			       IRQF_TRIGGER_RISING | IRQF_SHARED,
+			       "tmc-etr", byte_cntr_data);
+	if (ret) {
+		devm_kfree(dev, byte_cntr_data);
+		dev_err(dev, "Byte_cntr interrupt registration failed\n");
+		return NULL;
+	}
+
+	ret = byte_cntr_register_chardev(byte_cntr_data);
+	if (ret) {
+		devm_free_irq(dev, byte_cntr_irq, byte_cntr_data);
+		devm_kfree(dev, byte_cntr_data);
+		dev_err(dev, "Byte_cntr char dev registration failed\n");
+		return NULL;
+	}
+
+	tmcdrvdata = drvdata;
+	byte_cntr_data->byte_cntr_irq = byte_cntr_irq;
+	atomic_set(&byte_cntr_data->irq_cnt, 0);
+	init_waitqueue_head(&byte_cntr_data->wq);
+	mutex_init(&byte_cntr_data->byte_cntr_lock);
+
+	return byte_cntr_data;
+}
+EXPORT_SYMBOL(byte_cntr_init);
diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.h b/drivers/hwtracing/coresight/coresight-byte-cntr.h
new file mode 100644
index 0000000..94e9089
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-byte-cntr.h
@@ -0,0 +1,24 @@
+#ifndef _CORESIGHT_BYTE_CNTR_H
+#define _CORESIGHT_BYTE_CNTR_H
+#include <linux/cdev.h>
+#include <linux/amba/bus.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+
+struct byte_cntr {
+	struct cdev		dev;
+	struct class		*driver_class;
+	bool			enable;
+	bool			read_active;
+	uint32_t		byte_cntr_value;
+	uint32_t		block_size;
+	int			byte_cntr_irq;
+	atomic_t		irq_cnt;
+	wait_queue_head_t	wq;
+	struct mutex		byte_cntr_lock;
+};
+
+extern void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data);
+extern void tmc_etr_byte_cntr_stop(struct byte_cntr *byte_cntr_data);
+
+#endif
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c
index 71e4103..ee4b8b4 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x.c
@@ -976,7 +976,12 @@
 
 	spin_lock_init(&drvdata->spinlock);
 
-	drvdata->cpu = pdata ? pdata->cpu : 0;
+	drvdata->cpu = pdata ? pdata->cpu : -1;
+
+	if (drvdata->cpu == -1) {
+		dev_info(dev, "CPU not available\n");
+		return -ENODEV;
+	}
 
 	get_online_cpus();
 
diff --git a/drivers/hwtracing/coresight/coresight-ost.c b/drivers/hwtracing/coresight/coresight-ost.c
index 3399c27..a5075ba 100644
--- a/drivers/hwtracing/coresight/coresight-ost.c
+++ b/drivers/hwtracing/coresight/coresight-ost.c
@@ -123,13 +123,14 @@
 
 static int stm_trace_data_header(void __iomem *addr)
 {
-	char hdr[16];
+	char hdr[24];
 	int len = 0;
 
-	*(uint16_t *)(hdr) = STM_MAKE_VERSION(0, 1);
+	*(uint16_t *)(hdr) = STM_MAKE_VERSION(0, 2);
 	*(uint16_t *)(hdr + 2) = STM_HEADER_MAGIC;
 	*(uint32_t *)(hdr + 4) = raw_smp_processor_id();
 	*(uint64_t *)(hdr + 8) = sched_clock();
+	*(uint64_t *)(hdr + 16) = task_tgid_nr(get_current());
 
 	len += stm_ost_send(addr, hdr, sizeof(hdr));
 	len += stm_ost_send(addr, current->comm, TASK_COMM_LEN);
diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
index 3af358a..afe9f3d 100644
--- a/drivers/hwtracing/coresight/coresight-priv.h
+++ b/drivers/hwtracing/coresight/coresight-priv.h
@@ -153,12 +153,14 @@
 extern void msm_qdss_csr_disable_bam_to_usb(void);
 extern void msm_qdss_csr_disable_flush(void);
 extern int coresight_csr_hwctrl_set(uint64_t addr, uint32_t val);
+extern void coresight_csr_set_byte_cntr(uint32_t count);
 #else
 static inline void msm_qdss_csr_enable_bam_to_usb(void) {}
 static inline void msm_qdss_csr_disable_bam_to_usb(void) {}
 static inline void msm_qdss_csr_disable_flush(void) {}
-static inline int coresight_csr_hwctrl_set(uint64_t addr,	95
+static inline int coresight_csr_hwctrl_set(uint64_t addr,
 					   uint32_t val) { return -EINVAL; }
+static inline void coresight_csr_set_byte_cntr(uint32_t count) {}
 #endif
 
 #endif
diff --git a/drivers/hwtracing/coresight/coresight-tgu.c b/drivers/hwtracing/coresight/coresight-tgu.c
new file mode 100644
index 0000000..e919f47
--- /dev/null
+++ b/drivers/hwtracing/coresight/coresight-tgu.c
@@ -0,0 +1,534 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/amba/bus.h>
+#include <linux/topology.h>
+#include <linux/of.h>
+#include <linux/coresight.h>
+
+#include "coresight-priv.h"
+
+#define tgu_writel(drvdata, val, off)	__raw_writel((val), drvdata->base + off)
+#define tgu_readl(drvdata, off)		__raw_readl(drvdata->base + off)
+
+#define TGU_LOCK(drvdata)						\
+do {									\
+	mb(); /* ensure configuration take effect before we lock it */	\
+	tgu_writel(drvdata, 0x0, CORESIGHT_LAR);			\
+} while (0)
+#define TGU_UNLOCK(drvdata)						\
+do {									\
+	tgu_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	mb(); /* ensure unlock take effect before we configure */	\
+} while (0)
+
+#define TGU_CONTROL			0x0000
+#define TIMER0_STATUS			0x0004
+#define COUNTER0_STATUS			0x000C
+#define TGU_STATUS			0x0014
+#define TIMER0_COMPARE_STEP(n)		(0x0040 + 0x1D8 * n)
+#define COUNTER0_COMPARE_STEP(n)	(0x0048 + 0x1D8 * n)
+#define GROUP_REG_STEP(grp, reg, step)	(0x0074 + 0x60 * grp + 0x4 * reg + \
+								 0x1D8 * step)
+#define CONDITION_DECODE_STEP(m, n)	(0x0050 + 0x4 * m + 0x1D8 * n)
+#define CONDITION_SELECT_STEP(m, n)	(0x0060 + 0x4 * m + 0x1D8 * n)
+#define GROUP0				0x0074
+#define GROUP1				0x00D4
+#define GROUP2				0x0134
+#define GROUP3				0x0194
+#define TGU_LAR				0x0FB0
+
+#define MAX_GROUP_SETS			256
+#define MAX_GROUPS			4
+#define MAX_CONDITION_SETS		64
+#define MAX_TIMER_COUNTER_SETS		8
+
+#define to_tgu_drvdata(c)		container_of(c, struct tgu_drvdata, tgu)
+
+struct Trigger_group_data {
+	unsigned long grpaddr;
+	unsigned long value;
+};
+
+struct Trigger_condition_data {
+	unsigned long condaddr;
+	unsigned long value;
+};
+
+struct Trigger_select_data {
+	unsigned long selectaddr;
+	unsigned long value;
+};
+
+struct Trigger_timer_data {
+	unsigned long timeraddr;
+	unsigned long value;
+};
+
+struct Trigger_counter_data {
+	unsigned long counteraddr;
+	unsigned long value;
+};
+struct tgu_drvdata {
+	void __iomem			*base;
+	struct device			*dev;
+	struct coresight_device		*csdev;
+	struct clk			*clk;
+	spinlock_t			spinlock;
+	int				max_steps;
+	int				max_conditions;
+	int				max_regs;
+	int				max_timer_counter;
+	struct Trigger_group_data	*grp_data;
+	struct Trigger_condition_data	*condition_data;
+	struct Trigger_select_data	*select_data;
+	struct Trigger_timer_data	*timer_data;
+	struct Trigger_counter_data	*counter_data;
+	int				grp_refcnt;
+	int				cond_refcnt;
+	int				select_refcnt;
+	int				timer_refcnt;
+	int				counter_refcnt;
+	bool				enable;
+};
+
+static ssize_t enable_tgu(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	unsigned long value;
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	int ret, i, j;
+
+	if (kstrtoul(buf, 16, &value))
+		return -EINVAL;
+
+	/* Enable clock */
+	ret = pm_runtime_get_sync(drvdata->dev);
+	if (ret)
+		return ret;
+
+	spin_lock(&drvdata->spinlock);
+	/* Unlock the TGU LAR */
+	TGU_UNLOCK(drvdata);
+
+	if (value) {
+
+		/* Disable TGU to program the triggers */
+		tgu_writel(drvdata, 0, TGU_CONTROL);
+
+		/* program the TGU Group data for the desired use case*/
+
+		for (i = 0; i <= drvdata->grp_refcnt; i++)
+			tgu_writel(drvdata, drvdata->grp_data[i].value,
+						drvdata->grp_data[i].grpaddr);
+
+		/* program the unused Condition Decode registers NOT bits to 1*/
+		for (i = 0; i <= drvdata->max_conditions; i++) {
+			for (j = 0; j <= drvdata->max_steps; j++)
+				tgu_writel(drvdata, 0x1000000,
+						CONDITION_DECODE_STEP(i, j));
+		}
+		/* program the TGU Condition Decode for the desired use case*/
+		for (i = 0; i <= drvdata->cond_refcnt; i++)
+			tgu_writel(drvdata, drvdata->condition_data[i].value,
+					drvdata->condition_data[i].condaddr);
+
+		/* program the TGU Condition Select for the desired use case*/
+		for (i = 0; i <= drvdata->select_refcnt; i++)
+			tgu_writel(drvdata, drvdata->select_data[i].value,
+					drvdata->select_data[i].selectaddr);
+
+		/*  Timer and Counter Check */
+		for (i = 0; i <= drvdata->timer_refcnt; i++)
+			tgu_writel(drvdata, drvdata->timer_data[i].value,
+					drvdata->timer_data[i].timeraddr);
+
+		for (i = 0; i <= drvdata->counter_refcnt; i++)
+			tgu_writel(drvdata, drvdata->counter_data[i].value,
+					drvdata->counter_data[i].counteraddr);
+
+		/* Enable TGU to program the triggers */
+		tgu_writel(drvdata, 1, TGU_CONTROL);
+
+		drvdata->enable = true;
+		dev_dbg(dev, "Coresight-TGU enabled\n");
+
+	} else {
+		/* Disable TGU to program the triggers */
+		tgu_writel(drvdata, 0, TGU_CONTROL);
+		TGU_LOCK(drvdata);
+		spin_unlock(&drvdata->spinlock);
+
+		pm_runtime_put(drvdata->dev);
+		dev_dbg(dev, "Coresight-TGU disabled\n");
+	}
+
+	TGU_LOCK(drvdata);
+	spin_unlock(&drvdata->spinlock);
+	return size;
+}
+static DEVICE_ATTR(enable_tgu, 0200, NULL, enable_tgu);
+
+static ssize_t reset_tgu(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t size)
+{
+	unsigned long value;
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	int ret;
+
+	if (kstrtoul(buf, 16, &value))
+		return -EINVAL;
+
+	if (!drvdata->enable) {
+		/* Enable clock */
+		ret = pm_runtime_get_sync(drvdata->dev);
+		if (ret)
+			return ret;
+	}
+
+	spin_lock(&drvdata->spinlock);
+	/* Unlock the TGU LAR */
+	TGU_UNLOCK(drvdata);
+
+	if (value) {
+		/* Disable TGU to program the triggers */
+		tgu_writel(drvdata, 0, TGU_CONTROL);
+
+		/* Reset the Reference counters*/
+		drvdata->grp_refcnt = 0;
+		drvdata->cond_refcnt = 0;
+		drvdata->select_refcnt = 0;
+		drvdata->timer_refcnt = 0;
+		drvdata->counter_refcnt = 0;
+
+		dev_dbg(dev, "Coresight-TGU disabled\n");
+	} else
+		dev_dbg(dev, "Invalid input to reset the TGU\n");
+
+	TGU_LOCK(drvdata);
+	spin_unlock(&drvdata->spinlock);
+	pm_runtime_put(drvdata->dev);
+	return size;
+}
+static DEVICE_ATTR(reset_tgu, 0200, NULL, reset_tgu);
+
+static ssize_t set_group(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t size)
+{
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	int grp, reg, step;
+	unsigned long value;
+
+	if (drvdata->grp_refcnt >= MAX_GROUP_SETS) {
+		dev_err(drvdata->dev, " Too many groups are being configured");
+		return -EINVAL;
+	}
+
+	if (sscanf(buf, "%d %d %d %lx", &grp, &reg, &step, &value) != 4)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	if ((grp <= MAX_GROUPS) && (reg <= drvdata->max_regs)) {
+		drvdata->grp_data[drvdata->grp_refcnt].grpaddr =
+						GROUP_REG_STEP(grp, reg, step);
+		drvdata->grp_data[drvdata->grp_refcnt].value = value;
+		drvdata->grp_refcnt++;
+	} else
+		dev_err(drvdata->dev, "Invalid group data\n");
+
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR(set_group, 0200, NULL, set_group);
+
+static ssize_t tgu_set_condition(struct device *dev, struct device_attribute
+					*attr, const char *buf, size_t size)
+{
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long value;
+	int cond, step;
+
+	if (drvdata->cond_refcnt >= MAX_CONDITION_SETS) {
+		dev_err(drvdata->dev, " Too many groups are being configured");
+		return -EINVAL;
+	}
+
+	if (sscanf(buf, "%d %d %lx", &cond, &step, &value) != 3)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	if ((cond <= drvdata->max_conditions) && (step <=
+						drvdata->max_steps)) {
+		drvdata->condition_data[drvdata->cond_refcnt].condaddr =
+					CONDITION_DECODE_STEP(cond, step);
+		drvdata->condition_data[drvdata->cond_refcnt].value = value;
+		drvdata->cond_refcnt++;
+	} else
+		dev_err(drvdata->dev, "Invalid condition decode data\n");
+
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR(set_condition, 0200, NULL, tgu_set_condition);
+
+static ssize_t tgu_set_select(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t size)
+{
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long value;
+	int select, step;
+
+	if (drvdata->select_refcnt >= MAX_CONDITION_SETS) {
+		dev_err(drvdata->dev, " Too many groups are being configured");
+		return -EINVAL;
+	}
+
+	if (sscanf(buf, "%d %d %lx", &select, &step, &value) != 3)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+
+	if ((select <= drvdata->max_conditions) && (step <=
+					drvdata->max_steps)) {
+		drvdata->select_data[drvdata->select_refcnt].selectaddr =
+					CONDITION_SELECT_STEP(select, step);
+		drvdata->select_data[drvdata->select_refcnt].value = value;
+		drvdata->select_refcnt++;
+	} else
+		dev_err(drvdata->dev, "Invalid select decode data\n");
+
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR(set_select, 0200, NULL, tgu_set_select);
+
+static ssize_t tgu_set_timers(struct device *dev, struct device_attribute *attr,
+						const char *buf, size_t size)
+{
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long value;
+	int step;
+
+	if (drvdata->select_refcnt >= MAX_TIMER_COUNTER_SETS) {
+		dev_err(drvdata->dev, " Too many groups are being configured");
+		return -EINVAL;
+	}
+
+	if (sscanf(buf, "%d %lx", &step, &value) != 2)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	if (step <= drvdata->max_timer_counter) {
+		drvdata->timer_data[drvdata->timer_refcnt].timeraddr =
+						TIMER0_COMPARE_STEP(step);
+		drvdata->timer_data[drvdata->timer_refcnt].value = value;
+		drvdata->timer_refcnt++;
+	} else
+		dev_err(drvdata->dev, "Invalid TGU timer data\n");
+
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR(set_timer, 0200, NULL, tgu_set_timers);
+
+static ssize_t tgu_set_counters(struct device *dev, struct device_attribute
+					*attr, const char *buf, size_t size)
+{
+	struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long value;
+	int step;
+
+	if (drvdata->counter_refcnt >= MAX_TIMER_COUNTER_SETS) {
+		dev_err(drvdata->dev, " Too many groups are being configured");
+		return -EINVAL;
+	}
+
+	if (sscanf(buf, "%d %lx", &step, &value) != 2)
+		return -EINVAL;
+
+	spin_lock(&drvdata->spinlock);
+	if (step <= drvdata->max_timer_counter) {
+		drvdata->counter_data[drvdata->counter_refcnt].counteraddr =
+						COUNTER0_COMPARE_STEP(step);
+		drvdata->counter_data[drvdata->counter_refcnt].value = value;
+		drvdata->counter_refcnt++;
+	} else
+		dev_err(drvdata->dev, "Invalid TGU counter data\n");
+
+	spin_unlock(&drvdata->spinlock);
+
+	return size;
+}
+static DEVICE_ATTR(set_counter, 0200, NULL, tgu_set_counters);
+
+static struct attribute *tgu_attrs[] = {
+	&dev_attr_enable_tgu.attr,
+	&dev_attr_reset_tgu.attr,
+	&dev_attr_set_group.attr,
+	&dev_attr_set_condition.attr,
+	&dev_attr_set_select.attr,
+	&dev_attr_set_timer.attr,
+	&dev_attr_set_counter.attr,
+	NULL,
+};
+
+static struct attribute_group tgu_attr_grp = {
+	.attrs = tgu_attrs,
+};
+
+static const struct attribute_group *tgu_attr_grps[] = {
+	&tgu_attr_grp,
+	NULL,
+};
+
+static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
+{
+	int ret = 0;
+	struct device *dev = &adev->dev;
+	struct coresight_platform_data *pdata;
+	struct tgu_drvdata *drvdata;
+	struct coresight_desc *desc;
+
+	pdata = of_get_coresight_platform_data(dev, adev->dev.of_node);
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+	adev->dev.platform_data = pdata;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+
+	drvdata->dev = &adev->dev;
+
+	dev_set_drvdata(dev, drvdata);
+
+	drvdata->base = devm_ioremap_resource(dev, &adev->res);
+	if (!drvdata->base)
+		return -ENOMEM;
+
+	spin_lock_init(&drvdata->spinlock);
+
+	ret = of_property_read_u32(adev->dev.of_node, "tgu-steps",
+						&drvdata->max_steps);
+	if (ret)
+		return -EINVAL;
+
+	ret = of_property_read_u32(adev->dev.of_node, "tgu-conditions",
+						&drvdata->max_conditions);
+	if (ret)
+		return -EINVAL;
+
+	ret = of_property_read_u32(adev->dev.of_node, "tgu-regs",
+							&drvdata->max_regs);
+	if (ret)
+		return -EINVAL;
+
+	ret = of_property_read_u32(adev->dev.of_node, "tgu-timer-counters",
+						&drvdata->max_timer_counter);
+	if (ret)
+		return -EINVAL;
+
+	/* Alloc memory for Grps, Conditions and Steps */
+	drvdata->grp_data = devm_kzalloc(dev, MAX_GROUP_SETS *
+				       sizeof(*drvdata->grp_data),
+				       GFP_KERNEL);
+	if (!drvdata->grp_data)
+		return -ENOMEM;
+
+	drvdata->condition_data = devm_kzalloc(dev, MAX_CONDITION_SETS *
+				       sizeof(*drvdata->condition_data),
+				       GFP_KERNEL);
+
+	if (!drvdata->condition_data)
+		return -ENOMEM;
+
+	drvdata->select_data = devm_kzalloc(dev, MAX_CONDITION_SETS *
+				       sizeof(*drvdata->select_data),
+				       GFP_KERNEL);
+	if (!drvdata->select_data)
+		return -ENOMEM;
+
+	drvdata->timer_data = devm_kzalloc(dev, MAX_TIMER_COUNTER_SETS *
+				       sizeof(*drvdata->timer_data),
+				       GFP_KERNEL);
+	if (!drvdata->timer_data)
+		return -ENOMEM;
+
+	drvdata->counter_data = devm_kzalloc(dev, MAX_TIMER_COUNTER_SETS *
+				       sizeof(*drvdata->counter_data),
+				       GFP_KERNEL);
+	if (!drvdata->counter_data)
+		return -ENOMEM;
+
+	drvdata->enable = false;
+
+	desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+	if (!desc) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	desc->type = CORESIGHT_DEV_TYPE_NONE;
+	desc->pdata = adev->dev.platform_data;
+	desc->dev = &adev->dev;
+	desc->groups = tgu_attr_grps;
+	drvdata->csdev = coresight_register(desc);
+	if (IS_ERR(drvdata->csdev)) {
+		ret = PTR_ERR(drvdata->csdev);
+		goto err;
+	}
+
+	pm_runtime_put(&adev->dev);
+	dev_dbg(dev, "TGU initialized\n");
+	return 0;
+err:
+	pm_runtime_put(&adev->dev);
+	return ret;
+}
+
+static struct amba_id tgu_ids[] = {
+	{
+		.id	=	0x0003b999,
+		.mask	=	0x0003ffff,
+		.data	=	"TGU",
+	},
+	{ 0, 0},
+};
+
+static struct amba_driver tgu_driver = {
+	.drv = {
+		.name			=	"coresight-tgu",
+		.owner			=	THIS_MODULE,
+		.suppress_bind_attrs	=	true,
+	},
+	.probe		=	tgu_probe,
+	.id_table	=	tgu_ids,
+};
+
+builtin_amba_driver(tgu_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CoreSight TGU driver");
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index 2a5a1bd..eb70e7a 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -351,7 +351,7 @@
 	tmc_etr_sg_tbl_flush(vaddr, size);
 }
 
-static void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp)
+void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp)
 {
 	uint32_t i = 0, pte_n = 0, last_pte;
 	uint32_t *virt_st_tbl, *virt_pte;
@@ -403,6 +403,7 @@
 			break;
 	}
 }
+EXPORT_SYMBOL(tmc_etr_sg_rwp_pos);
 
 static void tmc_etr_mem_reset(struct tmc_drvdata *drvdata)
 {
@@ -831,6 +832,8 @@
 	drvdata->sticky_enable = true;
 out:
 	spin_unlock_irqrestore(&drvdata->spinlock, flags);
+	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
+		tmc_etr_byte_cntr_start(drvdata->byte_cntr);
 	mutex_unlock(&drvdata->mem_lock);
 
 	if (!ret)
@@ -919,6 +922,7 @@
 	if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
 		coresight_cti_unmap_trigin(drvdata->cti_reset, 2, 0);
 		coresight_cti_unmap_trigout(drvdata->cti_flush, 3, 0);
+		tmc_etr_byte_cntr_stop(drvdata->byte_cntr);
 	}
 out:
 	mutex_unlock(&drvdata->mem_lock);
@@ -990,6 +994,11 @@
 		goto out;
 	}
 
+	if (drvdata->byte_cntr && drvdata->byte_cntr->enable) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	/* Disable the TMC if need be */
 	if (val == CS_MODE_SYSFS)
 		tmc_etr_disable_hw(drvdata);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
index b97ebb8..6f13eb3 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.c
+++ b/drivers/hwtracing/coresight/coresight-tmc.c
@@ -459,6 +459,42 @@
 }
 static DEVICE_ATTR_RW(mem_type);
 
+static ssize_t block_size_show(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	uint32_t val = 0;
+
+	if (drvdata->byte_cntr)
+		val = drvdata->byte_cntr->block_size;
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n",
+			val);
+}
+
+static ssize_t block_size_store(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *buf,
+			      size_t size)
+{
+	struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
+	unsigned long val;
+
+	if (kstrtoul(buf, 0, &val))
+		return -EINVAL;
+
+	if (!drvdata->byte_cntr)
+		return -EINVAL;
+
+	mutex_lock(&drvdata->byte_cntr->byte_cntr_lock);
+	drvdata->byte_cntr->block_size = val * 8;
+	mutex_unlock(&drvdata->byte_cntr->byte_cntr_lock);
+
+	return size;
+}
+static DEVICE_ATTR_RW(block_size);
+
 static struct attribute *coresight_tmc_etf_attrs[] = {
 	&dev_attr_trigger_cntr.attr,
 	NULL,
@@ -470,6 +506,7 @@
 	&dev_attr_trigger_cntr.attr,
 	&dev_attr_out_mode.attr,
 	&dev_attr_available_out_modes.attr,
+	&dev_attr_block_size.attr,
 	NULL,
 };
 
@@ -587,6 +624,8 @@
 		desc.groups = coresight_tmc_etr_groups;
 		desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
 
+		drvdata->byte_cntr = byte_cntr_init(adev, drvdata);
+
 		ret = tmc_etr_bam_init(adev, drvdata);
 		if (ret)
 			goto out;
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 6643adc..fe6bc76 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -27,6 +27,8 @@
 #include <linux/usb/usb_qdss.h>
 #include <linux/coresight-cti.h>
 
+#include "coresight-byte-cntr.h"
+
 #define TMC_RSZ			0x004
 #define TMC_STS			0x00c
 #define TMC_RRD			0x010
@@ -167,7 +169,7 @@
 	bool			enable;
 	char			*buf;
 	dma_addr_t		paddr;
-	void __iomem		*vaddr;
+	void			*vaddr;
 	u32			size;
 	u32			len;
 	local_t			mode;
@@ -187,6 +189,7 @@
 	bool			sticky_enable;
 	struct coresight_cti	*cti_flush;
 	struct coresight_cti	*cti_reset;
+	struct byte_cntr	*byte_cntr;
 };
 
 /* Generic functions */
@@ -214,5 +217,9 @@
 		  struct usb_qdss_ch *ch);
 int tmc_etr_bam_init(struct amba_device *adev,
 		     struct tmc_drvdata *drvdata);
+extern struct byte_cntr *byte_cntr_init(struct amba_device *adev,
+					struct tmc_drvdata *drvdata);
+extern void tmc_etr_sg_rwp_pos(struct tmc_drvdata *drvdata, uint32_t rwp);
+
 extern const struct coresight_ops tmc_etr_cs_ops;
 #endif
diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
index 81bbd78..5473fcf 100644
--- a/drivers/hwtracing/coresight/of_coresight.c
+++ b/drivers/hwtracing/coresight/of_coresight.c
@@ -182,8 +182,8 @@
 		} while (ep);
 	}
 
-	/* Affinity defaults to CPU0 */
-	pdata->cpu = 0;
+	/* Affinity defaults to invalid */
+	pdata->cpu = -1;
 	dn = of_parse_phandle(node, "cpu", 0);
 	for (cpu = 0; dn && cpu < nr_cpu_ids; cpu++) {
 		if (dn == of_get_cpu_node(cpu, NULL)) {
diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c
index 0bba384..63b5db4 100644
--- a/drivers/hwtracing/intel_th/pci.c
+++ b/drivers/hwtracing/intel_th/pci.c
@@ -85,6 +85,16 @@
 		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa2a6),
 		.driver_data = (kernel_ulong_t)0,
 	},
+	{
+		/* Cannon Lake H */
+		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326),
+		.driver_data = (kernel_ulong_t)0,
+	},
+	{
+		/* Cannon Lake LP */
+		PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6),
+		.driver_data = (kernel_ulong_t)0,
+	},
 	{ 0 },
 };
 
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 5269890..2dd60ee 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -1125,7 +1125,7 @@
 
 	stm_source_link_drop(src);
 
-	device_destroy(&stm_source_class, src->dev.devt);
+	device_unregister(&src->dev);
 }
 EXPORT_SYMBOL_GPL(stm_source_unregister_device);
 
diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c
index 0b86c61..c925a69 100644
--- a/drivers/i2c/busses/i2c-at91.c
+++ b/drivers/i2c/busses/i2c-at91.c
@@ -1180,6 +1180,7 @@
 
 static int at91_twi_resume_noirq(struct device *dev)
 {
+	struct at91_twi_dev *twi_dev = dev_get_drvdata(dev);
 	int ret;
 
 	if (!pm_runtime_status_suspended(dev)) {
@@ -1191,6 +1192,8 @@
 	pm_runtime_mark_last_busy(dev);
 	pm_request_autosuspend(dev);
 
+	at91_init_twi_bus(twi_dev);
+
 	return 0;
 }
 
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 0b42a12..b42d95f 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -319,7 +319,7 @@
 #endif
 
 #ifdef CONFIG_PM
-static int dw_i2c_plat_suspend(struct device *dev)
+static int dw_i2c_plat_runtime_suspend(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
@@ -343,11 +343,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int dw_i2c_plat_suspend(struct device *dev)
+{
+	pm_runtime_resume(dev);
+	return dw_i2c_plat_runtime_suspend(dev);
+}
+#endif
+
 static const struct dev_pm_ops dw_i2c_dev_pm_ops = {
 	.prepare = dw_i2c_plat_prepare,
 	.complete = dw_i2c_plat_complete,
 	SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume)
-	SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL)
+	SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend,
+			   dw_i2c_plat_resume,
+			   NULL)
 };
 
 #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops)
diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c
index f573448..7aea288 100644
--- a/drivers/i2c/busses/i2c-ismt.c
+++ b/drivers/i2c/busses/i2c-ismt.c
@@ -340,6 +340,11 @@
 			data->word = dma_buffer[0] | (dma_buffer[1] << 8);
 			break;
 		case I2C_SMBUS_BLOCK_DATA:
+			if (desc->rxbytes != dma_buffer[0] + 1)
+				return -EMSGSIZE;
+
+			memcpy(data->block, dma_buffer, desc->rxbytes);
+			break;
 		case I2C_SMBUS_I2C_BLOCK_DATA:
 			memcpy(&data->block[1], dma_buffer, desc->rxbytes);
 			data->block[0] = desc->rxbytes;
diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c
index 2aa61bb..73b97c7 100644
--- a/drivers/i2c/busses/i2c-meson.c
+++ b/drivers/i2c/busses/i2c-meson.c
@@ -175,7 +175,7 @@
 		wdata1 |= *buf++ << ((i - 4) * 8);
 
 	writel(wdata0, i2c->regs + REG_TOK_WDATA0);
-	writel(wdata0, i2c->regs + REG_TOK_WDATA1);
+	writel(wdata1, i2c->regs + REG_TOK_WDATA1);
 
 	dev_dbg(i2c->dev, "%s: data %08x %08x len %d\n", __func__,
 		wdata0, wdata1, len);
diff --git a/drivers/i2c/busses/i2c-msm-v2.c b/drivers/i2c/busses/i2c-msm-v2.c
index 3de0e25..4daed7f 100644
--- a/drivers/i2c/busses/i2c-msm-v2.c
+++ b/drivers/i2c/busses/i2c-msm-v2.c
@@ -2164,7 +2164,6 @@
 
 			++cur_buf->msg_idx;
 			++cur_msg;
-			}
 		} else {
 			cur_buf->is_init = true;
 		}
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index c21ca7b..8f1c5f2 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -94,6 +94,12 @@
 #define SB800_PIIX4_PORT_IDX_ALT	0x2e
 #define SB800_PIIX4_PORT_IDX_SEL	0x2f
 #define SB800_PIIX4_PORT_IDX_MASK	0x06
+#define SB800_PIIX4_PORT_IDX_SHIFT	1
+
+/* On kerncz, SmBus0Sel is at bit 20:19 of PMx00 DecodeEn */
+#define SB800_PIIX4_PORT_IDX_KERNCZ		0x02
+#define SB800_PIIX4_PORT_IDX_MASK_KERNCZ	0x18
+#define SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ	3
 
 /* insmod parameters */
 
@@ -149,6 +155,8 @@
  */
 static DEFINE_MUTEX(piix4_mutex_sb800);
 static u8 piix4_port_sel_sb800;
+static u8 piix4_port_mask_sb800;
+static u8 piix4_port_shift_sb800;
 static const char *piix4_main_port_names_sb800[PIIX4_MAX_ADAPTERS] = {
 	" port 0", " port 2", " port 3", " port 4"
 };
@@ -347,7 +355,19 @@
 
 	/* Find which register is used for port selection */
 	if (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD) {
-		piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT;
+		switch (PIIX4_dev->device) {
+		case PCI_DEVICE_ID_AMD_KERNCZ_SMBUS:
+			piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_KERNCZ;
+			piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK_KERNCZ;
+			piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT_KERNCZ;
+			break;
+		case PCI_DEVICE_ID_AMD_HUDSON2_SMBUS:
+		default:
+			piix4_port_sel_sb800 = SB800_PIIX4_PORT_IDX_ALT;
+			piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
+			piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
+			break;
+		}
 	} else {
 		mutex_lock(&piix4_mutex_sb800);
 		outb_p(SB800_PIIX4_PORT_IDX_SEL, SB800_PIIX4_SMB_IDX);
@@ -355,6 +375,8 @@
 		piix4_port_sel_sb800 = (port_sel & 0x01) ?
 				       SB800_PIIX4_PORT_IDX_ALT :
 				       SB800_PIIX4_PORT_IDX;
+		piix4_port_mask_sb800 = SB800_PIIX4_PORT_IDX_MASK;
+		piix4_port_shift_sb800 = SB800_PIIX4_PORT_IDX_SHIFT;
 		mutex_unlock(&piix4_mutex_sb800);
 	}
 
@@ -616,8 +638,8 @@
 	smba_en_lo = inb_p(SB800_PIIX4_SMB_IDX + 1);
 
 	port = adapdata->port;
-	if ((smba_en_lo & SB800_PIIX4_PORT_IDX_MASK) != port)
-		outb_p((smba_en_lo & ~SB800_PIIX4_PORT_IDX_MASK) | port,
+	if ((smba_en_lo & piix4_port_mask_sb800) != port)
+		outb_p((smba_en_lo & ~piix4_port_mask_sb800) | port,
 		       SB800_PIIX4_SMB_IDX + 1);
 
 	retval = piix4_access(adap, addr, flags, read_write,
@@ -706,7 +728,7 @@
 
 	adapdata->smba = smba;
 	adapdata->sb800_main = sb800_main;
-	adapdata->port = port << 1;
+	adapdata->port = port << piix4_port_shift_sb800;
 
 	/* set up the sysfs linkage to our parent device */
 	adap->dev.parent = &dev->dev;
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index f70dbf2..1bfb98e 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -72,7 +72,10 @@
 #define I2C_NACK		GP_IRQ1
 #define I2C_BUS_PROTO		GP_IRQ3
 #define I2C_ARB_LOST		GP_IRQ4
-#define DM_I2C_RX_ERR		((GP_IRQ1 | GP_IRQ3 | GP_IRQ4) >> 4)
+#define DM_I2C_CB_ERR		((BIT(GP_IRQ1) | BIT(GP_IRQ3) | BIT(GP_IRQ4)) \
+									<< 5)
+
+#define I2C_AUTO_SUSPEND_DELAY	250
 
 enum i2c_se_mode {
 	UNINITIALIZED,
@@ -191,13 +194,6 @@
 
 static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err)
 {
-	u32 m_cmd = readl_relaxed(gi2c->base + SE_GENI_M_CMD0);
-	u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS);
-	u32 geni_s = readl_relaxed(gi2c->base + SE_GENI_STATUS);
-	u32 geni_ios = readl_relaxed(gi2c->base + SE_GENI_IOS);
-	u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN);
-	u32 rx_st, tx_st;
-
 	if (gi2c->cur)
 		GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
 			    "len:%d, slv-addr:0x%x, RD/WR:%d\n", gi2c->cur->len,
@@ -211,23 +207,9 @@
 		GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, "%s\n",
 			     gi2c_log[err].msg);
 	}
-	if (gi2c->se_mode == GSI_ONLY)
-		goto err_out;
-
-	if (dma) {
-		rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT);
-		tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT);
-	} else {
-		rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS);
-		tx_st = readl_relaxed(gi2c->base + SE_GENI_TX_FIFO_STATUS);
-	}
-	GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
-		     "DMA:%d tx_stat:0x%x, rx_stat:0x%x, irq-stat:0x%x\n",
-		     dma, tx_st, rx_st, m_stat);
-err_out:
-	GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
-			     "m_cmd:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
-			     m_cmd, geni_s, geni_ios);
+	GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s: se-mode:%d\n", __func__,
+							gi2c->se_mode);
+	geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl);
 err_ret:
 	gi2c->err = gi2c_log[err].err;
 }
@@ -244,7 +226,7 @@
 	struct i2c_msg *cur = gi2c->cur;
 
 	if (!cur || (m_stat & M_CMD_FAILURE_EN) ||
-		    (dm_rx_st & (DM_I2C_RX_ERR)) ||
+		    (dm_rx_st & (DM_I2C_CB_ERR)) ||
 		    (m_stat & M_CMD_ABORT_EN)) {
 
 		if (m_stat & M_GP_IRQ_1_EN)
@@ -370,13 +352,33 @@
 				m_stat, cb_str->cb_event);
 }
 
+static void gi2c_gsi_cb_err(struct msm_gpi_dma_async_tx_cb_param *cb,
+								char *xfer)
+{
+	struct geni_i2c_dev *gi2c = cb->userdata;
+
+	if (cb->status & DM_I2C_CB_ERR) {
+		GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
+			    "%s TCE Unexpected Err, stat:0x%x\n",
+				xfer, cb->status);
+		if (cb->status & (BIT(GP_IRQ1) << 5))
+			geni_i2c_err(gi2c, I2C_NACK);
+		if (cb->status & (BIT(GP_IRQ3) << 5))
+			geni_i2c_err(gi2c, I2C_BUS_PROTO);
+		if (cb->status & (BIT(GP_IRQ4) << 5))
+			geni_i2c_err(gi2c, I2C_ARB_LOST);
+	}
+}
+
 static void gi2c_gsi_tx_cb(void *ptr)
 {
 	struct msm_gpi_dma_async_tx_cb_param *tx_cb = ptr;
 	struct geni_i2c_dev *gi2c = tx_cb->userdata;
 
-	if (!(gi2c->cur->flags & I2C_M_RD))
+	if (!(gi2c->cur->flags & I2C_M_RD)) {
+		gi2c_gsi_cb_err(tx_cb, "TX");
 		complete(&gi2c->xfer);
+	}
 }
 
 static void gi2c_gsi_rx_cb(void *ptr)
@@ -385,17 +387,7 @@
 	struct geni_i2c_dev *gi2c = rx_cb->userdata;
 
 	if (gi2c->cur->flags & I2C_M_RD) {
-		if (rx_cb->status & DM_I2C_RX_ERR) {
-			GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev,
-				    "RX TCE Unexpected Err, stat:0x%x\n",
-				    rx_cb->status);
-			if (rx_cb->status & GP_IRQ1)
-				geni_i2c_err(gi2c, I2C_NACK);
-			if (rx_cb->status & GP_IRQ3)
-				geni_i2c_err(gi2c, I2C_BUS_PROTO);
-			if (rx_cb->status & GP_IRQ4)
-				geni_i2c_err(gi2c, I2C_ARB_LOST);
-		}
+		gi2c_gsi_cb_err(rx_cb, "RX");
 		complete(&gi2c->xfer);
 	}
 }
@@ -406,12 +398,22 @@
 	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
 	int i, ret = 0, timeout = 0;
 
+	ret = pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
+				gi2c->i2c_rsc.geni_gpio_active);
+	if (ret) {
+		GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
+			"%s: Error %d pinctrl_select_state active\n",
+			__func__, ret);
+		return ret;
+	}
+
 	if (!gi2c->tx_c) {
 		gi2c->tx_c = dma_request_slave_channel(gi2c->dev, "tx");
 		if (!gi2c->tx_c) {
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 				    "tx dma req slv chan ret :%d\n", ret);
-			return -EIO;
+			ret = -EIO;
+			goto geni_i2c_gsi_xfer_out;
 		}
 		gi2c->tx_ev.init.callback = gi2c_ev_cb;
 		gi2c->tx_ev.init.cb_param = gi2c;
@@ -421,7 +423,7 @@
 		if (ret) {
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 				    "tx dma slave config ret :%d\n", ret);
-			return ret;
+			goto geni_i2c_gsi_xfer_out;
 		}
 	}
 	if (!gi2c->rx_c) {
@@ -429,7 +431,8 @@
 		if (!gi2c->rx_c) {
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 				    "rx dma req slv chan ret :%d\n", ret);
-			return -EIO;
+			ret = -EIO;
+			goto geni_i2c_gsi_xfer_out;
 		}
 		gi2c->rx_ev.init.cb_param = gi2c;
 		gi2c->rx_ev.init.callback = gi2c_ev_cb;
@@ -439,7 +442,7 @@
 		if (ret) {
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 				    "rx dma slave config ret :%d\n", ret);
-			return ret;
+			goto geni_i2c_gsi_xfer_out;
 		}
 	}
 
@@ -469,8 +472,8 @@
 		int stretch = (i < (num - 1));
 		dma_cookie_t tx_cookie, rx_cookie;
 		struct msm_gpi_tre *go_t = &gi2c->go_t;
-		struct device *rx_dev = gi2c->dev;
-		struct device *tx_dev = gi2c->dev;
+		struct device *rx_dev = gi2c->wrapper_dev;
+		struct device *tx_dev = gi2c->wrapper_dev;
 
 		gi2c->cur = &msgs[i];
 		if (!gi2c->cfg_sent) {
@@ -501,9 +504,8 @@
 
 		if (msgs[i].flags & I2C_M_RD) {
 			sg_init_table(&gi2c->rx_sg, 1);
-			gi2c->rx_ph = dma_map_single(rx_dev, msgs[i].buf,
-						     msgs[i].len,
-						     DMA_FROM_DEVICE);
+			geni_se_iommu_map_buf(rx_dev, &gi2c->rx_ph, msgs[i].buf,
+						msgs[i].len, DMA_FROM_DEVICE);
 			gi2c->rx_t.dword[0] =
 				MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->rx_ph);
 			gi2c->rx_t.dword[1] =
@@ -524,7 +526,7 @@
 				GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 					    "prep_slave_sg for rx failed\n");
 				gi2c->err = -ENOMEM;
-				return gi2c->err;
+				goto geni_i2c_gsi_xfer_out;
 			}
 			gi2c->rx_desc->callback = gi2c_gsi_rx_cb;
 			gi2c->rx_desc->callback_param = &gi2c->rx_cb;
@@ -533,9 +535,8 @@
 			rx_cookie = dmaengine_submit(gi2c->rx_desc);
 			dma_async_issue_pending(gi2c->rx_c);
 		} else {
-			gi2c->tx_ph = dma_map_single(tx_dev, msgs[i].buf,
-						     msgs[i].len,
-						     DMA_TO_DEVICE);
+			geni_se_iommu_map_buf(tx_dev, &gi2c->tx_ph, msgs[i].buf,
+						msgs[i].len, DMA_TO_DEVICE);
 			gi2c->tx_t.dword[0] =
 				MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->tx_ph);
 			gi2c->tx_t.dword[1] =
@@ -557,7 +558,7 @@
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
 				    "prep_slave_sg for tx failed\n");
 			gi2c->err = -ENOMEM;
-			return gi2c->err;
+			goto geni_i2c_gsi_xfer_out;
 		}
 		gi2c->tx_desc->callback = gi2c_gsi_tx_cb;
 		gi2c->tx_desc->callback_param = &gi2c->tx_cb;
@@ -568,11 +569,11 @@
 
 		timeout = wait_for_completion_timeout(&gi2c->xfer, HZ);
 		if (msgs[i].flags & I2C_M_RD)
-			dma_unmap_single(rx_dev, gi2c->rx_ph, msgs[i].len,
-					 DMA_FROM_DEVICE);
+			geni_se_iommu_unmap_buf(rx_dev, &gi2c->rx_ph,
+				msgs[i].len, DMA_FROM_DEVICE);
 		else
-			dma_unmap_single(tx_dev, gi2c->tx_ph, msgs[i].len,
-					 DMA_TO_DEVICE);
+			geni_se_iommu_unmap_buf(tx_dev, &gi2c->tx_ph,
+				msgs[i].len, DMA_TO_DEVICE);
 
 		if (!timeout) {
 			GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
@@ -582,10 +583,15 @@
 		if (gi2c->err) {
 			dmaengine_terminate_all(gi2c->tx_c);
 			gi2c->cfg_sent = 0;
-			return gi2c->err;
+			goto geni_i2c_gsi_xfer_out;
 		}
 	}
-	return gi2c->err;
+geni_i2c_gsi_xfer_out:
+	if (!ret && gi2c->err)
+		ret = gi2c->err;
+	pinctrl_select_state(gi2c->i2c_rsc.geni_pinctrl,
+				gi2c->i2c_rsc.geni_gpio_sleep);
+	return ret;
 }
 
 static int geni_i2c_xfer(struct i2c_adapter *adap,
@@ -709,7 +715,9 @@
 geni_i2c_txn_ret:
 	if (ret == 0)
 		ret = num;
-	pm_runtime_put_sync(gi2c->dev);
+
+	pm_runtime_mark_last_busy(gi2c->dev);
+	pm_runtime_put_autosuspend(gi2c->dev);
 	gi2c->cur = NULL;
 	gi2c->err = 0;
 	dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret);
@@ -853,6 +861,8 @@
 	strlcpy(gi2c->adap.name, "Geni-I2C", sizeof(gi2c->adap.name));
 
 	pm_runtime_set_suspended(gi2c->dev);
+	pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY);
+	pm_runtime_use_autosuspend(gi2c->dev);
 	pm_runtime_enable(gi2c->dev);
 	i2c_add_adapter(&gi2c->adap);
 
@@ -881,10 +891,13 @@
 {
 	struct geni_i2c_dev *gi2c = dev_get_drvdata(dev);
 
-	if (gi2c->se_mode == FIFO_SE_DMA)
+	if (gi2c->se_mode == FIFO_SE_DMA) {
 		disable_irq(gi2c->irq);
-
-	se_geni_resources_off(&gi2c->i2c_rsc);
+		se_geni_resources_off(&gi2c->i2c_rsc);
+	} else {
+		/* GPIO is set to sleep state already. So just clocks off */
+		se_geni_clks_off(&gi2c->i2c_rsc);
+	}
 	return 0;
 }
 
@@ -899,14 +912,27 @@
 		snprintf(ipc_name, I2C_NAME_SIZE, "i2c-%d", gi2c->adap.nr);
 		gi2c->ipcl = ipc_log_context_create(2, ipc_name, 0);
 	}
-	ret = se_geni_resources_on(&gi2c->i2c_rsc);
+
+	if (gi2c->se_mode != GSI_ONLY)
+		ret = se_geni_resources_on(&gi2c->i2c_rsc);
+	else
+		ret = se_geni_clks_on(&gi2c->i2c_rsc);
+
 	if (ret)
 		return ret;
 
 	if (gi2c->se_mode == UNINITIALIZED) {
-		u32 se_mode = readl_relaxed(gi2c->base +
-					GENI_IF_FIFO_DISABLE_RO);
+		int proto = get_se_proto(gi2c->base);
+		u32 se_mode;
 
+		if (unlikely(proto != I2C)) {
+			dev_err(gi2c->dev, "Invalid proto %d\n", proto);
+			se_geni_resources_off(&gi2c->i2c_rsc);
+			return -ENXIO;
+		}
+
+		se_mode = readl_relaxed(gi2c->base +
+					GENI_IF_FIFO_DISABLE_RO);
 		if (se_mode) {
 			gi2c->se_mode = GSI_ONLY;
 			geni_se_select_mode(gi2c->base, GSI_DMA);
@@ -933,8 +959,23 @@
 
 static int geni_i2c_suspend_noirq(struct device *device)
 {
-	if (!pm_runtime_status_suspended(device))
+	struct geni_i2c_dev *gi2c = dev_get_drvdata(device);
+	int ret;
+
+	/* Make sure no transactions are pending */
+	ret = i2c_trylock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
+	if (!ret) {
+		GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev,
+				"late I2C transaction request\n");
 		return -EBUSY;
+	}
+	if (!pm_runtime_status_suspended(device)) {
+		geni_i2c_runtime_suspend(device);
+		pm_runtime_disable(device);
+		pm_runtime_set_suspended(device);
+		pm_runtime_enable(device);
+	}
+	i2c_unlock_bus(&gi2c->adap, I2C_LOCK_SEGMENT);
 	return 0;
 }
 #else
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index 6263ea8..8f11d34 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -80,6 +80,7 @@
 #define ICIER_TEIE	0x40
 #define ICIER_RIE	0x20
 #define ICIER_NAKIE	0x10
+#define ICIER_SPIE	0x08
 
 #define ICSR2_NACKF	0x10
 
@@ -216,11 +217,10 @@
 		return IRQ_NONE;
 	}
 
-	if (riic->is_last || riic->err)
+	if (riic->is_last || riic->err) {
+		riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER);
 		writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
-
-	writeb(0, riic->base + RIIC_ICIER);
-	complete(&riic->msg_done);
+	}
 
 	return IRQ_HANDLED;
 }
@@ -240,13 +240,13 @@
 
 	if (riic->bytes_left == 1) {
 		/* STOP must come before we set ACKBT! */
-		if (riic->is_last)
+		if (riic->is_last) {
+			riic_clear_set_bit(riic, 0, ICIER_SPIE, RIIC_ICIER);
 			writeb(ICCR2_SP, riic->base + RIIC_ICCR2);
+		}
 
 		riic_clear_set_bit(riic, 0, ICMR3_ACKBT, RIIC_ICMR3);
 
-		writeb(0, riic->base + RIIC_ICIER);
-		complete(&riic->msg_done);
 	} else {
 		riic_clear_set_bit(riic, ICMR3_ACKBT, 0, RIIC_ICMR3);
 	}
@@ -259,6 +259,21 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t riic_stop_isr(int irq, void *data)
+{
+	struct riic_dev *riic = data;
+
+	/* read back registers to confirm writes have fully propagated */
+	writeb(0, riic->base + RIIC_ICSR2);
+	readb(riic->base + RIIC_ICSR2);
+	writeb(0, riic->base + RIIC_ICIER);
+	readb(riic->base + RIIC_ICIER);
+
+	complete(&riic->msg_done);
+
+	return IRQ_HANDLED;
+}
+
 static u32 riic_func(struct i2c_adapter *adap)
 {
 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
@@ -326,6 +341,7 @@
 	{ .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
 	{ .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
 	{ .res_num = 2, .isr = riic_tdre_isr, .name = "riic-tdre" },
+	{ .res_num = 3, .isr = riic_stop_isr, .name = "riic-stop" },
 	{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
 };
 
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 59b380d..c388882 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -193,7 +193,6 @@
 	struct regmap *regmap;
 	int irq;
 	struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
-	atomic_t active_intr;
 	struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
 	struct mutex mutex;
 	u8 fifo_mode, watermark;
@@ -493,11 +492,6 @@
 		goto out_fix_power_state;
 	}
 
-	if (state)
-		atomic_inc(&data->active_intr);
-	else
-		atomic_dec(&data->active_intr);
-
 	return 0;
 
 out_fix_power_state:
@@ -1709,8 +1703,7 @@
 	struct bmc150_accel_data *data = iio_priv(indio_dev);
 
 	mutex_lock(&data->mutex);
-	if (atomic_read(&data->active_intr))
-		bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+	bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
 	bmc150_accel_fifo_set_mode(data);
 	mutex_unlock(&data->mutex);
 
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index e6706a0..47c3d7f 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -257,7 +257,7 @@
 	unsigned int vref_mv)
 {
 	struct ad7793_state *st = iio_priv(indio_dev);
-	int i, ret = -1;
+	int i, ret;
 	unsigned long long scale_uv;
 	u32 id;
 
@@ -266,7 +266,7 @@
 		return ret;
 
 	/* reset the serial interface */
-	ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret));
+	ret = ad_sd_reset(&st->sd, 32);
 	if (ret < 0)
 		goto out;
 	usleep_range(500, 2000); /* Wait for at least 500us */
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index d10bd0c..22c4c17 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -177,6 +177,34 @@
 }
 EXPORT_SYMBOL_GPL(ad_sd_read_reg);
 
+/**
+ * ad_sd_reset() - Reset the serial interface
+ *
+ * @sigma_delta: The sigma delta device
+ * @reset_length: Number of SCLKs with DIN = 1
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
+	unsigned int reset_length)
+{
+	uint8_t *buf;
+	unsigned int size;
+	int ret;
+
+	size = DIV_ROUND_UP(reset_length, 8);
+	buf = kcalloc(size, sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	memset(buf, 0xff, size);
+	ret = spi_write(sigma_delta->spi, buf, size);
+	kfree(buf);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_reset);
+
 static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
 	unsigned int mode, unsigned int channel)
 {
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 7fd2494..64799ad 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -28,8 +28,6 @@
 #include <linux/iio/driver.h>
 
 #define AXP288_ADC_EN_MASK		0xF1
-#define AXP288_ADC_TS_PIN_GPADC		0xF2
-#define AXP288_ADC_TS_PIN_ON		0xF3
 
 enum axp288_adc_id {
 	AXP288_ADC_TS,
@@ -123,16 +121,6 @@
 	return IIO_VAL_INT;
 }
 
-static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
-				unsigned long address)
-{
-	/* channels other than GPADC do not need to switch TS pin */
-	if (address != AXP288_GP_ADC_H)
-		return 0;
-
-	return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
-}
-
 static int axp288_adc_read_raw(struct iio_dev *indio_dev,
 			struct iio_chan_spec const *chan,
 			int *val, int *val2, long mask)
@@ -143,16 +131,7 @@
 	mutex_lock(&indio_dev->mlock);
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
-					chan->address)) {
-			dev_err(&indio_dev->dev, "GPADC mode\n");
-			ret = -EINVAL;
-			break;
-		}
 		ret = axp288_adc_read_channel(val, chan->address, info->regmap);
-		if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
-						chan->address))
-			dev_err(&indio_dev->dev, "TS pin restore\n");
 		break;
 	default:
 		ret = -EINVAL;
@@ -162,15 +141,6 @@
 	return ret;
 }
 
-static int axp288_adc_set_state(struct regmap *regmap)
-{
-	/* ADC should be always enabled for internal FG to function */
-	if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
-		return -EIO;
-
-	return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
-}
-
 static const struct iio_info axp288_adc_iio_info = {
 	.read_raw = &axp288_adc_read_raw,
 	.driver_module = THIS_MODULE,
@@ -199,7 +169,7 @@
 	 * Set ADC to enabled state at all time, including system suspend.
 	 * otherwise internal fuel gauge functionality may be affected.
 	 */
-	ret = axp288_adc_set_state(axp20x->regmap);
+	ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to enable ADC device\n");
 		return ret;
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 72b32c1..ea264fa 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -401,6 +401,7 @@
 	{ .compatible = "fsl,imx25-gcq", },
 	{ /* Sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, mx25_gcq_ids);
 
 static struct platform_driver mx25_gcq_driver = {
 	.driver		= {
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 634717a..071dd23 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -17,6 +17,8 @@
  * MCP3204
  * MCP3208
  * ------------
+ * 13 bit converter
+ * MCP3301
  *
  * Datasheet can be found here:
  * http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf  mcp3001
@@ -96,7 +98,7 @@
 }
 
 static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
-				  bool differential, int device_index)
+				  bool differential, int device_index, int *val)
 {
 	int ret;
 
@@ -117,19 +119,25 @@
 
 	switch (device_index) {
 	case mcp3001:
-		return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+		*val = (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+		return 0;
 	case mcp3002:
 	case mcp3004:
 	case mcp3008:
-		return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+		*val = (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+		return 0;
 	case mcp3201:
-		return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+		*val = (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+		return 0;
 	case mcp3202:
 	case mcp3204:
 	case mcp3208:
-		return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+		*val = (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+		return 0;
 	case mcp3301:
-		return sign_extend32((adc->rx_buf[0] & 0x1f) << 8 | adc->rx_buf[1], 12);
+		*val = sign_extend32((adc->rx_buf[0] & 0x1f) << 8
+				    | adc->rx_buf[1], 12);
+		return 0;
 	default:
 		return -EINVAL;
 	}
@@ -150,12 +158,10 @@
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
 		ret = mcp320x_adc_conversion(adc, channel->address,
-			channel->differential, device_index);
-
+			channel->differential, device_index, val);
 		if (ret < 0)
 			goto out;
 
-		*val = ret;
 		ret = IIO_VAL_INT;
 		break;
 
@@ -312,6 +318,7 @@
 	indio_dev->name = spi_get_device_id(spi)->name;
 	indio_dev->modes = INDIO_DIRECT_MODE;
 	indio_dev->info = &mcp320x_info;
+	spi_set_drvdata(spi, indio_dev);
 
 	chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
 	indio_dev->channels = chip_info->channels;
diff --git a/drivers/iio/adc/qcom-rradc.c b/drivers/iio/adc/qcom-rradc.c
index b055ff6..02dfbf8 100644
--- a/drivers/iio/adc/qcom-rradc.c
+++ b/drivers/iio/adc/qcom-rradc.c
@@ -22,6 +22,7 @@
 #include <linux/regmap.h>
 #include <linux/delay.h>
 #include <linux/qpnp/qpnp-revid.h>
+#include <linux/power_supply.h>
 
 #define FG_ADC_RR_EN_CTL			0x46
 #define FG_ADC_RR_SKIN_TEMP_LSB			0x50
@@ -180,6 +181,9 @@
 #define FG_ADC_RR_VOLT_INPUT_FACTOR		8
 #define FG_ADC_RR_CURR_INPUT_FACTOR		2000
 #define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL	1886
+#define FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL	9
+#define FG_ADC_RR_CURR_USBIN_660_UV_VAL	579500
+
 #define FG_ADC_SCALE_MILLI_FACTOR		1000
 #define FG_ADC_KELVINMIL_CELSIUSMIL		273150
 
@@ -189,9 +193,11 @@
 #define FG_RR_ADC_STS_CHANNEL_READING_MASK	0x3
 #define FG_RR_ADC_STS_CHANNEL_STS		0x2
 
-#define FG_RR_CONV_CONTINUOUS_TIME_MIN_US	50000
-#define FG_RR_CONV_CONTINUOUS_TIME_MAX_US	51000
+#define FG_RR_CONV_CONTINUOUS_TIME_MIN_MS	50
 #define FG_RR_CONV_MAX_RETRY_CNT		50
+#define FG_RR_TP_REV_VERSION1		21
+#define FG_RR_TP_REV_VERSION2		29
+#define FG_RR_TP_REV_VERSION3		32
 
 /*
  * The channel number is not a physical index in hardware,
@@ -228,6 +234,8 @@
 	struct rradc_chan_prop		*chan_props;
 	struct device_node		*revid_dev_node;
 	struct pmic_revid_data		*pmic_fab_id;
+	int volt;
+	struct power_supply		*usb_trig;
 };
 
 struct rradc_channels {
@@ -353,7 +361,7 @@
 	return 0;
 }
 
-static int rradc_post_process_curr(struct rradc_chip *chip,
+static int rradc_post_process_usbin_curr(struct rradc_chip *chip,
 			struct rradc_chan_prop *prop, u16 adc_code,
 			int *result_ua)
 {
@@ -361,11 +369,33 @@
 
 	if (!prop)
 		return -EINVAL;
-
-	if (prop->channel == RR_ADC_USBIN_I)
-		scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
-	else
-		scale = FG_ADC_RR_CURR_INPUT_FACTOR;
+	if (chip->revid_dev_node) {
+		switch (chip->pmic_fab_id->pmic_subtype) {
+		case PM660_SUBTYPE:
+			if (((chip->pmic_fab_id->tp_rev
+				>= FG_RR_TP_REV_VERSION1)
+			&& (chip->pmic_fab_id->tp_rev
+				<= FG_RR_TP_REV_VERSION2))
+			|| (chip->pmic_fab_id->tp_rev
+				>= FG_RR_TP_REV_VERSION3)) {
+				chip->volt = div64_s64(chip->volt, 1000);
+				chip->volt = chip->volt *
+					FG_ADC_RR_CURR_USBIN_660_FACTOR_MIL;
+				chip->volt = FG_ADC_RR_CURR_USBIN_660_UV_VAL -
+					(chip->volt);
+				chip->volt = div64_s64(1000000000, chip->volt);
+				scale = chip->volt;
+			} else
+				scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+			break;
+		case PMI8998_SUBTYPE:
+			scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
+			break;
+		default:
+			pr_err("No PMIC subtype found\n");
+			return -EINVAL;
+		}
+	}
 
 	/* scale * V/A; 2.5V ADC full scale */
 	ua = ((int64_t)adc_code * scale);
@@ -376,6 +406,24 @@
 	return 0;
 }
 
+static int rradc_post_process_dcin_curr(struct rradc_chip *chip,
+			struct rradc_chan_prop *prop, u16 adc_code,
+			int *result_ua)
+{
+	int64_t ua = 0;
+
+	if (!prop)
+		return -EINVAL;
+
+	/* 0.5 V/A; 2.5V ADC full scale */
+	ua = ((int64_t)adc_code * FG_ADC_RR_CURR_INPUT_FACTOR);
+	ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
+	ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000));
+	*result_ua = ua;
+
+	return 0;
+}
+
 static int rradc_post_process_die_temp(struct rradc_chip *chip,
 			struct rradc_chan_prop *prop, u16 adc_code,
 			int *result_millidegc)
@@ -591,13 +639,13 @@
 			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
 			FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
 			FG_ADC_RR_AUX_THERM_STS)
-	RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
+	RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_usbin_curr,
 			FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB,
 			FG_ADC_RR_USB_IN_I_STS)
 	RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt,
 			FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB,
 			FG_ADC_RR_USB_IN_V_STS)
-	RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr,
+	RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_dcin_curr,
 			FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB,
 			FG_ADC_RR_DC_IN_I_STS)
 	RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt,
@@ -679,6 +727,24 @@
 	return rc;
 }
 
+static bool rradc_is_usb_present(struct rradc_chip *chip)
+{
+	union power_supply_propval pval;
+	int rc;
+	bool usb_present = false;
+
+	if (!chip->usb_trig) {
+		pr_debug("USB property not present\n");
+		return usb_present;
+	}
+
+	rc = power_supply_get_property(chip->usb_trig,
+			POWER_SUPPLY_PROP_PRESENT, &pval);
+	usb_present = (rc < 0) ? 0 : pval.intval;
+
+	return usb_present;
+}
+
 static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
 		struct rradc_chan_prop *prop, u8 *buf, u16 status)
 {
@@ -698,8 +764,18 @@
 			(retry_cnt < FG_RR_CONV_MAX_RETRY_CNT)) {
 		pr_debug("%s is not ready; nothing to read:0x%x\n",
 			rradc_chans[prop->channel].datasheet_name, buf[0]);
-		usleep_range(FG_RR_CONV_CONTINUOUS_TIME_MIN_US,
-				FG_RR_CONV_CONTINUOUS_TIME_MAX_US);
+
+		if (((prop->channel == RR_ADC_CHG_TEMP) ||
+			(prop->channel == RR_ADC_SKIN_TEMP) ||
+			(prop->channel == RR_ADC_USBIN_I) ||
+			(prop->channel == RR_ADC_DIE_TEMP)) &&
+					((!rradc_is_usb_present(chip)))) {
+			pr_debug("USB not present for %d\n", prop->channel);
+			rc = -ENODATA;
+			break;
+		}
+
+		msleep(FG_RR_CONV_CONTINUOUS_TIME_MIN_MS);
 		retry_cnt++;
 		rc = rradc_read(chip, status, buf, 1);
 		if (rc < 0) {
@@ -717,7 +793,7 @@
 static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
 			struct rradc_chan_prop *prop, u8 *buf)
 {
-	int rc = 0;
+	int rc = 0, ret = 0;
 	u16 status = 0;
 
 	rc = rradc_enable_continuous_mode(chip);
@@ -730,23 +806,25 @@
 	rc = rradc_read(chip, status, buf, 1);
 	if (rc < 0) {
 		pr_err("status read failed:%d\n", rc);
-		return rc;
+		ret = rc;
+		goto disable;
 	}
 
 	rc = rradc_check_status_ready_with_retry(chip, prop,
 						buf, status);
 	if (rc < 0) {
 		pr_err("Status read failed:%d\n", rc);
-		return rc;
+		ret = rc;
 	}
 
+disable:
 	rc = rradc_disable_continuous_mode(chip);
 	if (rc < 0) {
 		pr_err("Failed to switch to non continuous mode\n");
-		return rc;
+		ret = rc;
 	}
 
-	return rc;
+	return ret;
 }
 
 static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
@@ -955,6 +1033,21 @@
 
 	switch (mask) {
 	case IIO_CHAN_INFO_PROCESSED:
+		if (((chip->pmic_fab_id->tp_rev
+				>= FG_RR_TP_REV_VERSION1)
+		&& (chip->pmic_fab_id->tp_rev
+				<= FG_RR_TP_REV_VERSION2))
+		|| (chip->pmic_fab_id->tp_rev
+				>= FG_RR_TP_REV_VERSION3)) {
+			if (chan->address == RR_ADC_USBIN_I) {
+				prop = &chip->chan_props[RR_ADC_USBIN_V];
+				rc = rradc_do_conversion(chip, prop, &adc_code);
+				if (rc)
+					break;
+				prop->scale(chip, prop, adc_code, &chip->volt);
+			}
+		}
+
 		prop = &chip->chan_props[chan->address];
 		rc = rradc_do_conversion(chip, prop, &adc_code);
 		if (rc)
@@ -1090,6 +1183,10 @@
 	indio_dev->channels = chip->iio_chans;
 	indio_dev->num_channels = chip->nchannels;
 
+	chip->usb_trig = power_supply_get_by_name("usb");
+	if (!chip->usb_trig)
+		pr_debug("Error obtaining usb power supply\n");
+
 	return devm_iio_device_register(dev, indio_dev);
 }
 
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index cde6f13..472641f 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -80,18 +80,12 @@
 	8, 16, 32, 64, 128, 250, 475, 860
 };
 
-static const struct {
-	int scale;
-	int uscale;
-} ads1015_scale[] = {
-	{3, 0},
-	{2, 0},
-	{1, 0},
-	{0, 500000},
-	{0, 250000},
-	{0, 125000},
-	{0, 125000},
-	{0, 125000},
+/*
+ * Translation from PGA bits to full-scale positive and negative input voltage
+ * range in mV
+ */
+static int ads1015_fullscale_range[] = {
+	6144, 4096, 2048, 1024, 512, 256, 256, 256
 };
 
 #define ADS1015_V_CHAN(_chan, _addr) {				\
@@ -182,6 +176,12 @@
 	struct ads1015_channel_data channel_data[ADS1015_CHANNELS];
 
 	unsigned int *data_rate;
+	/*
+	 * Set to true when the ADC is switched to the continuous-conversion
+	 * mode and exits from a power-down state.  This flag is used to avoid
+	 * getting the stale result from the conversion register.
+	 */
+	bool conv_invalid;
 };
 
 static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
@@ -234,33 +234,43 @@
 		ret = pm_runtime_put_autosuspend(dev);
 	}
 
-	return ret;
+	return ret < 0 ? ret : 0;
 }
 
 static
 int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
 {
 	int ret, pga, dr, conv_time;
-	bool change;
+	unsigned int old, mask, cfg;
 
 	if (chan < 0 || chan >= ADS1015_CHANNELS)
 		return -EINVAL;
 
-	pga = data->channel_data[chan].pga;
-	dr = data->channel_data[chan].data_rate;
-
-	ret = regmap_update_bits_check(data->regmap, ADS1015_CFG_REG,
-				       ADS1015_CFG_MUX_MASK |
-				       ADS1015_CFG_PGA_MASK,
-				       chan << ADS1015_CFG_MUX_SHIFT |
-				       pga << ADS1015_CFG_PGA_SHIFT,
-				       &change);
-	if (ret < 0)
+	ret = regmap_read(data->regmap, ADS1015_CFG_REG, &old);
+	if (ret)
 		return ret;
 
-	if (change) {
-		conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]);
+	pga = data->channel_data[chan].pga;
+	dr = data->channel_data[chan].data_rate;
+	mask = ADS1015_CFG_MUX_MASK | ADS1015_CFG_PGA_MASK |
+		ADS1015_CFG_DR_MASK;
+	cfg = chan << ADS1015_CFG_MUX_SHIFT | pga << ADS1015_CFG_PGA_SHIFT |
+		dr << ADS1015_CFG_DR_SHIFT;
+
+	cfg = (old & ~mask) | (cfg & mask);
+
+	ret = regmap_write(data->regmap, ADS1015_CFG_REG, cfg);
+	if (ret)
+		return ret;
+
+	if (old != cfg || data->conv_invalid) {
+		int dr_old = (old & ADS1015_CFG_DR_MASK) >>
+				ADS1015_CFG_DR_SHIFT;
+
+		conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr_old]);
+		conv_time += DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]);
 		usleep_range(conv_time, conv_time + 1);
+		data->conv_invalid = false;
 	}
 
 	return regmap_read(data->regmap, ADS1015_CONV_REG, val);
@@ -297,17 +307,20 @@
 	return IRQ_HANDLED;
 }
 
-static int ads1015_set_scale(struct ads1015_data *data, int chan,
+static int ads1015_set_scale(struct ads1015_data *data,
+			     struct iio_chan_spec const *chan,
 			     int scale, int uscale)
 {
 	int i, ret, rindex = -1;
+	int fullscale = div_s64((scale * 1000000LL + uscale) <<
+				(chan->scan_type.realbits - 1), 1000000);
 
-	for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
-		if (ads1015_scale[i].scale == scale &&
-		    ads1015_scale[i].uscale == uscale) {
+	for (i = 0; i < ARRAY_SIZE(ads1015_fullscale_range); i++) {
+		if (ads1015_fullscale_range[i] == fullscale) {
 			rindex = i;
 			break;
 		}
+	}
 	if (rindex < 0)
 		return -EINVAL;
 
@@ -317,32 +330,23 @@
 	if (ret < 0)
 		return ret;
 
-	data->channel_data[chan].pga = rindex;
+	data->channel_data[chan->address].pga = rindex;
 
 	return 0;
 }
 
 static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate)
 {
-	int i, ret, rindex = -1;
+	int i;
 
-	for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
+	for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) {
 		if (data->data_rate[i] == rate) {
-			rindex = i;
-			break;
+			data->channel_data[chan].data_rate = i;
+			return 0;
 		}
-	if (rindex < 0)
-		return -EINVAL;
+	}
 
-	ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
-				 ADS1015_CFG_DR_MASK,
-				 rindex << ADS1015_CFG_DR_SHIFT);
-	if (ret < 0)
-		return ret;
-
-	data->channel_data[chan].data_rate = rindex;
-
-	return 0;
+	return -EINVAL;
 }
 
 static int ads1015_read_raw(struct iio_dev *indio_dev,
@@ -384,9 +388,9 @@
 	}
 	case IIO_CHAN_INFO_SCALE:
 		idx = data->channel_data[chan->address].pga;
-		*val = ads1015_scale[idx].scale;
-		*val2 = ads1015_scale[idx].uscale;
-		ret = IIO_VAL_INT_PLUS_MICRO;
+		*val = ads1015_fullscale_range[idx];
+		*val2 = chan->scan_type.realbits - 1;
+		ret = IIO_VAL_FRACTIONAL_LOG2;
 		break;
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		idx = data->channel_data[chan->address].data_rate;
@@ -413,7 +417,7 @@
 	mutex_lock(&data->lock);
 	switch (mask) {
 	case IIO_CHAN_INFO_SCALE:
-		ret = ads1015_set_scale(data, chan->address, val, val2);
+		ret = ads1015_set_scale(data, chan, val, val2);
 		break;
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		ret = ads1015_set_data_rate(data, chan->address, val);
@@ -445,7 +449,10 @@
 	.validate_scan_mask = &iio_validate_scan_mask_onehot,
 };
 
-static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
+static IIO_CONST_ATTR_NAMED(ads1015_scale_available, scale_available,
+	"3 2 1 0.5 0.25 0.125");
+static IIO_CONST_ATTR_NAMED(ads1115_scale_available, scale_available,
+	"0.1875 0.125 0.0625 0.03125 0.015625 0.007813");
 
 static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available,
 	sampling_frequency_available, "128 250 490 920 1600 2400 3300");
@@ -453,7 +460,7 @@
 	sampling_frequency_available, "8 16 32 64 128 250 475 860");
 
 static struct attribute *ads1015_attributes[] = {
-	&iio_const_attr_scale_available.dev_attr.attr,
+	&iio_const_attr_ads1015_scale_available.dev_attr.attr,
 	&iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr,
 	NULL,
 };
@@ -463,7 +470,7 @@
 };
 
 static struct attribute *ads1115_attributes[] = {
-	&iio_const_attr_scale_available.dev_attr.attr,
+	&iio_const_attr_ads1115_scale_available.dev_attr.attr,
 	&iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr,
 	NULL,
 };
@@ -624,6 +631,15 @@
 		dev_err(&client->dev, "iio triggered buffer setup failed\n");
 		return ret;
 	}
+
+	ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
+				ADS1015_CFG_MOD_MASK,
+				ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
+	if (ret)
+		return ret;
+
+	data->conv_invalid = true;
+
 	ret = pm_runtime_set_active(&client->dev);
 	if (ret)
 		goto err_buffer_cleanup;
@@ -679,10 +695,15 @@
 {
 	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
 	struct ads1015_data *data = iio_priv(indio_dev);
+	int ret;
 
-	return regmap_update_bits(data->regmap, ADS1015_CFG_REG,
+	ret = regmap_update_bits(data->regmap, ADS1015_CFG_REG,
 				  ADS1015_CFG_MOD_MASK,
 				  ADS1015_CONTINUOUS << ADS1015_CFG_MOD_SHIFT);
+	if (!ret)
+		data->conv_invalid = true;
+
+	return ret;
 }
 #endif
 
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 0c74869..7ffc5db 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -866,8 +866,10 @@
 
 	/* Enable 3v1 bias regulator for MADC[3:6] */
 	madc->usb3v1 = devm_regulator_get(madc->dev, "vusb3v1");
-	if (IS_ERR(madc->usb3v1))
-		return -ENODEV;
+	if (IS_ERR(madc->usb3v1)) {
+		ret = -ENODEV;
+		goto err_i2c;
+	}
 
 	ret = regulator_enable(madc->usb3v1);
 	if (ret)
@@ -876,11 +878,13 @@
 	ret = iio_device_register(iio_dev);
 	if (ret) {
 		dev_err(&pdev->dev, "could not register iio device\n");
-		goto err_i2c;
+		goto err_usb3v1;
 	}
 
 	return 0;
 
+err_usb3v1:
+	regulator_disable(madc->usb3v1);
 err_i2c:
 	twl4030_madc_set_current_generator(madc, 0, 0);
 err_current_generator:
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 228a003..d1bde6d 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -77,7 +77,7 @@
 #define VF610_ADC_ADSTS_MASK		0x300
 #define VF610_ADC_ADLPC_EN		0x80
 #define VF610_ADC_ADHSC_EN		0x400
-#define VF610_ADC_REFSEL_VALT		0x100
+#define VF610_ADC_REFSEL_VALT		0x800
 #define VF610_ADC_REFSEL_VBG		0x1000
 #define VF610_ADC_ADTRG_HARD		0x2000
 #define VF610_ADC_AVGS_8		0x4000
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 0a6beb3..56cf590 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1208,7 +1208,7 @@
 
 	ret = xadc->ops->setup(pdev, indio_dev, irq);
 	if (ret)
-		goto err_free_samplerate_trigger;
+		goto err_clk_disable_unprepare;
 
 	ret = request_irq(irq, xadc->ops->interrupt_handler, 0,
 			dev_name(&pdev->dev), indio_dev);
@@ -1268,6 +1268,8 @@
 
 err_free_irq:
 	free_irq(irq, indio_dev);
+err_clk_disable_unprepare:
+	clk_disable_unprepare(xadc->clk);
 err_free_samplerate_trigger:
 	if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
 		iio_trigger_free(xadc->samplerate_trigger);
@@ -1277,8 +1279,6 @@
 err_triggered_buffer_cleanup:
 	if (xadc->ops->flags & XADC_FLAGS_BUFFERED)
 		iio_triggered_buffer_cleanup(indio_dev);
-err_clk_disable_unprepare:
-	clk_disable_unprepare(xadc->clk);
 err_device_free:
 	kfree(indio_dev->channels);
 
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 6082934..b60e5d8 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -36,8 +36,6 @@
 	s32 poll_value = 0;
 
 	if (state) {
-		if (!atomic_read(&st->user_requested_state))
-			return 0;
 		if (sensor_hub_device_open(st->hsdev))
 			return -EIO;
 
@@ -86,6 +84,9 @@
 				       &report_val);
 	}
 
+	pr_debug("HID_SENSOR %s set power_state %d report_state %d\n",
+		 st->pdev->name, state_val, report_val);
+
 	sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
 			       st->power_state.index,
 			       sizeof(state_val), &state_val);
@@ -107,6 +108,7 @@
 		ret = pm_runtime_get_sync(&st->pdev->dev);
 	else {
 		pm_runtime_mark_last_busy(&st->pdev->dev);
+		pm_runtime_use_autosuspend(&st->pdev->dev);
 		ret = pm_runtime_put_autosuspend(&st->pdev->dev);
 	}
 	if (ret < 0) {
@@ -201,8 +203,6 @@
 	/* Default to 3 seconds, but can be changed from sysfs */
 	pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
 					 3000);
-	pm_runtime_use_autosuspend(&attrb->pdev->dev);
-
 	return ret;
 error_unreg_trigger:
 	iio_trigger_unregister(trig);
diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c
index ed63ffd..7ec2a0b 100644
--- a/drivers/iio/dummy/iio_simple_dummy_events.c
+++ b/drivers/iio/dummy/iio_simple_dummy_events.c
@@ -72,6 +72,7 @@
 				st->event_en = state;
 			else
 				return -EINVAL;
+			break;
 		default:
 			return -EINVAL;
 		}
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index 8cf84d3..1289842 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -696,7 +696,7 @@
 		.gyro_max_val = IIO_RAD_TO_DEGREE(22500),
 		.gyro_max_scale = 450,
 		.accel_max_val = IIO_M_S_2_TO_G(12500),
-		.accel_max_scale = 5,
+		.accel_max_scale = 10,
 	},
 	[ADIS16485] = {
 		.channels = adis16485_channels,
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index fc340ed..c5bc731 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -306,8 +306,10 @@
 	ret = indio_dev->info->debugfs_reg_access(indio_dev,
 						  indio_dev->cached_reg_addr,
 						  0, &val);
-	if (ret)
+	if (ret) {
 		dev_err(indio_dev->dev.parent, "%s: read failed\n", __func__);
+		return ret;
+	}
 
 	len = snprintf(buf, sizeof(buf), "0x%X\n", val);
 
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 04598ae..f0d3f74 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -626,7 +626,7 @@
 	struct tsl2563_chip *chip = iio_priv(dev_info);
 
 	iio_push_event(dev_info,
-		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+		       IIO_UNMOD_EVENT_CODE(IIO_INTENSITY,
 					    0,
 					    IIO_EV_TYPE_THRESH,
 					    IIO_EV_DIR_EITHER),
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
index f2b3bd7..b4f643f 100644
--- a/drivers/iio/magnetometer/mag3110.c
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -222,29 +222,39 @@
 			     int val, int val2, long mask)
 {
 	struct mag3110_data *data = iio_priv(indio_dev);
-	int rate;
+	int rate, ret;
 
-	if (iio_buffer_enabled(indio_dev))
-		return -EBUSY;
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret)
+		return ret;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_SAMP_FREQ:
 		rate = mag3110_get_samp_freq_index(data, val, val2);
-		if (rate < 0)
-			return -EINVAL;
+		if (rate < 0) {
+			ret = -EINVAL;
+			break;
+		}
 
 		data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK;
 		data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT;
-		return i2c_smbus_write_byte_data(data->client,
+		ret = i2c_smbus_write_byte_data(data->client,
 			MAG3110_CTRL_REG1, data->ctrl_reg1);
+		break;
 	case IIO_CHAN_INFO_CALIBBIAS:
-		if (val < -10000 || val > 10000)
-			return -EINVAL;
-		return i2c_smbus_write_word_swapped(data->client,
+		if (val < -10000 || val > 10000) {
+			ret = -EINVAL;
+			break;
+		}
+		ret = i2c_smbus_write_word_swapped(data->client,
 			MAG3110_OFF_X + 2 * chan->scan_index, val << 1);
+		break;
 	default:
-		return -EINVAL;
+		ret = -EINVAL;
+		break;
 	}
+	iio_device_release_direct_mode(indio_dev);
+	return ret;
 }
 
 static irqreturn_t mag3110_trigger_handler(int irq, void *p)
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index f762eb8..19aa957 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -558,7 +558,7 @@
 	u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) |
 		  BMP280_OSRS_PRESS_X(data->oversampling_press + 1);
 
-	ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_MEAS,
+	ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS,
 				 BMP280_OSRS_TEMP_MASK |
 				 BMP280_OSRS_PRESS_MASK |
 				 BMP280_MODE_MASK,
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index a74ed1f..8cc7156 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -308,6 +308,7 @@
 {
 	struct ms5611_state *st = iio_priv(indio_dev);
 	const struct ms5611_osr *osr = NULL;
+	int ret;
 
 	if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
 		return -EINVAL;
@@ -321,12 +322,11 @@
 	if (!osr)
 		return -EINVAL;
 
-	mutex_lock(&st->lock);
+	ret = iio_device_claim_direct_mode(indio_dev);
+	if (ret)
+		return ret;
 
-	if (iio_buffer_enabled(indio_dev)) {
-		mutex_unlock(&st->lock);
-		return -EBUSY;
-	}
+	mutex_lock(&st->lock);
 
 	if (chan->type == IIO_TEMP)
 		st->temp_osr = osr;
@@ -334,6 +334,8 @@
 		st->pressure_osr = osr;
 
 	mutex_unlock(&st->lock);
+	iio_device_release_direct_mode(indio_dev);
+
 	return 0;
 }
 
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index 1f06282..9ea147f 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/drivers/iio/proximity/sx9500.c
@@ -387,14 +387,18 @@
 			   int *val, int *val2, long mask)
 {
 	struct sx9500_data *data = iio_priv(indio_dev);
+	int ret;
 
 	switch (chan->type) {
 	case IIO_PROXIMITY:
 		switch (mask) {
 		case IIO_CHAN_INFO_RAW:
-			if (iio_buffer_enabled(indio_dev))
-				return -EBUSY;
-			return sx9500_read_proximity(data, chan, val);
+			ret = iio_device_claim_direct_mode(indio_dev);
+			if (ret)
+				return ret;
+			ret = sx9500_read_proximity(data, chan, val);
+			iio_device_release_direct_mode(indio_dev);
+			return ret;
 		case IIO_CHAN_INFO_SAMP_FREQ:
 			return sx9500_read_samp_freq(data, val, val2);
 		default:
diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c
index 572bc6f..e18f12b 100644
--- a/drivers/iio/trigger/iio-trig-interrupt.c
+++ b/drivers/iio/trigger/iio-trig-interrupt.c
@@ -58,7 +58,7 @@
 	trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
 	if (!trig_info) {
 		ret = -ENOMEM;
-		goto error_put_trigger;
+		goto error_free_trigger;
 	}
 	iio_trigger_set_drvdata(trig, trig_info);
 	trig_info->irq = irq;
@@ -83,8 +83,8 @@
 	free_irq(irq, trig);
 error_free_trig_info:
 	kfree(trig_info);
-error_put_trigger:
-	iio_trigger_put(trig);
+error_free_trigger:
+	iio_trigger_free(trig);
 error_ret:
 	return ret;
 }
@@ -99,7 +99,7 @@
 	iio_trigger_unregister(trig);
 	free_irq(trig_info->irq, trig);
 	kfree(trig_info);
-	iio_trigger_put(trig);
+	iio_trigger_free(trig);
 
 	return 0;
 }
diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c
index 3dfab2b..202e8b8 100644
--- a/drivers/iio/trigger/iio-trig-sysfs.c
+++ b/drivers/iio/trigger/iio-trig-sysfs.c
@@ -174,7 +174,7 @@
 	return 0;
 
 out2:
-	iio_trigger_put(t->trig);
+	iio_trigger_free(t->trig);
 free_t:
 	kfree(t);
 out1:
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 63e82f8..fb4ce03 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -446,15 +446,10 @@
 
 	ret = ipv6_stub->ipv6_dst_lookup(addr->net, NULL, &dst, &fl6);
 	if (ret < 0)
-		goto put;
+		return ret;
 
 	rt = (struct rt6_info *)dst;
-	if (ipv6_addr_any(&fl6.saddr)) {
-		ret = ipv6_dev_get_saddr(addr->net, ip6_dst_idev(dst)->dev,
-					 &fl6.daddr, 0, &fl6.saddr);
-		if (ret)
-			goto put;
-
+	if (ipv6_addr_any(&src_in->sin6_addr)) {
 		src_in->sin6_family = AF_INET6;
 		src_in->sin6_addr = fl6.saddr;
 	}
@@ -471,9 +466,6 @@
 
 	*pdst = dst;
 	return 0;
-put:
-	dst_release(dst);
-	return ret;
 }
 #else
 static int addr6_resolve(struct sockaddr_in6 *src_in,
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index f1510cc..6512a55 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1804,20 +1804,21 @@
 	skb_trim(skb, dlen);
 	mutex_lock(&ep->com.mutex);
 
-	/* update RX credits */
-	update_rx_credits(ep, dlen);
-
 	switch (ep->com.state) {
 	case MPA_REQ_SENT:
+		update_rx_credits(ep, dlen);
 		ep->rcv_seq += dlen;
 		disconnect = process_mpa_reply(ep, skb);
 		break;
 	case MPA_REQ_WAIT:
+		update_rx_credits(ep, dlen);
 		ep->rcv_seq += dlen;
 		disconnect = process_mpa_request(ep, skb);
 		break;
 	case FPDU_MODE: {
 		struct c4iw_qp_attributes attrs;
+
+		update_rx_credits(ep, dlen);
 		BUG_ON(!ep->com.qp);
 		if (status)
 			pr_err("%s Unexpected streaming data." \
@@ -2576,9 +2577,9 @@
 	c4iw_put_ep(&child_ep->com);
 reject:
 	reject_cr(dev, hwtid, skb);
+out:
 	if (parent_ep)
 		c4iw_put_ep(&parent_ep->com);
-out:
 	return 0;
 }
 
@@ -3440,7 +3441,7 @@
 		cm_id->provider_data = ep;
 		goto out;
 	}
-
+	remove_handle(ep->com.dev, &ep->com.dev->stid_idr, ep->stid);
 	cxgb4_free_stid(ep->com.dev->rdev.lldi.tids, ep->stid,
 			ep->com.local_addr.ss_family);
 fail2:
diff --git a/drivers/infiniband/hw/hfi1/init.c b/drivers/infiniband/hw/hfi1/init.c
index 34cfd34..a3dd27b 100644
--- a/drivers/infiniband/hw/hfi1/init.c
+++ b/drivers/infiniband/hw/hfi1/init.c
@@ -297,14 +297,15 @@
 		 * The resulting value will be rounded down to the closest
 		 * multiple of dd->rcv_entries.group_size.
 		 */
-		rcd->egrbufs.buffers = kcalloc(rcd->egrbufs.count,
-					       sizeof(*rcd->egrbufs.buffers),
-					       GFP_KERNEL);
+		rcd->egrbufs.buffers = kzalloc_node(
+			rcd->egrbufs.count * sizeof(*rcd->egrbufs.buffers),
+			GFP_KERNEL, numa);
 		if (!rcd->egrbufs.buffers)
 			goto bail;
-		rcd->egrbufs.rcvtids = kcalloc(rcd->egrbufs.count,
-					       sizeof(*rcd->egrbufs.rcvtids),
-					       GFP_KERNEL);
+		rcd->egrbufs.rcvtids = kzalloc_node(
+				rcd->egrbufs.count *
+				sizeof(*rcd->egrbufs.rcvtids),
+				GFP_KERNEL, numa);
 		if (!rcd->egrbufs.rcvtids)
 			goto bail;
 		rcd->egrbufs.size = eager_buffer_size;
@@ -322,8 +323,8 @@
 		rcd->egrbufs.rcvtid_size = HFI1_MAX_EAGER_BUFFER_SIZE;
 
 		if (ctxt < dd->first_user_ctxt) { /* N/A for PSM contexts */
-			rcd->opstats = kzalloc(sizeof(*rcd->opstats),
-				GFP_KERNEL);
+			rcd->opstats = kzalloc_node(sizeof(*rcd->opstats),
+						    GFP_KERNEL, numa);
 			if (!rcd->opstats)
 				goto bail;
 		}
diff --git a/drivers/infiniband/hw/hfi1/pcie.c b/drivers/infiniband/hw/hfi1/pcie.c
index 4ac8f33..335613a1 100644
--- a/drivers/infiniband/hw/hfi1/pcie.c
+++ b/drivers/infiniband/hw/hfi1/pcie.c
@@ -673,12 +673,12 @@
 
 #define UNSET_PSET 255
 #define DEFAULT_DISCRETE_PSET 2	/* discrete HFI */
-#define DEFAULT_MCP_PSET 4	/* MCP HFI */
+#define DEFAULT_MCP_PSET 6	/* MCP HFI */
 static uint pcie_pset = UNSET_PSET;
 module_param(pcie_pset, uint, S_IRUGO);
 MODULE_PARM_DESC(pcie_pset, "PCIe Eq Pset value to use, range is 0-10");
 
-static uint pcie_ctle = 1; /* discrete on, integrated off */
+static uint pcie_ctle = 3; /* discrete on, integrated on */
 module_param(pcie_ctle, uint, S_IRUGO);
 MODULE_PARM_DESC(pcie_ctle, "PCIe static CTLE mode, bit 0 - discrete on/off, bit 1 - integrated on/off");
 
diff --git a/drivers/infiniband/hw/hfi1/rc.c b/drivers/infiniband/hw/hfi1/rc.c
index 4bd5b5c..613074e9 100644
--- a/drivers/infiniband/hw/hfi1/rc.c
+++ b/drivers/infiniband/hw/hfi1/rc.c
@@ -551,7 +551,7 @@
 		case IB_WR_RDMA_WRITE:
 			if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
 				qp->s_lsn++;
-			/* FALLTHROUGH */
+			goto no_flow_control;
 		case IB_WR_RDMA_WRITE_WITH_IMM:
 			/* If no credit, return. */
 			if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) &&
@@ -559,6 +559,7 @@
 				qp->s_flags |= RVT_S_WAIT_SSN_CREDIT;
 				goto bail;
 			}
+no_flow_control:
 			put_ib_reth_vaddr(
 				wqe->rdma_wr.remote_addr,
 				&ohdr->u.rc.reth);
diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c
index 282c9fb..786f640 100644
--- a/drivers/infiniband/hw/mlx5/main.c
+++ b/drivers/infiniband/hw/mlx5/main.c
@@ -325,6 +325,27 @@
 	return cpu_to_be16(MLX5_CAP_ROCE(dev->mdev, r_roce_min_src_udp_port));
 }
 
+int mlx5_get_roce_gid_type(struct mlx5_ib_dev *dev, u8 port_num,
+			   int index, enum ib_gid_type *gid_type)
+{
+	struct ib_gid_attr attr;
+	union ib_gid gid;
+	int ret;
+
+	ret = ib_get_cached_gid(&dev->ib_dev, port_num, index, &gid, &attr);
+	if (ret)
+		return ret;
+
+	if (!attr.ndev)
+		return -ENODEV;
+
+	dev_put(attr.ndev);
+
+	*gid_type = attr.gid_type;
+
+	return 0;
+}
+
 static int mlx5_use_mad_ifc(struct mlx5_ib_dev *dev)
 {
 	if (MLX5_CAP_GEN(dev->mdev, port_type) == MLX5_CAP_PORT_TYPE_IB)
diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h
index 7d68990..86e1e081 100644
--- a/drivers/infiniband/hw/mlx5/mlx5_ib.h
+++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h
@@ -892,6 +892,8 @@
 
 __be16 mlx5_get_roce_udp_sport(struct mlx5_ib_dev *dev, u8 port_num,
 			       int index);
+int mlx5_get_roce_gid_type(struct mlx5_ib_dev *dev, u8 port_num,
+			   int index, enum ib_gid_type *gid_type);
 
 /* GSI QP helper functions */
 struct ib_qp *mlx5_ib_gsi_create_qp(struct ib_pd *pd,
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index aee3942..2665414 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -2226,6 +2226,7 @@
 {
 	enum rdma_link_layer ll = rdma_port_get_link_layer(&dev->ib_dev, port);
 	int err;
+	enum ib_gid_type gid_type;
 
 	if (attr_mask & IB_QP_PKEY_INDEX)
 		path->pkey_index = cpu_to_be16(alt ? attr->alt_pkey_index :
@@ -2244,10 +2245,16 @@
 	if (ll == IB_LINK_LAYER_ETHERNET) {
 		if (!(ah->ah_flags & IB_AH_GRH))
 			return -EINVAL;
+		err = mlx5_get_roce_gid_type(dev, port, ah->grh.sgid_index,
+					     &gid_type);
+		if (err)
+			return err;
 		memcpy(path->rmac, ah->dmac, sizeof(ah->dmac));
 		path->udp_sport = mlx5_get_roce_udp_sport(dev, port,
 							  ah->grh.sgid_index);
 		path->dci_cfi_prio_sl = (ah->sl & 0x7) << 4;
+		if (gid_type == IB_GID_TYPE_ROCE_UDP_ENCAP)
+			path->ecn_dscp = (ah->grh.traffic_class >> 2) & 0x3f;
 	} else {
 		path->fl_free_ar = (path_flags & MLX5_PATH_FLAG_FL) ? 0x80 : 0;
 		path->fl_free_ar |=
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index ded2717..cedb447 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -7080,7 +7080,7 @@
 	unsigned long flags;
 
 	while (wait) {
-		unsigned long shadow;
+		unsigned long shadow = 0;
 		int cstart, previ = -1;
 
 		/*
diff --git a/drivers/infiniband/hw/qib/qib_rc.c b/drivers/infiniband/hw/qib/qib_rc.c
index f3fe787..c1523f9 100644
--- a/drivers/infiniband/hw/qib/qib_rc.c
+++ b/drivers/infiniband/hw/qib/qib_rc.c
@@ -357,7 +357,7 @@
 		case IB_WR_RDMA_WRITE:
 			if (newreq && !(qp->s_flags & RVT_S_UNLIMITED_CREDIT))
 				qp->s_lsn++;
-			/* FALLTHROUGH */
+			goto no_flow_control;
 		case IB_WR_RDMA_WRITE_WITH_IMM:
 			/* If no credit, return. */
 			if (!(qp->s_flags & RVT_S_UNLIMITED_CREDIT) &&
@@ -365,7 +365,7 @@
 				qp->s_flags |= RVT_S_WAIT_SSN_CREDIT;
 				goto bail;
 			}
-
+no_flow_control:
 			ohdr->u.rc.reth.vaddr =
 				cpu_to_be64(wqe->rdma_wr.remote_addr);
 			ohdr->u.rc.reth.rkey =
diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c
index 6bac071..ee26a1b 100644
--- a/drivers/infiniband/sw/rxe/rxe_pool.c
+++ b/drivers/infiniband/sw/rxe/rxe_pool.c
@@ -274,6 +274,7 @@
 	if (index >= range)
 		index = find_first_zero_bit(pool->table, range);
 
+	WARN_ON_ONCE(index >= range);
 	set_bit(index, pool->table);
 	pool->last = index;
 	return index + pool->min_index;
diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c
index 9f46be5..9d08478 100644
--- a/drivers/infiniband/sw/rxe/rxe_req.c
+++ b/drivers/infiniband/sw/rxe/rxe_req.c
@@ -633,6 +633,7 @@
 				goto exit;
 			}
 			rmr->state = RXE_MEM_STATE_FREE;
+			rxe_drop_ref(rmr);
 			wqe->state = wqe_state_done;
 			wqe->status = IB_WC_SUCCESS;
 		} else if (wqe->wr.opcode == IB_WR_REG_MR) {
diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 4d2a346..39101b1 100644
--- a/drivers/infiniband/sw/rxe/rxe_resp.c
+++ b/drivers/infiniband/sw/rxe/rxe_resp.c
@@ -418,7 +418,7 @@
 static enum resp_states check_rkey(struct rxe_qp *qp,
 				   struct rxe_pkt_info *pkt)
 {
-	struct rxe_mem *mem;
+	struct rxe_mem *mem = NULL;
 	u64 va;
 	u32 rkey;
 	u32 resid;
@@ -452,38 +452,38 @@
 	mem = lookup_mem(qp->pd, access, rkey, lookup_remote);
 	if (!mem) {
 		state = RESPST_ERR_RKEY_VIOLATION;
-		goto err1;
+		goto err;
 	}
 
 	if (unlikely(mem->state == RXE_MEM_STATE_FREE)) {
 		state = RESPST_ERR_RKEY_VIOLATION;
-		goto err1;
+		goto err;
 	}
 
 	if (mem_check_range(mem, va, resid)) {
 		state = RESPST_ERR_RKEY_VIOLATION;
-		goto err2;
+		goto err;
 	}
 
 	if (pkt->mask & RXE_WRITE_MASK)	 {
 		if (resid > mtu) {
 			if (pktlen != mtu || bth_pad(pkt)) {
 				state = RESPST_ERR_LENGTH;
-				goto err2;
+				goto err;
 			}
 
 			qp->resp.resid = mtu;
 		} else {
 			if (pktlen != resid) {
 				state = RESPST_ERR_LENGTH;
-				goto err2;
+				goto err;
 			}
 			if ((bth_pad(pkt) != (0x3 & (-resid)))) {
 				/* This case may not be exactly that
 				 * but nothing else fits.
 				 */
 				state = RESPST_ERR_LENGTH;
-				goto err2;
+				goto err;
 			}
 		}
 	}
@@ -493,9 +493,9 @@
 	qp->resp.mr = mem;
 	return RESPST_EXECUTE;
 
-err2:
-	rxe_drop_ref(mem);
-err1:
+err:
+	if (mem)
+		rxe_drop_ref(mem);
 	return state;
 }
 
@@ -893,6 +893,7 @@
 					return RESPST_ERROR;
 				}
 				rmr->state = RXE_MEM_STATE_FREE;
+				rxe_drop_ref(rmr);
 			}
 
 			wc->qp			= &qp->ibqp;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 0616a65..7576166 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1392,7 +1392,7 @@
 
 	while (!list_empty(&priv->cm.reap_list)) {
 		p = list_entry(priv->cm.reap_list.next, typeof(*p), list);
-		list_del(&p->list);
+		list_del_init(&p->list);
 		spin_unlock_irqrestore(&priv->lock, flags);
 		netif_tx_unlock_bh(dev);
 		ipoib_cm_tx_destroy(p);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 08c4b02..183db0c 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1302,7 +1302,7 @@
 						   rcu_dereference_protected(neigh->hnext,
 									     lockdep_is_held(&priv->lock)));
 				/* remove from path/mc list */
-				list_del(&neigh->list);
+				list_del_init(&neigh->list);
 				call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
 			} else {
 				np = &neigh->hnext;
@@ -1466,7 +1466,7 @@
 					   rcu_dereference_protected(neigh->hnext,
 								     lockdep_is_held(&priv->lock)));
 			/* remove from parent list */
-			list_del(&neigh->list);
+			list_del_init(&neigh->list);
 			call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
 			return;
 		} else {
@@ -1551,7 +1551,7 @@
 						   rcu_dereference_protected(neigh->hnext,
 									     lockdep_is_held(&priv->lock)));
 				/* remove from parent list */
-				list_del(&neigh->list);
+				list_del_init(&neigh->list);
 				call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
 			} else {
 				np = &neigh->hnext;
@@ -1593,7 +1593,7 @@
 					   rcu_dereference_protected(neigh->hnext,
 								     lockdep_is_held(&priv->lock)));
 			/* remove from path/mc list */
-			list_del(&neigh->list);
+			list_del_init(&neigh->list);
 			call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
 		}
 	}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
index 57eadd2..93b50be 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
@@ -165,11 +165,11 @@
 out:
 	up_write(&ppriv->vlan_rwsem);
 
+	rtnl_unlock();
+
 	if (result)
 		free_netdev(priv->dev);
 
-	rtnl_unlock();
-
 	return result;
 }
 
@@ -193,7 +193,6 @@
 	list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
 		if (priv->pkey == pkey &&
 		    priv->child_type == IPOIB_LEGACY_CHILD) {
-			unregister_netdevice(priv->dev);
 			list_del(&priv->list);
 			dev = priv->dev;
 			break;
@@ -201,6 +200,11 @@
 	}
 	up_write(&ppriv->vlan_rwsem);
 
+	if (dev) {
+		ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);
+		unregister_netdevice(dev);
+	}
+
 	rtnl_unlock();
 
 	if (dev) {
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index 0fd612d..aaf43be 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -87,7 +87,8 @@
 	struct mpr121_touchkey *mpr121 = dev_id;
 	struct i2c_client *client = mpr121->client;
 	struct input_dev *input = mpr121->input_dev;
-	unsigned int key_num, key_val, pressed;
+	unsigned long bit_changed;
+	unsigned int key_num;
 	int reg;
 
 	reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
@@ -105,19 +106,23 @@
 
 	reg &= TOUCH_STATUS_MASK;
 	/* use old press bit to figure out which bit changed */
-	key_num = ffs(reg ^ mpr121->statusbits) - 1;
-	pressed = reg & (1 << key_num);
+	bit_changed = reg ^ mpr121->statusbits;
 	mpr121->statusbits = reg;
+	for_each_set_bit(key_num, &bit_changed, mpr121->keycount) {
+		unsigned int key_val, pressed;
 
-	key_val = mpr121->keycodes[key_num];
+		pressed = reg & BIT(key_num);
+		key_val = mpr121->keycodes[key_num];
 
-	input_event(input, EV_MSC, MSC_SCAN, key_num);
-	input_report_key(input, key_val, pressed);
+		input_event(input, EV_MSC, MSC_SCAN, key_num);
+		input_report_key(input, key_val, pressed);
+
+		dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
+			pressed ? "pressed" : "released");
+
+	}
 	input_sync(input);
 
-	dev_dbg(&client->dev, "key %d %d %s\n", key_num, key_val,
-		pressed ? "pressed" : "released");
-
 out:
 	return IRQ_HANDLED;
 }
@@ -231,6 +236,7 @@
 	input_dev->id.bustype = BUS_I2C;
 	input_dev->dev.parent = &client->dev;
 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 
 	input_dev->keycode = mpr121->keycodes;
 	input_dev->keycodesize = sizeof(mpr121->keycodes[0]);
diff --git a/drivers/input/misc/hbtp_input.c b/drivers/input/misc/hbtp_input.c
index 7a6f50b..e174102 100644
--- a/drivers/input/misc/hbtp_input.c
+++ b/drivers/input/misc/hbtp_input.c
@@ -30,10 +30,7 @@
 #include <linux/delay.h>
 #include <linux/completion.h>
 
-#if defined(CONFIG_FB)
-#include <linux/notifier.h>
-#include <linux/fb.h>
-#endif
+#include <linux/msm_drm_notify.h>
 
 #define HBTP_INPUT_NAME			"hbtp_input"
 #define DISP_COORDS_SIZE		2
@@ -41,6 +38,7 @@
 #define HBTP_PINCTRL_VALID_STATE_CNT		(2)
 #define HBTP_HOLD_DURATION_US			(10)
 #define HBTP_PINCTRL_DDIC_SEQ_NUM		(4)
+#define HBTP_WAIT_TIMEOUT_MS			2000
 
 struct hbtp_data {
 	struct platform_device *pdev;
@@ -50,9 +48,7 @@
 	struct mutex sensormutex;
 	struct hbtp_sensor_data *sensor_data;
 	bool touch_status[HBTP_MAX_FINGER];
-#if defined(CONFIG_FB)
-	struct notifier_block fb_notif;
-#endif
+	struct notifier_block dsi_panel_notif;
 	struct pinctrl *ts_pinctrl;
 	struct pinctrl_state *gpio_state_active;
 	struct pinctrl_state *gpio_state_suspend;
@@ -61,7 +57,7 @@
 	struct pinctrl_state *ddic_rst_state_suspend;
 	u32 ts_pinctrl_seq_delay;
 	u32 ddic_pinctrl_seq_delay[HBTP_PINCTRL_DDIC_SEQ_NUM];
-	u32 fb_resume_seq_delay;
+	u32 dsi_panel_resume_seq_delay;
 	bool lcd_on;
 	bool power_suspended;
 	bool power_sync_enabled;
@@ -92,70 +88,58 @@
 	struct kobject *sysfs_kobject;
 	s16 ROI[MAX_ROI_SIZE];
 	s16 accelBuffer[MAX_ACCEL_SIZE];
+	u32 display_status;
 };
 
 static struct hbtp_data *hbtp;
 
 static struct kobject *sensor_kobject;
 
-#if defined(CONFIG_FB)
-static int hbtp_fb_suspend(struct hbtp_data *ts);
-static int hbtp_fb_early_resume(struct hbtp_data *ts);
-static int hbtp_fb_resume(struct hbtp_data *ts);
-#endif
+static int hbtp_dsi_panel_suspend(struct hbtp_data *ts);
+static int hbtp_dsi_panel_early_resume(struct hbtp_data *ts);
 
-#if defined(CONFIG_FB)
-static int fb_notifier_callback(struct notifier_block *self,
+static int dsi_panel_notifier_callback(struct notifier_block *self,
 				 unsigned long event, void *data)
 {
 	int blank;
-	struct fb_event *evdata = data;
+	struct msm_drm_notifier *evdata = data;
 	struct hbtp_data *hbtp_data =
-	container_of(self, struct hbtp_data, fb_notif);
+	container_of(self, struct hbtp_data, dsi_panel_notif);
 
-	if (evdata && evdata->data && hbtp_data &&
-		(event == FB_EARLY_EVENT_BLANK ||
-		event == FB_R_EARLY_EVENT_BLANK)) {
+	if (!evdata || (evdata->id != 0))
+		return 0;
+
+	if (hbtp_data && (event == MSM_DRM_EARLY_EVENT_BLANK)) {
 		blank = *(int *)(evdata->data);
-		if (event == FB_EARLY_EVENT_BLANK) {
-			if (blank == FB_BLANK_UNBLANK) {
-				pr_debug("%s: receives EARLY_BLANK:UNBLANK\n",
+		if (blank == MSM_DRM_BLANK_UNBLANK) {
+			pr_debug("%s: receives EARLY_BLANK:UNBLANK\n",
 					__func__);
-				hbtp_data->lcd_on = true;
-				hbtp_fb_early_resume(hbtp_data);
-			} else if (blank == FB_BLANK_POWERDOWN) {
-				pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
-					__func__);
-				hbtp_data->lcd_on = false;
-			}
-		} else if (event == FB_R_EARLY_EVENT_BLANK) {
-			if (blank == FB_BLANK_UNBLANK) {
-				pr_debug("%s: receives R_EARLY_BALNK:UNBLANK\n",
-					__func__);
-				hbtp_data->lcd_on = false;
-				hbtp_fb_suspend(hbtp_data);
-			} else if (blank == FB_BLANK_POWERDOWN) {
-				pr_debug("%s: receives R_EARLY_BALNK:POWERDOWN\n",
-					__func__);
-				hbtp_data->lcd_on = true;
-			}
+			hbtp_data->lcd_on = true;
+			hbtp_dsi_panel_early_resume(hbtp_data);
+		} else if (blank == MSM_DRM_BLANK_POWERDOWN) {
+			pr_debug("%s: receives EARLY_BLANK:POWERDOWN\n",
+				__func__);
+			hbtp_data->lcd_on = false;
+		} else {
+			pr_err("%s: receives wrong data EARLY_BLANK:%d\n",
+				__func__, blank);
 		}
 	}
 
-	if (evdata && evdata->data && hbtp_data &&
-		event == FB_EVENT_BLANK) {
+	if (hbtp_data && event == MSM_DRM_EVENT_BLANK) {
 		blank = *(int *)(evdata->data);
-		if (blank == FB_BLANK_POWERDOWN) {
+		if (blank == MSM_DRM_BLANK_POWERDOWN) {
 			pr_debug("%s: receives BLANK:POWERDOWN\n", __func__);
-			hbtp_fb_suspend(hbtp_data);
-		} else if (blank == FB_BLANK_UNBLANK) {
+			hbtp_dsi_panel_suspend(hbtp_data);
+		} else if (blank == MSM_DRM_BLANK_UNBLANK) {
 			pr_debug("%s: receives BLANK:UNBLANK\n", __func__);
-			hbtp_fb_resume(hbtp_data);
+		} else {
+			pr_err("%s: receives wrong data BLANK:%d\n",
+				__func__, blank);
 		}
 	}
 	return 0;
 }
-#endif
 
 static ssize_t hbtp_sensor_roi_show(struct file *dev, struct kobject *kobj,
 		struct bin_attribute *attr, char *buf, loff_t pos,
@@ -1133,7 +1117,7 @@
 	}
 
 	if (of_property_read_u32(np, "qcom,fb-resume-delay-us",
-			&data->fb_resume_seq_delay)) {
+			&data->dsi_panel_resume_seq_delay)) {
 		dev_warn(&data->pdev->dev, "Can not find fb resume seq delay\n");
 	}
 
@@ -1163,7 +1147,7 @@
 	return rc;
 }
 
-static int hbtp_fb_suspend(struct hbtp_data *ts)
+static int hbtp_dsi_panel_suspend(struct hbtp_data *ts)
 {
 	int rc;
 	char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
@@ -1188,29 +1172,30 @@
 			goto err_power_disable;
 		}
 		ts->power_suspended = true;
-	}
+		if (ts->input_dev) {
+			kobject_uevent_env(&ts->input_dev->dev.kobj,
+					KOBJ_OFFLINE, envp);
 
-	if (ts->input_dev) {
-		kobject_uevent_env(&ts->input_dev->dev.kobj,
-				KOBJ_OFFLINE, envp);
-
-		if (ts->power_sig_enabled) {
-			pr_debug("%s: power_sig is enabled, wait for signal\n",
-				__func__);
-			mutex_unlock(&hbtp->mutex);
-			rc = wait_for_completion_interruptible(
-				&hbtp->power_suspend_sig);
-			if (rc != 0) {
-				pr_err("%s: wait for suspend is interrupted\n",
+			if (ts->power_sig_enabled) {
+				pr_debug("%s: power_sig is enabled, wait for signal\n",
+					__func__);
+				mutex_unlock(&hbtp->mutex);
+				rc = wait_for_completion_interruptible_timeout(
+					&hbtp->power_suspend_sig,
+					msecs_to_jiffies(HBTP_WAIT_TIMEOUT_MS));
+				if (rc <= 0) {
+					pr_err("%s: wait for suspend is interrupted\n",
+						__func__);
+				}
+				mutex_lock(&hbtp->mutex);
+				pr_debug("%s: Wait is done for suspend\n",
+					__func__);
+			} else {
+				pr_debug("%s: power_sig is NOT enabled\n",
 					__func__);
 			}
-			mutex_lock(&hbtp->mutex);
-			pr_debug("%s: Wait is done for suspend\n", __func__);
-		} else {
-			pr_debug("%s: power_sig is NOT enabled", __func__);
 		}
 	}
-
 	mutex_unlock(&hbtp->mutex);
 	return 0;
 err_power_disable:
@@ -1220,15 +1205,12 @@
 	return rc;
 }
 
-static int hbtp_fb_early_resume(struct hbtp_data *ts)
+static int hbtp_dsi_panel_early_resume(struct hbtp_data *ts)
 {
 	char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
 	int rc;
 
 	mutex_lock(&hbtp->mutex);
-
-	pr_debug("%s: hbtp_fb_early_resume\n", __func__);
-
 	if (ts->pdev && ts->power_sync_enabled) {
 		pr_debug("%s: power_sync is enabled\n", __func__);
 		if (!ts->power_suspended) {
@@ -1260,9 +1242,10 @@
 				pr_err("%s: power_sig is enabled, wait for signal\n",
 					__func__);
 				mutex_unlock(&hbtp->mutex);
-				rc = wait_for_completion_interruptible(
-					&hbtp->power_resume_sig);
-				if (rc != 0) {
+				rc = wait_for_completion_interruptible_timeout(
+					&hbtp->power_resume_sig,
+					msecs_to_jiffies(HBTP_WAIT_TIMEOUT_MS));
+				if (rc <= 0) {
 					pr_err("%s: wait for resume is interrupted\n",
 						__func__);
 				}
@@ -1273,12 +1256,13 @@
 					__func__);
 			}
 
-			if (ts->fb_resume_seq_delay) {
-				usleep_range(ts->fb_resume_seq_delay,
-					ts->fb_resume_seq_delay +
+			if (ts->dsi_panel_resume_seq_delay) {
+				usleep_range(ts->dsi_panel_resume_seq_delay,
+					ts->dsi_panel_resume_seq_delay +
 					HBTP_HOLD_DURATION_US);
-				pr_err("%s: fb_resume_seq_delay = %u\n",
-					__func__, ts->fb_resume_seq_delay);
+				pr_err("%s: dsi_panel_resume_seq_delay = %u\n",
+					__func__,
+					ts->dsi_panel_resume_seq_delay);
 			}
 		}
 	}
@@ -1292,22 +1276,6 @@
 	return rc;
 }
 
-static int hbtp_fb_resume(struct hbtp_data *ts)
-{
-	char *envp[2] = {HBTP_EVENT_TYPE_DISPLAY, NULL};
-
-	mutex_lock(&hbtp->mutex);
-	if (!ts->power_sync_enabled) {
-		pr_debug("%s: power_sync is disabled, send uevent\n", __func__);
-		if (ts->input_dev) {
-			kobject_uevent_env(&ts->input_dev->dev.kobj,
-				KOBJ_ONLINE, envp);
-		}
-	}
-	mutex_unlock(&hbtp->mutex);
-	return 0;
-}
-
 static int hbtp_pdev_probe(struct platform_device *pdev)
 {
 	int error;
@@ -1425,28 +1393,44 @@
 	if (ret) {
 		pr_err("hbtp: ret error: %zd\n", ret);
 		mutex_unlock(&hbtp->mutex);
-		return ret;
+		return 0;
 	}
-	if (!hbtp || !hbtp->input_dev) {
-		pr_err("hbtp: hbtp or hbtp->input_dev not ready!\n");
+	hbtp->display_status = status;
+	if (!hbtp->input_dev) {
+		pr_err("hbtp: hbtp->input_dev not ready!\n");
 		mutex_unlock(&hbtp->mutex);
 		return ret;
 	}
-	if (status) {
-		pr_debug("hbtp: display power on!\n");
-		kobject_uevent_env(&hbtp->input_dev->dev.kobj,
-			KOBJ_ONLINE, envp);
-	} else {
-		pr_debug("hbtp: display power off!\n");
-		kobject_uevent_env(&hbtp->input_dev->dev.kobj,
-			KOBJ_OFFLINE, envp);
+	if (!hbtp->power_sync_enabled) {
+		if (status) {
+			pr_debug("hbtp: display power on!\n");
+			kobject_uevent_env(&hbtp->input_dev->dev.kobj,
+				KOBJ_ONLINE, envp);
+		} else {
+			pr_debug("hbtp: display power off!\n");
+			kobject_uevent_env(&hbtp->input_dev->dev.kobj,
+				KOBJ_OFFLINE, envp);
+		}
 	}
 	mutex_unlock(&hbtp->mutex);
 	return count;
 }
 
+static ssize_t hbtp_display_pwr_show(struct kobject *kobj,
+		struct kobj_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+
+	mutex_lock(&hbtp->mutex);
+	ret = snprintf(buf, PAGE_SIZE, "%u\n", hbtp->display_status);
+	mutex_unlock(&hbtp->mutex);
+	return ret;
+}
+
 static struct kobj_attribute hbtp_display_attribute =
-		__ATTR(display_pwr, 0660, NULL, hbtp_display_pwr_store);
+		__ATTR(display_pwr, 0660, hbtp_display_pwr_show,
+			hbtp_display_pwr_store);
+
 
 static int __init hbtp_init(void)
 {
@@ -1463,6 +1447,7 @@
 
 	mutex_init(&hbtp->mutex);
 	mutex_init(&hbtp->sensormutex);
+	hbtp->display_status = 1;
 
 	error = misc_register(&hbtp_input_misc);
 	if (error) {
@@ -1470,15 +1455,13 @@
 		goto err_misc_reg;
 	}
 
-#if defined(CONFIG_FB)
-	hbtp->fb_notif.notifier_call = fb_notifier_callback;
-	error = fb_register_client(&hbtp->fb_notif);
+	hbtp->dsi_panel_notif.notifier_call = dsi_panel_notifier_callback;
+	error = msm_drm_register_client(&hbtp->dsi_panel_notif);
 	if (error) {
-		pr_err("%s: Unable to register fb_notifier: %d\n",
+		pr_err("%s: Unable to register dsi_panel_notifier: %d\n",
 			HBTP_INPUT_NAME, error);
-		goto err_fb_reg;
+		goto err_dsi_panel_reg;
 	}
-#endif
 
 	sensor_kobject = kobject_create_and_add("hbtpsensor", kernel_kobj);
 	if (!sensor_kobject) {
@@ -1527,10 +1510,8 @@
 err_sysfs_create_capdata:
 	kobject_put(sensor_kobject);
 err_kobject_create:
-#if defined(CONFIG_FB)
-	fb_unregister_client(&hbtp->fb_notif);
-err_fb_reg:
-#endif
+	msm_drm_unregister_client(&hbtp->dsi_panel_notif);
+err_dsi_panel_reg:
 	misc_deregister(&hbtp_input_misc);
 err_misc_reg:
 	kfree(hbtp->sensor_data);
@@ -1545,13 +1526,13 @@
 	sysfs_remove_bin_file(sensor_kobject, &vibdata_attr);
 	sysfs_remove_bin_file(sensor_kobject, &capdata_attr);
 	kobject_put(sensor_kobject);
+	sysfs_remove_file(hbtp->sysfs_kobject, &hbtp_display_attribute.attr);
+	kobject_put(hbtp->sysfs_kobject);
 	misc_deregister(&hbtp_input_misc);
 	if (hbtp->input_dev)
 		input_unregister_device(hbtp->input_dev);
 
-#if defined(CONFIG_FB)
-	fb_unregister_client(&hbtp->fb_notif);
-#endif
+	msm_drm_unregister_client(&hbtp->dsi_panel_notif);
 
 	platform_driver_unregister(&hbtp_pdev_driver);
 
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
index c5ab3dd..fdcc146 100644
--- a/drivers/input/misc/keychord.c
+++ b/drivers/input/misc/keychord.c
@@ -60,6 +60,10 @@
 	unsigned char		head;
 	unsigned char		tail;
 	__u16			buff[BUFFER_SIZE];
+	/* Bit to serialize writes to this device */
+#define KEYCHORD_BUSY			0x01
+	unsigned long		flags;
+	wait_queue_head_t	write_waitq;
 };
 
 static int check_keychord(struct keychord_device *kdev,
@@ -172,7 +176,6 @@
 		goto err_input_open_device;
 
 	pr_info("keychord: using input dev %s for fevent\n", dev->name);
-
 	return 0;
 
 err_input_open_device:
@@ -225,6 +228,41 @@
 }
 
 /*
+ * serializes writes on a device. can use mutex_lock_interruptible()
+ * for this particular use case as well - a matter of preference.
+ */
+static int
+keychord_write_lock(struct keychord_device *kdev)
+{
+	int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kdev->lock, flags);
+	while (kdev->flags & KEYCHORD_BUSY) {
+		spin_unlock_irqrestore(&kdev->lock, flags);
+		ret = wait_event_interruptible(kdev->write_waitq,
+			       ((kdev->flags & KEYCHORD_BUSY) == 0));
+		if (ret)
+			return ret;
+		spin_lock_irqsave(&kdev->lock, flags);
+	}
+	kdev->flags |= KEYCHORD_BUSY;
+	spin_unlock_irqrestore(&kdev->lock, flags);
+	return 0;
+}
+
+static void
+keychord_write_unlock(struct keychord_device *kdev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kdev->lock, flags);
+	kdev->flags &= ~KEYCHORD_BUSY;
+	spin_unlock_irqrestore(&kdev->lock, flags);
+	wake_up_interruptible(&kdev->write_waitq);
+}
+
+/*
  * keychord_write is used to configure the driver
  */
 static ssize_t keychord_write(struct file *file, const char __user *buffer,
@@ -250,6 +288,22 @@
 		return -EFAULT;
 	}
 
+	/*
+	 * Serialize writes to this device to prevent various races.
+	 * 1) writers racing here could do duplicate input_unregister_handler()
+	 *    calls, resulting in attempting to unlink a node from a list that
+	 *    does not exist.
+	 * 2) writers racing here could do duplicate input_register_handler() calls
+	 *    below, resulting in a duplicate insertion of a node into the list.
+	 * 3) a double kfree of keychords can occur (in the event that
+	 *    input_register_handler() fails below.
+	 */
+	ret = keychord_write_lock(kdev);
+	if (ret) {
+		kfree(keychords);
+		return ret;
+	}
+
 	/* unregister handler before changing configuration */
 	if (kdev->registered) {
 		input_unregister_handler(&kdev->input_handler);
@@ -318,15 +372,19 @@
 	if (ret) {
 		kfree(keychords);
 		kdev->keychords = 0;
+		keychord_write_unlock(kdev);
 		return ret;
 	}
 	kdev->registered = 1;
 
+	keychord_write_unlock(kdev);
+
 	return count;
 
 err_unlock_return:
 	spin_unlock_irqrestore(&kdev->lock, flags);
 	kfree(keychords);
+	keychord_write_unlock(kdev);
 	return -EINVAL;
 }
 
@@ -352,6 +410,7 @@
 
 	spin_lock_init(&kdev->lock);
 	init_waitqueue_head(&kdev->waitq);
+	init_waitqueue_head(&kdev->write_waitq);
 
 	kdev->input_handler.event = keychord_event;
 	kdev->input_handler.connect = keychord_connect;
@@ -373,6 +432,7 @@
 
 	if (kdev->registered)
 		input_unregister_handler(&kdev->input_handler);
+	kfree(kdev->keychords);
 	kfree(kdev);
 
 	return 0;
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 518e8a7..f26807c 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1212,14 +1212,24 @@
 
 	case SS4_PACKET_ID_TWO:
 		if (priv->flags & ALPS_BUTTONPAD) {
-			f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
+			if (IS_SS4PLUS_DEV(priv->dev_id)) {
+				f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1);
+			} else {
+				f->mt[0].x = SS4_BTL_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
+			}
 			f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0);
-			f->mt[1].x = SS4_BTL_MF_X_V2(p, 1);
 			f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1);
 		} else {
-			f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+			if (IS_SS4PLUS_DEV(priv->dev_id)) {
+				f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1);
+			} else {
+				f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+			}
 			f->mt[0].y = SS4_STD_MF_Y_V2(p, 0);
-			f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
 			f->mt[1].y = SS4_STD_MF_Y_V2(p, 1);
 		}
 		f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0;
@@ -1236,16 +1246,27 @@
 
 	case SS4_PACKET_ID_MULTI:
 		if (priv->flags & ALPS_BUTTONPAD) {
-			f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
+			if (IS_SS4PLUS_DEV(priv->dev_id)) {
+				f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1);
+			} else {
+				f->mt[2].x = SS4_BTL_MF_X_V2(p, 0);
+				f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
+			}
+
 			f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0);
-			f->mt[3].x = SS4_BTL_MF_X_V2(p, 1);
 			f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1);
 			no_data_x = SS4_MFPACKET_NO_AX_BL;
 			no_data_y = SS4_MFPACKET_NO_AY_BL;
 		} else {
-			f->mt[2].x = SS4_STD_MF_X_V2(p, 0);
+			if (IS_SS4PLUS_DEV(priv->dev_id)) {
+				f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1);
+			} else {
+				f->mt[0].x = SS4_STD_MF_X_V2(p, 0);
+				f->mt[1].x = SS4_STD_MF_X_V2(p, 1);
+			}
 			f->mt[2].y = SS4_STD_MF_Y_V2(p, 0);
-			f->mt[3].x = SS4_STD_MF_X_V2(p, 1);
 			f->mt[3].y = SS4_STD_MF_Y_V2(p, 1);
 			no_data_x = SS4_MFPACKET_NO_AX;
 			no_data_y = SS4_MFPACKET_NO_AY;
@@ -2535,8 +2556,8 @@
 
 	memset(otp, 0, sizeof(otp));
 
-	if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) ||
-	    alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]))
+	if (alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]) ||
+	    alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]))
 		return -1;
 
 	alps_update_device_area_ss4_v2(otp, priv);
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index dbfd260..7931237 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -91,6 +91,10 @@
 				 ((_b[1 + _i * 3]  << 5) & 0x1F00)	\
 				)
 
+#define SS4_PLUS_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 4) & 0x0070) | \
+				 ((_b[1 + (_i) * 3]  << 4) & 0x0F80)	\
+				)
+
 #define SS4_STD_MF_Y_V2(_b, _i)	(((_b[1 + (_i) * 3] << 3) & 0x0010) |	\
 				 ((_b[2 + (_i) * 3] << 5) & 0x01E0) |	\
 				 ((_b[2 + (_i) * 3] << 4) & 0x0E00)	\
@@ -100,6 +104,10 @@
 				 ((_b[0 + (_i) * 3] >> 3) & 0x0010)	\
 				)
 
+#define SS4_PLUS_BTL_MF_X_V2(_b, _i) (SS4_PLUS_STD_MF_X_V2(_b, _i) |	\
+				 ((_b[0 + (_i) * 3] >> 4) & 0x0008)	\
+				)
+
 #define SS4_BTL_MF_Y_V2(_b, _i)	(SS4_STD_MF_Y_V2(_b, _i) | \
 				 ((_b[0 + (_i) * 3] >> 3) & 0x0008)	\
 				)
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index da5458d..c9d491b 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -1234,7 +1234,14 @@
 	{ "ELAN0000", 0 },
 	{ "ELAN0100", 0 },
 	{ "ELAN0600", 0 },
+	{ "ELAN0602", 0 },
 	{ "ELAN0605", 0 },
+	{ "ELAN0608", 0 },
+	{ "ELAN0605", 0 },
+	{ "ELAN0609", 0 },
+	{ "ELAN060B", 0 },
+	{ "ELAN060C", 0 },
+	{ "ELAN0611", 0 },
 	{ "ELAN1000", 0 },
 	{ }
 };
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index 354d47e..7e2dc5e 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -265,7 +265,8 @@
 	if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID)))
 		return -1;
 
-	if (param[0] != TP_MAGIC_IDENT)
+	/* add new TP ID. */
+	if (!(param[0] & TP_MAGIC_IDENT))
 		return -1;
 
 	if (firmware_id)
@@ -380,8 +381,8 @@
 		return 0;
 
 	if (trackpoint_read(&psmouse->ps2dev, TP_EXT_BTN, &button_info)) {
-		psmouse_warn(psmouse, "failed to get extended button data\n");
-		button_info = 0;
+		psmouse_warn(psmouse, "failed to get extended button data, assuming 3 buttons\n");
+		button_info = 0x33;
 	}
 
 	psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
index 5617ed3..8805575 100644
--- a/drivers/input/mouse/trackpoint.h
+++ b/drivers/input/mouse/trackpoint.h
@@ -21,8 +21,9 @@
 #define TP_COMMAND		0xE2	/* Commands start with this */
 
 #define TP_READ_ID		0xE1	/* Sent for device identification */
-#define TP_MAGIC_IDENT		0x01	/* Sent after a TP_READ_ID followed */
+#define TP_MAGIC_IDENT		0x03	/* Sent after a TP_READ_ID followed */
 					/* by the firmware ID */
+					/* Firmware ID includes 0x1, 0x2, 0x3 */
 
 
 /*
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 5be14ad..dbf0983 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -905,6 +905,13 @@
 		},
 	},
 	{
+		/* Gigabyte P57 - Elantech touchpad */
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+			DMI_MATCH(DMI_PRODUCT_NAME, "P57"),
+		},
+	},
+	{
 		/* Schenker XMG C504 - Elantech touchpad */
 		.matches = {
 			DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c
index abf09ac..339a0e2 100644
--- a/drivers/input/tablet/gtco.c
+++ b/drivers/input/tablet/gtco.c
@@ -230,13 +230,17 @@
 
 	/* Walk  this report and pull out the info we need */
 	while (i < length) {
-		prefix = report[i];
-
-		/* Skip over prefix */
-		i++;
+		prefix = report[i++];
 
 		/* Determine data size and save the data in the proper variable */
-		size = PREF_SIZE(prefix);
+		size = (1U << PREF_SIZE(prefix)) >> 1;
+		if (i + size > length) {
+			dev_err(ddev,
+				"Not enough data (need %d, have %d)\n",
+				i + size, length);
+			break;
+		}
+
 		switch (size) {
 		case 1:
 			data = report[i];
@@ -244,8 +248,7 @@
 		case 2:
 			data16 = get_unaligned_le16(&report[i]);
 			break;
-		case 3:
-			size = 4;
+		case 4:
 			data32 = get_unaligned_le32(&report[i]);
 			break;
 		}
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index ee0a86b..620fc50 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -13,7 +13,7 @@
 obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
 obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
 obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
-obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
+obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-errata.o
 obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
 obj-$(CONFIG_DMAR_TABLE) += dmar.o
 obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 41800b6..1a0b110 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -3120,6 +3120,7 @@
 	mutex_unlock(&domain->api_lock);
 
 	domain_flush_tlb_pde(domain);
+	domain_flush_complete(domain);
 
 	return unmap_size;
 }
@@ -4294,6 +4295,7 @@
 		/* Setting */
 		irte->hi.fields.ga_root_ptr = (pi_data->base >> 12);
 		irte->hi.fields.vector = vcpu_pi_info->vector;
+		irte->lo.fields_vapic.ga_log_intr = 1;
 		irte->lo.fields_vapic.guest_mode = 1;
 		irte->lo.fields_vapic.ga_tag = pi_data->ga_tag;
 
diff --git a/drivers/iommu/arm-smmu-errata.c b/drivers/iommu/arm-smmu-errata.c
new file mode 100644
index 0000000..2ee2028
--- /dev/null
+++ b/drivers/iommu/arm-smmu-errata.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <soc/qcom/secure_buffer.h>
+#include <linux/arm-smmu-errata.h>
+
+static struct page *guard_pages[VMID_LAST];
+static DEFINE_MUTEX(guard_page_lock);
+
+struct page *arm_smmu_errata_get_guard_page(int vmid)
+{
+	struct page *page;
+	int ret;
+	int source_vm = VMID_HLOS;
+	int dest_vm = vmid;
+	int dest_perm = PERM_READ | PERM_WRITE | PERM_EXEC;
+	size_t size = ARM_SMMU_MIN_IOVA_ALIGN;
+
+	mutex_lock(&guard_page_lock);
+	page = guard_pages[vmid];
+	if (page)
+		goto out;
+
+	page = alloc_pages(GFP_KERNEL, get_order(size));
+	if (!page)
+		goto out;
+
+	if (vmid != VMID_HLOS) {
+		ret = hyp_assign_phys(page_to_phys(page), PAGE_ALIGN(size),
+				&source_vm, 1,
+				&dest_vm, &dest_perm, 1);
+		if (ret) {
+			__free_pages(page, get_order(size));
+			page = NULL;
+		}
+	}
+	guard_pages[vmid] = page;
+out:
+	mutex_unlock(&guard_page_lock);
+	return page;
+}
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index e6f9b2d..d3d975a 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1040,13 +1040,8 @@
 		}
 	}
 
-	/* Nuke the existing Config, as we're going to rewrite it */
-	val &= ~(STRTAB_STE_0_CFG_MASK << STRTAB_STE_0_CFG_SHIFT);
-
-	if (ste->valid)
-		val |= STRTAB_STE_0_V;
-	else
-		val &= ~STRTAB_STE_0_V;
+	/* Nuke the existing STE_0 value, as we're going to rewrite it */
+	val = ste->valid ? STRTAB_STE_0_V : 0;
 
 	if (ste->bypass) {
 		val |= disable_bypass ? STRTAB_STE_0_CFG_ABORT
@@ -1081,7 +1076,6 @@
 		val |= (ste->s1_cfg->cdptr_dma & STRTAB_STE_0_S1CTXPTR_MASK
 		        << STRTAB_STE_0_S1CTXPTR_SHIFT) |
 			STRTAB_STE_0_CFG_S1_TRANS;
-
 	}
 
 	if (ste->s2_cfg) {
diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
index a2c5579..280ce0cf 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -51,6 +51,9 @@
 #include <linux/of_platform.h>
 #include <linux/msm-bus.h>
 #include <dt-bindings/msm/msm-bus-ids.h>
+#include <linux/remote_spinlock.h>
+#include <linux/ktime.h>
+#include <trace/events/iommu.h>
 
 #include <linux/amba/bus.h>
 
@@ -98,6 +101,9 @@
 #define sCR0_VMID16EN			(1 << 31)
 #define sCR0_BSU_SHIFT			14
 #define sCR0_BSU_MASK			0x3
+#define sCR0_SHCFG_SHIFT		22
+#define sCR0_SHCFG_MASK			0x3
+#define sCR0_SHCFG_NSH			3
 
 /* Auxiliary Configuration register */
 #define ARM_SMMU_GR0_sACR		0x10
@@ -174,6 +180,9 @@
 #define S2CR_CBNDX_MASK			0xff
 #define S2CR_TYPE_SHIFT			16
 #define S2CR_TYPE_MASK			0x3
+#define S2CR_SHCFG_SHIFT		8
+#define S2CR_SHCFG_MASK			0x3
+#define S2CR_SHCFG_NSH			0x3
 enum arm_smmu_s2cr_type {
 	S2CR_TYPE_TRANS,
 	S2CR_TYPE_BYPASS,
@@ -248,6 +257,9 @@
 #define ARM_SMMU_CB_ATS1PR		0x800
 #define ARM_SMMU_CB_ATSR		0x8f0
 
+#define SCTLR_SHCFG_SHIFT		22
+#define SCTLR_SHCFG_MASK		0x3
+#define SCTLR_SHCFG_NSH			0x3
 #define SCTLR_S1_ASIDPNE		(1 << 12)
 #define SCTLR_CFCFG			(1 << 7)
 #define SCTLR_HUPCF			(1 << 8)
@@ -319,14 +331,6 @@
 	QCOM_SMMUV500,
 };
 
-struct arm_smmu_device;
-struct arm_smmu_arch_ops {
-	int (*init)(struct arm_smmu_device *smmu);
-	void (*device_reset)(struct arm_smmu_device *smmu);
-	phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
-					 dma_addr_t iova);
-};
-
 struct arm_smmu_impl_def_reg {
 	u32 offset;
 	u32 value;
@@ -400,6 +404,7 @@
 	int				regulator_defer;
 };
 
+struct arm_smmu_arch_ops;
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -427,6 +432,8 @@
 #define ARM_SMMU_OPT_DYNAMIC		(1 << 3)
 #define ARM_SMMU_OPT_3LVL_TABLES	(1 << 4)
 #define ARM_SMMU_OPT_NO_ASID_RETENTION	(1 << 5)
+#define ARM_SMMU_OPT_DISABLE_ATOS	(1 << 6)
+#define ARM_SMMU_OPT_MMU500_ERRATA1	(1 << 7)
 	u32				options;
 	enum arm_smmu_arch_version	version;
 	enum arm_smmu_implementation	model;
@@ -526,6 +533,10 @@
 	struct mutex			assign_lock;
 	struct list_head		secure_pool_list;
 	struct iommu_domain		domain;
+
+	bool				qsmmuv500_errata1_init;
+	bool				qsmmuv500_errata1_client;
+	bool				qsmmuv500_errata2_min_align;
 };
 
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
@@ -547,6 +558,8 @@
 	{ ARM_SMMU_OPT_DYNAMIC, "qcom,dynamic" },
 	{ ARM_SMMU_OPT_3LVL_TABLES, "qcom,use-3-lvl-tables" },
 	{ ARM_SMMU_OPT_NO_ASID_RETENTION, "qcom,no-asid-retention" },
+	{ ARM_SMMU_OPT_DISABLE_ATOS, "qcom,disable-atos" },
+	{ ARM_SMMU_OPT_MMU500_ERRATA1, "qcom,mmu500-errata-1" },
 	{ 0, NULL},
 };
 
@@ -561,9 +574,6 @@
 static int arm_smmu_assign_table(struct arm_smmu_domain *smmu_domain);
 static void arm_smmu_unassign_table(struct arm_smmu_domain *smmu_domain);
 
-static int arm_smmu_arch_init(struct arm_smmu_device *smmu);
-static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu);
-
 static uint64_t arm_smmu_iova_to_pte(struct iommu_domain *domain,
 				    dma_addr_t iova);
 
@@ -572,6 +582,7 @@
 static int arm_smmu_alloc_cb(struct iommu_domain *domain,
 				struct arm_smmu_device *smmu,
 				struct device *dev);
+static struct iommu_gather_ops qsmmuv500_errata1_smmu_gather_ops;
 
 static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
 {
@@ -627,6 +638,76 @@
 		mutex_unlock(&smmu_domain->assign_lock);
 }
 
+/*
+ * init()
+ * Hook for additional device tree parsing at probe time.
+ *
+ * device_reset()
+ * Hook for one-time architecture-specific register settings.
+ *
+ * iova_to_phys_hard()
+ * Provides debug information. May be called from the context fault irq handler.
+ *
+ * init_context_bank()
+ * Hook for architecture-specific settings which require knowledge of the
+ * dynamically allocated context bank number.
+ *
+ * device_group()
+ * Hook for checking whether a device is compatible with a said group.
+ */
+struct arm_smmu_arch_ops {
+	int (*init)(struct arm_smmu_device *smmu);
+	void (*device_reset)(struct arm_smmu_device *smmu);
+	phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
+					 dma_addr_t iova);
+	void (*init_context_bank)(struct arm_smmu_domain *smmu_domain,
+					struct device *dev);
+	int (*device_group)(struct device *dev, struct iommu_group *group);
+};
+
+static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
+{
+	if (!smmu->arch_ops)
+		return 0;
+	if (!smmu->arch_ops->init)
+		return 0;
+	return smmu->arch_ops->init(smmu);
+}
+
+static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu)
+{
+	if (!smmu->arch_ops)
+		return;
+	if (!smmu->arch_ops->device_reset)
+		return;
+	return smmu->arch_ops->device_reset(smmu);
+}
+
+static void arm_smmu_arch_init_context_bank(
+		struct arm_smmu_domain *smmu_domain, struct device *dev)
+{
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+	if (!smmu->arch_ops)
+		return;
+	if (!smmu->arch_ops->init_context_bank)
+		return;
+	return smmu->arch_ops->init_context_bank(smmu_domain, dev);
+}
+
+static int arm_smmu_arch_device_group(struct device *dev,
+					struct iommu_group *group)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+
+	if (!smmu->arch_ops)
+		return 0;
+	if (!smmu->arch_ops->device_group)
+		return 0;
+	return smmu->arch_ops->device_group(dev, group);
+}
+
 static struct device_node *dev_get_dev_node(struct device *dev)
 {
 	if (dev_is_pci(dev)) {
@@ -1170,6 +1251,7 @@
 	list_for_each_entry_safe(it, i, &smmu_domain->secure_pool_list, list) {
 		arm_smmu_unprepare_pgtable(smmu_domain, it->addr, it->size);
 		/* pages will be freed later (after being unassigned) */
+		list_del(&it->list);
 		kfree(it);
 	}
 }
@@ -1226,11 +1308,12 @@
 {
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
 	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	const struct iommu_gather_ops *tlb = smmu_domain->pgtbl_cfg.tlb;
 	phys_addr_t phys;
 	phys_addr_t phys_post_tlbiall;
 
 	phys = arm_smmu_iova_to_phys_hard(domain, iova);
-	arm_smmu_tlb_inv_context(smmu_domain);
+	tlb->tlb_flush_all(smmu_domain);
 	phys_post_tlbiall = arm_smmu_iova_to_phys_hard(domain, iova);
 
 	if (phys != phys_post_tlbiall) {
@@ -1514,6 +1597,9 @@
 	/* SCTLR */
 	reg = SCTLR_CFCFG | SCTLR_CFIE | SCTLR_CFRE | SCTLR_AFE | SCTLR_TRE;
 
+	/* Ensure bypass transactions are Non-shareable */
+	reg |= SCTLR_SHCFG_NSH << SCTLR_SHCFG_SHIFT;
+
 	if (smmu_domain->attributes & (1 << DOMAIN_ATTR_CB_STALL_DISABLE)) {
 		reg &= ~SCTLR_CFCFG;
 		reg |= SCTLR_HUPCF;
@@ -1586,6 +1672,7 @@
 	bool is_fast = smmu_domain->attributes & (1 << DOMAIN_ATTR_FAST);
 	unsigned long quirks = 0;
 	bool dynamic;
+	const struct iommu_gather_ops *tlb;
 
 	mutex_lock(&smmu_domain->init_mutex);
 	if (smmu_domain->smmu)
@@ -1700,6 +1787,13 @@
 		quirks |= IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT;
 	if (is_iommu_pt_coherent(smmu_domain))
 		quirks |= IO_PGTABLE_QUIRK_PAGE_TABLE_COHERENT;
+	if ((quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT) &&
+		(smmu->model == QCOM_SMMUV500))
+		quirks |= IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE;
+
+	tlb = &arm_smmu_gather_ops;
+	if (smmu->options & ARM_SMMU_OPT_MMU500_ERRATA1)
+		tlb = &qsmmuv500_errata1_smmu_gather_ops;
 
 	ret = arm_smmu_alloc_cb(domain, smmu, dev);
 	if (ret < 0)
@@ -1718,7 +1812,7 @@
 		.pgsize_bitmap	= smmu->pgsize_bitmap,
 		.ias		= ias,
 		.oas		= oas,
-		.tlb		= &arm_smmu_gather_ops,
+		.tlb		= tlb,
 		.iommu_dev	= smmu->dev,
 	};
 
@@ -1754,6 +1848,8 @@
 		arm_smmu_init_context_bank(smmu_domain,
 						&smmu_domain->pgtbl_cfg);
 
+		arm_smmu_arch_init_context_bank(smmu_domain, dev);
+
 		/*
 		 * Request context fault interrupt. Do this last to avoid the
 		 * handler seeing a half-initialised domain state.
@@ -1909,7 +2005,8 @@
 	struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
 	u32 reg = (s2cr->type & S2CR_TYPE_MASK) << S2CR_TYPE_SHIFT |
 		  (s2cr->cbndx & S2CR_CBNDX_MASK) << S2CR_CBNDX_SHIFT |
-		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT;
+		  (s2cr->privcfg & S2CR_PRIVCFG_MASK) << S2CR_PRIVCFG_SHIFT |
+		  S2CR_SHCFG_NSH << S2CR_SHCFG_SHIFT;
 
 	writel_relaxed(reg, ARM_SMMU_GR0(smmu) + ARM_SMMU_GR0_S2CR(idx));
 }
@@ -2117,13 +2214,13 @@
 		return;
 	}
 
-	arm_smmu_domain_remove_master(smmu_domain, fwspec);
+	if (atomic_domain)
+		arm_smmu_power_on_atomic(smmu->pwr);
+	else
+		arm_smmu_power_on(smmu->pwr);
 
-	/* Remove additional vote for atomic power */
-	if (atomic_domain) {
-		WARN_ON(arm_smmu_power_on_atomic(smmu->pwr));
-		arm_smmu_power_off(smmu->pwr);
-	}
+	arm_smmu_domain_remove_master(smmu_domain, fwspec);
+	arm_smmu_power_off(smmu->pwr);
 }
 
 static int arm_smmu_assign_table(struct arm_smmu_domain *smmu_domain)
@@ -2372,9 +2469,7 @@
 	if (!ops)
 		return -ENODEV;
 
-	ret = arm_smmu_domain_power_on(domain, smmu_domain->smmu);
-	if (ret)
-		return ret;
+	arm_smmu_secure_domain_lock(smmu_domain);
 
 	__saved_iova_start = iova;
 	idx_start = idx_end = 0;
@@ -2413,7 +2508,7 @@
 		arm_smmu_unmap(domain, __saved_iova_start, size_to_unmap);
 		iova = __saved_iova_start;
 	}
-	arm_smmu_domain_power_off(domain, smmu_domain->smmu);
+	arm_smmu_secure_domain_unlock(smmu_domain);
 	return iova - __saved_iova_start;
 }
 
@@ -2490,6 +2585,10 @@
 	phys_addr_t ret = 0;
 	unsigned long flags;
 	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+
+	if (smmu->options & ARM_SMMU_OPT_DISABLE_ATOS)
+		return 0;
 
 	if (smmu_domain->smmu->arch_ops &&
 	    smmu_domain->smmu->arch_ops->iova_to_phys_hard) {
@@ -2659,13 +2758,20 @@
 		group = smmu->s2crs[idx].group;
 	}
 
-	if (group)
-		return group;
+	if (!group) {
+		if (dev_is_pci(dev))
+			group = pci_device_group(dev);
+		else
+			group = generic_device_group(dev);
 
-	if (dev_is_pci(dev))
-		group = pci_device_group(dev);
-	else
-		group = generic_device_group(dev);
+		if (IS_ERR(group))
+			return NULL;
+	}
+
+	if (arm_smmu_arch_device_group(dev, group)) {
+		iommu_group_put(group);
+		return ERR_PTR(-EINVAL);
+	}
 
 	return group;
 }
@@ -2760,6 +2866,11 @@
 					& (1 << DOMAIN_ATTR_FAST));
 		ret = 0;
 		break;
+	case DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR:
+		*((int *)data) = !!(smmu_domain->attributes
+			& (1 << DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR));
+		ret = 0;
+		break;
 	case DOMAIN_ATTR_USE_UPSTREAM_HINT:
 		*((int *)data) = !!(smmu_domain->attributes &
 				   (1 << DOMAIN_ATTR_USE_UPSTREAM_HINT));
@@ -2788,6 +2899,10 @@
 			& (1 << DOMAIN_ATTR_CB_STALL_DISABLE));
 		ret = 0;
 		break;
+	case DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN:
+		*((int *)data) = smmu_domain->qsmmuv500_errata2_min_align;
+		ret = 0;
+		break;
 	default:
 		ret = -ENODEV;
 		break;
@@ -2912,11 +3027,28 @@
 		}
 		smmu_domain->secure_vmid = *((int *)data);
 		break;
-	case DOMAIN_ATTR_FAST:
+	case DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR:
 		if (*((int *)data))
-			smmu_domain->attributes |= 1 << DOMAIN_ATTR_FAST;
+			smmu_domain->attributes |=
+				1 << DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR;
 		ret = 0;
 		break;
+	/*
+	 * fast_smmu_unmap_page() and fast_smmu_alloc_iova() both
+	 * expect that the bus/clock/regulator are already on. Thus also
+	 * force DOMAIN_ATTR_ATOMIC to bet set.
+	 */
+	case DOMAIN_ATTR_FAST:
+	{
+		int fast = *((int *)data);
+
+		if (fast) {
+			smmu_domain->attributes |= 1 << DOMAIN_ATTR_FAST;
+			smmu_domain->attributes |= 1 << DOMAIN_ATTR_ATOMIC;
+		}
+		ret = 0;
+		break;
+	}
 	case DOMAIN_ATTR_USE_UPSTREAM_HINT:
 		/* can't be changed while attached */
 		if (smmu_domain->smmu != NULL) {
@@ -3060,68 +3192,12 @@
 	arm_smmu_power_off(smmu->pwr);
 }
 
-static unsigned long arm_smmu_reg_read(struct iommu_domain *domain,
-				       unsigned long offset)
-{
-	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
-	void __iomem *cb_base;
-	unsigned long val;
-
-	if (offset >= SZ_4K) {
-		pr_err("Invalid offset: 0x%lx\n", offset);
-		return 0;
-	}
-
-	smmu = smmu_domain->smmu;
-	if (!smmu) {
-		WARN(1, "Can't read registers of a detached domain\n");
-		val = 0;
-		return val;
-	}
-
-	if (arm_smmu_power_on(smmu->pwr))
-		return 0;
-
-	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
-	val = readl_relaxed(cb_base + offset);
-
-	arm_smmu_power_off(smmu->pwr);
-	return val;
-}
-
-static void arm_smmu_reg_write(struct iommu_domain *domain,
-			       unsigned long offset, unsigned long val)
-{
-	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
-	struct arm_smmu_device *smmu;
-	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
-	void __iomem *cb_base;
-
-	if (offset >= SZ_4K) {
-		pr_err("Invalid offset: 0x%lx\n", offset);
-		return;
-	}
-
-	smmu = smmu_domain->smmu;
-	if (!smmu) {
-		WARN(1, "Can't read registers of a detached domain\n");
-		return;
-	}
-
-	if (arm_smmu_power_on(smmu->pwr))
-		return;
-
-	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
-	writel_relaxed(val, cb_base + offset);
-
-	arm_smmu_power_off(smmu->pwr);
-}
-
 static void arm_smmu_tlbi_domain(struct iommu_domain *domain)
 {
-	arm_smmu_tlb_inv_context(to_smmu_domain(domain));
+	struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
+	const struct iommu_gather_ops *tlb = smmu_domain->pgtbl_cfg.tlb;
+
+	tlb->tlb_flush_all(smmu_domain);
 }
 
 static int arm_smmu_enable_config_clocks(struct iommu_domain *domain)
@@ -3157,8 +3233,6 @@
 	.of_xlate		= arm_smmu_of_xlate,
 	.pgsize_bitmap		= -1UL, /* Restricted during device attach */
 	.trigger_fault		= arm_smmu_trigger_fault,
-	.reg_read		= arm_smmu_reg_read,
-	.reg_write		= arm_smmu_reg_write,
 	.tlbi_domain		= arm_smmu_tlbi_domain,
 	.enable_config_clocks	= arm_smmu_enable_config_clocks,
 	.disable_config_clocks	= arm_smmu_disable_config_clocks,
@@ -3265,11 +3339,6 @@
 	u32 sctlr, sctlr_orig, fsr;
 	void __iomem *cb_base;
 
-	if (smmu->model == QCOM_SMMUV2) {
-		dev_err(smmu->dev, "ATOS support is disabled\n");
-		return 0;
-	}
-
 	ret = arm_smmu_power_on(smmu_domain->smmu->pwr);
 	if (ret)
 		return ret;
@@ -3394,6 +3463,10 @@
 	if (smmu->features & ARM_SMMU_FEAT_VMID16)
 		reg |= sCR0_VMID16EN;
 
+	/* Force bypass transaction to be Non-Shareable & not io-coherent */
+	reg &= ~(sCR0_SHCFG_MASK << sCR0_SHCFG_SHIFT);
+	reg |= sCR0_SHCFG_NSH << sCR0_SHCFG_SHIFT;
+
 	/* Push the button */
 	__arm_smmu_tlb_sync(smmu);
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
@@ -3942,24 +4015,6 @@
 	return 0;
 }
 
-static int arm_smmu_arch_init(struct arm_smmu_device *smmu)
-{
-	if (!smmu->arch_ops)
-		return 0;
-	if (!smmu->arch_ops->init)
-		return 0;
-	return smmu->arch_ops->init(smmu);
-}
-
-static void arm_smmu_arch_device_reset(struct arm_smmu_device *smmu)
-{
-	if (!smmu->arch_ops)
-		return;
-	if (!smmu->arch_ops->device_reset)
-		return;
-	return smmu->arch_ops->device_reset(smmu);
-}
-
 struct arm_smmu_match_data {
 	enum arm_smmu_arch_version version;
 	enum arm_smmu_implementation model;
@@ -4218,16 +4273,20 @@
 {
 	static bool registered;
 	int ret = 0;
+	ktime_t cur;
 
 	if (registered)
 		return 0;
 
+	cur = ktime_get();
 	ret = platform_driver_register(&qsmmuv500_tbu_driver);
 	if (ret)
 		return ret;
 
 	ret = platform_driver_register(&arm_smmu_driver);
 	registered = !ret;
+	trace_smmu_init(ktime_us_delta(ktime_get(), cur));
+
 	return ret;
 }
 
@@ -4282,12 +4341,29 @@
 #define DEBUG_PAR_PA_SHIFT		12
 #define DEBUG_PAR_FAULT_VAL		0x1
 
-#define TBU_DBG_TIMEOUT_US		30000
+#define TBU_DBG_TIMEOUT_US		100
+
+#define QSMMUV500_ACTLR_DEEP_PREFETCH_MASK	0x3
+#define QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT	0x8
+
+
+struct actlr_setting {
+	struct arm_smmu_smr smr;
+	u32 actlr;
+};
 
 struct qsmmuv500_archdata {
 	struct list_head		tbus;
 	void __iomem			*tcu_base;
 	u32				version;
+
+	struct actlr_setting		*actlrs;
+	u32				actlr_tbl_size;
+
+	struct arm_smmu_smr		*errata1_clients;
+	u32				num_errata1_clients;
+	remote_spinlock_t		errata1_lock;
+	ktime_t				last_tlbi_ktime;
 };
 #define get_qsmmuv500_archdata(smmu)				\
 	((struct qsmmuv500_archdata *)(smmu->archdata))
@@ -4308,11 +4384,137 @@
 	u32				halt_count;
 };
 
-static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu)
+struct qsmmuv500_group_iommudata {
+	bool has_actlr;
+	u32 actlr;
+};
+#define to_qsmmuv500_group_iommudata(group)				\
+	((struct qsmmuv500_group_iommudata *)				\
+		(iommu_group_get_iommudata(group)))
+
+
+static bool arm_smmu_fwspec_match_smr(struct iommu_fwspec *fwspec,
+				      struct arm_smmu_smr *smr)
+{
+	struct arm_smmu_smr *smr2;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+	int i, idx;
+
+	for_each_cfg_sme(fwspec, i, idx) {
+		smr2 = &smmu->smrs[idx];
+		/* Continue if table entry does not match */
+		if ((smr->id ^ smr2->id) & ~(smr->mask | smr2->mask))
+			continue;
+		return true;
+	}
+	return false;
+}
+
+#define ERRATA1_REMOTE_SPINLOCK       "S:6"
+#define ERRATA1_TLBI_INTERVAL_US		10
+static bool
+qsmmuv500_errata1_required(struct arm_smmu_domain *smmu_domain,
+				 struct qsmmuv500_archdata *data)
+{
+	bool ret = false;
+	int j;
+	struct arm_smmu_smr *smr;
+	struct iommu_fwspec *fwspec;
+
+	if (smmu_domain->qsmmuv500_errata1_init)
+		return smmu_domain->qsmmuv500_errata1_client;
+
+	fwspec = smmu_domain->dev->iommu_fwspec;
+	for (j = 0; j < data->num_errata1_clients; j++) {
+		smr = &data->errata1_clients[j];
+		if (arm_smmu_fwspec_match_smr(fwspec, smr)) {
+			ret = true;
+			break;
+		}
+	}
+
+	smmu_domain->qsmmuv500_errata1_init = true;
+	smmu_domain->qsmmuv500_errata1_client = ret;
+	return ret;
+}
+
+static void __qsmmuv500_errata1_tlbiall(struct arm_smmu_domain *smmu_domain)
+{
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct device *dev = smmu_domain->dev;
+	struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
+	void __iomem *base;
+	ktime_t cur;
+	u32 val;
+
+	base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
+	writel_relaxed(0, base + ARM_SMMU_CB_S1_TLBIALL);
+	writel_relaxed(0, base + ARM_SMMU_CB_TLBSYNC);
+	if (readl_poll_timeout_atomic(base + ARM_SMMU_CB_TLBSTATUS, val,
+				      !(val & TLBSTATUS_SACTIVE), 0, 100)) {
+		cur = ktime_get();
+		trace_errata_throttle_start(dev, 0);
+
+		msm_bus_noc_throttle_wa(true);
+		if (readl_poll_timeout_atomic(base + ARM_SMMU_CB_TLBSTATUS, val,
+				      !(val & TLBSTATUS_SACTIVE), 0, 10000)) {
+			dev_err(smmu->dev, "ERRATA1 TLBSYNC timeout");
+			trace_errata_failed(dev, 0);
+		}
+
+		msm_bus_noc_throttle_wa(false);
+
+		trace_errata_throttle_end(
+				dev, ktime_us_delta(ktime_get(), cur));
+	}
+}
+
+/* Must be called with clocks/regulators enabled */
+static void qsmmuv500_errata1_tlb_inv_context(void *cookie)
+{
+	struct arm_smmu_domain *smmu_domain = cookie;
+	struct device *dev = smmu_domain->dev;
+	struct qsmmuv500_archdata *data =
+			get_qsmmuv500_archdata(smmu_domain->smmu);
+	ktime_t cur;
+	unsigned long flags;
+	bool errata;
+
+	cur = ktime_get();
+	trace_errata_tlbi_start(dev, 0);
+
+	errata = qsmmuv500_errata1_required(smmu_domain, data);
+	remote_spin_lock_irqsave(&data->errata1_lock, flags);
+	if (errata) {
+		s64 delta;
+
+		delta = ktime_us_delta(ktime_get(), data->last_tlbi_ktime);
+		if (delta < ERRATA1_TLBI_INTERVAL_US)
+			udelay(ERRATA1_TLBI_INTERVAL_US - delta);
+
+		__qsmmuv500_errata1_tlbiall(smmu_domain);
+
+		data->last_tlbi_ktime = ktime_get();
+	} else {
+		__qsmmuv500_errata1_tlbiall(smmu_domain);
+	}
+	remote_spin_unlock_irqrestore(&data->errata1_lock, flags);
+
+	trace_errata_tlbi_end(dev, ktime_us_delta(ktime_get(), cur));
+}
+
+static struct iommu_gather_ops qsmmuv500_errata1_smmu_gather_ops = {
+	.tlb_flush_all	= qsmmuv500_errata1_tlb_inv_context,
+	.alloc_pages_exact = arm_smmu_alloc_pages_exact,
+	.free_pages_exact = arm_smmu_free_pages_exact,
+};
+
+static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu,
+				struct arm_smmu_domain *smmu_domain)
 {
 	unsigned long flags;
-	u32 val;
-	void __iomem *base;
+	u32 halt, fsr, sctlr_orig, sctlr, status;
+	void __iomem *base, *cb_base;
 
 	spin_lock_irqsave(&tbu->halt_lock, flags);
 	if (tbu->halt_count) {
@@ -4321,19 +4523,49 @@
 		return 0;
 	}
 
+	cb_base = ARM_SMMU_CB_BASE(smmu_domain->smmu) +
+		ARM_SMMU_CB(smmu_domain->smmu, smmu_domain->cfg.cbndx);
 	base = tbu->base;
-	val = readl_relaxed(base + DEBUG_SID_HALT_REG);
-	val |= DEBUG_SID_HALT_VAL;
-	writel_relaxed(val, base + DEBUG_SID_HALT_REG);
+	halt = readl_relaxed(base + DEBUG_SID_HALT_REG);
+	halt |= DEBUG_SID_HALT_VAL;
+	writel_relaxed(halt, base + DEBUG_SID_HALT_REG);
 
-	if (readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG,
-					val, (val & DEBUG_SR_HALT_ACK_VAL),
-					0, TBU_DBG_TIMEOUT_US)) {
+	if (!readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status,
+					(status & DEBUG_SR_HALT_ACK_VAL),
+					0, TBU_DBG_TIMEOUT_US))
+		goto out;
+
+	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
+	if (!(fsr & FSR_FAULT)) {
 		dev_err(tbu->dev, "Couldn't halt TBU!\n");
 		spin_unlock_irqrestore(&tbu->halt_lock, flags);
 		return -ETIMEDOUT;
 	}
 
+	/*
+	 * We are in a fault; Our request to halt the bus will not complete
+	 * until transactions in front of us (such as the fault itself) have
+	 * completed. Disable iommu faults and terminate any existing
+	 * transactions.
+	 */
+	sctlr_orig = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
+	sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
+	writel_relaxed(sctlr, cb_base + ARM_SMMU_CB_SCTLR);
+
+	writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR);
+	writel_relaxed(RESUME_TERMINATE, cb_base + ARM_SMMU_CB_RESUME);
+
+	if (readl_poll_timeout_atomic(base + DEBUG_SR_HALT_ACK_REG, status,
+					(status & DEBUG_SR_HALT_ACK_VAL),
+					0, TBU_DBG_TIMEOUT_US)) {
+		dev_err(tbu->dev, "Couldn't halt TBU from fault context!\n");
+		writel_relaxed(sctlr_orig, cb_base + ARM_SMMU_CB_SCTLR);
+		spin_unlock_irqrestore(&tbu->halt_lock, flags);
+		return -ETIMEDOUT;
+	}
+
+	writel_relaxed(sctlr_orig, cb_base + ARM_SMMU_CB_SCTLR);
+out:
 	tbu->halt_count = 1;
 	spin_unlock_irqrestore(&tbu->halt_lock, flags);
 	return 0;
@@ -4434,6 +4666,14 @@
 	void __iomem *cb_base;
 	u32 sctlr_orig, sctlr;
 	int needs_redo = 0;
+	ktime_t timeout;
+
+	/* only 36 bit iova is supported */
+	if (iova >= (1ULL << 36)) {
+		dev_err_ratelimited(smmu->dev, "ECATS: address too large: %pad\n",
+					&iova);
+		return 0;
+	}
 
 	cb_base = ARM_SMMU_CB_BASE(smmu) + ARM_SMMU_CB(smmu, cfg->cbndx);
 	tbu = qsmmuv500_find_tbu(smmu, sid);
@@ -4444,35 +4684,23 @@
 	if (ret)
 		return 0;
 
-	/*
-	 * Disable client transactions & wait for existing operations to
-	 * complete.
-	 */
-	ret = qsmmuv500_tbu_halt(tbu);
+	ret = qsmmuv500_tbu_halt(tbu, smmu_domain);
 	if (ret)
 		goto out_power_off;
 
+	/*
+	 * ECATS can trigger the fault interrupt, so disable it temporarily
+	 * and check for an interrupt manually.
+	 */
+	sctlr_orig = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
+	sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
+	writel_relaxed(sctlr, cb_base + ARM_SMMU_CB_SCTLR);
+
 	/* Only one concurrent atos operation */
 	ret = qsmmuv500_ecats_lock(smmu_domain, tbu, &flags);
 	if (ret)
 		goto out_resume;
 
-	/*
-	 * We can be called from an interrupt handler with FSR already set
-	 * so terminate the faulting transaction prior to starting ecats.
-	 * No new racing faults can occur since we in the halted state.
-	 * ECATS can trigger the fault interrupt, so disable it temporarily
-	 * and check for an interrupt manually.
-	 */
-	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
-	if (fsr & FSR_FAULT) {
-		writel_relaxed(fsr, cb_base + ARM_SMMU_CB_FSR);
-		writel_relaxed(RESUME_TERMINATE, cb_base + ARM_SMMU_CB_RESUME);
-	}
-	sctlr_orig = readl_relaxed(cb_base + ARM_SMMU_CB_SCTLR);
-	sctlr = sctlr_orig & ~(SCTLR_CFCFG | SCTLR_CFIE);
-	writel_relaxed(sctlr, cb_base + ARM_SMMU_CB_SCTLR);
-
 redo:
 	/* Set address and stream-id */
 	val = readq_relaxed(tbu->base + DEBUG_SID_HALT_REG);
@@ -4491,16 +4719,26 @@
 	writeq_relaxed(val, tbu->base + DEBUG_TXN_TRIGG_REG);
 
 	ret = 0;
-	if (readl_poll_timeout_atomic(tbu->base + DEBUG_SR_HALT_ACK_REG,
-				val, !(val & DEBUG_SR_ECATS_RUNNING_VAL),
-				0, TBU_DBG_TIMEOUT_US)) {
-		dev_err(tbu->dev, "ECATS translation timed out!\n");
+	//based on readx_poll_timeout_atomic
+	timeout = ktime_add_us(ktime_get(), TBU_DBG_TIMEOUT_US);
+	for (;;) {
+		val = readl_relaxed(tbu->base + DEBUG_SR_HALT_ACK_REG);
+		if (!(val & DEBUG_SR_ECATS_RUNNING_VAL))
+			break;
+		val = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
+		if (val & FSR_FAULT)
+			break;
+		if (ktime_compare(ktime_get(), timeout) > 0) {
+			dev_err(tbu->dev, "ECATS translation timed out!\n");
+			ret = -ETIMEDOUT;
+			break;
+		}
 	}
 
 	fsr = readl_relaxed(cb_base + ARM_SMMU_CB_FSR);
 	if (fsr & FSR_FAULT) {
 		dev_err(tbu->dev, "ECATS generated a fault interrupt! FSR = %llx\n",
-			val);
+			fsr);
 		ret = -EINVAL;
 
 		writel_relaxed(val, cb_base + ARM_SMMU_CB_FSR);
@@ -4561,6 +4799,83 @@
 	return qsmmuv500_iova_to_phys(domain, iova, sid);
 }
 
+static void qsmmuv500_release_group_iommudata(void *data)
+{
+	kfree(data);
+}
+
+/* If a device has a valid actlr, it must match */
+static int qsmmuv500_device_group(struct device *dev,
+				struct iommu_group *group)
+{
+	struct iommu_fwspec *fwspec = dev->iommu_fwspec;
+	struct arm_smmu_device *smmu = fwspec_smmu(fwspec);
+	struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
+	struct qsmmuv500_group_iommudata *iommudata;
+	u32 actlr, i;
+	struct arm_smmu_smr *smr;
+
+	iommudata = to_qsmmuv500_group_iommudata(group);
+	if (!iommudata) {
+		iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL);
+		if (!iommudata)
+			return -ENOMEM;
+
+		iommu_group_set_iommudata(group, iommudata,
+				qsmmuv500_release_group_iommudata);
+	}
+
+	for (i = 0; i < data->actlr_tbl_size; i++) {
+		smr = &data->actlrs[i].smr;
+		actlr = data->actlrs[i].actlr;
+
+		if (!arm_smmu_fwspec_match_smr(fwspec, smr))
+			continue;
+
+		if (!iommudata->has_actlr) {
+			iommudata->actlr = actlr;
+			iommudata->has_actlr = true;
+		} else if (iommudata->actlr != actlr) {
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,
+				struct device *dev)
+{
+	struct arm_smmu_device *smmu = smmu_domain->smmu;
+	struct qsmmuv500_group_iommudata *iommudata =
+		to_qsmmuv500_group_iommudata(dev->iommu_group);
+	void __iomem *cb_base;
+	const struct iommu_gather_ops *tlb;
+
+	if (!iommudata->has_actlr)
+		return;
+
+	tlb = smmu_domain->pgtbl_cfg.tlb;
+	cb_base = ARM_SMMU_CB_BASE(smmu) +
+			ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx);
+
+	writel_relaxed(iommudata->actlr, cb_base + ARM_SMMU_CB_ACTLR);
+
+	/*
+	 * Prefetch only works properly if the start and end of all
+	 * buffers in the page table are aligned to 16 Kb.
+	 */
+	if ((iommudata->actlr >> QSMMUV500_ACTLR_DEEP_PREFETCH_SHIFT) &&
+			QSMMUV500_ACTLR_DEEP_PREFETCH_MASK)
+		smmu_domain->qsmmuv500_errata2_min_align = true;
+
+	/*
+	 * Flush the context bank after modifying ACTLR to ensure there
+	 * are no cache entries with stale state
+	 */
+	tlb->tlb_flush_all(smmu_domain);
+}
+
 static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
 {
 	struct arm_smmu_device *smmu = cookie;
@@ -4580,6 +4895,70 @@
 	return 0;
 }
 
+static int qsmmuv500_parse_errata1(struct arm_smmu_device *smmu)
+{
+	int len, i;
+	struct device *dev = smmu->dev;
+	struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
+	struct arm_smmu_smr *smrs;
+	const __be32 *cell;
+
+	cell = of_get_property(dev->of_node, "qcom,mmu500-errata-1", NULL);
+	if (!cell)
+		return 0;
+
+	remote_spin_lock_init(&data->errata1_lock, ERRATA1_REMOTE_SPINLOCK);
+	len = of_property_count_elems_of_size(
+			dev->of_node, "qcom,mmu500-errata-1", sizeof(u32) * 2);
+	if (len < 0)
+		return 0;
+
+	smrs = devm_kzalloc(dev, sizeof(*smrs) * len, GFP_KERNEL);
+	if (!smrs)
+		return -ENOMEM;
+
+	for (i = 0; i < len; i++) {
+		smrs[i].id = of_read_number(cell++, 1);
+		smrs[i].mask = of_read_number(cell++, 1);
+	}
+
+	data->errata1_clients = smrs;
+	data->num_errata1_clients = len;
+	return 0;
+}
+
+static int qsmmuv500_read_actlr_tbl(struct arm_smmu_device *smmu)
+{
+	int len, i;
+	struct device *dev = smmu->dev;
+	struct qsmmuv500_archdata *data = get_qsmmuv500_archdata(smmu);
+	struct actlr_setting *actlrs;
+	const __be32 *cell;
+
+	cell = of_get_property(dev->of_node, "qcom,actlr", NULL);
+	if (!cell)
+		return 0;
+
+	len = of_property_count_elems_of_size(dev->of_node, "qcom,actlr",
+						sizeof(u32) * 3);
+	if (len < 0)
+		return 0;
+
+	actlrs = devm_kzalloc(dev, sizeof(*actlrs) * len, GFP_KERNEL);
+	if (!actlrs)
+		return -ENOMEM;
+
+	for (i = 0; i < len; i++) {
+		actlrs[i].smr.id = of_read_number(cell++, 1);
+		actlrs[i].smr.mask = of_read_number(cell++, 1);
+		actlrs[i].actlr = of_read_number(cell++, 1);
+	}
+
+	data->actlrs = actlrs;
+	data->actlr_tbl_size = len;
+	return 0;
+}
+
 static int qsmmuv500_arch_init(struct arm_smmu_device *smmu)
 {
 	struct resource *res;
@@ -4587,6 +4966,8 @@
 	struct qsmmuv500_archdata *data;
 	struct platform_device *pdev;
 	int ret;
+	u32 val;
+	void __iomem *reg;
 
 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 	if (!data)
@@ -4603,6 +4984,26 @@
 	data->version = readl_relaxed(data->tcu_base + TCU_HW_VERSION_HLOS1);
 	smmu->archdata = data;
 
+	ret = qsmmuv500_parse_errata1(smmu);
+	if (ret)
+		return ret;
+
+	ret = qsmmuv500_read_actlr_tbl(smmu);
+	if (ret)
+		return ret;
+
+	reg = ARM_SMMU_GR0(smmu);
+	val = readl_relaxed(reg + ARM_SMMU_GR0_sACR);
+	val &= ~ARM_MMU500_ACR_CACHE_LOCK;
+	writel_relaxed(val, reg + ARM_SMMU_GR0_sACR);
+	val = readl_relaxed(reg + ARM_SMMU_GR0_sACR);
+	/*
+	 * Modifiying the nonsecure copy of the sACR register is only
+	 * allowed if permission is given in the secure sACR register.
+	 * Attempt to detect if we were able to update the value.
+	 */
+	WARN_ON(val & ARM_MMU500_ACR_CACHE_LOCK);
+
 	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
 	if (ret)
 		return ret;
@@ -4618,6 +5019,8 @@
 struct arm_smmu_arch_ops qsmmuv500_arch_ops = {
 	.init = qsmmuv500_arch_init,
 	.iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,
+	.init_context_bank = qsmmuv500_init_cb,
+	.device_group = qsmmuv500_device_group,
 };
 
 static const struct of_device_id qsmmuv500_tbu_of_match[] = {
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index d92a352..57ae0dd 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -30,6 +30,8 @@
 #include <linux/pci.h>
 #include <linux/scatterlist.h>
 #include <linux/vmalloc.h>
+#include <linux/arm-smmu-errata.h>
+#include <soc/qcom/secure_buffer.h>
 
 struct iommu_dma_msi_page {
 	struct list_head	list;
@@ -41,6 +43,8 @@
 	struct iova_domain	iovad;
 	struct list_head	msi_page_list;
 	spinlock_t		msi_lock;
+	u32			min_iova_align;
+	struct page		*guard_page;
 };
 
 static inline struct iova_domain *cookie_iovad(struct iommu_domain *domain)
@@ -121,6 +125,28 @@
 	}
 }
 
+static int iommu_dma_arm_smmu_errata_init(struct iommu_domain *domain)
+{
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	int vmid = VMID_HLOS;
+	int min_iova_align = 0;
+
+	iommu_domain_get_attr(domain,
+			DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
+			&min_iova_align);
+	iommu_domain_get_attr(domain, DOMAIN_ATTR_SECURE_VMID, &vmid);
+	if (vmid >= VMID_LAST || vmid < 0)
+		vmid = VMID_HLOS;
+
+	if (min_iova_align) {
+		cookie->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN;
+		cookie->guard_page = arm_smmu_errata_get_guard_page(vmid);
+		if (!cookie->guard_page)
+			return -ENOMEM;
+	}
+	return 0;
+}
+
 /**
  * iommu_dma_init_domain - Initialise a DMA mapping domain
  * @domain: IOMMU domain previously prepared by iommu_get_dma_cookie()
@@ -142,6 +168,9 @@
 	if (!iovad)
 		return -ENODEV;
 
+	if (iommu_dma_arm_smmu_errata_init(domain))
+		return -ENODEV;
+
 	/* Use the smallest supported page size for IOVA granularity */
 	order = __ffs(domain->pgsize_bitmap);
 	base_pfn = max_t(unsigned long, 1, base >> order);
@@ -202,39 +231,86 @@
 	}
 }
 
-static struct iova *__alloc_iova(struct iommu_domain *domain, size_t size,
-		dma_addr_t dma_limit)
+static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain,
+		size_t size, dma_addr_t dma_limit, struct device *dev)
 {
 	struct iova_domain *iovad = cookie_iovad(domain);
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
 	unsigned long shift = iova_shift(iovad);
-	unsigned long length = iova_align(iovad, size) >> shift;
+	unsigned long iova_len;
+	unsigned long iova = 0;
+	dma_addr_t limit;
+	unsigned long guard_len;
+	dma_addr_t ret_iova;
+
+	if (cookie->min_iova_align)
+		guard_len = ALIGN(size, cookie->min_iova_align) - size;
+	else
+		guard_len = 0;
+	iova_len = (size + guard_len) >> shift;
+	/*
+	 * Freeing non-power-of-two-sized allocations back into the IOVA caches
+	 * will come back to bite us badly, so we have to waste a bit of space
+	 * rounding up anything cacheable to make sure that can't happen. The
+	 * order of the unadjusted size will still match upon freeing.
+	 */
+	if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
+		iova_len = roundup_pow_of_two(iova_len);
 
 	if (domain->geometry.force_aperture)
 		dma_limit = min(dma_limit, domain->geometry.aperture_end);
+
 	/*
-	 * Enforce size-alignment to be safe - there could perhaps be an
-	 * attribute to control this per-device, or at least per-domain...
+	 * Ensure iova is within range specified in iommu_dma_init_domain().
+	 * This also prevents unnecessary work iterating through the entire
+	 * rb_tree.
 	 */
-	return alloc_iova(iovad, length, dma_limit >> shift, true);
+	limit = min_t(dma_addr_t, dma_limit >> shift, iovad->dma_32bit_pfn);
+	iova = alloc_iova_fast(iovad, iova_len, limit);
+
+	ret_iova = (dma_addr_t)iova << shift;
+
+	if (guard_len &&
+		iommu_map(domain, ret_iova + size,
+			page_to_phys(cookie->guard_page),
+			guard_len, ARM_SMMU_GUARD_PROT)) {
+
+		free_iova_fast(iovad, iova, iova_len);
+		return 0;
+	}
+
+	return ret_iova;
 }
 
-/* The IOVA allocator knows what we mapped, so just unmap whatever that was */
-static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr)
+static void iommu_dma_free_iova(struct iommu_domain *domain,
+		struct iommu_dma_cookie *cookie,
+		dma_addr_t iova, size_t size)
+{
+	struct iova_domain *iovad = &cookie->iovad;
+	unsigned long shift = iova_shift(iovad);
+	unsigned long guard_len;
+
+	if (cookie->min_iova_align) {
+		guard_len = ALIGN(size, cookie->min_iova_align) - size;
+		iommu_unmap(domain, iova + size, guard_len);
+	} else {
+		guard_len = 0;
+	}
+
+	free_iova_fast(iovad, iova >> shift, (size + guard_len) >> shift);
+}
+
+static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr,
+		size_t size)
 {
 	struct iova_domain *iovad = cookie_iovad(domain);
-	unsigned long shift = iova_shift(iovad);
-	unsigned long pfn = dma_addr >> shift;
-	struct iova *iova = find_iova(iovad, pfn);
-	size_t size;
+	size_t iova_off = iova_offset(iovad, dma_addr);
 
-	if (WARN_ON(!iova))
-		return;
+	dma_addr -= iova_off;
+	size = iova_align(iovad, size + iova_off);
 
-	size = iova_size(iova) << shift;
-	size -= iommu_unmap(domain, pfn << shift, size);
-	/* ...and if we can't, then something is horribly, horribly wrong */
-	WARN_ON(size > 0);
-	__free_iova(iovad, iova);
+	WARN_ON(iommu_unmap(domain, dma_addr, size) != size);
+	iommu_dma_free_iova(domain, domain->iova_cookie, dma_addr, size);
 }
 
 static void __iommu_dma_free_pages(struct page **pages, int count)
@@ -316,7 +392,7 @@
 void iommu_dma_free(struct device *dev, struct page **pages, size_t size,
 		dma_addr_t *handle)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), *handle);
+	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), *handle, size);
 	__iommu_dma_free_pages(pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
 	*handle = DMA_ERROR_CODE;
 }
@@ -344,11 +420,11 @@
 		void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = cookie_iovad(domain);
-	struct iova *iova;
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iova_domain *iovad = &cookie->iovad;
 	struct page **pages;
 	struct sg_table sgt;
-	dma_addr_t dma_addr;
+	dma_addr_t iova;
 	unsigned int count, min_size, alloc_sizes = domain->pgsize_bitmap;
 
 	*handle = DMA_ERROR_CODE;
@@ -368,11 +444,11 @@
 	if (!pages)
 		return NULL;
 
-	iova = __alloc_iova(domain, size, dev->coherent_dma_mask);
+	size = iova_align(iovad, size);
+	iova = iommu_dma_alloc_iova(domain, size, dev->coherent_dma_mask, dev);
 	if (!iova)
 		goto out_free_pages;
 
-	size = iova_align(iovad, size);
 	if (sg_alloc_table_from_pages(&sgt, pages, count, 0, size, GFP_KERNEL))
 		goto out_free_iova;
 
@@ -388,19 +464,18 @@
 		sg_miter_stop(&miter);
 	}
 
-	dma_addr = iova_dma_addr(iovad, iova);
-	if (iommu_map_sg(domain, dma_addr, sgt.sgl, sgt.orig_nents, prot)
+	if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, prot)
 			< size)
 		goto out_free_sg;
 
-	*handle = dma_addr;
+	*handle = iova;
 	sg_free_table(&sgt);
 	return pages;
 
 out_free_sg:
 	sg_free_table(&sgt);
 out_free_iova:
-	__free_iova(iovad, iova);
+	iommu_dma_free_iova(domain, cookie, iova, size);
 out_free_pages:
 	__iommu_dma_free_pages(pages, count);
 	return NULL;
@@ -434,22 +509,22 @@
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
 		size_t size, int prot)
 {
-	dma_addr_t dma_addr;
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = cookie_iovad(domain);
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iova_domain *iovad = &cookie->iovad;
 	size_t iova_off = iova_offset(iovad, phys);
-	size_t len = iova_align(iovad, size + iova_off);
-	struct iova *iova = __alloc_iova(domain, len, dma_get_mask(dev));
+	dma_addr_t iova;
 
+	size = iova_align(iovad, size + iova_off);
+	iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
 	if (!iova)
 		return DMA_ERROR_CODE;
 
-	dma_addr = iova_dma_addr(iovad, iova);
-	if (iommu_map(domain, dma_addr, phys - iova_off, len, prot)) {
-		__free_iova(iovad, iova);
+	if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
+		iommu_dma_free_iova(domain, cookie, iova, size);
 		return DMA_ERROR_CODE;
 	}
-	return dma_addr + iova_off;
+	return iova + iova_off;
 }
 
 dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
@@ -461,7 +536,7 @@
 void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
 		enum dma_data_direction dir, unsigned long attrs)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
+	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
 }
 
 /*
@@ -550,10 +625,10 @@
 		int nents, int prot)
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
-	struct iova_domain *iovad = cookie_iovad(domain);
-	struct iova *iova;
+	struct iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct iova_domain *iovad = &cookie->iovad;
 	struct scatterlist *s, *prev = NULL;
-	dma_addr_t dma_addr;
+	dma_addr_t iova;
 	size_t iova_len = 0;
 	unsigned long mask = dma_get_seg_boundary(dev);
 	int i;
@@ -597,7 +672,7 @@
 		prev = s;
 	}
 
-	iova = __alloc_iova(domain, iova_len, dma_get_mask(dev));
+	iova = iommu_dma_alloc_iova(domain, iova_len, dma_get_mask(dev), dev);
 	if (!iova)
 		goto out_restore_sg;
 
@@ -605,14 +680,13 @@
 	 * We'll leave any physical concatenation to the IOMMU driver's
 	 * implementation - it knows better than we do.
 	 */
-	dma_addr = iova_dma_addr(iovad, iova);
-	if (iommu_map_sg(domain, dma_addr, sg, nents, prot) < iova_len)
+	if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
 		goto out_free_iova;
 
-	return __finalise_sg(dev, sg, nents, dma_addr);
+	return __finalise_sg(dev, sg, nents, iova);
 
 out_free_iova:
-	__free_iova(iovad, iova);
+	iommu_dma_free_iova(domain, cookie, iova, iova_len);
 out_restore_sg:
 	__invalidate_sg(sg, nents);
 	return 0;
@@ -621,11 +695,21 @@
 void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
 		enum dma_data_direction dir, unsigned long attrs)
 {
+	dma_addr_t start, end;
+	struct scatterlist *tmp;
+	int i;
 	/*
 	 * The scatterlist segments are mapped into a single
 	 * contiguous IOVA allocation, so this is incredibly easy.
 	 */
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), sg_dma_address(sg));
+	start = sg_dma_address(sg);
+	for_each_sg(sg_next(sg), tmp, nents - 1, i) {
+		if (sg_dma_len(tmp) == 0)
+			break;
+		sg = tmp;
+	}
+	end = sg_dma_address(sg) + sg_dma_len(sg);
+	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), start, end - start);
 }
 
 dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
@@ -638,7 +722,7 @@
 void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
+	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
 }
 
 int iommu_dma_supported(struct device *dev, u64 mask)
@@ -662,7 +746,7 @@
 	struct iommu_dma_cookie *cookie = domain->iova_cookie;
 	struct iommu_dma_msi_page *msi_page;
 	struct iova_domain *iovad = &cookie->iovad;
-	struct iova *iova;
+	dma_addr_t iova;
 	int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
 
 	msi_addr &= ~(phys_addr_t)iova_mask(iovad);
@@ -674,12 +758,13 @@
 	if (!msi_page)
 		return NULL;
 
-	iova = __alloc_iova(domain, iovad->granule, dma_get_mask(dev));
+	iova = iommu_dma_alloc_iova(domain, iovad->granule, dma_get_mask(dev),
+				    dev);
 	if (!iova)
 		goto out_free_page;
 
 	msi_page->phys = msi_addr;
-	msi_page->iova = iova_dma_addr(iovad, iova);
+	msi_page->iova = iova;
 	if (iommu_map(domain, msi_page->iova, msi_addr, iovad->granule, prot))
 		goto out_free_iova;
 
@@ -688,7 +773,7 @@
 	return msi_page;
 
 out_free_iova:
-	__free_iova(iovad, iova);
+	iommu_dma_free_iova(domain, cookie, iova, iovad->granule);
 out_free_page:
 	kfree(msi_page);
 	return NULL;
diff --git a/drivers/iommu/dma-mapping-fast.c b/drivers/iommu/dma-mapping-fast.c
index b5e817b..ad7ee11 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -14,11 +14,16 @@
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping-fast.h>
 #include <linux/io-pgtable-fast.h>
+#include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <asm/cacheflush.h>
 #include <asm/dma-iommu.h>
 #include <linux/slab.h>
 #include <linux/vmalloc.h>
+#include <trace/events/iommu.h>
+
+#include <soc/qcom/secure_buffer.h>
+#include <linux/arm-smmu-errata.h>
 
 /* some redundant definitions... :( TODO: move to io-pgtable-fast.h */
 #define FAST_PAGE_SHIFT		12
@@ -152,9 +157,18 @@
 					 unsigned long attrs,
 					 size_t size)
 {
-	unsigned long bit, prev_search_start, nbits = size >> FAST_PAGE_SHIFT;
-	unsigned long align = (1 << get_order(size)) - 1;
+	unsigned long bit, prev_search_start, nbits;
+	unsigned long align;
+	unsigned long guard_len;
+	dma_addr_t iova;
 
+	if (mapping->min_iova_align)
+		guard_len = ALIGN(size, mapping->min_iova_align) - size;
+	else
+		guard_len = 0;
+
+	nbits = (size + guard_len) >> FAST_PAGE_SHIFT;
+	align = (1 << get_order(size + guard_len)) - 1;
 	bit = bitmap_find_next_zero_area(
 		mapping->bitmap, mapping->num_4k_pages, mapping->next_start,
 		nbits, align);
@@ -191,7 +205,16 @@
 		av8l_fast_clear_stale_ptes(mapping->pgtbl_pmds, skip_sync);
 	}
 
-	return (bit << FAST_PAGE_SHIFT) + mapping->base;
+	iova =  (bit << FAST_PAGE_SHIFT) + mapping->base;
+	if (guard_len &&
+		iommu_map(mapping->domain, iova + size,
+			page_to_phys(mapping->guard_page),
+			guard_len, ARM_SMMU_GUARD_PROT)) {
+
+		bitmap_clear(mapping->bitmap, bit, nbits);
+		return DMA_ERROR_CODE;
+	}
+	return iova;
 }
 
 /*
@@ -285,7 +308,17 @@
 				  dma_addr_t iova, size_t size)
 {
 	unsigned long start_bit = (iova - mapping->base) >> FAST_PAGE_SHIFT;
-	unsigned long nbits = size >> FAST_PAGE_SHIFT;
+	unsigned long nbits;
+	unsigned long guard_len;
+
+	if (mapping->min_iova_align) {
+		guard_len = ALIGN(size, mapping->min_iova_align) - size;
+		iommu_unmap(mapping->domain, iova + size, guard_len);
+	} else {
+		guard_len = 0;
+	}
+	nbits = (size + guard_len) >> FAST_PAGE_SHIFT;
+
 
 	/*
 	 * We don't invalidate TLBs on unmap.  We invalidate TLBs on map
@@ -373,6 +406,8 @@
 	fast_dmac_clean_range(mapping, pmd, pmd + nptes);
 
 	spin_unlock_irqrestore(&mapping->lock, flags);
+
+	trace_map(mapping->domain, iova, phys_to_map, len, prot);
 	return iova + offset_from_phys_to_map;
 
 fail_free_iova:
@@ -402,8 +437,10 @@
 	spin_lock_irqsave(&mapping->lock, flags);
 	av8l_fast_unmap_public(pmd, len);
 	fast_dmac_clean_range(mapping, pmd, pmd + nptes);
-	__fast_smmu_free_iova(mapping, iova, len);
+	__fast_smmu_free_iova(mapping, iova - offset, len);
 	spin_unlock_irqrestore(&mapping->lock, flags);
+
+	trace_unmap(mapping->domain, iova - offset, len, len);
 }
 
 static void fast_smmu_sync_single_for_cpu(struct device *dev,
@@ -434,7 +471,8 @@
 			    int nents, enum dma_data_direction dir,
 			    unsigned long attrs)
 {
-	return -EINVAL;
+	/* 0 indicates error */
+	return 0;
 }
 
 static void fast_smmu_unmap_sg(struct device *dev,
@@ -707,7 +745,7 @@
 
 	iommu_unmap(mapping->domain, addr - offset, len);
 	spin_lock_irqsave(&mapping->lock, flags);
-	__fast_smmu_free_iova(mapping, addr, len);
+	__fast_smmu_free_iova(mapping, addr - offset, len);
 	spin_unlock_irqrestore(&mapping->lock, flags);
 }
 
@@ -855,33 +893,49 @@
 	spin_unlock_irqrestore(&mapping->lock, flags);
 }
 
+static int fast_smmu_errata_init(struct dma_iommu_mapping *mapping)
+{
+	struct dma_fast_smmu_mapping *fast = mapping->fast;
+	int vmid = VMID_HLOS;
+	int min_iova_align = 0;
+
+	iommu_domain_get_attr(mapping->domain,
+			DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
+			&min_iova_align);
+	iommu_domain_get_attr(mapping->domain, DOMAIN_ATTR_SECURE_VMID, &vmid);
+	if (vmid >= VMID_LAST || vmid < 0)
+		vmid = VMID_HLOS;
+
+	if (min_iova_align) {
+		fast->min_iova_align = ARM_SMMU_MIN_IOVA_ALIGN;
+		fast->guard_page = arm_smmu_errata_get_guard_page(vmid);
+		if (!fast->guard_page)
+			return -ENOMEM;
+	}
+	return 0;
+}
 
 /**
- * fast_smmu_attach_device
+ * fast_smmu_init_mapping
  * @dev: valid struct device pointer
  * @mapping: io address space mapping structure (returned from
- *	fast_smmu_create_mapping)
+ *	arm_iommu_create_mapping)
  *
- * Attaches specified io address space mapping to the provided device,
- * this replaces the dma operations (dma_map_ops pointer) with the
- * IOMMU aware version. More than one client might be attached to
- * the same io address space mapping.
+ * Called the first time a device is attached to this mapping.
+ * Not for dma client use.
  */
-int fast_smmu_attach_device(struct device *dev,
+int fast_smmu_init_mapping(struct device *dev,
 			    struct dma_iommu_mapping *mapping)
 {
-	int atomic_domain = 1;
+	int err;
 	struct iommu_domain *domain = mapping->domain;
-	struct iommu_group *group;
 	struct iommu_pgtbl_info info;
 	u64 size = (u64)mapping->bits << PAGE_SHIFT;
 
-	if (mapping->base + size > (SZ_1G * 4ULL))
+	if (mapping->base + size > (SZ_1G * 4ULL)) {
+		dev_err(dev, "Iova end address too large\n");
 		return -EINVAL;
-
-	if (iommu_domain_set_attr(domain, DOMAIN_ATTR_ATOMIC,
-				  &atomic_domain))
-		return -EINVAL;
+	}
 
 	mapping->fast = __fast_smmu_create_mapping_sized(mapping->base, size);
 	if (IS_ERR(mapping->fast))
@@ -889,59 +943,50 @@
 	mapping->fast->domain = domain;
 	mapping->fast->dev = dev;
 
+	if (fast_smmu_errata_init(mapping))
+		goto release_mapping;
+
 	fast_smmu_reserve_pci_windows(dev, mapping->fast);
 
-	group = dev->iommu_group;
-	if (!group) {
-		dev_err(dev, "No iommu associated with device\n");
-		return -ENODEV;
-	}
-
-	if (iommu_get_domain_for_dev(dev)) {
-		dev_err(dev, "Device already attached to other iommu_domain\n");
-		return -EINVAL;
-	}
-
-	if (iommu_attach_group(mapping->domain, group))
-		return -EINVAL;
-
 	if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PGTBL_INFO,
 				  &info)) {
 		dev_err(dev, "Couldn't get page table info\n");
-		fast_smmu_detach_device(dev, mapping);
-		return -EINVAL;
+		err = -EINVAL;
+		goto release_mapping;
 	}
 	mapping->fast->pgtbl_pmds = info.pmds;
 
 	if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PAGE_TABLE_IS_COHERENT,
-				  &mapping->fast->is_smmu_pt_coherent))
-		return -EINVAL;
+				  &mapping->fast->is_smmu_pt_coherent)) {
+		err = -EINVAL;
+		goto release_mapping;
+	}
 
 	mapping->fast->notifier.notifier_call = fast_smmu_notify;
 	av8l_register_notify(&mapping->fast->notifier);
 
-	dev->archdata.mapping = mapping;
-	set_dma_ops(dev, &fast_smmu_dma_ops);
-
+	mapping->ops = &fast_smmu_dma_ops;
 	return 0;
+
+release_mapping:
+	kfree(mapping->fast->bitmap);
+	kfree(mapping->fast);
+	return err;
 }
-EXPORT_SYMBOL(fast_smmu_attach_device);
 
 /**
- * fast_smmu_detach_device
- * @dev: valid struct device pointer
+ * fast_smmu_release_mapping
+ * @kref: dma_iommu_mapping->kref
  *
- * Detaches the provided device from a previously attached map.
- * This voids the dma operations (dma_map_ops pointer)
+ * Cleans up the given iommu mapping.
  */
-void fast_smmu_detach_device(struct device *dev,
-			     struct dma_iommu_mapping *mapping)
+void fast_smmu_release_mapping(struct kref *kref)
 {
-	iommu_detach_group(mapping->domain, dev->iommu_group);
-	dev->archdata.mapping = NULL;
-	set_dma_ops(dev, NULL);
+	struct dma_iommu_mapping *mapping =
+		container_of(kref, struct dma_iommu_mapping, kref);
 
 	kvfree(mapping->fast->bitmap);
 	kfree(mapping->fast);
+	iommu_domain_free(mapping->domain);
+	kfree(mapping);
 }
-EXPORT_SYMBOL(fast_smmu_detach_device);
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 30808e9..c7820b3 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -542,7 +542,10 @@
 	spin_lock_irqsave(&data->lock, flags);
 	if (is_sysmmu_active(data) && data->version >= MAKE_MMU_VER(3, 3)) {
 		clk_enable(data->clk_master);
-		__sysmmu_tlb_invalidate_entry(data, iova, 1);
+		if (sysmmu_block(data)) {
+			__sysmmu_tlb_invalidate_entry(data, iova, 1);
+			sysmmu_unblock(data);
+		}
 		clk_disable(data->clk_master);
 	}
 	spin_unlock_irqrestore(&data->lock, flags);
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index dde2876..981172d 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -468,8 +468,12 @@
 		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS)
 			pte |= ARM_LPAE_PTE_NSTABLE;
 		__arm_lpae_set_pte(ptep, pte, cfg);
-	} else {
+	} else if (!iopte_leaf(pte, lvl)) {
 		cptep = iopte_deref(pte, data);
+	} else {
+		/* We require an unmap first */
+		WARN_ON(!selftest_running);
+		return -EEXIST;
 	}
 
 	/* Rinse, repeat */
@@ -1011,6 +1015,11 @@
 		reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_IRGN0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
+	else if ((cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT) &&
+		(cfg->quirks & IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE))
+		reg = (ARM_LPAE_TCR_SH_NS << ARM_LPAE_TCR_SH0_SHIFT) |
+			(ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
+			(ARM_LPAE_TCR_RGN_WBWA << ARM_LPAE_TCR_ORGN0_SHIFT);
 	else if (cfg->quirks & IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT)
 		reg = (ARM_LPAE_TCR_SH_OS << ARM_LPAE_TCR_SH0_SHIFT) |
 			(ARM_LPAE_TCR_RGN_NC << ARM_LPAE_TCR_IRGN0_SHIFT) |
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index a686ad0..b35016e 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -81,6 +81,12 @@
 	 *
 	 * IO_PGTABLE_QUIRK_PAGE_TABLE_COHERENT: Set the page table as
 	 *	coherent.
+	 *
+	 * IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE:
+	 *	Having page tables which are non coherent, but cached in a
+	 *	system cache requires SH=Non-Shareable. This applies to the
+	 *	qsmmuv500 model. For data buffers SH=Non-Shareable is not
+	 *	required.
 	 */
 	#define IO_PGTABLE_QUIRK_ARM_NS		BIT(0)
 	#define IO_PGTABLE_QUIRK_NO_PERMS	BIT(1)
@@ -88,6 +94,7 @@
 	#define IO_PGTABLE_QUIRK_ARM_MTK_4GB	BIT(3)
 	#define IO_PGTABLE_QUIRK_QCOM_USE_UPSTREAM_HINT	BIT(4)
 	#define IO_PGTABLE_QUIRK_PAGE_TABLE_COHERENT BIT(5)
+	#define IO_PGTABLE_QUIRK_QSMMUV500_NON_SHAREABLE BIT(6)
 	unsigned long			quirks;
 	unsigned long			pgsize_bitmap;
 	unsigned int			ias;
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 56eff61b..22a708e 100644
--- a/drivers/iommu/iommu-debug.c
+++ b/drivers/iommu/iommu-debug.c
@@ -102,20 +102,25 @@
 	struct iommu_debug_attachment *attach;
 	struct iommu_group *group;
 
-	group = iommu_group_get(dev);
+	group = dev->iommu_group;
 	if (!group)
 		return;
 
+	mutex_lock(&iommu_debug_attachments_lock);
+	list_for_each_entry(attach, &iommu_debug_attachments, list)
+		if ((attach->domain == domain) && (attach->group == group))
+			goto out;
+
 	attach = kzalloc(sizeof(*attach), GFP_KERNEL);
 	if (!attach)
-		return;
+		goto out;
 
 	attach->domain = domain;
 	attach->group = group;
 	INIT_LIST_HEAD(&attach->list);
 
-	mutex_lock(&iommu_debug_attachments_lock);
 	list_add(&attach->list, &iommu_debug_attachments);
+out:
 	mutex_unlock(&iommu_debug_attachments_lock);
 }
 
@@ -128,7 +133,6 @@
 		if (it->domain != domain)
 			continue;
 		list_del(&it->list);
-		iommu_group_put(it->group);
 		kfree(it);
 	}
 
@@ -161,6 +165,7 @@
 struct iommu_debug_device {
 	struct device *dev;
 	struct iommu_domain *domain;
+	struct dma_iommu_mapping *mapping;
 	u64 iova;
 	u64 phys;
 	size_t len;
@@ -1247,6 +1252,8 @@
 
 		if (arm_iommu_attach_device(dev, dma_mapping))
 			goto out_release_mapping;
+
+		ddev->mapping = dma_mapping;
 		pr_err("Attached\n");
 	} else {
 		if (!dev->archdata.mapping) {
@@ -1260,7 +1267,7 @@
 			goto out;
 		}
 		arm_iommu_detach_device(dev);
-		arm_iommu_release_mapping(dev->archdata.mapping);
+		arm_iommu_release_mapping(ddev->mapping);
 		pr_err("Detached\n");
 	}
 	retval = count;
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 83cbf20..c333a36 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -426,6 +426,7 @@
 	if (ret)
 		goto err_put_group;
 
+
 	/* Notify any listeners about change to group. */
 	blocking_notifier_call_chain(&group->notifier,
 				     IOMMU_GROUP_NOTIFY_ADD_DEVICE, dev);
@@ -1077,6 +1078,7 @@
 	domain->type = type;
 	/* Assume all sizes by default; the driver may override this later */
 	domain->pgsize_bitmap  = bus->iommu_ops->pgsize_bitmap;
+	memset(domain->name, 0, IOMMU_DOMAIN_NAME_LEN);
 
 	return domain;
 }
@@ -1105,6 +1107,11 @@
 	if (!ret) {
 		trace_attach_device_to_domain(dev);
 		iommu_debug_attach_device(domain, dev);
+
+		if (!strnlen(domain->name, IOMMU_DOMAIN_NAME_LEN)) {
+			strlcpy(domain->name, dev_name(dev),
+				IOMMU_DOMAIN_NAME_LEN);
+		}
 	}
 	return ret;
 }
@@ -1398,7 +1405,7 @@
 	if (ret)
 		iommu_unmap(domain, orig_iova, orig_size - size);
 	else
-		trace_map(orig_iova, orig_paddr, orig_size);
+		trace_map(domain, orig_iova, orig_paddr, orig_size, prot);
 
 	return ret;
 }
@@ -1451,11 +1458,23 @@
 		unmapped += unmapped_page;
 	}
 
-	trace_unmap(orig_iova, size, unmapped);
+	trace_unmap(domain, orig_iova, size, unmapped);
 	return unmapped;
 }
 EXPORT_SYMBOL_GPL(iommu_unmap);
 
+size_t iommu_map_sg(struct iommu_domain *domain,
+				  unsigned long iova, struct scatterlist *sg,
+				  unsigned int nents, int prot)
+{
+	size_t mapped;
+
+	mapped = domain->ops->map_sg(domain, iova, sg, nents, prot);
+	trace_map_sg(domain, iova, mapped, prot);
+	return mapped;
+}
+EXPORT_SYMBOL(iommu_map_sg);
+
 size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
 			 struct scatterlist *sg, unsigned int nents, int prot)
 {
@@ -1519,8 +1538,49 @@
 }
 EXPORT_SYMBOL_GPL(iommu_domain_window_disable);
 
-struct dentry *iommu_debugfs_top;
+/**
+ * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
+ * @domain: the iommu domain where the fault has happened
+ * @dev: the device where the fault has happened
+ * @iova: the faulting address
+ * @flags: mmu fault flags (e.g. IOMMU_FAULT_READ/IOMMU_FAULT_WRITE/...)
+ *
+ * This function should be called by the low-level IOMMU implementations
+ * whenever IOMMU faults happen, to allow high-level users, that are
+ * interested in such events, to know about them.
+ *
+ * This event may be useful for several possible use cases:
+ * - mere logging of the event
+ * - dynamic TLB/PTE loading
+ * - if restarting of the faulting device is required
+ *
+ * Returns 0 on success and an appropriate error code otherwise (if dynamic
+ * PTE/TLB loading will one day be supported, implementations will be able
+ * to tell whether it succeeded or not according to this return value).
+ *
+ * Specifically, -ENOSYS is returned if a fault handler isn't installed
+ * (though fault handlers can also return -ENOSYS, in case they want to
+ * elicit the default behavior of the IOMMU drivers).
+ */
+int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
+		       unsigned long iova, int flags)
+{
+	int ret = -ENOSYS;
 
+	/*
+	 * if upper layers showed interest and installed a fault handler,
+	 * invoke it.
+	 */
+	if (domain->handler)
+		ret = domain->handler(domain, dev, iova, flags,
+						domain->handler_token);
+
+	trace_io_page_fault(dev, iova, flags);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(report_iommu_fault);
+
+struct dentry *iommu_debugfs_top;
 static int __init iommu_init(void)
 {
 	iommu_group_kset = kset_create_and_add("iommu_groups",
@@ -1617,30 +1677,6 @@
 		domain->ops->trigger_fault(domain, flags);
 }
 
-/**
- * iommu_reg_read() - read an IOMMU register
- *
- * Reads the IOMMU register at the given offset.
- */
-unsigned long iommu_reg_read(struct iommu_domain *domain, unsigned long offset)
-{
-	if (domain->ops->reg_read)
-		return domain->ops->reg_read(domain, offset);
-	return 0;
-}
-
-/**
- * iommu_reg_write() - write an IOMMU register
- *
- * Writes the given value to the IOMMU register at the given offset.
- */
-void iommu_reg_write(struct iommu_domain *domain, unsigned long offset,
-		     unsigned long val)
-{
-	if (domain->ops->reg_write)
-		domain->ops->reg_write(domain, offset, val);
-}
-
 void iommu_get_dm_regions(struct device *dev, struct list_head *list)
 {
 	const struct iommu_ops *ops = dev->bus->iommu_ops;
diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c
index e23001b..5c88ba7 100644
--- a/drivers/iommu/iova.c
+++ b/drivers/iommu/iova.c
@@ -56,13 +56,13 @@
 static struct rb_node *
 __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
 {
-	if ((*limit_pfn != iovad->dma_32bit_pfn) ||
+	if ((*limit_pfn > iovad->dma_32bit_pfn) ||
 		(iovad->cached32_node == NULL))
 		return rb_last(&iovad->rbroot);
 	else {
 		struct rb_node *prev_node = rb_prev(iovad->cached32_node);
 		struct iova *curr_iova =
-			container_of(iovad->cached32_node, struct iova, node);
+			rb_entry(iovad->cached32_node, struct iova, node);
 		*limit_pfn = curr_iova->pfn_lo - 1;
 		return prev_node;
 	}
@@ -86,11 +86,11 @@
 	if (!iovad->cached32_node)
 		return;
 	curr = iovad->cached32_node;
-	cached_iova = container_of(curr, struct iova, node);
+	cached_iova = rb_entry(curr, struct iova, node);
 
 	if (free->pfn_lo >= cached_iova->pfn_lo) {
 		struct rb_node *node = rb_next(&free->node);
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		/* only cache if it's below 32bit pfn */
 		if (node && iova->pfn_lo < iovad->dma_32bit_pfn)
@@ -100,6 +100,34 @@
 	}
 }
 
+/* Insert the iova into domain rbtree by holding writer lock */
+static void
+iova_insert_rbtree(struct rb_root *root, struct iova *iova,
+		   struct rb_node *start)
+{
+	struct rb_node **new, *parent = NULL;
+
+	new = (start) ? &start : &(root->rb_node);
+	/* Figure out where to put new node */
+	while (*new) {
+		struct iova *this = rb_entry(*new, struct iova, node);
+
+		parent = *new;
+
+		if (iova->pfn_lo < this->pfn_lo)
+			new = &((*new)->rb_left);
+		else if (iova->pfn_lo > this->pfn_lo)
+			new = &((*new)->rb_right);
+		else {
+			WARN_ON(1); /* this should not happen */
+			return;
+		}
+	}
+	/* Add new node and rebalance tree. */
+	rb_link_node(&iova->node, parent, new);
+	rb_insert_color(&iova->node, root);
+}
+
 /*
  * Computes the padding size required, to make the start address
  * naturally aligned on the power-of-two order of its size
@@ -125,7 +153,7 @@
 	curr = __get_cached_rbnode(iovad, &limit_pfn);
 	prev = curr;
 	while (curr) {
-		struct iova *curr_iova = container_of(curr, struct iova, node);
+		struct iova *curr_iova = rb_entry(curr, struct iova, node);
 
 		if (limit_pfn < curr_iova->pfn_lo)
 			goto move_left;
@@ -138,7 +166,7 @@
 				break;	/* found a free slot */
 		}
 adjust_limit_pfn:
-		limit_pfn = curr_iova->pfn_lo - 1;
+		limit_pfn = curr_iova->pfn_lo ? (curr_iova->pfn_lo - 1) : 0;
 move_left:
 		prev = curr;
 		curr = rb_prev(curr);
@@ -157,36 +185,8 @@
 	new->pfn_lo = limit_pfn - (size + pad_size) + 1;
 	new->pfn_hi = new->pfn_lo + size - 1;
 
-	/* Insert the new_iova into domain rbtree by holding writer lock */
-	/* Add new node and rebalance tree. */
-	{
-		struct rb_node **entry, *parent = NULL;
-
-		/* If we have 'prev', it's a valid place to start the
-		   insertion. Otherwise, start from the root. */
-		if (prev)
-			entry = &prev;
-		else
-			entry = &iovad->rbroot.rb_node;
-
-		/* Figure out where to put new node */
-		while (*entry) {
-			struct iova *this = container_of(*entry,
-							struct iova, node);
-			parent = *entry;
-
-			if (new->pfn_lo < this->pfn_lo)
-				entry = &((*entry)->rb_left);
-			else if (new->pfn_lo > this->pfn_lo)
-				entry = &((*entry)->rb_right);
-			else
-				BUG(); /* this should not happen */
-		}
-
-		/* Add new node and rebalance tree. */
-		rb_link_node(&new->node, parent, entry);
-		rb_insert_color(&new->node, &iovad->rbroot);
-	}
+	/* If we have 'prev', it's a valid place to start the insertion. */
+	iova_insert_rbtree(&iovad->rbroot, new, prev);
 	__cached_rbnode_insert_update(iovad, saved_pfn, new);
 
 	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
@@ -195,28 +195,6 @@
 	return 0;
 }
 
-static void
-iova_insert_rbtree(struct rb_root *root, struct iova *iova)
-{
-	struct rb_node **new = &(root->rb_node), *parent = NULL;
-	/* Figure out where to put new node */
-	while (*new) {
-		struct iova *this = container_of(*new, struct iova, node);
-
-		parent = *new;
-
-		if (iova->pfn_lo < this->pfn_lo)
-			new = &((*new)->rb_left);
-		else if (iova->pfn_lo > this->pfn_lo)
-			new = &((*new)->rb_right);
-		else
-			BUG(); /* this should not happen */
-	}
-	/* Add new node and rebalance tree. */
-	rb_link_node(&iova->node, parent, new);
-	rb_insert_color(&iova->node, root);
-}
-
 static struct kmem_cache *iova_cache;
 static unsigned int iova_cache_users;
 static DEFINE_MUTEX(iova_cache_mutex);
@@ -311,7 +289,7 @@
 	assert_spin_locked(&iovad->iova_rbtree_lock);
 
 	while (node) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		/* If pfn falls within iova's range, return iova */
 		if ((pfn >= iova->pfn_lo) && (pfn <= iova->pfn_hi)) {
@@ -463,7 +441,7 @@
 	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
 	node = rb_first(&iovad->rbroot);
 	while (node) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 
 		rb_erase(node, &iovad->rbroot);
 		free_iova_mem(iova);
@@ -477,7 +455,7 @@
 __is_range_overlap(struct rb_node *node,
 	unsigned long pfn_lo, unsigned long pfn_hi)
 {
-	struct iova *iova = container_of(node, struct iova, node);
+	struct iova *iova = rb_entry(node, struct iova, node);
 
 	if ((pfn_lo <= iova->pfn_hi) && (pfn_hi >= iova->pfn_lo))
 		return 1;
@@ -506,7 +484,7 @@
 
 	iova = alloc_and_init_iova(pfn_lo, pfn_hi);
 	if (iova)
-		iova_insert_rbtree(&iovad->rbroot, iova);
+		iova_insert_rbtree(&iovad->rbroot, iova, NULL);
 
 	return iova;
 }
@@ -541,7 +519,7 @@
 	spin_lock_irqsave(&iovad->iova_rbtree_lock, flags);
 	for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) {
 		if (__is_range_overlap(node, pfn_lo, pfn_hi)) {
-			iova = container_of(node, struct iova, node);
+			iova = rb_entry(node, struct iova, node);
 			__adjust_overlap_range(iova, &pfn_lo, &pfn_hi);
 			if ((pfn_lo >= iova->pfn_lo) &&
 				(pfn_hi <= iova->pfn_hi))
@@ -578,7 +556,7 @@
 
 	spin_lock_irqsave(&from->iova_rbtree_lock, flags);
 	for (node = rb_first(&from->rbroot); node; node = rb_next(node)) {
-		struct iova *iova = container_of(node, struct iova, node);
+		struct iova *iova = rb_entry(node, struct iova, node);
 		struct iova *new_iova;
 
 		new_iova = reserve_iova(to, iova->pfn_lo, iova->pfn_hi);
@@ -613,11 +591,11 @@
 	rb_erase(&iova->node, &iovad->rbroot);
 
 	if (prev) {
-		iova_insert_rbtree(&iovad->rbroot, prev);
+		iova_insert_rbtree(&iovad->rbroot, prev, NULL);
 		iova->pfn_lo = pfn_lo;
 	}
 	if (next) {
-		iova_insert_rbtree(&iovad->rbroot, next);
+		iova_insert_rbtree(&iovad->rbroot, next, NULL);
 		iova->pfn_hi = pfn_hi;
 	}
 	spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags);
diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c
index 28b26c8..0565070 100644
--- a/drivers/irqchip/irq-atmel-aic-common.c
+++ b/drivers/irqchip/irq-atmel-aic-common.c
@@ -142,9 +142,9 @@
 	struct device_node *np;
 	void __iomem *regs;
 
-	np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc");
+	np = of_find_compatible_node(NULL, NULL, "atmel,at91rm9200-rtc");
 	if (!np)
-		np = of_find_compatible_node(root, NULL,
+		np = of_find_compatible_node(NULL, NULL,
 					     "atmel,at91sam9x5-rtc");
 
 	if (!np)
@@ -196,7 +196,6 @@
 		return;
 
 	match = of_match_node(matches, root);
-	of_node_put(root);
 
 	if (match) {
 		void (*fixup)(struct device_node *) = match->data;
diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c
index 1eef56a..05bbf17 100644
--- a/drivers/irqchip/irq-crossbar.c
+++ b/drivers/irqchip/irq-crossbar.c
@@ -198,7 +198,8 @@
 
 static int __init crossbar_of_init(struct device_node *node)
 {
-	int i, size, max = 0, reserved = 0, entry;
+	int i, size, reserved = 0;
+	u32 max = 0, entry;
 	const __be32 *irqsr;
 	int ret = -ENOMEM;
 
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 779001e..01f9435 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -132,6 +132,45 @@
 }
 #endif
 
+/*
+ * gic_show_pending_irq - Shows the pending interrupts
+ * Note: Interrupts should be disabled on the cpu from which
+ * this is called to get accurate list of pending interrupts.
+ */
+void gic_show_pending_irqs(void)
+{
+	void __iomem *base;
+	u32 pending[32], enabled;
+	unsigned int j;
+
+	base = gic_data.dist_base;
+	for (j = 0; j * 32 < gic_data.irq_nr; j++) {
+		enabled = readl_relaxed(base +
+					GICD_ISENABLER + j * 4);
+		pending[j] = readl_relaxed(base +
+					GICD_ISPENDR + j * 4);
+		pending[j] &= enabled;
+		pr_err("Pending irqs[%d] %x\n", j, pending[j]);
+	}
+}
+
+/*
+ * get_gic_highpri_irq - Returns next high priority interrupt on current CPU
+ */
+unsigned int get_gic_highpri_irq(void)
+{
+	unsigned long flags;
+	unsigned int val = 0;
+
+	local_irq_save(flags);
+	val = read_gicreg(ICC_HPPIR1_EL1);
+	local_irq_restore(flags);
+
+	if (val >= 1020)
+		return 0;
+	return val;
+}
+
 static void gic_enable_redist(bool enable)
 {
 	void __iomem *rbase;
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index c0178a1..d74374f 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -1115,8 +1115,11 @@
 		gic_len = resource_size(&res);
 	}
 
-	if (mips_cm_present())
+	if (mips_cm_present()) {
 		write_gcr_gic_base(gic_base | CM_GCR_GIC_BASE_GICEN_MSK);
+		/* Ensure GIC region is enabled before trying to access it */
+		__sync();
+	}
 	gic_present = true;
 
 	__gic_init(gic_base, gic_len, cpu_vec, 0, node);
diff --git a/drivers/irqchip/qcom/pdc-sdm670.c b/drivers/irqchip/qcom/pdc-sdm670.c
index 7bd6333..21bb58e 100644
--- a/drivers/irqchip/qcom/pdc-sdm670.c
+++ b/drivers/irqchip/qcom/pdc-sdm670.c
@@ -120,13 +120,13 @@
 	{106, 653}, /* core_bi_px_gpio_132 */
 	{107, 654}, /* core_bi_px_gpio_133 */
 	{108, 655}, /* core_bi_px_gpio_145 */
+	{115, 662}, /* core_bi_px_gpio_41 */
+	{116, 663}, /* core_bi_px_gpio_89 */
+	{117, 664}, /* core_bi_px_gpio_31 */
+	{118, 665}, /* core_bi_px_gpio_49 */
 	{119, 666}, /* core_bi_px_to_mpm[2] */
 	{120, 667}, /* core_bi_px_to_mpm[3] */
 	{121, 668}, /* core_bi_px_to_mpm[4] */
-	{122, 669}, /* core_bi_px_gpio_41 */
-	{123, 670}, /* core_bi_px_gpio_89 */
-	{124, 671}, /* core_bi_px_gpio_31 */
-	{125, 95}, /* core_bi_px_gpio_49 */
 	{-1}
 };
 
diff --git a/drivers/irqchip/qcom/pdc-sdm845.c b/drivers/irqchip/qcom/pdc-sdm845.c
index 178cf1f0..2f90e10 100644
--- a/drivers/irqchip/qcom/pdc-sdm845.c
+++ b/drivers/irqchip/qcom/pdc-sdm845.c
@@ -130,10 +130,135 @@
 	{-1}
 };
 
+static struct pdc_pin sdm845_data_v2[] = {
+	{0, 512}, /* rpmh_wake */
+	{1, 513}, /* ee0_apps_hlos_spmi_periph_irq */
+	{2, 514}, /* ee1_apps_trustzone_spmi_periph_irq */
+	{3, 515}, /* secure_wdog_expired */
+	{4, 516}, /* secure_wdog_bark_irq */
+	{5, 517}, /* aop_wdog_expired_irq */
+	{6, 518}, /* qmp_usb3_lfps_rxterm_irq */
+	{7, 519}, /* qmp_usb3_lfps_rxterm_irq */
+	{8, 520}, /* eud_p0_dmse_int_mx */
+	{9, 521}, /* eud_p0_dpse_int_mx */
+	{10, 522}, /* eud_p1_dmse_int_mx */
+	{11, 523}, /* eud_p1_dpse_int_mx */
+	{12, 524}, /* eud_int_mx[1] */
+	{13, 525}, /* ssc_xpu_irq_summary */
+	{14, 526}, /* wd_bite_apps */
+	{15, 527}, /* ssc_vmidmt_irq_summary */
+	{16, 528}, /* q6ss_irq_out_apps_ipc[4] */
+	{17, 529}, /* not-connected */
+	{18, 530}, /* aoss_pmic_arb_mpu_xpu_summary_irq */
+	{19, 531}, /* apps_pdc_irq_in_19 */
+	{20, 532}, /* apps_pdc_irq_in_20 */
+	{21, 533}, /* apps_pdc_irq_in_21 */
+	{22, 534}, /* pdc_apps_epcb_timeout_summary_irq */
+	{23, 535}, /* spmi_protocol_irq */
+	{24, 536}, /* tsense0_tsense_max_min_int */
+	{25, 537}, /* tsense1_tsense_max_min_int */
+	{26, 538}, /* tsense0_upper_lower_intr */
+	{27, 539}, /* tsense1_upper_lower_intr */
+	{28, 540}, /* tsense0_critical_intr */
+	{29, 541}, /* tsense1_critical_intr */
+	{30, 542}, /* core_bi_px_gpio_1 */
+	{31, 543}, /* core_bi_px_gpio_3 */
+	{32, 544}, /* core_bi_px_gpio_5 */
+	{33, 545}, /* core_bi_px_gpio_10 */
+	{34, 546}, /* core_bi_px_gpio_11 */
+	{35, 547}, /* core_bi_px_gpio_20 */
+	{36, 548}, /* core_bi_px_gpio_22 */
+	{37, 549}, /* core_bi_px_gpio_24 */
+	{38, 550}, /* core_bi_px_gpio_26 */
+	{39, 551}, /* core_bi_px_gpio_30 */
+	{41, 553}, /* core_bi_px_gpio_32 */
+	{42, 554}, /* core_bi_px_gpio_34 */
+	{43, 555}, /* core_bi_px_gpio_36 */
+	{44, 556}, /* core_bi_px_gpio_37 */
+	{45, 557}, /* core_bi_px_gpio_38 */
+	{46, 558}, /* core_bi_px_gpio_39 */
+	{47, 559}, /* core_bi_px_gpio_40 */
+	{49, 561}, /* core_bi_px_gpio_43 */
+	{50, 562}, /* core_bi_px_gpio_44 */
+	{51, 563}, /* core_bi_px_gpio_46 */
+	{52, 564}, /* core_bi_px_gpio_48 */
+	{54, 566}, /* core_bi_px_gpio_52 */
+	{55, 567}, /* core_bi_px_gpio_53 */
+	{56, 568}, /* core_bi_px_gpio_54 */
+	{57, 569}, /* core_bi_px_gpio_56 */
+	{58, 570}, /* core_bi_px_gpio_57 */
+	{59, 571}, /* core_bi_px_gpio_58 */
+	{60, 572}, /* core_bi_px_gpio_59 */
+	{61, 573}, /* core_bi_px_gpio_60 */
+	{62, 574}, /* core_bi_px_gpio_61 */
+	{63, 575}, /* core_bi_px_gpio_62 */
+	{64, 576}, /* core_bi_px_gpio_63 */
+	{65, 577}, /* core_bi_px_gpio_64 */
+	{66, 578}, /* core_bi_px_gpio_66 */
+	{67, 579}, /* core_bi_px_gpio_68 */
+	{68, 580}, /* core_bi_px_gpio_71 */
+	{69, 581}, /* core_bi_px_gpio_73 */
+	{70, 582}, /* core_bi_px_gpio_77 */
+	{71, 583}, /* core_bi_px_gpio_78 */
+	{72, 584}, /* core_bi_px_gpio_79 */
+	{73, 585}, /* core_bi_px_gpio_80 */
+	{74, 586}, /* core_bi_px_gpio_84 */
+	{75, 587}, /* core_bi_px_gpio_85 */
+	{76, 588}, /* core_bi_px_gpio_86 */
+	{77, 589}, /* core_bi_px_gpio_88 */
+	{79, 591}, /* core_bi_px_gpio_91 */
+	{80, 592}, /* core_bi_px_gpio_92 */
+	{81, 593}, /* core_bi_px_gpio_95 */
+	{82, 594}, /* core_bi_px_gpio_96 */
+	{83, 595}, /* core_bi_px_gpio_97 */
+	{84, 596}, /* core_bi_px_gpio_101 */
+	{85, 597}, /* core_bi_px_gpio_103 */
+	{86, 598}, /* core_bi_px_gpio_104 */
+	{87, 599}, /* core_bi_px_to_mpm[6] */
+	{88, 600}, /* core_bi_px_to_mpm[0] */
+	{89, 601}, /* core_bi_px_to_mpm[1] */
+	{90, 602}, /* core_bi_px_gpio_115 */
+	{91, 603}, /* core_bi_px_gpio_116 */
+	{92, 604}, /* core_bi_px_gpio_117 */
+	{93, 605}, /* core_bi_px_gpio_118 */
+	{94, 641}, /* core_bi_px_gpio_119 */
+	{95, 642}, /* core_bi_px_gpio_120 */
+	{96, 643}, /* core_bi_px_gpio_121 */
+	{97, 644}, /* core_bi_px_gpio_122 */
+	{98, 645}, /* core_bi_px_gpio_123 */
+	{99, 646}, /* core_bi_px_gpio_124 */
+	{100, 647}, /* core_bi_px_gpio_125 */
+	{101, 648}, /* core_bi_px_to_mpm[5] */
+	{102, 649}, /* core_bi_px_gpio_127 */
+	{103, 650}, /* core_bi_px_gpio_128 */
+	{104, 651}, /* core_bi_px_gpio_129 */
+	{105, 652}, /* core_bi_px_gpio_130 */
+	{106, 653}, /* core_bi_px_gpio_132 */
+	{107, 654}, /* core_bi_px_gpio_133 */
+	{108, 655}, /* core_bi_px_gpio_145 */
+	{119, 666}, /* core_bi_px_to_mpm[2] */
+	{120, 667}, /* core_bi_px_to_mpm[3] */
+	{121, 668}, /* core_bi_px_to_mpm[4] */
+	{115, 662}, /* core_bi_px_gpio_41 */
+	{116, 663}, /* core_bi_px_gpio_89 */
+	{117, 664}, /* core_bi_px_gpio_31 */
+	{118, 665}, /* core_bi_px_gpio_49 */
+	{-1}
+};
+
 static int __init qcom_pdc_gic_init(struct device_node *node,
 		struct device_node *parent)
 {
+	pr_info("PDC SDM845 v1 initialized\n");
 	return qcom_pdc_init(node, parent, sdm845_data);
 }
 
+static int __init qcom_pdc_v2_gic_init(struct device_node *node,
+		struct device_node *parent)
+{
+	pr_info("PDC SDM845 v2 initialized\n");
+	return qcom_pdc_init(node, parent, sdm845_data_v2);
+}
+
 IRQCHIP_DECLARE(pdc_sdm845, "qcom,pdc-sdm845", qcom_pdc_gic_init);
+IRQCHIP_DECLARE(pdc_sdm845_v2, "qcom,pdc-sdm845-v2", qcom_pdc_v2_gic_init);
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
index bf3fbd0..64b5864 100644
--- a/drivers/isdn/i4l/isdn_ppp.c
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -828,7 +828,6 @@
 	isdn_net_local *lp;
 	struct ippp_struct *is;
 	int proto;
-	unsigned char protobuf[4];
 
 	is = file->private_data;
 
@@ -842,24 +841,28 @@
 	if (!lp)
 		printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
 	else {
-		/*
-		 * Don't reset huptimer for
-		 * LCP packets. (Echo requests).
-		 */
-		if (copy_from_user(protobuf, buf, 4))
-			return -EFAULT;
-		proto = PPP_PROTOCOL(protobuf);
-		if (proto != PPP_LCP)
-			lp->huptimer = 0;
+		if (lp->isdn_device < 0 || lp->isdn_channel < 0) {
+			unsigned char protobuf[4];
+			/*
+			 * Don't reset huptimer for
+			 * LCP packets. (Echo requests).
+			 */
+			if (copy_from_user(protobuf, buf, 4))
+				return -EFAULT;
 
-		if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+			proto = PPP_PROTOCOL(protobuf);
+			if (proto != PPP_LCP)
+				lp->huptimer = 0;
+
 			return 0;
+		}
 
 		if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
 		    lp->dialstate == 0 &&
 		    (lp->flags & ISDN_NET_CONNECTED)) {
 			unsigned short hl;
 			struct sk_buff *skb;
+			unsigned char *cpy_buf;
 			/*
 			 * we need to reserve enough space in front of
 			 * sk_buff. old call to dev_alloc_skb only reserved
@@ -872,11 +875,21 @@
 				return count;
 			}
 			skb_reserve(skb, hl);
-			if (copy_from_user(skb_put(skb, count), buf, count))
+			cpy_buf = skb_put(skb, count);
+			if (copy_from_user(cpy_buf, buf, count))
 			{
 				kfree_skb(skb);
 				return -EFAULT;
 			}
+
+			/*
+			 * Don't reset huptimer for
+			 * LCP packets. (Echo requests).
+			 */
+			proto = PPP_PROTOCOL(cpy_buf);
+			if (proto != PPP_LCP)
+				lp->huptimer = 0;
+
 			if (is->debug & 0x40) {
 				printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
 				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32, is->unit, lp->ppp_slot);
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 568c9f9..98ca29e 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -63,7 +63,6 @@
 
 #define QPNP_WLED_VLOOP_COMP_RES_MASK			0xF0
 #define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE		0x80
-#define QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM	320
 #define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM		20
 #define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM		20
 #define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM		320
@@ -106,10 +105,8 @@
 #define QPNP_WLED_BOOST_DUTY_MIN_NS	26
 #define QPNP_WLED_BOOST_DUTY_MAX_NS	156
 #define QPNP_WLED_DEF_BOOST_DUTY_NS	104
-#define QPNP_WLED_SWITCH_FREQ_MASK	0x70
-#define QPNP_WLED_SWITCH_FREQ_800_KHZ	800
-#define QPNP_WLED_SWITCH_FREQ_1600_KHZ	1600
-#define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80
+#define QPNP_WLED_SWITCH_FREQ_MASK	GENMASK(3, 0)
+#define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
 #define QPNP_WLED_OVP_MASK		GENMASK(1, 0)
 #define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT	BIT(6)
 #define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT	BIT(5)
@@ -123,6 +120,11 @@
 #define QPNP_WLED_ILIM_FAULT_BIT	BIT(0)
 #define QPNP_WLED_OVP_FAULT_BIT		BIT(1)
 #define QPNP_WLED_SC_FAULT_BIT		BIT(2)
+#define QPNP_WLED_OVP_FLT_RT_STS_BIT	BIT(1)
+
+/* QPNP_WLED_SOFTSTART_RAMP_DLY */
+#define SOFTSTART_OVERWRITE_BIT		BIT(7)
+#define SOFTSTART_RAMP_DELAY_MASK	GENMASK(2, 0)
 
 /* sink registers */
 #define QPNP_WLED_CURR_SINK_REG(b)	(b + 0x46)
@@ -403,6 +405,7 @@
 	bool			ovp_irq_disabled;
 	bool			auto_calib_enabled;
 	bool			auto_calib_done;
+	bool			module_dis_perm;
 	ktime_t			start_ovp_fault_time;
 };
 
@@ -599,6 +602,9 @@
 {
 	int rc;
 
+	if (wled->module_dis_perm)
+		return 0;
+
 	rc = qpnp_wled_masked_write_reg(wled,
 			QPNP_WLED_MODULE_EN_REG(base_addr),
 			QPNP_WLED_MODULE_EN_MASK,
@@ -1097,20 +1103,12 @@
 	return 0;
 }
 
-#define AUTO_CALIB_BRIGHTNESS		16
+#define AUTO_CALIB_BRIGHTNESS		200
 static int wled_auto_calibrate(struct qpnp_wled *wled)
 {
 	int rc = 0, i;
 	u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
 
-	mutex_lock(&wled->lock);
-
-	/* disable OVP IRQ */
-	if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
-		disable_irq_nosync(wled->ovp_irq);
-		wled->ovp_irq_disabled = true;
-	}
-
 	/* read configured sink configuration */
 	rc = qpnp_wled_read_reg(wled,
 		QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
@@ -1135,6 +1133,17 @@
 		goto failed_calib;
 	}
 
+	if (wled->en_cabc) {
+		for (i = 0; i < wled->max_strings; i++) {
+			reg = 0;
+			rc = qpnp_wled_masked_write_reg(wled,
+				QPNP_WLED_CABC_REG(wled->sink_base, i),
+				QPNP_WLED_CABC_MASK, reg);
+			if (rc < 0)
+				goto failed_calib;
+		}
+	}
+
 	/* disable all sinks */
 	rc = qpnp_wled_write_reg(wled,
 		 QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
@@ -1143,21 +1152,6 @@
 		goto failed_calib;
 	}
 
-	rc = qpnp_wled_masked_write_reg(wled,
-		QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
-		QPNP_WLED_MODULE_EN_MASK,
-		QPNP_WLED_MODULE_EN_MASK);
-	if (rc < 0) {
-		pr_err("Failed to enable WLED module rc=%d\n", rc);
-		goto failed_calib;
-	}
-	/*
-	 * Delay for the WLED soft-start, check the OVP status
-	 * only after soft-start is complete
-	 */
-	usleep_range(QPNP_WLED_SOFT_START_DLY_US,
-			QPNP_WLED_SOFT_START_DLY_US + 1000);
-
 	/* iterate through the strings one by one */
 	for (i = 0; i < wled->max_strings; i++) {
 		sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
@@ -1181,6 +1175,15 @@
 			goto failed_calib;
 		}
 
+		/* Enable the module */
+		rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+			QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
+		if (rc < 0) {
+			pr_err("Failed to enable WLED module rc=%d\n", rc);
+			goto failed_calib;
+		}
+
 		/* delay for WLED soft-start */
 		usleep_range(QPNP_WLED_SOFT_START_DLY_US,
 				QPNP_WLED_SOFT_START_DLY_US + 1000);
@@ -1197,6 +1200,15 @@
 						i + 1);
 		else
 			sink_valid |= sink_test;
+
+		/* Disable the module */
+		rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+			QPNP_WLED_MODULE_EN_MASK, 0);
+		if (rc < 0) {
+			pr_err("Failed to disable WLED module rc=%d\n", rc);
+			goto failed_calib;
+		}
 	}
 
 	if (sink_valid == sink_config) {
@@ -1210,14 +1222,7 @@
 
 	if (!sink_config) {
 		pr_warn("No valid WLED sinks found\n");
-		goto failed_calib;
-	}
-
-	rc = qpnp_wled_masked_write_reg(wled,
-			QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
-			QPNP_WLED_MODULE_EN_MASK, 0);
-	if (rc < 0) {
-		pr_err("Failed to disable WLED module rc=%d\n", rc);
+		wled->module_dis_perm = true;
 		goto failed_calib;
 	}
 
@@ -1231,6 +1236,15 @@
 
 	/* MODULATOR_EN setting for valid sinks */
 	for (i = 0; i < wled->max_strings; i++) {
+		if (wled->en_cabc) {
+			reg = 1 << QPNP_WLED_CABC_SHIFT;
+			rc = qpnp_wled_masked_write_reg(wled,
+				QPNP_WLED_CABC_REG(wled->sink_base, i),
+				QPNP_WLED_CABC_MASK, reg);
+			if (rc < 0)
+				goto failed_calib;
+		}
+
 		if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
 			reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
 		else
@@ -1259,7 +1273,8 @@
 	}
 
 	/* restore  brightness */
-	rc = qpnp_wled_set_level(wled, wled->cdev.brightness);
+	rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
+			AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
 	if (rc < 0) {
 		pr_err("Failed to set brightness after calibration rc=%d\n",
 						rc);
@@ -1280,11 +1295,6 @@
 			QPNP_WLED_SOFT_START_DLY_US + 1000);
 
 failed_calib:
-	if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
-		enable_irq(wled->ovp_irq);
-		wled->ovp_irq_disabled = false;
-	}
-	mutex_unlock(&wled->lock);
 	return rc;
 }
 
@@ -1320,6 +1330,38 @@
 	return false;
 }
 
+static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 fault_status = 0, rt_status = 0;
+
+	if (!wled->auto_calib_enabled)
+		return 0;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
+	if (rc < 0)
+		pr_err("Failed to read RT status rc=%d\n", rc);
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
+	if (rc < 0)
+		pr_err("Failed to read fault status rc=%d\n", rc);
+
+	if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
+			(fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
+		mutex_lock(&wled->lock);
+		rc = wled_auto_calibrate(wled);
+		if (rc < 0)
+			pr_err("Failed auto-calibration rc=%d\n", rc);
+		else
+			wled->auto_calib_done = true;
+		mutex_unlock(&wled->lock);
+	}
+
+	return rc;
+}
+
 /* ovp irq handler */
 static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
 {
@@ -1348,13 +1390,26 @@
 	if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
 		if (wled->auto_calib_enabled && !wled->auto_calib_done) {
 			if (qpnp_wled_auto_cal_required(wled)) {
-				rc = wled_auto_calibrate(wled);
-				if (rc < 0) {
-					pr_err("Failed auto-calibration rc=%d\n",
-							rc);
-					return IRQ_HANDLED;
+				mutex_lock(&wled->lock);
+				if (wled->ovp_irq > 0 &&
+						!wled->ovp_irq_disabled) {
+					disable_irq_nosync(wled->ovp_irq);
+					wled->ovp_irq_disabled = true;
 				}
-				wled->auto_calib_done = true;
+
+				rc = wled_auto_calibrate(wled);
+				if (rc < 0)
+					pr_err("Failed auto-calibration rc=%d\n",
+								rc);
+				else
+					wled->auto_calib_done = true;
+
+				if (wled->ovp_irq > 0 &&
+						wled->ovp_irq_disabled) {
+					enable_irq(wled->ovp_irq);
+					wled->ovp_irq_disabled = false;
+				}
+				mutex_unlock(&wled->lock);
 			}
 		}
 	}
@@ -1422,20 +1477,26 @@
 	u8 mask = 0, reg = 0;
 
 	/* Configure the LOOP COMP GM register */
-	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
-			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
-		if (wled->loop_auto_gm_en)
-			reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
+	if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
+		if (wled->disp_type_amoled) {
+			reg = 0;
+			mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+				QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+		} else {
+			if (wled->loop_auto_gm_en)
+				reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
 
-		if (wled->loop_auto_gm_thresh >
-				QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
-			wled->loop_auto_gm_thresh =
-				QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
+			if (wled->loop_auto_gm_thresh >
+					QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
+				wled->loop_auto_gm_thresh =
+					QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
 
-		reg |= wled->loop_auto_gm_thresh <<
-			QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
-		mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
-			QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+			reg |= wled->loop_auto_gm_thresh <<
+				QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
+			mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+				QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+		}
 	}
 
 	if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
@@ -1728,8 +1789,17 @@
 
 	/* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
 	reg = (wled->disp_type_amoled) ? 0 : 2;
-	rc = qpnp_wled_write_reg(wled,
-			QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base), reg);
+	mask = SOFTSTART_RAMP_DELAY_MASK;
+	if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		&& wled->disp_type_amoled) {
+		reg |= SOFTSTART_OVERWRITE_BIT;
+		mask |= SOFTSTART_OVERWRITE_BIT;
+	}
+
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
+			mask, reg);
 	if (rc)
 		return rc;
 
@@ -1751,21 +1821,24 @@
 		return rc;
 
 	/* Configure the SWITCHING FREQ register */
-	if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
-		temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
+	if (wled->switch_freq_khz == 1600)
+		reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
 	else
-		temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
+		reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
 
-	rc = qpnp_wled_read_reg(wled,
-			QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), &reg);
+	/*
+	 * Do not set the overwrite bit when switching frequency is selected
+	 * for AMOLED. This register is in logic reset block which can cause
+	 * the value to be overwritten during module enable/disable.
+	 */
+	mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+	if (!wled->disp_type_amoled)
+		reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
 	if (rc < 0)
 		return rc;
-	reg &= QPNP_WLED_SWITCH_FREQ_MASK;
-	reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
-	rc = qpnp_wled_write_reg(wled,
-			QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
-	if (rc)
-		return rc;
 
 	rc = qpnp_wled_ovp_config(wled);
 	if (rc < 0) {
@@ -1946,6 +2019,10 @@
 		return rc;
 	}
 
+	rc = qpnp_wled_auto_calibrate_at_init(wled);
+	if (rc < 0)
+		pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
+
 	/* setup ovp and sc irqs */
 	if (wled->ovp_irq >= 0) {
 		rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
@@ -2070,8 +2147,11 @@
 			return rc;
 		}
 
-		wled->loop_comp_res_kohm =
-			QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
+		wled->loop_comp_res_kohm = 320;
+		if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+			wled->loop_comp_res_kohm = 300;
+
 		rc = of_property_read_u32(pdev->dev.of_node,
 				"qcom,loop-comp-res-kohm", &temp_val);
 		if (!rc) {
@@ -2199,7 +2279,7 @@
 		return rc;
 	}
 
-	wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
+	wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
 	rc = of_property_read_u32(pdev->dev.of_node,
 			"qcom,switch-freq-khz", &temp_val);
 	if (!rc) {
@@ -2404,7 +2484,7 @@
 		wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
 
 	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
-			0, 0);
+			NULL, NULL);
 	if (!prop) {
 		dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
 		return rc;
@@ -2412,7 +2492,7 @@
 	wled->sink_base = be32_to_cpu(*prop);
 
 	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
-			0, 0);
+			NULL, NULL);
 	if (!prop) {
 		dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
 		return rc;
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 817dfa3..e3cfffb 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -875,6 +875,14 @@
 		}
 		if (led->mpp_cfg->pwm_mode == PWM_MODE) {
 			/*config pwm for brightness scaling*/
+			rc = pwm_change_mode(led->mpp_cfg->pwm_cfg->pwm_dev,
+					PM_PWM_MODE_PWM);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Failed to set PWM mode, rc = %d\n",
+					rc);
+				return rc;
+			}
 			period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
 			if (period_us > INT_MAX / NSEC_PER_USEC) {
 				duty_us = (period_us * led->cdev.brightness) /
@@ -1213,7 +1221,7 @@
 
 static int qpnp_flash_set(struct qpnp_led_data *led)
 {
-	int rc, error;
+	int rc = 0, error;
 	int val = led->cdev.brightness;
 
 	if (led->flash_cfg->torch_enable)
@@ -1251,7 +1259,8 @@
 				}
 			}
 
-			qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
+			rc = qpnp_led_masked_write(led,
+				FLASH_MAX_CURR(led->base),
 				FLASH_CURRENT_MASK,
 				TORCH_MAX_LEVEL);
 			if (rc) {
@@ -1261,7 +1270,7 @@
 				goto error_reg_write;
 			}
 
-			qpnp_led_masked_write(led,
+			rc = qpnp_led_masked_write(led,
 				FLASH_LED_TMR_CTRL(led->base),
 				FLASH_TMR_MASK,
 				FLASH_TMR_WATCHDOG);
@@ -1293,7 +1302,7 @@
 				goto error_reg_write;
 			}
 
-			qpnp_led_masked_write(led,
+			rc = qpnp_led_masked_write(led,
 				FLASH_WATCHDOG_TMR(led->base),
 				FLASH_WATCHDOG_MASK,
 				led->flash_cfg->duration);
@@ -1341,7 +1350,7 @@
 				goto error_flash_set;
 			}
 
-			qpnp_led_masked_write(led,
+			rc = qpnp_led_masked_write(led,
 				FLASH_LED_TMR_CTRL(led->base),
 				FLASH_TMR_MASK,
 				FLASH_TMR_SAFETY);
@@ -1580,6 +1589,14 @@
 		}
 
 		if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
+			rc = pwm_change_mode(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+					PM_PWM_MODE_PWM);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Failed to set PWM mode, rc = %d\n",
+					rc);
+				return rc;
+			}
 			period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
 			if (period_us > INT_MAX / NSEC_PER_USEC) {
 				duty_us = (period_us * led->cdev.brightness) /
@@ -1701,6 +1718,14 @@
 			led->rgb_cfg->pwm_cfg->mode =
 				led->rgb_cfg->pwm_cfg->default_mode;
 		if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+			rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev,
+					PM_PWM_MODE_PWM);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Failed to set PWM mode, rc = %d\n",
+					rc);
+				return rc;
+			}
 			period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
 			if (period_us > INT_MAX / NSEC_PER_USEC) {
 				duty_us = (period_us * led->cdev.brightness) /
@@ -2135,6 +2160,11 @@
 				dev_err(&pdev->dev, "Failed to configure pwm LUT\n");
 				return rc;
 			}
+			rc = pwm_change_mode(pwm_cfg->pwm_dev, PM_PWM_MODE_LPG);
+			if (rc < 0) {
+				dev_err(&pdev->dev, "Failed to set LPG mode\n");
+				return rc;
+			}
 		}
 	} else {
 		dev_err(&pdev->dev, "Invalid PWM device\n");
diff --git a/drivers/leds/trigger/ledtrig-heartbeat.c b/drivers/leds/trigger/ledtrig-heartbeat.c
index c9f3862..410c39c 100644
--- a/drivers/leds/trigger/ledtrig-heartbeat.c
+++ b/drivers/leds/trigger/ledtrig-heartbeat.c
@@ -19,7 +19,6 @@
 #include <linux/sched.h>
 #include <linux/leds.h>
 #include <linux/reboot.h>
-#include <linux/suspend.h>
 #include "../leds.h"
 
 static int panic_heartbeats;
@@ -155,30 +154,6 @@
 	.deactivate = heartbeat_trig_deactivate,
 };
 
-static int heartbeat_pm_notifier(struct notifier_block *nb,
-				 unsigned long pm_event, void *unused)
-{
-	int rc;
-
-	switch (pm_event) {
-	case PM_SUSPEND_PREPARE:
-	case PM_HIBERNATION_PREPARE:
-	case PM_RESTORE_PREPARE:
-		led_trigger_unregister(&heartbeat_led_trigger);
-		break;
-	case PM_POST_SUSPEND:
-	case PM_POST_HIBERNATION:
-	case PM_POST_RESTORE:
-		rc = led_trigger_register(&heartbeat_led_trigger);
-		if (rc)
-			pr_err("could not re-register heartbeat trigger\n");
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_DONE;
-}
-
 static int heartbeat_reboot_notifier(struct notifier_block *nb,
 				     unsigned long code, void *unused)
 {
@@ -193,10 +168,6 @@
 	return NOTIFY_DONE;
 }
 
-static struct notifier_block heartbeat_pm_nb = {
-	.notifier_call = heartbeat_pm_notifier,
-};
-
 static struct notifier_block heartbeat_reboot_nb = {
 	.notifier_call = heartbeat_reboot_notifier,
 };
@@ -213,14 +184,12 @@
 		atomic_notifier_chain_register(&panic_notifier_list,
 					       &heartbeat_panic_nb);
 		register_reboot_notifier(&heartbeat_reboot_nb);
-		register_pm_notifier(&heartbeat_pm_nb);
 	}
 	return rc;
 }
 
 static void __exit heartbeat_trig_exit(void)
 {
-	unregister_pm_notifier(&heartbeat_pm_nb);
 	unregister_reboot_notifier(&heartbeat_reboot_nb);
 	atomic_notifier_chain_unregister(&panic_notifier_list,
 					 &heartbeat_panic_nb);
diff --git a/drivers/mailbox/mailbox.c b/drivers/mailbox/mailbox.c
index ecb77cc..9dc85cd 100644
--- a/drivers/mailbox/mailbox.c
+++ b/drivers/mailbox/mailbox.c
@@ -63,7 +63,7 @@
 again:
 	spin_lock_irqsave(&chan->lock, flags);
 
-	if (!chan->msg_count || chan->active_req)
+	if (!chan->msg_count || (chan->active_req && err != -EAGAIN))
 		goto exit;
 
 	count = chan->msg_count;
@@ -296,8 +296,9 @@
 EXPORT_SYMBOL_GPL(mbox_send_message);
 
 /**
- * mbox_send_controller_data-	For client to submit a message to be
- *				sent only to the controller.
+ * mbox_write_controller_data -	For client to submit a message to be
+ *				written to the controller but not sent to
+ *				the remote processor.
  * @chan: Mailbox channel assigned to this client.
  * @mssg: Client specific message typecasted.
  *
@@ -308,7 +309,7 @@
  *	or transmission over chan (blocking mode).
  *	Negative value denotes failure.
  */
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg)
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg)
 {
 	unsigned long flags;
 	int err;
@@ -317,12 +318,12 @@
 		return -EINVAL;
 
 	spin_lock_irqsave(&chan->lock, flags);
-	err = chan->mbox->ops->send_controller_data(chan, mssg);
+	err = chan->mbox->ops->write_controller_data(chan, mssg);
 	spin_unlock_irqrestore(&chan->lock, flags);
 
 	return err;
 }
-EXPORT_SYMBOL(mbox_send_controller_data);
+EXPORT_SYMBOL(mbox_write_controller_data);
 
 bool mbox_controller_is_idle(struct mbox_chan *chan)
 {
diff --git a/drivers/mailbox/msm_qmp.c b/drivers/mailbox/msm_qmp.c
index f0bb0bc..d6e41ae 100644
--- a/drivers/mailbox/msm_qmp.c
+++ b/drivers/mailbox/msm_qmp.c
@@ -27,7 +27,7 @@
 #define QMP_VERSION	0x1
 #define QMP_FEATURES	0x0
 #define QMP_TOUT_MS	5000
-#define QMP_TX_TOUT_MS	2000
+#define QMP_TX_TOUT_MS	1000
 
 #define QMP_MBOX_LINK_DOWN		0xFFFF0000
 #define QMP_MBOX_LINK_UP		0x0000FFFF
@@ -229,7 +229,7 @@
 }
 
 /**
- * qmp_notify_timeout() - Notify client of tx timeout with -EIO
+ * qmp_notify_timeout() - Notify client of tx timeout with -ETIME
  * @work:	Structure for work that was scheduled.
  */
 static void qmp_notify_timeout(struct work_struct *work)
@@ -237,7 +237,7 @@
 	struct delayed_work *dwork = to_delayed_work(work);
 	struct qmp_mbox *mbox = container_of(dwork, struct qmp_mbox, dwork);
 	struct mbox_chan *chan = &mbox->ctrl.chans[mbox->idx_in_flight];
-	int err = -EIO;
+	int err = -ETIME;
 	unsigned long flags;
 
 	spin_lock_irqsave(&mbox->tx_lock, flags);
@@ -246,6 +246,7 @@
 		return;
 	}
 	pr_err("%s: qmp tx timeout for %d\n", __func__, mbox->idx_in_flight);
+	iowrite32(0, mbox->mdev->msgram + mbox->mcore_mbox_offset);
 	mbox->tx_sent = false;
 	spin_unlock_irqrestore(&mbox->tx_lock, flags);
 	mbox_chan_txdone(chan, err);
@@ -358,7 +359,7 @@
 	addr = mdev->msgram + mbox->mcore_mbox_offset;
 	if (mbox->tx_sent) {
 		spin_unlock_irqrestore(&mbox->tx_lock, flags);
-		return -EBUSY;
+		return -EAGAIN;
 	}
 
 	if (pkt->size + sizeof(pkt->size) > mbox->mcore_mbox_size) {
diff --git a/drivers/mailbox/qcom-rpmh-mailbox.c b/drivers/mailbox/qcom-rpmh-mailbox.c
index a69fe85..160b858 100644
--- a/drivers/mailbox/qcom-rpmh-mailbox.c
+++ b/drivers/mailbox/qcom-rpmh-mailbox.c
@@ -29,7 +29,7 @@
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/spinlock.h>
-
+#include <asm/arch_timer.h>
 #include <asm-generic/io.h>
 
 #include <soc/qcom/tcs.h>
@@ -1075,7 +1075,7 @@
 
 static const struct mbox_chan_ops mbox_ops = {
 	.send_data = chan_tcs_write,
-	.send_controller_data = chan_tcs_ctrl_write,
+	.write_controller_data = chan_tcs_ctrl_write,
 	.startup = chan_init,
 	.shutdown = chan_shutdown,
 };
diff --git a/drivers/mcb/mcb-lpc.c b/drivers/mcb/mcb-lpc.c
index d072c08..945091a 100644
--- a/drivers/mcb/mcb-lpc.c
+++ b/drivers/mcb/mcb-lpc.c
@@ -114,6 +114,12 @@
 	.flags = IORESOURCE_MEM,
 };
 
+static struct resource sc31_fpga_resource = {
+	.start = 0xf000e000,
+	.end = 0xf000e000 + CHAM_HEADER_SIZE,
+	.flags = IORESOURCE_MEM,
+};
+
 static struct platform_driver mcb_lpc_driver = {
 	.driver		= {
 		.name = "mcb-lpc",
@@ -132,6 +138,15 @@
 		.driver_data = (void *)&sc24_fpga_resource,
 		.callback = mcb_lpc_create_platform_device,
 	},
+	{
+		.ident = "SC31",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "MEN"),
+			DMI_MATCH(DMI_PRODUCT_VERSION, "14SC31"),
+		},
+		.driver_data = (void *)&sc31_fpga_resource,
+		.callback = mcb_lpc_create_platform_device,
+	},
 	{}
 };
 MODULE_DEVICE_TABLE(dmi, mcb_lpc_dmi_table);
diff --git a/drivers/md/bcache/bcache.h b/drivers/md/bcache/bcache.h
index c3ea03c..02619ca 100644
--- a/drivers/md/bcache/bcache.h
+++ b/drivers/md/bcache/bcache.h
@@ -333,6 +333,7 @@
 	/* Limit number of writeback bios in flight */
 	struct semaphore	in_flight;
 	struct task_struct	*writeback_thread;
+	struct workqueue_struct	*writeback_write_wq;
 
 	struct keybuf		writeback_keys;
 
diff --git a/drivers/md/bcache/request.c b/drivers/md/bcache/request.c
index a37c177..e0f1c6d 100644
--- a/drivers/md/bcache/request.c
+++ b/drivers/md/bcache/request.c
@@ -196,12 +196,12 @@
 	struct data_insert_op *op = container_of(cl, struct data_insert_op, cl);
 	struct bio *bio = op->bio, *n;
 
-	if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
-		wake_up_gc(op->c);
-
 	if (op->bypass)
 		return bch_data_invalidate(cl);
 
+	if (atomic_sub_return(bio_sectors(bio), &op->c->sectors_to_gc) < 0)
+		wake_up_gc(op->c);
+
 	/*
 	 * Journal writes are marked REQ_PREFLUSH; if the original write was a
 	 * flush, it'll wait on the journal write.
diff --git a/drivers/md/bcache/super.c b/drivers/md/bcache/super.c
index 66669c8..f4557f5 100644
--- a/drivers/md/bcache/super.c
+++ b/drivers/md/bcache/super.c
@@ -1025,7 +1025,7 @@
 	}
 
 	if (BDEV_STATE(&dc->sb) == BDEV_STATE_DIRTY) {
-		bch_sectors_dirty_init(dc);
+		bch_sectors_dirty_init(&dc->disk);
 		atomic_set(&dc->has_dirty, 1);
 		atomic_inc(&dc->count);
 		bch_writeback_queue(dc);
@@ -1058,6 +1058,8 @@
 	cancel_delayed_work_sync(&dc->writeback_rate_update);
 	if (!IS_ERR_OR_NULL(dc->writeback_thread))
 		kthread_stop(dc->writeback_thread);
+	if (dc->writeback_write_wq)
+		destroy_workqueue(dc->writeback_write_wq);
 
 	mutex_lock(&bch_register_lock);
 
@@ -1229,6 +1231,7 @@
 		goto err;
 
 	bcache_device_attach(d, c, u - c->uuids);
+	bch_sectors_dirty_init(d);
 	bch_flash_dev_request_init(d);
 	add_disk(d->disk);
 
@@ -1967,6 +1970,8 @@
 			else
 				err = "device busy";
 			mutex_unlock(&bch_register_lock);
+			if (!IS_ERR(bdev))
+				bdput(bdev);
 			if (attr == &ksysfs_register_quiet)
 				goto out;
 		}
diff --git a/drivers/md/bcache/sysfs.c b/drivers/md/bcache/sysfs.c
index b3ff57d..4fbb553 100644
--- a/drivers/md/bcache/sysfs.c
+++ b/drivers/md/bcache/sysfs.c
@@ -191,7 +191,7 @@
 {
 	struct cached_dev *dc = container_of(kobj, struct cached_dev,
 					     disk.kobj);
-	unsigned v = size;
+	ssize_t v = size;
 	struct cache_set *c;
 	struct kobj_uevent_env *env;
 
@@ -226,7 +226,7 @@
 		bch_cached_dev_run(dc);
 
 	if (attr == &sysfs_cache_mode) {
-		ssize_t v = bch_read_string_list(buf, bch_cache_modes + 1);
+		v = bch_read_string_list(buf, bch_cache_modes + 1);
 
 		if (v < 0)
 			return v;
diff --git a/drivers/md/bcache/util.c b/drivers/md/bcache/util.c
index dde6172..eb70f68 100644
--- a/drivers/md/bcache/util.c
+++ b/drivers/md/bcache/util.c
@@ -73,24 +73,44 @@
 STRTO_H(strtoll, long long)
 STRTO_H(strtoull, unsigned long long)
 
+/**
+ * bch_hprint() - formats @v to human readable string for sysfs.
+ *
+ * @v - signed 64 bit integer
+ * @buf - the (at least 8 byte) buffer to format the result into.
+ *
+ * Returns the number of bytes used by format.
+ */
 ssize_t bch_hprint(char *buf, int64_t v)
 {
 	static const char units[] = "?kMGTPEZY";
-	char dec[4] = "";
-	int u, t = 0;
+	int u = 0, t;
 
-	for (u = 0; v >= 1024 || v <= -1024; u++) {
-		t = v & ~(~0 << 10);
-		v >>= 10;
-	}
+	uint64_t q;
 
-	if (!u)
-		return sprintf(buf, "%llu", v);
+	if (v < 0)
+		q = -v;
+	else
+		q = v;
 
-	if (v < 100 && v > -100)
-		snprintf(dec, sizeof(dec), ".%i", t / 100);
+	/* For as long as the number is more than 3 digits, but at least
+	 * once, shift right / divide by 1024.  Keep the remainder for
+	 * a digit after the decimal point.
+	 */
+	do {
+		u++;
 
-	return sprintf(buf, "%lli%s%c", v, dec, units[u]);
+		t = q & ~(~0 << 10);
+		q >>= 10;
+	} while (q >= 1000);
+
+	if (v < 0)
+		/* '-', up to 3 digits, '.', 1 digit, 1 character, null;
+		 * yields 8 bytes.
+		 */
+		return sprintf(buf, "-%llu.%i%c", q, t * 10 / 1024, units[u]);
+	else
+		return sprintf(buf, "%llu.%i%c", q, t * 10 / 1024, units[u]);
 }
 
 ssize_t bch_snprint_string_list(char *buf, size_t size, const char * const list[],
diff --git a/drivers/md/bcache/writeback.c b/drivers/md/bcache/writeback.c
index e51644e..4ce2b19 100644
--- a/drivers/md/bcache/writeback.c
+++ b/drivers/md/bcache/writeback.c
@@ -20,7 +20,8 @@
 static void __update_writeback_rate(struct cached_dev *dc)
 {
 	struct cache_set *c = dc->disk.c;
-	uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size;
+	uint64_t cache_sectors = c->nbuckets * c->sb.bucket_size -
+				bcache_flash_devs_sectors_dirty(c);
 	uint64_t cache_dirty_target =
 		div_u64(cache_sectors * dc->writeback_percent, 100);
 
@@ -186,7 +187,7 @@
 
 	closure_bio_submit(&io->bio, cl);
 
-	continue_at(cl, write_dirty_finish, system_wq);
+	continue_at(cl, write_dirty_finish, io->dc->writeback_write_wq);
 }
 
 static void read_dirty_endio(struct bio *bio)
@@ -206,7 +207,7 @@
 
 	closure_bio_submit(&io->bio, cl);
 
-	continue_at(cl, write_dirty, system_wq);
+	continue_at(cl, write_dirty, io->dc->writeback_write_wq);
 }
 
 static void read_dirty(struct cached_dev *dc)
@@ -482,17 +483,17 @@
 	return MAP_CONTINUE;
 }
 
-void bch_sectors_dirty_init(struct cached_dev *dc)
+void bch_sectors_dirty_init(struct bcache_device *d)
 {
 	struct sectors_dirty_init op;
 
 	bch_btree_op_init(&op.op, -1);
-	op.inode = dc->disk.id;
+	op.inode = d->id;
 
-	bch_btree_map_keys(&op.op, dc->disk.c, &KEY(op.inode, 0, 0),
+	bch_btree_map_keys(&op.op, d->c, &KEY(op.inode, 0, 0),
 			   sectors_dirty_init_fn, 0);
 
-	dc->disk.sectors_dirty_last = bcache_dev_sectors_dirty(&dc->disk);
+	d->sectors_dirty_last = bcache_dev_sectors_dirty(d);
 }
 
 void bch_cached_dev_writeback_init(struct cached_dev *dc)
@@ -516,6 +517,11 @@
 
 int bch_cached_dev_writeback_start(struct cached_dev *dc)
 {
+	dc->writeback_write_wq = alloc_workqueue("bcache_writeback_wq",
+						WQ_MEM_RECLAIM, 0);
+	if (!dc->writeback_write_wq)
+		return -ENOMEM;
+
 	dc->writeback_thread = kthread_create(bch_writeback_thread, dc,
 					      "bcache_writeback");
 	if (IS_ERR(dc->writeback_thread))
diff --git a/drivers/md/bcache/writeback.h b/drivers/md/bcache/writeback.h
index 301eaf5..cdf8d25 100644
--- a/drivers/md/bcache/writeback.h
+++ b/drivers/md/bcache/writeback.h
@@ -14,6 +14,25 @@
 	return ret;
 }
 
+static inline uint64_t  bcache_flash_devs_sectors_dirty(struct cache_set *c)
+{
+	uint64_t i, ret = 0;
+
+	mutex_lock(&bch_register_lock);
+
+	for (i = 0; i < c->nr_uuids; i++) {
+		struct bcache_device *d = c->devices[i];
+
+		if (!d || !UUID_FLASH_ONLY(&c->uuids[i]))
+			continue;
+	   ret += bcache_dev_sectors_dirty(d);
+	}
+
+	mutex_unlock(&bch_register_lock);
+
+	return ret;
+}
+
 static inline unsigned offset_to_stripe(struct bcache_device *d,
 					uint64_t offset)
 {
@@ -85,7 +104,7 @@
 
 void bcache_dev_sectors_dirty_add(struct cache_set *, unsigned, uint64_t, int);
 
-void bch_sectors_dirty_init(struct cached_dev *dc);
+void bch_sectors_dirty_init(struct bcache_device *);
 void bch_cached_dev_writeback_init(struct cached_dev *);
 int bch_cached_dev_writeback_start(struct cached_dev *);
 
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index 2d82692..fb02c39 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -1992,6 +1992,11 @@
 	long pages;
 	struct bitmap_page *new_bp;
 
+	if (bitmap->storage.file && !init) {
+		pr_info("md: cannot resize file-based bitmap\n");
+		return -EINVAL;
+	}
+
 	if (chunksize == 0) {
 		/* If there is enough space, leave the chunk size unchanged,
 		 * else increase by factor of two until there is enough space.
diff --git a/drivers/md/dm-req-crypt.c b/drivers/md/dm-req-crypt.c
index 3ffe7e5..b4bdbc0 100644
--- a/drivers/md/dm-req-crypt.c
+++ b/drivers/md/dm-req-crypt.c
@@ -217,6 +217,7 @@
 			 * this should never happen
 			 */
 			WARN_ON(1);
+			return;
 		}
 	} else {
 		DMERR("%s io is NULL\n", __func__);
@@ -225,6 +226,7 @@
 		 * this should never happen
 		 */
 		WARN_ON(1);
+		return;
 	}
 
 	atomic_dec(&io->pending);
@@ -253,6 +255,7 @@
 			 * this should never happen
 			 */
 			WARN_ON(1);
+			return;
 		}
 	} else {
 		DMERR("%s io is NULL\n",
@@ -262,6 +265,7 @@
 		 * this should never happen
 		 */
 		WARN_ON(1);
+		return;
 	}
 
 	/* Should never get here if io or Clone is NULL */
@@ -445,6 +449,7 @@
 	if (!io || !io->cloned_request) {
 		DMERR("%s io is invalid\n", __func__);
 		WARN_ON(1); /* should not happen */
+		return;
 	}
 
 	clone = io->cloned_request;
@@ -696,6 +701,7 @@
 	if (!io || !io->cloned_request) {
 		DMERR("%s io is invalid\n", __func__);
 		WARN_ON(1); /* should not happen */
+		return;
 	}
 
 	clone = io->cloned_request;
@@ -740,6 +746,7 @@
 		err = DM_REQ_CRYPT_ERROR;
 		/* If io is not populated this should not be called */
 		WARN_ON(1);
+		return;
 	}
 	req = skcipher_request_alloc(tfm, GFP_KERNEL);
 	if (!req) {
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index bca4c0e..e289aae 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -848,8 +848,12 @@
 		tio = tio_from_request(rq);
 		/* Establish tio->ti before queuing work (map_tio_request) */
 		tio->ti = ti;
-		kthread_queue_work(&md->kworker, &tio->work);
+		spin_unlock(q->queue_lock);
+		if (map_request(tio) == DM_MAPIO_REQUEUE)
+			dm_requeue_original_request(tio, false);
+
 		BUG_ON(!irqs_disabled());
+		spin_lock(q->queue_lock);
 	}
 }
 
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index b0c0aef..12abf69 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -223,7 +223,8 @@
 	 * oldconf until no one uses it anymore.
 	 */
 	mddev_suspend(mddev);
-	oldconf = rcu_dereference(mddev->private);
+	oldconf = rcu_dereference_protected(mddev->private,
+			lockdep_is_held(&mddev->reconfig_mutex));
 	mddev->raid_disks++;
 	WARN_ONCE(mddev->raid_disks != newconf->raid_disks,
 		"copied raid_disks doesn't match mddev->raid_disks");
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 4c4aab0..b19b551 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1407,11 +1407,24 @@
 			mbio->bi_private = r10_bio;
 
 			atomic_inc(&r10_bio->remaining);
+
+			cb = blk_check_plugged(raid10_unplug, mddev,
+					       sizeof(*plug));
+			if (cb)
+				plug = container_of(cb, struct raid10_plug_cb,
+						    cb);
+			else
+				plug = NULL;
 			spin_lock_irqsave(&conf->device_lock, flags);
-			bio_list_add(&conf->pending_bio_list, mbio);
-			conf->pending_count++;
+			if (plug) {
+				bio_list_add(&plug->pending, mbio);
+				plug->pending_cnt++;
+			} else {
+				bio_list_add(&conf->pending_bio_list, mbio);
+				conf->pending_count++;
+			}
 			spin_unlock_irqrestore(&conf->device_lock, flags);
-			if (!mddev_check_plugged(mddev))
+			if (!plug)
 				md_wakeup_thread(mddev->thread);
 		}
 	}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 383f19c..7aea022 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -829,6 +829,14 @@
 			spin_unlock(&head->batch_head->batch_lock);
 			goto unlock_out;
 		}
+		/*
+		 * We must assign batch_head of this stripe within the
+		 * batch_lock, otherwise clear_batch_ready of batch head
+		 * stripe could clear BATCH_READY bit of this stripe and
+		 * this stripe->batch_head doesn't get assigned, which
+		 * could confuse clear_batch_ready for this stripe
+		 */
+		sh->batch_head = head->batch_head;
 
 		/*
 		 * at this point, head's BATCH_READY could be cleared, but we
@@ -836,8 +844,6 @@
 		 */
 		list_add(&sh->batch_list, &head->batch_list);
 		spin_unlock(&head->batch_head->batch_lock);
-
-		sh->batch_head = head->batch_head;
 	} else {
 		head->batch_head = head;
 		sh->batch_head = head->batch_head;
@@ -4277,7 +4283,8 @@
 
 		set_mask_bits(&sh->state, ~(STRIPE_EXPAND_SYNC_FLAGS |
 					    (1 << STRIPE_PREREAD_ACTIVE) |
-					    (1 << STRIPE_DEGRADED)),
+					    (1 << STRIPE_DEGRADED) |
+					    (1 << STRIPE_ON_UNPLUG_LIST)),
 			      head_sh->state & (1 << STRIPE_INSYNC));
 
 		sh->check_state = head_sh->check_state;
@@ -5844,6 +5851,8 @@
 
 	spin_unlock_irq(&conf->device_lock);
 
+	r5l_flush_stripe_to_raid(conf->log);
+
 	async_tx_issue_pending_all();
 	blk_finish_plug(&plug);
 
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index f2d39a9..0eadf08 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -4028,6 +4028,9 @@
 		hcount = 3 + dfil->todo;
 		if (hcount > count)
 			hcount = count;
+		if (hcount == 0)
+			return done;
+
 		result = dvb_dmxdev_buffer_read(dfil, &dfil->buffer,
 						file->f_flags & O_NONBLOCK,
 						buf, hcount, ppos);
diff --git a/drivers/media/dvb-core/dvb_demux.c b/drivers/media/dvb-core/dvb_demux.c
index 474684f..b2459ff 100644
--- a/drivers/media/dvb-core/dvb_demux.c
+++ b/drivers/media/dvb-core/dvb_demux.c
@@ -347,6 +347,9 @@
 						buf_size)
 					break;
 
+				if (current_size >= DVB_DMX_MAX_PATTERN_LEN)
+					break;
+
 				if (dvb_dmx_patterns_match(
 					(patterns[j]->pattern + current_size),
 					buf, (patterns[j]->mask + current_size),
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 4003831..7b1935a 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3118,6 +3118,9 @@
 	state->pdata.blank_data = 1;
 	state->pdata.op_format_mode_sel = ADV7604_OP_FORMAT_MODE0;
 	state->pdata.bus_order = ADV7604_BUS_ORDER_RGB;
+	state->pdata.dr_str_data = ADV76XX_DR_STR_MEDIUM_HIGH;
+	state->pdata.dr_str_clk = ADV76XX_DR_STR_MEDIUM_HIGH;
+	state->pdata.dr_str_sync = ADV76XX_DR_STR_MEDIUM_HIGH;
 
 	return 0;
 }
diff --git a/drivers/media/pci/bt8xx/dvb-bt8xx.c b/drivers/media/pci/bt8xx/dvb-bt8xx.c
index e69d338..ae550a1 100644
--- a/drivers/media/pci/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/pci/bt8xx/dvb-bt8xx.c
@@ -680,6 +680,7 @@
 		/*	DST is not a frontend, attaching the ASIC	*/
 		if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) {
 			pr_err("%s: Could not find a Twinhan DST\n", __func__);
+			kfree(state);
 			break;
 		}
 		/*	Attach other DST peripherals if any		*/
diff --git a/drivers/media/pci/saa7164/saa7164-bus.c b/drivers/media/pci/saa7164/saa7164-bus.c
index a18fe5d..b4857cd 100644
--- a/drivers/media/pci/saa7164/saa7164-bus.c
+++ b/drivers/media/pci/saa7164/saa7164-bus.c
@@ -393,11 +393,11 @@
 	msg_tmp.size = le16_to_cpu((__force __le16)msg_tmp.size);
 	msg_tmp.command = le32_to_cpu((__force __le32)msg_tmp.command);
 	msg_tmp.controlselector = le16_to_cpu((__force __le16)msg_tmp.controlselector);
+	memcpy(msg, &msg_tmp, sizeof(*msg));
 
 	/* No need to update the read positions, because this was a peek */
 	/* If the caller specifically want to peek, return */
 	if (peekonly) {
-		memcpy(msg, &msg_tmp, sizeof(*msg));
 		goto peekout;
 	}
 
@@ -442,21 +442,15 @@
 		space_rem = bus->m_dwSizeGetRing - curr_grp;
 
 		if (space_rem < sizeof(*msg)) {
-			/* msg wraps around the ring */
-			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, space_rem);
-			memcpy_fromio((u8 *)msg + space_rem, bus->m_pdwGetRing,
-				sizeof(*msg) - space_rem);
 			if (buf)
 				memcpy_fromio(buf, bus->m_pdwGetRing + sizeof(*msg) -
 					space_rem, buf_size);
 
 		} else if (space_rem == sizeof(*msg)) {
-			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 			if (buf)
 				memcpy_fromio(buf, bus->m_pdwGetRing, buf_size);
 		} else {
 			/* Additional data wraps around the ring */
-			memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 			if (buf) {
 				memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp +
 					sizeof(*msg), space_rem - sizeof(*msg));
@@ -469,15 +463,10 @@
 
 	} else {
 		/* No wrapping */
-		memcpy_fromio(msg, bus->m_pdwGetRing + curr_grp, sizeof(*msg));
 		if (buf)
 			memcpy_fromio(buf, bus->m_pdwGetRing + curr_grp + sizeof(*msg),
 				buf_size);
 	}
-	/* Convert from little endian to CPU */
-	msg->size = le16_to_cpu((__force __le16)msg->size);
-	msg->command = le32_to_cpu((__force __le32)msg->command);
-	msg->controlselector = le16_to_cpu((__force __le16)msg->controlselector);
 
 	/* Update the read positions, adjusting the ring */
 	saa7164_writel(bus->m_dwGetReadPos, new_grp);
diff --git a/drivers/media/pci/ttpci/av7110_hw.c b/drivers/media/pci/ttpci/av7110_hw.c
index 0583d56..41ba848 100644
--- a/drivers/media/pci/ttpci/av7110_hw.c
+++ b/drivers/media/pci/ttpci/av7110_hw.c
@@ -56,11 +56,11 @@
    by Nathan Laredo <laredo@gnu.org> */
 
 int av7110_debiwrite(struct av7110 *av7110, u32 config,
-		     int addr, u32 val, int count)
+		     int addr, u32 val, unsigned int count)
 {
 	struct saa7146_dev *dev = av7110->dev;
 
-	if (count <= 0 || count > 32764) {
+	if (count > 32764) {
 		printk("%s: invalid count %d\n", __func__, count);
 		return -1;
 	}
@@ -78,12 +78,12 @@
 	return 0;
 }
 
-u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, int count)
+u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int count)
 {
 	struct saa7146_dev *dev = av7110->dev;
 	u32 result = 0;
 
-	if (count > 32764 || count <= 0) {
+	if (count > 32764) {
 		printk("%s: invalid count %d\n", __func__, count);
 		return 0;
 	}
diff --git a/drivers/media/pci/ttpci/av7110_hw.h b/drivers/media/pci/ttpci/av7110_hw.h
index 1634aba..ccb1480 100644
--- a/drivers/media/pci/ttpci/av7110_hw.h
+++ b/drivers/media/pci/ttpci/av7110_hw.h
@@ -377,14 +377,14 @@
 
 /* DEBI (saa7146 data extension bus interface) access */
 extern int av7110_debiwrite(struct av7110 *av7110, u32 config,
-			    int addr, u32 val, int count);
+			    int addr, u32 val, unsigned int count);
 extern u32 av7110_debiread(struct av7110 *av7110, u32 config,
-			   int addr, int count);
+			   int addr, unsigned int count);
 
 
 /* DEBI during interrupt */
 /* single word writes */
-static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
 {
 	av7110_debiwrite(av7110, config, addr, val, count);
 }
@@ -397,7 +397,7 @@
 	av7110_debiwrite(av7110, config, addr, 0, count);
 }
 
-static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline u32 irdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
 {
 	u32 res;
 
@@ -408,7 +408,7 @@
 }
 
 /* DEBI outside interrupts, only for count <= 4! */
-static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline void wdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
 {
 	unsigned long flags;
 
@@ -417,7 +417,7 @@
 	spin_unlock_irqrestore(&av7110->debilock, flags);
 }
 
-static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, int count)
+static inline u32 rdebi(struct av7110 *av7110, u32 config, int addr, u32 val, unsigned int count)
 {
 	unsigned long flags;
 	u32 res;
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c
index 6efb2f1..bdb7a0a 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/davinci/vpfe_capture.c
@@ -1725,27 +1725,9 @@
 
 	switch (cmd) {
 	case VPFE_CMD_S_CCDC_RAW_PARAMS:
+		ret = -EINVAL;
 		v4l2_warn(&vpfe_dev->v4l2_dev,
-			  "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n");
-		if (ccdc_dev->hw_ops.set_params) {
-			ret = ccdc_dev->hw_ops.set_params(param);
-			if (ret) {
-				v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
-					"Error setting parameters in CCDC\n");
-				goto unlock_out;
-			}
-			ret = vpfe_get_ccdc_image_format(vpfe_dev,
-							 &vpfe_dev->fmt);
-			if (ret < 0) {
-				v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
-					"Invalid image format at CCDC\n");
-				goto unlock_out;
-			}
-		} else {
-			ret = -EINVAL;
-			v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev,
-				"VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
-		}
+			"VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n");
 		break;
 	default:
 		ret = -ENOTTY;
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c
index 787bd16..bbb5fee 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/exynos-gsc/gsc-core.c
@@ -849,9 +849,7 @@
 
 	if ((frame->fmt->pixelformat == V4L2_PIX_FMT_VYUY) ||
 		(frame->fmt->pixelformat == V4L2_PIX_FMT_YVYU) ||
-		(frame->fmt->pixelformat == V4L2_PIX_FMT_NV61) ||
 		(frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420) ||
-		(frame->fmt->pixelformat == V4L2_PIX_FMT_NV21) ||
 		(frame->fmt->pixelformat == V4L2_PIX_FMT_YVU420M))
 		swap(addr->cb, addr->cr);
 
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/exynos4-is/fimc-is.c
index 518ad34..7f92144 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/exynos4-is/fimc-is.c
@@ -825,12 +825,13 @@
 	is->irq = irq_of_parse_and_map(dev->of_node, 0);
 	if (!is->irq) {
 		dev_err(dev, "no irq found\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_iounmap;
 	}
 
 	ret = fimc_is_get_clocks(is);
 	if (ret < 0)
-		return ret;
+		goto err_iounmap;
 
 	platform_set_drvdata(pdev, is);
 
@@ -891,6 +892,8 @@
 	free_irq(is->irq, is);
 err_clk:
 	fimc_is_put_clocks(is);
+err_iounmap:
+	iounmap(is->pmu_regs);
 	return ret;
 }
 
@@ -947,6 +950,7 @@
 	fimc_is_unregister_subdevs(is);
 	vb2_dma_contig_clear_max_seg_size(dev);
 	fimc_is_put_clocks(is);
+	iounmap(is->pmu_regs);
 	fimc_is_debugfs_remove(is);
 	release_firmware(is->fw.f_w);
 	fimc_is_free_cpu_memory(is);
diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
index 2c90e47..be7f4a1 100644
--- a/drivers/media/platform/msm/broadcast/tspp.c
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -36,7 +36,6 @@
 #include <linux/regulator/consumer.h>
 #include <dt-bindings/regulator/qcom,rpmh-regulator.h>
 #include <linux/msm-sps.h>            /* BAM stuff */
-#include <linux/wakelock.h>      /* Locking functions */
 #include <linux/timer.h>         /* Timer services */
 #include <linux/jiffies.h>       /* Jiffies counter */
 #include <linux/qcom_tspp.h>
diff --git a/drivers/media/platform/msm/camera/Makefile b/drivers/media/platform/msm/camera/Makefile
index 48fa1c0..9e0aee9 100644
--- a/drivers/media/platform/msm/camera/Makefile
+++ b/drivers/media/platform/msm/camera/Makefile
@@ -10,3 +10,4 @@
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_icp/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_jpeg/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_fd/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme/
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
index 6c8bde1..6d699cf 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.c
@@ -67,11 +67,15 @@
 	return false;
 }
 
-void cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
-	enum cam_camnoc_irq_type evt_type, uint32_t evt_data)
+bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
+	struct cam_cpas_irq_data *irq_data)
 {
-	CAM_ERR(CAM_CDM, "CPAS error callback type=%d with data=%x", evt_type,
-		evt_data);
+	if (!irq_data)
+		return false;
+
+	CAM_DBG(CAM_CDM, "CPAS error callback type=%d", irq_data->irq_type);
+
+	return false;
 }
 
 struct cam_cdm_utils_ops *cam_cdm_get_ops(
@@ -338,6 +342,7 @@
 				}
 			} else {
 				client->stream_on = false;
+				rc = 0;
 				CAM_DBG(CAM_CDM,
 					"Client stream off success =%d",
 					cdm_hw->open_count);
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
index fa3ae04..497832b 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_core_common.h
@@ -32,8 +32,8 @@
 	uint32_t arg_size);
 bool cam_cdm_set_cam_hw_version(
 	uint32_t ver, struct cam_hw_version *cam_version);
-void cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
-	enum cam_camnoc_irq_type evt_type, uint32_t evt_data);
+bool cam_cdm_cpas_cb(uint32_t client_handle, void *userdata,
+	struct cam_cpas_irq_data *irq_data);
 struct cam_cdm_utils_ops *cam_cdm_get_ops(
 	uint32_t ver, struct cam_hw_version *cam_version, bool by_cam_version);
 int cam_virtual_cdm_submit_bl(struct cam_hw_info *cdm_hw,
diff --git a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
index b230d4e..d76f344 100644
--- a/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
+++ b/drivers/media/platform/msm/camera/cam_cdm/cam_cdm_virtual_core.c
@@ -131,8 +131,10 @@
 				cdm_cmd->cmd[i].len, client->data.base_array,
 				client->data.base_array_cnt, core->bl_tag);
 			if (rc) {
-				CAM_ERR(CAM_CDM, "write failed for cnt=%d:%d",
-					i, req->data->cmd_arrary_count);
+				CAM_ERR(CAM_CDM,
+					"write failed for cnt=%d:%d len %u",
+					i, req->data->cmd_arrary_count,
+					cdm_cmd->cmd[i].len);
 				break;
 			}
 		} else {
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 8f625ae..84402e4 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -14,6 +14,7 @@
 #include <linux/uaccess.h>
 #include "cam_context.h"
 #include "cam_debug_util.h"
+#include "cam_node.h"
 
 static int cam_context_handle_hw_event(void *context, uint32_t evt_id,
 	void *evt_data)
@@ -36,6 +37,26 @@
 	return rc;
 }
 
+int cam_context_shutdown(struct cam_context *ctx)
+{
+	int rc = 0;
+
+	if (ctx->state_machine[ctx->state].ioctl_ops.stop_dev) {
+		rc = ctx->state_machine[ctx->state].ioctl_ops.stop_dev(
+			ctx, NULL);
+		if (rc < 0)
+			CAM_ERR(CAM_CORE, "Error while dev stop %d", rc);
+	}
+	if (ctx->state_machine[ctx->state].ioctl_ops.release_dev) {
+		rc = ctx->state_machine[ctx->state].ioctl_ops.release_dev(
+			ctx, NULL);
+		if (rc < 0)
+			CAM_ERR(CAM_CORE, "Error while dev release %d", rc);
+	}
+
+	return rc;
+}
+
 int cam_context_handle_crm_get_dev_info(struct cam_context *ctx,
 	struct cam_req_mgr_device_info *info)
 {
@@ -113,8 +134,8 @@
 		rc = ctx->state_machine[ctx->state].crm_ops.unlink(
 			ctx, unlink);
 	} else {
-		CAM_ERR(CAM_CORE, "No crm unlink in dev %d, state %d",
-			ctx->dev_hdl, ctx->state);
+		CAM_ERR(CAM_CORE, "No crm unlink in dev %d, name %s, state %d",
+			ctx->dev_hdl, ctx->dev_name, ctx->state);
 		rc = -EPROTO;
 	}
 	mutex_unlock(&ctx->ctx_mutex);
@@ -312,14 +333,15 @@
 			ctx, cmd);
 	else
 		/* stop device can be optional for some driver */
-		CAM_WARN(CAM_CORE, "No stop device in dev %d, state %d",
-			ctx->dev_hdl, ctx->state);
+		CAM_WARN(CAM_CORE, "No stop device in dev %d, name %s state %d",
+			ctx->dev_hdl, ctx->dev_name, ctx->state);
 	mutex_unlock(&ctx->ctx_mutex);
 
 	return rc;
 }
 
 int cam_context_init(struct cam_context *ctx,
+	const char *dev_name,
 	struct cam_req_mgr_kmd_ops *crm_node_intf,
 	struct cam_hw_mgr_intf *hw_mgr_intf,
 	struct cam_ctx_request *req_list,
@@ -334,11 +356,15 @@
 	}
 
 	memset(ctx, 0, sizeof(*ctx));
-
+	ctx->dev_hdl = -1;
+	ctx->link_hdl = -1;
+	ctx->session_hdl = -1;
 	INIT_LIST_HEAD(&ctx->list);
 	mutex_init(&ctx->ctx_mutex);
+	mutex_init(&ctx->sync_mutex);
 	spin_lock_init(&ctx->lock);
 
+	ctx->dev_name = dev_name;
 	ctx->ctx_crm_intf = NULL;
 	ctx->crm_ctx_intf = crm_node_intf;
 	ctx->hw_mgr_intf = hw_mgr_intf;
@@ -353,6 +379,7 @@
 	for (i = 0; i < req_size; i++) {
 		INIT_LIST_HEAD(&ctx->req_list[i].list);
 		list_add_tail(&ctx->req_list[i].list, &ctx->free_req_list);
+		ctx->req_list[i].ctx = ctx;
 	}
 	ctx->state = CAM_CTX_AVAILABLE;
 	ctx->state_machine = NULL;
@@ -378,3 +405,20 @@
 
 	return 0;
 }
+
+void cam_context_putref(struct cam_context *ctx)
+{
+	kref_put(&ctx->refcount, cam_node_put_ctxt_to_free_list);
+	CAM_DBG(CAM_CORE, "ctx device hdl %ld, ref count %d",
+		ctx->dev_hdl, atomic_read(&(ctx->refcount.refcount)));
+}
+
+void cam_context_getref(struct cam_context *ctx)
+{
+	if (kref_get_unless_zero(&ctx->refcount) == 0) {
+		/* should never happen */
+		WARN(1, "cam_context_getref fail\n");
+	}
+	CAM_DBG(CAM_CORE, "ctx device hdl %ld, ref count %d",
+		ctx->dev_hdl, atomic_read(&(ctx->refcount.refcount)));
+}
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context.h b/drivers/media/platform/msm/camera/cam_core/cam_context.h
index d87c984..6d1589e 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -15,6 +15,7 @@
 
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
+#include <linux/kref.h>
 #include "cam_req_mgr_interface.h"
 #include "cam_hw_mgr_intf.h"
 
@@ -54,6 +55,8 @@
  * @num_out_map_entries:   Number of out map entries
  * @num_in_acked:          Number of in fence acked
  * @num_out_acked:         Number of out fence acked
+ * @flushed:               Request is flushed
+ * @ctx:                   The context to which this request belongs
  *
  */
 struct cam_ctx_request {
@@ -69,6 +72,8 @@
 	uint32_t                      num_out_map_entries;
 	uint32_t                      num_in_acked;
 	uint32_t                      num_out_acked;
+	int                           flushed;
+	struct cam_context           *ctx;
 };
 
 /**
@@ -135,6 +140,7 @@
 /**
  * struct cam_context - camera context object for the subdevice node
  *
+ * @dev_name:              String giving name of device associated
  * @list:                  Link list entry
  * @sessoin_hdl:           Session handle
  * @dev_hdl:               Device handle
@@ -155,9 +161,13 @@
  * @state_machine:         Top level state machine
  * @ctx_priv:              Private context pointer
  * @ctxt_to_hw_map:        Context to hardware mapping pointer
+ * @refcount:              Context object refcount
+ * @node:                  The main node to which this context belongs
+ * @sync_mutex:            mutex to sync with sync cb thread
  *
  */
 struct cam_context {
+	const char                  *dev_name;
 	struct list_head             list;
 	int32_t                      session_hdl;
 	int32_t                      dev_hdl;
@@ -183,15 +193,29 @@
 
 	void                        *ctx_priv;
 	void                        *ctxt_to_hw_map;
+
+	struct kref                  refcount;
+	void                        *node;
+	struct mutex                 sync_mutex;
 };
 
 /**
+ * cam_context_shutdown()
+ *
+ * @brief:        Calls while device close or shutdown
+ *
+ * @ctx:          Object pointer for cam_context
+ *
+ */
+int cam_context_shutdown(struct cam_context *ctx);
+
+/**
  * cam_context_handle_crm_get_dev_info()
  *
  * @brief:        Handle get device information command
  *
- * @ctx:                   Object pointer for cam_context
- * @info:                  Device information returned
+ * @ctx:          Object pointer for cam_context
+ * @info:         Device information returned
  *
  */
 int cam_context_handle_crm_get_dev_info(struct cam_context *ctx,
@@ -202,8 +226,8 @@
  *
  * @brief:        Handle link command
  *
- * @ctx:                   Object pointer for cam_context
- * @link:                  Link command payload
+ * @ctx:          Object pointer for cam_context
+ * @link:         Link command payload
  *
  */
 int cam_context_handle_crm_link(struct cam_context *ctx,
@@ -214,8 +238,8 @@
  *
  * @brief:        Handle unlink command
  *
- * @ctx:                   Object pointer for cam_context
- * @unlink:                Unlink command payload
+ * @ctx:          Object pointer for cam_context
+ * @unlink:       Unlink command payload
  *
  */
 int cam_context_handle_crm_unlink(struct cam_context *ctx,
@@ -226,8 +250,8 @@
  *
  * @brief:        Handle apply request command
  *
- * @ctx:                   Object pointer for cam_context
- * @apply:                 Apply request command payload
+ * @ctx:          Object pointer for cam_context
+ * @apply:        Apply request command payload
  *
  */
 int cam_context_handle_crm_apply_req(struct cam_context *ctx,
@@ -238,8 +262,8 @@
  *
  * @brief:        Handle flush request command
  *
- * @ctx:                   Object pointer for cam_context
- * @apply:                 Flush request command payload
+ * @ctx:          Object pointer for cam_context
+ * @apply:        Flush request command payload
  *
  */
 int cam_context_handle_crm_flush_req(struct cam_context *ctx,
@@ -250,8 +274,8 @@
  *
  * @brief:        Handle acquire device command
  *
- * @ctx:                   Object pointer for cam_context
- * @cmd:                   Acquire device command payload
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Acquire device command payload
  *
  */
 int cam_context_handle_acquire_dev(struct cam_context *ctx,
@@ -262,8 +286,8 @@
  *
  * @brief:        Handle release device command
  *
- * @ctx:                   Object pointer for cam_context
- * @cmd:                   Release device command payload
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Release device command payload
  *
  */
 int cam_context_handle_release_dev(struct cam_context *ctx,
@@ -274,8 +298,8 @@
  *
  * @brief:        Handle config device command
  *
- * @ctx:                   Object pointer for cam_context
- * @cmd:                   Config device command payload
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Config device command payload
  *
  */
 int cam_context_handle_config_dev(struct cam_context *ctx,
@@ -286,8 +310,8 @@
  *
  * @brief:        Handle start device command
  *
- * @ctx:                   Object pointer for cam_context
- * @cmd:                   Start device command payload
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Start device command payload
  *
  */
 int cam_context_handle_start_dev(struct cam_context *ctx,
@@ -298,8 +322,8 @@
  *
  * @brief:        Handle stop device command
  *
- * @ctx:                   Object pointer for cam_context
- * @cmd:                   Stop device command payload
+ * @ctx:          Object pointer for cam_context
+ * @cmd:          Stop device command payload
  *
  */
 int cam_context_handle_stop_dev(struct cam_context *ctx,
@@ -310,7 +334,7 @@
  *
  * @brief:        Camera context deinitialize function
  *
- * @ctx:                   Object pointer for cam_context
+ * @ctx:          Object pointer for cam_context
  *
  */
 int cam_context_deinit(struct cam_context *ctx);
@@ -321,6 +345,7 @@
  * @brief:        Camera context initialize function
  *
  * @ctx:                   Object pointer for cam_context
+ * @dev_name:              String giving name of device associated
  * @crm_node_intf:         Function table for crm to context interface
  * @hw_mgr_intf:           Function table for context to hw interface
  * @req_list:              Requests storage
@@ -328,10 +353,30 @@
  *
  */
 int cam_context_init(struct cam_context *ctx,
+		const char *dev_name,
 		struct cam_req_mgr_kmd_ops *crm_node_intf,
 		struct cam_hw_mgr_intf *hw_mgr_intf,
 		struct cam_ctx_request *req_list,
 		uint32_t req_size);
 
+/**
+ * cam_context_putref()
+ *
+ * @brief:       Put back context reference.
+ *
+ * @ctx:                  Context for which ref is returned
+ *
+ */
+void cam_context_putref(struct cam_context *ctx);
+
+/**
+ * cam_context_getref()
+ *
+ * @brief:       Get back context reference.
+ *
+ * @ctx:                  Context for which ref is taken
+ *
+ */
+void cam_context_getref(struct cam_context *ctx);
 
 #endif  /* _CAM_CONTEXT_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
index 01c629d..0a1c2cf 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.c
@@ -18,6 +18,7 @@
 #include <media/cam_defs.h>
 
 #include "cam_context.h"
+#include "cam_context_utils.h"
 #include "cam_mem_mgr.h"
 #include "cam_node.h"
 #include "cam_req_mgr_util.h"
@@ -25,6 +26,15 @@
 #include "cam_trace.h"
 #include "cam_debug_util.h"
 
+static inline int cam_context_validate_thread(void)
+{
+	if (in_interrupt()) {
+		WARN(1, "Invalid execution context\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
 int cam_context_buf_done_from_hw(struct cam_context *ctx,
 	void *done_event_data, uint32_t bubble_state)
 {
@@ -33,17 +43,23 @@
 	struct cam_ctx_request *req;
 	struct cam_hw_done_event_data *done =
 		(struct cam_hw_done_event_data *)done_event_data;
+	int rc;
 
 	if (!ctx || !done) {
 		CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, done);
 		return -EINVAL;
 	}
 
+	rc = cam_context_validate_thread();
+	if (rc)
+		return rc;
+
+	spin_lock(&ctx->lock);
 	if (list_empty(&ctx->active_req_list)) {
 		CAM_ERR(CAM_CTXT, "no active request");
+		spin_unlock(&ctx->lock);
 		return -EIO;
 	}
-
 	req = list_first_entry(&ctx->active_req_list,
 		struct cam_ctx_request, list);
 
@@ -52,15 +68,22 @@
 	if (done->request_id != req->request_id) {
 		CAM_ERR(CAM_CTXT, "mismatch: done req[%lld], active req[%lld]",
 			done->request_id, req->request_id);
+		spin_unlock(&ctx->lock);
 		return -EIO;
 	}
 
 	if (!req->num_out_map_entries) {
 		CAM_ERR(CAM_CTXT, "no output fence to signal");
+		spin_unlock(&ctx->lock);
 		return -EIO;
 	}
 
+	/*
+	 * since another thread may be adding/removing from active
+	 * list, so hold the lock
+	 */
 	list_del_init(&req->list);
+	spin_unlock(&ctx->lock);
 	if (!bubble_state)
 		result = CAM_SYNC_STATE_SIGNALED_SUCCESS;
 	else
@@ -71,41 +94,34 @@
 		req->out_map_entries[j].sync_id = -1;
 	}
 
+	/*
+	 * another thread may be adding/removing from free list,
+	 * so hold the lock
+	 */
+	spin_lock(&ctx->lock);
 	list_add_tail(&req->list, &ctx->free_req_list);
+	req->ctx = NULL;
+	spin_unlock(&ctx->lock);
 
 	return 0;
 }
 
-int cam_context_apply_req_to_hw(struct cam_context *ctx,
+static int cam_context_apply_req_to_hw(struct cam_ctx_request *req,
 	struct cam_req_mgr_apply_request *apply)
 {
 	int rc = 0;
-	struct cam_ctx_request *req;
+	struct cam_context *ctx = req->ctx;
 	struct cam_hw_config_args cfg;
 
-	if (!ctx || !apply) {
-		CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, apply);
-		rc = -EINVAL;
-		goto end;
-	}
-
 	if (!ctx->hw_mgr_intf) {
 		CAM_ERR(CAM_CTXT, "HW interface is not ready");
 		rc = -EFAULT;
 		goto end;
 	}
 
-	if (list_empty(&ctx->pending_req_list)) {
-		CAM_ERR(CAM_CTXT, "No available request for Apply id %lld",
-			apply->request_id);
-		rc = -EFAULT;
-		goto end;
-	}
-
 	spin_lock(&ctx->lock);
-	req = list_first_entry(&ctx->pending_req_list,
-		struct cam_ctx_request, list);
 	list_del_init(&req->list);
+	list_add_tail(&req->list, &ctx->active_req_list);
 	spin_unlock(&ctx->lock);
 
 	cfg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
@@ -114,11 +130,13 @@
 	cfg.out_map_entries = req->out_map_entries;
 	cfg.num_out_map_entries = req->num_out_map_entries;
 	cfg.priv = req->req_priv;
-	list_add_tail(&req->list, &ctx->active_req_list);
 
 	rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
-	if (rc)
+	if (rc) {
+		spin_lock(&ctx->lock);
 		list_del_init(&req->list);
+		spin_unlock(&ctx->lock);
+	}
 
 end:
 	return rc;
@@ -126,39 +144,52 @@
 
 static void cam_context_sync_callback(int32_t sync_obj, int status, void *data)
 {
-	struct cam_context *ctx = data;
-	struct cam_ctx_request *req = NULL;
+	struct cam_ctx_request *req = data;
+	struct cam_context *ctx = NULL;
 	struct cam_req_mgr_apply_request apply;
+	int rc;
 
-	if (!ctx) {
+	if (!req) {
 		CAM_ERR(CAM_CTXT, "Invalid input param");
 		return;
 	}
-
-	spin_lock(&ctx->lock);
-	if (!list_empty(&ctx->pending_req_list))
-		req = list_first_entry(&ctx->pending_req_list,
-			struct cam_ctx_request, list);
-	spin_unlock(&ctx->lock);
-
-	if (!req) {
-		CAM_ERR(CAM_CTXT, "No more request obj free");
+	rc = cam_context_validate_thread();
+	if (rc)
 		return;
-	}
 
+	ctx = req->ctx;
 	req->num_in_acked++;
 	if (req->num_in_acked == req->num_in_map_entries) {
 		apply.request_id = req->request_id;
-		cam_context_apply_req_to_hw(ctx, &apply);
+		/*
+		 * take mutex to ensure that another thread does
+		 * not flush the request while this
+		 * thread is submitting it to h/w. The submit to
+		 * h/w and adding to the active list should happen
+		 * in a critical section which is provided by this
+		 * mutex.
+		 */
+		mutex_lock(&ctx->sync_mutex);
+		if (!req->flushed) {
+			cam_context_apply_req_to_hw(req, &apply);
+			mutex_unlock(&ctx->sync_mutex);
+		} else {
+			mutex_unlock(&ctx->sync_mutex);
+			req->ctx = NULL;
+			req->flushed = 0;
+			spin_lock(&ctx->lock);
+			list_del_init(&req->list);
+			list_add_tail(&req->list, &ctx->free_req_list);
+			spin_unlock(&ctx->lock);
+		}
 	}
+	cam_context_putref(ctx);
 }
 
 int32_t cam_context_release_dev_to_hw(struct cam_context *ctx,
 	struct cam_release_dev_cmd *cmd)
 {
-	int i;
 	struct cam_hw_release_args arg;
-	struct cam_ctx_request *req;
 
 	if (!ctx) {
 		CAM_ERR(CAM_CTXT, "Invalid input param");
@@ -171,50 +202,14 @@
 	}
 
 	arg.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
-	if ((list_empty(&ctx->active_req_list)) &&
-		(list_empty(&ctx->pending_req_list)))
-		arg.active_req = false;
-	else
-		arg.active_req = true;
+	arg.active_req = false;
 
 	ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv, &arg);
 	ctx->ctxt_to_hw_map = NULL;
 
-	ctx->session_hdl = 0;
-	ctx->dev_hdl = 0;
-	ctx->link_hdl = 0;
-
-	while (!list_empty(&ctx->active_req_list)) {
-		req = list_first_entry(&ctx->active_req_list,
-			struct cam_ctx_request, list);
-		list_del_init(&req->list);
-		CAM_DBG(CAM_CTXT, "signal fence in active list, num %d",
-			req->num_out_map_entries);
-		for (i = 0; i < req->num_out_map_entries; i++) {
-			if (req->out_map_entries[i].sync_id > 0)
-				cam_sync_signal(req->out_map_entries[i].sync_id,
-					CAM_SYNC_STATE_SIGNALED_ERROR);
-		}
-		list_add_tail(&req->list, &ctx->free_req_list);
-	}
-
-	while (!list_empty(&ctx->pending_req_list)) {
-		req = list_first_entry(&ctx->pending_req_list,
-			struct cam_ctx_request, list);
-		list_del_init(&req->list);
-		for (i = 0; i < req->num_in_map_entries; i++)
-			if (req->in_map_entries[i].sync_id > 0)
-				cam_sync_deregister_callback(
-					cam_context_sync_callback, ctx,
-					req->in_map_entries[i].sync_id);
-		CAM_DBG(CAM_CTXT, "signal fence in pending list, num %d",
-			req->num_out_map_entries);
-		for (i = 0; i < req->num_out_map_entries; i++)
-			if (req->out_map_entries[i].sync_id > 0)
-				cam_sync_signal(req->out_map_entries[i].sync_id,
-					CAM_SYNC_STATE_SIGNALED_ERROR);
-		list_add_tail(&req->list, &ctx->free_req_list);
-	}
+	ctx->session_hdl = -1;
+	ctx->dev_hdl = -1;
+	ctx->link_hdl = -1;
 
 	return 0;
 }
@@ -241,6 +236,9 @@
 		rc = -EFAULT;
 		goto end;
 	}
+	rc = cam_context_validate_thread();
+	if (rc)
+		return rc;
 
 	spin_lock(&ctx->lock);
 	if (!list_empty(&ctx->free_req_list)) {
@@ -258,6 +256,7 @@
 
 	memset(req, 0, sizeof(*req));
 	INIT_LIST_HEAD(&req->list);
+	req->ctx = ctx;
 
 	/* for config dev, only memory handle is supported */
 	/* map packet from the memhandle */
@@ -303,10 +302,18 @@
 		list_add_tail(&req->list, &ctx->pending_req_list);
 		spin_unlock(&ctx->lock);
 		for (i = 0; i < req->num_in_map_entries; i++) {
+			cam_context_getref(ctx);
 			rc = cam_sync_register_callback(
 					cam_context_sync_callback,
-					(void *)ctx,
+					(void *)req,
 					req->in_map_entries[i].sync_id);
+			if (rc) {
+				CAM_ERR(CAM_CTXT,
+					"Failed register fence cb: %d ret = %d",
+					req->in_map_entries[i].sync_id, rc);
+				cam_context_putref(ctx);
+				goto free_req;
+			}
 			CAM_DBG(CAM_CTXT, "register in fence cb: %d ret = %d",
 				req->in_map_entries[i].sync_id, rc);
 		}
@@ -318,6 +325,7 @@
 free_req:
 	spin_lock(&ctx->lock);
 	list_add_tail(&req->list, &ctx->free_req_list);
+	req->ctx = NULL;
 	spin_unlock(&ctx->lock);
 end:
 	return rc;
@@ -452,6 +460,7 @@
 	uint32_t i;
 	struct cam_hw_stop_args stop;
 	struct cam_ctx_request *req;
+	struct list_head temp_list;
 
 	if (!ctx) {
 		CAM_ERR(CAM_CTXT, "Invalid input param");
@@ -465,6 +474,32 @@
 		goto end;
 	}
 
+	rc = cam_context_validate_thread();
+	if (rc)
+		goto end;
+
+	/*
+	 * flush pending requests, take the sync lock to synchronize with the
+	 * sync callback thread so that the sync cb thread does not try to
+	 * submit request to h/w while the request is being flushed
+	 */
+	mutex_lock(&ctx->sync_mutex);
+	INIT_LIST_HEAD(&temp_list);
+	spin_lock(&ctx->lock);
+	list_splice_init(&ctx->pending_req_list, &temp_list);
+	spin_unlock(&ctx->lock);
+	while (!list_empty(&temp_list)) {
+		req = list_first_entry(&temp_list,
+				struct cam_ctx_request, list);
+		list_del_init(&req->list);
+		req->flushed = 1;
+		for (i = 0; i < req->num_out_map_entries; i++)
+			if (req->out_map_entries[i].sync_id != -1)
+				cam_sync_signal(req->out_map_entries[i].sync_id,
+					CAM_SYNC_STATE_SIGNALED_ERROR);
+	}
+	mutex_unlock(&ctx->sync_mutex);
+
 	/* stop hw first */
 	if (ctx->ctxt_to_hw_map) {
 		stop.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
@@ -473,22 +508,17 @@
 				&stop);
 	}
 
-	/* flush pending and active queue */
-	while (!list_empty(&ctx->pending_req_list)) {
-		req = list_first_entry(&ctx->pending_req_list,
-				struct cam_ctx_request, list);
-		list_del_init(&req->list);
-		CAM_DBG(CAM_CTXT, "signal fence in pending list. fence num %d",
-			req->num_out_map_entries);
-		for (i = 0; i < req->num_out_map_entries; i++)
-			if (req->out_map_entries[i].sync_id != -1)
-				cam_sync_signal(req->out_map_entries[i].sync_id,
-					CAM_SYNC_STATE_SIGNALED_ERROR);
-		list_add_tail(&req->list, &ctx->free_req_list);
-	}
+	/*
+	 * flush active queue, at this point h/w layer below does not have any
+	 * reference to requests in active queue.
+	 */
+	INIT_LIST_HEAD(&temp_list);
+	spin_lock(&ctx->lock);
+	list_splice_init(&ctx->active_req_list, &temp_list);
+	spin_unlock(&ctx->lock);
 
-	while (!list_empty(&ctx->active_req_list)) {
-		req = list_first_entry(&ctx->active_req_list,
+	while (!list_empty(&temp_list)) {
+		req = list_first_entry(&temp_list,
 				struct cam_ctx_request, list);
 		list_del_init(&req->list);
 		CAM_DBG(CAM_CTXT, "signal fence in active list. fence num %d",
@@ -497,7 +527,14 @@
 			if (req->out_map_entries[i].sync_id != -1)
 				cam_sync_signal(req->out_map_entries[i].sync_id,
 					CAM_SYNC_STATE_SIGNALED_ERROR);
+		/*
+		 * The spin lock should be taken here to guard the free list,
+		 * as sync cb thread could be adding a pending req to free list
+		 */
+		spin_lock(&ctx->lock);
 		list_add_tail(&req->list, &ctx->free_req_list);
+		req->ctx = NULL;
+		spin_unlock(&ctx->lock);
 	}
 
 end:
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h
index f7982eb..45d9e56 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context_utils.h
@@ -17,8 +17,6 @@
 
 int cam_context_buf_done_from_hw(struct cam_context *ctx,
 	void *done_event_data, uint32_t bubble_state);
-int cam_context_apply_req_to_hw(struct cam_context *ctx,
-	struct cam_req_mgr_apply_request *apply);
 int32_t cam_context_release_dev_to_hw(struct cam_context *ctx,
 	struct cam_release_dev_cmd *cmd);
 int32_t cam_context_prepare_dev_to_hw(struct cam_context *ctx,
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
index aab75d5..4746152 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_hw_mgr_intf.h
@@ -203,8 +203,8 @@
  * @hw_write:              Function pointer for Write hardware registers
  * @hw_cmd:                Function pointer for any customized commands for the
  *                         hardware manager
- * @download_fw:           Function pointer for firmware downloading
- * @hw_close:              Function pointer for subdev close
+ * @hw_open:               Function pointer for HW init
+ * @hw_close:              Function pointer for HW deinit
  *
  */
 struct cam_hw_mgr_intf {
@@ -220,7 +220,7 @@
 	int (*hw_read)(void *hw_priv, void *read_args);
 	int (*hw_write)(void *hw_priv, void *write_args);
 	int (*hw_cmd)(void *hw_priv, void *write_args);
-	int (*download_fw)(void *hw_priv, void *fw_download_args);
+	int (*hw_open)(void *hw_priv, void *fw_download_args);
 	int (*hw_close)(void *hw_priv, void *hw_close_args);
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.c b/drivers/media/platform/msm/camera/cam_core/cam_node.c
index 043f44d..1f0213e 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -17,11 +17,33 @@
 #include "cam_node.h"
 #include "cam_trace.h"
 #include "cam_debug_util.h"
-static void  __cam_node_handle_shutdown(struct cam_node *node)
+
+static struct cam_context *cam_node_get_ctxt_from_free_list(
+		struct cam_node *node)
 {
-	if (node->hw_mgr_intf.hw_close)
-		node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
-			NULL);
+	struct cam_context *ctx = NULL;
+
+	mutex_lock(&node->list_mutex);
+	if (!list_empty(&node->free_ctx_list)) {
+		ctx = list_first_entry(&node->free_ctx_list,
+			struct cam_context, list);
+		list_del_init(&ctx->list);
+	}
+	mutex_unlock(&node->list_mutex);
+	if (ctx)
+		kref_init(&ctx->refcount);
+	return ctx;
+}
+
+void cam_node_put_ctxt_to_free_list(struct kref *ref)
+{
+	struct cam_context *ctx =
+		container_of(ref, struct cam_context, refcount);
+	struct cam_node *node = ctx->node;
+
+	mutex_lock(&node->list_mutex);
+	list_add_tail(&ctx->list, &node->free_ctx_list);
+	mutex_unlock(&node->list_mutex);
 }
 
 static int __cam_node_handle_query_cap(struct cam_node *node,
@@ -51,13 +73,7 @@
 	if (!acquire)
 		return -EINVAL;
 
-	mutex_lock(&node->list_mutex);
-	if (!list_empty(&node->free_ctx_list)) {
-		ctx = list_first_entry(&node->free_ctx_list,
-			struct cam_context, list);
-		list_del_init(&ctx->list);
-	}
-	mutex_unlock(&node->list_mutex);
+	ctx = cam_node_get_ctxt_from_free_list(node);
 	if (!ctx) {
 		rc = -ENOMEM;
 		goto err;
@@ -65,15 +81,14 @@
 
 	rc = cam_context_handle_acquire_dev(ctx, acquire);
 	if (rc) {
-		CAM_ERR(CAM_CORE, "Acquire device failed");
+		CAM_ERR(CAM_CORE, "Acquire device failed for node %s",
+			node->name);
 		goto free_ctx;
 	}
 
 	return 0;
 free_ctx:
-	mutex_lock(&node->list_mutex);
-	list_add_tail(&ctx->list, &node->free_ctx_list);
-	mutex_unlock(&node->list_mutex);
+	cam_context_putref(ctx);
 err:
 	return rc;
 }
@@ -82,6 +97,7 @@
 	struct cam_start_stop_dev_cmd *start)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!start)
 		return -EINVAL;
@@ -103,13 +119,18 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_start_dev(ctx, start);
+	rc = cam_context_handle_start_dev(ctx, start);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Start failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_stop_dev(struct cam_node *node,
 	struct cam_start_stop_dev_cmd *stop)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!stop)
 		return -EINVAL;
@@ -131,13 +152,18 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_stop_dev(ctx, stop);
+	rc = cam_context_handle_stop_dev(ctx, stop);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Stop failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_config_dev(struct cam_node *node,
 	struct cam_config_dev_cmd *config)
 {
 	struct cam_context *ctx = NULL;
+	int rc;
 
 	if (!config)
 		return -EINVAL;
@@ -159,7 +185,11 @@
 		return -EINVAL;
 	}
 
-	return cam_context_handle_config_dev(ctx, config);
+	rc = cam_context_handle_config_dev(ctx, config);
+	if (rc)
+		CAM_ERR(CAM_CORE, "Config failure for node %s", node->name);
+
+	return rc;
 }
 
 static int __cam_node_handle_release_dev(struct cam_node *node,
@@ -183,22 +213,21 @@
 
 	ctx = (struct cam_context *)cam_get_device_priv(release->dev_handle);
 	if (!ctx) {
-		CAM_ERR(CAM_CORE, "Can not get context for handle %d",
-			release->dev_handle);
+		CAM_ERR(CAM_CORE, "Can not get context for handle %d node %s",
+			release->dev_handle, node->name);
 		return -EINVAL;
 	}
 
 	rc = cam_context_handle_release_dev(ctx, release);
 	if (rc)
-		CAM_ERR(CAM_CORE, "context release failed");
+		CAM_ERR(CAM_CORE, "context release failed node %s", node->name);
 
 	rc = cam_destroy_device_hdl(release->dev_handle);
 	if (rc)
-		CAM_ERR(CAM_CORE, "destroy device handle is failed");
+		CAM_ERR(CAM_CORE, "destroy device handle is failed node %s",
+			node->name);
 
-	mutex_lock(&node->list_mutex);
-	list_add_tail(&ctx->list, &node->free_ctx_list);
-	mutex_unlock(&node->list_mutex);
+	cam_context_putref(ctx);
 	return rc;
 }
 
@@ -256,7 +285,7 @@
 		return -EINVAL;
 	}
 
-	trace_cam_apply_req("Node", apply);
+	trace_cam_apply_req("Node", apply->request_id);
 
 	return cam_context_handle_crm_apply_req(ctx, apply);
 }
@@ -290,6 +319,28 @@
 	return 0;
 }
 
+int cam_node_shutdown(struct cam_node *node)
+{
+	int i = 0;
+
+	if (!node)
+		return -EINVAL;
+
+	for (i = 0; i < node->ctx_size; i++) {
+		if (node->ctx_list[i].dev_hdl >= 0) {
+			cam_context_shutdown(&(node->ctx_list[i]));
+			cam_destroy_device_hdl(node->ctx_list[i].dev_hdl);
+			cam_context_putref(&(node->ctx_list[i]));
+		}
+	}
+
+	if (node->hw_mgr_intf.hw_close)
+		node->hw_mgr_intf.hw_close(node->hw_mgr_intf.hw_mgr_priv,
+			NULL);
+
+	return 0;
+}
+
 int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
 	struct cam_context *ctx_list, uint32_t ctx_size, char *name)
 {
@@ -324,6 +375,7 @@
 		}
 		INIT_LIST_HEAD(&ctx_list[i].list);
 		list_add_tail(&ctx_list[i].list, &node->free_ctx_list);
+		ctx_list[i].node = node;
 	}
 
 	node->state = CAM_NODE_STATE_INIT;
@@ -439,9 +491,6 @@
 		}
 		break;
 	}
-	case CAM_SD_SHUTDOWN:
-		__cam_node_handle_shutdown(node);
-		break;
 	default:
 		CAM_ERR(CAM_CORE, "Unknown op code %d", cmd->op_code);
 		rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_core/cam_node.h b/drivers/media/platform/msm/camera/cam_core/cam_node.h
index 6e4a641..4303ee3 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.h
@@ -13,6 +13,7 @@
 #ifndef _CAM_NODE_H_
 #define _CAM_NODE_H_
 
+#include <linux/kref.h>
 #include "cam_context.h"
 #include "cam_hw_mgr_intf.h"
 #include "cam_req_mgr_interface.h"
@@ -73,6 +74,16 @@
 int cam_node_deinit(struct cam_node *node);
 
 /**
+ * cam_node_shutdown()
+ *
+ * @brief:       Shutdowns/Closes the cam node.
+ *
+ * @node:                  Cam_node pointer
+ *
+ */
+int cam_node_shutdown(struct cam_node *node);
+
+/**
  * cam_node_init()
  *
  * @brief:       Initialization function for the Node interface.
@@ -87,4 +98,14 @@
 int cam_node_init(struct cam_node *node, struct cam_hw_mgr_intf *hw_mgr_intf,
 	struct cam_context *ctx_list, uint32_t ctx_size, char *name);
 
+/**
+ * cam_node_put_ctxt_to_free_list()
+ *
+ * @brief:       Put context in node free list.
+ *
+ * @ref:         Context's kref object
+ *
+ */
+void cam_node_put_ctxt_to_free_list(struct kref *ref);
+
 #endif /* _CAM_NODE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
index 6bf81af..fc84d9d 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.c
@@ -21,6 +21,8 @@
 #include "cam_cpas_hw_intf.h"
 #include "cam_cpas_soc.h"
 
+#define CAM_CPAS_AXI_MIN_BW (2048 * 1024)
+
 int cam_cpas_util_reg_update(struct cam_hw_info *cpas_hw,
 	enum cam_cpas_reg_base reg_base, struct cam_cpas_reg *reg_info)
 {
@@ -116,6 +118,12 @@
 	bus_client->curr_vote_level = idx;
 	mutex_unlock(&bus_client->lock);
 
+	if ((ab > 0) && (ab < CAM_CPAS_AXI_MIN_BW))
+		ab = CAM_CPAS_AXI_MIN_BW;
+
+	if ((ib > 0) && (ib < CAM_CPAS_AXI_MIN_BW))
+		ib = CAM_CPAS_AXI_MIN_BW;
+
 	pdata = bus_client->pdata;
 	path = &(pdata->usecase[idx]);
 	path->vectors[0].ab = ab;
@@ -362,7 +370,7 @@
 	list_for_each_entry_safe(curr_port, temp_port,
 		&cpas_core->axi_ports_list_head, sibling_port) {
 		rc = cam_cpas_util_vote_bus_client_bw(&curr_port->mnoc_bus,
-			mnoc_bw, 0);
+			mnoc_bw, mnoc_bw);
 		if (rc) {
 			CAM_ERR(CAM_CPAS,
 				"Failed in mnoc vote, enable=%d, rc=%d",
@@ -372,7 +380,7 @@
 
 		if (soc_private->axi_camnoc_based) {
 			cam_cpas_util_vote_bus_client_bw(
-				&curr_port->camnoc_bus, camnoc_bw, 0);
+				&curr_port->camnoc_bus, 0, camnoc_bw);
 			if (rc) {
 				CAM_ERR(CAM_CPAS,
 					"Failed in mnoc vote, enable=%d, %d",
@@ -563,7 +571,7 @@
 		camnoc_bw, mnoc_bw);
 
 	rc = cam_cpas_util_vote_bus_client_bw(&axi_port->mnoc_bus,
-		mnoc_bw, 0);
+		mnoc_bw, mnoc_bw);
 	if (rc) {
 		CAM_ERR(CAM_CPAS,
 			"Failed in mnoc vote ab[%llu] ib[%llu] rc=%d",
@@ -573,11 +581,11 @@
 
 	if (soc_private->axi_camnoc_based) {
 		rc = cam_cpas_util_vote_bus_client_bw(&axi_port->camnoc_bus,
-			camnoc_bw, 0);
+			0, camnoc_bw);
 		if (rc) {
 			CAM_ERR(CAM_CPAS,
 				"Failed camnoc vote ab[%llu] ib[%llu] rc=%d",
-				camnoc_bw, camnoc_bw, rc);
+				0, camnoc_bw, rc);
 			goto unlock_axi_port;
 		}
 	}
@@ -1250,7 +1258,7 @@
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	int i;
 
-	for (i = 0; i < CPAS_MAX_CLIENTS; i++) {
+	for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) {
 		mutex_init(&cpas_core->client_mutex[i]);
 		cpas_core->cpas_client[i] = NULL;
 	}
@@ -1263,7 +1271,7 @@
 	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
 	int i;
 
-	for (i = 0; i < CPAS_MAX_CLIENTS; i++) {
+	for (i = 0; i < CAM_CPAS_MAX_CLIENTS; i++) {
 		if (cpas_core->cpas_client[i]) {
 			cam_cpas_hw_unregister_client(cpas_hw, i);
 			cpas_core->cpas_client[i] = NULL;
@@ -1302,6 +1310,27 @@
 	return rc;
 }
 
+static int cam_cpas_util_get_hw_version(struct platform_device *pdev,
+	struct cam_hw_soc_info *soc_info)
+{
+	struct device_node *of_node = pdev->dev.of_node;
+	int rc;
+
+	soc_info->hw_version = 0;
+
+	rc = of_property_read_u32(of_node,
+		"qcom,cpas-hw-ver", &soc_info->hw_version);
+
+	CAM_DBG(CAM_CPAS, "CPAS HW VERSION %x", soc_info->hw_version);
+
+	if (rc) {
+		CAM_ERR(CAM_CPAS, "failed to get CPAS HW Version rc=%d", rc);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
 int cam_cpas_hw_probe(struct platform_device *pdev,
 	struct cam_hw_intf **hw_intf)
 {
@@ -1442,6 +1471,10 @@
 	if (rc)
 		goto axi_cleanup;
 
+	rc = cam_cpas_util_get_hw_version(pdev, &cpas_hw->soc_info);
+	if (rc)
+		goto axi_cleanup;
+
 	*hw_intf = cpas_hw_intf;
 	return 0;
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
index bbc99b7..aa3663d 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_hw.h
@@ -17,13 +17,14 @@
 #include "cam_cpas_hw_intf.h"
 #include "cam_common_util.h"
 
-#define CPAS_MAX_CLIENTS 20
+#define CAM_CPAS_MAX_CLIENTS 30
 #define CAM_CPAS_INFLIGHT_WORKS 5
 
 #define CAM_CPAS_GET_CLIENT_IDX(handle) (handle)
 #define CAM_CPAS_GET_CLIENT_HANDLE(indx) (indx)
 
-#define CAM_CPAS_CLIENT_VALID(indx) ((indx >= 0) && (indx < CPAS_MAX_CLIENTS))
+#define CAM_CPAS_CLIENT_VALID(indx) \
+	((indx >= 0) && (indx < CAM_CPAS_MAX_CLIENTS))
 #define CAM_CPAS_CLIENT_REGISTERED(cpas_core, indx)        \
 	((CAM_CPAS_CLIENT_VALID(indx)) && \
 	(cpas_core->cpas_client[indx]))
@@ -176,8 +177,8 @@
  */
 struct cam_cpas {
 	struct cam_cpas_hw_caps hw_caps;
-	struct cam_cpas_client *cpas_client[CPAS_MAX_CLIENTS];
-	struct mutex client_mutex[CPAS_MAX_CLIENTS];
+	struct cam_cpas_client *cpas_client[CAM_CPAS_MAX_CLIENTS];
+	struct mutex client_mutex[CAM_CPAS_MAX_CLIENTS];
 	uint32_t num_clients;
 	uint32_t registered_clients;
 	uint32_t streamon_clients;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index 3846784..d5108f6 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
@@ -50,24 +50,53 @@
 
 static struct cam_cpas_intf *g_cpas_intf;
 
+int cam_cpas_get_cpas_hw_version(uint32_t *hw_version)
+{
+	struct cam_hw_info *cpas_hw = NULL;
+
+	if (!CAM_CPAS_INTF_INITIALIZED()) {
+		CAM_ERR(CAM_CPAS, "cpas intf not initialized");
+		return -ENODEV;
+	}
+
+	if (!hw_version) {
+		CAM_ERR(CAM_CPAS, "invalid input %pK", hw_version);
+		return -EINVAL;
+	}
+
+	cpas_hw = (struct cam_hw_info  *) g_cpas_intf->hw_intf->hw_priv;
+
+	*hw_version = cpas_hw->soc_info.hw_version;
+
+	if (*hw_version == CAM_CPAS_TITAN_NONE) {
+		CAM_DBG(CAM_CPAS, "Didn't find a valid HW Version %d",
+			*hw_version);
+	}
+
+	return 0;
+}
+
+
 int cam_cpas_get_hw_info(uint32_t *camera_family,
 	struct cam_hw_version *camera_version,
-	struct cam_hw_version *cpas_version)
+	struct cam_hw_version *cpas_version,
+	uint32_t *cam_caps)
 {
 	if (!CAM_CPAS_INTF_INITIALIZED()) {
 		CAM_ERR(CAM_CPAS, "cpas intf not initialized");
 		return -ENODEV;
 	}
 
-	if (!camera_family || !camera_version || !cpas_version) {
-		CAM_ERR(CAM_CPAS, "invalid input %pK %pK %pK", camera_family,
-			camera_version, cpas_version);
+	if (!camera_family || !camera_version || !cpas_version || !cam_caps) {
+		CAM_ERR(CAM_CPAS, "invalid input %pK %pK %pK %pK",
+			camera_family, camera_version, cpas_version, cam_caps);
 		return -EINVAL;
 	}
 
 	*camera_family = g_cpas_intf->hw_caps.camera_family;
 	*camera_version = g_cpas_intf->hw_caps.camera_version;
 	*cpas_version = g_cpas_intf->hw_caps.cpas_version;
+	*cam_caps = g_cpas_intf->hw_caps.camera_capability;
 
 	return 0;
 }
@@ -337,6 +366,7 @@
 	switch (cmd->op_code) {
 	case CAM_QUERY_CAP: {
 		struct cam_cpas_query_cap query;
+		uint32_t cam_cpas;
 
 		rc = copy_from_user(&query, (void __user *) cmd->handle,
 			sizeof(query));
@@ -347,7 +377,7 @@
 		}
 
 		rc = cam_cpas_get_hw_info(&query.camera_family,
-			&query.camera_version, &query.cpas_version);
+			&query.camera_version, &query.cpas_version, &cam_cpas);
 		if (rc)
 			break;
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index b2ad513..d4fc039 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -14,8 +14,8 @@
 #define _CAM_CPAS_SOC_H_
 
 #include "cam_soc_util.h"
+#include "cam_cpas_hw.h"
 
-#define CAM_CPAS_MAX_CLIENTS 20
 #define CAM_REGULATOR_LEVEL_MAX 16
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index d9133b9..0e5ce85 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -24,6 +24,18 @@
 
 struct cam_camnoc_info *camnoc_info;
 
+#define CAMNOC_SLAVE_MAX_ERR_CODE 7
+static const char * const camnoc_salve_err_code[] = {
+	"Target Error",              /* err code 0 */
+	"Address decode error",      /* err code 1 */
+	"Unsupported request",       /* err code 2 */
+	"Disconnected target",       /* err code 3 */
+	"Security violation",        /* err code 4 */
+	"Hidden security violation", /* err code 5 */
+	"Timeout Error",             /* err code 6 */
+	"Unknown Error",             /* unknown err code */
+};
+
 static int cam_cpastop_get_hw_info(struct cam_hw_info *cpas_hw,
 	struct cam_cpas_hw_caps *hw_caps)
 {
@@ -106,91 +118,155 @@
 }
 
 static int cam_cpastop_handle_errlogger(struct cam_cpas *cpas_core,
-	struct cam_hw_soc_info *soc_info)
+	struct cam_hw_soc_info *soc_info,
+	struct cam_camnoc_irq_slave_err_data *slave_err)
 {
-	uint32_t reg_value[4];
-	int i;
-	int size = camnoc_info->error_logger_size;
 	int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+	int err_code_index = 0;
 
-	for (i = 0; (i + 3) < size; i = i + 4) {
-		reg_value[0] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i]);
-		reg_value[1] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 1]);
-		reg_value[2] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 2]);
-		reg_value[3] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 3]);
-		CAM_ERR(CAM_CPAS,
-			"offset[0x%x] values [0x%x] [0x%x] [0x%x] [0x%x]",
-			camnoc_info->error_logger[i], reg_value[0],
-			reg_value[1], reg_value[2], reg_value[3]);
+	if (!camnoc_info->err_logger) {
+		CAM_ERR_RATE_LIMIT(CAM_CPAS, "Invalid err logger info");
+		return -EINVAL;
 	}
 
-	if ((i + 2) < size) {
-		reg_value[0] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i]);
-		reg_value[1] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 1]);
-		reg_value[2] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 2]);
-		CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x] [0x%x] [0x%x]",
-			camnoc_info->error_logger[i], reg_value[0],
-			reg_value[1], reg_value[2]);
-		i = i + 3;
-	}
+	slave_err->mainctrl.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->mainctrl);
 
-	if ((i + 1) < size) {
-		reg_value[0] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i]);
-		reg_value[1] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i + 1]);
-		CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x] [0x%x]",
-			camnoc_info->error_logger[i], reg_value[0],
-			reg_value[1]);
-		i = i + 2;
-	}
+	slave_err->errvld.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errvld);
 
-	if (i < size) {
-		reg_value[0] = cam_io_r_mb(
-			soc_info->reg_map[camnoc_index].mem_base +
-			camnoc_info->error_logger[i]);
-		CAM_ERR(CAM_CPAS, "offset[0x%x] values [0x%x]",
-			camnoc_info->error_logger[i], reg_value[0]);
-	}
+	slave_err->errlog0_low.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog0_low);
+
+	slave_err->errlog0_high.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog0_high);
+
+	slave_err->errlog1_low.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog1_low);
+
+	slave_err->errlog1_high.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog1_high);
+
+	slave_err->errlog2_low.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog2_low);
+
+	slave_err->errlog2_high.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog2_high);
+
+	slave_err->errlog3_low.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog3_low);
+
+	slave_err->errlog3_high.value = cam_io_r_mb(
+		soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->err_logger->errlog3_high);
+
+	CAM_ERR_RATE_LIMIT(CAM_CPAS,
+		"Possible memory configuration issue, fault at SMMU raised as CAMNOC SLAVE_IRQ");
+
+	CAM_ERR_RATE_LIMIT(CAM_CPAS,
+		"mainctrl[0x%x 0x%x] errvld[0x%x 0x%x] stall_en=%d, fault_en=%d, err_vld=%d",
+		camnoc_info->err_logger->mainctrl,
+		slave_err->mainctrl.value,
+		camnoc_info->err_logger->errvld,
+		slave_err->errvld.value,
+		slave_err->mainctrl.stall_en,
+		slave_err->mainctrl.fault_en,
+		slave_err->errvld.err_vld);
+
+	err_code_index = slave_err->errlog0_low.err_code;
+	if (err_code_index > CAMNOC_SLAVE_MAX_ERR_CODE)
+		err_code_index = CAMNOC_SLAVE_MAX_ERR_CODE;
+
+	CAM_ERR_RATE_LIMIT(CAM_CPAS,
+		"errlog0 low[0x%x 0x%x] high[0x%x 0x%x] loginfo_vld=%d, word_error=%d, non_secure=%d, device=%d, opc=%d, err_code=%d(%s) sizef=%d, addr_space=%d, len1=%d",
+		camnoc_info->err_logger->errlog0_low,
+		slave_err->errlog0_low.value,
+		camnoc_info->err_logger->errlog0_high,
+		slave_err->errlog0_high.value,
+		slave_err->errlog0_low.loginfo_vld,
+		slave_err->errlog0_low.word_error,
+		slave_err->errlog0_low.non_secure,
+		slave_err->errlog0_low.device,
+		slave_err->errlog0_low.opc,
+		slave_err->errlog0_low.err_code,
+		camnoc_salve_err_code[err_code_index],
+		slave_err->errlog0_low.sizef,
+		slave_err->errlog0_low.addr_space,
+		slave_err->errlog0_high.len1);
+
+	CAM_ERR_RATE_LIMIT(CAM_CPAS,
+		"errlog1_low[0x%x 0x%x]  errlog1_high[0x%x 0x%x] errlog2_low[0x%x 0x%x]  errlog2_high[0x%x 0x%x] errlog3_low[0x%x 0x%x]  errlog3_high[0x%x 0x%x]",
+		camnoc_info->err_logger->errlog1_low,
+		slave_err->errlog1_low.value,
+		camnoc_info->err_logger->errlog1_high,
+		slave_err->errlog1_high.value,
+		camnoc_info->err_logger->errlog2_low,
+		slave_err->errlog2_low.value,
+		camnoc_info->err_logger->errlog2_high,
+		slave_err->errlog2_high.value,
+		camnoc_info->err_logger->errlog3_low,
+		slave_err->errlog3_low.value,
+		camnoc_info->err_logger->errlog3_high,
+		slave_err->errlog3_high.value);
 
 	return 0;
 }
 
-static int cam_cpastop_handle_ubwc_err(struct cam_cpas *cpas_core,
-	struct cam_hw_soc_info *soc_info, int i)
+static int cam_cpastop_handle_ubwc_enc_err(struct cam_cpas *cpas_core,
+	struct cam_hw_soc_info *soc_info, int i,
+	struct cam_camnoc_irq_ubwc_enc_data *enc_err)
 {
-	uint32_t reg_value;
 	int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
 
-	reg_value = cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
+	enc_err->encerr_status.value =
+		cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
 		camnoc_info->irq_err[i].err_status.offset);
 
-	CAM_ERR(CAM_CPAS,
-		"Dumping ubwc error status [%d]: offset[0x%x] value[0x%x]",
-		i, camnoc_info->irq_err[i].err_status.offset, reg_value);
+	/* Let clients handle the UBWC errors */
+	CAM_DBG(CAM_CPAS,
+		"ubwc enc err [%d]: offset[0x%x] value[0x%x]",
+		i, camnoc_info->irq_err[i].err_status.offset,
+		enc_err->encerr_status.value);
 
-	return reg_value;
+	return 0;
 }
 
-static int cam_cpastop_handle_ahb_timeout_err(struct cam_hw_info *cpas_hw)
+static int cam_cpastop_handle_ubwc_dec_err(struct cam_cpas *cpas_core,
+	struct cam_hw_soc_info *soc_info, int i,
+	struct cam_camnoc_irq_ubwc_dec_data *dec_err)
 {
-	CAM_ERR(CAM_CPAS, "ahb timout error");
+	int camnoc_index = cpas_core->regbase_index[CAM_CPAS_REG_CAMNOC];
+
+	dec_err->decerr_status.value =
+		cam_io_r_mb(soc_info->reg_map[camnoc_index].mem_base +
+		camnoc_info->irq_err[i].err_status.offset);
+
+	/* Let clients handle the UBWC errors */
+	CAM_DBG(CAM_CPAS,
+		"ubwc dec err status [%d]: offset[0x%x] value[0x%x] thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d",
+		i, camnoc_info->irq_err[i].err_status.offset,
+		dec_err->decerr_status.value,
+		dec_err->decerr_status.thr_err,
+		dec_err->decerr_status.fcl_err,
+		dec_err->decerr_status.len_md_err,
+		dec_err->decerr_status.format_err);
+
+	return 0;
+}
+
+static int cam_cpastop_handle_ahb_timeout_err(struct cam_hw_info *cpas_hw,
+	struct cam_camnoc_irq_ahb_timeout_data *ahb_err)
+{
+	CAM_ERR_RATE_LIMIT(CAM_CPAS, "ahb timout error");
 
 	return 0;
 }
@@ -228,10 +304,11 @@
 }
 
 static void cam_cpastop_notify_clients(struct cam_cpas *cpas_core,
-	enum cam_camnoc_hw_irq_type irq_type, uint32_t irq_data)
+	struct cam_cpas_irq_data *irq_data)
 {
 	int i;
 	struct cam_cpas_client *cpas_client;
+	bool error_handled = false;
 
 	CAM_DBG(CAM_CPAS,
 		"Notify CB : num_clients=%d, registered=%d, started=%d",
@@ -243,13 +320,15 @@
 			cpas_client = cpas_core->cpas_client[i];
 			if (cpas_client->data.cam_cpas_client_cb) {
 				CAM_DBG(CAM_CPAS,
-					"Calling client CB %d : %d 0x%x",
-					i, irq_type, irq_data);
-				cpas_client->data.cam_cpas_client_cb(
+					"Calling client CB %d : %d",
+					i, irq_data->irq_type);
+				error_handled =
+					cpas_client->data.cam_cpas_client_cb(
 					cpas_client->data.client_handle,
 					cpas_client->data.userdata,
-					(enum cam_camnoc_irq_type)irq_type,
 					irq_data);
+				if (error_handled)
+					break;
 			}
 		}
 	}
@@ -263,7 +342,7 @@
 	struct cam_hw_soc_info *soc_info;
 	int i;
 	enum cam_camnoc_hw_irq_type irq_type;
-	uint32_t irq_data;
+	struct cam_cpas_irq_data irq_data;
 
 	payload = container_of(work, struct cam_cpas_work_payload, work);
 	if (!payload) {
@@ -280,23 +359,30 @@
 			(camnoc_info->irq_err[i].enable)) {
 			irq_type = camnoc_info->irq_err[i].irq_type;
 			CAM_ERR(CAM_CPAS, "Error occurred, type=%d", irq_type);
-			irq_data = 0;
+			memset(&irq_data, 0x0, sizeof(irq_data));
+			irq_data.irq_type = (enum cam_camnoc_irq_type)irq_type;
 
 			switch (irq_type) {
 			case CAM_CAMNOC_HW_IRQ_SLAVE_ERROR:
-				irq_data = cam_cpastop_handle_errlogger(
-					cpas_core, soc_info);
+				cam_cpastop_handle_errlogger(
+					cpas_core, soc_info,
+					&irq_data.u.slave_err);
 				break;
 			case CAM_CAMNOC_HW_IRQ_IFE02_UBWC_ENCODE_ERROR:
 			case CAM_CAMNOC_HW_IRQ_IFE13_UBWC_ENCODE_ERROR:
-			case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
 			case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_ENCODE_ERROR:
-				irq_data = cam_cpastop_handle_ubwc_err(
-					cpas_core, soc_info, i);
+				cam_cpastop_handle_ubwc_enc_err(
+					cpas_core, soc_info, i,
+					&irq_data.u.enc_err);
+				break;
+			case CAM_CAMNOC_HW_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
+				cam_cpastop_handle_ubwc_dec_err(
+					cpas_core, soc_info, i,
+					&irq_data.u.dec_err);
 				break;
 			case CAM_CAMNOC_HW_IRQ_AHB_TIMEOUT:
-				irq_data = cam_cpastop_handle_ahb_timeout_err(
-					cpas_hw);
+				cam_cpastop_handle_ahb_timeout_err(
+					cpas_hw, &irq_data.u.ahb_err);
 				break;
 			case CAM_CAMNOC_HW_IRQ_CAMNOC_TEST:
 				CAM_DBG(CAM_CPAS, "TEST IRQ");
@@ -306,8 +392,7 @@
 				break;
 			}
 
-			cam_cpastop_notify_clients(cpas_core, irq_type,
-				irq_data);
+			cam_cpastop_notify_clients(cpas_core, &irq_data);
 
 			payload->irq_status &=
 				~camnoc_info->irq_err[i].sbm_port;
@@ -372,6 +457,8 @@
 				&camnoc_info->specific[i].safe_lut);
 			cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC,
 				&camnoc_info->specific[i].ubwc_ctl);
+			cam_cpas_util_reg_update(cpas_hw, CAM_CPAS_REG_CAMNOC,
+				&camnoc_info->specific[i].flag_out_set0_low);
 		}
 	}
 
@@ -402,9 +489,10 @@
 			CAM_CPAS_POLL_RETRY_CNT,
 			CAM_CPAS_POLL_MIN_USECS, CAM_CPAS_POLL_MAX_USECS);
 		if (rc) {
-			CAM_ERR(CAM_CPAS,
+			CAM_DBG(CAM_CPAS,
 				"camnoc flush slave pending trans failed");
 			/* Do not return error, passthrough */
+			rc = 0;
 		}
 	}
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index d5bb363..73f7e9b 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -110,6 +110,7 @@
 	struct cam_cpas_reg danger_lut;
 	struct cam_cpas_reg safe_lut;
 	struct cam_cpas_reg ubwc_ctl;
+	struct cam_cpas_reg flag_out_set0_low;
 };
 
 /**
@@ -172,6 +173,34 @@
 };
 
 /**
+ * struct cam_camnoc_err_logger_info : CAMNOC error logger register offsets
+ *
+ * @mainctrl: Register offset for mainctrl
+ * @errvld: Register offset for errvld
+ * @errlog0_low: Register offset for errlog0_low
+ * @errlog0_high: Register offset for errlog0_high
+ * @errlog1_low: Register offset for errlog1_low
+ * @errlog1_high: Register offset for errlog1_high
+ * @errlog2_low: Register offset for errlog2_low
+ * @errlog2_high: Register offset for errlog2_high
+ * @errlog3_low: Register offset for errlog3_low
+ * @errlog3_high: Register offset for errlog3_high
+ *
+ */
+struct cam_camnoc_err_logger_info {
+	uint32_t mainctrl;
+	uint32_t errvld;
+	uint32_t errlog0_low;
+	uint32_t errlog0_high;
+	uint32_t errlog1_low;
+	uint32_t errlog1_high;
+	uint32_t errlog2_low;
+	uint32_t errlog2_high;
+	uint32_t errlog3_low;
+	uint32_t errlog3_high;
+};
+
+/**
  * struct cam_camnoc_info : Overall CAMNOC settings info
  *
  * @specific: Pointer to CAMNOC SPECIFICTONTTPTR settings
@@ -179,8 +208,7 @@
  * @irq_sbm: Pointer to CAMNOC IRQ SBM settings
  * @irq_err: Pointer to CAMNOC IRQ Error settings
  * @irq_err_size: Array size of IRQ Error settings
- * @error_logger: Pointer to CAMNOC IRQ Error logger read registers
- * @error_logger_size: Array size of IRQ Error logger
+ * @err_logger: Pointer to CAMNOC IRQ Error logger read registers
  * @errata_wa_list: HW Errata workaround info
  *
  */
@@ -190,8 +218,7 @@
 	struct cam_camnoc_irq_sbm *irq_sbm;
 	struct cam_camnoc_irq_err *irq_err;
 	int irq_err_size;
-	uint32_t *error_logger;
-	int error_logger_size;
+	struct cam_camnoc_err_logger_info *err_logger;
 	struct cam_cpas_hw_errata_wa_list *errata_wa_list;
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
index b30cd05..2654b47 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop100.h
@@ -498,19 +498,17 @@
 	}
 };
 
-uint32_t slave_error_logger[] = {
-	0x2700, /* ERRLOGGER_SWID_LOW */
-	0x2704, /* ERRLOGGER_SWID_HIGH */
-	0x2708, /* ERRLOGGER_MAINCTL_LOW */
-	0x2710, /* ERRLOGGER_ERRVLD_LOW */
-	0x2720, /* ERRLOGGER_ERRLOG0_LOW */
-	0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
-	0x2728, /* ERRLOGGER_ERRLOG1_LOW */
-	0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
-	0x2730, /* ERRLOGGER_ERRLOG2_LOW */
-	0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
-	0x2738, /* ERRLOGGER_ERRLOG3_LOW */
-	0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
+static struct cam_camnoc_err_logger_info cam170_cpas100_err_logger_offsets = {
+	.mainctrl     =  0x2708, /* ERRLOGGER_MAINCTL_LOW */
+	.errvld       =  0x2710, /* ERRLOGGER_ERRVLD_LOW */
+	.errlog0_low  =  0x2720, /* ERRLOGGER_ERRLOG0_LOW */
+	.errlog0_high =  0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
+	.errlog1_low  =  0x2728, /* ERRLOGGER_ERRLOG1_LOW */
+	.errlog1_high =  0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
+	.errlog2_low  =  0x2730, /* ERRLOGGER_ERRLOG2_LOW */
+	.errlog2_high =  0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
+	.errlog3_low  =  0x2738, /* ERRLOGGER_ERRLOG3_LOW */
+	.errlog3_high =  0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
 };
 
 static struct cam_cpas_hw_errata_wa_list cam170_cpas100_errata_wa_list = {
@@ -533,9 +531,7 @@
 	.irq_err = &cam_cpas100_irq_err[0],
 	.irq_err_size = sizeof(cam_cpas100_irq_err) /
 		sizeof(cam_cpas100_irq_err[0]),
-	.error_logger = &slave_error_logger[0],
-	.error_logger_size = sizeof(slave_error_logger) /
-		sizeof(slave_error_logger[0]),
+	.err_logger = &cam170_cpas100_err_logger_offsets,
 	.errata_wa_list = &cam170_cpas100_errata_wa_list,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
index f4d0e36..4418fb1 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
@@ -258,14 +258,14 @@
 			.access_type = CAM_REG_TYPE_READ_WRITE,
 			.masked_value = 0,
 			.offset = 0x430, /* SPECIFIC_IFE02_PRIORITYLUT_LOW */
-			.value = 0x66665433,
+			.value = 0x44443333,
 		},
 		.priority_lut_high = {
 			.enable = true,
 			.access_type = CAM_REG_TYPE_READ_WRITE,
 			.masked_value = 0,
 			.offset = 0x434, /* SPECIFIC_IFE02_PRIORITYLUT_HIGH */
-			.value = 0x66666666,
+			.value = 0x66665555,
 		},
 		.urgency = {
 			.enable = true,
@@ -306,14 +306,14 @@
 			.access_type = CAM_REG_TYPE_READ_WRITE,
 			.masked_value = 0,
 			.offset = 0x830, /* SPECIFIC_IFE13_PRIORITYLUT_LOW */
-			.value = 0x66665433,
+			.value = 0x44443333,
 		},
 		.priority_lut_high = {
 			.enable = true,
 			.access_type = CAM_REG_TYPE_READ_WRITE,
 			.masked_value = 0,
 			.offset = 0x834, /* SPECIFIC_IFE13_PRIORITYLUT_HIGH */
-			.value = 0x66666666,
+			.value = 0x66665555,
 		},
 		.urgency = {
 			.enable = true,
@@ -494,28 +494,33 @@
 	},
 	{
 		.port_type = CAM_CAMNOC_ICP,
-		.enable = false,
-	}
+		.enable = true,
+		.flag_out_set0_low = {
+			.enable = true,
+			.access_type = CAM_REG_TYPE_WRITE,
+			.masked_value = 0,
+			.offset = 0x2088,
+			.value = 0x100000,
+		},
+	},
 };
 
-static uint32_t cam_cpas110_slave_error_logger[] = {
-	0x2700, /* ERRLOGGER_SWID_LOW */
-	0x2704, /* ERRLOGGER_SWID_HIGH */
-	0x2708, /* ERRLOGGER_MAINCTL_LOW */
-	0x2710, /* ERRLOGGER_ERRVLD_LOW */
-	0x2720, /* ERRLOGGER_ERRLOG0_LOW */
-	0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
-	0x2728, /* ERRLOGGER_ERRLOG1_LOW */
-	0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
-	0x2730, /* ERRLOGGER_ERRLOG2_LOW */
-	0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
-	0x2738, /* ERRLOGGER_ERRLOG3_LOW */
-	0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
+static struct cam_camnoc_err_logger_info cam170_cpas110_err_logger_offsets = {
+	.mainctrl     =  0x2708, /* ERRLOGGER_MAINCTL_LOW */
+	.errvld       =  0x2710, /* ERRLOGGER_ERRVLD_LOW */
+	.errlog0_low  =  0x2720, /* ERRLOGGER_ERRLOG0_LOW */
+	.errlog0_high =  0x2724, /* ERRLOGGER_ERRLOG0_HIGH */
+	.errlog1_low  =  0x2728, /* ERRLOGGER_ERRLOG1_LOW */
+	.errlog1_high =  0x272c, /* ERRLOGGER_ERRLOG1_HIGH */
+	.errlog2_low  =  0x2730, /* ERRLOGGER_ERRLOG2_LOW */
+	.errlog2_high =  0x2734, /* ERRLOGGER_ERRLOG2_HIGH */
+	.errlog3_low  =  0x2738, /* ERRLOGGER_ERRLOG3_LOW */
+	.errlog3_high =  0x273c, /* ERRLOGGER_ERRLOG3_HIGH */
 };
 
 static struct cam_cpas_hw_errata_wa_list cam170_cpas110_errata_wa_list = {
 	.camnoc_flush_slave_pending_trans = {
-		.enable = true,
+		.enable = false,
 		.data.reg_info = {
 			.access_type = CAM_REG_TYPE_READ,
 			.offset = 0x2100, /* SidebandManager_SenseIn0_Low */
@@ -533,9 +538,7 @@
 	.irq_err = &cam_cpas110_irq_err[0],
 	.irq_err_size = sizeof(cam_cpas110_irq_err) /
 		sizeof(cam_cpas110_irq_err[0]),
-	.error_logger = &cam_cpas110_slave_error_logger[0],
-	.error_logger_size = sizeof(cam_cpas110_slave_error_logger) /
-		sizeof(cam_cpas110_slave_error_logger[0]),
+	.err_logger = &cam170_cpas110_err_logger_offsets,
 	.errata_wa_list = &cam170_cpas110_errata_wa_list,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
index 3977b68..c844ef7 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
@@ -37,6 +37,20 @@
 };
 
 /**
+ * enum cam_cpas_hw_version - Enum for Titan CPAS HW Versions
+ */
+enum cam_cpas_hw_version {
+	CAM_CPAS_TITAN_NONE = 0,
+	CAM_CPAS_TITAN_170_V100 = 0x170100,
+	CAM_CPAS_TITAN_170_V110 = 0x170110,
+	CAM_CPAS_TITAN_170_V120 = 0x170120,
+	CAM_CPAS_TITAN_175_V100 = 0x175100,
+	CAM_CPAS_TITAN_175_V101 = 0x175101,
+	CAM_CPAS_TITAN_MAX
+};
+
+
+/**
  * enum cam_camnoc_irq_type - Enum for camnoc irq types
  *
  * @CAM_CAMNOC_IRQ_SLAVE_ERROR: Each slave port in CAMNOC (3 QSB ports and
@@ -68,6 +82,183 @@
 };
 
 /**
+ * struct cam_camnoc_irq_slave_err_data : Data for Slave error.
+ *
+ * @mainctrl     : Err logger mainctrl info
+ * @errvld       : Err logger errvld info
+ * @errlog0_low  : Err logger errlog0_low info
+ * @errlog0_high : Err logger errlog0_high info
+ * @errlog1_low  : Err logger errlog1_low info
+ * @errlog1_high : Err logger errlog1_high info
+ * @errlog2_low  : Err logger errlog2_low info
+ * @errlog2_high : Err logger errlog2_high info
+ * @errlog3_low  : Err logger errlog3_low info
+ * @errlog3_high : Err logger errlog3_high info
+ *
+ */
+struct cam_camnoc_irq_slave_err_data {
+	union {
+		struct {
+			uint32_t stall_en : 1; /* bit 0 */
+			uint32_t fault_en : 1; /* bit 1 */
+			uint32_t rsv      : 30; /* bits 2-31 */
+		};
+		uint32_t value;
+	} mainctrl;
+	union {
+		struct {
+			uint32_t err_vld : 1; /* bit 0 */
+			uint32_t rsv     : 31; /* bits 1-31 */
+		};
+		uint32_t value;
+	} errvld;
+	union {
+		struct {
+			uint32_t loginfo_vld : 1; /* bit 0 */
+			uint32_t word_error  : 1; /* bit 1 */
+			uint32_t non_secure  : 1; /* bit 2 */
+			uint32_t device      : 1; /* bit 3 */
+			uint32_t opc         : 3; /* bits 4 - 6 */
+			uint32_t rsv0        : 1; /* bit 7 */
+			uint32_t err_code    : 3; /* bits 8 - 10 */
+			uint32_t sizef       : 3; /* bits 11 - 13 */
+			uint32_t rsv1        : 2; /* bits 14 - 15 */
+			uint32_t addr_space  : 6; /* bits 16 - 21 */
+			uint32_t rsv2        : 10; /* bits 22 - 31 */
+		};
+		uint32_t value;
+	}  errlog0_low;
+	union {
+		struct {
+			uint32_t len1 : 10; /* bits 0 - 9 */
+			uint32_t rsv  : 22; /* bits 10 - 31 */
+		};
+		uint32_t value;
+	} errlog0_high;
+	union {
+		struct {
+			uint32_t path : 16; /* bits 0 - 15 */
+			uint32_t rsv  : 16; /* bits 16 - 31 */
+		};
+		uint32_t value;
+	} errlog1_low;
+	union {
+		struct {
+			uint32_t extid : 18; /* bits 0 - 17 */
+			uint32_t rsv   : 14; /* bits 18 - 31 */
+		};
+		uint32_t value;
+	} errlog1_high;
+	union {
+		struct {
+			uint32_t errlog2_lsb : 32; /* bits 0 - 31 */
+		};
+		uint32_t value;
+	} errlog2_low;
+	union {
+		struct {
+			uint32_t errlog2_msb : 16; /* bits 0 - 16 */
+			uint32_t rsv         : 16; /* bits 16 - 31 */
+		};
+		uint32_t value;
+	} errlog2_high;
+	union {
+		struct {
+			uint32_t errlog3_lsb : 32; /* bits 0 - 31 */
+		};
+		uint32_t value;
+	} errlog3_low;
+	union {
+		struct {
+			uint32_t errlog3_msb : 32; /* bits 0 - 31 */
+		};
+		uint32_t value;
+	} errlog3_high;
+};
+
+/**
+ * struct cam_camnoc_irq_ubwc_enc_data : Data for UBWC Encode error.
+ *
+ * @encerr_status : Encode error status
+ *
+ */
+struct cam_camnoc_irq_ubwc_enc_data {
+	union {
+		struct {
+			uint32_t encerrstatus : 3; /* bits 0 - 2 */
+			uint32_t rsv          : 29; /* bits 3 - 31 */
+		};
+		uint32_t value;
+	} encerr_status;
+};
+
+/**
+ * struct cam_camnoc_irq_ubwc_dec_data : Data for UBWC Decode error.
+ *
+ * @decerr_status : Decoder error status
+ * @thr_err       : Set to 1 if
+ *                  At least one of the bflc_len fields in the bit steam exceeds
+ *                  its threshold value. This error is possible only for
+ *                  RGBA1010102, TP10, and RGB565 formats
+ * @fcl_err       : Set to 1 if
+ *                  Fast clear with a legal non-RGB format
+ * @len_md_err    : Set to 1 if
+ *                  The calculated burst length does not match burst length
+ *                  specified by the metadata value
+ * @format_err    : Set to 1 if
+ *                  Illegal format
+ *                  1. bad format :2,3,6
+ *                  2. For 32B MAL, metadata=6
+ *                  3. For 32B MAL RGB565, Metadata != 0,1,7
+ *                  4. For 64B MAL RGB565, metadata[3:1] == 1,2
+ *
+ */
+struct cam_camnoc_irq_ubwc_dec_data {
+	union {
+		struct {
+			uint32_t thr_err    : 1; /* bit 0 */
+			uint32_t fcl_err    : 1; /* bit 1 */
+			uint32_t len_md_err : 1; /* bit 2 */
+			uint32_t format_err : 1; /* bit 3 */
+			uint32_t rsv        : 28; /* bits 4 - 31 */
+		};
+		uint32_t value;
+	} decerr_status;
+};
+
+struct cam_camnoc_irq_ahb_timeout_data {
+	uint32_t data;
+};
+
+/**
+ * struct cam_cpas_irq_data : CAMNOC IRQ data
+ *
+ * @irq_type  : To identify the type of IRQ
+ * @u         : Union of irq err data information
+ * @slave_err : Data for Slave error.
+ *              Valid if type is CAM_CAMNOC_IRQ_SLAVE_ERROR
+ * @enc_err   : Data for UBWC Encode error.
+ *              Valid if type is one of below:
+ *              CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR
+ *              CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR
+ *              CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR
+ * @dec_err   : Data for UBWC Decode error.
+ *              Valid if type is CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR
+ * @ahb_err   : Data for Slave error.
+ *              Valid if type is CAM_CAMNOC_IRQ_AHB_TIMEOUT
+ *
+ */
+struct cam_cpas_irq_data {
+	enum cam_camnoc_irq_type irq_type;
+	union {
+		struct cam_camnoc_irq_slave_err_data   slave_err;
+		struct cam_camnoc_irq_ubwc_enc_data    enc_err;
+		struct cam_camnoc_irq_ubwc_dec_data    dec_err;
+		struct cam_camnoc_irq_ahb_timeout_data ahb_err;
+	} u;
+};
+
+/**
  * struct cam_cpas_register_params : Register params for cpas client
  *
  * @identifier        : Input identifier string which is the device label
@@ -93,11 +284,10 @@
 	uint32_t        cell_index;
 	struct device  *dev;
 	void           *userdata;
-	void          (*cam_cpas_client_cb)(
+	bool          (*cam_cpas_client_cb)(
 			uint32_t                  client_handle,
 			void                     *userdata,
-			enum cam_camnoc_irq_type  event_type,
-			uint32_t                  event_data);
+			struct cam_cpas_irq_data *irq_data);
 	uint32_t        client_handle;
 };
 
@@ -300,6 +490,7 @@
  *                   CAM_FAMILY_CPAS_SS
  * @camera_version : Camera platform version
  * @cpas_version   : Camera cpas version
+ * @cam_caps       : Camera capability
  *
  * @return 0 on success.
  *
@@ -307,6 +498,20 @@
 int cam_cpas_get_hw_info(
 	uint32_t                 *camera_family,
 	struct cam_hw_version    *camera_version,
-	struct cam_hw_version    *cpas_version);
+	struct cam_hw_version    *cpas_version,
+	uint32_t                 *cam_caps);
+
+/**
+ * cam_cpas_get_cpas_hw_version()
+ *
+ * @brief: API to get camera cpas hw version
+ *
+ * @hw_version  : Camera cpas hw version
+ *
+ * @return 0 on success.
+ *
+ */
+int cam_cpas_get_cpas_hw_version(
+	uint32_t				 *hw_version);
 
 #endif /* _CAM_CPAS_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
index 4c29ffd..78c1dd3 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_context.c
@@ -15,6 +15,9 @@
 
 #include "cam_debug_util.h"
 #include "cam_fd_context.h"
+#include "cam_trace.h"
+
+static const char fd_dev_name[] = "fd";
 
 /* Functions in Available state */
 static int __cam_fd_ctx_acquire_dev_in_available(struct cam_context *ctx,
@@ -29,6 +32,7 @@
 	}
 
 	ctx->state = CAM_CTX_ACQUIRED;
+	trace_cam_context_state("FD", ctx);
 
 	return rc;
 }
@@ -46,6 +50,7 @@
 	}
 
 	ctx->state = CAM_CTX_AVAILABLE;
+	trace_cam_context_state("FD", ctx);
 
 	return rc;
 }
@@ -76,6 +81,7 @@
 	}
 
 	ctx->state = CAM_CTX_ACTIVATED;
+	trace_cam_context_state("FD", ctx);
 
 	return rc;
 }
@@ -93,6 +99,7 @@
 	}
 
 	ctx->state = CAM_CTX_ACQUIRED;
+	trace_cam_context_state("FD", ctx);
 
 	return rc;
 }
@@ -203,8 +210,8 @@
 
 	memset(fd_ctx, 0, sizeof(*fd_ctx));
 
-	rc = cam_context_init(base_ctx, NULL, hw_intf, fd_ctx->req_base,
-		CAM_CTX_REQ_MAX);
+	rc = cam_context_init(base_ctx, fd_dev_name, NULL, hw_intf,
+		fd_ctx->req_base, CAM_CTX_REQ_MAX);
 	if (rc) {
 		CAM_ERR(CAM_FD, "Camera Context Base init failed, rc=%d", rc);
 		return rc;
diff --git a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
index 27f5518..260dcd8 100644
--- a/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
+++ b/drivers/media/platform/msm/camera/cam_fd/cam_fd_dev.c
@@ -67,6 +67,7 @@
 	struct v4l2_subdev_fh *fh)
 {
 	struct cam_fd_dev *fd_dev = &g_fd_dev;
+	struct cam_node *node = v4l2_get_subdevdata(sd);
 
 	if (!fd_dev->probe_done) {
 		CAM_ERR(CAM_FD, "FD Dev not initialized, fd_dev=%pK", fd_dev);
@@ -78,6 +79,13 @@
 	CAM_DBG(CAM_FD, "FD Subdev open count %d", fd_dev->open_cnt);
 	mutex_unlock(&fd_dev->lock);
 
+	if (!node) {
+		CAM_ERR(CAM_FD, "Node ptr is NULL");
+		return -EINVAL;
+	}
+
+	cam_node_shutdown(node);
+
 	return 0;
 }
 
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c
index d226e17..bff42f4 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.c
@@ -26,6 +26,7 @@
 #include "cam_fd_hw_soc.h"
 #include "cam_fd_hw_mgr_intf.h"
 #include "cam_fd_hw_mgr.h"
+#include "cam_trace.h"
 
 static struct cam_fd_hw_mgr g_fd_hw_mgr;
 
@@ -334,7 +335,7 @@
 	/* Update required info in hw context */
 	hw_ctx->device_index = i;
 
-	CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+	CAM_DBG(CAM_FD, "ctx index=%d, device_index=%d", hw_ctx->ctx_index,
 		hw_ctx->device_index);
 
 	return 0;
@@ -480,6 +481,53 @@
 	return rc;
 }
 
+static int cam_fd_mgr_util_get_buf_map_requirement(uint32_t direction,
+	uint32_t resource_type, bool *need_io_map, bool *need_cpu_map)
+{
+	if (!need_io_map || !need_cpu_map) {
+		CAM_ERR(CAM_FD, "Invalid input pointers %pK %pK", need_io_map,
+			need_cpu_map);
+		return -EINVAL;
+	}
+
+	if (direction == CAM_BUF_INPUT) {
+		switch (resource_type) {
+		case CAM_FD_INPUT_PORT_ID_IMAGE:
+			*need_io_map = true;
+			*need_cpu_map = false;
+			break;
+		default:
+			CAM_WARN(CAM_FD, "Invalid port: dir %d, id %d",
+				direction, resource_type);
+			return -EINVAL;
+		}
+	} else if (direction == CAM_BUF_OUTPUT) {
+		switch (resource_type) {
+		case CAM_FD_OUTPUT_PORT_ID_RESULTS:
+			*need_io_map = true;
+			*need_cpu_map = true;
+			break;
+		case CAM_FD_OUTPUT_PORT_ID_RAW_RESULTS:
+			*need_io_map = true;
+			*need_cpu_map = true;
+			break;
+		case CAM_FD_OUTPUT_PORT_ID_WORK_BUFFER:
+			*need_io_map = true;
+			*need_cpu_map = false;
+			break;
+		default:
+			CAM_WARN(CAM_FD, "Invalid port: dir %d, id %d",
+				direction, resource_type);
+			return -EINVAL;
+		}
+	} else {
+		CAM_WARN(CAM_FD, "Invalid direction %d", direction);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int cam_fd_mgr_util_prepare_io_buf_info(int32_t iommu_hdl,
 	struct cam_hw_prepare_update_args *prepare,
 	struct cam_fd_hw_io_buffer *input_buf,
@@ -491,6 +539,7 @@
 	uint64_t io_addr[CAM_PACKET_MAX_PLANES];
 	uint64_t cpu_addr[CAM_PACKET_MAX_PLANES];
 	size_t size;
+	bool need_io_map, need_cpu_map;
 
 	/* Get IO Buf information */
 	num_out_buf = 0;
@@ -512,32 +561,55 @@
 			return -EINVAL;
 		}
 
+		rc = cam_fd_mgr_util_get_buf_map_requirement(
+			io_cfg[i].direction, io_cfg[i].resource_type,
+			&need_io_map, &need_cpu_map);
+		if (rc) {
+			CAM_WARN(CAM_FD, "Invalid io buff [%d] : %d %d %d",
+				i, io_cfg[i].direction,
+				io_cfg[i].resource_type, rc);
+			continue;
+		}
+
 		memset(io_addr, 0x0, sizeof(io_addr));
 		for (plane = 0; plane < CAM_PACKET_MAX_PLANES; plane++) {
 			if (!io_cfg[i].mem_handle[plane])
 				break;
 
-			rc = cam_mem_get_io_buf(io_cfg[i].mem_handle[plane],
-				iommu_hdl, &io_addr[plane], &size);
-			if ((rc) || (io_addr[plane] >> 32)) {
-				CAM_ERR(CAM_FD, "Invalid io addr for %d %d",
-					plane, rc);
-				return -ENOMEM;
+			io_addr[plane] = 0x0;
+			cpu_addr[plane] = 0x0;
+
+			if (need_io_map) {
+				rc = cam_mem_get_io_buf(
+					io_cfg[i].mem_handle[plane],
+					iommu_hdl, &io_addr[plane], &size);
+				if ((rc) || (io_addr[plane] >> 32)) {
+					CAM_ERR(CAM_FD,
+						"Invalid io buf %d %d %d %d",
+						io_cfg[i].direction,
+						io_cfg[i].resource_type, plane,
+						rc);
+					return -ENOMEM;
+				}
+
+				io_addr[plane] += io_cfg[i].offsets[plane];
 			}
 
-			/*
-			 * Buffers may be accessed by CPU as well, we do not
-			 * know at this point, so get both and send to HW layer
-			 */
-			rc = cam_mem_get_cpu_buf(io_cfg[i].mem_handle[plane],
-				&cpu_addr[plane], &size);
-			if (rc) {
-				CAM_ERR(CAM_FD, "unable to get buf address");
-				return rc;
-			}
+			if (need_cpu_map) {
+				rc = cam_mem_get_cpu_buf(
+					io_cfg[i].mem_handle[plane],
+					&cpu_addr[plane], &size);
+				if (rc) {
+					CAM_ERR(CAM_FD,
+						"Invalid cpu buf %d %d %d %d",
+						io_cfg[i].direction,
+						io_cfg[i].resource_type, plane,
+						rc);
+					return rc;
+				}
 
-			io_addr[plane] += io_cfg[i].offsets[plane];
-			cpu_addr[plane] += io_cfg[i].offsets[plane];
+				cpu_addr[plane] += io_cfg[i].offsets[plane];
+			}
 
 			CAM_DBG(CAM_FD, "IO Address[%d][%d] : %pK, %pK",
 				io_cfg[i].direction, plane, io_addr[plane],
@@ -764,6 +836,8 @@
 		return -EBUSY;
 	}
 
+	trace_cam_submit_to_hw("FD", frame_req->request_id);
+
 	list_del_init(&frame_req->list);
 	mutex_unlock(&hw_mgr->frame_req_mutex);
 
@@ -791,6 +865,8 @@
 	}
 
 	hw_device->ready_to_process = false;
+	hw_device->cur_hw_ctx = hw_ctx;
+	hw_device->req_id = frame_req->request_id;
 	mutex_unlock(&hw_device->lock);
 
 	rc = cam_fd_mgr_util_put_frame_req(
@@ -924,6 +1000,8 @@
 		frame_abort = false;
 	}
 
+	trace_cam_irq_handled("FD", irq_type);
+
 notify_context:
 	/* Do a callback to inform frame done or stop done */
 	if (frame_req->hw_ctx->event_cb) {
@@ -950,6 +1028,8 @@
 	 */
 	mutex_lock(&hw_device->lock);
 	hw_device->ready_to_process = true;
+	hw_device->req_id = -1;
+	hw_device->cur_hw_ctx = NULL;
 	CAM_DBG(CAM_FD, "ready_to_process=%d", hw_device->ready_to_process);
 	mutex_unlock(&hw_device->lock);
 
@@ -1130,6 +1210,7 @@
 	if (rc)
 		CAM_ERR(CAM_FD, "Failed in release device, rc=%d", rc);
 
+	hw_ctx->ctx_in_use = false;
 	list_del_init(&hw_ctx->list);
 	cam_fd_mgr_util_put_ctx(&hw_mgr->free_ctx_list, &hw_ctx);
 
@@ -1158,7 +1239,7 @@
 		return -EPERM;
 	}
 
-	CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+	CAM_DBG(CAM_FD, "ctx index=%d, device_index=%d", hw_ctx->ctx_index,
 		hw_ctx->device_index);
 
 	rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
@@ -1185,6 +1266,82 @@
 	return rc;
 }
 
+static int cam_fd_mgr_hw_flush(void *hw_mgr_priv,
+	struct cam_fd_hw_mgr_ctx *hw_ctx)
+{
+	int rc = 0;
+	struct cam_fd_mgr_frame_request *frame_req, *req_temp;
+	struct cam_fd_hw_stop_args hw_stop_args;
+	struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
+	struct cam_fd_device *hw_device;
+
+	if (!hw_mgr_priv || !hw_ctx) {
+		CAM_ERR(CAM_FD, "Invalid arguments %pK %pK",
+			hw_mgr_priv, hw_ctx);
+		return -EINVAL;
+	}
+
+	if (!hw_ctx->ctx_in_use) {
+		CAM_ERR(CAM_FD, "Invalid context is used, hw_ctx=%pK", hw_ctx);
+		return -EPERM;
+	}
+	CAM_DBG(CAM_FD, "ctx index=%d, hw_ctx=%d", hw_ctx->ctx_index,
+		hw_ctx->device_index);
+
+	rc = cam_fd_mgr_util_get_device(hw_mgr, hw_ctx, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_FD, "Error in getting device %d", rc);
+		return rc;
+	}
+
+	mutex_lock(&hw_mgr->frame_req_mutex);
+	list_for_each_entry_safe(frame_req, req_temp,
+		&hw_mgr->frame_pending_list_high, list) {
+		if (frame_req->hw_ctx != hw_ctx)
+			continue;
+
+		list_del_init(&frame_req->list);
+	}
+
+	list_for_each_entry_safe(frame_req, req_temp,
+		&hw_mgr->frame_pending_list_normal, list) {
+		if (frame_req->hw_ctx != hw_ctx)
+			continue;
+
+		list_del_init(&frame_req->list);
+	}
+
+	list_for_each_entry_safe(frame_req, req_temp,
+		&hw_mgr->frame_processing_list, list) {
+		if (frame_req->hw_ctx != hw_ctx)
+			continue;
+
+		list_del_init(&frame_req->list);
+	}
+	mutex_unlock(&hw_mgr->frame_req_mutex);
+
+	mutex_lock(&hw_device->lock);
+	if ((hw_device->ready_to_process == true) ||
+		(hw_device->cur_hw_ctx != hw_ctx))
+		goto end;
+
+	if (hw_device->hw_intf->hw_ops.stop) {
+		hw_stop_args.hw_ctx = hw_ctx;
+		rc = hw_device->hw_intf->hw_ops.stop(
+			hw_device->hw_intf->hw_priv, &hw_stop_args,
+			sizeof(hw_stop_args));
+		if (rc) {
+			CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc);
+			goto end;
+		}
+		hw_device->ready_to_process = true;
+	}
+
+end:
+	mutex_unlock(&hw_device->lock);
+	return rc;
+}
+
 static int cam_fd_mgr_hw_stop(void *hw_mgr_priv, void *mgr_stop_args)
 {
 	struct cam_fd_hw_mgr *hw_mgr = (struct cam_fd_hw_mgr *)hw_mgr_priv;
@@ -1192,7 +1349,6 @@
 		(struct cam_hw_stop_args *)mgr_stop_args;
 	struct cam_fd_hw_mgr_ctx *hw_ctx;
 	struct cam_fd_device *hw_device;
-	struct cam_fd_hw_stop_args hw_stop_args;
 	struct cam_fd_hw_deinit_args hw_deinit_args;
 	int rc = 0;
 
@@ -1219,21 +1375,9 @@
 	CAM_DBG(CAM_FD, "FD Device ready_to_process = %d",
 		hw_device->ready_to_process);
 
-	if ((hw_device->hw_intf->hw_ops.stop) &&
-		(hw_device->ready_to_process == false)) {
-		/*
-		 * Even if device is in processing state, we should submit
-		 * stop command only if this ctx is running on hw
-		 */
-		hw_stop_args.hw_ctx = hw_ctx;
-		rc = hw_device->hw_intf->hw_ops.stop(
-			hw_device->hw_intf->hw_priv, &hw_stop_args,
-			sizeof(hw_stop_args));
-		if (rc) {
-			CAM_ERR(CAM_FD, "Failed in HW Stop %d", rc);
-			return rc;
-		}
-	}
+	rc = cam_fd_mgr_hw_flush(hw_mgr, hw_ctx);
+	if (rc)
+		CAM_ERR(CAM_FD, "FD failed to flush");
 
 	if (hw_device->hw_intf->hw_ops.deinit) {
 		hw_deinit_args.hw_ctx = hw_ctx;
@@ -1300,7 +1444,7 @@
 
 	/* We do not expect any patching, but just do it anyway */
 	rc = cam_packet_util_process_patches(prepare->packet,
-		hw_mgr->device_iommu.non_secure);
+		hw_mgr->device_iommu.non_secure, -1);
 	if (rc) {
 		CAM_ERR(CAM_FD, "Patch FD packet failed, rc=%d", rc);
 		return rc;
@@ -1389,6 +1533,8 @@
 	}
 
 	frame_req = config->priv;
+
+	trace_cam_apply_req("FD", frame_req->request_id);
 	CAM_DBG(CAM_FD, "FrameHWConfig : Frame[%lld]", frame_req->request_id);
 
 	frame_req->num_hw_update_entries = config->num_hw_update_entries;
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h
index 135e006..db5d100 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/cam_fd_hw_mgr.h
@@ -80,14 +80,18 @@
  * @num_ctxts        : Number of context currently running on this device
  * @valid            : Whether this device is valid
  * @lock             : Lock used for protectin
+ * @cur_hw_ctx       : current hw context running in the device
+ * @req_id           : current processing req id
  */
 struct cam_fd_device {
-	struct cam_fd_hw_caps    hw_caps;
-	struct cam_hw_intf      *hw_intf;
-	bool                     ready_to_process;
-	uint32_t                 num_ctxts;
-	bool                     valid;
-	struct mutex             lock;
+	struct cam_fd_hw_caps     hw_caps;
+	struct cam_hw_intf       *hw_intf;
+	bool                      ready_to_process;
+	uint32_t                  num_ctxts;
+	bool                      valid;
+	struct mutex              lock;
+	struct cam_fd_hw_mgr_ctx *cur_hw_ctx;
+	int64_t                   req_id;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
index 51fcdcaa..51c8e4a 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.c
@@ -12,6 +12,7 @@
 
 #include "cam_fd_hw_core.h"
 #include "cam_fd_hw_soc.h"
+#include "cam_trace.h"
 
 #define CAM_FD_REG_VAL_PAIR_SIZE 256
 
@@ -30,6 +31,7 @@
 static void cam_fd_hw_util_cdm_callback(uint32_t handle, void *userdata,
 	enum cam_cdm_cb_status status, uint32_t cookie)
 {
+	trace_cam_cdm_cb("FD", status);
 	CAM_DBG(CAM_FD, "CDM hdl=%x, udata=%pK, status=%d, cookie=%d",
 		handle, userdata, status, cookie);
 }
@@ -124,13 +126,8 @@
 
 	time_left = wait_for_completion_timeout(&fd_core->reset_complete,
 		msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
-	if (time_left <= 0) {
-		CAM_ERR(CAM_FD, "HW reset wait failed time_left=%d", time_left);
-		return -EPERM;
-	}
-
-	cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
-		hw_static_info->core_regs.control, 0x0);
+	if (time_left <= 0)
+		CAM_WARN(CAM_FD, "HW reset timeout time_left=%d", time_left);
 
 	CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete");
 
@@ -148,9 +145,6 @@
 	/* Before triggering halt to HW, clear halt complete */
 	reinit_completion(&fd_core->halt_complete);
 
-	cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
-		hw_static_info->core_regs.control, 0x1);
-
 	if (hw_static_info->enable_errata_wa.single_irq_only) {
 		cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
 			hw_static_info->wrapper_regs.irq_mask,
@@ -162,13 +156,8 @@
 
 	time_left = wait_for_completion_timeout(&fd_core->halt_complete,
 		msecs_to_jiffies(CAM_FD_HW_HALT_RESET_TIMEOUT));
-	if (time_left <= 0) {
-		CAM_ERR(CAM_FD, "HW halt wait failed time_left=%d", time_left);
-		return -EPERM;
-	}
-
-	cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
-		hw_static_info->core_regs.control, 0x0);
+	if (time_left <= 0)
+		CAM_WARN(CAM_FD, "HW halt timeout time_left=%d", time_left);
 
 	CAM_DBG(CAM_FD, "FD Wrapper Halt complete");
 
@@ -397,12 +386,22 @@
 	prestart_args->pre_config_buf_size =
 		prestart_args->size - available_size;
 
-	/*
-	 * Currently, no post config commands, we trigger HW start directly
-	 * from start(). Start trigger command can be inserted into CDM
-	 * as post config commands.
-	 */
-	prestart_args->post_config_buf_size = 0;
+	/* Insert start trigger command into CDM as post config commands. */
+	num_cmds = cam_fd_cdm_write_reg_val_pair(reg_val_pair, 0,
+		hw_static_info->core_regs.control, 0x2);
+	size = ctx_hw_private->cdm_ops->cdm_required_size_reg_random(
+		num_cmds/2);
+	if ((size * 4) > available_size) {
+		CAM_ERR(CAM_FD, "Insufficient size:%d , expected size:%d",
+			available_size, size);
+		return -ENOMEM;
+	}
+	ctx_hw_private->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmds/2,
+		reg_val_pair);
+	cmd_buf_addr += size;
+	available_size -= (size * 4);
+
+	prestart_args->post_config_buf_size = size * 4;
 
 	CAM_DBG(CAM_FD, "PreConfig [%pK %d], PostConfig[%pK %d]",
 		prestart_args->cmd_buf_addr, prestart_args->pre_config_buf_size,
@@ -425,9 +424,10 @@
 	struct cam_fd_hw_req_private *req_private;
 	uint32_t base, face_cnt;
 	uint32_t *buffer;
+	unsigned long flags;
 	int i;
 
-	spin_lock(&fd_core->spin_lock);
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	if ((fd_core->core_state != CAM_FD_CORE_STATE_IDLE) ||
 		(fd_core->results_valid == false) ||
 		!fd_core->hw_req_private) {
@@ -435,12 +435,12 @@
 			"Invalid state for results state=%d, results=%d %pK",
 			fd_core->core_state, fd_core->results_valid,
 			fd_core->hw_req_private);
-		spin_unlock(&fd_core->spin_lock);
+		spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 		return -EINVAL;
 	}
 	fd_core->core_state = CAM_FD_CORE_STATE_READING_RESULTS;
 	req_private = fd_core->hw_req_private;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	/*
 	 * Copy the register value as is into output buffers.
@@ -512,10 +512,10 @@
 		}
 	}
 
-	spin_lock(&fd_core->spin_lock);
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	fd_core->hw_req_private = NULL;
 	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	return 0;
 }
@@ -573,6 +573,8 @@
 		return -EINVAL;
 	}
 
+	trace_cam_irq_activated("FD", irq_type);
+
 	cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
 		hw_static_info->wrapper_regs.irq_clear,
 		hw_static_info->irq_mask);
@@ -641,6 +643,7 @@
 	struct cam_fd_hw_init_args *init_args =
 		(struct cam_fd_hw_init_args *)init_hw_args;
 	int rc = 0;
+	unsigned long flags;
 
 	if (!fd_hw || !init_args) {
 		CAM_ERR(CAM_FD, "Invalid argument %pK %pK", fd_hw, init_args);
@@ -660,7 +663,6 @@
 
 	if (fd_hw->open_count > 0) {
 		rc = 0;
-		mutex_unlock(&fd_hw->hw_mutex);
 		goto cdm_streamon;
 	}
 
@@ -670,6 +672,11 @@
 		goto unlock_return;
 	}
 
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
+	fd_hw->hw_state = CAM_HW_STATE_POWER_UP;
+	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+
 	rc = cam_fd_hw_reset(hw_priv, NULL, 0);
 	if (rc) {
 		CAM_ERR(CAM_FD, "Reset Failed, rc=%d", rc);
@@ -678,14 +685,10 @@
 
 	cam_fd_hw_util_enable_power_on_settings(fd_hw);
 
-	fd_hw->hw_state = CAM_HW_STATE_POWER_UP;
-	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
+cdm_streamon:
 	fd_hw->open_count++;
 	CAM_DBG(CAM_FD, "FD HW Init ref count after %d", fd_hw->open_count);
 
-	mutex_unlock(&fd_hw->hw_mutex);
-
-cdm_streamon:
 	if (init_args->ctx_hw_private) {
 		struct cam_fd_ctx_hw_private *ctx_hw_private =
 			init_args->ctx_hw_private;
@@ -694,15 +697,24 @@
 		if (rc) {
 			CAM_ERR(CAM_FD, "CDM StreamOn fail :handle=0x%x, rc=%d",
 				ctx_hw_private->cdm_handle, rc);
-			return rc;
+			fd_hw->open_count--;
+			if (!fd_hw->open_count)
+				goto disable_soc;
 		}
 	}
 
+	mutex_unlock(&fd_hw->hw_mutex);
+
 	return rc;
 
 disable_soc:
 	if (cam_fd_soc_disable_resources(&fd_hw->soc_info))
 		CAM_ERR(CAM_FD, "Error in disable soc resources");
+
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
+	fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+	fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 unlock_return:
 	mutex_unlock(&fd_hw->hw_mutex);
 	return rc;
@@ -711,10 +723,11 @@
 int cam_fd_hw_deinit(void *hw_priv, void *deinit_hw_args, uint32_t arg_size)
 {
 	struct cam_hw_info *fd_hw = hw_priv;
-	struct cam_fd_core *fd_core;
+	struct cam_fd_core *fd_core = NULL;
 	struct cam_fd_hw_deinit_args *deinit_args =
 		(struct cam_fd_hw_deinit_args *)deinit_hw_args;
 	int rc = 0;
+	unsigned long flags;
 
 	if (!fd_hw || !deinit_hw_args) {
 		CAM_ERR(CAM_FD, "Invalid argument");
@@ -727,23 +740,7 @@
 		return -EINVAL;
 	}
 
-	fd_core = (struct cam_fd_core *)fd_hw->core_info;
-
-	if (deinit_args->ctx_hw_private) {
-		struct cam_fd_ctx_hw_private *ctx_hw_private =
-			deinit_args->ctx_hw_private;
-
-		rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
-		if (rc) {
-			CAM_ERR(CAM_FD,
-				"Failed in CDM StreamOff, handle=0x%x, rc=%d",
-				ctx_hw_private->cdm_handle, rc);
-			return rc;
-		}
-	}
-
 	mutex_lock(&fd_hw->hw_mutex);
-
 	if (fd_hw->open_count == 0) {
 		mutex_unlock(&fd_hw->hw_mutex);
 		CAM_ERR(CAM_FD, "Error Unbalanced deinit");
@@ -753,9 +750,9 @@
 	fd_hw->open_count--;
 	CAM_DBG(CAM_FD, "FD HW ref count=%d", fd_hw->open_count);
 
-	if (fd_hw->open_count) {
+	if (fd_hw->open_count > 0) {
 		rc = 0;
-		goto unlock_return;
+		goto positive_ref_cnt;
 	}
 
 	rc = cam_fd_soc_disable_resources(&fd_hw->soc_info);
@@ -763,9 +760,27 @@
 		CAM_ERR(CAM_FD, "Failed in Disable SOC, rc=%d", rc);
 
 	fd_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
-	fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
+	fd_core = (struct cam_fd_core *)fd_hw->core_info;
 
-unlock_return:
+	/* With the ref_cnt correct, this should never happen */
+	WARN_ON(!fd_core);
+
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
+	fd_core->core_state = CAM_FD_CORE_STATE_POWERDOWN;
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+positive_ref_cnt:
+	if (deinit_args->ctx_hw_private) {
+		struct cam_fd_ctx_hw_private *ctx_hw_private =
+			deinit_args->ctx_hw_private;
+
+		rc = cam_cdm_stream_off(ctx_hw_private->cdm_handle);
+		if (rc) {
+			CAM_ERR(CAM_FD,
+				"Failed in CDM StreamOff, handle=0x%x, rc=%d",
+				ctx_hw_private->cdm_handle, rc);
+		}
+	}
+
 	mutex_unlock(&fd_hw->hw_mutex);
 	return rc;
 }
@@ -774,6 +789,9 @@
 {
 	struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
 	struct cam_fd_core *fd_core;
+	struct cam_fd_hw_static_info *hw_static_info;
+	struct cam_hw_soc_info *soc_info;
+	unsigned long flags;
 	int rc;
 
 	if (!fd_hw) {
@@ -782,18 +800,30 @@
 	}
 
 	fd_core = (struct cam_fd_core *)fd_hw->core_info;
+	hw_static_info = fd_core->hw_static_info;
+	soc_info = &fd_hw->soc_info;
 
-	spin_lock(&fd_core->spin_lock);
-	if (fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS) {
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
+	if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) ||
+		(fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) {
 		CAM_ERR(CAM_FD, "Reset not allowed in %d state",
 			fd_core->core_state);
-		spin_unlock(&fd_core->spin_lock);
+		spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 		return -EINVAL;
 	}
 
 	fd_core->results_valid = false;
 	fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+
+	cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+		hw_static_info->wrapper_regs.cgc_disable, 0x1);
+
+	rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
+	if (rc) {
+		CAM_ERR(CAM_FD, "Failed in HALT rc=%d", rc);
+		return rc;
+	}
 
 	rc = cam_fd_hw_util_fdwrapper_sync_reset(fd_hw);
 	if (rc) {
@@ -801,9 +831,12 @@
 		return rc;
 	}
 
-	spin_lock(&fd_core->spin_lock);
+	cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+		hw_static_info->wrapper_regs.cgc_disable, 0x0);
+
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	return rc;
 }
@@ -816,6 +849,7 @@
 	struct cam_fd_hw_cmd_start_args *start_args =
 		(struct cam_fd_hw_cmd_start_args *)hw_start_args;
 	struct cam_fd_ctx_hw_private *ctx_hw_private;
+	unsigned long flags;
 	int rc;
 
 	if (!hw_priv || !start_args) {
@@ -833,11 +867,11 @@
 	fd_core = (struct cam_fd_core *)fd_hw->core_info;
 	hw_static_info = fd_core->hw_static_info;
 
-	spin_lock(&fd_core->spin_lock);
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	if (fd_core->core_state != CAM_FD_CORE_STATE_IDLE) {
 		CAM_ERR(CAM_FD, "Cannot start in %d state",
 			fd_core->core_state);
-		spin_unlock(&fd_core->spin_lock);
+		spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 		return -EINVAL;
 	}
 
@@ -850,7 +884,7 @@
 	fd_core->hw_req_private = start_args->hw_req_private;
 	fd_core->core_state = CAM_FD_CORE_STATE_PROCESSING;
 	fd_core->results_valid = false;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	ctx_hw_private = start_args->ctx_hw_private;
 
@@ -893,14 +927,11 @@
 		goto error;
 	}
 
-	cam_fd_soc_register_write(&fd_hw->soc_info, CAM_FD_REG_CORE,
-		hw_static_info->core_regs.control, 0x2);
-
 	return 0;
 error:
-	spin_lock(&fd_core->spin_lock);
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	return rc;
 }
@@ -909,6 +940,9 @@
 {
 	struct cam_hw_info *fd_hw = (struct cam_hw_info *)hw_priv;
 	struct cam_fd_core *fd_core;
+	struct cam_fd_hw_static_info *hw_static_info;
+	struct cam_hw_soc_info *soc_info;
+	unsigned long flags;
 	int rc;
 
 	if (!fd_hw) {
@@ -917,19 +951,24 @@
 	}
 
 	fd_core = (struct cam_fd_core *)fd_hw->core_info;
+	hw_static_info = fd_core->hw_static_info;
+	soc_info = &fd_hw->soc_info;
 
-	spin_lock(&fd_core->spin_lock);
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	if ((fd_core->core_state == CAM_FD_CORE_STATE_POWERDOWN) ||
 		(fd_core->core_state == CAM_FD_CORE_STATE_RESET_PROGRESS)) {
 		CAM_ERR(CAM_FD, "Reset not allowed in %d state",
 			fd_core->core_state);
-		spin_unlock(&fd_core->spin_lock);
+		spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 		return -EINVAL;
 	}
 
 	fd_core->results_valid = false;
 	fd_core->core_state = CAM_FD_CORE_STATE_RESET_PROGRESS;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
+
+	cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+		hw_static_info->wrapper_regs.cgc_disable, 0x1);
 
 	rc = cam_fd_hw_util_fdwrapper_halt(fd_hw);
 	if (rc) {
@@ -944,9 +983,12 @@
 		return rc;
 	}
 
-	spin_lock(&fd_core->spin_lock);
+	cam_fd_soc_register_write(soc_info, CAM_FD_REG_WRAPPER,
+		hw_static_info->wrapper_regs.cgc_disable, 0x0);
+
+	spin_lock_irqsave(&fd_core->spin_lock, flags);
 	fd_core->core_state = CAM_FD_CORE_STATE_IDLE;
-	spin_unlock(&fd_core->spin_lock);
+	spin_unlock_irqrestore(&fd_core->spin_lock, flags);
 
 	return rc;
 }
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
index 35bf6b6..3d9c5f0 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_core.h
@@ -30,7 +30,7 @@
 #define CAM_FD_IRQ_TO_MASK(irq)        (1 << (irq))
 #define CAM_FD_MASK_TO_IRQ(mask, irq)  ((mask) >> (irq))
 
-#define CAM_FD_HW_HALT_RESET_TIMEOUT   3000
+#define CAM_FD_HW_HALT_RESET_TIMEOUT   750
 
 /**
  * enum cam_fd_core_state - FD Core internal states
diff --git a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
index 9045dc1..f27d016 100644
--- a/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
+++ b/drivers/media/platform/msm/camera/cam_fd/fd_hw_mgr/fd_hw/cam_fd_hw_soc.c
@@ -20,11 +20,16 @@
 #include "cam_fd_hw_core.h"
 #include "cam_fd_hw_soc.h"
 
-static void cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
-	enum cam_camnoc_irq_type event_type, uint32_t event_data)
+static bool cam_fd_hw_util_cpas_callback(uint32_t handle, void *userdata,
+	struct cam_cpas_irq_data *irq_data)
 {
-	CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, event=%d, event_data=%d",
-		handle, userdata, event_type, event_data);
+	if (!irq_data)
+		return false;
+
+	CAM_DBG(CAM_FD, "CPAS hdl=%d, udata=%pK, irq_type=%d",
+		handle, userdata, irq_data->irq_type);
+
+	return false;
 }
 
 static int cam_fd_hw_soc_util_setup_regbase_indices(
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
index 15bd98c..0c37994 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_context.c
@@ -26,6 +26,8 @@
 #include "cam_trace.h"
 #include "cam_debug_util.h"
 
+static const char icp_dev_name[] = "icp";
+
 static int __cam_icp_acquire_dev_in_available(struct cam_context *ctx,
 	struct cam_acquire_dev_cmd *cmd)
 {
@@ -171,8 +173,8 @@
 		goto err;
 	}
 
-	rc = cam_context_init(ctx->base, NULL, hw_intf, ctx->req_base,
-		CAM_CTX_REQ_MAX);
+	rc = cam_context_init(ctx->base, icp_dev_name, NULL, hw_intf,
+		ctx->req_base, CAM_CTX_REQ_MAX);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "Camera Context Base init failed");
 		goto err;
diff --git a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
index bbdff27..51499de 100644
--- a/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
@@ -41,8 +41,8 @@
 struct cam_icp_subdev {
 	struct cam_subdev sd;
 	struct cam_node *node;
-	struct cam_context ctx[CAM_CTX_MAX];
-	struct cam_icp_context ctx_icp[CAM_CTX_MAX];
+	struct cam_context ctx[CAM_ICP_CTX_MAX];
+	struct cam_icp_context ctx_icp[CAM_ICP_CTX_MAX];
 	struct mutex icp_lock;
 	int32_t open_cnt;
 	int32_t reserved;
@@ -76,7 +76,7 @@
 	}
 
 	hw_mgr_intf = &node->hw_mgr_intf;
-	rc = hw_mgr_intf->download_fw(hw_mgr_intf->hw_mgr_priv, NULL);
+	rc = hw_mgr_intf->hw_open(hw_mgr_intf->hw_mgr_priv, NULL);
 	if (rc < 0) {
 		CAM_ERR(CAM_ICP, "FW download failed");
 		goto end;
@@ -114,7 +114,7 @@
 		goto end;
 	}
 
-	rc = hw_mgr_intf->hw_close(hw_mgr_intf->hw_mgr_priv, NULL);
+	rc = cam_node_shutdown(node);
 	if (rc < 0) {
 		CAM_ERR(CAM_ICP, "HW close failed");
 		goto end;
@@ -164,7 +164,7 @@
 		goto hw_init_fail;
 	}
 
-	for (i = 0; i < CAM_CTX_MAX; i++) {
+	for (i = 0; i < CAM_ICP_CTX_MAX; i++) {
 		g_icp_dev.ctx_icp[i].base = &g_icp_dev.ctx[i];
 		rc = cam_icp_context_init(&g_icp_dev.ctx_icp[i],
 					hw_mgr_intf);
@@ -175,7 +175,7 @@
 	}
 
 	rc = cam_node_init(node, hw_mgr_intf, g_icp_dev.ctx,
-				CAM_CTX_MAX, CAM_ICP_DEV_NAME);
+				CAM_ICP_CTX_MAX, CAM_ICP_DEV_NAME);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "ICP node init failed");
 		goto ctx_fail;
@@ -220,7 +220,7 @@
 		return -ENODEV;
 	}
 
-	for (i = 0; i < CAM_CTX_MAX; i++)
+	for (i = 0; i < CAM_ICP_CTX_MAX; i++)
 		cam_icp_context_deinit(&g_icp_dev.ctx_icp[i]);
 	cam_node_deinit(g_icp_dev.node);
 	cam_subdev_remove(&g_icp_dev.sd);
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
index f74938d..ce7a8b3 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_intf.h
@@ -60,10 +60,12 @@
  * hfi_read_message() - function for hfi read
  * @pmsg: buffer to place read message for hfi queue
  * @q_id: queue id
+ * @words_read: total number of words read from the queue
+ *              returned as output to the caller
  *
  * Returns success(zero)/failure(non zero)
  */
-int hfi_read_message(uint32_t *pmsg, uint8_t q_id);
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id, uint32_t *words_read);
 
 /**
  * hfi_init() - function initialize hfi after firmware download
@@ -109,6 +111,11 @@
  * cam_hfi_deinit() - cleanup HFI
  */
 void cam_hfi_deinit(void);
+/**
+ * hfi_set_debug_level() - set debug level
+ * @lvl: FW debug message level
+ */
+int hfi_set_debug_level(uint32_t lvl);
 
 /**
  * hfi_enable_ipe_bps_pc() - Enable interframe pc
@@ -119,4 +126,10 @@
  */
 int hfi_enable_ipe_bps_pc(bool enable);
 
+/**
+ * hfi_cmd_ubwc_config() - UBWC configuration to firmware
+ * @ubwc_cfg: UBWC configuration parameters
+ */
+int hfi_cmd_ubwc_config(uint32_t *ubwc_cfg);
+
 #endif /* _HFI_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_reg.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_reg.h
index 04e3c85..eb4b132 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_reg.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_reg.h
@@ -60,11 +60,11 @@
 
 #define ICP_CMD_Q_SIZE_IN_BYTES                 4096
 #define ICP_MSG_Q_SIZE_IN_BYTES                 4096
-#define ICP_DBG_Q_SIZE_IN_BYTES                 8192
+#define ICP_DBG_Q_SIZE_IN_BYTES                 102400
 
 #define ICP_SHARED_MEM_IN_BYTES                 (1024 * 1024)
 #define ICP_UNCACHED_HEAP_SIZE_IN_BYTES         (2 * 1024 * 1024)
-#define ICP_HFI_MAX_MSG_SIZE_IN_WORDS           128
+#define ICP_HFI_MAX_PKT_SIZE_IN_WORDS           25600
 
 #define ICP_HFI_QTBL_HOSTID1                    0x01000000
 #define ICP_HFI_QTBL_STATUS_ENABLED             0x00000001
@@ -109,7 +109,8 @@
  */
 enum reg_settings {
 	RESET,
-	SET
+	SET,
+	SET_WM = 1024
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
index 65dc4b3..aaa18bb 100644
--- a/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
+++ b/drivers/media/platform/msm/camera/cam_icp/fw_inc/hfi_sys_defs.h
@@ -156,6 +156,7 @@
 #define HFI_PROPERTY_ICP_COMMON_START  (HFI_DOMAIN_BASE_ICP + 0x0)
 
 #define HFI_PROP_SYS_DEBUG_CFG         (HFI_PROPERTY_ICP_COMMON_START + 0x1)
+#define HFI_PROP_SYS_UBWC_CFG          (HFI_PROPERTY_ICP_COMMON_START + 0x2)
 #define HFI_PROP_SYS_IMAGE_VER         (HFI_PROPERTY_ICP_COMMON_START + 0x3)
 #define HFI_PROP_SYS_SUPPORTED         (HFI_PROPERTY_ICP_COMMON_START + 0x4)
 #define HFI_PROP_SYS_IPEBPS_PC         (HFI_PROPERTY_ICP_COMMON_START + 0x5)
@@ -201,6 +202,8 @@
 #define HFI_DEBUG_MODE_QUEUE     0x00000001
 #define HFI_DEBUG_MODE_QDSS      0x00000002
 
+#define HFI_DEV_VERSION_MAX      0x5
+
 /**
  * start of sys command packet types
  * These commands are used to get system level information
@@ -257,6 +260,17 @@
 } __packed;
 
 /**
+ * struct hfi_cmd_ubwc_cfg
+ * Payload structure to configure HFI_PROP_SYS_UBWC_CFG
+ * @ubwc_fetch_cfg: UBWC configuration for fecth
+ * @ubwc_write_cfg: UBWC configuration for write
+ */
+struct hfi_cmd_ubwc_cfg {
+	uint32_t ubwc_fetch_cfg;
+	uint32_t ubwc_write_cfg;
+};
+
+/**
  * struct hfi_cmd_sys_init
  * command to initialization of system session
  * @size: packet size in bytes
@@ -371,14 +385,30 @@
 } __packed;
 
 /**
+ * struct hfi_msg_init_done_data
+ * @api_ver:    Firmware API version
+ * @dev_ver:    Device version
+ * @num_icp_hw: Number of ICP hardware information
+ * @dev_hw_ver: Supported hardware version information
+ * @reserved:   Reserved field
+ */
+struct hfi_msg_init_done_data {
+	uint32_t api_ver;
+	uint32_t dev_ver;
+	uint32_t num_icp_hw;
+	uint32_t dev_hw_ver[HFI_DEV_VERSION_MAX];
+	uint32_t reserved;
+};
+
+/**
  * struct hfi_msg_init_done
  * system init done message from firmware. Many system level properties
  * are returned with the packet
- * @size: packet size in bytes
- * @pkt_type: opcode of a packet
- * @err_type: error code associated with response
- * @num_prop: number of default capability info
- * @prop_data: array of property ids and corresponding structure pairs
+ * @size:      Packet size in bytes
+ * @pkt_type:  Opcode of a packet
+ * @err_type:  Error code associated with response
+ * @num_prop:  Number of default capability info
+ * @prop_data: Array of property ids and corresponding structure pairs
  */
 struct hfi_msg_init_done {
 	uint32_t size;
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index a315268..a8855ae 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -27,6 +27,7 @@
 #include "hfi_intf.h"
 #include "cam_icp_hw_mgr_intf.h"
 #include "cam_debug_util.h"
+#include "cam_soc_util.h"
 
 #define HFI_VERSION_INFO_MAJOR_VAL  1
 #define HFI_VERSION_INFO_MINOR_VAL  1
@@ -39,9 +40,6 @@
 #define HFI_VERSION_INFO_STEP_BMSK   0xFF
 #define HFI_VERSION_INFO_STEP_SHFT  0
 
-#define SOC_VERSION_HW1             0x10000
-#define SOC_VERSION_HW2             0x20000
-
 static struct hfi_info *g_hfi;
 unsigned int g_icp_mmu_hdl;
 static DEFINE_MUTEX(hfi_cmd_q_mutex);
@@ -111,7 +109,19 @@
 			new_write_idx << BYTE_WORD_SHIFT);
 	}
 
+	/*
+	 * To make sure command data in a command queue before
+	 * updating write index
+	 */
+	wmb();
+
 	q->qhdr_write_idx = new_write_idx;
+
+	/*
+	 * Before raising interrupt make sure command data is ready for
+	 * firmware to process
+	 */
+	wmb();
 	cam_io_w((uint32_t)INTR_ENABLE,
 		g_hfi->csr_base + HFI_REG_A5_CSR_HOST2ICPINT);
 err:
@@ -119,12 +129,13 @@
 	return rc;
 }
 
-int hfi_read_message(uint32_t *pmsg, uint8_t q_id)
+int hfi_read_message(uint32_t *pmsg, uint8_t q_id,
+	uint32_t *words_read)
 {
 	struct hfi_qtbl *q_tbl_ptr;
 	struct hfi_q_hdr *q;
-	uint32_t new_read_idx, size_in_words, temp;
-	uint32_t *read_q, *read_ptr;
+	uint32_t new_read_idx, size_in_words, word_diff, temp;
+	uint32_t *read_q, *read_ptr, *write_ptr;
 	int rc = 0;
 
 	if (!pmsg) {
@@ -168,10 +179,22 @@
 		read_q = (uint32_t *)g_hfi->map.dbg_q.kva;
 
 	read_ptr = (uint32_t *)(read_q + q->qhdr_read_idx);
-	size_in_words = (*read_ptr) >> BYTE_WORD_SHIFT;
+	write_ptr = (uint32_t *)(read_q + q->qhdr_write_idx);
+
+	if (write_ptr > read_ptr)
+		size_in_words = write_ptr - read_ptr;
+	else {
+		word_diff = read_ptr - write_ptr;
+		if (q_id == Q_MSG)
+			size_in_words = (ICP_MSG_Q_SIZE_IN_BYTES >>
+			BYTE_WORD_SHIFT) - word_diff;
+		else
+			size_in_words = (ICP_DBG_Q_SIZE_IN_BYTES >>
+			BYTE_WORD_SHIFT) - word_diff;
+	}
 
 	if ((size_in_words == 0) ||
-		(size_in_words > ICP_HFI_MAX_MSG_SIZE_IN_WORDS)) {
+		(size_in_words > ICP_HFI_MAX_PKT_SIZE_IN_WORDS)) {
 		CAM_ERR(CAM_HFI, "invalid HFI message packet size - 0x%08x",
 			size_in_words << BYTE_WORD_SHIFT);
 		q->qhdr_read_idx = q->qhdr_write_idx;
@@ -192,11 +215,39 @@
 	}
 
 	q->qhdr_read_idx = new_read_idx;
+	*words_read = size_in_words;
 err:
 	mutex_unlock(&hfi_msg_q_mutex);
 	return rc;
 }
 
+int hfi_cmd_ubwc_config(uint32_t *ubwc_cfg)
+{
+	uint8_t *prop;
+	struct hfi_cmd_prop *dbg_prop;
+	uint32_t size = 0;
+
+	size = sizeof(struct hfi_cmd_prop) +
+		sizeof(struct hfi_cmd_ubwc_cfg);
+
+	prop = kzalloc(size, GFP_KERNEL);
+	if (!prop)
+		return -ENOMEM;
+
+	dbg_prop = (struct hfi_cmd_prop *)prop;
+	dbg_prop->size = size;
+	dbg_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+	dbg_prop->num_prop = 1;
+	dbg_prop->prop_data[0] = HFI_PROP_SYS_UBWC_CFG;
+	dbg_prop->prop_data[1] = ubwc_cfg[0];
+	dbg_prop->prop_data[2] = ubwc_cfg[1];
+
+	hfi_write_cmd(prop);
+	kfree(prop);
+
+	return 0;
+}
+
 int hfi_enable_ipe_bps_pc(bool enable)
 {
 	uint8_t *prop;
@@ -223,6 +274,45 @@
 	return 0;
 }
 
+int hfi_set_debug_level(uint32_t lvl)
+{
+	uint8_t *prop;
+	struct hfi_cmd_prop *dbg_prop;
+	uint32_t size = 0, val;
+
+	val = HFI_DEBUG_MSG_LOW |
+		HFI_DEBUG_MSG_MEDIUM |
+		HFI_DEBUG_MSG_HIGH |
+		HFI_DEBUG_MSG_ERROR |
+		HFI_DEBUG_MSG_FATAL |
+		HFI_DEBUG_MSG_PERF |
+		HFI_DEBUG_CFG_WFI |
+		HFI_DEBUG_CFG_ARM9WD;
+
+	if (lvl > val)
+		return -EINVAL;
+
+	size = sizeof(struct hfi_cmd_prop) +
+		sizeof(struct hfi_debug);
+
+	prop = kzalloc(size, GFP_KERNEL);
+	if (!prop)
+		return -ENOMEM;
+
+	dbg_prop = (struct hfi_cmd_prop *)prop;
+	dbg_prop->size = size;
+	dbg_prop->pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+	dbg_prop->num_prop = 1;
+	dbg_prop->prop_data[0] = HFI_PROP_SYS_DEBUG_CFG;
+	dbg_prop->prop_data[1] = lvl;
+	dbg_prop->prop_data[2] = HFI_DEBUG_MODE_QUEUE;
+
+	hfi_write_cmd(prop);
+	kfree(prop);
+
+	return 0;
+}
+
 void hfi_send_system_cmd(uint32_t type, uint64_t data, uint32_t size)
 {
 	switch (type) {
@@ -385,10 +475,15 @@
 		ICP_FLAG_CSR_WAKE_UP_EN | ICP_CSR_EN_CLKGATE_WFI),
 		icp_base + HFI_REG_A5_CSR_A5_CONTROL);
 	} else {
+		/* Due to hardware bug in V1 ICP clock gating has to be
+		 * disabled, this is supposed to be fixed in V-2. But enabling
+		 * the clock gating is causing the firmware hang, hence
+		 * disabling the clock gating on both V1 and V2 until the
+		 * hardware team root causes this
+		 */
 		cam_io_w((uint32_t)ICP_FLAG_CSR_A5_EN |
 			ICP_FLAG_CSR_WAKE_UP_EN |
-			((soc_version == SOC_VERSION_HW1) ?
-			(ICP_CSR_EN_CLKGATE_WFI) : (0x0)),
+			ICP_CSR_EN_CLKGATE_WFI,
 			icp_base + HFI_REG_A5_CSR_A5_CONTROL);
 	}
 
@@ -451,8 +546,8 @@
 
 		dbg_q_hdr->qhdr_type = Q_DBG;
 		dbg_q_hdr->qhdr_rx_wm = SET;
-		dbg_q_hdr->qhdr_tx_wm = SET;
-		dbg_q_hdr->qhdr_rx_req = SET;
+		dbg_q_hdr->qhdr_tx_wm = SET_WM;
+		dbg_q_hdr->qhdr_rx_req = RESET;
 		dbg_q_hdr->qhdr_tx_req = RESET;
 		dbg_q_hdr->qhdr_rx_irq_status = RESET;
 		dbg_q_hdr->qhdr_tx_irq_status = RESET;
@@ -490,8 +585,8 @@
 		dbg_q_hdr->qhdr_type = Q_DBG | TX_EVENT_DRIVEN_MODE_2 |
 			RX_EVENT_DRIVEN_MODE_2;
 		dbg_q_hdr->qhdr_rx_wm = SET;
-		dbg_q_hdr->qhdr_tx_wm = SET;
-		dbg_q_hdr->qhdr_rx_req = SET;
+		dbg_q_hdr->qhdr_tx_wm = SET_WM;
+		dbg_q_hdr->qhdr_rx_req = RESET;
 		dbg_q_hdr->qhdr_tx_req = RESET;
 		dbg_q_hdr->qhdr_rx_irq_status = RESET;
 		dbg_q_hdr->qhdr_tx_irq_status = RESET;
@@ -569,17 +664,3 @@
 	mutex_unlock(&hfi_cmd_q_mutex);
 	mutex_unlock(&hfi_msg_q_mutex);
 }
-
-void icp_enable_fw_debug(void)
-{
-	hfi_send_system_cmd(HFI_CMD_SYS_SET_PROPERTY,
-		(uint64_t)HFI_PROP_SYS_DEBUG_CFG, 0);
-}
-
-int icp_ping_fw(void)
-{
-	hfi_send_system_cmd(HFI_CMD_SYS_PING,
-		(uint64_t)0x12123434, 0);
-
-	return 0;
-}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_core.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_core.c
index e200f6f..635d0df 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_core.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_core.c
@@ -266,8 +266,8 @@
 
 	cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
 	cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
-	cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
-	cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
+	cpas_vote.axi_vote.compressed_bw = CAM_ICP_A5_BW_BYTES_VOTE;
+	cpas_vote.axi_vote.uncompressed_bw = CAM_ICP_A5_BW_BYTES_VOTE;
 
 	rc = cam_cpas_start(core_info->cpas_handle,
 		&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
@@ -367,6 +367,7 @@
 	struct cam_hw_soc_info *soc_info = NULL;
 	struct cam_a5_device_core_info *core_info = NULL;
 	struct cam_a5_device_hw_info *hw_info = NULL;
+	struct a5_soc_info *a5_soc = NULL;
 	int rc = 0;
 
 	if (!device_priv) {
@@ -456,6 +457,14 @@
 			core_info->cpas_start = false;
 		}
 		break;
+	case CAM_ICP_A5_CMD_UBWC_CFG:
+		a5_soc = soc_info->soc_private;
+		if (!a5_soc) {
+			CAM_ERR(CAM_ICP, "A5 private soc info is NULL");
+			return -EINVAL;
+		}
+		rc = hfi_cmd_ubwc_config(a5_soc->ubwc_cfg);
+		break;
 	default:
 		break;
 	}
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
index 99e2e79..14c3c9c 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_dev.c
@@ -50,6 +50,40 @@
 };
 EXPORT_SYMBOL(cam_a5_hw_info);
 
+static bool cam_a5_cpas_cb(uint32_t client_handle, void *userdata,
+	struct cam_cpas_irq_data *irq_data)
+{
+	bool error_handled = false;
+
+	if (!irq_data)
+		return error_handled;
+
+	switch (irq_data->irq_type) {
+	case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_DECODE_ERROR:
+		CAM_ERR_RATE_LIMIT(CAM_ICP,
+			"IPE/BPS UBWC Decode error type=%d status=%x thr_err=%d, fcl_err=%d, len_md_err=%d, format_err=%d",
+			irq_data->irq_type,
+			irq_data->u.dec_err.decerr_status.value,
+			irq_data->u.dec_err.decerr_status.thr_err,
+			irq_data->u.dec_err.decerr_status.fcl_err,
+			irq_data->u.dec_err.decerr_status.len_md_err,
+			irq_data->u.dec_err.decerr_status.format_err);
+		error_handled = true;
+		break;
+	case CAM_CAMNOC_IRQ_IPE_BPS_UBWC_ENCODE_ERROR:
+		CAM_ERR_RATE_LIMIT(CAM_ICP,
+			"IPE/BPS UBWC Encode error type=%d status=%x",
+			irq_data->irq_type,
+			irq_data->u.enc_err.encerr_status.value);
+		error_handled = true;
+		break;
+	default:
+		break;
+	}
+
+	return error_handled;
+}
+
 int cam_a5_register_cpas(struct cam_hw_soc_info *soc_info,
 			struct cam_a5_device_core_info *core_info,
 			uint32_t hw_idx)
@@ -59,7 +93,7 @@
 
 	cpas_register_params.dev = &soc_info->pdev->dev;
 	memcpy(cpas_register_params.identifier, "icp", sizeof("icp"));
-	cpas_register_params.cam_cpas_client_cb = NULL;
+	cpas_register_params.cam_cpas_client_cb = cam_a5_cpas_cb;
 	cpas_register_params.cell_index = hw_idx;
 	cpas_register_params.userdata = NULL;
 
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.c
index f252931..3177513 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.c
@@ -22,11 +22,12 @@
 
 static int cam_a5_get_dt_properties(struct cam_hw_soc_info *soc_info)
 {
-	int rc = 0;
+	int rc = 0, i;
 	const char *fw_name;
 	struct a5_soc_info *camp_a5_soc_info;
 	struct device_node *of_node = NULL;
 	struct platform_device *pdev = NULL;
+	int num_ubwc_cfg;
 
 	pdev = soc_info->pdev;
 	of_node = pdev->dev.of_node;
@@ -41,9 +42,28 @@
 	fw_name = camp_a5_soc_info->fw_name;
 
 	rc = of_property_read_string(of_node, "fw_name", &fw_name);
-	if (rc < 0)
+	if (rc < 0) {
 		CAM_ERR(CAM_ICP, "fw_name read failed");
+		goto end;
+	}
 
+	num_ubwc_cfg = of_property_count_u32_elems(of_node, "ubwc-cfg");
+	if ((num_ubwc_cfg < 0) || (num_ubwc_cfg > ICP_UBWC_MAX)) {
+		CAM_ERR(CAM_ICP, "wrong ubwc_cfg: %d", num_ubwc_cfg);
+		rc = num_ubwc_cfg;
+		goto end;
+	}
+
+	for (i = 0; i < num_ubwc_cfg; i++) {
+		rc = of_property_read_u32_index(of_node, "ubwc-cfg",
+			i, &camp_a5_soc_info->ubwc_cfg[i]);
+		if (rc < 0) {
+			CAM_ERR(CAM_ICP, "unable to read ubwc cfg values");
+			break;
+		}
+	}
+
+end:
 	return rc;
 }
 
@@ -81,7 +101,7 @@
 	int rc = 0;
 
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, true);
+		CAM_SVS_VOTE, true);
 	if (rc)
 		CAM_ERR(CAM_ICP, "enable platform failed");
 
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.h
index 916143d..3593cfb 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/a5_hw/a5_soc.h
@@ -15,8 +15,11 @@
 
 #include "cam_soc_util.h"
 
+#define ICP_UBWC_MAX 2
+
 struct a5_soc_info {
 	char *fw_name;
+	uint32_t ubwc_cfg[ICP_UBWC_MAX];
 };
 
 int cam_a5_init_soc_resources(struct cam_hw_soc_info *soc_info,
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
index 2477e7d..400e1e7 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/bps_hw/bps_soc.c
@@ -65,7 +65,7 @@
 	int rc = 0;
 
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, false);
+		CAM_SVS_VOTE, false);
 	if (rc)
 		CAM_ERR(CAM_ICP, "enable platform failed");
 
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
index 7c5b405..340a1e2 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.c
@@ -24,6 +24,7 @@
 #include <linux/debugfs.h>
 #include <media/cam_defs.h>
 #include <media/cam_icp.h>
+#include <media/cam_cpas.h>
 
 #include "cam_sync_api.h"
 #include "cam_packet_util.h"
@@ -46,13 +47,34 @@
 #include "hfi_sys_defs.h"
 #include "cam_debug_util.h"
 #include "cam_soc_util.h"
+#include "cam_trace.h"
+#include "cam_cpas_api.h"
 
-#define ICP_WORKQ_NUM_TASK      30
 #define ICP_WORKQ_TASK_CMD_TYPE 1
 #define ICP_WORKQ_TASK_MSG_TYPE 2
 
 static struct cam_icp_hw_mgr icp_hw_mgr;
 
+static int cam_icp_send_ubwc_cfg(struct cam_icp_hw_mgr *hw_mgr)
+{
+	struct cam_hw_intf *a5_dev_intf = NULL;
+	int rc;
+
+	a5_dev_intf = hw_mgr->devices[CAM_ICP_DEV_A5][0];
+	if (!a5_dev_intf) {
+		CAM_ERR(CAM_ICP, "a5_dev_intf is NULL");
+		return -EINVAL;
+	}
+
+	rc = a5_dev_intf->hw_ops.process_cmd(
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_CMD_UBWC_CFG, NULL, 0);
+	if (rc)
+		CAM_ERR(CAM_ICP, "CAM_ICP_A5_CMD_UBWC_CFG is failed");
+
+	return rc;
+}
+
 static void cam_icp_hw_mgr_clk_info_update(struct cam_icp_hw_mgr *hw_mgr,
 	struct cam_icp_hw_ctx_data *ctx_data)
 {
@@ -73,13 +95,13 @@
 
 	for (i = 0; i < ICP_CLK_HW_MAX; i++) {
 		hw_mgr->clk_info[i].base_clk = 0;
-		hw_mgr->clk_info[i].curr_clk = ICP_TURBO_VOTE;
+		hw_mgr->clk_info[i].curr_clk = ICP_CLK_SVS_HZ;
 		hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD;
 		hw_mgr->clk_info[i].over_clked = 0;
 		hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
 		hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
 	}
-	hw_mgr->icp_default_clk = ICP_SVS_VOTE;
+	hw_mgr->icp_default_clk = ICP_CLK_SVS_HZ;
 }
 
 static int cam_icp_get_actual_clk_rate_idx(
@@ -209,14 +231,14 @@
 	int i;
 
 	for (i = 0; i < ICP_CLK_HW_MAX; i++) {
-		hw_mgr->clk_info[i].base_clk = ICP_TURBO_VOTE;
-		hw_mgr->clk_info[i].curr_clk = ICP_TURBO_VOTE;
+		hw_mgr->clk_info[i].base_clk = ICP_CLK_SVS_HZ;
+		hw_mgr->clk_info[i].curr_clk = ICP_CLK_SVS_HZ;
 		hw_mgr->clk_info[i].threshold = ICP_OVER_CLK_THRESHOLD;
 		hw_mgr->clk_info[i].over_clked = 0;
 		hw_mgr->clk_info[i].uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
 		hw_mgr->clk_info[i].compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
 	}
-	hw_mgr->icp_default_clk = ICP_SVS_VOTE;
+	hw_mgr->icp_default_clk = ICP_CLK_SVS_HZ;
 
 	return 0;
 }
@@ -295,6 +317,7 @@
 {
 	uint32_t next_clk_level;
 	uint32_t actual_clk;
+	bool rc = false;
 
 	/* 1. if current request frame cycles(fc) are more than previous
 	 *      frame fc
@@ -308,7 +331,8 @@
 	 * 2. if current fc is less than or equal to previous  frame fc
 	 *      Still Bump up the clock to next available level
 	 *      if it is available, then update clock, make overclk cnt to
-	 *      zero
+	 *      zero. If the clock is already at highest clock rate then
+	 *      no need to update the clock
 	 */
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
 	ctx_data->clk_info.curr_fc = clk_info->frame_cycles;
@@ -326,14 +350,19 @@
 				ctx_data, hw_mgr_clk_info->curr_clk);
 			hw_mgr_clk_info->curr_clk = next_clk_level;
 		}
+		rc = true;
 	} else {
-		hw_mgr_clk_info->curr_clk =
+		next_clk_level =
 			cam_icp_get_next_clk_rate(hw_mgr, ctx_data,
 			hw_mgr_clk_info->curr_clk);
+		if (hw_mgr_clk_info->curr_clk < next_clk_level) {
+			hw_mgr_clk_info->curr_clk = next_clk_level;
+			rc = true;
+		}
 	}
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
-	return true;
+	return rc;
 }
 
 static bool cam_icp_update_clk_overclk_free(struct cam_icp_hw_mgr *hw_mgr,
@@ -437,7 +466,7 @@
 
 static bool cam_icp_debug_clk_update(struct cam_icp_clk_info *hw_mgr_clk_info)
 {
-	if (icp_hw_mgr.icp_debug_clk < ICP_TURBO_VOTE &&
+	if (icp_hw_mgr.icp_debug_clk < ICP_CLK_TURBO_HZ &&
 		icp_hw_mgr.icp_debug_clk &&
 		icp_hw_mgr.icp_debug_clk != hw_mgr_clk_info->curr_clk) {
 		mutex_lock(&icp_hw_mgr.hw_mgr_mutex);
@@ -581,8 +610,9 @@
 	rc = cam_icp_update_bw(hw_mgr, ctx_data, hw_mgr_clk_info,
 		clk_info, busy);
 
-	CAM_DBG(CAM_ICP, "bw = %d update_bw = %d",
-		hw_mgr_clk_info->uncompressed_bw, rc);
+	CAM_DBG(CAM_ICP, "ubw = %lld, cbw = %lld, update_bw = %d",
+		hw_mgr_clk_info->uncompressed_bw,
+		hw_mgr_clk_info->compressed_bw, rc);
 
 	return rc;
 }
@@ -783,6 +813,21 @@
 	cam_icp_get_dbg_default_clk,
 	cam_icp_set_dbg_default_clk, "%16llu");
 
+static int cam_icp_set_a5_dbg_lvl(void *data, u64 val)
+{
+	icp_hw_mgr.a5_dbg_lvl = val;
+	return 0;
+}
+
+static int cam_icp_get_a5_dbg_lvl(void *data, u64 *val)
+{
+	*val = icp_hw_mgr.a5_dbg_lvl;
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_icp_debug_fs, cam_icp_get_a5_dbg_lvl,
+	cam_icp_set_a5_dbg_lvl, "%08llu");
+
 static int cam_icp_hw_mgr_create_debugfs_entry(void)
 {
 	int rc = 0;
@@ -791,15 +836,6 @@
 	if (!icp_hw_mgr.dentry)
 		return -ENOMEM;
 
-	if (!debugfs_create_bool("a5_debug",
-		0644,
-		icp_hw_mgr.dentry,
-		&icp_hw_mgr.a5_debug)) {
-		debugfs_remove_recursive(icp_hw_mgr.dentry);
-		rc = -ENOMEM;
-		goto err;
-	}
-
 	if (!debugfs_create_bool("icp_pc",
 		0644,
 		icp_hw_mgr.dentry,
@@ -818,6 +854,32 @@
 		goto err;
 	}
 
+	if (!debugfs_create_bool("a5_jtag_debug",
+		0644,
+		icp_hw_mgr.dentry,
+		&icp_hw_mgr.a5_jtag_debug)) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	if (!debugfs_create_bool("a5_debug_q",
+		0644,
+		icp_hw_mgr.dentry,
+		&icp_hw_mgr.a5_debug_q)) {
+		CAM_ERR(CAM_ICP, "failed to create a5_debug_q\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	if (!debugfs_create_file("a5_debug_lvl",
+		0644,
+		icp_hw_mgr.dentry,
+		NULL, &cam_icp_debug_fs)) {
+		CAM_ERR(CAM_ICP, "failed to create a5_dbg_lvl\n");
+		rc = -ENOMEM;
+		goto err;
+	}
+
 	return rc;
 err:
 	debugfs_remove_recursive(icp_hw_mgr.dentry);
@@ -880,6 +942,12 @@
 	buf_data.request_id = hfi_frame_process->request_id[idx];
 	ctx_data->ctxt_event_cb(ctx_data->context_priv, flag, &buf_data);
 	hfi_frame_process->request_id[idx] = 0;
+	if (ctx_data->hfi_frame_process.in_resource[idx] > 0) {
+		CAM_DBG(CAM_ICP, "Delete merged sync in object: %d",
+			ctx_data->hfi_frame_process.in_resource[idx]);
+		cam_sync_destroy(ctx_data->hfi_frame_process.in_resource[idx]);
+		ctx_data->hfi_frame_process.in_resource[idx] = 0;
+	}
 	clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
 	hfi_frame_process->fw_process_flag[idx] = false;
 	mutex_unlock(&ctx_data->ctx_mutex);
@@ -1078,13 +1146,103 @@
 	return rc;
 }
 
-static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
+static void cam_icp_mgr_process_dbg_buf(void)
+{
+	uint32_t *msg_ptr = NULL, *pkt_ptr = NULL;
+	struct hfi_msg_debug *dbg_msg;
+	uint32_t read_len, size_processed = 0;
+	char *dbg_buf;
+	int rc = 0;
+
+	rc = hfi_read_message(icp_hw_mgr.dbg_buf, Q_DBG, &read_len);
+	if (rc)
+		return;
+
+	msg_ptr = (uint32_t *)icp_hw_mgr.dbg_buf;
+	while (true) {
+		pkt_ptr = msg_ptr;
+		if (pkt_ptr[ICP_PACKET_TYPE] == HFI_MSG_SYS_DEBUG) {
+			dbg_msg = (struct hfi_msg_debug *)pkt_ptr;
+			dbg_buf = (char *)&dbg_msg->msg_data;
+			trace_cam_icp_fw_dbg(dbg_buf);
+		}
+		size_processed += (pkt_ptr[ICP_PACKET_SIZE] >>
+			BYTE_WORD_SHIFT);
+		if (size_processed >= read_len)
+			return;
+		msg_ptr += (pkt_ptr[ICP_PACKET_SIZE] >>
+		BYTE_WORD_SHIFT);
+		pkt_ptr = NULL;
+		dbg_msg = NULL;
+		dbg_buf = NULL;
+	}
+}
+
+static int cam_icp_process_msg_pkt_type(
+	struct cam_icp_hw_mgr *hw_mgr,
+	uint32_t *msg_ptr,
+	uint32_t *msg_processed_len)
 {
 	int rc = 0;
+	int size_processed = 0;
+	struct hfi_msg_ipebps_async_ack *async_ack = NULL;
+
+	switch (msg_ptr[ICP_PACKET_TYPE]) {
+	case HFI_MSG_SYS_INIT_DONE:
+		CAM_DBG(CAM_ICP, "received SYS_INIT_DONE");
+		complete(&hw_mgr->a5_complete);
+		size_processed = sizeof(struct hfi_msg_init_done);
+		break;
+
+	case HFI_MSG_SYS_PING_ACK:
+		CAM_DBG(CAM_ICP, "received SYS_PING_ACK");
+		rc = cam_icp_mgr_process_msg_ping_ack(msg_ptr);
+		size_processed = sizeof(struct hfi_msg_ping_ack);
+		break;
+
+	case HFI_MSG_IPEBPS_CREATE_HANDLE_ACK:
+		CAM_DBG(CAM_ICP, "received IPEBPS_CREATE_HANDLE_ACK");
+		rc = cam_icp_mgr_process_msg_create_handle(msg_ptr);
+		size_processed = sizeof(struct hfi_msg_create_handle_ack);
+		break;
+
+	case HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK:
+		CAM_DBG(CAM_ICP, "received ASYNC_INDIRECT_ACK");
+		rc = cam_icp_mgr_process_indirect_ack_msg(msg_ptr);
+		async_ack = (struct hfi_msg_ipebps_async_ack *)msg_ptr;
+		size_processed = async_ack->size;
+		async_ack = NULL;
+		break;
+
+	case  HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK:
+		CAM_DBG(CAM_ICP, "received ASYNC_DIRECT_ACK");
+		rc = cam_icp_mgr_process_direct_ack_msg(msg_ptr);
+		size_processed = sizeof(struct hfi_msg_ipebps_async_ack);
+		break;
+
+	case HFI_MSG_EVENT_NOTIFY:
+		CAM_DBG(CAM_ICP, "received EVENT_NOTIFY");
+		size_processed = sizeof(struct hfi_msg_event_notify);
+		break;
+
+	default:
+		CAM_ERR(CAM_ICP, "invalid msg : %u",
+			msg_ptr[ICP_PACKET_TYPE]);
+		rc = -EINVAL;
+		break;
+	}
+
+	*msg_processed_len = size_processed;
+	return rc;
+}
+
+static int32_t cam_icp_mgr_process_msg(void *priv, void *data)
+{
+	uint32_t read_len, msg_processed_len;
 	uint32_t *msg_ptr = NULL;
 	struct hfi_msg_work_data *task_data;
 	struct cam_icp_hw_mgr *hw_mgr;
-	int read_len;
+	int rc = 0;
 
 	if (!data || !priv) {
 		CAM_ERR(CAM_ICP, "Invalid data");
@@ -1094,48 +1252,31 @@
 	task_data = data;
 	hw_mgr = priv;
 
-	read_len = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG);
-	if (read_len < 0) {
+	rc = hfi_read_message(icp_hw_mgr.msg_buf, Q_MSG, &read_len);
+	if (rc) {
 		CAM_DBG(CAM_ICP, "Unable to read msg q");
-		return read_len;
+	} else {
+		read_len = read_len << BYTE_WORD_SHIFT;
+		msg_ptr = (uint32_t *)icp_hw_mgr.msg_buf;
+		while (true) {
+			rc = cam_icp_process_msg_pkt_type(hw_mgr, msg_ptr,
+				&msg_processed_len);
+			if (rc)
+				return rc;
+
+			read_len -= msg_processed_len;
+			if (read_len > 0) {
+				msg_ptr += (msg_processed_len >>
+				BYTE_WORD_SHIFT);
+				msg_processed_len = 0;
+			}
+			else
+				break;
+		}
 	}
 
-	msg_ptr = (uint32_t *)icp_hw_mgr.msg_buf;
-
-	switch (msg_ptr[ICP_PACKET_TYPE]) {
-	case HFI_MSG_SYS_INIT_DONE:
-		CAM_DBG(CAM_ICP, "received SYS_INIT_DONE");
-		complete(&hw_mgr->a5_complete);
-		break;
-
-	case HFI_MSG_SYS_PING_ACK:
-		CAM_DBG(CAM_ICP, "received SYS_PING_ACK");
-		rc = cam_icp_mgr_process_msg_ping_ack(msg_ptr);
-		break;
-
-	case HFI_MSG_IPEBPS_CREATE_HANDLE_ACK:
-		CAM_DBG(CAM_ICP, "received IPEBPS_CREATE_HANDLE_ACK");
-		rc = cam_icp_mgr_process_msg_create_handle(msg_ptr);
-		break;
-
-	case HFI_MSG_IPEBPS_ASYNC_COMMAND_INDIRECT_ACK:
-		rc = cam_icp_mgr_process_indirect_ack_msg(msg_ptr);
-		break;
-
-	case  HFI_MSG_IPEBPS_ASYNC_COMMAND_DIRECT_ACK:
-		rc = cam_icp_mgr_process_direct_ack_msg(msg_ptr);
-		break;
-
-	case HFI_MSG_EVENT_NOTIFY:
-		CAM_DBG(CAM_ICP, "received EVENT_NOTIFY");
-		break;
-
-	default:
-		CAM_ERR(CAM_ICP, "invalid msg : %u",
-			msg_ptr[ICP_PACKET_TYPE]);
-		rc = -EINVAL;
-		break;
-	}
+	if (icp_hw_mgr.a5_debug_q)
+		cam_icp_mgr_process_dbg_buf();
 
 	return rc;
 }
@@ -1170,12 +1311,52 @@
 
 static void cam_icp_free_hfi_mem(void)
 {
+	int rc;
 	cam_smmu_dealloc_firmware(icp_hw_mgr.iommu_hdl);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.qtbl);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.cmd_q);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.msg_q);
 	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.dbg_q);
-	cam_mem_mgr_release_mem(&icp_hw_mgr.hfi_mem.sec_heap);
+	rc = cam_mem_mgr_free_memory_region(&icp_hw_mgr.hfi_mem.sec_heap);
+	if (rc)
+		CAM_ERR(CAM_ICP, "failed to unreserve sec heap");
+}
+
+static int cam_icp_alloc_secheap_mem(struct cam_mem_mgr_memory_desc *secheap)
+{
+	int rc;
+	struct cam_mem_mgr_request_desc alloc;
+	struct cam_mem_mgr_memory_desc out;
+	struct cam_smmu_region_info secheap_info;
+
+	memset(&alloc, 0, sizeof(alloc));
+	memset(&out, 0, sizeof(out));
+
+	rc = cam_smmu_get_region_info(icp_hw_mgr.iommu_hdl,
+		CAM_SMMU_REGION_SECHEAP,
+		&secheap_info);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "Unable to get secheap memory info");
+		return rc;
+	}
+
+	alloc.size = secheap_info.iova_len;
+	alloc.align = 0;
+	alloc.flags = 0;
+	alloc.smmu_hdl = icp_hw_mgr.iommu_hdl;
+	rc = cam_mem_mgr_reserve_memory_region(&alloc,
+		CAM_SMMU_REGION_SECHEAP,
+		&out);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "Unable to reserve secheap memory");
+		return rc;
+	}
+
+	*secheap = out;
+	CAM_DBG(CAM_ICP, "kva: %llX, iova: %x, hdl: %x, len: %lld",
+		out.kva, out.iova, out.mem_handle, out.len);
+
+	return rc;
 }
 
 static int cam_icp_alloc_shared_mem(struct cam_mem_mgr_memory_desc *qtbl)
@@ -1267,9 +1448,9 @@
 		goto dbg_q_alloc_failed;
 	}
 
-	rc = cam_icp_alloc_shared_mem(&icp_hw_mgr.hfi_mem.sec_heap);
+	rc = cam_icp_alloc_secheap_mem(&icp_hw_mgr.hfi_mem.sec_heap);
 	if (rc) {
-		CAM_ERR(CAM_ICP, "Unable to allocate sec heap q memory");
+		CAM_ERR(CAM_ICP, "Unable to allocate sec heap memory");
 		goto sec_heap_alloc_failed;
 	}
 
@@ -1463,6 +1644,7 @@
 	for (i = 0; i < CAM_FRAME_CMD_MAX; i++)
 		clear_bit(i, hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
 	kfree(hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap);
+	hw_mgr->ctx_data[ctx_id].hfi_frame_process.bitmap = NULL;
 	cam_icp_hw_mgr_clk_info_update(hw_mgr, &hw_mgr->ctx_data[ctx_id]);
 	hw_mgr->ctx_data[ctx_id].clk_info.curr_fc = 0;
 	hw_mgr->ctx_data[ctx_id].clk_info.base_clk = 0;
@@ -1521,15 +1703,6 @@
 		return -EINVAL;
 	}
 
-	irq_cb.icp_hw_mgr_cb = NULL;
-	irq_cb.data = NULL;
-	rc = a5_dev_intf->hw_ops.process_cmd(
-		a5_dev_intf->hw_priv,
-		CAM_ICP_A5_SET_IRQ_CB,
-		&irq_cb, sizeof(irq_cb));
-	if (rc)
-		CAM_ERR(CAM_ICP, "deregister irq call back failed");
-
 	fw_buf_info.kva = 0;
 	fw_buf_info.iova = 0;
 	fw_buf_info.len = 0;
@@ -1548,8 +1721,19 @@
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
 	cam_hfi_deinit();
 	cam_icp_mgr_device_deinit(hw_mgr);
+
+	irq_cb.icp_hw_mgr_cb = NULL;
+	irq_cb.data = NULL;
+	rc = a5_dev_intf->hw_ops.process_cmd(
+		a5_dev_intf->hw_priv,
+		CAM_ICP_A5_SET_IRQ_CB,
+		&irq_cb, sizeof(irq_cb));
+	if (rc)
+		CAM_ERR(CAM_ICP, "deregister irq call back failed");
+
 	cam_icp_free_hfi_mem();
 	hw_mgr->fw_download = false;
+	hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	return rc;
@@ -1692,7 +1876,7 @@
 
 	return cam_hfi_init(0, &hfi_mem,
 		a5_dev->soc_info.reg_map[A5_SIERRA_BASE].mem_base,
-		hw_mgr->a5_debug);
+		hw_mgr->a5_jtag_debug);
 }
 
 static int cam_icp_mgr_send_fw_init(struct cam_icp_hw_mgr *hw_mgr)
@@ -1728,7 +1912,7 @@
 	return rc;
 }
 
-static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args)
+static int cam_icp_mgr_hw_open(void *hw_mgr_priv, void *download_fw_args)
 {
 	struct cam_hw_intf *a5_dev_intf = NULL;
 	struct cam_hw_info *a5_dev = NULL;
@@ -1790,10 +1974,15 @@
 		NULL, 0);
 	hw_mgr->fw_download = true;
 	hw_mgr->ctxt_cnt = 0;
-	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 	CAM_DBG(CAM_ICP, "FW download done successfully");
+
+	if (icp_hw_mgr.a5_debug_q)
+		hfi_set_debug_level(icp_hw_mgr.a5_dbg_lvl);
+
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 	if (!download_fw_args)
 		cam_icp_mgr_hw_close(hw_mgr, NULL);
+
 	return rc;
 
 fw_init_failed:
@@ -1880,6 +2069,7 @@
 	ctx_data = config_args->ctxt_to_hw_map;
 	mutex_lock(&ctx_data->ctx_mutex);
 	if (!ctx_data->in_use) {
+		mutex_unlock(&ctx_data->ctx_mutex);
 		CAM_ERR(CAM_ICP, "ctx is not in use");
 		return -EINVAL;
 	}
@@ -1973,13 +2163,16 @@
 	return rc;
 }
 
-static void cam_icp_mgr_process_io_cfg(struct cam_icp_hw_mgr *hw_mgr,
+static int cam_icp_mgr_process_io_cfg(struct cam_icp_hw_mgr *hw_mgr,
 	struct cam_icp_hw_ctx_data *ctx_data,
 	struct cam_packet *packet,
-	struct cam_hw_prepare_update_args *prepare_args)
+	struct cam_hw_prepare_update_args *prepare_args,
+	int32_t index)
 {
-	int i, j, k;
+	int i, j, k, rc = 0;
 	struct cam_buf_io_cfg *io_cfg_ptr = NULL;
+	int32_t sync_in_obj[CAM_MAX_IN_RES];
+	int32_t merged_sync_in_obj;
 
 	io_cfg_ptr = (struct cam_buf_io_cfg *) ((uint32_t *) &packet->payload +
 				packet->io_configs_offset/4);
@@ -1988,8 +2181,7 @@
 
 	for (i = 0, j = 0, k = 0; i < packet->num_io_configs; i++) {
 		if (io_cfg_ptr[i].direction == CAM_BUF_INPUT) {
-			prepare_args->in_map_entries[j++].sync_id =
-				io_cfg_ptr[i].fence;
+			sync_in_obj[j++] = io_cfg_ptr[i].fence;
 			prepare_args->num_in_map_entries++;
 		} else {
 			prepare_args->out_map_entries[k++].sync_id =
@@ -1999,6 +2191,33 @@
 		CAM_DBG(CAM_ICP, "dir[%d]: %u, fence: %u",
 			i, io_cfg_ptr[i].direction, io_cfg_ptr[i].fence);
 	}
+
+	if (prepare_args->num_in_map_entries > 1) {
+		rc = cam_sync_merge(&sync_in_obj[0],
+			prepare_args->num_in_map_entries, &merged_sync_in_obj);
+		if (rc) {
+			prepare_args->num_out_map_entries = 0;
+			prepare_args->num_in_map_entries = 0;
+			return rc;
+		}
+
+		ctx_data->hfi_frame_process.in_resource[index] =
+			merged_sync_in_obj;
+		prepare_args->in_map_entries[0].sync_id = merged_sync_in_obj;
+		prepare_args->num_in_map_entries = 1;
+		CAM_DBG(CAM_ICP, "Merged Sync obj = %d", merged_sync_in_obj);
+	} else if (prepare_args->num_in_map_entries == 1) {
+		prepare_args->in_map_entries[0].sync_id = sync_in_obj[0];
+		prepare_args->num_in_map_entries = 1;
+		ctx_data->hfi_frame_process.in_resource[index] = 0;
+	} else {
+		CAM_ERR(CAM_ICP, "No input fences");
+		prepare_args->num_in_map_entries = 0;
+		ctx_data->hfi_frame_process.in_resource[index] = 0;
+		rc = -EINVAL;
+	}
+
+	return rc;
 }
 
 static int cam_icp_packet_generic_blob_handler(void *user_data,
@@ -2146,21 +2365,28 @@
 	}
 
 	/* Update Buffer Address from handles and patch information */
-	rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl);
+	rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl,
+		hw_mgr->iommu_sec_hdl);
 	if (rc) {
 		mutex_unlock(&ctx_data->ctx_mutex);
 		return rc;
 	}
 
-	cam_icp_mgr_process_io_cfg(hw_mgr, ctx_data,
-		packet, prepare_args);
-
 	rc = cam_icp_mgr_update_hfi_frame_process(ctx_data, packet,
 		prepare_args, &idx);
 	if (rc) {
-		if (prepare_args->in_map_entries[0].sync_id > 0)
+		mutex_unlock(&ctx_data->ctx_mutex);
+		return rc;
+	}
+
+	rc = cam_icp_mgr_process_io_cfg(hw_mgr, ctx_data,
+		packet, prepare_args, idx);
+	if (rc) {
+		if (ctx_data->hfi_frame_process.in_resource[idx] > 0)
 			cam_sync_destroy(
-				prepare_args->in_map_entries[0].sync_id);
+				ctx_data->hfi_frame_process.in_resource[idx]);
+		clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
+		ctx_data->hfi_frame_process.request_id[idx] = -1;
 		mutex_unlock(&ctx_data->ctx_mutex);
 		return rc;
 	}
@@ -2195,6 +2421,13 @@
 
 		/* now release memory for hfi frame process command */
 		hfi_frame_process->request_id[idx] = 0;
+		if (ctx_data->hfi_frame_process.in_resource[idx] > 0) {
+			CAM_DBG(CAM_ICP, "Delete merged sync in object: %d",
+				ctx_data->hfi_frame_process.in_resource[idx]);
+			cam_sync_destroy(
+				ctx_data->hfi_frame_process.in_resource[idx]);
+			ctx_data->hfi_frame_process.in_resource[idx] = 0;
+	}
 		clear_bit(idx, ctx_data->hfi_frame_process.bitmap);
 	}
 	mutex_unlock(&ctx_data->ctx_mutex);
@@ -2216,9 +2449,16 @@
 	}
 
 	ctx_data = release_hw->ctxt_to_hw_map;
+	if (!ctx_data) {
+		CAM_ERR(CAM_ICP, "NULL ctx");
+		return -EINVAL;
+	}
+
 	ctx_id = ctx_data->ctx_id;
-	if (ctx_id < 0 || ctx_id >= CAM_ICP_CTX_MAX)
+	if (ctx_id < 0 || ctx_id >= CAM_ICP_CTX_MAX) {
 		CAM_ERR(CAM_ICP, "Invalid ctx id: %d", ctx_id);
+		return -EINVAL;
+	}
 
 	mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
 	if (!hw_mgr->ctx_data[ctx_id].in_use) {
@@ -2239,6 +2479,7 @@
 			NULL, 0);
 		cam_icp_mgr_hw_close(hw_mgr, NULL);
 		cam_icp_hw_mgr_reset_clk_info(hw_mgr);
+		hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
 	}
 
 	return rc;
@@ -2384,6 +2625,12 @@
 		sizeof(struct cam_icp_acquire_dev_info)))
 		return -EFAULT;
 
+	if (icp_dev_acquire_info.secure_mode > CAM_SECURE_MODE_SECURE) {
+		CAM_ERR(CAM_ICP, "Invalid mode:%d",
+			icp_dev_acquire_info.secure_mode);
+		return -EINVAL;
+	}
+
 	if (icp_dev_acquire_info.num_out_res > ICP_MAX_OUTPUT_SUPPORTED) {
 		CAM_ERR(CAM_ICP, "num of out resources exceeding : %u",
 			icp_dev_acquire_info.num_out_res);
@@ -2396,28 +2643,46 @@
 		return -EFAULT;
 	}
 
+	if (!hw_mgr->ctxt_cnt) {
+		hw_mgr->secure_mode = icp_dev_acquire_info.secure_mode;
+	} else {
+		if (hw_mgr->secure_mode != icp_dev_acquire_info.secure_mode) {
+			CAM_ERR(CAM_ICP,
+				"secure mode mismatch driver:%d, context:%d",
+				hw_mgr->secure_mode,
+				icp_dev_acquire_info.secure_mode);
+			return -EINVAL;
+		}
+	}
+
 	acquire_size = sizeof(struct cam_icp_acquire_dev_info) +
 		(icp_dev_acquire_info.num_out_res *
 		sizeof(struct cam_icp_res_info));
 	ctx_data->icp_dev_acquire_info = kzalloc(acquire_size, GFP_KERNEL);
-	if (!ctx_data->icp_dev_acquire_info)
+	if (!ctx_data->icp_dev_acquire_info) {
+		if (!hw_mgr->ctxt_cnt)
+			hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
 		return -ENOMEM;
+	}
 
 	if (copy_from_user(ctx_data->icp_dev_acquire_info,
 		(void __user *)args->acquire_info, acquire_size)) {
+		if (!hw_mgr->ctxt_cnt)
+			hw_mgr->secure_mode = CAM_SECURE_MODE_NON_SECURE;
 		kfree(ctx_data->icp_dev_acquire_info);
 		ctx_data->icp_dev_acquire_info = NULL;
 		return -EFAULT;
 	}
 
-	CAM_DBG(CAM_ICP, "%x %x %x %x %x %x %x",
+	CAM_DBG(CAM_ICP, "%x %x %x %x %x %x %x %u",
 		ctx_data->icp_dev_acquire_info->dev_type,
 		ctx_data->icp_dev_acquire_info->in_res.format,
 		ctx_data->icp_dev_acquire_info->in_res.width,
 		ctx_data->icp_dev_acquire_info->in_res.height,
 		ctx_data->icp_dev_acquire_info->in_res.fps,
 		ctx_data->icp_dev_acquire_info->num_out_res,
-		ctx_data->icp_dev_acquire_info->scratch_mem_size);
+		ctx_data->icp_dev_acquire_info->scratch_mem_size,
+		hw_mgr->secure_mode);
 
 	p_icp_out = ctx_data->icp_dev_acquire_info->out_res;
 	for (i = 0; i < icp_dev_acquire_info.num_out_res; i++)
@@ -2470,18 +2735,10 @@
 		goto acquire_info_failed;
 	icp_dev_acquire_info = ctx_data->icp_dev_acquire_info;
 
-	/* Get IOCONFIG command info */
-	if (icp_dev_acquire_info->secure_mode)
-		rc = cam_mem_get_io_buf(
-			icp_dev_acquire_info->io_config_cmd_handle,
-			hw_mgr->iommu_sec_hdl,
-			&io_buf_addr, &io_buf_size);
-	else
-		rc = cam_mem_get_io_buf(
-			icp_dev_acquire_info->io_config_cmd_handle,
-			hw_mgr->iommu_hdl,
-			&io_buf_addr, &io_buf_size);
-
+	rc = cam_mem_get_io_buf(
+		icp_dev_acquire_info->io_config_cmd_handle,
+		hw_mgr->iommu_hdl,
+		&io_buf_addr, &io_buf_size);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "unable to get src buf info from io desc");
 		goto get_io_buf_failed;
@@ -2497,12 +2754,16 @@
 		rc = cam_icp_clk_info_init(hw_mgr, ctx_data);
 		if (rc)
 			goto get_io_buf_failed;
-		rc = cam_icp_mgr_download_fw(hw_mgr, ctx_data);
+		rc = cam_icp_mgr_hw_open(hw_mgr, ctx_data);
 		if (rc)
 			goto get_io_buf_failed;
 		rc = cam_icp_mgr_ipe_bps_resume(hw_mgr, ctx_data);
 		if (rc)
 			goto ipe_bps_resume_failed;
+
+		rc = cam_icp_send_ubwc_cfg(hw_mgr);
+		if (rc)
+			goto ubwc_cfg_failed;
 		mutex_lock(&hw_mgr->hw_mgr_mutex);
 	}
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
@@ -2560,6 +2821,7 @@
 	cam_icp_mgr_destroy_handle(ctx_data);
 create_handle_failed:
 send_ping_failed:
+ubwc_cfg_failed:
 	cam_icp_mgr_ipe_bps_power_collapse(hw_mgr, ctx_data, 0);
 ipe_bps_resume_failed:
 	if (!hw_mgr->ctxt_cnt)
@@ -2635,6 +2897,9 @@
 		goto num_ipe_failed;
 	}
 
+	if (!icp_hw_mgr.ipe1_enable)
+		num_dev = 1;
+
 	icp_hw_mgr.devices[CAM_ICP_DEV_IPE] = kzalloc(
 		sizeof(struct cam_hw_intf *) * num_dev, GFP_KERNEL);
 	if (!icp_hw_mgr.devices[CAM_ICP_DEV_IPE]) {
@@ -2713,9 +2978,10 @@
 		if (!child_dev_intf) {
 			CAM_ERR(CAM_ICP, "no child device");
 			of_node_put(child_node);
+			if (!icp_hw_mgr.ipe1_enable)
+				continue;
 			goto compat_hw_name_failed;
 		}
-
 		icp_hw_mgr.devices[child_dev_intf->hw_type]
 			[child_dev_intf->hw_idx] = child_dev_intf;
 
@@ -2791,6 +3057,8 @@
 {
 	int i, rc = 0;
 	struct cam_hw_mgr_intf *hw_mgr_intf;
+	struct cam_cpas_query_cap query;
+	uint32_t cam_caps;
 
 	hw_mgr_intf = (struct cam_hw_mgr_intf *)hw_mgr_hdl;
 	if (!of_node || !hw_mgr_intf) {
@@ -2805,22 +3073,32 @@
 	hw_mgr_intf->hw_release = cam_icp_mgr_release_hw;
 	hw_mgr_intf->hw_prepare_update = cam_icp_mgr_prepare_hw_update;
 	hw_mgr_intf->hw_config = cam_icp_mgr_config_hw;
-	hw_mgr_intf->download_fw = cam_icp_mgr_download_fw;
+	hw_mgr_intf->hw_open = cam_icp_mgr_hw_open;
 	hw_mgr_intf->hw_close = cam_icp_mgr_hw_close;
 
+	icp_hw_mgr.secure_mode = CAM_SECURE_MODE_NON_SECURE;
 	mutex_init(&icp_hw_mgr.hw_mgr_mutex);
 	spin_lock_init(&icp_hw_mgr.hw_mgr_lock);
 
 	for (i = 0; i < CAM_ICP_CTX_MAX; i++)
 		mutex_init(&icp_hw_mgr.ctx_data[i].ctx_mutex);
 
+	cam_cpas_get_hw_info(&query.camera_family,
+		&query.camera_version, &query.cpas_version, &cam_caps);
+	if (cam_caps & CPAS_IPE0_BIT)
+		icp_hw_mgr.ipe0_enable = true;
+	if (cam_caps & CPAS_IPE1_BIT)
+		icp_hw_mgr.ipe1_enable = true;
+	if (cam_caps & CPAS_BPS_BIT)
+		icp_hw_mgr.bps_enable = true;
+
 	rc = cam_icp_mgr_init_devs(of_node);
 	if (rc)
 		goto dev_init_failed;
 
 	rc = cam_smmu_get_handle("icp", &icp_hw_mgr.iommu_hdl);
 	if (rc) {
-		CAM_ERR(CAM_ICP, "icp get iommu handle failed: %d", rc);
+		CAM_ERR(CAM_ICP, "get mmu handle failed: %d", rc);
 		goto icp_get_hdl_failed;
 	}
 
@@ -2830,6 +3108,12 @@
 		goto icp_attach_failed;
 	}
 
+	rc = cam_smmu_get_handle("cam-secure", &icp_hw_mgr.iommu_sec_hdl);
+	if (rc) {
+		CAM_ERR(CAM_ICP, "get secure mmu handle failed: %d", rc);
+		goto secure_hdl_failed;
+	}
+
 	rc = cam_icp_mgr_create_wq();
 	if (rc)
 		goto icp_wq_create_failed;
@@ -2839,6 +3123,9 @@
 	return rc;
 
 icp_wq_create_failed:
+	cam_smmu_destroy_handle(icp_hw_mgr.iommu_sec_hdl);
+	icp_hw_mgr.iommu_sec_hdl = -1;
+secure_hdl_failed:
 	cam_smmu_ops(icp_hw_mgr.iommu_hdl, CAM_SMMU_DETACH);
 icp_attach_failed:
 	cam_smmu_destroy_handle(icp_hw_mgr.iommu_hdl);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
index d4f5482..321f10c 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/cam_icp_hw_mgr.h
@@ -32,8 +32,9 @@
 #define CAM_FRAME_CMD_MAX       20
 
 #define CAM_MAX_OUT_RES         6
+#define CAM_MAX_IN_RES          8
 
-#define ICP_WORKQ_NUM_TASK      30
+#define ICP_WORKQ_NUM_TASK      100
 #define ICP_WORKQ_TASK_CMD_TYPE 1
 #define ICP_WORKQ_TASK_MSG_TYPE 2
 
@@ -44,6 +45,8 @@
 
 #define ICP_FRAME_PROCESS_SUCCESS 0
 #define ICP_FRAME_PROCESS_FAILURE 1
+#define ICP_MSG_BUF_SIZE        256
+#define ICP_DBG_BUF_SIZE        102400
 
 #define ICP_CLK_HW_IPE          0x0
 #define ICP_CLK_HW_BPS          0x1
@@ -51,6 +54,10 @@
 
 #define ICP_OVER_CLK_THRESHOLD  15
 
+#define CPAS_IPE0_BIT           0x1000
+#define CPAS_IPE1_BIT           0x2000
+#define CPAS_BPS_BIT            0x400
+
 /**
  * struct icp_hfi_mem_info
  * @qtbl: Memory info of queue table
@@ -114,6 +121,7 @@
 	uint64_t request_id[CAM_FRAME_CMD_MAX];
 	uint32_t num_out_resources[CAM_FRAME_CMD_MAX];
 	uint32_t out_resource[CAM_FRAME_CMD_MAX][CAM_MAX_OUT_RES];
+	uint32_t in_resource[CAM_FRAME_CMD_MAX];
 	uint32_t fw_process_flag[CAM_FRAME_CMD_MAX];
 	struct cam_icp_clk_bw_request clk_info[CAM_FRAME_CMD_MAX];
 };
@@ -123,6 +131,7 @@
  * @curr_fc: Context latest request frame cycles
  * @rt_flag: Flag to indicate real time request
  * @base_clk: Base clock to process the request
+ * @reserved: Reserved field
  * #uncompressed_bw: Current bandwidth voting
  * @compressed_bw: Current compressed bandwidth voting
  * @clk_rate: Supported clock rates for the context
@@ -131,8 +140,9 @@
 	uint32_t curr_fc;
 	uint32_t rt_flag;
 	uint32_t base_clk;
-	uint32_t uncompressed_bw;
-	uint32_t compressed_bw;
+	uint32_t reserved;
+	uint64_t uncompressed_bw;
+	uint64_t compressed_bw;
 	int32_t clk_rate[CAM_MAX_VOTE];
 };
 /**
@@ -195,8 +205,8 @@
 	uint32_t curr_clk;
 	uint32_t threshold;
 	uint32_t over_clked;
-	uint32_t uncompressed_bw;
-	uint32_t compressed_bw;
+	uint64_t uncompressed_bw;
+	uint64_t compressed_bw;
 };
 
 /**
@@ -226,6 +236,13 @@
  * @icp_debug_clk: Set clock based on debug value
  * @icp_default_clk: Set this clok if user doesn't supply
  * @clk_info: Clock info of hardware
+ * @secure_mode: Flag to enable/disable secure camera
+ * @a5_jtag_debug: entry to enable A5 JTAG debugging
+ * @a5_debug_q : entry to enable FW debug message
+ * @a5_dbg_lvl : debug level set to FW.
+ * @ipe0_enable: Flag for IPE0
+ * @ipe1_enable: Flag for IPE1
+ * @bps_enable: Flag for BPS
  */
 struct cam_icp_hw_mgr {
 	struct mutex hw_mgr_mutex;
@@ -241,8 +258,8 @@
 	struct icp_hfi_mem_info hfi_mem;
 	struct cam_req_mgr_core_workq *cmd_work;
 	struct cam_req_mgr_core_workq *msg_work;
-	uint32_t msg_buf[256];
-	uint32_t dbg_buf[256];
+	uint32_t msg_buf[ICP_MSG_BUF_SIZE];
+	uint32_t dbg_buf[ICP_DBG_BUF_SIZE];
 	struct completion a5_complete;
 	struct hfi_cmd_work_data *cmd_work_data;
 	struct hfi_msg_work_data *msg_work_data;
@@ -255,8 +272,16 @@
 	uint64_t icp_debug_clk;
 	uint64_t icp_default_clk;
 	struct cam_icp_clk_info clk_info[ICP_CLK_HW_MAX];
+	bool secure_mode;
+	bool a5_jtag_debug;
+	bool a5_debug_q;
+	u64 a5_dbg_lvl;
+	bool ipe0_enable;
+	bool ipe1_enable;
+	bool bps_enable;
 };
 
 static int cam_icp_mgr_hw_close(void *hw_priv, void *hw_close_args);
-static int cam_icp_mgr_download_fw(void *hw_mgr_priv, void *download_fw_args);
+static int cam_icp_mgr_hw_open(void *hw_mgr_priv, void *download_fw_args);
+
 #endif /* CAM_ICP_HW_MGR_H */
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h
index 2686877..dad7736 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_a5_hw_intf.h
@@ -31,6 +31,7 @@
 	CAM_ICP_A5_CMD_VOTE_CPAS,
 	CAM_ICP_A5_CMD_CPAS_START,
 	CAM_ICP_A5_CMD_CPAS_STOP,
+	CAM_ICP_A5_CMD_UBWC_CFG,
 	CAM_ICP_A5_CMD_MAX,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h
index 9300ea8..6915ad5 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/icp_hw_mgr/include/cam_icp_hw_intf.h
@@ -13,8 +13,6 @@
 #ifndef CAM_ICP_HW_INTF_H
 #define CAM_ICP_HW_INTF_H
 
-#define CAM_ICP_CTX_MAX                 8
-
 #define CAM_ICP_CMD_BUF_MAX_SIZE     128
 #define CAM_ICP_MSG_BUF_MAX_SIZE     CAM_ICP_CMD_BUF_MAX_SIZE
 
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
index d99a878..d2e04ef 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/include/cam_icp_hw_mgr_intf.h
@@ -18,8 +18,14 @@
 #include <linux/of.h>
 #include "cam_cpas_api.h"
 
-#define ICP_TURBO_VOTE           600000000
-#define ICP_SVS_VOTE             400000000
+#define ICP_CLK_TURBO_HZ         600000000
+#define ICP_CLK_SVS_HZ           400000000
+
+#define CAM_ICP_A5_BW_BYTES_VOTE 100000000
+
+#define CAM_ICP_CTX_MAX          36
+
+#define CPAS_IPE1_BIT            0x2000
 
 int cam_icp_hw_mgr_init(struct device_node *of_node,
 	uint64_t *hw_mgr_hdl);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_dev.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_dev.c
index cbd9d84..cc2b1b1 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_dev.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_dev.c
@@ -72,14 +72,25 @@
 	struct cam_ipe_device_core_info   *core_info = NULL;
 	struct cam_ipe_device_hw_info     *hw_info = NULL;
 	int                                rc = 0;
+	struct cam_cpas_query_cap query;
+	uint32_t cam_caps;
+	uint32_t hw_idx;
+
+	of_property_read_u32(pdev->dev.of_node,
+		"cell-index", &hw_idx);
+
+	cam_cpas_get_hw_info(&query.camera_family,
+		&query.camera_version, &query.cpas_version, &cam_caps);
+	if ((!(cam_caps & CPAS_IPE1_BIT)) && (hw_idx)) {
+		CAM_ERR(CAM_ICP, "IPE1 hw idx = %d\n", hw_idx);
+		return -EINVAL;
+	}
 
 	ipe_dev_intf = kzalloc(sizeof(struct cam_hw_intf), GFP_KERNEL);
 	if (!ipe_dev_intf)
 		return -ENOMEM;
 
-	of_property_read_u32(pdev->dev.of_node,
-		"cell-index", &ipe_dev_intf->hw_idx);
-
+	ipe_dev_intf->hw_idx = hw_idx;
 	ipe_dev = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
 	if (!ipe_dev) {
 		kfree(ipe_dev_intf);
diff --git a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
index 49176b5..71af1a2 100644
--- a/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
+++ b/drivers/media/platform/msm/camera/cam_icp/icp_hw/ipe_hw/ipe_soc.c
@@ -116,7 +116,7 @@
 	int rc = 0;
 
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, false);
+		CAM_SVS_VOTE, false);
 	if (rc) {
 		CAM_ERR(CAM_ICP, "enable platform failed");
 		return rc;
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
index 0ee368d..cfe5071 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.c
@@ -24,6 +24,93 @@
 #include "cam_trace.h"
 #include "cam_debug_util.h"
 
+static const char isp_dev_name[] = "isp";
+
+static int __cam_isp_ctx_enqueue_request_in_order(
+	struct cam_context *ctx, struct cam_ctx_request *req)
+{
+	struct cam_ctx_request           *req_current;
+	struct cam_ctx_request           *req_prev;
+	struct list_head                  temp_list;
+
+	INIT_LIST_HEAD(&temp_list);
+	spin_lock_bh(&ctx->lock);
+	if (list_empty(&ctx->pending_req_list)) {
+		list_add_tail(&req->list, &ctx->pending_req_list);
+	} else {
+		list_for_each_entry_safe_reverse(
+			req_current, req_prev, &ctx->pending_req_list, list) {
+			if (req->request_id < req_current->request_id) {
+				list_del_init(&req_current->list);
+				list_add(&req_current->list, &temp_list);
+				continue;
+			} else if (req->request_id == req_current->request_id) {
+				CAM_WARN(CAM_ISP,
+					"Received duplicated request %lld",
+					req->request_id);
+			}
+			break;
+		}
+		list_add_tail(&req->list, &ctx->pending_req_list);
+
+		if (!list_empty(&temp_list)) {
+			list_for_each_entry_safe(
+				req_current, req_prev, &temp_list, list) {
+				list_del_init(&req_current->list);
+				list_add_tail(&req_current->list,
+					&ctx->pending_req_list);
+			}
+		}
+	}
+	spin_unlock_bh(&ctx->lock);
+	return 0;
+}
+
+static const char *__cam_isp_resource_handle_id_to_type
+	(uint32_t resource_handle)
+{
+	switch (resource_handle) {
+	case CAM_ISP_IFE_OUT_RES_FULL:
+		return "CAM_ISP_IFE_OUT_RES_FULL";
+	case CAM_ISP_IFE_OUT_RES_DS4:
+		return "CAM_ISP_IFE_OUT_RES_DS4";
+	case CAM_ISP_IFE_OUT_RES_DS16:
+		return "CAM_ISP_IFE_OUT_RES_DS16";
+	case CAM_ISP_IFE_OUT_RES_RAW_DUMP:
+		return "CAM_ISP_IFE_OUT_RES_RAW_DUMP";
+	case CAM_ISP_IFE_OUT_RES_FD:
+		return "CAM_ISP_IFE_OUT_RES_FD";
+	case CAM_ISP_IFE_OUT_RES_PDAF:
+		return "CAM_ISP_IFE_OUT_RES_PDAF";
+	case CAM_ISP_IFE_OUT_RES_RDI_0:
+		return "CAM_ISP_IFE_OUT_RES_RDI_0";
+	case CAM_ISP_IFE_OUT_RES_RDI_1:
+		return "CAM_ISP_IFE_OUT_RES_RDI_1";
+	case CAM_ISP_IFE_OUT_RES_RDI_2:
+		return "CAM_ISP_IFE_OUT_RES_RDI_2";
+	case CAM_ISP_IFE_OUT_RES_RDI_3:
+		return "CAM_ISP_IFE_OUT_RES_RDI_3";
+	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE:
+		return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BE";
+	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST:
+		return "CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST";
+	case CAM_ISP_IFE_OUT_RES_STATS_TL_BG:
+		return "CAM_ISP_IFE_OUT_RES_STATS_TL_BG";
+	case CAM_ISP_IFE_OUT_RES_STATS_BF:
+		return "CAM_ISP_IFE_OUT_RES_STATS_BF";
+	case CAM_ISP_IFE_OUT_RES_STATS_AWB_BG:
+		return "CAM_ISP_IFE_OUT_RES_STATS_AWB_BG";
+	case CAM_ISP_IFE_OUT_RES_STATS_BHIST:
+		return "CAM_ISP_IFE_OUT_RES_STATS_BHIST";
+	case CAM_ISP_IFE_OUT_RES_STATS_RS:
+		return "CAM_ISP_IFE_OUT_RES_STATS_RS";
+	case CAM_ISP_IFE_OUT_RES_STATS_CS:
+		return "CAM_ISP_IFE_OUT_RES_STATS_CS";
+	default:
+		return "CAM_ISP_Invalid_Resource_Type";
+	}
+}
+
 static uint64_t __cam_isp_ctx_get_event_ts(uint32_t evt_id, void *evt_data)
 {
 	uint64_t ts = 0;
@@ -61,6 +148,30 @@
 	return ts;
 }
 
+static void __cam_isp_ctx_handle_buf_done_fail_log(
+	struct cam_isp_ctx_req *req_isp)
+{
+	int i;
+
+	if (req_isp->num_fence_map_out >= CAM_ISP_CTX_RES_MAX) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Num Resources exceed mMAX %d >= %d ",
+			req_isp->num_fence_map_out, CAM_ISP_CTX_RES_MAX);
+		return;
+	}
+
+	CAM_ERR_RATE_LIMIT(CAM_ISP,
+		"Resource Handles that fail to generate buf_done in prev frame");
+	for (i = 0; i < req_isp->num_fence_map_out; i++) {
+		if (req_isp->fence_map_out[i].sync_id != -1)
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Resource_Handle: [%s] Sync_ID: [0x%x]",
+			__cam_isp_resource_handle_id_to_type(
+			req_isp->fence_map_out[i].resource_handle),
+			req_isp->fence_map_out[i].sync_id);
+	}
+}
+
 static int __cam_isp_ctx_handle_buf_done_in_activated_state(
 	struct cam_isp_context *ctx_isp,
 	struct cam_isp_hw_done_event_data *done,
@@ -100,23 +211,35 @@
 			continue;
 		}
 
+		if (req_isp->fence_map_out[j].sync_id == -1) {
+			__cam_isp_ctx_handle_buf_done_fail_log(req_isp);
+			continue;
+		}
+
 		if (!bubble_state) {
-			CAM_DBG(CAM_ISP, "Sync with success: fd 0x%x",
-				   req_isp->fence_map_out[j].sync_id);
+			CAM_DBG(CAM_ISP,
+				"Sync with success: req %lld res 0x%x fd 0x%x",
+				req->request_id,
+				req_isp->fence_map_out[j].resource_handle,
+				req_isp->fence_map_out[j].sync_id);
+
 			rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
 				CAM_SYNC_STATE_SIGNALED_SUCCESS);
 			if (rc)
-				CAM_ERR(CAM_ISP, "Sync failed with rc = %d",
+				CAM_DBG(CAM_ISP, "Sync failed with rc = %d",
 					 rc);
-
 		} else if (!req_isp->bubble_report) {
-			CAM_DBG(CAM_ISP, "Sync with failure: fd 0x%x",
-				   req_isp->fence_map_out[j].sync_id);
+			CAM_DBG(CAM_ISP,
+				"Sync with failure: req %lld res 0x%x fd 0x%x",
+				req->request_id,
+				req_isp->fence_map_out[j].resource_handle,
+				req_isp->fence_map_out[j].sync_id);
+
 			rc = cam_sync_signal(req_isp->fence_map_out[j].sync_id,
 				CAM_SYNC_STATE_SIGNALED_ERROR);
 			if (rc)
 				CAM_ERR(CAM_ISP, "Sync failed with rc = %d",
-					 rc);
+					rc);
 		} else {
 			/*
 			 * Ignore the buffer done if bubble detect is on
@@ -124,18 +247,31 @@
 			 * bubble detects. But for safety, we just move the
 			 * current active request to the pending list here.
 			 */
+			CAM_DBG(CAM_ISP,
+				"buf done with bubble state %d recovery %d",
+				bubble_state, req_isp->bubble_report);
 			list_del_init(&req->list);
 			list_add(&req->list, &ctx->pending_req_list);
 			continue;
 		}
 
 		CAM_DBG(CAM_ISP, "req %lld, reset sync id 0x%x",
-			   req->request_id,
-			   req_isp->fence_map_out[j].sync_id);
-		req_isp->num_acked++;
-		req_isp->fence_map_out[j].sync_id = -1;
+			req->request_id,
+			req_isp->fence_map_out[j].sync_id);
+		if (!rc) {
+			req_isp->num_acked++;
+			req_isp->fence_map_out[j].sync_id = -1;
+		}
 	}
 
+	if (req_isp->num_acked > req_isp->num_fence_map_out) {
+		/* Should not happen */
+		CAM_ERR(CAM_ISP,
+			"WARNING: req_id %lld num_acked %d > map_out %d",
+			req->request_id, req_isp->num_acked,
+			req_isp->num_fence_map_out);
+		WARN_ON(req_isp->num_acked > req_isp->num_fence_map_out);
+	}
 	if (req_isp->num_acked == req_isp->num_fence_map_out) {
 		list_del_init(&req->list);
 		list_add_tail(&req->list, &ctx->free_req_list);
@@ -168,7 +304,7 @@
 		ctx_isp->sof_timestamp_val);
 	CAM_DBG(CAM_ISP, " sof status:%d", sof_event_status);
 
-	if (cam_req_mgr_notify_frame_message(&req_msg,
+	if (cam_req_mgr_notify_message(&req_msg,
 		V4L_EVENT_CAM_REQ_MGR_SOF, V4L_EVENT_CAM_REQ_MGR_EVENT))
 		CAM_ERR(CAM_ISP,
 			"Error in notifying the sof time for req id:%lld",
@@ -256,7 +392,7 @@
 		__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_SUCCESS);
 	} else {
-		CAM_ERR(CAM_ISP, "Can not notify SOF to CRM");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify SOF to CRM");
 		rc = -EFAULT;
 	}
 
@@ -291,6 +427,13 @@
 	return rc;
 }
 
+static int __cam_isp_ctx_reg_upd_in_hw_error(
+	struct cam_isp_context *ctx_isp, void *evt_data)
+{
+	ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_SOF;
+	return 0;
+}
+
 static int __cam_isp_ctx_sof_in_activated_state(
 	struct cam_isp_context *ctx_isp, void *evt_data)
 {
@@ -553,8 +696,13 @@
 	void *evt_data)
 {
 	int                              rc = 0;
-	struct cam_ctx_request          *req;
+	uint32_t                         i = 0;
+	bool                             found = 0;
+	struct cam_ctx_request          *req = NULL;
+	struct cam_ctx_request          *req_temp;
+	struct cam_isp_ctx_req          *req_isp = NULL;
 	struct cam_req_mgr_error_notify  notify;
+	uint64_t                         error_request_id;
 
 	struct cam_context *ctx = ctx_isp->base;
 	struct cam_isp_hw_error_event_data  *error_event_data =
@@ -565,7 +713,7 @@
 	CAM_DBG(CAM_ISP, "Enter error_type = %d", error_type);
 	if ((error_type == CAM_ISP_HW_ERROR_OVERFLOW) ||
 		(error_type == CAM_ISP_HW_ERROR_BUSIF_OVERFLOW))
-		notify.error = CRM_KMD_ERR_FATAL;
+		notify.error = CRM_KMD_ERR_OVERFLOW;
 
 	/*
 	 * Need to check the active req
@@ -574,32 +722,94 @@
 	 */
 
 	if (list_empty(&ctx->active_req_list)) {
-		CAM_ERR(CAM_ISP, "handling error with no active request");
-		rc = -EINVAL;
-		goto end;
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"handling error with no active request");
+	} else {
+		list_for_each_entry_safe(req, req_temp,
+			&ctx->active_req_list, list) {
+			req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+			if (!req_isp->bubble_report) {
+				for (i = 0; i < req_isp->num_fence_map_out;
+					i++) {
+					CAM_ERR(CAM_ISP, "req %llu, Sync fd %x",
+						req->request_id,
+						req_isp->fence_map_out[i].
+						sync_id);
+					if (req_isp->fence_map_out[i].sync_id
+						!= -1) {
+						rc = cam_sync_signal(
+						req_isp->fence_map_out[i].
+						sync_id,
+						CAM_SYNC_STATE_SIGNALED_ERROR);
+						req_isp->fence_map_out[i].
+						sync_id = -1;
+					}
+				}
+				list_del_init(&req->list);
+				list_add_tail(&req->list, &ctx->free_req_list);
+				ctx_isp->active_req_cnt--;
+			} else {
+				found = 1;
+				break;
+			}
+		}
 	}
 
-	req = list_first_entry(&ctx->active_req_list,
-				struct cam_ctx_request, list);
+	if (found) {
+		list_for_each_entry_safe_reverse(req, req_temp,
+			&ctx->active_req_list, list) {
+			req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+			list_del_init(&req->list);
+			list_add(&req->list, &ctx->pending_req_list);
+			ctx_isp->active_req_cnt--;
+		}
+	}
+
+	do {
+		if (list_empty(&ctx->pending_req_list)) {
+			error_request_id = ctx_isp->last_applied_req_id + 1;
+			req_isp = NULL;
+			break;
+		}
+		req = list_first_entry(&ctx->pending_req_list,
+			struct cam_ctx_request, list);
+		req_isp = (struct cam_isp_ctx_req *) req->req_priv;
+		error_request_id = ctx_isp->last_applied_req_id;
+
+		if (req_isp->bubble_report)
+			break;
+
+		for (i = 0; i < req_isp->num_fence_map_out; i++) {
+			if (req_isp->fence_map_out[i].sync_id != -1)
+				rc = cam_sync_signal(
+					req_isp->fence_map_out[i].sync_id,
+					CAM_SYNC_STATE_SIGNALED_ERROR);
+			req_isp->fence_map_out[i].sync_id = -1;
+		}
+		list_del_init(&req->list);
+		list_add_tail(&req->list, &ctx->free_req_list);
+
+	} while (req->request_id < ctx_isp->last_applied_req_id);
+
 
 	if (ctx->ctx_crm_intf && ctx->ctx_crm_intf->notify_err) {
 		notify.link_hdl = ctx->link_hdl;
 		notify.dev_hdl = ctx->dev_hdl;
-		notify.req_id = req->request_id;
+		notify.req_id = error_request_id;
+
+		if (req_isp && req_isp->bubble_report)
+			notify.error = CRM_KMD_ERR_BUBBLE;
+
+		CAM_WARN(CAM_ISP, "Notify CRM: req %lld, frame %lld\n",
+			error_request_id, ctx_isp->frame_id);
 
 		ctx->ctx_crm_intf->notify_err(&notify);
-		CAM_ERR(CAM_ISP, "Notify CRM about ERROR frame %lld",
-			ctx_isp->frame_id);
+		ctx_isp->substate_activated = CAM_ISP_CTX_ACTIVATED_HW_ERROR;
 	} else {
-		CAM_ERR(CAM_ISP, "Can not notify ERRROR to CRM");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify ERRROR to CRM");
 		rc = -EFAULT;
 	}
 
-	list_del_init(&req->list);
-	list_add(&req->list, &ctx->pending_req_list);
-	/* might need to check if active list is empty */
-
-end:
 	CAM_DBG(CAM_ISP, "Exit");
 	return rc;
 }
@@ -609,7 +819,7 @@
 	/* SOF */
 	{
 		.irq_ops = {
-			NULL,
+			__cam_isp_ctx_handle_error,
 			__cam_isp_ctx_sof_in_activated_state,
 			__cam_isp_ctx_reg_upd_in_sof,
 			__cam_isp_ctx_notify_sof_in_actived_state,
@@ -642,7 +852,7 @@
 	/* BUBBLE */
 	{
 		.irq_ops = {
-			NULL,
+			__cam_isp_ctx_handle_error,
 			__cam_isp_ctx_sof_in_activated_state,
 			NULL,
 			__cam_isp_ctx_notify_sof_in_actived_state,
@@ -653,7 +863,7 @@
 	/* Bubble Applied */
 	{
 		.irq_ops = {
-			NULL,
+			__cam_isp_ctx_handle_error,
 			__cam_isp_ctx_sof_in_activated_state,
 			__cam_isp_ctx_reg_upd_in_activated_state,
 			__cam_isp_ctx_epoch_in_bubble_applied,
@@ -661,6 +871,17 @@
 			__cam_isp_ctx_buf_done_in_bubble_applied,
 		},
 	},
+	/* HW ERROR */
+	{
+		.irq_ops = {
+			NULL,
+			__cam_isp_ctx_sof_in_activated_state,
+			__cam_isp_ctx_reg_upd_in_hw_error,
+			NULL,
+			NULL,
+			NULL,
+		},
+	},
 	/* HALT */
 	{
 	},
@@ -672,7 +893,9 @@
 {
 	int rc = 0;
 	struct cam_ctx_request          *req;
+	struct cam_ctx_request          *active_req;
 	struct cam_isp_ctx_req          *req_isp;
+	struct cam_isp_ctx_req          *active_req_isp;
 	struct cam_isp_context          *ctx_isp;
 	struct cam_hw_config_args        cfg;
 
@@ -690,14 +913,6 @@
 	 *
 	 */
 	ctx_isp = (struct cam_isp_context *) ctx->ctx_priv;
-	if (ctx_isp->active_req_cnt >=  2) {
-		CAM_ERR_RATE_LIMIT(CAM_ISP,
-			"Reject apply request due to congestion(cnt = %d)",
-			ctx_isp->active_req_cnt);
-		rc = -EFAULT;
-		goto end;
-	}
-
 	req = list_first_entry(&ctx->pending_req_list, struct cam_ctx_request,
 		list);
 
@@ -716,6 +931,25 @@
 	CAM_DBG(CAM_ISP, "Apply request %lld", req->request_id);
 	req_isp = (struct cam_isp_ctx_req *) req->req_priv;
 
+	if (ctx_isp->active_req_cnt >=  2) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Reject apply request (id %lld) due to congestion(cnt = %d)",
+			req->request_id,
+			ctx_isp->active_req_cnt);
+		if (!list_empty(&ctx->active_req_list)) {
+			active_req = list_first_entry(&ctx->active_req_list,
+				struct cam_ctx_request, list);
+			active_req_isp =
+				(struct cam_isp_ctx_req *) active_req->req_priv;
+			__cam_isp_ctx_handle_buf_done_fail_log(active_req_isp);
+		} else {
+			CAM_ERR_RATE_LIMIT(CAM_ISP,
+				"WARNING: should not happen (cnt = %d) but active_list empty",
+				ctx_isp->active_req_cnt);
+		}
+			rc = -EFAULT;
+			goto end;
+	}
 	req_isp->bubble_report = apply->report_if_bubble;
 
 	cfg.ctxt_to_hw_map = ctx_isp->hw_ctx;
@@ -724,11 +958,13 @@
 
 	rc = ctx->hw_mgr_intf->hw_config(ctx->hw_mgr_intf->hw_mgr_priv, &cfg);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Can not apply the configuration");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not apply the configuration");
 	} else {
 		spin_lock_bh(&ctx->lock);
 		ctx_isp->substate_activated = next_state;
-		CAM_DBG(CAM_ISP, "new state %d", next_state);
+		ctx_isp->last_applied_req_id = apply->request_id;
+		CAM_DBG(CAM_ISP, "new substate state %d, applied req %lld",
+			next_state, ctx_isp->last_applied_req_id);
 		spin_unlock_bh(&ctx->lock);
 	}
 end:
@@ -964,7 +1200,7 @@
 		__cam_isp_ctx_send_sof_timestamp(ctx_isp, request_id,
 			CAM_REQ_MGR_SOF_EVENT_SUCCESS);
 	} else {
-		CAM_ERR(CAM_ISP, "Can not notify SOF to CRM");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Can not notify SOF to CRM");
 	}
 
 	if (list_empty(&ctx->active_req_list))
@@ -1105,6 +1341,7 @@
 					CAM_SYNC_STATE_SIGNALED_ERROR);
 			}
 		list_add_tail(&req->list, &ctx->free_req_list);
+		ctx_isp->active_req_cnt--;
 	}
 
 	/* notify reqmgr with sof signal */
@@ -1347,9 +1584,9 @@
 		ctx_isp->hw_ctx = NULL;
 	}
 
-	ctx->session_hdl = 0;
-	ctx->dev_hdl = 0;
-	ctx->link_hdl = 0;
+	ctx->session_hdl = -1;
+	ctx->dev_hdl = -1;
+	ctx->link_hdl = -1;
 	ctx->ctx_crm_intf = NULL;
 	ctx_isp->frame_id = 0;
 	ctx_isp->active_req_cnt = 0;
@@ -1462,10 +1699,11 @@
 	req->request_id = packet->header.request_id;
 	req->status = 1;
 
-	if (ctx->state == CAM_CTX_ACTIVATED && ctx->ctx_crm_intf->add_req) {
+	if (ctx->state >= CAM_CTX_READY && ctx->ctx_crm_intf->add_req) {
 		add_req.link_hdl = ctx->link_hdl;
 		add_req.dev_hdl  = ctx->dev_hdl;
 		add_req.req_id   = req->request_id;
+		add_req.skip_before_applying = 0;
 		rc = ctx->ctx_crm_intf->add_req(&add_req);
 		if (rc) {
 			CAM_ERR(CAM_ISP, "Error: Adding request id=%llu",
@@ -1477,9 +1715,7 @@
 	CAM_DBG(CAM_ISP, "Packet request id 0x%llx",
 		packet->header.request_id);
 
-	spin_lock_bh(&ctx->lock);
-	list_add_tail(&req->list, &ctx->pending_req_list);
-	spin_unlock_bh(&ctx->lock);
+	__cam_isp_ctx_enqueue_request_in_order(ctx, req);
 
 	CAM_DBG(CAM_ISP, "Preprocessing Config %lld successful",
 		req->request_id);
@@ -1633,7 +1869,7 @@
 
 	rc = __cam_isp_ctx_config_dev_in_top_state(ctx, cmd);
 
-	if (!rc && ctx->link_hdl) {
+	if (!rc && (ctx->link_hdl >= 0)) {
 		ctx->state = CAM_CTX_READY;
 		trace_cam_context_state("ISP", ctx);
 	}
@@ -1671,7 +1907,7 @@
 {
 	int rc = 0;
 
-	ctx->link_hdl = 0;
+	ctx->link_hdl = -1;
 	ctx->ctx_crm_intf = NULL;
 
 	return rc;
@@ -1757,7 +1993,7 @@
 {
 	int rc = 0;
 
-	ctx->link_hdl = 0;
+	ctx->link_hdl = -1;
 	ctx->ctx_crm_intf = NULL;
 	ctx->state = CAM_CTX_ACQUIRED;
 	trace_cam_context_state("ISP", ctx);
@@ -1843,27 +2079,32 @@
 	struct cam_release_dev_cmd *cmd)
 {
 	int rc = 0;
-	struct cam_isp_context *ctx_isp =
-		(struct cam_isp_context *) ctx->ctx_priv;
 
-	__cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+	rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Stop device failed rc=%d", rc);
 
-	if (ctx_isp->hw_ctx) {
-		struct cam_hw_release_args   arg;
+	rc = __cam_isp_ctx_release_dev_in_top_state(ctx, cmd);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Release device failed rc=%d", rc);
 
-		arg.ctxt_to_hw_map = ctx_isp->hw_ctx;
-		ctx->hw_mgr_intf->hw_release(ctx->hw_mgr_intf->hw_mgr_priv,
-			&arg);
-		ctx_isp->hw_ctx = NULL;
-	}
+	return rc;
+}
 
-	ctx->session_hdl = 0;
-	ctx->dev_hdl = 0;
-	ctx->link_hdl = 0;
-	ctx->ctx_crm_intf = NULL;
+static int __cam_isp_ctx_unlink_in_activated(struct cam_context *ctx,
+	struct cam_req_mgr_core_dev_link_setup *unlink)
+{
+	int rc = 0;
 
-	ctx->state =  CAM_CTX_AVAILABLE;
-	trace_cam_context_state("ISP", ctx);
+	CAM_WARN(CAM_ISP,
+		"Received unlink in activated state. It's unexpected");
+	rc = __cam_isp_ctx_stop_dev_in_activated_unlock(ctx);
+	if (rc)
+		CAM_WARN(CAM_ISP, "Stop device failed rc=%d", rc);
+
+	rc = __cam_isp_ctx_unlink_in_ready(ctx, unlink);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Unlink failed rc=%d", rc);
 
 	return rc;
 }
@@ -1875,7 +2116,7 @@
 	struct cam_isp_context *ctx_isp =
 		(struct cam_isp_context *) ctx->ctx_priv;
 
-	trace_cam_apply_req("ISP", apply);
+	trace_cam_apply_req("ISP", apply->request_id);
 	CAM_DBG(CAM_ISP, "Enter: apply req in Substate %d request _id:%lld",
 		 ctx_isp->substate_activated, apply->request_id);
 	if (ctx_isp->substate_machine[ctx_isp->substate_activated].
@@ -1883,14 +2124,16 @@
 		rc = ctx_isp->substate_machine[ctx_isp->substate_activated].
 			crm_ops.apply_req(ctx, apply);
 	} else {
-		CAM_ERR(CAM_ISP, "No handle function in activated substate %d",
-			 ctx_isp->substate_activated);
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"No handle function in activated substate %d",
+			ctx_isp->substate_activated);
 		rc = -EFAULT;
 	}
 
 	if (rc)
-		CAM_ERR(CAM_ISP, "Apply failed in active substate %d",
-			 ctx_isp->substate_activated);
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"Apply failed in active substate %d",
+			ctx_isp->substate_activated);
 	return rc;
 }
 
@@ -1977,6 +2220,7 @@
 			.config_dev = __cam_isp_ctx_config_dev_in_top_state,
 		},
 		.crm_ops = {
+			.unlink = __cam_isp_ctx_unlink_in_activated,
 			.apply_req = __cam_isp_ctx_apply_req,
 			.flush_req = __cam_isp_ctx_flush_req_in_top_state,
 		},
@@ -2017,8 +2261,8 @@
 	}
 
 	/* camera context setup */
-	rc = cam_context_init(ctx_base, crm_node_intf, hw_intf, ctx->req_base,
-		CAM_CTX_REQ_MAX);
+	rc = cam_context_init(ctx_base, isp_dev_name, crm_node_intf, hw_intf,
+		ctx->req_base, CAM_CTX_REQ_MAX);
 	if (rc) {
 		CAM_ERR(CAM_ISP, "Camera Context Base init failed");
 		goto err;
@@ -2045,4 +2289,3 @@
 	memset(ctx, 0, sizeof(*ctx));
 	return rc;
 }
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
index 621d652..347290c 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_context.h
@@ -31,7 +31,7 @@
  * Maxiimum configuration entry size  - This is based on the
  * worst case DUAL IFE use case plus some margin.
  */
-#define CAM_ISP_CTX_CFG_MAX                     20
+#define CAM_ISP_CTX_CFG_MAX                     22
 
 /* forward declaration */
 struct cam_isp_context;
@@ -50,6 +50,7 @@
 	CAM_ISP_CTX_ACTIVATED_EPOCH,
 	CAM_ISP_CTX_ACTIVATED_BUBBLE,
 	CAM_ISP_CTX_ACTIVATED_BUBBLE_APPLIED,
+	CAM_ISP_CTX_ACTIVATED_HW_ERROR,
 	CAM_ISP_CTX_ACTIVATED_HALT,
 	CAM_ISP_CTX_ACTIVATED_MAX,
 };
@@ -111,6 +112,7 @@
  * @reported_req_id:       Last reported request id
  * @subscribe_event:       The irq event mask that CRM subscribes to, IFE will
  *                         invoke CRM cb at those event.
+ * @last_applied_req_id:   Last applied request id
  *
  */
 struct cam_isp_context {
@@ -129,6 +131,7 @@
 	int32_t                          active_req_cnt;
 	int64_t                          reported_req_id;
 	uint32_t                         subscribe_event;
+	int64_t                          last_applied_req_id;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
index 2bf7795..bcdba05 100644
--- a/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
+++ b/drivers/media/platform/msm/camera/cam_isp/cam_isp_dev.c
@@ -36,6 +36,25 @@
 	{}
 };
 
+static int cam_isp_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_node *node = v4l2_get_subdevdata(sd);
+
+	if (!node) {
+		CAM_ERR(CAM_ISP, "Node ptr is NULL");
+		return -EINVAL;
+	}
+
+	cam_node_shutdown(node);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_isp_subdev_internal_ops = {
+	.close = cam_isp_subdev_close,
+};
+
 static int cam_isp_dev_remove(struct platform_device *pdev)
 {
 	int rc = 0;
@@ -64,6 +83,7 @@
 	struct cam_hw_mgr_intf         hw_mgr_intf;
 	struct cam_node               *node;
 
+	g_isp_dev.sd.internal_ops = &cam_isp_subdev_internal_ops;
 	/* Initialze the v4l2 subdevice first. (create cam_node) */
 	rc = cam_subdev_probe(&g_isp_dev.sd, pdev, CAM_ISP_DEV_NAME,
 		CAM_IFE_DEVICE_TYPE);
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile
index 7e3c353..1f7dc76 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/Makefile
@@ -8,6 +8,7 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
 
 obj-$(CONFIG_SPECTRA_CAMERA) += hw_utils/ isp_hw/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_isp_hw_mgr.o cam_ife_hw_mgr.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index d0b0751..0362758 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
@@ -13,6 +13,8 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <soc/qcom/scm.h>
 #include <uapi/media/cam_isp.h>
 #include "cam_smmu_api.h"
 #include "cam_req_mgr_workq.h"
@@ -25,11 +27,62 @@
 #include "cam_cdm_intf_api.h"
 #include "cam_packet_util.h"
 #include "cam_debug_util.h"
+#include "cam_cpas_api.h"
 
 #define CAM_IFE_HW_ENTRIES_MAX  20
 
+#define TZ_SVC_SMMU_PROGRAM 0x15
+#define TZ_SAFE_SYSCALL_ID  0x3
+#define CAM_IFE_SAFE_DISABLE 0
+#define CAM_IFE_SAFE_ENABLE 1
+#define SMMU_SE_IFE 0
+
+#define CAM_ISP_PACKET_META_MAX                     \
+	(CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON + 1)
+
+#define CAM_ISP_GENERIC_BLOB_TYPE_MAX               \
+	(CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG + 1)
+
+static uint32_t blob_type_hw_cmd_map[CAM_ISP_GENERIC_BLOB_TYPE_MAX] = {
+	CAM_ISP_HW_CMD_GET_HFR_UPDATE,
+	CAM_ISP_HW_CMD_CLOCK_UPDATE,
+	CAM_ISP_HW_CMD_BW_UPDATE,
+};
+
 static struct cam_ife_hw_mgr g_ife_hw_mgr;
 
+static int cam_ife_notify_safe_lut_scm(bool safe_trigger)
+{
+	uint32_t camera_hw_version, rc = 0;
+	struct scm_desc desc = {0};
+
+	rc = cam_cpas_get_cpas_hw_version(&camera_hw_version);
+	if (!rc) {
+		switch (camera_hw_version) {
+		case CAM_CPAS_TITAN_170_V100:
+		case CAM_CPAS_TITAN_170_V110:
+		case CAM_CPAS_TITAN_175_V100:
+
+			desc.arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL);
+			desc.args[0] = SMMU_SE_IFE;
+			desc.args[1] = safe_trigger;
+
+			CAM_DBG(CAM_ISP, "Safe scm call %d", safe_trigger);
+			if (scm_call2(SCM_SIP_FNID(TZ_SVC_SMMU_PROGRAM,
+					TZ_SAFE_SYSCALL_ID), &desc)) {
+				CAM_ERR(CAM_ISP,
+					"scm call to Enable Safe failed");
+				rc = -EINVAL;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return rc;
+}
+
 static int cam_ife_mgr_get_hw_caps(void *hw_mgr_priv,
 	void *hw_caps_args)
 {
@@ -64,7 +117,7 @@
 		sizeof(struct cam_isp_query_cap_cmd)))
 		rc = -EFAULT;
 
-	CAM_DBG(CAM_ISP, "exit rc :%d !", rc);
+	CAM_DBG(CAM_ISP, "exit rc :%d", rc);
 
 	return rc;
 }
@@ -87,6 +140,39 @@
 	return rc;
 }
 
+static int cam_ife_hw_mgr_reset_csid_res(
+	struct cam_ife_hw_mgr_res   *isp_hw_res)
+{
+	int i;
+	int rc = 0;
+	struct cam_hw_intf      *hw_intf;
+	struct cam_csid_reset_cfg_args  csid_reset_args;
+
+	csid_reset_args.reset_type = CAM_IFE_CSID_RESET_PATH;
+
+	for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+		if (!isp_hw_res->hw_res[i])
+			continue;
+		csid_reset_args.node_res = isp_hw_res->hw_res[i];
+		hw_intf = isp_hw_res->hw_res[i]->hw_intf;
+		CAM_DBG(CAM_ISP, "Resetting csid hardware %d",
+			hw_intf->hw_idx);
+		if (hw_intf->hw_ops.reset) {
+			rc = hw_intf->hw_ops.reset(hw_intf->hw_priv,
+				&csid_reset_args,
+				sizeof(struct cam_csid_reset_cfg_args));
+			if (rc <= 0)
+				goto err;
+		}
+	}
+
+	return 0;
+err:
+	CAM_ERR(CAM_ISP, "RESET HW res failed: (type:%d, id:%d)",
+		isp_hw_res->res_type, isp_hw_res->res_id);
+	return rc;
+}
+
 static int cam_ife_hw_mgr_init_hw_res(
 	struct cam_ife_hw_mgr_res   *isp_hw_res)
 {
@@ -111,13 +197,14 @@
 
 	return 0;
 err:
-	CAM_ERR(CAM_ISP, "INIT HW res failed! (type:%d, id:%d)",
+	CAM_ERR(CAM_ISP, "INIT HW res failed: (type:%d, id:%d)",
 		isp_hw_res->res_type, isp_hw_res->res_id);
 	return rc;
 }
 
 static int cam_ife_hw_mgr_start_hw_res(
-	struct cam_ife_hw_mgr_res   *isp_hw_res)
+	struct cam_ife_hw_mgr_res   *isp_hw_res,
+	struct cam_ife_hw_mgr_ctx   *ctx)
 {
 	int i;
 	int rc = -1;
@@ -128,11 +215,13 @@
 			continue;
 		hw_intf = isp_hw_res->hw_res[i]->hw_intf;
 		if (hw_intf->hw_ops.start) {
+			isp_hw_res->hw_res[i]->rdi_only_ctx =
+				ctx->is_rdi_only_context;
 			rc = hw_intf->hw_ops.start(hw_intf->hw_priv,
 				isp_hw_res->hw_res[i],
 				sizeof(struct cam_isp_resource_node));
 			if (rc) {
-				CAM_ERR(CAM_ISP, "Can not start HW resources!");
+				CAM_ERR(CAM_ISP, "Can not start HW resources");
 				goto err;
 			}
 		} else {
@@ -143,7 +232,7 @@
 
 	return 0;
 err:
-	CAM_ERR(CAM_ISP, "Start hw res failed! (type:%d, id:%d)",
+	CAM_ERR(CAM_ISP, "Start hw res failed (type:%d, id:%d)",
 		isp_hw_res->res_type, isp_hw_res->res_id);
 	return rc;
 }
@@ -210,7 +299,7 @@
 			struct cam_ife_hw_mgr_res, list);
 		list_del_init(&res_ptr->list);
 	} else {
-		CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx!");
+		CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx");
 		rc = -1;
 	}
 	*res = res_ptr;
@@ -235,7 +324,7 @@
 				sizeof(struct cam_isp_resource_node));
 			if (rc)
 				CAM_ERR(CAM_ISP,
-					"Release hw resrouce id %d failed!",
+					"Release hw resrouce id %d failed",
 					isp_hw_res->res_id);
 			isp_hw_res->hw_res[i] = NULL;
 		} else
@@ -362,7 +451,7 @@
 			struct cam_ife_hw_mgr_ctx, list);
 		list_del_init(&ctx_ptr->list);
 	} else {
-		CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx!");
+		CAM_ERR(CAM_ISP, "No more free ife hw mgr ctx");
 		rc = -1;
 	}
 	*ife_ctx = ctx_ptr;
@@ -417,7 +506,7 @@
 	uint32_t i;
 
 	if (list_empty(&ctx->res_list_ife_src)) {
-		CAM_ERR(CAM_ISP, "Error! Mux List empty");
+		CAM_ERR(CAM_ISP, "Mux List empty");
 		return -ENODEV;
 	}
 
@@ -474,6 +563,8 @@
 		CAM_ERR(CAM_ISP, "invalid resource type");
 		goto err;
 	}
+	CAM_DBG(CAM_ISP, "vfe_in_res_id = %d, vfe_out_red_id = %d",
+		vfe_in_res_id, vfe_out_res_id);
 
 	vfe_acquire.rsrc_type = CAM_ISP_RESOURCE_VFE_OUT;
 	vfe_acquire.tasklet = ife_ctx->common.tasklet_info;
@@ -482,6 +573,9 @@
 	for (i = 0; i < in_port->num_out_res; i++) {
 		out_port = &in_port->data[i];
 
+		CAM_DBG(CAM_ISP, "i = %d, vfe_out_res_id = %d, out_port: %d",
+			i, vfe_out_res_id, out_port->res_type);
+
 		if (vfe_out_res_id != out_port->res_type)
 			continue;
 
@@ -490,6 +584,7 @@
 		vfe_acquire.vfe_out.out_port_info = out_port;
 		vfe_acquire.vfe_out.split_id = CAM_ISP_HW_SPLIT_LEFT;
 		vfe_acquire.vfe_out.unique_id = ife_ctx->ctx_index;
+		vfe_acquire.vfe_out.is_dual = 0;
 		hw_intf = ife_src_res->hw_res[0]->hw_intf;
 		rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
 			&vfe_acquire,
@@ -503,7 +598,9 @@
 	}
 
 	if (i == in_port->num_out_res) {
-		CAM_ERR(CAM_ISP, "Can not acquire out resource");
+		CAM_ERR(CAM_ISP,
+			"Cannot acquire out resource, i=%d, num_out_res=%d",
+			i, in_port->num_out_res);
 		goto err;
 	}
 
@@ -511,6 +608,7 @@
 	ife_out_res->is_dual_vfe = 0;
 	ife_out_res->res_id = vfe_out_res_id;
 	ife_out_res->res_type = CAM_ISP_RESOURCE_VFE_OUT;
+	ife_src_res->child[ife_src_res->num_children++] = ife_out_res;
 
 	return 0;
 err:
@@ -633,7 +731,8 @@
 				ife_src_res, in_port);
 			break;
 		default:
-			CAM_ERR(CAM_ISP, "Fatal: Unknown IFE SRC resource!");
+			CAM_ERR(CAM_ISP, "Unknown IFE SRC resource: %d",
+				ife_src_res->res_id);
 			break;
 		}
 		if (rc)
@@ -667,7 +766,7 @@
 		rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list,
 			&ife_src_res);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "No more free hw mgr resource!");
+			CAM_ERR(CAM_ISP, "No more free hw mgr resource");
 			goto err;
 		}
 		cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_src,
@@ -706,7 +805,7 @@
 			vfe_acquire.vfe_in.sync_mode = CAM_ISP_HW_SYNC_NONE;
 			break;
 		default:
-			CAM_ERR(CAM_ISP, "Wrong IFE CSID Resource Node!");
+			CAM_ERR(CAM_ISP, "Wrong IFE CSID Resource Node");
 			goto err;
 		}
 		ife_src_res->res_type = vfe_acquire.rsrc_type;
@@ -748,9 +847,11 @@
 		 * csid resource and ife source resource
 		 */
 		csid_res->child[0] = ife_src_res;
-		csid_res->num_children = 1;
 		ife_src_res->parent = csid_res;
 		csid_res->child[csid_res->num_children++] = ife_src_res;
+		CAM_DBG(CAM_ISP, "csid_res=%d  num_children=%d ife_src_res=%d",
+			csid_res->res_id, csid_res->num_children,
+			ife_src_res->res_id);
 	}
 
 	return 0;
@@ -765,18 +866,19 @@
 	uint32_t                            cid_res_id)
 {
 	int rc = -1;
-	int i, j;
+	int i;
 
 	struct cam_ife_hw_mgr               *ife_hw_mgr;
 	struct cam_ife_hw_mgr_res           *csid_res;
-	struct cam_hw_intf                   *hw_intf;
+	struct cam_ife_hw_mgr_res           *cid_res;
+	struct cam_hw_intf                  *hw_intf;
 	struct cam_csid_hw_reserve_resource_args  csid_acquire;
 
 	ife_hw_mgr = ife_ctx->hw_mgr;
 
 	rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &csid_res);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "No more free hw mgr resource!");
+		CAM_ERR(CAM_ISP, "No more free hw mgr resource");
 		goto err;
 	}
 	cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_csid, &csid_res);
@@ -787,70 +889,65 @@
 	csid_acquire.in_port = in_port;
 	csid_acquire.out_port = in_port->data;
 
-	if (in_port->usage_type)
-		csid_acquire.sync_mode = CAM_ISP_HW_SYNC_MASTER;
-	else
-		csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
-
-
-
-	for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
-		if (!ife_hw_mgr->csid_devices[i])
-			continue;
-
-		hw_intf = ife_hw_mgr->csid_devices[i];
-		rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv, &csid_acquire,
-			sizeof(csid_acquire));
-		if (rc)
-			continue;
-		else
-			break;
-	}
-
-	if (i == CAM_IFE_CSID_HW_NUM_MAX) {
-		CAM_ERR(CAM_ISP, "Can not acquire ife csid ipp resrouce!");
-		goto err;
-	}
-
-	CAM_DBG(CAM_ISP, "acquired csid(%d) left ipp resrouce successfully!",
-		 i);
-
 	csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
 	csid_res->res_id = CAM_IFE_PIX_PATH_RES_IPP;
 	csid_res->is_dual_vfe = in_port->usage_type;
-	csid_res->hw_res[0] = csid_acquire.node_res;
-	csid_res->hw_res[1] = NULL;
 
-	if (csid_res->is_dual_vfe) {
-		csid_acquire.sync_mode = CAM_ISP_HW_SYNC_SLAVE;
+	if (in_port->usage_type)
+		csid_res->is_dual_vfe = 1;
+	else {
+		csid_res->is_dual_vfe = 0;
+		csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
+	}
 
-		for (j = i + 1; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
-			if (!ife_hw_mgr->csid_devices[j])
+	list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid,
+		list) {
+		if (cid_res->res_id != cid_res_id)
+			continue;
+
+		for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+			if (!cid_res->hw_res[i])
 				continue;
 
-			hw_intf = ife_hw_mgr->csid_devices[j];
+			csid_acquire.node_res = NULL;
+			if (csid_res->is_dual_vfe) {
+				if (i == CAM_ISP_HW_SPLIT_LEFT)
+					csid_acquire.sync_mode =
+						CAM_ISP_HW_SYNC_MASTER;
+				else
+					csid_acquire.sync_mode =
+						CAM_ISP_HW_SYNC_SLAVE;
+			}
+
+			hw_intf = ife_hw_mgr->csid_devices[
+				cid_res->hw_res[i]->hw_intf->hw_idx];
 			rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
 				&csid_acquire, sizeof(csid_acquire));
-			if (rc)
-				continue;
-			else
-				break;
+			if (rc) {
+				CAM_ERR(CAM_ISP,
+					"Cannot acquire ife csid ipp resource");
+				goto err;
+			}
+
+			csid_res->hw_res[i] = csid_acquire.node_res;
+			CAM_DBG(CAM_ISP,
+				"acquired csid(%s)=%d ipp rsrc successfully",
+				(i == 0) ? "left" : "right",
+				hw_intf->hw_idx);
+
 		}
 
-		if (j == CAM_IFE_CSID_HW_NUM_MAX) {
+		if (i == CAM_IFE_CSID_HW_NUM_MAX) {
 			CAM_ERR(CAM_ISP,
-				"Can not acquire ife csid rdi resrouce!");
+				"Can not acquire ife csid ipp resource");
 			goto err;
 		}
-		csid_res->hw_res[1] = csid_acquire.node_res;
 
-		CAM_DBG(CAM_ISP,
-			"acquired csid(%d)right ipp resrouce successfully!", j);
-
+		csid_res->parent = cid_res;
+		cid_res->child[cid_res->num_children++] = csid_res;
 	}
-	csid_res->parent = &ife_ctx->res_list_ife_in;
-	ife_ctx->res_list_ife_in.child[
-		ife_ctx->res_list_ife_in.num_children++] = csid_res;
+
+	CAM_DBG(CAM_ISP, "acquire res %d", csid_acquire.res_id);
 
 	return 0;
 err:
@@ -862,7 +959,6 @@
 	uint32_t                 out_port_type)
 {
 	enum cam_ife_pix_path_res_id path_id;
-
 	switch (out_port_type) {
 	case CAM_ISP_IFE_OUT_RES_RDI_0:
 		path_id = CAM_IFE_PIX_PATH_RES_RDI_0;
@@ -882,6 +978,8 @@
 		break;
 	}
 
+	CAM_DBG(CAM_ISP, "out_port %d path_id %d", out_port_type, path_id);
+
 	return path_id;
 }
 
@@ -895,7 +993,8 @@
 
 	struct cam_ife_hw_mgr               *ife_hw_mgr;
 	struct cam_ife_hw_mgr_res           *csid_res;
-	struct cam_hw_intf                   *hw_intf;
+	struct cam_ife_hw_mgr_res           *cid_res;
+	struct cam_hw_intf                  *hw_intf;
 	struct cam_isp_out_port_info        *out_port;
 	struct cam_csid_hw_reserve_resource_args  csid_acquire;
 
@@ -909,7 +1008,7 @@
 		rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list,
 			&csid_res);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "No more free hw mgr resource!",
+			CAM_ERR(CAM_ISP, "No more free hw mgr resource",
 				__func__);
 			goto err;
 		}
@@ -920,6 +1019,7 @@
 		 * between the csid rdi type and out port rdi type
 		 */
 
+		memset(&csid_acquire, 0, sizeof(csid_acquire));
 		csid_acquire.res_id =
 			cam_ife_hw_mgr_get_ife_csid_rdi_res_type(
 				out_port->res_type);
@@ -930,39 +1030,58 @@
 		csid_acquire.out_port = out_port;
 		csid_acquire.sync_mode = CAM_ISP_HW_SYNC_NONE;
 
-		for (j = 0; j < CAM_IFE_CSID_HW_NUM_MAX; j++) {
-			if (!ife_hw_mgr->csid_devices[j])
+		list_for_each_entry(cid_res, &ife_ctx->res_list_ife_cid,
+			list) {
+			if (cid_res->res_id != cid_res_id)
 				continue;
 
-			hw_intf = ife_hw_mgr->csid_devices[j];
-			rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
-				&csid_acquire, sizeof(csid_acquire));
-			if (rc)
-				continue;
-			else
+			for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) {
+				if (!cid_res->hw_res[j])
+					continue;
+
+				csid_acquire.node_res = NULL;
+
+				hw_intf = ife_hw_mgr->csid_devices[
+					cid_res->hw_res[j]->hw_intf->hw_idx];
+				rc = hw_intf->hw_ops.reserve(hw_intf->hw_priv,
+					&csid_acquire, sizeof(csid_acquire));
+				if (rc) {
+					CAM_DBG(CAM_ISP,
+					 "CSID Path reserve failed hw=%d rc=%d",
+					 hw_intf->hw_idx, rc);
+					continue;
+				}
+
+				/* RDI does not need Dual ISP. Break */
 				break;
+			}
+
+			if (j == CAM_ISP_HW_SPLIT_MAX &&
+				csid_acquire.node_res == NULL) {
+				CAM_ERR(CAM_ISP,
+					"acquire csid rdi rsrc failed, cid %d",
+					cid_res_id);
+				goto err;
+			}
+
+			csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
+			csid_res->res_id = csid_acquire.res_id;
+			csid_res->is_dual_vfe = 0;
+			csid_res->hw_res[0] = csid_acquire.node_res;
+			csid_res->hw_res[1] = NULL;
+			CAM_DBG(CAM_ISP, "acquire res %d",
+				csid_acquire.res_id);
+			csid_res->parent = cid_res;
+			cid_res->child[cid_res->num_children++] =
+				csid_res;
+
+			/* Done with cid_res_id. Break */
+			break;
 		}
-
-		if (j == CAM_IFE_CSID_HW_NUM_MAX) {
-			CAM_ERR(CAM_ISP,
-				"Can not acquire ife csid rdi resrouce!");
-			goto err;
-		}
-
-		csid_res->res_type = CAM_ISP_RESOURCE_PIX_PATH;
-		csid_res->res_id = csid_acquire.res_id;
-		csid_res->is_dual_vfe = 0;
-		csid_res->hw_res[0] = csid_acquire.node_res;
-		csid_res->hw_res[1] = NULL;
-
-		csid_res->parent = &ife_ctx->res_list_ife_in;
-		ife_ctx->res_list_ife_in.child[
-			ife_ctx->res_list_ife_in.num_children++] = csid_res;
 	}
 
 	return 0;
 err:
-	/* resource resources at entry funciton */
 	return rc;
 }
 
@@ -978,7 +1097,7 @@
 		ife_ctx->res_list_ife_in.res_id = in_port->res_type;
 		ife_ctx->res_list_ife_in.is_dual_vfe = in_port->usage_type;
 	} else if (ife_ctx->res_list_ife_in.res_id != in_port->res_type) {
-		CAM_ERR(CAM_ISP, "No Free resource for this context!");
+		CAM_ERR(CAM_ISP, "No Free resource for this context");
 		goto err;
 	} else {
 		/* else do nothing */
@@ -1029,18 +1148,11 @@
 	struct cam_hw_intf                  *hw_intf;
 	struct cam_csid_hw_reserve_resource_args  csid_acquire;
 
-	/* no dual vfe for TPG */
-	if ((in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) &&
-		(in_port->usage_type != 0)) {
-		CAM_ERR(CAM_ISP, "No Dual VFE on TPG input!");
-		goto err;
-	}
-
 	ife_hw_mgr = ife_ctx->hw_mgr;
 
 	rc = cam_ife_hw_mgr_get_res(&ife_ctx->free_res_list, &cid_res);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "No more free hw mgr resource!");
+		CAM_ERR(CAM_ISP, "No more free hw mgr resource");
 		goto err;
 	}
 	cam_ife_hw_mgr_put_res(&ife_ctx->res_list_ife_cid, &cid_res);
@@ -1062,7 +1174,7 @@
 	}
 
 	if (i == CAM_IFE_CSID_HW_NUM_MAX || !csid_acquire.node_res) {
-		CAM_ERR(CAM_ISP, "Can not acquire ife csid rdi resrouce!");
+		CAM_ERR(CAM_ISP, "Can not acquire ife csid rdi resource");
 		goto err;
 	}
 
@@ -1093,7 +1205,7 @@
 
 		if (j == CAM_IFE_CSID_HW_NUM_MAX) {
 			CAM_ERR(CAM_ISP,
-				"Can not acquire ife csid rdi resrouce!");
+				"Can not acquire ife csid rdi resource");
 			goto err;
 		}
 		cid_res->hw_res[1] = csid_acquire.node_res;
@@ -1123,14 +1235,14 @@
 	/* get root node resource */
 	rc = cam_ife_hw_mgr_acquire_res_root(ife_ctx, in_port);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Can not acquire csid rx resource!");
+		CAM_ERR(CAM_ISP, "Can not acquire csid rx resource");
 		goto err;
 	}
 
 	/* get cid resource */
 	rc = cam_ife_mgr_acquire_cid_res(ife_ctx, in_port, &cid_res_id);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed!");
+		CAM_ERR(CAM_ISP, "Acquire IFE CID resource Failed");
 		goto err;
 	}
 
@@ -1138,7 +1250,7 @@
 		&pixel_count, &rdi_count);
 
 	if (!pixel_count && !rdi_count) {
-		CAM_ERR(CAM_ISP, "Error! no PIX or RDI resource");
+		CAM_ERR(CAM_ISP, "No PIX or RDI resource");
 		return -EINVAL;
 	}
 
@@ -1148,7 +1260,7 @@
 				cid_res_id);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
-				"Acquire IFE CSID IPP resource Failed!");
+				"Acquire IFE CSID IPP resource Failed");
 			goto err;
 		}
 	}
@@ -1159,7 +1271,7 @@
 			cid_res_id);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
-				"Acquire IFE CSID RDI resource Failed!");
+				"Acquire IFE CSID RDI resource Failed");
 			goto err;
 		}
 	}
@@ -1167,13 +1279,13 @@
 	/* get ife src resource */
 	rc = cam_ife_hw_mgr_acquire_res_ife_src(ife_ctx, in_port);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Acquire IFE SRC resource Failed!");
+		CAM_ERR(CAM_ISP, "Acquire IFE SRC resource Failed");
 		goto err;
 	}
 
 	rc = cam_ife_hw_mgr_acquire_res_ife_out(ife_ctx, in_port);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Acquire IFE OUT resource Failed!");
+		CAM_ERR(CAM_ISP, "Acquire IFE OUT resource Failed");
 		goto err;
 	}
 
@@ -1206,8 +1318,10 @@
 	struct cam_isp_in_port_info       *in_port = NULL;
 	struct cam_isp_resource           *isp_resource = NULL;
 	struct cam_cdm_acquire_data        cdm_acquire;
-	uint32_t                           num_pix_port = 0;
-	uint32_t                           num_rdi_port = 0;
+	uint32_t                           num_pix_port_per_in = 0;
+	uint32_t                           num_rdi_port_per_in = 0;
+	uint32_t                           total_pix_port = 0;
+	uint32_t                           total_rdi_port = 0;
 
 	CAM_DBG(CAM_ISP, "Enter...");
 
@@ -1219,7 +1333,7 @@
 	/* get the ife ctx */
 	rc = cam_ife_hw_mgr_get_ctx(&ife_hw_mgr->free_ctx_list, &ife_ctx);
 	if (rc || !ife_ctx) {
-		CAM_ERR(CAM_ISP, "Get ife hw context failed!");
+		CAM_ERR(CAM_ISP, "Get ife hw context failed");
 		goto err;
 	}
 
@@ -1245,16 +1359,17 @@
 
 	cdm_acquire.id = CAM_CDM_VIRTUAL;
 	cdm_acquire.cam_cdm_callback = cam_ife_cam_cdm_callback;
-	if (!cam_cdm_acquire(&cdm_acquire)) {
-		CAM_DBG(CAM_ISP, "Successfully acquired the CDM HW hdl=%x",
-			cdm_acquire.handle);
-		ife_ctx->cdm_handle = cdm_acquire.handle;
-		ife_ctx->cdm_ops = cdm_acquire.ops;
-	} else {
+	rc = cam_cdm_acquire(&cdm_acquire);
+	if (rc) {
 		CAM_ERR(CAM_ISP, "Failed to acquire the CDM HW");
-		goto err;
+		goto free_ctx;
 	}
 
+	CAM_DBG(CAM_ISP, "Successfully acquired the CDM HW hdl=%x",
+		cdm_acquire.handle);
+	ife_ctx->cdm_handle = cdm_acquire.handle;
+	ife_ctx->cdm_ops = cdm_acquire.ops;
+
 	isp_resource = (struct cam_isp_resource *)acquire_args->acquire_info;
 
 	/* acquire HW resources */
@@ -1271,7 +1386,10 @@
 			isp_resource[i].length);
 		if (in_port > 0) {
 			rc = cam_ife_mgr_acquire_hw_for_ctx(ife_ctx, in_port,
-				&num_pix_port, &num_rdi_port);
+				&num_pix_port_per_in, &num_rdi_port_per_in);
+			total_pix_port += num_pix_port_per_in;
+			total_rdi_port += num_rdi_port_per_in;
+
 			kfree(in_port);
 			if (rc) {
 				CAM_ERR(CAM_ISP, "can not acquire resource");
@@ -1279,7 +1397,7 @@
 			}
 		} else {
 			CAM_ERR(CAM_ISP,
-				"copy from user failed with in_port = %pK",
+				"Copy from user failed with in_port = %pK",
 				in_port);
 			rc = -EFAULT;
 			goto free_res;
@@ -1287,7 +1405,7 @@
 	}
 
 	/* Check whether context has only RDI resource */
-	if (!num_pix_port) {
+	if (!total_pix_port) {
 		ife_ctx->is_rdi_only_context = 1;
 		CAM_DBG(CAM_ISP, "RDI only context");
 	}
@@ -1295,8 +1413,8 @@
 	/* Process base info */
 	rc = cam_ife_mgr_process_base_info(ife_ctx);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Error process) base info!");
-		return -EINVAL;
+		CAM_ERR(CAM_ISP, "Process base info failed");
+		goto free_res;
 	}
 
 	acquire_args->ctxt_to_hw_map = ife_ctx;
@@ -1304,14 +1422,16 @@
 
 	cam_ife_hw_mgr_put_ctx(&ife_hw_mgr->used_ctx_list, &ife_ctx);
 
-	CAM_DBG(CAM_ISP, "Exit...(success)!");
+	CAM_DBG(CAM_ISP, "Exit...(success)");
 
 	return 0;
 free_res:
 	cam_ife_hw_mgr_release_hw_for_ctx(ife_ctx);
+	cam_cdm_release(ife_ctx->cdm_handle);
+free_ctx:
 	cam_ife_hw_mgr_put_ctx(&ife_hw_mgr->free_ctx_list, &ife_ctx);
 err:
-	CAM_DBG(CAM_ISP, "Exit...(rc=%d)!", rc);
+	CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc);
 	return rc;
 }
 
@@ -1334,14 +1454,16 @@
 	cfg = config_hw_args;
 	ctx = (struct cam_ife_hw_mgr_ctx *)cfg->ctxt_to_hw_map;
 	if (!ctx) {
-		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
 	if (!ctx->ctx_in_use || !ctx->cdm_cmd) {
-		CAM_ERR(CAM_ISP, "Invalid context parameters !");
+		CAM_ERR(CAM_ISP, "Invalid context parameters");
 		return -EPERM;
 	}
+	if (atomic_read(&ctx->overflow_pending))
+		return -EINVAL;
 
 	CAM_DBG(CAM_ISP, "Enter ctx id:%d num_hw_upd_entries %d",
 		ctx->ctx_index, cfg->num_hw_update_entries);
@@ -1373,8 +1495,7 @@
 	return rc;
 }
 
-static int cam_ife_mgr_stop_hw_in_overflow(void *hw_mgr_priv,
-		void *stop_hw_args)
+static int cam_ife_mgr_stop_hw_in_overflow(void *stop_hw_args)
 {
 	int                               rc        = 0;
 	struct cam_hw_stop_args          *stop_args = stop_hw_args;
@@ -1382,22 +1503,21 @@
 	struct cam_ife_hw_mgr_ctx        *ctx;
 	uint32_t                          i, master_base_idx = 0;
 
-	if (!hw_mgr_priv || !stop_hw_args) {
+	if (!stop_hw_args) {
 		CAM_ERR(CAM_ISP, "Invalid arguments");
 		return -EINVAL;
 	}
 	ctx = (struct cam_ife_hw_mgr_ctx *)stop_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
 	CAM_DBG(CAM_ISP, "Enter...ctx id:%d",
 		ctx->ctx_index);
 
-	/* stop resource will remove the irq mask from the hardware */
 	if (!ctx->num_base) {
-		CAM_ERR(CAM_ISP, "error number of bases are zero");
+		CAM_ERR(CAM_ISP, "Number of bases are zero");
 		return -EINVAL;
 	}
 
@@ -1409,17 +1529,13 @@
 		}
 	}
 
-	/*
-	 * if Context does not have PIX resources and has only RDI resource
-	 * then take the first base index.
-	 */
-
 	if (i == ctx->num_base)
 		master_base_idx = ctx->base[0].idx;
 
+
 	/* stop the master CIDs first */
 	cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_cid,
-			master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
+		master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
 
 	/* stop rest of the CIDs  */
 	for (i = 0; i < ctx->num_base; i++) {
@@ -1431,7 +1547,7 @@
 
 	/* stop the master CSID path first */
 	cam_ife_mgr_csid_stop_hw(ctx, &ctx->res_list_ife_csid,
-			master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
+		master_base_idx, CAM_CSID_HALT_IMMEDIATELY);
 
 	/* Stop rest of the CSID paths  */
 	for (i = 0; i < ctx->num_base; i++) {
@@ -1451,8 +1567,9 @@
 	for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++)
 		cam_ife_hw_mgr_stop_hw_res(&ctx->res_list_ife_out[i]);
 
-	/* update vote bandwidth should be done at the HW layer */
 
+	/* Stop tasklet for context */
+	cam_tasklet_stop(ctx->common.tasklet_info);
 	CAM_DBG(CAM_ISP, "Exit...ctx id:%d rc :%d",
 		ctx->ctx_index, rc);
 
@@ -1474,7 +1591,7 @@
 	}
 	ctx = (struct cam_ife_hw_mgr_ctx *)stop_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
@@ -1484,7 +1601,7 @@
 	/* Note:stop resource will remove the irq mask from the hardware */
 
 	if (!ctx->num_base) {
-		CAM_ERR(CAM_ISP, "error number of bases are zero");
+		CAM_ERR(CAM_ISP, "number of bases are zero");
 		return -EINVAL;
 	}
 
@@ -1568,43 +1685,41 @@
 
 	CAM_DBG(CAM_ISP, "Exit...ctx id:%d rc :%d", ctx->ctx_index, rc);
 
+	mutex_lock(&g_ife_hw_mgr.ctx_mutex);
+	if (!atomic_dec_return(&g_ife_hw_mgr.active_ctx_cnt)) {
+		rc = cam_ife_notify_safe_lut_scm(CAM_IFE_SAFE_DISABLE);
+		if (rc) {
+			CAM_ERR(CAM_ISP,
+				"SAFE SCM call failed:Check TZ/HYP dependency");
+			rc = 0;
+		}
+	}
+	mutex_unlock(&g_ife_hw_mgr.ctx_mutex);
+
 	return rc;
 }
 
-static int cam_ife_mgr_reset_hw(struct cam_ife_hw_mgr *hw_mgr,
+static int cam_ife_mgr_reset_vfe_hw(struct cam_ife_hw_mgr *hw_mgr,
 			uint32_t hw_idx)
 {
 	uint32_t i = 0;
-	struct cam_hw_intf             *csid_hw_intf;
 	struct cam_hw_intf             *vfe_hw_intf;
-	struct cam_csid_reset_cfg_args  csid_reset_args;
+	uint32_t vfe_reset_type;
 
 	if (!hw_mgr) {
 		CAM_DBG(CAM_ISP, "Invalid arguments");
 		return -EINVAL;
 	}
-
-	/* Reset IFE CSID HW */
-	csid_reset_args.reset_type = CAM_IFE_CSID_RESET_GLOBAL;
-
-	for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
-		if (hw_idx != hw_mgr->csid_devices[i]->hw_idx)
-			continue;
-
-		csid_hw_intf = hw_mgr->csid_devices[i];
-		csid_hw_intf->hw_ops.reset(csid_hw_intf->hw_priv,
-			&csid_reset_args,
-			sizeof(struct cam_csid_reset_cfg_args));
-		break;
-	}
-
 	/* Reset VFE HW*/
+	vfe_reset_type = CAM_VFE_HW_RESET_HW;
+
 	for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) {
 		if (hw_idx != hw_mgr->ife_devices[i]->hw_idx)
 			continue;
 		CAM_DBG(CAM_ISP, "VFE (id = %d) reset", hw_idx);
 		vfe_hw_intf = hw_mgr->ife_devices[i];
-		vfe_hw_intf->hw_ops.reset(vfe_hw_intf->hw_priv, NULL, 0);
+		vfe_hw_intf->hw_ops.reset(vfe_hw_intf->hw_priv,
+			&vfe_reset_type, sizeof(vfe_reset_type));
 		break;
 	}
 
@@ -1612,8 +1727,7 @@
 	return 0;
 }
 
-static int cam_ife_mgr_restart_hw(void *hw_mgr_priv,
-		void *start_hw_args)
+static int cam_ife_mgr_restart_hw(void *start_hw_args)
 {
 	int                               rc = -1;
 	struct cam_hw_start_args         *start_args = start_hw_args;
@@ -1621,25 +1735,27 @@
 	struct cam_ife_hw_mgr_res        *hw_mgr_res;
 	uint32_t                          i;
 
-	if (!hw_mgr_priv || !start_hw_args) {
+	if (!start_hw_args) {
 		CAM_ERR(CAM_ISP, "Invalid arguments");
 		return -EINVAL;
 	}
 
 	ctx = (struct cam_ife_hw_mgr_ctx *)start_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
-	CAM_DBG(CAM_ISP, "Enter... ctx id:%d", ctx->ctx_index);
-
 	CAM_DBG(CAM_ISP, "START IFE OUT ... in ctx id:%d", ctx->ctx_index);
+
+	cam_tasklet_start(ctx->common.tasklet_info);
+
 	/* start the IFE out devices */
 	for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
-		rc = cam_ife_hw_mgr_start_hw_res(&ctx->res_list_ife_out[i]);
+		rc = cam_ife_hw_mgr_start_hw_res(
+			&ctx->res_list_ife_out[i], ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)!", i);
+			CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)", i);
 			goto err;
 		}
 	}
@@ -1647,9 +1763,9 @@
 	CAM_DBG(CAM_ISP, "START IFE SRC ... in ctx id:%d", ctx->ctx_index);
 	/* Start the IFE mux in devices */
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
+		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)!",
+			CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1658,31 +1774,21 @@
 	CAM_DBG(CAM_ISP, "START CSID HW ... in ctx id:%d", ctx->ctx_index);
 	/* Start the IFE CSID HW devices */
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
+		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)!",
+			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
 	}
 
 	CAM_DBG(CAM_ISP, "START CID SRC ... in ctx id:%d", ctx->ctx_index);
-	/* Start the IFE CID HW devices */
-	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
-		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)!",
-				 hw_mgr_res->res_id);
-			goto err;
-		}
-	}
-
 	/* Start IFE root node: do nothing */
 	CAM_DBG(CAM_ISP, "Exit...(success)");
 	return 0;
 
 err:
-	cam_ife_mgr_stop_hw(hw_mgr_priv, start_hw_args);
+	cam_ife_mgr_stop_hw_in_overflow(start_hw_args);
 	CAM_DBG(CAM_ISP, "Exit...(rc=%d)", rc);
 	return rc;
 }
@@ -1702,7 +1808,7 @@
 
 	ctx = (struct cam_ife_hw_mgr_ctx *)start_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
@@ -1713,6 +1819,16 @@
 
 	cam_tasklet_start(ctx->common.tasklet_info);
 
+	/* set current csid debug information to CSID HW */
+	for (i = 0; i < CAM_IFE_CSID_HW_NUM_MAX; i++) {
+		if (g_ife_hw_mgr.csid_devices[i])
+			rc = g_ife_hw_mgr.csid_devices[i]->hw_ops.process_cmd(
+				g_ife_hw_mgr.csid_devices[i]->hw_priv,
+				CAM_IFE_CSID_SET_CSID_DEBUG,
+				&g_ife_hw_mgr.debug_cfg.csid_debug,
+				sizeof(g_ife_hw_mgr.debug_cfg.csid_debug));
+	}
+
 	/* INIT IFE Root: do nothing */
 
 	CAM_DBG(CAM_ISP, "INIT IFE CID ... in ctx id:%d",
@@ -1721,7 +1837,7 @@
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) {
 		rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not INIT IFE CID.(id :%d)!",
+			CAM_ERR(CAM_ISP, "Can not INIT IFE CID(id :%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1735,7 +1851,7 @@
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) {
 		rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not INIT IFE CSID.(id :%d)!",
+			CAM_ERR(CAM_ISP, "Can not INIT IFE CSID(id :%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1747,7 +1863,7 @@
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
 		rc = cam_ife_hw_mgr_init_hw_res(hw_mgr_res);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not INIT IFE SRC (%d)!",
+			CAM_ERR(CAM_ISP, "Can not INIT IFE SRC (%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1760,16 +1876,27 @@
 	for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
 		rc = cam_ife_hw_mgr_init_hw_res(&ctx->res_list_ife_out[i]);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not INIT IFE OUT (%d)!",
+			CAM_ERR(CAM_ISP, "Can not INIT IFE OUT (%d)",
 				 ctx->res_list_ife_out[i].res_id);
 			goto err;
 		}
 	}
 
+	mutex_lock(&g_ife_hw_mgr.ctx_mutex);
+	if (!atomic_fetch_inc(&g_ife_hw_mgr.active_ctx_cnt)) {
+		rc = cam_ife_notify_safe_lut_scm(CAM_IFE_SAFE_ENABLE);
+		if (rc) {
+			CAM_ERR(CAM_ISP,
+				"SAFE SCM call failed:Check TZ/HYP dependency");
+			rc = -1;
+		}
+	}
+	mutex_unlock(&g_ife_hw_mgr.ctx_mutex);
+
 	CAM_DBG(CAM_ISP, "start cdm interface");
 	rc = cam_cdm_stream_on(ctx->cdm_handle);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Can not start cdm (%d)!",
+		CAM_ERR(CAM_ISP, "Can not start cdm (%d)",
 			 ctx->cdm_handle);
 		goto err;
 	}
@@ -1786,9 +1913,10 @@
 		ctx->ctx_index);
 	/* start the IFE out devices */
 	for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
-		rc = cam_ife_hw_mgr_start_hw_res(&ctx->res_list_ife_out[i]);
+		rc = cam_ife_hw_mgr_start_hw_res(
+			&ctx->res_list_ife_out[i], ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)!",
+			CAM_ERR(CAM_ISP, "Can not start IFE OUT (%d)",
 				 i);
 			goto err;
 		}
@@ -1798,9 +1926,9 @@
 		ctx->ctx_index);
 	/* Start the IFE mux in devices */
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
+		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)!",
+			CAM_ERR(CAM_ISP, "Can not start IFE MUX (%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1810,9 +1938,9 @@
 		ctx->ctx_index);
 	/* Start the IFE CSID HW devices */
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
+		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)!",
+			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)",
 				 hw_mgr_res->res_id);
 			goto err;
 		}
@@ -1822,10 +1950,10 @@
 		ctx->ctx_index);
 	/* Start the IFE CID HW devices */
 	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_cid, list) {
-		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res);
+		rc = cam_ife_hw_mgr_start_hw_res(hw_mgr_res, ctx);
 		if (rc) {
-			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)!",
-				 hw_mgr_res->res_id);
+			CAM_ERR(CAM_ISP, "Can not start IFE CSID (%d)",
+				hw_mgr_res->res_id);
 			goto err;
 		}
 	}
@@ -1864,7 +1992,7 @@
 
 	ctx = (struct cam_ife_hw_mgr_ctx *)release_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Invalid context is used");
 		return -EPERM;
 	}
 
@@ -1890,6 +2018,331 @@
 	return rc;
 }
 
+static int cam_isp_blob_hfr_update(
+	uint32_t                               blob_type,
+	struct cam_isp_generic_blob_info      *blob_info,
+	struct cam_isp_resource_hfr_config    *hfr_config,
+	struct cam_hw_prepare_update_args     *prepare)
+{
+	struct cam_isp_port_hfr_config        *port_hfr_config;
+	struct cam_kmd_buf_info               *kmd_buf_info;
+	struct cam_ife_hw_mgr_ctx             *ctx = NULL;
+	struct cam_ife_hw_mgr_res             *hw_mgr_res;
+	uint32_t                               res_id_out, i;
+	uint32_t                               total_used_bytes = 0;
+	uint32_t                               kmd_buf_remain_size;
+	uint32_t                              *cmd_buf_addr;
+	uint32_t                               bytes_used = 0;
+	int                                    num_ent, rc = 0;
+
+	ctx = prepare->ctxt_to_hw_map;
+	CAM_DBG(CAM_ISP, "num_ports= %d",
+		hfr_config->num_ports);
+
+	/* Max one hw entries required for hfr config update */
+	if (prepare->num_hw_update_entries + 1 >=
+			prepare->max_hw_update_entries) {
+		CAM_ERR(CAM_ISP, "Insufficient  HW entries :%d %d",
+			prepare->num_hw_update_entries,
+			prepare->max_hw_update_entries);
+		return -EINVAL;
+	}
+
+	kmd_buf_info = blob_info->kmd_buf_info;
+	for (i = 0; i < hfr_config->num_ports; i++) {
+		port_hfr_config = &hfr_config->port_hfr_config[i];
+		res_id_out = port_hfr_config->resource_type & 0xFF;
+
+		CAM_DBG(CAM_ISP, "hfr config idx %d, type=%d", i,
+			res_id_out);
+
+		if (res_id_out >= CAM_IFE_HW_OUT_RES_MAX) {
+			CAM_ERR(CAM_ISP, "invalid out restype:%x",
+				port_hfr_config->resource_type);
+			return -EINVAL;
+		}
+
+		if ((kmd_buf_info->used_bytes
+			+ total_used_bytes) < kmd_buf_info->size) {
+			kmd_buf_remain_size = kmd_buf_info->size -
+			(kmd_buf_info->used_bytes +
+			total_used_bytes);
+		} else {
+			CAM_ERR(CAM_ISP,
+			"no free kmd memory for base %d",
+			blob_info->base_info->idx);
+			rc = -ENOMEM;
+			return rc;
+		}
+
+		cmd_buf_addr = kmd_buf_info->cpu_addr +
+			kmd_buf_info->used_bytes/4 +
+			total_used_bytes/4;
+		hw_mgr_res = &ctx->res_list_ife_out[res_id_out];
+
+		rc = cam_isp_add_cmd_buf_update(
+			hw_mgr_res, blob_type,
+			blob_type_hw_cmd_map[blob_type],
+			blob_info->base_info->idx,
+			(void *)cmd_buf_addr,
+			kmd_buf_remain_size,
+			(void *)port_hfr_config,
+			&bytes_used);
+		if (rc < 0) {
+			CAM_ERR(CAM_ISP,
+				"Failed cmd_update, base_idx=%d, rc=%d",
+				blob_info->base_info->idx, bytes_used);
+			return rc;
+		}
+
+		total_used_bytes += bytes_used;
+	}
+
+	if (total_used_bytes) {
+		/* Update the HW entries */
+		num_ent = prepare->num_hw_update_entries;
+		prepare->hw_update_entries[num_ent].handle =
+			kmd_buf_info->handle;
+		prepare->hw_update_entries[num_ent].len = total_used_bytes;
+		prepare->hw_update_entries[num_ent].offset =
+			kmd_buf_info->offset;
+		num_ent++;
+
+		kmd_buf_info->used_bytes += total_used_bytes;
+		kmd_buf_info->offset     += total_used_bytes;
+		prepare->num_hw_update_entries = num_ent;
+	}
+
+	return rc;
+}
+
+static int cam_isp_blob_clock_update(
+	uint32_t                               blob_type,
+	struct cam_isp_generic_blob_info      *blob_info,
+	struct cam_isp_clock_config           *clock_config,
+	struct cam_hw_prepare_update_args     *prepare)
+{
+	struct cam_ife_hw_mgr_ctx             *ctx = NULL;
+	struct cam_ife_hw_mgr_res             *hw_mgr_res;
+	struct cam_hw_intf                    *hw_intf;
+	struct cam_vfe_clock_update_args       clock_upd_args;
+	uint64_t                               clk_rate = 0;
+	int                                    rc = -EINVAL;
+	uint32_t                               i;
+	uint32_t                               j;
+
+	ctx = prepare->ctxt_to_hw_map;
+
+	CAM_DBG(CAM_ISP,
+		"usage=%u left_clk= %lu right_clk=%lu",
+		clock_config->usage_type,
+		clock_config->left_pix_hz,
+		clock_config->right_pix_hz);
+
+	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
+		for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+			clk_rate = 0;
+			if (!hw_mgr_res->hw_res[i])
+				continue;
+
+			if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
+				if (i == CAM_ISP_HW_SPLIT_LEFT)
+					clk_rate =
+						clock_config->left_pix_hz;
+				else
+					clk_rate =
+						clock_config->right_pix_hz;
+			else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0)
+					&& (hw_mgr_res->res_id <=
+					CAM_ISP_HW_VFE_IN_RDI3))
+				for (j = 0; j < clock_config->num_rdi; j++)
+					clk_rate = max(clock_config->rdi_hz[j],
+						clk_rate);
+			else
+				if (hw_mgr_res->hw_res[i]) {
+					CAM_ERR(CAM_ISP, "Invalid res_id %u",
+						hw_mgr_res->res_id);
+					rc = -EINVAL;
+					return rc;
+				}
+
+			hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+			if (hw_intf && hw_intf->hw_ops.process_cmd) {
+				clock_upd_args.node_res =
+					hw_mgr_res->hw_res[i];
+				CAM_DBG(CAM_ISP,
+				"res_id=%u i= %d clk=%llu\n",
+				hw_mgr_res->res_id, i, clk_rate);
+
+				clock_upd_args.clk_rate = clk_rate;
+
+				rc = hw_intf->hw_ops.process_cmd(
+					hw_intf->hw_priv,
+					CAM_ISP_HW_CMD_CLOCK_UPDATE,
+					&clock_upd_args,
+					sizeof(
+					struct cam_vfe_clock_update_args));
+				if (rc)
+					CAM_ERR(CAM_ISP, "Clock Update failed");
+			} else
+				CAM_WARN(CAM_ISP, "NULL hw_intf!");
+		}
+	}
+
+	return rc;
+}
+
+static int cam_isp_blob_bw_update(
+	uint32_t                               blob_type,
+	struct cam_isp_generic_blob_info      *blob_info,
+	struct cam_isp_bw_config              *bw_config,
+	struct cam_hw_prepare_update_args     *prepare)
+{
+	struct cam_ife_hw_mgr_ctx             *ctx = NULL;
+	struct cam_ife_hw_mgr_res             *hw_mgr_res;
+	struct cam_hw_intf                    *hw_intf;
+	struct cam_vfe_bw_update_args          bw_upd_args;
+	uint64_t                               cam_bw_bps = 0;
+	uint64_t                               ext_bw_bps = 0;
+	int                                    rc = -EINVAL;
+	uint32_t                               i;
+
+	ctx = prepare->ctxt_to_hw_map;
+
+	CAM_DBG(CAM_ISP,
+		"usage=%u left cam_bw_bps=%llu ext_bw_bps=%llu\n"
+		"right cam_bw_bps=%llu ext_bw_bps=%llu",
+		bw_config->usage_type,
+		bw_config->left_pix_vote.cam_bw_bps,
+		bw_config->left_pix_vote.ext_bw_bps,
+		bw_config->right_pix_vote.cam_bw_bps,
+		bw_config->right_pix_vote.ext_bw_bps);
+
+	list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_src, list) {
+		for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+			if (!hw_mgr_res->hw_res[i])
+				continue;
+
+			if (hw_mgr_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
+				if (i == CAM_ISP_HW_SPLIT_LEFT) {
+					cam_bw_bps =
+					bw_config->left_pix_vote.cam_bw_bps;
+					ext_bw_bps =
+					bw_config->left_pix_vote.ext_bw_bps;
+				} else {
+					cam_bw_bps =
+					bw_config->right_pix_vote.cam_bw_bps;
+					ext_bw_bps =
+					bw_config->right_pix_vote.ext_bw_bps;
+				}
+			else if ((hw_mgr_res->res_id >= CAM_ISP_HW_VFE_IN_RDI0)
+						&& (hw_mgr_res->res_id <=
+						CAM_ISP_HW_VFE_IN_RDI3)) {
+				uint32_t idx = hw_mgr_res->res_id -
+						CAM_ISP_HW_VFE_IN_RDI0;
+				if (idx >= bw_config->num_rdi)
+					continue;
+
+				cam_bw_bps =
+					bw_config->rdi_vote[idx].cam_bw_bps;
+				ext_bw_bps =
+					bw_config->rdi_vote[idx].ext_bw_bps;
+			} else
+				if (hw_mgr_res->hw_res[i]) {
+					CAM_ERR(CAM_ISP, "Invalid res_id %u",
+						hw_mgr_res->res_id);
+					rc = -EINVAL;
+					return rc;
+				}
+
+			hw_intf = hw_mgr_res->hw_res[i]->hw_intf;
+			if (hw_intf && hw_intf->hw_ops.process_cmd) {
+				bw_upd_args.node_res =
+					hw_mgr_res->hw_res[i];
+
+				bw_upd_args.camnoc_bw_bytes = cam_bw_bps;
+				bw_upd_args.external_bw_bytes = ext_bw_bps;
+
+				rc = hw_intf->hw_ops.process_cmd(
+					hw_intf->hw_priv,
+					CAM_ISP_HW_CMD_BW_UPDATE,
+					&bw_upd_args,
+					sizeof(struct cam_vfe_bw_update_args));
+				if (rc)
+					CAM_ERR(CAM_ISP, "BW Update failed");
+			} else
+				CAM_WARN(CAM_ISP, "NULL hw_intf!");
+		}
+	}
+
+	return rc;
+}
+
+static int cam_isp_packet_generic_blob_handler(void *user_data,
+	uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
+{
+	int rc = 0;
+	struct cam_isp_generic_blob_info  *blob_info = user_data;
+	struct cam_hw_prepare_update_args *prepare = NULL;
+
+	if (!blob_data || (blob_size == 0) || !blob_info) {
+		CAM_ERR(CAM_ISP, "Invalid info blob %pK %d prepare %pK",
+			blob_data, blob_size, prepare);
+		return -EINVAL;
+	}
+
+	if (blob_type >= CAM_ISP_GENERIC_BLOB_TYPE_MAX) {
+		CAM_ERR(CAM_ISP, "Invalid Blob Type %d Max %d", blob_type,
+			CAM_ISP_GENERIC_BLOB_TYPE_MAX);
+		return -EINVAL;
+	}
+
+	prepare = blob_info->prepare;
+	if (!prepare) {
+		CAM_ERR(CAM_ISP, "Failed. prepare is NULL, blob_type %d",
+			blob_type);
+		return -EINVAL;
+	}
+
+	switch (blob_type) {
+	case CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG: {
+		struct cam_isp_resource_hfr_config    *hfr_config =
+			(struct cam_isp_resource_hfr_config *)blob_data;
+
+		rc = cam_isp_blob_hfr_update(blob_type, blob_info,
+			hfr_config, prepare);
+		if (rc)
+			CAM_ERR(CAM_ISP, "HFR Update Failed");
+	}
+		break;
+	case CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG: {
+		struct cam_isp_clock_config    *clock_config =
+			(struct cam_isp_clock_config *)blob_data;
+
+		rc = cam_isp_blob_clock_update(blob_type, blob_info,
+			clock_config, prepare);
+		if (rc)
+			CAM_ERR(CAM_ISP, "Clock Update Failed");
+	}
+		break;
+	case CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG: {
+		struct cam_isp_bw_config    *bw_config =
+			(struct cam_isp_bw_config *)blob_data;
+
+		rc = cam_isp_blob_bw_update(blob_type, blob_info,
+			bw_config, prepare);
+		if (rc)
+			CAM_ERR(CAM_ISP, "Bandwidth Update Failed");
+	}
+		break;
+	default:
+		CAM_WARN(CAM_ISP, "Invalid blob type %d", blob_type);
+		break;
+	}
+
+	return rc;
+}
+
 static int cam_ife_mgr_prepare_hw_update(void *hw_mgr_priv,
 	void *prepare_hw_update_args)
 {
@@ -1901,7 +2354,6 @@
 	struct cam_kmd_buf_info           kmd_buf;
 	uint32_t                          i;
 	bool                              fill_fence = true;
-	struct cam_isp_generic_blob_info  blob_info;
 
 	if (!hw_mgr_priv || !prepare_hw_update_args) {
 		CAM_ERR(CAM_ISP, "Invalid args");
@@ -1923,20 +2375,13 @@
 		return rc;
 
 	rc = cam_packet_util_process_patches(prepare->packet,
-		hw_mgr->mgr_common.cmd_iommu_hdl);
+		hw_mgr->mgr_common.cmd_iommu_hdl,
+		hw_mgr->mgr_common.cmd_iommu_hdl_secure);
 	if (rc) {
 		CAM_ERR(CAM_ISP, "Patch ISP packet failed.");
 		return rc;
 	}
 
-	memset(&blob_info, 0x0, sizeof(struct cam_isp_generic_blob_info));
-	rc = cam_isp_process_generic_cmd_buffer(prepare, &blob_info);
-	if (rc) {
-		CAM_ERR(CAM_ISP, "Failed in generic blob cmd buffer, rc=%d",
-			rc);
-		goto end;
-	}
-
 	prepare->num_hw_update_entries = 0;
 	prepare->num_in_map_entries = 0;
 	prepare->num_out_map_entries = 0;
@@ -1957,8 +2402,10 @@
 
 		/* get command buffers */
 		if (ctx->base[i].split_id != CAM_ISP_HW_SPLIT_MAX) {
-			rc = cam_isp_add_command_buffers(prepare,
-				ctx->base[i].split_id);
+			rc = cam_isp_add_command_buffers(prepare, &kmd_buf,
+				&ctx->base[i],
+				cam_isp_packet_generic_blob_handler,
+				ctx->res_list_ife_out, CAM_IFE_HW_OUT_RES_MAX);
 			if (rc) {
 				CAM_ERR(CAM_ISP,
 					"Failed in add cmdbuf, i=%d, split_id=%d, rc=%d",
@@ -1967,21 +2414,9 @@
 			}
 		}
 
-		if (blob_info.hfr_config) {
-			rc = cam_isp_add_hfr_config_hw_update(
-				blob_info.hfr_config, prepare,
-				ctx->base[i].idx, &kmd_buf,
-				ctx->res_list_ife_out, CAM_IFE_HW_OUT_RES_MAX);
-			if (rc) {
-				CAM_ERR(CAM_ISP,
-					"Failed in hfr config, i=%d, rc=%d",
-					i, rc);
-				goto end;
-			}
-		}
-
 		/* get IO buffers */
 		rc = cam_isp_add_io_buffers(hw_mgr->mgr_common.img_iommu_hdl,
+			hw_mgr->mgr_common.img_iommu_hdl_secure,
 			prepare, ctx->base[i].idx,
 			&kmd_buf, ctx->res_list_ife_out,
 			CAM_IFE_HW_OUT_RES_MAX, fill_fence);
@@ -2005,7 +2440,7 @@
 	 * of op_code has some difference from KMD.
 	 */
 	if (((prepare->packet->header.op_code + 1) & 0xF) ==
-					CAM_ISP_PACKET_INIT_DEV)
+		CAM_ISP_PACKET_INIT_DEV)
 		goto end;
 
 	/* add reg update commands */
@@ -2032,7 +2467,6 @@
 	}
 
 end:
-	kfree(blob_info.hfr_config);
 	return rc;
 }
 
@@ -2049,7 +2483,7 @@
 
 	ctx = (struct cam_ife_hw_mgr_ctx *)hw_cmd_args->ctxt_to_hw_map;
 	if (!ctx || !ctx->ctx_in_use) {
-		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used!");
+		CAM_ERR(CAM_ISP, "Fatal: Invalid context is used");
 		return -EPERM;
 	}
 
@@ -2117,7 +2551,7 @@
 	}
 end:
 	if (rc)
-		CAM_ERR(CAM_ISP, "error in getting sof time stamp");
+		CAM_ERR(CAM_ISP, "Getting sof time stamp failed");
 
 	return rc;
 }
@@ -2125,17 +2559,18 @@
 static int cam_ife_mgr_process_recovery_cb(void *priv, void *data)
 {
 	int32_t rc = 0;
-	struct cam_hw_event_recovery_data   *recovery_data = priv;
-	struct cam_hw_start_args     start_args;
-	struct cam_ife_hw_mgr   *ife_hw_mgr = NULL;
-	uint32_t   hw_mgr_priv;
-	uint32_t i = 0;
+	struct cam_hw_event_recovery_data   *recovery_data = data;
+	struct cam_hw_start_args             start_args;
+	struct cam_hw_stop_args              stop_args;
+	struct cam_ife_hw_mgr               *ife_hw_mgr = priv;
+	struct cam_ife_hw_mgr_res           *hw_mgr_res;
+	uint32_t                             i = 0;
 
 	uint32_t error_type = recovery_data->error_type;
 	struct cam_ife_hw_mgr_ctx        *ctx = NULL;
 
 	/* Here recovery is performed */
-	CAM_DBG(CAM_ISP, "Enter: ErrorType = %d", error_type);
+	CAM_DBG(CAM_ISP, "ErrorType = %d", error_type);
 
 	switch (error_type) {
 	case CAM_ISP_HW_ERROR_OVERFLOW:
@@ -2146,20 +2581,57 @@
 			kfree(recovery_data);
 			return 0;
 		}
+		/* stop resources here */
+		CAM_DBG(CAM_ISP, "STOP: Number of affected context: %d",
+			recovery_data->no_of_context);
+		for (i = 0; i < recovery_data->no_of_context; i++) {
+			stop_args.ctxt_to_hw_map =
+				recovery_data->affected_ctx[i];
+			rc = cam_ife_mgr_stop_hw_in_overflow(&stop_args);
+			if (rc) {
+				CAM_ERR(CAM_ISP, "CTX stop failed(%d)", rc);
+				return rc;
+			}
+		}
 
-		ctx = recovery_data->affected_ctx[0];
-		ife_hw_mgr = ctx->hw_mgr;
+		CAM_DBG(CAM_ISP, "RESET: CSID PATH");
+		for (i = 0; i < recovery_data->no_of_context; i++) {
+			ctx = recovery_data->affected_ctx[i];
+			list_for_each_entry(hw_mgr_res, &ctx->res_list_ife_csid,
+				list) {
+				rc = cam_ife_hw_mgr_reset_csid_res(hw_mgr_res);
+				if (rc) {
+					CAM_ERR(CAM_ISP, "Failed RESET (%d)",
+						hw_mgr_res->res_id);
+					return rc;
+				}
+			}
+		}
+
+		CAM_DBG(CAM_ISP, "RESET: Calling VFE reset");
 
 		for (i = 0; i < CAM_VFE_HW_NUM_MAX; i++) {
 			if (recovery_data->affected_core[i])
-				rc = cam_ife_mgr_reset_hw(ife_hw_mgr, i);
+				cam_ife_mgr_reset_vfe_hw(ife_hw_mgr, i);
 		}
 
+		CAM_DBG(CAM_ISP, "START: Number of affected context: %d",
+			recovery_data->no_of_context);
+
 		for (i = 0; i < recovery_data->no_of_context; i++) {
-			start_args.ctxt_to_hw_map =
-				recovery_data->affected_ctx[i];
-			rc = cam_ife_mgr_restart_hw(&hw_mgr_priv, &start_args);
+			ctx =  recovery_data->affected_ctx[i];
+			start_args.ctxt_to_hw_map = ctx;
+
+			atomic_set(&ctx->overflow_pending, 0);
+
+			rc = cam_ife_mgr_restart_hw(&start_args);
+			if (rc) {
+				CAM_ERR(CAM_ISP, "CTX start failed(%d)", rc);
+				return rc;
+			}
+			CAM_DBG(CAM_ISP, "Started resources rc (%d)", rc);
 		}
+		CAM_DBG(CAM_ISP, "Recovery Done rc (%d)", rc);
 
 		break;
 
@@ -2185,8 +2657,6 @@
 	struct crm_workq_task        *task = NULL;
 	struct cam_hw_event_recovery_data  *recovery_data = NULL;
 
-	return 0;
-
 	recovery_data = kzalloc(sizeof(struct cam_hw_event_recovery_data),
 		GFP_ATOMIC);
 	if (!recovery_data)
@@ -2205,7 +2675,9 @@
 	}
 
 	task->process_cb = &cam_ife_mgr_process_recovery_cb;
-	rc = cam_req_mgr_workq_enqueue_task(task, recovery_data,
+	task->payload = recovery_data;
+	rc = cam_req_mgr_workq_enqueue_task(task,
+		recovery_data->affected_ctx[0]->hw_mgr,
 		CRM_TASK_PRIORITY_0);
 
 	return rc;
@@ -2218,9 +2690,9 @@
  *      affected_core[]
  *  b. Return 0 i.e.SUCCESS
  */
-static int cam_ife_hw_mgr_match_hw_idx(
+static int cam_ife_hw_mgr_is_ctx_affected(
 	struct cam_ife_hw_mgr_ctx   *ife_hwr_mgr_ctx,
-	uint32_t *affected_core)
+	uint32_t *affected_core, uint32_t size)
 {
 
 	int32_t rc = -EPERM;
@@ -2230,22 +2702,25 @@
 
 	CAM_DBG(CAM_ISP, "Enter:max_idx = %d", max_idx);
 
-	while (i < max_idx) {
+	if ((max_idx >= CAM_IFE_HW_NUM_MAX) ||
+		(size > CAM_IFE_HW_NUM_MAX)) {
+		CAM_ERR(CAM_ISP, "invalid parameter = %d", max_idx);
+		return rc;
+	}
+
+	for (i = 0; i < max_idx; i++) {
 		if (affected_core[ife_hwr_mgr_ctx->base[i].idx])
 			rc = 0;
 		else {
 			ctx_affected_core_idx[j] = ife_hwr_mgr_ctx->base[i].idx;
 			j = j + 1;
 		}
-
-		i = i + 1;
 	}
 
 	if (rc == 0) {
 		while (j) {
 			if (affected_core[ctx_affected_core_idx[j-1]] != 1)
 				affected_core[ctx_affected_core_idx[j-1]] = 1;
-
 			j = j - 1;
 		}
 	}
@@ -2261,7 +2736,7 @@
  *  d. For any dual VFE context, if copanion VFE is also serving
  *     other context it should also notify the CRM with fatal error
  */
-static int  cam_ife_hw_mgr_handle_overflow(
+static int  cam_ife_hw_mgr_process_overflow(
 	struct cam_ife_hw_mgr_ctx   *curr_ife_hwr_mgr_ctx,
 	struct cam_isp_hw_error_event_data *error_event_data,
 	uint32_t curr_core_idx,
@@ -2271,12 +2746,10 @@
 	struct cam_ife_hw_mgr_ctx   *ife_hwr_mgr_ctx = NULL;
 	cam_hw_event_cb_func	         ife_hwr_irq_err_cb;
 	struct cam_ife_hw_mgr		*ife_hwr_mgr = NULL;
-	uint32_t                            hw_mgr_priv = 1;
 	struct cam_hw_stop_args          stop_args;
 	uint32_t i = 0;
 
 	CAM_DBG(CAM_ISP, "Enter");
-	return 0;
 
 	if (!recovery_data) {
 		CAM_ERR(CAM_ISP, "recovery_data parameter is NULL",
@@ -2297,9 +2770,12 @@
 		 * with this context
 		 */
 		CAM_DBG(CAM_ISP, "Calling match Hw idx");
-		if (cam_ife_hw_mgr_match_hw_idx(ife_hwr_mgr_ctx, affected_core))
+		if (cam_ife_hw_mgr_is_ctx_affected(ife_hwr_mgr_ctx,
+			affected_core, CAM_IFE_HW_NUM_MAX))
 			continue;
 
+		atomic_set(&ife_hwr_mgr_ctx->overflow_pending, 1);
+
 		ife_hwr_irq_err_cb =
 		ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_ERROR];
 
@@ -2313,16 +2789,13 @@
 				ife_hwr_mgr_ctx;
 
 		/*
-		 * Stop the hw resources associated with this context
-		 * and call the error callback. In the call back function
-		 * corresponding ISP context will update CRM about fatal Error
+		 * In the call back function corresponding ISP context
+		 * will update CRM about fatal Error
 		 */
-		if (!cam_ife_mgr_stop_hw_in_overflow(&hw_mgr_priv,
-			&stop_args)) {
-			CAM_DBG(CAM_ISP, "Calling Error handler CB");
-			ife_hwr_irq_err_cb(ife_hwr_mgr_ctx->common.cb_priv,
-				CAM_ISP_HW_EVENT_ERROR, error_event_data);
-		}
+
+		ife_hwr_irq_err_cb(ife_hwr_mgr_ctx->common.cb_priv,
+			CAM_ISP_HW_EVENT_ERROR, error_event_data);
+
 	}
 	/* fill the affected_core in recovery data */
 	for (i = 0; i < CAM_IFE_HW_NUM_MAX; i++) {
@@ -2334,11 +2807,85 @@
 	return 0;
 }
 
+static int cam_ife_hw_mgr_get_err_type(
+	void                              *handler_priv,
+	void                              *payload)
+{
+	struct cam_isp_resource_node         *hw_res_l = NULL;
+	struct cam_isp_resource_node         *hw_res_r = NULL;
+	struct cam_ife_hw_mgr_ctx            *ife_hwr_mgr_ctx;
+	struct cam_vfe_top_irq_evt_payload   *evt_payload;
+	struct cam_ife_hw_mgr_res            *isp_ife_camif_res = NULL;
+	uint32_t  status = 0;
+	uint32_t  core_idx;
+
+	ife_hwr_mgr_ctx = handler_priv;
+	evt_payload = payload;
+
+	if (!evt_payload) {
+		CAM_ERR(CAM_ISP, "No payload");
+		return IRQ_HANDLED;
+	}
+
+	core_idx = evt_payload->core_index;
+	evt_payload->evt_id = CAM_ISP_HW_EVENT_ERROR;
+
+	list_for_each_entry(isp_ife_camif_res,
+		&ife_hwr_mgr_ctx->res_list_ife_src, list) {
+
+		if ((isp_ife_camif_res->res_type ==
+			CAM_IFE_HW_MGR_RES_UNINIT) ||
+			(isp_ife_camif_res->res_id != CAM_ISP_HW_VFE_IN_CAMIF))
+			continue;
+
+		hw_res_l = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_LEFT];
+		hw_res_r = isp_ife_camif_res->hw_res[CAM_ISP_HW_SPLIT_RIGHT];
+
+		CAM_DBG(CAM_ISP, "is_dual_vfe ? = %d\n",
+			isp_ife_camif_res->is_dual_vfe);
+
+		/* ERROR check for Left VFE */
+		if (!hw_res_l) {
+			CAM_DBG(CAM_ISP, "VFE(L) Device is NULL");
+			break;
+		}
+
+		CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx,
+			hw_res_l->hw_intf->hw_idx);
+
+		if (core_idx == hw_res_l->hw_intf->hw_idx) {
+			status = hw_res_l->bottom_half_handler(
+				hw_res_l, evt_payload);
+		}
+
+		if (status)
+			break;
+
+		/* ERROR check for Right  VFE */
+		if (!hw_res_r) {
+			CAM_DBG(CAM_ISP, "VFE(R) Device is NULL");
+			continue;
+		}
+		CAM_DBG(CAM_ISP, "core id= %d, HW id %d", core_idx,
+			hw_res_r->hw_intf->hw_idx);
+
+		if (core_idx == hw_res_r->hw_intf->hw_idx) {
+			status = hw_res_r->bottom_half_handler(
+				hw_res_r, evt_payload);
+		}
+
+		if (status)
+			break;
+	}
+	CAM_DBG(CAM_ISP, "Exit (status = %d)!", status);
+	return status;
+}
+
 static int  cam_ife_hw_mgr_handle_camif_error(
 	void                              *handler_priv,
 	void                              *payload)
 {
-	int32_t  rc = 0;
+	int32_t  error_status = CAM_ISP_HW_ERROR_NONE;
 	uint32_t core_idx;
 	struct cam_ife_hw_mgr_ctx               *ife_hwr_mgr_ctx;
 	struct cam_vfe_top_irq_evt_payload      *evt_payload;
@@ -2349,17 +2896,22 @@
 	evt_payload = payload;
 	core_idx = evt_payload->core_index;
 
-	rc = evt_payload->error_type;
-	CAM_DBG(CAM_ISP, "Enter: error_type (%d)", evt_payload->error_type);
-	switch (evt_payload->error_type) {
+	error_status = cam_ife_hw_mgr_get_err_type(ife_hwr_mgr_ctx,
+		evt_payload);
+
+	if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+		return error_status;
+
+	switch (error_status) {
 	case CAM_ISP_HW_ERROR_OVERFLOW:
 	case CAM_ISP_HW_ERROR_P2I_ERROR:
 	case CAM_ISP_HW_ERROR_VIOLATION:
+		CAM_DBG(CAM_ISP, "Enter: error_type (%d)", error_status);
 
 		error_event_data.error_type =
 				CAM_ISP_HW_ERROR_OVERFLOW;
 
-		cam_ife_hw_mgr_handle_overflow(ife_hwr_mgr_ctx,
+		cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx,
 				&error_event_data,
 				core_idx,
 				&recovery_data);
@@ -2369,12 +2921,10 @@
 		cam_ife_hw_mgr_do_error_recovery(&recovery_data);
 		break;
 	default:
-		CAM_DBG(CAM_ISP, "None error. Error type (%d)",
-			evt_payload->error_type);
+		CAM_DBG(CAM_ISP, "None error (%d)", error_status);
 	}
 
-	CAM_DBG(CAM_ISP, "Exit (%d)", rc);
-	return rc;
+	return error_status;
 }
 
 /*
@@ -2439,6 +2989,8 @@
 				rup_status = hw_res->bottom_half_handler(
 					hw_res, evt_payload);
 			}
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
 
 			if (!rup_status) {
 				ife_hwr_irq_rup_cb(
@@ -2470,6 +3022,8 @@
 				rup_status = hw_res->bottom_half_handler(
 					hw_res, evt_payload);
 
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
 			if (!rup_status) {
 				/* Send the Reg update hw event */
 				ife_hwr_irq_rup_cb(
@@ -2504,7 +3058,7 @@
 	case CAM_ISP_HW_EVENT_SOF:
 		event_cnt = ife_hw_mgr_ctx->sof_cnt;
 		break;
-	case CAM_ISP_HW_EVENT_REG_UPDATE:
+	case CAM_ISP_HW_EVENT_EPOCH:
 		event_cnt = ife_hw_mgr_ctx->epoch_cnt;
 		break;
 	case CAM_ISP_HW_EVENT_EOF:
@@ -2524,8 +3078,10 @@
 		return rc;
 	}
 
-	if ((event_cnt[core_idx0] - event_cnt[core_idx1] > 1) ||
-		(event_cnt[core_idx1] - event_cnt[core_idx0] > 1)) {
+	if ((event_cnt[core_idx0] &&
+		(event_cnt[core_idx0] - event_cnt[core_idx1] > 1)) ||
+		(event_cnt[core_idx1] &&
+		(event_cnt[core_idx1] - event_cnt[core_idx0] > 1))) {
 
 		CAM_WARN(CAM_ISP,
 			"One of the VFE cound not generate hw event %d",
@@ -2589,6 +3145,9 @@
 			if (core_idx == hw_res_l->hw_intf->hw_idx) {
 				epoch_status = hw_res_l->bottom_half_handler(
 					hw_res_l, evt_payload);
+				if (atomic_read(
+					&ife_hwr_mgr_ctx->overflow_pending))
+					break;
 				if (!epoch_status)
 					ife_hwr_irq_epoch_cb(
 						ife_hwr_mgr_ctx->common.cb_priv,
@@ -2612,6 +3171,8 @@
 
 				if (!epoch_status)
 					ife_hwr_mgr_ctx->epoch_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			/* SOF check for Right side VFE */
@@ -2621,6 +3182,8 @@
 
 				if (!epoch_status)
 					ife_hwr_mgr_ctx->epoch_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			core_index0 = hw_res_l->hw_intf->hw_idx;
@@ -2632,6 +3195,8 @@
 					core_index1,
 					evt_payload->evt_id);
 
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
 			if (!rc)
 				ife_hwr_irq_epoch_cb(
 					ife_hwr_mgr_ctx->common.cb_priv,
@@ -2692,6 +3257,8 @@
 		if (core_idx == hw_res_l->hw_intf->hw_idx) {
 			sof_status = hw_res_l->bottom_half_handler(hw_res_l,
 				evt_payload);
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
 			if (!sof_status) {
 				cam_ife_mgr_cmd_get_sof_timestamp(
 					ife_hwr_mgr_ctx,
@@ -2723,6 +3290,8 @@
 				hw_res_l, evt_payload);
 			if (!sof_status)
 				ife_hwr_mgr_ctx->sof_cnt[core_idx]++;
+			else
+				break;
 		}
 
 		/* SOF check for Right side VFE */
@@ -2738,17 +3307,27 @@
 				evt_payload);
 			if (!sof_status)
 				ife_hwr_mgr_ctx->sof_cnt[core_idx]++;
+			else
+				break;
 		}
 
 		core_index0 = hw_res_l->hw_intf->hw_idx;
 		core_index1 = hw_res_r->hw_intf->hw_idx;
 
+		if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+			break;
+
 		rc = cam_ife_hw_mgr_check_irq_for_dual_vfe(ife_hwr_mgr_ctx,
 			core_index0, core_index1, evt_payload->evt_id);
 
-		if (!rc)
+		if (!rc) {
+			cam_ife_mgr_cmd_get_sof_timestamp(
+					ife_hwr_mgr_ctx,
+					&sof_done_event_data.timestamp);
+
 			ife_hwr_irq_sof_cb(ife_hwr_mgr_ctx->common.cb_priv,
 				CAM_ISP_HW_EVENT_SOF, &sof_done_event_data);
+		}
 
 		break;
 
@@ -2757,7 +3336,7 @@
 		break;
 	}
 
-	CAM_DBG(CAM_ISP, "Exit (sof_status = %d)!", sof_status);
+	CAM_DBG(CAM_ISP, "Exit (sof_status = %d)", sof_status);
 
 	return 0;
 }
@@ -2896,6 +3475,9 @@
 			if (core_idx == hw_res_l->hw_intf->hw_idx) {
 				eof_status = hw_res_l->bottom_half_handler(
 					hw_res_l, evt_payload);
+				if (atomic_read(
+					&ife_hwr_mgr_ctx->overflow_pending))
+					break;
 				if (!eof_status)
 					ife_hwr_irq_eof_cb(
 						ife_hwr_mgr_ctx->common.cb_priv,
@@ -2916,6 +3498,8 @@
 
 				if (!eof_status)
 					ife_hwr_mgr_ctx->eof_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			/* EOF check for Right side VFE */
@@ -2925,6 +3509,8 @@
 
 				if (!eof_status)
 					ife_hwr_mgr_ctx->eof_cnt[core_idx]++;
+				else
+					break;
 			}
 
 			core_index0 = hw_res_l->hw_intf->hw_idx;
@@ -2936,10 +3522,13 @@
 					core_index1,
 					evt_payload->evt_id);
 
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
+
 			if (!rc)
 				ife_hwr_irq_eof_cb(
 					ife_hwr_mgr_ctx->common.cb_priv,
-					CAM_ISP_HW_EVENT_EPOCH,
+					CAM_ISP_HW_EVENT_EOF,
 					&eof_done_event_data);
 
 			break;
@@ -2949,7 +3538,7 @@
 		}
 	}
 
-	CAM_DBG(CAM_ISP, "Exit (eof_status = %d)!", eof_status);
+	CAM_DBG(CAM_ISP, "Exit (eof_status = %d)", eof_status);
 
 	return 0;
 }
@@ -2980,6 +3569,8 @@
 	ife_hwr_irq_wm_done_cb =
 		ife_hwr_mgr_ctx->common.event_cb[CAM_ISP_HW_EVENT_DONE];
 
+	evt_payload->evt_id = CAM_ISP_HW_EVENT_DONE;
+
 	for (i = 0; i < CAM_IFE_HW_OUT_RES_MAX; i++) {
 		isp_ife_out_res = &ife_hwr_mgr_ctx->res_list_ife_out[i];
 
@@ -2999,6 +3590,8 @@
 			hw_res_l->hw_intf->hw_idx))
 			buf_done_status = hw_res_l->bottom_half_handler(
 				hw_res_l, evt_payload);
+		else
+			continue;
 
 		switch (buf_done_status) {
 		case CAM_VFE_IRQ_STATUS_ERR_COMP:
@@ -3034,6 +3627,8 @@
 			buf_done_event_data.resource_handle[0] =
 				isp_ife_out_res->res_id;
 
+			if (atomic_read(&ife_hwr_mgr_ctx->overflow_pending))
+				break;
 			/* Report for Successful buf_done event if any */
 			if (buf_done_event_data.num_handles > 0 &&
 				ife_hwr_irq_wm_done_cb) {
@@ -3071,7 +3666,7 @@
 	 * the affected context and any successful buf_done event is not
 	 * reported.
 	 */
-	rc = cam_ife_hw_mgr_handle_overflow(ife_hwr_mgr_ctx,
+	rc = cam_ife_hw_mgr_process_overflow(ife_hwr_mgr_ctx,
 		&error_event_data, evt_payload->core_index,
 		&recovery_data);
 
@@ -3080,7 +3675,7 @@
 	 * for the first phase, we are going to reset entire HW.
 	 */
 
-	CAM_DBG(CAM_ISP, "Exit (buf_done_status (Error) = %d)!",
+	CAM_DBG(CAM_ISP, "Exit buf_done_status Error = %d",
 		buf_done_status);
 	return rc;
 }
@@ -3098,7 +3693,8 @@
 	evt_payload = evt_payload_priv;
 	ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)evt_payload->ctx;
 
-	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx", (uint64_t)evt_payload);
+	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx core index:0x%x",
+		(uint64_t)evt_payload, evt_payload->core_index);
 	CAM_DBG(CAM_ISP, "bus_irq_status_0: = %x", evt_payload->irq_reg_val[0]);
 	CAM_DBG(CAM_ISP, "bus_irq_status_1: = %x", evt_payload->irq_reg_val[1]);
 	CAM_DBG(CAM_ISP, "bus_irq_status_2: = %x", evt_payload->irq_reg_val[2]);
@@ -3109,8 +3705,6 @@
 		evt_payload->irq_reg_val[5]);
 	CAM_DBG(CAM_ISP, "bus_irq_dual_comp_owrt: = %x",
 		evt_payload->irq_reg_val[6]);
-
-	CAM_DBG(CAM_ISP, "Calling Buf_done");
 	/* WM Done */
 	return cam_ife_hw_mgr_handle_buf_done_for_hw_res(ife_hwr_mgr_ctx,
 		evt_payload_priv);
@@ -3128,7 +3722,9 @@
 	evt_payload = evt_payload_priv;
 	ife_hwr_mgr_ctx = (struct cam_ife_hw_mgr_ctx *)handler_priv;
 
-	CAM_DBG(CAM_ISP, "addr of evt_payload = %llx", (uint64_t)evt_payload);
+	CAM_DBG(CAM_ISP, "addr of evt_payload = %pK core_index:%d",
+		(void *)evt_payload,
+		evt_payload->core_index);
 	CAM_DBG(CAM_ISP, "irq_status_0: = %x", evt_payload->irq_reg_val[0]);
 	CAM_DBG(CAM_ISP, "irq_status_1: = %x", evt_payload->irq_reg_val[1]);
 	CAM_DBG(CAM_ISP, "Violation register: = %x",
@@ -3139,14 +3735,25 @@
 	 * for this context it needs to be handled remaining
 	 * interrupts are ignored.
 	 */
-	rc = cam_ife_hw_mgr_handle_camif_error(ife_hwr_mgr_ctx,
-		evt_payload_priv);
+	if (g_ife_hw_mgr.debug_cfg.enable_recovery) {
+		CAM_DBG(CAM_ISP, "IFE Mgr recovery is enabled");
+	       rc = cam_ife_hw_mgr_handle_camif_error(ife_hwr_mgr_ctx,
+		    evt_payload_priv);
+	} else {
+		CAM_DBG(CAM_ISP, "recovery is not enabled");
+		rc = 0;
+	}
+
 	if (rc) {
 		CAM_ERR(CAM_ISP, "Encountered Error (%d), ignoring other irqs",
 			 rc);
 		return IRQ_HANDLED;
 	}
 
+	CAM_DBG(CAM_ISP, "Calling EOF");
+	cam_ife_hw_mgr_handle_eof_for_camif_hw_res(ife_hwr_mgr_ctx,
+		evt_payload_priv);
+
 	CAM_DBG(CAM_ISP, "Calling SOF");
 	/* SOF IRQ */
 	cam_ife_hw_mgr_handle_sof(ife_hwr_mgr_ctx,
@@ -3161,8 +3768,6 @@
 	/* EPOCH IRQ */
 	cam_ife_hw_mgr_handle_epoch_for_camif_hw_res(ife_hwr_mgr_ctx,
 		evt_payload_priv);
-	cam_ife_hw_mgr_handle_eof_for_camif_hw_res(ife_hwr_mgr_ctx,
-		evt_payload_priv);
 
 	return IRQ_HANDLED;
 }
@@ -3199,6 +3804,60 @@
 	return 0;
 }
 
+static int cam_ife_set_csid_debug(void *data, u64 val)
+{
+	g_ife_hw_mgr.debug_cfg.csid_debug = val;
+	CAM_DBG(CAM_ISP, "Set CSID Debug value :%lld", val);
+	return 0;
+}
+
+static int cam_ife_get_csid_debug(void *data, u64 *val)
+{
+	*val = g_ife_hw_mgr.debug_cfg.csid_debug;
+	CAM_DBG(CAM_ISP, "Get CSID Debug value :%lld",
+		g_ife_hw_mgr.debug_cfg.csid_debug);
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cam_ife_csid_debug,
+	cam_ife_get_csid_debug,
+	cam_ife_set_csid_debug, "%16llu");
+
+static int cam_ife_hw_mgr_debug_register(void)
+{
+	g_ife_hw_mgr.debug_cfg.dentry = debugfs_create_dir("camera_ife",
+		NULL);
+
+	if (!g_ife_hw_mgr.debug_cfg.dentry) {
+		CAM_ERR(CAM_ISP, "failed to create dentry");
+		return -ENOMEM;
+	}
+
+	if (!debugfs_create_file("ife_csid_debug",
+		0644,
+		g_ife_hw_mgr.debug_cfg.dentry, NULL,
+		&cam_ife_csid_debug)) {
+		CAM_ERR(CAM_ISP, "failed to create cam_ife_csid_debug");
+		goto err;
+	}
+
+	if (!debugfs_create_u32("enable_recovery",
+		0644,
+		g_ife_hw_mgr.debug_cfg.dentry,
+		&g_ife_hw_mgr.debug_cfg.enable_recovery)) {
+		CAM_ERR(CAM_ISP, "failed to create enable_recovery");
+		goto err;
+	}
+	g_ife_hw_mgr.debug_cfg.enable_recovery = 0;
+
+	return 0;
+
+err:
+	debugfs_remove_recursive(g_ife_hw_mgr.debug_cfg.dentry);
+	return -ENOMEM;
+}
+
 int cam_ife_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf)
 {
 	int rc = -EFAULT;
@@ -3212,8 +3871,8 @@
 	mutex_init(&g_ife_hw_mgr.ctx_mutex);
 
 	if (CAM_IFE_HW_NUM_MAX != CAM_IFE_CSID_HW_NUM_MAX) {
-		CAM_ERR(CAM_ISP, "Fatal, CSID num is different then IFE num!");
-		goto end;
+		CAM_ERR(CAM_ISP, "CSID num is different then IFE num");
+		return -EINVAL;
 	}
 
 	/* fill ife hw intf information */
@@ -3237,8 +3896,8 @@
 		}
 	}
 	if (j == 0) {
-		CAM_ERR(CAM_ISP, "no valid IFE HW!");
-		goto end;
+		CAM_ERR(CAM_ISP, "no valid IFE HW");
+		return -EINVAL;
 	}
 
 	/* fill csid hw intf information */
@@ -3248,8 +3907,8 @@
 			j++;
 	}
 	if (!j) {
-		CAM_ERR(CAM_ISP, "no valid IFE CSID HW!");
-		goto end;
+		CAM_ERR(CAM_ISP, "no valid IFE CSID HW");
+		return -EINVAL;
 	}
 
 	cam_ife_hw_mgr_sort_dev_with_caps(&g_ife_hw_mgr);
@@ -3267,19 +3926,25 @@
 	 */
 	if (cam_smmu_get_handle("ife",
 		&g_ife_hw_mgr.mgr_common.img_iommu_hdl)) {
-		CAM_ERR(CAM_ISP, "Can not get iommu handle.");
-		goto end;
+		CAM_ERR(CAM_ISP, "Can not get iommu handle");
+		return -EINVAL;
 	}
 
 	if (cam_smmu_ops(g_ife_hw_mgr.mgr_common.img_iommu_hdl,
 		CAM_SMMU_ATTACH)) {
 		CAM_ERR(CAM_ISP, "Attach iommu handle failed.");
-		goto end;
+		goto attach_fail;
 	}
 
-	CAM_DBG(CAM_ISP, "got iommu_handle=%d",
-		g_ife_hw_mgr.mgr_common.img_iommu_hdl);
-	g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure = -1;
+	if (cam_smmu_get_handle("cam-secure",
+		&g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure)) {
+		CAM_ERR(CAM_ISP, "Failed to get secure iommu handle");
+		goto secure_fail;
+	}
+
+	CAM_DBG(CAM_ISP, "iommu_handles: non-secure[0x%x], secure[0x%x]",
+		g_ife_hw_mgr.mgr_common.img_iommu_hdl,
+		g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure);
 
 	if (!cam_cdm_get_iommu_handle("ife", &cdm_handles)) {
 		CAM_DBG(CAM_ISP, "Successfully acquired the CDM iommu handles");
@@ -3292,6 +3957,7 @@
 		g_ife_hw_mgr.mgr_common.cmd_iommu_hdl_secure = -1;
 	}
 
+	atomic_set(&g_ife_hw_mgr.active_ctx_cnt, 0);
 	for (i = 0; i < CAM_CTX_MAX; i++) {
 		memset(&g_ife_hw_mgr.ctx_pool[i], 0,
 			sizeof(g_ife_hw_mgr.ctx_pool[i]));
@@ -3341,7 +4007,6 @@
 	/* Create Worker for ife_hw_mgr with 10 tasks */
 	rc = cam_req_mgr_workq_create("cam_ife_worker", 10,
 			&g_ife_hw_mgr.workq, CRM_WORKQ_USAGE_NON_IRQ);
-
 	if (rc < 0) {
 		CAM_ERR(CAM_ISP, "Unable to create worker");
 		goto end;
@@ -3360,13 +4025,28 @@
 	hw_mgr_intf->hw_config = cam_ife_mgr_config_hw;
 	hw_mgr_intf->hw_cmd = cam_ife_mgr_cmd;
 
+	cam_ife_hw_mgr_debug_register();
 	CAM_DBG(CAM_ISP, "Exit");
+
 	return 0;
 end:
 	if (rc) {
-		for (i = 0; i < CAM_CTX_MAX; i++)
+		for (i = 0; i < CAM_CTX_MAX; i++) {
+			cam_tasklet_deinit(
+				&g_ife_hw_mgr.mgr_common.tasklet_pool[i]);
 			kfree(g_ife_hw_mgr.ctx_pool[i].cdm_cmd);
+			g_ife_hw_mgr.ctx_pool[i].cdm_cmd = NULL;
+			g_ife_hw_mgr.ctx_pool[i].common.tasklet_info = NULL;
+		}
 	}
+	cam_smmu_destroy_handle(
+		g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure);
+	g_ife_hw_mgr.mgr_common.img_iommu_hdl_secure = -1;
+secure_fail:
+	cam_smmu_ops(g_ife_hw_mgr.mgr_common.img_iommu_hdl,
+		CAM_SMMU_DETACH);
+attach_fail:
+	cam_smmu_destroy_handle(g_ife_hw_mgr.mgr_common.img_iommu_hdl);
+	g_ife_hw_mgr.mgr_common.img_iommu_hdl = -1;
 	return rc;
 }
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
index 750b43e..4d26138 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.h
@@ -69,10 +69,10 @@
 
 
 /**
- * struct ctx_base_info - base hardware information for the context
+ * struct ctx_base_info - Base hardware information for the context
  *
  * @idx:                 Base resource index
- * @split_id:            split info for the base resource
+ * @split_id:            Split info for the base resource
  *
  */
 struct ctx_base_info {
@@ -81,6 +81,20 @@
 };
 
 /**
+ * struct cam_ife_hw_mgr_debug - contain the debug information
+ *
+ * @dentry:              Debugfs entry
+ * @csid_debug:          csid debug information
+ * @enable_recovery      enable recovery
+ *
+ */
+struct cam_ife_hw_mgr_debug {
+	struct dentry  *dentry;
+	uint64_t       csid_debug;
+	uint32_t       enable_recovery;
+};
+
+/**
  * struct cam_vfe_hw_mgr_ctx - IFE HW manager Context object
  *
  * @list:                   used by the ctx list.
@@ -142,7 +156,6 @@
 	uint32_t                        eof_cnt[CAM_IFE_HW_NUM_MAX];
 	atomic_t                        overflow_pending;
 	uint32_t                        is_rdi_only_context;
-
 };
 
 /**
@@ -160,6 +173,7 @@
  * @ife_csid_dev_caps      csid device capability stored per core
  * @ife_dev_caps           ife device capability per core
  * @work q                 work queue for IFE hw manager
+ * @debug_cfg              debug configuration
  */
 struct cam_ife_hw_mgr {
 	struct cam_isp_hw_mgr          mgr_common;
@@ -168,6 +182,7 @@
 	struct cam_soc_reg_map        *cdm_reg_map[CAM_IFE_HW_NUM_MAX];
 
 	struct mutex                   ctx_mutex;
+	atomic_t                       active_ctx_cnt;
 	struct list_head               free_ctx_list;
 	struct list_head               used_ctx_list;
 	struct cam_ife_hw_mgr_ctx      ctx_pool[CAM_CTX_MAX];
@@ -175,7 +190,8 @@
 	struct cam_ife_csid_hw_caps    ife_csid_dev_caps[
 						CAM_IFE_CSID_HW_NUM_MAX];
 	struct cam_vfe_hw_get_hw_cap   ife_dev_caps[CAM_IFE_HW_NUM_MAX];
-	struct cam_req_mgr_core_workq  *workq;
+	struct cam_req_mgr_core_workq *workq;
+	struct cam_ife_hw_mgr_debug    debug_cfg;
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
index 698a4c8..3606af9 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_isp_packet_parser.c
@@ -13,6 +13,7 @@
 #include <uapi/media/cam_defs.h>
 #include <uapi/media/cam_isp.h>
 #include "cam_mem_mgr.h"
+#include "cam_isp_hw.h"
 #include "cam_vfe_hw_intf.h"
 #include "cam_isp_packet_parser.h"
 #include "cam_debug_util.h"
@@ -26,7 +27,7 @@
 	int rc = -EINVAL;
 	struct cam_ife_hw_mgr_res       *hw_mgr_res;
 	struct cam_isp_resource_node    *res;
-	struct cam_isp_hw_get_cdm_args   get_base;
+	struct cam_isp_hw_get_cmd_update get_base;
 	struct cam_hw_update_entry      *hw_entry;
 	uint32_t                         num_ent, i;
 
@@ -53,24 +54,25 @@
 				continue;
 
 			get_base.res  = res;
-			get_base.cmd_buf_addr = kmd_buf_info->cpu_addr +
+			get_base.cmd_type = CAM_ISP_HW_CMD_GET_CHANGE_BASE;
+			get_base.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr +
 				kmd_buf_info->used_bytes/4;
-			get_base.size  = kmd_buf_info->size -
+			get_base.cmd.size  = kmd_buf_info->size -
 					kmd_buf_info->used_bytes;
 
 			rc = res->hw_intf->hw_ops.process_cmd(
 				res->hw_intf->hw_priv,
-				CAM_VFE_HW_CMD_GET_CHANGE_BASE, &get_base,
-				sizeof(struct cam_isp_hw_get_cdm_args));
+				CAM_ISP_HW_CMD_GET_CHANGE_BASE, &get_base,
+				sizeof(struct cam_isp_hw_get_cmd_update));
 			if (rc)
 				return rc;
 
 			hw_entry[num_ent].handle = kmd_buf_info->handle;
-			hw_entry[num_ent].len    = get_base.used_bytes;
+			hw_entry[num_ent].len    = get_base.cmd.used_bytes;
 			hw_entry[num_ent].offset = kmd_buf_info->offset;
 
-			kmd_buf_info->used_bytes += get_base.used_bytes;
-			kmd_buf_info->offset     += get_base.used_bytes;
+			kmd_buf_info->used_bytes += get_base.cmd.used_bytes;
+			kmd_buf_info->offset     += get_base.cmd.used_bytes;
 			num_ent++;
 			prepare->num_hw_update_entries = num_ent;
 
@@ -82,18 +84,161 @@
 	return rc;
 }
 
+static int cam_isp_update_dual_config(
+	struct cam_hw_prepare_update_args  *prepare,
+	struct cam_cmd_buf_desc            *cmd_desc,
+	uint32_t                            split_id,
+	uint32_t                            base_idx,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out)
+{
+	int rc = -EINVAL;
+	struct cam_isp_dual_config                 *dual_config;
+	struct cam_ife_hw_mgr_res                  *hw_mgr_res;
+	struct cam_isp_resource_node               *res;
+	struct cam_isp_hw_dual_isp_update_args      dual_isp_update_args;
+	uint32_t                                    outport_id;
+	uint32_t                                    ports_plane_idx;
+	size_t                                      len = 0;
+	uint32_t                                   *cpu_addr;
+	uint32_t                                    i, j;
+
+	CAM_DBG(CAM_UTIL, "cmd des size %d, length: %d",
+		cmd_desc->size, cmd_desc->length);
+
+	rc = cam_packet_util_get_cmd_mem_addr(
+		cmd_desc->mem_handle, &cpu_addr, &len);
+	if (rc)
+		return rc;
+
+	cpu_addr += (cmd_desc->offset / 4);
+	dual_config = (struct cam_isp_dual_config *)cpu_addr;
+
+	for (i = 0; i < dual_config->num_ports; i++) {
+
+		if (i >= CAM_ISP_IFE_OUT_RES_MAX) {
+			CAM_ERR(CAM_UTIL,
+				"failed update for i:%d > size_isp_out:%d",
+				i, size_isp_out);
+			return -EINVAL;
+		}
+
+		hw_mgr_res = &res_list_isp_out[i];
+		for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) {
+			if (!hw_mgr_res->hw_res[j])
+				continue;
+
+			if (hw_mgr_res->hw_res[j]->hw_intf->hw_idx != base_idx)
+				continue;
+
+			res = hw_mgr_res->hw_res[j];
+
+			if (res->res_id < CAM_ISP_IFE_OUT_RES_BASE ||
+				res->res_id >= CAM_ISP_IFE_OUT_RES_MAX)
+				continue;
+
+			outport_id = res->res_id & 0xFF;
+
+			ports_plane_idx = (j * (dual_config->num_ports *
+				CAM_PACKET_MAX_PLANES)) +
+				(outport_id * CAM_PACKET_MAX_PLANES);
+
+			if (dual_config->stripes[ports_plane_idx].port_id == 0)
+				continue;
+
+			dual_isp_update_args.split_id = j;
+			dual_isp_update_args.res      = res;
+			dual_isp_update_args.dual_cfg = dual_config;
+			rc = res->hw_intf->hw_ops.process_cmd(
+				res->hw_intf->hw_priv,
+				CAM_ISP_HW_CMD_STRIPE_UPDATE,
+				&dual_isp_update_args,
+				sizeof(struct cam_isp_hw_dual_isp_update_args));
+			if (rc)
+				return rc;
+		}
+	}
+
+	return rc;
+}
+
+int cam_isp_add_cmd_buf_update(
+	struct cam_ife_hw_mgr_res            *hw_mgr_res,
+	uint32_t                              cmd_type,
+	uint32_t                              hw_cmd_type,
+	uint32_t                              base_idx,
+	uint32_t                             *cmd_buf_addr,
+	uint32_t                              kmd_buf_remain_size,
+	void                                 *cmd_update_data,
+	uint32_t                             *bytes_used)
+{
+	int rc = 0;
+	struct cam_isp_resource_node       *res;
+	struct cam_isp_hw_get_cmd_update    cmd_update;
+	uint32_t                            i;
+	uint32_t                            total_used_bytes = 0;
+
+	if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) {
+		CAM_ERR(CAM_ISP, "io res id:%d not valid",
+			hw_mgr_res->res_type);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < CAM_ISP_HW_SPLIT_MAX; i++) {
+		if (!hw_mgr_res->hw_res[i])
+			continue;
+
+		if (hw_mgr_res->hw_res[i]->hw_intf->hw_idx != base_idx)
+			continue;
+
+		res = hw_mgr_res->hw_res[i];
+		cmd_update.res = res;
+		cmd_update.cmd_type = hw_cmd_type;
+		cmd_update.cmd.cmd_buf_addr = cmd_buf_addr;
+		cmd_update.cmd.size = kmd_buf_remain_size;
+		cmd_update.data = cmd_update_data;
+
+		CAM_DBG(CAM_ISP, "cmd buffer 0x%pK, size %d",
+			cmd_update.cmd.cmd_buf_addr,
+			cmd_update.cmd.size);
+		rc = res->hw_intf->hw_ops.process_cmd(
+			res->hw_intf->hw_priv,
+			cmd_update.cmd_type, &cmd_update,
+			sizeof(struct cam_isp_hw_get_cmd_update));
+
+		if (rc) {
+			CAM_ERR(CAM_ISP, "get buf cmd error:%d",
+				res->res_id);
+			rc = -ENOMEM;
+			return rc;
+		}
+
+		total_used_bytes += cmd_update.cmd.used_bytes;
+	}
+	*bytes_used = total_used_bytes;
+	CAM_DBG(CAM_ISP, "total_used_bytes %u", total_used_bytes);
+	return rc;
+}
 
 int cam_isp_add_command_buffers(
 	struct cam_hw_prepare_update_args  *prepare,
-	uint32_t                            split_id)
+	struct cam_kmd_buf_info            *kmd_buf_info,
+	struct ctx_base_info               *base_info,
+	cam_packet_generic_blob_handler     blob_handler_cb,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out)
 {
 	int rc = 0;
-	uint32_t  cmd_meta_data, num_ent, i;
-	struct cam_cmd_buf_desc       *cmd_desc = NULL;
-	struct cam_hw_update_entry    *hw_entry;
+	uint32_t                           cmd_meta_data, num_ent, i;
+	uint32_t                           base_idx;
+	enum cam_isp_hw_split_id           split_id;
+	struct cam_cmd_buf_desc           *cmd_desc = NULL;
+	struct cam_hw_update_entry        *hw_entry;
 
 	hw_entry = prepare->hw_update_entries;
-	num_ent = prepare->num_hw_update_entries;
+	split_id = base_info->split_id;
+	base_idx = base_info->idx;
+
 	/*
 	 * set the cmd_desc to point the first command descriptor in the
 	 * packet
@@ -106,6 +251,7 @@
 		split_id, prepare->packet->num_cmd_buf);
 
 	for (i = 0; i < prepare->packet->num_cmd_buf; i++) {
+		num_ent = prepare->num_hw_update_entries;
 		if (!cmd_desc[i].length)
 			continue;
 
@@ -168,238 +314,83 @@
 
 			num_ent++;
 			break;
-		case CAM_ISP_PACKET_META_GENERIC_BLOB:
+		case CAM_ISP_PACKET_META_DUAL_CONFIG:
+			rc = cam_isp_update_dual_config(prepare,
+				&cmd_desc[i], split_id, base_idx,
+				res_list_isp_out, size_isp_out);
+
+			if (rc)
+				return rc;
+			break;
+		case CAM_ISP_PACKET_META_GENERIC_BLOB_LEFT:
+			if (split_id == CAM_ISP_HW_SPLIT_LEFT) {
+				struct cam_isp_generic_blob_info   blob_info;
+
+				prepare->num_hw_update_entries = num_ent;
+				blob_info.prepare = prepare;
+				blob_info.base_info = base_info;
+				blob_info.kmd_buf_info = kmd_buf_info;
+
+				rc = cam_packet_util_process_generic_cmd_buffer(
+					&cmd_desc[i],
+					blob_handler_cb,
+					&blob_info);
+				if (rc) {
+					CAM_ERR(CAM_ISP,
+						"Failed in processing blobs %d",
+						rc);
+					return rc;
+				}
+				num_ent = prepare->num_hw_update_entries;
+			}
+			break;
+		case CAM_ISP_PACKET_META_GENERIC_BLOB_RIGHT:
+			if (split_id == CAM_ISP_HW_SPLIT_RIGHT) {
+				struct cam_isp_generic_blob_info   blob_info;
+
+				prepare->num_hw_update_entries = num_ent;
+				blob_info.prepare = prepare;
+				blob_info.base_info = base_info;
+				blob_info.kmd_buf_info = kmd_buf_info;
+
+				rc = cam_packet_util_process_generic_cmd_buffer(
+					&cmd_desc[i],
+					blob_handler_cb,
+					&blob_info);
+				if (rc) {
+					CAM_ERR(CAM_ISP,
+						"Failed in processing blobs %d",
+						rc);
+					return rc;
+				}
+				num_ent = prepare->num_hw_update_entries;
+			}
+			break;
+		case CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON: {
+			struct cam_isp_generic_blob_info   blob_info;
+
+			prepare->num_hw_update_entries = num_ent;
+			blob_info.prepare = prepare;
+			blob_info.base_info = base_info;
+			blob_info.kmd_buf_info = kmd_buf_info;
+
+			rc = cam_packet_util_process_generic_cmd_buffer(
+				&cmd_desc[i],
+				blob_handler_cb,
+				&blob_info);
+			if (rc) {
+				CAM_ERR(CAM_ISP,
+					"Failed in processing blobs %d", rc);
+				return rc;
+			}
+			num_ent = prepare->num_hw_update_entries;
+		}
 			break;
 		default:
 			CAM_ERR(CAM_ISP, "invalid cdm command meta data %d",
 				cmd_meta_data);
 			return -EINVAL;
 		}
-	}
-
-	prepare->num_hw_update_entries = num_ent;
-
-	return rc;
-}
-
-static int cam_isp_handle_hfr_config(
-	struct cam_isp_generic_blob_info *blob_info,
-	struct cam_isp_resource_hfr_config *hfr_config, uint32_t blob_size)
-{
-	uint32_t cal_blob_size =
-		sizeof(struct cam_isp_resource_hfr_config) +
-		(sizeof(struct cam_isp_port_hfr_config) *
-		(hfr_config->num_io_configs - 1));
-
-	if (cal_blob_size != blob_size) {
-		CAM_ERR(CAM_ISP, "Invalid blob size %d %d",
-			cal_blob_size, blob_size);
-		return -EINVAL;
-	}
-
-	CAM_DBG(CAM_ISP, "HFR num_io_config = %d", hfr_config->num_io_configs);
-
-	if (blob_info->hfr_config) {
-		CAM_WARN(CAM_ISP,
-			"Ignoring previous hfr_config, prev=%d, curr=%d",
-			blob_info->hfr_config->num_io_configs,
-			hfr_config->num_io_configs);
-		kfree(blob_info->hfr_config);
-	}
-
-	blob_info->hfr_config = kzalloc(blob_size, GFP_ATOMIC);
-	if (!blob_info->hfr_config)
-		return -ENOMEM;
-
-	memcpy(blob_info->hfr_config, hfr_config, blob_size);
-
-	return 0;
-}
-
-static int cam_isp_packet_generic_blob_handler(void *user_data,
-	uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data)
-{
-	int rc = 0;
-
-	if (!blob_data || (blob_size == 0)) {
-		CAM_ERR(CAM_ISP, "Invalid blob info %pK %d", blob_data,
-			blob_size);
-		return -EINVAL;
-	}
-
-	switch (blob_type) {
-	case CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG:
-		rc = cam_isp_handle_hfr_config(user_data,
-			(struct cam_isp_resource_hfr_config *)blob_data,
-			blob_size);
-		if (rc)
-			CAM_ERR(CAM_ISP, "Failed in handling hfr config %d",
-				rc);
-
-		break;
-	default:
-		CAM_WARN(CAM_ISP, "Invalid blob type %d", blob_type);
-		break;
-	}
-
-	return rc;
-}
-
-int cam_isp_process_generic_cmd_buffer(
-	struct cam_hw_prepare_update_args *prepare,
-	struct cam_isp_generic_blob_info  *blob_info)
-{
-	int i, rc = 0;
-	struct cam_cmd_buf_desc *cmd_desc = NULL;
-
-	/*
-	 * set the cmd_desc to point the first command descriptor in the
-	 * packet
-	 */
-	cmd_desc = (struct cam_cmd_buf_desc *)
-			((uint8_t *)&prepare->packet->payload +
-			prepare->packet->cmd_buf_offset);
-
-	for (i = 0; i < prepare->packet->num_cmd_buf; i++) {
-		if (!cmd_desc[i].length)
-			continue;
-
-		if (cmd_desc[i].meta_data != CAM_ISP_PACKET_META_GENERIC_BLOB)
-			continue;
-
-		rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]);
-		if (rc)
-			return rc;
-
-		rc = cam_packet_util_process_generic_cmd_buffer(&cmd_desc[i],
-			cam_isp_packet_generic_blob_handler, blob_info);
-		if (rc)
-			CAM_ERR(CAM_ISP, "Failed in processing blobs %d", rc);
-
-		break;
-	}
-
-	return rc;
-}
-
-int cam_isp_add_hfr_config_hw_update(
-	struct cam_isp_resource_hfr_config   *hfr_config,
-	struct cam_hw_prepare_update_args    *prepare,
-	uint32_t                              base_idx,
-	struct cam_kmd_buf_info              *kmd_buf_info,
-	struct cam_ife_hw_mgr_res            *res_list_isp_out,
-	uint32_t                              size_isp_out)
-{
-	int rc = 0;
-	struct cam_isp_resource_node       *res;
-	struct cam_ife_hw_mgr_res          *hw_mgr_res;
-	struct cam_isp_hw_get_hfr_update    update_hfr;
-	struct cam_isp_port_hfr_config     *io_hfr_config;
-	uint32_t                            kmd_buf_remain_size;
-	uint32_t                            i, j;
-	uint32_t                            res_id_out;
-	uint32_t                            hfr_cfg_used_bytes, num_ent;
-
-	hfr_cfg_used_bytes = 0;
-
-	/* Max one hw entries required for hfr config update */
-	if (prepare->num_hw_update_entries + 1 >=
-			prepare->max_hw_update_entries) {
-		CAM_ERR(CAM_ISP, "Insufficient  HW entries :%d %d",
-			prepare->num_hw_update_entries,
-			prepare->max_hw_update_entries);
-		return -EINVAL;
-	}
-
-	CAM_DBG(CAM_ISP, "num_io_configs= %d", hfr_config->num_io_configs);
-
-	for (i = 0; i < hfr_config->num_io_configs; i++) {
-		io_hfr_config = &hfr_config->io_hfr_config[i];
-		res_id_out = io_hfr_config->resource_type & 0xFF;
-
-		CAM_DBG(CAM_ISP, "hfr config idx %d, type=%d", i, res_id_out);
-
-		if (res_id_out >= size_isp_out) {
-			CAM_ERR(CAM_ISP, "invalid out restype:%x",
-				io_hfr_config->resource_type);
-			return -EINVAL;
-		}
-
-		hw_mgr_res = &res_list_isp_out[res_id_out];
-		if (hw_mgr_res->res_type == CAM_IFE_HW_MGR_RES_UNINIT) {
-			CAM_ERR(CAM_ISP, "io res id:%d not valid",
-				io_hfr_config->resource_type);
-			return -EINVAL;
-		}
-
-		for (j = 0; j < CAM_ISP_HW_SPLIT_MAX; j++) {
-			if (!hw_mgr_res->hw_res[j])
-				continue;
-
-			if (hw_mgr_res->hw_res[j]->hw_intf->hw_idx != base_idx)
-				continue;
-
-			res = hw_mgr_res->hw_res[j];
-			if (res->res_id !=
-				io_hfr_config->resource_type) {
-				CAM_ERR(CAM_ISP,
-					"wm err res id:%d io res id:%d",
-					res->res_id,
-					io_hfr_config->resource_type);
-				return -EINVAL;
-			}
-
-			if ((kmd_buf_info->used_bytes + hfr_cfg_used_bytes) <
-				kmd_buf_info->size) {
-				kmd_buf_remain_size = kmd_buf_info->size -
-					(kmd_buf_info->used_bytes +
-					hfr_cfg_used_bytes);
-			} else {
-				CAM_ERR(CAM_ISP,
-					"no free kmd memory for base %d",
-					base_idx);
-				rc = -ENOMEM;
-				return rc;
-			}
-
-			update_hfr.cdm.res = res;
-			update_hfr.cdm.cmd_buf_addr = kmd_buf_info->cpu_addr +
-				kmd_buf_info->used_bytes/4 +
-					hfr_cfg_used_bytes/4;
-			update_hfr.cdm.size = kmd_buf_remain_size;
-			update_hfr.io_hfr_cfg    = io_hfr_config;
-
-			CAM_DBG(CAM_ISP, "cmd buffer 0x%pK, size %d",
-				update_hfr.cdm.cmd_buf_addr,
-				update_hfr.cdm.size);
-			rc = res->hw_intf->hw_ops.process_cmd(
-				res->hw_intf->hw_priv,
-				CAM_VFE_HW_CMD_GET_HFR_UPDATE, &update_hfr,
-				sizeof(struct cam_isp_hw_get_hfr_update));
-
-			if (rc) {
-				CAM_ERR(CAM_ISP, "get buf cmd error:%d",
-					res->res_id);
-				rc = -ENOMEM;
-				return rc;
-			}
-			hfr_cfg_used_bytes += update_hfr.cdm.used_bytes;
-		}
-	}
-
-	CAM_DBG(CAM_ISP, "hfr_cfg_used_bytes %d", hfr_cfg_used_bytes);
-	if (hfr_cfg_used_bytes) {
-		/* Update the HW entries */
-		num_ent = prepare->num_hw_update_entries;
-		prepare->hw_update_entries[num_ent].handle =
-					kmd_buf_info->handle;
-		prepare->hw_update_entries[num_ent].len = hfr_cfg_used_bytes;
-		prepare->hw_update_entries[num_ent].offset =
-			kmd_buf_info->offset;
-		num_ent++;
-
-		kmd_buf_info->used_bytes += hfr_cfg_used_bytes;
-		kmd_buf_info->offset     += hfr_cfg_used_bytes;
 		prepare->num_hw_update_entries = num_ent;
 	}
 
@@ -408,6 +399,7 @@
 
 int cam_isp_add_io_buffers(
 	int                                   iommu_hdl,
+	int                                   sec_iommu_hdl,
 	struct cam_hw_prepare_update_args    *prepare,
 	uint32_t                              base_idx,
 	struct cam_kmd_buf_info              *kmd_buf_info,
@@ -420,12 +412,16 @@
 	struct cam_buf_io_cfg              *io_cfg;
 	struct cam_isp_resource_node       *res;
 	struct cam_ife_hw_mgr_res          *hw_mgr_res;
-	struct cam_isp_hw_get_buf_update    update_buf;
+	struct cam_isp_hw_get_cmd_update    update_buf;
+	struct cam_isp_hw_get_wm_update     wm_update;
 	uint32_t                            kmd_buf_remain_size;
 	uint32_t                            i, j, num_out_buf, num_in_buf;
 	uint32_t                            res_id_out, res_id_in, plane_id;
 	uint32_t                            io_cfg_used_bytes, num_ent;
-	size_t size;
+	size_t                              size;
+	int32_t                             hdl;
+	int                                 mmu_hdl;
+	bool                                mode, is_buf_secure;
 
 	io_cfg = (struct cam_buf_io_cfg *) ((uint8_t *)
 			&prepare->packet->payload +
@@ -445,8 +441,8 @@
 
 	for (i = 0; i < prepare->packet->num_io_configs; i++) {
 		CAM_DBG(CAM_ISP, "======= io config idx %d ============", i);
-		CAM_DBG(CAM_ISP, "resource_type:%d fence:%d",
-			io_cfg[i].resource_type, io_cfg[i].fence);
+		CAM_DBG(CAM_ISP, "i %d resource_type:%d fence:%d",
+			i, io_cfg[i].resource_type, io_cfg[i].fence);
 		CAM_DBG(CAM_ISP, "format: %d", io_cfg[i].format);
 		CAM_DBG(CAM_ISP, "direction %d",
 			io_cfg[i].direction);
@@ -536,9 +532,31 @@
 				if (!io_cfg[i].mem_handle[plane_id])
 					break;
 
+				hdl = io_cfg[i].mem_handle[plane_id];
+				if (res->process_cmd(res,
+						CAM_ISP_HW_CMD_GET_SECURE_MODE,
+						&mode,
+						sizeof(bool)))
+					return -EINVAL;
+
+				is_buf_secure = cam_mem_is_secure_buf(hdl);
+				if ((mode == CAM_SECURE_MODE_SECURE) &&
+					is_buf_secure) {
+					mmu_hdl = sec_iommu_hdl;
+				} else if (
+					(mode == CAM_SECURE_MODE_NON_SECURE) &&
+					(!is_buf_secure)) {
+					mmu_hdl = iommu_hdl;
+				} else {
+					CAM_ERR_RATE_LIMIT(CAM_ISP,
+						"Invalid hdl: port mode[%u], buf mode[%u]",
+						mode, is_buf_secure);
+					return -EINVAL;
+				}
+
 				rc = cam_mem_get_io_buf(
 					io_cfg[i].mem_handle[plane_id],
-					iommu_hdl, &io_addr[plane_id], &size);
+					mmu_hdl, &io_addr[plane_id], &size);
 				if (rc) {
 					CAM_ERR(CAM_ISP,
 						"no io addr for plane%d",
@@ -580,22 +598,24 @@
 				rc = -ENOMEM;
 				return rc;
 			}
-			update_buf.cdm.res = res;
-			update_buf.cdm.cmd_buf_addr = kmd_buf_info->cpu_addr +
+			update_buf.res = res;
+			update_buf.cmd_type = CAM_ISP_HW_CMD_GET_BUF_UPDATE;
+			update_buf.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr +
 				kmd_buf_info->used_bytes/4 +
 					io_cfg_used_bytes/4;
-			update_buf.cdm.size = kmd_buf_remain_size;
-			update_buf.image_buf = io_addr;
-			update_buf.num_buf   = plane_id;
-			update_buf.io_cfg    = &io_cfg[i];
+			wm_update.image_buf = io_addr;
+			wm_update.num_buf   = plane_id;
+			wm_update.io_cfg    = &io_cfg[i];
+			update_buf.cmd.size = kmd_buf_remain_size;
+			update_buf.wm_update = &wm_update;
 
 			CAM_DBG(CAM_ISP, "cmd buffer 0x%pK, size %d",
-				update_buf.cdm.cmd_buf_addr,
-				update_buf.cdm.size);
+				update_buf.cmd.cmd_buf_addr,
+				update_buf.cmd.size);
 			rc = res->hw_intf->hw_ops.process_cmd(
 				res->hw_intf->hw_priv,
-				CAM_VFE_HW_CMD_GET_BUF_UPDATE, &update_buf,
-				sizeof(struct cam_isp_hw_get_buf_update));
+				CAM_ISP_HW_CMD_GET_BUF_UPDATE, &update_buf,
+				sizeof(struct cam_isp_hw_get_cmd_update));
 
 			if (rc) {
 				CAM_ERR(CAM_ISP, "get buf cmd error:%d",
@@ -603,7 +623,7 @@
 				rc = -ENOMEM;
 				return rc;
 			}
-			io_cfg_used_bytes += update_buf.cdm.used_bytes;
+			io_cfg_used_bytes += update_buf.cmd.used_bytes;
 		}
 	}
 
@@ -643,7 +663,7 @@
 	struct cam_isp_resource_node         *res;
 	struct cam_ife_hw_mgr_res            *hw_mgr_res;
 	struct cam_hw_update_entry           *hw_entry;
-	struct cam_isp_hw_get_cdm_args        get_regup;
+	struct cam_isp_hw_get_cmd_update      get_regup;
 	uint32_t kmd_buf_remain_size, num_ent, i, reg_update_size;
 
 	hw_entry = prepare->hw_update_entries;
@@ -683,22 +703,23 @@
 				return rc;
 			}
 
-			get_regup.cmd_buf_addr = kmd_buf_info->cpu_addr +
+			get_regup.cmd.cmd_buf_addr = kmd_buf_info->cpu_addr +
 				kmd_buf_info->used_bytes/4 +
 				reg_update_size/4;
-			get_regup.size = kmd_buf_remain_size;
+			get_regup.cmd.size = kmd_buf_remain_size;
+			get_regup.cmd_type = CAM_ISP_HW_CMD_GET_REG_UPDATE;
 			get_regup.res = res;
 
 			rc = res->hw_intf->hw_ops.process_cmd(
 				res->hw_intf->hw_priv,
-				CAM_VFE_HW_CMD_GET_REG_UPDATE, &get_regup,
-				sizeof(struct cam_isp_hw_get_cdm_args));
+				CAM_ISP_HW_CMD_GET_REG_UPDATE, &get_regup,
+				sizeof(struct cam_isp_hw_get_cmd_update));
 			if (rc)
 				return rc;
 
 			CAM_DBG(CAM_ISP, "Reg update added for res %d hw_id %d",
 				res->res_id, res->hw_intf->hw_idx);
-			reg_update_size += get_regup.used_bytes;
+			reg_update_size += get_regup.cmd.used_bytes;
 		}
 	}
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
index 4a7eff8..8863275 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/cam_tasklet_util.c
@@ -261,6 +261,15 @@
 	*tasklet_info = NULL;
 }
 
+static void cam_tasklet_flush(void  *tasklet_info)
+{
+	unsigned long data;
+	struct cam_tasklet_info *tasklet = tasklet_info;
+
+	data = (unsigned long)tasklet;
+	cam_tasklet_action(data);
+}
+
 int cam_tasklet_start(void  *tasklet_info)
 {
 	struct cam_tasklet_info       *tasklet = tasklet_info;
@@ -290,6 +299,7 @@
 {
 	struct cam_tasklet_info  *tasklet = tasklet_info;
 
+	cam_tasklet_flush(tasklet);
 	atomic_set(&tasklet->tasklet_active, 0);
 	tasklet_disable(&tasklet->tasklet);
 }
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
index 187e5bc..e3f2ce2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/include/cam_isp_packet_parser.h
@@ -20,26 +20,32 @@
 #include "cam_hw_intf.h"
 #include "cam_packet_util.h"
 
-/**
- * struct cam_isp_generic_blob_info  Generic blob information
+/*
+ * struct cam_isp_generic_blob_info
  *
- * @hfr_config             Initial configuration required to enable HFR
- *
+ * @prepare:            Payload for prepare command
+ * @ctx_base_info:      Base hardware information for the context
+ * @kmd_buf_info:       Kmd buffer to store the custom cmd data
  */
 struct cam_isp_generic_blob_info {
-	struct cam_isp_resource_hfr_config *hfr_config;
+	struct cam_hw_prepare_update_args     *prepare;
+	struct ctx_base_info                  *base_info;
+	struct cam_kmd_buf_info               *kmd_buf_info;
 };
 
-/**
+/*
+ * cam_isp_add_change_base()
+ *
  * @brief                  Add change base in the hw entries list
  *                         processe the isp source list get the change base from
  *                         ISP HW instance
  *
- * @prepare:               Contain the  packet and HW update variables
+ * @prepare:               Contain the packet and HW update variables
  * @res_list_isp_src:      Resource list for IFE/VFE source
  * @base_idx:              Base or dev index of the IFE/VFE HW instance for
  *                         which change change base need to be added
  * @kmd_buf_info:          Kmd buffer to store the change base command
+ *
  * @return:                0 for success
  *                         -EINVAL for Fail
  */
@@ -49,26 +55,70 @@
 	uint32_t                               base_idx,
 	struct cam_kmd_buf_info               *kmd_buf_info);
 
-/**
+/*
+ * cam_isp_add_cmd_buf_update()
+ *
+ * @brief                  Add command buffer in the HW entries list for given
+ *                         Blob Data.
+ *
+ * @hw_mgr_res:            HW resource to get the update from
+ * @cmd_type:              Cmd type to get update for
+ * @hw_cmd_type:           HW Cmd type corresponding to cmd_type
+ * @base_idx:              Base hardware index
+ * @cmd_buf_addr:          Cpu buf addr of kmd scratch buffer
+ * @kmd_buf_remain_size:   Remaining size left for cmd buffer update
+ * @cmd_update_data:       Data needed by HW to process the cmd and provide
+ *                         cmd buffer
+ * @bytes_used:            Address of the field to be populated with
+ *                         total bytes used as output to caller
+ *
+ * @return:                Negative for Failure
+ *                         otherwise returns bytes used
+ */
+int cam_isp_add_cmd_buf_update(
+	struct cam_ife_hw_mgr_res            *hw_mgr_res,
+	uint32_t                              cmd_type,
+	uint32_t                              hw_cmd_type,
+	uint32_t                              base_idx,
+	uint32_t                             *cmd_buf_addr,
+	uint32_t                              kmd_buf_remain_size,
+	void                                 *cmd_update_data,
+	uint32_t                             *bytes_used);
+
+/*
+ * cam_isp_add_command_buffers()
+ *
  * @brief                  Add command buffer in the HW entries list for given
  *                         left or right VFE/IFE instance.
  *
- * @prepare:               Contain the  packet and HW update variables
- * @dual_type:             Left of right command buffers to be extracted
+ * @prepare:               Contain the packet and HW update variables
+ * @kmd_buf_info:          KMD buffer to store the custom cmd data
+ * @base_info:             base hardware information
+ * @blob_handler_cb:       Call_back_function for Meta handling
+ * @res_list_isp_out:      IFE /VFE out resource list
+ * @size_isp_out:          Size of the res_list_isp_out array
  *
  * @return:                0 for success
- *                         -EINVAL for Fail
+ *                         Negative for Failure
  */
 int cam_isp_add_command_buffers(
-	struct cam_hw_prepare_update_args    *prepare,
-	enum cam_isp_hw_split_id              split_id);
+	struct cam_hw_prepare_update_args  *prepare,
+	struct cam_kmd_buf_info            *kmd_buf_info,
+	struct ctx_base_info               *base_info,
+	cam_packet_generic_blob_handler     blob_handler_cb,
+	struct cam_ife_hw_mgr_res          *res_list_isp_out,
+	uint32_t                            size_isp_out);
 
-/**
+/*
+ * cam_isp_add_io_buffers()
+ *
  * @brief                  Add io buffer configurations in the HW entries list
  *                         processe the io configurations based on the base
  *                         index and update the HW entries list
  *
  * @iommu_hdl:             Iommu handle to get the IO buf from memory manager
+ * @sec_iommu_hdl:         Secure iommu handle to get the IO buf from
+ *                         memory manager
  * @prepare:               Contain the  packet and HW update variables
  * @base_idx:              Base or dev index of the IFE/VFE HW instance
  * @kmd_buf_info:          Kmd buffer to store the change base command
@@ -79,7 +129,9 @@
  * @return:                0 for success
  *                         -EINVAL for Fail
  */
-int cam_isp_add_io_buffers(int	 iommu_hdl,
+int cam_isp_add_io_buffers(
+	int                                   iommu_hdl,
+	int                                   sec_iommu_hdl,
 	struct cam_hw_prepare_update_args    *prepare,
 	uint32_t                              base_idx,
 	struct cam_kmd_buf_info              *kmd_buf_info,
@@ -87,8 +139,9 @@
 	uint32_t                              size_isp_out,
 	bool                                  fill_fence);
 
-
-/**
+/*
+ * cam_isp_add_reg_update()
+ *
  * @brief                  Add reg update in the hw entries list
  *                         processe the isp source list get the reg update from
  *                         ISP HW instance
@@ -106,40 +159,4 @@
 	uint32_t                              base_idx,
 	struct cam_kmd_buf_info              *kmd_buf_info);
 
-/**
- * @brief                  Add HFR configurations in the HW entries list
- *                         processe the hfr configurations based on the base
- *                         index and update the HW entries list
- *
- * @hfr_config:            HFR resource configuration info
- * @prepare:               Contain the  packet and HW update variables
- * @base_idx:              Base or dev index of the IFE/VFE HW instance
- * @kmd_buf_info:          Kmd buffer to store the change base command
- * @res_list_isp_out:      IFE /VFE out resource list
- * @size_isp_out:          Size of the res_list_isp_out array
- *
- * @return:                0 for success
- *                         -EINVAL for Fail
- */
-int cam_isp_add_hfr_config_hw_update(
-	struct cam_isp_resource_hfr_config   *hfr_config,
-	struct cam_hw_prepare_update_args    *prepare,
-	uint32_t                              base_idx,
-	struct cam_kmd_buf_info              *kmd_buf_info,
-	struct cam_ife_hw_mgr_res            *res_list_isp_out,
-	uint32_t                              size_isp_out);
-
-/**
- * @brief                  Processing Generic command buffer.
- *
- * @prepare:               Contain the  packet and HW update variables
- * @blob_info:             Information from generic blob command buffer
- *
- * @return:                0 for success
- *                         -EINVAL for Fail
- */
-int cam_isp_process_generic_cmd_buffer(
-	struct cam_hw_prepare_update_args *prepare,
-	struct cam_isp_generic_blob_info  *blob_info);
-
 #endif /*_CAM_ISP_HW_PARSER_H */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
index 2341b38..44dc5c4 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.c
@@ -239,7 +239,8 @@
 	int                         i;
 	int                         rc = 0;
 	uint32_t                    irq_mask;
-	unsigned long               flags;
+	unsigned long               flags = 0;
+	bool                        need_lock;
 
 	if (!controller || !handler_priv || !evt_bit_mask_arr) {
 		CAM_ERR(CAM_ISP,
@@ -301,7 +302,9 @@
 	if (controller->hdl_idx > 0x3FFFFFFF)
 		controller->hdl_idx = 1;
 
-	write_lock_irqsave(&controller->rw_lock, flags);
+	need_lock = !in_irq();
+	if (need_lock)
+		write_lock_irqsave(&controller->rw_lock, flags);
 	for (i = 0; i < controller->num_registers; i++) {
 		controller->irq_register_arr[i].top_half_enable_mask[priority]
 			|= evt_bit_mask_arr[i];
@@ -313,7 +316,8 @@
 		cam_io_w_mb(irq_mask, controller->mem_base +
 			controller->irq_register_arr[i].mask_reg_offset);
 	}
-	write_unlock_irqrestore(&controller->rw_lock, flags);
+	if (need_lock)
+		write_unlock_irqrestore(&controller->rw_lock, flags);
 
 	list_add_tail(&evt_handler->list_node,
 		&controller->evt_handler_list_head);
@@ -329,6 +333,119 @@
 	return rc;
 }
 
+int cam_irq_controller_enable_irq(void *irq_controller, uint32_t handle)
+{
+	struct cam_irq_controller  *controller  = irq_controller;
+	struct cam_irq_evt_handler *evt_handler = NULL;
+	struct cam_irq_evt_handler *evt_handler_temp;
+	unsigned long               flags = 0;
+	unsigned int                i;
+	uint32_t                    irq_mask;
+	uint32_t                    found = 0;
+	int                         rc = -EINVAL;
+	bool                        need_lock;
+
+	if (!controller)
+		return rc;
+
+	list_for_each_entry_safe(evt_handler, evt_handler_temp,
+		&controller->evt_handler_list_head, list_node) {
+		if (evt_handler->index == handle) {
+			CAM_DBG(CAM_ISP, "enable item %d", handle);
+			found = 1;
+			rc = 0;
+			break;
+		}
+	}
+
+	if (!found)
+		return rc;
+
+	need_lock = !in_irq();
+	if (need_lock)
+		write_lock_irqsave(&controller->rw_lock, flags);
+	for (i = 0; i < controller->num_registers; i++) {
+		controller->irq_register_arr[i].
+		top_half_enable_mask[evt_handler->priority] |=
+			evt_handler->evt_bit_mask_arr[i];
+
+		irq_mask = cam_io_r_mb(controller->mem_base +
+			controller->irq_register_arr[i].
+			mask_reg_offset);
+		irq_mask |= evt_handler->evt_bit_mask_arr[i];
+
+		cam_io_w_mb(irq_mask, controller->mem_base +
+		controller->irq_register_arr[i].mask_reg_offset);
+	}
+	if (need_lock)
+		write_unlock_irqrestore(&controller->rw_lock, flags);
+
+	return rc;
+}
+
+int cam_irq_controller_disable_irq(void *irq_controller, uint32_t handle)
+{
+	struct cam_irq_controller  *controller  = irq_controller;
+	struct cam_irq_evt_handler *evt_handler = NULL;
+	struct cam_irq_evt_handler *evt_handler_temp;
+	unsigned long               flags = 0;
+	unsigned int                i;
+	uint32_t                    irq_mask;
+	uint32_t                    found = 0;
+	int                         rc = -EINVAL;
+	bool                        need_lock;
+
+	if (!controller)
+		return rc;
+
+	list_for_each_entry_safe(evt_handler, evt_handler_temp,
+		&controller->evt_handler_list_head, list_node) {
+		if (evt_handler->index == handle) {
+			CAM_DBG(CAM_ISP, "disable item %d", handle);
+			found = 1;
+			rc = 0;
+			break;
+		}
+	}
+
+	if (!found)
+		return rc;
+
+	need_lock = !in_irq();
+	if (need_lock)
+		write_lock_irqsave(&controller->rw_lock, flags);
+	for (i = 0; i < controller->num_registers; i++) {
+		controller->irq_register_arr[i].
+		top_half_enable_mask[evt_handler->priority] &=
+			~(evt_handler->evt_bit_mask_arr[i]);
+
+		irq_mask = cam_io_r_mb(controller->mem_base +
+			controller->irq_register_arr[i].
+			mask_reg_offset);
+		irq_mask &= ~(evt_handler->evt_bit_mask_arr[i]);
+
+		cam_io_w_mb(irq_mask, controller->mem_base +
+			controller->irq_register_arr[i].
+			mask_reg_offset);
+
+		/* Clear the IRQ bits of this handler */
+		cam_io_w_mb(evt_handler->evt_bit_mask_arr[i],
+			controller->mem_base +
+			controller->irq_register_arr[i].
+			clear_reg_offset);
+
+		if (controller->global_clear_offset)
+			cam_io_w_mb(
+				controller->global_clear_bitmask,
+				controller->mem_base +
+				controller->global_clear_offset);
+	}
+	if (need_lock)
+		write_unlock_irqrestore(&controller->rw_lock, flags);
+
+	return rc;
+}
+
 int cam_irq_controller_unsubscribe_irq(void *irq_controller,
 	uint32_t handle)
 {
@@ -338,8 +455,9 @@
 	uint32_t                    i;
 	uint32_t                    found = 0;
 	uint32_t                    irq_mask;
-	unsigned long               flags;
+	unsigned long               flags = 0;
 	int                         rc = -EINVAL;
+	bool                        need_lock;
 
 	list_for_each_entry_safe(evt_handler, evt_handler_temp,
 		&controller->evt_handler_list_head, list_node) {
@@ -353,8 +471,11 @@
 		}
 	}
 
+	need_lock = !in_irq();
+
 	if (found) {
-		write_lock_irqsave(&controller->rw_lock, flags);
+		if (need_lock)
+			write_lock_irqsave(&controller->rw_lock, flags);
 		for (i = 0; i < controller->num_registers; i++) {
 			controller->irq_register_arr[i].
 				top_half_enable_mask[evt_handler->priority] &=
@@ -380,7 +501,8 @@
 					controller->mem_base +
 					controller->global_clear_offset);
 		}
-		write_unlock_irqrestore(&controller->rw_lock, flags);
+		if (need_lock)
+			write_unlock_irqrestore(&controller->rw_lock, flags);
 
 		kfree(evt_handler->evt_bit_mask_arr);
 		kfree(evt_handler);
@@ -508,7 +630,6 @@
 					i, j, need_th_processing[j]);
 		}
 	}
-	read_unlock(&controller->rw_lock);
 	CAM_DBG(CAM_ISP, "unlocked controller %pK name %s rw_lock %pK",
 		controller, controller->name, &controller->rw_lock);
 
@@ -527,6 +648,7 @@
 				&controller->th_list_head[i]);
 		}
 	}
+	read_unlock(&controller->rw_lock);
 
 	return IRQ_HANDLED;
 }
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h
index 1990c51..7e307b5 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller/cam_irq_controller.h
@@ -214,4 +214,40 @@
  */
 irqreturn_t cam_irq_controller_handle_irq(int irq_num, void *priv);
 
+/*
+ * cam_irq_controller_disable_irq()
+ *
+ * @brief:              Disable the interrupts on given controller.
+ *                      Unsubscribe will disable the IRQ by default, so this is
+ *                      only needed if between subscribe/unsubscribe there is
+ *                      need to disable IRQ again
+ *
+ * @irq_controller:     Pointer to IRQ Controller that controls the registered
+ *                      events to it.
+ * @handle:             Handle returned on successful subscribe, used to
+ *                      identify the handler object
+ *
+ * @return:             0: events found and disabled
+ *                      Negative: events not registered on this controller
+ */
+int cam_irq_controller_disable_irq(void *irq_controller, uint32_t handle);
+
+/*
+ * cam_irq_controller_enable_irq()
+ *
+ * @brief:              Enable the interrupts on given controller.
+ *                      Subscribe will enable the IRQ by default, so this is
+ *                      only needed if between subscribe/unsubscribe there is
+ *                      need to enable IRQ again
+ *
+ * @irq_controller:     Pointer to IRQ Controller that controls the registered
+ *                      events to it.
+ * @handle:             Handle returned on successful subscribe, used to
+ *                      identify the handler object
+ *
+ * @return:             0: events found and enabled
+ *                      Negative: events not registered on this controller
+ */
+int cam_irq_controller_enable_irq(void *irq_controller, uint32_t handle);
+
 #endif /* _CAM_IRQ_CONTROLLER_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
index 8ff2a55..c68ddf7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid170.h
@@ -199,7 +199,7 @@
 	.csid_csi2_rx_captured_long_pkt_0_addr        = 0x130,
 	.csid_csi2_rx_captured_long_pkt_1_addr        = 0x134,
 	.csid_csi2_rx_captured_long_pkt_ftr_addr      = 0x138,
-	.csid_csi2_rx_captured_cphy_pkt_ftr_addr      = 0x13c,
+	.csid_csi2_rx_captured_cphy_pkt_hdr_addr      = 0x13c,
 	.csid_csi2_rx_lane0_misr_addr                 = 0x150,
 	.csid_csi2_rx_lane1_misr_addr                 = 0x154,
 	.csid_csi2_rx_lane2_misr_addr                 = 0x158,
@@ -213,6 +213,14 @@
 	.csi2_irq_mask_all                            = 0xFFFFFFF,
 	.csi2_misr_enable_shift_val                   = 6,
 	.csi2_vc_mode_shift_val                       = 2,
+	.csi2_capture_long_pkt_en_shift               = 0,
+	.csi2_capture_short_pkt_en_shift              = 1,
+	.csi2_capture_cphy_pkt_en_shift               = 2,
+	.csi2_capture_long_pkt_dt_shift               = 4,
+	.csi2_capture_long_pkt_vc_shift               = 10,
+	.csi2_capture_short_pkt_vc_shift              = 15,
+	.csi2_capture_cphy_pkt_dt_shift               = 20,
+	.csi2_capture_cphy_pkt_vc_shift               = 26,
 };
 
 static struct cam_ife_csid_csi2_tpg_reg_offset
@@ -236,9 +244,10 @@
 	.csid_tpg_cgen_n_y1_addr                      = 0x664,
 	.csid_tpg_cgen_n_y2_addr                      = 0x668,
 
-	/*configurations */
+	/* configurations */
 	.tpg_dtn_cfg_offset                           = 0xc,
 	.tpg_cgen_cfg_offset                          = 0x20,
+	.tpg_cpas_ife_reg_offset                      = 0x28,
 };
 
 static struct cam_ife_csid_common_reg_offset
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
index 9103136..9a368cf 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.c
@@ -32,7 +32,14 @@
 #define CAM_IFE_CSID_TIMEOUT_SLEEP_US                  1000
 #define CAM_IFE_CSID_TIMEOUT_ALL_US                    1000000
 
-#define MEASURE_EN                                     0
+/*
+ * Constant Factors needed to change QTimer ticks to nanoseconds
+ * QTimer Freq = 19.2 MHz
+ * Time(us) = ticks/19.2
+ * Time(ns) = ticks/19.2 * 1000
+ */
+#define CAM_IFE_CSID_QTIMER_MUL_FACTOR                 10000
+#define CAM_IFE_CSID_QTIMER_DIV_FACTOR                 192
 
 static int cam_ife_csid_is_ipp_format_supported(
 	uint32_t in_format)
@@ -85,6 +92,7 @@
 	case CAM_FORMAT_MIPI_RAW_8:
 		switch (out_format) {
 		case CAM_FORMAT_MIPI_RAW_8:
+		case CAM_FORMAT_PLAIN128:
 			*decode_fmt = 0xf;
 			break;
 		case CAM_FORMAT_PLAIN8:
@@ -99,6 +107,7 @@
 	case CAM_FORMAT_MIPI_RAW_10:
 		switch (out_format) {
 		case CAM_FORMAT_MIPI_RAW_10:
+		case CAM_FORMAT_PLAIN128:
 			*decode_fmt = 0xf;
 			break;
 		case CAM_FORMAT_PLAIN16_10:
@@ -196,7 +205,7 @@
 	}
 
 	if (rc)
-		CAM_ERR(CAM_ISP, "Unsupported format pair in %d out %d\n",
+		CAM_ERR(CAM_ISP, "Unsupported format pair in %d out %d",
 			in_format, out_format);
 
 	return rc;
@@ -562,7 +571,7 @@
 	struct cam_ife_csid_cid_data       *cid_data;
 
 	CAM_DBG(CAM_ISP,
-		"CSID:%d res_sel:%d Lane type:%d lane_num:%d dt:%d vc:%d",
+		"CSID:%d res_sel:0x%x Lane type:%d lane_num:%d dt:%d vc:%d",
 		csid_hw->hw_intf->hw_idx,
 		cid_reserv->in_port->res_type,
 		cid_reserv->in_port->lane_type,
@@ -627,6 +636,12 @@
 		goto end;
 	}
 
+	if (cid_reserv->in_port->res_type == CAM_ISP_IFE_IN_RES_PHY_3 &&
+		csid_hw->hw_intf->hw_idx != 2) {
+		rc = -EINVAL;
+		goto end;
+	}
+
 	if (csid_hw->csi2_reserve_cnt) {
 		/* current configure res type should match requested res type */
 		if (csid_hw->res_type != cid_reserv->in_port->res_type) {
@@ -683,11 +698,24 @@
 			}
 			csid_hw->tpg_cfg.in_format =
 				cid_reserv->in_port->format;
-			csid_hw->tpg_cfg.width =
-				cid_reserv->in_port->left_width;
+			csid_hw->tpg_cfg.usage_type =
+				cid_reserv->in_port->usage_type;
+			if (cid_reserv->in_port->usage_type)
+				csid_hw->tpg_cfg.width =
+					(cid_reserv->in_port->right_stop + 1);
+			else
+				csid_hw->tpg_cfg.width =
+					cid_reserv->in_port->left_width;
+
 			csid_hw->tpg_cfg.height = cid_reserv->in_port->height;
 			csid_hw->tpg_cfg.test_pattern =
 				cid_reserv->in_port->test_pattern;
+
+			CAM_DBG(CAM_ISP, "CSID:%d TPG width:%d height:%d",
+				csid_hw->hw_intf->hw_idx,
+				csid_hw->tpg_cfg.width,
+				csid_hw->tpg_cfg.height);
+
 			cid_data->tpg_set = 1;
 		} else {
 			csid_hw->csi2_rx_cfg.phy_sel =
@@ -815,6 +843,7 @@
 	path_data->sync_mode = reserve->sync_mode;
 	path_data->height  = reserve->in_port->height;
 	path_data->start_line = reserve->in_port->line_start;
+	path_data->end_line = reserve->in_port->line_stop;
 	if (reserve->in_port->res_type == CAM_ISP_IFE_IN_RES_TPG) {
 		path_data->dt = CAM_IFE_CSID_TPG_DT_VAL;
 		path_data->vc = CAM_IFE_CSID_TPG_VC_VAL;
@@ -826,18 +855,32 @@
 	if (reserve->sync_mode == CAM_ISP_HW_SYNC_MASTER) {
 		path_data->crop_enable = 1;
 		path_data->start_pixel = reserve->in_port->left_start;
+		path_data->end_pixel = reserve->in_port->left_stop;
 		path_data->width  = reserve->in_port->left_width;
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:startpixel 0x%x endpixel:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_pixel,
+			path_data->end_pixel);
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_line,
+			path_data->end_line);
 	} else if (reserve->sync_mode == CAM_ISP_HW_SYNC_SLAVE) {
 		path_data->crop_enable = 1;
 		path_data->start_pixel = reserve->in_port->right_start;
+		path_data->end_pixel = reserve->in_port->right_stop;
 		path_data->width  = reserve->in_port->right_width;
+		CAM_DBG(CAM_ISP, "CSID:%d slave:start:0x%x end:0x%x width 0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_pixel,
+			path_data->end_pixel, path_data->width);
+		CAM_DBG(CAM_ISP, "CSID:%dmaster:line start:0x%x line end:0x%x",
+			csid_hw->hw_intf->hw_idx, path_data->start_line,
+			path_data->end_line);
 	} else {
 		path_data->crop_enable = 0;
 		path_data->width  = reserve->in_port->left_width;
 		path_data->start_pixel = reserve->in_port->left_start;
 	}
 
-	CAM_DBG(CAM_ISP, "Res %d width %d height %d\n", reserve->res_id,
+	CAM_DBG(CAM_ISP, "Res %d width %d height %d", reserve->res_id,
 		path_data->width, path_data->height);
 	reserve->node_res = res;
 
@@ -994,6 +1037,7 @@
 static int cam_ife_csid_tpg_start(struct cam_ife_csid_hw   *csid_hw,
 	struct cam_isp_resource_node       *res)
 {
+	int rc = 0;
 	uint32_t  val = 0;
 	struct cam_hw_soc_info    *soc_info;
 
@@ -1039,6 +1083,15 @@
 			}
 		}
 
+		/* Enable the IFE force clock on for dual isp case */
+		if (csid_hw->tpg_cfg.usage_type) {
+			rc = cam_ife_csid_enable_ife_force_clock_on(soc_info,
+				csid_hw->csid_info->csid_reg->tpg_reg->
+				tpg_cpas_ife_reg_offset);
+			if (rc)
+				return rc;
+		}
+
 		CAM_DBG(CAM_ISP, "============ TPG control ============");
 		val = (4 << 20);
 		val |= (0x80 << 8);
@@ -1058,6 +1111,7 @@
 static int cam_ife_csid_tpg_stop(struct cam_ife_csid_hw   *csid_hw,
 	struct cam_isp_resource_node       *res)
 {
+	int rc = 0;
 	struct cam_hw_soc_info    *soc_info;
 
 	if (csid_hw->tpg_start_cnt)
@@ -1073,6 +1127,12 @@
 		CAM_DBG(CAM_ISP, "CSID:%d stop CSID TPG",
 			csid_hw->hw_intf->hw_idx);
 
+		/* Disable the IFE force clock on for dual isp case */
+		if (csid_hw->tpg_cfg.usage_type)
+			rc = cam_ife_csid_disable_ife_force_clock_on(soc_info,
+				csid_hw->csid_info->csid_reg->tpg_reg->
+				tpg_cpas_ife_reg_offset);
+
 		/*stop the TPG */
 		cam_io_w_mb(0,  soc_info->reg_map[0].mem_base +
 		csid_hw->csid_info->csid_reg->tpg_reg->csid_tpg_ctrl_addr);
@@ -1100,8 +1160,8 @@
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->tpg_reg->csid_tpg_vc_cfg0_addr);
 
-	/* vertical blanking count = 0x740, horzontal blanking count = 0x740*/
-	val = (0x740 << 12) | 0x740;
+	/* vertical blanking count = 0x3FF, horzontal blanking count = 0x740*/
+	val = (0x3FF << 12) | 0x740;
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->tpg_reg->csid_tpg_vc_cfg1_addr);
 
@@ -1202,7 +1262,34 @@
 		CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW |
 		CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION |
 		CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION |
+		CSID_CSI2_RX_ERROR_CRC |
+		CSID_CSI2_RX_ERROR_ECC |
+		CSID_CSI2_RX_ERROR_MMAPPED_VC_DT |
+		CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW |
+		CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME |
 		CSID_CSI2_RX_ERROR_CPHY_PH_CRC;
+
+	/* Enable the interrupt based on csid debug info set */
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ)
+		val |= CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ)
+		val |= CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED |
+			CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->csi2_reg->csid_csi2_rx_irq_mask_addr);
 
@@ -1277,9 +1364,9 @@
 		(path_data->dt << csid_reg->cmn_reg->dt_shift_val) |
 		(path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) |
 		(decode_format << csid_reg->cmn_reg->fmt_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable <<
 		csid_reg->cmn_reg->crop_h_en_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable <<
 		csid_reg->cmn_reg->crop_v_en_shift_val) |
 		(1 << 1) | 1;
 	val |= (1 << csid_reg->ipp_reg->pix_store_en_shift_val);
@@ -1291,21 +1378,21 @@
 		csid_reg->ipp_reg->csid_ipp_cfg1_addr);
 
 	if (path_data->crop_enable) {
-		val = ((path_data->width +
-			path_data->start_pixel) & 0xFFFF <<
+		val = (((path_data->end_pixel & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_pixel & 0xFFFF);
-
+			(path_data->start_pixel & 0xFFFF));
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->ipp_reg->csid_ipp_hcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 
-		val = ((path_data->height +
-			path_data->start_line) & 0xFFFF <<
+		val = (((path_data->end_line & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_line & 0xFFFF);
-
+			(path_data->start_line & 0xFFFF));
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->ipp_reg->csid_ipp_vcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 	}
 
 	/* set frame drop pattern to 0 and period to 1 */
@@ -1350,6 +1437,34 @@
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->ipp_reg->csid_ipp_cfg0_addr);
 
+	/* configure the rx packet capture based on csid debug set */
+	val = 0;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val = ((1 <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift));
+
+	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+		csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr);
+	CAM_DBG(CAM_ISP, "rx capture control value 0x%x", val);
+
 	res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW;
 
 	return rc;
@@ -1439,6 +1554,12 @@
 
 	/* Enable the required ipp interrupts */
 	val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_SOF;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_EOF;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->ipp_reg->csid_ipp_irq_mask_addr);
 
@@ -1567,9 +1688,9 @@
 		(path_data->cid << csid_reg->cmn_reg->dt_id_shift_val) |
 		(path_format << csid_reg->cmn_reg->fmt_shift_val) |
 		(plain_fmt << csid_reg->cmn_reg->plain_fmt_shit_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable  <<
 			csid_reg->cmn_reg->crop_h_en_shift_val) |
-		(path_data->crop_enable & 1 <<
+		(path_data->crop_enable  <<
 		csid_reg->cmn_reg->crop_v_en_shift_val) |
 		(1 << 2) | 3;
 
@@ -1581,21 +1702,23 @@
 			csid_reg->rdi_reg[id]->csid_rdi_cfg1_addr);
 
 	if (path_data->crop_enable) {
-		val = ((path_data->width +
-			path_data->start_pixel) & 0xFFFF <<
+		val = (((path_data->end_pixel & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_pixel & 0xFFFF);
+			(path_data->start_pixel & 0xFFFF));
 
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->rdi_reg[id]->csid_rdi_rpp_hcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Horizontal crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 
-		val = ((path_data->height +
-			path_data->start_line) & 0xFFFF <<
+		val = (((path_data->end_line & 0xFFFF) <<
 			csid_reg->cmn_reg->crop_shift) |
-			(path_data->start_line & 0xFFFF);
+			(path_data->start_line & 0xFFFF));
 
 		cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 			csid_reg->rdi_reg[id]->csid_rdi_rpp_vcrop_addr);
+		CAM_DBG(CAM_ISP, "CSID:%d Vertical Crop config val: 0x%x",
+			csid_hw->hw_intf->hw_idx, val);
 	}
 	/* set frame drop pattern to 0 and period to 1 */
 	cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
@@ -1627,21 +1750,35 @@
 	val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr);
 	val |= (1 << csid_reg->cmn_reg->path_en_shift_val);
-#if MEASURE_EN
-	val |= 0x2;
-	cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base +
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg0_addr);
-	cam_io_w_mb(path_data->height << 16 | path_data->width,
-		soc_info->reg_map[0].mem_base +
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg1_addr);
-	CAM_DBG(CAM_ISP, "measure_cfg1 0x%x offset 0x%x\n",
-		path_data->height << 16 | path_data->width,
-		csid_reg->rdi_reg[id]->csid_rdi_format_measure_cfg0_addr);
 
-#endif
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_cfg0_addr);
 
+	/* configure the rx packet capture based on csid debug set */
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE)
+		val = ((1 <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_en_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_short_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_long_pkt_vc_shift));
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE)
+		val |= ((1 <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_en_shift) |
+			(path_data->dt <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_dt_shift) |
+			(path_data->vc <<
+			csid_reg->csi2_reg->csi2_capture_cphy_pkt_vc_shift));
+	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
+		csid_reg->csi2_reg->csid_csi2_rx_capture_ctrl_addr);
+
 	res->res_state = CAM_ISP_RESOURCE_STATE_INIT_HW;
 
 	return rc;
@@ -1708,11 +1845,13 @@
 			csid_reg->rdi_reg[id]->csid_rdi_ctrl_addr);
 
 	/* Enable the required RDI interrupts */
-	val = CSID_PATH_INFO_RST_DONE |
-#if MEASURE_EN
-		CSID_PATH_INFO_INPUT_SOF |
-#endif
-		CSID_PATH_ERROR_FIFO_OVERFLOW;
+	val = CSID_PATH_INFO_RST_DONE | CSID_PATH_ERROR_FIFO_OVERFLOW;
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_SOF;
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ)
+		val |= CSID_PATH_INFO_INPUT_EOF;
+
 	cam_io_w_mb(val, soc_info->reg_map[0].mem_base +
 		csid_reg->rdi_reg[id]->csid_rdi_irq_mask_addr);
 
@@ -1842,8 +1981,27 @@
 		time_stamp->time_stamp_val |= time_32;
 	}
 
+	time_stamp->time_stamp_val = mul_u64_u32_div(
+		time_stamp->time_stamp_val,
+		CAM_IFE_CSID_QTIMER_MUL_FACTOR,
+		CAM_IFE_CSID_QTIMER_DIV_FACTOR);
+
 	return 0;
 }
+
+static int cam_ife_csid_set_csid_debug(struct cam_ife_csid_hw   *csid_hw,
+	void *cmd_args)
+{
+	uint32_t  *csid_debug;
+
+	csid_debug = (uint32_t  *) cmd_args;
+	csid_hw->csid_debug = *csid_debug;
+	CAM_DBG(CAM_ISP, "CSID:%d set csid debug value:%d",
+		csid_hw->hw_intf->hw_idx, csid_hw->csid_debug);
+
+	return 0;
+}
+
 static int cam_ife_csid_res_wait_for_halt(
 	struct cam_ife_csid_hw          *csid_hw,
 	struct cam_isp_resource_node    *res)
@@ -2387,6 +2545,9 @@
 	case CAM_IFE_CSID_CMD_GET_TIME_STAMP:
 		rc = cam_ife_csid_get_time_stamp(csid_hw, cmd_args);
 		break;
+	case CAM_IFE_CSID_SET_CSID_DEBUG:
+		rc = cam_ife_csid_set_csid_debug(csid_hw, cmd_args);
+		break;
 	default:
 		CAM_ERR(CAM_ISP, "CSID:%d un supported cmd:%d",
 			csid_hw->hw_intf->hw_idx, cmd_type);
@@ -2403,11 +2564,9 @@
 	struct cam_ife_csid_hw          *csid_hw;
 	struct cam_hw_soc_info          *soc_info;
 	struct cam_ife_csid_reg_offset  *csid_reg;
-	uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0,
-		irq_status_rdi[4];
-#if MEASURE_EN
+	uint32_t i, irq_status_top, irq_status_rx, irq_status_ipp = 0;
+	uint32_t irq_status_rdi[4] = {0, 0, 0, 0};
 	uint32_t val;
-#endif
 
 	csid_hw = (struct cam_ife_csid_hw *)data;
 
@@ -2453,8 +2612,12 @@
 	cam_io_w_mb(1, soc_info->reg_map[0].mem_base +
 		csid_reg->cmn_reg->csid_irq_cmd_addr);
 
+	CAM_DBG(CAM_ISP, "irq_status_top = 0x%x", irq_status_top);
 	CAM_DBG(CAM_ISP, "irq_status_rx = 0x%x", irq_status_rx);
 	CAM_DBG(CAM_ISP, "irq_status_ipp = 0x%x", irq_status_ipp);
+	CAM_DBG(CAM_ISP, "irq_status_rdi0= 0x%x", irq_status_rdi[0]);
+	CAM_DBG(CAM_ISP, "irq_status_rdi1= 0x%x", irq_status_rdi[1]);
+	CAM_DBG(CAM_ISP, "irq_status_rdi2= 0x%x", irq_status_rdi[2]);
 
 	if (irq_status_top) {
 		CAM_DBG(CAM_ISP, "CSID global reset complete......Exit");
@@ -2469,37 +2632,144 @@
 	}
 
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE0_FIFO_OVERFLOW) {
-		pr_err_ratelimited("CSID:%d lane 0 over flow",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 0 over flow",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE1_FIFO_OVERFLOW) {
-		pr_err_ratelimited("CSID:%d lane 1 over flow",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 1 over flow",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE2_FIFO_OVERFLOW) {
-		pr_err_ratelimited("CSID:%d lane 2 over flow",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 2 over flow",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_LANE3_FIFO_OVERFLOW) {
-		pr_err_ratelimited("CSID:%d lane 3 over flow",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d lane 3 over flow",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_TG_FIFO_OVERFLOW) {
-		pr_err_ratelimited("CSID:%d TG OVER  FLOW",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d TG OVER  FLOW",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_EOT_RECEPTION) {
-		pr_err_ratelimited("CSID:%d CPHY_EOT_RECEPTION",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_EOT_RECEPTION",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_SOT_RECEPTION) {
-		pr_err_ratelimited("CSID:%d CPHY_SOT_RECEPTION",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_SOT_RECEPTION",
 			 csid_hw->hw_intf->hw_idx);
 	}
 	if (irq_status_rx & CSID_CSI2_RX_ERROR_CPHY_PH_CRC) {
-		pr_err_ratelimited("CSID:%d CPHY_PH_CRC",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d CPHY_PH_CRC",
 			 csid_hw->hw_intf->hw_idx);
 	}
+	if (irq_status_rx & CSID_CSI2_RX_ERROR_CRC) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_CRC",
+			 csid_hw->hw_intf->hw_idx);
+	}
+	if (irq_status_rx & CSID_CSI2_RX_ERROR_ECC) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_ECC",
+			 csid_hw->hw_intf->hw_idx);
+	}
+	if (irq_status_rx & CSID_CSI2_RX_ERROR_MMAPPED_VC_DT) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d MMAPPED_VC_DT",
+			 csid_hw->hw_intf->hw_idx);
+	}
+		if (irq_status_rx & CSID_CSI2_RX_ERROR_STREAM_UNDERFLOW) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d ERROR_STREAM_UNDERFLOW",
+			 csid_hw->hw_intf->hw_idx);
+	}
+	if (irq_status_rx & CSID_CSI2_RX_ERROR_UNBOUNDED_FRAME) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "CSID:%d UNBOUNDED_FRAME",
+			 csid_hw->hw_intf->hw_idx);
+	}
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOT_IRQ) {
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_EOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+	}
+
+	if (csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOT_IRQ) {
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL0_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL1_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL1_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL2_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL2_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+		if (irq_status_rx & CSID_CSI2_RX_INFO_PHY_DL3_SOT_CAPTURED) {
+			CAM_ERR(CAM_ISP, "CSID:%d PHY_DL3_SOT_CAPTURED",
+				csid_hw->hw_intf->hw_idx);
+		}
+	}
+
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_LONG_PKT_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d LONG_PKT_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_0_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long packet VC :%d DT:%d WC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x3F), (val & 0xFFFF));
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_1_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long packet ECC :%d",
+			csid_hw->hw_intf->hw_idx, val);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_long_pkt_ftr_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d long pkt cal CRC:%d expected CRC:%d",
+			csid_hw->hw_intf->hw_idx, (val >> 16), (val & 0xFFFF));
+	}
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_SHORT_PKT_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d SHORT_PKT_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_short_pkt_0_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d short pkt VC :%d DT:%d LC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF));
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_short_pkt_1_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d short packet ECC :%d", val);
+	}
+
+	if ((csid_hw->csid_debug & CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE) &&
+		(irq_status_rx & CSID_CSI2_RX_INFO_CPHY_PKT_HDR_CAPTURED)) {
+		CAM_ERR(CAM_ISP, "CSID:%d CPHY_PKT_HDR_CAPTURED",
+			csid_hw->hw_intf->hw_idx);
+		val = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+			csid_reg->csi2_reg->
+			csid_csi2_rx_captured_cphy_pkt_hdr_addr);
+		CAM_ERR(CAM_ISP, "CSID:%d cphy packet VC :%d DT:%d WC:%d",
+			csid_hw->hw_intf->hw_idx,
+			(val >> 22), ((val >> 16) & 0x1F), (val & 0xFFFF));
+	}
 
 	/*read the IPP errors */
 	if (csid_reg->cmn_reg->no_pix) {
@@ -2509,14 +2779,16 @@
 			CAM_DBG(CAM_ISP, "CSID IPP reset complete");
 			complete(&csid_hw->csid_ipp_complete);
 		}
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_SOF)
-			CAM_DBG(CAM_ISP, "CSID IPP SOF received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_SOL)
-			CAM_DBG(CAM_ISP, "CSID IPP SOL received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOL)
-			CAM_DBG(CAM_ISP, "CSID IPP EOL received");
-		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOF)
-			CAM_DBG(CAM_ISP, "CSID IPP EOF received");
+
+		if ((irq_status_ipp & CSID_PATH_INFO_INPUT_SOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID:%d IPP SOF received",
+				csid_hw->hw_intf->hw_idx);
+
+		if ((irq_status_ipp & CSID_PATH_INFO_INPUT_EOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID:%d IPP EOF received",
+				csid_hw->hw_intf->hw_idx);
 
 		if (irq_status_ipp & CSID_PATH_INFO_INPUT_EOF)
 			complete(&csid_hw->csid_ipp_complete);
@@ -2534,21 +2806,17 @@
 	for (i = 0; i < csid_reg->cmn_reg->no_rdis; i++) {
 		if (irq_status_rdi[i] &
 			BIT(csid_reg->cmn_reg->path_rst_done_shift_val)) {
-			CAM_DBG(CAM_ISP, "CSID rdi%d reset complete", i);
+			CAM_DBG(CAM_ISP, "CSID RDI%d reset complete", i);
 			complete(&csid_hw->csid_rdin_complete[i]);
 		}
 
-		if (irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_SOF) {
-			CAM_DBG(CAM_ISP, "CSID RDI SOF received");
-#if MEASURE_EN
-			val = cam_io_r(soc_info->reg_map[0].mem_base +
-				csid_reg->rdi_reg[i]->
-				csid_rdi_format_measure0_addr);
-			CAM_ERR(CAM_ISP, "measure 0x%x\n", val);
-#endif
-		}
-		if (irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_EOF)
-			CAM_DBG(CAM_ISP, "CSID RDI EOF received");
+		if ((irq_status_rdi[i] & CSID_PATH_INFO_INPUT_SOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_SOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID RDI:%d SOF received", i);
+
+		if ((irq_status_rdi[i]  & CSID_PATH_INFO_INPUT_EOF) &&
+			(csid_hw->csid_debug & CSID_DEBUG_ENABLE_EOF_IRQ))
+			CAM_ERR(CAM_ISP, "CSID RDI:%d EOF received", i);
 
 		if (irq_status_rdi[i] & CSID_PATH_INFO_INPUT_EOF)
 			complete(&csid_hw->csid_rdin_complete[i]);
@@ -2676,6 +2944,7 @@
 		ife_csid_hw->rdi_res[i].res_priv = path_data;
 	}
 
+	ife_csid_hw->csid_debug = 0;
 	return 0;
 err:
 	if (rc) {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
index d5f032f..07217f5 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_core.h
@@ -22,7 +22,7 @@
 #define CAM_IFE_CSID_RDI_MAX         4
 
 #define CSID_CSI2_RX_INFO_PHY_DL0_EOT_CAPTURED    BIT(0)
-#define CSID_CSI2_RX_NFO_PHY_DL1_EOT_CAPTURED     BIT(1)
+#define CSID_CSI2_RX_INFO_PHY_DL1_EOT_CAPTURED    BIT(1)
 #define CSID_CSI2_RX_INFO_PHY_DL2_EOT_CAPTURED    BIT(2)
 #define CSID_CSI2_RX_INFO_PHY_DL3_EOT_CAPTURED    BIT(3)
 #define CSID_CSI2_RX_INFO_PHY_DL0_SOT_CAPTURED    BIT(4)
@@ -65,6 +65,18 @@
 #define CSID_PATH_ERROR_PIX_COUNT                 BIT(13)
 #define CSID_PATH_ERROR_LINE_COUNT                BIT(14)
 
+/*
+ * Debug values enable the corresponding interrupts and debug logs provide
+ * necessary information
+ */
+#define CSID_DEBUG_ENABLE_SOF_IRQ                 BIT(0)
+#define CSID_DEBUG_ENABLE_EOF_IRQ                 BIT(1)
+#define CSID_DEBUG_ENABLE_SOT_IRQ                 BIT(2)
+#define CSID_DEBUG_ENABLE_EOT_IRQ                 BIT(3)
+#define CSID_DEBUG_ENABLE_SHORT_PKT_CAPTURE       BIT(4)
+#define CSID_DEBUG_ENABLE_LONG_PKT_CAPTURE        BIT(5)
+#define CSID_DEBUG_ENABLE_CPHY_PKT_CAPTURE        BIT(6)
+
 /* enum cam_csid_path_halt_mode select the path halt mode control */
 enum cam_csid_path_halt_mode {
 	CSID_HALT_MODE_INTERNAL,
@@ -187,7 +199,7 @@
 	uint32_t csid_csi2_rx_captured_long_pkt_0_addr;
 	uint32_t csid_csi2_rx_captured_long_pkt_1_addr;
 	uint32_t csid_csi2_rx_captured_long_pkt_ftr_addr;
-	uint32_t csid_csi2_rx_captured_cphy_pkt_ftr_addr;
+	uint32_t csid_csi2_rx_captured_cphy_pkt_hdr_addr;
 	uint32_t csid_csi2_rx_lane0_misr_addr;
 	uint32_t csid_csi2_rx_lane1_misr_addr;
 	uint32_t csid_csi2_rx_lane2_misr_addr;
@@ -202,6 +214,14 @@
 	uint32_t csi2_irq_mask_all;
 	uint32_t csi2_misr_enable_shift_val;
 	uint32_t csi2_vc_mode_shift_val;
+	uint32_t csi2_capture_long_pkt_en_shift;
+	uint32_t csi2_capture_short_pkt_en_shift;
+	uint32_t csi2_capture_cphy_pkt_en_shift;
+	uint32_t csi2_capture_long_pkt_dt_shift;
+	uint32_t csi2_capture_long_pkt_vc_shift;
+	uint32_t csi2_capture_short_pkt_vc_shift;
+	uint32_t csi2_capture_cphy_pkt_dt_shift;
+	uint32_t csi2_capture_cphy_pkt_vc_shift;
 };
 
 struct cam_ife_csid_csi2_tpg_reg_offset {
@@ -226,6 +246,7 @@
 	/*configurations */
 	uint32_t tpg_dtn_cfg_offset;
 	uint32_t tpg_cgen_cfg_offset;
+	uint32_t tpg_cpas_ife_reg_offset;
 
 };
 struct cam_ife_csid_common_reg_offset {
@@ -321,6 +342,7 @@
  * @height:           height
  * @test_pattern :    pattern
  * @in_format:        decode format
+ * @usage_type:       whether dual isp is required
  *
  */
 struct cam_ife_csid_tpg_cfg  {
@@ -328,6 +350,7 @@
 	uint32_t                        height;
 	uint32_t                        test_pattern;
 	uint32_t                        in_format;
+	uint32_t                        usage_type;
 };
 
 /**
@@ -358,8 +381,10 @@
  * @crop_enable:    crop is enable or disabled, if enabled
  *                  then remaining parameters are valid.
  * @start_pixel:    start pixel
+ * @end_pixel:      end_pixel
  * @width:          width
  * @start_line:     start line
+ * @end_line:       end_line
  * @height:         heigth
  * @sync_mode:       Applicable for IPP/RDI path reservation
  *                  Reserving the path for master IPP or slave IPP
@@ -367,6 +392,7 @@
  *                  for RDI, set  mode to none
  * @master_idx:     For Slave reservation, Give master IFE instance Index.
  *                  Slave will synchronize with master Start and stop operations
+ * @clk_rate        Clock rate
  *
  */
 struct cam_ife_csid_path_cfg {
@@ -377,11 +403,14 @@
 	uint32_t                        out_format;
 	bool                            crop_enable;
 	uint32_t                        start_pixel;
+	uint32_t                        end_pixel;
 	uint32_t                        width;
 	uint32_t                        start_line;
+	uint32_t                        end_line;
 	uint32_t                        height;
 	enum cam_isp_hw_sync_mode       sync_mode;
 	uint32_t                        master_idx;
+	uint64_t                        clk_rate;
 };
 
 /**
@@ -403,6 +432,9 @@
  * @csid_csi2_reset_complete: csi2 reset completion
  * @csid_ipp_reset_complete:  ipp reset completion
  * @csid_rdin_reset_complete: rdi n completion
+ * @csid_debug:               csid debug information to enable the SOT, EOT,
+ *                            SOF, EOF, measure etc in the csid hw
+ * @clk_rate                  Clock rate
  *
  */
 struct cam_ife_csid_hw {
@@ -422,6 +454,8 @@
 	struct completion                csid_csi2_complete;
 	struct completion                csid_ipp_complete;
 	struct completion    csid_rdin_complete[CAM_IFE_CSID_RDI_MAX];
+	uint64_t                         csid_debug;
+	uint64_t                         clk_rate;
 };
 
 int cam_ife_csid_hw_probe_init(struct cam_hw_intf  *csid_hw_intf,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
index e857f8b..952426d 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_lite170.h
@@ -210,7 +210,7 @@
 	.csid_csi2_rx_captured_long_pkt_0_addr        = 0x130,
 	.csid_csi2_rx_captured_long_pkt_1_addr        = 0x134,
 	.csid_csi2_rx_captured_long_pkt_ftr_addr      = 0x138,
-	.csid_csi2_rx_captured_cphy_pkt_ftr_addr      = 0x13c,
+	.csid_csi2_rx_captured_cphy_pkt_hdr_addr      = 0x13c,
 	.csid_csi2_rx_lane0_misr_addr                 = 0x150,
 	.csid_csi2_rx_lane1_misr_addr                 = 0x154,
 	.csid_csi2_rx_lane2_misr_addr                 = 0x158,
@@ -224,6 +224,14 @@
 	.csi2_irq_mask_all                            = 0xFFFFFFF,
 	.csi2_misr_enable_shift_val                   = 6,
 	.csi2_vc_mode_shift_val                       = 2,
+	.csi2_capture_long_pkt_en_shift               = 0,
+	.csi2_capture_short_pkt_en_shift              = 1,
+	.csi2_capture_cphy_pkt_en_shift               = 2,
+	.csi2_capture_long_pkt_dt_shift               = 4,
+	.csi2_capture_long_pkt_vc_shift               = 10,
+	.csi2_capture_short_pkt_vc_shift              = 15,
+	.csi2_capture_cphy_pkt_dt_shift               = 20,
+	.csi2_capture_cphy_pkt_vc_shift               = 26,
 };
 
 
@@ -252,6 +260,7 @@
 	/*configurations */
 	.tpg_dtn_cfg_offset                           = 0xc,
 	.tpg_cgen_cfg_offset                          = 0x20,
+	.tpg_cpas_ife_reg_offset                      = 0x28,
 };
 
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
index 22c11d3..e11ff63 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.c
@@ -129,8 +129,8 @@
 
 	ahb_vote.type = CAM_VOTE_ABSOLUTE;
 	ahb_vote.vote.level = CAM_SVS_VOTE;
-	axi_vote.compressed_bw = 640000000;
-	axi_vote.uncompressed_bw = 640000000;
+	axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
+	axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
 
 	CAM_DBG(CAM_ISP, "csid vote compressed_bw:%lld uncompressed_bw:%lld",
 		axi_vote.compressed_bw, axi_vote.uncompressed_bw);
@@ -143,7 +143,7 @@
 	}
 
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, true);
+		CAM_SVS_VOTE, true);
 	if (rc) {
 		CAM_ERR(CAM_ISP, "enable platform failed");
 		goto stop_cpas;
@@ -181,3 +181,59 @@
 	return rc;
 }
 
+int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info  *soc_info,
+	uint32_t cpas_ife_base_offset)
+{
+	int rc = 0;
+	struct cam_csid_soc_private       *soc_private;
+	uint32_t                           cpass_ife_force_clk_offset;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		return -EINVAL;
+	}
+
+	soc_private = soc_info->soc_private;
+	cpass_ife_force_clk_offset =
+		cpas_ife_base_offset + (0x4 * soc_info->index);
+	rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP,
+		cpass_ife_force_clk_offset, 1, 1);
+
+	if (rc)
+		CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock On failed",
+			soc_info->index);
+	else
+		CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock On",
+		soc_info->index);
+
+	return rc;
+}
+
+int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info,
+	uint32_t cpas_ife_base_offset)
+{
+	int rc = 0;
+	struct cam_csid_soc_private       *soc_private;
+	uint32_t                           cpass_ife_force_clk_offset;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		return -EINVAL;
+	}
+
+	soc_private = soc_info->soc_private;
+	cpass_ife_force_clk_offset =
+		cpas_ife_base_offset + (0x4 * soc_info->index);
+	rc = cam_cpas_reg_write(soc_private->cpas_handle, CAM_CPAS_REG_CPASTOP,
+		cpass_ife_force_clk_offset,  1, 0);
+
+	if (rc)
+		CAM_ERR(CAM_ISP, "CPASS set IFE:%d Force clock Off failed",
+			soc_info->index);
+	else
+		CAM_DBG(CAM_ISP, "CPASS set IFE:%d Force clock off",
+		soc_info->index);
+
+	return rc;
+}
+
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
index 1a30722..8e963de 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/ife_csid_hw/cam_ife_csid_soc.h
@@ -82,4 +82,33 @@
  */
 int cam_ife_csid_disable_soc_resources(struct cam_hw_soc_info *soc_info);
 
+/**
+ * cam_ife_csid_enable_ife_force_clock()
+ *
+ * @brief:                 if csid testgen used for dual isp case, before
+ *                         starting csid test gen, enable ife force clock on
+ *                         through cpas
+ *
+ * @soc_info:              soc info structure pointer
+ * @cpas_ife_base_offset:  cpas ife force clock base reg offset value
+ *
+ */
+int cam_ife_csid_enable_ife_force_clock_on(struct cam_hw_soc_info  *soc_info,
+	uint32_t cpas_ife_base_offset);
+
+/**
+ * cam_ife_csid_disable_ife_force_clock_on()
+ *
+ * @brief:                 disable the IFE force clock on after dual ISP
+ *                         CSID test gen stop
+ *
+ * @soc_info:              soc info structure pointer
+ * @cpas_ife_base_offset:  cpas ife force clock base reg offset value
+ *
+ */
+int cam_ife_csid_disable_ife_force_clock_on(struct cam_hw_soc_info *soc_info,
+	uint32_t cpas_ife_base_offset);
+
+
+
 #endif
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
index a70707a..37e0ce3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_ife_csid_hw_intf.h
@@ -151,6 +151,7 @@
  */
 enum cam_ife_csid_cmd_type {
 	CAM_IFE_CSID_CMD_GET_TIME_STAMP,
+	CAM_IFE_CSID_SET_CSID_DEBUG,
 	CAM_IFE_CSID_CMD_MAX,
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
index 60f1c8b..257a5ac 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_isp_hw.h
@@ -15,8 +15,10 @@
 
 #include <linux/completion.h>
 #include "cam_hw.h"
+#include <uapi/media/cam_isp.h>
 #include "cam_soc_util.h"
 #include "cam_irq_controller.h"
+#include <uapi/media/cam_isp.h>
 
 /*
  * struct cam_isp_timestamp:
@@ -81,6 +83,18 @@
 	CAM_ISP_RESOURCE_MAX,
 };
 
+enum cam_isp_hw_cmd_type {
+	CAM_ISP_HW_CMD_GET_CHANGE_BASE,
+	CAM_ISP_HW_CMD_GET_BUF_UPDATE,
+	CAM_ISP_HW_CMD_GET_REG_UPDATE,
+	CAM_ISP_HW_CMD_GET_HFR_UPDATE,
+	CAM_ISP_HW_CMD_GET_SECURE_MODE,
+	CAM_ISP_HW_CMD_STRIPE_UPDATE,
+	CAM_ISP_HW_CMD_CLOCK_UPDATE,
+	CAM_ISP_HW_CMD_BW_UPDATE,
+	CAM_ISP_HW_CMD_MAX,
+};
+
 /*
  * struct cam_isp_resource_node:
  *
@@ -98,6 +112,9 @@
  * @tasklet_info:                 Tasklet structure that will be used to
  *                                schedule IRQ events related to this resource
  * @irq_handle:                   handle returned on subscribing for IRQ event
+ * @rdi_only_ctx:                 resouce belong to rdi only context or not
+ * @init:                         function pointer to init the HW resource
+ * @deinit:                       function pointer to deinit the HW resource
  * @start:                        function pointer to start the HW resource
  * @stop:                         function pointer to stop the HW resource
  * @process_cmd:                  function pointer for processing commands
@@ -115,7 +132,12 @@
 	void                          *cdm_ops;
 	void                          *tasklet_info;
 	int                            irq_handle;
+	int                            rdi_only_ctx;
 
+	int (*init)(struct cam_isp_resource_node *rsrc_node,
+		void *init_args, uint32_t arg_size);
+	int (*deinit)(struct cam_isp_resource_node *rsrc_node,
+		void *deinit_args, uint32_t arg_size);
 	int (*start)(struct cam_isp_resource_node *rsrc_node);
 	int (*stop)(struct cam_isp_resource_node *rsrc_node);
 	int (*process_cmd)(struct cam_isp_resource_node *rsrc_node,
@@ -125,54 +147,73 @@
 };
 
 /*
- * struct cam_isp_hw_get_cdm_args:
+ * struct cam_isp_hw_cmd_buf_update:
  *
- * @Brief:           Contain the command buffer information
- *                   to store the CDM commands.
+ * @Brief:           Contain the new created command buffer information
  *
- * @res:             Resource node
  * @cmd_buf_addr:    Command buffer to store the change base command
  * @size:            Size of the buffer in bytes
  * @used_bytes:      Consumed bytes in the command buffer
  *
  */
-struct cam_isp_hw_get_cdm_args {
-	struct cam_isp_resource_node   *res;
+struct cam_isp_hw_cmd_buf_update {
 	uint32_t                       *cmd_buf_addr;
 	uint32_t                        size;
 	uint32_t                        used_bytes;
 };
 
 /*
- * struct cam_isp_hw_get_buf_update:
+ * struct cam_isp_hw_get_wm_update:
  *
- * @Brief:         Get cdm commands for buffer updates.
+ * @Brief:         Get cmd buffer for WM updates.
  *
- * @ cdm:          Command buffer information
  * @ image_buf:    image buffer address array
  * @ num_buf:      Number of buffers in the image_buf array
  * @ io_cfg:       IO buffer config information sent from UMD
  *
  */
-struct cam_isp_hw_get_buf_update {
-	struct cam_isp_hw_get_cdm_args  cdm;
+struct cam_isp_hw_get_wm_update {
 	uint64_t                       *image_buf;
 	uint32_t                        num_buf;
 	struct cam_buf_io_cfg          *io_cfg;
 };
 
 /*
- * struct cam_isp_hw_get_hfr_update:
+ * struct cam_isp_hw_get_cmd_update:
  *
- * @Brief:         Get cdm commands for HFR updates.
+ * @Brief:         Get cmd buffer update for different CMD types
  *
- * @ cdm:          Command buffer information
- * @ io_hfr_cfg:   IO buffer config information sent from UMD
+ * @res:           Resource node
+ * @cmd_type:      Command type for which to get update
+ * @cmd:           Command buffer information
  *
  */
-struct cam_isp_hw_get_hfr_update {
-	struct cam_isp_hw_get_cdm_args  cdm;
-	struct cam_isp_port_hfr_config *io_hfr_cfg;
+struct cam_isp_hw_get_cmd_update {
+	struct cam_isp_resource_node     *res;
+	enum cam_isp_hw_cmd_type          cmd_type;
+	struct cam_isp_hw_cmd_buf_update  cmd;
+	union {
+		void                                 *data;
+		struct cam_isp_hw_get_wm_update      *wm_update;
+		struct cam_isp_port_hfr_config       *hfr_update;
+		struct cam_isp_clock_config          *clock_update;
+		struct cam_isp_bw_config             *bw_update;
+	};
 };
 
+/*
+ * struct cam_isp_hw_dual_isp_update_args:
+ *
+ * @Brief:        update the dual isp striping configuration.
+ *
+ * @ split_id:    spilt id to inform left or rifht
+ * @ res:         resource node
+ * @ dual_cfg:    dual isp configuration
+ *
+ */
+struct cam_isp_hw_dual_isp_update_args {
+	enum cam_isp_hw_split_id         split_id;
+	struct cam_isp_resource_node    *res;
+	struct cam_isp_dual_config      *dual_cfg;
+};
 #endif /* _CAM_ISP_HW_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
index a08248d..b771ec6 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include/cam_vfe_hw_intf.h
@@ -44,14 +44,6 @@
 	CAM_ISP_HW_VFE_CORE_MAX,
 };
 
-enum cam_vfe_hw_cmd_type {
-	CAM_VFE_HW_CMD_GET_CHANGE_BASE,
-	CAM_VFE_HW_CMD_GET_BUF_UPDATE,
-	CAM_VFE_HW_CMD_GET_REG_UPDATE,
-	CAM_VFE_HW_CMD_GET_HFR_UPDATE,
-	CAM_VFE_HW_CMD_MAX,
-};
-
 enum cam_vfe_hw_irq_status {
 	CAM_VFE_IRQ_STATUS_ERR_COMP             = -3,
 	CAM_VFE_IRQ_STATUS_COMP_OWRT            = -2,
@@ -78,6 +70,12 @@
 	CAM_IFE_BUS_IRQ_REGISTERS_MAX,
 };
 
+enum cam_vfe_reset_type {
+	CAM_VFE_HW_RESET_HW_AND_REG,
+	CAM_VFE_HW_RESET_HW,
+	CAM_VFE_HW_RESET_MAX,
+};
+
 /*
  * struct cam_vfe_hw_get_hw_cap:
  *
@@ -163,6 +161,31 @@
 };
 
 /*
+ * struct cam_vfe_clock_update_args:
+ *
+ * @node_res:                Resource to get the time stamp
+ * @clk_rate:                Clock rate requested
+ */
+struct cam_vfe_clock_update_args {
+	struct cam_isp_resource_node      *node_res;
+	uint64_t                           clk_rate;
+};
+
+/*
+ * struct cam_vfe_bw_update_args:
+ *
+ * @node_res:             Resource to get the time stamp
+ * @camnoc_bw_bytes:      Bandwidth vote request for CAMNOC
+ * @external_bw_bytes:    Bandwidth vote request from CAMNOC
+ *                        out to the rest of the path-to-DDR
+ */
+struct cam_vfe_bw_update_args {
+	struct cam_isp_resource_node      *node_res;
+	uint64_t                           camnoc_bw_bytes;
+	uint64_t                           external_bw_bytes;
+};
+
+/*
  * struct cam_vfe_top_irq_evt_payload:
  *
  * @Brief:                   This structure is used to save payload for IRQ
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
index 89db01d..187aeaf 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.c
@@ -25,7 +25,6 @@
 #include "cam_debug_util.h"
 
 static const char drv_name[] = "vfe";
-
 static uint32_t irq_reg_offset[CAM_IFE_IRQ_REGISTERS_MAX] = {
 	0x0000006C,
 	0x00000070,
@@ -34,7 +33,12 @@
 
 static uint32_t camif_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
 	0x0003FD1F,
-	0x0FFF7EB3,
+	0x00000000,
+};
+
+static uint32_t camif_irq_err_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
+	0x00000000,
+	0x0FFF7EBC,
 };
 
 static uint32_t rdi_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
@@ -83,6 +87,7 @@
 	}
 
 	spin_lock_irqsave(&vfe_core_info->spin_lock, flags);
+	(*evt_payload)->error_type = 0;
 	list_add_tail(&(*evt_payload)->list, &vfe_core_info->free_payload_list);
 	spin_unlock_irqrestore(&vfe_core_info->spin_lock, flags);
 
@@ -125,9 +130,6 @@
 	CAM_DBG(CAM_ISP, "IRQ status_0 = 0x%x", th_payload->evt_status_arr[0]);
 
 	if (th_payload->evt_status_arr[0] & (1<<31)) {
-		CAM_DBG(CAM_ISP, "Calling Complete for RESET CMD");
-		complete(handler_priv->reset_complete);
-
 		/*
 		 * Clear All IRQs to avoid spurious IRQs immediately
 		 * after Reset Done.
@@ -135,6 +137,9 @@
 		cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x64);
 		cam_io_w(0xFFFFFFFF, handler_priv->mem_base + 0x68);
 		cam_io_w(0x1, handler_priv->mem_base + 0x58);
+		CAM_DBG(CAM_ISP, "Calling Complete for RESET CMD");
+		complete(handler_priv->reset_complete);
+
 
 		rc = 0;
 	}
@@ -143,12 +148,69 @@
 	return rc;
 }
 
+static int cam_vfe_irq_err_top_half(uint32_t    evt_id,
+	struct cam_irq_th_payload   *th_payload)
+{
+	int32_t                              rc;
+	int                                  i;
+	struct cam_vfe_irq_handler_priv     *handler_priv;
+	struct cam_vfe_top_irq_evt_payload  *evt_payload;
+	struct cam_vfe_hw_core_info         *core_info;
+
+	CAM_DBG(CAM_ISP, "IRQ status_0 = %x, IRQ status_1 = %x",
+		th_payload->evt_status_arr[0], th_payload->evt_status_arr[1]);
+
+	handler_priv = th_payload->handler_priv;
+	core_info =  handler_priv->core_info;
+	/*
+	 *  need to handle overflow condition here, otherwise irq storm
+	 *  will block everything
+	 */
+
+	if (th_payload->evt_status_arr[1]) {
+		CAM_ERR(CAM_ISP, "IRQ status_1: %x, Masking all interrupts",
+			th_payload->evt_status_arr[1]);
+		cam_irq_controller_disable_irq(core_info->vfe_irq_controller,
+			core_info->irq_err_handle);
+	}
+
+	rc  = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload);
+	if (rc) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"No tasklet_cmd is free in queue\n");
+		return rc;
+	}
+
+	cam_isp_hw_get_timestamp(&evt_payload->ts);
+
+	evt_payload->core_index = handler_priv->core_index;
+	evt_payload->core_info  = handler_priv->core_info;
+	evt_payload->evt_id  = evt_id;
+
+	for (i = 0; i < th_payload->num_registers; i++)
+		evt_payload->irq_reg_val[i] = th_payload->evt_status_arr[i];
+
+	for (; i < CAM_IFE_IRQ_REGISTERS_MAX; i++) {
+		evt_payload->irq_reg_val[i] = cam_io_r(handler_priv->mem_base +
+			irq_reg_offset[i]);
+	}
+
+	CAM_DBG(CAM_ISP, "Violation status = %x", evt_payload->irq_reg_val[2]);
+
+	th_payload->evt_payload_priv = evt_payload;
+
+	return rc;
+}
+
 int cam_vfe_init_hw(void *hw_priv, void *init_hw_args, uint32_t arg_size)
 {
 	struct cam_hw_info                *vfe_hw = hw_priv;
 	struct cam_hw_soc_info            *soc_info = NULL;
 	struct cam_vfe_hw_core_info       *core_info = NULL;
+	struct cam_isp_resource_node      *isp_res = NULL;
 	int rc = 0;
+	uint32_t                           reset_core_args =
+					CAM_VFE_HW_RESET_HW_AND_REG;
 
 	CAM_DBG(CAM_ISP, "Enter");
 	if (!hw_priv) {
@@ -177,27 +239,37 @@
 		goto decrement_open_cnt;
 	}
 
+	isp_res   = (struct cam_isp_resource_node *)init_hw_args;
+	if (isp_res && isp_res->init) {
+		rc = isp_res->init(isp_res, NULL, 0);
+		if (rc) {
+			CAM_ERR(CAM_ISP, "init Failed rc=%d", rc);
+			goto disable_soc;
+		}
+	}
+
 	CAM_DBG(CAM_ISP, "Enable soc done");
 
+	/* Do HW Reset */
+	rc = cam_vfe_reset(hw_priv, &reset_core_args, sizeof(uint32_t));
+	if (rc) {
+		CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc);
+		goto deinint_vfe_res;
+	}
+
 	rc = core_info->vfe_bus->hw_ops.init(core_info->vfe_bus->bus_priv,
 		NULL, 0);
 	if (rc) {
 		CAM_ERR(CAM_ISP, "Bus HW init Failed rc=%d", rc);
-		goto disable_soc;
+		goto deinint_vfe_res;
 	}
 
-	/* Do HW Reset */
-	rc = cam_vfe_reset(hw_priv, NULL, 0);
-	if (rc) {
-		CAM_ERR(CAM_ISP, "Reset Failed rc=%d", rc);
-		goto deinit_bus;
-	}
+	vfe_hw->hw_state = CAM_HW_STATE_POWER_UP;
+	return rc;
 
-	return 0;
-
-deinit_bus:
-	core_info->vfe_bus->hw_ops.deinit(core_info->vfe_bus->bus_priv,
-		NULL, 0);
+deinint_vfe_res:
+	if (isp_res && isp_res->deinit)
+		isp_res->deinit(isp_res, NULL, 0);
 disable_soc:
 	cam_vfe_disable_soc_resources(soc_info);
 decrement_open_cnt:
@@ -211,6 +283,8 @@
 {
 	struct cam_hw_info                *vfe_hw = hw_priv;
 	struct cam_hw_soc_info            *soc_info = NULL;
+	struct cam_vfe_hw_core_info       *core_info = NULL;
+	struct cam_isp_resource_node      *isp_res = NULL;
 	int rc = 0;
 
 	CAM_DBG(CAM_ISP, "Enter");
@@ -234,6 +308,19 @@
 	mutex_unlock(&vfe_hw->hw_mutex);
 
 	soc_info = &vfe_hw->soc_info;
+	core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
+
+	rc = core_info->vfe_bus->hw_ops.deinit(core_info->vfe_bus->bus_priv,
+		NULL, 0);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Bus HW deinit Failed rc=%d", rc);
+
+	isp_res   = (struct cam_isp_resource_node *)deinit_hw_args;
+	if (isp_res && isp_res->deinit) {
+		rc = isp_res->deinit(isp_res, NULL, 0);
+		if (rc)
+			CAM_ERR(CAM_ISP, "deinit failed");
+	}
 
 	/* Turn OFF Regulators, Clocks and other SOC resources */
 	CAM_DBG(CAM_ISP, "Disable SOC resource");
@@ -282,7 +369,8 @@
 	reinit_completion(&vfe_hw->hw_complete);
 
 	CAM_DBG(CAM_ISP, "calling RESET");
-	core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv, NULL, 0);
+	core_info->vfe_top->hw_ops.reset(core_info->vfe_top->top_priv,
+		reset_core_args, arg_size);
 	CAM_DBG(CAM_ISP, "waiting for vfe reset complete");
 	/* Wait for Completion or Timeout of 500ms */
 	rc = wait_for_completion_timeout(&vfe_hw->hw_complete, 500);
@@ -309,20 +397,37 @@
 	time_stamp->mono_time.tv_usec   = ts.tv_nsec/1000;
 }
 
-
-int cam_vfe_irq_top_half(uint32_t    evt_id,
+static int cam_vfe_irq_top_half(uint32_t    evt_id,
 	struct cam_irq_th_payload   *th_payload)
 {
 	int32_t                              rc;
 	int                                  i;
 	struct cam_vfe_irq_handler_priv     *handler_priv;
 	struct cam_vfe_top_irq_evt_payload  *evt_payload;
+	struct cam_vfe_hw_core_info         *core_info;
 
 	handler_priv = th_payload->handler_priv;
 
 	CAM_DBG(CAM_ISP, "IRQ status_0 = %x", th_payload->evt_status_arr[0]);
 	CAM_DBG(CAM_ISP, "IRQ status_1 = %x", th_payload->evt_status_arr[1]);
 
+	/*
+	 *  need to handle non-recoverable condition here, otherwise irq storm
+	 *  will block everything.
+	 */
+	if (th_payload->evt_status_arr[0] & 0x3FC00) {
+		CAM_ERR(CAM_ISP,
+			"Encountered Error Irq_status0=0x%x Status1=0x%x",
+			th_payload->evt_status_arr[0],
+			th_payload->evt_status_arr[1]);
+		CAM_ERR(CAM_ISP,
+			"Stopping further IRQ processing from this HW index=%d",
+			handler_priv->core_index);
+		cam_io_w(0, handler_priv->mem_base + 0x60);
+		cam_io_w(0, handler_priv->mem_base + 0x5C);
+		return 0;
+	}
+
 	rc  = cam_vfe_get_evt_payload(handler_priv->core_info, &evt_payload);
 	if (rc) {
 		CAM_ERR_RATE_LIMIT(CAM_ISP,
@@ -330,6 +435,7 @@
 		return rc;
 	}
 
+	core_info =  handler_priv->core_info;
 	cam_isp_hw_get_timestamp(&evt_payload->ts);
 
 	evt_payload->core_index = handler_priv->core_index;
@@ -345,22 +451,6 @@
 	}
 	CAM_DBG(CAM_ISP, "Violation status = %x", evt_payload->irq_reg_val[2]);
 
-	/*
-	 *  need to handle overflow condition here, otherwise irq storm
-	 *  will block everything.
-	 */
-	if (evt_payload->irq_reg_val[1]) {
-		CAM_ERR(CAM_ISP,
-			"Encountered Error Irq_status1=0x%x. Stopping further IRQ processing from this HW",
-			evt_payload->irq_reg_val[1]);
-		CAM_ERR(CAM_ISP, "Violation status = %x",
-			evt_payload->irq_reg_val[2]);
-		cam_io_w(0, handler_priv->mem_base + 0x60);
-		cam_io_w(0, handler_priv->mem_base + 0x5C);
-
-		evt_payload->error_type = CAM_ISP_HW_ERROR_OVERFLOW;
-	}
-
 	th_payload->evt_payload_priv = evt_payload;
 
 	CAM_DBG(CAM_ISP, "Exit");
@@ -441,7 +531,7 @@
 	struct cam_vfe_hw_core_info       *core_info = NULL;
 	struct cam_hw_info                *vfe_hw  = hw_priv;
 	struct cam_isp_resource_node      *isp_res;
-	int rc = -ENODEV;
+	int rc = 0;
 
 	if (!hw_priv || !start_args ||
 		(arg_size != sizeof(struct cam_isp_resource_node))) {
@@ -451,35 +541,72 @@
 
 	core_info = (struct cam_vfe_hw_core_info *)vfe_hw->core_info;
 	isp_res = (struct cam_isp_resource_node  *)start_args;
+	core_info->tasklet_info = isp_res->tasklet_info;
 
 	mutex_lock(&vfe_hw->hw_mutex);
 	if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_IN) {
-		if (isp_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF)
-			isp_res->irq_handle = cam_irq_controller_subscribe_irq(
-				core_info->vfe_irq_controller,
-				CAM_IRQ_PRIORITY_1,
-				camif_irq_reg_mask, &core_info->irq_payload,
-				cam_vfe_irq_top_half, cam_ife_mgr_do_tasklet,
-				isp_res->tasklet_info, cam_tasklet_enqueue_cmd);
-		else
-			isp_res->irq_handle = cam_irq_controller_subscribe_irq(
-				core_info->vfe_irq_controller,
-				CAM_IRQ_PRIORITY_1,
-				rdi_irq_reg_mask, &core_info->irq_payload,
-				cam_vfe_irq_top_half, cam_ife_mgr_do_tasklet,
-				isp_res->tasklet_info, cam_tasklet_enqueue_cmd);
+		if (isp_res->res_id == CAM_ISP_HW_VFE_IN_CAMIF) {
+			isp_res->irq_handle =
+				cam_irq_controller_subscribe_irq(
+					core_info->vfe_irq_controller,
+					CAM_IRQ_PRIORITY_1,
+					camif_irq_reg_mask,
+					&core_info->irq_payload,
+					cam_vfe_irq_top_half,
+					cam_ife_mgr_do_tasklet,
+					isp_res->tasklet_info,
+					cam_tasklet_enqueue_cmd);
+			if (isp_res->irq_handle < 1)
+				rc = -ENOMEM;
+		} else if (isp_res->rdi_only_ctx) {
+			isp_res->irq_handle =
+				cam_irq_controller_subscribe_irq(
+					core_info->vfe_irq_controller,
+					CAM_IRQ_PRIORITY_1,
+					rdi_irq_reg_mask,
+					&core_info->irq_payload,
+					cam_vfe_irq_top_half,
+					cam_ife_mgr_do_tasklet,
+					isp_res->tasklet_info,
+					cam_tasklet_enqueue_cmd);
+			if (isp_res->irq_handle < 1)
+				rc = -ENOMEM;
+		}
 
-		if (isp_res->irq_handle > 0)
+		if (rc == 0) {
 			rc = core_info->vfe_top->hw_ops.start(
 				core_info->vfe_top->top_priv, isp_res,
 				sizeof(struct cam_isp_resource_node));
-		else
+			if (rc)
+				CAM_ERR(CAM_ISP, "Start failed. type:%d",
+					isp_res->res_type);
+		} else {
 			CAM_ERR(CAM_ISP,
 				"Error! subscribe irq controller failed");
+		}
 	} else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
 		rc = core_info->vfe_bus->hw_ops.start(isp_res, NULL, 0);
 	} else {
 		CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type);
+		rc = -EFAULT;
+	}
+
+	if (!core_info->irq_err_handle) {
+		core_info->irq_err_handle =
+			cam_irq_controller_subscribe_irq(
+				core_info->vfe_irq_controller,
+				CAM_IRQ_PRIORITY_0,
+				camif_irq_err_reg_mask,
+				&core_info->irq_payload,
+				cam_vfe_irq_err_top_half,
+				cam_ife_mgr_do_tasklet,
+				core_info->tasklet_info,
+				cam_tasklet_enqueue_cmd);
+		if (core_info->irq_err_handle < 1) {
+			CAM_ERR(CAM_ISP, "Error handle subscribe failure");
+			rc = -ENOMEM;
+			core_info->irq_err_handle = 0;
+		}
 	}
 
 	mutex_unlock(&vfe_hw->hw_mutex);
@@ -510,14 +637,20 @@
 		rc = core_info->vfe_top->hw_ops.stop(
 			core_info->vfe_top->top_priv, isp_res,
 			sizeof(struct cam_isp_resource_node));
+
 	} else if (isp_res->res_type == CAM_ISP_RESOURCE_VFE_OUT) {
-		cam_irq_controller_unsubscribe_irq(
-			core_info->vfe_irq_controller, isp_res->irq_handle);
 		rc = core_info->vfe_bus->hw_ops.stop(isp_res, NULL, 0);
 	} else {
 		CAM_ERR(CAM_ISP, "Invalid res type:%d", isp_res->res_type);
 	}
 
+	if (core_info->irq_err_handle) {
+		cam_irq_controller_unsubscribe_irq(
+			core_info->vfe_irq_controller,
+			core_info->irq_err_handle);
+		core_info->irq_err_handle = 0;
+	}
+
 	mutex_unlock(&vfe_hw->hw_mutex);
 
 	return rc;
@@ -552,15 +685,17 @@
 	hw_info = core_info->vfe_hw_info;
 
 	switch (cmd_type) {
-	case CAM_VFE_HW_CMD_GET_CHANGE_BASE:
-	case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+	case CAM_ISP_HW_CMD_GET_CHANGE_BASE:
+	case CAM_ISP_HW_CMD_GET_REG_UPDATE:
+	case CAM_ISP_HW_CMD_CLOCK_UPDATE:
+	case CAM_ISP_HW_CMD_BW_UPDATE:
 		rc = core_info->vfe_top->hw_ops.process_cmd(
 			core_info->vfe_top->top_priv, cmd_type, cmd_args,
 			arg_size);
-
 		break;
-	case CAM_VFE_HW_CMD_GET_BUF_UPDATE:
-	case CAM_VFE_HW_CMD_GET_HFR_UPDATE:
+	case CAM_ISP_HW_CMD_GET_BUF_UPDATE:
+	case CAM_ISP_HW_CMD_GET_HFR_UPDATE:
+	case CAM_ISP_HW_CMD_STRIPE_UPDATE:
 		rc = core_info->vfe_bus->hw_ops.process_cmd(
 			core_info->vfe_bus->bus_priv, cmd_type, cmd_args,
 			arg_size);
@@ -676,4 +811,3 @@
 
 	return rc;
 }
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
index ee29e1cf..0674a6ad 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_core.h
@@ -50,12 +50,13 @@
 	void                               *vfe_irq_controller;
 	struct cam_vfe_top                 *vfe_top;
 	struct cam_vfe_bus                 *vfe_bus;
-
+	void                               *tasklet_info;
 	struct cam_vfe_top_irq_evt_payload  evt_payload[CAM_VFE_EVT_MAX];
 	struct list_head                    free_payload_list;
 	struct cam_vfe_irq_handler_priv     irq_payload;
 	uint32_t                            cpas_handle;
 	int                                 irq_handle;
+	int                                 irq_err_handle;
 	spinlock_t                          spin_lock;
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
index b5ca432..0f93664 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.c
@@ -15,6 +15,30 @@
 #include "cam_vfe_soc.h"
 #include "cam_debug_util.h"
 
+static bool cam_vfe_cpas_cb(uint32_t client_handle, void *userdata,
+	struct cam_cpas_irq_data *irq_data)
+{
+	bool error_handled = false;
+
+	if (!irq_data)
+		return error_handled;
+
+	switch (irq_data->irq_type) {
+	case CAM_CAMNOC_IRQ_IFE02_UBWC_ENCODE_ERROR:
+	case CAM_CAMNOC_IRQ_IFE13_UBWC_ENCODE_ERROR:
+		CAM_ERR_RATE_LIMIT(CAM_ISP,
+			"IFE UBWC Encode error type=%d status=%x",
+			irq_data->irq_type,
+			irq_data->u.enc_err.encerr_status.value);
+		error_handled = true;
+		break;
+	default:
+		break;
+	}
+
+	return error_handled;
+}
+
 static int cam_vfe_get_dt_properties(struct cam_hw_soc_info *soc_info)
 {
 	int rc = 0;
@@ -76,6 +100,12 @@
 		goto free_soc_private;
 	}
 
+	rc = cam_soc_util_get_option_clk_by_name(soc_info,
+		CAM_VFE_DSP_CLK_NAME, &soc_private->dsp_clk,
+		&soc_private->dsp_clk_index, &soc_private->dsp_clk_rate);
+	if (rc)
+		CAM_WARN(CAM_ISP, "option clk get failed");
+
 	rc = cam_vfe_request_platform_resource(soc_info, vfe_irq_handler,
 		irq_data);
 	if (rc < 0) {
@@ -89,6 +119,8 @@
 		CAM_HW_IDENTIFIER_LENGTH);
 	cpas_register_param.cell_index = soc_info->index;
 	cpas_register_param.dev = soc_info->dev;
+	cpas_register_param.cam_cpas_client_cb = cam_vfe_cpas_cb;
+	cpas_register_param.userdata = soc_info;
 	rc = cam_cpas_register_client(&cpas_register_param);
 	if (rc) {
 		CAM_ERR(CAM_ISP, "CPAS registration failed rc=%d", rc);
@@ -132,6 +164,11 @@
 		CAM_ERR(CAM_ISP,
 			"Error! Release platform resources failed rc=%d", rc);
 
+	rc = cam_soc_util_clk_put(&soc_private->dsp_clk);
+	if (rc < 0)
+		CAM_ERR(CAM_ISP,
+			"Error Put dsp clk failed rc=%d", rc);
+
 	kfree(soc_private);
 
 	return rc;
@@ -179,6 +216,54 @@
 	return rc;
 }
 
+int cam_vfe_soc_enable_clk(struct cam_hw_soc_info *soc_info,
+	const char *clk_name)
+{
+	int  rc = 0;
+	struct cam_vfe_soc_private       *soc_private;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		rc = -EINVAL;
+		return rc;
+	}
+	soc_private = soc_info->soc_private;
+
+	if (strcmp(clk_name, CAM_VFE_DSP_CLK_NAME) == 0) {
+		rc = cam_soc_util_clk_enable(soc_private->dsp_clk,
+			CAM_VFE_DSP_CLK_NAME, soc_private->dsp_clk_rate);
+		if (rc)
+			CAM_ERR(CAM_ISP,
+			"Error enable dsp clk failed rc=%d", rc);
+	}
+
+	return rc;
+}
+
+int cam_vfe_soc_disable_clk(struct cam_hw_soc_info *soc_info,
+	const char *clk_name)
+{
+	int  rc = 0;
+	struct cam_vfe_soc_private       *soc_private;
+
+	if (!soc_info) {
+		CAM_ERR(CAM_ISP, "Error Invalid params");
+		rc = -EINVAL;
+		return rc;
+	}
+	soc_private = soc_info->soc_private;
+
+	if (strcmp(clk_name, CAM_VFE_DSP_CLK_NAME) == 0) {
+		rc = cam_soc_util_clk_disable(soc_private->dsp_clk,
+			CAM_VFE_DSP_CLK_NAME);
+		if (rc)
+			CAM_ERR(CAM_ISP,
+			"Error enable dsp clk failed rc=%d", rc);
+	}
+
+	return rc;
+}
+
 
 int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info)
 {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h
index 094c977..7a4dbea 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/cam_vfe_soc.h
@@ -16,6 +16,8 @@
 #include "cam_soc_util.h"
 #include "cam_isp_hw.h"
 
+#define CAM_VFE_DSP_CLK_NAME "ife_dsp_clk"
+
 /*
  * struct cam_vfe_soc_private:
  *
@@ -26,7 +28,10 @@
  *                           with CPAS.
  */
 struct cam_vfe_soc_private {
-	uint32_t cpas_handle;
+	uint32_t    cpas_handle;
+	struct clk *dsp_clk;
+	int32_t     dsp_clk_index;
+	int32_t     dsp_clk_rate;
 };
 
 /*
@@ -80,4 +85,32 @@
  */
 int cam_vfe_disable_soc_resources(struct cam_hw_soc_info *soc_info);
 
+/*
+ * cam_vfe_soc_enable_clk()
+ *
+ * @brief:                   Enable clock with given name
+ *
+ * @soc_info:                Device soc information
+ * @clk_name:                Name of clock to enable
+ *
+ * @Return:                  0: Success
+ *                           Non-zero: Failure
+ */
+int cam_vfe_soc_enable_clk(struct cam_hw_soc_info *soc_info,
+	const char *clk_name);
+
+/*
+ * cam_vfe_soc_disable_dsp_clk()
+ *
+ * @brief:                   Disable clock with given name
+ *
+ * @soc_info:                Device soc information
+ * @clk_name:                Name of clock to enable
+ *
+ * @Return:                  0: Success
+ *                           Non-zero: Failure
+ */
+int cam_vfe_soc_disable_clk(struct cam_hw_soc_info *soc_info,
+	const char *clk_name);
+
 #endif /* _CAM_VFE_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
index 77e4eb3..39a5603 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/Makefile
@@ -11,4 +11,4 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
 
-obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe170.o cam_vfe_lite170.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
index dea906e..a4ba2e1 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe170.h
@@ -67,12 +67,18 @@
 	.extern_reg_update_mask          = 1,
 	.pixel_pattern_shift             = 0,
 	.pixel_pattern_mask              = 0x7,
+	.dsp_mode_shift                  = 23,
+	.dsp_mode_mask                   = 0x1,
+	.dsp_en_shift                    = 3,
+	.dsp_en_mask                     = 0x1,
 	.reg_update_cmd_data             = 0x1,
 	.epoch_line_cfg                  = 0x00140014,
 	.sof_irq_mask                    = 0x00000001,
 	.epoch0_irq_mask                 = 0x00000004,
 	.reg_update_irq_mask             = 0x00000010,
 	.eof_irq_mask                    = 0x00000002,
+	.error_irq_mask0                 = 0x0003FC00,
+	.error_irq_mask1                 = 0x0FFF7E80,
 };
 
 struct cam_vfe_top_ver2_reg_offset_module_ctrl lens_170_reg = {
@@ -193,6 +199,7 @@
 	.meta_offset      = 0x0000253C,
 	.meta_stride      = 0x00002540,
 	.mode_cfg         = 0x00002544,
+	.bw_limit         = 0x000025A0,
 };
 
 static struct cam_vfe_bus_ver2_reg_offset_ubwc_client ubwc_regs_client_4 = {
@@ -203,6 +210,7 @@
 	.meta_offset      = 0x0000263C,
 	.meta_stride      = 0x00002640,
 	.mode_cfg         = 0x00002644,
+	.bw_limit         = 0x000026A0,
 };
 
 static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = {
@@ -222,8 +230,12 @@
 		.comp_error_status            = 0x0000206C,
 		.comp_ovrwr_status            = 0x00002070,
 		.dual_comp_error_status       = 0x00002074,
-		.dual_comp_error_status       = 0x00002078,
+		.dual_comp_ovrwr_status       = 0x00002078,
+		.addr_sync_cfg                = 0x0000207C,
+		.addr_sync_frame_hdr          = 0x00002080,
+		.addr_sync_no_sync            = 0x00002084,
 	},
+	.num_client = 20,
 	.bus_client_reg = {
 		/* BUS Client 0 */
 		{
@@ -674,30 +686,54 @@
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */
 		{
 			.comp_mask                    = 0x0000202C,
+			.addr_sync_mask               = 0x00002088,
 		},
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */
 		{
 			.comp_mask                    = 0x00002030,
+			.addr_sync_mask               = 0x0000208C,
+
 		},
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */
 		{
 			.comp_mask                    = 0x00002034,
+			.addr_sync_mask               = 0x00002090,
+
 		},
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */
 		{
 			.comp_mask                    = 0x00002038,
+			.addr_sync_mask               = 0x00002094,
 		},
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */
 		{
 			.comp_mask                    = 0x0000203C,
+			.addr_sync_mask               = 0x00002098,
 		},
 		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */
 		{
 			.comp_mask                    = 0x00002040,
+			.addr_sync_mask               = 0x0000209C,
 		},
 	},
+	.num_out = 18,
 	.vfe_out_hw_info = {
 		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
 			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_FULL,
 			.max_width     = 4096,
 			.max_height    = 4096,
@@ -728,21 +764,6 @@
 			.max_height    = -1,
 		},
 		{
-			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
-			.max_width     = -1,
-			.max_height    = -1,
-		},
-		{
-			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
-			.max_width     = -1,
-			.max_height    = -1,
-		},
-		{
-			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
-			.max_width     = -1,
-			.max_height    = -1,
-		},
-		{
 			.vfe_out_type  =
 				CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
 			.max_width     = -1,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c
new file mode 100644
index 0000000..3c8abbf
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.c
@@ -0,0 +1,51 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "cam_vfe_lite170.h"
+#include "cam_vfe_hw_intf.h"
+#include "cam_vfe_core.h"
+#include "cam_vfe_dev.h"
+
+static const struct of_device_id cam_vfe170_dt_match[] = {
+	{
+		.compatible = "qcom,vfe-lite170",
+		.data = &cam_vfe_lite170_hw_info,
+	},
+	{}
+};
+MODULE_DEVICE_TABLE(of, cam_vfe170_dt_match);
+
+static struct platform_driver cam_vfe170_driver = {
+	.probe = cam_vfe_probe,
+	.remove = cam_vfe_remove,
+	.driver = {
+		.name = "cam_vfe_lite170",
+		.owner = THIS_MODULE,
+		.of_match_table = cam_vfe170_dt_match,
+	},
+};
+
+static int __init cam_vfe170_init_module(void)
+{
+	return platform_driver_register(&cam_vfe170_driver);
+}
+
+static void __exit cam_vfe170_exit_module(void)
+{
+	platform_driver_unregister(&cam_vfe170_driver);
+}
+
+module_init(cam_vfe170_init_module);
+module_exit(cam_vfe170_exit_module);
+MODULE_DESCRIPTION("CAM VFE170 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h
new file mode 100644
index 0000000..2f95feb
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe170/cam_vfe_lite170.h
@@ -0,0 +1,336 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_VFE_LITE170_H_
+#define _CAM_VFE_LITE170_H_
+
+#include "cam_vfe_bus_ver2.h"
+#include "cam_irq_controller.h"
+#include "cam_vfe_top_ver2.h"
+#include "cam_vfe_core.h"
+
+static struct cam_irq_register_set vfe170_top_irq_reg_set[2] = {
+	{
+		.mask_reg_offset   = 0x0000005C,
+		.clear_reg_offset  = 0x00000064,
+		.status_reg_offset = 0x0000006C,
+	},
+	{
+		.mask_reg_offset   = 0x00000060,
+		.clear_reg_offset  = 0x00000068,
+		.status_reg_offset = 0x00000070,
+	},
+};
+
+static struct cam_irq_controller_reg_info vfe170_top_irq_reg_info = {
+	.num_registers = 2,
+	.irq_reg_set = vfe170_top_irq_reg_set,
+	.global_clear_offset  = 0x00000058,
+	.global_clear_bitmask = 0x00000001,
+};
+
+static struct cam_vfe_top_ver2_reg_offset_common vfe170_top_common_reg = {
+	.hw_version               = 0x00000000,
+	.hw_capability            = 0x00000004,
+	.lens_feature             = 0x00000008,
+	.stats_feature            = 0x0000000C,
+	.color_feature            = 0x00000010,
+	.zoom_feature             = 0x00000014,
+	.global_reset_cmd         = 0x00000018,
+	.module_ctrl              = {
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+	},
+	.bus_cgc_ovd              = 0x0000003C,
+	.core_cfg                 = 0x00000000,
+	.three_D_cfg              = 0x00000000,
+	.violation_status         = 0x0000007C,
+	.reg_update_cmd           = 0x000004AC,
+};
+
+static struct cam_vfe_rdi_ver2_reg vfe170_rdi_reg = {
+	.reg_update_cmd           = 0x000004AC,
+};
+
+static struct cam_vfe_rdi_reg_data  vfe170_rdi_0_data = {
+	.reg_update_cmd_data      = 0x2,
+	.sof_irq_mask             = 0x8000000,
+	.reg_update_irq_mask      = 0x20,
+};
+
+static struct cam_vfe_rdi_reg_data  vfe170_rdi_1_data = {
+	.reg_update_cmd_data      = 0x4,
+	.sof_irq_mask             = 0x10000000,
+	.reg_update_irq_mask      = 0x40,
+};
+
+static struct cam_vfe_rdi_reg_data  vfe170_rdi_2_data = {
+	.reg_update_cmd_data      = 0x8,
+	.sof_irq_mask             = 0x20000000,
+	.reg_update_irq_mask      = 0x80,
+};
+
+static struct cam_vfe_rdi_reg_data  vfe170_rdi_3_data = {
+	.reg_update_cmd_data      = 0x10,
+	.sof_irq_mask             = 0x40000000,
+	.reg_update_irq_mask      = 0x100,
+};
+
+static struct cam_vfe_top_ver2_hw_info vfe170_top_hw_info = {
+	.common_reg = &vfe170_top_common_reg,
+	.camif_hw_info = {
+		.common_reg = NULL,
+		.camif_reg  = NULL,
+		.reg_data   = NULL,
+		},
+	.rdi_hw_info = {
+		.common_reg = &vfe170_top_common_reg,
+		.rdi_reg    = &vfe170_rdi_reg,
+		.reg_data = {
+			&vfe170_rdi_0_data,
+			&vfe170_rdi_1_data,
+			&vfe170_rdi_2_data,
+			&vfe170_rdi_3_data,
+			},
+		},
+	.mux_type = {
+		CAM_VFE_RDI_VER_1_0,
+		CAM_VFE_RDI_VER_1_0,
+		CAM_VFE_RDI_VER_1_0,
+		CAM_VFE_RDI_VER_1_0,
+	},
+};
+
+static struct cam_irq_register_set vfe170_bus_irq_reg[3] = {
+		{
+			.mask_reg_offset   = 0x00002044,
+			.clear_reg_offset  = 0x00002050,
+			.status_reg_offset = 0x0000205C,
+		},
+		{
+			.mask_reg_offset   = 0x00002048,
+			.clear_reg_offset  = 0x00002054,
+			.status_reg_offset = 0x00002060,
+		},
+		{
+			.mask_reg_offset   = 0x0000204C,
+			.clear_reg_offset  = 0x00002058,
+			.status_reg_offset = 0x00002064,
+		},
+};
+
+static struct cam_vfe_bus_ver2_hw_info vfe170_bus_hw_info = {
+	.common_reg = {
+		.hw_version                   = 0x00002000,
+		.hw_capability                = 0x00002004,
+		.sw_reset                     = 0x00002008,
+		.cgc_ovd                      = 0x0000200C,
+		.pwr_iso_cfg                  = 0x000020CC,
+		.dual_master_comp_cfg         = 0x00002028,
+		.irq_reg_info = {
+			.num_registers        = 3,
+			.irq_reg_set          = vfe170_bus_irq_reg,
+			.global_clear_offset  = 0x00002068,
+			.global_clear_bitmask = 0x00000001,
+		},
+		.comp_error_status            = 0x0000206C,
+		.comp_ovrwr_status            = 0x00002070,
+		.dual_comp_error_status       = 0x00002074,
+		.dual_comp_ovrwr_status       = 0x00002078,
+		.addr_sync_cfg                = 0x0000207C,
+		.addr_sync_frame_hdr          = 0x00002080,
+		.addr_sync_no_sync            = 0x00002084,
+	},
+	.num_client = 4,
+	.bus_client_reg = {
+		/* BUS Client 0 */
+		{
+			.status0                  = 0x00002200,
+			.status1                  = 0x00002204,
+			.cfg                      = 0x00002208,
+			.header_addr              = 0x0000220C,
+			.header_cfg               = 0x00002210,
+			.image_addr               = 0x00002214,
+			.image_addr_offset        = 0x00002218,
+			.buffer_width_cfg         = 0x0000221C,
+			.buffer_height_cfg        = 0x00002220,
+			.packer_cfg               = 0x00002224,
+			.stride                   = 0x00002228,
+			.irq_subsample_period     = 0x00002248,
+			.irq_subsample_pattern    = 0x0000224C,
+			.framedrop_period         = 0x00002250,
+			.framedrop_pattern        = 0x00002254,
+			.frame_inc                = 0x00002258,
+			.burst_limit              = 0x0000225C,
+			.ubwc_regs                = NULL,
+		},
+		/* BUS Client 1 */
+		{
+			.status0                  = 0x00002300,
+			.status1                  = 0x00002304,
+			.cfg                      = 0x00002308,
+			.header_addr              = 0x0000230C,
+			.header_cfg               = 0x00002310,
+			.image_addr               = 0x00002314,
+			.image_addr_offset        = 0x00002318,
+			.buffer_width_cfg         = 0x0000231C,
+			.buffer_height_cfg        = 0x00002320,
+			.packer_cfg               = 0x00002324,
+			.stride                   = 0x00002328,
+			.irq_subsample_period     = 0x00002348,
+			.irq_subsample_pattern    = 0x0000234C,
+			.framedrop_period         = 0x00002350,
+			.framedrop_pattern        = 0x00002354,
+			.frame_inc                = 0x00002358,
+			.burst_limit              = 0x0000235C,
+			.ubwc_regs                = NULL,
+		},
+		/* BUS Client 2 */
+		{
+			.status0                  = 0x00002400,
+			.status1                  = 0x00002404,
+			.cfg                      = 0x00002408,
+			.header_addr              = 0x0000240C,
+			.header_cfg               = 0x00002410,
+			.image_addr               = 0x00002414,
+			.image_addr_offset        = 0x00002418,
+			.buffer_width_cfg         = 0x0000241C,
+			.buffer_height_cfg        = 0x00002420,
+			.packer_cfg               = 0x00002424,
+			.stride                   = 0x00002428,
+			.irq_subsample_period     = 0x00002448,
+			.irq_subsample_pattern    = 0x0000244C,
+			.framedrop_period         = 0x00002450,
+			.framedrop_pattern        = 0x00002454,
+			.frame_inc                = 0x00002458,
+			.burst_limit              = 0x0000245C,
+			.ubwc_regs                = NULL,
+		},
+		/* BUS Client 3 */
+		{
+			.status0                  = 0x00002500,
+			.status1                  = 0x00002504,
+			.cfg                      = 0x00002508,
+			.header_addr              = 0x0000250C,
+			.header_cfg               = 0x00002510,
+			.image_addr               = 0x00002514,
+			.image_addr_offset        = 0x00002518,
+			.buffer_width_cfg         = 0x0000251C,
+			.buffer_height_cfg        = 0x00002520,
+			.packer_cfg               = 0x00002524,
+			.stride                   = 0x00002528,
+			.irq_subsample_period     = 0x00002548,
+			.irq_subsample_pattern    = 0x0000254C,
+			.framedrop_period         = 0x00002550,
+			.framedrop_pattern        = 0x00002554,
+			.frame_inc                = 0x00002558,
+			.burst_limit              = 0x0000255C,
+			.ubwc_regs                = NULL,
+		},
+	},
+	.comp_grp_reg = {
+		/* CAM_VFE_BUS_VER2_COMP_GRP_0 */
+		{
+			.comp_mask                    = 0x00002010,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_1 */
+		{
+			.comp_mask                    = 0x00002014,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_2 */
+		{
+			.comp_mask                    = 0x00002018,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_3 */
+		{
+			.comp_mask                    = 0x0000201C,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_4 */
+		{
+			.comp_mask                    = 0x00002020,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_5 */
+		{
+			.comp_mask                    = 0x00002024,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 */
+		{
+			.comp_mask                    = 0x0000202C,
+			.addr_sync_mask               = 0x00002088,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_1 */
+		{
+			.comp_mask                    = 0x00002030,
+			.addr_sync_mask               = 0x0000208C,
+
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_2 */
+		{
+			.comp_mask                    = 0x00002034,
+			.addr_sync_mask               = 0x00002090,
+
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_3 */
+		{
+			.comp_mask                    = 0x00002038,
+			.addr_sync_mask               = 0x00002094,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_4 */
+		{
+			.comp_mask                    = 0x0000203C,
+			.addr_sync_mask               = 0x00002098,
+		},
+		/* CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 */
+		{
+			.comp_mask                    = 0x00002040,
+			.addr_sync_mask               = 0x0000209C,
+		},
+	},
+	.num_out = 4,
+	.vfe_out_hw_info = {
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+		{
+			.vfe_out_type  = CAM_VFE_BUS_VER2_VFE_OUT_RDI3,
+			.max_width     = -1,
+			.max_height    = -1,
+		},
+	},
+};
+
+static struct cam_vfe_hw_info cam_vfe_lite170_hw_info = {
+	.irq_reg_info                  = &vfe170_top_irq_reg_info,
+
+	.bus_version                   = CAM_VFE_BUS_VER_2_0,
+	.bus_hw_info                   = &vfe170_bus_hw_info,
+
+	.top_version                   = CAM_VFE_TOP_VER_2_0,
+	.top_hw_info                   = &vfe170_top_hw_info,
+
+};
+
+#endif /* _CAM_VFE_LITE170_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
index 4a328ee..24133a6 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/Makefile
@@ -9,5 +9,6 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
 
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_bus.o cam_vfe_bus_ver2.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
index 03c5eb3..e94bb62 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.c
@@ -12,6 +12,7 @@
 
 #include <linux/ratelimit.h>
 #include <linux/slab.h>
+#include <uapi/media/cam_isp.h>
 #include "cam_io_util.h"
 #include "cam_debug_util.h"
 #include "cam_cdm_util.h"
@@ -24,6 +25,7 @@
 #include "cam_vfe_bus_ver2.h"
 #include "cam_vfe_core.h"
 #include "cam_debug_util.h"
+#include "cam_cpas_api.h"
 
 static const char drv_name[] = "vfe_bus";
 
@@ -34,14 +36,18 @@
 
 #define CAM_VFE_BUS_VER2_PAYLOAD_MAX             256
 
-#define CAM_VFE_RDI_BUS_DEFAULT_WIDTH           0xFF01
-#define CAM_VFE_RDI_BUS_DEFAULT_STRIDE          0xFF01
+#define CAM_VFE_RDI_BUS_DEFAULT_WIDTH               0xFF01
+#define CAM_VFE_RDI_BUS_DEFAULT_STRIDE              0xFF01
+#define CAM_VFE_BUS_INTRA_CLIENT_MASK               0x3
+#define CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT    8
+#define CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL        0xFFFFF
 
 #define ALIGNUP(value, alignment) \
 	((value + alignment - 1) / alignment * alignment)
 
 #define MAX_BUF_UPDATE_REG_NUM   \
-	(sizeof(struct cam_vfe_bus_ver2_reg_offset_bus_client)/4)
+	((sizeof(struct cam_vfe_bus_ver2_reg_offset_bus_client) +  \
+	sizeof(struct cam_vfe_bus_ver2_reg_offset_ubwc_client))/4)
 #define MAX_REG_VAL_PAIR_SIZE    \
 	(MAX_BUF_UPDATE_REG_NUM * 2 * CAM_PACKET_MAX_PLANES)
 
@@ -51,6 +57,12 @@
 		buf_array[index++] = val;                  \
 	} while (0)
 
+static uint32_t bus_error_irq_mask[3] = {
+	0x7800,
+	0x0000,
+	0x00C0,
+};
+
 enum cam_vfe_bus_packer_format {
 	PACKER_FMT_PLAIN_128                   = 0x0,
 	PACKER_FMT_PLAIN_8                     = 0x1,
@@ -71,6 +83,16 @@
 	PACKER_FMT_MAX                         = 0xF,
 };
 
+enum cam_vfe_bus_comp_grp_id {
+	CAM_VFE_BUS_COMP_GROUP_NONE = -EINVAL,
+	CAM_VFE_BUS_COMP_GROUP_ID_0 = 0x0,
+	CAM_VFE_BUS_COMP_GROUP_ID_1 = 0x1,
+	CAM_VFE_BUS_COMP_GROUP_ID_2 = 0x2,
+	CAM_VFE_BUS_COMP_GROUP_ID_3 = 0x3,
+	CAM_VFE_BUS_COMP_GROUP_ID_4 = 0x4,
+	CAM_VFE_BUS_COMP_GROUP_ID_5 = 0x5,
+};
+
 struct cam_vfe_bus_ver2_common_data {
 	uint32_t                                    core_index;
 	void __iomem                               *mem_base;
@@ -84,6 +106,10 @@
 	struct cam_vfe_bus_irq_evt_payload          evt_payload[
 		CAM_VFE_BUS_VER2_PAYLOAD_MAX];
 	struct list_head                            free_payload_list;
+	struct mutex                                bus_mutex;
+	uint32_t                                    secure_mode;
+	uint32_t                                    num_sec_out;
+	uint32_t                                    addr_no_sync;
 };
 
 struct cam_vfe_bus_ver2_wm_resource_data {
@@ -120,6 +146,7 @@
 	uint32_t             framedrop_pattern;
 
 	uint32_t             en_cfg;
+	uint32_t             is_dual;
 };
 
 struct cam_vfe_bus_ver2_comp_grp_data {
@@ -135,6 +162,10 @@
 	uint32_t                         dual_slave_core;
 	uint32_t                         intra_client_mask;
 	uint32_t                         composite_mask;
+	uint32_t                         addr_sync_mode;
+
+	uint32_t                         acquire_dev_cnt;
+	uint32_t                         irq_trigger_cnt;
 
 	void                            *ctx;
 };
@@ -155,10 +186,13 @@
 	uint32_t                         max_width;
 	uint32_t                         max_height;
 	struct cam_cdm_utils_ops        *cdm_util_ops;
+	uint32_t                         secure_mode;
 };
 
 struct cam_vfe_bus_ver2_priv {
 	struct cam_vfe_bus_ver2_common_data common_data;
+	uint32_t                            num_client;
+	uint32_t                            num_out;
 
 	struct cam_isp_resource_node  bus_client[CAM_VFE_BUS_VER2_MAX_CLIENTS];
 	struct cam_isp_resource_node  comp_grp[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
@@ -169,15 +203,20 @@
 	struct list_head                    used_comp_grp;
 
 	uint32_t                            irq_handle;
+	uint32_t                            error_irq_handle;
 };
 
+static int cam_vfe_bus_process_cmd(
+	struct cam_isp_resource_node *priv,
+	uint32_t cmd_type, void *cmd_args, uint32_t arg_size);
+
 static int cam_vfe_bus_get_evt_payload(
 	struct cam_vfe_bus_ver2_common_data  *common_data,
 	struct cam_vfe_bus_irq_evt_payload  **evt_payload)
 {
 	if (list_empty(&common_data->free_payload_list)) {
 		*evt_payload = NULL;
-		CAM_ERR(CAM_ISP, "No free payload");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "No free payload");
 		return -ENODEV;
 	}
 
@@ -187,6 +226,28 @@
 	return 0;
 }
 
+static enum cam_vfe_bus_comp_grp_id
+	cam_vfe_bus_comp_grp_id_convert(uint32_t comp_grp)
+{
+	switch (comp_grp) {
+	case CAM_ISP_RES_COMP_GROUP_ID_0:
+		return CAM_VFE_BUS_COMP_GROUP_ID_0;
+	case CAM_ISP_RES_COMP_GROUP_ID_1:
+		return CAM_VFE_BUS_COMP_GROUP_ID_1;
+	case CAM_ISP_RES_COMP_GROUP_ID_2:
+		return CAM_VFE_BUS_COMP_GROUP_ID_2;
+	case CAM_ISP_RES_COMP_GROUP_ID_3:
+		return CAM_VFE_BUS_COMP_GROUP_ID_3;
+	case CAM_ISP_RES_COMP_GROUP_ID_4:
+		return CAM_VFE_BUS_COMP_GROUP_ID_4;
+	case CAM_ISP_RES_COMP_GROUP_ID_5:
+		return CAM_VFE_BUS_COMP_GROUP_ID_5;
+	case CAM_ISP_RES_COMP_GROUP_NONE:
+	default:
+		return CAM_VFE_BUS_COMP_GROUP_NONE;
+	}
+}
+
 static int cam_vfe_bus_put_evt_payload(void     *core_info,
 	struct cam_vfe_bus_irq_evt_payload     **evt_payload)
 {
@@ -202,7 +263,7 @@
 		CAM_ERR(CAM_ISP, "No payload to put");
 		return -EINVAL;
 	}
-
+	(*evt_payload)->error_type = 0;
 	ife_irq_regs = (*evt_payload)->irq_reg_val;
 	status_reg0 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0];
 	status_reg1 = ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS1];
@@ -228,23 +289,37 @@
 	uint32_t                          *intra_client_mask)
 {
 	int rc = 0;
+	uint32_t camera_hw_version = 0;
+	uint32_t version_based_intra_client_mask = 0x1;
 
 	*intra_client_mask = 0;
 
+
 	if (dual_slave_core == current_core) {
 		CAM_ERR(CAM_ISP,
 			"Invalid params. Same core as Master and Slave");
 		return -EINVAL;
 	}
 
+	rc = cam_cpas_get_cpas_hw_version(&camera_hw_version);
+
+	CAM_DBG(CAM_ISP, "CPAS VERSION %d", camera_hw_version);
+
+	switch (camera_hw_version) {
+	case CAM_CPAS_TITAN_170_V100:
+		version_based_intra_client_mask = 0x3;
+		break;
+	default:
+		version_based_intra_client_mask = 0x1;
+		break;
+	}
+
+
 	switch (current_core) {
 	case CAM_VFE_BUS_VER2_VFE_CORE_0:
 		switch (dual_slave_core) {
 		case CAM_VFE_BUS_VER2_VFE_CORE_1:
-			*intra_client_mask = 0x1;
-			break;
-		case CAM_VFE_BUS_VER2_VFE_CORE_2:
-			*intra_client_mask = 0x2;
+			*intra_client_mask = version_based_intra_client_mask;
 			break;
 		default:
 			CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
@@ -256,25 +331,7 @@
 	case CAM_VFE_BUS_VER2_VFE_CORE_1:
 		switch (dual_slave_core) {
 		case CAM_VFE_BUS_VER2_VFE_CORE_0:
-			*intra_client_mask = 0x1;
-			break;
-		case CAM_VFE_BUS_VER2_VFE_CORE_2:
-			*intra_client_mask = 0x2;
-			break;
-		default:
-			CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
-				dual_slave_core);
-			rc = -EINVAL;
-			break;
-		}
-		break;
-	case CAM_VFE_BUS_VER2_VFE_CORE_2:
-		switch (dual_slave_core) {
-		case CAM_VFE_BUS_VER2_VFE_CORE_0:
-			*intra_client_mask = 0x1;
-			break;
-		case CAM_VFE_BUS_VER2_VFE_CORE_1:
-			*intra_client_mask = 0x2;
+			*intra_client_mask = version_based_intra_client_mask;
 			break;
 		default:
 			CAM_ERR(CAM_ISP, "Invalid value for slave core %u",
@@ -293,6 +350,34 @@
 	return rc;
 }
 
+static bool cam_vfe_bus_can_be_secure(uint32_t out_type)
+{
+	switch (out_type) {
+	case CAM_VFE_BUS_VER2_VFE_OUT_FULL:
+	case CAM_VFE_BUS_VER2_VFE_OUT_DS4:
+	case CAM_VFE_BUS_VER2_VFE_OUT_DS16:
+	case CAM_VFE_BUS_VER2_VFE_OUT_FD:
+	case CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP:
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
+		return true;
+
+	case CAM_VFE_BUS_VER2_VFE_OUT_PDAF:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BF:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_AWB_BG:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_BHIST:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_RS:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_CS:
+	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_IHIST:
+	default:
+		return false;
+	}
+}
+
 static enum cam_vfe_bus_ver2_vfe_out_type
 	cam_vfe_bus_get_out_res_id(uint32_t res_type)
 {
@@ -315,6 +400,8 @@
 		return CAM_VFE_BUS_VER2_VFE_OUT_RDI1;
 	case CAM_ISP_IFE_OUT_RES_RDI_2:
 		return CAM_VFE_BUS_VER2_VFE_OUT_RDI2;
+	case CAM_ISP_IFE_OUT_RES_RDI_3:
+		return CAM_VFE_BUS_VER2_VFE_OUT_RDI3;
 	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BE:
 		return CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE;
 	case CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST:
@@ -346,6 +433,7 @@
 	case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
 	case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
 	case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI3:
 		switch (format) {
 		case CAM_FORMAT_MIPI_RAW_8:
 		case CAM_FORMAT_MIPI_RAW_10:
@@ -382,6 +470,7 @@
 		case CAM_FORMAT_UBWC_NV12_4R:
 		case CAM_FORMAT_UBWC_TP10:
 		case CAM_FORMAT_UBWC_P010:
+		case CAM_FORMAT_PLAIN16_10:
 			return 2;
 		default:
 			break;
@@ -472,6 +561,42 @@
 	int wm_idx = -1;
 
 	switch (vfe_out_res_id) {
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
+		switch (plane) {
+		case PLANE_Y:
+			wm_idx = 0;
+			break;
+		default:
+			break;
+		}
+		break;
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
+		switch (plane) {
+		case PLANE_Y:
+			wm_idx = 1;
+			break;
+		default:
+			break;
+		}
+		break;
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
+		switch (plane) {
+		case PLANE_Y:
+			wm_idx = 2;
+			break;
+		default:
+			break;
+		}
+		break;
+	case CAM_VFE_BUS_VER2_VFE_OUT_RDI3:
+		switch (plane) {
+		case PLANE_Y:
+			wm_idx = 3;
+			break;
+		default:
+			break;
+		}
+		break;
 	case CAM_VFE_BUS_VER2_VFE_OUT_FULL:
 		switch (plane) {
 		case PLANE_Y:
@@ -532,33 +657,6 @@
 			break;
 		}
 		break;
-	case CAM_VFE_BUS_VER2_VFE_OUT_RDI0:
-		switch (plane) {
-		case PLANE_Y:
-			wm_idx = 0;
-			break;
-		default:
-			break;
-		}
-		break;
-	case CAM_VFE_BUS_VER2_VFE_OUT_RDI1:
-		switch (plane) {
-		case PLANE_Y:
-			wm_idx = 1;
-			break;
-		default:
-			break;
-		}
-		break;
-	case CAM_VFE_BUS_VER2_VFE_OUT_RDI2:
-		switch (plane) {
-		case PLANE_Y:
-			wm_idx = 2;
-			break;
-		default:
-			break;
-		}
-		break;
 	case CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE:
 		switch (plane) {
 		case PLANE_Y:
@@ -648,10 +746,12 @@
 }
 
 static enum cam_vfe_bus_packer_format
-	cam_vfe_bus_get_packer_fmt(uint32_t out_fmt)
+	cam_vfe_bus_get_packer_fmt(uint32_t out_fmt, int wm_index)
 {
 	switch (out_fmt) {
 	case CAM_FORMAT_NV21:
+		if (wm_index == 4 || wm_index == 6)
+			return PACKER_FMT_PLAIN_8_LSB_MSB_10_ODD_EVEN;
 	case CAM_FORMAT_NV12:
 	case CAM_FORMAT_UBWC_NV12:
 	case CAM_FORMAT_UBWC_NV12_4R:
@@ -685,6 +785,8 @@
 	case CAM_FORMAT_UBWC_TP10:
 	case CAM_FORMAT_TP10:
 		return PACKER_FMT_TP_10;
+	case CAM_FORMAT_ARGB_14:
+		return PACKER_FMT_ARGB_14;
 	default:
 		return PACKER_FMT_MAX;
 	}
@@ -697,10 +799,10 @@
 	void                                  *ctx,
 	enum cam_vfe_bus_ver2_vfe_out_type     vfe_out_res_id,
 	enum cam_vfe_bus_plane_type            plane,
-	enum cam_isp_hw_split_id               split_id,
 	uint32_t                               subscribe_irq,
 	struct cam_isp_resource_node         **wm_res,
-	uint32_t                              *client_done_mask)
+	uint32_t                              *client_done_mask,
+	uint32_t                               is_dual)
 {
 	uint32_t wm_idx = 0;
 	struct cam_isp_resource_node              *wm_res_local = NULL;
@@ -711,7 +813,7 @@
 
 	/* No need to allocate for BUS VER2. VFE OUT to WM is fixed. */
 	wm_idx = cam_vfe_bus_get_wm_idx(vfe_out_res_id, plane);
-	if (wm_idx < 0 || wm_idx >= CAM_VFE_BUS_VER2_MAX_CLIENTS) {
+	if (wm_idx < 0 || wm_idx >= ver2_bus_priv->num_client) {
 		CAM_ERR(CAM_ISP, "Unsupported VFE out %d plane %d",
 			vfe_out_res_id, plane);
 		return -EINVAL;
@@ -725,10 +827,14 @@
 	rsrc_data->irq_enabled = subscribe_irq;
 	rsrc_data->ctx = ctx;
 	rsrc_data->format = out_port_info->format;
-	rsrc_data->pack_fmt = cam_vfe_bus_get_packer_fmt(rsrc_data->format);
+	rsrc_data->pack_fmt = cam_vfe_bus_get_packer_fmt(rsrc_data->format,
+		wm_idx);
 
 	rsrc_data->width = out_port_info->width;
 	rsrc_data->height = out_port_info->height;
+	rsrc_data->is_dual = is_dual;
+	/* Set WM offset value to default */
+	rsrc_data->offset  = 0;
 	CAM_DBG(CAM_ISP, "WM %d width %d height %d", rsrc_data->index,
 		rsrc_data->width, rsrc_data->height);
 
@@ -742,6 +848,7 @@
 		case CAM_FORMAT_MIPI_RAW_14:
 		case CAM_FORMAT_MIPI_RAW_16:
 		case CAM_FORMAT_MIPI_RAW_20:
+		case CAM_FORMAT_PLAIN128:
 			rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH;
 			rsrc_data->height = 0;
 			rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE;
@@ -755,10 +862,11 @@
 			rsrc_data->stride = rsrc_data->width;
 			break;
 		case CAM_FORMAT_PLAIN16_10:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x2;
-			rsrc_data->width = rsrc_data->width * 2;
-			rsrc_data->stride = rsrc_data->width;
+			rsrc_data->width = CAM_VFE_RDI_BUS_DEFAULT_WIDTH;
+			rsrc_data->height = 0;
+			rsrc_data->stride = CAM_VFE_RDI_BUS_DEFAULT_STRIDE;
+			rsrc_data->pack_fmt = 0x0;
+			rsrc_data->en_cfg = 0x3;
 			break;
 		case CAM_FORMAT_PLAIN16_12:
 			rsrc_data->en_cfg = 0x1;
@@ -786,10 +894,6 @@
 			rsrc_data->en_cfg = 0x1;
 			rsrc_data->pack_fmt = 0xA;
 			break;
-		case CAM_FORMAT_PLAIN128:
-			rsrc_data->en_cfg = 0x1;
-			rsrc_data->pack_fmt = 0x0;
-			break;
 		default:
 			CAM_ERR(CAM_ISP, "Unsupported RDI format %d",
 				rsrc_data->format);
@@ -809,7 +913,7 @@
 			case PLANE_Y:
 				break;
 			default:
-				CAM_ERR(CAM_ISP, "Invalid plane %d\n", plane);
+				CAM_ERR(CAM_ISP, "Invalid plane %d", plane);
 				return -EINVAL;
 			}
 			break;
@@ -825,7 +929,7 @@
 			case PLANE_Y:
 				break;
 			default:
-				CAM_ERR(CAM_ISP, "Invalid plane %d\n", plane);
+				CAM_ERR(CAM_ISP, "Invalid plane %d", plane);
 				return -EINVAL;
 			}
 			break;
@@ -840,7 +944,7 @@
 			case PLANE_Y:
 				break;
 			default:
-				CAM_ERR(CAM_ISP, "Invalid plane %d\n", plane);
+				CAM_ERR(CAM_ISP, "Invalid plane %d", plane);
 				return -EINVAL;
 			}
 			break;
@@ -854,12 +958,25 @@
 			case PLANE_Y:
 				break;
 			default:
-				CAM_ERR(CAM_ISP, "Invalid plane %d\n", plane);
+				CAM_ERR(CAM_ISP, "Invalid plane %d", plane);
 				return -EINVAL;
 			}
 			break;
+		case CAM_FORMAT_PLAIN16_10:
+			switch (plane) {
+			case PLANE_C:
+				rsrc_data->height /= 2;
+				break;
+			case PLANE_Y:
+				break;
+			default:
+				CAM_ERR(CAM_ISP, "Invalid plane %d", plane);
+				return -EINVAL;
+			}
+			rsrc_data->width *= 2;
+			break;
 		default:
-			CAM_ERR(CAM_ISP, "Invalid format %d\n",
+			CAM_ERR(CAM_ISP, "Invalid format %d",
 				rsrc_data->format);
 			return -EINVAL;
 		}
@@ -870,16 +987,35 @@
 		rsrc_data->height = 0;
 		rsrc_data->stride = 1;
 		rsrc_data->en_cfg = 0x3;
-	} else {
-		/* Write master 5-6 DS ports , 9 - Raw dump , 10 PDAF */
+	}  else if (rsrc_data->index == 9) {
+		/* Write master 9 - Raw dump */
+		rsrc_data->width = rsrc_data->width * 2;
+		rsrc_data->stride = rsrc_data->width;
+		rsrc_data->en_cfg = 0x1;
+
+		/* LSB aligned */
+		rsrc_data->pack_fmt |= 0x10;
+	}  else {
+		/* Write master 5-6 DS ports, 10 PDAF */
+		uint32_t align_width;
 		rsrc_data->width = rsrc_data->width * 4;
 		rsrc_data->height = rsrc_data->height / 2;
 		rsrc_data->en_cfg = 0x1;
+		CAM_DBG(CAM_ISP, "before width %d", rsrc_data->width);
+		align_width = ALIGNUP(rsrc_data->width, 16);
+		if (align_width != rsrc_data->width) {
+			CAM_WARN(CAM_ISP,
+				"Override width %u with expected %u",
+				rsrc_data->width, align_width);
+			rsrc_data->width = align_width;
+		}
 	}
 
 	*client_done_mask = (1 << wm_idx);
 	*wm_res = wm_res_local;
 
+	CAM_DBG(CAM_ISP, "WM %d: processed width %d, processed  height %d",
+		rsrc_data->index, rsrc_data->width, rsrc_data->height);
 	return 0;
 }
 
@@ -912,6 +1048,7 @@
 	rsrc_data->init_cfg_done = false;
 	rsrc_data->hfr_cfg_done = false;
 	rsrc_data->en_cfg = 0;
+	rsrc_data->is_dual = 0;
 
 	wm_res->tasklet_info = NULL;
 	wm_res->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -928,8 +1065,6 @@
 		rsrc_data->common_data;
 	uint32_t                   bus_irq_reg_mask[CAM_VFE_BUS_IRQ_MAX] = {0};
 
-	cam_io_w_mb(0, common_data->mem_base + rsrc_data->hw_regs->header_addr);
-	cam_io_w_mb(0, common_data->mem_base + rsrc_data->hw_regs->header_cfg);
 	cam_io_w(0xf, common_data->mem_base + rsrc_data->hw_regs->burst_limit);
 
 	cam_io_w_mb(rsrc_data->width,
@@ -1011,6 +1146,8 @@
 		common_data->mem_base + common_data->common_reg->sw_reset);
 
 	wm_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
+	rsrc_data->init_cfg_done = false;
+	rsrc_data->hfr_cfg_done = false;
 
 	return rc;
 }
@@ -1026,7 +1163,7 @@
 
 	wm_res = th_payload->handler_priv;
 	if (!wm_res) {
-		CAM_ERR_RATE_LIMIT(CAM_ISP, "Error! No resource\n");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Error: No resource");
 		return -ENODEV;
 	}
 
@@ -1038,7 +1175,7 @@
 	rc  = cam_vfe_bus_get_evt_payload(rsrc_data->common_data, &evt_payload);
 	if (rc) {
 		CAM_ERR_RATE_LIMIT(CAM_ISP,
-			"No tasklet_cmd is free in queue\n");
+			"No tasklet_cmd is free in queue");
 		return rc;
 	}
 
@@ -1135,10 +1272,8 @@
 
 	rsrc_data = wm_res->res_priv;
 	wm_res->res_priv = NULL;
-	if (!rsrc_data) {
-		CAM_ERR(CAM_ISP, "Error! WM res priv is NULL");
+	if (!rsrc_data)
 		return -ENOMEM;
-	}
 	kfree(rsrc_data);
 
 	return 0;
@@ -1188,16 +1323,23 @@
 	struct cam_isp_resource_node       **comp_grp)
 {
 	int rc = 0;
+	uint32_t bus_comp_grp_id;
 	struct cam_isp_resource_node           *comp_grp_local = NULL;
 	struct cam_vfe_bus_ver2_comp_grp_data  *rsrc_data = NULL;
 
-	/* Check if matching comp_grp already acquired */
-	cam_vfe_bus_match_comp_grp(ver2_bus_priv, &comp_grp_local,
-		out_port_info->comp_grp_id, unique_id);
+	bus_comp_grp_id = cam_vfe_bus_comp_grp_id_convert(
+		out_port_info->comp_grp_id);
+	/* Perform match only if there is valid comp grp request */
+	if (out_port_info->comp_grp_id != CAM_ISP_RES_COMP_GROUP_NONE) {
+		/* Check if matching comp_grp already acquired */
+		cam_vfe_bus_match_comp_grp(ver2_bus_priv, &comp_grp_local,
+			bus_comp_grp_id, unique_id);
+	}
 
 	if (!comp_grp_local) {
 		/* First find a free group */
 		if (is_dual) {
+			CAM_DBG(CAM_ISP, "Acquire dual comp group");
 			if (list_empty(&ver2_bus_priv->free_dual_comp_grp)) {
 				CAM_ERR(CAM_ISP, "No Free Composite Group");
 				return -ENODEV;
@@ -1210,7 +1352,10 @@
 				dual_slave_core,
 				comp_grp_local->hw_intf->hw_idx,
 				&rsrc_data->intra_client_mask);
+			if (rc)
+				return rc;
 		} else {
+			CAM_DBG(CAM_ISP, "Acquire comp group");
 			if (list_empty(&ver2_bus_priv->free_comp_grp)) {
 				CAM_ERR(CAM_ISP, "No Free Composite Group");
 				return -ENODEV;
@@ -1228,7 +1373,12 @@
 		rsrc_data->is_master = is_master;
 		rsrc_data->composite_mask = 0;
 		rsrc_data->unique_id = unique_id;
-		rsrc_data->comp_grp_local_idx = out_port_info->comp_grp_id;
+		rsrc_data->comp_grp_local_idx = bus_comp_grp_id;
+
+		if (is_master)
+			rsrc_data->addr_sync_mode = 0;
+		else
+			rsrc_data->addr_sync_mode = 1;
 
 		list_add_tail(&comp_grp_local->list,
 			&ver2_bus_priv->used_comp_grp);
@@ -1245,7 +1395,10 @@
 		}
 	}
 
+	CAM_DBG(CAM_ISP, "Comp Grp type %u", rsrc_data->comp_grp_type);
+
 	rsrc_data->ctx = ctx;
+	rsrc_data->acquire_dev_cnt++;
 	*comp_grp = comp_grp_local;
 
 	return rc;
@@ -1260,16 +1413,23 @@
 	int match_found = 0;
 
 	if (!in_comp_grp) {
-		CAM_ERR(CAM_ISP, "Invalid Params Comp Grp %pK", in_rsrc_data);
+		CAM_ERR(CAM_ISP, "Invalid Params Comp Grp %pK", in_comp_grp);
 		return -EINVAL;
 	}
 
 	if (in_comp_grp->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE) {
-		/* Already Released. Do Nothing */
+		CAM_ERR(CAM_ISP, "Already released Comp Grp");
 		return 0;
 	}
 
+	if (in_comp_grp->res_state == CAM_ISP_RESOURCE_STATE_STREAMING) {
+		CAM_ERR(CAM_ISP, "Invalid State %d",
+			in_comp_grp->res_state);
+		return -EBUSY;
+	}
+
 	in_rsrc_data = in_comp_grp->res_priv;
+	CAM_DBG(CAM_ISP, "Comp Grp type %u", in_rsrc_data->comp_grp_type);
 
 	list_for_each_entry(comp_grp, &ver2_bus_priv->used_comp_grp, list) {
 		if (comp_grp == in_comp_grp) {
@@ -1284,23 +1444,32 @@
 		return -ENODEV;
 	}
 
+	in_rsrc_data->acquire_dev_cnt--;
+	if (in_rsrc_data->acquire_dev_cnt == 0) {
+		list_del(&comp_grp->list);
 
-	list_del(&comp_grp->list);
-	if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
-		in_rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
-		list_add_tail(&comp_grp->list,
-			&ver2_bus_priv->free_dual_comp_grp);
-	else if (in_rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0
-		&& in_rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)
-		list_add_tail(&comp_grp->list, &ver2_bus_priv->free_comp_grp);
+		in_rsrc_data->unique_id = 0;
+		in_rsrc_data->comp_grp_local_idx = CAM_VFE_BUS_COMP_GROUP_NONE;
+		in_rsrc_data->composite_mask = 0;
+		in_rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX;
+		in_rsrc_data->addr_sync_mode = 0;
 
-	in_rsrc_data->unique_id = 0;
-	in_rsrc_data->comp_grp_local_idx = 0;
-	in_rsrc_data->composite_mask = 0;
-	in_rsrc_data->dual_slave_core = CAM_VFE_BUS_VER2_VFE_CORE_MAX;
+		comp_grp->tasklet_info = NULL;
+		comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
 
-	comp_grp->tasklet_info = NULL;
-	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
+		if (in_rsrc_data->comp_grp_type >=
+			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
+			in_rsrc_data->comp_grp_type <=
+			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5)
+			list_add_tail(&comp_grp->list,
+				&ver2_bus_priv->free_dual_comp_grp);
+		else if (in_rsrc_data->comp_grp_type >=
+			CAM_VFE_BUS_VER2_COMP_GRP_0 &&
+			in_rsrc_data->comp_grp_type <=
+			CAM_VFE_BUS_VER2_COMP_GRP_5)
+			list_add_tail(&comp_grp->list,
+				&ver2_bus_priv->free_comp_grp);
+	}
 
 	return 0;
 }
@@ -1308,55 +1477,103 @@
 static int cam_vfe_bus_start_comp_grp(struct cam_isp_resource_node *comp_grp)
 {
 	int rc = 0;
+	uint32_t addr_sync_cfg;
 	struct cam_vfe_bus_ver2_comp_grp_data      *rsrc_data =
 		comp_grp->res_priv;
 	struct cam_vfe_bus_ver2_common_data        *common_data =
 		rsrc_data->common_data;
 	uint32_t bus_irq_reg_mask[CAM_VFE_BUS_IRQ_MAX] = {0};
 
+	CAM_DBG(CAM_ISP, "comp group id:%d streaming state:%d",
+		rsrc_data->comp_grp_type, comp_grp->res_state);
+
 	cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
 		rsrc_data->hw_regs->comp_mask);
+	if (comp_grp->res_state == CAM_ISP_RESOURCE_STATE_STREAMING)
+		return 0;
 
 	CAM_DBG(CAM_ISP, "composite_mask is 0x%x", rsrc_data->composite_mask);
 	CAM_DBG(CAM_ISP, "composite_mask addr 0x%x",
 		rsrc_data->hw_regs->comp_mask);
 
 	if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
-		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
-		rsrc_data->is_master) {
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) {
 		int dual_comp_grp = (rsrc_data->comp_grp_type -
 			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
-		int intra_client_en = cam_io_r_mb(common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
 
-		/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
-		intra_client_en |=
-			(rsrc_data->intra_client_mask << dual_comp_grp * 2);
+		if (rsrc_data->is_master) {
+			int intra_client_en = cam_io_r_mb(
+				common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
 
-		cam_io_w_mb(intra_client_en, common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
+			/*
+			 * 2 Bits per comp_grp. Hence left shift by
+			 * comp_grp * 2
+			 */
+			intra_client_en |=
+				(rsrc_data->intra_client_mask <<
+					(dual_comp_grp * 2));
 
-		bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG2] = (1 << dual_comp_grp);
+			cam_io_w_mb(intra_client_en, common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
+
+			bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG2] =
+				(1 << dual_comp_grp);
+		}
+
+		CAM_DBG(CAM_ISP, "addr_sync_mask addr 0x%x",
+			rsrc_data->hw_regs->addr_sync_mask);
+		cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
+			rsrc_data->hw_regs->addr_sync_mask);
+
+		addr_sync_cfg = cam_io_r_mb(common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+		addr_sync_cfg |= (rsrc_data->addr_sync_mode << dual_comp_grp);
+		/*
+		 * 2 Bits per dual_comp_grp. dual_comp_grp stats at bit number
+		 * 8. Hence left shift cdual_comp_grp dual comp_grp * 2 and
+		 * add 8
+		 */
+		addr_sync_cfg |=
+			(rsrc_data->intra_client_mask <<
+				((dual_comp_grp * 2) +
+				CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT));
+		cam_io_w_mb(addr_sync_cfg, common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+
+		common_data->addr_no_sync &= ~(rsrc_data->composite_mask);
+		cam_io_w_mb(common_data->addr_no_sync, common_data->mem_base +
+			common_data->common_reg->addr_sync_no_sync);
+		CAM_DBG(CAM_ISP, "addr_sync_cfg: 0x%x addr_no_sync_cfg: 0x%x",
+			addr_sync_cfg, common_data->addr_no_sync);
 	} else {
 		/* IRQ bits for COMP GRP start at 5. So add 5 to the shift */
 		bus_irq_reg_mask[CAM_VFE_BUS_IRQ_REG0] =
 			(1 << (rsrc_data->comp_grp_type + 5));
 	}
 
-	/* Subscribe IRQ */
+	/*
+	 * For Dual composite subscribe IRQ only for master
+	 * For regular composite, subscribe IRQ always
+	 */
 	CAM_DBG(CAM_ISP, "Subscribe COMP_GRP%d IRQ", rsrc_data->comp_grp_type);
-	comp_grp->irq_handle = cam_irq_controller_subscribe_irq(
-		common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1,
-		bus_irq_reg_mask, comp_grp,
-		comp_grp->top_half_handler,
-		cam_ife_mgr_do_tasklet_buf_done,
-		comp_grp->tasklet_info, cam_tasklet_enqueue_cmd);
-	if (comp_grp->irq_handle < 0) {
-		CAM_ERR(CAM_ISP, "Subscribe IRQ failed for comp_grp %d",
-			rsrc_data->comp_grp_type);
-		return -EFAULT;
+	if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) &&
+		(rsrc_data->is_master)) ||
+		(rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) {
+		comp_grp->irq_handle = cam_irq_controller_subscribe_irq(
+			common_data->bus_irq_controller, CAM_IRQ_PRIORITY_1,
+			bus_irq_reg_mask, comp_grp,
+			comp_grp->top_half_handler,
+			cam_ife_mgr_do_tasklet_buf_done,
+			comp_grp->tasklet_info, cam_tasklet_enqueue_cmd);
+		if (comp_grp->irq_handle < 0) {
+			CAM_ERR(CAM_ISP, "Subscribe IRQ failed for comp_grp %d",
+				rsrc_data->comp_grp_type);
+			return -EFAULT;
+		}
 	}
-
 	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
 
 	return rc;
@@ -1365,32 +1582,65 @@
 static int cam_vfe_bus_stop_comp_grp(struct cam_isp_resource_node *comp_grp)
 {
 	int rc = 0;
+	uint32_t addr_sync_cfg;
 	struct cam_vfe_bus_ver2_comp_grp_data      *rsrc_data =
 		comp_grp->res_priv;
 	struct cam_vfe_bus_ver2_common_data        *common_data =
 		rsrc_data->common_data;
 
 	/* Unsubscribe IRQ */
-	rc = cam_irq_controller_unsubscribe_irq(
-		common_data->bus_irq_controller,
-		comp_grp->irq_handle);
+	if (((rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) &&
+		(rsrc_data->is_master)) ||
+		(rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_0 &&
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_5)) {
+		rc = cam_irq_controller_unsubscribe_irq(
+			common_data->bus_irq_controller,
+			comp_grp->irq_handle);
+	}
 
 	cam_io_w_mb(rsrc_data->composite_mask, common_data->mem_base +
 		rsrc_data->hw_regs->comp_mask);
 	if (rsrc_data->comp_grp_type >= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0 &&
-		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5 &&
-		rsrc_data->is_master) {
+		rsrc_data->comp_grp_type <= CAM_VFE_BUS_VER2_COMP_GRP_DUAL_5) {
+
 		int dual_comp_grp = (rsrc_data->comp_grp_type -
 			CAM_VFE_BUS_VER2_COMP_GRP_DUAL_0);
-		int intra_client_en = cam_io_r_mb(common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
 
-		/* 2 Bits per comp_grp. Hence left shift by comp_grp * 2 */
-		intra_client_en &=
-			~(rsrc_data->intra_client_mask << dual_comp_grp * 2);
+		if (rsrc_data->is_master) {
+			int intra_client_en = cam_io_r_mb(
+				common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
 
-		cam_io_w_mb(intra_client_en, common_data->mem_base +
-			common_data->common_reg->dual_master_comp_cfg);
+			/*
+			 * 2 Bits per comp_grp. Hence left shift by
+			 * comp_grp * 2
+			 */
+			intra_client_en &=
+				~(rsrc_data->intra_client_mask <<
+					dual_comp_grp * 2);
+
+			cam_io_w_mb(intra_client_en, common_data->mem_base +
+				common_data->common_reg->dual_master_comp_cfg);
+		}
+
+		addr_sync_cfg = cam_io_r_mb(common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+		addr_sync_cfg &= ~(1 << dual_comp_grp);
+		addr_sync_cfg &= ~(CAM_VFE_BUS_INTRA_CLIENT_MASK <<
+			((dual_comp_grp * 2) +
+			CAM_VFE_BUS_ADDR_SYNC_INTRA_CLIENT_SHIFT));
+		cam_io_w_mb(addr_sync_cfg, common_data->mem_base +
+			common_data->common_reg->addr_sync_cfg);
+
+		cam_io_w_mb(0, common_data->mem_base +
+			rsrc_data->hw_regs->addr_sync_mask);
+		common_data->addr_no_sync |= rsrc_data->composite_mask;
+		cam_io_w_mb(common_data->addr_no_sync, common_data->mem_base +
+			common_data->common_reg->addr_sync_no_sync);
+		CAM_DBG(CAM_ISP, "addr_sync_cfg: 0x% addr_no_sync_cfg: 0x%x",
+			addr_sync_cfg, common_data->addr_no_sync);
+
 	}
 
 	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
@@ -1409,7 +1659,7 @@
 
 	comp_grp = th_payload->handler_priv;
 	if (!comp_grp) {
-		CAM_ERR_RATE_LIMIT(CAM_ISP, "Error! No resource\n");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "No resource");
 		return -ENODEV;
 	}
 
@@ -1417,11 +1667,12 @@
 
 	CAM_DBG(CAM_ISP, "IRQ status_0 = %x", th_payload->evt_status_arr[0]);
 	CAM_DBG(CAM_ISP, "IRQ status_1 = %x", th_payload->evt_status_arr[1]);
+	CAM_DBG(CAM_ISP, "IRQ status_2 = %x", th_payload->evt_status_arr[2]);
 
 	rc  = cam_vfe_bus_get_evt_payload(rsrc_data->common_data, &evt_payload);
 	if (rc) {
 		CAM_ERR_RATE_LIMIT(CAM_ISP,
-			"No tasklet_cmd is free in queue\n");
+			"No tasklet_cmd is free in queue");
 		return rc;
 	}
 
@@ -1491,8 +1742,13 @@
 
 		/* Regular Composite SUCCESS */
 		if (status_reg & BIT(comp_grp_id + 5)) {
-			cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0] &=
-				~BIT(comp_grp_id + 5);
+			rsrc_data->irq_trigger_cnt++;
+			if (rsrc_data->irq_trigger_cnt ==
+				rsrc_data->acquire_dev_cnt) {
+				cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS0] &=
+					~BIT(comp_grp_id + 5);
+				rsrc_data->irq_trigger_cnt = 0;
+			}
 			rc = CAM_VFE_IRQ_STATUS_SUCCESS;
 		}
 
@@ -1530,15 +1786,20 @@
 
 		/* DUAL Composite SUCCESS */
 		if (status_reg & BIT(comp_grp_id)) {
-			cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2] &=
-				~BIT(comp_grp_id + 5);
+			rsrc_data->irq_trigger_cnt++;
+			if (rsrc_data->irq_trigger_cnt ==
+				rsrc_data->acquire_dev_cnt) {
+				cam_ife_irq_regs[CAM_IFE_IRQ_BUS_REG_STATUS2] &=
+					~BIT(comp_grp_id);
+				rsrc_data->irq_trigger_cnt = 0;
+			}
 			rc = CAM_VFE_IRQ_STATUS_SUCCESS;
 		}
 
 		break;
 	default:
 		rc = CAM_VFE_IRQ_STATUS_ERR;
-		CAM_ERR(CAM_ISP, "Error! Invalid comp_grp_type %u",
+		CAM_ERR(CAM_ISP, "Invalid comp_grp_type %u",
 			rsrc_data->comp_grp_type);
 		break;
 	}
@@ -1559,10 +1820,9 @@
 
 	rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_comp_grp_data),
 		GFP_KERNEL);
-	if (!rsrc_data) {
-		CAM_DBG(CAM_ISP, "Failed to alloc for comp_grp_priv");
+	if (!rsrc_data)
 		return -ENOMEM;
-	}
+
 	comp_grp->res_priv = rsrc_data;
 
 	comp_grp->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
@@ -1609,7 +1869,7 @@
 	comp_grp->res_priv = NULL;
 
 	if (!rsrc_data) {
-		CAM_ERR(CAM_ISP, "Error! comp_grp_priv is NULL");
+		CAM_ERR(CAM_ISP, "comp_grp_priv is NULL");
 		return -ENODEV;
 	}
 	kfree(rsrc_data);
@@ -1617,6 +1877,22 @@
 	return 0;
 }
 
+static int cam_vfe_bus_get_secure_mode(void *priv, void *cmd_args,
+	uint32_t arg_size)
+{
+	bool *mode = cmd_args;
+	struct cam_isp_resource_node *res =
+		(struct cam_isp_resource_node *) priv;
+	struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data =
+		(struct cam_vfe_bus_ver2_vfe_out_data *)res->res_priv;
+
+	*mode =
+		(rsrc_data->secure_mode == CAM_SECURE_MODE_SECURE) ?
+		true : false;
+
+	return 0;
+}
+
 static int cam_vfe_bus_acquire_vfe_out(void *bus_priv, void *acquire_args,
 	uint32_t args_size)
 {
@@ -1624,7 +1900,7 @@
 	int                                     i;
 	enum cam_vfe_bus_ver2_vfe_out_type      vfe_out_res_id;
 	uint32_t                                format;
-	uint32_t                                num_wm;
+	int                                     num_wm;
 	uint32_t                                subscribe_irq;
 	uint32_t                                client_done_mask;
 	struct cam_vfe_bus_ver2_priv           *ver2_bus_priv = bus_priv;
@@ -1632,6 +1908,7 @@
 	struct cam_vfe_hw_vfe_out_acquire_args *out_acquire_args;
 	struct cam_isp_resource_node           *rsrc_node = NULL;
 	struct cam_vfe_bus_ver2_vfe_out_data   *rsrc_data = NULL;
+	uint32_t                                secure_caps = 0, mode;
 
 	if (!bus_priv || !acquire_args) {
 		CAM_ERR(CAM_ISP, "Invalid Param");
@@ -1661,6 +1938,33 @@
 	}
 
 	rsrc_data = rsrc_node->res_priv;
+	secure_caps = cam_vfe_bus_can_be_secure(
+		rsrc_data->out_type);
+	mode = out_acquire_args->out_port_info->secure_mode;
+	mutex_lock(&rsrc_data->common_data->bus_mutex);
+	if (secure_caps) {
+		if (!rsrc_data->common_data->num_sec_out) {
+			rsrc_data->secure_mode = mode;
+			rsrc_data->common_data->secure_mode = mode;
+		} else {
+			if (mode == rsrc_data->common_data->secure_mode) {
+				rsrc_data->secure_mode =
+					rsrc_data->common_data->secure_mode;
+			} else {
+				rc = -EINVAL;
+				CAM_ERR_RATE_LIMIT(CAM_ISP,
+					"Mismatch: Acquire mode[%d], drvr mode[%d]",
+					rsrc_data->common_data->secure_mode,
+					mode);
+				mutex_unlock(
+					&rsrc_data->common_data->bus_mutex);
+				return -EINVAL;
+			}
+		}
+		rsrc_data->common_data->num_sec_out++;
+	}
+	mutex_unlock(&rsrc_data->common_data->bus_mutex);
+
 	rsrc_data->num_wm = num_wm;
 	rsrc_node->res_id = out_acquire_args->out_port_info->res_type;
 	rsrc_node->tasklet_info = acq_args->tasklet;
@@ -1668,10 +1972,12 @@
 	rsrc_data->cdm_util_ops = out_acquire_args->cdm_ops;
 
 	/* Reserve Composite Group */
-	if (num_wm > 1 || (out_acquire_args->out_port_info->comp_grp_id >
-		CAM_ISP_RES_COMP_GROUP_NONE &&
-		out_acquire_args->out_port_info->comp_grp_id <
-		CAM_ISP_RES_COMP_GROUP_ID_MAX)) {
+		if (num_wm > 1 || (out_acquire_args->is_dual) ||
+			(out_acquire_args->out_port_info->comp_grp_id >
+			CAM_ISP_RES_COMP_GROUP_NONE &&
+			out_acquire_args->out_port_info->comp_grp_id <
+			CAM_ISP_RES_COMP_GROUP_ID_MAX)) {
+
 		rc = cam_vfe_bus_acquire_comp_grp(ver2_bus_priv,
 			out_acquire_args->out_port_info,
 			acq_args->tasklet,
@@ -1702,10 +2008,10 @@
 			out_acquire_args->ctx,
 			vfe_out_res_id,
 			i,
-			out_acquire_args->split_id,
 			subscribe_irq,
 			&rsrc_data->wm_res[i],
-			&client_done_mask);
+			&client_done_mask,
+			out_acquire_args->is_dual);
 		if (rc) {
 			CAM_ERR(CAM_ISP,
 				"VFE%d WM acquire failed for Out %d rc=%d",
@@ -1741,6 +2047,7 @@
 	uint32_t i;
 	struct cam_isp_resource_node          *vfe_out = NULL;
 	struct cam_vfe_bus_ver2_vfe_out_data  *rsrc_data = NULL;
+	uint32_t                               secure_caps = 0;
 
 	if (!bus_priv || !release_args) {
 		CAM_ERR(CAM_ISP, "Invalid input bus_priv %pK release_args %pK",
@@ -1752,7 +2059,7 @@
 	rsrc_data = vfe_out->res_priv;
 
 	if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
-		CAM_ERR(CAM_ISP, "Error! Invalid resource state:%d",
+		CAM_ERR(CAM_ISP, "Invalid resource state:%d",
 			vfe_out->res_state);
 	}
 
@@ -1768,6 +2075,32 @@
 	vfe_out->cdm_ops = NULL;
 	rsrc_data->cdm_util_ops = NULL;
 
+	secure_caps = cam_vfe_bus_can_be_secure(rsrc_data->out_type);
+	mutex_lock(&rsrc_data->common_data->bus_mutex);
+	if (secure_caps) {
+		if (rsrc_data->secure_mode ==
+			rsrc_data->common_data->secure_mode) {
+			rsrc_data->common_data->num_sec_out--;
+			rsrc_data->secure_mode =
+				CAM_SECURE_MODE_NON_SECURE;
+		} else {
+			/*
+			 * The validity of the mode is properly
+			 * checked while acquiring the output port.
+			 * not expected to reach here, unless there is
+			 * some corruption.
+			 */
+			CAM_ERR(CAM_ISP, "driver[%d],resource[%d] mismatch",
+				rsrc_data->common_data->secure_mode,
+				rsrc_data->secure_mode);
+		}
+
+		if (!rsrc_data->common_data->num_sec_out)
+			rsrc_data->common_data->secure_mode =
+				CAM_SECURE_MODE_NON_SECURE;
+	}
+	mutex_unlock(&rsrc_data->common_data->bus_mutex);
+
 	if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED)
 		vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
 
@@ -1792,7 +2125,7 @@
 	CAM_DBG(CAM_ISP, "Start resource index %d", rsrc_data->out_type);
 
 	if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_RESERVED) {
-		CAM_ERR(CAM_ISP, "Error! Invalid resource state:%d",
+		CAM_ERR(CAM_ISP, "Invalid resource state:%d",
 			vfe_out->res_state);
 		return -EACCES;
 	}
@@ -1803,28 +2136,6 @@
 	if (rsrc_data->comp_grp)
 		rc = cam_vfe_bus_start_comp_grp(rsrc_data->comp_grp);
 
-	/* BUS_WR_INPUT_IF_ADDR_SYNC_CFG */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000207C);
-	/*  BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002080);
-	/* BUS_WR_INPUT_IF_ADDR_SYNC_NO_SYNC */
-	cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x00002084);
-	/*  BUS_WR_INPUT_IF_ADDR_SYNC_0 */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002088);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000208c);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002090);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002094);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x00002098);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000209c);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a0);
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x000020a4);
-
-	/* no clock gating at bus input */
-	cam_io_w_mb(0xFFFFF, rsrc_data->common_data->mem_base + 0x0000200C);
-
-	/* BUS_WR_TEST_BUS_CTRL */
-	cam_io_w_mb(0x0, rsrc_data->common_data->mem_base + 0x0000211C);
-
 	vfe_out->res_state = CAM_ISP_RESOURCE_STATE_STREAMING;
 	return rc;
 }
@@ -1844,6 +2155,7 @@
 
 	if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_AVAILABLE ||
 		vfe_out->res_state == CAM_ISP_RESOURCE_STATE_RESERVED) {
+		CAM_DBG(CAM_ISP, "vfe_out res_state is %d", vfe_out->res_state);
 		return rc;
 	}
 
@@ -1853,7 +2165,6 @@
 	for (i = 0; i < rsrc_data->num_wm; i++)
 		rc = cam_vfe_bus_stop_wm(rsrc_data->wm_res[i]);
 
-
 	vfe_out->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
 	return rc;
 }
@@ -1888,39 +2199,59 @@
 	return rc;
 }
 
-static int cam_vfe_bus_init_vfe_out_resource(uint32_t index,
-	struct cam_vfe_bus_ver2_priv    *ver2_bus_priv,
-	struct cam_vfe_bus_ver2_hw_info *ver2_hw_info,
-	struct cam_isp_resource_node    *vfe_out)
+static int cam_vfe_bus_init_vfe_out_resource(uint32_t  index,
+	struct cam_vfe_bus_ver2_priv                  *ver2_bus_priv,
+	struct cam_vfe_bus_ver2_hw_info               *ver2_hw_info)
 {
+	struct cam_isp_resource_node         *vfe_out = NULL;
 	struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = NULL;
 	int rc = 0;
+	int32_t vfe_out_type =
+		ver2_hw_info->vfe_out_hw_info[index].vfe_out_type;
+
+	if (vfe_out_type < 0 ||
+		vfe_out_type >= CAM_VFE_BUS_VER2_VFE_OUT_MAX) {
+		CAM_ERR(CAM_ISP, "Init VFE Out failed, Invalid type=%d",
+			vfe_out_type);
+		return -EINVAL;
+	}
+
+	vfe_out = &ver2_bus_priv->vfe_out[vfe_out_type];
+	if (vfe_out->res_state != CAM_ISP_RESOURCE_STATE_UNAVAILABLE ||
+		vfe_out->res_priv) {
+		CAM_ERR(CAM_ISP,
+			"Error. Looks like same resource is init again");
+		return -EFAULT;
+	}
 
 	rsrc_data = kzalloc(sizeof(struct cam_vfe_bus_ver2_vfe_out_data),
 		GFP_KERNEL);
 	if (!rsrc_data) {
-		CAM_DBG(CAM_ISP, "Error! Failed to alloc for vfe out priv");
 		rc = -ENOMEM;
 		return rc;
 	}
+
 	vfe_out->res_priv = rsrc_data;
 
 	vfe_out->res_type = CAM_ISP_RESOURCE_VFE_OUT;
 	vfe_out->res_state = CAM_ISP_RESOURCE_STATE_AVAILABLE;
 	INIT_LIST_HEAD(&vfe_out->list);
 
-	rsrc_data->out_type    = index;
+	rsrc_data->out_type    =
+		ver2_hw_info->vfe_out_hw_info[index].vfe_out_type;
 	rsrc_data->common_data = &ver2_bus_priv->common_data;
 	rsrc_data->max_width   =
 		ver2_hw_info->vfe_out_hw_info[index].max_width;
 	rsrc_data->max_height  =
 		ver2_hw_info->vfe_out_hw_info[index].max_height;
+	rsrc_data->secure_mode = CAM_SECURE_MODE_NON_SECURE;
 
 	vfe_out->start = cam_vfe_bus_start_vfe_out;
 	vfe_out->stop = cam_vfe_bus_stop_vfe_out;
 	vfe_out->top_half_handler = cam_vfe_bus_handle_vfe_out_done_top_half;
 	vfe_out->bottom_half_handler =
 		cam_vfe_bus_handle_vfe_out_done_bottom_half;
+	vfe_out->process_cmd = cam_vfe_bus_process_cmd;
 	vfe_out->hw_intf = ver2_bus_priv->common_data.hw_intf;
 
 	return 0;
@@ -1931,6 +2262,15 @@
 {
 	struct cam_vfe_bus_ver2_vfe_out_data *rsrc_data = vfe_out->res_priv;
 
+	if (vfe_out->res_state == CAM_ISP_RESOURCE_STATE_UNAVAILABLE) {
+		/*
+		 * This is not error. It can happen if the resource is
+		 * never supported in the HW.
+		 */
+		CAM_DBG(CAM_ISP, "HW%d Res %d already deinitialized");
+		return 0;
+	}
+
 	vfe_out->start = NULL;
 	vfe_out->stop = NULL;
 	vfe_out->top_half_handler = NULL;
@@ -1941,10 +2281,8 @@
 	INIT_LIST_HEAD(&vfe_out->list);
 	vfe_out->res_priv = NULL;
 
-	if (!rsrc_data) {
-		CAM_ERR(CAM_ISP, "Error! vfe out priv is NULL");
+	if (!rsrc_data)
 		return -ENOMEM;
-	}
 	kfree(rsrc_data);
 
 	return 0;
@@ -1961,43 +2299,57 @@
 		bus_priv->common_data.bus_irq_controller);
 }
 
-static int cam_vfe_bus_update_buf(void *priv, void *cmd_args,
+static int cam_vfe_bus_error_irq_top_half(uint32_t evt_id,
+	struct cam_irq_th_payload *th_payload)
+{
+	int i = 0;
+	struct cam_vfe_bus_ver2_priv  *bus_priv = th_payload->handler_priv;
+
+	CAM_ERR_RATE_LIMIT(CAM_ISP, "Bus Err IRQ");
+	for (i = 0; i < th_payload->num_registers; i++) {
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "IRQ_Status%d: 0x%x", i,
+			th_payload->evt_status_arr[i]);
+	}
+	cam_irq_controller_disable_irq(bus_priv->common_data.bus_irq_controller,
+		bus_priv->error_irq_handle);
+
+	/* Returning error stops from enqueuing bottom half */
+	return -EFAULT;
+}
+
+static int cam_vfe_bus_update_wm(void *priv, void *cmd_args,
 	uint32_t arg_size)
 {
 	struct cam_vfe_bus_ver2_priv             *bus_priv;
-	struct cam_isp_hw_get_buf_update         *update_buf;
+	struct cam_isp_hw_get_cmd_update         *update_buf;
 	struct cam_buf_io_cfg                    *io_cfg;
 	struct cam_vfe_bus_ver2_vfe_out_data     *vfe_out_data = NULL;
 	struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL;
 	uint32_t *reg_val_pair;
 	uint32_t  i, j, size = 0;
-	uint32_t  frame_inc = 0;
-
-	/*
-	 * Need the entire buf io config so we can get the stride info
-	 * for the wm.
-	 */
+	uint32_t  frame_inc = 0, ubwc_bw_limit = 0, camera_hw_version, val;
+	int rc = 0;
 
 	bus_priv = (struct cam_vfe_bus_ver2_priv  *) priv;
-	update_buf =  (struct cam_isp_hw_get_buf_update *) cmd_args;
+	update_buf =  (struct cam_isp_hw_get_cmd_update *) cmd_args;
 
 	vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *)
-		update_buf->cdm.res->res_priv;
+		update_buf->res->res_priv;
 
 	if (!vfe_out_data || !vfe_out_data->cdm_util_ops) {
 		CAM_ERR(CAM_ISP, "Failed! Invalid data");
 		return -EINVAL;
 	}
 
-	if (update_buf->num_buf != vfe_out_data->num_wm) {
+	if (update_buf->wm_update->num_buf != vfe_out_data->num_wm) {
 		CAM_ERR(CAM_ISP,
 			"Failed! Invalid number buffers:%d required:%d",
-			update_buf->num_buf, vfe_out_data->num_wm);
+			update_buf->wm_update->num_buf, vfe_out_data->num_wm);
 		return -EINVAL;
 	}
 
 	reg_val_pair = &vfe_out_data->common_data->io_buf_update[0];
-	io_cfg = update_buf->io_cfg;
+	io_cfg = update_buf->wm_update->io_cfg;
 
 	for (i = 0, j = 0; i < vfe_out_data->num_wm; i++) {
 		if (j >= (MAX_REG_VAL_PAIR_SIZE - MAX_BUF_UPDATE_REG_NUM * 2)) {
@@ -2009,15 +2361,33 @@
 
 		wm_data = vfe_out_data->wm_res[i]->res_priv;
 
+		/* update width register */
+		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
+			wm_data->hw_regs->buffer_width_cfg,
+			wm_data->width);
+		CAM_DBG(CAM_ISP, "WM %d image width 0x%x",
+			wm_data->index, reg_val_pair[j-1]);
+
 		/* For initial configuration program all bus registers */
-		if ((wm_data->stride != io_cfg->planes[i].plane_stride ||
+		val = io_cfg->planes[i].plane_stride;
+		CAM_DBG(CAM_ISP, "before stride %d", val);
+		val = ALIGNUP(val, 16);
+		if (val != io_cfg->planes[i].plane_stride &&
+			val != wm_data->stride)
+			CAM_WARN(CAM_ISP,
+				"Warning stride %u expected %u",
+				io_cfg->planes[i].plane_stride,
+				val);
+
+		if ((wm_data->stride != val ||
 			!wm_data->init_cfg_done) && (wm_data->index >= 3)) {
 			CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 				wm_data->hw_regs->stride,
 				io_cfg->planes[i].plane_stride);
-			wm_data->stride = io_cfg->planes[i].plane_stride;
+			wm_data->stride = val;
+			CAM_DBG(CAM_ISP, "WM %d image stride 0x%x",
+				wm_data->index, reg_val_pair[j-1]);
 		}
-		CAM_DBG(CAM_ISP, "image stride 0x%x", wm_data->stride);
 
 		if (wm_data->framedrop_pattern != io_cfg->framedrop_pattern ||
 			!wm_data->hfr_cfg_done) {
@@ -2025,9 +2395,10 @@
 				wm_data->hw_regs->framedrop_pattern,
 				io_cfg->framedrop_pattern);
 			wm_data->framedrop_pattern = io_cfg->framedrop_pattern;
+			CAM_DBG(CAM_ISP, "WM %d framedrop pattern 0x%x",
+				wm_data->index, reg_val_pair[j-1]);
 		}
-		CAM_DBG(CAM_ISP, "framedrop pattern 0x%x",
-			wm_data->framedrop_pattern);
+
 
 		if (wm_data->framedrop_period != io_cfg->framedrop_period ||
 			!wm_data->hfr_cfg_done) {
@@ -2035,9 +2406,9 @@
 				wm_data->hw_regs->framedrop_period,
 				io_cfg->framedrop_period);
 			wm_data->framedrop_period = io_cfg->framedrop_period;
+			CAM_DBG(CAM_ISP, "WM %d framedrop period 0x%x",
+				wm_data->index, reg_val_pair[j-1]);
 		}
-		CAM_DBG(CAM_ISP, "framedrop period 0x%x",
-			wm_data->framedrop_period);
 
 		if (wm_data->irq_subsample_period != io_cfg->subsample_period
 			|| !wm_data->hfr_cfg_done) {
@@ -2046,9 +2417,9 @@
 				io_cfg->subsample_period);
 			wm_data->irq_subsample_period =
 				io_cfg->subsample_period;
+			CAM_DBG(CAM_ISP, "WM %d irq subsample period 0x%x",
+				wm_data->index, reg_val_pair[j-1]);
 		}
-		CAM_DBG(CAM_ISP, "irq subsample period 0x%x",
-			wm_data->irq_subsample_period);
 
 		if (wm_data->irq_subsample_pattern != io_cfg->subsample_pattern
 			|| !wm_data->hfr_cfg_done) {
@@ -2057,9 +2428,9 @@
 				io_cfg->subsample_pattern);
 			wm_data->irq_subsample_pattern =
 				io_cfg->subsample_pattern;
+			CAM_DBG(CAM_ISP, "WM %d irq subsample pattern 0x%x",
+				wm_data->index, reg_val_pair[j-1]);
 		}
-		CAM_DBG(CAM_ISP, "irq subsample pattern 0x%x",
-			wm_data->irq_subsample_pattern);
 
 		if (wm_data->en_ubwc) {
 			if (!wm_data->hw_regs->ubwc_regs) {
@@ -2075,28 +2446,50 @@
 					io_cfg->planes[i].packer_config);
 				wm_data->packer_cfg =
 					io_cfg->planes[i].packer_config;
+				CAM_DBG(CAM_ISP, "WM %d packer cfg 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "packer cfg 0x%x",
-				wm_data->packer_cfg);
 
-			if (wm_data->tile_cfg != io_cfg->planes[i].tile_config
+			if (wm_data->is_dual) {
+				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
+					wm_data->hw_regs->ubwc_regs->tile_cfg,
+					wm_data->tile_cfg);
+			} else if ((wm_data->tile_cfg !=
+				io_cfg->planes[i].tile_config)
 				|| !wm_data->init_cfg_done) {
 				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 					wm_data->hw_regs->ubwc_regs->tile_cfg,
 					io_cfg->planes[i].tile_config);
 				wm_data->tile_cfg =
 					io_cfg->planes[i].tile_config;
+				CAM_DBG(CAM_ISP, "WM %d tile cfg 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "tile cfg 0x%x", wm_data->tile_cfg);
 
-			if (wm_data->h_init != io_cfg->planes[i].h_init ||
+			if (wm_data->is_dual) {
+				if ((wm_data->h_init != wm_data->offset) ||
+					!wm_data->init_cfg_done) {
+					/*
+					 * For dual ife h init value need to
+					 * take from offset.  Striping config
+					 * update offset value.
+					 */
+					CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair,
+						j,
+						wm_data->hw_regs->ubwc_regs->
+						h_init, wm_data->offset);
+					wm_data->h_init = wm_data->offset;
+				}
+			} else if (wm_data->h_init !=
+				io_cfg->planes[i].h_init ||
 				!wm_data->init_cfg_done) {
 				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 					wm_data->hw_regs->ubwc_regs->h_init,
 					io_cfg->planes[i].h_init);
 				wm_data->h_init = io_cfg->planes[i].h_init;
+				CAM_DBG(CAM_ISP, "WM %d h_init 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "h_init 0x%x", wm_data->h_init);
 
 			if (wm_data->v_init != io_cfg->planes[i].v_init ||
 				!wm_data->init_cfg_done) {
@@ -2104,8 +2497,9 @@
 					wm_data->hw_regs->ubwc_regs->v_init,
 					io_cfg->planes[i].v_init);
 				wm_data->v_init = io_cfg->planes[i].v_init;
+				CAM_DBG(CAM_ISP, "WM %d v_init 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "v_init 0x%x", wm_data->v_init);
 
 			if (wm_data->ubwc_meta_stride !=
 				io_cfg->planes[i].meta_stride ||
@@ -2116,9 +2510,9 @@
 					io_cfg->planes[i].meta_stride);
 				wm_data->ubwc_meta_stride =
 					io_cfg->planes[i].meta_stride;
+				CAM_DBG(CAM_ISP, "WM %d meta stride 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "meta stride 0x%x",
-				wm_data->ubwc_meta_stride);
 
 			if (wm_data->ubwc_mode_cfg !=
 				io_cfg->planes[i].mode_config ||
@@ -2128,9 +2522,9 @@
 					io_cfg->planes[i].mode_config);
 				wm_data->ubwc_mode_cfg =
 					io_cfg->planes[i].mode_config;
+				CAM_DBG(CAM_ISP, "WM %d ubwc mode cfg 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "ubwc mode cfg 0x%x",
-				wm_data->ubwc_mode_cfg);
 
 			if (wm_data->ubwc_meta_offset !=
 				io_cfg->planes[i].meta_offset ||
@@ -2141,35 +2535,65 @@
 					io_cfg->planes[i].meta_offset);
 				wm_data->ubwc_meta_offset =
 					io_cfg->planes[i].meta_offset;
+				CAM_DBG(CAM_ISP, "WM %d ubwc meta offset 0x%x",
+					wm_data->index, reg_val_pair[j-1]);
 			}
-			CAM_DBG(CAM_ISP, "ubwc meta offset 0x%x",
-				wm_data->ubwc_meta_offset);
 
 			/* UBWC meta address */
 			CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 				wm_data->hw_regs->ubwc_regs->meta_addr,
-				update_buf->image_buf[i]);
-			CAM_DBG(CAM_ISP, "ubwc meta addr 0x%llx",
-				update_buf->image_buf[i]);
+				update_buf->wm_update->image_buf[i]);
+			CAM_DBG(CAM_ISP, "WM %d ubwc meta addr 0x%llx",
+				wm_data->index,
+				update_buf->wm_update->image_buf[i]);
+
+			/* Enable UBWC bandwidth limit if required */
+			rc = cam_cpas_get_cpas_hw_version(&camera_hw_version);
+			if (camera_hw_version == CAM_CPAS_TITAN_170_V110
+					&& !rc) {
+				switch (wm_data->format) {
+				case CAM_FORMAT_UBWC_TP10:
+					ubwc_bw_limit = 0x8 | BIT(0);
+					break;
+				case CAM_FORMAT_UBWC_NV12_4R:
+					ubwc_bw_limit = 0xB | BIT(0);
+					break;
+				default:
+					ubwc_bw_limit = 0;
+					break;
+				}
+			}
+
+			if (ubwc_bw_limit) {
+				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
+					wm_data->hw_regs->ubwc_regs->bw_limit,
+					ubwc_bw_limit);
+				CAM_DBG(CAM_ISP, "WM %d ubwc bw limit 0x%x",
+					wm_data->index, ubwc_bw_limit);
+			}
 		}
 
 		/* WM Image address */
 		if (wm_data->en_ubwc)
 			CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 				wm_data->hw_regs->image_addr,
-				(update_buf->image_buf[i] +
+				(update_buf->wm_update->image_buf[i] +
 				io_cfg->planes[i].meta_size));
 		else
 			CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 				wm_data->hw_regs->image_addr,
-				update_buf->image_buf[i]);
-
-		CAM_DBG(CAM_ISP, "image address 0x%x", reg_val_pair[j-1]);
+				update_buf->wm_update->image_buf[i] +
+				wm_data->offset);
+		CAM_DBG(CAM_ISP, "WM %d image address 0x%x",
+			wm_data->index, reg_val_pair[j-1]);
 
 		frame_inc = io_cfg->planes[i].plane_stride *
 			io_cfg->planes[i].slice_height;
 		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 			wm_data->hw_regs->frame_inc, frame_inc);
+		CAM_DBG(CAM_ISP, "WM %d frame_inc %d",
+			wm_data->index, reg_val_pair[j-1]);
+
 
 		/* enable the WM */
 		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
@@ -2184,18 +2608,18 @@
 	size = vfe_out_data->cdm_util_ops->cdm_required_size_reg_random(j/2);
 
 	/* cdm util returns dwords, need to convert to bytes */
-	if ((size * 4) > update_buf->cdm.size) {
+	if ((size * 4) > update_buf->cmd.size) {
 		CAM_ERR(CAM_ISP,
 			"Failed! Buf size:%d insufficient, expected size:%d",
-			update_buf->cdm.size, size);
+			update_buf->cmd.size, size);
 		return -ENOMEM;
 	}
 
 	vfe_out_data->cdm_util_ops->cdm_write_regrandom(
-		update_buf->cdm.cmd_buf_addr, j/2, reg_val_pair);
+		update_buf->cmd.cmd_buf_addr, j/2, reg_val_pair);
 
 	/* cdm util returns dwords, need to convert to bytes */
-	update_buf->cdm.used_bytes = size * 4;
+	update_buf->cmd.used_bytes = size * 4;
 
 	return 0;
 }
@@ -2204,7 +2628,7 @@
 	uint32_t arg_size)
 {
 	struct cam_vfe_bus_ver2_priv             *bus_priv;
-	struct cam_isp_hw_get_hfr_update         *update_hfr;
+	struct cam_isp_hw_get_cmd_update         *update_hfr;
 	struct cam_vfe_bus_ver2_vfe_out_data     *vfe_out_data = NULL;
 	struct cam_vfe_bus_ver2_wm_resource_data *wm_data = NULL;
 	struct cam_isp_port_hfr_config           *hfr_cfg = NULL;
@@ -2212,10 +2636,10 @@
 	uint32_t  i, j, size = 0;
 
 	bus_priv = (struct cam_vfe_bus_ver2_priv  *) priv;
-	update_hfr =  (struct cam_isp_hw_get_hfr_update *) cmd_args;
+	update_hfr =  (struct cam_isp_hw_get_cmd_update *) cmd_args;
 
 	vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *)
-		update_hfr->cdm.res->res_priv;
+		update_hfr->res->res_priv;
 
 	if (!vfe_out_data || !vfe_out_data->cdm_util_ops) {
 		CAM_ERR(CAM_ISP, "Failed! Invalid data");
@@ -2223,7 +2647,7 @@
 	}
 
 	reg_val_pair = &vfe_out_data->common_data->io_buf_update[0];
-	hfr_cfg = update_hfr->io_hfr_cfg;
+	hfr_cfg = update_hfr->hfr_update;
 
 	for (i = 0, j = 0; i < vfe_out_data->num_wm; i++) {
 		if (j >= (MAX_REG_VAL_PAIR_SIZE - MAX_BUF_UPDATE_REG_NUM * 2)) {
@@ -2242,9 +2666,9 @@
 				wm_data->hw_regs->framedrop_pattern,
 				hfr_cfg->framedrop_pattern);
 			wm_data->framedrop_pattern = hfr_cfg->framedrop_pattern;
+			CAM_DBG(CAM_ISP, "WM %d framedrop pattern 0x%x",
+				wm_data->index, wm_data->framedrop_pattern);
 		}
-		CAM_DBG(CAM_ISP, "framedrop pattern 0x%x",
-			wm_data->framedrop_pattern);
 
 		if (wm_data->framedrop_period != hfr_cfg->framedrop_period ||
 			!wm_data->hfr_cfg_done) {
@@ -2252,9 +2676,9 @@
 				wm_data->hw_regs->framedrop_period,
 				hfr_cfg->framedrop_period);
 			wm_data->framedrop_period = hfr_cfg->framedrop_period;
+			CAM_DBG(CAM_ISP, "WM %d framedrop period 0x%x",
+				wm_data->index, wm_data->framedrop_period);
 		}
-		CAM_DBG(CAM_ISP, "framedrop period 0x%x",
-			wm_data->framedrop_period);
 
 		if (wm_data->irq_subsample_period != hfr_cfg->subsample_period
 			|| !wm_data->hfr_cfg_done) {
@@ -2263,9 +2687,9 @@
 				hfr_cfg->subsample_period);
 			wm_data->irq_subsample_period =
 				hfr_cfg->subsample_period;
+			CAM_DBG(CAM_ISP, "WM %d irq subsample period 0x%x",
+				wm_data->index, wm_data->irq_subsample_period);
 		}
-		CAM_DBG(CAM_ISP, "irq subsample period 0x%x",
-			wm_data->irq_subsample_period);
 
 		if (wm_data->irq_subsample_pattern != hfr_cfg->subsample_pattern
 			|| !wm_data->hfr_cfg_done) {
@@ -2274,9 +2698,9 @@
 				hfr_cfg->subsample_pattern);
 			wm_data->irq_subsample_pattern =
 				hfr_cfg->subsample_pattern;
+			CAM_DBG(CAM_ISP, "WM %d irq subsample pattern 0x%x",
+				wm_data->index, wm_data->irq_subsample_pattern);
 		}
-		CAM_DBG(CAM_ISP, "irq subsample pattern 0x%x",
-			wm_data->irq_subsample_pattern);
 
 		/* set initial configuration done */
 		if (!wm_data->hfr_cfg_done)
@@ -2286,18 +2710,62 @@
 	size = vfe_out_data->cdm_util_ops->cdm_required_size_reg_random(j/2);
 
 	/* cdm util returns dwords, need to convert to bytes */
-	if ((size * 4) > update_hfr->cdm.size) {
+	if ((size * 4) > update_hfr->cmd.size) {
 		CAM_ERR(CAM_ISP,
 			"Failed! Buf size:%d insufficient, expected size:%d",
-			update_hfr->cdm.size, size);
+			update_hfr->cmd.size, size);
 		return -ENOMEM;
 	}
 
 	vfe_out_data->cdm_util_ops->cdm_write_regrandom(
-		update_hfr->cdm.cmd_buf_addr, j/2, reg_val_pair);
+		update_hfr->cmd.cmd_buf_addr, j/2, reg_val_pair);
 
 	/* cdm util returns dwords, need to convert to bytes */
-	update_hfr->cdm.used_bytes = size * 4;
+	update_hfr->cmd.used_bytes = size * 4;
+
+	return 0;
+}
+
+static int cam_vfe_bus_update_stripe_cfg(void *priv, void *cmd_args,
+	uint32_t arg_size)
+{
+	struct cam_vfe_bus_ver2_priv                *bus_priv;
+	struct cam_isp_hw_dual_isp_update_args      *stripe_args;
+	struct cam_vfe_bus_ver2_vfe_out_data        *vfe_out_data = NULL;
+	struct cam_vfe_bus_ver2_wm_resource_data    *wm_data = NULL;
+	struct cam_isp_dual_stripe_config           *stripe_config;
+	uint32_t outport_id, ports_plane_idx, i;
+
+	bus_priv = (struct cam_vfe_bus_ver2_priv  *) priv;
+	stripe_args = (struct cam_isp_hw_dual_isp_update_args *)cmd_args;
+
+	vfe_out_data = (struct cam_vfe_bus_ver2_vfe_out_data *)
+		stripe_args->res->res_priv;
+
+	if (!vfe_out_data) {
+		CAM_ERR(CAM_ISP, "Failed! Invalid data");
+		return -EINVAL;
+	}
+
+	outport_id = stripe_args->res->res_id & 0xFF;
+	if (stripe_args->res->res_id < CAM_ISP_IFE_OUT_RES_BASE ||
+		stripe_args->res->res_id >= CAM_ISP_IFE_OUT_RES_MAX)
+		return 0;
+
+	ports_plane_idx = (stripe_args->split_id *
+	(stripe_args->dual_cfg->num_ports * CAM_PACKET_MAX_PLANES)) +
+	(outport_id * CAM_PACKET_MAX_PLANES);
+	for (i = 0; i < vfe_out_data->num_wm; i++) {
+		wm_data = vfe_out_data->wm_res[i]->res_priv;
+		stripe_config = (struct cam_isp_dual_stripe_config  *)
+			&stripe_args->dual_cfg->stripes[ports_plane_idx + i];
+		wm_data->width = stripe_config->width;
+		wm_data->offset = stripe_config->offset;
+		wm_data->tile_cfg = stripe_config->tileconfig;
+		CAM_DBG(CAM_ISP, "id:%x wm:%d width:0x%x offset:%x tilecfg:%x",
+			stripe_args->res->res_id, i, wm_data->width,
+			wm_data->offset, wm_data->tile_cfg);
+	}
 
 	return 0;
 }
@@ -2321,7 +2789,7 @@
 	uint32_t                         top_irq_reg_mask[2] = {0};
 
 	if (!bus_priv) {
-		CAM_ERR(CAM_ISP, "Error! Invalid args");
+		CAM_ERR(CAM_ISP, "Invalid args");
 		return -EINVAL;
 	}
 
@@ -2342,6 +2810,36 @@
 		return -EFAULT;
 	}
 
+	bus_priv->error_irq_handle = cam_irq_controller_subscribe_irq(
+		bus_priv->common_data.bus_irq_controller,
+		CAM_IRQ_PRIORITY_0,
+		bus_error_irq_mask,
+		bus_priv,
+		cam_vfe_bus_error_irq_top_half,
+		NULL,
+		NULL,
+		NULL);
+
+	if (bus_priv->irq_handle <= 0) {
+		CAM_ERR(CAM_ISP, "Failed to subscribe BUS IRQ");
+		return -EFAULT;
+	}
+
+	/* BUS_WR_INPUT_IF_ADDR_SYNC_FRAME_HEADER */
+	cam_io_w_mb(0x0, bus_priv->common_data.mem_base +
+		bus_priv->common_data.common_reg->addr_sync_frame_hdr);
+
+	/* no clock gating at bus input */
+	cam_io_w_mb(0xFFFFF, bus_priv->common_data.mem_base + 0x0000200C);
+
+	/* BUS_WR_TEST_BUS_CTRL */
+	cam_io_w_mb(0x0, bus_priv->common_data.mem_base + 0x0000211C);
+
+	/* if addr_no_sync has default value then config the addr no sync reg */
+	cam_io_w_mb(CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL,
+		bus_priv->common_data.mem_base +
+		bus_priv->common_data.common_reg->addr_sync_no_sync);
+
 	return 0;
 }
 
@@ -2351,39 +2849,63 @@
 	struct cam_vfe_bus_ver2_priv    *bus_priv = hw_priv;
 	int                              rc;
 
-	if (!bus_priv || (bus_priv->irq_handle <= 0)) {
-		CAM_ERR(CAM_ISP, "Error! Invalid args");
+	if (!bus_priv || (bus_priv->irq_handle <= 0) ||
+		(bus_priv->error_irq_handle <= 0)) {
+		CAM_ERR(CAM_ISP, "Error: Invalid args");
 		return -EINVAL;
 	}
 
 	rc = cam_irq_controller_unsubscribe_irq(
+		bus_priv->common_data.bus_irq_controller,
+		bus_priv->error_irq_handle);
+	if (rc)
+		CAM_ERR(CAM_ISP, "Failed to unsubscribe error irq rc=%d", rc);
+
+	bus_priv->error_irq_handle = 0;
+
+	rc = cam_irq_controller_unsubscribe_irq(
 		bus_priv->common_data.vfe_irq_controller,
 		bus_priv->irq_handle);
 	if (rc)
 		CAM_ERR(CAM_ISP, "Failed to unsubscribe irq rc=%d", rc);
 
+	bus_priv->irq_handle = 0;
+
 	return rc;
 }
 
-static int cam_vfe_bus_process_cmd(void *priv,
+static int __cam_vfe_bus_process_cmd(void *priv,
+	uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
+{
+	return cam_vfe_bus_process_cmd(priv, cmd_type, cmd_args, arg_size);
+}
+
+static int cam_vfe_bus_process_cmd(
+	struct cam_isp_resource_node *priv,
 	uint32_t cmd_type, void *cmd_args, uint32_t arg_size)
 {
 	int rc = -EINVAL;
 
 	if (!priv || !cmd_args) {
-		CAM_ERR_RATE_LIMIT(CAM_ISP, "Error! Invalid input arguments\n");
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid input arguments");
 		return -EINVAL;
 	}
 
 	switch (cmd_type) {
-	case CAM_VFE_HW_CMD_GET_BUF_UPDATE:
-		rc = cam_vfe_bus_update_buf(priv, cmd_args, arg_size);
+	case CAM_ISP_HW_CMD_GET_BUF_UPDATE:
+		rc = cam_vfe_bus_update_wm(priv, cmd_args, arg_size);
 		break;
-	case CAM_VFE_HW_CMD_GET_HFR_UPDATE:
+	case CAM_ISP_HW_CMD_GET_HFR_UPDATE:
 		rc = cam_vfe_bus_update_hfr(priv, cmd_args, arg_size);
 		break;
+	case CAM_ISP_HW_CMD_GET_SECURE_MODE:
+		rc = cam_vfe_bus_get_secure_mode(priv, cmd_args, arg_size);
+		break;
+	case CAM_ISP_HW_CMD_STRIPE_UPDATE:
+		rc = cam_vfe_bus_update_stripe_cfg(priv, cmd_args, arg_size);
+		break;
 	default:
-		CAM_ERR_RATE_LIMIT(CAM_ISP, "Inval camif process command:%d\n",
+		CAM_ERR_RATE_LIMIT(CAM_ISP, "Invalid camif process command:%d",
 			cmd_type);
 		break;
 	}
@@ -2430,18 +2952,26 @@
 	}
 	vfe_bus_local->bus_priv = bus_priv;
 
+	bus_priv->num_client                     = ver2_hw_info->num_client;
+	bus_priv->num_out                        = ver2_hw_info->num_out;
+	bus_priv->common_data.num_sec_out        = 0;
+	bus_priv->common_data.secure_mode        = CAM_SECURE_MODE_NON_SECURE;
 	bus_priv->common_data.core_index         = soc_info->index;
 	bus_priv->common_data.mem_base           =
 		CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX);
 	bus_priv->common_data.hw_intf            = hw_intf;
 	bus_priv->common_data.vfe_irq_controller = vfe_irq_controller;
 	bus_priv->common_data.common_reg         = &ver2_hw_info->common_reg;
+	bus_priv->common_data.addr_no_sync       =
+		CAM_VFE_BUS_ADDR_NO_SYNC_DEFAULT_VAL;
+
+	mutex_init(&bus_priv->common_data.bus_mutex);
 
 	rc = cam_irq_controller_init(drv_name, bus_priv->common_data.mem_base,
 		&ver2_hw_info->common_reg.irq_reg_info,
 		&bus_priv->common_data.bus_irq_controller);
 	if (rc) {
-		CAM_ERR(CAM_ISP, "Error! cam_irq_controller_init failed");
+		CAM_ERR(CAM_ISP, "cam_irq_controller_init failed");
 		goto free_bus_priv;
 	}
 
@@ -2449,11 +2979,11 @@
 	INIT_LIST_HEAD(&bus_priv->free_dual_comp_grp);
 	INIT_LIST_HEAD(&bus_priv->used_comp_grp);
 
-	for (i = 0; i < CAM_VFE_BUS_VER2_MAX_CLIENTS; i++) {
+	for (i = 0; i < bus_priv->num_client; i++) {
 		rc = cam_vfe_bus_init_wm_resource(i, bus_priv, bus_hw_info,
 			&bus_priv->bus_client[i]);
 		if (rc < 0) {
-			CAM_ERR(CAM_ISP, "Error! Init WM failed rc=%d", rc);
+			CAM_ERR(CAM_ISP, "Init WM failed rc=%d", rc);
 			goto deinit_wm;
 		}
 	}
@@ -2467,9 +2997,9 @@
 		}
 	}
 
-	for (i = 0; i < CAM_VFE_BUS_VER2_VFE_OUT_MAX; i++) {
-		rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv, bus_hw_info,
-			&bus_priv->vfe_out[i]);
+	for (i = 0; i < bus_priv->num_out; i++) {
+		rc = cam_vfe_bus_init_vfe_out_resource(i, bus_priv,
+			bus_hw_info);
 		if (rc < 0) {
 			CAM_ERR(CAM_ISP, "Init VFE Out failed rc=%d", rc);
 			goto deinit_vfe_out;
@@ -2491,7 +3021,7 @@
 	vfe_bus_local->hw_ops.deinit       = cam_vfe_bus_deinit_hw;
 	vfe_bus_local->top_half_handler    = cam_vfe_bus_ver2_handle_irq;
 	vfe_bus_local->bottom_half_handler = NULL;
-	vfe_bus_local->hw_ops.process_cmd  = cam_vfe_bus_process_cmd;
+	vfe_bus_local->hw_ops.process_cmd  = __cam_vfe_bus_process_cmd;
 
 	*vfe_bus = vfe_bus_local;
 
@@ -2512,7 +3042,7 @@
 
 deinit_wm:
 	if (i < 0)
-		i = CAM_VFE_BUS_VER2_MAX_CLIENTS;
+		i = bus_priv->num_client;
 	for (--i; i >= 0; i--)
 		cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]);
 
@@ -2534,14 +3064,14 @@
 	struct cam_vfe_bus              *vfe_bus_local;
 
 	if (!vfe_bus || !*vfe_bus) {
-		CAM_ERR(CAM_ISP, "Error! Invalid input");
+		CAM_ERR(CAM_ISP, "Invalid input");
 		return -EINVAL;
 	}
 	vfe_bus_local = *vfe_bus;
 
 	bus_priv = vfe_bus_local->bus_priv;
 	if (!bus_priv) {
-		CAM_ERR(CAM_ISP, "Error! bus_priv is NULL");
+		CAM_ERR(CAM_ISP, "bus_priv is NULL");
 		rc = -ENODEV;
 		goto free_bus_local;
 	}
@@ -2550,25 +3080,25 @@
 	for (i = 0; i < CAM_VFE_BUS_VER2_PAYLOAD_MAX; i++)
 		INIT_LIST_HEAD(&bus_priv->common_data.evt_payload[i].list);
 
-	for (i = 0; i < CAM_VFE_BUS_VER2_MAX_CLIENTS; i++) {
+	for (i = 0; i < bus_priv->num_client; i++) {
 		rc = cam_vfe_bus_deinit_wm_resource(&bus_priv->bus_client[i]);
 		if (rc < 0)
 			CAM_ERR(CAM_ISP,
-				"Error! Deinit WM failed rc=%d", rc);
+				"Deinit WM failed rc=%d", rc);
 	}
 
 	for (i = 0; i < CAM_VFE_BUS_VER2_COMP_GRP_MAX; i++) {
 		rc = cam_vfe_bus_deinit_comp_grp(&bus_priv->comp_grp[i]);
 		if (rc < 0)
 			CAM_ERR(CAM_ISP,
-				"Error! Deinit Comp Grp failed rc=%d", rc);
+				"Deinit Comp Grp failed rc=%d", rc);
 	}
 
 	for (i = 0; i < CAM_VFE_BUS_VER2_VFE_OUT_MAX; i++) {
 		rc = cam_vfe_bus_deinit_vfe_out_resource(&bus_priv->vfe_out[i]);
 		if (rc < 0)
 			CAM_ERR(CAM_ISP,
-				"Error! Deinit VFE Out failed rc=%d", rc);
+				"Deinit VFE Out failed rc=%d", rc);
 	}
 
 	INIT_LIST_HEAD(&bus_priv->free_comp_grp);
@@ -2579,8 +3109,9 @@
 		&bus_priv->common_data.bus_irq_controller);
 	if (rc)
 		CAM_ERR(CAM_ISP,
-			"Error! Deinit IRQ Controller failed rc=%d", rc);
+			"Deinit IRQ Controller failed rc=%d", rc);
 
+	mutex_destroy(&bus_priv->common_data.bus_mutex);
 	kfree(vfe_bus_local->bus_priv);
 
 free_bus_local:
@@ -2590,4 +3121,3 @@
 
 	return rc;
 }
-
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
index ba98077..5a12f74 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/cam_vfe_bus_ver2.h
@@ -21,7 +21,6 @@
 enum cam_vfe_bus_ver2_vfe_core_id {
 	CAM_VFE_BUS_VER2_VFE_CORE_0,
 	CAM_VFE_BUS_VER2_VFE_CORE_1,
-	CAM_VFE_BUS_VER2_VFE_CORE_2,
 	CAM_VFE_BUS_VER2_VFE_CORE_MAX,
 };
 
@@ -42,15 +41,16 @@
 };
 
 enum cam_vfe_bus_ver2_vfe_out_type {
+	CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
+	CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
+	CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
+	CAM_VFE_BUS_VER2_VFE_OUT_RDI3,
 	CAM_VFE_BUS_VER2_VFE_OUT_FULL,
 	CAM_VFE_BUS_VER2_VFE_OUT_DS4,
 	CAM_VFE_BUS_VER2_VFE_OUT_DS16,
 	CAM_VFE_BUS_VER2_VFE_OUT_RAW_DUMP,
 	CAM_VFE_BUS_VER2_VFE_OUT_FD,
 	CAM_VFE_BUS_VER2_VFE_OUT_PDAF,
-	CAM_VFE_BUS_VER2_VFE_OUT_RDI0,
-	CAM_VFE_BUS_VER2_VFE_OUT_RDI1,
-	CAM_VFE_BUS_VER2_VFE_OUT_RDI2,
 	CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BE,
 	CAM_VFE_BUS_VER2_VFE_OUT_STATS_HDR_BHIST,
 	CAM_VFE_BUS_VER2_VFE_OUT_STATS_TL_BG,
@@ -81,8 +81,8 @@
 	uint32_t dual_comp_error_status;
 	uint32_t dual_comp_ovrwr_status;
 	uint32_t addr_sync_cfg;
-	uint32_t addr_syn_frame_hdr;
-	uint32_t addr_syn_no_sync;
+	uint32_t addr_sync_frame_hdr;
+	uint32_t addr_sync_no_sync;
 };
 
 /*
@@ -98,6 +98,7 @@
 	uint32_t meta_offset;
 	uint32_t meta_stride;
 	uint32_t mode_cfg;
+	uint32_t bw_limit;
 };
 
 /*
@@ -130,9 +131,12 @@
  * struct cam_vfe_bus_ver2_reg_offset_comp_grp:
  *
  * @Brief:        Register offsets for Composite Group registers
+ * comp_mask:     Comp group register address
+ * addr_sync_mask:Address sync group register address
  */
 struct cam_vfe_bus_ver2_reg_offset_comp_grp {
 	uint32_t                            comp_mask;
+	uint32_t                            addr_sync_mask;
 };
 
 /*
@@ -157,11 +161,13 @@
  * @vfe_out_hw_info:  VFE output capability
  */
 struct cam_vfe_bus_ver2_hw_info {
-	struct cam_vfe_bus_ver2_reg_offset_common  common_reg;
+	struct cam_vfe_bus_ver2_reg_offset_common common_reg;
+	uint32_t num_client;
 	struct cam_vfe_bus_ver2_reg_offset_bus_client
 		bus_client_reg[CAM_VFE_BUS_VER2_MAX_CLIENTS];
 	struct cam_vfe_bus_ver2_reg_offset_comp_grp
 		comp_grp_reg[CAM_VFE_BUS_VER2_COMP_GRP_MAX];
+	uint32_t num_out;
 	struct cam_vfe_bus_ver2_vfe_out_hw_info
 		vfe_out_hw_info[CAM_VFE_BUS_VER2_VFE_OUT_MAX];
 };
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
index c089911..0763bca 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_bus/include/cam_vfe_bus.h
@@ -17,8 +17,8 @@
 #include "cam_hw_intf.h"
 #include "cam_isp_hw.h"
 
-#define CAM_VFE_BUS_VER_1_0 0x1000
-#define CAM_VFE_BUS_VER_2_0 0x2000
+#define CAM_VFE_BUS_VER_1_0             0x1000
+#define CAM_VFE_BUS_VER_2_0             0x2000
 
 enum cam_vfe_bus_plane_type {
 	PLANE_Y,
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile
index 0a94746..9a2c12c 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/Makefile
@@ -1,10 +1,13 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils/
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm/
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_core/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/hw_utils/irq_controller
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw
 
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_vfe_top.o cam_vfe_top_ver2.o cam_vfe_camif_ver2.o cam_vfe_rdi.o
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
index f255691..9848454 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.c
@@ -16,6 +16,7 @@
 #include "cam_isp_hw_mgr_intf.h"
 #include "cam_isp_hw.h"
 #include "cam_vfe_hw_intf.h"
+#include "cam_vfe_soc.h"
 #include "cam_vfe_top.h"
 #include "cam_vfe_top_ver2.h"
 #include "cam_vfe_camif_ver2.h"
@@ -28,8 +29,10 @@
 	struct cam_vfe_camif_ver2_reg               *camif_reg;
 	struct cam_vfe_top_ver2_reg_offset_common   *common_reg;
 	struct cam_vfe_camif_reg_data               *reg_data;
+	struct cam_hw_soc_info                      *soc_info;
 
 	enum cam_isp_hw_sync_mode          sync_mode;
+	uint32_t                           dsp_mode;
 	uint32_t                           pix_pattern;
 	uint32_t                           first_pixel;
 	uint32_t                           first_line;
@@ -66,11 +69,11 @@
 {
 	uint32_t                          size = 0;
 	uint32_t                          reg_val_pair[2];
-	struct cam_isp_hw_get_cdm_args   *cdm_args = cmd_args;
+	struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args;
 	struct cam_cdm_utils_ops         *cdm_util_ops = NULL;
 	struct cam_vfe_mux_camif_data    *rsrc_data = NULL;
 
-	if (arg_size != sizeof(struct cam_isp_hw_get_cdm_args)) {
+	if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) {
 		CAM_ERR(CAM_ISP, "Invalid cmd size");
 		return -EINVAL;
 	}
@@ -89,9 +92,9 @@
 
 	size = cdm_util_ops->cdm_required_size_reg_random(1);
 	/* since cdm returns dwords, we need to convert it into bytes */
-	if ((size * 4) > cdm_args->size) {
+	if ((size * 4) > cdm_args->cmd.size) {
 		CAM_ERR(CAM_ISP, "buf size:%d is not sufficient, expected: %d",
-			cdm_args->size, size);
+			cdm_args->cmd.size, size);
 		return -EINVAL;
 	}
 
@@ -101,10 +104,10 @@
 	CAM_DBG(CAM_ISP, "CAMIF reg_update_cmd %x offset %x",
 		reg_val_pair[1], reg_val_pair[0]);
 
-	cdm_util_ops->cdm_write_regrandom(cdm_args->cmd_buf_addr,
+	cdm_util_ops->cdm_write_regrandom(cdm_args->cmd.cmd_buf_addr,
 		1, reg_val_pair);
 
-	cdm_args->used_bytes = size * 4;
+	cdm_args->cmd.used_bytes = size * 4;
 
 	return 0;
 }
@@ -113,8 +116,8 @@
 	struct cam_isp_resource_node  *camif_res,
 	void                          *acquire_param)
 {
-	struct cam_vfe_mux_camif_data      *camif_data;
-	struct cam_vfe_acquire_args        *acquire_data;
+	struct cam_vfe_mux_camif_data    *camif_data;
+	struct cam_vfe_acquire_args      *acquire_data;
 
 	int rc = 0;
 
@@ -128,6 +131,7 @@
 
 	camif_data->sync_mode   = acquire_data->vfe_in.sync_mode;
 	camif_data->pix_pattern = acquire_data->vfe_in.in_port->test_pattern;
+	camif_data->dsp_mode    = acquire_data->vfe_in.in_port->dsp_mode;
 	camif_data->first_pixel = acquire_data->vfe_in.in_port->left_start;
 	camif_data->last_pixel  = acquire_data->vfe_in.in_port->left_stop;
 	camif_data->first_line  = acquire_data->vfe_in.in_port->line_start;
@@ -136,6 +140,61 @@
 	return rc;
 }
 
+static int cam_vfe_camif_resource_init(
+	struct cam_isp_resource_node        *camif_res,
+	void *init_args, uint32_t arg_size)
+{
+	struct cam_vfe_mux_camif_data    *camif_data;
+	struct cam_hw_soc_info           *soc_info;
+	int rc = 0;
+
+	if (!camif_res) {
+		CAM_ERR(CAM_ISP, "Error Invalid input arguments");
+		return -EINVAL;
+	}
+
+	camif_data   = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+
+	soc_info = camif_data->soc_info;
+
+	if ((camif_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) &&
+		(camif_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) {
+		rc = cam_vfe_soc_enable_clk(soc_info, CAM_VFE_DSP_CLK_NAME);
+		if (rc)
+			CAM_ERR(CAM_ISP, "failed to enable dsp clk");
+	}
+
+	return rc;
+}
+
+static int cam_vfe_camif_resource_deinit(
+	struct cam_isp_resource_node        *camif_res,
+	void *init_args, uint32_t arg_size)
+{
+	struct cam_vfe_mux_camif_data    *camif_data;
+	struct cam_hw_soc_info           *soc_info;
+	int rc = 0;
+
+	if (!camif_res) {
+		CAM_ERR(CAM_ISP, "Error Invalid input arguments");
+		return -EINVAL;
+	}
+
+	camif_data   = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
+
+	soc_info = camif_data->soc_info;
+
+	if ((camif_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) &&
+		(camif_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) {
+		rc = cam_vfe_soc_disable_clk(soc_info, CAM_VFE_DSP_CLK_NAME);
+		if (rc)
+			CAM_ERR(CAM_ISP, "failed to disable dsp clk");
+	}
+
+	return rc;
+
+}
+
 static int cam_vfe_camif_resource_start(
 	struct cam_isp_resource_node        *camif_res)
 {
@@ -158,8 +217,23 @@
 	/*config vfe core*/
 	val = (rsrc_data->pix_pattern <<
 			rsrc_data->reg_data->pixel_pattern_shift);
+	if (rsrc_data->sync_mode == CAM_ISP_HW_SYNC_SLAVE)
+		val |= (1 << rsrc_data->reg_data->extern_reg_update_shift);
+
+	if ((rsrc_data->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) &&
+		(rsrc_data->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) {
+		/* DSP mode reg val is CAM_ISP_DSP_MODE - 1 */
+		val |= (((rsrc_data->dsp_mode - 1) &
+			rsrc_data->reg_data->dsp_mode_mask) <<
+			rsrc_data->reg_data->dsp_mode_shift);
+		val |= (0x1 << rsrc_data->reg_data->dsp_en_shift);
+	}
+
 	cam_io_w_mb(val, rsrc_data->mem_base + rsrc_data->common_reg->core_cfg);
 
+	CAM_DBG(CAM_ISP, "hw id:%d core_cfg val:%d", camif_res->hw_intf->hw_idx,
+		val);
+
 	cam_io_w_mb(0x00400040, rsrc_data->mem_base +
 		rsrc_data->camif_reg->camif_config);
 	cam_io_w_mb(0x1, rsrc_data->mem_base +
@@ -188,6 +262,7 @@
 	struct cam_vfe_mux_camif_data       *camif_priv;
 	struct cam_vfe_camif_ver2_reg       *camif_reg;
 	int rc = 0;
+	uint32_t val = 0;
 
 	if (!camif_res) {
 		CAM_ERR(CAM_ISP, "Error! Invalid input arguments");
@@ -201,6 +276,15 @@
 	camif_priv = (struct cam_vfe_mux_camif_data *)camif_res->res_priv;
 	camif_reg = camif_priv->camif_reg;
 
+	if ((camif_priv->dsp_mode >= CAM_ISP_DSP_MODE_ONE_WAY) &&
+		(camif_priv->dsp_mode <= CAM_ISP_DSP_MODE_ROUND)) {
+		val = cam_io_r_mb(camif_priv->mem_base +
+				camif_priv->common_reg->core_cfg);
+		val &= (~(1 << camif_priv->reg_data->dsp_en_shift));
+		cam_io_w_mb(val, camif_priv->mem_base +
+			camif_priv->common_reg->core_cfg);
+	}
+
 	if (camif_res->res_state == CAM_ISP_RESOURCE_STATE_STREAMING)
 		camif_res->res_state = CAM_ISP_RESOURCE_STATE_RESERVED;
 
@@ -218,7 +302,7 @@
 	}
 
 	switch (cmd_type) {
-	case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+	case CAM_ISP_HW_CMD_GET_REG_UPDATE:
 		rc = cam_vfe_camif_get_reg_update(rsrc_node, cmd_args,
 			arg_size);
 		break;
@@ -245,6 +329,7 @@
 	struct cam_vfe_mux_camif_data        *camif_priv;
 	struct cam_vfe_top_irq_evt_payload   *payload;
 	uint32_t                              irq_status0;
+	uint32_t                              irq_status1;
 
 	if (!handler_priv || !evt_payload_priv)
 		return ret;
@@ -253,6 +338,7 @@
 	camif_priv = camif_node->res_priv;
 	payload = evt_payload_priv;
 	irq_status0 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS0];
+	irq_status1 = payload->irq_reg_val[CAM_IFE_IRQ_CAMIF_REG_STATUS1];
 
 	CAM_DBG(CAM_ISP, "event ID:%d", payload->evt_id);
 	CAM_DBG(CAM_ISP, "irq_status_0 = %x", irq_status0);
@@ -283,6 +369,15 @@
 			ret = CAM_VFE_IRQ_STATUS_SUCCESS;
 		}
 		break;
+	case CAM_ISP_HW_EVENT_ERROR:
+		if (irq_status1 & camif_priv->reg_data->error_irq_mask1) {
+			CAM_DBG(CAM_ISP, "Received ERROR\n");
+			ret = CAM_ISP_HW_ERROR_OVERFLOW;
+			cam_vfe_put_evt_payload(payload->core_info, &payload);
+		} else {
+			ret = CAM_ISP_HW_ERROR_NONE;
+		}
+		break;
 	default:
 		break;
 	}
@@ -309,14 +404,17 @@
 
 	camif_node->res_priv = camif_priv;
 
-	camif_priv->mem_base   = soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base;
-	camif_priv->camif_reg  = camif_info->camif_reg;
-	camif_priv->common_reg = camif_info->common_reg;
-	camif_priv->reg_data   = camif_info->reg_data;
-	camif_priv->hw_intf    = hw_intf;
+	camif_priv->mem_base    = soc_info->reg_map[VFE_CORE_BASE_IDX].mem_base;
+	camif_priv->camif_reg   = camif_info->camif_reg;
+	camif_priv->common_reg  = camif_info->common_reg;
+	camif_priv->reg_data    = camif_info->reg_data;
+	camif_priv->hw_intf     = hw_intf;
+	camif_priv->soc_info    = soc_info;
 
-	camif_node->start = cam_vfe_camif_resource_start;
-	camif_node->stop  = cam_vfe_camif_resource_stop;
+	camif_node->init    = cam_vfe_camif_resource_init;
+	camif_node->deinit  = cam_vfe_camif_resource_deinit;
+	camif_node->start   = cam_vfe_camif_resource_start;
+	camif_node->stop    = cam_vfe_camif_resource_stop;
 	camif_node->process_cmd = cam_vfe_camif_process_cmd;
 	camif_node->top_half_handler = cam_vfe_camif_handle_irq_top_half;
 	camif_node->bottom_half_handler = cam_vfe_camif_handle_irq_bottom_half;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
index 847b7d5..4a73bd7 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_camif_ver2.h
@@ -50,12 +50,19 @@
 	uint32_t     pixel_pattern_shift;
 	uint32_t     pixel_pattern_mask;
 
+	uint32_t     dsp_mode_shift;
+	uint32_t     dsp_mode_mask;
+	uint32_t     dsp_en_shift;
+	uint32_t     dsp_en_mask;
+
 	uint32_t     reg_update_cmd_data;
 	uint32_t     epoch_line_cfg;
 	uint32_t     sof_irq_mask;
 	uint32_t     epoch0_irq_mask;
 	uint32_t     reg_update_irq_mask;
 	uint32_t     eof_irq_mask;
+	uint32_t     error_irq_mask0;
+	uint32_t     error_irq_mask1;
 };
 
 struct cam_vfe_camif_ver2_hw_info {
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
index 461e3c3..28e99f2 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_rdi.c
@@ -35,11 +35,11 @@
 {
 	uint32_t                          size = 0;
 	uint32_t                          reg_val_pair[2];
-	struct cam_isp_hw_get_cdm_args   *cdm_args = cmd_args;
+	struct cam_isp_hw_get_cmd_update *cdm_args = cmd_args;
 	struct cam_cdm_utils_ops         *cdm_util_ops = NULL;
 	struct cam_vfe_mux_rdi_data      *rsrc_data = NULL;
 
-	if (arg_size != sizeof(struct cam_isp_hw_get_cdm_args)) {
+	if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) {
 		CAM_ERR(CAM_ISP, "Error - Invalid cmd size");
 		return -EINVAL;
 	}
@@ -57,22 +57,22 @@
 
 	size = cdm_util_ops->cdm_required_size_reg_random(1);
 	/* since cdm returns dwords, we need to convert it into bytes */
-	if ((size * 4) > cdm_args->size) {
+	if ((size * 4) > cdm_args->cmd.size) {
 		CAM_ERR(CAM_ISP,
 			"Error - buf size:%d is not sufficient, expected: %d",
-			cdm_args->size, size * 4);
+			cdm_args->cmd.size, size * 4);
 		return -EINVAL;
 	}
 
 	rsrc_data = rdi_res->res_priv;
 	reg_val_pair[0] = rsrc_data->rdi_reg->reg_update_cmd;
 	reg_val_pair[1] = rsrc_data->reg_data->reg_update_cmd_data;
-	CAM_DBG(CAM_ISP, "Error - RDI%d reg_update_cmd %x",
+	CAM_DBG(CAM_ISP, "RDI%d reg_update_cmd %x",
 		rdi_res->res_id - CAM_ISP_HW_VFE_IN_RDI0, reg_val_pair[1]);
 
-	cdm_util_ops->cdm_write_regrandom(cdm_args->cmd_buf_addr,
+	cdm_util_ops->cdm_write_regrandom(cdm_args->cmd.cmd_buf_addr,
 		1, reg_val_pair);
-	cdm_args->used_bytes = size * 4;
+	cdm_args->cmd.used_bytes = size * 4;
 
 	return 0;
 }
@@ -158,7 +158,7 @@
 	}
 
 	switch (cmd_type) {
-	case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+	case CAM_ISP_HW_CMD_GET_REG_UPDATE:
 		rc = cam_vfe_rdi_get_reg_update(rsrc_node, cmd_args,
 			arg_size);
 		break;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
index f87953d..1b8cdf3 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/cam_vfe_top_ver2.c
@@ -17,6 +17,11 @@
 #include "cam_vfe_top.h"
 #include "cam_vfe_top_ver2.h"
 #include "cam_debug_util.h"
+#include "cam_cpas_api.h"
+#include "cam_vfe_soc.h"
+
+#define CAM_VFE_HW_RESET_HW_AND_REG_VAL   0x00003F9F
+#define CAM_VFE_HW_RESET_HW_VAL           0x00003F87
 
 struct cam_vfe_top_ver2_common_data {
 	struct cam_hw_soc_info                     *soc_info;
@@ -26,8 +31,11 @@
 
 struct cam_vfe_top_ver2_priv {
 	struct cam_vfe_top_ver2_common_data common_data;
-	struct cam_vfe_camif               *camif;
 	struct cam_isp_resource_node        mux_rsrc[CAM_VFE_TOP_VER2_MUX_MAX];
+	unsigned long                       hw_clk_rate;
+	struct cam_axi_vote                 hw_axi_vote;
+	struct cam_axi_vote             req_axi_vote[CAM_VFE_TOP_VER2_MUX_MAX];
+	unsigned long                   req_clk_rate[CAM_VFE_TOP_VER2_MUX_MAX];
 };
 
 static int cam_vfe_top_mux_get_base(struct cam_vfe_top_ver2_priv *top_priv,
@@ -35,10 +43,10 @@
 {
 	uint32_t                          size = 0;
 	uint32_t                          mem_base = 0;
-	struct cam_isp_hw_get_cdm_args   *cdm_args  = cmd_args;
+	struct cam_isp_hw_get_cmd_update *cdm_args  = cmd_args;
 	struct cam_cdm_utils_ops         *cdm_util_ops = NULL;
 
-	if (arg_size != sizeof(struct cam_isp_hw_get_cdm_args)) {
+	if (arg_size != sizeof(struct cam_isp_hw_get_cmd_update)) {
 		CAM_ERR(CAM_ISP, "Error! Invalid cmd size");
 		return -EINVAL;
 	}
@@ -59,9 +67,9 @@
 
 	size = cdm_util_ops->cdm_required_size_changebase();
 	/* since cdm returns dwords, we need to convert it into bytes */
-	if ((size * 4) > cdm_args->size) {
+	if ((size * 4) > cdm_args->cmd.size) {
 		CAM_ERR(CAM_ISP, "buf size:%d is not sufficient, expected: %d",
-			cdm_args->size, size);
+			cdm_args->cmd.size, size);
 		return -EINVAL;
 	}
 
@@ -70,21 +78,190 @@
 	CAM_DBG(CAM_ISP, "core %d mem_base 0x%x",
 		top_priv->common_data.soc_info->index, mem_base);
 
-	cdm_util_ops->cdm_write_changebase(cdm_args->cmd_buf_addr, mem_base);
-	cdm_args->used_bytes = (size * 4);
+	cdm_util_ops->cdm_write_changebase(
+	cdm_args->cmd.cmd_buf_addr, mem_base);
+	cdm_args->cmd.used_bytes = (size * 4);
 
 	return 0;
 }
 
+static int cam_vfe_top_set_hw_clk_rate(
+	struct cam_vfe_top_ver2_priv *top_priv)
+{
+	struct cam_hw_soc_info        *soc_info = NULL;
+	int                            i, rc = 0;
+	unsigned long                  max_clk_rate = 0;
+
+	soc_info = top_priv->common_data.soc_info;
+
+	for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+		if (top_priv->req_clk_rate[i] > max_clk_rate)
+			max_clk_rate = top_priv->req_clk_rate[i];
+	}
+	if (max_clk_rate == top_priv->hw_clk_rate)
+		return 0;
+
+	CAM_DBG(CAM_ISP, "VFE: Clock name=%s idx=%d clk=%lld",
+		soc_info->clk_name[soc_info->src_clk_idx],
+		soc_info->src_clk_idx, max_clk_rate);
+
+	rc = cam_soc_util_set_clk_rate(
+		soc_info->clk[soc_info->src_clk_idx],
+		soc_info->clk_name[soc_info->src_clk_idx],
+		max_clk_rate);
+
+	if (!rc)
+		top_priv->hw_clk_rate = max_clk_rate;
+	else
+		CAM_ERR(CAM_ISP, "Set Clock rate failed, rc=%d", rc);
+
+	return rc;
+}
+
+static int cam_vfe_top_set_axi_bw_vote(
+	struct cam_vfe_top_ver2_priv *top_priv)
+{
+	struct cam_axi_vote sum = {0, 0};
+	int i, rc = 0;
+	struct cam_hw_soc_info   *soc_info =
+		top_priv->common_data.soc_info;
+	struct cam_vfe_soc_private *soc_private =
+		soc_info->soc_private;
+
+	if (!soc_private) {
+		CAM_ERR(CAM_ISP, "Error soc_private NULL");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+		sum.uncompressed_bw +=
+			top_priv->req_axi_vote[i].uncompressed_bw;
+		sum.compressed_bw +=
+			top_priv->req_axi_vote[i].compressed_bw;
+	}
+
+	CAM_DBG(CAM_ISP, "BW Vote: u=%lld c=%lld",
+		sum.uncompressed_bw,
+		sum.compressed_bw);
+
+	if ((top_priv->hw_axi_vote.uncompressed_bw ==
+		sum.uncompressed_bw) &&
+		(top_priv->hw_axi_vote.compressed_bw ==
+		sum.compressed_bw))
+		return 0;
+
+	rc = cam_cpas_update_axi_vote(
+			soc_private->cpas_handle,
+			&sum);
+	if (!rc) {
+		top_priv->hw_axi_vote.uncompressed_bw = sum.uncompressed_bw;
+		top_priv->hw_axi_vote.compressed_bw = sum.compressed_bw;
+	} else
+		CAM_ERR(CAM_ISP, "BW request failed, rc=%d", rc);
+
+	return rc;
+}
+
+static int cam_vfe_top_clock_update(
+	struct cam_vfe_top_ver2_priv *top_priv,
+	void *cmd_args, uint32_t arg_size)
+{
+	struct cam_vfe_clock_update_args     *clk_update = NULL;
+	struct cam_isp_resource_node         *res = NULL;
+	struct cam_hw_info                   *hw_info = NULL;
+	int                                   i, rc = 0;
+
+	clk_update =
+		(struct cam_vfe_clock_update_args *)cmd_args;
+	res = clk_update->node_res;
+
+	if (!res || !res->hw_intf->hw_priv) {
+		CAM_ERR(CAM_ISP, "Invalid input res %pK", res);
+		return -EINVAL;
+	}
+
+	hw_info = res->hw_intf->hw_priv;
+
+	if (res->res_type != CAM_ISP_RESOURCE_VFE_IN ||
+		res->res_id >= CAM_ISP_HW_VFE_IN_MAX) {
+		CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d",
+			res->hw_intf->hw_idx, res->res_type,
+			res->res_id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+		if (top_priv->mux_rsrc[i].res_id == res->res_id) {
+			top_priv->req_clk_rate[i] = clk_update->clk_rate;
+			break;
+		}
+	}
+
+	if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
+		CAM_DBG(CAM_ISP, "VFE:%d Not ready to set clocks yet :%d",
+			res->hw_intf->hw_idx,
+			hw_info->hw_state);
+	} else
+		rc = cam_vfe_top_set_hw_clk_rate(top_priv);
+
+	return rc;
+}
+
+static int cam_vfe_top_bw_update(
+	struct cam_vfe_top_ver2_priv *top_priv,
+	void *cmd_args, uint32_t arg_size)
+{
+	struct cam_vfe_bw_update_args        *bw_update = NULL;
+	struct cam_isp_resource_node         *res = NULL;
+	struct cam_hw_info                   *hw_info = NULL;
+	int                                   rc = 0;
+	int                                   i;
+
+	bw_update = (struct cam_vfe_bw_update_args *)cmd_args;
+	res = bw_update->node_res;
+
+	if (!res || !res->hw_intf->hw_priv)
+		return -EINVAL;
+
+	hw_info = res->hw_intf->hw_priv;
+
+	if (res->res_type != CAM_ISP_RESOURCE_VFE_IN ||
+		res->res_id >= CAM_ISP_HW_VFE_IN_MAX) {
+		CAM_ERR(CAM_ISP, "VFE:%d Invalid res_type:%d res id%d",
+			res->hw_intf->hw_idx, res->res_type,
+			res->res_id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+		if (top_priv->mux_rsrc[i].res_id == res->res_id) {
+			top_priv->req_axi_vote[i].uncompressed_bw =
+				bw_update->camnoc_bw_bytes;
+			top_priv->req_axi_vote[i].compressed_bw =
+				bw_update->external_bw_bytes;
+			break;
+		}
+	}
+
+	if (hw_info->hw_state != CAM_HW_STATE_POWER_UP) {
+		CAM_DBG(CAM_ISP, "VFE:%d Not ready to set BW yet :%d",
+			res->hw_intf->hw_idx,
+			hw_info->hw_state);
+	} else
+		rc = cam_vfe_top_set_axi_bw_vote(top_priv);
+
+	return rc;
+}
+
 static int cam_vfe_top_mux_get_reg_update(
 	struct cam_vfe_top_ver2_priv *top_priv,
 	void *cmd_args, uint32_t arg_size)
 {
-	struct cam_isp_hw_get_cdm_args   *cdm_args = cmd_args;
+	struct cam_isp_hw_get_cmd_update  *cmd_update = cmd_args;
 
-	if (cdm_args->res->process_cmd)
-		return cdm_args->res->process_cmd(cdm_args->res,
-			CAM_VFE_HW_CMD_GET_REG_UPDATE, cmd_args, arg_size);
+	if (cmd_update->res->process_cmd)
+		return cmd_update->res->process_cmd(cmd_update->res,
+			CAM_ISP_HW_CMD_GET_REG_UPDATE, cmd_args, arg_size);
 
 	return -EINVAL;
 }
@@ -107,12 +284,24 @@
 	struct cam_vfe_top_ver2_priv   *top_priv = device_priv;
 	struct cam_hw_soc_info         *soc_info = NULL;
 	struct cam_vfe_top_ver2_reg_offset_common *reg_common = NULL;
+	uint32_t *reset_reg_args = reset_core_args;
+	uint32_t reset_reg_val;
 
-	if (!top_priv) {
+	if (!top_priv || !reset_reg_args) {
 		CAM_ERR(CAM_ISP, "Invalid arguments");
 		return -EINVAL;
 	}
 
+	switch (*reset_reg_args) {
+	case CAM_VFE_HW_RESET_HW_AND_REG:
+		reset_reg_val = CAM_VFE_HW_RESET_HW_AND_REG_VAL;
+		break;
+	default:
+		reset_reg_val = CAM_VFE_HW_RESET_HW_VAL;
+		break;
+	}
+
+	CAM_DBG(CAM_ISP, "reset reg value: %x", reset_reg_val);
 	soc_info = top_priv->common_data.soc_info;
 	reg_common = top_priv->common_data.common_reg;
 
@@ -121,7 +310,7 @@
 		CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) + 0x5C);
 
 	/* Reset HW */
-	cam_io_w_mb(0x00003F9F,
+	cam_io_w_mb(reset_reg_val,
 		CAM_SOC_GET_REG_MAP_START(soc_info, VFE_CORE_BASE_IDX) +
 		reg_common->global_reset_cmd);
 
@@ -214,9 +403,21 @@
 		return -EINVAL;
 	}
 
-	top_priv = (struct cam_vfe_top_ver2_priv   *)device_priv;
+	top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
 	mux_res = (struct cam_isp_resource_node *)start_args;
 
+	rc = cam_vfe_top_set_hw_clk_rate(top_priv);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "set_hw_clk_rate failed, rc=%d", rc);
+		return rc;
+	}
+
+	rc = cam_vfe_top_set_axi_bw_vote(top_priv);
+	if (rc) {
+		CAM_ERR(CAM_ISP, "set_hw_clk_rate failed, rc=%d", rc);
+		return rc;
+	}
+
 	if (mux_res->start) {
 		rc = mux_res->start(mux_res);
 	} else {
@@ -232,7 +433,7 @@
 {
 	struct cam_vfe_top_ver2_priv            *top_priv;
 	struct cam_isp_resource_node            *mux_res;
-	int rc = 0;
+	int i, rc = 0;
 
 	if (!device_priv || !stop_args) {
 		CAM_ERR(CAM_ISP, "Error! Invalid input arguments");
@@ -251,8 +452,16 @@
 		rc = -EINVAL;
 	}
 
-	return rc;
+	if (!rc) {
+		for (i = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
+			if (top_priv->mux_rsrc[i].res_id == mux_res->res_id)
+				top_priv->req_clk_rate[i] = 0;
+			top_priv->req_axi_vote[i].compressed_bw = 0;
+			top_priv->req_axi_vote[i].uncompressed_bw = 0;
+		}
+	}
 
+	return rc;
 }
 
 int cam_vfe_top_read(void *device_priv,
@@ -280,13 +489,21 @@
 	top_priv = (struct cam_vfe_top_ver2_priv *)device_priv;
 
 	switch (cmd_type) {
-	case CAM_VFE_HW_CMD_GET_CHANGE_BASE:
+	case CAM_ISP_HW_CMD_GET_CHANGE_BASE:
 		rc = cam_vfe_top_mux_get_base(top_priv, cmd_args, arg_size);
 		break;
-	case CAM_VFE_HW_CMD_GET_REG_UPDATE:
+	case CAM_ISP_HW_CMD_GET_REG_UPDATE:
 		rc = cam_vfe_top_mux_get_reg_update(top_priv, cmd_args,
 			arg_size);
 		break;
+	case CAM_ISP_HW_CMD_CLOCK_UPDATE:
+		rc = cam_vfe_top_clock_update(top_priv, cmd_args,
+			arg_size);
+		break;
+	case CAM_ISP_HW_CMD_BW_UPDATE:
+		rc = cam_vfe_top_bw_update(top_priv, cmd_args,
+			arg_size);
+		break;
 	default:
 		rc = -EINVAL;
 		CAM_ERR(CAM_ISP, "Error! Invalid cmd:%d", cmd_type);
@@ -322,12 +539,19 @@
 		goto free_vfe_top;
 	}
 	vfe_top->top_priv = top_priv;
+	top_priv->hw_clk_rate = 0;
+	top_priv->hw_axi_vote.compressed_bw = 0;
+	top_priv->hw_axi_vote.uncompressed_bw = 0;
 
 	for (i = 0, j = 0; i < CAM_VFE_TOP_VER2_MUX_MAX; i++) {
 		top_priv->mux_rsrc[i].res_type = CAM_ISP_RESOURCE_VFE_IN;
 		top_priv->mux_rsrc[i].hw_intf = hw_intf;
 		top_priv->mux_rsrc[i].res_state =
 			CAM_ISP_RESOURCE_STATE_AVAILABLE;
+		top_priv->req_clk_rate[i] = 0;
+		top_priv->req_axi_vote[i].compressed_bw = 0;
+		top_priv->req_axi_vote[i].uncompressed_bw = 0;
+
 		if (ver2_hw_info->mux_type[i] == CAM_VFE_CAMIF_VER_2_0) {
 			top_priv->mux_rsrc[i].res_id =
 				CAM_ISP_HW_VFE_IN_CAMIF;
diff --git a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h
index dbb211f..81e3b48 100644
--- a/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h
+++ b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/isp_hw/vfe_hw/vfe_top/include/cam_vfe_top.h
@@ -29,21 +29,6 @@
 	struct cam_hw_ops       hw_ops;
 };
 
-struct cam_vfe_camif {
-	void               *camif_priv;
-	int (*start_resource)(void *priv,
-		struct cam_isp_resource_node *camif_res);
-	int (*stop_resource)(void *priv,
-		struct cam_isp_resource_node *camif_res);
-	int (*acquire_resource)(void *priv,
-		struct cam_isp_resource_node *camif_res,
-		void *acquire_param);
-	int (*release_resource)(void *priv,
-		struct cam_isp_resource_node *camif_res);
-	int (*process_cmd)(void *priv, uint32_t cmd_type, void *cmd_args,
-				uint32_t arg_size);
-};
-
 int cam_vfe_top_init(uint32_t          top_version,
 	struct cam_hw_soc_info        *soc_info,
 	struct cam_hw_intf            *hw_intf,
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
index a299179..6fcd7f6 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_context.c
@@ -21,6 +21,8 @@
 #include "cam_context_utils.h"
 #include "cam_debug_util.h"
 
+static const char jpeg_dev_name[] = "jpeg";
+
 static int __cam_jpeg_ctx_acquire_dev_in_available(struct cam_context *ctx,
 	struct cam_acquire_dev_cmd *cmd)
 {
@@ -109,8 +111,8 @@
 	for (i = 0; i < CAM_CTX_REQ_MAX; i++)
 		ctx->req_base[i].req_priv = ctx;
 
-	rc = cam_context_init(ctx_base, NULL, hw_intf, ctx->req_base,
-		CAM_CTX_REQ_MAX);
+	rc = cam_context_init(ctx_base, jpeg_dev_name, NULL, hw_intf,
+		ctx->req_base, CAM_CTX_REQ_MAX);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "Camera Context Base init failed");
 		goto err;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
index fb68ddb..a400388 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/cam_jpeg_dev.c
@@ -34,6 +34,25 @@
 	{ }
 };
 
+static int cam_jpeg_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_node *node = v4l2_get_subdevdata(sd);
+
+	if (!node) {
+		CAM_ERR(CAM_JPEG, "Node ptr is NULL");
+		return -EINVAL;
+	}
+
+	cam_node_shutdown(node);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_jpeg_subdev_internal_ops = {
+	.close = cam_jpeg_subdev_close,
+};
+
 static int cam_jpeg_dev_remove(struct platform_device *pdev)
 {
 	int rc;
@@ -60,6 +79,7 @@
 	struct cam_hw_mgr_intf hw_mgr_intf;
 	struct cam_node *node;
 
+	g_jpeg_dev.sd.internal_ops = &cam_jpeg_subdev_internal_ops;
 	rc = cam_subdev_probe(&g_jpeg_dev.sd, pdev, CAM_JPEG_DEV_NAME,
 		CAM_JPEG_DEVICE_TYPE);
 	if (rc) {
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
index b06b5c4..df95100 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/cam_jpeg_hw_mgr.c
@@ -28,8 +28,6 @@
 #include "cam_hw_mgr_intf.h"
 #include "cam_jpeg_hw_mgr_intf.h"
 #include "cam_jpeg_hw_mgr.h"
-#include "cam_enc_hw_intf.h"
-#include "cam_dma_hw_intf.h"
 #include "cam_smmu_api.h"
 #include "cam_mem_mgr.h"
 #include "cam_req_mgr_workq.h"
@@ -38,6 +36,9 @@
 #include "cam_debug_util.h"
 
 #define CAM_JPEG_HW_ENTRIES_MAX  20
+#define CAM_JPEG_CHBASE          0
+#define CAM_JPEG_CFG             1
+#define CAM_JPEG_PARAM           2
 
 static struct cam_jpeg_hw_mgr g_jpeg_hw_mgr;
 
@@ -88,13 +89,20 @@
 	}
 	rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
 		hw_mgr->devices[dev_type][0]->hw_priv,
-		CAM_JPEG_ENC_CMD_SET_IRQ_CB,
+		CAM_JPEG_CMD_SET_IRQ_CB,
 		&irq_cb, sizeof(irq_cb));
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "CMD_SET_IRQ_CB failed %d", rc);
 		return rc;
 	}
 
+	if (hw_mgr->devices[dev_type][0]->hw_ops.deinit) {
+		rc = hw_mgr->devices[dev_type][0]->hw_ops.deinit(
+			hw_mgr->devices[dev_type][0]->hw_priv, NULL, 0);
+		if (rc)
+			CAM_ERR(CAM_JPEG, "Failed to Deinit %d HW", dev_type);
+	}
+
 	mutex_lock(&g_jpeg_hw_mgr.hw_mgr_mutex);
 	hw_mgr->device_in_use[dev_type][0] = false;
 	p_cfg_req = hw_mgr->dev_hw_cfg_args[dev_type][0];
@@ -126,7 +134,8 @@
 	}
 
 	rc = cam_mem_get_cpu_buf(
-		p_cfg_req->hw_cfg_args.hw_update_entries[1].handle,
+		p_cfg_req->hw_cfg_args.
+		hw_update_entries[CAM_JPEG_PARAM].handle,
 		(uint64_t *)&kaddr, &cmd_buf_len);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "unable to get info for cmd buf: %x %d",
@@ -137,8 +146,8 @@
 	cmd_buf_kaddr = (uint32_t *)kaddr;
 
 	cmd_buf_kaddr =
-		(cmd_buf_kaddr +
-		(p_cfg_req->hw_cfg_args.hw_update_entries[1].offset/4));
+		(cmd_buf_kaddr + (p_cfg_req->hw_cfg_args.
+		hw_update_entries[CAM_JPEG_PARAM].offset / sizeof(uint32_t)));
 
 	p_params = (struct cam_jpeg_config_inout_param_info *)cmd_buf_kaddr;
 
@@ -214,26 +223,79 @@
 
 
 static int cam_jpeg_mgr_release_ctx(
-	struct cam_jpeg_hw_mgr *hw_mgr, int ctx_id)
+	struct cam_jpeg_hw_mgr *hw_mgr, struct cam_jpeg_hw_ctx_data *ctx_data)
 {
-	if (ctx_id >= CAM_JPEG_CTX_MAX) {
-		CAM_ERR(CAM_JPEG, "ctx_id is wrong: %d", ctx_id);
+	if (!ctx_data) {
+		CAM_ERR(CAM_JPEG, "invalid ctx_data %pK", ctx_data);
 		return -EINVAL;
 	}
 
-	mutex_lock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
-	if (!hw_mgr->ctx_data[ctx_id].in_use) {
-		CAM_ERR(CAM_JPEG, "ctx is already in use: %d", ctx_id);
-		mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+	mutex_lock(&ctx_data->ctx_mutex);
+	if (!ctx_data->in_use) {
+		CAM_ERR(CAM_JPEG, "ctx is already un-used: %pK", ctx_data);
+		mutex_unlock(&ctx_data->ctx_mutex);
 		return -EINVAL;
 	}
 
-	hw_mgr->ctx_data[ctx_id].in_use = 0;
-	mutex_unlock(&hw_mgr->ctx_data[ctx_id].ctx_mutex);
+	ctx_data->in_use = false;
+	mutex_unlock(&ctx_data->ctx_mutex);
 
 	return 0;
 }
 
+static int cam_jpeg_insert_cdm_change_base(
+	struct cam_hw_config_args *config_args,
+	struct cam_jpeg_hw_ctx_data *ctx_data,
+	struct cam_jpeg_hw_mgr *hw_mgr)
+{
+	int rc;
+	uint32_t dev_type;
+	struct cam_cdm_bl_request *cdm_cmd;
+	uint32_t size;
+	uint32_t mem_cam_base;
+	uint64_t iova_addr;
+	uint32_t *ch_base_iova_addr;
+	size_t ch_base_len;
+
+	rc = cam_mem_get_cpu_buf(config_args->
+		hw_update_entries[CAM_JPEG_CHBASE].handle,
+		&iova_addr, &ch_base_len);
+	if (rc) {
+		CAM_ERR(CAM_JPEG,
+			"unable to get src buf info for cmd buf: %d", rc);
+		return rc;
+	}
+	CAM_DBG(CAM_JPEG, "iova %pK len %zu offset %d",
+		(void *)iova_addr, ch_base_len,
+		config_args->hw_update_entries[CAM_JPEG_CHBASE].offset);
+	ch_base_iova_addr = (uint32_t *)iova_addr;
+	ch_base_iova_addr = (ch_base_iova_addr +
+		(config_args->hw_update_entries[CAM_JPEG_CHBASE].offset /
+		sizeof(uint32_t)));
+
+	dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
+	mem_cam_base = hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base;
+	size = hw_mgr->cdm_info[dev_type][0].cdm_ops->
+		cdm_required_size_changebase();
+	hw_mgr->cdm_info[dev_type][0].cdm_ops->
+		cdm_write_changebase(ch_base_iova_addr, mem_cam_base);
+
+	cdm_cmd = ctx_data->cdm_cmd;
+	cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].bl_addr.mem_handle =
+		config_args->hw_update_entries[CAM_JPEG_CHBASE].handle;
+	cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset =
+		config_args->hw_update_entries[CAM_JPEG_CHBASE].offset;
+	cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len = size * sizeof(uint32_t);
+	cdm_cmd->cmd_arrary_count++;
+
+	ch_base_iova_addr += size;
+	*ch_base_iova_addr = 0;
+	ch_base_iova_addr += size;
+	*ch_base_iova_addr = 0;
+
+	return rc;
+}
+
 static int cam_jpeg_mgr_process_cmd(void *priv, void *data)
 {
 	int rc;
@@ -249,11 +311,8 @@
 	uint32_t dev_type;
 	struct cam_jpeg_set_irq_cb irq_cb;
 	struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL;
-	uint32_t size = 0;
-	uint32_t mem_cam_base = 0;
 	struct cam_hw_done_event_data buf_data;
 
-	CAM_DBG(CAM_JPEG, "in cam_jpeg_mgr_process_cmd");
 	if (!hw_mgr || !task_data) {
 		CAM_ERR(CAM_JPEG, "Invalid arguments %pK %pK",
 			hw_mgr, task_data);
@@ -280,7 +339,8 @@
 		hw_mgr->dev_hw_cfg_args[p_cfg_req->dev_type][0] = p_cfg_req;
 		list_del_init(&p_cfg_req->list);
 	} else {
-		CAM_ERR(CAM_JPEG, "NOT dequeing, just return");
+		CAM_DBG(CAM_JPEG, "Not dequeing, just return");
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
 		rc = -EFAULT;
 		goto end;
 	}
@@ -289,13 +349,14 @@
 	config_args = (struct cam_hw_config_args *)&p_cfg_req->hw_cfg_args;
 	request_id = task_data->request_id;
 	if (request_id != (uint64_t)config_args->priv) {
-		CAM_WARN(CAM_JPEG, "not a recent req %d %d",
+		CAM_DBG(CAM_JPEG, "not a recent req %lld %lld",
 			request_id, (uint64_t)config_args->priv);
 	}
 
 	if (!config_args->num_hw_update_entries) {
 		CAM_ERR(CAM_JPEG, "No hw update enteries are available");
-		return -EINVAL;
+		rc = -EINVAL;
+		goto end_unusedev;
 	}
 
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
@@ -303,7 +364,8 @@
 	if (!ctx_data->in_use) {
 		CAM_ERR(CAM_JPEG, "ctx is not in use");
 		mutex_unlock(&hw_mgr->hw_mgr_mutex);
-		return -EINVAL;
+		rc = -EINVAL;
+		goto end_unusedev;
 	}
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
@@ -312,99 +374,99 @@
 	if (dev_type != p_cfg_req->dev_type)
 		CAM_WARN(CAM_JPEG, "dev types not same something wrong");
 
+	if (!hw_mgr->devices[dev_type][0]->hw_ops.init) {
+		CAM_ERR(CAM_JPEG, "hw op init null ");
+		rc = -EFAULT;
+		goto end;
+	}
+	rc = hw_mgr->devices[dev_type][0]->hw_ops.init(
+		hw_mgr->devices[dev_type][0]->hw_priv,
+		ctx_data,
+		sizeof(ctx_data));
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
+		goto end;
+	}
+
 	irq_cb.jpeg_hw_mgr_cb = cam_jpeg_hw_mgr_cb;
 	irq_cb.data = (void *)ctx_data;
 	irq_cb.b_set_cb = true;
 	if (!hw_mgr->devices[dev_type][0]->hw_ops.process_cmd) {
 		CAM_ERR(CAM_JPEG, "op process_cmd null ");
-		return -EINVAL;
+		rc = -EFAULT;
+		goto end_callcb;
 	}
 	rc = hw_mgr->devices[dev_type][0]->hw_ops.process_cmd(
 		hw_mgr->devices[dev_type][0]->hw_priv,
-		CAM_JPEG_ENC_CMD_SET_IRQ_CB,
+		CAM_JPEG_CMD_SET_IRQ_CB,
 		&irq_cb, sizeof(irq_cb));
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "SET_IRQ_CB failed %d", rc);
-		return -EINVAL;
+		goto end_callcb;
 	}
 
 	if (!hw_mgr->devices[dev_type][0]->hw_ops.reset) {
 		CAM_ERR(CAM_JPEG, "op reset null ");
-		return -EINVAL;
+		rc = -EFAULT;
+		goto end_callcb;
 	}
 	rc = hw_mgr->devices[dev_type][0]->hw_ops.reset(
 		hw_mgr->devices[dev_type][0]->hw_priv,
 		NULL, 0);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "jpeg hw reset failed %d", rc);
-		return -EINVAL;
+		goto end_callcb;
 	}
 
-	mem_cam_base = (uint64_t)hw_mgr->cdm_reg_map[dev_type][0]->
-		mem_cam_base;
-	size = hw_mgr->cdm_info[dev_type][0].cdm_ops->
-		cdm_required_size_changebase();
-	hw_mgr->cdm_info[dev_type][0].cdm_ops->
-		cdm_write_changebase(ctx_data->cmd_chbase_buf_addr,
-		(uint64_t)hw_mgr->cdm_reg_map[dev_type][0]->mem_cam_base);
-	ctx_data->cdm_cmd_chbase->cmd_arrary_count = 1;
-	ctx_data->cdm_cmd_chbase->type = CAM_CDM_BL_CMD_TYPE_KERNEL_IOVA;
-	ctx_data->cdm_cmd_chbase->flag = false;
-	ctx_data->cdm_cmd_chbase->userdata = NULL;
-	ctx_data->cdm_cmd_chbase->cookie = 0;
-	ctx_data->cdm_cmd_chbase->cmd[0].bl_addr.kernel_iova =
-		ctx_data->cmd_chbase_buf_addr;
-	ctx_data->cdm_cmd_chbase->cmd[0].offset = 0;
-	ctx_data->cdm_cmd_chbase->cmd[0].len = size;
-	rc = cam_cdm_submit_bls(hw_mgr->cdm_info[dev_type][0].cdm_handle,
-		ctx_data->cdm_cmd_chbase);
-	if (rc)
-		CAM_ERR(CAM_JPEG, "failed cdm cmd %d", rc);
+	cdm_cmd = ctx_data->cdm_cmd;
+	cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
+	cdm_cmd->flag = false;
+	cdm_cmd->userdata = NULL;
+	cdm_cmd->cookie = 0;
+	cdm_cmd->cmd_arrary_count = 0;
 
-	CAM_DBG(CAM_JPEG, "cfg e %pK num %d",
-		config_args->hw_update_entries,
-		config_args->num_hw_update_entries);
+	rc = cam_jpeg_insert_cdm_change_base(config_args,
+		ctx_data, hw_mgr);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "insert change base failed %d", rc);
+		goto end_callcb;
+	}
 
-	if (config_args->num_hw_update_entries > 0) {
-		cdm_cmd = ctx_data->cdm_cmd;
-		cdm_cmd->cmd_arrary_count =
-			config_args->num_hw_update_entries - 1;
-		cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
-		cdm_cmd->flag = false;
-		cdm_cmd->userdata = NULL;
-		cdm_cmd->cookie = 0;
+	CAM_DBG(CAM_JPEG, "num hw up %d", config_args->num_hw_update_entries);
+	for (i = CAM_JPEG_CFG; i < (config_args->num_hw_update_entries - 1);
+		i++) {
+		cmd = (config_args->hw_update_entries + i);
+		cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].
+			bl_addr.mem_handle = cmd->handle;
+		cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].offset =
+			cmd->offset;
+		cdm_cmd->cmd[cdm_cmd->cmd_arrary_count].len =
+			cmd->len;
+		CAM_DBG(CAM_JPEG, "i %d entry h %d o %d l %d",
+			i, cmd->handle, cmd->offset, cmd->len);
+		cdm_cmd->cmd_arrary_count++;
+	}
 
-		for (i = 0; i <= cdm_cmd->cmd_arrary_count; i++) {
-			cmd = (config_args->hw_update_entries + i);
-			cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle;
-			cdm_cmd->cmd[i].offset = cmd->offset;
-			cdm_cmd->cmd[i].len = cmd->len;
-		}
+	rc = cam_cdm_submit_bls(
+		hw_mgr->cdm_info[dev_type][0].cdm_handle,
+		cdm_cmd);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "Failed to apply the configs %d", rc);
+		goto end_callcb;
+	}
 
-		rc = cam_cdm_submit_bls(
-			hw_mgr->cdm_info[dev_type][0].cdm_handle,
-			cdm_cmd);
-		if (rc) {
-			CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
-				rc);
-			goto end_callcb;
-		}
-
-		if (!hw_mgr->devices[dev_type][0]->hw_ops.start) {
-			CAM_ERR(CAM_JPEG, "op start null ");
-			rc = -EINVAL;
-			goto end_callcb;
-		}
-		rc = hw_mgr->devices[dev_type][0]->hw_ops.start(
-			hw_mgr->devices[dev_type][0]->hw_priv,
-			NULL, 0);
-		if (rc) {
-			CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
-				rc);
-			goto end_callcb;
-		}
-	} else {
-		CAM_ERR(CAM_JPEG, "No commands to config");
+	if (!hw_mgr->devices[dev_type][0]->hw_ops.start) {
+		CAM_ERR(CAM_JPEG, "op start null ");
+		rc = -EINVAL;
+		goto end_callcb;
+	}
+	rc = hw_mgr->devices[dev_type][0]->hw_ops.start(
+		hw_mgr->devices[dev_type][0]->hw_priv,
+		NULL, 0);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "Failed to apply the configs %d",
+			rc);
+		goto end_callcb;
 	}
 
 	return rc;
@@ -422,6 +484,12 @@
 			(uint64_t)p_cfg_req->hw_cfg_args.priv;
 		ctx_data->ctxt_event_cb(ctx_data->context_priv, 0, &buf_data);
 	}
+end_unusedev:
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
+	hw_mgr->device_in_use[p_cfg_req->dev_type][0] = false;
+	hw_mgr->dev_hw_cfg_args[p_cfg_req->dev_type][0] = NULL;
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
 end:
 
 	return rc;
@@ -475,8 +543,8 @@
 
 	request_id = (uint64_t)config_args->priv;
 	hw_update_entries = config_args->hw_update_entries;
-	CAM_DBG(CAM_JPEG, "ctx_data = %pK req_id = %d %pK",
-		ctx_data, request_id, config_args->priv);
+	CAM_DBG(CAM_JPEG, "ctx_data = %pK req_id = %lld %lld",
+		ctx_data, request_id, (uint64_t)config_args->priv);
 	task = cam_req_mgr_workq_get_task(g_jpeg_hw_mgr.work_process_frame);
 	if (!task) {
 		CAM_ERR(CAM_JPEG, "no empty task");
@@ -534,6 +602,7 @@
 	struct cam_packet *packet = NULL;
 	struct cam_cmd_buf_desc *cmd_desc = NULL;
 	struct cam_buf_io_cfg *io_cfg_ptr = NULL;
+	struct cam_kmd_buf_info kmd_buf;
 
 	if (!prepare_args || !hw_mgr) {
 		CAM_ERR(CAM_JPEG, "Invalid args %pK %pK",
@@ -563,7 +632,14 @@
 			packet->header.op_code & 0xff);
 		return -EINVAL;
 	}
-	if ((packet->num_cmd_buf > 2) || !packet->num_patches ||
+
+	rc = cam_packet_util_validate_packet(packet);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "invalid packet %d", rc);
+		return rc;
+	}
+
+	if ((packet->num_cmd_buf > 5) || !packet->num_patches ||
 		!packet->num_io_configs) {
 		CAM_ERR(CAM_JPEG, "wrong number of cmd/patch info: %u %u",
 			packet->num_cmd_buf,
@@ -578,7 +654,7 @@
 		(void *)packet, (void *)cmd_desc,
 		sizeof(struct cam_cmd_buf_desc));
 
-	rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl);
+	rc = cam_packet_util_process_patches(packet, hw_mgr->iommu_hdl, -1);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "Patch processing failed %d", rc);
 		return rc;
@@ -610,16 +686,31 @@
 			i, io_cfg_ptr[i].direction, io_cfg_ptr[i].fence);
 	}
 
-	for (i = 0; i < packet->num_cmd_buf;  i++) {
-		prepare_args->hw_update_entries[i].len =
-			(uint32_t)cmd_desc[i].length;
-		prepare_args->hw_update_entries[i].handle =
-			(uint32_t)cmd_desc[i].mem_handle;
-		prepare_args->hw_update_entries[i].offset =
-			(uint32_t)cmd_desc[i].offset;
-		prepare_args->num_hw_update_entries++;
-	}
 
+	j = prepare_args->num_hw_update_entries;
+	rc = cam_packet_util_get_kmd_buffer(packet, &kmd_buf);
+	if (rc) {
+		CAM_ERR(CAM_JPEG, "get kmd buf failed %d", rc);
+		return rc;
+	}
+	/* fill kmd buf info into 1st hw update entry */
+	prepare_args->hw_update_entries[j].len =
+		(uint32_t)kmd_buf.used_bytes;
+	prepare_args->hw_update_entries[j].handle =
+		(uint32_t)kmd_buf.handle;
+	prepare_args->hw_update_entries[j].offset =
+		(uint32_t)kmd_buf.offset;
+	j++;
+
+	for (i = 0; i < packet->num_cmd_buf;  i++, j++) {
+		prepare_args->hw_update_entries[j].len =
+			(uint32_t)cmd_desc[i].length;
+		prepare_args->hw_update_entries[j].handle =
+			(uint32_t)cmd_desc[i].mem_handle;
+		prepare_args->hw_update_entries[j].offset =
+			(uint32_t)cmd_desc[i].offset;
+	}
+	prepare_args->num_hw_update_entries = j;
 	prepare_args->priv = (void *)packet->header.request_id;
 
 	CAM_DBG(CAM_JPEG, "will wait on input sync sync_id %d",
@@ -631,13 +722,12 @@
 static int cam_jpeg_mgr_release_hw(void *hw_mgr_priv, void *release_hw_args)
 {
 	int rc;
-	int ctx_id = 0;
 	struct cam_hw_release_args *release_hw = release_hw_args;
 	struct cam_jpeg_hw_mgr *hw_mgr = hw_mgr_priv;
 	struct cam_jpeg_hw_ctx_data *ctx_data = NULL;
 	uint32_t dev_type;
 
-	if (!release_hw || !hw_mgr) {
+	if (!hw_mgr || !release_hw || !release_hw->ctxt_to_hw_map) {
 		CAM_ERR(CAM_JPEG, "Invalid args");
 		return -EINVAL;
 	}
@@ -651,6 +741,11 @@
 	dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
 
 	mutex_lock(&hw_mgr->hw_mgr_mutex);
+	if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 0) {
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		CAM_ERR(CAM_JPEG, "Error Unbalanced deinit");
+		return -EFAULT;
+	}
 
 	hw_mgr->cdm_info[dev_type][0].ref_cnt--;
 	if (!(hw_mgr->cdm_info[dev_type][0].ref_cnt)) {
@@ -663,15 +758,9 @@
 		cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
 	}
 
-	if (g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.deinit) {
-		rc = g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.deinit(
-			g_jpeg_hw_mgr.devices[dev_type][0]->hw_priv, NULL, 0);
-		if (rc)
-			CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
-	}
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
-	rc = cam_jpeg_mgr_release_ctx(hw_mgr, ctx_id);
+	rc = cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data);
 	if (rc) {
 		mutex_unlock(&hw_mgr->hw_mgr_mutex);
 		return -EINVAL;
@@ -737,6 +826,11 @@
 	ctx_data->jpeg_dev_acquire_info = jpeg_dev_acquire_info;
 	mutex_unlock(&ctx_data->ctx_mutex);
 
+	if (ctx_data->jpeg_dev_acquire_info.dev_type >=
+		CAM_JPEG_RES_TYPE_MAX) {
+		rc = -EINVAL;
+		goto acq_cdm_hdl_failed;
+	}
 	dev_type = ctx_data->jpeg_dev_acquire_info.dev_type;
 	if (!hw_mgr->cdm_info[dev_type][0].ref_cnt) {
 
@@ -787,20 +881,6 @@
 		goto start_cdm_hdl_failed;
 	}
 
-	if (!g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.init) {
-		CAM_ERR(CAM_JPEG, "hw op init null ");
-		rc = -EINVAL;
-		goto start_cdm_hdl_failed;
-	}
-	rc = g_jpeg_hw_mgr.devices[dev_type][0]->hw_ops.init(
-		g_jpeg_hw_mgr.devices[dev_type][0]->hw_priv,
-		ctx_data,
-		sizeof(ctx_data));
-	if (rc) {
-		CAM_ERR(CAM_JPEG, "Failed to Init %d HW", dev_type);
-		goto start_cdm_hdl_failed;
-	}
-
 	if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
 		if (cam_cdm_stream_on(
 			hw_mgr->cdm_info[dev_type][0].cdm_handle)) {
@@ -833,12 +913,15 @@
 	return rc;
 
 copy_to_user_failed:
-	cam_cdm_stream_off(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+	if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
+		cam_cdm_stream_off(hw_mgr->cdm_info[dev_type][0].cdm_handle);
 start_cdm_hdl_failed:
-	cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+	if (hw_mgr->cdm_info[dev_type][0].ref_cnt == 1)
+		cam_cdm_release(hw_mgr->cdm_info[dev_type][0].cdm_handle);
+	hw_mgr->cdm_info[dev_type][0].ref_cnt--;
 acq_cdm_hdl_failed:
 	kfree(ctx_data->cdm_cmd);
-	cam_jpeg_mgr_release_ctx(hw_mgr, ctx_id);
+	cam_jpeg_mgr_release_ctx(hw_mgr, ctx_data);
 	mutex_unlock(&hw_mgr->hw_mgr_mutex);
 
 	return rc;
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h
deleted file mode 100644
index f0b4e00..0000000
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_enc_hw_intf.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef CAM_JPEG_ENC_HW_INTF_H
-#define CAM_JPEG_ENC_HW_INTF_H
-
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
-
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
-
-enum cam_jpeg_enc_cmd_type {
-	CAM_JPEG_ENC_CMD_CDM_CFG,
-	CAM_JPEG_ENC_CMD_SET_IRQ_CB,
-	CAM_JPEG_ENC_CMD_MAX,
-};
-
-#endif /* CAM_JPEG_ENC_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
index 3204388..44b134a 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_intf.h
@@ -13,14 +13,32 @@
 #ifndef CAM_JPEG_HW_INTF_H
 #define CAM_JPEG_HW_INTF_H
 
+#include "cam_cpas_api.h"
+
 #define CAM_JPEG_CTX_MAX              8
 #define CAM_JPEG_DEV_PER_TYPE_MAX     1
 
 #define CAM_JPEG_CMD_BUF_MAX_SIZE     128
 #define CAM_JPEG_MSG_BUF_MAX_SIZE     CAM_JPEG_CMD_BUF_MAX_SIZE
 
+#define JPEG_VOTE                     640000000
+
 enum cam_jpeg_hw_type {
 	CAM_JPEG_DEV_ENC,
 	CAM_JPEG_DEV_DMA,
 };
+
+struct cam_jpeg_set_irq_cb {
+	int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
+		int32_t result_size, void *data);
+	void *data;
+	uint32_t b_set_cb;
+};
+
+enum cam_jpeg_cmd_type {
+	CAM_JPEG_CMD_CDM_CFG,
+	CAM_JPEG_CMD_SET_IRQ_CB,
+	CAM_JPEG_CMD_MAX,
+};
+
 #endif
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
index d5c8c9d..5fb4e3ad 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_jpeg_hw_mgr_intf.h
@@ -17,34 +17,8 @@
 #include <uapi/media/cam_defs.h>
 #include <linux/of.h>
 
-#include "cam_cpas_api.h"
-
-#define JPEG_TURBO_VOTE           640000000
 
 int cam_jpeg_hw_mgr_init(struct device_node *of_node,
 	uint64_t *hw_mgr_hdl);
 
-/**
- * struct cam_jpeg_cpas_vote
- * @ahb_vote: AHB vote info
- * @axi_vote: AXI vote info
- * @ahb_vote_valid: Flag for ahb vote data
- * @axi_vote_valid: Flag for axi vote data
- */
-struct cam_jpeg_cpas_vote {
-	struct cam_ahb_vote ahb_vote;
-	struct cam_axi_vote axi_vote;
-	uint32_t ahb_vote_valid;
-	uint32_t axi_vote_valid;
-};
-
-struct cam_jpeg_set_irq_cb {
-	int32_t (*jpeg_hw_mgr_cb)(
-		uint32_t irq_status,
-		int32_t result_size,
-		void *data);
-	void *data;
-	uint32_t b_set_cb;
-};
-
 #endif /* CAM_JPEG_HW_MGR_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
index 05c1a95..2d343dd 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.c
@@ -26,7 +26,6 @@
 #include "jpeg_dma_soc.h"
 #include "cam_soc_util.h"
 #include "cam_io_util.h"
-#include "cam_dma_hw_intf.h"
 #include "cam_jpeg_hw_intf.h"
 #include "cam_jpeg_hw_mgr_intf.h"
 #include "cam_cpas_api.h"
@@ -38,7 +37,8 @@
 	struct cam_hw_info *jpeg_dma_dev = device_priv;
 	struct cam_hw_soc_info *soc_info = NULL;
 	struct cam_jpeg_dma_device_core_info *core_info = NULL;
-	struct cam_jpeg_cpas_vote cpas_vote;
+	struct cam_ahb_vote ahb_vote;
+	struct cam_axi_vote axi_vote;
 	int rc;
 
 	if (!device_priv) {
@@ -57,22 +57,40 @@
 		return -EINVAL;
 	}
 
-	cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
-	cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
-	cpas_vote.axi_vote.compressed_bw = JPEG_TURBO_VOTE;
-	cpas_vote.axi_vote.uncompressed_bw = JPEG_TURBO_VOTE;
+	mutex_lock(&core_info->core_mutex);
+	if (++core_info->ref_count > 1) {
+		mutex_unlock(&core_info->core_mutex);
+		return 0;
+	}
+
+	ahb_vote.type = CAM_VOTE_ABSOLUTE;
+	ahb_vote.vote.level = CAM_SVS_VOTE;
+	axi_vote.compressed_bw = JPEG_VOTE;
+	axi_vote.uncompressed_bw = JPEG_VOTE;
 
 	rc = cam_cpas_start(core_info->cpas_handle,
-		&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
-	if (rc)
+		&ahb_vote, &axi_vote);
+	if (rc) {
 		CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
+		goto cpas_failed;
+	}
 
 	rc = cam_jpeg_dma_enable_soc_resources(soc_info);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "soc enable is failed %d", rc);
-		cam_cpas_stop(core_info->cpas_handle);
+		goto soc_failed;
 	}
 
+	mutex_unlock(&core_info->core_mutex);
+
+	return 0;
+
+soc_failed:
+	cam_cpas_stop(core_info->cpas_handle);
+cpas_failed:
+	--core_info->ref_count;
+	mutex_unlock(&core_info->core_mutex);
+
 	return rc;
 }
 
@@ -98,6 +116,19 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(&core_info->core_mutex);
+	if (--core_info->ref_count > 0) {
+		mutex_unlock(&core_info->core_mutex);
+		return 0;
+	}
+
+	if (core_info->ref_count < 0) {
+		CAM_ERR(CAM_JPEG, "ref cnt %d", core_info->ref_count);
+		core_info->ref_count = 0;
+		mutex_unlock(&core_info->core_mutex);
+		return -EFAULT;
+	}
+
 	rc = cam_jpeg_dma_disable_soc_resources(soc_info);
 	if (rc)
 		CAM_ERR(CAM_JPEG, "soc enable failed %d", rc);
@@ -106,6 +137,8 @@
 	if (rc)
 		CAM_ERR(CAM_JPEG, "cpas stop failed: %d", rc);
 
+	mutex_unlock(&core_info->core_mutex);
+
 	return 0;
 }
 
@@ -121,7 +154,7 @@
 		return -EINVAL;
 	}
 
-	if (cmd_type >= CAM_JPEG_DMA_CMD_MAX) {
+	if (cmd_type >= CAM_JPEG_CMD_MAX) {
 		CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type);
 		return -EINVAL;
 	}
@@ -131,7 +164,7 @@
 		core_info;
 
 	switch (cmd_type) {
-	case CAM_JPEG_DMA_CMD_SET_IRQ_CB:
+	case CAM_JPEG_CMD_SET_IRQ_CB:
 	{
 		struct cam_jpeg_set_irq_cb *irq_cb = cmd_args;
 
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
index bb4e34a..a4d5d89 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_core.h
@@ -19,16 +19,12 @@
 #include <linux/platform_device.h>
 #include <linux/dma-buf.h>
 
+#include "cam_jpeg_hw_intf.h"
+
 struct cam_jpeg_dma_device_hw_info {
 	uint32_t reserved;
 };
 
-struct cam_jpeg_dma_set_irq_cb {
-	int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
-		int32_t result_size, void *data);
-	void *data;
-};
-
 enum cam_jpeg_dma_core_state {
 	CAM_JPEG_DMA_CORE_NOT_READY,
 	CAM_JPEG_DMA_CORE_READY,
@@ -40,7 +36,9 @@
 	enum cam_jpeg_dma_core_state core_state;
 	struct cam_jpeg_dma_device_hw_info *jpeg_dma_hw_info;
 	uint32_t cpas_handle;
-	struct cam_jpeg_dma_set_irq_cb irq_cb;
+	struct cam_jpeg_set_irq_cb irq_cb;
+	int32_t ref_count;
+	struct mutex core_mutex;
 };
 
 int cam_jpeg_dma_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c
index 55a344d..ed58b41 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_dev.c
@@ -99,6 +99,7 @@
 	if (rc)
 		CAM_ERR(CAM_JPEG, " unreg failed to reg cpas %d", rc);
 
+	mutex_destroy(&core_info->core_mutex);
 	kfree(core_info);
 
 deinit_soc:
@@ -165,13 +166,14 @@
 	hw_info = (struct cam_jpeg_dma_device_hw_info *)match_dev->data;
 	core_info->jpeg_dma_hw_info = hw_info;
 	core_info->core_state = CAM_JPEG_DMA_CORE_NOT_READY;
+	mutex_init(&core_info->core_mutex);
 
 	rc = cam_jpeg_dma_init_soc_resources(&jpeg_dma_dev->soc_info,
 		cam_jpeg_dma_irq,
 		jpeg_dma_dev);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "%failed to init_soc %d", rc);
-		goto error_match_dev;
+		goto error_init_soc;
 	}
 
 	rc = cam_jpeg_dma_register_cpas(&jpeg_dma_dev->soc_info,
@@ -191,6 +193,8 @@
 
 error_reg_cpas:
 	rc = cam_soc_util_release_platform_resource(&jpeg_dma_dev->soc_info);
+error_init_soc:
+	mutex_destroy(&core_info->core_mutex);
 error_match_dev:
 	kfree(jpeg_dma_dev->core_info);
 error_alloc_core:
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c
index efc161b..63d54fd 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_dma_hw/jpeg_dma_soc.c
@@ -55,7 +55,7 @@
 {
 	int rc;
 
-	rc = cam_soc_util_disable_platform_resource(soc_info, true, false);
+	rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
 	if (rc)
 		CAM_ERR(CAM_JPEG, "disable platform failed %d", rc);
 
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h
new file mode 100644
index 0000000..725af47
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/cam_jpeg_enc_hw_info_ver_4_2_0.h
@@ -0,0 +1,74 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CAM_JPEG_ENC_HW_INFO_TITAN170_H
+#define CAM_JPEG_ENC_HW_INFO_TITAN170_H
+
+#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
+#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
+
+#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000
+#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
+
+#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
+#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
+
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF      (0x1<<19)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR     (0x1<<20)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR   (0x1<<21)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW    (0x1<<23)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM       (0x1<<24)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ   (0x1<<25)
+#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM       (0x1<<26)
+#define CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK        (0x1<<29)
+
+#define CAM_JPEG_HW_MASK_COMP_FRAMEDONE \
+		CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK
+#define CAM_JPEG_HW_MASK_COMP_RESET_ACK \
+		CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK
+#define CAM_JPEG_HW_MASK_COMP_ERR \
+		(CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \
+		CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \
+		CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK)
+
+static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = {
+	.reg_offset = {
+		.hw_version = 0x0,
+		.int_clr = 0x1c,
+		.int_status = 0x20,
+		.int_mask = 0x18,
+		.hw_cmd = 0x10,
+		.reset_cmd = 0x8,
+		.encode_size = 0x180,
+	},
+	.reg_val = {
+		.int_clr_clearall = 0xFFFFFFFF,
+		.int_mask_disable_all = 0x00000000,
+		.int_mask_enable_all = 0xFFFFFFFF,
+		.hw_cmd_start = 0x00000001,
+		.reset_cmd = 0x00032093,
+	},
+	.int_status = {
+		.framedone = CAM_JPEG_HW_MASK_COMP_FRAMEDONE,
+		.resetdone = CAM_JPEG_HW_MASK_COMP_RESET_ACK,
+		.iserror = CAM_JPEG_HW_MASK_COMP_ERR,
+	}
+};
+
+#endif /* CAM_JPEG_ENC_HW_INFO_TITAN170_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
index 25405cf..a7c4e06 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.c
@@ -26,52 +26,17 @@
 #include "jpeg_enc_soc.h"
 #include "cam_soc_util.h"
 #include "cam_io_util.h"
-#include "cam_enc_hw_intf.h"
 #include "cam_jpeg_hw_intf.h"
 #include "cam_jpeg_hw_mgr_intf.h"
 #include "cam_cpas_api.h"
 #include "cam_debug_util.h"
 
-#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK 0x00000001
-#define CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_SHIFT 0x00000000
-
-#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK 0x10000000
-#define CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_SHIFT 0x0000000a
-
-#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_MASK 0x00000800
-#define CAM_JPEG_HW_IRQ_STATUS_BUS_ERROR_SHIFT 0x0000000b
-
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF      (0x1<<19)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR     (0x1<<20)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR   (0x1<<21)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF (0x1<<22)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW    (0x1<<23)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM       (0x1<<24)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ   (0x1<<25)
-#define CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM       (0x1<<26)
-#define CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK        (0x1<<29)
-
-#define CAM_JPEG_HW_MASK_COMP_FRAMEDONE \
-		CAM_JPEG_HW_IRQ_STATUS_FRAMEDONE_MASK
-#define CAM_JPEG_HW_MASK_COMP_RESET_ACK \
-		CAM_JPEG_HW_IRQ_STATUS_RESET_ACK_MASK
-#define CAM_JPEG_HW_MASK_COMP_ERR \
-		(CAM_JPEG_HW_IRQ_STATUS_DCD_UNESCAPED_FF | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_HUFFMAN_ERROR | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_COEFFICIENT_ERR | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_BIT_STUFF | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_SCAN_UNDERFLOW | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_INVALID_RSM_SEQ | \
-		CAM_JPEG_HW_IRQ_STATUS_DCD_MISSING_RSM | \
-		CAM_JPEG_HW_IRQ_STATUS_VIOLATION_MASK)
-
-#define CAM_JPEG_HW_IRQ_IS_FRAME_DONE(jpeg_irq_status) \
-	(jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_FRAMEDONE)
-#define CAM_JPEG_HW_IRQ_IS_RESET_ACK(jpeg_irq_status) \
-	(jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_RESET_ACK)
-#define CAM_JPEG_HW_IRQ_IS_ERR(jpeg_irq_status) \
-	(jpeg_irq_status & CAM_JPEG_HW_MASK_COMP_ERR)
+#define CAM_JPEG_HW_IRQ_IS_FRAME_DONE(jpeg_irq_status, hi) \
+	((jpeg_irq_status) & (hi)->int_status.framedone)
+#define CAM_JPEG_HW_IRQ_IS_RESET_ACK(jpeg_irq_status, hi) \
+	((jpeg_irq_status) & (hi)->int_status.resetdone)
+#define CAM_JPEG_HW_IRQ_IS_ERR(jpeg_irq_status, hi) \
+	((jpeg_irq_status) & (hi)->int_status.iserror)
 
 #define CAM_JPEG_ENC_RESET_TIMEOUT msecs_to_jiffies(500)
 
@@ -81,7 +46,8 @@
 	struct cam_hw_info *jpeg_enc_dev = device_priv;
 	struct cam_hw_soc_info *soc_info = NULL;
 	struct cam_jpeg_enc_device_core_info *core_info = NULL;
-	struct cam_jpeg_cpas_vote cpas_vote;
+	struct cam_ahb_vote ahb_vote;
+	struct cam_axi_vote axi_vote;
 	int rc;
 
 	if (!device_priv) {
@@ -100,22 +66,40 @@
 		return -EINVAL;
 	}
 
-	cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
-	cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
-	cpas_vote.axi_vote.compressed_bw = JPEG_TURBO_VOTE;
-	cpas_vote.axi_vote.uncompressed_bw = JPEG_TURBO_VOTE;
+	mutex_lock(&core_info->core_mutex);
+	if (++core_info->ref_count > 1) {
+		mutex_unlock(&core_info->core_mutex);
+		return 0;
+	}
+
+	ahb_vote.type = CAM_VOTE_ABSOLUTE;
+	ahb_vote.vote.level = CAM_SVS_VOTE;
+	axi_vote.compressed_bw = JPEG_VOTE;
+	axi_vote.uncompressed_bw = JPEG_VOTE;
 
 	rc = cam_cpas_start(core_info->cpas_handle,
-		&cpas_vote.ahb_vote, &cpas_vote.axi_vote);
-	if (rc)
+		&ahb_vote, &axi_vote);
+	if (rc) {
 		CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
+		goto cpas_failed;
+	}
 
 	rc = cam_jpeg_enc_enable_soc_resources(soc_info);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "soc enable is failed %d", rc);
-		cam_cpas_stop(core_info->cpas_handle);
+		goto soc_failed;
 	}
 
+	mutex_unlock(&core_info->core_mutex);
+
+	return 0;
+
+soc_failed:
+	cam_cpas_stop(core_info->cpas_handle);
+cpas_failed:
+	--core_info->ref_count;
+	mutex_unlock(&core_info->core_mutex);
+
 	return rc;
 }
 
@@ -141,14 +125,29 @@
 		return -EINVAL;
 	}
 
+	mutex_lock(&core_info->core_mutex);
+	if (--core_info->ref_count > 0) {
+		mutex_unlock(&core_info->core_mutex);
+		return 0;
+	}
+
+	if (core_info->ref_count < 0) {
+		CAM_ERR(CAM_JPEG, "ref cnt %d", core_info->ref_count);
+		core_info->ref_count = 0;
+		mutex_unlock(&core_info->core_mutex);
+		return -EFAULT;
+	}
+
 	rc = cam_jpeg_enc_disable_soc_resources(soc_info);
 	if (rc)
-		CAM_ERR(CAM_JPEG, "soc enable failed %d", rc);
+		CAM_ERR(CAM_JPEG, "soc disable failed %d", rc);
 
 	rc = cam_cpas_stop(core_info->cpas_handle);
 	if (rc)
 		CAM_ERR(CAM_JPEG, "cpas stop failed: %d", rc);
 
+	mutex_unlock(&core_info->core_mutex);
+
 	return 0;
 }
 
@@ -174,17 +173,19 @@
 	mem_base = soc_info->reg_map[0].mem_base;
 
 	irq_status = cam_io_r_mb(mem_base +
-		core_info->jpeg_enc_hw_info->int_status);
+		core_info->jpeg_enc_hw_info->reg_offset.int_status);
 
 	cam_io_w_mb(irq_status,
 		soc_info->reg_map[0].mem_base +
-		core_info->jpeg_enc_hw_info->int_clr);
+		core_info->jpeg_enc_hw_info->reg_offset.int_clr);
 
 	CAM_DBG(CAM_JPEG, "irq_num %d  irq_status = %x , core_state %d",
 		irq_num, irq_status, core_info->core_state);
-	if (CAM_JPEG_HW_IRQ_IS_FRAME_DONE(irq_status)) {
+	if (CAM_JPEG_HW_IRQ_IS_FRAME_DONE(irq_status, hw_info)) {
 		if (core_info->core_state == CAM_JPEG_ENC_CORE_READY) {
-			encoded_size = cam_io_r_mb(mem_base + 0x180);
+			encoded_size = cam_io_r_mb(mem_base +
+				core_info->jpeg_enc_hw_info->reg_offset.
+				encode_size);
 			if (core_info->irq_cb.jpeg_hw_mgr_cb) {
 				core_info->irq_cb.jpeg_hw_mgr_cb(irq_status,
 					encoded_size,
@@ -196,7 +197,7 @@
 
 		core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY;
 	}
-	if (CAM_JPEG_HW_IRQ_IS_RESET_ACK(irq_status)) {
+	if (CAM_JPEG_HW_IRQ_IS_RESET_ACK(irq_status, hw_info)) {
 		if (core_info->core_state == CAM_JPEG_ENC_CORE_RESETTING) {
 			core_info->core_state = CAM_JPEG_ENC_CORE_READY;
 			complete(&jpeg_enc_dev->hw_complete);
@@ -205,7 +206,7 @@
 		}
 	}
 	/* Unexpected/unintended HW interrupt */
-	if (CAM_JPEG_HW_IRQ_IS_ERR(irq_status)) {
+	if (CAM_JPEG_HW_IRQ_IS_ERR(irq_status, hw_info)) {
 		core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY;
 		CAM_ERR_RATE_LIMIT(CAM_JPEG,
 			"error irq_num %d  irq_status = %x , core_state %d",
@@ -252,10 +253,14 @@
 
 	core_info->core_state = CAM_JPEG_ENC_CORE_RESETTING;
 
-	cam_io_w_mb(0x00000000, mem_base + hw_info->int_mask);
-	cam_io_w_mb(0xFFFFFFFF, mem_base + hw_info->int_clr);
-	cam_io_w_mb(0xFFFFFFFF, mem_base + hw_info->int_mask);
-	cam_io_w_mb(0x00032093, mem_base + hw_info->reset_cmd);
+	cam_io_w_mb(hw_info->reg_val.int_mask_disable_all,
+		mem_base + hw_info->reg_offset.int_mask);
+	cam_io_w_mb(hw_info->reg_val.int_clr_clearall,
+		mem_base + hw_info->reg_offset.int_clr);
+	cam_io_w_mb(hw_info->reg_val.int_mask_enable_all,
+		mem_base + hw_info->reg_offset.int_mask);
+	cam_io_w_mb(hw_info->reg_val.reset_cmd,
+		mem_base + hw_info->reg_offset.reset_cmd);
 
 	rem_jiffies = wait_for_completion_timeout(&jpeg_enc_dev->hw_complete,
 		CAM_JPEG_ENC_RESET_TIMEOUT);
@@ -292,7 +297,8 @@
 		return -EINVAL;
 	}
 
-	cam_io_w_mb(0x00000001, mem_base + 0x00000010);
+	cam_io_w_mb(hw_info->reg_val.hw_cmd_start,
+		mem_base + hw_info->reg_offset.hw_cmd);
 
 	return 0;
 }
@@ -309,7 +315,7 @@
 		return -EINVAL;
 	}
 
-	if (cmd_type >= CAM_JPEG_ENC_CMD_MAX) {
+	if (cmd_type >= CAM_JPEG_CMD_MAX) {
 		CAM_ERR(CAM_JPEG, "Invalid command : %x", cmd_type);
 		return -EINVAL;
 	}
@@ -319,7 +325,7 @@
 		core_info;
 
 	switch (cmd_type) {
-	case CAM_JPEG_ENC_CMD_SET_IRQ_CB:
+	case CAM_JPEG_CMD_SET_IRQ_CB:
 	{
 		struct cam_jpeg_set_irq_cb *irq_cb = cmd_args;
 
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
index 6ae4cdc..4f5d625 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_core.h
@@ -19,18 +19,36 @@
 #include <linux/platform_device.h>
 #include <linux/dma-buf.h>
 
-struct cam_jpeg_enc_device_hw_info {
+#include "cam_jpeg_hw_intf.h"
+
+struct cam_jpeg_enc_reg_offsets {
 	uint32_t hw_version;
 	uint32_t int_status;
 	uint32_t int_clr;
 	uint32_t int_mask;
+	uint32_t hw_cmd;
+	uint32_t reset_cmd;
+	uint32_t encode_size;
+};
+
+struct cam_jpeg_enc_regval {
+	uint32_t int_clr_clearall;
+	uint32_t int_mask_disable_all;
+	uint32_t int_mask_enable_all;
+	uint32_t hw_cmd_start;
 	uint32_t reset_cmd;
 };
 
-struct cam_jpeg_enc_set_irq_cb {
-	int32_t (*jpeg_hw_mgr_cb)(uint32_t irq_status,
-		int32_t result_size, void *data);
-	void *data;
+struct cam_jpeg_enc_int_status {
+	uint32_t framedone;
+	uint32_t resetdone;
+	uint32_t iserror;
+};
+
+struct cam_jpeg_enc_device_hw_info {
+	struct cam_jpeg_enc_reg_offsets reg_offset;
+	struct cam_jpeg_enc_regval reg_val;
+	struct cam_jpeg_enc_int_status int_status;
 };
 
 enum cam_jpeg_enc_core_state {
@@ -44,7 +62,9 @@
 	enum cam_jpeg_enc_core_state core_state;
 	struct cam_jpeg_enc_device_hw_info *jpeg_enc_hw_info;
 	uint32_t cpas_handle;
-	struct cam_jpeg_enc_set_irq_cb irq_cb;
+	struct cam_jpeg_set_irq_cb irq_cb;
+	int32_t ref_count;
+	struct mutex core_mutex;
 };
 
 int cam_jpeg_enc_init_hw(void *device_priv,
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
index a8f309a..735bd21 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_dev.c
@@ -25,15 +25,7 @@
 #include "cam_jpeg_hw_mgr_intf.h"
 #include "cam_cpas_api.h"
 #include "cam_debug_util.h"
-
-static struct cam_jpeg_enc_device_hw_info cam_jpeg_enc_hw_info = {
-	.int_clr = 0x1c,
-	.int_status = 0x20,
-	.int_mask = 0x18,
-	.reset_cmd = 0x8,
-	.hw_version = 0x0,
-};
-EXPORT_SYMBOL(cam_jpeg_enc_hw_info);
+#include "cam_jpeg_enc_hw_info_ver_4_2_0.h"
 
 static int cam_jpeg_enc_register_cpas(struct cam_hw_soc_info *soc_info,
 	struct cam_jpeg_enc_device_core_info *core_info,
@@ -103,6 +95,7 @@
 	if (rc)
 		CAM_ERR(CAM_JPEG, " unreg failed to reg cpas %d", rc);
 
+	mutex_destroy(&core_info->core_mutex);
 	kfree(core_info);
 
 deinit_soc:
@@ -171,13 +164,14 @@
 	hw_info = (struct cam_jpeg_enc_device_hw_info *)match_dev->data;
 	core_info->jpeg_enc_hw_info = hw_info;
 	core_info->core_state = CAM_JPEG_ENC_CORE_NOT_READY;
+	mutex_init(&core_info->core_mutex);
 
 	rc = cam_jpeg_enc_init_soc_resources(&jpeg_enc_dev->soc_info,
 		cam_jpeg_enc_irq,
 		jpeg_enc_dev);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, " failed to init_soc %d", rc);
-		goto error_match_dev;
+		goto error_init_soc;
 	}
 
 	rc = cam_jpeg_enc_register_cpas(&jpeg_enc_dev->soc_info,
@@ -195,6 +189,8 @@
 
 error_reg_cpas:
 	cam_soc_util_release_platform_resource(&jpeg_enc_dev->soc_info);
+error_init_soc:
+	mutex_destroy(&core_info->core_mutex);
 error_match_dev:
 	kfree(jpeg_enc_dev->core_info);
 error_alloc_core:
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c
index 3f450cd..ddf2465 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c
+++ b/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/jpeg_enc_hw/jpeg_enc_soc.c
@@ -55,7 +55,7 @@
 {
 	int rc;
 
-	rc = cam_soc_util_disable_platform_resource(soc_info, true, false);
+	rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
 	if (rc)
 		CAM_ERR(CAM_JPEG, "disable platform failed %d", rc);
 
diff --git a/drivers/media/platform/msm/camera/cam_lrme/Makefile b/drivers/media/platform/msm/camera/cam_lrme/Makefile
new file mode 100644
index 0000000..fba4529
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include/
+
+obj-$(CONFIG_SPECTRA_CAMERA) += lrme_hw_mgr/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_dev.o cam_lrme_context.o
diff --git a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c
new file mode 100644
index 0000000..0aa5ade
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.c
@@ -0,0 +1,241 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_debug_util.h"
+#include "cam_lrme_context.h"
+
+static int __cam_lrme_ctx_acquire_dev_in_available(struct cam_context *ctx,
+	struct cam_acquire_dev_cmd *cmd)
+{
+	int rc = 0;
+	uint64_t ctxt_to_hw_map = (uint64_t)ctx->ctxt_to_hw_map;
+	struct cam_lrme_context *lrme_ctx = ctx->ctx_priv;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_acquire_dev_to_hw(ctx, cmd);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to acquire");
+		return rc;
+	}
+
+	ctxt_to_hw_map |= (lrme_ctx->index << CAM_LRME_CTX_INDEX_SHIFT);
+	ctx->ctxt_to_hw_map = (void *)ctxt_to_hw_map;
+
+	ctx->state = CAM_CTX_ACQUIRED;
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_release_dev_in_acquired(struct cam_context *ctx,
+	struct cam_release_dev_cmd *cmd)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_release_dev_to_hw(ctx, cmd);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to release");
+		return rc;
+	}
+
+	ctx->state = CAM_CTX_AVAILABLE;
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_start_dev_in_acquired(struct cam_context *ctx,
+	struct cam_start_stop_dev_cmd *cmd)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_start_dev_to_hw(ctx, cmd);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to start");
+		return rc;
+	}
+
+	ctx->state = CAM_CTX_ACTIVATED;
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_config_dev_in_activated(struct cam_context *ctx,
+	struct cam_config_dev_cmd *cmd)
+{
+	int rc;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_prepare_dev_to_hw(ctx, cmd);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to config");
+		return rc;
+	}
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_stop_dev_in_activated(struct cam_context *ctx,
+	struct cam_start_stop_dev_cmd *cmd)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_stop_dev_to_hw(ctx);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to stop dev");
+		return rc;
+	}
+
+	ctx->state = CAM_CTX_ACQUIRED;
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_release_dev_in_activated(struct cam_context *ctx,
+	struct cam_release_dev_cmd *cmd)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = __cam_lrme_ctx_stop_dev_in_activated(ctx, NULL);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to stop");
+		return rc;
+	}
+
+	rc = cam_context_release_dev_to_hw(ctx, cmd);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to release");
+		return rc;
+	}
+
+	ctx->state = CAM_CTX_AVAILABLE;
+
+	return rc;
+}
+
+static int __cam_lrme_ctx_handle_irq_in_activated(void *context,
+	uint32_t evt_id, void *evt_data)
+{
+	int rc;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	rc = cam_context_buf_done_from_hw(context, evt_data, evt_id);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed in buf done, rc=%d", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+/* top state machine */
+static struct cam_ctx_ops
+	cam_lrme_ctx_state_machine[CAM_CTX_STATE_MAX] = {
+	/* Uninit */
+	{
+		.ioctl_ops = {},
+		.crm_ops = {},
+		.irq_ops = NULL,
+	},
+	/* Available */
+	{
+		.ioctl_ops = {
+			.acquire_dev = __cam_lrme_ctx_acquire_dev_in_available,
+		},
+		.crm_ops = {},
+		.irq_ops = NULL,
+	},
+	/* Acquired */
+	{
+		.ioctl_ops = {
+			.release_dev = __cam_lrme_ctx_release_dev_in_acquired,
+			.start_dev = __cam_lrme_ctx_start_dev_in_acquired,
+		},
+		.crm_ops = {},
+		.irq_ops = NULL,
+	},
+	/* Ready */
+	{
+		.ioctl_ops = {},
+		.crm_ops = {},
+		.irq_ops = NULL,
+	},
+	/* Activate */
+	{
+		.ioctl_ops = {
+			.config_dev = __cam_lrme_ctx_config_dev_in_activated,
+			.release_dev = __cam_lrme_ctx_release_dev_in_activated,
+			.stop_dev = __cam_lrme_ctx_stop_dev_in_activated,
+		},
+		.crm_ops = {},
+		.irq_ops = __cam_lrme_ctx_handle_irq_in_activated,
+	},
+};
+
+int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx,
+	struct cam_context *base_ctx,
+	struct cam_hw_mgr_intf *hw_intf,
+	uint64_t index)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	if (!base_ctx || !lrme_ctx) {
+		CAM_ERR(CAM_LRME, "Invalid input");
+		return -EINVAL;
+	}
+
+	memset(lrme_ctx, 0, sizeof(*lrme_ctx));
+
+	rc = cam_context_init(base_ctx, "lrme", NULL, hw_intf,
+		lrme_ctx->req_base, CAM_CTX_REQ_MAX);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to init context");
+		return rc;
+	}
+	lrme_ctx->base = base_ctx;
+	lrme_ctx->index = index;
+	base_ctx->ctx_priv = lrme_ctx;
+	base_ctx->state_machine = cam_lrme_ctx_state_machine;
+
+	return rc;
+}
+
+int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx)
+{
+	int rc = 0;
+
+	CAM_DBG(CAM_LRME, "Enter");
+
+	if (!lrme_ctx) {
+		CAM_ERR(CAM_LRME, "No ctx to deinit");
+		return -EINVAL;
+	}
+
+	rc = cam_context_deinit(lrme_ctx->base);
+
+	memset(lrme_ctx, 0, sizeof(*lrme_ctx));
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h
new file mode 100644
index 0000000..882f7ac
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_context.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_CONTEXT_H_
+#define _CAM_LRME_CONTEXT_H_
+
+#include "cam_context.h"
+#include "cam_context_utils.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_req_mgr_interface.h"
+#include "cam_sync_api.h"
+
+#define CAM_LRME_CTX_INDEX_SHIFT 32
+
+/**
+ * struct cam_lrme_context
+ *
+ * @base      : Base context pointer for this LRME context
+ * @req_base  : List of base request for this LRME context
+ */
+struct cam_lrme_context {
+	struct cam_context         *base;
+	struct cam_ctx_request      req_base[CAM_CTX_REQ_MAX];
+	uint64_t index;
+};
+
+int cam_lrme_context_init(struct cam_lrme_context *lrme_ctx,
+	struct cam_context *base_ctx, struct cam_hw_mgr_intf *hw_intf,
+	uint64_t index);
+int cam_lrme_context_deinit(struct cam_lrme_context *lrme_ctx);
+
+#endif /* _CAM_LRME_CONTEXT_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_dev.c b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_dev.c
new file mode 100644
index 0000000..5be16ef
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/cam_lrme_dev.c
@@ -0,0 +1,233 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_subdev.h"
+#include "cam_node.h"
+#include "cam_lrme_context.h"
+#include "cam_lrme_hw_mgr.h"
+#include "cam_lrme_hw_mgr_intf.h"
+
+#define CAM_LRME_DEV_NAME "cam-lrme"
+
+/**
+ * struct cam_lrme_dev
+ *
+ * @sd       : Subdev information
+ * @ctx      : List of base contexts
+ * @lrme_ctx : List of LRME contexts
+ * @lock     : Mutex for LRME subdev
+ * @open_cnt : Open count of LRME subdev
+ */
+struct cam_lrme_dev {
+	struct cam_subdev        sd;
+	struct cam_context       ctx[CAM_CTX_MAX];
+	struct cam_lrme_context  lrme_ctx[CAM_CTX_MAX];
+	struct mutex             lock;
+	uint32_t                 open_cnt;
+};
+
+static struct cam_lrme_dev *g_lrme_dev;
+
+static int cam_lrme_dev_buf_done_cb(void *ctxt_to_hw_map, uint32_t evt_id,
+	void *evt_data)
+{
+	uint64_t index;
+	struct cam_context *ctx;
+	int rc;
+
+	index = CAM_LRME_DECODE_CTX_INDEX(ctxt_to_hw_map);
+	CAM_DBG(CAM_LRME, "ctx index %llu, evt_id %u\n", index, evt_id);
+	ctx = &g_lrme_dev->ctx[index];
+	rc = ctx->irq_cb_intf(ctx, evt_id, evt_data);
+	if (rc)
+		CAM_ERR(CAM_LRME, "irq callback failed");
+
+	return rc;
+}
+
+static int cam_lrme_dev_open(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_lrme_dev *lrme_dev = g_lrme_dev;
+
+	if (!lrme_dev) {
+		CAM_ERR(CAM_LRME,
+			"LRME Dev not initialized, dev=%pK", lrme_dev);
+		return -ENODEV;
+	}
+
+	mutex_lock(&lrme_dev->lock);
+	lrme_dev->open_cnt++;
+	mutex_unlock(&lrme_dev->lock);
+
+	return 0;
+}
+
+static int cam_lrme_dev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_lrme_dev *lrme_dev = g_lrme_dev;
+	struct cam_node *node = v4l2_get_subdevdata(sd);
+
+	if (!lrme_dev) {
+		CAM_ERR(CAM_LRME, "Invalid args");
+		return -ENODEV;
+	}
+
+	mutex_lock(&lrme_dev->lock);
+	lrme_dev->open_cnt--;
+	mutex_unlock(&lrme_dev->lock);
+
+	if (!node) {
+		CAM_ERR(CAM_LRME, "Node is NULL");
+		return -EINVAL;
+	}
+
+	if (lrme_dev->open_cnt == 0)
+		cam_node_shutdown(node);
+
+	return 0;
+}
+
+static const struct v4l2_subdev_internal_ops cam_lrme_subdev_internal_ops = {
+	.open = cam_lrme_dev_open,
+	.close = cam_lrme_dev_close,
+};
+
+static int cam_lrme_dev_probe(struct platform_device *pdev)
+{
+	int rc;
+	int i;
+	struct cam_hw_mgr_intf hw_mgr_intf;
+	struct cam_node *node;
+
+	g_lrme_dev = kzalloc(sizeof(struct cam_lrme_dev), GFP_KERNEL);
+	if (!g_lrme_dev) {
+		CAM_ERR(CAM_LRME, "No memory");
+		return -ENOMEM;
+	}
+	g_lrme_dev->sd.internal_ops = &cam_lrme_subdev_internal_ops;
+
+	mutex_init(&g_lrme_dev->lock);
+
+	rc = cam_subdev_probe(&g_lrme_dev->sd, pdev, CAM_LRME_DEV_NAME,
+		CAM_LRME_DEVICE_TYPE);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "LRME cam_subdev_probe failed");
+		goto free_mem;
+	}
+	node = (struct cam_node *)g_lrme_dev->sd.token;
+
+	rc = cam_lrme_hw_mgr_init(&hw_mgr_intf, cam_lrme_dev_buf_done_cb);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Can not initialized LRME HW manager");
+		goto unregister;
+	}
+
+	for (i = 0; i < CAM_CTX_MAX; i++) {
+		rc = cam_lrme_context_init(&g_lrme_dev->lrme_ctx[i],
+				&g_lrme_dev->ctx[i],
+				&node->hw_mgr_intf, i);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "LRME context init failed");
+			goto deinit_ctx;
+		}
+	}
+
+	rc = cam_node_init(node, &hw_mgr_intf, g_lrme_dev->ctx, CAM_CTX_MAX,
+		CAM_LRME_DEV_NAME);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "LRME node init failed");
+		goto deinit_ctx;
+	}
+
+	CAM_DBG(CAM_LRME, "%s probe complete", g_lrme_dev->sd.name);
+
+	return 0;
+
+deinit_ctx:
+	for (--i; i >= 0; i--) {
+		if (cam_lrme_context_deinit(&g_lrme_dev->lrme_ctx[i]))
+			CAM_ERR(CAM_LRME, "LRME context %d deinit failed", i);
+	}
+unregister:
+	if (cam_subdev_remove(&g_lrme_dev->sd))
+		CAM_ERR(CAM_LRME, "Failed in subdev remove");
+free_mem:
+	kfree(g_lrme_dev);
+
+	return rc;
+}
+
+static int cam_lrme_dev_remove(struct platform_device *pdev)
+{
+	int i;
+	int rc = 0;
+
+	for (i = 0; i < CAM_CTX_MAX; i++) {
+		rc = cam_lrme_context_deinit(&g_lrme_dev->lrme_ctx[i]);
+		if (rc)
+			CAM_ERR(CAM_LRME, "LRME context %d deinit failed", i);
+	}
+
+	rc = cam_lrme_hw_mgr_deinit();
+	if (rc)
+		CAM_ERR(CAM_LRME, "Failed in hw mgr deinit, rc=%d", rc);
+
+	rc = cam_subdev_remove(&g_lrme_dev->sd);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Unregister failed");
+
+	mutex_destroy(&g_lrme_dev->lock);
+	kfree(g_lrme_dev);
+	g_lrme_dev = NULL;
+
+	return rc;
+}
+
+static const struct of_device_id cam_lrme_dt_match[] = {
+	{
+		.compatible = "qcom,cam-lrme"
+	},
+	{}
+};
+
+static struct platform_driver cam_lrme_driver = {
+	.probe = cam_lrme_dev_probe,
+	.remove = cam_lrme_dev_remove,
+	.driver = {
+		.name = "cam_lrme",
+		.owner = THIS_MODULE,
+		.of_match_table = cam_lrme_dt_match,
+	},
+};
+
+static int __init cam_lrme_dev_init_module(void)
+{
+	return platform_driver_register(&cam_lrme_driver);
+}
+
+static void __exit cam_lrme_dev_exit_module(void)
+{
+	platform_driver_unregister(&cam_lrme_driver);
+}
+
+module_init(cam_lrme_dev_init_module);
+module_exit(cam_lrme_dev_exit_module);
+MODULE_DESCRIPTION("MSM LRME driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/Makefile b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/Makefile
new file mode 100644
index 0000000..e4c8e0d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/Makefile
@@ -0,0 +1,14 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw
+ccflags-y += -Idrivers/media/platform/msm/camera
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += lrme_hw/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_hw_mgr.o
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c
new file mode 100644
index 0000000..448086d
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.c
@@ -0,0 +1,1034 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_cpas.h>
+#include <media/cam_req_mgr.h>
+
+#include "cam_io_util.h"
+#include "cam_soc_util.h"
+#include "cam_mem_mgr_api.h"
+#include "cam_smmu_api.h"
+#include "cam_packet_util.h"
+#include "cam_lrme_context.h"
+#include "cam_lrme_hw_intf.h"
+#include "cam_lrme_hw_core.h"
+#include "cam_lrme_hw_soc.h"
+#include "cam_lrme_hw_mgr_intf.h"
+#include "cam_lrme_hw_mgr.h"
+
+static struct cam_lrme_hw_mgr g_lrme_hw_mgr;
+
+static int cam_lrme_mgr_util_reserve_device(struct cam_lrme_hw_mgr *hw_mgr,
+	struct cam_lrme_acquire_args *lrme_acquire_args)
+{
+	int i, index = 0;
+	uint32_t min_ctx = UINT_MAX;
+	struct cam_lrme_device *hw_device = NULL;
+
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
+	if (!hw_mgr->device_count) {
+		mutex_unlock(&hw_mgr->hw_mgr_mutex);
+		CAM_ERR(CAM_LRME, "No device is registered");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < hw_mgr->device_count && i < CAM_LRME_HW_MAX; i++) {
+		hw_device = &hw_mgr->hw_device[i];
+		if (!hw_device->num_context) {
+			index = i;
+			break;
+		}
+		if (hw_device->num_context < min_ctx) {
+			min_ctx = hw_device->num_context;
+			index = i;
+		}
+	}
+
+	hw_device = &hw_mgr->hw_device[index];
+	hw_device->num_context++;
+
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+	CAM_DBG(CAM_LRME, "reserve device index %d", index);
+
+	return index;
+}
+
+static int cam_lrme_mgr_util_get_device(struct cam_lrme_hw_mgr *hw_mgr,
+	uint32_t device_index, struct cam_lrme_device **hw_device)
+{
+	if (!hw_mgr) {
+		CAM_ERR(CAM_LRME, "invalid params hw_mgr %pK", hw_mgr);
+		return -EINVAL;
+	}
+
+	if (device_index >= CAM_LRME_HW_MAX) {
+		CAM_ERR(CAM_LRME, "Wrong device index %d", device_index);
+		return -EINVAL;
+	}
+
+	*hw_device = &hw_mgr->hw_device[device_index];
+
+	return 0;
+}
+
+static int cam_lrme_mgr_util_packet_validate(struct cam_packet *packet)
+{
+	struct cam_cmd_buf_desc *cmd_desc = NULL;
+	int i, rc;
+
+	if (!packet) {
+		CAM_ERR(CAM_LRME, "Invalid args");
+		return -EINVAL;
+	}
+
+	CAM_DBG(CAM_LRME, "Packet request=%d, op_code=0x%x, size=%d, flags=%d",
+		packet->header.request_id, packet->header.op_code,
+		packet->header.size, packet->header.flags);
+	CAM_DBG(CAM_LRME,
+		"Packet cmdbuf(offset=%d, num=%d) io(offset=%d, num=%d)",
+		packet->cmd_buf_offset, packet->num_cmd_buf,
+		packet->io_configs_offset, packet->num_io_configs);
+	CAM_DBG(CAM_LRME,
+		"Packet Patch(offset=%d, num=%d) kmd(offset=%d, num=%d)",
+		packet->patch_offset, packet->num_patches,
+		packet->kmd_cmd_buf_offset, packet->kmd_cmd_buf_index);
+
+	if (cam_packet_util_validate_packet(packet)) {
+		CAM_ERR(CAM_LRME, "invalid packet:%d %d %d %d %d",
+			packet->kmd_cmd_buf_index,
+			packet->num_cmd_buf, packet->cmd_buf_offset,
+			packet->io_configs_offset, packet->header.size);
+		return -EINVAL;
+	}
+
+	if (!packet->num_io_configs) {
+		CAM_ERR(CAM_LRME, "no io configs");
+		return -EINVAL;
+	}
+
+	cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *)&packet->payload +
+		packet->cmd_buf_offset);
+
+	for (i = 0; i < packet->num_cmd_buf; i++) {
+		if (!cmd_desc[i].length)
+			continue;
+
+		CAM_DBG(CAM_LRME,
+			"CmdBuf[%d] hdl=%d, offset=%d, size=%d, len=%d, type=%d, meta_data=%d",
+			i,
+			cmd_desc[i].mem_handle, cmd_desc[i].offset,
+			cmd_desc[i].size, cmd_desc[i].length, cmd_desc[i].type,
+			cmd_desc[i].meta_data);
+
+		rc = cam_packet_util_validate_cmd_desc(&cmd_desc[i]);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Invalid cmd buffer %d", i);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int cam_lrme_mgr_util_prepare_io_buffer(int32_t iommu_hdl,
+	struct cam_hw_prepare_update_args *prepare,
+	struct cam_lrme_hw_io_buffer *input_buf,
+	struct cam_lrme_hw_io_buffer *output_buf, uint32_t io_buf_size)
+{
+	int rc = -EINVAL;
+	uint32_t num_in_buf, num_out_buf, i, j, plane;
+	struct cam_buf_io_cfg *io_cfg;
+	uint64_t io_addr[CAM_PACKET_MAX_PLANES];
+	size_t size;
+
+	num_in_buf = 0;
+	num_out_buf = 0;
+	io_cfg = (struct cam_buf_io_cfg *)((uint8_t *)
+		 &prepare->packet->payload +
+		 prepare->packet->io_configs_offset);
+
+	for (i = 0; i < prepare->packet->num_io_configs; i++) {
+		CAM_DBG(CAM_LRME,
+			"IOConfig[%d] : handle[%d] Dir[%d] Res[%d] Fence[%d], Format[%d]",
+			i, io_cfg[i].mem_handle[0], io_cfg[i].direction,
+			io_cfg[i].resource_type,
+			io_cfg[i].fence, io_cfg[i].format);
+
+		if ((num_in_buf > io_buf_size) ||
+			(num_out_buf > io_buf_size)) {
+			CAM_ERR(CAM_LRME, "Invalid number of buffers %d %d %d",
+				num_in_buf, num_out_buf, io_buf_size);
+			return -EINVAL;
+		}
+
+		memset(io_addr, 0, sizeof(io_addr));
+		for (plane = 0; plane < CAM_PACKET_MAX_PLANES; plane++) {
+			if (!io_cfg[i].mem_handle[plane])
+				break;
+
+			rc = cam_mem_get_io_buf(io_cfg[i].mem_handle[plane],
+				iommu_hdl, &io_addr[plane], &size);
+			if (rc) {
+				CAM_ERR(CAM_LRME, "Cannot get io buf for %d %d",
+					plane, rc);
+				return -ENOMEM;
+			}
+
+			io_addr[plane] += io_cfg[i].offsets[plane];
+
+			if (io_addr[plane] >> 32) {
+				CAM_ERR(CAM_LRME, "Invalid io addr for %d %d",
+					plane, rc);
+				return -ENOMEM;
+			}
+
+			CAM_DBG(CAM_LRME, "IO Address[%d][%d] : %llu",
+				io_cfg[i].direction, plane, io_addr[plane]);
+		}
+
+		switch (io_cfg[i].direction) {
+		case CAM_BUF_INPUT: {
+			prepare->in_map_entries[num_in_buf].resource_handle =
+				io_cfg[i].resource_type;
+			prepare->in_map_entries[num_in_buf].sync_id =
+				io_cfg[i].fence;
+
+			input_buf[num_in_buf].valid = true;
+			for (j = 0; j < plane; j++)
+				input_buf[num_in_buf].io_addr[j] = io_addr[j];
+			input_buf[num_in_buf].num_plane = plane;
+			input_buf[num_in_buf].io_cfg = &io_cfg[i];
+
+			num_in_buf++;
+			break;
+		}
+		case CAM_BUF_OUTPUT: {
+			prepare->out_map_entries[num_out_buf].resource_handle =
+				io_cfg[i].resource_type;
+			prepare->out_map_entries[num_out_buf].sync_id =
+				io_cfg[i].fence;
+
+			output_buf[num_out_buf].valid = true;
+			for (j = 0; j < plane; j++)
+				output_buf[num_out_buf].io_addr[j] = io_addr[j];
+			output_buf[num_out_buf].num_plane = plane;
+			output_buf[num_out_buf].io_cfg = &io_cfg[i];
+
+			num_out_buf++;
+			break;
+		}
+		default:
+			CAM_ERR(CAM_LRME, "Unsupported io direction %d",
+				io_cfg[i].direction);
+			return -EINVAL;
+		}
+	}
+	prepare->num_in_map_entries = num_in_buf;
+	prepare->num_out_map_entries = num_out_buf;
+
+	return 0;
+}
+
+static int cam_lrme_mgr_util_prepare_hw_update_entries(
+	struct cam_lrme_hw_mgr *hw_mgr,
+	struct cam_hw_prepare_update_args *prepare,
+	struct cam_lrme_hw_cmd_config_args *config_args,
+	struct cam_kmd_buf_info *kmd_buf_info)
+{
+	int i, rc = 0;
+	struct cam_lrme_device *hw_device = NULL;
+	uint32_t *kmd_buf_addr;
+	uint32_t num_entry;
+	uint32_t kmd_buf_max_size;
+	uint32_t kmd_buf_used_bytes = 0;
+	struct cam_hw_update_entry *hw_entry;
+	struct cam_cmd_buf_desc *cmd_desc = NULL;
+
+	hw_device = config_args->hw_device;
+	if (!hw_device) {
+		CAM_ERR(CAM_LRME, "Invalid hw_device");
+		return -EINVAL;
+	}
+
+	kmd_buf_addr = (uint32_t *)((uint8_t *)kmd_buf_info->cpu_addr +
+		kmd_buf_info->used_bytes);
+	kmd_buf_max_size = kmd_buf_info->size - kmd_buf_info->used_bytes;
+
+	config_args->cmd_buf_addr = kmd_buf_addr;
+	config_args->size = kmd_buf_max_size;
+	config_args->config_buf_size = 0;
+
+	if (hw_device->hw_intf.hw_ops.process_cmd) {
+		rc = hw_device->hw_intf.hw_ops.process_cmd(
+			hw_device->hw_intf.hw_priv,
+			CAM_LRME_HW_CMD_PREPARE_HW_UPDATE,
+			config_args,
+			sizeof(struct cam_lrme_hw_cmd_config_args));
+		if (rc) {
+			CAM_ERR(CAM_LRME,
+				"Failed in CMD_PREPARE_HW_UPDATE %d", rc);
+			return rc;
+		}
+	} else {
+		CAM_ERR(CAM_LRME, "Can't find handle function");
+		return -EINVAL;
+	}
+
+	kmd_buf_used_bytes += config_args->config_buf_size;
+
+	if (!kmd_buf_used_bytes || (kmd_buf_used_bytes > kmd_buf_max_size)) {
+		CAM_ERR(CAM_LRME, "Invalid kmd used bytes %d (%d)",
+			kmd_buf_used_bytes, kmd_buf_max_size);
+		return -ENOMEM;
+	}
+
+	hw_entry = prepare->hw_update_entries;
+	num_entry = 0;
+
+	if (config_args->config_buf_size) {
+		if ((num_entry + 1) >= prepare->max_hw_update_entries) {
+			CAM_ERR(CAM_LRME, "Insufficient  HW entries :%d %d",
+				num_entry, prepare->max_hw_update_entries);
+			return -EINVAL;
+		}
+
+		hw_entry[num_entry].handle = kmd_buf_info->handle;
+		hw_entry[num_entry].len = config_args->config_buf_size;
+		hw_entry[num_entry].offset = kmd_buf_info->offset;
+
+		kmd_buf_info->used_bytes += config_args->config_buf_size;
+		kmd_buf_info->offset += config_args->config_buf_size;
+		num_entry++;
+	}
+
+	cmd_desc = (struct cam_cmd_buf_desc *)((uint8_t *)
+		&prepare->packet->payload + prepare->packet->cmd_buf_offset);
+
+	for (i = 0; i < prepare->packet->num_cmd_buf; i++) {
+		if (!cmd_desc[i].length)
+			continue;
+
+		if ((num_entry + 1) >= prepare->max_hw_update_entries) {
+			CAM_ERR(CAM_LRME, "Exceed max num of entry");
+			return -EINVAL;
+		}
+
+		hw_entry[num_entry].handle = cmd_desc[i].mem_handle;
+		hw_entry[num_entry].len = cmd_desc[i].length;
+		hw_entry[num_entry].offset = cmd_desc[i].offset;
+		num_entry++;
+	}
+	prepare->num_hw_update_entries = num_entry;
+
+	CAM_DBG(CAM_LRME, "FinalConfig : hw_entries=%d, Sync(in=%d, out=%d)",
+		prepare->num_hw_update_entries, prepare->num_in_map_entries,
+		prepare->num_out_map_entries);
+
+	return rc;
+}
+
+static void cam_lrme_mgr_util_put_frame_req(
+	struct list_head *src_list,
+	struct list_head *list,
+	spinlock_t *lock)
+{
+	spin_lock(lock);
+	list_add_tail(list, src_list);
+	spin_unlock(lock);
+}
+
+static int cam_lrme_mgr_util_get_frame_req(
+	struct list_head *src_list,
+	struct cam_lrme_frame_request **frame_req,
+	spinlock_t *lock)
+{
+	int rc = 0;
+	struct cam_lrme_frame_request *req_ptr = NULL;
+
+	spin_lock(lock);
+	if (!list_empty(src_list)) {
+		req_ptr = list_first_entry(src_list,
+			struct cam_lrme_frame_request, frame_list);
+		list_del_init(&req_ptr->frame_list);
+	} else {
+		rc = -ENOENT;
+	}
+	*frame_req = req_ptr;
+	spin_unlock(lock);
+
+	return rc;
+}
+
+
+static int cam_lrme_mgr_util_submit_req(void *priv, void *data)
+{
+	struct cam_lrme_device *hw_device;
+	struct cam_lrme_hw_mgr *hw_mgr;
+	struct cam_lrme_frame_request *frame_req = NULL;
+	struct cam_lrme_hw_submit_args submit_args;
+	struct cam_lrme_mgr_work_data *work_data;
+	int rc;
+	int req_prio = 0;
+
+	if (!priv) {
+		CAM_ERR(CAM_LRME, "worker doesn't have private data");
+		return -EINVAL;
+	}
+
+	hw_mgr = (struct cam_lrme_hw_mgr *)priv;
+	work_data = (struct cam_lrme_mgr_work_data *)data;
+	hw_device = work_data->hw_device;
+
+	rc = cam_lrme_mgr_util_get_frame_req(&hw_device->
+		frame_pending_list_high, &frame_req, &hw_device->high_req_lock);
+
+	if (!frame_req) {
+		rc = cam_lrme_mgr_util_get_frame_req(&hw_device->
+				frame_pending_list_normal, &frame_req,
+				&hw_device->normal_req_lock);
+		if (frame_req)
+			req_prio = 1;
+	}
+
+	if (!frame_req) {
+		CAM_DBG(CAM_LRME, "No pending request");
+		return 0;
+	}
+
+	if (hw_device->hw_intf.hw_ops.process_cmd) {
+		submit_args.hw_update_entries = frame_req->hw_update_entries;
+		submit_args.num_hw_update_entries =
+			frame_req->num_hw_update_entries;
+		submit_args.frame_req = frame_req;
+
+		rc = hw_device->hw_intf.hw_ops.process_cmd(
+			hw_device->hw_intf.hw_priv,
+			CAM_LRME_HW_CMD_SUBMIT,
+			&submit_args, sizeof(struct cam_lrme_hw_submit_args));
+
+		if (rc == -EBUSY)
+			CAM_DBG(CAM_LRME, "device busy");
+		else if (rc)
+			CAM_ERR(CAM_LRME, "submit request failed rc %d", rc);
+		if (rc) {
+			req_prio == 0 ? spin_lock(&hw_device->high_req_lock) :
+				spin_lock(&hw_device->normal_req_lock);
+			list_add(&frame_req->frame_list,
+				(req_prio == 0 ?
+				 &hw_device->frame_pending_list_high :
+				 &hw_device->frame_pending_list_normal));
+			req_prio == 0 ? spin_unlock(&hw_device->high_req_lock) :
+				spin_unlock(&hw_device->normal_req_lock);
+		}
+		if (rc == -EBUSY)
+			rc = 0;
+	} else {
+		req_prio == 0 ? spin_lock(&hw_device->high_req_lock) :
+			spin_lock(&hw_device->normal_req_lock);
+		list_add(&frame_req->frame_list,
+			(req_prio == 0 ?
+			 &hw_device->frame_pending_list_high :
+			 &hw_device->frame_pending_list_normal));
+		req_prio == 0 ? spin_unlock(&hw_device->high_req_lock) :
+			spin_unlock(&hw_device->normal_req_lock);
+		rc = -EINVAL;
+	}
+
+	CAM_DBG(CAM_LRME, "End of submit, rc %d", rc);
+
+	return rc;
+}
+
+static int cam_lrme_mgr_util_schedule_frame_req(
+	struct cam_lrme_hw_mgr *hw_mgr, struct cam_lrme_device *hw_device)
+{
+	int rc = 0;
+	struct crm_workq_task *task;
+	struct cam_lrme_mgr_work_data *work_data;
+
+	task = cam_req_mgr_workq_get_task(hw_device->work);
+	if (!task) {
+		CAM_ERR(CAM_LRME, "Can not get task for worker");
+		return -ENOMEM;
+	}
+
+	work_data = (struct cam_lrme_mgr_work_data *)task->payload;
+	work_data->hw_device = hw_device;
+
+	task->process_cb = cam_lrme_mgr_util_submit_req;
+	CAM_DBG(CAM_LRME, "enqueue submit task");
+	rc = cam_req_mgr_workq_enqueue_task(task, hw_mgr, CRM_TASK_PRIORITY_0);
+
+	return rc;
+}
+
+static int cam_lrme_mgr_util_release(struct cam_lrme_hw_mgr *hw_mgr,
+	uint32_t device_index)
+{
+	int rc = 0;
+	struct cam_lrme_device *hw_device;
+
+	rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in getting device %d", rc);
+		return rc;
+	}
+
+	mutex_lock(&hw_mgr->hw_mgr_mutex);
+	hw_device->num_context--;
+	mutex_unlock(&hw_mgr->hw_mgr_mutex);
+
+	return rc;
+}
+
+static int cam_lrme_mgr_cb(void *data,
+	struct cam_lrme_hw_cb_args *cb_args)
+{
+	struct cam_lrme_hw_mgr *hw_mgr = &g_lrme_hw_mgr;
+	int rc = 0;
+	bool frame_abort = true;
+	struct cam_lrme_frame_request *frame_req;
+	struct cam_lrme_device *hw_device;
+
+	if (!data || !cb_args) {
+		CAM_ERR(CAM_LRME, "Invalid input args");
+		return -EINVAL;
+	}
+
+	hw_device = (struct cam_lrme_device *)data;
+	frame_req = cb_args->frame_req;
+
+	if (cb_args->cb_type & CAM_LRME_CB_PUT_FRAME) {
+		memset(frame_req, 0x0, sizeof(*frame_req));
+		INIT_LIST_HEAD(&frame_req->frame_list);
+		cam_lrme_mgr_util_put_frame_req(&hw_mgr->frame_free_list,
+				&frame_req->frame_list,
+				&hw_mgr->free_req_lock);
+		cb_args->cb_type &= ~CAM_LRME_CB_PUT_FRAME;
+		frame_req = NULL;
+	}
+
+	if (cb_args->cb_type & CAM_LRME_CB_COMP_REG_UPDATE) {
+		cb_args->cb_type &= ~CAM_LRME_CB_COMP_REG_UPDATE;
+		CAM_DBG(CAM_LRME, "Reg update");
+	}
+
+	if (!frame_req)
+		return rc;
+
+	if (cb_args->cb_type & CAM_LRME_CB_BUF_DONE) {
+		cb_args->cb_type &= ~CAM_LRME_CB_BUF_DONE;
+		frame_abort = false;
+	} else if (cb_args->cb_type & CAM_LRME_CB_ERROR) {
+		cb_args->cb_type &= ~CAM_LRME_CB_ERROR;
+		frame_abort = true;
+	} else {
+		CAM_ERR(CAM_LRME, "Wrong cb type %d, req %lld",
+			cb_args->cb_type, frame_req->req_id);
+		return -EINVAL;
+	}
+
+	if (hw_mgr->event_cb) {
+		struct cam_hw_done_event_data buf_data;
+
+		buf_data.request_id = frame_req->req_id;
+		CAM_DBG(CAM_LRME, "frame req %llu, frame_abort %d",
+			frame_req->req_id, frame_abort);
+		rc = hw_mgr->event_cb(frame_req->ctxt_to_hw_map,
+			frame_abort, &buf_data);
+	} else {
+		CAM_ERR(CAM_LRME, "No cb function");
+	}
+	memset(frame_req, 0x0, sizeof(*frame_req));
+	INIT_LIST_HEAD(&frame_req->frame_list);
+	cam_lrme_mgr_util_put_frame_req(&hw_mgr->frame_free_list,
+				&frame_req->frame_list,
+				&hw_mgr->free_req_lock);
+
+	rc = cam_lrme_mgr_util_schedule_frame_req(hw_mgr, hw_device);
+
+	return rc;
+}
+
+static int cam_lrme_mgr_get_caps(void *hw_mgr_priv, void *hw_get_caps_args)
+{
+	int rc = 0;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_query_cap_cmd *args = hw_get_caps_args;
+
+	if (sizeof(struct cam_lrme_query_cap_cmd) != args->size) {
+		CAM_ERR(CAM_LRME,
+			"sizeof(struct cam_query_cap_cmd) = %lu, args->size = %d",
+			sizeof(struct cam_query_cap_cmd), args->size);
+		return -EFAULT;
+	}
+
+	if (copy_to_user((void __user *)args->caps_handle, &(hw_mgr->lrme_caps),
+		sizeof(struct cam_lrme_query_cap_cmd))) {
+		CAM_ERR(CAM_LRME, "copy to user failed");
+		return -EFAULT;
+	}
+
+	return rc;
+}
+
+static int cam_lrme_mgr_hw_acquire(void *hw_mgr_priv, void *hw_acquire_args)
+{
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_acquire_args *args =
+		(struct cam_hw_acquire_args *)hw_acquire_args;
+	struct cam_lrme_acquire_args lrme_acquire_args;
+	uint64_t device_index;
+
+	if (!hw_mgr_priv || !args) {
+		CAM_ERR(CAM_LRME,
+		"Invalid input params hw_mgr_priv %pK, acquire_args %pK",
+		hw_mgr_priv, args);
+		return -EINVAL;
+	}
+
+	if (copy_from_user(&lrme_acquire_args,
+		(void __user *)args->acquire_info,
+		sizeof(struct cam_lrme_acquire_args))) {
+		CAM_ERR(CAM_LRME, "Failed to copy acquire args from user");
+		return -EFAULT;
+	}
+
+	device_index = cam_lrme_mgr_util_reserve_device(hw_mgr,
+		&lrme_acquire_args);
+	CAM_DBG(CAM_LRME, "Get device id %llu", device_index);
+
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Get wrong device id %llu", device_index);
+		return -EINVAL;
+	}
+
+	/* device_index is the right 4 bit in ctxt_to_hw_map */
+	args->ctxt_to_hw_map = (void *)device_index;
+
+	return 0;
+}
+
+static int cam_lrme_mgr_hw_release(void *hw_mgr_priv, void *hw_release_args)
+{
+	int rc = 0;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_release_args *args =
+		(struct cam_hw_release_args *)hw_release_args;
+	uint64_t device_index;
+
+	if (!hw_mgr_priv || !hw_release_args) {
+		CAM_ERR(CAM_LRME, "Invalid arguments %pK, %pK",
+			hw_mgr_priv, hw_release_args);
+		return -EINVAL;
+	}
+
+	device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map);
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Invalid device index %llu", device_index);
+		return -EPERM;
+	}
+
+	rc = cam_lrme_mgr_util_release(hw_mgr, device_index);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Failed in release device, rc=%d", rc);
+
+	return rc;
+}
+
+static int cam_lrme_mgr_hw_start(void *hw_mgr_priv, void *hw_start_args)
+{
+	int rc = 0;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_start_args *args =
+		(struct cam_hw_start_args *)hw_start_args;
+	struct cam_lrme_device *hw_device;
+	uint32_t device_index;
+
+	if (!hw_mgr || !args) {
+		CAM_ERR(CAM_LRME, "Invald input params");
+		return -EINVAL;
+	}
+
+	device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map);
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Invalid device index %d", device_index);
+		return -EPERM;
+	}
+
+	CAM_DBG(CAM_LRME, "Start device index %d", device_index);
+
+	rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to get hw device");
+		return rc;
+	}
+
+	if (hw_device->hw_intf.hw_ops.start) {
+		rc = hw_device->hw_intf.hw_ops.start(
+			hw_device->hw_intf.hw_priv, NULL, 0);
+	} else {
+		CAM_ERR(CAM_LRME, "Invald start function");
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int cam_lrme_mgr_hw_stop(void *hw_mgr_priv, void *stop_args)
+{
+	int rc = 0;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_stop_args *args =
+		(struct cam_hw_stop_args *)stop_args;
+	struct cam_lrme_device *hw_device;
+	uint32_t device_index;
+
+	if (!hw_mgr_priv || !stop_args) {
+		CAM_ERR(CAM_LRME, "Invalid arguments");
+		return -EINVAL;
+	}
+
+	device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map);
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Invalid device index %d", device_index);
+		return -EPERM;
+	}
+
+	CAM_DBG(CAM_LRME, "Stop device index %d", device_index);
+
+	rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to get hw device");
+		return rc;
+	}
+
+	if (hw_device->hw_intf.hw_ops.stop) {
+		rc = hw_device->hw_intf.hw_ops.stop(
+			hw_device->hw_intf.hw_priv, NULL, 0);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Failed in HW stop %d", rc);
+			goto end;
+		}
+	}
+
+end:
+	return rc;
+}
+
+static int cam_lrme_mgr_hw_prepare_update(void *hw_mgr_priv,
+	void *hw_prepare_update_args)
+{
+	int rc = 0, i;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_prepare_update_args *args =
+		(struct cam_hw_prepare_update_args *)hw_prepare_update_args;
+	struct cam_lrme_device *hw_device;
+	struct cam_kmd_buf_info kmd_buf;
+	struct cam_lrme_hw_cmd_config_args config_args;
+	struct cam_lrme_frame_request *frame_req = NULL;
+	uint32_t device_index;
+
+	if (!hw_mgr_priv || !hw_prepare_update_args) {
+		CAM_ERR(CAM_LRME, "Invalid args %pK %pK",
+			hw_mgr_priv, hw_prepare_update_args);
+		return -EINVAL;
+	}
+
+	device_index = CAM_LRME_DECODE_DEVICE_INDEX(args->ctxt_to_hw_map);
+	if (device_index >= hw_mgr->device_count) {
+		CAM_ERR(CAM_LRME, "Invalid device index %d", device_index);
+		return -EPERM;
+	}
+
+	rc = cam_lrme_mgr_util_get_device(hw_mgr, device_index, &hw_device);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in getting device %d", rc);
+		goto error;
+	}
+
+	rc = cam_lrme_mgr_util_packet_validate(args->packet);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in packet validation %d", rc);
+		goto error;
+	}
+
+	rc = cam_packet_util_get_kmd_buffer(args->packet, &kmd_buf);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in get kmd buf buffer %d", rc);
+		goto error;
+	}
+
+	CAM_DBG(CAM_LRME,
+		"KMD Buf : hdl=%d, cpu_addr=%pK, offset=%d, size=%d, used=%d",
+		kmd_buf.handle, kmd_buf.cpu_addr, kmd_buf.offset,
+		kmd_buf.size, kmd_buf.used_bytes);
+
+	rc = cam_packet_util_process_patches(args->packet,
+		hw_mgr->device_iommu.non_secure, hw_mgr->device_iommu.secure);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Patch packet failed, rc=%d", rc);
+		return rc;
+	}
+
+	memset(&config_args, 0, sizeof(config_args));
+	config_args.hw_device = hw_device;
+
+	rc = cam_lrme_mgr_util_prepare_io_buffer(
+		hw_mgr->device_iommu.non_secure, args,
+		config_args.input_buf, config_args.output_buf,
+		CAM_LRME_MAX_IO_BUFFER);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in prepare IO Buf %d", rc);
+		goto error;
+	}
+	/* Check port number */
+	if (args->num_in_map_entries == 0 || args->num_out_map_entries == 0) {
+		CAM_ERR(CAM_LRME, "Error in port number in %d, out %d",
+			args->num_in_map_entries, args->num_out_map_entries);
+		goto error;
+	}
+
+	rc = cam_lrme_mgr_util_prepare_hw_update_entries(hw_mgr, args,
+		&config_args, &kmd_buf);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Error in hw update entries %d", rc);
+		goto error;
+	}
+
+	rc = cam_lrme_mgr_util_get_frame_req(&hw_mgr->frame_free_list,
+		&frame_req, &hw_mgr->free_req_lock);
+	if (rc || !frame_req) {
+		CAM_ERR(CAM_LRME, "Can not get free frame request");
+		goto error;
+	}
+
+	frame_req->ctxt_to_hw_map = args->ctxt_to_hw_map;
+	frame_req->req_id = args->packet->header.request_id;
+	frame_req->hw_device = hw_device;
+	frame_req->num_hw_update_entries = args->num_hw_update_entries;
+	for (i = 0; i < args->num_hw_update_entries; i++)
+		frame_req->hw_update_entries[i] = args->hw_update_entries[i];
+
+	args->priv = frame_req;
+
+	CAM_DBG(CAM_LRME, "FramePrepare : Frame[%lld]", frame_req->req_id);
+
+	return 0;
+
+error:
+	return rc;
+}
+
+static int cam_lrme_mgr_hw_config(void *hw_mgr_priv,
+	void *hw_config_args)
+{
+	int rc = 0;
+	struct cam_lrme_hw_mgr *hw_mgr = hw_mgr_priv;
+	struct cam_hw_config_args *args =
+		(struct cam_hw_config_args *)hw_config_args;
+	struct cam_lrme_frame_request *frame_req;
+	struct cam_lrme_device *hw_device = NULL;
+	enum cam_lrme_hw_mgr_ctx_priority priority;
+
+	if (!hw_mgr_priv || !hw_config_args) {
+		CAM_ERR(CAM_LRME, "Invalid arguments, hw_mgr %pK, config %pK",
+			hw_mgr_priv, hw_config_args);
+		return -EINVAL;
+	}
+
+	if (!args->num_hw_update_entries) {
+		CAM_ERR(CAM_LRME, "No hw update entries");
+		return -EINVAL;
+	}
+
+	frame_req = (struct cam_lrme_frame_request *)args->priv;
+	if (!frame_req) {
+		CAM_ERR(CAM_LRME, "No frame request");
+		return -EINVAL;
+	}
+
+	hw_device = frame_req->hw_device;
+	if (!hw_device)
+		return -EINVAL;
+
+	priority = CAM_LRME_DECODE_PRIORITY(args->ctxt_to_hw_map);
+	if (priority == CAM_LRME_PRIORITY_HIGH) {
+		cam_lrme_mgr_util_put_frame_req(
+			&hw_device->frame_pending_list_high,
+			&frame_req->frame_list, &hw_device->high_req_lock);
+	} else {
+		cam_lrme_mgr_util_put_frame_req(
+			&hw_device->frame_pending_list_normal,
+			&frame_req->frame_list, &hw_device->normal_req_lock);
+	}
+
+	CAM_DBG(CAM_LRME, "schedule req %llu", frame_req->req_id);
+	rc = cam_lrme_mgr_util_schedule_frame_req(hw_mgr, hw_device);
+
+	return rc;
+}
+
+int cam_lrme_mgr_register_device(
+	struct cam_hw_intf *lrme_hw_intf,
+	struct cam_iommu_handle *device_iommu,
+	struct cam_iommu_handle *cdm_iommu)
+{
+	struct cam_lrme_device *hw_device;
+	char buf[128];
+	int i, rc;
+
+	hw_device = &g_lrme_hw_mgr.hw_device[lrme_hw_intf->hw_idx];
+
+	g_lrme_hw_mgr.device_iommu = *device_iommu;
+	g_lrme_hw_mgr.cdm_iommu = *cdm_iommu;
+
+	memcpy(&hw_device->hw_intf, lrme_hw_intf, sizeof(struct cam_hw_intf));
+
+	spin_lock_init(&hw_device->high_req_lock);
+	spin_lock_init(&hw_device->normal_req_lock);
+	INIT_LIST_HEAD(&hw_device->frame_pending_list_high);
+	INIT_LIST_HEAD(&hw_device->frame_pending_list_normal);
+
+	rc = snprintf(buf, sizeof(buf), "cam_lrme_device_submit_worker%d",
+		lrme_hw_intf->hw_idx);
+	CAM_DBG(CAM_LRME, "Create submit workq for %s", buf);
+	rc = cam_req_mgr_workq_create(buf,
+		CAM_LRME_WORKQ_NUM_TASK,
+		&hw_device->work, CRM_WORKQ_USAGE_NON_IRQ);
+	if (rc) {
+		CAM_ERR(CAM_LRME,
+			"Unable to create a worker, rc=%d", rc);
+		return rc;
+	}
+
+	for (i = 0; i < CAM_LRME_WORKQ_NUM_TASK; i++)
+		hw_device->work->task.pool[i].payload =
+			&hw_device->work_data[i];
+
+	if (hw_device->hw_intf.hw_ops.process_cmd) {
+		struct cam_lrme_hw_cmd_set_cb cb_args;
+
+		cb_args.cam_lrme_hw_mgr_cb = cam_lrme_mgr_cb;
+		cb_args.data = hw_device;
+
+		rc = hw_device->hw_intf.hw_ops.process_cmd(
+			hw_device->hw_intf.hw_priv,
+			CAM_LRME_HW_CMD_REGISTER_CB,
+			&cb_args, sizeof(cb_args));
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Register cb failed");
+			goto destroy_workqueue;
+		}
+		CAM_DBG(CAM_LRME, "cb registered");
+	}
+
+	if (hw_device->hw_intf.hw_ops.get_hw_caps) {
+		rc = hw_device->hw_intf.hw_ops.get_hw_caps(
+			hw_device->hw_intf.hw_priv, &hw_device->hw_caps,
+			sizeof(hw_device->hw_caps));
+		if (rc)
+			CAM_ERR(CAM_LRME, "Get caps failed");
+	} else {
+		CAM_ERR(CAM_LRME, "No get_hw_caps function");
+		goto destroy_workqueue;
+	}
+	g_lrme_hw_mgr.lrme_caps.dev_caps[lrme_hw_intf->hw_idx] =
+		hw_device->hw_caps;
+	g_lrme_hw_mgr.device_count++;
+	g_lrme_hw_mgr.lrme_caps.device_iommu = g_lrme_hw_mgr.device_iommu;
+	g_lrme_hw_mgr.lrme_caps.cdm_iommu = g_lrme_hw_mgr.cdm_iommu;
+	g_lrme_hw_mgr.lrme_caps.num_devices = g_lrme_hw_mgr.device_count;
+
+	hw_device->valid = true;
+
+	CAM_DBG(CAM_LRME, "device registration done");
+	return 0;
+
+destroy_workqueue:
+	cam_req_mgr_workq_destroy(&hw_device->work);
+
+	return rc;
+}
+
+int cam_lrme_mgr_deregister_device(int device_index)
+{
+	struct cam_lrme_device *hw_device;
+
+	hw_device = &g_lrme_hw_mgr.hw_device[device_index];
+	cam_req_mgr_workq_destroy(&hw_device->work);
+	memset(hw_device, 0x0, sizeof(struct cam_lrme_device));
+	g_lrme_hw_mgr.device_count--;
+
+	return 0;
+}
+
+int cam_lrme_hw_mgr_deinit(void)
+{
+	mutex_destroy(&g_lrme_hw_mgr.hw_mgr_mutex);
+	memset(&g_lrme_hw_mgr, 0x0, sizeof(g_lrme_hw_mgr));
+
+	return 0;
+}
+
+int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf,
+	cam_hw_event_cb_func cam_lrme_dev_buf_done_cb)
+{
+	int i, rc = 0;
+	struct cam_lrme_frame_request *frame_req;
+
+	if (!hw_mgr_intf)
+		return -EINVAL;
+
+	CAM_DBG(CAM_LRME, "device count %d", g_lrme_hw_mgr.device_count);
+	if (g_lrme_hw_mgr.device_count > CAM_LRME_HW_MAX) {
+		CAM_ERR(CAM_LRME, "Invalid count of devices");
+		return -EINVAL;
+	}
+
+	memset(hw_mgr_intf, 0, sizeof(*hw_mgr_intf));
+
+	mutex_init(&g_lrme_hw_mgr.hw_mgr_mutex);
+	spin_lock_init(&g_lrme_hw_mgr.free_req_lock);
+	INIT_LIST_HEAD(&g_lrme_hw_mgr.frame_free_list);
+
+	/* Init hw mgr frame requests and add to free list */
+	for (i = 0; i < CAM_CTX_REQ_MAX * CAM_CTX_MAX; i++) {
+		frame_req = &g_lrme_hw_mgr.frame_req[i];
+
+		memset(frame_req, 0x0, sizeof(*frame_req));
+		INIT_LIST_HEAD(&frame_req->frame_list);
+
+		list_add_tail(&frame_req->frame_list,
+			&g_lrme_hw_mgr.frame_free_list);
+	}
+
+	hw_mgr_intf->hw_mgr_priv = &g_lrme_hw_mgr;
+	hw_mgr_intf->hw_get_caps = cam_lrme_mgr_get_caps;
+	hw_mgr_intf->hw_acquire = cam_lrme_mgr_hw_acquire;
+	hw_mgr_intf->hw_release = cam_lrme_mgr_hw_release;
+	hw_mgr_intf->hw_start = cam_lrme_mgr_hw_start;
+	hw_mgr_intf->hw_stop = cam_lrme_mgr_hw_stop;
+	hw_mgr_intf->hw_prepare_update = cam_lrme_mgr_hw_prepare_update;
+	hw_mgr_intf->hw_config = cam_lrme_mgr_hw_config;
+	hw_mgr_intf->hw_read = NULL;
+	hw_mgr_intf->hw_write = NULL;
+	hw_mgr_intf->hw_close = NULL;
+
+	g_lrme_hw_mgr.event_cb = cam_lrme_dev_buf_done_cb;
+
+	CAM_DBG(CAM_LRME, "Hw mgr init done");
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h
new file mode 100644
index 0000000..f7ce4d2
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr.h
@@ -0,0 +1,120 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_HW_MGR_H_
+#define _CAM_LRME_HW_MGR_H_
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <media/cam_lrme.h>
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_cpas_api.h"
+#include "cam_debug_util.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_req_mgr_workq.h"
+#include "cam_lrme_hw_intf.h"
+#include "cam_context.h"
+
+#define CAM_LRME_HW_MAX 1
+#define CAM_LRME_WORKQ_NUM_TASK 10
+
+#define CAM_LRME_DECODE_DEVICE_INDEX(ctxt_to_hw_map) \
+	((uint64_t)ctxt_to_hw_map & 0xF)
+
+#define CAM_LRME_DECODE_PRIORITY(ctxt_to_hw_map) \
+	(((uint64_t)ctxt_to_hw_map & 0xF0) >> 4)
+
+#define CAM_LRME_DECODE_CTX_INDEX(ctxt_to_hw_map) \
+	((uint64_t)ctxt_to_hw_map >> CAM_LRME_CTX_INDEX_SHIFT)
+
+/**
+ * enum cam_lrme_hw_mgr_ctx_priority
+ *
+ * CAM_LRME_PRIORITY_HIGH   : High priority client
+ * CAM_LRME_PRIORITY_NORMAL : Normal priority client
+ */
+enum cam_lrme_hw_mgr_ctx_priority {
+	CAM_LRME_PRIORITY_HIGH,
+	CAM_LRME_PRIORITY_NORMAL,
+};
+
+/**
+ * struct cam_lrme_mgr_work_data : HW Mgr work data
+ *
+ * hw_device : Pointer to the hw device
+ */
+struct cam_lrme_mgr_work_data {
+	struct cam_lrme_device *hw_device;
+};
+
+/**
+ * struct cam_lrme_device     : LRME HW device
+ *
+ * @hw_caps                   : HW device's capabilities
+ * @hw_intf                   : HW device's interface information
+ * @num_context               : Number of contexts using this device
+ * @valid                     : Whether this device is valid
+ * @work                      : HW device's work queue
+ * @work_data                 : HW device's work data
+ * @frame_pending_list_high   : High priority request queue
+ * @frame_pending_list_normal : Normal priority request queue
+ * @high_req_lock             : Spinlock of high priority queue
+ * @normal_req_lock           : Spinlock of normal priority queue
+ */
+struct cam_lrme_device {
+	struct cam_lrme_dev_cap        hw_caps;
+	struct cam_hw_intf             hw_intf;
+	uint32_t                       num_context;
+	bool                           valid;
+	struct cam_req_mgr_core_workq *work;
+	struct cam_lrme_mgr_work_data  work_data[CAM_LRME_WORKQ_NUM_TASK];
+	struct list_head               frame_pending_list_high;
+	struct list_head               frame_pending_list_normal;
+	spinlock_t                     high_req_lock;
+	spinlock_t                     normal_req_lock;
+};
+
+/**
+ * struct cam_lrme_hw_mgr : LRME HW manager
+ *
+ * @device_count    : Number of HW devices
+ * @frame_free_list : List of free frame request
+ * @hw_mgr_mutex    : Mutex to protect HW manager data
+ * @free_req_lock   :Spinlock to protect frame_free_list
+ * @hw_device       : List of HW devices
+ * @device_iommu    : Device iommu
+ * @cdm_iommu       : cdm iommu
+ * @frame_req       : List of frame request to use
+ * @lrme_caps       : LRME capabilities
+ * @event_cb        : IRQ callback function
+ */
+struct cam_lrme_hw_mgr {
+	uint32_t                      device_count;
+	struct list_head              frame_free_list;
+	struct mutex                  hw_mgr_mutex;
+	spinlock_t                    free_req_lock;
+	struct cam_lrme_device        hw_device[CAM_LRME_HW_MAX];
+	struct cam_iommu_handle       device_iommu;
+	struct cam_iommu_handle       cdm_iommu;
+	struct cam_lrme_frame_request frame_req[CAM_CTX_REQ_MAX * CAM_CTX_MAX];
+	struct cam_lrme_query_cap_cmd lrme_caps;
+	cam_hw_event_cb_func          event_cb;
+};
+
+int cam_lrme_mgr_register_device(struct cam_hw_intf *lrme_hw_intf,
+	struct cam_iommu_handle *device_iommu,
+	struct cam_iommu_handle *cdm_iommu);
+int cam_lrme_mgr_deregister_device(int device_index);
+
+#endif /* _CAM_LRME_HW_MGR_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h
similarity index 64%
rename from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
rename to drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h
index 71b21b9..8bb609c 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/cam_lrme_hw_mgr_intf.h
@@ -10,19 +10,16 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#ifndef _CAM_LRME_HW_MGR_INTF_H_
+#define _CAM_LRME_HW_MGR_INTF_H_
 
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+#include <linux/of.h>
 
+#include "cam_debug_util.h"
 #include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
 
-enum cam_jpeg_dma_cmd_type {
-	CAM_JPEG_DMA_CMD_CDM_CFG,
-	CAM_JPEG_DMA_CMD_SET_IRQ_CB,
-	CAM_JPEG_DMA_CMD_MAX,
-};
+int cam_lrme_hw_mgr_init(struct cam_hw_mgr_intf *hw_mgr_intf,
+	cam_hw_event_cb_func cam_lrme_dev_buf_done_cb);
+int cam_lrme_hw_mgr_deinit(void);
 
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
+#endif /* _CAM_LRME_HW_MGR_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile
new file mode 100644
index 0000000..c65d862
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/Makefile
@@ -0,0 +1,13 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cdm
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw
+ccflags-y += -Idrivers/media/platform/msm/camera0
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_lrme_hw_dev.o cam_lrme_hw_core.o cam_lrme_hw_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
new file mode 100644
index 0000000..0318739
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.c
@@ -0,0 +1,1022 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_lrme_hw_core.h"
+#include "cam_lrme_hw_soc.h"
+#include "cam_smmu_api.h"
+
+static void cam_lrme_cdm_write_reg_val_pair(uint32_t *buffer,
+	uint32_t *index, uint32_t reg_offset, uint32_t reg_value)
+{
+	buffer[(*index)++] = reg_offset;
+	buffer[(*index)++] = reg_value;
+}
+
+static void cam_lrme_hw_util_fill_fe_reg(struct cam_lrme_hw_io_buffer *io_buf,
+	uint32_t index, uint32_t *reg_val_pair, uint32_t *num_cmd,
+	struct cam_lrme_hw_info *hw_info)
+{
+	uint32_t reg_val;
+
+	/* 1. config buffer size */
+	reg_val = io_buf->io_cfg->planes[0].width;
+	reg_val |= (io_buf->io_cfg->planes[0].height << 16);
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_rd_reg.bus_client_reg[index].rd_buffer_size,
+		reg_val);
+
+	CAM_DBG(CAM_LRME,
+		"width %d", io_buf->io_cfg->planes[0].width);
+	CAM_DBG(CAM_LRME,
+		"height %d", io_buf->io_cfg->planes[0].height);
+
+	/* 2. config image address */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_rd_reg.bus_client_reg[index].addr_image,
+		io_buf->io_addr[0]);
+
+	CAM_DBG(CAM_LRME, "io addr %llu", io_buf->io_addr[0]);
+
+	/* 3. config stride */
+	reg_val = io_buf->io_cfg->planes[0].plane_stride;
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_rd_reg.bus_client_reg[index].rd_stride,
+		reg_val);
+
+	CAM_DBG(CAM_LRME, "plane_stride %d",
+		io_buf->io_cfg->planes[0].plane_stride);
+
+	/* 4. enable client */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_rd_reg.bus_client_reg[index].core_cfg, 0x1);
+
+	/* 5. unpack_cfg */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_rd_reg.bus_client_reg[index].unpack_cfg_0, 0x0);
+}
+
+static void cam_lrme_hw_util_fill_we_reg(struct cam_lrme_hw_io_buffer *io_buf,
+	uint32_t index, uint32_t *reg_val_pair, uint32_t *num_cmd,
+	struct cam_lrme_hw_info *hw_info)
+{
+	/* config client mode */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].cfg,
+		0x1);
+
+	/* image address */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].addr_image,
+		io_buf->io_addr[0]);
+	CAM_DBG(CAM_LRME, "io addr %llu", io_buf->io_addr[0]);
+
+	/* buffer width and height */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].buffer_width_cfg,
+		io_buf->io_cfg->planes[0].width);
+	CAM_DBG(CAM_LRME, "width %d", io_buf->io_cfg->planes[0].width);
+
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].buffer_height_cfg,
+		io_buf->io_cfg->planes[0].height);
+	CAM_DBG(CAM_LRME, "height %d", io_buf->io_cfg->planes[0].height);
+
+	/* packer cfg */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].packer_cfg,
+		(index == 0) ? 0x1 : 0x5);
+
+	/* client stride */
+	cam_lrme_cdm_write_reg_val_pair(reg_val_pair, num_cmd,
+		hw_info->bus_wr_reg.bus_client_reg[index].wr_stride,
+		io_buf->io_cfg->planes[0].meta_stride);
+	CAM_DBG(CAM_LRME, "plane_stride %d",
+		io_buf->io_cfg->planes[0].plane_stride);
+}
+
+
+static int cam_lrme_hw_util_process_config_hw(struct cam_hw_info *lrme_hw,
+	struct cam_lrme_hw_cmd_config_args *config_args)
+{
+	int i;
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_cdm_info *hw_cdm_info;
+	uint32_t *cmd_buf_addr = config_args->cmd_buf_addr;
+	uint32_t reg_val_pair[CAM_LRME_MAX_REG_PAIR_NUM];
+	struct cam_lrme_hw_io_buffer *io_buf;
+	struct cam_lrme_hw_info *hw_info =
+		((struct cam_lrme_core *)lrme_hw->core_info)->hw_info;
+	uint32_t num_cmd = 0;
+	uint32_t size;
+	uint32_t mem_base, available_size = config_args->size;
+	uint32_t output_res_mask = 0, input_res_mask = 0;
+
+
+	if (!cmd_buf_addr) {
+		CAM_ERR(CAM_LRME, "Invalid input args");
+		return -EINVAL;
+	}
+
+	hw_cdm_info =
+		((struct cam_lrme_core *)lrme_hw->core_info)->hw_cdm_info;
+
+	for (i = 0; i < CAM_LRME_MAX_IO_BUFFER; i++) {
+		io_buf = &config_args->input_buf[i];
+
+		if (io_buf->valid == false)
+			break;
+
+		if (io_buf->io_cfg->direction != CAM_BUF_INPUT) {
+			CAM_ERR(CAM_LRME, "Incorrect direction %d %d",
+				io_buf->io_cfg->direction, CAM_BUF_INPUT);
+			return -EINVAL;
+		}
+		CAM_DBG(CAM_LRME,
+			"resource_type %d", io_buf->io_cfg->resource_type);
+
+		switch (io_buf->io_cfg->resource_type) {
+		case CAM_LRME_IO_TYPE_TAR:
+			cam_lrme_hw_util_fill_fe_reg(io_buf, 0, reg_val_pair,
+				&num_cmd, hw_info);
+
+			input_res_mask |= CAM_LRME_INPUT_PORT_TYPE_TAR;
+			break;
+		case CAM_LRME_IO_TYPE_REF:
+			cam_lrme_hw_util_fill_fe_reg(io_buf, 1, reg_val_pair,
+				&num_cmd, hw_info);
+
+			input_res_mask |= CAM_LRME_INPUT_PORT_TYPE_REF;
+			break;
+		default:
+			CAM_ERR(CAM_LRME, "wrong resource_type %d",
+				io_buf->io_cfg->resource_type);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < CAM_LRME_BUS_RD_MAX_CLIENTS; i++)
+		if (!((input_res_mask >> i) & 0x1))
+			cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd,
+				hw_info->bus_rd_reg.bus_client_reg[i].core_cfg,
+				0x0);
+
+	for (i = 0; i < CAM_LRME_MAX_IO_BUFFER; i++) {
+		io_buf = &config_args->output_buf[i];
+
+		if (io_buf->valid == false)
+			break;
+
+		if (io_buf->io_cfg->direction != CAM_BUF_OUTPUT) {
+			CAM_ERR(CAM_LRME, "Incorrect direction %d %d",
+				io_buf->io_cfg->direction, CAM_BUF_INPUT);
+			return -EINVAL;
+		}
+
+		CAM_DBG(CAM_LRME, "resource_type %d",
+			io_buf->io_cfg->resource_type);
+		switch (io_buf->io_cfg->resource_type) {
+		case CAM_LRME_IO_TYPE_DS2:
+			cam_lrme_hw_util_fill_we_reg(io_buf, 0, reg_val_pair,
+				&num_cmd, hw_info);
+
+			output_res_mask |= CAM_LRME_OUTPUT_PORT_TYPE_DS2;
+			break;
+		case CAM_LRME_IO_TYPE_RES:
+			cam_lrme_hw_util_fill_we_reg(io_buf, 1, reg_val_pair,
+				&num_cmd, hw_info);
+
+			output_res_mask |= CAM_LRME_OUTPUT_PORT_TYPE_RES;
+			break;
+
+		default:
+			CAM_ERR(CAM_LRME, "wrong resource_type %d",
+				io_buf->io_cfg->resource_type);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < CAM_LRME_BUS_RD_MAX_CLIENTS; i++)
+		if (!((output_res_mask >> i) & 0x1))
+			cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd,
+				hw_info->bus_wr_reg.bus_client_reg[i].cfg, 0x0);
+
+	if (output_res_mask) {
+		/* write composite mask */
+		cam_lrme_cdm_write_reg_val_pair(reg_val_pair, &num_cmd,
+			hw_info->bus_wr_reg.common_reg.composite_mask_0,
+			output_res_mask);
+	}
+
+	size = hw_cdm_info->cdm_ops->cdm_required_size_changebase();
+	if ((size * 4) > available_size) {
+		CAM_ERR(CAM_LRME, "buf size:%d is not sufficient, expected: %d",
+			available_size, size);
+		return -EINVAL;
+	}
+
+	mem_base = CAM_SOC_GET_REG_MAP_CAM_BASE(soc_info, CAM_LRME_BASE_IDX);
+
+	hw_cdm_info->cdm_ops->cdm_write_changebase(cmd_buf_addr, mem_base);
+	cmd_buf_addr += size;
+	available_size -= (size * 4);
+
+	size = hw_cdm_info->cdm_ops->cdm_required_size_reg_random(
+		num_cmd / 2);
+
+	if ((size * 4) > available_size) {
+		CAM_ERR(CAM_LRME, "buf size:%d is not sufficient, expected: %d",
+			available_size, size);
+		return -ENOMEM;
+	}
+
+	hw_cdm_info->cdm_ops->cdm_write_regrandom(cmd_buf_addr, num_cmd / 2,
+		reg_val_pair);
+	cmd_buf_addr += size;
+	available_size -= (size * 4);
+
+	config_args->config_buf_size =
+		config_args->size - available_size;
+
+	return 0;
+}
+
+static int cam_lrme_hw_util_submit_go(struct cam_hw_info *lrme_hw)
+{
+	struct cam_lrme_core *lrme_core;
+	struct cam_hw_soc_info *soc_info;
+	struct cam_lrme_hw_info   *hw_info;
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	hw_info = lrme_core->hw_info;
+	soc_info = &lrme_hw->soc_info;
+
+	cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base +
+		hw_info->bus_rd_reg.common_reg.cmd);
+
+	return 0;
+}
+
+static int cam_lrme_hw_util_reset(struct cam_hw_info *lrme_hw,
+	uint32_t reset_type)
+{
+	struct cam_lrme_core *lrme_core;
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_hw_info *hw_info;
+	long time_left;
+
+	lrme_core = lrme_hw->core_info;
+	hw_info = lrme_core->hw_info;
+
+	switch (reset_type) {
+	case CAM_LRME_HW_RESET_TYPE_HW_RESET:
+		reinit_completion(&lrme_core->reset_complete);
+		cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base +
+			hw_info->titan_reg.top_rst_cmd);
+		time_left = wait_for_completion_timeout(
+			&lrme_core->reset_complete,
+			msecs_to_jiffies(CAM_LRME_HW_RESET_TIMEOUT));
+		if (time_left <= 0) {
+			CAM_ERR(CAM_LRME,
+				"HW reset wait failed time_left=%ld",
+				time_left);
+			return -ETIMEDOUT;
+		}
+		break;
+	case CAM_LRME_HW_RESET_TYPE_SW_RESET:
+		cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base +
+			hw_info->bus_wr_reg.common_reg.sw_reset);
+		cam_io_w_mb(0x3, soc_info->reg_map[0].mem_base +
+			hw_info->bus_rd_reg.common_reg.sw_reset);
+		reinit_completion(&lrme_core->reset_complete);
+		cam_io_w_mb(0x2, soc_info->reg_map[0].mem_base +
+			hw_info->titan_reg.top_rst_cmd);
+		time_left = wait_for_completion_timeout(
+			&lrme_core->reset_complete,
+			msecs_to_jiffies(CAM_LRME_HW_RESET_TIMEOUT));
+		if (time_left <= 0) {
+			CAM_ERR(CAM_LRME,
+				"SW reset wait failed time_left=%ld",
+				time_left);
+			return -ETIMEDOUT;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+int cam_lrme_hw_util_get_caps(struct cam_hw_info *lrme_hw,
+	struct cam_lrme_dev_cap *hw_caps)
+{
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_hw_info *hw_info =
+		((struct cam_lrme_core *)lrme_hw->core_info)->hw_info;
+	uint32_t reg_value;
+
+	if (!hw_info) {
+		CAM_ERR(CAM_LRME, "Invalid hw info data");
+		return -EINVAL;
+	}
+
+	reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		hw_info->clc_reg.clc_hw_version);
+	hw_caps->clc_hw_version.gen =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C);
+	hw_caps->clc_hw_version.rev =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+	hw_caps->clc_hw_version.step =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+	reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		hw_info->bus_rd_reg.common_reg.hw_version);
+	hw_caps->bus_rd_hw_version.gen =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C);
+	hw_caps->bus_rd_hw_version.rev =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+	hw_caps->bus_rd_hw_version.step =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+	reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.hw_version);
+	hw_caps->bus_wr_hw_version.gen =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C);
+	hw_caps->bus_wr_hw_version.rev =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+	hw_caps->bus_wr_hw_version.step =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+	reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		hw_info->titan_reg.top_hw_version);
+	hw_caps->top_hw_version.gen =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C);
+	hw_caps->top_hw_version.rev =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+	hw_caps->top_hw_version.step =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+	reg_value = cam_io_r_mb(soc_info->reg_map[0].mem_base +
+		hw_info->titan_reg.top_titan_version);
+	hw_caps->top_titan_version.gen =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xf0000000, 0x1C);
+	hw_caps->top_titan_version.rev =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xfff0000, 0x10);
+	hw_caps->top_titan_version.step =
+		CAM_BITS_MASK_SHIFT(reg_value, 0xffff, 0x0);
+
+	return 0;
+}
+
+static int cam_lrme_hw_util_submit_req(struct cam_lrme_core *lrme_core,
+	struct cam_lrme_frame_request *frame_req)
+{
+	struct cam_lrme_cdm_info *hw_cdm_info =
+		lrme_core->hw_cdm_info;
+	struct cam_cdm_bl_request *cdm_cmd = hw_cdm_info->cdm_cmd;
+	struct cam_hw_update_entry *cmd;
+	int i, rc = 0;
+
+	if (frame_req->num_hw_update_entries > 0) {
+		cdm_cmd->cmd_arrary_count = frame_req->num_hw_update_entries;
+		cdm_cmd->type = CAM_CDM_BL_CMD_TYPE_MEM_HANDLE;
+		cdm_cmd->flag = false;
+		cdm_cmd->userdata = NULL;
+		cdm_cmd->cookie = 0;
+
+		for (i = 0; i <= frame_req->num_hw_update_entries; i++) {
+			cmd = (frame_req->hw_update_entries + i);
+			cdm_cmd->cmd[i].bl_addr.mem_handle = cmd->handle;
+			cdm_cmd->cmd[i].offset = cmd->offset;
+			cdm_cmd->cmd[i].len = cmd->len;
+		}
+
+		rc = cam_cdm_submit_bls(hw_cdm_info->cdm_handle, cdm_cmd);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Failed to submit cdm commands");
+			return -EINVAL;
+		}
+	} else {
+		CAM_ERR(CAM_LRME, "No hw update entry");
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int cam_lrme_hw_util_process_err(struct cam_hw_info *lrme_hw)
+{
+	struct cam_lrme_core *lrme_core = lrme_hw->core_info;
+	struct cam_lrme_frame_request *req_proc, *req_submit;
+	struct cam_lrme_hw_cb_args cb_args;
+	int rc;
+
+	req_proc = lrme_core->req_proc;
+	req_submit = lrme_core->req_submit;
+	cb_args.cb_type = CAM_LRME_CB_ERROR;
+
+	if ((lrme_core->state != CAM_LRME_CORE_STATE_PROCESSING) &&
+		(lrme_core->state != CAM_LRME_CORE_STATE_REQ_PENDING) &&
+		(lrme_core->state != CAM_LRME_CORE_STATE_REQ_PROC_PEND)) {
+		CAM_ERR(CAM_LRME, "Get error irq in wrong state %d",
+			lrme_core->state);
+	}
+
+	CAM_ERR_RATE_LIMIT(CAM_LRME, "Start recovery");
+	lrme_core->state = CAM_LRME_CORE_STATE_RECOVERY;
+	rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Failed to reset");
+
+	lrme_core->req_proc = NULL;
+	lrme_core->req_submit = NULL;
+	if (!rc)
+		lrme_core->state = CAM_LRME_CORE_STATE_IDLE;
+
+	cb_args.frame_req = req_proc;
+	lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core->hw_mgr_cb.data,
+		&cb_args);
+
+	cb_args.frame_req = req_submit;
+	lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core->hw_mgr_cb.data,
+		&cb_args);
+
+	return rc;
+}
+
+static int cam_lrme_hw_util_process_reg_update(
+	struct cam_hw_info *lrme_hw, struct cam_lrme_hw_cb_args *cb_args)
+{
+	struct cam_lrme_core *lrme_core = lrme_hw->core_info;
+	int rc = 0;
+
+	cb_args->cb_type |= CAM_LRME_CB_COMP_REG_UPDATE;
+	if (lrme_core->state == CAM_LRME_CORE_STATE_REQ_PENDING) {
+		lrme_core->state = CAM_LRME_CORE_STATE_PROCESSING;
+	} else {
+		CAM_ERR(CAM_LRME, "Reg update in wrong state %d",
+			lrme_core->state);
+		rc = cam_lrme_hw_util_process_err(lrme_hw);
+		if (rc)
+			CAM_ERR(CAM_LRME, "Failed to reset");
+		return -EINVAL;
+	}
+
+	lrme_core->req_proc = lrme_core->req_submit;
+	lrme_core->req_submit = NULL;
+
+	return 0;
+}
+
+static int cam_lrme_hw_util_process_idle(
+	struct cam_hw_info *lrme_hw, struct cam_lrme_hw_cb_args *cb_args)
+{
+	struct cam_lrme_core *lrme_core = lrme_hw->core_info;
+	int rc = 0;
+
+	cb_args->cb_type |= CAM_LRME_CB_BUF_DONE;
+	switch (lrme_core->state) {
+	case CAM_LRME_CORE_STATE_REQ_PROC_PEND:
+		cam_lrme_hw_util_submit_go(lrme_hw);
+		lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING;
+		break;
+
+	case CAM_LRME_CORE_STATE_PROCESSING:
+		lrme_core->state = CAM_LRME_CORE_STATE_IDLE;
+		break;
+
+	default:
+		CAM_ERR(CAM_LRME, "Idle in wrong state %d",
+			lrme_core->state);
+		rc = cam_lrme_hw_util_process_err(lrme_hw);
+		return rc;
+	}
+	cb_args->frame_req = lrme_core->req_proc;
+	lrme_core->req_proc = NULL;
+
+	return 0;
+}
+
+void cam_lrme_set_irq(struct cam_hw_info *lrme_hw,
+	enum cam_lrme_irq_set set)
+{
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_core *lrme_core = lrme_hw->core_info;
+	struct cam_lrme_hw_info *hw_info = lrme_core->hw_info;
+
+	switch (set) {
+	case CAM_LRME_IRQ_ENABLE:
+		cam_io_w_mb(0xFFFF,
+			soc_info->reg_map[0].mem_base +
+			hw_info->titan_reg.top_irq_mask);
+		cam_io_w_mb(0xFFFF,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_wr_reg.common_reg.irq_mask_0);
+		cam_io_w_mb(0xFFFF,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_wr_reg.common_reg.irq_mask_1);
+		cam_io_w_mb(0xFFFF,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_rd_reg.common_reg.irq_mask);
+		break;
+
+	case CAM_LRME_IRQ_DISABLE:
+		cam_io_w_mb(0x0,
+			soc_info->reg_map[0].mem_base +
+			hw_info->titan_reg.top_irq_mask);
+		cam_io_w_mb(0x0,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_wr_reg.common_reg.irq_mask_0);
+		cam_io_w_mb(0x0,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_wr_reg.common_reg.irq_mask_1);
+		cam_io_w_mb(0x0,
+			soc_info->reg_map[0].mem_base +
+			hw_info->bus_rd_reg.common_reg.irq_mask);
+		break;
+	}
+}
+
+
+int cam_lrme_hw_process_irq(void *priv, void *data)
+{
+	struct cam_lrme_hw_work_data *work_data;
+	struct cam_hw_info *lrme_hw;
+	struct cam_lrme_core *lrme_core;
+	int rc = 0;
+	uint32_t top_irq_status, fe_irq_status;
+	uint32_t *we_irq_status;
+	struct cam_lrme_hw_cb_args cb_args;
+
+	if (!data || !priv) {
+		CAM_ERR(CAM_LRME, "Invalid data %pK %pK", data, priv);
+		return -EINVAL;
+	}
+
+	memset(&cb_args, 0, sizeof(struct cam_lrme_hw_cb_args));
+	lrme_hw = (struct cam_hw_info *)priv;
+	work_data = (struct cam_lrme_hw_work_data *)data;
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	top_irq_status = work_data->top_irq_status;
+	fe_irq_status = work_data->fe_irq_status;
+	we_irq_status = work_data->we_irq_status;
+
+	CAM_DBG(CAM_LRME,
+		"top status %x, fe status %x, we status0 %x, we status1 %x",
+		top_irq_status, fe_irq_status, we_irq_status[0],
+		we_irq_status[1]);
+	CAM_DBG(CAM_LRME, "Current state %d", lrme_core->state);
+
+	mutex_lock(&lrme_hw->hw_mutex);
+
+	if (top_irq_status & (1 << 3)) {
+		CAM_DBG(CAM_LRME, "Error");
+		rc = cam_lrme_hw_util_process_err(lrme_hw);
+		if (rc)
+			CAM_ERR(CAM_LRME, "Process error failed");
+		goto end;
+	}
+
+	if (we_irq_status[0] & (1 << 1)) {
+		CAM_DBG(CAM_LRME, "reg update");
+		rc = cam_lrme_hw_util_process_reg_update(lrme_hw, &cb_args);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Process reg_update failed");
+			goto end;
+		}
+	}
+
+	if (top_irq_status & (1 << 4)) {
+		CAM_DBG(CAM_LRME, "IDLE");
+
+		rc = cam_lrme_hw_util_process_idle(lrme_hw, &cb_args);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Process idle failed");
+			goto end;
+		}
+	}
+
+	if (lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb) {
+		lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb(lrme_core->
+			hw_mgr_cb.data, &cb_args);
+	} else {
+		CAM_ERR(CAM_LRME, "No hw mgr cb");
+		rc = -EINVAL;
+	}
+
+end:
+	mutex_unlock(&lrme_hw->hw_mutex);
+	return rc;
+}
+
+int cam_lrme_hw_start(void *hw_priv, void *hw_start_args, uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv;
+	int rc = 0;
+	struct cam_lrme_core *lrme_core;
+
+	if (!lrme_hw) {
+		CAM_ERR(CAM_LRME,
+			"Invalid input params, lrme_hw %pK",
+			lrme_hw);
+		return -EINVAL;
+	}
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+
+	mutex_lock(&lrme_hw->hw_mutex);
+
+	if (lrme_hw->open_count > 0) {
+		CAM_DBG(CAM_LRME, "This device is activated before");
+		goto unlock;
+	}
+
+	rc = cam_lrme_soc_enable_resources(lrme_hw);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to enable soc resources");
+		goto unlock;
+	}
+
+	rc = cam_lrme_hw_util_reset(lrme_hw, CAM_LRME_HW_RESET_TYPE_HW_RESET);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to reset hw");
+		goto disable_soc;
+	}
+
+	if (lrme_core->hw_cdm_info) {
+		struct cam_lrme_cdm_info *hw_cdm_info =
+			lrme_core->hw_cdm_info;
+
+		rc = cam_cdm_stream_on(hw_cdm_info->cdm_handle);
+		if (rc) {
+			CAM_ERR(CAM_LRME, "Failed to stream on cdm");
+			goto disable_soc;
+		}
+	}
+
+	lrme_hw->hw_state = CAM_HW_STATE_POWER_UP;
+	lrme_hw->open_count++;
+	lrme_core->state = CAM_LRME_CORE_STATE_IDLE;
+
+	mutex_unlock(&lrme_hw->hw_mutex);
+	return rc;
+
+disable_soc:
+	if (cam_lrme_soc_disable_resources(lrme_hw))
+		CAM_ERR(CAM_LRME, "Error in disable soc resources");
+unlock:
+	mutex_unlock(&lrme_hw->hw_mutex);
+	return rc;
+}
+
+int cam_lrme_hw_stop(void *hw_priv, void *hw_stop_args, uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv;
+	int rc = 0;
+	struct cam_lrme_core *lrme_core;
+
+	if (!lrme_hw) {
+		CAM_ERR(CAM_LRME, "Invalid argument");
+		return -EINVAL;
+	}
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+
+	mutex_lock(&lrme_hw->hw_mutex);
+
+	if (lrme_hw->open_count == 0) {
+		mutex_unlock(&lrme_hw->hw_mutex);
+		CAM_ERR(CAM_LRME, "Error Unbalanced stop");
+		return -EINVAL;
+	}
+	lrme_hw->open_count--;
+
+	if (lrme_hw->open_count)
+		goto unlock;
+
+	lrme_core->req_proc = NULL;
+	lrme_core->req_submit = NULL;
+
+	if (lrme_core->hw_cdm_info) {
+		struct cam_lrme_cdm_info *hw_cdm_info =
+			lrme_core->hw_cdm_info;
+
+		rc = cam_cdm_stream_off(hw_cdm_info->cdm_handle);
+		if (rc) {
+			CAM_ERR(CAM_LRME,
+				"Failed in CDM StreamOff, handle=0x%x, rc=%d",
+				hw_cdm_info->cdm_handle, rc);
+			goto unlock;
+		}
+	}
+
+	rc = cam_lrme_soc_disable_resources(lrme_hw);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed in Disable SOC, rc=%d", rc);
+		goto unlock;
+	}
+
+	lrme_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+	if (lrme_core->state == CAM_LRME_CORE_STATE_IDLE) {
+		lrme_core->state = CAM_LRME_CORE_STATE_INIT;
+	} else {
+		CAM_ERR(CAM_LRME, "HW in wrong state %d", lrme_core->state);
+		return -EINVAL;
+	}
+
+unlock:
+	mutex_unlock(&lrme_hw->hw_mutex);
+	return rc;
+}
+
+int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
+	uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv;
+	struct cam_lrme_core *lrme_core;
+	struct cam_lrme_hw_submit_args *args =
+		(struct cam_lrme_hw_submit_args *)hw_submit_args;
+	int rc = 0;
+	struct cam_lrme_frame_request *frame_req;
+
+
+	if (!hw_priv || !hw_submit_args) {
+		CAM_ERR(CAM_LRME, "Invalid input");
+		return -EINVAL;
+	}
+
+	if (sizeof(struct cam_lrme_hw_submit_args) != arg_size) {
+		CAM_ERR(CAM_LRME,
+			"size of args %lu, arg_size %d",
+			sizeof(struct cam_lrme_hw_submit_args), arg_size);
+		return -EINVAL;
+	}
+
+	frame_req = args->frame_req;
+
+	mutex_lock(&lrme_hw->hw_mutex);
+
+	if (lrme_hw->open_count == 0) {
+		CAM_ERR(CAM_LRME, "HW is not open");
+		mutex_unlock(&lrme_hw->hw_mutex);
+		return -EINVAL;
+	}
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	if (lrme_core->state != CAM_LRME_CORE_STATE_IDLE &&
+		lrme_core->state != CAM_LRME_CORE_STATE_PROCESSING) {
+		mutex_unlock(&lrme_hw->hw_mutex);
+		CAM_DBG(CAM_LRME, "device busy, can not submit, state %d",
+			lrme_core->state);
+		return -EBUSY;
+	}
+
+	if (lrme_core->req_submit != NULL) {
+		CAM_ERR(CAM_LRME, "req_submit is not NULL");
+		return -EBUSY;
+	}
+
+	rc = cam_lrme_hw_util_submit_req(lrme_core, frame_req);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Submit req failed");
+		goto error;
+	}
+
+	switch (lrme_core->state) {
+	case CAM_LRME_CORE_STATE_PROCESSING:
+		lrme_core->state = CAM_LRME_CORE_STATE_REQ_PROC_PEND;
+		break;
+
+	case CAM_LRME_CORE_STATE_IDLE:
+		cam_lrme_hw_util_submit_go(lrme_hw);
+		lrme_core->state = CAM_LRME_CORE_STATE_REQ_PENDING;
+		break;
+
+	default:
+		CAM_ERR(CAM_LRME, "Wrong hw state");
+		rc = -EINVAL;
+		goto error;
+	}
+
+	lrme_core->req_submit = frame_req;
+	mutex_unlock(&lrme_hw->hw_mutex);
+	CAM_DBG(CAM_LRME, "Release lock, submit done for req %llu",
+		frame_req->req_id);
+
+	return 0;
+
+error:
+	mutex_unlock(&lrme_hw->hw_mutex);
+
+	return rc;
+
+}
+
+int cam_lrme_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw = hw_priv;
+	struct cam_lrme_core *lrme_core;
+	struct cam_lrme_hw_reset_args *lrme_reset_args = reset_core_args;
+	int rc;
+
+	if (!hw_priv) {
+		CAM_ERR(CAM_LRME, "Invalid input args");
+		return -EINVAL;
+	}
+
+	if (!reset_core_args ||
+		sizeof(struct cam_lrme_hw_reset_args) != arg_size) {
+		CAM_ERR(CAM_LRME, "Invalid reset args");
+		return -EINVAL;
+	}
+
+	lrme_core = lrme_hw->core_info;
+
+	mutex_lock(&lrme_hw->hw_mutex);
+	if (lrme_core->state == CAM_LRME_CORE_STATE_RECOVERY) {
+		mutex_unlock(&lrme_hw->hw_mutex);
+		CAM_ERR(CAM_LRME, "Reset not allowed in %d state",
+			lrme_core->state);
+		return -EINVAL;
+	}
+
+	lrme_core->state = CAM_LRME_CORE_STATE_RECOVERY;
+
+	rc = cam_lrme_hw_util_reset(lrme_hw, lrme_reset_args->reset_type);
+	if (rc) {
+		mutex_unlock(&lrme_hw->hw_mutex);
+		CAM_ERR(CAM_FD, "Failed to reset");
+		return rc;
+	}
+
+	lrme_core->state = CAM_LRME_CORE_STATE_IDLE;
+
+	mutex_unlock(&lrme_hw->hw_mutex);
+
+	return 0;
+}
+
+int cam_lrme_hw_get_caps(void *hw_priv, void *get_hw_cap_args,
+	uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw;
+	struct cam_lrme_core *lrme_core;
+	struct cam_lrme_dev_cap *lrme_hw_caps =
+		(struct cam_lrme_dev_cap *)get_hw_cap_args;
+
+	if (!hw_priv || !get_hw_cap_args) {
+		CAM_ERR(CAM_LRME, "Invalid input pointers %pK %pK",
+			hw_priv, get_hw_cap_args);
+		return -EINVAL;
+	}
+
+	lrme_hw = (struct cam_hw_info *)hw_priv;
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	*lrme_hw_caps = lrme_core->hw_caps;
+
+	return 0;
+}
+
+irqreturn_t cam_lrme_hw_irq(int irq_num, void *data)
+{
+	struct cam_hw_info *lrme_hw;
+	struct cam_lrme_core *lrme_core;
+	struct cam_hw_soc_info *soc_info;
+	struct cam_lrme_hw_info   *hw_info;
+	struct crm_workq_task *task;
+	struct cam_lrme_hw_work_data *work_data;
+	uint32_t top_irq_status, fe_irq_status, we_irq_status0, we_irq_status1;
+	int rc;
+
+	if (!data) {
+		CAM_ERR(CAM_LRME, "Invalid data in IRQ callback");
+		return -EINVAL;
+	}
+
+	lrme_hw = (struct cam_hw_info *)data;
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	soc_info = &lrme_hw->soc_info;
+	hw_info = lrme_core->hw_info;
+
+	top_irq_status = cam_io_r_mb(
+		soc_info->reg_map[0].mem_base +
+		hw_info->titan_reg.top_irq_status);
+	CAM_DBG(CAM_LRME, "top_irq_status %x", top_irq_status);
+	cam_io_w_mb(top_irq_status,
+		soc_info->reg_map[0].mem_base +
+		hw_info->titan_reg.top_irq_clear);
+	top_irq_status &= CAM_LRME_TOP_IRQ_MASK;
+
+	fe_irq_status = cam_io_r_mb(
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_rd_reg.common_reg.irq_status);
+	CAM_DBG(CAM_LRME, "fe_irq_status %x", fe_irq_status);
+	cam_io_w_mb(fe_irq_status,
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_rd_reg.common_reg.irq_clear);
+	fe_irq_status &= CAM_LRME_FE_IRQ_MASK;
+
+	we_irq_status0 = cam_io_r_mb(
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.irq_status_0);
+	CAM_DBG(CAM_LRME, "we_irq_status[0] %x", we_irq_status0);
+	cam_io_w_mb(we_irq_status0,
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.irq_clear_0);
+	we_irq_status0 &= CAM_LRME_WE_IRQ_MASK_0;
+
+	we_irq_status1 = cam_io_r_mb(
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.irq_status_1);
+	CAM_DBG(CAM_LRME, "we_irq_status[1] %x", we_irq_status1);
+	cam_io_w_mb(we_irq_status1,
+		soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.irq_clear_1);
+	we_irq_status1 &= CAM_LRME_WE_IRQ_MASK_1;
+
+	cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base +
+		hw_info->titan_reg.top_irq_cmd);
+	cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base +
+		hw_info->bus_wr_reg.common_reg.irq_cmd);
+	cam_io_w_mb(0x1, soc_info->reg_map[0].mem_base +
+		hw_info->bus_rd_reg.common_reg.irq_cmd);
+
+	if (top_irq_status & 0x1) {
+		complete(&lrme_core->reset_complete);
+		top_irq_status &= (~0x1);
+	}
+
+	if (top_irq_status || fe_irq_status ||
+		we_irq_status0 || we_irq_status1) {
+		task = cam_req_mgr_workq_get_task(lrme_core->work);
+		if (!task) {
+			CAM_ERR(CAM_LRME, "no empty task available");
+			return -ENOMEM;
+		}
+		work_data = (struct cam_lrme_hw_work_data *)task->payload;
+		work_data->top_irq_status = top_irq_status;
+		work_data->fe_irq_status = fe_irq_status;
+		work_data->we_irq_status[0] = we_irq_status0;
+		work_data->we_irq_status[1] = we_irq_status1;
+		task->process_cb = cam_lrme_hw_process_irq;
+		rc = cam_req_mgr_workq_enqueue_task(task, data,
+			CRM_TASK_PRIORITY_0);
+		if (rc)
+			CAM_ERR(CAM_LRME,
+				"Failed in enqueue work task, rc=%d", rc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+	void *cmd_args, uint32_t arg_size)
+{
+	struct cam_hw_info *lrme_hw = (struct cam_hw_info *)hw_priv;
+	int rc = 0;
+
+	switch (cmd_type) {
+	case CAM_LRME_HW_CMD_PREPARE_HW_UPDATE: {
+		struct cam_lrme_hw_cmd_config_args *config_args;
+
+		config_args = (struct cam_lrme_hw_cmd_config_args *)cmd_args;
+		rc = cam_lrme_hw_util_process_config_hw(lrme_hw, config_args);
+		break;
+	}
+
+	case CAM_LRME_HW_CMD_REGISTER_CB: {
+		struct cam_lrme_hw_cmd_set_cb *cb_args;
+		struct cam_lrme_device *hw_device;
+		struct cam_lrme_core *lrme_core =
+			(struct cam_lrme_core *)lrme_hw->core_info;
+		cb_args = (struct cam_lrme_hw_cmd_set_cb *)cmd_args;
+		lrme_core->hw_mgr_cb.cam_lrme_hw_mgr_cb =
+			cb_args->cam_lrme_hw_mgr_cb;
+		lrme_core->hw_mgr_cb.data = cb_args->data;
+		hw_device = cb_args->data;
+		rc = 0;
+		break;
+	}
+
+	case CAM_LRME_HW_CMD_SUBMIT: {
+		struct cam_lrme_hw_submit_args *submit_args;
+
+		submit_args = (struct cam_lrme_hw_submit_args *)cmd_args;
+		rc = cam_lrme_hw_submit_req(hw_priv,
+			submit_args, arg_size);
+		break;
+	}
+
+	default:
+		break;
+	}
+
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h
new file mode 100644
index 0000000..bf2f370
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_core.h
@@ -0,0 +1,457 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_HW_CORE_H_
+#define _CAM_LRME_HW_CORE_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_defs.h>
+#include <media/cam_lrme.h>
+
+#include "cam_common_util.h"
+#include "cam_debug_util.h"
+#include "cam_io_util.h"
+#include "cam_cpas_api.h"
+#include "cam_cdm_intf_api.h"
+#include "cam_lrme_hw_intf.h"
+#include "cam_lrme_hw_soc.h"
+#include "cam_req_mgr_workq.h"
+
+#define CAM_LRME_HW_RESET_TIMEOUT 3000
+
+#define CAM_LRME_BUS_RD_MAX_CLIENTS 2
+#define CAM_LRME_BUS_WR_MAX_CLIENTS 2
+
+#define CAM_LRME_HW_WORKQ_NUM_TASK 30
+
+#define CAM_LRME_TOP_IRQ_MASK          0x19
+#define CAM_LRME_WE_IRQ_MASK_0         0x2
+#define CAM_LRME_WE_IRQ_MASK_1         0x0
+#define CAM_LRME_FE_IRQ_MASK           0x0
+
+#define CAM_LRME_MAX_REG_PAIR_NUM 60
+
+/**
+ * enum cam_lrme_irq_set
+ *
+ * @CAM_LRME_IRQ_ENABLE  : Enable irqs
+ * @CAM_LRME_IRQ_DISABLE : Disable irqs
+ */
+enum cam_lrme_irq_set {
+	CAM_LRME_IRQ_ENABLE,
+	CAM_LRME_IRQ_DISABLE,
+};
+
+/**
+ * struct cam_lrme_cdm_info : information used to submit cdm command
+ *
+ * @cdm_handle      : CDM handle for this device
+ * @cdm_ops         : CDM ops
+ * @cdm_cmd         : CDM command pointer
+ */
+struct cam_lrme_cdm_info {
+	uint32_t                   cdm_handle;
+	struct cam_cdm_utils_ops  *cdm_ops;
+	struct cam_cdm_bl_request *cdm_cmd;
+};
+
+/**
+ * struct cam_lrme_hw_work_data : Work data for HW work queue
+ *
+ * @top_irq_status : Top registers irq status
+ * @fe_irq_status  : FE engine irq status
+ * @we_irq_status  : WE engine irq status
+ */
+struct cam_lrme_hw_work_data {
+	uint32_t                          top_irq_status;
+	uint32_t                          fe_irq_status;
+	uint32_t                          we_irq_status[2];
+};
+
+/**
+ *  enum cam_lrme_core_state : LRME core states
+ *
+ * @CAM_LRME_CORE_STATE_UNINIT        : LRME is in uninit state
+ * @CAM_LRME_CORE_STATE_INIT          : LRME is in init state after probe
+ * @ CAM_LRME_CORE_STATE_IDLE         : LRME is in idle state. Hardware is in
+ *                                      this state when no frame is processing
+ *                                      or waiting for this core.
+ * @CAM_LRME_CORE_STATE_REQ_PENDING   : LRME is in pending state. One frame is
+ *                                      waiting for processing
+ * @CAM_LRME_CORE_STATE_PROCESSING    : LRME is in processing state. HW manager
+ *                                      can submit one more frame to HW
+ * @CAM_LRME_CORE_STATE_REQ_PROC_PEND : Indicate two frames are inside HW.
+ * @CAM_LRME_CORE_STATE_RECOVERY      : Indicate core is in the process of reset
+ * @CAM_LRME_CORE_STATE_MAX           : upper limit of states
+ */
+enum cam_lrme_core_state {
+	CAM_LRME_CORE_STATE_UNINIT,
+	CAM_LRME_CORE_STATE_INIT,
+	CAM_LRME_CORE_STATE_IDLE,
+	CAM_LRME_CORE_STATE_REQ_PENDING,
+	CAM_LRME_CORE_STATE_PROCESSING,
+	CAM_LRME_CORE_STATE_REQ_PROC_PEND,
+	CAM_LRME_CORE_STATE_RECOVERY,
+	CAM_LRME_CORE_STATE_MAX,
+};
+
+/**
+ *  struct cam_lrme_core : LRME HW core information
+ *
+ * @hw_info        : Pointer to base HW information structure
+ * @device_iommu   : Device iommu handle
+ * @cdm_iommu      : CDM iommu handle
+ * @hw_caps        : Hardware capabilities
+ * @state          : Hardware state
+ * @reset_complete : Reset completion
+ * @work           : Hardware workqueue to handle irq events
+ * @work_data      : Work data used by hardware workqueue
+ * @hw_mgr_cb      : Hw manager callback
+ * @req_proc       : Pointer to the processing frame request
+ * @req_submit     : Pointer to the frame request waiting for processing
+ * @hw_cdm_info    : CDM information used by this device
+ * @hw_idx         : Hardware index
+ */
+struct cam_lrme_core {
+	struct cam_lrme_hw_info          *hw_info;
+	struct cam_iommu_handle           device_iommu;
+	struct cam_iommu_handle           cdm_iommu;
+	struct cam_lrme_dev_cap           hw_caps;
+	enum cam_lrme_core_state          state;
+	struct completion                 reset_complete;
+	struct cam_req_mgr_core_workq    *work;
+	struct cam_lrme_hw_work_data      work_data[CAM_LRME_HW_WORKQ_NUM_TASK];
+	struct cam_lrme_hw_cmd_set_cb     hw_mgr_cb;
+	struct cam_lrme_frame_request    *req_proc;
+	struct cam_lrme_frame_request    *req_submit;
+	struct cam_lrme_cdm_info         *hw_cdm_info;
+	uint32_t                          hw_idx;
+};
+
+/**
+ * struct cam_lrme_bus_rd_reg_common : Offsets of FE common registers
+ *
+ * @hw_version    : Offset of hw_version register
+ * @hw_capability : Offset of hw_capability register
+ * @sw_reset      : Offset of sw_reset register
+ * @cgc_override  : Offset of cgc_override register
+ * @irq_mask      : Offset of irq_mask register
+ * @irq_clear     : Offset of irq_clear register
+ * @irq_cmd       : Offset of irq_cmd register
+ * @irq_status    : Offset of irq_status register
+ * @cmd           : Offset of cmd register
+ * @irq_set       : Offset of irq_set register
+ * @misr_reset    : Offset of misr_reset register
+ * @security_cfg  : Offset of security_cfg register
+ * @pwr_iso_cfg   : Offset of pwr_iso_cfg register
+ * @pwr_iso_seed  : Offset of pwr_iso_seed register
+ * @test_bus_ctrl : Offset of test_bus_ctrl register
+ * @spare         : Offset of spare register
+ */
+struct cam_lrme_bus_rd_reg_common {
+	uint32_t hw_version;
+	uint32_t hw_capability;
+	uint32_t sw_reset;
+	uint32_t cgc_override;
+	uint32_t irq_mask;
+	uint32_t irq_clear;
+	uint32_t irq_cmd;
+	uint32_t irq_status;
+	uint32_t cmd;
+	uint32_t irq_set;
+	uint32_t misr_reset;
+	uint32_t security_cfg;
+	uint32_t pwr_iso_cfg;
+	uint32_t pwr_iso_seed;
+	uint32_t test_bus_ctrl;
+	uint32_t spare;
+};
+
+/**
+ * struct cam_lrme_bus_wr_reg_common : Offset of WE common registers
+ * @hw_version        : Offset of hw_version register
+ * @hw_capability     : Offset of hw_capability register
+ * @sw_reset          : Offset of sw_reset register
+ * @cgc_override      : Offset of cgc_override register
+ * @misr_reset        : Offset of misr_reset register
+ * @pwr_iso_cfg       : Offset of pwr_iso_cfg register
+ * @test_bus_ctrl     : Offset of test_bus_ctrl register
+ * @composite_mask_0  : Offset of composite_mask_0 register
+ * @irq_mask_0        : Offset of irq_mask_0 register
+ * @irq_mask_1        : Offset of irq_mask_1 register
+ * @irq_clear_0       : Offset of irq_clear_0 register
+ * @irq_clear_1       : Offset of irq_clear_1 register
+ * @irq_status_0      : Offset of irq_status_0 register
+ * @irq_status_1      : Offset of irq_status_1 register
+ * @irq_cmd           : Offset of irq_cmd register
+ * @irq_set_0         : Offset of irq_set_0 register
+ * @irq_set_1         : Offset of irq_set_1 register
+ * @addr_fifo_status  : Offset of addr_fifo_status register
+ * @frame_header_cfg0 : Offset of frame_header_cfg0 register
+ * @frame_header_cfg1 : Offset of frame_header_cfg1 register
+ * @spare             : Offset of spare register
+ */
+struct cam_lrme_bus_wr_reg_common {
+	uint32_t hw_version;
+	uint32_t hw_capability;
+	uint32_t sw_reset;
+	uint32_t cgc_override;
+	uint32_t misr_reset;
+	uint32_t pwr_iso_cfg;
+	uint32_t test_bus_ctrl;
+	uint32_t composite_mask_0;
+	uint32_t irq_mask_0;
+	uint32_t irq_mask_1;
+	uint32_t irq_clear_0;
+	uint32_t irq_clear_1;
+	uint32_t irq_status_0;
+	uint32_t irq_status_1;
+	uint32_t irq_cmd;
+	uint32_t irq_set_0;
+	uint32_t irq_set_1;
+	uint32_t addr_fifo_status;
+	uint32_t frame_header_cfg0;
+	uint32_t frame_header_cfg1;
+	uint32_t spare;
+};
+
+/**
+ * struct cam_lrme_bus_rd_bus_client : Offset of FE registers
+ *
+ * @core_cfg                : Offset of core_cfg register
+ * @ccif_meta_data          : Offset of ccif_meta_data register
+ * @addr_image              : Offset of addr_image register
+ * @rd_buffer_size          : Offset of rd_buffer_size register
+ * @rd_stride               : Offset of rd_stride register
+ * @unpack_cfg_0            : Offset of unpack_cfg_0 register
+ * @latency_buff_allocation : Offset of latency_buff_allocation register
+ * @burst_limit_cfg         : Offset of burst_limit_cfg register
+ * @misr_cfg_0              : Offset of misr_cfg_0 register
+ * @misr_cfg_1              : Offset of misr_cfg_1 register
+ * @misr_rd_val             : Offset of misr_rd_val register
+ * @debug_status_cfg        : Offset of debug_status_cfg register
+ * @debug_status_0          : Offset of debug_status_0 register
+ * @debug_status_1          : Offset of debug_status_1 register
+ */
+struct cam_lrme_bus_rd_bus_client {
+	uint32_t core_cfg;
+	uint32_t ccif_meta_data;
+	uint32_t addr_image;
+	uint32_t rd_buffer_size;
+	uint32_t rd_stride;
+	uint32_t unpack_cfg_0;
+	uint32_t latency_buff_allocation;
+	uint32_t burst_limit_cfg;
+	uint32_t misr_cfg_0;
+	uint32_t misr_cfg_1;
+	uint32_t misr_rd_val;
+	uint32_t debug_status_cfg;
+	uint32_t debug_status_0;
+	uint32_t debug_status_1;
+};
+
+/**
+ * struct cam_lrme_bus_wr_bus_client : Offset of WE registers
+ *
+ * @status_0                  : Offset of status_0 register
+ * @status_1                  : Offset of status_1 register
+ * @cfg                       : Offset of cfg register
+ * @addr_frame_header         : Offset of addr_frame_header register
+ * @frame_header_cfg          : Offset of frame_header_cfg register
+ * @addr_image                : Offset of addr_image register
+ * @addr_image_offset         : Offset of addr_image_offset register
+ * @buffer_width_cfg          : Offset of buffer_width_cfg register
+ * @buffer_height_cfg         : Offset of buffer_height_cfg register
+ * @packer_cfg                : Offset of packer_cfg register
+ * @wr_stride                 : Offset of wr_stride register
+ * @irq_subsample_cfg_period  : Offset of irq_subsample_cfg_period register
+ * @irq_subsample_cfg_pattern : Offset of irq_subsample_cfg_pattern register
+ * @burst_limit_cfg           : Offset of burst_limit_cfg register
+ * @misr_cfg                  : Offset of misr_cfg register
+ * @misr_rd_word_sel          : Offset of misr_rd_word_sel register
+ * @misr_val                  : Offset of misr_val register
+ * @debug_status_cfg          : Offset of debug_status_cfg register
+ * @debug_status_0            : Offset of debug_status_0 register
+ * @debug_status_1            : Offset of debug_status_1 register
+ */
+struct cam_lrme_bus_wr_bus_client {
+	uint32_t status_0;
+	uint32_t status_1;
+	uint32_t cfg;
+	uint32_t addr_frame_header;
+	uint32_t frame_header_cfg;
+	uint32_t addr_image;
+	uint32_t addr_image_offset;
+	uint32_t buffer_width_cfg;
+	uint32_t buffer_height_cfg;
+	uint32_t packer_cfg;
+	uint32_t wr_stride;
+	uint32_t irq_subsample_cfg_period;
+	uint32_t irq_subsample_cfg_pattern;
+	uint32_t burst_limit_cfg;
+	uint32_t misr_cfg;
+	uint32_t misr_rd_word_sel;
+	uint32_t misr_val;
+	uint32_t debug_status_cfg;
+	uint32_t debug_status_0;
+	uint32_t debug_status_1;
+};
+
+/**
+ * struct cam_lrme_bus_rd_hw_info : FE registers information
+ *
+ * @common_reg     : FE common register
+ * @bus_client_reg : List of FE bus registers information
+ */
+struct cam_lrme_bus_rd_hw_info {
+	struct cam_lrme_bus_rd_reg_common common_reg;
+	struct cam_lrme_bus_rd_bus_client
+		bus_client_reg[CAM_LRME_BUS_RD_MAX_CLIENTS];
+};
+
+/**
+ * struct cam_lrme_bus_wr_hw_info : WE engine registers information
+ *
+ * @common_reg     : WE common register
+ * @bus_client_reg : List of WE bus registers information
+ */
+struct cam_lrme_bus_wr_hw_info {
+	struct cam_lrme_bus_wr_reg_common common_reg;
+	struct cam_lrme_bus_wr_bus_client
+		bus_client_reg[CAM_LRME_BUS_WR_MAX_CLIENTS];
+};
+
+/**
+ * struct cam_lrme_clc_reg : Offset of clc registers
+ *
+ * @clc_hw_version                 : Offset of clc_hw_version register
+ * @clc_hw_status                  : Offset of clc_hw_status register
+ * @clc_hw_status_dbg              : Offset of clc_hw_status_dbg register
+ * @clc_module_cfg                 : Offset of clc_module_cfg register
+ * @clc_moduleformat               : Offset of clc_moduleformat register
+ * @clc_rangestep                  : Offset of clc_rangestep register
+ * @clc_offset                     : Offset of clc_offset register
+ * @clc_maxallowedsad              : Offset of clc_maxallowedsad register
+ * @clc_minallowedtarmad           : Offset of clc_minallowedtarmad register
+ * @clc_meaningfulsaddiff          : Offset of clc_meaningfulsaddiff register
+ * @clc_minsaddiffdenom            : Offset of clc_minsaddiffdenom register
+ * @clc_robustnessmeasuredistmap_0 : Offset of measuredistmap_0 register
+ * @clc_robustnessmeasuredistmap_1 : Offset of measuredistmap_1 register
+ * @clc_robustnessmeasuredistmap_2 : Offset of measuredistmap_2 register
+ * @clc_robustnessmeasuredistmap_3 : Offset of measuredistmap_3 register
+ * @clc_robustnessmeasuredistmap_4 : Offset of measuredistmap_4 register
+ * @clc_robustnessmeasuredistmap_5 : Offset of measuredistmap_5 register
+ * @clc_robustnessmeasuredistmap_6 : Offset of measuredistmap_6 register
+ * @clc_robustnessmeasuredistmap_7 : Offset of measuredistmap_7 register
+ * @clc_ds_crop_horizontal         : Offset of clc_ds_crop_horizontal register
+ * @clc_ds_crop_vertical           : Offset of clc_ds_crop_vertical register
+ * @clc_tar_pd_unpacker            : Offset of clc_tar_pd_unpacker register
+ * @clc_ref_pd_unpacker            : Offset of clc_ref_pd_unpacker register
+ * @clc_sw_override                : Offset of clc_sw_override register
+ * @clc_tar_height                 : Offset of clc_tar_height register
+ * @clc_test_bus_ctrl              : Offset of clc_test_bus_ctrl register
+ * @clc_spare                      : Offset of clc_spare register
+ */
+struct cam_lrme_clc_reg {
+	uint32_t clc_hw_version;
+	uint32_t clc_hw_status;
+	uint32_t clc_hw_status_dbg;
+	uint32_t clc_module_cfg;
+	uint32_t clc_moduleformat;
+	uint32_t clc_rangestep;
+	uint32_t clc_offset;
+	uint32_t clc_maxallowedsad;
+	uint32_t clc_minallowedtarmad;
+	uint32_t clc_meaningfulsaddiff;
+	uint32_t clc_minsaddiffdenom;
+	uint32_t clc_robustnessmeasuredistmap_0;
+	uint32_t clc_robustnessmeasuredistmap_1;
+	uint32_t clc_robustnessmeasuredistmap_2;
+	uint32_t clc_robustnessmeasuredistmap_3;
+	uint32_t clc_robustnessmeasuredistmap_4;
+	uint32_t clc_robustnessmeasuredistmap_5;
+	uint32_t clc_robustnessmeasuredistmap_6;
+	uint32_t clc_robustnessmeasuredistmap_7;
+	uint32_t clc_ds_crop_horizontal;
+	uint32_t clc_ds_crop_vertical;
+	uint32_t clc_tar_pd_unpacker;
+	uint32_t clc_ref_pd_unpacker;
+	uint32_t clc_sw_override;
+	uint32_t clc_tar_height;
+	uint32_t clc_ref_height;
+	uint32_t clc_test_bus_ctrl;
+	uint32_t clc_spare;
+};
+
+/**
+ * struct cam_lrme_titan_reg : Offset of LRME top registers
+ *
+ * @top_hw_version           : Offset of top_hw_version register
+ * @top_titan_version        : Offset of top_titan_version register
+ * @top_rst_cmd              : Offset of top_rst_cmd register
+ * @top_core_clk_cfg         : Offset of top_core_clk_cfg register
+ * @top_irq_status           : Offset of top_irq_status register
+ * @top_irq_mask             : Offset of top_irq_mask register
+ * @top_irq_clear            : Offset of top_irq_clear register
+ * @top_irq_set              : Offset of top_irq_set register
+ * @top_irq_cmd              : Offset of top_irq_cmd register
+ * @top_violation_status     : Offset of top_violation_status register
+ * @top_spare                : Offset of top_spare register
+ */
+struct cam_lrme_titan_reg {
+	uint32_t top_hw_version;
+	uint32_t top_titan_version;
+	uint32_t top_rst_cmd;
+	uint32_t top_core_clk_cfg;
+	uint32_t top_irq_status;
+	uint32_t top_irq_mask;
+	uint32_t top_irq_clear;
+	uint32_t top_irq_set;
+	uint32_t top_irq_cmd;
+	uint32_t top_violation_status;
+	uint32_t top_spare;
+};
+
+/**
+ * struct cam_lrme_hw_info : LRME registers information
+ *
+ * @clc_reg    : LRME CLC registers
+ * @bus_rd_reg : LRME FE registers
+ * @bus_wr_reg : LRME WE registers
+ * @titan_reg  : LRME top reisters
+ */
+struct cam_lrme_hw_info {
+	struct cam_lrme_clc_reg clc_reg;
+	struct cam_lrme_bus_rd_hw_info bus_rd_reg;
+	struct cam_lrme_bus_wr_hw_info bus_wr_reg;
+	struct cam_lrme_titan_reg titan_reg;
+};
+
+int cam_lrme_hw_process_irq(void *priv, void *data);
+int cam_lrme_hw_submit_req(void *hw_priv, void *hw_submit_args,
+	uint32_t arg_size);
+int cam_lrme_hw_reset(void *hw_priv, void *reset_core_args, uint32_t arg_size);
+int cam_lrme_hw_stop(void *hw_priv, void *stop_args, uint32_t arg_size);
+int cam_lrme_hw_get_caps(void *hw_priv, void *get_hw_cap_args,
+	uint32_t arg_size);
+irqreturn_t cam_lrme_hw_irq(int irq_num, void *data);
+int cam_lrme_hw_process_cmd(void *hw_priv, uint32_t cmd_type,
+	void *cmd_args, uint32_t arg_size);
+int cam_lrme_hw_util_get_caps(struct cam_hw_info *lrme_hw,
+	struct cam_lrme_dev_cap *hw_caps);
+int cam_lrme_hw_start(void *hw_priv, void *hw_init_args, uint32_t arg_size);
+int cam_lrme_hw_flush(void *hw_priv, void *hw_flush_args, uint32_t arg_size);
+void cam_lrme_set_irq(struct cam_hw_info *lrme_hw, enum cam_lrme_irq_set set);
+
+#endif /* _CAM_LRME_HW_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c
new file mode 100644
index 0000000..2e63752
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_dev.c
@@ -0,0 +1,320 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_req_mgr.h>
+
+#include "cam_subdev.h"
+#include "cam_lrme_hw_intf.h"
+#include "cam_lrme_hw_core.h"
+#include "cam_lrme_hw_soc.h"
+#include "cam_lrme_hw_reg.h"
+#include "cam_req_mgr_workq.h"
+#include "cam_lrme_hw_mgr.h"
+#include "cam_mem_mgr_api.h"
+#include "cam_smmu_api.h"
+
+#define CAM_LRME_HW_WORKQ_NUM_TASK 30
+
+static int cam_lrme_hw_dev_util_cdm_acquire(struct cam_lrme_core *lrme_core,
+	struct cam_hw_info *lrme_hw)
+{
+	int rc, i;
+	struct cam_cdm_bl_request *cdm_cmd;
+	struct cam_cdm_acquire_data cdm_acquire;
+	struct cam_lrme_cdm_info *hw_cdm_info;
+
+	hw_cdm_info = kzalloc(sizeof(struct cam_lrme_cdm_info),
+		GFP_KERNEL);
+	if (!hw_cdm_info) {
+		CAM_ERR(CAM_LRME, "No memory for hw_cdm_info");
+		return -ENOMEM;
+	}
+
+	cdm_cmd = kzalloc((sizeof(struct cam_cdm_bl_request) +
+		((CAM_LRME_MAX_HW_ENTRIES - 1) *
+		sizeof(struct cam_cdm_bl_cmd))), GFP_KERNEL);
+	if (!cdm_cmd) {
+		CAM_ERR(CAM_LRME, "No memory for cdm_cmd");
+		kfree(hw_cdm_info);
+		return -ENOMEM;
+	}
+
+	memset(&cdm_acquire, 0, sizeof(cdm_acquire));
+	strlcpy(cdm_acquire.identifier, "lrmecdm", sizeof("lrmecdm"));
+	cdm_acquire.cell_index = lrme_hw->soc_info.index;
+	cdm_acquire.handle = 0;
+	cdm_acquire.userdata = hw_cdm_info;
+	cdm_acquire.cam_cdm_callback = NULL;
+	cdm_acquire.id = CAM_CDM_VIRTUAL;
+	cdm_acquire.base_array_cnt = lrme_hw->soc_info.num_reg_map;
+	for (i = 0; i < lrme_hw->soc_info.num_reg_map; i++)
+		cdm_acquire.base_array[i] = &lrme_hw->soc_info.reg_map[i];
+
+	rc = cam_cdm_acquire(&cdm_acquire);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Can't acquire cdm");
+		goto error;
+	}
+
+	hw_cdm_info->cdm_cmd = cdm_cmd;
+	hw_cdm_info->cdm_ops = cdm_acquire.ops;
+	hw_cdm_info->cdm_handle = cdm_acquire.handle;
+
+	lrme_core->hw_cdm_info = hw_cdm_info;
+	CAM_DBG(CAM_LRME, "cdm acquire done");
+
+	return 0;
+error:
+	kfree(cdm_cmd);
+	kfree(hw_cdm_info);
+	return rc;
+}
+
+static int cam_lrme_hw_dev_probe(struct platform_device *pdev)
+{
+	struct cam_hw_info *lrme_hw;
+	struct cam_hw_intf lrme_hw_intf;
+	struct cam_lrme_core *lrme_core;
+	const struct of_device_id *match_dev = NULL;
+	struct cam_lrme_hw_info *hw_info;
+	int rc, i;
+
+	lrme_hw = kzalloc(sizeof(struct cam_hw_info), GFP_KERNEL);
+	if (!lrme_hw) {
+		CAM_ERR(CAM_LRME, "No memory to create lrme_hw");
+		return -ENOMEM;
+	}
+
+	lrme_core = kzalloc(sizeof(struct cam_lrme_core), GFP_KERNEL);
+	if (!lrme_core) {
+		CAM_ERR(CAM_LRME, "No memory to create lrme_core");
+		kfree(lrme_hw);
+		return -ENOMEM;
+	}
+
+	lrme_hw->core_info = lrme_core;
+	lrme_hw->hw_state = CAM_HW_STATE_POWER_DOWN;
+	lrme_hw->soc_info.pdev = pdev;
+	lrme_hw->soc_info.dev = &pdev->dev;
+	lrme_hw->soc_info.dev_name = pdev->name;
+	lrme_hw->open_count = 0;
+	lrme_core->state = CAM_LRME_CORE_STATE_INIT;
+
+	mutex_init(&lrme_hw->hw_mutex);
+	spin_lock_init(&lrme_hw->hw_lock);
+	init_completion(&lrme_hw->hw_complete);
+	init_completion(&lrme_core->reset_complete);
+
+	rc = cam_req_mgr_workq_create("cam_lrme_hw_worker",
+		CAM_LRME_HW_WORKQ_NUM_TASK,
+		&lrme_core->work, CRM_WORKQ_USAGE_IRQ);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Unable to create a workq, rc=%d", rc);
+		goto free_memory;
+	}
+
+	for (i = 0; i < CAM_LRME_HW_WORKQ_NUM_TASK; i++)
+		lrme_core->work->task.pool[i].payload =
+			&lrme_core->work_data[i];
+
+	match_dev = of_match_device(pdev->dev.driver->of_match_table,
+		&pdev->dev);
+	if (!match_dev || !match_dev->data) {
+		CAM_ERR(CAM_LRME, "No Of_match data, %pK", match_dev);
+		rc = -EINVAL;
+		goto destroy_workqueue;
+	}
+	hw_info = (struct cam_lrme_hw_info *)match_dev->data;
+	lrme_core->hw_info = hw_info;
+
+	rc = cam_lrme_soc_init_resources(&lrme_hw->soc_info,
+		cam_lrme_hw_irq, lrme_hw);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to init soc, rc=%d", rc);
+		goto destroy_workqueue;
+	}
+
+	rc = cam_lrme_hw_dev_util_cdm_acquire(lrme_core, lrme_hw);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to acquire cdm");
+		goto deinit_platform_res;
+	}
+
+	rc = cam_smmu_get_handle("lrme", &lrme_core->device_iommu.non_secure);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Get iommu handle failed");
+		goto release_cdm;
+	}
+
+	rc = cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_ATTACH);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "LRME attach iommu handle failed, rc=%d", rc);
+		goto destroy_smmu;
+	}
+
+	rc = cam_lrme_hw_start(lrme_hw, NULL, 0);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to hw init, rc=%d", rc);
+		goto detach_smmu;
+	}
+
+	rc = cam_lrme_hw_util_get_caps(lrme_hw, &lrme_core->hw_caps);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to get hw caps, rc=%d", rc);
+		if (cam_lrme_hw_stop(lrme_hw, NULL, 0))
+			CAM_ERR(CAM_LRME, "Failed in hw deinit");
+		goto detach_smmu;
+	}
+
+	rc = cam_lrme_hw_stop(lrme_hw, NULL, 0);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to deinit hw, rc=%d", rc);
+		goto detach_smmu;
+	}
+
+	lrme_core->hw_idx = lrme_hw->soc_info.index;
+	lrme_hw_intf.hw_priv = lrme_hw;
+	lrme_hw_intf.hw_idx = lrme_hw->soc_info.index;
+	lrme_hw_intf.hw_ops.get_hw_caps = cam_lrme_hw_get_caps;
+	lrme_hw_intf.hw_ops.init = NULL;
+	lrme_hw_intf.hw_ops.deinit = NULL;
+	lrme_hw_intf.hw_ops.reset = cam_lrme_hw_reset;
+	lrme_hw_intf.hw_ops.reserve = NULL;
+	lrme_hw_intf.hw_ops.release = NULL;
+	lrme_hw_intf.hw_ops.start = cam_lrme_hw_start;
+	lrme_hw_intf.hw_ops.stop = cam_lrme_hw_stop;
+	lrme_hw_intf.hw_ops.read = NULL;
+	lrme_hw_intf.hw_ops.write = NULL;
+	lrme_hw_intf.hw_ops.process_cmd = cam_lrme_hw_process_cmd;
+	lrme_hw_intf.hw_type = CAM_HW_LRME;
+
+	rc = cam_cdm_get_iommu_handle("lrmecdm", &lrme_core->cdm_iommu);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to acquire the CDM iommu handles");
+		goto detach_smmu;
+	}
+
+	rc = cam_lrme_mgr_register_device(&lrme_hw_intf,
+		&lrme_core->device_iommu,
+		&lrme_core->cdm_iommu);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to register device");
+		goto detach_smmu;
+	}
+
+	platform_set_drvdata(pdev, lrme_hw);
+	CAM_DBG(CAM_LRME, "LRME-%d probe successful", lrme_hw_intf.hw_idx);
+
+	return rc;
+
+detach_smmu:
+	cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_DETACH);
+destroy_smmu:
+	cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure);
+release_cdm:
+	cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle);
+	kfree(lrme_core->hw_cdm_info->cdm_cmd);
+	kfree(lrme_core->hw_cdm_info);
+deinit_platform_res:
+	if (cam_lrme_soc_deinit_resources(&lrme_hw->soc_info))
+		CAM_ERR(CAM_LRME, "Failed in soc deinit");
+	mutex_destroy(&lrme_hw->hw_mutex);
+destroy_workqueue:
+	cam_req_mgr_workq_destroy(&lrme_core->work);
+free_memory:
+	mutex_destroy(&lrme_hw->hw_mutex);
+	kfree(lrme_hw);
+	kfree(lrme_core);
+
+	return rc;
+}
+
+static int cam_lrme_hw_dev_remove(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct cam_hw_info *lrme_hw;
+	struct cam_lrme_core *lrme_core;
+
+	lrme_hw = platform_get_drvdata(pdev);
+	if (!lrme_hw) {
+		CAM_ERR(CAM_LRME, "Invalid lrme_hw from fd_hw_intf");
+		rc = -ENODEV;
+		goto deinit_platform_res;
+	}
+
+	lrme_core = (struct cam_lrme_core *)lrme_hw->core_info;
+	if (!lrme_core) {
+		CAM_ERR(CAM_LRME, "Invalid lrme_core from fd_hw");
+		rc = -EINVAL;
+		goto deinit_platform_res;
+	}
+
+	cam_smmu_ops(lrme_core->device_iommu.non_secure, CAM_SMMU_DETACH);
+	cam_smmu_destroy_handle(lrme_core->device_iommu.non_secure);
+	cam_cdm_release(lrme_core->hw_cdm_info->cdm_handle);
+	cam_lrme_mgr_deregister_device(lrme_core->hw_idx);
+
+	kfree(lrme_core->hw_cdm_info->cdm_cmd);
+	kfree(lrme_core->hw_cdm_info);
+	kfree(lrme_core);
+
+deinit_platform_res:
+	rc = cam_lrme_soc_deinit_resources(&lrme_hw->soc_info);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Error in LRME soc deinit, rc=%d", rc);
+
+	mutex_destroy(&lrme_hw->hw_mutex);
+	kfree(lrme_hw);
+
+	return rc;
+}
+
+static const struct of_device_id cam_lrme_hw_dt_match[] = {
+	{
+		.compatible = "qcom,lrme",
+		.data = &cam_lrme10_hw_info,
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, cam_lrme_hw_dt_match);
+
+static struct platform_driver cam_lrme_hw_driver = {
+	.probe = cam_lrme_hw_dev_probe,
+	.remove = cam_lrme_hw_dev_remove,
+	.driver = {
+		.name = "cam_lrme_hw",
+		.owner = THIS_MODULE,
+		.of_match_table = cam_lrme_hw_dt_match,
+	},
+};
+
+static int __init cam_lrme_hw_init_module(void)
+{
+	return platform_driver_register(&cam_lrme_hw_driver);
+}
+
+static void __exit cam_lrme_hw_exit_module(void)
+{
+	platform_driver_unregister(&cam_lrme_hw_driver);
+}
+
+module_init(cam_lrme_hw_init_module);
+module_exit(cam_lrme_hw_exit_module);
+MODULE_DESCRIPTION("CAM LRME HW driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h
new file mode 100644
index 0000000..d16b174
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_intf.h
@@ -0,0 +1,200 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_HW_INTF_H_
+#define _CAM_LRME_HW_INTF_H_
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <media/cam_cpas.h>
+#include <media/cam_req_mgr.h>
+#include <media/cam_lrme.h>
+
+#include "cam_io_util.h"
+#include "cam_soc_util.h"
+#include "cam_hw.h"
+#include "cam_hw_intf.h"
+#include "cam_subdev.h"
+#include "cam_cpas_api.h"
+#include "cam_hw_mgr_intf.h"
+#include "cam_debug_util.h"
+
+
+#define CAM_LRME_MAX_IO_BUFFER 2
+#define CAM_LRME_MAX_HW_ENTRIES 5
+
+#define CAM_LRME_BASE_IDX 0
+
+/**
+ *  enum cam_lrme_hw_type : Enum for LRME HW type
+ *
+ * @CAM_HW_LRME : LRME HW type
+ */
+enum cam_lrme_hw_type {
+	CAM_HW_LRME,
+};
+
+/**
+ * enum cam_lrme_cb_type : HW manager call back type
+ *
+ * @CAM_LRME_CB_BUF_DONE        : Indicate buf done has been generated
+ * @CAM_LRME_CB_COMP_REG_UPDATE : Indicate receiving WE comp reg update
+ * @CAM_LRME_CB_PUT_FRAME       : Request HW manager to put back the frame
+ * @CAM_LRME_CB_ERROR           : Indicate error irq has been generated
+ */
+enum cam_lrme_cb_type {
+	CAM_LRME_CB_BUF_DONE = 1,
+	CAM_LRME_CB_COMP_REG_UPDATE = 1 << 1,
+	CAM_LRME_CB_PUT_FRAME = 1 << 2,
+	CAM_LRME_CB_ERROR = 1 << 3,
+};
+
+/**
+ * enum cam_lrme_hw_cmd_type : HW CMD type
+ *
+ * @CAM_LRME_HW_CMD_prepare_hw_update : Prepare HW update
+ * @CAM_LRME_HW_CMD_REGISTER_CB       : register HW manager callback
+ * @CAM_LRME_HW_CMD_SUBMIT            : Submit frame to HW
+ */
+enum cam_lrme_hw_cmd_type {
+	CAM_LRME_HW_CMD_PREPARE_HW_UPDATE,
+	CAM_LRME_HW_CMD_REGISTER_CB,
+	CAM_LRME_HW_CMD_SUBMIT,
+};
+
+/**
+ * enum cam_lrme_hw_reset_type : Type of reset
+ *
+ * @CAM_LRME_HW_RESET_TYPE_HW_RESET : HW reset
+ * @CAM_LRME_HW_RESET_TYPE_SW_RESET : SW reset
+ */
+enum cam_lrme_hw_reset_type {
+	CAM_LRME_HW_RESET_TYPE_HW_RESET,
+	CAM_LRME_HW_RESET_TYPE_SW_RESET,
+};
+
+/**
+ *struct cam_lrme_frame_request : LRME frame request
+ *
+ * @frame_list            : List head
+ * @req_id                : Request ID
+ * @ctxt_to_hw_map        : Information about context id, priority and device id
+ * @hw_device             : Pointer to HW device
+ * @hw_update_entries     : List of hw_update_entries
+ * @num_hw_update_entries : number of hw_update_entries
+ */
+struct cam_lrme_frame_request {
+	struct list_head           frame_list;
+	uint64_t                   req_id;
+	void                      *ctxt_to_hw_map;
+	struct cam_lrme_device    *hw_device;
+	struct cam_hw_update_entry hw_update_entries[CAM_LRME_MAX_HW_ENTRIES];
+	uint32_t                   num_hw_update_entries;
+};
+
+/**
+ * struct cam_lrme_hw_io_buffer : IO buffer information
+ *
+ * @valid     : Indicate whether this IO config is valid
+ * @io_cfg    : Pointer to IO configuration
+ * @num_buf   : Number of buffers
+ * @num_plane : Number of planes
+ * @io_addr   : List of IO address
+ */
+struct cam_lrme_hw_io_buffer {
+	bool                   valid;
+	struct cam_buf_io_cfg *io_cfg;
+	uint32_t               num_buf;
+	uint32_t               num_plane;
+	uint64_t               io_addr[CAM_PACKET_MAX_PLANES];
+};
+
+/**
+ * struct cam_lrme_hw_cmd_config_args : Args for prepare HW update
+ *
+ * @hw_device       : Pointer to HW device
+ * @input_buf       : List of input buffers
+ * @output_buf      : List of output buffers
+ * @cmd_buf_addr    : Pointer to available KMD buffer
+ * @size            : Available KMD buffer size
+ * @config_buf_size : Size used to prepare update
+ */
+struct cam_lrme_hw_cmd_config_args {
+	struct cam_lrme_device *hw_device;
+	struct cam_lrme_hw_io_buffer input_buf[CAM_LRME_MAX_IO_BUFFER];
+	struct cam_lrme_hw_io_buffer output_buf[CAM_LRME_MAX_IO_BUFFER];
+	uint32_t *cmd_buf_addr;
+	uint32_t size;
+	uint32_t config_buf_size;
+};
+
+/**
+ * struct cam_lrme_hw_flush_args : Args for flush HW
+ *
+ * @ctxt_to_hw_map : Identity of context
+ * @req_to_flush   : Pointer to the frame need to flush in
+ *                   case of single frame flush
+ * @flush_type     : Flush type
+ */
+struct cam_lrme_hw_flush_args {
+	void                          *ctxt_to_hw_map;
+	struct cam_lrme_frame_request *req_to_flush;
+	uint32_t                       flush_type;
+};
+
+/**
+ * struct cam_lrme_hw_reset_args : Args for reset HW
+ *
+ * @reset_type : Enum cam_lrme_hw_reset_type
+ */
+struct cam_lrme_hw_reset_args {
+	uint32_t reset_type;
+};
+
+/**
+ * struct cam_lrme_hw_cb_args : HW manager callback args
+ *
+ * @cb_type   : Callback event type
+ * @frame_req : Pointer to the frame associated with the cb
+ */
+struct cam_lrme_hw_cb_args {
+	uint32_t                       cb_type;
+	struct cam_lrme_frame_request *frame_req;
+};
+
+/**
+ * struct cam_lrme_hw_cmd_set_cb : Args for set callback function
+ *
+ * @cam_lrme_hw_mgr_cb : Callback function pointer
+ * @data               : Data sent along with callback function
+ */
+struct cam_lrme_hw_cmd_set_cb {
+	 int (*cam_lrme_hw_mgr_cb)(void *data,
+		struct cam_lrme_hw_cb_args *args);
+	 void *data;
+};
+
+/**
+ * struct cam_lrme_hw_submit_args : Args for submit request
+ *
+ * @hw_update_entries     : List of hw update entries used to program registers
+ * @num_hw_update_entries : Number of hw update entries
+ * @frame_req             : Pointer to the frame request
+ */
+struct cam_lrme_hw_submit_args {
+	 struct cam_hw_update_entry    *hw_update_entries;
+	 uint32_t            num_hw_update_entries;
+	 struct cam_lrme_frame_request *frame_req;
+};
+
+#endif /* _CAM_LRME_HW_INTF_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h
new file mode 100644
index 0000000..39cfde7
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_reg.h
@@ -0,0 +1,193 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_HW_REG_H_
+#define _CAM_LRME_HW_REG_H_
+
+#include "cam_lrme_hw_core.h"
+
+static struct cam_lrme_hw_info cam_lrme10_hw_info = {
+	.clc_reg = {
+		.clc_hw_version                 = 0x00000000,
+		.clc_hw_status                  = 0x00000004,
+		.clc_hw_status_dbg              = 0x00000008,
+		.clc_module_cfg                 = 0x00000060,
+		.clc_moduleformat               = 0x000000A8,
+		.clc_rangestep                  = 0x00000068,
+		.clc_offset                     = 0x0000006C,
+		.clc_maxallowedsad              = 0x00000070,
+		.clc_minallowedtarmad           = 0x00000074,
+		.clc_meaningfulsaddiff          = 0x00000078,
+		.clc_minsaddiffdenom            = 0x0000007C,
+		.clc_robustnessmeasuredistmap_0 = 0x00000080,
+		.clc_robustnessmeasuredistmap_1 = 0x00000084,
+		.clc_robustnessmeasuredistmap_2 = 0x00000088,
+		.clc_robustnessmeasuredistmap_3 = 0x0000008C,
+		.clc_robustnessmeasuredistmap_4 = 0x00000090,
+		.clc_robustnessmeasuredistmap_5 = 0x00000094,
+		.clc_robustnessmeasuredistmap_6 = 0x00000098,
+		.clc_robustnessmeasuredistmap_7 = 0x0000009C,
+		.clc_ds_crop_horizontal         = 0x000000A0,
+		.clc_ds_crop_vertical           = 0x000000A4,
+		.clc_tar_pd_unpacker            = 0x000000AC,
+		.clc_ref_pd_unpacker            = 0x000000B0,
+		.clc_sw_override                = 0x000000B4,
+		.clc_tar_height                 = 0x000000B8,
+		.clc_ref_height                 = 0x000000BC,
+		.clc_test_bus_ctrl              = 0x000001F8,
+		.clc_spare                      = 0x000001FC,
+	},
+	.bus_rd_reg = {
+		.common_reg = {
+			.hw_version     = 0x00000200,
+			.hw_capability  = 0x00000204,
+			.sw_reset       = 0x00000208,
+			.cgc_override   = 0x0000020C,
+			.irq_mask       = 0x00000210,
+			.irq_clear      = 0x00000214,
+			.irq_cmd        = 0x00000218,
+			.irq_status     = 0x0000021C,
+			.cmd            = 0x00000220,
+			.irq_set        = 0x00000224,
+			.misr_reset     = 0x0000022C,
+			.security_cfg   = 0x00000230,
+			.pwr_iso_cfg    = 0x00000234,
+			.pwr_iso_seed   = 0x00000238,
+			.test_bus_ctrl  = 0x00000248,
+			.spare          = 0x0000024C,
+		},
+		.bus_client_reg = {
+			/* bus client 0 */
+			{
+				.core_cfg                = 0x00000250,
+				.ccif_meta_data          = 0x00000254,
+				.addr_image              = 0x00000258,
+				.rd_buffer_size          = 0x0000025C,
+				.rd_stride               = 0x00000260,
+				.unpack_cfg_0            = 0x00000264,
+				.latency_buff_allocation = 0x00000278,
+				.burst_limit_cfg         = 0x00000280,
+				.misr_cfg_0              = 0x00000284,
+				.misr_cfg_1              = 0x00000288,
+				.misr_rd_val             = 0x0000028C,
+				.debug_status_cfg        = 0x00000290,
+				.debug_status_0          = 0x00000294,
+				.debug_status_1          = 0x00000298,
+			},
+			/* bus client 1 */
+			{
+				.core_cfg                = 0x000002F0,
+				.ccif_meta_data          = 0x000002F4,
+				.addr_image              = 0x000002F8,
+				.rd_buffer_size          = 0x000002FC,
+				.rd_stride               = 0x00000300,
+				.unpack_cfg_0            = 0x00000304,
+				.latency_buff_allocation = 0x00000318,
+				.burst_limit_cfg         = 0x00000320,
+				.misr_cfg_0              = 0x00000324,
+				.misr_cfg_1              = 0x00000328,
+				.misr_rd_val             = 0x0000032C,
+				.debug_status_cfg        = 0x00000330,
+				.debug_status_0          = 0x00000334,
+				.debug_status_1          = 0x00000338,
+			},
+		},
+	},
+	.bus_wr_reg = {
+		.common_reg = {
+			.hw_version        = 0x00000500,
+			.hw_capability     = 0x00000504,
+			.sw_reset          = 0x00000508,
+			.cgc_override      = 0x0000050C,
+			.misr_reset        = 0x000005C8,
+			.pwr_iso_cfg       = 0x000005CC,
+			.test_bus_ctrl     = 0x0000061C,
+			.composite_mask_0  = 0x00000510,
+			.irq_mask_0        = 0x00000544,
+			.irq_mask_1        = 0x00000548,
+			.irq_clear_0       = 0x00000550,
+			.irq_clear_1       = 0x00000554,
+			.irq_status_0      = 0x0000055C,
+			.irq_status_1      = 0x00000560,
+			.irq_cmd           = 0x00000568,
+			.irq_set_0         = 0x000005BC,
+			.irq_set_1         = 0x000005C0,
+			.addr_fifo_status  = 0x000005A8,
+			.frame_header_cfg0 = 0x000005AC,
+			.frame_header_cfg1 = 0x000005B0,
+			.spare             = 0x00000620,
+		},
+		.bus_client_reg = {
+			/* bus client 0 */
+			{
+				.status_0                  = 0x00000700,
+				.status_1                  = 0x00000704,
+				.cfg                       = 0x00000708,
+				.addr_frame_header         = 0x0000070C,
+				.frame_header_cfg          = 0x00000710,
+				.addr_image                = 0x00000714,
+				.addr_image_offset         = 0x00000718,
+				.buffer_width_cfg          = 0x0000071C,
+				.buffer_height_cfg         = 0x00000720,
+				.packer_cfg                = 0x00000724,
+				.wr_stride                 = 0x00000728,
+				.irq_subsample_cfg_period  = 0x00000748,
+				.irq_subsample_cfg_pattern = 0x0000074C,
+				.burst_limit_cfg           = 0x0000075C,
+				.misr_cfg                  = 0x00000760,
+				.misr_rd_word_sel          = 0x00000764,
+				.misr_val                  = 0x00000768,
+				.debug_status_cfg          = 0x0000076C,
+				.debug_status_0            = 0x00000770,
+				.debug_status_1            = 0x00000774,
+			},
+			/* bus client 1 */
+			{
+				.status_0                  = 0x00000800,
+				.status_1                  = 0x00000804,
+				.cfg                       = 0x00000808,
+				.addr_frame_header         = 0x0000080C,
+				.frame_header_cfg          = 0x00000810,
+				.addr_image                = 0x00000814,
+				.addr_image_offset         = 0x00000818,
+				.buffer_width_cfg          = 0x0000081C,
+				.buffer_height_cfg         = 0x00000820,
+				.packer_cfg                = 0x00000824,
+				.wr_stride                 = 0x00000828,
+				.irq_subsample_cfg_period  = 0x00000848,
+				.irq_subsample_cfg_pattern = 0x0000084C,
+				.burst_limit_cfg           = 0x0000085C,
+				.misr_cfg                  = 0x00000860,
+				.misr_rd_word_sel          = 0x00000864,
+				.misr_val                  = 0x00000868,
+				.debug_status_cfg          = 0x0000086C,
+				.debug_status_0            = 0x00000870,
+				.debug_status_1            = 0x00000874,
+			},
+		},
+	},
+	.titan_reg = {
+		.top_hw_version        = 0x00000900,
+		.top_titan_version     = 0x00000904,
+		.top_rst_cmd           = 0x00000908,
+		.top_core_clk_cfg      = 0x00000920,
+		.top_irq_status        = 0x0000090C,
+		.top_irq_mask          = 0x00000910,
+		.top_irq_clear         = 0x00000914,
+		.top_irq_set           = 0x00000918,
+		.top_irq_cmd           = 0x0000091C,
+		.top_violation_status  = 0x00000924,
+		.top_spare             = 0x000009FC,
+	},
+};
+
+#endif /* _CAM_LRME_HW_REG_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c
new file mode 100644
index 0000000..75de0dd
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.c
@@ -0,0 +1,158 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include "cam_lrme_hw_core.h"
+#include "cam_lrme_hw_soc.h"
+
+
+int cam_lrme_soc_enable_resources(struct cam_hw_info *lrme_hw)
+{
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_soc_private *soc_private =
+		(struct cam_lrme_soc_private *)soc_info->soc_private;
+	struct cam_ahb_vote ahb_vote;
+	struct cam_axi_vote axi_vote;
+	int rc = 0;
+
+	ahb_vote.type = CAM_VOTE_ABSOLUTE;
+	ahb_vote.vote.level = CAM_SVS_VOTE;
+	axi_vote.compressed_bw = 7200000;
+	axi_vote.uncompressed_bw = 7200000;
+	rc = cam_cpas_start(soc_private->cpas_handle, &ahb_vote, &axi_vote);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to start cpas, rc %d", rc);
+		return -EFAULT;
+	}
+
+	rc = cam_soc_util_enable_platform_resource(soc_info, true, CAM_SVS_VOTE,
+		true);
+	if (rc) {
+		CAM_ERR(CAM_LRME,
+			"Failed to enable platform resource, rc %d", rc);
+		goto stop_cpas;
+	}
+
+	cam_lrme_set_irq(lrme_hw, CAM_LRME_IRQ_ENABLE);
+
+	return rc;
+
+stop_cpas:
+	if (cam_cpas_stop(soc_private->cpas_handle))
+		CAM_ERR(CAM_LRME, "Failed to stop cpas");
+
+	return rc;
+}
+
+int cam_lrme_soc_disable_resources(struct cam_hw_info *lrme_hw)
+{
+	struct cam_hw_soc_info *soc_info = &lrme_hw->soc_info;
+	struct cam_lrme_soc_private *soc_private;
+	int rc = 0;
+
+	soc_private = soc_info->soc_private;
+
+	cam_lrme_set_irq(lrme_hw, CAM_LRME_IRQ_DISABLE);
+
+	rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed to disable platform resource");
+		return rc;
+	}
+	rc = cam_cpas_stop(soc_private->cpas_handle);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Failed to stop cpas");
+
+	return rc;
+}
+
+int cam_lrme_soc_init_resources(struct cam_hw_soc_info *soc_info,
+	irq_handler_t irq_handler, void *private_data)
+{
+	struct cam_lrme_soc_private *soc_private;
+	struct cam_cpas_register_params cpas_register_param;
+	int rc;
+
+	rc = cam_soc_util_get_dt_properties(soc_info);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed in get_dt_properties, rc=%d", rc);
+		return rc;
+	}
+
+	rc = cam_soc_util_request_platform_resource(soc_info, irq_handler,
+		private_data);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "Failed in request_platform_resource rc=%d",
+			rc);
+		return rc;
+	}
+
+	soc_private = kzalloc(sizeof(struct cam_lrme_soc_private), GFP_KERNEL);
+	if (!soc_private) {
+		rc = -ENOMEM;
+		goto release_res;
+	}
+	soc_info->soc_private = soc_private;
+
+	memset(&cpas_register_param, 0, sizeof(cpas_register_param));
+	strlcpy(cpas_register_param.identifier,
+		"lrmecpas", CAM_HW_IDENTIFIER_LENGTH);
+	cpas_register_param.cell_index = soc_info->index;
+	cpas_register_param.dev = &soc_info->pdev->dev;
+	cpas_register_param.userdata = private_data;
+	cpas_register_param.cam_cpas_client_cb = NULL;
+
+	rc = cam_cpas_register_client(&cpas_register_param);
+	if (rc) {
+		CAM_ERR(CAM_LRME, "CPAS registration failed");
+		goto free_soc_private;
+	}
+	soc_private->cpas_handle = cpas_register_param.client_handle;
+	CAM_DBG(CAM_LRME, "CPAS handle=%d", soc_private->cpas_handle);
+
+	return rc;
+
+free_soc_private:
+	kfree(soc_info->soc_private);
+	soc_info->soc_private = NULL;
+release_res:
+	cam_soc_util_release_platform_resource(soc_info);
+
+	return rc;
+}
+
+int cam_lrme_soc_deinit_resources(struct cam_hw_soc_info *soc_info)
+{
+	struct cam_lrme_soc_private *soc_private =
+		(struct cam_lrme_soc_private *)soc_info->soc_private;
+	int rc;
+
+	rc = cam_cpas_unregister_client(soc_private->cpas_handle);
+	if (rc)
+		CAM_ERR(CAM_LRME, "Unregister cpas failed, handle=%d, rc=%d",
+			soc_private->cpas_handle, rc);
+
+	rc = cam_soc_util_release_platform_resource(soc_info);
+	if (rc)
+		CAM_ERR(CAM_LRME, "release platform failed, rc=%d", rc);
+
+	kfree(soc_info->soc_private);
+	soc_info->soc_private = NULL;
+
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h
new file mode 100644
index 0000000..44e8486
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_lrme/lrme_hw_mgr/lrme_hw/cam_lrme_hw_soc.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _CAM_LRME_HW_SOC_H_
+#define _CAM_LRME_HW_SOC_H_
+
+#include "cam_soc_util.h"
+
+struct cam_lrme_soc_private {
+	uint32_t cpas_handle;
+};
+
+int cam_lrme_soc_enable_resources(struct cam_hw_info *lrme_hw);
+int cam_lrme_soc_disable_resources(struct cam_hw_info *lrme_hw);
+int cam_lrme_soc_init_resources(struct cam_hw_soc_info *soc_info,
+	irq_handler_t irq_handler, void *private_data);
+int cam_lrme_soc_deinit_resources(struct cam_hw_soc_info *soc_info);
+
+#endif /* _CAM_LRME_HW_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
index c150244..6ad0934 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.c
@@ -14,6 +14,7 @@
 #include <linux/types.h>
 #include <linux/mutex.h>
 #include <linux/msm_ion.h>
+#include <linux/slab.h>
 #include <asm/cacheflush.h>
 
 #include "cam_req_mgr_util.h"
@@ -52,6 +53,8 @@
 		rc = DMA_FROM_DEVICE;
 	else if (flags & CAM_MEM_FLAG_HW_READ_WRITE)
 		rc = DMA_BIDIRECTIONAL;
+	else if (flags & CAM_MEM_FLAG_PROTECTED_MODE)
+		rc = DMA_BIDIRECTIONAL;
 
 	return rc;
 }
@@ -113,56 +116,6 @@
 	return rc;
 }
 
-static int cam_mem_mgr_cleanup_table(void)
-{
-	int i;
-
-	mutex_lock(&tbl.m_lock);
-	for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
-		if (!tbl.bufq[i].active) {
-			CAM_DBG(CAM_CRM,
-				"Buffer inactive at idx=%d, continuing", i);
-			continue;
-		} else {
-			CAM_ERR(CAM_CRM,
-				"Active buffer at idx=%d, possible leak", i);
-		}
-
-		mutex_lock(&tbl.bufq[i].q_lock);
-		ion_free(tbl.client, tbl.bufq[i].i_hdl);
-		tbl.bufq[i].fd = -1;
-		tbl.bufq[i].flags = 0;
-		tbl.bufq[i].buf_handle = -1;
-		tbl.bufq[i].vaddr = 0;
-		tbl.bufq[i].len = 0;
-		memset(tbl.bufq[i].hdls, 0,
-			sizeof(int32_t) * tbl.bufq[i].num_hdl);
-		tbl.bufq[i].num_hdl = 0;
-		tbl.bufq[i].i_hdl = NULL;
-		tbl.bufq[i].active = false;
-		mutex_unlock(&tbl.bufq[i].q_lock);
-		mutex_destroy(&tbl.bufq[i].q_lock);
-	}
-	bitmap_zero(tbl.bitmap, tbl.bits);
-	/* We need to reserve slot 0 because 0 is invalid */
-	set_bit(0, tbl.bitmap);
-	mutex_unlock(&tbl.m_lock);
-
-	return 0;
-}
-
-void cam_mem_mgr_deinit(void)
-{
-	cam_mem_mgr_cleanup_table();
-	mutex_lock(&tbl.m_lock);
-	bitmap_zero(tbl.bitmap, tbl.bits);
-	kfree(tbl.bitmap);
-	tbl.bitmap = NULL;
-	cam_mem_util_client_destroy();
-	mutex_unlock(&tbl.m_lock);
-	mutex_destroy(&tbl.m_lock);
-}
-
 static int32_t cam_mem_get_slot(void)
 {
 	int32_t idx;
@@ -211,10 +164,16 @@
 		goto handle_mismatch;
 	}
 
-	rc = cam_smmu_get_iova(mmu_handle,
-		tbl.bufq[idx].fd,
-		iova_ptr,
-		len_ptr);
+	if (CAM_MEM_MGR_IS_SECURE_HDL(buf_handle))
+		rc = cam_smmu_get_stage2_iova(mmu_handle,
+			tbl.bufq[idx].fd,
+			iova_ptr,
+			len_ptr);
+	else
+		rc = cam_smmu_get_iova(mmu_handle,
+			tbl.bufq[idx].fd,
+			iova_ptr,
+			len_ptr);
 	if (rc < 0)
 		CAM_ERR(CAM_CRM, "fail to get buf hdl :%d", buf_handle);
 
@@ -341,7 +300,40 @@
 }
 EXPORT_SYMBOL(cam_mem_mgr_cache_ops);
 
-static int cam_mem_util_get_ion_buffer(size_t len,
+static int cam_mem_util_get_dma_buf(size_t len,
+	size_t align,
+	unsigned int heap_id_mask,
+	unsigned int flags,
+	struct ion_handle **hdl,
+	struct dma_buf **buf)
+{
+	int rc = 0;
+
+	if (!hdl || !buf) {
+		CAM_ERR(CAM_CRM, "Invalid params");
+		return -EINVAL;
+	}
+
+	*hdl = ion_alloc(tbl.client, len, align, heap_id_mask, flags);
+	if (IS_ERR_OR_NULL(*hdl))
+		return -ENOMEM;
+
+	*buf = ion_share_dma_buf(tbl.client, *hdl);
+	if (IS_ERR_OR_NULL(*buf)) {
+		CAM_ERR(CAM_CRM, "get dma buf fail");
+		rc = -EINVAL;
+		goto get_buf_fail;
+	}
+
+	return rc;
+
+get_buf_fail:
+	ion_free(tbl.client, *hdl);
+	return rc;
+
+}
+
+static int cam_mem_util_get_dma_buf_fd(size_t len,
 	size_t align,
 	unsigned int heap_id_mask,
 	unsigned int flags,
@@ -350,13 +342,18 @@
 {
 	int rc = 0;
 
+	if (!hdl || !fd) {
+		CAM_ERR(CAM_CRM, "Invalid params");
+		return -EINVAL;
+	}
+
 	*hdl = ion_alloc(tbl.client, len, align, heap_id_mask, flags);
 	if (IS_ERR_OR_NULL(*hdl))
 		return -ENOMEM;
 
 	*fd = ion_share_dma_buf_fd(tbl.client, *hdl);
 	if (*fd < 0) {
-		CAM_ERR(CAM_CRM, "dma buf get fd fail");
+		CAM_ERR(CAM_CRM, "get fd fail");
 		rc = -EINVAL;
 		goto get_fd_fail;
 	}
@@ -376,17 +373,19 @@
 	uint32_t ion_flag = 0;
 	int rc;
 
-	if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)
+	if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE) {
 		heap_id = ION_HEAP(ION_SECURE_DISPLAY_HEAP_ID);
-	else
+		ion_flag |= ION_FLAG_SECURE | ION_FLAG_CP_CAMERA;
+	} else {
 		heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+	}
 
 	if (cmd->flags & CAM_MEM_FLAG_CACHE)
 		ion_flag |= ION_FLAG_CACHED;
 	else
 		ion_flag &= ~ION_FLAG_CACHED;
 
-	rc = cam_mem_util_get_ion_buffer(cmd->len,
+	rc = cam_mem_util_get_dma_buf_fd(cmd->len,
 		cmd->align,
 		heap_id,
 		ion_flag,
@@ -466,10 +465,11 @@
 
 	if (flags & CAM_MEM_FLAG_PROTECTED_MODE) {
 		for (i = 0; i < num_hdls; i++) {
-			rc = cam_smmu_map_sec_iova(mmu_hdls[i],
+			rc = cam_smmu_map_stage2_iova(mmu_hdls[i],
 				fd,
 				dir,
-				(dma_addr_t *)hw_vaddr,
+				tbl.client,
+				(ion_phys_addr_t *)hw_vaddr,
 				len);
 
 			if (rc < 0) {
@@ -480,7 +480,7 @@
 		}
 	} else {
 		for (i = 0; i < num_hdls; i++) {
-			rc = cam_smmu_map_iova(mmu_hdls[i],
+			rc = cam_smmu_map_user_iova(mmu_hdls[i],
 				fd,
 				dir,
 				(dma_addr_t *)hw_vaddr,
@@ -498,10 +498,10 @@
 multi_map_fail:
 	if (flags & CAM_MEM_FLAG_PROTECTED_MODE)
 		for (--i; i > 0; i--)
-			cam_smmu_unmap_sec_iova(mmu_hdls[i], fd);
+			cam_smmu_unmap_stage2_iova(mmu_hdls[i], fd);
 	else
 		for (--i; i > 0; i--)
-			cam_smmu_unmap_iova(mmu_hdls[i],
+			cam_smmu_unmap_user_iova(mmu_hdls[i],
 				fd,
 				CAM_SMMU_REGION_IO);
 	return rc;
@@ -543,8 +543,9 @@
 		goto slot_fail;
 	}
 
-	if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE ||
-		cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) {
+	if ((cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) ||
+		(cmd->flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) ||
+		(cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)) {
 
 		enum cam_smmu_region_id region;
 
@@ -568,8 +569,11 @@
 
 	mutex_lock(&tbl.bufq[idx].q_lock);
 	tbl.bufq[idx].fd = ion_fd;
+	tbl.bufq[idx].dma_buf = NULL;
 	tbl.bufq[idx].flags = cmd->flags;
 	tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, ion_fd);
+	if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)
+		CAM_MEM_MGR_SET_SECURE_HDL(tbl.bufq[idx].buf_handle, true);
 	tbl.bufq[idx].kmdvaddr = 0;
 
 	if (cmd->num_hdl > 0)
@@ -630,7 +634,8 @@
 		return -EINVAL;
 	}
 
-	if (cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) {
+	if ((cmd->flags & CAM_MEM_FLAG_HW_READ_WRITE) ||
+		(cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)) {
 		rc = cam_mem_util_map_hw_va(cmd->flags,
 			cmd->mmu_hdls,
 			cmd->num_hdl,
@@ -650,8 +655,11 @@
 
 	mutex_lock(&tbl.bufq[idx].q_lock);
 	tbl.bufq[idx].fd = cmd->fd;
+	tbl.bufq[idx].dma_buf = NULL;
 	tbl.bufq[idx].flags = cmd->flags;
 	tbl.bufq[idx].buf_handle = GET_MEM_HANDLE(idx, cmd->fd);
+	if (cmd->flags & CAM_MEM_FLAG_PROTECTED_MODE)
+		CAM_MEM_MGR_SET_SECURE_HDL(tbl.bufq[idx].buf_handle, true);
 	tbl.bufq[idx].kmdvaddr = 0;
 
 	if (cmd->num_hdl > 0)
@@ -678,7 +686,8 @@
 }
 
 static int cam_mem_util_unmap_hw_va(int32_t idx,
-	enum cam_smmu_region_id region)
+	enum cam_smmu_region_id region,
+	enum cam_smmu_mapping_client client)
 {
 	int i;
 	uint32_t flags;
@@ -699,25 +708,102 @@
 
 	if (flags & CAM_MEM_FLAG_PROTECTED_MODE) {
 		for (i = 0; i < num_hdls; i++) {
-			rc = cam_smmu_unmap_sec_iova(mmu_hdls[i], fd);
+			rc = cam_smmu_unmap_stage2_iova(mmu_hdls[i], fd);
 			if (rc < 0)
 				goto unmap_end;
 		}
 	} else {
 		for (i = 0; i < num_hdls; i++) {
-			rc = cam_smmu_unmap_iova(mmu_hdls[i],
-				fd,
-				region);
+			if (client == CAM_SMMU_MAPPING_USER) {
+			rc = cam_smmu_unmap_user_iova(mmu_hdls[i],
+				fd, region);
+			} else if (client == CAM_SMMU_MAPPING_KERNEL) {
+				rc = cam_smmu_unmap_kernel_iova(mmu_hdls[i],
+					tbl.bufq[idx].dma_buf, region);
+			} else {
+				CAM_ERR(CAM_CRM,
+					"invalid caller for unmapping : %d",
+					client);
+				rc = -EINVAL;
+			}
 			if (rc < 0)
 				goto unmap_end;
 		}
 	}
 
+	return rc;
+
 unmap_end:
+	CAM_ERR(CAM_CRM, "unmapping failed");
 	return rc;
 }
 
-static int cam_mem_util_unmap(int32_t idx)
+static void cam_mem_mgr_unmap_active_buf(int idx)
+{
+	enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED;
+
+	if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS)
+		region = CAM_SMMU_REGION_SHARED;
+	else if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE)
+		region = CAM_SMMU_REGION_IO;
+
+	cam_mem_util_unmap_hw_va(idx, region, CAM_SMMU_MAPPING_USER);
+}
+
+static int cam_mem_mgr_cleanup_table(void)
+{
+	int i;
+
+	mutex_lock(&tbl.m_lock);
+	for (i = 1; i < CAM_MEM_BUFQ_MAX; i++) {
+		if (!tbl.bufq[i].active) {
+			CAM_DBG(CAM_CRM,
+				"Buffer inactive at idx=%d, continuing", i);
+			continue;
+		} else {
+			CAM_INFO(CAM_CRM,
+			"Active buffer at idx=%d, possible leak needs unmapping",
+			i);
+			cam_mem_mgr_unmap_active_buf(i);
+		}
+
+		mutex_lock(&tbl.bufq[i].q_lock);
+		ion_free(tbl.client, tbl.bufq[i].i_hdl);
+		tbl.bufq[i].fd = -1;
+		tbl.bufq[i].flags = 0;
+		tbl.bufq[i].buf_handle = -1;
+		tbl.bufq[i].vaddr = 0;
+		tbl.bufq[i].len = 0;
+		memset(tbl.bufq[i].hdls, 0,
+			sizeof(int32_t) * tbl.bufq[i].num_hdl);
+		tbl.bufq[i].num_hdl = 0;
+		tbl.bufq[i].i_hdl = NULL;
+		tbl.bufq[i].active = false;
+		mutex_unlock(&tbl.bufq[i].q_lock);
+		mutex_destroy(&tbl.bufq[i].q_lock);
+	}
+	bitmap_zero(tbl.bitmap, tbl.bits);
+	/* We need to reserve slot 0 because 0 is invalid */
+	set_bit(0, tbl.bitmap);
+	mutex_unlock(&tbl.m_lock);
+
+	return 0;
+}
+
+void cam_mem_mgr_deinit(void)
+{
+	cam_mem_mgr_cleanup_table();
+	mutex_lock(&tbl.m_lock);
+	bitmap_zero(tbl.bitmap, tbl.bits);
+	kfree(tbl.bitmap);
+	tbl.bitmap = NULL;
+	cam_mem_util_client_destroy();
+	mutex_unlock(&tbl.m_lock);
+	mutex_destroy(&tbl.m_lock);
+}
+
+static int cam_mem_util_unmap(int32_t idx,
+	enum cam_smmu_mapping_client client)
 {
 	int rc = 0;
 	enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED;
@@ -741,9 +827,10 @@
 			region = CAM_SMMU_REGION_IO;
 	}
 
-	if (tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE ||
-		tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS)
-		rc = cam_mem_util_unmap_hw_va(idx, region);
+	if ((tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_READ_WRITE) ||
+		(tbl.bufq[idx].flags & CAM_MEM_FLAG_HW_SHARED_ACCESS) ||
+		(tbl.bufq[idx].flags & CAM_MEM_FLAG_PROTECTED_MODE))
+		rc = cam_mem_util_unmap_hw_va(idx, region, client);
 
 
 	mutex_lock(&tbl.bufq[idx].q_lock);
@@ -754,9 +841,10 @@
 		sizeof(int32_t) * CAM_MEM_MMU_MAX_HANDLE);
 
 	CAM_DBG(CAM_CRM,
-		"Ion handle at idx = %d freeing = %pK, fd = %d, imported %d",
+		"Ion handle at idx = %d freeing = %pK, fd = %d, imported %d dma_buf %pK",
 		idx, tbl.bufq[idx].i_hdl, tbl.bufq[idx].fd,
-		tbl.bufq[idx].is_imported);
+		tbl.bufq[idx].is_imported,
+		tbl.bufq[idx].dma_buf);
 
 	if (tbl.bufq[idx].i_hdl) {
 		ion_free(tbl.client, tbl.bufq[idx].i_hdl);
@@ -764,6 +852,7 @@
 	}
 
 	tbl.bufq[idx].fd = -1;
+	tbl.bufq[idx].dma_buf = NULL;
 	tbl.bufq[idx].is_imported = false;
 	tbl.bufq[idx].len = 0;
 	tbl.bufq[idx].num_hdl = 0;
@@ -801,7 +890,7 @@
 	}
 
 	CAM_DBG(CAM_CRM, "Releasing hdl = %u", cmd->buf_handle);
-	rc = cam_mem_util_unmap(idx);
+	rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_USER);
 
 	return rc;
 }
@@ -810,17 +899,19 @@
 	struct cam_mem_mgr_memory_desc *out)
 {
 	struct ion_handle *hdl;
-	int ion_fd;
+	struct dma_buf *buf = NULL;
+	int ion_fd = -1;
 	int rc = 0;
 	uint32_t heap_id;
 	int32_t ion_flag = 0;
 	uint64_t kvaddr;
 	dma_addr_t iova = 0;
 	size_t request_len = 0;
-	int32_t idx;
 	uint32_t mem_handle;
+	int32_t idx;
 	int32_t smmu_hdl = 0;
 	int32_t num_hdl = 0;
+
 	enum cam_smmu_region_id region = CAM_SMMU_REGION_SHARED;
 
 	if (!inp || !out) {
@@ -842,18 +933,18 @@
 
 	heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
 
-	rc = cam_mem_util_get_ion_buffer(inp->size,
+	rc = cam_mem_util_get_dma_buf(inp->size,
 		inp->align,
 		heap_id,
 		ion_flag,
 		&hdl,
-		&ion_fd);
+		&buf);
 
 	if (rc) {
 		CAM_ERR(CAM_CRM, "ION alloc failed for shared buffer");
 		goto ion_fail;
 	} else {
-		CAM_DBG(CAM_CRM, "Got ION fd = %d, hdl = %pK", ion_fd, hdl);
+		CAM_DBG(CAM_CRM, "Got dma_buf = %pK, hdl = %pK", buf, hdl);
 	}
 
 	rc = cam_mem_util_map_cpu_va(hdl, &kvaddr, &request_len);
@@ -876,8 +967,8 @@
 			region = CAM_SMMU_REGION_IO;
 	}
 
-	rc = cam_smmu_map_iova(inp->smmu_hdl,
-		ion_fd,
+	rc = cam_smmu_map_kernel_iova(inp->smmu_hdl,
+		buf,
 		CAM_SMMU_MAP_RW,
 		&iova,
 		&request_len,
@@ -899,7 +990,8 @@
 
 	mutex_lock(&tbl.bufq[idx].q_lock);
 	mem_handle = GET_MEM_HANDLE(idx, ion_fd);
-	tbl.bufq[idx].fd = ion_fd;
+	tbl.bufq[idx].dma_buf = buf;
+	tbl.bufq[idx].fd = -1;
 	tbl.bufq[idx].flags = inp->flags;
 	tbl.bufq[idx].buf_handle = mem_handle;
 	tbl.bufq[idx].kmdvaddr = kvaddr;
@@ -923,9 +1015,8 @@
 
 	return rc;
 slot_fail:
-	cam_smmu_unmap_iova(inp->smmu_hdl,
-		ion_fd,
-		region);
+	cam_smmu_unmap_kernel_iova(inp->smmu_hdl,
+	buf, region);
 smmu_fail:
 	ion_unmap_kernel(tbl.client, hdl);
 map_fail:
@@ -963,8 +1054,172 @@
 	}
 
 	CAM_DBG(CAM_CRM, "Releasing hdl = %X", inp->mem_handle);
-	rc = cam_mem_util_unmap(idx);
+	rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_KERNEL);
 
 	return rc;
 }
 EXPORT_SYMBOL(cam_mem_mgr_release_mem);
+
+int cam_mem_mgr_reserve_memory_region(struct cam_mem_mgr_request_desc *inp,
+	enum cam_smmu_region_id region,
+	struct cam_mem_mgr_memory_desc *out)
+{
+	struct ion_handle *hdl;
+	struct dma_buf *buf = NULL;
+	int rc = 0;
+	int ion_fd = -1;
+	uint32_t heap_id;
+	dma_addr_t iova = 0;
+	size_t request_len = 0;
+	uint32_t mem_handle;
+	int32_t idx;
+	int32_t smmu_hdl = 0;
+	int32_t num_hdl = 0;
+
+	if (!inp || !out) {
+		CAM_ERR(CAM_CRM, "Invalid param(s)");
+		return -EINVAL;
+	}
+
+	if (!inp->smmu_hdl) {
+		CAM_ERR(CAM_CRM, "Invalid SMMU handle");
+		return -EINVAL;
+	}
+
+	if (region != CAM_SMMU_REGION_SECHEAP) {
+		CAM_ERR(CAM_CRM, "Only secondary heap supported");
+		return -EINVAL;
+	}
+
+	heap_id = ION_HEAP(ION_SYSTEM_HEAP_ID);
+	rc = cam_mem_util_get_dma_buf(inp->size,
+		inp->align,
+		heap_id,
+		0,
+		&hdl,
+		&buf);
+
+	if (rc) {
+		CAM_ERR(CAM_CRM, "ION alloc failed for sec heap buffer");
+		goto ion_fail;
+	} else {
+		CAM_DBG(CAM_CRM, "Got dma_buf = %pK, hdl = %pK", buf, hdl);
+	}
+
+	rc = cam_smmu_reserve_sec_heap(inp->smmu_hdl,
+		buf,
+		&iova,
+		&request_len);
+
+	if (rc) {
+		CAM_ERR(CAM_CRM, "Reserving secondary heap failed");
+		goto smmu_fail;
+	}
+
+	smmu_hdl = inp->smmu_hdl;
+	num_hdl = 1;
+
+	idx = cam_mem_get_slot();
+	if (idx < 0) {
+		rc = -ENOMEM;
+		goto slot_fail;
+	}
+
+	mutex_lock(&tbl.bufq[idx].q_lock);
+	mem_handle = GET_MEM_HANDLE(idx, ion_fd);
+	tbl.bufq[idx].fd = -1;
+	tbl.bufq[idx].dma_buf = buf;
+	tbl.bufq[idx].flags = inp->flags;
+	tbl.bufq[idx].buf_handle = mem_handle;
+	tbl.bufq[idx].kmdvaddr = 0;
+
+	tbl.bufq[idx].vaddr = iova;
+
+	tbl.bufq[idx].i_hdl = hdl;
+	tbl.bufq[idx].len = request_len;
+	tbl.bufq[idx].num_hdl = num_hdl;
+	memcpy(tbl.bufq[idx].hdls, &smmu_hdl,
+		sizeof(int32_t));
+	tbl.bufq[idx].is_imported = false;
+	mutex_unlock(&tbl.bufq[idx].q_lock);
+
+	out->kva = 0;
+	out->iova = (uint32_t)iova;
+	out->smmu_hdl = smmu_hdl;
+	out->mem_handle = mem_handle;
+	out->len = request_len;
+	out->region = region;
+
+	return rc;
+
+slot_fail:
+	cam_smmu_release_sec_heap(smmu_hdl);
+smmu_fail:
+	ion_free(tbl.client, hdl);
+ion_fail:
+	return rc;
+}
+EXPORT_SYMBOL(cam_mem_mgr_reserve_memory_region);
+
+int cam_mem_mgr_free_memory_region(struct cam_mem_mgr_memory_desc *inp)
+{
+	int32_t idx;
+	int rc;
+	int32_t smmu_hdl;
+
+	if (!inp) {
+		CAM_ERR(CAM_CRM, "Invalid argument");
+		return -EINVAL;
+	}
+
+	if (inp->region != CAM_SMMU_REGION_SECHEAP) {
+		CAM_ERR(CAM_CRM, "Only secondary heap supported");
+		return -EINVAL;
+	}
+
+	idx = CAM_MEM_MGR_GET_HDL_IDX(inp->mem_handle);
+	if (idx >= CAM_MEM_BUFQ_MAX || idx <= 0) {
+		CAM_ERR(CAM_CRM, "Incorrect index extracted from mem handle");
+		return -EINVAL;
+	}
+
+	if (!tbl.bufq[idx].active) {
+		CAM_ERR(CAM_CRM, "Released buffer state should be active");
+		return -EINVAL;
+	}
+
+	if (tbl.bufq[idx].buf_handle != inp->mem_handle) {
+		CAM_ERR(CAM_CRM,
+			"Released buf handle not matching within table");
+		return -EINVAL;
+	}
+
+	if (tbl.bufq[idx].num_hdl != 1) {
+		CAM_ERR(CAM_CRM,
+			"Sec heap region should have only one smmu hdl");
+		return -ENODEV;
+	}
+
+	memcpy(&smmu_hdl, tbl.bufq[idx].hdls,
+		sizeof(int32_t));
+	if (inp->smmu_hdl != smmu_hdl) {
+		CAM_ERR(CAM_CRM,
+			"Passed SMMU handle doesn't match with internal hdl");
+		return -ENODEV;
+	}
+
+	rc = cam_smmu_release_sec_heap(inp->smmu_hdl);
+	if (rc) {
+		CAM_ERR(CAM_CRM,
+			"Sec heap region release failed");
+		return -ENODEV;
+	}
+
+	CAM_DBG(CAM_CRM, "Releasing hdl = %X", inp->mem_handle);
+	rc = cam_mem_util_unmap(idx, CAM_SMMU_MAPPING_KERNEL);
+	if (rc)
+		CAM_ERR(CAM_CRM, "unmapping secondary heap failed");
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_mem_mgr_free_memory_region);
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h
index 06588c4..83727d2 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr.h
@@ -14,15 +14,23 @@
 #define _CAM_MEM_MGR_H_
 
 #include <linux/mutex.h>
+#include <linux/dma-buf.h>
 #include <media/cam_req_mgr.h>
 #include "cam_mem_mgr_api.h"
 
 #define CAM_MEM_BUFQ_MAX 1024
 
+/*Enum for possible SMMU operations */
+enum cam_smmu_mapping_client {
+	CAM_SMMU_MAPPING_USER,
+	CAM_SMMU_MAPPING_KERNEL,
+};
+
 /**
  * struct cam_mem_buf_queue
  *
  * @i_hdl:       ion handle for the buffer
+ * @dma_buf:     pointer to the allocated dma_buf in the table
  * @q_lock:      mutex lock for buffer
  * @hdls:        list of mapped handles
  * @num_hdl:     number of handles
@@ -38,6 +46,7 @@
  */
 struct cam_mem_buf_queue {
 	struct ion_handle *i_hdl;
+	struct dma_buf *dma_buf;
 	struct mutex q_lock;
 	int32_t hdls[CAM_MEM_MMU_MAX_HANDLE];
 	int32_t num_hdl;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h
index 0858b8a..7588c17 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_mem_mgr_api.h
@@ -95,4 +95,31 @@
 int cam_mem_get_cpu_buf(int32_t buf_handle, uint64_t *vaddr_ptr,
 	size_t *len);
 
+static inline bool cam_mem_is_secure_buf(int32_t buf_handle)
+{
+	return CAM_MEM_MGR_IS_SECURE_HDL(buf_handle);
+}
+
+/**
+ * @brief: Reserves a memory region
+ *
+ * @inp:  Information specifying requested region properties
+ * @region : Region which is to be reserved
+ * @out   : Information about reserved region
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_reserve_memory_region(struct cam_mem_mgr_request_desc *inp,
+		enum cam_smmu_region_id region,
+		struct cam_mem_mgr_memory_desc *out);
+
+/**
+ * @brief: Frees a memory region
+ *
+ * @inp   : Information about region which is to be freed
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_mem_mgr_free_memory_region(struct cam_mem_mgr_memory_desc *inp);
+
 #endif /* _CAM_MEM_MGR_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
index 96d5b6e..244746b 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.c
@@ -21,9 +21,27 @@
 #include "cam_req_mgr_debug.h"
 #include "cam_trace.h"
 #include "cam_debug_util.h"
+#include "cam_req_mgr_dev.h"
 
 static struct cam_req_mgr_core_device *g_crm_core_dev;
 
+
+void cam_req_mgr_handle_core_shutdown(void)
+{
+	struct cam_req_mgr_core_session *session;
+	struct cam_req_mgr_core_session *tsession;
+	struct cam_req_mgr_session_info ses_info;
+
+	if (!list_empty(&g_crm_core_dev->session_head)) {
+		list_for_each_entry_safe(session, tsession,
+			&g_crm_core_dev->session_head, entry) {
+			ses_info.session_hdl =
+				session->session_hdl;
+			cam_req_mgr_destroy_session(&ses_info);
+		}
+	}
+}
+
 static int __cam_req_mgr_setup_payload(struct cam_req_mgr_core_workq *workq)
 {
 	int32_t                  i = 0;
@@ -180,6 +198,15 @@
 		tbl->pd, curr_idx, tbl->slot[curr_idx].state,
 		tbl->skip_traverse, traverse_data->in_q->slot[curr_idx].status);
 
+	if (tbl->inject_delay > 0) {
+		CAM_DBG(CAM_CRM, "Injecting Delay of one frame");
+		apply_data[tbl->pd].req_id = -1;
+		tbl->inject_delay--;
+		/* This pd table is not ready to proceed with asked idx */
+		SET_FAILURE_BIT(traverse_data->result, tbl->pd);
+		return -EAGAIN;
+	}
+
 	/* Check if req is ready or in skip mode or pd tbl is in skip mode */
 	if (tbl->slot[curr_idx].state == CRM_REQ_STATE_READY ||
 		traverse_data->in_q->slot[curr_idx].skip_idx == 1 ||
@@ -414,7 +441,7 @@
 		}
 	}
 	if (rc < 0) {
-		CAM_ERR(CAM_CRM, "APPLY FAILED pd %d req_id %lld",
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "APPLY FAILED pd %d req_id %lld",
 			dev->dev_info.p_delay, apply_req.request_id);
 		/* Apply req failed notify already applied devs */
 		for (; i >= 0; i--) {
@@ -525,7 +552,8 @@
 
 	if (trigger == CAM_TRIGGER_POINT_SOF) {
 		if (link->trigger_mask) {
-			CAM_ERR(CAM_CRM, "Applying for last EOF fails");
+			CAM_ERR_RATE_LIMIT(CAM_CRM,
+				"Applying for last EOF fails");
 			return -EINVAL;
 		}
 		rc = __cam_req_mgr_check_link_is_ready(link, slot->idx);
@@ -536,24 +564,26 @@
 			  * hence try again in next sof
 			  */
 			slot->status = CRM_SLOT_STATUS_REQ_PENDING;
+			spin_lock_bh(&link->link_state_spin_lock);
 			if (link->state == CAM_CRM_LINK_STATE_ERR) {
 				/*
 				 * During error recovery all tables should be
 				 * ready, don't expect to enter here.
 				 * @TODO: gracefully handle if recovery fails.
 				 */
-				CAM_ERR(CAM_CRM,
+				CAM_ERR_RATE_LIMIT(CAM_CRM,
 					"FATAL recovery cant finish idx %d status %d",
 					in_q->rd_idx,
 					in_q->slot[in_q->rd_idx].status);
 				rc = -EPERM;
 			}
+			spin_unlock_bh(&link->link_state_spin_lock);
 			return rc;
 		}
 	}
 	if (trigger == CAM_TRIGGER_POINT_EOF &&
 			(!(link->trigger_mask & CAM_TRIGGER_POINT_SOF))) {
-		CAM_ERR(CAM_CRM, "Applying for last SOF fails");
+		CAM_DBG(CAM_CRM, "Applying for last SOF fails");
 		return -EINVAL;
 	}
 
@@ -564,13 +594,14 @@
 	} else {
 		link->trigger_mask |= trigger;
 
+		spin_lock_bh(&link->link_state_spin_lock);
 		if (link->state == CAM_CRM_LINK_STATE_ERR) {
 			CAM_WARN(CAM_CRM, "Err recovery done idx %d",
 				in_q->rd_idx);
-			mutex_lock(&link->lock);
 			link->state = CAM_CRM_LINK_STATE_READY;
-			mutex_unlock(&link->lock);
 		}
+		spin_unlock_bh(&link->link_state_spin_lock);
+
 		if (link->trigger_mask == link->subscribe_event) {
 			slot->status = CRM_SLOT_STATUS_REQ_APPLIED;
 			link->trigger_mask = 0;
@@ -779,15 +810,34 @@
  */
 static void __cam_req_mgr_sof_freeze(unsigned long data)
 {
-	struct cam_req_mgr_timer *timer = (struct cam_req_mgr_timer *)data;
-	struct cam_req_mgr_core_link *link = NULL;
+	struct cam_req_mgr_timer     *timer = (struct cam_req_mgr_timer *)data;
+	struct cam_req_mgr_core_link    *link = NULL;
+	struct cam_req_mgr_core_session *session = NULL;
+	struct cam_req_mgr_message       msg;
 
 	if (!timer) {
 		CAM_ERR(CAM_CRM, "NULL timer");
 		return;
 	}
 	link = (struct cam_req_mgr_core_link *)timer->parent;
-	CAM_ERR(CAM_CRM, "SOF freeze for link %x", link->link_hdl);
+	session = (struct cam_req_mgr_core_session *)link->parent;
+
+	CAM_ERR(CAM_CRM, "SOF freeze for session %d link 0x%x",
+		session->session_hdl, link->link_hdl);
+
+	memset(&msg, 0, sizeof(msg));
+
+	msg.session_hdl = session->session_hdl;
+	msg.u.err_msg.error_type = CAM_REQ_MGR_ERROR_TYPE_DEVICE;
+	msg.u.err_msg.request_id = 0;
+	msg.u.err_msg.link_hdl   = link->link_hdl;
+
+
+	if (cam_req_mgr_notify_message(&msg,
+		V4L_EVENT_CAM_REQ_MGR_ERROR, V4L_EVENT_CAM_REQ_MGR_EVENT))
+		CAM_ERR(CAM_CRM,
+			"Error notifying SOF freeze for session %d link 0x%x",
+			session->session_hdl, link->link_hdl);
 }
 
 /**
@@ -832,14 +882,14 @@
  * @brief    : Cleans up the mem allocated while linking
  * @link     : pointer to link, mem associated with this link is freed
  *
+ * @return   : returns if unlink for any device was success or failure
  */
-static void __cam_req_mgr_destroy_link_info(struct cam_req_mgr_core_link *link)
+static int __cam_req_mgr_destroy_link_info(struct cam_req_mgr_core_link *link)
 {
 	int32_t                                 i = 0;
 	struct cam_req_mgr_connected_device    *dev;
 	struct cam_req_mgr_core_dev_link_setup  link_data;
-
-	mutex_lock(&link->lock);
+	int                                     rc = 0;
 
 	link_data.link_enable = 0;
 	link_data.link_hdl = link->link_hdl;
@@ -852,7 +902,11 @@
 		if (dev != NULL) {
 			link_data.dev_hdl = dev->dev_hdl;
 			if (dev->ops && dev->ops->link_setup)
-				dev->ops->link_setup(&link_data);
+				rc = dev->ops->link_setup(&link_data);
+				if (rc)
+					CAM_ERR(CAM_CRM,
+						"Unlink failed dev_hdl %d",
+						dev->dev_hdl);
 			dev->dev_hdl = 0;
 			dev->parent = NULL;
 			dev->ops = NULL;
@@ -867,7 +921,7 @@
 	link->num_devs = 0;
 	link->max_delay = 0;
 
-	mutex_unlock(&link->lock);
+	return rc;
 }
 
 /**
@@ -902,8 +956,15 @@
 		CAM_ERR(CAM_CRM, "failed to create link, no mem");
 		return NULL;
 	}
-	in_q = &session->in_q;
+	in_q = (struct cam_req_mgr_req_queue *)
+		kzalloc(sizeof(struct cam_req_mgr_req_queue), GFP_KERNEL);
+	if (!in_q) {
+		CAM_ERR(CAM_CRM, "failed to create input queue, no mem");
+		kfree(link);
+		return NULL;
+	}
 	mutex_init(&link->lock);
+	spin_lock_init(&link->link_state_spin_lock);
 
 	mutex_lock(&link->lock);
 	link->state = CAM_CRM_LINK_STATE_AVAILABLE;
@@ -928,7 +989,7 @@
 }
 
 /**
- * __cam_req_mgr_reserve_link()
+ * __cam_req_mgr_unreserve_link()
  *
  * @brief  : Reserves one link data struct within session
  * @session: session identifier
@@ -960,6 +1021,8 @@
 		CAM_DBG(CAM_CRM, "Active session links (%d)",
 			session->num_links);
 	}
+	kfree((*link)->req.in_q);
+	(*link)->req.in_q = NULL;
 	kfree(*link);
 	*link = NULL;
 	mutex_unlock(&session->lock);
@@ -1178,7 +1241,7 @@
 		}
 	}
 	if (!tbl) {
-		CAM_ERR(CAM_CRM, "dev_hdl not found %x, %x %x",
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "dev_hdl not found %x, %x %x",
 			add_req->dev_hdl,
 			link->l_dev[0].dev_hdl,
 			link->l_dev[1].dev_hdl);
@@ -1201,6 +1264,10 @@
 		mutex_unlock(&link->req.lock);
 		goto end;
 	}
+
+	if (add_req->skip_before_applying > tbl->inject_delay)
+		tbl->inject_delay = add_req->skip_before_applying;
+
 	slot = &tbl->slot[idx];
 	if (slot->state != CRM_REQ_STATE_PENDING &&
 		slot->state != CRM_REQ_STATE_EMPTY) {
@@ -1267,8 +1334,9 @@
 	if (err_info->error == CRM_KMD_ERR_BUBBLE) {
 		idx = __cam_req_mgr_find_slot_for_req(in_q, err_info->req_id);
 		if (idx < 0) {
-			CAM_ERR(CAM_CRM, "req_id %lld not found in input queue",
-			err_info->req_id);
+			CAM_ERR_RATE_LIMIT(CAM_CRM,
+				"req_id %lld not found in input queue",
+				err_info->req_id);
 		} else {
 			CAM_DBG(CAM_CRM, "req_id %lld found at idx %d",
 				err_info->req_id, idx);
@@ -1307,9 +1375,9 @@
 			__cam_req_mgr_tbl_set_all_skip_cnt(&link->req.l_tbl);
 			in_q->rd_idx = idx;
 			in_q->slot[idx].status = CRM_SLOT_STATUS_REQ_ADDED;
-			mutex_lock(&link->lock);
+			spin_lock_bh(&link->link_state_spin_lock);
 			link->state = CAM_CRM_LINK_STATE_ERR;
-			mutex_unlock(&link->lock);
+			spin_unlock_bh(&link->link_state_spin_lock);
 		}
 	}
 	mutex_unlock(&link->req.lock);
@@ -1360,11 +1428,14 @@
 	CAM_DBG(CAM_CRM, "link_hdl %x curent idx %d req_status %d",
 		link->link_hdl, in_q->rd_idx, in_q->slot[in_q->rd_idx].status);
 
+	spin_lock_bh(&link->link_state_spin_lock);
 	if (link->state == CAM_CRM_LINK_STATE_ERR)
 		CAM_WARN(CAM_CRM, "Error recovery idx %d status %d",
 			in_q->rd_idx,
 			in_q->slot[in_q->rd_idx].status);
 
+	spin_unlock_bh(&link->link_state_spin_lock);
+
 	if (in_q->slot[in_q->rd_idx].status == CRM_SLOT_STATUS_REQ_APPLIED) {
 		/*
 		 * Do NOT reset req q slot data here, it can not be done
@@ -1405,8 +1476,7 @@
 
 	if (!add_req) {
 		CAM_ERR(CAM_CRM, "sof_data is NULL");
-		rc = -EINVAL;
-		goto end;
+		return -EINVAL;
 	}
 
 	CAM_DBG(CAM_CRM, "E: dev %x dev req %lld",
@@ -1416,9 +1486,18 @@
 
 	if (!link) {
 		CAM_DBG(CAM_CRM, "link ptr NULL %x", add_req->link_hdl);
-		rc = -EINVAL;
+		return -EINVAL;
+	}
+
+	mutex_lock(&link->lock);
+	spin_lock_bh(&link->link_state_spin_lock);
+	if (link->state != CAM_CRM_LINK_STATE_READY) {
+		CAM_WARN(CAM_CRM, "invalid link state:%d", link->state);
+		rc = -EPERM;
+		spin_unlock_bh(&link->link_state_spin_lock);
 		goto end;
 	}
+	spin_unlock_bh(&link->link_state_spin_lock);
 
 	/* Validate if req id is present in input queue */
 	idx = __cam_req_mgr_find_slot_for_req(link->req.in_q, add_req->req_id);
@@ -1430,7 +1509,7 @@
 
 	task = cam_req_mgr_workq_get_task(link->workq);
 	if (!task) {
-		CAM_ERR(CAM_CRM, "no empty task dev %x req %lld",
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "no empty task dev %x req %lld",
 			add_req->dev_hdl, add_req->req_id);
 		rc = -EBUSY;
 		goto end;
@@ -1442,12 +1521,14 @@
 	dev_req->req_id = add_req->req_id;
 	dev_req->link_hdl = add_req->link_hdl;
 	dev_req->dev_hdl = add_req->dev_hdl;
+	dev_req->skip_before_applying = add_req->skip_before_applying;
 	task->process_cb = &cam_req_mgr_process_add_req;
 	rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
 	CAM_DBG(CAM_CRM, "X: dev %x dev req %lld",
 		add_req->dev_hdl, add_req->req_id);
 
 end:
+	mutex_unlock(&link->lock);
 	return rc;
 }
 
@@ -1483,6 +1564,15 @@
 		goto end;
 	}
 
+	spin_lock_bh(&link->link_state_spin_lock);
+	if (link->state != CAM_CRM_LINK_STATE_READY) {
+		CAM_WARN(CAM_CRM, "invalid link state:%d", link->state);
+		spin_unlock_bh(&link->link_state_spin_lock);
+		rc = -EPERM;
+		goto end;
+	}
+	spin_unlock_bh(&link->link_state_spin_lock);
+
 	crm_timer_reset(link->watchdog);
 	task = cam_req_mgr_workq_get_task(link->workq);
 	if (!task) {
@@ -1537,6 +1627,15 @@
 		goto end;
 	}
 
+	spin_lock_bh(&link->link_state_spin_lock);
+	if (link->state != CAM_CRM_LINK_STATE_READY) {
+		CAM_WARN(CAM_CRM, "invalid link state:%d", link->state);
+		spin_unlock_bh(&link->link_state_spin_lock);
+		rc = -EPERM;
+		goto end;
+	}
+	spin_unlock_bh(&link->link_state_spin_lock);
+
 	crm_timer_reset(link->watchdog);
 	task = cam_req_mgr_workq_get_task(link->workq);
 	if (!task) {
@@ -1597,7 +1696,6 @@
 	if (rc < 0)
 		return rc;
 
-	mutex_lock(&link->lock);
 	max_delay = CAM_PIPELINE_DELAY_0;
 	for (i = 0; i < link_info->num_devices; i++) {
 		dev = &link->l_dev[i];
@@ -1700,7 +1798,6 @@
 	/* At start, expect max pd devices, all are in skip state */
 	__cam_req_mgr_tbl_set_all_skip_cnt(&link->req.l_tbl);
 
-	mutex_unlock(&link->lock);
 	return 0;
 
 error:
@@ -1840,11 +1937,9 @@
 	if (link->link_hdl < 0) {
 		CAM_ERR(CAM_CRM,
 			"Insufficient memory to create new device handle");
-		mutex_unlock(&link->lock);
 		rc = link->link_hdl;
 		goto link_hdl_fail;
 	}
-	mutex_unlock(&link->lock);
 	link_info->link_hdl = link->link_hdl;
 
 	/* Allocate memory to hold data of all linked devs */
@@ -1861,9 +1956,9 @@
 	if (rc < 0)
 		goto setup_failed;
 
-	mutex_lock(&link->lock);
+	spin_lock_bh(&link->link_state_spin_lock);
 	link->state = CAM_CRM_LINK_STATE_READY;
-	mutex_unlock(&link->lock);
+	spin_unlock_bh(&link->link_state_spin_lock);
 
 	/* Create worker for current link */
 	snprintf(buf, sizeof(buf), "%x-%x",
@@ -1894,6 +1989,7 @@
 		goto setup_failed;
 	}
 
+	mutex_unlock(&link->lock);
 	mutex_unlock(&g_crm_core_dev->crm_lock);
 	return rc;
 setup_failed:
@@ -1902,10 +1998,8 @@
 	cam_destroy_device_hdl(link->link_hdl);
 	link_info->link_hdl = 0;
 link_hdl_fail:
-	mutex_lock(&link->lock);
-	link->state = CAM_CRM_LINK_STATE_AVAILABLE;
 	mutex_unlock(&link->lock);
-
+	__cam_req_mgr_unreserve_link(cam_session, &link);
 	mutex_unlock(&g_crm_core_dev->crm_lock);
 	return rc;
 }
@@ -1940,6 +2034,11 @@
 		mutex_unlock(&g_crm_core_dev->crm_lock);
 		return -EINVAL;
 	}
+
+	mutex_lock(&link->lock);
+	spin_lock_bh(&link->link_state_spin_lock);
+	link->state = CAM_CRM_LINK_STATE_IDLE;
+	spin_unlock_bh(&link->link_state_spin_lock);
 	__cam_req_mgr_print_req_tbl(&link->req);
 
 	/* Destroy workq payload data */
@@ -1951,8 +2050,12 @@
 
 	cam_req_mgr_workq_destroy(&link->workq);
 
-	/* Cleanuprequest tables */
-	__cam_req_mgr_destroy_link_info(link);
+	/* Cleanup request tables and unlink devices */
+	rc = __cam_req_mgr_destroy_link_info(link);
+	if (rc) {
+		CAM_ERR(CAM_CORE, "Unlink failed. Cannot proceed");
+		return rc;
+	}
 
 	/* Free memory holding data of linked devs */
 	__cam_req_mgr_destroy_subdev(link->l_dev);
@@ -1965,6 +2068,7 @@
 	}
 
 	/* Free curent link and put back into session's free pool of links */
+	mutex_unlock(&link->lock);
 	__cam_req_mgr_unreserve_link(cam_session, &link);
 	mutex_unlock(&g_crm_core_dev->crm_lock);
 
@@ -2088,10 +2192,10 @@
 	flush->link_hdl = flush_info->link_hdl;
 	flush->flush_type = flush_info->flush_type;
 	task->process_cb = &cam_req_mgr_process_flush_req;
+	init_completion(&link->workq_comp);
 	rc = cam_req_mgr_workq_enqueue_task(task, link, CRM_TASK_PRIORITY_0);
 
 	/* Blocking call */
-	init_completion(&link->workq_comp);
 	rc = wait_for_completion_timeout(
 		&link->workq_comp,
 		msecs_to_jiffies(CAM_REQ_MGR_SCHED_REQ_TIMEOUT));
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
index 98a2a4f..e17047d6 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core.h
@@ -12,6 +12,7 @@
 #ifndef _CAM_REQ_MGR_CORE_H_
 #define _CAM_REQ_MGR_CORE_H_
 
+#include <linux/spinlock.h>
 #include "cam_req_mgr_interface.h"
 #include "cam_req_mgr_core_defs.h"
 #include "cam_req_mgr_timer.h"
@@ -173,6 +174,7 @@
  * @pd_delta      : differnce between this table's pipeline delay and next
  * @num_slots     : number of request slots present in the table
  * @slot          : array of slots tracking requests availability at devices
+ * @inject_delay  : insert extra bubbling for flash type of use cases
  */
 struct cam_req_mgr_req_tbl {
 	int32_t                     id;
@@ -184,6 +186,7 @@
 	int32_t                     pd_delta;
 	int32_t                     num_slots;
 	struct cam_req_mgr_tbl_slot slot[MAX_REQ_SLOTS];
+	uint32_t                    inject_delay;
 };
 
 /**
@@ -257,27 +260,28 @@
 /**
  * struct cam_req_mgr_core_link
  * -  Link Properties
- * @link_hdl       : Link identifier
- * @num_devs       : num of connected devices to this link
- * @max_delay      : Max of pipeline delay of all connected devs
- * @workq          : Pointer to handle workq related jobs
- * @pd_mask        : each set bit indicates the device with pd equal to bit
- *                   position is available.
+ * @link_hdl             : Link identifier
+ * @num_devs             : num of connected devices to this link
+ * @max_delay            : Max of pipeline delay of all connected devs
+ * @workq                : Pointer to handle workq related jobs
+ * @pd_mask              : each set bit indicates the device with pd equal to
+ *                          bit position is available.
  * - List of connected devices
- * @l_dev          : List of connected devices to this link
+ * @l_dev                : List of connected devices to this link
  * - Request handling data struct
- * @req            : req data holder.
+ * @req                  : req data holder.
  * - Timer
- * @watchdog       : watchdog timer to recover from sof freeze
+ * @watchdog             : watchdog timer to recover from sof freeze
  * - Link private data
- * @workq_comp     : conditional variable to block user thread for workq to
- *                   finish schedule request processing
- * @state          : link state machine
- * @parent         : pvt data - link's parent is session
- * @lock           : mutex lock to guard link data operations
- * @subscribe_event: irqs that link subscribes, IFE should send notification
- * to CRM at those hw events.
- * @trigger_mask   : mask on which irq the req is already applied
+ * @workq_comp           : conditional variable to block user thread for workq
+ *                          to finish schedule request processing
+ * @state                : link state machine
+ * @parent               : pvt data - link's parent is session
+ * @lock                 : mutex lock to guard link data operations
+ * @link_state_spin_lock : spin lock to protect link state variable
+ * @subscribe_event      : irqs that link subscribes, IFE should send
+ *                         notification to CRM at those hw events.
+ * @trigger_mask         : mask on which irq the req is already applied
  */
 struct cam_req_mgr_core_link {
 	int32_t                              link_hdl;
@@ -292,6 +296,7 @@
 	enum cam_req_mgr_link_state          state;
 	void                                *parent;
 	struct mutex                         lock;
+	spinlock_t                           link_state_spin_lock;
 	uint32_t                             subscribe_event;
 	uint32_t                             trigger_mask;
 };
@@ -315,7 +320,6 @@
 	int32_t                       session_hdl;
 	uint32_t                      num_links;
 	struct cam_req_mgr_core_link *links[MAX_LINKS_PER_SESSION];
-	struct cam_req_mgr_req_queue  in_q;
 	struct list_head              entry;
 	struct mutex                  lock;
 	int32_t                       force_err_recovery;
@@ -405,5 +409,11 @@
  * @brief: cleanp crm core
  */
 int cam_req_mgr_core_device_deinit(void);
+
+/**
+ * cam_req_mgr_handle_core_shutdown()
+ * @brief: Handles camera close
+ */
+void cam_req_mgr_handle_core_shutdown(void);
 #endif
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
index f61c41e..475b640 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_core_defs.h
@@ -15,13 +15,9 @@
 #define CRM_TRACE_ENABLE 0
 #define CRM_DEBUG_MUTEX 0
 
-#define SET_SUCCESS_BIT(ret, pd)	{\
-	(ret) |= (1 << (pd));	\
-	}
+#define SET_SUCCESS_BIT(ret, pd)  (ret |= (1 << (pd)))
 
-#define SET_FAILURE_BIT(ret, pd)	{\
-	(ret) &= (0 << (pd));	\
-	}
+#define SET_FAILURE_BIT(ret, pd)  (ret &= (~(1 << (pd))))
 
 #define CRM_GET_REQ_ID(in_q, idx) in_q->slot[idx].req_id
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index 7a2bc09..49c3c56e 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
@@ -151,9 +151,6 @@
 
 static int cam_req_mgr_close(struct file *filep)
 {
-	struct v4l2_subdev *sd;
-	struct cam_control cam_ctrl;
-
 	mutex_lock(&g_dev.cam_lock);
 
 	if (g_dev.open_cnt <= 0) {
@@ -161,14 +158,7 @@
 		return -EINVAL;
 	}
 
-	cam_ctrl.op_code = CAM_SD_SHUTDOWN;
-	list_for_each_entry(sd, &g_dev.v4l2_dev->subdevs, list) {
-		if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
-			continue;
-		v4l2_subdev_call(sd, core, ioctl, VIDIOC_CAM_CONTROL,
-			&cam_ctrl);
-	}
-
+	cam_req_mgr_handle_core_shutdown();
 	g_dev.open_cnt--;
 	v4l2_fh_release(filep);
 
@@ -472,7 +462,7 @@
 	return rc;
 }
 
-int cam_req_mgr_notify_frame_message(struct cam_req_mgr_message *msg,
+int cam_req_mgr_notify_message(struct cam_req_mgr_message *msg,
 	uint32_t id,
 	uint32_t type)
 {
@@ -491,7 +481,7 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(cam_req_mgr_notify_frame_message);
+EXPORT_SYMBOL(cam_req_mgr_notify_message);
 
 void cam_video_device_cleanup(void)
 {
@@ -500,6 +490,12 @@
 	g_dev.video = NULL;
 }
 
+void cam_register_subdev_fops(struct v4l2_file_operations *fops)
+{
+	*fops = v4l2_subdev_fops;
+}
+EXPORT_SYMBOL(cam_register_subdev_fops);
+
 int cam_register_subdev(struct cam_subdev *csd)
 {
 	struct v4l2_subdev *sd;
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.h
index 77faed9..93278b8 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.h
@@ -43,7 +43,7 @@
 #define CAM_REQ_MGR_GET_PAYLOAD_PTR(ev, type)        \
 	(type *)((char *)ev.u.data)
 
-int cam_req_mgr_notify_frame_message(struct cam_req_mgr_message *msg,
+int cam_req_mgr_notify_message(struct cam_req_mgr_message *msg,
 	uint32_t id,
 	uint32_t type);
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
index 6195b59..ce8dfa7 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_interface.h
@@ -216,15 +216,18 @@
 
 /**
  * struct cam_req_mgr_add_request
- * @link_hdl : link identifier
- * @dev_hdl  : device handle which has sent this req id
- * @req_id   : req id which device is ready to process
- *
+ * @link_hdl             : link identifier
+ * @dev_hdl              : device handle which has sent this req id
+ * @req_id               : req id which device is ready to process
+ * @skip_before_applying : before applying req mgr introduce bubble
+ *                         by not sending request to device/s.
+ *                         ex: IFE and Flash
  */
 struct cam_req_mgr_add_request {
 	int32_t  link_hdl;
 	int32_t  dev_hdl;
 	uint64_t req_id;
+	uint32_t skip_before_applying;
 };
 
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
index a9134fb..f357941 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_util.c
@@ -203,29 +203,29 @@
 
 	spin_lock_bh(&hdl_tbl_lock);
 	if (!hdl_tbl) {
-		CAM_ERR(CAM_CRM, "Hdl tbl is NULL");
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "Hdl tbl is NULL");
 		goto device_priv_fail;
 	}
 
 	idx = CAM_REQ_MGR_GET_HDL_IDX(dev_hdl);
 	if (idx >= CAM_REQ_MGR_MAX_HANDLES) {
-		CAM_ERR(CAM_CRM, "Invalid idx");
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid idx");
 		goto device_priv_fail;
 	}
 
 	if (hdl_tbl->hdl[idx].state != HDL_ACTIVE) {
-		CAM_ERR(CAM_CRM, "Invalid state");
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid state");
 		goto device_priv_fail;
 	}
 
 	type = CAM_REQ_MGR_GET_HDL_TYPE(dev_hdl);
 	if (HDL_TYPE_DEV != type && HDL_TYPE_SESSION != type) {
-		CAM_ERR(CAM_CRM, "Invalid type");
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid type");
 		goto device_priv_fail;
 	}
 
 	if (hdl_tbl->hdl[idx].hdl_value != dev_hdl) {
-		CAM_ERR(CAM_CRM, "Invalid hdl");
+		CAM_ERR_RATE_LIMIT(CAM_CRM, "Invalid hdl");
 		goto device_priv_fail;
 	}
 
@@ -317,6 +317,8 @@
 	}
 
 	hdl_tbl->hdl[idx].state = HDL_FREE;
+	hdl_tbl->hdl[idx].ops   = NULL;
+	hdl_tbl->hdl[idx].priv  = NULL;
 	clear_bit(idx, hdl_tbl->bitmap);
 	spin_unlock_bh(&hdl_tbl_lock);
 
diff --git a/drivers/media/platform/msm/camera/cam_req_mgr/cam_subdev.h b/drivers/media/platform/msm/camera/cam_req_mgr/cam_subdev.h
index 78f2223..8cd3214 100644
--- a/drivers/media/platform/msm/camera/cam_req_mgr/cam_subdev.h
+++ b/drivers/media/platform/msm/camera/cam_req_mgr/cam_subdev.h
@@ -82,6 +82,15 @@
 int cam_subdev_remove(struct cam_subdev *sd);
 
 /**
+ * cam_register_subdev_fops()
+ *
+ * @brief:   This common utility function assigns subdev ops
+ *
+ * @fops:    v4l file operations
+ */
+void cam_register_subdev_fops(struct v4l2_file_operations *fops);
+
+/**
  * cam_register_subdev()
  *
  * @brief:   This is the common utility function to be called by each camera
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile
index b66480c..65c2327 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_res_mgr/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_utils/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/
@@ -6,3 +7,4 @@
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_flash/
 obj-$(CONFIG_SPECTRA_CAMERA) += cam_eeprom/
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile
index 4e8ea8b..c0bebfd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/Makefile
@@ -1,6 +1,7 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
index bcf4133..abfc190 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.c
@@ -15,6 +15,129 @@
 #include "cam_actuator_core.h"
 #include "cam_sensor_util.h"
 #include "cam_trace.h"
+#include "cam_res_mgr_api.h"
+
+int32_t cam_actuator_construct_default_power_setting(
+	struct cam_sensor_power_ctrl_t *power_info)
+{
+	int rc = 0;
+
+	power_info->power_setting_size = 1;
+	power_info->power_setting =
+		(struct cam_sensor_power_setting *)
+		kzalloc(sizeof(struct cam_sensor_power_setting),
+			GFP_KERNEL);
+	if (!power_info->power_setting)
+		return -ENOMEM;
+
+	power_info->power_setting[0].seq_type = SENSOR_VAF;
+	power_info->power_setting[0].seq_val = CAM_VAF;
+	power_info->power_setting[0].config_val = 1;
+
+	power_info->power_down_setting_size = 1;
+	power_info->power_down_setting =
+		(struct cam_sensor_power_setting *)
+		kzalloc(sizeof(struct cam_sensor_power_setting),
+			GFP_KERNEL);
+	if (!power_info->power_down_setting) {
+		rc = -ENOMEM;
+		goto free_power_settings;
+	}
+
+	power_info->power_setting[0].seq_type = SENSOR_VAF;
+	power_info->power_setting[0].seq_val = CAM_VAF;
+	power_info->power_setting[0].config_val = 0;
+
+	return rc;
+
+free_power_settings:
+	kfree(power_info->power_setting);
+	return rc;
+}
+
+static int32_t cam_actuator_power_up(struct cam_actuator_ctrl_t *a_ctrl)
+{
+	int rc = 0;
+	struct cam_hw_soc_info  *soc_info =
+		&a_ctrl->soc_info;
+	struct cam_actuator_soc_private  *soc_private;
+	struct cam_sensor_power_ctrl_t *power_info;
+
+	soc_private =
+		(struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
+	/* Parse and fill vreg params for power up settings */
+	rc = msm_camera_fill_vreg_params(
+		&a_ctrl->soc_info,
+		power_info->power_setting,
+		power_info->power_setting_size);
+	if (rc) {
+		CAM_ERR(CAM_ACTUATOR,
+			"failed to fill vreg params for power up rc:%d", rc);
+		return rc;
+	}
+
+	/* Parse and fill vreg params for power down settings*/
+	rc = msm_camera_fill_vreg_params(
+		&a_ctrl->soc_info,
+		power_info->power_down_setting,
+		power_info->power_down_setting_size);
+	if (rc) {
+		CAM_ERR(CAM_ACTUATOR,
+			"failed to fill vreg params power down rc:%d", rc);
+		return rc;
+	}
+
+	power_info->dev = soc_info->dev;
+
+	rc = cam_sensor_core_power_up(power_info, soc_info);
+	if (rc) {
+		CAM_ERR(CAM_ACTUATOR, "failed in ois power up rc %d", rc);
+		return rc;
+	}
+
+	/* VREG needs some delay to power up */
+	usleep_range(2000, 2050);
+
+	rc = camera_io_init(&a_ctrl->io_master_info);
+	if (rc < 0)
+		CAM_ERR(CAM_ACTUATOR, "cci_init failed: rc: %d", rc);
+
+	return rc;
+}
+
+static int32_t cam_actuator_power_down(struct cam_actuator_ctrl_t *a_ctrl)
+{
+	int32_t rc = 0;
+	struct cam_sensor_power_ctrl_t *power_info;
+	struct cam_hw_soc_info *soc_info = &a_ctrl->soc_info;
+	struct cam_actuator_soc_private  *soc_private;
+
+	if (!a_ctrl) {
+		CAM_ERR(CAM_ACTUATOR, "failed: e_ctrl %pK", a_ctrl);
+		return -EINVAL;
+	}
+
+	soc_private =
+		(struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+	soc_info = &a_ctrl->soc_info;
+
+	if (!power_info) {
+		CAM_ERR(CAM_ACTUATOR, "failed: power_info %pK", power_info);
+		return -EINVAL;
+	}
+	rc = msm_camera_power_down(power_info, soc_info);
+	if (rc) {
+		CAM_ERR(CAM_ACTUATOR, "power down the core is failed:%d", rc);
+		return rc;
+	}
+
+	camera_io_release(&a_ctrl->io_master_info);
+
+	return rc;
+}
 
 static int32_t cam_actuator_i2c_modes_util(
 	struct camera_io_master *io_master_info,
@@ -93,6 +216,8 @@
 
 	i2c_info = (struct cam_cmd_i2c_info *)cmd_buf;
 	if (a_ctrl->io_master_info.master_type == CCI_MASTER) {
+		a_ctrl->io_master_info.cci_client->cci_i2c_master =
+			a_ctrl->cci_i2c_master;
 		a_ctrl->io_master_info.cci_client->i2c_freq_mode =
 			i2c_info->i2c_freq_mode;
 		a_ctrl->io_master_info.cci_client->sid =
@@ -161,7 +286,7 @@
 	}
 	request_id = apply->request_id % MAX_PER_FRAME_ARRAY;
 
-	trace_cam_apply_req("Actuator", apply);
+	trace_cam_apply_req("Actuator", apply->request_id);
 
 	CAM_DBG(CAM_ACTUATOR, "Request Id: %lld", apply->request_id);
 
@@ -315,16 +440,29 @@
 		cmd_buf += (sizeof(struct cam_cmd_i2c_info)/sizeof(uint32_t));
 		i2c_data->init_settings.request_id = 0;
 		i2c_reg_settings->is_settings_valid = 1;
-		rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+		rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
 			&cmd_desc[1], 1);
 		if (rc < 0) {
 			CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
 				rc);
 			return rc;
 		}
+
+		rc = cam_actuator_apply_settings(a_ctrl,
+			&a_ctrl->i2c_data.init_settings);
+		if (rc < 0)
+			CAM_ERR(CAM_ACTUATOR, "Cannot apply Init settings");
+
+		/* Delete the request even if the apply is failed */
+		rc = delete_request(&a_ctrl->i2c_data.init_settings);
+		if (rc < 0) {
+			CAM_WARN(CAM_ACTUATOR,
+				"Fail in deleting the Init settings");
+			rc = 0;
+		}
 	} else if ((csl_packet->header.op_code & 0xFFFFFF) ==
 		CAM_ACTUATOR_PACKET_AUTO_MOVE_LENS) {
-		a_ctrl->act_apply_state =
+		a_ctrl->setting_apply_state =
 			ACT_APPLY_SETTINGS_NOW;
 
 		i2c_data = &(a_ctrl->i2c_data);
@@ -336,7 +474,7 @@
 		offset = (uint32_t *)&csl_packet->payload;
 		offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
 		cmd_desc = (struct cam_cmd_buf_desc *)(offset);
-		rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+		rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
 			cmd_desc, 1);
 		if (rc < 0) {
 			CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
@@ -356,7 +494,7 @@
 		offset = (uint32_t *)&csl_packet->payload;
 		offset += csl_packet->cmd_buf_offset / sizeof(uint32_t);
 		cmd_desc = (struct cam_cmd_buf_desc *)(offset);
-		rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings,
+		rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
 			cmd_desc, 1);
 		if (rc < 0) {
 			CAM_ERR(CAM_ACTUATOR, "Actuator pkt parsing failed: %d",
@@ -370,6 +508,7 @@
 		add_req.link_hdl = a_ctrl->bridge_intf.link_hdl;
 		add_req.req_id = csl_packet->header.request_id;
 		add_req.dev_hdl = a_ctrl->bridge_intf.device_hdl;
+		add_req.skip_before_applying = 0;
 		if (a_ctrl->bridge_intf.crm_cb &&
 			a_ctrl->bridge_intf.crm_cb->add_req)
 			a_ctrl->bridge_intf.crm_cb->add_req(&add_req);
@@ -380,104 +519,27 @@
 	return rc;
 }
 
-static int32_t cam_actuator_vreg_control(
-	struct cam_actuator_ctrl_t *a_ctrl,
-	int config)
+void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl)
 {
-	int rc = 0, cnt;
-	struct cam_hw_soc_info  *soc_info;
+	int rc;
 
-	soc_info = &a_ctrl->soc_info;
-	cnt = soc_info->num_rgltr;
+	if (a_ctrl->cam_act_state == CAM_ACTUATOR_INIT)
+		return;
 
-	if (!cnt)
-		return 0;
-
-	if (cnt >= CAM_SOC_MAX_REGULATOR) {
-		CAM_ERR(CAM_ACTUATOR, "Regulators more than supported %d", cnt);
-		return -EINVAL;
-	}
-
-	if (config)
-		rc = cam_soc_util_enable_platform_resource(soc_info, false, 0,
-			false);
-	else
-		rc = cam_soc_util_disable_platform_resource(soc_info, false,
-			false);
-
-	return rc;
-}
-
-static int32_t cam_actuator_power_up(struct cam_actuator_ctrl_t *a_ctrl)
-{
-	int rc = 0;
-	struct cam_hw_soc_info  *soc_info =
-		&a_ctrl->soc_info;
-	struct msm_camera_gpio_num_info *gpio_num_info = NULL;
-
-	rc = cam_actuator_vreg_control(a_ctrl, 1);
-	if (rc < 0) {
-		CAM_ERR(CAM_ACTUATOR, "Actuator Reg Failed %d", rc);
-		return rc;
-	}
-
-	gpio_num_info = a_ctrl->gpio_num_info;
-
-	if (soc_info->gpio_data &&
-		gpio_num_info &&
-		gpio_num_info->valid[SENSOR_VAF] == 1) {
-		rc = cam_soc_util_request_platform_resource(&a_ctrl->soc_info,
-			NULL, NULL);
-		rc = cam_soc_util_enable_platform_resource(&a_ctrl->soc_info,
-			false, 0, false);
-		if (rc < 0) {
-			CAM_ERR(CAM_ACTUATOR, "Failed in req gpio: %d", rc);
-			return rc;
-		}
-
-		gpio_set_value_cansleep(
-			gpio_num_info->gpio_num[SENSOR_VAF],
-			1);
-	}
-
-	/* VREG needs some delay to power up */
-	usleep_range(2000, 2050);
-
-	return rc;
-}
-
-static int32_t cam_actuator_power_down(struct cam_actuator_ctrl_t *a_ctrl)
-{
-	int32_t rc = 0;
-	struct cam_hw_soc_info *soc_info =
-		&a_ctrl->soc_info;
-	struct msm_camera_gpio_num_info *gpio_num_info = NULL;
-
-	rc = cam_actuator_vreg_control(a_ctrl, 0);
-	if (rc < 0) {
-		CAM_ERR(CAM_ACTUATOR, "Failed %d");
-		return rc;
-	}
-
-	gpio_num_info = a_ctrl->gpio_num_info;
-
-	if (soc_info->gpio_data &&
-		gpio_num_info &&
-		gpio_num_info->valid[SENSOR_VAF] == 1) {
-
-		gpio_set_value_cansleep(
-			gpio_num_info->gpio_num[SENSOR_VAF],
-			GPIOF_OUT_INIT_LOW);
-
-		rc = cam_soc_util_release_platform_resource(&a_ctrl->soc_info);
-		rc |= cam_soc_util_disable_platform_resource(&a_ctrl->soc_info,
-					0, 0);
+	if ((a_ctrl->cam_act_state == CAM_ACTUATOR_START) ||
+		(a_ctrl->cam_act_state == CAM_ACTUATOR_ACQUIRE)) {
+		rc = cam_actuator_power_down(a_ctrl);
 		if (rc < 0)
-			CAM_ERR(CAM_ACTUATOR,
-				"Failed to disable platform resources: %d", rc);
-	}
+			CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
 
-	return rc;
+		rc = cam_destroy_device_hdl(a_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_ACTUATOR, "destroying  dhdl failed");
+		a_ctrl->bridge_intf.device_hdl = -1;
+		a_ctrl->bridge_intf.link_hdl = -1;
+		a_ctrl->bridge_intf.session_hdl = -1;
+	}
+	a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
 }
 
 int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl,
@@ -491,7 +553,7 @@
 		return -EINVAL;
 	}
 
-	pr_debug("Opcode to Actuator: %d", cmd->op_code);
+	CAM_DBG(CAM_ACTUATOR, "Opcode to Actuator: %d", cmd->op_code);
 
 	mutex_lock(&(a_ctrl->actuator_mutex));
 	switch (cmd->op_code) {
@@ -533,9 +595,30 @@
 			goto release_mutex;
 		}
 
+		rc = cam_actuator_power_up(a_ctrl);
+		if (rc < 0) {
+			CAM_ERR(CAM_ACTUATOR, " Actuator Power up failed");
+			goto release_mutex;
+		}
+
+		a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
 	}
 		break;
 	case CAM_RELEASE_DEV: {
+		if (a_ctrl->cam_act_state != CAM_ACTUATOR_ACQUIRE) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_ACTUATOR,
+			"Not in right state to release : %d",
+			a_ctrl->cam_act_state);
+			goto release_mutex;
+		}
+
+		rc = cam_actuator_power_down(a_ctrl);
+		if (rc < 0) {
+			CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
+			goto release_mutex;
+		}
+
 		if (a_ctrl->bridge_intf.device_hdl == -1) {
 			CAM_ERR(CAM_ACTUATOR, "link hdl: %d device hdl: %d",
 				a_ctrl->bridge_intf.device_hdl,
@@ -549,12 +632,13 @@
 		a_ctrl->bridge_intf.device_hdl = -1;
 		a_ctrl->bridge_intf.link_hdl = -1;
 		a_ctrl->bridge_intf.session_hdl = -1;
+		a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
 	}
 		break;
 	case CAM_QUERY_CAP: {
 		struct cam_actuator_query_cap actuator_cap = {0};
 
-		actuator_cap.slot_info = a_ctrl->id;
+		actuator_cap.slot_info = a_ctrl->soc_info.index;
 		if (copy_to_user((void __user *) cmd->handle, &actuator_cap,
 			sizeof(struct cam_actuator_query_cap))) {
 			CAM_ERR(CAM_ACTUATOR, "Failed Copy to User");
@@ -564,52 +648,51 @@
 	}
 		break;
 	case CAM_START_DEV: {
-		rc = cam_actuator_power_up(a_ctrl);
-		if (rc < 0) {
-			CAM_ERR(CAM_ACTUATOR, " Actuator Power up failed");
-			goto release_mutex;
-		}
-		rc = camera_io_init(&a_ctrl->io_master_info);
-		if (rc < 0) {
-			CAM_ERR(CAM_ACTUATOR, "cci_init failed");
-			cam_actuator_power_down(a_ctrl);
-		}
-
-		rc = cam_actuator_apply_settings(a_ctrl,
-			&a_ctrl->i2c_data.init_settings);
-		if (rc < 0)
-			CAM_ERR(CAM_ACTUATOR, "Cannot apply Init settings");
-
-		/* Delete the request even if the apply is failed */
-		rc = delete_request(&a_ctrl->i2c_data.init_settings);
-		if (rc < 0) {
-			CAM_ERR(CAM_ACTUATOR,
-				"Fail in deleting the Init settings");
+		if (a_ctrl->cam_act_state != CAM_ACTUATOR_ACQUIRE) {
 			rc = -EINVAL;
+			CAM_WARN(CAM_ACTUATOR,
+			"Not in right state to start : %d",
+			a_ctrl->cam_act_state);
 			goto release_mutex;
 		}
+		a_ctrl->cam_act_state = CAM_ACTUATOR_START;
 	}
 		break;
 	case CAM_STOP_DEV: {
-		rc = camera_io_release(&a_ctrl->io_master_info);
-		if (rc < 0)
-			CAM_ERR(CAM_ACTUATOR, "Failed in releasing CCI");
-		rc = cam_actuator_power_down(a_ctrl);
-		if (rc < 0) {
-			CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
+		struct i2c_settings_array *i2c_set = NULL;
+		int i;
+
+		if (a_ctrl->cam_act_state != CAM_ACTUATOR_START) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_ACTUATOR,
+			"Not in right state to stop : %d",
+			a_ctrl->cam_act_state);
 			goto release_mutex;
 		}
+
+		for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+			i2c_set = &(a_ctrl->i2c_data.per_frame[i]);
+
+			if (i2c_set->is_settings_valid == 1) {
+				rc = delete_request(i2c_set);
+				if (rc < 0)
+					CAM_ERR(CAM_SENSOR,
+						"delete request: %lld rc: %d",
+						i2c_set->request_id, rc);
+			}
+		}
+		a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
 	}
 		break;
 	case CAM_CONFIG_DEV: {
-		a_ctrl->act_apply_state =
+		a_ctrl->setting_apply_state =
 			ACT_APPLY_SETTINGS_LATER;
 		rc = cam_actuator_i2c_pkt_parse(a_ctrl, arg);
 		if (rc < 0) {
 			CAM_ERR(CAM_ACTUATOR, "Failed in actuator Parsing");
 		}
 
-		if (a_ctrl->act_apply_state ==
+		if (a_ctrl->setting_apply_state ==
 			ACT_APPLY_SETTINGS_NOW) {
 			rc = cam_actuator_apply_settings(a_ctrl,
 				&a_ctrl->i2c_data.init_settings);
@@ -628,8 +711,6 @@
 		}
 	}
 		break;
-	case CAM_SD_SHUTDOWN:
-		break;
 	default:
 		CAM_ERR(CAM_ACTUATOR, "Invalid Opcode %d", cmd->op_code);
 	}
@@ -639,3 +720,50 @@
 
 	return rc;
 }
+
+int32_t cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush_req)
+{
+	int32_t rc = 0, i;
+	uint32_t cancel_req_id_found = 0;
+	struct cam_actuator_ctrl_t *a_ctrl = NULL;
+	struct i2c_settings_array *i2c_set = NULL;
+
+	if (!flush_req)
+		return -EINVAL;
+
+	a_ctrl = (struct cam_actuator_ctrl_t *)
+		cam_get_device_priv(flush_req->dev_hdl);
+	if (!a_ctrl) {
+		CAM_ERR(CAM_ACTUATOR, "Device data is NULL");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+		i2c_set = &(a_ctrl->i2c_data.per_frame[i]);
+
+		if ((flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ)
+				&& (i2c_set->request_id != flush_req->req_id))
+			continue;
+
+		if (i2c_set->is_settings_valid == 1) {
+			rc = delete_request(i2c_set);
+			if (rc < 0)
+				CAM_ERR(CAM_ACTUATOR,
+					"delete request: %lld rc: %d",
+					i2c_set->request_id, rc);
+
+			if (flush_req->type ==
+				CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
+				cancel_req_id_found = 1;
+				break;
+			}
+		}
+	}
+
+	if (flush_req->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ &&
+		!cancel_req_id_found)
+		CAM_DBG(CAM_ACTUATOR,
+			"Flush request id:%lld not found in the pending list",
+			flush_req->req_id);
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
index d2cb96d..c28d79d 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_core.h
@@ -16,6 +16,16 @@
 #include "cam_actuator_dev.h"
 
 /**
+ * @power_info: power setting info to control the power
+ *
+ * This API construct the default actuator power setting.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int32_t cam_actuator_construct_default_power_setting(
+	struct cam_sensor_power_ctrl_t *power_info);
+
+/**
  * @apply: Req mgr structure for applying request
  *
  * This API applies the request that is mentioned
@@ -30,6 +40,14 @@
 int32_t cam_actuator_publish_dev_info(struct cam_req_mgr_device_info *info);
 
 /**
+ * @flush: Req mgr structure for flushing request
+ *
+ * This API flushes the request that is mentioned
+ */
+int cam_actuator_flush_request(struct cam_req_mgr_flush_request *flush);
+
+
+/**
  * @link: Link setup info
  *
  * This API establishes link actuator subdevice with req mgr
@@ -45,4 +63,11 @@
  */
 int32_t cam_actuator_driver_cmd(struct cam_actuator_ctrl_t *a_ctrl, void *arg);
 
+/**
+ * @a_ctrl: Actuator ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl);
+
 #endif /* _CAM_ACTUATOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
index e58f737..c5c9b0a 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.c
@@ -79,6 +79,24 @@
 }
 #endif
 
+static int cam_actuator_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_actuator_ctrl_t *a_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!a_ctrl) {
+		CAM_ERR(CAM_ACTUATOR, "a_ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&(a_ctrl->actuator_mutex));
+	cam_actuator_shutdown(a_ctrl);
+	mutex_unlock(&(a_ctrl->actuator_mutex));
+
+	return 0;
+}
+
 static struct v4l2_subdev_core_ops cam_actuator_subdev_core_ops = {
 	.ioctl = cam_actuator_subdev_ioctl,
 #ifdef CONFIG_COMPAT
@@ -90,7 +108,9 @@
 	.core = &cam_actuator_subdev_core_ops,
 };
 
-static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_actuator_internal_ops = {
+	.close = cam_actuator_subdev_close,
+};
 
 static int cam_actuator_init_subdev(struct cam_actuator_ctrl_t *a_ctrl)
 {
@@ -120,9 +140,11 @@
 static int32_t cam_actuator_driver_i2c_probe(struct i2c_client *client,
 	const struct i2c_device_id *id)
 {
-	int32_t rc = 0, i = 0;
-	struct cam_actuator_ctrl_t *a_ctrl;
-	struct cam_hw_soc_info *soc_info = NULL;
+	int32_t                         rc = 0;
+	int32_t                         i = 0;
+	struct cam_actuator_ctrl_t      *a_ctrl;
+	struct cam_hw_soc_info          *soc_info = NULL;
+	struct cam_actuator_soc_private *soc_private = NULL;
 
 	if (client == NULL || id == NULL) {
 		CAM_ERR(CAM_ACTUATOR, "Invalid Args client: %pK id: %pK",
@@ -144,6 +166,14 @@
 
 	i2c_set_clientdata(client, a_ctrl);
 
+	soc_private = kzalloc(sizeof(struct cam_actuator_soc_private),
+		GFP_KERNEL);
+	if (!soc_private) {
+		rc = -ENOMEM;
+		goto free_ctrl;
+	}
+	a_ctrl->soc_info.soc_private = soc_private;
+
 	a_ctrl->io_master_info.client = client;
 	soc_info = &a_ctrl->soc_info;
 	soc_info->dev = &client->dev;
@@ -158,7 +188,11 @@
 
 	rc = cam_actuator_init_subdev(a_ctrl);
 	if (rc)
-		goto free_ctrl;
+		goto free_soc;
+
+	if (soc_private->i2c_info.slave_addr != 0)
+		a_ctrl->io_master_info.client->addr =
+			soc_private->i2c_info.slave_addr;
 
 	a_ctrl->i2c_data.per_frame =
 		(struct i2c_settings_array *)
@@ -174,14 +208,6 @@
 	for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
 		INIT_LIST_HEAD(&(a_ctrl->i2c_data.per_frame[i].list_head));
 
-	rc = cam_soc_util_request_platform_resource(&a_ctrl->soc_info,
-		NULL, NULL);
-	if (rc < 0) {
-		CAM_ERR(CAM_ACTUATOR,
-			"Requesting Platform Resources failed rc %d", rc);
-		goto free_mem;
-	}
-
 	a_ctrl->bridge_intf.device_hdl = -1;
 	a_ctrl->bridge_intf.ops.get_dev_info =
 		cam_actuator_publish_dev_info;
@@ -191,11 +217,24 @@
 		cam_actuator_apply_request;
 
 	v4l2_set_subdevdata(&(a_ctrl->v4l2_dev_str.sd), a_ctrl);
+
+	rc = cam_actuator_construct_default_power_setting(
+		&soc_private->power_info);
+	if (rc < 0) {
+		CAM_ERR(CAM_ACTUATOR,
+			"Construct default actuator power setting failed.");
+		goto free_mem;
+	}
+
+	a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
+
 	return rc;
 free_mem:
 	kfree(a_ctrl->i2c_data.per_frame);
 unreg_subdev:
 	cam_unregister_subdev(&(a_ctrl->v4l2_dev_str));
+free_soc:
+	kfree(soc_private);
 free_ctrl:
 	kfree(a_ctrl);
 	return rc;
@@ -203,8 +242,10 @@
 
 static int32_t cam_actuator_platform_remove(struct platform_device *pdev)
 {
-	struct cam_actuator_ctrl_t  *a_ctrl;
 	int32_t rc = 0;
+	struct cam_actuator_ctrl_t      *a_ctrl;
+	struct cam_actuator_soc_private *soc_private;
+	struct cam_sensor_power_ctrl_t  *power_info;
 
 	a_ctrl = platform_get_drvdata(pdev);
 	if (!a_ctrl) {
@@ -212,8 +253,15 @@
 		return 0;
 	}
 
+	soc_private =
+		(struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
 	kfree(a_ctrl->io_master_info.cci_client);
 	a_ctrl->io_master_info.cci_client = NULL;
+	kfree(power_info->power_setting);
+	kfree(power_info->power_down_setting);
+	kfree(a_ctrl->soc_info.soc_private);
 	kfree(a_ctrl->i2c_data.per_frame);
 	a_ctrl->i2c_data.per_frame = NULL;
 	devm_kfree(&pdev->dev, a_ctrl);
@@ -223,17 +271,29 @@
 
 static int32_t cam_actuator_driver_i2c_remove(struct i2c_client *client)
 {
-	struct cam_actuator_ctrl_t  *a_ctrl = i2c_get_clientdata(client);
 	int32_t rc = 0;
+	struct cam_actuator_ctrl_t      *a_ctrl =
+		i2c_get_clientdata(client);
+	struct cam_actuator_soc_private *soc_private;
+	struct cam_sensor_power_ctrl_t  *power_info;
 
 	/* Handle I2C Devices */
 	if (!a_ctrl) {
 		CAM_ERR(CAM_ACTUATOR, "Actuator device is NULL");
 		return -EINVAL;
 	}
+
+	soc_private =
+		(struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
 	/*Free Allocated Mem */
 	kfree(a_ctrl->i2c_data.per_frame);
 	a_ctrl->i2c_data.per_frame = NULL;
+	kfree(power_info->power_setting);
+	kfree(power_info->power_down_setting);
+	kfree(a_ctrl->soc_info.soc_private);
+	a_ctrl->soc_info.soc_private = NULL;
 	kfree(a_ctrl);
 	return rc;
 }
@@ -246,8 +306,10 @@
 static int32_t cam_actuator_driver_platform_probe(
 	struct platform_device *pdev)
 {
-	int32_t rc = 0, i = 0;
-	struct cam_actuator_ctrl_t *a_ctrl = NULL;
+	int32_t                         rc = 0;
+	int32_t                         i = 0;
+	struct cam_actuator_ctrl_t      *a_ctrl = NULL;
+	struct cam_actuator_soc_private *soc_private = NULL;
 
 	/* Create sensor control structure */
 	a_ctrl = devm_kzalloc(&pdev->dev,
@@ -264,15 +326,28 @@
 
 	a_ctrl->io_master_info.cci_client = kzalloc(sizeof(
 		struct cam_sensor_cci_client), GFP_KERNEL);
-	if (!(a_ctrl->io_master_info.cci_client))
-		return -ENOMEM;
+	if (!(a_ctrl->io_master_info.cci_client)) {
+		rc = -ENOMEM;
+		goto free_ctrl;
+	}
+
+	soc_private = kzalloc(sizeof(struct cam_actuator_soc_private),
+		GFP_KERNEL);
+	if (!soc_private) {
+		rc = -ENOMEM;
+		goto free_cci_client;
+	}
+	a_ctrl->soc_info.soc_private = soc_private;
+	soc_private->power_info.dev = &pdev->dev;
 
 	a_ctrl->i2c_data.per_frame =
 		(struct i2c_settings_array *)
 		kzalloc(sizeof(struct i2c_settings_array) *
 		MAX_PER_FRAME_ARRAY, GFP_KERNEL);
-	if (a_ctrl->i2c_data.per_frame == NULL)
-		return -ENOMEM;
+	if (a_ctrl->i2c_data.per_frame == NULL) {
+		rc = -ENOMEM;
+		goto free_soc;
+	}
 
 	INIT_LIST_HEAD(&(a_ctrl->i2c_data.init_settings.list_head));
 
@@ -282,24 +357,16 @@
 	rc = cam_actuator_parse_dt(a_ctrl, &(pdev->dev));
 	if (rc < 0) {
 		CAM_ERR(CAM_ACTUATOR, "Paring actuator dt failed rc %d", rc);
-		goto free_ctrl;
+		goto free_mem;
 	}
 
 	/* Fill platform device id*/
-	pdev->id = a_ctrl->id;
+	pdev->id = a_ctrl->soc_info.index;
 
 	rc = cam_actuator_init_subdev(a_ctrl);
 	if (rc)
 		goto free_mem;
 
-	rc = cam_soc_util_request_platform_resource(&a_ctrl->soc_info,
-			NULL, NULL);
-	if (rc < 0) {
-		CAM_ERR(CAM_ACTUATOR,
-			"Requesting Platform Resources failed rc %d", rc);
-		goto unreg_subdev;
-	}
-
 	a_ctrl->bridge_intf.device_hdl = -1;
 	a_ctrl->bridge_intf.ops.get_dev_info =
 		cam_actuator_publish_dev_info;
@@ -307,15 +374,29 @@
 		cam_actuator_establish_link;
 	a_ctrl->bridge_intf.ops.apply_req =
 		cam_actuator_apply_request;
+	a_ctrl->bridge_intf.ops.flush_req =
+		cam_actuator_flush_request;
 
 	platform_set_drvdata(pdev, a_ctrl);
 	v4l2_set_subdevdata(&a_ctrl->v4l2_dev_str.sd, a_ctrl);
 
+	rc = cam_actuator_construct_default_power_setting(
+		&soc_private->power_info);
+	if (rc < 0) {
+		CAM_ERR(CAM_ACTUATOR,
+			"Construct default actuator power setting failed.");
+		goto unreg_subdev;
+	}
+
 	return rc;
 unreg_subdev:
 	cam_unregister_subdev(&(a_ctrl->v4l2_dev_str));
 free_mem:
 	kfree(a_ctrl->i2c_data.per_frame);
+free_soc:
+	kfree(soc_private);
+free_cci_client:
+	kfree(a_ctrl->io_master_info.cci_client);
 free_ctrl:
 	devm_kfree(&pdev->dev, a_ctrl);
 	return rc;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
index fdf881f3..8b8b1ef 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_dev.h
@@ -48,11 +48,32 @@
 #define ACTUATOR_MAX_POLL_COUNT 10
 
 
-enum msm_actuator_state_t {
+enum cam_actuator_apply_state_t {
 	ACT_APPLY_SETTINGS_NOW,
 	ACT_APPLY_SETTINGS_LATER,
 };
 
+enum cam_actuator_state {
+	CAM_ACTUATOR_INIT,
+	CAM_ACTUATOR_ACQUIRE,
+	CAM_ACTUATOR_START,
+};
+
+/**
+ * struct cam_actuator_i2c_info_t - I2C info
+ * @slave_addr      :   slave address
+ * @i2c_freq_mode   :   i2c frequency mode
+ */
+struct cam_actuator_i2c_info_t {
+	uint16_t slave_addr;
+	uint8_t i2c_freq_mode;
+};
+
+struct cam_actuator_soc_private {
+	struct cam_actuator_i2c_info_t i2c_info;
+	struct cam_sensor_power_ctrl_t power_info;
+};
+
 /**
  * struct intf_params
  * @device_hdl: Device Handle
@@ -75,8 +96,10 @@
  * @cci_i2c_master: I2C structure
  * @io_master_info: Information about the communication master
  * @actuator_mutex: Actuator mutex
- * @id: Cell Index
  * @act_apply_state: Actuator settings aRegulator config
+ * @id: Cell Index
+ * @res_apply_state: Actuator settings apply state
+ * @cam_act_state:   Actuator state
  * @gconf: GPIO config
  * @pinctrl_info: Pinctrl information
  * @v4l2_dev_str: V4L2 device structure
@@ -92,8 +115,8 @@
 	struct cam_hw_soc_info soc_info;
 	struct mutex actuator_mutex;
 	uint32_t id;
-	enum msm_actuator_state_t act_apply_state;
-	struct msm_camera_gpio_num_info *gpio_num_info;
+	enum cam_actuator_apply_state_t setting_apply_state;
+	enum cam_actuator_state cam_act_state;
 	uint8_t cam_pinctrl_status;
 	struct cam_subdev v4l2_dev_str;
 	struct i2c_data_settings i2c_data;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c
index f47ec2f..55b7c72 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_actuator/cam_actuator_soc.c
@@ -22,9 +22,12 @@
 int32_t cam_actuator_parse_dt(struct cam_actuator_ctrl_t *a_ctrl,
 	struct device *dev)
 {
-	int32_t                   rc = 0;
-	struct cam_hw_soc_info *soc_info = &a_ctrl->soc_info;
-	struct device_node *of_node = NULL;
+	int32_t                         rc = 0;
+	struct cam_hw_soc_info          *soc_info = &a_ctrl->soc_info;
+	struct cam_actuator_soc_private *soc_private =
+		(struct cam_actuator_soc_private *)a_ctrl->soc_info.soc_private;
+	struct cam_sensor_power_ctrl_t  *power_info = &soc_private->power_info;
+	struct device_node              *of_node = NULL;
 
 	/* Initialize mutex */
 	mutex_init(&(a_ctrl->actuator_mutex));
@@ -61,9 +64,8 @@
 	}
 
 	rc = cam_sensor_util_init_gpio_pin_tbl(soc_info,
-		&a_ctrl->gpio_num_info);
-
-	if ((rc < 0) || (!a_ctrl->gpio_num_info)) {
+		&power_info->gpio_num_info);
+	if ((rc < 0) || (!power_info->gpio_num_info)) {
 		CAM_ERR(CAM_ACTUATOR, "No/Error Actuator GPIOs");
 		return -EINVAL;
 	}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
index c62b251..d7a6504 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_core.c
@@ -441,7 +441,7 @@
 			if (cmd->reg_addr + 1 ==
 				(cmd+1)->reg_addr) {
 				len += data_len;
-				*pack += data_len;
+				(*pack)++;
 			} else {
 				break;
 			}
@@ -730,10 +730,30 @@
 					reg_addr++;
 			} else {
 				if ((i + 1) <= cci_dev->payload_size) {
-					data[i++] = (i2c_cmd->reg_data &
-						0xFF00) >> 8; /* MSB */
-					data[i++] = i2c_cmd->reg_data &
-						0x00FF; /* LSB */
+					switch (i2c_msg->data_type) {
+					case CAMERA_SENSOR_I2C_TYPE_DWORD:
+						data[i++] = (i2c_cmd->reg_data &
+							0xFF000000) >> 24;
+						/* fallthrough */
+					case CAMERA_SENSOR_I2C_TYPE_3B:
+						data[i++] = (i2c_cmd->reg_data &
+							0x00FF0000) >> 16;
+						/* fallthrough */
+					case CAMERA_SENSOR_I2C_TYPE_WORD:
+						data[i++] = (i2c_cmd->reg_data &
+							0x0000FF00) >> 8;
+						/* fallthrough */
+					case CAMERA_SENSOR_I2C_TYPE_BYTE:
+						data[i++] = i2c_cmd->reg_data &
+							0x000000FF;
+						break;
+					default:
+						CAM_ERR(CAM_CCI,
+							"invalid data type: %d",
+							i2c_msg->data_type);
+						return -EINVAL;
+					}
+
 					if (c_ctrl->cmd ==
 						MSM_CCI_I2C_WRITE_SEQ)
 						reg_addr++;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
index 6aa7586..6cfb965 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.c
@@ -30,6 +30,11 @@
 {
 	int32_t rc = 0;
 
+	if (arg == NULL) {
+		CAM_ERR(CAM_CCI, "Invalid Args");
+		return rc;
+	}
+
 	switch (cmd) {
 	case VIDIOC_MSM_CCI_CFG:
 		rc = cam_cci_core_cfg(sd, arg);
@@ -44,6 +49,14 @@
 	return rc;
 }
 
+#ifdef CONFIG_COMPAT
+static long cam_cci_subdev_compat_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, unsigned long arg)
+{
+	return cam_cci_subdev_ioctl(sd, cmd, NULL);
+}
+#endif
+
 irqreturn_t cam_cci_irq(int irq_num, void *data)
 {
 	uint32_t irq;
@@ -162,6 +175,9 @@
 
 static struct v4l2_subdev_core_ops cci_subdev_core_ops = {
 	.ioctl = cam_cci_subdev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = cam_cci_subdev_compat_ioctl,
+#endif
 	.interrupt_service_routine = cam_cci_irq_routine,
 };
 
@@ -171,6 +187,34 @@
 
 static const struct v4l2_subdev_internal_ops cci_subdev_intern_ops;
 
+static struct v4l2_file_operations cci_v4l2_subdev_fops;
+
+static long cam_cci_subdev_do_ioctl(
+	struct file *file, unsigned int cmd, void *arg)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+	return cam_cci_subdev_ioctl(sd, cmd, NULL);
+}
+
+static long cam_cci_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	return video_usercopy(file, cmd, arg, cam_cci_subdev_do_ioctl);
+}
+
+#ifdef CONFIG_COMPAT
+static long cam_cci_subdev_fops_compat_ioctl(struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+	return v4l2_subdev_call(sd, core, ioctl, cmd, NULL);
+}
+#endif
+
 static int cam_cci_platform_probe(struct platform_device *pdev)
 {
 	struct cam_cpas_register_params cpas_parms;
@@ -222,6 +266,13 @@
 	v4l2_set_subdevdata(&new_cci_dev->v4l2_dev_str.sd, new_cci_dev);
 	g_cci_subdev = &new_cci_dev->v4l2_dev_str.sd;
 
+	cam_register_subdev_fops(&cci_v4l2_subdev_fops);
+	cci_v4l2_subdev_fops.unlocked_ioctl = cam_cci_subdev_fops_ioctl;
+#ifdef CONFIG_COMPAT
+	cci_v4l2_subdev_fops.compat_ioctl32 =
+		cam_cci_subdev_fops_compat_ioctl;
+#endif
+
 	cpas_parms.cam_cpas_client_cb = NULL;
 	cpas_parms.cell_index = 0;
 	cpas_parms.dev = &pdev->dev;
@@ -271,6 +322,26 @@
 	},
 };
 
+static int cam_cci_assign_fops(void)
+{
+	struct v4l2_subdev *sd;
+
+	sd = g_cci_subdev;
+	if (!sd || !(sd->devnode)) {
+		CAM_ERR(CAM_CRM,
+			"Invalid args sd node: %pK", sd);
+		return -EINVAL;
+	}
+	sd->devnode->fops = &cci_v4l2_subdev_fops;
+
+	return 0;
+}
+
+static int __init cam_cci_late_init(void)
+{
+	return cam_cci_assign_fops();
+}
+
 static int __init cam_cci_init_module(void)
 {
 	return platform_driver_register(&cci_driver);
@@ -282,6 +353,7 @@
 }
 
 module_init(cam_cci_init_module);
+late_initcall(cam_cci_late_init);
 module_exit(cam_cci_exit_module);
 MODULE_DESCRIPTION("MSM CCI driver");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
index 4c996e08..d0ee0f6 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_dev.h
@@ -289,7 +289,7 @@
 irqreturn_t cam_cci_irq(int irq_num, void *data);
 
 #ifdef CONFIG_SPECTRA_CAMERA
-struct v4l2_subdev *cam_cci_get_subdev(void);
+extern struct v4l2_subdev *cam_cci_get_subdev(void);
 #else
 static inline struct v4l2_subdev *cam_cci_get_subdev(void)
 {
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
index 8de4472..cf7a65f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cam_cci_soc.c
@@ -99,7 +99,7 @@
 
 	/* Enable Regulators and IRQ*/
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, true);
+		CAM_LOWSVS_VOTE, true);
 	if (rc < 0) {
 		CAM_DBG(CAM_CCI, "request platform resources failed");
 		goto platform_enable_failed;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
index bdd3e72..cb44cb8 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c
@@ -14,10 +14,33 @@
 #include "cam_csiphy_core.h"
 #include "cam_csiphy_dev.h"
 #include "cam_csiphy_soc.h"
+
+#include <soc/qcom/scm.h>
 #include <cam_mem_mgr.h>
 
-static int cam_csiphy_mem_dmp_param;
-module_param(cam_csiphy_mem_dmp_param, int, 0644);
+#define SCM_SVC_CAMERASS 0x18
+#define SECURE_SYSCALL_ID 0x6
+
+static int csiphy_dump;
+module_param(csiphy_dump, int, 0644);
+
+static int cam_csiphy_notify_secure_mode(int phy, bool protect)
+{
+	struct scm_desc desc = {0};
+
+	desc.arginfo = SCM_ARGS(2, SCM_VAL, SCM_VAL);
+	desc.args[0] = protect;
+	desc.args[1] = phy;
+
+	CAM_DBG(CAM_CSIPHY, "phy : %d, protect : %d", phy, protect);
+	if (scm_call2(SCM_SIP_FNID(SCM_SVC_CAMERASS, SECURE_SYSCALL_ID),
+		&desc)) {
+		CAM_ERR(CAM_CSIPHY, "scm call to hypervisor failed");
+		return -EINVAL;
+	}
+
+	return 0;
+}
 
 void cam_csiphy_query_cap(struct csiphy_device *csiphy_dev,
 	struct cam_csiphy_query_cap *csiphy_cap)
@@ -70,17 +93,10 @@
 		return -EINVAL;
 	}
 
-	csiphy_dev->csiphy_info =
-		kzalloc(sizeof(struct cam_csiphy_info), GFP_KERNEL);
-	if (!csiphy_dev->csiphy_info)
-		return -ENOMEM;
-
 	rc = cam_mem_get_cpu_buf((int32_t) cfg_dev->packet_handle,
 		(uint64_t *)&generic_ptr, &len);
 	if (rc < 0) {
 		CAM_ERR(CAM_CSIPHY, "Failed to get packet Mem address: %d", rc);
-		kfree(csiphy_dev->csiphy_info);
-		csiphy_dev->csiphy_info = NULL;
 		return rc;
 	}
 
@@ -88,8 +104,6 @@
 		CAM_ERR(CAM_CSIPHY,
 			"offset is out of bounds: offset: %lld len: %zu",
 			cfg_dev->offset, len);
-		kfree(csiphy_dev->csiphy_info);
-		csiphy_dev->csiphy_info = NULL;
 		return -EINVAL;
 	}
 
@@ -104,8 +118,6 @@
 	if (rc < 0) {
 		CAM_ERR(CAM_CSIPHY,
 			"Failed to get cmd buf Mem address : %d", rc);
-		kfree(csiphy_dev->csiphy_info);
-		csiphy_dev->csiphy_info = NULL;
 		return rc;
 	}
 
@@ -113,13 +125,26 @@
 	cmd_buf += cmd_desc->offset / 4;
 	cam_cmd_csiphy_info = (struct cam_csiphy_info *)cmd_buf;
 
-	csiphy_dev->csiphy_info->lane_cnt = cam_cmd_csiphy_info->lane_cnt;
-	csiphy_dev->csiphy_info->lane_mask = cam_cmd_csiphy_info->lane_mask;
-	csiphy_dev->csiphy_info->csiphy_3phase =
+	csiphy_dev->config_count++;
+	csiphy_dev->csiphy_info.lane_cnt += cam_cmd_csiphy_info->lane_cnt;
+	csiphy_dev->csiphy_info.lane_mask |= cam_cmd_csiphy_info->lane_mask;
+	csiphy_dev->csiphy_info.csiphy_3phase =
 		cam_cmd_csiphy_info->csiphy_3phase;
-	csiphy_dev->csiphy_info->combo_mode = cam_cmd_csiphy_info->combo_mode;
-	csiphy_dev->csiphy_info->settle_time = cam_cmd_csiphy_info->settle_time;
-	csiphy_dev->csiphy_info->data_rate = cam_cmd_csiphy_info->data_rate;
+	csiphy_dev->csiphy_info.combo_mode |= cam_cmd_csiphy_info->combo_mode;
+	if (cam_cmd_csiphy_info->combo_mode == 1)
+		csiphy_dev->csiphy_info.settle_time_combo_sensor =
+			cam_cmd_csiphy_info->settle_time;
+	else
+		csiphy_dev->csiphy_info.settle_time =
+			cam_cmd_csiphy_info->settle_time;
+	csiphy_dev->csiphy_info.data_rate = cam_cmd_csiphy_info->data_rate;
+	csiphy_dev->csiphy_info.secure_mode = cam_cmd_csiphy_info->secure_mode;
+
+	if (csiphy_dev->csiphy_info.secure_mode &&
+		(csiphy_dev->config_count == 1))
+		rc = cam_csiphy_notify_secure_mode(
+			csiphy_dev->soc_info.index,
+			CAM_SECURE_MODE_SECURE);
 
 	return rc;
 }
@@ -205,14 +230,8 @@
 	void __iomem *csiphybase;
 	struct csiphy_reg_t (*reg_array)[MAX_SETTINGS_PER_LANE];
 
-	if (csiphy_dev->csiphy_info == NULL) {
-		CAM_ERR(CAM_CSIPHY, "csiphy_info is NULL, No/Fail CONFIG_DEV?");
-		return -EINVAL;
-	}
-
-	lane_cnt = csiphy_dev->csiphy_info->lane_cnt;
-	lane_mask = csiphy_dev->csiphy_info->lane_mask & 0x1f;
-	settle_cnt = (csiphy_dev->csiphy_info->settle_time / 200000000);
+	lane_cnt = csiphy_dev->csiphy_info.lane_cnt;
+	lane_mask = csiphy_dev->csiphy_info.lane_mask & 0x1f;
 	csiphybase = csiphy_dev->soc_info.reg_map[0].mem_base;
 
 	if (!csiphybase) {
@@ -231,8 +250,8 @@
 		mask <<= 1;
 	}
 
-	if (!csiphy_dev->csiphy_info->csiphy_3phase) {
-		if (csiphy_dev->csiphy_info->combo_mode == 1)
+	if (!csiphy_dev->csiphy_info.csiphy_3phase) {
+		if (csiphy_dev->csiphy_info.combo_mode == 1)
 			reg_array =
 				csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg;
 		else
@@ -242,7 +261,7 @@
 		cfg_size = csiphy_dev->ctrl_reg->csiphy_reg.
 			csiphy_2ph_config_array_size;
 	} else {
-		if (csiphy_dev->csiphy_info->combo_mode == 1)
+		if (csiphy_dev->csiphy_info.combo_mode == 1)
 			reg_array =
 				csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg;
 		else
@@ -283,6 +302,12 @@
 			continue;
 		}
 
+		settle_cnt = (csiphy_dev->csiphy_info.settle_time / 200000000);
+		if (csiphy_dev->csiphy_info.combo_mode == 1 &&
+			(lane_pos >= 3))
+			settle_cnt =
+				(csiphy_dev->csiphy_info.
+				settle_time_combo_sensor / 200000000);
 		for (i = 0; i < cfg_size; i++) {
 			switch (reg_array[lane_pos][i].csiphy_param_type) {
 			case CSIPHY_LANE_ENABLE:
@@ -321,6 +346,44 @@
 	return rc;
 }
 
+void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev)
+{
+	struct cam_hw_soc_info *soc_info;
+
+	if (csiphy_dev->csiphy_state == CAM_CSIPHY_INIT)
+		return;
+
+	if (csiphy_dev->csiphy_state == CAM_CSIPHY_START) {
+		soc_info = &csiphy_dev->soc_info;
+
+		cam_csiphy_reset(csiphy_dev);
+		cam_soc_util_disable_platform_resource(soc_info, true, true);
+
+		cam_cpas_stop(csiphy_dev->cpas_handle);
+		csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
+	}
+
+	if (csiphy_dev->csiphy_state == CAM_CSIPHY_ACQUIRE) {
+		if (csiphy_dev->bridge_intf.device_hdl[0] != -1)
+			cam_destroy_device_hdl(
+				csiphy_dev->bridge_intf.device_hdl[0]);
+		if (csiphy_dev->bridge_intf.device_hdl[1] != -1)
+			cam_destroy_device_hdl(
+				csiphy_dev->bridge_intf.device_hdl[1]);
+		csiphy_dev->bridge_intf.device_hdl[0] = -1;
+		csiphy_dev->bridge_intf.device_hdl[1] = -1;
+		csiphy_dev->bridge_intf.link_hdl[0] = -1;
+		csiphy_dev->bridge_intf.link_hdl[1] = -1;
+		csiphy_dev->bridge_intf.session_hdl[0] = -1;
+		csiphy_dev->bridge_intf.session_hdl[1] = -1;
+	}
+
+	csiphy_dev->ref_count = 0;
+	csiphy_dev->is_acquired_dev_combo_mode = 0;
+	csiphy_dev->acquire_count = 0;
+	csiphy_dev->csiphy_state = CAM_CSIPHY_INIT;
+}
+
 int32_t cam_csiphy_core_cfg(void *phy_dev,
 			void *arg)
 {
@@ -353,6 +416,14 @@
 
 		csiphy_acq_params.combo_mode = 0;
 
+		if (copy_from_user(&csiphy_acq_params,
+			(void __user *)csiphy_acq_dev.info_handle,
+			sizeof(csiphy_acq_params))) {
+			CAM_ERR(CAM_CSIPHY,
+				"Failed copying from User");
+			goto release_mutex;
+		}
+
 		if (csiphy_dev->acquire_count == 2) {
 			CAM_ERR(CAM_CSIPHY,
 					"CSIPHY device do not allow more than 2 acquires");
@@ -360,6 +431,27 @@
 			goto release_mutex;
 		}
 
+		if ((csiphy_acq_params.combo_mode == 1) &&
+			(csiphy_dev->is_acquired_dev_combo_mode == 1)) {
+			CAM_ERR(CAM_CSIPHY,
+				"Multiple Combo Acq are not allowed: cm: %d, acm: %d",
+				csiphy_acq_params.combo_mode,
+				csiphy_dev->is_acquired_dev_combo_mode);
+			rc = -EINVAL;
+			goto release_mutex;
+		}
+
+		if ((csiphy_acq_params.combo_mode != 1) &&
+			(csiphy_dev->is_acquired_dev_combo_mode != 1) &&
+			(csiphy_dev->acquire_count == 1)) {
+			CAM_ERR(CAM_CSIPHY,
+				"Multiple Acquires are not allowed cm: %d acm: %d",
+				csiphy_acq_params.combo_mode,
+				csiphy_dev->is_acquired_dev_combo_mode);
+			rc = -EINVAL;
+			goto release_mutex;
+		}
+
 		bridge_params.ops = NULL;
 		bridge_params.session_hdl = csiphy_acq_dev.session_handle;
 		bridge_params.v4l2_sub_dev_flag = 0;
@@ -384,7 +476,9 @@
 		}
 		if (csiphy_acq_params.combo_mode == 1)
 			csiphy_dev->is_acquired_dev_combo_mode = 1;
+
 		csiphy_dev->acquire_count++;
+		csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
 	}
 		break;
 	case CAM_QUERY_CAP: {
@@ -400,17 +494,28 @@
 	}
 		break;
 	case CAM_STOP_DEV: {
+		if ((csiphy_dev->csiphy_state != CAM_CSIPHY_START) ||
+			!csiphy_dev->start_dev_count) {
+			CAM_ERR(CAM_CSIPHY, "Not in right state to stop : %d",
+				csiphy_dev->csiphy_state);
+			goto release_mutex;
+		}
+
+		if (--csiphy_dev->start_dev_count) {
+			CAM_DBG(CAM_CSIPHY, "Stop Dev ref Cnt: %d",
+				csiphy_dev->start_dev_count);
+			goto release_mutex;
+		}
+
 		rc = cam_csiphy_disable_hw(csiphy_dev);
-		if (rc < 0) {
+		if (rc < 0)
 			CAM_ERR(CAM_CSIPHY, "Failed in csiphy release");
-			cam_cpas_stop(csiphy_dev->cpas_handle);
-			goto release_mutex;
-		}
+
 		rc = cam_cpas_stop(csiphy_dev->cpas_handle);
-		if (rc < 0) {
+		if (rc < 0)
 			CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc);
-			goto release_mutex;
-		}
+
+		csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
 	}
 		break;
 	case CAM_RELEASE_DEV: {
@@ -439,10 +544,24 @@
 		} else {
 			csiphy_dev->bridge_intf.device_hdl[1] = -1;
 			csiphy_dev->bridge_intf.link_hdl[1] = -1;
-			csiphy_dev->bridge_intf.
-				session_hdl[1] = -1;
+			csiphy_dev->bridge_intf.session_hdl[1] = -1;
+			csiphy_dev->is_acquired_dev_combo_mode = 0;
 		}
+
+		csiphy_dev->config_count--;
 		csiphy_dev->acquire_count--;
+
+		if (csiphy_dev->csiphy_info.secure_mode &&
+			(!csiphy_dev->config_count)) {
+			csiphy_dev->csiphy_info.secure_mode =
+				CAM_SECURE_MODE_NON_SECURE;
+			rc = cam_csiphy_notify_secure_mode(
+				csiphy_dev->soc_info.index,
+				CAM_SECURE_MODE_NON_SECURE);
+		}
+
+		if (csiphy_dev->acquire_count == 0)
+			csiphy_dev->csiphy_state = CAM_CSIPHY_INIT;
 	}
 		break;
 	case CAM_CONFIG_DEV: {
@@ -464,6 +583,11 @@
 		struct cam_ahb_vote ahb_vote;
 		struct cam_axi_vote axi_vote;
 
+		if (csiphy_dev->csiphy_state == CAM_CSIPHY_START) {
+			csiphy_dev->start_dev_count++;
+			goto release_mutex;
+		}
+
 		ahb_vote.type = CAM_VOTE_ABSOLUTE;
 		ahb_vote.vote.level = CAM_SVS_VOTE;
 		axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW;
@@ -483,18 +607,19 @@
 			goto release_mutex;
 		}
 		rc = cam_csiphy_config_dev(csiphy_dev);
-		if (cam_csiphy_mem_dmp_param == 1)
+		if (csiphy_dump == 1)
 			cam_csiphy_mem_dmp(&csiphy_dev->soc_info);
 
 		if (rc < 0) {
 			CAM_ERR(CAM_CSIPHY, "cam_csiphy_config_dev failed");
+			cam_csiphy_disable_hw(csiphy_dev);
 			cam_cpas_stop(csiphy_dev->cpas_handle);
 			goto release_mutex;
 		}
+		csiphy_dev->start_dev_count++;
+		csiphy_dev->csiphy_state = CAM_CSIPHY_START;
 	}
 		break;
-	case CAM_SD_SHUTDOWN:
-		break;
 	default:
 		CAM_ERR(CAM_CSIPHY, "Invalid Opcode: %d", cmd->op_code);
 		rc = -EINVAL;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
index 6eeeea4..361004b 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h
@@ -49,4 +49,11 @@
  */
 irqreturn_t cam_csiphy_irq(int irq_num, void *data);
 
+/**
+ * @csiphy_dev: CSIPhy device structure
+ *
+ * This API handles the CSIPhy close
+ */
+void cam_csiphy_shutdown(struct csiphy_device *csiphy_dev);
+
 #endif /* _CAM_CSIPHY_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
index 6e74344..e2f061f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c
@@ -38,6 +38,24 @@
 	return rc;
 }
 
+static int cam_csiphy_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct csiphy_device *csiphy_dev =
+		v4l2_get_subdevdata(sd);
+
+	if (!csiphy_dev) {
+		CAM_ERR(CAM_CSIPHY, "csiphy_dev ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&csiphy_dev->mutex);
+	cam_csiphy_shutdown(csiphy_dev);
+	mutex_unlock(&csiphy_dev->mutex);
+
+	return 0;
+}
+
 #ifdef CONFIG_COMPAT
 static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, unsigned long arg)
@@ -89,7 +107,9 @@
 	.core = &csiphy_subdev_core_ops,
 };
 
-static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops;
+static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops = {
+	.close = cam_csiphy_subdev_close,
+};
 
 static int32_t cam_csiphy_platform_probe(struct platform_device *pdev)
 {
@@ -157,6 +177,7 @@
 		NULL;
 
 	new_csiphy_dev->acquire_count = 0;
+	new_csiphy_dev->start_dev_count = 0;
 	new_csiphy_dev->is_acquired_dev_combo_mode = 0;
 
 	cpas_parms.cam_cpas_client_cb = NULL;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
index 8ed5ba4..afe4239 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.h
@@ -67,6 +67,12 @@
 #define CDBG(fmt, args...) pr_debug(fmt, ##args)
 #endif
 
+enum cam_csiphy_state {
+	CAM_CSIPHY_INIT,
+	CAM_CSIPHY_ACQUIRE,
+	CAM_CSIPHY_START,
+};
+
 /**
  * struct csiphy_reg_parms_t
  * @mipi_csiphy_glbl_irq_cmd_addr: CSIPhy irq addr
@@ -150,6 +156,32 @@
 };
 
 /**
+ * cam_csiphy_param: Provides cmdbuffer structre
+ * @lane_mask     :  Lane mask details
+ * @lane_assign   :  Lane sensor will be using
+ * @csiphy_3phase :  Mentions DPHY or CPHY
+ * @combo_mode    :  Info regarding combo_mode is enable / disable
+ * @lane_cnt      :  Total number of lanes
+ * @reserved
+ * @3phase        :  Details whether 3Phase / 2Phase operation
+ * @settle_time   :  Settling time in ms
+ * @settle_time_combo_sensor   :  Settling time in ms
+ * @data_rate     :  Data rate in mbps
+ *
+ */
+struct cam_csiphy_param {
+	uint16_t    lane_mask;
+	uint16_t    lane_assign;
+	uint8_t     csiphy_3phase;
+	uint8_t     combo_mode;
+	uint8_t     lane_cnt;
+	uint8_t     secure_mode;
+	uint64_t    settle_time;
+	uint64_t    settle_time_combo_sensor;
+	uint64_t    data_rate;
+};
+
+/**
  * struct csiphy_device
  * @pdev: Platform device
  * @irq: Interrupt structure
@@ -171,6 +203,7 @@
  * @ref_count: Reference count
  * @clk_lane: Clock lane
  * @acquire_count: Acquire device count
+ * @start_dev_count: Start count
  * @is_acquired_dev_combo_mode:
  *    Flag that mentions whether already acquired
  *   device is for combo mode
@@ -178,7 +211,7 @@
 struct csiphy_device {
 	struct mutex mutex;
 	uint32_t hw_version;
-	uint32_t csiphy_state;
+	enum cam_csiphy_state csiphy_state;
 	struct csiphy_ctrl_t *ctrl_reg;
 	uint32_t csiphy_max_clk;
 	struct msm_cam_clk_info csiphy_3p_clk_info[2];
@@ -190,14 +223,16 @@
 	uint8_t is_csiphy_3phase_hw;
 	uint8_t num_irq_registers;
 	struct cam_subdev v4l2_dev_str;
-	struct cam_csiphy_info *csiphy_info;
+	struct cam_csiphy_param csiphy_info;
 	struct intf_params bridge_intf;
 	uint32_t clk_lane;
 	uint32_t acquire_count;
+	uint32_t start_dev_count;
 	char device_name[20];
 	uint32_t is_acquired_dev_combo_mode;
 	struct cam_hw_soc_info   soc_info;
 	uint32_t cpas_handle;
+	uint32_t config_count;
 };
 
 #endif /* _CAM_CSIPHY_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
index 506fc0e..6db5a97 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_soc.c
@@ -14,6 +14,47 @@
 #include "cam_csiphy_core.h"
 #include "include/cam_csiphy_1_0_hwreg.h"
 
+#define BYTES_PER_REGISTER           4
+#define NUM_REGISTER_PER_LINE        4
+#define REG_OFFSET(__start, __i)    ((__start) + ((__i) * BYTES_PER_REGISTER))
+
+static int cam_io_phy_dump(void __iomem *base_addr,
+	uint32_t start_offset, int size)
+{
+	char          line_str[128];
+	char         *p_str;
+	int           i;
+	uint32_t      data;
+
+	CAM_INFO(CAM_CSIPHY, "addr=%pK offset=0x%x size=%d",
+		base_addr, start_offset, size);
+
+	if (!base_addr || (size <= 0))
+		return -EINVAL;
+
+	line_str[0] = '\0';
+	p_str = line_str;
+	for (i = 0; i < size; i++) {
+		if (i % NUM_REGISTER_PER_LINE == 0) {
+			snprintf(p_str, 12, "0x%08x: ",
+				REG_OFFSET(start_offset, i));
+			p_str += 11;
+		}
+		data = readl_relaxed(base_addr + REG_OFFSET(start_offset, i));
+		snprintf(p_str, 9, "%08x ", data);
+		p_str += 8;
+		if ((i + 1) % NUM_REGISTER_PER_LINE == 0) {
+			CAM_ERR(CAM_CSIPHY, "%s", line_str);
+			line_str[0] = '\0';
+			p_str = line_str;
+		}
+	}
+	if (line_str[0] != '\0')
+		CAM_ERR(CAM_CSIPHY, "%s", line_str);
+
+	return 0;
+}
+
 int32_t cam_csiphy_mem_dmp(struct cam_hw_soc_info *soc_info)
 {
 	int32_t rc = 0;
@@ -27,7 +68,7 @@
 	}
 	addr = soc_info->reg_map[0].mem_base;
 	size = resource_size(soc_info->mem_block[0]);
-	rc = cam_io_dump(addr, 0, (size >> 2));
+	rc = cam_io_phy_dump(addr, 0, (size >> 2));
 	if (rc < 0) {
 		CAM_ERR(CAM_CSIPHY, "generating dump failed %d", rc);
 		return rc;
@@ -49,7 +90,7 @@
 	}
 
 	rc = cam_soc_util_enable_platform_resource(soc_info, true,
-		CAM_TURBO_VOTE, ENABLE_IRQ);
+		CAM_SVS_VOTE, ENABLE_IRQ);
 	if (rc < 0) {
 		CAM_ERR(CAM_CSIPHY, "failed to enable platform resources %d",
 			rc);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
index 7a4aede..2977834 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/include/cam_csiphy_1_0_hwreg.h
@@ -65,7 +65,7 @@
 		{0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
 		{0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -81,7 +81,7 @@
 		{0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -97,7 +97,7 @@
 		{0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
 		{0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -113,7 +113,7 @@
 		{0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
 		{0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -129,7 +129,7 @@
 		{0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
 		{0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 };
@@ -148,7 +148,7 @@
 		{0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
 		{0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0060, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -164,7 +164,7 @@
 		{0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0760, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -179,7 +179,7 @@
 		{0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
 		{0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0260, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -194,7 +194,7 @@
 		{0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
 		{0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0460, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 	{
@@ -210,7 +210,7 @@
 		{0x060C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0638, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
-		{0x0660, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+		{0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
 		{0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
 	},
 };
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
index 02b2c51..72b1779 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.c
@@ -176,6 +176,8 @@
 		return rc;
 	}
 
+	power_info->dev = soc_info->dev;
+
 	rc = cam_sensor_core_power_up(power_info, soc_info);
 	if (rc) {
 		CAM_ERR(CAM_EEPROM, "failed in eeprom power up rc %d", rc);
@@ -243,7 +245,8 @@
 	struct camera_io_master *client = &e_ctrl->io_master_info;
 	uint8_t                  id[2];
 
-	rc = cam_spi_query_id(client, 0, &id[0], 2);
+	rc = cam_spi_query_id(client, 0, CAMERA_SENSOR_I2C_TYPE_WORD,
+		&id[0], 2);
 	if (rc)
 		return rc;
 	CAM_DBG(CAM_EEPROM, "read 0x%x 0x%x, check 0x%x 0x%x",
@@ -288,6 +291,8 @@
 		CAM_ERR(CAM_EEPROM, "failed: eeprom power up rc %d", rc);
 		goto data_mem_free;
 	}
+
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_CONFIG;
 	if (e_ctrl->eeprom_device_type == MSM_CAMERA_SPI_DEVICE) {
 		rc = cam_eeprom_match_id(e_ctrl);
 		if (rc) {
@@ -304,12 +309,17 @@
 	rc = cam_eeprom_power_down(e_ctrl);
 	if (rc)
 		CAM_ERR(CAM_EEPROM, "failed: eeprom power down rc %d", rc);
+
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
 	return rc;
 power_down:
-	rc = cam_eeprom_power_down(e_ctrl);
+	cam_eeprom_power_down(e_ctrl);
 data_mem_free:
 	kfree(e_ctrl->cal_data.mapdata);
 	kfree(e_ctrl->cal_data.map);
+	e_ctrl->cal_data.num_data = 0;
+	e_ctrl->cal_data.num_map = 0;
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
 	return rc;
 }
 
@@ -483,7 +493,7 @@
 	uint16_t                        cmd_length_in_bytes = 0;
 	struct cam_cmd_i2c_info        *i2c_info = NULL;
 	int                             num_map = -1;
-	struct cam_eeprom_memory_map_t *map;
+	struct cam_eeprom_memory_map_t *map = NULL;
 	struct cam_eeprom_soc_private  *soc_private =
 		(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
 	struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
@@ -647,8 +657,15 @@
 	struct cam_packet              *csl_packet = NULL;
 	struct cam_eeprom_soc_private  *soc_private =
 		(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
+	struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
 
 	ioctl_ctrl = (struct cam_control *)arg;
+
+	if (ioctl_ctrl->handle_type != CAM_HANDLE_USER_POINTER) {
+		CAM_ERR(CAM_EEPROM, "Invalid Handle Type");
+		return -EINVAL;
+	}
+
 	if (copy_from_user(&dev_config, (void __user *) ioctl_ctrl->handle,
 		sizeof(dev_config)))
 		return -EFAULT;
@@ -659,68 +676,123 @@
 			"error in converting command Handle Error: %d", rc);
 		return rc;
 	}
+
+	if (dev_config.offset > pkt_len) {
+		CAM_ERR(CAM_EEPROM,
+			"Offset is out of bound: off: %lld, %zu",
+			dev_config.offset, pkt_len);
+		return -EINVAL;
+	}
+
 	csl_packet = (struct cam_packet *)
 		(generic_pkt_addr + dev_config.offset);
 	switch (csl_packet->header.op_code & 0xFFFFFF) {
 	case CAM_EEPROM_PACKET_OPCODE_INIT:
 		if (e_ctrl->userspace_probe == false) {
-			rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet);
-			CAM_ERR(CAM_EEPROM,
-				"Eeprom already probed at kernel boot");
-			rc = -EINVAL;
-		break;
-		}
-		if (e_ctrl->cal_data.num_data == 0) {
-			rc = cam_eeprom_init_pkt_parser(e_ctrl, csl_packet);
-			if (rc) {
-				CAM_ERR(CAM_EEPROM,
-					"Failed in parsing the pkt");
+			rc = cam_eeprom_parse_read_memory_map(
+					e_ctrl->soc_info.dev->of_node, e_ctrl);
+			if (rc < 0) {
+				CAM_ERR(CAM_EEPROM, "Failed: rc : %d", rc);
 				return rc;
 			}
-
-			e_ctrl->cal_data.mapdata =
-				kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL);
-			if (!e_ctrl->cal_data.mapdata) {
-				rc = -ENOMEM;
-				CAM_ERR(CAM_EEPROM, "failed");
-				goto error;
-			}
-
-			rc = cam_eeprom_power_up(e_ctrl,
-				&soc_private->power_info);
-			if (rc) {
-				CAM_ERR(CAM_EEPROM, "failed rc %d", rc);
-				goto memdata_free;
-			}
-
-			rc = cam_eeprom_read_memory(e_ctrl, &e_ctrl->cal_data);
-			if (rc) {
-				CAM_ERR(CAM_EEPROM,
-					"read_eeprom_memory failed");
-				goto power_down;
-			}
-
 			rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet);
-			rc = cam_eeprom_power_down(e_ctrl);
-		} else {
-			CAM_DBG(CAM_EEPROM, "Already read eeprom");
+			kfree(e_ctrl->cal_data.mapdata);
+			kfree(e_ctrl->cal_data.map);
+			e_ctrl->cal_data.num_data = 0;
+			e_ctrl->cal_data.num_map = 0;
+			CAM_DBG(CAM_EEPROM,
+				"Returning the data using kernel probe");
+			break;
 		}
+		rc = cam_eeprom_init_pkt_parser(e_ctrl, csl_packet);
+		if (rc) {
+			CAM_ERR(CAM_EEPROM,
+				"Failed in parsing the pkt");
+			return rc;
+		}
+
+		e_ctrl->cal_data.mapdata =
+			kzalloc(e_ctrl->cal_data.num_data, GFP_KERNEL);
+		if (!e_ctrl->cal_data.mapdata) {
+			rc = -ENOMEM;
+			CAM_ERR(CAM_EEPROM, "failed");
+			goto error;
+		}
+
+		rc = cam_eeprom_power_up(e_ctrl,
+			&soc_private->power_info);
+		if (rc) {
+			CAM_ERR(CAM_EEPROM, "failed rc %d", rc);
+			goto memdata_free;
+		}
+
+		e_ctrl->cam_eeprom_state = CAM_EEPROM_CONFIG;
+		rc = cam_eeprom_read_memory(e_ctrl, &e_ctrl->cal_data);
+		if (rc) {
+			CAM_ERR(CAM_EEPROM,
+				"read_eeprom_memory failed");
+			goto power_down;
+		}
+
+		rc = cam_eeprom_get_cal_data(e_ctrl, csl_packet);
+		rc = cam_eeprom_power_down(e_ctrl);
+		e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
+		kfree(e_ctrl->cal_data.mapdata);
+		kfree(e_ctrl->cal_data.map);
+		e_ctrl->cal_data.num_data = 0;
+		e_ctrl->cal_data.num_map = 0;
 		break;
 	default:
 		break;
 	}
-	kfree(e_ctrl->cal_data.mapdata);
-	kfree(e_ctrl->cal_data.map);
 	return rc;
 power_down:
-	rc = cam_eeprom_power_down(e_ctrl);
+	cam_eeprom_power_down(e_ctrl);
 memdata_free:
 	kfree(e_ctrl->cal_data.mapdata);
 error:
+	kfree(power_info->power_setting);
+	kfree(power_info->power_down_setting);
 	kfree(e_ctrl->cal_data.map);
+	e_ctrl->cal_data.num_data = 0;
+	e_ctrl->cal_data.num_map = 0;
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
 	return rc;
 }
 
+void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl)
+{
+	int rc;
+	struct cam_eeprom_soc_private  *soc_private =
+		(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
+	struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
+
+	if (e_ctrl->cam_eeprom_state == CAM_EEPROM_INIT)
+		return;
+
+	if (e_ctrl->cam_eeprom_state == CAM_EEPROM_CONFIG) {
+		rc = cam_eeprom_power_down(e_ctrl);
+		if (rc < 0)
+			CAM_ERR(CAM_EEPROM, "EEPROM Power down failed");
+		e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
+	}
+
+	if (e_ctrl->cam_eeprom_state == CAM_EEPROM_ACQUIRE) {
+		rc = cam_destroy_device_hdl(e_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_EEPROM, "destroying the device hdl");
+
+		e_ctrl->bridge_intf.device_hdl = -1;
+		e_ctrl->bridge_intf.link_hdl = -1;
+		e_ctrl->bridge_intf.session_hdl = -1;
+
+		kfree(power_info->power_setting);
+		kfree(power_info->power_down_setting);
+	}
+
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
+}
+
 /**
  * cam_eeprom_driver_cmd - Handle eeprom cmds
  * @e_ctrl:     ctrl structure
@@ -742,7 +814,7 @@
 	mutex_lock(&(e_ctrl->eeprom_mutex));
 	switch (cmd->op_code) {
 	case CAM_QUERY_CAP:
-		eeprom_cap.slot_info = e_ctrl->subdev_id;
+		eeprom_cap.slot_info = e_ctrl->soc_info.index;
 		if (e_ctrl->userspace_probe == false)
 			eeprom_cap.eeprom_kernel_probe = true;
 		else
@@ -763,6 +835,33 @@
 			CAM_ERR(CAM_EEPROM, "Failed to acquire dev");
 			goto release_mutex;
 		}
+		e_ctrl->cam_eeprom_state = CAM_EEPROM_ACQUIRE;
+		break;
+	case CAM_RELEASE_DEV:
+		if (e_ctrl->cam_eeprom_state != CAM_EEPROM_ACQUIRE) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_EEPROM,
+			"Not in right state to release : %d",
+			e_ctrl->cam_eeprom_state);
+			goto release_mutex;
+		}
+
+		if (e_ctrl->bridge_intf.device_hdl == -1) {
+			CAM_ERR(CAM_EEPROM,
+				"Invalid Handles: link hdl: %d device hdl: %d",
+				e_ctrl->bridge_intf.device_hdl,
+				e_ctrl->bridge_intf.link_hdl);
+			rc = -EINVAL;
+			goto release_mutex;
+		}
+		rc = cam_destroy_device_hdl(e_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_EEPROM,
+				"failed in destroying the device hdl");
+		e_ctrl->bridge_intf.device_hdl = -1;
+		e_ctrl->bridge_intf.link_hdl = -1;
+		e_ctrl->bridge_intf.session_hdl = -1;
+		e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
 		break;
 	case CAM_CONFIG_DEV:
 		rc = cam_eeprom_pkt_parse(e_ctrl, arg);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
index 84736df..c9fccbb 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_core.h
@@ -17,5 +17,12 @@
 int32_t cam_eeprom_driver_cmd(struct cam_eeprom_ctrl_t *e_ctrl, void *arg);
 int32_t cam_eeprom_parse_read_memory_map(struct device_node *of_node,
 	struct cam_eeprom_ctrl_t *e_ctrl);
+/**
+ * @e_ctrl: EEPROM ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl);
+
 #endif
 /* _CAM_EEPROM_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
index 9bbc6df..5eb29c3 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.c
@@ -34,6 +34,24 @@
 	return rc;
 }
 
+static int cam_eeprom_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_eeprom_ctrl_t *e_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!e_ctrl) {
+		CAM_ERR(CAM_EEPROM, "e_ctrl ptr is NULL");
+			return -EINVAL;
+	}
+
+	mutex_lock(&(e_ctrl->eeprom_mutex));
+	cam_eeprom_shutdown(e_ctrl);
+	mutex_unlock(&(e_ctrl->eeprom_mutex));
+
+	return 0;
+}
+
 int32_t cam_eeprom_update_i2c_info(struct cam_eeprom_ctrl_t *e_ctrl,
 	struct cam_eeprom_i2c_info_t *i2c_info)
 {
@@ -98,7 +116,9 @@
 }
 #endif
 
-static const struct v4l2_subdev_internal_ops cam_eeprom_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_eeprom_internal_ops = {
+	.close = cam_eeprom_subdev_close,
+};
 
 static struct v4l2_subdev_core_ops cam_eeprom_subdev_core_ops = {
 	.ioctl = cam_eeprom_subdev_ioctl,
@@ -152,6 +172,12 @@
 		goto probe_failure;
 	}
 
+	soc_private = kzalloc(sizeof(*soc_private), GFP_KERNEL);
+	if (!soc_private)
+		goto ectrl_free;
+
+	e_ctrl->soc_info.soc_private = soc_private;
+
 	i2c_set_clientdata(client, e_ctrl);
 
 	mutex_init(&(e_ctrl->eeprom_mutex));
@@ -175,22 +201,6 @@
 		goto free_soc;
 	}
 
-	if (e_ctrl->userspace_probe == false) {
-		rc = cam_eeprom_parse_read_memory_map(soc_info->dev->of_node,
-			e_ctrl);
-		if (rc) {
-			CAM_ERR(CAM_EEPROM, "failed: read mem map rc %d", rc);
-			goto free_soc;
-		}
-	}
-
-	soc_private = (struct cam_eeprom_soc_private *)(id->driver_data);
-	if (!soc_private) {
-		CAM_ERR(CAM_EEPROM, "board info NULL");
-		rc = -EINVAL;
-		goto ectrl_free;
-	}
-
 	rc = cam_eeprom_init_subdev(e_ctrl);
 	if (rc)
 		goto free_soc;
@@ -208,6 +218,7 @@
 	e_ctrl->bridge_intf.ops.link_setup = NULL;
 	e_ctrl->bridge_intf.ops.apply_req = NULL;
 	v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl);
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
 
 	return rc;
 free_soc:
@@ -242,12 +253,9 @@
 		return -EINVAL;
 	}
 
-	kfree(e_ctrl->cal_data.mapdata);
-	kfree(e_ctrl->cal_data.map);
-	if (soc_private) {
-		kfree(soc_private->power_info.gpio_num_info);
+	if (soc_private)
 		kfree(soc_private);
-	}
+
 	kfree(e_ctrl);
 
 	return 0;
@@ -307,13 +315,10 @@
 		goto board_free;
 	}
 
-	if (e_ctrl->userspace_probe == false) {
-		rc = cam_eeprom_parse_read_memory_map(soc_info->dev->of_node,
-			e_ctrl);
-		if (rc) {
-			CAM_ERR(CAM_EEPROM, "failed: read mem map rc %d", rc);
-			goto board_free;
-		}
+	rc = cam_eeprom_spi_parse_of(spi_client);
+	if (rc) {
+		CAM_ERR(CAM_EEPROM, "Device tree parsing error");
+		goto board_free;
 	}
 
 	rc = cam_eeprom_init_subdev(e_ctrl);
@@ -369,8 +374,6 @@
 	}
 
 	kfree(e_ctrl->io_master_info.spi_client);
-	kfree(e_ctrl->cal_data.mapdata);
-	kfree(e_ctrl->cal_data.map);
 	soc_private =
 		(struct cam_eeprom_soc_private *)e_ctrl->soc_info.soc_private;
 	if (soc_private) {
@@ -414,6 +417,7 @@
 		goto free_cci_client;
 	}
 	e_ctrl->soc_info.soc_private = soc_private;
+	soc_private->power_info.dev = &pdev->dev;
 
 	/* Initialize mutex */
 	mutex_init(&(e_ctrl->eeprom_mutex));
@@ -428,15 +432,6 @@
 		goto free_soc;
 	}
 
-	if (e_ctrl->userspace_probe == false) {
-		rc = cam_eeprom_parse_read_memory_map(pdev->dev.of_node,
-			e_ctrl);
-		if (rc) {
-			CAM_ERR(CAM_EEPROM, "failed: read mem map rc %d", rc);
-			goto free_soc;
-		}
-	}
-
 	rc = cam_eeprom_init_subdev(e_ctrl);
 	if (rc)
 		goto free_soc;
@@ -448,6 +443,9 @@
 
 	platform_set_drvdata(pdev, e_ctrl);
 	v4l2_set_subdevdata(&e_ctrl->v4l2_dev_str.sd, e_ctrl);
+
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
+
 	return rc;
 free_soc:
 	kfree(soc_private);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
index a98bf00..4a2190d 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_dev.h
@@ -37,6 +37,12 @@
 #define MSM_EEPROM_MAX_MEM_MAP_CNT             8
 #define MSM_EEPROM_MEM_MAP_PROPERTIES_CNT      8
 
+enum cam_eeprom_state {
+	CAM_EEPROM_INIT,
+	CAM_EEPROM_ACQUIRE,
+	CAM_EEPROM_CONFIG,
+};
+
 /**
  * struct cam_eeprom_map_t - eeprom map
  * @data_type       :   Data type
@@ -154,7 +160,7 @@
  * @cci_i2c_master  :   I2C structure
  * @v4l2_dev_str    :   V4L2 device structure
  * @bridge_intf     :   bridge interface params
- * @subdev_id       :   subdev id
+ * @cam_eeprom_state:   eeprom_device_state
  * @userspace_probe :   flag indicates userspace or kernel probe
  * @cal_data        :   Calibration data
  * @device_name     :   Device name
@@ -171,7 +177,7 @@
 	struct cam_subdev v4l2_dev_str;
 	struct cam_eeprom_intf_params bridge_intf;
 	enum msm_camera_device_type_t eeprom_device_type;
-	uint32_t subdev_id;
+	enum cam_eeprom_state cam_eeprom_state;
 	bool userspace_probe;
 	struct cam_eeprom_memory_block_t cal_data;
 	char device_name[20];
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c
index 5e12f4a..70c40fd 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.c
@@ -20,6 +20,96 @@
 #include "cam_eeprom_soc.h"
 #include "cam_debug_util.h"
 
+#define cam_eeprom_spi_parse_cmd(spi_dev, name, out)          \
+	{                                                     \
+		spi_dev->cmd_tbl.name.opcode = out[0];        \
+		spi_dev->cmd_tbl.name.addr_len = out[1];      \
+		spi_dev->cmd_tbl.name.dummy_len = out[2];     \
+		spi_dev->cmd_tbl.name.delay_intv = out[3];    \
+		spi_dev->cmd_tbl.name.delay_count = out[4];   \
+	}
+
+int cam_eeprom_spi_parse_of(struct cam_sensor_spi_client *spi_dev)
+{
+	int rc = -EFAULT;
+	uint32_t tmp[5];
+
+	rc  = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-read", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, read, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get read data");
+		return -EFAULT;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-readseq", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, read_seq, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get readseq data");
+		return -EFAULT;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-queryid", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, query_id, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get queryid data");
+		return -EFAULT;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-pprog", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, page_program, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get page program data");
+		return -EFAULT;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-wenable", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, write_enable, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get write enable data");
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-readst", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, read_status, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get readdst data");
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"spiop-erase", tmp, 5);
+	if (!rc) {
+		cam_eeprom_spi_parse_cmd(spi_dev, erase, tmp);
+	} else {
+		CAM_ERR(CAM_EEPROM, "Failed to get erase data");
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(spi_dev->spi_master->dev.of_node,
+		"eeprom-id", tmp, 2);
+	if (rc) {
+		CAM_ERR(CAM_EEPROM, "Failed to get eeprom id");
+		return rc;
+	}
+
+	spi_dev->mfr_id0 = tmp[0];
+	spi_dev->device_id0 = tmp[1];
+
+	return 0;
+}
+
 /*
  * cam_eeprom_parse_memory_map() - parse memory map in device node
  * @of:         device node
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h
index 08c436c..d311549 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_eeprom/cam_eeprom_soc.h
@@ -14,6 +14,8 @@
 
 #include "cam_eeprom_dev.h"
 
+int cam_eeprom_spi_parse_of(struct cam_sensor_spi_client *client);
+
 int cam_eeprom_parse_dt_memory_map(struct device_node *of,
 	struct cam_eeprom_memory_block_t *data);
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/Makefile
index 9aab0e4..c7889a5 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/Makefile
@@ -1,5 +1,6 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sync
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
index 0aaf8b0c..55da264 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
@@ -14,6 +14,7 @@
 
 #include "cam_sensor_cmn_header.h"
 #include "cam_flash_core.h"
+#include "cam_res_mgr_api.h"
 
 int cam_flash_prepare(struct cam_flash_ctrl *flash_ctrl,
 	enum cam_flash_state state)
@@ -25,7 +26,7 @@
 		return -EINVAL;
 	}
 
-	if ((state == CAM_FLASH_STATE_INIT) &&
+	if ((state == CAM_FLASH_STATE_START) &&
 		(flash_ctrl->is_regulator_enabled == false)) {
 		rc = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
 			ENABLE_REGULATOR, NULL);
@@ -35,7 +36,8 @@
 			return rc;
 		}
 		flash_ctrl->is_regulator_enabled = true;
-	} else if ((state == CAM_FLASH_STATE_RELEASE) &&
+		flash_ctrl->flash_state = CAM_FLASH_STATE_START;
+	} else if ((state == CAM_FLASH_STATE_STOP) &&
 		(flash_ctrl->is_regulator_enabled == true)) {
 		rc = qpnp_flash_led_prepare(flash_ctrl->switch_trigger,
 			DISABLE_REGULATOR, NULL);
@@ -45,6 +47,7 @@
 			return rc;
 		}
 		flash_ctrl->is_regulator_enabled = false;
+		flash_ctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
 	} else {
 		CAM_ERR(CAM_FLASH, "Wrong Flash State : %d",
 			flash_ctrl->flash_state);
@@ -54,6 +57,74 @@
 	return rc;
 }
 
+static int cam_flash_flush_nrt(struct cam_flash_ctrl *fctrl)
+{
+	int j = 0;
+	struct cam_flash_frame_setting *nrt_settings;
+
+	if (!fctrl)
+		return -EINVAL;
+
+	nrt_settings = &fctrl->nrt_info;
+
+	if (nrt_settings->cmn_attr.cmd_type ==
+		CAMERA_SENSOR_FLASH_CMD_TYPE_INIT) {
+		fctrl->flash_init_setting.cmn_attr.is_settings_valid = false;
+	} else if ((nrt_settings->cmn_attr.cmd_type ==
+		CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET) ||
+		(nrt_settings->cmn_attr.cmd_type ==
+		CAMERA_SENSOR_FLASH_CMD_TYPE_RER)) {
+		fctrl->nrt_info.cmn_attr.is_settings_valid = false;
+		fctrl->nrt_info.cmn_attr.count = 0;
+		fctrl->nrt_info.num_iterations = 0;
+		fctrl->nrt_info.led_on_delay_ms = 0;
+		fctrl->nrt_info.led_off_delay_ms = 0;
+		for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++)
+			fctrl->nrt_info.led_current_ma[j] = 0;
+	}
+
+	return 0;
+}
+
+int cam_flash_flush_request(struct cam_req_mgr_flush_request *flush)
+{
+	int rc = 0;
+	int i = 0, j = 0;
+	struct cam_flash_ctrl *fctrl = NULL;
+	int frame_offset = 0;
+
+	fctrl = (struct cam_flash_ctrl *) cam_get_device_priv(flush->dev_hdl);
+	if (!fctrl) {
+		CAM_ERR(CAM_FLASH, "Device data is NULL");
+		return -EINVAL;
+	}
+
+	if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_ALL) {
+	/* flush all requests*/
+		for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+			fctrl->per_frame[i].cmn_attr.request_id = 0;
+			fctrl->per_frame[i].cmn_attr.is_settings_valid = false;
+			fctrl->per_frame[i].cmn_attr.count = 0;
+			for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++)
+				fctrl->per_frame[i].led_current_ma[j] = 0;
+		}
+
+		rc = cam_flash_flush_nrt(fctrl);
+		if (rc)
+			CAM_ERR(CAM_FLASH, "NonRealTime flush error");
+	} else if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
+	/* flush request with req_id*/
+		frame_offset = flush->req_id % MAX_PER_FRAME_ARRAY;
+		fctrl->per_frame[frame_offset].cmn_attr.request_id = 0;
+		fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid =
+			false;
+		fctrl->per_frame[frame_offset].cmn_attr.count = 0;
+		for (i = 0; i < CAM_FLASH_MAX_LED_TRIGGERS; i++)
+			fctrl->per_frame[frame_offset].led_current_ma[i] = 0;
+	}
+	return rc;
+}
+
 static int cam_flash_ops(struct cam_flash_ctrl *flash_ctrl,
 	struct cam_flash_frame_setting *flash_data, enum camera_flash_opcode op)
 {
@@ -82,7 +153,8 @@
 
 				CAM_DBG(CAM_FLASH,
 					"Led_Current[%d] = %d", i, curr);
-				led_trigger_event(flash_ctrl->torch_trigger[i],
+				cam_res_mgr_led_trigger_event(
+					flash_ctrl->torch_trigger[i],
 					curr);
 			}
 		}
@@ -99,7 +171,8 @@
 
 				CAM_DBG(CAM_FLASH, "LED flash_current[%d]: %d",
 					i, curr);
-				led_trigger_event(flash_ctrl->flash_trigger[i],
+				cam_res_mgr_led_trigger_event(
+					flash_ctrl->flash_trigger[i],
 					curr);
 			}
 		}
@@ -109,7 +182,9 @@
 	}
 
 	if (flash_ctrl->switch_trigger)
-		led_trigger_event(flash_ctrl->switch_trigger, LED_SWITCH_ON);
+		cam_res_mgr_led_trigger_event(
+			flash_ctrl->switch_trigger,
+			LED_SWITCH_ON);
 
 	return 0;
 }
@@ -125,18 +200,21 @@
 
 	for (i = 0; i < flash_ctrl->flash_num_sources; i++)
 		if (flash_ctrl->flash_trigger[i])
-			led_trigger_event(flash_ctrl->flash_trigger[i],
+			cam_res_mgr_led_trigger_event(
+				flash_ctrl->flash_trigger[i],
 				LED_OFF);
 
 	for (i = 0; i < flash_ctrl->torch_num_sources; i++)
 		if (flash_ctrl->torch_trigger[i])
-			led_trigger_event(flash_ctrl->torch_trigger[i],
+			cam_res_mgr_led_trigger_event(
+				flash_ctrl->torch_trigger[i],
 				LED_OFF);
 
 	if (flash_ctrl->switch_trigger)
-		led_trigger_event(flash_ctrl->switch_trigger,
+		cam_res_mgr_led_trigger_event(flash_ctrl->switch_trigger,
 			LED_SWITCH_OFF);
 
+	flash_ctrl->flash_state = CAM_FLASH_STATE_START;
 	return 0;
 }
 
@@ -153,7 +231,8 @@
 
 	for (i = 0; i < flash_ctrl->flash_num_sources; i++)
 		if (flash_ctrl->flash_trigger[i])
-			led_trigger_event(flash_ctrl->flash_trigger[i],
+			cam_res_mgr_led_trigger_event(
+				flash_ctrl->flash_trigger[i],
 				LED_OFF);
 
 	rc = cam_flash_ops(flash_ctrl, flash_data,
@@ -177,7 +256,8 @@
 
 	for (i = 0; i < flash_ctrl->torch_num_sources; i++)
 		if (flash_ctrl->torch_trigger[i])
-			led_trigger_event(flash_ctrl->torch_trigger[i],
+			cam_res_mgr_led_trigger_event(
+				flash_ctrl->torch_trigger[i],
 				LED_OFF);
 
 	rc = cam_flash_ops(flash_ctrl, flash_data,
@@ -236,72 +316,44 @@
 			flash_data = &fctrl->nrt_info;
 			if (flash_data->opcode ==
 				CAMERA_SENSOR_FLASH_OP_FIRELOW) {
-				if (!(fctrl->is_regulator_enabled)) {
-					rc = cam_flash_prepare(fctrl,
-						CAM_FLASH_STATE_INIT);
-					if (rc) {
-						CAM_ERR(CAM_FLASH,
-							"Reg Enable Failed %d",
-							rc);
-						goto nrt_del_req;
-					}
-					fctrl->flash_state =
-						CAM_FLASH_STATE_INIT;
-					rc = cam_flash_low(fctrl, flash_data);
-					if (rc) {
-						CAM_ERR(CAM_FLASH,
-							"Torch ON failed : %d",
-							rc);
-						goto nrt_del_req;
-					}
-					fctrl->flash_state =
-						CAM_FLASH_STATE_LOW;
-				}
-			} else if (flash_data->opcode ==
-				CAMERA_SENSOR_FLASH_OP_OFF) {
-				if (fctrl->flash_state !=
-					CAM_FLASH_STATE_INIT) {
-					rc = cam_flash_off(fctrl);
-					if (rc)
-						CAM_ERR(CAM_FLASH,
-							"LED off failed: %d",
-							rc);
-				}
-
-				rc = cam_flash_prepare(fctrl,
-					CAM_FLASH_STATE_RELEASE);
+				rc = cam_flash_low(fctrl, flash_data);
 				if (rc) {
 					CAM_ERR(CAM_FLASH,
-						"Regulator Disable failed %d",
+						"Torch ON failed : %d",
 						rc);
 					goto nrt_del_req;
 				}
-
 				fctrl->flash_state =
-					CAM_FLASH_STATE_RELEASE;
-				fctrl->is_regulator_enabled = false;
+						CAM_FLASH_STATE_LOW;
+			} else if (flash_data->opcode ==
+				CAMERA_SENSOR_FLASH_OP_OFF) {
+				if (fctrl->flash_state ==
+					CAM_FLASH_STATE_LOW) {
+					rc = cam_flash_off(fctrl);
+					if (rc)
+						CAM_ERR(CAM_FLASH,
+						"LED off failed: %d",
+						rc);
+				}
 			}
 		} else if (fctrl->nrt_info.cmn_attr.cmd_type ==
 			CAMERA_SENSOR_FLASH_CMD_TYPE_RER) {
 			flash_data = &fctrl->nrt_info;
 
-			if (fctrl->flash_state != CAM_FLASH_STATE_INIT) {
+			if (fctrl->flash_state != CAM_FLASH_STATE_START) {
 				rc = cam_flash_off(fctrl);
 				if (rc) {
 					CAM_ERR(CAM_FLASH,
 						"Flash off failed: %d",
 						rc);
-				} else {
-					fctrl->flash_state =
-						CAM_FLASH_STATE_INIT;
+					goto nrt_del_req;
 				}
 			}
-
 			num_iterations = flash_data->num_iterations;
 			for (i = 0; i < num_iterations; i++) {
 				/* Turn On Torch */
 				if (fctrl->flash_state ==
-					CAM_FLASH_STATE_INIT) {
+					CAM_FLASH_STATE_START) {
 					rc = cam_flash_low(fctrl, flash_data);
 					if (rc) {
 						CAM_ERR(CAM_FLASH,
@@ -310,11 +362,12 @@
 					}
 					fctrl->flash_state =
 						CAM_FLASH_STATE_LOW;
-				}
-				usleep_range(
-				flash_data->led_on_delay_ms * 1000,
-				flash_data->led_on_delay_ms * 1000 + 100);
 
+					usleep_range(
+					flash_data->led_on_delay_ms * 1000,
+					flash_data->led_on_delay_ms * 1000 +
+						100);
+				}
 				/* Turn Off Torch */
 				rc = cam_flash_off(fctrl);
 				if (rc) {
@@ -323,7 +376,7 @@
 						rc);
 					continue;
 				}
-				fctrl->flash_state = CAM_FLASH_STATE_INIT;
+				fctrl->flash_state = CAM_FLASH_STATE_START;
 				usleep_range(
 				flash_data->led_off_delay_ms * 1000,
 				flash_data->led_off_delay_ms * 1000 + 100);
@@ -334,9 +387,10 @@
 		flash_data = &fctrl->per_frame[frame_offset];
 
 		if ((flash_data->opcode == CAMERA_SENSOR_FLASH_OP_FIREHIGH) &&
-			(flash_data->cmn_attr.is_settings_valid)) {
+			(flash_data->cmn_attr.is_settings_valid) &&
+			(flash_data->cmn_attr.request_id == req_id)) {
 			/* Turn On Flash */
-			if (fctrl->flash_state == CAM_FLASH_STATE_INIT) {
+			if (fctrl->flash_state == CAM_FLASH_STATE_START) {
 				rc = cam_flash_high(fctrl, flash_data);
 				if (rc) {
 					CAM_ERR(CAM_FLASH,
@@ -348,9 +402,10 @@
 			}
 		} else if ((flash_data->opcode ==
 			CAMERA_SENSOR_FLASH_OP_FIRELOW) &&
-			(flash_data->cmn_attr.is_settings_valid)) {
+			(flash_data->cmn_attr.is_settings_valid) &&
+			(flash_data->cmn_attr.request_id == req_id)) {
 			/* Turn On Torch */
-			if (fctrl->flash_state == CAM_FLASH_STATE_INIT) {
+			if (fctrl->flash_state == CAM_FLASH_STATE_START) {
 				rc = cam_flash_low(fctrl, flash_data);
 				if (rc) {
 					CAM_ERR(CAM_FLASH,
@@ -361,23 +416,19 @@
 				fctrl->flash_state = CAM_FLASH_STATE_LOW;
 			}
 		} else if ((flash_data->opcode == CAMERA_SENSOR_FLASH_OP_OFF) &&
-			(flash_data->cmn_attr.is_settings_valid)) {
-			if ((fctrl->flash_state != CAM_FLASH_STATE_RELEASE) ||
-				(fctrl->flash_state != CAM_FLASH_STATE_INIT)) {
+			(flash_data->cmn_attr.is_settings_valid) &&
+			(flash_data->cmn_attr.request_id == req_id)) {
+			if ((fctrl->flash_state == CAM_FLASH_STATE_LOW) ||
+				(fctrl->flash_state == CAM_FLASH_STATE_HIGH)) {
 				rc = cam_flash_off(fctrl);
 				if (rc) {
 					CAM_ERR(CAM_FLASH,
 						"Flash off failed %d", rc);
-				} else {
-					fctrl->flash_state =
-						CAM_FLASH_STATE_INIT;
+					goto apply_setting_err;
 				}
 			}
 		} else {
-			CAM_ERR(CAM_FLASH, "Wrong opcode : %d",
-				flash_data->opcode);
-			rc = -EINVAL;
-			goto apply_setting_err;
+			CAM_DBG(CAM_FLASH, "NOP opcode: req_id: %u", req_id);
 		}
 	}
 
@@ -470,6 +521,19 @@
 			csl_packet->cmd_buf_offset);
 		frame_offset = csl_packet->header.request_id %
 			MAX_PER_FRAME_ARRAY;
+		if (fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid
+			== true) {
+			fctrl->per_frame[frame_offset].cmn_attr.request_id = 0;
+			fctrl->per_frame[frame_offset].
+				cmn_attr.is_settings_valid = false;
+			for (i = 0;
+			i < fctrl->per_frame[frame_offset].cmn_attr.count;
+			i++) {
+				fctrl->per_frame[frame_offset].
+					led_current_ma[i] = 0;
+			}
+		}
+
 		fctrl->per_frame[frame_offset].cmn_attr.request_id =
 			csl_packet->header.request_id;
 		fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid =
@@ -479,6 +543,10 @@
 			(uint64_t *)&generic_ptr, &len_of_buffer);
 		cmd_buf = (uint32_t *)((uint8_t *)generic_ptr +
 			cmd_desc->offset);
+
+		if (!cmd_buf)
+			return -EINVAL;
+
 		cmn_hdr = (struct common_header *)cmd_buf;
 
 		switch (cmn_hdr->cmd_type) {
@@ -487,6 +555,11 @@
 				"CAMERA_FLASH_CMD_TYPE_OPS case called");
 			flash_operation_info =
 				(struct cam_flash_set_on_off *) cmd_buf;
+			if (!flash_operation_info) {
+				CAM_ERR(CAM_FLASH, "flash_operation_info Null");
+				return -EINVAL;
+			}
+
 			fctrl->per_frame[frame_offset].opcode =
 				flash_operation_info->opcode;
 			fctrl->per_frame[frame_offset].cmn_attr.count =
@@ -502,7 +575,6 @@
 				cmn_hdr->cmd_type);
 			return -EINVAL;
 		}
-
 		break;
 	}
 	case CAM_FLASH_PACKET_OPCODE_NON_REALTIME_SET_OPS: {
@@ -596,6 +668,8 @@
 		break;
 	}
 	case CAM_PKT_NOP_OPCODE: {
+		CAM_DBG(CAM_FLASH, "NOP Packet is Received: req_id: %u",
+			csl_packet->header.request_id);
 		goto update_req_mgr;
 	}
 	default:
@@ -611,6 +685,13 @@
 		add_req.link_hdl = fctrl->bridge_intf.link_hdl;
 		add_req.req_id = csl_packet->header.request_id;
 		add_req.dev_hdl = fctrl->bridge_intf.device_hdl;
+
+		if ((csl_packet->header.op_code & 0xFFFFF) ==
+			CAM_FLASH_PACKET_OPCODE_SET_OPS)
+			add_req.skip_before_applying = 1;
+		else
+			add_req.skip_before_applying = 0;
+
 		if (fctrl->bridge_intf.crm_cb &&
 			fctrl->bridge_intf.crm_cb->add_req)
 			fctrl->bridge_intf.crm_cb->add_req(&add_req);
@@ -625,7 +706,7 @@
 	info->dev_id = CAM_REQ_MGR_DEVICE_FLASH;
 	strlcpy(info->name, CAM_FLASH_NAME, sizeof(info->name));
 	info->p_delay = CAM_FLASH_PIPELINE_DELAY;
-	info->trigger = CAM_TRIGGER_POINT_EOF;
+	info->trigger = CAM_TRIGGER_POINT_SOF;
 	return 0;
 }
 
@@ -653,74 +734,82 @@
 	return 0;
 }
 
-static int cam_flash_flush_nrt(struct cam_flash_ctrl *fctrl)
+
+int cam_flash_stop_dev(struct cam_flash_ctrl *fctrl)
 {
-	int j = 0;
-	struct cam_flash_frame_setting *nrt_settings;
+	int rc = 0, i, j;
 
-	if (!fctrl)
-		return -EINVAL;
+	if ((fctrl->flash_state == CAM_FLASH_STATE_LOW) ||
+		(fctrl->flash_state == CAM_FLASH_STATE_HIGH))
+		cam_flash_off(fctrl);
 
-	nrt_settings = &fctrl->nrt_info;
-
-	if (nrt_settings->cmn_attr.cmd_type ==
-		CAMERA_SENSOR_FLASH_CMD_TYPE_INIT) {
-		fctrl->flash_init_setting.cmn_attr.is_settings_valid = false;
-	} else if ((nrt_settings->cmn_attr.cmd_type ==
-		CAMERA_SENSOR_FLASH_CMD_TYPE_WIDGET) ||
-		(nrt_settings->cmn_attr.cmd_type ==
-		CAMERA_SENSOR_FLASH_CMD_TYPE_RER)) {
-		fctrl->nrt_info.cmn_attr.is_settings_valid = false;
-		fctrl->nrt_info.cmn_attr.count = 0;
-		fctrl->nrt_info.num_iterations = 0;
-		fctrl->nrt_info.led_on_delay_ms = 0;
-		fctrl->nrt_info.led_off_delay_ms = 0;
+	for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+		fctrl->per_frame[i].cmn_attr.request_id = 0;
+		fctrl->per_frame[i].cmn_attr.is_settings_valid = false;
+		fctrl->per_frame[i].cmn_attr.count = 0;
 		for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++)
-			fctrl->nrt_info.led_current_ma[j] = 0;
+			fctrl->per_frame[i].led_current_ma[j] = 0;
 	}
 
-	return 0;
+	rc = cam_flash_flush_nrt(fctrl);
+	if (rc) {
+		CAM_ERR(CAM_FLASH,
+			"NonRealTime Dev flush failed rc: %d", rc);
+		return rc;
+	}
+
+	if ((fctrl->flash_state == CAM_FLASH_STATE_START) &&
+		(fctrl->is_regulator_enabled == true)) {
+		rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_STOP);
+		if (rc)
+			CAM_ERR(CAM_FLASH, "Disable Regulator Failed rc: %d",
+				rc);
+	}
+
+	return rc;
 }
 
-int cam_flash_flush_request(struct cam_req_mgr_flush_request *flush)
+int cam_flash_release_dev(struct cam_flash_ctrl *fctrl)
 {
 	int rc = 0;
-	int i = 0, j = 0;
-	struct cam_flash_ctrl *fctrl = NULL;
-	int frame_offset = 0;
 
-	fctrl = (struct cam_flash_ctrl *) cam_get_device_priv(flush->dev_hdl);
-	if (!fctrl) {
-		CAM_ERR(CAM_FLASH, "Device data is NULL");
-		return -EINVAL;
-	}
-
-	if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_ALL) {
-	/* flush all requests*/
-		for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
-			fctrl->per_frame[i].cmn_attr.request_id = 0;
-			fctrl->per_frame[i].cmn_attr.is_settings_valid = false;
-			fctrl->per_frame[i].cmn_attr.count = 0;
-			for (j = 0; j < CAM_FLASH_MAX_LED_TRIGGERS; j++)
-				fctrl->per_frame[i].led_current_ma[j] = 0;
-		}
-
-		rc = cam_flash_flush_nrt(fctrl);
+	if (fctrl->bridge_intf.device_hdl != 1) {
+		rc = cam_destroy_device_hdl(fctrl->bridge_intf.device_hdl);
 		if (rc)
-			CAM_ERR(CAM_FLASH, "NonRealTime flush error");
-	} else if (flush->type == CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ) {
-	/* flush request with req_id*/
-		frame_offset = flush->req_id % MAX_PER_FRAME_ARRAY;
-		fctrl->per_frame[frame_offset].cmn_attr.request_id = 0;
-		fctrl->per_frame[frame_offset].cmn_attr.is_settings_valid =
-			false;
-		fctrl->per_frame[frame_offset].cmn_attr.count = 0;
-		for (i = 0; i < CAM_FLASH_MAX_LED_TRIGGERS; i++)
-			fctrl->per_frame[frame_offset].led_current_ma[i] = 0;
+			CAM_ERR(CAM_FLASH,
+				"Failed in destroying device handle rc = %d",
+				rc);
+		fctrl->bridge_intf.device_hdl = -1;
+		fctrl->bridge_intf.link_hdl = -1;
+		fctrl->bridge_intf.session_hdl = -1;
 	}
+
 	return rc;
 }
 
+void cam_flash_shutdown(struct cam_flash_ctrl *fctrl)
+{
+	int rc;
+
+	if (fctrl->flash_state == CAM_FLASH_STATE_INIT)
+		return;
+
+	if (fctrl->flash_state == CAM_FLASH_STATE_ACQUIRE) {
+		cam_flash_release_dev(fctrl);
+		return;
+	}
+
+	rc = cam_flash_stop_dev(fctrl);
+	if (rc)
+		CAM_ERR(CAM_FLASH, "Stop Failed rc: %d", rc);
+
+	rc = cam_flash_release_dev(fctrl);
+	if (rc)
+		CAM_ERR(CAM_FLASH, "Release failed rc: %d", rc);
+
+	fctrl->flash_state = CAM_FLASH_STATE_INIT;
+}
+
 int cam_flash_apply_request(struct cam_req_mgr_apply_request *apply)
 {
 	int rc = 0;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
index 4b0cf8d..d5ea04c 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.h
@@ -29,5 +29,7 @@
 int cam_flash_off(struct cam_flash_ctrl *fctrl);
 int cam_flash_prepare(struct cam_flash_ctrl *flash_ctrl,
 	enum cam_flash_state state);
-
+void cam_flash_shutdown(struct cam_flash_ctrl *flash_ctrl);
+int cam_flash_stop_dev(struct cam_flash_ctrl *flash_ctrl);
+int cam_flash_release_dev(struct cam_flash_ctrl *fctrl);
 #endif /*_CAM_FLASH_CORE_H_*/
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
index 7040ba2..2b371a3 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.c
@@ -36,6 +36,13 @@
 		struct cam_create_dev_hdl bridge_params;
 
 		CAM_DBG(CAM_FLASH, "CAM_ACQUIRE_DEV");
+
+		if (fctrl->flash_state != CAM_FLASH_STATE_INIT) {
+			CAM_ERR(CAM_FLASH,
+				"Cannot apply Acquire dev: Prev state: %d",
+				fctrl->flash_state);
+		}
+
 		if (fctrl->bridge_intf.device_hdl != -1) {
 			CAM_ERR(CAM_FLASH, "Device is already acquired");
 			rc = -EINVAL;
@@ -70,11 +77,19 @@
 			rc = -EFAULT;
 			goto release_mutex;
 		}
+		fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
 		break;
 	}
 	case CAM_RELEASE_DEV: {
 		CAM_DBG(CAM_FLASH, "CAM_RELEASE_DEV");
-		if (fctrl->bridge_intf.device_hdl == -1) {
+		if (fctrl->flash_state != CAM_FLASH_STATE_ACQUIRE) {
+			CAM_WARN(CAM_FLASH,
+				"Cannot apply Release dev: Prev state:%d",
+				fctrl->flash_state);
+		}
+
+		if (fctrl->bridge_intf.device_hdl == -1 &&
+			fctrl->flash_state == CAM_FLASH_STATE_ACQUIRE) {
 			CAM_ERR(CAM_FLASH,
 				"Invalid Handle: Link Hdl: %d device hdl: %d",
 				fctrl->bridge_intf.device_hdl,
@@ -82,18 +97,16 @@
 			rc = -EINVAL;
 			goto release_mutex;
 		}
-		rc = cam_destroy_device_hdl(fctrl->bridge_intf.device_hdl);
+		rc = cam_flash_release_dev(fctrl);
 		if (rc)
 			CAM_ERR(CAM_FLASH,
 				"Failed in destroying the device Handle rc= %d",
 				rc);
-		fctrl->bridge_intf.device_hdl = -1;
-		fctrl->bridge_intf.link_hdl = -1;
-		fctrl->bridge_intf.session_hdl = -1;
+		fctrl->flash_state = CAM_FLASH_STATE_INIT;
 		break;
 	}
 	case CAM_QUERY_CAP: {
-		struct cam_flash_query_cap_info flash_cap;
+		struct cam_flash_query_cap_info flash_cap = {0};
 
 		CAM_DBG(CAM_FLASH, "CAM_QUERY_CAP");
 		flash_cap.slot_info = fctrl->soc_info.index;
@@ -118,33 +131,42 @@
 	}
 	case CAM_START_DEV: {
 		CAM_DBG(CAM_FLASH, "CAM_START_DEV");
-		rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_INIT);
+		if (fctrl->flash_state != CAM_FLASH_STATE_ACQUIRE) {
+			CAM_WARN(CAM_FLASH,
+				"Cannot apply Start Dev: Prev state: %d",
+				fctrl->flash_state);
+			rc = -EINVAL;
+			goto release_mutex;
+		}
+
+		rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_START);
 		if (rc) {
 			CAM_ERR(CAM_FLASH,
 				"Enable Regulator Failed rc = %d", rc);
 			goto release_mutex;
 		}
-		fctrl->flash_state = CAM_FLASH_STATE_INIT;
 		rc = cam_flash_apply_setting(fctrl, 0);
 		if (rc) {
 			CAM_ERR(CAM_FLASH, "cannot apply settings rc = %d", rc);
 			goto release_mutex;
 		}
+		fctrl->flash_state = CAM_FLASH_STATE_START;
 		break;
 	}
 	case CAM_STOP_DEV: {
-		CAM_DBG(CAM_FLASH, "CAM_STOP_DEV");
-		if (fctrl->flash_state != CAM_FLASH_STATE_INIT)
-			cam_flash_off(fctrl);
+		if (fctrl->flash_state != CAM_FLASH_STATE_START) {
+			CAM_WARN(CAM_FLASH,
+				"Cannot apply Stop dev: Prev state is: %d",
+				fctrl->flash_state);
+		}
 
-		rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_RELEASE);
+		rc = cam_flash_stop_dev(fctrl);
 		if (rc) {
-			CAM_ERR(CAM_FLASH, "Disable Regulator Failed ret = %d",
+			CAM_ERR(CAM_FLASH, "Stop Dev Failed rc = %d",
 				rc);
 			goto release_mutex;
 		}
-		fctrl->flash_state = CAM_FLASH_STATE_RELEASE;
-
+		fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
 		break;
 	}
 	case CAM_CONFIG_DEV: {
@@ -256,6 +278,24 @@
 	return 0;
 }
 
+static int cam_flash_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_flash_ctrl *flash_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!flash_ctrl) {
+		CAM_ERR(CAM_FLASH, "Flash ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&flash_ctrl->flash_mutex);
+	cam_flash_shutdown(flash_ctrl);
+	mutex_unlock(&flash_ctrl->flash_mutex);
+
+	return 0;
+}
+
 static struct v4l2_subdev_core_ops cam_flash_subdev_core_ops = {
 	.ioctl = cam_flash_subdev_ioctl,
 #ifdef CONFIG_COMPAT
@@ -267,7 +307,9 @@
 	.core = &cam_flash_subdev_core_ops,
 };
 
-static const struct v4l2_subdev_internal_ops cam_flash_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_flash_internal_ops = {
+	.close = cam_flash_subdev_close,
+};
 
 static int32_t cam_flash_platform_probe(struct platform_device *pdev)
 {
@@ -322,6 +364,7 @@
 	mutex_init(&(flash_ctrl->flash_mutex));
 	mutex_init(&(flash_ctrl->flash_wq_mutex));
 
+	flash_ctrl->flash_state = CAM_FLASH_STATE_INIT;
 	CAM_DBG(CAM_FLASH, "Probe success");
 	return rc;
 free_resource:
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
index 1897eb6..bacf088 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_dev.h
@@ -49,9 +49,11 @@
 
 enum cam_flash_state {
 	CAM_FLASH_STATE_INIT,
+	CAM_FLASH_STATE_ACQUIRE,
+	CAM_FLASH_STATE_START,
 	CAM_FLASH_STATE_LOW,
 	CAM_FLASH_STATE_HIGH,
-	CAM_FLASH_STATE_RELEASE,
+	CAM_FLASH_STATE_STOP,
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_soc.c
index a9ab169..a195762 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_soc.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_soc.c
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include "cam_flash_soc.h"
+#include "cam_res_mgr_api.h"
 
 static int32_t cam_get_source_node_info(
 	struct device_node *of_node,
@@ -38,7 +39,7 @@
 		} else {
 			CAM_DBG(CAM_FLASH, "switch trigger %s",
 				soc_private->switch_trigger_name);
-			led_trigger_register_simple(
+			cam_res_mgr_led_trigger_register(
 				soc_private->switch_trigger_name,
 				&fctrl->switch_trigger);
 		}
@@ -111,7 +112,7 @@
 			CAM_DBG(CAM_FLASH, "max_current[%d]: %d",
 				i, soc_private->flash_max_current[i]);
 
-			led_trigger_register_simple(
+			cam_res_mgr_led_trigger_register(
 				soc_private->flash_trigger_name[i],
 				&fctrl->flash_trigger[i]);
 		}
@@ -172,7 +173,7 @@
 			CAM_DBG(CAM_FLASH, "max_current[%d]: %d",
 				i, soc_private->torch_max_current[i]);
 
-			led_trigger_register_simple(
+			cam_res_mgr_led_trigger_register(
 				soc_private->torch_trigger_name[i],
 				&fctrl->torch_trigger[i]);
 		}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/Makefile
new file mode 100644
index 0000000..9397c68
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/Makefile
@@ -0,0 +1,10 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_ois_dev.o cam_ois_core.o cam_ois_soc.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
new file mode 100644
index 0000000..d825f5e
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.c
@@ -0,0 +1,685 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <cam_sensor_cmn_header.h>
+#include "cam_ois_core.h"
+#include "cam_ois_soc.h"
+#include "cam_sensor_util.h"
+#include "cam_debug_util.h"
+#include "cam_res_mgr_api.h"
+
+int32_t cam_ois_construct_default_power_setting(
+	struct cam_sensor_power_ctrl_t *power_info)
+{
+	int rc = 0;
+
+	power_info->power_setting_size = 1;
+	power_info->power_setting =
+		(struct cam_sensor_power_setting *)
+		kzalloc(sizeof(struct cam_sensor_power_setting),
+			GFP_KERNEL);
+	if (!power_info->power_setting)
+		return -ENOMEM;
+
+	power_info->power_setting[0].seq_type = SENSOR_VAF;
+	power_info->power_setting[0].seq_val = CAM_VAF;
+	power_info->power_setting[0].config_val = 1;
+
+	power_info->power_down_setting_size = 1;
+	power_info->power_down_setting =
+		(struct cam_sensor_power_setting *)
+		kzalloc(sizeof(struct cam_sensor_power_setting),
+			GFP_KERNEL);
+	if (!power_info->power_down_setting) {
+		rc = -ENOMEM;
+		goto free_power_settings;
+	}
+
+	power_info->power_setting[0].seq_type = SENSOR_VAF;
+	power_info->power_setting[0].seq_val = CAM_VAF;
+	power_info->power_setting[0].config_val = 0;
+
+	return rc;
+
+free_power_settings:
+	kfree(power_info->power_setting);
+	return rc;
+}
+
+
+/**
+ * cam_ois_get_dev_handle - get device handle
+ * @o_ctrl:     ctrl structure
+ * @arg:        Camera control command argument
+ *
+ * Returns success or failure
+ */
+static int cam_ois_get_dev_handle(struct cam_ois_ctrl_t *o_ctrl,
+	void *arg)
+{
+	struct cam_sensor_acquire_dev    ois_acq_dev;
+	struct cam_create_dev_hdl        bridge_params;
+	struct cam_control              *cmd = (struct cam_control *)arg;
+
+	if (o_ctrl->bridge_intf.device_hdl != -1) {
+		CAM_ERR(CAM_OIS, "Device is already acquired");
+		return -EFAULT;
+	}
+	if (copy_from_user(&ois_acq_dev, (void __user *) cmd->handle,
+		sizeof(ois_acq_dev)))
+		return -EFAULT;
+
+	bridge_params.session_hdl = ois_acq_dev.session_handle;
+	bridge_params.ops = &o_ctrl->bridge_intf.ops;
+	bridge_params.v4l2_sub_dev_flag = 0;
+	bridge_params.media_entity_flag = 0;
+	bridge_params.priv = o_ctrl;
+
+	ois_acq_dev.device_handle =
+		cam_create_device_hdl(&bridge_params);
+	o_ctrl->bridge_intf.device_hdl = ois_acq_dev.device_handle;
+	o_ctrl->bridge_intf.session_hdl = ois_acq_dev.session_handle;
+
+	CAM_DBG(CAM_OIS, "Device Handle: %d", ois_acq_dev.device_handle);
+	if (copy_to_user((void __user *) cmd->handle, &ois_acq_dev,
+		sizeof(struct cam_sensor_acquire_dev))) {
+		CAM_ERR(CAM_OIS, "ACQUIRE_DEV: copy to user failed");
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static int cam_ois_power_up(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int                             rc = 0;
+	struct cam_hw_soc_info          *soc_info =
+		&o_ctrl->soc_info;
+	struct cam_ois_soc_private *soc_private;
+	struct cam_sensor_power_ctrl_t  *power_info;
+
+	soc_private =
+		(struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
+	/* Parse and fill vreg params for power up settings */
+	rc = msm_camera_fill_vreg_params(
+		&o_ctrl->soc_info,
+		power_info->power_setting,
+		power_info->power_setting_size);
+	if (rc) {
+		CAM_ERR(CAM_OIS,
+			"failed to fill vreg params for power up rc:%d", rc);
+		return rc;
+	}
+
+	/* Parse and fill vreg params for power down settings*/
+	rc = msm_camera_fill_vreg_params(
+		&o_ctrl->soc_info,
+		power_info->power_down_setting,
+		power_info->power_down_setting_size);
+	if (rc) {
+		CAM_ERR(CAM_OIS,
+			"failed to fill vreg params power down rc:%d", rc);
+		return rc;
+	}
+
+	power_info->dev = soc_info->dev;
+
+	rc = cam_sensor_core_power_up(power_info, soc_info);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "failed in ois power up rc %d", rc);
+		return rc;
+	}
+
+	/* VREG needs some delay to power up */
+	usleep_range(2000, 2050);
+
+	rc = camera_io_init(&o_ctrl->io_master_info);
+	if (rc)
+		CAM_ERR(CAM_OIS, "cci_init failed: rc: %d", rc);
+
+	return rc;
+}
+
+static int cam_ois_power_down(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int32_t                         rc = 0;
+	struct cam_sensor_power_ctrl_t  *power_info;
+	struct cam_hw_soc_info          *soc_info =
+		&o_ctrl->soc_info;
+	struct cam_ois_soc_private *soc_private;
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "failed: o_ctrl %pK", o_ctrl);
+		return -EINVAL;
+	}
+
+	soc_private =
+		(struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+	soc_info = &o_ctrl->soc_info;
+
+	if (!power_info) {
+		CAM_ERR(CAM_OIS, "failed: power_info %pK", power_info);
+		return -EINVAL;
+	}
+
+	rc = msm_camera_power_down(power_info, soc_info);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "power down the core is failed:%d", rc);
+		return rc;
+	}
+
+	camera_io_release(&o_ctrl->io_master_info);
+
+	return rc;
+}
+
+static int cam_ois_apply_settings(struct cam_ois_ctrl_t *o_ctrl,
+	struct i2c_settings_array *i2c_set)
+{
+	struct i2c_settings_list *i2c_list;
+	int32_t rc = 0;
+	uint32_t i, size;
+
+	if (o_ctrl == NULL || i2c_set == NULL) {
+		CAM_ERR(CAM_OIS, "Invalid Args");
+		return -EINVAL;
+	}
+
+	if (i2c_set->is_settings_valid != 1) {
+		CAM_ERR(CAM_OIS, " Invalid settings");
+		return -EINVAL;
+	}
+
+	list_for_each_entry(i2c_list,
+		&(i2c_set->list_head), list) {
+		if (i2c_list->op_code ==  CAM_SENSOR_I2C_WRITE_RANDOM) {
+			rc = camera_io_dev_write(&(o_ctrl->io_master_info),
+				&(i2c_list->i2c_settings));
+			if (rc < 0) {
+				CAM_ERR(CAM_OIS,
+					"Failed in Applying i2c wrt settings");
+				return rc;
+			}
+		} else if (i2c_list->op_code == CAM_SENSOR_I2C_POLL) {
+			size = i2c_list->i2c_settings.size;
+			for (i = 0; i < size; i++) {
+				rc = camera_io_dev_poll(
+					&(o_ctrl->io_master_info),
+					i2c_list->i2c_settings.
+						reg_setting[i].reg_addr,
+					i2c_list->i2c_settings.
+						reg_setting[i].reg_data,
+					i2c_list->i2c_settings.
+						reg_setting[i].data_mask,
+					i2c_list->i2c_settings.addr_type,
+					i2c_list->i2c_settings.data_type,
+					i2c_list->i2c_settings.
+						reg_setting[i].delay);
+				if (rc < 0) {
+					CAM_ERR(CAM_OIS,
+						"i2c poll apply setting Fail");
+					return rc;
+				}
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int cam_ois_slaveInfo_pkt_parser(struct cam_ois_ctrl_t *o_ctrl,
+	uint32_t *cmd_buf)
+{
+	int32_t rc = 0;
+	struct cam_cmd_ois_info *ois_info;
+
+	if (!o_ctrl || !cmd_buf) {
+		CAM_ERR(CAM_OIS, "Invalid Args");
+		return -EINVAL;
+	}
+
+	ois_info = (struct cam_cmd_ois_info *)cmd_buf;
+	if (o_ctrl->io_master_info.master_type == CCI_MASTER) {
+		o_ctrl->io_master_info.cci_client->i2c_freq_mode =
+			ois_info->i2c_freq_mode;
+		o_ctrl->io_master_info.cci_client->sid =
+			ois_info->slave_addr >> 1;
+		o_ctrl->ois_fw_flag = ois_info->ois_fw_flag;
+		o_ctrl->is_ois_calib = ois_info->is_ois_calib;
+		memcpy(o_ctrl->ois_name, ois_info->ois_name, 32);
+		o_ctrl->io_master_info.cci_client->retries = 3;
+		o_ctrl->io_master_info.cci_client->id_map = 0;
+		memcpy(&(o_ctrl->opcode), &(ois_info->opcode),
+			sizeof(struct cam_ois_opcode));
+		CAM_DBG(CAM_OIS, "Slave addr: 0x%x Freq Mode: %d",
+			ois_info->slave_addr, ois_info->i2c_freq_mode);
+	} else if (o_ctrl->io_master_info.master_type == I2C_MASTER) {
+		o_ctrl->io_master_info.client->addr = ois_info->slave_addr;
+		CAM_DBG(CAM_OIS, "Slave addr: 0x%x", ois_info->slave_addr);
+	} else {
+		CAM_ERR(CAM_OIS, "Invalid Master type : %d",
+			o_ctrl->io_master_info.master_type);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int cam_ois_fw_download(struct cam_ois_ctrl_t *o_ctrl)
+{
+	uint16_t                           total_bytes = 0;
+	uint8_t                           *ptr = NULL;
+	int32_t                            rc = 0, cnt;
+	const struct firmware             *fw = NULL;
+	const char                        *fw_name_prog = NULL;
+	const char                        *fw_name_coeff = NULL;
+	char                               name_prog[32] = {0};
+	char                               name_coeff[32] = {0};
+	struct device                     *dev = &(o_ctrl->pdev->dev);
+	struct cam_sensor_i2c_reg_setting  i2c_reg_setting;
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "Invalid Args");
+		return -EINVAL;
+	}
+
+	snprintf(name_coeff, 32, "%s.coeff", o_ctrl->ois_name);
+
+	snprintf(name_prog, 32, "%s.prog", o_ctrl->ois_name);
+
+	/* cast pointer as const pointer*/
+	fw_name_prog = name_prog;
+	fw_name_coeff = name_coeff;
+
+	/* Load FW */
+	rc = request_firmware(&fw, fw_name_prog, dev);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_prog);
+		return rc;
+	}
+
+	total_bytes = fw->size;
+	i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE;
+	i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE;
+	i2c_reg_setting.size = total_bytes;
+	i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *)
+		kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * total_bytes,
+		GFP_KERNEL);
+	if (!i2c_reg_setting.reg_setting) {
+		CAM_ERR(CAM_OIS, "Failed in allocating i2c_array");
+		release_firmware(fw);
+		return -ENOMEM;
+	}
+
+	for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes;
+		cnt++, ptr++) {
+		i2c_reg_setting.reg_setting[cnt].reg_addr =
+			o_ctrl->opcode.prog;
+		i2c_reg_setting.reg_setting[cnt].reg_data = *ptr;
+		i2c_reg_setting.reg_setting[cnt].delay = 0;
+		i2c_reg_setting.reg_setting[cnt].data_mask = 0;
+	}
+
+	rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info),
+		&i2c_reg_setting, 1);
+	if (rc < 0) {
+		CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc);
+		goto release_firmware;
+	}
+	kfree(i2c_reg_setting.reg_setting);
+	release_firmware(fw);
+
+	rc = request_firmware(&fw, fw_name_coeff, dev);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "Failed to locate %s", fw_name_coeff);
+		return rc;
+	}
+
+	total_bytes = fw->size;
+	i2c_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE;
+	i2c_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE;
+	i2c_reg_setting.size = total_bytes;
+	i2c_reg_setting.reg_setting = (struct cam_sensor_i2c_reg_array *)
+		kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * total_bytes,
+		GFP_KERNEL);
+	if (!i2c_reg_setting.reg_setting) {
+		CAM_ERR(CAM_OIS, "Failed in allocating i2c_array");
+		release_firmware(fw);
+		return -ENOMEM;
+	}
+
+	for (cnt = 0, ptr = (uint8_t *)fw->data; cnt < total_bytes;
+		cnt++, ptr++) {
+		i2c_reg_setting.reg_setting[cnt].reg_addr =
+			o_ctrl->opcode.coeff;
+		i2c_reg_setting.reg_setting[cnt].reg_data = *ptr;
+		i2c_reg_setting.reg_setting[cnt].delay = 0;
+		i2c_reg_setting.reg_setting[cnt].data_mask = 0;
+	}
+
+	rc = camera_io_dev_write_continuous(&(o_ctrl->io_master_info),
+		&i2c_reg_setting, 1);
+	if (rc < 0)
+		CAM_ERR(CAM_OIS, "OIS FW download failed %d", rc);
+
+release_firmware:
+	kfree(i2c_reg_setting.reg_setting);
+	release_firmware(fw);
+
+	return rc;
+}
+
+/**
+ * cam_ois_pkt_parse - Parse csl packet
+ * @o_ctrl:     ctrl structure
+ * @arg:        Camera control command argument
+ *
+ * Returns success or failure
+ */
+static int cam_ois_pkt_parse(struct cam_ois_ctrl_t *o_ctrl, void *arg)
+{
+	int32_t                         rc = 0;
+	uint64_t                        generic_ptr;
+	struct cam_control             *ioctl_ctrl = NULL;
+	struct cam_config_dev_cmd       dev_config;
+	struct i2c_settings_array      *i2c_reg_settings = NULL;
+	struct cam_cmd_buf_desc        *cmd_desc = NULL;
+	uint64_t                        generic_pkt_addr;
+	size_t                          pkt_len;
+	struct cam_packet              *csl_packet = NULL;
+	size_t                          len_of_buff = 0;
+	uint32_t                       *offset = NULL, *cmd_buf;
+
+	ioctl_ctrl = (struct cam_control *)arg;
+	if (copy_from_user(&dev_config, (void __user *) ioctl_ctrl->handle,
+		sizeof(dev_config)))
+		return -EFAULT;
+	rc = cam_mem_get_cpu_buf(dev_config.packet_handle,
+		(uint64_t *)&generic_pkt_addr, &pkt_len);
+	if (rc) {
+		CAM_ERR(CAM_OIS,
+			"error in converting command Handle Error: %d", rc);
+		return rc;
+	}
+
+	if (dev_config.offset > pkt_len) {
+		CAM_ERR(CAM_OIS,
+			"offset is out of bound: off: %lld len: %zu",
+			dev_config.offset, pkt_len);
+		return -EINVAL;
+	}
+
+	csl_packet = (struct cam_packet *)
+		(generic_pkt_addr + dev_config.offset);
+	switch (csl_packet->header.op_code & 0xFFFFFF) {
+	case CAM_OIS_PACKET_OPCODE_INIT:
+		offset = (uint32_t *)&csl_packet->payload;
+		offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t));
+		cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+
+		if ((csl_packet->num_cmd_buf < 2) &&
+			(csl_packet->num_cmd_buf > 3)) {
+			CAM_ERR(CAM_OIS, "wrong cmd Buffer count: %d",
+				csl_packet->num_cmd_buf);
+			return -EINVAL;
+		}
+
+		rc = cam_mem_get_cpu_buf(cmd_desc[0].mem_handle,
+			(uint64_t *)&generic_ptr, &len_of_buff);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "Failed to get cpu buf");
+			return rc;
+		}
+
+		cmd_buf = (uint32_t *)generic_ptr;
+		cmd_buf += cmd_desc->offset / sizeof(uint32_t);
+		rc = cam_ois_slaveInfo_pkt_parser(o_ctrl, cmd_buf);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "Failed in parsing the pkt");
+			return rc;
+		}
+
+		cmd_buf += (sizeof(struct cam_cmd_i2c_info)/sizeof(uint32_t));
+
+		i2c_reg_settings = &(o_ctrl->i2c_init_data);
+		i2c_reg_settings->is_settings_valid = 1;
+		i2c_reg_settings->request_id = 0;
+		rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
+			&cmd_desc[1], 1);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d",
+				rc);
+			return rc;
+		}
+
+		if (o_ctrl->is_ois_calib) {
+			i2c_reg_settings = &(o_ctrl->i2c_calib_data);
+			i2c_reg_settings->is_settings_valid = 1;
+			i2c_reg_settings->request_id = 0;
+			rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
+				&cmd_desc[2], 1);
+			if (rc < 0) {
+				CAM_ERR(CAM_OIS,
+					"OIS pkt parsing failed: %d", rc);
+				return rc;
+			}
+		}
+
+		if (o_ctrl->ois_fw_flag) {
+			rc = cam_ois_fw_download(o_ctrl);
+			if (rc) {
+				CAM_ERR(CAM_OIS, "Failed OIS FW Download");
+				goto pwr_dwn;
+			}
+		}
+
+		rc = cam_ois_apply_settings(o_ctrl, &o_ctrl->i2c_init_data);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "Cannot apply Init settings");
+			goto pwr_dwn;
+		}
+
+		if (o_ctrl->is_ois_calib) {
+			rc = cam_ois_apply_settings(o_ctrl,
+				&o_ctrl->i2c_calib_data);
+			if (rc) {
+				CAM_ERR(CAM_OIS, "Cannot apply calib data");
+				goto pwr_dwn;
+			}
+		}
+
+		rc = delete_request(&o_ctrl->i2c_init_data);
+		if (rc < 0) {
+			CAM_WARN(CAM_OIS,
+				"Fail deleting Init data: rc: %d", rc);
+			rc = 0;
+		}
+		rc = delete_request(&o_ctrl->i2c_calib_data);
+		if (rc < 0) {
+			CAM_WARN(CAM_OIS,
+				"Fail deleting Calibration data: rc: %d", rc);
+			rc = 0;
+		}
+		break;
+	case CAM_OIS_PACKET_OPCODE_OIS_CONTROL:
+		offset = (uint32_t *)&csl_packet->payload;
+		offset += (csl_packet->cmd_buf_offset / sizeof(uint32_t));
+		cmd_desc = (struct cam_cmd_buf_desc *)(offset);
+		i2c_reg_settings = &(o_ctrl->i2c_mode_data);
+		i2c_reg_settings->is_settings_valid = 1;
+		i2c_reg_settings->request_id = 0;
+		rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
+			cmd_desc, 1);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "OIS pkt parsing failed: %d", rc);
+			return rc;
+		}
+
+		rc = cam_ois_apply_settings(o_ctrl, i2c_reg_settings);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "Cannot apply mode settings");
+			return rc;
+		}
+
+		rc = delete_request(i2c_reg_settings);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS,
+				"Fail deleting Mode data: rc: %d", rc);
+		break;
+	default:
+		break;
+	}
+	return rc;
+pwr_dwn:
+	cam_ois_power_down(o_ctrl);
+	return rc;
+}
+
+void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int rc;
+
+	if (o_ctrl->cam_ois_state == CAM_OIS_INIT)
+		return;
+
+	if ((o_ctrl->cam_ois_state == CAM_OIS_START) ||
+		(o_ctrl->cam_ois_state == CAM_OIS_ACQUIRE)) {
+		rc = cam_ois_power_down(o_ctrl);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS, "OIS Power down failed");
+
+		rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS, "destroying the device hdl");
+		o_ctrl->bridge_intf.device_hdl = -1;
+		o_ctrl->bridge_intf.link_hdl = -1;
+		o_ctrl->bridge_intf.session_hdl = -1;
+	}
+
+	o_ctrl->cam_ois_state = CAM_OIS_INIT;
+}
+
+/**
+ * cam_ois_driver_cmd - Handle ois cmds
+ * @e_ctrl:     ctrl structure
+ * @arg:        Camera control command argument
+ *
+ * Returns success or failure
+ */
+int cam_ois_driver_cmd(struct cam_ois_ctrl_t *o_ctrl, void *arg)
+{
+	int                            rc = 0;
+	struct cam_ois_query_cap_t     ois_cap = {0};
+	struct cam_control            *cmd = (struct cam_control *)arg;
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "e_ctrl is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&(o_ctrl->ois_mutex));
+	switch (cmd->op_code) {
+	case CAM_QUERY_CAP:
+		ois_cap.slot_info = o_ctrl->soc_info.index;
+
+		if (copy_to_user((void __user *) cmd->handle,
+			&ois_cap,
+			sizeof(struct cam_ois_query_cap_t))) {
+			CAM_ERR(CAM_OIS, "Failed Copy to User");
+			rc = -EFAULT;
+			goto release_mutex;
+		}
+		CAM_DBG(CAM_OIS, "ois_cap: ID: %d", ois_cap.slot_info);
+		break;
+	case CAM_ACQUIRE_DEV:
+		rc = cam_ois_get_dev_handle(o_ctrl, arg);
+		if (rc) {
+			CAM_ERR(CAM_OIS, "Failed to acquire dev");
+			goto release_mutex;
+		}
+
+		rc = cam_ois_power_up(o_ctrl);
+		if (rc) {
+			CAM_ERR(CAM_OIS, " OIS Power up failed");
+			goto release_mutex;
+		}
+
+		o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
+		break;
+	case CAM_START_DEV:
+		if (o_ctrl->cam_ois_state != CAM_OIS_ACQUIRE) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_OIS,
+			"Not in right state for start : %d",
+			o_ctrl->cam_ois_state);
+			goto release_mutex;
+		}
+		o_ctrl->cam_ois_state = CAM_OIS_START;
+		break;
+	case CAM_CONFIG_DEV:
+		rc = cam_ois_pkt_parse(o_ctrl, arg);
+		if (rc) {
+			CAM_ERR(CAM_OIS, "Failed in ois pkt Parsing");
+			goto release_mutex;
+		}
+		break;
+	case CAM_RELEASE_DEV:
+		if (o_ctrl->cam_ois_state != CAM_OIS_ACQUIRE) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_OIS,
+			"Not in right state for release : %d",
+			o_ctrl->cam_ois_state);
+			goto release_mutex;
+		}
+
+		rc = cam_ois_power_down(o_ctrl);
+		if (rc < 0) {
+			CAM_ERR(CAM_OIS, "OIS Power down failed");
+			goto release_mutex;
+		}
+
+		if (o_ctrl->bridge_intf.device_hdl == -1) {
+			CAM_ERR(CAM_OIS, "link hdl: %d device hdl: %d",
+				o_ctrl->bridge_intf.device_hdl,
+				o_ctrl->bridge_intf.link_hdl);
+			rc = -EINVAL;
+			goto release_mutex;
+		}
+		rc = cam_destroy_device_hdl(o_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS, "destroying the device hdl");
+		o_ctrl->bridge_intf.device_hdl = -1;
+		o_ctrl->bridge_intf.link_hdl = -1;
+		o_ctrl->bridge_intf.session_hdl = -1;
+		o_ctrl->cam_ois_state = CAM_OIS_INIT;
+		break;
+	case CAM_STOP_DEV:
+		if (o_ctrl->cam_ois_state != CAM_OIS_START) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_OIS,
+			"Not in right state for stop : %d",
+			o_ctrl->cam_ois_state);
+		}
+		o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
+		break;
+	default:
+		CAM_ERR(CAM_OIS, "invalid opcode");
+		goto release_mutex;
+	}
+release_mutex:
+	mutex_unlock(&(o_ctrl->ois_mutex));
+	return rc;
+}
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h
new file mode 100644
index 0000000..516ac88
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_core.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_OIS_CORE_H_
+#define _CAM_OIS_CORE_H_
+
+#include "cam_ois_dev.h"
+
+/**
+ * @power_info: power setting info to control the power
+ *
+ * This API construct the default ois power setting.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int32_t cam_ois_construct_default_power_setting(
+	struct cam_sensor_power_ctrl_t *power_info);
+
+
+int cam_ois_driver_cmd(struct cam_ois_ctrl_t *e_ctrl, void *arg);
+
+/**
+ * @o_ctrl: OIS ctrl structure
+ *
+ * This API handles the shutdown ioctl/close
+ */
+void cam_ois_shutdown(struct cam_ois_ctrl_t *o_ctrl);
+
+#endif
+/* _CAM_OIS_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
new file mode 100644
index 0000000..d9b43a4
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "cam_ois_dev.h"
+#include "cam_req_mgr_dev.h"
+#include "cam_ois_soc.h"
+#include "cam_ois_core.h"
+#include "cam_debug_util.h"
+
+static long cam_ois_subdev_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, void *arg)
+{
+	int                       rc     = 0;
+	struct cam_ois_ctrl_t *o_ctrl = v4l2_get_subdevdata(sd);
+
+	switch (cmd) {
+	case VIDIOC_CAM_CONTROL:
+		rc = cam_ois_driver_cmd(o_ctrl, arg);
+		break;
+	default:
+		rc = -ENOIOCTLCMD;
+		break;
+	}
+
+	return rc;
+}
+
+static int cam_ois_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_ois_ctrl_t *o_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "o_ctrl ptr is NULL");
+			return -EINVAL;
+	}
+
+	mutex_lock(&(o_ctrl->ois_mutex));
+	cam_ois_shutdown(o_ctrl);
+	mutex_unlock(&(o_ctrl->ois_mutex));
+
+	return 0;
+}
+
+static int32_t cam_ois_update_i2c_info(struct cam_ois_ctrl_t *o_ctrl,
+	struct cam_ois_i2c_info_t *i2c_info)
+{
+	struct cam_sensor_cci_client        *cci_client = NULL;
+
+	if (o_ctrl->io_master_info.master_type == CCI_MASTER) {
+		cci_client = o_ctrl->io_master_info.cci_client;
+		if (!cci_client) {
+			CAM_ERR(CAM_OIS, "failed: cci_client %pK",
+				cci_client);
+			return -EINVAL;
+		}
+		cci_client->cci_i2c_master = o_ctrl->cci_i2c_master;
+		cci_client->sid = (i2c_info->slave_addr) >> 1;
+		cci_client->retries = 3;
+		cci_client->id_map = 0;
+		cci_client->i2c_freq_mode = i2c_info->i2c_freq_mode;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_COMPAT
+static long cam_ois_init_subdev_do_ioctl(struct v4l2_subdev *sd,
+	unsigned int cmd, unsigned long arg)
+{
+	struct cam_control cmd_data;
+	int32_t rc = 0;
+
+	if (copy_from_user(&cmd_data, (void __user *)arg,
+		sizeof(cmd_data))) {
+		CAM_ERR(CAM_OIS,
+			"Failed to copy from user_ptr=%pK size=%zu",
+			(void __user *)arg, sizeof(cmd_data));
+		return -EFAULT;
+	}
+
+	switch (cmd) {
+	case VIDIOC_CAM_CONTROL:
+		rc = cam_ois_subdev_ioctl(sd, cmd, &cmd_data);
+		if (rc) {
+			CAM_ERR(CAM_OIS,
+				"Failed in ois suddev handling rc %d",
+				rc);
+			return rc;
+		}
+		break;
+	default:
+		CAM_ERR(CAM_OIS, "Invalid compat ioctl: %d", cmd);
+		rc = -EINVAL;
+	}
+
+	if (!rc) {
+		if (copy_to_user((void __user *)arg, &cmd_data,
+			sizeof(cmd_data))) {
+			CAM_ERR(CAM_OIS,
+				"Failed to copy from user_ptr=%pK size=%zu",
+				(void __user *)arg, sizeof(cmd_data));
+			rc = -EFAULT;
+		}
+	}
+	return rc;
+}
+#endif
+
+static const struct v4l2_subdev_internal_ops cam_ois_internal_ops = {
+	.close = cam_ois_subdev_close,
+};
+
+static struct v4l2_subdev_core_ops cam_ois_subdev_core_ops = {
+	.ioctl = cam_ois_subdev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = cam_ois_init_subdev_do_ioctl,
+#endif
+};
+
+static struct v4l2_subdev_ops cam_ois_subdev_ops = {
+	.core = &cam_ois_subdev_core_ops,
+};
+
+static int cam_ois_init_subdev_param(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int rc = 0;
+
+	o_ctrl->v4l2_dev_str.internal_ops = &cam_ois_internal_ops;
+	o_ctrl->v4l2_dev_str.ops = &cam_ois_subdev_ops;
+	strlcpy(o_ctrl->device_name, CAM_OIS_NAME,
+		sizeof(o_ctrl->device_name));
+	o_ctrl->v4l2_dev_str.name = o_ctrl->device_name;
+	o_ctrl->v4l2_dev_str.sd_flags =
+		(V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS);
+	o_ctrl->v4l2_dev_str.ent_function = CAM_OIS_DEVICE_TYPE;
+	o_ctrl->v4l2_dev_str.token = o_ctrl;
+
+	rc = cam_register_subdev(&(o_ctrl->v4l2_dev_str));
+	if (rc)
+		CAM_ERR(CAM_OIS, "fail to create subdev");
+
+	return rc;
+}
+
+static int cam_ois_i2c_driver_probe(struct i2c_client *client,
+	 const struct i2c_device_id *id)
+{
+	int                          rc = 0;
+	struct cam_ois_ctrl_t       *o_ctrl = NULL;
+	struct cam_ois_soc_private  *soc_private = NULL;
+
+	if (client == NULL || id == NULL) {
+		CAM_ERR(CAM_OIS, "Invalid Args client: %pK id: %pK",
+			client, id);
+		return -EINVAL;
+	}
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+		CAM_ERR(CAM_OIS, "i2c_check_functionality failed");
+		goto probe_failure;
+	}
+
+	o_ctrl = kzalloc(sizeof(*o_ctrl), GFP_KERNEL);
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "kzalloc failed");
+		rc = -ENOMEM;
+		goto probe_failure;
+	}
+
+	i2c_set_clientdata(client, o_ctrl);
+
+	o_ctrl->soc_info.dev = &client->dev;
+	o_ctrl->soc_info.dev_name = client->name;
+	o_ctrl->ois_device_type = MSM_CAMERA_I2C_DEVICE;
+	o_ctrl->io_master_info.master_type = I2C_MASTER;
+	o_ctrl->io_master_info.client = client;
+
+	soc_private = kzalloc(sizeof(struct cam_ois_soc_private),
+		GFP_KERNEL);
+	if (!soc_private) {
+		rc = -ENOMEM;
+		goto octrl_free;
+	}
+
+	o_ctrl->soc_info.soc_private = soc_private;
+	rc = cam_ois_driver_soc_init(o_ctrl);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "failed: cam_sensor_parse_dt rc %d", rc);
+		goto octrl_free;
+	}
+
+	rc = cam_ois_init_subdev_param(o_ctrl);
+	if (rc)
+		goto octrl_free;
+
+	rc = cam_ois_construct_default_power_setting(
+		&soc_private->power_info);
+	if (rc < 0) {
+		CAM_ERR(CAM_OIS,
+			"Construct default ois power setting failed.");
+		goto unreg_subdev;
+	}
+
+	o_ctrl->cam_ois_state = CAM_OIS_INIT;
+
+	return rc;
+
+unreg_subdev:
+	cam_unregister_subdev(&(o_ctrl->v4l2_dev_str));
+octrl_free:
+	kfree(o_ctrl);
+probe_failure:
+	return rc;
+}
+
+static int cam_ois_i2c_driver_remove(struct i2c_client *client)
+{
+	struct cam_ois_ctrl_t          *o_ctrl = i2c_get_clientdata(client);
+	struct cam_ois_soc_private     *soc_private;
+	struct cam_sensor_power_ctrl_t *power_info;
+
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "ois device is NULL");
+		return -EINVAL;
+	}
+
+	soc_private =
+		(struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
+	kfree(power_info->power_setting);
+	kfree(power_info->power_down_setting);
+	kfree(o_ctrl->soc_info.soc_private);
+	kfree(o_ctrl);
+
+	return 0;
+}
+
+static int32_t cam_ois_platform_driver_probe(
+	struct platform_device *pdev)
+{
+	int32_t                         rc = 0;
+	struct cam_ois_ctrl_t          *o_ctrl = NULL;
+	struct cam_ois_soc_private     *soc_private = NULL;
+
+	o_ctrl = kzalloc(sizeof(struct cam_ois_ctrl_t), GFP_KERNEL);
+	if (!o_ctrl)
+		return -ENOMEM;
+
+	o_ctrl->soc_info.pdev = pdev;
+	o_ctrl->pdev = pdev;
+	o_ctrl->soc_info.dev = &pdev->dev;
+	o_ctrl->soc_info.dev_name = pdev->name;
+
+	o_ctrl->ois_device_type = MSM_CAMERA_PLATFORM_DEVICE;
+
+	o_ctrl->io_master_info.master_type = CCI_MASTER;
+	o_ctrl->io_master_info.cci_client = kzalloc(
+		sizeof(struct cam_sensor_cci_client), GFP_KERNEL);
+	if (!o_ctrl->io_master_info.cci_client)
+		goto free_o_ctrl;
+
+	soc_private = kzalloc(sizeof(struct cam_ois_soc_private),
+		GFP_KERNEL);
+	if (!soc_private) {
+		rc = -ENOMEM;
+		goto free_cci_client;
+	}
+	o_ctrl->soc_info.soc_private = soc_private;
+
+	INIT_LIST_HEAD(&(o_ctrl->i2c_init_data.list_head));
+	INIT_LIST_HEAD(&(o_ctrl->i2c_calib_data.list_head));
+	INIT_LIST_HEAD(&(o_ctrl->i2c_mode_data.list_head));
+	mutex_init(&(o_ctrl->ois_mutex));
+	rc = cam_ois_driver_soc_init(o_ctrl);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "failed: soc init rc %d", rc);
+		goto free_soc;
+	}
+
+	rc = cam_ois_init_subdev_param(o_ctrl);
+	if (rc)
+		goto free_soc;
+
+	rc = cam_ois_update_i2c_info(o_ctrl, &soc_private->i2c_info);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "failed: to update i2c info rc %d", rc);
+		goto unreg_subdev;
+	}
+	o_ctrl->bridge_intf.device_hdl = -1;
+
+	rc = cam_ois_construct_default_power_setting(
+		&soc_private->power_info);
+	if (rc < 0) {
+		CAM_ERR(CAM_OIS,
+			"Construct default ois power setting failed.");
+		goto unreg_subdev;
+	}
+
+	platform_set_drvdata(pdev, o_ctrl);
+	v4l2_set_subdevdata(&o_ctrl->v4l2_dev_str.sd, o_ctrl);
+
+	o_ctrl->cam_ois_state = CAM_OIS_INIT;
+
+	return rc;
+unreg_subdev:
+	cam_unregister_subdev(&(o_ctrl->v4l2_dev_str));
+free_soc:
+	kfree(soc_private);
+free_cci_client:
+	kfree(o_ctrl->io_master_info.cci_client);
+free_o_ctrl:
+	kfree(o_ctrl);
+	return rc;
+}
+
+static int cam_ois_platform_driver_remove(struct platform_device *pdev)
+{
+	struct cam_ois_ctrl_t          *o_ctrl;
+	struct cam_ois_soc_private     *soc_private;
+	struct cam_sensor_power_ctrl_t *power_info;
+
+	o_ctrl = platform_get_drvdata(pdev);
+	if (!o_ctrl) {
+		CAM_ERR(CAM_OIS, "ois device is NULL");
+		return -EINVAL;
+	}
+
+	soc_private =
+		(struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+	power_info = &soc_private->power_info;
+
+	kfree(power_info->power_setting);
+	kfree(power_info->power_down_setting);
+	kfree(o_ctrl->soc_info.soc_private);
+	kfree(o_ctrl->io_master_info.cci_client);
+	kfree(o_ctrl);
+	return 0;
+}
+
+static const struct of_device_id cam_ois_dt_match[] = {
+	{ .compatible = "qcom,ois" },
+	{ }
+};
+
+
+MODULE_DEVICE_TABLE(of, cam_ois_dt_match);
+
+static struct platform_driver cam_ois_platform_driver = {
+	.driver = {
+		.name = "qcom,ois",
+		.owner = THIS_MODULE,
+		.of_match_table = cam_ois_dt_match,
+	},
+	.probe = cam_ois_platform_driver_probe,
+	.remove = cam_ois_platform_driver_remove,
+};
+static const struct i2c_device_id cam_ois_i2c_id[] = {
+	{ "msm_ois", (kernel_ulong_t)NULL},
+	{ }
+};
+
+static struct i2c_driver cam_ois_i2c_driver = {
+	.id_table = cam_ois_i2c_id,
+	.probe  = cam_ois_i2c_driver_probe,
+	.remove = cam_ois_i2c_driver_remove,
+	.driver = {
+		.name = "msm_ois",
+	},
+};
+
+static struct cam_ois_registered_driver_t registered_driver = {
+	0, 0};
+
+static int __init cam_ois_driver_init(void)
+{
+	int rc = 0;
+
+	rc = platform_driver_register(&cam_ois_platform_driver);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "platform_driver_register failed rc = %d",
+			rc);
+		return rc;
+	}
+
+	registered_driver.platform_driver = 1;
+
+	rc = i2c_add_driver(&cam_ois_i2c_driver);
+	if (rc) {
+		CAM_ERR(CAM_OIS, "i2c_add_driver failed rc = %d", rc);
+		return rc;
+	}
+
+	registered_driver.i2c_driver = 1;
+	return rc;
+}
+
+static void __exit cam_ois_driver_exit(void)
+{
+	if (registered_driver.platform_driver)
+		platform_driver_unregister(&cam_ois_platform_driver);
+
+	if (registered_driver.i2c_driver)
+		i2c_del_driver(&cam_ois_i2c_driver);
+}
+
+module_init(cam_ois_driver_init);
+module_exit(cam_ois_driver_exit);
+MODULE_DESCRIPTION("CAM OIS driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h
new file mode 100644
index 0000000..80f1e84
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_dev.h
@@ -0,0 +1,131 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CAM_OIS_DEV_H_
+#define _CAM_OIS_DEV_H_
+
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/cam_sensor.h>
+#include <cam_sensor_i2c.h>
+#include <cam_sensor_spi.h>
+#include <cam_sensor_io.h>
+#include <cam_cci_dev.h>
+#include <cam_req_mgr_util.h>
+#include <cam_req_mgr_interface.h>
+#include <cam_mem_mgr.h>
+#include <cam_subdev.h>
+#include "cam_soc_util.h"
+
+#define DEFINE_MSM_MUTEX(mutexname) \
+	static struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
+
+enum cam_ois_state {
+	CAM_OIS_INIT,
+	CAM_OIS_ACQUIRE,
+	CAM_OIS_START,
+};
+
+/**
+ * struct cam_ois_registered_driver_t - registered driver info
+ * @platform_driver      :   flag indicates if platform driver is registered
+ * @i2c_driver           :   flag indicates if i2c driver is registered
+ *
+ */
+struct cam_ois_registered_driver_t {
+	bool platform_driver;
+	bool i2c_driver;
+};
+
+/**
+ * struct cam_ois_i2c_info_t - I2C info
+ * @slave_addr      :   slave address
+ * @i2c_freq_mode   :   i2c frequency mode
+ *
+ */
+struct cam_ois_i2c_info_t {
+	uint16_t slave_addr;
+	uint8_t i2c_freq_mode;
+};
+
+/**
+ * struct cam_ois_soc_private - ois soc private data structure
+ * @ois_name        :   ois name
+ * @i2c_info        :   i2c info structure
+ * @power_info      :   ois power info
+ *
+ */
+struct cam_ois_soc_private {
+	const char *ois_name;
+	struct cam_ois_i2c_info_t i2c_info;
+	struct cam_sensor_power_ctrl_t power_info;
+};
+
+/**
+ * struct cam_ois_intf_params - bridge interface params
+ * @device_hdl   : Device Handle
+ * @session_hdl  : Session Handle
+ * @ops          : KMD operations
+ * @crm_cb       : Callback API pointers
+ */
+struct cam_ois_intf_params {
+	int32_t device_hdl;
+	int32_t session_hdl;
+	int32_t link_hdl;
+	struct cam_req_mgr_kmd_ops ops;
+	struct cam_req_mgr_crm_cb *crm_cb;
+};
+
+/**
+ * struct cam_ois_ctrl_t - OIS ctrl private data
+ * @pdev            :   platform device
+ * @ois_mutex       :   ois mutex
+ * @soc_info        :   ois soc related info
+ * @io_master_info  :   Information about the communication master
+ * @cci_i2c_master  :   I2C structure
+ * @v4l2_dev_str    :   V4L2 device structure
+ * @bridge_intf     :   bridge interface params
+ * @i2c_init_data   :   ois i2c init settings
+ * @i2c_mode_data   :   ois i2c mode settings
+ * @i2c_calib_data  :   ois i2c calib settings
+ * @ois_device_type :   ois device type
+ * @cam_ois_state   :   ois_device_state
+ * @ois_name        :   ois name
+ * @ois_fw_flag     :   flag for firmware download
+ * @is_ois_calib    :   flag for Calibration data
+ * @opcode          :   ois opcode
+ * @device_name     :   Device name
+ *
+ */
+struct cam_ois_ctrl_t {
+	struct platform_device *pdev;
+	struct mutex ois_mutex;
+	struct cam_hw_soc_info soc_info;
+	struct camera_io_master io_master_info;
+	enum cci_i2c_master_t cci_i2c_master;
+	struct cam_subdev v4l2_dev_str;
+	struct cam_ois_intf_params bridge_intf;
+	struct i2c_settings_array i2c_init_data;
+	struct i2c_settings_array i2c_calib_data;
+	struct i2c_settings_array i2c_mode_data;
+	enum msm_camera_device_type_t ois_device_type;
+	enum cam_ois_state cam_ois_state;
+	char device_name[20];
+	char ois_name[32];
+	uint8_t ois_fw_flag;
+	uint8_t is_ois_calib;
+	struct cam_ois_opcode opcode;
+};
+
+#endif /*_CAM_OIS_DEV_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c
new file mode 100644
index 0000000..5886413
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <cam_sensor_cmn_header.h>
+#include <cam_sensor_util.h>
+#include <cam_sensor_io.h>
+#include <cam_req_mgr_util.h>
+
+#include "cam_ois_soc.h"
+#include "cam_debug_util.h"
+
+/**
+ * @e_ctrl: ctrl structure
+ *
+ * Parses ois dt
+ */
+static int cam_ois_get_dt_data(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int                             rc = 0;
+	struct cam_hw_soc_info         *soc_info = &o_ctrl->soc_info;
+	struct cam_ois_soc_private     *soc_private =
+		(struct cam_ois_soc_private *)o_ctrl->soc_info.soc_private;
+	struct cam_sensor_power_ctrl_t *power_info = &soc_private->power_info;
+	struct device_node             *of_node = NULL;
+
+	of_node = soc_info->dev->of_node;
+
+	if (!of_node) {
+		CAM_ERR(CAM_OIS, "of_node is NULL, device type %d",
+			o_ctrl->ois_device_type);
+		return -EINVAL;
+	}
+	rc = cam_soc_util_get_dt_properties(soc_info);
+	if (rc < 0) {
+		CAM_ERR(CAM_OIS, "cam_soc_util_get_dt_properties rc %d",
+			rc);
+		return rc;
+	}
+
+	if (!soc_info->gpio_data) {
+		CAM_INFO(CAM_OIS, "No GPIO found");
+		return 0;
+	}
+
+	if (!soc_info->gpio_data->cam_gpio_common_tbl_size) {
+		CAM_INFO(CAM_OIS, "No GPIO found");
+		return -EINVAL;
+	}
+
+	rc = cam_sensor_util_init_gpio_pin_tbl(soc_info,
+		&power_info->gpio_num_info);
+	if ((rc < 0) || (!power_info->gpio_num_info)) {
+		CAM_ERR(CAM_OIS, "No/Error OIS GPIOs");
+		return -EINVAL;
+	}
+
+	return rc;
+}
+/**
+ * @o_ctrl: ctrl structure
+ *
+ * This function is called from cam_ois_platform/i2c_driver_probe, it parses
+ * the ois dt node.
+ */
+int cam_ois_driver_soc_init(struct cam_ois_ctrl_t *o_ctrl)
+{
+	int                             rc = 0;
+	struct cam_hw_soc_info         *soc_info = &o_ctrl->soc_info;
+	struct device_node             *of_node = NULL;
+
+	if (!soc_info->dev) {
+		CAM_ERR(CAM_OIS, "soc_info is not initialized");
+		return -EINVAL;
+	}
+
+	of_node = soc_info->dev->of_node;
+	if (!of_node) {
+		CAM_ERR(CAM_OIS, "dev.of_node NULL");
+		return -EINVAL;
+	}
+
+	if (o_ctrl->ois_device_type == MSM_CAMERA_PLATFORM_DEVICE) {
+		rc = of_property_read_u32(of_node, "cci-master",
+			&o_ctrl->cci_i2c_master);
+		if (rc < 0) {
+			CAM_DBG(CAM_OIS, "failed rc %d", rc);
+			return rc;
+		}
+	}
+
+	rc = cam_ois_get_dt_data(o_ctrl);
+	if (rc < 0)
+		CAM_DBG(CAM_OIS, "failed: ois get dt data rc %d", rc);
+
+	return rc;
+}
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.h
similarity index 70%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.h
index c06b806..af04134 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_ois/cam_ois_soc.h
@@ -9,15 +9,11 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
+#ifndef _CAM_OIS_SOC_H_
+#define _CAM_OIS_SOC_H_
 
+#include "cam_ois_dev.h"
 
-/dts-v1/;
+int cam_ois_driver_soc_init(struct cam_ois_ctrl_t *o_ctrl);
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
-};
+#endif/* _CAM_OIS_SOC_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/Makefile
new file mode 100644
index 0000000..516faf5
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/Makefile
@@ -0,0 +1,9 @@
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_core
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
+
+obj-$(CONFIG_SPECTRA_CAMERA) += cam_res_mgr.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c
new file mode 100644
index 0000000..bb3789b
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr.c
@@ -0,0 +1,737 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include "cam_debug_util.h"
+#include "cam_res_mgr_api.h"
+#include "cam_res_mgr_private.h"
+
+static struct cam_res_mgr *cam_res;
+
+static void cam_res_mgr_free_res(void)
+{
+	struct cam_dev_res *dev_res, *dev_temp;
+	struct cam_gpio_res *gpio_res, *gpio_temp;
+	struct cam_flash_res *flash_res, *flash_temp;
+
+	if (!cam_res)
+		return;
+
+	mutex_lock(&cam_res->gpio_res_lock);
+	list_for_each_entry_safe(gpio_res, gpio_temp,
+		&cam_res->gpio_res_list, list) {
+		list_for_each_entry_safe(dev_res, dev_temp,
+			&gpio_res->dev_list, list) {
+			list_del_init(&dev_res->list);
+			kfree(dev_res);
+		}
+		list_del_init(&gpio_res->list);
+		kfree(gpio_res);
+	}
+	mutex_unlock(&cam_res->gpio_res_lock);
+
+	mutex_lock(&cam_res->flash_res_lock);
+	list_for_each_entry_safe(flash_res, flash_temp,
+		&cam_res->flash_res_list, list) {
+		list_del_init(&flash_res->list);
+		kfree(flash_res);
+	}
+	mutex_unlock(&cam_res->flash_res_lock);
+
+	mutex_lock(&cam_res->clk_res_lock);
+	cam_res->shared_clk_ref_count = 0;
+	mutex_unlock(&cam_res->clk_res_lock);
+}
+
+void cam_res_mgr_led_trigger_register(const char *name, struct led_trigger **tp)
+{
+	bool found = false;
+	struct cam_flash_res *flash_res;
+
+	if (!cam_res) {
+		/*
+		 * If this driver not probed, then just register the
+		 * led trigger.
+		 */
+		led_trigger_register_simple(name, tp);
+		return;
+	}
+
+	mutex_lock(&cam_res->flash_res_lock);
+	list_for_each_entry(flash_res, &cam_res->flash_res_list, list) {
+		if (!strcmp(flash_res->name, name)) {
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&cam_res->flash_res_lock);
+
+	if (found) {
+		*tp = flash_res->trigger;
+	} else {
+		flash_res = kzalloc(sizeof(struct cam_flash_res), GFP_KERNEL);
+		if (!flash_res) {
+			CAM_ERR(CAM_RES,
+				"Failed to malloc memory for flash_res:%s",
+				name);
+			*tp = NULL;
+			return;
+		}
+
+		led_trigger_register_simple(name, tp);
+		INIT_LIST_HEAD(&flash_res->list);
+		flash_res->trigger = *tp;
+		flash_res->name = name;
+
+		mutex_lock(&cam_res->flash_res_lock);
+		list_add_tail(&flash_res->list, &cam_res->flash_res_list);
+		mutex_unlock(&cam_res->flash_res_lock);
+	}
+}
+EXPORT_SYMBOL(cam_res_mgr_led_trigger_register);
+
+void cam_res_mgr_led_trigger_unregister(struct led_trigger *tp)
+{
+	bool found = false;
+	struct cam_flash_res *flash_res;
+
+	if (!cam_res) {
+		/*
+		 * If this driver not probed, then just unregister the
+		 * led trigger.
+		 */
+		led_trigger_unregister_simple(tp);
+		return;
+	}
+
+	mutex_lock(&cam_res->flash_res_lock);
+	list_for_each_entry(flash_res, &cam_res->flash_res_list, list) {
+		if (flash_res->trigger == tp) {
+			found = true;
+			break;
+		}
+	}
+
+	if (found) {
+		led_trigger_unregister_simple(tp);
+		list_del_init(&flash_res->list);
+		kfree(flash_res);
+	}
+	mutex_unlock(&cam_res->flash_res_lock);
+}
+EXPORT_SYMBOL(cam_res_mgr_led_trigger_unregister);
+
+void cam_res_mgr_led_trigger_event(struct led_trigger *trig,
+	enum led_brightness brightness)
+{
+	bool found = false;
+	struct cam_flash_res *flash_res;
+
+	if (!cam_res) {
+		/*
+		 * If this driver not probed, then just trigger
+		 * the led event.
+		 */
+		led_trigger_event(trig, brightness);
+		return;
+	}
+
+	mutex_lock(&cam_res->flash_res_lock);
+	list_for_each_entry(flash_res, &cam_res->flash_res_list, list) {
+		if (flash_res->trigger == trig) {
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&cam_res->flash_res_lock);
+
+	if (found)
+		led_trigger_event(trig, brightness);
+}
+EXPORT_SYMBOL(cam_res_mgr_led_trigger_event);
+
+int cam_res_mgr_shared_pinctrl_init(void)
+{
+	struct cam_soc_pinctrl_info *pinctrl_info;
+
+	/*
+	 * We allow the cam_res is NULL or shared_gpio_enabled
+	 * is false, it means this driver no probed or doesn't
+	 * have shared gpio in this device.
+	 */
+	if (!cam_res || !cam_res->shared_gpio_enabled) {
+		CAM_DBG(CAM_RES, "Not support shared gpio.");
+		return 0;
+	}
+
+	if (cam_res->pstatus != PINCTRL_STATUS_PUT) {
+		CAM_DBG(CAM_RES, "The shared pinctrl already been got.");
+		return 0;
+	}
+
+	pinctrl_info = &cam_res->dt.pinctrl_info;
+
+	pinctrl_info->pinctrl =
+		devm_pinctrl_get(cam_res->dev);
+	if (IS_ERR_OR_NULL(pinctrl_info->pinctrl)) {
+		CAM_ERR(CAM_RES, "Pinctrl not available");
+		cam_res->shared_gpio_enabled = false;
+		return -EINVAL;
+	}
+
+	pinctrl_info->gpio_state_active =
+		pinctrl_lookup_state(pinctrl_info->pinctrl,
+			CAM_RES_MGR_DEFAULT);
+	if (IS_ERR_OR_NULL(pinctrl_info->gpio_state_active)) {
+		CAM_ERR(CAM_RES,
+			"Failed to get the active state pinctrl handle");
+		cam_res->shared_gpio_enabled = false;
+		return -EINVAL;
+	}
+
+	pinctrl_info->gpio_state_suspend =
+		pinctrl_lookup_state(pinctrl_info->pinctrl,
+			CAM_RES_MGR_SLEEP);
+	if (IS_ERR_OR_NULL(pinctrl_info->gpio_state_suspend)) {
+		CAM_ERR(CAM_RES,
+			"Failed to get the active state pinctrl handle");
+		cam_res->shared_gpio_enabled = false;
+		return -EINVAL;
+	}
+
+	mutex_lock(&cam_res->gpio_res_lock);
+	cam_res->pstatus = PINCTRL_STATUS_GOT;
+	mutex_unlock(&cam_res->gpio_res_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_init);
+
+static bool cam_res_mgr_shared_pinctrl_check_hold(void)
+{
+	int index = 0;
+	int dev_num = 0;
+	bool hold = false;
+	struct list_head *list;
+	struct cam_gpio_res *gpio_res;
+	struct cam_res_mgr_dt *dt = &cam_res->dt;
+
+	for (; index < dt->num_shared_gpio; index++) {
+		list_for_each_entry(gpio_res,
+			&cam_res->gpio_res_list, list) {
+
+			if (gpio_res->gpio ==
+				dt->shared_gpio[index]) {
+				list_for_each(list, &gpio_res->dev_list)
+					dev_num++;
+
+				if (dev_num >= 2) {
+					hold = true;
+					break;
+				}
+			}
+		}
+	}
+
+	if (cam_res->shared_clk_ref_count > 1)
+		hold = true;
+
+	return hold;
+}
+
+void cam_res_mgr_shared_pinctrl_put(void)
+{
+	struct cam_soc_pinctrl_info *pinctrl_info;
+
+	if (!cam_res || !cam_res->shared_gpio_enabled) {
+		CAM_DBG(CAM_RES, "Not support shared gpio.");
+		return;
+	}
+
+	mutex_lock(&cam_res->gpio_res_lock);
+	if (cam_res->pstatus == PINCTRL_STATUS_PUT) {
+		CAM_DBG(CAM_RES, "The shared pinctrl already been put");
+		mutex_unlock(&cam_res->gpio_res_lock);
+		return;
+	}
+
+	if (cam_res_mgr_shared_pinctrl_check_hold()) {
+		CAM_INFO(CAM_RES, "Need hold put this pinctrl");
+		mutex_unlock(&cam_res->gpio_res_lock);
+		return;
+	}
+
+	pinctrl_info = &cam_res->dt.pinctrl_info;
+
+	devm_pinctrl_put(pinctrl_info->pinctrl);
+
+	cam_res->pstatus = PINCTRL_STATUS_PUT;
+	mutex_unlock(&cam_res->gpio_res_lock);
+}
+EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_put);
+
+int cam_res_mgr_shared_pinctrl_select_state(bool active)
+{
+	int rc = 0;
+	struct cam_soc_pinctrl_info *pinctrl_info;
+
+	if (!cam_res || !cam_res->shared_gpio_enabled) {
+		CAM_DBG(CAM_RES, "Not support shared gpio.");
+		return 0;
+	}
+
+	mutex_lock(&cam_res->gpio_res_lock);
+	if (cam_res->pstatus == PINCTRL_STATUS_PUT) {
+		CAM_DBG(CAM_RES, "The shared pinctrl alerady been put.!");
+		mutex_unlock(&cam_res->gpio_res_lock);
+		return 0;
+	}
+
+	pinctrl_info = &cam_res->dt.pinctrl_info;
+
+	if (active && (cam_res->pstatus != PINCTRL_STATUS_ACTIVE)) {
+		rc = pinctrl_select_state(pinctrl_info->pinctrl,
+			pinctrl_info->gpio_state_active);
+		cam_res->pstatus = PINCTRL_STATUS_ACTIVE;
+	} else if (!active &&
+		!cam_res_mgr_shared_pinctrl_check_hold()) {
+		rc = pinctrl_select_state(pinctrl_info->pinctrl,
+			pinctrl_info->gpio_state_suspend);
+		cam_res->pstatus = PINCTRL_STATUS_SUSPEND;
+	}
+	mutex_unlock(&cam_res->gpio_res_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_select_state);
+
+int cam_res_mgr_shared_pinctrl_post_init(void)
+{
+	int ret = 0;
+	struct cam_soc_pinctrl_info *pinctrl_info;
+
+	if (!cam_res || !cam_res->shared_gpio_enabled) {
+		CAM_DBG(CAM_RES, "Not support shared gpio.");
+		return ret;
+	}
+
+	mutex_lock(&cam_res->gpio_res_lock);
+	if (cam_res->pstatus == PINCTRL_STATUS_PUT) {
+		CAM_DBG(CAM_RES, "The shared pinctrl alerady been put.!");
+		mutex_unlock(&cam_res->gpio_res_lock);
+		return ret;
+	}
+
+	pinctrl_info = &cam_res->dt.pinctrl_info;
+
+	/*
+	 * If no gpio resource in gpio_res_list, and
+	 * no shared clk now, it means this device
+	 * don't have shared gpio.
+	 */
+	if (list_empty(&cam_res->gpio_res_list) &&
+		cam_res->shared_clk_ref_count < 1) {
+		ret = pinctrl_select_state(pinctrl_info->pinctrl,
+			pinctrl_info->gpio_state_suspend);
+		devm_pinctrl_put(pinctrl_info->pinctrl);
+		cam_res->pstatus = PINCTRL_STATUS_PUT;
+	}
+	mutex_unlock(&cam_res->gpio_res_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(cam_res_mgr_shared_pinctrl_post_init);
+
+static int cam_res_mgr_add_device(struct device *dev,
+	struct cam_gpio_res *gpio_res)
+{
+	struct cam_dev_res *dev_res = NULL;
+
+	dev_res = kzalloc(sizeof(struct cam_dev_res), GFP_KERNEL);
+	if (!dev_res)
+		return -ENOMEM;
+
+	dev_res->dev = dev;
+	INIT_LIST_HEAD(&dev_res->list);
+
+	list_add_tail(&dev_res->list, &gpio_res->dev_list);
+
+	return 0;
+}
+
+static bool cam_res_mgr_gpio_is_shared(uint gpio)
+{
+	int index = 0;
+	bool found = false;
+	struct cam_res_mgr_dt *dt = &cam_res->dt;
+
+	for (; index < dt->num_shared_gpio; index++) {
+		if (gpio == dt->shared_gpio[index]) {
+			found = true;
+			break;
+		}
+	}
+
+	return found;
+}
+
+int cam_res_mgr_gpio_request(struct device *dev, uint gpio,
+		unsigned long flags, const char *label)
+{
+	int rc = 0;
+	bool found = false;
+	struct cam_gpio_res *gpio_res = NULL;
+
+	if (cam_res && cam_res->shared_gpio_enabled) {
+		mutex_lock(&cam_res->gpio_res_lock);
+		list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) {
+			if (gpio == gpio_res->gpio) {
+				found = true;
+				break;
+			}
+		}
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	/*
+	 * found equal to false has two situation:
+	 * 1. shared gpio not enabled
+	 * 2. shared gpio enabled, but not find this gpio
+	 *    from the gpio_res_list
+	 * These two situation both need request gpio.
+	 */
+	if (!found) {
+		rc = gpio_request_one(gpio, flags, label);
+		if (rc) {
+			CAM_ERR(CAM_RES, "gpio %d:%s request fails",
+				gpio, label);
+			return rc;
+		}
+	}
+
+	/*
+	 * If the gpio is in the shared list, and not find
+	 * from gpio_res_list, then insert a cam_gpio_res
+	 * to gpio_res_list.
+	 */
+	if (!found && cam_res
+		&& cam_res->shared_gpio_enabled &&
+		cam_res_mgr_gpio_is_shared(gpio)) {
+
+		gpio_res = kzalloc(sizeof(struct cam_gpio_res), GFP_KERNEL);
+		if (!gpio_res)
+			return -ENOMEM;
+
+		gpio_res->gpio = gpio;
+		gpio_res->power_on_count = 0;
+		INIT_LIST_HEAD(&gpio_res->list);
+		INIT_LIST_HEAD(&gpio_res->dev_list);
+
+		rc = cam_res_mgr_add_device(dev, gpio_res);
+		if (rc) {
+			kfree(gpio_res);
+			return rc;
+		}
+
+		mutex_lock(&cam_res->gpio_res_lock);
+		list_add_tail(&gpio_res->list, &cam_res->gpio_res_list);
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	if (found && cam_res
+		&& cam_res->shared_gpio_enabled) {
+		struct cam_dev_res *dev_res = NULL;
+
+		found = 0;
+		mutex_lock(&cam_res->gpio_res_lock);
+		list_for_each_entry(dev_res, &gpio_res->dev_list, list) {
+			if (dev_res->dev == dev) {
+				found = 1;
+				break;
+			}
+		}
+
+		if (!found)
+			rc = cam_res_mgr_add_device(dev, gpio_res);
+
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_res_mgr_gpio_request);
+
+static void cam_res_mgr_gpio_free(struct device *dev, uint gpio)
+{
+	bool found = false;
+	bool need_free = true;
+	int dev_num =  0;
+	struct cam_gpio_res *gpio_res = NULL;
+
+	if (cam_res && cam_res->shared_gpio_enabled) {
+		mutex_lock(&cam_res->gpio_res_lock);
+		list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) {
+			if (gpio == gpio_res->gpio) {
+				found = true;
+				break;
+			}
+		}
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	if (found && cam_res
+		&& cam_res->shared_gpio_enabled) {
+		struct list_head *list;
+		struct cam_dev_res *dev_res = NULL;
+
+		mutex_lock(&cam_res->gpio_res_lock);
+		/* Count the dev number in the dev_list */
+		list_for_each(list, &gpio_res->dev_list)
+			dev_num++;
+
+		/*
+		 * Need free the gpio if only has last 1 device
+		 * in the dev_list, otherwise, not free this
+		 * gpio.
+		 */
+		if (dev_num == 1) {
+			dev_res = list_first_entry(&gpio_res->dev_list,
+				struct cam_dev_res, list);
+			list_del_init(&dev_res->list);
+			kfree(dev_res);
+
+			list_del_init(&gpio_res->list);
+			kfree(gpio_res);
+		} else {
+			list_for_each_entry(dev_res,
+				&gpio_res->dev_list, list) {
+				if (dev_res->dev == dev) {
+					list_del_init(&dev_res->list);
+					kfree(dev_res);
+					need_free = false;
+					break;
+				}
+			}
+		}
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	if (need_free)
+		gpio_free(gpio);
+}
+
+void cam_res_mgr_gpio_free_arry(struct device *dev,
+		const struct gpio *array, size_t num)
+{
+	while (num--)
+		cam_res_mgr_gpio_free(dev, (array[num]).gpio);
+}
+EXPORT_SYMBOL(cam_res_mgr_gpio_free_arry);
+
+int cam_res_mgr_gpio_set_value(unsigned int gpio, int value)
+{
+	int rc = 0;
+	bool found = false;
+	struct cam_gpio_res *gpio_res = NULL;
+
+	if (cam_res && cam_res->shared_gpio_enabled) {
+		mutex_lock(&cam_res->gpio_res_lock);
+		list_for_each_entry(gpio_res, &cam_res->gpio_res_list, list) {
+			if (gpio == gpio_res->gpio) {
+				found = true;
+				break;
+			}
+		}
+		mutex_unlock(&cam_res->gpio_res_lock);
+	}
+
+	/*
+	 * Set the value directly if can't find the gpio from
+	 * gpio_res_list, otherwise, need add ref count support
+	 **/
+	if (!found) {
+		gpio_set_value_cansleep(gpio, value);
+	} else {
+		if (value) {
+			gpio_res->power_on_count++;
+			if (gpio_res->power_on_count < 2) {
+				gpio_set_value_cansleep(gpio, value);
+				CAM_DBG(CAM_RES,
+					"Shared GPIO(%d) : HIGH", gpio);
+			}
+		} else {
+			gpio_res->power_on_count--;
+			if (gpio_res->power_on_count < 1) {
+				gpio_set_value_cansleep(gpio, value);
+				CAM_DBG(CAM_RES,
+					"Shared GPIO(%d) : LOW", gpio);
+			}
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(cam_res_mgr_gpio_set_value);
+
+void cam_res_mgr_shared_clk_config(bool value)
+{
+	if (!cam_res)
+		return;
+
+	mutex_lock(&cam_res->clk_res_lock);
+	if (value)
+		cam_res->shared_clk_ref_count++;
+	else
+		cam_res->shared_clk_ref_count--;
+	mutex_unlock(&cam_res->clk_res_lock);
+}
+EXPORT_SYMBOL(cam_res_mgr_shared_clk_config);
+
+static int cam_res_mgr_parse_dt(struct device *dev)
+{
+	int rc = 0;
+	struct device_node *of_node = NULL;
+	struct cam_res_mgr_dt *dt = &cam_res->dt;
+
+	of_node = dev->of_node;
+
+	dt->num_shared_gpio = of_property_count_u32_elems(of_node,
+		"shared-gpios");
+
+	if (dt->num_shared_gpio > MAX_SHARED_GPIO_SIZE ||
+		dt->num_shared_gpio <= 0) {
+		/*
+		 * Not really an error, it means dtsi not configure
+		 * the shared gpio.
+		 */
+		CAM_DBG(CAM_RES, "Invalid GPIO number %d. No shared gpio.",
+			dt->num_shared_gpio);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_array(of_node, "shared-gpios",
+		dt->shared_gpio, dt->num_shared_gpio);
+	if (rc) {
+		CAM_ERR(CAM_RES, "Get shared gpio array failed.");
+		return -EINVAL;
+	}
+
+	dt->pinctrl_info.pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR_OR_NULL(dt->pinctrl_info.pinctrl)) {
+		CAM_ERR(CAM_RES, "Pinctrl not available");
+		return -EINVAL;
+	}
+
+	/*
+	 * Check the pinctrl state to make sure the gpio
+	 * shared enabled.
+	 */
+	dt->pinctrl_info.gpio_state_active =
+		pinctrl_lookup_state(dt->pinctrl_info.pinctrl,
+			CAM_RES_MGR_DEFAULT);
+	if (IS_ERR_OR_NULL(dt->pinctrl_info.gpio_state_active)) {
+		CAM_ERR(CAM_RES,
+			"Failed to get the active state pinctrl handle");
+		return -EINVAL;
+	}
+
+	dt->pinctrl_info.gpio_state_suspend =
+		pinctrl_lookup_state(dt->pinctrl_info.pinctrl,
+			CAM_RES_MGR_SLEEP);
+	if (IS_ERR_OR_NULL(dt->pinctrl_info.gpio_state_suspend)) {
+		CAM_ERR(CAM_RES,
+			"Failed to get the active state pinctrl handle");
+		return -EINVAL;
+	}
+
+	devm_pinctrl_put(dt->pinctrl_info.pinctrl);
+
+	return rc;
+}
+
+static int cam_res_mgr_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	cam_res = kzalloc(sizeof(*cam_res), GFP_KERNEL);
+	if (!cam_res)
+		return -ENOMEM;
+
+	cam_res->dev = &pdev->dev;
+	mutex_init(&cam_res->flash_res_lock);
+	mutex_init(&cam_res->gpio_res_lock);
+	mutex_init(&cam_res->clk_res_lock);
+
+	rc = cam_res_mgr_parse_dt(&pdev->dev);
+	if (rc) {
+		CAM_INFO(CAM_RES, "Disable shared gpio support.");
+		cam_res->shared_gpio_enabled = false;
+	} else {
+		CAM_INFO(CAM_RES, "Enable shared gpio support.");
+		cam_res->shared_gpio_enabled = true;
+	}
+
+	cam_res->shared_clk_ref_count = 0;
+	cam_res->pstatus = PINCTRL_STATUS_PUT;
+
+	INIT_LIST_HEAD(&cam_res->gpio_res_list);
+	INIT_LIST_HEAD(&cam_res->flash_res_list);
+
+	return 0;
+}
+
+static int cam_res_mgr_remove(struct platform_device *pdev)
+{
+	if (cam_res) {
+		cam_res_mgr_free_res();
+		kfree(cam_res);
+		cam_res = NULL;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id cam_res_mgr_dt_match[] = {
+	{.compatible = "qcom,cam-res-mgr"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, cam_res_mgr_dt_match);
+
+static struct platform_driver cam_res_mgr_driver = {
+	.probe = cam_res_mgr_probe,
+	.remove = cam_res_mgr_remove,
+	.driver = {
+		.name = "cam_res_mgr",
+		.owner = THIS_MODULE,
+		.of_match_table = cam_res_mgr_dt_match,
+	},
+};
+
+static int __init cam_res_mgr_init(void)
+{
+	return platform_driver_register(&cam_res_mgr_driver);
+}
+
+static void __exit cam_res_mgr_exit(void)
+{
+	platform_driver_unregister(&cam_res_mgr_driver);
+}
+
+module_init(cam_res_mgr_init);
+module_exit(cam_res_mgr_exit);
+MODULE_DESCRIPTION("Camera resource manager driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h
new file mode 100644
index 0000000..7fb13ba
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_api.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CAM_RES_MGR_API_H__
+#define __CAM_RES_MGR_API_H__
+
+#include <linux/leds.h>
+
+/**
+ * @brief: Register the led trigger
+ *
+ *  The newly registered led trigger is assigned to flash_res_list.
+ *
+ * @name  : Pointer to int led trigger name
+ * @tp    : Save the returned led trigger
+ *
+ * @return None
+ */
+void cam_res_mgr_led_trigger_register(const char *name,
+	struct led_trigger **tp);
+
+/**
+ * @brief: Unregister the led trigger
+ *
+ *  Free the flash_res if this led trigger isn't used by other device .
+ *
+ * @tp : Pointer to the led trigger
+ *
+ * @return None
+ */
+void cam_res_mgr_led_trigger_unregister(struct led_trigger *tp);
+
+/**
+ * @brief: Trigger the event to led core
+ *
+ * @trig       : Pointer to the led trigger
+ * @brightness : The brightness need to fire
+ *
+ * @return None
+ */
+void cam_res_mgr_led_trigger_event(struct led_trigger *trig,
+	enum led_brightness brightness);
+
+/**
+ * @brief: Get the corresponding pinctrl of dev
+ *
+ *  Init the shared pinctrl if shared pinctrl enabled.
+ *
+ * @return None
+ */
+int cam_res_mgr_shared_pinctrl_init(void);
+
+/**
+ * @brief: Put the pinctrl
+ *
+ *  Put the shared pinctrl.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+void cam_res_mgr_shared_pinctrl_put(void);
+
+/**
+ * @brief: Select the corresponding state
+ *
+ *  Active state can be selected directly, but need hold to suspend the
+ *  pinctrl if the gpios in this pinctrl also held by other pinctrl.
+ *
+ * @active   : The flag to indicate whether active or suspend
+ * the shared pinctrl.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_res_mgr_shared_pinctrl_select_state(bool active);
+
+/**
+ * @brief: Post init shared pinctrl
+ *
+ *  Post init to check if the device really has shared gpio,
+ *  suspend and put the pinctrl if not use shared gpio.
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_res_mgr_shared_pinctrl_post_init(void);
+
+/**
+ * @brief: Request a gpio
+ *
+ *  Will alloc a gpio_res for the new gpio, other find the corresponding
+ *  gpio_res.
+ *
+ * @dev   : Pointer to the device
+ * @gpio  : The GPIO number
+ * @flags : GPIO configuration as specified by GPIOF_*
+ * @label : A literal description string of this GPIO
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_res_mgr_gpio_request(struct device *dev, unsigned int gpio,
+		unsigned long flags, const char *label);
+
+/**
+ * @brief: Free a array GPIO
+ *
+ *  Free the GPIOs and release corresponding gpio_res.
+ *
+ * @dev   : Pointer to the device
+ * @gpio  : Array of the GPIO number
+ * @num   : The number of gpio
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+void cam_res_mgr_gpio_free_arry(struct device *dev,
+	const struct gpio *array, size_t num);
+
+/**
+ * @brief: Set GPIO power level
+ *
+ *  Add ref count support for shared GPIOs.
+ *
+ * @gpio   : The GPIO number
+ * @value  : The power level need to setup
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ * -EINVAL will be returned if the gpio can't be found in gpio_res_list.
+ */
+int cam_res_mgr_gpio_set_value(unsigned int gpio, int value);
+
+/**
+ * @brief: Config the shared clk ref count
+ *
+ *  Config the shared clk ref count..
+ *
+ * @value   : get or put the shared clk.
+ *
+ * @return None
+ */
+void cam_res_mgr_shared_clk_config(bool value);
+
+#endif /* __CAM_RES_MGR_API_H__ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h
new file mode 100644
index 0000000..53a8778
--- /dev/null
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr/cam_res_mgr_private.h
@@ -0,0 +1,117 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CAM_RES_MGR_PRIVATE_H__
+#define __CAM_RES_MGR_PRIVATE_H__
+
+#include <linux/list.h>
+#include <linux/leds.h>
+#include "cam_soc_util.h"
+
+#define MAX_SHARED_GPIO_SIZE 16
+
+/* pinctrl states name */
+#define CAM_RES_MGR_SLEEP	"cam_res_mgr_suspend"
+#define CAM_RES_MGR_DEFAULT	"cam_res_mgr_default"
+
+/**
+ * enum pinctrl_status - Enum for pinctrl status
+ */
+enum pinctrl_status {
+	PINCTRL_STATUS_GOT = 0,
+	PINCTRL_STATUS_ACTIVE,
+	PINCTRL_STATUS_SUSPEND,
+	PINCTRL_STATUS_PUT,
+};
+
+/**
+ * struct cam_dev_res
+ *
+ * @list : List member used to append this node to a dev list
+ * @dev  : Device pointer associated with device
+ */
+struct cam_dev_res {
+	struct list_head list;
+	struct device    *dev;
+};
+
+/**
+ * struct cam_gpio_res
+ *
+ * @list           : List member used to append this node to a gpio list
+ * @dev_list       : List the device which request this gpio
+ * @gpio           : Gpio value
+ * @power_on_count : Record the power on times of this gpio
+ */
+struct cam_gpio_res {
+	struct list_head list;
+	struct list_head dev_list;
+	unsigned int     gpio;
+	int              power_on_count;
+};
+
+/**
+ * struct cam_pinctrl_res
+ *
+ * @list           : List member used to append this node to a linked list
+ * @name           : Pointer to the flash trigger's name.
+ * @trigger        : Pointer to the flash trigger
+ */
+struct cam_flash_res {
+	struct list_head   list;
+	const char         *name;
+	struct led_trigger *trigger;
+};
+
+/**
+ * struct cam_res_mgr_dt
+ *
+ * @shared_gpio     : Shared gpios list in the device tree
+ * @num_shared_gpio : The number of shared gpio
+ * @pinctrl_info    : Pinctrl information
+ */
+struct cam_res_mgr_dt {
+	uint                        shared_gpio[MAX_SHARED_GPIO_SIZE];
+	int                         num_shared_gpio;
+	struct cam_soc_pinctrl_info pinctrl_info;
+};
+
+/**
+ * struct cam_pinctrl_res
+ *
+ * @dev                 : Pointer to the device
+ * @dt                  : Device tree resource
+ * @shared_gpio_enabled : The flag to indicate if support shared gpio
+ * @pstatus             : Shared pinctrl status
+ * @gpio_res_list       : List head of the gpio resource
+ * @flash_res_list      : List head of the flash resource
+ * @gpio_res_lock       : GPIO resource lock
+ * @flash_res_lock      : Flash resource lock
+ * @clk_res_lock        : Clk resource lock
+ */
+struct cam_res_mgr {
+	struct device         *dev;
+	struct cam_res_mgr_dt dt;
+
+	bool                  shared_gpio_enabled;
+	enum pinctrl_status   pstatus;
+
+	uint                  shared_clk_ref_count;
+
+	struct list_head      gpio_res_list;
+	struct list_head      flash_res_list;
+	struct mutex          gpio_res_lock;
+	struct mutex          flash_res_lock;
+	struct mutex          clk_res_lock;
+};
+
+#endif /* __CAM_RES_MGR_PRIVATE_H__ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
index edbb335..97158e4 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.c
@@ -17,6 +17,82 @@
 #include "cam_soc_util.h"
 #include "cam_trace.h"
 
+
+static void cam_sensor_update_req_mgr(
+	struct cam_sensor_ctrl_t *s_ctrl,
+	struct cam_packet *csl_packet)
+{
+	struct cam_req_mgr_add_request add_req;
+
+	add_req.link_hdl = s_ctrl->bridge_intf.link_hdl;
+	add_req.req_id = csl_packet->header.request_id;
+	CAM_DBG(CAM_SENSOR, " Rxed Req Id: %lld",
+		csl_packet->header.request_id);
+	add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl;
+	add_req.skip_before_applying = 0;
+	if (s_ctrl->bridge_intf.crm_cb &&
+		s_ctrl->bridge_intf.crm_cb->add_req)
+		s_ctrl->bridge_intf.crm_cb->add_req(&add_req);
+
+	CAM_DBG(CAM_SENSOR, " add req to req mgr: %lld",
+			add_req.req_id);
+}
+
+static void cam_sensor_release_resource(
+	struct cam_sensor_ctrl_t *s_ctrl)
+{
+	struct i2c_settings_array *i2c_set = NULL;
+	int i, rc;
+
+	i2c_set = &(s_ctrl->i2c_data.init_settings);
+	if (i2c_set->is_settings_valid == 1) {
+		i2c_set->is_settings_valid = -1;
+		rc = delete_request(i2c_set);
+		if (rc < 0)
+			CAM_ERR(CAM_SENSOR,
+				"failed while deleting Init settings");
+	}
+
+	i2c_set = &(s_ctrl->i2c_data.config_settings);
+	if (i2c_set->is_settings_valid == 1) {
+		i2c_set->is_settings_valid = -1;
+		rc = delete_request(i2c_set);
+		if (rc < 0)
+			CAM_ERR(CAM_SENSOR,
+				"failed while deleting Res settings");
+	}
+	i2c_set = &(s_ctrl->i2c_data.streamoff_settings);
+	if (i2c_set->is_settings_valid == 1) {
+		i2c_set->is_settings_valid = -1;
+		rc = delete_request(i2c_set);
+		if (rc < 0)
+			CAM_ERR(CAM_SENSOR,
+				"failed while deleting Streamoff settings");
+	}
+	i2c_set = &(s_ctrl->i2c_data.streamon_settings);
+	if (i2c_set->is_settings_valid == 1) {
+		i2c_set->is_settings_valid = -1;
+		rc = delete_request(i2c_set);
+		if (rc < 0)
+			CAM_ERR(CAM_SENSOR,
+				"failed while deleting Streamoff settings");
+	}
+	if (s_ctrl->i2c_data.per_frame != NULL) {
+		for (i = 0; i < MAX_PER_FRAME_ARRAY; i++) {
+			i2c_set = &(s_ctrl->i2c_data.per_frame[i]);
+
+			if (i2c_set->is_settings_valid == 1) {
+				i2c_set->is_settings_valid = -1;
+				rc = delete_request(i2c_set);
+				if (rc < 0)
+					CAM_ERR(CAM_SENSOR,
+						"delete request: %lld rc: %d",
+						i2c_set->request_id, rc);
+			}
+		}
+	}
+}
+
 static int32_t cam_sensor_i2c_pkt_parse(struct cam_sensor_ctrl_t *s_ctrl,
 	void *arg)
 {
@@ -30,7 +106,6 @@
 	uint32_t *offset = NULL;
 	struct cam_config_dev_cmd config;
 	struct i2c_data_settings *i2c_data = NULL;
-	struct cam_req_mgr_add_request add_req;
 
 	ioctl_ctrl = (struct cam_control *)arg;
 
@@ -63,13 +138,33 @@
 
 	i2c_data = &(s_ctrl->i2c_data);
 	CAM_DBG(CAM_SENSOR, "Header OpCode: %d", csl_packet->header.op_code);
-	if ((csl_packet->header.op_code & 0xFFFFFF) ==
-		CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG) {
+	switch (csl_packet->header.op_code & 0xFFFFFF) {
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: {
 		i2c_reg_settings = &i2c_data->init_settings;
 		i2c_reg_settings->request_id = 0;
 		i2c_reg_settings->is_settings_valid = 1;
-	} else if ((csl_packet->header.op_code & 0xFFFFFF) ==
-		CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) {
+		break;
+	}
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG: {
+		i2c_reg_settings = &i2c_data->config_settings;
+		i2c_reg_settings->request_id = 0;
+		i2c_reg_settings->is_settings_valid = 1;
+		break;
+	}
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: {
+		i2c_reg_settings = &i2c_data->streamon_settings;
+		i2c_reg_settings->request_id = 0;
+		i2c_reg_settings->is_settings_valid = 1;
+		break;
+	}
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: {
+		i2c_reg_settings = &i2c_data->streamoff_settings;
+		i2c_reg_settings->request_id = 0;
+		i2c_reg_settings->is_settings_valid = 1;
+		break;
+	}
+
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE: {
 		i2c_reg_settings =
 			&i2c_data->
 			per_frame[csl_packet->header.request_id %
@@ -87,14 +182,13 @@
 				return rc;
 			}
 		}
-
-		i2c_reg_settings->request_id =
-			csl_packet->header.request_id;
-		i2c_reg_settings->is_settings_valid = 1;
-	} else if ((csl_packet->header.op_code & 0xFFFFFF) ==
-		CAM_PKT_NOP_OPCODE) {
-		goto update_req_mgr;
-	} else {
+	break;
+	}
+	case CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP: {
+		cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+		return rc;
+	}
+	default:
 		CAM_ERR(CAM_SENSOR, "Invalid Packet Header");
 		return -EINVAL;
 	}
@@ -103,27 +197,19 @@
 	offset += csl_packet->cmd_buf_offset / 4;
 	cmd_desc = (struct cam_cmd_buf_desc *)(offset);
 
-	rc = cam_sensor_i2c_pkt_parser(i2c_reg_settings, cmd_desc, 1);
+	rc = cam_sensor_i2c_command_parser(i2c_reg_settings, cmd_desc, 1);
 	if (rc < 0) {
 		CAM_ERR(CAM_SENSOR, "Fail parsing I2C Pkt: %d", rc);
 		return rc;
 	}
 
-update_req_mgr:
-	if (((csl_packet->header.op_code & 0xFFFFFF) ==
-		CAM_PKT_NOP_OPCODE) || (csl_packet->header.op_code ==
-		CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE)) {
-		add_req.link_hdl = s_ctrl->bridge_intf.link_hdl;
-		add_req.req_id = csl_packet->header.request_id;
-		CAM_DBG(CAM_SENSOR, " Rxed Req Id: %lld",
-			 csl_packet->header.request_id);
-		add_req.dev_hdl = s_ctrl->bridge_intf.device_hdl;
-		if (s_ctrl->bridge_intf.crm_cb &&
-			s_ctrl->bridge_intf.crm_cb->add_req)
-			s_ctrl->bridge_intf.crm_cb->add_req(&add_req);
-		CAM_DBG(CAM_SENSOR, " add req to req mgr: %lld",
-			add_req.req_id);
+	if ((csl_packet->header.op_code & 0xFFFFFF) ==
+		CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE) {
+		i2c_reg_settings->request_id =
+			csl_packet->header.request_id;
+		cam_sensor_update_req_mgr(s_ctrl, csl_packet);
 	}
+
 	return rc;
 }
 
@@ -214,6 +300,8 @@
 			i2c_info->i2c_freq_mode);
 	}
 
+	s_ctrl->sensordata->slave_info.sensor_slave_addr =
+		i2c_info->slave_addr;
 	return rc;
 }
 
@@ -376,6 +464,38 @@
 	return sensor_id;
 }
 
+void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl)
+{
+	struct cam_sensor_power_ctrl_t *power_info =
+		&s_ctrl->sensordata->power_info;
+	int rc = 0;
+
+	s_ctrl->is_probe_succeed = 0;
+	if (s_ctrl->sensor_state == CAM_SENSOR_INIT)
+		return;
+
+	cam_sensor_release_resource(s_ctrl);
+
+	if ((s_ctrl->sensor_state == CAM_SENSOR_START) ||
+		(s_ctrl->sensor_state == CAM_SENSOR_ACQUIRE)) {
+		cam_sensor_power_down(s_ctrl);
+		rc = cam_destroy_device_hdl(s_ctrl->bridge_intf.device_hdl);
+		if (rc < 0)
+			CAM_ERR(CAM_SENSOR, " failed destroying dhdl");
+		s_ctrl->bridge_intf.device_hdl = -1;
+		s_ctrl->bridge_intf.link_hdl = -1;
+		s_ctrl->bridge_intf.session_hdl = -1;
+		s_ctrl->sensor_state = CAM_SENSOR_PROBE;
+	}
+
+	if (s_ctrl->sensor_state == CAM_SENSOR_PROBE) {
+		kfree(power_info->power_setting);
+		kfree(power_info->power_down_setting);
+	}
+
+	s_ctrl->sensor_state = CAM_SENSOR_INIT;
+}
+
 int cam_sensor_match_id(struct cam_sensor_ctrl_t *s_ctrl)
 {
 	int rc = 0;
@@ -510,8 +630,12 @@
 			goto release_mutex;
 		}
 
-		CAM_DBG(CAM_SENSOR, "Probe Succeeded on the slot: %d",
-			s_ctrl->soc_info.index);
+		CAM_INFO(CAM_SENSOR,
+			"Probe Succees,slot:%d,slave_addr:0x%x,sensor_id:0x%x",
+			s_ctrl->soc_info.index,
+			s_ctrl->sensordata->slave_info.sensor_slave_addr,
+			s_ctrl->sensordata->slave_info.sensor_id);
+
 		rc = cam_sensor_power_down(s_ctrl);
 		if (rc < 0) {
 			CAM_ERR(CAM_SENSOR, "fail in Sensor Power Down");
@@ -524,6 +648,7 @@
 		 * probed on this slot
 		 */
 		s_ctrl->is_probe_succeed = 1;
+		s_ctrl->sensor_state = CAM_SENSOR_INIT;
 	}
 		break;
 	case CAM_ACQUIRE_DEV: {
@@ -561,9 +686,32 @@
 			rc = -EFAULT;
 			goto release_mutex;
 		}
+
+		rc = cam_sensor_power_up(s_ctrl);
+		if (rc < 0) {
+			CAM_ERR(CAM_SENSOR, "Sensor Power up failed");
+			goto release_mutex;
+		}
+
+		s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
 	}
 		break;
 	case CAM_RELEASE_DEV: {
+		if (s_ctrl->sensor_state != CAM_SENSOR_ACQUIRE) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_SENSOR,
+			"Not in right state to release : %d",
+			s_ctrl->sensor_state);
+			goto release_mutex;
+		}
+
+		rc = cam_sensor_power_down(s_ctrl);
+		if (rc < 0) {
+			CAM_ERR(CAM_SENSOR, "Sensor Power Down failed");
+			goto release_mutex;
+		}
+
+		cam_sensor_release_resource(s_ctrl);
 		if (s_ctrl->bridge_intf.device_hdl == -1) {
 			CAM_ERR(CAM_SENSOR,
 				"Invalid Handles: link hdl: %d device hdl: %d",
@@ -579,6 +727,8 @@
 		s_ctrl->bridge_intf.device_hdl = -1;
 		s_ctrl->bridge_intf.link_hdl = -1;
 		s_ctrl->bridge_intf.session_hdl = -1;
+
+		s_ctrl->sensor_state = CAM_SENSOR_INIT;
 	}
 		break;
 	case CAM_QUERY_CAP: {
@@ -594,31 +744,46 @@
 		break;
 	}
 	case CAM_START_DEV: {
-		rc = cam_sensor_power_up(s_ctrl);
-		if (rc < 0) {
-			CAM_ERR(CAM_SENSOR, "Sensor Power up failed");
-			goto release_mutex;
-		}
-		rc = cam_sensor_apply_settings(s_ctrl, 0);
-		if (rc < 0) {
-			CAM_ERR(CAM_SENSOR, "cannot apply settings");
-			goto release_mutex;
-		}
-		rc = delete_request(&s_ctrl->i2c_data.init_settings);
-		if (rc < 0) {
-			CAM_ERR(CAM_SENSOR,
-				"Fail in deleting the Init settings");
+		if (s_ctrl->sensor_state != CAM_SENSOR_ACQUIRE) {
 			rc = -EINVAL;
+			CAM_WARN(CAM_SENSOR,
+			"Not in right state to start : %d",
+			s_ctrl->sensor_state);
 			goto release_mutex;
 		}
+
+		if (s_ctrl->i2c_data.streamon_settings.is_settings_valid &&
+			(s_ctrl->i2c_data.streamon_settings.request_id == 0)) {
+			rc = cam_sensor_apply_settings(s_ctrl, 0,
+				CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"cannot apply streamon settings");
+				goto release_mutex;
+			}
+		}
+		s_ctrl->sensor_state = CAM_SENSOR_START;
 	}
 		break;
 	case CAM_STOP_DEV: {
-		rc = cam_sensor_power_down(s_ctrl);
-		if (rc < 0) {
-			CAM_ERR(CAM_SENSOR, "Sensor Power Down failed");
+		if (s_ctrl->sensor_state != CAM_SENSOR_START) {
+			rc = -EINVAL;
+			CAM_WARN(CAM_SENSOR,
+			"Not in right state to stop : %d",
+			s_ctrl->sensor_state);
 			goto release_mutex;
 		}
+
+		if (s_ctrl->i2c_data.streamoff_settings.is_settings_valid &&
+			(s_ctrl->i2c_data.streamoff_settings.request_id == 0)) {
+			rc = cam_sensor_apply_settings(s_ctrl, 0,
+				CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+				"cannot apply streamoff settings");
+			}
+		}
+		s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
 	}
 		break;
 	case CAM_CONFIG_DEV: {
@@ -627,10 +792,44 @@
 			CAM_ERR(CAM_SENSOR, "Failed CCI Config: %d", rc);
 			goto release_mutex;
 		}
+		if (s_ctrl->i2c_data.init_settings.is_settings_valid &&
+			(s_ctrl->i2c_data.init_settings.request_id == 0)) {
+
+			rc = cam_sensor_apply_settings(s_ctrl, 0,
+				CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"cannot apply init settings");
+				goto release_mutex;
+			}
+			rc = delete_request(&s_ctrl->i2c_data.init_settings);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"Fail in deleting the Init settings");
+				goto release_mutex;
+			}
+			s_ctrl->i2c_data.init_settings.request_id = -1;
+		}
+
+		if (s_ctrl->i2c_data.config_settings.is_settings_valid &&
+			(s_ctrl->i2c_data.config_settings.request_id == 0)) {
+			rc = cam_sensor_apply_settings(s_ctrl, 0,
+				CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"cannot apply config settings");
+				goto release_mutex;
+			}
+			rc = delete_request(&s_ctrl->i2c_data.config_settings);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"Fail in deleting the config settings");
+				goto release_mutex;
+			}
+			s_ctrl->i2c_data.config_settings.request_id = -1;
+		}
 	}
 		break;
-	case CAM_SD_SHUTDOWN:
-		break;
 	default:
 		CAM_ERR(CAM_SENSOR, "Invalid Opcode: %d", cmd->op_code);
 		rc = -EINVAL;
@@ -686,9 +885,9 @@
 	struct cam_sensor_ctrl_t *s_ctrl = v4l2_get_subdevdata(sd);
 
 	mutex_lock(&(s_ctrl->cam_sensor_mutex));
-	if (!on && s_ctrl->sensor_state == CAM_SENSOR_POWER_UP) {
+	if (!on && s_ctrl->sensor_state == CAM_SENSOR_START) {
 		cam_sensor_power_down(s_ctrl);
-		s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
+		s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
 	}
 	mutex_unlock(&(s_ctrl->cam_sensor_mutex));
 
@@ -722,15 +921,9 @@
 		return rc;
 	}
 
-	if (s_ctrl->io_master_info.master_type == CCI_MASTER) {
-		rc = camera_io_init(&(s_ctrl->io_master_info));
-		if (rc < 0) {
-			CAM_ERR(CAM_SENSOR, "cci_init failed");
-			return -EINVAL;
-		}
-	}
-
-	s_ctrl->sensor_state = CAM_SENSOR_POWER_UP;
+	rc = camera_io_init(&(s_ctrl->io_master_info));
+	if (rc < 0)
+		CAM_ERR(CAM_SENSOR, "cci_init failed: rc: %d", rc);
 
 	return rc;
 }
@@ -759,23 +952,41 @@
 		return rc;
 	}
 
-	if (s_ctrl->io_master_info.master_type == CCI_MASTER)
-		camera_io_release(&(s_ctrl->io_master_info));
-
-	s_ctrl->sensor_state = CAM_SENSOR_POWER_DOWN;
+	camera_io_release(&(s_ctrl->io_master_info));
 
 	return rc;
 }
 
 int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl,
-	int64_t req_id)
+	int64_t req_id, enum cam_sensor_packet_opcodes opcode)
 {
 	int rc = 0, offset, del_req_id;
 	struct i2c_settings_array *i2c_set = NULL;
 	struct i2c_settings_list *i2c_list;
 
 	if (req_id == 0) {
-		i2c_set = &s_ctrl->i2c_data.init_settings;
+		switch (opcode) {
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON: {
+			i2c_set = &s_ctrl->i2c_data.streamon_settings;
+			break;
+		}
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG: {
+			i2c_set = &s_ctrl->i2c_data.init_settings;
+			break;
+		}
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG: {
+			i2c_set = &s_ctrl->i2c_data.config_settings;
+			break;
+		}
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF: {
+			i2c_set = &s_ctrl->i2c_data.streamoff_settings;
+			break;
+		}
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE:
+		case CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE:
+		default:
+			return 0;
+		}
 		if (i2c_set->is_settings_valid == 1) {
 			list_for_each_entry(i2c_list,
 				&(i2c_set->list_head), list) {
@@ -789,12 +1000,7 @@
 					return rc;
 				}
 			}
-			rc = delete_request(&(s_ctrl->i2c_data.init_settings));
 			i2c_set->is_settings_valid = 0;
-			if (rc < 0) {
-				CAM_ERR(CAM_SENSOR,
-				"Failed in deleting the Init request: %d", rc);
-			}
 		}
 	} else {
 		offset = req_id % MAX_PER_FRAME_ARRAY;
@@ -813,30 +1019,29 @@
 					return rc;
 				}
 			}
-			del_req_id = (req_id +
-				MAX_PER_FRAME_ARRAY -
-				MAX_SYSTEM_PIPELINE_DELAY) %
-				MAX_PER_FRAME_ARRAY;
-			CAM_DBG(CAM_SENSOR, "Deleting the Request: %d",
-				del_req_id);
-			if (req_id >
-				s_ctrl->i2c_data.per_frame[del_req_id].
-				request_id) {
-				s_ctrl->i2c_data.per_frame[del_req_id].
-					request_id = 0;
-				rc = delete_request(
-					&(s_ctrl->i2c_data.
-					per_frame[del_req_id]));
-				if (rc < 0)
-					CAM_ERR(CAM_SENSOR,
-						"Delete request Fail:%d rc:%d",
-						del_req_id, rc);
-			}
 		} else {
 			CAM_DBG(CAM_SENSOR,
 				"Invalid/NOP request to apply: %lld", req_id);
 		}
+
+		del_req_id = (req_id + MAX_PER_FRAME_ARRAY -
+			MAX_SYSTEM_PIPELINE_DELAY) % MAX_PER_FRAME_ARRAY;
+		CAM_DBG(CAM_SENSOR, "Deleting the Request: %d", del_req_id);
+
+		if ((req_id >
+			 s_ctrl->i2c_data.per_frame[del_req_id].request_id) &&
+			(s_ctrl->i2c_data.per_frame[del_req_id].
+				is_settings_valid == 1)) {
+			s_ctrl->i2c_data.per_frame[del_req_id].request_id = 0;
+			rc = delete_request(
+				&(s_ctrl->i2c_data.per_frame[del_req_id]));
+			if (rc < 0)
+				CAM_ERR(CAM_SENSOR,
+					"Delete request Fail:%d rc:%d",
+					del_req_id, rc);
+		}
 	}
+
 	return rc;
 }
 
@@ -855,8 +1060,9 @@
 		return -EINVAL;
 	}
 	CAM_DBG(CAM_SENSOR, " Req Id: %lld", apply->request_id);
-	trace_cam_apply_req("Sensor", apply);
-	rc = cam_sensor_apply_settings(s_ctrl, apply->request_id);
+	trace_cam_apply_req("Sensor", apply->request_id);
+	rc = cam_sensor_apply_settings(s_ctrl, apply->request_id,
+		CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE);
 	return rc;
 }
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
index c8158fa..4570179 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_core.h
@@ -40,10 +40,12 @@
 /**
  * @s_ctrl: Sensor ctrl structure
  * @req_id: Request id
+ * @opcode: opcode for settings
  *
  * This API applies the req_id settings to sensor
  */
-int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id);
+int cam_sensor_apply_settings(struct cam_sensor_ctrl_t *s_ctrl, int64_t req_id,
+	enum cam_sensor_packet_opcodes opcode);
 
 /**
  * @apply: Req mgr structure for applying request
@@ -81,4 +83,11 @@
  */
 int32_t cam_sensor_driver_cmd(struct cam_sensor_ctrl_t *s_ctrl, void *arg);
 
+/**
+ * @s_ctrl: Sensor ctrl structure
+ *
+ * This API handles the camera sensor close/shutdown
+ */
+void cam_sensor_shutdown(struct cam_sensor_ctrl_t *s_ctrl);
+
 #endif /* _CAM_SENSOR_CORE_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
index 122aa3e..8ea767f 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.c
@@ -34,6 +34,24 @@
 	return rc;
 }
 
+static int cam_sensor_subdev_close(struct v4l2_subdev *sd,
+	struct v4l2_subdev_fh *fh)
+{
+	struct cam_sensor_ctrl_t *s_ctrl =
+		v4l2_get_subdevdata(sd);
+
+	if (!s_ctrl) {
+		CAM_ERR(CAM_SENSOR, "s_ctrl ptr is NULL");
+		return -EINVAL;
+	}
+
+	mutex_lock(&(s_ctrl->cam_sensor_mutex));
+	cam_sensor_shutdown(s_ctrl);
+	mutex_unlock(&(s_ctrl->cam_sensor_mutex));
+
+	return 0;
+}
+
 #ifdef CONFIG_COMPAT
 static long cam_sensor_init_subdev_do_ioctl(struct v4l2_subdev *sd,
 	unsigned int cmd, unsigned long arg)
@@ -85,7 +103,9 @@
 	.core = &cam_sensor_subdev_core_ops,
 };
 
-static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops;
+static const struct v4l2_subdev_internal_ops cam_sensor_internal_ops = {
+	.close = cam_sensor_subdev_close,
+};
 
 static int cam_sensor_init_subdev_params(struct cam_sensor_ctrl_t *s_ctrl)
 {
@@ -163,6 +183,9 @@
 	}
 
 	INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.config_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head));
 
 	for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
 		INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head));
@@ -269,6 +292,9 @@
 	}
 
 	INIT_LIST_HEAD(&(s_ctrl->i2c_data.init_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.config_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamon_settings.list_head));
+	INIT_LIST_HEAD(&(s_ctrl->i2c_data.streamoff_settings.list_head));
 
 	for (i = 0; i < MAX_PER_FRAME_ARRAY; i++)
 		INIT_LIST_HEAD(&(s_ctrl->i2c_data.per_frame[i].list_head));
@@ -283,6 +309,8 @@
 	platform_set_drvdata(pdev, s_ctrl);
 	v4l2_set_subdevdata(&(s_ctrl->v4l2_dev_str.sd), s_ctrl);
 
+	s_ctrl->sensor_state = CAM_SENSOR_INIT;
+
 	return rc;
 unreg_subdev:
 	cam_unregister_subdev(&(s_ctrl->v4l2_dev_str));
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
index f3c70c4..8c49837 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor/cam_sensor_dev.h
@@ -50,8 +50,10 @@
 #define CAMX_SENSOR_DEV_NAME "cam-sensor-driver"
 
 enum cam_sensor_state_t {
-	CAM_SENSOR_POWER_DOWN,
-	CAM_SENSOR_POWER_UP,
+	CAM_SENSOR_INIT,
+	CAM_SENSOR_PROBE,
+	CAM_SENSOR_ACQUIRE,
+	CAM_SENSOR_START,
 };
 
 /**
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
index 3257703..2c1f520 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_cci_i2c.c
@@ -38,7 +38,7 @@
 	rc = v4l2_subdev_call(cci_client->cci_subdev,
 		core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl);
 	if (rc < 0) {
-		CAM_ERR(CAM_SENSOR, "line %d rc = %d", rc);
+		CAM_ERR(CAM_SENSOR, "rc = %d", rc);
 		return rc;
 	}
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
index c9cf088..8eb04ec 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_io.c
@@ -58,7 +58,7 @@
 			addr, data, addr_type, data_type);
 	} else if (io_master_info->master_type == SPI_MASTER) {
 		return cam_spi_read(io_master_info,
-			addr, data, addr_type);
+			addr, data, addr_type, data_type);
 	} else {
 		CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
 			io_master_info->master_type);
@@ -78,8 +78,8 @@
 		return cam_qup_i2c_read_seq(io_master_info->client,
 			addr, data, addr_type, num_bytes);
 	} else if (io_master_info->master_type == SPI_MASTER) {
-		return cam_spi_read(io_master_info,
-			addr, (uint32_t *)data, addr_type);
+		return cam_spi_read_seq(io_master_info,
+			addr, data, addr_type, num_bytes);
 	} else {
 		CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
 			io_master_info->master_type);
@@ -153,6 +153,9 @@
 			cam_cci_get_subdev();
 		return cam_sensor_cci_i2c_util(io_master_info->cci_client,
 			MSM_CCI_INIT);
+	} else if ((io_master_info->master_type == I2C_MASTER) ||
+			(io_master_info->master_type == SPI_MASTER)) {
+		return 0;
 	} else {
 		CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
 			io_master_info->master_type);
@@ -170,6 +173,9 @@
 	if (io_master_info->master_type == CCI_MASTER) {
 		return cam_sensor_cci_i2c_util(io_master_info->cci_client,
 			MSM_CCI_RELEASE);
+	} else if ((io_master_info->master_type == I2C_MASTER) ||
+			(io_master_info->master_type == SPI_MASTER)) {
+		return 0;
 	} else {
 		CAM_ERR(CAM_SENSOR, "Invalid Comm. Master:%d",
 			io_master_info->master_type);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c
index 4011aa0..131b0ae 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.c
@@ -59,25 +59,27 @@
  * instruction and the data length.
  */
 static void cam_set_addr(uint32_t addr, uint8_t addr_len,
-	enum camera_sensor_i2c_type type,
+	enum camera_sensor_i2c_type addr_type,
 	char *str)
 {
-	int i, len;
-
 	if (!addr_len)
 		return;
 
-	if (addr_len < type)
-		CAM_DBG(CAM_EEPROM, "omitting higher bits in address");
-
-	/* only support transfer MSB first for now */
-	len = addr_len - type;
-	for (i = len; i < addr_len; i++) {
-		if (i >= 0)
-			str[i] = (addr >> (BITS_PER_BYTE * (addr_len - i - 1)))
-				& 0xFF;
+	if (addr_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
+		str[0] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
+		str[0] = addr >> 8;
+		str[1] = addr;
+	} else if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		str[0] = addr >> 16;
+		str[1] = addr >> 8;
+		str[2] = addr;
+	} else {
+		str[0] = addr >> 24;
+		str[1] = addr >> 16;
+		str[2] = addr >> 8;
+		str[3] = addr;
 	}
-
 }
 
 /**
@@ -105,6 +107,7 @@
  */
 static int32_t cam_spi_tx_helper(struct camera_io_master *client,
 	struct cam_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
 	uint32_t num_byte, char *tx, char *rx)
 {
 	int32_t rc = -EINVAL;
@@ -112,10 +115,6 @@
 	char *ctx = NULL, *crx = NULL;
 	uint32_t len, hlen;
 	uint8_t retries = client->spi_client->retries;
-	enum camera_sensor_i2c_type addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
-
-	if (addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
-		return rc;
 
 	hlen = cam_camera_spi_get_hlen(inst);
 	len = hlen + num_byte;
@@ -166,6 +165,7 @@
 
 static int32_t cam_spi_tx_read(struct camera_io_master *client,
 	struct cam_camera_spi_inst *inst, uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
 	uint32_t num_byte, char *tx, char *rx)
 {
 	int32_t rc = -EINVAL;
@@ -173,12 +173,6 @@
 	char *ctx = NULL, *crx = NULL;
 	uint32_t hlen;
 	uint8_t retries = client->spi_client->retries;
-	enum camera_sensor_i2c_type addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
-
-	if ((addr_type != CAMERA_SENSOR_I2C_TYPE_WORD)
-		&& (addr_type != CAMERA_SENSOR_I2C_TYPE_BYTE)
-		&& (addr_type != CAMERA_SENSOR_I2C_TYPE_3B))
-		return rc;
 
 	hlen = cam_camera_spi_get_hlen(inst);
 	if (tx) {
@@ -204,14 +198,8 @@
 	}
 
 	ctx[0] = inst->opcode;
-	if (addr_type == CAMERA_SENSOR_I2C_TYPE_3B) {
-		cam_set_addr(addr, inst->addr_len, addr_type,
-			ctx + 1);
-	} else {
-		ctx[1] = (addr >> BITS_PER_BYTE) & 0xFF;
-		ctx[2] = (addr & 0xFF);
-		ctx[3] = 0;
-	}
+	cam_set_addr(addr, inst->addr_len, addr_type, ctx + 1);
+
 	CAM_DBG(CAM_EEPROM, "tx(%u): %02x %02x %02x %02x", hlen, ctx[0],
 		ctx[1], ctx[2],	ctx[3]);
 	while ((rc = cam_spi_txfr_read(spi, ctx, crx, hlen, num_byte))
@@ -235,18 +223,23 @@
 
 int cam_spi_read(struct camera_io_master *client,
 	uint32_t addr, uint32_t *data,
+	enum camera_sensor_i2c_type addr_type,
 	enum camera_sensor_i2c_type data_type)
 {
 	int rc = -EINVAL;
 	uint8_t temp[CAMERA_SENSOR_I2C_TYPE_MAX];
 
-	if ((data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
-		|| (data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))
+	if (addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
+		|| data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID
+		|| data_type >= CAMERA_SENSOR_I2C_TYPE_MAX) {
+		CAM_ERR(CAM_SENSOR, "Failed with addr/data_type verification");
 		return rc;
+	}
 
 	rc = cam_spi_tx_read(client,
 		&client->spi_client->cmd_tbl.read, addr, &temp[0],
-		data_type, NULL, NULL);
+		addr_type, data_type, NULL, NULL);
 	if (rc < 0) {
 		CAM_ERR(CAM_SENSOR, "failed %d", rc);
 		return rc;
@@ -254,23 +247,50 @@
 
 	if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE)
 		*data = temp[0];
-	else
+	else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD)
 		*data = (temp[0] << BITS_PER_BYTE) | temp[1];
+	else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B)
+		*data = (temp[0] << 16 | temp[1] << 8 | temp[2]);
+	else
+		*data = (temp[0] << 24 | temp[1] << 16 | temp[2] << 8 |
+			temp[3]);
 
 	CAM_DBG(CAM_SENSOR, "addr 0x%x, data %u", addr, *data);
 	return rc;
 }
 
+int32_t cam_spi_read_seq(struct camera_io_master *client,
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type, int32_t num_bytes)
+{
+	if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
+		|| (addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)) {
+		CAM_ERR(CAM_SENSOR, "Failed with addr_type verification");
+		return -EINVAL;
+	}
+
+	if (num_bytes == 0) {
+		CAM_ERR(CAM_SENSOR, "num_byte: 0x%x", num_bytes);
+		return -EINVAL;
+	}
+
+	return cam_spi_tx_helper(client,
+		&client->spi_client->cmd_tbl.read_seq, addr, data,
+		addr_type, num_bytes, NULL, NULL);
+}
+
 int cam_spi_query_id(struct camera_io_master *client,
-	uint32_t addr, uint8_t *data, uint32_t num_byte)
+	uint32_t addr, enum camera_sensor_i2c_type addr_type,
+	uint8_t *data, uint32_t num_byte)
 {
 	return cam_spi_tx_helper(client,
-		&client->spi_client->cmd_tbl.query_id, addr, data, num_byte,
-		NULL, NULL);
+		&client->spi_client->cmd_tbl.query_id,
+		addr, data, addr_type, num_byte, NULL, NULL);
 }
 
 static int32_t cam_spi_read_status_reg(
-	struct camera_io_master *client, uint8_t *status)
+	struct camera_io_master *client, uint8_t *status,
+	enum camera_sensor_i2c_type addr_type)
 {
 	struct cam_camera_spi_inst *rs =
 		&client->spi_client->cmd_tbl.read_status;
@@ -279,16 +299,17 @@
 		CAM_ERR(CAM_SENSOR, "not implemented yet");
 		return -ENXIO;
 	}
-	return cam_spi_tx_helper(client, rs, 0, status, 1, NULL, NULL);
+	return cam_spi_tx_helper(client, rs, 0, status,
+		addr_type, 1, NULL, NULL);
 }
 
 static int32_t cam_spi_device_busy(struct camera_io_master *client,
-	uint8_t *busy)
+	uint8_t *busy, enum camera_sensor_i2c_type addr_type)
 {
 	int rc;
 	uint8_t st = 0;
 
-	rc = cam_spi_read_status_reg(client,  &st);
+	rc = cam_spi_read_status_reg(client, &st, addr_type);
 	if (rc < 0) {
 		CAM_ERR(CAM_SENSOR, "failed to read status reg");
 		return rc;
@@ -298,14 +319,15 @@
 }
 
 static int32_t cam_spi_wait(struct camera_io_master *client,
-	struct cam_camera_spi_inst *inst)
+	struct cam_camera_spi_inst *inst,
+	enum camera_sensor_i2c_type addr_type)
 {
 	uint8_t busy;
 	int i, rc;
 
 	CAM_DBG(CAM_SENSOR, "op 0x%x wait start", inst->opcode);
 	for (i = 0; i < inst->delay_count; i++) {
-		rc = cam_spi_device_busy(client, &busy);
+		rc = cam_spi_device_busy(client, &busy, addr_type);
 		if (rc < 0)
 			return rc;
 		if (!busy)
@@ -321,8 +343,8 @@
 	return 0;
 }
 
-static int32_t cam_spi_write_enable(
-	struct camera_io_master *client)
+static int32_t cam_spi_write_enable(struct camera_io_master *client,
+	enum camera_sensor_i2c_type addr_type)
 {
 	struct cam_camera_spi_inst *we =
 		&client->spi_client->cmd_tbl.write_enable;
@@ -334,7 +356,8 @@
 		CAM_ERR(CAM_SENSOR, "not implemented yet");
 		return -EINVAL;
 	}
-	rc = cam_spi_tx_helper(client, we, 0, NULL, 0, NULL, NULL);
+	rc = cam_spi_tx_helper(client, we, 0, NULL, addr_type,
+		0, NULL, NULL);
 	if (rc < 0)
 		CAM_ERR(CAM_SENSOR, "write enable failed");
 	return rc;
@@ -354,7 +377,9 @@
  * used outside cam_spi_write_seq().
  */
 static int32_t cam_spi_page_program(struct camera_io_master *client,
-	uint32_t addr, uint8_t *data, uint16_t len, uint8_t *tx)
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	uint16_t len, uint8_t *tx)
 {
 	int rc;
 	struct cam_camera_spi_inst *pg =
@@ -362,10 +387,9 @@
 	struct spi_device *spi = client->spi_client->spi_master;
 	uint8_t retries = client->spi_client->retries;
 	uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
-	enum camera_sensor_i2c_type addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
 
 	CAM_DBG(CAM_SENSOR, "addr 0x%x, size 0x%x", addr, len);
-	rc = cam_spi_write_enable(client);
+	rc = cam_spi_write_enable(client, addr_type);
 	if (rc < 0)
 		return rc;
 	memset(tx, 0, header_len);
@@ -375,7 +399,7 @@
 	CAM_DBG(CAM_SENSOR, "tx(%u): %02x %02x %02x %02x",
 		len, tx[0], tx[1], tx[2], tx[3]);
 	while ((rc = spi_write(spi, tx, len + header_len)) && retries) {
-		rc = cam_spi_wait(client, pg);
+		rc = cam_spi_wait(client, pg, addr_type);
 		msleep(client->spi_client->retry_delay);
 		retries--;
 	}
@@ -383,41 +407,54 @@
 		CAM_ERR(CAM_SENSOR, "failed %d", rc);
 		return rc;
 	}
-	rc = cam_spi_wait(client, pg);
+	rc = cam_spi_wait(client, pg, addr_type);
 		return rc;
 }
 
 int cam_spi_write(struct camera_io_master *client,
-	uint32_t addr, uint16_t data,
+	uint32_t addr, uint32_t data,
+	enum camera_sensor_i2c_type addr_type,
 	enum camera_sensor_i2c_type data_type)
 {
 	struct cam_camera_spi_inst *pg =
 		&client->spi_client->cmd_tbl.page_program;
 	uint8_t header_len = sizeof(pg->opcode) + pg->addr_len + pg->dummy_len;
 	uint16_t len = 0;
-	char buf[2];
+	char buf[CAMERA_SENSOR_I2C_TYPE_MAX];
 	char *tx;
 	int rc = -EINVAL;
-	enum camera_sensor_i2c_type addr_type = CAMERA_SENSOR_I2C_TYPE_WORD;
 
-	if ((addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
-		|| (data_type != CAMERA_SENSOR_I2C_TYPE_BYTE
-		&& data_type != CAMERA_SENSOR_I2C_TYPE_WORD))
+	if ((addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
+		|| (addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
+		|| (data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
+		|| (data_type != CAMERA_SENSOR_I2C_TYPE_MAX))
 		return rc;
+
 	CAM_DBG(CAM_EEPROM, "Data: 0x%x", data);
 	len = header_len + (uint8_t)data_type;
 	tx = kmalloc(len, GFP_KERNEL | GFP_DMA);
 	if (!tx)
 		goto NOMEM;
+
 	if (data_type == CAMERA_SENSOR_I2C_TYPE_BYTE) {
 		buf[0] = data;
 		CAM_DBG(CAM_EEPROM, "Byte %d: 0x%x", len, buf[0]);
 	} else if (data_type == CAMERA_SENSOR_I2C_TYPE_WORD) {
 		buf[0] = (data >> BITS_PER_BYTE) & 0x00FF;
 		buf[1] = (data & 0x00FF);
+	} else if (data_type == CAMERA_SENSOR_I2C_TYPE_3B) {
+		buf[0] = (data >> 16) & 0x00FF;
+		buf[1] = (data >> 8) & 0x00FF;
+		buf[2] = (data & 0x00FF);
+	} else {
+		buf[0] = (data >> 24) & 0x00FF;
+		buf[1] = (data >> 16) & 0x00FF;
+		buf[2] = (data >> 8) & 0x00FF;
+		buf[3] = (data & 0x00FF);
 	}
+
 	rc = cam_spi_page_program(client, addr, buf,
-		(uint16_t)data_type, tx);
+		addr_type, data_type, tx);
 	if (rc < 0)
 		goto ERROR;
 	goto OUT;
@@ -442,18 +479,22 @@
 
 	if (!client || !write_setting)
 		return rc;
-	if (write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX
-		|| (write_setting->data_type != CAMERA_SENSOR_I2C_TYPE_BYTE
-		&& write_setting->data_type != CAMERA_SENSOR_I2C_TYPE_WORD))
+
+	if ((write_setting->addr_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
+		|| (write_setting->addr_type >= CAMERA_SENSOR_I2C_TYPE_MAX)
+		|| (write_setting->data_type <= CAMERA_SENSOR_I2C_TYPE_INVALID)
+		|| (write_setting->data_type >= CAMERA_SENSOR_I2C_TYPE_MAX))
 		return rc;
+
 	reg_setting = write_setting->reg_setting;
-	client_addr_type = addr_type;
+	client_addr_type = write_setting->addr_type;
 	addr_type = write_setting->addr_type;
 	for (i = 0; i < write_setting->size; i++) {
 		CAM_DBG(CAM_SENSOR, "addr %x data %x",
 			reg_setting->reg_addr, reg_setting->reg_data);
-		rc = cam_spi_write(client, reg_setting->reg_addr,
-			reg_setting->reg_data, write_setting->data_type);
+		rc = cam_spi_write(client,
+			reg_setting->reg_addr, reg_setting->reg_data,
+			write_setting->addr_type, write_setting->data_type);
 		if (rc < 0)
 			break;
 		reg_setting++;
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h
index a497491..ec1bede 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io/cam_sensor_spi.h
@@ -78,13 +78,22 @@
 
 int cam_spi_read(struct camera_io_master *client,
 	uint32_t addr, uint32_t *data,
+	enum camera_sensor_i2c_type addr_type,
 	enum camera_sensor_i2c_type data_type);
 
+int cam_spi_read_seq(struct camera_io_master *client,
+	uint32_t addr, uint8_t *data,
+	enum camera_sensor_i2c_type addr_type,
+	int32_t num_bytes);
+
 int cam_spi_query_id(struct camera_io_master *client,
-	uint32_t addr, uint8_t *data, uint32_t num_byte);
+	uint32_t addr,
+	enum camera_sensor_i2c_type addr_type,
+	uint8_t *data, uint32_t num_byte);
 
 int cam_spi_write(struct camera_io_master *client,
-	uint32_t addr, uint16_t data,
+	uint32_t addr, uint32_t data,
+	enum camera_sensor_i2c_type addr_type,
 	enum camera_sensor_i2c_type data_type);
 
 int cam_spi_write_table(struct camera_io_master *client,
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile
index bf61fb3..98ee3ae 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/Makefile
@@ -2,6 +2,7 @@
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci
+ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_res_mgr
 ccflags-y += -Idrivers/media/platform/msm/camera/cam_smmu/
 
 obj-$(CONFIG_SPECTRA_CAMERA) +=  cam_sensor_util.o
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
index 6ebd58b..2e91efc 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
@@ -33,6 +33,7 @@
 #define CAM_CSIPHY_NAME    "cam-csiphy"
 #define CAM_FLASH_NAME     "cam-flash"
 #define CAM_EEPROM_NAME    "cam-eeprom"
+#define CAM_OIS_NAME       "cam-ois"
 
 #define MAX_SYSTEM_PIPELINE_DELAY 2
 
@@ -152,7 +153,10 @@
 	CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMON,
 	CAM_SENSOR_PACKET_OPCODE_SENSOR_UPDATE,
 	CAM_SENSOR_PACKET_OPCODE_SENSOR_INITIAL_CONFIG,
-	CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_PROBE,
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_CONFIG,
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF,
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP = 127
 };
 
 enum cam_actuator_packet_opcodes {
@@ -165,6 +169,11 @@
 	CAM_EEPROM_PACKET_OPCODE_INIT
 };
 
+enum cam_ois_packet_opcodes {
+	CAM_OIS_PACKET_OPCODE_INIT,
+	CAM_OIS_PACKET_OPCODE_OIS_CONTROL
+};
+
 enum msm_bus_perf_setting {
 	S_INIT,
 	S_PREVIEW,
@@ -273,6 +282,9 @@
 
 struct i2c_data_settings {
 	struct i2c_settings_array init_settings;
+	struct i2c_settings_array config_settings;
+	struct i2c_settings_array streamon_settings;
+	struct i2c_settings_array streamoff_settings;
 	struct i2c_settings_array *per_frame;
 };
 
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
index f5df775..0a3878e 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.c
@@ -13,6 +13,7 @@
 #include <linux/kernel.h>
 #include "cam_sensor_util.h"
 #include <cam_mem_mgr.h>
+#include "cam_res_mgr_api.h"
 
 #define CAM_SENSOR_PINCTRL_STATE_SLEEP "cam_suspend"
 #define CAM_SENSOR_PINCTRL_STATE_DEFAULT "cam_default"
@@ -217,9 +218,19 @@
 		sizeof(cam_cmd_i2c_continuous_wr->reg_addr) +
 		sizeof(struct cam_cmd_read) *
 		(cam_cmd_i2c_continuous_wr->header.count));
-	i2c_list->op_code = cam_cmd_i2c_continuous_wr->header.op_code;
+	if (cam_cmd_i2c_continuous_wr->header.op_code ==
+		CAMERA_SENSOR_I2C_OP_CONT_WR_BRST)
+		i2c_list->op_code = CAM_SENSOR_I2C_WRITE_BURST;
+	else if (cam_cmd_i2c_continuous_wr->header.op_code ==
+		CAMERA_SENSOR_I2C_OP_CONT_WR_SEQN)
+		i2c_list->op_code = CAM_SENSOR_I2C_WRITE_SEQ;
+	else
+		return -EINVAL;
+
 	i2c_list->i2c_settings.addr_type =
 		cam_cmd_i2c_continuous_wr->header.addr_type;
+	i2c_list->i2c_settings.data_type =
+		cam_cmd_i2c_continuous_wr->header.data_type;
 
 	for (cnt = 0; cnt < (cam_cmd_i2c_continuous_wr->header.count);
 		cnt++) {
@@ -240,7 +251,7 @@
 }
 
 /**
- * Name : cam_sensor_i2c_pkt_parser
+ * Name : cam_sensor_i2c_command_parser
  * Description : Parse CSL CCI packet and apply register settings
  * Parameters :  s_ctrl  input/output    sub_device
  *              arg     input           cam_control
@@ -250,7 +261,7 @@
  * WAIT + n x RND_WR with num_cmd_buf = 1. Do not exepect RD/WR
  * with different cmd_type and op_code in one command buffer.
  */
-int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings,
 	struct cam_cmd_buf_desc   *cmd_desc, int32_t num_cmd_buffers)
 {
 	int16_t                   rc = 0, i = 0;
@@ -388,7 +399,7 @@
 	uint16_t power_setting_size)
 {
 	int32_t rc = 0, j = 0, i = 0;
-	uint32_t num_vreg;
+	int num_vreg;
 
 	/* Validate input parameters */
 	if (!soc_info || !power_setting) {
@@ -399,7 +410,7 @@
 
 	num_vreg = soc_info->num_rgltr;
 
-	if (num_vreg <= 0) {
+	if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) {
 		CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg);
 		return -EINVAL;
 	}
@@ -604,7 +615,8 @@
 
 	if (gpio_en) {
 		for (i = 0; i < size; i++) {
-			rc = gpio_request_one(gpio_tbl[i].gpio,
+			rc = cam_res_mgr_gpio_request(soc_info->dev,
+					gpio_tbl[i].gpio,
 					gpio_tbl[i].flags, gpio_tbl[i].label);
 			if (rc) {
 				/*
@@ -617,7 +629,7 @@
 			}
 		}
 	} else {
-		gpio_free_array(gpio_tbl, size);
+		cam_res_mgr_gpio_free_arry(soc_info->dev, gpio_tbl, size);
 	}
 
 	return rc;
@@ -1147,7 +1159,7 @@
 
 	sensor_pctrl->pinctrl = devm_pinctrl_get(dev);
 	if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) {
-		CAM_ERR(CAM_SENSOR, "Getting pinctrl handle failed");
+		CAM_DBG(CAM_SENSOR, "Getting pinctrl handle failed");
 		return -EINVAL;
 	}
 	sensor_pctrl->gpio_state_active =
@@ -1166,8 +1178,16 @@
 			"Failed to get the suspend state pinctrl handle");
 		return -EINVAL;
 	}
+
+	if (cam_res_mgr_shared_pinctrl_init()) {
+		CAM_ERR(CAM_SENSOR,
+			"Failed to init shared pinctrl");
+		return -EINVAL;
+	}
+
 	return 0;
 }
+
 int msm_cam_sensor_handle_reg_gpio(int seq_type,
 	struct msm_camera_gpio_num_info *gpio_num_info, int val)
 {
@@ -1185,7 +1205,7 @@
 	if (gpio_num_info->valid[gpio_offset] == 1) {
 		CAM_DBG(CAM_SENSOR, "VALID GPIO offset: %d, seqtype: %d",
 			 gpio_offset, seq_type);
-		gpio_set_value_cansleep(
+		cam_res_mgr_gpio_set_value(
 			gpio_num_info->gpio_num
 			[gpio_offset], val);
 	}
@@ -1210,14 +1230,18 @@
 	gpio_num_info = ctrl->gpio_num_info;
 	num_vreg = soc_info->num_rgltr;
 
-	if ((num_vreg == 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) {
-		CAM_ERR(CAM_SENSOR, "Regulators are not initialized");
+	if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) {
+		CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg);
 		return -EINVAL;
 	}
 
+	if (soc_info->use_shared_clk)
+		cam_res_mgr_shared_clk_config(true);
+
 	ret = msm_camera_pinctrl_init(&(ctrl->pinctrl_info), ctrl->dev);
 	if (ret < 0) {
-		CAM_ERR(CAM_SENSOR, "Initialization of pinctrl failed");
+		/* Some sensor subdev no pinctrl. */
+		CAM_DBG(CAM_SENSOR, "Initialization of pinctrl failed");
 		ctrl->cam_pinctrl_status = 0;
 	} else {
 		ctrl->cam_pinctrl_status = 1;
@@ -1228,14 +1252,25 @@
 		no_gpio = rc;
 
 	if (ctrl->cam_pinctrl_status) {
-		ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+		ret = pinctrl_select_state(
+			ctrl->pinctrl_info.pinctrl,
 			ctrl->pinctrl_info.gpio_state_active);
 		if (ret)
 			CAM_ERR(CAM_SENSOR, "cannot set pin to active state");
+
+		ret = cam_res_mgr_shared_pinctrl_select_state(true);
+		if (ret)
+			CAM_ERR(CAM_SENSOR,
+				"Cannot set shared pin to active state");
+
+		ret = cam_res_mgr_shared_pinctrl_post_init();
+		if (ret)
+			CAM_ERR(CAM_SENSOR,
+				"Failed to post init shared pinctrl");
 	}
 
 	for (index = 0; index < ctrl->power_setting_size; index++) {
-		CAM_DBG(CAM_SENSOR, "index: %d",  index);
+		CAM_DBG(CAM_SENSOR, "index: %d", index);
 		power_setting = &ctrl->power_setting[index];
 		CAM_DBG(CAM_SENSOR, "seq_type %d", power_setting->seq_type);
 
@@ -1413,7 +1448,7 @@
 			if (!gpio_num_info->valid
 				[power_setting->seq_type])
 				continue;
-			gpio_set_value_cansleep(
+			cam_res_mgr_gpio_set_value(
 				gpio_num_info->gpio_num
 				[power_setting->seq_type], GPIOF_OUT_INIT_LOW);
 			break;
@@ -1460,13 +1495,21 @@
 				(power_setting->delay * 1000) + 1000);
 		}
 	}
+
 	if (ctrl->cam_pinctrl_status) {
-		ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+		ret = pinctrl_select_state(
+				ctrl->pinctrl_info.pinctrl,
 				ctrl->pinctrl_info.gpio_state_suspend);
 		if (ret)
 			CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state");
-		devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+		cam_res_mgr_shared_pinctrl_select_state(false);
+		pinctrl_put(ctrl->pinctrl_info.pinctrl);
+		cam_res_mgr_shared_pinctrl_put();
 	}
+
+	if (soc_info->use_shared_clk)
+		cam_res_mgr_shared_clk_config(false);
+
 	ctrl->cam_pinctrl_status = 0;
 
 	cam_sensor_util_request_gpio_table(soc_info, 0);
@@ -1556,6 +1599,11 @@
 	gpio_num_info = ctrl->gpio_num_info;
 	num_vreg = soc_info->num_rgltr;
 
+	if ((num_vreg <= 0) || (num_vreg > CAM_SOC_MAX_REGULATOR)) {
+		CAM_ERR(CAM_SENSOR, "failed: num_vreg %d", num_vreg);
+		return -EINVAL;
+	}
+
 	for (index = 0; index < ctrl->power_down_setting_size; index++) {
 		CAM_DBG(CAM_SENSOR, "index %d",  index);
 		pd = &ctrl->power_down_setting[index];
@@ -1584,7 +1632,7 @@
 			if (!gpio_num_info->valid[pd->seq_type])
 				continue;
 
-			gpio_set_value_cansleep(
+			cam_res_mgr_gpio_set_value(
 				gpio_num_info->gpio_num
 				[pd->seq_type],
 				(int) pd->config_val);
@@ -1647,13 +1695,20 @@
 	}
 
 	if (ctrl->cam_pinctrl_status) {
-		ret = pinctrl_select_state(ctrl->pinctrl_info.pinctrl,
+		ret = pinctrl_select_state(
+				ctrl->pinctrl_info.pinctrl,
 				ctrl->pinctrl_info.gpio_state_suspend);
 		if (ret)
 			CAM_ERR(CAM_SENSOR, "cannot set pin to suspend state");
-		devm_pinctrl_put(ctrl->pinctrl_info.pinctrl);
+
+		cam_res_mgr_shared_pinctrl_select_state(false);
+		pinctrl_put(ctrl->pinctrl_info.pinctrl);
+		cam_res_mgr_shared_pinctrl_put();
 	}
 
+	if (soc_info->use_shared_clk)
+		cam_res_mgr_shared_clk_config(false);
+
 	ctrl->cam_pinctrl_status = 0;
 
 	cam_sensor_util_request_gpio_table(soc_info, 0);
diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
index 8a26369..d2079b0 100644
--- a/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
+++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_util.h
@@ -34,7 +34,7 @@
 int msm_camera_pinctrl_init
 	(struct msm_pinctrl_info *sensor_pctrl, struct device *dev);
 
-int cam_sensor_i2c_pkt_parser(struct i2c_settings_array *i2c_reg_settings,
+int cam_sensor_i2c_command_parser(struct i2c_settings_array *i2c_reg_settings,
 	struct cam_cmd_buf_desc *cmd_desc, int32_t num_cmd_buffers);
 
 int32_t delete_request(struct i2c_settings_array *i2c_array);
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
index ff7a0e5..7824102 100644
--- a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.c
@@ -21,7 +21,8 @@
 #include <linux/msm_dma_iommu_mapping.h>
 #include <linux/workqueue.h>
 #include <linux/genalloc.h>
-
+#include <soc/qcom/scm.h>
+#include <soc/qcom/secure_buffer.h>
 #include "cam_smmu_api.h"
 #include "cam_debug_util.h"
 
@@ -72,7 +73,7 @@
 
 enum cam_smmu_buf_state {
 	CAM_SMMU_BUFF_EXIST,
-	CAM_SMMU_BUFF_NOT_EXIST
+	CAM_SMMU_BUFF_NOT_EXIST,
 };
 
 enum cam_smmu_init_dir {
@@ -87,6 +88,12 @@
 	dma_addr_t base;
 };
 
+struct secheap_buf_info {
+	struct dma_buf *buf;
+	struct dma_buf_attachment *attach;
+	struct sg_table *table;
+};
+
 struct cam_context_bank_info {
 	struct device *dev;
 	struct dma_iommu_mapping *mapping;
@@ -98,7 +105,9 @@
 	uint8_t firmware_support;
 	uint8_t shared_support;
 	uint8_t io_support;
+	uint8_t secheap_support;
 	bool is_fw_allocated;
+	bool is_secheap_allocated;
 
 	struct scratch_mapping scratch_map;
 	struct gen_pool *shared_mem_pool;
@@ -107,8 +116,11 @@
 	struct cam_smmu_region_info firmware_info;
 	struct cam_smmu_region_info shared_info;
 	struct cam_smmu_region_info io_info;
+	struct cam_smmu_region_info secheap_info;
+	struct secheap_buf_info secheap_buf;
 
 	struct list_head smmu_buf_list;
+	struct list_head smmu_buf_kernel_list;
 	struct mutex lock;
 	int handle;
 	enum cam_smmu_ops_param state;
@@ -118,6 +130,7 @@
 		int, void*);
 	void *token[CAM_SMMU_CB_MAX];
 	int cb_count;
+	int secure_count;
 };
 
 struct cam_iommu_cb_set {
@@ -151,6 +164,17 @@
 	size_t phys_len;
 };
 
+struct cam_sec_buff_info {
+	struct ion_handle *i_hdl;
+	struct ion_client *i_client;
+	enum dma_data_direction dir;
+	int ref_count;
+	dma_addr_t paddr;
+	struct list_head list;
+	int ion_fd;
+	size_t len;
+};
+
 static struct cam_iommu_cb_set iommu_cb_set;
 
 static enum dma_data_direction cam_smmu_translate_dir(
@@ -166,6 +190,12 @@
 static struct cam_dma_buff_info *cam_smmu_find_mapping_by_ion_index(int idx,
 	int ion_fd);
 
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_dma_buf(int idx,
+	struct dma_buf *buf);
+
+static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx,
+	int ion_fd);
+
 static int cam_smmu_init_scratch_map(struct scratch_mapping *scratch_map,
 	dma_addr_t base, size_t size,
 	int order);
@@ -182,7 +212,11 @@
 
 static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
 	enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
-	size_t *len_ptr,
+	size_t *len_ptr, enum cam_smmu_region_id region_id);
+
+static int cam_smmu_map_kernel_buffer_and_add_to_list(int idx,
+	struct dma_buf *buf, enum dma_data_direction dma_dir,
+	dma_addr_t *paddr_ptr, size_t *len_ptr,
 	enum cam_smmu_region_id region_id);
 
 static int cam_smmu_alloc_scratch_buffer_add_to_list(int idx,
@@ -198,9 +232,13 @@
 	struct cam_dma_buff_info *mapping_info,
 	int idx);
 
-static void cam_smmu_clean_buffer_list(int idx);
+static void cam_smmu_clean_user_buffer_list(int idx);
 
-static void cam_smmu_print_list(int idx);
+static void cam_smmu_clean_kernel_buffer_list(int idx);
+
+static void cam_smmu_print_user_list(int idx);
+
+static void cam_smmu_print_kernel_list(int idx);
 
 static void cam_smmu_print_table(void);
 
@@ -245,7 +283,7 @@
 	kfree(payload);
 }
 
-static void cam_smmu_print_list(int idx)
+static void cam_smmu_print_user_list(int idx)
 {
 	struct cam_dma_buff_info *mapping;
 
@@ -260,6 +298,21 @@
 	}
 }
 
+static void cam_smmu_print_kernel_list(int idx)
+{
+	struct cam_dma_buff_info *mapping;
+
+	CAM_ERR(CAM_SMMU, "index = %d", idx);
+	list_for_each_entry(mapping,
+		&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) {
+		CAM_ERR(CAM_SMMU,
+			"dma_buf = %pK, paddr= 0x%pK, len = %u, region = %d",
+			 mapping->buf, (void *)mapping->paddr,
+			 (unsigned int)mapping->len,
+			 mapping->region_id);
+	}
+}
+
 static void cam_smmu_print_table(void)
 {
 	int i;
@@ -463,6 +516,7 @@
 	for (i = 0; i < iommu_cb_set.cb_num; i++) {
 		iommu_cb_set.cb_info[i].handle = HANDLE_INIT;
 		INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_list);
+		INIT_LIST_HEAD(&iommu_cb_set.cb_info[i].smmu_buf_kernel_list);
 		iommu_cb_set.cb_info[i].state = CAM_SMMU_DETACH;
 		iommu_cb_set.cb_info[i].dev = NULL;
 		iommu_cb_set.cb_info[i].cb_count = 0;
@@ -544,7 +598,15 @@
 					"Error: %s already got handle 0x%x",
 					name,
 					iommu_cb_set.cb_info[i].handle);
+
+				if (iommu_cb_set.cb_info[i].is_secure)
+					iommu_cb_set.cb_info[i].secure_count++;
+
 				mutex_unlock(&iommu_cb_set.cb_info[i].lock);
+				if (iommu_cb_set.cb_info[i].is_secure) {
+					*hdl = iommu_cb_set.cb_info[i].handle;
+					return 0;
+				}
 				return -EINVAL;
 			}
 
@@ -556,6 +618,8 @@
 			/* put handle in the table */
 			iommu_cb_set.cb_info[i].handle = handle;
 			iommu_cb_set.cb_info[i].cb_count = 0;
+			if (iommu_cb_set.cb_info[i].is_secure)
+				iommu_cb_set.cb_info[i].secure_count++;
 			*hdl = handle;
 			CAM_DBG(CAM_SMMU, "%s creates handle 0x%x",
 				name, handle);
@@ -683,7 +747,13 @@
 {
 	struct cam_dma_buff_info *mapping;
 
-	list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+	if (ion_fd < 0) {
+		CAM_ERR(CAM_SMMU, "Invalid fd %d", ion_fd);
+		return NULL;
+	}
+
+	list_for_each_entry(mapping,
+			&iommu_cb_set.cb_info[idx].smmu_buf_list,
 			list) {
 		if (mapping->ion_fd == ion_fd) {
 			CAM_DBG(CAM_SMMU, "find ion_fd %d", ion_fd);
@@ -691,12 +761,53 @@
 		}
 	}
 
+	CAM_ERR(CAM_SMMU, "Error: Cannot find entry by index %d", idx);
+
+	return NULL;
+}
+
+static struct cam_dma_buff_info *cam_smmu_find_mapping_by_dma_buf(int idx,
+	struct dma_buf *buf)
+{
+	struct cam_dma_buff_info *mapping;
+
+	if (!buf) {
+		CAM_ERR(CAM_SMMU, "Invalid dma_buf");
+		return NULL;
+	}
+
+	list_for_each_entry(mapping,
+			&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list,
+			list) {
+		if (mapping->buf == buf) {
+			CAM_DBG(CAM_SMMU, "find dma_buf %pK", buf);
+			return mapping;
+		}
+	}
+
+	CAM_ERR(CAM_SMMU, "Error: Cannot find entry by index %d", idx);
+
+	return NULL;
+}
+
+static struct cam_sec_buff_info *cam_smmu_find_mapping_by_sec_buf_idx(int idx,
+	int ion_fd)
+{
+	struct cam_sec_buff_info *mapping;
+
+	list_for_each_entry(mapping, &iommu_cb_set.cb_info[idx].smmu_buf_list,
+		list) {
+		if (mapping->ion_fd == ion_fd) {
+			CAM_DBG(CAM_SMMU, "find ion_fd %d", ion_fd);
+			return mapping;
+		}
+	}
 	CAM_ERR(CAM_SMMU, "Error: Cannot find fd %d by index %d",
 		ion_fd, idx);
 	return NULL;
 }
 
-static void cam_smmu_clean_buffer_list(int idx)
+static void cam_smmu_clean_user_buffer_list(int idx)
 {
 	int ret;
 	struct cam_dma_buff_info *mapping_info, *temp;
@@ -733,6 +844,40 @@
 	}
 }
 
+static void cam_smmu_clean_kernel_buffer_list(int idx)
+{
+	int ret;
+	struct cam_dma_buff_info *mapping_info, *temp;
+
+	list_for_each_entry_safe(mapping_info, temp,
+			&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) {
+		CAM_DBG(CAM_SMMU,
+			"Free mapping address %pK, i = %d, dma_buf = %pK",
+			(void *)mapping_info->paddr, idx,
+			mapping_info->buf);
+
+		/* Clean up regular mapped buffers */
+		ret = cam_smmu_unmap_buf_and_remove_from_list(
+				mapping_info,
+				idx);
+
+		if (ret < 0) {
+			CAM_ERR(CAM_SMMU,
+				"Buffer delete in kernel list failed: idx = %d",
+				idx);
+			CAM_ERR(CAM_SMMU,
+				"Buffer delete failed: addr = %lx, dma_buf = %pK",
+				(unsigned long)mapping_info->paddr,
+				mapping_info->buf);
+			/*
+			 * Ignore this error and continue to delete other
+			 * buffers in the list
+			 */
+			continue;
+		}
+	}
+}
+
 static int cam_smmu_attach(int idx)
 {
 	int ret;
@@ -1081,6 +1226,15 @@
 		region_info->iova_start = cb->io_info.iova_start;
 		region_info->iova_len = cb->io_info.iova_len;
 		break;
+	case CAM_SMMU_REGION_SECHEAP:
+		if (!cb->secheap_support) {
+			CAM_ERR(CAM_SMMU, "Secondary heap not supported");
+			mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+			return -ENODEV;
+		}
+		region_info->iova_start = cb->secheap_info.iova_start;
+		region_info->iova_len = cb->secheap_info.iova_len;
+		break;
 	default:
 		CAM_ERR(CAM_SMMU, "Invalid region id: %d for smmu hdl: %X",
 			smmu_hdl, region_id);
@@ -1093,25 +1247,173 @@
 }
 EXPORT_SYMBOL(cam_smmu_get_region_info);
 
-static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
-	 enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
-	 size_t *len_ptr,
-	 enum cam_smmu_region_id region_id)
+int cam_smmu_reserve_sec_heap(int32_t smmu_hdl,
+	struct dma_buf *buf,
+	dma_addr_t *iova,
+	size_t *request_len)
 {
-	int rc = -1;
-	struct cam_dma_buff_info *mapping_info;
-	struct dma_buf *buf = NULL;
+	struct secheap_buf_info *secheap_buf = NULL;
+	size_t size = 0;
+	uint32_t sec_heap_iova = 0;
+	size_t sec_heap_iova_len = 0;
+	int idx;
+	int rc = 0;
+
+	idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+		CAM_ERR(CAM_SMMU,
+			"Error: handle or index invalid. idx = %d hdl = %x",
+			idx, smmu_hdl);
+		return -EINVAL;
+	}
+
+	if (!iommu_cb_set.cb_info[idx].secheap_support) {
+		CAM_ERR(CAM_SMMU, "Secondary heap not supported");
+		return -EINVAL;
+	}
+
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+
+	if (iommu_cb_set.cb_info[idx].is_secheap_allocated) {
+		CAM_ERR(CAM_SMMU, "Trying to allocate secheap twice");
+		rc = -ENOMEM;
+		mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+		return rc;
+	}
+
+	if (IS_ERR_OR_NULL(buf)) {
+		rc = PTR_ERR(buf);
+		CAM_ERR(CAM_SMMU,
+			"Error: dma get buf failed. rc = %d", rc);
+		goto err_out;
+	}
+
+	secheap_buf = &iommu_cb_set.cb_info[idx].secheap_buf;
+	secheap_buf->buf = buf;
+	secheap_buf->attach = dma_buf_attach(secheap_buf->buf,
+		iommu_cb_set.cb_info[idx].dev);
+	if (IS_ERR_OR_NULL(secheap_buf->attach)) {
+		rc = PTR_ERR(secheap_buf->attach);
+		CAM_ERR(CAM_SMMU, "Error: dma buf attach failed");
+		goto err_put;
+	}
+
+	secheap_buf->table = dma_buf_map_attachment(secheap_buf->attach,
+		DMA_BIDIRECTIONAL);
+	if (IS_ERR_OR_NULL(secheap_buf->table)) {
+		rc = PTR_ERR(secheap_buf->table);
+		CAM_ERR(CAM_SMMU, "Error: dma buf map attachment failed");
+		goto err_detach;
+	}
+
+	sec_heap_iova = iommu_cb_set.cb_info[idx].secheap_info.iova_start;
+	sec_heap_iova_len = iommu_cb_set.cb_info[idx].secheap_info.iova_len;
+	size = iommu_map_sg(iommu_cb_set.cb_info[idx].mapping->domain,
+		sec_heap_iova,
+		secheap_buf->table->sgl,
+		secheap_buf->table->nents,
+		IOMMU_READ | IOMMU_WRITE);
+	if (size != sec_heap_iova_len) {
+		CAM_ERR(CAM_SMMU, "IOMMU mapping failed");
+		goto err_unmap_sg;
+	}
+
+	iommu_cb_set.cb_info[idx].is_secheap_allocated = true;
+	*iova = (uint32_t)sec_heap_iova;
+	*request_len = sec_heap_iova_len;
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+
+	return rc;
+
+err_unmap_sg:
+	dma_buf_unmap_attachment(secheap_buf->attach,
+		secheap_buf->table,
+		DMA_BIDIRECTIONAL);
+err_detach:
+	dma_buf_detach(secheap_buf->buf,
+		secheap_buf->attach);
+err_put:
+	dma_buf_put(secheap_buf->buf);
+err_out:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_reserve_sec_heap);
+
+int cam_smmu_release_sec_heap(int32_t smmu_hdl)
+{
+	int idx;
+	size_t size = 0;
+	uint32_t sec_heap_iova = 0;
+	size_t sec_heap_iova_len = 0;
+	struct secheap_buf_info *secheap_buf = NULL;
+
+	idx = GET_SMMU_TABLE_IDX(smmu_hdl);
+	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+		CAM_ERR(CAM_SMMU,
+			"Error: handle or index invalid. idx = %d hdl = %x",
+			idx, smmu_hdl);
+		return -EINVAL;
+	}
+
+	if (!iommu_cb_set.cb_info[idx].secheap_support) {
+		CAM_ERR(CAM_SMMU, "Secondary heap not supported");
+		return -EINVAL;
+	}
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+
+	if (!iommu_cb_set.cb_info[idx].is_secheap_allocated) {
+		CAM_ERR(CAM_SMMU, "Trying to release secheap twice");
+		mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+		return -ENOMEM;
+	}
+
+	secheap_buf = &iommu_cb_set.cb_info[idx].secheap_buf;
+	sec_heap_iova = iommu_cb_set.cb_info[idx].secheap_info.iova_start;
+	sec_heap_iova_len = iommu_cb_set.cb_info[idx].secheap_info.iova_len;
+
+	size = iommu_unmap(iommu_cb_set.cb_info[idx].mapping->domain,
+		sec_heap_iova,
+		sec_heap_iova_len);
+	if (size != sec_heap_iova_len) {
+		CAM_ERR(CAM_SMMU, "Failed: Unmapped = %zu, requested = %zu",
+			size,
+			sec_heap_iova_len);
+	}
+
+	dma_buf_unmap_attachment(secheap_buf->attach,
+		secheap_buf->table, DMA_BIDIRECTIONAL);
+	dma_buf_detach(secheap_buf->buf, secheap_buf->attach);
+	dma_buf_put(secheap_buf->buf);
+	iommu_cb_set.cb_info[idx].is_secheap_allocated = false;
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(cam_smmu_release_sec_heap);
+
+static int cam_smmu_map_buffer_validate(struct dma_buf *buf,
+	int idx, enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+	size_t *len_ptr, enum cam_smmu_region_id region_id,
+	struct cam_dma_buff_info **mapping_info)
+{
 	struct dma_buf_attachment *attach = NULL;
 	struct sg_table *table = NULL;
 	struct iommu_domain *domain;
 	size_t size = 0;
 	uint32_t iova = 0;
+	int rc = 0;
 
-	/* allocate memory for each buffer information */
-	buf = dma_buf_get(ion_fd);
 	if (IS_ERR_OR_NULL(buf)) {
 		rc = PTR_ERR(buf);
-		CAM_ERR(CAM_SMMU, "Error: dma get buf failed. fd = %d", ion_fd);
+		CAM_ERR(CAM_SMMU,
+			"Error: dma get buf failed. rc = %d", rc);
+		goto err_out;
+	}
+
+	if (!mapping_info) {
+		rc = -EINVAL;
+		CAM_ERR(CAM_SMMU, "Error: mapping_info is invalid");
 		goto err_out;
 	}
 
@@ -1146,18 +1448,13 @@
 			goto err_unmap_sg;
 		}
 
-		size = iommu_map_sg(domain,
-			iova,
-			table->sgl,
-			table->nents,
-			IOMMU_READ | IOMMU_WRITE);
+		size = iommu_map_sg(domain, iova, table->sgl, table->nents,
+				IOMMU_READ | IOMMU_WRITE);
 
 		if (size < 0) {
 			CAM_ERR(CAM_SMMU, "IOMMU mapping failed");
 			rc = cam_smmu_free_iova(iova,
-				size,
-				iommu_cb_set.cb_info[idx].handle);
-
+				size, iommu_cb_set.cb_info[idx].handle);
 			if (rc)
 				CAM_ERR(CAM_SMMU, "IOVA free failed");
 			rc = -ENOMEM;
@@ -1169,7 +1466,7 @@
 		}
 	} else if (region_id == CAM_SMMU_REGION_IO) {
 		rc = msm_dma_map_sg_lazy(iommu_cb_set.cb_info[idx].dev,
-			table->sgl, table->nents, dma_dir, buf);
+		table->sgl, table->nents, dma_dir, buf);
 
 		if (rc != table->nents) {
 			CAM_ERR(CAM_SMMU, "Error: msm_dma_map_sg_lazy failed");
@@ -1201,33 +1498,31 @@
 	}
 
 	/* fill up mapping_info */
-	mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
-	if (!mapping_info) {
+	*mapping_info = kzalloc(sizeof(struct cam_dma_buff_info), GFP_KERNEL);
+	if (!(*mapping_info)) {
 		rc = -ENOSPC;
 		goto err_alloc;
 	}
-	mapping_info->ion_fd = ion_fd;
-	mapping_info->buf = buf;
-	mapping_info->attach = attach;
-	mapping_info->table = table;
-	mapping_info->paddr = *paddr_ptr;
-	mapping_info->len = *len_ptr;
-	mapping_info->dir = dma_dir;
-	mapping_info->ref_count = 1;
-	mapping_info->region_id = region_id;
+
+	(*mapping_info)->buf = buf;
+	(*mapping_info)->attach = attach;
+	(*mapping_info)->table = table;
+	(*mapping_info)->paddr = *paddr_ptr;
+	(*mapping_info)->len = *len_ptr;
+	(*mapping_info)->dir = dma_dir;
+	(*mapping_info)->ref_count = 1;
+	(*mapping_info)->region_id = region_id;
 
 	if (!*paddr_ptr || !*len_ptr) {
 		CAM_ERR(CAM_SMMU, "Error: Space Allocation failed");
-		kfree(mapping_info);
+		kfree(*mapping_info);
 		rc = -ENOSPC;
 		goto err_alloc;
 	}
-	CAM_DBG(CAM_SMMU, "ion_fd = %d, dev = %pK, paddr= %pK, len = %u",
-		ion_fd, (void *)iommu_cb_set.cb_info[idx].dev,
+	CAM_DBG(CAM_SMMU, "dma_buf = %pK, dev = %pK, paddr= %pK, len = %u",
+		buf, (void *)iommu_cb_set.cb_info[idx].dev,
 		(void *)*paddr_ptr, (unsigned int)*len_ptr);
 
-	/* add to the list */
-	list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
 	return 0;
 
 err_alloc:
@@ -1256,6 +1551,60 @@
 	return rc;
 }
 
+
+static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
+	 enum dma_data_direction dma_dir, dma_addr_t *paddr_ptr,
+	 size_t *len_ptr, enum cam_smmu_region_id region_id)
+{
+	int rc = -1;
+	struct cam_dma_buff_info *mapping_info = NULL;
+	struct dma_buf *buf = NULL;
+
+	/* returns the dma_buf structure related to an fd */
+	buf = dma_buf_get(ion_fd);
+
+	rc = cam_smmu_map_buffer_validate(buf, idx, dma_dir, paddr_ptr, len_ptr,
+		region_id, &mapping_info);
+
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "buffer validation failure");
+		return rc;
+	}
+
+	mapping_info->ion_fd = ion_fd;
+	/* add to the list */
+	list_add(&mapping_info->list,
+		&iommu_cb_set.cb_info[idx].smmu_buf_list);
+
+	return 0;
+}
+
+static int cam_smmu_map_kernel_buffer_and_add_to_list(int idx,
+	struct dma_buf *buf, enum dma_data_direction dma_dir,
+	dma_addr_t *paddr_ptr, size_t *len_ptr,
+	enum cam_smmu_region_id region_id)
+{
+	int rc = -1;
+	struct cam_dma_buff_info *mapping_info = NULL;
+
+	rc = cam_smmu_map_buffer_validate(buf, idx, dma_dir, paddr_ptr, len_ptr,
+		region_id, &mapping_info);
+
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "buffer validation failure");
+		return rc;
+	}
+
+	mapping_info->ion_fd = -1;
+
+	/* add to the list */
+	list_add(&mapping_info->list,
+		&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list);
+
+	return 0;
+}
+
+
 static int cam_smmu_unmap_buf_and_remove_from_list(
 	struct cam_dma_buff_info *mapping_info,
 	int idx)
@@ -1322,8 +1671,7 @@
 }
 
 static enum cam_smmu_buf_state cam_smmu_check_fd_in_list(int idx,
-	int ion_fd, dma_addr_t *paddr_ptr,
-	size_t *len_ptr)
+	int ion_fd, dma_addr_t *paddr_ptr, size_t *len_ptr)
 {
 	struct cam_dma_buff_info *mapping;
 
@@ -1339,6 +1687,42 @@
 	return CAM_SMMU_BUFF_NOT_EXIST;
 }
 
+static enum cam_smmu_buf_state cam_smmu_check_dma_buf_in_list(int idx,
+	struct dma_buf *buf, dma_addr_t *paddr_ptr, size_t *len_ptr)
+{
+	struct cam_dma_buff_info *mapping;
+
+	list_for_each_entry(mapping,
+		&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list, list) {
+		if (mapping->buf == buf) {
+			*paddr_ptr = mapping->paddr;
+			*len_ptr = mapping->len;
+			return CAM_SMMU_BUFF_EXIST;
+		}
+	}
+
+	return CAM_SMMU_BUFF_NOT_EXIST;
+}
+
+static enum cam_smmu_buf_state cam_smmu_check_secure_fd_in_list(int idx,
+					int ion_fd, dma_addr_t *paddr_ptr,
+					size_t *len_ptr)
+{
+	struct cam_sec_buff_info *mapping;
+
+	list_for_each_entry(mapping,
+			&iommu_cb_set.cb_info[idx].smmu_buf_list,
+			list) {
+		if (mapping->ion_fd == ion_fd) {
+			*paddr_ptr = mapping->paddr;
+			*len_ptr = mapping->len;
+			return CAM_SMMU_BUFF_EXIST;
+		}
+	}
+
+	return CAM_SMMU_BUFF_NOT_EXIST;
+}
+
 int cam_smmu_get_handle(char *identifier, int *handle_ptr)
 {
 	int ret = 0;
@@ -1722,24 +2106,210 @@
 	return rc;
 }
 
-int cam_smmu_map_sec_iova(int handle, int ion_fd,
-	enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
-	size_t *len_ptr)
+static int cam_smmu_map_stage2_buffer_and_add_to_list(int idx, int ion_fd,
+		 enum dma_data_direction dma_dir, struct ion_client *client,
+		 dma_addr_t *paddr_ptr,
+		 size_t *len_ptr)
 {
-	/* not implemented yet */
-	return -EPERM;
-}
-EXPORT_SYMBOL(cam_smmu_map_sec_iova);
+	int rc = 0;
+	struct ion_handle *i_handle = NULL;
+	struct cam_sec_buff_info *mapping_info;
 
-int cam_smmu_map_iova(int handle, int ion_fd,
-	enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
-	size_t *len_ptr, enum cam_smmu_region_id region_id)
+	/* clean the content from clients */
+	*paddr_ptr = (dma_addr_t)NULL;
+	*len_ptr = (size_t)0;
+
+	i_handle = ion_import_dma_buf_fd(client, ion_fd);
+	if (IS_ERR_OR_NULL((void *)(i_handle))) {
+		CAM_ERR(CAM_SMMU, "ion import dma buffer failed");
+		return -EINVAL;
+	}
+
+	/* return addr and len to client */
+	rc = ion_phys(client, i_handle, paddr_ptr, len_ptr);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "ION Get Physical failed, rc: %d",
+			rc);
+		return rc;
+	}
+
+	/* fill up mapping_info */
+	mapping_info = kzalloc(sizeof(struct cam_sec_buff_info), GFP_KERNEL);
+	if (!mapping_info)
+		return -ENOMEM;
+
+	mapping_info->ion_fd = ion_fd;
+	mapping_info->paddr = *paddr_ptr;
+	mapping_info->len = *len_ptr;
+	mapping_info->dir = dma_dir;
+	mapping_info->ref_count = 1;
+	mapping_info->i_hdl = i_handle;
+	mapping_info->i_client = client;
+
+	CAM_DBG(CAM_SMMU, "ion_fd = %d, dev = %pK, paddr= %pK, len = %u",
+			ion_fd,
+			(void *)iommu_cb_set.cb_info[idx].dev,
+			(void *)*paddr_ptr, (unsigned int)*len_ptr);
+
+	/* add to the list */
+	list_add(&mapping_info->list, &iommu_cb_set.cb_info[idx].smmu_buf_list);
+
+	return rc;
+}
+
+int cam_smmu_map_stage2_iova(int handle,
+		int ion_fd, enum cam_smmu_map_dir dir,
+		struct ion_client *client, ion_phys_addr_t *paddr_ptr,
+		size_t *len_ptr)
 {
 	int idx, rc;
 	enum dma_data_direction dma_dir;
 	enum cam_smmu_buf_state buf_state;
 
 	if (!paddr_ptr || !len_ptr) {
+		CAM_ERR(CAM_SMMU,
+			"Error: Invalid inputs, paddr_ptr:%pK, len_ptr: %pK",
+			paddr_ptr, len_ptr);
+		return -EINVAL;
+	}
+	/* clean the content from clients */
+	*paddr_ptr = (dma_addr_t)NULL;
+	*len_ptr = (size_t)0;
+
+	dma_dir = cam_smmu_translate_dir(dir);
+	if (dma_dir == DMA_NONE) {
+		CAM_ERR(CAM_SMMU,
+			"Error: translate direction failed. dir = %d", dir);
+		return -EINVAL;
+	}
+
+	idx = GET_SMMU_TABLE_IDX(handle);
+	if ((handle == HANDLE_INIT) ||
+		(idx < 0) ||
+		(idx >= iommu_cb_set.cb_num)) {
+		CAM_ERR(CAM_SMMU,
+			"Error: handle or index invalid. idx = %d hdl = %x",
+			idx, handle);
+		return -EINVAL;
+	}
+
+	if (!iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't map secure mem to non secure cb");
+		return -EINVAL;
+	}
+
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].handle != handle) {
+		CAM_ERR(CAM_SMMU,
+			"Error: hdl is not valid, table_hdl = %x, hdl = %x",
+			iommu_cb_set.cb_info[idx].handle, handle);
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+	buf_state = cam_smmu_check_secure_fd_in_list(idx, ion_fd, paddr_ptr,
+			len_ptr);
+	if (buf_state == CAM_SMMU_BUFF_EXIST) {
+		CAM_DBG(CAM_SMMU, "fd:%d already in list, give same addr back",
+			ion_fd);
+		rc = 0;
+		goto get_addr_end;
+	}
+	rc = cam_smmu_map_stage2_buffer_and_add_to_list(idx, ion_fd, dma_dir,
+			client, paddr_ptr, len_ptr);
+	if (rc < 0) {
+		CAM_ERR(CAM_SMMU, "Error: mapping or add list fail");
+		goto get_addr_end;
+	}
+
+get_addr_end:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_map_stage2_iova);
+
+static int cam_smmu_secure_unmap_buf_and_remove_from_list(
+		struct cam_sec_buff_info *mapping_info,
+		int idx)
+{
+	if (!mapping_info) {
+		CAM_ERR(CAM_SMMU, "Error: List doesn't exist");
+		return -EINVAL;
+	}
+	ion_free(mapping_info->i_client, mapping_info->i_hdl);
+	list_del_init(&mapping_info->list);
+
+	CAM_DBG(CAM_SMMU, "unmap fd: %d, idx : %d", mapping_info->ion_fd, idx);
+
+	/* free one buffer */
+	kfree(mapping_info);
+	return 0;
+}
+
+int cam_smmu_unmap_stage2_iova(int handle, int ion_fd)
+{
+	int idx, rc;
+	struct cam_sec_buff_info *mapping_info;
+
+	/* find index in the iommu_cb_set.cb_info */
+	idx = GET_SMMU_TABLE_IDX(handle);
+	if ((handle == HANDLE_INIT) ||
+		(idx < 0) ||
+		(idx >= iommu_cb_set.cb_num)) {
+		CAM_ERR(CAM_SMMU,
+			"Error: handle or index invalid. idx = %d hdl = %x",
+			idx, handle);
+		return -EINVAL;
+	}
+
+	if (!iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't unmap secure mem from non secure cb");
+		return -EINVAL;
+	}
+
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].handle != handle) {
+		CAM_ERR(CAM_SMMU,
+			"Error: hdl is not valid, table_hdl = %x, hdl = %x",
+			iommu_cb_set.cb_info[idx].handle, handle);
+		rc = -EINVAL;
+		goto put_addr_end;
+	}
+
+	/* based on ion fd and index, we can find mapping info of buffer */
+	mapping_info = cam_smmu_find_mapping_by_sec_buf_idx(idx, ion_fd);
+	if (!mapping_info) {
+		CAM_ERR(CAM_SMMU,
+			"Error: Invalid params! idx = %d, fd = %d",
+			idx, ion_fd);
+		rc = -EINVAL;
+		goto put_addr_end;
+	}
+
+	/* unmapping one buffer from device */
+	rc = cam_smmu_secure_unmap_buf_and_remove_from_list(mapping_info, idx);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail");
+		goto put_addr_end;
+	}
+
+put_addr_end:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_unmap_stage2_iova);
+
+static int cam_smmu_map_iova_validate_params(int handle,
+	enum cam_smmu_map_dir dir,
+	dma_addr_t *paddr_ptr, size_t *len_ptr,
+	enum cam_smmu_region_id region_id)
+{
+	int idx, rc = 0;
+	enum dma_data_direction dma_dir;
+
+	if (!paddr_ptr || !len_ptr) {
 		CAM_ERR(CAM_SMMU, "Input pointers are invalid");
 		return -EINVAL;
 	}
@@ -1767,7 +2337,34 @@
 		return -EINVAL;
 	}
 
+	return rc;
+}
+
+int cam_smmu_map_user_iova(int handle, int ion_fd,
+	enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
+	size_t *len_ptr, enum cam_smmu_region_id region_id)
+{
+	int idx, rc = 0;
+	enum cam_smmu_buf_state buf_state;
+	enum dma_data_direction dma_dir;
+
+	rc = cam_smmu_map_iova_validate_params(handle, dir, paddr_ptr,
+		len_ptr, region_id);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "initial checks failed, unable to proceed");
+		return rc;
+	}
+
+	dma_dir = cam_smmu_translate_dir(dir);
+	idx = GET_SMMU_TABLE_IDX(handle);
 	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't map non-secure mem to secure cb");
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
 	if (iommu_cb_set.cb_info[idx].handle != handle) {
 		CAM_ERR(CAM_SMMU, "hdl is not valid, table_hdl = %x, hdl = %x",
 			iommu_cb_set.cb_info[idx].handle, handle);
@@ -1778,20 +2375,19 @@
 	if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
 		CAM_ERR(CAM_SMMU,
 			"Err:Dev %s should call SMMU attach before map buffer",
-				iommu_cb_set.cb_info[idx].name);
+			iommu_cb_set.cb_info[idx].name);
 		rc = -EINVAL;
 		goto get_addr_end;
 	}
 
-	buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr,
-		len_ptr);
+	buf_state = cam_smmu_check_fd_in_list(idx, ion_fd, paddr_ptr, len_ptr);
 	if (buf_state == CAM_SMMU_BUFF_EXIST) {
 		CAM_ERR(CAM_SMMU,
-			"ion_fd:%d already in the list, give same addr back",
-			 ion_fd);
+			"ion_fd: %d already in the list", ion_fd);
 		rc = -EALREADY;
 		goto get_addr_end;
 	}
+
 	rc = cam_smmu_map_buffer_and_add_to_list(idx, ion_fd, dma_dir,
 			paddr_ptr, len_ptr, region_id);
 	if (rc < 0)
@@ -1801,8 +2397,67 @@
 	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
 	return rc;
 }
-EXPORT_SYMBOL(cam_smmu_map_iova);
+EXPORT_SYMBOL(cam_smmu_map_user_iova);
 
+int cam_smmu_map_kernel_iova(int handle, struct dma_buf *buf,
+	enum cam_smmu_map_dir dir, dma_addr_t *paddr_ptr,
+	size_t *len_ptr, enum cam_smmu_region_id region_id)
+{
+	int idx, rc = 0;
+	enum cam_smmu_buf_state buf_state;
+	enum dma_data_direction dma_dir;
+
+	rc = cam_smmu_map_iova_validate_params(handle, dir, paddr_ptr,
+		len_ptr, region_id);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "initial checks failed, unable to proceed");
+		return rc;
+	}
+
+	dma_dir = cam_smmu_translate_dir(dir);
+	idx = GET_SMMU_TABLE_IDX(handle);
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't map non-secure mem to secure cb");
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+	if (iommu_cb_set.cb_info[idx].handle != handle) {
+		CAM_ERR(CAM_SMMU, "hdl is not valid, table_hdl = %x, hdl = %x",
+			iommu_cb_set.cb_info[idx].handle, handle);
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+	if (iommu_cb_set.cb_info[idx].state != CAM_SMMU_ATTACH) {
+		CAM_ERR(CAM_SMMU,
+			"Err:Dev %s should call SMMU attach before map buffer",
+			iommu_cb_set.cb_info[idx].name);
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+	buf_state = cam_smmu_check_dma_buf_in_list(idx, buf,
+	paddr_ptr, len_ptr);
+	if (buf_state == CAM_SMMU_BUFF_EXIST) {
+		CAM_ERR(CAM_SMMU,
+			"dma_buf :%pK already in the list", buf);
+		rc = -EALREADY;
+		goto get_addr_end;
+	}
+
+	rc = cam_smmu_map_kernel_buffer_and_add_to_list(idx, buf, dma_dir,
+			paddr_ptr, len_ptr, region_id);
+	if (rc < 0)
+		CAM_ERR(CAM_SMMU, "mapping or add list fail");
+
+get_addr_end:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_map_kernel_iova);
 
 int cam_smmu_get_iova(int handle, int ion_fd,
 	dma_addr_t *paddr_ptr, size_t *len_ptr)
@@ -1832,6 +2487,12 @@
 		return -EINVAL;
 	}
 
+	if (iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't get non-secure mem from secure cb");
+		return -EINVAL;
+	}
+
 	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
 	if (iommu_cb_set.cb_info[idx].handle != handle) {
 		CAM_ERR(CAM_SMMU,
@@ -1854,19 +2515,69 @@
 }
 EXPORT_SYMBOL(cam_smmu_get_iova);
 
-int cam_smmu_unmap_sec_iova(int handle, int ion_fd)
+int cam_smmu_get_stage2_iova(int handle, int ion_fd,
+	dma_addr_t *paddr_ptr, size_t *len_ptr)
 {
-	/* not implemented yet */
-	return -EPERM;
-}
-EXPORT_SYMBOL(cam_smmu_unmap_sec_iova);
+	int idx, rc = 0;
+	enum cam_smmu_buf_state buf_state;
 
-int cam_smmu_unmap_iova(int handle,
-	int ion_fd,
-	enum cam_smmu_region_id region_id)
+	if (!paddr_ptr || !len_ptr) {
+		CAM_ERR(CAM_SMMU, "Error: Input pointers are invalid");
+		return -EINVAL;
+	}
+
+	if (handle == HANDLE_INIT) {
+		CAM_ERR(CAM_SMMU, "Error: Invalid handle");
+		return -EINVAL;
+	}
+
+	/* clean the content from clients */
+	*paddr_ptr = (dma_addr_t)NULL;
+	*len_ptr = (size_t)0;
+
+	idx = GET_SMMU_TABLE_IDX(handle);
+	if (idx < 0 || idx >= iommu_cb_set.cb_num) {
+		CAM_ERR(CAM_SMMU,
+			"Error: handle or index invalid. idx = %d hdl = %x",
+			idx, handle);
+		return -EINVAL;
+	}
+
+	if (!iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't get secure mem from non secure cb");
+		return -EINVAL;
+	}
+
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].handle != handle) {
+		CAM_ERR(CAM_SMMU,
+			"Error: hdl is not valid, table_hdl = %x, hdl = %x",
+			iommu_cb_set.cb_info[idx].handle, handle);
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+	buf_state = cam_smmu_check_secure_fd_in_list(idx,
+		ion_fd,
+		paddr_ptr,
+		len_ptr);
+
+	if (buf_state == CAM_SMMU_BUFF_NOT_EXIST) {
+		CAM_ERR(CAM_SMMU, "ion_fd:%d not in the mapped list", ion_fd);
+		rc = -EINVAL;
+		goto get_addr_end;
+	}
+
+get_addr_end:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_get_stage2_iova);
+
+static int cam_smmu_unmap_validate_params(int handle)
 {
-	int idx, rc;
-	struct cam_dma_buff_info *mapping_info;
+	int idx;
 
 	if (handle == HANDLE_INIT) {
 		CAM_ERR(CAM_SMMU, "Error: Invalid handle");
@@ -1882,7 +2593,30 @@
 		return -EINVAL;
 	}
 
+	return 0;
+}
+
+int cam_smmu_unmap_user_iova(int handle,
+	int ion_fd, enum cam_smmu_region_id region_id)
+{
+	int idx, rc;
+	struct cam_dma_buff_info *mapping_info;
+
+	rc = cam_smmu_unmap_validate_params(handle);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "unmap util validation failure");
+		return rc;
+	}
+
+	idx = GET_SMMU_TABLE_IDX(handle);
 	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't unmap non-secure mem from secure cb");
+		rc = -EINVAL;
+		goto unmap_end;
+	}
+
 	if (iommu_cb_set.cb_info[idx].handle != handle) {
 		CAM_ERR(CAM_SMMU,
 			"Error: hdl is not valid, table_hdl = %x, hdl = %x",
@@ -1891,10 +2625,12 @@
 		goto unmap_end;
 	}
 
-	/* Based on ion fd and index, we can find mapping info of buffer */
+	/* Based on ion_fd & index, we can find mapping info of buffer */
 	mapping_info = cam_smmu_find_mapping_by_ion_index(idx, ion_fd);
+
 	if (!mapping_info) {
-		CAM_ERR(CAM_SMMU, "Error: Invalid params idx = %d, fd = %d",
+		CAM_ERR(CAM_SMMU,
+			"Error: Invalid params idx = %d, fd = %d",
 			idx, ion_fd);
 		rc = -EINVAL;
 		goto unmap_end;
@@ -1910,7 +2646,60 @@
 	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
 	return rc;
 }
-EXPORT_SYMBOL(cam_smmu_unmap_iova);
+EXPORT_SYMBOL(cam_smmu_unmap_user_iova);
+
+int cam_smmu_unmap_kernel_iova(int handle,
+	struct dma_buf *buf, enum cam_smmu_region_id region_id)
+{
+	int idx, rc;
+	struct cam_dma_buff_info *mapping_info;
+
+	rc = cam_smmu_unmap_validate_params(handle);
+	if (rc) {
+		CAM_ERR(CAM_SMMU, "unmap util validation failure");
+		return rc;
+	}
+
+	idx = GET_SMMU_TABLE_IDX(handle);
+	mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+	if (iommu_cb_set.cb_info[idx].is_secure) {
+		CAM_ERR(CAM_SMMU,
+			"Error: can't unmap non-secure mem from secure cb");
+		rc = -EINVAL;
+		goto unmap_end;
+	}
+
+	if (iommu_cb_set.cb_info[idx].handle != handle) {
+		CAM_ERR(CAM_SMMU,
+			"Error: hdl is not valid, table_hdl = %x, hdl = %x",
+			iommu_cb_set.cb_info[idx].handle, handle);
+		rc = -EINVAL;
+		goto unmap_end;
+	}
+
+	/* Based on dma_buf & index, we can find mapping info of buffer */
+	mapping_info = cam_smmu_find_mapping_by_dma_buf(idx, buf);
+
+	if (!mapping_info) {
+		CAM_ERR(CAM_SMMU,
+			"Error: Invalid params idx = %d, dma_buf = %pK",
+			idx, buf);
+		rc = -EINVAL;
+		goto unmap_end;
+	}
+
+	/* Unmapping one buffer from device */
+	CAM_DBG(CAM_SMMU, "SMMU: removing buffer idx = %d", idx);
+	rc = cam_smmu_unmap_buf_and_remove_from_list(mapping_info, idx);
+	if (rc < 0)
+		CAM_ERR(CAM_SMMU, "Error: unmap or remove list fail");
+
+unmap_end:
+	mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+	return rc;
+}
+EXPORT_SYMBOL(cam_smmu_unmap_kernel_iova);
+
 
 int cam_smmu_put_iova(int handle, int ion_fd)
 {
@@ -1983,10 +2772,34 @@
 	}
 
 	if (!list_empty_careful(&iommu_cb_set.cb_info[idx].smmu_buf_list)) {
-		CAM_ERR(CAM_SMMU, "Client %s buffer list is not clean",
+		CAM_ERR(CAM_SMMU, "UMD %s buffer list is not clean",
 			iommu_cb_set.cb_info[idx].name);
-		cam_smmu_print_list(idx);
-		cam_smmu_clean_buffer_list(idx);
+		cam_smmu_print_user_list(idx);
+		cam_smmu_clean_user_buffer_list(idx);
+	}
+
+	if (!list_empty_careful(
+		&iommu_cb_set.cb_info[idx].smmu_buf_kernel_list)) {
+		CAM_ERR(CAM_SMMU, "KMD %s buffer list is not clean",
+			iommu_cb_set.cb_info[idx].name);
+		cam_smmu_print_kernel_list(idx);
+		cam_smmu_clean_kernel_buffer_list(idx);
+	}
+
+	if (&iommu_cb_set.cb_info[idx].is_secure) {
+		if (iommu_cb_set.cb_info[idx].secure_count == 0) {
+			mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+			return -EPERM;
+		}
+
+		iommu_cb_set.cb_info[idx].secure_count--;
+		if (iommu_cb_set.cb_info[idx].secure_count == 0) {
+			iommu_cb_set.cb_info[idx].cb_count = 0;
+			iommu_cb_set.cb_info[idx].handle = HANDLE_INIT;
+		}
+
+		mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+		return 0;
 	}
 
 	iommu_cb_set.cb_info[idx].cb_count = 0;
@@ -2039,6 +2852,7 @@
 
 	cb->dev = dev;
 	cb->is_fw_allocated = false;
+	cb->is_secheap_allocated = false;
 
 	/* Create a pool with 4K granularity for supporting shared memory */
 	if (cb->shared_support) {
@@ -2168,7 +2982,15 @@
 	}
 
 	mem_map_node = of_get_child_by_name(of_node, "iova-mem-map");
+	cb->is_secure = of_property_read_bool(of_node, "qcom,secure-cb");
+
+	/*
+	 * We always expect a memory map node, except when it is a secure
+	 * context bank.
+	 */
 	if (!mem_map_node) {
+		if (cb->is_secure)
+			return 0;
 		CAM_ERR(CAM_SMMU, "iova-mem-map not present");
 		return -EINVAL;
 	}
@@ -2232,6 +3054,11 @@
 			cb->io_info.iova_start = region_start;
 			cb->io_info.iova_len = region_len;
 			break;
+		case CAM_SMMU_REGION_SECHEAP:
+			cb->secheap_support = 1;
+			cb->secheap_info.iova_start = region_start;
+			cb->secheap_info.iova_len = region_len;
+			break;
 		default:
 			CAM_ERR(CAM_SMMU,
 				"Incorrect region id present in DT file: %d",
@@ -2292,6 +3119,12 @@
 		return rc;
 	}
 
+	if (cb->is_secure) {
+		/* increment count to next bank */
+		iommu_cb_set.cb_init_count++;
+		return 0;
+	}
+
 	/* set up the iommu mapping for the  context bank */
 	if (type == CAM_QSMMU) {
 		CAM_ERR(CAM_SMMU, "Error: QSMMU ctx not supported for : %s",
diff --git a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h
index 20445f3..b062258 100644
--- a/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h
+++ b/drivers/media/platform/msm/camera/cam_smmu/cam_smmu_api.h
@@ -45,7 +45,8 @@
 	CAM_SMMU_REGION_FIRMWARE,
 	CAM_SMMU_REGION_SHARED,
 	CAM_SMMU_REGION_SCRATCH,
-	CAM_SMMU_REGION_IO
+	CAM_SMMU_REGION_IO,
+	CAM_SMMU_REGION_SECHEAP
 };
 
 /**
@@ -85,7 +86,7 @@
 int cam_smmu_ops(int handle, enum cam_smmu_ops_param op);
 
 /**
- * @brief       : Maps IOVA for calling driver
+ * @brief       : Maps user space IOVA for calling driver
  *
  * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
  * @param ion_fd: ION handle identifying the memory buffer.
@@ -95,25 +96,54 @@
  *                returned if region_id is CAM_SMMU_REGION_IO. If region_id is
  *                CAM_SMMU_REGION_SHARED, dma_addr is used as an input parameter
  *                which specifies the cpu virtual address to map.
- * @len         : Length of buffer mapped returned by CAM SMMU driver.
+ * @len_ptr     : Length of buffer mapped returned by CAM SMMU driver.
  * @return Status of operation. Negative in case of error. Zero otherwise.
  */
-int cam_smmu_map_iova(int handle,
+int cam_smmu_map_user_iova(int handle,
 	int ion_fd, enum cam_smmu_map_dir dir,
 	dma_addr_t *dma_addr, size_t *len_ptr,
 	enum cam_smmu_region_id region_id);
 
 /**
- * @brief       : Unmaps IOVA for calling driver
+ * @brief        : Maps kernel space IOVA for calling driver
+ *
+ * @param handle : Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param buf    : dma_buf allocated for kernel usage in mem_mgr
+ * @dir          : Mapping direction: which will traslate toDMA_BIDIRECTIONAL,
+ *                 DMA_TO_DEVICE or DMA_FROM_DEVICE
+ * @dma_addr     : Pointer to physical address where mapped address will be
+ *                 returned if region_id is CAM_SMMU_REGION_IO. If region_id is
+ *                 CAM_SMMU_REGION_SHARED, dma_addr is used as an input
+ *                 parameter which specifies the cpu virtual address to map.
+ * @len_ptr      : Length of buffer mapped returned by CAM SMMU driver.
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_map_kernel_iova(int handle,
+	struct dma_buf *buf, enum cam_smmu_map_dir dir,
+	dma_addr_t *dma_addr, size_t *len_ptr,
+	enum cam_smmu_region_id region_id);
+
+/**
+ * @brief       : Unmaps user space IOVA for calling driver
  *
  * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
  * @param ion_fd: ION handle identifying the memory buffer.
  *
  * @return Status of operation. Negative in case of error. Zero otherwise.
  */
-int cam_smmu_unmap_iova(int handle,
-	int ion_fd,
-	enum cam_smmu_region_id region_id);
+int cam_smmu_unmap_user_iova(int handle,
+	int ion_fd, enum cam_smmu_region_id region_id);
+
+/**
+ * @brief       : Unmaps kernel IOVA for calling driver
+ *
+ * @param handle: Handle to identify the CAMSMMU client (VFE, CPP, FD etc.)
+ * @param buf   : dma_buf allocated for the kernel
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_unmap_kernel_iova(int handle,
+	struct dma_buf *buf, enum cam_smmu_region_id region_id);
 
 /**
  * @brief          : Allocates a scratch buffer
@@ -204,6 +234,19 @@
  */
 int cam_smmu_get_iova(int handle, int ion_fd,
 	dma_addr_t *paddr_ptr, size_t *len_ptr);
+
+/**
+ * @brief Maps memory from an ION fd into IOVA space
+ *
+ * @param handle: SMMU handle identifying the secure context bank to map to
+ * @param ion_fd: ION fd of memory to map to
+ * @param paddr_ptr: Pointer IOVA address that will be returned
+ * @param len_ptr: Length of memory mapped
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_get_stage2_iova(int handle, int ion_fd,
+	dma_addr_t *paddr_ptr, size_t *len_ptr);
 /**
  * @brief Unmaps memory from context bank
  *
@@ -217,27 +260,28 @@
 /**
  * @brief Maps secure memory for SMMU handle
  *
- * @param handle: SMMU handle identifying context bank
+ * @param handle: SMMU handle identifying secure context bank
  * @param ion_fd: ION fd to map securely
  * @param dir: DMA Direction for the mapping
+ * @param client: Ion client passed by caller
  * @param dma_addr: Returned IOVA address after mapping
  * @param len_ptr: Length of memory mapped
  *
  * @return Status of operation. Negative in case of error. Zero otherwise.
  */
-int cam_smmu_map_sec_iova(int handle,
-	int ion_fd, enum cam_smmu_map_dir dir,
-	dma_addr_t *dma_addr, size_t *len_ptr);
+int cam_smmu_map_stage2_iova(int handle,
+	int ion_fd, enum cam_smmu_map_dir dir, struct ion_client *client,
+	ion_phys_addr_t *dma_addr, size_t *len_ptr);
 
 /**
  * @brief Unmaps secure memopry for SMMU handle
  *
- * @param handle: SMMU handle identifying context bank
+ * @param handle: SMMU handle identifying secure context bank
  * @param ion_fd: ION fd to unmap
  *
  * @return Status of operation. Negative in case of error. Zero otherwise.
  */
-int cam_smmu_unmap_sec_iova(int handle, int ion_fd);
+int cam_smmu_unmap_stage2_iova(int handle, int ion_fd);
 
 
 /**
@@ -276,4 +320,29 @@
 int cam_smmu_get_region_info(int32_t smmu_hdl,
 	enum cam_smmu_region_id region_id,
 	struct cam_smmu_region_info *region_info);
+
+/**
+ * @brief Reserves secondary heap
+ *
+ * @param smmu_hdl: SMMU handle identifying the context bank
+ * @param iova: IOVA of secondary heap after reservation has completed
+ * @param buf: Allocated dma_buf for secondary heap
+ * @param request_len: Length of secondary heap after reservation has completed
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_reserve_sec_heap(int32_t smmu_hdl,
+	struct dma_buf *buf,
+	dma_addr_t *iova,
+	size_t *request_len);
+
+/**
+ * @brief Releases secondary heap
+ *
+ * @param smmu_hdl: SMMU handle identifying the context bank
+ *
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_release_sec_heap(int32_t smmu_hdl);
+
 #endif /* _CAM_SMMU_API_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index 3fe7b00..e7dcbe7 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -25,22 +25,25 @@
 {
 	int rc;
 	long idx;
+	bool bit;
 
 	do {
 		idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS);
-			if (idx >= CAM_SYNC_MAX_OBJS)
-				return -ENOMEM;
-	} while (!spin_trylock_bh(&sync_dev->row_spinlocks[idx]));
+		if (idx >= CAM_SYNC_MAX_OBJS)
+			return -ENOMEM;
+		bit = test_and_set_bit(idx, sync_dev->bitmap);
+	} while (bit);
 
+	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
 	rc = cam_sync_init_object(sync_dev->sync_table, idx, name);
 	if (rc) {
 		CAM_ERR(CAM_SYNC, "Error: Unable to init row at idx = %ld",
 			idx);
+		clear_bit(idx, sync_dev->bitmap);
 		spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 		return -EINVAL;
 	}
 
-	set_bit(idx, sync_dev->bitmap);
 	*sync_obj = idx;
 	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 
@@ -227,6 +230,8 @@
 				spin_unlock_bh(
 					&sync_dev->row_spinlocks[
 						parent_info->sync_id]);
+				spin_unlock_bh(
+					&sync_dev->row_spinlocks[sync_obj]);
 				return rc;
 			}
 		}
@@ -299,6 +304,12 @@
 {
 	int rc;
 	long idx = 0;
+	bool bit;
+
+	if (!sync_obj || !merged_obj) {
+		CAM_ERR(CAM_SYNC, "Invalid pointer(s)");
+		return -EINVAL;
+	}
 
 	rc = cam_sync_util_validate_merge(sync_obj,
 		num_objs);
@@ -307,48 +318,34 @@
 		return -EINVAL;
 	}
 
-	rc = cam_sync_util_find_and_set_empty_row(sync_dev, &idx);
-	if (rc < 0) {
-		CAM_ERR(CAM_SYNC,
-			"Error: Unable to find empty row, table full");
-		return -EINVAL;
-	}
+	do {
+		idx = find_first_zero_bit(sync_dev->bitmap, CAM_SYNC_MAX_OBJS);
+		if (idx >= CAM_SYNC_MAX_OBJS)
+			return -ENOMEM;
+		bit = test_and_set_bit(idx, sync_dev->bitmap);
+	} while (bit);
 
-	if (idx <= 0 || idx >= CAM_SYNC_MAX_OBJS) {
-		CAM_ERR(CAM_SYNC,
-			"Error: Invalid empty row index returned = %ld", idx);
-		return -EINVAL;
-	}
+	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
 
 	rc = cam_sync_init_group_object(sync_dev->sync_table,
 		idx, sync_obj,
 		num_objs);
-
 	if (rc < 0) {
 		CAM_ERR(CAM_SYNC, "Error: Unable to init row at idx = %ld",
 			idx);
+		clear_bit(idx, sync_dev->bitmap);
+		spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 		return -EINVAL;
 	}
 
 	*merged_obj = idx;
+	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 
 	return 0;
 }
 
 int cam_sync_destroy(int32_t sync_obj)
 {
-	struct sync_table_row *row = NULL;
-
-	if (sync_obj >= CAM_SYNC_MAX_OBJS || sync_obj <= 0)
-		return -EINVAL;
-
-	row = sync_dev->sync_table + sync_obj;
-	if (row->state == CAM_SYNC_STATE_INVALID) {
-		CAM_ERR(CAM_SYNC,
-			"Error: accessing an uninitialized sync obj: idx = %d",
-			sync_obj);
-		return -EINVAL;
-	}
 
 	cam_sync_deinit_object(sync_dev->sync_table, sync_obj);
 	return 0;
@@ -986,7 +983,7 @@
 	set_bit(0, sync_dev->bitmap);
 
 	sync_dev->work_queue = alloc_workqueue(CAM_SYNC_WORKQUEUE_NAME,
-		WQ_HIGHPRI | WQ_UNBOUND, 0);
+		WQ_HIGHPRI | WQ_UNBOUND, 1);
 
 	if (!sync_dev->work_queue) {
 		CAM_ERR(CAM_SYNC,
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h b/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
index ba9bef4..e2a7fcb 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_private.h
@@ -55,6 +55,18 @@
 };
 
 /**
+ * enum sync_list_clean_type - Enum to indicate the type of list clean action
+ * to be peformed, i.e. specific sync ID or all list sync ids.
+ *
+ * @SYNC_CLEAN_ID  : Specific object to be cleaned in the list
+ * @SYNC_CLEAN_ALL : Clean all objects in the list
+ */
+enum sync_list_clean_type {
+	SYNC_LIST_CLEAN_ID,
+	SYNC_LIST_CLEAN_ALL
+};
+
+/**
  * struct sync_parent_info - Single node of information about a parent
  * of a sync object, usually part of the parents linked list
  *
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
index c62aacf..6aa7c23 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.c
@@ -98,18 +98,39 @@
 	return CAM_SYNC_STATE_SIGNALED_ERROR;
 }
 
+static int cam_sync_util_get_group_object_remaining_count(
+	struct sync_table_row *table,
+	uint32_t *sync_objs,
+	uint32_t num_objs)
+{
+	int i;
+	struct sync_table_row *child_row = NULL;
+	int remaining_count = 0;
+
+	if (!table || !sync_objs)
+		return -EINVAL;
+
+	for (i = 0; i < num_objs; i++) {
+		child_row = table + sync_objs[i];
+		if (child_row->state == CAM_SYNC_STATE_ACTIVE)
+			remaining_count++;
+	}
+
+	return remaining_count;
+}
+
 int cam_sync_init_group_object(struct sync_table_row *table,
 	uint32_t idx,
 	uint32_t *sync_objs,
 	uint32_t num_objs)
 {
 	int i;
+	int remaining;
 	struct sync_child_info *child_info;
 	struct sync_parent_info *parent_info;
 	struct sync_table_row *row = table + idx;
 	struct sync_table_row *child_row = NULL;
 
-	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
 	INIT_LIST_HEAD(&row->parents_list);
 
 	INIT_LIST_HEAD(&row->children_list);
@@ -124,8 +145,7 @@
 
 		if (!child_info) {
 			cam_sync_util_cleanup_children_list(
-				&row->children_list);
-			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+				&row->children_list, SYNC_LIST_CLEAN_ALL, 0);
 			return -ENOMEM;
 		}
 
@@ -140,11 +160,11 @@
 		parent_info = kzalloc(sizeof(*parent_info), GFP_ATOMIC);
 		if (!parent_info) {
 			cam_sync_util_cleanup_parents_list(
-				&child_row->parents_list);
+				&child_row->parents_list,
+				SYNC_LIST_CLEAN_ALL, 0);
 			cam_sync_util_cleanup_children_list(
-				&row->children_list);
+				&row->children_list, SYNC_LIST_CLEAN_ALL, 0);
 			spin_unlock_bh(&sync_dev->row_spinlocks[sync_objs[i]]);
-			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 			return -ENOMEM;
 		}
 		parent_info->sync_id = idx;
@@ -156,7 +176,15 @@
 	row->sync_id = idx;
 	row->state = cam_sync_util_get_group_object_state(table,
 		sync_objs, num_objs);
-	row->remaining = num_objs;
+	remaining = cam_sync_util_get_group_object_remaining_count(table,
+		sync_objs, num_objs);
+	if (remaining < 0) {
+		CAM_ERR(CAM_SYNC, "Failed getting remaining count");
+		return -ENODEV;
+	}
+
+	row->remaining = remaining;
+
 	init_completion(&row->signaled);
 	INIT_LIST_HEAD(&row->callback_list);
 	INIT_LIST_HEAD(&row->user_payload_list);
@@ -164,35 +192,137 @@
 	if (row->state != CAM_SYNC_STATE_ACTIVE)
 		complete_all(&row->signaled);
 
-	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 	return 0;
 }
 
 int cam_sync_deinit_object(struct sync_table_row *table, uint32_t idx)
 {
 	struct sync_table_row *row = table + idx;
-	struct sync_child_info *child_info, *temp_child;
+	struct sync_child_info *child_info, *temp_child, *child_copy_info;
 	struct sync_callback_info *sync_cb, *temp_cb;
-	struct sync_parent_info *parent_info, *temp_parent;
+	struct sync_parent_info *parent_info, *temp_parent, *parent_copy_info;
 	struct sync_user_payload *upayload_info, *temp_upayload;
+	struct sync_table_row   *child_row = NULL, *parent_row = NULL;
+	struct list_head child_copy_list, parent_copy_list;
 
 	if (!table || idx <= 0 || idx >= CAM_SYNC_MAX_OBJS)
 		return -EINVAL;
 
 	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
-	clear_bit(idx, sync_dev->bitmap);
-	list_for_each_entry_safe(child_info, temp_child,
-				&row->children_list, list) {
+	if (row->state == CAM_SYNC_STATE_INVALID) {
+		CAM_ERR(CAM_SYNC,
+			"Error: accessing an uninitialized sync obj: idx = %d",
+			idx);
+		spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+		return -EINVAL;
+	}
+
+	/* Objects child and parent objects will be added into this list */
+	INIT_LIST_HEAD(&child_copy_list);
+	INIT_LIST_HEAD(&parent_copy_list);
+
+	list_for_each_entry_safe(child_info, temp_child, &row->children_list,
+		list) {
+		if (child_info->sync_id <= 0)
+			continue;
+
+		child_copy_info = kzalloc(sizeof(*child_copy_info), GFP_ATOMIC);
+		if (!child_copy_info) {
+			/* No free memory, clean up the child_copy_list */
+			while (!list_empty(&child_copy_list)) {
+				child_info = list_first_entry(&child_copy_list,
+					struct sync_child_info, list);
+				list_del_init(&child_info->list);
+				kfree(child_info);
+			}
+			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+			goto deinit;
+		}
+		child_copy_info->sync_id = child_info->sync_id;
+		list_add_tail(&child_copy_info->list, &child_copy_list);
+	}
+
+	list_for_each_entry_safe(parent_info, temp_parent, &row->parents_list,
+		list) {
+		if (parent_info->sync_id <= 0)
+			continue;
+		parent_copy_info = kzalloc(sizeof(*parent_copy_info),
+			GFP_ATOMIC);
+		if (!parent_copy_info) {
+			/* No free memory, clean up the parent_copy_list */
+			while (!list_empty(&parent_copy_list)) {
+				parent_info = list_first_entry(
+					&parent_copy_list,
+					struct sync_parent_info, list);
+				list_del_init(&parent_info->list);
+				kfree(parent_info);
+			}
+			/* No free memory, clean up the child_copy_list */
+			while (!list_empty(&child_copy_list)) {
+				child_info = list_first_entry(&child_copy_list,
+					struct sync_child_info, list);
+				list_del_init(&child_info->list);
+				kfree(child_info);
+			}
+			spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+			goto deinit;
+		}
+		parent_copy_info->sync_id = parent_info->sync_id;
+		list_add_tail(&parent_copy_info->list, &parent_copy_list);
+	}
+
+	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
+	/* Cleanup the child to parent link from child list*/
+	while (!list_empty(&child_copy_list)) {
+		child_info = list_first_entry(&child_copy_list,
+			struct sync_child_info, list);
+		child_row = sync_dev->sync_table + child_info->sync_id;
+		spin_lock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
+		if (child_row->state == CAM_SYNC_STATE_INVALID) {
+			spin_unlock_bh(&sync_dev->row_spinlocks[
+				child_info->sync_id]);
+			list_del_init(&child_info->list);
+			kfree(child_info);
+			continue;
+		}
+
+		cam_sync_util_cleanup_parents_list(&child_row->parents_list,
+			SYNC_LIST_CLEAN_ID, idx);
+
+		spin_unlock_bh(&sync_dev->row_spinlocks[child_info->sync_id]);
 		list_del_init(&child_info->list);
 		kfree(child_info);
 	}
 
-	list_for_each_entry_safe(parent_info, temp_parent,
-				&row->parents_list, list) {
+	/* Cleanup the parent to child link */
+	while (!list_empty(&parent_copy_list)) {
+		parent_info = list_first_entry(&parent_copy_list,
+			struct sync_parent_info, list);
+		parent_row = sync_dev->sync_table + parent_info->sync_id;
+		spin_lock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
+		if (parent_row->state == CAM_SYNC_STATE_INVALID) {
+			spin_unlock_bh(&sync_dev->row_spinlocks[
+				parent_info->sync_id]);
+			list_del_init(&parent_info->list);
+			kfree(parent_info);
+			continue;
+		}
+
+		cam_sync_util_cleanup_children_list(&parent_row->children_list,
+			SYNC_LIST_CLEAN_ID, idx);
+
+		spin_unlock_bh(&sync_dev->row_spinlocks[parent_info->sync_id]);
 		list_del_init(&parent_info->list);
 		kfree(parent_info);
 	}
 
+deinit:
+	spin_lock_bh(&sync_dev->row_spinlocks[idx]);
+	cam_sync_util_cleanup_children_list(&row->children_list,
+		SYNC_LIST_CLEAN_ALL, 0);
+	cam_sync_util_cleanup_parents_list(&row->parents_list,
+		SYNC_LIST_CLEAN_ALL, 0);
+
 	list_for_each_entry_safe(upayload_info, temp_upayload,
 				&row->user_payload_list, list) {
 		list_del_init(&upayload_info->list);
@@ -207,6 +337,7 @@
 
 	row->state = CAM_SYNC_STATE_INVALID;
 	memset(row, 0, sizeof(*row));
+	clear_bit(idx, sync_dev->bitmap);
 	spin_unlock_bh(&sync_dev->row_spinlocks[idx]);
 
 	return 0;
@@ -218,11 +349,14 @@
 		struct sync_callback_info,
 		cb_dispatch_work);
 
+	spin_lock_bh(&sync_dev->row_spinlocks[cb_info->sync_obj]);
+	list_del_init(&cb_info->list);
+	spin_unlock_bh(&sync_dev->row_spinlocks[cb_info->sync_obj]);
+
 	cb_info->callback_func(cb_info->sync_obj,
 		cb_info->status,
 		cb_info->cb_data);
 
-	list_del_init(&cb_info->list);
 	kfree(cb_info);
 }
 
@@ -323,26 +457,48 @@
 	return result;
 }
 
-void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean)
+void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj)
 {
 	struct sync_child_info *child_info = NULL;
 	struct sync_child_info *temp_child_info = NULL;
+	uint32_t                curr_sync_obj;
 
 	list_for_each_entry_safe(child_info,
 			temp_child_info, list_to_clean, list) {
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(child_info->sync_id != sync_obj))
+			continue;
+
+		curr_sync_obj = child_info->sync_id;
 		list_del_init(&child_info->list);
 		kfree(child_info);
+
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(curr_sync_obj == sync_obj))
+			break;
 	}
 }
 
-void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean)
+void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj)
 {
 	struct sync_parent_info *parent_info = NULL;
 	struct sync_parent_info *temp_parent_info = NULL;
+	uint32_t                 curr_sync_obj;
 
 	list_for_each_entry_safe(parent_info,
 			temp_parent_info, list_to_clean, list) {
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(parent_info->sync_id != sync_obj))
+			continue;
+
+		curr_sync_obj = parent_info->sync_id;
 		list_del_init(&parent_info->list);
 		kfree(parent_info);
+
+		if ((list_clean_type == SYNC_LIST_CLEAN_ID) &&
+			(curr_sync_obj == sync_obj))
+			break;
 	}
 }
diff --git a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
index 8b60ce1..1c5c4bf 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync_util.h
@@ -141,17 +141,25 @@
 /**
  * @brief: Function to clean up the children of a sync object
  * @param list_to_clean : List to clean up
+ * @list_clean_type     : Clean specific object or clean all objects
+ * @sync_obj            : Sync object to be clean if list clean type is
+ *                         SYNC_LIST_CLEAN_ID
  *
  * @return None
  */
-void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean);
+void cam_sync_util_cleanup_children_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj);
 
 /**
  * @brief: Function to clean up the parents of a sync object
  * @param list_to_clean : List to clean up
+ * @list_clean_type     : Clean specific object or clean all objects
+ * @sync_obj            : Sync object to be clean if list clean type is
+ *                         SYNC_LIST_CLEAN_ID
  *
  * @return None
  */
-void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean);
+void cam_sync_util_cleanup_parents_list(struct list_head *list_to_clean,
+	uint32_t list_clean_type, uint32_t sync_obj);
 
 #endif /* __CAM_SYNC_UTIL_H__ */
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.c
index 21f90ca..f451155 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.c
@@ -83,6 +83,9 @@
 	case CAM_HFI:
 		name = "CAM-HFI";
 		break;
+	case CAM_OIS:
+		name = "CAM-OIS";
+		break;
 	default:
 		name = "CAM";
 		break;
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
index 7275d56..c0160c4 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
@@ -33,6 +33,8 @@
 #define CAM_UTIL       (1 << 17)
 #define CAM_HFI        (1 << 18)
 #define CAM_CTXT       (1 << 19)
+#define CAM_OIS        (1 << 20)
+#define CAM_RES        (1 << 21)
 
 #define STR_BUFFER_MAX_LENGTH  1024
 
@@ -118,7 +120,7 @@
  * @brief :     This Macro will prevent error print logs with ratelimit
  */
 #define CAM_ERR_RATE_LIMIT(__module, fmt, args...)                 \
-	pr_err_ratelimited("CAM_ERR: %s: %s: %d\n" fmt,            \
+	pr_err_ratelimited("CAM_ERR: %s: %s: %d" fmt "\n",            \
 		cam_get_module_name(__module), __func__,  __LINE__, ##args)
 
 #endif /* _CAM_DEBUG_UTIL_H_ */
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
index 4323358..aecce12 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.c
@@ -17,7 +17,7 @@
 #include "cam_packet_util.h"
 #include "cam_debug_util.h"
 
-static int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
+int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
 	size_t *len)
 {
 	int rc = 0;
@@ -124,7 +124,7 @@
 }
 
 int cam_packet_util_process_patches(struct cam_packet *packet,
-	int32_t iommu_hdl)
+	int32_t iommu_hdl, int32_t sec_mmu_hdl)
 {
 	struct cam_patch_desc *patch_desc = NULL;
 	uint64_t   iova_addr;
@@ -136,6 +136,7 @@
 	size_t     src_buf_size;
 	int        i;
 	int        rc = 0;
+	int32_t    hdl;
 
 	/* process patch descriptor */
 	patch_desc = (struct cam_patch_desc *)
@@ -146,8 +147,11 @@
 			sizeof(struct cam_patch_desc));
 
 	for (i = 0; i < packet->num_patches; i++) {
+
+		hdl = cam_mem_is_secure_buf(patch_desc[i].src_buf_hdl) ?
+			sec_mmu_hdl : iommu_hdl;
 		rc = cam_mem_get_io_buf(patch_desc[i].src_buf_hdl,
-			iommu_hdl, &iova_addr, &src_buf_size);
+			hdl, &iova_addr, &src_buf_size);
 		if (rc < 0) {
 			CAM_ERR(CAM_UTIL, "unable to get src buf address");
 			return rc;
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
index 2fa7585..94d2693 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_packet_util.h
@@ -38,6 +38,21 @@
 	uint32_t blob_type, uint32_t blob_size, uint8_t *blob_data);
 
 /**
+ * cam_packet_util_get_cmd_mem_addr()
+ *
+ * @brief                  Get command buffer address
+ *
+ * @handle:                Command buffer memory handle
+ * @buf_addr:              Command buffer cpu mapped address
+ * @len:                   Command buffer length
+ *
+ * @return:                0 for success
+ *                         -EINVAL for Fail
+ */
+int cam_packet_util_get_cmd_mem_addr(int handle, uint32_t **buf_addr,
+	size_t *len);
+
+/**
  * cam_packet_util_validate_packet()
  *
  * @brief                  Validate the packet
@@ -83,12 +98,14 @@
  *
  * @packet:             Input packet containing Command Buffers and Patches
  * @iommu_hdl:          IOMMU handle of the HW Device that received the packet
+ * @sec_iommu_hdl:      Secure IOMMU handle of the HW Device that
+ *                      received the packet
  *
  * @return:             0: Success
  *                      Negative: Failure
  */
 int cam_packet_util_process_patches(struct cam_packet *packet,
-	int32_t iommu_hdl);
+	int32_t iommu_hdl, int32_t sec_mmu_hdl);
 
 /**
  * cam_packet_util_process_generic_cmd_buffer()
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
index 743dfda..07fb944 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
@@ -196,6 +196,92 @@
 	return rc;
 }
 
+int cam_soc_util_clk_put(struct clk **clk)
+{
+	if (!(*clk)) {
+		CAM_ERR(CAM_UTIL, "Invalid params clk");
+		return -EINVAL;
+	}
+
+	clk_put(*clk);
+	*clk = NULL;
+
+	return 0;
+}
+
+static struct clk *cam_soc_util_option_clk_get(struct device_node *np,
+	int index)
+{
+	struct of_phandle_args clkspec;
+	struct clk *clk;
+	int rc;
+
+	if (index < 0)
+		return ERR_PTR(-EINVAL);
+
+	rc = of_parse_phandle_with_args(np, "clocks-option", "#clock-cells",
+		index, &clkspec);
+	if (rc)
+		return ERR_PTR(rc);
+
+	clk = of_clk_get_from_provider(&clkspec);
+	of_node_put(clkspec.np);
+
+	return clk;
+}
+
+int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info,
+	const char *clk_name, struct clk **clk, int32_t *clk_index,
+	int32_t *clk_rate)
+{
+	int index = 0;
+	int rc = 0;
+	struct device_node *of_node = NULL;
+
+	if (!soc_info || !clk_name || !clk) {
+		CAM_ERR(CAM_UTIL,
+			"Invalid params soc_info %pK clk_name %s clk %pK",
+			soc_info, clk_name, clk);
+		return -EINVAL;
+	}
+
+	of_node = soc_info->dev->of_node;
+
+	index = of_property_match_string(of_node, "clock-names-option",
+		clk_name);
+
+	*clk = cam_soc_util_option_clk_get(of_node, index);
+	if (IS_ERR(*clk)) {
+		CAM_ERR(CAM_UTIL, "No clk named %s found. Dev %s", clk_name,
+			soc_info->dev_name);
+		*clk_index = -1;
+		return -EFAULT;
+	}
+	*clk_index = index;
+
+	rc = of_property_read_u32_index(of_node, "clock-rates-option",
+		index, clk_rate);
+	if (rc) {
+		CAM_ERR(CAM_UTIL,
+			"Error reading clock-rates clk_name %s index %d",
+			clk_name, index);
+		cam_soc_util_clk_put(clk);
+		*clk_rate = 0;
+		return rc;
+	}
+
+	/*
+	 * Option clocks are assumed to be available to single Device here.
+	 * Hence use INIT_RATE instead of NO_SET_RATE.
+	 */
+	*clk_rate = (*clk_rate == 0) ? (int32_t)INIT_RATE : *clk_rate;
+
+	CAM_DBG(CAM_UTIL, "clk_name %s index %d clk_rate %d",
+		clk_name, *clk_index, *clk_rate);
+
+	return 0;
+}
+
 int cam_soc_util_clk_enable(struct clk *clk, const char *clk_name,
 	int32_t clk_rate)
 {
@@ -324,6 +410,13 @@
 
 	of_node = soc_info->dev->of_node;
 
+	if (!of_property_read_bool(of_node, "use-shared-clk")) {
+		CAM_DBG(CAM_UTIL, "No shared clk parameter defined");
+		soc_info->use_shared_clk = false;
+	} else {
+		soc_info->use_shared_clk = true;
+	}
+
 	count = of_property_count_strings(of_node, "clock-names");
 
 	CAM_DBG(CAM_UTIL, "count = %d", count);
@@ -407,7 +500,7 @@
 
 			soc_info->clk_rate[level][j] =
 				(soc_info->clk_rate[level][j] == 0) ?
-				(long)NO_SET_RATE :
+				(int32_t)NO_SET_RATE :
 				soc_info->clk_rate[level][j];
 
 			CAM_DBG(CAM_UTIL, "soc_info->clk_rate[%d][%d] = %d",
@@ -766,7 +859,8 @@
 
 	count = of_property_count_strings(of_node, "reg-names");
 	if (count <= 0) {
-		CAM_ERR(CAM_UTIL, "no reg-names found");
+		CAM_WARN(CAM_UTIL, "no reg-names found for: %s",
+			soc_info->dev_name);
 		count = 0;
 	}
 	soc_info->num_mem_block = count;
@@ -802,7 +896,8 @@
 	rc = of_property_read_string_index(of_node, "interrupt-names", 0,
 		&soc_info->irq_name);
 	if (rc) {
-		CAM_WARN(CAM_UTIL, "No interrupt line present");
+		CAM_WARN(CAM_UTIL, "No interrupt line preset for: %s",
+			soc_info->dev_name);
 		rc = 0;
 	} else {
 		soc_info->irq_line =
@@ -1267,14 +1362,14 @@
 	if (!soc_info)
 		return -EINVAL;
 
+	if (disble_irq)
+		rc |= cam_soc_util_irq_disable(soc_info);
+
 	if (disable_clocks)
 		cam_soc_util_clk_disable_default(soc_info);
 
 	cam_soc_util_regulator_disable_default(soc_info);
 
-	if (disble_irq)
-		rc |= cam_soc_util_irq_disable(soc_info);
-
 	if (soc_info->pinctrl_info.pinctrl &&
 		soc_info->pinctrl_info.gpio_state_suspend)
 		rc = pinctrl_select_state(soc_info->pinctrl_info.pinctrl,
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
index 86c2737..4a87d50 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
@@ -35,7 +35,7 @@
 #define CAM_SOC_MAX_BASE            CAM_SOC_MAX_BLOCK
 
 /* maximum number of device regulator */
-#define CAM_SOC_MAX_REGULATOR       4
+#define CAM_SOC_MAX_REGULATOR       5
 
 /* maximum number of device clock */
 #define CAM_SOC_MAX_CLK             32
@@ -180,6 +180,7 @@
 	struct regulator               *rgltr[CAM_SOC_MAX_REGULATOR];
 	uint32_t                        rgltr_delay[CAM_SOC_MAX_REGULATOR];
 
+	uint32_t                        use_shared_clk;
 	uint32_t                        num_clk;
 	const char                     *clk_name[CAM_SOC_MAX_CLK];
 	struct clk                     *clk[CAM_SOC_MAX_CLK];
@@ -377,6 +378,35 @@
 	int32_t clk_rate);
 
 /**
+ * cam_soc_util_get_option_clk_by_name()
+ *
+ * @brief:              Get reference to optional clk using name
+ *
+ * @soc_info:           Device soc information
+ * @clk_name:           Name of clock to find reference for
+ * @clk:                Clock reference pointer to be filled if Success
+ * @clk_index:          Clk index in the option clk array to be returned
+ * @clk_rate:           Clk rate in the option clk array
+ *
+ * @return:             0: Success
+ *                      Negative: Failure
+ */
+int cam_soc_util_get_option_clk_by_name(struct cam_hw_soc_info *soc_info,
+	const char *clk_name, struct clk **clk, int32_t *clk_index,
+	int32_t *clk_rate);
+
+/**
+ * cam_soc_util_clk_put()
+ *
+ * @brief:              Put clock specified in params
+ *
+ * @clk:                Reference to the Clock that needs to be put
+ *
+ * @return:             Success or failure
+ */
+int cam_soc_util_clk_put(struct clk **clk);
+
+/**
  * cam_soc_util_clk_enable()
  *
  * @brief:              Enable clock specified in params
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_trace.h b/drivers/media/platform/msm/camera/cam_utils/cam_trace.h
index 2e9e61f..90ec566 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_trace.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_trace.h
@@ -71,6 +71,21 @@
 	)
 );
 
+TRACE_EVENT(cam_icp_fw_dbg,
+	TP_PROTO(char *dbg_message),
+	TP_ARGS(dbg_message),
+	TP_STRUCT__entry(
+		__string(dbg_message, dbg_message)
+	),
+	TP_fast_assign(
+		__assign_str(dbg_message, dbg_message);
+	),
+	TP_printk(
+		"%s: ",
+		__get_str(dbg_message)
+	)
+);
+
 TRACE_EVENT(cam_buf_done,
 	TP_PROTO(const char *ctx_type, struct cam_context *ctx,
 		struct cam_ctx_request *req),
@@ -92,15 +107,15 @@
 );
 
 TRACE_EVENT(cam_apply_req,
-	TP_PROTO(const char *entity, struct cam_req_mgr_apply_request *req),
-	TP_ARGS(entity, req),
+	TP_PROTO(const char *entity, uint64_t req_id),
+	TP_ARGS(entity, req_id),
 	TP_STRUCT__entry(
 		__string(entity, entity)
 		__field(uint64_t, req_id)
 	),
 	TP_fast_assign(
 		__assign_str(entity, entity);
-		__entry->req_id = req->request_id;
+		__entry->req_id = req_id;
 	),
 	TP_printk(
 		"%8s: ApplyRequest request=%llu",
@@ -217,6 +232,75 @@
 			__entry->devicemap, __entry->link, __entry->session
 	)
 );
+
+TRACE_EVENT(cam_submit_to_hw,
+	TP_PROTO(const char *entity, uint64_t req_id),
+	TP_ARGS(entity, req_id),
+	TP_STRUCT__entry(
+		__string(entity, entity)
+		__field(uint64_t, req_id)
+	),
+	TP_fast_assign(
+		__assign_str(entity, entity);
+		__entry->req_id = req_id;
+	),
+	TP_printk(
+		"%8s: submit request=%llu",
+			__get_str(entity), __entry->req_id
+	)
+);
+
+TRACE_EVENT(cam_irq_activated,
+	TP_PROTO(const char *entity, uint32_t irq_type),
+	TP_ARGS(entity, irq_type),
+	TP_STRUCT__entry(
+		__string(entity, entity)
+		__field(uint32_t, irq_type)
+	),
+	TP_fast_assign(
+		__assign_str(entity, entity);
+		__entry->irq_type = irq_type;
+	),
+	TP_printk(
+		"%8s: got irq type=%d",
+			__get_str(entity), __entry->irq_type
+	)
+);
+
+TRACE_EVENT(cam_irq_handled,
+	TP_PROTO(const char *entity, uint32_t irq_type),
+	TP_ARGS(entity, irq_type),
+	TP_STRUCT__entry(
+		__string(entity, entity)
+		__field(uint32_t, irq_type)
+	),
+	TP_fast_assign(
+		__assign_str(entity, entity);
+		__entry->irq_type = irq_type;
+	),
+	TP_printk(
+		"%8s: handled irq type=%d",
+			__get_str(entity), __entry->irq_type
+	)
+);
+
+TRACE_EVENT(cam_cdm_cb,
+	TP_PROTO(const char *entity, uint32_t status),
+	TP_ARGS(entity, status),
+	TP_STRUCT__entry(
+		__string(entity, entity)
+		__field(uint32_t, status)
+	),
+	TP_fast_assign(
+		__assign_str(entity, entity);
+		__entry->status = status;
+	),
+	TP_printk(
+		"%8s: cdm cb status=%d",
+			__get_str(entity), __entry->status
+	)
+);
+
 #endif /* _CAM_TRACE_H */
 
 /* This part must be outside protection */
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index f16c1ba..0a01b6f 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -523,13 +523,17 @@
 	int level;
 	struct mpq_demux *mpq_demux = fp->private_data;
 
-	if (count >= 16)
+	if (count == 0 || count >= 16)
 		return -EINVAL;
 
-	ret_count = simple_write_to_buffer(user_str, 16, position, user_buffer,
+	memset(user_str, '\0', sizeof(user_str));
+
+	ret_count = simple_write_to_buffer(user_str, 15, position, user_buffer,
 		count);
 	if (ret_count < 0)
 		return ret_count;
+	else if (ret_count == 0)
+		return -EINVAL;
 
 	ret = kstrtoint(user_str, 0, &level);
 	if (ret)
@@ -873,6 +877,7 @@
 		mpq_demux->sdmx_eos = 0;
 		mpq_demux->sdmx_log_level = SDMX_LOG_NO_PRINT;
 		mpq_demux->ts_packet_timestamp_source = 0;
+		mpq_demux->disable_cache_ops = 1;
 
 		if (mpq_demux->demux.feednum > MPQ_MAX_DMX_FILES) {
 			MPQ_DVB_ERR_PRINT(
@@ -6345,7 +6350,8 @@
 			continue;
 
 		/* Invalidate output buffer before processing the results */
-		mpq_sdmx_invalidate_buffer(mpq_feed);
+		if (!mpq_demux->disable_cache_ops)
+			mpq_sdmx_invalidate_buffer(mpq_feed);
 
 		if (sts->error_indicators & SDMX_FILTER_ERR_MD_BUF_FULL)
 			MPQ_DVB_ERR_PRINT(
@@ -6567,13 +6573,15 @@
 	 * We must flush the buffer before SDMX starts reading from it
 	 * so that it gets a valid data in memory.
 	 */
-	ret = msm_ion_do_cache_op(mpq_demux->ion_client,
-		ion_handle, rbuf->data,
-		rbuf->size, ION_IOC_CLEAN_CACHES);
-	if (ret)
-		MPQ_DVB_ERR_PRINT(
-			"%s: msm_ion_do_cache_op failed, ret = %d\n",
-			__func__, ret);
+	if (!mpq_demux->disable_cache_ops) {
+		ret = msm_ion_do_cache_op(mpq_demux->ion_client,
+					  ion_handle, rbuf->data,
+					  rbuf->size, ION_IOC_CLEAN_CACHES);
+		if (ret)
+			MPQ_DVB_ERR_PRINT(
+				"%s: msm_ion_do_cache_op failed, ret = %d\n",
+				__func__, ret);
+	}
 
 	return mpq_sdmx_process(mpq_demux, &buf_desc, count,
 				read_offset, mpq_demux->demux.ts_packet_size);
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index 0c20a89..a187707 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -543,6 +543,8 @@
 
 	ktime_t last_notification_time;
 	int ts_packet_timestamp_source;
+	/* Disable cache operations on qseecom heap since not supported */
+	int disable_cache_ops;
 };
 
 /**
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
index ae01baf..dc041a7 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.c
@@ -139,6 +139,43 @@
 	return clk_forced_on;
 }
 
+void sde_mdp_halt_vbif_xin(struct sde_mdp_vbif_halt_params *params)
+{
+	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	u32 reg_val;
+	bool forced_on;
+
+	if (!mdata || !params || !params->reg_off_mdp_clk_ctrl) {
+		SDEROT_ERR("null input parameter\n");
+		return;
+	}
+
+	if (params->xin_id > MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1) {
+		SDEROT_ERR("xin_id:%d exceed max limit\n", params->xin_id);
+		return;
+	}
+
+	forced_on = force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
+		params->reg_off_mdp_clk_ctrl, true);
+
+	SDEROT_EVTLOG(forced_on, params->xin_id);
+
+	reg_val = SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL0);
+	SDE_VBIF_WRITE(mdata, MMSS_VBIF_XIN_HALT_CTRL0,
+		reg_val | BIT(params->xin_id));
+
+	/* this is a polling operation */
+	sde_mdp_wait_for_xin_halt(params->xin_id);
+
+	reg_val = SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL0);
+	SDE_VBIF_WRITE(mdata, MMSS_VBIF_XIN_HALT_CTRL0,
+		reg_val & ~BIT(params->xin_id));
+
+	if (forced_on)
+		force_on_xin_clk(params->bit_off_mdp_clk_ctrl,
+			params->reg_off_mdp_clk_ctrl, false);
+}
+
 u32 sde_mdp_get_ot_limit(u32 width, u32 height, u32 pixfmt, u32 fps, u32 is_rd)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
index b1438d5..c85d255 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_base.h
@@ -39,6 +39,7 @@
 #define SDE_MDP_HW_REV_300	SDE_MDP_REV(3, 0, 0)	/* 8998 v1.0 */
 #define SDE_MDP_HW_REV_301	SDE_MDP_REV(3, 0, 1)	/* 8998 v1.1 */
 #define SDE_MDP_HW_REV_400	SDE_MDP_REV(4, 0, 0)	/* sdm845 v1.0 */
+#define SDE_MDP_HW_REV_410	SDE_MDP_REV(4, 1, 0)	/* sdm670 v1.0 */
 
 #define SDE_MDP_VBIF_4_LEVEL_REMAPPER	4
 #define SDE_MDP_VBIF_8_LEVEL_REMAPPER	8
@@ -62,6 +63,18 @@
 	u32 rotsts_busy_mask;
 };
 
+/*
+ * struct sde_mdp_vbif_halt_params: parameters for issue halt request to vbif
+ * @xin_id: xin port number of vbif
+ * @reg_off_mdp_clk_ctrl: reg offset for vbif clock control
+ * @bit_off_mdp_clk_ctrl: bit offset for vbif clock control
+ */
+struct sde_mdp_vbif_halt_params {
+	u32 xin_id;
+	u32 reg_off_mdp_clk_ctrl;
+	u32 bit_off_mdp_clk_ctrl;
+};
+
 enum sde_bus_vote_type {
 	VOTE_INDEX_DISABLE,
 	VOTE_INDEX_19_MHZ,
@@ -157,6 +170,7 @@
 	struct reg_bus_client *reg_bus_clt;
 	bool domain_attached;
 	int domain;
+	u32 sid;
 };
 
 /*
@@ -274,6 +288,8 @@
 
 void sde_mdp_set_ot_limit(struct sde_mdp_set_ot_params *params);
 
+void sde_mdp_halt_vbif_xin(struct sde_mdp_vbif_halt_params *params);
+
 int sde_mdp_init_vbif(void);
 
 #define SDE_VBIF_WRITE(mdata, offset, value) \
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
index 0704602..c7d1074 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
@@ -25,7 +25,6 @@
 #include <linux/msm-bus-board.h>
 #include <linux/regulator/consumer.h>
 #include <linux/dma-direction.h>
-#include <linux/sde_rsc.h>
 #include <soc/qcom/scm.h>
 #include <soc/qcom/secure_buffer.h>
 #include <asm/cacheflush.h>
@@ -45,18 +44,12 @@
 /* Rotator device id to be used in SCM call */
 #define SDE_ROTATOR_DEVICE	21
 
-#ifndef VMID_CP_CAMERA_PREVIEW
-#define VMID_CP_CAMERA_PREVIEW	VMID_INVAL
-#endif
-
-/* SCM call function id to be used for switching between secure and non
+/*
+ * SCM call function id to be used for switching between secure and non
  * secure context
  */
 #define MEM_PROTECT_SD_CTRL_SWITCH 0x18
 
-/* Rotator secure SID */
-#define SDE_ROTATOR_SECURE_SID  0xe01
-
 /* waiting for hw time out, 3 vsync for 30fps*/
 #define ROT_HW_ACQUIRE_TIMEOUT_IN_MS 100
 
@@ -71,6 +64,9 @@
 #define ROT_OVERHEAD_NUMERATOR		27
 #define ROT_OVERHEAD_DENOMINATOR	10000
 
+/* Minimum Rotator Clock value */
+#define ROT_MIN_ROT_CLK			20000000
+
 /* default minimum bandwidth vote */
 #define ROT_ENABLE_BW_VOTE		64000
 /*
@@ -304,7 +300,7 @@
 
 static int sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
 {
-	int ret;
+	int ret = 0;
 
 	if (mgr->regulator_enable == on) {
 		SDEROT_ERR("Regulators already in selected mode on=%d\n", on);
@@ -322,16 +318,13 @@
 	if (mgr->ops_hw_pre_pmevent)
 		mgr->ops_hw_pre_pmevent(mgr, on);
 
-	if (mgr->rsc_client)
-		ret = sde_rsc_client_state_update(mgr->rsc_client,
-				on ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE,
-				NULL, SDE_RSC_INVALID_CRTC_ID, NULL);
-	else
+	if (!sde_rot_mgr_pd_enabled(mgr))
 		ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
 			mgr->module_power.num_vreg, on);
 	if (ret) {
 		pr_err("rotator regulator failed to %s ret:%d client:%d\n",
-		      on ? "enable" : "disable", ret, mgr->rsc_client != NULL);
+		      on ? "enable" : "disable", ret,
+				      sde_rot_mgr_pd_enabled(mgr));
 		return ret;
 	}
 
@@ -601,7 +594,7 @@
 
 	if (test_bit(SDE_CAPS_SEC_ATTACH_DETACH_SMMU,
 		mdata->sde_caps_map)) {
-		sid_info = SDE_ROTATOR_SECURE_SID;
+		sid_info = mdata->sde_smmu[SDE_IOMMU_DOMAIN_ROT_SECURE].sid;
 		desc.arginfo = SCM_ARGS(4, SCM_VAL, SCM_RW, SCM_VAL, SCM_VAL);
 		desc.args[0] = SDE_ROTATOR_DEVICE;
 		desc.args[1] = SCM_BUFFER_PHYS(&sid_info);
@@ -631,9 +624,12 @@
 				return -EINVAL;
 			}
 
-			SDEROT_DBG("scm_call(1) ret=%d, resp=%x",
+			SDEROT_DBG(
+			  "scm(1) sid0x%x dev0x%llx vmid0x%llx ret%d resp%x\n",
+				sid_info, desc.args[0], desc.args[3],
 				ret, resp);
-			SDEROT_EVTLOG(1);
+			SDEROT_EVTLOG(1, sid_info, desc.args[0], desc.args[3],
+					ret, resp);
 		} else if (mdata->sec_cam_en && !enable) {
 			/*
 			 * Disable secure camera operation
@@ -648,12 +644,16 @@
 				MEM_PROTECT_SD_CTRL_SWITCH), &desc);
 			resp = desc.ret[0];
 
-			SDEROT_DBG("scm_call(0): ret=%d, resp=%x",
+			SDEROT_DBG(
+			  "scm(0) sid0x%x dev0x%llx vmid0x%llx ret%d resp%d\n",
+				sid_info, desc.args[0], desc.args[3],
 				ret, resp);
 
 			/* force smmu to reattach */
 			sde_smmu_secure_ctrl(1);
-			SDEROT_EVTLOG(0);
+
+			SDEROT_EVTLOG(0, sid_info, desc.args[0], desc.args[3],
+					ret, resp);
 		}
 	} else {
 		return 0;
@@ -1377,6 +1377,9 @@
 	if (rot_dev->min_rot_clk > perf->clk_rate)
 		perf->clk_rate = rot_dev->min_rot_clk;
 
+	if (mgr->min_rot_clk > perf->clk_rate)
+		perf->clk_rate = mgr->min_rot_clk;
+
 	read_bw =  sde_rotator_calc_buf_bw(in_fmt, config->input.width,
 				config->input.height, max_fps);
 
@@ -2382,11 +2385,15 @@
 	}
 
 	SDEROT_DBG(
-		"reconfig session id=%u in{%u,%u}f:%u out{%u,%u}f:%u fps:%d clk:%lu, bw:%llu\n",
+		"reconfig session id=%u in{%u,%u}f:%x out{%u,%u}f:%x fps:%d clk:%lu bw:%llu\n",
 		config->session_id, config->input.width, config->input.height,
 		config->input.format, config->output.width,
 		config->output.height, config->output.format,
 		config->frame_rate, perf->clk_rate, perf->bw);
+	SDEROT_EVTLOG(config->session_id, config->input.width,
+			config->input.height, config->input.format,
+			config->output.width, config->output.height,
+			config->output.format, config->frame_rate);
 done:
 	return ret;
 }
@@ -2506,6 +2513,45 @@
 	req->finished = true;
 }
 
+void sde_rotator_abort_inline_request(struct sde_rot_mgr *mgr,
+		struct sde_rot_file_private *private,
+		struct sde_rot_entry_container *req)
+{
+	struct kthread_work *commit_work;
+	struct kthread_work *done_work;
+	struct sde_rot_entry *entry;
+	struct sde_rot_hw_resource *hw;
+	int i;
+
+	if (!mgr || !private || !req || !req->entries)
+		return;
+
+	for (i = 0; i < req->count; i++) {
+		entry = &req->entries[i];
+		if (!entry)
+			continue;
+
+		commit_work = &entry->commit_work;
+		done_work = &entry->done_work;
+
+		hw = sde_rotator_get_hw_resource(entry->commitq, entry);
+		if (!hw) {
+			SDEROT_ERR("no hw for the queue\n");
+			SDEROT_EVTLOG(i, req->count, SDE_ROT_EVTLOG_ERROR);
+			continue;
+		}
+
+		SDEROT_EVTLOG(i, req->count);
+
+		mgr->ops_abort_hw(hw, entry);
+
+		sde_rot_mgr_unlock(mgr);
+		kthread_flush_work(commit_work);
+		kthread_flush_work(done_work);
+		sde_rot_mgr_lock(mgr);
+	}
+}
+
 int sde_rotator_handle_request_common(struct sde_rot_mgr *mgr,
 	struct sde_rot_file_private *private,
 	struct sde_rot_entry_container *req)
@@ -2959,16 +3005,7 @@
 {
 	int ret;
 
-	mgr->rsc_client = sde_rsc_client_create(
-			SDE_RSC_INDEX, "sde_rotator_core", false);
-	if (IS_ERR(mgr->rsc_client)) {
-		ret = PTR_ERR(mgr->rsc_client);
-		pr_err("rsc client create returned %d\n", ret);
-		mgr->rsc_client = NULL;
-		return ret;
-	}
-
-	if (!mgr->rsc_client) {
+	if (!sde_rot_mgr_pd_enabled(mgr)) {
 		ret = sde_rotator_get_dt_vreg_data(
 				&pdev->dev, &mgr->module_power);
 		if (ret)
@@ -2996,12 +3033,8 @@
 	sde_rotator_unregister_clk(mgr);
 	sde_rotator_bus_scale_unregister(mgr);
 
-	if (mgr->rsc_client) {
-		sde_rsc_client_destroy(mgr->rsc_client);
-		mgr->rsc_client = NULL;
-	} else {
+	if (!sde_rot_mgr_pd_enabled(mgr))
 		sde_rotator_put_dt_vreg_data(&pdev->dev, &mgr->module_power);
-	}
 }
 
 int sde_rotator_core_init(struct sde_rot_mgr **pmgr,
@@ -3079,8 +3112,11 @@
 	} else if (IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version,
 			SDE_MDP_HW_REV_300) ||
 		IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version,
-			SDE_MDP_HW_REV_400)) {
+			SDE_MDP_HW_REV_400) ||
+		IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version,
+			SDE_MDP_HW_REV_410)) {
 		mgr->ops_hw_init = sde_rotator_r3_init;
+		mgr->min_rot_clk = ROT_MIN_ROT_CLK;
 	} else {
 		ret = -ENODEV;
 		SDEROT_ERR("unsupported sde version %x\n",
@@ -3146,6 +3182,26 @@
 	devm_kfree(dev, mgr);
 }
 
+void sde_rotator_core_dump(struct sde_rot_mgr *mgr)
+{
+	if (!mgr) {
+		SDEROT_ERR("null parameters\n");
+		return;
+	}
+
+	sde_rotator_resource_ctrl(mgr, true);
+	/* dump first snapshot */
+	if (mgr->ops_hw_dump_status)
+		mgr->ops_hw_dump_status(mgr->hw_data);
+
+	SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus", "vbif_dbg_bus");
+
+	/* dump second snapshot for comparison */
+	if (mgr->ops_hw_dump_status)
+		mgr->ops_hw_dump_status(mgr->hw_data);
+	sde_rotator_resource_ctrl(mgr, false);
+}
+
 static void sde_rotator_suspend_cancel_rot_work(struct sde_rot_mgr *mgr)
 {
 	struct sde_rot_file_private *priv, *priv_next;
@@ -3395,3 +3451,38 @@
 
 	return sde_rotator_config_session(mgr, private, config);
 }
+
+/*
+ * sde_rotator_session_validate - validate session
+ */
+int sde_rotator_session_validate(struct sde_rot_mgr *mgr,
+	struct sde_rot_file_private *private,
+	struct sde_rotation_config *config)
+{
+	int ret;
+
+	if (!mgr || !private || !config) {
+		SDEROT_ERR("null parameters\n");
+		return -EINVAL;
+	}
+
+	SDEROT_DBG(
+		"validate session id=%u in{%u,%u}f:%x out{%u,%u}f:%x fps:%d\n",
+		config->session_id, config->input.width, config->input.height,
+		config->input.format, config->output.width,
+		config->output.height, config->output.format,
+		config->frame_rate);
+
+	ret = sde_rotator_verify_config_all(mgr, config);
+	if (ret) {
+		SDEROT_WARN("rotator verify format failed %d\n", ret);
+		return ret;
+	}
+
+	if (config->output.sbuf && mgr->sbuf_ctx != private && mgr->sbuf_ctx) {
+		SDEROT_WARN("too many sbuf sessions\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
index eb8cb88..e23ed7a 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
@@ -385,7 +385,6 @@
  * @reg_bus: register bus configuration state
  * @module_power: power/clock configuration state
  * @regulator_enable: true if foot switch is enabled; false otherwise
- * @rsc_client: pointer to rsc client handle
  * @res_ref_cnt: reference count of how many times resource is requested
  * @rot_enable_clk_cnt: reference count of how many times clock is requested
  * @rot_clk: array of rotator and periphery clocks
@@ -396,6 +395,7 @@
  * @pixel_per_clk: rotator hardware performance in pixel for clock
  * @fudge_factor: fudge factor for clock calculation
  * @overhead: software overhead for offline rotation in msec
+ * @min_rot_clk: minimum rotator clock rate
  * @sbuf_ctx: pointer to sbuf session context
  * @ops_xxx: function pointers of rotator HAL layer
  * @hw_data: private handle of rotator HAL layer
@@ -430,8 +430,6 @@
 	struct sde_module_power module_power;
 	bool regulator_enable;
 
-	struct sde_rsc_client *rsc_client;
-
 	int res_ref_cnt;
 	int rot_enable_clk_cnt;
 	struct sde_rot_clk *rot_clk;
@@ -443,6 +441,7 @@
 	struct sde_mult_factor pixel_per_clk;
 	struct sde_mult_factor fudge_factor;
 	struct sde_mult_factor overhead;
+	unsigned long min_rot_clk;
 
 	struct sde_rot_file_private *sbuf_ctx;
 
@@ -450,6 +449,8 @@
 			struct sde_rot_entry *entry);
 	int (*ops_cancel_hw)(struct sde_rot_hw_resource *hw,
 			struct sde_rot_entry *entry);
+	int (*ops_abort_hw)(struct sde_rot_hw_resource *hw,
+			struct sde_rot_entry *entry);
 	int (*ops_kickoff_entry)(struct sde_rot_hw_resource *hw,
 			struct sde_rot_entry *entry);
 	int (*ops_wait_for_entry)(struct sde_rot_hw_resource *hw,
@@ -477,6 +478,7 @@
 	int (*ops_hw_get_downscale_caps)(struct sde_rot_mgr *mgr, char *caps,
 			int len);
 	int (*ops_hw_get_maxlinewidth)(struct sde_rot_mgr *mgr);
+	void (*ops_hw_dump_status)(struct sde_rot_mgr *mgr);
 
 	void *hw_data;
 };
@@ -569,6 +571,12 @@
 void sde_rotator_core_destroy(struct sde_rot_mgr *mgr);
 
 /*
+ * sde_rotator_core_dump - perform register dump
+ * @mgr: Pointer to rotator manager
+ */
+void sde_rotator_core_dump(struct sde_rot_mgr *mgr);
+
+/*
  * sde_rotator_session_open - open a new rotator per file session
  * @mgr: Pointer to rotator manager
  * @pprivate: Pointer to pointer of the newly initialized per file session
@@ -602,6 +610,17 @@
 	struct sde_rotation_config *config);
 
 /*
+ * sde_rotator_session_validate - validate session configuration
+ * @mgr: Pointer to rotator manager
+ * @private: Pointer to per file session
+ * @config: Pointer to rotator configuration
+ * return: 0 if success; error code otherwise
+ */
+int sde_rotator_session_validate(struct sde_rot_mgr *mgr,
+	struct sde_rot_file_private *private,
+	struct sde_rotation_config *config);
+
+/*
  * sde_rotator_req_init - allocate a new request and initialzie with given
  *	array of rotation items
  * @rot_dev: Pointer to rotator device
@@ -660,6 +679,19 @@
 	struct sde_rot_entry_container *req);
 
 /*
+ * sde_rotator_abort_inline_request - abort inline rotation request after start
+ *	This function allows inline rotation requests to be aborted after
+ *	sde_rotator_req_set_start has already been issued.
+ * @mgr: Pointer to rotator manager
+ * @private: Pointer to rotator manager per file context
+ * @req: Pointer to rotation request
+ * return: none
+ */
+void sde_rotator_abort_inline_request(struct sde_rot_mgr *mgr,
+		struct sde_rot_file_private *private,
+		struct sde_rot_entry_container *req);
+
+/*
  * sde_rotator_handle_request_common - add the given request to rotator
  *	manager and clean up completed requests
  * @rot_dev: Pointer to rotator device
@@ -755,6 +787,15 @@
 	mutex_unlock(&mgr->lock);
 }
 
+/*
+ * sde_rot_mgr_pd_enabled - return true if power domain is enabled
+ * @mgr: Pointer to rotator manager
+ */
+static inline bool sde_rot_mgr_pd_enabled(struct sde_rot_mgr *mgr)
+{
+	return mgr && mgr->device && mgr->device->pm_domain;
+}
+
 #if defined(CONFIG_PM)
 int sde_rotator_runtime_resume(struct device *dev);
 int sde_rotator_runtime_suspend(struct device *dev);
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
index 46f64d2..b9158e1 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.c
@@ -638,18 +638,6 @@
 }
 
 /*
- * sde_rot_dump_panic - Issue evtlog dump and generic panic
- */
-void sde_rot_dump_panic(bool do_panic)
-{
-	sde_rot_evtlog_dump_all();
-	sde_rot_dump_reg_all();
-
-	if (do_panic)
-		panic("sde_rotator");
-}
-
-/*
  * sde_rot_evtlog_tout_handler - log dump timeout handler
  * @queue: boolean indicate putting log dump into queue
  * @name: function name having timeout
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
index 2fc8e3f..fa53083 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_debug.h
@@ -42,7 +42,6 @@
 		SDE_ROT_EVTLOG_TOUT_DATA_LIMITER)
 
 void sde_rot_evtlog(const char *name, int line, int flag, ...);
-void sde_rot_dump_panic(bool do_panic);
 void sde_rot_evtlog_tout_handler(bool queue, const char *name, ...);
 
 struct sde_rotator_device;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index 4ad960d8..523ff5b 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
@@ -1162,7 +1162,6 @@
 		struct sde_rotator_request *request)
 {
 	struct sde_rotator_ctx *ctx;
-	struct sde_rot_entry_container *req;
 
 	if (!request || !request->ctx) {
 		SDEROT_ERR("invalid parameters\n");
@@ -1170,11 +1169,7 @@
 	}
 
 	ctx = request->ctx;
-	req = request->req;
-
-	if (req && req->entries && req->count)
-		ctx->retired_sequence_id =
-				req->entries[req->count - 1].item.sequence_id;
+	ctx->retired_sequence_id = request->sequence_id;
 
 	wake_up(&ctx->wait_queue);
 
@@ -1199,6 +1194,7 @@
 	ctx = request->ctx;
 
 	request->req = NULL;
+	request->sequence_id = 0;
 	request->committed = false;
 	spin_lock(&ctx->list_lock);
 	list_del_init(&request->list);
@@ -1216,17 +1212,14 @@
 static bool sde_rotator_is_request_retired(struct sde_rotator_request *request)
 {
 	struct sde_rotator_ctx *ctx;
-	struct sde_rot_entry_container *req;
 	u32 sequence_id;
 	s32 retire_delta;
 
-	if (!request || !request->ctx || !request->req ||
-			!request->req->entries || !request->req->count)
+	if (!request || !request->ctx)
 		return true;
 
 	ctx = request->ctx;
-	req = request->req;
-	sequence_id = req->entries[req->count - 1].item.sequence_id;
+	sequence_id = request->sequence_id;
 
 	retire_delta = (s32) (ctx->retired_sequence_id - sequence_id);
 
@@ -1447,6 +1440,61 @@
 EXPORT_SYMBOL(sde_rotator_inline_get_pixfmt_caps);
 
 /*
+ * _sde_rotator_inline_cleanup - perform inline related request cleanup
+ *	This function assumes rot_dev->mgr lock has been taken when called.
+ * @handle: Pointer to rotator context
+ * @request: Pointer to rotation request
+ * return: 0 if success; -EAGAIN if cleanup should be retried
+ */
+static int _sde_rotator_inline_cleanup(void *handle,
+		struct sde_rotator_request *request)
+{
+	struct sde_rotator_ctx *ctx;
+	struct sde_rotator_device *rot_dev;
+	int ret;
+
+	if (!handle || !request) {
+		SDEROT_ERR("invalid rotator handle/request\n");
+		return -EINVAL;
+	}
+
+	ctx = handle;
+	rot_dev = ctx->rot_dev;
+
+	if (!rot_dev || !rot_dev->mgr) {
+		SDEROT_ERR("invalid rotator device\n");
+		return -EINVAL;
+	}
+
+	if (request->committed) {
+		/* wait until request is finished */
+		sde_rot_mgr_unlock(rot_dev->mgr);
+		mutex_unlock(&rot_dev->lock);
+		ret = wait_event_timeout(ctx->wait_queue,
+			sde_rotator_is_request_retired(request),
+			msecs_to_jiffies(rot_dev->streamoff_timeout));
+		mutex_lock(&rot_dev->lock);
+		sde_rot_mgr_lock(rot_dev->mgr);
+
+		if (!ret) {
+			SDEROT_ERR("timeout w/o retire s:%d\n",
+					ctx->session_id);
+			SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
+			sde_rotator_abort_inline_request(rot_dev->mgr,
+					ctx->private, request->req);
+			return -EAGAIN;
+		} else if (ret == 1) {
+			SDEROT_ERR("timeout w/ retire s:%d\n", ctx->session_id);
+			SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
+		}
+	}
+
+	sde_rotator_req_finish(rot_dev->mgr, ctx->private, request->req);
+	sde_rotator_retire_request(request);
+	return 0;
+}
+
+/*
  * sde_rotator_inline_commit - commit given rotator command
  * @handle: Pointer to rotator context
  * @cmd: Pointer to rotator command
@@ -1460,9 +1508,10 @@
 	struct sde_rotator_device *rot_dev;
 	struct sde_rotator_request *request = NULL;
 	struct sde_rot_entry_container *req = NULL;
+	struct sde_rotation_config rotcfg;
 	ktime_t *ts;
 	u32 flags = 0;
-	int i, ret;
+	int i, ret = 0;
 
 	if (!handle || !cmd) {
 		SDEROT_ERR("invalid rotator handle/cmd\n");
@@ -1472,13 +1521,13 @@
 	ctx = handle;
 	rot_dev = ctx->rot_dev;
 
-	if (!rot_dev) {
+	if (!rot_dev || !rot_dev->mgr) {
 		SDEROT_ERR("invalid rotator device\n");
 		return -EINVAL;
 	}
 
 	SDEROT_DBG(
-		"s:%d.%u src:(%u,%u,%u,%u)/%ux%u/%c%c%c%c dst:(%u,%u,%u,%u)/%c%c%c%c r:%d f:%d/%d s:%d fps:%u clk:%llu bw:%llu wb:%d vid:%d cmd:%d\n",
+		"s:%d.%u src:(%u,%u,%u,%u)/%ux%u/%c%c%c%c dst:(%u,%u,%u,%u)/%c%c%c%c r:%d f:%d/%d s:%d fps:%u clk:%llu bw:%llu prefill:%llu wb:%d vid:%d cmd:%d\n",
 		ctx->session_id, cmd->sequence_id,
 		cmd->src_rect_x, cmd->src_rect_y,
 		cmd->src_rect_w, cmd->src_rect_h,
@@ -1490,7 +1539,7 @@
 		cmd->dst_pixfmt >> 0, cmd->dst_pixfmt >> 8,
 		cmd->dst_pixfmt >> 16, cmd->dst_pixfmt >> 24,
 		cmd->rot90, cmd->hflip, cmd->vflip, cmd->secure, cmd->fps,
-		cmd->clkrate, cmd->data_bw,
+		cmd->clkrate, cmd->data_bw, cmd->prefill_bw,
 		cmd->dst_writeback, cmd->video_mode, cmd_type);
 	SDEROT_EVTLOG(ctx->session_id, cmd->sequence_id,
 		cmd->src_rect_x, cmd->src_rect_y,
@@ -1498,12 +1547,13 @@
 		cmd->src_pixfmt,
 		cmd->dst_rect_w, cmd->dst_rect_h,
 		cmd->dst_pixfmt,
+		cmd->fps, cmd->clkrate, cmd->data_bw, cmd->prefill_bw,
 		(cmd->rot90 << 0) | (cmd->hflip << 1) | (cmd->vflip << 2) |
 		(cmd->secure << 3) | (cmd->dst_writeback << 4) |
-		(cmd->video_mode << 5),
-		cmd->fps, cmd->clkrate, cmd->data_bw,
-		cmd_type);
+		(cmd->video_mode << 5) |
+		(cmd_type << 24));
 
+	mutex_lock(&rot_dev->lock);
 	sde_rot_mgr_lock(rot_dev->mgr);
 
 	if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE ||
@@ -1584,11 +1634,8 @@
 			ret = -ENOMEM;
 			goto error_init_request;
 		}
-	}
 
-	if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE) {
-		struct sde_rotation_config rotcfg;
-
+		/* initialize session configuration */
 		memset(&rotcfg, 0, sizeof(struct sde_rotation_config));
 		rotcfg.flags = flags;
 		rotcfg.frame_rate = cmd->fps;
@@ -1606,25 +1653,16 @@
 		rotcfg.output.comp_ratio.numer = 1;
 		rotcfg.output.comp_ratio.denom = 1;
 		rotcfg.output.sbuf = true;
+	}
 
-		if (memcmp(&rotcfg, &ctx->rotcfg, sizeof(rotcfg))) {
-			ret = sde_rotator_session_config(rot_dev->mgr,
-					ctx->private, &rotcfg);
-			if (ret) {
-				SDEROT_WARN("fail session config s:%d\n",
-						ctx->session_id);
-				goto error_session_config;
-			}
+	if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE) {
 
-			ctx->rotcfg = rotcfg;
-		}
-
-		ret = sde_rotator_validate_request(rot_dev->mgr, ctx->private,
-				req);
+		ret = sde_rotator_session_validate(rot_dev->mgr,
+				ctx->private, &rotcfg);
 		if (ret) {
-			SDEROT_WARN("fail validate request s:%d\n",
+			SDEROT_WARN("fail session validation s:%d\n",
 					ctx->session_id);
-			goto error_validate_request;
+			goto error_session_validate;
 		}
 
 		devm_kfree(rot_dev->dev, req);
@@ -1632,6 +1670,18 @@
 
 	} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_COMMIT) {
 
+		if (memcmp(&rotcfg, &ctx->rotcfg, sizeof(rotcfg))) {
+			ret = sde_rotator_session_config(rot_dev->mgr,
+					ctx->private, &rotcfg);
+			if (ret) {
+				SDEROT_ERR("fail session config s:%d\n",
+						ctx->session_id);
+				goto error_session_config;
+			}
+
+			ctx->rotcfg = rotcfg;
+		}
+
 		request = list_first_entry_or_null(&ctx->retired_list,
 				struct sde_rotator_request, list);
 		if (!request) {
@@ -1642,6 +1692,7 @@
 		}
 
 		request->req = req;
+		request->sequence_id = req->entries[0].item.sequence_id;
 
 		spin_lock(&ctx->list_lock);
 		list_del_init(&request->list);
@@ -1712,52 +1763,62 @@
 		}
 
 		request = cmd->priv_handle;
-		req = request->req;
 
-		if (request->committed) {
-			/* wait until request is finished */
-			sde_rot_mgr_unlock(rot_dev->mgr);
-			ret = wait_event_timeout(ctx->wait_queue,
-				sde_rotator_is_request_retired(request),
-				msecs_to_jiffies(rot_dev->streamoff_timeout));
-			if (!ret) {
-				SDEROT_ERR("timeout w/o retire s:%d\n",
-						ctx->session_id);
-				SDEROT_EVTLOG(ctx->session_id,
-						SDE_ROT_EVTLOG_ERROR);
-			} else if (ret == 1) {
-				SDEROT_ERR("timeout w/ retire s:%d\n",
-						ctx->session_id);
-				SDEROT_EVTLOG(ctx->session_id,
-						SDE_ROT_EVTLOG_ERROR);
-			}
-			sde_rot_mgr_lock(rot_dev->mgr);
+		/* attempt single retry if first cleanup attempt failed */
+		if (_sde_rotator_inline_cleanup(handle, request) == -EAGAIN)
+			_sde_rotator_inline_cleanup(handle, request);
+
+		cmd->priv_handle = NULL;
+	} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_ABORT) {
+		if (!cmd->priv_handle) {
+			ret = -EINVAL;
+			SDEROT_ERR("invalid private handle\n");
+			goto error_invalid_handle;
 		}
 
-		sde_rotator_req_finish(rot_dev->mgr, ctx->private, req);
-		sde_rotator_retire_request(request);
+		request = cmd->priv_handle;
+		if (!sde_rotator_is_request_retired(request))
+			sde_rotator_abort_inline_request(rot_dev->mgr,
+					ctx->private, request->req);
 	}
 
 	sde_rot_mgr_unlock(rot_dev->mgr);
+	mutex_unlock(&rot_dev->lock);
 	return 0;
 
 error_handle_request:
 	sde_rotator_update_retire_sequence(request);
 	sde_rotator_retire_request(request);
 error_retired_list:
-error_validate_request:
+error_session_validate:
 error_session_config:
 	devm_kfree(rot_dev->dev, req);
 error_invalid_handle:
 error_init_request:
 	sde_rot_mgr_unlock(rot_dev->mgr);
+	mutex_unlock(&rot_dev->lock);
 	return ret;
 }
 EXPORT_SYMBOL(sde_rotator_inline_commit);
 
 void sde_rotator_inline_reg_dump(struct platform_device *pdev)
 {
-	sde_rot_dump_panic(false);
+	struct sde_rotator_device *rot_dev;
+
+	if (!pdev) {
+		SDEROT_ERR("invalid platform device\n");
+		return;
+	}
+
+	rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
+	if (!rot_dev || !rot_dev->mgr) {
+		SDEROT_ERR("invalid rotator device\n");
+		return;
+	}
+
+	sde_rot_mgr_lock(rot_dev->mgr);
+	sde_rotator_core_dump(rot_dev->mgr);
+	sde_rot_mgr_unlock(rot_dev->mgr);
 }
 EXPORT_SYMBOL(sde_rotator_inline_reg_dump);
 
@@ -2183,6 +2244,7 @@
 	if (ret < 0)
 		SDEDEV_ERR(ctx->rot_dev->dev, "fail qbuf s:%d t:%d r:%d\n",
 				ctx->session_id, buf->type, ret);
+	SDEROT_EVTLOG(buf->type, buf->bytesused, buf->length, buf->m.fd, ret);
 
 	return ret;
 }
@@ -2365,6 +2427,7 @@
 	a->pixelaspect.numerator = 1;
 	a->pixelaspect.denominator = 1;
 
+	SDEROT_EVTLOG(format->fmt.pix.width, format->fmt.pix.height, a->type);
 	return 0;
 }
 
@@ -2614,13 +2677,6 @@
 			}
 
 			/**
-			 * Loose any reference to sync fence once we pass
-			 * it to user. Driver does not clean up user
-			 * unclosed fence descriptors.
-			 */
-			vbinfo->fence = NULL;
-
-			/**
 			 * Cache fence descriptor in case user calls this
 			 * ioctl multiple times. Cached value would be stale
 			 * if user duplicated and closed old descriptor.
@@ -2765,6 +2821,18 @@
 }
 #endif
 
+static int sde_rotator_ctrl_subscribe_event(struct v4l2_fh *fh,
+				const struct v4l2_event_subscription *sub)
+{
+	return -EINVAL;
+}
+
+static int sde_rotator_event_unsubscribe(struct v4l2_fh *fh,
+			   const struct v4l2_event_subscription *sub)
+{
+	return -EINVAL;
+}
+
 /* V4l2 ioctl handlers */
 static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = {
 	.vidioc_querycap          = sde_rotator_querycap,
@@ -2789,8 +2857,8 @@
 	.vidioc_s_parm            = sde_rotator_s_parm,
 	.vidioc_default           = sde_rotator_private_ioctl,
 	.vidioc_log_status        = v4l2_ctrl_log_status,
-	.vidioc_subscribe_event   = v4l2_ctrl_subscribe_event,
-	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+	.vidioc_subscribe_event   = sde_rotator_ctrl_subscribe_event,
+	.vidioc_unsubscribe_event = sde_rotator_event_unsubscribe,
 };
 
 /*
@@ -3004,6 +3072,8 @@
 
 	sde_rotator_queue_request(rot_dev->mgr, ctx->private, req);
 	request->req = req;
+	request->sequence_id = item.sequence_id;
+	request->committed = true;
 
 	return 0;
 error_handle_request:
@@ -3012,6 +3082,8 @@
 error_fence_wait:
 error_null_buffer:
 	request->req = NULL;
+	request->sequence_id = 0;
+	request->committed = false;
 	return ret;
 }
 
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
index 5ea9e15..a464a39 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.h
@@ -90,9 +90,12 @@
  * @list: list head for submit/retire list
  * @submit_work: submit work structure
  * @retire_work: retire work structure
- * @request: Pointer to core layer rotator manager request
+ * @req: Pointer to core layer rotator manager request
+ *	 Request can be freed by core layer during sde_rotator_stop_streaming.
+ *	 Avoid dereference in dev layer if possible.
  * @ctx: Pointer to parent context
  * @committed: true if request committed to hardware
+ * @sequence_id: sequence identifier of this request
  */
 struct sde_rotator_request {
 	struct list_head list;
@@ -101,6 +104,7 @@
 	struct sde_rot_entry_container *req;
 	struct sde_rotator_ctx *ctx;
 	bool committed;
+	u32 sequence_id;
 };
 
 /*
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
index 474662e..ba70489 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_inline.h
@@ -27,12 +27,14 @@
  * @SDE_ROTATOR_INLINE_CMD_COMMIT: commit command to hardware
  * @SDE_ROTATOR_INLINE_CMD_START: ready to start inline rotation
  * @SDE_ROTATOR_INLINE_CMD_CLEANUP: cleanup after commit is done
+ * @SDE_ROTATOR_INLINE_CMD_ABORT: abort current commit and reset
  */
 enum sde_rotator_inline_cmd_type {
 	SDE_ROTATOR_INLINE_CMD_VALIDATE,
 	SDE_ROTATOR_INLINE_CMD_COMMIT,
 	SDE_ROTATOR_INLINE_CMD_START,
 	SDE_ROTATOR_INLINE_CMD_CLEANUP,
+	SDE_ROTATOR_INLINE_CMD_ABORT,
 };
 
 /**
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
index 89ad438..40db488 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r1.c
@@ -349,6 +349,12 @@
 	return 0;
 }
 
+static int sde_rotator_abort_hw(struct sde_rot_hw_resource *hw,
+	struct sde_rot_entry *entry)
+{
+	return 0;
+}
+
 static int sde_rotator_kickoff_entry(struct sde_rot_hw_resource *hw,
 	struct sde_rot_entry *entry)
 {
@@ -691,6 +697,7 @@
 	mgr->hw_data = hw_data;
 	mgr->ops_config_hw = sde_rotator_config_hw;
 	mgr->ops_cancel_hw = sde_rotator_cancel_hw;
+	mgr->ops_abort_hw = sde_rotator_abort_hw;
 	mgr->ops_kickoff_entry = sde_rotator_kickoff_entry;
 	mgr->ops_wait_for_entry = sde_rotator_wait_for_entry;
 	mgr->ops_hw_alloc = sde_rotator_hw_alloc_ext;
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 2f2a439..6ecec03 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -37,6 +37,7 @@
 #include "sde_rotator_debug.h"
 
 #define RES_UHD              (3840*2160)
+#define MS_TO_US(t) ((t) * USEC_PER_MSEC)
 
 /* traffic shaping clock ticks = finish_time x 19.2MHz */
 #define TRAFFIC_SHAPE_CLKTICK_14MS   268800
@@ -48,7 +49,13 @@
 #define XIN_WRITEBACK		1
 
 /* wait for at most 2 vsync for lowest refresh rate (24hz) */
-#define KOFF_TIMEOUT		(42 * 32)
+#define KOFF_TIMEOUT		(42 * 8)
+
+/*
+ * When in sbuf mode, select a much longer wait, to allow the other driver
+ * to detect timeouts and abort if necessary.
+ */
+#define KOFF_TIMEOUT_SBUF	(10000)
 
 /* default stream buffer headroom in lines */
 #define DEFAULT_SBUF_HEADROOM	20
@@ -65,35 +72,54 @@
 	do { \
 		SDEROT_DBG("SDEREG.W:[%s:0x%X] <= 0x%X\n", #off, (off),\
 				(u32)(data));\
-		*p++ = REGDMA_OP_REGWRITE | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (data); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_REGWRITE | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_MODIFY(p, off, mask, data) \
 	do { \
 		SDEROT_DBG("SDEREG.M:[%s:0x%X] <= 0x%X\n", #off, (off),\
 				(u32)(data));\
-		*p++ = REGDMA_OP_REGMODIFY | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (mask); \
-		*p++ = (data); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_REGMODIFY | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(mask, p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_BLKWRITE_INC(p, off, len) \
 	do { \
 		SDEROT_DBG("SDEREG.B:[%s:0x%X:0x%X]\n", #off, (off),\
 				(u32)(len));\
-		*p++ = REGDMA_OP_BLKWRITE_INC | \
-			((off) & REGDMA_ADDR_OFFSET_MASK); \
-		*p++ = (len); \
+		writel_relaxed_no_log( \
+				(REGDMA_OP_BLKWRITE_INC | \
+				 ((off) & REGDMA_ADDR_OFFSET_MASK)), \
+				p); \
+		p += sizeof(u32); \
+		writel_relaxed_no_log(len, p); \
+		p += sizeof(u32); \
 	} while (0)
 
 #define SDE_REGDMA_BLKWRITE_DATA(p, data) \
 	do { \
 		SDEROT_DBG("SDEREG.I:[:] <= 0x%X\n", (u32)(data));\
-		*(p) = (data); \
-		(p)++; \
+		writel_relaxed_no_log(data, p); \
+		p += sizeof(u32); \
+	} while (0)
+
+#define SDE_REGDMA_READ(p, data) \
+	do { \
+		data = readl_relaxed_no_log(p); \
+		p += sizeof(u32); \
 	} while (0)
 
 /* Macro for directly accessing mapped registers */
@@ -107,6 +133,9 @@
 #define SDE_ROTREG_READ(base, off) \
 	readl_relaxed(base + (off))
 
+#define SDE_ROTTOP_IN_OFFLINE_MODE(_rottop_op_mode_) \
+	(((_rottop_op_mode_) & ROTTOP_OP_MODE_ROT_OUT_MASK) == 0)
+
 static const u32 sde_hw_rotator_v3_inpixfmts[] = {
 	SDE_PIX_FMT_XRGB_8888,
 	SDE_PIX_FMT_ARGB_8888,
@@ -511,6 +540,8 @@
 		SDE_ROT_REGDUMP_READ },
 	{ "SDEROT_VBIF_NRT", SDE_ROT_VBIF_NRT_OFFSET, 0x590,
 		SDE_ROT_REGDUMP_VBIF },
+	{ "SDEROT_REGDMA_RESET", ROTTOP_SW_RESET_OVERRIDE, 0,
+		SDE_ROT_REGDUMP_WRITE },
 };
 
 struct sde_rot_cdp_params {
@@ -646,13 +677,127 @@
 	}
 }
 
+static void sde_hw_rotator_halt_vbif_xin_client(void)
+{
+	struct sde_mdp_vbif_halt_params halt_params;
+
+	memset(&halt_params, 0, sizeof(struct sde_mdp_vbif_halt_params));
+	halt_params.xin_id = XIN_SSPP;
+	halt_params.reg_off_mdp_clk_ctrl = MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+	halt_params.bit_off_mdp_clk_ctrl =
+		MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN0;
+	sde_mdp_halt_vbif_xin(&halt_params);
+
+	memset(&halt_params, 0, sizeof(struct sde_mdp_vbif_halt_params));
+	halt_params.xin_id = XIN_WRITEBACK;
+	halt_params.reg_off_mdp_clk_ctrl = MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0;
+	halt_params.bit_off_mdp_clk_ctrl =
+		MMSS_VBIF_NRT_VBIF_CLK_FORCE_CTRL0_XIN1;
+	sde_mdp_halt_vbif_xin(&halt_params);
+}
+
 /**
- * sde_hw_rotator_dump_status - Dump hw rotator status on error
+ * sde_hw_rotator_reset - Reset rotator hardware
+ * @rot: pointer to hw rotator
+ * @ctx: pointer to current rotator context during the hw hang (optional)
+ */
+static int sde_hw_rotator_reset(struct sde_hw_rotator *rot,
+		struct sde_hw_rotator_context *ctx)
+{
+	struct sde_hw_rotator_context *rctx = NULL;
+	u32 int_mask = (REGDMA_INT_0_MASK | REGDMA_INT_1_MASK |
+			REGDMA_INT_2_MASK);
+	u32 last_ts[ROT_QUEUE_MAX] = {0,};
+	u32 latest_ts;
+	int elapsed_time, t;
+	int i, j;
+	unsigned long flags;
+
+	if (!rot) {
+		SDEROT_ERR("NULL rotator\n");
+		return -EINVAL;
+	}
+
+	/* sw reset the hw rotator */
+	SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_SW_RESET_OVERRIDE, 1);
+	usleep_range(MS_TO_US(10), MS_TO_US(20));
+	SDE_ROTREG_WRITE(rot->mdss_base, ROTTOP_SW_RESET_OVERRIDE, 0);
+
+	/* halt vbif xin client to ensure no pending transaction */
+	sde_hw_rotator_halt_vbif_xin_client();
+
+	/* if no ctx is specified, skip ctx wake up */
+	if (!ctx)
+		return 0;
+
+	if (ctx->q_id >= ROT_QUEUE_MAX) {
+		SDEROT_ERR("context q_id out of range: %d\n", ctx->q_id);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&rot->rotisr_lock, flags);
+
+	/* update timestamp register with current context */
+	last_ts[ctx->q_id] = ctx->timestamp;
+	sde_hw_rotator_update_swts(rot, ctx, ctx->timestamp);
+	SDEROT_EVTLOG(ctx->timestamp);
+
+	/*
+	 * Search for any pending rot session, and look for last timestamp
+	 * per hw queue.
+	 */
+	for (i = 0; i < ROT_QUEUE_MAX; i++) {
+		latest_ts = atomic_read(&rot->timestamp[i]);
+		latest_ts &= SDE_REGDMA_SWTS_MASK;
+		elapsed_time = sde_hw_rotator_elapsed_swts(latest_ts,
+			last_ts[i]);
+
+		for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+			rctx = rot->rotCtx[i][j];
+			if (rctx && rctx != ctx) {
+				rctx->last_regdma_isr_status = int_mask;
+				rctx->last_regdma_timestamp  = rctx->timestamp;
+
+				t = sde_hw_rotator_elapsed_swts(latest_ts,
+							rctx->timestamp);
+				if (t < elapsed_time) {
+					elapsed_time = t;
+					last_ts[i] = rctx->timestamp;
+					sde_hw_rotator_update_swts(rot, rctx,
+							last_ts[i]);
+				}
+
+				SDEROT_DBG("rotctx[%d][%d], ts:%d\n",
+						i, j, rctx->timestamp);
+				SDEROT_EVTLOG(i, j, rctx->timestamp,
+						last_ts[i]);
+			}
+		}
+	}
+
+	/* Finally wakeup all pending rotator context in queue */
+	for (i = 0; i < ROT_QUEUE_MAX; i++) {
+		for (j = 0; j < SDE_HW_ROT_REGDMA_TOTAL_CTX; j++) {
+			rctx = rot->rotCtx[i][j];
+			if (rctx && rctx != ctx)
+				wake_up_all(&rctx->regdma_waitq);
+		}
+	}
+
+	spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+	return 0;
+}
+
+/**
+ * _sde_hw_rotator_dump_status - Dump hw rotator status on error
  * @rot: Pointer to hw rotator
  */
-static void sde_hw_rotator_dump_status(struct sde_hw_rotator *rot)
+static void _sde_hw_rotator_dump_status(struct sde_hw_rotator *rot,
+		u32 *ubwcerr)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
+	u32 reg = 0;
 
 	SDEROT_ERR(
 		"op_mode = %x, int_en = %x, int_status = %x\n",
@@ -681,21 +826,51 @@
 		SDE_ROTREG_READ(rot->mdss_base,
 			REGDMA_CSR_REGDMA_FSM_STATE));
 
+	SDEROT_ERR("rottop: op_mode = %x, status = %x, clk_status = %x\n",
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_OP_MODE),
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS),
+		SDE_ROTREG_READ(rot->mdss_base, ROTTOP_CLK_STATUS));
+
+	reg = SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS);
+	if (ubwcerr)
+		*ubwcerr = reg;
 	SDEROT_ERR(
-		"UBWC decode status = %x, UBWC encode status = %x\n",
-		SDE_ROTREG_READ(rot->mdss_base, ROT_SSPP_UBWC_ERROR_STATUS),
+		"UBWC decode status = %x, UBWC encode status = %x\n", reg,
 		SDE_ROTREG_READ(rot->mdss_base, ROT_WB_UBWC_ERROR_STATUS));
 
 	SDEROT_ERR("VBIF XIN HALT status = %x VBIF AXI HALT status = %x\n",
 		SDE_VBIF_READ(mdata, MMSS_VBIF_XIN_HALT_CTRL1),
 		SDE_VBIF_READ(mdata, MMSS_VBIF_AXI_HALT_CTRL1));
 
-	SDEROT_ERR(
-		"sbuf_status_plane0 = %x, sbuf_status_plane1 = %x\n",
-		SDE_ROTREG_READ(rot->mdss_base,
-			ROT_WB_SBUF_STATUS_PLANE0),
-		SDE_ROTREG_READ(rot->mdss_base,
-			ROT_WB_SBUF_STATUS_PLANE1));
+	SDEROT_ERR("sspp unpack wr: plane0 = %x, plane1 = %x, plane2 = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FETCH_SMP_WR_PLANE2));
+	SDEROT_ERR("sspp unpack rd: plane0 = %x, plane1 = %x, plane2 = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+					ROT_SSPP_SMP_UNPACK_RD_PLANE2));
+	SDEROT_ERR("sspp: unpack_ln = %x, unpack_blk = %x, fill_lvl = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_UNPACK_LINE_COUNT),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_UNPACK_BLK_COUNT),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_SSPP_FILL_LEVELS));
+
+	SDEROT_ERR("wb: sbuf0 = %x, sbuf1 = %x, sys_cache = %x\n",
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SBUF_STATUS_PLANE0),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SBUF_STATUS_PLANE1),
+			SDE_ROTREG_READ(rot->mdss_base,
+				ROT_WB_SYS_CACHE_MODE));
 }
 
 /**
@@ -851,7 +1026,7 @@
 static void sde_hw_rotator_setup_timestamp_packet(
 		struct sde_hw_rotator_context *ctx, u32 mask, u32 swts)
 {
-	u32 *wrptr;
+	char __iomem *wrptr;
 
 	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
@@ -908,7 +1083,7 @@
 		struct sde_rot_cdp_params *params)
 {
 	int reg_val;
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	if (!params->enable) {
 		SDE_REGDMA_WRITE(wrptr, params->offset, 0x0);
@@ -942,7 +1117,7 @@
 static void sde_hw_rotator_setup_qos_lut_wr(struct sde_hw_rotator_context *ctx)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	/* Offline rotation setting */
 	if (!ctx->sbuf_mode) {
@@ -1000,7 +1175,7 @@
 static void sde_hw_rotator_setup_qos_lut_rd(struct sde_hw_rotator_context *ctx)
 {
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
+	char __iomem *wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	/* Offline rotation setting */
 	if (!ctx->sbuf_mode) {
@@ -1070,7 +1245,7 @@
 	struct sde_mdp_data *data;
 	struct sde_rot_cdp_params cdp_params = {0};
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32 opmode = 0;
 	u32 chroma_samp = 0;
 	u32 src_format = 0;
@@ -1081,8 +1256,10 @@
 	int i;
 
 	if (ctx->rot->mode == ROT_REGDMA_ON) {
-		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_INT_EN,
-				REGDMA_INT_MASK);
+		if (rot->irq_num >= 0)
+			SDE_ROTREG_WRITE(rot->mdss_base,
+					REGDMA_CSR_REGDMA_INT_EN,
+					REGDMA_INT_MASK);
 		SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_CSR_REGDMA_OP_MODE,
 				REGDMA_EN);
 	}
@@ -1289,7 +1466,7 @@
 	struct sde_rot_data_type *mdata = sde_rot_get_mdata();
 	struct sde_mdp_format_params *fmt;
 	struct sde_rot_cdp_params cdp_params = {0};
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32 pack = 0;
 	u32 dst_format = 0;
 	u32 no_partial_writes = 0;
@@ -1435,8 +1612,7 @@
 	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	/* setup traffic shaper for 4k 30fps content or if prefill_bw is set */
-	if (!ctx->sbuf_mode &&
-			(ctx->is_traffic_shaping || cfg->prefill_bw)) {
+	if (ctx->is_traffic_shaping || cfg->prefill_bw) {
 		u32 bw;
 
 		/*
@@ -1458,7 +1634,7 @@
 
 		/* use prefill bandwidth instead if specified */
 		if (cfg->prefill_bw)
-			bw = DIV_ROUND_UP(cfg->prefill_bw,
+			bw = DIV_ROUND_UP_SECTOR_T(cfg->prefill_bw,
 					TRAFFIC_SHAPE_VSYNC_CLK);
 
 		if (bw > 0xFF)
@@ -1487,13 +1663,18 @@
 		enum sde_rot_queue_prio queue_id)
 {
 	struct sde_hw_rotator *rot = ctx->rot;
-	u32 *wrptr;
-	u32 *rdptr;
-	u8 *addr;
+	char __iomem *wrptr;
+	char __iomem *mem_rdptr;
+	char __iomem *addr;
 	u32 mask;
+	u32 cmd0, cmd1, cmd2;
 	u32 blksize;
 
-	rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
+	/*
+	 * when regdma is not using, the regdma segment is just a normal
+	 * DRAM, and not an iomem.
+	 */
+	mem_rdptr = sde_hw_rotator_get_regdma_segment_base(ctx);
 	wrptr = sde_hw_rotator_get_regdma_segment(ctx);
 
 	if (rot->irq_num >= 0) {
@@ -1511,54 +1692,65 @@
 	SDEROT_DBG("BEGIN %d\n", ctx->timestamp);
 	/* Write all command stream to Rotator blocks */
 	/* Rotator will start right away after command stream finish writing */
-	while (rdptr < wrptr) {
-		u32 op = REGDMA_OP_MASK & *rdptr;
+	while (mem_rdptr < wrptr) {
+		u32 op = REGDMA_OP_MASK & readl_relaxed_no_log(mem_rdptr);
 
 		switch (op) {
 		case REGDMA_OP_NOP:
 			SDEROT_DBG("NOP\n");
-			rdptr++;
+			mem_rdptr += sizeof(u32);
 			break;
 		case REGDMA_OP_REGWRITE:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("REGW %6.6x %8.8x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			writel_relaxed(*rdptr++, addr);
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			writel_relaxed(cmd1, addr);
 			break;
 		case REGDMA_OP_REGMODIFY:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
+			SDE_REGDMA_READ(mem_rdptr, cmd2);
 			SDEROT_DBG("REGM %6.6x %8.8x %8.8x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1], rdptr[2]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1, cmd2);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			mask = *rdptr++;
-			writel_relaxed((readl_relaxed(addr) & mask) | *rdptr++,
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			mask = cmd1;
+			writel_relaxed((readl_relaxed(addr) & mask) | cmd2,
 					addr);
 			break;
 		case REGDMA_OP_BLKWRITE_SINGLE:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("BLKWS %6.6x %6.6x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			blksize = *rdptr++;
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			blksize = cmd1;
 			while (blksize--) {
-				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
-				writel_relaxed(*rdptr++, addr);
+				SDE_REGDMA_READ(mem_rdptr, cmd0);
+				SDEROT_DBG("DATA %8.8x\n", cmd0);
+				writel_relaxed(cmd0, addr);
 			}
 			break;
 		case REGDMA_OP_BLKWRITE_INC:
+			SDE_REGDMA_READ(mem_rdptr, cmd0);
+			SDE_REGDMA_READ(mem_rdptr, cmd1);
 			SDEROT_DBG("BLKWI %6.6x %6.6x\n",
-					rdptr[0] & REGDMA_ADDR_OFFSET_MASK,
-					rdptr[1]);
+					cmd0 & REGDMA_ADDR_OFFSET_MASK,
+					cmd1);
 			addr =  rot->mdss_base +
-				(*rdptr++ & REGDMA_ADDR_OFFSET_MASK);
-			blksize = *rdptr++;
+				(cmd0 & REGDMA_ADDR_OFFSET_MASK);
+			blksize = cmd1;
 			while (blksize--) {
-				SDEROT_DBG("DATA %8.8x\n", rdptr[0]);
-				writel_relaxed(*rdptr++, addr);
+				SDE_REGDMA_READ(mem_rdptr, cmd0);
+				SDEROT_DBG("DATA %8.8x\n", cmd0);
+				writel_relaxed(cmd0, addr);
 				addr += 4;
 			}
 			break;
@@ -1567,7 +1759,7 @@
 			 * Skip data for now for unregonized OP mode
 			 */
 			SDEROT_DBG("UNDEFINED\n");
-			rdptr++;
+			mem_rdptr += sizeof(u32);
 			break;
 		}
 	}
@@ -1585,11 +1777,11 @@
 		enum sde_rot_queue_prio queue_id)
 {
 	struct sde_hw_rotator *rot = ctx->rot;
-	u32 *wrptr;
+	char __iomem *wrptr;
 	u32  regdmaSlot;
 	u32  offset;
-	long length;
-	long ts_length;
+	u32  length;
+	u32  ts_length;
 	u32  enableInt;
 	u32  swts = 0;
 	u32  mask = 0;
@@ -1608,15 +1800,15 @@
 	 * Start REGDMA with command offset and size
 	 */
 	regdmaSlot = sde_hw_rotator_get_regdma_ctxidx(ctx);
-	length = ((long)wrptr - (long)ctx->regdma_base) / 4;
-	offset = (u32)(ctx->regdma_base - (u32 *)(rot->mdss_base +
-				REGDMA_RAM_REGDMA_CMD_RAM));
+	length = (wrptr - ctx->regdma_base) / 4;
+	offset = (ctx->regdma_base - (rot->mdss_base +
+				REGDMA_RAM_REGDMA_CMD_RAM)) / sizeof(u32);
 	enableInt = ((ctx->timestamp & 1) + 1) << 30;
 	trig_sel = ctx->sbuf_mode ? REGDMA_CMD_TRIG_SEL_MDP_FLUSH :
 			REGDMA_CMD_TRIG_SEL_SW_START;
 
 	SDEROT_DBG(
-		"regdma(%d)[%d] <== INT:0x%X|length:%ld|offset:0x%X, ts:%X\n",
+		"regdma(%d)[%d] <== INT:0x%X|length:%d|offset:0x%X, ts:%X\n",
 		queue_id, regdmaSlot, enableInt, length, offset,
 		ctx->timestamp);
 
@@ -1640,17 +1832,22 @@
 		mask = ~(SDE_REGDMA_SWTS_MASK << SDE_REGDMA_SWTS_SHIFT);
 	}
 
+	SDEROT_EVTLOG(ctx->timestamp, queue_id, length, offset, ctx->sbuf_mode);
+
 	/* timestamp update can only be used in offline multi-context mode */
 	if (!ctx->sbuf_mode) {
 		/* Write timestamp after previous rotator job finished */
 		sde_hw_rotator_setup_timestamp_packet(ctx, mask, swts);
 		offset += length;
 		ts_length = sde_hw_rotator_get_regdma_segment(ctx) - wrptr;
+		ts_length /= sizeof(u32);
 		WARN_ON((length + ts_length) > SDE_HW_ROT_REGDMA_SEG_SIZE);
 
 		/* ensure command packet is issue before the submit command */
 		wmb();
 
+		SDEROT_EVTLOG(queue_id, enableInt, ts_length, offset);
+
 		if (queue_id == ROT_QUEUE_HIGH_PRIORITY) {
 			SDE_ROTREG_WRITE(rot->mdss_base,
 					REGDMA_CSR_REGDMA_QUEUE_0_SUBMIT,
@@ -1687,6 +1884,8 @@
 	if (rot->irq_num >= 0) {
 		SDEROT_DBG("Wait for Rotator completion\n");
 		rc = wait_for_completion_timeout(&ctx->rot_comp,
+				ctx->sbuf_mode ?
+				msecs_to_jiffies(KOFF_TIMEOUT_SBUF) :
 				msecs_to_jiffies(rot->koff_timeout));
 
 		spin_lock_irqsave(&rot->rotisr_lock, flags);
@@ -1745,12 +1944,14 @@
 {
 	struct sde_hw_rotator *rot = ctx->rot;
 	int rc = 0;
+	bool abort;
 	u32 status;
 	u32 last_isr;
 	u32 last_ts;
 	u32 int_id;
 	u32 swts;
 	u32 sts = 0;
+	u32 ubwcerr = 0;
 	unsigned long flags;
 
 	if (rot->irq_num >= 0) {
@@ -1758,6 +1959,8 @@
 				ctx, ctx->timestamp);
 		rc = wait_event_timeout(ctx->regdma_waitq,
 				!sde_hw_rotator_pending_swts(rot, ctx, &swts),
+				ctx->sbuf_mode ?
+				msecs_to_jiffies(KOFF_TIMEOUT_SBUF) :
 				msecs_to_jiffies(rot->koff_timeout));
 
 		ATRACE_INT("sde_rot_done", 0);
@@ -1765,18 +1968,19 @@
 
 		last_isr = ctx->last_regdma_isr_status;
 		last_ts  = ctx->last_regdma_timestamp;
+		abort    = ctx->abort;
 		status   = last_isr & REGDMA_INT_MASK;
 		int_id   = last_ts & 1;
 		SDEROT_DBG("INT status:0x%X, INT id:%d, timestamp:0x%X\n",
 				status, int_id, last_ts);
 
-		if (rc == 0 || (status & REGDMA_INT_ERR_MASK)) {
+		if (rc == 0 || (status & REGDMA_INT_ERR_MASK) || abort) {
 			bool pending;
 
 			pending = sde_hw_rotator_pending_swts(rot, ctx, &swts);
 			SDEROT_ERR(
-				"Timeout wait for regdma interrupt status, ts:0x%X/0x%X pending:%d\n",
-				ctx->timestamp, swts, pending);
+				"Timeout wait for regdma interrupt status, ts:0x%X/0x%X, pending:%d, abort:%d\n",
+				ctx->timestamp, swts, pending, abort);
 
 			if (status & REGDMA_WATCHDOG_INT)
 				SDEROT_ERR("REGDMA watchdog interrupt\n");
@@ -1787,8 +1991,26 @@
 			else if (status & REGDMA_INVALID_CMD)
 				SDEROT_ERR("REGDMA invalid command\n");
 
-			sde_hw_rotator_dump_status(rot);
-			status = ROT_ERROR_BIT;
+			_sde_hw_rotator_dump_status(rot, &ubwcerr);
+
+			if (ubwcerr || abort) {
+				/*
+				 * Perform recovery for ROT SSPP UBWC decode
+				 * error.
+				 * - SW reset rotator hw block
+				 * - reset TS logic so all pending rotation
+				 *   in hw queue got done signalled
+				 */
+				spin_unlock_irqrestore(&rot->rotisr_lock,
+						flags);
+				if (!sde_hw_rotator_reset(rot, ctx))
+					status = REGDMA_INCOMPLETE_CMD;
+				else
+					status = ROT_ERROR_BIT;
+				spin_lock_irqsave(&rot->rotisr_lock, flags);
+			} else {
+				status = ROT_ERROR_BIT;
+			}
 		} else {
 			if (rc == 1)
 				SDEROT_WARN(
@@ -1814,12 +2036,12 @@
 		if (last_isr & REGDMA_INT_ERR_MASK) {
 			SDEROT_ERR("Rotator error, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else if (pending) {
 			SDEROT_ERR("Rotator timeout, ts:0x%X/0x%X status:%x\n",
 				ctx->timestamp, swts, last_isr);
-			sde_hw_rotator_dump_status(rot);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			status = ROT_ERROR_BIT;
 		} else {
 			status = 0;
@@ -1829,7 +2051,7 @@
 				last_isr);
 	}
 
-	sts = (status & ROT_ERROR_BIT) ? -ENODEV : 0;
+	sts = (status & (ROT_ERROR_BIT | REGDMA_INCOMPLETE_CMD)) ? -ENODEV : 0;
 
 	if (status & ROT_ERROR_BIT)
 		SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
@@ -1981,7 +2203,7 @@
 {
 	struct sde_hw_rotator *rot;
 	u32 l_ts, h_ts, swts, hwts;
-	u32 rotsts, regdmasts;
+	u32 rotsts, regdmasts, rotopmode;
 
 	/*
 	 * Check last HW timestamp with SW timestamp before power off event.
@@ -2006,19 +2228,37 @@
 		regdmasts = SDE_ROTREG_READ(rot->mdss_base,
 				REGDMA_CSR_REGDMA_BLOCK_STATUS);
 		rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+		rotopmode = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_OP_MODE);
 
 		SDEROT_DBG(
-			"swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
-				swts, hwts, regdmasts, rotsts);
-		SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
+			"swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x, rottop-opmode:0x%x\n",
+				swts, hwts, regdmasts, rotsts, rotopmode);
+		SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts, rotopmode);
 
 		if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
 					(rotsts & ROT_STATUS_MASK))) {
 			SDEROT_ERR(
 				"Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
 				swts, hwts, regdmasts, rotsts);
+			_sde_hw_rotator_dump_status(rot, NULL);
 			SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
 					"vbif_dbg_bus", "panic");
+		} else if (!SDE_ROTTOP_IN_OFFLINE_MODE(rotopmode) &&
+				((regdmasts & REGDMA_BUSY) ||
+						(rotsts & ROT_BUSY_BIT))) {
+			/*
+			 * rotator can stuck in inline while mdp is detached
+			 */
+			SDEROT_WARN(
+				"Inline Rot busy: regdma-sts:0x%x, rottop-sts:0x%x, rottop-opmode:0x%x\n",
+				regdmasts, rotsts, rotopmode);
+			sde_hw_rotator_reset(rot, NULL);
+		} else if ((regdmasts & REGDMA_BUSY) ||
+				(rotsts & ROT_BUSY_BIT)) {
+			_sde_hw_rotator_dump_status(rot, NULL);
+			SDEROT_EVTLOG_TOUT_HANDLER("rot", "rot_dbg_bus",
+					"vbif_dbg_bus", "panic");
+			sde_hw_rotator_reset(rot, NULL);
 		}
 
 		/* Turn off rotator clock after checking rotator registers */
@@ -2322,7 +2562,7 @@
 				if (status & BIT(0)) {
 					SDEROT_ERR("rotator busy 0x%x\n",
 							status);
-					sde_hw_rotator_dump_status(rot);
+					_sde_hw_rotator_dump_status(rot, NULL);
 					SDEROT_EVTLOG_TOUT_HANDLER("rot",
 							"vbif_dbg_bus",
 							"panic");
@@ -2426,6 +2666,12 @@
 	wb_cfg.fps = entry->perf->config.frame_rate;
 	wb_cfg.bw = entry->perf->bw;
 	wb_cfg.fmt = sde_get_format_params(item->output.format);
+	if (!wb_cfg.fmt) {
+		SDEROT_ERR("null format\n");
+		ret = -EINVAL;
+		goto error;
+	}
+
 	wb_cfg.dst_rect = &item->dst_rect;
 	wb_cfg.data = &entry->dst_buf;
 	sde_mdp_get_plane_sizes(wb_cfg.fmt, item->output.width,
@@ -2604,6 +2850,42 @@
 	return 0;
 }
 
+static int sde_hw_rotator_abort_kickoff(struct sde_rot_hw_resource *hw,
+		struct sde_rot_entry *entry)
+{
+	struct sde_hw_rotator *rot;
+	struct sde_hw_rotator_resource_info *resinfo;
+	struct sde_hw_rotator_context *ctx;
+	unsigned long flags;
+
+	if (!hw || !entry) {
+		SDEROT_ERR("null hw resource/entry\n");
+		return -EINVAL;
+	}
+
+	resinfo = container_of(hw, struct sde_hw_rotator_resource_info, hw);
+	rot = resinfo->rot;
+
+	/* Lookup rotator context from session-id */
+	ctx = sde_hw_rotator_get_ctx(rot, entry->item.session_id,
+			entry->item.sequence_id, hw->wb_id);
+	if (!ctx) {
+		SDEROT_ERR("Cannot locate rotator ctx from sesison id:%d\n",
+				entry->item.session_id);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&rot->rotisr_lock, flags);
+	sde_hw_rotator_update_swts(rot, ctx, ctx->timestamp);
+	ctx->abort = true;
+	wake_up_all(&ctx->regdma_waitq);
+	spin_unlock_irqrestore(&rot->rotisr_lock, flags);
+
+	SDEROT_EVTLOG(entry->item.session_id, ctx->timestamp);
+
+	return 0;
+}
+
 /*
  * sde_hw_rotator_wait4done - wait for completion notification
  * @hw: Pointer to rotator resource
@@ -2698,7 +2980,9 @@
 	SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
 
 	/* features exposed via mdss h/w version */
-	if (IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version, SDE_MDP_HW_REV_400)) {
+	if (IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version, SDE_MDP_HW_REV_400) ||
+		IS_SDE_MAJOR_MINOR_SAME(mdata->mdss_version,
+			SDE_MDP_HW_REV_410)) {
 		SDEROT_DBG("Supporting sys cache inline rotation\n");
 		set_bit(SDE_CAPS_SBUF_1,  mdata->sde_caps_map);
 		set_bit(SDE_CAPS_UBWC_2,  mdata->sde_caps_map);
@@ -3266,6 +3550,21 @@
 }
 
 /*
+ * sde_hw_rotator_dump_status - dump status to debug output
+ * @mgr: Pointer to rotator manager
+ * return: none
+ */
+static void sde_hw_rotator_dump_status(struct sde_rot_mgr *mgr)
+{
+	if (!mgr || !mgr->hw_data) {
+		SDEROT_ERR("null parameters\n");
+		return;
+	}
+
+	_sde_hw_rotator_dump_status(mgr->hw_data, NULL);
+}
+
+/*
  * sde_hw_rotator_parse_dt - parse r3 specific device tree settings
  * @hw_data: Pointer to rotator hw
  * @dev: Pointer to platform device
@@ -3381,6 +3680,7 @@
 	mgr->ops_hw_free = sde_hw_rotator_free_ext;
 	mgr->ops_config_hw = sde_hw_rotator_config;
 	mgr->ops_cancel_hw = sde_hw_rotator_cancel;
+	mgr->ops_abort_hw = sde_hw_rotator_abort_kickoff;
 	mgr->ops_kickoff_entry = sde_hw_rotator_kickoff;
 	mgr->ops_wait_for_entry = sde_hw_rotator_wait4done;
 	mgr->ops_hw_validate_entry = sde_hw_rotator_validate_entry;
@@ -3393,6 +3693,7 @@
 	mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
 	mgr->ops_hw_get_downscale_caps = sde_hw_rotator_get_downscale_caps;
 	mgr->ops_hw_get_maxlinewidth = sde_hw_rotator_get_maxlinewidth;
+	mgr->ops_hw_dump_status = sde_hw_rotator_dump_status;
 
 	ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
 	if (ret)
@@ -3432,21 +3733,22 @@
 	/* REGDMA initialization */
 	if (rot->mode == ROT_REGDMA_OFF) {
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
-			rot->cmd_wr_ptr[0][i] = &rot->cmd_queue[
-				SDE_HW_ROT_REGDMA_SEG_SIZE * i];
+			rot->cmd_wr_ptr[0][i] = (char __iomem *)(
+					&rot->cmd_queue[
+					SDE_HW_ROT_REGDMA_SEG_SIZE * i]);
 	} else {
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
 			rot->cmd_wr_ptr[ROT_QUEUE_HIGH_PRIORITY][i] =
-				(u32 *)(rot->mdss_base +
+				rot->mdss_base +
 					REGDMA_RAM_REGDMA_CMD_RAM +
-					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i);
+					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 * i;
 
 		for (i = 0; i < SDE_HW_ROT_REGDMA_TOTAL_CTX; i++)
 			rot->cmd_wr_ptr[ROT_QUEUE_LOW_PRIORITY][i] =
-				(u32 *)(rot->mdss_base +
+				rot->mdss_base +
 					REGDMA_RAM_REGDMA_CMD_RAM +
 					SDE_HW_ROT_REGDMA_SEG_SIZE * 4 *
-					(i + SDE_HW_ROT_REGDMA_TOTAL_CTX));
+					(i + SDE_HW_ROT_REGDMA_TOTAL_CTX);
 	}
 
 	for (i = 0; i < ROT_QUEUE_MAX; i++) {
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
index 2afd032..aaaa28c 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
@@ -50,6 +50,8 @@
 #define ROTTOP_START_CTRL_TRIG_SEL_REGDMA       2
 #define ROTTOP_START_CTRL_TRIG_SEL_MDP          3
 
+#define ROTTOP_OP_MODE_ROT_OUT_MASK             (0x3 << 4)
+
 /* SDE_ROT_SSPP:
  * OFFSET=0x0A8900
  */
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
index 4c1316c..1ff43d6 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h
@@ -218,8 +218,8 @@
 	enum   sde_rot_queue_prio q_id;
 	u32    session_id;
 	u32    sequence_id;
-	u32    *regdma_base;
-	u32    *regdma_wrptr;
+	char __iomem *regdma_base;
+	char __iomem *regdma_wrptr;
 	u32    timestamp;
 	struct completion rot_comp;
 	wait_queue_head_t regdma_waitq;
@@ -231,6 +231,7 @@
 	bool   is_secure;
 	bool   is_traffic_shaping;
 	bool   sbuf_mode;
+	bool   abort;
 	u32    start_ctrl;
 	u32    sys_cache_mode;
 	u32    op_mode;
@@ -280,7 +281,7 @@
 	u32    cmd_queue[SDE_HW_ROT_REGDMA_RAM_SIZE];
 
 	/* Cmd Queue Write Ptr */
-	u32   *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
+	char __iomem *cmd_wr_ptr[ROT_QUEUE_MAX][SDE_HW_ROT_REGDMA_TOTAL_CTX];
 
 	/* Rotator Context */
 	struct sde_hw_rotator_context
@@ -349,7 +350,7 @@
  * @ctx: Rotator Context
  * return: base segment address
  */
-static inline u32 *sde_hw_rotator_get_regdma_segment_base(
+static inline char __iomem *sde_hw_rotator_get_regdma_segment_base(
 		struct sde_hw_rotator_context *ctx)
 {
 	SDEROT_DBG("regdma base @slot[%d]: %p\n",
@@ -365,11 +366,11 @@
  * @ctx: Rotator Context
  * return: segment address
  */
-static inline u32 *sde_hw_rotator_get_regdma_segment(
+static inline char __iomem *sde_hw_rotator_get_regdma_segment(
 		struct sde_hw_rotator_context *ctx)
 {
 	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
-	u32 *addr = ctx->regdma_wrptr;
+	char __iomem *addr = ctx->regdma_wrptr;
 
 	SDEROT_DBG("regdma slot[%d] ==> %p\n", idx, addr);
 	return addr;
@@ -383,7 +384,7 @@
  */
 static inline void sde_hw_rotator_put_regdma_segment(
 		struct sde_hw_rotator_context *ctx,
-		u32 *wrptr)
+		char __iomem *wrptr)
 {
 	u32 idx = sde_hw_rotator_get_regdma_ctxidx(ctx);
 
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
index 9e47187..b817ff0 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
@@ -36,6 +36,14 @@
 #define SMMU_SDE_ROT_SEC	"qcom,smmu_sde_rot_sec"
 #define SMMU_SDE_ROT_UNSEC	"qcom,smmu_sde_rot_unsec"
 
+#ifndef SZ_4G
+#define SZ_4G	(((size_t) SZ_1G) * 4)
+#endif
+
+#ifndef SZ_2G
+#define SZ_2G	(((size_t) SZ_1G) * 2)
+#endif
+
 struct sde_smmu_domain {
 	char *ctx_name;
 	int domain;
@@ -487,9 +495,9 @@
 }
 
 static struct sde_smmu_domain sde_rot_unsec = {
-	"rot_0", SDE_IOMMU_DOMAIN_ROT_UNSECURE, SZ_128K, (SZ_1G - SZ_128K)};
+	"rot_0", SDE_IOMMU_DOMAIN_ROT_UNSECURE, SZ_2G, (SZ_4G - SZ_2G)};
 static struct sde_smmu_domain sde_rot_sec = {
-	"rot_1", SDE_IOMMU_DOMAIN_ROT_SECURE, SZ_1G, SZ_2G};
+	"rot_1", SDE_IOMMU_DOMAIN_ROT_SECURE, SZ_2G, (SZ_4G - SZ_2G)};
 
 static const struct of_device_id sde_smmu_dt_match[] = {
 	{ .compatible = SMMU_SDE_ROT_UNSEC, .data = &sde_rot_unsec},
@@ -517,6 +525,8 @@
 	const struct of_device_id *match;
 	struct sde_module_power *mp;
 	char name[MAX_CLIENT_NAME_LEN];
+	int mdphtw_llc_enable = 1;
+	u32 sid = 0;
 
 	if (!mdata) {
 		SDEROT_INFO(
@@ -538,6 +548,11 @@
 
 	if (of_find_property(pdev->dev.of_node, "iommus", NULL)) {
 		dev = &pdev->dev;
+		rc = of_property_read_u32_index(pdev->dev.of_node, "iommus",
+			1, &sid);
+		if (rc)
+			SDEROT_DBG("SID not defined for domain:%d",
+					smmu_domain.domain);
 	} else {
 		SDEROT_ERR("Invalid SMMU ctx for domain:%d\n",
 				smmu_domain.domain);
@@ -546,6 +561,7 @@
 
 	sde_smmu = &mdata->sde_smmu[smmu_domain.domain];
 	sde_smmu->domain = smmu_domain.domain;
+	sde_smmu->sid = sid;
 	mp = &sde_smmu->mp;
 	memset(mp, 0, sizeof(struct sde_module_power));
 
@@ -605,6 +621,13 @@
 		goto disable_power;
 	}
 
+	rc = iommu_domain_set_attr(sde_smmu->mmu_mapping->domain,
+			DOMAIN_ATTR_USE_UPSTREAM_HINT, &mdphtw_llc_enable);
+	if (rc) {
+		SDEROT_ERR("couldn't enable rot pagetable walks: %d\n", rc);
+		goto release_mapping;
+	}
+
 	if (smmu_domain.domain == SDE_IOMMU_DOMAIN_ROT_SECURE) {
 		int secure_vmid = VMID_CP_PIXEL;
 
diff --git a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
index 9ef4282..ac4ab54 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_util.c
@@ -35,6 +35,7 @@
 
 #include "sde_rotator_util.h"
 #include "sde_rotator_smmu.h"
+#include "sde_rotator_debug.h"
 
 #define Y_TILEWIDTH     48
 #define Y_TILEHEIGHT    4
@@ -1038,6 +1039,8 @@
 			break;
 		}
 	}
+	SDEROT_EVTLOG(data->num_planes, dir, data->p[0].addr, data->p[0].len,
+			data->p[0].mapped);
 
 	return rc;
 }
diff --git a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
index 45e8771..cdcfa96 100644
--- a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
+++ b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
@@ -312,6 +312,7 @@
 	case HAL_COLOR_FORMAT_NV12_UBWC:
 		return 8;
 	case HAL_COLOR_FORMAT_NV12_TP10_UBWC:
+	case HAL_COLOR_FORMAT_P010:
 		return 10;
 	default:
 		dprintk(VIDC_ERR,
@@ -462,7 +463,7 @@
 	bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8));
 
 	bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc,
-		fp_div(FP_INT(256 * 30), FP_INT(1000 * 1000)));
+		fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000)));
 
 	dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc :
 		fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor,
@@ -715,7 +716,7 @@
 	bw_for_1x_8bpc = fp_div(FP_INT(width * height), FP_INT(32 * 8));
 
 	bw_for_1x_8bpc = fp_mult(bw_for_1x_8bpc,
-		fp_div(FP_INT(256 * 30), FP_INT(1000 * 1000)));
+		fp_div(FP_INT(((int)(256 * fps))), FP_INT(1000 * 1000)));
 
 	dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc :
 		fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor,
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index a11be2c..b6f206e 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1420,6 +1420,9 @@
 		case HAL_FLIP_VERTICAL:
 			hfi->flip = HFI_FLIP_VERTICAL;
 			break;
+		case HAL_FLIP_BOTH:
+			hfi->flip = HFI_FLIP_HORIZONTAL | HFI_FLIP_VERTICAL;
+			break;
 		default:
 			dprintk(VIDC_ERR, "Invalid flip setting: %#x\n",
 				prop->flip);
@@ -1629,7 +1632,11 @@
 			&pkt->rg_property_data[1];
 
 		hfi->input_color_primaries = hal->input_color_primaries;
-		hfi->custom_matrix_enabled = hal->custom_matrix_enabled;
+		if (hal->custom_matrix_enabled)
+			/* Bit Mask to enable all custom values */
+			hfi->custom_matrix_enabled = 0x7;
+		else
+			hfi->custom_matrix_enabled = 0x0;
 		memcpy(hfi->csc_matrix, hal->csc_matrix,
 				sizeof(hfi->csc_matrix));
 		memcpy(hfi->csc_bias, hal->csc_bias, sizeof(hfi->csc_bias));
@@ -1874,6 +1881,22 @@
 		pkt->size += sizeof(u32) + sizeof(*work_mode);
 		break;
 	}
+	case HAL_PARAM_VENC_HDR10_PQ_SEI:
+	{
+		struct hfi_hdr10_pq_sei *hfi;
+		struct hal_hdr10_pq_sei *prop =
+			(struct hal_hdr10_pq_sei *) pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI;
+		hfi = (struct hfi_hdr10_pq_sei *)
+			&pkt->rg_property_data[1];
+
+		memcpy(hfi, prop, sizeof(*hfi));
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_hdr10_pq_sei);
+		break;
+	}
 	/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
 	case HAL_CONFIG_BUFFER_REQUIREMENTS:
 	case HAL_CONFIG_PRIORITY:
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index 52b9b32..03dfde6 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -348,11 +348,10 @@
 		info->response_type = HAL_RESPONSE_UNUSED;
 		break;
 	default:
-		/* All other errors are not expected and treated as sys error */
 		dprintk(VIDC_ERR,
-			"%s: data1 %#x, data2 %#x, treat as sys error\n",
-			__func__, pkt->event_data1, pkt->event_data2);
-		info->response_type = HAL_SYS_ERROR;
+			"%s: session %x data1 %#x, data2 %#x\n", __func__,
+			pkt->session_id, pkt->event_data1, pkt->event_data2);
+		info->response_type = HAL_SESSION_ERROR;
 		break;
 	}
 
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index 9b23376..5198bc3 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -80,6 +80,8 @@
 			dprintk(VIDC_ERR,
 				"Size mismatch! Dmabuf size: %zu Expected Size: %lu",
 				buf->size, *buffer_size);
+			msm_vidc_res_handle_fatal_hw_error(smem_client->res,
+					true);
 			goto mem_buf_size_mismatch;
 		}
 		/* Prepare a dma buf for dma on the given device */
@@ -327,6 +329,7 @@
 	if (ion_flags & ION_FLAG_SECURE)
 		smem->flags |= SMEM_SECURE;
 
+	buffer_size = smem->size;
 	rc = msm_ion_get_device_address(inst->mem_client, ion_handle,
 			align, &iova, &buffer_size, smem->flags,
 			smem->buffer_type, &smem->mapping_info);
@@ -603,22 +606,25 @@
 		__func__, mem->handle, mem->device_addr, mem->size,
 		mem->kvaddr, mem->buffer_type);
 
-	if (mem->device_addr)
+	if (mem->device_addr) {
 		msm_ion_put_device_address(client, mem->handle, mem->flags,
 			&mem->mapping_info, mem->buffer_type);
+		mem->device_addr = 0x0;
+	}
 
-	if (mem->kvaddr)
+	if (mem->kvaddr) {
 		ion_unmap_kernel(client->clnt, mem->handle);
+		mem->kvaddr = NULL;
+	}
 
 	if (mem->handle) {
 		trace_msm_smem_buffer_ion_op_start("FREE",
 				(u32)mem->buffer_type, -1, mem->size, -1,
 				mem->flags, -1);
 		ion_free(client->clnt, mem->handle);
+		mem->handle = NULL;
 		trace_msm_smem_buffer_ion_op_end("FREE", (u32)mem->buffer_type,
 			-1, mem->size, -1, mem->flags, -1);
-	} else {
-		dprintk(VIDC_ERR, "%s: invalid ion_handle\n", __func__);
 	}
 
 	return rc;
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 56f9675..fa40091 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -448,14 +448,14 @@
 static DEVICE_ATTR(thermal_level, 0644, show_thermal_level,
 		store_thermal_level);
 
-static ssize_t show_platform_version(struct device *dev,
+static ssize_t show_sku_version(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
 	return scnprintf(buf, PAGE_SIZE, "%d",
-			vidc_driver->platform_version);
+			vidc_driver->sku_version);
 }
 
-static ssize_t store_platform_version(struct device *dev,
+static ssize_t store_sku_version(struct device *dev,
 		struct device_attribute *attr, const char *buf,
 		size_t count)
 {
@@ -463,13 +463,13 @@
 	return count;
 }
 
-static DEVICE_ATTR(platform_version, 0444, show_platform_version,
-		store_platform_version);
+static DEVICE_ATTR(sku_version, 0444, show_sku_version,
+		store_sku_version);
 
 static struct attribute *msm_vidc_core_attrs[] = {
 		&dev_attr_pwr_collapse_delay.attr,
 		&dev_attr_thermal_level.attr,
-		&dev_attr_platform_version.attr,
+		&dev_attr_sku_version.attr,
 		NULL
 };
 
@@ -487,8 +487,6 @@
 static int msm_vidc_probe_vidc_device(struct platform_device *pdev)
 {
 	int rc = 0;
-	void __iomem *base;
-	struct resource *res;
 	struct msm_vidc_core *core;
 	struct device *dev;
 	int nr = BASE_DEVICE_NUMBER;
@@ -605,32 +603,7 @@
 	core->debugfs_root = msm_vidc_debugfs_init_core(
 		core, vidc_driver->debugfs_root);
 
-	vidc_driver->platform_version = 0;
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
-	if (!res) {
-		dprintk(VIDC_DBG, "failed to get efuse resource\n");
-	} else {
-		base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
-		if (!base) {
-			dprintk(VIDC_ERR,
-				"failed efuse ioremap: res->start %#x, size %d\n",
-				(u32)res->start, (u32)resource_size(res));
-		} else {
-			u32 efuse = 0;
-			struct platform_version_table *pf_ver_tbl =
-				core->resources.pf_ver_tbl;
-
-			efuse = readl_relaxed(base);
-			vidc_driver->platform_version =
-				(efuse & pf_ver_tbl->version_mask) >>
-				pf_ver_tbl->version_shift;
-			dprintk(VIDC_DBG,
-				"efuse 0x%x, platform version 0x%x\n",
-				efuse, vidc_driver->platform_version);
-
-			devm_iounmap(&pdev->dev, base);
-		}
-	}
+	vidc_driver->sku_version = core->resources.sku_version;
 
 	dprintk(VIDC_DBG, "populating sub devices\n");
 	/*
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 56e7a99..9238176 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -413,7 +413,16 @@
 		.minimum = 0,
 		.maximum = INT_MAX,
 		.default_value = 0,
-		.step = OPERATING_FRAME_RATE_STEP,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
+		.name = "Set Decoder Frame rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = INT_MAX,
+		.default_value = 0,
+		.step = 1,
 	},
 	{
 		.id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE,
@@ -528,7 +537,7 @@
 		.name = "VP8",
 		.description = "VP8 compressed format",
 		.fourcc = V4L2_PIX_FMT_VP8,
-		.get_frame_size = get_frame_size_compressed,
+		.get_frame_size = get_frame_size_compressed_full_yuv,
 		.type = OUTPUT_PORT,
 		.defer_outputs = false,
 	},
@@ -581,6 +590,13 @@
 
 		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto err_invalid_fmt;
+		}
+
 		msm_comm_set_color_format(inst,
 				msm_comm_get_hal_output_buffer(inst),
 				f->fmt.pix_mp.pixelformat);
@@ -648,6 +664,12 @@
 		}
 		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto err_invalid_fmt;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_INPUT;
 		frame_sz.width = inst->prop.width[OUTPUT_PORT];
@@ -786,7 +808,7 @@
 
 int msm_vdec_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
 {
-	int rc = 0;
+	int rc = 0, temp;
 	struct hal_nal_stream_format_supported stream_format;
 	struct hal_enable_picture enable_picture;
 	struct hal_enable hal_property;
@@ -1011,6 +1033,31 @@
 					rc);
 			break;
 		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY:
+			temp_ctrl = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT);
+			switch (temp_ctrl->val) {
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC:
+				temp = V4L2_PIX_FMT_NV12_UBWC;
+				break;
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC:
+				temp = V4L2_PIX_FMT_NV12_TP10_UBWC;
+				break;
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE:
+			default:
+				dprintk(VIDC_DBG,
+					"set default dpb color format as NV12_UBWC\n");
+				temp = V4L2_PIX_FMT_NV12_UBWC;
+				break;
+			}
+			rc = msm_comm_set_color_format(inst,
+				HAL_BUFFER_OUTPUT, temp);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"%s Failed setting output color format: %#x\n",
+					__func__, rc);
+				break;
+			}
+
 			multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
 			multi_stream.enable = true;
 			pdata = &multi_stream;
@@ -1114,11 +1161,27 @@
 		}
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
-		dprintk(VIDC_DBG,
-			"inst(%pK) operating rate changed from %d to %d\n",
-			inst, inst->clk_data.operating_rate >> 16,
-				ctrl->val >> 16);
-		inst->clk_data.operating_rate = ctrl->val;
+		if (((ctrl->val >> 16) < inst->capability.frame_rate.min ||
+			(ctrl->val >> 16) > inst->capability.frame_rate.max) &&
+			ctrl->val != INT_MAX) {
+			dprintk(VIDC_ERR, "Invalid operating rate %u\n",
+				(ctrl->val >> 16));
+			rc = -ENOTSUPP;
+		} else if (ctrl->val == INT_MAX) {
+			dprintk(VIDC_DBG,
+				"inst(%pK) Request for turbo mode\n", inst);
+			inst->clk_data.turbo_mode = true;
+		} else if (msm_vidc_validate_operating_rate(inst, ctrl->val)) {
+			dprintk(VIDC_ERR, "Failed to set operating rate\n");
+			rc = -ENOTSUPP;
+		} else {
+			dprintk(VIDC_DBG,
+				"inst(%pK) operating rate changed from %d to %d\n",
+				inst, inst->clk_data.operating_rate >> 16,
+					ctrl->val >> 16);
+			inst->clk_data.operating_rate = ctrl->val;
+			inst->clk_data.turbo_mode = false;
+		}
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE:
 		if (ctrl->val ==
@@ -1220,6 +1283,14 @@
 				}
 				rc = msm_vidc_update_host_buff_counts(inst);
 				inst->clk_data.dpb_fourcc = fourcc;
+				control.id =
+				V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT;
+				control.value = ext_control[i].value;
+				rc = msm_comm_s_ctrl(inst, &control);
+				if (rc)
+					dprintk(VIDC_ERR,
+						"%s: set control dpb color format %d failed\n",
+						__func__, control.value);
 				break;
 			default:
 				dprintk(VIDC_ERR,
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index dfb2ad5..dd62fb7 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -61,6 +61,14 @@
 	NULL
 };
 
+static const char *const mpeg_video_flip[] = {
+	"No Flip",
+	"Horizontal Flip",
+	"Vertical Flip",
+	"Both",
+	NULL
+};
+
 static const char *const h264_video_entropy_cabac_model[] = {
 	"Model 0",
 	"Model 1",
@@ -667,7 +675,7 @@
 		.name = "Extradata Type",
 		.type = V4L2_CTRL_TYPE_MENU,
 		.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
-		.maximum = V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO,
+		.maximum = V4L2_MPEG_VIDC_EXTRADATA_ROI_QP,
 		.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
 		.menu_skip_mask = ~(
 			(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
@@ -687,8 +695,7 @@
 			(1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) |
 			(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
 			(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
-			(1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) |
-			(1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO)
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP)
 			),
 		.qmenu = mpeg_video_vidc_extradata,
 	},
@@ -909,7 +916,16 @@
 		.minimum = 0,
 		.maximum = INT_MAX,
 		.default_value = 0,
-		.step = OPERATING_FRAME_RATE_STEP,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
+		.name = "Set Encoder Frame rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = INT_MAX,
+		.default_value = 0,
+		.step = 1,
 	},
 	{
 		.id = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE,
@@ -1036,6 +1052,150 @@
 		.default_value = 0,
 		.step = 1,
 	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_FLIP,
+		.name = "Flip",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH)
+		),
+		.qmenu = mpeg_video_flip,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_HDR_INFO,
+		.name = "Enable/Disable HDR INFO",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED,
+		.maximum = V4L2_MPEG_VIDC_VENC_HDR_INFO_ENABLED,
+		.default_value = V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00,
+		.name = "RGB PRIMARIES[0][0]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01,
+		.name = "RGB PRIMARIES[0][1]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10,
+		.name = "RGB PRIMARIES[1][0]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11,
+		.name = "RGB PRIMARIES[1][1]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20,
+		.name = "RGB PRIMARIES[2][0]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21,
+		.name = "RGB PRIMARIES[2][1]",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X,
+		.name = "WHITE POINT X",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y,
+		.name = "WHITE POINT Y",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM,
+		.name = "MAX DISPLAY LUMINANCE",
+		.type =  V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM,
+		.name = "MIN DISPLAY LUMINANCE",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_MAX_CLL,
+		.name = "MAX CLL",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_MAX_FLL,
+		.name = "MAX FLL",
+		.type = V4L2_CTRL_TYPE_U32,
+		.minimum = 0,
+		.maximum = UINT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
 
 };
 
@@ -1112,6 +1272,13 @@
 		.get_frame_size = get_frame_size_compressed,
 		.type = CAPTURE_PORT,
 	},
+	{
+		.name = "YCbCr Semiplanar 4:2:0 10bit",
+		.description = "Y/CbCr 4:2:0 10bit",
+		.fourcc = V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010,
+		.get_frame_size = get_frame_size_p010,
+		.type = OUTPUT_PORT,
+	},
 };
 
 static int msm_venc_set_csc(struct msm_vidc_inst *inst,
@@ -1278,6 +1445,18 @@
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
 	{
+		struct v4l2_ctrl *hybrid_hp = TRY_GET_CTRL(
+			V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
+		if ((ctrl->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR
+			|| ctrl->val ==
+			 V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+			&& hybrid_hp->val) {
+			dprintk(VIDC_ERR,
+				"CBR_VFR/VBR_VFR not allowed with Hybrid HP\n");
+			rc = -ENOTSUPP;
+			break;
+		}
 		property_id = HAL_PARAM_VENC_RATE_CONTROL;
 		property_val = ctrl->val;
 		pdata = &property_val;
@@ -1420,11 +1599,27 @@
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
 	{
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FLIP);
 		property_id = HAL_PARAM_VPE_ROTATION;
 		vpe_rotation.rotate = msm_comm_v4l2_to_hal(
 				V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
 				ctrl->val);
-		vpe_rotation.flip = HAL_FLIP_NONE;
+		vpe_rotation.flip = msm_comm_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_FLIP,
+				temp_ctrl->val);
+		pdata = &vpe_rotation;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_FLIP:
+	{
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_ROTATION);
+		property_id = HAL_PARAM_VPE_ROTATION;
+		vpe_rotation.rotate = msm_comm_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
+				temp_ctrl->val);
+		vpe_rotation.flip = msm_comm_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_FLIP,
+				ctrl->val);
 		pdata = &vpe_rotation;
 		break;
 	}
@@ -1464,13 +1659,14 @@
 		pdata = &multi_slice_control;
 		break;
 	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE: {
-		bool codec_avc =
+		bool codecs_supported =
+			inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC ||
 			inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264 ||
 			inst->fmts[CAPTURE_PORT].fourcc ==
 							V4L2_PIX_FMT_H264_NO_SC;
 
 		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
-		if (codec_avc && temp_ctrl->val ==
+		if (codecs_supported && temp_ctrl->val ==
 				V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
 			property_id = HAL_PARAM_VENC_SLICE_DELIVERY_MODE;
 			enable.enable = true;
@@ -1700,10 +1896,26 @@
 		pdata = &enable;
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+	{
+		struct v4l2_ctrl *rate_control;
+
+		rate_control =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL);
+		if ((rate_control->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR ||
+			rate_control->val ==
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR)
+			&& ctrl->val) {
+			dprintk(VIDC_ERR,
+				"Hybrid HP not allowed with CBR_VFR/VBR_VFR\n");
+			rc = -ENOTSUPP;
+			break;
+		}
 		property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE;
 		hyb_hierp.layers = ctrl->val;
 		pdata = &hyb_hierp;
 		break;
+	}
 	case V4L2_CID_VIDC_QBUF_MODE:
 		property_id = HAL_PARAM_SYNC_BASED_INTERRUPT;
 		enable.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED;
@@ -1817,12 +2029,27 @@
 		}
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
-		dprintk(VIDC_DBG,
-			"inst(%pK) operating rate changed from %d to %d\n",
-			inst, inst->clk_data.operating_rate >> 16,
+		if (((ctrl->val >> 16) < inst->capability.frame_rate.min ||
+			 (ctrl->val >> 16) > inst->capability.frame_rate.max) &&
+			  ctrl->val != INT_MAX) {
+			dprintk(VIDC_ERR, "Invalid operating rate %u\n",
+				(ctrl->val >> 16));
+			rc = -ENOTSUPP;
+		} else if (ctrl->val == INT_MAX) {
+			dprintk(VIDC_DBG, "inst(%pK) Request for turbo mode\n",
+				inst);
+			inst->clk_data.turbo_mode = true;
+		} else if (msm_vidc_validate_operating_rate(inst, ctrl->val)) {
+			dprintk(VIDC_ERR, "Failed to set operating rate\n");
+			rc = -ENOTSUPP;
+		} else {
+			dprintk(VIDC_DBG,
+				"inst(%pK) operating rate changed from %d to %d\n",
+				inst, inst->clk_data.operating_rate >> 16,
 				ctrl->val >> 16);
-		inst->clk_data.operating_rate = ctrl->val;
-
+			inst->clk_data.operating_rate = ctrl->val;
+			inst->clk_data.turbo_mode = false;
+		}
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE:
 	{
@@ -1996,6 +2223,19 @@
 	case V4L2_CID_MPEG_VIDC_VIDEO_I_FRAME_QP_MAX:
 	case V4L2_CID_MPEG_VIDC_VIDEO_P_FRAME_QP_MAX:
 	case V4L2_CID_MPEG_VIDC_VIDEO_B_FRAME_QP_MAX:
+	case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20:
+	case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21:
+	case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X:
+	case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y:
+	case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM:
+	case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM:
+	case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL:
+	case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL:
 		dprintk(VIDC_DBG, "Set the control : %#x using ext ctrl\n",
 			ctrl->id);
 		break;
@@ -2034,6 +2274,11 @@
 	struct hal_frame_size blur_res;
 	struct hal_quantization_range qp_range;
 	struct hal_quantization qp;
+	struct hal_hdr10_pq_sei hdr10_sei_params;
+	struct msm_vidc_mastering_display_colour_sei_payload *mdisp_sei
+		= &(hdr10_sei_params.disp_color_sei);
+	struct msm_vidc_content_light_level_sei_payload *cll_sei
+		= &(hdr10_sei_params.cll_sei);
 
 	if (!inst || !inst->core || !inst->core->device || !ctrl) {
 		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
@@ -2182,6 +2427,75 @@
 			i++;
 			}
 			break;
+		case V4L2_CID_MPEG_VIDC_VENC_HDR_INFO:
+			if (control[i].value ==
+				V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED)
+				break;
+			memset(&hdr10_sei_params, 0, sizeof(hdr10_sei_params));
+			i++;
+			while (i < ctrl->count) {
+				switch (control[i].id) {
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00:
+					mdisp_sei->nDisplayPrimariesX[0] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01:
+					mdisp_sei->nDisplayPrimariesY[0] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10:
+					mdisp_sei->nDisplayPrimariesX[1] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11:
+					mdisp_sei->nDisplayPrimariesY[1] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20:
+					mdisp_sei->nDisplayPrimariesX[2] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21:
+					mdisp_sei->nDisplayPrimariesY[2] =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X:
+					mdisp_sei->nWhitePointX =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y:
+					mdisp_sei->nWhitePointY =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM:
+					mdisp_sei->
+						nMaxDisplayMasteringLuminance =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM:
+					mdisp_sei->
+						nMinDisplayMasteringLuminance =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_MAX_CLL:
+					cll_sei->nMaxContentLight =
+						control[i].value;
+					break;
+				case V4L2_CID_MPEG_VIDC_VENC_MAX_FLL:
+					cll_sei->nMaxPicAverageLight =
+						control[i].value;
+					break;
+				default:
+					dprintk(VIDC_ERR,
+							"Unknown Ctrl:%d, not part of HDR Info",
+							control[i].id);
+				}
+				i++;
+			}
+			property_id =
+				HAL_PARAM_VENC_HDR10_PQ_SEI;
+			pdata = &hdr10_sei_params;
+			break;
 		default:
 			dprintk(VIDC_ERR, "Invalid id set: %d\n",
 				control[i].id);
@@ -2385,6 +2699,12 @@
 
 		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
 		frame_sz.width = inst->prop.width[CAPTURE_PORT];
@@ -2438,11 +2758,30 @@
 			inst->bufq[fmt->type].plane_sizes[i] =
 				f->fmt.pix_mp.plane_fmt[i].sizeimage;
 		}
+		/*
+		 * Input extradata buffer size may change upon updating
+		 * CAPTURE plane buffer size.
+		 */
+
+		extra_idx = EXTRADATA_IDX(inst->bufq[OUTPUT_PORT].num_planes);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			buff_req_buffer = get_buff_req_buffer(inst,
+					HAL_BUFFER_EXTRADATA_INPUT);
+			inst->bufq[OUTPUT_PORT].plane_sizes[extra_idx] =
+				buff_req_buffer ?
+				buff_req_buffer->buffer_size : 0;
+		}
 	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
 		struct hal_frame_size frame_sz;
 
 		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
 		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
 
 		frame_sz.buffer_type = HAL_BUFFER_INPUT;
 		frame_sz.width = inst->prop.width[OUTPUT_PORT];
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index 6a7588c..dabe667 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -179,6 +179,10 @@
 	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
 		msm_vidc_ctrl_get_range(ctrl, &inst->capability.slice_bytes);
 		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE:
+	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
+		msm_vidc_ctrl_get_range(ctrl, &inst->capability.frame_rate);
+		break;
 	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
 	case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE:
 	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
@@ -276,6 +280,7 @@
 		break;
 	case V4L2_PIX_FMT_NV12_TP10_UBWC:
 		color_format = COLOR_FMT_NV12_BPP10_UBWC;
+		break;
 	case V4L2_PIX_FMT_SDE_Y_CBCR_H2V2_P010:
 		color_format = COLOR_FMT_P010;
 		break;
@@ -600,8 +605,10 @@
 	mutex_lock(&q->lock);
 	rc = vb2_streamon(&q->vb2_bufq, i);
 	mutex_unlock(&q->lock);
-	if (rc)
+	if (rc) {
 		dprintk(VIDC_ERR, "streamon failed on port: %d\n", i);
+		msm_comm_kill_session(inst);
+	}
 	return rc;
 }
 EXPORT_SYMBOL(msm_vidc_streamon);
@@ -621,6 +628,17 @@
 			"Failed to find buffer queue for type = %d\n", i);
 		return -EINVAL;
 	}
+
+	if (!inst->in_reconfig) {
+		dprintk(VIDC_DBG, "%s: inst %pK release resources\n",
+			__func__, inst);
+		rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+		if (rc)
+			dprintk(VIDC_ERR,
+				"%s: inst %pK move to rel res done failed\n",
+				__func__, inst);
+	}
+
 	dprintk(VIDC_DBG, "Calling streamoff\n");
 	mutex_lock(&q->lock);
 	rc = vb2_streamoff(&q->vb2_bufq, i);
@@ -830,7 +848,8 @@
 
 	/* For decoder No need to sanity till LOAD_RESOURCES */
 	if (inst->session_type == MSM_VIDC_DECODER &&
-			inst->state < MSM_VIDC_LOAD_RESOURCES_DONE) {
+			(inst->state < MSM_VIDC_LOAD_RESOURCES_DONE ||
+			inst->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)) {
 		dprintk(VIDC_DBG,
 			"No need to verify buffer counts : %pK\n", inst);
 		return 0;
@@ -883,6 +902,13 @@
 		goto fail_start;
 	}
 
+	rc = msm_vidc_check_scaling_supported(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"This session scaling is not supported %pK\n", inst);
+		goto fail_start;
+	}
+
 	/* Decide work mode for current session */
 	rc = msm_vidc_decide_work_mode(inst);
 	if (rc) {
@@ -987,10 +1013,9 @@
 	}
 
 fail_start:
-	if (rc) {
-		dprintk(VIDC_ERR, "%s: kill session %pK\n", __func__, inst);
-		msm_comm_kill_session(inst);
-	}
+	if (rc)
+		dprintk(VIDC_ERR, "%s: inst %pK session %x failed to start\n",
+			__func__, inst, hash32_ptr(inst->session));
 	return rc;
 }
 
@@ -1147,14 +1172,17 @@
 
 	mbuf = msm_comm_get_vidc_buffer(inst, vb2);
 	if (IS_ERR_OR_NULL(mbuf)) {
-		if (PTR_ERR(mbuf) != -EEXIST)
-			print_vb2_buffer(VIDC_ERR, "failed to get vidc-buf",
-				inst, vb2);
-		return;
+		if (PTR_ERR(mbuf) == -EEXIST)
+			return;
+		print_vb2_buffer(VIDC_ERR, "failed to get vidc-buf",
+			inst, vb2);
+		rc = -EINVAL;
+		goto error;
 	}
 	if (!kref_get_mbuf(inst, mbuf)) {
 		dprintk(VIDC_ERR, "%s: mbuf not found\n", __func__);
-		return;
+		rc = -EINVAL;
+		goto error;
 	}
 
 	rc = msm_comm_qbuf(inst, mbuf);
@@ -1162,6 +1190,10 @@
 		print_vidc_buffer(VIDC_ERR, "failed qbuf", inst, mbuf);
 
 	kref_put_mbuf(mbuf);
+
+error:
+	if (rc)
+		msm_comm_generate_session_error(inst);
 }
 
 static const struct vb2_ops msm_vidc_vb2q_ops = {
@@ -1476,6 +1508,16 @@
 					HAL_BUFFER_INPUT);
 			return -EINVAL;
 		}
+
+		if (inst->session_type == MSM_VIDC_DECODER &&
+			!(inst->flags & VIDC_THUMBNAIL) &&
+			inst->fmts[OUTPUT_PORT].fourcc ==
+				V4L2_PIX_FMT_VP9 &&
+			bufreq->buffer_count_min_host <
+				MIN_NUM_OUTPUT_BUFFERS_VP9)
+			bufreq->buffer_count_min_host =
+				MIN_NUM_OUTPUT_BUFFERS_VP9;
+
 		ctrl->val = bufreq->buffer_count_min_host;
 		break;
 	case V4L2_CID_MPEG_VIDC_VIDEO_TME_PAYLOAD_VERSION:
@@ -1740,12 +1782,6 @@
 		dprintk(VIDC_ERR,
 			"Failed to release mark_data buffers\n");
 
-	/*
-	 * At this point all buffes should be with driver
-	 * irrespective of scenario
-	 */
-	msm_comm_validate_output_buffers(inst);
-
 	msm_comm_release_eos_buffers(inst);
 
 	if (msm_comm_release_output_buffers(inst, true))
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index d557959..1d22077 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -22,6 +22,19 @@
 #define MSM_VIDC_MIN_UBWC_COMPRESSION_RATIO (1 << 16)
 #define MSM_VIDC_MAX_UBWC_COMPRESSION_RATIO (5 << 16)
 
+static inline void msm_dcvs_print_dcvs_stats(struct clock_data *dcvs)
+{
+	dprintk(VIDC_PROF,
+		"DCVS: Load_Low %d, Load Norm %d, Load High %d\n",
+		dcvs->load_low,
+		dcvs->load_norm,
+		dcvs->load_high);
+
+	dprintk(VIDC_PROF,
+		"DCVS: min_threshold %d, max_threshold %d\n",
+		dcvs->min_threshold, dcvs->max_threshold);
+}
+
 static inline unsigned long int get_ubwc_compression_ratio(
 	struct ubwc_cr_stats_info_type ubwc_stats_info)
 {
@@ -133,7 +146,7 @@
 	vote_data->use_dpb_read = false;
 
 	/* Check if driver can vote for lower bus BW */
-	if (inst->clk_data.load <= inst->clk_data.load_norm) {
+	if (inst->clk_data.load < inst->clk_data.load_norm) {
 		vote_data->compression_ratio = max_cr;
 		vote_data->complexity_factor = min_cf;
 		vote_data->input_cr = max_input_cr;
@@ -270,9 +283,11 @@
 	return rc;
 }
 
-static inline int get_pending_bufs_fw(struct msm_vidc_inst *inst)
+static inline int get_bufs_outside_fw(struct msm_vidc_inst *inst)
 {
-	int fw_out_qsize = 0;
+	u32 fw_out_qsize = 0, i = 0;
+	struct vb2_queue *q = NULL;
+	struct vb2_buffer *vb = NULL;
 
 	/*
 	 * DCVS always operates on Uncompressed buffers.
@@ -281,10 +296,36 @@
 
 	if (inst->state >= MSM_VIDC_OPEN_DONE &&
 			inst->state < MSM_VIDC_STOP_DONE) {
-		if (inst->session_type == MSM_VIDC_DECODER)
-			fw_out_qsize = inst->count.ftb - inst->count.fbd;
-		else
-			fw_out_qsize = inst->count.etb - inst->count.ebd;
+
+		/*
+		 * For decoder, there will be some frames with client
+		 * but not to be displayed. Ex : VP9 DECODE_ONLY frames.
+		 * Hence don't count them.
+		 */
+
+		if (inst->session_type == MSM_VIDC_DECODER) {
+			struct vb2_v4l2_buffer *vbuf = NULL;
+
+			q = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+			for (i = 0; i < q->num_buffers; i++) {
+				vb = q->bufs[i];
+				if (!vb)
+					continue;
+				vbuf = to_vb2_v4l2_buffer(vb);
+				if (vbuf &&
+					vb->state != VB2_BUF_STATE_ACTIVE &&
+					!(vbuf->flags &
+						V4L2_QCOM_BUF_FLAG_DECODEONLY))
+					fw_out_qsize++;
+			}
+		} else {
+			q = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+			for (i = 0; i < q->num_buffers; i++) {
+				vb = q->bufs[i];
+				if (vb && vb->state != VB2_BUF_STATE_ACTIVE)
+					fw_out_qsize++;
+			}
+		}
 	}
 
 	return fw_out_qsize;
@@ -315,7 +356,7 @@
 
 	core = inst->core;
 	mutex_lock(&inst->lock);
-	fw_pending_bufs = get_pending_bufs_fw(inst);
+	buffers_outside_fw = get_bufs_outside_fw(inst);
 
 	output_buf_req = get_buff_req_buffer(inst,
 			dcvs->buffer_type);
@@ -332,8 +373,8 @@
 
 	min_output_buf = output_buf_req->buffer_count_min;
 
-	/* Buffers outside FW are with display */
-	buffers_outside_fw = total_output_buf - fw_pending_bufs;
+	/* Buffers outside Display are with FW. */
+	fw_pending_bufs = total_output_buf - buffers_outside_fw;
 	dprintk(VIDC_PROF,
 		"Counts : total_output_buf = %d Min buffers = %d fw_pending_bufs = %d buffers_outside_fw = %d\n",
 		total_output_buf, min_output_buf, fw_pending_bufs,
@@ -359,7 +400,7 @@
 
 	if (buffers_outside_fw <= dcvs->max_threshold)
 		dcvs->load = dcvs->load_high;
-	else if (fw_pending_bufs <= min_output_buf)
+	else if (fw_pending_bufs < min_output_buf)
 		dcvs->load = dcvs->load_low;
 	else
 		dcvs->load = dcvs->load_norm;
@@ -509,6 +550,14 @@
 	unsigned long vpp_cycles = 0, vsp_cycles = 0;
 	u32 vpp_cycles_per_mb;
 	u32 mbs_per_second;
+	struct msm_vidc_core *core = NULL;
+	int i = 0;
+	struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
+	u64 rate = 0;
+	struct clock_data *dcvs = NULL;
+
+	core = inst->core;
+	dcvs = &inst->clk_data;
 
 	mbs_per_second = msm_comm_get_inst_load_per_core(inst,
 		LOAD_CALC_NO_QUIRKS);
@@ -544,6 +593,22 @@
 
 	freq = max(vpp_cycles, vsp_cycles);
 
+	dprintk(VIDC_DBG, "Update DCVS Load\n");
+	allowed_clks_tbl = core->resources.allowed_clks_tbl;
+	for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) {
+		rate = allowed_clks_tbl[i].clock_rate;
+		if (rate >= freq)
+			break;
+	}
+
+	dcvs->load_norm = rate;
+	dcvs->load_low = i < (core->resources.allowed_clks_tbl_size - 1) ?
+		allowed_clks_tbl[i+1].clock_rate : dcvs->load_norm;
+	dcvs->load_high = i > 0 ? allowed_clks_tbl[i-1].clock_rate :
+		dcvs->load_norm;
+
+	msm_dcvs_print_dcvs_stats(dcvs);
+
 	dprintk(VIDC_PROF, "%s Inst %pK : Filled Len = %d Freq = %lu\n",
 		__func__, inst, filled_len, freq);
 
@@ -553,7 +618,8 @@
 static int msm_vidc_set_clocks(struct msm_vidc_core *core)
 {
 	struct hfi_device *hdev;
-	unsigned long freq = 0, rate = 0;
+	unsigned long freq_core_1 = 0, freq_core_2 = 0, rate = 0;
+	unsigned long freq_core_max = 0;
 	struct msm_vidc_inst *temp = NULL;
 	int rc = 0, i = 0;
 	struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
@@ -568,14 +634,33 @@
 
 	mutex_lock(&core->lock);
 	list_for_each_entry(temp, &core->instances, list) {
-		freq += temp->clk_data.curr_freq;
+
+		if (temp->clk_data.core_id == VIDC_CORE_ID_1)
+			freq_core_1 += temp->clk_data.min_freq;
+		else if (temp->clk_data.core_id == VIDC_CORE_ID_2)
+			freq_core_2 += temp->clk_data.min_freq;
+		else if (temp->clk_data.core_id == VIDC_CORE_ID_3) {
+			freq_core_1 += temp->clk_data.min_freq / 2;
+			freq_core_2 += temp->clk_data.min_freq / 2;
+		}
+
+		freq_core_max = max_t(unsigned long, freq_core_1, freq_core_2);
+
+		if (temp->clk_data.turbo_mode) {
+			dprintk(VIDC_PROF,
+				"Found an instance with Turbo request\n");
+			freq_core_max = msm_vidc_max_freq(core);
+			break;
+		}
 	}
+
 	for (i = core->resources.allowed_clks_tbl_size - 1; i >= 0; i--) {
 		rate = allowed_clks_tbl[i].clock_rate;
-		if (rate >= freq)
+		if (rate >= freq_core_max)
 			break;
 	}
-	core->min_freq = freq;
+
+	core->min_freq = freq_core_max;
 	core->curr_freq = rate;
 	mutex_unlock(&core->lock);
 
@@ -587,19 +672,22 @@
 	return rc;
 }
 
-int msm_vidc_update_operating_rate(struct msm_vidc_inst *inst)
+int msm_vidc_validate_operating_rate(struct msm_vidc_inst *inst,
+	u32 operating_rate)
 {
-	struct v4l2_ctrl *ctrl = NULL;
 	struct msm_vidc_inst *temp;
 	struct msm_vidc_core *core;
 	unsigned long max_freq, freq_left, ops_left, load, cycles, freq = 0;
 	unsigned long mbs_per_second;
+	int rc = 0;
+	u32 curr_operating_rate = 0;
 
 	if (!inst || !inst->core) {
 		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
 		return -EINVAL;
 	}
 	core = inst->core;
+	curr_operating_rate = inst->clk_data.operating_rate >> 16;
 
 	mutex_lock(&core->lock);
 	max_freq = msm_vidc_max_freq(core);
@@ -614,51 +702,37 @@
 
 	freq_left = max_freq - freq;
 
-	list_for_each_entry(temp, &core->instances, list) {
-
-		if (!temp ||
-				temp->state < MSM_VIDC_START_DONE ||
-				temp->state >= MSM_VIDC_RELEASE_RESOURCES_DONE)
-			continue;
-
-		mbs_per_second = msm_comm_get_inst_load_per_core(temp,
+	mbs_per_second = msm_comm_get_inst_load_per_core(inst,
 		LOAD_CALC_NO_QUIRKS);
 
-		cycles = temp->clk_data.entry->vpp_cycles;
-		if (temp->session_type == MSM_VIDC_ENCODER)
-			cycles = temp->flags & VIDC_LOW_POWER ?
-				temp->clk_data.entry->low_power_cycles :
-				cycles;
+	cycles = inst->clk_data.entry->vpp_cycles;
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		cycles = inst->flags & VIDC_LOW_POWER ?
+			inst->clk_data.entry->low_power_cycles :
+			cycles;
 
-		load = cycles * mbs_per_second;
+	load = cycles * mbs_per_second;
 
-		ops_left = load ? (freq_left / load) : 0;
-		/* Convert remaining operating rate to Q16 format */
-		ops_left = ops_left << 16;
+	ops_left = load ? (freq_left / load) : 0;
 
-		ctrl = v4l2_ctrl_find(&temp->ctrl_handler,
-			V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE);
-		if (ctrl) {
-			dprintk(VIDC_DBG,
-				"%s: Before Range = %lld --> %lld\n",
-				ctrl->name, ctrl->minimum, ctrl->maximum);
-			dprintk(VIDC_DBG,
-				"%s: Before Def value = %lld Cur val = %d\n",
-				ctrl->name, ctrl->default_value, ctrl->val);
-			v4l2_ctrl_modify_range(ctrl, ctrl->minimum,
-				ctrl->val + ops_left, ctrl->step,
-				ctrl->default_value);
-			dprintk(VIDC_DBG,
-				"%s: Updated Range = %lld --> %lld\n",
-				ctrl->name, ctrl->minimum, ctrl->maximum);
-			dprintk(VIDC_DBG,
-				"%s: Updated Def value = %lld Cur val = %d\n",
-				ctrl->name, ctrl->default_value, ctrl->val);
-		}
+	operating_rate = operating_rate >> 16;
+
+	if ((curr_operating_rate + ops_left) >= operating_rate ||
+			!msm_vidc_clock_scaling ||
+			inst->clk_data.buffer_counter < DCVS_FTB_WINDOW) {
+		dprintk(VIDC_DBG,
+			"Requestd operating rate is valid %u\n",
+			operating_rate);
+		rc = 0;
+	} else {
+		dprintk(VIDC_DBG,
+			"Current load is high for requested settings. Cannot set operating rate to %u\n",
+			operating_rate);
+		rc = -EINVAL;
 	}
 	mutex_unlock(&core->lock);
 
-	return 0;
+	return rc;
 }
 
 int msm_comm_scale_clocks(struct msm_vidc_inst *inst)
@@ -701,9 +775,9 @@
 
 	if (inst->clk_data.buffer_counter < DCVS_FTB_WINDOW ||
 		!msm_vidc_clock_scaling)
-		inst->clk_data.curr_freq = msm_vidc_max_freq(inst->core);
+		inst->clk_data.min_freq = msm_vidc_max_freq(inst->core);
 	else
-		inst->clk_data.curr_freq = freq;
+		inst->clk_data.min_freq = freq;
 
 	msm_vidc_set_clocks(inst->core);
 
@@ -789,19 +863,6 @@
 	return rc;
 }
 
-static inline void msm_dcvs_print_dcvs_stats(struct clock_data *dcvs)
-{
-	dprintk(VIDC_PROF,
-		"DCVS: Load_Low %d, Load Norm %d, Load High %d\n",
-		dcvs->load_low,
-		dcvs->load_norm,
-		dcvs->load_high);
-
-	dprintk(VIDC_PROF,
-		"DCVS: min_threshold %d, max_threshold %d\n",
-		dcvs->min_threshold, dcvs->max_threshold);
-}
-
 void msm_clock_data_reset(struct msm_vidc_inst *inst)
 {
 	struct msm_vidc_core *core;
@@ -844,7 +905,8 @@
 			return;
 		}
 		dcvs->max_threshold = output_buf_req->buffer_count_actual -
-			output_buf_req->buffer_count_min_host + 1;
+			output_buf_req->buffer_count_min_host + 2;
+
 		dcvs->min_threshold =
 			msm_vidc_get_extra_buff_count(inst, dcvs->buffer_type);
 	} else {
@@ -980,8 +1042,8 @@
 		return 0;
 	}
 	mbs_per_frame = msm_vidc_get_mbs_per_frame(inst);
-	if (mbs_per_frame >= inst->core->resources.max_hq_mbs_per_frame ||
-		inst->prop.fps >= inst->core->resources.max_hq_fps) {
+	if (mbs_per_frame > inst->core->resources.max_hq_mbs_per_frame ||
+		inst->prop.fps > inst->core->resources.max_hq_fps) {
 		enable = true;
 	}
 
@@ -1025,17 +1087,21 @@
 }
 
 static u32 get_core_load(struct msm_vidc_core *core,
-	u32 core_id, bool lp_mode)
+	u32 core_id, bool lp_mode, bool real_time)
 {
 	struct msm_vidc_inst *inst = NULL;
 	u32 current_inst_mbs_per_sec = 0, load = 0;
+	bool real_time_mode = false;
 
 	mutex_lock(&core->lock);
 	list_for_each_entry(inst, &core->instances, list) {
 		u32 cycles, lp_cycles;
 
+		real_time_mode = inst->flags & VIDC_REALTIME ? true : false;
 		if (!(inst->clk_data.core_id & core_id))
 			continue;
+		if (real_time_mode != real_time)
+			continue;
 		if (inst->session_type == MSM_VIDC_DECODER) {
 			cycles = lp_cycles = inst->clk_data.entry->vpp_cycles;
 		} else if (inst->session_type == MSM_VIDC_ENCODER) {
@@ -1080,10 +1146,10 @@
 	max_freq = msm_vidc_max_freq(inst->core);
 	inst->clk_data.core_id = 0;
 
-	core0_load = get_core_load(core, VIDC_CORE_ID_1, false);
-	core1_load = get_core_load(core, VIDC_CORE_ID_2, false);
-	core0_lp_load = get_core_load(core, VIDC_CORE_ID_1, true);
-	core1_lp_load = get_core_load(core, VIDC_CORE_ID_2, true);
+	core0_load = get_core_load(core, VIDC_CORE_ID_1, false, true);
+	core1_load = get_core_load(core, VIDC_CORE_ID_2, false, true);
+	core0_lp_load = get_core_load(core, VIDC_CORE_ID_1, true, true);
+	core1_lp_load = get_core_load(core, VIDC_CORE_ID_2, true, true);
 
 	min_load = min(core0_load, core1_load);
 	min_core_id = core0_load < core1_load ?
@@ -1095,6 +1161,16 @@
 	lp_cycles = inst->session_type == MSM_VIDC_ENCODER ?
 			inst->clk_data.entry->low_power_cycles :
 			inst->clk_data.entry->vpp_cycles;
+	/*
+	 * Incase there is only 1 core enabled, mark it as the core
+	 * with min load. This ensures that this core is selected and
+	 * video session is set to run on the enabled core.
+	 */
+	if (inst->capability.max_video_cores.max <= VIDC_CORE_ID_1) {
+		min_core_id = min_lp_core_id = VIDC_CORE_ID_1;
+		min_load = core0_load;
+		min_lp_load = core0_lp_load;
+	}
 
 	current_inst_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS) *
 		inst->clk_data.entry->vpp_cycles;
@@ -1102,9 +1178,9 @@
 	current_inst_lp_load = msm_comm_get_inst_load(inst,
 		LOAD_CALC_NO_QUIRKS) * lp_cycles;
 
-	dprintk(VIDC_DBG, "Core 0 Load = %d Core 1 Load = %d\n",
+	dprintk(VIDC_DBG, "Core 0 RT Load = %d Core 1 RT Load = %d\n",
 		 core0_load, core1_load);
-	dprintk(VIDC_DBG, "Core 0 LP Load = %d Core 1 LP Load = %d\n",
+	dprintk(VIDC_DBG, "Core 0 RT LP Load = %d Core 1 RT LP Load = %d\n",
 		core0_lp_load, core1_lp_load);
 	dprintk(VIDC_DBG, "Max Load = %lu\n", max_freq);
 	dprintk(VIDC_DBG, "Current Load = %d Current LP Load = %d\n",
@@ -1118,7 +1194,8 @@
 		V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE);
 
 	/* Try for preferred core based on settings. */
-	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode) {
+	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode &&
+		inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) {
 		if (current_inst_load / 2 + core0_load <= max_freq &&
 			current_inst_load / 2 + core1_load <= max_freq) {
 			if (inst->clk_data.work_mode == VIDC_WORK_MODE_2) {
@@ -1129,7 +1206,8 @@
 		}
 	}
 
-	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode) {
+	if (inst->session_type == MSM_VIDC_ENCODER && hier_mode &&
+		inst->capability.max_video_cores.max >= VIDC_CORE_ID_3) {
 		if (current_inst_lp_load / 2 +
 				core0_lp_load <= max_freq &&
 			current_inst_lp_load / 2 +
@@ -1184,7 +1262,38 @@
 
 	rc = msm_comm_scale_clocks_and_bus(inst);
 
+	msm_print_core_status(core, VIDC_CORE_ID_1);
+	msm_print_core_status(core, VIDC_CORE_ID_2);
+
 	return rc;
 }
 
+void msm_print_core_status(struct msm_vidc_core *core, u32 core_id)
+{
+	struct msm_vidc_inst *inst = NULL;
 
+	dprintk(VIDC_PROF, "Instances running on core %u", core_id);
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+
+		if ((inst->clk_data.core_id != core_id) &&
+			(inst->clk_data.core_id != VIDC_CORE_ID_3))
+			continue;
+
+		dprintk(VIDC_PROF,
+			"inst %pK (%4ux%4u) to (%4ux%4u) %3u %s %s %s %s %lu\n",
+			inst,
+			inst->prop.width[OUTPUT_PORT],
+			inst->prop.height[OUTPUT_PORT],
+			inst->prop.width[CAPTURE_PORT],
+			inst->prop.height[CAPTURE_PORT],
+			inst->prop.fps,
+			inst->session_type == MSM_VIDC_ENCODER ? "ENC" : "DEC",
+			inst->clk_data.work_mode == VIDC_WORK_MODE_1 ?
+				"WORK_MODE_1" : "WORK_MODE_2",
+			inst->flags & VIDC_LOW_POWER ? "LP" : "HQ",
+			inst->flags & VIDC_REALTIME ? "RealTime" : "NonRTime",
+			inst->clk_data.min_freq);
+	}
+	mutex_unlock(&core->lock);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
index 707f034..142d63f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
@@ -22,7 +22,8 @@
 #define DCVS_DEC_EXTRA_OUTPUT_BUFFERS 4
 
 void msm_clock_data_reset(struct msm_vidc_inst *inst);
-int msm_vidc_update_operating_rate(struct msm_vidc_inst *inst);
+int msm_vidc_validate_operating_rate(struct msm_vidc_inst *inst,
+	u32 operating_rate);
 int msm_vidc_get_extra_buff_count(struct msm_vidc_inst *inst,
 	enum hal_buffer buffer_type);
 int msm_dcvs_try_enable(struct msm_vidc_inst *inst);
@@ -31,6 +32,7 @@
 void msm_comm_free_freq_table(struct msm_vidc_inst *inst);
 int msm_vidc_decide_work_mode(struct msm_vidc_inst *inst);
 int msm_vidc_decide_core_and_power_mode(struct msm_vidc_inst *inst);
+void msm_print_core_status(struct msm_vidc_core *core, u32 core_id);
 void msm_vidc_clear_freq_entry(struct msm_vidc_inst *inst,
 	u32 device_addr);
 void msm_comm_free_input_cr_table(struct msm_vidc_inst *inst);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 428bf71..9dce3f9 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -70,8 +70,6 @@
 	"Extradata UBWC CR stats info",
 };
 
-static void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
-static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
 static void handle_session_error(enum hal_command_response cmd, void *data);
 static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
 
@@ -560,6 +558,19 @@
 		default:
 			goto unknown_value;
 		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_FLIP:
+		switch (value) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE:
+			return HAL_FLIP_NONE;
+		case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI:
+			return HAL_FLIP_HORIZONTAL;
+		case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT:
+			return HAL_FLIP_VERTICAL;
+		case V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH:
+			return HAL_FLIP_BOTH;
+		default:
+			goto unknown_value;
+		}
 	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
 		switch (value) {
 		case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
@@ -1437,6 +1448,9 @@
 				V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
 				&inst->capability.bframe);
 	}
+	msm_vidc_comm_update_ctrl(inst,
+		V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE,
+		&inst->capability.frame_rate);
 }
 
 static void handle_session_init_done(enum hal_command_response cmd, void *data)
@@ -1670,6 +1684,11 @@
 		event_notify->crop_data.height;
 	inst->prop.crop_info.width =
 		event_notify->crop_data.width;
+	/* HW returns progressive_only flag in pic_struct. */
+	inst->pic_struct =
+		event_notify->pic_struct ?
+		MSM_VIDC_PIC_STRUCT_PROGRESSIVE :
+		MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED;
 
 	ptr = (u32 *)seq_changed_event.u.data;
 	ptr[0] = event_notify->height;
@@ -2089,7 +2108,8 @@
 	}
 
 	hdev = inst->core->device;
-	dprintk(VIDC_WARN, "Session error received for session %pK\n", inst);
+	dprintk(VIDC_ERR, "Session error received for inst %pK session %x\n",
+		inst, hash32_ptr(inst->session));
 
 	if (response->status == VIDC_ERR_MAX_CLIENTS) {
 		dprintk(VIDC_WARN, "Too many clients, rejecting %pK", inst);
@@ -2112,6 +2132,8 @@
 		event = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
 	}
 
+	/* change state before sending error to client */
+	change_inst_state(inst, MSM_VIDC_CORE_INVALID);
 	msm_vidc_queue_v4l2_event(inst, event);
 	put_inst(inst);
 }
@@ -2161,6 +2183,7 @@
 				"Got SYS_ERR but unable to identify core\n");
 		return;
 	}
+	hdev = core->device;
 
 	mutex_lock(&core->lock);
 	if (core->state == VIDC_CORE_UNINIT) {
@@ -2179,9 +2202,11 @@
 			"%s: Send sys error for inst %pK\n", __func__, inst);
 		change_inst_state(inst, MSM_VIDC_CORE_INVALID);
 		msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_SYS_ERROR);
-		msm_comm_print_inst_info(inst);
+		if (!core->trigger_ssr)
+			msm_comm_print_inst_info(inst);
 	}
-	hdev = core->device;
+	/* handle the hw error before core released to get full debug info */
+	msm_vidc_handle_hw_error(core);
 	dprintk(VIDC_DBG, "Calling core_release\n");
 	rc = call_hfi_op(hdev, core_release, hdev->hfi_device_data);
 	if (rc) {
@@ -2192,10 +2217,7 @@
 	core->state = VIDC_CORE_UNINIT;
 	mutex_unlock(&core->lock);
 
-	dprintk(VIDC_ERR,
-		"SYS_ERROR can potentially crash the system\n");
-
-	msm_vidc_handle_hw_error(core);
+	dprintk(VIDC_WARN, "SYS_ERROR handled.\n");
 }
 
 void msm_comm_session_clean(struct msm_vidc_inst *inst)
@@ -2272,6 +2294,8 @@
 	mutex_lock(&inst->bufq[port].lock);
 	found = false;
 	list_for_each_entry(vb, &q->queued_list, queued_entry) {
+		if (vb->state != VB2_BUF_STATE_ACTIVE)
+			continue;
 		if (msm_comm_compare_vb2_planes(inst, mbuf, vb)) {
 			found = true;
 			break;
@@ -2306,9 +2330,6 @@
 			__func__, vb->type);
 		return -EINVAL;
 	}
-	msm_vidc_debugfs_update(inst, port == CAPTURE_PORT ?
-			MSM_VIDC_DEBUGFS_EVENT_FBD :
-			MSM_VIDC_DEBUGFS_EVENT_EBD);
 
 	mutex_lock(&inst->bufq[port].lock);
 	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
@@ -2363,8 +2384,8 @@
 	empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
 	/* If this is internal EOS buffer, handle it in driver */
 	if (is_eos_buffer(inst, empty_buf_done->packet_buffer)) {
-		dprintk(VIDC_DBG, "Received EOS buffer %pK\n",
-			(void *)(u64)empty_buf_done->packet_buffer);
+		dprintk(VIDC_DBG, "Received EOS buffer 0x%x\n",
+			empty_buf_done->packet_buffer);
 		goto exit;
 	}
 
@@ -2434,6 +2455,7 @@
 	 */
 	msm_comm_put_vidc_buffer(inst, mbuf);
 	msm_comm_vb2_buffer_done(inst, vb2);
+	msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
 	kref_put_mbuf(mbuf);
 exit:
 	put_inst(inst);
@@ -2635,6 +2657,7 @@
 	 */
 	msm_comm_put_vidc_buffer(inst, mbuf);
 	msm_comm_vb2_buffer_done(inst, vb2);
+	msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
 	kref_put_mbuf(mbuf);
 
 exit:
@@ -2774,7 +2797,8 @@
 	hdev = inst->core->device;
 	abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE);
 
-	dprintk(VIDC_WARN, "%s: inst %pK\n", __func__, inst);
+	dprintk(VIDC_WARN, "%s: inst %pK session %x\n", __func__,
+		inst, hash32_ptr(inst->session));
 	rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
 	if (rc) {
 		dprintk(VIDC_ERR,
@@ -2786,8 +2810,8 @@
 			msecs_to_jiffies(
 				inst->core->resources.msm_vidc_hw_rsp_timeout));
 	if (!rc) {
-		dprintk(VIDC_ERR, "%s: inst %pK abort timed out\n",
-				__func__, inst);
+		dprintk(VIDC_ERR, "%s: inst %pK session %x abort timed out\n",
+				__func__, inst, hash32_ptr(inst->session));
 		msm_comm_generate_sys_error(inst);
 		rc = -EBUSY;
 	} else {
@@ -3011,6 +3035,23 @@
 	return msm_vidc_deinit_core(inst);
 }
 
+static int msm_comm_session_init_done(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc;
+
+	dprintk(VIDC_DBG, "inst %pK: waiting for session init done\n", inst);
+	rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
+			HAL_SESSION_INIT_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR, "Session init failed for inst %pK\n", inst);
+		msm_comm_generate_sys_error(inst);
+		return rc;
+	}
+
+	return rc;
+}
+
 static int msm_comm_session_init(int flipped_state,
 	struct msm_vidc_inst *inst)
 {
@@ -3295,20 +3336,11 @@
 	}
 
 	mutex_lock(&core->lock);
-	if (core->state != VIDC_CORE_INIT_DONE) {
-		dprintk(VIDC_ERR,
-				"%s - fw is not in proper state, skip suspend\n",
-				__func__);
-		rc = -EINVAL;
-		goto exit;
-	}
-
 	rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data);
 	if (rc)
 		dprintk(VIDC_WARN, "Failed to suspend\n");
-
-exit:
 	mutex_unlock(&core->lock);
+
 	return rc;
 }
 
@@ -3348,7 +3380,7 @@
 	enum hal_buffer buffer_type)
 {
 	int rc = 0;
-	struct internal_buf *binfo;
+	struct internal_buf *binfo = NULL;
 	u32 smem_flags = 0, buffer_size;
 	struct hal_buffer_requirements *output_buf, *extradata_buf;
 	int i;
@@ -3457,10 +3489,10 @@
 	}
 	return rc;
 fail_set_buffers:
-	kfree(binfo);
-fail_kzalloc:
 	msm_comm_smem_free(inst, &binfo->smem);
 err_no_mem:
+	kfree(binfo);
+fail_kzalloc:
 	return rc;
 }
 
@@ -3671,8 +3703,8 @@
 	if (inst->state == MSM_VIDC_CORE_INVALID) {
 		dprintk(VIDC_ERR, "%s: inst %pK is in invalid\n",
 			__func__, inst);
-		mutex_unlock(&inst->sync_lock);
-		return -EINVAL;
+		rc = -EINVAL;
+		goto exit;
 	}
 
 	flipped_state = get_flipped_state(inst->state, state);
@@ -3693,8 +3725,7 @@
 		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_OPEN_DONE:
-		rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
-			HAL_SESSION_INIT_DONE);
+		rc = msm_comm_session_init_done(flipped_state, inst);
 		if (rc || state <= get_flipped_state(inst->state, state))
 			break;
 	case MSM_VIDC_LOAD_RESOURCES:
@@ -3754,6 +3785,8 @@
 		rc = -EINVAL;
 		break;
 	}
+
+exit:
 	mutex_unlock(&inst->sync_lock);
 
 	if (rc) {
@@ -3792,8 +3825,8 @@
 		data.timestamp = LLONG_MAX;
 		data.extradata_addr = data.device_addr;
 		data.extradata_size = 0;
-		dprintk(VIDC_DBG, "Queueing EOS buffer %pK\n",
-				(void *)(u64)data.device_addr);
+		dprintk(VIDC_DBG, "Queueing EOS buffer 0x%x\n",
+				data.device_addr);
 		hdev = inst->core->device;
 
 		rc = call_hfi_op(hdev, session_etb, inst->session,
@@ -4147,6 +4180,19 @@
 	output_count = (batch_mode ? &count_single_batch : &count_buffers)
 		(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
 
+	if (!batch_mode && mbuf) {
+		/*
+		 * don't queue output_mplane buffers if buffer queued
+		 * by client is capture_mplane type and vice versa.
+		 */
+		if (mbuf->vvb.vb2_buf.type ==
+				V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+			output_count = 0;
+		else if (mbuf->vvb.vb2_buf.type ==
+				V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+			capture_count = 0;
+	}
+
 	/*
 	 * Somewhat complicated logic to prevent queuing the buffer to hardware.
 	 * Don't queue if:
@@ -4742,6 +4788,7 @@
 	mutex_lock(&inst->eosbufs.lock);
 	list_for_each_entry_safe(buf, next, &inst->eosbufs.list, list) {
 		list_del(&buf->list);
+		msm_comm_smem_free(inst, &buf->smem);
 		kfree(buf);
 	}
 	INIT_LIST_HEAD(&inst->eosbufs.list);
@@ -4998,6 +5045,9 @@
 	enum vidc_ports ports[] = {OUTPUT_PORT, CAPTURE_PORT};
 	int c = 0;
 
+	/* before flush ensure venus released all buffers */
+	msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+
 	for (c = 0; c < ARRAY_SIZE(ports); ++c) {
 		enum vidc_ports port = ports[c];
 
@@ -5060,6 +5110,9 @@
 		return 0;
 	}
 
+	/* enable in flush */
+	inst->in_flush = true;
+
 	mutex_lock(&inst->registeredbufs.lock);
 	list_for_each_entry_safe(mbuf, next, &inst->registeredbufs.list, list) {
 		/* don't flush input buffers if input flush is not requested */
@@ -5100,9 +5153,6 @@
 	}
 	mutex_unlock(&inst->registeredbufs.lock);
 
-	/* enable in flush */
-	inst->in_flush = true;
-
 	hdev = inst->core->device;
 	if (ip_flush) {
 		dprintk(VIDC_DBG, "Send flush on all ports to firmware\n");
@@ -5261,6 +5311,8 @@
 		return -EINVAL;
 	}
 	hdev = core->device;
+
+	mutex_lock(&core->lock);
 	if (core->state == VIDC_CORE_INIT_DONE) {
 		/*
 		 * In current implementation user-initiated SSR triggers
@@ -5276,7 +5328,11 @@
 				__func__);
 			core->trigger_ssr = false;
 		}
+	} else {
+		dprintk(VIDC_WARN, "%s: video core %pK not initialized\n",
+			__func__, core);
 	}
+	mutex_unlock(&core->lock);
 
 	return rc;
 }
@@ -5289,8 +5345,7 @@
 		LOAD_CALC_IGNORE_NON_REALTIME_LOAD;
 
 	if (inst->state == MSM_VIDC_OPEN_DONE) {
-		max_load_adj = inst->core->resources.max_load +
-			inst->capability.mbs_per_frame.max;
+		max_load_adj = inst->core->resources.max_load;
 		num_mbs_per_sec = msm_comm_get_load(inst->core,
 					MSM_VIDC_DECODER, quirks);
 		num_mbs_per_sec += msm_comm_get_load(inst->core,
@@ -5405,7 +5460,7 @@
 	int rc = 0;
 	struct hfi_device *hdev;
 	struct msm_vidc_core *core;
-	u32 output_height, output_width;
+	u32 output_height, output_width, input_height, input_width;
 	u32 rotation;
 
 	if (!inst || !inst->core || !inst->core->device) {
@@ -5428,18 +5483,34 @@
 		return -ENOTSUPP;
 	}
 
+	output_height = inst->prop.height[CAPTURE_PORT];
+	output_width = inst->prop.width[CAPTURE_PORT];
+	input_height = inst->prop.height[OUTPUT_PORT];
+	input_width = inst->prop.width[OUTPUT_PORT];
+
+	if (input_width % 2 != 0 || input_height % 2 != 0 ||
+			output_width % 2 != 0 || output_height % 2 != 0) {
+		dprintk(VIDC_ERR,
+			"Height and Width should be even numbers for NV12\n");
+		dprintk(VIDC_ERR,
+			"Input WxH = (%u)x(%u), Output WxH = (%u)x(%u)\n",
+			input_width, input_height,
+			output_width, output_height);
+		rc = -ENOTSUPP;
+	}
+
 	rotation =  msm_comm_g_ctrl_for_id(inst,
 					V4L2_CID_MPEG_VIDC_VIDEO_ROTATION);
 
-	output_height = inst->prop.height[CAPTURE_PORT];
-	output_width = inst->prop.width[CAPTURE_PORT];
+	output_height = ALIGN(inst->prop.height[CAPTURE_PORT], 16);
+	output_width = ALIGN(inst->prop.width[CAPTURE_PORT], 16);
 
 	if ((output_width != output_height) &&
 		(rotation == V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90 ||
 		rotation == V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270)) {
 
-		output_width = inst->prop.height[CAPTURE_PORT];
-		output_height = inst->prop.width[CAPTURE_PORT];
+		output_width = ALIGN(inst->prop.height[CAPTURE_PORT], 16);
+		output_height = ALIGN(inst->prop.width[CAPTURE_PORT], 16);
 		dprintk(VIDC_DBG,
 			"Rotation=%u Swapped Output W=%u H=%u to check capability",
 			rotation, output_width, output_height);
@@ -5472,10 +5543,6 @@
 			capability->width.max, capability->height.max);
 			rc = -ENOTSUPP;
 		}
-
-		if (!rc && msm_vidc_check_scaling_supported(inst)) {
-			rc = -ENOTSUPP;
-		}
 	}
 	if (rc) {
 		dprintk(VIDC_ERR,
@@ -5484,7 +5551,7 @@
 	return rc;
 }
 
-static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
+void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
 {
 	enum hal_command_response cmd = HAL_SESSION_ERROR;
 	struct msm_vidc_cb_cmd_done response = {0};
@@ -5499,7 +5566,7 @@
 	handle_session_error(cmd, (void *)&response);
 }
 
-static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
+void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
 {
 	struct msm_vidc_core *core;
 	enum hal_command_response cmd = HAL_SYS_ERROR;
@@ -5529,8 +5596,8 @@
 		return 0;
 	}
 
-	dprintk(VIDC_WARN, "%s: inst %pK, state %d\n", __func__,
-			inst, inst->state);
+	dprintk(VIDC_WARN, "%s: inst %pK, session %x state %d\n", __func__,
+			inst, hash32_ptr(inst->session), inst->state);
 	/*
 	 * We're internally forcibly killing the session, if fw is aware of
 	 * the session send session_abort to firmware to clean up and release
@@ -5541,8 +5608,9 @@
 			inst->state == MSM_VIDC_CORE_INVALID) {
 		rc = msm_comm_session_abort(inst);
 		if (rc) {
-			dprintk(VIDC_WARN, "%s: inst %pK abort failed\n",
-				__func__, inst);
+			dprintk(VIDC_ERR,
+				"%s: inst %pK session %x abort failed\n",
+				__func__, inst, hash32_ptr(inst->session));
 			change_inst_state(inst, MSM_VIDC_CORE_INVALID);
 		}
 	}
@@ -5550,7 +5618,8 @@
 	change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
 	msm_comm_session_clean(inst);
 
-	dprintk(VIDC_WARN, "%s: inst %pK handled\n", __func__, inst);
+	dprintk(VIDC_WARN, "%s: inst %pK session %x handled\n", __func__,
+		inst, hash32_ptr(inst->session));
 	return rc;
 }
 
@@ -5838,8 +5907,8 @@
 		goto exit;
 	}
 
-	fps = USEC_PER_SEC;
-	do_div(fps, us_per_frame);
+	fps = us_per_frame > USEC_PER_SEC ?
+		0 : USEC_PER_SEC / (u32)us_per_frame;
 
 	if (fps % 15 == 14 || fps % 24 == 23)
 		fps = fps + 1;
@@ -6298,10 +6367,26 @@
 	}
 
 	mutex_lock(&inst->registeredbufs.lock);
-	list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
-		if (msm_comm_compare_dma_planes(inst, mbuf, dma_planes)) {
-			found = true;
-			break;
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
+			if (msm_comm_compare_dma_planes(inst, mbuf,
+					dma_planes)) {
+				found = true;
+				break;
+			}
+		}
+	} else {
+		/*
+		 * for encoder, client may queue the same buffer with different
+		 * fd before driver returned old buffer to the client. This
+		 * buffer should be treated as new buffer. Search the list with
+		 * fd so that it will be treated as new msm_vidc_buffer.
+		 */
+		list_for_each_entry(mbuf, &inst->registeredbufs.list, list) {
+			if (msm_comm_compare_vb2_planes(inst, mbuf, vb2)) {
+				found = true;
+				break;
+			}
 		}
 	}
 
@@ -6438,7 +6523,10 @@
 			if (msm_smem_unmap_dma_buf(inst, &mbuf->smem[i]))
 				print_vidc_buffer(VIDC_ERR,
 					"dqbuf: unmap failed..", inst, mbuf);
-		} /* else RBR event expected */
+		} else {
+			/* RBR event expected */
+			mbuf->flags |= MSM_VIDC_FLAG_RBR_PENDING;
+		}
 	}
 	/*
 	 * remove the entry if plane[0].refcount is zero else
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
index 79f3c6b..fa9cdee 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -77,6 +77,8 @@
 		V4L2_CTRL_DRIVER_PRIV(idx))
 void msm_comm_session_clean(struct msm_vidc_inst *inst);
 int msm_comm_kill_session(struct msm_vidc_inst *inst);
+void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
+void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
 enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst);
 enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst);
 int msm_comm_smem_alloc(struct msm_vidc_inst *inst, size_t size, u32 align,
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index c1828b3..dbebe5d 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -113,7 +113,6 @@
 
 #define MSM_VIDC_ERROR(value)					\
 	do {							\
-		dprintk(VIDC_DBG, "Fatal Level = %d\n", value);\
 		BUG_ON(value);					\
 	} while (0)
 
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index a0f7d2e..0b6331c 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_internal.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -45,6 +45,7 @@
 #define MIN_SUPPORTED_HEIGHT 32
 #define DEFAULT_FPS 15
 #define MIN_NUM_OUTPUT_BUFFERS 1
+#define MIN_NUM_OUTPUT_BUFFERS_VP9 6
 #define MIN_NUM_CAPTURE_BUFFERS 1
 #define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
 #define MAX_NUM_CAPTURE_BUFFERS VIDEO_MAX_FRAME // same as VB2_MAX_FRAME
@@ -195,12 +196,33 @@
 	int low_power_cycles;
 };
 
+enum efuse_purpose {
+	SKU_VERSION = 0,
+};
+
+enum sku_version {
+	SKU_VERSION_0 = 0,
+	SKU_VERSION_1,
+	SKU_VERSION_2,
+};
+
+struct msm_vidc_efuse_data {
+	u32 start_address;
+	u32 size;
+	u32 mask;
+	u32 shift;
+	enum efuse_purpose purpose;
+};
+
 struct msm_vidc_platform_data {
 	struct msm_vidc_common_data *common_data;
 	unsigned int common_data_length;
 	struct msm_vidc_codec_data *codec_data;
 	unsigned int codec_data_length;
 	struct msm_vidc_csc_coeff csc_data;
+	struct msm_vidc_efuse_data *efuse_data;
+	unsigned int efuse_data_length;
+	unsigned int sku_version;
 };
 
 struct msm_vidc_format {
@@ -218,7 +240,7 @@
 	int num_cores;
 	struct dentry *debugfs_root;
 	int thermal_level;
-	u32 platform_version;
+	u32 sku_version;
 };
 
 struct msm_video_device {
@@ -286,6 +308,7 @@
 	u32 opb_fourcc;
 	enum hal_work_mode work_mode;
 	bool low_latency_mode;
+	bool turbo_mode;
 };
 
 struct profile_data {
@@ -400,9 +423,9 @@
 	u32 id;
 	char name[MAX_NAME_LENGTH];
 	enum v4l2_ctrl_type type;
-	s32 minimum;
-	s32 maximum;
-	s32 default_value;
+	s64 minimum;
+	s64 maximum;
+	s64 default_value;
 	u32 step;
 	u32 menu_skip_mask;
 	u32 flags;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_platform.c b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
index 0fe09e2..d7641c3 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
@@ -24,6 +24,7 @@
 #include <linux/version.h>
 #include <linux/io.h>
 #include "msm_vidc_internal.h"
+#include "msm_vidc_debug.h"
 
 
 #define CODEC_ENTRY(n, p, vsp, vpp, lp) \
@@ -35,6 +36,15 @@
 	.low_power_cycles = lp	\
 }
 
+#define EFUSE_ENTRY(sa, s, m, sh, p) \
+{	\
+	.start_address = sa,		\
+	.size = s,	\
+	.mask = m,	\
+	.shift = sh,	\
+	.purpose = p	\
+}
+
 static struct msm_vidc_codec_data default_codec_data[] =  {
 	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320),
 	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 125, 675, 320),
@@ -44,7 +54,19 @@
 	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320),
 	CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320),
 	CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320),
-	CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 125, 675, 320),
+	CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540),
+	CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200),
+	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200),
+	CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200),
+	CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_DECODER, 50, 200, 200),
+	CODEC_ENTRY(V4L2_PIX_FMT_VP9, MSM_VIDC_DECODER, 50, 200, 200),
+};
+
+static struct msm_vidc_codec_data sdm670_codec_data[] =  {
+	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_ENCODER, 125, 675, 320),
+	CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_ENCODER, 125, 675, 320),
+	CODEC_ENTRY(V4L2_PIX_FMT_VP8, MSM_VIDC_ENCODER, 125, 675, 320),
+	CODEC_ENTRY(V4L2_PIX_FMT_TME, MSM_VIDC_ENCODER, 0, 540, 540),
 	CODEC_ENTRY(V4L2_PIX_FMT_MPEG2, MSM_VIDC_DECODER, 50, 200, 200),
 	CODEC_ENTRY(V4L2_PIX_FMT_H264, MSM_VIDC_DECODER, 50, 200, 200),
 	CODEC_ENTRY(V4L2_PIX_FMT_HEVC, MSM_VIDC_DECODER, 50, 200, 200),
@@ -93,12 +115,71 @@
 		.value = 1,
 	},
 	{
+		.key = "qcom,domain-attr-cache-pagetables",
+		.value = 1,
+	},
+	{
 		.key = "qcom,max-secure-instances",
 		.value = 5,
 	},
 	{
 		.key = "qcom,max-hw-load",
-		.value = 2563200,
+		.value = 3110400,	/* 4096x2160@90 */
+	},
+	{
+		.key = "qcom,max-hq-mbs-per-frame",
+		.value = 8160,
+	},
+	{
+		.key = "qcom,max-hq-frames-per-sec",
+		.value = 60,
+	},
+	{
+		.key = "qcom,max-b-frame-size",
+		.value = 8160,
+	},
+	{
+		.key = "qcom,max-b-frames-per-sec",
+		.value = 60,
+	},
+	{
+		.key = "qcom,power-collapse-delay",
+		.value = 1500,
+	},
+	{
+		.key = "qcom,hw-resp-timeout",
+		.value = 1000,
+	},
+	{
+		.key = "qcom,debug-timeout",
+		.value = 0,
+	},
+};
+
+static struct msm_vidc_common_data sdm670_common_data_v0[] = {
+	{
+		.key = "qcom,never-unload-fw",
+		.value = 1,
+	},
+	{
+		.key = "qcom,sw-power-collapse",
+		.value = 1,
+	},
+	{
+		.key = "qcom,domain-attr-non-fatal-faults",
+		.value = 1,
+	},
+	{
+		.key = "qcom,domain-attr-cache-pagetables",
+		.value = 1,
+	},
+	{
+		.key = "qcom,max-secure-instances",
+		.value = 5,
+	},
+	{
+		.key = "qcom,max-hw-load",
+		.value = 1944000,
 	},
 	{
 		.key = "qcom,max-hq-mbs-per-frame",
@@ -124,12 +205,62 @@
 		.key = "qcom,hw-resp-timeout",
 		.value = 250,
 	},
+};
+
+static struct msm_vidc_common_data sdm670_common_data_v1[] = {
 	{
-		.key = "qcom,debug-timeout",
-		.value = 0,
+		.key = "qcom,never-unload-fw",
+		.value = 1,
+	},
+	{
+		.key = "qcom,sw-power-collapse",
+		.value = 1,
+	},
+	{
+		.key = "qcom,domain-attr-non-fatal-faults",
+		.value = 1,
+	},
+	{
+		.key = "qcom,domain-attr-cache-pagetables",
+		.value = 1,
+	},
+	{
+		.key = "qcom,max-secure-instances",
+		.value = 5,
+	},
+	{
+		.key = "qcom,max-hw-load",
+		.value = 1216800,
+	},
+	{
+		.key = "qcom,max-hq-mbs-per-frame",
+		.value = 8160,
+	},
+	{
+		.key = "qcom,max-hq-frames-per-sec",
+		.value = 60,
+	},
+	{
+		.key = "qcom,max-b-frame-size",
+		.value = 8160,
+	},
+	{
+		.key = "qcom,max-b-frames-per-sec",
+		.value = 60,
+	},
+	{
+		.key = "qcom,power-collapse-delay",
+		.value = 500,
+	},
+	{
+		.key = "qcom,hw-resp-timeout",
+		.value = 250,
 	},
 };
 
+static struct msm_vidc_efuse_data sdm670_efuse_data[] = {
+	EFUSE_ENTRY(0x007801A0, 4, 0x00008000, 0x0f, SKU_VERSION),
+};
 
 static struct msm_vidc_platform_data default_data = {
 	.codec_data = default_codec_data,
@@ -139,6 +270,9 @@
 	.csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff,
 	.csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff,
 	.csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff,
+	.efuse_data = NULL,
+	.efuse_data_length = 0,
+	.sku_version = 0,
 };
 
 static struct msm_vidc_platform_data sdm845_data = {
@@ -149,6 +283,22 @@
 	.csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff,
 	.csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff,
 	.csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff,
+	.efuse_data = NULL,
+	.efuse_data_length = 0,
+	.sku_version = 0,
+};
+
+static struct msm_vidc_platform_data sdm670_data = {
+	.codec_data = sdm670_codec_data,
+	.codec_data_length =  ARRAY_SIZE(sdm670_codec_data),
+	.common_data = sdm670_common_data_v0,
+	.common_data_length =  ARRAY_SIZE(sdm670_common_data_v0),
+	.csc_data.vpe_csc_custom_bias_coeff = vpe_csc_custom_bias_coeff,
+	.csc_data.vpe_csc_custom_matrix_coeff = vpe_csc_custom_matrix_coeff,
+	.csc_data.vpe_csc_custom_limit_coeff = vpe_csc_custom_limit_coeff,
+	.efuse_data = sdm670_efuse_data,
+	.efuse_data_length = ARRAY_SIZE(sdm670_efuse_data),
+	.sku_version = 0,
 };
 
 static const struct of_device_id msm_vidc_dt_match[] = {
@@ -156,15 +306,63 @@
 		.compatible = "qcom,sdm845-vidc",
 		.data = &sdm845_data,
 	},
+	{
+		.compatible = "qcom,sdm670-vidc",
+		.data = &sdm670_data,
+	},
 	{},
 };
 
 MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
 
+static int msm_vidc_read_efuse(
+		struct msm_vidc_platform_data *data, struct device *dev)
+{
+	void __iomem *base;
+	uint32_t i;
+	struct msm_vidc_efuse_data *efuse_data = data->efuse_data;
+	uint32_t efuse_data_count = data->efuse_data_length;
+
+	for (i = 0; i < efuse_data_count; i++) {
+
+		switch ((efuse_data[i]).purpose) {
+
+		case SKU_VERSION:
+			base = devm_ioremap(dev, (efuse_data[i]).start_address,
+					(efuse_data[i]).size);
+			if (!base) {
+				dprintk(VIDC_ERR,
+					"failed efuse ioremap: res->start %#x, size %d\n",
+					(efuse_data[i]).start_address,
+					(efuse_data[i]).size);
+					return -EINVAL;
+			} else {
+				u32 efuse = 0;
+
+				efuse = readl_relaxed(base);
+				data->sku_version =
+					(efuse & (efuse_data[i]).mask) >>
+					(efuse_data[i]).shift;
+				dprintk(VIDC_DBG,
+					"efuse 0x%x, platform version 0x%x\n",
+					efuse, data->sku_version);
+
+				devm_iounmap(dev, base);
+			}
+			break;
+
+		default:
+			break;
+		}
+	}
+	return 0;
+}
+
 void *vidc_get_drv_data(struct device *dev)
 {
 	struct msm_vidc_platform_data *driver_data = NULL;
 	const struct of_device_id *match;
+	int rc = 0;
 
 	if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) {
 		driver_data = &default_data;
@@ -176,6 +374,21 @@
 	if (match)
 		driver_data = (struct msm_vidc_platform_data *)match->data;
 
+	if (!of_find_property(dev->of_node, "sku-index", NULL) ||
+			!driver_data) {
+		goto exit;
+	} else if (!strcmp(match->compatible, "qcom,sdm670-vidc")) {
+		rc = msm_vidc_read_efuse(driver_data, dev);
+		if (rc)
+			goto exit;
+
+		if (driver_data->sku_version == SKU_VERSION_1) {
+			driver_data->common_data = sdm670_common_data_v1;
+			driver_data->common_data_length =
+					ARRAY_SIZE(sdm670_common_data_v1);
+		}
+	}
+
 exit:
 	return driver_data;
 }
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index f343939..b1a240d 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -73,12 +73,6 @@
 	res->clock_freq_tbl.clk_prof_entries = NULL;
 }
 
-static inline void msm_vidc_free_platform_version_table(
-		struct msm_vidc_platform_resources *res)
-{
-	res->pf_ver_tbl = NULL;
-}
-
 static inline void msm_vidc_free_reg_table(
 			struct msm_vidc_platform_resources *res)
 {
@@ -133,7 +127,6 @@
 {
 	msm_vidc_free_clock_table(res);
 	msm_vidc_free_regulator_table(res);
-	msm_vidc_free_platform_version_table(res);
 	msm_vidc_free_allowed_clocks_table(res);
 	msm_vidc_free_reg_table(res);
 	msm_vidc_free_qdss_addr_table(res);
@@ -344,33 +337,6 @@
 }
 EXPORT_SYMBOL(msm_vidc_load_u32_table);
 
-static int msm_vidc_load_platform_version_table(
-		struct msm_vidc_platform_resources *res)
-{
-	int rc = 0;
-	struct platform_device *pdev = res->pdev;
-
-	if (!of_find_property(pdev->dev.of_node,
-			"qcom,platform-version", NULL)) {
-		dprintk(VIDC_DBG, "qcom,platform-version not found\n");
-		return 0;
-	}
-
-	rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node,
-			"qcom,platform-version",
-			sizeof(*res->pf_ver_tbl),
-			(u32 **)&res->pf_ver_tbl,
-			NULL);
-	if (rc) {
-		dprintk(VIDC_ERR,
-			"%s: failed to read platform version table\n",
-			__func__);
-		return rc;
-	}
-
-	return 0;
-}
-
 /* A comparator to compare loads (needed later on) */
 static int cmp(const void *a, const void *b)
 {
@@ -712,6 +678,29 @@
 	return rc;
 }
 
+static int msm_decide_dt_node(
+		struct msm_vidc_platform_resources *res) {
+	struct platform_device *pdev = res->pdev;
+	int rc = 0;
+	u32 sku_index = 0;
+
+	rc = of_property_read_u32(pdev->dev.of_node, "sku-index",
+			&sku_index);
+	if (rc) {
+		dprintk(VIDC_DBG, "'sku_index' not found in node\n");
+		return 0;
+	}
+
+	if (sku_index != res->sku_version) {
+		dprintk(VIDC_DBG,
+			"Failed to parser dt: sku_index %d res->sku_version - %d\n",
+			sku_index, res->sku_version);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 static int find_key_value(struct msm_vidc_platform_data *platform_data,
 	const char *key)
 {
@@ -743,6 +732,8 @@
 	res->codec_data_count = platform_data->codec_data_length;
 	res->codec_data = platform_data->codec_data;
 
+	res->sku_version = platform_data->sku_version;
+
 	res->fw_name = "venus";
 
 	dprintk(VIDC_DBG, "Firmware filename: %s\n", res->fw_name);
@@ -785,6 +776,8 @@
 			"qcom,hw-resp-timeout");
 	res->non_fatal_pagefaults = find_key_value(platform_data,
 			"qcom,domain-attr-non-fatal-faults");
+	res->cache_pagetables = find_key_value(platform_data,
+			"qcom,domain-attr-cache-pagetables");
 
 	res->csc_coeff_data = &platform_data->csc_data;
 
@@ -805,6 +798,11 @@
 		return -ENOENT;
 	}
 
+	rc = msm_decide_dt_node(res);
+	if (rc)
+		return rc;
+
+
 	INIT_LIST_HEAD(&res->context_banks);
 
 	res->firmware_base = (phys_addr_t)firmware_base;
@@ -816,10 +814,6 @@
 	kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
 	res->irq = kres ? kres->start : -1;
 
-	rc = msm_vidc_load_platform_version_table(res);
-	if (rc)
-		dprintk(VIDC_ERR, "Failed to load pf version table: %d\n", rc);
-
 	rc = msm_vidc_load_subcache_info(res);
 	if (rc)
 		dprintk(VIDC_WARN, "Failed to load subcache info: %d\n", rc);
@@ -909,14 +903,14 @@
 	return VMID_INVAL;
 }
 
-static int msm_vidc_setup_context_bank(struct context_bank_info *cb,
-		struct device *dev)
+static int msm_vidc_setup_context_bank(struct msm_vidc_platform_resources *res,
+		struct context_bank_info *cb, struct device *dev)
 {
 	int rc = 0;
 	int secure_vmid = VMID_INVAL;
 	struct bus_type *bus;
 
-	if (!dev || !cb) {
+	if (!dev || !cb || !res) {
 		dprintk(VIDC_ERR,
 			"%s: Invalid Input params\n", __func__);
 		return -EINVAL;
@@ -950,6 +944,19 @@
 		}
 	}
 
+	if (res->cache_pagetables) {
+		int cache_pagetables = 1;
+
+		rc = iommu_domain_set_attr(cb->mapping->domain,
+			DOMAIN_ATTR_USE_UPSTREAM_HINT, &cache_pagetables);
+		if (rc) {
+			WARN_ONCE(rc,
+				"%s: failed to set cache pagetables attribute, %d\n",
+				__func__, rc);
+			rc = 0;
+		}
+	}
+
 	rc = arm_iommu_attach_device(cb->dev, cb->mapping);
 	if (rc) {
 		dprintk(VIDC_ERR, "%s - Couldn't arm_iommu_attach_device\n",
@@ -1063,7 +1070,7 @@
 		cb->name, cb->addr_range.start,
 		cb->addr_range.size, cb->buffer_type);
 
-	rc = msm_vidc_setup_context_bank(cb, dev);
+	rc = msm_vidc_setup_context_bank(&core->resources, cb, dev);
 	if (rc) {
 		dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc);
 		goto err_setup_cb;
@@ -1175,7 +1182,7 @@
 			goto err_setup_cb;
 		}
 
-		rc = msm_vidc_setup_context_bank(cb, cb->dev);
+		rc = msm_vidc_setup_context_bank(res, cb, cb->dev);
 		if (rc) {
 			dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc);
 			goto err_setup_cb;
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
index 8309fce..23e33fe 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_resources.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -21,11 +21,6 @@
 
 #define MAX_BUFFER_TYPES 32
 
-struct platform_version_table {
-	u32 version_mask;
-	u32 version_shift;
-};
-
 struct dcvs_table {
 	u32 load;
 	u32 load_low;
@@ -153,7 +148,7 @@
 	phys_addr_t register_base;
 	uint32_t register_size;
 	uint32_t irq;
-	struct platform_version_table *pf_ver_tbl;
+	uint32_t sku_version;
 	struct allowed_clock_rates_table *allowed_clks_tbl;
 	u32 allowed_clks_tbl_size;
 	struct clock_freq_table clock_freq_tbl;
@@ -190,6 +185,7 @@
 	int msm_vidc_firmware_unload_delay;
 	uint32_t msm_vidc_pwr_collapse_delay;
 	bool non_fatal_pagefaults;
+	bool cache_pagetables;
 	struct msm_vidc_codec_data *codec_data;
 	int codec_data_count;
 	struct msm_vidc_csc_coeff *csc_coeff_data;
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index c94da61..7e7ed47 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -503,6 +503,11 @@
 
 	if (queue->qhdr_read_idx == queue->qhdr_write_idx) {
 		queue->qhdr_rx_req = receive_request;
+		/*
+		 * mb() to ensure qhdr is updated in main memory
+		 * so that venus reads the updated header values
+		 */
+		mb();
 		*pb_tx_req_is_set = 0;
 		dprintk(VIDC_DBG,
 			"%s queue is empty, rx_req = %u, tx_req = %u, read_idx = %u\n",
@@ -550,6 +555,11 @@
 		queue->qhdr_rx_req = 0;
 	else
 		queue->qhdr_rx_req = receive_request;
+	/*
+	 * mb() to ensure qhdr is updated in main memory
+	 * so that venus reads the updated header values
+	 */
+	mb();
 
 	*pb_tx_req_is_set = (queue->qhdr_tx_req == 1) ? 1 : 0;
 
@@ -1029,8 +1039,10 @@
 	flush_delayed_work(&venus_hfi_pm_work);
 
 	mutex_lock(&device->lock);
-	if (device->power_enabled)
+	if (device->power_enabled) {
+		dprintk(VIDC_WARN, "%s: Venus is busy\n", __func__);
 		rc = -EBUSY;
+	}
 	mutex_unlock(&device->lock);
 	return rc;
 }
@@ -1047,9 +1059,9 @@
 
 	mutex_lock(&device->lock);
 
-	if (device->power_enabled) {
-		dprintk(VIDC_DBG, "Venus is busy\n");
-		rc = -EBUSY;
+	if (!device->power_enabled) {
+		dprintk(VIDC_WARN, "%s: venus power off\n", __func__);
+		rc = -EINVAL;
 		goto exit;
 	}
 	__flush_debug_queue(device, NULL);
@@ -1693,6 +1705,12 @@
 
 	dev->bus_vote.data =
 		kzalloc(sizeof(struct vidc_bus_vote_data), GFP_KERNEL);
+	if (!dev->bus_vote.data) {
+		dprintk(VIDC_ERR, "Bus vote data memory is not allocated\n");
+		rc = -ENOMEM;
+		goto err_no_mem;
+	}
+
 	dev->bus_vote.data_count = 1;
 	dev->bus_vote.data->power_mode = VIDC_POWER_TURBO;
 
@@ -1751,9 +1769,8 @@
 	if (rc || __iface_cmdq_write(dev, &version_pkt))
 		dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n");
 
-	rc = __enable_subcaches(device);
-	if (!rc)
-		__set_subcaches(device);
+	__enable_subcaches(device);
+	__set_subcaches(device);
 
 	if (dev->res->pm_qos_latency_us) {
 #ifdef CONFIG_SMP
@@ -1770,6 +1787,7 @@
 	__set_state(dev, VENUS_STATE_DEINIT);
 	__unload_fw(dev);
 err_load_fw:
+err_no_mem:
 	dprintk(VIDC_ERR, "Core init failed\n");
 	mutex_unlock(&dev->lock);
 	return rc;
@@ -3790,8 +3808,9 @@
 	venus_hfi_for_each_subcache(device, sinfo) {
 		rc = llcc_slice_activate(sinfo->subcache);
 		if (rc) {
-			dprintk(VIDC_ERR, "Failed to activate %s: %d\n",
+			dprintk(VIDC_WARN, "Failed to activate %s: %d\n",
 				sinfo->name, rc);
+			msm_vidc_res_handle_fatal_hw_error(device->res, true);
 			goto err_activate_fail;
 		}
 		sinfo->isactive = true;
@@ -3806,7 +3825,7 @@
 err_activate_fail:
 	__release_subcaches(device);
 	__disable_subcaches(device);
-	return -EINVAL;
+	return 0;
 }
 
 static int __set_subcaches(struct venus_hfi_device *device)
@@ -3848,25 +3867,25 @@
 
 		rc = __core_set_resource(device, &rhdr, (void *)sc_res_info);
 		if (rc) {
-			dprintk(VIDC_ERR, "Failed to set subcaches %d\n", rc);
+			dprintk(VIDC_WARN, "Failed to set subcaches %d\n", rc);
 			goto err_fail_set_subacaches;
 		}
-	}
 
-	venus_hfi_for_each_subcache(device, sinfo) {
-		if (sinfo->isactive == true)
-			sinfo->isset = true;
-	}
+		venus_hfi_for_each_subcache(device, sinfo) {
+			if (sinfo->isactive == true)
+				sinfo->isset = true;
+		}
 
-	dprintk(VIDC_DBG, "Set Subcaches done to Venus\n");
-	device->res->sys_cache_res_set = true;
+		dprintk(VIDC_DBG, "Set Subcaches done to Venus\n");
+		device->res->sys_cache_res_set = true;
+	}
 
 	return 0;
 
 err_fail_set_subacaches:
 	__disable_subcaches(device);
 
-	return rc;
+	return 0;
 }
 
 static int __release_subcaches(struct venus_hfi_device *device)
@@ -3905,13 +3924,13 @@
 
 		rc = __core_release_resource(device, &rhdr);
 		if (rc)
-			dprintk(VIDC_ERR,
+			dprintk(VIDC_WARN,
 				"Failed to release %d subcaches\n", c);
 	}
 
 	device->res->sys_cache_res_set = false;
 
-	return rc;
+	return 0;
 }
 
 static int __disable_subcaches(struct venus_hfi_device *device)
@@ -3929,7 +3948,7 @@
 				sinfo->name);
 			rc = llcc_slice_deactivate(sinfo->subcache);
 			if (rc) {
-				dprintk(VIDC_ERR,
+				dprintk(VIDC_WARN,
 					"Failed to de-activate %s: %d\n",
 					sinfo->name, rc);
 			}
@@ -3937,7 +3956,7 @@
 		}
 	}
 
-	return rc;
+	return 0;
 }
 
 static int __venus_power_on(struct venus_hfi_device *device)
@@ -4111,9 +4130,8 @@
 
 	__sys_set_debug(device, msm_vidc_fw_debug);
 
-	rc = __enable_subcaches(device);
-	if (!rc)
-		__set_subcaches(device);
+	__enable_subcaches(device);
+	__set_subcaches(device);
 
 	dprintk(VIDC_PROF, "Resumed from power collapse\n");
 exit:
@@ -4282,10 +4300,65 @@
 	return rc;
 }
 
+static void __noc_error_info(struct venus_hfi_device *device, u32 core_num)
+{
+	u32 vcodec_core_video_noc_base_offs, val;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s: null device\n", __func__);
+		return;
+	}
+	if (!core_num) {
+		vcodec_core_video_noc_base_offs =
+			VCODEC_CORE0_VIDEO_NOC_BASE_OFFS;
+	} else if (core_num == 1) {
+		vcodec_core_video_noc_base_offs =
+			VCODEC_CORE1_VIDEO_NOC_BASE_OFFS;
+	} else {
+		dprintk(VIDC_ERR, "%s: invalid core_num %u\n",
+			__func__, core_num);
+		return;
+	}
+
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_SWID_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_SWID_LOW:     %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_SWID_HIGH_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_SWID_HIGH:    %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_MAINCTL_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_MAINCTL_LOW:  %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG0_LOW:  %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_HIGH_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG0_HIGH: %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG1_LOW:  %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_HIGH_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG1_HIGH: %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG2_LOW:  %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_HIGH_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG2_HIGH: %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_LOW_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG3_LOW:  %#x\n", core_num, val);
+	val = __read_register(device, vcodec_core_video_noc_base_offs +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_HIGH_OFFS);
+	dprintk(VIDC_ERR, "CORE%d_NOC_ERR_ERRLOG3_HIGH: %#x\n", core_num, val);
+}
+
 static int venus_hfi_noc_error_info(void *dev)
 {
 	struct venus_hfi_device *device;
-	u32 val = 0;
+	const u32 core0 = 0, core1 = 1;
 
 	if (!dev) {
 		dprintk(VIDC_ERR, "%s: null device\n", __func__);
@@ -4296,44 +4369,13 @@
 	mutex_lock(&device->lock);
 	dprintk(VIDC_ERR, "%s: non error information\n", __func__);
 
-	val = __read_register(device, 0x0C500);
-	dprintk(VIDC_ERR, "NOC_ERR_SWID_LOW(0x00AA0C500):     %#x\n", val);
+	if (__read_register(device, VCODEC_CORE0_VIDEO_NOC_BASE_OFFS +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS))
+		__noc_error_info(device, core0);
 
-	val = __read_register(device, 0x0C504);
-	dprintk(VIDC_ERR, "NOC_ERR_SWID_HIGH(0x00AA0C504):    %#x\n", val);
-
-	val = __read_register(device, 0x0C508);
-	dprintk(VIDC_ERR, "NOC_ERR_MAINCTL_LOW(0x00AA0C508):  %#x\n", val);
-
-	val = __read_register(device, 0x0C510);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRVLD_LOW(0x00AA0C510):   %#x\n", val);
-
-	val = __read_register(device, 0x0C518);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRCLR_LOW(0x00AA0C518):   %#x\n", val);
-
-	val = __read_register(device, 0x0C520);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG0_LOW(0x00AA0C520):  %#x\n", val);
-
-	val = __read_register(device, 0x0C524);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG0_HIGH(0x00AA0C524): %#x\n", val);
-
-	val = __read_register(device, 0x0C528);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG1_LOW(0x00AA0C528):  %#x\n", val);
-
-	val = __read_register(device, 0x0C52C);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG1_HIGH(0x00AA0C52C): %#x\n", val);
-
-	val = __read_register(device, 0x0C530);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG2_LOW(0x00AA0C530):  %#x\n", val);
-
-	val = __read_register(device, 0x0C534);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG2_HIGH(0x00AA0C534): %#x\n", val);
-
-	val = __read_register(device, 0x0C538);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG3_LOW(0x00AA0C538):  %#x\n", val);
-
-	val = __read_register(device, 0x0C53C);
-	dprintk(VIDC_ERR, "NOC_ERR_ERRLOG3_HIGH(0x00AA0C53C): %#x\n", val);
+	if (__read_register(device, VCODEC_CORE1_VIDEO_NOC_BASE_OFFS +
+			VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS))
+		__noc_error_info(device, core1);
 
 	mutex_unlock(&device->lock);
 
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 738828d..2260b55 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -226,6 +226,7 @@
 	HAL_PARAM_VIDEO_CORES_USAGE,
 	HAL_PARAM_VIDEO_WORK_MODE,
 	HAL_PARAM_SECURE,
+	HAL_PARAM_VENC_HDR10_PQ_SEI,
 };
 
 enum hal_domain {
@@ -660,6 +661,7 @@
 	HAL_FLIP_NONE,
 	HAL_FLIP_HORIZONTAL,
 	HAL_FLIP_VERTICAL,
+	HAL_FLIP_BOTH,
 	HAL_UNUSED_FLIP = 0x10000000,
 };
 
@@ -1397,6 +1399,11 @@
 	u32 rg_property_data[1];
 };
 
+struct hal_hdr10_pq_sei {
+	struct msm_vidc_mastering_display_colour_sei_payload disp_color_sei;
+	struct msm_vidc_content_light_level_sei_payload cll_sei;
+};
+
 #define call_hfi_op(q, op, args...)			\
 	(((q) && (q)->op) ? ((q)->op(args)) : 0)
 
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
index 001ca39..ca6d803 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -321,6 +321,10 @@
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x033)
 #define  HFI_PROPERTY_PARAM_VENC_IFRAMESIZE			\
 	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034)
+#define  HFI_PROPERTY_PARAM_VENC_SEND_OUTPUT_FOR_SKIPPED_FRAMES	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x035)
+#define  HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x036)
 
 #define HFI_PROPERTY_CONFIG_VENC_COMMON_START				\
 	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
@@ -1058,4 +1062,24 @@
 	u32 packet_type;
 	u32 trigger_type;
 };
+
+struct hfi_mastering_display_colour_sei_payload {
+	u32 display_primariesX[3];
+	u32 display_primariesY[3];
+	u32 white_pointX;
+	u32 white_pointY;
+	u32 max_display_mastering_luminance;
+	u32 min_display_mastering_luminance;
+};
+
+struct hfi_content_light_level_sei_payload {
+	u32 max_content_light;
+	u32 max_pic_average_light;
+};
+
+struct hfi_hdr10_pq_sei {
+	struct hfi_mastering_display_colour_sei_payload mdisp_info;
+	struct hfi_content_light_level_sei_payload cll_info;
+};
+
 #endif
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
index fc32d73..3433483 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_io.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
@@ -190,4 +190,25 @@
 #define VIDC_UC_REGION_ADDR 0x000D2064
 #define VIDC_UC_REGION_SIZE 0x000D2068
 
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: vcodec noc error log registers
+ * --------------------------------------------------------------------------
+ */
+#define VCODEC_CORE0_VIDEO_NOC_BASE_OFFS		0x00004000
+#define VCODEC_CORE1_VIDEO_NOC_BASE_OFFS		0x0000C000
+#define VCODEC_COREX_VIDEO_NOC_ERR_SWID_LOW_OFFS	0x0500
+#define VCODEC_COREX_VIDEO_NOC_ERR_SWID_HIGH_OFFS	0x0504
+#define VCODEC_COREX_VIDEO_NOC_ERR_MAINCTL_LOW_OFFS	0x0508
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRVLD_LOW_OFFS	0x0510
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRCLR_LOW_OFFS	0x0518
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_LOW_OFFS	0x0520
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG0_HIGH_OFFS	0x0524
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_LOW_OFFS	0x0528
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG1_HIGH_OFFS	0x052C
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_LOW_OFFS	0x0530
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG2_HIGH_OFFS	0x0534
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_LOW_OFFS	0x0538
+#define VCODEC_COREX_VIDEO_NOC_ERR_ERRLOG3_HIGH_OFFS	0x053C
+
 #endif
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mtk-vpu/mtk_vpu.c
index c9bf58c..04b8b87 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.c
+++ b/drivers/media/platform/mtk-vpu/mtk_vpu.c
@@ -23,6 +23,7 @@
 #include <linux/of_reserved_mem.h>
 #include <linux/sched.h>
 #include <linux/sizes.h>
+#include <linux/dma-mapping.h>
 
 #include "mtk_vpu.h"
 
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
index c3277308..b49f80c 100644
--- a/drivers/media/rc/ir-lirc-codec.c
+++ b/drivers/media/rc/ir-lirc-codec.c
@@ -254,7 +254,7 @@
 		return 0;
 
 	case LIRC_GET_REC_RESOLUTION:
-		val = dev->rx_resolution;
+		val = dev->rx_resolution / 1000;
 		break;
 
 	case LIRC_SET_WIDEBAND_RECEIVER:
diff --git a/drivers/media/usb/cx231xx/cx231xx-core.c b/drivers/media/usb/cx231xx/cx231xx-core.c
index 8b099fe..71b65ab 100644
--- a/drivers/media/usb/cx231xx/cx231xx-core.c
+++ b/drivers/media/usb/cx231xx/cx231xx-core.c
@@ -356,7 +356,12 @@
 	 */
 	if ((ven_req->wLength > 4) && ((ven_req->bRequest == 0x4) ||
 					(ven_req->bRequest == 0x5) ||
-					(ven_req->bRequest == 0x6))) {
+					(ven_req->bRequest == 0x6) ||
+
+					/* Internal Master 3 Bus can send
+					 * and receive only 4 bytes per time
+					 */
+					(ven_req->bRequest == 0x2))) {
 		unsend_size = 0;
 		pdata = ven_req->pBuff;
 
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 252ab99..9f2a64c 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -2005,6 +2005,13 @@
 		goto done;
 	}
 
+	/* Validate the user-provided bit-size and offset */
+	if (mapping->size > 32 ||
+	    mapping->offset + mapping->size > ctrl->info.size * 8) {
+		ret = -EINVAL;
+		goto done;
+	}
+
 	list_for_each_entry(map, &ctrl->info.mappings, list) {
 		if (mapping->id == map->id) {
 			uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s', "
diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c
index 207cc49..8062d37 100644
--- a/drivers/mfd/ab8500-sysctrl.c
+++ b/drivers/mfd/ab8500-sysctrl.c
@@ -98,7 +98,7 @@
 	u8 bank;
 
 	if (sysctrl_dev == NULL)
-		return -EINVAL;
+		return -EPROBE_DEFER;
 
 	bank = (reg >> 8);
 	if (!valid_bank(bank))
@@ -114,11 +114,13 @@
 	u8 bank;
 
 	if (sysctrl_dev == NULL)
-		return -EINVAL;
+		return -EPROBE_DEFER;
 
 	bank = (reg >> 8);
-	if (!valid_bank(bank))
+	if (!valid_bank(bank)) {
+		pr_err("invalid bank\n");
 		return -EINVAL;
+	}
 
 	return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
 		(u8)(reg & 0xFF), mask, value);
@@ -145,9 +147,15 @@
 	return 0;
 }
 
+static const struct of_device_id ab8500_sysctrl_match[] = {
+	{ .compatible = "stericsson,ab8500-sysctrl", },
+	{}
+};
+
 static struct platform_driver ab8500_sysctrl_driver = {
 	.driver = {
 		.name = "ab8500-sysctrl",
+		.of_match_table = ab8500_sysctrl_match,
 	},
 	.probe = ab8500_sysctrl_probe,
 	.remove = ab8500_sysctrl_remove,
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index ba130be..9617fc3 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -205,14 +205,14 @@
 static struct resource axp288_power_button_resources[] = {
 	{
 		.name	= "PEK_DBR",
-		.start	= AXP288_IRQ_POKN,
-		.end	= AXP288_IRQ_POKN,
+		.start	= AXP288_IRQ_POKP,
+		.end	= AXP288_IRQ_POKP,
 		.flags	= IORESOURCE_IRQ,
 	},
 	{
 		.name	= "PEK_DBF",
-		.start	= AXP288_IRQ_POKP,
-		.end	= AXP288_IRQ_POKP,
+		.start	= AXP288_IRQ_POKN,
+		.end	= AXP288_IRQ_POKN,
 		.flags	= IORESOURCE_IRQ,
 	},
 };
diff --git a/drivers/mfd/qcom-i2c-pmic.c b/drivers/mfd/qcom-i2c-pmic.c
index 590e4c1..28c3343 100644
--- a/drivers/mfd/qcom-i2c-pmic.c
+++ b/drivers/mfd/qcom-i2c-pmic.c
@@ -62,8 +62,12 @@
 	struct irq_domain	*domain;
 	struct i2c_pmic_periph	*periph;
 	struct pinctrl		*pinctrl;
+	struct mutex		irq_complete;
 	const char		*pinctrl_name;
 	int			num_periphs;
+	int			summary_irq;
+	bool			resume_completed;
+	bool			irq_waiting;
 };
 
 static void i2c_pmic_irq_bus_lock(struct irq_data *d)
@@ -400,6 +404,16 @@
 	unsigned int summary_status;
 	int rc, i;
 
+	mutex_lock(&chip->irq_complete);
+	chip->irq_waiting = true;
+	if (!chip->resume_completed) {
+		pr_debug("IRQ triggered before device-resume\n");
+		disable_irq_nosync(irq);
+		mutex_unlock(&chip->irq_complete);
+		return IRQ_HANDLED;
+	}
+	chip->irq_waiting = false;
+
 	for (i = 0; i < DIV_ROUND_UP(chip->num_periphs, BITS_PER_BYTE); i++) {
 		rc = regmap_read(chip->regmap, I2C_INTR_STATUS_BASE + i,
 				&summary_status);
@@ -416,6 +430,8 @@
 		i2c_pmic_summary_status_handler(chip, periph, summary_status);
 	}
 
+	mutex_unlock(&chip->irq_complete);
+
 	return IRQ_HANDLED;
 }
 
@@ -559,6 +575,9 @@
 		}
 	}
 
+	chip->resume_completed = true;
+	mutex_init(&chip->irq_complete);
+
 	rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
 				       i2c_pmic_irq_handler,
 				       IRQF_ONESHOT | IRQF_SHARED,
@@ -568,6 +587,7 @@
 		goto cleanup;
 	}
 
+	chip->summary_irq = client->irq;
 	enable_irq_wake(client->irq);
 
 probe_children:
@@ -594,6 +614,17 @@
 }
 
 #ifdef CONFIG_PM_SLEEP
+static int i2c_pmic_suspend_noirq(struct device *dev)
+{
+	struct i2c_pmic *chip = dev_get_drvdata(dev);
+
+	if (chip->irq_waiting) {
+		pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n");
+		return -EBUSY;
+	}
+	return 0;
+}
+
 static int i2c_pmic_suspend(struct device *dev)
 {
 	struct i2c_pmic *chip = dev_get_drvdata(dev);
@@ -618,6 +649,11 @@
 			pr_err_ratelimited("Couldn't enable 0x%04x wake irqs 0x%02x rc=%d\n",
 			       periph->addr, periph->wake, rc);
 	}
+	if (!rc) {
+		mutex_lock(&chip->irq_complete);
+		chip->resume_completed = false;
+		mutex_unlock(&chip->irq_complete);
+	}
 
 	return rc;
 }
@@ -647,10 +683,38 @@
 			       periph->addr, periph->synced[IRQ_EN_SET], rc);
 	}
 
+	mutex_lock(&chip->irq_complete);
+	chip->resume_completed = true;
+	if (chip->irq_waiting) {
+		mutex_unlock(&chip->irq_complete);
+		/* irq was pending, call the handler */
+		i2c_pmic_irq_handler(chip->summary_irq, chip);
+		enable_irq(chip->summary_irq);
+	} else {
+		mutex_unlock(&chip->irq_complete);
+	}
+
 	return rc;
 }
+#else
+static int i2c_pmic_suspend(struct device *dev)
+{
+	return 0;
+}
+static int i2c_pmic_resume(struct device *dev)
+{
+	return 0;
+}
+static int i2c_pmic_suspend_noirq(struct device *dev)
+{
+	return 0
+}
 #endif
-static SIMPLE_DEV_PM_OPS(i2c_pmic_pm_ops, i2c_pmic_suspend, i2c_pmic_resume);
+static const struct dev_pm_ops i2c_pmic_pm_ops = {
+	.suspend	= i2c_pmic_suspend,
+	.suspend_noirq	= i2c_pmic_suspend_noirq,
+	.resume		= i2c_pmic_resume,
+};
 
 static const struct of_device_id i2c_pmic_match_table[] = {
 	{ .compatible = "qcom,i2c-pmic", },
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index cada3c1..e3f4c39 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -791,6 +791,13 @@
 	  Per UID based io statistics exported to /proc/uid_io
 	  Per UID based procstat control in /proc/uid_procstat
 
+config UID_SYS_STATS_DEBUG
+	bool "Per-TASK statistics"
+	depends on UID_SYS_STATS
+	default n
+	help
+	  Per TASK based io statistics exported to /proc/uid_io
+
 config MEMORY_STATE_TIME
 	tristate "Memory freq/bandwidth time statistics"
 	depends on PROFILING
diff --git a/drivers/misc/cxl/api.c b/drivers/misc/cxl/api.c
index 2e5233b..ae85616 100644
--- a/drivers/misc/cxl/api.c
+++ b/drivers/misc/cxl/api.c
@@ -244,6 +244,10 @@
 		ctx->real_mode = false;
 	}
 
+	/*
+	 * Increment driver use count. Enables global TLBIs for hash
+	 * and callbacks to handle the segment table
+	 */
 	cxl_ctx_get();
 
 	if ((rc = cxl_ops->attach_process(ctx, kernel, wed, 0))) {
diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c
index afa2113..d3e0094 100644
--- a/drivers/misc/cxl/file.c
+++ b/drivers/misc/cxl/file.c
@@ -91,7 +91,6 @@
 
 	pr_devel("afu_open pe: %i\n", ctx->pe);
 	file->private_data = ctx;
-	cxl_ctx_get();
 
 	/* indicate success */
 	rc = 0;
@@ -213,6 +212,12 @@
 	ctx->glpid = get_task_pid(current->group_leader, PIDTYPE_PID);
 
 
+	/*
+	 * Increment driver use count. Enables global TLBIs for hash
+	 * and callbacks to handle the segment table
+	 */
+	cxl_ctx_get();
+
 	trace_cxl_attach(ctx, work.work_element_descriptor, work.num_interrupts, amr);
 
 	if ((rc = cxl_ops->attach_process(ctx, false, work.work_element_descriptor,
@@ -222,6 +227,7 @@
 		put_pid(ctx->glpid);
 		put_pid(ctx->pid);
 		ctx->glpid = ctx->pid = NULL;
+		cxl_ctx_put();
 		goto out;
 	}
 
diff --git a/drivers/misc/cxl/pci.c b/drivers/misc/cxl/pci.c
index fa4fe02..eef202d 100644
--- a/drivers/misc/cxl/pci.c
+++ b/drivers/misc/cxl/pci.c
@@ -1620,6 +1620,9 @@
 	cxl_sysfs_adapter_remove(adapter);
 	cxl_debugfs_adapter_remove(adapter);
 
+	/* Flush adapter datacache as its about to be removed */
+	cxl_data_cache_flush(adapter);
+
 	cxl_deconfigure_adapter(adapter);
 
 	device_unregister(&adapter->dev);
diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c
index 7eeb71a..4d44084 100644
--- a/drivers/misc/lkdtm_core.c
+++ b/drivers/misc/lkdtm_core.c
@@ -535,7 +535,9 @@
 	/* Handle test-specific clean-up. */
 	lkdtm_usercopy_exit();
 
-	unregister_jprobe(lkdtm_jprobe);
+	if (lkdtm_jprobe != NULL)
+		unregister_jprobe(lkdtm_jprobe);
+
 	pr_info("Crash point unregistered\n");
 }
 
diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c
index e2af61f..451d417 100644
--- a/drivers/misc/mei/client.c
+++ b/drivers/misc/mei/client.c
@@ -1320,6 +1320,9 @@
 		return -EOPNOTSUPP;
 	}
 
+	if (!mei_cl_is_connected(cl))
+		return -ENODEV;
+
 	rets = pm_runtime_get(dev->dev);
 	if (rets < 0 && rets != -EINPROGRESS) {
 		pm_runtime_put_noidle(dev->dev);
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index 3c11de0..690b28b 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -135,7 +135,7 @@
 	struct qpnp_misc_dev *mdev = NULL;
 	struct qpnp_misc_dev *mdev_found = NULL;
 	int rc;
-	u8 temp;
+	u8 temp = 0;
 
 	if (IS_ERR_OR_NULL(node)) {
 		pr_err("Invalid device node pointer\n");
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 7077b30..4c4835d 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -252,7 +252,6 @@
 	bool  whitelist_support;
 	bool  commonlib_loaded;
 	bool  commonlib64_loaded;
-	struct ion_handle *cmnlib_ion_handle;
 	struct ce_hw_usage_info ce_info;
 
 	int qsee_bw_count;
@@ -1901,20 +1900,22 @@
 	ptr_app->blocked_on_listener_id = resp->data;
 
 	/* sleep until listener is available */
-	qseecom.app_block_ref_cnt++;
-	ptr_app->app_blocked = true;
-	mutex_unlock(&app_access_lock);
-	if (wait_event_freezable(
+	do {
+		qseecom.app_block_ref_cnt++;
+		ptr_app->app_blocked = true;
+		mutex_unlock(&app_access_lock);
+		if (wait_event_freezable(
 			list_ptr->listener_block_app_wq,
 			!list_ptr->listener_in_use)) {
-		pr_err("Interrupted: listener_id %d, app_id %d\n",
+			pr_err("Interrupted: listener_id %d, app_id %d\n",
 				resp->data, ptr_app->app_id);
-		ret = -ERESTARTSYS;
-		goto exit;
-	}
-	mutex_lock(&app_access_lock);
-	ptr_app->app_blocked = false;
-	qseecom.app_block_ref_cnt--;
+			ret = -ERESTARTSYS;
+			goto exit;
+		}
+		mutex_lock(&app_access_lock);
+		ptr_app->app_blocked = false;
+		qseecom.app_block_ref_cnt--;
+	}  while (list_ptr->listener_in_use);
 
 	ptr_app->blocked_on_listener_id = 0;
 	/* notify the blocked app that listener is available */
@@ -1965,18 +1966,20 @@
 	pr_debug("lsntr %d in_use = %d\n",
 			resp->data, list_ptr->listener_in_use);
 	/* sleep until listener is available */
-	qseecom.app_block_ref_cnt++;
-	mutex_unlock(&app_access_lock);
-	if (wait_event_freezable(
+	do {
+		qseecom.app_block_ref_cnt++;
+		mutex_unlock(&app_access_lock);
+		if (wait_event_freezable(
 			list_ptr->listener_block_app_wq,
 			!list_ptr->listener_in_use)) {
-		pr_err("Interrupted: listener_id %d, session_id %d\n",
+			pr_err("Interrupted: listener_id %d, session_id %d\n",
 				resp->data, session_id);
-		ret = -ERESTARTSYS;
-		goto exit;
-	}
-	mutex_lock(&app_access_lock);
-	qseecom.app_block_ref_cnt--;
+			ret = -ERESTARTSYS;
+			goto exit;
+		}
+		mutex_lock(&app_access_lock);
+		qseecom.app_block_ref_cnt--;
+	}  while (list_ptr->listener_in_use);
 
 	/* notify TZ that listener is available */
 	pr_warn("Lsntr %d is available, unblock session(%d) in TZ\n",
@@ -2617,6 +2620,8 @@
 				if (!strcmp((void *)ptr_app->app_name,
 					(void *)data->client.app_name)) {
 					found_app = true;
+					if (ptr_app->app_blocked)
+						app_crash = false;
 					if (app_crash || ptr_app->ref_cnt == 1)
 						unload = true;
 					break;
@@ -3126,6 +3131,7 @@
 				struct qseecom_send_cmd_req *req)
 {
 	int ret = 0;
+	int ret2 = 0;
 	u32 reqd_len_sb_in = 0;
 	struct qseecom_client_send_data_ireq send_data_req = {0};
 	struct qseecom_client_send_data_64bit_ireq send_data_req_64bit = {0};
@@ -3224,32 +3230,38 @@
 	if (ret) {
 		pr_err("scm_call() failed with err: %d (app_id = %d)\n",
 					ret, data->client.app_id);
-		return ret;
+		goto exit;
 	}
 
 	if (qseecom.qsee_reentrancy_support) {
 		ret = __qseecom_process_reentrancy(&resp, ptr_app, data);
+		if (ret)
+			goto exit;
 	} else {
 		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
 			ret = __qseecom_process_incomplete_cmd(data, &resp);
 			if (ret) {
 				pr_err("process_incomplete_cmd failed err: %d\n",
 						ret);
-				return ret;
+				goto exit;
 			}
 		} else {
 			if (resp.result != QSEOS_RESULT_SUCCESS) {
 				pr_err("Response result %d not supported\n",
 								resp.result);
 				ret = -EINVAL;
+				goto exit;
 			}
 		}
 	}
-	ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+exit:
+	ret2 = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
 				data->client.sb_virt, data->client.sb_length,
 				ION_IOC_INV_CACHES);
-	if (ret)
-		pr_err("cache operation failed %d\n", ret);
+	if (ret2) {
+		pr_err("cache operation failed %d\n", ret2);
+		return ret2;
+	}
 	return ret;
 }
 
@@ -4291,6 +4303,7 @@
 	void *cmd_buf = NULL;
 	size_t cmd_len;
 	uint32_t app_arch = 0;
+	struct ion_handle *cmnlib_ion_handle = NULL;
 
 	if (!cmnlib_name) {
 		pr_err("cmnlib_name is NULL\n");
@@ -4305,7 +4318,7 @@
 	if (__qseecom_get_fw_size(cmnlib_name, &fw_size, &app_arch))
 		return -EIO;
 
-	ret = __qseecom_allocate_img_data(&qseecom.cmnlib_ion_handle,
+	ret = __qseecom_allocate_img_data(&cmnlib_ion_handle,
 						&img_data, fw_size, &pa);
 	if (ret)
 		return -EIO;
@@ -4346,7 +4359,7 @@
 		goto exit_unregister_bus_bw_need;
 	}
 
-	ret = msm_ion_do_cache_op(qseecom.ion_clnt, qseecom.cmnlib_ion_handle,
+	ret = msm_ion_do_cache_op(qseecom.ion_clnt, cmnlib_ion_handle,
 				img_data, fw_size,
 				ION_IOC_CLEAN_INV_CACHES);
 	if (ret) {
@@ -4394,7 +4407,7 @@
 	}
 
 exit_free_img_data:
-	__qseecom_free_img_data(&qseecom.cmnlib_ion_handle);
+	__qseecom_free_img_data(&cmnlib_ion_handle);
 	return ret;
 }
 
@@ -4778,8 +4791,12 @@
 	resp.data = desc->ret[2];	/*listener_id*/
 
 	mutex_lock(&app_access_lock);
-	ret = __qseecom_process_reentrancy(&resp, &dummy_app_entry,
+	if (qseecom.qsee_reentrancy_support)
+		ret = __qseecom_process_reentrancy(&resp, &dummy_app_entry,
 					&dummy_private_data);
+	else
+		ret = __qseecom_process_incomplete_cmd(&dummy_private_data,
+					&resp);
 	mutex_unlock(&app_access_lock);
 	if (ret)
 		pr_err("Failed on cmd %d for lsnr %d session %d, ret = %d\n",
@@ -6575,6 +6592,7 @@
 	bool found_app = false;
 	unsigned long flags;
 	int ret = 0;
+	int ret2 = 0;
 	uint32_t reqd_len_sb_in = 0;
 	void *cmd_buf = NULL;
 	size_t cmd_len;
@@ -6684,43 +6702,47 @@
 	if (ret) {
 		pr_err("scm_call() failed with err: %d (app_id = %d)\n",
 					ret, data->client.app_id);
-		return ret;
+		goto exit;
 	}
 
 	if (qseecom.qsee_reentrancy_support) {
 		ret = __qseecom_process_reentrancy(&resp, ptr_app, data);
+		if (ret)
+			goto exit;
 	} else {
 		if (resp.result == QSEOS_RESULT_INCOMPLETE) {
 			ret = __qseecom_process_incomplete_cmd(data, &resp);
 			if (ret) {
 				pr_err("process_incomplete_cmd failed err: %d\n",
 						ret);
-				return ret;
+				goto exit;
 			}
 		} else {
 			if (resp.result != QSEOS_RESULT_SUCCESS) {
 				pr_err("Response result %d not supported\n",
 								resp.result);
 				ret = -EINVAL;
+				goto exit;
 			}
 		}
 	}
-	ret = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
+exit:
+	ret2 = msm_ion_do_cache_op(qseecom.ion_clnt, data->client.ihandle,
 				data->client.sb_virt, data->client.sb_length,
 				ION_IOC_INV_CACHES);
-	if (ret) {
+	if (ret2) {
 		pr_err("cache operation failed %d\n", ret);
-		return ret;
+		return ret2;
 	}
 
 	if ((cmd_id == QSEOS_TEE_OPEN_SESSION) ||
 			(cmd_id == QSEOS_TEE_REQUEST_CANCELLATION)) {
-		ret = __qseecom_update_qteec_req_buf(
+		ret2 = __qseecom_update_qteec_req_buf(
 			(struct qseecom_qteec_modfd_req *)req, data, true);
-		if (ret)
-			return ret;
+		if (ret2)
+			return ret2;
 	}
-	return 0;
+	return ret;
 }
 
 static int qseecom_qteec_open_session(struct qseecom_dev_handle *data,
@@ -7844,7 +7866,7 @@
 	uint32_t hlos_num_ce_hw_instances;
 	uint32_t disk_encrypt_pipe;
 	uint32_t file_encrypt_pipe;
-	uint32_t hlos_ce_hw_instance[MAX_CE_PIPE_PAIR_PER_UNIT];
+	uint32_t hlos_ce_hw_instance[MAX_CE_PIPE_PAIR_PER_UNIT] = {0};
 	int i;
 	const int *tbl;
 	int size;
diff --git a/drivers/misc/uid_sys_stats.c b/drivers/misc/uid_sys_stats.c
index 8bf4c57..7d69dd5 100644
--- a/drivers/misc/uid_sys_stats.c
+++ b/drivers/misc/uid_sys_stats.c
@@ -19,6 +19,7 @@
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
+#include <linux/mm.h>
 #include <linux/proc_fs.h>
 #include <linux/profile.h>
 #include <linux/rtmutex.h>
@@ -53,6 +54,15 @@
 #define UID_STATE_DEAD_TASKS	4
 #define UID_STATE_SIZE		5
 
+#define MAX_TASK_COMM_LEN 256
+
+struct task_entry {
+	char comm[MAX_TASK_COMM_LEN];
+	pid_t pid;
+	struct io_stats io[UID_STATE_SIZE];
+	struct hlist_node hash;
+};
+
 struct uid_entry {
 	uid_t uid;
 	cputime_t utime;
@@ -62,8 +72,231 @@
 	int state;
 	struct io_stats io[UID_STATE_SIZE];
 	struct hlist_node hash;
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+	DECLARE_HASHTABLE(task_entries, UID_HASH_BITS);
+#endif
 };
 
+static u64 compute_write_bytes(struct task_struct *task)
+{
+	if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
+		return 0;
+
+	return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
+}
+
+static void compute_io_bucket_stats(struct io_stats *io_bucket,
+					struct io_stats *io_curr,
+					struct io_stats *io_last,
+					struct io_stats *io_dead)
+{
+	/* tasks could switch to another uid group, but its io_last in the
+	 * previous uid group could still be positive.
+	 * therefore before each update, do an overflow check first
+	 */
+	int64_t delta;
+
+	delta = io_curr->read_bytes + io_dead->read_bytes -
+		io_last->read_bytes;
+	io_bucket->read_bytes += delta > 0 ? delta : 0;
+	delta = io_curr->write_bytes + io_dead->write_bytes -
+		io_last->write_bytes;
+	io_bucket->write_bytes += delta > 0 ? delta : 0;
+	delta = io_curr->rchar + io_dead->rchar - io_last->rchar;
+	io_bucket->rchar += delta > 0 ? delta : 0;
+	delta = io_curr->wchar + io_dead->wchar - io_last->wchar;
+	io_bucket->wchar += delta > 0 ? delta : 0;
+	delta = io_curr->fsync + io_dead->fsync - io_last->fsync;
+	io_bucket->fsync += delta > 0 ? delta : 0;
+
+	io_last->read_bytes = io_curr->read_bytes;
+	io_last->write_bytes = io_curr->write_bytes;
+	io_last->rchar = io_curr->rchar;
+	io_last->wchar = io_curr->wchar;
+	io_last->fsync = io_curr->fsync;
+
+	memset(io_dead, 0, sizeof(struct io_stats));
+}
+
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+static void get_full_task_comm(struct task_entry *task_entry,
+		struct task_struct *task)
+{
+	int i = 0, offset = 0, len = 0;
+	/* save one byte for terminating null character */
+	int unused_len = MAX_TASK_COMM_LEN - TASK_COMM_LEN - 1;
+	char buf[unused_len];
+	struct mm_struct *mm = task->mm;
+
+	/* fill the first TASK_COMM_LEN bytes with thread name */
+	get_task_comm(task_entry->comm, task);
+	i = strlen(task_entry->comm);
+	while (i < TASK_COMM_LEN)
+		task_entry->comm[i++] = ' ';
+
+	/* next the executable file name */
+	if (mm) {
+		down_read(&mm->mmap_sem);
+		if (mm->exe_file) {
+			char *pathname = d_path(&mm->exe_file->f_path, buf,
+					unused_len);
+
+			if (!IS_ERR(pathname)) {
+				len = strlcpy(task_entry->comm + i, pathname,
+						unused_len);
+				i += len;
+				task_entry->comm[i++] = ' ';
+				unused_len--;
+			}
+		}
+		up_read(&mm->mmap_sem);
+	}
+	unused_len -= len;
+
+	/* fill the rest with command line argument
+	 * replace each null or new line character
+	 * between args in argv with whitespace */
+	len = get_cmdline(task, buf, unused_len);
+	while (offset < len) {
+		if (buf[offset] != '\0' && buf[offset] != '\n')
+			task_entry->comm[i++] = buf[offset];
+		else
+			task_entry->comm[i++] = ' ';
+		offset++;
+	}
+
+	/* get rid of trailing whitespaces in case when arg is memset to
+	 * zero before being reset in userspace
+	 */
+	while (task_entry->comm[i-1] == ' ')
+		i--;
+	task_entry->comm[i] = '\0';
+}
+
+static struct task_entry *find_task_entry(struct uid_entry *uid_entry,
+		struct task_struct *task)
+{
+	struct task_entry *task_entry;
+
+	hash_for_each_possible(uid_entry->task_entries, task_entry, hash,
+			task->pid) {
+		if (task->pid == task_entry->pid) {
+			/* if thread name changed, update the entire command */
+			int len = strnchr(task_entry->comm, ' ', TASK_COMM_LEN)
+				- task_entry->comm;
+
+			if (strncmp(task_entry->comm, task->comm, len))
+				get_full_task_comm(task_entry, task);
+			return task_entry;
+		}
+	}
+	return NULL;
+}
+
+static struct task_entry *find_or_register_task(struct uid_entry *uid_entry,
+		struct task_struct *task)
+{
+	struct task_entry *task_entry;
+	pid_t pid = task->pid;
+
+	task_entry = find_task_entry(uid_entry, task);
+	if (task_entry)
+		return task_entry;
+
+	task_entry = kzalloc(sizeof(struct task_entry), GFP_ATOMIC);
+	if (!task_entry)
+		return NULL;
+
+	get_full_task_comm(task_entry, task);
+
+	task_entry->pid = pid;
+	hash_add(uid_entry->task_entries, &task_entry->hash, (unsigned int)pid);
+
+	return task_entry;
+}
+
+static void remove_uid_tasks(struct uid_entry *uid_entry)
+{
+	struct task_entry *task_entry;
+	unsigned long bkt_task;
+	struct hlist_node *tmp_task;
+
+	hash_for_each_safe(uid_entry->task_entries, bkt_task,
+			tmp_task, task_entry, hash) {
+		hash_del(&task_entry->hash);
+		kfree(task_entry);
+	}
+}
+
+static void set_io_uid_tasks_zero(struct uid_entry *uid_entry)
+{
+	struct task_entry *task_entry;
+	unsigned long bkt_task;
+
+	hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+		memset(&task_entry->io[UID_STATE_TOTAL_CURR], 0,
+			sizeof(struct io_stats));
+	}
+}
+
+static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
+		struct task_struct *task, int slot)
+{
+	struct task_entry *task_entry = find_or_register_task(uid_entry, task);
+	struct io_stats *task_io_slot = &task_entry->io[slot];
+
+	task_io_slot->read_bytes += task->ioac.read_bytes;
+	task_io_slot->write_bytes += compute_write_bytes(task);
+	task_io_slot->rchar += task->ioac.rchar;
+	task_io_slot->wchar += task->ioac.wchar;
+	task_io_slot->fsync += task->ioac.syscfs;
+}
+
+static void compute_io_uid_tasks(struct uid_entry *uid_entry)
+{
+	struct task_entry *task_entry;
+	unsigned long bkt_task;
+
+	hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+		compute_io_bucket_stats(&task_entry->io[uid_entry->state],
+					&task_entry->io[UID_STATE_TOTAL_CURR],
+					&task_entry->io[UID_STATE_TOTAL_LAST],
+					&task_entry->io[UID_STATE_DEAD_TASKS]);
+	}
+}
+
+static void show_io_uid_tasks(struct seq_file *m, struct uid_entry *uid_entry)
+{
+	struct task_entry *task_entry;
+	unsigned long bkt_task;
+
+	hash_for_each(uid_entry->task_entries, bkt_task, task_entry, hash) {
+		/* Separated by comma because space exists in task comm */
+		seq_printf(m, "task,%s,%lu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu,%llu\n",
+				task_entry->comm,
+				(unsigned long)task_entry->pid,
+				task_entry->io[UID_STATE_FOREGROUND].rchar,
+				task_entry->io[UID_STATE_FOREGROUND].wchar,
+				task_entry->io[UID_STATE_FOREGROUND].read_bytes,
+				task_entry->io[UID_STATE_FOREGROUND].write_bytes,
+				task_entry->io[UID_STATE_BACKGROUND].rchar,
+				task_entry->io[UID_STATE_BACKGROUND].wchar,
+				task_entry->io[UID_STATE_BACKGROUND].read_bytes,
+				task_entry->io[UID_STATE_BACKGROUND].write_bytes,
+				task_entry->io[UID_STATE_FOREGROUND].fsync,
+				task_entry->io[UID_STATE_BACKGROUND].fsync);
+	}
+}
+#else
+static void remove_uid_tasks(struct uid_entry *uid_entry) {};
+static void set_io_uid_tasks_zero(struct uid_entry *uid_entry) {};
+static void add_uid_tasks_io_stats(struct uid_entry *uid_entry,
+		struct task_struct *task, int slot) {};
+static void compute_io_uid_tasks(struct uid_entry *uid_entry) {};
+static void show_io_uid_tasks(struct seq_file *m,
+		struct uid_entry *uid_entry) {}
+#endif
+
 static struct uid_entry *find_uid_entry(uid_t uid)
 {
 	struct uid_entry *uid_entry;
@@ -87,7 +320,9 @@
 		return NULL;
 
 	uid_entry->uid = uid;
-
+#ifdef CONFIG_UID_SYS_STATS_DEBUG
+	hash_init(uid_entry->task_entries);
+#endif
 	hash_add(hash_table, &uid_entry->hash, uid);
 
 	return uid_entry;
@@ -193,6 +428,7 @@
 		hash_for_each_possible_safe(hash_table, uid_entry, tmp,
 							hash, (uid_t)uid_start) {
 			if (uid_start == uid_entry->uid) {
+				remove_uid_tasks(uid_entry);
 				hash_del(&uid_entry->hash);
 				kfree(uid_entry);
 			}
@@ -209,13 +445,6 @@
 	.write		= uid_remove_write,
 };
 
-static u64 compute_write_bytes(struct task_struct *task)
-{
-	if (task->ioac.write_bytes <= task->ioac.cancelled_write_bytes)
-		return 0;
-
-	return task->ioac.write_bytes - task->ioac.cancelled_write_bytes;
-}
 
 static void add_uid_io_stats(struct uid_entry *uid_entry,
 			struct task_struct *task, int slot)
@@ -227,28 +456,8 @@
 	io_slot->rchar += task->ioac.rchar;
 	io_slot->wchar += task->ioac.wchar;
 	io_slot->fsync += task->ioac.syscfs;
-}
 
-static void compute_uid_io_bucket_stats(struct io_stats *io_bucket,
-					struct io_stats *io_curr,
-					struct io_stats *io_last,
-					struct io_stats *io_dead)
-{
-	io_bucket->read_bytes += io_curr->read_bytes + io_dead->read_bytes -
-		io_last->read_bytes;
-	io_bucket->write_bytes += io_curr->write_bytes + io_dead->write_bytes -
-		io_last->write_bytes;
-	io_bucket->rchar += io_curr->rchar + io_dead->rchar - io_last->rchar;
-	io_bucket->wchar += io_curr->wchar + io_dead->wchar - io_last->wchar;
-	io_bucket->fsync += io_curr->fsync + io_dead->fsync - io_last->fsync;
-
-	io_last->read_bytes = io_curr->read_bytes;
-	io_last->write_bytes = io_curr->write_bytes;
-	io_last->rchar = io_curr->rchar;
-	io_last->wchar = io_curr->wchar;
-	io_last->fsync = io_curr->fsync;
-
-	memset(io_dead, 0, sizeof(struct io_stats));
+	add_uid_tasks_io_stats(uid_entry, task, slot);
 }
 
 static void update_io_stats_all_locked(void)
@@ -259,9 +468,11 @@
 	unsigned long bkt;
 	uid_t uid;
 
-	hash_for_each(hash_table, bkt, uid_entry, hash)
+	hash_for_each(hash_table, bkt, uid_entry, hash) {
 		memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
 			sizeof(struct io_stats));
+		set_io_uid_tasks_zero(uid_entry);
+	}
 
 	rcu_read_lock();
 	do_each_thread(temp, task) {
@@ -275,10 +486,11 @@
 	rcu_read_unlock();
 
 	hash_for_each(hash_table, bkt, uid_entry, hash) {
-		compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+		compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
 					&uid_entry->io[UID_STATE_TOTAL_CURR],
 					&uid_entry->io[UID_STATE_TOTAL_LAST],
 					&uid_entry->io[UID_STATE_DEAD_TASKS]);
+		compute_io_uid_tasks(uid_entry);
 	}
 }
 
@@ -289,6 +501,7 @@
 
 	memset(&uid_entry->io[UID_STATE_TOTAL_CURR], 0,
 		sizeof(struct io_stats));
+	set_io_uid_tasks_zero(uid_entry);
 
 	rcu_read_lock();
 	do_each_thread(temp, task) {
@@ -298,12 +511,14 @@
 	} while_each_thread(temp, task);
 	rcu_read_unlock();
 
-	compute_uid_io_bucket_stats(&uid_entry->io[uid_entry->state],
+	compute_io_bucket_stats(&uid_entry->io[uid_entry->state],
 				&uid_entry->io[UID_STATE_TOTAL_CURR],
 				&uid_entry->io[UID_STATE_TOTAL_LAST],
 				&uid_entry->io[UID_STATE_DEAD_TASKS]);
+	compute_io_uid_tasks(uid_entry);
 }
 
+
 static int uid_io_show(struct seq_file *m, void *v)
 {
 	struct uid_entry *uid_entry;
@@ -315,21 +530,22 @@
 
 	hash_for_each(hash_table, bkt, uid_entry, hash) {
 		seq_printf(m, "%d %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
-			uid_entry->uid,
-			uid_entry->io[UID_STATE_FOREGROUND].rchar,
-			uid_entry->io[UID_STATE_FOREGROUND].wchar,
-			uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
-			uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
-			uid_entry->io[UID_STATE_BACKGROUND].rchar,
-			uid_entry->io[UID_STATE_BACKGROUND].wchar,
-			uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
-			uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
-			uid_entry->io[UID_STATE_FOREGROUND].fsync,
-			uid_entry->io[UID_STATE_BACKGROUND].fsync);
+				uid_entry->uid,
+				uid_entry->io[UID_STATE_FOREGROUND].rchar,
+				uid_entry->io[UID_STATE_FOREGROUND].wchar,
+				uid_entry->io[UID_STATE_FOREGROUND].read_bytes,
+				uid_entry->io[UID_STATE_FOREGROUND].write_bytes,
+				uid_entry->io[UID_STATE_BACKGROUND].rchar,
+				uid_entry->io[UID_STATE_BACKGROUND].wchar,
+				uid_entry->io[UID_STATE_BACKGROUND].read_bytes,
+				uid_entry->io[UID_STATE_BACKGROUND].write_bytes,
+				uid_entry->io[UID_STATE_FOREGROUND].fsync,
+				uid_entry->io[UID_STATE_BACKGROUND].fsync);
+
+		show_io_uid_tasks(m, uid_entry);
 	}
 
 	rt_mutex_unlock(&uid_lock);
-
 	return 0;
 }
 
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 120fd54..538a8d9 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -1221,16 +1221,16 @@
 	if (md->area_type & MMC_BLK_DATA_AREA_RPMB)
 		mmc_blk_part_switch(card, dev_get_drvdata(&card->dev));
 
-	mmc_put_card(card);
-
-	err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
-
 	if (mmc_card_cmdq(card)) {
 		if (mmc_cmdq_halt(card->host, false))
 			pr_err("%s: %s: cmdq unhalt failed\n",
 			       mmc_hostname(card->host), __func__);
 	}
 
+	mmc_put_card(card);
+
+	err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
+
 cmd_done:
 	mmc_blk_put(md);
 cmd_err:
@@ -3461,6 +3461,8 @@
 	else
 		mrq->data->error = -ETIMEDOUT;
 
+	host->err_stats[MMC_ERR_CMDQ_REQ_TIMEOUT]++;
+
 	if (mrq->cmd && mrq->cmd->error) {
 		if (!(mrq->req->cmd_flags & REQ_PREFLUSH)) {
 			/*
@@ -3518,15 +3520,23 @@
 	/* RED error - Fatal: requires reset */
 	if (mrq->cmdq_req->resp_err) {
 		err = mrq->cmdq_req->resp_err;
+		goto reset;
+	}
+
+	/*
+	 * TIMEOUT errrors can happen because of execution error
+	 * in the last command. So send cmd 13 to get device status
+	 */
+	if ((mrq->cmd && (mrq->cmd->error == -ETIMEDOUT)) ||
+			(mrq->data && (mrq->data->error == -ETIMEDOUT))) {
 		if (mmc_host_halt(host) || mmc_host_cq_disable(host)) {
 			ret = get_card_status(host->card, &status, 0);
 			if (ret)
 				pr_err("%s: CMD13 failed with err %d\n",
 						mmc_hostname(host), ret);
 		}
-		pr_err("%s: Response error detected with device status 0x%08x\n",
+		pr_err("%s: Timeout error detected with device status 0x%08x\n",
 			mmc_hostname(host), status);
-		goto reset;
 	}
 
 	/*
@@ -3572,7 +3582,7 @@
 	else if (mrq->data && mrq->data->error)
 		err = mrq->data->error;
 
-	if (err || cmdq_req->resp_err) {
+	if ((err || cmdq_req->resp_err) && !cmdq_req->skip_err_handling) {
 		pr_err("%s: %s: txfr error(%d)/resp_err(%d)\n",
 				mmc_hostname(mrq->host), __func__, err,
 				cmdq_req->resp_err);
@@ -3609,6 +3619,17 @@
 		blk_end_request_all(rq, err);
 		goto out;
 	}
+	/*
+	 * In case of error, cmdq_req->data.bytes_xfered is set to 0.
+	 * If we call blk_end_request() with nr_bytes as 0 then the request
+	 * never gets completed. So in case of error, to complete a request
+	 * with error we should use blk_end_request_all().
+	 */
+	if (err && cmdq_req->skip_err_handling) {
+		cmdq_req->skip_err_handling = false;
+		blk_end_request_all(rq, err);
+		goto out;
+	}
 
 	blk_end_request(rq, err, cmdq_req->data.bytes_xfered);
 
@@ -3889,6 +3910,7 @@
 	struct mmc_host *host = card->host;
 	struct mmc_cmdq_context_info *ctx = &host->cmdq_ctx;
 	u8 part_config = card->ext_csd.part_config;
+	int ret = 0, err = 0;
 
 	if ((main_md->part_curr == md->part_type) &&
 	    (card->part_curr == md->part_type))
@@ -3898,40 +3920,70 @@
 		 card->ext_csd.cmdq_support &&
 		 (md->flags & MMC_BLK_CMD_QUEUE)));
 
-	if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state))
-		WARN_ON(mmc_cmdq_halt(host, true));
+	if (!test_bit(CMDQ_STATE_HALT, &ctx->curr_state)) {
+		ret = mmc_cmdq_halt(host, true);
+		if (ret) {
+			pr_err("%s: %s: halt: failed: %d\n",
+				mmc_hostname(host), __func__,  ret);
+			goto out;
+		}
+	}
 
 	/* disable CQ mode in card */
 	if (mmc_card_cmdq(card)) {
-		WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+		ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 				 EXT_CSD_CMDQ, 0,
-				  card->ext_csd.generic_cmd6_time));
+				 card->ext_csd.generic_cmd6_time);
+		if (ret) {
+			pr_err("%s: %s: cmdq mode disable failed %d\n",
+				mmc_hostname(host), __func__, ret);
+			goto cmdq_unhalt;
+		}
 		mmc_card_clr_cmdq(card);
 	}
 
 	part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
 	part_config |= md->part_type;
 
-	WARN_ON(mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+	ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
 			 EXT_CSD_PART_CONFIG, part_config,
-			  card->ext_csd.part_time));
+			 card->ext_csd.part_time);
+	if (ret) {
+		pr_err("%s: %s: mmc_switch failure, %d -> %d , err = %d\n",
+			mmc_hostname(host), __func__, main_md->part_curr,
+			md->part_type, ret);
+		goto cmdq_switch;
+	}
 
 	card->ext_csd.part_config = part_config;
 	card->part_curr = md->part_type;
 
 	main_md->part_curr = md->part_type;
 
-	WARN_ON(mmc_blk_cmdq_switch(card, md, true));
-	WARN_ON(mmc_cmdq_halt(host, false));
-
-	return 0;
+cmdq_switch:
+	err = mmc_blk_cmdq_switch(card, md, true);
+	if (err) {
+		pr_err("%s: %s: mmc_blk_cmdq_switch failed: %d\n",
+			mmc_hostname(host), __func__,  err);
+		ret = err;
+	}
+cmdq_unhalt:
+	err = mmc_cmdq_halt(host, false);
+	if (err) {
+		pr_err("%s: %s: unhalt: failed: %d\n",
+			mmc_hostname(host), __func__,  err);
+		ret = err;
+	}
+out:
+	return ret;
 }
 
 static int mmc_blk_cmdq_issue_rq(struct mmc_queue *mq, struct request *req)
 {
-	int ret;
+	int ret, err = 0;
 	struct mmc_blk_data *md = mq->data;
 	struct mmc_card *card = md->queue.card;
+	struct mmc_host *host = card->host;
 
 	mmc_get_card(card);
 
@@ -3952,9 +4004,20 @@
 
 	ret = mmc_blk_cmdq_part_switch(card, md);
 	if (ret) {
-		pr_err("%s: %s: partition switch failed %d\n",
+		pr_err("%s: %s: partition switch failed %d, resetting cmdq\n",
 				md->disk->disk_name, __func__, ret);
-		goto out;
+
+		mmc_blk_cmdq_reset(host, false);
+		err = mmc_blk_cmdq_part_switch(card, md);
+		if (!err) {
+			pr_err("%s: %s: partition switch success err = %d\n",
+				md->disk->disk_name, __func__, err);
+		} else {
+			pr_err("%s: %s: partition switch failed err = %d\n",
+				md->disk->disk_name, __func__, err);
+			ret = err;
+			goto out;
+		}
 	}
 
 	if (req) {
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index a531cb4..fb2ca3d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -403,6 +403,7 @@
 		return ret;
 
 	mmc_card_set_present(card);
+	device_enable_async_suspend(&card->dev);
 
 	return 0;
 }
@@ -430,7 +431,6 @@
 	}
 
 	kfree(card->wr_pack_stats.packing_events);
-	kfree(card->cached_ext_csd);
 
 	put_device(&card->dev);
 }
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 978dd9a..30180af 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -456,10 +456,11 @@
 }
 EXPORT_SYMBOL(mmc_clk_update_freq);
 
-void mmc_recovery_fallback_lower_speed(struct mmc_host *host)
+int mmc_recovery_fallback_lower_speed(struct mmc_host *host)
 {
+	int err = 0;
 	if (!host->card)
-		return;
+		return -EINVAL;
 
 	if (host->sdr104_wa && mmc_card_sd(host->card) &&
 	    (host->ios.timing == MMC_TIMING_UHS_SDR104) &&
@@ -467,9 +468,14 @@
 		pr_err("%s: %s: blocked SDR104, lower the bus-speed (SDR50 / DDR50)\n",
 			mmc_hostname(host), __func__);
 		mmc_host_clear_sdr104(host);
-		mmc_hw_reset(host);
+		err = mmc_hw_reset(host);
 		host->card->sdr104_blocked = true;
 	}
+	if (err)
+		pr_err("%s: %s: Fallback to lower speed mode failed with err=%d\n",
+			mmc_hostname(host), __func__, err);
+
+	return err;
 }
 
 static int mmc_devfreq_set_target(struct device *dev,
@@ -537,7 +543,7 @@
 	if (err && err != -EAGAIN) {
 		pr_err("%s: clock scale to %lu failed with error %d\n",
 			mmc_hostname(host), *freq, err);
-		mmc_recovery_fallback_lower_speed(host);
+		err = mmc_recovery_fallback_lower_speed(host);
 	} else {
 		pr_debug("%s: clock change to %lu finished successfully (%s)\n",
 			mmc_hostname(host), *freq, current->comm);
@@ -1203,6 +1209,46 @@
 	return 0;
 }
 
+static int mmc_cmdq_check_retune(struct mmc_host *host)
+{
+	bool cmdq_mode;
+	int err = 0;
+
+	if (!host->need_retune || host->doing_retune || !host->card ||
+			mmc_card_hs400es(host->card) ||
+			(host->ios.clock <= MMC_HIGH_DDR_MAX_DTR))
+		return 0;
+
+	cmdq_mode = mmc_card_cmdq(host->card);
+	if (cmdq_mode) {
+		err = mmc_cmdq_halt(host, true);
+		if (err) {
+			pr_err("%s: %s: failed halting queue (%d)\n",
+				mmc_hostname(host), __func__, err);
+			host->cmdq_ops->dumpstate(host);
+			goto halt_failed;
+		}
+	}
+
+	mmc_retune_hold(host);
+	err = mmc_retune(host);
+	mmc_retune_release(host);
+
+	if (cmdq_mode) {
+		if (mmc_cmdq_halt(host, false)) {
+			pr_err("%s: %s: cmdq unhalt failed\n",
+			mmc_hostname(host), __func__);
+			host->cmdq_ops->dumpstate(host);
+		}
+	}
+
+halt_failed:
+	pr_debug("%s: %s: Retuning done err: %d\n",
+				mmc_hostname(host), __func__, err);
+
+	return err;
+}
+
 static void mmc_start_cmdq_request(struct mmc_host *host,
 				   struct mmc_request *mrq)
 {
@@ -1227,6 +1273,7 @@
 	}
 
 	mmc_host_clk_hold(host);
+	mmc_cmdq_check_retune(host);
 	if (likely(host->cmdq_ops->request))
 		host->cmdq_ops->request(host, mrq);
 	else
@@ -3407,6 +3454,13 @@
 		pm_wakeup_event(mmc_dev(host), 5000);
 
 	host->detect_change = 1;
+	/*
+	 * Change in cd_gpio state, so make sure detection part is
+	 * not overided because of manual resume.
+	 */
+	if (cd_irq && mmc_bus_manual_resume(host))
+		host->ignore_bus_resume_flags = true;
+
 	mmc_schedule_delayed_work(&host->detect, delay);
 }
 
@@ -4127,6 +4181,7 @@
 
 	if (!host->max_busy_timeout ||
 			(host->caps2 & MMC_CAP2_MAX_DISCARD_SIZE))
+		return UINT_MAX;
 
 	/*
 	 * Without erase_group_def set, MMC erase timeout depends on clock
@@ -4298,8 +4353,7 @@
 
 	if (ret) {
 		if (host->ops->get_cd && host->ops->get_cd(host)) {
-			mmc_recovery_fallback_lower_speed(host);
-			ret = 0;
+			ret = mmc_recovery_fallback_lower_speed(host);
 		} else {
 			mmc_card_set_removed(host->card);
 			if (host->card->sdr104_blocked) {
@@ -4352,6 +4406,18 @@
 }
 EXPORT_SYMBOL(mmc_detect_card_removed);
 
+/*
+ * This should be called to make sure that detect work(mmc_rescan)
+ * is completed.Drivers may use this function from async schedule/probe
+ * contexts to make sure that the bootdevice detection is completed on
+ * completion of async_schedule.
+ */
+void mmc_flush_detect_work(struct mmc_host *host)
+{
+	flush_delayed_work(&host->detect);
+}
+EXPORT_SYMBOL(mmc_flush_detect_work);
+
 void mmc_rescan(struct work_struct *work)
 {
 	unsigned long flags;
@@ -4387,6 +4453,8 @@
 		host->bus_ops->detect(host);
 
 	host->detect_change = 0;
+	if (host->ignore_bus_resume_flags)
+		host->ignore_bus_resume_flags = false;
 
 	/*
 	 * Let mmc_bus_put() free the bus/bus_ops if we've found that
@@ -4415,6 +4483,7 @@
 		goto out;
 	}
 	mmc_rescan_try_freq(host, host->f_min);
+	host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
 	mmc_release_host(host);
 
  out:
@@ -4643,7 +4712,8 @@
 
 		spin_lock_irqsave(&host->lock, flags);
 		host->rescan_disable = 0;
-		if (mmc_bus_manual_resume(host)) {
+		if (mmc_bus_manual_resume(host) &&
+				!host->ignore_bus_resume_flags) {
 			spin_unlock_irqrestore(&host->lock, flags);
 			break;
 		}
diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c
index d1a0235..15c3e9e 100644
--- a/drivers/mmc/core/debugfs.c
+++ b/drivers/mmc/core/debugfs.c
@@ -381,6 +381,79 @@
 DEFINE_SIMPLE_ATTRIBUTE(mmc_err_state, mmc_err_state_get,
 		mmc_err_state_clear, "%llu\n");
 
+static int mmc_err_stats_show(struct seq_file *file, void *data)
+{
+	struct mmc_host *host = (struct mmc_host *)file->private;
+
+	if (!host)
+		return -EINVAL;
+
+	seq_printf(file, "# Command Timeout Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_CMD_TIMEOUT]);
+
+	seq_printf(file, "# Command CRC Errors Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_CMD_CRC]);
+
+	seq_printf(file, "# Data Timeout Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_DAT_TIMEOUT]);
+
+	seq_printf(file, "# Data CRC Errors Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_DAT_CRC]);
+
+	seq_printf(file, "# Auto-Cmd Error Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_ADMA]);
+
+	seq_printf(file, "# ADMA Error Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_ADMA]);
+
+	seq_printf(file, "# Tuning Error Occurred:\t %d\n",
+		   host->err_stats[MMC_ERR_TUNING]);
+
+	seq_printf(file, "# CMDQ RED Errors:\t\t %d\n",
+		   host->err_stats[MMC_ERR_CMDQ_RED]);
+
+	seq_printf(file, "# CMDQ GCE Errors:\t\t %d\n",
+		   host->err_stats[MMC_ERR_CMDQ_GCE]);
+
+	seq_printf(file, "# CMDQ ICCE Errors:\t\t %d\n",
+		   host->err_stats[MMC_ERR_CMDQ_ICCE]);
+
+	seq_printf(file, "# Request Timedout:\t %d\n",
+		   host->err_stats[MMC_ERR_REQ_TIMEOUT]);
+
+	seq_printf(file, "# CMDQ Request Timedout:\t %d\n",
+		   host->err_stats[MMC_ERR_CMDQ_REQ_TIMEOUT]);
+
+	seq_printf(file, "# ICE Config Errors:\t\t %d\n",
+		   host->err_stats[MMC_ERR_ICE_CFG]);
+	return 0;
+}
+
+static int mmc_err_stats_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, mmc_err_stats_show, inode->i_private);
+}
+
+static ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf,
+				   size_t cnt, loff_t *ppos)
+{
+	struct mmc_host *host = filp->f_mapping->host->i_private;
+
+	if (!host)
+		return -EINVAL;
+
+	pr_debug("%s: Resetting MMC error statistics", __func__);
+	memset(host->err_stats, 0, sizeof(host->err_stats));
+
+	return cnt;
+}
+
+static const struct file_operations mmc_err_stats_fops = {
+	.open	= mmc_err_stats_open,
+	.read	= seq_read,
+	.write	= mmc_err_stats_write,
+};
+
 void mmc_add_host_debugfs(struct mmc_host *host)
 {
 	struct dentry *root;
@@ -430,6 +503,10 @@
 		&mmc_err_state))
 		goto err_node;
 
+	if (!debugfs_create_file("err_stats", 0600, root, host,
+		&mmc_err_stats_fops))
+		goto err_node;
+
 #ifdef CONFIG_MMC_CLKGATE
 	if (!debugfs_create_u32("clk_delay", (S_IRUSR | S_IWUSR),
 				root, &host->clk_delay))
@@ -592,9 +669,10 @@
 {
 	struct mmc_card *card = filp->private_data;
 	struct mmc_wr_pack_stats *pack_stats;
-	int i;
+	int i, ret = 0;
 	int max_num_of_packed_reqs = 0;
-	char *temp_buf;
+	char *temp_buf, *temp_ubuf;
+	size_t tubuf_cnt = 0;
 
 	if (!card)
 		return cnt;
@@ -620,15 +698,24 @@
 
 	max_num_of_packed_reqs = card->ext_csd.max_packed_writes;
 
-	temp_buf = kmalloc(TEMP_BUF_SIZE, GFP_KERNEL);
+	if (cnt <= (strlen_user(ubuf) + 1))
+		goto exit;
+
+	temp_buf = kzalloc(TEMP_BUF_SIZE, GFP_KERNEL);
 	if (!temp_buf)
 		goto exit;
 
+	tubuf_cnt = cnt - strlen_user(ubuf) - 1;
+
+	temp_ubuf = kzalloc(tubuf_cnt, GFP_KERNEL);
+	if (!temp_ubuf)
+		goto cleanup;
+
 	spin_lock(&pack_stats->lock);
 
 	snprintf(temp_buf, TEMP_BUF_SIZE, "%s: write packing statistics:\n",
 		mmc_hostname(card->host));
-	strlcat(ubuf, temp_buf, cnt);
+	strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 
 	for (i = 1 ; i <= max_num_of_packed_reqs ; ++i) {
 		if (pack_stats->packing_events[i]) {
@@ -636,63 +723,63 @@
 				 "%s: Packed %d reqs - %d times\n",
 				mmc_hostname(card->host), i,
 				pack_stats->packing_events[i]);
-			strlcat(ubuf, temp_buf, cnt);
+			strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 		}
 	}
 
 	snprintf(temp_buf, TEMP_BUF_SIZE,
 		 "%s: stopped packing due to the following reasons:\n",
 		 mmc_hostname(card->host));
-	strlcat(ubuf, temp_buf, cnt);
+	strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 
 	if (pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: exceed max num of segments\n",
 			 mmc_hostname(card->host),
 			 pack_stats->pack_stop_reason[EXCEEDS_SEGMENTS]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[EXCEEDS_SECTORS]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: exceed max num of sectors\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[EXCEEDS_SECTORS]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[WRONG_DATA_DIR]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: wrong data direction\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[WRONG_DATA_DIR]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: flush or discard\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[FLUSH_OR_DISCARD]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[EMPTY_QUEUE]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: empty queue\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[EMPTY_QUEUE]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[REL_WRITE]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: rel write\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[REL_WRITE]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[THRESHOLD]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: Threshold\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[THRESHOLD]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 
 	if (pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]) {
@@ -700,25 +787,36 @@
 			 "%s: %d times: Large sector alignment\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[LARGE_SEC_ALIGN]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[RANDOM]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: random request\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[RANDOM]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
 	if (pack_stats->pack_stop_reason[FUA]) {
 		snprintf(temp_buf, TEMP_BUF_SIZE,
 			 "%s: %d times: fua request\n",
 			mmc_hostname(card->host),
 			pack_stats->pack_stop_reason[FUA]);
-		strlcat(ubuf, temp_buf, cnt);
+		strlcat(temp_ubuf, temp_buf, tubuf_cnt);
 	}
+	if (strlen_user(ubuf) < cnt - strlen(temp_ubuf))
+		ret = copy_to_user((ubuf + strlen_user(ubuf)),
+				temp_ubuf, tubuf_cnt);
+	else
+		ret = -EFAULT;
+	if (ret)
+		pr_err("%s: %s: Copy to userspace failed: %s\n",
+				mmc_hostname(card->host), __func__, ubuf);
 
 	spin_unlock(&pack_stats->lock);
 
+	kfree(temp_ubuf);
+
+cleanup:
 	kfree(temp_buf);
 
 	pr_info("%s", ubuf);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 127ab0f..8a503b2 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -434,7 +434,8 @@
 	else
 		return 0;
 
-	if (!host->need_retune || host->doing_retune || !host->card)
+	if (!host->need_retune || host->doing_retune || !host->card ||
+			mmc_card_hs400es(host->card))
 		return 0;
 
 	host->need_retune = 0;
@@ -482,19 +483,17 @@
  */
 int mmc_of_parse(struct mmc_host *host)
 {
-	struct device_node *np;
+	struct device *dev = host->parent;
 	u32 bus_width;
 	int ret;
 	bool cd_cap_invert, cd_gpio_invert = false;
 	bool ro_cap_invert, ro_gpio_invert = false;
 
-	if (!host->parent || !host->parent->of_node)
+	if (!dev || !dev_fwnode(dev))
 		return 0;
 
-	np = host->parent->of_node;
-
 	/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
-	if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
+	if (device_property_read_u32(dev, "bus-width", &bus_width) < 0) {
 		dev_dbg(host->parent,
 			"\"bus-width\" property is missing, assuming 1 bit.\n");
 		bus_width = 1;
@@ -516,7 +515,7 @@
 	}
 
 	/* f_max is obtained from the optional "max-frequency" property */
-	of_property_read_u32(np, "max-frequency", &host->f_max);
+	device_property_read_u32(dev, "max-frequency", &host->f_max);
 
 	/*
 	 * Configure CD and WP pins. They are both by default active low to
@@ -531,12 +530,12 @@
 	 */
 
 	/* Parse Card Detection */
-	if (of_property_read_bool(np, "non-removable")) {
+	if (device_property_read_bool(dev, "non-removable")) {
 		host->caps |= MMC_CAP_NONREMOVABLE;
 	} else {
-		cd_cap_invert = of_property_read_bool(np, "cd-inverted");
+		cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
 
-		if (of_property_read_bool(np, "broken-cd"))
+		if (device_property_read_bool(dev, "broken-cd"))
 			host->caps |= MMC_CAP_NEEDS_POLL;
 
 		ret = mmc_gpiod_request_cd(host, "cd", 0, true,
@@ -562,7 +561,7 @@
 	}
 
 	/* Parse Write Protection */
-	ro_cap_invert = of_property_read_bool(np, "wp-inverted");
+	ro_cap_invert = device_property_read_bool(dev, "wp-inverted");
 
 	ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
 	if (!ret)
@@ -570,62 +569,62 @@
 	else if (ret != -ENOENT && ret != -ENOSYS)
 		return ret;
 
-	if (of_property_read_bool(np, "disable-wp"))
+	if (device_property_read_bool(dev, "disable-wp"))
 		host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
 
 	/* See the comment on CD inversion above */
 	if (ro_cap_invert ^ ro_gpio_invert)
 		host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
 
-	if (of_property_read_bool(np, "cap-sd-highspeed"))
+	if (device_property_read_bool(dev, "cap-sd-highspeed"))
 		host->caps |= MMC_CAP_SD_HIGHSPEED;
-	if (of_property_read_bool(np, "cap-mmc-highspeed"))
+	if (device_property_read_bool(dev, "cap-mmc-highspeed"))
 		host->caps |= MMC_CAP_MMC_HIGHSPEED;
-	if (of_property_read_bool(np, "sd-uhs-sdr12"))
+	if (device_property_read_bool(dev, "sd-uhs-sdr12"))
 		host->caps |= MMC_CAP_UHS_SDR12;
-	if (of_property_read_bool(np, "sd-uhs-sdr25"))
+	if (device_property_read_bool(dev, "sd-uhs-sdr25"))
 		host->caps |= MMC_CAP_UHS_SDR25;
-	if (of_property_read_bool(np, "sd-uhs-sdr50"))
+	if (device_property_read_bool(dev, "sd-uhs-sdr50"))
 		host->caps |= MMC_CAP_UHS_SDR50;
-	if (of_property_read_bool(np, "sd-uhs-sdr104"))
+	if (device_property_read_bool(dev, "sd-uhs-sdr104"))
 		host->caps |= MMC_CAP_UHS_SDR104;
-	if (of_property_read_bool(np, "sd-uhs-ddr50"))
+	if (device_property_read_bool(dev, "sd-uhs-ddr50"))
 		host->caps |= MMC_CAP_UHS_DDR50;
-	if (of_property_read_bool(np, "cap-power-off-card"))
+	if (device_property_read_bool(dev, "cap-power-off-card"))
 		host->caps |= MMC_CAP_POWER_OFF_CARD;
-	if (of_property_read_bool(np, "cap-mmc-hw-reset"))
+	if (device_property_read_bool(dev, "cap-mmc-hw-reset"))
 		host->caps |= MMC_CAP_HW_RESET;
-	if (of_property_read_bool(np, "cap-sdio-irq"))
+	if (device_property_read_bool(dev, "cap-sdio-irq"))
 		host->caps |= MMC_CAP_SDIO_IRQ;
-	if (of_property_read_bool(np, "full-pwr-cycle"))
+	if (device_property_read_bool(dev, "full-pwr-cycle"))
 		host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
-	if (of_property_read_bool(np, "keep-power-in-suspend"))
+	if (device_property_read_bool(dev, "keep-power-in-suspend"))
 		host->pm_caps |= MMC_PM_KEEP_POWER;
-	if (of_property_read_bool(np, "wakeup-source") ||
-	    of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
+	if (device_property_read_bool(dev, "wakeup-source") ||
+	    device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */
 		host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
-	if (of_property_read_bool(np, "mmc-ddr-1_8v"))
+	if (device_property_read_bool(dev, "mmc-ddr-1_8v"))
 		host->caps |= MMC_CAP_1_8V_DDR;
-	if (of_property_read_bool(np, "mmc-ddr-1_2v"))
+	if (device_property_read_bool(dev, "mmc-ddr-1_2v"))
 		host->caps |= MMC_CAP_1_2V_DDR;
-	if (of_property_read_bool(np, "mmc-hs200-1_8v"))
+	if (device_property_read_bool(dev, "mmc-hs200-1_8v"))
 		host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
-	if (of_property_read_bool(np, "mmc-hs200-1_2v"))
+	if (device_property_read_bool(dev, "mmc-hs200-1_2v"))
 		host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
-	if (of_property_read_bool(np, "mmc-hs400-1_8v"))
+	if (device_property_read_bool(dev, "mmc-hs400-1_8v"))
 		host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
-	if (of_property_read_bool(np, "mmc-hs400-1_2v"))
+	if (device_property_read_bool(dev, "mmc-hs400-1_2v"))
 		host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
-	if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
+	if (device_property_read_bool(dev, "mmc-hs400-enhanced-strobe"))
 		host->caps2 |= MMC_CAP2_HS400_ES;
-	if (of_property_read_bool(np, "no-sdio"))
+	if (device_property_read_bool(dev, "no-sdio"))
 		host->caps2 |= MMC_CAP2_NO_SDIO;
-	if (of_property_read_bool(np, "no-sd"))
+	if (device_property_read_bool(dev, "no-sd"))
 		host->caps2 |= MMC_CAP2_NO_SD;
-	if (of_property_read_bool(np, "no-mmc"))
+	if (device_property_read_bool(dev, "no-mmc"))
 		host->caps2 |= MMC_CAP2_NO_MMC;
 
-	host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
+	host->dsr_req = !device_property_read_u32(dev, "dsr", &host->dsr);
 	if (host->dsr_req && (host->dsr & ~0xffff)) {
 		dev_err(host->parent,
 			"device tree specified broken value for DSR: 0x%x, ignoring\n",
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 409718b..dd58288 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -750,7 +750,6 @@
 		return err;
 	}
 
-	card->cached_ext_csd = ext_csd;
 	err = mmc_decode_ext_csd(card, ext_csd);
 	kfree(ext_csd);
 	return err;
@@ -1315,6 +1314,8 @@
 	if (card->ext_csd.strobe_support && host->ops->enhanced_strobe) {
 		mmc_host_clk_hold(host);
 		err = host->ops->enhanced_strobe(host);
+		if (!err)
+			host->ios.enhanced_strobe = true;
 		mmc_host_clk_release(host);
 	} else if ((host->caps2 & MMC_CAP2_HS400_POST_TUNING) &&
 			host->ops->execute_tuning) {
@@ -1357,10 +1358,6 @@
 	int err;
 	u8 val;
 
-	/* Reduce frequency to HS */
-	max_dtr = card->ext_csd.hs_max_dtr;
-	mmc_set_clock(host, max_dtr);
-
 	/* Switch HS400 to HS DDR */
 	val = EXT_CSD_TIMING_HS;
 	err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
@@ -1371,6 +1368,10 @@
 
 	mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
 
+	/* Reduce frequency to HS */
+	max_dtr = card->ext_csd.hs_max_dtr;
+	mmc_set_clock(host, max_dtr);
+
 	err = mmc_switch_status(card, false);
 	if (err)
 		goto out_err;
@@ -1413,10 +1414,27 @@
 	return err;
 }
 
+static void mmc_select_driver_type(struct mmc_card *card)
+{
+	int card_drv_type, drive_strength, drv_type;
+
+	card_drv_type = card->ext_csd.raw_driver_strength |
+			mmc_driver_type_mask(0);
+
+	drive_strength = mmc_select_drive_strength(card,
+						   card->ext_csd.hs200_max_dtr,
+						   card_drv_type, &drv_type);
+
+	card->drive_strength = drive_strength;
+
+	if (drv_type)
+		mmc_set_driver_type(card->host, drv_type);
+}
+
 static int mmc_select_hs400es(struct mmc_card *card)
 {
 	struct mmc_host *host = card->host;
-	int err = 0;
+	int err = -EINVAL;
 	u8 val;
 
 	if (!(host->caps & MMC_CAP_8_BIT_DATA)) {
@@ -1461,6 +1479,8 @@
 		goto out_err;
 	}
 
+	mmc_select_driver_type(card);
+
 	/* Switch card to HS400 */
 	val = EXT_CSD_TIMING_HS400 |
 	      card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
@@ -1494,23 +1514,6 @@
 	return err;
 }
 
-static void mmc_select_driver_type(struct mmc_card *card)
-{
-	int card_drv_type, drive_strength, drv_type;
-
-	card_drv_type = card->ext_csd.raw_driver_strength |
-			mmc_driver_type_mask(0);
-
-	drive_strength = mmc_select_drive_strength(card,
-						   card->ext_csd.hs200_max_dtr,
-						   card_drv_type, &drv_type);
-
-	card->drive_strength = drive_strength;
-
-	if (drv_type)
-		mmc_set_driver_type(card->host, drv_type);
-}
-
 /*
  * For device supporting HS200 mode, the following sequence
  * should be done before executing the tuning process.
@@ -1604,12 +1607,19 @@
 	/* For Enhance Strobe HS400 flow */
 	if (card->ext_csd.strobe_support &&
 	    card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
-	    card->host->caps & MMC_CAP_8_BIT_DATA)
-		err = mmc_select_hs400es(card);
-	else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
+	    card->host->caps & MMC_CAP_8_BIT_DATA) {
+		err = mmc_select_hs400(card);
+		if (err) {
+			pr_err("%s: %s: mmc_select_hs400 failed : %d\n",
+					mmc_hostname(card->host), __func__,
+					err);
+			err = mmc_select_hs400es(card);
+		}
+	} else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
 		err = mmc_select_hs200(card);
-	else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
+	} else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) {
 		err = mmc_select_hs(card);
+	}
 
 	if (err && err != -EBADMSG)
 		return err;
@@ -2148,7 +2158,7 @@
 		err = mmc_select_hs400(card);
 		if (err)
 			goto free_card;
-	} else {
+	} else if (!mmc_card_hs400es(card)) {
 		/* Select the desired bus width optionally */
 		err = mmc_select_bus_width(card);
 		if (err > 0 && mmc_card_hs(card)) {
@@ -2159,11 +2169,11 @@
 	}
 
 	card->clk_scaling_lowest = host->f_min;
-	if ((card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS400) ||
-			(card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS200))
+	if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400) ||
+			(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200))
 		card->clk_scaling_highest = card->ext_csd.hs200_max_dtr;
-	else if ((card->mmc_avail_type | EXT_CSD_CARD_TYPE_HS) ||
-			(card->mmc_avail_type | EXT_CSD_CARD_TYPE_DDR_52))
+	else if ((card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS) ||
+			(card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52))
 		card->clk_scaling_highest = card->ext_csd.hs_max_dtr;
 	else
 		card->clk_scaling_highest = card->csd.max_dtr;
@@ -2863,10 +2873,13 @@
 	int err = 0;
 
 	MMC_TRACE(host, "%s: Enter\n", __func__);
+	err = _mmc_resume(host);
+	pm_runtime_set_active(&host->card->dev);
+	pm_runtime_mark_last_busy(&host->card->dev);
 	pm_runtime_enable(&host->card->dev);
-
 	MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
-	return 0;
+
+	return err;
 }
 
 #define MAX_DEFER_SUSPEND_COUNTER 20
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 650f658..10d55b8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1275,7 +1275,10 @@
 	if (!err) {
 		pm_runtime_disable(&host->card->dev);
 		pm_runtime_set_suspended(&host->card->dev);
-	}
+	/* if suspend fails, force mmc_detect_change during resume */
+	} else if (mmc_bus_manual_resume(host))
+		host->ignore_bus_resume_flags = true;
+
 	MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
 
 	return err;
@@ -1345,10 +1348,13 @@
 	int err = 0;
 
 	MMC_TRACE(host, "%s: Enter\n", __func__);
+	err = _mmc_sd_resume(host);
+	pm_runtime_set_active(&host->card->dev);
+	pm_runtime_mark_last_busy(&host->card->dev);
 	pm_runtime_enable(&host->card->dev);
-
 	MMC_TRACE(host, "%s: Exit err: %d\n", __func__, err);
-	return 0;
+
+	return err;
 }
 
 /*
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index e32ed3d..6098489 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -277,7 +277,7 @@
 		sdio_free_func_cis(func);
 
 	kfree(func->info);
-
+	kfree(func->tmpbuf);
 	kfree(func);
 }
 
@@ -292,6 +292,16 @@
 	if (!func)
 		return ERR_PTR(-ENOMEM);
 
+	/*
+	 * allocate buffer separately to make sure it's properly aligned for
+	 * DMA usage (incl. 64 bit DMA)
+	 */
+	func->tmpbuf = kmalloc(4, GFP_KERNEL);
+	if (!func->tmpbuf) {
+		kfree(func);
+		return ERR_PTR(-ENOMEM);
+	}
+
 	func->card = card;
 
 	device_initialize(&func->dev);
diff --git a/drivers/mmc/host/cmdq_hci.c b/drivers/mmc/host/cmdq_hci.c
index 7c3638c..f16a999 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -145,6 +145,29 @@
 	mb();
 }
 
+static int cmdq_clear_task_poll(struct cmdq_host *cq_host, unsigned int tag)
+{
+	int retries = 100;
+
+	cmdq_clear_set_irqs(cq_host, CQIS_TCL, 0);
+	cmdq_writel(cq_host, 1<<tag, CQTCLR);
+	while (retries) {
+		/*
+		 * Task Clear register and doorbell,
+		 * both should indicate that task is cleared
+		 */
+		if ((cmdq_readl(cq_host, CQTCLR) & 1<<tag) ||
+			(cmdq_readl(cq_host, CQTDBR) & 1<<tag)) {
+			udelay(5);
+			retries--;
+			continue;
+		} else
+			break;
+	}
+
+	cmdq_clear_set_irqs(cq_host, 0, CQIS_TCL);
+	return retries ? 0 : -ETIMEDOUT;
+}
 
 #define DRV_NAME "cmdq-host"
 
@@ -197,6 +220,10 @@
 static void cmdq_dumpregs(struct cmdq_host *cq_host)
 {
 	struct mmc_host *mmc = cq_host->mmc;
+	int offset = 0;
+
+	if (cq_host->offset_changed)
+		offset = CQ_V5_VENDOR_CFG;
 
 	MMC_TRACE(mmc,
 	"%s: 0x0C=0x%08x 0x10=0x%08x 0x14=0x%08x 0x18=0x%08x 0x28=0x%08x 0x2C=0x%08x 0x30=0x%08x 0x34=0x%08x 0x54=0x%08x 0x58=0x%08x 0x5C=0x%08x 0x48=0x%08x\n",
@@ -243,7 +270,7 @@
 		cmdq_readl(cq_host, CQCRI),
 		cmdq_readl(cq_host, CQCRA));
 	pr_err(DRV_NAME": Vendor cfg 0x%08x\n",
-	       cmdq_readl(cq_host, CQ_VENDOR_CFG));
+	       cmdq_readl(cq_host, CQ_VENDOR_CFG + offset));
 	pr_err(DRV_NAME ": ===========================================\n");
 
 	cmdq_dump_task_history(cq_host);
@@ -345,6 +372,7 @@
 {
 	int err = 0;
 	u32 cqcfg;
+	u32 cqcap = 0;
 	bool dcmd_enable;
 	struct cmdq_host *cq_host = mmc_cmdq_private(mmc);
 
@@ -373,6 +401,24 @@
 	cqcfg = ((cq_host->caps & CMDQ_TASK_DESC_SZ_128 ? CQ_TASK_DESC_SZ : 0) |
 			(dcmd_enable ? CQ_DCMD : 0));
 
+	cqcap = cmdq_readl(cq_host, CQCAP);
+	if (cqcap & CQCAP_CS) {
+		/*
+		 * In case host controller supports cryptographic operations
+		 * then, it uses 128bit task descriptor. Upper 64 bits of task
+		 * descriptor would be used to pass crypto specific informaton.
+		 */
+		cq_host->caps |= CMDQ_CAP_CRYPTO_SUPPORT |
+				 CMDQ_TASK_DESC_SZ_128;
+		cqcfg |= CQ_ICE_ENABLE;
+		/*
+		 * For SDHC v5.0 onwards, ICE 3.0 specific registers are added
+		 * in CQ register space, due to which few CQ registers are
+		 * shifted. Set offset_changed boolean to use updated address.
+		 */
+		cq_host->offset_changed = true;
+	}
+
 	cmdq_writel(cq_host, cqcfg, CQCFG);
 	/* enable CQ_HOST */
 	cmdq_writel(cq_host, cmdq_readl(cq_host, CQCFG) | CQ_ENABLE,
@@ -688,6 +734,30 @@
 		upper_32_bits(*task_desc));
 }
 
+static inline
+void cmdq_prep_crypto_desc(struct cmdq_host *cq_host, u64 *task_desc,
+			u64 ice_ctx)
+{
+	u64 *ice_desc = NULL;
+
+	if (cq_host->caps & CMDQ_CAP_CRYPTO_SUPPORT) {
+		/*
+		 * Get the address of ice context for the given task descriptor.
+		 * ice context is present in the upper 64bits of task descriptor
+		 * ice_conext_base_address = task_desc + 8-bytes
+		 */
+		ice_desc = (__le64 *)((u8 *)task_desc +
+						CQ_TASK_DESC_TASK_PARAMS_SIZE);
+		memset(ice_desc, 0, CQ_TASK_DESC_ICE_PARAMS_SIZE);
+
+		/*
+		 *  Assign upper 64bits data of task descritor with ice context
+		 */
+		if (ice_ctx)
+			*ice_desc = cpu_to_le64(ice_ctx);
+	}
+}
+
 static void cmdq_pm_qos_vote(struct sdhci_host *host, struct mmc_request *mrq)
 {
 	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -711,6 +781,7 @@
 	u32 tag = mrq->cmdq_req->tag;
 	struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
 	struct sdhci_host *host = mmc_priv(mmc);
+	u64 ice_ctx = 0;
 
 	if (!cq_host->enabled) {
 		pr_err("%s: CMDQ host not enabled yet !!!\n",
@@ -730,8 +801,9 @@
 	}
 
 	if (cq_host->ops->crypto_cfg) {
-		err = cq_host->ops->crypto_cfg(mmc, mrq, tag);
+		err = cq_host->ops->crypto_cfg(mmc, mrq, tag, &ice_ctx);
 		if (err) {
+			mmc->err_stats[MMC_ERR_ICE_CFG]++;
 			pr_err("%s: failed to configure crypto: err %d tag %d\n",
 					mmc_hostname(mmc), err, tag);
 			goto out;
@@ -743,6 +815,9 @@
 	cmdq_prep_task_desc(mrq, &data, 1,
 			    (mrq->cmdq_req->cmdq_req_flags & QBR));
 	*task_desc = cpu_to_le64(data);
+
+	cmdq_prep_crypto_desc(cq_host, task_desc, ice_ctx);
+
 	cmdq_log_task_desc_history(cq_host, *task_desc, false);
 
 	err = cmdq_prep_tran_desc(mrq, cq_host, tag);
@@ -777,17 +852,31 @@
 {
 	struct mmc_request *mrq;
 	struct cmdq_host *cq_host = (struct cmdq_host *)mmc_cmdq_private(mmc);
+	int offset = 0;
+	int err = 0;
 
+	if (cq_host->offset_changed)
+		offset = CQ_V5_VENDOR_CFG;
 	mrq = get_req_by_tag(cq_host, tag);
 	if (tag == cq_host->dcmd_slot)
 		mrq->cmd->resp[0] = cmdq_readl(cq_host, CQCRDCT);
 
 	if (mrq->cmdq_req->cmdq_req_flags & DCMD)
-		cmdq_writel(cq_host, cmdq_readl(cq_host, CQ_VENDOR_CFG) |
-			    CMDQ_SEND_STATUS_TRIGGER, CQ_VENDOR_CFG);
+		cmdq_writel(cq_host,
+			cmdq_readl(cq_host, CQ_VENDOR_CFG + offset) |
+			CMDQ_SEND_STATUS_TRIGGER, CQ_VENDOR_CFG + offset);
 
 	cmdq_runtime_pm_put(cq_host);
-	if (cq_host->ops->crypto_cfg_reset)
+
+	if (cq_host->ops->crypto_cfg_end) {
+		err = cq_host->ops->crypto_cfg_end(mmc, mrq);
+		if (err) {
+			pr_err("%s: failed to end ice config: err %d tag %d\n",
+					mmc_hostname(mmc), err, tag);
+		}
+	}
+	if (!(cq_host->caps & CMDQ_CAP_CRYPTO_SUPPORT) &&
+			cq_host->ops->crypto_cfg_reset)
 		cq_host->ops->crypto_cfg_reset(mmc, tag);
 	mrq->done(mrq);
 }
@@ -801,6 +890,8 @@
 	struct mmc_request *mrq;
 	int ret;
 	u32 dbr_set = 0;
+	u32 dev_pend_set = 0;
+	int stat_err = 0;
 
 	status = cmdq_readl(cq_host, CQIS);
 
@@ -809,7 +900,9 @@
 	MMC_TRACE(mmc, "%s: CQIS: 0x%x err: %d\n",
 		__func__, status, err);
 
-	if (err || (status & CQIS_RED)) {
+	stat_err = status & (CQIS_RED | CQIS_GCE | CQIS_ICCE);
+
+	if (err || stat_err) {
 		err_info = cmdq_readl(cq_host, CQTERRI);
 		pr_err("%s: err: %d status: 0x%08x task-err-info (0x%08lx)\n",
 		       mmc_hostname(mmc), err, status, err_info);
@@ -912,7 +1005,7 @@
 		 * CQE detected a response error from device
 		 * In most cases, this would require a reset.
 		 */
-		if (status & CQIS_RED) {
+		if (stat_err & CQIS_RED) {
 			/*
 			 * will check if the RED error is due to a bkops
 			 * exception once the queue is empty
@@ -923,6 +1016,7 @@
 				mmc->card->bkops.needs_check = true;
 
 			mrq->cmdq_req->resp_err = true;
+			mmc->err_stats[MMC_ERR_CMDQ_RED]++;
 			pr_err("%s: Response error (0x%08x) from card !!!",
 				mmc_hostname(mmc), cmdq_readl(cq_host, CQCRA));
 
@@ -931,6 +1025,64 @@
 			mrq->cmdq_req->resp_arg = cmdq_readl(cq_host, CQCRA);
 		}
 
+		/*
+		 * Generic Crypto error detected by CQE.
+		 * Its a fatal, would require cmdq reset.
+		 */
+		if (stat_err & CQIS_GCE) {
+			if (mrq->data)
+				mrq->data->error = -EIO;
+			mmc->err_stats[MMC_ERR_CMDQ_GCE]++;
+			pr_err("%s: Crypto generic error while processing task %lu!",
+				mmc_hostname(mmc), tag);
+			MMC_TRACE(mmc, "%s: GCE error detected with tag %lu\n",
+					__func__, tag);
+		}
+		/*
+		 * Invalid crypto config error detected by CQE, clear the task.
+		 * Task can be cleared only when CQE is halt state.
+		 */
+		if (stat_err & CQIS_ICCE) {
+			/*
+			 * Invalid Crypto Config Error is detected at the
+			 * beginning of the transfer before the actual execution
+			 * started. So just clear the task in CQE. No need to
+			 * clear in device. Only the task which caused ICCE has
+			 * to be cleared. Other tasks can be continue processing
+			 * The first task which is about to be prepared would
+			 * cause ICCE Error.
+			 */
+			dbr_set = cmdq_readl(cq_host, CQTDBR);
+			dev_pend_set = cmdq_readl(cq_host, CQDPT);
+			if (dbr_set ^ dev_pend_set)
+				tag = ffs(dbr_set ^ dev_pend_set) - 1;
+			mrq = get_req_by_tag(cq_host, tag);
+			mmc->err_stats[MMC_ERR_CMDQ_ICCE]++;
+			pr_err("%s: Crypto config error while processing task %lu!",
+				mmc_hostname(mmc), tag);
+			MMC_TRACE(mmc, "%s: ICCE error with tag %lu\n",
+						__func__, tag);
+			if (mrq->data)
+				mrq->data->error = -EIO;
+			else if (mrq->cmd)
+				mrq->cmd->error = -EIO;
+			/*
+			 * If CQE is halted and tag is valid then clear the task
+			 * then un-halt CQE and set flag to skip error recovery.
+			 * If any of the condtions is not met thene it will
+			 * enter into default error recovery path.
+			 */
+			if (!ret && (dbr_set ^ dev_pend_set)) {
+				ret = cmdq_clear_task_poll(cq_host, tag);
+				if (ret) {
+					pr_err("%s: %s: task[%lu] clear failed ret=%d\n",
+						mmc_hostname(mmc),
+						__func__, tag, ret);
+				} else if (!cmdq_halt_poll(mmc, false)) {
+					mrq->cmdq_req->skip_err_handling = true;
+				}
+			}
+		}
 		cmdq_finish_data(mmc, tag);
 	} else {
 		cmdq_writel(cq_host, status, CQIS);
@@ -1001,6 +1153,7 @@
 			cq_host->ops->clear_set_irqs(mmc, true);
 		cmdq_writel(cq_host, cmdq_readl(cq_host, CQCTL) & ~HALT,
 			    CQCTL);
+		mmc_host_clr_halt(mmc);
 		return 0;
 	}
 
diff --git a/drivers/mmc/host/cmdq_hci.h b/drivers/mmc/host/cmdq_hci.h
index 8e9f765..1aabce9 100644
--- a/drivers/mmc/host/cmdq_hci.h
+++ b/drivers/mmc/host/cmdq_hci.h
@@ -18,11 +18,13 @@
 #define CQVER		0x00
 /* capabilities */
 #define CQCAP		0x04
+#define CQCAP_CS	(1 << 28)
 /* configuration */
 #define CQCFG		0x08
 #define CQ_DCMD		0x00001000
 #define CQ_TASK_DESC_SZ 0x00000100
 #define CQ_ENABLE	0x00000001
+#define CQ_ICE_ENABLE	0x00000002
 
 /* control */
 #define CQCTL		0x0C
@@ -35,6 +37,8 @@
 #define CQIS_TCC	(1 << 1)
 #define CQIS_RED	(1 << 2)
 #define CQIS_TCL	(1 << 3)
+#define CQIS_GCE	(1 << 4)
+#define CQIS_ICCE	(1 << 5)
 
 /* interrupt status enable */
 #define CQISTE		0x14
@@ -110,7 +114,7 @@
 /* command response argument */
 #define CQCRA		0x5C
 
-#define CQ_INT_ALL	0xF
+#define CQ_INT_ALL	0x3F
 #define CQIC_DEFAULT_ICCTH 31
 #define CQIC_DEFAULT_ICTOVAL 1
 
@@ -141,9 +145,17 @@
 #define DAT_ADDR_LO(x)	((x & 0xFFFFFFFF) << 32)
 #define DAT_ADDR_HI(x)	((x & 0xFFFFFFFF) << 0)
 
+/*
+ * Add new macro for updated CQ vendor specific
+ * register address for SDHC v5.0 onwards.
+ */
+#define CQ_V5_VENDOR_CFG	0x900
 #define CQ_VENDOR_CFG	0x100
 #define CMDQ_SEND_STATUS_TRIGGER (1 << 31)
 
+#define CQ_TASK_DESC_TASK_PARAMS_SIZE	8
+#define CQ_TASK_DESC_ICE_PARAMS_SIZE	8
+
 struct task_history {
 	u64 task;
 	bool is_dcmd;
@@ -161,6 +173,7 @@
 	u32 dcmd_slot;
 	u32 caps;
 #define CMDQ_TASK_DESC_SZ_128 0x1
+#define CMDQ_CAP_CRYPTO_SUPPORT 0x2
 
 	u32 quirks;
 #define CMDQ_QUIRK_SHORT_TXFR_DESC_SZ 0x1
@@ -169,6 +182,7 @@
 	bool enabled;
 	bool halted;
 	bool init_done;
+	bool offset_changed;
 
 	u8 *desc_base;
 
@@ -209,7 +223,8 @@
 	int (*reset)(struct mmc_host *mmc);
 	void (*post_cqe_halt)(struct mmc_host *mmc);
 	int (*crypto_cfg)(struct mmc_host *mmc, struct mmc_request *mrq,
-				u32 slot);
+				u32 slot, u64 *ice_ctx);
+	int (*crypto_cfg_end)(struct mmc_host *mmc, struct mmc_request *mrq);
 	void (*crypto_cfg_reset)(struct mmc_host *mmc, unsigned int slot);
 };
 
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index df478ae..f81f417 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -2610,8 +2610,8 @@
 	host->slot[id] = slot;
 
 	mmc->ops = &dw_mci_ops;
-	if (of_property_read_u32_array(host->dev->of_node,
-				       "clock-freq-min-max", freq, 2)) {
+	if (device_property_read_u32_array(host->dev, "clock-freq-min-max",
+					   freq, 2)) {
 		mmc->f_min = DW_MCI_FREQ_MIN;
 		mmc->f_max = DW_MCI_FREQ_MAX;
 	} else {
@@ -2709,7 +2709,6 @@
 {
 	int addr_config;
 	struct device *dev = host->dev;
-	struct device_node *np = dev->of_node;
 
 	/*
 	* Check tansfer mode from HCON[17:16]
@@ -2770,8 +2769,9 @@
 		dev_info(host->dev, "Using internal DMA controller.\n");
 	} else {
 		/* TRANS_MODE_EDMAC: check dma bindings again */
-		if ((of_property_count_strings(np, "dma-names") < 0) ||
-		    (!of_find_property(np, "dmas", NULL))) {
+		if ((device_property_read_string_array(dev, "dma-names",
+						       NULL, 0) < 0) ||
+		    !device_property_present(dev, "dmas")) {
 			goto no_dma;
 		}
 		host->dma_ops = &dw_mci_edmac_ops;
@@ -2931,7 +2931,6 @@
 {
 	struct dw_mci_board *pdata;
 	struct device *dev = host->dev;
-	struct device_node *np = dev->of_node;
 	const struct dw_mci_drv_data *drv_data = host->drv_data;
 	int ret;
 	u32 clock_frequency;
@@ -2948,15 +2947,16 @@
 	}
 
 	/* find out number of slots supported */
-	of_property_read_u32(np, "num-slots", &pdata->num_slots);
+	device_property_read_u32(dev, "num-slots", &pdata->num_slots);
 
-	if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth))
+	if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth))
 		dev_info(dev,
 			 "fifo-depth property not found, using value of FIFOTH register as default\n");
 
-	of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
+	device_property_read_u32(dev, "card-detect-delay",
+				 &pdata->detect_delay_ms);
 
-	if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
+	if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency))
 		pdata->bus_hz = clock_frequency;
 
 	if (drv_data && drv_data->parse_dt) {
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index c531dee..8f27fe3 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -21,6 +21,7 @@
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
 #include <linux/gpio.h>
+#include <linux/interrupt.h>
 #include <linux/irq.h>
 #include <linux/io.h>
 
diff --git a/drivers/mmc/host/sdhci-msm-ice.c b/drivers/mmc/host/sdhci-msm-ice.c
index d624b48..f86ce5b 100644
--- a/drivers/mmc/host/sdhci-msm-ice.c
+++ b/drivers/mmc/host/sdhci-msm-ice.c
@@ -276,6 +276,58 @@
 	mb();
 }
 
+static inline
+void sdhci_msm_ice_hci_update_cmdq_cfg(u64 dun, unsigned int bypass,
+				short key_index, u64 *ice_ctx)
+{
+	/*
+	 * The naming convention got changed between ICE2.0 and ICE3.0
+	 * registers fields. Below is the equivalent names for
+	 * ICE3.0 Vs ICE2.0:
+	 *   Data Unit Number(DUN) == Logical Base address(LBA)
+	 *   Crypto Configuration index (CCI) == Key Index
+	 *   Crypto Enable (CE) == !BYPASS
+	 */
+	if (ice_ctx)
+		*ice_ctx = DATA_UNIT_NUM(dun) |
+			CRYPTO_CONFIG_INDEX(key_index) |
+			CRYPTO_ENABLE(!bypass);
+}
+
+static
+void sdhci_msm_ice_hci_update_noncq_cfg(struct sdhci_host *host,
+		u64 dun, unsigned int bypass, short key_index)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	unsigned int crypto_params = 0;
+	/*
+	 * The naming convention got changed between ICE2.0 and ICE3.0
+	 * registers fields. Below is the equivalent names for
+	 * ICE3.0 Vs ICE2.0:
+	 *   Data Unit Number(DUN) == Logical Base address(LBA)
+	 *   Crypto Configuration index (CCI) == Key Index
+	 *   Crypto Enable (CE) == !BYPASS
+	 */
+	/* Configure ICE bypass mode */
+	crypto_params |=
+		(!bypass & MASK_SDHCI_MSM_ICE_HCI_PARAM_CE)
+			<< OFFSET_SDHCI_MSM_ICE_HCI_PARAM_CE;
+	/* Configure Crypto Configure Index (CCI) */
+	crypto_params |= (key_index &
+			 MASK_SDHCI_MSM_ICE_HCI_PARAM_CCI)
+			 << OFFSET_SDHCI_MSM_ICE_HCI_PARAM_CCI;
+
+	writel_relaxed((crypto_params & 0xFFFFFFFF),
+		msm_host->cryptoio + ICE_NONCQ_CRYPTO_PARAMS);
+
+	/* Update DUN */
+	writel_relaxed((dun & 0xFFFFFFFF),
+		msm_host->cryptoio + ICE_NONCQ_CRYPTO_DUN);
+	/* Ensure ICE registers are configured before issuing SDHCI request */
+	mb();
+}
+
 int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
 			u32 slot)
 {
@@ -308,7 +360,88 @@
 				slot, bypass, key_index);
 	}
 
-	sdhci_msm_ice_update_cfg(host, lba, slot, bypass, key_index);
+	if (msm_host->ice_hci_support) {
+		/* For ICE HCI / ICE3.0 */
+		sdhci_msm_ice_hci_update_noncq_cfg(host, lba, bypass,
+						key_index);
+	} else {
+		/* For ICE versions earlier to ICE3.0 */
+		sdhci_msm_ice_update_cfg(host, lba, slot, bypass, key_index);
+	}
+	return 0;
+}
+
+int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
+			struct mmc_request *mrq, u32 slot, u64 *ice_ctx)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	int err = 0;
+	short key_index = 0;
+	sector_t lba = 0;
+	unsigned int bypass = SDHCI_MSM_ICE_ENABLE_BYPASS;
+	struct request *req;
+
+	if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+		pr_err("%s: ice is in invalid state %d\n",
+			mmc_hostname(host->mmc), msm_host->ice.state);
+		return -EINVAL;
+	}
+
+	WARN_ON(!mrq);
+	if (!mrq)
+		return -EINVAL;
+	req = mrq->req;
+	if (req) {
+		lba = req->__sector;
+		err = sdhci_msm_ice_get_cfg(msm_host, req, &bypass, &key_index);
+		if (err)
+			return err;
+		pr_debug("%s: %s: slot %d bypass %d key_index %d\n",
+				mmc_hostname(host->mmc),
+				(rq_data_dir(req) == WRITE) ? "WRITE" : "READ",
+				slot, bypass, key_index);
+	}
+
+	if (msm_host->ice_hci_support) {
+		/* For ICE HCI / ICE3.0 */
+		sdhci_msm_ice_hci_update_cmdq_cfg(lba, bypass, key_index,
+						ice_ctx);
+	} else {
+		/* For ICE versions earlier to ICE3.0 */
+		sdhci_msm_ice_update_cfg(host, lba, slot, bypass, key_index);
+	}
+	return 0;
+}
+
+int sdhci_msm_ice_cfg_end(struct sdhci_host *host, struct mmc_request *mrq)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	int err = 0;
+	struct request *req;
+
+	if (!host->is_crypto_en)
+		return 0;
+
+	if (msm_host->ice.state != SDHCI_MSM_ICE_STATE_ACTIVE) {
+		pr_err("%s: ice is in invalid state %d\n",
+			mmc_hostname(host->mmc), msm_host->ice.state);
+		return -EINVAL;
+	}
+
+	req = mrq->req;
+	if (req) {
+		if (msm_host->ice.vops->config_end) {
+			err = msm_host->ice.vops->config_end(req);
+			if (err) {
+				pr_err("%s: ice config end failed %d\n",
+						mmc_hostname(host->mmc), err);
+				return err;
+			}
+		}
+	}
+
 	return 0;
 }
 
diff --git a/drivers/mmc/host/sdhci-msm-ice.h b/drivers/mmc/host/sdhci-msm-ice.h
index 23922cf..6296174 100644
--- a/drivers/mmc/host/sdhci-msm-ice.h
+++ b/drivers/mmc/host/sdhci-msm-ice.h
@@ -42,6 +42,8 @@
 #define ICE_HCI_SUPPORT		(1 << 28)
 #define ICE_CQ_CONFIG		0x08
 #define CRYPTO_GENERAL_ENABLE	(1 << 1)
+#define ICE_NONCQ_CRYPTO_PARAMS	0x70
+#define ICE_NONCQ_CRYPTO_DUN	0x74
 
 /* ICE3.0 register which got added hc reg space */
 #define HC_VENDOR_SPECIFIC_FUNC4	0x260
@@ -52,8 +54,10 @@
 /* SDHCI MSM ICE CTRL Info register offset */
 enum {
 	OFFSET_SDHCI_MSM_ICE_CTRL_INFO_BYPASS     = 0,
-	OFFSET_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX  = 0x1,
-	OFFSET_SDHCI_MSM_ICE_CTRL_INFO_CDU        = 0x6,
+	OFFSET_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX  = 1,
+	OFFSET_SDHCI_MSM_ICE_CTRL_INFO_CDU        = 6,
+	OFFSET_SDHCI_MSM_ICE_HCI_PARAM_CCI	  = 0,
+	OFFSET_SDHCI_MSM_ICE_HCI_PARAM_CE	  = 8,
 };
 
 /* SDHCI MSM ICE CTRL Info register masks */
@@ -61,6 +65,8 @@
 	MASK_SDHCI_MSM_ICE_CTRL_INFO_BYPASS     = 0x1,
 	MASK_SDHCI_MSM_ICE_CTRL_INFO_KEY_INDEX  = 0x1F,
 	MASK_SDHCI_MSM_ICE_CTRL_INFO_CDU        = 0x7,
+	MASK_SDHCI_MSM_ICE_HCI_PARAM_CE		= 0x1,
+	MASK_SDHCI_MSM_ICE_HCI_PARAM_CCI	= 0xff
 };
 
 /* SDHCI MSM ICE encryption/decryption bypass state */
@@ -99,6 +105,9 @@
 void sdhci_msm_ice_cfg_reset(struct sdhci_host *host, u32 slot);
 int sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq,
 			u32 slot);
+int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
+			struct mmc_request *mrq, u32 slot, u64 *ice_ctx);
+int sdhci_msm_ice_cfg_end(struct sdhci_host *host, struct mmc_request *mrq);
 int sdhci_msm_ice_reset(struct sdhci_host *host);
 int sdhci_msm_ice_resume(struct sdhci_host *host);
 int sdhci_msm_ice_suspend(struct sdhci_host *host);
@@ -130,6 +139,16 @@
 {
 	return 0;
 }
+static inline int sdhci_msm_ice_cmdq_cfg(struct sdhci_host *host,
+		struct mmc_request *mrq, u32 slot, u64 *ice_ctx)
+{
+	return 0;
+}
+static inline int sdhci_msm_ice_cfg_end(struct sdhci_host *host,
+			struct mmc_request *mrq)
+{
+	return 0;
+}
 inline int sdhci_msm_ice_reset(struct sdhci_host *host)
 {
 	return 0;
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 1e25b31..e817a02 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -146,6 +146,7 @@
 #define CORE_START_CDC_TRAFFIC		(1 << 6)
 
 #define CORE_PWRSAVE_DLL	(1 << 3)
+#define CORE_FIFO_ALT_EN	(1 << 10)
 #define CORE_CMDEN_HS400_INPUT_MASK_CNT (1 << 13)
 
 #define CORE_DDR_CAL_EN		(1 << 0)
@@ -967,7 +968,10 @@
 	 * Reprogramming the value in case it might have been modified by
 	 * bootloaders.
 	 */
-	if (msm_host->rclk_delay_fix) {
+	if (msm_host->pdata->rclk_wa) {
+		writel_relaxed(msm_host->pdata->ddr_config, host->ioaddr +
+			msm_host_offset->CORE_DDR_CONFIG_2);
+	} else if (msm_host->rclk_delay_fix) {
 		writel_relaxed(DDR_CONFIG_2_POR_VAL, host->ioaddr +
 			msm_host_offset->CORE_DDR_CONFIG_2);
 	} else {
@@ -1143,6 +1147,7 @@
 	bool drv_type_changed = false;
 	struct mmc_card *card = host->mmc->card;
 	int sts_retry;
+	u8 last_good_phase = 0;
 
 	/*
 	 * Tuning is required for SDR104, HS200 and HS400 cards and
@@ -1228,6 +1233,22 @@
 		mmc_wait_for_req(mmc, &mrq);
 
 		if (card && (cmd.error || data.error)) {
+			/*
+			 * Set the dll to last known good phase while sending
+			 * status command to ensure that status command won't
+			 * fail due to bad phase.
+			 */
+			if (tuned_phase_cnt)
+				last_good_phase =
+					tuned_phases[tuned_phase_cnt-1];
+			else if (msm_host->saved_tuning_phase !=
+					INVALID_TUNING_PHASE)
+				last_good_phase = msm_host->saved_tuning_phase;
+
+			rc = msm_config_cm_dll_phase(host, last_good_phase);
+			if (rc)
+				goto kfree;
+
 			sts_cmd.opcode = MMC_SEND_STATUS;
 			sts_cmd.arg = card->rca << 16;
 			sts_cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -1263,6 +1284,11 @@
 			pr_debug("%s: %s: found *** good *** phase = %d\n",
 				mmc_hostname(mmc), __func__, phase);
 		} else {
+			/* Ignore crc errors occurred during tuning */
+			if (cmd.error)
+				mmc->err_stats[MMC_ERR_CMD_CRC]--;
+			else if (data.error)
+				mmc->err_stats[MMC_ERR_DAT_CRC]--;
 			pr_debug("%s: %s: found ## bad ## phase = %d\n",
 				mmc_hostname(mmc), __func__, phase);
 		}
@@ -1380,6 +1406,32 @@
 	return ret;
 }
 
+static int sdhci_msm_config_pinctrl_drv_type(struct sdhci_msm_pltfm_data *pdata,
+		unsigned int clock)
+{
+	int ret = 0;
+
+	if (clock > 150000000) {
+		if (pdata->pctrl_data->pins_drv_type_200MHz)
+			ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+				pdata->pctrl_data->pins_drv_type_200MHz);
+	} else if (clock > 75000000) {
+		if (pdata->pctrl_data->pins_drv_type_100MHz)
+			ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+				pdata->pctrl_data->pins_drv_type_100MHz);
+	} else if (clock > 400000) {
+		if (pdata->pctrl_data->pins_drv_type_50MHz)
+			ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+				pdata->pctrl_data->pins_drv_type_50MHz);
+	} else {
+		if (pdata->pctrl_data->pins_drv_type_400KHz)
+			ret = pinctrl_select_state(pdata->pctrl_data->pctrl,
+				pdata->pctrl_data->pins_drv_type_400KHz);
+	}
+
+	return ret;
+}
+
 static int sdhci_msm_setup_pinctrl(struct sdhci_msm_pltfm_data *pdata,
 		bool enable)
 {
@@ -1560,6 +1612,35 @@
 		dev_err(dev, "Could not get sleep pinstates, err:%d\n", ret);
 		goto out;
 	}
+
+	pctrl_data->pins_drv_type_400KHz = pinctrl_lookup_state(
+			pctrl_data->pctrl, "ds_400KHz");
+	if (IS_ERR(pctrl_data->pins_drv_type_400KHz)) {
+		dev_dbg(dev, "Could not get 400K pinstates, err:%d\n", ret);
+		pctrl_data->pins_drv_type_400KHz = NULL;
+	}
+
+	pctrl_data->pins_drv_type_50MHz = pinctrl_lookup_state(
+			pctrl_data->pctrl, "ds_50MHz");
+	if (IS_ERR(pctrl_data->pins_drv_type_50MHz)) {
+		dev_dbg(dev, "Could not get 50M pinstates, err:%d\n", ret);
+		pctrl_data->pins_drv_type_50MHz = NULL;
+	}
+
+	pctrl_data->pins_drv_type_100MHz = pinctrl_lookup_state(
+			pctrl_data->pctrl, "ds_100MHz");
+	if (IS_ERR(pctrl_data->pins_drv_type_100MHz)) {
+		dev_dbg(dev, "Could not get 100M pinstates, err:%d\n", ret);
+		pctrl_data->pins_drv_type_100MHz = NULL;
+	}
+
+	pctrl_data->pins_drv_type_200MHz = pinctrl_lookup_state(
+			pctrl_data->pctrl, "ds_200MHz");
+	if (IS_ERR(pctrl_data->pins_drv_type_200MHz)) {
+		dev_dbg(dev, "Could not get 200M pinstates, err:%d\n", ret);
+		pctrl_data->pins_drv_type_200MHz = NULL;
+	}
+
 	pdata->pctrl_data = pctrl_data;
 out:
 	return ret;
@@ -1884,8 +1965,16 @@
 			dev_err(dev, "Invalid clock table\n");
 			goto out;
 		}
+		if (ice_clk_table_len != 2) {
+			dev_err(dev, "Need max and min frequencies in the table\n");
+			goto out;
+		}
 		pdata->sup_ice_clk_table = ice_clk_table;
 		pdata->sup_ice_clk_cnt = ice_clk_table_len;
+		pdata->ice_clk_max = pdata->sup_ice_clk_table[0];
+		pdata->ice_clk_min = pdata->sup_ice_clk_table[1];
+		dev_dbg(dev, "supported ICE clock rates (Hz): max: %u min: %u\n",
+				pdata->ice_clk_max, pdata->ice_clk_min);
 	}
 
 	pdata->vreg_data = devm_kzalloc(dev, sizeof(struct
@@ -1960,6 +2049,9 @@
 	msm_host->regs_restore.is_supported =
 		of_property_read_bool(np, "qcom,restore-after-cx-collapse");
 
+	if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config))
+		pdata->rclk_wa = true;
+
 	return pdata;
 out:
 	return NULL;
@@ -2987,11 +3079,20 @@
 		}
 	}
 
+	if (!IS_ERR(msm_host->bus_aggr_clk)) {
+		rc = clk_prepare_enable(msm_host->bus_aggr_clk);
+		if (rc) {
+			pr_err("%s: %s: failed to enable the bus aggr clk with error %d\n",
+			       mmc_hostname(host->mmc), __func__, rc);
+			goto disable_pclk;
+		}
+	}
+
 	rc = clk_prepare_enable(msm_host->clk);
 	if (rc) {
 		pr_err("%s: %s: failed to enable the host-clk with error %d\n",
 		       mmc_hostname(host->mmc), __func__, rc);
-		goto disable_pclk;
+		goto disable_bus_aggr_clk;
 	}
 
 	if (!IS_ERR(msm_host->ice_clk)) {
@@ -3011,6 +3112,9 @@
 disable_host_clk:
 	if (!IS_ERR(msm_host->clk))
 		clk_disable_unprepare(msm_host->clk);
+disable_bus_aggr_clk:
+	if (!IS_ERR(msm_host->bus_aggr_clk))
+		clk_disable_unprepare(msm_host->bus_aggr_clk);
 disable_pclk:
 	if (!IS_ERR(msm_host->pclk))
 		clk_disable_unprepare(msm_host->pclk);
@@ -3032,6 +3136,8 @@
 			clk_disable_unprepare(msm_host->clk);
 		if (!IS_ERR(msm_host->ice_clk))
 			clk_disable_unprepare(msm_host->ice_clk);
+		if (!IS_ERR(msm_host->bus_aggr_clk))
+			clk_disable_unprepare(msm_host->bus_aggr_clk);
 		if (!IS_ERR(msm_host->pclk))
 			clk_disable_unprepare(msm_host->pclk);
 		sdhci_msm_bus_voting(host, 0);
@@ -3123,6 +3229,8 @@
 		clk_disable_unprepare(msm_host->clk);
 	if (!IS_ERR(msm_host->ice_clk))
 		clk_disable_unprepare(msm_host->ice_clk);
+	if (!IS_ERR_OR_NULL(msm_host->bus_aggr_clk))
+		clk_disable_unprepare(msm_host->bus_aggr_clk);
 	if (!IS_ERR_OR_NULL(msm_host->pclk))
 		clk_disable_unprepare(msm_host->pclk);
 	atomic_set(&msm_host->controller_clock, 0);
@@ -3296,6 +3404,16 @@
 		}
 		msm_host->clk_rate = sup_clock;
 		host->clock = clock;
+
+		/* Configure pinctrl drive type according to
+		 * current clock rate
+		 */
+		rc = sdhci_msm_config_pinctrl_drv_type(msm_host->pdata, clock);
+		if (rc)
+			pr_err("%s: %s: Failed to set pinctrl drive type for clock rate %u (%d)\n",
+					mmc_hostname(host->mmc), __func__,
+					clock, rc);
+
 		/*
 		 * Update the bus vote in case of frequency change due to
 		 * clock scaling.
@@ -3390,6 +3508,8 @@
 	/* registers offset changed starting from 4.2.0 */
 	int offset = minor >= SDHCI_MSM_VER_420 ? 0 : 0x48;
 
+	if (cq_host->offset_changed)
+		offset += CQ_V5_VENDOR_CFG;
 	pr_err("---- Debug RAM dump ----\n");
 	pr_err(DRV_NAME ": Debug RAM wrap-around: 0x%08x | Debug RAM overlap: 0x%08x\n",
 	       cmdq_readl(cq_host, CQ_CMD_DBG_RAM_WA + offset),
@@ -4095,8 +4215,37 @@
 	return max_curr;
 }
 
+static int sdhci_msm_notify_load(struct sdhci_host *host, enum mmc_load state)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct sdhci_msm_host *msm_host = pltfm_host->priv;
+	int ret = 0;
+	u32 clk_rate = 0;
+
+	if (!IS_ERR(msm_host->ice_clk)) {
+		clk_rate = (state == MMC_LOAD_LOW) ?
+			msm_host->pdata->ice_clk_min :
+			msm_host->pdata->ice_clk_max;
+		if (msm_host->ice_clk_rate == clk_rate)
+			return 0;
+		pr_debug("%s: changing ICE clk rate to %u\n",
+				mmc_hostname(host->mmc), clk_rate);
+		ret = clk_set_rate(msm_host->ice_clk, clk_rate);
+		if (ret) {
+			pr_err("%s: ICE_CLK rate set failed (%d) for %u\n",
+				mmc_hostname(host->mmc), ret, clk_rate);
+			return ret;
+		}
+		msm_host->ice_clk_rate = clk_rate;
+	}
+	return 0;
+}
+
 static struct sdhci_ops sdhci_msm_ops = {
 	.crypto_engine_cfg = sdhci_msm_ice_cfg,
+	.crypto_engine_cmdq_cfg = sdhci_msm_ice_cmdq_cfg,
+	.crypto_engine_cfg_end = sdhci_msm_ice_cfg_end,
+	.crypto_cfg_reset = sdhci_msm_ice_cfg_reset,
 	.crypto_engine_reset = sdhci_msm_ice_reset,
 	.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
 	.check_power_status = sdhci_msm_check_power_status,
@@ -4119,6 +4268,7 @@
 	.pre_req = sdhci_msm_pre_req,
 	.post_req = sdhci_msm_post_req,
 	.get_current_limit = sdhci_msm_get_current_limit,
+	.notify_load = sdhci_msm_notify_load,
 };
 
 static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host,
@@ -4196,7 +4346,7 @@
 	 * starts coming.
 	 */
 	if ((major == 1) && ((minor == 0x42) || (minor == 0x46) ||
-				(minor == 0x49)))
+				(minor == 0x49) || (minor >= 0x6b)))
 		msm_host->use_14lpp_dll = true;
 
 	/* Fake 3.0V support for SDIO devices which requires such voltage */
@@ -4221,8 +4371,10 @@
 	/* keep track of the value in SDHCI_CAPABILITIES */
 	msm_host->caps_0 = caps;
 
-	if ((major == 1) && (minor >= 0x6b))
+	if ((major == 1) && (minor >= 0x6b)) {
 		msm_host->ice_hci_support = true;
+		host->cdr_support = true;
+	}
 }
 
 #ifdef CONFIG_MMC_CQ_HCI
@@ -4289,6 +4441,7 @@
 	struct resource *tlmm_memres = NULL;
 	void __iomem *tlmm_mem;
 	unsigned long flags;
+	bool force_probe;
 
 	pr_debug("%s: Enter %s\n", dev_name(&pdev->dev), __func__);
 	msm_host = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_msm_host),
@@ -4328,7 +4481,7 @@
 		 */
 		dev_err(&pdev->dev, "%s: required ICE device not probed yet err = %d\n",
 			__func__, ret);
-		goto out_host_free;
+		goto pltfm_free;
 
 	} else if (ret == -ENODEV) {
 		/*
@@ -4340,7 +4493,7 @@
 	} else if (ret) {
 		dev_err(&pdev->dev, "%s: sdhci_msm_ice_get_dev failed %d\n",
 			__func__, ret);
-		goto out_host_free;
+		goto pltfm_free;
 	}
 
 	/* Extract platform data */
@@ -4352,8 +4505,13 @@
 			goto pltfm_free;
 		}
 
+		/* Read property to determine if the probe is forced */
+		force_probe = of_find_property(pdev->dev.of_node,
+			"qcom,force-sdhc1-probe", NULL);
+
 		/* skip the probe if eMMC isn't a boot device */
-		if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev)) {
+		if ((ret == 1) && !sdhci_msm_is_bootdevice(&pdev->dev)
+		    && !force_probe) {
 			ret = -ENODEV;
 			goto pltfm_free;
 		}
@@ -4402,25 +4560,35 @@
 	}
 	atomic_set(&msm_host->controller_clock, 1);
 
+	/* Setup SDC ufs bus aggr clock */
+	msm_host->bus_aggr_clk = devm_clk_get(&pdev->dev, "bus_aggr_clk");
+	if (!IS_ERR(msm_host->bus_aggr_clk)) {
+		ret = clk_prepare_enable(msm_host->bus_aggr_clk);
+		if (ret) {
+			dev_err(&pdev->dev, "Bus aggregate clk not enabled\n");
+			goto pclk_disable;
+		}
+	}
+
 	if (msm_host->ice.pdev) {
 		/* Setup SDC ICE clock */
 		msm_host->ice_clk = devm_clk_get(&pdev->dev, "ice_core_clk");
 		if (!IS_ERR(msm_host->ice_clk)) {
 			/* ICE core has only one clock frequency for now */
 			ret = clk_set_rate(msm_host->ice_clk,
-					msm_host->pdata->sup_ice_clk_table[0]);
+					msm_host->pdata->ice_clk_max);
 			if (ret) {
 				dev_err(&pdev->dev, "ICE_CLK rate set failed (%d) for %u\n",
 					ret,
-					msm_host->pdata->sup_ice_clk_table[0]);
-				goto pclk_disable;
+					msm_host->pdata->ice_clk_max);
+				goto bus_aggr_clk_disable;
 			}
 			ret = clk_prepare_enable(msm_host->ice_clk);
 			if (ret)
-				goto pclk_disable;
+				goto bus_aggr_clk_disable;
 
 			msm_host->ice_clk_rate =
-				msm_host->pdata->sup_clk_table[0];
+				msm_host->pdata->ice_clk_max;
 		}
 	}
 
@@ -4428,18 +4596,18 @@
 	msm_host->clk = devm_clk_get(&pdev->dev, "core_clk");
 	if (IS_ERR(msm_host->clk)) {
 		ret = PTR_ERR(msm_host->clk);
-		goto pclk_disable;
+		goto bus_aggr_clk_disable;
 	}
 
 	/* Set to the minimum supported clock frequency */
 	ret = clk_set_rate(msm_host->clk, sdhci_msm_get_min_clock(host));
 	if (ret) {
 		dev_err(&pdev->dev, "MClk rate set failed (%d)\n", ret);
-		goto pclk_disable;
+		goto bus_aggr_clk_disable;
 	}
 	ret = clk_prepare_enable(msm_host->clk);
 	if (ret)
-		goto pclk_disable;
+		goto bus_aggr_clk_disable;
 
 	msm_host->clk_rate = sdhci_msm_get_min_clock(host);
 	atomic_set(&msm_host->clks_on, 1);
@@ -4518,6 +4686,14 @@
 	writel_relaxed(CORE_VENDOR_SPEC_POR_VAL,
 	host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC);
 
+	/*
+	 * Ensure SDHCI FIFO is enabled by disabling alternative FIFO
+	 */
+	writel_relaxed((readl_relaxed(host->ioaddr +
+			msm_host_offset->CORE_VENDOR_SPEC3) &
+			~CORE_FIFO_ALT_EN), host->ioaddr +
+			msm_host_offset->CORE_VENDOR_SPEC3);
+
 	if (!msm_host->mci_removed) {
 		/* Set HC_MODE_EN bit in HC_MODE register */
 		writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
@@ -4772,6 +4948,9 @@
 		       mmc_hostname(host->mmc), __func__, ret);
 		device_remove_file(&pdev->dev, &msm_host->auto_cmd21_attr);
 	}
+	if (sdhci_msm_is_bootdevice(&pdev->dev))
+		mmc_flush_detect_work(host->mmc);
+
 	/* Successful initialization */
 	goto out;
 
@@ -4796,6 +4975,9 @@
 clk_disable:
 	if (!IS_ERR(msm_host->clk))
 		clk_disable_unprepare(msm_host->clk);
+bus_aggr_clk_disable:
+	if (!IS_ERR(msm_host->bus_aggr_clk))
+		clk_disable_unprepare(msm_host->bus_aggr_clk);
 pclk_disable:
 	if (!IS_ERR(msm_host->pclk))
 		clk_disable_unprepare(msm_host->pclk);
@@ -4914,8 +5096,6 @@
 		if (msm_host->msm_bus_vote.client_handle)
 			sdhci_msm_bus_cancel_work_and_set_vote(host, 0);
 	}
-	trace_sdhci_msm_runtime_suspend(mmc_hostname(host->mmc), 0,
-			ktime_to_us(ktime_sub(ktime_get(), start)));
 
 	if (host->is_crypto_en) {
 		ret = sdhci_msm_ice_suspend(host);
@@ -4923,6 +5103,8 @@
 			pr_err("%s: failed to suspend crypto engine %d\n",
 					mmc_hostname(host->mmc), ret);
 	}
+	trace_sdhci_msm_runtime_suspend(mmc_hostname(host->mmc), 0,
+			ktime_to_us(ktime_sub(ktime_get(), start)));
 	return 0;
 }
 
@@ -5050,7 +5232,7 @@
 }
 
 static const struct dev_pm_ops sdhci_msm_pmops = {
-	SET_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(sdhci_msm_suspend, sdhci_msm_resume)
 	SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume,
 			   NULL)
 	.suspend_noirq = sdhci_msm_suspend_noirq,
@@ -5074,6 +5256,7 @@
 	.driver		= {
 		.name	= "sdhci_msm",
 		.owner	= THIS_MODULE,
+		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
 		.of_match_table = sdhci_msm_dt_match,
 		.pm	= SDHCI_MSM_PMOPS,
 	},
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index cdbaaa9..6e15a73 100644
--- a/drivers/mmc/host/sdhci-msm.h
+++ b/drivers/mmc/host/sdhci-msm.h
@@ -76,6 +76,10 @@
 	struct pinctrl          *pctrl;
 	struct pinctrl_state    *pins_active;
 	struct pinctrl_state    *pins_sleep;
+	struct pinctrl_state    *pins_drv_type_400KHz;
+	struct pinctrl_state    *pins_drv_type_50MHz;
+	struct pinctrl_state    *pins_drv_type_100MHz;
+	struct pinctrl_state    *pins_drv_type_200MHz;
 };
 
 struct sdhci_msm_bus_voting_data {
@@ -151,6 +155,10 @@
 	unsigned char sup_ice_clk_cnt;
 	struct sdhci_msm_pm_qos_data pm_qos_data;
 	bool sdr104_wa;
+	u32 ice_clk_max;
+	u32 ice_clk_min;
+	u32 ddr_config;
+	bool rclk_wa;
 };
 
 struct sdhci_msm_bus_vote {
@@ -201,6 +209,7 @@
 	int	pwr_irq;	/* power irq */
 	struct clk	 *clk;     /* main SD/MMC bus clock */
 	struct clk	 *pclk;    /* SDHC peripheral bus clock */
+	struct clk	 *bus_aggr_clk; /* Axi clock shared with UFS */
 	struct clk	 *bus_clk; /* SDHC bus voter clock */
 	struct clk	 *ff_clk; /* CDC calibration fixed feedback clock */
 	struct clk	 *sleep_clk; /* CDC calibration sleep clock */
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index a8b430f..83b84ff 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -31,6 +31,7 @@
 
 #define SDMMC_MC1R	0x204
 #define		SDMMC_MC1R_DDR		BIT(3)
+#define		SDMMC_MC1R_FCD		BIT(7)
 #define SDMMC_CACR	0x230
 #define		SDMMC_CACR_CAPWREN	BIT(0)
 #define		SDMMC_CACR_KEY		(0x46 << 8)
@@ -43,6 +44,15 @@
 	struct clk *mainck;
 };
 
+static void sdhci_at91_set_force_card_detect(struct sdhci_host *host)
+{
+	u8 mc1r;
+
+	mc1r = readb(host->ioaddr + SDMMC_MC1R);
+	mc1r |= SDMMC_MC1R_FCD;
+	writeb(mc1r, host->ioaddr + SDMMC_MC1R);
+}
+
 static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
 {
 	u16 clk;
@@ -112,10 +122,18 @@
 	sdhci_set_uhs_signaling(host, timing);
 }
 
+static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
+{
+	sdhci_reset(host, mask);
+
+	if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+		sdhci_at91_set_force_card_detect(host);
+}
+
 static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
 	.set_clock		= sdhci_at91_set_clock,
 	.set_bus_width		= sdhci_set_bus_width,
-	.reset			= sdhci_reset,
+	.reset			= sdhci_at91_reset,
 	.set_uhs_signaling	= sdhci_at91_set_uhs_signaling,
 	.set_power		= sdhci_at91_set_power,
 };
@@ -322,6 +340,21 @@
 		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
 	}
 
+	/*
+	 * If the device attached to the MMC bus is not removable, it is safer
+	 * to set the Force Card Detect bit. People often don't connect the
+	 * card detect signal and use this pin for another purpose. If the card
+	 * detect pin is not muxed to SDHCI controller, a default value is
+	 * used. This value can be different from a SoC revision to another
+	 * one. Problems come when this default value is not card present. To
+	 * avoid this case, if the device is non removable then the card
+	 * detection procedure using the SDMCC_CD signal is bypassed.
+	 * This bit is reset when a software reset for all command is performed
+	 * so we need to implement our own reset function to set back this bit.
+	 */
+	if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
+		sdhci_at91_set_force_card_detect(host);
+
 	pm_runtime_put_autosuspend(&pdev->dev);
 
 	return 0;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 68e49bb..566be69 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1785,6 +1785,22 @@
 	return err;
 }
 
+static int sdhci_crypto_cfg_end(struct sdhci_host *host,
+				struct mmc_request *mrq)
+{
+	int err = 0;
+
+	if (host->ops->crypto_engine_cfg_end) {
+		err = host->ops->crypto_engine_cfg_end(host, mrq);
+		if (err) {
+			pr_err("%s: failed to configure crypto\n",
+					mmc_hostname(host->mmc));
+			return err;
+		}
+	}
+	return 0;
+}
+
 static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct sdhci_host *host;
@@ -2475,6 +2491,8 @@
 		 */
 		mmc_retune_disable(mmc);
 		err = host->ops->platform_execute_tuning(host, opcode);
+		if (err)
+			host->mmc->err_stats[MMC_ERR_TUNING]++;
 		mmc_retune_enable(mmc);
 		return err;
 	}
@@ -2685,8 +2703,8 @@
 
 	data->host_cookie = COOKIE_UNMAPPED;
 
-	if (host->ops->pre_req)
-		host->ops->pre_req(host, mrq);
+	if (host->ops->post_req)
+		host->ops->post_req(host, mrq);
 }
 
 static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -2698,6 +2716,9 @@
 
 	if (host->flags & SDHCI_REQ_USE_DMA)
 		sdhci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED);
+
+	if (host->ops->pre_req)
+		host->ops->pre_req(host, mrq);
 }
 
 static inline bool sdhci_has_requests(struct sdhci_host *host)
@@ -2876,6 +2897,7 @@
 	mmiowb();
 	spin_unlock_irqrestore(&host->lock, flags);
 
+	sdhci_crypto_cfg_end(host, mrq);
 	mmc_request_done(host->mmc, mrq);
 
 	return false;
@@ -2899,8 +2921,10 @@
 	spin_lock_irqsave(&host->lock, flags);
 
 	if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
+		host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
 		pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
 		       mmc_hostname(host->mmc));
+		MMC_TRACE(host->mmc, "Timeout waiting for h/w interrupt\n");
 		sdhci_dumpregs(host);
 
 		host->cmd->error = -ETIMEDOUT;
@@ -2922,6 +2946,7 @@
 
 	if (host->data || host->data_cmd ||
 	    (host->cmd && sdhci_data_line_cmd(host->cmd))) {
+		host->mmc->err_stats[MMC_ERR_REQ_TIMEOUT]++;
 		pr_err("%s: Timeout waiting for hardware interrupt.\n",
 		       mmc_hostname(host->mmc));
 		MMC_TRACE(host->mmc, "Timeout waiting for h/w interrupt\n");
@@ -2980,13 +3005,17 @@
 	if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
 		       SDHCI_INT_END_BIT | SDHCI_INT_INDEX |
 		       SDHCI_INT_AUTO_CMD_ERR)) {
-		if (intmask & SDHCI_INT_TIMEOUT)
+		if (intmask & SDHCI_INT_TIMEOUT) {
 			host->cmd->error = -ETIMEDOUT;
-		else
+			host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
+		} else {
 			host->cmd->error = -EILSEQ;
+			host->mmc->err_stats[MMC_ERR_CMD_CRC]++;
+		}
 
 		if (intmask & SDHCI_INT_AUTO_CMD_ERR) {
 			auto_cmd_status = host->auto_cmd_err_sts;
+			host->mmc->err_stats[MMC_ERR_AUTO_CMD]++;
 			pr_err_ratelimited("%s: %s: AUTO CMD err sts 0x%08x\n",
 				mmc_hostname(host->mmc), __func__, auto_cmd_status);
 			if (auto_cmd_status & (SDHCI_AUTO_CMD12_NOT_EXEC |
@@ -3088,12 +3117,6 @@
 		 * above in sdhci_cmd_irq().
 		 */
 		if (data_cmd && (data_cmd->flags & MMC_RSP_BUSY)) {
-			if (intmask & SDHCI_INT_DATA_TIMEOUT) {
-				host->data_cmd = NULL;
-				data_cmd->error = -ETIMEDOUT;
-				sdhci_finish_mrq(host, data_cmd->mrq);
-				return;
-			}
 			if (intmask & SDHCI_INT_DATA_END) {
 				host->data_cmd = NULL;
 				/*
@@ -3108,8 +3131,22 @@
 				return;
 			}
 			if (host->quirks2 &
-				SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD)
+				SDHCI_QUIRK2_IGNORE_DATATOUT_FOR_R1BCMD) {
+				pr_err_ratelimited("%s: %s: ignoring interrupt: 0x%08x due to DATATOUT_FOR_R1B quirk\n",
+						mmc_hostname(host->mmc),
+						__func__, intmask);
+				MMC_TRACE(host->mmc,
+					"%s: Quirk ignoring intr: 0x%08x\n",
+						__func__, intmask);
 				return;
+			}
+			if (intmask & SDHCI_INT_DATA_TIMEOUT) {
+				host->data_cmd = NULL;
+				data_cmd->error = -ETIMEDOUT;
+				host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
+				sdhci_finish_mrq(host, data_cmd->mrq);
+				return;
+			}
 		}
 
 		/*
@@ -3130,16 +3167,21 @@
 		return;
 	}
 
-	if (intmask & SDHCI_INT_DATA_TIMEOUT)
+	if (intmask & SDHCI_INT_DATA_TIMEOUT) {
 		host->data->error = -ETIMEDOUT;
+		host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]++;
+	}
 	else if (intmask & SDHCI_INT_DATA_END_BIT)
 		host->data->error = -EILSEQ;
 	else if ((intmask & SDHCI_INT_DATA_CRC) &&
-		(command != MMC_BUS_TEST_R))
+		(command != MMC_BUS_TEST_R)) {
 		host->data->error = -EILSEQ;
+		host->mmc->err_stats[MMC_ERR_DAT_CRC]++;
+	}
 	else if (intmask & SDHCI_INT_ADMA_ERROR) {
 		pr_err("%s: ADMA error\n", mmc_hostname(host->mmc));
 		sdhci_adma_show_error(host);
+		host->mmc->err_stats[MMC_ERR_ADMA]++;
 		host->data->error = -EIO;
 		if (host->ops->adma_workaround)
 			host->ops->adma_workaround(host, intmask);
@@ -3218,24 +3260,31 @@
 }
 
 #ifdef CONFIG_MMC_CQ_HCI
-static int sdhci_get_cmd_err(u32 intmask)
+static int sdhci_get_cmd_err(struct sdhci_host *host, u32 intmask)
 {
-	if (intmask & SDHCI_INT_TIMEOUT)
+	if (intmask & SDHCI_INT_TIMEOUT) {
+		host->mmc->err_stats[MMC_ERR_CMD_TIMEOUT]++;
 		return -ETIMEDOUT;
-	else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
-			    SDHCI_INT_INDEX))
+	} else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
+			    SDHCI_INT_INDEX)) {
+		host->mmc->err_stats[MMC_ERR_CMD_CRC]++;
 		return -EILSEQ;
+	}
 	return 0;
 }
 
-static int sdhci_get_data_err(u32 intmask)
+static int sdhci_get_data_err(struct sdhci_host *host, u32 intmask)
 {
-	if (intmask & SDHCI_INT_DATA_TIMEOUT)
+	if (intmask & SDHCI_INT_DATA_TIMEOUT) {
+		host->mmc->err_stats[MMC_ERR_DAT_TIMEOUT]++;
 		return -ETIMEDOUT;
-	else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
+	} else if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
+		host->mmc->err_stats[MMC_ERR_DAT_CRC]++;
 		return -EILSEQ;
-	else if (intmask & SDHCI_INT_ADMA_ERROR)
+	} else if (intmask & MMC_ERR_ADMA) {
+		host->mmc->err_stats[MMC_ERR_ADMA]++;
 		return -EIO;
+	}
 	return 0;
 }
 
@@ -3246,9 +3295,9 @@
 	irqreturn_t ret;
 
 	if (intmask & SDHCI_INT_CMD_MASK)
-		err = sdhci_get_cmd_err(intmask);
+		err = sdhci_get_cmd_err(host, intmask);
 	else if (intmask & SDHCI_INT_DATA_MASK)
-		err = sdhci_get_data_err(intmask);
+		err = sdhci_get_data_err(host, intmask);
 
 	ret = cmdq_irq(host->mmc, err);
 	if (err) {
@@ -3689,7 +3738,7 @@
 			ctrl |= SDHCI_CTRL_ADMA32;
 		sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
 	}
-	if (host->ops->toggle_cdr)
+	if (host->ops->toggle_cdr && !host->cdr_support)
 		host->ops->toggle_cdr(host, false);
 }
 
@@ -3768,14 +3817,46 @@
 	sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
 }
 static int sdhci_cmdq_crypto_cfg(struct mmc_host *mmc,
-		struct mmc_request *mrq, u32 slot)
+		struct mmc_request *mrq, u32 slot, u64 *ice_ctx)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	int err = 0;
+
+	if (!host->is_crypto_en)
+		return 0;
+
+	if (host->crypto_reset_reqd && host->ops->crypto_engine_reset) {
+		err = host->ops->crypto_engine_reset(host);
+		if (err) {
+			pr_err("%s: crypto reset failed\n",
+					mmc_hostname(host->mmc));
+			goto out;
+		}
+		host->crypto_reset_reqd = false;
+	}
+
+	if (host->ops->crypto_engine_cmdq_cfg) {
+		err = host->ops->crypto_engine_cmdq_cfg(host, mrq,
+				slot, ice_ctx);
+		if (err) {
+			pr_err("%s: failed to configure crypto\n",
+					mmc_hostname(host->mmc));
+			goto out;
+		}
+	}
+out:
+	return err;
+}
+
+static int sdhci_cmdq_crypto_cfg_end(struct mmc_host *mmc,
+					struct mmc_request *mrq)
 {
 	struct sdhci_host *host = mmc_priv(mmc);
 
 	if (!host->is_crypto_en)
 		return 0;
 
-	return sdhci_crypto_cfg(host, mrq, slot);
+	return sdhci_crypto_cfg_end(host, mrq);
 }
 
 static void sdhci_cmdq_crypto_cfg_reset(struct mmc_host *mmc, unsigned int slot)
@@ -3835,7 +3916,13 @@
 }
 
 static int sdhci_cmdq_crypto_cfg(struct mmc_host *mmc,
-		struct mmc_request *mrq, u32 slot)
+		struct mmc_request *mrq, u32 slot, u64 *ice_ctx)
+{
+	return 0;
+}
+
+static int sdhci_cmdq_crypto_cfg_end(struct mmc_host *mmc,
+				struct mmc_request *mrq)
 {
 	return 0;
 }
@@ -3856,6 +3943,7 @@
 	.post_cqe_halt = sdhci_cmdq_post_cqe_halt,
 	.set_transfer_params = sdhci_cmdq_set_transfer_params,
 	.crypto_cfg	= sdhci_cmdq_crypto_cfg,
+	.crypto_cfg_end	= sdhci_cmdq_crypto_cfg_end,
 	.crypto_cfg_reset	= sdhci_cmdq_crypto_cfg_reset,
 };
 
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 04e806c..2b67d0a 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -518,7 +518,7 @@
  * Some controllers may use PIO mode to workaround HW issues in ADMA for
  * eMMC tuning commands.
  */
-#define SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING (1 << 23)
+#define SDHCI_QUIRK2_USE_PIO_FOR_EMMC_TUNING (1 << 29)
 
 
 	int irq;		/* Device IRQ */
@@ -569,6 +569,7 @@
 	bool bus_on;		/* Bus power prevents runtime suspend */
 	bool preset_enabled;	/* Preset is enabled */
 	bool pending_reset;	/* Cmd/data reset is pending */
+	bool cdr_support;
 
 	struct mmc_request *mrqs_done[SDHCI_MAX_MRQS];	/* Requests done */
 	struct mmc_request *mrq;	/* Current request */
@@ -670,6 +671,10 @@
 	int	(*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
 	int	(*crypto_engine_cfg)(struct sdhci_host *host,
 				struct mmc_request *mrq, u32 slot);
+	int	(*crypto_engine_cmdq_cfg)(struct sdhci_host *host,
+			struct mmc_request *mrq, u32 slot, u64 *ice_ctx);
+	int	(*crypto_engine_cfg_end)(struct sdhci_host *host,
+					struct mmc_request *mrq);
 	int	(*crypto_engine_reset)(struct sdhci_host *host);
 	void	(*crypto_cfg_reset)(struct sdhci_host *host, unsigned int slot);
 	void	(*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index 44b56b6..da24e2c 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -111,7 +111,7 @@
 	return dma_map_page(dev, page, offset, size, dir);
 }
 
-#ifdef CONFIG_MSM_BUS_SCALING
+#ifdef CONFIG_QCOM_BUS_SCALING
 static int msm_nand_bus_set_vote(struct msm_nand_info *info,
 			unsigned int vote)
 {
@@ -130,9 +130,11 @@
 {
 	int ret = 0;
 
-	if (IS_ERR_OR_NULL(info->clk_data.qpic_clk)) {
-		ret = -EINVAL;
-		goto out;
+	if (!info->clk_data.rpmh_clk) {
+		if (IS_ERR_OR_NULL(info->clk_data.qpic_clk)) {
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 	if (atomic_read(&info->clk_data.clk_enabled) == vote)
 		goto out;
@@ -142,15 +144,18 @@
 			pr_err("Failed to vote for bus with %d\n", ret);
 			goto out;
 		}
-		ret = clk_prepare_enable(info->clk_data.qpic_clk);
-		if (ret) {
-			pr_err("Failed to enable the bus-clock with error %d\n",
-				ret);
-			msm_nand_bus_set_vote(info, 0);
-			goto out;
+		if (!info->clk_data.rpmh_clk) {
+			ret = clk_prepare_enable(info->clk_data.qpic_clk);
+			if (ret) {
+				pr_err("Failed to enable the bus-clock with error %d\n",
+					ret);
+				msm_nand_bus_set_vote(info, 0);
+				goto out;
+			}
 		}
 	} else if (atomic_read(&info->clk_data.clk_enabled) && !vote) {
-		clk_disable_unprepare(info->clk_data.qpic_clk);
+		if (!info->clk_data.rpmh_clk)
+			clk_disable_unprepare(info->clk_data.qpic_clk);
 		msm_nand_bus_set_vote(info, 0);
 	}
 	atomic_set(&info->clk_data.clk_enabled, vote);
@@ -283,7 +288,7 @@
 }
 #endif
 
-#ifdef CONFIG_MSM_BUS_SCALING
+#ifdef CONFIG_QCOM_BUS_SCALING
 static int msm_nand_bus_register(struct platform_device *pdev,
 		struct msm_nand_info *info)
 {
@@ -314,6 +319,7 @@
 static int msm_nand_bus_register(struct platform_device *pdev,
 		struct msm_nand_info *info)
 {
+	pr_info("couldn't register due to missing config option\n");
 	return 0;
 }
 
@@ -3273,7 +3279,7 @@
 	temp_ptable = smem_get_entry(SMEM_AARM_PARTITION_TABLE, &len, 0,
 					SMEM_ANY_HOST_FLAG);
 
-	if (!temp_ptable) {
+	if (IS_ERR_OR_NULL(temp_ptable)) {
 		pr_err("Error reading partition table header\n");
 		goto out;
 	}
@@ -3313,7 +3319,7 @@
 	 */
 	temp_ptable = smem_get_entry(SMEM_AARM_PARTITION_TABLE, &len, 0,
 					SMEM_ANY_HOST_FLAG);
-	if (!temp_ptable) {
+	if (IS_ERR_OR_NULL(temp_ptable)) {
 		pr_err("Error reading partition table\n");
 		goto out;
 	}
@@ -3327,10 +3333,12 @@
 			continue;
 		/* Convert name to lower case and discard the initial chars */
 		mtd_part[i].name        = pentry->name;
+		strsep(&(mtd_part[i].name), delimiter);
+		if (!mtd_part[i].name)
+			mtd_part[i].name = pentry->name;
 		for (j = 0; j < strlen(mtd_part[i].name); j++)
 			*(mtd_part[i].name + j) =
 				tolower(*(mtd_part[i].name + j));
-		strsep(&(mtd_part[i].name), delimiter);
 		mtd_part[i].offset      = pentry->offset;
 		mtd_part[i].mask_flags  = pentry->attr;
 		mtd_part[i].size        = pentry->length;
@@ -3465,16 +3473,22 @@
 	err = msm_nand_bus_register(pdev, info);
 	if (err)
 		goto out;
-	info->clk_data.qpic_clk = devm_clk_get(&pdev->dev, "core_clk");
-	if (!IS_ERR_OR_NULL(info->clk_data.qpic_clk)) {
-		err = clk_set_rate(info->clk_data.qpic_clk,
-			MSM_NAND_BUS_VOTE_MAX_RATE);
-	} else {
-		err = PTR_ERR(info->clk_data.qpic_clk);
-		pr_err("Failed to get clock handle, err=%d\n", err);
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,qpic-clk-rpmh"))
+		info->clk_data.rpmh_clk = true;
+
+	if (!info->clk_data.rpmh_clk) {
+		info->clk_data.qpic_clk = devm_clk_get(&pdev->dev, "core_clk");
+		if (!IS_ERR_OR_NULL(info->clk_data.qpic_clk)) {
+			err = clk_set_rate(info->clk_data.qpic_clk,
+				MSM_NAND_BUS_VOTE_MAX_RATE);
+		} else {
+			err = PTR_ERR(info->clk_data.qpic_clk);
+			pr_err("Failed to get clock handle, err=%d\n", err);
+		}
+		if (err)
+			goto bus_unregister;
 	}
-	if (err)
-		goto bus_unregister;
 
 	err = msm_nand_setup_clocks_and_bus_bw(info, true);
 	if (err)
diff --git a/drivers/mtd/devices/msm_qpic_nand.h b/drivers/mtd/devices/msm_qpic_nand.h
index 9b6701c..043c215 100644
--- a/drivers/mtd/devices/msm_qpic_nand.h
+++ b/drivers/mtd/devices/msm_qpic_nand.h
@@ -295,6 +295,7 @@
 	uint32_t client_handle;
 	atomic_t clk_enabled;
 	atomic_t curr_vote;
+	bool rpmh_clk;
 };
 
 /* Structure that defines NANDc private data. */
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index d7f724b..0c84ee8 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -877,6 +877,8 @@
 	}
 }
 
+#define MXC_V1_ECCBYTES		5
+
 static int mxc_v1_ooblayout_ecc(struct mtd_info *mtd, int section,
 				struct mtd_oob_region *oobregion)
 {
@@ -886,7 +888,7 @@
 		return -ERANGE;
 
 	oobregion->offset = (section * 16) + 6;
-	oobregion->length = nand_chip->ecc.bytes;
+	oobregion->length = MXC_V1_ECCBYTES;
 
 	return 0;
 }
@@ -908,8 +910,7 @@
 			oobregion->length = 4;
 		}
 	} else {
-		oobregion->offset = ((section - 1) * 16) +
-				    nand_chip->ecc.bytes + 6;
+		oobregion->offset = ((section - 1) * 16) + MXC_V1_ECCBYTES + 6;
 		if (section < nand_chip->ecc.steps)
 			oobregion->length = (section * 16) + 6 -
 					    oobregion->offset;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index f222f8a..31a6ee3 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -64,8 +64,14 @@
 
 	if (!section) {
 		oobregion->offset = 0;
-		oobregion->length = 4;
+		if (mtd->oobsize == 16)
+			oobregion->length = 4;
+		else
+			oobregion->length = 3;
 	} else {
+		if (mtd->oobsize == 8)
+			return -ERANGE;
+
 		oobregion->offset = 6;
 		oobregion->length = ecc->total - 4;
 	}
@@ -1081,7 +1087,9 @@
 	 * Ensure the timing mode has been changed on the chip side
 	 * before changing timings on the controller side.
 	 */
-	if (chip->onfi_version) {
+	if (chip->onfi_version &&
+	    (le16_to_cpu(chip->onfi_params.opt_cmd) &
+	     ONFI_OPT_CMD_SET_GET_FEATURES)) {
 		u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = {
 			chip->onfi_timing_mode_default,
 		};
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2af9869..c821cca 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -52,6 +52,12 @@
 		{ .id = {0xad, 0xde, 0x94, 0xda, 0x74, 0xc4} },
 		  SZ_8K, SZ_8K, SZ_2M, NAND_NEED_SCRAMBLING, 6, 640,
 		  NAND_ECC_INFO(40, SZ_1K), 4 },
+	{"MT29F8G08ABBCAH4 8G 3.3V 8-bit",
+		{ .id = {0x2c, 0xa3, 0x90, 0x26, 0x00, 0x00, 0x00, 0x00} },
+		SZ_4K, SZ_1K, SZ_256K, 0, 4, 224, NAND_ECC_INFO(8, SZ_512)},
+	{"TC58NYG2S0HBAI4 4G 1.8V 8-bit",
+		{ .id = {0x98, 0xac, 0x90, 0x26, 0x76, 0x00, 0x00, 0x00} },
+		SZ_4K, SZ_512, SZ_256K, 0, 5, 256, NAND_ECC_INFO(8, SZ_512) },
 
 	LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
 	LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
diff --git a/drivers/mtd/nand/qcom_nandc.c b/drivers/mtd/nand/qcom_nandc.c
index 57d483a..6f0fd15 100644
--- a/drivers/mtd/nand/qcom_nandc.c
+++ b/drivers/mtd/nand/qcom_nandc.c
@@ -109,7 +109,11 @@
 #define	READ_ADDR			0
 
 /* NAND_DEV_CMD_VLD bits */
-#define	READ_START_VLD			0
+#define	READ_START_VLD			BIT(0)
+#define	READ_STOP_VLD			BIT(1)
+#define	WRITE_START_VLD			BIT(2)
+#define	ERASE_START_VLD			BIT(3)
+#define	SEQ_READ_START_VLD		BIT(4)
 
 /* NAND_EBI2_ECC_BUF_CFG bits */
 #define	NUM_STEPS			0
@@ -148,6 +152,10 @@
 #define	FETCH_ID			0xb
 #define	RESET_DEVICE			0xd
 
+/* Default Value for NAND_DEV_CMD_VLD */
+#define NAND_DEV_CMD_VLD_VAL		(READ_START_VLD | WRITE_START_VLD | \
+					 ERASE_START_VLD | SEQ_READ_START_VLD)
+
 /*
  * the NAND controller performs reads/writes with ECC in 516 byte chunks.
  * the driver calls the chunks 'step' or 'codeword' interchangeably
@@ -672,8 +680,7 @@
 
 	/* configure CMD1 and VLD for ONFI param probing */
 	nandc_set_reg(nandc, NAND_DEV_CMD_VLD,
-		      (nandc->vld & ~(1 << READ_START_VLD))
-		      | 0 << READ_START_VLD);
+		      (nandc->vld & ~READ_START_VLD));
 	nandc_set_reg(nandc, NAND_DEV_CMD1,
 		      (nandc->cmd1 & ~(0xFF << READ_ADDR))
 		      | NAND_CMD_PARAM << READ_ADDR);
@@ -1893,7 +1900,7 @@
 				| wide_bus << WIDE_FLASH
 				| 1 << DEV0_CFG1_ECC_DISABLE;
 
-	host->ecc_bch_cfg = host->bch_enabled << ECC_CFG_ECC_DISABLE
+	host->ecc_bch_cfg = !host->bch_enabled << ECC_CFG_ECC_DISABLE
 				| 0 << ECC_SW_RESET
 				| host->cw_data << ECC_NUM_DATA_BYTES
 				| 1 << ECC_FORCE_CLK_OPEN
@@ -1972,13 +1979,14 @@
 {
 	/* kill onenand */
 	nandc_write(nandc, SFLASHC_BURST_CFG, 0);
+	nandc_write(nandc, NAND_DEV_CMD_VLD, NAND_DEV_CMD_VLD_VAL);
 
 	/* enable ADM DMA */
 	nandc_write(nandc, NAND_FLASH_CHIP_SELECT, DM_EN);
 
 	/* save the original values of these registers */
 	nandc->cmd1 = nandc_read(nandc, NAND_DEV_CMD1);
-	nandc->vld = nandc_read(nandc, NAND_DEV_CMD_VLD);
+	nandc->vld = NAND_DEV_CMD_VLD_VAL;
 
 	return 0;
 }
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 8b8470c..f9b2a77 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -320,6 +320,10 @@
 
 		ret = wait_for_completion_timeout(&nfc->complete,
 						msecs_to_jiffies(timeout_ms));
+		if (!ret)
+			ret = -ETIMEDOUT;
+		else
+			ret = 0;
 
 		writel(0, nfc->regs + NFC_REG_INT);
 	} else {
diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c
index cf7c189..d065c0e 100644
--- a/drivers/net/can/c_can/c_can_pci.c
+++ b/drivers/net/can/c_can/c_can_pci.c
@@ -178,7 +178,6 @@
 		break;
 	case BOSCH_D_CAN:
 		priv->regs = reg_map_d_can;
-		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
 		break;
 	default:
 		ret = -EINVAL;
diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c
index e36d105..717530e 100644
--- a/drivers/net/can/c_can/c_can_platform.c
+++ b/drivers/net/can/c_can/c_can_platform.c
@@ -320,7 +320,6 @@
 		break;
 	case BOSCH_D_CAN:
 		priv->regs = reg_map_d_can;
-		priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES;
 		priv->read_reg = c_can_plat_read_reg_aligned_to_16bit;
 		priv->write_reg = c_can_plat_write_reg_aligned_to_16bit;
 		priv->read_reg32 = d_can_plat_read_reg32;
diff --git a/drivers/net/can/ifi_canfd/ifi_canfd.c b/drivers/net/can/ifi_canfd/ifi_canfd.c
index 481895b..c06ef43 100644
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c
@@ -670,9 +670,9 @@
 	       priv->base + IFI_CANFD_FTIME);
 
 	/* Configure transmitter delay */
-	tdc = (dbt->brp * (dbt->phase_seg1 + 1)) & IFI_CANFD_TDELAY_MASK;
-	writel(IFI_CANFD_TDELAY_EN | IFI_CANFD_TDELAY_ABS | tdc,
-	       priv->base + IFI_CANFD_TDELAY);
+	tdc = dbt->brp * (dbt->prop_seg + dbt->phase_seg1);
+	tdc &= IFI_CANFD_TDELAY_MASK;
+	writel(IFI_CANFD_TDELAY_EN | tdc, priv->base + IFI_CANFD_TDELAY);
 }
 
 static void ifi_canfd_set_filter(struct net_device *ndev, const u32 id,
diff --git a/drivers/net/can/sun4i_can.c b/drivers/net/can/sun4i_can.c
index 68ef0a4..1ac2090 100644
--- a/drivers/net/can/sun4i_can.c
+++ b/drivers/net/can/sun4i_can.c
@@ -342,7 +342,7 @@
 
 	/* enter the selected mode */
 	mod_reg_val = readl(priv->base + SUN4I_REG_MSEL_ADDR);
-	if (priv->can.ctrlmode & CAN_CTRLMODE_PRESUME_ACK)
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
 		mod_reg_val |= SUN4I_MSEL_LOOPBACK_MODE;
 	else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
 		mod_reg_val |= SUN4I_MSEL_LISTEN_ONLY_MODE;
@@ -539,6 +539,13 @@
 		}
 		stats->rx_over_errors++;
 		stats->rx_errors++;
+
+		/* reset the CAN IP by entering reset mode
+		 * ignoring timeout error
+		 */
+		set_reset_mode(dev);
+		set_normal_mode(dev);
+
 		/* clear bit */
 		sun4i_can_write_cmdreg(priv, SUN4I_CMD_CLEAR_OR_FLAG);
 	}
@@ -653,8 +660,9 @@
 			netif_wake_queue(dev);
 			can_led_event(dev, CAN_LED_EVENT_TX);
 		}
-		if (isrc & SUN4I_INT_RBUF_VLD) {
-			/* receive interrupt */
+		if ((isrc & SUN4I_INT_RBUF_VLD) &&
+		    !(isrc & SUN4I_INT_DATA_OR)) {
+			/* receive interrupt - don't read if overrun occurred */
 			while (status & SUN4I_STA_RBUF_RDY) {
 				/* RX buffer is not empty */
 				sun4i_can_rx(dev);
@@ -811,7 +819,6 @@
 	priv->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING |
 				       CAN_CTRLMODE_LISTENONLY |
 				       CAN_CTRLMODE_LOOPBACK |
-				       CAN_CTRLMODE_PRESUME_ACK |
 				       CAN_CTRLMODE_3_SAMPLES;
 	priv->base = addr;
 	priv->clk = clk;
diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c
index be928ce..9fdb0f0 100644
--- a/drivers/net/can/usb/esd_usb2.c
+++ b/drivers/net/can/usb/esd_usb2.c
@@ -333,7 +333,7 @@
 		}
 
 		cf->can_id = id & ESD_IDMASK;
-		cf->can_dlc = get_can_dlc(msg->msg.rx.dlc);
+		cf->can_dlc = get_can_dlc(msg->msg.rx.dlc & ~ESD_RTR);
 
 		if (id & ESD_EXTID)
 			cf->can_id |= CAN_EFF_FLAG;
diff --git a/drivers/net/can/usb/gs_usb.c b/drivers/net/can/usb/gs_usb.c
index 05369dc..eea9aea 100644
--- a/drivers/net/can/usb/gs_usb.c
+++ b/drivers/net/can/usb/gs_usb.c
@@ -375,6 +375,8 @@
 
 		gs_free_tx_context(txc);
 
+		atomic_dec(&dev->active_tx_urbs);
+
 		netif_wake_queue(netdev);
 	}
 
@@ -463,14 +465,6 @@
 			  urb->transfer_buffer_length,
 			  urb->transfer_buffer,
 			  urb->transfer_dma);
-
-	atomic_dec(&dev->active_tx_urbs);
-
-	if (!netif_device_present(netdev))
-		return;
-
-	if (netif_queue_stopped(netdev))
-		netif_wake_queue(netdev);
 }
 
 static netdev_tx_t gs_can_start_xmit(struct sk_buff *skb,
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
index d51e0c4..4224e06 100644
--- a/drivers/net/can/usb/kvaser_usb.c
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -137,6 +137,7 @@
 #define CMD_RESET_ERROR_COUNTER		49
 #define CMD_TX_ACKNOWLEDGE		50
 #define CMD_CAN_ERROR_EVENT		51
+#define CMD_FLUSH_QUEUE_REPLY		68
 
 #define CMD_LEAF_USB_THROTTLE		77
 #define CMD_LEAF_LOG_MESSAGE		106
@@ -1301,6 +1302,11 @@
 			goto warn;
 		break;
 
+	case CMD_FLUSH_QUEUE_REPLY:
+		if (dev->family != KVASER_LEAF)
+			goto warn;
+		break;
+
 	default:
 warn:		dev_warn(dev->udev->dev.parent,
 			 "Unhandled message (%d)\n", msg->id);
@@ -1609,7 +1615,8 @@
 	if (err)
 		netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
 
-	if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+	err = kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel);
+	if (err)
 		netdev_warn(netdev, "Cannot reset card, error %d\n", err);
 
 	err = kvaser_usb_stop_chip(priv);
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 947adda..c26debc 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -326,6 +326,7 @@
 
 static void b53_set_forwarding(struct b53_device *dev, int enable)
 {
+	struct dsa_switch *ds = dev->ds;
 	u8 mgmt;
 
 	b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
@@ -336,6 +337,15 @@
 		mgmt &= ~SM_SW_FWD_EN;
 
 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+
+	/* Include IMP port in dumb forwarding mode when no tagging protocol is
+	 * set
+	 */
+	if (ds->ops->get_tag_protocol(ds) == DSA_TAG_PROTO_NONE) {
+		b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, &mgmt);
+		mgmt |= B53_MII_DUMB_FWDG_EN;
+		b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt);
+	}
 }
 
 static void b53_enable_vlan(struct b53_device *dev, bool enable)
@@ -1558,6 +1568,7 @@
 		.dev_name = "BCM53125",
 		.vlans = 4096,
 		.enabled_ports = 0xff,
+		.arl_entries = 4,
 		.cpu_port = B53_CPU_PORT,
 		.vta_regs = B53_VTA_REGS,
 		.duplex_reg = B53_DUPLEX_STAT_GE,
diff --git a/drivers/net/dsa/b53/b53_regs.h b/drivers/net/dsa/b53/b53_regs.h
index dac0af4..8104400 100644
--- a/drivers/net/dsa/b53/b53_regs.h
+++ b/drivers/net/dsa/b53/b53_regs.h
@@ -104,6 +104,10 @@
 #define  B53_UC_FWD_EN			BIT(6)
 #define  B53_MC_FWD_EN			BIT(7)
 
+/* Switch control (8 bit) */
+#define B53_SWITCH_CTRL			0x22
+#define  B53_MII_DUMB_FWDG_EN		BIT(6)
+
 /* (16 bit) */
 #define B53_UC_FLOOD_MASK		0x32
 #define B53_MC_FLOOD_MASK		0x34
diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c
index 3066d9c..e2512ab 100644
--- a/drivers/net/ethernet/amazon/ena/ena_com.c
+++ b/drivers/net/ethernet/amazon/ena/ena_com.c
@@ -36,9 +36,9 @@
 /*****************************************************************************/
 
 /* Timeout in micro-sec */
-#define ADMIN_CMD_TIMEOUT_US (1000000)
+#define ADMIN_CMD_TIMEOUT_US (3000000)
 
-#define ENA_ASYNC_QUEUE_DEPTH 4
+#define ENA_ASYNC_QUEUE_DEPTH 16
 #define ENA_ADMIN_QUEUE_DEPTH 32
 
 #define MIN_ENA_VER (((ENA_COMMON_SPEC_VERSION_MAJOR) << \
diff --git a/drivers/net/ethernet/amazon/ena/ena_netdev.h b/drivers/net/ethernet/amazon/ena/ena_netdev.h
index 69d7e9e..c5eaf76 100644
--- a/drivers/net/ethernet/amazon/ena/ena_netdev.h
+++ b/drivers/net/ethernet/amazon/ena/ena_netdev.h
@@ -100,7 +100,7 @@
 /* Number of queues to check for missing queues per timer service */
 #define ENA_MONITORED_TX_QUEUES	4
 /* Max timeout packets before device reset */
-#define MAX_NUM_OF_TIMEOUTED_PACKETS 32
+#define MAX_NUM_OF_TIMEOUTED_PACKETS 128
 
 #define ENA_TX_RING_IDX_NEXT(idx, ring_size) (((idx) + 1) & ((ring_size) - 1))
 
@@ -116,9 +116,9 @@
 #define ENA_IO_IRQ_IDX(q)		(ENA_IO_IRQ_FIRST_IDX + (q))
 
 /* ENA device should send keep alive msg every 1 sec.
- * We wait for 3 sec just to be on the safe side.
+ * We wait for 6 sec just to be on the safe side.
  */
-#define ENA_DEVICE_KALIVE_TIMEOUT	(3 * HZ)
+#define ENA_DEVICE_KALIVE_TIMEOUT	(6 * HZ)
 
 #define ENA_MMIO_DISABLE_REG_READ	BIT(0)
 
diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c
index e078d8d..29d29af 100644
--- a/drivers/net/ethernet/aurora/nb8800.c
+++ b/drivers/net/ethernet/aurora/nb8800.c
@@ -609,7 +609,7 @@
 		mac_mode |= HALF_DUPLEX;
 
 	if (gigabit) {
-		if (priv->phy_mode == PHY_INTERFACE_MODE_RGMII)
+		if (phy_interface_is_rgmii(dev->phydev))
 			mac_mode |= RGMII_MODE;
 
 		mac_mode |= GMAC_MODE;
@@ -1277,11 +1277,10 @@
 		break;
 
 	case PHY_INTERFACE_MODE_RGMII:
-		pad_mode = PAD_MODE_RGMII;
-		break;
-
+	case PHY_INTERFACE_MODE_RGMII_ID:
+	case PHY_INTERFACE_MODE_RGMII_RXID:
 	case PHY_INTERFACE_MODE_RGMII_TXID:
-		pad_mode = PAD_MODE_RGMII | PAD_MODE_GTX_CLK_DELAY;
+		pad_mode = PAD_MODE_RGMII;
 		break;
 
 	default:
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 20e569b..333df54 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -97,6 +97,8 @@
 	BCM57407_NPAR,
 	BCM57414_NPAR,
 	BCM57416_NPAR,
+	BCM57452,
+	BCM57454,
 	NETXTREME_E_VF,
 	NETXTREME_C_VF,
 };
@@ -131,6 +133,8 @@
 	{ "Broadcom BCM57407 NetXtreme-E Ethernet Partition" },
 	{ "Broadcom BCM57414 NetXtreme-E Ethernet Partition" },
 	{ "Broadcom BCM57416 NetXtreme-E Ethernet Partition" },
+	{ "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" },
+	{ "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" },
 	{ "Broadcom NetXtreme-E Ethernet Virtual Function" },
 	{ "Broadcom NetXtreme-C Ethernet Virtual Function" },
 };
@@ -166,6 +170,8 @@
 	{ PCI_VDEVICE(BROADCOM, 0x16ed), .driver_data = BCM57414_NPAR },
 	{ PCI_VDEVICE(BROADCOM, 0x16ee), .driver_data = BCM57416_NPAR },
 	{ PCI_VDEVICE(BROADCOM, 0x16ef), .driver_data = BCM57416_NPAR },
+	{ PCI_VDEVICE(BROADCOM, 0x16f1), .driver_data = BCM57452 },
+	{ PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 },
 #ifdef CONFIG_BNXT_SRIOV
 	{ PCI_VDEVICE(BROADCOM, 0x16c1), .driver_data = NETXTREME_E_VF },
 	{ PCI_VDEVICE(BROADCOM, 0x16cb), .driver_data = NETXTREME_C_VF },
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index a927a73..edae2dc 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -8720,11 +8720,14 @@
 	tg3_mem_rx_release(tp);
 	tg3_mem_tx_release(tp);
 
+	/* Protect tg3_get_stats64() from reading freed tp->hw_stats. */
+	tg3_full_lock(tp, 0);
 	if (tp->hw_stats) {
 		dma_free_coherent(&tp->pdev->dev, sizeof(struct tg3_hw_stats),
 				  tp->hw_stats, tp->stats_mapping);
 		tp->hw_stats = NULL;
 	}
+	tg3_full_unlock(tp);
 }
 
 /*
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index e813951..9e073fb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -317,12 +317,12 @@
 
 	if (v != MBOX_OWNER_DRV) {
 		ret = (v == MBOX_OWNER_FW) ? -EBUSY : -ETIMEDOUT;
-		t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
+		t4_record_mbox(adap, cmd, size, access, ret);
 		return ret;
 	}
 
 	/* Copy in the new mailbox command and send it on its way ... */
-	t4_record_mbox(adap, cmd, MBOX_LEN, access, 0);
+	t4_record_mbox(adap, cmd, size, access, 0);
 	for (i = 0; i < size; i += 8)
 		t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++));
 
@@ -371,7 +371,7 @@
 	}
 
 	ret = (pcie_fw & PCIE_FW_ERR_F) ? -ENXIO : -ETIMEDOUT;
-	t4_record_mbox(adap, cmd, MBOX_LEN, access, ret);
+	t4_record_mbox(adap, cmd, size, access, ret);
 	dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n",
 		*(const u8 *)cmd, mbox);
 	t4_report_fw_error(adap);
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
index 736db9d..81021f8 100644
--- a/drivers/net/ethernet/freescale/fman/mac.c
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -622,6 +622,9 @@
 		goto no_mem;
 	}
 
+	pdev->dev.of_node = node;
+	pdev->dev.parent = priv->dev;
+
 	ret = platform_device_add_data(pdev, &data, sizeof(data));
 	if (ret)
 		goto err;
diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index 3f4e711..fd20688 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -3690,7 +3690,7 @@
 		u32 tempval1 = gfar_read(&regs->maccfg1);
 		u32 tempval = gfar_read(&regs->maccfg2);
 		u32 ecntrl = gfar_read(&regs->ecntrl);
-		u32 tx_flow_oldval = (tempval & MACCFG1_TX_FLOW);
+		u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW);
 
 		if (phydev->duplex != priv->oldduplex) {
 			if (!(phydev->duplex))
diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c
index aaf6fec..3660a3d 100644
--- a/drivers/net/ethernet/ibm/emac/mal.c
+++ b/drivers/net/ethernet/ibm/emac/mal.c
@@ -402,7 +402,7 @@
 	unsigned long flags;
 
 	MAL_DBG2(mal, "poll(%d)" NL, budget);
- again:
+
 	/* Process TX skbs */
 	list_for_each(l, &mal->poll_list) {
 		struct mal_commac *mc =
@@ -451,7 +451,6 @@
 			spin_lock_irqsave(&mal->lock, flags);
 			mal_disable_eob_irq(mal);
 			spin_unlock_irqrestore(&mal->lock, flags);
-			goto again;
 		}
 		mc->ops->poll_tx(mc->dev);
 	}
diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c
index 9f2184b..b8778e7 100644
--- a/drivers/net/ethernet/ibm/ibmvnic.c
+++ b/drivers/net/ethernet/ibm/ibmvnic.c
@@ -1253,6 +1253,7 @@
 				release_sub_crq_queue(adapter,
 						      adapter->tx_scrq[i]);
 			}
+		kfree(adapter->tx_scrq);
 		adapter->tx_scrq = NULL;
 	}
 
@@ -1265,6 +1266,7 @@
 				release_sub_crq_queue(adapter,
 						      adapter->rx_scrq[i]);
 			}
+		kfree(adapter->rx_scrq);
 		adapter->rx_scrq = NULL;
 	}
 
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 9affd7c..6a62447 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -7882,6 +7882,11 @@
 		pci_enable_wake(pdev, PCI_D3hot, 0);
 		pci_enable_wake(pdev, PCI_D3cold, 0);
 
+		/* In case of PCI error, adapter lose its HW address
+		 * so we should re-assign it here.
+		 */
+		hw->hw_addr = adapter->io_addr;
+
 		igb_reset(adapter);
 		wr32(E1000_WUS, ~0);
 		result = PCI_ERS_RESULT_RECOVERED;
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c
index 707bc46..6ea10a9 100644
--- a/drivers/net/ethernet/marvell/mvneta.c
+++ b/drivers/net/ethernet/marvell/mvneta.c
@@ -28,6 +28,7 @@
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include <linux/phy.h>
+#include <linux/phy_fixed.h>
 #include <linux/platform_device.h>
 #include <linux/skbuff.h>
 #include <net/hwbm.h>
diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
index 0a4e81a..ed6fae9 100644
--- a/drivers/net/ethernet/marvell/mvpp2.c
+++ b/drivers/net/ethernet/marvell/mvpp2.c
@@ -4413,13 +4413,12 @@
 		struct mvpp2_txq_pcpu_buf *tx_buf =
 			txq_pcpu->buffs + txq_pcpu->txq_get_index;
 
-		mvpp2_txq_inc_get(txq_pcpu);
-
 		dma_unmap_single(port->dev->dev.parent, tx_buf->phys,
 				 tx_buf->size, DMA_TO_DEVICE);
-		if (!tx_buf->skb)
-			continue;
-		dev_kfree_skb_any(tx_buf->skb);
+		if (tx_buf->skb)
+			dev_kfree_skb_any(tx_buf->skb);
+
+		mvpp2_txq_inc_get(txq_pcpu);
 	}
 }
 
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
index a5fc46b..d4d97ca 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c
@@ -88,10 +88,17 @@
 	}
 }
 
+#define MLX4_EN_WRAP_AROUND_SEC	10UL
+/* By scheduling the overflow check every 5 seconds, we have a reasonably
+ * good chance we wont miss a wrap around.
+ * TOTO: Use a timer instead of a work queue to increase the guarantee.
+ */
+#define MLX4_EN_OVERFLOW_PERIOD (MLX4_EN_WRAP_AROUND_SEC * HZ / 2)
+
 void mlx4_en_ptp_overflow_check(struct mlx4_en_dev *mdev)
 {
 	bool timeout = time_is_before_jiffies(mdev->last_overflow_check +
-					      mdev->overflow_period);
+					      MLX4_EN_OVERFLOW_PERIOD);
 	unsigned long flags;
 
 	if (timeout) {
@@ -236,7 +243,6 @@
 	.enable		= mlx4_en_phc_enable,
 };
 
-#define MLX4_EN_WRAP_AROUND_SEC	10ULL
 
 /* This function calculates the max shift that enables the user range
  * of MLX4_EN_WRAP_AROUND_SEC values in the cycles register.
@@ -261,7 +267,6 @@
 {
 	struct mlx4_dev *dev = mdev->dev;
 	unsigned long flags;
-	u64 ns, zero = 0;
 
 	/* mlx4_en_init_timestamp is called for each netdev.
 	 * mdev->ptp_clock is common for all ports, skip initialization if
@@ -285,13 +290,6 @@
 			 ktime_to_ns(ktime_get_real()));
 	write_unlock_irqrestore(&mdev->clock_lock, flags);
 
-	/* Calculate period in seconds to call the overflow watchdog - to make
-	 * sure counter is checked at least once every wrap around.
-	 */
-	ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero);
-	do_div(ns, NSEC_PER_SEC / 2 / HZ);
-	mdev->overflow_period = ns;
-
 	/* Configure the PHC */
 	mdev->ptp_clock_info = mlx4_en_ptp_clock_info;
 	snprintf(mdev->ptp_clock_info.name, 16, "mlx4 ptp");
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index 5d48458..bcbb80f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -724,16 +724,21 @@
  * header, the HW adds it. To address that, we are subtracting the pseudo
  * header checksum from the checksum value provided by the HW.
  */
-static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb,
-				struct iphdr *iph)
+static int get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb,
+			       struct iphdr *iph)
 {
 	__u16 length_for_csum = 0;
 	__wsum csum_pseudo_header = 0;
+	__u8 ipproto = iph->protocol;
+
+	if (unlikely(ipproto == IPPROTO_SCTP))
+		return -1;
 
 	length_for_csum = (be16_to_cpu(iph->tot_len) - (iph->ihl << 2));
 	csum_pseudo_header = csum_tcpudp_nofold(iph->saddr, iph->daddr,
-						length_for_csum, iph->protocol, 0);
+						length_for_csum, ipproto, 0);
 	skb->csum = csum_sub(hw_checksum, csum_pseudo_header);
+	return 0;
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
@@ -744,17 +749,20 @@
 static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb,
 			       struct ipv6hdr *ipv6h)
 {
+	__u8 nexthdr = ipv6h->nexthdr;
 	__wsum csum_pseudo_hdr = 0;
 
-	if (unlikely(ipv6h->nexthdr == IPPROTO_FRAGMENT ||
-		     ipv6h->nexthdr == IPPROTO_HOPOPTS))
+	if (unlikely(nexthdr == IPPROTO_FRAGMENT ||
+		     nexthdr == IPPROTO_HOPOPTS ||
+		     nexthdr == IPPROTO_SCTP))
 		return -1;
-	hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(ipv6h->nexthdr));
+	hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(nexthdr));
 
 	csum_pseudo_hdr = csum_partial(&ipv6h->saddr,
 				       sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0);
 	csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ipv6h->payload_len);
-	csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ntohs(ipv6h->nexthdr));
+	csum_pseudo_hdr = csum_add(csum_pseudo_hdr,
+				   (__force __wsum)htons(nexthdr));
 
 	skb->csum = csum_sub(hw_checksum, csum_pseudo_hdr);
 	skb->csum = csum_add(skb->csum, csum_partial(ipv6h, sizeof(struct ipv6hdr), 0));
@@ -777,11 +785,10 @@
 	}
 
 	if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4))
-		get_fixed_ipv4_csum(hw_checksum, skb, hdr);
+		return get_fixed_ipv4_csum(hw_checksum, skb, hdr);
 #if IS_ENABLED(CONFIG_IPV6)
-	else if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6))
-		if (unlikely(get_fixed_ipv6_csum(hw_checksum, skb, hdr)))
-			return -1;
+	if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6))
+		return get_fixed_ipv6_csum(hw_checksum, skb, hdr);
 #endif
 	return 0;
 }
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 551786f..727122d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -430,7 +430,7 @@
 		/* Virtual PCI function needs to determine UAR page size from
 		 * firmware. Only master PCI function can set the uar page size
 		 */
-		if (enable_4k_uar)
+		if (enable_4k_uar || !dev->persist->num_vfs)
 			dev->uar_page_shift = DEFAULT_UAR_PAGE_SHIFT;
 		else
 			dev->uar_page_shift = PAGE_SHIFT;
@@ -841,8 +841,6 @@
 		return -ENOSYS;
 	}
 
-	mlx4_log_num_mgm_entry_size = hca_param.log_mc_entry_sz;
-
 	dev->caps.hca_core_clock = hca_param.hca_core_clock;
 
 	memset(&dev_cap, 0, sizeof(dev_cap));
@@ -2269,7 +2267,7 @@
 
 		dev->caps.max_fmr_maps = (1 << (32 - ilog2(dev->caps.num_mpts))) - 1;
 
-		if (enable_4k_uar) {
+		if (enable_4k_uar || !dev->persist->num_vfs) {
 			init_hca.log_uar_sz = ilog2(dev->caps.num_uars) +
 						    PAGE_SHIFT - DEFAULT_UAR_PAGE_SHIFT;
 			init_hca.uar_page_sz = DEFAULT_UAR_PAGE_SHIFT - 12;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index a3528dd..df0f396 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -419,7 +419,6 @@
 	struct cyclecounter	cycles;
 	struct timecounter	clock;
 	unsigned long		last_overflow_check;
-	unsigned long		overflow_period;
 	struct ptp_clock	*ptp_clock;
 	struct ptp_clock_info	ptp_clock_info;
 	struct notifier_block	nb;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
index cb45390..f7fabec 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c
@@ -770,6 +770,10 @@
 	mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
 }
 
+static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg);
+static void mlx5_free_cmd_msg(struct mlx5_core_dev *dev,
+			      struct mlx5_cmd_msg *msg);
+
 static void cmd_work_handler(struct work_struct *work)
 {
 	struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work);
@@ -779,16 +783,27 @@
 	struct mlx5_cmd_layout *lay;
 	struct semaphore *sem;
 	unsigned long flags;
+	int alloc_ret;
 
 	sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem;
 	down(sem);
 	if (!ent->page_queue) {
-		ent->idx = alloc_ent(cmd);
-		if (ent->idx < 0) {
+		alloc_ret = alloc_ent(cmd);
+		if (alloc_ret < 0) {
+			if (ent->callback) {
+				ent->callback(-EAGAIN, ent->context);
+				mlx5_free_cmd_msg(dev, ent->out);
+				free_msg(dev, ent->in);
+				free_cmd(ent);
+			} else {
+				ent->ret = -EAGAIN;
+				complete(&ent->done);
+			}
 			mlx5_core_err(dev, "failed to allocate command entry\n");
 			up(sem);
 			return;
 		}
+		ent->idx = alloc_ret;
 	} else {
 		ent->idx = cmd->max_reg_cmds;
 		spin_lock_irqsave(&cmd->alloc_lock, flags);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
index 13dc388..1612ec0 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c
@@ -62,12 +62,14 @@
 	struct delayed_work *dwork = to_delayed_work(work);
 	struct mlx5e_tstamp *tstamp = container_of(dwork, struct mlx5e_tstamp,
 						   overflow_work);
+	struct mlx5e_priv *priv = container_of(tstamp, struct mlx5e_priv, tstamp);
 	unsigned long flags;
 
 	write_lock_irqsave(&tstamp->lock, flags);
 	timecounter_read(&tstamp->clock);
 	write_unlock_irqrestore(&tstamp->lock, flags);
-	schedule_delayed_work(&tstamp->overflow_work, tstamp->overflow_period);
+	queue_delayed_work(priv->wq, &tstamp->overflow_work,
+			   msecs_to_jiffies(tstamp->overflow_period * 1000));
 }
 
 int mlx5e_hwstamp_set(struct net_device *dev, struct ifreq *ifr)
@@ -263,7 +265,7 @@
 
 	INIT_DELAYED_WORK(&tstamp->overflow_work, mlx5e_timestamp_overflow);
 	if (tstamp->overflow_period)
-		schedule_delayed_work(&tstamp->overflow_work, 0);
+		queue_delayed_work(priv->wq, &tstamp->overflow_work, 0);
 	else
 		mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n");
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
index e034dbc..cf070fc 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c
@@ -276,7 +276,7 @@
 
 static bool outer_header_zero(u32 *match_criteria)
 {
-	int size = MLX5_ST_SZ_BYTES(fte_match_param);
+	int size = MLX5_FLD_SZ_BYTES(fte_match_param, outer_headers);
 	char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria,
 					     outer_headers);
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
index 6ffd5d2..52a3810 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c
@@ -651,9 +651,14 @@
 	int vport;
 	int err;
 
+	/* disable PF RoCE so missed packets don't go through RoCE steering */
+	mlx5_dev_list_lock();
+	mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+	mlx5_dev_list_unlock();
+
 	err = esw_create_offloads_fdb_table(esw, nvports);
 	if (err)
-		return err;
+		goto create_fdb_err;
 
 	err = esw_create_offloads_table(esw);
 	if (err)
@@ -673,11 +678,6 @@
 			goto err_reps;
 	}
 
-	/* disable PF RoCE so missed packets don't go through RoCE steering */
-	mlx5_dev_list_lock();
-	mlx5_remove_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
-	mlx5_dev_list_unlock();
-
 	return 0;
 
 err_reps:
@@ -694,6 +694,13 @@
 
 create_ft_err:
 	esw_destroy_offloads_fdb_table(esw);
+
+create_fdb_err:
+	/* enable back PF RoCE */
+	mlx5_dev_list_lock();
+	mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+	mlx5_dev_list_unlock();
+
 	return err;
 }
 
@@ -701,11 +708,6 @@
 {
 	int err, err1, num_vfs = esw->dev->priv.sriov.num_vfs;
 
-	/* enable back PF RoCE */
-	mlx5_dev_list_lock();
-	mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
-	mlx5_dev_list_unlock();
-
 	mlx5_eswitch_disable_sriov(esw);
 	err = mlx5_eswitch_enable_sriov(esw, num_vfs, SRIOV_LEGACY);
 	if (err) {
@@ -715,6 +717,11 @@
 			esw_warn(esw->dev, "Failed setting eswitch back to offloads, err %d\n", err);
 	}
 
+	/* enable back PF RoCE */
+	mlx5_dev_list_lock();
+	mlx5_add_dev_by_protocol(esw->dev, MLX5_INTERFACE_PROTOCOL_IB);
+	mlx5_dev_list_unlock();
+
 	return err;
 }
 
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag.c
index b5d5519..0ca4623 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/lag.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/lag.c
@@ -157,22 +157,17 @@
 static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker,
 					   u8 *port1, u8 *port2)
 {
-	if (tracker->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) {
-		if (tracker->netdev_state[0].tx_enabled) {
-			*port1 = 1;
-			*port2 = 1;
-		} else {
-			*port1 = 2;
-			*port2 = 2;
-		}
-	} else {
-		*port1 = 1;
-		*port2 = 2;
-		if (!tracker->netdev_state[0].link_up)
-			*port1 = 2;
-		else if (!tracker->netdev_state[1].link_up)
-			*port2 = 1;
+	*port1 = 1;
+	*port2 = 2;
+	if (!tracker->netdev_state[0].tx_enabled ||
+	    !tracker->netdev_state[0].link_up) {
+		*port1 = 2;
+		return;
 	}
+
+	if (!tracker->netdev_state[1].tx_enabled ||
+	    !tracker->netdev_state[1].link_up)
+		*port2 = 1;
 }
 
 static void mlx5_activate_lag(struct mlx5_lag *ldev,
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index f902c4d..d50350c 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
@@ -249,15 +249,14 @@
 }
 
 static struct mlxsw_sp_span_entry *
-mlxsw_sp_span_entry_find(struct mlxsw_sp_port *port)
+mlxsw_sp_span_entry_find(struct mlxsw_sp *mlxsw_sp, u8 local_port)
 {
-	struct mlxsw_sp *mlxsw_sp = port->mlxsw_sp;
 	int i;
 
 	for (i = 0; i < mlxsw_sp->span.entries_count; i++) {
 		struct mlxsw_sp_span_entry *curr = &mlxsw_sp->span.entries[i];
 
-		if (curr->used && curr->local_port == port->local_port)
+		if (curr->used && curr->local_port == local_port)
 			return curr;
 	}
 	return NULL;
@@ -268,7 +267,8 @@
 {
 	struct mlxsw_sp_span_entry *span_entry;
 
-	span_entry = mlxsw_sp_span_entry_find(port);
+	span_entry = mlxsw_sp_span_entry_find(port->mlxsw_sp,
+					      port->local_port);
 	if (span_entry) {
 		/* Already exists, just take a reference */
 		span_entry->ref_count++;
@@ -453,12 +453,13 @@
 }
 
 static void mlxsw_sp_span_mirror_remove(struct mlxsw_sp_port *from,
-					struct mlxsw_sp_port *to,
+					u8 destination_port,
 					enum mlxsw_sp_span_type type)
 {
 	struct mlxsw_sp_span_entry *span_entry;
 
-	span_entry = mlxsw_sp_span_entry_find(to);
+	span_entry = mlxsw_sp_span_entry_find(from->mlxsw_sp,
+					      destination_port);
 	if (!span_entry) {
 		netdev_err(from->dev, "no span entry found\n");
 		return;
@@ -1255,10 +1256,8 @@
 static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port,
 					   struct tc_cls_matchall_offload *cls)
 {
-	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 	struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry;
 	enum mlxsw_sp_span_type span_type;
-	struct mlxsw_sp_port *to_port;
 
 	mall_tc_entry = mlxsw_sp_port_mirror_entry_find(mlxsw_sp_port,
 							cls->cookie);
@@ -1269,11 +1268,12 @@
 
 	switch (mall_tc_entry->type) {
 	case MLXSW_SP_PORT_MALL_MIRROR:
-		to_port = mlxsw_sp->ports[mall_tc_entry->mirror.to_local_port];
 		span_type = mall_tc_entry->mirror.ingress ?
 				MLXSW_SP_SPAN_INGRESS : MLXSW_SP_SPAN_EGRESS;
 
-		mlxsw_sp_span_mirror_remove(mlxsw_sp_port, to_port, span_type);
+		mlxsw_sp_span_mirror_remove(mlxsw_sp_port,
+					    mall_tc_entry->mirror.to_local_port,
+					    span_type);
 		break;
 	default:
 		WARN_ON(1);
@@ -4172,6 +4172,8 @@
 			return -EINVAL;
 		if (!info->linking)
 			break;
+		if (netdev_has_any_upper_dev(upper_dev))
+			return -EINVAL;
 		/* HW limitation forbids to put ports to multiple bridges. */
 		if (netif_is_bridge_master(upper_dev) &&
 		    !mlxsw_sp_master_bridge_check(mlxsw_sp, upper_dev))
@@ -4185,6 +4187,10 @@
 		if (netif_is_lag_port(dev) && is_vlan_dev(upper_dev) &&
 		    !netif_is_lag_master(vlan_dev_real_dev(upper_dev)))
 			return -EINVAL;
+		if (!info->linking)
+			break;
+		if (netdev_has_any_upper_dev(upper_dev))
+			return -EINVAL;
 		break;
 	case NETDEV_CHANGEUPPER:
 		upper_dev = info->upper_dev;
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
index aee3fd2..4ca82bd 100644
--- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
+++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c
@@ -871,8 +871,7 @@
 	return NETDEV_TX_OK;
 
 err_unmap:
-	--f;
-	while (f >= 0) {
+	while (--f >= 0) {
 		frag = &skb_shinfo(skb)->frags[f];
 		dma_unmap_page(&nn->pdev->dev,
 			       tx_ring->txbufs[wr_idx].dma_addr,
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h
index 653bb57..433f8be 100644
--- a/drivers/net/ethernet/qlogic/qed/qed.h
+++ b/drivers/net/ethernet/qlogic/qed/qed.h
@@ -642,7 +642,9 @@
 #define OOO_LB_TC 9
 
 int qed_configure_vport_wfq(struct qed_dev *cdev, u16 vp_id, u32 rate);
-void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate);
+void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev,
+					 struct qed_ptt *p_ptt,
+					 u32 min_pf_rate);
 
 void qed_clean_wfq_db(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
 #define QED_LEADING_HWFN(dev)   (&dev->hwfns[0])
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
index a4789a9..9d59cb8 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dcbx.c
@@ -1222,7 +1222,7 @@
 {
 	struct qed_dcbx_get *dcbx_info;
 
-	dcbx_info = kzalloc(sizeof(*dcbx_info), GFP_KERNEL);
+	dcbx_info = kmalloc(sizeof(*dcbx_info), GFP_ATOMIC);
 	if (!dcbx_info)
 		return NULL;
 
diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c
index edae5fc..afe5e57 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_dev.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c
@@ -877,7 +877,7 @@
 		/* Either EDPM is mandatory, or we are attempting to allocate a
 		 * WID per CPU.
 		 */
-		n_cpus = num_active_cpus();
+		n_cpus = num_present_cpus();
 		rc = qed_hw_init_dpi_size(p_hwfn, p_ptt, pwm_regsize, n_cpus);
 	}
 
@@ -2732,7 +2732,8 @@
 }
 
 /* API to configure WFQ from mcp link change */
-void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate)
+void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev,
+					 struct qed_ptt *p_ptt, u32 min_pf_rate)
 {
 	int i;
 
@@ -2746,8 +2747,7 @@
 	for_each_hwfn(cdev, i) {
 		struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
 
-		__qed_configure_vp_wfq_on_link_change(p_hwfn,
-						      p_hwfn->p_dpc_ptt,
+		__qed_configure_vp_wfq_on_link_change(p_hwfn, p_ptt,
 						      min_pf_rate);
 	}
 }
diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
index bdc9ba9..8b7d2f9 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c
@@ -628,7 +628,8 @@
 
 	/* Min bandwidth configuration */
 	__qed_configure_pf_min_bandwidth(p_hwfn, p_ptt, p_link, min_bw);
-	qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_link->min_pf_rate);
+	qed_configure_vp_wfq_on_link_change(p_hwfn->cdev, p_ptt,
+					    p_link->min_pf_rate);
 
 	p_link->an = !!(status & LINK_STATUS_AUTO_NEGOTIATE_ENABLED);
 	p_link->an_complete = !!(status &
diff --git a/drivers/net/ethernet/qlogic/qed/qed_roce.c b/drivers/net/ethernet/qlogic/qed/qed_roce.c
index f3a825a..d9dcb0d 100644
--- a/drivers/net/ethernet/qlogic/qed/qed_roce.c
+++ b/drivers/net/ethernet/qlogic/qed/qed_roce.c
@@ -1766,13 +1766,13 @@
 	if (rc)
 		goto err_resp;
 
-	dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
-			  p_resp_ramrod_res, resp_ramrod_res_phys);
-
 	out_params->rq_psn = le32_to_cpu(p_resp_ramrod_res->psn);
 	rq_err_state = GET_FIELD(le32_to_cpu(p_resp_ramrod_res->err_flag),
 				 ROCE_QUERY_QP_RESP_OUTPUT_PARAMS_ERROR_FLG);
 
+	dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_resp_ramrod_res),
+			  p_resp_ramrod_res, resp_ramrod_res_phys);
+
 	if (!(qp->req_offloaded)) {
 		/* Don't send query qp for the requester */
 		out_params->sq_psn = qp->sq_psn;
@@ -1813,9 +1813,6 @@
 	if (rc)
 		goto err_req;
 
-	dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
-			  p_req_ramrod_res, req_ramrod_res_phys);
-
 	out_params->sq_psn = le32_to_cpu(p_req_ramrod_res->psn);
 	sq_err_state = GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
 				 ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_ERR_FLG);
@@ -1823,6 +1820,9 @@
 		GET_FIELD(le32_to_cpu(p_req_ramrod_res->flags),
 			  ROCE_QUERY_QP_REQ_OUTPUT_PARAMS_SQ_DRAINING_FLG);
 
+	dma_free_coherent(&p_hwfn->cdev->pdev->dev, sizeof(*p_req_ramrod_res),
+			  p_req_ramrod_res, req_ramrod_res_phys);
+
 	out_params->draining = false;
 
 	if (rq_err_state)
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
index 7567cc4..634e414 100644
--- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
+++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
@@ -1221,7 +1221,7 @@
 	struct qede_rx_queue *rxq = NULL;
 	struct sw_rx_data *sw_rx_data;
 	union eth_rx_cqe *cqe;
-	int i, rc = 0;
+	int i, iter, rc = 0;
 	u8 *data_ptr;
 
 	for_each_queue(i) {
@@ -1240,7 +1240,7 @@
 	 * enabled. This is because the queue 0 is configured as the default
 	 * queue and that the loopback traffic is not IP.
 	 */
-	for (i = 0; i < QEDE_SELFTEST_POLL_COUNT; i++) {
+	for (iter = 0; iter < QEDE_SELFTEST_POLL_COUNT; iter++) {
 		if (!qede_has_rx_work(rxq)) {
 			usleep_range(100, 200);
 			continue;
@@ -1287,7 +1287,7 @@
 		qed_chain_recycle_consumed(&rxq->rx_comp_ring);
 	}
 
-	if (i == QEDE_SELFTEST_POLL_COUNT) {
+	if (iter == QEDE_SELFTEST_POLL_COUNT) {
 		DP_NOTICE(edev, "Failed to receive the traffic\n");
 		return -1;
 	}
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
index 829be21..be258d9 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c
@@ -724,7 +724,7 @@
 	seg_hdr->cookie = MPI_COREDUMP_COOKIE;
 	seg_hdr->segNum = seg_number;
 	seg_hdr->segSize = seg_size;
-	memcpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1);
+	strncpy(seg_hdr->description, desc, (sizeof(seg_hdr->description)) - 1);
 }
 
 /*
diff --git a/drivers/net/ethernet/qualcomm/emac/emac-mac.c b/drivers/net/ethernet/qualcomm/emac/emac-mac.c
index 0b4deb3..f683bfb 100644
--- a/drivers/net/ethernet/qualcomm/emac/emac-mac.c
+++ b/drivers/net/ethernet/qualcomm/emac/emac-mac.c
@@ -932,7 +932,8 @@
 
 		curr_rxbuf->dma_addr =
 			dma_map_single(adpt->netdev->dev.parent, skb->data,
-				       curr_rxbuf->length, DMA_FROM_DEVICE);
+				       adpt->rxbuf_size, DMA_FROM_DEVICE);
+
 		ret = dma_mapping_error(adpt->netdev->dev.parent,
 					curr_rxbuf->dma_addr);
 		if (ret) {
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 12be259..2140ded 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -574,6 +574,7 @@
 	.rpadir_value   = 2 << 16,
 	.no_trimd	= 1,
 	.no_ade		= 1,
+	.hw_crc		= 1,
 	.tsu		= 1,
 	.select_mii	= 1,
 	.shift_rd0	= 1,
@@ -802,7 +803,7 @@
 
 	.ecsr_value	= ECSR_ICD | ECSR_MPD,
 	.ecsipr_value	= ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
-	.eesipr_value	= DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
+	.eesipr_value	= DMAC_M_RFRMER | DMAC_M_ECI | 0x003f07ff,
 
 	.tx_check	= EESR_TC1 | EESR_FTC,
 	.eesr_err_check	= EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
@@ -832,7 +833,7 @@
 
 	.ecsr_value	= ECSR_ICD | ECSR_MPD,
 	.ecsipr_value	= ECSIPR_LCHNGIP | ECSIPR_ICDIP | ECSIPR_MPDIP,
-	.eesipr_value	= DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff,
+	.eesipr_value	= DMAC_M_RFRMER | DMAC_M_ECI | 0x003f07ff,
 
 	.tx_check	= EESR_TC1 | EESR_FTC,
 	.eesr_err_check	= EESR_TWB1 | EESR_TWB | EESR_TABT | EESR_RABT |
diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index d050f37..5024280 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -339,7 +339,7 @@
 	ECMR_DPAD = 0x00200000, ECMR_RZPF = 0x00100000,
 	ECMR_ZPF = 0x00080000, ECMR_PFR = 0x00040000, ECMR_RXF = 0x00020000,
 	ECMR_TXF = 0x00010000, ECMR_MCT = 0x00002000, ECMR_PRCEF = 0x00001000,
-	ECMR_PMDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020,
+	ECMR_MPDE = 0x00000200, ECMR_RE = 0x00000040, ECMR_TE = 0x00000020,
 	ECMR_RTM = 0x00000010, ECMR_ILB = 0x00000008, ECMR_ELB = 0x00000004,
 	ECMR_DM = 0x00000002, ECMR_PRM = 0x00000001,
 };
diff --git a/drivers/net/ethernet/rocker/rocker_tlv.h b/drivers/net/ethernet/rocker/rocker_tlv.h
index a63ef82..dfae3c9 100644
--- a/drivers/net/ethernet/rocker/rocker_tlv.h
+++ b/drivers/net/ethernet/rocker/rocker_tlv.h
@@ -139,40 +139,52 @@
 int rocker_tlv_put(struct rocker_desc_info *desc_info,
 		   int attrtype, int attrlen, const void *data);
 
-static inline int rocker_tlv_put_u8(struct rocker_desc_info *desc_info,
-				    int attrtype, u8 value)
+static inline int
+rocker_tlv_put_u8(struct rocker_desc_info *desc_info, int attrtype, u8 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &value);
+	u8 tmp = value; /* work around GCC PR81715 */
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &tmp);
 }
 
-static inline int rocker_tlv_put_u16(struct rocker_desc_info *desc_info,
-				     int attrtype, u16 value)
+static inline int
+rocker_tlv_put_u16(struct rocker_desc_info *desc_info, int attrtype, u16 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &value);
+	u16 tmp = value;
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &tmp);
 }
 
-static inline int rocker_tlv_put_be16(struct rocker_desc_info *desc_info,
-				      int attrtype, __be16 value)
+static inline int
+rocker_tlv_put_be16(struct rocker_desc_info *desc_info, int attrtype, __be16 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &value);
+	__be16 tmp = value;
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &tmp);
 }
 
-static inline int rocker_tlv_put_u32(struct rocker_desc_info *desc_info,
-				     int attrtype, u32 value)
+static inline int
+rocker_tlv_put_u32(struct rocker_desc_info *desc_info, int attrtype, u32 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &value);
+	u32 tmp = value;
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &tmp);
 }
 
-static inline int rocker_tlv_put_be32(struct rocker_desc_info *desc_info,
-				      int attrtype, __be32 value)
+static inline int
+rocker_tlv_put_be32(struct rocker_desc_info *desc_info, int attrtype, __be32 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &value);
+	__be32 tmp = value;
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &tmp);
 }
 
-static inline int rocker_tlv_put_u64(struct rocker_desc_info *desc_info,
-				     int attrtype, u64 value)
+static inline int
+rocker_tlv_put_u64(struct rocker_desc_info *desc_info, int attrtype, u64 value)
 {
-	return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &value);
+	u64 tmp = value;
+
+	return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &tmp);
 }
 
 static inline struct rocker_tlv *
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index c4ada72..1d85109 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -197,11 +197,15 @@
 	nic_data->datapath_caps =
 		MCDI_DWORD(outbuf, GET_CAPABILITIES_OUT_FLAGS1);
 
-	if (outlen >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN)
+	if (outlen >= MC_CMD_GET_CAPABILITIES_V2_OUT_LEN) {
 		nic_data->datapath_caps2 = MCDI_DWORD(outbuf,
 				GET_CAPABILITIES_V2_OUT_FLAGS2);
-	else
+		nic_data->piobuf_size = MCDI_WORD(outbuf,
+				GET_CAPABILITIES_V2_OUT_SIZE_PIO_BUFF);
+	} else {
 		nic_data->datapath_caps2 = 0;
+		nic_data->piobuf_size = ER_DZ_TX_PIOBUF_SIZE;
+	}
 
 	/* record the DPCPU firmware IDs to determine VEB vswitching support.
 	 */
@@ -825,8 +829,8 @@
 			offset = ((efx->tx_channel_offset + efx->n_tx_channels -
 				   tx_queue->channel->channel - 1) *
 				  efx_piobuf_size);
-			index = offset / ER_DZ_TX_PIOBUF_SIZE;
-			offset = offset % ER_DZ_TX_PIOBUF_SIZE;
+			index = offset / nic_data->piobuf_size;
+			offset = offset % nic_data->piobuf_size;
 
 			/* When the host page size is 4K, the first
 			 * host page in the WC mapping may be within
@@ -1161,11 +1165,11 @@
 	 * functions of the controller.
 	 */
 	if (efx_piobuf_size != 0 &&
-	    ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
+	    nic_data->piobuf_size / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >=
 	    efx->n_tx_channels) {
 		unsigned int n_piobufs =
 			DIV_ROUND_UP(efx->n_tx_channels,
-				     ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size);
+				     nic_data->piobuf_size / efx_piobuf_size);
 
 		rc = efx_ef10_alloc_piobufs(efx, n_piobufs);
 		if (rc)
diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h
index 73bee7e..73028f2 100644
--- a/drivers/net/ethernet/sfc/nic.h
+++ b/drivers/net/ethernet/sfc/nic.h
@@ -500,6 +500,7 @@
  * @pio_write_base: Base address for writing PIO buffers
  * @pio_write_vi_base: Relative VI number for @pio_write_base
  * @piobuf_handle: Handle of each PIO buffer allocated
+ * @piobuf_size: size of a single PIO buffer
  * @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC
  *	reboot
  * @rx_rss_context: Firmware handle for our RSS context
@@ -537,6 +538,7 @@
 	void __iomem *wc_membase, *pio_write_base;
 	unsigned int pio_write_vi_base;
 	unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT];
+	u16 piobuf_size;
 	bool must_restore_piobufs;
 	u32 rx_rss_context;
 	bool rx_rss_context_exclusive;
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 2337789..6f26acd 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -27,7 +27,6 @@
 
 #ifdef EFX_USE_PIO
 
-#define EFX_PIOBUF_SIZE_MAX ER_DZ_TX_PIOBUF_SIZE
 #define EFX_PIOBUF_SIZE_DEF ALIGN(256, L1_CACHE_BYTES)
 unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF;
 
diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c
index ff038e5..36a04e1 100644
--- a/drivers/net/hyperv/netvsc_drv.c
+++ b/drivers/net/hyperv/netvsc_drv.c
@@ -1084,7 +1084,12 @@
 	bool notify = false, reschedule = false;
 	unsigned long flags, next_reconfig, delay;
 
-	rtnl_lock();
+	/* if changes are happening, comeback later */
+	if (!rtnl_trylock()) {
+		schedule_delayed_work(&ndev_ctx->dwork, LINKCHANGE_INT);
+		return;
+	}
+
 	if (ndev_ctx->start_remove)
 		goto out_unlock;
 
diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c
index bca6a1e..e1bb802 100644
--- a/drivers/net/irda/mcs7780.c
+++ b/drivers/net/irda/mcs7780.c
@@ -141,9 +141,19 @@
 static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val)
 {
 	struct usb_device *dev = mcs->usbdev;
-	int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
-				  MCS_RD_RTYPE, 0, reg, val, 2,
-				  msecs_to_jiffies(MCS_CTRL_TIMEOUT));
+	void *dmabuf;
+	int ret;
+
+	dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL);
+	if (!dmabuf)
+		return -ENOMEM;
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+			      MCS_RD_RTYPE, 0, reg, dmabuf, 2,
+			      msecs_to_jiffies(MCS_CTRL_TIMEOUT));
+
+	memcpy(val, dmabuf, sizeof(__u16));
+	kfree(dmabuf);
 
 	return ret;
 }
diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
index a5d66e2..2caac0c 100644
--- a/drivers/net/macsec.c
+++ b/drivers/net/macsec.c
@@ -3510,6 +3510,7 @@
 module_exit(macsec_exit);
 
 MODULE_ALIAS_RTNL_LINK("macsec");
+MODULE_ALIAS_GENL_FAMILY("macsec");
 
 MODULE_DESCRIPTION("MACsec IEEE 802.1AE");
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index 4cad955..8f84961 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -29,9 +29,11 @@
 #define MII_DP83867_MICR	0x12
 #define MII_DP83867_ISR		0x13
 #define DP83867_CTRL		0x1f
+#define DP83867_CFG3		0x1e
 
 /* Extended Registers */
 #define DP83867_RGMIICTL	0x0032
+#define DP83867_STRAP_STS1	0x006E
 #define DP83867_RGMIIDCTL	0x0086
 
 #define DP83867_SW_RESET	BIT(15)
@@ -55,9 +57,13 @@
 #define DP83867_RGMII_TX_CLK_DELAY_EN		BIT(1)
 #define DP83867_RGMII_RX_CLK_DELAY_EN		BIT(0)
 
+/* STRAP_STS1 bits */
+#define DP83867_STRAP_STS1_RESERVED		BIT(11)
+
 /* PHY CTRL bits */
 #define DP83867_PHYCR_FIFO_DEPTH_SHIFT		14
 #define DP83867_PHYCR_FIFO_DEPTH_MASK		(3 << 14)
+#define DP83867_PHYCR_RESERVED_MASK		BIT(11)
 
 /* RGMIIDCTL bits */
 #define DP83867_RGMII_TX_CLK_DELAY_SHIFT	4
@@ -90,6 +96,8 @@
 		micr_status |=
 			(MII_DP83867_MICR_AN_ERR_INT_EN |
 			MII_DP83867_MICR_SPEED_CHNG_INT_EN |
+			MII_DP83867_MICR_AUTONEG_COMP_INT_EN |
+			MII_DP83867_MICR_LINK_STS_CHNG_INT_EN |
 			MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN |
 			MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN);
 
@@ -138,7 +146,7 @@
 static int dp83867_config_init(struct phy_device *phydev)
 {
 	struct dp83867_private *dp83867;
-	int ret, val;
+	int ret, val, bs;
 	u16 delay;
 
 	if (!phydev->priv) {
@@ -161,6 +169,22 @@
 			return val;
 		val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK;
 		val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT);
+
+		/* The code below checks if "port mirroring" N/A MODE4 has been
+		 * enabled during power on bootstrap.
+		 *
+		 * Such N/A mode enabled by mistake can put PHY IC in some
+		 * internal testing mode and disable RGMII transmission.
+		 *
+		 * In this particular case one needs to check STRAP_STS1
+		 * register's bit 11 (marked as RESERVED).
+		 */
+
+		bs = phy_read_mmd_indirect(phydev, DP83867_STRAP_STS1,
+					   DP83867_DEVADDR);
+		if (bs & DP83867_STRAP_STS1_RESERVED)
+			val &= ~DP83867_PHYCR_RESERVED_MASK;
+
 		ret = phy_write(phydev, MII_DP83867_PHYCTRL, val);
 		if (ret)
 			return ret;
@@ -190,6 +214,13 @@
 				       DP83867_DEVADDR, delay);
 	}
 
+	/* Enable Interrupt output INT_OE in CFG3 register */
+	if (phy_interrupt_is_valid(phydev)) {
+		val = phy_read(phydev, DP83867_CFG3);
+		val |= BIT(7);
+		phy_write(phydev, DP83867_CFG3, val);
+	}
+
 	return 0;
 }
 
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index edd30eb..6e12401 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1060,6 +1060,15 @@
 			if (old_link != phydev->link)
 				phydev->state = PHY_CHANGELINK;
 		}
+		/*
+		 * Failsafe: check that nobody set phydev->link=0 between two
+		 * poll cycles, otherwise we won't leave RUNNING state as long
+		 * as link remains down.
+		 */
+		if (!phydev->link && phydev->state == PHY_RUNNING) {
+			phydev->state = PHY_CHANGELINK;
+			phydev_err(phydev, "no link in PHY_RUNNING\n");
+		}
 		break;
 	case PHY_CHANGELINK:
 		err = phy_read_status(phydev);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 9e7b783..bf02f8e 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1714,6 +1714,8 @@
 {
 	struct phy_device *phydev = to_phy_device(dev);
 
+	cancel_delayed_work_sync(&phydev->state_queue);
+
 	mutex_lock(&phydev->lock);
 	phydev->state = PHY_DOWN;
 	mutex_unlock(&phydev->lock);
diff --git a/drivers/net/phy/xilinx_gmii2rgmii.c b/drivers/net/phy/xilinx_gmii2rgmii.c
index d15dd39..2e5150b 100644
--- a/drivers/net/phy/xilinx_gmii2rgmii.c
+++ b/drivers/net/phy/xilinx_gmii2rgmii.c
@@ -44,7 +44,7 @@
 	priv->phy_drv->read_status(phydev);
 
 	val = mdiobus_read(phydev->mdio.bus, priv->addr, XILINX_GMII2RGMII_REG);
-	val &= XILINX_GMII2RGMII_SPEED_MASK;
+	val &= ~XILINX_GMII2RGMII_SPEED_MASK;
 
 	if (phydev->speed == SPEED_1000)
 		val |= BMCR_SPEED1000;
diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c
index 5489c0e..96fa0e6 100644
--- a/drivers/net/ppp/ppp_generic.c
+++ b/drivers/net/ppp/ppp_generic.c
@@ -119,6 +119,7 @@
 	int		n_channels;	/* how many channels are attached 54 */
 	spinlock_t	rlock;		/* lock for receive side 58 */
 	spinlock_t	wlock;		/* lock for transmit side 5c */
+	int		*xmit_recursion __percpu; /* xmit recursion detect */
 	int		mru;		/* max receive unit 60 */
 	unsigned int	flags;		/* control bits 64 */
 	unsigned int	xstate;		/* transmit state bits 68 */
@@ -1024,6 +1025,7 @@
 	struct ppp *ppp = netdev_priv(dev);
 	int indx;
 	int err;
+	int cpu;
 
 	ppp->dev = dev;
 	ppp->ppp_net = src_net;
@@ -1038,6 +1040,15 @@
 	INIT_LIST_HEAD(&ppp->channels);
 	spin_lock_init(&ppp->rlock);
 	spin_lock_init(&ppp->wlock);
+
+	ppp->xmit_recursion = alloc_percpu(int);
+	if (!ppp->xmit_recursion) {
+		err = -ENOMEM;
+		goto err1;
+	}
+	for_each_possible_cpu(cpu)
+		(*per_cpu_ptr(ppp->xmit_recursion, cpu)) = 0;
+
 #ifdef CONFIG_PPP_MULTILINK
 	ppp->minseq = -1;
 	skb_queue_head_init(&ppp->mrq);
@@ -1049,11 +1060,15 @@
 
 	err = ppp_unit_register(ppp, conf->unit, conf->ifname_is_set);
 	if (err < 0)
-		return err;
+		goto err2;
 
 	conf->file->private_data = &ppp->file;
 
 	return 0;
+err2:
+	free_percpu(ppp->xmit_recursion);
+err1:
+	return err;
 }
 
 static const struct nla_policy ppp_nl_policy[IFLA_PPP_MAX + 1] = {
@@ -1399,18 +1414,16 @@
 	ppp_xmit_unlock(ppp);
 }
 
-static DEFINE_PER_CPU(int, ppp_xmit_recursion);
-
 static void ppp_xmit_process(struct ppp *ppp)
 {
 	local_bh_disable();
 
-	if (unlikely(__this_cpu_read(ppp_xmit_recursion)))
+	if (unlikely(*this_cpu_ptr(ppp->xmit_recursion)))
 		goto err;
 
-	__this_cpu_inc(ppp_xmit_recursion);
+	(*this_cpu_ptr(ppp->xmit_recursion))++;
 	__ppp_xmit_process(ppp);
-	__this_cpu_dec(ppp_xmit_recursion);
+	(*this_cpu_ptr(ppp->xmit_recursion))--;
 
 	local_bh_enable();
 
@@ -1901,23 +1914,23 @@
 	spin_unlock_bh(&pch->downl);
 	/* see if there is anything from the attached unit to be sent */
 	if (skb_queue_empty(&pch->file.xq)) {
-		read_lock_bh(&pch->upl);
 		ppp = pch->ppp;
 		if (ppp)
 			__ppp_xmit_process(ppp);
-		read_unlock_bh(&pch->upl);
 	}
 }
 
 static void ppp_channel_push(struct channel *pch)
 {
-	local_bh_disable();
-
-	__this_cpu_inc(ppp_xmit_recursion);
-	__ppp_channel_push(pch);
-	__this_cpu_dec(ppp_xmit_recursion);
-
-	local_bh_enable();
+	read_lock_bh(&pch->upl);
+	if (pch->ppp) {
+		(*this_cpu_ptr(pch->ppp->xmit_recursion))++;
+		__ppp_channel_push(pch);
+		(*this_cpu_ptr(pch->ppp->xmit_recursion))--;
+	} else {
+		__ppp_channel_push(pch);
+	}
+	read_unlock_bh(&pch->upl);
 }
 
 /*
@@ -3056,6 +3069,7 @@
 #endif /* CONFIG_PPP_FILTER */
 
 	kfree_skb(ppp->xmit_pending);
+	free_percpu(ppp->xmit_recursion);
 
 	free_netdev(ppp->dev);
 }
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index a380649..2668170 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -2366,8 +2366,10 @@
 
 	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
 			  TEAM_CMD_OPTIONS_GET);
-	if (!hdr)
+	if (!hdr) {
+		nlmsg_free(skb);
 		return -EMSGSIZE;
+	}
 
 	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
 		goto nla_put_failure;
@@ -2639,8 +2641,10 @@
 
 	hdr = genlmsg_put(skb, portid, seq, &team_nl_family, flags | NLM_F_MULTI,
 			  TEAM_CMD_PORT_LIST_GET);
-	if (!hdr)
+	if (!hdr) {
+		nlmsg_free(skb);
 		return -EMSGSIZE;
+	}
 
 	if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
 		goto nla_put_failure;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 35aa28b..7e5ae26 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1283,11 +1283,13 @@
 	switch (tun->flags & TUN_TYPE_MASK) {
 	case IFF_TUN:
 		if (tun->flags & IFF_NO_PI) {
-			switch (skb->data[0] & 0xf0) {
-			case 0x40:
+			u8 ip_version = skb->len ? (skb->data[0] >> 4) : 0;
+
+			switch (ip_version) {
+			case 4:
 				pi.proto = htons(ETH_P_IP);
 				break;
-			case 0x60:
+			case 6:
 				pi.proto = htons(ETH_P_IPV6);
 				break;
 			default:
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index cdde590..3a72862 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -364,7 +364,7 @@
 	  optionally with LEDs that indicate traffic
 
 config USB_NET_PLUSB
-	tristate "Prolific PL-2301/2302/25A1 based cables"
+	tristate "Prolific PL-2301/2302/25A1/27A1 based cables"
 	# if the handshake/init/reset problems, from original 'plusb',
 	# are ever resolved ... then remove "experimental"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index afbfc0f..dc6d3b0 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -769,8 +769,10 @@
 	u8 *buf;
 	int len;
 	int temp;
+	int err;
 	u8 iface_no;
 	struct usb_cdc_parsed_header hdr;
+	u16 curr_ntb_format;
 
 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
 	if (!ctx)
@@ -875,6 +877,32 @@
 		goto error2;
 	}
 
+	/*
+	 * Some Huawei devices have been observed to come out of reset in NDP32 mode.
+	 * Let's check if this is the case, and set the device to NDP16 mode again if
+	 * needed.
+	*/
+	if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) {
+		err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT,
+				      USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE,
+				      0, iface_no, &curr_ntb_format, 2);
+		if (err < 0) {
+			goto error2;
+		}
+
+		if (curr_ntb_format == USB_CDC_NCM_NTB32_FORMAT) {
+			dev_info(&intf->dev, "resetting NTB format to 16-bit");
+			err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT,
+					       USB_TYPE_CLASS | USB_DIR_OUT
+					       | USB_RECIP_INTERFACE,
+					       USB_CDC_NCM_NTB16_FORMAT,
+					       iface_no, NULL, 0);
+
+			if (err < 0)
+				goto error2;
+		}
+	}
+
 	cdc_ncm_find_endpoints(dev, ctx->data);
 	cdc_ncm_find_endpoints(dev, ctx->control);
 	if (!dev->in || !dev->out || !dev->status) {
diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c
index 2680a65..63f28908 100644
--- a/drivers/net/usb/huawei_cdc_ncm.c
+++ b/drivers/net/usb/huawei_cdc_ncm.c
@@ -80,6 +80,12 @@
 	 * be at the end of the frame.
 	 */
 	drvflags |= CDC_NCM_FLAG_NDP_TO_END;
+
+	/* Additionally, it has been reported that some Huawei E3372H devices, with
+	 * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence
+	 * needing to be set to the NTB16 one again.
+	 */
+	drvflags |= CDC_NCM_FLAG_RESET_NTB16;
 	ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags);
 	if (ret)
 		goto err;
diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
index 22e1a9a..6fe5937 100644
--- a/drivers/net/usb/plusb.c
+++ b/drivers/net/usb/plusb.c
@@ -102,7 +102,7 @@
 }
 
 static const struct driver_info	prolific_info = {
-	.description =	"Prolific PL-2301/PL-2302/PL-25A1",
+	.description =	"Prolific PL-2301/PL-2302/PL-25A1/PL-27A1",
 	.flags =	FLAG_POINTTOPOINT | FLAG_NO_SETINT,
 		/* some PL-2302 versions seem to fail usb_set_interface() */
 	.reset =	pl_reset,
@@ -139,6 +139,17 @@
 					 * Host-to-Host Cable
 					 */
 	.driver_info =  (unsigned long) &prolific_info,
+
+},
+
+/* super speed cables */
+{
+	USB_DEVICE(0x067b, 0x27a1),     /* PL-27A1, no eeprom
+					 * also: goobay Active USB 3.0
+					 * Data Link,
+					 * Unitek Y-3501
+					 */
+	.driver_info =  (unsigned long) &prolific_info,
 },
 
 	{ },		// END
@@ -158,5 +169,5 @@
 module_usb_driver(plusb_driver);
 
 MODULE_AUTHOR("David Brownell");
-MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1 USB Host to Host Link Driver");
+MODULE_DESCRIPTION("Prolific PL-2301/2302/25A1/27A1 USB Host to Host Link Driver");
 MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 2f260c6..49a27dc 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -876,6 +876,7 @@
 	{QMI_FIXED_INTF(0x19d2, 0x1428, 2)},	/* Telewell TW-LTE 4G v2 */
 	{QMI_FIXED_INTF(0x19d2, 0x2002, 4)},	/* ZTE (Vodafone) K3765-Z */
 	{QMI_FIXED_INTF(0x2001, 0x7e19, 4)},	/* D-Link DWM-221 B1 */
+	{QMI_FIXED_INTF(0x2001, 0x7e35, 4)},	/* D-Link DWM-222 */
 	{QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)},    /* Sierra Wireless MC7700 */
 	{QMI_FIXED_INTF(0x114f, 0x68a2, 8)},    /* Sierra Wireless MC7750 */
 	{QMI_FIXED_INTF(0x1199, 0x68a2, 8)},	/* Sierra Wireless MC7710 in QMI mode */
diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c
index 766c63b..45226db 100644
--- a/drivers/net/wireless/ath/ath10k/ahb.c
+++ b/drivers/net/wireless/ath/ath10k/ahb.c
@@ -33,6 +33,9 @@
 
 MODULE_DEVICE_TABLE(of, ath10k_ahb_of_match);
 
+#define QCA4019_SRAM_ADDR      0x000C0000
+#define QCA4019_SRAM_LEN       0x00040000 /* 256 kb */
+
 static inline struct ath10k_ahb *ath10k_ahb_priv(struct ath10k *ar)
 {
 	return &((struct ath10k_pci *)ar->drv_priv)->ahb[0];
@@ -699,6 +702,25 @@
 	return ret;
 }
 
+static u32 ath10k_ahb_qca4019_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
+{
+	u32 val = 0, region = addr & 0xfffff;
+
+	val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS);
+
+	if (region >= QCA4019_SRAM_ADDR && region <=
+	    (QCA4019_SRAM_ADDR + QCA4019_SRAM_LEN)) {
+		/* SRAM contents for QCA4019 can be directly accessed and
+		 * no conversions are required
+		 */
+		val |= region;
+	} else {
+		val |= 0x100000 | region;
+	}
+
+	return val;
+}
+
 static const struct ath10k_hif_ops ath10k_ahb_hif_ops = {
 	.tx_sg                  = ath10k_pci_hif_tx_sg,
 	.diag_read              = ath10k_pci_hif_diag_read,
@@ -766,6 +788,7 @@
 	ar_pci->mem_len = ar_ahb->mem_len;
 	ar_pci->ar = ar;
 	ar_pci->bus_ops = &ath10k_ahb_bus_ops;
+	ar_pci->targ_cpu_to_ce_addr = ath10k_ahb_qca4019_targ_cpu_to_ce_addr;
 
 	ret = ath10k_pci_setup_resource(ar);
 	if (ret) {
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 972b5e2..366d3dc 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -1852,6 +1852,12 @@
 		goto err_wmi_detach;
 	}
 
+	/* If firmware indicates Full Rx Reorder support it must be used in a
+	 * slightly different manner. Let HTT code know.
+	 */
+	ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
+						ar->wmi.svc_map));
+
 	status = ath10k_htt_rx_alloc(&ar->htt);
 	if (status) {
 		ath10k_err(ar, "failed to alloc htt rx: %d\n", status);
@@ -1964,12 +1970,6 @@
 		}
 	}
 
-	/* If firmware indicates Full Rx Reorder support it must be used in a
-	 * slightly different manner. Let HTT code know.
-	 */
-	ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
-						ar->wmi.svc_map));
-
 	status = ath10k_htt_rx_ring_refill(ar);
 	if (status) {
 		ath10k_err(ar, "failed to refill htt rx ring: %d\n", status);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index be5b527..90c0c4a 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -314,6 +314,7 @@
 	struct ieee80211_vif *vif;
 	struct ieee80211_sta *sta;
 
+	bool removed;
 	int vdev_id;
 	u8 addr[ETH_ALEN];
 	DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index f2e85eb..30e98af 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3738,6 +3738,9 @@
 	if (!peer)
 		return NULL;
 
+	if (peer->removed)
+		return NULL;
+
 	if (peer->sta)
 		return peer->sta->txq[tid];
 	else if (peer->vif)
@@ -7422,6 +7425,20 @@
 	return 0;
 }
 
+static void ath10k_mac_op_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+					     struct ieee80211_vif *vif,
+					     struct ieee80211_sta *sta)
+{
+	struct ath10k *ar;
+	struct ath10k_peer *peer;
+
+	ar = hw->priv;
+
+	list_for_each_entry(peer, &ar->peers, list)
+		if (peer->sta == sta)
+			peer->removed = true;
+}
+
 static const struct ieee80211_ops ath10k_ops = {
 	.tx				= ath10k_mac_op_tx,
 	.wake_tx_queue			= ath10k_mac_op_wake_tx_queue,
@@ -7462,6 +7479,7 @@
 	.assign_vif_chanctx		= ath10k_mac_op_assign_vif_chanctx,
 	.unassign_vif_chanctx		= ath10k_mac_op_unassign_vif_chanctx,
 	.switch_vif_chanctx		= ath10k_mac_op_switch_vif_chanctx,
+	.sta_pre_rcu_remove		= ath10k_mac_op_sta_pre_rcu_remove,
 
 	CFG80211_TESTMODE_CMD(ath10k_tm_cmd)
 
diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c
index 410bcda..25b8d50 100644
--- a/drivers/net/wireless/ath/ath10k/pci.c
+++ b/drivers/net/wireless/ath/ath10k/pci.c
@@ -840,29 +840,33 @@
 	ath10k_pci_rx_post(ar);
 }
 
+static u32 ath10k_pci_qca988x_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
+{
+	u32 val = 0, region = addr & 0xfffff;
+
+	val = (ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS + CORE_CTRL_ADDRESS)
+				 & 0x7ff) << 21;
+	val |= 0x100000 | region;
+	return val;
+}
+
+static u32 ath10k_pci_qca99x0_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
+{
+	u32 val = 0, region = addr & 0xfffff;
+
+	val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS);
+	val |= 0x100000 | region;
+	return val;
+}
+
 static u32 ath10k_pci_targ_cpu_to_ce_addr(struct ath10k *ar, u32 addr)
 {
-	u32 val = 0;
+	struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
 
-	switch (ar->hw_rev) {
-	case ATH10K_HW_QCA988X:
-	case ATH10K_HW_QCA9887:
-	case ATH10K_HW_QCA6174:
-	case ATH10K_HW_QCA9377:
-		val = (ath10k_pci_read32(ar, SOC_CORE_BASE_ADDRESS +
-					  CORE_CTRL_ADDRESS) &
-		       0x7ff) << 21;
-		break;
-	case ATH10K_HW_QCA9888:
-	case ATH10K_HW_QCA99X0:
-	case ATH10K_HW_QCA9984:
-	case ATH10K_HW_QCA4019:
-		val = ath10k_pci_read32(ar, PCIE_BAR_REG_ADDRESS);
-		break;
-	}
+	if (WARN_ON_ONCE(!ar_pci->targ_cpu_to_ce_addr))
+		return -ENOTSUPP;
 
-	val |= 0x100000 | (addr & 0xfffff);
-	return val;
+	return ar_pci->targ_cpu_to_ce_addr(ar, addr);
 }
 
 /*
@@ -3171,6 +3175,7 @@
 	bool pci_ps;
 	int (*pci_soft_reset)(struct ath10k *ar);
 	int (*pci_hard_reset)(struct ath10k *ar);
+	u32 (*targ_cpu_to_ce_addr)(struct ath10k *ar, u32 addr);
 
 	switch (pci_dev->device) {
 	case QCA988X_2_0_DEVICE_ID:
@@ -3178,12 +3183,14 @@
 		pci_ps = false;
 		pci_soft_reset = ath10k_pci_warm_reset;
 		pci_hard_reset = ath10k_pci_qca988x_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr;
 		break;
 	case QCA9887_1_0_DEVICE_ID:
 		hw_rev = ATH10K_HW_QCA9887;
 		pci_ps = false;
 		pci_soft_reset = ath10k_pci_warm_reset;
 		pci_hard_reset = ath10k_pci_qca988x_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr;
 		break;
 	case QCA6164_2_1_DEVICE_ID:
 	case QCA6174_2_1_DEVICE_ID:
@@ -3191,30 +3198,35 @@
 		pci_ps = true;
 		pci_soft_reset = ath10k_pci_warm_reset;
 		pci_hard_reset = ath10k_pci_qca6174_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr;
 		break;
 	case QCA99X0_2_0_DEVICE_ID:
 		hw_rev = ATH10K_HW_QCA99X0;
 		pci_ps = false;
 		pci_soft_reset = ath10k_pci_qca99x0_soft_chip_reset;
 		pci_hard_reset = ath10k_pci_qca99x0_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca99x0_targ_cpu_to_ce_addr;
 		break;
 	case QCA9984_1_0_DEVICE_ID:
 		hw_rev = ATH10K_HW_QCA9984;
 		pci_ps = false;
 		pci_soft_reset = ath10k_pci_qca99x0_soft_chip_reset;
 		pci_hard_reset = ath10k_pci_qca99x0_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca99x0_targ_cpu_to_ce_addr;
 		break;
 	case QCA9888_2_0_DEVICE_ID:
 		hw_rev = ATH10K_HW_QCA9888;
 		pci_ps = false;
 		pci_soft_reset = ath10k_pci_qca99x0_soft_chip_reset;
 		pci_hard_reset = ath10k_pci_qca99x0_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca99x0_targ_cpu_to_ce_addr;
 		break;
 	case QCA9377_1_0_DEVICE_ID:
 		hw_rev = ATH10K_HW_QCA9377;
 		pci_ps = true;
 		pci_soft_reset = NULL;
 		pci_hard_reset = ath10k_pci_qca6174_chip_reset;
+		targ_cpu_to_ce_addr = ath10k_pci_qca988x_targ_cpu_to_ce_addr;
 		break;
 	default:
 		WARN_ON(1);
@@ -3241,6 +3253,7 @@
 	ar_pci->bus_ops = &ath10k_pci_bus_ops;
 	ar_pci->pci_soft_reset = pci_soft_reset;
 	ar_pci->pci_hard_reset = pci_hard_reset;
+	ar_pci->targ_cpu_to_ce_addr = targ_cpu_to_ce_addr;
 
 	ar->id.vendor = pdev->vendor;
 	ar->id.device = pdev->device;
diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h
index 9854ad5..577bb87 100644
--- a/drivers/net/wireless/ath/ath10k/pci.h
+++ b/drivers/net/wireless/ath/ath10k/pci.h
@@ -238,6 +238,11 @@
 	/* Chip specific pci full reset function */
 	int (*pci_hard_reset)(struct ath10k *ar);
 
+	/* chip specific methods for converting target CPU virtual address
+	 * space to CE address space
+	 */
+	u32 (*targ_cpu_to_ce_addr)(struct ath10k *ar, u32 addr);
+
 	/* Keep this entry in the last, memory for struct ath10k_ahb is
 	 * allocated (ahb support enabled case) in the continuation of
 	 * this struct.
diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c
index b7fe0af..63754ee 100644
--- a/drivers/net/wireless/ath/ath6kl/cfg80211.c
+++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c
@@ -807,9 +807,15 @@
 					WLAN_STATUS_SUCCESS, GFP_KERNEL);
 		cfg80211_put_bss(ar->wiphy, bss);
 	} else if (vif->sme_state == SME_CONNECTED) {
+		struct cfg80211_roam_info roam_info = {
+			.bss = bss,
+			.req_ie = assoc_req_ie,
+			.req_ie_len = assoc_req_len,
+			.resp_ie = assoc_resp_ie,
+			.resp_ie_len = assoc_resp_len,
+		};
 		/* inform roam event to cfg80211 */
-		cfg80211_roamed_bss(vif->ndev, bss, assoc_req_ie, assoc_req_len,
-				    assoc_resp_ie, assoc_resp_len, GFP_KERNEL);
+		cfg80211_roamed(vif->ndev, &roam_info, GFP_KERNEL);
 	}
 }
 
diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c
index e1d59da..ca8797c 100644
--- a/drivers/net/wireless/ath/wcn36xx/main.c
+++ b/drivers/net/wireless/ath/wcn36xx/main.c
@@ -1165,11 +1165,12 @@
 	wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n");
 
 	release_firmware(wcn->nv);
-	mutex_destroy(&wcn->hal_mutex);
 
 	ieee80211_unregister_hw(hw);
 	iounmap(wcn->dxe_base);
 	iounmap(wcn->ccu_base);
+
+	mutex_destroy(&wcn->hal_mutex);
 	ieee80211_free_hw(hw);
 
 	return 0;
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig
index 0e66348..2ab6c59 100644
--- a/drivers/net/wireless/ath/wil6210/Kconfig
+++ b/drivers/net/wireless/ath/wil6210/Kconfig
@@ -60,3 +60,15 @@
 	---help---
 	  Say Y here to enable wil6210 driver support for MSM
 	  platform specific features
+
+config WIL6210_DEBUGFS
+	bool "wil6210 debugfs support"
+	depends on WIL6210
+	depends on DEBUG_FS
+	default y
+	---help---
+	  Say Y here to enable wil6210 debugfs support, using the
+	  kernel debugfs infrastructure. Select this
+	  option if you are interested in debugging the driver.
+
+	  If unsure, say Y to make it easier to debug problems.
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile
index 4874c5b..94df1de 100644
--- a/drivers/net/wireless/ath/wil6210/Makefile
+++ b/drivers/net/wireless/ath/wil6210/Makefile
@@ -4,7 +4,7 @@
 wil6210-y += netdev.o
 wil6210-y += cfg80211.o
 wil6210-y += pcie_bus.o
-wil6210-y += debugfs.o
+wil6210-$(CONFIG_WIL6210_DEBUGFS) += debugfs.o
 wil6210-y += sysfs.o
 wil6210-y += wmi.o
 wil6210-y += interrupt.o
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 7a0b226..51030c3 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -454,6 +454,34 @@
 	return rc;
 }
 
+static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
+					 struct wireless_dev *wdev)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+
+	wil_dbg_misc(wil, "start_p2p_device: entered\n");
+	wil->p2p.p2p_dev_started = 1;
+	return 0;
+}
+
+static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
+					 struct wireless_dev *wdev)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	struct wil_p2p_info *p2p = &wil->p2p;
+
+	if (!p2p->p2p_dev_started)
+		return;
+
+	wil_dbg_misc(wil, "stop_p2p_device: entered\n");
+	mutex_lock(&wil->mutex);
+	mutex_lock(&wil->p2p_wdev_mutex);
+	wil_p2p_stop_radio_operations(wil);
+	p2p->p2p_dev_started = 0;
+	mutex_unlock(&wil->p2p_wdev_mutex);
+	mutex_unlock(&wil->mutex);
+}
+
 static struct wireless_dev *
 wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
 		       unsigned char name_assign_type,
@@ -502,6 +530,7 @@
 		return -EINVAL;
 	}
 
+	wil_cfg80211_stop_p2p_device(wiphy, wdev);
 	wil_p2p_wdev_free(wil);
 
 	return 0;
@@ -886,7 +915,7 @@
 		wil->bss = bss;
 		/* Connect can take lots of time */
 		mod_timer(&wil->connect_timer,
-			  jiffies + msecs_to_jiffies(2000));
+			  jiffies + msecs_to_jiffies(5000));
 	} else {
 		clear_bit(wil_status_fwconnecting, wil->status);
 	}
@@ -947,7 +976,7 @@
 			 u64 *cookie)
 {
 	const u8 *buf = params->buf;
-	size_t len = params->len;
+	size_t len = params->len, total;
 	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
 	int rc;
 	bool tx_status = false;
@@ -972,7 +1001,11 @@
 	if (len < sizeof(struct ieee80211_hdr_3addr))
 		return -EINVAL;
 
-	cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
+	total = sizeof(*cmd) + len;
+	if (total < len)
+		return -EINVAL;
+
+	cmd = kmalloc(total, GFP_KERNEL);
 	if (!cmd) {
 		rc = -ENOMEM;
 		goto out;
@@ -982,7 +1015,7 @@
 	cmd->len = cpu_to_le16(len);
 	memcpy(cmd->payload, buf, len);
 
-	rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
+	rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, total,
 		      WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
 	if (rc == 0)
 		tx_status = !evt.evt.status;
@@ -1736,34 +1769,6 @@
 	return 0;
 }
 
-static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
-					 struct wireless_dev *wdev)
-{
-	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
-
-	wil_dbg_misc(wil, "start_p2p_device: entered\n");
-	wil->p2p.p2p_dev_started = 1;
-	return 0;
-}
-
-static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
-					 struct wireless_dev *wdev)
-{
-	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
-	struct wil_p2p_info *p2p = &wil->p2p;
-
-	if (!p2p->p2p_dev_started)
-		return;
-
-	wil_dbg_misc(wil, "stop_p2p_device: entered\n");
-	mutex_lock(&wil->mutex);
-	mutex_lock(&wil->p2p_wdev_mutex);
-	wil_p2p_stop_radio_operations(wil);
-	p2p->p2p_dev_started = 0;
-	mutex_unlock(&wil->p2p_wdev_mutex);
-	mutex_unlock(&wil->mutex);
-}
-
 static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
 				       struct net_device *dev,
 				       bool enabled, int timeout)
@@ -1801,9 +1806,13 @@
 
 	wil_dbg_pm(wil, "suspending\n");
 
+	mutex_lock(&wil->mutex);
 	wil_p2p_stop_discovery(wil);
 
+	mutex_lock(&wil->p2p_wdev_mutex);
 	wil_abort_scan(wil, true);
+	mutex_unlock(&wil->p2p_wdev_mutex);
+	mutex_unlock(&wil->mutex);
 
 out:
 	return rc;
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 45a8081..831780a 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -242,12 +242,19 @@
 static int wil_mbox_debugfs_show(struct seq_file *s, void *data)
 {
 	struct wil6210_priv *wil = s->private;
+	int ret;
+
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
 
 	wil_print_ring(s, "tx", wil->csr + HOST_MBOX +
 		       offsetof(struct wil6210_mbox_ctl, tx));
 	wil_print_ring(s, "rx", wil->csr + HOST_MBOX +
 		       offsetof(struct wil6210_mbox_ctl, rx));
 
+	wil_pm_runtime_put(wil);
+
 	return 0;
 }
 
@@ -265,15 +272,38 @@
 
 static int wil_debugfs_iomem_x32_set(void *data, u64 val)
 {
-	writel(val, (void __iomem *)data);
+	struct wil_debugfs_iomem_data *d = (struct
+					    wil_debugfs_iomem_data *)data;
+	struct wil6210_priv *wil = d->wil;
+	int ret;
+
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
+	writel_relaxed(val, (void __iomem *)d->offset);
+
 	wmb(); /* make sure write propagated to HW */
 
+	wil_pm_runtime_put(wil);
+
 	return 0;
 }
 
 static int wil_debugfs_iomem_x32_get(void *data, u64 *val)
 {
-	*val = readl((void __iomem *)data);
+	struct wil_debugfs_iomem_data *d = (struct
+					    wil_debugfs_iomem_data *)data;
+	struct wil6210_priv *wil = d->wil;
+	int ret;
+
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
+	*val = readl_relaxed((void __iomem *)d->offset);
+
+	wil_pm_runtime_put(wil);
 
 	return 0;
 }
@@ -284,10 +314,21 @@
 static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
 						   umode_t mode,
 						   struct dentry *parent,
-						   void *value)
+						   void *value,
+						   struct wil6210_priv *wil)
 {
-	return debugfs_create_file(name, mode, parent, value,
-				   &fops_iomem_x32);
+	struct dentry *file;
+	struct wil_debugfs_iomem_data *data = &wil->dbg_data.data_arr[
+					      wil->dbg_data.iomem_data_count];
+
+	data->wil = wil;
+	data->offset = value;
+
+	file = debugfs_create_file(name, mode, parent, data, &fops_iomem_x32);
+	if (!IS_ERR_OR_NULL(file))
+		wil->dbg_data.iomem_data_count++;
+
+	return file;
 }
 
 static int wil_debugfs_ulong_set(void *data, u64 val)
@@ -346,7 +387,8 @@
 		case doff_io32:
 			f = wil_debugfs_create_iomem_x32(tbl[i].name,
 							 tbl[i].mode, dbg,
-							 base + tbl[i].off);
+							 base + tbl[i].off,
+							 wil);
 			break;
 		case doff_u8:
 			f = debugfs_create_u8(tbl[i].name, tbl[i].mode, dbg,
@@ -475,13 +517,22 @@
 static int wil_memread_debugfs_show(struct seq_file *s, void *data)
 {
 	struct wil6210_priv *wil = s->private;
-	void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr));
+	void __iomem *a;
+	int ret;
+
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
+	a = wmi_buffer(wil, cpu_to_le32(mem_addr));
 
 	if (a)
 		seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, readl(a));
 	else
 		seq_printf(s, "[0x%08x] = INVALID\n", mem_addr);
 
+	wil_pm_runtime_put(wil);
+
 	return 0;
 }
 
@@ -502,10 +553,12 @@
 {
 	enum { max_count = 4096 };
 	struct wil_blob_wrapper *wil_blob = file->private_data;
+	struct wil6210_priv *wil = wil_blob->wil;
 	loff_t pos = *ppos;
 	size_t available = wil_blob->blob.size;
 	void *buf;
 	size_t ret;
+	int rc;
 
 	if (test_bit(wil_status_suspending, wil_blob->wil->status) ||
 	    test_bit(wil_status_suspended, wil_blob->wil->status))
@@ -526,10 +579,19 @@
 	if (!buf)
 		return -ENOMEM;
 
+	rc = wil_pm_runtime_get(wil);
+	if (rc < 0) {
+		kfree(buf);
+		return rc;
+	}
+
 	wil_memcpy_fromio_32(buf, (const void __iomem *)
 			     wil_blob->blob.data + pos, count);
 
 	ret = copy_to_user(user_buf, buf, count);
+
+	wil_pm_runtime_put(wil);
+
 	kfree(buf);
 	if (ret == count)
 		return -EFAULT;
@@ -1786,6 +1848,13 @@
 	{},
 };
 
+static const int dbg_off_count = 4 * (ARRAY_SIZE(isr_off) - 1) +
+				ARRAY_SIZE(dbg_wil_regs) - 1 +
+				ARRAY_SIZE(pseudo_isr_off) - 1 +
+				ARRAY_SIZE(lgc_itr_cnt_off) - 1 +
+				ARRAY_SIZE(tx_itr_cnt_off) - 1 +
+				ARRAY_SIZE(rx_itr_cnt_off) - 1;
+
 int wil6210_debugfs_init(struct wil6210_priv *wil)
 {
 	struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME,
@@ -1794,6 +1863,17 @@
 	if (IS_ERR_OR_NULL(dbg))
 		return -ENODEV;
 
+	wil->dbg_data.data_arr = kcalloc(dbg_off_count,
+					 sizeof(struct wil_debugfs_iomem_data),
+					 GFP_KERNEL);
+	if (!wil->dbg_data.data_arr) {
+		debugfs_remove_recursive(dbg);
+		wil->debug = NULL;
+		return -ENOMEM;
+	}
+
+	wil->dbg_data.iomem_data_count = 0;
+
 	wil_pmc_init(wil);
 
 	wil6210_debugfs_init_files(wil, dbg);
@@ -1818,6 +1898,8 @@
 	debugfs_remove_recursive(wil->debug);
 	wil->debug = NULL;
 
+	kfree(wil->dbg_data.data_arr);
+
 	/* free pmc memory without sending command to fw, as it will
 	 * be reset on the way down anyway
 	 */
diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c
index adcfef4..66200f6 100644
--- a/drivers/net/wireless/ath/wil6210/ethtool.c
+++ b/drivers/net/wireless/ath/wil6210/ethtool.c
@@ -47,9 +47,14 @@
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
 	u32 tx_itr_en, tx_itr_val = 0;
 	u32 rx_itr_en, rx_itr_val = 0;
+	int ret;
 
 	wil_dbg_misc(wil, "ethtoolops_get_coalesce\n");
 
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
 	tx_itr_en = wil_r(wil, RGF_DMA_ITR_TX_CNT_CTL);
 	if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN)
 		tx_itr_val = wil_r(wil, RGF_DMA_ITR_TX_CNT_TRSH);
@@ -58,6 +63,8 @@
 	if (rx_itr_en & BIT_DMA_ITR_RX_CNT_CTL_EN)
 		rx_itr_val = wil_r(wil, RGF_DMA_ITR_RX_CNT_TRSH);
 
+	wil_pm_runtime_put(wil);
+
 	cp->tx_coalesce_usecs = tx_itr_val;
 	cp->rx_coalesce_usecs = rx_itr_val;
 	return 0;
@@ -67,6 +74,7 @@
 				       struct ethtool_coalesce *cp)
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
+	int ret;
 
 	wil_dbg_misc(wil, "ethtoolops_set_coalesce: rx %d usec, tx %d usec\n",
 		     cp->rx_coalesce_usecs, cp->tx_coalesce_usecs);
@@ -86,8 +94,15 @@
 
 	wil->tx_max_burst_duration = cp->tx_coalesce_usecs;
 	wil->rx_max_burst_duration = cp->rx_coalesce_usecs;
+
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
 	wil_configure_interrupt_moderation(wil);
 
+	wil_pm_runtime_put(wil);
+
 	return 0;
 
 out_bad:
diff --git a/drivers/net/wireless/ath/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
index f94c894..17939ce 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.c
+++ b/drivers/net/wireless/ath/wil6210/ftm.c
@@ -38,6 +38,9 @@
 /* initial token to use on non-secure FTM measurement */
 #define WIL_TOF_FTM_DEFAULT_INITIAL_TOKEN	2
 
+/* maximum AOA burst period, limited by FW */
+#define WIL_AOA_MAX_BURST_PERIOD	255
+
 #define WIL_TOF_FTM_MAX_LCI_LENGTH		(240)
 #define WIL_TOF_FTM_MAX_LCR_LENGTH		(240)
 
@@ -62,6 +65,7 @@
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
+	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD] = { .type = NLA_U16 },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ] = { .type = NLA_U32 },
 };
 
@@ -315,8 +319,8 @@
 	struct wmi_tof_session_start_cmd *cmd;
 
 	mutex_lock(&wil->ftm.lock);
-	if (wil->ftm.session_started) {
-		wil_err(wil, "FTM session already running\n");
+	if (wil->ftm.session_started || wil->ftm.aoa_started) {
+		wil_err(wil, "FTM or AOA session already running\n");
 		rc = -EAGAIN;
 		goto out;
 	}
@@ -360,6 +364,7 @@
 	}
 
 	cmd->session_id = cpu_to_le32(WIL_FTM_FW_SESSION_ID);
+	cmd->aoa_type = request->aoa_type;
 	cmd->num_of_dest = cpu_to_le16(request->n_peers);
 	for (i = 0; i < request->n_peers; i++) {
 		ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
@@ -402,6 +407,8 @@
 			request->peers[i].params.burst_duration;
 		cmd->ftm_dest_info[i].burst_period =
 			cpu_to_le16(request->peers[i].params.burst_period);
+		cmd->ftm_dest_info[i].num_burst_per_aoa_meas =
+			request->peers[i].aoa_burst_period;
 	}
 
 	rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
@@ -487,8 +494,8 @@
 
 	mutex_lock(&wil->ftm.lock);
 
-	if (wil->ftm.aoa_started) {
-		wil_err(wil, "AOA measurement already running\n");
+	if (wil->ftm.aoa_started || wil->ftm.session_started) {
+		wil_err(wil, "AOA or FTM measurement already running\n");
 		rc = -EAGAIN;
 		goto out;
 	}
@@ -529,8 +536,8 @@
 
 	mutex_lock(&wil->ftm.lock);
 
-	if (!wil->ftm.aoa_started) {
-		wil_info(wil, "AOA not started, not sending result\n");
+	if (!wil->ftm.aoa_started && !wil->ftm.session_started) {
+		wil_info(wil, "AOA/FTM not started, not sending result\n");
 		goto out;
 	}
 
@@ -683,6 +690,10 @@
 	int data_len = len - offsetof(struct wmi_aoa_meas_event, meas_data);
 	struct wil_aoa_meas_result *res;
 
+	if (data_len < 0) {
+		wil_err(wil, "AOA event too short (%d)\n", len);
+		return;
+	}
 	data_len = min_t(int, le16_to_cpu(evt->length), data_len);
 
 	res = kmalloc(sizeof(*res) + data_len, GFP_KERNEL);
@@ -754,6 +765,7 @@
 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
 	struct nlattr *peer;
 	int rc, n_peers = 0, index = 0, tmp;
+	u32 aoa_type = 0;
 
 	if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
 		return -ENOTSUPP;
@@ -775,6 +787,14 @@
 		return -EINVAL;
 	}
 
+	if (tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]) {
+		aoa_type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
+		if (aoa_type >= QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX) {
+			wil_err(wil, "invalid AOA type: %d\n", aoa_type);
+			return -EINVAL;
+		}
+	}
+
 	nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
 			    tmp)
 		n_peers++;
@@ -798,6 +818,7 @@
 
 	request->session_cookie =
 		nla_get_u64(tb[QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE]);
+	request->aoa_type = aoa_type;
 	request->n_peers = n_peers;
 	nla_for_each_nested(peer, tb[QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS],
 			    tmp) {
@@ -826,6 +847,18 @@
 		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID])
 			request->peers[index].secure_token_id = nla_get_u8(
 			   tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID]);
+		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]) {
+			request->peers[index].aoa_burst_period = nla_get_u16(
+			  tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD]);
+			if (request->peers[index].aoa_burst_period >
+			    WIL_AOA_MAX_BURST_PERIOD) {
+				wil_err(wil, "Invalid AOA burst period at index: %d\n",
+					index);
+				rc = -EINVAL;
+				goto out;
+			}
+		}
+
 		rc = wil_ftm_parse_meas_params(
 			wil,
 			tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS],
diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h
index 8efa292..21923c2 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.h
+++ b/drivers/net/wireless/ath/wil6210/ftm.h
@@ -437,12 +437,14 @@
 	u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
 	struct wil_ftm_meas_params params;
 	u8 secure_token_id;
+	u16 aoa_burst_period; /* 0 if no AOA, >0 every <value> bursts */
 };
 
 /* session request, passed to wil_ftm_cfg80211_start_session */
 struct wil_ftm_session_request {
 	u64 session_cookie;
 	u32 n_peers;
+	u32 aoa_type; /* enum qca_wlan_vendor_attr_aoa_type */
 	/* keep last, variable size according to n_peers */
 	struct wil_ftm_meas_peer_info peers[0];
 };
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c
index e01acac..77d1902 100644
--- a/drivers/net/wireless/ath/wil6210/fw_inc.c
+++ b/drivers/net/wireless/ath/wil6210/fw_inc.c
@@ -26,14 +26,17 @@
 					     prefix_type, rowsize,	\
 					     groupsize, buf, len, ascii)
 
-#define FW_ADDR_CHECK(ioaddr, val, msg) do { \
-		ioaddr = wmi_buffer(wil, val); \
-		if (!ioaddr) { \
-			wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
-				   le32_to_cpu(val)); \
-			return -EINVAL; \
-		} \
-	} while (0)
+static bool wil_fw_addr_check(struct wil6210_priv *wil,
+			      void __iomem **ioaddr, __le32 val,
+			      u32 size, const char *msg)
+{
+	*ioaddr = wmi_buffer_block(wil, val, size);
+	if (!(*ioaddr)) {
+		wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val));
+		return false;
+	}
+	return true;
+}
 
 /**
  * wil_fw_verify - verify firmware file validity
@@ -124,24 +127,19 @@
 	return 0;
 }
 
-static int fw_handle_comment(struct wil6210_priv *wil, const void *data,
-			     size_t size)
-{
-	wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true);
-
-	return 0;
-}
-
 static int
-fw_handle_capabilities(struct wil6210_priv *wil, const void *data,
-		       size_t size)
+fw_handle_comment(struct wil6210_priv *wil, const void *data,
+		  size_t size)
 {
 	const struct wil_fw_record_capabilities *rec = data;
 	size_t capa_size;
 
 	if (size < sizeof(*rec) ||
-	    le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC)
+	    le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) {
+		wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1,
+				data, size, true);
 		return 0;
+	}
 
 	capa_size = size - offsetof(struct wil_fw_record_capabilities,
 				    capabilities);
@@ -165,7 +163,8 @@
 		return -EINVAL;
 	}
 
-	FW_ADDR_CHECK(dst, d->addr, "address");
+	if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
+		return -EINVAL;
 	wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr),
 		   s);
 	wil_memcpy_toio_32(dst, d->data, s);
@@ -197,7 +196,8 @@
 		return -EINVAL;
 	}
 
-	FW_ADDR_CHECK(dst, d->addr, "address");
+	if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address"))
+		return -EINVAL;
 
 	v = le32_to_cpu(d->value);
 	wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
@@ -253,7 +253,8 @@
 		u32 v = le32_to_cpu(block[i].value);
 		u32 x, y;
 
-		FW_ADDR_CHECK(dst, block[i].addr, "address");
+		if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address"))
+			return -EINVAL;
 
 		x = readl(dst);
 		y = (x & m) | (v & ~m);
@@ -319,10 +320,15 @@
 	wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n",
 		   n, gw_cmd);
 
-	FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
-	FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr");
-	FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
-	FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+	if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
+			       "gateway_addr_addr") ||
+	    !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0,
+			       "gateway_value_addr") ||
+	    !wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
+			       "gateway_cmd_addr") ||
+	    !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
+			       "gateway_ctrl_address"))
+		return -EINVAL;
 
 	wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x"
 		   " cmd 0x%08x ctl 0x%08x\n",
@@ -378,12 +384,19 @@
 	wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
 		   n, gw_cmd);
 
-	FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr");
+	if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0,
+			       "gateway_addr_addr"))
+		return -EINVAL;
 	for (k = 0; k < ARRAY_SIZE(block->value); k++)
-		FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k],
-			      "gateway_value_addr");
-	FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr");
-	FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address");
+		if (!wil_fw_addr_check(wil, &gwa_val[k],
+				       d->gateway_value_addr[k],
+				       0, "gateway_value_addr"))
+			return -EINVAL;
+	if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0,
+			       "gateway_cmd_addr") ||
+	    !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0,
+			       "gateway_ctrl_address"))
+		return -EINVAL;
 
 	wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
 		   le32_to_cpu(d->gateway_addr_addr),
@@ -422,7 +435,7 @@
 	int (*parse_handler)(struct wil6210_priv *wil, const void *data,
 			     size_t size);
 } wil_fw_handlers[] = {
-	{wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities},
+	{wil_fw_type_comment, fw_handle_comment, fw_handle_comment},
 	{wil_fw_type_data, fw_handle_data, fw_ignore_section},
 	{wil_fw_type_fill, fw_handle_fill, fw_ignore_section},
 	/* wil_fw_type_action */
@@ -517,7 +530,7 @@
 
 	rc = request_firmware(&fw, name, wil_to_dev(wil));
 	if (rc) {
-		wil_err_fw(wil, "Failed to load firmware %s\n", name);
+		wil_err_fw(wil, "Failed to load firmware %s rc %d\n", name, rc);
 		return rc;
 	}
 	wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index cad8a95c4..5cf3417 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -244,7 +244,7 @@
 	wil_dbg_irq(wil, "ISR RX 0x%08x\n", isr);
 
 	if (unlikely(!isr)) {
-		wil_err(wil, "spurious IRQ: RX\n");
+		wil_err_ratelimited(wil, "spurious IRQ: RX\n");
 		return IRQ_NONE;
 	}
 
@@ -269,11 +269,12 @@
 				need_unmask = false;
 				napi_schedule(&wil->napi_rx);
 			} else {
-				wil_err(wil,
+				wil_err_ratelimited(
+					wil,
 					"Got Rx interrupt while stopping interface\n");
 			}
 		} else {
-			wil_err(wil, "Got Rx interrupt while in reset\n");
+			wil_err_ratelimited(wil, "Got Rx interrupt while in reset\n");
 		}
 	}
 
@@ -302,7 +303,7 @@
 	wil_dbg_irq(wil, "ISR TX 0x%08x\n", isr);
 
 	if (unlikely(!isr)) {
-		wil_err(wil, "spurious IRQ: TX\n");
+		wil_err_ratelimited(wil, "spurious IRQ: TX\n");
 		return IRQ_NONE;
 	}
 
@@ -318,12 +319,13 @@
 			need_unmask = false;
 			napi_schedule(&wil->napi_tx);
 		} else {
-			wil_err(wil, "Got Tx interrupt while in reset\n");
+			wil_err_ratelimited(wil, "Got Tx interrupt while in reset\n");
 		}
 	}
 
 	if (unlikely(isr))
-		wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr);
+		wil_err_ratelimited(wil, "un-handled TX ISR bits 0x%08x\n",
+				    isr);
 
 	/* Tx IRQ will be enabled when NAPI processing finished */
 
@@ -356,6 +358,25 @@
 	wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx);
 }
 
+static bool wil_validate_mbox_regs(struct wil6210_priv *wil)
+{
+	size_t min_size = sizeof(struct wil6210_mbox_hdr) +
+		sizeof(struct wmi_cmd_hdr);
+
+	if (wil->mbox_ctl.rx.entry_size < min_size) {
+		wil_err(wil, "rx mbox entry too small (%d)\n",
+			wil->mbox_ctl.rx.entry_size);
+		return false;
+	}
+	if (wil->mbox_ctl.tx.entry_size < min_size) {
+		wil_err(wil, "tx mbox entry too small (%d)\n",
+			wil->mbox_ctl.tx.entry_size);
+		return false;
+	}
+
+	return true;
+}
+
 static irqreturn_t wil6210_irq_misc(int irq, void *cookie)
 {
 	struct wil6210_priv *wil = cookie;
@@ -391,7 +412,8 @@
 	if (isr & ISR_MISC_FW_READY) {
 		wil_dbg_irq(wil, "IRQ: FW ready\n");
 		wil_cache_mbox_regs(wil);
-		set_bit(wil_status_mbox_ready, wil->status);
+		if (wil_validate_mbox_regs(wil))
+			set_bit(wil_status_mbox_ready, wil->status);
 		/**
 		 * Actual FW ready indicated by the
 		 * WMI_FW_READY_EVENTID
diff --git a/drivers/net/wireless/ath/wil6210/ioctl.c b/drivers/net/wireless/ath/wil6210/ioctl.c
index f8d2c20..25bc439 100644
--- a/drivers/net/wireless/ath/wil6210/ioctl.c
+++ b/drivers/net/wireless/ath/wil6210/ioctl.c
@@ -221,6 +221,10 @@
 {
 	int ret;
 
+	ret = wil_pm_runtime_get(wil);
+	if (ret < 0)
+		return ret;
+
 	switch (cmd) {
 	case WIL_IOCTL_MEMIO:
 		ret = wil_ioc_memio_dword(wil, data);
@@ -233,9 +237,12 @@
 		break;
 	default:
 		wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
+		wil_pm_runtime_put(wil);
 		return -ENOIOCTLCMD;
 	}
 
+	wil_pm_runtime_put(wil);
+
 	wil_dbg_ioctl(wil, "ioctl(0x%04x) -> %d\n", cmd, ret);
 	return ret;
 }
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index d90fe1c..ae5a1b6 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -765,6 +765,8 @@
 	u8 retry_short;
 	int rc;
 
+	wil_refresh_fw_capabilities(wil);
+
 	rc = wmi_get_mgmt_retry(wil, &retry_short);
 	if (!rc) {
 		wiphy->retry_short = retry_short;
@@ -772,6 +774,35 @@
 	}
 }
 
+void wil_refresh_fw_capabilities(struct wil6210_priv *wil)
+{
+	struct wiphy *wiphy = wil_to_wiphy(wil);
+	int features;
+
+	wil->keep_radio_on_during_sleep =
+		test_bit(WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND,
+			 wil->platform_capa) &&
+		test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
+
+	wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
+		 wil->keep_radio_on_during_sleep);
+
+	if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
+		wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+	else
+		wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
+
+	if (wil->platform_ops.set_features) {
+		features = (test_bit(WMI_FW_CAPABILITY_REF_CLOCK_CONTROL,
+				     wil->fw_capabilities) &&
+			    test_bit(WIL_PLATFORM_CAPA_EXT_CLK,
+				     wil->platform_capa)) ?
+			BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL) : 0;
+
+		wil->platform_ops.set_features(wil->platform_handle, features);
+	}
+}
+
 void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r)
 {
 	le32_to_cpus(&r->base);
@@ -934,6 +965,29 @@
 	return rc;
 }
 
+static void wil_pre_fw_config(struct wil6210_priv *wil)
+{
+	/* Mark FW as loaded from host */
+	wil_s(wil, RGF_USER_USAGE_6, 1);
+
+	/* clear any interrupts which on-card-firmware
+	 * may have set
+	 */
+	wil6210_clear_irq(wil);
+	/* CAF_ICR - clear and mask */
+	/* it is W1C, clear by writing back same value */
+	wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
+	wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
+	/* clear PAL_UNIT_ICR (potential D0->D3 leftover) */
+	wil_s(wil, RGF_PAL_UNIT_ICR + offsetof(struct RGF_ICR, ICR), 0);
+
+	if (wil->fw_calib_result > 0) {
+		__le32 val = cpu_to_le32(wil->fw_calib_result |
+						(CALIB_RESULT_SIGNATURE << 8));
+		wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val);
+	}
+}
+
 /*
  * We reset all the structures, and we reset the UMAC.
  * After calling this routine, you're expected to reload
@@ -942,6 +996,7 @@
 int wil_reset(struct wil6210_priv *wil, bool load_fw)
 {
 	int rc;
+	unsigned long status_flags = BIT(wil_status_resetting);
 
 	wil_dbg_misc(wil, "reset\n");
 
@@ -962,9 +1017,18 @@
 	if (wil->hw_version == HW_VER_UNKNOWN)
 		return -ENODEV;
 
-	wil_dbg_misc(wil, "Prevent DS in BL & mark FW to set T_POWER_ON=0\n");
-	wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP |
-	      BIT_USER_SUPPORT_T_POWER_ON_0);
+	wil_dbg_misc(wil, "Prevent DS in BL\n");
+	wil_s(wil, RGF_USER_USAGE_8, BIT_USER_PREVENT_DEEP_SLEEP);
+
+	if (test_bit(WIL_PLATFORM_CAPA_T_PWR_ON_0, wil->platform_capa)) {
+		wil_dbg_misc(wil, "Notify FW to set T_POWER_ON=0\n");
+		wil_s(wil, RGF_USER_USAGE_8, BIT_USER_SUPPORT_T_POWER_ON_0);
+	}
+
+	if (test_bit(WIL_PLATFORM_CAPA_EXT_CLK, wil->platform_capa)) {
+		wil_dbg_misc(wil, "Notify FW on ext clock configuration\n");
+		wil_s(wil, RGF_USER_USAGE_8, BIT_USER_EXT_CLK);
+	}
 
 	if (wil->platform_ops.notify) {
 		rc = wil->platform_ops.notify(wil->platform_handle,
@@ -975,6 +1039,14 @@
 	}
 
 	set_bit(wil_status_resetting, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the reset.
+		 * following crash dump collection, reset would take place.
+		 */
+		wil_dbg_misc(wil, "reject reset while collecting crash dump\n");
+		rc = -EBUSY;
+		goto out;
+	}
 
 	cancel_work_sync(&wil->disconnect_worker);
 	wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false);
@@ -989,7 +1061,11 @@
 
 	/* prevent NAPI from being scheduled and prevent wmi commands */
 	mutex_lock(&wil->wmi_mutex);
-	bitmap_zero(wil->status, wil_status_last);
+	if (test_bit(wil_status_suspending, wil->status))
+		status_flags |= BIT(wil_status_suspending);
+	bitmap_and(wil->status, wil->status, &status_flags,
+		   wil_status_last);
+	wil_dbg_misc(wil, "wil->status (0x%lx)\n", *wil->status);
 	mutex_unlock(&wil->wmi_mutex);
 
 	wil_mask_irq(wil);
@@ -1007,14 +1083,14 @@
 	wil_rx_fini(wil);
 	if (rc) {
 		wil_bl_crash_info(wil, true);
-		return rc;
+		goto out;
 	}
 
 	rc = wil_get_bl_info(wil);
 	if (rc == -EAGAIN && !load_fw) /* ignore RF error if not going up */
 		rc = 0;
 	if (rc)
-		return rc;
+		goto out;
 
 	wil_set_oob_mode(wil, oob_mode);
 	if (load_fw) {
@@ -1026,29 +1102,12 @@
 		/* Loading f/w from the file */
 		rc = wil_request_firmware(wil, wil->wil_fw_name, true);
 		if (rc)
-			return rc;
+			goto out;
 		rc = wil_request_firmware(wil, WIL_BOARD_FILE_NAME, true);
 		if (rc)
-			return rc;
+			goto out;
 
-		/* Mark FW as loaded from host */
-		wil_s(wil, RGF_USER_USAGE_6, 1);
-
-		/* clear any interrupts which on-card-firmware
-		 * may have set
-		 */
-		wil6210_clear_irq(wil);
-		/* CAF_ICR - clear and mask */
-		/* it is W1C, clear by writing back same value */
-		wil_s(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, ICR), 0);
-		wil_w(wil, RGF_CAF_ICR + offsetof(struct RGF_ICR, IMV), ~0);
-
-		if (wil->fw_calib_result > 0) {
-			__le32 val = cpu_to_le32(wil->fw_calib_result |
-						 (CALIB_RESULT_SIGNATURE << 8));
-			wil_w(wil, RGF_USER_FW_CALIB_RESULT, (u32 __force)val);
-		}
-
+		wil_pre_fw_config(wil);
 		wil_release_cpu(wil);
 	}
 
@@ -1058,6 +1117,8 @@
 	reinit_completion(&wil->wmi_call);
 	reinit_completion(&wil->halp.comp);
 
+	clear_bit(wil_status_resetting, wil->status);
+
 	if (load_fw) {
 		wil_configure_interrupt_moderation(wil);
 		wil_unmask_irq(wil);
@@ -1074,13 +1135,17 @@
 			return rc;
 		}
 
+		wil_collect_fw_info(wil);
+
 		if (wil->ps_profile != WMI_PS_PROFILE_TYPE_DEFAULT)
 			wil_ps_update(wil, wil->ps_profile);
 
 		if (wil->tt_data_set)
 			wmi_set_tt_cfg(wil, &wil->tt_data);
 
-		wil_collect_fw_info(wil);
+		if (wil->snr_thresh.enabled)
+			wmi_set_snr_thresh(wil, wil->snr_thresh.omni,
+					   wil->snr_thresh.direct);
 
 		if (wil->platform_ops.notify) {
 			rc = wil->platform_ops.notify(wil->platform_handle,
@@ -1094,6 +1159,10 @@
 	}
 
 	return rc;
+
+out:
+	clear_bit(wil_status_resetting, wil->status);
+	return rc;
 }
 
 void wil_fw_error_recovery(struct wil6210_priv *wil)
@@ -1201,9 +1270,7 @@
 	wil_abort_scan(wil, false);
 	mutex_unlock(&wil->p2p_wdev_mutex);
 
-	wil_reset(wil, false);
-
-	return 0;
+	return wil_reset(wil, false);
 }
 
 int wil_down(struct wil6210_priv *wil)
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c
index d80e7f4..40cd32a 100644
--- a/drivers/net/wireless/ath/wil6210/netdev.c
+++ b/drivers/net/wireless/ath/wil6210/netdev.c
@@ -26,6 +26,7 @@
 static int wil_open(struct net_device *ndev)
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
+	int rc;
 
 	wil_dbg_misc(wil, "open\n");
 
@@ -35,16 +36,29 @@
 		return -EINVAL;
 	}
 
-	return wil_up(wil);
+	rc = wil_pm_runtime_get(wil);
+	if (rc < 0)
+		return rc;
+
+	rc = wil_up(wil);
+	if (rc)
+		wil_pm_runtime_put(wil);
+
+	return rc;
 }
 
 static int wil_stop(struct net_device *ndev)
 {
 	struct wil6210_priv *wil = ndev_to_wil(ndev);
+	int rc;
 
 	wil_dbg_misc(wil, "stop\n");
 
-	return wil_down(wil);
+	rc = wil_down(wil);
+	if (!rc)
+		wil_pm_runtime_put(wil);
+
+	return rc;
 }
 
 static int wil_change_mtu(struct net_device *ndev, int new_mtu)
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 42a5235..370068a 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -21,6 +21,7 @@
 #include <linux/suspend.h>
 #include "wil6210.h"
 #include <linux/rtnetlink.h>
+#include <linux/pm_runtime.h>
 
 static bool use_msi = true;
 module_param(use_msi, bool, 0444);
@@ -44,9 +45,11 @@
 	u32 jtag_id = wil_r(wil, RGF_USER_JTAG_DEV_ID);
 	u8 chip_revision = (wil_r(wil, RGF_USER_REVISION_ID) &
 			    RGF_USER_REVISION_ID_MASK);
+	int platform_capa;
 
 	bitmap_zero(wil->hw_capabilities, hw_capability_last);
 	bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX);
+	bitmap_zero(wil->platform_capa, WIL_PLATFORM_CAPA_MAX);
 	wil->wil_fw_name = ftm_mode ? WIL_FW_NAME_FTM_DEFAULT :
 			   WIL_FW_NAME_DEFAULT;
 	wil->chip_revision = chip_revision;
@@ -82,11 +85,17 @@
 
 	wil_info(wil, "Board hardware is %s\n", wil->hw_name);
 
+	/* Get platform capabilities */
+	if (wil->platform_ops.get_capa) {
+		platform_capa =
+			wil->platform_ops.get_capa(wil->platform_handle);
+		memcpy(wil->platform_capa, &platform_capa,
+		       min(sizeof(wil->platform_capa), sizeof(platform_capa)));
+	}
+
 	/* extract FW capabilities from file without loading the FW */
 	wil_request_firmware(wil, wil->wil_fw_name, false);
-
-	if (test_bit(WMI_FW_CAPABILITY_RSSI_REPORTING, wil->fw_capabilities))
-		wil_to_wiphy(wil)->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+	wil_refresh_fw_capabilities(wil);
 }
 
 void wil_disable_irq(struct wil6210_priv *wil)
@@ -289,15 +298,6 @@
 	wil_set_capabilities(wil);
 	wil6210_clear_irq(wil);
 
-	wil->keep_radio_on_during_sleep =
-		wil->platform_ops.keep_radio_on_during_sleep &&
-		wil->platform_ops.keep_radio_on_during_sleep(
-			wil->platform_handle) &&
-		test_bit(WMI_FW_CAPABILITY_D3_SUSPEND, wil->fw_capabilities);
-
-	wil_info(wil, "keep_radio_on_during_sleep (%d)\n",
-		 wil->keep_radio_on_during_sleep);
-
 	/* FW should raise IRQ when ready */
 	rc = wil_if_pcie_enable(wil);
 	if (rc) {
@@ -327,6 +327,8 @@
 	wil6210_debugfs_init(wil);
 	wil6210_sysfs_init(wil);
 
+	wil_pm_runtime_allow(wil);
+
 	return 0;
 
 bus_disable:
@@ -359,6 +361,8 @@
 #endif /* CONFIG_PM_SLEEP */
 #endif /* CONFIG_PM */
 
+	wil_pm_runtime_forbid(wil);
+
 	wil6210_sysfs_remove(wil);
 	wil6210_debugfs_remove(wil);
 	rtnl_lock();
@@ -488,10 +492,40 @@
 }
 #endif /* CONFIG_PM_SLEEP */
 
+static int wil6210_pm_runtime_idle(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct wil6210_priv *wil = pci_get_drvdata(pdev);
+
+	wil_dbg_pm(wil, "Runtime idle\n");
+
+	return wil_can_suspend(wil, true);
+}
+
+static int wil6210_pm_runtime_resume(struct device *dev)
+{
+	return wil6210_resume(dev, true);
+}
+
+static int wil6210_pm_runtime_suspend(struct device *dev)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct wil6210_priv *wil = pci_get_drvdata(pdev);
+
+	if (test_bit(wil_status_suspended, wil->status)) {
+		wil_dbg_pm(wil, "trying to suspend while suspended\n");
+		return 1;
+	}
+
+	return wil6210_suspend(dev, true);
+}
 #endif /* CONFIG_PM */
 
 static const struct dev_pm_ops wil6210_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(wil6210_pm_suspend, wil6210_pm_resume)
+	SET_RUNTIME_PM_OPS(wil6210_pm_runtime_suspend,
+			   wil6210_pm_runtime_resume,
+			   wil6210_pm_runtime_idle)
 };
 
 static struct pci_driver wil6210_driver = {
diff --git a/drivers/net/wireless/ath/wil6210/pm.c b/drivers/net/wireless/ath/wil6210/pm.c
index 8f5d1b44..153c1cf 100644
--- a/drivers/net/wireless/ath/wil6210/pm.c
+++ b/drivers/net/wireless/ath/wil6210/pm.c
@@ -16,15 +16,26 @@
 
 #include "wil6210.h"
 #include <linux/jiffies.h>
+#include <linux/pm_runtime.h>
+
+#define WIL6210_AUTOSUSPEND_DELAY_MS (1000)
 
 int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime)
 {
 	int rc = 0;
 	struct wireless_dev *wdev = wil->wdev;
 	struct net_device *ndev = wil_to_ndev(wil);
+	bool wmi_only = test_bit(WMI_FW_CAPABILITY_WMI_ONLY,
+				 wil->fw_capabilities);
 
 	wil_dbg_pm(wil, "can_suspend: %s\n", is_runtime ? "runtime" : "system");
 
+	if (wmi_only || debug_fw) {
+		wil_dbg_pm(wil, "Deny any suspend - %s mode\n",
+			   wmi_only ? "wmi_only" : "debug_fw");
+		rc = -EPERM;
+		goto out;
+	}
 	if (!(ndev->flags & IFF_UP)) {
 		/* can always sleep when down */
 		wil_dbg_pm(wil, "Interface is down\n");
@@ -44,6 +55,10 @@
 	/* interface is running */
 	switch (wdev->iftype) {
 	case NL80211_IFTYPE_MONITOR:
+		wil_dbg_pm(wil, "Sniffer\n");
+		rc = -EBUSY;
+		goto out;
+	/* for STA-like interface, don't runtime suspend */
 	case NL80211_IFTYPE_STATION:
 	case NL80211_IFTYPE_P2P_CLIENT:
 		if (test_bit(wil_status_fwconnecting, wil->status)) {
@@ -51,6 +66,12 @@
 			rc = -EBUSY;
 			goto out;
 		}
+		/* Runtime pm not supported in case the interface is up */
+		if (is_runtime) {
+			wil_dbg_pm(wil, "STA-like interface\n");
+			rc = -EBUSY;
+			goto out;
+		}
 		break;
 	/* AP-like interface - can't suspend */
 	default:
@@ -120,6 +141,13 @@
 
 	/* Prevent handling of new tx and wmi commands */
 	set_bit(wil_status_suspending, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the suspend */
+		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+		clear_bit(wil_status_suspending, wil->status);
+		wil->suspend_stats.rejected_by_host++;
+		return -EBUSY;
+	}
 	wil_update_net_queues_bh(wil, NULL, true);
 
 	if (!wil_is_tx_idle(wil)) {
@@ -230,6 +258,15 @@
 
 	wil_dbg_pm(wil, "suspend radio off\n");
 
+	set_bit(wil_status_suspending, wil->status);
+	if (test_bit(wil_status_collecting_dumps, wil->status)) {
+		/* Device collects crash dump, cancel the suspend */
+		wil_dbg_pm(wil, "reject suspend while collecting crash dump\n");
+		clear_bit(wil_status_suspending, wil->status);
+		wil->suspend_stats.rejected_by_host++;
+		return -EBUSY;
+	}
+
 	/* if netif up, hardware is alive, shut it down */
 	if (ndev->flags & IFF_UP) {
 		rc = wil_down(wil);
@@ -254,6 +291,7 @@
 	set_bit(wil_status_suspended, wil->status);
 
 out:
+	clear_bit(wil_status_suspending, wil->status);
 	wil_dbg_pm(wil, "suspend radio off: %d\n", rc);
 
 	return rc;
@@ -348,3 +386,44 @@
 		   is_runtime ? "runtime" : "system", rc, suspend_time_usec);
 	return rc;
 }
+
+void wil_pm_runtime_allow(struct wil6210_priv *wil)
+{
+	struct device *dev = wil_to_dev(wil);
+
+	pm_runtime_put_noidle(dev);
+	pm_runtime_set_autosuspend_delay(dev, WIL6210_AUTOSUSPEND_DELAY_MS);
+	pm_runtime_use_autosuspend(dev);
+	pm_runtime_allow(dev);
+}
+
+void wil_pm_runtime_forbid(struct wil6210_priv *wil)
+{
+	struct device *dev = wil_to_dev(wil);
+
+	pm_runtime_forbid(dev);
+	pm_runtime_get_noresume(dev);
+}
+
+int wil_pm_runtime_get(struct wil6210_priv *wil)
+{
+	int rc;
+	struct device *dev = wil_to_dev(wil);
+
+	rc = pm_runtime_get_sync(dev);
+	if (rc < 0) {
+		wil_err(wil, "pm_runtime_get_sync() failed, rc = %d\n", rc);
+		pm_runtime_put_noidle(dev);
+		return rc;
+	}
+
+	return 0;
+}
+
+void wil_pm_runtime_put(struct wil6210_priv *wil)
+{
+	struct device *dev = wil_to_dev(wil);
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+}
diff --git a/drivers/net/wireless/ath/wil6210/sysfs.c b/drivers/net/wireless/ath/wil6210/sysfs.c
index b91bf51..7c9a790 100644
--- a/drivers/net/wireless/ath/wil6210/sysfs.c
+++ b/drivers/net/wireless/ath/wil6210/sysfs.c
@@ -268,10 +268,49 @@
 		   wil_fst_link_loss_sysfs_show,
 		   wil_fst_link_loss_sysfs_store);
 
+static ssize_t
+wil_snr_thresh_sysfs_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct wil6210_priv *wil = dev_get_drvdata(dev);
+	ssize_t len = 0;
+
+	if (wil->snr_thresh.enabled)
+		len = snprintf(buf, PAGE_SIZE, "omni=%d, direct=%d\n",
+			       wil->snr_thresh.omni, wil->snr_thresh.direct);
+
+	return len;
+}
+
+static ssize_t
+wil_snr_thresh_sysfs_store(struct device *dev,
+			   struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct wil6210_priv *wil = dev_get_drvdata(dev);
+	int rc;
+	short omni, direct;
+
+	/* to disable snr threshold, set both omni and direct to 0 */
+	if (sscanf(buf, "%hd %hd", &omni, &direct) != 2)
+		return -EINVAL;
+
+	rc = wmi_set_snr_thresh(wil, omni, direct);
+	if (!rc)
+		rc = count;
+
+	return rc;
+}
+
+static DEVICE_ATTR(snr_thresh, 0644,
+		   wil_snr_thresh_sysfs_show,
+		   wil_snr_thresh_sysfs_store);
+
 static struct attribute *wil6210_sysfs_entries[] = {
 	&dev_attr_ftm_txrx_offset.attr,
 	&dev_attr_thermal_throttling.attr,
 	&dev_attr_fst_link_loss.attr,
+	&dev_attr_snr_thresh.attr,
 	NULL
 };
 
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index b4ca4e3..bb43f3f 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -165,6 +165,7 @@
 #define RGF_USER_USAGE_8		(0x880020)
 	#define BIT_USER_PREVENT_DEEP_SLEEP	BIT(0)
 	#define BIT_USER_SUPPORT_T_POWER_ON_0	BIT(1)
+	#define BIT_USER_EXT_CLK		BIT(2)
 #define RGF_USER_HW_MACHINE_STATE	(0x8801dc)
 	#define HW_MACHINE_BOOT_DONE	(0x3fffffd)
 #define RGF_USER_USER_CPU_0		(0x8801e0)
@@ -272,6 +273,7 @@
 	#define BIT_DMA_PSEUDO_CAUSE_MISC	BIT(2)
 
 #define RGF_HP_CTRL			(0x88265c)
+#define RGF_PAL_UNIT_ICR		(0x88266c) /* struct RGF_ICR */
 #define RGF_PCIE_LOS_COUNTER_CTL	(0x882dc4)
 
 /* MAC timer, usec, for packet lifetime */
@@ -444,6 +446,7 @@
 	wil_status_suspending, /* suspend in progress */
 	wil_status_suspended, /* suspend completed, device is suspended */
 	wil_status_resuming, /* resume in progress */
+	wil_status_collecting_dumps, /* crashdump collection in progress */
 	wil_status_last /* keep last */
 };
 
@@ -620,6 +623,16 @@
 	u32 off_ms;
 };
 
+struct wil_debugfs_iomem_data {
+	void *offset;
+	struct wil6210_priv *wil;
+};
+
+struct wil_debugfs_data {
+	struct wil_debugfs_iomem_data *data_arr;
+	int iomem_data_count;
+};
+
 extern struct blink_on_off_time led_blink_time[WIL_LED_TIME_LAST];
 extern u8 led_id;
 extern u8 led_polarity;
@@ -637,6 +650,7 @@
 	const char *wil_fw_name;
 	DECLARE_BITMAP(hw_capabilities, hw_capability_last);
 	DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
+	DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
 	u8 n_mids; /* number of additional MIDs as reported by FW */
 	u32 recovery_count; /* num of FW recovery attempts in a short time */
 	u32 recovery_state; /* FW recovery state machine */
@@ -712,6 +726,7 @@
 	u8 abft_len;
 	u8 wakeup_trigger;
 	struct wil_suspend_stats suspend_stats;
+	struct wil_debugfs_data dbg_data;
 
 	void *platform_handle;
 	struct wil_platform_ops platform_ops;
@@ -736,6 +751,11 @@
 	struct wil_ftm_priv ftm;
 	bool tt_data_set;
 	struct wmi_tt_data tt_data;
+	struct {
+		bool enabled;
+		short omni;
+		short direct;
+	} snr_thresh;
 
 	int fw_calib_result;
 
@@ -869,10 +889,12 @@
 int __wil_up(struct wil6210_priv *wil);
 int wil_down(struct wil6210_priv *wil);
 int __wil_down(struct wil6210_priv *wil);
+void wil_refresh_fw_capabilities(struct wil6210_priv *wil);
 void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r);
 int wil_find_cid(struct wil6210_priv *wil, const u8 *mac);
 void wil_set_ethtoolops(struct net_device *ndev);
 
+void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr, u32 size);
 void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr);
 void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr);
 int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr,
@@ -951,8 +973,13 @@
 			 struct cfg80211_mgmt_tx_params *params,
 			 u64 *cookie);
 
+#if defined(CONFIG_WIL6210_DEBUGFS)
 int wil6210_debugfs_init(struct wil6210_priv *wil);
 void wil6210_debugfs_remove(struct wil6210_priv *wil);
+#else
+static inline int wil6210_debugfs_init(struct wil6210_priv *wil) { return 0; }
+static inline void wil6210_debugfs_remove(struct wil6210_priv *wil) {}
+#endif
 int wil6210_sysfs_init(struct wil6210_priv *wil);
 void wil6210_sysfs_remove(struct wil6210_priv *wil);
 int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
@@ -1008,6 +1035,11 @@
 			 bool load);
 bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name);
 
+void wil_pm_runtime_allow(struct wil6210_priv *wil);
+void wil_pm_runtime_forbid(struct wil6210_priv *wil);
+int wil_pm_runtime_get(struct wil6210_priv *wil);
+void wil_pm_runtime_put(struct wil6210_priv *wil);
+
 int wil_can_suspend(struct wil6210_priv *wil, bool is_runtime);
 int wil_suspend(struct wil6210_priv *wil, bool is_runtime);
 int wil_resume(struct wil6210_priv *wil, bool is_runtime);
@@ -1043,4 +1075,5 @@
 				const u8 *addr,
 				bool fst_link_loss);
 
+int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct);
 #endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
index e53cf0c..1ed3306 100644
--- a/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
+++ b/drivers/net/wireless/ath/wil6210/wil_crash_dump.c
@@ -72,6 +72,15 @@
 		return -EINVAL;
 	}
 
+	set_bit(wil_status_collecting_dumps, wil->status);
+	if (test_bit(wil_status_suspending, wil->status) ||
+	    test_bit(wil_status_suspended, wil->status) ||
+	    test_bit(wil_status_resetting, wil->status)) {
+		wil_err(wil, "cannot collect fw dump during suspend/reset\n");
+		clear_bit(wil_status_collecting_dumps, wil->status);
+		return -EINVAL;
+	}
+
 	/* copy to crash dump area */
 	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) {
 		map = &fw_mapping[i];
@@ -91,6 +100,8 @@
 				     (const void __iomem * __force)data, len);
 	}
 
+	clear_bit(wil_status_collecting_dumps, wil->status);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.h b/drivers/net/wireless/ath/wil6210/wil_platform.h
index 621005b..620a1b3 100644
--- a/drivers/net/wireless/ath/wil6210/wil_platform.h
+++ b/drivers/net/wireless/ath/wil6210/wil_platform.h
@@ -27,6 +27,18 @@
 	WIL_PLATFORM_EVT_POST_SUSPEND = 4,
 };
 
+enum wil_platform_features {
+	WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL = 0,
+	WIL_PLATFORM_FEATURE_MAX,
+};
+
+enum wil_platform_capa {
+	WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND = 0,
+	WIL_PLATFORM_CAPA_T_PWR_ON_0 = 1,
+	WIL_PLATFORM_CAPA_EXT_CLK = 2,
+	WIL_PLATFORM_CAPA_MAX,
+};
+
 /**
  * struct wil_platform_ops - wil platform module calls from this
  * driver to platform driver
@@ -37,7 +49,8 @@
 	int (*resume)(void *handle, bool device_powered_on);
 	void (*uninit)(void *handle);
 	int (*notify)(void *handle, enum wil_platform_event evt);
-	bool (*keep_radio_on_during_sleep)(void *handle);
+	int (*get_capa)(void *handle);
+	void (*set_features)(void *handle, int features);
 };
 
 /**
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 43cdaef..9520c39 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -141,13 +141,15 @@
 /**
  * Check address validity for WMI buffer; remap if needed
  * @ptr - internal (linker) fw/ucode address
+ * @size - if non zero, validate the block does not
+ *  exceed the device memory (bar)
  *
  * Valid buffer should be DWORD aligned
  *
  * return address for accessing buffer from the host;
  * if buffer is not valid, return NULL.
  */
-void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
+void __iomem *wmi_buffer_block(struct wil6210_priv *wil, __le32 ptr_, u32 size)
 {
 	u32 off;
 	u32 ptr = le32_to_cpu(ptr_);
@@ -162,10 +164,17 @@
 	off = HOSTADDR(ptr);
 	if (off > wil->bar_size - 4)
 		return NULL;
+	if (size && ((off + size > wil->bar_size) || (off + size < off)))
+		return NULL;
 
 	return wil->csr + off;
 }
 
+void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_)
+{
+	return wmi_buffer_block(wil, ptr_, 0);
+}
+
 /**
  * Check address validity
  */
@@ -223,7 +232,7 @@
 	uint retry;
 	int rc = 0;
 
-	if (sizeof(cmd) + len > r->entry_size) {
+	if (len > r->entry_size - sizeof(cmd)) {
 		wil_err(wil, "WMI size too large: %d bytes, max is %d\n",
 			(int)(sizeof(cmd) + len), r->entry_size);
 		return -ERANGE;
@@ -369,7 +378,7 @@
 	s32 signal;
 	__le16 fc;
 	u32 d_len;
-	u16 d_status;
+	s16 snr;
 
 	if (flen < 0) {
 		wil_err(wil, "MGMT Rx: short event, len %d\n", len);
@@ -391,13 +400,13 @@
 		signal = 100 * data->info.rssi;
 	else
 		signal = data->info.sqi;
-	d_status = le16_to_cpu(data->info.status);
+	snr = le16_to_cpu(data->info.snr); /* 1/4 dB units */
 	fc = rx_mgmt_frame->frame_control;
 
 	wil_dbg_wmi(wil, "MGMT Rx: channel %d MCS %d RSSI %d SQI %d%%\n",
 		    data->info.channel, data->info.mcs, data->info.rssi,
 		    data->info.sqi);
-	wil_dbg_wmi(wil, "status 0x%04x len %d fc 0x%04x\n", d_status, d_len,
+	wil_dbg_wmi(wil, "snr %ddB len %d fc 0x%04x\n", snr / 4, d_len,
 		    le16_to_cpu(fc));
 	wil_dbg_wmi(wil, "qid %d mid %d cid %d\n",
 		    data->info.qid, data->info.mid, data->info.cid);
@@ -425,6 +434,11 @@
 
 		wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
 
+		if (wil->snr_thresh.enabled && snr < wil->snr_thresh.omni) {
+			wil_dbg_wmi(wil, "snr below threshold. dropping\n");
+			return;
+		}
+
 		bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
 						d_len, signal, GFP_KERNEL);
 		if (bss) {
@@ -1412,8 +1426,14 @@
 	};
 	int rc;
 	u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len;
-	struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL);
+	struct wmi_set_appie_cmd *cmd;
 
+	if (len < ie_len) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	cmd = kzalloc(len, GFP_KERNEL);
 	if (!cmd) {
 		rc = -ENOMEM;
 		goto out;
@@ -2150,3 +2170,32 @@
 	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
 	return rc;
 }
+
+int wmi_set_snr_thresh(struct wil6210_priv *wil, short omni, short direct)
+{
+	int rc;
+	struct wmi_set_connect_snr_thr_cmd cmd = {
+		.enable = true,
+		.omni_snr_thr = cpu_to_le16(omni),
+		.direct_snr_thr = cpu_to_le16(direct),
+	};
+
+	if (!test_bit(WMI_FW_CAPABILITY_CONNECT_SNR_THR, wil->fw_capabilities))
+		return -ENOTSUPP;
+
+	if (omni == 0 && direct == 0)
+		cmd.enable = false;
+
+	wil_dbg_wmi(wil, "%s snr thresh omni=%d, direct=%d (1/4 dB units)\n",
+		    cmd.enable ? "enable" : "disable", omni, direct);
+
+	rc = wmi_send(wil, WMI_SET_CONNECT_SNR_THR_CMDID, &cmd, sizeof(cmd));
+	if (rc)
+		return rc;
+
+	wil->snr_thresh.enabled = cmd.enable;
+	wil->snr_thresh.omni = omni;
+	wil->snr_thresh.direct = direct;
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index 1b426d7..809e320 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -36,6 +36,11 @@
 #define WMI_PROX_RANGE_NUM		(3)
 #define WMI_MAX_LOSS_DMG_BEACONS	(20)
 #define MAX_NUM_OF_SECTORS		(128)
+#define WMI_SCHED_MAX_ALLOCS_PER_CMD	(4)
+#define WMI_RF_DTYPE_LENGTH		(3)
+#define WMI_RF_ETYPE_LENGTH		(3)
+#define WMI_RF_RX2TX_LENGTH		(3)
+#define WMI_RF_ETYPE_VAL_PER_RANGE	(5)
 
 /* Mailbox interface
  * used for commands and events
@@ -52,15 +57,22 @@
  * the host
  */
 enum wmi_fw_capability {
-	WMI_FW_CAPABILITY_FTM			= 0,
-	WMI_FW_CAPABILITY_PS_CONFIG		= 1,
-	WMI_FW_CAPABILITY_RF_SECTORS		= 2,
-	WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT	= 3,
-	WMI_FW_CAPABILITY_DISABLE_AP_SME	= 4,
-	WMI_FW_CAPABILITY_WMI_ONLY		= 5,
-	WMI_FW_CAPABILITY_THERMAL_THROTTLING	= 7,
-	WMI_FW_CAPABILITY_D3_SUSPEND		= 8,
-	WMI_FW_CAPABILITY_RSSI_REPORTING	= 12,
+	WMI_FW_CAPABILITY_FTM				= 0,
+	WMI_FW_CAPABILITY_PS_CONFIG			= 1,
+	WMI_FW_CAPABILITY_RF_SECTORS			= 2,
+	WMI_FW_CAPABILITY_MGMT_RETRY_LIMIT		= 3,
+	WMI_FW_CAPABILITY_DISABLE_AP_SME		= 4,
+	WMI_FW_CAPABILITY_WMI_ONLY			= 5,
+	WMI_FW_CAPABILITY_THERMAL_THROTTLING		= 7,
+	WMI_FW_CAPABILITY_D3_SUSPEND			= 8,
+	WMI_FW_CAPABILITY_LONG_RANGE			= 9,
+	WMI_FW_CAPABILITY_FIXED_SCHEDULING		= 10,
+	WMI_FW_CAPABILITY_MULTI_DIRECTED_OMNIS		= 11,
+	WMI_FW_CAPABILITY_RSSI_REPORTING		= 12,
+	WMI_FW_CAPABILITY_SET_SILENT_RSSI_TABLE		= 13,
+	WMI_FW_CAPABILITY_LO_POWER_CALIB_FROM_OTP	= 14,
+	WMI_FW_CAPABILITY_CONNECT_SNR_THR		= 16,
+	WMI_FW_CAPABILITY_REF_CLOCK_CONTROL		= 18,
 	WMI_FW_CAPABILITY_MAX,
 };
 
@@ -80,6 +92,7 @@
 	WMI_START_SCAN_CMDID				= 0x07,
 	WMI_SET_BSS_FILTER_CMDID			= 0x09,
 	WMI_SET_PROBED_SSID_CMDID			= 0x0A,
+	/* deprecated */
 	WMI_SET_LISTEN_INT_CMDID			= 0x0B,
 	WMI_BCON_CTRL_CMDID				= 0x0F,
 	WMI_ADD_CIPHER_KEY_CMDID			= 0x16,
@@ -94,26 +107,28 @@
 	WMI_ECHO_CMDID					= 0x803,
 	WMI_DEEP_ECHO_CMDID				= 0x804,
 	WMI_CONFIG_MAC_CMDID				= 0x805,
+	/* deprecated */
 	WMI_CONFIG_PHY_DEBUG_CMDID			= 0x806,
 	WMI_ADD_DEBUG_TX_PCKT_CMDID			= 0x808,
 	WMI_PHY_GET_STATISTICS_CMDID			= 0x809,
+	/* deprecated */
 	WMI_FS_TUNE_CMDID				= 0x80A,
+	/* deprecated */
 	WMI_CORR_MEASURE_CMDID				= 0x80B,
 	WMI_READ_RSSI_CMDID				= 0x80C,
 	WMI_TEMP_SENSE_CMDID				= 0x80E,
 	WMI_DC_CALIB_CMDID				= 0x80F,
+	/* deprecated */
 	WMI_SEND_TONE_CMDID				= 0x810,
+	/* deprecated */
 	WMI_IQ_TX_CALIB_CMDID				= 0x811,
+	/* deprecated */
 	WMI_IQ_RX_CALIB_CMDID				= 0x812,
-	WMI_SET_UCODE_IDLE_CMDID			= 0x813,
 	WMI_SET_WORK_MODE_CMDID				= 0x815,
 	WMI_LO_LEAKAGE_CALIB_CMDID			= 0x816,
-	WMI_MARLON_R_READ_CMDID				= 0x818,
-	WMI_MARLON_R_WRITE_CMDID			= 0x819,
-	WMI_MARLON_R_TXRX_SEL_CMDID			= 0x81A,
-	MAC_IO_STATIC_PARAMS_CMDID			= 0x81B,
-	MAC_IO_DYNAMIC_PARAMS_CMDID			= 0x81C,
+	WMI_LO_POWER_CALIB_FROM_OTP_CMDID		= 0x817,
 	WMI_SILENT_RSSI_CALIB_CMDID			= 0x81D,
+	/* deprecated */
 	WMI_RF_RX_TEST_CMDID				= 0x81E,
 	WMI_CFG_RX_CHAIN_CMDID				= 0x820,
 	WMI_VRING_CFG_CMDID				= 0x821,
@@ -127,11 +142,6 @@
 	WMI_SET_PCP_CHANNEL_CMDID			= 0x829,
 	WMI_GET_PCP_CHANNEL_CMDID			= 0x82A,
 	WMI_SW_TX_REQ_CMDID				= 0x82B,
-	WMI_READ_MAC_RXQ_CMDID				= 0x830,
-	WMI_READ_MAC_TXQ_CMDID				= 0x831,
-	WMI_WRITE_MAC_RXQ_CMDID				= 0x832,
-	WMI_WRITE_MAC_TXQ_CMDID				= 0x833,
-	WMI_WRITE_MAC_XQ_FIELD_CMDID			= 0x834,
 	WMI_MLME_PUSH_CMDID				= 0x835,
 	WMI_BEAMFORMING_MGMT_CMDID			= 0x836,
 	WMI_BF_TXSS_MGMT_CMDID				= 0x837,
@@ -145,9 +155,13 @@
 	WMI_MAINTAIN_RESUME_CMDID			= 0x851,
 	WMI_RS_MGMT_CMDID				= 0x852,
 	WMI_RF_MGMT_CMDID				= 0x853,
-	WMI_OTP_READ_CMDID				= 0x856,
-	WMI_OTP_WRITE_CMDID				= 0x857,
+	WMI_RF_XPM_READ_CMDID				= 0x856,
+	WMI_RF_XPM_WRITE_CMDID				= 0x857,
 	WMI_LED_CFG_CMDID				= 0x858,
+	WMI_SET_CONNECT_SNR_THR_CMDID			= 0x85B,
+	WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID		= 0x85C,
+	WMI_RF_PWR_ON_DELAY_CMDID			= 0x85D,
+	WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID		= 0x85E,
 	/* Performance monitoring commands */
 	WMI_BF_CTRL_CMDID				= 0x862,
 	WMI_NOTIFY_REQ_CMDID				= 0x863,
@@ -155,7 +169,6 @@
 	WMI_GET_RF_STATUS_CMDID				= 0x866,
 	WMI_GET_BASEBAND_TYPE_CMDID			= 0x867,
 	WMI_UNIT_TEST_CMDID				= 0x900,
-	WMI_HICCUP_CMDID				= 0x901,
 	WMI_FLASH_READ_CMDID				= 0x902,
 	WMI_FLASH_WRITE_CMDID				= 0x903,
 	/* Power management */
@@ -175,16 +188,6 @@
 	WMI_GET_PCP_FACTOR_CMDID			= 0x91B,
 	/* Power Save Configuration Commands */
 	WMI_PS_DEV_PROFILE_CFG_CMDID			= 0x91C,
-	/* Not supported yet */
-	WMI_PS_DEV_CFG_CMDID				= 0x91D,
-	/* Not supported yet */
-	WMI_PS_DEV_CFG_READ_CMDID			= 0x91E,
-	/* Per MAC Power Save Configuration commands
-	 * Not supported yet
-	 */
-	WMI_PS_MID_CFG_CMDID				= 0x91F,
-	/* Not supported yet */
-	WMI_PS_MID_CFG_READ_CMDID			= 0x920,
 	WMI_RS_CFG_CMDID				= 0x921,
 	WMI_GET_DETAILED_RS_RES_CMDID			= 0x922,
 	WMI_AOA_MEAS_CMDID				= 0x923,
@@ -195,13 +198,16 @@
 	WMI_DEL_STA_CMDID				= 0x936,
 	WMI_SET_THERMAL_THROTTLING_CFG_CMDID		= 0x940,
 	WMI_GET_THERMAL_THROTTLING_CFG_CMDID		= 0x941,
+	/* Read Power Save profile type */
+	WMI_PS_DEV_PROFILE_CFG_READ_CMDID		= 0x942,
 	WMI_TOF_SESSION_START_CMDID			= 0x991,
 	WMI_TOF_GET_CAPABILITIES_CMDID			= 0x992,
 	WMI_TOF_SET_LCR_CMDID				= 0x993,
 	WMI_TOF_SET_LCI_CMDID				= 0x994,
-	WMI_TOF_CHANNEL_INFO_CMDID			= 0x995,
+	WMI_TOF_CFG_RESPONDER_CMDID			= 0x996,
 	WMI_TOF_SET_TX_RX_OFFSET_CMDID			= 0x997,
 	WMI_TOF_GET_TX_RX_OFFSET_CMDID			= 0x998,
+	WMI_TOF_CHANNEL_INFO_CMDID			= 0x999,
 	WMI_GET_RF_SECTOR_PARAMS_CMDID			= 0x9A0,
 	WMI_SET_RF_SECTOR_PARAMS_CMDID			= 0x9A1,
 	WMI_GET_SELECTED_RF_SECTOR_INDEX_CMDID		= 0x9A2,
@@ -210,12 +216,20 @@
 	WMI_PRIO_TX_SECTORS_ORDER_CMDID			= 0x9A5,
 	WMI_PRIO_TX_SECTORS_NUMBER_CMDID		= 0x9A6,
 	WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_CMDID	= 0x9A7,
+	WMI_SCHEDULING_SCHEME_CMDID			= 0xA01,
+	WMI_FIXED_SCHEDULING_CONFIG_CMDID		= 0xA02,
+	WMI_ENABLE_FIXED_SCHEDULING_CMDID		= 0xA03,
+	WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID	= 0xA04,
+	WMI_SET_LONG_RANGE_CONFIG_CMDID			= 0xA05,
 	WMI_SET_MAC_ADDRESS_CMDID			= 0xF003,
 	WMI_ABORT_SCAN_CMDID				= 0xF007,
 	WMI_SET_PROMISCUOUS_MODE_CMDID			= 0xF041,
+	/* deprecated */
 	WMI_GET_PMK_CMDID				= 0xF048,
 	WMI_SET_PASSPHRASE_CMDID			= 0xF049,
+	/* deprecated */
 	WMI_SEND_ASSOC_RES_CMDID			= 0xF04A,
+	/* deprecated */
 	WMI_SET_ASSOC_REQ_RELAY_CMDID			= 0xF04B,
 	WMI_MAC_ADDR_REQ_CMDID				= 0xF04D,
 	WMI_FW_VER_CMDID				= 0xF04E,
@@ -441,11 +455,6 @@
 	__le32 rf_mgmt_type;
 } __packed;
 
-/* WMI_RF_RX_TEST_CMDID */
-struct wmi_rf_rx_test_cmd {
-	__le32 sector;
-} __packed;
-
 /* WMI_CORR_MEASURE_CMDID */
 struct wmi_corr_measure_cmd {
 	__le32 freq_mhz;
@@ -658,6 +667,20 @@
 	struct wmi_bcast_vring_cfg vring_cfg;
 } __packed;
 
+/* WMI_LO_POWER_CALIB_FROM_OTP_CMDID */
+struct wmi_lo_power_calib_from_otp_cmd {
+	/* index to read from OTP. zero based */
+	u8 index;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_LO_POWER_CALIB_FROM_OTP_EVENTID */
+struct wmi_lo_power_calib_from_otp_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
 /* WMI_VRING_BA_EN_CMDID */
 struct wmi_vring_ba_en_cmd {
 	u8 ringid;
@@ -693,6 +716,24 @@
 	WMI_SNIFFER_ON	= 0x01,
 };
 
+/* WMI_SILENT_RSSI_TABLE */
+enum wmi_silent_rssi_table {
+	RF_TEMPERATURE_CALIB_DEFAULT_DB		= 0x00,
+	RF_TEMPERATURE_CALIB_HIGH_POWER_DB	= 0x01,
+};
+
+/* WMI_SILENT_RSSI_STATUS */
+enum wmi_silent_rssi_status {
+	SILENT_RSSI_SUCCESS	= 0x00,
+	SILENT_RSSI_FAILURE	= 0x01,
+};
+
+/* WMI_SET_ACTIVE_SILENT_RSSI_TABLE_CMDID */
+struct wmi_set_active_silent_rssi_table_cmd {
+	/* enum wmi_silent_rssi_table */
+	__le32 table;
+} __packed;
+
 enum wmi_sniffer_cfg_phy_info_mode {
 	WMI_SNIFFER_PHY_INFO_DISABLED	= 0x00,
 	WMI_SNIFFER_PHY_INFO_ENABLED	= 0x01,
@@ -836,18 +877,85 @@
 	__le32 value;
 } __packed;
 
-/* WMI_OTP_READ_CMDID */
-struct wmi_otp_read_cmd {
-	__le32 addr;
-	__le32 size;
-	__le32 values;
+/* WMI_RF_PWR_ON_DELAY_CMDID
+ * set FW time parameters used through RF resetting
+ *  RF reset consists of bringing its power down for a period of time, then
+ * bringing the power up
+ * Returned event: WMI_RF_PWR_ON_DELAY_RSP_EVENTID
+ */
+struct wmi_rf_pwr_on_delay_cmd {
+	/* time in usec the FW waits after bringing the RF PWR down,
+	 * set 0 for default
+	 */
+	__le16 down_delay_usec;
+	/* time in usec the FW waits after bringing the RF PWR up,
+	 * set 0 for default
+	 */
+	__le16 up_delay_usec;
 } __packed;
 
-/* WMI_OTP_WRITE_CMDID */
-struct wmi_otp_write_cmd {
-	__le32 addr;
-	__le32 size;
-	__le32 values;
+/* \WMI_SET_HIGH_POWER_TABLE_PARAMS_CMDID
+ * This API controls the Tx and Rx gain over temperature.
+ * It controls the Tx D-type, Rx D-type and Rx E-type amplifiers.
+ * It also controls the Tx gain index, by controlling the Rx to Tx gain index
+ * offset.
+ * The control is divided by 3 temperature values to 4 temperature ranges.
+ * Each parameter uses its own temperature values.
+ * Returned event: WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID
+ */
+struct wmi_set_high_power_table_params_cmd {
+	/* Temperature range for Tx D-type parameters */
+	u8 tx_dtype_temp[WMI_RF_DTYPE_LENGTH];
+	u8 reserved0;
+	/* Tx D-type values to be used for each temperature range */
+	__le32 tx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1];
+	/* Temperature range for Rx D-type parameters */
+	u8 rx_dtype_temp[WMI_RF_DTYPE_LENGTH];
+	u8 reserved1;
+	/* Rx D-type values to be used for each temperature range */
+	__le32 rx_dtype_conf[WMI_RF_DTYPE_LENGTH + 1];
+	/* Temperature range for Rx E-type parameters */
+	u8 rx_etype_temp[WMI_RF_ETYPE_LENGTH];
+	u8 reserved2;
+	/* Rx E-type values to be used for each temperature range.
+	 * The last 4 values of any range are the first 4 values of the next
+	 * range and so on
+	 */
+	__le32 rx_etype_conf[WMI_RF_ETYPE_VAL_PER_RANGE + WMI_RF_ETYPE_LENGTH];
+	/* Temperature range for rx_2_tx_offs parameters */
+	u8 rx_2_tx_temp[WMI_RF_RX2TX_LENGTH];
+	u8 reserved3;
+	/* Rx to Tx gain index offset */
+	s8 rx_2_tx_offs[WMI_RF_RX2TX_LENGTH + 1];
+} __packed;
+
+/* CMD: WMI_RF_XPM_READ_CMDID */
+struct wmi_rf_xpm_read_cmd {
+	u8 rf_id;
+	u8 reserved[3];
+	/* XPM bit start address in range [0,8191]bits - rounded by FW to
+	 * multiple of 8bits
+	 */
+	__le32 xpm_bit_address;
+	__le32 num_bytes;
+} __packed;
+
+/* CMD: WMI_RF_XPM_WRITE_CMDID */
+struct wmi_rf_xpm_write_cmd {
+	u8 rf_id;
+	u8 reserved0[3];
+	/* XPM bit start address in range [0,8191]bits - rounded by FW to
+	 * multiple of 8bits
+	 */
+	__le32 xpm_bit_address;
+	__le32 num_bytes;
+	/* boolean flag indicating whether FW should verify the write
+	 * operation
+	 */
+	u8 verify;
+	u8 reserved1[3];
+	/* actual size=num_bytes */
+	u8 data_bytes[0];
 } __packed;
 
 /* WMI_TEMP_SENSE_CMDID
@@ -990,19 +1098,26 @@
 	 */
 	__le16 burst_period;
 	u8 dst_mac[WMI_MAC_LEN];
-	__le16 reserved;
+	u8 reserved;
+	u8 num_burst_per_aoa_meas;
 } __packed;
 
 /* WMI_TOF_SESSION_START_CMDID */
 struct wmi_tof_session_start_cmd {
 	__le32 session_id;
-	u8 num_of_aoa_measures;
+	u8 reserved1;
 	u8 aoa_type;
 	__le16 num_of_dest;
 	u8 reserved[4];
 	struct wmi_ftm_dest_info ftm_dest_info[0];
 } __packed;
 
+/* WMI_TOF_CFG_RESPONDER_CMDID */
+struct wmi_tof_cfg_responder_cmd {
+	u8 enable;
+	u8 reserved[3];
+} __packed;
+
 enum wmi_tof_channel_info_report_type {
 	WMI_TOF_CHANNEL_INFO_TYPE_CIR			= 0x1,
 	WMI_TOF_CHANNEL_INFO_TYPE_RSSI			= 0x2,
@@ -1023,7 +1138,99 @@
 	__le32 tx_offset;
 	/* RX delay offset */
 	__le32 rx_offset;
-	__le32 reserved[2];
+	/* Mask to define which RFs to configure. 0 means all RFs */
+	__le32 rf_mask;
+	/* Offset to strongest tap of CIR */
+	__le32 precursor;
+} __packed;
+
+/* WMI_TOF_GET_TX_RX_OFFSET_CMDID */
+struct wmi_tof_get_tx_rx_offset_cmd {
+	/* rf index to read offsets from */
+	u8 rf_index;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */
+struct wmi_map_mcs_to_schd_params {
+	u8 mcs;
+	/* time in usec from start slot to start tx flow - default 15 */
+	u8 time_in_usec_before_initiate_tx;
+	/* RD enable - if yes consider RD according to STA mcs */
+	u8 rd_enabled;
+	u8 reserved;
+	/* time in usec from start slot to stop vring */
+	__le16 time_in_usec_to_stop_vring;
+	/* timeout to force flush from start of slot */
+	__le16 flush_to_in_usec;
+	/* per mcs the mac buffer limit size in bytes */
+	__le32 mac_buff_size_in_bytes;
+} __packed;
+
+/* WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID */
+struct wmi_fixed_scheduling_config_complete_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
+#define WMI_NUM_MCS	(13)
+
+/* WMI_FIXED_SCHEDULING_CONFIG_CMDID */
+struct wmi_fixed_scheduling_config_cmd {
+	/* defaults in the SAS table */
+	struct wmi_map_mcs_to_schd_params mcs_to_schd_params_map[WMI_NUM_MCS];
+	/* default 150 uSec */
+	__le16 max_sta_rd_ppdu_duration_in_usec;
+	/* default 300 uSec */
+	__le16 max_sta_grant_ppdu_duration_in_usec;
+	/* default 1000 uSec */
+	__le16 assoc_slot_duration_in_usec;
+	/* default 360 uSec */
+	__le16 virtual_slot_duration_in_usec;
+	/* each this field value slots start with grant frame to the station
+	 * - default 2
+	 */
+	u8 number_of_ap_slots_for_initiate_grant;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_ENABLE_FIXED_SCHEDULING_CMDID */
+struct wmi_enable_fixed_scheduling_cmd {
+	__le32 reserved;
+} __packed;
+
+/* WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID */
+struct wmi_enable_fixed_scheduling_complete_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_CMDID */
+struct wmi_set_multi_directed_omnis_config_cmd {
+	/* number of directed omnis at destination AP */
+	u8 dest_ap_num_directed_omnis;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID */
+struct wmi_set_multi_directed_omnis_config_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_SET_LONG_RANGE_CONFIG_CMDID */
+struct wmi_set_long_range_config_cmd {
+	__le32 reserved;
+} __packed;
+
+/* WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID */
+struct wmi_set_long_range_config_complete_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
 } __packed;
 
 /* WMI Events
@@ -1039,19 +1246,22 @@
 	WMI_FW_READY_EVENTID				= 0x1801,
 	WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID		= 0x200,
 	WMI_ECHO_RSP_EVENTID				= 0x1803,
+	/* deprecated */
 	WMI_FS_TUNE_DONE_EVENTID			= 0x180A,
+	/* deprecated */
 	WMI_CORR_MEASURE_EVENTID			= 0x180B,
 	WMI_READ_RSSI_EVENTID				= 0x180C,
 	WMI_TEMP_SENSE_DONE_EVENTID			= 0x180E,
 	WMI_DC_CALIB_DONE_EVENTID			= 0x180F,
+	/* deprecated */
 	WMI_IQ_TX_CALIB_DONE_EVENTID			= 0x1811,
+	/* deprecated */
 	WMI_IQ_RX_CALIB_DONE_EVENTID			= 0x1812,
 	WMI_SET_WORK_MODE_DONE_EVENTID			= 0x1815,
 	WMI_LO_LEAKAGE_CALIB_DONE_EVENTID		= 0x1816,
-	WMI_MARLON_R_READ_DONE_EVENTID			= 0x1818,
-	WMI_MARLON_R_WRITE_DONE_EVENTID			= 0x1819,
-	WMI_MARLON_R_TXRX_SEL_DONE_EVENTID		= 0x181A,
+	WMI_LO_POWER_CALIB_FROM_OTP_EVENTID		= 0x1817,
 	WMI_SILENT_RSSI_CALIB_DONE_EVENTID		= 0x181D,
+	/* deprecated */
 	WMI_RF_RX_TEST_DONE_EVENTID			= 0x181E,
 	WMI_CFG_RX_CHAIN_DONE_EVENTID			= 0x1820,
 	WMI_VRING_CFG_DONE_EVENTID			= 0x1821,
@@ -1062,11 +1272,6 @@
 	WMI_GET_SSID_EVENTID				= 0x1828,
 	WMI_GET_PCP_CHANNEL_EVENTID			= 0x182A,
 	WMI_SW_TX_COMPLETE_EVENTID			= 0x182B,
-	WMI_READ_MAC_RXQ_EVENTID			= 0x1830,
-	WMI_READ_MAC_TXQ_EVENTID			= 0x1831,
-	WMI_WRITE_MAC_RXQ_EVENTID			= 0x1832,
-	WMI_WRITE_MAC_TXQ_EVENTID			= 0x1833,
-	WMI_WRITE_MAC_XQ_FIELD_EVENTID			= 0x1834,
 	WMI_BEAMFORMING_MGMT_DONE_EVENTID		= 0x1836,
 	WMI_BF_TXSS_MGMT_DONE_EVENTID			= 0x1837,
 	WMI_BF_RXSS_MGMT_DONE_EVENTID			= 0x1839,
@@ -1077,8 +1282,12 @@
 	WMI_TX_MGMT_PACKET_EVENTID			= 0x1841,
 	WMI_LINK_MAINTAIN_CFG_WRITE_DONE_EVENTID	= 0x1842,
 	WMI_LINK_MAINTAIN_CFG_READ_DONE_EVENTID		= 0x1843,
-	WMI_OTP_READ_RESULT_EVENTID			= 0x1856,
+	WMI_RF_XPM_READ_RESULT_EVENTID			= 0x1856,
+	WMI_RF_XPM_WRITE_RESULT_EVENTID			= 0x1857,
 	WMI_LED_CFG_DONE_EVENTID			= 0x1858,
+	WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID		= 0x185C,
+	WMI_RF_PWR_ON_DELAY_RSP_EVENTID			= 0x185D,
+	WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID		= 0x185E,
 	/* Performance monitoring events */
 	WMI_DATA_PORT_OPEN_EVENTID			= 0x1860,
 	WMI_WBE_LINK_DOWN_EVENTID			= 0x1861,
@@ -1107,14 +1316,6 @@
 	WMI_PCP_FACTOR_EVENTID				= 0x191A,
 	/* Power Save Configuration Events */
 	WMI_PS_DEV_PROFILE_CFG_EVENTID			= 0x191C,
-	/* Not supported yet */
-	WMI_PS_DEV_CFG_EVENTID				= 0x191D,
-	/* Not supported yet */
-	WMI_PS_DEV_CFG_READ_EVENTID			= 0x191E,
-	/* Not supported yet */
-	WMI_PS_MID_CFG_EVENTID				= 0x191F,
-	/* Not supported yet */
-	WMI_PS_MID_CFG_READ_EVENTID			= 0x1920,
 	WMI_RS_CFG_DONE_EVENTID				= 0x1921,
 	WMI_GET_DETAILED_RS_RES_EVENTID			= 0x1922,
 	WMI_AOA_MEAS_EVENTID				= 0x1923,
@@ -1123,14 +1324,17 @@
 	WMI_GET_MGMT_RETRY_LIMIT_EVENTID		= 0x1931,
 	WMI_SET_THERMAL_THROTTLING_CFG_EVENTID		= 0x1940,
 	WMI_GET_THERMAL_THROTTLING_CFG_EVENTID		= 0x1941,
+	/* return the Power Save profile */
+	WMI_PS_DEV_PROFILE_CFG_READ_EVENTID		= 0x1942,
 	WMI_TOF_SESSION_END_EVENTID			= 0x1991,
 	WMI_TOF_GET_CAPABILITIES_EVENTID		= 0x1992,
 	WMI_TOF_SET_LCR_EVENTID				= 0x1993,
 	WMI_TOF_SET_LCI_EVENTID				= 0x1994,
 	WMI_TOF_FTM_PER_DEST_RES_EVENTID		= 0x1995,
-	WMI_TOF_CHANNEL_INFO_EVENTID			= 0x1996,
+	WMI_TOF_CFG_RESPONDER_EVENTID			= 0x1996,
 	WMI_TOF_SET_TX_RX_OFFSET_EVENTID		= 0x1997,
 	WMI_TOF_GET_TX_RX_OFFSET_EVENTID		= 0x1998,
+	WMI_TOF_CHANNEL_INFO_EVENTID			= 0x1999,
 	WMI_GET_RF_SECTOR_PARAMS_DONE_EVENTID		= 0x19A0,
 	WMI_SET_RF_SECTOR_PARAMS_DONE_EVENTID		= 0x19A1,
 	WMI_GET_SELECTED_RF_SECTOR_INDEX_DONE_EVENTID	= 0x19A2,
@@ -1139,12 +1343,18 @@
 	WMI_PRIO_TX_SECTORS_ORDER_EVENTID		= 0x19A5,
 	WMI_PRIO_TX_SECTORS_NUMBER_EVENTID		= 0x19A6,
 	WMI_PRIO_TX_SECTORS_SET_DEFAULT_CFG_EVENTID	= 0x19A7,
+	WMI_SCHEDULING_SCHEME_EVENTID			= 0x1A01,
+	WMI_FIXED_SCHEDULING_CONFIG_COMPLETE_EVENTID	= 0x1A02,
+	WMI_ENABLE_FIXED_SCHEDULING_COMPLETE_EVENTID	= 0x1A03,
+	WMI_SET_MULTI_DIRECTED_OMNIS_CONFIG_EVENTID	= 0x1A04,
+	WMI_SET_LONG_RANGE_CONFIG_COMPLETE_EVENTID	= 0x1A05,
 	WMI_SET_CHANNEL_EVENTID				= 0x9000,
 	WMI_ASSOC_REQ_EVENTID				= 0x9001,
 	WMI_EAPOL_RX_EVENTID				= 0x9002,
 	WMI_MAC_ADDR_RESP_EVENTID			= 0x9003,
 	WMI_FW_VER_EVENTID				= 0x9004,
 	WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID		= 0x9005,
+	WMI_COMMAND_NOT_SUPPORTED_EVENTID		= 0xFFFF,
 };
 
 /* Events data structures */
@@ -1201,7 +1411,7 @@
 	__le32 bl_minor;
 	__le32 bl_subminor;
 	__le32 bl_build;
-	/* The number of entries in the FW capabilies array */
+	/* The number of entries in the FW capabilities array */
 	u8 fw_capabilities_len;
 	u8 reserved[3];
 	/* FW capabilities info
@@ -1246,7 +1456,9 @@
 	__le32 board_file_platform_type;
 	/* board file version */
 	__le32 board_file_version;
-	__le32 reserved[2];
+	/* enabled XIFs bit vector */
+	__le32 enabled_xif_vector;
+	__le32 reserved;
 } __packed;
 
 /* WMI_GET_BASEBAND_TYPE_EVENTID */
@@ -1300,6 +1512,7 @@
 	/* enum wmi_phy_capability */
 	u8 phy_capability;
 	u8 numof_additional_mids;
+	/* rfc read calibration result. 5..15 */
 	u8 rfc_read_calib_result;
 	u8 reserved[3];
 } __packed;
@@ -1580,7 +1793,7 @@
 	u8 reserved[3];
 } __packed;
 
-/* WMI_CORR_MEASURE_EVENTID */
+/* WMI_CORR_MEASURE_EVENTID - deprecated */
 struct wmi_corr_measure_event {
 	/* signed */
 	__le32 i;
@@ -1610,27 +1823,31 @@
 	u8 range;
 	u8 sqi;
 	__le16 stype;
-	__le16 status;
+	__le16 snr;
 	__le32 len;
-	/* Not resolved when == 0xFFFFFFFF  ==> Broadcast to all MIDS */
+	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
 	u8 qid;
-	/* Not resolved when == 0xFFFFFFFF  ==> Broadcast to all MIDS */
+	/* Not resolved when == 0xFFFFFFFF == > Broadcast to all MIDS */
 	u8 mid;
 	u8 cid;
 	/* From Radio MNGR */
 	u8 channel;
 } __packed;
 
-/* wmi_otp_read_write_cmd */
-struct wmi_otp_read_write_cmd {
-	__le32 addr;
-	__le32 size;
-	u8 values[0];
+/* EVENT: WMI_RF_XPM_READ_RESULT_EVENTID */
+struct wmi_rf_xpm_read_result_event {
+	/* enum wmi_fw_status_e - success=0 or fail=1 */
+	u8 status;
+	u8 reserved[3];
+	/* requested num_bytes of data */
+	u8 data_bytes[0];
 } __packed;
 
-/* WMI_OTP_READ_RESULT_EVENTID */
-struct wmi_otp_read_result_event {
-	u8 payload[0];
+/* EVENT: WMI_RF_XPM_WRITE_RESULT_EVENTID */
+struct wmi_rf_xpm_write_result_event {
+	/* enum wmi_fw_status_e - success=0 or fail=1 */
+	u8 status;
+	u8 reserved[3];
 } __packed;
 
 /* WMI_TX_MGMT_PACKET_EVENTID */
@@ -1649,6 +1866,20 @@
 	__le32 echoed_value;
 } __packed;
 
+/* WMI_RF_PWR_ON_DELAY_RSP_EVENTID */
+struct wmi_rf_pwr_on_delay_rsp_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_SET_HIGH_POWER_TABLE_PARAMS_EVENTID */
+struct wmi_set_high_power_table_params_event {
+	/* wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
 /* WMI_TEMP_SENSE_DONE_EVENTID
  *
  * Measure MAC and radio temperatures
@@ -1726,14 +1957,22 @@
 	u8 reserved;
 } __packed;
 
+/* \WMI_SET_CONNECT_SNR_THR_CMDID */
+struct wmi_set_connect_snr_thr_cmd {
+	u8 enable;
+	u8 reserved;
+	/* 1/4 Db units */
+	__le16 omni_snr_thr;
+	/* 1/4 Db units */
+	__le16 direct_snr_thr;
+} __packed;
+
 /* WMI_LED_CFG_DONE_EVENTID */
 struct wmi_led_cfg_done_event {
 	/* led config status */
 	__le32 status;
 } __packed;
 
-#define WMI_NUM_MCS	(13)
-
 /* Rate search parameters configuration per connection */
 struct wmi_rs_cfg {
 	/* The maximal allowed PER for each MCS
@@ -1758,6 +1997,98 @@
 	__le32 mcs_en_vec;
 } __packed;
 
+/* Slot types */
+enum wmi_sched_scheme_slot_type {
+	WMI_SCHED_SLOT_SP		= 0x0,
+	WMI_SCHED_SLOT_CBAP		= 0x1,
+	WMI_SCHED_SLOT_IDLE		= 0x2,
+	WMI_SCHED_SLOT_ANNOUNCE_NO_ACK	= 0x3,
+	WMI_SCHED_SLOT_DISCOVERY	= 0x4,
+};
+
+enum wmi_sched_scheme_slot_flags {
+	WMI_SCHED_SCHEME_SLOT_PERIODIC	= 0x1,
+};
+
+struct wmi_sched_scheme_slot {
+	/* in microsecond */
+	__le32 tbtt_offset;
+	/* wmi_sched_scheme_slot_flags */
+	u8 flags;
+	/* wmi_sched_scheme_slot_type */
+	u8 type;
+	/* in microsecond */
+	__le16 duration;
+	/* frame_exchange_sequence_duration */
+	__le16 tx_op;
+	/* time in microseconds between two consecutive slots
+	 * relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+	 */
+	__le16 period;
+	/* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+	 * number of times to repeat allocation
+	 */
+	u8 num_of_blocks;
+	/* relevant only if flag WMI_SCHED_SCHEME_SLOT_PERIODIC set
+	 * every idle_period allocation will be idle
+	 */
+	u8 idle_period;
+	u8 src_aid;
+	u8 dest_aid;
+	__le32 reserved;
+} __packed;
+
+enum wmi_sched_scheme_flags {
+	/* should not be set when clearing scheduling scheme */
+	WMI_SCHED_SCHEME_ENABLE		= 0x01,
+	WMI_SCHED_PROTECTED_SP		= 0x02,
+	/* should be set only on first WMI fragment of scheme */
+	WMI_SCHED_FIRST			= 0x04,
+	/* should be set only on last WMI fragment of scheme */
+	WMI_SCHED_LAST			= 0x08,
+	WMI_SCHED_IMMEDIATE_START	= 0x10,
+};
+
+enum wmi_sched_scheme_advertisment {
+	/* ESE is not advertised at all, STA has to be configured with WMI
+	 * also
+	 */
+	WMI_ADVERTISE_ESE_DISABLED		= 0x0,
+	WMI_ADVERTISE_ESE_IN_BEACON		= 0x1,
+	WMI_ADVERTISE_ESE_IN_ANNOUNCE_FRAME	= 0x2,
+};
+
+/* WMI_SCHEDULING_SCHEME_CMD */
+struct wmi_scheduling_scheme_cmd {
+	u8 serial_num;
+	/* wmi_sched_scheme_advertisment */
+	u8 ese_advertisment;
+	/* wmi_sched_scheme_flags */
+	__le16 flags;
+	u8 num_allocs;
+	u8 reserved[3];
+	__le64 start_tbtt;
+	/* allocations list */
+	struct wmi_sched_scheme_slot allocs[WMI_SCHED_MAX_ALLOCS_PER_CMD];
+} __packed;
+
+enum wmi_sched_scheme_failure_type {
+	WMI_SCHED_SCHEME_FAILURE_NO_ERROR		= 0x00,
+	WMI_SCHED_SCHEME_FAILURE_OLD_START_TSF_ERR	= 0x01,
+};
+
+/* WMI_SCHEDULING_SCHEME_EVENTID */
+struct wmi_scheduling_scheme_event {
+	/* wmi_fw_status_e */
+	u8 status;
+	/* serial number given in command */
+	u8 serial_num;
+	/* wmi_sched_scheme_failure_type */
+	u8 failure_type;
+	/* alignment to 32b */
+	u8 reserved[1];
+} __packed;
+
 /* WMI_RS_CFG_CMDID */
 struct wmi_rs_cfg_cmd {
 	/* connection id */
@@ -1975,6 +2306,19 @@
 	WMI_PS_PROFILE_TYPE_LOW_LATENCY_PS	= 0x03,
 };
 
+/* WMI_PS_DEV_PROFILE_CFG_READ_CMDID */
+struct wmi_ps_dev_profile_cfg_read_cmd {
+	/* reserved */
+	__le32 reserved;
+} __packed;
+
+/* WMI_PS_DEV_PROFILE_CFG_READ_EVENTID */
+struct wmi_ps_dev_profile_cfg_read_event {
+	/* wmi_ps_profile_type_e */
+	u8 ps_profile;
+	u8 reserved[3];
+} __packed;
+
 /* WMI_PS_DEV_PROFILE_CFG_CMDID
  *
  * Power save profile to be used by the device
@@ -2023,157 +2367,6 @@
 	WMI_PS_D3_RESP_POLICY_APPROVED	= 0x02,
 };
 
-/* Device common power save configurations */
-struct wmi_ps_dev_cfg {
-	/* lowest level of PS allowed while unassociated, enum wmi_ps_level_e
-	 */
-	u8 ps_unassoc_min_level;
-	/* lowest deep sleep clock level while nonassoc, enum
-	 * wmi_ps_deep_sleep_clk_level_e
-	 */
-	u8 ps_unassoc_deep_sleep_min_level;
-	/* lowest level of PS allowed while associated, enum wmi_ps_level_e */
-	u8 ps_assoc_min_level;
-	/* lowest deep sleep clock level while assoc, enum
-	 * wmi_ps_deep_sleep_clk_level_e
-	 */
-	u8 ps_assoc_deep_sleep_min_level;
-	/* enum wmi_ps_deep_sleep_clk_level_e */
-	u8 ps_assoc_low_latency_ds_min_level;
-	/* enum wmi_ps_d3_resp_policy_e */
-	u8 ps_D3_response_policy;
-	/* BOOL */
-	u8 ps_D3_pm_pme_enabled;
-	/* BOOL */
-	u8 ps_halp_enable;
-	u8 ps_deep_sleep_enter_thresh_msec;
-	/* BOOL */
-	u8 ps_voltage_scaling_en;
-} __packed;
-
-/* WMI_PS_DEV_CFG_CMDID
- *
- * Configure common Power Save parameters of the device and all MIDs.
- *
- * Returned event:
- * - WMI_PS_DEV_CFG_EVENTID
- */
-struct wmi_ps_dev_cfg_cmd {
-	/* Device Power Save configuration to be applied */
-	struct wmi_ps_dev_cfg ps_dev_cfg;
-	/* alignment to 32b */
-	u8 reserved[2];
-} __packed;
-
-/* WMI_PS_DEV_CFG_EVENTID */
-struct wmi_ps_dev_cfg_event {
-	/* wmi_ps_cfg_cmd_status_e */
-	__le32 status;
-} __packed;
-
-/* WMI_PS_DEV_CFG_READ_CMDID
- *
- * request to retrieve  device Power Save configuration
- * (WMI_PS_DEV_CFG_CMD params)
- *
- * Returned event:
- * - WMI_PS_DEV_CFG_READ_EVENTID
- */
-struct wmi_ps_dev_cfg_read_cmd {
-	__le32 reserved;
-} __packed;
-
-/* WMI_PS_DEV_CFG_READ_EVENTID */
-struct wmi_ps_dev_cfg_read_event {
-	/* wmi_ps_cfg_cmd_status_e */
-	__le32 status;
-	/* Retrieved device Power Save configuration (WMI_PS_DEV_CFG_CMD
-	 * params)
-	 */
-	struct wmi_ps_dev_cfg dev_ps_cfg;
-	/* alignment to 32b */
-	u8 reserved[2];
-} __packed;
-
-/* Per Mac Power Save configurations */
-struct wmi_ps_mid_cfg {
-	/* Low power RX in BTI is enabled, BOOL */
-	u8 beacon_lprx_enable;
-	/* Sync to sector ID enabled, BOOL */
-	u8 beacon_sync_to_sectorId_enable;
-	/* Low power RX in DTI is enabled, BOOL */
-	u8 frame_exchange_lprx_enable;
-	/* Sleep Cycle while in scheduled PS, 1-31 */
-	u8 scheduled_sleep_cycle_pow2;
-	/* Stay Awake for k BIs every (sleep_cycle - k) BIs, 1-31 */
-	u8 scheduled_num_of_awake_bis;
-	u8 am_to_traffic_load_thresh_mbp;
-	u8 traffic_to_am_load_thresh_mbps;
-	u8 traffic_to_am_num_of_no_traffic_bis;
-	/* BOOL */
-	u8 continuous_traffic_psm;
-	__le16 no_traffic_to_min_usec;
-	__le16 no_traffic_to_max_usec;
-	__le16 snoozing_sleep_interval_milisec;
-	u8 max_no_data_awake_events;
-	/* Trigger WEB after k failed beacons */
-	u8 num_of_failed_beacons_rx_to_trigger_web;
-	/* Trigger BF after k failed beacons */
-	u8 num_of_failed_beacons_rx_to_trigger_bf;
-	/* Trigger SOB after k successful beacons */
-	u8 num_of_successful_beacons_rx_to_trigger_sob;
-} __packed;
-
-/* WMI_PS_MID_CFG_CMDID
- *
- * Configure Power Save parameters of a specific MID.
- * These parameters are relevant for the specific BSS this MID belongs to.
- *
- * Returned event:
- * - WMI_PS_MID_CFG_EVENTID
- */
-struct wmi_ps_mid_cfg_cmd {
-	/* MAC ID */
-	u8 mid;
-	/* mid PS configuration to be applied */
-	struct wmi_ps_mid_cfg ps_mid_cfg;
-} __packed;
-
-/* WMI_PS_MID_CFG_EVENTID */
-struct wmi_ps_mid_cfg_event {
-	/* MAC ID */
-	u8 mid;
-	/* alignment to 32b */
-	u8 reserved[3];
-	/* wmi_ps_cfg_cmd_status_e */
-	__le32 status;
-} __packed;
-
-/* WMI_PS_MID_CFG_READ_CMDID
- *
- * request to retrieve Power Save configuration of mid
- * (WMI_PS_MID_CFG_CMD params)
- *
- * Returned event:
- * - WMI_PS_MID_CFG_READ_EVENTID
- */
-struct wmi_ps_mid_cfg_read_cmd {
-	/* MAC ID */
-	u8 mid;
-	/* alignment to 32b */
-	u8 reserved[3];
-} __packed;
-
-/* WMI_PS_MID_CFG_READ_EVENTID */
-struct wmi_ps_mid_cfg_read_event {
-	/* MAC ID */
-	u8 mid;
-	/* Retrieved MID Power Save configuration(WMI_PS_MID_CFG_CMD params) */
-	struct wmi_ps_mid_cfg mid_ps_cfg;
-	/* wmi_ps_cfg_cmd_status_e */
-	__le32 status;
-} __packed;
-
 #define WMI_AOA_MAX_DATA_SIZE	(128)
 
 enum wmi_aoa_meas_status {
@@ -2264,6 +2457,20 @@
 	u8 reserved[3];
 } __packed;
 
+/* WMI_TOF_SET_LCI_EVENTID */
+struct wmi_tof_set_lci_event {
+	/* enum wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
+/* WMI_TOF_SET_LCR_EVENTID */
+struct wmi_tof_set_lcr_event {
+	/* enum wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
 /* Responder FTM Results */
 struct wmi_responder_ftm_res {
 	u8 t1[6];
@@ -2317,10 +2524,19 @@
 	__le32 tsf_sync;
 	/* actual received ftm per burst */
 	u8 actual_ftm_per_burst;
-	u8 reserved0[7];
+	/* Measurments are from RFs, defined by the mask */
+	__le32 meas_rf_mask;
+	u8 reserved0[3];
 	struct wmi_responder_ftm_res responder_ftm_res[0];
 } __packed;
 
+/* WMI_TOF_CFG_RESPONDER_EVENTID */
+struct wmi_tof_cfg_responder_event {
+	/* enum wmi_fw_status */
+	u8 status;
+	u8 reserved[3];
+} __packed;
+
 enum wmi_tof_channel_info_type {
 	WMI_TOF_CHANNEL_INFO_AOA		= 0x00,
 	WMI_TOF_CHANNEL_INFO_LCI		= 0x01,
@@ -2357,12 +2573,15 @@
 struct wmi_tof_get_tx_rx_offset_event {
 	/* enum wmi_fw_status */
 	u8 status;
-	u8 reserved1[3];
+	/* RF index used to read the offsets */
+	u8 rf_index;
+	u8 reserved1[2];
 	/* TX delay offset */
 	__le32 tx_offset;
 	/* RX delay offset */
 	__le32 rx_offset;
-	__le32 reserved2[2];
+	/* Offset to strongest tap of CIR */
+	__le32 precursor;
 } __packed;
 
 /* Result status codes for WMI commands */
@@ -2625,4 +2844,23 @@
 	u8 reserved[3];
 } __packed;
 
+/* WMI_SET_SILENT_RSSI_TABLE_DONE_EVENTID */
+struct wmi_set_silent_rssi_table_done_event {
+	/* enum wmi_silent_rssi_status */
+	__le32 status;
+	/* enum wmi_silent_rssi_table */
+	__le32 table;
+} __packed;
+
+/* \WMI_COMMAND_NOT_SUPPORTED_EVENTID */
+struct wmi_command_not_supported_event {
+	/* device id */
+	u8 mid;
+	u8 reserved0;
+	__le16 command_id;
+	/* for UT command only, otherwise reserved */
+	__le16 command_subtype;
+	__le16 reserved1;
+} __packed;
+
 #endif /* __WILOCITY_WMI_H__ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index b85398c..bc59aa2 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -980,7 +980,7 @@
 
 	eth_broadcast_addr(params_le->bssid);
 	params_le->bss_type = DOT11_BSSTYPE_ANY;
-	params_le->scan_type = 0;
+	params_le->scan_type = BRCMF_SCANTYPE_ACTIVE;
 	params_le->channel_num = 0;
 	params_le->nprobes = cpu_to_le32(-1);
 	params_le->active_time = cpu_to_le32(-1);
@@ -988,12 +988,9 @@
 	params_le->home_time = cpu_to_le32(-1);
 	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
 
-	/* if request is null exit so it will be all channel broadcast scan */
-	if (!request)
-		return;
-
 	n_ssids = request->n_ssids;
 	n_channels = request->n_channels;
+
 	/* Copy channel array if applicable */
 	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
 		  n_channels);
@@ -1030,16 +1027,8 @@
 			ptr += sizeof(ssid_le);
 		}
 	} else {
-		brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
-		if ((request->ssids) && request->ssids->ssid_len) {
-			brcmf_dbg(SCAN, "SSID %s len=%d\n",
-				  params_le->ssid_le.SSID,
-				  request->ssids->ssid_len);
-			params_le->ssid_le.SSID_len =
-				cpu_to_le32(request->ssids->ssid_len);
-			memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
-				request->ssids->ssid_len);
-		}
+		brcmf_dbg(SCAN, "Performing passive scan\n");
+		params_le->scan_type = BRCMF_SCANTYPE_PASSIVE;
 	}
 	/* Adding mask to channel numbers */
 	params_le->channel_num =
@@ -3099,6 +3088,7 @@
 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
 	s32 status;
 	struct brcmf_escan_result_le *escan_result_le;
+	u32 escan_buflen;
 	struct brcmf_bss_info_le *bss_info_le;
 	struct brcmf_bss_info_le *bss = NULL;
 	u32 bi_length;
@@ -3115,11 +3105,23 @@
 
 	if (status == BRCMF_E_STATUS_PARTIAL) {
 		brcmf_dbg(SCAN, "ESCAN Partial result\n");
+		if (e->datalen < sizeof(*escan_result_le)) {
+			brcmf_err("invalid event data length\n");
+			goto exit;
+		}
 		escan_result_le = (struct brcmf_escan_result_le *) data;
 		if (!escan_result_le) {
 			brcmf_err("Invalid escan result (NULL pointer)\n");
 			goto exit;
 		}
+		escan_buflen = le32_to_cpu(escan_result_le->buflen);
+		if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
+		    escan_buflen > e->datalen ||
+		    escan_buflen < sizeof(*escan_result_le)) {
+			brcmf_err("Invalid escan buffer length: %d\n",
+				  escan_buflen);
+			goto exit;
+		}
 		if (le16_to_cpu(escan_result_le->bss_count) != 1) {
 			brcmf_err("Invalid bss_count %d: ignoring\n",
 				  escan_result_le->bss_count);
@@ -3136,9 +3138,8 @@
 		}
 
 		bi_length = le32_to_cpu(bss_info_le->length);
-		if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
-					WL_ESCAN_RESULTS_FIXED_SIZE)) {
-			brcmf_err("Invalid bss_info length %d: ignoring\n",
+		if (bi_length != escan_buflen -	WL_ESCAN_RESULTS_FIXED_SIZE) {
+			brcmf_err("Ignoring invalid bss_info length: %d\n",
 				  bi_length);
 			goto exit;
 		}
@@ -5419,6 +5420,7 @@
 	struct ieee80211_supported_band *band;
 	struct brcmf_bss_info_le *bi;
 	struct brcmu_chan ch;
+	struct cfg80211_roam_info roam_info = {};
 	u32 freq;
 	s32 err = 0;
 	u8 *buf;
@@ -5457,9 +5459,15 @@
 
 done:
 	kfree(buf);
-	cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
-			conn_info->req_ie, conn_info->req_ie_len,
-			conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
+
+	roam_info.channel = notify_channel;
+	roam_info.bssid = profile->bssid;
+	roam_info.req_ie = conn_info->req_ie;
+	roam_info.req_ie_len = conn_info->req_ie_len;
+	roam_info.resp_ie = conn_info->resp_ie;
+	roam_info.resp_ie_len = conn_info->resp_ie_len;
+
+	cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
 	brcmf_dbg(CONN, "Report roaming result\n");
 
 	set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
@@ -6573,8 +6581,7 @@
 			wiphy->bands[NL80211_BAND_5GHZ] = band;
 		}
 	}
-	err = brcmf_setup_wiphybands(wiphy);
-	return err;
+	return 0;
 }
 
 static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
@@ -6939,6 +6946,12 @@
 		goto priv_out;
 	}
 
+	err = brcmf_setup_wiphybands(wiphy);
+	if (err) {
+		brcmf_err("Setting wiphy bands failed (%d)\n", err);
+		goto wiphy_unreg_out;
+	}
+
 	/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
 	 * setup 40MHz in 2GHz band and enable OBSS scanning.
 	 */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
index e64557c..6f8a4b0 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/debug.c
@@ -32,16 +32,25 @@
 {
 	void *dump;
 	size_t ramsize;
+	int err;
 
 	ramsize = brcmf_bus_get_ramsize(bus);
-	if (ramsize) {
-		dump = vzalloc(len + ramsize);
-		if (!dump)
-			return -ENOMEM;
-		memcpy(dump, data, len);
-		brcmf_bus_get_memdump(bus, dump + len, ramsize);
-		dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
+	if (!ramsize)
+		return -ENOTSUPP;
+
+	dump = vzalloc(len + ramsize);
+	if (!dump)
+		return -ENOMEM;
+
+	memcpy(dump, data, len);
+	err = brcmf_bus_get_memdump(bus, dump + len, ramsize);
+	if (err) {
+		vfree(dump);
+		return err;
 	}
+
+	dev_coredumpv(bus->dev, dump, len + ramsize, GFP_KERNEL);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
index 79c081f..6afcf86 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
@@ -429,7 +429,8 @@
 	if (code != BRCMF_E_IF && !fweh->evt_handler[code])
 		return;
 
-	if (datalen > BRCMF_DCMD_MAXLEN)
+	if (datalen > BRCMF_DCMD_MAXLEN ||
+	    datalen + sizeof(*event_packet) > packet_len)
 		return;
 
 	if (in_interrupt())
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index a4118c0..5901357 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -45,6 +45,11 @@
 #define BRCMF_SCAN_PARAMS_COUNT_MASK	0x0000ffff
 #define BRCMF_SCAN_PARAMS_NSSID_SHIFT	16
 
+/* scan type definitions */
+#define BRCMF_SCANTYPE_DEFAULT		0xFF
+#define BRCMF_SCANTYPE_ACTIVE		0
+#define BRCMF_SCANTYPE_PASSIVE		1
+
 /* primary (ie tx) key */
 #define BRCMF_PRIMARY_KEY		(1 << 1)
 #define DOT11_BSSTYPE_ANY		2
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
index 8744b9b..8e3c6f4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c
@@ -4161,11 +4161,6 @@
 		goto fail;
 	}
 
-	/* allocate scatter-gather table. sg support
-	 * will be disabled upon allocation failure.
-	 */
-	brcmf_sdiod_sgtable_alloc(bus->sdiodev);
-
 	/* Query the F2 block size, set roundup accordingly */
 	bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
 	bus->roundup = min(max_roundup, bus->blocksize);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
index b3aab2f..ef68546 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/phy/phy_n.c
@@ -14764,8 +14764,8 @@
 }
 
 static void
-wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, u8 *events, u8 *dlys,
-		       u8 len)
+wlc_phy_set_rfseq_nphy(struct brcms_phy *pi, u8 cmd, const u8 *events,
+		       const u8 *dlys, u8 len)
 {
 	u32 t1_offset, t2_offset;
 	u8 ctr;
@@ -15240,16 +15240,16 @@
 static void wlc_phy_workarounds_nphy_gainctrl_2057_rev6(struct brcms_phy *pi)
 {
 	u16 currband;
-	s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 };
-	s8 *lna1_gain_db = NULL;
-	s8 *lna1_gain_db_2 = NULL;
-	s8 *lna2_gain_db = NULL;
-	s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 };
-	s8 *tia_gain_db;
-	s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 };
-	s8 *tia_gainbits;
-	u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f };
-	u16 *rfseq_init_gain;
+	static const s8 lna1G_gain_db_rev7[] = { 9, 14, 19, 24 };
+	const s8 *lna1_gain_db = NULL;
+	const s8 *lna1_gain_db_2 = NULL;
+	const s8 *lna2_gain_db = NULL;
+	static const s8 tiaA_gain_db_rev7[] = { -9, -6, -3, 0, 3, 3, 3, 3, 3, 3 };
+	const s8 *tia_gain_db;
+	static const s8 tiaA_gainbits_rev7[] = { 0, 1, 2, 3, 4, 4, 4, 4, 4, 4 };
+	const s8 *tia_gainbits;
+	static const u16 rfseqA_init_gain_rev7[] = { 0x624f, 0x624f };
+	const u16 *rfseq_init_gain;
 	u16 init_gaincode;
 	u16 clip1hi_gaincode;
 	u16 clip1md_gaincode = 0;
@@ -15310,10 +15310,9 @@
 
 			if ((freq <= 5080) || (freq == 5825)) {
 
-				s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 };
-				s8 lna1A_gain_db_2_rev7[] = {
-					11, 17, 22, 25};
-				s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
+				static const s8 lna1A_gain_db_rev7[] = { 11, 16, 20, 24 };
+				static const s8 lna1A_gain_db_2_rev7[] = { 11, 17, 22, 25};
+				static const s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
 
 				crsminu_th = 0x3e;
 				lna1_gain_db = lna1A_gain_db_rev7;
@@ -15321,10 +15320,9 @@
 				lna2_gain_db = lna2A_gain_db_rev7;
 			} else if ((freq >= 5500) && (freq <= 5700)) {
 
-				s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 };
-				s8 lna1A_gain_db_2_rev7[] = {
-					12, 18, 22, 26};
-				s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 };
+				static const s8 lna1A_gain_db_rev7[] = { 11, 17, 21, 25 };
+				static const s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26};
+				static const s8 lna2A_gain_db_rev7[] = { 1, 8, 12, 16 };
 
 				crsminu_th = 0x45;
 				clip1md_gaincode_B = 0x14;
@@ -15335,10 +15333,9 @@
 				lna2_gain_db = lna2A_gain_db_rev7;
 			} else {
 
-				s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 };
-				s8 lna1A_gain_db_2_rev7[] = {
-					12, 18, 22, 26};
-				s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
+				static const s8 lna1A_gain_db_rev7[] = { 12, 18, 22, 26 };
+				static const s8 lna1A_gain_db_2_rev7[] = { 12, 18, 22, 26};
+				static const s8 lna2A_gain_db_rev7[] = { -1, 6, 10, 14 };
 
 				crsminu_th = 0x41;
 				lna1_gain_db = lna1A_gain_db_rev7;
@@ -15450,65 +15447,65 @@
 		NPHY_RFSEQ_CMD_CLR_HIQ_DIS,
 		NPHY_RFSEQ_CMD_SET_HPF_BW
 	};
-	u8 rfseq_updategainu_dlys[] = { 10, 30, 1 };
-	s8 lna1G_gain_db[] = { 7, 11, 16, 23 };
-	s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 };
-	s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 };
-	s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 };
-	s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 };
-	s8 lna1A_gain_db[] = { 7, 11, 17, 23 };
-	s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 };
-	s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 };
-	s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 };
-	s8 *lna1_gain_db = NULL;
-	s8 lna2G_gain_db[] = { -5, 6, 10, 14 };
-	s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 };
-	s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 };
-	s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 };
-	s8 lna2A_gain_db[] = { -6, 2, 6, 10 };
-	s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 };
-	s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 };
-	s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 };
-	s8 *lna2_gain_db = NULL;
-	s8 tiaG_gain_db[] = {
+	static const u8 rfseq_updategainu_dlys[] = { 10, 30, 1 };
+	static const s8 lna1G_gain_db[] = { 7, 11, 16, 23 };
+	static const s8 lna1G_gain_db_rev4[] = { 8, 12, 17, 25 };
+	static const s8 lna1G_gain_db_rev5[] = { 9, 13, 18, 26 };
+	static const s8 lna1G_gain_db_rev6[] = { 8, 13, 18, 25 };
+	static const s8 lna1G_gain_db_rev6_224B0[] = { 10, 14, 19, 27 };
+	static const s8 lna1A_gain_db[] = { 7, 11, 17, 23 };
+	static const s8 lna1A_gain_db_rev4[] = { 8, 12, 18, 23 };
+	static const s8 lna1A_gain_db_rev5[] = { 6, 10, 16, 21 };
+	static const s8 lna1A_gain_db_rev6[] = { 6, 10, 16, 21 };
+	const s8 *lna1_gain_db = NULL;
+	static const s8 lna2G_gain_db[] = { -5, 6, 10, 14 };
+	static const s8 lna2G_gain_db_rev5[] = { -3, 7, 11, 16 };
+	static const s8 lna2G_gain_db_rev6[] = { -5, 6, 10, 14 };
+	static const s8 lna2G_gain_db_rev6_224B0[] = { -5, 6, 10, 15 };
+	static const s8 lna2A_gain_db[] = { -6, 2, 6, 10 };
+	static const s8 lna2A_gain_db_rev4[] = { -5, 2, 6, 10 };
+	static const s8 lna2A_gain_db_rev5[] = { -7, 0, 4, 8 };
+	static const s8 lna2A_gain_db_rev6[] = { -7, 0, 4, 8 };
+	const s8 *lna2_gain_db = NULL;
+	static const s8 tiaG_gain_db[] = {
 		0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A };
-	s8 tiaA_gain_db[] = {
+	static const s8 tiaA_gain_db[] = {
 		0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13 };
-	s8 tiaA_gain_db_rev4[] = {
+	static const s8 tiaA_gain_db_rev4[] = {
 		0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
-	s8 tiaA_gain_db_rev5[] = {
+	static const s8 tiaA_gain_db_rev5[] = {
 		0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
-	s8 tiaA_gain_db_rev6[] = {
+	static const s8 tiaA_gain_db_rev6[] = {
 		0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d };
-	s8 *tia_gain_db;
-	s8 tiaG_gainbits[] = {
+	const s8 *tia_gain_db;
+	static const s8 tiaG_gainbits[] = {
 		0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
-	s8 tiaA_gainbits[] = {
+	static const s8 tiaA_gainbits[] = {
 		0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06 };
-	s8 tiaA_gainbits_rev4[] = {
+	static const s8 tiaA_gainbits_rev4[] = {
 		0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
-	s8 tiaA_gainbits_rev5[] = {
+	static const s8 tiaA_gainbits_rev5[] = {
 		0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
-	s8 tiaA_gainbits_rev6[] = {
+	static const s8 tiaA_gainbits_rev6[] = {
 		0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
-	s8 *tia_gainbits;
-	s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 };
-	s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 };
-	u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f };
-	u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f };
-	u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f };
-	u16 rfseqG_init_gain_rev5_elna[] = {
+	const s8 *tia_gainbits;
+	static const s8 lpf_gain_db[] = { 0x00, 0x06, 0x0c, 0x12, 0x12, 0x12 };
+	static const s8 lpf_gainbits[] = { 0x00, 0x01, 0x02, 0x03, 0x03, 0x03 };
+	static const u16 rfseqG_init_gain[] = { 0x613f, 0x613f, 0x613f, 0x613f };
+	static const u16 rfseqG_init_gain_rev4[] = { 0x513f, 0x513f, 0x513f, 0x513f };
+	static const u16 rfseqG_init_gain_rev5[] = { 0x413f, 0x413f, 0x413f, 0x413f };
+	static const u16 rfseqG_init_gain_rev5_elna[] = {
 		0x013f, 0x013f, 0x013f, 0x013f };
-	u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f };
-	u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f };
-	u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f };
-	u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f };
-	u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f };
-	u16 rfseqA_init_gain_rev4_elna[] = {
+	static const u16 rfseqG_init_gain_rev6[] = { 0x513f, 0x513f };
+	static const u16 rfseqG_init_gain_rev6_224B0[] = { 0x413f, 0x413f };
+	static const u16 rfseqG_init_gain_rev6_elna[] = { 0x113f, 0x113f };
+	static const u16 rfseqA_init_gain[] = { 0x516f, 0x516f, 0x516f, 0x516f };
+	static const u16 rfseqA_init_gain_rev4[] = { 0x614f, 0x614f, 0x614f, 0x614f };
+	static const u16 rfseqA_init_gain_rev4_elna[] = {
 		0x314f, 0x314f, 0x314f, 0x314f };
-	u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f };
-	u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f };
-	u16 *rfseq_init_gain;
+	static const u16 rfseqA_init_gain_rev5[] = { 0x714f, 0x714f, 0x714f, 0x714f };
+	static const u16 rfseqA_init_gain_rev6[] = { 0x714f, 0x714f };
+	const u16 *rfseq_init_gain;
 	u16 initG_gaincode = 0x627e;
 	u16 initG_gaincode_rev4 = 0x527e;
 	u16 initG_gaincode_rev5 = 0x427e;
@@ -15538,10 +15535,10 @@
 	u16 clip1mdA_gaincode_rev6 = 0x2084;
 	u16 clip1md_gaincode = 0;
 	u16 clip1loG_gaincode = 0x0074;
-	u16 clip1loG_gaincode_rev5[] = {
+	static const u16 clip1loG_gaincode_rev5[] = {
 		0x0062, 0x0064, 0x006a, 0x106a, 0x106c, 0x1074, 0x107c, 0x207c
 	};
-	u16 clip1loG_gaincode_rev6[] = {
+	static const u16 clip1loG_gaincode_rev6[] = {
 		0x106a, 0x106c, 0x1074, 0x107c, 0x007e, 0x107e, 0x207e, 0x307e
 	};
 	u16 clip1loG_gaincode_rev6_224B0 = 0x1074;
@@ -16066,7 +16063,7 @@
 
 static void wlc_phy_workarounds_nphy(struct brcms_phy *pi)
 {
-	u8 rfseq_rx2tx_events[] = {
+	static const u8 rfseq_rx2tx_events[] = {
 		NPHY_RFSEQ_CMD_NOP,
 		NPHY_RFSEQ_CMD_RXG_FBW,
 		NPHY_RFSEQ_CMD_TR_SWITCH,
@@ -16076,7 +16073,7 @@
 		NPHY_RFSEQ_CMD_EXT_PA
 	};
 	u8 rfseq_rx2tx_dlys[] = { 8, 6, 6, 2, 4, 60, 1 };
-	u8 rfseq_tx2rx_events[] = {
+	static const u8 rfseq_tx2rx_events[] = {
 		NPHY_RFSEQ_CMD_NOP,
 		NPHY_RFSEQ_CMD_EXT_PA,
 		NPHY_RFSEQ_CMD_TX_GAIN,
@@ -16085,8 +16082,8 @@
 		NPHY_RFSEQ_CMD_RXG_FBW,
 		NPHY_RFSEQ_CMD_CLR_HIQ_DIS
 	};
-	u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 };
-	u8 rfseq_tx2rx_events_rev3[] = {
+	static const u8 rfseq_tx2rx_dlys[] = { 8, 6, 2, 4, 4, 6, 1 };
+	static const u8 rfseq_tx2rx_events_rev3[] = {
 		NPHY_REV3_RFSEQ_CMD_EXT_PA,
 		NPHY_REV3_RFSEQ_CMD_INT_PA_PU,
 		NPHY_REV3_RFSEQ_CMD_TX_GAIN,
@@ -16096,7 +16093,7 @@
 		NPHY_REV3_RFSEQ_CMD_CLR_HIQ_DIS,
 		NPHY_REV3_RFSEQ_CMD_END
 	};
-	u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 };
+	static const u8 rfseq_tx2rx_dlys_rev3[] = { 8, 4, 2, 2, 4, 4, 6, 1 };
 	u8 rfseq_rx2tx_events_rev3[] = {
 		NPHY_REV3_RFSEQ_CMD_NOP,
 		NPHY_REV3_RFSEQ_CMD_RXG_FBW,
@@ -16110,7 +16107,7 @@
 	};
 	u8 rfseq_rx2tx_dlys_rev3[] = { 8, 6, 6, 4, 4, 18, 42, 1, 1 };
 
-	u8 rfseq_rx2tx_events_rev3_ipa[] = {
+	static const u8 rfseq_rx2tx_events_rev3_ipa[] = {
 		NPHY_REV3_RFSEQ_CMD_NOP,
 		NPHY_REV3_RFSEQ_CMD_RXG_FBW,
 		NPHY_REV3_RFSEQ_CMD_TR_SWITCH,
@@ -16121,15 +16118,15 @@
 		NPHY_REV3_RFSEQ_CMD_INT_PA_PU,
 		NPHY_REV3_RFSEQ_CMD_END
 	};
-	u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
-	u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f };
+	static const u8 rfseq_rx2tx_dlys_rev3_ipa[] = { 8, 6, 6, 4, 4, 16, 43, 1, 1 };
+	static const u16 rfseq_rx2tx_dacbufpu_rev7[] = { 0x10f, 0x10f };
 
 	s16 alpha0, alpha1, alpha2;
 	s16 beta0, beta1, beta2;
 	u32 leg_data_weights, ht_data_weights, nss1_data_weights,
 	    stbc_data_weights;
 	u8 chan_freq_range = 0;
-	u16 dac_control = 0x0002;
+	static const u16 dac_control = 0x0002;
 	u16 aux_adc_vmid_rev7_core0[] = { 0x8e, 0x96, 0x96, 0x96 };
 	u16 aux_adc_vmid_rev7_core1[] = { 0x8f, 0x9f, 0x9f, 0x96 };
 	u16 aux_adc_vmid_rev4[] = { 0xa2, 0xb4, 0xb4, 0x89 };
@@ -16139,8 +16136,8 @@
 	u16 aux_adc_gain_rev4[] = { 0x02, 0x02, 0x02, 0x00 };
 	u16 aux_adc_gain_rev3[] = { 0x02, 0x02, 0x02, 0x00 };
 	u16 *aux_adc_gain;
-	u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 };
-	u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 };
+	static const u16 sk_adc_vmid[] = { 0xb4, 0xb4, 0xb4, 0x24 };
+	static const u16 sk_adc_gain[] = { 0x02, 0x02, 0x02, 0x02 };
 	s32 min_nvar_val = 0x18d;
 	s32 min_nvar_offset_6mbps = 20;
 	u8 pdetrange;
@@ -16151,9 +16148,9 @@
 	u16 rfseq_rx2tx_lpf_h_hpc_rev7 = 0x77;
 	u16 rfseq_tx2rx_lpf_h_hpc_rev7 = 0x77;
 	u16 rfseq_pktgn_lpf_h_hpc_rev7 = 0x77;
-	u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 };
-	u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 };
-	u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 };
+	static const u16 rfseq_htpktgn_lpf_hpc_rev7[] = { 0x77, 0x11, 0x11 };
+	static const u16 rfseq_pktgn_lpf_hpc_rev7[] = { 0x11, 0x11 };
+	static const u16 rfseq_cckpktgn_lpf_hpc_rev7[] = { 0x11, 0x11 };
 	u16 ipalvlshift_3p3_war_en = 0;
 	u16 rccal_bcap_val, rccal_scap_val;
 	u16 rccal_tx20_11b_bcap = 0;
@@ -24291,13 +24288,13 @@
 	u16 bbmult;
 	u16 tblentry;
 
-	struct nphy_txiqcal_ladder ladder_lo[] = {
+	static const struct nphy_txiqcal_ladder ladder_lo[] = {
 		{3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
 		{25, 0}, {25, 1}, {25, 2}, {25, 3}, {25, 4}, {25, 5},
 		{25, 6}, {25, 7}, {35, 7}, {50, 7}, {71, 7}, {100, 7}
 	};
 
-	struct nphy_txiqcal_ladder ladder_iq[] = {
+	static const struct nphy_txiqcal_ladder ladder_iq[] = {
 		{3, 0}, {4, 0}, {6, 0}, {9, 0}, {13, 0}, {18, 0},
 		{25, 0}, {35, 0}, {50, 0}, {71, 0}, {100, 0}, {100, 1},
 		{100, 2}, {100, 3}, {100, 4}, {100, 5}, {100, 6}, {100, 7}
@@ -25773,67 +25770,67 @@
 	u16 cal_gain[2];
 	struct nphy_iqcal_params cal_params[2];
 	u32 tbl_len;
-	void *tbl_ptr;
+	const void *tbl_ptr;
 	bool ladder_updated[2];
 	u8 mphase_cal_lastphase = 0;
 	int bcmerror = 0;
 	bool phyhang_avoid_state = false;
 
-	u16 tbl_tx_iqlo_cal_loft_ladder_20[] = {
+	static const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = {
 		0x0300, 0x0500, 0x0700, 0x0900, 0x0d00, 0x1100, 0x1900, 0x1901,
 		0x1902,
 		0x1903, 0x1904, 0x1905, 0x1906, 0x1907, 0x2407, 0x3207, 0x4607,
 		0x6407
 	};
 
-	u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = {
+	static const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = {
 		0x0200, 0x0300, 0x0600, 0x0900, 0x0d00, 0x1100, 0x1900, 0x2400,
 		0x3200,
 		0x4600, 0x6400, 0x6401, 0x6402, 0x6403, 0x6404, 0x6405, 0x6406,
 		0x6407
 	};
 
-	u16 tbl_tx_iqlo_cal_loft_ladder_40[] = {
+	static const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = {
 		0x0200, 0x0300, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1201,
 		0x1202,
 		0x1203, 0x1204, 0x1205, 0x1206, 0x1207, 0x1907, 0x2307, 0x3207,
 		0x4707
 	};
 
-	u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = {
+	static const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = {
 		0x0100, 0x0200, 0x0400, 0x0700, 0x0900, 0x0c00, 0x1200, 0x1900,
 		0x2300,
 		0x3200, 0x4700, 0x4701, 0x4702, 0x4703, 0x4704, 0x4705, 0x4706,
 		0x4707
 	};
 
-	u16 tbl_tx_iqlo_cal_startcoefs[] = {
+	static const u16 tbl_tx_iqlo_cal_startcoefs[] = {
 		0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
 		0x0000
 	};
 
-	u16 tbl_tx_iqlo_cal_cmds_fullcal[] = {
+	static const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = {
 		0x8123, 0x8264, 0x8086, 0x8245, 0x8056,
 		0x9123, 0x9264, 0x9086, 0x9245, 0x9056
 	};
 
-	u16 tbl_tx_iqlo_cal_cmds_recal[] = {
+	static const u16 tbl_tx_iqlo_cal_cmds_recal[] = {
 		0x8101, 0x8253, 0x8053, 0x8234, 0x8034,
 		0x9101, 0x9253, 0x9053, 0x9234, 0x9034
 	};
 
-	u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = {
+	static const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[] = {
 		0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
 		0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
 		0x0000
 	};
 
-	u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = {
+	static const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = {
 		0x8434, 0x8334, 0x8084, 0x8267, 0x8056, 0x8234,
 		0x9434, 0x9334, 0x9084, 0x9267, 0x9056, 0x9234
 	};
 
-	u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = {
+	static const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = {
 		0x8423, 0x8323, 0x8073, 0x8256, 0x8045, 0x8223,
 		0x9423, 0x9323, 0x9073, 0x9256, 0x9045, 0x9223
 	};
diff --git a/drivers/net/wireless/cnss_genl/cnss_nl.c b/drivers/net/wireless/cnss_genl/cnss_nl.c
index fafd9ce..29dd4c9 100644
--- a/drivers/net/wireless/cnss_genl/cnss_nl.c
+++ b/drivers/net/wireless/cnss_genl/cnss_nl.c
@@ -64,6 +64,8 @@
 	[CLD80211_ATTR_VENDOR_DATA] = { .type = NLA_NESTED },
 	[CLD80211_ATTR_DATA] = { .type = NLA_BINARY,
 				 .len = CLD80211_MAX_NL_DATA },
+	[CLD80211_ATTR_META_DATA] = { .type = NLA_BINARY,
+				 .len = CLD80211_MAX_NL_DATA },
 };
 
 static int cld80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
diff --git a/drivers/net/wireless/cnss_utils/cnss_utils.c b/drivers/net/wireless/cnss_utils/cnss_utils.c
index d73846e..4955130 100644
--- a/drivers/net/wireless/cnss_utils/cnss_utils.c
+++ b/drivers/net/wireless/cnss_utils/cnss_utils.c
@@ -34,6 +34,11 @@
 	u32 no_of_mac_addr_set;
 };
 
+enum mac_type {
+	CNSS_MAC_PROVISIONED,
+	CNSS_MAC_DERIVED,
+};
+
 static struct cnss_utils_priv {
 	struct cnss_unsafe_channel_list unsafe_channel_list;
 	struct cnss_dfs_nol_info dfs_nol_info;
@@ -42,8 +47,8 @@
 	/* generic spin-lock for dfs_nol info */
 	spinlock_t dfs_nol_info_lock;
 	int driver_load_cnt;
-	bool is_wlan_mac_set;
 	struct cnss_wlan_mac_addr wlan_mac_addr;
+	struct cnss_wlan_mac_addr wlan_der_mac_addr;
 	enum cnss_utils_cc_src cc_source;
 } *cnss_utils_priv;
 
@@ -189,7 +194,8 @@
 }
 EXPORT_SYMBOL(cnss_utils_get_driver_load_cnt);
 
-int cnss_utils_set_wlan_mac_address(const u8 *in, const uint32_t len)
+static int set_wlan_mac_address(const u8 *mac_list, const uint32_t len,
+				enum mac_type type)
 {
 	struct cnss_utils_priv *priv = cnss_utils_priv;
 	u32 no_of_mac_addr;
@@ -200,11 +206,6 @@
 	if (!priv)
 		return -EINVAL;
 
-	if (priv->is_wlan_mac_set) {
-		pr_debug("WLAN MAC address is already set\n");
-		return 0;
-	}
-
 	if (len == 0 || (len % ETH_ALEN) != 0) {
 		pr_err("Invalid length %d\n", len);
 		return -EINVAL;
@@ -217,24 +218,45 @@
 		return -EINVAL;
 	}
 
-	priv->is_wlan_mac_set = true;
-	addr = &priv->wlan_mac_addr;
+	if (type == CNSS_MAC_PROVISIONED)
+		addr = &priv->wlan_mac_addr;
+	else
+		addr = &priv->wlan_der_mac_addr;
+
+	if (addr->no_of_mac_addr_set) {
+		pr_err("WLAN MAC address is already set, num %d type %d\n",
+		       addr->no_of_mac_addr_set, type);
+		return 0;
+	}
+
 	addr->no_of_mac_addr_set = no_of_mac_addr;
 	temp = &addr->mac_addr[0][0];
 
 	for (iter = 0; iter < no_of_mac_addr;
-	     ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
-		ether_addr_copy(temp, in);
+	     ++iter, temp += ETH_ALEN, mac_list += ETH_ALEN) {
+		ether_addr_copy(temp, mac_list);
 		pr_debug("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
 			 temp[0], temp[1], temp[2],
 			 temp[3], temp[4], temp[5]);
 	}
-
 	return 0;
 }
+
+int cnss_utils_set_wlan_mac_address(const u8 *mac_list, const uint32_t len)
+{
+	return set_wlan_mac_address(mac_list, len, CNSS_MAC_PROVISIONED);
+}
 EXPORT_SYMBOL(cnss_utils_set_wlan_mac_address);
 
-u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
+int cnss_utils_set_wlan_derived_mac_address(
+				const u8 *mac_list, const uint32_t len)
+{
+	return set_wlan_mac_address(mac_list, len, CNSS_MAC_DERIVED);
+}
+EXPORT_SYMBOL(cnss_utils_set_wlan_derived_mac_address);
+
+static u8 *get_wlan_mac_address(struct device *dev,
+				u32 *num, enum mac_type type)
 {
 	struct cnss_utils_priv *priv = cnss_utils_priv;
 	struct cnss_wlan_mac_addr *addr = NULL;
@@ -242,20 +264,36 @@
 	if (!priv)
 		goto out;
 
-	if (!priv->is_wlan_mac_set) {
-		pr_debug("WLAN MAC address is not set\n");
+	if (type == CNSS_MAC_PROVISIONED)
+		addr = &priv->wlan_mac_addr;
+	else
+		addr = &priv->wlan_der_mac_addr;
+
+	if (!addr->no_of_mac_addr_set) {
+		pr_err("WLAN MAC address is not set, type %d\n", type);
 		goto out;
 	}
-
-	addr = &priv->wlan_mac_addr;
 	*num = addr->no_of_mac_addr_set;
 	return &addr->mac_addr[0][0];
+
 out:
 	*num = 0;
 	return NULL;
 }
+
+u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num)
+{
+	return get_wlan_mac_address(dev, num, CNSS_MAC_PROVISIONED);
+}
 EXPORT_SYMBOL(cnss_utils_get_wlan_mac_address);
 
+u8 *cnss_utils_get_wlan_derived_mac_address(
+			struct device *dev, uint32_t *num)
+{
+	return get_wlan_mac_address(dev, num, CNSS_MAC_DERIVED);
+}
+EXPORT_SYMBOL(cnss_utils_get_wlan_derived_mac_address);
+
 void cnss_utils_set_cc_source(struct device *dev,
 			      enum cnss_utils_cc_src cc_source)
 {
diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
index 4b97371..838946d 100644
--- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c
@@ -1190,11 +1190,11 @@
 				next_reclaimed;
 			IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n",
 						  next_reclaimed);
+			iwlagn_check_ratid_empty(priv, sta_id, tid);
 		}
 
 		iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);
 
-		iwlagn_check_ratid_empty(priv, sta_id, tid);
 		freed = 0;
 
 		/* process frames */
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
index 3bd6fc1..33f4d7c 100644
--- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c
@@ -78,6 +78,7 @@
 /* NVM offsets (in words) definitions */
 enum wkp_nvm_offsets {
 	/* NVM HW-Section offset (in words) definitions */
+	SUBSYSTEM_ID = 0x0A,
 	HW_ADDR = 0x15,
 
 	/* NVM SW-Section offset (in words) definitions */
@@ -262,13 +263,12 @@
 static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
 				struct iwl_nvm_data *data,
 				const __le16 * const nvm_ch_flags,
-				bool lar_supported)
+				bool lar_supported, bool no_wide_in_5ghz)
 {
 	int ch_idx;
 	int n_channels = 0;
 	struct ieee80211_channel *channel;
 	u16 ch_flags;
-	bool is_5ghz;
 	int num_of_ch, num_2ghz_channels;
 	const u8 *nvm_chan;
 
@@ -283,12 +283,20 @@
 	}
 
 	for (ch_idx = 0; ch_idx < num_of_ch; ch_idx++) {
+		bool is_5ghz = (ch_idx >= num_2ghz_channels);
+
 		ch_flags = __le16_to_cpup(nvm_ch_flags + ch_idx);
 
-		if (ch_idx >= num_2ghz_channels &&
-		    !data->sku_cap_band_52GHz_enable)
+		if (is_5ghz && !data->sku_cap_band_52GHz_enable)
 			continue;
 
+		/* workaround to disable wide channels in 5GHz */
+		if (no_wide_in_5ghz && is_5ghz) {
+			ch_flags &= ~(NVM_CHANNEL_40MHZ |
+				     NVM_CHANNEL_80MHZ |
+				     NVM_CHANNEL_160MHZ);
+		}
+
 		if (ch_flags & NVM_CHANNEL_160MHZ)
 			data->vht160_supported = true;
 
@@ -311,8 +319,8 @@
 		n_channels++;
 
 		channel->hw_value = nvm_chan[ch_idx];
-		channel->band = (ch_idx < num_2ghz_channels) ?
-				NL80211_BAND_2GHZ : NL80211_BAND_5GHZ;
+		channel->band = is_5ghz ?
+				NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
 		channel->center_freq =
 			ieee80211_channel_to_frequency(
 				channel->hw_value, channel->band);
@@ -324,7 +332,6 @@
 		 * is not used in mvm, and is used for backwards compatibility
 		 */
 		channel->max_power = IWL_DEFAULT_MAX_TX_POWER;
-		is_5ghz = channel->band == NL80211_BAND_5GHZ;
 
 		/* don't put limitations in case we're using LAR */
 		if (!lar_supported)
@@ -441,7 +448,8 @@
 static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg,
 			    struct iwl_nvm_data *data,
 			    const __le16 *ch_section,
-			    u8 tx_chains, u8 rx_chains, bool lar_supported)
+			    u8 tx_chains, u8 rx_chains, bool lar_supported,
+			    bool no_wide_in_5ghz)
 {
 	int n_channels;
 	int n_used = 0;
@@ -450,12 +458,14 @@
 	if (cfg->device_family != IWL_DEVICE_FAMILY_8000)
 		n_channels = iwl_init_channel_map(
 				dev, cfg, data,
-				&ch_section[NVM_CHANNELS], lar_supported);
+				&ch_section[NVM_CHANNELS], lar_supported,
+				no_wide_in_5ghz);
 	else
 		n_channels = iwl_init_channel_map(
 				dev, cfg, data,
 				&ch_section[NVM_CHANNELS_FAMILY_8000],
-				lar_supported);
+				lar_supported,
+				no_wide_in_5ghz);
 
 	sband = &data->bands[NL80211_BAND_2GHZ];
 	sband->band = NL80211_BAND_2GHZ;
@@ -658,6 +668,39 @@
 	return 0;
 }
 
+static bool
+iwl_nvm_no_wide_in_5ghz(struct device *dev, const struct iwl_cfg *cfg,
+			const __le16 *nvm_hw)
+{
+	/*
+	 * Workaround a bug in Indonesia SKUs where the regulatory in
+	 * some 7000-family OTPs erroneously allow wide channels in
+	 * 5GHz.  To check for Indonesia, we take the SKU value from
+	 * bits 1-4 in the subsystem ID and check if it is either 5 or
+	 * 9.  In those cases, we need to force-disable wide channels
+	 * in 5GHz otherwise the FW will throw a sysassert when we try
+	 * to use them.
+	 */
+	if (cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+		/*
+		 * Unlike the other sections in the NVM, the hw
+		 * section uses big-endian.
+		 */
+		u16 subsystem_id = be16_to_cpup((const __be16 *)nvm_hw
+						+ SUBSYSTEM_ID);
+		u8 sku = (subsystem_id & 0x1e) >> 1;
+
+		if (sku == 5 || sku == 9) {
+			IWL_DEBUG_EEPROM(dev,
+					 "disabling wide channels in 5GHz (0x%0x %d)\n",
+					 subsystem_id, sku);
+			return true;
+		}
+	}
+
+	return false;
+}
+
 struct iwl_nvm_data *
 iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg,
 		   const __le16 *nvm_hw, const __le16 *nvm_sw,
@@ -668,6 +711,7 @@
 	struct device *dev = trans->dev;
 	struct iwl_nvm_data *data;
 	bool lar_enabled;
+	bool no_wide_in_5ghz = iwl_nvm_no_wide_in_5ghz(dev, cfg, nvm_hw);
 	u32 sku, radio_cfg;
 	u16 lar_config;
 	const __le16 *ch_section;
@@ -738,7 +782,7 @@
 	}
 
 	iwl_init_sbands(dev, cfg, data, ch_section, tx_chains, rx_chains,
-			lar_fw_supported && lar_enabled);
+			lar_fw_supported && lar_enabled, no_wide_in_5ghz);
 	data->calib_version = 255;
 
 	return data;
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
index 1db1dc1..9789f3c 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
@@ -1548,6 +1548,11 @@
 	struct iwl_mvm_mc_iter_data *data = _data;
 	struct iwl_mvm *mvm = data->mvm;
 	struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd;
+	struct iwl_host_cmd hcmd = {
+		.id = MCAST_FILTER_CMD,
+		.flags = CMD_ASYNC,
+		.dataflags[0] = IWL_HCMD_DFL_NOCOPY,
+	};
 	int ret, len;
 
 	/* if we don't have free ports, mcast frames will be dropped */
@@ -1562,7 +1567,10 @@
 	memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN);
 	len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4);
 
-	ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_ASYNC, len, cmd);
+	hcmd.len[0] = len;
+	hcmd.data[0] = cmd;
+
+	ret = iwl_mvm_send_cmd(mvm, &hcmd);
 	if (ret)
 		IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret);
 }
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 0556d13..092ae00 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -499,15 +499,17 @@
 	switch (info->control.vif->type) {
 	case NL80211_IFTYPE_AP:
 		/*
-		 * handle legacy hostapd as well, where station may be added
-		 * only after assoc.
+		 * Handle legacy hostapd as well, where station may be added
+		 * only after assoc. Take care of the case where we send a
+		 * deauth to a station that we don't have.
 		 */
-		if (ieee80211_is_probe_resp(fc) || ieee80211_is_auth(fc))
+		if (ieee80211_is_probe_resp(fc) || ieee80211_is_auth(fc) ||
+		    ieee80211_is_deauth(fc))
 			return IWL_MVM_DQA_AP_PROBE_RESP_QUEUE;
 		if (info->hw_queue == info->control.vif->cab_queue)
 			return info->hw_queue;
 
-		WARN_ON_ONCE(1);
+		WARN_ONCE(1, "fc=0x%02x", le16_to_cpu(fc));
 		return IWL_MVM_DQA_AP_PROBE_RESP_QUEUE;
 	case NL80211_IFTYPE_P2P_DEVICE:
 		if (ieee80211_is_mgmt(fc))
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
index 2f8134b..177fd5b 100644
--- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c
@@ -429,6 +429,7 @@
 	{IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)},
 	{IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)},
 	{IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)},
+	{IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7265_2ac_cfg)},
 
 /* 8000 Series */
 	{IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)},
diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c
index 257a9ea..4ac6764 100644
--- a/drivers/net/wireless/intersil/p54/fwio.c
+++ b/drivers/net/wireless/intersil/p54/fwio.c
@@ -488,7 +488,7 @@
 
 			entry += sizeof(__le16);
 			chan->pa_points_per_curve = 8;
-			memset(chan->curve_data, 0, sizeof(*chan->curve_data));
+			memset(chan->curve_data, 0, sizeof(chan->curve_data));
 			memcpy(chan->curve_data, entry,
 			       sizeof(struct p54_pa_curve_data_sample) *
 			       min((u8)8, curve_data->points_per_channel));
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 0fd7d7e..d2a28a9 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1357,8 +1357,6 @@
 				       txi->control.rates,
 				       ARRAY_SIZE(txi->control.rates));
 
-	txi->rate_driver_data[0] = channel;
-
 	if (skb->len >= 24 + 8 &&
 	    ieee80211_is_probe_resp(hdr->frame_control)) {
 		/* fake header transmission time */
@@ -3048,6 +3046,7 @@
 static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
 {
 	struct hwsim_new_radio_params param = { 0 };
+	const char *hwname = NULL;
 
 	param.reg_strict = info->attrs[HWSIM_ATTR_REG_STRICT_REG];
 	param.p2p_device = info->attrs[HWSIM_ATTR_SUPPORT_P2P_DEVICE];
@@ -3061,8 +3060,14 @@
 	if (info->attrs[HWSIM_ATTR_NO_VIF])
 		param.no_vif = true;
 
-	if (info->attrs[HWSIM_ATTR_RADIO_NAME])
-		param.hwname = nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
+	if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+		hwname = kasprintf(GFP_KERNEL, "%.*s",
+				   nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+				   (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+		if (!hwname)
+			return -ENOMEM;
+		param.hwname = hwname;
+	}
 
 	if (info->attrs[HWSIM_ATTR_USE_CHANCTX])
 		param.use_chanctx = true;
@@ -3090,11 +3095,15 @@
 	s64 idx = -1;
 	const char *hwname = NULL;
 
-	if (info->attrs[HWSIM_ATTR_RADIO_ID])
+	if (info->attrs[HWSIM_ATTR_RADIO_ID]) {
 		idx = nla_get_u32(info->attrs[HWSIM_ATTR_RADIO_ID]);
-	else if (info->attrs[HWSIM_ATTR_RADIO_NAME])
-		hwname = (void *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]);
-	else
+	} else if (info->attrs[HWSIM_ATTR_RADIO_NAME]) {
+		hwname = kasprintf(GFP_KERNEL, "%.*s",
+				   nla_len(info->attrs[HWSIM_ATTR_RADIO_NAME]),
+				   (char *)nla_data(info->attrs[HWSIM_ATTR_RADIO_NAME]));
+		if (!hwname)
+			return -ENOMEM;
+	} else
 		return -EINVAL;
 
 	spin_lock_bh(&hwsim_radio_lock);
@@ -3103,7 +3112,8 @@
 			if (data->idx != idx)
 				continue;
 		} else {
-			if (strcmp(hwname, wiphy_name(data->hw->wiphy)))
+			if (!hwname ||
+			    strcmp(hwname, wiphy_name(data->hw->wiphy)))
 				continue;
 		}
 
@@ -3114,10 +3124,12 @@
 		spin_unlock_bh(&hwsim_radio_lock);
 		mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
 					 info);
+		kfree(hwname);
 		return 0;
 	}
 	spin_unlock_bh(&hwsim_radio_lock);
 
+	kfree(hwname);
 	return -ENODEV;
 }
 
diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c
index 301170c..033ff88 100644
--- a/drivers/net/wireless/marvell/libertas/cmd.c
+++ b/drivers/net/wireless/marvell/libertas/cmd.c
@@ -305,7 +305,7 @@
 	}
 
 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
-	return 0;
+	return ret;
 }
 
 static int lbs_wait_for_ds_awake(struct lbs_private *priv)
diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
index afdbbf5..8677a53 100644
--- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c
@@ -4188,7 +4188,7 @@
 	if (adapter->config_bands & BAND_A)
 		n_channels_a = mwifiex_band_5ghz.n_channels;
 
-	adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a);
+	adapter->num_in_chan_stats = n_channels_bg + n_channels_a;
 	adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) *
 				      adapter->num_in_chan_stats);
 
diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c
index 97c9765..78d59a6 100644
--- a/drivers/net/wireless/marvell/mwifiex/scan.c
+++ b/drivers/net/wireless/marvell/mwifiex/scan.c
@@ -2479,6 +2479,12 @@
 					      sizeof(struct mwifiex_chan_stats);
 
 	for (i = 0 ; i < num_chan; i++) {
+		if (adapter->survey_idx >= adapter->num_in_chan_stats) {
+			mwifiex_dbg(adapter, WARN,
+				    "FW reported too many channel results (max %d)\n",
+				    adapter->num_in_chan_stats);
+			return;
+		}
 		chan_stats.chan_num = fw_chan_stats->chan_num;
 		chan_stats.bandcfg = fw_chan_stats->bandcfg;
 		chan_stats.flags = fw_chan_stats->flags;
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
index 4b0bb6b..c636e60 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c
@@ -646,10 +646,9 @@
 			    !test_bit(ENTRY_DATA_STATUS_PENDING, &entry->flags))
 				break;
 
-			if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags))
+			if (test_bit(ENTRY_DATA_IO_FAILED, &entry->flags) ||
+			    rt2800usb_entry_txstatus_timeout(entry))
 				rt2x00lib_txdone_noinfo(entry, TXDONE_FAILURE);
-			else if (rt2800usb_entry_txstatus_timeout(entry))
-				rt2x00lib_txdone_noinfo(entry, TXDONE_UNKNOWN);
 			else
 				break;
 		}
diff --git a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
index 82d949e..4e725d1 100644
--- a/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
+++ b/drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu_core.c
@@ -6316,6 +6316,13 @@
 	.driver_info = (unsigned long)&rtl8192cu_fops},
 {USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0x7822, 0xff, 0xff, 0xff),
 	.driver_info = (unsigned long)&rtl8192cu_fops},
+/* found in rtl8192eu vendor driver */
+{USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0107, 0xff, 0xff, 0xff),
+	.driver_info = (unsigned long)&rtl8192eu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(0x2019, 0xab33, 0xff, 0xff, 0xff),
+	.driver_info = (unsigned long)&rtl8192eu_fops},
+{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_REALTEK, 0x818c, 0xff, 0xff, 0xff),
+	.driver_info = (unsigned long)&rtl8192eu_fops},
 #endif
 { }
 };
diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c
index 5be4fc9..75ffeaa 100644
--- a/drivers/net/wireless/realtek/rtlwifi/pci.c
+++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
@@ -2269,7 +2269,7 @@
 	/* find adapter */
 	if (!_rtl_pci_find_adapter(pdev, hw)) {
 		err = -ENODEV;
-		goto fail3;
+		goto fail2;
 	}
 
 	/* Init IO handler */
@@ -2339,10 +2339,10 @@
 	pci_set_drvdata(pdev, NULL);
 	rtl_deinit_core(hw);
 
+fail2:
 	if (rtlpriv->io.pci_mem_start != 0)
 		pci_iounmap(pdev, (void __iomem *)rtlpriv->io.pci_mem_start);
 
-fail2:
 	pci_release_regions(pdev);
 	complete(&rtlpriv->firmware_loading_complete);
 
diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
index 2cbef96..1281ebe 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/hw.c
@@ -1128,7 +1128,7 @@
 	}
 	if (0 == tmp) {
 		read_addr = REG_DBI_RDATA + addr % 4;
-		ret = rtl_read_byte(rtlpriv, read_addr);
+		ret = rtl_read_word(rtlpriv, read_addr);
 	}
 	return ret;
 }
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 603c904..280196a 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -2830,15 +2830,22 @@
 	}
 
 	if (priv->infra_mode == NDIS_80211_INFRA_INFRA) {
-		if (!roamed)
+		if (!roamed) {
 			cfg80211_connect_result(usbdev->net, bssid, req_ie,
 						req_ie_len, resp_ie,
 						resp_ie_len, 0, GFP_KERNEL);
-		else
-			cfg80211_roamed(usbdev->net,
-					get_current_channel(usbdev, NULL),
-					bssid, req_ie, req_ie_len,
-					resp_ie, resp_ie_len, GFP_KERNEL);
+		} else {
+			struct cfg80211_roam_info roam_info = {
+				.channel = get_current_channel(usbdev, NULL),
+				.bssid = bssid,
+				.req_ie = req_ie,
+				.req_ie_len = req_ie_len,
+				.resp_ie = resp_ie,
+				.resp_ie_len = resp_ie_len,
+			};
+
+			cfg80211_roamed(usbdev->net, &roam_info, GFP_KERNEL);
+		}
 	} else if (priv->infra_mode == NDIS_80211_INFRA_ADHOC)
 		cfg80211_ibss_joined(usbdev->net, bssid,
 				     get_current_channel(usbdev, NULL),
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index bbf7604..1c539c8 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -1571,6 +1571,7 @@
 
 	wl->state = WL1251_STATE_OFF;
 	mutex_init(&wl->mutex);
+	spin_lock_init(&wl->wl_lock);
 
 	wl->tx_mgmt_frm_rate = DEFAULT_HW_GEN_TX_RATE;
 	wl->tx_mgmt_frm_mod = DEFAULT_HW_GEN_MODULATION_TYPE;
diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
index 3ce1f7d..cb7365b 100644
--- a/drivers/net/xen-netback/common.h
+++ b/drivers/net/xen-netback/common.h
@@ -199,6 +199,7 @@
 	unsigned long   remaining_credit;
 	struct timer_list credit_timeout;
 	u64 credit_window_start;
+	bool rate_limited;
 
 	/* Statistics */
 	struct xenvif_stats stats;
diff --git a/drivers/net/xen-netback/hash.c b/drivers/net/xen-netback/hash.c
index e8c5ddd..3c4c58b 100644
--- a/drivers/net/xen-netback/hash.c
+++ b/drivers/net/xen-netback/hash.c
@@ -39,7 +39,7 @@
 	unsigned long flags;
 	bool found;
 
-	new = kmalloc(sizeof(*entry), GFP_KERNEL);
+	new = kmalloc(sizeof(*entry), GFP_ATOMIC);
 	if (!new)
 		return;
 
diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
index b009d79..5bfaf55 100644
--- a/drivers/net/xen-netback/interface.c
+++ b/drivers/net/xen-netback/interface.c
@@ -105,7 +105,11 @@
 
 	if (work_done < budget) {
 		napi_complete(napi);
-		xenvif_napi_schedule_or_enable_events(queue);
+		/* If the queue is rate-limited, it shall be
+		 * rescheduled in the timer callback.
+		 */
+		if (likely(!queue->rate_limited))
+			xenvif_napi_schedule_or_enable_events(queue);
 	}
 
 	return work_done;
diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
index 47b4810..a7bdb1f 100644
--- a/drivers/net/xen-netback/netback.c
+++ b/drivers/net/xen-netback/netback.c
@@ -67,6 +67,7 @@
 unsigned int rx_stall_timeout_msecs = 60000;
 module_param(rx_stall_timeout_msecs, uint, 0444);
 
+#define MAX_QUEUES_DEFAULT 8
 unsigned int xenvif_max_queues;
 module_param_named(max_queues, xenvif_max_queues, uint, 0644);
 MODULE_PARM_DESC(max_queues,
@@ -179,6 +180,7 @@
 		max_credit = ULONG_MAX; /* wrapped: clamp to ULONG_MAX */
 
 	queue->remaining_credit = min(max_credit, max_burst);
+	queue->rate_limited = false;
 }
 
 void xenvif_tx_credit_callback(unsigned long data)
@@ -685,8 +687,10 @@
 		msecs_to_jiffies(queue->credit_usec / 1000);
 
 	/* Timer could already be pending in rare cases. */
-	if (timer_pending(&queue->credit_timeout))
+	if (timer_pending(&queue->credit_timeout)) {
+		queue->rate_limited = true;
 		return true;
+	}
 
 	/* Passed the point where we can replenish credit? */
 	if (time_after_eq64(now, next_credit)) {
@@ -701,6 +705,7 @@
 		mod_timer(&queue->credit_timeout,
 			  next_credit);
 		queue->credit_window_start = next_credit;
+		queue->rate_limited = true;
 
 		return true;
 	}
@@ -1622,11 +1627,12 @@
 	if (!xen_domain())
 		return -ENODEV;
 
-	/* Allow as many queues as there are CPUs if user has not
+	/* Allow as many queues as there are CPUs but max. 8 if user has not
 	 * specified a value.
 	 */
 	if (xenvif_max_queues == 0)
-		xenvif_max_queues = num_online_cpus();
+		xenvif_max_queues = min_t(unsigned int, MAX_QUEUES_DEFAULT,
+					  num_online_cpus());
 
 	if (fatal_skb_slots < XEN_NETBK_LEGACY_SLOTS_MAX) {
 		pr_info("fatal_skb_slots too small (%d), bump it to XEN_NETBK_LEGACY_SLOTS_MAX (%d)\n",
diff --git a/drivers/nfc/fdp/i2c.c b/drivers/nfc/fdp/i2c.c
index 712936f..fbd26ec 100644
--- a/drivers/nfc/fdp/i2c.c
+++ b/drivers/nfc/fdp/i2c.c
@@ -177,6 +177,16 @@
 		/* Packet that contains a length */
 		if (tmp[0] == 0 && tmp[1] == 0) {
 			phy->next_read_size = (tmp[2] << 8) + tmp[3] + 3;
+			/*
+			 * Ensure next_read_size does not exceed sizeof(tmp)
+			 * for reading that many bytes during next iteration
+			 */
+			if (phy->next_read_size > FDP_NCI_I2C_MAX_PAYLOAD) {
+				dev_dbg(&client->dev, "%s: corrupted packet\n",
+					__func__);
+				phy->next_read_size = 5;
+				goto flush;
+			}
 		} else {
 			phy->next_read_size = FDP_NCI_I2C_MIN_PAYLOAD;
 
diff --git a/drivers/nfc/nq-nci.c b/drivers/nfc/nq-nci.c
index baa4f94..ea4bedf 100644
--- a/drivers/nfc/nq-nci.c
+++ b/drivers/nfc/nq-nci.c
@@ -354,6 +354,7 @@
 				usleep_range(1000, 1100);
 			}
 			gpio_set_value(nqx_dev->ese_gpio, 1);
+			usleep_range(1000, 1100);
 			if (gpio_get_value(nqx_dev->ese_gpio)) {
 				dev_dbg(&nqx_dev->client->dev, "ese_gpio is enabled\n");
 				r = 0;
@@ -406,6 +407,7 @@
 			 * there's no need to send the i2c commands
 			 */
 			gpio_set_value(nqx_dev->ese_gpio, 0);
+			usleep_range(1000, 1100);
 		}
 
 		if (!gpio_get_value(nqx_dev->ese_gpio)) {
diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c
index 798a32b..2062852 100644
--- a/drivers/nfc/st21nfca/dep.c
+++ b/drivers/nfc/st21nfca/dep.c
@@ -217,7 +217,8 @@
 
 	atr_req = (struct st21nfca_atr_req *)skb->data;
 
-	if (atr_req->length < sizeof(struct st21nfca_atr_req)) {
+	if (atr_req->length < sizeof(struct st21nfca_atr_req) ||
+	    atr_req->length > skb->len) {
 		r = -EPROTO;
 		goto exit;
 	}
diff --git a/drivers/nfc/st21nfca/se.c b/drivers/nfc/st21nfca/se.c
index 3a98563..6e84e12 100644
--- a/drivers/nfc/st21nfca/se.c
+++ b/drivers/nfc/st21nfca/se.c
@@ -320,23 +320,33 @@
 		 * AID		81	5 to 16
 		 * PARAMETERS	82	0 to 255
 		 */
-		if (skb->len < NFC_MIN_AID_LENGTH + 2 &&
+		if (skb->len < NFC_MIN_AID_LENGTH + 2 ||
 		    skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG)
 			return -EPROTO;
 
+		/*
+		 * Buffer should have enough space for at least
+		 * two tag fields + two length fields + aid_len (skb->data[1])
+		 */
+		if (skb->len < skb->data[1] + 4)
+			return -EPROTO;
+
 		transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev,
 						   skb->len - 2, GFP_KERNEL);
 
 		transaction->aid_len = skb->data[1];
 		memcpy(transaction->aid, &skb->data[2],
 		       transaction->aid_len);
-
-		/* Check next byte is PARAMETERS tag (82) */
-		if (skb->data[transaction->aid_len + 2] !=
-		    NFC_EVT_TRANSACTION_PARAMS_TAG)
-			return -EPROTO;
-
 		transaction->params_len = skb->data[transaction->aid_len + 3];
+
+		/* Check next byte is PARAMETERS tag (82) and the length field */
+		if (skb->data[transaction->aid_len + 2] !=
+		    NFC_EVT_TRANSACTION_PARAMS_TAG ||
+		    skb->len < transaction->aid_len + transaction->params_len + 4) {
+			devm_kfree(dev, transaction);
+			return -EPROTO;
+		}
+
 		memcpy(transaction->params, skb->data +
 		       transaction->aid_len + 4, transaction->params_len);
 
diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c
index c234ee43..24222a5 100644
--- a/drivers/ntb/ntb_transport.c
+++ b/drivers/ntb/ntb_transport.c
@@ -176,14 +176,12 @@
 	u64 rx_err_ver;
 	u64 rx_memcpy;
 	u64 rx_async;
-	u64 dma_rx_prep_err;
 	u64 tx_bytes;
 	u64 tx_pkts;
 	u64 tx_ring_full;
 	u64 tx_err_no_buf;
 	u64 tx_memcpy;
 	u64 tx_async;
-	u64 dma_tx_prep_err;
 };
 
 struct ntb_transport_mw {
@@ -256,8 +254,6 @@
 #define QP_TO_MW(nt, qp)	((qp) % nt->mw_count)
 #define NTB_QP_DEF_NUM_ENTRIES	100
 #define NTB_LINK_DOWN_TIMEOUT	10
-#define DMA_RETRIES		20
-#define DMA_OUT_RESOURCE_TO	msecs_to_jiffies(50)
 
 static void ntb_transport_rxc_db(unsigned long data);
 static const struct ntb_ctx_ops ntb_transport_ops;
@@ -518,12 +514,6 @@
 	out_offset += snprintf(buf + out_offset, out_count - out_offset,
 			       "free tx - \t%u\n",
 			       ntb_transport_tx_free_entry(qp));
-	out_offset += snprintf(buf + out_offset, out_count - out_offset,
-			       "DMA tx prep err - \t%llu\n",
-			       qp->dma_tx_prep_err);
-	out_offset += snprintf(buf + out_offset, out_count - out_offset,
-			       "DMA rx prep err - \t%llu\n",
-			       qp->dma_rx_prep_err);
 
 	out_offset += snprintf(buf + out_offset, out_count - out_offset,
 			       "\n");
@@ -625,7 +615,7 @@
 	if (!mw->virt_addr)
 		return -ENOMEM;
 
-	if (qp_count % mw_count && mw_num + 1 < qp_count / mw_count)
+	if (mw_num < qp_count % mw_count)
 		num_qps_mw = qp_count / mw_count + 1;
 	else
 		num_qps_mw = qp_count / mw_count;
@@ -770,8 +760,6 @@
 	qp->tx_err_no_buf = 0;
 	qp->tx_memcpy = 0;
 	qp->tx_async = 0;
-	qp->dma_tx_prep_err = 0;
-	qp->dma_rx_prep_err = 0;
 }
 
 static void ntb_qp_link_cleanup(struct ntb_transport_qp *qp)
@@ -933,10 +921,8 @@
 		ntb_free_mw(nt, i);
 
 	/* if there's an actual failure, we should just bail */
-	if (rc < 0) {
-		ntb_link_disable(ndev);
+	if (rc < 0)
 		return;
-	}
 
 out:
 	if (ntb_link_is_up(ndev, NULL, NULL) == 1)
@@ -1002,7 +988,7 @@
 	qp->event_handler = NULL;
 	ntb_qp_link_down_reset(qp);
 
-	if (qp_count % mw_count && mw_num + 1 < qp_count / mw_count)
+	if (mw_num < qp_count % mw_count)
 		num_qps_mw = qp_count / mw_count + 1;
 	else
 		num_qps_mw = qp_count / mw_count;
@@ -1125,8 +1111,8 @@
 	qp_count = ilog2(qp_bitmap);
 	if (max_num_clients && max_num_clients < qp_count)
 		qp_count = max_num_clients;
-	else if (mw_count < qp_count)
-		qp_count = mw_count;
+	else if (nt->mw_count < qp_count)
+		qp_count = nt->mw_count;
 
 	qp_bitmap &= BIT_ULL(qp_count) - 1;
 
@@ -1314,7 +1300,6 @@
 	struct dmaengine_unmap_data *unmap;
 	dma_cookie_t cookie;
 	void *buf = entry->buf;
-	int retries = 0;
 
 	len = entry->len;
 	device = chan->device;
@@ -1343,22 +1328,11 @@
 
 	unmap->from_cnt = 1;
 
-	for (retries = 0; retries < DMA_RETRIES; retries++) {
-		txd = device->device_prep_dma_memcpy(chan,
-						     unmap->addr[1],
-						     unmap->addr[0], len,
-						     DMA_PREP_INTERRUPT);
-		if (txd)
-			break;
-
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(DMA_OUT_RESOURCE_TO);
-	}
-
-	if (!txd) {
-		qp->dma_rx_prep_err++;
+	txd = device->device_prep_dma_memcpy(chan, unmap->addr[1],
+					     unmap->addr[0], len,
+					     DMA_PREP_INTERRUPT);
+	if (!txd)
 		goto err_get_unmap;
-	}
 
 	txd->callback_result = ntb_rx_copy_callback;
 	txd->callback_param = entry;
@@ -1603,7 +1577,6 @@
 	struct dmaengine_unmap_data *unmap;
 	dma_addr_t dest;
 	dma_cookie_t cookie;
-	int retries = 0;
 
 	device = chan->device;
 	dest = qp->tx_mw_phys + qp->tx_max_frame * entry->tx_index;
@@ -1625,21 +1598,10 @@
 
 	unmap->to_cnt = 1;
 
-	for (retries = 0; retries < DMA_RETRIES; retries++) {
-		txd = device->device_prep_dma_memcpy(chan, dest,
-						     unmap->addr[0], len,
-						     DMA_PREP_INTERRUPT);
-		if (txd)
-			break;
-
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(DMA_OUT_RESOURCE_TO);
-	}
-
-	if (!txd) {
-		qp->dma_tx_prep_err++;
+	txd = device->device_prep_dma_memcpy(chan, dest, unmap->addr[0], len,
+					     DMA_PREP_INTERRUPT);
+	if (!txd)
 		goto err_get_unmap;
-	}
 
 	txd->callback_result = ntb_tx_copy_callback;
 	txd->callback_param = entry;
diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 5a3f008..eef1a68 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -77,7 +77,7 @@
 	kref_init(&host->ref);
 	uuid_be_gen(&host->id);
 	snprintf(host->nqn, NVMF_NQN_SIZE,
-		"nqn.2014-08.org.nvmexpress:NVMf:uuid:%pUb", &host->id);
+		"nqn.2014-08.org.nvmexpress:uuid:%pUb", &host->id);
 
 	mutex_lock(&nvmf_hosts_mutex);
 	list_add_tail(&host->list, &nvmf_hosts);
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index 14eac73..54ea90f 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -96,7 +96,7 @@
 	struct mutex shutdown_lock;
 	bool subsystem;
 	void __iomem *cmb;
-	dma_addr_t cmb_dma_addr;
+	pci_bus_addr_t cmb_bus_addr;
 	u64 cmb_size;
 	u32 cmbsz;
 	u32 cmbloc;
@@ -1037,7 +1037,7 @@
 	if (qid && dev->cmb && use_cmb_sqes && NVME_CMB_SQS(dev->cmbsz)) {
 		unsigned offset = (qid - 1) * roundup(SQ_SIZE(depth),
 						      dev->ctrl.page_size);
-		nvmeq->sq_dma_addr = dev->cmb_dma_addr + offset;
+		nvmeq->sq_dma_addr = dev->cmb_bus_addr + offset;
 		nvmeq->sq_cmds_io = dev->cmb + offset;
 	} else {
 		nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth),
@@ -1343,7 +1343,7 @@
 	resource_size_t bar_size;
 	struct pci_dev *pdev = to_pci_dev(dev->dev);
 	void __iomem *cmb;
-	dma_addr_t dma_addr;
+	int bar;
 
 	dev->cmbsz = readl(dev->bar + NVME_REG_CMBSZ);
 	if (!(NVME_CMB_SZ(dev->cmbsz)))
@@ -1356,7 +1356,8 @@
 	szu = (u64)1 << (12 + 4 * NVME_CMB_SZU(dev->cmbsz));
 	size = szu * NVME_CMB_SZ(dev->cmbsz);
 	offset = szu * NVME_CMB_OFST(dev->cmbloc);
-	bar_size = pci_resource_len(pdev, NVME_CMB_BIR(dev->cmbloc));
+	bar = NVME_CMB_BIR(dev->cmbloc);
+	bar_size = pci_resource_len(pdev, bar);
 
 	if (offset > bar_size)
 		return NULL;
@@ -1369,12 +1370,11 @@
 	if (size > bar_size - offset)
 		size = bar_size - offset;
 
-	dma_addr = pci_resource_start(pdev, NVME_CMB_BIR(dev->cmbloc)) + offset;
-	cmb = ioremap_wc(dma_addr, size);
+	cmb = ioremap_wc(pci_resource_start(pdev, bar) + offset, size);
 	if (!cmb)
 		return NULL;
 
-	dev->cmb_dma_addr = dma_addr;
+	dev->cmb_bus_addr = pci_bus_address(pdev, bar) + offset;
 	dev->cmb_size = size;
 	return cmb;
 }
diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c
index 286fda4..ab4f8db 100644
--- a/drivers/nvme/host/rdma.c
+++ b/drivers/nvme/host/rdma.c
@@ -337,8 +337,6 @@
 	struct ib_device *ibdev = dev->dev;
 	int ret;
 
-	BUG_ON(queue_idx >= ctrl->queue_count);
-
 	ret = nvme_rdma_alloc_qe(ibdev, &req->sqe, sizeof(struct nvme_command),
 			DMA_TO_DEVICE);
 	if (ret)
@@ -643,8 +641,22 @@
 
 static int nvme_rdma_init_io_queues(struct nvme_rdma_ctrl *ctrl)
 {
+	struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
+	unsigned int nr_io_queues;
 	int i, ret;
 
+	nr_io_queues = min(opts->nr_io_queues, num_online_cpus());
+	ret = nvme_set_queue_count(&ctrl->ctrl, &nr_io_queues);
+	if (ret)
+		return ret;
+
+	ctrl->queue_count = nr_io_queues + 1;
+	if (ctrl->queue_count < 2)
+		return 0;
+
+	dev_info(ctrl->ctrl.device,
+		"creating %d I/O queues.\n", nr_io_queues);
+
 	for (i = 1; i < ctrl->queue_count; i++) {
 		ret = nvme_rdma_init_queue(ctrl, i,
 					   ctrl->ctrl.opts->queue_size);
@@ -1795,20 +1807,8 @@
 
 static int nvme_rdma_create_io_queues(struct nvme_rdma_ctrl *ctrl)
 {
-	struct nvmf_ctrl_options *opts = ctrl->ctrl.opts;
 	int ret;
 
-	ret = nvme_set_queue_count(&ctrl->ctrl, &opts->nr_io_queues);
-	if (ret)
-		return ret;
-
-	ctrl->queue_count = opts->nr_io_queues + 1;
-	if (ctrl->queue_count < 2)
-		return 0;
-
-	dev_info(ctrl->ctrl.device,
-		"creating %d I/O queues.\n", opts->nr_io_queues);
-
 	ret = nvme_rdma_init_io_queues(ctrl);
 	if (ret)
 		return ret;
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index c807c28..4a1cab5 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -24,6 +24,7 @@
 #include <linux/of_reserved_mem.h>
 #include <linux/sort.h>
 #include <linux/slab.h>
+#include <linux/kmemleak.h>
 
 #define MAX_RESERVED_REGIONS	32
 static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
@@ -54,8 +55,10 @@
 	}
 
 	*res_base = base;
-	if (nomap)
+	if (nomap) {
+		kmemleak_ignore_phys(base);
 		return memblock_remove(base, size);
+	}
 	return 0;
 }
 #else
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 5c63b92..ed92c12 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -956,7 +956,7 @@
 
 	dino_dev->hba.dev = dev;
 	dino_dev->hba.base_addr = ioremap_nocache(hpa, 4096);
-	dino_dev->hba.lmmio_space_offset = 0;	/* CPU addrs == bus addrs */
+	dino_dev->hba.lmmio_space_offset = PCI_F_EXTEND;
 	spin_lock_init(&dino_dev->dinosaur_pen);
 	dino_dev->hba.iommu = ccio_get_iommu(dev);
 
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index d11cdbb..7b5cf6d 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -672,8 +672,9 @@
 	WARN_ON(!dev->block_cfg_access);
 
 	dev->block_cfg_access = 0;
-	wake_up_all(&pci_cfg_wait);
 	raw_spin_unlock_irqrestore(&pci_lock, flags);
+
+	wake_up_all(&pci_cfg_wait);
 }
 EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
 
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
index 57dc4a0..9e0989c 100644
--- a/drivers/pci/host/pci-msm.c
+++ b/drivers/pci/host/pci-msm.c
@@ -19,6 +19,7 @@
 #include <linux/clk.h>
 #include <linux/debugfs.h>
 #include <linux/delay.h>
+#include <linux/jiffies.h>
 #include <linux/gpio.h>
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
@@ -120,6 +121,12 @@
 #define PCIE20_PLR_IATU_LTAR	     0x918
 #define PCIE20_PLR_IATU_UTAR	     0x91c
 
+
+#define PCIE20_PORT_LINK_CTRL_REG	0x710
+#define PCIE20_GEN3_RELATED_REG	0x890
+#define PCIE20_PIPE_LOOPBACK_CONTROL	0x8b8
+#define LOOPBACK_BASE_ADDR_OFFSET	0x8000
+
 #define PCIE20_CTRL1_TYPE_CFG0		0x04
 #define PCIE20_CTRL1_TYPE_CFG1		0x05
 
@@ -140,11 +147,15 @@
 
 #define PERST_PROPAGATION_DELAY_US_MIN	  1000
 #define PERST_PROPAGATION_DELAY_US_MAX	  1005
+#define SWITCH_DELAY_MAX	  20
 #define REFCLK_STABILIZATION_DELAY_US_MIN     1000
 #define REFCLK_STABILIZATION_DELAY_US_MAX     1005
 #define LINK_UP_TIMEOUT_US_MIN		    5000
 #define LINK_UP_TIMEOUT_US_MAX		    5100
 #define LINK_UP_CHECK_MAX_COUNT		   20
+#define EP_UP_TIMEOUT_US_MIN	1000
+#define EP_UP_TIMEOUT_US_MAX	1005
+#define EP_UP_TIMEOUT_US	1000000
 #define PHY_STABILIZATION_DELAY_US_MIN	  995
 #define PHY_STABILIZATION_DELAY_US_MAX	  1005
 #define POWER_DOWN_DELAY_US_MIN		10
@@ -492,6 +503,7 @@
 	uint32_t			max_link_speed;
 	bool				 ext_ref_clk;
 	uint32_t			   ep_latency;
+	uint32_t			switch_latency;
 	uint32_t			wr_halt_size;
 	uint32_t			slv_addr_space_size;
 	uint32_t			cpl_timeout;
@@ -546,12 +558,37 @@
 	struct msm_pcie_device_info   pcidev_table[MAX_DEVICE_NUM];
 };
 
-
 /* debug mask sys interface */
 static int msm_pcie_debug_mask;
 module_param_named(debug_mask, msm_pcie_debug_mask,
 			    int, 0644);
 
+/*
+ * For each bit set, invert the default capability
+ * option for the corresponding root complex
+ * and its devices.
+ */
+static int msm_pcie_invert_l0s_support;
+module_param_named(invert_l0s_support, msm_pcie_invert_l0s_support,
+			    int, 0644);
+static int msm_pcie_invert_l1_support;
+module_param_named(invert_l1_support, msm_pcie_invert_l1_support,
+			    int, 0644);
+static int msm_pcie_invert_l1ss_support;
+module_param_named(invert_l1ss_support, msm_pcie_invert_l1ss_support,
+			    int, 0644);
+static int msm_pcie_invert_aer_support;
+module_param_named(invert_aer_support, msm_pcie_invert_aer_support,
+			    int, 0644);
+
+/*
+ * For each bit set, keep the resources on when link training fails
+ * or linkdown occurs for the corresponding root complex
+ */
+static int msm_pcie_keep_resources_on;
+module_param_named(keep_resources_on, msm_pcie_keep_resources_on,
+			    int, 0644);
+
 /* debugfs values */
 static u32 rc_sel;
 static u32 base_sel;
@@ -731,6 +768,8 @@
 };
 
 static int msm_pcie_config_device(struct pci_dev *dev, void *pdev);
+static void msm_pcie_config_link_pm_rc(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable);
 
 #ifdef CONFIG_ARM
 #define PCIE_BUS_PRIV_DATA(bus) \
@@ -784,6 +823,17 @@
 	wmb();
 }
 
+static inline void msm_pcie_config_clear_set_dword(struct pci_dev *pdev,
+	int pos, u32 clear, u32 set)
+{
+	u32 val;
+
+	pci_read_config_dword(pdev, pos, &val);
+	val &= ~clear;
+	val |= set;
+	pci_write_config_dword(pdev, pos, val);
+}
+
 static inline void msm_pcie_config_clock_mem(struct msm_pcie_dev_t *dev,
 	struct msm_pcie_clk_info_t *info)
 {
@@ -1112,6 +1162,8 @@
 		dev->n_fts);
 	PCIE_DBG_FS(dev, "ep_latency: %dms\n",
 		dev->ep_latency);
+	PCIE_DBG_FS(dev, "switch_latency: %dms\n",
+		dev->switch_latency);
 	PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n",
 		dev->wr_halt_size);
 	PCIE_DBG_FS(dev, "slv_addr_space_size: 0x%x\n",
@@ -1184,6 +1236,14 @@
 static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
 					u32 testcase)
 {
+	phys_addr_t dbi_base_addr =
+		dev->res[MSM_PCIE_RES_DM_CORE].resource->start;
+	phys_addr_t loopback_lbar_phy =
+		dbi_base_addr + LOOPBACK_BASE_ADDR_OFFSET;
+	static uint32_t loopback_val = 0x1;
+	static u64 loopback_ddr_phy;
+	static uint32_t *loopback_ddr_vir;
+	static void __iomem *loopback_lbar_vir;
 	int ret, i;
 	u32 base_sel_size = 0;
 	u32 val = 0;
@@ -1597,6 +1657,282 @@
 			readl_relaxed(dev->res[base_sel - 1].base + (i + 28)));
 		}
 		break;
+	case 14:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Allocate 4K DDR memory and map LBAR.\n",
+			dev->rc_idx);
+		loopback_ddr_vir = dma_alloc_coherent(&dev->pdev->dev,
+			(SZ_1K * sizeof(*loopback_ddr_vir)),
+			&loopback_ddr_phy, GFP_KERNEL);
+		if (!loopback_ddr_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: failed to dma_alloc_coherent.\n",
+				dev->rc_idx);
+		} else {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: VIR DDR memory address: 0x%pK\n",
+				dev->rc_idx, loopback_ddr_vir);
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: PHY DDR memory address: 0x%llx\n",
+				dev->rc_idx, loopback_ddr_phy);
+		}
+
+		PCIE_DBG_FS(dev, "PCIe: RC%d: map LBAR: 0x%llx\n",
+			dev->rc_idx, loopback_lbar_phy);
+		loopback_lbar_vir = devm_ioremap(&dev->pdev->dev,
+			loopback_lbar_phy, SZ_4K);
+		if (!loopback_lbar_vir) {
+			PCIE_DBG_FS(dev, "PCIe: RC%d: failed to map 0x%llx\n",
+				dev->rc_idx, loopback_lbar_phy);
+		} else {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: successfully mapped 0x%llx to 0x%pK\n",
+				dev->rc_idx, loopback_lbar_phy,
+				loopback_lbar_vir);
+		}
+		break;
+	case 15:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Release 4K DDR memory and unmap LBAR.\n",
+			dev->rc_idx);
+
+		if (loopback_ddr_vir) {
+			dma_free_coherent(&dev->pdev->dev, SZ_4K,
+				loopback_ddr_vir, loopback_ddr_phy);
+			loopback_ddr_vir = NULL;
+		}
+
+		if (loopback_lbar_vir) {
+			devm_iounmap(&dev->pdev->dev,
+				loopback_lbar_vir);
+			loopback_lbar_vir = NULL;
+		}
+		break;
+	case 16:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Print DDR and LBAR addresses.\n",
+			dev->rc_idx);
+
+		if (!loopback_ddr_vir || !loopback_lbar_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: DDR or LBAR address is not mapped\n",
+				dev->rc_idx);
+			break;
+		}
+
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PHY DDR address: 0x%llx\n",
+			dev->rc_idx, loopback_ddr_phy);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: VIR DDR address: 0x%pK\n",
+			dev->rc_idx, loopback_ddr_vir);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PHY LBAR address: 0x%llx\n",
+			dev->rc_idx, loopback_lbar_phy);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: VIR LBAR address: 0x%pK\n",
+			dev->rc_idx, loopback_lbar_vir);
+		break;
+	case 17:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Configure Loopback.\n",
+			dev->rc_idx);
+
+		writel_relaxed(0x10000,
+			dev->dm_core + PCIE20_GEN3_RELATED_REG);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: 0x%llx: 0x%x\n",
+			dev->rc_idx,
+			dbi_base_addr + PCIE20_GEN3_RELATED_REG,
+			readl_relaxed(dev->dm_core +
+				PCIE20_GEN3_RELATED_REG));
+
+		writel_relaxed(0x80000001,
+			dev->dm_core + PCIE20_PIPE_LOOPBACK_CONTROL);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: 0x%llx: 0x%x\n",
+			dev->rc_idx,
+			dbi_base_addr + PCIE20_PIPE_LOOPBACK_CONTROL,
+			readl_relaxed(dev->dm_core +
+				PCIE20_PIPE_LOOPBACK_CONTROL));
+
+		writel_relaxed(0x00010124,
+			dev->dm_core + PCIE20_PORT_LINK_CTRL_REG);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: 0x%llx: 0x%x\n",
+			dev->rc_idx,
+			dbi_base_addr + PCIE20_PORT_LINK_CTRL_REG,
+			readl_relaxed(dev->dm_core +
+				PCIE20_PORT_LINK_CTRL_REG));
+		break;
+	case 18:
+		PCIE_DBG_FS(dev, "PCIe: RC%d: Setup iATU.\n", dev->rc_idx);
+
+		if (!loopback_ddr_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: DDR address is not mapped.\n",
+				dev->rc_idx);
+			break;
+		}
+
+		writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_VIEWPORT);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_VIEWPORT:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_VIEWPORT,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT));
+
+		writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_CTRL1);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_CTRL1:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_CTRL1,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1));
+
+		writel_relaxed(loopback_lbar_phy,
+			dev->dm_core + PCIE20_PLR_IATU_LBAR);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_LBAR:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LBAR,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR));
+
+		writel_relaxed(0x0, dev->dm_core + PCIE20_PLR_IATU_UBAR);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_UBAR:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_UBAR,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR));
+
+		writel_relaxed(loopback_lbar_phy + 0xfff,
+			dev->dm_core + PCIE20_PLR_IATU_LAR);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_LAR:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LAR,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR));
+
+		writel_relaxed(loopback_ddr_phy,
+			dev->dm_core + PCIE20_PLR_IATU_LTAR);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_LTAR:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_LTAR,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR));
+
+		writel_relaxed(0, dev->dm_core + PCIE20_PLR_IATU_UTAR);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_UTAR:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_UTAR,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR));
+
+		writel_relaxed(0x80000000,
+			dev->dm_core + PCIE20_PLR_IATU_CTRL2);
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: PCIE20_PLR_IATU_CTRL2:\t0x%llx: 0x%x\n",
+			dev->rc_idx, dbi_base_addr + PCIE20_PLR_IATU_CTRL2,
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2));
+		break;
+	case 19:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Read DDR values.\n",
+			dev->rc_idx);
+
+		if (!loopback_ddr_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: DDR is not mapped\n",
+				dev->rc_idx);
+			break;
+		}
+
+		for (i = 0; i < SZ_1K; i += 8) {
+			PCIE_DBG_FS(dev,
+				"0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+				i,
+				loopback_ddr_vir[i],
+				loopback_ddr_vir[i + 1],
+				loopback_ddr_vir[i + 2],
+				loopback_ddr_vir[i + 3],
+				loopback_ddr_vir[i + 4],
+				loopback_ddr_vir[i + 5],
+				loopback_ddr_vir[i + 6],
+				loopback_ddr_vir[i + 7]);
+		}
+		break;
+	case 20:
+		PCIE_DBG_FS(dev,
+			"PCIe: RC%d: Read LBAR values.\n",
+			dev->rc_idx);
+
+		if (!loopback_lbar_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: LBAR address is not mapped\n",
+				dev->rc_idx);
+			break;
+		}
+
+		for (i = 0; i < SZ_4K; i += 32) {
+			PCIE_DBG_FS(dev,
+				"0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+				i,
+				readl_relaxed(loopback_lbar_vir + i),
+				readl_relaxed(loopback_lbar_vir + (i + 4)),
+				readl_relaxed(loopback_lbar_vir + (i + 8)),
+				readl_relaxed(loopback_lbar_vir + (i + 12)),
+				readl_relaxed(loopback_lbar_vir + (i + 16)),
+				readl_relaxed(loopback_lbar_vir + (i + 20)),
+				readl_relaxed(loopback_lbar_vir + (i + 24)),
+				readl_relaxed(loopback_lbar_vir + (i + 28)));
+		}
+		break;
+	case 21:
+		PCIE_DBG_FS(dev, "PCIe: RC%d: Write 0x%x to DDR.\n",
+			dev->rc_idx, loopback_val);
+
+		if (!loopback_ddr_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: DDR address is not mapped\n",
+				dev->rc_idx);
+			break;
+		}
+
+		memset(loopback_ddr_vir, loopback_val,
+			(SZ_1K * sizeof(*loopback_ddr_vir)));
+
+		if (unlikely(loopback_val == UINT_MAX))
+			loopback_val = 1;
+		else
+			loopback_val++;
+		break;
+	case 22:
+		PCIE_DBG_FS(dev, "PCIe: RC%d: Write 0x%x to LBAR.\n",
+			dev->rc_idx, loopback_val);
+
+		if (!loopback_lbar_vir) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d: LBAR address is not mapped\n",
+				dev->rc_idx);
+			break;
+		}
+
+		for (i = 0; i < SZ_4K; i += 32) {
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + i),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 4)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 8)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 12)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 16)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 20)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 24)),
+			writel_relaxed(loopback_val,
+				loopback_lbar_vir + (i + 28));
+		}
+
+		if (unlikely(loopback_val == UINT_MAX))
+			loopback_val = 1;
+		else
+			loopback_val++;
+		break;
 	default:
 		PCIE_DBG_FS(dev, "Invalid testcase: %d.\n", testcase);
 		break;
@@ -3014,194 +3350,6 @@
 	}
 }
 
-static void msm_pcie_config_link_state(struct msm_pcie_dev_t *dev)
-{
-	u32 val;
-	u32 current_offset;
-	u32 ep_l1sub_ctrl1_offset = 0;
-	u32 ep_l1sub_cap_reg1_offset = 0;
-	u32 ep_link_cap_offset = 0;
-	u32 ep_link_ctrlstts_offset = 0;
-	u32 ep_dev_ctrl2stts2_offset = 0;
-
-	/* Enable the AUX Clock and the Core Clk to be synchronous for L1SS*/
-	if (!dev->aux_clk_sync && dev->l1ss_supported)
-		msm_pcie_write_mask(dev->parf +
-				PCIE20_PARF_SYS_CTRL, BIT(3), 0);
-
-	current_offset = readl_relaxed(dev->conf + PCIE_CAP_PTR_OFFSET) & 0xff;
-
-	while (current_offset) {
-		if (msm_pcie_check_align(dev, current_offset))
-			return;
-
-		val = readl_relaxed(dev->conf + current_offset);
-		if ((val & 0xff) == PCIE20_CAP_ID) {
-			ep_link_cap_offset = current_offset + 0x0c;
-			ep_link_ctrlstts_offset = current_offset + 0x10;
-			ep_dev_ctrl2stts2_offset = current_offset + 0x28;
-			break;
-		}
-		current_offset = (val >> 8) & 0xff;
-	}
-
-	if (!ep_link_cap_offset) {
-		PCIE_DBG(dev,
-			"RC%d endpoint does not support PCIe capability registers\n",
-			dev->rc_idx);
-		return;
-	}
-
-	PCIE_DBG(dev,
-		"RC%d: ep_link_cap_offset: 0x%x\n",
-		dev->rc_idx, ep_link_cap_offset);
-
-	if (dev->common_clk_en) {
-		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
-					0, BIT(6));
-
-		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
-					0, BIT(6));
-
-		if (dev->shadow_en) {
-			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
-				readl_relaxed(dev->dm_core +
-					PCIE20_CAP_LINKCTRLSTATUS);
-
-			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
-				readl_relaxed(dev->conf +
-					ep_link_ctrlstts_offset);
-		}
-
-		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->dm_core +
-			PCIE20_CAP_LINKCTRLSTATUS));
-		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
-	}
-
-	if (dev->clk_power_manage_en) {
-		val = readl_relaxed(dev->conf + ep_link_cap_offset);
-		if (val & BIT(18)) {
-			msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
-						0, BIT(8));
-
-			if (dev->shadow_en)
-				dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
-					readl_relaxed(dev->conf +
-						ep_link_ctrlstts_offset);
-
-			PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
-				readl_relaxed(dev->conf +
-					ep_link_ctrlstts_offset));
-		}
-	}
-
-	if (dev->l0s_supported) {
-		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
-					0, BIT(0));
-		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
-					0, BIT(0));
-		if (dev->shadow_en) {
-			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
-						readl_relaxed(dev->dm_core +
-						PCIE20_CAP_LINKCTRLSTATUS);
-			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
-						readl_relaxed(dev->conf +
-						ep_link_ctrlstts_offset);
-		}
-		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->dm_core +
-			PCIE20_CAP_LINKCTRLSTATUS));
-		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
-	}
-
-	if (dev->l1_supported) {
-		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
-					0, BIT(1));
-		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
-					0, BIT(1));
-		if (dev->shadow_en) {
-			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
-						readl_relaxed(dev->dm_core +
-						PCIE20_CAP_LINKCTRLSTATUS);
-			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
-						readl_relaxed(dev->conf +
-						ep_link_ctrlstts_offset);
-		}
-		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->dm_core +
-			PCIE20_CAP_LINKCTRLSTATUS));
-		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
-			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
-	}
-
-	if (dev->l1ss_supported) {
-		current_offset = PCIE_EXT_CAP_OFFSET;
-		while (current_offset) {
-			if (msm_pcie_check_align(dev, current_offset))
-				return;
-
-			val = readl_relaxed(dev->conf + current_offset);
-			if ((val & 0xffff) == L1SUB_CAP_ID) {
-				ep_l1sub_cap_reg1_offset = current_offset + 0x4;
-				ep_l1sub_ctrl1_offset = current_offset + 0x8;
-				break;
-			}
-			current_offset = val >> 20;
-		}
-		if (!ep_l1sub_ctrl1_offset) {
-			PCIE_DBG(dev,
-				"RC%d endpoint does not support l1ss registers\n",
-				dev->rc_idx);
-			return;
-		}
-
-		val = readl_relaxed(dev->conf + ep_l1sub_cap_reg1_offset);
-
-		PCIE_DBG2(dev, "EP's L1SUB_CAPABILITY_REG_1: 0x%x\n", val);
-		PCIE_DBG2(dev, "RC%d: ep_l1sub_ctrl1_offset: 0x%x\n",
-				dev->rc_idx, ep_l1sub_ctrl1_offset);
-
-		val &= 0xf;
-
-		msm_pcie_write_reg_field(dev->dm_core, PCIE20_L1SUB_CONTROL1,
-					0xf, val);
-		msm_pcie_write_mask(dev->dm_core +
-					PCIE20_DEVICE_CONTROL2_STATUS2,
-					0, BIT(10));
-		msm_pcie_write_reg_field(dev->conf, ep_l1sub_ctrl1_offset,
-					0xf, val);
-		msm_pcie_write_mask(dev->conf + ep_dev_ctrl2stts2_offset,
-					0, BIT(10));
-		if (dev->shadow_en) {
-			dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] =
-					readl_relaxed(dev->dm_core +
-					PCIE20_L1SUB_CONTROL1);
-			dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] =
-					readl_relaxed(dev->dm_core +
-					PCIE20_DEVICE_CONTROL2_STATUS2);
-			dev->ep_shadow[0][ep_l1sub_ctrl1_offset / 4] =
-					readl_relaxed(dev->conf +
-					ep_l1sub_ctrl1_offset);
-			dev->ep_shadow[0][ep_dev_ctrl2stts2_offset / 4] =
-					readl_relaxed(dev->conf +
-					ep_dev_ctrl2stts2_offset);
-		}
-		PCIE_DBG2(dev, "RC's L1SUB_CONTROL1:0x%x\n",
-			readl_relaxed(dev->dm_core + PCIE20_L1SUB_CONTROL1));
-		PCIE_DBG2(dev, "RC's DEVICE_CONTROL2_STATUS2:0x%x\n",
-			readl_relaxed(dev->dm_core +
-			PCIE20_DEVICE_CONTROL2_STATUS2));
-		PCIE_DBG2(dev, "EP's L1SUB_CONTROL1:0x%x\n",
-			readl_relaxed(dev->conf + ep_l1sub_ctrl1_offset));
-		PCIE_DBG2(dev, "EP's DEVICE_CONTROL2_STATUS2:0x%x\n",
-			readl_relaxed(dev->conf +
-			ep_dev_ctrl2stts2_offset));
-	}
-}
-
 static void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev)
 {
 	int i;
@@ -3622,6 +3770,7 @@
 	uint32_t val;
 	long int retries = 0;
 	int link_check_count = 0;
+	unsigned long ep_up_timeout = 0;
 
 	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
 
@@ -3782,6 +3931,8 @@
 				1 - dev->gpio[MSM_PCIE_GPIO_PERST].on);
 	usleep_range(dev->perst_delay_us_min, dev->perst_delay_us_max);
 
+	ep_up_timeout = jiffies + usecs_to_jiffies(EP_UP_TIMEOUT_US);
+
 	/* setup Gen3 specific configurations */
 	if (dev->max_link_speed == GEN3_SPEED)
 		msm_pcie_setup_gen3(dev);
@@ -3821,24 +3972,59 @@
 		goto link_fail;
 	}
 
-	msm_pcie_config_controller(dev);
-
-	if (!dev->msi_gicm_addr)
-		msm_pcie_config_msi_controller(dev);
-
-	msm_pcie_config_link_state(dev);
-
-	if (dev->enumerated)
-		pci_walk_bus(dev->dev->bus, &msm_pcie_config_device, dev);
-
 	dev->link_status = MSM_PCIE_LINK_ENABLED;
 	dev->power_on = true;
 	dev->suspending = false;
 	dev->link_turned_on_counter++;
 
+	if (dev->switch_latency) {
+		PCIE_DBG(dev, "switch_latency: %dms\n",
+			dev->switch_latency);
+		if (dev->switch_latency <= SWITCH_DELAY_MAX)
+			usleep_range(dev->switch_latency * 1000,
+				dev->switch_latency * 1000);
+		else
+			msleep(dev->switch_latency);
+	}
+
+	msm_pcie_config_controller(dev);
+
+	/* check endpoint configuration space is accessible */
+	while (time_before(jiffies, ep_up_timeout)) {
+		if (readl_relaxed(dev->conf) != PCIE_LINK_DOWN)
+			break;
+		usleep_range(EP_UP_TIMEOUT_US_MIN, EP_UP_TIMEOUT_US_MAX);
+	}
+
+	if (readl_relaxed(dev->conf) != PCIE_LINK_DOWN) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d: endpoint config space is accessible\n",
+			dev->rc_idx);
+	} else {
+		PCIE_ERR(dev,
+			"PCIe: RC%d: endpoint config space is not accessible\n",
+			dev->rc_idx);
+		dev->link_status = MSM_PCIE_LINK_DISABLED;
+		dev->power_on = false;
+		dev->link_turned_off_counter++;
+		ret = -ENODEV;
+		goto link_fail;
+	}
+
+	if (!dev->msi_gicm_addr)
+		msm_pcie_config_msi_controller(dev);
+
+	if (dev->enumerated) {
+		pci_walk_bus(dev->dev->bus, &msm_pcie_config_device, dev);
+		msm_pcie_config_link_pm_rc(dev, dev->dev, true);
+	}
+
 	goto out;
 
 link_fail:
+	if (msm_pcie_keep_resources_on & BIT(dev->rc_idx))
+		goto out;
+
 	if (dev->gpio[MSM_PCIE_GPIO_EP].num)
 		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num,
 				1 - dev->gpio[MSM_PCIE_GPIO_EP].on);
@@ -4032,8 +4218,9 @@
 					if (pcie_dev->num_ep > 1)
 						pcie_dev->pending_ep_reg = true;
 
-					msm_pcie_config_ep_aer(pcie_dev,
-						&dev_table_t[index]);
+					if (pcie_dev->aer_enable)
+						msm_pcie_config_ep_aer(pcie_dev,
+							&dev_table_t[index]);
 
 					break;
 				}
@@ -4213,6 +4400,8 @@
 				ret = -ENODEV;
 				goto out;
 			}
+
+			msm_pcie_config_link_pm_rc(dev, dev->dev, true);
 		} else {
 			PCIE_ERR(dev, "PCIe: failed to enable RC%d.\n",
 				dev->rc_idx);
@@ -4547,8 +4736,10 @@
 			panic("User has chosen to panic on linkdown\n");
 
 		/* assert PERST */
-		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
-				dev->gpio[MSM_PCIE_GPIO_PERST].on);
+		if (!(msm_pcie_keep_resources_on & BIT(dev->rc_idx)))
+			gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+					dev->gpio[MSM_PCIE_GPIO_PERST].on);
+
 		PCIE_ERR(dev, "PCIe link is down for RC%d\n", dev->rc_idx);
 
 		if (dev->num_ep > 1) {
@@ -5198,6 +5389,238 @@
 		disable_irq(dev->wake_n);
 }
 
+static void msm_pcie_config_l0s(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable)
+{
+	u32 val;
+	u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+	u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+	pci_read_config_dword(pdev, lnkcap_offset, &val);
+	if (!(val & BIT(10))) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d: PCI device does not support L0s\n",
+			dev->rc_idx);
+		return;
+	}
+
+	if (enable)
+		msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+			PCI_EXP_LNKCTL_ASPM_L0S);
+	else
+		msm_pcie_config_clear_set_dword(pdev, lnkctl_offset,
+			PCI_EXP_LNKCTL_ASPM_L0S, 0);
+
+	pci_read_config_dword(pdev, lnkctl_offset, &val);
+	PCIE_DBG2(dev, "PCIe: RC%d: LINKCTRLSTATUS:0x%x\n", dev->rc_idx, val);
+}
+
+static void msm_pcie_config_l1(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable)
+{
+	u32 val;
+	u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+	u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+	pci_read_config_dword(pdev, lnkcap_offset, &val);
+	if (!(val & BIT(11))) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d: PCI device does not support L1\n",
+			dev->rc_idx);
+		return;
+	}
+
+	if (enable)
+		msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+			PCI_EXP_LNKCTL_ASPM_L1);
+	else
+		msm_pcie_config_clear_set_dword(pdev, lnkctl_offset,
+			PCI_EXP_LNKCTL_ASPM_L1, 0);
+
+	pci_read_config_dword(pdev, lnkctl_offset, &val);
+	PCIE_DBG2(dev, "PCIe: RC%d: LINKCTRLSTATUS:0x%x\n", dev->rc_idx, val);
+}
+
+static void msm_pcie_config_l1ss(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable)
+{
+	bool l1_1_cap_support, l1_2_cap_support;
+	u32 val, val2;
+	u32 l1ss_cap_id_offset, l1ss_cap_offset, l1ss_ctl1_offset;
+	u32 devctl2_offset = pdev->pcie_cap + PCI_EXP_DEVCTL2;
+
+	l1ss_cap_id_offset = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS);
+	if (!l1ss_cap_id_offset) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d could not find L1ss capability register for device\n",
+			dev->rc_idx);
+		return;
+	}
+
+	l1ss_cap_offset = l1ss_cap_id_offset + PCI_L1SS_CAP;
+	l1ss_ctl1_offset = l1ss_cap_id_offset + PCI_L1SS_CTL1;
+
+	pci_read_config_dword(pdev, l1ss_cap_offset, &val);
+	l1_1_cap_support = !!(val & (PCI_L1SS_CAP_ASPM_L1_1));
+	l1_2_cap_support = !!(val & (PCI_L1SS_CAP_ASPM_L1_2));
+	if (!l1_1_cap_support && !l1_2_cap_support) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d: PCI device does not support L1.1 and L1.2\n",
+			dev->rc_idx);
+		return;
+	}
+
+	/* Enable the AUX Clock and the Core Clk to be synchronous for L1ss */
+	if (pci_is_root_bus(pdev->bus) && !dev->aux_clk_sync) {
+		if (enable)
+			msm_pcie_write_mask(dev->parf +
+				PCIE20_PARF_SYS_CTRL, BIT(3), 0);
+		else
+			msm_pcie_write_mask(dev->parf +
+				PCIE20_PARF_SYS_CTRL, 0, BIT(3));
+	}
+
+	if (enable) {
+		msm_pcie_config_clear_set_dword(pdev, devctl2_offset, 0,
+			PCI_EXP_DEVCTL2_LTR_EN);
+		msm_pcie_config_clear_set_dword(pdev, l1ss_ctl1_offset, 0,
+			(l1_1_cap_support ? PCI_L1SS_CTL1_ASPM_L1_1 : 0) |
+			(l1_2_cap_support ? PCI_L1SS_CTL1_ASPM_L1_2 : 0));
+	} else {
+		msm_pcie_config_clear_set_dword(pdev, devctl2_offset,
+			PCI_EXP_DEVCTL2_LTR_EN, 0);
+		msm_pcie_config_clear_set_dword(pdev, l1ss_ctl1_offset,
+			(l1_1_cap_support ? PCI_L1SS_CTL1_ASPM_L1_1 : 0) |
+			(l1_2_cap_support ? PCI_L1SS_CTL1_ASPM_L1_2 : 0), 0);
+	}
+
+	pci_read_config_dword(pdev, l1ss_ctl1_offset, &val);
+	PCIE_DBG2(dev, "PCIe: RC%d: L1SUB_CONTROL1:0x%x\n", dev->rc_idx, val);
+
+	pci_read_config_dword(pdev, devctl2_offset, &val2);
+	PCIE_DBG2(dev, "PCIe: RC%d: DEVICE_CONTROL2_STATUS2::0x%x\n",
+		dev->rc_idx, val2);
+}
+
+static void msm_pcie_config_clock_power_management(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev)
+{
+	u32 val;
+	u32 lnkcap_offset = pdev->pcie_cap + PCI_EXP_LNKCAP;
+	u32 lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL;
+
+	if (pci_is_root_bus(pdev->bus))
+		return;
+
+	pci_read_config_dword(pdev, lnkcap_offset, &val);
+	if (val & PCI_EXP_LNKCAP_CLKPM)
+		msm_pcie_config_clear_set_dword(pdev, lnkctl_offset, 0,
+			PCI_EXP_LNKCTL_CLKREQ_EN);
+	else
+		PCIE_DBG(dev,
+			"PCIe: RC%d: PCI device does not support clock power management\n",
+			dev->rc_idx);
+}
+
+static void msm_pcie_config_link_pm(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable)
+{
+	if (dev->common_clk_en)
+		msm_pcie_config_clear_set_dword(pdev,
+			pdev->pcie_cap + PCI_EXP_LNKCTL, 0,
+			PCI_EXP_LNKCTL_CCC);
+
+	if (dev->clk_power_manage_en)
+		msm_pcie_config_clock_power_management(dev, pdev);
+	if (dev->l0s_supported)
+		msm_pcie_config_l0s(dev, pdev, enable);
+	if (dev->l1ss_supported)
+		msm_pcie_config_l1ss(dev, pdev, enable);
+	if (dev->l1_supported)
+		msm_pcie_config_l1(dev, pdev, enable);
+}
+
+static void msm_pcie_config_link_pm_rc(struct msm_pcie_dev_t *dev,
+				struct pci_dev *pdev, bool enable)
+{
+	bool child_l0s_enable = 0, child_l1_enable = 0, child_l1ss_enable = 0;
+
+	if (!pdev->subordinate || !(&pdev->subordinate->devices)) {
+		PCIE_DBG(dev,
+			"PCIe: RC%d: no device connected to root complex\n",
+			dev->rc_idx);
+		return;
+	}
+
+	if (dev->l0s_supported) {
+		struct pci_dev *child_pdev, *c_pdev;
+
+		list_for_each_entry_safe(child_pdev, c_pdev,
+			&pdev->subordinate->devices, bus_list) {
+			u32 val;
+
+			pci_read_config_dword(child_pdev,
+				pdev->pcie_cap + PCI_EXP_LNKCTL, &val);
+			child_l0s_enable = !!(val & PCI_EXP_LNKCTL_ASPM_L0S);
+			if (child_l0s_enable)
+				break;
+		}
+
+		if (child_l0s_enable)
+			msm_pcie_config_l0s(dev, pdev, enable);
+		else
+			dev->l0s_supported = false;
+	}
+
+	if (dev->l1ss_supported) {
+		struct pci_dev *child_pdev, *c_pdev;
+
+		list_for_each_entry_safe(child_pdev, c_pdev,
+			&pdev->subordinate->devices, bus_list) {
+			u32 val;
+			u32 l1ss_cap_id_offset =
+				pci_find_ext_capability(child_pdev,
+					PCI_EXT_CAP_ID_L1SS);
+
+			if (!l1ss_cap_id_offset)
+				continue;
+
+			pci_read_config_dword(child_pdev,
+				l1ss_cap_id_offset + PCI_L1SS_CTL1, &val);
+			child_l1ss_enable = !!(val &
+				(PCI_L1SS_CTL1_ASPM_L1_1 |
+				PCI_L1SS_CTL1_ASPM_L1_2));
+			if (child_l1ss_enable)
+				break;
+		}
+
+		if (child_l1ss_enable)
+			msm_pcie_config_l1ss(dev, pdev, enable);
+		else
+			dev->l1ss_supported = false;
+	}
+
+	if (dev->l1_supported) {
+		struct pci_dev *child_pdev, *c_pdev;
+
+		list_for_each_entry_safe(child_pdev, c_pdev,
+			&pdev->subordinate->devices, bus_list) {
+			u32 val;
+
+			pci_read_config_dword(child_pdev,
+				pdev->pcie_cap + PCI_EXP_LNKCTL, &val);
+			child_l1_enable = !!(val & PCI_EXP_LNKCTL_ASPM_L1);
+			if (child_l1_enable)
+				break;
+		}
+
+		if (child_l1_enable)
+			msm_pcie_config_l1(dev, pdev, enable);
+		else
+			dev->l1_supported = false;
+	}
+}
+
 static int msm_pcie_config_device(struct pci_dev *dev, void *pdev)
 {
 	struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *)pdev;
@@ -5210,6 +5633,9 @@
 
 	msm_pcie_configure_sid(pcie_dev, dev);
 
+	if (!pci_is_root_bus(dev->bus))
+		msm_pcie_config_link_pm(pcie_dev, dev, true);
+
 	return 0;
 }
 
@@ -5251,16 +5677,25 @@
 	msm_pcie_dev[rc_idx].l0s_supported =
 		of_property_read_bool((&pdev->dev)->of_node,
 				"qcom,l0s-supported");
+	if (msm_pcie_invert_l0s_support & BIT(rc_idx))
+		msm_pcie_dev[rc_idx].l0s_supported =
+			!msm_pcie_dev[rc_idx].l0s_supported;
 	PCIE_DBG(&msm_pcie_dev[rc_idx], "L0s is %s supported.\n",
 		msm_pcie_dev[rc_idx].l0s_supported ? "" : "not");
 	msm_pcie_dev[rc_idx].l1_supported =
 		of_property_read_bool((&pdev->dev)->of_node,
 				"qcom,l1-supported");
+	if (msm_pcie_invert_l1_support & BIT(rc_idx))
+		msm_pcie_dev[rc_idx].l1_supported =
+			!msm_pcie_dev[rc_idx].l1_supported;
 	PCIE_DBG(&msm_pcie_dev[rc_idx], "L1 is %s supported.\n",
 		msm_pcie_dev[rc_idx].l1_supported ? "" : "not");
 	msm_pcie_dev[rc_idx].l1ss_supported =
 		of_property_read_bool((&pdev->dev)->of_node,
 				"qcom,l1ss-supported");
+	if (msm_pcie_invert_l1ss_support & BIT(rc_idx))
+		msm_pcie_dev[rc_idx].l1ss_supported =
+			!msm_pcie_dev[rc_idx].l1ss_supported;
 	PCIE_DBG(&msm_pcie_dev[rc_idx], "L1ss is %s supported.\n",
 		msm_pcie_dev[rc_idx].l1ss_supported ? "" : "not");
 	msm_pcie_dev[rc_idx].common_clk_en =
@@ -5366,6 +5801,20 @@
 		PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n",
 			rc_idx, msm_pcie_dev[rc_idx].ep_latency);
 
+	msm_pcie_dev[rc_idx].switch_latency = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+					"qcom,switch-latency",
+					&msm_pcie_dev[rc_idx].switch_latency);
+
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+				"RC%d: switch-latency does not exist.\n",
+				rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+				"RC%d: switch-latency: 0x%x.\n",
+				rc_idx, msm_pcie_dev[rc_idx].switch_latency);
+
 	msm_pcie_dev[rc_idx].wr_halt_size = 0;
 	ret = of_property_read_u32(pdev->dev.of_node,
 				"qcom,wr-halt-size",
@@ -5500,6 +5949,8 @@
 	msm_pcie_dev[rc_idx].suspending = false;
 	msm_pcie_dev[rc_idx].wake_counter = 0;
 	msm_pcie_dev[rc_idx].aer_enable = true;
+	if (msm_pcie_invert_aer_support)
+		msm_pcie_dev[rc_idx].aer_enable = false;
 	msm_pcie_dev[rc_idx].power_on = false;
 	msm_pcie_dev[rc_idx].use_msi = false;
 	msm_pcie_dev[rc_idx].use_pinctrl = false;
diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c
index 45a89d9..90e0b6f 100644
--- a/drivers/pci/host/pci-mvebu.c
+++ b/drivers/pci/host/pci-mvebu.c
@@ -133,6 +133,12 @@
 	int nports;
 };
 
+struct mvebu_pcie_window {
+	phys_addr_t base;
+	phys_addr_t remap;
+	size_t size;
+};
+
 /* Structure representing one PCIe interface */
 struct mvebu_pcie_port {
 	char *name;
@@ -150,10 +156,8 @@
 	struct mvebu_sw_pci_bridge bridge;
 	struct device_node *dn;
 	struct mvebu_pcie *pcie;
-	phys_addr_t memwin_base;
-	size_t memwin_size;
-	phys_addr_t iowin_base;
-	size_t iowin_size;
+	struct mvebu_pcie_window memwin;
+	struct mvebu_pcie_window iowin;
 	u32 saved_pcie_stat;
 };
 
@@ -379,23 +383,45 @@
 	}
 }
 
+static void mvebu_pcie_set_window(struct mvebu_pcie_port *port,
+				  unsigned int target, unsigned int attribute,
+				  const struct mvebu_pcie_window *desired,
+				  struct mvebu_pcie_window *cur)
+{
+	if (desired->base == cur->base && desired->remap == cur->remap &&
+	    desired->size == cur->size)
+		return;
+
+	if (cur->size != 0) {
+		mvebu_pcie_del_windows(port, cur->base, cur->size);
+		cur->size = 0;
+		cur->base = 0;
+
+		/*
+		 * If something tries to change the window while it is enabled
+		 * the change will not be done atomically. That would be
+		 * difficult to do in the general case.
+		 */
+	}
+
+	if (desired->size == 0)
+		return;
+
+	mvebu_pcie_add_windows(port, target, attribute, desired->base,
+			       desired->size, desired->remap);
+	*cur = *desired;
+}
+
 static void mvebu_pcie_handle_iobase_change(struct mvebu_pcie_port *port)
 {
-	phys_addr_t iobase;
+	struct mvebu_pcie_window desired = {};
 
 	/* Are the new iobase/iolimit values invalid? */
 	if (port->bridge.iolimit < port->bridge.iobase ||
 	    port->bridge.iolimitupper < port->bridge.iobaseupper ||
 	    !(port->bridge.command & PCI_COMMAND_IO)) {
-
-		/* If a window was configured, remove it */
-		if (port->iowin_base) {
-			mvebu_pcie_del_windows(port, port->iowin_base,
-					       port->iowin_size);
-			port->iowin_base = 0;
-			port->iowin_size = 0;
-		}
-
+		mvebu_pcie_set_window(port, port->io_target, port->io_attr,
+				      &desired, &port->iowin);
 		return;
 	}
 
@@ -412,32 +438,27 @@
 	 * specifications. iobase is the bus address, port->iowin_base
 	 * is the CPU address.
 	 */
-	iobase = ((port->bridge.iobase & 0xF0) << 8) |
-		(port->bridge.iobaseupper << 16);
-	port->iowin_base = port->pcie->io.start + iobase;
-	port->iowin_size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
-			    (port->bridge.iolimitupper << 16)) -
-			    iobase) + 1;
+	desired.remap = ((port->bridge.iobase & 0xF0) << 8) |
+			(port->bridge.iobaseupper << 16);
+	desired.base = port->pcie->io.start + desired.remap;
+	desired.size = ((0xFFF | ((port->bridge.iolimit & 0xF0) << 8) |
+			 (port->bridge.iolimitupper << 16)) -
+			desired.remap) +
+		       1;
 
-	mvebu_pcie_add_windows(port, port->io_target, port->io_attr,
-			       port->iowin_base, port->iowin_size,
-			       iobase);
+	mvebu_pcie_set_window(port, port->io_target, port->io_attr, &desired,
+			      &port->iowin);
 }
 
 static void mvebu_pcie_handle_membase_change(struct mvebu_pcie_port *port)
 {
+	struct mvebu_pcie_window desired = {.remap = MVEBU_MBUS_NO_REMAP};
+
 	/* Are the new membase/memlimit values invalid? */
 	if (port->bridge.memlimit < port->bridge.membase ||
 	    !(port->bridge.command & PCI_COMMAND_MEMORY)) {
-
-		/* If a window was configured, remove it */
-		if (port->memwin_base) {
-			mvebu_pcie_del_windows(port, port->memwin_base,
-					       port->memwin_size);
-			port->memwin_base = 0;
-			port->memwin_size = 0;
-		}
-
+		mvebu_pcie_set_window(port, port->mem_target, port->mem_attr,
+				      &desired, &port->memwin);
 		return;
 	}
 
@@ -447,14 +468,12 @@
 	 * window to setup, according to the PCI-to-PCI bridge
 	 * specifications.
 	 */
-	port->memwin_base  = ((port->bridge.membase & 0xFFF0) << 16);
-	port->memwin_size  =
-		(((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
-		port->memwin_base + 1;
+	desired.base = ((port->bridge.membase & 0xFFF0) << 16);
+	desired.size = (((port->bridge.memlimit & 0xFFF0) << 16) | 0xFFFFF) -
+		       desired.base + 1;
 
-	mvebu_pcie_add_windows(port, port->mem_target, port->mem_attr,
-			       port->memwin_base, port->memwin_size,
-			       MVEBU_MBUS_NO_REMAP);
+	mvebu_pcie_set_window(port, port->mem_target, port->mem_attr, &desired,
+			      &port->memwin);
 }
 
 /*
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index b57fc6d..d08dfc8 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -586,6 +586,14 @@
 	events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
 			   PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
 			   PCI_EXP_SLTSTA_DLLSC);
+
+	/*
+	 * If we've already reported a power fault, don't report it again
+	 * until we've done something to handle it.
+	 */
+	if (ctrl->power_fault_detected)
+		events &= ~PCI_EXP_SLTSTA_PFD;
+
 	if (!events)
 		return IRQ_NONE;
 
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index de0ea47..e5824c7 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -1062,6 +1062,8 @@
 		if (rc) {
 			ctrl_info(ctrl, "Can't get msi for the hotplug controller\n");
 			ctrl_info(ctrl, "Use INTx for the hotplug controller\n");
+		} else {
+			pci_set_master(pdev);
 		}
 
 		rc = request_irq(ctrl->pci_dev->irq, shpc_isr, IRQF_SHARED,
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 3455f75..0e9a9db 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -730,7 +730,7 @@
 	ret = 0;
 out:
 	kfree(masks);
-	return 0;
+	return ret;
 }
 
 static void msix_program_entries(struct pci_dev *dev,
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 1b07865..f9f4d1c 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -527,7 +527,7 @@
 				     const char *buf, size_t count)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
-	char *driver_override, *old = pdev->driver_override, *cp;
+	char *driver_override, *old, *cp;
 
 	/* We need to keep extra room for a newline */
 	if (count >= (PAGE_SIZE - 1))
@@ -541,12 +541,15 @@
 	if (cp)
 		*cp = '\0';
 
+	device_lock(dev);
+	old = pdev->driver_override;
 	if (strlen(driver_override)) {
 		pdev->driver_override = driver_override;
 	} else {
 		kfree(driver_override);
 		pdev->driver_override = NULL;
 	}
+	device_unlock(dev);
 
 	kfree(old);
 
@@ -557,8 +560,12 @@
 				    struct device_attribute *attr, char *buf)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
+	ssize_t len;
 
-	return snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override);
+	device_lock(dev);
+	len = snprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override);
+	device_unlock(dev);
+	return len;
 }
 static DEVICE_ATTR_RW(driver_override);
 
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0e75d94..671610c 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -82,6 +82,7 @@
 	tristate "AMD GPIO pin control"
 	depends on GPIOLIB
 	select GPIOLIB_IRQCHIP
+	select PINMUX
 	select PINCONF
 	select GENERIC_PINCONF
 	help
diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c
index 5419de8..0a96502 100644
--- a/drivers/pinctrl/intel/pinctrl-baytrail.c
+++ b/drivers/pinctrl/intel/pinctrl-baytrail.c
@@ -1466,7 +1466,7 @@
 			   val & BYT_INPUT_EN ? "  " : "in",
 			   val & BYT_OUTPUT_EN ? "   " : "out",
 			   val & BYT_LEVEL ? "hi" : "lo",
-			   comm->pad_map[i], comm->pad_map[i] * 32,
+			   comm->pad_map[i], comm->pad_map[i] * 16,
 			   conf0 & 0x7,
 			   conf0 & BYT_TRIG_NEG ? " fall" : "     ",
 			   conf0 & BYT_TRIG_POS ? " rise" : "     ",
diff --git a/drivers/pinctrl/intel/pinctrl-merrifield.c b/drivers/pinctrl/intel/pinctrl-merrifield.c
index 9931be6..04d6fd2 100644
--- a/drivers/pinctrl/intel/pinctrl-merrifield.c
+++ b/drivers/pinctrl/intel/pinctrl-merrifield.c
@@ -343,9 +343,9 @@
 
 static const unsigned int mrfld_sdio_pins[] = { 50, 51, 52, 53, 54, 55, 56 };
 static const unsigned int mrfld_spi5_pins[] = { 90, 91, 92, 93, 94, 95, 96 };
-static const unsigned int mrfld_uart0_pins[] = { 124, 125, 126, 127 };
-static const unsigned int mrfld_uart1_pins[] = { 128, 129, 130, 131 };
-static const unsigned int mrfld_uart2_pins[] = { 132, 133, 134, 135 };
+static const unsigned int mrfld_uart0_pins[] = { 115, 116, 117, 118 };
+static const unsigned int mrfld_uart1_pins[] = { 119, 120, 121, 122 };
+static const unsigned int mrfld_uart2_pins[] = { 123, 124, 125, 126 };
 static const unsigned int mrfld_pwm0_pins[] = { 144 };
 static const unsigned int mrfld_pwm1_pins[] = { 145 };
 static const unsigned int mrfld_pwm2_pins[] = { 132 };
diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
index c3928aa..7511723 100644
--- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
+++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c
@@ -85,6 +85,7 @@
 	MESON_PIN(GPIODV_15, EE_OFF),
 	MESON_PIN(GPIODV_16, EE_OFF),
 	MESON_PIN(GPIODV_17, EE_OFF),
+	MESON_PIN(GPIODV_18, EE_OFF),
 	MESON_PIN(GPIODV_19, EE_OFF),
 	MESON_PIN(GPIODV_20, EE_OFF),
 	MESON_PIN(GPIODV_21, EE_OFF),
diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c
index c9a1469..a5b7bd3 100644
--- a/drivers/pinctrl/pinctrl-amd.c
+++ b/drivers/pinctrl/pinctrl-amd.c
@@ -32,6 +32,7 @@
 #include <linux/pinctrl/pinconf.h>
 #include <linux/pinctrl/pinconf-generic.h>
 
+#include "core.h"
 #include "pinctrl-utils.h"
 #include "pinctrl-amd.h"
 
@@ -712,6 +713,69 @@
 	.pin_config_group_set = amd_pinconf_group_set,
 };
 
+#ifdef CONFIG_PM_SLEEP
+static bool amd_gpio_should_save(struct amd_gpio *gpio_dev, unsigned int pin)
+{
+	const struct pin_desc *pd = pin_desc_get(gpio_dev->pctrl, pin);
+
+	if (!pd)
+		return false;
+
+	/*
+	 * Only restore the pin if it is actually in use by the kernel (or
+	 * by userspace).
+	 */
+	if (pd->mux_owner || pd->gpio_owner ||
+	    gpiochip_line_is_irq(&gpio_dev->gc, pin))
+		return true;
+
+	return false;
+}
+
+int amd_gpio_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
+	struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
+	int i;
+
+	for (i = 0; i < desc->npins; i++) {
+		int pin = desc->pins[i].number;
+
+		if (!amd_gpio_should_save(gpio_dev, pin))
+			continue;
+
+		gpio_dev->saved_regs[i] = readl(gpio_dev->base + pin*4);
+	}
+
+	return 0;
+}
+
+int amd_gpio_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct amd_gpio *gpio_dev = platform_get_drvdata(pdev);
+	struct pinctrl_desc *desc = gpio_dev->pctrl->desc;
+	int i;
+
+	for (i = 0; i < desc->npins; i++) {
+		int pin = desc->pins[i].number;
+
+		if (!amd_gpio_should_save(gpio_dev, pin))
+			continue;
+
+		writel(gpio_dev->saved_regs[i], gpio_dev->base + pin*4);
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops amd_gpio_pm_ops = {
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(amd_gpio_suspend,
+				     amd_gpio_resume)
+};
+#endif
+
 static struct pinctrl_desc amd_pinctrl_desc = {
 	.pins	= kerncz_pins,
 	.npins = ARRAY_SIZE(kerncz_pins),
@@ -751,6 +815,14 @@
 		return -EINVAL;
 	}
 
+#ifdef CONFIG_PM_SLEEP
+	gpio_dev->saved_regs = devm_kcalloc(&pdev->dev, amd_pinctrl_desc.npins,
+					    sizeof(*gpio_dev->saved_regs),
+					    GFP_KERNEL);
+	if (!gpio_dev->saved_regs)
+		return -ENOMEM;
+#endif
+
 	gpio_dev->pdev = pdev;
 	gpio_dev->gc.direction_input	= amd_gpio_direction_input;
 	gpio_dev->gc.direction_output	= amd_gpio_direction_output;
@@ -839,6 +911,9 @@
 	.driver		= {
 		.name	= "amd_gpio",
 		.acpi_match_table = ACPI_PTR(amd_gpio_acpi_match),
+#ifdef CONFIG_PM_SLEEP
+		.pm	= &amd_gpio_pm_ops,
+#endif
 	},
 	.probe		= amd_gpio_probe,
 	.remove		= amd_gpio_remove,
diff --git a/drivers/pinctrl/pinctrl-amd.h b/drivers/pinctrl/pinctrl-amd.h
index 7bfea47..e8bbb20 100644
--- a/drivers/pinctrl/pinctrl-amd.h
+++ b/drivers/pinctrl/pinctrl-amd.h
@@ -95,6 +95,7 @@
 	struct gpio_chip        gc;
 	struct resource         *res;
 	struct platform_device  *pdev;
+	u32			*saved_regs;
 };
 
 /*  KERNCZ configuration*/
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 8da8571..d39a17f 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -79,6 +79,16 @@
 	  This is the pinctrl, pinmux, pinconf and gpiolib driver for the
 	  Qualcomm TLMM block found on the Qualcomm 8916 platform.
 
+config PINCTRL_MSM8953
+	tristate "Qualcomm Technologies Inc MSM8953 pin controller driver"
+	depends on GPIOLIB && OF
+	select PINCTRL_MSM
+	help
+	  This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+	  Qualcomm Technologies Inc TLMM block found on the Qualcomm
+	  Technologies Inc MSM8953 platform.
+	  If unsure say N.
+
 config PINCTRL_SDM845
 	tristate "Qualcomm Technologies Inc SDM845 pin controller driver"
 	depends on GPIOLIB && OF
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 392ed2a..d92db11 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -11,10 +11,11 @@
 obj-$(CONFIG_PINCTRL_MSM8996)   += pinctrl-msm8996.o
 obj-$(CONFIG_PINCTRL_QDF2XXX)	+= pinctrl-qdf2xxx.o
 obj-$(CONFIG_PINCTRL_MDM9615)	+= pinctrl-mdm9615.o
+obj-$(CONFIG_PINCTRL_MSM8953)   += pinctrl-msm8953.o
 obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-gpio.o
 obj-$(CONFIG_PINCTRL_QCOM_SPMI_PMIC) += pinctrl-spmi-mpp.o
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
-obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o
+obj-$(CONFIG_PINCTRL_SDM845) += pinctrl-sdm845.o pinctrl-sdm845-v2.o
 obj-$(CONFIG_PINCTRL_SDM670) += pinctrl-sdm670.o
 obj-$(CONFIG_PINCTRL_SDXPOORWILLS)	+= pinctrl-sdxpoorwills.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 166935ec..c8f8813 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -15,6 +15,7 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -39,7 +40,6 @@
 
 #define MAX_NR_GPIO 300
 #define PS_HOLD_OFFSET 0x820
-#define STATUS_OFFSET 0x10
 
 /**
  * struct msm_pinctrl - state for a pinctrl-msm device
@@ -72,35 +72,6 @@
 	void __iomem *regs;
 };
 
-static u32 msm_pinctrl_find_base(const struct msm_pinctrl *pctrl, u32 gpio_id)
-{
-	int i;
-	u32 val;
-	const struct msm_pinctrl_soc_data *soc_data = pctrl->soc;
-
-	if (gpio_id >= soc_data->ngpios || !soc_data->pin_base)
-		return 0;
-
-	if (soc_data->pin_base[gpio_id])
-		return soc_data->pin_base[gpio_id];
-
-	for (i = 0; i < soc_data->n_tile_offsets; i++) {
-		val = readl_relaxed(pctrl->regs +
-			soc_data->tile_offsets[i] + STATUS_OFFSET
-			+ soc_data->reg_size * gpio_id);
-		if (val) {
-			soc_data->pin_base[gpio_id] = soc_data->tile_offsets[i];
-			return soc_data->tile_offsets[i];
-		}
-	}
-
-	/* In the case that the soc_data does not support dynamic base
-	 * detection, we return 0 here.
-	 */
-	WARN_ONCE(1, "%s:Dynamic base detection is not supported\n", __func__);
-	return 0;
-}
-
 static int msm_get_groups_count(struct pinctrl_dev *pctldev)
 {
 	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
@@ -170,11 +141,10 @@
 	struct msm_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, mask, base;
+	u32 val, mask;
 	int i;
 
 	g = &pctrl->soc->groups[group];
-	base = msm_pinctrl_find_base(pctrl, group);
 	mask = GENMASK(g->mux_bit + order_base_2(g->nfuncs) - 1, g->mux_bit);
 
 	for (i = 0; i < g->nfuncs; i++) {
@@ -187,10 +157,10 @@
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val &= ~mask;
 	val |= i << g->mux_bit;
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -255,16 +225,15 @@
 	unsigned arg;
 	unsigned bit;
 	int ret;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[group];
-	base = msm_pinctrl_find_base(pctrl, group);
 
 	ret = msm_config_reg(pctrl, g, param, &mask, &bit);
 	if (ret < 0)
 		return ret;
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	arg = (val >> bit) & mask;
 
 	/* Convert register value to pinconf value */
@@ -289,7 +258,7 @@
 		if (!arg)
 			return -EINVAL;
 
-		val = readl(pctrl->regs + base + g->io_reg);
+		val = readl(pctrl->regs + g->io_reg);
 		arg = !!(val & BIT(g->in_bit));
 		break;
 	case PIN_CONFIG_INPUT_ENABLE:
@@ -320,12 +289,11 @@
 	unsigned arg;
 	unsigned bit;
 	int ret;
-	u32 val, base;
+	u32 val;
 	int i;
 
 	g = &pctrl->soc->groups[group];
 
-	base = msm_pinctrl_find_base(pctrl, group);
 	for (i = 0; i < num_configs; i++) {
 		param = pinconf_to_config_param(configs[i]);
 		arg = pinconf_to_config_argument(configs[i]);
@@ -358,12 +326,12 @@
 		case PIN_CONFIG_OUTPUT:
 			/* set output value */
 			spin_lock_irqsave(&pctrl->lock, flags);
-			val = readl(pctrl->regs + base + g->io_reg);
+			val = readl(pctrl->regs + g->io_reg);
 			if (arg)
 				val |= BIT(g->out_bit);
 			else
 				val &= ~BIT(g->out_bit);
-			writel(val, pctrl->regs + base + g->io_reg);
+			writel(val, pctrl->regs + g->io_reg);
 			spin_unlock_irqrestore(&pctrl->lock, flags);
 
 			/* enable output */
@@ -386,10 +354,10 @@
 		}
 
 		spin_lock_irqsave(&pctrl->lock, flags);
-		val = readl(pctrl->regs + base + g->ctl_reg);
+		val = readl(pctrl->regs + g->ctl_reg);
 		val &= ~(mask << bit);
 		val |= arg << bit;
-		writel(val, pctrl->regs + base + g->ctl_reg);
+		writel(val, pctrl->regs + g->ctl_reg);
 		spin_unlock_irqrestore(&pctrl->lock, flags);
 	}
 
@@ -414,16 +382,15 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val &= ~BIT(g->oe_bit);
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -435,23 +402,22 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	if (value)
 		val |= BIT(g->out_bit);
 	else
 		val &= ~BIT(g->out_bit);
-	writel(val, pctrl->regs + base + g->io_reg);
+	writel(val, pctrl->regs + g->io_reg);
 
-	val = readl(pctrl->regs + base + g->ctl_reg);
+	val = readl(pctrl->regs + g->ctl_reg);
 	val |= BIT(g->oe_bit);
-	writel(val, pctrl->regs + base + g->ctl_reg);
+	writel(val, pctrl->regs + g->ctl_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -462,12 +428,11 @@
 {
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	return !!(val & BIT(g->in_bit));
 }
 
@@ -476,19 +441,18 @@
 	const struct msm_pingroup *g;
 	struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->io_reg);
+	val = readl(pctrl->regs + g->io_reg);
 	if (value)
 		val |= BIT(g->out_bit);
 	else
 		val &= ~BIT(g->out_bit);
-	writel(val, pctrl->regs + base + g->io_reg);
+	writel(val, pctrl->regs + g->io_reg);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 }
@@ -508,7 +472,7 @@
 	int is_out;
 	int drive;
 	int pull;
-	u32 ctl_reg, base;
+	u32 ctl_reg;
 
 	static const char * const pulls[] = {
 		"no pull",
@@ -518,9 +482,7 @@
 	};
 
 	g = &pctrl->soc->groups[offset];
-	base = msm_pinctrl_find_base(pctrl, offset);
-
-	ctl_reg = readl(pctrl->regs + base + g->ctl_reg);
+	ctl_reg = readl(pctrl->regs + g->ctl_reg);
 
 	is_out = !!(ctl_reg & BIT(g->oe_bit));
 	func = (ctl_reg >> g->mux_bit) & 7;
@@ -579,21 +541,21 @@
  */
 static void msm_gpio_update_dual_edge_pos(struct msm_pinctrl *pctrl,
 					  const struct msm_pingroup *g,
-					  struct irq_data *d, u32 base)
+					  struct irq_data *d)
 {
 	int loop_limit = 100;
 	unsigned val, val2, intstat;
 	unsigned pol;
 
 	do {
-		val = readl(pctrl->regs + base + g->io_reg) & BIT(g->in_bit);
+		val = readl(pctrl->regs + g->io_reg) & BIT(g->in_bit);
 
-		pol = readl(pctrl->regs + base + g->intr_cfg_reg);
+		pol = readl(pctrl->regs + g->intr_cfg_reg);
 		pol ^= BIT(g->intr_polarity_bit);
-		writel(pol, pctrl->regs + base + g->intr_cfg_reg);
+		writel(pol, pctrl->regs + g->intr_cfg_reg);
 
-		val2 = readl(pctrl->regs + base + g->io_reg) & BIT(g->in_bit);
-		intstat = readl(pctrl->regs + base + g->intr_status_reg);
+		val2 = readl(pctrl->regs + g->io_reg) & BIT(g->in_bit);
+		intstat = readl(pctrl->regs + g->intr_status_reg);
 		if (intstat || (val == val2))
 			return;
 	} while (loop_limit-- > 0);
@@ -607,16 +569,15 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val &= ~BIT(g->intr_enable_bit);
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	clear_bit(d->hwirq, pctrl->enabled_irqs);
 
@@ -629,16 +590,15 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val |= BIT(g->intr_enable_bit);
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	set_bit(d->hwirq, pctrl->enabled_irqs);
 
@@ -651,22 +611,21 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
 
 	spin_lock_irqsave(&pctrl->lock, flags);
 
-	val = readl(pctrl->regs + base + g->intr_status_reg);
+	val = readl(pctrl->regs + g->intr_status_reg);
 	if (g->intr_ack_high)
 		val |= BIT(g->intr_status_bit);
 	else
 		val &= ~BIT(g->intr_status_bit);
-	writel(val, pctrl->regs + base + g->intr_status_reg);
+	writel(val, pctrl->regs + g->intr_status_reg);
 
 	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(pctrl, g, d, base);
+		msm_gpio_update_dual_edge_pos(pctrl, g, d);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 }
@@ -677,10 +636,10 @@
 	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
 	const struct msm_pingroup *g;
 	unsigned long flags;
-	u32 val, base;
+	u32 val;
 
 	g = &pctrl->soc->groups[d->hwirq];
-	base = msm_pinctrl_find_base(pctrl, d->hwirq);
+
 	spin_lock_irqsave(&pctrl->lock, flags);
 
 	/*
@@ -692,17 +651,17 @@
 		clear_bit(d->hwirq, pctrl->dual_edge_irqs);
 
 	/* Route interrupts to application cpu */
-	val = readl(pctrl->regs + base + g->intr_target_reg);
+	val = readl(pctrl->regs + g->intr_target_reg);
 	val &= ~(7 << g->intr_target_bit);
 	val |= g->intr_target_kpss_val << g->intr_target_bit;
-	writel(val, pctrl->regs + base + g->intr_target_reg);
+	writel(val, pctrl->regs + g->intr_target_reg);
 
 	/* Update configuration for gpio.
 	 * RAW_STATUS_EN is left on for all gpio irqs. Due to the
 	 * internal circuitry of TLMM, toggling the RAW_STATUS
 	 * could cause the INTR_STATUS to be set for EDGE interrupts.
 	 */
-	val = readl(pctrl->regs + base + g->intr_cfg_reg);
+	val = readl(pctrl->regs + g->intr_cfg_reg);
 	val |= BIT(g->intr_raw_status_bit);
 	if (g->intr_detection_width == 2) {
 		val &= ~(3 << g->intr_detection_bit);
@@ -750,10 +709,10 @@
 	} else {
 		BUG();
 	}
-	writel(val, pctrl->regs + base + g->intr_cfg_reg);
+	writel(val, pctrl->regs + g->intr_cfg_reg);
 
 	if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
-		msm_gpio_update_dual_edge_pos(pctrl, g, d, base);
+		msm_gpio_update_dual_edge_pos(pctrl, g, d);
 
 	spin_unlock_irqrestore(&pctrl->lock, flags);
 
@@ -789,11 +748,46 @@
 	.irq_set_wake   = msm_gpio_irq_set_wake,
 };
 
+static bool is_gpio_dual_edge(struct irq_data *d, irq_hw_number_t *dir_conn_irq)
+{
+	struct irq_desc *desc = irq_data_to_desc(d);
+	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+	int i;
+
+	if (!parent_data)
+		return false;
+
+	for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+		const struct msm_dir_conn *dir_conn = &pctrl->soc->dir_conn[i];
+
+		if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32)
+				!= parent_data->hwirq) {
+			*dir_conn_irq = dir_conn->hwirq + 32;
+			return true;
+		}
+	}
+	return false;
+}
+
 static void msm_dirconn_irq_mask(struct irq_data *d)
 {
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	irq_hw_number_t dir_conn_irq = 0;
 
+	if (!parent_data)
+		return;
+
+	if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+		struct irq_data *dir_conn_data =
+			irq_get_irq_data(irq_find_mapping(parent_data->domain,
+						dir_conn_irq));
+
+		if (dir_conn_data && dir_conn_data->chip->irq_mask)
+			dir_conn_data->chip->irq_mask(dir_conn_data);
+	}
 	if (parent_data->chip->irq_mask)
 		parent_data->chip->irq_mask(parent_data);
 }
@@ -802,7 +796,19 @@
 {
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	irq_hw_number_t dir_conn_irq = 0;
 
+	if (!parent_data)
+		return;
+
+	if (is_gpio_dual_edge(d, &dir_conn_irq)) {
+		struct irq_data *dir_conn_data =
+			irq_get_irq_data(irq_find_mapping(parent_data->domain,
+						dir_conn_irq));
+
+		if (dir_conn_data && dir_conn_data->chip->irq_unmask)
+			dir_conn_data->chip->irq_unmask(dir_conn_data);
+	}
 	if (parent_data->chip->irq_unmask)
 		parent_data->chip->irq_unmask(parent_data);
 }
@@ -812,6 +818,9 @@
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
 
+	if (!parent_data)
+		return;
+
 	if (parent_data->chip->irq_ack)
 		parent_data->chip->irq_ack(parent_data);
 }
@@ -821,6 +830,9 @@
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
 
+	if (!parent_data)
+		return;
+
 	if (parent_data->chip->irq_eoi)
 		parent_data->chip->irq_eoi(parent_data);
 }
@@ -831,6 +843,9 @@
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
 
+	if (!parent_data)
+		return 0;
+
 	if (parent_data->chip->irq_set_affinity)
 		return parent_data->chip->irq_set_affinity(parent_data,
 				maskval, force);
@@ -843,16 +858,167 @@
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
 
+	if (!parent_data)
+		return 0;
+
 	if (parent_data->chip->irq_set_vcpu_affinity)
 		return parent_data->chip->irq_set_vcpu_affinity(parent_data,
 				vcpu_info);
 	return 0;
 }
 
+static void msm_dirconn_cfg_reg(struct irq_data *d, u32 offset)
+{
+	u32 val = 0;
+	const struct msm_pingroup *g;
+	unsigned long flags;
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+
+	spin_lock_irqsave(&pctrl->lock, flags);
+	g = &pctrl->soc->groups[d->hwirq];
+
+	val = readl_relaxed(pctrl->regs + g->dir_conn_reg + (offset * 4));
+	val = (d->hwirq) & 0xFF;
+
+	writel_relaxed(val, pctrl->regs + g->dir_conn_reg + (offset * 4));
+
+	//write the dir_conn_en bit
+	val = readl_relaxed(pctrl->regs + g->intr_cfg_reg);
+	val |= BIT(g->dir_conn_en_bit);
+	writel_relaxed(val, pctrl->regs + g->intr_cfg_reg);
+	spin_unlock_irqrestore(&pctrl->lock, flags);
+}
+
+static void msm_dirconn_uncfg_reg(struct irq_data *d, u32 offset)
+{
+	const struct msm_pingroup *g;
+	unsigned long flags;
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+
+	spin_lock_irqsave(&pctrl->lock, flags);
+	g = &pctrl->soc->groups[d->hwirq];
+
+	writel_relaxed(BIT(8), pctrl->regs + g->dir_conn_reg + (offset * 4));
+	spin_unlock_irqrestore(&pctrl->lock, flags);
+}
+
+static int select_dir_conn_mux(struct irq_data *d, irq_hw_number_t *irq)
+{
+	struct msm_dir_conn *dc = NULL;
+	struct irq_desc *desc = irq_data_to_desc(d);
+	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
+	struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
+	int i;
+
+	if (!parent_data)
+		return -EINVAL;
+
+	for (i = 0; i < pctrl->soc->n_dir_conns; i++) {
+		struct msm_dir_conn *dir_conn =
+			(struct msm_dir_conn *)&pctrl->soc->dir_conn[i];
+
+		/* Check if there is already mux assigned for this gpio */
+		if (dir_conn->gpio == d->hwirq && (dir_conn->hwirq + 32) !=
+				parent_data->hwirq) {
+			*irq = dir_conn->hwirq + 32;
+			return pctrl->soc->dir_conn_irq_base - dir_conn->hwirq;
+		}
+
+		if (dir_conn->gpio)
+			continue;
+
+		/* Use the first unused direct connect available */
+		dc = dir_conn;
+		break;
+	}
+
+	if (dc) {
+		*irq = dc->hwirq + 32;
+		dc->gpio = (u32)d->hwirq;
+		return pctrl->soc->dir_conn_irq_base - (u32)dc->hwirq;
+	}
+
+	pr_err("%s: No direct connects available for interrupt %lu\n",
+				__func__, d->hwirq);
+	return -EINVAL;
+}
+
+static void add_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq)
+{
+	struct irq_desc *desc = irq_data_to_desc(d);
+	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	struct irq_data *dir_conn_data = NULL;
+	int offset = 0;
+	unsigned int virt = 0;
+
+	offset = select_dir_conn_mux(d, &irq);
+	if (offset < 0 || !parent_data)
+		return;
+
+	virt = irq_find_mapping(parent_data->domain, irq);
+	msm_dirconn_cfg_reg(d, offset);
+	irq_set_handler_data(virt, d);
+	desc = irq_to_desc(virt);
+	if (!desc)
+		return;
+
+	dir_conn_data = &(desc->irq_data);
+
+	if (dir_conn_data) {
+		if (dir_conn_data->chip && dir_conn_data->chip->irq_set_type)
+			dir_conn_data->chip->irq_set_type(dir_conn_data,
+					IRQ_TYPE_EDGE_RISING);
+		if (dir_conn_data->chip && dir_conn_data->chip->irq_unmask)
+			dir_conn_data->chip->irq_unmask(dir_conn_data);
+	}
+}
+
+static void remove_dirconn_tlmm(struct irq_data *d, irq_hw_number_t irq)
+{
+	struct irq_desc *desc = irq_data_to_desc(d);
+	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	struct irq_data *dir_conn_data = NULL;
+	int offset = 0;
+	unsigned int virt = 0;
+
+	virt = irq_find_mapping(parent_data->domain, irq);
+	msm_dirconn_uncfg_reg(d, offset);
+	irq_set_handler_data(virt, NULL);
+	desc = irq_to_desc(virt);
+	if (!desc)
+		return;
+
+	dir_conn_data = &(desc->irq_data);
+
+	if (dir_conn_data) {
+		if (dir_conn_data->chip && dir_conn_data->chip->irq_mask)
+			dir_conn_data->chip->irq_mask(dir_conn_data);
+	}
+}
+
 static int msm_dirconn_irq_set_type(struct irq_data *d, unsigned int type)
 {
 	struct irq_desc *desc = irq_data_to_desc(d);
 	struct irq_data *parent_data = irq_get_irq_data(desc->parent_irq);
+	irq_hw_number_t irq = 0;
+
+	if (!parent_data)
+		return 0;
+
+	if (type == IRQ_TYPE_EDGE_BOTH) {
+		add_dirconn_tlmm(d, irq);
+	} else {
+		if (is_gpio_dual_edge(d, &irq))
+			remove_dirconn_tlmm(d, irq);
+	}
+
+	if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
+		irq_set_handler_locked(d, handle_level_irq);
+	else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING))
+		irq_set_handler_locked(d, handle_edge_irq);
 
 	if (parent_data->chip->irq_set_type)
 		return parent_data->chip->irq_set_type(parent_data, type);
@@ -882,7 +1048,7 @@
 	struct irq_chip *chip = irq_desc_get_chip(desc);
 	int irq_pin;
 	int handled = 0;
-	u32 val, base;
+	u32 val;
 	int i;
 
 	chained_irq_enter(chip, desc);
@@ -893,8 +1059,7 @@
 	 */
 	for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) {
 		g = &pctrl->soc->groups[i];
-		base = msm_pinctrl_find_base(pctrl, i);
-		val = readl(pctrl->regs + base + g->intr_status_reg);
+		val = readl(pctrl->regs + g->intr_status_reg);
 		if (val & BIT(g->intr_status_bit)) {
 			irq_pin = irq_find_mapping(gc->irqdomain, i);
 			generic_handle_irq(irq_pin);
@@ -947,14 +1112,19 @@
 		fwspec.param_count = 3;
 		parent_irq = irq_create_fwspec_mapping(&fwspec);
 
-		irq = irq_find_mapping(pctrl->chip.irqdomain, dirconn->gpio);
+		if (dirconn->gpio != 0) {
+			irq = irq_find_mapping(pctrl->chip.irqdomain,
+					dirconn->gpio);
 
-		irq_set_parent(irq, parent_irq);
-		irq_set_chip(irq, &msm_dirconn_irq_chip);
-		irq_set_chip_data(irq, irq_get_irq_data(parent_irq));
-		__irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
+			irq_set_parent(irq, parent_irq);
+			irq_set_chip(irq, &msm_dirconn_irq_chip);
+			__irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
 				false, NULL);
-		irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+			irq_set_handler_data(parent_irq, irq_get_irq_data(irq));
+		} else {
+			__irq_set_handler(parent_irq, msm_gpio_dirconn_handler,
+				false, NULL);
+		}
 	}
 }
 
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 7ffc2e3..1c6df2f 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -43,6 +43,8 @@
  * @intr_status_reg:      Offset of the register holding the status bits for this group.
  * @intr_target_reg:      Offset of the register specifying routing of the interrupts
  *                        from this group.
+ * @dir_conn_reg:         Offset of the register specifying direct connect
+ *                        setup of this group.
  * @mux_bit:              Offset in @ctl_reg for the pinmux function selection.
  * @pull_bit:             Offset in @ctl_reg for the bias configuration.
  * @drv_bit:              Offset in @ctl_reg for the drive strength configuration.
@@ -75,6 +77,7 @@
 	u32 intr_cfg_reg;
 	u32 intr_status_reg;
 	u32 intr_target_reg;
+	u32 dir_conn_reg;
 
 	unsigned mux_bit:5;
 
@@ -95,6 +98,7 @@
 	unsigned intr_polarity_bit:5;
 	unsigned intr_detection_bit:5;
 	unsigned intr_detection_width:5;
+	unsigned dir_conn_en_bit:8;
 }
 
 /**
@@ -118,6 +122,8 @@
  * @ngpio:      The number of pingroups the driver should expose as GPIOs.
  * @dir_conn:   An array describing all the pins directly connected to GIC.
  * @ndirconns:  The number of pins directly connected to GIC
+ * @dir_conn_offsets:   Direct connect register offsets for each tile.
+ * @dir_conn_irq_base:  Direct connect interrupt base register for kpss.
  */
 struct msm_pinctrl_soc_data {
 	const struct pinctrl_pin_desc *pins;
@@ -129,10 +135,7 @@
 	unsigned ngpios;
 	const struct msm_dir_conn *dir_conn;
 	unsigned int n_dir_conns;
-	const u32 *tile_offsets;
-	unsigned int n_tile_offsets;
-	u32 *pin_base;
-	unsigned int reg_size;
+	unsigned int dir_conn_irq_base;
 };
 
 int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-msm8953.c b/drivers/pinctrl/qcom/pinctrl-msm8953.c
new file mode 100644
index 0000000..ba1cad0
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-msm8953.c
@@ -0,0 +1,1686 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname)			                \
+	[msm_mux_##fname] = {		                \
+		.name = #fname,				\
+		.groups = fname##_groups,               \
+		.ngroups = ARRAY_SIZE(fname##_groups),	\
+	}
+
+#define REG_SIZE 0x1000
+#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+	{					        \
+		.name = "gpio" #id,			\
+		.pins = gpio##id##_pins,		\
+		.npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins),	\
+		.funcs = (int[]){			\
+			msm_mux_gpio, /* gpio mode */	\
+			msm_mux_##f1,			\
+			msm_mux_##f2,			\
+			msm_mux_##f3,			\
+			msm_mux_##f4,			\
+			msm_mux_##f5,			\
+			msm_mux_##f6,			\
+			msm_mux_##f7,			\
+			msm_mux_##f8,			\
+			msm_mux_##f9			\
+		},				        \
+		.nfuncs = 10,				\
+		.ctl_reg = REG_SIZE * id,			\
+		.io_reg = 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = 0x8 + REG_SIZE * id,		\
+		.intr_status_reg = 0xc + REG_SIZE * id,	\
+		.intr_target_reg = 0x8 + REG_SIZE * id,	\
+		.mux_bit = 2,			\
+		.pull_bit = 0,			\
+		.drv_bit = 6,			\
+		.oe_bit = 9,			\
+		.in_bit = 0,			\
+		.out_bit = 1,			\
+		.intr_enable_bit = 0,		\
+		.intr_status_bit = 0,		\
+		.intr_target_bit = 5,		\
+		.intr_target_kpss_val = 4,	\
+		.intr_raw_status_bit = 4,	\
+		.intr_polarity_bit = 1,		\
+		.intr_detection_bit = 2,	\
+		.intr_detection_width = 2,	\
+	}
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
+	{					        \
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = ctl,				\
+		.io_reg = 0,				\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = pull,			\
+		.drv_bit = drv,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = -1,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+static const struct pinctrl_pin_desc msm8953_pins[] = {
+	PINCTRL_PIN(0, "GPIO_0"),
+	PINCTRL_PIN(1, "GPIO_1"),
+	PINCTRL_PIN(2, "GPIO_2"),
+	PINCTRL_PIN(3, "GPIO_3"),
+	PINCTRL_PIN(4, "GPIO_4"),
+	PINCTRL_PIN(5, "GPIO_5"),
+	PINCTRL_PIN(6, "GPIO_6"),
+	PINCTRL_PIN(7, "GPIO_7"),
+	PINCTRL_PIN(8, "GPIO_8"),
+	PINCTRL_PIN(9, "GPIO_9"),
+	PINCTRL_PIN(10, "GPIO_10"),
+	PINCTRL_PIN(11, "GPIO_11"),
+	PINCTRL_PIN(12, "GPIO_12"),
+	PINCTRL_PIN(13, "GPIO_13"),
+	PINCTRL_PIN(14, "GPIO_14"),
+	PINCTRL_PIN(15, "GPIO_15"),
+	PINCTRL_PIN(16, "GPIO_16"),
+	PINCTRL_PIN(17, "GPIO_17"),
+	PINCTRL_PIN(18, "GPIO_18"),
+	PINCTRL_PIN(19, "GPIO_19"),
+	PINCTRL_PIN(20, "GPIO_20"),
+	PINCTRL_PIN(21, "GPIO_21"),
+	PINCTRL_PIN(22, "GPIO_22"),
+	PINCTRL_PIN(23, "GPIO_23"),
+	PINCTRL_PIN(24, "GPIO_24"),
+	PINCTRL_PIN(25, "GPIO_25"),
+	PINCTRL_PIN(26, "GPIO_26"),
+	PINCTRL_PIN(27, "GPIO_27"),
+	PINCTRL_PIN(28, "GPIO_28"),
+	PINCTRL_PIN(29, "GPIO_29"),
+	PINCTRL_PIN(30, "GPIO_30"),
+	PINCTRL_PIN(31, "GPIO_31"),
+	PINCTRL_PIN(32, "GPIO_32"),
+	PINCTRL_PIN(33, "GPIO_33"),
+	PINCTRL_PIN(34, "GPIO_34"),
+	PINCTRL_PIN(35, "GPIO_35"),
+	PINCTRL_PIN(36, "GPIO_36"),
+	PINCTRL_PIN(37, "GPIO_37"),
+	PINCTRL_PIN(38, "GPIO_38"),
+	PINCTRL_PIN(39, "GPIO_39"),
+	PINCTRL_PIN(40, "GPIO_40"),
+	PINCTRL_PIN(41, "GPIO_41"),
+	PINCTRL_PIN(42, "GPIO_42"),
+	PINCTRL_PIN(43, "GPIO_43"),
+	PINCTRL_PIN(44, "GPIO_44"),
+	PINCTRL_PIN(45, "GPIO_45"),
+	PINCTRL_PIN(46, "GPIO_46"),
+	PINCTRL_PIN(47, "GPIO_47"),
+	PINCTRL_PIN(48, "GPIO_48"),
+	PINCTRL_PIN(49, "GPIO_49"),
+	PINCTRL_PIN(50, "GPIO_50"),
+	PINCTRL_PIN(51, "GPIO_51"),
+	PINCTRL_PIN(52, "GPIO_52"),
+	PINCTRL_PIN(53, "GPIO_53"),
+	PINCTRL_PIN(54, "GPIO_54"),
+	PINCTRL_PIN(55, "GPIO_55"),
+	PINCTRL_PIN(56, "GPIO_56"),
+	PINCTRL_PIN(57, "GPIO_57"),
+	PINCTRL_PIN(58, "GPIO_58"),
+	PINCTRL_PIN(59, "GPIO_59"),
+	PINCTRL_PIN(60, "GPIO_60"),
+	PINCTRL_PIN(61, "GPIO_61"),
+	PINCTRL_PIN(62, "GPIO_62"),
+	PINCTRL_PIN(63, "GPIO_63"),
+	PINCTRL_PIN(64, "GPIO_64"),
+	PINCTRL_PIN(65, "GPIO_65"),
+	PINCTRL_PIN(66, "GPIO_66"),
+	PINCTRL_PIN(67, "GPIO_67"),
+	PINCTRL_PIN(68, "GPIO_68"),
+	PINCTRL_PIN(69, "GPIO_69"),
+	PINCTRL_PIN(70, "GPIO_70"),
+	PINCTRL_PIN(71, "GPIO_71"),
+	PINCTRL_PIN(72, "GPIO_72"),
+	PINCTRL_PIN(73, "GPIO_73"),
+	PINCTRL_PIN(74, "GPIO_74"),
+	PINCTRL_PIN(75, "GPIO_75"),
+	PINCTRL_PIN(76, "GPIO_76"),
+	PINCTRL_PIN(77, "GPIO_77"),
+	PINCTRL_PIN(78, "GPIO_78"),
+	PINCTRL_PIN(79, "GPIO_79"),
+	PINCTRL_PIN(80, "GPIO_80"),
+	PINCTRL_PIN(81, "GPIO_81"),
+	PINCTRL_PIN(82, "GPIO_82"),
+	PINCTRL_PIN(83, "GPIO_83"),
+	PINCTRL_PIN(84, "GPIO_84"),
+	PINCTRL_PIN(85, "GPIO_85"),
+	PINCTRL_PIN(86, "GPIO_86"),
+	PINCTRL_PIN(87, "GPIO_87"),
+	PINCTRL_PIN(88, "GPIO_88"),
+	PINCTRL_PIN(89, "GPIO_89"),
+	PINCTRL_PIN(90, "GPIO_90"),
+	PINCTRL_PIN(91, "GPIO_91"),
+	PINCTRL_PIN(92, "GPIO_92"),
+	PINCTRL_PIN(93, "GPIO_93"),
+	PINCTRL_PIN(94, "GPIO_94"),
+	PINCTRL_PIN(95, "GPIO_95"),
+	PINCTRL_PIN(96, "GPIO_96"),
+	PINCTRL_PIN(97, "GPIO_97"),
+	PINCTRL_PIN(98, "GPIO_98"),
+	PINCTRL_PIN(99, "GPIO_99"),
+	PINCTRL_PIN(100, "GPIO_100"),
+	PINCTRL_PIN(101, "GPIO_101"),
+	PINCTRL_PIN(102, "GPIO_102"),
+	PINCTRL_PIN(103, "GPIO_103"),
+	PINCTRL_PIN(104, "GPIO_104"),
+	PINCTRL_PIN(105, "GPIO_105"),
+	PINCTRL_PIN(106, "GPIO_106"),
+	PINCTRL_PIN(107, "GPIO_107"),
+	PINCTRL_PIN(108, "GPIO_108"),
+	PINCTRL_PIN(109, "GPIO_109"),
+	PINCTRL_PIN(110, "GPIO_110"),
+	PINCTRL_PIN(111, "GPIO_111"),
+	PINCTRL_PIN(112, "GPIO_112"),
+	PINCTRL_PIN(113, "GPIO_113"),
+	PINCTRL_PIN(114, "GPIO_114"),
+	PINCTRL_PIN(115, "GPIO_115"),
+	PINCTRL_PIN(116, "GPIO_116"),
+	PINCTRL_PIN(117, "GPIO_117"),
+	PINCTRL_PIN(118, "GPIO_118"),
+	PINCTRL_PIN(119, "GPIO_119"),
+	PINCTRL_PIN(120, "GPIO_120"),
+	PINCTRL_PIN(121, "GPIO_121"),
+	PINCTRL_PIN(122, "GPIO_122"),
+	PINCTRL_PIN(123, "GPIO_123"),
+	PINCTRL_PIN(124, "GPIO_124"),
+	PINCTRL_PIN(125, "GPIO_125"),
+	PINCTRL_PIN(126, "GPIO_126"),
+	PINCTRL_PIN(127, "GPIO_127"),
+	PINCTRL_PIN(128, "GPIO_128"),
+	PINCTRL_PIN(129, "GPIO_129"),
+	PINCTRL_PIN(130, "GPIO_130"),
+	PINCTRL_PIN(131, "GPIO_131"),
+	PINCTRL_PIN(132, "GPIO_132"),
+	PINCTRL_PIN(133, "GPIO_133"),
+	PINCTRL_PIN(134, "GPIO_134"),
+	PINCTRL_PIN(135, "GPIO_135"),
+	PINCTRL_PIN(136, "GPIO_136"),
+	PINCTRL_PIN(137, "GPIO_137"),
+	PINCTRL_PIN(138, "GPIO_138"),
+	PINCTRL_PIN(139, "GPIO_139"),
+	PINCTRL_PIN(140, "GPIO_140"),
+	PINCTRL_PIN(141, "GPIO_141"),
+	PINCTRL_PIN(142, "SDC1_CLK"),
+	PINCTRL_PIN(143, "SDC1_CMD"),
+	PINCTRL_PIN(144, "SDC1_DATA"),
+	PINCTRL_PIN(145, "SDC1_RCLK"),
+	PINCTRL_PIN(146, "SDC2_CLK"),
+	PINCTRL_PIN(147, "SDC2_CMD"),
+	PINCTRL_PIN(148, "SDC2_DATA"),
+	PINCTRL_PIN(149, "QDSD_CLK"),
+	PINCTRL_PIN(150, "QDSD_CMD"),
+	PINCTRL_PIN(151, "QDSD_DATA0"),
+	PINCTRL_PIN(152, "QDSD_DATA1"),
+	PINCTRL_PIN(153, "QDSD_DATA2"),
+	PINCTRL_PIN(154, "QDSD_DATA3"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+	static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+
+static const unsigned int sdc1_clk_pins[] = { 142 };
+static const unsigned int sdc1_cmd_pins[] = { 143 };
+static const unsigned int sdc1_data_pins[] = { 144 };
+static const unsigned int sdc1_rclk_pins[] = { 145 };
+static const unsigned int sdc2_clk_pins[] = { 146 };
+static const unsigned int sdc2_cmd_pins[] = { 147 };
+static const unsigned int sdc2_data_pins[] = { 148 };
+static const unsigned int qdsd_clk_pins[] = { 149 };
+static const unsigned int qdsd_cmd_pins[] = { 150 };
+static const unsigned int qdsd_data0_pins[] = { 151 };
+static const unsigned int qdsd_data1_pins[] = { 152 };
+static const unsigned int qdsd_data2_pins[] = { 153 };
+static const unsigned int qdsd_data3_pins[] = { 154 };
+
+enum msm8953_functions {
+	msm_mux_gpio,
+	msm_mux_blsp_spi1,
+	msm_mux_smb_int,
+	msm_mux_adsp_ext,
+	msm_mux_prng_rosc,
+	msm_mux_blsp_i2c1,
+	msm_mux_qdss_cti_trig_out_b0,
+	msm_mux_qdss_cti_trig_out_a1,
+	msm_mux_blsp_spi2,
+	msm_mux_blsp_uart2,
+	msm_mux_ldo_update,
+	msm_mux_dac_calib0,
+	msm_mux_ldo_en,
+	msm_mux_blsp_i2c2,
+	msm_mux_gcc_gp1_clk_b,
+	msm_mux_atest_gpsadc_dtest0_native,
+	msm_mux_blsp_spi3,
+	msm_mux_qdss_tracedata_b,
+	msm_mux_pwr_modem_enabled_b,
+	msm_mux_blsp_i2c3,
+	msm_mux_gcc_gp2_clk_b,
+	msm_mux_gcc_gp3_clk_b,
+	msm_mux_hall_int,
+	msm_mux_blsp_spi4,
+	msm_mux_blsp_uart4,
+	msm_mux_pwr_nav_enabled_b,
+	msm_mux_dac_calib1,
+	msm_mux_cap_int,
+	msm_mux_pwr_crypto_enabled_b,
+	msm_mux_dac_calib2,
+	msm_mux_blsp_i2c4,
+	msm_mux_nfc_disable,
+	msm_mux_blsp_spi5,
+	msm_mux_blsp_uart5,
+	msm_mux_qdss_traceclk_a,
+	msm_mux_atest_bbrx1,
+	msm_mux_nfc_irq,
+	msm_mux_m_voc,
+	msm_mux_qdss_cti_trig_in_a0,
+	msm_mux_atest_bbrx0,
+	msm_mux_blsp_i2c5,
+	msm_mux_qdss_tracectl_a,
+	msm_mux_atest_gpsadc_dtest1_native,
+	msm_mux_qdss_tracedata_a,
+	msm_mux_blsp_spi6,
+	msm_mux_blsp_uart6,
+	msm_mux_qdss_tracectl_b,
+	msm_mux_dac_calib15,
+	msm_mux_qdss_cti_trig_in_b0,
+	msm_mux_dac_calib16,
+	msm_mux_blsp_i2c6,
+	msm_mux_qdss_traceclk_b,
+	msm_mux_atest_wlan0,
+	msm_mux_atest_wlan1,
+	msm_mux_mdp_vsync,
+	msm_mux_pri_mi2s_mclk_a,
+	msm_mux_sec_mi2s_mclk_a,
+	msm_mux_qdss_cti_trig_out_b1,
+	msm_mux_cam_mclk,
+	msm_mux_dac_calib3,
+	msm_mux_cci_i2c,
+	msm_mux_pwr_modem_enabled_a,
+	msm_mux_dac_calib4,
+	msm_mux_dac_calib19,
+	msm_mux_flash_strobe,
+	msm_mux_cci_timer0,
+	msm_mux_cci_timer1,
+	msm_mux_cam_irq,
+	msm_mux_cci_timer2,
+	msm_mux_blsp1_spi,
+	msm_mux_pwr_nav_enabled_a,
+	msm_mux_ois_sync,
+	msm_mux_cci_timer3,
+	msm_mux_cci_timer4,
+	msm_mux_blsp3_spi,
+	msm_mux_qdss_cti_trig_out_a0,
+	msm_mux_dac_calib7,
+	msm_mux_accel_int,
+	msm_mux_gcc_gp1_clk_a,
+	msm_mux_dac_calib8,
+	msm_mux_alsp_int,
+	msm_mux_gcc_gp2_clk_a,
+	msm_mux_dac_calib9,
+	msm_mux_mag_int,
+	msm_mux_gcc_gp3_clk_a,
+	msm_mux_pwr_crypto_enabled_a,
+	msm_mux_cci_async,
+	msm_mux_cam1_standby,
+	msm_mux_dac_calib5,
+	msm_mux_cam1_rst,
+	msm_mux_dac_calib6,
+	msm_mux_dac_calib10,
+	msm_mux_gyro_int,
+	msm_mux_dac_calib11,
+	msm_mux_pressure_int,
+	msm_mux_dac_calib12,
+	msm_mux_blsp6_spi,
+	msm_mux_dac_calib13,
+	msm_mux_fp_int,
+	msm_mux_qdss_cti_trig_in_b1,
+	msm_mux_dac_calib14,
+	msm_mux_uim_batt,
+	msm_mux_cam0_ldo,
+	msm_mux_sd_write,
+	msm_mux_uim1_data,
+	msm_mux_uim1_clk,
+	msm_mux_uim1_reset,
+	msm_mux_uim1_present,
+	msm_mux_uim2_data,
+	msm_mux_uim2_clk,
+	msm_mux_uim2_reset,
+	msm_mux_uim2_present,
+	msm_mux_ts_xvdd,
+	msm_mux_mipi_dsi0,
+	msm_mux_nfc_dwl,
+	msm_mux_us_euro,
+	msm_mux_atest_char3,
+	msm_mux_dbg_out,
+	msm_mux_bimc_dte0,
+	msm_mux_ts_resout,
+	msm_mux_ts_sample,
+	msm_mux_sec_mi2s_mclk_b,
+	msm_mux_pri_mi2s,
+	msm_mux_codec_reset,
+	msm_mux_cdc_pdm0,
+	msm_mux_atest_char1,
+	msm_mux_ebi_cdc,
+	msm_mux_dac_calib17,
+	msm_mux_us_emitter,
+	msm_mux_atest_char0,
+	msm_mux_pri_mi2s_mclk_b,
+	msm_mux_lpass_slimbus,
+	msm_mux_lpass_slimbus0,
+	msm_mux_lpass_slimbus1,
+	msm_mux_codec_int1,
+	msm_mux_codec_int2,
+	msm_mux_wcss_bt,
+	msm_mux_atest_char2,
+	msm_mux_ebi_ch0,
+	msm_mux_wcss_wlan2,
+	msm_mux_wcss_wlan1,
+	msm_mux_wcss_wlan0,
+	msm_mux_wcss_wlan,
+	msm_mux_wcss_fm,
+	msm_mux_ext_lpass,
+	msm_mux_mss_lte,
+	msm_mux_key_volp,
+	msm_mux_pbs0,
+	msm_mux_cri_trng0,
+	msm_mux_key_snapshot,
+	msm_mux_pbs1,
+	msm_mux_cri_trng1,
+	msm_mux_key_focus,
+	msm_mux_pbs2,
+	msm_mux_cri_trng,
+	msm_mux_gcc_tlmm,
+	msm_mux_key_home,
+	msm_mux_pwr_down,
+	msm_mux_dmic0_clk,
+	msm_mux_blsp7_spi,
+	msm_mux_hdmi_int,
+	msm_mux_dmic0_data,
+	msm_mux_qdss_cti_trig_in_a1,
+	msm_mux_pri_mi2s_ws,
+	msm_mux_wsa_io,
+	msm_mux_wsa_en,
+	msm_mux_blsp_spi8,
+	msm_mux_wsa_irq,
+	msm_mux_blsp_i2c8,
+	msm_mux_gcc_plltest,
+	msm_mux_nav_pps_in_a,
+	msm_mux_pa_indicator,
+	msm_mux_nav_pps_in_b,
+	msm_mux_nav_pps,
+	msm_mux_modem_tsync,
+	msm_mux_nav_tsync,
+	msm_mux_ssbi_wtr1,
+	msm_mux_gsm1_tx,
+	msm_mux_dac_calib18,
+	msm_mux_gsm0_tx,
+	msm_mux_atest_char,
+	msm_mux_atest_tsens,
+	msm_mux_bimc_dte1,
+	msm_mux_dac_calib20,
+	msm_mux_cam2_rst,
+	msm_mux_ddr_bist,
+	msm_mux_dac_calib21,
+	msm_mux_cam2_standby,
+	msm_mux_dac_calib22,
+	msm_mux_cam3_rst,
+	msm_mux_dac_calib23,
+	msm_mux_cam3_standby,
+	msm_mux_dac_calib24,
+	msm_mux_sdcard_det,
+	msm_mux_dac_calib25,
+	msm_mux_cam1_ldo,
+	msm_mux_sec_mi2s,
+	msm_mux_blsp_spi7,
+	msm_mux_blsp_i2c7,
+	msm_mux_ss_switch,
+	msm_mux_tsens_max,
+	msm_mux_NA,
+};
+
+static const char * const gpio_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+	"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+	"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+	"gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+	"gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+	"gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+	"gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+	"gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+	"gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+	"gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+	"gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+	"gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+	"gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+	"gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+	"gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+	"gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+	"gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+	"gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134",
+	"gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140",
+	"gpio141",
+};
+static const char * const blsp_spi1_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const smb_int_groups[] = {
+	"gpio1",
+};
+static const char * const adsp_ext_groups[] = {
+	"gpio1",
+};
+static const char * const prng_rosc_groups[] = {
+	"gpio2",
+};
+static const char * const blsp_i2c1_groups[] = {
+	"gpio2", "gpio3",
+};
+static const char * const qdss_cti_trig_out_b0_groups[] = {
+	"gpio2",
+};
+static const char * const qdss_cti_trig_out_a1_groups[] = {
+	"gpio3",
+};
+static const char * const blsp_spi2_groups[] = {
+	"gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const blsp_uart2_groups[] = {
+	"gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const ldo_update_groups[] = {
+	"gpio4",
+};
+static const char * const dac_calib0_groups[] = {
+	"gpio4",
+};
+static const char * const ldo_en_groups[] = {
+	"gpio5",
+};
+static const char * const blsp_i2c2_groups[] = {
+	"gpio6", "gpio7",
+};
+static const char * const gcc_gp1_clk_b_groups[] = {
+	"gpio6", "gpio41",
+};
+static const char * const atest_gpsadc_dtest0_native_groups[] = {
+	"gpio7",
+};
+static const char * const blsp_spi3_groups[] = {
+	"gpio8", "gpio9", "gpio10", "gpio11",
+};
+static const char * const qdss_tracedata_b_groups[] = {
+	"gpio8", "gpio9", "gpio12", "gpio13", "gpio23", "gpio42", "gpio43",
+	"gpio44", "gpio45", "gpio46", "gpio47", "gpio66", "gpio86", "gpio87",
+	"gpio88", "gpio92",
+};
+static const char * const pwr_modem_enabled_b_groups[] = {
+	"gpio9",
+};
+static const char * const blsp_i2c3_groups[] = {
+	"gpio10", "gpio11",
+};
+static const char * const gcc_gp2_clk_b_groups[] = {
+	"gpio10",
+};
+static const char * const gcc_gp3_clk_b_groups[] = {
+	"gpio11",
+};
+static const char * const hall_int_groups[] = {
+	"gpio12",
+};
+static const char * const blsp_spi4_groups[] = {
+	"gpio12", "gpio13", "gpio14", "gpio15",
+};
+static const char * const blsp_uart4_groups[] = {
+	"gpio12", "gpio13", "gpio14", "gpio15",
+};
+static const char * const pwr_nav_enabled_b_groups[] = {
+	"gpio12",
+};
+static const char * const dac_calib1_groups[] = {
+	"gpio12",
+};
+static const char * const cap_int_groups[] = {
+	"gpio13",
+};
+static const char * const pwr_crypto_enabled_b_groups[] = {
+	"gpio13",
+};
+static const char * const dac_calib2_groups[] = {
+	"gpio13",
+};
+static const char * const blsp_i2c4_groups[] = {
+	"gpio14", "gpio15",
+};
+static const char * const nfc_disable_groups[] = {
+	"gpio16",
+};
+static const char * const blsp_spi5_groups[] = {
+	"gpio16", "gpio17", "gpio18", "gpio19",
+};
+static const char * const blsp_uart5_groups[] = {
+	"gpio16", "gpio17", "gpio18", "gpio19",
+};
+static const char * const qdss_traceclk_a_groups[] = {
+	"gpio16",
+};
+static const char * const atest_bbrx1_groups[] = {
+	"gpio16",
+};
+static const char * const nfc_irq_groups[] = {
+	"gpio17",
+};
+static const char * const m_voc_groups[] = {
+	"gpio17", "gpio21",
+};
+static const char * const qdss_cti_trig_in_a0_groups[] = {
+	"gpio17",
+};
+static const char * const atest_bbrx0_groups[] = {
+	"gpio17",
+};
+static const char * const blsp_i2c5_groups[] = {
+	"gpio18", "gpio19",
+};
+static const char * const qdss_tracectl_a_groups[] = {
+	"gpio18",
+};
+static const char * const atest_gpsadc_dtest1_native_groups[] = {
+	"gpio18",
+};
+static const char * const qdss_tracedata_a_groups[] = {
+	"gpio19", "gpio26", "gpio27", "gpio28", "gpio29", "gpio30", "gpio31",
+	"gpio32", "gpio33", "gpio34", "gpio35", "gpio36", "gpio38", "gpio39",
+	"gpio40", "gpio50",
+};
+static const char * const blsp_spi6_groups[] = {
+	"gpio20", "gpio21", "gpio22", "gpio23",
+};
+static const char * const blsp_uart6_groups[] = {
+	"gpio20", "gpio21", "gpio22", "gpio23",
+};
+static const char * const qdss_tracectl_b_groups[] = {
+	"gpio20",
+};
+static const char * const dac_calib15_groups[] = {
+	"gpio20",
+};
+static const char * const qdss_cti_trig_in_b0_groups[] = {
+	"gpio21",
+};
+static const char * const dac_calib16_groups[] = {
+	"gpio21",
+};
+static const char * const blsp_i2c6_groups[] = {
+	"gpio22", "gpio23",
+};
+static const char * const qdss_traceclk_b_groups[] = {
+	"gpio22",
+};
+static const char * const atest_wlan0_groups[] = {
+	"gpio22",
+};
+static const char * const atest_wlan1_groups[] = {
+	"gpio23",
+};
+static const char * const mdp_vsync_groups[] = {
+	"gpio24", "gpio25",
+};
+static const char * const pri_mi2s_mclk_a_groups[] = {
+	"gpio25",
+};
+static const char * const sec_mi2s_mclk_a_groups[] = {
+	"gpio25",
+};
+static const char * const qdss_cti_trig_out_b1_groups[] = {
+	"gpio25",
+};
+static const char * const cam_mclk_groups[] = {
+	"gpio26", "gpio27", "gpio28", "gpio128",
+};
+static const char * const dac_calib3_groups[] = {
+	"gpio28",
+};
+static const char * const cci_i2c_groups[] = {
+	"gpio29", "gpio30", "gpio31", "gpio32",
+};
+static const char * const pwr_modem_enabled_a_groups[] = {
+	"gpio29",
+};
+static const char * const dac_calib4_groups[] = {
+	"gpio29",
+};
+static const char * const dac_calib19_groups[] = {
+	"gpio30",
+};
+static const char * const flash_strobe_groups[] = {
+	"gpio33", "gpio34",
+};
+static const char * const cci_timer0_groups[] = {
+	"gpio33",
+};
+static const char * const cci_timer1_groups[] = {
+	"gpio34",
+};
+static const char * const cam_irq_groups[] = {
+	"gpio35",
+};
+static const char * const cci_timer2_groups[] = {
+	"gpio35",
+};
+static const char * const blsp1_spi_groups[] = {
+	"gpio35", "gpio36",
+};
+static const char * const pwr_nav_enabled_a_groups[] = {
+	"gpio35",
+};
+static const char * const ois_sync_groups[] = {
+	"gpio36",
+};
+static const char * const cci_timer3_groups[] = {
+	"gpio36",
+};
+static const char * const cci_timer4_groups[] = {
+	"gpio41",
+};
+static const char * const blsp3_spi_groups[] = {
+	"gpio41", "gpio50",
+};
+static const char * const qdss_cti_trig_out_a0_groups[] = {
+	"gpio41",
+};
+static const char * const dac_calib7_groups[] = {
+	"gpio41",
+};
+static const char * const accel_int_groups[] = {
+	"gpio42",
+};
+static const char * const gcc_gp1_clk_a_groups[] = {
+	"gpio42",
+};
+static const char * const dac_calib8_groups[] = {
+	"gpio42",
+};
+static const char * const alsp_int_groups[] = {
+	"gpio43",
+};
+static const char * const gcc_gp2_clk_a_groups[] = {
+	"gpio43",
+};
+static const char * const dac_calib9_groups[] = {
+	"gpio43",
+};
+static const char * const mag_int_groups[] = {
+	"gpio44",
+};
+static const char * const gcc_gp3_clk_a_groups[] = {
+	"gpio44",
+};
+static const char * const pwr_crypto_enabled_a_groups[] = {
+	"gpio36",
+};
+static const char * const cci_async_groups[] = {
+	"gpio38",
+};
+static const char * const cam1_standby_groups[] = {
+	"gpio39",
+};
+static const char * const dac_calib5_groups[] = {
+	"gpio39",
+};
+static const char * const cam1_rst_groups[] = {
+	"gpio40",
+};
+static const char * const dac_calib6_groups[] = {
+	"gpio40",
+};
+static const char * const dac_calib10_groups[] = {
+	"gpio44",
+};
+static const char * const gyro_int_groups[] = {
+	"gpio45",
+};
+static const char * const dac_calib11_groups[] = {
+	"gpio45",
+};
+static const char * const pressure_int_groups[] = {
+	"gpio46",
+};
+static const char * const dac_calib12_groups[] = {
+	"gpio46",
+};
+static const char * const blsp6_spi_groups[] = {
+	"gpio47", "gpio48",
+};
+static const char * const dac_calib13_groups[] = {
+	"gpio47",
+};
+static const char * const fp_int_groups[] = {
+	"gpio48",
+};
+static const char * const qdss_cti_trig_in_b1_groups[] = {
+	"gpio48",
+};
+static const char * const dac_calib14_groups[] = {
+	"gpio48",
+};
+static const char * const uim_batt_groups[] = {
+	"gpio49",
+};
+static const char * const cam0_ldo_groups[] = {
+	"gpio50",
+};
+static const char * const sd_write_groups[] = {
+	"gpio50",
+};
+static const char * const uim1_data_groups[] = {
+	"gpio51",
+};
+static const char * const uim1_clk_groups[] = {
+	"gpio52",
+};
+static const char * const uim1_reset_groups[] = {
+	"gpio53",
+};
+static const char * const uim1_present_groups[] = {
+	"gpio54",
+};
+static const char * const uim2_data_groups[] = {
+	"gpio55",
+};
+static const char * const uim2_clk_groups[] = {
+	"gpio56",
+};
+static const char * const uim2_reset_groups[] = {
+	"gpio57",
+};
+static const char * const uim2_present_groups[] = {
+	"gpio58",
+};
+static const char * const ts_xvdd_groups[] = {
+	"gpio60",
+};
+static const char * const mipi_dsi0_groups[] = {
+	"gpio61",
+};
+static const char * const nfc_dwl_groups[] = {
+	"gpio62",
+};
+static const char * const us_euro_groups[] = {
+	"gpio63",
+};
+static const char * const atest_char3_groups[] = {
+	"gpio63",
+};
+static const char * const dbg_out_groups[] = {
+	"gpio63",
+};
+static const char * const bimc_dte0_groups[] = {
+	"gpio63", "gpio65",
+};
+static const char * const ts_resout_groups[] = {
+	"gpio64",
+};
+static const char * const ts_sample_groups[] = {
+	"gpio65",
+};
+static const char * const sec_mi2s_mclk_b_groups[] = {
+	"gpio66",
+};
+static const char * const pri_mi2s_groups[] = {
+	"gpio66", "gpio88", "gpio91", "gpio93", "gpio94", "gpio95",
+};
+static const char * const codec_reset_groups[] = {
+	"gpio67",
+};
+static const char * const cdc_pdm0_groups[] = {
+	"gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73",
+	"gpio74",
+};
+static const char * const atest_char1_groups[] = {
+	"gpio67",
+};
+static const char * const ebi_cdc_groups[] = {
+	"gpio67", "gpio69", "gpio118", "gpio119", "gpio120", "gpio123",
+};
+static const char * const dac_calib17_groups[] = {
+	"gpio67",
+};
+static const char * const us_emitter_groups[] = {
+	"gpio68",
+};
+static const char * const atest_char0_groups[] = {
+	"gpio68",
+};
+static const char * const pri_mi2s_mclk_b_groups[] = {
+	"gpio69",
+};
+static const char * const lpass_slimbus_groups[] = {
+	"gpio70",
+};
+static const char * const lpass_slimbus0_groups[] = {
+	"gpio71",
+};
+static const char * const lpass_slimbus1_groups[] = {
+	"gpio72",
+};
+static const char * const codec_int1_groups[] = {
+	"gpio73",
+};
+static const char * const codec_int2_groups[] = {
+	"gpio74",
+};
+static const char * const wcss_bt_groups[] = {
+	"gpio75", "gpio83", "gpio84",
+};
+static const char * const atest_char2_groups[] = {
+	"gpio75",
+};
+static const char * const ebi_ch0_groups[] = {
+	"gpio75",
+};
+static const char * const wcss_wlan2_groups[] = {
+	"gpio76",
+};
+static const char * const wcss_wlan1_groups[] = {
+	"gpio77",
+};
+static const char * const wcss_wlan0_groups[] = {
+	"gpio78",
+};
+static const char * const wcss_wlan_groups[] = {
+	"gpio79", "gpio80",
+};
+static const char * const wcss_fm_groups[] = {
+	"gpio81", "gpio82",
+};
+static const char * const ext_lpass_groups[] = {
+	"gpio81",
+};
+static const char * const mss_lte_groups[] = {
+	"gpio82", "gpio83",
+};
+static const char * const key_volp_groups[] = {
+	"gpio85",
+};
+static const char * const pbs0_groups[] = {
+	"gpio85",
+};
+static const char * const cri_trng0_groups[] = {
+	"gpio85",
+};
+static const char * const key_snapshot_groups[] = {
+	"gpio86",
+};
+static const char * const pbs1_groups[] = {
+	"gpio86",
+};
+static const char * const cri_trng1_groups[] = {
+	"gpio86",
+};
+static const char * const key_focus_groups[] = {
+	"gpio87",
+};
+static const char * const pbs2_groups[] = {
+	"gpio87",
+};
+static const char * const cri_trng_groups[] = {
+	"gpio87",
+};
+static const char * const gcc_tlmm_groups[] = {
+	"gpio87",
+};
+static const char * const key_home_groups[] = {
+	"gpio88",
+};
+static const char * const pwr_down_groups[] = {
+	"gpio89",
+};
+static const char * const dmic0_clk_groups[] = {
+	"gpio89",
+};
+static const char * const blsp7_spi_groups[] = {
+	"gpio89", "gpio90",
+};
+static const char * const hdmi_int_groups[] = {
+	"gpio90",
+};
+static const char * const dmic0_data_groups[] = {
+	"gpio90",
+};
+static const char * const qdss_cti_trig_in_a1_groups[] = {
+	"gpio91",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+	"gpio92",
+};
+static const char * const wsa_io_groups[] = {
+	"gpio94", "gpio95",
+};
+static const char * const wsa_en_groups[] = {
+	"gpio96",
+};
+static const char * const blsp_spi8_groups[] = {
+	"gpio96", "gpio97", "gpio98", "gpio99",
+};
+static const char * const wsa_irq_groups[] = {
+	"gpio97",
+};
+static const char * const blsp_i2c8_groups[] = {
+	"gpio98", "gpio99",
+};
+static const char * const gcc_plltest_groups[] = {
+	"gpio98", "gpio99",
+};
+static const char * const nav_pps_in_a_groups[] = {
+	"gpio111",
+};
+static const char * const pa_indicator_groups[] = {
+	"gpio112",
+};
+static const char * const nav_pps_in_b_groups[] = {
+	"gpio113",
+};
+static const char * const nav_pps_groups[] = {
+	"gpio113",
+};
+static const char * const modem_tsync_groups[] = {
+	"gpio113",
+};
+static const char * const nav_tsync_groups[] = {
+	"gpio113",
+};
+static const char * const ssbi_wtr1_groups[] = {
+	"gpio114", "gpio123",
+};
+static const char * const gsm1_tx_groups[] = {
+	"gpio115",
+};
+static const char * const dac_calib18_groups[] = {
+	"gpio115",
+};
+static const char * const gsm0_tx_groups[] = {
+	"gpio117",
+};
+static const char * const atest_char_groups[] = {
+	"gpio120",
+};
+static const char * const atest_tsens_groups[] = {
+	"gpio120",
+};
+static const char * const bimc_dte1_groups[] = {
+	"gpio121", "gpio122",
+};
+static const char * const dac_calib20_groups[] = {
+	"gpio128",
+};
+static const char * const cam2_rst_groups[] = {
+	"gpio129",
+};
+static const char * const ddr_bist_groups[] = {
+	"gpio129", "gpio130", "gpio131", "gpio132",
+};
+static const char * const dac_calib21_groups[] = {
+	"gpio129",
+};
+static const char * const cam2_standby_groups[] = {
+	"gpio130",
+};
+static const char * const dac_calib22_groups[] = {
+	"gpio130",
+};
+static const char * const cam3_rst_groups[] = {
+	"gpio131",
+};
+static const char * const dac_calib23_groups[] = {
+	"gpio131",
+};
+static const char * const cam3_standby_groups[] = {
+	"gpio132",
+};
+static const char * const dac_calib24_groups[] = {
+	"gpio132",
+};
+static const char * const sdcard_det_groups[] = {
+	"gpio133",
+};
+static const char * const dac_calib25_groups[] = {
+	"gpio133",
+};
+static const char * const cam1_ldo_groups[] = {
+	"gpio134",
+};
+static const char * const sec_mi2s_groups[] = {
+	"gpio135", "gpio136", "gpio137", "gpio138",
+};
+static const char * const blsp_spi7_groups[] = {
+	"gpio135", "gpio136", "gpio137", "gpio138",
+};
+static const char * const blsp_i2c7_groups[] = {
+	"gpio135", "gpio136",
+};
+static const char * const ss_switch_groups[] = {
+	"gpio139",
+};
+static const char * const tsens_max_groups[] = {
+	"gpio139",
+};
+
+static const struct msm_function msm8953_functions[] = {
+	FUNCTION(gpio),
+	FUNCTION(blsp_spi1),
+	FUNCTION(smb_int),
+	FUNCTION(adsp_ext),
+	FUNCTION(prng_rosc),
+	FUNCTION(blsp_i2c1),
+	FUNCTION(qdss_cti_trig_out_b0),
+	FUNCTION(qdss_cti_trig_out_a1),
+	FUNCTION(blsp_spi2),
+	FUNCTION(blsp_uart2),
+	FUNCTION(ldo_update),
+	FUNCTION(dac_calib0),
+	FUNCTION(ldo_en),
+	FUNCTION(blsp_i2c2),
+	FUNCTION(gcc_gp1_clk_b),
+	FUNCTION(atest_gpsadc_dtest0_native),
+	FUNCTION(blsp_spi3),
+	FUNCTION(qdss_tracedata_b),
+	FUNCTION(pwr_modem_enabled_b),
+	FUNCTION(blsp_i2c3),
+	FUNCTION(gcc_gp2_clk_b),
+	FUNCTION(gcc_gp3_clk_b),
+	FUNCTION(hall_int),
+	FUNCTION(blsp_spi4),
+	FUNCTION(blsp_uart4),
+	FUNCTION(pwr_nav_enabled_b),
+	FUNCTION(dac_calib1),
+	FUNCTION(cap_int),
+	FUNCTION(pwr_crypto_enabled_b),
+	FUNCTION(dac_calib2),
+	FUNCTION(blsp_i2c4),
+	FUNCTION(nfc_disable),
+	FUNCTION(blsp_spi5),
+	FUNCTION(blsp_uart5),
+	FUNCTION(qdss_traceclk_a),
+	FUNCTION(atest_bbrx1),
+	FUNCTION(nfc_irq),
+	FUNCTION(m_voc),
+	FUNCTION(qdss_cti_trig_in_a0),
+	FUNCTION(atest_bbrx0),
+	FUNCTION(blsp_i2c5),
+	FUNCTION(qdss_tracectl_a),
+	FUNCTION(atest_gpsadc_dtest1_native),
+	FUNCTION(qdss_tracedata_a),
+	FUNCTION(blsp_spi6),
+	FUNCTION(blsp_uart6),
+	FUNCTION(qdss_tracectl_b),
+	FUNCTION(dac_calib15),
+	FUNCTION(qdss_cti_trig_in_b0),
+	FUNCTION(dac_calib16),
+	FUNCTION(blsp_i2c6),
+	FUNCTION(qdss_traceclk_b),
+	FUNCTION(atest_wlan0),
+	FUNCTION(atest_wlan1),
+	FUNCTION(mdp_vsync),
+	FUNCTION(pri_mi2s_mclk_a),
+	FUNCTION(sec_mi2s_mclk_a),
+	FUNCTION(qdss_cti_trig_out_b1),
+	FUNCTION(cam_mclk),
+	FUNCTION(dac_calib3),
+	FUNCTION(cci_i2c),
+	FUNCTION(pwr_modem_enabled_a),
+	FUNCTION(dac_calib4),
+	FUNCTION(dac_calib19),
+	FUNCTION(flash_strobe),
+	FUNCTION(cci_timer0),
+	FUNCTION(cci_timer1),
+	FUNCTION(cam_irq),
+	FUNCTION(cci_timer2),
+	FUNCTION(blsp1_spi),
+	FUNCTION(pwr_nav_enabled_a),
+	FUNCTION(ois_sync),
+	FUNCTION(cci_timer3),
+	FUNCTION(cci_timer4),
+	FUNCTION(blsp3_spi),
+	FUNCTION(qdss_cti_trig_out_a0),
+	FUNCTION(dac_calib7),
+	FUNCTION(accel_int),
+	FUNCTION(gcc_gp1_clk_a),
+	FUNCTION(dac_calib8),
+	FUNCTION(alsp_int),
+	FUNCTION(gcc_gp2_clk_a),
+	FUNCTION(dac_calib9),
+	FUNCTION(mag_int),
+	FUNCTION(gcc_gp3_clk_a),
+	FUNCTION(pwr_crypto_enabled_a),
+	FUNCTION(cci_async),
+	FUNCTION(cam1_standby),
+	FUNCTION(dac_calib5),
+	FUNCTION(cam1_rst),
+	FUNCTION(dac_calib6),
+	FUNCTION(dac_calib10),
+	FUNCTION(gyro_int),
+	FUNCTION(dac_calib11),
+	FUNCTION(pressure_int),
+	FUNCTION(dac_calib12),
+	FUNCTION(blsp6_spi),
+	FUNCTION(dac_calib13),
+	FUNCTION(fp_int),
+	FUNCTION(qdss_cti_trig_in_b1),
+	FUNCTION(dac_calib14),
+	FUNCTION(uim_batt),
+	FUNCTION(cam0_ldo),
+	FUNCTION(sd_write),
+	FUNCTION(uim1_data),
+	FUNCTION(uim1_clk),
+	FUNCTION(uim1_reset),
+	FUNCTION(uim1_present),
+	FUNCTION(uim2_data),
+	FUNCTION(uim2_clk),
+	FUNCTION(uim2_reset),
+	FUNCTION(uim2_present),
+	FUNCTION(ts_xvdd),
+	FUNCTION(mipi_dsi0),
+	FUNCTION(nfc_dwl),
+	FUNCTION(us_euro),
+	FUNCTION(atest_char3),
+	FUNCTION(dbg_out),
+	FUNCTION(bimc_dte0),
+	FUNCTION(ts_resout),
+	FUNCTION(ts_sample),
+	FUNCTION(sec_mi2s_mclk_b),
+	FUNCTION(pri_mi2s),
+	FUNCTION(codec_reset),
+	FUNCTION(cdc_pdm0),
+	FUNCTION(atest_char1),
+	FUNCTION(ebi_cdc),
+	FUNCTION(dac_calib17),
+	FUNCTION(us_emitter),
+	FUNCTION(atest_char0),
+	FUNCTION(pri_mi2s_mclk_b),
+	FUNCTION(lpass_slimbus),
+	FUNCTION(lpass_slimbus0),
+	FUNCTION(lpass_slimbus1),
+	FUNCTION(codec_int1),
+	FUNCTION(codec_int2),
+	FUNCTION(wcss_bt),
+	FUNCTION(atest_char2),
+	FUNCTION(ebi_ch0),
+	FUNCTION(wcss_wlan2),
+	FUNCTION(wcss_wlan1),
+	FUNCTION(wcss_wlan0),
+	FUNCTION(wcss_wlan),
+	FUNCTION(wcss_fm),
+	FUNCTION(ext_lpass),
+	FUNCTION(mss_lte),
+	FUNCTION(key_volp),
+	FUNCTION(pbs0),
+	FUNCTION(cri_trng0),
+	FUNCTION(key_snapshot),
+	FUNCTION(pbs1),
+	FUNCTION(cri_trng1),
+	FUNCTION(key_focus),
+	FUNCTION(pbs2),
+	FUNCTION(cri_trng),
+	FUNCTION(gcc_tlmm),
+	FUNCTION(key_home),
+	FUNCTION(pwr_down),
+	FUNCTION(dmic0_clk),
+	FUNCTION(blsp7_spi),
+	FUNCTION(hdmi_int),
+	FUNCTION(dmic0_data),
+	FUNCTION(qdss_cti_trig_in_a1),
+	FUNCTION(pri_mi2s_ws),
+	FUNCTION(wsa_io),
+	FUNCTION(wsa_en),
+	FUNCTION(blsp_spi8),
+	FUNCTION(wsa_irq),
+	FUNCTION(blsp_i2c8),
+	FUNCTION(gcc_plltest),
+	FUNCTION(nav_pps_in_a),
+	FUNCTION(pa_indicator),
+	FUNCTION(nav_pps_in_b),
+	FUNCTION(nav_pps),
+	FUNCTION(modem_tsync),
+	FUNCTION(nav_tsync),
+	FUNCTION(ssbi_wtr1),
+	FUNCTION(gsm1_tx),
+	FUNCTION(dac_calib18),
+	FUNCTION(gsm0_tx),
+	FUNCTION(atest_char),
+	FUNCTION(atest_tsens),
+	FUNCTION(bimc_dte1),
+	FUNCTION(dac_calib20),
+	FUNCTION(cam2_rst),
+	FUNCTION(ddr_bist),
+	FUNCTION(dac_calib21),
+	FUNCTION(cam2_standby),
+	FUNCTION(dac_calib22),
+	FUNCTION(cam3_rst),
+	FUNCTION(dac_calib23),
+	FUNCTION(cam3_standby),
+	FUNCTION(dac_calib24),
+	FUNCTION(sdcard_det),
+	FUNCTION(dac_calib25),
+	FUNCTION(cam1_ldo),
+	FUNCTION(sec_mi2s),
+	FUNCTION(blsp_spi7),
+	FUNCTION(blsp_i2c7),
+	FUNCTION(ss_switch),
+	FUNCTION(tsens_max),
+};
+
+static const struct msm_pingroup msm8953_groups[] = {
+	PINGROUP(0, blsp_spi1, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(1, blsp_spi1, adsp_ext, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(2, blsp_spi1, blsp_i2c1, prng_rosc, NA, NA, NA,
+		 qdss_cti_trig_out_b0, NA, NA),
+	PINGROUP(3, blsp_spi1, blsp_i2c1, NA, NA, NA, qdss_cti_trig_out_a1, NA,
+		 NA, NA),
+	PINGROUP(4, blsp_spi2, blsp_uart2, ldo_update, NA, dac_calib0, NA, NA,
+		 NA, NA),
+	PINGROUP(5, blsp_spi2, blsp_uart2, ldo_en, NA, NA, NA, NA, NA, NA),
+	PINGROUP(6, blsp_spi2, blsp_uart2, blsp_i2c2, gcc_gp1_clk_b, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(7, blsp_spi2, blsp_uart2, blsp_i2c2, NA,
+		 atest_gpsadc_dtest0_native, NA, NA, NA, NA),
+	PINGROUP(8, blsp_spi3, NA, NA, qdss_tracedata_b, NA, NA, NA, NA, NA),
+	PINGROUP(9, blsp_spi3, pwr_modem_enabled_b, NA, NA, qdss_tracedata_b,
+		 NA, NA, NA, NA),
+	PINGROUP(10, blsp_spi3, blsp_i2c3, gcc_gp2_clk_b, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(11, blsp_spi3, blsp_i2c3, gcc_gp3_clk_b, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(12, blsp_spi4, blsp_uart4, pwr_nav_enabled_b, NA, NA,
+		 qdss_tracedata_b, NA, dac_calib1, NA),
+	PINGROUP(13, blsp_spi4, blsp_uart4, pwr_crypto_enabled_b, NA, NA, NA,
+		 qdss_tracedata_b, NA, dac_calib2),
+	PINGROUP(14, blsp_spi4, blsp_uart4, blsp_i2c4, NA, NA, NA, NA, NA, NA),
+	PINGROUP(15, blsp_spi4, blsp_uart4, blsp_i2c4, NA, NA, NA, NA, NA, NA),
+	PINGROUP(16, blsp_spi5, blsp_uart5, NA, NA, qdss_traceclk_a, NA,
+		 atest_bbrx1, NA, NA),
+	PINGROUP(17, blsp_spi5, blsp_uart5, m_voc, qdss_cti_trig_in_a0, NA,
+		 atest_bbrx0, NA, NA, NA),
+	PINGROUP(18, blsp_spi5, blsp_uart5, blsp_i2c5, qdss_tracectl_a, NA,
+		 atest_gpsadc_dtest1_native, NA, NA, NA),
+	PINGROUP(19, blsp_spi5, blsp_uart5, blsp_i2c5, qdss_tracedata_a, NA,
+		 NA, NA, NA, NA),
+	PINGROUP(20, blsp_spi6, blsp_uart6, NA, NA, NA, qdss_tracectl_b, NA,
+		 dac_calib15, NA),
+	PINGROUP(21, blsp_spi6, blsp_uart6, m_voc, NA, NA, NA,
+		 qdss_cti_trig_in_b0, NA, dac_calib16),
+	PINGROUP(22, blsp_spi6, blsp_uart6, blsp_i2c6, qdss_traceclk_b, NA,
+		 atest_wlan0, NA, NA, NA),
+	PINGROUP(23, blsp_spi6, blsp_uart6, blsp_i2c6, qdss_tracedata_b, NA,
+		 atest_wlan1, NA, NA, NA),
+	PINGROUP(24, mdp_vsync, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(25, mdp_vsync, pri_mi2s_mclk_a, sec_mi2s_mclk_a,
+		 qdss_cti_trig_out_b1, NA, NA, NA, NA, NA),
+	PINGROUP(26, cam_mclk, NA, NA, NA, qdss_tracedata_a, NA, NA, NA, NA),
+	PINGROUP(27, cam_mclk, NA, NA, NA, qdss_tracedata_a, NA, NA, NA, NA),
+	PINGROUP(28, cam_mclk, NA, NA, NA, qdss_tracedata_a, NA, dac_calib3,
+		 NA, NA),
+	PINGROUP(29, cci_i2c, pwr_modem_enabled_a, NA, NA, NA,
+		 qdss_tracedata_a, NA, dac_calib4, NA),
+	PINGROUP(30, cci_i2c, NA, NA, NA, qdss_tracedata_a, NA, dac_calib19,
+		 NA, NA),
+	PINGROUP(31, cci_i2c, NA, NA, NA, qdss_tracedata_a, NA, NA, NA, NA),
+	PINGROUP(32, cci_i2c, NA, NA, NA, qdss_tracedata_a, NA, NA, NA, NA),
+	PINGROUP(33, cci_timer0, NA, NA, NA, NA, qdss_tracedata_a, NA, NA, NA),
+	PINGROUP(34, cci_timer1, NA, NA, NA, NA, qdss_tracedata_a, NA, NA, NA),
+	PINGROUP(35, cci_timer2, blsp1_spi, pwr_nav_enabled_a, NA, NA, NA,
+		 qdss_tracedata_a, NA, NA),
+	PINGROUP(36, cci_timer3, blsp1_spi, NA, pwr_crypto_enabled_a, NA, NA,
+		 NA, qdss_tracedata_a, NA),
+	PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(38, cci_async, NA, qdss_tracedata_a, NA, NA, NA, NA, NA, NA),
+	PINGROUP(39, NA, NA, NA, qdss_tracedata_a, NA, dac_calib5, NA, NA, NA),
+	PINGROUP(40, NA, NA, qdss_tracedata_a, NA, dac_calib6, NA, NA, NA, NA),
+	PINGROUP(41, cci_timer4, blsp3_spi, gcc_gp1_clk_b, NA, NA,
+		 qdss_cti_trig_out_a0, NA, dac_calib7, NA),
+	PINGROUP(42, gcc_gp1_clk_a, qdss_tracedata_b, NA, dac_calib8, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(43, gcc_gp2_clk_a, qdss_tracedata_b, NA, dac_calib9, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(44, gcc_gp3_clk_a, qdss_tracedata_b, NA, dac_calib10, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(45, NA, qdss_tracedata_b, NA, dac_calib11, NA, NA, NA, NA, NA),
+	PINGROUP(46, qdss_tracedata_b, NA, dac_calib12, NA, NA, NA, NA, NA, NA),
+	PINGROUP(47, blsp6_spi, qdss_tracedata_b, NA, dac_calib13, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(48, blsp6_spi, NA, qdss_cti_trig_in_b1, NA, dac_calib14, NA,
+		 NA, NA, NA),
+	PINGROUP(49, uim_batt, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(50, blsp3_spi, sd_write, NA, NA, NA, qdss_tracedata_a, NA, NA,
+		 NA),
+	PINGROUP(51, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(52, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(53, uim1_reset, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(54, uim1_present, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(55, uim2_data, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(56, uim2_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(57, uim2_reset, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(58, uim2_present, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(61, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(63, atest_char3, dbg_out, bimc_dte0, NA, NA, NA, NA, NA, NA),
+	PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(65, bimc_dte0, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(66, sec_mi2s_mclk_b, pri_mi2s, NA, qdss_tracedata_b, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(67, cdc_pdm0, atest_char1, ebi_cdc, NA, dac_calib17, NA, NA,
+		 NA, NA),
+	PINGROUP(68, cdc_pdm0, atest_char0, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(69, cdc_pdm0, pri_mi2s_mclk_b, ebi_cdc, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(70, lpass_slimbus, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(71, lpass_slimbus0, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(72, lpass_slimbus1, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(73, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(74, cdc_pdm0, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(75, wcss_bt, atest_char2, NA, ebi_ch0, NA, NA, NA, NA, NA),
+	PINGROUP(76, wcss_wlan2, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(77, wcss_wlan1, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(78, wcss_wlan0, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(79, wcss_wlan, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(80, wcss_wlan, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(81, wcss_fm, ext_lpass, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(82, wcss_fm, mss_lte, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(83, wcss_bt, mss_lte, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(84, wcss_bt, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(85, pbs0, cri_trng0, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(86, pbs1, cri_trng1, qdss_tracedata_b, NA, NA, NA, NA, NA, NA),
+	PINGROUP(87, pbs2, cri_trng, qdss_tracedata_b, gcc_tlmm, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(88, pri_mi2s, NA, NA, NA, qdss_tracedata_b, NA, NA, NA, NA),
+	PINGROUP(89, dmic0_clk, blsp7_spi, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(90, dmic0_data, blsp7_spi, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(91, pri_mi2s, NA, NA, NA, qdss_cti_trig_in_a1, NA, NA, NA, NA),
+	PINGROUP(92, pri_mi2s_ws, NA, NA, NA, qdss_tracedata_b, NA, NA, NA, NA),
+	PINGROUP(93, pri_mi2s, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(94, wsa_io, pri_mi2s, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(95, wsa_io, pri_mi2s, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(96, blsp_spi8, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(97, blsp_spi8, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(98, blsp_i2c8, blsp_spi8, gcc_plltest, NA, NA, NA, NA, NA, NA),
+	PINGROUP(99, blsp_i2c8, blsp_spi8, gcc_plltest, NA, NA, NA, NA, NA, NA),
+	PINGROUP(100, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(101, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(102, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(103, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(105, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(106, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(107, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(108, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(109, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(110, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(111, NA, NA, nav_pps_in_a, NA, NA, NA, NA, NA, NA),
+	PINGROUP(112, NA, pa_indicator, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(113, NA, nav_pps_in_b, nav_pps, modem_tsync, nav_tsync, NA,
+		 NA, NA, NA),
+	PINGROUP(114, NA, ssbi_wtr1, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(115, NA, gsm1_tx, NA, dac_calib18, NA, NA, NA, NA, NA),
+	PINGROUP(116, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(117, gsm0_tx, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(118, NA, ebi_cdc, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(119, NA, ebi_cdc, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(120, NA, atest_char, ebi_cdc, NA, atest_tsens, NA, NA, NA, NA),
+	PINGROUP(121, NA, NA, NA, bimc_dte1, NA, NA, NA, NA, NA),
+	PINGROUP(122, NA, NA, NA, bimc_dte1, NA, NA, NA, NA, NA),
+	PINGROUP(123, NA, ssbi_wtr1, ebi_cdc, NA, NA, NA, NA, NA, NA),
+	PINGROUP(124, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(125, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(128, cam_mclk, NA, dac_calib20, NA, NA, NA, NA, NA, NA),
+	PINGROUP(129, ddr_bist, NA, dac_calib21, NA, NA, NA, NA, NA, NA),
+	PINGROUP(130, ddr_bist, NA, dac_calib22, NA, NA, NA, NA, NA, NA),
+	PINGROUP(131, ddr_bist, NA, dac_calib23, NA, NA, NA, NA, NA, NA),
+	PINGROUP(132, ddr_bist, NA, dac_calib24, NA, NA, NA, NA, NA, NA),
+	PINGROUP(133, NA, dac_calib25, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(134, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(135, sec_mi2s, blsp_spi7, blsp_i2c7, NA, NA, NA, NA, NA, NA),
+	PINGROUP(136, sec_mi2s, blsp_spi7, blsp_i2c7, NA, NA, NA, NA, NA, NA),
+	PINGROUP(137, sec_mi2s, blsp_spi7, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(138, sec_mi2s, blsp_spi7, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(139, tsens_max, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(140, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(141, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	SDC_QDSD_PINGROUP(sdc1_clk, 0x10a000, 13, 6),
+	SDC_QDSD_PINGROUP(sdc1_cmd, 0x10a000, 11, 3),
+	SDC_QDSD_PINGROUP(sdc1_data, 0x10a000, 9, 0),
+	SDC_QDSD_PINGROUP(sdc1_rclk, 0x10a000, 15, 0),
+	SDC_QDSD_PINGROUP(sdc2_clk, 0x109000, 14, 6),
+	SDC_QDSD_PINGROUP(sdc2_cmd, 0x109000, 11, 3),
+	SDC_QDSD_PINGROUP(sdc2_data, 0x109000, 9, 0),
+	SDC_QDSD_PINGROUP(qdsd_clk, 0x19c000, 3, 0),
+	SDC_QDSD_PINGROUP(qdsd_cmd, 0x19c000, 8, 5),
+	SDC_QDSD_PINGROUP(qdsd_data0, 0x19c000, 13, 10),
+	SDC_QDSD_PINGROUP(qdsd_data1, 0x19c000, 18, 15),
+	SDC_QDSD_PINGROUP(qdsd_data2, 0x19c000, 23, 20),
+	SDC_QDSD_PINGROUP(qdsd_data3, 0x19c000, 28, 25),
+};
+
+static const struct msm_pinctrl_soc_data msm8953_pinctrl = {
+	.pins = msm8953_pins,
+	.npins = ARRAY_SIZE(msm8953_pins),
+	.functions = msm8953_functions,
+	.nfunctions = ARRAY_SIZE(msm8953_functions),
+	.groups = msm8953_groups,
+	.ngroups = ARRAY_SIZE(msm8953_groups),
+	.ngpios = 142,
+};
+
+static int msm8953_pinctrl_probe(struct platform_device *pdev)
+{
+	return msm_pinctrl_probe(pdev, &msm8953_pinctrl);
+}
+
+static const struct of_device_id msm8953_pinctrl_of_match[] = {
+	{ .compatible = "qcom,msm8953-pinctrl", },
+	{ },
+};
+
+static struct platform_driver msm8953_pinctrl_driver = {
+	.driver = {
+		.name = "msm8953-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = msm8953_pinctrl_of_match,
+	},
+	.probe = msm8953_pinctrl_probe,
+	.remove = msm_pinctrl_remove,
+};
+
+static int __init msm8953_pinctrl_init(void)
+{
+	return platform_driver_register(&msm8953_pinctrl_driver);
+}
+arch_initcall(msm8953_pinctrl_init);
+
+static void __exit msm8953_pinctrl_exit(void)
+{
+	platform_driver_unregister(&msm8953_pinctrl_driver);
+}
+module_exit(msm8953_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI msm8953 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, msm8953_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm670.c b/drivers/pinctrl/qcom/pinctrl-sdm670.c
index b454cc442..f7af6da 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm670.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm670.c
@@ -25,8 +25,13 @@
 		.ngroups = ARRAY_SIZE(fname##_groups),	\
 	}
 
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
+#define WEST	0x00100000
+
+#define DUMMY	0x0
 #define REG_SIZE 0x1000
-#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
 	{						\
 		.name = "gpio" #id,			\
 		.pins = gpio##id##_pins,		\
@@ -44,11 +49,13 @@
 			msm_mux_##f9			\
 		},					\
 		.nfuncs = 10,				\
-		.ctl_reg = REG_SIZE * id,		\
-		.io_reg = 0x4 + REG_SIZE * id,		\
-		.intr_cfg_reg = 0x8 + REG_SIZE * id,	\
-		.intr_status_reg = 0xc + REG_SIZE * id,	\
-		.intr_target_reg = 0x8 + REG_SIZE * id,	\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.dir_conn_reg = (base == NORTH) ? base + 0xa3000 : \
+			((base == SOUTH) ? base + 0xa6000 : base + 0xa4000), \
 		.mux_bit = 2,			\
 		.pull_bit = 0,			\
 		.drv_bit = 6,			\
@@ -63,6 +70,7 @@
 		.intr_polarity_bit = 1,		\
 		.intr_detection_bit = 2,	\
 		.intr_detection_width = 2,	\
+		.dir_conn_en_bit = 8,	        \
 	}
 
 #define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
@@ -114,10 +122,6 @@
 		.intr_detection_bit = -1,		\
 		.intr_detection_width = -1,		\
 	}
-
-static const u32 sdm670_tile_offsets[] = {0x100000, 0x500000, 0x900000};
-static u32 sdm670_pin_base[150];
-
 static const struct pinctrl_pin_desc sdm670_pins[] = {
 	PINCTRL_PIN(0, "GPIO_0"),
 	PINCTRL_PIN(1, "GPIO_1"),
@@ -1332,259 +1336,258 @@
  * Clients would not be able to request these dummy pin groups.
  */
 static const struct msm_pingroup sdm670_groups[] = {
-	[0] = PINGROUP(0, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[1] = PINGROUP(1, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[2] = PINGROUP(2, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[3] = PINGROUP(3, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[4] = PINGROUP(4, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[5] = PINGROUP(5, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[6] = PINGROUP(6, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
-	[7] = PINGROUP(7, qup9, ddr_bist, NA, atest_tsens2,
+	[0] = PINGROUP(0, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, SOUTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
 		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA),
-	[8] = PINGROUP(8, qup_l4, GP_PDM1, ddr_bist, NA, NA, NA, NA, NA,
+	[8] = PINGROUP(8, WEST, qup_l4, GP_PDM1, ddr_bist, NA, NA, NA, NA, NA,
 		       NA),
-	[9] = PINGROUP(9, qup_l5, ddr_bist, NA, NA, NA, NA, NA, NA, NA),
-	[10] = PINGROUP(10, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+	[9] = PINGROUP(9, WEST, qup_l5, ddr_bist, NA, NA, NA, NA, NA, NA, NA),
+	[10] = PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
 			atest_usb11, ddr_pxi2, NA, NA, NA),
-	[11] = PINGROUP(11, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+	[11] = PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
 			atest_usb10, ddr_pxi2, NA, NA, NA),
-	[12] = PINGROUP(12, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
 			NA, NA, NA, NA),
-	[13] = PINGROUP(13, cam_mclk, pll_bypassnl, qdss_gpio0, ddr_pxi3,
+	[13] = PINGROUP(13, WEST, cam_mclk, pll_bypassnl, qdss_gpio0, ddr_pxi3,
 			NA, NA, NA, NA, NA),
-	[14] = PINGROUP(14, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
+	[14] = PINGROUP(14, WEST, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
 			NA, NA, NA),
-	[15] = PINGROUP(15, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, NA,
+	[15] = PINGROUP(15, WEST, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA, NA,
 			NA),
-	[16] = PINGROUP(16, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, NA,
+	[16] = PINGROUP(16, WEST, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA, NA,
 			NA),
-	[17] = PINGROUP(17, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+	[17] = PINGROUP(17, WEST, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
 			NA, NA),
-	[18] = PINGROUP(18, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+	[18] = PINGROUP(18, WEST, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
 			NA, NA),
-	[19] = PINGROUP(19, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+	[19] = PINGROUP(19, WEST, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
 			NA, NA),
-	[20] = PINGROUP(20, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+	[20] = PINGROUP(20, WEST, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
 			NA, NA),
-	[21] = PINGROUP(21, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
+	[21] = PINGROUP(21, WEST, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
 			NA, NA, NA),
-	[22] = PINGROUP(22, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
+	[22] = PINGROUP(22, WEST, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
 			NA, NA, NA),
-	[23] = PINGROUP(23, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+	[23] = PINGROUP(23, WEST, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
 			NA, NA),
-	[24] = PINGROUP(24, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+	[24] = PINGROUP(24, WEST, cci_timer3, cci_async, qdss_gpio10, NA, NA,
 			NA, NA, NA, NA),
-	[25] = PINGROUP(25, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+	[25] = PINGROUP(25, WEST, cci_timer4, cci_async, qdss_gpio11, NA, NA,
 			NA, NA, NA, NA),
-	[26] = PINGROUP(26, cci_async, qdss_gpio12, JITTER_BIST, NA, NA,
+	[26] = PINGROUP(26, WEST, cci_async, qdss_gpio12, JITTER_BIST, NA, NA,
 			NA, NA, NA, NA),
-	[27] = PINGROUP(27, qup2, qdss_gpio13, PLL_BIST, NA, NA, NA, NA,
+	[27] = PINGROUP(27, WEST, qup2, qdss_gpio13, PLL_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[28] = PINGROUP(28, qup2, qdss_gpio14, AGERA_PLL, NA, NA, NA, NA,
+	[28] = PINGROUP(28, WEST, qup2, qdss_gpio14, AGERA_PLL, NA, NA, NA, NA,
 			NA, NA),
-	[29] = PINGROUP(29, qup2, NA, phase_flag1, qdss_gpio15,
+	[29] = PINGROUP(29, WEST, qup2, NA, phase_flag1, qdss_gpio15,
 			atest_tsens, NA, NA, NA, NA),
-	[30] = PINGROUP(30, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
+	[30] = PINGROUP(30, WEST, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
 			NA, NA),
-	[31] = PINGROUP(31, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[32] = PINGROUP(32, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[33] = PINGROUP(33, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[34] = PINGROUP(34, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[35] = PINGROUP(35, pci_e0, QUP_L4, JITTER_BIST, NA, NA, NA, NA,
+	[31] = PINGROUP(31, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[32] = PINGROUP(32, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[33] = PINGROUP(33, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[34] = PINGROUP(34, WEST, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
+	[35] = PINGROUP(35, NORTH, pci_e0, QUP_L4, JITTER_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[36] = PINGROUP(36, pci_e0, QUP_L5, PLL_BIST, NA, NA, NA, NA,
+	[36] = PINGROUP(36, NORTH, pci_e0, QUP_L5, PLL_BIST, NA, NA, NA, NA,
 			NA, NA),
-	[37] = PINGROUP(37, QUP_L6, AGERA_PLL, NA, NA, NA, NA, NA, NA,
+	[37] = PINGROUP(37, NORTH, QUP_L6, AGERA_PLL, NA, NA, NA, NA, NA, NA,
 			NA),
-	[38] = PINGROUP(38, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
-	[39] = PINGROUP(39, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, NORTH, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
 			NA),
-	[40] = PINGROUP(40, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+	[40] = PINGROUP(40, NORTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
 			NA, NA),
-	[41] = PINGROUP(41, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
+	[41] = PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
 			NA),
-	[42] = PINGROUP(42, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
+	[42] = PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
 			NA),
-	[43] = PINGROUP(43, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+	[43] = PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
 			NA),
-	[44] = PINGROUP(44, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+	[44] = PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
 			NA),
-	[45] = PINGROUP(45, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[46] = PINGROUP(46, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[47] = PINGROUP(47, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[48] = PINGROUP(48, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[49] = PINGROUP(49, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[50] = PINGROUP(50, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[51] = PINGROUP(51, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[52] = PINGROUP(52, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+	[45] = PINGROUP(45, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, SOUTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
 			NA, NA, NA),
-	[53] = PINGROUP(53, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
 			NA),
-	[54] = PINGROUP(54, qup10, GP_PDM0, phase_flag12, NA,
+	[54] = PINGROUP(54, NORTH, qup10, GP_PDM0, phase_flag12, NA,
 			wlan1_adc1, atest_usb13, ddr_pxi1, NA, NA),
-	[55] = PINGROUP(55, qup10, phase_flag13, NA, wlan1_adc0,
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, wlan1_adc0,
 			atest_usb12, ddr_pxi1, NA, NA, NA),
-	[56] = PINGROUP(56, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
 			NA),
-	[57] = PINGROUP(57, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
 			NA, NA, NA),
-	[58] = PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[59] = PINGROUP(59, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[60] = PINGROUP(60, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[61] = PINGROUP(61, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[62] = PINGROUP(62, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[63] = PINGROUP(63, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[64] = PINGROUP(64, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[65] = PINGROUP(65, pri_mi2s, qup8, wsa_clk, NA, NA, NA, NA, NA,
+	[58] = PINGROUP(58, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[59] = PINGROUP(59, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[60] = PINGROUP(60, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, wsa_clk, NA, NA, NA, NA, NA,
 			NA),
-	[66] = PINGROUP(66, pri_mi2s_ws, qup8, wsa_data, GP_PDM1, NA,
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, wsa_data, GP_PDM1, NA,
 			NA, NA, NA, NA),
-	[67] = PINGROUP(67, pri_mi2s, qup8, NA, atest_usb2, NA, NA, NA,
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, atest_usb2, NA, NA, NA,
 			NA, NA),
-	[68] = PINGROUP(68, pri_mi2s, qup8, NA, atest_usb23, NA, NA, NA,
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, atest_usb23, NA, NA, NA,
 			NA, NA),
-	[69] = PINGROUP(69, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[70] = PINGROUP(70, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[71] = PINGROUP(71, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[72] = PINGROUP(72, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[73] = PINGROUP(73, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[74] = PINGROUP(74, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[75] = PINGROUP(75, ter_mi2s, phase_flag8, qdss_gpio8,
+	[69] = PINGROUP(69, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[70] = PINGROUP(70, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[71] = PINGROUP(71, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[73] = PINGROUP(73, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[74] = PINGROUP(74, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8,
 			atest_usb22, QUP_L4, NA, NA, NA, NA),
-	[76] = PINGROUP(76, ter_mi2s, phase_flag9, qdss_gpio9,
+	[76] = PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9,
 			atest_usb21, QUP_L5, NA, NA, NA, NA),
-	[77] = PINGROUP(77, ter_mi2s, phase_flag4, qdss_gpio10,
+	[77] = PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10,
 			atest_usb20, QUP_L6, NA, NA, NA, NA),
-	[78] = PINGROUP(78, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+	[78] = PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
 			NA),
-	[79] = PINGROUP(79, sec_mi2s, GP_PDM2, NA, qdss_gpio11, NA, NA,
+	[79] = PINGROUP(79, NORTH, sec_mi2s, GP_PDM2, NA, qdss_gpio11, NA, NA,
 			NA, NA, NA),
-	[80] = PINGROUP(80, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
 			NA, NA),
-	[81] = PINGROUP(81, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[82] = PINGROUP(82, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[83] = PINGROUP(83, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[84] = PINGROUP(84, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
-	[85] = PINGROUP(85, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[86] = PINGROUP(86, qup5, copy_gp, NA, NA, NA, NA, NA, NA, NA),
-	[87] = PINGROUP(87, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[88] = PINGROUP(88, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[89] = PINGROUP(89, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, SOUTH, qup5, copy_gp, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
 			NA, NA, NA, NA),
-	[90] = PINGROUP(90, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
 			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti),
-	[91] = PINGROUP(91, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
 			qdss_cti, NA, NA, NA),
-	[92] = PINGROUP(92, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
 			NA, NA, NA, NA),
-	[93] = PINGROUP(93, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
 			NA, NA, NA, NA),
-	[94] = PINGROUP(94, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
 			NA),
-	[95] = PINGROUP(95, tsif2_data, sdc41, qup7, GP_PDM0, NA, NA,
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, GP_PDM0, NA, NA,
 			NA, NA, NA),
-	[96] = PINGROUP(96, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
 			NA, NA, NA, NA),
-	[97] = PINGROUP(97, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+	[97] = PINGROUP(97, WEST, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
 			NA),
-	[98] = PINGROUP(98, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+	[98] = PINGROUP(98, WEST, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
 			NA, NA),
-	[99] = PINGROUP(99, phase_flag14, prng_rosc, NA, NA, NA, NA, NA,
+	[99] = PINGROUP(99, NORTH, phase_flag14, prng_rosc, NA, NA, NA, NA, NA,
 			NA, NA),
-	[100] = PINGROUP(100, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
+	[100] = PINGROUP(100, WEST, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[101] = PINGROUP(101, NA, phase_flag5, NA, NA, NA, NA, NA, NA,
+	[101] = PINGROUP(101, WEST, NA, phase_flag5, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[102] = PINGROUP(102, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
+	[102] = PINGROUP(102, WEST, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[103] = PINGROUP(103, pci_e1, COPY_PHASE, NA, NA, NA, NA, NA, NA,
+	[103] = PINGROUP(103, WEST, pci_e1, COPY_PHASE, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[104] = PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[105] = PINGROUP(105, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+	[104] = PINGROUP(104, DUMMY, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
 			 NA, NA),
-	[106] = PINGROUP(106, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
 			 NA, NA),
-	[107] = PINGROUP(107, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
 			 NA, NA),
-	[108] = PINGROUP(108, uim2_present, qup13, NA, NA, NA, NA, NA,
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[109] = PINGROUP(109, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
-	[110] = PINGROUP(110, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
-	[111] = PINGROUP(111, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[112] = PINGROUP(112, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[113] = PINGROUP(113, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[114] = PINGROUP(114, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
+	[114] = PINGROUP(114, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
 			 NA, NA),
-	[115] = PINGROUP(115, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
+	[115] = PINGROUP(115, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA,
 			 NA, NA),
-	[116] = PINGROUP(116, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[117] = PINGROUP(117, NA, qdss_gpio0, atest_char, NA, NA, NA,
+	[116] = PINGROUP(116, SOUTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
 			 NA, NA, NA),
-	[118] = PINGROUP(118, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
 			 NA, NA, NA, NA),
-	[119] = PINGROUP(119, NA, qdss_gpio2, atest_char2, NA, NA, NA,
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
 			 NA, NA, NA),
-	[120] = PINGROUP(120, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
 			 NA, NA, NA),
-	[121] = PINGROUP(121, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
 			 NA, NA, NA),
-	[122] = PINGROUP(122, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
+	[122] = PINGROUP(122, NORTH, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[123] = PINGROUP(123, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+	[123] = PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
 			 NA),
-	[124] = PINGROUP(124, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+	[124] = PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
 			 NA),
-	[125] = PINGROUP(125, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[126] = PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[127] = PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[128] = PINGROUP(128, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
+	[125] = PINGROUP(125, NORTH, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
+	[126] = PINGROUP(126, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
 			 NA, NA),
-	[129] = PINGROUP(129, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
+	[129] = PINGROUP(129, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, NA, NA, NA,
 			 NA, NA),
-	[130] = PINGROUP(130, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+	[130] = PINGROUP(130, WEST, qlink_request, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[131] = PINGROUP(131, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+	[131] = PINGROUP(131, WEST, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[132] = PINGROUP(132, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[133] = PINGROUP(133, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[134] = PINGROUP(134, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[135] = PINGROUP(135, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+	[132] = PINGROUP(132, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, WEST, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[136] = PINGROUP(136, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[137] = PINGROUP(137, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+	[136] = PINGROUP(136, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, WEST, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
 			 NA),
-	[138] = PINGROUP(138, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+	[138] = PINGROUP(138, WEST, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
 			 NA),
-	[139] = PINGROUP(139, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+	[139] = PINGROUP(139, WEST, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[140] = PINGROUP(140, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+	[140] = PINGROUP(140, WEST, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
 			 NA),
-	[141] = PINGROUP(141, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+	[141] = PINGROUP(141, WEST, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[142] = PINGROUP(142, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+	[142] = PINGROUP(142, WEST, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[143] = PINGROUP(143, NA, NAV_PPS, NAV_PPS, GPS_TX, phase_flag31,
+	[143] = PINGROUP(143, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, phase_flag31,
 			 NA, NA, NA, NA),
-	[144] = PINGROUP(144, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[145] = PINGROUP(145, mss_lte, GPS_TX, NA, NA, NA, NA, NA, NA,
+	[144] = PINGROUP(144, SOUTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
+	[145] = PINGROUP(145, SOUTH, mss_lte, GPS_TX, NA, NA, NA, NA, NA, NA,
 			 NA),
-	[146] = PINGROUP(146, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[147] = PINGROUP(147, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[148] = PINGROUP(148, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[149] = PINGROUP(149, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[150] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x599000, 15, 0),
-	[151] = SDC_QDSD_PINGROUP(sdc1_clk, 0x599000, 13, 6),
-	[152] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x599000, 11, 3),
-	[153] = SDC_QDSD_PINGROUP(sdc1_data, 0x599000, 9, 0),
-	[154] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
-	[155] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
-	[156] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
-	[157] = UFS_RESET(ufs_reset, 0x99f000),
+	[146] = PINGROUP(146, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, WEST, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[150] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x99000, 15, 0),
+	[151] = SDC_QDSD_PINGROUP(sdc1_clk, 0x99000, 13, 6),
+	[152] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x99000, 11, 3),
+	[153] = SDC_QDSD_PINGROUP(sdc1_data, 0x99000, 9, 0),
+	[154] = SDC_QDSD_PINGROUP(sdc2_clk, 0x9a000, 14, 6),
+	[155] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x9a000, 11, 3),
+	[156] = SDC_QDSD_PINGROUP(sdc2_data, 0x9a000, 9, 0),
+	[157] = UFS_RESET(ufs_reset, 0x9f000),
 };
-
 static const struct msm_dir_conn sdm670_dir_conn[] = {
 	{1, 510},
 	{3, 511},
@@ -1596,7 +1599,7 @@
 	{24, 517},
 	{26, 518},
 	{30, 519},
-	{31, 639},
+	{31, 632},
 	{32, 521},
 	{34, 522},
 	{36, 523},
@@ -1604,12 +1607,12 @@
 	{38, 525},
 	{39, 526},
 	{40, 527},
-	{41, 637},
+	{41, 630},
 	{43, 529},
 	{44, 530},
 	{46, 531},
 	{48, 532},
-	{49, 640},
+	{49, 633},
 	{52, 534},
 	{53, 535},
 	{54, 536},
@@ -1625,7 +1628,7 @@
 	{85, 555},
 	{86, 556},
 	{88, 557},
-	{89, 638},
+	{89, 631},
 	{91, 559},
 	{92, 560},
 	{95, 561},
@@ -1651,6 +1654,14 @@
 	{132, 621},
 	{133, 622},
 	{145, 623},
+	{0, 216},
+	{0, 215},
+	{0, 214},
+	{0, 213},
+	{0, 212},
+	{0, 211},
+	{0, 210},
+	{0, 209},
 };
 
 static const struct msm_pinctrl_soc_data sdm670_pinctrl = {
@@ -1663,10 +1674,7 @@
 	.ngpios = 150,
 	.dir_conn = sdm670_dir_conn,
 	.n_dir_conns = ARRAY_SIZE(sdm670_dir_conn),
-	.tile_offsets = sdm670_tile_offsets,
-	.n_tile_offsets = ARRAY_SIZE(sdm670_tile_offsets),
-	.pin_base = sdm670_pin_base,
-	.reg_size = REG_SIZE,
+	.dir_conn_irq_base = 216,
 };
 
 static int sdm670_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
new file mode 100644
index 0000000..e77dcd9
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -0,0 +1,1818 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname)					\
+	[msm_mux_##fname] = {				\
+		.name = #fname,				\
+		.groups = fname##_groups,		\
+		.ngroups = ARRAY_SIZE(fname##_groups),	\
+	}
+
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
+#define EAST	0x00100000
+#define REG_SIZE 0x1000
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)	\
+	{						\
+		.name = "gpio" #id,			\
+		.pins = gpio##id##_pins,		\
+		.npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins),	\
+		.funcs = (int[]){			\
+			msm_mux_gpio, /* gpio mode */	\
+			msm_mux_##f1,			\
+			msm_mux_##f2,			\
+			msm_mux_##f3,			\
+			msm_mux_##f4,			\
+			msm_mux_##f5,			\
+			msm_mux_##f6,			\
+			msm_mux_##f7,			\
+			msm_mux_##f8,			\
+			msm_mux_##f9,			\
+			msm_mux_##f10			\
+		},					\
+		.nfuncs = 11,				\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.dir_conn_reg = (base == NORTH) ? base + 0xa4000 : \
+			((base == SOUTH) ? base + 0xa8000 : base + 0x9e000), \
+		.mux_bit = 2,			\
+		.pull_bit = 0,			\
+		.drv_bit = 6,			\
+		.oe_bit = 9,			\
+		.in_bit = 0,			\
+		.out_bit = 1,			\
+		.intr_enable_bit = 0,		\
+		.intr_status_bit = 0,		\
+		.intr_target_bit = 5,		\
+		.intr_target_kpss_val = 3,	\
+		.intr_raw_status_bit = 4,	\
+		.intr_polarity_bit = 1,		\
+		.intr_detection_bit = 2,	\
+		.intr_detection_width = 2,	\
+		.dir_conn_en_bit = 8,	        \
+	}
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = ctl,				\
+		.io_reg = 0,				\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = pull,			\
+		.drv_bit = drv,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = -1,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+
+#define UFS_RESET(pg_name, offset)				\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = offset,			\
+		.io_reg = offset + 0x4,			\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = 3,				\
+		.drv_bit = 0,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = 0,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+static const struct pinctrl_pin_desc sdm845_pins[] = {
+	PINCTRL_PIN(0, "GPIO_0"),
+	PINCTRL_PIN(1, "GPIO_1"),
+	PINCTRL_PIN(2, "GPIO_2"),
+	PINCTRL_PIN(3, "GPIO_3"),
+	PINCTRL_PIN(4, "GPIO_4"),
+	PINCTRL_PIN(5, "GPIO_5"),
+	PINCTRL_PIN(6, "GPIO_6"),
+	PINCTRL_PIN(7, "GPIO_7"),
+	PINCTRL_PIN(8, "GPIO_8"),
+	PINCTRL_PIN(9, "GPIO_9"),
+	PINCTRL_PIN(10, "GPIO_10"),
+	PINCTRL_PIN(11, "GPIO_11"),
+	PINCTRL_PIN(12, "GPIO_12"),
+	PINCTRL_PIN(13, "GPIO_13"),
+	PINCTRL_PIN(14, "GPIO_14"),
+	PINCTRL_PIN(15, "GPIO_15"),
+	PINCTRL_PIN(16, "GPIO_16"),
+	PINCTRL_PIN(17, "GPIO_17"),
+	PINCTRL_PIN(18, "GPIO_18"),
+	PINCTRL_PIN(19, "GPIO_19"),
+	PINCTRL_PIN(20, "GPIO_20"),
+	PINCTRL_PIN(21, "GPIO_21"),
+	PINCTRL_PIN(22, "GPIO_22"),
+	PINCTRL_PIN(23, "GPIO_23"),
+	PINCTRL_PIN(24, "GPIO_24"),
+	PINCTRL_PIN(25, "GPIO_25"),
+	PINCTRL_PIN(26, "GPIO_26"),
+	PINCTRL_PIN(27, "GPIO_27"),
+	PINCTRL_PIN(28, "GPIO_28"),
+	PINCTRL_PIN(29, "GPIO_29"),
+	PINCTRL_PIN(30, "GPIO_30"),
+	PINCTRL_PIN(31, "GPIO_31"),
+	PINCTRL_PIN(32, "GPIO_32"),
+	PINCTRL_PIN(33, "GPIO_33"),
+	PINCTRL_PIN(34, "GPIO_34"),
+	PINCTRL_PIN(35, "GPIO_35"),
+	PINCTRL_PIN(36, "GPIO_36"),
+	PINCTRL_PIN(37, "GPIO_37"),
+	PINCTRL_PIN(38, "GPIO_38"),
+	PINCTRL_PIN(39, "GPIO_39"),
+	PINCTRL_PIN(40, "GPIO_40"),
+	PINCTRL_PIN(41, "GPIO_41"),
+	PINCTRL_PIN(42, "GPIO_42"),
+	PINCTRL_PIN(43, "GPIO_43"),
+	PINCTRL_PIN(44, "GPIO_44"),
+	PINCTRL_PIN(45, "GPIO_45"),
+	PINCTRL_PIN(46, "GPIO_46"),
+	PINCTRL_PIN(47, "GPIO_47"),
+	PINCTRL_PIN(48, "GPIO_48"),
+	PINCTRL_PIN(49, "GPIO_49"),
+	PINCTRL_PIN(50, "GPIO_50"),
+	PINCTRL_PIN(51, "GPIO_51"),
+	PINCTRL_PIN(52, "GPIO_52"),
+	PINCTRL_PIN(53, "GPIO_53"),
+	PINCTRL_PIN(54, "GPIO_54"),
+	PINCTRL_PIN(55, "GPIO_55"),
+	PINCTRL_PIN(56, "GPIO_56"),
+	PINCTRL_PIN(57, "GPIO_57"),
+	PINCTRL_PIN(58, "GPIO_58"),
+	PINCTRL_PIN(59, "GPIO_59"),
+	PINCTRL_PIN(60, "GPIO_60"),
+	PINCTRL_PIN(61, "GPIO_61"),
+	PINCTRL_PIN(62, "GPIO_62"),
+	PINCTRL_PIN(63, "GPIO_63"),
+	PINCTRL_PIN(64, "GPIO_64"),
+	PINCTRL_PIN(65, "GPIO_65"),
+	PINCTRL_PIN(66, "GPIO_66"),
+	PINCTRL_PIN(67, "GPIO_67"),
+	PINCTRL_PIN(68, "GPIO_68"),
+	PINCTRL_PIN(69, "GPIO_69"),
+	PINCTRL_PIN(70, "GPIO_70"),
+	PINCTRL_PIN(71, "GPIO_71"),
+	PINCTRL_PIN(72, "GPIO_72"),
+	PINCTRL_PIN(73, "GPIO_73"),
+	PINCTRL_PIN(74, "GPIO_74"),
+	PINCTRL_PIN(75, "GPIO_75"),
+	PINCTRL_PIN(76, "GPIO_76"),
+	PINCTRL_PIN(77, "GPIO_77"),
+	PINCTRL_PIN(78, "GPIO_78"),
+	PINCTRL_PIN(79, "GPIO_79"),
+	PINCTRL_PIN(80, "GPIO_80"),
+	PINCTRL_PIN(81, "GPIO_81"),
+	PINCTRL_PIN(82, "GPIO_82"),
+	PINCTRL_PIN(83, "GPIO_83"),
+	PINCTRL_PIN(84, "GPIO_84"),
+	PINCTRL_PIN(85, "GPIO_85"),
+	PINCTRL_PIN(86, "GPIO_86"),
+	PINCTRL_PIN(87, "GPIO_87"),
+	PINCTRL_PIN(88, "GPIO_88"),
+	PINCTRL_PIN(89, "GPIO_89"),
+	PINCTRL_PIN(90, "GPIO_90"),
+	PINCTRL_PIN(91, "GPIO_91"),
+	PINCTRL_PIN(92, "GPIO_92"),
+	PINCTRL_PIN(93, "GPIO_93"),
+	PINCTRL_PIN(94, "GPIO_94"),
+	PINCTRL_PIN(95, "GPIO_95"),
+	PINCTRL_PIN(96, "GPIO_96"),
+	PINCTRL_PIN(97, "GPIO_97"),
+	PINCTRL_PIN(98, "GPIO_98"),
+	PINCTRL_PIN(99, "GPIO_99"),
+	PINCTRL_PIN(100, "GPIO_100"),
+	PINCTRL_PIN(101, "GPIO_101"),
+	PINCTRL_PIN(102, "GPIO_102"),
+	PINCTRL_PIN(103, "GPIO_103"),
+	PINCTRL_PIN(104, "GPIO_104"),
+	PINCTRL_PIN(105, "GPIO_105"),
+	PINCTRL_PIN(106, "GPIO_106"),
+	PINCTRL_PIN(107, "GPIO_107"),
+	PINCTRL_PIN(108, "GPIO_108"),
+	PINCTRL_PIN(109, "GPIO_109"),
+	PINCTRL_PIN(110, "GPIO_110"),
+	PINCTRL_PIN(111, "GPIO_111"),
+	PINCTRL_PIN(112, "GPIO_112"),
+	PINCTRL_PIN(113, "GPIO_113"),
+	PINCTRL_PIN(114, "GPIO_114"),
+	PINCTRL_PIN(115, "GPIO_115"),
+	PINCTRL_PIN(116, "GPIO_116"),
+	PINCTRL_PIN(117, "GPIO_117"),
+	PINCTRL_PIN(118, "GPIO_118"),
+	PINCTRL_PIN(119, "GPIO_119"),
+	PINCTRL_PIN(120, "GPIO_120"),
+	PINCTRL_PIN(121, "GPIO_121"),
+	PINCTRL_PIN(122, "GPIO_122"),
+	PINCTRL_PIN(123, "GPIO_123"),
+	PINCTRL_PIN(124, "GPIO_124"),
+	PINCTRL_PIN(125, "GPIO_125"),
+	PINCTRL_PIN(126, "GPIO_126"),
+	PINCTRL_PIN(127, "GPIO_127"),
+	PINCTRL_PIN(128, "GPIO_128"),
+	PINCTRL_PIN(129, "GPIO_129"),
+	PINCTRL_PIN(130, "GPIO_130"),
+	PINCTRL_PIN(131, "GPIO_131"),
+	PINCTRL_PIN(132, "GPIO_132"),
+	PINCTRL_PIN(133, "GPIO_133"),
+	PINCTRL_PIN(134, "GPIO_134"),
+	PINCTRL_PIN(135, "GPIO_135"),
+	PINCTRL_PIN(136, "GPIO_136"),
+	PINCTRL_PIN(137, "GPIO_137"),
+	PINCTRL_PIN(138, "GPIO_138"),
+	PINCTRL_PIN(139, "GPIO_139"),
+	PINCTRL_PIN(140, "GPIO_140"),
+	PINCTRL_PIN(141, "GPIO_141"),
+	PINCTRL_PIN(142, "GPIO_142"),
+	PINCTRL_PIN(143, "GPIO_143"),
+	PINCTRL_PIN(144, "GPIO_144"),
+	PINCTRL_PIN(145, "GPIO_145"),
+	PINCTRL_PIN(146, "GPIO_146"),
+	PINCTRL_PIN(147, "GPIO_147"),
+	PINCTRL_PIN(148, "GPIO_148"),
+	PINCTRL_PIN(149, "GPIO_149"),
+	PINCTRL_PIN(150, "SDC2_CLK"),
+	PINCTRL_PIN(151, "SDC2_CMD"),
+	PINCTRL_PIN(152, "SDC2_DATA"),
+	PINCTRL_PIN(153, "UFS_RESET"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+	static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(58);
+DECLARE_MSM_GPIO_PINS(59);
+DECLARE_MSM_GPIO_PINS(60);
+DECLARE_MSM_GPIO_PINS(61);
+DECLARE_MSM_GPIO_PINS(62);
+DECLARE_MSM_GPIO_PINS(63);
+DECLARE_MSM_GPIO_PINS(64);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(69);
+DECLARE_MSM_GPIO_PINS(70);
+DECLARE_MSM_GPIO_PINS(71);
+DECLARE_MSM_GPIO_PINS(72);
+DECLARE_MSM_GPIO_PINS(73);
+DECLARE_MSM_GPIO_PINS(74);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(104);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+DECLARE_MSM_GPIO_PINS(142);
+DECLARE_MSM_GPIO_PINS(143);
+DECLARE_MSM_GPIO_PINS(144);
+DECLARE_MSM_GPIO_PINS(145);
+DECLARE_MSM_GPIO_PINS(146);
+DECLARE_MSM_GPIO_PINS(147);
+DECLARE_MSM_GPIO_PINS(148);
+DECLARE_MSM_GPIO_PINS(149);
+
+static const unsigned int sdc2_clk_pins[] = { 150 };
+static const unsigned int sdc2_cmd_pins[] = { 151 };
+static const unsigned int sdc2_data_pins[] = { 152 };
+static const unsigned int ufs_reset_pins[] = { 153 };
+
+enum sdm845_functions {
+	msm_mux_ddr_pxi3,
+	msm_mux_cam_mclk,
+	msm_mux_pll_bypassnl,
+	msm_mux_qdss_gpio0,
+	msm_mux_pll_reset,
+	msm_mux_qdss_gpio1,
+	msm_mux_qdss_gpio2,
+	msm_mux_qdss_gpio3,
+	msm_mux_cci_i2c,
+	msm_mux_qup1,
+	msm_mux_qdss_gpio4,
+	msm_mux_qdss_gpio5,
+	msm_mux_qdss_gpio6,
+	msm_mux_qdss_gpio7,
+	msm_mux_cci_timer0,
+	msm_mux_gcc_gp2,
+	msm_mux_qdss_gpio8,
+	msm_mux_cci_timer1,
+	msm_mux_gcc_gp3,
+	msm_mux_qdss_gpio,
+	msm_mux_cci_timer2,
+	msm_mux_qdss_gpio9,
+	msm_mux_cci_timer3,
+	msm_mux_cci_async,
+	msm_mux_qdss_gpio10,
+	msm_mux_cci_timer4,
+	msm_mux_qdss_gpio11,
+	msm_mux_qdss_gpio12,
+	msm_mux_qup2,
+	msm_mux_qdss_gpio13,
+	msm_mux_qdss_gpio14,
+	msm_mux_phase_flag1,
+	msm_mux_qdss_gpio15,
+	msm_mux_phase_flag2,
+	msm_mux_qup11,
+	msm_mux_qup14,
+	msm_mux_pci_e0,
+	msm_mux_jitter_bist,
+	msm_mux_pll_bist,
+	msm_mux_atest_tsens,
+	msm_mux_agera_pll,
+	msm_mux_usb_phy,
+	msm_mux_lpass_slimbus,
+	msm_mux_sd_write,
+	msm_mux_tsif1_error,
+	msm_mux_qup3,
+	msm_mux_qup6,
+	msm_mux_qup12,
+	msm_mux_phase_flag16,
+	msm_mux_qup10,
+	msm_mux_phase_flag11,
+	msm_mux_phase_flag12,
+	msm_mux_phase_flag13,
+	msm_mux_phase_flag17,
+	msm_mux_qua_mi2s,
+	msm_mux_gcc_gp1,
+	msm_mux_phase_flag18,
+	msm_mux_phase_flag19,
+	msm_mux_phase_flag20,
+	msm_mux_cri_trng0,
+	msm_mux_phase_flag21,
+	msm_mux_cri_trng1,
+	msm_mux_phase_flag22,
+	msm_mux_cri_trng,
+	msm_mux_phase_flag23,
+	msm_mux_phase_flag24,
+	msm_mux_pri_mi2s,
+	msm_mux_sp_cmu,
+	msm_mux_phase_flag25,
+	msm_mux_qup8,
+	msm_mux_pri_mi2s_ws,
+	msm_mux_spkr_i2s,
+	msm_mux_audio_ref,
+	msm_mux_tsense_pwm1,
+	msm_mux_tsense_pwm2,
+	msm_mux_btfm_slimbus,
+	msm_mux_atest_usb2,
+	msm_mux_ter_mi2s,
+	msm_mux_phase_flag7,
+	msm_mux_atest_usb23,
+	msm_mux_phase_flag8,
+	msm_mux_atest_usb22,
+	msm_mux_phase_flag9,
+	msm_mux_atest_usb21,
+	msm_mux_phase_flag4,
+	msm_mux_atest_usb20,
+	msm_mux_sec_mi2s,
+	msm_mux_qup15,
+	msm_mux_qup5,
+	msm_mux_tsif1_clk,
+	msm_mux_qup4,
+	msm_mux_qspi_cs,
+	msm_mux_tgu_ch3,
+	msm_mux_phase_flag10,
+	msm_mux_tsif1_en,
+	msm_mux_mdp_vsync0,
+	msm_mux_mdp_vsync1,
+	msm_mux_mdp_vsync2,
+	msm_mux_mdp_vsync3,
+	msm_mux_tgu_ch0,
+	msm_mux_phase_flag0,
+	msm_mux_tsif1_data,
+	msm_mux_sdc4_cmd,
+	msm_mux_qspi0,
+	msm_mux_tgu_ch1,
+	msm_mux_tsif2_error,
+	msm_mux_sdc43,
+	msm_mux_qspi1,
+	msm_mux_vfr_1,
+	msm_mux_tgu_ch2,
+	msm_mux_tsif2_clk,
+	msm_mux_sdc4_clk,
+	msm_mux_qup7,
+	msm_mux_qspi2,
+	msm_mux_tsif2_en,
+	msm_mux_sdc42,
+	msm_mux_qspi3,
+	msm_mux_tsif2_data,
+	msm_mux_sdc41,
+	msm_mux_qspi_clk,
+	msm_mux_tsif2_sync,
+	msm_mux_sdc40,
+	msm_mux_phase_flag3,
+	msm_mux_ldo_en,
+	msm_mux_ldo_update,
+	msm_mux_phase_flag14,
+	msm_mux_phase_flag15,
+	msm_mux_pci_e1,
+	msm_mux_prng_rosc,
+	msm_mux_phase_flag5,
+	msm_mux_uim2_data,
+	msm_mux_qup13,
+	msm_mux_uim2_clk,
+	msm_mux_uim2_reset,
+	msm_mux_uim2_present,
+	msm_mux_uim1_data,
+	msm_mux_uim1_clk,
+	msm_mux_uim1_reset,
+	msm_mux_uim1_present,
+	msm_mux_uim_batt,
+	msm_mux_edp_hot,
+	msm_mux_nav_pps,
+	msm_mux_atest_char,
+	msm_mux_adsp_ext,
+	msm_mux_atest_char3,
+	msm_mux_atest_char2,
+	msm_mux_atest_char1,
+	msm_mux_atest_char0,
+	msm_mux_qlink_request,
+	msm_mux_qlink_enable,
+	msm_mux_pa_indicator,
+	msm_mux_phase_flag26,
+	msm_mux_phase_flag27,
+	msm_mux_phase_flag28,
+	msm_mux_phase_flag6,
+	msm_mux_phase_flag29,
+	msm_mux_phase_flag30,
+	msm_mux_phase_flag31,
+	msm_mux_mss_lte,
+	msm_mux_qup0,
+	msm_mux_gpio,
+	msm_mux_qup9,
+	msm_mux_qdss_cti,
+	msm_mux_ddr_pxi0,
+	msm_mux_ddr_bist,
+	msm_mux_atest_tsens2,
+	msm_mux_vsense_trigger,
+	msm_mux_atest_usb1,
+	msm_mux_qup_l4,
+	msm_mux_wlan1_adc1,
+	msm_mux_atest_usb13,
+	msm_mux_ddr_pxi1,
+	msm_mux_qup_l5,
+	msm_mux_wlan1_adc0,
+	msm_mux_atest_usb12,
+	msm_mux_mdp_vsync,
+	msm_mux_qup_l6,
+	msm_mux_wlan2_adc1,
+	msm_mux_atest_usb11,
+	msm_mux_ddr_pxi2,
+	msm_mux_edp_lcd,
+	msm_mux_dbg_out,
+	msm_mux_wlan2_adc0,
+	msm_mux_atest_usb10,
+	msm_mux_m_voc,
+	msm_mux_tsif1_sync,
+	msm_mux_NA,
+};
+
+static const char * const ddr_pxi3_groups[] = {
+	"gpio12", "gpio13",
+};
+static const char * const cam_mclk_groups[] = {
+	"gpio13", "gpio14", "gpio15", "gpio16",
+};
+static const char * const pll_bypassnl_groups[] = {
+	"gpio13",
+};
+static const char * const qdss_gpio0_groups[] = {
+	"gpio13", "gpio117",
+};
+static const char * const pll_reset_groups[] = {
+	"gpio14",
+};
+static const char * const qdss_gpio1_groups[] = {
+	"gpio14", "gpio118",
+};
+static const char * const qdss_gpio2_groups[] = {
+	"gpio15", "gpio119",
+};
+static const char * const qdss_gpio3_groups[] = {
+	"gpio16", "gpio120",
+};
+static const char * const cci_i2c_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qup1_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qdss_gpio4_groups[] = {
+	"gpio17", "gpio121",
+};
+static const char * const qdss_gpio5_groups[] = {
+	"gpio18", "gpio122",
+};
+static const char * const qdss_gpio6_groups[] = {
+	"gpio19", "gpio41",
+};
+static const char * const qdss_gpio7_groups[] = {
+	"gpio20", "gpio42",
+};
+static const char * const cci_timer0_groups[] = {
+	"gpio21",
+};
+static const char * const gcc_gp2_groups[] = {
+	"gpio21", "gpio58",
+};
+static const char * const qdss_gpio8_groups[] = {
+	"gpio21", "gpio75",
+};
+static const char * const cci_timer1_groups[] = {
+	"gpio22",
+};
+static const char * const gcc_gp3_groups[] = {
+	"gpio22", "gpio59",
+};
+static const char * const qdss_gpio_groups[] = {
+	"gpio22", "gpio30", "gpio123", "gpio124",
+};
+static const char * const cci_timer2_groups[] = {
+	"gpio23",
+};
+static const char * const qdss_gpio9_groups[] = {
+	"gpio23", "gpio76",
+};
+static const char * const cci_timer3_groups[] = {
+	"gpio24",
+};
+static const char * const cci_async_groups[] = {
+	"gpio24", "gpio25", "gpio26",
+};
+static const char * const qdss_gpio10_groups[] = {
+	"gpio24", "gpio77",
+};
+static const char * const cci_timer4_groups[] = {
+	"gpio25",
+};
+static const char * const qdss_gpio11_groups[] = {
+	"gpio25", "gpio79",
+};
+static const char * const qdss_gpio12_groups[] = {
+	"gpio26", "gpio80",
+};
+static const char * const qup2_groups[] = {
+	"gpio27", "gpio28", "gpio29", "gpio30",
+};
+static const char * const qdss_gpio13_groups[] = {
+	"gpio27", "gpio93",
+};
+static const char * const qdss_gpio14_groups[] = {
+	"gpio28", "gpio43",
+};
+static const char * const phase_flag1_groups[] = {
+	"gpio29",
+};
+static const char * const qdss_gpio15_groups[] = {
+	"gpio29", "gpio44",
+};
+static const char * const phase_flag2_groups[] = {
+	"gpio30",
+};
+static const char * const qup11_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const qup14_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const pci_e0_groups[] = {
+	"gpio35", "gpio36",
+};
+static const char * const jitter_bist_groups[] = {
+	"gpio35",
+};
+static const char * const pll_bist_groups[] = {
+	"gpio36",
+};
+static const char * const atest_tsens_groups[] = {
+	"gpio36",
+};
+static const char * const agera_pll_groups[] = {
+	"gpio37",
+};
+static const char * const usb_phy_groups[] = {
+	"gpio38",
+};
+static const char * const lpass_slimbus_groups[] = {
+	"gpio39", "gpio70", "gpio71", "gpio72",
+};
+static const char * const sd_write_groups[] = {
+	"gpio40",
+};
+static const char * const tsif1_error_groups[] = {
+	"gpio40",
+};
+static const char * const qup3_groups[] = {
+	"gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const qup6_groups[] = {
+	"gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const qup12_groups[] = {
+	"gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const phase_flag16_groups[] = {
+	"gpio52",
+};
+static const char * const qup10_groups[] = {
+	"gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const phase_flag11_groups[] = {
+	"gpio53",
+};
+static const char * const phase_flag12_groups[] = {
+	"gpio54",
+};
+static const char * const phase_flag13_groups[] = {
+	"gpio55",
+};
+static const char * const phase_flag17_groups[] = {
+	"gpio56",
+};
+static const char * const qua_mi2s_groups[] = {
+	"gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+};
+static const char * const gcc_gp1_groups[] = {
+	"gpio57", "gpio78",
+};
+static const char * const phase_flag18_groups[] = {
+	"gpio57",
+};
+static const char * const phase_flag19_groups[] = {
+	"gpio58",
+};
+static const char * const phase_flag20_groups[] = {
+	"gpio59",
+};
+static const char * const cri_trng0_groups[] = {
+	"gpio60",
+};
+static const char * const phase_flag21_groups[] = {
+	"gpio60",
+};
+static const char * const cri_trng1_groups[] = {
+	"gpio61",
+};
+static const char * const phase_flag22_groups[] = {
+	"gpio61",
+};
+static const char * const cri_trng_groups[] = {
+	"gpio62",
+};
+static const char * const phase_flag23_groups[] = {
+	"gpio62",
+};
+static const char * const phase_flag24_groups[] = {
+	"gpio63",
+};
+static const char * const pri_mi2s_groups[] = {
+	"gpio64", "gpio65", "gpio67", "gpio68",
+};
+static const char * const sp_cmu_groups[] = {
+	"gpio64",
+};
+static const char * const phase_flag25_groups[] = {
+	"gpio64",
+};
+static const char * const qup8_groups[] = {
+	"gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+	"gpio66",
+};
+static const char * const spkr_i2s_groups[] = {
+	"gpio69", "gpio70", "gpio71", "gpio72",
+};
+static const char * const audio_ref_groups[] = {
+	"gpio69",
+};
+static const char * const tsense_pwm1_groups[] = {
+	"gpio71",
+};
+static const char * const tsense_pwm2_groups[] = {
+	"gpio71",
+};
+static const char * const btfm_slimbus_groups[] = {
+	"gpio73", "gpio74",
+};
+static const char * const atest_usb2_groups[] = {
+	"gpio73",
+};
+static const char * const ter_mi2s_groups[] = {
+	"gpio74", "gpio75", "gpio76", "gpio77", "gpio78",
+};
+static const char * const phase_flag7_groups[] = {
+	"gpio74",
+};
+static const char * const atest_usb23_groups[] = {
+	"gpio74",
+};
+static const char * const phase_flag8_groups[] = {
+	"gpio75",
+};
+static const char * const atest_usb22_groups[] = {
+	"gpio75",
+};
+static const char * const phase_flag9_groups[] = {
+	"gpio76",
+};
+static const char * const atest_usb21_groups[] = {
+	"gpio76",
+};
+static const char * const phase_flag4_groups[] = {
+	"gpio77",
+};
+static const char * const atest_usb20_groups[] = {
+	"gpio77",
+};
+static const char * const sec_mi2s_groups[] = {
+	"gpio79", "gpio80", "gpio81", "gpio82", "gpio83",
+};
+static const char * const qup15_groups[] = {
+	"gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const qup5_groups[] = {
+	"gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const tsif1_clk_groups[] = {
+	"gpio89",
+};
+static const char * const qup4_groups[] = {
+	"gpio89", "gpio90", "gpio91", "gpio92",
+};
+static const char * const qspi_cs_groups[] = {
+	"gpio89", "gpio90",
+};
+static const char * const tgu_ch3_groups[] = {
+	"gpio89",
+};
+static const char * const phase_flag10_groups[] = {
+	"gpio89",
+};
+static const char * const tsif1_en_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync0_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync1_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync2_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync3_groups[] = {
+	"gpio90",
+};
+static const char * const tgu_ch0_groups[] = {
+	"gpio90",
+};
+static const char * const phase_flag0_groups[] = {
+	"gpio90",
+};
+static const char * const tsif1_data_groups[] = {
+	"gpio91",
+};
+static const char * const sdc4_cmd_groups[] = {
+	"gpio91",
+};
+static const char * const qspi0_groups[] = {
+	"gpio91",
+};
+static const char * const tgu_ch1_groups[] = {
+	"gpio91",
+};
+static const char * const tsif2_error_groups[] = {
+	"gpio92",
+};
+static const char * const sdc43_groups[] = {
+	"gpio92",
+};
+static const char * const qspi1_groups[] = {
+	"gpio92",
+};
+static const char * const vfr_1_groups[] = {
+	"gpio92",
+};
+static const char * const tgu_ch2_groups[] = {
+	"gpio92",
+};
+static const char * const tsif2_clk_groups[] = {
+	"gpio93",
+};
+static const char * const sdc4_clk_groups[] = {
+	"gpio93",
+};
+static const char * const qup7_groups[] = {
+	"gpio93", "gpio94", "gpio95", "gpio96",
+};
+static const char * const qspi2_groups[] = {
+	"gpio93",
+};
+static const char * const tsif2_en_groups[] = {
+	"gpio94",
+};
+static const char * const sdc42_groups[] = {
+	"gpio94",
+};
+static const char * const qspi3_groups[] = {
+	"gpio94",
+};
+static const char * const tsif2_data_groups[] = {
+	"gpio95",
+};
+static const char * const sdc41_groups[] = {
+	"gpio95",
+};
+static const char * const qspi_clk_groups[] = {
+	"gpio95",
+};
+static const char * const tsif2_sync_groups[] = {
+	"gpio96",
+};
+static const char * const sdc40_groups[] = {
+	"gpio96",
+};
+static const char * const phase_flag3_groups[] = {
+	"gpio96",
+};
+static const char * const ldo_en_groups[] = {
+	"gpio97",
+};
+static const char * const ldo_update_groups[] = {
+	"gpio98",
+};
+static const char * const phase_flag14_groups[] = {
+	"gpio99",
+};
+static const char * const phase_flag15_groups[] = {
+	"gpio100",
+};
+static const char * const pci_e1_groups[] = {
+	"gpio102", "gpio103",
+};
+static const char * const prng_rosc_groups[] = {
+	"gpio102",
+};
+static const char * const phase_flag5_groups[] = {
+	"gpio103",
+};
+static const char * const uim2_data_groups[] = {
+	"gpio105",
+};
+static const char * const qup13_groups[] = {
+	"gpio105", "gpio106", "gpio107", "gpio108",
+};
+static const char * const uim2_clk_groups[] = {
+	"gpio106",
+};
+static const char * const uim2_reset_groups[] = {
+	"gpio107",
+};
+static const char * const uim2_present_groups[] = {
+	"gpio108",
+};
+static const char * const uim1_data_groups[] = {
+	"gpio109",
+};
+static const char * const uim1_clk_groups[] = {
+	"gpio110",
+};
+static const char * const uim1_reset_groups[] = {
+	"gpio111",
+};
+static const char * const uim1_present_groups[] = {
+	"gpio112",
+};
+static const char * const uim_batt_groups[] = {
+	"gpio113",
+};
+static const char * const edp_hot_groups[] = {
+	"gpio113",
+};
+static const char * const nav_pps_groups[] = {
+	"gpio114", "gpio114", "gpio115", "gpio115", "gpio128", "gpio128",
+	"gpio129", "gpio129", "gpio143", "gpio143",
+};
+static const char * const atest_char_groups[] = {
+	"gpio117",
+};
+static const char * const adsp_ext_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char3_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char2_groups[] = {
+	"gpio119",
+};
+static const char * const atest_char1_groups[] = {
+	"gpio120",
+};
+static const char * const atest_char0_groups[] = {
+	"gpio121",
+};
+static const char * const qlink_request_groups[] = {
+	"gpio130",
+};
+static const char * const qlink_enable_groups[] = {
+	"gpio131",
+};
+static const char * const pa_indicator_groups[] = {
+	"gpio135",
+};
+static const char * const phase_flag26_groups[] = {
+	"gpio137",
+};
+static const char * const phase_flag27_groups[] = {
+	"gpio138",
+};
+static const char * const phase_flag28_groups[] = {
+	"gpio139",
+};
+static const char * const phase_flag6_groups[] = {
+	"gpio140",
+};
+static const char * const phase_flag29_groups[] = {
+	"gpio141",
+};
+static const char * const phase_flag30_groups[] = {
+	"gpio142",
+};
+static const char * const phase_flag31_groups[] = {
+	"gpio143",
+};
+static const char * const mss_lte_groups[] = {
+	"gpio144", "gpio145",
+};
+static const char * const qup0_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const gpio_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+	"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+	"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+	"gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+	"gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+	"gpio50", "gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56",
+	"gpio57", "gpio58", "gpio59", "gpio60", "gpio61", "gpio62", "gpio63",
+	"gpio64", "gpio65", "gpio66", "gpio67", "gpio68", "gpio69", "gpio70",
+	"gpio71", "gpio72", "gpio73", "gpio74", "gpio75", "gpio76", "gpio77",
+	"gpio78", "gpio79", "gpio80", "gpio81", "gpio82", "gpio83", "gpio84",
+	"gpio85", "gpio86", "gpio87", "gpio88", "gpio89", "gpio90", "gpio91",
+	"gpio92", "gpio93", "gpio94", "gpio95", "gpio96", "gpio97", "gpio98",
+	"gpio99", "gpio100", "gpio101", "gpio102", "gpio103", "gpio104",
+	"gpio105", "gpio106", "gpio107", "gpio108", "gpio109", "gpio110",
+	"gpio111", "gpio112", "gpio113", "gpio114", "gpio115", "gpio116",
+	"gpio117", "gpio118", "gpio119", "gpio120", "gpio121", "gpio122",
+	"gpio123", "gpio124", "gpio125", "gpio126", "gpio127", "gpio128",
+	"gpio129", "gpio130", "gpio131", "gpio132", "gpio133", "gpio134",
+	"gpio135", "gpio136", "gpio137", "gpio138", "gpio139", "gpio140",
+	"gpio141", "gpio142", "gpio143", "gpio144", "gpio145", "gpio146",
+	"gpio147", "gpio148", "gpio149",
+};
+static const char * const qup9_groups[] = {
+	"gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const qdss_cti_groups[] = {
+	"gpio4", "gpio5", "gpio51", "gpio52", "gpio62", "gpio63", "gpio90",
+	"gpio91",
+};
+static const char * const ddr_pxi0_groups[] = {
+	"gpio6", "gpio7",
+};
+static const char * const ddr_bist_groups[] = {
+	"gpio7", "gpio8", "gpio9", "gpio10",
+};
+static const char * const atest_tsens2_groups[] = {
+	"gpio7",
+};
+static const char * const vsense_trigger_groups[] = {
+	"gpio7",
+};
+static const char * const atest_usb1_groups[] = {
+	"gpio7",
+};
+static const char * const qup_l4_groups[] = {
+	"gpio8", "gpio35", "gpio105", "gpio123",
+};
+static const char * const wlan1_adc1_groups[] = {
+	"gpio8",
+};
+static const char * const atest_usb13_groups[] = {
+	"gpio8",
+};
+static const char * const ddr_pxi1_groups[] = {
+	"gpio8", "gpio9",
+};
+static const char * const qup_l5_groups[] = {
+	"gpio9", "gpio36", "gpio106", "gpio124",
+};
+static const char * const wlan1_adc0_groups[] = {
+	"gpio9",
+};
+static const char * const atest_usb12_groups[] = {
+	"gpio9",
+};
+static const char * const mdp_vsync_groups[] = {
+	"gpio10", "gpio11", "gpio12", "gpio97", "gpio98",
+};
+static const char * const qup_l6_groups[] = {
+	"gpio10", "gpio37", "gpio107", "gpio125",
+};
+static const char * const wlan2_adc1_groups[] = {
+	"gpio10",
+};
+static const char * const atest_usb11_groups[] = {
+	"gpio10",
+};
+static const char * const ddr_pxi2_groups[] = {
+	"gpio10", "gpio11",
+};
+static const char * const edp_lcd_groups[] = {
+	"gpio11",
+};
+static const char * const dbg_out_groups[] = {
+	"gpio11",
+};
+static const char * const wlan2_adc0_groups[] = {
+	"gpio11",
+};
+static const char * const atest_usb10_groups[] = {
+	"gpio11",
+};
+static const char * const m_voc_groups[] = {
+	"gpio12",
+};
+static const char * const tsif1_sync_groups[] = {
+	"gpio12",
+};
+
+static const struct msm_function sdm845_functions[] = {
+	FUNCTION(ddr_pxi3),
+	FUNCTION(cam_mclk),
+	FUNCTION(pll_bypassnl),
+	FUNCTION(qdss_gpio0),
+	FUNCTION(pll_reset),
+	FUNCTION(qdss_gpio1),
+	FUNCTION(qdss_gpio2),
+	FUNCTION(qdss_gpio3),
+	FUNCTION(cci_i2c),
+	FUNCTION(qup1),
+	FUNCTION(qdss_gpio4),
+	FUNCTION(qdss_gpio5),
+	FUNCTION(qdss_gpio6),
+	FUNCTION(qdss_gpio7),
+	FUNCTION(cci_timer0),
+	FUNCTION(gcc_gp2),
+	FUNCTION(qdss_gpio8),
+	FUNCTION(cci_timer1),
+	FUNCTION(gcc_gp3),
+	FUNCTION(qdss_gpio),
+	FUNCTION(cci_timer2),
+	FUNCTION(qdss_gpio9),
+	FUNCTION(cci_timer3),
+	FUNCTION(cci_async),
+	FUNCTION(qdss_gpio10),
+	FUNCTION(cci_timer4),
+	FUNCTION(qdss_gpio11),
+	FUNCTION(qdss_gpio12),
+	FUNCTION(qup2),
+	FUNCTION(qdss_gpio13),
+	FUNCTION(qdss_gpio14),
+	FUNCTION(phase_flag1),
+	FUNCTION(qdss_gpio15),
+	FUNCTION(phase_flag2),
+	FUNCTION(qup11),
+	FUNCTION(qup14),
+	FUNCTION(pci_e0),
+	FUNCTION(jitter_bist),
+	FUNCTION(pll_bist),
+	FUNCTION(atest_tsens),
+	FUNCTION(agera_pll),
+	FUNCTION(usb_phy),
+	FUNCTION(lpass_slimbus),
+	FUNCTION(sd_write),
+	FUNCTION(tsif1_error),
+	FUNCTION(qup3),
+	FUNCTION(qup6),
+	FUNCTION(qup12),
+	FUNCTION(phase_flag16),
+	FUNCTION(qup10),
+	FUNCTION(phase_flag11),
+	FUNCTION(phase_flag12),
+	FUNCTION(phase_flag13),
+	FUNCTION(phase_flag17),
+	FUNCTION(qua_mi2s),
+	FUNCTION(gcc_gp1),
+	FUNCTION(phase_flag18),
+	FUNCTION(phase_flag19),
+	FUNCTION(phase_flag20),
+	FUNCTION(cri_trng0),
+	FUNCTION(phase_flag21),
+	FUNCTION(cri_trng1),
+	FUNCTION(phase_flag22),
+	FUNCTION(cri_trng),
+	FUNCTION(phase_flag23),
+	FUNCTION(phase_flag24),
+	FUNCTION(pri_mi2s),
+	FUNCTION(sp_cmu),
+	FUNCTION(phase_flag25),
+	FUNCTION(qup8),
+	FUNCTION(pri_mi2s_ws),
+	FUNCTION(spkr_i2s),
+	FUNCTION(audio_ref),
+	FUNCTION(tsense_pwm1),
+	FUNCTION(tsense_pwm2),
+	FUNCTION(btfm_slimbus),
+	FUNCTION(atest_usb2),
+	FUNCTION(ter_mi2s),
+	FUNCTION(phase_flag7),
+	FUNCTION(atest_usb23),
+	FUNCTION(phase_flag8),
+	FUNCTION(atest_usb22),
+	FUNCTION(phase_flag9),
+	FUNCTION(atest_usb21),
+	FUNCTION(phase_flag4),
+	FUNCTION(atest_usb20),
+	FUNCTION(sec_mi2s),
+	FUNCTION(qup15),
+	FUNCTION(qup5),
+	FUNCTION(tsif1_clk),
+	FUNCTION(qup4),
+	FUNCTION(qspi_cs),
+	FUNCTION(tgu_ch3),
+	FUNCTION(phase_flag10),
+	FUNCTION(tsif1_en),
+	FUNCTION(mdp_vsync0),
+	FUNCTION(mdp_vsync1),
+	FUNCTION(mdp_vsync2),
+	FUNCTION(mdp_vsync3),
+	FUNCTION(tgu_ch0),
+	FUNCTION(phase_flag0),
+	FUNCTION(tsif1_data),
+	FUNCTION(sdc4_cmd),
+	FUNCTION(qspi0),
+	FUNCTION(tgu_ch1),
+	FUNCTION(tsif2_error),
+	FUNCTION(sdc43),
+	FUNCTION(qspi1),
+	FUNCTION(vfr_1),
+	FUNCTION(tgu_ch2),
+	FUNCTION(tsif2_clk),
+	FUNCTION(sdc4_clk),
+	FUNCTION(qup7),
+	FUNCTION(qspi2),
+	FUNCTION(tsif2_en),
+	FUNCTION(sdc42),
+	FUNCTION(qspi3),
+	FUNCTION(tsif2_data),
+	FUNCTION(sdc41),
+	FUNCTION(qspi_clk),
+	FUNCTION(tsif2_sync),
+	FUNCTION(sdc40),
+	FUNCTION(phase_flag3),
+	FUNCTION(ldo_en),
+	FUNCTION(ldo_update),
+	FUNCTION(phase_flag14),
+	FUNCTION(phase_flag15),
+	FUNCTION(pci_e1),
+	FUNCTION(prng_rosc),
+	FUNCTION(phase_flag5),
+	FUNCTION(uim2_data),
+	FUNCTION(qup13),
+	FUNCTION(uim2_clk),
+	FUNCTION(uim2_reset),
+	FUNCTION(uim2_present),
+	FUNCTION(uim1_data),
+	FUNCTION(uim1_clk),
+	FUNCTION(uim1_reset),
+	FUNCTION(uim1_present),
+	FUNCTION(uim_batt),
+	FUNCTION(edp_hot),
+	FUNCTION(nav_pps),
+	FUNCTION(atest_char),
+	FUNCTION(adsp_ext),
+	FUNCTION(atest_char3),
+	FUNCTION(atest_char2),
+	FUNCTION(atest_char1),
+	FUNCTION(atest_char0),
+	FUNCTION(qlink_request),
+	FUNCTION(qlink_enable),
+	FUNCTION(pa_indicator),
+	FUNCTION(phase_flag26),
+	FUNCTION(phase_flag27),
+	FUNCTION(phase_flag28),
+	FUNCTION(phase_flag6),
+	FUNCTION(phase_flag29),
+	FUNCTION(phase_flag30),
+	FUNCTION(phase_flag31),
+	FUNCTION(mss_lte),
+	FUNCTION(qup0),
+	FUNCTION(gpio),
+	FUNCTION(qup9),
+	FUNCTION(qdss_cti),
+	FUNCTION(ddr_pxi0),
+	FUNCTION(ddr_bist),
+	FUNCTION(atest_tsens2),
+	FUNCTION(vsense_trigger),
+	FUNCTION(atest_usb1),
+	FUNCTION(qup_l4),
+	FUNCTION(wlan1_adc1),
+	FUNCTION(atest_usb13),
+	FUNCTION(ddr_pxi1),
+	FUNCTION(qup_l5),
+	FUNCTION(wlan1_adc0),
+	FUNCTION(atest_usb12),
+	FUNCTION(mdp_vsync),
+	FUNCTION(qup_l6),
+	FUNCTION(wlan2_adc1),
+	FUNCTION(atest_usb11),
+	FUNCTION(ddr_pxi2),
+	FUNCTION(edp_lcd),
+	FUNCTION(dbg_out),
+	FUNCTION(wlan2_adc0),
+	FUNCTION(atest_usb10),
+	FUNCTION(m_voc),
+	FUNCTION(tsif1_sync),
+};
+
+/* Every pin is maintained as a single group, and missing or non-existing pin
+ * would be maintained as dummy group to synchronize pin group index with
+ * pin descriptor registered with pinctrl core.
+ * Clients would not be able to request these dummy pin groups.
+ */
+static const struct msm_pingroup sdm845_groups[] = {
+	[0] = PINGROUP(0, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, EAST, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
+		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA),
+	[8] = PINGROUP(8, EAST, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
+		       atest_usb13, ddr_pxi1, NA, NA),
+	[9] = PINGROUP(9, EAST, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
+		       ddr_pxi1, NA, NA, NA, NA),
+	[10] = PINGROUP(10, EAST, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+			atest_usb11, ddr_pxi2, NA, NA, NA, NA),
+	[11] = PINGROUP(11, EAST, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+			atest_usb10, ddr_pxi2, NA, NA, NA, NA),
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
+			NA, NA, NA, NA, NA),
+	[13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0,
+			ddr_pxi3, NA, NA, NA, NA, NA, NA),
+	[14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
+			NA, NA, NA, NA),
+	[15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+			NA, NA, NA),
+	[18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+			NA, NA, NA),
+	[19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+			NA, NA, NA),
+	[20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+			NA, NA, NA),
+	[21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
+			NA, NA, NA, NA),
+	[22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
+			NA, NA, NA, NA),
+	[23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+			NA, NA, NA, NA, NA),
+	[25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+			NA, NA, NA, NA, NA),
+	[26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[27] = PINGROUP(27, EAST, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[28] = PINGROUP(28, EAST, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[29] = PINGROUP(29, EAST, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
+			NA, NA, NA, NA),
+	[30] = PINGROUP(30, EAST, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
+			NA, NA, NA),
+	[31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
+			NA, NA, NA),
+	[36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
+			NA, NA, NA, NA, NA),
+	[37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, EAST, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[41] = PINGROUP(41, EAST, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA,
+			NA),
+	[42] = PINGROUP(42, EAST, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA,
+			NA),
+	[43] = PINGROUP(43, EAST, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+			NA, NA),
+	[44] = PINGROUP(44, EAST, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+			NA, NA),
+	[45] = PINGROUP(45, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, EAST, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+			NA, NA, NA, NA),
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
+			NA, NA),
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+			NA, NA, NA, NA),
+	[58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
+			NA, NA, NA, NA),
+	[59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
+			NA, NA, NA, NA),
+	[60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
+			NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
+			NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+			NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+			NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+			NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[69] = PINGROUP(69, EAST, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[70] = PINGROUP(70, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[71] = PINGROUP(71, EAST, lpass_slimbus, spkr_i2s, tsense_pwm1,
+			tsense_pwm2, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, EAST, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[73] = PINGROUP(73, EAST, btfm_slimbus, atest_usb2, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[74] = PINGROUP(74, EAST, btfm_slimbus, ter_mi2s, phase_flag7,
+			atest_usb23, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, EAST, ter_mi2s, phase_flag8, qdss_gpio8,
+			atest_usb22, NA, NA, NA, NA, NA, NA),
+	[76] = PINGROUP(76, EAST, ter_mi2s, phase_flag9, qdss_gpio9,
+			atest_usb21, NA, NA, NA, NA, NA, NA),
+	[77] = PINGROUP(77, EAST, ter_mi2s, phase_flag4, qdss_gpio10,
+			atest_usb20, NA, NA, NA, NA, NA, NA),
+	[78] = PINGROUP(78, EAST, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
+			NA, NA, NA),
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+			NA, NA, NA),
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, EAST, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, qspi_cs, tgu_ch3,
+			phase_flag10, NA, NA, NA, NA, NA),
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, qspi_cs,
+			mdp_vsync1, mdp_vsync2, mdp_vsync3, tgu_ch0,
+			phase_flag0, qdss_cti),
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, qspi0, tgu_ch1,
+			NA, qdss_cti, NA, NA, NA),
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, qspi1, vfr_1,
+			tgu_ch2, NA, NA, NA, NA),
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, qspi2, NA,
+			qdss_gpio13, NA, NA, NA, NA),
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, qspi3, NA, NA, NA,
+			NA, NA, NA),
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, qspi_clk, NA, NA,
+			NA, NA, NA, NA),
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+			NA, NA, NA, NA, NA),
+	[97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+			NA, NA),
+	[98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+			NA, NA, NA),
+	[99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+			 NA, NA, NA, NA, NA),
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[122] = PINGROUP(122, EAST, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[123] = PINGROUP(123, EAST, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[124] = PINGROUP(124, EAST, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[125] = PINGROUP(125, EAST, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[126] = PINGROUP(126, EAST, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31,
+			 NA, NA, NA, NA, NA),
+	[144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
+	[151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
+	[152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
+	[153] = UFS_RESET(ufs_reset, 0x99f000),
+};
+
+static const struct msm_dir_conn sdm845_dir_conn[] = {
+	{1, 510},
+	{3, 511},
+	{5, 512},
+	{10, 513},
+	{11, 514},
+	{20, 515},
+	{22, 516},
+	{24, 517},
+	{26, 518},
+	{30, 519},
+	{31, 632},
+	{32, 521},
+	{34, 522},
+	{36, 523},
+	{37, 524},
+	{38, 525},
+	{39, 526},
+	{40, 527},
+	{41, 630},
+	{43, 529},
+	{44, 530},
+	{46, 531},
+	{48, 532},
+	{49, 633 },
+	{52, 534},
+	{53, 535},
+	{54, 536},
+	{56, 537},
+	{57, 538},
+	{58, 539},
+	{59, 540},
+	{60, 541},
+	{61, 542},
+	{62, 543},
+	{63, 544},
+	{64, 545},
+	{66, 546},
+	{68, 547},
+	{71, 548},
+	{73, 549},
+	{77, 550},
+	{78, 551},
+	{79, 552},
+	{80, 553},
+	{84, 554},
+	{85, 555},
+	{86, 556},
+	{88, 557},
+	{89, 631},
+	{91, 559},
+	{92, 560},
+	{95, 561},
+	{96, 562},
+	{97, 563},
+	{101, 564},
+	{103, 565},
+	{104, 566},
+	{115, 570},
+	{116, 571},
+	{117, 572},
+	{118, 573},
+	{119, 609},
+	{120, 610},
+	{121, 611},
+	{122, 612},
+	{123, 613},
+	{124, 614},
+	{125, 615},
+	{126, 616},
+	{127, 617},
+	{128, 618},
+	{129, 619},
+	{130, 620},
+	{132, 621},
+	{133, 622},
+	{145, 623},
+	{0, 216},
+	{0, 215},
+	{0, 214},
+	{0, 213},
+	{0, 212},
+	{0, 211},
+	{0, 210},
+	{0, 209},
+};
+
+static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
+	.pins = sdm845_pins,
+	.npins = ARRAY_SIZE(sdm845_pins),
+	.functions = sdm845_functions,
+	.nfunctions = ARRAY_SIZE(sdm845_functions),
+	.groups = sdm845_groups,
+	.ngroups = ARRAY_SIZE(sdm845_groups),
+	.ngpios = 150,
+	.dir_conn = sdm845_dir_conn,
+	.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
+	.dir_conn_irq_base = 216,
+};
+
+static int sdm845_pinctrl_probe(struct platform_device *pdev)
+{
+	return msm_pinctrl_probe(pdev, &sdm845_pinctrl);
+}
+
+static const struct of_device_id sdm845_pinctrl_of_match[] = {
+	{ .compatible = "qcom,sdm845-pinctrl-v2", },
+	{ },
+};
+
+static struct platform_driver sdm845_pinctrl_driver = {
+	.driver = {
+		.name = "sdm845-v2-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = sdm845_pinctrl_of_match,
+	},
+	.probe = sdm845_pinctrl_probe,
+	.remove = msm_pinctrl_remove,
+};
+
+static int __init sdm845_pinctrl_init(void)
+{
+	return platform_driver_register(&sdm845_pinctrl_driver);
+}
+arch_initcall(sdm845_pinctrl_init);
+
+static void __exit sdm845_pinctrl_exit(void)
+{
+	platform_driver_unregister(&sdm845_pinctrl_driver);
+}
+module_exit(sdm845_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI sdm845-v2 pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, sdm845_pinctrl_of_match);
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index 8faabb0..ee5bd19 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -25,8 +25,10 @@
 		.ngroups = ARRAY_SIZE(fname##_groups),	\
 	}
 
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
 #define REG_SIZE 0x1000
-#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10)	\
 	{						\
 		.name = "gpio" #id,			\
 		.pins = gpio##id##_pins,		\
@@ -41,14 +43,17 @@
 			msm_mux_##f6,			\
 			msm_mux_##f7,			\
 			msm_mux_##f8,			\
-			msm_mux_##f9			\
+			msm_mux_##f9,			\
+			msm_mux_##f10			\
 		},					\
-		.nfuncs = 10,				\
-		.ctl_reg = REG_SIZE * id,		\
-		.io_reg = 0x4 + REG_SIZE * id,		\
-		.intr_cfg_reg = 0x8 + REG_SIZE * id,	\
-		.intr_status_reg = 0xc + REG_SIZE * id,	\
-		.intr_target_reg = 0x8 + REG_SIZE * id,	\
+		.nfuncs = 11,				\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.dir_conn_reg = (base == NORTH) ? base + 0xa5000 :\
+					base + 0xa8000, \
 		.mux_bit = 2,			\
 		.pull_bit = 0,			\
 		.drv_bit = 6,			\
@@ -63,6 +68,7 @@
 		.intr_polarity_bit = 1,		\
 		.intr_detection_bit = 2,	\
 		.intr_detection_width = 2,	\
+		.dir_conn_en_bit = 8,	        \
 	}
 
 #define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
@@ -115,9 +121,6 @@
 		.intr_detection_width = -1,		\
 	}
 
-static const u32 sdm845_tile_offsets[] = {0x500000, 0x900000, 0x100000};
-static u32 sdm845_pin_base[150];
-
 static const struct pinctrl_pin_desc sdm845_pins[] = {
 	PINCTRL_PIN(0, "GPIO_0"),
 	PINCTRL_PIN(1, "GPIO_1"),
@@ -1371,262 +1374,285 @@
  * Clients would not be able to request these dummy pin groups.
  */
 static const struct msm_pingroup sdm845_groups[] = {
-	[0] = PINGROUP(0, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[1] = PINGROUP(1, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[2] = PINGROUP(2, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[3] = PINGROUP(3, qup0, NA, NA, NA, NA, NA, NA, NA, NA),
-	[4] = PINGROUP(4, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[5] = PINGROUP(5, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[6] = PINGROUP(6, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA),
-	[7] = PINGROUP(7, qup9, ddr_bist, NA, atest_tsens2,
-		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA),
-	[8] = PINGROUP(8, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
-		       atest_usb13, ddr_pxi1, NA),
-	[9] = PINGROUP(9, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
-		       ddr_pxi1, NA, NA, NA),
-	[10] = PINGROUP(10, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
-			atest_usb11, ddr_pxi2, NA, NA, NA),
-	[11] = PINGROUP(11, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
-			atest_usb10, ddr_pxi2, NA, NA, NA),
-	[12] = PINGROUP(12, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
-			NA, NA, NA, NA),
-	[13] = PINGROUP(13, cam_mclk, pll_bypassnl, qdss_gpio0,
-			ddr_pxi3, NA, NA, NA, NA, NA),
-	[14] = PINGROUP(14, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
-			NA, NA, NA),
-	[15] = PINGROUP(15, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
-			NA, NA),
-	[16] = PINGROUP(16, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
-			NA, NA),
-	[17] = PINGROUP(17, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
-			NA, NA),
-	[18] = PINGROUP(18, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
-			NA, NA),
-	[19] = PINGROUP(19, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
-			NA, NA),
-	[20] = PINGROUP(20, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
-			NA, NA),
-	[21] = PINGROUP(21, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
-			NA, NA, NA),
-	[22] = PINGROUP(22, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
-			NA, NA, NA),
-	[23] = PINGROUP(23, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
-			NA, NA),
-	[24] = PINGROUP(24, cci_timer3, cci_async, qdss_gpio10, NA, NA,
-			NA, NA, NA, NA),
-	[25] = PINGROUP(25, cci_timer4, cci_async, qdss_gpio11, NA, NA,
-			NA, NA, NA, NA),
-	[26] = PINGROUP(26, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
-			NA, NA),
-	[27] = PINGROUP(27, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
-			NA),
-	[28] = PINGROUP(28, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
-			NA),
-	[29] = PINGROUP(29, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
-			NA, NA, NA),
-	[30] = PINGROUP(30, qup2, phase_flag2, qdss_gpio, NA, NA, NA, NA,
-			NA, NA),
-	[31] = PINGROUP(31, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[32] = PINGROUP(32, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[33] = PINGROUP(33, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[34] = PINGROUP(34, qup11, qup14, NA, NA, NA, NA, NA, NA, NA),
-	[35] = PINGROUP(35, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
-			NA, NA),
-	[36] = PINGROUP(36, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
-			NA, NA, NA, NA),
-	[37] = PINGROUP(37, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
-			NA),
-	[38] = PINGROUP(38, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA),
-	[39] = PINGROUP(39, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
-			NA),
-	[40] = PINGROUP(40, sd_write, tsif1_error, NA, NA, NA, NA, NA,
-			NA, NA),
-	[41] = PINGROUP(41, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA, NA),
-	[42] = PINGROUP(42, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA, NA),
-	[43] = PINGROUP(43, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
-			NA),
-	[44] = PINGROUP(44, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
-			NA),
-	[45] = PINGROUP(45, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[46] = PINGROUP(46, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[47] = PINGROUP(47, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[48] = PINGROUP(48, qup6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[49] = PINGROUP(49, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[50] = PINGROUP(50, qup12, NA, NA, NA, NA, NA, NA, NA, NA),
-	[51] = PINGROUP(51, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	[52] = PINGROUP(52, qup12, phase_flag16, qdss_cti, NA, NA, NA,
-			NA, NA, NA),
-	[53] = PINGROUP(53, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
-			NA),
-	[54] = PINGROUP(54, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
-			NA),
-	[55] = PINGROUP(55, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
-			NA),
-	[56] = PINGROUP(56, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
-			NA),
-	[57] = PINGROUP(57, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
-			NA, NA, NA),
-	[58] = PINGROUP(58, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
-			NA, NA, NA),
-	[59] = PINGROUP(59, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
-			NA, NA, NA),
-	[60] = PINGROUP(60, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
-			NA, NA, NA, NA),
-	[61] = PINGROUP(61, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
-			NA, NA, NA, NA),
-	[62] = PINGROUP(62, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+	[0] = PINGROUP(0, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[1] = PINGROUP(1, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[2] = PINGROUP(2, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[3] = PINGROUP(3, NORTH, qup0, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[4] = PINGROUP(4, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[5] = PINGROUP(5, NORTH, qup9, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[6] = PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, NA, NA, NA, NA, NA, NA,
+		       NA),
+	[7] = PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2,
+		       vsense_trigger, atest_usb1, ddr_pxi0, NA, NA, NA),
+	[8] = PINGROUP(8, NORTH, qup_l4, NA, ddr_bist, NA, NA, wlan1_adc1,
+		       atest_usb13, ddr_pxi1, NA, NA),
+	[9] = PINGROUP(9, NORTH, qup_l5, ddr_bist, NA, wlan1_adc0, atest_usb12,
+		       ddr_pxi1, NA, NA, NA, NA),
+	[10] = PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+			atest_usb11, ddr_pxi2, NA, NA, NA, NA),
+	[11] = PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+			atest_usb10, ddr_pxi2, NA, NA, NA, NA),
+	[12] = PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, NA,
 			NA, NA, NA, NA, NA),
-	[63] = PINGROUP(63, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+	[13] = PINGROUP(13, SOUTH, cam_mclk, pll_bypassnl, qdss_gpio0,
+			ddr_pxi3, NA, NA, NA, NA, NA, NA),
+	[14] = PINGROUP(14, SOUTH, cam_mclk, pll_reset, qdss_gpio1, NA, NA, NA,
 			NA, NA, NA, NA),
-	[64] = PINGROUP(64, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+	[15] = PINGROUP(15, SOUTH, cam_mclk, qdss_gpio2, NA, NA, NA, NA, NA,
 			NA, NA, NA),
-	[65] = PINGROUP(65, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[66] = PINGROUP(66, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
-			NA),
-	[67] = PINGROUP(67, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[68] = PINGROUP(68, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA),
-	[69] = PINGROUP(69, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
-			NA),
-	[70] = PINGROUP(70, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
-			NA, NA),
-	[71] = PINGROUP(71, lpass_slimbus, spkr_i2s, tsense_pwm1,
-			tsense_pwm2, NA, NA, NA, NA, NA),
-	[72] = PINGROUP(72, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
-			NA, NA),
-	[73] = PINGROUP(73, btfm_slimbus, atest_usb2, NA, NA, NA, NA, NA,
-			NA, NA),
-	[74] = PINGROUP(74, btfm_slimbus, ter_mi2s, phase_flag7,
-			atest_usb23, NA, NA, NA, NA, NA),
-	[75] = PINGROUP(75, ter_mi2s, phase_flag8, qdss_gpio8,
-			atest_usb22, NA, NA, NA, NA, NA),
-	[76] = PINGROUP(76, ter_mi2s, phase_flag9, qdss_gpio9,
-			atest_usb21, NA, NA, NA, NA, NA),
-	[77] = PINGROUP(77, ter_mi2s, phase_flag4, qdss_gpio10,
-			atest_usb20, NA, NA, NA, NA, NA),
-	[78] = PINGROUP(78, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
-			NA),
-	[79] = PINGROUP(79, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
-			NA, NA),
-	[80] = PINGROUP(80, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
-			NA, NA),
-	[81] = PINGROUP(81, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[82] = PINGROUP(82, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[83] = PINGROUP(83, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA),
-	[84] = PINGROUP(84, qup15, NA, NA, NA, NA, NA, NA, NA, NA),
-	[85] = PINGROUP(85, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[86] = PINGROUP(86, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[87] = PINGROUP(87, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[88] = PINGROUP(88, qup5, NA, NA, NA, NA, NA, NA, NA, NA),
-	[89] = PINGROUP(89, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+	[16] = PINGROUP(16, SOUTH, cam_mclk, qdss_gpio3, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[17] = PINGROUP(17, SOUTH, cci_i2c, qup1, qdss_gpio4, NA, NA, NA, NA,
+			NA, NA, NA),
+	[18] = PINGROUP(18, SOUTH, cci_i2c, qup1, NA, qdss_gpio5, NA, NA, NA,
+			NA, NA, NA),
+	[19] = PINGROUP(19, SOUTH, cci_i2c, qup1, NA, qdss_gpio6, NA, NA, NA,
+			NA, NA, NA),
+	[20] = PINGROUP(20, SOUTH, cci_i2c, qup1, NA, qdss_gpio7, NA, NA, NA,
+			NA, NA, NA),
+	[21] = PINGROUP(21, SOUTH, cci_timer0, gcc_gp2, qdss_gpio8, NA, NA, NA,
 			NA, NA, NA, NA),
-	[90] = PINGROUP(90, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
-			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti),
-	[91] = PINGROUP(91, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
-			qdss_cti, NA, NA, NA),
-	[92] = PINGROUP(92, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+	[22] = PINGROUP(22, SOUTH, cci_timer1, gcc_gp3, qdss_gpio, NA, NA, NA,
 			NA, NA, NA, NA),
-	[93] = PINGROUP(93, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
-			NA, NA, NA, NA),
-	[94] = PINGROUP(94, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
-			NA),
-	[95] = PINGROUP(95, tsif2_data, sdc41, qup7, NA, NA, NA, NA, NA,
-			NA),
-	[96] = PINGROUP(96, tsif2_sync, sdc40, qup7, phase_flag3, NA,
-			NA, NA, NA, NA),
-	[97] = PINGROUP(97, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
-			NA),
-	[98] = PINGROUP(98, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+	[23] = PINGROUP(23, SOUTH, cci_timer2, qdss_gpio9, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[24] = PINGROUP(24, SOUTH, cci_timer3, cci_async, qdss_gpio10, NA, NA,
+			NA, NA, NA, NA, NA),
+	[25] = PINGROUP(25, SOUTH, cci_timer4, cci_async, qdss_gpio11, NA, NA,
+			NA, NA, NA, NA, NA),
+	[26] = PINGROUP(26, SOUTH, cci_async, qdss_gpio12, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[27] = PINGROUP(27, NORTH, qup2, qdss_gpio13, NA, NA, NA, NA, NA, NA,
 			NA, NA),
-	[99] = PINGROUP(99, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+	[28] = PINGROUP(28, NORTH, qup2, qdss_gpio14, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[29] = PINGROUP(29, NORTH, qup2, NA, phase_flag1, qdss_gpio15, NA, NA,
+			NA, NA, NA, NA),
+	[30] = PINGROUP(30, NORTH, qup2, phase_flag2, qdss_gpio, NA, NA, NA,
+			NA, NA, NA, NA),
+	[31] = PINGROUP(31, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
 			NA),
-	[100] = PINGROUP(100, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[101] = PINGROUP(101, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[102] = PINGROUP(102, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[103] = PINGROUP(103, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
+	[32] = PINGROUP(32, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[33] = PINGROUP(33, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[34] = PINGROUP(34, NORTH, qup11, qup14, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[35] = PINGROUP(35, SOUTH, pci_e0, qup_l4, jitter_bist, NA, NA, NA, NA,
+			NA, NA, NA),
+	[36] = PINGROUP(36, SOUTH, pci_e0, qup_l5, pll_bist, NA, atest_tsens,
+			NA, NA, NA, NA, NA),
+	[37] = PINGROUP(37, SOUTH, qup_l6, agera_pll, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[38] = PINGROUP(38, NORTH, usb_phy, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, NORTH, lpass_slimbus, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[40] = PINGROUP(40, SOUTH, sd_write, tsif1_error, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[41] = PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, NA, NA, NA, NA, NA,
+			NA, NA),
+	[42] = PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[43] = PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, NA, NA, NA, NA, NA,
+			NA, NA),
+	[44] = PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, NA, NA, NA, NA, NA,
+			NA, NA),
+	[45] = PINGROUP(45, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, NORTH, qup6, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NORTH, qup12, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NORTH, qup12, qdss_cti, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[52] = PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, NA, NA, NA,
+			NA, NA, NA, NA),
+	[53] = PINGROUP(53, NORTH, qup10, phase_flag11, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[54] = PINGROUP(54, NORTH, qup10, NA, phase_flag12, NA, NA, NA, NA, NA,
+			NA, NA),
+	[55] = PINGROUP(55, NORTH, qup10, phase_flag13, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[56] = PINGROUP(56, NORTH, qup10, phase_flag17, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[57] = PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, NA, NA, NA,
+			NA, NA, NA, NA),
+	[58] = PINGROUP(58, NORTH, qua_mi2s, gcc_gp2, phase_flag19, NA, NA, NA,
+			NA, NA, NA, NA),
+	[59] = PINGROUP(59, NORTH, qua_mi2s, gcc_gp3, phase_flag20, NA, NA, NA,
+			NA, NA, NA, NA),
+	[60] = PINGROUP(60, NORTH, qua_mi2s, cri_trng0, phase_flag21, NA, NA,
+			NA, NA, NA, NA, NA),
+	[61] = PINGROUP(61, NORTH, qua_mi2s, cri_trng1, phase_flag22, NA, NA,
+			NA, NA, NA, NA, NA),
+	[62] = PINGROUP(62, NORTH, qua_mi2s, cri_trng, phase_flag23, qdss_cti,
+			NA, NA, NA, NA, NA, NA),
+	[63] = PINGROUP(63, NORTH, qua_mi2s, NA, phase_flag24, qdss_cti, NA,
+			NA, NA, NA, NA, NA),
+	[64] = PINGROUP(64, NORTH, pri_mi2s, sp_cmu, phase_flag25, NA, NA, NA,
+			NA, NA, NA, NA),
+	[65] = PINGROUP(65, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[66] = PINGROUP(66, NORTH, pri_mi2s_ws, qup8, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[67] = PINGROUP(67, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[68] = PINGROUP(68, NORTH, pri_mi2s, qup8, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[69] = PINGROUP(69, NORTH, spkr_i2s, audio_ref, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[70] = PINGROUP(70, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[71] = PINGROUP(71, NORTH, lpass_slimbus, spkr_i2s, tsense_pwm1,
+			tsense_pwm2, NA, NA, NA, NA, NA, NA),
+	[72] = PINGROUP(72, NORTH, lpass_slimbus, spkr_i2s, NA, NA, NA, NA, NA,
+			NA, NA, NA),
+	[73] = PINGROUP(73, NORTH, btfm_slimbus, atest_usb2, NA, NA, NA, NA,
+			NA, NA, NA, NA),
+	[74] = PINGROUP(74, NORTH, btfm_slimbus, ter_mi2s, phase_flag7,
+			atest_usb23, NA, NA, NA, NA, NA, NA),
+	[75] = PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8,
+			atest_usb22, NA, NA, NA, NA, NA, NA),
+	[76] = PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9,
+			atest_usb21, NA, NA, NA, NA, NA, NA),
+	[77] = PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10,
+			atest_usb20, NA, NA, NA, NA, NA, NA),
+	[78] = PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[79] = PINGROUP(79, NORTH, sec_mi2s, NA, NA, qdss_gpio11, NA, NA, NA,
+			NA, NA, NA),
+	[80] = PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, NA, NA, NA, NA,
+			NA, NA, NA),
+	[81] = PINGROUP(81, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[82] = PINGROUP(82, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[83] = PINGROUP(83, NORTH, sec_mi2s, qup15, NA, NA, NA, NA, NA, NA, NA,
+			NA),
+	[84] = PINGROUP(84, NORTH, qup15, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, SOUTH, qup5, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, NA,
+			NA, NA, NA, NA, NA),
+	[90] = PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1,
+			mdp_vsync2, mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti,
+			NA),
+	[91] = PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA,
+			qdss_cti, NA, NA, NA, NA),
+	[92] = PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2,
+			NA, NA, NA, NA, NA),
+	[93] = PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
+			NA, NA, NA, NA, NA),
+	[94] = PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[95] = PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, NA, NA, NA, NA, NA,
+			NA, NA),
+	[96] = PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, NA,
+			NA, NA, NA, NA, NA),
+	[97] = PINGROUP(97, NORTH, NA, NA, mdp_vsync, ldo_en, NA, NA, NA, NA,
+			NA, NA),
+	[98] = PINGROUP(98, NORTH, NA, mdp_vsync, ldo_update, NA, NA, NA, NA,
+			NA, NA, NA),
+	[99] = PINGROUP(99, NORTH, phase_flag14, NA, NA, NA, NA, NA, NA, NA,
+			NA, NA),
+	[100] = PINGROUP(100, NORTH, phase_flag15, NA, NA, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[104] = PINGROUP(104, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[105] = PINGROUP(105, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+	[101] = PINGROUP(101, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[102] = PINGROUP(102, NORTH, pci_e1, prng_rosc, NA, NA, NA, NA, NA, NA,
 			 NA, NA),
-	[106] = PINGROUP(106, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
-			 NA, NA),
-	[107] = PINGROUP(107, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
-			 NA, NA),
-	[108] = PINGROUP(108, uim2_present, qup13, NA, NA, NA, NA, NA,
-			 NA, NA),
-	[109] = PINGROUP(109, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA),
-	[110] = PINGROUP(110, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
-	[111] = PINGROUP(111, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[112] = PINGROUP(112, uim1_present, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[113] = PINGROUP(113, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[114] = PINGROUP(114, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
-			 NA),
-	[115] = PINGROUP(115, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
-			 NA),
-	[116] = PINGROUP(116, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[117] = PINGROUP(117, NA, qdss_gpio0, atest_char, NA, NA, NA,
+	[103] = PINGROUP(103, NORTH, pci_e1, phase_flag5, NA, NA, NA, NA, NA,
 			 NA, NA, NA),
-	[118] = PINGROUP(118, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+	[104] = PINGROUP(104, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[105] = PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[106] = PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[107] = PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[108] = PINGROUP(108, NORTH, uim2_present, qup13, NA, NA, NA, NA, NA,
+			 NA, NA, NA),
+	[109] = PINGROUP(109, NORTH, uim1_data, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[110] = PINGROUP(110, NORTH, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[111] = PINGROUP(111, NORTH, uim1_reset, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[112] = PINGROUP(112, NORTH, uim1_present, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[113] = PINGROUP(113, NORTH, uim_batt, edp_hot, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[114] = PINGROUP(114, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[115] = PINGROUP(115, NORTH, NA, nav_pps, nav_pps, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[116] = PINGROUP(116, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[117] = PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, NA, NA, NA,
 			 NA, NA, NA, NA),
-	[119] = PINGROUP(119, NA, qdss_gpio2, atest_char2, NA, NA, NA,
-			 NA, NA, NA),
-	[120] = PINGROUP(120, NA, qdss_gpio3, atest_char1, NA, NA, NA,
-			 NA, NA, NA),
-	[121] = PINGROUP(121, NA, qdss_gpio4, atest_char0, NA, NA, NA,
-			 NA, NA, NA),
-	[122] = PINGROUP(122, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA, NA),
-	[123] = PINGROUP(123, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
-			 NA),
-	[124] = PINGROUP(124, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
-			 NA),
-	[125] = PINGROUP(125, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA),
-	[126] = PINGROUP(126, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[127] = PINGROUP(127, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[128] = PINGROUP(128, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[129] = PINGROUP(129, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[130] = PINGROUP(130, qlink_request, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[131] = PINGROUP(131, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[132] = PINGROUP(132, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[133] = PINGROUP(133, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[134] = PINGROUP(134, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[135] = PINGROUP(135, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[136] = PINGROUP(136, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[137] = PINGROUP(137, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
-			 NA),
-	[138] = PINGROUP(138, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
-			 NA),
-	[139] = PINGROUP(139, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[140] = PINGROUP(140, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
-			 NA),
-	[141] = PINGROUP(141, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[142] = PINGROUP(142, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
-			 NA),
-	[143] = PINGROUP(143, NA, nav_pps, nav_pps, NA, phase_flag31,
+	[118] = PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3, NA,
+			 NA, NA, NA, NA, NA),
+	[119] = PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, NA, NA, NA,
 			 NA, NA, NA, NA),
-	[144] = PINGROUP(144, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[145] = PINGROUP(145, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA),
-	[146] = PINGROUP(146, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[147] = PINGROUP(147, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[148] = PINGROUP(148, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	[149] = PINGROUP(149, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[120] = PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[121] = PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, NA, NA, NA,
+			 NA, NA, NA, NA),
+	[122] = PINGROUP(122, NORTH, NA, qdss_gpio5, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[123] = PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[124] = PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[125] = PINGROUP(125, NORTH, qup_l6, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[126] = PINGROUP(126, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[127] = PINGROUP(127, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[128] = PINGROUP(128, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[129] = PINGROUP(129, NORTH, nav_pps, nav_pps, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[130] = PINGROUP(130, NORTH, qlink_request, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[131] = PINGROUP(131, NORTH, qlink_enable, NA, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[132] = PINGROUP(132, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[133] = PINGROUP(133, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[134] = PINGROUP(134, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[135] = PINGROUP(135, NORTH, NA, pa_indicator, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[136] = PINGROUP(136, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[137] = PINGROUP(137, NORTH, NA, NA, phase_flag26, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[138] = PINGROUP(138, NORTH, NA, NA, phase_flag27, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[139] = PINGROUP(139, NORTH, NA, phase_flag28, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[140] = PINGROUP(140, NORTH, NA, NA, phase_flag6, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[141] = PINGROUP(141, NORTH, NA, phase_flag29, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[142] = PINGROUP(142, NORTH, NA, phase_flag30, NA, NA, NA, NA, NA, NA,
+			 NA, NA),
+	[143] = PINGROUP(143, NORTH, NA, nav_pps, nav_pps, NA, phase_flag31,
+			 NA, NA, NA, NA, NA),
+	[144] = PINGROUP(144, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[145] = PINGROUP(145, NORTH, mss_lte, NA, NA, NA, NA, NA, NA, NA, NA,
+			 NA),
+	[146] = PINGROUP(146, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[147] = PINGROUP(147, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[148] = PINGROUP(148, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[149] = PINGROUP(149, NORTH, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA),
 	[150] = SDC_QDSD_PINGROUP(sdc2_clk, 0x99a000, 14, 6),
 	[151] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x99a000, 11, 3),
 	[152] = SDC_QDSD_PINGROUP(sdc2_data, 0x99a000, 9, 0),
 	[153] = UFS_RESET(ufs_reset, 0x99f000),
 };
 
-static const struct msm_dir_conn sdm845_dir_conn[] = {
+static struct msm_dir_conn sdm845_dir_conn[] = {
 	{1, 510},
 	{3, 511},
 	{5, 512},
@@ -1637,7 +1663,7 @@
 	{24, 517},
 	{26, 518},
 	{30, 519},
-	{31, 639},
+	{31, 632},
 	{32, 521},
 	{34, 522},
 	{36, 523},
@@ -1645,12 +1671,12 @@
 	{38, 525},
 	{39, 526},
 	{40, 527},
-	{41, 637},
+	{41, 630},
 	{43, 529},
 	{44, 530},
 	{46, 531},
 	{48, 532},
-	{49, 640},
+	{49, 633},
 	{52, 534},
 	{53, 535},
 	{54, 536},
@@ -1675,7 +1701,7 @@
 	{85, 555},
 	{86, 556},
 	{88, 557},
-	{89, 638},
+	{89, 631},
 	{91, 559},
 	{92, 560},
 	{95, 561},
@@ -1695,6 +1721,7 @@
 	{123, 613},
 	{124, 614},
 	{125, 615},
+	{126, 616},
 	{127, 617},
 	{128, 618},
 	{129, 619},
@@ -1702,6 +1729,14 @@
 	{132, 621},
 	{133, 622},
 	{145, 623},
+	{0, 216},
+	{0, 215},
+	{0, 214},
+	{0, 213},
+	{0, 212},
+	{0, 211},
+	{0, 210},
+	{0, 209},
 };
 
 static const struct msm_pinctrl_soc_data sdm845_pinctrl = {
@@ -1714,10 +1749,7 @@
 	.ngpios = 150,
 	.dir_conn = sdm845_dir_conn,
 	.n_dir_conns = ARRAY_SIZE(sdm845_dir_conn),
-	.tile_offsets = sdm845_tile_offsets,
-	.n_tile_offsets = ARRAY_SIZE(sdm845_tile_offsets),
-	.pin_base = sdm845_pin_base,
-	.reg_size = REG_SIZE,
+	.dir_conn_irq_base = 216,
 };
 
 static int sdm845_pinctrl_probe(struct platform_device *pdev)
diff --git a/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
index 4a21eb6..6ceb39a 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdxpoorwills.c
@@ -85,6 +85,32 @@
 		.intr_enable_bit = -1,			\
 		.intr_status_bit = -1,			\
 		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+
+#define UFS_RESET(pg_name, offset)				\
+	{					        \
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = offset,			\
+		.io_reg = offset + 0x4,			\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = 3,				\
+		.drv_bit = 0,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = 0,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
 		.intr_polarity_bit = -1,		\
 		.intr_detection_bit = -1,		\
 		.intr_detection_width = -1,		\
@@ -190,12 +216,13 @@
 	PINCTRL_PIN(97, "GPIO_97"),
 	PINCTRL_PIN(98, "GPIO_98"),
 	PINCTRL_PIN(99, "GPIO_99"),
-	PINCTRL_PIN(100, "SDC1_CLK"),
-	PINCTRL_PIN(101, "SDC1_CMD"),
-	PINCTRL_PIN(102, "SDC1_DATA"),
-	PINCTRL_PIN(103, "SDC2_CLK"),
-	PINCTRL_PIN(104, "SDC2_CMD"),
-	PINCTRL_PIN(105, "SDC2_DATA"),
+	PINCTRL_PIN(100, "SDC1_RCLK"),
+	PINCTRL_PIN(101, "SDC1_CLK"),
+	PINCTRL_PIN(102, "SDC1_CMD"),
+	PINCTRL_PIN(103, "SDC1_DATA"),
+	PINCTRL_PIN(104, "SDC2_CLK"),
+	PINCTRL_PIN(105, "SDC2_CMD"),
+	PINCTRL_PIN(106, "SDC2_DATA"),
 };
 
 #define DECLARE_MSM_GPIO_PINS(pin) \
@@ -301,21 +328,22 @@
 DECLARE_MSM_GPIO_PINS(98);
 DECLARE_MSM_GPIO_PINS(99);
 
-static const unsigned int sdc1_clk_pins[] = { 100 };
-static const unsigned int sdc1_cmd_pins[] = { 101 };
-static const unsigned int sdc1_data_pins[] = { 102 };
-static const unsigned int sdc2_clk_pins[] = { 103 };
-static const unsigned int sdc2_cmd_pins[] = { 104 };
-static const unsigned int sdc2_data_pins[] = { 105 };
+static const unsigned int sdc1_rclk_pins[] = { 100 };
+static const unsigned int sdc1_clk_pins[] = { 101 };
+static const unsigned int sdc1_cmd_pins[] = { 102 };
+static const unsigned int sdc1_data_pins[] = { 103 };
+static const unsigned int sdc2_clk_pins[] = { 104 };
+static const unsigned int sdc2_cmd_pins[] = { 105 };
+static const unsigned int sdc2_data_pins[] = { 106 };
 
 enum sdxpoorwills_functions {
-	msm_mux_qdss_stm31,
-	msm_mux_blsp_uart1,
-	msm_mux_gpio,
 	msm_mux_uim2_data,
+	msm_mux_gpio,
+	msm_mux_qdss_stm31,
 	msm_mux_ebi0_wrcdc,
 	msm_mux_uim2_present,
 	msm_mux_qdss_stm30,
+	msm_mux_blsp_uart1,
 	msm_mux_uim2_reset,
 	msm_mux_blsp_i2c1,
 	msm_mux_qdss_stm29,
@@ -340,14 +368,22 @@
 	msm_mux_blsp_i2c3,
 	msm_mux_gcc_gp3,
 	msm_mux_qdss_stm19,
-	msm_mux_qdss12,
+	msm_mux_qdss4,
 	msm_mux_qdss_stm18,
-	msm_mux_qdss13,
+	msm_mux_qdss5,
 	msm_mux_qdss_stm17,
-	msm_mux_qdss14,
+	msm_mux_qdss6,
 	msm_mux_bimc_dte0,
 	msm_mux_native_tsens,
-	msm_mux_vsense_trigger,
+	msm_mux_qdss_stm16,
+	msm_mux_qdss7,
+	msm_mux_bimc_dte1,
+	msm_mux_sec_mi2s,
+	msm_mux_blsp_spi4,
+	msm_mux_blsp_uart4,
+	msm_mux_qdss_cti,
+	msm_mux_qdss_stm27,
+	msm_mux_qdss8,
 	msm_mux_qdss_stm26,
 	msm_mux_qdss9,
 	msm_mux_blsp_i2c4,
@@ -358,26 +394,19 @@
 	msm_mux_gcc_gp2,
 	msm_mux_qdss_stm24,
 	msm_mux_qdss11,
-	msm_mux_qdss_stm16,
-	msm_mux_qdss15,
-	msm_mux_bimc_dte1,
-	msm_mux_sec_mi2s,
-	msm_mux_blsp_spi4,
-	msm_mux_blsp_uart4,
-	msm_mux_qdss_cti,
-	msm_mux_qdss_stm27,
-	msm_mux_qdss8,
 	msm_mux_ebi2_a,
-	msm_mux_qdss_stm3,
 	msm_mux_ebi2_lcd,
-	msm_mux_qdss_stm2,
 	msm_mux_pll_bist,
-	msm_mux_qdss_stm1,
-	msm_mux_qdss_stm0,
 	msm_mux_adsp_ext,
-	msm_mux_epm1,
+	msm_mux_qdss_stm11,
 	msm_mux_m_voc,
+	msm_mux_qdss_stm10,
 	msm_mux_native_char,
+	msm_mux_native_char3,
+	msm_mux_nav_pps,
+	msm_mux_nav_dr,
+	msm_mux_native_char2,
+	msm_mux_native_tsense,
 	msm_mux_native_char1,
 	msm_mux_pa_indicator,
 	msm_mux_qdss_traceclk,
@@ -386,92 +415,69 @@
 	msm_mux_qlink_req,
 	msm_mux_pll_test,
 	msm_mux_cri_trng,
-	msm_mux_wmss_reset,
-	msm_mux_native_char3,
-	msm_mux_nav_pps,
-	msm_mux_nav_dr,
-	msm_mux_native_char2,
-	msm_mux_native_tsense,
 	msm_mux_prng_rosc,
 	msm_mux_cri_trng0,
 	msm_mux_cri_trng1,
 	msm_mux_pll_ref,
 	msm_mux_coex_uart,
-	msm_mux_qdss_stm11,
-	msm_mux_qdss_stm10,
-	msm_mux_ddr_pxi0,
-	msm_mux_ap2mdm_status,
+	msm_mux_qdss_tracectl,
 	msm_mux_ddr_bist,
-	msm_mux_mdm2ap_status,
-	msm_mux_ap2mdm_err,
-	msm_mux_mdm2ap_err,
-	msm_mux_ap2mdm_vdd,
-	msm_mux_mdm2ap_vdd,
-	msm_mux_ap2mdm_wake,
-	msm_mux_pciehost_rst,
 	msm_mux_blsp_spi1,
-	msm_mux_qdss_stm14,
-	msm_mux_pcie_wake,
-	msm_mux_mdm2ap_wake,
 	msm_mux_pci_e,
+	msm_mux_tgu_ch0,
+	msm_mux_pcie_clkreq,
+	msm_mux_qdss_stm15,
+	msm_mux_qdss_stm14,
 	msm_mux_qdss_stm13,
+	msm_mux_mgpi_clk,
+	msm_mux_qdss_stm12,
+	msm_mux_qdss_stm9,
 	msm_mux_i2s_mclk,
 	msm_mux_audio_ref,
 	msm_mux_ldo_update,
 	msm_mux_qdss_stm8,
 	msm_mux_qdss_stm7,
-	msm_mux_qdss4,
-	msm_mux_tgu_ch0,
-	msm_mux_pcie_clkreq,
-	msm_mux_qdss_stm9,
-	msm_mux_qdss_stm15,
-	msm_mux_mgpi_clk,
-	msm_mux_qdss_stm12,
-	msm_mux_qdss_tracectl,
-	msm_mux_atest_char,
+	msm_mux_qdss12,
 	msm_mux_qdss_stm6,
-	msm_mux_qdss5,
-	msm_mux_atest_char3,
+	msm_mux_qdss13,
 	msm_mux_qdss_stm5,
-	msm_mux_qdss6,
-	msm_mux_atest_char2,
+	msm_mux_qdss14,
 	msm_mux_qdss_stm4,
-	msm_mux_qdss7,
-	msm_mux_atest_char1,
+	msm_mux_qdss15,
 	msm_mux_uim1_data,
-	msm_mux_atest_char0,
+	msm_mux_qdss_stm3,
 	msm_mux_uim1_present,
+	msm_mux_qdss_stm2,
 	msm_mux_uim1_reset,
+	msm_mux_qdss_stm1,
 	msm_mux_uim1_clk,
+	msm_mux_qdss_stm0,
 	msm_mux_dbg_out,
 	msm_mux_gcc_plltest,
-	msm_mux_usb2phy_ac,
 	msm_mux_NA,
 };
 
-static const char * const qdss_stm31_groups[] = {
+static const char * const uim2_data_groups[] = {
 	"gpio0",
 };
-static const char * const blsp_uart1_groups[] = {
-	"gpio0", "gpio1", "gpio2", "gpio3", "gpio20", "gpio21", "gpio22",
-	"gpio23",
-};
 static const char * const gpio_groups[] = {
 	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
 	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
 	"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
-	"gpio22", "gpio23", "gpio24", "gpio26", "gpio27", "gpio28", "gpio29",
-	"gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35", "gpio36",
-	"gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42", "gpio43",
-	"gpio44", "gpio45", "gpio54", "gpio55", "gpio56", "gpio58", "gpio59",
-	"gpio60", "gpio61", "gpio62", "gpio63", "gpio64", "gpio65", "gpio66",
-	"gpio67", "gpio68", "gpio69", "gpio70", "gpio71", "gpio72", "gpio73",
-	"gpio74", "gpio75", "gpio76", "gpio77", "gpio78", "gpio79", "gpio80",
-	"gpio81", "gpio82", "gpio83", "gpio84", "gpio85", "gpio86", "gpio87",
-	"gpio88", "gpio89", "gpio90", "gpio91", "gpio92", "gpio93", "gpio94",
-	"gpio95", "gpio96", "gpio97", "gpio98", "gpio99",
+	"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+	"gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+	"gpio43", "gpio44", "gpio45", "gpio46", "gpio47", "gpio48", "gpio49",
+	"gpio50", "gpio51", "gpio52", "gpio52", "gpio53", "gpio53", "gpio54",
+	"gpio55", "gpio56", "gpio57", "gpio58", "gpio59", "gpio60", "gpio61",
+	"gpio62", "gpio63", "gpio64", "gpio65", "gpio66", "gpio67", "gpio68",
+	"gpio69", "gpio70", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75",
+	"gpio76", "gpio77", "gpio78", "gpio79", "gpio80", "gpio81", "gpio82",
+	"gpio83", "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89",
+	"gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96",
+	"gpio97", "gpio98", "gpio99",
 };
-static const char * const uim2_data_groups[] = {
+static const char * const qdss_stm31_groups[] = {
 	"gpio0",
 };
 static const char * const ebi0_wrcdc_groups[] = {
@@ -483,6 +489,10 @@
 static const char * const qdss_stm30_groups[] = {
 	"gpio1",
 };
+static const char * const blsp_uart1_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio20", "gpio21", "gpio22",
+	"gpio23",
+};
 static const char * const uim2_reset_groups[] = {
 	"gpio2",
 };
@@ -557,19 +567,19 @@
 static const char * const qdss_stm19_groups[] = {
 	"gpio12",
 };
-static const char * const qdss12_groups[] = {
+static const char * const qdss4_groups[] = {
 	"gpio12",
 };
 static const char * const qdss_stm18_groups[] = {
 	"gpio13",
 };
-static const char * const qdss13_groups[] = {
+static const char * const qdss5_groups[] = {
 	"gpio13",
 };
 static const char * const qdss_stm17_groups[] = {
 	"gpio14",
 };
-static const char * const qdss14_groups[] = {
+static const char * const qdss6_groups[] = {
 	"gpio14",
 };
 static const char * const bimc_dte0_groups[] = {
@@ -578,8 +588,36 @@
 static const char * const native_tsens_groups[] = {
 	"gpio14",
 };
-static const char * const vsense_trigger_groups[] = {
-	"gpio14",
+static const char * const qdss_stm16_groups[] = {
+	"gpio15",
+};
+static const char * const qdss7_groups[] = {
+	"gpio15",
+};
+static const char * const bimc_dte1_groups[] = {
+	"gpio15", "gpio60",
+};
+static const char * const sec_mi2s_groups[] = {
+	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+	"gpio23",
+};
+static const char * const blsp_spi4_groups[] = {
+	"gpio16", "gpio17", "gpio18", "gpio19", "gpio52", "gpio62", "gpio71",
+};
+static const char * const blsp_uart4_groups[] = {
+	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
+	"gpio23",
+};
+static const char * const qdss_cti_groups[] = {
+	"gpio16", "gpio16", "gpio17", "gpio17", "gpio22", "gpio22", "gpio23",
+	"gpio23", "gpio54", "gpio54", "gpio55", "gpio55", "gpio59", "gpio61",
+	"gpio88", "gpio88", "gpio89", "gpio89",
+};
+static const char * const qdss_stm27_groups[] = {
+	"gpio16",
+};
+static const char * const qdss8_groups[] = {
+	"gpio16",
 };
 static const char * const qdss_stm26_groups[] = {
 	"gpio17",
@@ -611,70 +649,45 @@
 static const char * const qdss11_groups[] = {
 	"gpio19",
 };
-static const char * const qdss_stm16_groups[] = {
-	"gpio15",
-};
-static const char * const qdss15_groups[] = {
-	"gpio15",
-};
-static const char * const bimc_dte1_groups[] = {
-	"gpio15", "gpio60",
-};
-static const char * const sec_mi2s_groups[] = {
-	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
-	"gpio23",
-};
-static const char * const blsp_spi4_groups[] = {
-	"gpio16", "gpio17", "gpio18", "gpio19", "gpio52", "gpio62", "gpio71",
-};
-static const char * const blsp_uart4_groups[] = {
-	"gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", "gpio22",
-	"gpio23",
-};
-static const char * const qdss_cti_groups[] = {
-	"gpio16", "gpio16", "gpio17", "gpio17", "gpio22", "gpio22", "gpio23",
-	"gpio23", "gpio54", "gpio54", "gpio55", "gpio55", "gpio59", "gpio61",
-	"gpio88", "gpio88", "gpio89", "gpio89",
-};
-static const char * const qdss_stm27_groups[] = {
-	"gpio16",
-};
-static const char * const qdss8_groups[] = {
-	"gpio16",
-};
 static const char * const ebi2_a_groups[] = {
 	"gpio20",
 };
-static const char * const qdss_stm3_groups[] = {
-	"gpio20",
-};
 static const char * const ebi2_lcd_groups[] = {
 	"gpio21", "gpio22", "gpio23",
 };
-static const char * const qdss_stm2_groups[] = {
-	"gpio21",
-};
 static const char * const pll_bist_groups[] = {
 	"gpio22",
 };
-static const char * const qdss_stm1_groups[] = {
-	"gpio22",
-};
-static const char * const qdss_stm0_groups[] = {
-	"gpio23",
-};
 static const char * const adsp_ext_groups[] = {
 	"gpio24", "gpio25",
 };
-static const char * const epm1_groups[] = {
-	"gpio25",
+static const char * const qdss_stm11_groups[] = {
+	"gpio24",
 };
 static const char * const m_voc_groups[] = {
 	"gpio25", "gpio46", "gpio59", "gpio61",
 };
+static const char * const qdss_stm10_groups[] = {
+	"gpio25",
+};
 static const char * const native_char_groups[] = {
 	"gpio26",
 };
+static const char * const native_char3_groups[] = {
+	"gpio28",
+};
+static const char * const nav_pps_groups[] = {
+	"gpio29", "gpio42", "gpio62",
+};
+static const char * const nav_dr_groups[] = {
+	"gpio29", "gpio42", "gpio62",
+};
+static const char * const native_char2_groups[] = {
+	"gpio29",
+};
+static const char * const native_tsense_groups[] = {
+	"gpio29",
+};
 static const char * const native_char1_groups[] = {
 	"gpio32",
 };
@@ -699,24 +712,6 @@
 static const char * const cri_trng_groups[] = {
 	"gpio36",
 };
-static const char * const wmss_reset_groups[] = {
-	"gpio28",
-};
-static const char * const native_char3_groups[] = {
-	"gpio28",
-};
-static const char * const nav_pps_groups[] = {
-	"gpio29", "gpio42", "gpio62",
-};
-static const char * const nav_dr_groups[] = {
-	"gpio29", "gpio42", "gpio62",
-};
-static const char * const native_char2_groups[] = {
-	"gpio29",
-};
-static const char * const native_tsense_groups[] = {
-	"gpio29",
-};
 static const char * const prng_rosc_groups[] = {
 	"gpio38",
 };
@@ -732,59 +727,41 @@
 static const char * const coex_uart_groups[] = {
 	"gpio44", "gpio45",
 };
-static const char * const qdss_stm11_groups[] = {
+static const char * const qdss_tracectl_groups[] = {
 	"gpio44",
 };
-static const char * const qdss_stm10_groups[] = {
-	"gpio45",
-};
-static const char * const ddr_pxi0_groups[] = {
-	"gpio45", "gpio46",
-};
-static const char * const ap2mdm_status_groups[] = {
-	"gpio46",
-};
 static const char * const ddr_bist_groups[] = {
 	"gpio46", "gpio47", "gpio48", "gpio49",
 };
-static const char * const mdm2ap_status_groups[] = {
-	"gpio47",
-};
-static const char * const ap2mdm_err_groups[] = {
-	"gpio48",
-};
-static const char * const mdm2ap_err_groups[] = {
-	"gpio49",
-};
-static const char * const ap2mdm_vdd_groups[] = {
-	"gpio50",
-};
-static const char * const mdm2ap_vdd_groups[] = {
-	"gpio51",
-};
-static const char * const ap2mdm_wake_groups[] = {
-	"gpio52",
-};
-static const char * const pciehost_rst_groups[] = {
-	"gpio52",
-};
 static const char * const blsp_spi1_groups[] = {
 	"gpio52", "gpio62", "gpio71", "gpio72", "gpio73", "gpio74", "gpio75",
 };
-static const char * const qdss_stm14_groups[] = {
-	"gpio52",
-};
-static const char * const pcie_wake_groups[] = {
-	"gpio53",
-};
-static const char * const mdm2ap_wake_groups[] = {
-	"gpio53",
-};
 static const char * const pci_e_groups[] = {
-	"gpio53", "gpio57",
+	"gpio53",
+};
+static const char * const tgu_ch0_groups[] = {
+	"gpio55",
+};
+static const char * const pcie_clkreq_groups[] = {
+	"gpio56",
+};
+static const char * const qdss_stm15_groups[] = {
+	"gpio57",
+};
+static const char * const qdss_stm14_groups[] = {
+	"gpio58",
 };
 static const char * const qdss_stm13_groups[] = {
-	"gpio53",
+	"gpio59",
+};
+static const char * const mgpi_clk_groups[] = {
+	"gpio60", "gpio71",
+};
+static const char * const qdss_stm12_groups[] = {
+	"gpio60",
+};
+static const char * const qdss_stm9_groups[] = {
+	"gpio61",
 };
 static const char * const i2s_mclk_groups[] = {
 	"gpio62",
@@ -801,93 +778,66 @@
 static const char * const qdss_stm7_groups[] = {
 	"gpio63",
 };
-static const char * const qdss4_groups[] = {
-	"gpio63",
-};
-static const char * const tgu_ch0_groups[] = {
-	"gpio55",
-};
-static const char * const pcie_clkreq_groups[] = {
-	"gpio56",
-};
-static const char * const qdss_stm9_groups[] = {
-	"gpio56",
-};
-static const char * const qdss_stm15_groups[] = {
-	"gpio57",
-};
-static const char * const mgpi_clk_groups[] = {
-	"gpio60", "gpio71",
-};
-static const char * const qdss_stm12_groups[] = {
-	"gpio60",
-};
-static const char * const qdss_tracectl_groups[] = {
-	"gpio60",
-};
-static const char * const atest_char_groups[] = {
+static const char * const qdss12_groups[] = {
 	"gpio63",
 };
 static const char * const qdss_stm6_groups[] = {
 	"gpio64",
 };
-static const char * const qdss5_groups[] = {
-	"gpio64",
-};
-static const char * const atest_char3_groups[] = {
+static const char * const qdss13_groups[] = {
 	"gpio64",
 };
 static const char * const qdss_stm5_groups[] = {
 	"gpio65",
 };
-static const char * const qdss6_groups[] = {
-	"gpio65",
-};
-static const char * const atest_char2_groups[] = {
+static const char * const qdss14_groups[] = {
 	"gpio65",
 };
 static const char * const qdss_stm4_groups[] = {
 	"gpio66",
 };
-static const char * const qdss7_groups[] = {
-	"gpio66",
-};
-static const char * const atest_char1_groups[] = {
+static const char * const qdss15_groups[] = {
 	"gpio66",
 };
 static const char * const uim1_data_groups[] = {
 	"gpio67",
 };
-static const char * const atest_char0_groups[] = {
+static const char * const qdss_stm3_groups[] = {
 	"gpio67",
 };
 static const char * const uim1_present_groups[] = {
 	"gpio68",
 };
+static const char * const qdss_stm2_groups[] = {
+	"gpio68",
+};
 static const char * const uim1_reset_groups[] = {
 	"gpio69",
 };
+static const char * const qdss_stm1_groups[] = {
+	"gpio69",
+};
 static const char * const uim1_clk_groups[] = {
 	"gpio70",
 };
+static const char * const qdss_stm0_groups[] = {
+	"gpio70",
+};
 static const char * const dbg_out_groups[] = {
 	"gpio71",
 };
 static const char * const gcc_plltest_groups[] = {
 	"gpio73", "gpio74",
 };
-static const char * const usb2phy_ac_groups[] = {
-	"gpio87",
-};
 
 static const struct msm_function sdxpoorwills_functions[] = {
-	FUNCTION(qdss_stm31),
-	FUNCTION(blsp_uart1),
-	FUNCTION(gpio),
 	FUNCTION(uim2_data),
+	FUNCTION(gpio),
+	FUNCTION(qdss_stm31),
 	FUNCTION(ebi0_wrcdc),
 	FUNCTION(uim2_present),
 	FUNCTION(qdss_stm30),
+	FUNCTION(blsp_uart1),
 	FUNCTION(uim2_reset),
 	FUNCTION(blsp_i2c1),
 	FUNCTION(qdss_stm29),
@@ -912,14 +862,22 @@
 	FUNCTION(blsp_i2c3),
 	FUNCTION(gcc_gp3),
 	FUNCTION(qdss_stm19),
-	FUNCTION(qdss12),
+	FUNCTION(qdss4),
 	FUNCTION(qdss_stm18),
-	FUNCTION(qdss13),
+	FUNCTION(qdss5),
 	FUNCTION(qdss_stm17),
-	FUNCTION(qdss14),
+	FUNCTION(qdss6),
 	FUNCTION(bimc_dte0),
 	FUNCTION(native_tsens),
-	FUNCTION(vsense_trigger),
+	FUNCTION(qdss_stm16),
+	FUNCTION(qdss7),
+	FUNCTION(bimc_dte1),
+	FUNCTION(sec_mi2s),
+	FUNCTION(blsp_spi4),
+	FUNCTION(blsp_uart4),
+	FUNCTION(qdss_cti),
+	FUNCTION(qdss_stm27),
+	FUNCTION(qdss8),
 	FUNCTION(qdss_stm26),
 	FUNCTION(qdss9),
 	FUNCTION(blsp_i2c4),
@@ -930,26 +888,19 @@
 	FUNCTION(gcc_gp2),
 	FUNCTION(qdss_stm24),
 	FUNCTION(qdss11),
-	FUNCTION(qdss_stm16),
-	FUNCTION(qdss15),
-	FUNCTION(bimc_dte1),
-	FUNCTION(sec_mi2s),
-	FUNCTION(blsp_spi4),
-	FUNCTION(blsp_uart4),
-	FUNCTION(qdss_cti),
-	FUNCTION(qdss_stm27),
-	FUNCTION(qdss8),
 	FUNCTION(ebi2_a),
-	FUNCTION(qdss_stm3),
 	FUNCTION(ebi2_lcd),
-	FUNCTION(qdss_stm2),
 	FUNCTION(pll_bist),
-	FUNCTION(qdss_stm1),
-	FUNCTION(qdss_stm0),
 	FUNCTION(adsp_ext),
-	FUNCTION(epm1),
+	FUNCTION(qdss_stm11),
 	FUNCTION(m_voc),
+	FUNCTION(qdss_stm10),
 	FUNCTION(native_char),
+	FUNCTION(native_char3),
+	FUNCTION(nav_pps),
+	FUNCTION(nav_dr),
+	FUNCTION(native_char2),
+	FUNCTION(native_tsense),
 	FUNCTION(native_char1),
 	FUNCTION(pa_indicator),
 	FUNCTION(qdss_traceclk),
@@ -958,204 +909,200 @@
 	FUNCTION(qlink_req),
 	FUNCTION(pll_test),
 	FUNCTION(cri_trng),
-	FUNCTION(wmss_reset),
-	FUNCTION(native_char3),
-	FUNCTION(nav_pps),
-	FUNCTION(nav_dr),
-	FUNCTION(native_char2),
-	FUNCTION(native_tsense),
 	FUNCTION(prng_rosc),
 	FUNCTION(cri_trng0),
 	FUNCTION(cri_trng1),
 	FUNCTION(pll_ref),
 	FUNCTION(coex_uart),
-	FUNCTION(qdss_stm11),
-	FUNCTION(qdss_stm10),
-	FUNCTION(ddr_pxi0),
-	FUNCTION(ap2mdm_status),
+	FUNCTION(qdss_tracectl),
 	FUNCTION(ddr_bist),
-	FUNCTION(mdm2ap_status),
-	FUNCTION(ap2mdm_err),
-	FUNCTION(mdm2ap_err),
-	FUNCTION(ap2mdm_vdd),
-	FUNCTION(mdm2ap_vdd),
-	FUNCTION(ap2mdm_wake),
-	FUNCTION(pciehost_rst),
 	FUNCTION(blsp_spi1),
-	FUNCTION(qdss_stm14),
-	FUNCTION(pcie_wake),
-	FUNCTION(mdm2ap_wake),
 	FUNCTION(pci_e),
+	FUNCTION(tgu_ch0),
+	FUNCTION(pcie_clkreq),
+	FUNCTION(qdss_stm15),
+	FUNCTION(qdss_stm14),
 	FUNCTION(qdss_stm13),
+	FUNCTION(mgpi_clk),
+	FUNCTION(qdss_stm12),
+	FUNCTION(qdss_stm9),
 	FUNCTION(i2s_mclk),
 	FUNCTION(audio_ref),
 	FUNCTION(ldo_update),
 	FUNCTION(qdss_stm8),
 	FUNCTION(qdss_stm7),
-	FUNCTION(qdss4),
-	FUNCTION(tgu_ch0),
-	FUNCTION(pcie_clkreq),
-	FUNCTION(qdss_stm9),
-	FUNCTION(qdss_stm15),
-	FUNCTION(mgpi_clk),
-	FUNCTION(qdss_stm12),
-	FUNCTION(qdss_tracectl),
-	FUNCTION(atest_char),
+	FUNCTION(qdss12),
 	FUNCTION(qdss_stm6),
-	FUNCTION(qdss5),
-	FUNCTION(atest_char3),
+	FUNCTION(qdss13),
 	FUNCTION(qdss_stm5),
-	FUNCTION(qdss6),
-	FUNCTION(atest_char2),
+	FUNCTION(qdss14),
 	FUNCTION(qdss_stm4),
-	FUNCTION(qdss7),
-	FUNCTION(atest_char1),
+	FUNCTION(qdss15),
 	FUNCTION(uim1_data),
-	FUNCTION(atest_char0),
+	FUNCTION(qdss_stm3),
 	FUNCTION(uim1_present),
+	FUNCTION(qdss_stm2),
 	FUNCTION(uim1_reset),
+	FUNCTION(qdss_stm1),
 	FUNCTION(uim1_clk),
+	FUNCTION(qdss_stm0),
 	FUNCTION(dbg_out),
 	FUNCTION(gcc_plltest),
-	FUNCTION(usb2phy_ac),
 };
 
+/* Every pin is maintained as a single group, and missing or non-existing pin
+ * would be maintained as dummy group to synchronize pin group index with
+ * pin descriptor registered with pinctrl core.
+ * Clients would not be able to request these dummy pin groups.
+ */
 static const struct msm_pingroup sdxpoorwills_groups[] = {
-	PINGROUP(0, uim2_data, blsp_uart1, qdss_stm31, ebi0_wrcdc, NA, NA, NA,
-		 NA, NA),
-	PINGROUP(1, uim2_present, blsp_uart1, qdss_stm30, NA, NA, NA, NA, NA,
-		 NA),
-	PINGROUP(2, uim2_reset, blsp_uart1, blsp_i2c1, qdss_stm29, ebi0_wrcdc,
-		 NA, NA, NA, NA),
-	PINGROUP(3, uim2_clk, blsp_uart1, blsp_i2c1, qdss_stm28, NA, NA, NA,
-		 NA, NA),
-	PINGROUP(4, blsp_spi2, blsp_uart2, NA, qdss_stm23, qdss3, NA, NA, NA,
-		 NA),
-	PINGROUP(5, blsp_spi2, blsp_uart2, NA, qdss_stm22, qdss2, NA, NA, NA,
-		 NA),
-	PINGROUP(6, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm21, qdss1,
-		 NA, NA, NA),
-	PINGROUP(7, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm20, qdss0,
-		 NA, NA, NA),
-	PINGROUP(8, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, ldo_en, NA, NA,
-		 NA, NA),
-	PINGROUP(9, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, NA, NA, NA, NA,
-		 NA),
-	PINGROUP(10, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3, ext_dbg, NA,
-		 NA, NA, NA),
-	PINGROUP(11, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3, ext_dbg,
-		 gcc_gp3, NA, NA, NA),
-	PINGROUP(12, pri_mi2s, NA, qdss_stm19, qdss12, NA, NA, NA, NA, NA),
-	PINGROUP(13, pri_mi2s, NA, qdss_stm18, qdss13, NA, NA, NA, NA, NA),
-	PINGROUP(14, pri_mi2s, NA, NA, qdss_stm17, qdss14, bimc_dte0,
-		 native_tsens, vsense_trigger, NA),
-	PINGROUP(15, pri_mi2s, NA, NA, qdss_stm16, qdss15, NA, NA, bimc_dte1,
-		 NA),
-	PINGROUP(16, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti, qdss_cti, NA,
-		 NA, qdss_stm27, qdss8),
-	PINGROUP(17, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti, qdss_cti, NA,
-		 qdss_stm26, qdss9, NA),
-	PINGROUP(18, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, gcc_gp1, NA,
-		 qdss_stm25, qdss10, NA),
-	PINGROUP(19, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4, jitter_bist,
-		 gcc_gp2, NA, qdss_stm24, qdss11),
-	PINGROUP(20, sec_mi2s, ebi2_a, blsp_uart1, blsp_uart4, NA, qdss_stm3,
-		 NA, NA, NA),
-	PINGROUP(21, sec_mi2s, ebi2_lcd, blsp_uart1, blsp_uart4, NA, NA,
-		 qdss_stm2, NA, NA),
-	PINGROUP(22, sec_mi2s, ebi2_lcd, blsp_uart1, qdss_cti, qdss_cti,
-		 blsp_uart4, pll_bist, NA, qdss_stm1),
-	PINGROUP(23, sec_mi2s, ebi2_lcd, qdss_cti, qdss_cti, blsp_uart1,
-		 blsp_uart4, NA, qdss_stm0, NA),
-	PINGROUP(24, adsp_ext, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(25, m_voc, adsp_ext, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(26, NA, NA, NA, native_char, NA, NA, NA, NA, NA),
-	PINGROUP(27, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(28, wmss_reset, native_char3, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(29, NA, NA, nav_pps, nav_dr, NA, native_char2, native_tsense,
-		 NA, NA),
-	PINGROUP(30, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(31, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(32, NA, native_char1, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(33, NA, pa_indicator, qdss_traceclk, native_char0, NA, NA, NA,
-		 NA, NA),
-	PINGROUP(34, qlink_en, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(35, qlink_req, pll_test, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(36, NA, NA, cri_trng, NA, NA, NA, NA, NA, NA),
-	PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(38, NA, NA, prng_rosc, NA, NA, NA, NA, NA, NA),
-	PINGROUP(39, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(40, NA, NA, cri_trng0, NA, NA, NA, NA, NA, NA),
-	PINGROUP(41, NA, NA, cri_trng1, NA, NA, NA, NA, NA, NA),
-	PINGROUP(42, nav_pps, NA, nav_dr, pll_ref, NA, NA, NA, NA, NA),
-	PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(44, coex_uart, NA, qdss_stm11, NA, NA, NA, NA, NA, NA),
-	PINGROUP(45, coex_uart, NA, qdss_stm10, ddr_pxi0, NA, NA, NA, NA, NA),
-	PINGROUP(46, m_voc, ddr_bist, ddr_pxi0, NA, NA, NA, NA, NA, NA),
-	PINGROUP(47, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(48, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(49, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(50, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(51, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(52, blsp_spi2, blsp_spi1, blsp_spi3, blsp_spi4, NA, NA,
-		 qdss_stm14, NA, NA),
-	PINGROUP(53, pci_e, NA, NA, qdss_stm13, NA, NA, NA, NA, NA),
-	PINGROUP(54, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(55, qdss_cti, qdss_cti, tgu_ch0, NA, NA, NA, NA, NA, NA),
-	PINGROUP(56, pcie_clkreq, NA, qdss_stm9, NA, NA, NA, NA, NA, NA),
-	PINGROUP(57, NA, qdss_stm15, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(58, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(59, qdss_cti, m_voc, bimc_dte0, NA, NA, NA, NA, NA, NA),
-	PINGROUP(60, mgpi_clk, NA, qdss_stm12, qdss_tracectl, bimc_dte1, NA,
-		 NA, NA, NA),
-	PINGROUP(61, qdss_cti, NA, m_voc, NA, NA, NA, NA, NA, NA),
-	PINGROUP(62, i2s_mclk, nav_pps, nav_dr, audio_ref, blsp_spi1,
-		 blsp_spi2, blsp_spi3, blsp_spi4, ldo_update),
-	PINGROUP(63, blsp_uart2, NA, qdss_stm7, qdss4, atest_char, NA, NA, NA,
-		 NA),
-	PINGROUP(64, blsp_uart2, NA, qdss_stm6, qdss5, atest_char3, NA, NA, NA,
-		 NA),
-	PINGROUP(65, blsp_uart2, blsp_i2c2, NA, qdss_stm5, qdss6, atest_char2,
-		 NA, NA, NA),
-	PINGROUP(66, blsp_uart2, blsp_i2c2, NA, qdss_stm4, qdss7, atest_char1,
-		 NA, NA, NA),
-	PINGROUP(67, uim1_data, atest_char0, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(68, uim1_present, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(69, uim1_reset, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(70, uim1_clk, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(71, mgpi_clk, blsp_spi1, blsp_spi2, blsp_spi3, blsp_spi4,
-		 dbg_out, NA, NA, NA),
-	PINGROUP(72, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(73, NA, blsp_spi1, NA, gcc_plltest, NA, NA, NA, NA, NA),
-	PINGROUP(74, NA, blsp_spi1, NA, blsp_i2c1, gcc_plltest, NA, NA, NA, NA),
-	PINGROUP(75, NA, blsp_spi1, NA, blsp_i2c1, NA, NA, NA, NA, NA),
-	PINGROUP(76, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(77, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(82, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(83, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(84, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(85, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(87, NA, NA, usb2phy_ac, NA, NA, NA, NA, NA, NA),
-	PINGROUP(88, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(89, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(90, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(91, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(92, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(93, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(94, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(95, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(96, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(97, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(98, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA),
-	SDC_QDSD_PINGROUP(sdc1_clk, 0x9a000, 13, 6),
-	SDC_QDSD_PINGROUP(sdc1_cmd, 0x9a000, 11, 3),
-	SDC_QDSD_PINGROUP(sdc1_data, 0x9a000, 9, 0),
+	[0] = PINGROUP(0, uim2_data, blsp_uart1, qdss_stm31, ebi0_wrcdc, NA,
+		       NA, NA, NA, NA),
+	[1] = PINGROUP(1, uim2_present, blsp_uart1, qdss_stm30, NA, NA, NA, NA,
+		       NA, NA),
+	[2] = PINGROUP(2, uim2_reset, blsp_uart1, blsp_i2c1, qdss_stm29,
+		       ebi0_wrcdc, NA, NA, NA, NA),
+	[3] = PINGROUP(3, uim2_clk, blsp_uart1, blsp_i2c1, qdss_stm28, NA, NA,
+		       NA, NA, NA),
+	[4] = PINGROUP(4, blsp_spi2, blsp_uart2, NA, qdss_stm23, qdss3, NA, NA,
+		       NA, NA),
+	[5] = PINGROUP(5, blsp_spi2, blsp_uart2, NA, qdss_stm22, qdss2, NA, NA,
+		       NA, NA),
+	[6] = PINGROUP(6, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm21,
+		       qdss1, NA, NA, NA),
+	[7] = PINGROUP(7, blsp_spi2, blsp_uart2, blsp_i2c2, NA, qdss_stm20,
+		       qdss0, NA, NA, NA),
+	[8] = PINGROUP(8, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, ldo_en, NA,
+		       NA, NA, NA),
+	[9] = PINGROUP(9, pri_mi2s, blsp_spi3, blsp_uart3, ext_dbg, NA, NA, NA,
+		       NA, NA),
+	[10] = PINGROUP(10, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3,
+			ext_dbg, NA, NA, NA, NA),
+	[11] = PINGROUP(11, pri_mi2s, blsp_spi3, blsp_uart3, blsp_i2c3,
+			ext_dbg, gcc_gp3, NA, NA, NA),
+	[12] = PINGROUP(12, pri_mi2s, NA, qdss_stm19, qdss4, NA, NA, NA, NA,
+			NA),
+	[13] = PINGROUP(13, pri_mi2s, NA, qdss_stm18, qdss5, NA, NA, NA, NA,
+			NA),
+	[14] = PINGROUP(14, pri_mi2s, NA, NA, qdss_stm17, qdss6, bimc_dte0,
+			native_tsens, NA, NA),
+	[15] = PINGROUP(15, pri_mi2s, NA, NA, qdss_stm16, qdss7, NA, NA,
+			bimc_dte1, NA),
+	[16] = PINGROUP(16, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti,
+			qdss_cti, NA, qdss_stm27, qdss8, NA),
+	[17] = PINGROUP(17, sec_mi2s, blsp_spi4, blsp_uart4, qdss_cti,
+			qdss_cti, qdss_stm26, qdss9, NA, NA),
+	[18] = PINGROUP(18, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4,
+			gcc_gp1, qdss_stm25, qdss10, NA, NA),
+	[19] = PINGROUP(19, sec_mi2s, blsp_spi4, blsp_uart4, blsp_i2c4,
+			jitter_bist, gcc_gp2, qdss_stm24, qdss11, NA),
+	[20] = PINGROUP(20, sec_mi2s, ebi2_a, blsp_uart1, blsp_uart4, NA, NA,
+			NA, NA, NA),
+	[21] = PINGROUP(21, sec_mi2s, ebi2_lcd, blsp_uart1, blsp_uart4, NA, NA,
+			NA, NA, NA),
+	[22] = PINGROUP(22, sec_mi2s, ebi2_lcd, blsp_uart1, qdss_cti, qdss_cti,
+			blsp_uart4, pll_bist, NA, NA),
+	[23] = PINGROUP(23, sec_mi2s, ebi2_lcd, qdss_cti, qdss_cti, blsp_uart1,
+			blsp_uart4, NA, NA, NA),
+	[24] = PINGROUP(24, adsp_ext, NA, qdss_stm11, NA, NA, NA, NA, NA, NA),
+	[25] = PINGROUP(25, m_voc, adsp_ext, NA, qdss_stm10, NA, NA, NA, NA,
+			NA),
+	[26] = PINGROUP(26, NA, NA, NA, native_char, NA, NA, NA, NA, NA),
+	[27] = PINGROUP(27, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[28] = PINGROUP(28, NA, native_char3, NA, NA, NA, NA, NA, NA, NA),
+	[29] = PINGROUP(29, NA, NA, nav_pps, nav_dr, NA, native_char2,
+			native_tsense, NA, NA),
+	[30] = PINGROUP(30, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[31] = PINGROUP(31, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[32] = PINGROUP(32, NA, native_char1, NA, NA, NA, NA, NA, NA, NA),
+	[33] = PINGROUP(33, NA, pa_indicator, qdss_traceclk, native_char0, NA,
+			NA, NA, NA, NA),
+	[34] = PINGROUP(34, qlink_en, NA, NA, NA, NA, NA, NA, NA, NA),
+	[35] = PINGROUP(35, qlink_req, pll_test, NA, NA, NA, NA, NA, NA, NA),
+	[36] = PINGROUP(36, NA, NA, cri_trng, NA, NA, NA, NA, NA, NA),
+	[37] = PINGROUP(37, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[38] = PINGROUP(38, NA, NA, prng_rosc, NA, NA, NA, NA, NA, NA),
+	[39] = PINGROUP(39, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[40] = PINGROUP(40, NA, NA, cri_trng0, NA, NA, NA, NA, NA, NA),
+	[41] = PINGROUP(41, NA, NA, cri_trng1, NA, NA, NA, NA, NA, NA),
+	[42] = PINGROUP(42, nav_pps, nav_dr, pll_ref, NA, NA, NA, NA, NA, NA),
+	[43] = PINGROUP(43, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[44] = PINGROUP(44, coex_uart, qdss_tracectl, NA, NA, NA, NA, NA, NA,
+			NA),
+	[45] = PINGROUP(45, coex_uart, NA, NA, NA, NA, NA, NA, NA, NA),
+	[46] = PINGROUP(46, m_voc, ddr_bist, NA, NA, NA, NA, NA, NA, NA),
+	[47] = PINGROUP(47, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+	[48] = PINGROUP(48, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+	[49] = PINGROUP(49, ddr_bist, NA, NA, NA, NA, NA, NA, NA, NA),
+	[50] = PINGROUP(50, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[51] = PINGROUP(51, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[52] = PINGROUP(52, blsp_spi2, blsp_spi1, blsp_spi3, blsp_spi4, NA, NA,
+			NA, NA, NA),
+	[53] = PINGROUP(53, pci_e, NA, NA, NA, NA, NA, NA, NA, NA),
+	[54] = PINGROUP(54, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[55] = PINGROUP(55, qdss_cti, qdss_cti, tgu_ch0, NA, NA, NA, NA, NA,
+			NA),
+	[56] = PINGROUP(56, pcie_clkreq, NA, NA, NA, NA, NA, NA, NA, NA),
+	[57] = PINGROUP(57, NA, qdss_stm15, NA, NA, NA, NA, NA, NA, NA),
+	[58] = PINGROUP(58, NA, qdss_stm14, NA, NA, NA, NA, NA, NA, NA),
+	[59] = PINGROUP(59, qdss_cti, m_voc, NA, qdss_stm13, bimc_dte0, NA, NA,
+			NA, NA),
+	[60] = PINGROUP(60, mgpi_clk, NA, qdss_stm12, bimc_dte1, NA, NA, NA,
+			NA, NA),
+	[61] = PINGROUP(61, qdss_cti, NA, m_voc, NA, qdss_stm9, NA, NA, NA, NA),
+	[62] = PINGROUP(62, i2s_mclk, nav_pps, nav_dr, audio_ref, blsp_spi1,
+			blsp_spi2, blsp_spi3, blsp_spi4, ldo_update),
+	[63] = PINGROUP(63, blsp_uart2, NA, qdss_stm7, qdss12, NA, NA, NA, NA,
+			NA),
+	[64] = PINGROUP(64, blsp_uart2, qdss_stm6, qdss13, NA, NA, NA, NA, NA,
+			NA),
+	[65] = PINGROUP(65, blsp_uart2, blsp_i2c2, NA, qdss_stm5, qdss14, NA,
+			NA, NA, NA),
+	[66] = PINGROUP(66, blsp_uart2, blsp_i2c2, NA, qdss_stm4, qdss15, NA,
+			NA, NA, NA),
+	[67] = PINGROUP(67, uim1_data, NA, qdss_stm3, NA, NA, NA, NA, NA, NA),
+	[68] = PINGROUP(68, uim1_present, qdss_stm2, NA, NA, NA, NA, NA, NA,
+			NA),
+	[69] = PINGROUP(69, uim1_reset, qdss_stm1, NA, NA, NA, NA, NA, NA, NA),
+	[70] = PINGROUP(70, uim1_clk, NA, qdss_stm0, NA, NA, NA, NA, NA, NA),
+	[71] = PINGROUP(71, mgpi_clk, blsp_spi1, blsp_spi2, blsp_spi3,
+			blsp_spi4, dbg_out, NA, NA, NA),
+	[72] = PINGROUP(72, NA, blsp_spi1, NA, NA, NA, NA, NA, NA, NA),
+	[73] = PINGROUP(73, NA, blsp_spi1, NA, gcc_plltest, NA, NA, NA, NA, NA),
+	[74] = PINGROUP(74, NA, blsp_spi1, NA, blsp_i2c1, gcc_plltest, NA, NA,
+			NA, NA),
+	[75] = PINGROUP(75, NA, blsp_spi1, NA, blsp_i2c1, NA, NA, NA, NA, NA),
+	[76] = PINGROUP(76, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
+	[77] = PINGROUP(77, blsp_i2c4, NA, NA, NA, NA, NA, NA, NA, NA),
+	[78] = PINGROUP(78, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[79] = PINGROUP(79, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[80] = PINGROUP(80, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[81] = PINGROUP(81, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[82] = PINGROUP(82, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[83] = PINGROUP(83, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[84] = PINGROUP(84, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[85] = PINGROUP(85, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[86] = PINGROUP(86, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[87] = PINGROUP(87, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[88] = PINGROUP(88, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[89] = PINGROUP(89, qdss_cti, qdss_cti, NA, NA, NA, NA, NA, NA, NA),
+	[90] = PINGROUP(90, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[91] = PINGROUP(91, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[92] = PINGROUP(92, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[93] = PINGROUP(93, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[94] = PINGROUP(94, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[95] = PINGROUP(95, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[96] = PINGROUP(96, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[97] = PINGROUP(97, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[98] = PINGROUP(98, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[99] = PINGROUP(99, NA, NA, NA, NA, NA, NA, NA, NA, NA),
+	[100] = SDC_QDSD_PINGROUP(sdc1_rclk, 0x9a000, 15, 0),
+	[101] = SDC_QDSD_PINGROUP(sdc1_clk, 0x9a000, 13, 6),
+	[102] = SDC_QDSD_PINGROUP(sdc1_cmd, 0x9a000, 11, 3),
+	[103] = SDC_QDSD_PINGROUP(sdc1_data, 0x9a000, 9, 0),
+	[104] = SDC_QDSD_PINGROUP(sdc2_clk, 0x0, 14, 6),
+	[105] = SDC_QDSD_PINGROUP(sdc2_cmd, 0x0, 11, 3),
+	[106] = SDC_QDSD_PINGROUP(sdc2_data, 0x0, 9, 0),
 };
 
 static const struct msm_pinctrl_soc_data sdxpoorwills_pinctrl = {
diff --git a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
index f06fb1f..2ecbd22 100644
--- a/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
+++ b/drivers/pinctrl/qcom/pinctrl-spmi-gpio.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -471,6 +471,7 @@
 
 	pad = pctldev->desc->pins[pin].drv_data;
 
+	pad->is_enabled = true;
 	for (i = 0; i < nconfs; i++) {
 		param = pinconf_to_config_param(configs[i]);
 		arg = pinconf_to_config_argument(configs[i]);
@@ -619,6 +620,10 @@
 			return ret;
 	}
 
+	val = pad->is_enabled << PMIC_GPIO_REG_MASTER_EN_SHIFT;
+
+	ret = pmic_gpio_write(state, pad, PMIC_GPIO_REG_EN_CTL, val);
+
 	return ret;
 }
 
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index d32fa2b..e8aee6d 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -195,8 +195,6 @@
 
 	spin_unlock_irqrestore(&bank->slock, flags);
 
-	exynos_irq_unmask(irqd);
-
 	return 0;
 }
 
@@ -217,8 +215,6 @@
 	shift = irqd->hwirq * bank_type->fld_width[PINCFG_TYPE_FUNC];
 	mask = (1 << bank_type->fld_width[PINCFG_TYPE_FUNC]) - 1;
 
-	exynos_irq_mask(irqd);
-
 	spin_lock_irqsave(&bank->slock, flags);
 
 	con = readl(d->virt_base + reg_con);
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
index 862a096..be5c71d 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c
@@ -811,6 +811,7 @@
 		  SUNXI_FUNCTION(0x2, "lcd1"),		/* D16 */
 		  SUNXI_FUNCTION(0x3, "pata"),		/* ATAD12 */
 		  SUNXI_FUNCTION(0x4, "keypad"),	/* IN6 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
 		  SUNXI_FUNCTION_IRQ(0x6, 16),		/* EINT16 */
 		  SUNXI_FUNCTION(0x7, "csi1")),		/* D16 */
 	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 17),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
index 77a0236..b190904 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
@@ -508,57 +508,71 @@
 static const int usb1_muxvals[] = {0, 0};
 static const unsigned usb2_pins[] = {50, 51};
 static const int usb2_muxvals[] = {0, 0};
-static const unsigned port_range_pins[] = {
+static const unsigned port_range0_pins[] = {
 	159, 160, 161, 162, 163, 164, 165, 166,		/* PORT0x */
 	0, 1, 2, 3, 4, 5, 6, 7,				/* PORT1x */
 	8, 9, 10, 11, 12, 13, 14, 15,			/* PORT2x */
-	16, 17, 18, -1, -1, -1, -1, -1,			/* PORT3x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT4x */
-	-1, -1, -1, 46, 47, 48, 49, 50,			/* PORT5x */
-	51, -1, -1, 54, 55, 56, 57, 58,			/* PORT6x */
+	16, 17, 18,					/* PORT30-32 */
+};
+static const int port_range0_muxvals[] = {
+	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT0x */
+	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT1x */
+	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT2x */
+	15, 15, 15,					/* PORT30-32 */
+};
+static const unsigned port_range1_pins[] = {
+	46, 47, 48, 49, 50,				/* PORT53-57 */
+	51,						/* PORT60 */
+};
+static const int port_range1_muxvals[] = {
+	15, 15, 15, 15, 15,				/* PORT53-57 */
+	15,						/* PORT60 */
+};
+static const unsigned port_range2_pins[] = {
+	54, 55, 56, 57, 58,				/* PORT63-67 */
 	59, 60, 69, 70, 71, 72, 73, 74,			/* PORT7x */
 	75, 76, 77, 78, 79, 80, 81, 82,			/* PORT8x */
 	83, 84, 85, 86, 87, 88, 89, 90,			/* PORT9x */
 	91, 92, 93, 94, 95, 96, 97, 98,			/* PORT10x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT11x */
-	99, 100, 101, 102, 103, 104, 105, 106,		/* PORT12x */
-	107, 108, 109, 110, 111, 112, 113, 114,		/* PORT13x */
-	115, 116, 117, 118, 119, 120, 121, 122,		/* PORT14x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT15x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT16x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT17x */
-	61, 62, 63, 64, 65, 66, 67, 68,			/* PORT18x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT19x */
-	123, 124, 125, 126, 127, 128, 129, 130,		/* PORT20x */
-	131, 132, 133, 134, 135, 136, 137, 138,		/* PORT21x */
-	139, 140, 141, 142, -1, -1, -1, -1,		/* PORT22x */
-	147, 148, 149, 150, 151, 152, 153, 154,		/* PORT23x */
-	155, 156, 157, 143, 144, 145, 146, 158,		/* PORT24x */
 };
-static const int port_range_muxvals[] = {
-	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT0x */
-	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT1x */
-	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT2x */
-	15, 15, 15, -1, -1, -1, -1, -1,			/* PORT3x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT4x */
-	-1, -1, -1, 15, 15, 15, 15, 15,			/* PORT5x */
-	15, -1, -1, 15, 15, 15, 15, 15,			/* PORT6x */
+static const int port_range2_muxvals[] = {
+	15, 15, 15, 15, 15,				/* PORT63-67 */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT7x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT8x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT9x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT10x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT11x */
+};
+static const unsigned port_range3_pins[] = {
+	99, 100, 101, 102, 103, 104, 105, 106,		/* PORT12x */
+	107, 108, 109, 110, 111, 112, 113, 114,		/* PORT13x */
+	115, 116, 117, 118, 119, 120, 121, 122,		/* PORT14x */
+};
+static const int port_range3_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT12x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT13x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT14x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT15x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT16x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT17x */
+};
+static const unsigned port_range4_pins[] = {
+	61, 62, 63, 64, 65, 66, 67, 68,			/* PORT18x */
+};
+static const int port_range4_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT18x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT19x */
+};
+static const unsigned port_range5_pins[] = {
+	123, 124, 125, 126, 127, 128, 129, 130,		/* PORT20x */
+	131, 132, 133, 134, 135, 136, 137, 138,		/* PORT21x */
+	139, 140, 141, 142,				/* PORT220-223 */
+};
+static const int port_range5_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT20x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT21x */
-	15, 15, 15, 15, -1, -1, -1, -1,			/* PORT22x */
+	15, 15, 15, 15,					/* PORT220-223 */
+};
+static const unsigned port_range6_pins[] = {
+	147, 148, 149, 150, 151, 152, 153, 154,		/* PORT23x */
+	155, 156, 157, 143, 144, 145, 146, 158,		/* PORT24x */
+};
+static const int port_range6_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT23x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT24x */
 };
@@ -607,147 +621,153 @@
 	UNIPHIER_PINCTRL_GROUP(usb0),
 	UNIPHIER_PINCTRL_GROUP(usb1),
 	UNIPHIER_PINCTRL_GROUP(usb2),
-	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range0),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range1),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range2),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range3),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range4),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range5),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range6),
 	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_IRQ(xirq),
 	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_IRQ(xirq_alternatives),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port00, port_range, 0),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port01, port_range, 1),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port02, port_range, 2),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port03, port_range, 3),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port04, port_range, 4),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port05, port_range, 5),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port06, port_range, 6),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port07, port_range, 7),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port10, port_range, 8),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port11, port_range, 9),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port12, port_range, 10),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port13, port_range, 11),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port14, port_range, 12),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port15, port_range, 13),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port16, port_range, 14),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port17, port_range, 15),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port20, port_range, 16),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port21, port_range, 17),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port22, port_range, 18),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port23, port_range, 19),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port24, port_range, 20),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port25, port_range, 21),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port26, port_range, 22),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port27, port_range, 23),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port30, port_range, 24),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port31, port_range, 25),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port32, port_range, 26),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port53, port_range, 43),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port54, port_range, 44),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port55, port_range, 45),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port56, port_range, 46),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port57, port_range, 47),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port60, port_range, 48),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port63, port_range, 51),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port64, port_range, 52),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port65, port_range, 53),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port66, port_range, 54),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port67, port_range, 55),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port70, port_range, 56),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port71, port_range, 57),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port72, port_range, 58),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port73, port_range, 59),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port74, port_range, 60),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port75, port_range, 61),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port76, port_range, 62),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port77, port_range, 63),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port80, port_range, 64),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port81, port_range, 65),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port82, port_range, 66),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port83, port_range, 67),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port84, port_range, 68),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port85, port_range, 69),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port86, port_range, 70),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port87, port_range, 71),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port90, port_range, 72),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port91, port_range, 73),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port92, port_range, 74),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port93, port_range, 75),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port94, port_range, 76),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port95, port_range, 77),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port96, port_range, 78),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port97, port_range, 79),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port100, port_range, 80),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port101, port_range, 81),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port102, port_range, 82),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port103, port_range, 83),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port104, port_range, 84),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port105, port_range, 85),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port106, port_range, 86),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port107, port_range, 87),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port120, port_range, 96),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port121, port_range, 97),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port122, port_range, 98),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port123, port_range, 99),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port124, port_range, 100),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port125, port_range, 101),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port126, port_range, 102),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port127, port_range, 103),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port130, port_range, 104),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port131, port_range, 105),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port132, port_range, 106),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port133, port_range, 107),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port134, port_range, 108),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port135, port_range, 109),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port136, port_range, 110),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port137, port_range, 111),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port140, port_range, 112),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port141, port_range, 113),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port142, port_range, 114),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port143, port_range, 115),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port144, port_range, 116),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port145, port_range, 117),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port146, port_range, 118),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port147, port_range, 119),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port180, port_range, 144),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port181, port_range, 145),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port182, port_range, 146),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port183, port_range, 147),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port184, port_range, 148),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port185, port_range, 149),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port186, port_range, 150),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port187, port_range, 151),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port200, port_range, 160),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port201, port_range, 161),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port202, port_range, 162),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port203, port_range, 163),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port204, port_range, 164),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port205, port_range, 165),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port206, port_range, 166),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port207, port_range, 167),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port210, port_range, 168),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port211, port_range, 169),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port212, port_range, 170),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port213, port_range, 171),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port214, port_range, 172),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port215, port_range, 173),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port216, port_range, 174),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port217, port_range, 175),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port220, port_range, 176),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port221, port_range, 177),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port222, port_range, 178),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port223, port_range, 179),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port230, port_range, 184),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port231, port_range, 185),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port232, port_range, 186),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port233, port_range, 187),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port234, port_range, 188),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port235, port_range, 189),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port236, port_range, 190),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port237, port_range, 191),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port240, port_range, 192),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port241, port_range, 193),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port242, port_range, 194),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port243, port_range, 195),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port244, port_range, 196),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port245, port_range, 197),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port246, port_range, 198),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port247, port_range, 199),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port00, port_range0, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port01, port_range0, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port02, port_range0, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port03, port_range0, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port04, port_range0, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port05, port_range0, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port06, port_range0, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port07, port_range0, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port10, port_range0, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port11, port_range0, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port12, port_range0, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port13, port_range0, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port14, port_range0, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port15, port_range0, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port16, port_range0, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port17, port_range0, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port20, port_range0, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port21, port_range0, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port22, port_range0, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port23, port_range0, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port24, port_range0, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port25, port_range0, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port26, port_range0, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port27, port_range0, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port30, port_range0, 24),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port31, port_range0, 25),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port32, port_range0, 26),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port53, port_range1, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port54, port_range1, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port55, port_range1, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port56, port_range1, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port57, port_range1, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port60, port_range1, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port63, port_range2, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port64, port_range2, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port65, port_range2, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port66, port_range2, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port67, port_range2, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port70, port_range2, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port71, port_range2, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port72, port_range2, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port73, port_range2, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port74, port_range2, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port75, port_range2, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port76, port_range2, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port77, port_range2, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port80, port_range2, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port81, port_range2, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port82, port_range2, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port83, port_range2, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port84, port_range2, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port85, port_range2, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port86, port_range2, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port87, port_range2, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port90, port_range2, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port91, port_range2, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port92, port_range2, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port93, port_range2, 24),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port94, port_range2, 25),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port95, port_range2, 26),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port96, port_range2, 27),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port97, port_range2, 28),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port100, port_range2, 29),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port101, port_range2, 30),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port102, port_range2, 31),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port103, port_range2, 32),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port104, port_range2, 33),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port105, port_range2, 34),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port106, port_range2, 35),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port107, port_range2, 36),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port120, port_range3, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port121, port_range3, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port122, port_range3, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port123, port_range3, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port124, port_range3, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port125, port_range3, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port126, port_range3, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port127, port_range3, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port130, port_range3, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port131, port_range3, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port132, port_range3, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port133, port_range3, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port134, port_range3, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port135, port_range3, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port136, port_range3, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port137, port_range3, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port140, port_range3, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port141, port_range3, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port142, port_range3, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port143, port_range3, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port144, port_range3, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port145, port_range3, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port146, port_range3, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port147, port_range3, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port180, port_range4, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port181, port_range4, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port182, port_range4, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port183, port_range4, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port184, port_range4, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port185, port_range4, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port186, port_range4, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port187, port_range4, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port200, port_range5, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port201, port_range5, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port202, port_range5, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port203, port_range5, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port204, port_range5, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port205, port_range5, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port206, port_range5, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port207, port_range5, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port210, port_range5, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port211, port_range5, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port212, port_range5, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port213, port_range5, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port214, port_range5, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port215, port_range5, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port216, port_range5, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port217, port_range5, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port220, port_range5, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port221, port_range5, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port222, port_range5, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port223, port_range5, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port230, port_range6, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port231, port_range6, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port232, port_range6, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port233, port_range6, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port234, port_range6, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port235, port_range6, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port236, port_range6, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port237, port_range6, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port240, port_range6, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port241, port_range6, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port242, port_range6, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port243, port_range6, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port244, port_range6, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port245, port_range6, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port246, port_range6, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port247, port_range6, 15),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq0, xirq, 0),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq1, xirq, 1),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq2, xirq, 2),
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
index 9668633..73b828b 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
@@ -597,7 +597,7 @@
 static const int usb2_muxvals[] = {0, 0};
 static const unsigned usb3_pins[] = {52, 53};
 static const int usb3_muxvals[] = {0, 0};
-static const unsigned port_range_pins[] = {
+static const unsigned port_range0_pins[] = {
 	168, 169, 170, 171, 172, 173, 174, 175,		/* PORT0x */
 	0, 1, 2, 3, 4, 5, 6, 7,				/* PORT1x */
 	8, 9, 10, 11, 12, 13, 14, 15,			/* PORT2x */
@@ -609,23 +609,8 @@
 	75, 76, 77, 78, 79, 80, 81, 82,			/* PORT8x */
 	83, 84, 85, 86, 87, 88, 89, 90,			/* PORT9x */
 	91, 92, 93, 94, 95, 96, 97, 98,			/* PORT10x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT11x */
-	99, 100, 101, 102, 103, 104, 105, 106,		/* PORT12x */
-	107, 108, 109, 110, 111, 112, 113, 114,		/* PORT13x */
-	115, 116, 117, 118, 119, 120, 121, 122,		/* PORT14x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT15x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT16x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT17x */
-	61, 62, 63, 64, 65, 66, 67, 68,			/* PORT18x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT19x */
-	123, 124, 125, 126, 127, 128, 129, 130,		/* PORT20x */
-	131, 132, 133, 134, 135, 136, 137, 138,		/* PORT21x */
-	139, 140, 141, 142, 143, 144, 145, 146,		/* PORT22x */
-	147, 148, 149, 150, 151, 152, 153, 154,		/* PORT23x */
-	155, 156, 157, 158, 159, 160, 161, 162,		/* PORT24x */
-	163, 164, 165, 166, 167,			/* PORT25x */
 };
-static const int port_range_muxvals[] = {
+static const int port_range0_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT0x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT1x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT2x */
@@ -637,21 +622,38 @@
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT8x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT9x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT10x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT11x */
+};
+static const unsigned port_range1_pins[] = {
+	99, 100, 101, 102, 103, 104, 105, 106,		/* PORT12x */
+	107, 108, 109, 110, 111, 112, 113, 114,		/* PORT13x */
+	115, 116, 117, 118, 119, 120, 121, 122,		/* PORT14x */
+};
+static const int port_range1_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT12x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT13x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT14x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT15x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT16x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT17x */
+};
+static const unsigned port_range2_pins[] = {
+	61, 62, 63, 64, 65, 66, 67, 68,			/* PORT18x */
+};
+static const int port_range2_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT18x */
-	-1, -1, -1, -1, -1, -1, -1, -1,			/* PORT19x */
+};
+static const unsigned port_range3_pins[] = {
+	123, 124, 125, 126, 127, 128, 129, 130,		/* PORT20x */
+	131, 132, 133, 134, 135, 136, 137, 138,		/* PORT21x */
+	139, 140, 141, 142, 143, 144, 145, 146,		/* PORT22x */
+	147, 148, 149, 150, 151, 152, 153, 154,		/* PORT23x */
+	155, 156, 157, 158, 159, 160, 161, 162,		/* PORT24x */
+	163, 164, 165, 166, 167,			/* PORT250-254 */
+};
+static const int port_range3_muxvals[] = {
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT20x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT21x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT22x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT23x */
 	15, 15, 15, 15, 15, 15, 15, 15,			/* PORT24x */
-	15, 15, 15, 15, 15,				/* PORT25x */
+	15, 15, 15, 15, 15,				/* PORT250-254 */
 };
 static const unsigned xirq_pins[] = {
 	149, 150, 151, 152, 153, 154, 155, 156,		/* XIRQ0-7 */
@@ -695,174 +697,177 @@
 	UNIPHIER_PINCTRL_GROUP(usb1),
 	UNIPHIER_PINCTRL_GROUP(usb2),
 	UNIPHIER_PINCTRL_GROUP(usb3),
-	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range0),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range1),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range2),
+	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_PORT(port_range3),
 	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_IRQ(xirq),
 	UNIPHIER_PINCTRL_GROUP_GPIO_RANGE_IRQ(xirq_alternatives),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port00, port_range, 0),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port01, port_range, 1),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port02, port_range, 2),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port03, port_range, 3),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port04, port_range, 4),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port05, port_range, 5),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port06, port_range, 6),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port07, port_range, 7),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port10, port_range, 8),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port11, port_range, 9),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port12, port_range, 10),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port13, port_range, 11),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port14, port_range, 12),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port15, port_range, 13),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port16, port_range, 14),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port17, port_range, 15),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port20, port_range, 16),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port21, port_range, 17),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port22, port_range, 18),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port23, port_range, 19),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port24, port_range, 20),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port25, port_range, 21),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port26, port_range, 22),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port27, port_range, 23),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port30, port_range, 24),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port31, port_range, 25),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port32, port_range, 26),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port33, port_range, 27),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port34, port_range, 28),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port35, port_range, 29),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port36, port_range, 30),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port37, port_range, 31),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port40, port_range, 32),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port41, port_range, 33),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port42, port_range, 34),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port43, port_range, 35),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port44, port_range, 36),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port45, port_range, 37),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port46, port_range, 38),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port47, port_range, 39),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port50, port_range, 40),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port51, port_range, 41),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port52, port_range, 42),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port53, port_range, 43),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port54, port_range, 44),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port55, port_range, 45),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port56, port_range, 46),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port57, port_range, 47),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port60, port_range, 48),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port61, port_range, 49),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port62, port_range, 50),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port63, port_range, 51),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port64, port_range, 52),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port65, port_range, 53),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port66, port_range, 54),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port67, port_range, 55),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port70, port_range, 56),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port71, port_range, 57),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port72, port_range, 58),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port73, port_range, 59),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port74, port_range, 60),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port75, port_range, 61),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port76, port_range, 62),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port77, port_range, 63),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port80, port_range, 64),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port81, port_range, 65),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port82, port_range, 66),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port83, port_range, 67),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port84, port_range, 68),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port85, port_range, 69),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port86, port_range, 70),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port87, port_range, 71),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port90, port_range, 72),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port91, port_range, 73),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port92, port_range, 74),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port93, port_range, 75),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port94, port_range, 76),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port95, port_range, 77),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port96, port_range, 78),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port97, port_range, 79),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port100, port_range, 80),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port101, port_range, 81),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port102, port_range, 82),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port103, port_range, 83),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port104, port_range, 84),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port105, port_range, 85),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port106, port_range, 86),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port107, port_range, 87),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port120, port_range, 96),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port121, port_range, 97),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port122, port_range, 98),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port123, port_range, 99),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port124, port_range, 100),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port125, port_range, 101),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port126, port_range, 102),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port127, port_range, 103),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port130, port_range, 104),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port131, port_range, 105),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port132, port_range, 106),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port133, port_range, 107),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port134, port_range, 108),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port135, port_range, 109),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port136, port_range, 110),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port137, port_range, 111),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port140, port_range, 112),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port141, port_range, 113),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port142, port_range, 114),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port143, port_range, 115),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port144, port_range, 116),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port145, port_range, 117),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port146, port_range, 118),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port147, port_range, 119),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port180, port_range, 144),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port181, port_range, 145),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port182, port_range, 146),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port183, port_range, 147),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port184, port_range, 148),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port185, port_range, 149),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port186, port_range, 150),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port187, port_range, 151),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port200, port_range, 160),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port201, port_range, 161),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port202, port_range, 162),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port203, port_range, 163),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port204, port_range, 164),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port205, port_range, 165),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port206, port_range, 166),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port207, port_range, 167),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port210, port_range, 168),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port211, port_range, 169),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port212, port_range, 170),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port213, port_range, 171),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port214, port_range, 172),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port215, port_range, 173),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port216, port_range, 174),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port217, port_range, 175),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port220, port_range, 176),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port221, port_range, 177),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port222, port_range, 178),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port223, port_range, 179),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port224, port_range, 180),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port225, port_range, 181),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port226, port_range, 182),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port227, port_range, 183),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port230, port_range, 184),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port231, port_range, 185),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port232, port_range, 186),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port233, port_range, 187),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port234, port_range, 188),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port235, port_range, 189),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port236, port_range, 190),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port237, port_range, 191),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port240, port_range, 192),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port241, port_range, 193),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port242, port_range, 194),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port243, port_range, 195),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port244, port_range, 196),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port245, port_range, 197),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port246, port_range, 198),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port247, port_range, 199),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port250, port_range, 200),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port251, port_range, 201),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port252, port_range, 202),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port253, port_range, 203),
-	UNIPHIER_PINCTRL_GROUP_SINGLE(port254, port_range, 204),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port00, port_range0, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port01, port_range0, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port02, port_range0, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port03, port_range0, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port04, port_range0, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port05, port_range0, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port06, port_range0, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port07, port_range0, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port10, port_range0, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port11, port_range0, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port12, port_range0, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port13, port_range0, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port14, port_range0, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port15, port_range0, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port16, port_range0, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port17, port_range0, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port20, port_range0, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port21, port_range0, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port22, port_range0, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port23, port_range0, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port24, port_range0, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port25, port_range0, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port26, port_range0, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port27, port_range0, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port30, port_range0, 24),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port31, port_range0, 25),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port32, port_range0, 26),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port33, port_range0, 27),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port34, port_range0, 28),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port35, port_range0, 29),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port36, port_range0, 30),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port37, port_range0, 31),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port40, port_range0, 32),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port41, port_range0, 33),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port42, port_range0, 34),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port43, port_range0, 35),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port44, port_range0, 36),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port45, port_range0, 37),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port46, port_range0, 38),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port47, port_range0, 39),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port50, port_range0, 40),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port51, port_range0, 41),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port52, port_range0, 42),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port53, port_range0, 43),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port54, port_range0, 44),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port55, port_range0, 45),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port56, port_range0, 46),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port57, port_range0, 47),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port60, port_range0, 48),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port61, port_range0, 49),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port62, port_range0, 50),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port63, port_range0, 51),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port64, port_range0, 52),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port65, port_range0, 53),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port66, port_range0, 54),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port67, port_range0, 55),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port70, port_range0, 56),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port71, port_range0, 57),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port72, port_range0, 58),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port73, port_range0, 59),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port74, port_range0, 60),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port75, port_range0, 61),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port76, port_range0, 62),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port77, port_range0, 63),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port80, port_range0, 64),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port81, port_range0, 65),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port82, port_range0, 66),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port83, port_range0, 67),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port84, port_range0, 68),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port85, port_range0, 69),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port86, port_range0, 70),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port87, port_range0, 71),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port90, port_range0, 72),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port91, port_range0, 73),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port92, port_range0, 74),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port93, port_range0, 75),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port94, port_range0, 76),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port95, port_range0, 77),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port96, port_range0, 78),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port97, port_range0, 79),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port100, port_range0, 80),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port101, port_range0, 81),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port102, port_range0, 82),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port103, port_range0, 83),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port104, port_range0, 84),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port105, port_range0, 85),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port106, port_range0, 86),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port107, port_range0, 87),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port120, port_range1, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port121, port_range1, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port122, port_range1, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port123, port_range1, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port124, port_range1, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port125, port_range1, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port126, port_range1, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port127, port_range1, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port130, port_range1, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port131, port_range1, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port132, port_range1, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port133, port_range1, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port134, port_range1, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port135, port_range1, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port136, port_range1, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port137, port_range1, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port140, port_range1, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port141, port_range1, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port142, port_range1, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port143, port_range1, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port144, port_range1, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port145, port_range1, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port146, port_range1, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port147, port_range1, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port180, port_range2, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port181, port_range2, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port182, port_range2, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port183, port_range2, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port184, port_range2, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port185, port_range2, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port186, port_range2, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port187, port_range2, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port200, port_range3, 0),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port201, port_range3, 1),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port202, port_range3, 2),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port203, port_range3, 3),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port204, port_range3, 4),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port205, port_range3, 5),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port206, port_range3, 6),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port207, port_range3, 7),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port210, port_range3, 8),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port211, port_range3, 9),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port212, port_range3, 10),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port213, port_range3, 11),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port214, port_range3, 12),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port215, port_range3, 13),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port216, port_range3, 14),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port217, port_range3, 15),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port220, port_range3, 16),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port221, port_range3, 17),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port222, port_range3, 18),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port223, port_range3, 19),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port224, port_range3, 20),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port225, port_range3, 21),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port226, port_range3, 22),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port227, port_range3, 23),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port230, port_range3, 24),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port231, port_range3, 25),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port232, port_range3, 26),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port233, port_range3, 27),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port234, port_range3, 28),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port235, port_range3, 29),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port236, port_range3, 30),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port237, port_range3, 31),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port240, port_range3, 32),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port241, port_range3, 33),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port242, port_range3, 34),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port243, port_range3, 35),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port244, port_range3, 36),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port245, port_range3, 37),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port246, port_range3, 38),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port247, port_range3, 39),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port250, port_range3, 40),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port251, port_range3, 41),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port252, port_range3, 42),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port253, port_range3, 43),
+	UNIPHIER_PINCTRL_GROUP_SINGLE(port254, port_range3, 44),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq0, xirq, 0),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq1, xirq, 1),
 	UNIPHIER_PINCTRL_GROUP_SINGLE(xirq2, xirq, 2),
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index f5f783c..6727da6 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -24,8 +24,6 @@
 #define GSI_CMD_TIMEOUT (5*HZ)
 #define GSI_STOP_CMD_TIMEOUT_MS 20
 #define GSI_MAX_CH_LOW_WEIGHT 15
-#define GSI_MHI_ER_START 10
-#define GSI_MHI_ER_END 16
 
 #define GSI_RESET_WA_MIN_SLEEP 1000
 #define GSI_RESET_WA_MAX_SLEEP 2000
@@ -829,10 +827,23 @@
 		return -GSI_STATUS_ERROR;
 	}
 
-	/* bitmap is max events excludes reserved events */
+	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;
+		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);
+		return -GSI_STATUS_ERROR;
+	}
+
 	gsi_ctx->evt_bmap = ~((1 << gsi_ctx->max_ev) - 1);
-	gsi_ctx->evt_bmap |= ((1 << (GSI_MHI_ER_END + 1)) - 1) ^
-		((1 << GSI_MHI_ER_START) - 1);
+
+	/* exclude reserved mhi events */
+	if (props->mhi_er_id_limits_valid)
+		gsi_ctx->evt_bmap |=
+			((1 << (props->mhi_er_id_limits[1] + 1)) - 1) ^
+			((1 << (props->mhi_er_id_limits[0])) - 1);
 
 	/*
 	 * enable all interrupts but GSI_BREAK_POINT.
@@ -1084,8 +1095,8 @@
 
 	if (props->intf == GSI_EVT_CHTYPE_MHI_EV &&
 			(!props->evchid_valid ||
-			props->evchid > GSI_MHI_ER_END ||
-			props->evchid < GSI_MHI_ER_START)) {
+			props->evchid > gsi_ctx->per.mhi_er_id_limits[1] ||
+			props->evchid < gsi_ctx->per.mhi_er_id_limits[0])) {
 		GSIERR("MHI requires evchid valid=%d val=%u\n",
 				props->evchid_valid, props->evchid);
 		return -GSI_STATUS_INVALID_PARAMS;
@@ -2598,15 +2609,16 @@
 	if (curr == GSI_CHAN_MODE_CALLBACK &&
 			mode == GSI_CHAN_MODE_POLL) {
 		__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0);
+		atomic_set(&ctx->poll_mode, mode);
 		ctx->stats.callback_to_poll++;
 	}
 
 	if (curr == GSI_CHAN_MODE_POLL &&
 			mode == GSI_CHAN_MODE_CALLBACK) {
+		atomic_set(&ctx->poll_mode, mode);
 		__gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0);
 		ctx->stats.poll_to_callback++;
 	}
-	atomic_set(&ctx->poll_mode, mode);
 	spin_unlock_irqrestore(&gsi_ctx->slock, flags);
 
 	return GSI_STATUS_SUCCESS;
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 4a9232e..96b9bd6 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -1095,7 +1095,7 @@
 EXPORT_SYMBOL(ipa_reset_flt);
 
 /**
- * allocate_nat_device() - Allocates memory for the NAT device
+ * ipa_allocate_nat_device() - Allocates memory for the NAT device
  * @mem:	[in/out] memory parameters
  *
  * Called by NAT client driver to allocate memory for the NAT entries. Based on
@@ -1103,15 +1103,55 @@
  *
  * Returns:	0 on success, negative on failure
  */
-int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
 {
 	int ret;
 
-	IPA_API_DISPATCH_RETURN(allocate_nat_device, mem);
+	IPA_API_DISPATCH_RETURN(ipa_allocate_nat_device, mem);
 
 	return ret;
 }
-EXPORT_SYMBOL(allocate_nat_device);
+EXPORT_SYMBOL(ipa_allocate_nat_device);
+
+/**
+ * ipa_allocate_nat_table() - Allocates memory for the NAT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by NAT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_allocate_nat_table, table_alloc);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_allocate_nat_table);
+
+
+/**
+ * ipa_allocate_ipv6ct_table() - Allocates memory for the IPv6CT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by IPv6CT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_allocate_ipv6ct_table, table_alloc);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_allocate_ipv6ct_table);
 
 /**
  * ipa_nat_init_cmd() - Post IP_V4_NAT_INIT command to IPA HW
@@ -1132,6 +1172,25 @@
 EXPORT_SYMBOL(ipa_nat_init_cmd);
 
 /**
+ * ipa_ipv6ct_init_cmd() - Post IP_V6_CONN_TRACK_INIT command to IPA HW
+ * @init:	[in] initialization command attributes
+ *
+ * Called by IPv6CT client driver to post IP_V6_CONN_TRACK_INIT command
+ * to IPA HW.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_ipv6ct_init_cmd, init);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_ipv6ct_init_cmd);
+
+/**
  * ipa_nat_dma_cmd() - Post NAT_DMA command to IPA HW
  * @dma:	[in] initialization command attributes
  *
@@ -1150,8 +1209,26 @@
 EXPORT_SYMBOL(ipa_nat_dma_cmd);
 
 /**
- * ipa_nat_del_cmd() - Delete a NAT table
- * @del:	[in] delete table table table parameters
+ * ipa_table_dma_cmd() - Post TABLE_DMA command to IPA HW
+ * @dma:	[in] initialization command attributes
+ *
+ * Called by NAT/IPv6CT client to post TABLE_DMA command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_table_dma_cmd, dma);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_table_dma_cmd);
+
+/**
+ * ipa_nat_del_cmd() - Delete the NAT table
+ * @del:	[in] delete NAT table parameters
  *
  * Called by NAT client driver to delete the nat table
  *
@@ -1168,6 +1245,60 @@
 EXPORT_SYMBOL(ipa_nat_del_cmd);
 
 /**
+ * ipa_del_nat_table() - Delete the NAT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by NAT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_del_nat_table, del);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_del_nat_table);
+
+/**
+ * ipa_del_ipv6ct_table() - Delete the IPv6CT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by IPv6CT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_del_ipv6ct_table, del);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_del_ipv6ct_table);
+
+/**
+ * ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
+ * @mdfy_pdn:	[in] PDN info to be written to SRAM
+ *
+ * Called by NAT client driver to modify an entry in the PDN config table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_nat_mdfy_pdn, mdfy_pdn);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_nat_mdfy_pdn);
+
+/**
  * ipa_send_msg() - Send "message" from kernel client to IPA driver
  * @meta: [in] message meta-data
  * @buff: [in] the payload for message
@@ -3004,6 +3135,69 @@
 }
 EXPORT_SYMBOL(ipa_ntn_uc_dereg_rdyCB);
 
+/**
+ * ipa_conn_wdi3_pipes() - connect wdi3 pipes
+ */
+int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_conn_wdi3_pipes, in, out);
+
+	return ret;
+}
+
+/**
+ * ipa_disconn_wdi3_pipes() - disconnect wdi3 pipes
+ */
+int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_disconn_wdi3_pipes, ipa_ep_idx_tx,
+		ipa_ep_idx_rx);
+
+	return ret;
+}
+
+/**
+ * ipa_enable_wdi3_pipes() - enable wdi3 pipes
+ */
+int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_enable_wdi3_pipes, ipa_ep_idx_tx,
+		ipa_ep_idx_rx);
+
+	return ret;
+}
+
+/**
+ * ipa_disable_wdi3_pipes() - disable wdi3 pipes
+ */
+int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_disable_wdi3_pipes, ipa_ep_idx_tx,
+		ipa_ep_idx_rx);
+
+	return ret;
+}
+
+/**
+ * ipa_tz_unlock_reg() - Allow AP access to memory regions controlled by TZ
+ */
+int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs)
+{
+	int ret;
+
+	IPA_API_DISPATCH_RETURN(ipa_tz_unlock_reg, reg_info, num_regs);
+
+	return ret;
+}
 
 static const struct dev_pm_ops ipa_pm_ops = {
 	.suspend_noirq = ipa_ap_suspend,
diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h
index 20471eb..b526711 100644
--- a/drivers/platform/msm/ipa/ipa_api.h
+++ b/drivers/platform/msm/ipa/ipa_api.h
@@ -12,6 +12,7 @@
 
 #include <linux/ipa_mhi.h>
 #include <linux/ipa_uc_offload.h>
+#include <linux/ipa_wdi3.h>
 #include "ipa_common_i.h"
 
 #ifndef _IPA_API_H_
@@ -114,14 +115,30 @@
 
 	int (*ipa_reset_flt)(enum ipa_ip_type ip);
 
-	int (*allocate_nat_device)(struct ipa_ioc_nat_alloc_mem *mem);
+	int (*ipa_allocate_nat_device)(struct ipa_ioc_nat_alloc_mem *mem);
+
+	int (*ipa_allocate_nat_table)(
+		struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+
+	int (*ipa_allocate_ipv6ct_table)(
+		struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
 
 	int (*ipa_nat_init_cmd)(struct ipa_ioc_v4_nat_init *init);
 
+	int (*ipa_ipv6ct_init_cmd)(struct ipa_ioc_ipv6ct_init *init);
+
 	int (*ipa_nat_dma_cmd)(struct ipa_ioc_nat_dma_cmd *dma);
 
+	int (*ipa_table_dma_cmd)(struct ipa_ioc_nat_dma_cmd *dma);
+
 	int (*ipa_nat_del_cmd)(struct ipa_ioc_v4_nat_del *del);
 
+	int (*ipa_del_nat_table)(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+	int (*ipa_del_ipv6ct_table)(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+	int (*ipa_nat_mdfy_pdn)(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
+
 	int (*ipa_send_msg)(struct ipa_msg_meta *meta, void *buff,
 		ipa_msg_free_fn callback);
 
@@ -385,6 +402,21 @@
 		void *user_data);
 
 	void (*ipa_ntn_uc_dereg_rdyCB)(void);
+
+	int (*ipa_conn_wdi3_pipes)(struct ipa_wdi3_conn_in_params *in,
+		struct ipa_wdi3_conn_out_params *out);
+
+	int (*ipa_disconn_wdi3_pipes)(int ipa_ep_idx_tx,
+		int ipa_ep_idx_rx);
+
+	int (*ipa_enable_wdi3_pipes)(int ipa_ep_idx_tx,
+		int ipa_ep_idx_rx);
+
+	int (*ipa_disable_wdi3_pipes)(int ipa_ep_idx_tx,
+		int ipa_ep_idx_rx);
+
+	int (*ipa_tz_unlock_reg)(struct ipa_tz_unlock_reg_info *reg_info,
+		u16 num_regs);
 };
 
 #ifdef CONFIG_IPA
diff --git a/drivers/platform/msm/ipa/ipa_clients/Makefile b/drivers/platform/msm/ipa/ipa_clients/Makefile
index 61625f5..738d88f 100644
--- a/drivers/platform/msm/ipa/ipa_clients/Makefile
+++ b/drivers/platform/msm/ipa/ipa_clients/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
-obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o
+obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
+obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o ipa_uc_offload.o ipa_wdi3.o
 obj-$(CONFIG_ECM_IPA) += ecm_ipa.o
 obj-$(CONFIG_RNDIS_IPA) += rndis_ipa.o
diff --git a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
index 105294a..2975192 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ecm_ipa.c
@@ -19,6 +19,8 @@
 #include <linux/sched.h>
 #include <linux/atomic.h>
 #include <linux/ecm_ipa.h>
+#include "../ipa_common_i.h"
+#include "../ipa_v3/ipa_pm.h"
 
 #define DRIVER_NAME "ecm_ipa"
 #define ECM_IPA_IPV4_HDR_NAME "ecm_eth_ipv4"
@@ -120,6 +122,7 @@
  * @usb_to_ipa_client: producer client
  * @ipa_rm_resource_name_prod: IPA resource manager producer resource
  * @ipa_rm_resource_name_cons: IPA resource manager consumer resource
+ * @pm_hdl: handle for IPA PM
  */
 struct ecm_ipa_dev {
 	struct net_device *net;
@@ -137,6 +140,7 @@
 	enum ipa_client_type usb_to_ipa_client;
 	enum ipa_rm_resource_name ipa_rm_resource_name_prod;
 	enum ipa_rm_resource_name ipa_rm_resource_name_cons;
+	u32 pm_hdl;
 };
 
 static int ecm_ipa_open(struct net_device *net);
@@ -158,6 +162,8 @@
 static struct net_device_stats *ecm_ipa_get_stats(struct net_device *net);
 static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void ecm_ipa_destroy_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
+static int ecm_ipa_register_pm_client(struct ecm_ipa_dev *ecm_ipa_ctx);
+static void ecm_ipa_deregister_pm_client(struct ecm_ipa_dev *ecm_ipa_ctx);
 static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx);
 static netdev_tx_t ecm_ipa_start_xmit
@@ -403,27 +409,34 @@
 	ECM_IPA_DEBUG("usb_to_ipa_client = %d\n",
 		      ecm_ipa_ctx->usb_to_ipa_client);
 
-	ecm_ipa_ctx->ipa_rm_resource_name_cons =
-		ipa_get_rm_resource_from_ep(ipa_to_usb_hdl);
-	if (ecm_ipa_ctx->ipa_rm_resource_name_cons < 0) {
-		ECM_IPA_ERROR("Error getting CONS RM resource from handle %d\n",
+	if (ipa_pm_is_used()) {
+		retval = ecm_ipa_register_pm_client(ecm_ipa_ctx);
+	} else {
+		ecm_ipa_ctx->ipa_rm_resource_name_cons =
+			ipa_get_rm_resource_from_ep(ipa_to_usb_hdl);
+		if (ecm_ipa_ctx->ipa_rm_resource_name_cons < 0) {
+			ECM_IPA_ERROR(
+			"Error getting CONS RM resource from handle %d\n",
+				      ecm_ipa_ctx->ipa_rm_resource_name_cons);
+			return -EINVAL;
+		}
+		ECM_IPA_DEBUG("ipa_rm_resource_name_cons = %d\n",
 			      ecm_ipa_ctx->ipa_rm_resource_name_cons);
-		return -EINVAL;
-	}
-	ECM_IPA_DEBUG("ipa_rm_resource_name_cons = %d\n",
-		      ecm_ipa_ctx->ipa_rm_resource_name_cons);
 
-	ecm_ipa_ctx->ipa_rm_resource_name_prod =
-		ipa_get_rm_resource_from_ep(usb_to_ipa_hdl);
-	if (ecm_ipa_ctx->ipa_rm_resource_name_prod < 0) {
-		ECM_IPA_ERROR("Error getting PROD RM resource from handle %d\n",
+		ecm_ipa_ctx->ipa_rm_resource_name_prod =
+			ipa_get_rm_resource_from_ep(usb_to_ipa_hdl);
+		if (ecm_ipa_ctx->ipa_rm_resource_name_prod < 0) {
+			ECM_IPA_ERROR(
+			"Error getting PROD RM resource from handle %d\n",
+				      ecm_ipa_ctx->ipa_rm_resource_name_prod);
+			return -EINVAL;
+		}
+		ECM_IPA_DEBUG("ipa_rm_resource_name_prod = %d\n",
 			      ecm_ipa_ctx->ipa_rm_resource_name_prod);
-		return -EINVAL;
-	}
-	ECM_IPA_DEBUG("ipa_rm_resource_name_prod = %d\n",
-		      ecm_ipa_ctx->ipa_rm_resource_name_prod);
 
-	retval = ecm_ipa_create_rm_resource(ecm_ipa_ctx);
+		retval = ecm_ipa_create_rm_resource(ecm_ipa_ctx);
+	}
+
 	if (retval) {
 		ECM_IPA_ERROR("fail on RM create\n");
 		goto fail_create_rm;
@@ -488,7 +501,10 @@
 fail:
 	ecm_ipa_deregister_properties();
 fail_create_rm:
-	ecm_ipa_destroy_rm_resource(ecm_ipa_ctx);
+	if (ipa_pm_is_used())
+		ecm_ipa_deregister_pm_client(ecm_ipa_ctx);
+	else
+		ecm_ipa_destroy_rm_resource(ecm_ipa_ctx);
 	return retval;
 }
 EXPORT_SYMBOL(ecm_ipa_connect);
@@ -746,7 +762,10 @@
 	netif_stop_queue(ecm_ipa_ctx->net);
 	ECM_IPA_DEBUG("queue stopped\n");
 
-	ecm_ipa_destroy_rm_resource(ecm_ipa_ctx);
+	if (ipa_pm_is_used())
+		ecm_ipa_deregister_pm_client(ecm_ipa_ctx);
+	else
+		ecm_ipa_destroy_rm_resource(ecm_ipa_ctx);
 
 	outstanding_dropped_pkts =
 		atomic_read(&ecm_ipa_ctx->outstanding_pkts);
@@ -1117,15 +1136,65 @@
 	ECM_IPA_LOG_EXIT();
 }
 
+static void ecm_ipa_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	struct ecm_ipa_dev *ecm_ipa_ctx = p;
+
+	ECM_IPA_LOG_ENTRY();
+	if (event != IPA_PM_CLIENT_ACTIVATED) {
+		ECM_IPA_ERROR("unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+
+	if (netif_queue_stopped(ecm_ipa_ctx->net)) {
+		ECM_IPA_DEBUG("Resource Granted - starting queue\n");
+		netif_start_queue(ecm_ipa_ctx->net);
+	}
+	ECM_IPA_LOG_EXIT();
+}
+
+static int ecm_ipa_register_pm_client(struct ecm_ipa_dev *ecm_ipa_ctx)
+{
+	int result;
+	struct ipa_pm_register_params pm_reg;
+
+	memset(&pm_reg, 0, sizeof(pm_reg));
+	pm_reg.name = ecm_ipa_ctx->net->name;
+	pm_reg.user_data = ecm_ipa_ctx;
+	pm_reg.callback = ecm_ipa_pm_cb;
+	pm_reg.group = IPA_PM_GROUP_APPS;
+	result = ipa_pm_register(&pm_reg, &ecm_ipa_ctx->pm_hdl);
+	if (result) {
+		ECM_IPA_ERROR("failed to create IPA PM client %d\n", result);
+		return result;
+	}
+	return 0;
+}
+
+static void ecm_ipa_deregister_pm_client(struct ecm_ipa_dev *ecm_ipa_ctx)
+{
+	ipa_pm_deactivate_sync(ecm_ipa_ctx->pm_hdl);
+	ipa_pm_deregister(ecm_ipa_ctx->pm_hdl);
+	ecm_ipa_ctx->pm_hdl = ~0;
+}
+
 static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx)
 {
+	if (ipa_pm_is_used())
+		return ipa_pm_activate(ecm_ipa_ctx->pm_hdl);
+
 	return ipa_rm_inactivity_timer_request_resource(
 		IPA_RM_RESOURCE_STD_ECM_PROD);
 }
 
 static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx)
 {
-	ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
+	if (ipa_pm_is_used())
+		ipa_pm_deferred_deactivate(ecm_ipa_ctx->pm_hdl);
+	else
+		ipa_rm_inactivity_timer_release_resource(
+			IPA_RM_RESOURCE_STD_ECM_PROD);
 }
 
 /**
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 9b3b53d..7a683ec 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -19,6 +19,7 @@
 #include <linux/ipa_qmi_service_v01.h>
 #include <linux/ipa_mhi.h>
 #include "../ipa_common_i.h"
+#include "../ipa_v3/ipa_pm.h"
 
 #define IPA_MHI_DRV_NAME "ipa_mhi_client"
 #define IPA_MHI_DBG(fmt, args...) \
@@ -40,11 +41,6 @@
 #define IPA_MHI_MAX_UL_CHANNELS 1
 #define IPA_MHI_MAX_DL_CHANNELS 1
 
-#if (IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) > \
-	(IPA_MHI_GSI_ER_END - IPA_MHI_GSI_ER_START)
-#error not enought event rings for MHI
-#endif
-
 /* bit #40 in address should be asserted for MHI transfers over pcie */
 #define IPA_MHI_CLIENT_HOST_ADDR_COND(addr) \
 	((ipa_mhi_client_ctx->assert_bit40)?(IPA_MHI_HOST_ADDR(addr)):(addr))
@@ -136,6 +132,8 @@
 	u32 use_ipadma;
 	bool assert_bit40;
 	bool test_mode;
+	u32 pm_hdl;
+	u32 modem_pm_hdl;
 };
 
 static struct ipa_mhi_client_ctx *ipa_mhi_client_ctx;
@@ -182,6 +180,12 @@
 			return -ENOMEM;
 		}
 
+		res = ipa_dma_enable();
+		if (res) {
+			IPA_MHI_ERR("failed to enable IPA DMA rc=%d\n", res);
+			goto fail_dma_enable;
+		}
+
 		if (dir == IPA_MHI_DMA_FROM_HOST) {
 			res = ipa_dma_sync_memcpy(mem.phys_base, host_addr,
 				size);
@@ -203,8 +207,7 @@
 				goto fail_memcopy;
 			}
 		}
-		dma_free_coherent(pdev, mem.size, mem.base,
-			mem.phys_base);
+		goto dma_succeed;
 	} else {
 		void *host_ptr;
 
@@ -227,9 +230,14 @@
 	IPA_MHI_FUNC_EXIT();
 	return 0;
 
+dma_succeed:
+	IPA_MHI_FUNC_EXIT();
+	res = 0;
 fail_memcopy:
-	dma_free_coherent(ipa_get_dma_dev(), mem.size, mem.base,
-			mem.phys_base);
+	if (ipa_dma_disable())
+		IPA_MHI_ERR("failed to disable IPA DMA\n");
+fail_dma_enable:
+	dma_free_coherent(pdev, mem.size, mem.base, mem.phys_base);
 	return res;
 }
 
@@ -834,25 +842,38 @@
 	IPA_MHI_DBG("event_context_array_addr 0x%llx\n",
 		ipa_mhi_client_ctx->event_context_array_addr);
 
-	/* Add MHI <-> Q6 dependencies to IPA RM */
-	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
-		IPA_RM_RESOURCE_Q6_CONS);
-	if (res && res != -EINPROGRESS) {
-		IPA_MHI_ERR("failed to add dependency %d\n", res);
-		goto fail_add_mhi_q6_dep;
-	}
+	if (ipa_pm_is_used()) {
+		res = ipa_pm_activate_sync(ipa_mhi_client_ctx->pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("failed activate client %d\n", res);
+			goto fail_pm_activate;
+		}
+		res = ipa_pm_activate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("failed activate modem client %d\n", res);
+			goto fail_pm_activate_modem;
+		}
+	} else {
+		/* Add MHI <-> Q6 dependencies to IPA RM */
+		res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
+			IPA_RM_RESOURCE_Q6_CONS);
+		if (res && res != -EINPROGRESS) {
+			IPA_MHI_ERR("failed to add dependency %d\n", res);
+			goto fail_add_mhi_q6_dep;
+		}
 
-	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
-		IPA_RM_RESOURCE_MHI_CONS);
-	if (res && res != -EINPROGRESS) {
-		IPA_MHI_ERR("failed to add dependency %d\n", res);
-		goto fail_add_q6_mhi_dep;
-	}
+		res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
+			IPA_RM_RESOURCE_MHI_CONS);
+		if (res && res != -EINPROGRESS) {
+			IPA_MHI_ERR("failed to add dependency %d\n", res);
+			goto fail_add_q6_mhi_dep;
+		}
 
-	res = ipa_mhi_request_prod();
-	if (res) {
-		IPA_MHI_ERR("failed request prod %d\n", res);
-		goto fail_request_prod;
+		res = ipa_mhi_request_prod();
+		if (res) {
+			IPA_MHI_ERR("failed request prod %d\n", res);
+			goto fail_request_prod;
+		}
 	}
 
 	/* gsi params */
@@ -880,14 +901,23 @@
 	return 0;
 
 fail_init_engine:
-	ipa_mhi_release_prod();
+	if (!ipa_pm_is_used())
+		ipa_mhi_release_prod();
 fail_request_prod:
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
-		IPA_RM_RESOURCE_MHI_CONS);
+	if (!ipa_pm_is_used())
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+			IPA_RM_RESOURCE_MHI_CONS);
 fail_add_q6_mhi_dep:
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
-		IPA_RM_RESOURCE_Q6_CONS);
+	if (!ipa_pm_is_used())
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
+			IPA_RM_RESOURCE_Q6_CONS);
 fail_add_mhi_q6_dep:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+fail_pm_activate_modem:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->pm_hdl);
+fail_pm_activate:
 	ipa_mhi_set_state(IPA_MHI_STATE_INITIALIZED);
 	return res;
 }
@@ -1539,8 +1569,7 @@
 		internal.start.gsi.mhi = &channel->ch_scratch.mhi;
 		internal.start.gsi.cached_gsi_evt_ring_hdl =
 				&channel->cached_gsi_evt_ring_hdl;
-		internal.start.gsi.evchid =
-				channel->index + IPA_MHI_GSI_ER_START;
+		internal.start.gsi.evchid = channel->index;
 
 		res = ipa_connect_mhi_pipe(&internal, clnt_hdl);
 		if (res) {
@@ -2095,20 +2124,32 @@
 	 */
 	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 
-	IPA_MHI_DBG("release prod\n");
-	res = ipa_mhi_release_prod();
-	if (res) {
-		IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
-		goto fail_release_prod;
-	}
+	if (ipa_pm_is_used()) {
+		res = ipa_pm_deactivate_sync(ipa_mhi_client_ctx->pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("fail to deactivate client %d\n", res);
+			goto fail_deactivate_pm;
+		}
+		res = ipa_pm_deactivate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("fail to deactivate client %d\n", res);
+			goto fail_deactivate_modem_pm;
+		}
+	} else {
+		IPA_MHI_DBG("release prod\n");
+		res = ipa_mhi_release_prod();
+		if (res) {
+			IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
+			goto fail_release_prod;
+		}
 
-	IPA_MHI_DBG("wait for cons release\n");
-	res = ipa_mhi_wait_for_cons_release();
-	if (res) {
-		IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed %d\n", res);
-		goto fail_release_cons;
+		IPA_MHI_DBG("wait for cons release\n");
+		res = ipa_mhi_wait_for_cons_release();
+		if (res) {
+			IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed\n");
+			goto fail_release_cons;
+		}
 	}
-
 	usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN, IPA_MHI_SUSPEND_SLEEP_MAX);
 
 	res = ipa_mhi_suspend_dl(force);
@@ -2132,8 +2173,15 @@
 
 fail_suspend_dl_channel:
 fail_release_cons:
+	if (!ipa_pm_is_used())
 	ipa_mhi_request_prod();
 fail_release_prod:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+fail_deactivate_modem_pm:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->pm_hdl);
+fail_deactivate_pm:
 	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 fail_suspend_ul_channel:
 	ipa_mhi_resume_channels(true, ipa_mhi_client_ctx->ul_channels);
@@ -2193,10 +2241,23 @@
 		ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
 	}
 
-	res = ipa_mhi_request_prod();
-	if (res) {
-		IPA_MHI_ERR("ipa_mhi_request_prod failed %d\n", res);
-		goto fail_request_prod;
+	if (ipa_pm_is_used()) {
+		res = ipa_pm_activate_sync(ipa_mhi_client_ctx->pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("fail to activate client %d\n", res);
+			goto fail_pm_activate;
+		}
+		ipa_pm_activate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+		if (res) {
+			IPA_MHI_ERR("fail to activate client %d\n", res);
+			goto fail_pm_activate_modem;
+		}
+	} else {
+		res = ipa_mhi_request_prod();
+		if (res) {
+			IPA_MHI_ERR("ipa_mhi_request_prod failed %d\n", res);
+			goto fail_request_prod;
+		}
 	}
 
 	/* resume all UL channels */
@@ -2234,8 +2295,15 @@
 fail_resume_dl_channels2:
 	ipa_mhi_suspend_channels(ipa_mhi_client_ctx->ul_channels);
 fail_resume_ul_channels:
-	ipa_mhi_release_prod();
+	if (!ipa_pm_is_used())
+		ipa_mhi_release_prod();
 fail_request_prod:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+fail_pm_activate_modem:
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(ipa_mhi_client_ctx->pm_hdl);
+fail_pm_activate:
 	ipa_mhi_suspend_channels(ipa_mhi_client_ctx->dl_channels);
 fail_resume_dl_channels:
 	ipa_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
@@ -2319,6 +2387,85 @@
 	debugfs_remove_recursive(dent);
 }
 
+static void ipa_mhi_delete_rm_resources(void)
+{
+	int res;
+
+	if (ipa_mhi_client_ctx->state != IPA_MHI_STATE_INITIALIZED  &&
+		ipa_mhi_client_ctx->state != IPA_MHI_STATE_READY) {
+
+		IPA_MHI_DBG("release prod\n");
+		res = ipa_mhi_release_prod();
+		if (res) {
+			IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n",
+				res);
+			goto fail;
+		}
+		IPA_MHI_DBG("wait for cons release\n");
+		res = ipa_mhi_wait_for_cons_release();
+		if (res) {
+			IPA_MHI_ERR("ipa_mhi_wait_for_cons_release%d\n",
+				res);
+			goto fail;
+		}
+
+		usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN,
+			IPA_MHI_SUSPEND_SLEEP_MAX);
+
+		IPA_MHI_DBG("deleate dependency Q6_PROD->MHI_CONS\n");
+		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+			IPA_RM_RESOURCE_MHI_CONS);
+		if (res) {
+			IPA_MHI_ERR(
+				"Error deleting dependency %d->%d, res=%d\n",
+				IPA_RM_RESOURCE_Q6_PROD,
+				IPA_RM_RESOURCE_MHI_CONS,
+				res);
+			goto fail;
+		}
+		IPA_MHI_DBG("deleate dependency MHI_PROD->Q6_CONS\n");
+		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
+			IPA_RM_RESOURCE_Q6_CONS);
+		if (res) {
+			IPA_MHI_ERR(
+				"Error deleting dependency %d->%d, res=%d\n",
+				IPA_RM_RESOURCE_MHI_PROD,
+				IPA_RM_RESOURCE_Q6_CONS,
+				res);
+			goto fail;
+		}
+	}
+
+	res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
+	if (res) {
+		IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
+			IPA_RM_RESOURCE_MHI_PROD, res);
+		goto fail;
+	}
+
+	res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
+	if (res) {
+		IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
+			IPA_RM_RESOURCE_MHI_CONS, res);
+		goto fail;
+	}
+
+	return;
+fail:
+	ipa_assert();
+}
+
+static void ipa_mhi_deregister_pm(void)
+{
+	ipa_pm_deactivate_sync(ipa_mhi_client_ctx->pm_hdl);
+	ipa_pm_deregister(ipa_mhi_client_ctx->pm_hdl);
+	ipa_mhi_client_ctx->pm_hdl = ~0;
+
+	ipa_pm_deactivate_sync(ipa_mhi_client_ctx->modem_pm_hdl);
+	ipa_pm_deregister(ipa_mhi_client_ctx->modem_pm_hdl);
+	ipa_mhi_client_ctx->modem_pm_hdl = ~0;
+}
+
 /**
  * ipa_mhi_destroy() - Destroy MHI IPA
  *
@@ -2351,63 +2498,12 @@
 		ipa_uc_mhi_cleanup();
 	}
 
+	if (ipa_pm_is_used())
+		ipa_mhi_deregister_pm();
+	else
+		ipa_mhi_delete_rm_resources();
 
-	if (ipa_mhi_client_ctx->state != IPA_MHI_STATE_INITIALIZED  &&
-			ipa_mhi_client_ctx->state != IPA_MHI_STATE_READY) {
-		IPA_MHI_DBG("release prod\n");
-		res = ipa_mhi_release_prod();
-		if (res) {
-			IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
-			goto fail;
-		}
-		IPA_MHI_DBG("wait for cons release\n");
-		res = ipa_mhi_wait_for_cons_release();
-		if (res) {
-			IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed %d\n",
-				res);
-			goto fail;
-		}
-		usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN,
-				IPA_MHI_SUSPEND_SLEEP_MAX);
-
-		IPA_MHI_DBG("deleate dependency Q6_PROD->MHI_CONS\n");
-		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
-			IPA_RM_RESOURCE_MHI_CONS);
-		if (res) {
-			IPA_MHI_ERR(
-				"Error deleting dependency %d->%d, res=%d\n"
-				, IPA_RM_RESOURCE_Q6_PROD,
-				IPA_RM_RESOURCE_MHI_CONS,
-				res);
-			goto fail;
-		}
-		IPA_MHI_DBG("deleate dependency MHI_PROD->Q6_CONS\n");
-		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
-			IPA_RM_RESOURCE_Q6_CONS);
-		if (res) {
-			IPA_MHI_ERR(
-				"Error deleting dependency %d->%d, res=%d\n",
-			IPA_RM_RESOURCE_MHI_PROD,
-			IPA_RM_RESOURCE_Q6_CONS,
-			res);
-			goto fail;
-		}
-	}
-
-	res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
-	if (res) {
-		IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
-			IPA_RM_RESOURCE_MHI_PROD, res);
-		goto fail;
-	}
-
-	res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
-	if (res) {
-		IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
-			IPA_RM_RESOURCE_MHI_CONS, res);
-		goto fail;
-	}
-
+	ipa_dma_destroy();
 	ipa_mhi_debugfs_destroy();
 	destroy_workqueue(ipa_mhi_client_ctx->wq);
 	kfree(ipa_mhi_client_ctx);
@@ -2420,6 +2516,132 @@
 	ipa_assert();
 }
 
+static void ipa_mhi_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	unsigned long flags;
+
+	IPA_MHI_FUNC_ENTRY();
+
+	if (event != IPA_PM_REQUEST_WAKEUP) {
+		IPA_MHI_ERR("Unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+
+	IPA_MHI_DBG("%s\n", MHI_STATE_STR(ipa_mhi_client_ctx->state));
+	spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+	if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_SUSPENDED) {
+		ipa_mhi_notify_wakeup();
+	} else if (ipa_mhi_client_ctx->state ==
+		IPA_MHI_STATE_SUSPEND_IN_PROGRESS) {
+		/* wakeup event will be trigger after suspend finishes */
+		ipa_mhi_client_ctx->trigger_wakeup = true;
+	}
+	spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+	IPA_MHI_DBG("EXIT");
+}
+
+static int ipa_mhi_register_pm(void)
+{
+	int res;
+	struct ipa_pm_register_params params;
+
+	memset(&params, 0, sizeof(params));
+	params.name = "MHI";
+	params.callback = ipa_mhi_pm_cb;
+	params.group = IPA_PM_GROUP_DEFAULT;
+	res = ipa_pm_register(&params, &ipa_mhi_client_ctx->pm_hdl);
+	if (res) {
+		IPA_MHI_ERR("fail to register with PM %d\n", res);
+		return res;
+	}
+
+	res = ipa_pm_associate_ipa_cons_to_client(ipa_mhi_client_ctx->pm_hdl,
+		IPA_CLIENT_MHI_CONS);
+	if (res) {
+		IPA_MHI_ERR("fail to associate cons with PM %d\n", res);
+		goto fail_pm_cons;
+	}
+
+	res = ipa_pm_set_perf_profile(ipa_mhi_client_ctx->pm_hdl, 1000);
+	if (res) {
+		IPA_MHI_ERR("fail to set perf profile to PM %d\n", res);
+		goto fail_pm_cons;
+	}
+
+	/* create a modem client for clock scaling */
+	memset(&params, 0, sizeof(params));
+	params.name = "MODEM (MHI)";
+	params.group = IPA_PM_GROUP_MODEM;
+	params.skip_clk_vote = true;
+	res = ipa_pm_register(&params, &ipa_mhi_client_ctx->modem_pm_hdl);
+	if (res) {
+		IPA_MHI_ERR("fail to register with PM %d\n", res);
+		goto fail_pm_cons;
+	}
+
+	return 0;
+
+fail_pm_cons:
+	ipa_pm_deregister(ipa_mhi_client_ctx->pm_hdl);
+	ipa_mhi_client_ctx->pm_hdl = ~0;
+	return res;
+}
+
+static int ipa_mhi_create_rm_resources(void)
+{
+	int res;
+	struct ipa_rm_create_params mhi_prod_params;
+	struct ipa_rm_create_params mhi_cons_params;
+	struct ipa_rm_perf_profile profile;
+
+	/* Create PROD in IPA RM */
+	memset(&mhi_prod_params, 0, sizeof(mhi_prod_params));
+	mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD;
+	mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS;
+	mhi_prod_params.reg_params.notify_cb = ipa_mhi_rm_prod_notify;
+	res = ipa_rm_create_resource(&mhi_prod_params);
+	if (res) {
+		IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n");
+		goto fail_create_rm_prod;
+	}
+
+	memset(&profile, 0, sizeof(profile));
+	profile.max_supported_bandwidth_mbps = 1000;
+	res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_PROD, &profile);
+	if (res) {
+		IPA_MHI_ERR("fail to set profile to MHI_PROD\n");
+		goto fail_perf_rm_prod;
+	}
+
+	/* Create CONS in IPA RM */
+	memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
+	mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
+	mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS;
+	mhi_cons_params.request_resource = ipa_mhi_rm_cons_request;
+	mhi_cons_params.release_resource = ipa_mhi_rm_cons_release;
+	res = ipa_rm_create_resource(&mhi_cons_params);
+	if (res) {
+		IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n");
+		goto fail_create_rm_cons;
+	}
+
+	memset(&profile, 0, sizeof(profile));
+	profile.max_supported_bandwidth_mbps = 1000;
+	res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_CONS, &profile);
+	if (res) {
+		IPA_MHI_ERR("fail to set profile to MHI_CONS\n");
+		goto fail_perf_rm_cons;
+	}
+fail_perf_rm_cons:
+	ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
+fail_create_rm_cons:
+fail_perf_rm_prod:
+	ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
+fail_create_rm_prod:
+	return res;
+}
+
 /**
  * ipa_mhi_init() - Initialize IPA MHI driver
  * @params: initialization params
@@ -2437,9 +2659,6 @@
 int ipa_mhi_init(struct ipa_mhi_init_params *params)
 {
 	int res;
-	struct ipa_rm_create_params mhi_prod_params;
-	struct ipa_rm_create_params mhi_cons_params;
-	struct ipa_rm_perf_profile profile;
 
 	IPA_MHI_FUNC_ENTRY();
 
@@ -2500,43 +2719,20 @@
 		goto fail_create_wq;
 	}
 
-	/* Create PROD in IPA RM */
-	memset(&mhi_prod_params, 0, sizeof(mhi_prod_params));
-	mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD;
-	mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS;
-	mhi_prod_params.reg_params.notify_cb = ipa_mhi_rm_prod_notify;
-	res = ipa_rm_create_resource(&mhi_prod_params);
+	res = ipa_dma_init();
 	if (res) {
-		IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n");
-		goto fail_create_rm_prod;
+		IPA_MHI_ERR("failed to init ipa dma %d\n", res);
+		goto fail_dma_init;
 	}
 
-	memset(&profile, 0, sizeof(profile));
-	profile.max_supported_bandwidth_mbps = 1000;
-	res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_PROD, &profile);
+	if (ipa_pm_is_used())
+		res = ipa_mhi_register_pm();
+	else
+		res = ipa_mhi_create_rm_resources();
 	if (res) {
-		IPA_MHI_ERR("fail to set profile to MHI_PROD\n");
-		goto fail_perf_rm_prod;
-	}
-
-	/* Create CONS in IPA RM */
-	memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
-	mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
-	mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS;
-	mhi_cons_params.request_resource = ipa_mhi_rm_cons_request;
-	mhi_cons_params.release_resource = ipa_mhi_rm_cons_release;
-	res = ipa_rm_create_resource(&mhi_cons_params);
-	if (res) {
-		IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n");
-		goto fail_create_rm_cons;
-	}
-
-	memset(&profile, 0, sizeof(profile));
-	profile.max_supported_bandwidth_mbps = 1000;
-	res = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_MHI_CONS, &profile);
-	if (res) {
-		IPA_MHI_ERR("fail to set profile to MHI_CONS\n");
-		goto fail_perf_rm_cons;
+		IPA_MHI_ERR("failed to create RM resources\n");
+		res = -EFAULT;
+		goto fail_rm;
 	}
 
 	/* Initialize uC interface */
@@ -2551,12 +2747,9 @@
 	IPA_MHI_FUNC_EXIT();
 	return 0;
 
-fail_perf_rm_cons:
-	ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
-fail_create_rm_cons:
-fail_perf_rm_prod:
-	ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
-fail_create_rm_prod:
+fail_rm:
+	ipa_dma_destroy();
+fail_dma_init:
 	destroy_workqueue(ipa_mhi_client_ctx->wq);
 fail_create_wq:
 	kfree(ipa_mhi_client_ctx);
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
index a15a9d8..e19d297 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_uc_offload.c
@@ -13,6 +13,7 @@
 #include <linux/ipa_uc_offload.h>
 #include <linux/msm_ipa.h>
 #include "../ipa_common_i.h"
+#include "../ipa_v3/ipa_pm.h"
 
 #define IPA_NTN_DMA_POOL_ALIGNMENT 8
 #define OFFLOAD_DRV_NAME "ipa_uc_offload"
@@ -69,6 +70,7 @@
 	char netdev_name[IPA_RESOURCE_NAME_MAX];
 	ipa_notify_cb notify;
 	struct completion ntn_completion;
+	u32 pm_hdl;
 };
 
 static struct ipa_uc_offload_ctx *ipa_uc_offload_ctx[IPA_UC_MAX_PROT_SIZE];
@@ -113,22 +115,53 @@
 	return 0;
 }
 
-static int ipa_uc_offload_ntn_reg_intf(
-	struct ipa_uc_offload_intf_params *inp,
-	struct ipa_uc_offload_out_params *outp,
+static void ipa_uc_offload_ntn_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	/* suspend/resume is not supported */
+	IPA_UC_OFFLOAD_DBG("event = %d\n", event);
+}
+
+static int ipa_uc_offload_ntn_register_pm_client(
 	struct ipa_uc_offload_ctx *ntn_ctx)
 {
-	struct ipa_ioc_add_hdr *hdr = NULL;
-	struct ipa_tx_intf tx;
-	struct ipa_rx_intf rx;
-	struct ipa_ioc_tx_intf_prop tx_prop[2];
-	struct ipa_ioc_rx_intf_prop rx_prop[2];
-	struct ipa_rm_create_params param;
-	u32 len;
-	int ret = 0;
+	int res;
+	struct ipa_pm_register_params params;
 
-	IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n",
-					 inp->netdev_name);
+	memset(&params, 0, sizeof(params));
+	params.name = "ETH";
+	params.callback = ipa_uc_offload_ntn_pm_cb;
+	params.user_data = ntn_ctx;
+	params.group = IPA_PM_GROUP_DEFAULT;
+	res = ipa_pm_register(&params, &ntn_ctx->pm_hdl);
+	if (res) {
+		IPA_UC_OFFLOAD_ERR("fail to register with PM %d\n", res);
+		return res;
+	}
+
+	res = ipa_pm_associate_ipa_cons_to_client(ntn_ctx->pm_hdl,
+		IPA_CLIENT_ETHERNET_CONS);
+	if (res) {
+		IPA_UC_OFFLOAD_ERR("fail to associate cons with PM %d\n", res);
+		ipa_pm_deregister(ntn_ctx->pm_hdl);
+		ntn_ctx->pm_hdl = ~0;
+		return res;
+	}
+
+	return 0;
+}
+
+static void ipa_uc_offload_ntn_deregister_pm_client(
+	struct ipa_uc_offload_ctx *ntn_ctx)
+{
+	ipa_pm_deactivate_sync(ntn_ctx->pm_hdl);
+	ipa_pm_deregister(ntn_ctx->pm_hdl);
+}
+static int ipa_uc_offload_ntn_create_rm_resources(
+	struct ipa_uc_offload_ctx *ntn_ctx)
+{
+	int ret;
+	struct ipa_rm_create_params param;
+
 	memset(&param, 0, sizeof(param));
 	param.name = IPA_RM_RESOURCE_ETHERNET_PROD;
 	param.reg_params.user_data = ntn_ctx;
@@ -147,9 +180,37 @@
 	ret = ipa_rm_create_resource(&param);
 	if (ret) {
 		IPA_UC_OFFLOAD_ERR("fail to create ETHERNET_CONS resource\n");
-		goto fail_create_rm_cons;
+		ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
+		return -EFAULT;
 	}
 
+	return 0;
+}
+
+static int ipa_uc_offload_ntn_reg_intf(
+	struct ipa_uc_offload_intf_params *inp,
+	struct ipa_uc_offload_out_params *outp,
+	struct ipa_uc_offload_ctx *ntn_ctx)
+{
+	struct ipa_ioc_add_hdr *hdr = NULL;
+	struct ipa_tx_intf tx;
+	struct ipa_rx_intf rx;
+	struct ipa_ioc_tx_intf_prop tx_prop[2];
+	struct ipa_ioc_rx_intf_prop rx_prop[2];
+	int ret = 0;
+	u32 len;
+
+
+	IPA_UC_OFFLOAD_DBG("register interface for netdev %s\n",
+					 inp->netdev_name);
+	if (ipa_pm_is_used())
+		ret = ipa_uc_offload_ntn_register_pm_client(ntn_ctx);
+	else
+		ret = ipa_uc_offload_ntn_create_rm_resources(ntn_ctx);
+	if (ret) {
+		IPA_UC_OFFLOAD_ERR("fail to create rm resource\n");
+		return -EFAULT;
+	}
 	memcpy(ntn_ctx->netdev_name, inp->netdev_name, IPA_RESOURCE_NAME_MAX);
 	ntn_ctx->hdr_len = inp->hdr_info[0].hdr_len;
 	ntn_ctx->notify = inp->notify;
@@ -228,9 +289,12 @@
 fail:
 	kfree(hdr);
 fail_alloc:
-	ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_CONS);
-fail_create_rm_cons:
-	ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
+	if (ipa_pm_is_used()) {
+		ipa_uc_offload_ntn_deregister_pm_client(ntn_ctx);
+	} else {
+		ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_CONS);
+		ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
+	}
 	return ret;
 }
 
@@ -348,25 +412,34 @@
 		return -EINVAL;
 	}
 
-	result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
-		IPA_RM_RESOURCE_APPS_CONS);
-	if (result) {
-		IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n", result);
-		return result;
-	}
+	if (ipa_pm_is_used()) {
+		result = ipa_pm_activate_sync(ntn_ctx->pm_hdl);
+		if (result) {
+			IPA_UC_OFFLOAD_ERR("fail to activate: %d\n", result);
+			return result;
+		}
+	} else {
+		result = ipa_rm_add_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
+			IPA_RM_RESOURCE_APPS_CONS);
+		if (result) {
+			IPA_UC_OFFLOAD_ERR("fail to add rm dependency: %d\n",
+				result);
+			return result;
+		}
 
-	result = ipa_rm_request_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
-	if (result == -EINPROGRESS) {
-		if (wait_for_completion_timeout(&ntn_ctx->ntn_completion,
-			10*HZ) == 0) {
-			IPA_UC_OFFLOAD_ERR("ETH_PROD resource req time out\n");
+		result = ipa_rm_request_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
+		if (result == -EINPROGRESS) {
+			if (wait_for_completion_timeout(&ntn_ctx->ntn_completion
+				, 10*HZ) == 0) {
+				IPA_UC_OFFLOAD_ERR("ETH_PROD req timeout\n");
+				result = -EFAULT;
+				goto fail;
+			}
+		} else if (result != 0) {
+			IPA_UC_OFFLOAD_ERR("fail to request resource\n");
 			result = -EFAULT;
 			goto fail;
 		}
-	} else if (result != 0) {
-		IPA_UC_OFFLOAD_ERR("fail to request resource\n");
-		result = -EFAULT;
-		goto fail;
 	}
 
 	ntn_ctx->state = IPA_UC_OFFLOAD_STATE_UP;
@@ -383,8 +456,9 @@
 	return 0;
 
 fail:
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
-		IPA_RM_RESOURCE_APPS_CONS);
+	if (!ipa_pm_is_used())
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
+			IPA_RM_RESOURCE_APPS_CONS);
 	return result;
 }
 
@@ -455,6 +529,11 @@
 		return -EINVAL;
 	}
 
+	if (ipa_pm_is_used())
+		return ipa_pm_set_perf_profile(
+			ipa_uc_offload_ctx[IPA_UC_NTN]->pm_hdl,
+			profile->max_supported_bw_mbps);
+
 	if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) {
 		IPA_UC_OFFLOAD_ERR("fail to setup rm perf profile\n");
 		return -EFAULT;
@@ -471,18 +550,27 @@
 
 	ntn_ctx->state = IPA_UC_OFFLOAD_STATE_INITIALIZED;
 
-	ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
-	if (ret) {
-		IPA_UC_OFFLOAD_ERR("fail to release ETHERNET_PROD res: %d\n",
-						  ret);
-		return -EFAULT;
-	}
+	if (ipa_pm_is_used()) {
+		ret = ipa_pm_deactivate_sync(ntn_ctx->pm_hdl);
+		if (ret) {
+			IPA_UC_OFFLOAD_ERR("fail to deactivate res: %d\n",
+			ret);
+			return -EFAULT;
+		}
+	} else {
+		ret = ipa_rm_release_resource(IPA_RM_RESOURCE_ETHERNET_PROD);
+		if (ret) {
+			IPA_UC_OFFLOAD_ERR("fail release ETHERNET_PROD: %d\n",
+							ret);
+			return -EFAULT;
+		}
 
-	ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
-		IPA_RM_RESOURCE_APPS_CONS);
-	if (ret) {
-		IPA_UC_OFFLOAD_ERR("fail to del dep ETH_PROD->APPS, %d\n", ret);
-		return -EFAULT;
+		ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ETHERNET_PROD,
+			IPA_RM_RESOURCE_APPS_CONS);
+		if (ret) {
+			IPA_UC_OFFLOAD_ERR("fail del dep ETH->APPS, %d\n", ret);
+			return -EFAULT;
+		}
 	}
 
 	ipa_ep_idx_ul = ipa_get_ep_mapping(IPA_CLIENT_ETHERNET_PROD);
@@ -539,14 +627,18 @@
 	int len, result = 0;
 	struct ipa_ioc_del_hdr *hdr;
 
-	if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_PROD)) {
-		IPA_UC_OFFLOAD_ERR("fail to delete ETHERNET_PROD resource\n");
-		return -EFAULT;
-	}
+	if (ipa_pm_is_used()) {
+		ipa_uc_offload_ntn_deregister_pm_client(ntn_ctx);
+	} else {
+		if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_PROD)) {
+			IPA_UC_OFFLOAD_ERR("fail to delete ETHERNET_PROD\n");
+			return -EFAULT;
+		}
 
-	if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_CONS)) {
-		IPA_UC_OFFLOAD_ERR("fail to delete ETHERNET_CONS resource\n");
-		return -EFAULT;
+		if (ipa_rm_delete_resource(IPA_RM_RESOURCE_ETHERNET_CONS)) {
+			IPA_UC_OFFLOAD_ERR("fail to delete ETHERNET_CONS\n");
+			return -EFAULT;
+		}
 	}
 
 	len = sizeof(struct ipa_ioc_del_hdr) + 2 * sizeof(struct ipa_hdr_del);
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
index f5d8d61..745e429 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_usb.c
@@ -119,6 +119,12 @@
 	bool cons_requested_released;
 };
 
+struct ipa3_usb_pm_context {
+	struct ipa_pm_register_params reg_params;
+	struct work_struct *remote_wakeup_work;
+	u32 hdl;
+};
+
 enum ipa3_usb_state {
 	IPA_USB_INVALID,
 	IPA_USB_INITIALIZED,
@@ -157,6 +163,7 @@
  */
 struct ipa3_usb_transport_type_ctx {
 	struct ipa3_usb_rm_context rm_ctx;
+	struct ipa3_usb_pm_context pm_ctx;
 	int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *user_data);
 	void *user_data;
 	enum ipa3_usb_state state;
@@ -362,7 +369,8 @@
 			ipa3_usb_state_to_string(new_state));
 	}
 
-	if (state_legal && (new_state == IPA_USB_CONNECTED)) {
+	if (!ipa_pm_is_used() &&
+		state_legal && (new_state == IPA_USB_CONNECTED)) {
 		rm_ctx = &ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx;
 		if ((rm_ctx->cons_state == IPA_USB_CONS_GRANTED) ||
 			rm_ctx->cons_requested_released) {
@@ -656,6 +664,30 @@
 	return ipa3_usb_cons_release_resource_cb_do(IPA_USB_TRANSPORT_DPL);
 }
 
+static void ipa3_usb_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	struct ipa3_usb_transport_type_ctx *ttype_ctx =
+		(struct ipa3_usb_transport_type_ctx *)p;
+	unsigned long flags;
+
+	IPA_USB_DBG_LOW("entry\n");
+
+	if (event != IPA_PM_REQUEST_WAKEUP) {
+		IPA_USB_ERR("Unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+
+	spin_lock_irqsave(&ipa3_usb_ctx->state_lock, flags);
+	IPA_USB_DBG("state is %s\n",
+		ipa3_usb_state_to_string(ttype_ctx->state));
+	if (ttype_ctx->state == IPA_USB_SUSPENDED)
+		queue_work(ipa3_usb_ctx->wq,
+			ttype_ctx->pm_ctx.remote_wakeup_work);
+	spin_unlock_irqrestore(&ipa3_usb_ctx->state_lock, flags);
+	IPA_USB_DBG_LOW("exit\n");
+}
+
 static char *ipa3_usb_teth_prot_to_string(enum ipa_usb_teth_prot teth_prot)
 {
 	switch (teth_prot) {
@@ -703,6 +735,59 @@
 	return 0;
 }
 
+static int ipa3_usb_register_pm(enum ipa3_usb_transport_type ttype)
+{
+	struct ipa3_usb_transport_type_ctx *ttype_ctx =
+		&ipa3_usb_ctx->ttype_ctx[ttype];
+	int result;
+
+	memset(&ttype_ctx->pm_ctx.reg_params, 0,
+		sizeof(ttype_ctx->pm_ctx.reg_params));
+	ttype_ctx->pm_ctx.reg_params.name = (ttype == IPA_USB_TRANSPORT_DPL) ?
+				"USB DPL" : "USB";
+	ttype_ctx->pm_ctx.reg_params.callback = ipa3_usb_pm_cb;
+	ttype_ctx->pm_ctx.reg_params.user_data = ttype_ctx;
+	ttype_ctx->pm_ctx.reg_params.group = IPA_PM_GROUP_DEFAULT;
+
+	result = ipa_pm_register(&ttype_ctx->pm_ctx.reg_params,
+		&ttype_ctx->pm_ctx.hdl);
+	if (result) {
+		IPA_USB_ERR("fail to register with PM %d\n", result);
+		goto fail_pm_reg;
+	}
+
+	result = ipa_pm_associate_ipa_cons_to_client(ttype_ctx->pm_ctx.hdl,
+		(ttype == IPA_USB_TRANSPORT_DPL) ?
+		IPA_CLIENT_USB_DPL_CONS : IPA_CLIENT_USB_CONS);
+	if (result) {
+		IPA_USB_ERR("fail to associate cons with PM %d\n", result);
+		goto fail_pm_cons;
+	}
+
+	return 0;
+
+fail_pm_cons:
+	ipa_pm_deregister(ttype_ctx->pm_ctx.hdl);
+fail_pm_reg:
+	memset(&ttype_ctx->pm_ctx.reg_params, 0,
+		sizeof(ttype_ctx->pm_ctx.reg_params));
+	return result;
+}
+
+static int ipa3_usb_deregister_pm(enum ipa3_usb_transport_type ttype)
+{
+	struct ipa3_usb_pm_context *pm_ctx =
+		&ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx;
+	int result;
+
+	result = ipa_pm_deregister(pm_ctx->hdl);
+	if (result)
+		return result;
+
+	memset(&pm_ctx->reg_params, 0, sizeof(pm_ctx->reg_params));
+	return 0;
+}
+
 static int ipa3_usb_create_rm_resources(enum ipa3_usb_transport_type ttype)
 {
 	struct ipa3_usb_rm_context *rm_ctx;
@@ -802,7 +887,10 @@
 	}
 
 	/* Create IPA RM USB resources */
-	result = ipa3_usb_create_rm_resources(ttype);
+	if (ipa_pm_is_used())
+		result = ipa3_usb_register_pm(ttype);
+	else
+		result = ipa3_usb_create_rm_resources(ttype);
 	if (result) {
 		IPA_USB_ERR("Failed creating IPA RM USB resources\n");
 		goto bad_params;
@@ -934,12 +1022,18 @@
 teth_prot_init_fail:
 	if ((IPA3_USB_IS_TTYPE_DPL(ttype))
 		|| (ipa3_usb_ctx->num_init_prot == 0)) {
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid = false;
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_valid = false;
-		ipa_rm_delete_resource(
+		if (ipa_pm_is_used()) {
+			ipa3_usb_deregister_pm(ttype);
+		} else {
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid =
+				false;
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_valid =
+				false;
+			ipa_rm_delete_resource(
 			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_params.name);
-		ipa_rm_delete_resource(
+			ipa_rm_delete_resource(
 			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_params.name);
+		}
 	}
 bad_params:
 	mutex_unlock(&ipa3_usb_ctx->general_mutex);
@@ -1393,6 +1487,9 @@
 {
 	int res = 0;
 
+	if (ipa_pm_is_used())
+		return 0;
+
 	/*
 	 * Add DPL dependency to RM dependency graph, first add_dependency call
 	 * is sync in order to make sure the IPA clocks are up before we
@@ -1572,6 +1669,9 @@
 {
 	int res;
 
+	if (ipa_pm_is_used())
+		return 0;
+
 	/* Remove DPL RM dependency */
 	res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_DPL_DUMMY_PROD,
 				    IPA_RM_RESOURCE_Q6_CONS);
@@ -1699,32 +1799,51 @@
 		return result;
 	}
 
-	/* Set RM PROD & CONS perf profile */
-	profile.max_supported_bandwidth_mbps =
-			params->max_supported_bandwidth_mbps;
-	result = ipa_rm_set_perf_profile(
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_params.name,
-		&profile);
-	if (result) {
-		IPA_USB_ERR("failed to set %s perf profile\n",
-			ipa_rm_resource_str(ipa3_usb_ctx->ttype_ctx[ttype].
-				rm_ctx.prod_params.name));
-		return result;
-	}
-	result = ipa_rm_set_perf_profile(
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_params.name,
-		&profile);
-	if (result) {
-		IPA_USB_ERR("failed to set %s perf profile\n",
-			ipa_rm_resource_str(ipa3_usb_ctx->ttype_ctx[ttype].
-				rm_ctx.cons_params.name));
-		return result;
-	}
+	if (ipa_pm_is_used()) {
+		result = ipa_pm_set_perf_profile(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl,
+			params->max_supported_bandwidth_mbps);
+		if (result) {
+			IPA_USB_ERR("failed to set perf profile\n");
+			return result;
+		}
 
-	/* Request PROD */
-	result = ipa3_usb_request_prod(ttype);
-	if (result)
-		return result;
+		result = ipa_pm_activate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+		if (result) {
+			IPA_USB_ERR("failed to activate pm\n");
+			return result;
+		}
+	} else {
+		/* Set RM PROD & CONS perf profile */
+		profile.max_supported_bandwidth_mbps =
+				params->max_supported_bandwidth_mbps;
+		result = ipa_rm_set_perf_profile(
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_params.name,
+			&profile);
+		if (result) {
+			IPA_USB_ERR("failed to set %s perf profile\n",
+				ipa_rm_resource_str(ipa3_usb_ctx->
+					ttype_ctx[ttype].
+					rm_ctx.prod_params.name));
+			return result;
+		}
+		result = ipa_rm_set_perf_profile(
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_params.name,
+			&profile);
+		if (result) {
+			IPA_USB_ERR("failed to set %s perf profile\n",
+				ipa_rm_resource_str(ipa3_usb_ctx->
+					ttype_ctx[ttype].
+					rm_ctx.cons_params.name));
+			return result;
+		}
+
+		/* Request PROD */
+		result = ipa3_usb_request_prod(ttype);
+		if (result)
+			return result;
+	}
 
 	if (params->teth_prot != IPA_USB_DIAG) {
 		/* Start UL channel */
@@ -1775,7 +1894,11 @@
 		ipa3_reset_gsi_event_ring(params->usb_to_ipa_clnt_hdl);
 	}
 connect_ul_fail:
-	ipa3_usb_release_prod(ttype);
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		ipa3_usb_release_prod(ttype);
 	return result;
 }
 
@@ -2224,7 +2347,11 @@
 		goto bad_params;
 
 	if (orig_state != IPA_USB_SUSPENDED) {
-		result = ipa3_usb_release_prod(ttype);
+		if (ipa_pm_is_used())
+			result = ipa_pm_deactivate_sync(
+				ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+		else
+			result = ipa3_usb_release_prod(ttype);
 		if (result) {
 			IPA_USB_ERR("failed to release PROD.\n");
 			goto bad_params;
@@ -2334,13 +2461,19 @@
 		(ipa3_usb_ctx->num_init_prot == 0)) {
 		if (!ipa3_usb_set_state(IPA_USB_INVALID, false, ttype))
 			IPA_USB_ERR("failed to change state to invalid\n");
-		ipa_rm_delete_resource(
+		if (ipa_pm_is_used()) {
+			ipa3_usb_deregister_pm(ttype);
+		} else {
+			ipa_rm_delete_resource(
 			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_params.name);
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid = false;
-		ipa_rm_delete_resource(
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.prod_valid =
+				false;
+			ipa_rm_delete_resource(
 			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_params.name);
-		ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_valid = false;
-		ipa3_usb_ctx->ttype_ctx[ttype].ipa_usb_notify_cb = NULL;
+			ipa3_usb_ctx->ttype_ctx[ttype].rm_ctx.cons_valid =
+				false;
+			ipa3_usb_ctx->ttype_ctx[ttype].ipa_usb_notify_cb = NULL;
+		}
 	}
 
 	IPA_USB_DBG_LOW("exit\n");
@@ -2400,7 +2533,11 @@
 	if (result)
 		goto start_ul;
 
-	result = ipa3_usb_release_prod(ttype);
+	if (ipa_pm_is_used())
+		result = ipa_pm_deactivate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		result = ipa3_usb_release_prod(ttype);
 	if (result) {
 		IPA_USB_ERR("failed to release PROD.\n");
 		goto connect_teth;
@@ -2477,7 +2614,11 @@
 	}
 	ipa3_usb_ctx->qmi_req_id++;
 
-	result = ipa3_usb_release_prod(ttype);
+	if (ipa_pm_is_used())
+		result = ipa_pm_deactivate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		result = ipa3_usb_release_prod(ttype);
 	if (result) {
 		IPA_USB_ERR("failed to release PROD\n");
 		goto release_prod_fail;
@@ -2543,7 +2684,11 @@
 		"DPL channel":"Data Tethering channels");
 
 	/* Request USB_PROD */
-	result = ipa3_usb_request_prod(ttype);
+	if (ipa_pm_is_used())
+		result = ipa_pm_activate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		result = ipa3_usb_request_prod(ttype);
 	if (result)
 		goto fail_exit;
 
@@ -2590,7 +2735,11 @@
 disconn_teth:
 	(void)ipa3_usb_disconnect_teth_prot(teth_prot);
 release_prod:
-	(void)ipa3_usb_release_prod(ttype);
+	if (ipa_pm_is_used())
+		(void)ipa_pm_deactivate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		(void)ipa3_usb_release_prod(ttype);
 fail_exit:
 	return result;
 }
@@ -2642,7 +2791,11 @@
 	}
 
 	/* Request USB_PROD */
-	result = ipa3_usb_request_prod(ttype);
+	if (ipa_pm_is_used())
+		result = ipa_pm_activate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		result = ipa3_usb_request_prod(ttype);
 	if (result)
 		goto prod_req_fail;
 
@@ -2685,7 +2838,11 @@
 			IPA_USB_ERR("Error stopping UL channel: %d\n", result);
 	}
 start_ul_fail:
-	ipa3_usb_release_prod(ttype);
+	if (ipa_pm_is_used())
+		ipa_pm_deactivate_sync(
+			ipa3_usb_ctx->ttype_ctx[ttype].pm_ctx.hdl);
+	else
+		ipa3_usb_release_prod(ttype);
 prod_req_fail:
 	/* Change state back to prev_state */
 	if (!ipa3_usb_set_state(prev_state, true, ttype))
@@ -2722,6 +2879,20 @@
 	ipa3_usb_ctx->dl_data_pending = false;
 	mutex_init(&ipa3_usb_ctx->general_mutex);
 
+	if (ipa_pm_is_used()) {
+		struct ipa3_usb_pm_context *pm_ctx;
+
+		pm_ctx =
+			&ipa3_usb_ctx->ttype_ctx[IPA_USB_TRANSPORT_TETH].pm_ctx;
+		pm_ctx->hdl = ~0;
+		pm_ctx->remote_wakeup_work =
+			&ipa3_usb_notify_remote_wakeup_work;
+		pm_ctx = &ipa3_usb_ctx->ttype_ctx[IPA_USB_TRANSPORT_DPL].pm_ctx;
+		pm_ctx->hdl = ~0;
+		pm_ctx->remote_wakeup_work =
+			&ipa3_usb_dpl_notify_remote_wakeup_work;
+	}
+
 	for (i = 0; i < IPA_USB_TRANSPORT_MAX; i++) {
 		ipa3_usb_ctx->ttype_ctx[i].rm_ctx.prod_valid = false;
 		ipa3_usb_ctx->ttype_ctx[i].rm_ctx.cons_valid = false;
diff --git a/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
new file mode 100644
index 0000000..f4c8763
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_wdi3.c
@@ -0,0 +1,526 @@
+/* Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ipa_wdi3.h>
+#include <linux/msm_ipa.h>
+#include <linux/string.h>
+#include "../ipa_common_i.h"
+
+#define OFFLOAD_DRV_NAME "ipa_wdi3"
+#define IPA_WDI3_DBG(fmt, args...) \
+	do { \
+		pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPA_WDI3_DBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+#define IPA_WDI3_ERR(fmt, args...) \
+	do { \
+		pr_err(OFFLOAD_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			OFFLOAD_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+
+struct ipa_wdi3_intf_info {
+	char netdev_name[IPA_RESOURCE_NAME_MAX];
+	u8 hdr_len;
+	u32 partial_hdr_hdl[IPA_IP_MAX];
+	struct list_head link;
+};
+
+struct ipa_wdi3_context {
+	struct list_head head_intf_list;
+	ipa_notify_cb notify;
+	void *priv;
+	struct completion wdi3_completion;
+	struct mutex lock;
+};
+
+static struct ipa_wdi3_context *ipa_wdi3_ctx;
+
+static int ipa_wdi3_commit_partial_hdr(
+	struct ipa_ioc_add_hdr *hdr,
+	const char *netdev_name,
+	struct ipa_wdi3_hdr_info *hdr_info)
+{
+	int i;
+
+	if (!hdr || !hdr_info || !netdev_name) {
+		IPA_WDI3_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+
+	hdr->commit = 1;
+	hdr->num_hdrs = 2;
+
+	snprintf(hdr->hdr[0].name, sizeof(hdr->hdr[0].name),
+			 "%s_ipv4", netdev_name);
+	snprintf(hdr->hdr[1].name, sizeof(hdr->hdr[1].name),
+			 "%s_ipv6", netdev_name);
+	for (i = IPA_IP_v4; i < IPA_IP_MAX; i++) {
+		hdr->hdr[i].hdr_len = hdr_info[i].hdr_len;
+		memcpy(hdr->hdr[i].hdr, hdr_info[i].hdr, hdr->hdr[i].hdr_len);
+		hdr->hdr[i].type = hdr_info[i].hdr_type;
+		hdr->hdr[i].is_partial = 1;
+		hdr->hdr[i].is_eth2_ofst_valid = 1;
+		hdr->hdr[i].eth2_ofst = hdr_info[i].dst_mac_addr_offset;
+	}
+
+	if (ipa_add_hdr(hdr)) {
+		IPA_WDI3_ERR("fail to add partial headers\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+int ipa_wdi3_reg_intf(struct ipa_wdi3_reg_intf_in_params *in)
+{
+	struct ipa_ioc_add_hdr *hdr;
+	struct ipa_wdi3_intf_info *new_intf;
+	struct ipa_wdi3_intf_info *entry;
+	struct ipa_tx_intf tx;
+	struct ipa_rx_intf rx;
+	struct ipa_ioc_tx_intf_prop tx_prop[2];
+	struct ipa_ioc_rx_intf_prop rx_prop[2];
+	u32 len;
+	int ret = 0;
+
+	if (in == NULL) {
+		IPA_WDI3_ERR("invalid params in=%pK\n", in);
+		return -EINVAL;
+	}
+
+	if (!ipa_wdi3_ctx) {
+		ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL);
+		if (ipa_wdi3_ctx == NULL) {
+			IPA_WDI3_ERR("fail to alloc wdi3 ctx\n");
+			return -ENOMEM;
+		}
+		mutex_init(&ipa_wdi3_ctx->lock);
+		INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list);
+	}
+
+	IPA_WDI3_DBG("register interface for netdev %s\n",
+		in->netdev_name);
+
+	mutex_lock(&ipa_wdi3_ctx->lock);
+	list_for_each_entry(entry, &ipa_wdi3_ctx->head_intf_list, link)
+		if (strcmp(entry->netdev_name, in->netdev_name) == 0) {
+			IPA_WDI3_DBG("intf was added before.\n");
+			mutex_unlock(&ipa_wdi3_ctx->lock);
+			return 0;
+		}
+
+	IPA_WDI3_DBG("intf was not added before, proceed.\n");
+	new_intf = kzalloc(sizeof(*new_intf), GFP_KERNEL);
+	if (new_intf == NULL) {
+		IPA_WDI3_ERR("fail to alloc new intf\n");
+		mutex_unlock(&ipa_wdi3_ctx->lock);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&new_intf->link);
+	strlcpy(new_intf->netdev_name, in->netdev_name,
+		sizeof(new_intf->netdev_name));
+	new_intf->hdr_len = in->hdr_info[0].hdr_len;
+
+	/* add partial header */
+	len = sizeof(struct ipa_ioc_add_hdr) + 2 * sizeof(struct ipa_hdr_add);
+	hdr = kzalloc(len, GFP_KERNEL);
+	if (hdr == NULL) {
+		IPA_WDI3_ERR("fail to alloc %d bytes\n", len);
+		ret = -EFAULT;
+		goto fail_alloc_hdr;
+	}
+
+	if (ipa_wdi3_commit_partial_hdr(hdr, in->netdev_name, in->hdr_info)) {
+		IPA_WDI3_ERR("fail to commit partial headers\n");
+		ret = -EFAULT;
+		goto fail_commit_hdr;
+	}
+
+	new_intf->partial_hdr_hdl[IPA_IP_v4] = hdr->hdr[IPA_IP_v4].hdr_hdl;
+	new_intf->partial_hdr_hdl[IPA_IP_v6] = hdr->hdr[IPA_IP_v6].hdr_hdl;
+	IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n",
+		hdr->hdr[IPA_IP_v4].hdr_hdl, hdr->hdr[IPA_IP_v6].hdr_hdl);
+
+	/* populate tx prop */
+	tx.num_props = 2;
+	tx.prop = tx_prop;
+
+	memset(tx_prop, 0, sizeof(tx_prop));
+	tx_prop[0].ip = IPA_IP_v4;
+	tx_prop[0].dst_pipe = IPA_CLIENT_WLAN1_CONS;
+	tx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type;
+	strlcpy(tx_prop[0].hdr_name, hdr->hdr[IPA_IP_v4].name,
+		sizeof(tx_prop[0].hdr_name));
+
+	tx_prop[1].ip = IPA_IP_v6;
+	tx_prop[1].dst_pipe = IPA_CLIENT_WLAN1_CONS;
+	tx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type;
+	strlcpy(tx_prop[1].hdr_name, hdr->hdr[IPA_IP_v6].name,
+		sizeof(tx_prop[1].hdr_name));
+
+	/* populate rx prop */
+	rx.num_props = 2;
+	rx.prop = rx_prop;
+
+	memset(rx_prop, 0, sizeof(rx_prop));
+	rx_prop[0].ip = IPA_IP_v4;
+	rx_prop[0].src_pipe = IPA_CLIENT_WLAN1_PROD;
+	rx_prop[0].hdr_l2_type = in->hdr_info[0].hdr_type;
+	if (in->is_meta_data_valid) {
+		rx_prop[0].attrib.attrib_mask |= IPA_FLT_META_DATA;
+		rx_prop[0].attrib.meta_data = in->meta_data;
+		rx_prop[0].attrib.meta_data_mask = in->meta_data_mask;
+	}
+
+	rx_prop[1].ip = IPA_IP_v6;
+	rx_prop[1].src_pipe = IPA_CLIENT_WLAN1_PROD;
+	rx_prop[1].hdr_l2_type = in->hdr_info[1].hdr_type;
+	if (in->is_meta_data_valid) {
+		rx_prop[1].attrib.attrib_mask |= IPA_FLT_META_DATA;
+		rx_prop[1].attrib.meta_data = in->meta_data;
+		rx_prop[1].attrib.meta_data_mask = in->meta_data_mask;
+	}
+
+	if (ipa_register_intf(in->netdev_name, &tx, &rx)) {
+		IPA_WDI3_ERR("fail to add interface prop\n");
+		ret = -EFAULT;
+		goto fail_commit_hdr;
+	}
+
+	list_add(&new_intf->link, &ipa_wdi3_ctx->head_intf_list);
+	init_completion(&ipa_wdi3_ctx->wdi3_completion);
+
+	kfree(hdr);
+	mutex_unlock(&ipa_wdi3_ctx->lock);
+	return 0;
+
+fail_commit_hdr:
+	kfree(hdr);
+fail_alloc_hdr:
+	kfree(new_intf);
+	mutex_unlock(&ipa_wdi3_ctx->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_reg_intf);
+
+int ipa_wdi3_dereg_intf(const char *netdev_name)
+{
+	int len, ret = 0;
+	struct ipa_ioc_del_hdr *hdr = NULL;
+	struct ipa_wdi3_intf_info *entry;
+	struct ipa_wdi3_intf_info *next;
+
+	if (!netdev_name) {
+		IPA_WDI3_ERR("no netdev name.\n");
+		return -EINVAL;
+	}
+
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+		return -EPERM;
+	}
+
+	mutex_lock(&ipa_wdi3_ctx->lock);
+	list_for_each_entry_safe(entry, next, &ipa_wdi3_ctx->head_intf_list,
+		link)
+		if (strcmp(entry->netdev_name, netdev_name) == 0) {
+			len = sizeof(struct ipa_ioc_del_hdr) +
+				2 * sizeof(struct ipa_hdr_del);
+			hdr = kzalloc(len, GFP_KERNEL);
+			if (hdr == NULL) {
+				IPA_WDI3_ERR("fail to alloc %d bytes\n", len);
+				mutex_unlock(&ipa_wdi3_ctx->lock);
+				return -ENOMEM;
+			}
+
+			hdr->commit = 1;
+			hdr->num_hdls = 2;
+			hdr->hdl[0].hdl = entry->partial_hdr_hdl[0];
+			hdr->hdl[1].hdl = entry->partial_hdr_hdl[1];
+			IPA_WDI3_DBG("IPv4 hdr hdl: %d IPv6 hdr hdl: %d\n",
+				hdr->hdl[0].hdl, hdr->hdl[1].hdl);
+
+			if (ipa_del_hdr(hdr)) {
+				IPA_WDI3_ERR("fail to delete partial header\n");
+				ret = -EFAULT;
+				goto fail;
+			}
+
+			if (ipa_deregister_intf(entry->netdev_name)) {
+				IPA_WDI3_ERR("fail to del interface props\n");
+				ret = -EFAULT;
+				goto fail;
+			}
+			list_del(&entry->link);
+			kfree(entry);
+
+			break;
+		}
+
+fail:
+	kfree(hdr);
+	mutex_unlock(&ipa_wdi3_ctx->lock);
+	return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_dereg_intf);
+
+static void ipa_wdi3_rm_notify(void *user_data, enum ipa_rm_event event,
+		unsigned long data)
+{
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("Invalid context\n");
+		return;
+	}
+
+	switch (event) {
+	case IPA_RM_RESOURCE_GRANTED:
+		complete_all(&ipa_wdi3_ctx->wdi3_completion);
+		break;
+
+	case IPA_RM_RESOURCE_RELEASED:
+		break;
+
+	default:
+		IPA_WDI3_ERR("Invalid RM Evt: %d", event);
+		break;
+	}
+}
+
+static int ipa_wdi3_cons_release(void)
+{
+	return 0;
+}
+
+static int ipa_wdi3_cons_request(void)
+{
+	int ret = 0;
+
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("wdi3 ctx is not initialized\n");
+		ret = -EFAULT;
+	}
+
+	return ret;
+}
+
+int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+			struct ipa_wdi3_conn_out_params *out)
+{
+	int ret = 0;
+	struct ipa_rm_create_params param;
+
+	if (!(in && out)) {
+		IPA_WDI3_ERR("empty parameters. in=%pK out=%pK\n", in, out);
+		return -EINVAL;
+	}
+
+	if (!ipa_wdi3_ctx) {
+		ipa_wdi3_ctx = kzalloc(sizeof(*ipa_wdi3_ctx), GFP_KERNEL);
+		if (ipa_wdi3_ctx == NULL) {
+			IPA_WDI3_ERR("fail to alloc wdi3 ctx\n");
+			return -EFAULT;
+		}
+		mutex_init(&ipa_wdi3_ctx->lock);
+		INIT_LIST_HEAD(&ipa_wdi3_ctx->head_intf_list);
+	}
+	ipa_wdi3_ctx->notify = in->notify;
+	ipa_wdi3_ctx->priv = in->priv;
+
+	memset(&param, 0, sizeof(param));
+	param.name = IPA_RM_RESOURCE_WLAN_PROD;
+	param.reg_params.user_data = ipa_wdi3_ctx;
+	param.reg_params.notify_cb = ipa_wdi3_rm_notify;
+	param.floor_voltage = IPA_VOLTAGE_SVS;
+	ret = ipa_rm_create_resource(&param);
+	if (ret) {
+		IPA_WDI3_ERR("fail to create WLAN_PROD resource\n");
+		return -EFAULT;
+	}
+
+	memset(&param, 0, sizeof(param));
+	param.name = IPA_RM_RESOURCE_WLAN_CONS;
+	param.request_resource = ipa_wdi3_cons_request;
+	param.release_resource = ipa_wdi3_cons_release;
+	ret = ipa_rm_create_resource(&param);
+	if (ret) {
+		IPA_WDI3_ERR("fail to create WLAN_CONS resource\n");
+		goto fail_create_rm_cons;
+	}
+
+	if (ipa_rm_add_dependency(IPA_RM_RESOURCE_WLAN_PROD,
+		IPA_RM_RESOURCE_APPS_CONS)) {
+		IPA_WDI3_ERR("fail to add rm dependency\n");
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	if (ipa_conn_wdi3_pipes(in, out)) {
+		IPA_WDI3_ERR("fail to setup wdi3 pipes\n");
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS);
+fail_create_rm_cons:
+	ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD);
+
+	return ret;
+}
+EXPORT_SYMBOL(ipa_wdi3_conn_pipes);
+
+int ipa_wdi3_disconn_pipes(void)
+{
+	int ipa_ep_idx_rx, ipa_ep_idx_tx;
+
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("wdi3 ctx is not initialized\n");
+		return -EPERM;
+	}
+
+	ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+	ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+	if (ipa_disconn_wdi3_pipes(ipa_ep_idx_rx, ipa_ep_idx_tx)) {
+		IPA_WDI3_ERR("fail to tear down wdi3 pipes\n");
+		return -EFAULT;
+	}
+
+	if (ipa_rm_delete_dependency(IPA_RM_RESOURCE_WLAN_PROD,
+				IPA_RM_RESOURCE_APPS_CONS)) {
+		IPA_WDI3_ERR("fail to delete rm dependency\n");
+		return -EFAULT;
+	}
+
+	if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_PROD)) {
+		IPA_WDI3_ERR("fail to delete WLAN_PROD resource\n");
+		return -EFAULT;
+	}
+
+	if (ipa_rm_delete_resource(IPA_RM_RESOURCE_WLAN_CONS)) {
+		IPA_WDI3_ERR("fail to delete WLAN_CONS resource\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_disconn_pipes);
+
+int ipa_wdi3_enable_pipes(void)
+{
+	int ret;
+	int ipa_ep_idx_tx, ipa_ep_idx_rx;
+
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+		return -EPERM;
+	}
+
+	ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+	ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+	if (ipa_enable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
+		IPA_WDI3_ERR("fail to enable wdi3 pipes\n");
+		return -EFAULT;
+	}
+
+	ret = ipa_rm_request_resource(IPA_RM_RESOURCE_WLAN_PROD);
+	if (ret == -EINPROGRESS) {
+		if (wait_for_completion_timeout(&ipa_wdi3_ctx->wdi3_completion,
+			10*HZ) == 0) {
+			IPA_WDI3_ERR("WLAN_PROD resource req time out\n");
+			return -EFAULT;
+		}
+	} else if (ret != 0) {
+		IPA_WDI3_ERR("fail to request resource\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_enable_pipes);
+
+int ipa_wdi3_disable_pipes(void)
+{
+	int ret;
+	int ipa_ep_idx_tx, ipa_ep_idx_rx;
+
+	if (!ipa_wdi3_ctx) {
+		IPA_WDI3_ERR("wdi3 ctx is not initialized.\n");
+		return -EPERM;
+	}
+
+	ret = ipa_rm_release_resource(IPA_RM_RESOURCE_WLAN_PROD);
+	if (ret != 0) {
+		IPA_WDI3_ERR("fail to release resource\n");
+		return -EFAULT;
+	}
+
+	ipa_ep_idx_rx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_PROD);
+	ipa_ep_idx_tx = ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+	if (ipa_disable_wdi3_pipes(ipa_ep_idx_tx, ipa_ep_idx_rx)) {
+		IPA_WDI3_ERR("fail to disable wdi3 pipes\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_disable_pipes);
+
+int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile)
+{
+	struct ipa_rm_perf_profile rm_profile;
+	enum ipa_rm_resource_name resource_name;
+
+	if (profile == NULL) {
+		IPA_WDI3_ERR("Invalid input\n");
+		return -EINVAL;
+	}
+
+	rm_profile.max_supported_bandwidth_mbps =
+		profile->max_supported_bw_mbps;
+
+	if (profile->client == IPA_CLIENT_WLAN1_PROD) {
+		resource_name = IPA_RM_RESOURCE_WLAN_PROD;
+	} else if (profile->client == IPA_CLIENT_WLAN1_CONS) {
+		resource_name = IPA_RM_RESOURCE_WLAN_CONS;
+	} else {
+		IPA_WDI3_ERR("not supported\n");
+		return -EINVAL;
+	}
+
+	if (ipa_rm_set_perf_profile(resource_name, &rm_profile)) {
+		IPA_WDI3_ERR("fail to setup rm perf profile\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ipa_wdi3_set_perf_profile);
diff --git a/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c b/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
index a623d0b..f0d1102 100644
--- a/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_clients/odu_bridge.c
@@ -27,6 +27,7 @@
 #include <linux/cdev.h>
 #include <linux/ipa_odu_bridge.h>
 #include "../ipa_common_i.h"
+#include "../ipa_v3/ipa_pm.h"
 
 #define ODU_BRIDGE_DRV_NAME "odu_ipa_bridge"
 
@@ -152,6 +153,7 @@
 	void *logbuf_low;
 	struct completion rm_comp;
 	void (*wakeup_request)(void *);
+	u32 pm_hdl;
 };
 static struct odu_bridge_ctx *odu_bridge_ctx;
 
@@ -273,20 +275,22 @@
 	memset(&odu_prod_params, 0, sizeof(odu_prod_params));
 	memset(&odu_emb_cons_params, 0, sizeof(odu_emb_cons_params));
 
-	/* Build IPA Resource manager dependency graph */
-	ODU_BRIDGE_DBG_LOW("build dependency graph\n");
-	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+	if (!ipa_pm_is_used()) {
+		/* Build IPA Resource manager dependency graph */
+		ODU_BRIDGE_DBG_LOW("build dependency graph\n");
+		res = ipa_rm_add_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
 					IPA_RM_RESOURCE_Q6_CONS);
-	if (res && res != -EINPROGRESS) {
-		ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
-		goto fail_add_dependency_1;
-	}
+		if (res && res != -EINPROGRESS) {
+			ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
+			goto fail_add_dependency_1;
+		}
 
-	res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
+		res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
 					IPA_RM_RESOURCE_ODU_ADAPT_CONS);
-	if (res && res != -EINPROGRESS) {
-		ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
-		goto fail_add_dependency_2;
+		if (res && res != -EINPROGRESS) {
+			ODU_BRIDGE_ERR("ipa_rm_add_dependency() failed\n");
+			goto fail_add_dependency_2;
+		}
 	}
 
 	/* configure RX (ODU->IPA) EP */
@@ -346,10 +350,12 @@
 	ipa_teardown_sys_pipe(odu_bridge_ctx->odu_prod_hdl);
 	odu_bridge_ctx->odu_prod_hdl = 0;
 fail_odu_prod:
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+	if (!ipa_pm_is_used())
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
 				IPA_RM_RESOURCE_ODU_ADAPT_CONS);
 fail_add_dependency_2:
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+	if (!ipa_pm_is_used())
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
 				IPA_RM_RESOURCE_Q6_CONS);
 fail_add_dependency_1:
 	return res;
@@ -397,17 +403,19 @@
 		ODU_BRIDGE_ERR("teardown ODU EMB CONS failed\n");
 	odu_bridge_ctx->odu_emb_cons_hdl = 0;
 
-	/* Delete IPA Resource manager dependency graph */
-	ODU_BRIDGE_DBG("deleting dependency graph\n");
-	res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
-		IPA_RM_RESOURCE_Q6_CONS);
-	if (res && res != -EINPROGRESS)
-		ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
+	if (!ipa_pm_is_used()) {
+		/* Delete IPA Resource manager dependency graph */
+		ODU_BRIDGE_DBG("deleting dependency graph\n");
+		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
+			IPA_RM_RESOURCE_Q6_CONS);
+		if (res && res != -EINPROGRESS)
+			ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
 
-	res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
-		IPA_RM_RESOURCE_ODU_ADAPT_CONS);
-	if (res && res != -EINPROGRESS)
-		ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
+		res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+			IPA_RM_RESOURCE_ODU_ADAPT_CONS);
+		if (res && res != -EINPROGRESS)
+			ODU_BRIDGE_ERR("ipa_rm_delete_dependency() failed\n");
+	}
 
 	return 0;
 }
@@ -1319,24 +1327,58 @@
 	return 0;
 }
 
-/* IPA Bridge API is the new API which will replaces old odu_bridge API */
-int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
+static void ipa_br_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	ODU_BRIDGE_FUNC_ENTRY();
+	if (event != IPA_PM_REQUEST_WAKEUP) {
+		ODU_BRIDGE_ERR("Unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+
+	if (odu_bridge_ctx->is_suspended)
+		odu_bridge_ctx->wakeup_request(odu_bridge_ctx->priv);
+	ODU_BRIDGE_FUNC_EXIT();
+}
+
+static int ipa_br_register_pm(void)
+{
+	struct ipa_pm_register_params reg_params;
+	int ret;
+
+	memset(&reg_params, 0, sizeof(reg_params));
+	reg_params.name = "ODU Bridge";
+	reg_params.callback = ipa_br_pm_cb;
+	reg_params.group = IPA_PM_GROUP_DEFAULT;
+
+	ret = ipa_pm_register(&reg_params,
+		&odu_bridge_ctx->pm_hdl);
+	if (ret) {
+		ODU_BRIDGE_ERR("fail to register with PM %d\n", ret);
+		goto fail_pm_reg;
+	}
+
+	ret = ipa_pm_associate_ipa_cons_to_client(odu_bridge_ctx->pm_hdl,
+		IPA_CLIENT_ODU_EMB_CONS);
+	if (ret) {
+		ODU_BRIDGE_ERR("fail to associate cons with PM %d\n", ret);
+		goto fail_pm_cons;
+	}
+
+	return 0;
+
+fail_pm_cons:
+	ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
+	odu_bridge_ctx->pm_hdl = ~0;
+fail_pm_reg:
+	return ret;
+}
+
+static int ipa_br_create_rm_resources(void)
 {
 	int ret;
 	struct ipa_rm_create_params create_params;
 
-	if (!params || !params->wakeup_request || !hdl) {
-		ODU_BRIDGE_ERR("NULL arg\n");
-		return -EINVAL;
-	}
-
-
-	ret = odu_bridge_init(&params->info);
-	if (ret)
-		return ret;
-
-	odu_bridge_ctx->wakeup_request = params->wakeup_request;
-
 	/* create IPA RM resources for power management */
 	init_completion(&odu_bridge_ctx->rm_comp);
 	memset(&create_params, 0, sizeof(create_params));
@@ -1368,9 +1410,6 @@
 		goto fail_rm_cons;
 	}
 
-	/* handle is ignored for now */
-	*hdl = 0;
-
 	return 0;
 
 fail_rm_cons:
@@ -1379,6 +1418,41 @@
 fail_add_dep:
 	ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
 fail_rm_prod:
+	return ret;
+}
+
+/* IPA Bridge API is the new API which will replaces old odu_bridge API */
+int ipa_bridge_init(struct ipa_bridge_init_params *params, u32 *hdl)
+{
+	int ret;
+
+	if (!params || !params->wakeup_request || !hdl) {
+		ODU_BRIDGE_ERR("NULL arg\n");
+		return -EINVAL;
+	}
+
+
+	ret = odu_bridge_init(&params->info);
+	if (ret)
+		return ret;
+
+	odu_bridge_ctx->wakeup_request = params->wakeup_request;
+
+	if (ipa_pm_is_used())
+		ret = ipa_br_register_pm();
+	else
+		ret = ipa_br_create_rm_resources();
+	if (ret) {
+		ODU_BRIDGE_ERR("fail to register woth RM/PM %d\n", ret);
+		goto fail_pm;
+	}
+
+	/* handle is ignored for now */
+	*hdl = 0;
+
+	return 0;
+
+fail_pm:
 	odu_bridge_cleanup();
 	return ret;
 }
@@ -1398,7 +1472,10 @@
 		return -EFAULT;
 	}
 
-	ret = ipa_br_request_prod();
+	if (ipa_pm_is_used())
+		ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
+	else
+		ret = ipa_br_request_prod();
 	if (ret)
 		return ret;
 
@@ -1411,6 +1488,10 @@
 	struct ipa_rm_perf_profile profile = {0};
 	int ret;
 
+	if (ipa_pm_is_used())
+		return ipa_pm_set_perf_profile(odu_bridge_ctx->pm_hdl,
+			bandwidth);
+
 	profile.max_supported_bandwidth_mbps = bandwidth;
 	ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_ODU_ADAPT_PROD, &profile);
 	if (ret) {
@@ -1436,7 +1517,10 @@
 	if (ret)
 		return ret;
 
-	ret = ipa_br_release_prod();
+	if (ipa_pm_is_used())
+		ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
+	else
+		ret = ipa_br_release_prod();
 	if (ret)
 		return ret;
 
@@ -1470,7 +1554,10 @@
 		return ret;
 	}
 
-	ret = ipa_br_release_prod();
+	if (ipa_pm_is_used())
+		ret = ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
+	else
+		ret = ipa_br_release_prod();
 	if (ret) {
 		ODU_BRIDGE_ERR("failed to release prod %d\n", ret);
 		ipa_start_gsi_channel(odu_bridge_ctx->odu_emb_cons_hdl);
@@ -1501,7 +1588,10 @@
 		return -EFAULT;
 	}
 
-	ret = ipa_br_request_prod();
+	if (ipa_pm_is_used())
+		ret = ipa_pm_activate_sync(odu_bridge_ctx->pm_hdl);
+	else
+		ret = ipa_br_request_prod();
 	if (ret)
 		return ret;
 
@@ -1523,12 +1613,27 @@
 }
 EXPORT_SYMBOL(ipa_bridge_tx_dp);
 
-int ipa_bridge_cleanup(u32 hdl)
+static void ipa_br_delete_rm_resources(void)
 {
 	ipa_rm_delete_dependency(IPA_RM_RESOURCE_ODU_ADAPT_PROD,
 		IPA_RM_RESOURCE_APPS_CONS);
 	ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_PROD);
 	ipa_rm_delete_resource(IPA_RM_RESOURCE_ODU_ADAPT_CONS);
+}
+
+static void ipa_br_deregister_pm(void)
+{
+	ipa_pm_deactivate_sync(odu_bridge_ctx->pm_hdl);
+	ipa_pm_deregister(odu_bridge_ctx->pm_hdl);
+	odu_bridge_ctx->pm_hdl = ~0;
+}
+
+int ipa_bridge_cleanup(u32 hdl)
+{
+	if (ipa_pm_is_used())
+		ipa_br_deregister_pm();
+	else
+		ipa_br_delete_rm_resources();
 	return odu_bridge_cleanup();
 }
 EXPORT_SYMBOL(ipa_bridge_cleanup);
diff --git a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
index f62cb27..2e87bd2 100644
--- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
@@ -9,7 +9,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
 #include <linux/atomic.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
@@ -27,6 +26,8 @@
 #include <linux/random.h>
 #include <linux/rndis_ipa.h>
 #include <linux/workqueue.h>
+#include "../ipa_common_i.h"
+#include "../ipa_v3/ipa_pm.h"
 
 #define CREATE_TRACE_POINTS
 #include "rndis_ipa_trace.h"
@@ -160,6 +161,7 @@
  * state is changed to RNDIS_IPA_CONNECTED_AND_UP
  * @xmit_error_delayed_work: work item for cases where IPA driver Tx fails
  * @state_lock: used to protect the state variable.
+ * @pm_hdl: handle for IPA PM framework
  */
 struct rndis_ipa_dev {
 	struct net_device *net;
@@ -188,6 +190,7 @@
 	void (*device_ready_notify)(void);
 	struct delayed_work xmit_error_delayed_work;
 	spinlock_t state_lock; /* Spinlock for the state variable.*/
+	u32 pm_hdl;
 };
 
 /**
@@ -233,6 +236,8 @@
 	unsigned long data);
 static int rndis_ipa_create_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx);
 static int rndis_ipa_destroy_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx);
+static int rndis_ipa_register_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx);
+static int rndis_ipa_deregister_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx);
 static bool rx_filter(struct sk_buff *skb);
 static bool tx_filter(struct sk_buff *skb);
 static bool rm_enabled(struct rndis_ipa_dev *rndis_ipa_ctx);
@@ -701,7 +706,10 @@
 		return -EINVAL;
 	}
 
-	result = rndis_ipa_create_rm_resource(rndis_ipa_ctx);
+	if (ipa_pm_is_used())
+		result = rndis_ipa_register_pm_client(rndis_ipa_ctx);
+	else
+		result = rndis_ipa_create_rm_resource(rndis_ipa_ctx);
 	if (result) {
 		RNDIS_IPA_ERROR("fail on RM create\n");
 		goto fail_create_rm;
@@ -763,7 +771,10 @@
 	return 0;
 
 fail:
-	rndis_ipa_destroy_rm_resource(rndis_ipa_ctx);
+	if (ipa_pm_is_used())
+		rndis_ipa_deregister_pm_client(rndis_ipa_ctx);
+	else
+		rndis_ipa_destroy_rm_resource(rndis_ipa_ctx);
 fail_create_rm:
 	return result;
 }
@@ -1235,7 +1246,10 @@
 	rndis_ipa_ctx->net->stats.tx_dropped += outstanding_dropped_pkts;
 	atomic_set(&rndis_ipa_ctx->outstanding_pkts, 0);
 
-	retval = rndis_ipa_destroy_rm_resource(rndis_ipa_ctx);
+	if (ipa_pm_is_used())
+		retval = rndis_ipa_deregister_pm_client(rndis_ipa_ctx);
+	else
+		retval = rndis_ipa_destroy_rm_resource(rndis_ipa_ctx);
 	if (retval) {
 		RNDIS_IPA_ERROR("Fail to clean RM\n");
 		return retval;
@@ -1772,6 +1786,29 @@
 	return result;
 }
 
+static void rndis_ipa_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	struct rndis_ipa_dev *rndis_ipa_ctx = p;
+
+	RNDIS_IPA_LOG_ENTRY();
+
+	if (event != IPA_PM_CLIENT_ACTIVATED) {
+		RNDIS_IPA_ERROR("unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+	RNDIS_IPA_DEBUG("Resource Granted\n");
+
+	if (netif_queue_stopped(rndis_ipa_ctx->net)) {
+		RNDIS_IPA_DEBUG("starting queue\n");
+		netif_start_queue(rndis_ipa_ctx->net);
+	} else {
+		RNDIS_IPA_DEBUG("queue already awake\n");
+	}
+
+	RNDIS_IPA_LOG_EXIT();
+}
+
 /**
  * rndis_ipa_destroy_rm_resource() - delete the dependency and destroy
  * the resource done on rndis_ipa_create_rm_resource()
@@ -1831,6 +1868,33 @@
 	return result;
 }
 
+static int rndis_ipa_register_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx)
+{
+	int result;
+	struct ipa_pm_register_params pm_reg;
+
+	memset(&pm_reg, 0, sizeof(pm_reg));
+
+	pm_reg.name = rndis_ipa_ctx->net->name;
+	pm_reg.user_data = rndis_ipa_ctx;
+	pm_reg.callback = rndis_ipa_pm_cb;
+	pm_reg.group = IPA_PM_GROUP_APPS;
+	result = ipa_pm_register(&pm_reg, &rndis_ipa_ctx->pm_hdl);
+	if (result) {
+		RNDIS_IPA_ERROR("failed to create IPA PM client %d\n", result);
+		return result;
+	}
+	return 0;
+}
+
+static int rndis_ipa_deregister_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx)
+{
+	ipa_pm_deactivate_sync(rndis_ipa_ctx->pm_hdl);
+	ipa_pm_deregister(rndis_ipa_ctx->pm_hdl);
+	rndis_ipa_ctx->pm_hdl = ~0;
+	return 0;
+}
+
 /**
  * resource_request() - request for the Netdev resource
  * @rndis_ipa_ctx: main driver context
@@ -1849,11 +1913,14 @@
 	int result = 0;
 
 	if (!rm_enabled(rndis_ipa_ctx))
-		goto out;
-	result = ipa_rm_inactivity_timer_request_resource(
+		return result;
+
+	if (ipa_pm_is_used())
+		return ipa_pm_activate(rndis_ipa_ctx->pm_hdl);
+
+	return ipa_rm_inactivity_timer_request_resource(
 			DRV_RESOURCE_ID);
-out:
-	return result;
+
 }
 
 /**
@@ -1868,9 +1935,12 @@
 static void resource_release(struct rndis_ipa_dev *rndis_ipa_ctx)
 {
 	if (!rm_enabled(rndis_ipa_ctx))
-		goto out;
-	ipa_rm_inactivity_timer_release_resource(DRV_RESOURCE_ID);
-out:
+		return;
+	if (ipa_pm_is_used())
+		ipa_pm_deferred_deactivate(rndis_ipa_ctx->pm_hdl);
+	else
+		ipa_rm_inactivity_timer_release_resource(DRV_RESOURCE_ID);
+
 	return;
 }
 
@@ -2420,9 +2490,13 @@
 	(struct file *file,
 	const char __user *buf, size_t count, loff_t *ppos)
 {
-	struct rndis_ipa_dev *rndis_ipa_ctx = file->private_data;
+	struct rndis_ipa_dev *rndis_ipa_ctx = NULL;
 	int result;
 
+	if (file == NULL)
+		return -EFAULT;
+	rndis_ipa_ctx = file->private_data;
+
 	result = ipa_cfg_ep(rndis_ipa_ctx->usb_to_ipa_hdl, &ipa_to_usb_ep_cfg);
 	if (result) {
 		pr_err("failed to re-configure USB to IPA point\n");
diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h
index 32c8b25..98a1cf9 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -18,6 +18,11 @@
 #include <linux/ipc_logging.h>
 #include <linux/ipa.h>
 #include <linux/ipa_uc_offload.h>
+#include <linux/ipa_wdi3.h>
+#include <linux/ratelimit.h>
+
+#define WARNON_RATELIMIT_BURST 1
+#define IPA_RATELIMIT_BURST 1
 
 #define __FILENAME__ \
 	(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
@@ -103,6 +108,39 @@
 		ipa_dec_client_disable_clks(&log_info); \
 	} while (0)
 
+/*
+ * Printing one warning message in 5 seconds if multiple warning messages
+ * are coming back to back.
+ */
+
+#define WARN_ON_RATELIMIT_IPA(condition)				\
+({								\
+	static DEFINE_RATELIMIT_STATE(_rs,			\
+				DEFAULT_RATELIMIT_INTERVAL,	\
+				WARNON_RATELIMIT_BURST);	\
+	int rtn = !!(condition);				\
+								\
+	if (unlikely(rtn && __ratelimit(&_rs)))			\
+		WARN_ON(rtn);					\
+})
+
+/*
+ * Printing one error message in 5 seconds if multiple error messages
+ * are coming back to back.
+ */
+
+#define pr_err_ratelimited_ipa(fmt, ...)				\
+	printk_ratelimited_ipa(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
+#define printk_ratelimited_ipa(fmt, ...)				\
+({									\
+	static DEFINE_RATELIMIT_STATE(_rs,				\
+				      DEFAULT_RATELIMIT_INTERVAL,	\
+				      IPA_RATELIMIT_BURST);		\
+									\
+	if (__ratelimit(&_rs))						\
+		printk(fmt, ##__VA_ARGS__);				\
+})
+
 #define ipa_assert_on(condition)\
 do {\
 	if (unlikely(condition))\
@@ -144,9 +182,6 @@
 	u32 size;
 };
 
-#define IPA_MHI_GSI_ER_START 10
-#define IPA_MHI_GSI_ER_END 16
-
 /**
  * enum ipa3_mhi_burst_mode - MHI channel burst mode state
  *
@@ -383,7 +418,19 @@
 int ipa_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *user_data),
 			      void *user_data);
 void ipa_ntn_uc_dereg_rdyCB(void);
+
+int ipa_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out);
+
+int ipa_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
+int ipa_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
+int ipa_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
 const char *ipa_get_version_string(enum ipa_hw_type ver);
 int ipa_start_gsi_channel(u32 clnt_hdl);
 
+bool ipa_pm_is_used(void);
+
 #endif /* _IPA_COMMON_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v2/Makefile b/drivers/platform/msm/ipa/ipa_v2/Makefile
index 69b8a4c..fb03970 100644
--- a/drivers/platform/msm/ipa/ipa_v2/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v2/Makefile
@@ -1,6 +1,7 @@
 obj-$(CONFIG_IPA) += 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_uc.o ipa_uc_wdi.o ipa_dma.o ipa_uc_mhi.o ipa_mhi.o ipa_uc_ntn.o \
+	ipa_wdi3_i.o
 
 obj-$(CONFIG_RMNET_IPA) += 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_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c
index bfd0446..c760f75 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c
@@ -531,11 +531,12 @@
 	kfree(buff);
 }
 
-static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
 {
 	int retval;
 	struct ipa_wan_msg *wan_msg;
 	struct ipa_msg_meta msg_meta;
+	struct ipa_wan_msg cache_wan_msg;
 
 	wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL);
 	if (!wan_msg) {
@@ -549,6 +550,8 @@
 		return -EFAULT;
 	}
 
+	memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg));
+
 	memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 	msg_meta.msg_type = msg_type;
 	msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -559,6 +562,25 @@
 		return retval;
 	}
 
+	if (is_cache) {
+		mutex_lock(&ipa_ctx->ipa_cne_evt_lock);
+
+		/* cache the cne event */
+		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+			ipa_ctx->num_ipa_cne_evt_req].wan_msg,
+			&cache_wan_msg,
+			sizeof(cache_wan_msg));
+
+		memcpy(&ipa_ctx->ipa_cne_evt_req_cache[
+			ipa_ctx->num_ipa_cne_evt_req].msg_meta,
+			&msg_meta,
+			sizeof(struct ipa_msg_meta));
+
+		ipa_ctx->num_ipa_cne_evt_req++;
+		ipa_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+		mutex_unlock(&ipa_ctx->ipa_cne_evt_lock);
+	}
+
 	return 0;
 }
 
@@ -580,8 +602,6 @@
 
 	if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
 		return -ENOTTY;
-	if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
-		return -ENOTTY;
 
 	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 
@@ -1328,21 +1348,21 @@
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
-		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
-		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+		retval = ipa_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
-		retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+		retval = ipa_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
 		if (retval) {
 			IPAERR("ipa_send_wan_msg failed: %d\n", retval);
 			break;
@@ -1443,7 +1463,7 @@
 		}
 		break;
 
-	default:        /* redundant, as cmd was checked against MAXNR */
+	default:
 		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 		return -ENOTTY;
 	}
@@ -4169,6 +4189,7 @@
 
 	mutex_init(&ipa_ctx->lock);
 	mutex_init(&ipa_ctx->nat_mem.lock);
+	mutex_init(&ipa_ctx->ipa_cne_evt_lock);
 
 	idr_init(&ipa_ctx->ipa_idr);
 	spin_lock_init(&ipa_ctx->idr_lock);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
index 2b517a1..a249567 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c
@@ -86,7 +86,9 @@
 	__stringify(ADD_VLAN_IFACE),
 	__stringify(DEL_VLAN_IFACE),
 	__stringify(ADD_L2TP_VLAN_MAPPING),
-	__stringify(DEL_L2TP_VLAN_MAPPING)
+	__stringify(DEL_L2TP_VLAN_MAPPING),
+	__stringify(IPA_PER_CLIENT_STATS_CONNECT_EVENT),
+	__stringify(IPA_PER_CLIENT_STATS_DISCONNECT_EVENT),
 };
 
 const char *ipa_hdr_l2_type_name[] = {
@@ -811,10 +813,11 @@
 			eq = true;
 		} else {
 			rt_tbl = ipa_id_find(entry->rule.rt_tbl_hdl);
-			if (rt_tbl)
-				rt_tbl_idx = rt_tbl->idx;
+			if (rt_tbl == NULL ||
+				rt_tbl->cookie != IPA_RT_TBL_COOKIE)
+				rt_tbl_idx =  ~0;
 			else
-				rt_tbl_idx = ~0;
+				rt_tbl_idx = rt_tbl->idx;
 			bitmap = entry->rule.attrib.attrib_mask;
 			eq = false;
 		}
@@ -841,10 +844,11 @@
 				eq = true;
 			} else {
 				rt_tbl = ipa_id_find(entry->rule.rt_tbl_hdl);
-				if (rt_tbl)
-					rt_tbl_idx = rt_tbl->idx;
-				else
+				if (rt_tbl == NULL ||
+					rt_tbl->cookie != IPA_RT_TBL_COOKIE)
 					rt_tbl_idx = ~0;
+				else
+					rt_tbl_idx = rt_tbl->idx;
 				bitmap = entry->rule.attrib.attrib_mask;
 				eq = false;
 			}
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
index f7b0864..0a079f4 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_flt.c
@@ -23,10 +23,10 @@
 static int ipa_generate_hw_rule_from_eq(
 		const struct ipa_ipfltri_rule_eq *attrib, u8 **buf)
 {
-	int num_offset_meq_32 = attrib->num_offset_meq_32;
-	int num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
-	int num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
-	int num_offset_meq_128 = attrib->num_offset_meq_128;
+	uint8_t num_offset_meq_32 = attrib->num_offset_meq_32;
+	uint8_t num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
+	uint8_t num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
+	uint8_t num_offset_meq_128 = attrib->num_offset_meq_128;
 	int i;
 
 	if (attrib->tos_eq_present) {
@@ -1037,6 +1037,11 @@
 				goto error;
 			}
 		}
+	} else {
+		if (rule->rt_tbl_idx > 0) {
+			IPAERR_RL("invalid RT tbl\n");
+			goto error;
+		}
 	}
 
 	entry = kmem_cache_zalloc(ipa_ctx->flt_rule_cache, GFP_KERNEL);
@@ -1158,6 +1163,11 @@
 				goto error;
 			}
 		}
+	} else {
+		if (frule->rule.rt_tbl_idx > 0) {
+			IPAERR_RL("invalid RT tbl\n");
+			goto error;
+		}
 	}
 
 	entry->rule = frule->rule;
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
index d4e39d7..2f72d88 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_hdr.c
@@ -1374,6 +1374,7 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa_ctx->lock);
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_hdr(lookup->name);
 	if (entry) {
 		lookup->hdl = entry->id;
@@ -1496,6 +1497,7 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa_ctx->lock);
+	copy->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_hdr(copy->name);
 	if (entry) {
 		memcpy(copy->hdr, entry->hdr, entry->hdr_len);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
index 141bff1..67b0be6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -63,6 +63,9 @@
 
 #define IPA_MAX_STATUS_STAT_NUM 30
 
+
+#define IPA_MAX_NUM_REQ_CACHE 10
+
 #define IPADBG(fmt, args...) \
 	pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
 #define IPAERR(fmt, args...) \
@@ -968,6 +971,11 @@
 	bool uplink;
 };
 
+struct ipa_cne_evt {
+	struct ipa_wan_msg wan_msg;
+	struct ipa_msg_meta msg_meta;
+};
+
 /**
  * struct ipa_context - IPA context
  * @class: pointer to the struct class
@@ -1164,6 +1172,9 @@
 	u32 ipa_rx_min_timeout_usec;
 	u32 ipa_rx_max_timeout_usec;
 	u32 ipa_polling_iteration;
+	struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+	int num_ipa_cne_evt_req;
+	struct mutex ipa_cne_evt_lock;
 };
 
 /**
@@ -1535,6 +1546,12 @@
 int ipa2_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
 void ipa2_ntn_uc_dereg_rdyCB(void);
 
+int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out);
+int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+
 /*
  * To retrieve doorbell physical address of
  * wlan pipes
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
index e6954b7..e6048d1 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_intf.c
@@ -234,6 +234,7 @@
 	}
 
 	mutex_lock(&ipa_ctx->lock);
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
 		if (!strcmp(entry->name, lookup->name)) {
 			lookup->num_tx_props = entry->num_tx_props;
@@ -269,6 +270,7 @@
 	}
 
 	mutex_lock(&ipa_ctx->lock);
+	tx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
 		if (!strcmp(entry->name, tx->name)) {
 			/* add the entry check */
@@ -310,6 +312,7 @@
 	}
 
 	mutex_lock(&ipa_ctx->lock);
+	rx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	list_for_each_entry(entry, &ipa_ctx->intf_list, link) {
 		if (!strcmp(entry->name, rx->name)) {
 			/* add the entry check */
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
index 57e0a53..825c538 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service.c
@@ -512,6 +512,7 @@
 	struct ipa_install_fltr_rule_resp_msg_v01 resp;
 	struct msg_desc req_desc, resp_desc;
 	int rc;
+	int i;
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->filter_spec_list_len == 0) {
@@ -521,6 +522,38 @@
 		req->filter_spec_list_len);
 	}
 
+	if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) {
+		IPAWANDBG(
+		"IPACM passes the number of filtering rules exceed limit\n");
+		return -EINVAL;
+	} else if (req->source_pipe_index_valid != 0) {
+		IPAWANDBG(
+		"IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+			req->source_pipe_index_valid);
+		return -EINVAL;
+	} else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) {
+		IPAWANDBG(
+		"IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
+	}
+	for (i = 0; i < req->filter_spec_list_len; i++) {
+		if ((req->filter_spec_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V4_V01) &&
+			(req->filter_spec_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V6_V01))
+			return -EINVAL;
+		if (req->filter_spec_list[i].is_mux_id_valid == false)
+			return -EINVAL;
+		if (req->filter_spec_list[i].is_routing_table_index_valid
+			== false)
+			return -EINVAL;
+		if ((req->filter_spec_list[i].filter_action <=
+			QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+			(req->filter_spec_list[i].filter_action >
+			QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+			return -EINVAL;
+	}
 	mutex_lock(&ipa_qmi_lock);
 	if (ipa_qmi_ctx != NULL) {
 		/* cache the qmi_filter_request */
@@ -655,9 +688,8 @@
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->filter_index_list_len == 0) {
-		IPAWANERR(" delete UL filter rule for pipe %d\n",
+		IPAWANDBG(" delete UL filter rule for pipe %d\n",
 		req->source_pipe_index);
-		return -EINVAL;
 	} else if (req->filter_index_list_len > QMI_IPA_MAX_FILTERS_V01) {
 		IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
 		req->source_pipe_index,
@@ -674,6 +706,25 @@
 				req->filter_index_list[i].filter_handle,
 				req->filter_index_list[i].filter_index);
 		return -EINVAL;
+	} else if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) {
+		IPAWANERR(" UL filter rule for pipe %d install_status = %d\n",
+			req->source_pipe_index, req->install_status);
+		return -EINVAL;
+	} else if (req->source_pipe_index >= ipa_ctx->ipa_num_pipes) {
+		IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
+	} else if (((req->embedded_pipe_index_valid != true) ||
+			(req->embedded_call_mux_id_valid != true)) &&
+			((req->embedded_pipe_index_valid != false) ||
+			(req->embedded_call_mux_id_valid != false))) {
+		IPAWANERR(
+			"IPACM passes embedded pipe and mux valid not valid\n");
+		return -EINVAL;
+	} else if (req->embedded_pipe_index >= ipa_ctx->ipa_num_pipes) {
+		IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
 	}
 
 	mutex_lock(&ipa_qmi_lock);
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
index dd59140..5228b2d 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_qmi_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1436,6 +1436,66 @@
 			start_ipv6_filter_idx),
 	},
 	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x17,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			rule_id_valid),
+	},
+	{
+		.data_type	= QMI_DATA_LEN,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x17,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			rule_id_len),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= QMI_IPA_MAX_FILTERS_V01,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= VAR_LEN_ARRAY,
+		.tlv_type	= 0x17,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			rule_id),
+	},
+	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id_valid),
+	},
+	{
+		.data_type	= QMI_DATA_LEN,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id_len),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= QMI_IPA_MAX_CLIENT_DST_PIPES_V01,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= VAR_LEN_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id),
+	},
+	{
 		.data_type	= QMI_EOTI,
 		.is_array	= NO_ARRAY,
 		.tlv_type	= QMI_COMMON_TLV_TYPE,
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
index bada5cc..321cc89 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_rt.c
@@ -863,6 +863,7 @@
 
 	mutex_lock(&ipa_ctx->lock);
 	/* check if this table exists */
+	in->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_rt_tbl(in->ip, in->name);
 	if (!entry) {
 		mutex_unlock(&ipa_ctx->lock);
@@ -1099,6 +1100,7 @@
 
 	mutex_lock(&ipa_ctx->lock);
 	for (i = 0; i < rules->num_rules; i++) {
+		rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 		if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
 					&rules->rules[i].rule,
 					rules->rules[i].at_rear,
@@ -1368,6 +1370,7 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa_ctx->lock);
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_rt_tbl(lookup->ip, lookup->name);
 	if (entry && entry->cookie == IPA_RT_TBL_COOKIE) {
 		if (entry->ref_cnt == U32_MAX) {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
index a98d602..afe6368 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_uc_offload_i.h
@@ -26,6 +26,9 @@
 #define IPA_NTN_TX_DIR 1
 #define IPA_NTN_RX_DIR 2
 
+#define IPA_WDI3_TX_DIR 1
+#define IPA_WDI3_RX_DIR 2
+
 /**
  *  @brief   Enum value determined based on the feature it
  *           corresponds to
@@ -45,16 +48,20 @@
  * enum ipa_hw_features - Values that represent the features supported in IPA HW
  * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
  * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
+ * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
  * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
  * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
  * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW
 */
 enum ipa_hw_features {
 	IPA_HW_FEATURE_COMMON = 0x0,
 	IPA_HW_FEATURE_MHI    = 0x1,
+	IPA_HW_FEATURE_POWER_COLLAPSE = 0x2,
 	IPA_HW_FEATURE_WDI    = 0x3,
 	IPA_HW_FEATURE_NTN    = 0x4,
 	IPA_HW_FEATURE_OFFLOAD = 0x5,
+	IPA_HW_FEATURE_WDI3    = 0x6,
 	IPA_HW_FEATURE_MAX    = IPA_HW_NUM_FEATURES
 };
 
@@ -277,6 +284,33 @@
 
 } __packed;
 
+struct IpaHwWdi3SetUpCmdData_t {
+	u32  transfer_ring_base_pa;
+	u32  transfer_ring_base_pa_hi;
+
+	u32  transfer_ring_size;
+
+	u32  transfer_ring_doorbell_pa;
+	u32  transfer_ring_doorbell_pa_hi;
+
+	u32  event_ring_base_pa;
+	u32  event_ring_base_pa_hi;
+
+	u32  event_ring_size;
+
+	u32  event_ring_doorbell_pa;
+	u32  event_ring_doorbell_pa_hi;
+
+	u16  num_pkt_buffers;
+	u8   ipa_pipe_number;
+	u8   dir;
+
+	u16  pkt_offset;
+	u16  reserved0;
+
+	u32  desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+} __packed;
+
 /**
  * struct IpaHwNtnCommonChCmdData_t - Structure holding the
  * parameters for Ntn Tear down command data params
@@ -291,6 +325,13 @@
 	uint32_t raw32b;
 } __packed;
 
+union IpaHwWdi3CommonChCmdData_t {
+	struct IpaHwWdi3CommonChCmdParams_t {
+		u32  ipa_pipe_number :8;
+		u32  reserved        :24;
+	} __packed params;
+	u32 raw32b;
+} __packed;
 
 /**
  * struct IpaHwNTNErrorEventData_t - Structure holding the
@@ -408,13 +449,30 @@
  * the offload commands from CPU
  * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
  *				Offload protocol's Tx/Rx Path
- * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down
+ *				Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable
+ *				Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable
+ *				Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend
+ *				Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume
  *				Offload protocol's Tx/ Rx Path
  */
 enum ipa_cpu_2_hw_offload_commands {
 	IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP  =
 		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
-	IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+	IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+	IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE  =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+	IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+	IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND  =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+	IPA_CPU_2_HW_CMD_OFFLOAD_RESUME =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
 };
 
 
@@ -525,6 +583,7 @@
  */
 union IpaHwSetUpCmd {
 	struct IpaHwNtnSetUpCmdData_t NtnSetupCh_params;
+	struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params;
 } __packed;
 
 /**
@@ -545,6 +604,7 @@
  */
 union IpaHwCommonChCmd {
 	union IpaHwNtnCommonChCmdData_t NtnCommonCh_params;
+	union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params;
 } __packed;
 
 struct IpaHwOffloadCommonChCmdData_t {
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
index 2c88244..e611abd 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -4969,7 +4969,7 @@
 	api_ctrl->ipa_mdfy_flt_rule = ipa2_mdfy_flt_rule;
 	api_ctrl->ipa_commit_flt = ipa2_commit_flt;
 	api_ctrl->ipa_reset_flt = ipa2_reset_flt;
-	api_ctrl->allocate_nat_device = ipa2_allocate_nat_device;
+	api_ctrl->ipa_allocate_nat_device = ipa2_allocate_nat_device;
 	api_ctrl->ipa_nat_init_cmd = ipa2_nat_init_cmd;
 	api_ctrl->ipa_nat_dma_cmd = ipa2_nat_dma_cmd;
 	api_ctrl->ipa_nat_del_cmd = ipa2_nat_del_cmd;
@@ -5088,6 +5088,10 @@
 	api_ctrl->ipa_get_pdev = ipa2_get_pdev;
 	api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa2_ntn_uc_reg_rdyCB;
 	api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa2_ntn_uc_dereg_rdyCB;
+	api_ctrl->ipa_conn_wdi3_pipes = ipa2_conn_wdi3_pipes;
+	api_ctrl->ipa_disconn_wdi3_pipes = ipa2_disconn_wdi3_pipes;
+	api_ctrl->ipa_enable_wdi3_pipes = ipa2_enable_wdi3_pipes;
+	api_ctrl->ipa_disable_wdi3_pipes = ipa2_disable_wdi3_pipes;
 
 	return 0;
 }
diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c
new file mode 100644
index 0000000..a2c33a1
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_wdi3_i.c
@@ -0,0 +1,406 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "ipa_i.h"
+#include "ipa_uc_offload_i.h"
+#include <linux/ipa_wdi3.h>
+
+#define IPA_HW_WDI3_RX_MBOX_START_INDEX 48
+#define IPA_HW_WDI3_TX_MBOX_START_INDEX 50
+
+static int ipa_send_wdi3_setup_pipe_cmd(
+	struct ipa_wdi3_setup_info *info, u8 dir)
+{
+	int ipa_ep_idx;
+	int result = 0;
+	struct ipa_mem_buffer cmd;
+	struct IpaHwWdi3SetUpCmdData_t *wdi3_params;
+	struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+	if (info == NULL) {
+		IPAERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	ipa_ep_idx = ipa_get_ep_mapping(info->client);
+	IPAERR("ep number: %d\n", ipa_ep_idx);
+	if (ipa_ep_idx == -1) {
+		IPAERR("fail to get ep idx.\n");
+		return -EFAULT;
+	}
+
+	IPAERR("client=%d ep=%d\n", info->client, ipa_ep_idx);
+	IPAERR("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa);
+	IPAERR("ring_size = %hu\n", info->transfer_ring_size);
+	IPAERR("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa);
+	IPAERR("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa);
+	IPAERR("evt_ring_size = %hu\n", info->event_ring_size);
+	IPAERR("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa);
+	IPAERR("num_pkt_buffers = %hu\n", info->num_pkt_buffers);
+	IPAERR("pkt_offset = %d.\n", info->pkt_offset);
+
+	cmd.size = sizeof(*cmd_data);
+	cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+			&cmd.phys_base, GFP_KERNEL);
+	if (cmd.base == NULL) {
+		IPAERR("fail to get DMA memory.\n");
+		return -ENOMEM;
+	}
+	IPAERR("suceeded in allocating memory.\n");
+
+	cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+	cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+	wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params;
+	wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa;
+	wdi3_params->transfer_ring_base_pa_hi =
+		(u32)((u64)info->transfer_ring_base_pa >> 32);
+	wdi3_params->transfer_ring_size = info->transfer_ring_size;
+	wdi3_params->transfer_ring_doorbell_pa =
+		(u32)info->transfer_ring_doorbell_pa;
+	wdi3_params->transfer_ring_doorbell_pa_hi =
+		(u32)((u64)info->transfer_ring_doorbell_pa >> 32);
+	wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa;
+	wdi3_params->event_ring_base_pa_hi =
+		(u32)((u64)info->event_ring_base_pa >> 32);
+	wdi3_params->event_ring_size = info->event_ring_size;
+	wdi3_params->event_ring_doorbell_pa =
+		(u32)info->event_ring_doorbell_pa;
+	wdi3_params->event_ring_doorbell_pa_hi =
+		(u32)((u64)info->event_ring_doorbell_pa >> 32);
+	wdi3_params->num_pkt_buffers = info->num_pkt_buffers;
+	wdi3_params->ipa_pipe_number = ipa_ep_idx;
+	wdi3_params->dir = dir;
+	wdi3_params->pkt_offset = info->pkt_offset;
+	memcpy(wdi3_params->desc_format_template, info->desc_format_template,
+		sizeof(wdi3_params->desc_format_template));
+	IPAERR("suceeded in populating the command memory.\n");
+
+	result = ipa_uc_send_cmd((u32)(cmd.phys_base),
+				IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+				IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+				false, 10*HZ);
+	if (result) {
+		IPAERR("uc setup channel cmd failed: %d\n", result);
+		result = -EFAULT;
+	}
+
+	dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+	IPAERR("suceeded in freeing memory.\n");
+	return result;
+}
+
+int ipa2_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out)
+{
+	struct ipa_ep_context *ep_rx;
+	struct ipa_ep_context *ep_tx;
+	int ipa_ep_idx_rx;
+	int ipa_ep_idx_tx;
+	int result = 0;
+
+	if (in == NULL || out == NULL) {
+		IPAERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client);
+	ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client);
+	if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) {
+		IPAERR("fail to alloc EP.\n");
+		return -EFAULT;
+	}
+
+	ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+	ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+
+	if (ep_rx->valid || ep_tx->valid) {
+		IPAERR("EP already allocated.\n");
+		return -EFAULT;
+	}
+
+	memset(ep_rx, 0, offsetof(struct ipa_ep_context, sys));
+	memset(ep_tx, 0, offsetof(struct ipa_ep_context, sys));
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+	/* setup rx ep cfg */
+	ep_rx->valid = 1;
+	ep_rx->client = in->rx.client;
+	result = ipa_disable_data_path(ipa_ep_idx_rx);
+	if (result) {
+		IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_rx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+	ep_rx->client_notify = in->notify;
+	ep_rx->priv = in->priv;
+
+	memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg));
+
+	if (ipa_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) {
+		IPAERR("fail to setup rx pipe cfg\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	IPAERR("configured RX EP.\n");
+
+	if (ipa_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) {
+		IPAERR("fail to send cmd to uc for rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	IPAERR("rx pipe was setup.\n");
+
+	ipa_install_dflt_flt_rules(ipa_ep_idx_rx);
+	out->rx_uc_db_pa = ipa_ctx->ipa_wrapper_base +
+		IPA_REG_BASE_OFST_v2_5 +
+		IPA_UC_MAILBOX_m_n_OFFS_v2_5(
+		IPA_HW_WDI3_RX_MBOX_START_INDEX/32,
+		IPA_HW_WDI3_RX_MBOX_START_INDEX % 32);
+	IPADBG("client %d (ep: %d) connected\n", in->rx.client,
+		ipa_ep_idx_rx);
+
+	/* setup dl ep cfg */
+	ep_tx->valid = 1;
+	ep_tx->client = in->tx.client;
+	result = ipa_disable_data_path(ipa_ep_idx_tx);
+	if (result) {
+		IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_tx);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg));
+
+	if (ipa_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) {
+		IPAERR("fail to setup tx pipe cfg\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	IPAERR("configured TX EP in DMA mode.\n");
+
+	if (ipa_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) {
+		IPAERR("fail to send cmd to uc for tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	IPAERR("tx pipe was setup.\n");
+
+	out->tx_uc_db_pa = ipa_ctx->ipa_wrapper_base +
+		IPA_REG_BASE_OFST_v2_5 +
+		IPA_UC_MAILBOX_m_n_OFFS_v2_5(
+		IPA_HW_WDI3_TX_MBOX_START_INDEX/32,
+		IPA_HW_WDI3_TX_MBOX_START_INDEX % 32);
+	out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4);
+	IPADBG("client %d (ep: %d) connected\n", in->tx.client,
+		ipa_ep_idx_tx);
+
+fail:
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+	return result;
+}
+
+static int ipa_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command)
+{
+	struct ipa_mem_buffer cmd;
+	struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+	union IpaHwWdi3CommonChCmdData_t *wdi3;
+	int result = 0;
+
+	cmd.size = sizeof(*cmd_data);
+	cmd.base = dma_alloc_coherent(ipa_ctx->uc_pdev, cmd.size,
+		&cmd.phys_base, GFP_KERNEL);
+	if (cmd.base == NULL) {
+		IPAERR("fail to get DMA memory.\n");
+		return -ENOMEM;
+	}
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+	/* enable the TX pipe */
+	cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+	cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+	wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params;
+	wdi3->params.ipa_pipe_number = ipa_ep_idx;
+	IPAERR("cmd: %d ep_idx: %d\n", command, ipa_ep_idx);
+	result = ipa_uc_send_cmd((u32)(cmd.phys_base), command,
+				IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+				false, 10*HZ);
+	if (result) {
+		result = -EFAULT;
+		goto fail;
+	}
+
+fail:
+	dma_free_coherent(ipa_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+	return result;
+}
+
+int ipa2_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+	/* tear down tx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+		IPAERR("fail to tear down tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	ipa_disable_data_path(ipa_ep_idx_tx);
+	memset(ep_tx, 0, sizeof(struct ipa_ep_context));
+	IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx);
+
+	/* tear down rx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+		IPAERR("fail to tear down rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	ipa_disable_data_path(ipa_ep_idx_rx);
+	ipa_delete_dflt_flt_rules(ipa_ep_idx_rx);
+	memset(ep_rx, 0, sizeof(struct ipa_ep_context));
+	IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx);
+
+fail:
+	return result;
+}
+
+int ipa2_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPAERR("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPAERR("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+	/* enable tx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+		IPAERR("fail to enable tx pipe\n");
+		WARN_ON(1);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* resume tx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+		IPAERR("fail to resume tx pipe\n");
+		WARN_ON(1);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* enable rx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+		IPAERR("fail to enable rx pipe\n");
+		WARN_ON(1);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* resume rx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+		IPAERR("fail to resume rx pipe\n");
+		WARN_ON(1);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+	/* enable data path */
+	result = ipa_enable_data_path(ipa_ep_idx_rx);
+	if (result) {
+		IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_rx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+
+	result = ipa_enable_data_path(ipa_ep_idx_tx);
+	if (result) {
+		IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_tx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+fail:
+	return result;
+}
+
+int ipa2_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	ep_tx = &ipa_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa_ctx->ep[ipa_ep_idx_rx];
+
+	/* suspend tx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+		IPAERR("fail to suspend tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* disable tx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+		IPAERR("fail to disable tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* suspend rx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+		IPAERR("fail to suspend rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* disable rx pipe */
+	if (ipa_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+		IPAERR("fail to disable rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+fail:
+	return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
index 9c75202..8ea1d99 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -411,12 +411,15 @@
 {
 	int i, j;
 
+	/* prevent multi-threads accessing num_q6_rule */
+	mutex_lock(&add_mux_channel_lock);
 	if (rule_req->filter_spec_list_valid == true) {
 		num_q6_rule = rule_req->filter_spec_list_len;
 		IPAWANDBG("Received (%d) install_flt_req\n", num_q6_rule);
 	} else {
 		num_q6_rule = 0;
 		IPAWANERR("got no UL rules from modem\n");
+		mutex_unlock(&add_mux_channel_lock);
 		return -EINVAL;
 	}
 
@@ -610,9 +613,11 @@
 	num_q6_rule = 0;
 	memset(ipa_qmi_ctx->q6_ul_filter_rule, 0,
 		sizeof(ipa_qmi_ctx->q6_ul_filter_rule));
+	mutex_unlock(&add_mux_channel_lock);
 	return -EINVAL;
 
 success:
+	mutex_unlock(&add_mux_channel_lock);
 	return 0;
 }
 
@@ -644,6 +649,8 @@
 		return -ENOMEM;
 	}
 
+	memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
+
 	param->commit = 1;
 	param->ep = IPA_CLIENT_APPS_LAN_WAN_PROD;
 	param->global = false;
@@ -1511,8 +1518,8 @@
 		/*  Get driver name  */
 		case RMNET_IOCTL_GET_DRIVER_NAME:
 			memcpy(&extend_ioctl_data.u.if_name,
-						ipa_netdevs[0]->name,
-							sizeof(IFNAMSIZ));
+						ipa_netdevs[0]->name, IFNAMSIZ);
+			extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0';
 			if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data,
 					&extend_ioctl_data,
 					sizeof(struct rmnet_ioctl_extended_s)))
@@ -1535,6 +1542,8 @@
 				mutex_unlock(&add_mux_channel_lock);
 				return -EFAULT;
 			}
+			extend_ioctl_data.u.rmnet_mux_val.vchannel_name
+				[IFNAMSIZ-1] = '\0';
 			IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
 			extend_ioctl_data.u.rmnet_mux_val.mux_id,
 			extend_ioctl_data.u.rmnet_mux_val.vchannel_name);
@@ -1621,9 +1630,12 @@
 				/* already got Q6 UL filter rules*/
 				if (ipa_qmi_ctx &&
 					ipa_qmi_ctx->modem_cfg_emb_pipe_flt
-					== false)
+					== false) {
+					/* protect num_q6_rule */
+					mutex_lock(&add_mux_channel_lock);
 					rc = wwan_add_ul_flt_rule_to_ipa();
-				else
+					mutex_unlock(&add_mux_channel_lock);
+				} else
 					rc = 0;
 				egress_set = true;
 				if (rc)
@@ -1653,6 +1665,7 @@
 				sizeof(wan_msg->upstream_ifname);
 			strlcpy(wan_msg->upstream_ifname,
 				extend_ioctl_data.u.if_name, len);
+			wan_msg->upstream_ifname[len - 1] = '\0';
 			memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 			msg_meta.msg_type = WAN_XLAT_CONNECT;
 			msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -2685,6 +2698,9 @@
 	enum ipa_upstream_type upstream_type;
 	int rc = 0;
 
+	/* prevent string buffer overflows */
+	data->interface_name[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->interface_name);
 
@@ -2974,6 +2990,10 @@
 	enum ipa_upstream_type upstream_type;
 	int rc = 0;
 
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+	data->tetherIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
@@ -3008,6 +3028,10 @@
 	int rc = 0;
 
 	memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
@@ -3051,6 +3075,9 @@
 
 	memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
 
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/Makefile b/drivers/platform/msm/ipa/ipa_v3/Makefile
index e3f8d45..ae4dccf 100644
--- a/drivers/platform/msm/ipa/ipa_v3/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/Makefile
@@ -4,6 +4,6 @@
 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_hw_stats.o ipa_pm.o ipa_wdi3_i.o
 
 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 ede5254..59d93f3 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -86,6 +86,9 @@
 #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
@@ -147,15 +150,33 @@
 #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_NAT_DMA32 _IOWR(IPA_IOC_MAGIC, \
-				IPA_IOCTL_NAT_DMA, \
+#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)
@@ -211,6 +232,18 @@
 	compat_size_t size;
 	compat_off_t offset;
 };
+
+/**
+ * struct ipa_ioc_nat_ipv6ct_table_alloc32 - table memory allocation
+ * properties
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_ipv6ct_table_alloc32 {
+	compat_size_t size;
+	compat_off_t offset;
+};
+
 #endif
 
 #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311
@@ -236,8 +269,8 @@
 	ipa3_transport_release_resource);
 static void ipa_gsi_notify_cb(struct gsi_per_notify *notify);
 
-static void ipa3_post_init_wq(struct work_struct *work);
-static DECLARE_WORK(ipa3_post_init_work, ipa3_post_init_wq);
+static void ipa3_load_ipa_fw(struct work_struct *work);
+static DECLARE_WORK(ipa3_fw_loading_work, ipa3_load_ipa_fw);
 
 static void ipa_dec_clients_disable_clks_on_wq(struct work_struct *work);
 static DECLARE_WORK(ipa_dec_clients_disable_clks_on_wq_work,
@@ -255,7 +288,7 @@
 	bool present;
 	bool arm_smmu;
 	bool fast_map;
-	bool s1_bypass;
+	bool s1_bypass_arr[IPA_SMMU_CB_MAX];
 	bool use_64_bit_dma_mask;
 	u32 ipa_base;
 	u32 ipa_size;
@@ -436,14 +469,6 @@
 		flags);
 }
 
-enum ipa_smmu_cb_type {
-	IPA_SMMU_CB_AP,
-	IPA_SMMU_CB_WLAN,
-	IPA_SMMU_CB_UC,
-	IPA_SMMU_CB_MAX
-
-};
-
 static struct ipa_smmu_cb_ctx smmu_cb[IPA_SMMU_CB_MAX];
 
 struct iommu_domain *ipa3_get_smmu_domain(void)
@@ -540,11 +565,12 @@
 	kfree(buff);
 }
 
-static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type)
+static int ipa3_send_wan_msg(unsigned long usr_param, uint8_t msg_type, bool is_cache)
 {
 	int retval;
 	struct ipa_wan_msg *wan_msg;
 	struct ipa_msg_meta msg_meta;
+	struct ipa_wan_msg cache_wan_msg;
 
 	wan_msg = kzalloc(sizeof(struct ipa_wan_msg), GFP_KERNEL);
 	if (!wan_msg) {
@@ -552,12 +578,14 @@
 		return -ENOMEM;
 	}
 
-	if (copy_from_user((u8 *)wan_msg, (u8 *)usr_param,
+	if (copy_from_user(wan_msg, (const void __user *)usr_param,
 		sizeof(struct ipa_wan_msg))) {
 		kfree(wan_msg);
 		return -EFAULT;
 	}
 
+	memcpy(&cache_wan_msg, wan_msg, sizeof(cache_wan_msg));
+
 	memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 	msg_meta.msg_type = msg_type;
 	msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -568,6 +596,25 @@
 		return retval;
 	}
 
+	if (is_cache) {
+		mutex_lock(&ipa3_ctx->ipa_cne_evt_lock);
+
+		/* cache the cne event */
+		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+			ipa3_ctx->num_ipa_cne_evt_req].wan_msg,
+			&cache_wan_msg,
+			sizeof(cache_wan_msg));
+
+		memcpy(&ipa3_ctx->ipa_cne_evt_req_cache[
+			ipa3_ctx->num_ipa_cne_evt_req].msg_meta,
+			&msg_meta,
+			sizeof(struct ipa_msg_meta));
+
+		ipa3_ctx->num_ipa_cne_evt_req++;
+		ipa3_ctx->num_ipa_cne_evt_req %= IPA_MAX_NUM_REQ_CACHE;
+		mutex_unlock(&ipa3_ctx->ipa_cne_evt_lock);
+	}
+
 	return 0;
 }
 
@@ -663,10 +710,14 @@
 	u8 header[128] = { 0 };
 	u8 *param = NULL;
 	struct ipa_ioc_nat_alloc_mem nat_mem;
+	struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
 	struct ipa_ioc_v4_nat_init nat_init;
+	struct ipa_ioc_ipv6ct_init ipv6ct_init;
 	struct ipa_ioc_v4_nat_del nat_del;
+	struct ipa_ioc_nat_ipv6ct_table_del table_del;
 	struct ipa_ioc_nat_pdn_entry mdfy_pdn;
 	struct ipa_ioc_rm_dependency rm_depend;
+	struct ipa_ioc_nat_dma_cmd *table_dma_cmd;
 	size_t sz;
 	int pre_entry;
 
@@ -674,8 +725,6 @@
 
 	if (_IOC_TYPE(cmd) != IPA_IOC_MAGIC)
 		return -ENOTTY;
-	if (_IOC_NR(cmd) >= IPA_IOCTL_MAX)
-		return -ENOTTY;
 
 	if (!ipa3_is_ready()) {
 		IPAERR("IPA not ready, waiting for init completion\n");
@@ -686,8 +735,8 @@
 
 	switch (cmd) {
 	case IPA_IOC_ALLOC_NAT_MEM:
-		if (copy_from_user((u8 *)&nat_mem, (u8 *)arg,
-					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+		if (copy_from_user(&nat_mem, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_alloc_mem))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -698,15 +747,53 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, (u8 *)&nat_mem,
-					sizeof(struct ipa_ioc_nat_alloc_mem))) {
+		if (copy_to_user((void __user *)arg, &nat_mem,
+			sizeof(struct ipa_ioc_nat_alloc_mem))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
+	case IPA_IOC_ALLOC_NAT_TABLE:
+		if (copy_from_user(&table_alloc, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa3_allocate_nat_table(&table_alloc)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (table_alloc.offset &&
+			copy_to_user((void __user *)arg, &table_alloc, sizeof(
+				struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ALLOC_IPV6CT_TABLE:
+		if (copy_from_user(&table_alloc, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa3_allocate_ipv6ct_table(&table_alloc)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (table_alloc.offset &&
+			copy_to_user((void __user *)arg, &table_alloc, sizeof(
+				struct ipa_ioc_nat_ipv6ct_table_alloc))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case IPA_IOC_V4_INIT_NAT:
-		if (copy_from_user((u8 *)&nat_init, (u8 *)arg,
-					sizeof(struct ipa_ioc_v4_nat_init))) {
+		if (copy_from_user(&nat_init, (const void __user *)arg,
+			sizeof(struct ipa_ioc_v4_nat_init))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -716,45 +803,56 @@
 		}
 		break;
 
-	case IPA_IOC_NAT_DMA:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_nat_dma_cmd))) {
+	case IPA_IOC_INIT_IPV6CT_TABLE:
+		if (copy_from_user(&ipv6ct_init, (const void __user *)arg,
+			sizeof(struct ipa_ioc_ipv6ct_init))) {
 			retval = -EFAULT;
 			break;
 		}
-		pre_entry =
-			((struct ipa_ioc_nat_dma_cmd *)header)->entries;
-		pyld_sz =
-		   sizeof(struct ipa_ioc_nat_dma_cmd) +
-		   pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
+		if (ipa3_ipv6ct_init_cmd(&ipv6ct_init)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_TABLE_DMA_CMD:
+		table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)header;
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_dma_cmd))) {
+			retval = -EFAULT;
+			break;
+		}
+		pre_entry = table_dma_cmd->entries;
+		pyld_sz = sizeof(struct ipa_ioc_nat_dma_cmd) +
+			pre_entry * sizeof(struct ipa_ioc_nat_dma_one);
 		param = kzalloc(pyld_sz, GFP_KERNEL);
 		if (!param) {
 			retval = -ENOMEM;
 			break;
 		}
 
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
+		table_dma_cmd = (struct ipa_ioc_nat_dma_cmd *)param;
+
 		/* add check in case user-space module compromised */
-		if (unlikely(((struct ipa_ioc_nat_dma_cmd *)param)->entries
-			!= pre_entry)) {
+		if (unlikely(table_dma_cmd->entries != pre_entry)) {
 			IPAERR_RL("current %d pre %d\n",
-				((struct ipa_ioc_nat_dma_cmd *)param)->entries,
-				pre_entry);
+				table_dma_cmd->entries, pre_entry);
 			retval = -EFAULT;
 			break;
 		}
-		if (ipa3_nat_dma_cmd((struct ipa_ioc_nat_dma_cmd *)param)) {
+		if (ipa3_table_dma_cmd(table_dma_cmd)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_V4_DEL_NAT:
-		if (copy_from_user((u8 *)&nat_del, (u8 *)arg,
-					sizeof(struct ipa_ioc_v4_nat_del))) {
+		if (copy_from_user(&nat_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_v4_nat_del))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -764,8 +862,32 @@
 		}
 		break;
 
+	case IPA_IOC_DEL_NAT_TABLE:
+		if (copy_from_user(&table_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa3_del_nat_table(&table_del)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_DEL_IPV6CT_TABLE:
+		if (copy_from_user(&table_del, (const void __user *)arg,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_del))) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa3_del_ipv6ct_table(&table_del)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case IPA_IOC_NAT_MODIFY_PDN:
-		if (copy_from_user((u8 *)&mdfy_pdn, (const void __user *)arg,
+		if (copy_from_user(&mdfy_pdn, (const void __user *)arg,
 			sizeof(struct ipa_ioc_nat_pdn_entry))) {
 			retval = -EFAULT;
 			break;
@@ -777,8 +899,8 @@
 		break;
 
 	case IPA_IOC_ADD_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -792,7 +914,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -809,15 +931,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -831,7 +953,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -849,15 +971,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -871,7 +993,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -889,13 +1011,56 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case IPA_IOC_ADD_RT_RULE_EXT:
+		if (copy_from_user(header,
+				(const void __user *)arg,
+				sizeof(struct ipa_ioc_add_rt_rule_ext))) {
+			retval = -EFAULT;
+			break;
+		}
+		pre_entry =
+			((struct ipa_ioc_add_rt_rule_ext *)header)->num_rules;
+		pyld_sz =
+		   sizeof(struct ipa_ioc_add_rt_rule_ext) +
+		   pre_entry * sizeof(struct ipa_rt_rule_add_ext);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		/* add check in case user-space module compromised */
+		if (unlikely(
+			((struct ipa_ioc_add_rt_rule_ext *)param)->num_rules
+			!= pre_entry)) {
+			IPAERR(" prevent memory corruption(%d not match %d)\n",
+				((struct ipa_ioc_add_rt_rule_ext *)param)->
+				num_rules,
+				pre_entry);
+			retval = -EINVAL;
+			break;
+		}
+		if (ipa3_add_rt_rule_ext(
+			(struct ipa_ioc_add_rt_rule_ext *)param)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_ADD_RT_RULE_AFTER:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_add_rt_rule_after))) {
 
 			retval = -EFAULT;
@@ -911,7 +1076,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -931,15 +1096,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_MDFY_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_mdfy_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_mdfy_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -953,7 +1118,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -971,15 +1136,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_RT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_rt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_rt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -993,7 +1158,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1010,15 +1175,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_add_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1032,7 +1197,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1050,15 +1215,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_ADD_FLT_RULE_AFTER:
-		if (copy_from_user(header, (u8 *)arg,
-				sizeof(struct ipa_ioc_add_flt_rule_after))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_add_flt_rule_after))) {
 
 			retval = -EFAULT;
 			break;
@@ -1074,7 +1239,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1093,15 +1258,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_DEL_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_del_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_del_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1115,7 +1280,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1133,15 +1298,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 
 	case IPA_IOC_MDFY_FLT_RULE:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_mdfy_flt_rule))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_mdfy_flt_rule))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1155,7 +1320,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1173,7 +1338,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1198,8 +1363,8 @@
 		retval = ipa3_reset_flt(arg);
 		break;
 	case IPA_IOC_GET_RT_TBL:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_get_rt_tbl))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_rt_tbl))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1207,7 +1372,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
+		if (copy_to_user((void __user *)arg, header,
 					sizeof(struct ipa_ioc_get_rt_tbl))) {
 			retval = -EFAULT;
 			break;
@@ -1217,8 +1382,8 @@
 		retval = ipa3_put_rt_tbl(arg);
 		break;
 	case IPA_IOC_GET_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_get_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1226,8 +1391,8 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_get_hdr))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_get_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1239,8 +1404,8 @@
 		retval = ipa3_cfg_filter(arg);
 		break;
 	case IPA_IOC_COPY_HDR:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_copy_hdr))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_copy_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1248,15 +1413,15 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_copy_hdr))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_copy_hdr))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_query_intf))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_query_intf))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1264,21 +1429,21 @@
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_query_intf))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_query_intf))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_TX_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_tx_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_tx_props *)header)->num_tx_props
-				> IPA_NUM_PROPS_MAX) {
+			> IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1292,7 +1457,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1307,24 +1472,24 @@
 			break;
 		}
 		if (ipa3_query_intf_tx_props(
-				(struct ipa_ioc_query_intf_tx_props *)param)) {
+			(struct ipa_ioc_query_intf_tx_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_RX_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_rx_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_rx_props *)header)->num_rx_props
-				> IPA_NUM_PROPS_MAX) {
+			> IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1338,7 +1503,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1352,24 +1517,24 @@
 			break;
 		}
 		if (ipa3_query_intf_rx_props(
-				(struct ipa_ioc_query_intf_rx_props *)param)) {
+			(struct ipa_ioc_query_intf_rx_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_QUERY_INTF_EXT_PROPS:
 		sz = sizeof(struct ipa_ioc_query_intf_ext_props);
-		if (copy_from_user(header, (u8 *)arg, sz)) {
+		if (copy_from_user(header, (const void __user *)arg, sz)) {
 			retval = -EFAULT;
 			break;
 		}
 
 		if (((struct ipa_ioc_query_intf_ext_props *)
-				header)->num_ext_props > IPA_NUM_PROPS_MAX) {
+			header)->num_ext_props > IPA_NUM_PROPS_MAX) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1383,7 +1548,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1397,18 +1562,18 @@
 			break;
 		}
 		if (ipa3_query_intf_ext_props(
-				(struct ipa_ioc_query_intf_ext_props *)param)) {
+			(struct ipa_ioc_query_intf_ext_props *)param)) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_PULL_MSG:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_msg_meta))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_msg_meta))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1421,7 +1586,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1435,20 +1600,24 @@
 			break;
 		}
 		if (ipa3_pull_msg((struct ipa_msg_meta *)param,
-				 (char *)param + sizeof(struct ipa_msg_meta),
-				 ((struct ipa_msg_meta *)param)->msg_len) !=
-		       ((struct ipa_msg_meta *)param)->msg_len) {
+			(char *)param + sizeof(struct ipa_msg_meta),
+			((struct ipa_msg_meta *)param)->msg_len) !=
+			((struct ipa_msg_meta *)param)->msg_len) {
 			retval = -1;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_RM_ADD_DEPENDENCY:
-		if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
-				sizeof(struct ipa_ioc_rm_dependency))) {
+		/* deprecate if IPA PM is used */
+		if (ipa3_ctx->use_ipa_pm)
+			return 0;
+
+		if (copy_from_user(&rm_depend, (const void __user *)arg,
+			sizeof(struct ipa_ioc_rm_dependency))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1456,8 +1625,12 @@
 			rm_depend.resource_name, rm_depend.depends_on_name);
 		break;
 	case IPA_IOC_RM_DEL_DEPENDENCY:
-		if (copy_from_user((u8 *)&rm_depend, (u8 *)arg,
-				sizeof(struct ipa_ioc_rm_dependency))) {
+		/* deprecate if IPA PM is used */
+		if (ipa3_ctx->use_ipa_pm)
+			return 0;
+
+		if (copy_from_user(&rm_depend, (const void __user *)arg,
+			sizeof(struct ipa_ioc_rm_dependency))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1468,7 +1641,7 @@
 		{
 			struct ipa_ioc_generate_flt_eq flt_eq;
 
-			if (copy_from_user(&flt_eq, (u8 *)arg,
+			if (copy_from_user(&flt_eq, (const void __user *)arg,
 				sizeof(struct ipa_ioc_generate_flt_eq))) {
 				retval = -EFAULT;
 				break;
@@ -1478,7 +1651,7 @@
 				retval = -EFAULT;
 				break;
 			}
-			if (copy_to_user((u8 *)arg, &flt_eq,
+			if (copy_to_user((void __user *)arg, &flt_eq,
 				sizeof(struct ipa_ioc_generate_flt_eq))) {
 				retval = -EFAULT;
 				break;
@@ -1491,25 +1664,25 @@
 			break;
 		}
 	case IPA_IOC_QUERY_RT_TBL_INDEX:
-		if (copy_from_user(header, (u8 *)arg,
-				sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
 			retval = -EFAULT;
 			break;
 		}
 		if (ipa3_query_rt_index(
-			 (struct ipa_ioc_get_rt_tbl_indx *)header)) {
+			(struct ipa_ioc_get_rt_tbl_indx *)header)) {
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-				sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_get_rt_tbl_indx))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_WRITE_QMAPID:
-		if (copy_from_user(header, (u8 *)arg,
-					sizeof(struct ipa_ioc_write_qmapid))) {
+		if (copy_from_user(header, (const void __user *)arg,
+			sizeof(struct ipa_ioc_write_qmapid))) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1517,35 +1690,35 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, header,
-					sizeof(struct ipa_ioc_write_qmapid))) {
+		if (copy_to_user((void __user *)arg, header,
+			sizeof(struct ipa_ioc_write_qmapid))) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_ADD:
-		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD);
+		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_ADD, true);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_UPSTREAM_ROUTE_DEL:
-		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL);
+		retval = ipa3_send_wan_msg(arg, WAN_UPSTREAM_ROUTE_DEL, true);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_NOTIFY_WAN_EMBMS_CONNECTED:
-		retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT);
+		retval = ipa3_send_wan_msg(arg, WAN_EMBMS_CONNECT, false);
 		if (retval) {
 			IPAERR("ipa3_send_wan_msg failed: %d\n", retval);
 			break;
 		}
 		break;
 	case IPA_IOC_ADD_HDR_PROC_CTX:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_add_hdr_proc_ctx))) {
 			retval = -EFAULT;
 			break;
@@ -1561,7 +1734,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1579,13 +1752,13 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
 		break;
 	case IPA_IOC_DEL_HDR_PROC_CTX:
-		if (copy_from_user(header, (u8 *)arg,
+		if (copy_from_user(header, (const void __user *)arg,
 			sizeof(struct ipa_ioc_del_hdr_proc_ctx))) {
 			retval = -EFAULT;
 			break;
@@ -1600,7 +1773,7 @@
 			retval = -ENOMEM;
 			break;
 		}
-		if (copy_from_user(param, (u8 *)arg, pyld_sz)) {
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1619,7 +1792,7 @@
 			retval = -EFAULT;
 			break;
 		}
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1633,7 +1806,7 @@
 			break;
 		}
 		memcpy(param, &ipa3_ctx->ipa_hw_type, pyld_sz);
-		if (copy_to_user((u8 *)arg, param, pyld_sz)) {
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
 			retval = -EFAULT;
 			break;
 		}
@@ -1667,7 +1840,7 @@
 		}
 		break;
 
-	default:        /* redundant, as cmd was checked against MAXNR */
+	default:
 		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 		return -ENOTTY;
 	}
@@ -1691,8 +1864,8 @@
 	struct ipa_rt_rule_add *rt_rule_entry;
 
 	rt_rule =
-	   kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
-			   sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
+		kzalloc(sizeof(struct ipa_ioc_add_rt_rule) + 1 *
+			sizeof(struct ipa_rt_rule_add), GFP_KERNEL);
 	if (!rt_rule) {
 		IPAERR("fail to alloc mem\n");
 		return -ENOMEM;
@@ -1702,7 +1875,7 @@
 	rt_rule->commit = 1;
 	rt_rule->ip = IPA_IP_v4;
 	strlcpy(rt_rule->rt_tbl_name, IPA_DFLT_RT_TBL_NAME,
-			IPA_RESOURCE_NAME_MAX);
+		IPA_RESOURCE_NAME_MAX);
 
 	rt_rule_entry = &rt_rule->rules[0];
 	rt_rule_entry->at_rear = 1;
@@ -1831,10 +2004,7 @@
 		IPAERR("failed to construct dma_shared_mem imm cmd\n");
 		return -ENOMEM;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 
 	rc = ipa3_send_cmd(1, &desc);
 	if (rc) {
@@ -1946,6 +2116,12 @@
 			if (ep_idx == -1)
 				continue;
 
+			/* from IPA 4.0 pipe suspend is not supported */
+			if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0)
+				ipahal_write_reg_n_fields(
+				IPA_ENDP_INIT_CTRL_n,
+				ep_idx, &ep_suspend);
+
 			/*
 			 * ipa3_cfg_ep_holb is not used here because we are
 			 * setting HOLB on Q6 pipes, and from APPS perspective
@@ -1958,12 +2134,6 @@
 			ipahal_write_reg_n_fields(
 				IPA_ENDP_INIT_HOL_BLOCK_EN_n,
 				ep_idx, &ep_holb);
-
-			/* from IPA 4.0 pipe suspend is not supported */
-			if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0)
-				ipahal_write_reg_n_fields(
-					IPA_ENDP_INIT_CTRL_n,
-					ep_idx, &ep_suspend);
 		}
 	}
 }
@@ -2095,6 +2265,12 @@
 		if (!ipa3_ctx->ep[pipe_idx].valid ||
 		    ipa3_ctx->ep[pipe_idx].skip_ep_cfg) {
 
+			if (num_cmds >= ipa3_ctx->ep_flt_num) {
+				IPAERR("number of commands is out of range\n");
+				retval = -ENOBUFS;
+				goto free_empty_img;
+			}
+
 			cmd.is_read = false;
 			cmd.skip_pipeline_clear = false;
 			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -2112,14 +2288,12 @@
 				retval = -ENOMEM;
 				goto free_empty_img;
 			}
-			desc[num_cmds].opcode = cmd_pyld[num_cmds]->opcode;
-			desc[num_cmds].pyld = cmd_pyld[num_cmds]->data;
-			desc[num_cmds].len = cmd_pyld[num_cmds]->len;
-			desc[num_cmds].type = IPA_IMM_CMD_DESC;
-			num_cmds++;
+			ipa3_init_imm_cmd_desc(&desc[num_cmds],
+				cmd_pyld[num_cmds]);
+			++num_cmds;
 		}
 
-		flt_idx++;
+		++flt_idx;
 	}
 
 	IPADBG("Sending %d descriptors for flt tbl clearing\n", num_cmds);
@@ -2211,10 +2385,7 @@
 		retval = -ENOMEM;
 		goto free_desc;
 	}
-	desc->opcode = cmd_pyld->opcode;
-	desc->pyld = cmd_pyld->data;
-	desc->len = cmd_pyld->len;
-	desc->type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(desc, cmd_pyld);
 
 	IPADBG("Sending 1 descriptor for rt tbl clearing\n");
 	retval = ipa3_send_cmd(1, desc);
@@ -2301,10 +2472,7 @@
 		retval = -EFAULT;
 		goto bail_desc;
 	}
-	desc->opcode = cmd_pyld->opcode;
-	desc->pyld = cmd_pyld->data;
-	desc->len = cmd_pyld->len;
-	desc->type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(desc, cmd_pyld);
 
 	IPADBG("Sending 1 descriptor for tbls flush\n");
 	retval = ipa3_send_cmd(1, desc);
@@ -2331,7 +2499,6 @@
 	struct ipahal_imm_cmd_register_write reg_write;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
 	int retval;
-	struct ipahal_reg_valmask valmask;
 
 	desc = kcalloc(ipa3_ctx->ipa_num_pipes, sizeof(struct ipa3_desc),
 			GFP_KERNEL);
@@ -2346,39 +2513,10 @@
 		if (ep_idx == -1)
 			continue;
 
-		if (ipa3_ctx->ep[ep_idx].valid &&
-			ipa3_ctx->ep[ep_idx].skip_ep_cfg) {
-			BUG_ON(num_descs >= ipa3_ctx->ipa_num_pipes);
-
-			reg_write.skip_pipeline_clear = false;
-			reg_write.pipeline_clear_options =
-				IPAHAL_HPS_CLEAR;
-			reg_write.offset =
-				ipahal_get_reg_n_ofst(IPA_ENDP_STATUS_n,
-					ep_idx);
-			ipahal_get_status_ep_valmask(
-				ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS),
-				&valmask);
-			reg_write.value = valmask.val;
-			reg_write.value_mask = valmask.mask;
-			cmd_pyld = ipahal_construct_imm_cmd(
-				IPA_IMM_CMD_REGISTER_WRITE, &reg_write, false);
-			if (!cmd_pyld) {
-				IPAERR("fail construct register_write cmd\n");
-				BUG();
-			}
-
-			desc[num_descs].opcode = cmd_pyld->opcode;
-			desc[num_descs].type = IPA_IMM_CMD_DESC;
-			desc[num_descs].callback = ipa3_destroy_imm;
-			desc[num_descs].user1 = cmd_pyld;
-			desc[num_descs].pyld = cmd_pyld->data;
-			desc[num_descs].len = cmd_pyld->len;
-			num_descs++;
-		}
-
-		/* disable statuses for modem producers */
-		if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
+		/* disable statuses for all modem controlled prod pipes */
+		if (IPA_CLIENT_IS_Q6_PROD(client_idx) ||
+			(ipa3_ctx->ep[ep_idx].valid &&
+			ipa3_ctx->ep[ep_idx].skip_ep_cfg)) {
 			ipa_assert_on(num_descs >= ipa3_ctx->ipa_num_pipes);
 
 			reg_write.skip_pipeline_clear = false;
@@ -2397,13 +2535,10 @@
 				return -EFAULT;
 			}
 
-			desc[num_descs].opcode = cmd_pyld->opcode;
-			desc[num_descs].type = IPA_IMM_CMD_DESC;
+			ipa3_init_imm_cmd_desc(&desc[num_descs], cmd_pyld);
 			desc[num_descs].callback = ipa3_destroy_imm;
 			desc[num_descs].user1 = cmd_pyld;
-			desc[num_descs].pyld = cmd_pyld->data;
-			desc[num_descs].len = cmd_pyld->len;
-			num_descs++;
+			++num_descs;
 		}
 	}
 
@@ -2573,7 +2708,7 @@
  */
 int _ipa_init_hdr_v3_0(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_hdr_init_local cmd = {0};
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2601,10 +2736,7 @@
 			mem.phys_base);
 		return -EFAULT;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2628,7 +2760,6 @@
 		return -ENOMEM;
 	}
 	memset(mem.base, 0, mem.size);
-	memset(&desc, 0, sizeof(desc));
 
 	dma_cmd.is_read = false;
 	dma_cmd.skip_pipeline_clear = false;
@@ -2646,10 +2777,7 @@
 			mem.phys_base);
 		return -EFAULT;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2677,7 +2805,7 @@
  */
 int _ipa_init_rt4_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v4_routing_init v4_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2718,10 +2846,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2743,7 +2868,7 @@
  */
 int _ipa_init_rt6_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v6_routing_init v6_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2784,10 +2909,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2809,7 +2931,7 @@
  */
 int _ipa_init_flt4_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v4_filter_init v4_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2844,10 +2966,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -2869,7 +2988,7 @@
  */
 int _ipa_init_flt6_v3(void)
 {
-	struct ipa3_desc desc = { 0 };
+	struct ipa3_desc desc;
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_ip_v6_filter_init v6_cmd;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -2905,10 +3024,7 @@
 		goto free_mem;
 	}
 
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 	IPA_DUMP_BUFF(mem.base, mem.phys_base, mem.size);
 
 	if (ipa3_send_cmd(1, &desc)) {
@@ -3126,9 +3242,38 @@
 }
 
 #ifdef CONFIG_COMPAT
+
+static long compat_ipa3_nat_ipv6ct_alloc_table(unsigned long arg,
+	int (alloc_func)(struct ipa_ioc_nat_ipv6ct_table_alloc *))
+{
+	long retval;
+	struct ipa_ioc_nat_ipv6ct_table_alloc32 table_alloc32;
+	struct ipa_ioc_nat_ipv6ct_table_alloc table_alloc;
+
+	retval = copy_from_user(&table_alloc32, (const void __user *)arg,
+		sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+	if (retval)
+		return retval;
+
+	table_alloc.size = (size_t)table_alloc32.size;
+	table_alloc.offset = (off_t)table_alloc32.offset;
+
+	retval = alloc_func(&table_alloc);
+	if (retval)
+		return retval;
+
+	if (table_alloc.offset) {
+		table_alloc32.offset = (compat_off_t)table_alloc.offset;
+		retval = copy_to_user((void __user *)arg, &table_alloc32,
+			sizeof(struct ipa_ioc_nat_ipv6ct_table_alloc32));
+	}
+
+	return retval;
+}
+
 long compat_ipa3_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int retval = 0;
+	long retval = 0;
 	struct ipa3_ioc_nat_alloc_mem32 nat_mem32;
 	struct ipa_ioc_nat_alloc_mem nat_mem;
 
@@ -3173,11 +3318,10 @@
 		cmd = IPA_IOC_GET_HDR;
 		break;
 	case IPA_IOC_ALLOC_NAT_MEM32:
-		if (copy_from_user((u8 *)&nat_mem32, (u8 *)arg,
-			sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
-			retval = -EFAULT;
-			goto ret;
-		}
+		retval = copy_from_user(&nat_mem32, (const void __user *)arg,
+			sizeof(struct ipa3_ioc_nat_alloc_mem32));
+		if (retval)
+			return retval;
 		memcpy(nat_mem.dev_name, nat_mem32.dev_name,
 				IPA_RESOURCE_NAME_MAX);
 		nat_mem.size = (size_t)nat_mem32.size;
@@ -3186,26 +3330,40 @@
 		/* null terminate the string */
 		nat_mem.dev_name[IPA_RESOURCE_NAME_MAX - 1] = '\0';
 
-		if (ipa3_allocate_nat_device(&nat_mem)) {
-			retval = -EFAULT;
-			goto ret;
-		}
+		retval = ipa3_allocate_nat_device(&nat_mem);
+		if (retval)
+			return retval;
 		nat_mem32.offset = (compat_off_t)nat_mem.offset;
-		if (copy_to_user((u8 *)arg, (u8 *)&nat_mem32,
-			sizeof(struct ipa3_ioc_nat_alloc_mem32))) {
-			retval = -EFAULT;
-		}
-ret:
+		retval = copy_to_user((void __user *)arg, &nat_mem32,
+			sizeof(struct ipa3_ioc_nat_alloc_mem32));
 		return retval;
+	case IPA_IOC_ALLOC_NAT_TABLE32:
+		return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+			ipa3_allocate_nat_table);
+	case IPA_IOC_ALLOC_IPV6CT_TABLE32:
+		return compat_ipa3_nat_ipv6ct_alloc_table(arg,
+			ipa3_allocate_ipv6ct_table);
 	case IPA_IOC_V4_INIT_NAT32:
 		cmd = IPA_IOC_V4_INIT_NAT;
 		break;
-	case IPA_IOC_NAT_DMA32:
-		cmd = IPA_IOC_NAT_DMA;
+	case IPA_IOC_INIT_IPV6CT_TABLE32:
+		cmd = IPA_IOC_INIT_IPV6CT_TABLE;
+		break;
+	case IPA_IOC_TABLE_DMA_CMD32:
+		cmd = IPA_IOC_TABLE_DMA_CMD;
 		break;
 	case IPA_IOC_V4_DEL_NAT32:
 		cmd = IPA_IOC_V4_DEL_NAT;
 		break;
+	case IPA_IOC_DEL_NAT_TABLE32:
+		cmd = IPA_IOC_DEL_NAT_TABLE;
+		break;
+	case IPA_IOC_DEL_IPV6CT_TABLE32:
+		cmd = IPA_IOC_DEL_IPV6CT_TABLE;
+		break;
+	case IPA_IOC_NAT_MODIFY_PDN32:
+		cmd = IPA_IOC_NAT_MODIFY_PDN;
+		break;
 	case IPA_IOC_GET_NAT_OFFSET32:
 		cmd = IPA_IOC_GET_NAT_OFFSET;
 		break;
@@ -3693,6 +3851,53 @@
 	spin_unlock_irqrestore(&ipa3_ctx->wakelock_ref_cnt.spinlock, flags);
 }
 
+int ipa3_set_clock_plan_from_pm(int idx)
+{
+	u32 clk_rate;
+
+	IPADBG_LOW("idx = %d\n", idx);
+
+	if (idx <= 0 || idx >= ipa3_ctx->ctrl->msm_bus_data_ptr->num_usecases) {
+		IPAERR("bad voltage\n");
+		return -EINVAL;
+	}
+
+	if (idx == 1)
+		clk_rate = ipa3_ctx->ctrl->ipa_clk_rate_svs;
+	else if (idx == 2)
+		clk_rate = ipa3_ctx->ctrl->ipa_clk_rate_nominal;
+	else if (idx == 3)
+		clk_rate = ipa3_ctx->ctrl->ipa_clk_rate_turbo;
+	else {
+		IPAERR("bad voltage\n");
+		WARN_ON(1);
+		return -EFAULT;
+	}
+
+	if (clk_rate == ipa3_ctx->curr_ipa_clk_rate) {
+		IPADBG_LOW("Same voltage\n");
+		return 0;
+	}
+
+	mutex_lock(&ipa3_ctx->ipa3_active_clients.mutex);
+	ipa3_ctx->curr_ipa_clk_rate = clk_rate;
+	ipa3_ctx->ipa3_active_clients.bus_vote_idx = idx;
+	IPADBG_LOW("setting clock rate to %u\n", ipa3_ctx->curr_ipa_clk_rate);
+	if (atomic_read(&ipa3_ctx->ipa3_active_clients.cnt) > 0) {
+		if (ipa3_clk)
+			clk_set_rate(ipa3_clk, ipa3_ctx->curr_ipa_clk_rate);
+		if (msm_bus_scale_client_update_request(ipa3_ctx->ipa_bus_hdl,
+				ipa3_get_bus_vote()))
+			WARN_ON(1);
+	} else {
+		IPADBG_LOW("clocks are gated, not setting rate\n");
+	}
+	mutex_unlock(&ipa3_ctx->ipa3_active_clients.mutex);
+	IPADBG_LOW("Done\n");
+
+	return 0;
+}
+
 int ipa3_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
 				  u32 bandwidth_mbps)
 {
@@ -3795,14 +4000,19 @@
 	u32 i = 0;
 	int res;
 	struct ipa_ep_cfg_holb holb_cfg;
+	u32 pipe_bitmask = 0;
 
 	IPADBG("interrupt=%d, interrupt_data=%u\n",
 		interrupt, suspend_data);
 	memset(&holb_cfg, 0, sizeof(holb_cfg));
 	holb_cfg.tmr_val = 0;
 
-	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
+	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++, bmsk = bmsk << 1) {
 		if ((suspend_data & bmsk) && (ipa3_ctx->ep[i].valid)) {
+			if (ipa3_ctx->use_ipa_pm) {
+				pipe_bitmask |= bmsk;
+				continue;
+			}
 			if (IPA_CLIENT_IS_APPS_CONS(ipa3_ctx->ep[i].client)) {
 				/*
 				 * pipe will be unsuspended as part of
@@ -3841,7 +4051,13 @@
 				}
 			}
 		}
-		bmsk = bmsk << 1;
+	}
+	if (ipa3_ctx->use_ipa_pm) {
+		res = ipa_pm_handle_suspend(pipe_bitmask);
+		if (res) {
+			IPAERR("ipa_pm_handle_suspend failed %d\n", res);
+			return;
+		}
 	}
 }
 
@@ -4236,6 +4452,12 @@
 	gsi_props.req_clk_cb = NULL;
 	gsi_props.rel_clk_cb = NULL;
 
+	if (ipa3_ctx->ipa_config_is_mhi) {
+		gsi_props.mhi_er_id_limits_valid = true;
+		gsi_props.mhi_er_id_limits[0] = resource_p->mhi_evid_limits[0];
+		gsi_props.mhi_er_id_limits[1] = resource_p->mhi_evid_limits[1];
+	}
+
 	result = gsi_register_device(&gsi_props,
 		&ipa3_ctx->gsi_dev_hdl);
 	if (result != GSI_STATUS_SUCCESS) {
@@ -4296,6 +4518,7 @@
 	ipa3_register_panic_hdlr();
 
 	ipa3_ctx->q6_proxy_clk_vote_valid = true;
+	ipa3_ctx->q6_proxy_clk_vote_cnt++;
 
 	mutex_lock(&ipa3_ctx->lock);
 	ipa3_ctx->ipa_initialization_complete = true;
@@ -4316,11 +4539,6 @@
 	return result;
 }
 
-static void ipa3_post_init_wq(struct work_struct *work)
-{
-	ipa3_post_init(&ipa3_res, ipa3_ctx->dev);
-}
-
 static int ipa3_manual_load_ipa_fws(void)
 {
 	int result;
@@ -4378,11 +4596,36 @@
 	return 0;
 }
 
+static void ipa3_load_ipa_fw(struct work_struct *work)
+{
+	int result;
+
+	IPADBG("Entry\n");
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+	if (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();
+
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+	if (result) {
+		IPAERR("IPA FW loading process has failed\n");
+		return;
+	}
+	pr_info("IPA FW loaded successfully\n");
+
+	result = ipa3_post_init(&ipa3_res, ipa3_ctx->dev);
+	if (result)
+		IPAERR("IPA post init failed %d\n", result);
+}
+
 static ssize_t ipa3_write(struct file *file, const char __user *buf,
 			  size_t count, loff_t *ppos)
 {
 	unsigned long missing;
-	int result = -EINVAL;
 
 	char dbg_buff[16] = { 0 };
 
@@ -4396,6 +4639,9 @@
 		return -EFAULT;
 	}
 
+	if (count > 0)
+		dbg_buff[count - 1] = '\0';
+
 	/* Prevent consequent calls from trying to load the FW again. */
 	if (ipa3_is_ready())
 		return count;
@@ -4412,71 +4658,77 @@
 		}
 	}
 
-	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
-
-	if (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();
-
-	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
-
-	if (result) {
-		IPAERR("IPA FW loading process has failed\n");
-		return result;
-	}
-
 	queue_work(ipa3_ctx->transport_power_mgmt_wq,
-		&ipa3_post_init_work);
-	pr_info("IPA FW loaded successfully\n");
+		&ipa3_fw_loading_work);
 
+	IPADBG("Scheduled a work to load IPA FW\n");
 	return count;
 }
 
-static int ipa3_tz_unlock_reg(struct ipa3_context *ipa3_ctx)
+/**
+ * ipa3_tz_unlock_reg - Unlocks memory regions so that they become accessible
+ *	from AP.
+ * @reg_info - Pointer to array of memory regions to unlock
+ * @num_regs - Number of elements in the array
+ *
+ * Converts the input array of regions to a struct that TZ understands and
+ * issues an SCM call.
+ * Also flushes the memory cache to DDR in order to make sure that TZ sees the
+ * correct data structure.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs)
 {
 	int i, size, ret, resp;
 	struct tz_smmu_ipa_protect_region_iovec_s *ipa_tz_unlock_vec;
 	struct tz_smmu_ipa_protect_region_s cmd_buf;
+	struct scm_desc desc = {0};
 
-	if (ipa3_ctx && ipa3_ctx->ipa_tz_unlock_reg_num > 0) {
-		size = ipa3_ctx->ipa_tz_unlock_reg_num *
-			sizeof(struct tz_smmu_ipa_protect_region_iovec_s);
-		ipa_tz_unlock_vec = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
-		if (ipa_tz_unlock_vec == NULL)
-			return -ENOMEM;
-
-		for (i = 0; i < ipa3_ctx->ipa_tz_unlock_reg_num; i++) {
-			ipa_tz_unlock_vec[i].input_addr =
-				ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
-				(ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
-				0xFFF);
-			ipa_tz_unlock_vec[i].output_addr =
-				ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr ^
-				(ipa3_ctx->ipa_tz_unlock_reg[i].reg_addr &
-				0xFFF);
-			ipa_tz_unlock_vec[i].size =
-				ipa3_ctx->ipa_tz_unlock_reg[i].size;
-			ipa_tz_unlock_vec[i].attr = IPA_TZ_UNLOCK_ATTRIBUTE;
-		}
-
-		/* pass physical address of command buffer */
-		cmd_buf.iovec_buf = virt_to_phys((void *)ipa_tz_unlock_vec);
-		cmd_buf.size_bytes = size;
-
-		/* flush cache to DDR */
-		__cpuc_flush_dcache_area((void *)ipa_tz_unlock_vec, size);
-		outer_flush_range(cmd_buf.iovec_buf, cmd_buf.iovec_buf + size);
-
-		ret = scm_call(SCM_SVC_MP, TZ_MEM_PROTECT_REGION_ID, &cmd_buf,
-				sizeof(cmd_buf), &resp, sizeof(resp));
-		if (ret) {
-			IPAERR("scm call SCM_SVC_MP failed: %d\n", ret);
-			kfree(ipa_tz_unlock_vec);
-			return -EFAULT;
-		}
-		kfree(ipa_tz_unlock_vec);
+	if (reg_info ==  NULL || num_regs == 0) {
+		IPAERR("Bad parameters\n");
+		return -EFAULT;
 	}
+
+	size = num_regs * sizeof(struct tz_smmu_ipa_protect_region_iovec_s);
+	ipa_tz_unlock_vec = kzalloc(PAGE_ALIGN(size), GFP_KERNEL);
+	if (ipa_tz_unlock_vec == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < num_regs; i++) {
+		ipa_tz_unlock_vec[i].input_addr = reg_info[i].reg_addr ^
+			(reg_info[i].reg_addr & 0xFFF);
+		ipa_tz_unlock_vec[i].output_addr = reg_info[i].reg_addr ^
+			(reg_info[i].reg_addr & 0xFFF);
+		ipa_tz_unlock_vec[i].size = reg_info[i].size;
+		ipa_tz_unlock_vec[i].attr = IPA_TZ_UNLOCK_ATTRIBUTE;
+	}
+
+	/* pass physical address of command buffer */
+	cmd_buf.iovec_buf = virt_to_phys((void *)ipa_tz_unlock_vec);
+	cmd_buf.size_bytes = size;
+
+	/* flush cache to DDR */
+	__cpuc_flush_dcache_area((void *)ipa_tz_unlock_vec, size);
+	outer_flush_range(cmd_buf.iovec_buf, cmd_buf.iovec_buf + size);
+	if (!is_scm_armv8())
+		ret = scm_call(SCM_SVC_MP, TZ_MEM_PROTECT_REGION_ID,
+			&cmd_buf, sizeof(cmd_buf), &resp, sizeof(resp));
+	else {
+		desc.args[0] = virt_to_phys((void *)ipa_tz_unlock_vec);
+		desc.args[1] = size;
+		desc.arginfo = SCM_ARGS(2, SCM_RO, SCM_VAL);
+		ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+			TZ_MEM_PROTECT_REGION_ID), &desc);
+	}
+
+	if (ret) {
+		IPAERR("scm call SCM_SVC_MP failed: %d\n", ret);
+		kfree(ipa_tz_unlock_vec);
+		return -EFAULT;
+	}
+	kfree(ipa_tz_unlock_vec);
+
 	return 0;
 }
 
@@ -4583,10 +4835,14 @@
 	ipa3_ctx->pdev = ipa_dev;
 	ipa3_ctx->uc_pdev = ipa_dev;
 	ipa3_ctx->smmu_present = smmu_info.present;
-	if (!ipa3_ctx->smmu_present)
-		ipa3_ctx->smmu_s1_bypass = true;
-	else
-		ipa3_ctx->smmu_s1_bypass = smmu_info.s1_bypass;
+	if (!ipa3_ctx->smmu_present) {
+		for (i = 0; i < IPA_SMMU_CB_MAX; i++)
+			ipa3_ctx->s1_bypass_arr[i] = true;
+	} else {
+		ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP] =
+			smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP];
+	}
+
 	ipa3_ctx->ipa_wrapper_base = resource_p->ipa_mem_base;
 	ipa3_ctx->ipa_wrapper_size = resource_p->ipa_mem_size;
 	ipa3_ctx->ipa_hw_type = resource_p->ipa_hw_type;
@@ -4602,7 +4858,10 @@
 	ipa3_ctx->ee = resource_p->ee;
 	ipa3_ctx->apply_rg10_wa = resource_p->apply_rg10_wa;
 	ipa3_ctx->gsi_ch20_wa = resource_p->gsi_ch20_wa;
+	ipa3_ctx->use_ipa_pm = resource_p->use_ipa_pm;
 	ipa3_ctx->ipa3_active_clients_logging.log_rdy = false;
+	ipa3_ctx->mhi_evid_limits[0] = resource_p->mhi_evid_limits[0];
+	ipa3_ctx->mhi_evid_limits[1] = resource_p->mhi_evid_limits[1];
 	if (resource_p->ipa_tz_unlock_reg) {
 		ipa3_ctx->ipa_tz_unlock_reg_num =
 			resource_p->ipa_tz_unlock_reg_num;
@@ -4623,7 +4882,10 @@
 	}
 
 	/* unlock registers for uc */
-	ipa3_tz_unlock_reg(ipa3_ctx);
+	result = ipa3_tz_unlock_reg(ipa3_ctx->ipa_tz_unlock_reg,
+				    ipa3_ctx->ipa_tz_unlock_reg_num);
+	if (result)
+		IPAERR("Failed to unlock memory region using TZ\n");
 
 	/* default aggregation parameters */
 	ipa3_ctx->aggregation_type = IPA_MBIM_16;
@@ -4875,8 +5137,9 @@
 	mutex_init(&ipa3_ctx->msg_lock);
 
 	mutex_init(&ipa3_ctx->lock);
-	mutex_init(&ipa3_ctx->nat_mem.lock);
 	mutex_init(&ipa3_ctx->q6_proxy_clk_vote_mutex);
+	mutex_init(&ipa3_ctx->ipa_cne_evt_lock);
+	ipa3_ctx->q6_proxy_clk_vote_cnt = 0;
 
 	idr_init(&ipa3_ctx->ipa_idr);
 	spin_lock_init(&ipa3_ctx->idr_lock);
@@ -4904,37 +5167,47 @@
 		goto fail_device_create;
 	}
 
-	if (ipa3_create_nat_device()) {
-		IPAERR("unable to create nat device\n");
+	if (ipa3_nat_ipv6ct_init_devices()) {
+		IPAERR("unable to init NAT and IPv6CT devices\n");
 		result = -ENODEV;
-		goto fail_nat_dev_add;
+		goto fail_nat_ipv6ct_init_dev;
 	}
 
 	/* Create a wakeup source. */
 	wakeup_source_init(&ipa3_ctx->w_lock, "IPA_WS");
 	spin_lock_init(&ipa3_ctx->wakelock_ref_cnt.spinlock);
 
-	/* Initialize IPA RM (resource manager) */
-	result = ipa_rm_initialize();
-	if (result) {
-		IPAERR("RM initialization failed (%d)\n", -result);
-		result = -ENODEV;
-		goto fail_ipa_rm_init;
-	}
-	IPADBG("IPA resource manager initialized");
+	/* Initialize Power Management framework */
+	if (ipa3_ctx->use_ipa_pm) {
+		result = ipa_pm_init(&ipa3_res.pm_init);
+		if (result) {
+			IPAERR("IPA PM initialization failed (%d)\n", -result);
+			result = -ENODEV;
+			goto fail_ipa_rm_init;
+		}
+		IPADBG("IPA resource manager initialized");
+	} else {
+		result = ipa_rm_initialize();
+		if (result) {
+			IPAERR("RM initialization failed (%d)\n", -result);
+			result = -ENODEV;
+			goto fail_ipa_rm_init;
+		}
+		IPADBG("IPA resource manager initialized");
 
-	result = ipa3_create_apps_resource();
-	if (result) {
-		IPAERR("Failed to create APPS_CONS resource\n");
-		result = -ENODEV;
-		goto fail_create_apps_resource;
+		result = ipa3_create_apps_resource();
+		if (result) {
+			IPAERR("Failed to create APPS_CONS resource\n");
+			result = -ENODEV;
+			goto fail_create_apps_resource;
+		}
 	}
 
 	result = ipa3_alloc_pkt_init();
 	if (result) {
 		IPAERR("Failed to alloc pkt_init payload\n");
 		result = -ENODEV;
-		goto fail_create_apps_resource;
+		goto fail_allok_pkt_init;
 	}
 
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v3_5)
@@ -4945,6 +5218,13 @@
 	init_completion(&ipa3_ctx->init_completion_obj);
 	init_completion(&ipa3_ctx->uc_loaded_completion_obj);
 
+	result = ipa3_dma_setup();
+	if (result) {
+		IPAERR("Failed to setup IPA DMA\n");
+		result = -ENODEV;
+		goto fail_ipa_dma_setup;
+	}
+
 	/*
 	 * We can't register the GSI driver yet, as it expects
 	 * the GSI FW to be up and running before the registration.
@@ -4957,7 +5237,7 @@
 		if (result) {
 			IPAERR("gsi pre FW loading config failed\n");
 			result = -ENODEV;
-			goto fail_ipa_init_interrupts;
+			goto fail_gsi_pre_fw_load_init;
 		}
 	}
 
@@ -4977,16 +5257,25 @@
 	return 0;
 
 fail_cdev_add:
-fail_ipa_init_interrupts:
-	ipa_rm_delete_resource(IPA_RM_RESOURCE_APPS_CONS);
+fail_gsi_pre_fw_load_init:
+	ipa3_dma_shutdown();
+fail_ipa_dma_setup:
+fail_allok_pkt_init:
+	if (ipa3_ctx->use_ipa_pm)
+		ipa_pm_destroy();
+	else
+		ipa_rm_delete_resource(IPA_RM_RESOURCE_APPS_CONS);
 fail_create_apps_resource:
-	ipa_rm_exit();
+	if (!ipa3_ctx->use_ipa_pm)
+		ipa_rm_exit();
 fail_ipa_rm_init:
-fail_nat_dev_add:
+	ipa3_nat_ipv6ct_destroy_devices();
+fail_nat_ipv6ct_init_dev:
 	device_destroy(ipa3_ctx->class, ipa3_ctx->dev_num);
 fail_device_create:
 	unregister_chrdev_region(ipa3_ctx->dev_num, 1);
 fail_alloc_chrdev_region:
+	idr_destroy(&ipa3_ctx->ipa_idr);
 	rset = &ipa3_ctx->reap_rt_tbl_set[IPA_IP_v6];
 	idr_destroy(&rset->rule_ids);
 	rset = &ipa3_ctx->reap_rt_tbl_set[IPA_IP_v4];
@@ -4995,7 +5284,6 @@
 	idr_destroy(&ipa3_ctx->rt_tbl_set[IPA_IP_v4].rule_ids);
 	ipa3_free_dma_task_for_gsi();
 fail_dma_task:
-	idr_destroy(&ipa3_ctx->ipa_idr);
 	kmem_cache_destroy(ipa3_ctx->rx_pkt_wrapper_cache);
 fail_rx_pkt_wrapper_cache:
 	kmem_cache_destroy(ipa3_ctx->tx_pkt_wrapper_cache);
@@ -5049,6 +5337,101 @@
 	return result;
 }
 
+bool ipa_pm_is_used(void)
+{
+	return (ipa3_ctx) ? ipa3_ctx->use_ipa_pm : false;
+}
+
+static int get_ipa_dts_pm_info(struct platform_device *pdev,
+	struct ipa3_plat_drv_res *ipa_drv_res)
+{
+	int result;
+	int i, j;
+
+	ipa_drv_res->use_ipa_pm = of_property_read_bool(pdev->dev.of_node,
+		"qcom,use-ipa-pm");
+	IPADBG("use_ipa_pm=%d\n", ipa_drv_res->use_ipa_pm);
+	if (!ipa_drv_res->use_ipa_pm)
+		return 0;
+
+	result = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-bus,num-cases",
+		&ipa_drv_res->pm_init.threshold_size);
+	/* No vote is ignored */
+	ipa_drv_res->pm_init.threshold_size -= 2;
+	if (result || ipa_drv_res->pm_init.threshold_size >
+		IPA_PM_THRESHOLD_MAX) {
+		IPAERR("invalid property qcom,msm-bus,num-cases %d\n",
+			ipa_drv_res->pm_init.threshold_size);
+		return -EFAULT;
+	}
+
+	result = of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,throughput-threshold",
+		ipa_drv_res->pm_init.default_threshold,
+		ipa_drv_res->pm_init.threshold_size);
+	if (result) {
+		IPAERR("failed to read qcom,throughput-thresholds\n");
+		return -EFAULT;
+	}
+
+	result = of_property_count_strings(pdev->dev.of_node,
+		"qcom,scaling-exceptions");
+	if (result < 0) {
+		IPADBG("no exception list for ipa pm\n");
+		result = 0;
+	}
+
+	if (result % (ipa_drv_res->pm_init.threshold_size + 1)) {
+		IPAERR("failed to read qcom,scaling-exceptions\n");
+		return -EFAULT;
+	}
+
+	ipa_drv_res->pm_init.exception_size = result /
+		(ipa_drv_res->pm_init.threshold_size + 1);
+	if (ipa_drv_res->pm_init.exception_size >=
+		IPA_PM_EXCEPTION_MAX) {
+		IPAERR("exception list larger then max %d\n",
+			ipa_drv_res->pm_init.exception_size);
+		return -EFAULT;
+	}
+
+	for (i = 0; i < ipa_drv_res->pm_init.exception_size; i++) {
+		struct ipa_pm_exception *ex = ipa_drv_res->pm_init.exceptions;
+
+		result = of_property_read_string_index(pdev->dev.of_node,
+			"qcom,scaling-exceptions",
+			i * ipa_drv_res->pm_init.threshold_size,
+			&ex[i].usecase);
+		if (result) {
+			IPAERR("failed to read qcom,scaling-exceptions");
+			return -EFAULT;
+		}
+
+		for (j = 0; j < ipa_drv_res->pm_init.threshold_size; j++) {
+			const char *str;
+
+			result = of_property_read_string_index(
+				pdev->dev.of_node,
+				"qcom,scaling-exceptions",
+				i * ipa_drv_res->pm_init.threshold_size + j + 1,
+				&str);
+			if (result) {
+				IPAERR("failed to read qcom,scaling-exceptions"
+					);
+				return -EFAULT;
+			}
+
+			if (kstrtou32(str, 0, &ex[i].threshold[j])) {
+				IPAERR("error str=%s\n", str);
+				return -EFAULT;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int get_ipa_dts_configuration(struct platform_device *pdev,
 		struct ipa3_plat_drv_res *ipa_drv_res)
 {
@@ -5056,6 +5439,7 @@
 	struct resource *resource;
 	u32 *ipa_tz_unlock_reg;
 	int elem_num;
+	u32 mhi_evid_limits[2];
 
 	/* initialize ipa3_res */
 	ipa_drv_res->ipa_pipe_mem_start_ofst = IPA_PIPE_MEM_START_OFST;
@@ -5072,6 +5456,8 @@
 	ipa_drv_res->gsi_ch20_wa = false;
 	ipa_drv_res->ipa_tz_unlock_reg_num = 0;
 	ipa_drv_res->ipa_tz_unlock_reg = NULL;
+	ipa_drv_res->mhi_evid_limits[0] = IPA_MHI_GSI_EVENT_RING_ID_START;
+	ipa_drv_res->mhi_evid_limits[1] = IPA_MHI_GSI_EVENT_RING_ID_END;
 
 	/* Get IPA HW Version */
 	result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver",
@@ -5248,6 +5634,34 @@
 		? "Needed" : "Not needed");
 
 	elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
+		"qcom,mhi-event-ring-id-limits", sizeof(u32));
+
+	if (elem_num == 2) {
+		if (of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,mhi-event-ring-id-limits", mhi_evid_limits, 2)) {
+			IPAERR("failed to read mhi event ring id limits\n");
+			return -EFAULT;
+		}
+		if (mhi_evid_limits[0] > mhi_evid_limits[1]) {
+			IPAERR("mhi event ring id low limit > high limit\n");
+			return -EFAULT;
+		}
+		ipa_drv_res->mhi_evid_limits[0] = mhi_evid_limits[0];
+		ipa_drv_res->mhi_evid_limits[1] = mhi_evid_limits[1];
+		IPADBG(": mhi-event-ring-id-limits start=%u end=%u\n",
+			mhi_evid_limits[0], mhi_evid_limits[1]);
+	} else {
+		if (elem_num > 0) {
+			IPAERR("Invalid mhi event ring id limits number %d\n",
+				elem_num);
+			return -EINVAL;
+		}
+		IPADBG("use default mhi evt ring id limits start=%u end=%u\n",
+			ipa_drv_res->mhi_evid_limits[0],
+			ipa_drv_res->mhi_evid_limits[1]);
+	}
+
+	elem_num = of_property_count_elems_of_size(pdev->dev.of_node,
 		"qcom,ipa-tz-unlock-reg", sizeof(u32));
 
 	if (elem_num > 0 && elem_num % 2 == 0) {
@@ -5281,12 +5695,20 @@
 				ipa_tz_unlock_reg[pos++];
 			ipa_drv_res->ipa_tz_unlock_reg[i].size =
 				ipa_tz_unlock_reg[pos++];
-			IPADBG("tz unlock reg %d: addr 0x%pa size %d\n", i,
+			IPADBG("tz unlock reg %d: addr 0x%pa size %llu\n", i,
 				&ipa_drv_res->ipa_tz_unlock_reg[i].reg_addr,
 				ipa_drv_res->ipa_tz_unlock_reg[i].size);
 		}
 		kfree(ipa_tz_unlock_reg);
 	}
+
+	/* get IPA PM related information */
+	result = get_ipa_dts_pm_info(pdev, ipa_drv_res);
+	if (result) {
+		IPAERR("failed to get pm info from dts %d\n", result);
+		return result;
+	}
+
 	return 0;
 }
 
@@ -5312,7 +5734,10 @@
 	}
 	cb->valid = true;
 
-	if (smmu_info.s1_bypass) {
+	if (of_property_read_bool(dev->of_node, "qcom,smmu-s1-bypass")) {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN] = true;
+		ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN] = true;
+
 		if (iommu_domain_set_attr(cb->iommu,
 					DOMAIN_ATTR_S1_BYPASS,
 					&bypass)) {
@@ -5320,8 +5745,11 @@
 			cb->valid = false;
 			return -EIO;
 		}
-		IPADBG("SMMU S1 BYPASS\n");
+		IPADBG("WLAN SMMU S1 BYPASS\n");
 	} else {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN] = false;
+		ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN] = false;
+
 		if (iommu_domain_set_attr(cb->iommu,
 					DOMAIN_ATTR_ATOMIC,
 					&atomic_ctx)) {
@@ -5329,7 +5757,7 @@
 			cb->valid = false;
 			return -EIO;
 		}
-		IPADBG("SMMU ATTR ATOMIC\n");
+		IPADBG(" WLAN SMMU ATTR ATOMIC\n");
 
 		if (smmu_info.fast_map) {
 			if (iommu_domain_set_attr(cb->iommu,
@@ -5343,6 +5771,9 @@
 		}
 	}
 
+	pr_info("IPA smmu_info.s1_bypass_arr[WLAN]=%d smmu_info.fast_map=%d\n",
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_WLAN], smmu_info.fast_map);
+
 	ret = iommu_attach_device(cb->iommu, dev);
 	if (ret) {
 		IPAERR("could not attach device ret=%d\n", ret);
@@ -5430,20 +5861,27 @@
 	cb->valid = true;
 
 	IPADBG("UC CB PROBE sub pdev=%p set attribute\n", dev);
-	if (smmu_info.s1_bypass) {
+
+	if (of_property_read_bool(dev->of_node, "qcom,smmu-s1-bypass")) {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC] = true;
+		ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC] = true;
+
 		if (iommu_domain_set_attr(cb->mapping->domain,
-				DOMAIN_ATTR_S1_BYPASS,
-				&bypass)) {
+			DOMAIN_ATTR_S1_BYPASS,
+			&bypass)) {
 			IPAERR("couldn't set bypass\n");
 			arm_iommu_release_mapping(cb->mapping);
 			cb->valid = false;
 			return -EIO;
 		}
-		IPADBG("SMMU S1 BYPASS\n");
+		IPADBG("UC SMMU S1 BYPASS\n");
 	} else {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC] = false;
+		ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC] = false;
+
 		if (iommu_domain_set_attr(cb->mapping->domain,
-				DOMAIN_ATTR_ATOMIC,
-				&atomic_ctx)) {
+			DOMAIN_ATTR_ATOMIC,
+			&atomic_ctx)) {
 			IPAERR("couldn't set domain as atomic\n");
 			arm_iommu_release_mapping(cb->mapping);
 			cb->valid = false;
@@ -5453,8 +5891,8 @@
 
 		if (smmu_info.fast_map) {
 			if (iommu_domain_set_attr(cb->mapping->domain,
-					DOMAIN_ATTR_FAST,
-					&fast)) {
+				DOMAIN_ATTR_FAST,
+				&fast)) {
 				IPAERR("couldn't set fast map\n");
 				arm_iommu_release_mapping(cb->mapping);
 				cb->valid = false;
@@ -5464,6 +5902,9 @@
 		}
 	}
 
+	pr_info("IPA smmu_info.s1_bypass_arr[UC]=%d smmu_info.fast_map=%d\n",
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_UC], smmu_info.fast_map);
+
 	IPADBG("UC CB PROBE sub pdev=%p attaching IOMMU device\n", dev);
 	ret = arm_iommu_attach_device(cb->dev, cb->mapping);
 	if (ret) {
@@ -5530,7 +5971,9 @@
 	IPADBG("SMMU mapping created\n");
 	cb->valid = true;
 
-	if (smmu_info.s1_bypass) {
+	if (of_property_read_bool(dev->of_node,
+		"qcom,smmu-s1-bypass")) {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP] = true;
 		if (iommu_domain_set_attr(cb->mapping->domain,
 				DOMAIN_ATTR_S1_BYPASS,
 				&bypass)) {
@@ -5539,8 +5982,9 @@
 			cb->valid = false;
 			return -EIO;
 		}
-		IPADBG("SMMU S1 BYPASS\n");
+		IPADBG("AP/USB SMMU S1 BYPASS\n");
 	} else {
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP] = false;
 		if (iommu_domain_set_attr(cb->mapping->domain,
 				DOMAIN_ATTR_ATOMIC,
 				&atomic_ctx)) {
@@ -5549,7 +5993,7 @@
 			cb->valid = false;
 			return -EIO;
 		}
-		IPADBG("SMMU atomic set\n");
+		IPADBG("AP/USB SMMU atomic set\n");
 
 		if (iommu_domain_set_attr(cb->mapping->domain,
 				DOMAIN_ATTR_FAST,
@@ -5562,6 +6006,9 @@
 		IPADBG("SMMU fast map set\n");
 	}
 
+	pr_info("IPA smmu_info.s1_bypass_arr[AP]=%d smmu_info.fast_map=%d\n",
+		smmu_info.s1_bypass_arr[IPA_SMMU_CB_AP], smmu_info.fast_map);
+
 	result = arm_iommu_attach_device(cb->dev, cb->mapping);
 	if (result) {
 		IPAERR("couldn't attach to IOMMU ret=%d\n", result);
@@ -5745,17 +6192,12 @@
 
 	if (of_property_read_bool(pdev_p->dev.of_node, "qcom,arm-smmu")) {
 		if (of_property_read_bool(pdev_p->dev.of_node,
-		    "qcom,smmu-s1-bypass"))
-			smmu_info.s1_bypass = true;
-		if (of_property_read_bool(pdev_p->dev.of_node,
 			"qcom,smmu-fast-map"))
 			smmu_info.fast_map = true;
 		if (of_property_read_bool(pdev_p->dev.of_node,
 			"qcom,use-64-bit-dma-mask"))
 			smmu_info.use_64_bit_dma_mask = true;
 		smmu_info.arm_smmu = true;
-		pr_info("IPA smmu_info.s1_bypass=%d smmu_info.fast_map=%d\n",
-			smmu_info.s1_bypass, smmu_info.fast_map);
 	} else if (of_property_read_bool(pdev_p->dev.of_node,
 				"qcom,msm-smmu")) {
 		IPAERR("Legacy IOMMU not supported\n");
@@ -5824,11 +6266,16 @@
 		}
 	}
 
-	/*
-	 * Release transport IPA resource without waiting for inactivity timer
-	 */
-	atomic_set(&ipa3_ctx->transport_pm.eot_activity, 0);
-	ipa3_transport_release_resource(NULL);
+	if (ipa3_ctx->use_ipa_pm) {
+		ipa_pm_deactivate_all_deferred();
+	} else {
+		/*
+		 * Release transport IPA resource without waiting
+		 * for inactivity timer
+		 */
+		atomic_set(&ipa3_ctx->transport_pm.eot_activity, 0);
+		ipa3_transport_release_resource(NULL);
+	}
 	IPADBG("Exit\n");
 
 	return 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 1af968e..59fe07f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -488,7 +488,7 @@
 	struct iommu_domain *smmu_domain;
 	int res;
 
-	if (ipa3_ctx->smmu_s1_bypass)
+	if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
 		return 0;
 
 	smmu_domain = ipa3_get_smmu_domain();
@@ -520,7 +520,7 @@
 	struct iommu_domain *smmu_domain;
 	int res;
 
-	if (ipa3_ctx->smmu_s1_bypass)
+	if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_AP])
 		return 0;
 
 	smmu_domain = ipa3_get_smmu_domain();
@@ -743,6 +743,7 @@
 	return 0;
 }
 
+/* This function called as part of usb pipe resume */
 int ipa3_xdci_connect(u32 clnt_hdl)
 {
 	int result;
@@ -782,11 +783,14 @@
 	return result;
 }
 
+
+/* This function called as part of usb pipe connect */
 int ipa3_xdci_start(u32 clnt_hdl, u8 xferrscidx, bool xferrscidx_valid)
 {
 	struct ipa3_ep_context *ep;
 	int result = -EFAULT;
 	enum gsi_status gsi_res;
+	struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
 
 	IPADBG("entry\n");
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes  ||
@@ -808,6 +812,22 @@
 			goto write_chan_scratch_fail;
 		}
 	}
+
+	if (IPA_CLIENT_IS_PROD(ep->client) && ep->skip_ep_cfg) {
+		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_delay = true;
+		ep->ep_delay_set = true;
+
+		result = ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+		if (result)
+			IPAERR("client (ep: %d) failed result=%d\n",
+			clnt_hdl, result);
+		else
+			IPADBG("client (ep: %d) success\n", clnt_hdl);
+	} else {
+		ep->ep_delay_set = false;
+	}
+
 	gsi_res = gsi_start_channel(ep->gsi_chan_hdl);
 	if (gsi_res != GSI_STATUS_SUCCESS) {
 		IPAERR("Error starting channel: %d\n", gsi_res);
@@ -975,6 +995,7 @@
 		msecs_to_jiffies(IPA_CHANNEL_STOP_IN_PROC_TO_MSEC);
 	int res;
 
+	IPADBG("entry\n");
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 		ipa3_ctx->ep[clnt_hdl].valid == 0 ||
 		!stop_in_proc) {
@@ -1012,13 +1033,16 @@
 
 /* Clocks should be voted for before invoking this function */
 static int ipa3_stop_ul_chan_with_data_drain(u32 qmi_req_id,
-		u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl)
+		u32 source_pipe_bitmask, bool should_force_clear, u32 clnt_hdl,
+		bool remove_delay)
 {
 	int result;
 	bool is_empty = false;
 	int i;
 	bool stop_in_proc;
 	struct ipa3_ep_context *ep;
+	struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
+	IPADBG("entry\n");
 
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 		ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1039,6 +1063,22 @@
 	if (!stop_in_proc)
 		goto exit;
 
+	if (remove_delay && ep->ep_delay_set == true) {
+		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_delay = false;
+		result = ipa3_cfg_ep_ctrl(clnt_hdl,
+			&ep_cfg_ctrl);
+		if (result) {
+			IPAERR
+			("client (ep: %d) failed to remove delay result=%d\n",
+				clnt_hdl, result);
+		} else {
+			IPADBG("client (ep: %d) delay removed\n",
+				clnt_hdl);
+			ep->ep_delay_set = false;
+		}
+	}
+
 	/* if stop_in_proc, lets wait for emptiness */
 	for (i = 0; i < IPA_POLL_FOR_EMPTINESS_NUM; i++) {
 		result = ipa3_is_xdci_channel_empty(ep, &is_empty);
@@ -1104,6 +1144,22 @@
 	if (should_force_clear)
 		ipa3_disable_force_clear(qmi_req_id);
 exit:
+	if (remove_delay && ep->ep_delay_set == true) {
+		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
+		ep_cfg_ctrl.ipa_ep_delay = false;
+		result = ipa3_cfg_ep_ctrl(clnt_hdl,
+			&ep_cfg_ctrl);
+		if (result) {
+			IPAERR
+			("client (ep: %d) failed to remove delay result=%d\n",
+				clnt_hdl, result);
+		} else {
+			IPADBG("client (ep: %d) delay removed\n",
+				clnt_hdl);
+			ep->ep_delay_set = false;
+		}
+	}
+	IPADBG("exit\n");
 	return result;
 }
 
@@ -1133,7 +1189,8 @@
 		source_pipe_bitmask = 1 <<
 			ipa3_get_ep_mapping(ep->client);
 		result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id,
-			source_pipe_bitmask, should_force_clear, clnt_hdl);
+			source_pipe_bitmask, should_force_clear, clnt_hdl,
+			true);
 		if (result) {
 			IPAERR("Fail to stop UL channel with data drain\n");
 			WARN_ON(1);
@@ -1318,7 +1375,8 @@
 	if (!is_dpl) {
 		source_pipe_bitmask = 1 << ipa3_get_ep_mapping(ul_ep->client);
 		result = ipa3_stop_ul_chan_with_data_drain(qmi_req_id,
-			source_pipe_bitmask, should_force_clear, ul_clnt_hdl);
+			source_pipe_bitmask, should_force_clear, ul_clnt_hdl,
+			false);
 		if (result) {
 			IPAERR("Error stopping UL channel: result = %d\n",
 				result);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
index 153548b..5da83e5 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c
@@ -17,7 +17,9 @@
 #include <linux/stringify.h>
 #include "ipa_i.h"
 #include "../ipa_rm_i.h"
+#include "ipahal/ipahal_nat.h"
 
+#define IPA_MAX_ENTRY_STRING_LEN 500
 #define IPA_MAX_MSG_LEN 4096
 #define IPA_DBG_MAX_RULE_IN_TBL 128
 #define IPA_DBG_ACTIVE_CLIENT_BUF_SIZE ((IPA3_ACTIVE_CLIENTS_LOG_LINE_LEN \
@@ -26,17 +28,18 @@
 #define IPA_DUMP_STATUS_FIELD(f) \
 	pr_err(#f "=0x%x\n", status->f)
 
-const char *ipa3_excp_name[] = {
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD0),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_RSVD1),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IHL),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_TAG),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_NAT),
-	__stringify_1(IPA_A5_MUX_HDR_EXCP_FLAG_IP),
+#define IPA_READ_ONLY_MODE  0444
+#define IPA_READ_WRITE_MODE 0664
+#define IPA_WRITE_ONLY_MODE 0220
+
+struct ipa3_debugfs_file {
+	const char *name;
+	umode_t mode;
+	void *data;
+	const struct file_operations fops;
 };
 
+
 const char *ipa3_event_name[] = {
 	__stringify(WLAN_CLIENT_CONNECT),
 	__stringify(WLAN_CLIENT_DISCONNECT),
@@ -67,7 +70,9 @@
 	__stringify(ADD_VLAN_IFACE),
 	__stringify(DEL_VLAN_IFACE),
 	__stringify(ADD_L2TP_VLAN_MAPPING),
-	__stringify(DEL_L2TP_VLAN_MAPPING)
+	__stringify(DEL_L2TP_VLAN_MAPPING),
+	__stringify(IPA_PER_CLIENT_STATS_CONNECT_EVENT),
+	__stringify(IPA_PER_CLIENT_STATS_DISCONNECT_EVENT),
 };
 
 const char *ipa3_hdr_l2_type_name[] = {
@@ -87,30 +92,6 @@
 };
 
 static struct dentry *dent;
-static struct dentry *dfile_gen_reg;
-static struct dentry *dfile_ep_reg;
-static struct dentry *dfile_keep_awake;
-static struct dentry *dfile_ep_holb;
-static struct dentry *dfile_hdr;
-static struct dentry *dfile_proc_ctx;
-static struct dentry *dfile_ip4_rt;
-static struct dentry *dfile_ip4_rt_hw;
-static struct dentry *dfile_ip6_rt;
-static struct dentry *dfile_ip6_rt_hw;
-static struct dentry *dfile_ip4_flt;
-static struct dentry *dfile_ip4_flt_hw;
-static struct dentry *dfile_ip6_flt;
-static struct dentry *dfile_ip6_flt_hw;
-static struct dentry *dfile_stats;
-static struct dentry *dfile_wstats;
-static struct dentry *dfile_wdi_stats;
-static struct dentry *dfile_ntn_stats;
-static struct dentry *dfile_dbg_cnt;
-static struct dentry *dfile_msg;
-static struct dentry *dfile_ip4_nat;
-static struct dentry *dfile_rm_stats;
-static struct dentry *dfile_status_stats;
-static struct dentry *dfile_active_clients;
 static char dbg_buff[IPA_MAX_MSG_LEN];
 static char *active_clients_buf;
 
@@ -910,10 +891,11 @@
 				eq = true;
 			} else {
 				rt_tbl = ipa3_id_find(entry->rule.rt_tbl_hdl);
-				if (rt_tbl)
-					rt_tbl_idx = rt_tbl->idx;
+				if (rt_tbl == NULL ||
+					rt_tbl->cookie != IPA_RT_TBL_COOKIE)
+					rt_tbl_idx =  ~0;
 				else
-					rt_tbl_idx = ~0;
+					rt_tbl_idx = rt_tbl->idx;
 				bitmap = entry->rule.attrib.attrib_mask;
 				eq = false;
 			}
@@ -1515,204 +1497,364 @@
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
+static int ipa3_read_table(
+	char *table_addr, u32 table_size,
+	char *buff, u32 buff_size,
+	u32 *total_num_entries,
+	u32 *rule_id,
+	enum ipahal_nat_type nat_type)
+{
+	int result;
+	char *entry;
+	size_t entry_size;
+	bool entry_zeroed;
+	u32 i, num_entries = 0, id = *rule_id, pos = 0;
+
+	IPADBG("\n");
+
+	if (table_addr == NULL)
+		return 0;
+
+	result = ipahal_nat_entry_size(nat_type, &entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of %s entry\n",
+			ipahal_nat_type_str(nat_type));
+		return 0;
+	}
+
+	for (i = 0, entry = table_addr;
+		i < table_size;
+		++i, ++id, entry += entry_size) {
+		result = ipahal_nat_is_entry_zeroed(nat_type, entry,
+			&entry_zeroed);
+		if (result) {
+			IPAERR(
+				"Failed to determine whether the %s entry is definitely zero",
+				ipahal_nat_type_str(nat_type));
+			goto bail;
+		}
+		if (entry_zeroed)
+			continue;
+
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"\tEntry_Index=%d\n", id);
+
+		pos += ipahal_nat_stringify_entry(nat_type, entry,
+			buff + pos, buff_size - pos);
+
+		++num_entries;
+	}
+
+	if (num_entries)
+		pos += scnprintf(buff + pos, buff_size - pos, "\n");
+	else
+		pos += scnprintf(buff + pos, buff_size - pos, "\tEmpty\n\n");
+
+	IPADBG("return\n");
+bail:
+	*rule_id = id;
+	*total_num_entries += num_entries;
+	return pos;
+}
+
+static int ipa3_start_read_memory_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	char *buff, u32 buff_size,
+	enum ipahal_nat_type nat_type,
+	u32 *num_entries)
+{
+	u32 rule_id = 0, pos = 0;
+
+	IPADBG("\n");
+
+	pos += scnprintf(buff + pos, buff_size - pos, "%s_Table_Size=%d\n",
+		dev->name, dev->table_entries + 1);
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"%s_Expansion_Table_Size=%d\n",
+		dev->name, dev->expn_table_entries);
+
+	if (!dev->is_sys_mem)
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"Not supported for local(shared) memory\n");
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"\n%s Base Table:\n", dev->name);
+	pos += ipa3_read_table(dev->base_table_addr, dev->table_entries + 1,
+		buff + pos, buff_size - pos, num_entries, &rule_id, nat_type);
+
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"%s Expansion Table:\n", dev->name);
+	pos += ipa3_read_table(
+		dev->expansion_table_addr, dev->expn_table_entries,
+		buff + pos, buff_size - pos,
+		num_entries,
+		&rule_id,
+		nat_type);
+
+	IPADBG("return\n");
+	return pos;
+}
+
+static int ipa3_finish_read_memory_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	char *buff, u32 buff_size,
+	u32 curr_pos,
+	u32 num_entries)
+{
+	u32 pos = 0;
+
+	IPADBG("\n");
+
+	/*
+	 * A real buffer and buff size, so need to use the
+	 * real current position
+	 */
+	pos += scnprintf(buff + curr_pos, buff_size - curr_pos,
+		"Overall number %s entries: %d\n\n", dev->name, num_entries);
+
+	if (curr_pos + pos >= buff_size - 1)
+		IPAERR(
+			"The %s debug information is larger than the internal buffer, so the read information might be incomplete",
+			dev->name);
+
+	IPADBG("return\n");
+	return pos;
+}
+
+static int ipa3_read_pdn_table(char *buff, u32 buff_size)
+{
+	int i, result;
+	char *pdn_entry;
+	size_t pdn_entry_size;
+	bool entry_zeroed;
+	u32 pos = 0;
+
+	IPADBG("\n");
+
+	result = ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of PDN entry");
+		return 0;
+	}
+
+	for (i = 0, pdn_entry = ipa3_ctx->nat_mem.pdn_mem.base;
+		i < IPA_MAX_PDN_NUM;
+		++i, pdn_entry += pdn_entry_size) {
+		result = ipahal_nat_is_entry_zeroed(IPAHAL_NAT_IPV4_PDN,
+			pdn_entry, &entry_zeroed);
+		if (result) {
+			IPAERR(
+				"Failed to determine whether the PDN entry is definitely zero");
+			goto bail;
+		}
+		if (entry_zeroed)
+			continue;
+
+		pos += scnprintf(buff + pos, buff_size - pos, "PDN %d: ", i);
+
+		pos += ipahal_nat_stringify_entry(IPAHAL_NAT_IPV4_PDN,
+			pdn_entry, buff + pos, buff_size - pos);
+	}
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	IPADBG("return\n");
+bail:
+	return pos;
+}
+
 static ssize_t ipa3_read_nat4(struct file *file,
 		char __user *ubuf, size_t count,
-		loff_t *ppos) {
+		loff_t *ppos)
+{
+	ssize_t ret;
+	char *buff;
+	u32 rule_id = 0, pos = 0, num_entries = 0, index_num_entries = 0;
+	const u32 buff_size = IPA_MAX_MSG_LEN + 2 * IPA_MAX_ENTRY_STRING_LEN * (
+		ipa3_ctx->nat_mem.dev.table_entries + 1 +
+		ipa3_ctx->nat_mem.dev.expn_table_entries);
 
-#define ENTRY_U32_FIELDS 8
-#define NAT_ENTRY_ENABLE 0x8000
-#define NAT_ENTRY_RST_FIN_BIT 0x4000
-#define BASE_TABLE 0
-#define EXPANSION_TABLE 1
+	IPADBG("\n");
 
-	u32 *base_tbl, *indx_tbl;
-	u32 tbl_size, *tmp;
-	u32 value, i, j, rule_id;
-	u16 enable, tbl_entry, flag;
-	u32 no_entrys = 0;
-	struct ipa_pdn_entry *pdn_table = ipa3_ctx->nat_mem.pdn_mem.base;
+	buff = kzalloc(buff_size, GFP_KERNEL);
+	if (buff == NULL)
+		return 0;
 
-	mutex_lock(&ipa3_ctx->nat_mem.lock);
-	value = ipa3_ctx->nat_mem.public_ip_addr;
-	pr_err(
-				"Table IP Address:%d.%d.%d.%d\n",
-				((value & 0xFF000000) >> 24),
-				((value & 0x00FF0000) >> 16),
-				((value & 0x0000FF00) >> 8),
-				((value & 0x000000FF)));
-
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
-		for (i = 0; i < IPA_MAX_PDN_NUM; i++) {
-			pr_err(
-				"PDN %d: ip 0x%X, src_metadata 0x%X, dst_metadata 0x%X\n",
-				i, pdn_table[i].public_ip,
-				pdn_table[i].src_metadata,
-				pdn_table[i].dst_metadata);
-		}
-
-	pr_err("Table Size:%d\n",
-				ipa3_ctx->nat_mem.size_base_tables);
-
-	pr_err("Expansion Table Size:%d\n",
-				ipa3_ctx->nat_mem.size_expansion_tables-1);
-
-	if (!ipa3_ctx->nat_mem.is_sys_mem)
-		pr_err("Not supported for local(shared) memory\n");
-
-	/* Print Base tables */
-	rule_id = 0;
-	for (j = 0; j < 2; j++) {
-		if (j == BASE_TABLE) {
-			tbl_size = ipa3_ctx->nat_mem.size_base_tables;
-			base_tbl = (u32 *)ipa3_ctx->nat_mem.ipv4_rules_addr;
-
-			pr_err("\nBase Table:\n");
-		} else {
-			tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
-			base_tbl =
-			 (u32 *)ipa3_ctx->nat_mem.ipv4_expansion_rules_addr;
-
-			pr_err("\nExpansion Base Table:\n");
-		}
-
-		if (base_tbl != NULL) {
-			for (i = 0; i <= tbl_size; i++, rule_id++) {
-				tmp = base_tbl;
-				value = tmp[4];
-				enable = ((value & 0xFFFF0000) >> 16);
-
-				if (enable & NAT_ENTRY_ENABLE) {
-					no_entrys++;
-					pr_err("Rule:%d ", rule_id);
-
-					value = *tmp;
-					pr_err(
-						"Private_IP:%d.%d.%d.%d ",
-						((value & 0xFF000000) >> 24),
-						((value & 0x00FF0000) >> 16),
-						((value & 0x0000FF00) >> 8),
-						((value & 0x000000FF)));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Target_IP:%d.%d.%d.%d ",
-						((value & 0xFF000000) >> 24),
-						((value & 0x00FF0000) >> 16),
-						((value & 0x0000FF00) >> 8),
-						((value & 0x000000FF)));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Next_Index:%d  Public_Port:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Private_Port:%d  Target_Port:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					flag = ((value & 0xFFFF0000) >> 16);
-					if (flag & NAT_ENTRY_RST_FIN_BIT) {
-						pr_err(
-								"IP_CKSM_delta:0x%x  Flags:%s ",
-							  (value & 0x0000FFFF),
-								"Direct_To_A5");
-					} else {
-						pr_err(
-							"IP_CKSM_delta:0x%x  Flags:%s ",
-							(value & 0x0000FFFF),
-							"Fwd_to_route");
-					}
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Time_stamp:0x%x Proto:%d ",
-						(value & 0x00FFFFFF),
-						((value & 0xFF000000) >> 24));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"Prev_Index:%d  Indx_tbl_entry:%d ",
-						(value & 0x0000FFFF),
-						((value & 0xFFFF0000) >> 16));
-					tmp++;
-
-					value = *tmp;
-					pr_err(
-						"TCP_UDP_cksum_delta:0x%x\n",
-						((value & 0xFFFF0000) >> 16));
-				}
-
-				base_tbl += ENTRY_U32_FIELDS;
-
-			}
-		}
+	if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT hasn't been initialized or not supported\n");
+		goto ret;
 	}
 
+	mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
+
+	if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT H/W hasn't been initialized\n");
+		goto bail;
+	}
+
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		pos += ipa3_read_pdn_table(buff + pos, buff_size - pos);
+	} else {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"NAT Table IP Address=%pI4h\n\n",
+			&ipa3_ctx->nat_mem.public_ip_addr);
+	}
+
+	pos += ipa3_start_read_memory_device(&ipa3_ctx->nat_mem.dev,
+		buff + pos, buff_size - pos, IPAHAL_NAT_IPV4, &num_entries);
+
 	/* Print Index tables */
-	rule_id = 0;
-	for (j = 0; j < 2; j++) {
-		if (j == BASE_TABLE) {
-			tbl_size = ipa3_ctx->nat_mem.size_base_tables;
-			indx_tbl = (u32 *)ipa3_ctx->nat_mem.index_table_addr;
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"ipaNatTable Index Table:\n");
+	pos += ipa3_read_table(
+		ipa3_ctx->nat_mem.index_table_addr,
+		ipa3_ctx->nat_mem.dev.table_entries + 1,
+		buff + pos, buff_size - pos,
+		&index_num_entries,
+		&rule_id,
+		IPAHAL_NAT_IPV4_INDEX);
 
-			pr_err("\nIndex Table:\n");
-		} else {
-			tbl_size = ipa3_ctx->nat_mem.size_expansion_tables-1;
-			indx_tbl =
-			 (u32 *)ipa3_ctx->nat_mem.index_table_expansion_addr;
+	pos += scnprintf(buff + pos, buff_size - pos,
+		"ipaNatTable Expansion Index Table:\n");
+	pos += ipa3_read_table(
+		ipa3_ctx->nat_mem.index_table_expansion_addr,
+		ipa3_ctx->nat_mem.dev.expn_table_entries,
+		buff + pos, buff_size - pos,
+		&index_num_entries,
+		&rule_id,
+		IPAHAL_NAT_IPV4_INDEX);
 
-			pr_err("\nExpansion Index Table:\n");
-		}
+	if (num_entries != index_num_entries)
+		IPAERR(
+			"The NAT table number of entries %d is different from index table number of entries %d\n",
+			num_entries, index_num_entries);
 
-		if (indx_tbl != NULL) {
-			for (i = 0; i <= tbl_size; i++, rule_id++) {
-				tmp = indx_tbl;
-				value = *tmp;
-				tbl_entry = (value & 0x0000FFFF);
+	pos += ipa3_finish_read_memory_device(&ipa3_ctx->nat_mem.dev,
+		buff, buff_size, pos, num_entries);
 
-				if (tbl_entry) {
-					pr_err("Rule:%d ", rule_id);
+	IPADBG("return\n");
+bail:
+	mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
+ret:
+	ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+	kfree(buff);
+	return ret;
+}
 
-					value = *tmp;
-					pr_err(
-						"Table_Entry:%d  Next_Index:%d\n",
-						tbl_entry,
-						((value & 0xFFFF0000) >> 16));
-				}
+static ssize_t ipa3_read_ipv6ct(struct file *file,
+	char __user *ubuf, size_t count,
+	loff_t *ppos) {
+	ssize_t ret;
+	char *buff;
+	u32 pos = 0, num_entries = 0;
+	const u32 buff_size = IPA_MAX_MSG_LEN + IPA_MAX_ENTRY_STRING_LEN * (
+		ipa3_ctx->nat_mem.dev.table_entries + 1 +
+		ipa3_ctx->nat_mem.dev.expn_table_entries);
 
-				indx_tbl++;
-			}
-		}
+	IPADBG("\n");
+
+	buff = kzalloc(buff_size, GFP_KERNEL);
+	if (buff == NULL)
+		return 0;
+
+	pos += scnprintf(buff + pos, buff_size - pos, "\n");
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"IPv6 connection tracking hasn't been initialized or not supported\n");
+		goto ret;
 	}
-	pr_err("Current No. Nat Entries: %d\n", no_entrys);
-	mutex_unlock(&ipa3_ctx->nat_mem.lock);
 
-	return 0;
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+		pos += scnprintf(buff + pos, buff_size - pos,
+			"IPv6 connection tracking H/W hasn't been initialized\n");
+		goto bail;
+	}
+
+	pos += ipa3_start_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+		buff + pos, buff_size - pos, IPAHAL_NAT_IPV6CT, &num_entries);
+	pos += ipa3_finish_read_memory_device(&ipa3_ctx->ipv6ct_mem.dev,
+		buff, buff_size, pos, num_entries);
+
+	IPADBG("return\n");
+bail:
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+ret:
+	ret = simple_read_from_buffer(ubuf, count, ppos, buff, pos);
+	kfree(buff);
+	return ret;
 }
 
 static ssize_t ipa3_rm_read_stats(struct file *file, char __user *ubuf,
 		size_t count, loff_t *ppos)
 {
-	int result, nbytes, cnt = 0;
+	int result, cnt = 0;
+
+	/* deprecate if IPA PM is used */
+	if (ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA RM is disabled\n");
+		goto ret;
+	}
 
 	result = ipa_rm_stat(dbg_buff, IPA_MAX_MSG_LEN);
 	if (result < 0) {
-		nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
 				"Error in printing RM stat %d\n", result);
-		cnt += nbytes;
-	} else
-		cnt += result;
+		goto ret;
+	}
+	cnt += result;
+ret:
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
 
+static ssize_t ipa3_pm_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int result, cnt = 0;
+
+	if (!ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA PM is disabled\n");
+		goto ret;
+	}
+
+	result = ipa_pm_stat(dbg_buff, IPA_MAX_MSG_LEN);
+	if (result < 0) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+				"Error in printing PM stat %d\n", result);
+		goto ret;
+	}
+	cnt += result;
+ret:
+	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
+}
+
+static ssize_t ipa3_pm_ex_read_stats(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int result, cnt = 0;
+
+	if (!ipa3_ctx->use_ipa_pm) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+			"IPA PM is disabled\n");
+		goto ret;
+	}
+
+	result = ipa_pm_exceptions_stat(dbg_buff, IPA_MAX_MSG_LEN);
+	if (result < 0) {
+		cnt += scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
+				"Error in printing PM stat %d\n", result);
+		goto ret;
+	}
+	cnt += result;
+ret:
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
 }
 
@@ -1857,104 +1999,139 @@
 	return count;
 }
 
-const struct file_operations ipa3_gen_reg_ops = {
-	.read = ipa3_read_gen_reg,
-};
-
-const struct file_operations ipa3_ep_reg_ops = {
-	.read = ipa3_read_ep_reg,
-	.write = ipa3_write_ep_reg,
-};
-
-const struct file_operations ipa3_keep_awake_ops = {
-	.read = ipa3_read_keep_awake,
-	.write = ipa3_write_keep_awake,
-};
-
-const struct file_operations ipa3_ep_holb_ops = {
-	.write = ipa3_write_ep_holb,
-};
-
-const struct file_operations ipa3_hdr_ops = {
-	.read = ipa3_read_hdr,
-};
-
-const struct file_operations ipa3_rt_ops = {
-	.read = ipa3_read_rt,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_rt_hw_ops = {
-	.read = ipa3_read_rt_hw,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_proc_ctx_ops = {
-	.read = ipa3_read_proc_ctx,
-};
-
-const struct file_operations ipa3_flt_ops = {
-	.read = ipa3_read_flt,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_flt_hw_ops = {
-	.read = ipa3_read_flt_hw,
-	.open = ipa3_open_dbg,
-};
-
-const struct file_operations ipa3_stats_ops = {
-	.read = ipa3_read_stats,
-};
-
-const struct file_operations ipa3_wstats_ops = {
-	.read = ipa3_read_wstats,
-};
-
-const struct file_operations ipa3_wdi_ops = {
-	.read = ipa3_read_wdi,
-};
-
-const struct file_operations ipa3_ntn_ops = {
-	.read = ipa3_read_ntn,
-};
-
-const struct file_operations ipa3_msg_ops = {
-	.read = ipa3_read_msg,
-};
-
-const struct file_operations ipa3_dbg_cnt_ops = {
-	.read = ipa3_read_dbg_cnt,
-	.write = ipa3_write_dbg_cnt,
-};
-
-const struct file_operations ipa3_status_stats_ops = {
-	.read = ipa_status_stats_read,
-};
-
-const struct file_operations ipa3_nat4_ops = {
-	.read = ipa3_read_nat4,
-};
-
-const struct file_operations ipa3_rm_stats = {
-	.read = ipa3_rm_read_stats,
-};
-
-const struct file_operations ipa3_active_clients = {
-	.read = ipa3_print_active_clients_log,
-	.write = ipa3_clear_active_clients_log,
-};
-
-const struct file_operations ipa3_ipc_low_ops = {
-	.write = ipa3_enable_ipc_low,
+static const struct ipa3_debugfs_file debugfs_files[] = {
+	{
+		"gen_reg", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_gen_reg
+		}
+	}, {
+		"active_clients", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_print_active_clients_log,
+			.write = ipa3_clear_active_clients_log
+		}
+	}, {
+		"ep_reg", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_ep_reg,
+			.write = ipa3_write_ep_reg,
+		}
+	}, {
+		"keep_awake", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_keep_awake,
+			.write = ipa3_write_keep_awake,
+		}
+	}, {
+		"holb", IPA_WRITE_ONLY_MODE, NULL, {
+			.write = ipa3_write_ep_holb,
+		}
+	}, {
+		"hdr", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_hdr,
+		}
+	}, {
+		"proc_ctx", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_proc_ctx,
+		}
+	}, {
+		"ip4_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_rt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_rt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_rt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_rt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_rt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_rt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_flt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip4_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v4, {
+			.read = ipa3_read_flt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_flt", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_flt,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"ip6_flt_hw", IPA_READ_ONLY_MODE, (void *)IPA_IP_v6, {
+			.read = ipa3_read_flt_hw,
+			.open = ipa3_open_dbg,
+		}
+	}, {
+		"stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_stats,
+		}
+	}, {
+		"wstats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_wstats,
+		}
+	}, {
+		"wdi", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_wdi,
+		}
+	}, {
+		"ntn", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_ntn,
+		}
+	}, {
+		"dbg_cnt", IPA_READ_WRITE_MODE, NULL, {
+			.read = ipa3_read_dbg_cnt,
+			.write = ipa3_write_dbg_cnt,
+		}
+	}, {
+		"msg", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_msg,
+		}
+	}, {
+		"ip4_nat", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_nat4,
+		}
+	}, {
+		"ipv6ct", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_read_ipv6ct,
+		}
+	}, {
+		"rm_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_rm_read_stats,
+		}
+	}, {
+		"pm_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_pm_read_stats,
+		}
+	}, {
+		"pm_ex_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa3_pm_ex_read_stats,
+		}
+	}, {
+		"status_stats", IPA_READ_ONLY_MODE, NULL, {
+			.read = ipa_status_stats_read,
+		}
+	}, {
+		"enable_low_prio_print", IPA_WRITE_ONLY_MODE, NULL, {
+			.write = ipa3_enable_ipc_low,
+		}
+	}
 };
 
 void ipa3_debugfs_init(void)
 {
-	const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
-	const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
-			S_IWUSR | S_IWGRP;
-	const mode_t write_only_mode = S_IWUSR | S_IWGRP;
+	const size_t debugfs_files_num =
+		sizeof(debugfs_files) / sizeof(struct ipa3_debugfs_file);
+	size_t i;
 	struct dentry *file;
 
 	dent = debugfs_create_dir("ipa", 0);
@@ -1963,26 +2140,24 @@
 		return;
 	}
 
-	file = debugfs_create_u32("hw_type", read_only_mode,
-			dent, &ipa3_ctx->ipa_hw_type);
+	file = debugfs_create_u32("hw_type", IPA_READ_ONLY_MODE,
+		dent, &ipa3_ctx->ipa_hw_type);
 	if (!file) {
 		IPAERR("could not create hw_type file\n");
 		goto fail;
 	}
 
 
-	dfile_gen_reg = debugfs_create_file("gen_reg", read_only_mode, dent, 0,
-			&ipa3_gen_reg_ops);
-	if (!dfile_gen_reg || IS_ERR(dfile_gen_reg)) {
-		IPAERR("fail to create file for debug_fs gen_reg\n");
-		goto fail;
-	}
+	for (i = 0; i < debugfs_files_num; ++i) {
+		const struct ipa3_debugfs_file *curr = &debugfs_files[i];
 
-	dfile_active_clients = debugfs_create_file("active_clients",
-			read_write_mode, dent, 0, &ipa3_active_clients);
-	if (!dfile_active_clients || IS_ERR(dfile_active_clients)) {
-		IPAERR("fail to create file for debug_fs active_clients\n");
-		goto fail;
+		file = debugfs_create_file(curr->name, curr->mode, dent,
+			curr->data, &curr->fops);
+		if (!file || IS_ERR(file)) {
+			IPAERR("fail to create file for debug_fs %s\n",
+				curr->name);
+			goto fail;
+		}
 	}
 
 	active_clients_buf = NULL;
@@ -1991,161 +2166,7 @@
 	if (active_clients_buf == NULL)
 		IPAERR("fail to allocate active clients memory buffer");
 
-	dfile_ep_reg = debugfs_create_file("ep_reg", read_write_mode, dent, 0,
-			&ipa3_ep_reg_ops);
-	if (!dfile_ep_reg || IS_ERR(dfile_ep_reg)) {
-		IPAERR("fail to create file for debug_fs ep_reg\n");
-		goto fail;
-	}
-
-	dfile_keep_awake = debugfs_create_file("keep_awake", read_write_mode,
-			dent, 0, &ipa3_keep_awake_ops);
-	if (!dfile_keep_awake || IS_ERR(dfile_keep_awake)) {
-		IPAERR("fail to create file for debug_fs dfile_keep_awake\n");
-		goto fail;
-	}
-
-	dfile_ep_holb = debugfs_create_file("holb", write_only_mode, dent,
-			0, &ipa3_ep_holb_ops);
-	if (!dfile_ep_holb || IS_ERR(dfile_ep_holb)) {
-		IPAERR("fail to create file for debug_fs dfile_ep_hol_en\n");
-		goto fail;
-	}
-
-	dfile_hdr = debugfs_create_file("hdr", read_only_mode, dent, 0,
-			&ipa3_hdr_ops);
-	if (!dfile_hdr || IS_ERR(dfile_hdr)) {
-		IPAERR("fail to create file for debug_fs hdr\n");
-		goto fail;
-	}
-
-	dfile_proc_ctx = debugfs_create_file("proc_ctx", read_only_mode, dent,
-		0, &ipa3_proc_ctx_ops);
-	if (!dfile_hdr || IS_ERR(dfile_hdr)) {
-		IPAERR("fail to create file for debug_fs proc_ctx\n");
-		goto fail;
-	}
-
-	dfile_ip4_rt = debugfs_create_file("ip4_rt", read_only_mode, dent,
-			(void *)IPA_IP_v4, &ipa3_rt_ops);
-	if (!dfile_ip4_rt || IS_ERR(dfile_ip4_rt)) {
-		IPAERR("fail to create file for debug_fs ip4 rt\n");
-		goto fail;
-	}
-
-	dfile_ip4_rt_hw = debugfs_create_file("ip4_rt_hw", read_only_mode, dent,
-		(void *)IPA_IP_v4, &ipa3_rt_hw_ops);
-	if (!dfile_ip4_rt_hw || IS_ERR(dfile_ip4_rt_hw)) {
-		IPAERR("fail to create file for debug_fs ip4 rt hw\n");
-		goto fail;
-	}
-
-	dfile_ip6_rt = debugfs_create_file("ip6_rt", read_only_mode, dent,
-			(void *)IPA_IP_v6, &ipa3_rt_ops);
-	if (!dfile_ip6_rt || IS_ERR(dfile_ip6_rt)) {
-		IPAERR("fail to create file for debug_fs ip6:w rt\n");
-		goto fail;
-	}
-
-	dfile_ip6_rt_hw = debugfs_create_file("ip6_rt_hw", read_only_mode, dent,
-		(void *)IPA_IP_v6, &ipa3_rt_hw_ops);
-	if (!dfile_ip6_rt_hw || IS_ERR(dfile_ip6_rt_hw)) {
-		IPAERR("fail to create file for debug_fs ip6 rt hw\n");
-		goto fail;
-	}
-
-	dfile_ip4_flt = debugfs_create_file("ip4_flt", read_only_mode, dent,
-			(void *)IPA_IP_v4, &ipa3_flt_ops);
-	if (!dfile_ip4_flt || IS_ERR(dfile_ip4_flt)) {
-		IPAERR("fail to create file for debug_fs ip4 flt\n");
-		goto fail;
-	}
-
-	dfile_ip4_flt_hw = debugfs_create_file("ip4_flt_hw", read_only_mode,
-			dent, (void *)IPA_IP_v4, &ipa3_flt_hw_ops);
-	if (!dfile_ip4_flt_hw || IS_ERR(dfile_ip4_flt_hw)) {
-		IPAERR("fail to create file for debug_fs ip4 flt\n");
-		goto fail;
-	}
-
-	dfile_ip6_flt = debugfs_create_file("ip6_flt", read_only_mode, dent,
-			(void *)IPA_IP_v6, &ipa3_flt_ops);
-	if (!dfile_ip6_flt || IS_ERR(dfile_ip6_flt)) {
-		IPAERR("fail to create file for debug_fs ip6 flt\n");
-		goto fail;
-	}
-
-	dfile_ip6_flt_hw = debugfs_create_file("ip6_flt_hw", read_only_mode,
-			dent, (void *)IPA_IP_v6, &ipa3_flt_hw_ops);
-	if (!dfile_ip6_flt_hw || IS_ERR(dfile_ip6_flt_hw)) {
-		IPAERR("fail to create file for debug_fs ip6 flt\n");
-		goto fail;
-	}
-
-	dfile_stats = debugfs_create_file("stats", read_only_mode, dent, 0,
-			&ipa3_stats_ops);
-	if (!dfile_stats || IS_ERR(dfile_stats)) {
-		IPAERR("fail to create file for debug_fs stats\n");
-		goto fail;
-	}
-
-	dfile_wstats = debugfs_create_file("wstats", read_only_mode,
-			dent, 0, &ipa3_wstats_ops);
-	if (!dfile_wstats || IS_ERR(dfile_wstats)) {
-		IPAERR("fail to create file for debug_fs wstats\n");
-		goto fail;
-	}
-
-	dfile_wdi_stats = debugfs_create_file("wdi", read_only_mode, dent, 0,
-			&ipa3_wdi_ops);
-	if (!dfile_wdi_stats || IS_ERR(dfile_wdi_stats)) {
-		IPAERR("fail to create file for debug_fs wdi stats\n");
-		goto fail;
-	}
-
-	dfile_ntn_stats = debugfs_create_file("ntn", read_only_mode, dent, 0,
-			&ipa3_ntn_ops);
-	if (!dfile_ntn_stats || IS_ERR(dfile_ntn_stats)) {
-		IPAERR("fail to create file for debug_fs ntn stats\n");
-		goto fail;
-	}
-
-	dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
-			&ipa3_dbg_cnt_ops);
-	if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {
-		IPAERR("fail to create file for debug_fs dbg_cnt\n");
-		goto fail;
-	}
-
-	dfile_msg = debugfs_create_file("msg", read_only_mode, dent, 0,
-			&ipa3_msg_ops);
-	if (!dfile_msg || IS_ERR(dfile_msg)) {
-		IPAERR("fail to create file for debug_fs msg\n");
-		goto fail;
-	}
-
-	dfile_ip4_nat = debugfs_create_file("ip4_nat", read_only_mode, dent,
-			0, &ipa3_nat4_ops);
-	if (!dfile_ip4_nat || IS_ERR(dfile_ip4_nat)) {
-		IPAERR("fail to create file for debug_fs ip4 nat\n");
-		goto fail;
-	}
-
-	dfile_rm_stats = debugfs_create_file("rm_stats",
-			read_only_mode, dent, 0, &ipa3_rm_stats);
-	if (!dfile_rm_stats || IS_ERR(dfile_rm_stats)) {
-		IPAERR("fail to create file for debug_fs rm_stats\n");
-		goto fail;
-	}
-
-	dfile_status_stats = debugfs_create_file("status_stats",
-			read_only_mode, dent, 0, &ipa3_status_stats_ops);
-	if (!dfile_status_stats || IS_ERR(dfile_status_stats)) {
-		IPAERR("fail to create file for debug_fs status_stats\n");
-		goto fail;
-	}
-
-	file = debugfs_create_u32("enable_clock_scaling", read_write_mode,
+	file = debugfs_create_u32("enable_clock_scaling", IPA_READ_WRITE_MODE,
 		dent, &ipa3_ctx->enable_clock_scaling);
 	if (!file) {
 		IPAERR("could not create enable_clock_scaling file\n");
@@ -2153,7 +2174,7 @@
 	}
 
 	file = debugfs_create_u32("clock_scaling_bw_threshold_nominal_mbps",
-		read_write_mode, dent,
+		IPA_READ_WRITE_MODE, dent,
 		&ipa3_ctx->ctrl->clock_scaling_bw_threshold_nominal);
 	if (!file) {
 		IPAERR("could not create bw_threshold_nominal_mbps\n");
@@ -2161,20 +2182,13 @@
 	}
 
 	file = debugfs_create_u32("clock_scaling_bw_threshold_turbo_mbps",
-		read_write_mode, dent,
-		&ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
+			IPA_READ_WRITE_MODE, dent,
+			&ipa3_ctx->ctrl->clock_scaling_bw_threshold_turbo);
 	if (!file) {
 		IPAERR("could not create bw_threshold_turbo_mbps\n");
 		goto fail;
 	}
 
-	file = debugfs_create_file("enable_low_prio_print", write_only_mode,
-		dent, 0, &ipa3_ipc_low_ops);
-	if (!file) {
-		IPAERR("could not create enable_low_prio_print file\n");
-		goto fail;
-	}
-
 	ipa_debugfs_init_stats(dent);
 
 	return;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
index 5a29cbc..1a612dc 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dma.c
@@ -77,7 +77,7 @@
 
 /**
  * struct ipa3_dma_ctx -IPADMA driver context information
- * @is_enabled:is ipa_dma enabled?
+ * @enable_ref_cnt: ipa dma enable reference count
  * @destroy_pending: destroy ipa_dma after handling all pending memcpy
  * @ipa_dma_xfer_wrapper_cache: cache of ipa3_dma_xfer_wrapper structs
  * @sync_lock: lock for synchronisation in sync_memcpy
@@ -96,7 +96,7 @@
  * @total_uc_memcpy: total number of uc memcpy (statistics)
  */
 struct ipa3_dma_ctx {
-	bool is_enabled;
+	unsigned int enable_ref_cnt;
 	bool destroy_pending;
 	struct kmem_cache *ipa_dma_xfer_wrapper_cache;
 	struct mutex sync_lock;
@@ -121,6 +121,70 @@
 };
 static struct ipa3_dma_ctx *ipa3_dma_ctx;
 
+/**
+ * struct ipa3_dma_init_refcnt_ctrl -IPADMA driver init control information
+ * @ref_cnt: reference count for initialization operations
+ * @lock: lock for the reference count
+ */
+struct ipa3_dma_init_refcnt_ctrl {
+	unsigned int ref_cnt;
+	struct mutex lock;
+};
+static struct ipa3_dma_init_refcnt_ctrl *ipa3_dma_init_refcnt_ctrl;
+
+/**
+ * ipa3_dma_setup() - One time setup for IPA DMA
+ *
+ * This function should be called once to setup ipa dma
+ *  by creating the init reference count controller
+ *
+ * Return codes: 0: success
+ *		 Negative value: failure
+ */
+int ipa3_dma_setup(void)
+{
+	IPADMA_FUNC_ENTRY();
+
+	if (ipa3_dma_init_refcnt_ctrl) {
+		IPADMA_ERR("Setup already done\n");
+		return -EFAULT;
+	}
+
+	ipa3_dma_init_refcnt_ctrl =
+		kzalloc(sizeof(*(ipa3_dma_init_refcnt_ctrl)), GFP_KERNEL);
+
+	if (!ipa3_dma_init_refcnt_ctrl) {
+		IPADMA_ERR("kzalloc error.\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&ipa3_dma_init_refcnt_ctrl->lock);
+
+	IPADMA_FUNC_EXIT();
+	return 0;
+}
+
+/**
+ * ipa3_dma_shutdown() - Clear setup operations.
+ *
+ * Cleanup for the setup function.
+ * Should be called during IPA driver unloading.
+ * It assumes all ipa_dma operations are done and ipa_dma is destroyed.
+ *
+ * Return codes: None.
+ */
+void ipa3_dma_shutdown(void)
+{
+	IPADMA_FUNC_ENTRY();
+
+	if (!ipa3_dma_init_refcnt_ctrl)
+		return;
+
+	kfree(ipa3_dma_init_refcnt_ctrl);
+	ipa3_dma_init_refcnt_ctrl = NULL;
+
+	IPADMA_FUNC_EXIT();
+}
 
 /**
  * ipa3_dma_init() -Initialize IPADMA.
@@ -129,8 +193,10 @@
  *	MEMCPY_DMA_SYNC_PROD ->MEMCPY_DMA_SYNC_CONS
  *	MEMCPY_DMA_ASYNC_PROD->MEMCPY_DMA_SYNC_CONS
  *
+ * Can be executed several times (re-entrant)
+ *
  * Return codes: 0: success
- *		-EFAULT: IPADMA is already initialized
+ *		-EFAULT: Mismatch between context existence and init ref_cnt
  *		-EINVAL: IPA driver is not initialized
  *		-ENOMEM: allocating memory error
  *		-EPERM: pipe connection failed
@@ -145,21 +211,43 @@
 
 	IPADMA_FUNC_ENTRY();
 
+	if (!ipa3_dma_init_refcnt_ctrl) {
+		IPADMA_ERR("Setup isn't done yet!\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
+	if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 0) {
+		IPADMA_DBG("Already initialized refcnt=%d\n",
+			ipa3_dma_init_refcnt_ctrl->ref_cnt);
+		if (!ipa3_dma_ctx) {
+			IPADMA_ERR("Context missing. refcnt=%d\n",
+				ipa3_dma_init_refcnt_ctrl->ref_cnt);
+			res = -EFAULT;
+		} else {
+			ipa3_dma_init_refcnt_ctrl->ref_cnt++;
+		}
+		goto init_unlock;
+	}
+
 	if (ipa3_dma_ctx) {
-		IPADMA_ERR("Already initialized.\n");
-		return -EFAULT;
+		IPADMA_ERR("Context already exist\n");
+		res = -EFAULT;
+		goto init_unlock;
 	}
 
 	if (!ipa3_is_ready()) {
 		IPADMA_ERR("IPA is not ready yet\n");
-		return -EINVAL;
+		res = -EINVAL;
+		goto init_unlock;
 	}
 
 	ipa_dma_ctx_t = kzalloc(sizeof(*(ipa3_dma_ctx)), GFP_KERNEL);
 
 	if (!ipa_dma_ctx_t) {
 		IPADMA_ERR("kzalloc error.\n");
-		return -ENOMEM;
+		res = -ENOMEM;
+		goto init_unlock;
 	}
 
 	ipa_dma_ctx_t->ipa_dma_xfer_wrapper_cache =
@@ -176,7 +264,7 @@
 	mutex_init(&ipa_dma_ctx_t->sync_lock);
 	spin_lock_init(&ipa_dma_ctx_t->pending_lock);
 	init_completion(&ipa_dma_ctx_t->done);
-	ipa_dma_ctx_t->is_enabled = false;
+	ipa_dma_ctx_t->enable_ref_cnt = 0;
 	ipa_dma_ctx_t->destroy_pending = false;
 	atomic_set(&ipa_dma_ctx_t->async_memcpy_pending_cnt, 0);
 	atomic_set(&ipa_dma_ctx_t->sync_memcpy_pending_cnt, 0);
@@ -290,10 +378,12 @@
 	}
 	ipa3_dma_debugfs_init();
 	ipa3_dma_ctx = ipa_dma_ctx_t;
+	ipa3_dma_init_refcnt_ctrl->ref_cnt = 1;
 	IPADMA_DBG("ASYNC MEMCPY pipes are connected\n");
 
 	IPADMA_FUNC_EXIT();
-	return res;
+	goto init_unlock;
+
 fail_async_cons:
 	ipa3_teardown_sys_pipe(ipa_dma_ctx_t->ipa_dma_async_prod_hdl);
 fail_async_prod:
@@ -309,6 +399,8 @@
 fail_mem_ctrl:
 	kfree(ipa_dma_ctx_t);
 	ipa3_dma_ctx = NULL;
+init_unlock:
+	mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
 	return res;
 
 }
@@ -316,26 +408,29 @@
 /**
  * ipa3_dma_enable() -Vote for IPA clocks.
  *
+ * Can be executed several times (re-entrant)
+ *
  *Return codes: 0: success
  *		-EINVAL: IPADMA is not initialized
- *		-EPERM: Operation not permitted as ipa_dma is already
- *		 enabled
  */
 int ipa3_dma_enable(void)
 {
 	IPADMA_FUNC_ENTRY();
-	if (ipa3_dma_ctx == NULL) {
+	if ((ipa3_dma_ctx == NULL) ||
+		(ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
 		IPADMA_ERR("IPADMA isn't initialized, can't enable\n");
-		return -EPERM;
+		return -EINVAL;
 	}
 	mutex_lock(&ipa3_dma_ctx->enable_lock);
-	if (ipa3_dma_ctx->is_enabled) {
-		IPADMA_ERR("Already enabled.\n");
+	if (ipa3_dma_ctx->enable_ref_cnt > 0) {
+		IPADMA_ERR("Already enabled refcnt=%d\n",
+			ipa3_dma_ctx->enable_ref_cnt);
+		ipa3_dma_ctx->enable_ref_cnt++;
 		mutex_unlock(&ipa3_dma_ctx->enable_lock);
-		return -EPERM;
+		return 0;
 	}
 	IPA_ACTIVE_CLIENTS_INC_SPECIAL("DMA");
-	ipa3_dma_ctx->is_enabled = true;
+	ipa3_dma_ctx->enable_ref_cnt = 1;
 	mutex_unlock(&ipa3_dma_ctx->enable_lock);
 
 	IPADMA_FUNC_EXIT();
@@ -375,32 +470,45 @@
 int ipa3_dma_disable(void)
 {
 	unsigned long flags;
+	int res = 0;
+	bool dec_clks = false;
 
 	IPADMA_FUNC_ENTRY();
-	if (ipa3_dma_ctx == NULL) {
+	if ((ipa3_dma_ctx == NULL) ||
+		(ipa3_dma_init_refcnt_ctrl->ref_cnt < 1)) {
 		IPADMA_ERR("IPADMA isn't initialized, can't disable\n");
-		return -EPERM;
+		return -EINVAL;
 	}
 	mutex_lock(&ipa3_dma_ctx->enable_lock);
 	spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
-	if (!ipa3_dma_ctx->is_enabled) {
-		IPADMA_ERR("Already disabled.\n");
-		spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
-		mutex_unlock(&ipa3_dma_ctx->enable_lock);
-		return -EPERM;
+	if (ipa3_dma_ctx->enable_ref_cnt > 1) {
+		IPADMA_DBG("Multiple enablement done. refcnt=%d\n",
+			ipa3_dma_ctx->enable_ref_cnt);
+		ipa3_dma_ctx->enable_ref_cnt--;
+		goto completed;
 	}
+
+	if (ipa3_dma_ctx->enable_ref_cnt == 0) {
+		IPADMA_ERR("Already disabled\n");
+		res = -EPERM;
+		goto completed;
+	}
+
 	if (ipa3_dma_work_pending()) {
 		IPADMA_ERR("There is pending work, can't disable.\n");
-		spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
-		mutex_unlock(&ipa3_dma_ctx->enable_lock);
-		return -EFAULT;
+		res = -EFAULT;
+		goto completed;
 	}
-	ipa3_dma_ctx->is_enabled = false;
-	spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
-	IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA");
-	mutex_unlock(&ipa3_dma_ctx->enable_lock);
+	ipa3_dma_ctx->enable_ref_cnt = 0;
+	dec_clks = true;
 	IPADMA_FUNC_EXIT();
-	return 0;
+
+completed:
+	spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
+	if (dec_clks)
+		IPA_ACTIVE_CLIENTS_DEC_SPECIAL("DMA");
+	mutex_unlock(&ipa3_dma_ctx->enable_lock);
+	return res;
 }
 
 /**
@@ -449,7 +557,7 @@
 		return	-EINVAL;
 	}
 	spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
-	if (!ipa3_dma_ctx->is_enabled) {
+	if (!ipa3_dma_ctx->enable_ref_cnt) {
 		IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
 		spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
 		return -EPERM;
@@ -695,7 +803,7 @@
 		return -EINVAL;
 	}
 	spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
-	if (!ipa3_dma_ctx->is_enabled) {
+	if (!ipa3_dma_ctx->enable_ref_cnt) {
 		IPADMA_ERR("can't memcpy, IPA_DMA isn't enabled\n");
 		spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
 		return -EPERM;
@@ -877,7 +985,7 @@
 	}
 
 	spin_lock_irqsave(&ipa3_dma_ctx->pending_lock, flags);
-	if (!ipa3_dma_ctx->is_enabled) {
+	if (!ipa3_dma_ctx->enable_ref_cnt) {
 		IPADMA_ERR("can't memcpy, IPADMA isn't enabled\n");
 		spin_unlock_irqrestore(&ipa3_dma_ctx->pending_lock, flags);
 		return -EPERM;
@@ -911,17 +1019,36 @@
 	int res = 0;
 
 	IPADMA_FUNC_ENTRY();
-	if (!ipa3_dma_ctx) {
-		IPADMA_ERR("IPADMA isn't initialized\n");
+
+	if (!ipa3_dma_init_refcnt_ctrl) {
+		IPADMA_ERR("Setup isn't done\n");
 		return;
 	}
 
+	mutex_lock(&ipa3_dma_init_refcnt_ctrl->lock);
+	if (ipa3_dma_init_refcnt_ctrl->ref_cnt > 1) {
+		IPADMA_DBG("Multiple initialization done. refcnt=%d\n",
+			ipa3_dma_init_refcnt_ctrl->ref_cnt);
+		ipa3_dma_init_refcnt_ctrl->ref_cnt--;
+		goto completed;
+	}
+
+	if ((!ipa3_dma_ctx) || (ipa3_dma_init_refcnt_ctrl->ref_cnt == 0)) {
+		IPADMA_ERR("IPADMA isn't initialized ctx=%pK\n", ipa3_dma_ctx);
+		goto completed;
+	}
+
 	if (ipa3_dma_work_pending()) {
 		ipa3_dma_ctx->destroy_pending = true;
 		IPADMA_DBG("There are pending memcpy, wait for completion\n");
 		wait_for_completion(&ipa3_dma_ctx->done);
 	}
 
+	if (ipa3_dma_ctx->enable_ref_cnt > 0) {
+		IPADMA_ERR("IPADMA still enabled\n");
+		goto completed;
+	}
+
 	res = ipa3_teardown_sys_pipe(ipa3_dma_ctx->ipa_dma_async_cons_hdl);
 	if (res)
 		IPADMA_ERR("teardown IPADMA ASYNC CONS failed\n");
@@ -947,7 +1074,11 @@
 	kfree(ipa3_dma_ctx);
 	ipa3_dma_ctx = NULL;
 
+	ipa3_dma_init_refcnt_ctrl->ref_cnt = 0;
 	IPADMA_FUNC_EXIT();
+
+completed:
+	mutex_unlock(&ipa3_dma_init_refcnt_ctrl->lock);
 }
 
 /**
@@ -1006,15 +1137,30 @@
 {
 	int nbytes = 0;
 
+	if (!ipa3_dma_init_refcnt_ctrl) {
+		nbytes += scnprintf(&dbg_buff[nbytes],
+			IPADMA_MAX_MSG_LEN - nbytes,
+			"Setup was not done\n");
+		goto completed;
+
+	}
+
 	if (!ipa3_dma_ctx) {
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
-			"Not initialized\n");
+			"Status:\n	Not initialized (ref_cnt=%d)\n",
+			ipa3_dma_init_refcnt_ctrl->ref_cnt);
 	} else {
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
-			"Status:\n	IPADMA is %s\n",
-			(ipa3_dma_ctx->is_enabled) ? "Enabled" : "Disabled");
+			"Status:\n	Initialized (ref_cnt=%d)\n",
+			ipa3_dma_init_refcnt_ctrl->ref_cnt);
+		nbytes += scnprintf(&dbg_buff[nbytes],
+			IPADMA_MAX_MSG_LEN - nbytes,
+			"	%s (ref_cnt=%d)\n",
+			(ipa3_dma_ctx->enable_ref_cnt > 0) ?
+			"Enabled" : "Disabled",
+			ipa3_dma_ctx->enable_ref_cnt);
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
 			"Statistics:\n	total sync memcpy: %d\n	",
@@ -1025,17 +1171,23 @@
 			atomic_read(&ipa3_dma_ctx->total_async_memcpy));
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
+			"total uc memcpy: %d\n	",
+			atomic_read(&ipa3_dma_ctx->total_uc_memcpy));
+		nbytes += scnprintf(&dbg_buff[nbytes],
+			IPADMA_MAX_MSG_LEN - nbytes,
 			"pending sync memcpy jobs: %d\n	",
 			atomic_read(&ipa3_dma_ctx->sync_memcpy_pending_cnt));
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
-			"pending async memcpy jobs: %d\n",
+			"pending async memcpy jobs: %d\n	",
 			atomic_read(&ipa3_dma_ctx->async_memcpy_pending_cnt));
 		nbytes += scnprintf(&dbg_buff[nbytes],
 			IPADMA_MAX_MSG_LEN - nbytes,
 			"pending uc memcpy jobs: %d\n",
 			atomic_read(&ipa3_dma_ctx->uc_memcpy_pending_cnt));
 	}
+
+completed:
 	return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
index 02c5991..ee312c7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -73,6 +73,8 @@
 
 #define IPA_TX_SEND_COMPL_NOP_DELAY_NS (2 * 1000 * 1000)
 
+#define IPA_APPS_BW_FOR_PM 700
+
 static struct sk_buff *ipa3_get_skb_ipa_rx(unsigned int len, gfp_t flags);
 static void ipa3_replenish_wlan_rx_cache(struct ipa3_sys_context *sys);
 static void ipa3_replenish_rx_cache(struct ipa3_sys_context *sys);
@@ -134,8 +136,8 @@
 					DMA_TO_DEVICE);
 			} else {
 				dma_unmap_page(ipa3_ctx->pdev,
-					next_pkt->mem.phys_base,
-					next_pkt->mem.size,
+					tx_pkt->mem.phys_base,
+					tx_pkt->mem.size,
 					DMA_TO_DEVICE);
 			}
 		}
@@ -748,7 +750,10 @@
 	int inactive_cycles = 0;
 	int cnt;
 
-	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+	if (ipa3_ctx->use_ipa_pm)
+		ipa_pm_activate_sync(sys->pm_hdl);
+	else
+		IPA_ACTIVE_CLIENTS_INC_SIMPLE();
 	do {
 		cnt = ipa3_handle_rx_core(sys, true, true);
 		if (cnt == 0)
@@ -772,7 +777,10 @@
 
 	trace_poll_to_intr3(sys->ep->client);
 	ipa3_rx_switch_to_intr_mode(sys);
-	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+	if (ipa3_ctx->use_ipa_pm)
+		ipa_pm_deferred_deactivate(sys->pm_hdl);
+	else
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
 }
 
 static void ipa3_switch_to_intr_rx_work_func(struct work_struct *work)
@@ -799,6 +807,32 @@
 	return HRTIMER_NORESTART;
 }
 
+static void ipa_pm_sys_pipe_cb(void *p, enum ipa_pm_cb_event event)
+{
+	struct ipa3_sys_context *sys = (struct ipa3_sys_context *)p;
+
+	switch (event) {
+	case IPA_PM_CLIENT_ACTIVATED:
+		/*
+		 * this event is ignored as the sync version of activation
+		 * will be used.
+		 */
+		break;
+	case IPA_PM_REQUEST_WAKEUP:
+		/*
+		 * pipe will be unsuspended as part of
+		 * enabling IPA clocks
+		 */
+		ipa_pm_activate_sync(sys->pm_hdl);
+		ipa_pm_deferred_deactivate(sys->pm_hdl);
+		break;
+	default:
+		IPAERR("Unexpected event %d\n", event);
+		WARN_ON(1);
+		return;
+	}
+}
+
 /**
  * ipa3_setup_sys_pipe() - Setup an IPA GPI pipe and perform
  * IPA EP configuration
@@ -846,6 +880,9 @@
 	memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
 
 	if (!ep->sys) {
+		struct ipa_pm_register_params pm_reg;
+
+		memset(&pm_reg, 0, sizeof(pm_reg));
 		ep->sys = kzalloc(sizeof(struct ipa3_sys_context), GFP_KERNEL);
 		if (!ep->sys) {
 			IPAERR("failed to sys ctx for client %d\n",
@@ -884,6 +921,35 @@
 		hrtimer_init(&ep->sys->db_timer, CLOCK_MONOTONIC,
 			HRTIMER_MODE_REL);
 		ep->sys->db_timer.function = ipa3_ring_doorbell_timer_fn;
+
+		/* create IPA PM resources for handling polling mode */
+		if (ipa3_ctx->use_ipa_pm &&
+			IPA_CLIENT_IS_CONS(sys_in->client)) {
+			pm_reg.name = ipa_clients_strings[sys_in->client];
+			pm_reg.callback = ipa_pm_sys_pipe_cb;
+			pm_reg.user_data = ep->sys;
+			pm_reg.group = IPA_PM_GROUP_APPS;
+			result = ipa_pm_register(&pm_reg, &ep->sys->pm_hdl);
+			if (result) {
+				IPAERR("failed to create IPA PM client %d\n",
+					result);
+				goto fail_pm;
+			}
+
+			result = ipa_pm_associate_ipa_cons_to_client(
+				ep->sys->pm_hdl, sys_in->client);
+			if (result) {
+				IPAERR("failed to associate IPA PM client\n");
+				goto fail_gen2;
+			}
+
+			result = ipa_pm_set_perf_profile(ep->sys->pm_hdl,
+				IPA_APPS_BW_FOR_PM);
+			if (result) {
+				IPAERR("failed to set profile IPA PM client\n");
+				goto fail_gen2;
+			}
+		}
 	} else {
 		memset(ep->sys, 0, offsetof(struct ipa3_sys_context, ep));
 	}
@@ -984,6 +1050,9 @@
 	return 0;
 
 fail_gen2:
+	if (ipa3_ctx->use_ipa_pm)
+		ipa_pm_deregister(ep->sys->pm_hdl);
+fail_pm:
 	destroy_workqueue(ep->sys->repl_wq);
 fail_wq2:
 	destroy_workqueue(ep->sys->wq);
@@ -1402,7 +1471,8 @@
 	sys = container_of(work, struct ipa3_sys_context, work);
 
 	if (sys->ep->napi_enabled) {
-		IPA_ACTIVE_CLIENTS_INC_SPECIAL("NAPI");
+		if (!ipa3_ctx->use_ipa_pm)
+			IPA_ACTIVE_CLIENTS_INC_SPECIAL("NAPI");
 		sys->ep->client_notify(sys->ep->priv,
 				IPA_CLIENT_START_POLL, 0);
 	} else
@@ -2683,6 +2753,21 @@
 		return 0;
 	}
 
+	if (in->client == IPA_CLIENT_APPS_WAN_PROD) {
+		sys->policy = IPA_POLICY_INTR_MODE;
+		sys->use_comm_evt_ring = true;
+		INIT_WORK(&sys->work, ipa3_send_nop_desc);
+
+		/*
+		 * enable source notification status for exception packets
+		 * (i.e. QMAP commands) to be routed to modem.
+		 */
+		sys->ep->status.status_en = true;
+		sys->ep->status.status_ep =
+			ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_CONS);
+		return 0;
+	}
+
 	if (IPA_CLIENT_IS_MEMCPY_DMA_PROD(in->client)) {
 		sys->policy = IPA_POLICY_NOINTR_MODE;
 		return 0;
@@ -3268,11 +3353,48 @@
 	}
 }
 
+void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys)
+{
+	bool clk_off;
+
+	atomic_set(&sys->curr_polling_state, 1);
+	ipa3_inc_acquire_wakelock();
+
+	/*
+	 * pm deactivate is done in wq context
+	 * or after NAPI poll
+	 */
+	if (ipa3_ctx->use_ipa_pm) {
+		clk_off = ipa_pm_activate(sys->pm_hdl);
+		if (!clk_off && sys->ep->napi_enabled) {
+			sys->ep->client_notify(sys->ep->priv,
+				IPA_CLIENT_START_POLL, 0);
+			return;
+		}
+		queue_work(sys->wq, &sys->work);
+		return;
+	}
+
+	if (sys->ep->napi_enabled) {
+		struct ipa_active_client_logging_info log;
+
+		IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log, "NAPI");
+		clk_off = ipa3_inc_client_enable_clks_no_block(
+			&log);
+		if (!clk_off) {
+			sys->ep->client_notify(sys->ep->priv,
+				IPA_CLIENT_START_POLL, 0);
+			return;
+		}
+	}
+
+	queue_work(sys->wq, &sys->work);
+}
+
 static void ipa_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify)
 {
 	struct ipa3_sys_context *sys;
 	struct ipa3_rx_pkt_wrapper *rx_pkt_expected, *rx_pkt_rcvd;
-	int clk_off;
 
 	if (!notify) {
 		IPAERR("gsi notify is NULL.\n");
@@ -3302,22 +3424,7 @@
 			/* put the gsi channel into polling mode */
 			gsi_config_channel_mode(sys->ep->gsi_chan_hdl,
 				GSI_CHAN_MODE_POLL);
-			ipa3_inc_acquire_wakelock();
-			atomic_set(&sys->curr_polling_state, 1);
-			if (sys->ep->napi_enabled) {
-				struct ipa_active_client_logging_info log;
-
-				IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log, "NAPI");
-				clk_off = ipa3_inc_client_enable_clks_no_block(
-					&log);
-				if (!clk_off)
-					sys->ep->client_notify(sys->ep->priv,
-						IPA_CLIENT_START_POLL, 0);
-				else
-					queue_work(sys->wq, &sys->work);
-			} else {
-				queue_work(sys->wq, &sys->work);
-			}
+			__ipa_gsi_irq_rx_scedule_poll(sys);
 		}
 		break;
 	default:
@@ -3709,7 +3816,7 @@
 		cnt += IPA_WAN_AGGR_PKT_CNT;
 		total_cnt++;
 
-		if (ep->sys->len == 0 || total_cnt >= ep->sys->rx_pool_sz) {
+		if (ep->sys->len == 0) {
 			total_cnt = 0;
 			cnt = cnt-1;
 			break;
@@ -3719,7 +3826,10 @@
 	if (cnt < weight) {
 		ep->client_notify(ep->priv, IPA_CLIENT_COMP_NAPI, 0);
 		ipa3_rx_switch_to_intr_mode(ep->sys);
-		ipa3_dec_client_disable_clks_no_block(&log);
+		if (ipa3_ctx->use_ipa_pm)
+			ipa_pm_deferred_deactivate(ep->sys->pm_hdl);
+		else
+			ipa3_dec_client_disable_clks_no_block(&log);
 	}
 
 	return cnt;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index beca549..0f3940f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -62,7 +62,7 @@
 
 	res = ipahal_flt_generate_hw_rule(&gen_params, &entry->hw_len, buf);
 	if (res)
-		IPAERR("failed to generate flt h/w rule\n");
+		IPAERR_RL("failed to generate flt h/w rule\n");
 
 	return 0;
 }
@@ -311,7 +311,7 @@
 	}
 
 	if (ipahal_fltrt_allocate_hw_tbl_imgs(alloc_params)) {
-		IPAERR("fail to allocate FLT HW TBL images. IP %d\n", ip);
+		IPAERR_RL("fail to allocate FLT HW TBL images. IP %d\n", ip);
 		rc = -ENOMEM;
 		goto allocate_failed;
 	}
@@ -319,14 +319,14 @@
 	if (ipa_translate_flt_tbl_to_hw_fmt(ip, IPA_RULE_HASHABLE,
 		alloc_params->hash_bdy.base, alloc_params->hash_hdr.base,
 		hash_bdy_start_ofst)) {
-		IPAERR("fail to translate hashable flt tbls to hw format\n");
+		IPAERR_RL("fail to translate hashable flt tbls to hw format\n");
 		rc = -EPERM;
 		goto translate_fail;
 	}
 	if (ipa_translate_flt_tbl_to_hw_fmt(ip, IPA_RULE_NON_HASHABLE,
 		alloc_params->nhash_bdy.base, alloc_params->nhash_hdr.base,
 		nhash_bdy_start_ofst)) {
-		IPAERR("fail to translate non-hash flt tbls to hw format\n");
+		IPAERR_RL("fail to translate non-hash flt tbls to hw format\n");
 		rc = -EPERM;
 		goto translate_fail;
 	}
@@ -385,19 +385,15 @@
  *  payload pointers buffers for headers and bodies of flt structure
  *  as well as place for flush imm.
  * @ipt: the ip address family type
+ * @entries: the number of entries
  * @desc: [OUT] descriptor buffer
  * @cmd: [OUT] imm commands payload pointers buffer
  *
  * Return: 0 on success, negative on failure
  */
-static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip,
+static int ipa_flt_alloc_cmd_buffers(enum ipa_ip_type ip, u16 entries,
 	struct ipa3_desc **desc, struct ipahal_imm_cmd_pyld ***cmd_pyld)
 {
-	u16 entries;
-
-	/* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
-	entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
-
 	*desc = kcalloc(entries, sizeof(**desc), GFP_ATOMIC);
 	if (*desc == NULL) {
 		IPAERR("fail to alloc desc blob ip %d\n", ip);
@@ -473,6 +469,7 @@
 	struct ipahal_reg_valmask valmask;
 	u32 tbl_hdr_width;
 	struct ipa3_flt_tbl *tbl;
+	u16 entries;
 
 	tbl_hdr_width = ipahal_get_hw_tbl_hdr_width();
 	memset(&alloc_params, 0, sizeof(alloc_params));
@@ -533,7 +530,7 @@
 	}
 
 	if (ipa_generate_flt_hw_tbl_img(ip, &alloc_params)) {
-		IPAERR("fail to generate FLT HW TBL image. IP %d\n", ip);
+		IPAERR_RL("fail to generate FLT HW TBL image. IP %d\n", ip);
 		rc = -EFAULT;
 		goto prep_failed;
 	}
@@ -549,7 +546,10 @@
 		goto fail_size_valid;
 	}
 
-	if (ipa_flt_alloc_cmd_buffers(ip, &desc, &cmd_pyld)) {
+	/* +3: 2 for bodies (hashable and non-hashable) and 1 for flushing */
+	entries = (ipa3_ctx->ep_flt_num) * 2 + 3;
+
+	if (ipa_flt_alloc_cmd_buffers(ip, entries, &desc, &cmd_pyld)) {
 		rc = -ENOMEM;
 		goto fail_size_valid;
 	}
@@ -573,11 +573,8 @@
 		rc = -EFAULT;
 		goto fail_reg_write_construct;
 	}
-	desc[0].opcode = cmd_pyld[0]->opcode;
-	desc[0].pyld = cmd_pyld[0]->data;
-	desc[0].len = cmd_pyld[0]->len;
-	desc[0].type = IPA_IMM_CMD_DESC;
-	num_cmd++;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
 
 	hdr_idx = 0;
 	for (i = 0; i < ipa3_ctx->ipa_num_pipes; i++) {
@@ -591,6 +588,13 @@
 			continue;
 		}
 
+		if (num_cmd + 1 >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		IPADBG_LOW("Prepare imm cmd for hdr at index %d for pipe %d\n",
 			hdr_idx, i);
 
@@ -607,12 +611,11 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
@@ -627,17 +630,23 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 
-		hdr_idx++;
+		++hdr_idx;
 	}
 
 	if (lcl_nhash) {
+		if (num_cmd >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -649,14 +658,20 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 	}
 	if (lcl_hash) {
+		if (num_cmd >= entries) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -668,12 +683,11 @@
 		if (!cmd_pyld[num_cmd]) {
 			IPAERR("fail construct dma_shared_mem cmd: IP = %d\n",
 				ip);
+			rc = -ENOMEM;
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd++].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
 	}
 
 	if (ipa3_send_cmd(num_cmd, desc)) {
@@ -731,40 +745,45 @@
 	if (rule->action != IPA_PASS_TO_EXCEPTION) {
 		if (!rule->eq_attrib_type) {
 			if (!rule->rt_tbl_hdl) {
-				IPAERR("invalid RT tbl\n");
+				IPAERR_RL("invalid RT tbl\n");
 				goto error;
 			}
 
 			*rt_tbl = ipa3_id_find(rule->rt_tbl_hdl);
 			if (*rt_tbl == NULL) {
-				IPAERR("RT tbl not found\n");
+				IPAERR_RL("RT tbl not found\n");
 				goto error;
 			}
 
 			if ((*rt_tbl)->cookie != IPA_RT_TBL_COOKIE) {
-				IPAERR("RT table cookie is invalid\n");
+				IPAERR_RL("RT table cookie is invalid\n");
 				goto error;
 			}
 		} else {
 			if (rule->rt_tbl_idx > ((ip == IPA_IP_v4) ?
 				IPA_MEM_PART(v4_modem_rt_index_hi) :
 				IPA_MEM_PART(v6_modem_rt_index_hi))) {
-				IPAERR("invalid RT tbl\n");
+				IPAERR_RL("invalid RT tbl\n");
 				goto error;
 			}
 		}
+	} else {
+		if (rule->rt_tbl_idx > 0) {
+			IPAERR("invalid RT tbl\n");
+			goto error;
+		}
 	}
 
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
 		if (rule->pdn_idx) {
 			if (rule->action == IPA_PASS_TO_EXCEPTION ||
 				rule->action == IPA_PASS_TO_ROUTING) {
-				IPAERR(
+				IPAERR_RL(
 					"PDN index should be 0 when action is not pass to NAT\n");
 				goto error;
 			} else {
 				if (rule->pdn_idx >= IPA_MAX_PDN_NUM) {
-					IPAERR("PDN index %d is too large\n",
+					IPAERR_RL("PDN index %d is too large\n",
 						rule->pdn_idx);
 					goto error;
 				}
@@ -773,8 +792,9 @@
 	}
 
 	if (rule->rule_id) {
-		if (!(rule->rule_id & ipahal_get_rule_id_hi_bit())) {
-			IPAERR("invalid rule_id provided 0x%x\n"
+		if ((rule->rule_id < ipahal_get_rule_id_hi_bit()) ||
+		(rule->rule_id >= ((ipahal_get_rule_id_hi_bit()<<1)-1))) {
+			IPAERR_RL("invalid rule_id provided 0x%x\n"
 				"rule_id with bit 0x%x are auto generated\n",
 				rule->rule_id, ipahal_get_rule_id_hi_bit());
 			goto error;
@@ -808,8 +828,8 @@
 	} else {
 		id = ipa3_alloc_rule_id(tbl->rule_ids);
 		if (id < 0) {
-			IPAERR("failed to allocate rule id\n");
-			WARN_ON(1);
+			IPAERR_RL("failed to allocate rule id\n");
+			WARN_ON_RATELIMIT_IPA(1);
 			goto rule_id_fail;
 		}
 	}
@@ -833,8 +853,8 @@
 		entry->rt_tbl->ref_cnt++;
 	id = ipa3_id_alloc(entry);
 	if (id < 0) {
-		IPAERR("failed to add to tree\n");
-		WARN_ON(1);
+		IPAERR_RL("failed to add to tree\n");
+		WARN_ON_RATELIMIT_IPA(1);
 		goto ipa_insert_failed;
 	}
 	*rule_hdl = id;
@@ -879,7 +899,8 @@
 ipa_insert_failed:
 	list_del(&entry->link);
 	/* if rule id was allocated from idr, remove it */
-	if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+	if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+		(entry->rule_id >= ipahal_get_low_rule_id()))
 		idr_remove(entry->tbl->rule_ids, entry->rule_id);
 	kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
 
@@ -926,7 +947,8 @@
 ipa_insert_failed:
 	list_del(&entry->link);
 	/* if rule id was allocated from idr, remove it */
-	if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+	if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+		(entry->rule_id >= ipahal_get_low_rule_id()))
 		idr_remove(entry->tbl->rule_ids, entry->rule_id);
 	kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
 
@@ -960,7 +982,8 @@
 		entry->tbl->rule_cnt, entry->rule_id);
 	entry->cookie = 0;
 	/* if rule id was allocated from idr, remove it */
-	if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+	if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+		(entry->rule_id >= ipahal_get_low_rule_id()))
 		idr_remove(entry->tbl->rule_ids, entry->rule_id);
 
 	kmem_cache_free(ipa3_ctx->flt_rule_cache, entry);
@@ -1016,6 +1039,11 @@
 				goto error;
 			}
 		}
+	} else {
+		if (frule->rule.rt_tbl_idx > 0) {
+			IPAERR_RL("invalid RT tbl\n");
+			goto error;
+		}
 	}
 
 	entry->rule = frule->rule;
@@ -1170,6 +1198,13 @@
 		goto bail;
 	}
 
+	if (entry->cookie != IPA_FLT_COOKIE) {
+		IPAERR_RL("Invalid cookie value =  %u flt hdl id = %d\n",
+			entry->cookie, rules->add_after_hdl);
+		result = -EINVAL;
+		goto bail;
+	}
+
 	if (entry->tbl != tbl) {
 		IPAERR_RL("given entry does not match the table\n");
 		result = -EINVAL;
@@ -1364,7 +1399,7 @@
 		list_for_each_entry_safe(entry, next, &tbl->head_flt_rule_list,
 				link) {
 			if (ipa3_id_find(entry->id) == NULL) {
-				WARN_ON(1);
+				WARN_ON_RATELIMIT_IPA(1);
 				mutex_unlock(&ipa3_ctx->lock);
 				return -EFAULT;
 			}
@@ -1373,7 +1408,8 @@
 			if (entry->rt_tbl)
 				entry->rt_tbl->ref_cnt--;
 			/* if rule id was allocated from idr, remove it */
-			if (!(entry->rule_id & ipahal_get_rule_id_hi_bit()))
+			if ((entry->rule_id < ipahal_get_rule_id_hi_bit()) &&
+				(entry->rule_id >= ipahal_get_low_rule_id()))
 				idr_remove(entry->tbl->rule_ids,
 					entry->rule_id);
 			entry->cookie = 0;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
index cc29f8f..a37df7e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -199,9 +199,6 @@
 				IPAERR("fail construct dma_shared_mem cmd\n");
 				goto end;
 			}
-			desc[0].opcode = hdr_cmd_pyld->opcode;
-			desc[0].pyld = hdr_cmd_pyld->data;
-			desc[0].len = hdr_cmd_pyld->len;
 		}
 	} else {
 		if (hdr_mem.size > IPA_MEM_PART(apps_hdr_size_ddr)) {
@@ -217,12 +214,9 @@
 				IPAERR("fail construct hdr_init_system cmd\n");
 				goto end;
 			}
-			desc[0].opcode = hdr_cmd_pyld->opcode;
-			desc[0].pyld = hdr_cmd_pyld->data;
-			desc[0].len = hdr_cmd_pyld->len;
 		}
 	}
-	desc[0].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[0], hdr_cmd_pyld);
 	IPA_DUMP_BUFF(hdr_mem.base, hdr_mem.phys_base, hdr_mem.size);
 
 	proc_ctx_size = IPA_MEM_PART(apps_hdr_proc_ctx_size);
@@ -249,9 +243,6 @@
 				IPAERR("fail construct dma_shared_mem cmd\n");
 				goto end;
 			}
-			desc[1].opcode = ctx_cmd_pyld->opcode;
-			desc[1].pyld = ctx_cmd_pyld->data;
-			desc[1].len = ctx_cmd_pyld->len;
 		}
 	} else {
 		proc_ctx_size_ddr = IPA_MEM_PART(apps_hdr_proc_ctx_size_ddr);
@@ -277,12 +268,9 @@
 				IPAERR("fail construct register_write cmd\n");
 				goto end;
 			}
-			desc[1].opcode = ctx_cmd_pyld->opcode;
-			desc[1].pyld = ctx_cmd_pyld->data;
-			desc[1].len = ctx_cmd_pyld->len;
 		}
 	}
-	desc[1].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[1], ctx_cmd_pyld);
 	IPA_DUMP_BUFF(ctx_mem.base, ctx_mem.phys_base, ctx_mem.size);
 
 	if (ipa3_send_cmd(2, desc))
@@ -355,7 +343,7 @@
 	}
 	if (hdr_entry->cookie != IPA_HDR_COOKIE) {
 		IPAERR_RL("Invalid header cookie %u\n", hdr_entry->cookie);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EINVAL;
 	}
 	IPADBG("Associated header is name=%s is_hdr_proc_ctx=%d\n",
@@ -385,7 +373,7 @@
 		bin = IPA_HDR_PROC_CTX_BIN1;
 	} else {
 		IPAERR_RL("unexpected needed len %d\n", needed_len);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		goto bad_len;
 	}
 
@@ -430,8 +418,8 @@
 
 	id = ipa3_id_alloc(entry);
 	if (id < 0) {
-		IPAERR("failed to alloc id\n");
-		WARN_ON(1);
+		IPAERR_RL("failed to alloc id\n");
+		WARN_ON_RATELIMIT_IPA(1);
 		goto ipa_insert_failed;
 	}
 	entry->id = id;
@@ -567,8 +555,8 @@
 
 	id = ipa3_id_alloc(entry);
 	if (id < 0) {
-		IPAERR("failed to alloc id\n");
-		WARN_ON(1);
+		IPAERR_RL("failed to alloc id\n");
+		WARN_ON_RATELIMIT_IPA(1);
 		goto ipa_insert_failed;
 	}
 	entry->id = id;
@@ -996,7 +984,7 @@
 			if (entry->is_hdr_proc_ctx) {
 				IPAERR("default header is proc ctx\n");
 				mutex_unlock(&ipa3_ctx->lock);
-				WARN_ON(1);
+				WARN_ON_RATELIMIT_IPA(1);
 				return -EFAULT;
 			}
 			continue;
@@ -1004,7 +992,7 @@
 
 		if (ipa3_id_find(entry->id) == NULL) {
 			mutex_unlock(&ipa3_ctx->lock);
-			WARN_ON(1);
+			WARN_ON_RATELIMIT_IPA(1);
 			return -EFAULT;
 		}
 		if (entry->is_hdr_proc_ctx) {
@@ -1058,7 +1046,7 @@
 
 		if (ipa3_id_find(ctx_entry->id) == NULL) {
 			mutex_unlock(&ipa3_ctx->lock);
-			WARN_ON(1);
+			WARN_ON_RATELIMIT_IPA(1);
 			return -EFAULT;
 		}
 		list_del(&ctx_entry->link);
@@ -1133,6 +1121,7 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa3_ctx->lock);
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_hdr(lookup->name);
 	if (entry) {
 		lookup->hdl = entry->id;
@@ -1255,6 +1244,7 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa3_ctx->lock);
+	copy->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa_find_hdr(copy->name);
 	if (entry) {
 		memcpy(copy->hdr, entry->hdr, entry->hdr_len);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h b/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h
deleted file mode 100644
index dff3a3f..0000000
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hw_defs.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Copyright (c) 2012-2016, 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 _IPA_HW_DEFS_H
-#define _IPA_HW_DEFS_H
-#include <linux/bitops.h>
-
-/* This header defines various HW related data types */
-
-
-#define IPA_A5_MUX_HDR_EXCP_FLAG_IP		BIT(7)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_NAT		BIT(6)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_SW_FLT	BIT(5)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_TAG		BIT(4)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_REPLICATED	BIT(3)
-#define IPA_A5_MUX_HDR_EXCP_FLAG_IHL		BIT(2)
-
-/**
- * struct ipa3_a5_mux_hdr - A5 MUX header definition
- * @interface_id: interface ID
- * @src_pipe_index: source pipe index
- * @flags: flags
- * @metadata: metadata
- *
- * A5 MUX header is in BE, A5 runs in LE. This struct definition
- * allows A5 SW to correctly parse the header
- */
-struct ipa3_a5_mux_hdr {
-	u16 interface_id;
-	u8 src_pipe_index;
-	u8 flags;
-	u32 metadata;
-};
-
-#endif /* _IPA_HW_DEFS_H */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index b891606..3eff209 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -27,7 +27,6 @@
 #include <linux/iommu.h>
 #include <linux/platform_device.h>
 #include <linux/firmware.h>
-#include "ipa_hw_defs.h"
 #include "ipa_qmi_service.h"
 #include "../ipa_api.h"
 #include "ipahal/ipahal_reg.h"
@@ -36,9 +35,11 @@
 #include "ipahal/ipahal_hw_stats.h"
 #include "../ipa_common_i.h"
 #include "ipa_uc_offload_i.h"
+#include "ipa_pm.h"
 
+#define IPA_DEV_NAME_MAX_LEN 15
 #define DRV_NAME "ipa"
-#define NAT_DEV_NAME "ipaNatTable"
+
 #define IPA_COOKIE 0x57831603
 #define IPA_RT_RULE_COOKIE 0x57831604
 #define IPA_RT_TBL_COOKIE 0x57831605
@@ -71,6 +72,8 @@
 
 #define IPA_IPC_LOG_PAGES 50
 
+#define IPA_MAX_NUM_REQ_CACHE 10
+
 #define IPADBG(fmt, args...) \
 	do { \
 		pr_debug(DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args);\
@@ -103,7 +106,7 @@
 
 #define IPAERR_RL(fmt, args...) \
 	do { \
-		pr_err_ratelimited(DRV_NAME " %s:%d " fmt, __func__,\
+		pr_err_ratelimited_ipa(DRV_NAME " %s:%d " fmt, __func__,\
 		__LINE__, ## args);\
 		if (ipa3_ctx) { \
 			IPA_IPC_LOGGING(ipa3_ctx->logbuf, \
@@ -122,6 +125,8 @@
 
 #define IPA_RAM_NAT_OFST    0
 #define IPA_RAM_NAT_SIZE    0
+#define IPA_RAM_IPV6CT_OFST 0
+#define IPA_RAM_IPV6CT_SIZE 0
 #define IPA_MEM_CANARY_VAL 0xdeadbeef
 
 #define IPA_STATS
@@ -428,6 +433,7 @@
 	int id;
 	u16 prio;
 	u16 rule_id;
+	u16 rule_id_valid;
 };
 
 /**
@@ -553,6 +559,7 @@
 	u32 qmi_request_sent;
 	bool napi_enabled;
 	u32 eot_in_poll_err;
+	bool ep_delay_set;
 
 	/* sys MUST be the last element of this struct */
 	struct ipa3_sys_context *sys;
@@ -645,6 +652,7 @@
 	struct workqueue_struct *wq;
 	struct workqueue_struct *repl_wq;
 	struct ipa3_status_stats *status_stat;
+	u32 pm_hdl;
 	/* ordering is important - other immutable fields go below */
 };
 
@@ -775,57 +783,101 @@
 	u32 dst_metadata;
 	u32 resrvd;
 };
+
 /**
- * struct ipa3_nat_mem - IPA NAT memory description
+ * struct ipa3_nat_ipv6ct_tmp_mem - NAT/IPv6CT temporary memory
+ *
+ * In case NAT/IPv6CT table are destroyed the HW is provided with the
+ * temporary memory
+ *
+ * @vaddr: the address of the temporary memory
+ * @dma_handle: the handle of the temporary memory
+ */
+struct ipa3_nat_ipv6ct_tmp_mem {
+	void *vaddr;
+	dma_addr_t dma_handle;
+};
+
+/**
+ * struct ipa3_nat_ipv6ct_common_mem - IPA NAT/IPv6CT memory device
  * @class: pointer to the struct class
  * @dev: the dev_t of the device
  * @cdev: cdev of the device
  * @dev_num: device number
- * @vaddr: virtual address
- * @dma_handle: DMA handle
- * @size: NAT memory size
- * @is_mapped: flag indicating if NAT memory is mapped
- * @is_sys_mem: flag indicating if NAT memory is sys memory
- * @is_dev_init: flag indicating if NAT device is initialized
- * @lock: NAT memory mutex
- * @nat_base_address: nat table virutal address
- * @ipv4_rules_addr: base nat table address
- * @ipv4_expansion_rules_addr: expansion table address
- * @index_table_addr: index table address
- * @index_table_expansion_addr: index expansion table address
- * @size_base_tables: base table size
- * @size_expansion_tables: expansion table size
- * @public_ip_addr: ip address of nat table
- * @pdn_mem: pdn config table SW cache memory structure
+ * @vaddr: the virtual address in the system memory
+ * @dma_handle: the system memory DMA handle
+ * @phys_mem_size: the physical size in the shared memory
+ * @smem_offset: the offset in the shared memory
+ * @size: memory size
+ * @is_mapped: flag indicating if memory is mapped
+ * @is_sys_mem: flag indicating if memory is sys memory
+ * @is_mem_allocated: flag indicating if the memory is allocated
+ * @is_hw_init: flag indicating if the corresponding HW is initialized
+ * @is_dev_init: flag indicating if device is initialized
+ * @lock: memory mutex
+ * @base_address: table virtual address
+ * @base_table_addr: base table address
+ * @expansion_table_addr: expansion table address
+ * @table_entries: num of entries in the base table
+ * @expn_table_entries: num of entries in the expansion table
+ * @tmp_mem: temporary memory used to always provide HW with a legal memory
+ * @name: the device name
  */
-struct ipa3_nat_mem {
+struct ipa3_nat_ipv6ct_common_mem {
 	struct class *class;
 	struct device *dev;
 	struct cdev cdev;
 	dev_t dev_num;
+
+	/* system memory */
 	void *vaddr;
 	dma_addr_t dma_handle;
+
+	/* shared memory */
+	u32 phys_mem_size;
+	u32 smem_offset;
+
 	size_t size;
 	bool is_mapped;
 	bool is_sys_mem;
+	bool is_mem_allocated;
+	bool is_hw_init;
 	bool is_dev_init;
-	bool is_dev;
 	struct mutex lock;
-	void *nat_base_address;
-	char *ipv4_rules_addr;
-	char *ipv4_expansion_rules_addr;
+	void *base_address;
+	char *base_table_addr;
+	char *expansion_table_addr;
+	u32 table_entries;
+	u32 expn_table_entries;
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	char name[IPA_DEV_NAME_MAX_LEN];
+};
+
+/**
+ * struct ipa3_nat_mem - IPA NAT memory description
+ * @dev: the memory device structure
+ * @index_table_addr: index table address
+ * @index_table_expansion_addr: index expansion table address
+ * @public_ip_addr: ip address of nat table
+ * @pdn_mem: pdn config table SW cache memory structure
+ */
+struct ipa3_nat_mem {
+	struct ipa3_nat_ipv6ct_common_mem dev;
 	char *index_table_addr;
 	char *index_table_expansion_addr;
-	u32 size_base_tables;
-	u32 size_expansion_tables;
 	u32 public_ip_addr;
-	void *tmp_vaddr;
-	dma_addr_t tmp_dma_handle;
-	bool is_tmp_mem;
 	struct ipa_mem_buffer pdn_mem;
 };
 
 /**
+* struct ipa3_ipv6ct_mem - IPA IPv6 connection tracking memory description
+* @dev: the memory device structure
+*/
+struct ipa3_ipv6ct_mem {
+	struct ipa3_nat_ipv6ct_common_mem dev;
+};
+
+/**
  * enum ipa3_hw_mode - IPA hardware mode
  * @IPA_HW_Normal: Regular IPA hardware
  * @IPA_HW_Virtual: IPA hardware supporting virtual memory allocation
@@ -867,6 +919,7 @@
 struct ipa3_active_clients {
 	struct mutex mutex;
 	atomic_t cnt;
+	int bus_vote_idx;
 };
 
 struct ipa3_wakelock_ref_cnt {
@@ -1028,11 +1081,6 @@
 	void *user_data;
 };
 
-struct ipa_tz_unlock_reg_info {
-	u64 reg_addr;
-	u32 size;
-};
-
 struct ipa_dma_task_info {
 	struct ipa_mem_buffer mem;
 	struct ipahal_imm_cmd_pyld *cmd_pyld;
@@ -1088,6 +1136,18 @@
 	struct ipa_hw_stats_drop drop;
 };
 
+struct ipa_cne_evt {
+	struct ipa_wan_msg wan_msg;
+	struct ipa_msg_meta msg_meta;
+};
+
+enum ipa_smmu_cb_type {
+	IPA_SMMU_CB_AP,
+	IPA_SMMU_CB_WLAN,
+	IPA_SMMU_CB_UC,
+	IPA_SMMU_CB_MAX
+};
+
 /**
  * struct ipa3_context - IPA context
  * @class: pointer to the struct class
@@ -1124,6 +1184,7 @@
  *  from non-restricted bytes
  * @smem_restricted_bytes: the bytes that SW should not use in the shared mem
  * @nat_mem: NAT memory
+ * @ipv6ct_mem: IPv6CT memory
  * @excp_hdr_hdl: exception header handle
  * @dflt_v4_rt_rule_hdl: default v4 routing rule handle
  * @dflt_v6_rt_rule_hdl: default v6 routing rule handle
@@ -1173,6 +1234,7 @@
  * @ipa_ready_cb_list: A list of all the clients who require a CB when IPA
  *  driver is ready/initialized.
  * @init_completion_obj: Completion object to be used in case IPA driver hasn't
+ * @mhi_evid_limits: MHI event rings start and end ids
  *  finished initializing. Example of use - IOCTLs to /dev/ipa
  * IPA context - holds all relevant info about IPA driver and its state
  */
@@ -1210,6 +1272,7 @@
 	u16 smem_restricted_bytes;
 	u16 smem_reqd_sz;
 	struct ipa3_nat_mem nat_mem;
+	struct ipa3_ipv6ct_mem ipv6ct_mem;
 	u32 excp_hdr_hdl;
 	u32 dflt_v4_rt_rule_hdl;
 	u32 dflt_v6_rt_rule_hdl;
@@ -1268,6 +1331,7 @@
 	u32 curr_ipa_clk_rate;
 	bool q6_proxy_clk_vote_valid;
 	struct mutex q6_proxy_clk_vote_mutex;
+	u32 q6_proxy_clk_vote_cnt;
 	u32 ipa_num_pipes;
 	dma_addr_t pkt_init_imm[IPA3_MAX_NUM_PIPES];
 	u32 pkt_init_imm_opcode;
@@ -1286,7 +1350,7 @@
 	bool apply_rg10_wa;
 	bool gsi_ch20_wa;
 	bool smmu_present;
-	bool smmu_s1_bypass;
+	bool s1_bypass_arr[IPA_SMMU_CB_MAX];
 	u32 wdi_map_cnt;
 	struct wakeup_source w_lock;
 	struct ipa3_wakelock_ref_cnt wakelock_ref_cnt;
@@ -1300,10 +1364,15 @@
 	struct completion init_completion_obj;
 	struct completion uc_loaded_completion_obj;
 	struct ipa3_smp2p_info smp2p_info;
+	u32 mhi_evid_limits[2]; /* start and end values */
 	u32 ipa_tz_unlock_reg_num;
 	struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
 	struct ipa_dma_task_info dma_task_info;
 	struct ipa_hw_stats hw_stats;
+	struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE];
+	int num_ipa_cne_evt_req;
+	struct mutex ipa_cne_evt_lock;
+	bool use_ipa_pm;
 };
 
 struct ipa3_plat_drv_res {
@@ -1329,8 +1398,11 @@
 	bool apply_rg10_wa;
 	bool gsi_ch20_wa;
 	bool tethered_flow_control;
+	u32 mhi_evid_limits[2]; /* start and end values */
 	u32 ipa_tz_unlock_reg_num;
 	struct ipa_tz_unlock_reg_info *ipa_tz_unlock_reg;
+	bool use_ipa_pm;
+	struct ipa_pm_init_params pm_init;
 };
 
 /**
@@ -1690,6 +1762,8 @@
  */
 int ipa3_add_rt_rule(struct ipa_ioc_add_rt_rule *rules);
 
+int ipa3_add_rt_rule_ext(struct ipa_ioc_add_rt_rule_ext *rules);
+
 int ipa3_add_rt_rule_after(struct ipa_ioc_add_rt_rule_after *rules);
 
 int ipa3_del_rt_rule(struct ipa_ioc_del_rt_rule *hdls);
@@ -1724,13 +1798,24 @@
 /*
  * NAT
  */
+int ipa3_nat_ipv6ct_init_devices(void);
+void ipa3_nat_ipv6ct_destroy_devices(void);
+
 int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa3_allocate_nat_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+int ipa3_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
 
 int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init);
 
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
 int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
 
 int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
 
 int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
 
@@ -1809,6 +1894,11 @@
 int ipa3_tear_down_uc_offload_pipes(int ipa_ep_idx_ul, int ipa_ep_idx_dl);
 int ipa3_ntn_uc_reg_rdyCB(void (*ipauc_ready_cb)(void *), void *priv);
 void ipa3_ntn_uc_dereg_rdyCB(void);
+int ipa3_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out);
+int ipa3_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa3_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
+int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx);
 
 /*
  * To retrieve doorbell physical address of
@@ -2076,8 +2166,9 @@
 		    bool polling_mode, unsigned long timeout_jiffies);
 void ipa3_uc_register_handlers(enum ipa3_hw_features feature,
 			      struct ipa3_uc_hdlrs *hdlrs);
-int ipa3_create_nat_device(void);
 int ipa3_uc_notify_clk_state(bool enabled);
+int ipa3_dma_setup(void);
+void ipa3_dma_shutdown(void);
 void ipa3_dma_async_memcpy_notify_cb(void *priv,
 		enum ipa_dp_evt_type evt, unsigned long data);
 
@@ -2216,4 +2307,9 @@
 int ipa3_alloc_common_event_ring(void);
 int ipa3_allocate_dma_task_for_gsi(void);
 void ipa3_free_dma_task_for_gsi(void);
+int ipa3_set_clock_plan_from_pm(int idx);
+void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys);
+int ipa3_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
+void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
+	struct ipahal_imm_cmd_pyld *cmd_pyld);
 #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 6d82da2..d69d6ae 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_interrupts.c
@@ -336,7 +336,7 @@
 	ipa3_process_interrupts(true);
 	IPADBG_LOW("Exit\n");
 
-	ipa3_dec_client_disable_clks(&log_info);
+	ipa3_dec_client_disable_clks_no_block(&log_info);
 	return IRQ_HANDLED;
 }
 /**
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index 2bd7b79..40ef59a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -221,10 +221,11 @@
 	int result = -EINVAL;
 
 	if (lookup == NULL) {
-		IPAERR("invalid param lookup=%p\n", lookup);
+		IPAERR_RL("invalid param lookup=%p\n", lookup);
 		return result;
 	}
 
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	if (strnlen(lookup->name, IPA_RESOURCE_NAME_MAX) ==
 			IPA_RESOURCE_NAME_MAX) {
 		IPAERR_RL("Interface name too long. (%s)\n", lookup->name);
@@ -267,6 +268,7 @@
 		return result;
 	}
 
+	tx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	if (strnlen(tx->name, IPA_RESOURCE_NAME_MAX) == IPA_RESOURCE_NAME_MAX) {
 		IPAERR_RL("Interface name too long. (%s)\n", tx->name);
 		return result;
@@ -314,6 +316,7 @@
 		return result;
 	}
 
+	rx->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	if (strnlen(rx->name, IPA_RESOURCE_NAME_MAX) == IPA_RESOURCE_NAME_MAX) {
 		IPAERR_RL("Interface name too long. (%s)\n", rx->name);
 		return result;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
index 08f2a8c..cb970ba 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_mhi.c
@@ -356,6 +356,14 @@
 		return -EINVAL;
 	}
 
+	if ((IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) >
+		((ipa3_ctx->mhi_evid_limits[1] -
+		ipa3_ctx->mhi_evid_limits[0]) + 1)) {
+		IPAERR("Not enough event rings for MHI\n");
+		ipa_assert();
+		return -EINVAL;
+	}
+
 	/* Initialize IPA MHI engine */
 	gsi_ep_info = ipa3_get_gsi_ep_info(IPA_CLIENT_MHI_PROD);
 	if (!gsi_ep_info) {
@@ -407,6 +415,8 @@
 		return -EINVAL;
 	}
 
+	in->start.gsi.evchid += ipa3_ctx->mhi_evid_limits[0];
+
 	client = in->sys->client;
 	ipa_ep_idx = ipa3_get_ep_mapping(client);
 	if (ipa_ep_idx == -1) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
index 958fc6c..c2daa05 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_nat.c
@@ -18,23 +18,29 @@
 #include <linux/uaccess.h>
 #include "ipa_i.h"
 #include "ipahal/ipahal.h"
+#include "ipahal/ipahal_nat.h"
 
 #define IPA_NAT_PHYS_MEM_OFFSET  0
+#define IPA_IPV6CT_PHYS_MEM_OFFSET  0
 #define IPA_NAT_PHYS_MEM_SIZE  IPA_RAM_NAT_SIZE
+#define IPA_IPV6CT_PHYS_MEM_SIZE  IPA_RAM_IPV6CT_SIZE
 
-#define IPA_NAT_TEMP_MEM_SIZE 128
+#define IPA_NAT_IPV6CT_TEMP_MEM_SIZE 128
 
-enum nat_table_type {
+#define IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC 3
+#define IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC 2
+#define IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC 4
+
+enum ipa_nat_ipv6ct_table_type {
 	IPA_NAT_BASE_TBL = 0,
 	IPA_NAT_EXPN_TBL = 1,
 	IPA_NAT_INDX_TBL = 2,
 	IPA_NAT_INDEX_EXPN_TBL = 3,
+	IPA_IPV6CT_BASE_TBL = 4,
+	IPA_IPV6CT_EXPN_TBL = 5
 };
 
-#define NAT_TABLE_ENTRY_SIZE_BYTE 32
-#define NAT_INTEX_TABLE_ENTRY_SIZE_BYTE 4
-
-static int ipa3_nat_vma_fault_remap(
+static int ipa3_nat_ipv6ct_vma_fault_remap(
 	 struct vm_area_struct *vma, struct vm_fault *vmf)
 {
 	IPADBG("\n");
@@ -44,193 +50,340 @@
 }
 
 /* VMA related file operations functions */
-static struct vm_operations_struct ipa3_nat_remap_vm_ops = {
-	.fault = ipa3_nat_vma_fault_remap,
+static const struct vm_operations_struct ipa3_nat_ipv6ct_remap_vm_ops = {
+	.fault = ipa3_nat_ipv6ct_vma_fault_remap,
 };
 
-static int ipa3_nat_open(struct inode *inode, struct file *filp)
+static int ipa3_nat_ipv6ct_open(struct inode *inode, struct file *filp)
 {
-	struct ipa3_nat_mem *nat_ctx;
+	struct ipa3_nat_ipv6ct_common_mem *dev;
 
 	IPADBG("\n");
-	nat_ctx = container_of(inode->i_cdev, struct ipa3_nat_mem, cdev);
-	filp->private_data = nat_ctx;
+	dev = container_of(inode->i_cdev,
+		struct ipa3_nat_ipv6ct_common_mem, cdev);
+	filp->private_data = dev;
 	IPADBG("return\n");
 
 	return 0;
 }
 
-static int ipa3_nat_mmap(struct file *filp, struct vm_area_struct *vma)
+static int ipa3_nat_ipv6ct_mmap(struct file *filp, struct vm_area_struct *vma)
 {
+	struct ipa3_nat_ipv6ct_common_mem *dev =
+		(struct ipa3_nat_ipv6ct_common_mem *)filp->private_data;
 	unsigned long vsize = vma->vm_end - vma->vm_start;
-	struct ipa3_nat_mem *nat_ctx =
-		(struct ipa3_nat_mem *)filp->private_data;
 	unsigned long phys_addr;
-	int result;
+	int result = 0;
 
-	mutex_lock(&nat_ctx->lock);
-	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
-	if (nat_ctx->is_sys_mem) {
-		IPADBG("Mapping system memory\n");
-		if (nat_ctx->is_mapped) {
-			IPAERR("mapping already exists, only 1 supported\n");
+	IPADBG("\n");
+
+	if (!dev->is_dev_init) {
+		IPAERR("attempt to mmap %s before dev init\n", dev->name);
+		return -EPERM;
+	}
+
+	mutex_lock(&dev->lock);
+	if (!dev->is_mem_allocated) {
+		IPAERR_RL("attempt to mmap %s before the memory allocation\n",
+			dev->name);
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (dev->is_sys_mem) {
+		if (dev->is_mapped) {
+			IPAERR("%s already mapped, only 1 mapping supported\n",
+				dev->name);
 			result = -EINVAL;
 			goto bail;
 		}
-		IPADBG("map sz=0x%zx\n", nat_ctx->size);
+	} else {
+		if ((dev->phys_mem_size == 0) || (vsize > dev->phys_mem_size)) {
+			IPAERR_RL("wrong parameters to %s mapping\n",
+				dev->name);
+			result = -EINVAL;
+			goto bail;
+		}
+	}
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	if (dev->is_sys_mem) {
+		IPADBG("Mapping system memory\n");
+		IPADBG("map sz=0x%zx\n", dev->size);
 		result =
 			dma_mmap_coherent(
-				 ipa3_ctx->pdev, vma,
-				 nat_ctx->vaddr, nat_ctx->dma_handle,
-				 nat_ctx->size);
-
+				ipa3_ctx->pdev, vma,
+				dev->vaddr, dev->dma_handle,
+				dev->size);
 		if (result) {
 			IPAERR("unable to map memory. Err:%d\n", result);
 			goto bail;
 		}
-		ipa3_ctx->nat_mem.nat_base_address = nat_ctx->vaddr;
+		dev->base_address = dev->vaddr;
 	} else {
 		IPADBG("Mapping shared(local) memory\n");
 		IPADBG("map sz=0x%lx\n", vsize);
 
-		if ((IPA_NAT_PHYS_MEM_SIZE == 0) ||
-				(vsize > IPA_NAT_PHYS_MEM_SIZE)) {
-			result = -EINVAL;
-			goto bail;
-		}
 		phys_addr = ipa3_ctx->ipa_wrapper_base +
 			ipa3_ctx->ctrl->ipa_reg_base_ofst +
 			ipahal_get_reg_n_ofst(IPA_SRAM_DIRECT_ACCESS_n,
-			IPA_NAT_PHYS_MEM_OFFSET);
+				dev->smem_offset);
 
 		if (remap_pfn_range(
-			 vma, vma->vm_start,
-			 phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
+			vma, vma->vm_start,
+			phys_addr >> PAGE_SHIFT, vsize, vma->vm_page_prot)) {
 			IPAERR("remap failed\n");
 			result = -EAGAIN;
 			goto bail;
 		}
-		ipa3_ctx->nat_mem.nat_base_address = (void *)vma->vm_start;
+		dev->base_address = (void *)vma->vm_start;
 	}
-	nat_ctx->is_mapped = true;
-	vma->vm_ops = &ipa3_nat_remap_vm_ops;
-	IPADBG("return\n");
 	result = 0;
+	vma->vm_ops = &ipa3_nat_ipv6ct_remap_vm_ops;
+	dev->is_mapped = true;
+	IPADBG("return\n");
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&dev->lock);
 	return result;
 }
 
-static const struct file_operations ipa3_nat_fops = {
+static const struct file_operations ipa3_nat_ipv6ct_fops = {
 	.owner = THIS_MODULE,
-	.open = ipa3_nat_open,
-	.mmap = ipa3_nat_mmap
+	.open = ipa3_nat_ipv6ct_open,
+	.mmap = ipa3_nat_ipv6ct_mmap
 };
 
 /**
- * ipa3_allocate_temp_nat_memory() - Allocates temp nat memory
- *
- * Called during nat table delete
+ * ipa3_allocate_nat_ipv6ct_tmp_memory() - Allocates the NAT\IPv6CT temp memory
  */
-void ipa3_allocate_temp_nat_memory(void)
+static struct ipa3_nat_ipv6ct_tmp_mem *ipa3_nat_ipv6ct_allocate_tmp_memory(void)
 {
-	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
-	int gfp_flags = GFP_KERNEL | __GFP_ZERO;
-
-	nat_ctx->tmp_vaddr =
-		dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_TEMP_MEM_SIZE,
-				&nat_ctx->tmp_dma_handle, gfp_flags);
-
-	if (nat_ctx->tmp_vaddr == NULL) {
-		IPAERR("Temp Memory alloc failed\n");
-		nat_ctx->is_tmp_mem = false;
-		return;
-	}
-
-	nat_ctx->is_tmp_mem = true;
-	IPADBG("IPA NAT allocated temp memory successfully\n");
-}
-
-/**
- * ipa3_create_nat_device() - Create the NAT device
- *
- * Called during ipa init to create nat device
- *
- * Returns:	0 on success, negative on failure
- */
-int ipa3_create_nat_device(void)
-{
-	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
-	int result;
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 
 	IPADBG("\n");
 
-	mutex_lock(&nat_ctx->lock);
-	nat_ctx->class = class_create(THIS_MODULE, NAT_DEV_NAME);
-	if (IS_ERR(nat_ctx->class)) {
-		IPAERR("unable to create the class\n");
-		result = -ENODEV;
-		goto vaddr_alloc_fail;
+	tmp_mem = kzalloc(sizeof(*tmp_mem), GFP_KERNEL);
+	if (tmp_mem == NULL)
+		return NULL;
+
+	tmp_mem->vaddr =
+		dma_alloc_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+			&tmp_mem->dma_handle, gfp_flags);
+	if (tmp_mem->vaddr == NULL)
+		goto bail_tmp_mem;
+
+	IPADBG("IPA successfully allocated temp memory\n");
+	return tmp_mem;
+
+bail_tmp_mem:
+	kfree(tmp_mem);
+	return NULL;
+}
+
+static int ipa3_nat_ipv6ct_init_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	const char *name,
+	u32 phys_mem_size,
+	u32 smem_offset,
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem)
+{
+	int result;
+
+	IPADBG("Init %s\n", name);
+
+	if (strnlen(name, IPA_DEV_NAME_MAX_LEN) == IPA_DEV_NAME_MAX_LEN) {
+		IPAERR("device name is too long\n");
+		return -ENODEV;
 	}
-	result = alloc_chrdev_region(&nat_ctx->dev_num,
-					0,
-					1,
-					NAT_DEV_NAME);
+
+	strlcpy(dev->name, name, IPA_DEV_NAME_MAX_LEN);
+
+	dev->class = class_create(THIS_MODULE, name);
+	if (IS_ERR(dev->class)) {
+		IPAERR("unable to create the class for %s\n", name);
+		return -ENODEV;
+	}
+	result = alloc_chrdev_region(&dev->dev_num, 0, 1, name);
 	if (result) {
-		IPAERR("alloc_chrdev_region err.\n");
+		IPAERR("alloc_chrdev_region err. for %s\n", name);
 		result = -ENODEV;
 		goto alloc_chrdev_region_fail;
 	}
 
-	nat_ctx->dev =
-	   device_create(nat_ctx->class, NULL, nat_ctx->dev_num, nat_ctx,
-			"%s", NAT_DEV_NAME);
+	dev->dev = device_create(dev->class, NULL, dev->dev_num, NULL, name);
 
-	if (IS_ERR(nat_ctx->dev)) {
-		IPAERR("device_create err:%ld\n", PTR_ERR(nat_ctx->dev));
+	if (IS_ERR(dev->dev)) {
+		IPAERR("device_create err:%ld\n", PTR_ERR(dev->dev));
 		result = -ENODEV;
 		goto device_create_fail;
 	}
 
-	cdev_init(&nat_ctx->cdev, &ipa3_nat_fops);
-	nat_ctx->cdev.owner = THIS_MODULE;
-	nat_ctx->cdev.ops = &ipa3_nat_fops;
+	cdev_init(&dev->cdev, &ipa3_nat_ipv6ct_fops);
+	dev->cdev.owner = THIS_MODULE;
 
-	result = cdev_add(&nat_ctx->cdev, nat_ctx->dev_num, 1);
+	mutex_init(&dev->lock);
+	mutex_lock(&dev->lock);
+
+	result = cdev_add(&dev->cdev, dev->dev_num, 1);
 	if (result) {
 		IPAERR("cdev_add err=%d\n", -result);
 		goto cdev_add_fail;
 	}
-	IPADBG("ipa nat dev added successful. major:%d minor:%d\n",
-			MAJOR(nat_ctx->dev_num),
-			MINOR(nat_ctx->dev_num));
 
-	nat_ctx->is_dev = true;
-	ipa3_allocate_temp_nat_memory();
-	IPADBG("IPA NAT device created successfully\n");
-	result = 0;
-	goto bail;
+	dev->phys_mem_size = phys_mem_size;
+	dev->smem_offset = smem_offset;
+
+	dev->is_dev_init = true;
+	mutex_unlock(&dev->lock);
+
+	IPADBG("ipa dev %s added successful. major:%d minor:%d\n", name,
+		MAJOR(dev->dev_num), MINOR(dev->dev_num));
+	return 0;
 
 cdev_add_fail:
-	device_destroy(nat_ctx->class, nat_ctx->dev_num);
+	mutex_unlock(&dev->lock);
+	device_destroy(dev->class, dev->dev_num);
 device_create_fail:
-	unregister_chrdev_region(nat_ctx->dev_num, 1);
+	unregister_chrdev_region(dev->dev_num, 1);
 alloc_chrdev_region_fail:
-	class_destroy(nat_ctx->class);
-vaddr_alloc_fail:
-	if (nat_ctx->vaddr) {
-		IPADBG("Releasing system memory\n");
-		dma_free_coherent(
-			 ipa3_ctx->pdev, nat_ctx->size,
-			 nat_ctx->vaddr, nat_ctx->dma_handle);
-		nat_ctx->vaddr = NULL;
-		nat_ctx->dma_handle = 0;
-		nat_ctx->size = 0;
+	class_destroy(dev->class);
+	return result;
+}
+
+static void ipa3_nat_ipv6ct_destroy_device(
+	struct ipa3_nat_ipv6ct_common_mem *dev)
+{
+	IPADBG("\n");
+
+	mutex_lock(&dev->lock);
+
+	device_destroy(dev->class, dev->dev_num);
+	unregister_chrdev_region(dev->dev_num, 1);
+	class_destroy(dev->class);
+	dev->is_dev_init = false;
+
+	mutex_unlock(&dev->lock);
+
+	IPADBG("return\n");
+}
+
+/**
+ * ipa3_nat_ipv6ct_init_devices() - Initialize the NAT and IPv6CT devices
+ *
+ * Called during IPA init to create memory device
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_nat_ipv6ct_init_devices(void)
+{
+	struct ipa3_nat_ipv6ct_tmp_mem *tmp_mem;
+	int result;
+
+	IPADBG("\n");
+
+	/*
+	 * Allocate NAT/IPv6CT temporary memory. The memory is never deleted,
+	 * because provided to HW once NAT or IPv6CT table is deleted.
+	 * NULL is a legal value
+	 */
+	tmp_mem = ipa3_nat_ipv6ct_allocate_tmp_memory();
+
+	if (ipa3_nat_ipv6ct_init_device(
+		&ipa3_ctx->nat_mem.dev,
+		IPA_NAT_DEV_NAME,
+		IPA_NAT_PHYS_MEM_SIZE,
+		IPA_NAT_PHYS_MEM_OFFSET,
+		tmp_mem)) {
+		IPAERR("unable to create nat device\n");
+		result = -ENODEV;
+		goto fail_init_nat_dev;
 	}
 
-bail:
-	mutex_unlock(&nat_ctx->lock);
+	if ((ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) &&
+		ipa3_nat_ipv6ct_init_device(
+			&ipa3_ctx->ipv6ct_mem.dev,
+			IPA_IPV6CT_DEV_NAME,
+			IPA_IPV6CT_PHYS_MEM_SIZE,
+			IPA_IPV6CT_PHYS_MEM_OFFSET,
+			tmp_mem)) {
+		IPAERR("unable to create IPv6CT device\n");
+		result = -ENODEV;
+		goto fail_init_ipv6ct_dev;
+	}
 
+	return 0;
+
+fail_init_ipv6ct_dev:
+	ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+fail_init_nat_dev:
+	if (tmp_mem != NULL) {
+		dma_free_coherent(ipa3_ctx->pdev, IPA_NAT_IPV6CT_TEMP_MEM_SIZE,
+			tmp_mem->vaddr, tmp_mem->dma_handle);
+		kfree(tmp_mem);
+	}
+	return result;
+}
+
+/**
+ * ipa3_nat_ipv6ct_destroy_devices() - destroy the NAT and IPv6CT devices
+ *
+ * Called during IPA init to destroy nat device
+ */
+void ipa3_nat_ipv6ct_destroy_devices(void)
+{
+	ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->nat_mem.dev);
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+		ipa3_nat_ipv6ct_destroy_device(&ipa3_ctx->ipv6ct_mem.dev);
+}
+
+static int ipa3_nat_ipv6ct_allocate_mem(struct ipa3_nat_ipv6ct_common_mem *dev,
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
+	int result = 0;
+
+	IPADBG("passed memory size %zu for %s\n",
+		table_alloc->size, dev->name);
+
+	if (!dev->is_dev_init) {
+		IPAERR("%s hasn't been initialized\n", dev->name);
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (dev->is_mem_allocated) {
+		IPAERR("Memory already allocated\n");
+		result = 0;
+		goto bail;
+	}
+
+	if (!table_alloc->size) {
+		IPAERR_RL("Invalid Parameters\n");
+		result = -EPERM;
+		goto bail;
+	}
+
+	if (table_alloc->size > IPA_NAT_PHYS_MEM_SIZE) {
+		IPADBG("Allocating system memory\n");
+		dev->is_sys_mem = true;
+		dev->vaddr =
+		   dma_alloc_coherent(ipa3_ctx->pdev, table_alloc->size,
+			   &dev->dma_handle, gfp_flags);
+		if (dev->vaddr == NULL) {
+			IPAERR("memory alloc failed\n");
+			result = -ENOMEM;
+			goto bail;
+		}
+		dev->size = table_alloc->size;
+	} else {
+		IPADBG("using shared(local) memory\n");
+		dev->is_sys_mem = false;
+	}
+
+	IPADBG("return\n");
+
+bail:
 	return result;
 }
 
@@ -245,60 +398,51 @@
  */
 int ipa3_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
 {
+	int result;
+	struct ipa_ioc_nat_ipv6ct_table_alloc tmp;
+
+	tmp.size = mem->size;
+	tmp.offset = 0;
+
+	result = ipa3_allocate_nat_table(&tmp);
+	if (result)
+		goto bail;
+
+	mem->offset = tmp.offset;
+
+bail:
+	return result;
+}
+
+/**
+ * ipa3_allocate_nat_table() - Allocates memory for the NAT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by NAT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
 	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
 	gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 	int result;
 
-	IPADBG("passed memory size %zu\n", mem->size);
+	IPADBG("\n");
 
-	mutex_lock(&nat_ctx->lock);
-	if (strcmp(mem->dev_name, NAT_DEV_NAME)) {
-		IPAERR_RL("Nat device name mismatch\n");
-		IPAERR_RL("Expect: %s Recv: %s\n", NAT_DEV_NAME, mem->dev_name);
-		result = -EPERM;
+	mutex_lock(&nat_ctx->dev.lock);
+
+	result = ipa3_nat_ipv6ct_allocate_mem(&nat_ctx->dev, table_alloc);
+	if (result)
 		goto bail;
-	}
 
-	if (nat_ctx->is_dev != true) {
-		IPAERR("Nat device not created successfully during boot up\n");
-		result = -EPERM;
-		goto bail;
-	}
-
-	if (nat_ctx->is_dev_init == true) {
-		IPAERR("Device already init\n");
-		result = 0;
-		goto bail;
-	}
-
-	if (mem->size <= 0 ||
-			nat_ctx->is_dev_init == true) {
-		IPAERR_RL("Invalid Parameters or device is already init\n");
-		result = -EPERM;
-		goto bail;
-	}
-
-	if (mem->size > IPA_NAT_PHYS_MEM_SIZE) {
-		IPADBG("Allocating system memory\n");
-		nat_ctx->is_sys_mem = true;
-		nat_ctx->vaddr =
-		   dma_alloc_coherent(ipa3_ctx->pdev, mem->size,
-				   &nat_ctx->dma_handle, gfp_flags);
-		if (nat_ctx->vaddr == NULL) {
-			IPAERR("memory alloc failed\n");
-			result = -ENOMEM;
-			goto bail;
-		}
-		nat_ctx->size = mem->size;
-	} else {
-		IPADBG("using shared(local) memory\n");
-		nat_ctx->is_sys_mem = false;
-	}
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		struct ipa_pdn_entry *pdn_entries;
-		struct ipa_mem_buffer *pdn_mem = &ipa3_ctx->nat_mem.pdn_mem;
+		size_t pdn_entry_size;
+		struct ipa_mem_buffer *pdn_mem = &nat_ctx->pdn_mem;
 
-		pdn_mem->size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
+		ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+		pdn_mem->size = pdn_entry_size * IPA_MAX_PDN_NUM;
 		if (IPA_MEM_PART(pdn_config_size) < pdn_mem->size) {
 			IPAERR(
 				"number of PDN entries exceeds SRAM available space\n");
@@ -315,25 +459,346 @@
 			result = -ENOMEM;
 			goto fail_alloc_pdn;
 		}
-		pdn_entries = pdn_mem->base;
-		memset(pdn_entries, 0, pdn_mem->size);
+		memset(pdn_mem->base, 0, pdn_mem->size);
 		IPADBG("IPA NAT dev allocated PDN memory successfully\n");
 	}
 
-	nat_ctx->is_dev_init = true;
+	nat_ctx->dev.is_mem_allocated = true;
 	IPADBG("IPA NAT dev init successfully\n");
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
+
+	IPADBG("return\n");
 
 	return 0;
 
 fail_alloc_pdn:
-	if (nat_ctx->vaddr) {
-		dma_free_coherent(ipa3_ctx->pdev, mem->size, nat_ctx->vaddr,
-			nat_ctx->dma_handle);
-		nat_ctx->vaddr = NULL;
+	if (nat_ctx->dev.vaddr) {
+		dma_free_coherent(ipa3_ctx->pdev, table_alloc->size,
+			nat_ctx->dev.vaddr, nat_ctx->dev.dma_handle);
+		nat_ctx->dev.vaddr = NULL;
 	}
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
+
+	return result;
+}
+
+/**
+ * ipa3_allocate_ipv6ct_table() - Allocates memory for the IPv6CT table
+ * @table_alloc: [in/out] memory parameters
+ *
+ * Called by IPv6CT client to allocate memory for the table entries.
+ * Based on the request size either shared or system memory will be used.
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	int result;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+	result = ipa3_nat_ipv6ct_allocate_mem(
+		&ipa3_ctx->ipv6ct_mem.dev, table_alloc);
+	if (result)
+		goto bail;
+
+	ipa3_ctx->ipv6ct_mem.dev.is_mem_allocated = true;
+	IPADBG("IPA IPv6CT dev init successfully\n");
+
+bail:
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+	return result;
+}
+
+static int ipa3_nat_ipv6ct_check_table_params(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	uint32_t offset, uint16_t entries_num,
+	enum ipahal_nat_type nat_type)
+{
+	int result;
+	size_t entry_size, table_size;
+
+	result = ipahal_nat_entry_size(nat_type, &entry_size);
+	if (result) {
+		IPAERR("Failed to retrieve size of entry for %s\n",
+			ipahal_nat_type_str(nat_type));
+		return result;
+	}
+	table_size = entry_size * entries_num;
+
+	/* check for integer overflow */
+	if (offset > UINT_MAX - table_size) {
+		IPAERR_RL("Detected overflow\n");
+		return -EPERM;
+	}
+
+	/* Check offset is not beyond allocated size */
+	if (dev->size < offset + table_size) {
+		IPAERR_RL("Table offset not valid\n");
+		IPAERR_RL("offset:%d entries:%d table_size:%zu mem_size:%zu\n",
+			offset, entries_num, table_size, dev->size);
+		return -EPERM;
+	}
+
+	if (dev->is_sys_mem && offset > UINT_MAX - dev->dma_handle) {
+		IPAERR_RL("Failed due to integer overflow\n");
+		IPAERR_RL("%s dma_handle: 0x%pa offset: 0x%x\n",
+			dev->name, &dev->dma_handle, offset);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+static inline void ipa3_nat_ipv6ct_create_init_cmd(
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd,
+	bool is_shared,
+	dma_addr_t base_addr,
+	uint8_t tbl_index,
+	uint32_t base_table_offset,
+	uint32_t expn_table_offset,
+	uint16_t table_entries,
+	uint16_t expn_table_entries,
+	const char *table_name)
+{
+	table_init_cmd->base_table_addr_shared = is_shared;
+	table_init_cmd->expansion_table_addr_shared = is_shared;
+
+	table_init_cmd->base_table_addr = base_addr + base_table_offset;
+	IPADBG("%s base table offset:0x%x\n", table_name, base_table_offset);
+
+	table_init_cmd->expansion_table_addr = base_addr + expn_table_offset;
+	IPADBG("%s expn table offset:0x%x\n", table_name, expn_table_offset);
+
+	table_init_cmd->table_index = tbl_index;
+	IPADBG("%s table index:0x%x\n", table_name, tbl_index);
+
+	table_init_cmd->size_base_table = table_entries;
+	IPADBG("%s base table size:0x%x\n", table_name, table_entries);
+
+	table_init_cmd->size_expansion_table = expn_table_entries;
+	IPADBG("%s expansion table size:0x%x\n",
+		table_name, expn_table_entries);
+}
+
+static inline void ipa3_nat_ipv6ct_init_device_structure(
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	uint32_t base_table_offset,
+	uint32_t expn_table_offset,
+	uint16_t table_entries,
+	uint16_t expn_table_entries)
+{
+	dev->base_table_addr = (char *)dev->base_address + base_table_offset;
+	IPADBG("%s base_table_addr: 0x%p\n", dev->name, dev->base_table_addr);
+
+	dev->expansion_table_addr =
+		(char *)dev->base_address + expn_table_offset;
+	IPADBG("%s expansion_table_addr: 0x%p\n",
+		dev->name, dev->expansion_table_addr);
+
+	IPADBG("%s table_entries: %d\n", dev->name, table_entries);
+	dev->table_entries = table_entries;
+
+	IPADBG("%s expn_table_entries: %d\n", dev->name, expn_table_entries);
+	dev->expn_table_entries = expn_table_entries;
+}
+
+static void ipa3_nat_create_init_cmd(
+	struct ipa_ioc_v4_nat_init *init,
+	bool is_shared,
+	dma_addr_t base_addr,
+	struct ipahal_imm_cmd_ip_v4_nat_init *cmd)
+{
+	IPADBG("\n");
+
+	ipa3_nat_ipv6ct_create_init_cmd(
+		&cmd->table_init,
+		is_shared,
+		base_addr,
+		init->tbl_index,
+		init->ipv4_rules_offset,
+		init->expn_rules_offset,
+		init->table_entries,
+		init->expn_table_entries,
+		ipa3_ctx->nat_mem.dev.name);
+
+	cmd->index_table_addr_shared = is_shared;
+	cmd->index_table_expansion_addr_shared = is_shared;
+
+	cmd->index_table_addr =
+		base_addr + init->index_offset;
+	IPADBG("index_offset:0x%x\n", init->index_offset);
+
+	cmd->index_table_expansion_addr =
+		base_addr + init->index_expn_offset;
+	IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		/*
+		 * starting IPAv4.0 public ip field changed to store the
+		 * PDN config table offset in SMEM
+		 */
+		cmd->public_addr_info = IPA_MEM_PART(pdn_config_ofst);
+		IPADBG("pdn config base:0x%x\n", cmd->public_addr_info);
+	} else {
+		cmd->public_addr_info = init->ip_addr;
+		IPADBG("Public IP address:%pI4h\n", &cmd->public_addr_info);
+	}
+
+	IPADBG("return\n");
+}
+
+static void ipa3_nat_create_modify_pdn_cmd(
+	struct ipahal_imm_cmd_dma_shared_mem *mem_cmd, bool zero_mem)
+{
+	size_t pdn_entry_size, mem_size;
+
+	IPADBG("\n");
+
+	ipahal_nat_entry_size(IPAHAL_NAT_IPV4_PDN, &pdn_entry_size);
+	mem_size = pdn_entry_size * IPA_MAX_PDN_NUM;
+
+	if (zero_mem)
+		memset(ipa3_ctx->nat_mem.pdn_mem.base, 0, mem_size);
+
+	/* Copy the PDN config table to SRAM */
+	mem_cmd->is_read = false;
+	mem_cmd->skip_pipeline_clear = false;
+	mem_cmd->pipeline_clear_options = IPAHAL_HPS_CLEAR;
+	mem_cmd->size = mem_size;
+	mem_cmd->system_addr = ipa3_ctx->nat_mem.pdn_mem.phys_base;
+	mem_cmd->local_addr = ipa3_ctx->smem_restricted_bytes +
+		IPA_MEM_PART(pdn_config_ofst);
+
+	IPADBG("return\n");
+}
+
+static int ipa3_nat_send_init_cmd(struct ipahal_imm_cmd_ip_v4_nat_init *cmd,
+	bool zero_pdn_table)
+{
+	struct ipa3_desc desc[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
+	struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC];
+	int i, num_cmd = 0, result;
+
+	IPADBG("\n");
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	cmd_pyld[num_cmd] =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR("failed to construct NOP imm cmd\n");
+		return -ENOMEM;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
+		IPA_IMM_CMD_IP_V4_NAT_INIT, cmd, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR_RL("fail to construct NAT init imm cmd\n");
+		result = -EPERM;
+		goto destroy_imm_cmd;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
+		struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
+
+		if (num_cmd >= IPA_NAT_MAX_NUM_OF_INIT_CMD_DESC) {
+			IPAERR("number of commands is out of range\n");
+			result = -ENOBUFS;
+			goto destroy_imm_cmd;
+		}
+
+		/* Copy the PDN config table to SRAM */
+		ipa3_nat_create_modify_pdn_cmd(&mem_cmd, zero_pdn_table);
+		cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
+			IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
+		if (!cmd_pyld[num_cmd]) {
+			IPAERR(
+				"fail construct dma_shared_mem cmd: for pdn table");
+			result = -ENOMEM;
+			goto destroy_imm_cmd;
+		}
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
+		IPADBG("added PDN table copy cmd\n");
+	}
+
+	result = ipa3_send_cmd(num_cmd, desc);
+	if (result) {
+		IPAERR("fail to send NAT init immediate command\n");
+		goto destroy_imm_cmd;
+	}
+
+	IPADBG("return\n");
+
+destroy_imm_cmd:
+	for (i = 0; i < num_cmd; ++i)
+		ipahal_destroy_imm_cmd(cmd_pyld[i]);
+
+	return result;
+}
+
+static int ipa3_ipv6ct_send_init_cmd(struct ipahal_imm_cmd_ip_v6_ct_init *cmd)
+{
+	struct ipa3_desc desc[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+	struct ipahal_imm_cmd_pyld
+		*cmd_pyld[IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC];
+	int i, num_cmd = 0, result;
+
+	IPADBG("\n");
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	cmd_pyld[num_cmd] =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR("failed to construct NOP imm cmd\n");
+		return -ENOMEM;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	if (num_cmd >= IPA_IPV6CT_MAX_NUM_OF_INIT_CMD_DESC) {
+		IPAERR("number of commands is out of range\n");
+		result = -ENOBUFS;
+		goto destroy_imm_cmd;
+	}
+
+	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
+		IPA_IMM_CMD_IP_V6_CT_INIT, cmd, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR_RL("fail to construct IPv6CT init imm cmd\n");
+		result = -EPERM;
+		goto destroy_imm_cmd;
+	}
+
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	result = ipa3_send_cmd(num_cmd, desc);
+	if (result) {
+		IPAERR("Fail to send IPv6CT init immediate command\n");
+		goto destroy_imm_cmd;
+	}
+
+	IPADBG("return\n");
+
+destroy_imm_cmd:
+	for (i = 0; i < num_cmd; ++i)
+		ipahal_destroy_imm_cmd(cmd_pyld[i]);
 
 	return result;
 }
@@ -349,214 +814,79 @@
  */
 int ipa3_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
 {
-#define TBL_ENTRY_SIZE 32
-#define INDX_TBL_ENTRY_SIZE 4
-
-	struct ipa3_desc desc[3];
 	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
-	int num_cmd = 0;
-	int i = 0;
-	struct ipahal_imm_cmd_pyld *cmd_pyld[3];
-	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
-	int result = 0;
-	u32 offset = 0;
-	size_t tmp;
+	int result;
 
 	IPADBG("\n");
+
+	if (!ipa3_ctx->nat_mem.dev.is_mapped) {
+		IPAERR_RL("attempt to init %s before mmap\n",
+			ipa3_ctx->nat_mem.dev.name);
+		return -EPERM;
+	}
+
+	if (init->tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+		return -EPERM;
+	}
+
 	if (init->table_entries == 0) {
-		IPADBG("Table entries is zero\n");
+		IPAERR_RL("Table entries is zero\n");
 		return -EPERM;
 	}
 
-	/* check for integer overflow */
-	if (init->ipv4_rules_offset >
-		UINT_MAX - (TBL_ENTRY_SIZE * (init->table_entries + 1))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->ipv4_rules_offset +
-		(TBL_ENTRY_SIZE * (init->table_entries + 1));
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->ipv4_rules_offset, (init->table_entries + 1),
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->ipv4_rules_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV4);
+	if (result) {
+		IPAERR_RL("Bad params for NAT base table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->expn_rules_offset >
-		(UINT_MAX - (TBL_ENTRY_SIZE * init->expn_table_entries))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Expn Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->expn_rules_offset +
-		(TBL_ENTRY_SIZE * init->expn_table_entries);
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Expn Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->expn_rules_offset, init->expn_table_entries,
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->expn_rules_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV4);
+	if (result) {
+		IPAERR_RL("Bad params for NAT expansion table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->index_offset >
-		UINT_MAX - (INDX_TBL_ENTRY_SIZE * (init->table_entries + 1))) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Indx Table Entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->index_offset +
-		(INDX_TBL_ENTRY_SIZE * (init->table_entries + 1));
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Indx Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->index_offset, (init->table_entries + 1),
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->index_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV4_INDEX);
+	if (result) {
+		IPAERR_RL("Bad params for index table\n");
+		return result;
 	}
 
-	/* check for integer overflow */
-	if (init->index_expn_offset >
-		UINT_MAX - (INDX_TBL_ENTRY_SIZE * init->expn_table_entries)) {
-		IPAERR_RL("Detected overflow\n");
-		return -EPERM;
-	}
-	/* Check Expn Table entry offset is not
-	 * beyond allocated size
-	 */
-	tmp = init->index_expn_offset +
-		(INDX_TBL_ENTRY_SIZE * init->expn_table_entries);
-	if (tmp > ipa3_ctx->nat_mem.size) {
-		IPAERR_RL("Indx Expn Table rules offset not valid\n");
-		IPAERR_RL("offset:%d entrys:%d size:%zu mem_size:%zu\n",
-			init->index_expn_offset, init->expn_table_entries,
-			tmp, ipa3_ctx->nat_mem.size);
-		return -EPERM;
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->nat_mem.dev,
+		init->index_expn_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV4_INDEX);
+	if (result) {
+		IPAERR_RL("Bad params for index expansion table\n");
+		return result;
 	}
 
-	memset(&desc, 0, sizeof(desc));
-	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	cmd_pyld[num_cmd] =
-		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!cmd_pyld[num_cmd]) {
-		IPAERR("failed to construct NOP imm cmd\n");
-		result = -ENOMEM;
-		goto bail;
-	}
-
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
-	desc[num_cmd].callback = NULL;
-	desc[num_cmd].user1 = NULL;
-	desc[num_cmd].user2 = 0;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	num_cmd++;
-
-	if (ipa3_ctx->nat_mem.vaddr) {
+	if (ipa3_ctx->nat_mem.dev.is_sys_mem) {
 		IPADBG("using system memory for nat table\n");
-		cmd.ipv4_rules_addr_shared = false;
-		cmd.ipv4_expansion_rules_addr_shared = false;
-		cmd.index_table_addr_shared = false;
-		cmd.index_table_expansion_addr_shared = false;
-
-		offset = UINT_MAX - ipa3_ctx->nat_mem.dma_handle;
-
-		if ((init->ipv4_rules_offset > offset) ||
-				(init->expn_rules_offset > offset) ||
-				(init->index_offset > offset) ||
-				(init->index_expn_offset > offset)) {
-			IPAERR_RL("Failed due to integer overflow\n");
-			IPAERR_RL("nat.mem.dma_handle: 0x%pa\n",
-				&ipa3_ctx->nat_mem.dma_handle);
-			IPAERR_RL("ipv4_rules_offset: 0x%x\n",
-				init->ipv4_rules_offset);
-			IPAERR_RL("expn_rules_offset: 0x%x\n",
-				init->expn_rules_offset);
-			IPAERR_RL("index_offset: 0x%x\n",
-				init->index_offset);
-			IPAERR_RL("index_expn_offset: 0x%x\n",
-				init->index_expn_offset);
-			result = -EPERM;
-			goto destroy_imm_cmd;
-		}
-		cmd.ipv4_rules_addr =
-			ipa3_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
-		IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
-
-		cmd.ipv4_expansion_rules_addr =
-		   ipa3_ctx->nat_mem.dma_handle + init->expn_rules_offset;
-		IPADBG("expn_rules_offset:0x%x\n", init->expn_rules_offset);
-
-		cmd.index_table_addr =
-			ipa3_ctx->nat_mem.dma_handle + init->index_offset;
-		IPADBG("index_offset:0x%x\n", init->index_offset);
-
-		cmd.index_table_expansion_addr =
-		   ipa3_ctx->nat_mem.dma_handle + init->index_expn_offset;
-		IPADBG("index_expn_offset:0x%x\n", init->index_expn_offset);
+		/*
+		 * Safe to process, since integer overflow was
+		 * checked in ipa3_nat_ipv6ct_check_table_params
+		 */
+		ipa3_nat_create_init_cmd(init, false,
+			ipa3_ctx->nat_mem.dev.dma_handle, &cmd);
 	} else {
 		IPADBG("using shared(local) memory for nat table\n");
-		cmd.ipv4_rules_addr_shared = true;
-		cmd.ipv4_expansion_rules_addr_shared = true;
-		cmd.index_table_addr_shared = true;
-		cmd.index_table_expansion_addr_shared = true;
-
-		cmd.ipv4_rules_addr = init->ipv4_rules_offset +
-				IPA_RAM_NAT_OFST;
-
-		cmd.ipv4_expansion_rules_addr = init->expn_rules_offset +
-				IPA_RAM_NAT_OFST;
-
-		cmd.index_table_addr = init->index_offset  +
-				IPA_RAM_NAT_OFST;
-
-		cmd.index_table_expansion_addr = init->index_expn_offset +
-				IPA_RAM_NAT_OFST;
+		ipa3_nat_create_init_cmd(init, true, IPA_RAM_NAT_OFST, &cmd);
 	}
-	cmd.table_index = init->tbl_index;
-	IPADBG("Table index:0x%x\n", cmd.table_index);
-	cmd.size_base_tables = init->table_entries;
-	IPADBG("Base Table size:0x%x\n", cmd.size_base_tables);
-	cmd.size_expansion_tables = init->expn_table_entries;
-	IPADBG("Expansion Table size:0x%x\n", cmd.size_expansion_tables);
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		/*
-		 * public ip field changed to store the PDN config base
-		 * address in IPAv4
-		 */
-		cmd.public_ip_addr = IPA_MEM_PART(pdn_config_ofst);
-		IPADBG("pdn config base:0x%x\n", cmd.public_ip_addr);
-	} else {
-		cmd.public_ip_addr = init->ip_addr;
-		IPADBG("Public ip address:0x%x\n", cmd.public_ip_addr);
-	}
-	cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
-		IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
-	if (!cmd_pyld[num_cmd]) {
-		IPAERR_RL("Fail to construct ip_v4_nat_init imm cmd\n");
-		result = -EPERM;
-		goto destroy_imm_cmd;
-	}
-
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
-	desc[num_cmd].callback = NULL;
-	desc[num_cmd].user1 = NULL;
-	desc[num_cmd].user2 = 0;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	num_cmd++;
 
 	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
 		struct ipa_pdn_entry *pdn_entries;
@@ -569,87 +899,154 @@
 		pdn_entries[0].resrvd = 0;
 
 		IPADBG("Public ip address:0x%x\n", init->ip_addr);
-
-		/* Copy the PDN config table to SRAM */
-		mem_cmd.is_read = false;
-		mem_cmd.skip_pipeline_clear = false;
-		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
-		mem_cmd.size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
-		mem_cmd.system_addr = ipa3_ctx->nat_mem.pdn_mem.phys_base;
-		mem_cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
-			IPA_MEM_PART(pdn_config_ofst);
-		cmd_pyld[num_cmd] = ipahal_construct_imm_cmd(
-			IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
-		if (!cmd_pyld[num_cmd]) {
-			IPAERR(
-			"fail construct dma_shared_mem cmd: for pdn table");
-			result = -ENOMEM;
-			goto destroy_imm_cmd;
-		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
-		desc[num_cmd].callback = NULL;
-		desc[num_cmd].user1 = NULL;
-		desc[num_cmd].user2 = 0;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		num_cmd++;
-		IPADBG("added PDN table copy cmd\n");
 	}
 
-	IPADBG("posting v4 init command\n");
-	if (ipa3_send_cmd(num_cmd, desc)) {
-		IPAERR("Fail to send immediate command\n");
-		result = -EPERM;
-		goto destroy_imm_cmd;
+	IPADBG("posting NAT init command\n");
+	result = ipa3_nat_send_init_cmd(&cmd, false);
+	if (result) {
+		IPAERR("Fail to send NAT init immediate command\n");
+		return result;
 	}
 
+	ipa3_nat_ipv6ct_init_device_structure(
+		&ipa3_ctx->nat_mem.dev,
+		init->ipv4_rules_offset,
+		init->expn_rules_offset,
+		init->table_entries,
+		init->expn_table_entries);
+
 	ipa3_ctx->nat_mem.public_ip_addr = init->ip_addr;
-	IPADBG("Table ip address:0x%x", ipa3_ctx->nat_mem.public_ip_addr);
-
-	ipa3_ctx->nat_mem.ipv4_rules_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->ipv4_rules_offset;
-	IPADBG("ipv4_rules_addr: 0x%p\n",
-				 ipa3_ctx->nat_mem.ipv4_rules_addr);
-
-	ipa3_ctx->nat_mem.ipv4_expansion_rules_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->expn_rules_offset;
-	IPADBG("ipv4_expansion_rules_addr: 0x%p\n",
-				 ipa3_ctx->nat_mem.ipv4_expansion_rules_addr);
+	IPADBG("Public IP address:%pI4h\n", &ipa3_ctx->nat_mem.public_ip_addr);
 
 	ipa3_ctx->nat_mem.index_table_addr =
-		 (char *)ipa3_ctx->nat_mem.nat_base_address +
+		 (char *)ipa3_ctx->nat_mem.dev.base_address +
 		 init->index_offset;
 	IPADBG("index_table_addr: 0x%p\n",
 				 ipa3_ctx->nat_mem.index_table_addr);
 
 	ipa3_ctx->nat_mem.index_table_expansion_addr =
-	 (char *)ipa3_ctx->nat_mem.nat_base_address + init->index_expn_offset;
+	 (char *)ipa3_ctx->nat_mem.dev.base_address + init->index_expn_offset;
 	IPADBG("index_table_expansion_addr: 0x%p\n",
 				 ipa3_ctx->nat_mem.index_table_expansion_addr);
 
-	IPADBG("size_base_tables: %d\n", init->table_entries);
-	ipa3_ctx->nat_mem.size_base_tables  = init->table_entries;
-
-	IPADBG("size_expansion_tables: %d\n", init->expn_table_entries);
-	ipa3_ctx->nat_mem.size_expansion_tables = init->expn_table_entries;
-
+	ipa3_ctx->nat_mem.dev.is_hw_init = true;
 	IPADBG("return\n");
-destroy_imm_cmd:
-	for (i = 0; i < num_cmd; i++)
-		ipahal_destroy_imm_cmd(cmd_pyld[i]);
-bail:
-	return result;
+	return 0;
 }
 
 /**
-* ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
-* @mdfy_pdn:	[in] PDN info to be written to SRAM
-*
-* Called by NAT client driver to modify an entry in the PDN config table
-*
-* Returns:	0 on success, negative on failure
-*/
+ * ipa3_ipv6ct_init_cmd() - Post IP_V6_CONN_TRACK_INIT command to IPA HW
+ * @init:	[in] initialization command attributes
+ *
+ * Called by NAT client driver to post IP_V6_CONN_TRACK_INIT command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+	struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+	int result;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_mapped) {
+		IPAERR_RL("attempt to init %s before mmap\n",
+			ipa3_ctx->ipv6ct_mem.dev.name);
+		return -EPERM;
+	}
+
+	if (init->tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", init->tbl_index);
+		return -EPERM;
+	}
+
+	if (init->table_entries == 0) {
+		IPAERR_RL("Table entries is zero\n");
+		return -EPERM;
+	}
+
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->base_table_offset,
+		init->table_entries + 1,
+		IPAHAL_NAT_IPV6CT);
+	if (result) {
+		IPAERR_RL("Bad params for IPv6CT base table\n");
+		return result;
+	}
+
+	result = ipa3_nat_ipv6ct_check_table_params(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->expn_table_offset,
+		init->expn_table_entries,
+		IPAHAL_NAT_IPV6CT);
+	if (result) {
+		IPAERR_RL("Bad params for IPv6CT expansion table\n");
+		return result;
+	}
+
+	if (ipa3_ctx->ipv6ct_mem.dev.is_sys_mem) {
+		IPADBG("using system memory for nat table\n");
+		/*
+		 * Safe to process, since integer overflow was
+		 * checked in ipa3_nat_ipv6ct_check_table_params
+		 */
+		ipa3_nat_ipv6ct_create_init_cmd(
+			&cmd.table_init,
+			false,
+			ipa3_ctx->ipv6ct_mem.dev.dma_handle,
+			init->tbl_index,
+			init->base_table_offset,
+			init->expn_table_offset,
+			init->table_entries,
+			init->expn_table_entries,
+			ipa3_ctx->ipv6ct_mem.dev.name);
+	} else {
+		IPADBG("using shared(local) memory for nat table\n");
+		ipa3_nat_ipv6ct_create_init_cmd(
+			&cmd.table_init,
+			true,
+			IPA_RAM_IPV6CT_OFST,
+			init->tbl_index,
+			init->base_table_offset,
+			init->expn_table_offset,
+			init->table_entries,
+			init->expn_table_entries,
+			ipa3_ctx->ipv6ct_mem.dev.name);
+	}
+
+	IPADBG("posting ip_v6_ct_init imm command\n");
+	result = ipa3_ipv6ct_send_init_cmd(&cmd);
+	if (result) {
+		IPAERR("fail to send IPv6CT init immediate command\n");
+		return result;
+	}
+
+	ipa3_nat_ipv6ct_init_device_structure(
+		&ipa3_ctx->ipv6ct_mem.dev,
+		init->base_table_offset,
+		init->expn_table_offset,
+		init->table_entries,
+		init->expn_table_entries);
+
+	ipa3_ctx->ipv6ct_mem.dev.is_hw_init = true;
+	IPADBG("return\n");
+	return 0;
+}
+
+/**
+ * ipa3_nat_mdfy_pdn() - Modify a PDN entry in PDN config table in IPA SRAM
+ * @mdfy_pdn:	[in] PDN info to be written to SRAM
+ *
+ * Called by NAT client driver to modify an entry in the PDN config table
+ *
+ * Returns:	0 on success, negative on failure
+ */
 int ipa3_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
 {
 	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
@@ -659,21 +1056,24 @@
 	struct ipa3_nat_mem *nat_ctx = &(ipa3_ctx->nat_mem);
 	struct ipa_pdn_entry *pdn_entries = nat_ctx->pdn_mem.base;
 
+	IPADBG("\n");
+
 	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
-		IPAERR("IPA HW does not support multi PDN\n");
+		IPAERR_RL("IPA HW does not support multi PDN\n");
 		return -EPERM;
 	}
-	if (!nat_ctx->is_dev_init) {
-		IPAERR("attempt to modify a PDN entry before dev init\n");
+	if (!nat_ctx->dev.is_mem_allocated) {
+		IPAERR_RL(
+			"attempt to modify a PDN entry before the PDN table memory allocation\n");
 		return -EPERM;
 	}
 
 	if (mdfy_pdn->pdn_index > (IPA_MAX_PDN_NUM - 1)) {
-		IPAERR("pdn index out of range %d\n", mdfy_pdn->pdn_index);
+		IPAERR_RL("pdn index out of range %d\n", mdfy_pdn->pdn_index);
 		return -EPERM;
 	}
 
-	mutex_lock(&nat_ctx->lock);
+	mutex_lock(&nat_ctx->dev.lock);
 
 	/* store ip in pdn entries cache array */
 	pdn_entries[mdfy_pdn->pdn_index].public_ip =
@@ -683,21 +1083,13 @@
 	pdn_entries[mdfy_pdn->pdn_index].src_metadata =
 		mdfy_pdn->src_metadata;
 
-	IPADBG("Modify PDN in index %d: ", mdfy_pdn->pdn_index);
-	IPADBG("Public ip address:0x%x, ", mdfy_pdn->public_ip);
-	IPADBG("dst metadata:0x%x, ", mdfy_pdn->dst_metadata);
-	IPADBG("src metadata:0x%x\n", mdfy_pdn->src_metadata);
-
-	memset(&desc, 0, sizeof(desc));
+	IPADBG("Modify PDN in index: %d Public ip address:%pI4h\n",
+		mdfy_pdn->pdn_index, &mdfy_pdn->public_ip);
+	IPADBG("Modify PDN dst metadata: 0x%x src metadata: 0x%x\n",
+		mdfy_pdn->dst_metadata, mdfy_pdn->src_metadata);
 
 	/* Copy the PDN config table to SRAM */
-	mem_cmd.is_read = false;
-	mem_cmd.skip_pipeline_clear = false;
-	mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
-	mem_cmd.size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
-	mem_cmd.system_addr = nat_ctx->pdn_mem.phys_base;
-	mem_cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
-		IPA_MEM_PART(pdn_config_ofst);
+	ipa3_nat_create_modify_pdn_cmd(&mem_cmd, false);
 	cmd_pyld = ipahal_construct_imm_cmd(
 		IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
 	if (!cmd_pyld) {
@@ -706,25 +1098,199 @@
 		result = -ENOMEM;
 		goto bail;
 	}
-	desc.opcode = cmd_pyld->opcode;
-	desc.type = IPA_IMM_CMD_DESC;
-	desc.callback = NULL;
-	desc.user1 = NULL;
-	desc.user2 = 0;
-	desc.pyld = cmd_pyld->data;
-	desc.len = cmd_pyld->len;
+	ipa3_init_imm_cmd_desc(&desc, cmd_pyld);
 
-	IPADBG("sending PDN table copy cmd");
-	if (ipa3_send_cmd(1, &desc)) {
-		IPAERR("Fail to send immediate command\n");
-		result = -EPERM;
-	}
+	IPADBG("sending PDN table copy cmd\n");
+	result = ipa3_send_cmd(1, &desc);
+	if (result)
+		IPAERR("Fail to send PDN table copy immediate command\n");
 
 	ipahal_destroy_imm_cmd(cmd_pyld);
+
+	IPADBG("return\n");
+
 bail:
-	mutex_unlock(&nat_ctx->lock);
+	mutex_unlock(&nat_ctx->dev.lock);
 	return result;
 }
+
+static uint32_t ipa3_nat_ipv6ct_calculate_table_size(uint8_t base_addr)
+{
+	size_t entry_size;
+	u32 entries_num;
+	enum ipahal_nat_type nat_type;
+
+	switch (base_addr) {
+	case IPA_NAT_BASE_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV4;
+		break;
+	case IPA_NAT_EXPN_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV4;
+		break;
+	case IPA_NAT_INDX_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV4_INDEX;
+		break;
+	case IPA_NAT_INDEX_EXPN_TBL:
+		entries_num = ipa3_ctx->nat_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV4_INDEX;
+		break;
+	case IPA_IPV6CT_BASE_TBL:
+		entries_num = ipa3_ctx->ipv6ct_mem.dev.table_entries + 1;
+		nat_type = IPAHAL_NAT_IPV6CT;
+		break;
+	case IPA_IPV6CT_EXPN_TBL:
+		entries_num = ipa3_ctx->ipv6ct_mem.dev.expn_table_entries;
+		nat_type = IPAHAL_NAT_IPV6CT;
+		break;
+	default:
+		IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+			base_addr);
+		return 0;
+	}
+
+	ipahal_nat_entry_size(nat_type, &entry_size);
+	return entry_size * entries_num;
+}
+
+static int ipa3_table_validate_table_dma_one(struct ipa_ioc_nat_dma_one *param)
+{
+	uint32_t table_size;
+
+	if (param->table_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", param->table_index);
+		return -EPERM;
+	}
+
+	switch (param->base_addr) {
+	case IPA_NAT_BASE_TBL:
+	case IPA_NAT_EXPN_TBL:
+	case IPA_NAT_INDX_TBL:
+	case IPA_NAT_INDEX_EXPN_TBL:
+		if (!ipa3_ctx->nat_mem.dev.is_hw_init) {
+			IPAERR_RL("attempt to write to %s before HW int\n",
+				ipa3_ctx->nat_mem.dev.name);
+			return -EPERM;
+		}
+		break;
+	case IPA_IPV6CT_BASE_TBL:
+	case IPA_IPV6CT_EXPN_TBL:
+		if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+			IPAERR_RL("IPv6 connection tracking isn't supported\n");
+			return -EPERM;
+		}
+
+		if (!ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+			IPAERR_RL("attempt to write to %s before HW int\n",
+				ipa3_ctx->ipv6ct_mem.dev.name);
+			return -EPERM;
+		}
+		break;
+	default:
+		IPAERR_RL("Invalid base_addr %d for table DMA command\n",
+			param->base_addr);
+		return -EPERM;
+	}
+
+	table_size = ipa3_nat_ipv6ct_calculate_table_size(param->base_addr);
+	if (!table_size) {
+		IPAERR_RL("Failed to calculate table size for base_addr %d\n",
+			param->base_addr);
+		return -EPERM;
+	}
+
+	if (param->offset >= table_size) {
+		IPAERR_RL("Invalid offset %d for table DMA command\n",
+			param->offset);
+		IPAERR_RL("table_index %d base addr %d size %d\n",
+			param->table_index, param->base_addr, table_size);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+
+/**
+ * ipa3_table_dma_cmd() - Post TABLE_DMA command to IPA HW
+ * @dma:	[in] initialization command attributes
+ *
+ * Called by NAT/IPv6CT clients to post TABLE_DMA command to IPA HW
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	struct ipahal_imm_cmd_table_dma cmd;
+	enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
+	struct ipahal_imm_cmd_pyld *cmd_pyld[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+	struct ipa3_desc desc[IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC];
+	uint8_t cnt, num_cmd = 0;
+	int result = 0;
+
+	IPADBG("\n");
+	if (!dma->entries ||
+		dma->entries >= IPA_MAX_NUM_OF_TABLE_DMA_CMD_DESC) {
+		IPAERR_RL("Invalid number of entries %d\n",
+			dma->entries);
+		result = -EPERM;
+		goto bail;
+	}
+
+	for (cnt = 0; cnt < dma->entries; ++cnt) {
+		result = ipa3_table_validate_table_dma_one(&dma->dma[cnt]);
+		if (result) {
+			IPAERR_RL("Table DMA command parameter %d is invalid\n",
+				cnt);
+			goto bail;
+		}
+	}
+
+	/* NO-OP IC for ensuring that IPA pipeline is empty */
+	cmd_pyld[num_cmd] =
+		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
+	if (!cmd_pyld[num_cmd]) {
+		IPAERR("Failed to construct NOP imm cmd\n");
+		result = -ENOMEM;
+		goto destroy_imm_cmd;
+	}
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+	++num_cmd;
+
+	/* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
+		cmd_name = IPA_IMM_CMD_TABLE_DMA;
+
+	for (cnt = 0; cnt < dma->entries; ++cnt) {
+		cmd.table_index = dma->dma[cnt].table_index;
+		cmd.base_addr = dma->dma[cnt].base_addr;
+		cmd.offset = dma->dma[cnt].offset;
+		cmd.data = dma->dma[cnt].data;
+		cmd_pyld[num_cmd] =
+			ipahal_construct_imm_cmd(cmd_name, &cmd, false);
+		if (!cmd_pyld[num_cmd]) {
+			IPAERR_RL("Fail to construct table_dma imm cmd\n");
+			result = -ENOMEM;
+			goto destroy_imm_cmd;
+		}
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
+		++num_cmd;
+	}
+	result = ipa3_send_cmd(num_cmd, desc);
+	if (result)
+		IPAERR("Fail to send table_dma immediate command\n");
+
+	IPADBG("return\n");
+
+destroy_imm_cmd:
+	for (cnt = 0; cnt < num_cmd; ++cnt)
+		ipahal_destroy_imm_cmd(cmd_pyld[cnt]);
+bail:
+	return result;
+}
+
 /**
  * ipa3_nat_dma_cmd() - Post NAT_DMA command to IPA HW
  * @dma:	[in] initialization command attributes
@@ -735,221 +1301,138 @@
  */
 int ipa3_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
 {
-#define NUM_OF_DESC 2
-
-	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
-	struct ipahal_imm_cmd_nat_dma cmd;
-	enum ipahal_imm_cmd_name cmd_name = IPA_IMM_CMD_NAT_DMA;
-	struct ipahal_imm_cmd_pyld *cmd_pyld = NULL;
-	struct ipa3_desc *desc = NULL;
-	u16 size = 0, cnt = 0;
-	int ret = 0;
-
-	IPADBG("\n");
-	if (dma->entries <= 0) {
-		IPAERR_RL("Invalid number of commands %d\n",
-			dma->entries);
-		ret = -EPERM;
-		goto bail;
-	}
-
-	for (cnt = 0; cnt < dma->entries; cnt++) {
-		if (dma->dma[cnt].table_index >= 1) {
-			IPAERR_RL("Invalid table index %d\n",
-				dma->dma[cnt].table_index);
-			ret = -EPERM;
-			goto bail;
-		}
-
-		switch (dma->dma[cnt].base_addr) {
-		case IPA_NAT_BASE_TBL:
-			if (dma->dma[cnt].offset >=
-				(ipa3_ctx->nat_mem.size_base_tables + 1) *
-				NAT_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_EXPN_TBL:
-			if (dma->dma[cnt].offset >=
-				ipa3_ctx->nat_mem.size_expansion_tables *
-				NAT_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_INDX_TBL:
-			if (dma->dma[cnt].offset >=
-				(ipa3_ctx->nat_mem.size_base_tables + 1) *
-				NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		case IPA_NAT_INDEX_EXPN_TBL:
-			if (dma->dma[cnt].offset >=
-				ipa3_ctx->nat_mem.size_expansion_tables *
-				NAT_INTEX_TABLE_ENTRY_SIZE_BYTE) {
-				IPAERR_RL("Invalid offset %d\n",
-					dma->dma[cnt].offset);
-				ret = -EPERM;
-				goto bail;
-			}
-
-			break;
-
-		default:
-			IPAERR_RL("Invalid base_addr %d\n",
-				dma->dma[cnt].base_addr);
-			ret = -EPERM;
-			goto bail;
-		}
-	}
-
-	size = sizeof(struct ipa3_desc) * NUM_OF_DESC;
-	desc = kzalloc(size, GFP_KERNEL);
-	if (desc == NULL) {
-		IPAERR("Failed to alloc memory\n");
-		ret = -ENOMEM;
-		goto bail;
-	}
-
-	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	nop_cmd_pyld =
-		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!nop_cmd_pyld) {
-		IPAERR("Failed to construct NOP imm cmd\n");
-		ret = -ENOMEM;
-		goto bail;
-	}
-	desc[0].type = IPA_IMM_CMD_DESC;
-	desc[0].opcode = nop_cmd_pyld->opcode;
-	desc[0].callback = NULL;
-	desc[0].user1 = NULL;
-	desc[0].user2 = 0;
-	desc[0].pyld = nop_cmd_pyld->data;
-	desc[0].len = nop_cmd_pyld->len;
-
-	/* NAT_DMA was renamed to TABLE_DMA starting from IPAv4 */
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0)
-		cmd_name = IPA_IMM_CMD_TABLE_DMA;
-
-	for (cnt = 0; cnt < dma->entries; cnt++) {
-		cmd.table_index = dma->dma[cnt].table_index;
-		cmd.base_addr = dma->dma[cnt].base_addr;
-		cmd.offset = dma->dma[cnt].offset;
-		cmd.data = dma->dma[cnt].data;
-		cmd_pyld = ipahal_construct_imm_cmd(cmd_name, &cmd, false);
-		if (!cmd_pyld) {
-			IPAERR_RL("Fail to construct nat_dma imm cmd\n");
-			continue;
-		}
-		desc[1].type = IPA_IMM_CMD_DESC;
-		desc[1].opcode = cmd_pyld->opcode;
-		desc[1].callback = NULL;
-		desc[1].user1 = NULL;
-		desc[1].user2 = 0;
-		desc[1].pyld = cmd_pyld->data;
-		desc[1].len = cmd_pyld->len;
-
-		ret = ipa3_send_cmd(NUM_OF_DESC, desc);
-		if (ret == -EPERM)
-			IPAERR("Fail to send immediate command %d\n", cnt);
-		ipahal_destroy_imm_cmd(cmd_pyld);
-	}
-
-bail:
-	if (desc != NULL)
-		kfree(desc);
-
-	if (nop_cmd_pyld != NULL)
-		ipahal_destroy_imm_cmd(nop_cmd_pyld);
-
-	return ret;
+	return ipa3_table_dma_cmd(dma);
 }
 
-/**
- * ipa3_nat_free_mem_and_device() - free the NAT memory and remove the device
- * @nat_ctx:	[in] the IPA NAT memory to free
- *
- * Called by NAT client driver to free the NAT memory and remove the device
- */
-void ipa3_nat_free_mem_and_device(struct ipa3_nat_mem *nat_ctx)
+static void ipa3_nat_ipv6ct_free_mem(struct ipa3_nat_ipv6ct_common_mem *dev)
 {
-	struct ipahal_imm_cmd_dma_shared_mem mem_cmd = { 0 };
-	struct ipa3_desc desc;
-	struct ipahal_imm_cmd_pyld *cmd_pyld;
+	IPADBG("\n");
+	if (!dev->is_mem_allocated) {
+		IPADBG("attempt to delete %s before memory allocation\n",
+			dev->name);
+		/* Deletion of partly initialized table is not an error */
+		goto clear;
+	}
+
+	if (dev->is_sys_mem) {
+		IPADBG("freeing the dma memory for %s\n", dev->name);
+		dma_free_coherent(
+			ipa3_ctx->pdev, dev->size,
+			dev->vaddr, dev->dma_handle);
+		dev->size = 0;
+		dev->vaddr = NULL;
+	}
+
+	dev->is_mem_allocated = false;
+
+clear:
+	dev->table_entries = 0;
+	dev->expn_table_entries = 0;
+	dev->base_table_addr = NULL;
+	dev->expansion_table_addr = NULL;
+
+	dev->is_hw_init = false;
+	dev->is_mapped = false;
+	dev->is_sys_mem = false;
+
+	IPADBG("return\n");
+}
+
+static int ipa3_nat_ipv6ct_create_del_table_cmd(
+	uint8_t tbl_index,
+	u32 base_addr,
+	struct ipa3_nat_ipv6ct_common_mem *dev,
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common *table_init_cmd)
+{
+	bool mem_type_shared = true;
 
 	IPADBG("\n");
-	mutex_lock(&nat_ctx->lock);
 
-	if (nat_ctx->is_sys_mem) {
-		IPADBG("freeing the dma memory\n");
-		dma_free_coherent(
-			 ipa3_ctx->pdev, nat_ctx->size,
-			 nat_ctx->vaddr, nat_ctx->dma_handle);
-		nat_ctx->size = 0;
-		nat_ctx->vaddr = NULL;
+	if (tbl_index >= 1) {
+		IPAERR_RL("Unsupported table index %d\n", tbl_index);
+		return -EPERM;
 	}
 
-	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0) {
-		struct ipa_pdn_entry *pdn_entries =
-			nat_ctx->pdn_mem.base;
-
-		/* zero the PDN table and copy the PDN config table to SRAM */
-		IPADBG("zeroing the PDN config table\n");
-		memset(pdn_entries, 0, sizeof(struct ipa_pdn_entry) *
-			IPA_MAX_PDN_NUM);
-		mem_cmd.is_read = false;
-		mem_cmd.skip_pipeline_clear = false;
-		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
-		mem_cmd.size = sizeof(struct ipa_pdn_entry) * IPA_MAX_PDN_NUM;
-		mem_cmd.system_addr = nat_ctx->pdn_mem.phys_base;
-		mem_cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
-			IPA_MEM_PART(pdn_config_ofst);
-		cmd_pyld = ipahal_construct_imm_cmd(
-			IPA_IMM_CMD_DMA_SHARED_MEM, &mem_cmd, false);
-		if (!cmd_pyld) {
-			IPAERR(
-				"fail construct dma_shared_mem cmd: for pdn table");
-			goto lbl_free_pdn;
-		}
-		memset(&desc, 0, sizeof(desc));
-		desc.opcode = cmd_pyld->opcode;
-		desc.pyld = cmd_pyld->data;
-		desc.len = cmd_pyld->len;
-		desc.type = IPA_IMM_CMD_DESC;
-
-		IPADBG("sending PDN table copy cmd\n");
-		if (ipa3_send_cmd(1, &desc))
-			IPAERR("Fail to send immediate command\n");
-
-		ipahal_destroy_imm_cmd(cmd_pyld);
-lbl_free_pdn:
-		IPADBG("freeing the PDN memory\n");
-		dma_free_coherent(ipa3_ctx->pdev,
-			nat_ctx->pdn_mem.size,
-			nat_ctx->pdn_mem.base,
-			nat_ctx->pdn_mem.phys_base);
+	if (dev->tmp_mem != NULL) {
+		IPADBG("using temp memory during %s del\n", dev->name);
+		mem_type_shared = false;
+		base_addr = dev->tmp_mem->dma_handle;
 	}
-	nat_ctx->is_mapped = false;
-	nat_ctx->is_sys_mem = false;
-	nat_ctx->is_dev_init = false;
 
-	mutex_unlock(&nat_ctx->lock);
+	table_init_cmd->table_index = tbl_index;
+	table_init_cmd->base_table_addr = base_addr;
+	table_init_cmd->base_table_addr_shared = mem_type_shared;
+	table_init_cmd->expansion_table_addr = base_addr;
+	table_init_cmd->expansion_table_addr_shared = mem_type_shared;
+	table_init_cmd->size_base_table = 0;
+	table_init_cmd->size_expansion_table = 0;
 	IPADBG("return\n");
+
+	return 0;
+}
+
+static int ipa3_nat_send_del_table_cmd(uint8_t tbl_index)
+{
+	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
+	int result;
+
+	IPADBG("\n");
+
+	result = ipa3_nat_ipv6ct_create_del_table_cmd(
+		tbl_index,
+		IPA_NAT_PHYS_MEM_OFFSET,
+		&ipa3_ctx->nat_mem.dev,
+		&cmd.table_init);
+	if (result) {
+		IPAERR(
+			"Fail to create immediate command to delete NAT table\n");
+		return result;
+	}
+
+	cmd.index_table_addr = cmd.table_init.base_table_addr;
+	cmd.index_table_addr_shared = cmd.table_init.base_table_addr_shared;
+	cmd.index_table_expansion_addr = cmd.index_table_addr;
+	cmd.index_table_expansion_addr_shared = cmd.index_table_addr_shared;
+	cmd.public_addr_info = 0;
+
+	IPADBG("posting NAT delete command\n");
+	result = ipa3_nat_send_init_cmd(&cmd, true);
+	if (result) {
+		IPAERR("Fail to send NAT delete immediate command\n");
+		return result;
+	}
+
+	IPADBG("return\n");
+	return 0;
+}
+
+static int ipa3_ipv6ct_send_del_table_cmd(uint8_t tbl_index)
+{
+	struct ipahal_imm_cmd_ip_v6_ct_init cmd;
+	int result;
+
+	IPADBG("\n");
+
+	result = ipa3_nat_ipv6ct_create_del_table_cmd(
+		tbl_index,
+		IPA_IPV6CT_PHYS_MEM_OFFSET,
+		&ipa3_ctx->ipv6ct_mem.dev,
+		&cmd.table_init);
+	if (result) {
+		IPAERR(
+			"Fail to create immediate command to delete IPv6CT table\n");
+		return result;
+	}
+
+	IPADBG("posting IPv6CT delete command\n");
+	result = ipa3_ipv6ct_send_init_cmd(&cmd);
+	if (result) {
+		IPAERR("Fail to send IPv6CT delete immediate command\n");
+		return result;
+	}
+
+	IPADBG("return\n");
+	return 0;
 }
 
 /**
@@ -962,94 +1445,109 @@
  */
 int ipa3_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
 {
-	struct ipahal_imm_cmd_pyld *nop_cmd_pyld = NULL;
-	struct ipa3_desc desc[2];
-	struct ipahal_imm_cmd_ip_v4_nat_init cmd;
-	struct ipahal_imm_cmd_pyld *cmd_pyld;
-	bool mem_type_shared = true;
-	u32 base_addr = IPA_NAT_PHYS_MEM_OFFSET;
-	int result;
-
-	IPADBG("\n");
-	if (ipa3_ctx->nat_mem.is_tmp_mem) {
-		IPAERR("using temp memory during nat del\n");
-		mem_type_shared = false;
-		base_addr = ipa3_ctx->nat_mem.tmp_dma_handle;
-	}
+	struct ipa_ioc_nat_ipv6ct_table_del tmp;
 
 	if ((ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) &&
 		(del->public_ip_addr == 0)) {
-		IPADBG("Bad Parameter\n");
-		result = -EPERM;
-		goto bail;
+		IPAERR_RL("Bad Parameter public IP address\n");
+		return -EPERM;
 	}
 
-	memset(&desc, 0, sizeof(desc));
-	/* NO-OP IC for ensuring that IPA pipeline is empty */
-	nop_cmd_pyld =
-		ipahal_construct_nop_imm_cmd(false, IPAHAL_HPS_CLEAR, false);
-	if (!nop_cmd_pyld) {
-		IPAERR("Failed to construct NOP imm cmd\n");
-		result = -ENOMEM;
-		goto bail;
-	}
-	desc[0].opcode = nop_cmd_pyld->opcode;
-	desc[0].type = IPA_IMM_CMD_DESC;
-	desc[0].callback = NULL;
-	desc[0].user1 = NULL;
-	desc[0].user2 = 0;
-	desc[0].pyld = nop_cmd_pyld->data;
-	desc[0].len = nop_cmd_pyld->len;
+	tmp.table_index = del->table_index;
 
-	cmd.table_index = del->table_index;
-	cmd.ipv4_rules_addr = base_addr;
-	cmd.ipv4_rules_addr_shared = mem_type_shared;
-	cmd.ipv4_expansion_rules_addr = base_addr;
-	cmd.ipv4_expansion_rules_addr_shared = mem_type_shared;
-	cmd.index_table_addr = base_addr;
-	cmd.index_table_addr_shared = mem_type_shared;
-	cmd.index_table_expansion_addr = base_addr;
-	cmd.index_table_expansion_addr_shared = mem_type_shared;
-	cmd.size_base_tables = 0;
-	cmd.size_expansion_tables = 0;
-	cmd.public_ip_addr = 0;
-	cmd_pyld = ipahal_construct_imm_cmd(
-		IPA_IMM_CMD_IP_V4_NAT_INIT, &cmd, false);
-	if (!cmd_pyld) {
-		IPAERR_RL("Fail to construct ip_v4_nat_init imm cmd\n");
-		result = -EPERM;
-		goto destroy_regwrt_imm_cmd;
-	}
-	desc[1].opcode = cmd_pyld->opcode;
-	desc[1].type = IPA_IMM_CMD_DESC;
-	desc[1].callback = NULL;
-	desc[1].user1 = NULL;
-	desc[1].user2 = 0;
-	desc[1].pyld = cmd_pyld->data;
-	desc[1].len = cmd_pyld->len;
+	return ipa3_del_nat_table(&tmp);
+}
 
-	if (ipa3_send_cmd(2, desc)) {
-		IPAERR("Fail to send immediate command\n");
-		result = -EPERM;
-		goto destroy_imm_cmd;
+/**
+ * ipa3_del_nat_table() - Delete the NAT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by NAT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	int result = 0;
+
+	IPADBG("\n");
+	if (!ipa3_ctx->nat_mem.dev.is_dev_init) {
+		IPAERR("NAT hasn't been initialized\n");
+		return -EPERM;
 	}
 
-	ipa3_ctx->nat_mem.size_base_tables = 0;
-	ipa3_ctx->nat_mem.size_expansion_tables = 0;
+	mutex_lock(&ipa3_ctx->nat_mem.dev.lock);
+
+	if (ipa3_ctx->nat_mem.dev.is_hw_init) {
+		result = ipa3_nat_send_del_table_cmd(del->table_index);
+		if (result) {
+			IPAERR(
+				"Fail to send immediate command to delete NAT table\n");
+			goto bail;
+		}
+	}
+
 	ipa3_ctx->nat_mem.public_ip_addr = 0;
-	ipa3_ctx->nat_mem.ipv4_rules_addr = 0;
-	ipa3_ctx->nat_mem.ipv4_expansion_rules_addr = 0;
 	ipa3_ctx->nat_mem.index_table_addr = 0;
 	ipa3_ctx->nat_mem.index_table_expansion_addr = 0;
 
-	ipa3_nat_free_mem_and_device(&ipa3_ctx->nat_mem);
-	IPADBG("return\n");
-	result = 0;
+	if (ipa3_ctx->ipa_hw_type >= IPA_HW_v4_0 &&
+		ipa3_ctx->nat_mem.dev.is_mem_allocated) {
+		IPADBG("freeing the PDN memory\n");
+		dma_free_coherent(ipa3_ctx->pdev,
+			ipa3_ctx->nat_mem.pdn_mem.size,
+			ipa3_ctx->nat_mem.pdn_mem.base,
+			ipa3_ctx->nat_mem.pdn_mem.phys_base);
+	}
 
-destroy_imm_cmd:
-	ipahal_destroy_imm_cmd(cmd_pyld);
-destroy_regwrt_imm_cmd:
-	ipahal_destroy_imm_cmd(nop_cmd_pyld);
+	ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->nat_mem.dev);
+	IPADBG("return\n");
+
 bail:
+	mutex_unlock(&ipa3_ctx->nat_mem.dev.lock);
 	return result;
 }
+
+/**
+ * ipa3_del_ipv6ct_table() - Delete the IPv6CT table
+ * @del:	[in] delete table parameters
+ *
+ * Called by IPv6CT client to delete the table
+ *
+ * Returns:	0 on success, negative on failure
+ */
+int ipa3_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	int result = 0;
+
+	IPADBG("\n");
+
+	if (ipa3_ctx->ipa_hw_type < IPA_HW_v4_0) {
+		IPAERR_RL("IPv6 connection tracking isn't supported\n");
+		return -EPERM;
+	}
+
+	if (!ipa3_ctx->ipv6ct_mem.dev.is_dev_init) {
+		IPAERR("IPv6 connection tracking hasn't been initialized\n");
+		return -EPERM;
+	}
+
+	mutex_lock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+
+	if (ipa3_ctx->ipv6ct_mem.dev.is_hw_init) {
+		result = ipa3_ipv6ct_send_del_table_cmd(del->table_index);
+		if (result) {
+			IPAERR(
+				"Fail to send immediate command to delete IPv6CT table\n");
+			goto bail;
+		}
+	}
+
+	ipa3_nat_ipv6ct_free_mem(&ipa3_ctx->ipv6ct_mem.dev);
+	IPADBG("return\n");
+
+bail:
+	mutex_unlock(&ipa3_ctx->ipv6ct_mem.dev.lock);
+	return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
new file mode 100644
index 0000000..fea9b3b
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.c
@@ -0,0 +1,1312 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include "ipa_pm.h"
+#include "ipa_i.h"
+
+static const char *client_state_to_str[IPA_PM_STATE_MAX] = {
+	__stringify(IPA_PM_DEACTIVATED),
+	__stringify(IPA_PM_DEACTIVATE_IN_PROGRESS),
+	__stringify(IPA_PM_ACTIVATE_IN_PROGRESS),
+	__stringify(IPA_PM_ACTIVATED),
+	__stringify(IPA_PM_ACTIVATED_PENDING_DEACTIVATION),
+	__stringify(IPA_PM_ACTIVATED_TIMER_SET),
+	__stringify(IPA_PM_ACTIVATED_PENDING_RESCHEDULE),
+};
+
+static const char *ipa_pm_group_to_str[IPA_PM_GROUP_MAX] = {
+	__stringify(IPA_PM_GROUP_DEFAULT),
+	__stringify(IPA_PM_GROUP_APPS),
+	__stringify(IPA_PM_GROUP_MODEM),
+};
+
+
+#define IPA_PM_DRV_NAME "ipa_pm"
+
+#define IPA_PM_DBG(fmt, args...) \
+	do { \
+		pr_debug(IPA_PM_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+#define IPA_PM_DBG_LOW(fmt, args...) \
+	do { \
+		pr_debug(IPA_PM_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+#define IPA_PM_ERR(fmt, args...) \
+	do { \
+		pr_err(IPA_PM_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+			IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
+		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+			IPA_PM_DRV_NAME " %s:%d " fmt, ## args); \
+	} while (0)
+#define IPA_PM_DBG_STATE(hdl, name, state) \
+	IPA_PM_DBG_LOW("Client[%d] %s: %s\n", hdl, name, \
+		client_state_to_str[state])
+
+
+#if IPA_PM_MAX_CLIENTS > 32
+#error max client greater than 32 all bitmask types should be changed
+#endif
+
+/*
+ * struct ipa_pm_exception_list - holds information about an exception
+ * @pending: number of clients in exception that have not yet been adctivated
+ * @bitmask: bitmask of the clients in the exception based on handle
+ * @threshold: the threshold values for the exception
+ */
+struct ipa_pm_exception_list {
+	char clients[IPA_PM_MAX_EX_CL];
+	int pending;
+	u32 bitmask;
+	int threshold[IPA_PM_THRESHOLD_MAX];
+};
+
+/*
+ * struct clk_scaling_db - holds information about threshholds and exceptions
+ * @lock: lock the bitmasks and thresholds
+ * @exception_list: pointer to the list of exceptions
+ * @work: work for clock scaling algorithm
+ * @active_client_bitmask: the bits represent handles in the clients array that
+ * contain non-null client
+ * @threshold_size: size of the throughput threshold
+ * @exception_size: size of the exception list
+ * @cur_vote: idx of the threshold
+ * @default_threshold: the thresholds used if no exception passes
+ * @current_threshold: the current threshold of the clock plan
+ */
+struct clk_scaling_db {
+	spinlock_t lock;
+	struct ipa_pm_exception_list exception_list[IPA_PM_EXCEPTION_MAX];
+	struct work_struct work;
+	u32 active_client_bitmask;
+	int threshold_size;
+	int exception_size;
+	int cur_vote;
+	int default_threshold[IPA_PM_THRESHOLD_MAX];
+	int *current_threshold;
+};
+
+/*
+ * ipa_pm state names
+ *
+ * Timer free states:
+ * @IPA_PM_DEACTIVATED: client starting state when registered
+ * @IPA_PM_DEACTIVATE_IN_PROGRESS: deactivate was called in progress of a client
+ *				   activating
+ * @IPA_PM_ACTIVATE_IN_PROGRESS: client is being activated by work_queue
+ * @IPA_PM_ACTIVATED: client is activated without any timers
+ *
+ * Timer set states:
+ * @IPA_PM_ACTIVATED_PENDING_DEACTIVATION: moves to deactivate once timer pass
+ * @IPA_PM_ACTIVATED_TIMER_SET: client was activated while timer was set, so
+ *			 when the timer pass, client will still be activated
+ *@IPA_PM_ACTIVATED_PENDING_RESCHEDULE: state signifying extended timer when
+ *             a client is deferred_deactivated when a time ris still active
+ */
+enum ipa_pm_state {
+	IPA_PM_DEACTIVATED,
+	IPA_PM_DEACTIVATE_IN_PROGRESS,
+	IPA_PM_ACTIVATE_IN_PROGRESS,
+	IPA_PM_ACTIVATED,
+	IPA_PM_ACTIVATED_PENDING_DEACTIVATION,
+	IPA_PM_ACTIVATED_TIMER_SET,
+	IPA_PM_ACTIVATED_PENDING_RESCHEDULE,
+};
+
+#define IPA_PM_STATE_ACTIVE(state) \
+	(state == IPA_PM_ACTIVATED ||\
+		state == IPA_PM_ACTIVATED_PENDING_DEACTIVATION ||\
+		state  == IPA_PM_ACTIVATED_TIMER_SET ||\
+		state == IPA_PM_ACTIVATED_PENDING_RESCHEDULE)
+
+#define IPA_PM_STATE_IN_PROGRESS(state) \
+	(state == IPA_PM_ACTIVATE_IN_PROGRESS \
+		|| state == IPA_PM_DEACTIVATE_IN_PROGRESS)
+
+/*
+ * struct ipa_pm_client - holds information about a specific IPA client
+ * @name: string name of the client
+ * @callback: pointer to the client's callback function
+ * @callback_params: pointer to the client's callback parameters
+ * @state: Activation state of the client
+ * @skip_clk_vote: 0 if client votes for clock when activated, 1 if no vote
+ * @group: the ipa_pm_group the client belongs to
+ * @hdl: handle of the client
+ * @throughput: the throughput of the client for clock scaling
+ * @state_lock: spinlock to lock the pm_states
+ * @activate_work: work for activate (blocking case)
+ * @deactivate work: delayed work for deferred_deactivate function
+ * @complete: generic wait-for-completion handler
+ */
+struct ipa_pm_client {
+	char name[IPA_PM_MAX_EX_CL];
+	void (*callback)(void*, enum ipa_pm_cb_event);
+	void *callback_params;
+	enum ipa_pm_state state;
+	bool skip_clk_vote;
+	int group;
+	int hdl;
+	int throughput;
+	spinlock_t state_lock;
+	struct work_struct activate_work;
+	struct delayed_work deactivate_work;
+	struct completion complete;
+};
+
+/*
+ * struct ipa_pm_ctx - global ctx that will hold the client arrays and tput info
+ * @clients: array to the clients with the handle as its index
+ * @clients_by_pipe: array to the clients with endpoint as the index
+ * @wq: work queue for deferred deactivate, activate, and clk_scaling work
+ 8 @clk_scaling: pointer to clock scaling database
+ * @client_mutex: global mutex to  lock the client arrays
+ * @aggragated_tput: aggragated tput value of all valid activated clients
+ * @group_tput: combined throughput for the groups
+ */
+struct ipa_pm_ctx {
+	struct ipa_pm_client *clients[IPA_PM_MAX_CLIENTS];
+	struct ipa_pm_client *clients_by_pipe[IPA3_MAX_NUM_PIPES];
+	struct workqueue_struct *wq;
+	struct clk_scaling_db clk_scaling;
+	struct mutex client_mutex;
+	int aggregated_tput;
+	int group_tput[IPA_PM_GROUP_MAX];
+};
+
+static struct ipa_pm_ctx *ipa_pm_ctx;
+
+/**
+ * pop_max_from_array() -pop the max and move the last element to where the
+ * max was popped
+ * @arr: array to be searched for max
+ * @n: size of the array
+ *
+ * Returns: max value of the array
+ */
+static int pop_max_from_array(int *arr, int *n)
+{
+	int i;
+	int max, max_idx;
+
+	max_idx = *n - 1;
+	max = 0;
+
+	if (*n == 0)
+		return 0;
+
+	for (i = 0; i < *n; i++) {
+		if (arr[i] > max) {
+			max = arr[i];
+			max_idx = i;
+		}
+	}
+	(*n)--;
+	arr[max_idx] = arr[*n];
+
+	return max;
+}
+
+/**
+ * calculate_throughput() - calculate the aggregated throughput
+ * based on active clients
+ *
+ * Returns: aggregated tput value
+ */
+static int calculate_throughput(void)
+{
+	int client_tput[IPA_PM_MAX_CLIENTS] = { 0 };
+	bool group_voted[IPA_PM_GROUP_MAX] = { false };
+	int i, n;
+	int max, second_max, aggregated_tput;
+	struct ipa_pm_client *client;
+
+	/* Create a basic array to hold throughputs*/
+	for (i = 0, n = 0; i < IPA_PM_MAX_CLIENTS; i++) {
+		client = ipa_pm_ctx->clients[i];
+		if (client != NULL && IPA_PM_STATE_ACTIVE(client->state)) {
+			/* default case */
+			if (client->group == IPA_PM_GROUP_DEFAULT) {
+				client_tput[n++] = client->throughput;
+			} else if (group_voted[client->group] == false) {
+				client_tput[n++] = ipa_pm_ctx->group_tput
+					[client->group];
+				group_voted[client->group] = true;
+			}
+		}
+	}
+	/*the array will only use n+1 spots. n will be the last index used*/
+
+	aggregated_tput = 0;
+
+	/**
+	 * throughput algorithm:
+	 * 1) pop the max and second_max
+	 * 2) add the 2nd max to aggregated tput
+	 * 3) insert the value of max - 2nd max
+	 * 4) repeat until array is of size 1
+	 */
+	while (n > 1) {
+		max = pop_max_from_array(client_tput, &n);
+		second_max = pop_max_from_array(client_tput, &n);
+		client_tput[n++] = max - second_max;
+		aggregated_tput += second_max;
+	}
+
+	IPA_PM_DBG_LOW("Aggregated throughput: %d\n", aggregated_tput);
+
+	return aggregated_tput;
+}
+
+/**
+ * deactivate_client() - turn off the bit in the active client bitmask based on
+ * the handle passed in
+ * @hdl: The index of the client to be deactivated
+ */
+static void deactivate_client(u32 hdl)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
+	ipa_pm_ctx->clk_scaling.active_client_bitmask &= ~(1 << hdl);
+	spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
+	IPA_PM_DBG_LOW("active bitmask: %x\n",
+		ipa_pm_ctx->clk_scaling.active_client_bitmask);
+}
+
+/**
+ * activate_client() - turn on the bit in the active client bitmask based on
+ * the handle passed in
+ * @hdl: The index of the client to be activated
+ */
+static void activate_client(u32 hdl)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
+	ipa_pm_ctx->clk_scaling.active_client_bitmask |= (1 << hdl);
+	spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
+	IPA_PM_DBG_LOW("active bitmask: %x\n",
+		ipa_pm_ctx->clk_scaling.active_client_bitmask);
+}
+
+/**
+ * deactivate_client() - get threshold
+ *
+ * Returns: threshold of the exception that passes or default if none pass
+ */
+static void set_current_threshold(void)
+{
+	int i;
+	struct clk_scaling_db *clk;
+	struct ipa_pm_exception_list *exception;
+	unsigned long flags;
+
+	clk = &ipa_pm_ctx->clk_scaling;
+
+	spin_lock_irqsave(&ipa_pm_ctx->clk_scaling.lock, flags);
+	for (i = 0; i < clk->exception_size; i++) {
+		exception = &clk->exception_list[i];
+		if (exception->pending == 0 && (exception->bitmask
+			& ~clk->active_client_bitmask) == 0) {
+			spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock,
+				 flags);
+			clk->current_threshold = exception->threshold;
+			IPA_PM_DBG("Exception %d set\n", i);
+			return;
+		}
+	}
+	clk->current_threshold = clk->default_threshold;
+	spin_unlock_irqrestore(&ipa_pm_ctx->clk_scaling.lock, flags);
+}
+
+/**
+ * do_clk_scaling() - set the clock based on the activated clients
+ *
+ * Returns: 0 if success, negative otherwise
+ */
+static int do_clk_scaling(void)
+{
+	int i, tput;
+	int new_th_idx = 1;
+	struct clk_scaling_db *clk_scaling;
+
+	clk_scaling = &ipa_pm_ctx->clk_scaling;
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	IPA_PM_DBG("clock scaling started\n");
+	tput = calculate_throughput();
+	ipa_pm_ctx->aggregated_tput = tput;
+	set_current_threshold();
+
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	for (i = 0; i < clk_scaling->threshold_size; i++) {
+		if (tput > clk_scaling->current_threshold[i])
+			new_th_idx++;
+	}
+
+	IPA_PM_DBG("old idx was at %d\n", ipa_pm_ctx->clk_scaling.cur_vote);
+
+
+	if (ipa_pm_ctx->clk_scaling.cur_vote != new_th_idx) {
+		ipa_pm_ctx->clk_scaling.cur_vote = new_th_idx;
+		ipa3_set_clock_plan_from_pm(ipa_pm_ctx->clk_scaling.cur_vote);
+	}
+
+	IPA_PM_DBG("new idx is at %d\n", ipa_pm_ctx->clk_scaling.cur_vote);
+
+	return 0;
+}
+
+/**
+ * clock_scaling_func() - set the clock on a work queue
+ */
+static void clock_scaling_func(struct work_struct *work)
+{
+	do_clk_scaling();
+}
+
+/**
+ * activate_work_func - activate a client and vote for clock on a work queue
+ */
+static void activate_work_func(struct work_struct *work)
+{
+	struct ipa_pm_client *client;
+	bool dec_clk = false;
+	unsigned long flags;
+
+	client = container_of(work, struct ipa_pm_client, activate_work);
+	if (!client->skip_clk_vote)
+		IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+	if (client->state == IPA_PM_ACTIVATE_IN_PROGRESS) {
+		client->state = IPA_PM_ACTIVATED;
+	} else if (client->state == IPA_PM_DEACTIVATE_IN_PROGRESS) {
+		client->state = IPA_PM_DEACTIVATED;
+		dec_clk = true;
+	} else {
+		IPA_PM_ERR("unexpected state %d\n", client->state);
+		WARN_ON(1);
+	}
+	spin_unlock_irqrestore(&client->state_lock, flags);
+
+	complete_all(&client->complete);
+
+	if (dec_clk) {
+		ipa_set_tag_process_before_gating(true);
+		if (!client->skip_clk_vote)
+			IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+
+		IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+		return;
+	}
+
+	activate_client(client->hdl);
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	if (client->callback) {
+		client->callback(client->callback_params,
+			IPA_PM_CLIENT_ACTIVATED);
+	} else {
+		IPA_PM_ERR("client has no callback");
+		WARN_ON(1);
+	}
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+	do_clk_scaling();
+}
+
+/**
+ * delayed_deferred_deactivate_work_func - deferred deactivate on a work queue
+ */
+static void delayed_deferred_deactivate_work_func(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct ipa_pm_client *client;
+	unsigned long flags;
+
+	dwork = container_of(work, struct delayed_work, work);
+	client = container_of(dwork, struct ipa_pm_client, deactivate_work);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+	switch (client->state) {
+	case IPA_PM_ACTIVATED_TIMER_SET:
+		client->state = IPA_PM_ACTIVATED;
+		goto bail;
+	case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
+		queue_delayed_work(ipa_pm_ctx->wq, &client->deactivate_work,
+			msecs_to_jiffies(IPA_PM_DEFERRED_TIMEOUT));
+		client->state = IPA_PM_ACTIVATED_PENDING_DEACTIVATION;
+		goto bail;
+	case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
+		client->state = IPA_PM_DEACTIVATED;
+		IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		ipa_set_tag_process_before_gating(true);
+		if (!client->skip_clk_vote)
+			IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+
+		deactivate_client(client->hdl);
+		do_clk_scaling();
+		return;
+	default:
+		IPA_PM_ERR("unexpected state %d\n", client->state);
+		WARN_ON(1);
+		goto bail;
+	}
+
+bail:
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+	spin_unlock_irqrestore(&client->state_lock, flags);
+}
+
+static int find_next_open_array_element(const char *name)
+{
+	int i, n;
+
+	n = -ENOBUFS;
+
+	for (i = IPA_PM_MAX_CLIENTS - 1; i >= 0; i--) {
+		if (ipa_pm_ctx->clients[i] == NULL) {
+			n = i;
+			continue;
+		}
+
+		if (strlen(name) == strlen(ipa_pm_ctx->clients[i]->name))
+			if (!strcmp(name, ipa_pm_ctx->clients[i]->name))
+				return -EEXIST;
+	}
+	return n;
+}
+
+/**
+ * add_client_to_exception_list() - add client to the exception list and
+ * update pending if necessary
+ * @hdl: index of the IPA client
+ *
+ * Returns: 0 if success, negative otherwise
+ */
+static int add_client_to_exception_list(u32 hdl)
+{
+	int i;
+	struct ipa_pm_exception_list *exception;
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
+		exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
+		if (strnstr(exception->clients, ipa_pm_ctx->clients[hdl]->name,
+			strlen(exception->clients))) {
+			exception->pending--;
+
+			if (exception->pending < 0) {
+				WARN_ON(1);
+				exception->pending = 0;
+				mutex_unlock(&ipa_pm_ctx->client_mutex);
+				return -EPERM;
+			}
+			exception->bitmask |= (1 << hdl);
+		}
+	}
+	IPA_PM_DBG("%s added to exception list\n",
+		ipa_pm_ctx->clients[hdl]->name);
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	return 0;
+}
+
+/**
+ * remove_client_to_exception_list() - remove client from the exception list and
+ * update pending if necessary
+ * @hdl: index of the IPA client
+ *
+ * Returns: 0 if success, negative otherwise
+ */
+static int remove_client_from_exception_list(u32 hdl)
+{
+	int i;
+	struct ipa_pm_exception_list *exception;
+
+	for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
+		exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
+		if (exception->bitmask & (1 << hdl)) {
+			exception->pending++;
+			exception->bitmask &= ~(1 << hdl);
+		}
+	}
+	IPA_PM_DBG("Client %d removed from exception list\n", hdl);
+
+	return 0;
+}
+
+/**
+ * ipa_pm_init() - initialize  IPA PM Components
+ * @ipa_pm_init_params: parameters needed to fill exceptions and thresholds
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_init(struct ipa_pm_init_params *params)
+{
+	int i, j;
+	struct clk_scaling_db *clk_scaling;
+
+	if (params == NULL) {
+		IPA_PM_ERR("Invalid Params\n");
+		return -EINVAL;
+	}
+
+	if (params->threshold_size <= 0
+		|| params->threshold_size > IPA_PM_THRESHOLD_MAX) {
+		IPA_PM_ERR("Invalid threshold size\n");
+		return -EINVAL;
+	}
+
+	if (params->exception_size < 0
+		|| params->exception_size > IPA_PM_EXCEPTION_MAX) {
+		IPA_PM_ERR("Invalid exception size\n");
+		return -EINVAL;
+	}
+
+	IPA_PM_DBG("IPA PM initialization started\n");
+
+	if (ipa_pm_ctx != NULL) {
+		IPA_PM_ERR("Already initialized\n");
+		return -EPERM;
+	}
+
+
+	ipa_pm_ctx = kzalloc(sizeof(*ipa_pm_ctx), GFP_KERNEL);
+	if (!ipa_pm_ctx) {
+		IPA_PM_ERR(":kzalloc err.\n");
+		return -ENOMEM;
+	}
+
+	ipa_pm_ctx->wq = create_singlethread_workqueue("ipa_pm_activate");
+	if (!ipa_pm_ctx->wq) {
+		IPA_PM_ERR("create workqueue failed\n");
+		kfree(ipa_pm_ctx);
+		return -ENOMEM;
+	}
+
+	mutex_init(&ipa_pm_ctx->client_mutex);
+
+	/* Populate and init locks in clk_scaling_db */
+	clk_scaling = &ipa_pm_ctx->clk_scaling;
+	spin_lock_init(&clk_scaling->lock);
+	clk_scaling->threshold_size = params->threshold_size;
+	clk_scaling->exception_size = params->exception_size;
+	INIT_WORK(&clk_scaling->work, clock_scaling_func);
+
+	for (i = 0; i < params->threshold_size; i++)
+		clk_scaling->default_threshold[i] =
+			params->default_threshold[i];
+
+	/* Populate exception list*/
+	for (i = 0; i < params->exception_size; i++) {
+		strlcpy(clk_scaling->exception_list[i].clients,
+			params->exceptions[i].usecase, IPA_PM_MAX_EX_CL);
+		IPA_PM_DBG("Usecase: %s\n", params->exceptions[i].usecase);
+
+		/* Parse the commas to count the size of the clients */
+		for (j = 0; j < IPA_PM_MAX_EX_CL &&
+			clk_scaling->exception_list[i].clients[j]; j++) {
+			if (clk_scaling->exception_list[i].clients[j] == ',')
+				clk_scaling->exception_list[i].pending++;
+		}
+
+		clk_scaling->exception_list[i].pending++;
+		IPA_PM_DBG("Pending: %d\n", clk_scaling->
+			exception_list[i].pending);
+
+		/* populate the threshold */
+		for (j = 0; j < params->threshold_size; j++) {
+			clk_scaling->exception_list[i].threshold[j]
+			= params->exceptions[i].threshold[j];
+		}
+
+	}
+	IPA_PM_DBG("initialization success");
+
+	return 0;
+}
+
+int ipa_pm_destroy(void)
+{
+	IPA_PM_DBG("IPA PM destroy started\n");
+
+	if (ipa_pm_ctx == NULL) {
+		IPA_PM_ERR("Already destroyed\n");
+		return -EPERM;
+	}
+
+	destroy_workqueue(ipa_pm_ctx->wq);
+
+	kfree(ipa_pm_ctx);
+	ipa_pm_ctx = NULL;
+
+	return 0;
+}
+
+/**
+ * ipa_rm_delete_register() - register an IPA PM client with the PM
+ * @register_params: params for a client like throughput, callback, etc.
+ * @hdl: int pointer that will be used as an index to access the client
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Side effects: *hdl is replaced with the client index or -EEXIST if
+ * client is already registered
+ */
+int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl)
+{
+	struct ipa_pm_client *client;
+
+	if (params == NULL || hdl == NULL || params->name == NULL) {
+		IPA_PM_ERR("Invalid Params\n");
+		return -EINVAL;
+	}
+
+	IPA_PM_DBG("IPA PM registering client\n");
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+
+	*hdl = find_next_open_array_element(params->name);
+
+	if (*hdl > IPA_CLIENT_MAX) {
+		mutex_unlock(&ipa_pm_ctx->client_mutex);
+		IPA_PM_ERR("client is already registered or array is full\n");
+		return *hdl;
+	}
+
+	ipa_pm_ctx->clients[*hdl] = kzalloc(sizeof
+		(struct ipa_pm_client), GFP_KERNEL);
+	if (!ipa_pm_ctx->clients[*hdl]) {
+		mutex_unlock(&ipa_pm_ctx->client_mutex);
+		IPA_PM_ERR(":kzalloc err.\n");
+		return -ENOMEM;
+	}
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	client = ipa_pm_ctx->clients[*hdl];
+
+	spin_lock_init(&client->state_lock);
+
+	INIT_DELAYED_WORK(&client->deactivate_work,
+		delayed_deferred_deactivate_work_func);
+
+	INIT_WORK(&client->activate_work, activate_work_func);
+
+	/* populate fields */
+	strlcpy(client->name, params->name, IPA_PM_MAX_EX_CL);
+	client->callback = params->callback;
+	client->callback_params = params->user_data;
+	client->group = params->group;
+	client->hdl = *hdl;
+	client->skip_clk_vote = params->skip_clk_vote;
+
+	/* add client to exception list */
+	if (add_client_to_exception_list(*hdl)) {
+		ipa_pm_deregister(*hdl);
+		IPA_PM_ERR("Fail to add client to exception_list\n");
+		return -EPERM;
+	}
+
+	IPA_PM_DBG("IPA PM client registered with handle %d\n", *hdl);
+	return 0;
+}
+
+/**
+ * ipa_pm_deregister() - deregister IPA client from the PM
+ * @hdl: index of the client in the array
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_deregister(u32 hdl)
+{
+	struct ipa_pm_client *client;
+	int i;
+	unsigned long flags;
+
+	if (hdl >= IPA_PM_MAX_CLIENTS) {
+		IPA_PM_ERR("Invalid Param\n");
+		return -EINVAL;
+	}
+
+	if (ipa_pm_ctx->clients[hdl] == NULL) {
+		IPA_PM_ERR("Client is Null\n");
+		return -EINVAL;
+	}
+
+	IPA_PM_DBG("IPA PM deregistering client\n");
+
+	client = ipa_pm_ctx->clients[hdl];
+	spin_lock_irqsave(&client->state_lock, flags);
+	if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		wait_for_completion(&client->complete);
+		spin_lock_irqsave(&client->state_lock, flags);
+	}
+
+	if (IPA_PM_STATE_ACTIVE(client->state)) {
+		IPA_PM_DBG("Activated clients cannot be deregistered");
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		return -EPERM;
+	}
+	spin_unlock_irqrestore(&client->state_lock, flags);
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+
+	/* nullify pointers in pipe array */
+	for (i = 0; i < IPA3_MAX_NUM_PIPES; i++) {
+		if (ipa_pm_ctx->clients_by_pipe[i] == ipa_pm_ctx->clients[hdl])
+			ipa_pm_ctx->clients_by_pipe[i] = NULL;
+	}
+	kfree(client);
+	ipa_pm_ctx->clients[hdl] = NULL;
+
+	remove_client_from_exception_list(hdl);
+	IPA_PM_DBG("IPA PM client %d deregistered\n", hdl);
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	return 0;
+}
+
+/**
+ * ipa_pm_associate_ipa_cons_to_client() - add mapping to pipe with ipa cllent
+ * @hdl: index of the client to be mapped
+ * @consumer: the pipe/consumer name to be pipped to the client
+ *
+ * Returns: 0 on success, negative on failure
+ *
+ * Side effects: multiple pipes are allowed to be mapped to a single client
+ */
+int ipa_pm_associate_ipa_cons_to_client(u32 hdl, enum ipa_client_type consumer)
+{
+	int idx;
+
+	if (hdl >= IPA_PM_MAX_CLIENTS || consumer < 0 ||
+		consumer >= IPA_CLIENT_MAX) {
+		IPA_PM_ERR("invalid params\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	idx = ipa_get_ep_mapping(consumer);
+
+	IPA_PM_DBG("Mapping pipe %d to client %d\n", idx, hdl);
+
+	if (idx < 0) {
+		mutex_unlock(&ipa_pm_ctx->client_mutex);
+		IPA_PM_DBG("Pipe is not used\n");
+		return 0;
+	}
+
+	if (ipa_pm_ctx->clients[hdl] == NULL) {
+		mutex_unlock(&ipa_pm_ctx->client_mutex);
+		IPA_PM_ERR("Client is NULL\n");
+		return -EPERM;
+	}
+
+	if (ipa_pm_ctx->clients_by_pipe[idx] != NULL) {
+		mutex_unlock(&ipa_pm_ctx->client_mutex);
+		IPA_PM_ERR("Pipe is already mapped\n");
+		return -EPERM;
+	}
+	ipa_pm_ctx->clients_by_pipe[idx] = ipa_pm_ctx->clients[hdl];
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	IPA_PM_DBG("Pipe %d is mapped to client %d\n", idx, hdl);
+
+	return 0;
+}
+
+static int ipa_pm_activate_helper(struct ipa_pm_client *client, bool sync)
+{
+	struct ipa_active_client_logging_info log_info;
+	int result = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+
+	if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
+		if (sync) {
+			spin_unlock_irqrestore(&client->state_lock, flags);
+			wait_for_completion(&client->complete);
+			spin_lock_irqsave(&client->state_lock, flags);
+		} else {
+			client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
+			spin_unlock_irqrestore(&client->state_lock, flags);
+			return -EINPROGRESS;
+		}
+	}
+
+	switch (client->state) {
+	case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
+	case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
+		client->state = IPA_PM_ACTIVATED_TIMER_SET;
+	case IPA_PM_ACTIVATED:
+	case IPA_PM_ACTIVATED_TIMER_SET:
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		return 0;
+	case IPA_PM_DEACTIVATED:
+		break;
+	default:
+		IPA_PM_ERR("Invalid State\n");
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		return -EPERM;
+	}
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+
+	IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log_info, client->name);
+	if (!client->skip_clk_vote) {
+		if (sync) {
+			client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
+			spin_unlock_irqrestore(&client->state_lock, flags);
+			IPA_ACTIVE_CLIENTS_INC_SPECIAL(client->name);
+			spin_lock_irqsave(&client->state_lock, flags);
+		} else
+			result = ipa3_inc_client_enable_clks_no_block
+				 (&log_info);
+	}
+
+	/* we got the clocks */
+	if (result == 0) {
+		client->state = IPA_PM_ACTIVATED;
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		activate_client(client->hdl);
+		if (sync)
+			do_clk_scaling();
+		else
+			queue_work(ipa_pm_ctx->wq,
+				   &ipa_pm_ctx->clk_scaling.work);
+		IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+		return 0;
+	}
+
+	client->state = IPA_PM_ACTIVATE_IN_PROGRESS;
+	init_completion(&client->complete);
+	queue_work(ipa_pm_ctx->wq, &client->activate_work);
+	spin_unlock_irqrestore(&client->state_lock, flags);
+	IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+	return -EINPROGRESS;
+}
+
+/**
+ * ipa_pm_activate(): activate ipa client to vote for clock(). Can be called
+ * from atomic context and returns -EINPROGRESS if cannot be done synchronously
+ * @hdl: index of the client in the array
+ *
+ * Returns: 0 on success, -EINPROGRESS if operation cannot be done synchronously
+ * and other negatives on failure
+ */
+int ipa_pm_activate(u32 hdl)
+{
+	if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
+		IPA_PM_ERR("Invalid Param\n");
+		return -EINVAL;
+	}
+
+	return ipa_pm_activate_helper(ipa_pm_ctx->clients[hdl], false);
+}
+
+/**
+ * ipa_pm_activate(): activate ipa client to vote for clock synchronously.
+ * Cannot be called from an atomic contex.
+ * @hdl: index of the client in the array
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_activate_sync(u32 hdl)
+{
+	if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
+		IPA_PM_ERR("Invalid Param\n");
+		return -EINVAL;
+	}
+
+	return ipa_pm_activate_helper(ipa_pm_ctx->clients[hdl], true);
+}
+
+/**
+ * ipa_pm_deferred_deactivate(): schedule a timer to deactivate client and
+ * devote clock. Can be called from atomic context (asynchronously)
+ * @hdl: index of the client in the array
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_deferred_deactivate(u32 hdl)
+{
+	struct ipa_pm_client *client;
+	unsigned long flags;
+
+	if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
+		IPA_PM_ERR("Invalid Param\n");
+		return -EINVAL;
+	}
+
+	client = ipa_pm_ctx->clients[hdl];
+	IPA_PM_DBG_STATE(hdl, client->name, client->state);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	switch (client->state) {
+	case IPA_PM_ACTIVATE_IN_PROGRESS:
+		client->state = IPA_PM_DEACTIVATE_IN_PROGRESS;
+	case IPA_PM_DEACTIVATED:
+		IPA_PM_DBG_STATE(hdl, client->name, client->state);
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		return 0;
+	case IPA_PM_ACTIVATED:
+		client->state = IPA_PM_ACTIVATED_PENDING_DEACTIVATION;
+		queue_delayed_work(ipa_pm_ctx->wq, &client->deactivate_work,
+			msecs_to_jiffies(IPA_PM_DEFERRED_TIMEOUT));
+		break;
+	case IPA_PM_ACTIVATED_TIMER_SET:
+	case IPA_PM_ACTIVATED_PENDING_DEACTIVATION:
+		client->state = IPA_PM_ACTIVATED_PENDING_RESCHEDULE;
+	case IPA_PM_DEACTIVATE_IN_PROGRESS:
+	case IPA_PM_ACTIVATED_PENDING_RESCHEDULE:
+		break;
+	}
+	IPA_PM_DBG_STATE(hdl, client->name, client->state);
+	spin_unlock_irqrestore(&client->state_lock, flags);
+
+	return 0;
+}
+
+/**
+ * ipa_pm_deactivate_all_deferred(): Cancel the deferred deactivation timer and
+ * immediately devotes for IPA clocks
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_deactivate_all_deferred(void)
+{
+	int i;
+	bool run_algorithm = false;
+	struct ipa_pm_client *client;
+	unsigned long flags;
+
+	for (i = 0; i < IPA_PM_MAX_CLIENTS; i++) {
+		client = ipa_pm_ctx->clients[i];
+
+		if (client == NULL)
+			continue;
+
+		cancel_delayed_work_sync(&client->deactivate_work);
+
+		if (IPA_PM_STATE_IN_PROGRESS(client->state)) {
+			wait_for_completion(&client->complete);
+			continue;
+		}
+
+		spin_lock_irqsave(&client->state_lock, flags);
+		IPA_PM_DBG_STATE(client->hdl, client->name, client->state);
+
+		if (client->state == IPA_PM_ACTIVATED_TIMER_SET) {
+			client->state = IPA_PM_ACTIVATED;
+			IPA_PM_DBG_STATE(client->hdl, client->name,
+				client->state);
+			spin_unlock_irqrestore(&client->state_lock, flags);
+		} else if ((client->state ==
+			IPA_PM_ACTIVATED_PENDING_DEACTIVATION) ||
+			(client->state ==
+			IPA_PM_ACTIVATED_PENDING_RESCHEDULE)) {
+			run_algorithm = true;
+			client->state = IPA_PM_DEACTIVATED;
+			IPA_PM_DBG_STATE(client->hdl, client->name,
+				client->state);
+			spin_unlock_irqrestore(&client->state_lock, flags);
+			ipa_set_tag_process_before_gating(true);
+			if (!client->skip_clk_vote)
+				IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+			deactivate_client(client->hdl);
+		} else /* if activated or deactivated, we do nothing */
+			spin_unlock_irqrestore(&client->state_lock, flags);
+	}
+
+	if (run_algorithm)
+		do_clk_scaling();
+
+	return 0;
+}
+
+/**
+ * ipa_pm_deactivate_sync(): deactivate ipa client and devote clock. Cannot be
+ * called from atomic context.
+ * @hdl: index of the client in the array
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_deactivate_sync(u32 hdl)
+{
+	struct ipa_pm_client *client = ipa_pm_ctx->clients[hdl];
+	unsigned long flags;
+
+	if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL) {
+		IPA_PM_ERR("Invalid Param\n");
+		return -EINVAL;
+	}
+
+	cancel_delayed_work_sync(&client->deactivate_work);
+
+	if (IPA_PM_STATE_IN_PROGRESS(client->state))
+		wait_for_completion(&client->complete);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	IPA_PM_DBG_STATE(hdl, client->name, client->state);
+
+	if (client->state == IPA_PM_DEACTIVATED) {
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		return 0;
+	}
+
+	spin_unlock_irqrestore(&client->state_lock, flags);
+
+	/* else case (Deactivates all Activated cases)*/
+	ipa_set_tag_process_before_gating(true);
+	if (!client->skip_clk_vote)
+		IPA_ACTIVE_CLIENTS_DEC_SPECIAL(client->name);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	client->state = IPA_PM_DEACTIVATED;
+	IPA_PM_DBG_STATE(hdl, client->name, client->state);
+	spin_unlock_irqrestore(&client->state_lock, flags);
+	deactivate_client(hdl);
+	do_clk_scaling();
+
+	return 0;
+}
+
+/**
+ * ipa_pm_handle_suspend(): calls the callbacks of suspended clients to wake up
+ * @pipe_bitmask: the bits represent the indexes of the clients to be woken up
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_handle_suspend(u32 pipe_bitmask)
+{
+	int i;
+	struct ipa_pm_client *client;
+	bool client_notified[IPA_PM_MAX_CLIENTS] = { false };
+
+	IPA_PM_DBG_LOW("bitmask: %d",  pipe_bitmask);
+
+	if (pipe_bitmask == 0)
+		return 0;
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	for (i = 0; i < IPA3_MAX_NUM_PIPES; i++) {
+		if (pipe_bitmask & (1 << i)) {
+			client = ipa_pm_ctx->clients_by_pipe[i];
+			if (client && client_notified[client->hdl] == false) {
+				if (client->callback) {
+					client->callback(client->callback_params
+						, IPA_PM_REQUEST_WAKEUP);
+					client_notified[client->hdl] = true;
+				} else {
+					IPA_PM_ERR("client has no callback");
+					WARN_ON(1);
+				}
+			}
+		}
+	}
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+	return 0;
+}
+
+/**
+ * ipa_pm_set_perf_profile(): Adds/changes the throughput requirement to IPA PM
+ * to be used for clock scaling
+ * @hdl: index of the client in the array
+ * @throughput: the new throughput value to be set for that client
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_pm_set_perf_profile(u32 hdl, int throughput)
+{
+	struct ipa_pm_client *client = ipa_pm_ctx->clients[hdl];
+	unsigned long flags;
+
+	if (hdl >= IPA_PM_MAX_CLIENTS || ipa_pm_ctx->clients[hdl] == NULL
+		|| throughput < 0) {
+		IPA_PM_ERR("Invalid Params\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	if (client->group == IPA_PM_GROUP_DEFAULT)
+		IPA_PM_DBG_LOW("Old throughput: %d\n",  client->throughput);
+	else
+		IPA_PM_DBG_LOW("old Group %d throughput: %d\n",
+			client->group, ipa_pm_ctx->group_tput[client->group]);
+
+	if (client->group == IPA_PM_GROUP_DEFAULT)
+		client->throughput = throughput;
+	else
+		ipa_pm_ctx->group_tput[client->group] = throughput;
+
+	if (client->group == IPA_PM_GROUP_DEFAULT)
+		IPA_PM_DBG_LOW("New throughput: %d\n",  client->throughput);
+	else
+		IPA_PM_DBG_LOW("New Group %d throughput: %d\n",
+			client->group, ipa_pm_ctx->group_tput[client->group]);
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	spin_lock_irqsave(&client->state_lock, flags);
+	if (IPA_PM_STATE_ACTIVE(client->state) || (client->group !=
+			IPA_PM_GROUP_DEFAULT)) {
+		spin_unlock_irqrestore(&client->state_lock, flags);
+		do_clk_scaling();
+		return 0;
+	}
+	spin_unlock_irqrestore(&client->state_lock, flags);
+
+	return 0;
+}
+
+/**
+ * ipa_pm_stat() - print PM stat
+ * @buf: [in] The user buff used to print
+ * @size: [in] The size of buf
+ * Returns: number of bytes used on success, negative on failure
+ *
+ * This function is called by ipa_debugfs in order to receive
+ * a picture of the clients in the PM and the throughput, threshold and cur vote
+ */
+int ipa_pm_stat(char *buf, int size)
+{
+	struct ipa_pm_client *client;
+	struct clk_scaling_db *clk = &ipa_pm_ctx->clk_scaling;
+	int i, j, tput, cnt = 0, result = 0;
+	unsigned long flags;
+
+	if (!buf || size < 0)
+		return -EINVAL;
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+
+	result = scnprintf(buf + cnt, size - cnt, "\n\nCurrent threshold: [");
+	cnt += result;
+
+	for (i = 0; i < clk->threshold_size; i++) {
+		result = scnprintf(buf + cnt, size - cnt,
+			"%d, ", clk->current_threshold[i]);
+		cnt += result;
+	}
+
+	result = scnprintf(buf + cnt, size - cnt, "\b\b]\n");
+	cnt += result;
+
+	result = scnprintf(buf + cnt, size - cnt,
+		"Aggregated tput: %d, Cur vote: %d",
+		ipa_pm_ctx->aggregated_tput, clk->cur_vote);
+	cnt += result;
+
+	result = scnprintf(buf + cnt, size - cnt, "\n\nRegistered Clients:\n");
+	cnt += result;
+
+
+	for (i = 0; i < IPA_PM_MAX_CLIENTS; i++) {
+		client = ipa_pm_ctx->clients[i];
+
+		if (client == NULL)
+			continue;
+
+		spin_lock_irqsave(&client->state_lock, flags);
+		if (client->group == IPA_PM_GROUP_DEFAULT)
+			tput = client->throughput;
+		else
+			tput = ipa_pm_ctx->group_tput[client->group];
+
+		result = scnprintf(buf + cnt, size - cnt,
+		"Client[%d]: %s State:%s\nGroup: %s Throughput: %d Pipes: ",
+			i, client->name, client_state_to_str[client->state],
+			ipa_pm_group_to_str[client->group], tput);
+		cnt += result;
+
+		for (j = 0; j < IPA3_MAX_NUM_PIPES; j++) {
+			if (ipa_pm_ctx->clients_by_pipe[j] == client) {
+				result = scnprintf(buf + cnt, size - cnt,
+					"%d, ", j);
+				cnt += result;
+			}
+		}
+
+		result = scnprintf(buf + cnt, size - cnt, "\b\b\n\n");
+		cnt += result;
+		spin_unlock_irqrestore(&client->state_lock, flags);
+	}
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	return cnt;
+}
+
+/**
+ * ipa_pm_exceptions_stat() - print PM exceptions stat
+ * @buf: [in] The user buff used to print
+ * @size: [in] The size of buf
+ * Returns: number of bytes used on success, negative on failure
+ *
+ * This function is called by ipa_debugfs in order to receive
+ * a full picture of the exceptions in the PM
+ */
+int ipa_pm_exceptions_stat(char *buf, int size)
+{
+	int i, j, cnt = 0, result = 0;
+	struct ipa_pm_exception_list *exception;
+
+	if (!buf || size < 0)
+		return -EINVAL;
+
+	result = scnprintf(buf + cnt, size - cnt, "\n");
+	cnt += result;
+
+	mutex_lock(&ipa_pm_ctx->client_mutex);
+	for (i = 0; i < ipa_pm_ctx->clk_scaling.exception_size; i++) {
+		exception = &ipa_pm_ctx->clk_scaling.exception_list[i];
+		if (exception == NULL) {
+			result = scnprintf(buf + cnt, size - cnt,
+			"Exception %d is NULL\n\n", i);
+			cnt += result;
+			continue;
+		}
+
+		result = scnprintf(buf + cnt, size - cnt,
+			"Exception %d: %s\nPending: %d Bitmask: %d Threshold: ["
+			, i, exception->clients, exception->pending,
+			exception->bitmask);
+		cnt += result;
+		for (j = 0; j < ipa_pm_ctx->clk_scaling.threshold_size; j++) {
+			result = scnprintf(buf + cnt, size - cnt,
+				"%d, ", exception->threshold[j]);
+			cnt += result;
+		}
+		result = scnprintf(buf + cnt, size - cnt, "\b\b]\n\n");
+		cnt += result;
+	}
+	mutex_unlock(&ipa_pm_ctx->client_mutex);
+
+	return cnt;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
new file mode 100644
index 0000000..ca022b5
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_pm.h
@@ -0,0 +1,110 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_PM_H_
+#define _IPA_PM_H_
+
+#include <linux/msm_ipa.h>
+
+/* internal to ipa */
+#define IPA_PM_MAX_CLIENTS 10
+#define IPA_PM_MAX_EX_CL 64
+#define IPA_PM_THRESHOLD_MAX 2
+#define IPA_PM_EXCEPTION_MAX 2
+#define IPA_PM_DEFERRED_TIMEOUT 100
+#define IPA_PM_STATE_MAX 7
+
+/*
+ * ipa_pm group names
+ *
+ * Default stands for individual clients while other groups share one throughput
+ * Some groups also have special flags like modem which do not vote for clock
+ * but is accounted for in clock scaling while activated
+ */
+enum ipa_pm_group {
+	IPA_PM_GROUP_DEFAULT,
+	IPA_PM_GROUP_APPS,
+	IPA_PM_GROUP_MODEM,
+	IPA_PM_GROUP_MAX,
+};
+
+/*
+ * ipa_pm_cb_event
+ *
+ * specifies what kind of callback is being called.
+ * IPA_PM_CLIENT_ACTIVATED: the client has completed asynchronous activation
+ * IPA_PM_REQUEST_WAKEUP: wake up the client after it has been suspended
+ */
+enum ipa_pm_cb_event {
+	IPA_PM_CLIENT_ACTIVATED,
+	IPA_PM_REQUEST_WAKEUP,
+	IPA_PM_CB_EVENT_MAX,
+};
+
+/*
+ * struct ipa_pm_exception - clients included in exception and its threshold
+ * @usecase: comma separated client names
+ * @threshold: the threshold values for the exception
+ */
+struct ipa_pm_exception {
+	const char *usecase;
+	int threshold[IPA_PM_THRESHOLD_MAX];
+};
+
+/*
+ * struct ipa_pm_init_params - parameters needed for initializng the pm
+ * @default_threshold: the thresholds used if no exception passes
+ * @threshold_size: size of the threshold
+ * @exceptions: list of exceptions  for the pm
+ * @exception_size: size of the exception_list
+ */
+struct ipa_pm_init_params {
+	int default_threshold[IPA_PM_THRESHOLD_MAX];
+	int threshold_size;
+	struct ipa_pm_exception exceptions[IPA_PM_EXCEPTION_MAX];
+	int exception_size;
+};
+
+/*
+ * struct ipa_pm_register_params - parameters needed to register a client
+ * @name: name of the client
+ * @callback: pointer to the client's callback function
+ * @user_data: pointer to the client's callback parameters
+ * @group: group number of the client
+ * @skip_clk_vote: 0 if client votes for clock when activated, 1 if no vote
+ */
+struct ipa_pm_register_params {
+	const char *name;
+	void (*callback)(void*, enum ipa_pm_cb_event);
+	void *user_data;
+	enum ipa_pm_group group;
+	bool skip_clk_vote;
+};
+
+int ipa_pm_register(struct ipa_pm_register_params *params, u32 *hdl);
+int ipa_pm_associate_ipa_cons_to_client(u32 hdl, enum ipa_client_type consumer);
+int ipa_pm_activate(u32 hdl);
+int ipa_pm_activate_sync(u32 hdl);
+int ipa_pm_deferred_deactivate(u32 hdl);
+int ipa_pm_deactivate_sync(u32 hdl);
+int ipa_pm_set_perf_profile(u32 hdl, int throughput);
+int ipa_pm_deregister(u32 hdl);
+
+/* IPA Internal Functions */
+int ipa_pm_init(struct ipa_pm_init_params *params);
+int ipa_pm_destroy(void);
+int ipa_pm_handle_suspend(u32 pipe_bitmask);
+int ipa_pm_deactivate_all_deferred(void);
+int ipa_pm_stat(char *buf, int size);
+int ipa_pm_exceptions_stat(char *buf, int size);
+
+#endif /* _IPA_PM_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index 61bccc6..1c8715a 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -603,15 +603,49 @@
 	struct ipa_install_fltr_rule_resp_msg_v01 resp;
 	struct msg_desc req_desc, resp_desc;
 	int rc;
+	int i;
 
 	/* check if the filter rules from IPACM is valid */
-	if (req->filter_spec_ex_list_len == 0) {
+	if (req->filter_spec_list_len == 0) {
 		IPAWANDBG("IPACM pass zero rules to Q6\n");
 	} else {
 		IPAWANDBG("IPACM pass %u rules to Q6\n",
 		req->filter_spec_ex_list_len);
 	}
 
+	if (req->filter_spec_list_len >= QMI_IPA_MAX_FILTERS_V01) {
+		IPAWANDBG(
+		"IPACM passes the number of filtering rules exceed limit\n");
+		return -EINVAL;
+	} else if (req->source_pipe_index_valid != 0) {
+		IPAWANDBG(
+		"IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+			req->source_pipe_index_valid);
+		return -EINVAL;
+	} else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+		IPAWANDBG(
+		"IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
+	}
+	for (i = 0; i < req->filter_spec_list_len; i++) {
+		if ((req->filter_spec_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V4_V01) &&
+			(req->filter_spec_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V6_V01))
+			return -EINVAL;
+		if (req->filter_spec_list[i].is_mux_id_valid == false)
+			return -EINVAL;
+		if (req->filter_spec_list[i].is_routing_table_index_valid
+			== false)
+			return -EINVAL;
+		if ((req->filter_spec_list[i].filter_action <=
+			QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+			(req->filter_spec_list[i].filter_action >
+			QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+			return -EINVAL;
+	}
+
 	mutex_lock(&ipa3_qmi_lock);
 	if (ipa3_qmi_ctx != NULL) {
 		/* cache the qmi_filter_request */
@@ -653,6 +687,7 @@
 	struct ipa_install_fltr_rule_resp_ex_msg_v01 resp;
 	struct msg_desc req_desc, resp_desc;
 	int rc;
+	int i;
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->filter_spec_ex_list_len == 0) {
@@ -662,6 +697,34 @@
 		req->filter_spec_ex_list_len);
 	}
 
+	if (req->filter_spec_ex_list_len >= QMI_IPA_MAX_FILTERS_EX_V01) {
+		IPAWANDBG(
+		"IPACM pass the number of filtering rules exceed limit\n");
+		return -EINVAL;
+	} else if (req->source_pipe_index_valid != 0) {
+		IPAWANDBG(
+		"IPACM passes source_pipe_index_valid not zero 0 != %d\n",
+			req->source_pipe_index_valid);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < req->filter_spec_ex_list_len-1; i++) {
+		if ((req->filter_spec_ex_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V4_V01) &&
+			(req->filter_spec_ex_list[i].ip_type !=
+			QMI_IPA_IP_TYPE_V6_V01))
+			return -EINVAL;
+		if (req->filter_spec_ex_list[i].is_mux_id_valid == false)
+			return -EINVAL;
+		if (req->filter_spec_ex_list[i].is_routing_table_index_valid
+			== false)
+			return -EINVAL;
+		if ((req->filter_spec_ex_list[i].filter_action <=
+			QMI_IPA_FILTER_ACTION_INVALID_V01) &&
+			(req->filter_spec_ex_list[i].filter_action >
+			QMI_IPA_FILTER_ACTION_EXCEPTION_V01))
+			return -EINVAL;
+	}
 	mutex_lock(&ipa3_qmi_lock);
 	if (ipa3_qmi_ctx != NULL) {
 		/* cache the qmi_filter_request */
@@ -695,6 +758,57 @@
 		resp.resp.error, "ipa_install_filter");
 }
 
+/* sending ul-filter-install-request to modem*/
+int ipa3_qmi_ul_filter_request_send(
+	struct ipa_configure_ul_firewall_rules_req_msg_v01 *req)
+{
+	struct ipa_configure_ul_firewall_rules_resp_msg_v01 resp;
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	IPAWANDBG("IPACM pass %u rules to Q6\n",
+		req->firewall_rules_list_len);
+
+	mutex_lock(&ipa3_qmi_lock);
+	if (ipa3_qmi_ctx != NULL) {
+		/* cache the qmi_filter_request */
+		memcpy(
+		&(ipa3_qmi_ctx->ipa_configure_ul_firewall_rules_req_msg_cache[
+		ipa3_qmi_ctx->num_ipa_configure_ul_firewall_rules_req_msg]),
+		req,
+		sizeof(struct
+		ipa_configure_ul_firewall_rules_req_msg_v01));
+		ipa3_qmi_ctx->num_ipa_configure_ul_firewall_rules_req_msg++;
+		ipa3_qmi_ctx->num_ipa_configure_ul_firewall_rules_req_msg %=
+			MAX_NUM_QMI_RULE_CACHE;
+	}
+	mutex_unlock(&ipa3_qmi_lock);
+
+	req_desc.max_msg_len =
+		QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_MAX_MSG_LEN_V01;
+	req_desc.msg_id = QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01;
+	req_desc.ei_array =
+		ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei;
+
+	memset(&resp, 0,
+		sizeof(struct ipa_configure_ul_firewall_rules_resp_msg_v01));
+	resp_desc.max_msg_len =
+		QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_MAX_MSG_LEN_V01;
+	resp_desc.msg_id = QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_V01;
+	resp_desc.ei_array =
+		ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei;
+
+	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc,
+		req,
+		sizeof(
+		struct ipa_configure_ul_firewall_rules_req_msg_v01),
+		&resp_desc, &resp, sizeof(resp),
+		QMI_SEND_REQ_TIMEOUT_MS);
+	return ipa3_check_qmi_response(rc,
+		QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01, resp.resp.result,
+		resp.resp.error, "ipa_received_ul_firewall_filter");
+}
+
 int ipa3_qmi_enable_force_clear_datapath_send(
 	struct ipa_enable_force_clear_datapath_req_msg_v01 *req)
 {
@@ -798,14 +912,37 @@
 
 	/* check if the filter rules from IPACM is valid */
 	if (req->rule_id_len == 0) {
-		IPAWANERR(" delete UL filter rule for pipe %d\n",
+		IPAWANDBG(" delete UL filter rule for pipe %d\n",
 		req->source_pipe_index);
-		return -EINVAL;
 	} else if (req->rule_id_len > QMI_IPA_MAX_FILTERS_V01) {
 		IPAWANERR(" UL filter rule for pipe %d exceed max (%u)\n",
 		req->source_pipe_index,
 		req->rule_id_len);
 		return -EINVAL;
+	} else if (req->install_status != IPA_QMI_RESULT_SUCCESS_V01) {
+		IPAWANERR(" UL filter rule for pipe %d install_status = %d\n",
+			req->source_pipe_index, req->install_status);
+		return -EINVAL;
+	} else if (req->rule_id_valid != 1) {
+		IPAWANERR(" UL filter rule for pipe %d rule_id_valid = %d\n",
+			req->source_pipe_index, req->rule_id_valid);
+		return -EINVAL;
+	} else if (req->source_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+		IPAWANDBG(
+		"IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
+	} else if (((req->embedded_pipe_index_valid != true) ||
+			(req->embedded_call_mux_id_valid != true)) &&
+			((req->embedded_pipe_index_valid != false) ||
+			(req->embedded_call_mux_id_valid != false))) {
+		IPAWANERR(
+			"IPACM passes embedded pipe and mux valid not valid\n");
+		return -EINVAL;
+	} else if (req->embedded_pipe_index >= ipa3_ctx->ipa_num_pipes) {
+		IPAWANERR("IPACM passes source pipe index not valid ID = %d\n",
+		req->source_pipe_index);
+		return -EINVAL;
 	}
 
 	if (req->source_pipe_index == -1) {
@@ -881,6 +1018,7 @@
 			       void *ind_cb_priv)
 {
 	struct ipa_data_usage_quota_reached_ind_msg_v01 qmi_ind;
+	struct ipa_configure_ul_firewall_rules_ind_msg_v01 qmi_ul_firewall_ind;
 	struct msg_desc qmi_ind_desc;
 	int rc = 0;
 
@@ -909,6 +1047,36 @@
 		ipa3_broadcast_quota_reach_ind(qmi_ind.apn.mux_id,
 			IPA_UPSTEAM_MODEM);
 	}
+
+	if (msg_id == QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01) {
+		memset(&qmi_ul_firewall_ind, 0, sizeof(
+			struct ipa_configure_ul_firewall_rules_ind_msg_v01));
+		qmi_ind_desc.max_msg_len =
+			QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_MAX_MSG_LEN_V01;
+		qmi_ind_desc.msg_id = QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01;
+		qmi_ind_desc.ei_array =
+			ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei;
+
+		rc = qmi_kernel_decode(
+			&qmi_ind_desc, &qmi_ul_firewall_ind, msg, msg_len);
+		if (rc < 0) {
+			IPAWANERR("Error decoding msg_id %d\n", msg_id);
+			return;
+		}
+
+		IPAWANDBG("UL firewall rules install indication on Q6");
+		if (qmi_ul_firewall_ind.result.is_success ==
+				QMI_IPA_UL_FIREWALL_STATUS_SUCCESS_V01) {
+			IPAWANDBG(" : Success\n");
+			IPAWANDBG
+			("Mux ID : %d\n", qmi_ul_firewall_ind.result.mux_id);
+		} else if (qmi_ul_firewall_ind.result.is_success ==
+				QMI_IPA_UL_FIREWALL_STATUS_FAILURE_V01){
+			IPAWANERR(": Failure\n");
+		} else {
+			IPAWANERR(": Unexpected Result");
+		}
+	}
 }
 
 static void ipa3_q6_clnt_svc_arrive(struct work_struct *work)
@@ -1214,7 +1382,6 @@
 /* voting for bus BW to ipa_rm*/
 int ipa3_vote_for_bus_bw(uint32_t *bw_mbps)
 {
-	struct ipa_rm_perf_profile profile;
 	int ret;
 
 	if (bw_mbps == NULL) {
@@ -1222,16 +1389,13 @@
 		return -EINVAL;
 	}
 
-	memset(&profile, 0, sizeof(profile));
-	profile.max_supported_bandwidth_mbps = *bw_mbps;
-	ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_Q6_PROD,
-			&profile);
+	ret = ipa3_wwan_set_modem_perf_profile(*bw_mbps);
 	if (ret)
 		IPAWANERR("Failed to set perf profile to BW %u\n",
-			profile.max_supported_bandwidth_mbps);
+			*bw_mbps);
 	else
 		IPAWANDBG("Succeeded to set perf profile to BW %u\n",
-			profile.max_supported_bandwidth_mbps);
+			*bw_mbps);
 
 	return ret;
 }
@@ -1364,6 +1528,74 @@
 		resp.resp.error, "ipa_stop_data_usage_quota_req_msg_v01");
 }
 
+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)
+{
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.max_msg_len =
+		QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_MAX_MSG_LEN_V01;
+	req_desc.msg_id =
+		QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01;
+	req_desc.ei_array =
+		ipa3_enable_per_client_stats_req_msg_data_v01_ei;
+
+	resp_desc.max_msg_len =
+		QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_MAX_MSG_LEN_V01;
+	resp_desc.msg_id =
+		QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_V01;
+	resp_desc.ei_array =
+		ipa3_enable_per_client_stats_resp_msg_data_v01_ei;
+
+	IPAWANDBG("Sending QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01\n");
+
+	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
+		sizeof(struct ipa_enable_per_client_stats_req_msg_v01),
+		&resp_desc, resp,
+		sizeof(struct ipa_enable_per_client_stats_resp_msg_v01),
+		QMI_SEND_STATS_REQ_TIMEOUT_MS);
+
+	IPAWANDBG("QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_V01 received\n");
+
+	return ipa3_check_qmi_response(rc,
+		QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01, resp->resp.result,
+		resp->resp.error, "ipa3_qmi_enable_per_client_stats");
+}
+
+int ipa3_qmi_get_per_client_packet_stats(
+	struct ipa_get_stats_per_client_req_msg_v01 *req,
+	struct ipa_get_stats_per_client_resp_msg_v01 *resp)
+{
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.max_msg_len = QMI_IPA_GET_STATS_PER_CLIENT_REQ_MAX_MSG_LEN_V01;
+	req_desc.msg_id = QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01;
+	req_desc.ei_array = ipa3_get_stats_per_client_req_msg_data_v01_ei;
+
+	resp_desc.max_msg_len =
+		QMI_IPA_GET_STATS_PER_CLIENT_RESP_MAX_MSG_LEN_V01;
+	resp_desc.msg_id = QMI_IPA_GET_STATS_PER_CLIENT_RESP_V01;
+	resp_desc.ei_array = ipa3_get_stats_per_client_resp_msg_data_v01_ei;
+
+	IPAWANDBG("Sending QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01\n");
+
+	rc = qmi_send_req_wait(ipa_q6_clnt, &req_desc, req,
+			sizeof(struct ipa_get_stats_per_client_req_msg_v01),
+			&resp_desc, resp,
+			sizeof(struct ipa_get_stats_per_client_resp_msg_v01),
+			QMI_SEND_STATS_REQ_TIMEOUT_MS);
+
+	IPAWANDBG("QMI_IPA_GET_STATS_PER_CLIENT_RESP_V01 received\n");
+
+	return ipa3_check_qmi_response(rc,
+		QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01, resp->resp.result,
+		resp->resp.error,
+		"struct ipa_get_stats_per_client_req_msg_v01");
+}
+
 void ipa3_qmi_init(void)
 {
 	mutex_init(&ipa3_qmi_lock);
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 d5d8503..3351a33 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.h
@@ -32,54 +32,62 @@
 
 #define IPAWANDBG(fmt, args...) \
 	do { \
-		pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		pr_debug(DEV_NAME " %s:%d " fmt, __func__,\
+				__LINE__, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
 
 #define IPAWANDBG_LOW(fmt, args...) \
 	do { \
-		pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		pr_debug(DEV_NAME " %s:%d " fmt, __func__,\
+				__LINE__, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
 #define IPAWANERR(fmt, args...) \
 	do { \
-		pr_err(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		pr_err(DEV_NAME " %s:%d " fmt, __func__,\
+				__LINE__, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
 #define IPAWANINFO(fmt, args...) \
 	do { \
-		pr_info(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args); \
+		pr_info(DEV_NAME " %s:%d " fmt, __func__,\
+				__LINE__, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 		IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
-			DEV_NAME " %s:%d " fmt, ## args); \
+				DEV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
 extern struct ipa3_qmi_context *ipa3_qmi_ctx;
 
 struct ipa3_qmi_context {
-struct ipa_ioc_ext_intf_prop q6_ul_filter_rule[MAX_NUM_Q6_RULE];
-u32 q6_ul_filter_rule_hdl[MAX_NUM_Q6_RULE];
-int num_ipa_install_fltr_rule_req_msg;
-struct ipa_install_fltr_rule_req_msg_v01
+	struct ipa_ioc_ext_intf_prop q6_ul_filter_rule[MAX_NUM_Q6_RULE];
+	u32 q6_ul_filter_rule_hdl[MAX_NUM_Q6_RULE];
+	int num_ipa_install_fltr_rule_req_msg;
+	struct ipa_install_fltr_rule_req_msg_v01
 		ipa_install_fltr_rule_req_msg_cache[MAX_NUM_QMI_RULE_CACHE];
-int num_ipa_install_fltr_rule_req_ex_msg;
-struct ipa_install_fltr_rule_req_ex_msg_v01
+	int num_ipa_install_fltr_rule_req_ex_msg;
+	struct ipa_install_fltr_rule_req_ex_msg_v01
 		ipa_install_fltr_rule_req_ex_msg_cache[MAX_NUM_QMI_RULE_CACHE];
-int num_ipa_fltr_installed_notif_req_msg;
-struct ipa_fltr_installed_notif_req_msg_v01
+	int num_ipa_fltr_installed_notif_req_msg;
+	struct ipa_fltr_installed_notif_req_msg_v01
 		ipa_fltr_installed_notif_req_msg_cache[MAX_NUM_QMI_RULE_CACHE];
-bool modem_cfg_emb_pipe_flt;
+	int num_ipa_configure_ul_firewall_rules_req_msg;
+	struct ipa_configure_ul_firewall_rules_req_msg_v01
+		ipa_configure_ul_firewall_rules_req_msg_cache
+			[MAX_NUM_QMI_RULE_CACHE];
+	bool modem_cfg_emb_pipe_flt;
 };
 
 struct ipa3_rmnet_mux_val {
@@ -95,16 +103,24 @@
 extern struct elem_info ipa3_init_modem_driver_resp_msg_data_v01_ei[];
 extern struct elem_info ipa3_indication_reg_req_msg_data_v01_ei[];
 extern struct elem_info ipa3_indication_reg_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_master_driver_init_complt_ind_msg_data_v01_ei[];
+
+extern struct elem_info
+	ipa3_master_driver_init_complt_ind_msg_data_v01_ei[];
 extern struct elem_info ipa3_install_fltr_rule_req_msg_data_v01_ei[];
 extern struct elem_info ipa3_install_fltr_rule_resp_msg_data_v01_ei[];
 extern struct elem_info ipa3_fltr_installed_notif_req_msg_data_v01_ei[];
-extern struct elem_info ipa3_fltr_installed_notif_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_enable_force_clear_datapath_req_msg_data_v01_ei[];
-extern struct elem_info ipa3_enable_force_clear_datapath_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_disable_force_clear_datapath_req_msg_data_v01_ei[];
+
+extern struct elem_info
+	ipa3_fltr_installed_notif_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_enable_force_clear_datapath_req_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_enable_force_clear_datapath_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_disable_force_clear_datapath_req_msg_data_v01_ei[];
 extern struct elem_info
 	ipa3_disable_force_clear_datapath_resp_msg_data_v01_ei[];
+
 extern struct elem_info ipa3_config_req_msg_data_v01_ei[];
 extern struct elem_info ipa3_config_resp_msg_data_v01_ei[];
 extern struct elem_info ipa3_get_data_stats_req_msg_data_v01_ei[];
@@ -112,14 +128,44 @@
 extern struct elem_info ipa3_get_apn_data_stats_req_msg_data_v01_ei[];
 extern struct elem_info ipa3_get_apn_data_stats_resp_msg_data_v01_ei[];
 extern struct elem_info ipa3_set_data_usage_quota_req_msg_data_v01_ei[];
-extern struct elem_info ipa3_set_data_usage_quota_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_data_usage_quota_reached_ind_msg_data_v01_ei[];
-extern struct elem_info ipa3_stop_data_usage_quota_req_msg_data_v01_ei[];
-extern struct elem_info ipa3_stop_data_usage_quota_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_init_modem_driver_cmplt_req_msg_data_v01_ei[];
-extern struct elem_info ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei[];
-extern struct elem_info ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[];
-extern struct elem_info ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[];
+
+extern struct elem_info
+	ipa3_set_data_usage_quota_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_data_usage_quota_reached_ind_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_stop_data_usage_quota_req_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_stop_data_usage_quota_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_init_modem_driver_cmplt_req_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_init_modem_driver_cmplt_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_install_fltr_rule_req_ex_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_install_fltr_rule_resp_ex_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_ul_firewall_rule_type_data_v01_ei[];
+extern struct elem_info
+	ipa3_ul_firewall_config_result_type_data_v01_ei[];
+extern struct
+	elem_info ipa3_per_client_stats_info_type_data_v01_ei[];
+extern struct elem_info
+	ipa3_enable_per_client_stats_req_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_enable_per_client_stats_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_get_stats_per_client_req_msg_data_v01_ei[];
+
+extern struct elem_info
+	ipa3_get_stats_per_client_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[];
+extern struct elem_info
+	ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[];
 
 /**
  * struct ipa3_rmnet_context - IPA rmnet context
@@ -148,6 +194,9 @@
 int ipa3_qmi_filter_request_ex_send(
 	struct ipa_install_fltr_rule_req_ex_msg_v01 *req);
 
+int ipa3_qmi_ul_filter_request_send(
+	struct ipa_configure_ul_firewall_rules_req_msg_v01 *req);
+
 /* sending filter-installed-notify-request to modem*/
 int ipa3_qmi_filter_notify_send(struct ipa_fltr_installed_notif_req_msg_v01
 		*req);
@@ -194,6 +243,16 @@
 	struct wan_ioctl_query_tether_stats_all *data);
 
 int rmnet_ipa3_reset_tethering_stats(struct wan_ioctl_reset_tether_stats *data);
+int rmnet_ipa3_set_lan_client_info(struct wan_ioctl_lan_client_info *data);
+
+int rmnet_ipa3_clear_lan_client_info(struct wan_ioctl_lan_client_info *data);
+
+int rmnet_ipa3_send_lan_client_msg(struct wan_ioctl_send_lan_client_msg *data);
+
+int rmnet_ipa3_enable_per_client_stats(bool *data);
+
+int rmnet_ipa3_query_per_client_stats(
+	struct wan_ioctl_query_per_client_stats *data);
 
 int ipa3_qmi_get_data_stats(struct ipa_get_data_stats_req_msg_v01 *req,
 	struct ipa_get_data_stats_resp_msg_v01 *resp);
@@ -207,6 +266,17 @@
 
 void ipa3_q6_handshake_complete(bool ssr_bootup);
 
+int ipa3_wwan_set_modem_perf_profile(int throughput);
+
+int ipa3_wwan_set_modem_state(struct wan_ioctl_notify_wan_state *state);
+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);
+
+int ipa3_qmi_get_per_client_packet_stats(
+	struct ipa_get_stats_per_client_req_msg_v01 *req,
+	struct ipa_get_stats_per_client_resp_msg_v01 *resp);
+
 void ipa3_qmi_init(void);
 
 void ipa3_qmi_cleanup(void);
@@ -227,6 +297,12 @@
 	return -EPERM;
 }
 
+static inline int ipa3_qmi_ul_filter_request_send(
+	struct ipa_configure_ul_firewall_rules_req_msg_v01 *req)
+{
+	return -EPERM;
+}
+
 static inline int ipa3_qmi_filter_request_ex_send(
 	struct ipa_install_fltr_rule_req_ex_msg_v01 *req)
 {
@@ -323,12 +399,29 @@
 
 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_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)
+{
+	return -EPERM;
+}
+
+static inline int ipa3_qmi_get_per_client_packet_stats(
+	struct ipa_get_stats_per_client_req_msg_v01 *req,
+	struct ipa_get_stats_per_client_resp_msg_v01 *resp)
+{
+	return -EPERM;
+}
+
 static inline void ipa3_qmi_init(void)
 {
+
 }
 
 static inline void ipa3_qmi_cleanup(void)
 {
+
 }
 
 #endif /* CONFIG_RMNET_IPA3 */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
index d2d4158..703acd7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service_v01.c
@@ -16,6 +16,8 @@
 
 #include <soc/qcom/msm_qmi_interface.h>
 
+#include "ipa_qmi_service.h"
+
 /* Type Definitions  */
 static struct elem_info ipa3_hdr_tbl_info_type_data_v01_ei[] = {
 	{
@@ -1756,6 +1758,36 @@
 			rule_id),
 	},
 	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id_valid),
+	},
+	{
+		.data_type	= QMI_DATA_LEN,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id_len),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= QMI_IPA_MAX_CLIENT_DST_PIPES_V01,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= VAR_LEN_ARRAY,
+		.tlv_type	= 0x18,
+		.offset		= offsetof(
+			struct ipa_fltr_installed_notif_req_msg_v01,
+			dst_pipe_id),
+	},
+	{
 		.data_type	= QMI_EOTI,
 		.is_array	= NO_ARRAY,
 		.tlv_type	= QMI_COMMON_TLV_TYPE,
@@ -2923,3 +2955,432 @@
 		.tlv_type	= QMI_COMMON_TLV_TYPE,
 	},
 };
+
+struct elem_info ipa3_per_client_stats_info_type_data_v01_ei[] = {
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				client_id),
+	},
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				src_pipe_id),
+	},
+	{
+			.data_type	= QMI_UNSIGNED_8_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint64_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_ul_ipv4_bytes),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_8_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint64_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_ul_ipv6_bytes),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_8_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint64_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_dl_ipv4_bytes),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_8_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint64_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_dl_ipv6_bytes),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_ul_ipv4_pkts),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_ul_ipv6_pkts),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_dl_ipv4_pkts),
+
+	},
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_per_client_stats_info_type_v01,
+				num_dl_ipv6_pkts),
+
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_ul_firewall_rule_type_data_v01_ei[] = {
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_ul_firewall_rule_type_v01,
+				ip_type),
+	},
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= 1,
+		.elem_size	= sizeof(struct ipa_filter_rule_type_v01),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+		.offset		= offsetof(struct ipa_ul_firewall_rule_type_v01,
+					filter_rule),
+		.ei_array	= ipa3_filter_rule_type_data_v01_ei,
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_ul_firewall_config_result_type_data_v01_ei[] = {
+	{
+			.data_type	= QMI_UNSIGNED_4_BYTE,
+			.elem_len	= 1,
+			.elem_size	= sizeof(uint32_t),
+			.is_array	= NO_ARRAY,
+			.tlv_type	= QMI_COMMON_TLV_TYPE,
+			.offset		= offsetof(
+				struct ipa_ul_firewall_config_result_type_v01,
+				is_success),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+		.offset		= offsetof(
+			struct ipa_ul_firewall_config_result_type_v01,
+			mux_id),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_enable_per_client_stats_req_msg_data_v01_ei[] = {
+	{
+				.data_type	= QMI_UNSIGNED_1_BYTE,
+				.elem_len	= 1,
+				.elem_size	= sizeof(uint8_t),
+				.is_array	= NO_ARRAY,
+				.tlv_type	= 0x01,
+				.offset		= offsetof(struct
+				ipa_enable_per_client_stats_req_msg_v01,
+				enable_per_client_stats),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_enable_per_client_stats_resp_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= 1,
+		.elem_size	= sizeof(struct qmi_response_type_v01),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x02,
+		.offset		= offsetof(
+			struct ipa_enable_per_client_stats_resp_msg_v01,
+			resp),
+		.ei_array	= get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_get_stats_per_client_req_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x01,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_req_msg_v01,
+			client_id),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x02,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_req_msg_v01,
+			src_pipe_id),
+	},
+	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_req_msg_v01,
+			reset_stats_valid),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_1_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_req_msg_v01,
+			reset_stats),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_get_stats_per_client_resp_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= 1,
+		.elem_size	= sizeof(struct qmi_response_type_v01),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x02,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_resp_msg_v01,
+			resp),
+		.ei_array	= get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_resp_msg_v01,
+			per_client_stats_list_valid),
+	},
+	{
+		.data_type	= QMI_DATA_LEN,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_resp_msg_v01,
+			per_client_stats_list_len),
+	},
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= QMI_IPA_MAX_PER_CLIENTS_V01,
+		.elem_size	=
+			sizeof(struct ipa_per_client_stats_info_type_v01),
+		.is_array	= VAR_LEN_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_get_stats_per_client_resp_msg_v01,
+			per_client_stats_list),
+		.ei_array	=
+			ipa3_per_client_stats_info_type_data_v01_ei,
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_configure_ul_firewall_rules_req_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_DATA_LEN,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x1,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			firewall_rules_list_len),
+	},
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= QMI_IPA_MAX_UL_FIREWALL_RULES_V01,
+		.elem_size	= sizeof(struct ipa_ul_firewall_rule_type_v01),
+		.is_array	= VAR_LEN_ARRAY,
+		.tlv_type	= 0x1,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			firewall_rules_list),
+		.ei_array	=
+			ipa3_ul_firewall_rule_type_data_v01_ei,
+	},
+	{
+		.data_type	= QMI_UNSIGNED_4_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint32_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x2,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			mux_id),
+	},
+	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			disable_valid),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_1_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x10,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			disable),
+	},
+	{
+		.data_type	= QMI_OPT_FLAG,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x11,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			are_blacklist_filters_valid),
+	},
+	{
+		.data_type	= QMI_UNSIGNED_1_BYTE,
+		.elem_len	= 1,
+		.elem_size	= sizeof(uint8_t),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x11,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_req_msg_v01,
+			are_blacklist_filters),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_configure_ul_firewall_rules_resp_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= 1,
+		.elem_size	= sizeof(struct qmi_response_type_v01),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x02,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_resp_msg_v01,
+			resp),
+		.ei_array	= get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info ipa3_configure_ul_firewall_rules_ind_msg_data_v01_ei[] = {
+	{
+		.data_type	= QMI_STRUCT,
+		.elem_len	= 1,
+		.elem_size	= sizeof(
+			struct ipa_ul_firewall_config_result_type_v01),
+		.is_array	= NO_ARRAY,
+		.tlv_type	= 0x01,
+		.offset		= offsetof(
+			struct ipa_configure_ul_firewall_rules_ind_msg_v01,
+			result),
+		.ei_array	=
+		ipa3_ul_firewall_config_result_type_data_v01_ei,
+	},
+	{
+		.data_type	= QMI_EOTI,
+		.is_array	= NO_ARRAY,
+		.tlv_type	= QMI_COMMON_TLV_TYPE,
+	},
+};
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index dd691f1..fc76604 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -21,6 +21,8 @@
 #define IPA_RT_STATUS_OF_DEL_FAILED	(-1)
 #define IPA_RT_STATUS_OF_MDFY_FAILED (-1)
 
+#define IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC 5
+
 #define IPA_RT_GET_RULE_TYPE(__entry) \
 	( \
 	((__entry)->rule.hashable) ? \
@@ -57,15 +59,15 @@
 	gen_params.ipt = ip;
 	gen_params.dst_pipe_idx = ipa3_get_ep_mapping(entry->rule.dst);
 	if (gen_params.dst_pipe_idx == -1) {
-		IPAERR("Wrong destination pipe specified in RT rule\n");
-		WARN_ON(1);
+		IPAERR_RL("Wrong destination pipe specified in RT rule\n");
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EPERM;
 	}
 	if (!IPA_CLIENT_IS_CONS(entry->rule.dst)) {
-		IPAERR("No RT rule on IPA_client_producer pipe.\n");
-		IPAERR("pipe_idx: %d dst_pipe: %d\n",
+		IPAERR_RL("No RT rule on IPA_client_producer pipe.\n");
+		IPAERR_RL("pipe_idx: %d dst_pipe: %d\n",
 				gen_params.dst_pipe_idx, entry->rule.dst);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EPERM;
 	}
 
@@ -143,14 +145,14 @@
 			tbl_mem.size = tbl->sz[rlt] -
 				ipahal_get_hw_tbl_hdr_width();
 			if (ipahal_fltrt_allocate_hw_sys_tbl(&tbl_mem)) {
-				IPAERR("fail to alloc sys tbl of size %d\n",
+				IPAERR_RL("fail to alloc sys tbl of size %d\n",
 					tbl_mem.size);
 				goto err;
 			}
 
 			if (ipahal_fltrt_write_addr_to_hdr(tbl_mem.phys_base,
 				hdr, tbl->idx - apps_start_idx, true)) {
-				IPAERR("fail to wrt sys tbl addr to hdr\n");
+				IPAERR_RL("fail to wrt sys tbl addr to hdr\n");
 				goto hdr_update_fail;
 			}
 
@@ -164,7 +166,7 @@
 				res = ipa_generate_rt_hw_rule(ip, entry,
 					tbl_mem_buf);
 				if (res) {
-					IPAERR("failed to gen HW RT rule\n");
+					IPAERR_RL("failed to gen HW RT rule\n");
 					goto hdr_update_fail;
 				}
 				tbl_mem_buf += entry->hw_len;
@@ -181,7 +183,7 @@
 			/* update the hdr at the right index */
 			if (ipahal_fltrt_write_addr_to_hdr(offset, hdr,
 				tbl->idx - apps_start_idx, true)) {
-				IPAERR("fail to wrt lcl tbl ofst to hdr\n");
+				IPAERR_RL("fail to wrt lcl tbl ofst to hdr\n");
 				goto hdr_update_fail;
 			}
 
@@ -193,7 +195,7 @@
 				res = ipa_generate_rt_hw_rule(ip, entry,
 					body_i);
 				if (res) {
-					IPAERR("failed to gen HW RT rule\n");
+					IPAERR_RL("failed to gen HW RT rule\n");
 					goto err;
 				}
 				body_i += entry->hw_len;
@@ -294,7 +296,7 @@
 
 		res = ipa_generate_rt_hw_rule(ip, entry, NULL);
 		if (res) {
-			IPAERR("failed to calculate HW RT rule size\n");
+			IPAERR_RL("failed to calculate HW RT rule size\n");
 			return -EPERM;
 		}
 
@@ -309,8 +311,8 @@
 
 	if ((tbl->sz[IPA_RULE_HASHABLE] +
 		tbl->sz[IPA_RULE_NON_HASHABLE]) == 0) {
-		WARN_ON(1);
-		IPAERR("rt tbl %s is with zero total size\n", tbl->name);
+		WARN_ON_RATELIMIT_IPA(1);
+		IPAERR_RL("rt tbl %s is with zero total size\n", tbl->name);
 	}
 
 	hdr_width = ipahal_get_hw_tbl_hdr_width();
@@ -432,10 +434,11 @@
  */
 int __ipa_commit_rt_v3(enum ipa_ip_type ip)
 {
-	struct ipa3_desc desc[5];
+	struct ipa3_desc desc[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
 	struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
 	struct ipahal_imm_cmd_dma_shared_mem  mem_cmd = {0};
-	struct ipahal_imm_cmd_pyld *cmd_pyld[5];
+	struct ipahal_imm_cmd_pyld
+		*cmd_pyld[IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC];
 	int num_cmd = 0;
 	struct ipahal_fltrt_alloc_imgs_params alloc_params;
 	u32 num_modem_rt_index;
@@ -557,10 +560,7 @@
 		IPAERR("fail construct register_write imm cmd. IP %d\n", ip);
 		goto fail_size_valid;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	mem_cmd.is_read = false;
@@ -575,10 +575,7 @@
 		IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
 		goto fail_imm_cmd_construct;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	mem_cmd.is_read = false;
@@ -593,13 +590,17 @@
 		IPAERR("fail construct dma_shared_mem imm cmd. IP %d\n", ip);
 		goto fail_imm_cmd_construct;
 	}
-	desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-	desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-	desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-	desc[num_cmd].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 	num_cmd++;
 
 	if (lcl_nhash) {
+		if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -613,13 +614,17 @@
 				ip);
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 		num_cmd++;
 	}
 	if (lcl_hash) {
+		if (num_cmd >= IPA_RT_MAX_NUM_OF_COMMIT_TABLES_CMD_DESC) {
+			IPAERR("number of commands is out of range: IP = %d\n",
+				ip);
+			rc = -ENOBUFS;
+			goto fail_imm_cmd_construct;
+		}
+
 		mem_cmd.is_read = false;
 		mem_cmd.skip_pipeline_clear = false;
 		mem_cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
@@ -633,10 +638,7 @@
 				ip);
 			goto fail_imm_cmd_construct;
 		}
-		desc[num_cmd].opcode = cmd_pyld[num_cmd]->opcode;
-		desc[num_cmd].pyld = cmd_pyld[num_cmd]->data;
-		desc[num_cmd].len = cmd_pyld[num_cmd]->len;
-		desc[num_cmd].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[num_cmd], cmd_pyld[num_cmd]);
 		num_cmd++;
 	}
 
@@ -732,6 +734,7 @@
 	}
 
 	mutex_lock(&ipa3_ctx->lock);
+	in->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	/* check if this table exists */
 	entry = __ipa3_find_rt_tbl(in->ip, in->name);
 	if (!entry) {
@@ -816,8 +819,8 @@
 
 		id = ipa3_id_alloc(entry);
 		if (id < 0) {
-			IPAERR("failed to add to tree\n");
-			WARN_ON(1);
+			IPAERR_RL("failed to add to tree\n");
+			WARN_ON_RATELIMIT_IPA(1);
 			goto ipa_insert_failed;
 		}
 		entry->id = id;
@@ -856,7 +859,7 @@
 	else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6])
 		ip = IPA_IP_v6;
 	else {
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EPERM;
 	}
 
@@ -889,14 +892,14 @@
 				struct ipa3_hdr_proc_ctx_entry **proc_ctx)
 {
 	if (rule->hdr_hdl && rule->hdr_proc_ctx_hdl) {
-		IPAERR("rule contains both hdr_hdl and hdr_proc_ctx_hdl\n");
+		IPAERR_RL("rule contains both hdr_hdl and hdr_proc_ctx_hdl\n");
 		return -EPERM;
 	}
 
 	if (rule->hdr_hdl) {
 		*hdr = ipa3_id_find(rule->hdr_hdl);
 		if ((*hdr == NULL) || ((*hdr)->cookie != IPA_HDR_COOKIE)) {
-			IPAERR("rt rule does not point to valid hdr\n");
+			IPAERR_RL("rt rule does not point to valid hdr\n");
 			return -EPERM;
 		}
 	} else if (rule->hdr_proc_ctx_hdl) {
@@ -904,7 +907,7 @@
 		if ((*proc_ctx == NULL) ||
 			((*proc_ctx)->cookie != IPA_PROC_HDR_COOKIE)) {
 
-			IPAERR("rt rule does not point to valid proc ctx\n");
+			IPAERR_RL("rt rule does not point to valid proc ctx\n");
 			return -EPERM;
 		}
 	}
@@ -915,7 +918,8 @@
 static int __ipa_create_rt_entry(struct ipa3_rt_entry **entry,
 		const struct ipa_rt_rule *rule,
 		struct ipa3_rt_tbl *tbl, struct ipa3_hdr_entry *hdr,
-		struct ipa3_hdr_proc_ctx_entry *proc_ctx)
+		struct ipa3_hdr_proc_ctx_entry *proc_ctx,
+		u16 rule_id)
 {
 	int id;
 
@@ -930,11 +934,16 @@
 	(*(entry))->tbl = tbl;
 	(*(entry))->hdr = hdr;
 	(*(entry))->proc_ctx = proc_ctx;
-	id = ipa3_alloc_rule_id(tbl->rule_ids);
-	if (id < 0) {
-		IPAERR("failed to allocate rule id\n");
-		WARN_ON(1);
-		goto alloc_rule_id_fail;
+	if (rule_id) {
+		id = rule_id;
+		(*(entry))->rule_id_valid = 1;
+	} else {
+		id = ipa3_alloc_rule_id(tbl->rule_ids);
+		if (id < 0) {
+			IPAERR_RL("failed to allocate rule id\n");
+			WARN_ON_RATELIMIT_IPA(1);
+			goto alloc_rule_id_fail;
+		}
 	}
 	(*(entry))->rule_id = id;
 
@@ -958,8 +967,8 @@
 		entry->proc_ctx->ref_cnt++;
 	id = ipa3_id_alloc(entry);
 	if (id < 0) {
-		IPAERR("failed to add to tree\n");
-		WARN_ON(1);
+		IPAERR_RL("failed to add to tree\n");
+		WARN_ON_RATELIMIT_IPA(1);
 		goto ipa_insert_failed;
 	}
 	IPADBG("add rt rule tbl_idx=%d rule_cnt=%d rule_id=%d\n",
@@ -981,7 +990,8 @@
 }
 
 static int __ipa_add_rt_rule(enum ipa_ip_type ip, const char *name,
-		const struct ipa_rt_rule *rule, u8 at_rear, u32 *rule_hdl)
+		const struct ipa_rt_rule *rule, u8 at_rear, u32 *rule_hdl,
+		u16 rule_id)
 {
 	struct ipa3_rt_tbl *tbl;
 	struct ipa3_rt_entry *entry;
@@ -1009,7 +1019,8 @@
 		goto error;
 	}
 
-	if (__ipa_create_rt_entry(&entry, rule, tbl, hdr, proc_ctx))
+	if (__ipa_create_rt_entry(&entry, rule, tbl, hdr, proc_ctx,
+		rule_id))
 		goto error;
 
 	if (at_rear)
@@ -1040,7 +1051,7 @@
 	if (__ipa_rt_validate_hndls(rule, &hdr, &proc_ctx))
 		goto error;
 
-	if (__ipa_create_rt_entry(&entry, rule, tbl, hdr, proc_ctx))
+	if (__ipa_create_rt_entry(&entry, rule, tbl, hdr, proc_ctx, 0))
 		goto error;
 
 	list_add(&entry->link, &((*add_after_entry)->link));
@@ -1081,11 +1092,58 @@
 
 	mutex_lock(&ipa3_ctx->lock);
 	for (i = 0; i < rules->num_rules; i++) {
+		rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 		if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
 					&rules->rules[i].rule,
 					rules->rules[i].at_rear,
-					&rules->rules[i].rt_rule_hdl)) {
-			IPAERR_RL("failed to add rt rule %d\n", i);
+					&rules->rules[i].rt_rule_hdl,
+					0)) {
+			IPAERR("failed to add rt rule %d\n", i);
+			rules->rules[i].status = IPA_RT_STATUS_OF_ADD_FAILED;
+		} else {
+			rules->rules[i].status = 0;
+		}
+	}
+
+	if (rules->commit)
+		if (ipa3_ctx->ctrl->ipa3_commit_rt(rules->ip)) {
+			ret = -EPERM;
+			goto bail;
+		}
+
+	ret = 0;
+bail:
+	mutex_unlock(&ipa3_ctx->lock);
+	return ret;
+}
+
+/**
+ * ipa3_add_rt_rule_ext() - Add the specified routing rules to SW with rule id
+ * and optionally commit to IPA HW
+ * @rules:	[inout] set of routing rules to add
+ *
+ * Returns:	0 on success, negative on failure
+ *
+ * Note:	Should not be called from atomic context
+ */
+int ipa3_add_rt_rule_ext(struct ipa_ioc_add_rt_rule_ext *rules)
+{
+	int i;
+	int ret;
+
+	if (rules == NULL || rules->num_rules == 0 || rules->ip >= IPA_IP_MAX) {
+		IPAERR("bad parm\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&ipa3_ctx->lock);
+	for (i = 0; i < rules->num_rules; i++) {
+		if (__ipa_add_rt_rule(rules->ip, rules->rt_tbl_name,
+					&rules->rules[i].rule,
+					rules->rules[i].at_rear,
+					&rules->rules[i].rt_rule_hdl,
+					rules->rules[i].rule_id)) {
+			IPAERR("failed to add rt rule %d\n", i);
 			rules->rules[i].status = IPA_RT_STATUS_OF_ADD_FAILED;
 		} else {
 			rules->rules[i].status = 0;
@@ -1126,7 +1184,7 @@
 	}
 
 	mutex_lock(&ipa3_ctx->lock);
-
+	rules->rt_tbl_name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	tbl = __ipa3_find_rt_tbl(rules->ip, rules->rt_tbl_name);
 	if (tbl == NULL || (tbl->cookie != IPA_RT_TBL_COOKIE)) {
 		IPAERR_RL("failed finding rt tbl name = %s\n",
@@ -1135,8 +1193,8 @@
 		goto bail;
 	}
 
-	if (tbl->rule_cnt <= 0) {
-		IPAERR_RL("tbl->rule_cnt <= 0");
+	if (!tbl->rule_cnt) {
+		IPAERR_RL("tbl->rule_cnt == 0");
 		ret = -EINVAL;
 		goto bail;
 	}
@@ -1149,6 +1207,13 @@
 		goto bail;
 	}
 
+	if (entry->cookie != IPA_RT_RULE_COOKIE) {
+		IPAERR_RL("Invalid cookie value =  %u rule %d in rt tbls\n",
+			entry->cookie, rules->add_after_hdl);
+		ret = -EINVAL;
+		goto bail;
+	}
+
 	if (entry->tbl != tbl) {
 		IPAERR_RL("given rt rule does not match the table\n");
 		ret = -EINVAL;
@@ -1226,7 +1291,9 @@
 	IPADBG("del rt rule tbl_idx=%d rule_cnt=%d rule_id=%d\n ref_cnt=%u",
 		entry->tbl->idx, entry->tbl->rule_cnt,
 		entry->rule_id, entry->tbl->ref_cnt);
-	idr_remove(entry->tbl->rule_ids, entry->rule_id);
+		/* if rule id was allocated from idr, remove it */
+	if (!entry->rule_id_valid)
+		idr_remove(entry->tbl->rule_ids, entry->rule_id);
 	if (entry->tbl->rule_cnt == 0 && entry->tbl->ref_cnt == 0) {
 		if (__ipa_del_rt_tbl(entry->tbl))
 			IPAERR_RL("fail to del RT tbl\n");
@@ -1366,7 +1433,7 @@
 		list_for_each_entry_safe(rule, rule_next,
 					 &tbl->head_rt_rule_list, link) {
 			if (ipa3_id_find(rule->id) == NULL) {
-				WARN_ON(1);
+				WARN_ON_RATELIMIT_IPA(1);
 				mutex_unlock(&ipa3_ctx->lock);
 				return -EFAULT;
 			}
@@ -1394,7 +1461,7 @@
 		}
 
 		if (ipa3_id_find(tbl->id) == NULL) {
-			WARN_ON(1);
+			WARN_ON_RATELIMIT_IPA(1);
 			mutex_unlock(&ipa3_ctx->lock);
 			return -EFAULT;
 		}
@@ -1449,10 +1516,11 @@
 		return -EINVAL;
 	}
 	mutex_lock(&ipa3_ctx->lock);
+	lookup->name[IPA_RESOURCE_NAME_MAX-1] = '\0';
 	entry = __ipa3_find_rt_tbl(lookup->ip, lookup->name);
 	if (entry && entry->cookie == IPA_RT_TBL_COOKIE) {
 		if (entry->ref_cnt == U32_MAX) {
-			IPAERR("fail: ref count crossed limit\n");
+			IPAERR_RL("fail: ref count crossed limit\n");
 			goto ret;
 		}
 		entry->ref_cnt++;
@@ -1504,7 +1572,7 @@
 	else if (entry->set == &ipa3_ctx->rt_tbl_set[IPA_IP_v6])
 		ip = IPA_IP_v6;
 	else {
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		result = -EINVAL;
 		goto ret;
 	}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
index 44afb28..8d415a1 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_offload_i.h
@@ -26,6 +26,9 @@
 #define IPA_NTN_TX_DIR 1
 #define IPA_NTN_RX_DIR 2
 
+#define IPA_WDI3_TX_DIR 1
+#define IPA_WDI3_RX_DIR 2
+
 /**
  *  @brief   Enum value determined based on the feature it
  *           corresponds to
@@ -48,9 +51,9 @@
  * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
  * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
  * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
- * @IPA_HW_FEATURE_ZIP: Feature related to CMP/DCMP operation in IPA HW
  * @IPA_HW_FEATURE_NTN : Feature related to NTN operation in IPA HW
  * @IPA_HW_FEATURE_OFFLOAD : Feature related to NTN operation in IPA HW
+ * @IPA_HW_FEATURE_WDI3 : Feature related to WDI operation in IPA HW
 */
 enum ipa3_hw_features {
 	IPA_HW_FEATURE_COMMON		=	0x0,
@@ -59,7 +62,8 @@
 	IPA_HW_FEATURE_WDI		=	0x3,
 	IPA_HW_FEATURE_ZIP		=	0x4,
 	IPA_HW_FEATURE_NTN		=	0x5,
-	IPA_HW_FEATURE_OFFLOAD	=	0x6,
+	IPA_HW_FEATURE_OFFLOAD		=	0x6,
+	IPA_HW_FEATURE_WDI3		=	0x7,
 	IPA_HW_FEATURE_MAX		=	IPA_HW_NUM_FEATURES
 };
 
@@ -343,6 +347,33 @@
 
 } __packed;
 
+struct IpaHwWdi3SetUpCmdData_t {
+	u32  transfer_ring_base_pa;
+	u32  transfer_ring_base_pa_hi;
+
+	u32  transfer_ring_size;
+
+	u32  transfer_ring_doorbell_pa;
+	u32  transfer_ring_doorbell_pa_hi;
+
+	u32  event_ring_base_pa;
+	u32  event_ring_base_pa_hi;
+
+	u32  event_ring_size;
+
+	u32  event_ring_doorbell_pa;
+	u32  event_ring_doorbell_pa_hi;
+
+	u16  num_pkt_buffers;
+	u8   ipa_pipe_number;
+	u8   dir;
+
+	u16  pkt_offset;
+	u16  reserved0;
+
+	u32  desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+} __packed;
+
 /**
  * struct Ipa3HwNtnCommonChCmdData_t - Structure holding the
  * parameters for Ntn Tear down command data params
@@ -357,6 +388,13 @@
 	uint32_t raw32b;
 } __packed;
 
+union IpaHwWdi3CommonChCmdData_t {
+	struct IpaHwWdi3CommonChCmdParams_t {
+		u32  ipa_pipe_number :8;
+		u32  reserved        :24;
+	} __packed params;
+	u32 raw32b;
+} __packed;
 
 /**
  * struct Ipa3HwNTNErrorEventData_t - Structure holding the
@@ -447,13 +485,30 @@
  * the offload commands from CPU
  * @IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP : Command to set up
  *				Offload protocol's Tx/Rx Path
- * @IPA_CPU_2_HW_CMD_OFFLOAD_RX_SET_UP : Command to tear down
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN : Command to tear down
+ *				Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE : Command to enable
+ *				Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE : Command to disable
+ *				Offload protocol's Tx/ Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND : Command to suspend
+ *				Offload protocol's Tx/Rx Path
+ * @IPA_CPU_2_HW_CMD_OFFLOAD_RESUME : Command to resume
  *				Offload protocol's Tx/ Rx Path
  */
 enum ipa_cpu_2_hw_offload_commands {
 	IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP  =
 		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 1),
-	IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN,
+	IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 2),
+	IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE  =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 3),
+	IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 4),
+	IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND  =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 5),
+	IPA_CPU_2_HW_CMD_OFFLOAD_RESUME =
+		FEATURE_ENUM_VAL(IPA_HW_FEATURE_OFFLOAD, 6),
 };
 
 
@@ -523,6 +578,7 @@
  */
 union IpaHwSetUpCmd {
 	struct Ipa3HwNtnSetUpCmdData_t NtnSetupCh_params;
+	struct IpaHwWdi3SetUpCmdData_t Wdi3SetupCh_params;
 } __packed;
 
 /**
@@ -553,6 +609,7 @@
  */
 union IpaHwCommonChCmd {
 	union Ipa3HwNtnCommonChCmdData_t NtnCommonCh_params;
+	union IpaHwWdi3CommonChCmdData_t Wdi3CommonCh_params;
 } __packed;
 
 struct IpaHwOffloadCommonChCmdData_t {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index c97d2b3..941e489 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -620,8 +620,9 @@
 		unsigned long iova, size_t len)
 {
 	IPADBG("--res_idx=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", res_idx,
-			&pa, iova, len);
-	wdi_res[res_idx].res = kzalloc(sizeof(struct ipa_wdi_res), GFP_KERNEL);
+		&pa, iova, len);
+	wdi_res[res_idx].res = kzalloc(sizeof(*wdi_res[res_idx].res),
+		GFP_KERNEL);
 	if (!wdi_res[res_idx].res)
 		BUG();
 	wdi_res[res_idx].nents = 1;
@@ -647,7 +648,8 @@
 		return;
 	}
 
-	wdi_res[res_idx].res = kcalloc(sgt->nents, sizeof(struct ipa_wdi_res),
+	wdi_res[res_idx].res = kcalloc(sgt->nents,
+		sizeof(*wdi_res[res_idx].res),
 			GFP_KERNEL);
 	if (!wdi_res[res_idx].res)
 		BUG();
@@ -672,19 +674,19 @@
 		unsigned long *iova)
 {
 	/* support for SMMU on WLAN but no SMMU on IPA */
-	if (wlan_smmu_en && ipa3_ctx->smmu_s1_bypass) {
+	if (wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
 		IPAERR("Unsupported SMMU pairing\n");
 		return -EINVAL;
 	}
 
 	/* legacy: no SMMUs on either end */
-	if (!wlan_smmu_en && ipa3_ctx->smmu_s1_bypass) {
+	if (!wlan_smmu_en && ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
 		*iova = pa;
 		return 0;
 	}
 
 	/* no SMMU on WLAN but SMMU on IPA */
-	if (!wlan_smmu_en && !ipa3_ctx->smmu_s1_bypass) {
+	if (!wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
 		if (ipa_create_uc_smmu_mapping_pa(pa, len,
 			(res_idx == IPA_WDI_CE_DB_RES) ? true : false, iova)) {
 			IPAERR("Fail to create mapping res %d\n", res_idx);
@@ -695,7 +697,7 @@
 	}
 
 	/* SMMU on WLAN and SMMU on IPA */
-	if (wlan_smmu_en && !ipa3_ctx->smmu_s1_bypass) {
+	if (wlan_smmu_en && !ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_UC]) {
 		switch (res_idx) {
 		case IPA_WDI_RX_RING_RP_RES:
 		case IPA_WDI_RX_COMP_RING_WP_RES:
@@ -1821,6 +1823,11 @@
 		return -EINVAL;
 	}
 
+	if (ipa3_ctx->s1_bypass_arr[IPA_SMMU_CB_WLAN]) {
+		IPAERR("IPA SMMU not enabled\n");
+		return -EINVAL;
+	}
+
 	for (i = 0; i < num_buffers; i++) {
 		IPADBG("i=%d pa=0x%pa iova=0x%lx sz=0x%zx\n", i,
 			&info[i].pa, info[i].iova, info[i].size);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index 5206337..065a099 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1113,12 +1113,6 @@
 			{ 31, 31, 8, 8, IPA_EE_AP } },
 
 	/* IPA_4_0 */
-	[IPA_4_0][IPA_CLIENT_WLAN1_PROD]          = {
-			true, IPA_v4_0_GROUP_UL_DL,
-			true,
-			IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
-			QMB_MASTER_SELECT_DDR,
-			{ 7, 9, 8, 16, IPA_EE_AP } },
 	[IPA_4_0][IPA_CLIENT_USB_PROD]            = {
 			true, IPA_v4_0_GROUP_UL_DL,
 			true,
@@ -1348,13 +1342,13 @@
 			true,
 			IPA_DPS_HPS_SEQ_TYPE_2ND_PKT_PROCESS_PASS_NO_DEC_UCP,
 			QMB_MASTER_SELECT_DDR,
-			{ 3, 0, 16, 32, IPA_EE_Q6 } },
+			{ 6, 2, 12, 24, IPA_EE_Q6 } },
 	[IPA_4_0_MHI][IPA_CLIENT_Q6_WAN_PROD]         = {
 			true, IPA_v4_0_GROUP_UL_DL,
 			true,
 			IPA_DPS_HPS_SEQ_TYPE_PKT_PROCESS_NO_DEC_UCP,
 			QMB_MASTER_SELECT_DDR,
-			{ 6, 2, 12, 24, IPA_EE_Q6 } },
+			{ 3, 0, 16, 32, IPA_EE_Q6 } },
 	[IPA_4_0_MHI][IPA_CLIENT_Q6_CMD_PROD]	  = {
 			true, IPA_v4_0_MHI_GROUP_PCIE,
 			false,
@@ -2787,7 +2781,7 @@
 		ep_hdr->hdr_ofst_pkt_size_valid,
 		ep_hdr->hdr_additional_const_len);
 
-	IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x",
+	IPADBG("ofst_metadata=0x%x, ofst_metadata_valid=%d, len=0x%x\n",
 		ep_hdr->hdr_ofst_metadata,
 		ep_hdr->hdr_ofst_metadata_valid,
 		ep_hdr->hdr_len);
@@ -2946,7 +2940,7 @@
 	if (!IPA_CLIENT_IS_CONS(ep_mode->dst))
 		ep = ipa3_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS);
 
-	IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d",
+	IPADBG("pipe=%d mode=%d(%s), dst_client_number=%d\n",
 			clnt_hdl,
 			ep_mode->mode,
 			ipa3_get_mode_type_str(ep_mode->mode),
@@ -3908,13 +3902,10 @@
 		res = -ENOMEM;
 		goto fail_free_tag_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	/* IP_PACKET_INIT IC for tag status to be sent to apps */
 	pktinit_cmd.destination_pipe_index =
@@ -3926,13 +3917,10 @@
 		res = -ENOMEM;
 		goto fail_free_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	/* status IC */
 	status.tag = IPA_COOKIE;
@@ -3943,13 +3931,10 @@
 		res = -ENOMEM;
 		goto fail_free_desc;
 	}
-	tag_desc[desc_idx].opcode = cmd_pyld->opcode;
-	tag_desc[desc_idx].pyld = cmd_pyld->data;
-	tag_desc[desc_idx].len = cmd_pyld->len;
-	tag_desc[desc_idx].type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&tag_desc[desc_idx], cmd_pyld);
 	tag_desc[desc_idx].callback = ipa3_tag_destroy_imm;
 	tag_desc[desc_idx].user1 = cmd_pyld;
-	desc_idx++;
+	++desc_idx;
 
 	comp = kzalloc(sizeof(*comp), GFP_KERNEL);
 	if (!comp) {
@@ -3972,6 +3957,12 @@
 
 	memcpy(skb_put(dummy_skb, sizeof(comp)), &comp, sizeof(comp));
 
+	if (desc_idx >= IPA_TAG_MAX_DESC) {
+		IPAERR("number of commands is out of range\n");
+		res = -ENOBUFS;
+		goto fail_free_skb;
+	}
+
 	tag_desc[desc_idx].pyld = dummy_skb->data;
 	tag_desc[desc_idx].len = dummy_skb->len;
 	tag_desc[desc_idx].type = IPA_DATA_DESC_SKB;
@@ -3984,7 +3975,7 @@
 	if (res) {
 		IPAERR("failed to send TAG packets %d\n", res);
 		res = -ENOMEM;
-		goto fail_free_comp;
+		goto fail_free_skb;
 	}
 	kfree(tag_desc);
 	tag_desc = NULL;
@@ -4012,6 +4003,8 @@
 
 	return 0;
 
+fail_free_skb:
+	kfree_skb(dummy_skb);
 fail_free_comp:
 	kfree(comp);
 fail_free_desc:
@@ -4082,19 +4075,16 @@
 			goto fail_alloc_reg_write_agg_close;
 		}
 
-		desc[desc_idx].opcode = cmd_pyld->opcode;
-		desc[desc_idx].pyld = cmd_pyld->data;
-		desc[desc_idx].len = cmd_pyld->len;
-		desc[desc_idx].type = IPA_IMM_CMD_DESC;
+		ipa3_init_imm_cmd_desc(&desc[desc_idx], cmd_pyld);
 		desc[desc_idx].callback = ipa3_tag_destroy_imm;
 		desc[desc_idx].user1 = cmd_pyld;
-		desc_idx++;
+		++desc_idx;
 	}
 
 	return desc_idx;
 
 fail_alloc_reg_write_agg_close:
-	for (i = 0; i < desc_idx; i++)
+	for (i = 0; i < desc_idx; ++i)
 		if (desc[desc_idx].callback)
 			desc[desc_idx].callback(desc[desc_idx].user1,
 				desc[desc_idx].user2);
@@ -4198,7 +4188,9 @@
 	mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
 	if (ipa3_ctx->q6_proxy_clk_vote_valid) {
 		IPA_ACTIVE_CLIENTS_DEC_SPECIAL("PROXY_CLK_VOTE");
-		ipa3_ctx->q6_proxy_clk_vote_valid = false;
+		ipa3_ctx->q6_proxy_clk_vote_cnt--;
+		if (ipa3_ctx->q6_proxy_clk_vote_cnt == 0)
+			ipa3_ctx->q6_proxy_clk_vote_valid = false;
 	}
 	mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
 }
@@ -4214,8 +4206,10 @@
 		return;
 
 	mutex_lock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
-	if (!ipa3_ctx->q6_proxy_clk_vote_valid) {
+	if (!ipa3_ctx->q6_proxy_clk_vote_valid ||
+		(ipa3_ctx->q6_proxy_clk_vote_cnt > 0)) {
 		IPA_ACTIVE_CLIENTS_INC_SPECIAL("PROXY_CLK_VOTE");
+		ipa3_ctx->q6_proxy_clk_vote_cnt++;
 		ipa3_ctx->q6_proxy_clk_vote_valid = true;
 	}
 	mutex_unlock(&ipa3_ctx->q6_proxy_clk_vote_mutex);
@@ -4377,10 +4371,17 @@
 	api_ctrl->ipa_mdfy_flt_rule = ipa3_mdfy_flt_rule;
 	api_ctrl->ipa_commit_flt = ipa3_commit_flt;
 	api_ctrl->ipa_reset_flt = ipa3_reset_flt;
-	api_ctrl->allocate_nat_device = ipa3_allocate_nat_device;
+	api_ctrl->ipa_allocate_nat_device = ipa3_allocate_nat_device;
+	api_ctrl->ipa_allocate_nat_table = ipa3_allocate_nat_table;
+	api_ctrl->ipa_allocate_ipv6ct_table = ipa3_allocate_ipv6ct_table;
 	api_ctrl->ipa_nat_init_cmd = ipa3_nat_init_cmd;
+	api_ctrl->ipa_ipv6ct_init_cmd = ipa3_ipv6ct_init_cmd;
 	api_ctrl->ipa_nat_dma_cmd = ipa3_nat_dma_cmd;
+	api_ctrl->ipa_table_dma_cmd = ipa3_table_dma_cmd;
 	api_ctrl->ipa_nat_del_cmd = ipa3_nat_del_cmd;
+	api_ctrl->ipa_del_nat_table = ipa3_del_nat_table;
+	api_ctrl->ipa_del_ipv6ct_table = ipa3_del_ipv6ct_table;
+	api_ctrl->ipa_nat_mdfy_pdn = ipa3_nat_mdfy_pdn;
 	api_ctrl->ipa_send_msg = ipa3_send_msg;
 	api_ctrl->ipa_register_pull_msg = ipa3_register_pull_msg;
 	api_ctrl->ipa_deregister_pull_msg = ipa3_deregister_pull_msg;
@@ -4497,6 +4498,11 @@
 	api_ctrl->ipa_get_pdev = ipa3_get_pdev;
 	api_ctrl->ipa_ntn_uc_reg_rdyCB = ipa3_ntn_uc_reg_rdyCB;
 	api_ctrl->ipa_ntn_uc_dereg_rdyCB = ipa3_ntn_uc_dereg_rdyCB;
+	api_ctrl->ipa_conn_wdi3_pipes = ipa3_conn_wdi3_pipes;
+	api_ctrl->ipa_disconn_wdi3_pipes = ipa3_disconn_wdi3_pipes;
+	api_ctrl->ipa_enable_wdi3_pipes = ipa3_enable_wdi3_pipes;
+	api_ctrl->ipa_disable_wdi3_pipes = ipa3_disable_wdi3_pipes;
+	api_ctrl->ipa_tz_unlock_reg = ipa3_tz_unlock_reg;
 
 	return 0;
 }
@@ -4855,11 +4861,8 @@
 		IPADBG("ch %ld not empty\n", ep->gsi_chan_hdl);
 		/* queue a work to start polling if don't have one */
 		atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1);
-		if (!atomic_read(&ep->sys->curr_polling_state)) {
-			ipa3_inc_acquire_wakelock();
-			atomic_set(&ep->sys->curr_polling_state, 1);
-			queue_work(ep->sys->wq, &ep->sys->work);
-		}
+		if (!atomic_read(&ep->sys->curr_polling_state))
+			__ipa_gsi_irq_rx_scedule_poll(ep->sys);
 	}
 }
 
@@ -4995,12 +4998,9 @@
  */
 int ipa3_inject_dma_task_for_gsi(void)
 {
-	struct ipa3_desc desc = {0};
+	struct ipa3_desc desc;
 
-	desc.opcode = ipa3_ctx->dma_task_info.cmd_pyld->opcode;
-	desc.pyld = ipa3_ctx->dma_task_info.cmd_pyld->data;
-	desc.len = ipa3_ctx->dma_task_info.cmd_pyld->len;
-	desc.type = IPA_IMM_CMD_DESC;
+	ipa3_init_imm_cmd_desc(&desc, ipa3_ctx->dma_task_info.cmd_pyld);
 
 	IPADBG("sending 1B packet to IPA\n");
 	if (ipa3_send_cmd_timeout(1, &desc,
@@ -5323,3 +5323,14 @@
 	ipahal_write_reg_fields(IPA_IDLE_INDICATION_CFG,
 			&idle_indication_cfg);
 }
+
+void ipa3_init_imm_cmd_desc(struct ipa3_desc *desc,
+	struct ipahal_imm_cmd_pyld *cmd_pyld)
+{
+	memset(desc, 0, sizeof(*desc));
+	desc->opcode = cmd_pyld->opcode;
+	desc->pyld = cmd_pyld->data;
+	desc->len = cmd_pyld->len;
+	desc->type = IPA_IMM_CMD_DESC;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c b/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c
new file mode 100644
index 0000000..7801745
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_wdi3_i.c
@@ -0,0 +1,407 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "ipa_i.h"
+#include <linux/ipa_wdi3.h>
+
+#define IPA_HW_WDI3_RX_MBOX_START_INDEX 48
+#define IPA_HW_WDI3_TX_MBOX_START_INDEX 50
+
+static int ipa3_send_wdi3_setup_pipe_cmd(
+	struct ipa_wdi3_setup_info *info, u8 dir)
+{
+	int ipa_ep_idx;
+	int result = 0;
+	struct ipa_mem_buffer cmd;
+	struct IpaHwWdi3SetUpCmdData_t *wdi3_params;
+	struct IpaHwOffloadSetUpCmdData_t *cmd_data;
+
+	if (info == NULL) {
+		IPAERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	ipa_ep_idx = ipa_get_ep_mapping(info->client);
+	if (ipa_ep_idx == -1) {
+		IPAERR("fail to get ep idx.\n");
+		return -EFAULT;
+	}
+
+	IPADBG("client=%d ep=%d\n", info->client, ipa_ep_idx);
+	IPADBG("ring_base_pa = 0x%pad\n", &info->transfer_ring_base_pa);
+	IPADBG("ring_size = %hu\n", info->transfer_ring_size);
+	IPADBG("ring_db_pa = 0x%pad\n", &info->transfer_ring_doorbell_pa);
+	IPADBG("evt_ring_base_pa = 0x%pad\n", &info->event_ring_base_pa);
+	IPADBG("evt_ring_size = %hu\n", info->event_ring_size);
+	IPADBG("evt_ring_db_pa = 0x%pad\n", &info->event_ring_doorbell_pa);
+	IPADBG("num_pkt_buffers = %hu\n", info->num_pkt_buffers);
+	IPADBG("pkt_offset = %d\n", info->pkt_offset);
+
+	cmd.size = sizeof(*cmd_data);
+	cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+			&cmd.phys_base, GFP_KERNEL);
+	if (cmd.base == NULL) {
+		IPAERR("fail to get DMA memory.\n");
+		return -ENOMEM;
+	}
+
+	cmd_data = (struct IpaHwOffloadSetUpCmdData_t *)cmd.base;
+	cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+	wdi3_params = &cmd_data->SetupCh_params.Wdi3SetupCh_params;
+	wdi3_params->transfer_ring_base_pa = (u32)info->transfer_ring_base_pa;
+	wdi3_params->transfer_ring_base_pa_hi =
+		(u32)((u64)info->transfer_ring_base_pa >> 32);
+	wdi3_params->transfer_ring_size = info->transfer_ring_size;
+	wdi3_params->transfer_ring_doorbell_pa =
+		(u32)info->transfer_ring_doorbell_pa;
+	wdi3_params->transfer_ring_doorbell_pa_hi =
+		(u32)((u64)info->transfer_ring_doorbell_pa >> 32);
+	wdi3_params->event_ring_base_pa = (u32)info->event_ring_base_pa;
+	wdi3_params->event_ring_base_pa_hi =
+		(u32)((u64)info->event_ring_base_pa >> 32);
+	wdi3_params->event_ring_size = info->event_ring_size;
+	wdi3_params->event_ring_doorbell_pa =
+		(u32)info->event_ring_doorbell_pa;
+	wdi3_params->event_ring_doorbell_pa_hi =
+		(u32)((u64)info->event_ring_doorbell_pa >> 32);
+	wdi3_params->num_pkt_buffers = info->num_pkt_buffers;
+	wdi3_params->ipa_pipe_number = ipa_ep_idx;
+	wdi3_params->dir = dir;
+	wdi3_params->pkt_offset = info->pkt_offset;
+	memcpy(wdi3_params->desc_format_template, info->desc_format_template,
+		sizeof(wdi3_params->desc_format_template));
+
+	result = ipa3_uc_send_cmd((u32)(cmd.phys_base),
+				IPA_CPU_2_HW_CMD_OFFLOAD_CHANNEL_SET_UP,
+				IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+				false, 10*HZ);
+	if (result) {
+		IPAERR("uc setup channel cmd failed: %d\n", result);
+		result = -EFAULT;
+	}
+
+	dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+	return result;
+}
+
+int ipa3_conn_wdi3_pipes(struct ipa_wdi3_conn_in_params *in,
+	struct ipa_wdi3_conn_out_params *out)
+{
+	struct ipa3_ep_context *ep_rx;
+	struct ipa3_ep_context *ep_tx;
+	int ipa_ep_idx_rx;
+	int ipa_ep_idx_tx;
+	int result = 0;
+
+	if (in == NULL || out == NULL) {
+		IPAERR("invalid input\n");
+		return -EINVAL;
+	}
+
+	ipa_ep_idx_rx = ipa_get_ep_mapping(in->rx.client);
+	ipa_ep_idx_tx = ipa_get_ep_mapping(in->tx.client);
+	if (ipa_ep_idx_rx == -1 || ipa_ep_idx_tx == -1) {
+		IPAERR("fail to alloc EP.\n");
+		return -EFAULT;
+	}
+	if (ipa_ep_idx_rx >= IPA3_MAX_NUM_PIPES ||
+		ipa_ep_idx_tx >= IPA3_MAX_NUM_PIPES) {
+		IPAERR("ep out of range.\n");
+		return -EFAULT;
+	}
+
+	ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+	ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+
+	if (ep_rx->valid || ep_tx->valid) {
+		IPAERR("EP already allocated.\n");
+		return -EFAULT;
+	}
+
+	memset(ep_rx, 0, offsetof(struct ipa3_ep_context, sys));
+	memset(ep_tx, 0, offsetof(struct ipa3_ep_context, sys));
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+	/* setup rx ep cfg */
+	ep_rx->valid = 1;
+	ep_rx->client = in->rx.client;
+	result = ipa3_disable_data_path(ipa_ep_idx_rx);
+	if (result) {
+		IPAERR("disable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_rx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+	ep_rx->client_notify = in->notify;
+	ep_rx->priv = in->priv;
+
+	memcpy(&ep_rx->cfg, &in->rx.ipa_ep_cfg, sizeof(ep_rx->cfg));
+
+	if (ipa3_cfg_ep(ipa_ep_idx_rx, &ep_rx->cfg)) {
+		IPAERR("fail to setup rx pipe cfg\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	if (ipa3_send_wdi3_setup_pipe_cmd(&in->rx, IPA_WDI3_RX_DIR)) {
+		IPAERR("fail to send cmd to uc for rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	ipa3_install_dflt_flt_rules(ipa_ep_idx_rx);
+	out->rx_uc_db_pa = ipa3_ctx->ipa_wrapper_base +
+		ipahal_get_reg_base() +
+		ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
+		IPA_HW_WDI3_RX_MBOX_START_INDEX/32,
+		IPA_HW_WDI3_RX_MBOX_START_INDEX % 32);
+
+	IPADBG("client %d (ep: %d) connected\n", in->rx.client,
+		ipa_ep_idx_rx);
+
+	/* setup dl ep cfg */
+	ep_tx->valid = 1;
+	ep_tx->client = in->tx.client;
+	result = ipa3_disable_data_path(ipa_ep_idx_tx);
+	if (result) {
+		IPAERR("disable data path failed res=%d ep=%d.\n", result,
+			ipa_ep_idx_tx);
+		result = -EFAULT;
+		goto fail;
+	}
+
+	memcpy(&ep_tx->cfg, &in->tx.ipa_ep_cfg, sizeof(ep_tx->cfg));
+
+	if (ipa3_cfg_ep(ipa_ep_idx_tx, &ep_tx->cfg)) {
+		IPAERR("fail to setup tx pipe cfg\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	if (ipa3_send_wdi3_setup_pipe_cmd(&in->tx, IPA_WDI3_TX_DIR)) {
+		IPAERR("fail to send cmd to uc for tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	out->tx_uc_db_pa = ipa3_ctx->ipa_wrapper_base +
+		ipahal_get_reg_base() +
+		ipahal_get_reg_mn_ofst(IPA_UC_MAILBOX_m_n,
+		IPA_HW_WDI3_TX_MBOX_START_INDEX/32,
+		IPA_HW_WDI3_TX_MBOX_START_INDEX % 32);
+	out->tx_uc_db_va = ioremap(out->tx_uc_db_pa, 4);
+	if (!out->tx_uc_db_va) {
+		IPAERR("fail to ioremap tx uc db\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	IPADBG("client %d (ep: %d) connected\n", in->tx.client,
+		ipa_ep_idx_tx);
+
+fail:
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+	return result;
+}
+
+static int ipa3_send_wdi3_common_ch_cmd(int ipa_ep_idx, int command)
+{
+	struct ipa_mem_buffer cmd;
+	struct IpaHwOffloadCommonChCmdData_t *cmd_data;
+	union IpaHwWdi3CommonChCmdData_t *wdi3;
+	int result = 0;
+
+	cmd.size = sizeof(*cmd_data);
+	cmd.base = dma_alloc_coherent(ipa3_ctx->uc_pdev, cmd.size,
+		&cmd.phys_base, GFP_KERNEL);
+	if (cmd.base == NULL) {
+		IPAERR("fail to get DMA memory.\n");
+		return -ENOMEM;
+	}
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+	/* enable the TX pipe */
+	cmd_data = (struct IpaHwOffloadCommonChCmdData_t *)cmd.base;
+	cmd_data->protocol = IPA_HW_FEATURE_WDI3;
+
+	wdi3 = &cmd_data->CommonCh_params.Wdi3CommonCh_params;
+	wdi3->params.ipa_pipe_number = ipa_ep_idx;
+	result = ipa3_uc_send_cmd((u32)(cmd.phys_base), command,
+				IPA_HW_2_CPU_OFFLOAD_CMD_STATUS_SUCCESS,
+				false, 10*HZ);
+	if (result) {
+		result = -EFAULT;
+		goto fail;
+	}
+
+fail:
+	dma_free_coherent(ipa3_ctx->uc_pdev, cmd.size, cmd.base, cmd.phys_base);
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+	return result;
+}
+
+int ipa3_disconn_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa3_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	if (ipa_ep_idx_tx < 0 || ipa_ep_idx_tx >= IPA3_MAX_NUM_PIPES ||
+		ipa_ep_idx_rx < 0 || ipa_ep_idx_rx >= IPA3_MAX_NUM_PIPES) {
+		IPAERR("invalid ipa ep index\n");
+		return -EINVAL;
+	}
+
+	ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+	/* tear down tx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+		IPAERR("fail to tear down tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	ipa3_disable_data_path(ipa_ep_idx_tx);
+	memset(ep_tx, 0, sizeof(struct ipa3_ep_context));
+	IPADBG("tx client (ep: %d) disconnected\n", ipa_ep_idx_tx);
+
+	/* tear down rx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_TEAR_DOWN)) {
+		IPAERR("fail to tear down rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+	ipa3_disable_data_path(ipa_ep_idx_rx);
+	ipa3_delete_dflt_flt_rules(ipa_ep_idx_rx);
+	memset(ep_rx, 0, sizeof(struct ipa3_ep_context));
+	IPADBG("rx client (ep: %d) disconnected\n", ipa_ep_idx_rx);
+
+fail:
+	return result;
+}
+
+int ipa3_enable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa3_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+	/* enable tx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+		IPAERR("fail to enable tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* resume tx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+		IPAERR("fail to resume tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* enable rx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_ENABLE)) {
+		IPAERR("fail to enable rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* resume rx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_RESUME)) {
+		IPAERR("fail to resume rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+	/* enable data path */
+	result = ipa3_enable_data_path(ipa_ep_idx_rx);
+	if (result) {
+		IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_rx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+
+	result = ipa3_enable_data_path(ipa_ep_idx_tx);
+	if (result) {
+		IPAERR("enable data path failed res=%d clnt=%d.\n", result,
+			ipa_ep_idx_tx);
+		IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+		return -EFAULT;
+	}
+
+	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+
+fail:
+	return result;
+}
+
+int ipa3_disable_wdi3_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx)
+{
+	struct ipa3_ep_context *ep_tx, *ep_rx;
+	int result = 0;
+
+	IPADBG("ep_tx = %d\n", ipa_ep_idx_tx);
+	IPADBG("ep_rx = %d\n", ipa_ep_idx_rx);
+
+	ep_tx = &ipa3_ctx->ep[ipa_ep_idx_tx];
+	ep_rx = &ipa3_ctx->ep[ipa_ep_idx_rx];
+
+	/* suspend tx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+		IPAERR("fail to suspend tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* disable tx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_tx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+		IPAERR("fail to disable tx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* suspend rx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_SUSPEND)) {
+		IPAERR("fail to suspend rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+	/* disable rx pipe */
+	if (ipa3_send_wdi3_common_ch_cmd(ipa_ep_idx_rx,
+		IPA_CPU_2_HW_CMD_OFFLOAD_DISABLE)) {
+		IPAERR("fail to disable rx pipe\n");
+		result = -EFAULT;
+		goto fail;
+	}
+
+fail:
+	return result;
+}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
index 67e491b..869ee7e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/Makefile
@@ -1,3 +1,3 @@
 obj-$(CONFIG_IPA3) += ipa_hal.o
 
-ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o
+ipa_hal-y := ipahal.o ipahal_reg.o ipahal_fltrt.o ipahal_hw_stats.o ipahal_nat.o
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index 56fed2a..d015b22 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -16,7 +16,7 @@
 #include "ipahal_reg_i.h"
 #include "ipahal_fltrt_i.h"
 #include "ipahal_hw_stats_i.h"
-
+#include "ipahal_nat_i.h"
 
 struct ipahal_context *ipahal_ctx;
 
@@ -35,6 +35,7 @@
 	__stringify(IPA_IMM_CMD_IP_PACKET_TAG_STATUS),
 	__stringify(IPA_IMM_CMD_DMA_TASK_32B_ADDR),
 	__stringify(IPA_IMM_CMD_TABLE_DMA),
+	__stringify(IPA_IMM_CMD_IP_V6_CT_INIT)
 };
 
 static const char *ipahal_pkt_status_exception_to_str
@@ -352,8 +353,8 @@
 {
 	struct ipahal_imm_cmd_pyld *pyld;
 	struct ipa_imm_cmd_hw_nat_dma *data;
-	struct ipahal_imm_cmd_nat_dma *nat_params =
-		(struct ipahal_imm_cmd_nat_dma *)params;
+	struct ipahal_imm_cmd_table_dma *nat_params =
+		(struct ipahal_imm_cmd_table_dma *)params;
 
 	pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
 	if (unlikely(!pyld)) {
@@ -519,24 +520,55 @@
 	pyld->len = sizeof(*data);
 	data = (struct ipa_imm_cmd_hw_ip_v4_nat_init *)pyld->data;
 
-	data->ipv4_rules_addr = nat4_params->ipv4_rules_addr;
+	data->ipv4_rules_addr = nat4_params->table_init.base_table_addr;
 	data->ipv4_expansion_rules_addr =
-		nat4_params->ipv4_expansion_rules_addr;
+		nat4_params->table_init.expansion_table_addr;
 	data->index_table_addr = nat4_params->index_table_addr;
 	data->index_table_expansion_addr =
 		nat4_params->index_table_expansion_addr;
-	data->table_index = nat4_params->table_index;
+	data->table_index = nat4_params->table_init.table_index;
 	data->ipv4_rules_addr_type =
-		nat4_params->ipv4_rules_addr_shared ? 1 : 0;
+		nat4_params->table_init.base_table_addr_shared ? 1 : 0;
 	data->ipv4_expansion_rules_addr_type =
-		nat4_params->ipv4_expansion_rules_addr_shared ? 1 : 0;
+		nat4_params->table_init.expansion_table_addr_shared ? 1 : 0;
 	data->index_table_addr_type =
 		nat4_params->index_table_addr_shared ? 1 : 0;
 	data->index_table_expansion_addr_type =
 		nat4_params->index_table_expansion_addr_shared ? 1 : 0;
-	data->size_base_tables = nat4_params->size_base_tables;
-	data->size_expansion_tables = nat4_params->size_expansion_tables;
-	data->public_ip_addr = nat4_params->public_ip_addr;
+	data->size_base_tables = nat4_params->table_init.size_base_table;
+	data->size_expansion_tables =
+		nat4_params->table_init.size_expansion_table;
+	data->public_addr_info = nat4_params->public_addr_info;
+
+	return pyld;
+}
+
+static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_ip_v6_ct_init(
+	enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
+{
+	struct ipahal_imm_cmd_pyld *pyld;
+	struct ipa_imm_cmd_hw_ip_v6_ct_init *data;
+	struct ipahal_imm_cmd_ip_v6_ct_init *ipv6ct_params =
+		(struct ipahal_imm_cmd_ip_v6_ct_init *)params;
+
+	pyld = IPAHAL_MEM_ALLOC(sizeof(*pyld) + sizeof(*data), is_atomic_ctx);
+	if (unlikely(!pyld))
+		return pyld;
+	pyld->opcode = ipahal_imm_cmd_get_opcode(cmd);
+	pyld->len = sizeof(*data);
+	data = (struct ipa_imm_cmd_hw_ip_v6_ct_init *)pyld->data;
+
+	data->table_addr = ipv6ct_params->table_init.base_table_addr;
+	data->expansion_table_addr =
+		ipv6ct_params->table_init.expansion_table_addr;
+	data->table_index = ipv6ct_params->table_init.table_index;
+	data->table_addr_type =
+		ipv6ct_params->table_init.base_table_addr_shared ? 1 : 0;
+	data->expansion_table_addr_type =
+		ipv6ct_params->table_init.expansion_table_addr_shared ? 1 : 0;
+	data->size_base_table = ipv6ct_params->table_init.size_base_table;
+	data->size_expansion_table =
+		ipv6ct_params->table_init.size_expansion_table;
 
 	return pyld;
 }
@@ -595,6 +627,15 @@
 	return pyld;
 }
 
+static struct ipahal_imm_cmd_pyld *ipa_imm_cmd_construct_dummy(
+	enum ipahal_imm_cmd_name cmd, const void *params, bool is_atomic_ctx)
+{
+	IPAHAL_ERR("no construct function for IMM_CMD=%s, IPA ver %d\n",
+		ipahal_imm_cmd_name_str(cmd), ipahal_ctx->hw_type);
+	WARN_ON(1);
+	return NULL;
+}
+
 /*
  * struct ipahal_imm_cmd_obj - immediate command H/W information for
  *  specific IPA version
@@ -668,14 +709,17 @@
 		12},
 	/* NAT_DMA was renamed to TABLE_DMA for IPAv4 */
 	[IPA_HW_v4_0][IPA_IMM_CMD_NAT_DMA] = {
-		NULL,
-		-1 },
+		ipa_imm_cmd_construct_dummy,
+		-1},
 	[IPA_HW_v4_0][IPA_IMM_CMD_TABLE_DMA] = {
 		ipa_imm_cmd_construct_table_dma_ipav4,
 		14},
 	[IPA_HW_v4_0][IPA_IMM_CMD_DMA_SHARED_MEM] = {
 		ipa_imm_cmd_construct_dma_shared_mem_v_4_0,
 		19},
+	[IPA_HW_v4_0][IPA_IMM_CMD_IP_V6_CT_INIT] = {
+		ipa_imm_cmd_construct_ip_v6_ct_init,
+		23}
 };
 
 /*
@@ -1517,13 +1561,21 @@
 	if (ipahal_hw_stats_init(ipa_hw_type)) {
 		IPAHAL_ERR("failed to init ipahal hw stats\n");
 		result = -EFAULT;
-		goto bail_free_ctx;
+		goto bail_free_fltrt;
+	}
+
+	if (ipahal_nat_init(ipa_hw_type)) {
+		IPAHAL_ERR("failed to init ipahal NAT\n");
+		result = -EFAULT;
+		goto bail_free_fltrt;
 	}
 
 	ipahal_debugfs_init();
 
 	return 0;
 
+bail_free_fltrt:
+	ipahal_fltrt_destroy();
 bail_free_ctx:
 	kfree(ipahal_ctx);
 	ipahal_ctx = NULL;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 56b884b..0c2697c 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -37,6 +37,7 @@
 	IPA_IMM_CMD_IP_PACKET_TAG_STATUS,
 	IPA_IMM_CMD_DMA_TASK_32B_ADDR,
 	IPA_IMM_CMD_TABLE_DMA,
+	IPA_IMM_CMD_IP_V6_CT_INIT,
 	IPA_IMM_CMD_MAX,
 };
 
@@ -46,19 +47,19 @@
  * struct ipahal_imm_cmd_ip_v4_filter_init - IP_V4_FILTER_INIT cmd payload
  * Inits IPv4 filter block.
  * @hash_rules_addr: Addr in sys mem where ipv4 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv4 hashable flt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable flt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv4 non-hashable flt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v4_filter_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -67,79 +68,98 @@
  * struct ipahal_imm_cmd_ip_v6_filter_init - IP_V6_FILTER_INIT cmd payload
  * Inits IPv6 filter block.
  * @hash_rules_addr: Addr in sys mem where ipv6 hashable flt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv6 hashable flt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable flt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv6 non-hashable flt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v6_filter_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
 
 /*
+ * struct ipahal_imm_cmd_nat_ipv6ct_init_common - NAT/IPv6CT table init command
+ *                                                common part
+ * @base_table_addr: Address in sys/shared mem where base table start
+ * @expansion_table_addr: Address in sys/shared mem where expansion table
+ *  starts. Entries that result in hash collision are located in this table.
+ * @base_table_addr_shared: base_table_addr in shared mem (if not, then sys)
+ * @expansion_table_addr_shared: expansion_rules_addr in
+ *  shared mem (if not, then sys)
+ * @size_base_table: Num of entries in the base table
+ * @size_expansion_table: Num of entries in the expansion table
+ * @table_index: For future support of multiple tables
+ */
+struct ipahal_imm_cmd_nat_ipv6ct_init_common {
+	u64 base_table_addr;
+	u64 expansion_table_addr;
+	bool base_table_addr_shared;
+	bool expansion_table_addr_shared;
+	u16 size_base_table;
+	u16 size_expansion_table;
+	u8 table_index;
+};
+
+/*
  * struct ipahal_imm_cmd_ip_v4_nat_init - IP_V4_NAT_INIT cmd payload
  * Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- *  cache address abd itger related parameters.
- * @table_index: For future support of multiple NAT tables
- * @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_rules_addr_shared: ipv4_rules_addr in shared mem (if not, then sys)
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- *  table starts. IPv4 NAT rules that result in NAT collision are located
- *  in this table.
- * @ipv4_expansion_rules_addr_shared: ipv4_expansion_rules_addr in
- *  shared mem (if not, then sys)
+ *  cache address and other related parameters.
+ * @table_init: table initialization parameters
  * @index_table_addr: Addr in sys/shared mem where index table, which points
  *  to NAT table starts
- * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
  * @index_table_expansion_addr: Addr in sys/shared mem where expansion index
  *  table starts
+ * @index_table_addr_shared: index_table_addr in shared mem (if not, then sys)
  * @index_table_expansion_addr_shared: index_table_expansion_addr in
  *  shared mem (if not, then sys)
- * @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
- *  idx tbl (each)
- * @public_ip_addr: public IP address
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ *                    IPA H/W >= 4.0 - PDN config table offset in SMEM
+ *                    IPA H/W < 4.0  - The public IP address
  */
 struct ipahal_imm_cmd_ip_v4_nat_init {
-	u8 table_index;
-	u64 ipv4_rules_addr;
-	bool ipv4_rules_addr_shared;
-	u64 ipv4_expansion_rules_addr;
-	bool ipv4_expansion_rules_addr_shared;
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
 	u64 index_table_addr;
-	bool index_table_addr_shared;
 	u64 index_table_expansion_addr;
+	bool index_table_addr_shared;
 	bool index_table_expansion_addr_shared;
-	u16 size_base_tables;
-	u16 size_expansion_tables;
-	u32 public_ip_addr;
+	u32 public_addr_info;
+};
+
+/*
+ * struct ipahal_imm_cmd_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT cmd payload
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ *  cache address and other related parameters.
+ * @table_init: table initialization parameters
+ */
+struct ipahal_imm_cmd_ip_v6_ct_init {
+	struct ipahal_imm_cmd_nat_ipv6ct_init_common table_init;
 };
 
 /*
  * struct ipahal_imm_cmd_ip_v4_routing_init - IP_V4_ROUTING_INIT cmd payload
  * Inits IPv4 routing table/structure - with the rules and other related params
  * @hash_rules_addr: Addr in sys mem where ipv4 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv4 hashable rt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv4 non-hashable rt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv4 non-hashable rt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v4_routing_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -148,19 +168,19 @@
  * struct ipahal_imm_cmd_ip_v6_routing_init - IP_V6_ROUTING_INIT cmd payload
  * Inits IPv6 routing table/structure - with the rules and other related params
  * @hash_rules_addr: Addr in sys mem where ipv6 hashable rt tbl starts
+ * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
  * @hash_rules_size: Size in bytes of the hashable tbl to cpy to local mem
  * @hash_local_addr: Addr in shared mem where ipv6 hashable rt tbl should
  *  be copied to
- * @nhash_rules_addr: Addr in sys mem where ipv6 non-hashable rt tbl starts
  * @nhash_rules_size: Size in bytes of the non-hashable tbl to cpy to local mem
  * @nhash_local_addr: Addr in shared mem where ipv6 non-hashable rt tbl should
  *  be copied to
  */
 struct ipahal_imm_cmd_ip_v6_routing_init {
 	u64 hash_rules_addr;
+	u64 nhash_rules_addr;
 	u32 hash_rules_size;
 	u32 hash_local_addr;
-	u64 nhash_rules_addr;
 	u32 nhash_rules_size;
 	u32 nhash_local_addr;
 };
@@ -189,36 +209,20 @@
 };
 
 /*
- * struct ipahal_imm_cmd_nat_dma - NAT_DMA cmd payload
- * Perform DMA operation on NAT related mem addressess. Copy data into
- *  different locations within NAT associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the NAT tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
- * @offset: offset in bytes from base addr to write 'data' to
- * @data: data to be written
- */
-struct ipahal_imm_cmd_nat_dma {
-	u8 table_index;
-	u8 base_addr;
-	u32 offset;
-	u16 data;
-};
-
-/*
  * struct ipahal_imm_cmd_table_dma - TABLE_DMA cmd payload
  * Perform DMA operation on NAT and IPV6 connection tracking related mem
- * addresses. Copy data into different locations within IPV6CT and NAT
+ * addresses. Copy data into different locations within IPv6CT and NAT
  * associated tbls. (For add/remove NAT rules)
- * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
- * @base_addr: Base addr to which the DMA operation should be performed.
  * @offset: offset in bytes from base addr to write 'data' to
  * @data: data to be written
+ * @table_index: NAT tbl index. Defines the tbl on which to perform DMA op.
+ * @base_addr: Base addr to which the DMA operation should be performed.
  */
 struct ipahal_imm_cmd_table_dma {
-	u8 table_index;
-	u8 base_addr;
 	u32 offset;
 	u16 data;
+	u8 table_index;
+	u8 base_addr;
 };
 
 /*
@@ -275,6 +279,7 @@
 /*
  * struct ipahal_imm_cmd_dma_shared_mem - DMA_SHARED_MEM cmd payload
  * Perform mem copy into or out of the SW area of IPA local mem
+ * @system_addr: Address in system memory
  * @size: Size in bytes of data to copy. Expected size is up to 2K bytes
  * @local_addr: Address in IPA local memory
  * @clear_after_read: Clear local memory at the end of a read operation allows
@@ -282,16 +287,15 @@
  * @is_read: Read operation from local memory? If not, then write.
  * @skip_pipeline_clear: if to skip pipeline clear waiting (don't wait)
  * @pipeline_clear_option: options for pipeline clear waiting
- * @system_addr: Address in system memory
  */
 struct ipahal_imm_cmd_dma_shared_mem {
+	u64 system_addr;
 	u32 size;
 	u32 local_addr;
 	bool clear_after_read;
 	bool is_read;
 	bool skip_pipeline_clear;
 	enum ipahal_pipeline_clear_option pipeline_clear_options;
-	u64 system_addr;
 };
 
 /*
@@ -515,6 +519,7 @@
  *   following statuses: IPA_STATUS_PACKET, IPA_STATUS_DROPPED_PACKET,
  *   IPA_STATUS_SUSPENDED_PACKET.
  *  Other statuses types has different status packet structure.
+ * @tag_info: S/W defined value provided via immediate command
  * @status_opcode: The Type of the status (Opcode).
  * @exception: The first exception that took place.
  *  In case of exception, src endp and pkt len are always valid.
@@ -522,9 +527,6 @@
  *  and processing it may passed at IPA. See enum ipahal_pkt_status_mask
  * @pkt_len: Pkt pyld len including hdr and retained hdr if used. Does
  *  not include padding or checksum trailer len.
- * @endp_src_idx: Source end point index.
- * @endp_dest_idx: Destination end point index.
- *  Not valid in case of exception
  * @metadata: meta data value used by packet
  * @flt_local: Filter table location flag: Does matching flt rule belongs to
  *  flt tbl that resides in lcl memory? (if not, then system mem)
@@ -535,57 +537,59 @@
  *  specifies to retain header?
  * @flt_miss: Filtering miss flag: Was their a filtering rule miss?
  *   In case of miss, all flt info to be ignored
- * @flt_rule_id: The ID of the matching filter rule (if no miss).
- *  This info can be combined with endp_src_idx to locate the exact rule.
  * @rt_local: Route table location flag: Does matching rt rule belongs to
  *  rt tbl that resides in lcl memory? (if not, then system mem)
  * @rt_hash: Route hash hit flag: Does matching rt rule was in hash tbl?
  * @ucp: UC Processing flag
- * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
  * @rt_miss: Routing miss flag: Was their a routing rule miss?
- * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
- *  can be combined with rt_tbl_idx to locate the exact rule.
  * @nat_hit: NAT hit flag: Was their NAT hit?
- * @nat_entry_idx: Index of the NAT entry used of NAT processing
  * @nat_type: Defines the type of the NAT operation:
- * @tag_info: S/W defined value provided via immediate command
- * @seq_num: Per source endp unique packet sequence number
  * @time_of_day_ctr: running counter from IPA clock
  * @hdr_local: Header table location flag: In header insertion, was the header
  *  taken from the table resides in local memory? (If no, then system mem)
- * @hdr_offset: Offset of used header in the header table
  * @frag_hit: Frag hit flag: Was their frag rule hit in H/W frag table?
+ * @flt_rule_id: The ID of the matching filter rule (if no miss).
+ *  This info can be combined with endp_src_idx to locate the exact rule.
+ * @rt_rule_id: The ID of the matching rt rule. (if no miss). This info
+ *  can be combined with rt_tbl_idx to locate the exact rule.
+ * @nat_entry_idx: Index of the NAT entry used of NAT processing
+ * @hdr_offset: Offset of used header in the header table
+ * @endp_src_idx: Source end point index.
+ * @endp_dest_idx: Destination end point index.
+ *  Not valid in case of exception
+ * @rt_tbl_idx: Index of rt tbl that contains the rule on which was a match
+ * @seq_num: Per source endp unique packet sequence number
  * @frag_rule: Frag rule index in H/W frag table in case of frag hit
  */
 struct ipahal_pkt_status {
+	u64 tag_info;
 	enum ipahal_pkt_status_opcode status_opcode;
 	enum ipahal_pkt_status_exception exception;
 	u32 status_mask;
 	u32 pkt_len;
-	u8 endp_src_idx;
-	u8 endp_dest_idx;
 	u32 metadata;
 	bool flt_local;
 	bool flt_hash;
 	bool flt_global;
 	bool flt_ret_hdr;
 	bool flt_miss;
-	u16 flt_rule_id;
 	bool rt_local;
 	bool rt_hash;
 	bool ucp;
-	u8 rt_tbl_idx;
 	bool rt_miss;
-	u16 rt_rule_id;
 	bool nat_hit;
-	u16 nat_entry_idx;
 	enum ipahal_pkt_status_nat_type nat_type;
-	u64 tag_info;
-	u8 seq_num;
 	u32 time_of_day_ctr;
 	bool hdr_local;
-	u16 hdr_offset;
 	bool frag_hit;
+	u16 flt_rule_id;
+	u16 rt_rule_id;
+	u16 nat_entry_idx;
+	u16 hdr_offset;
+	u8 endp_src_idx;
+	u8 endp_dest_idx;
+	u8 rt_tbl_idx;
+	u8 seq_num;
 	u8 frag_rule;
 };
 
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 acc72f0..a677046 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -187,17 +187,17 @@
 		if (attrib->attrib_mask & IPA_FLT_NEXT_HDR ||
 		    attrib->attrib_mask & IPA_FLT_TC ||
 		    attrib->attrib_mask & IPA_FLT_FLOW_LABEL) {
-			IPAHAL_ERR("v6 attrib's specified for v4 rule\n");
+			IPAHAL_ERR_RL("v6 attrib's specified for v4 rule\n");
 			return -EPERM;
 		}
 	} else if (ipt == IPA_IP_v6) {
 		if (attrib->attrib_mask & IPA_FLT_TOS ||
 		    attrib->attrib_mask & IPA_FLT_PROTOCOL) {
-			IPAHAL_ERR("v4 attrib's specified for v6 rule\n");
+			IPAHAL_ERR_RL("v4 attrib's specified for v6 rule\n");
 			return -EPERM;
 		}
 	} else {
-		IPAHAL_ERR("unsupported ip %d\n", ipt);
+		IPAHAL_ERR_RL("unsupported ip %d\n", ipt);
 		return -EPERM;
 	}
 
@@ -236,7 +236,7 @@
 		break;
 	default:
 		IPAHAL_ERR("Invalid HDR type %d\n", params->hdr_type);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EINVAL;
 	};
 
@@ -294,8 +294,8 @@
 		rule_hdr->u.hdr.action = 0x3;
 		break;
 	default:
-		IPAHAL_ERR("Invalid Rule Action %d\n", params->rule->action);
-		WARN_ON(1);
+		IPAHAL_ERR_RL("Invalid Rule Action %d\n", params->rule->action);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EINVAL;
 	}
 	ipa_assert_on(params->rt_tbl_idx & ~0x1F);
@@ -316,14 +316,14 @@
 	if (params->rule->eq_attrib_type) {
 		if (ipa_fltrt_generate_hw_rule_bdy_from_eq(
 			&params->rule->eq_attrib, &buf)) {
-			IPAHAL_ERR("fail to generate hw rule from eq\n");
+			IPAHAL_ERR_RL("fail to generate hw rule from eq\n");
 			return -EPERM;
 		}
 		en_rule = params->rule->eq_attrib.rule_eq_bitmap;
 	} else {
 		if (ipa_fltrt_generate_hw_rule_bdy(params->ipt,
 			&params->rule->attrib, &buf, &en_rule)) {
-			IPAHAL_ERR("fail to generate hw rule\n");
+			IPAHAL_ERR_RL("fail to generate hw rule\n");
 			return -EPERM;
 		}
 	}
@@ -343,7 +343,7 @@
 	if (*hw_len == 0) {
 		*hw_len = buf - start;
 	} else if (*hw_len != (buf - start)) {
-		IPAHAL_ERR("hw_len differs b/w passed=0x%x calc=%td\n",
+		IPAHAL_ERR_RL("hw_len differs b/w passed=0x%x calc=%td\n",
 			*hw_len, (buf - start));
 		return -EPERM;
 	}
@@ -376,7 +376,7 @@
 		break;
 	default:
 		IPAHAL_ERR("Invalid Rule Action %d\n", params->rule->action);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		return -EINVAL;
 	}
 
@@ -1381,7 +1381,7 @@
 	sz = IPA3_0_HW_TBL_WIDTH * 2 + IPA3_0_HW_RULE_START_ALIGNMENT;
 	extra_wrd_buf = kzalloc(sz, GFP_KERNEL);
 	if (!extra_wrd_buf) {
-		IPAHAL_ERR("failed to allocate %d bytes\n", sz);
+		IPAHAL_ERR_RL("failed to allocate %d bytes\n", sz);
 		rc = -ENOMEM;
 		goto fail_extra_alloc;
 	}
@@ -1389,7 +1389,7 @@
 	sz = IPA3_0_HW_RULE_BUF_SIZE + IPA3_0_HW_RULE_START_ALIGNMENT;
 	rest_wrd_buf = kzalloc(sz, GFP_KERNEL);
 	if (!rest_wrd_buf) {
-		IPAHAL_ERR("failed to allocate %d bytes\n", sz);
+		IPAHAL_ERR_RL("failed to allocate %d bytes\n", sz);
 		rc = -ENOMEM;
 		goto fail_rest_alloc;
 	}
@@ -1407,14 +1407,14 @@
 
 	rc = ipa_fltrt_rule_generation_err_check(ipt, attrib);
 	if (rc) {
-		IPAHAL_ERR("rule generation err check failed\n");
+		IPAHAL_ERR_RL("rule generation err check failed\n");
 		goto fail_err_check;
 	}
 
 	if (ipt == IPA_IP_v4) {
 		if (ipa_fltrt_generate_hw_rule_bdy_ip4(en_rule, attrib,
 			&extra_wrd_i, &rest_wrd_i)) {
-			IPAHAL_ERR("failed to build ipv4 hw rule\n");
+			IPAHAL_ERR_RL("failed to build ipv4 hw rule\n");
 			rc = -EPERM;
 			goto fail_err_check;
 		}
@@ -1422,12 +1422,12 @@
 	} else if (ipt == IPA_IP_v6) {
 		if (ipa_fltrt_generate_hw_rule_bdy_ip6(en_rule, attrib,
 			&extra_wrd_i, &rest_wrd_i)) {
-			IPAHAL_ERR("failed to build ipv6 hw rule\n");
+			IPAHAL_ERR_RL("failed to build ipv6 hw rule\n");
 			rc = -EPERM;
 			goto fail_err_check;
 		}
 	} else {
-		IPAHAL_ERR("unsupported ip %d\n", ipt);
+		IPAHAL_ERR_RL("unsupported ip %d\n", ipt);
 		goto fail_err_check;
 	}
 
@@ -1500,10 +1500,10 @@
 static int ipa_fltrt_generate_hw_rule_bdy_from_eq(
 		const struct ipa_ipfltri_rule_eq *attrib, u8 **buf)
 {
-	int num_offset_meq_32 = attrib->num_offset_meq_32;
-	int num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
-	int num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
-	int num_offset_meq_128 = attrib->num_offset_meq_128;
+	uint8_t num_offset_meq_32 = attrib->num_offset_meq_32;
+	uint8_t num_ihl_offset_range_16 = attrib->num_ihl_offset_range_16;
+	uint8_t num_ihl_offset_meq_32 = attrib->num_ihl_offset_meq_32;
+	uint8_t num_offset_meq_128 = attrib->num_offset_meq_128;
 	int i;
 	int extra_bytes;
 	u8 *extra;
@@ -1514,7 +1514,7 @@
 	 * of equations that needs extra word param
 	 */
 	if (extra_bytes > 13) {
-		IPAHAL_ERR("too much extra bytes\n");
+		IPAHAL_ERR_RL("too much extra bytes\n");
 		return -EPERM;
 	} else if (extra_bytes > IPA3_0_HW_TBL_HDR_WIDTH) {
 		/* two extra words */
@@ -2041,7 +2041,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_SRC_ADDR) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2069,7 +2069,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_DST_ADDR) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2097,7 +2097,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_TOS_MASKED) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2114,7 +2114,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_MAC_DST_ADDR_ETHER_II) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2130,7 +2130,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_MAC_SRC_ADDR_ETHER_II) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2146,7 +2146,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_MAC_DST_ADDR_802_3) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2162,7 +2162,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_MAC_SRC_ADDR_802_3) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq128, ofst_meq128)) {
-			IPAHAL_ERR("ran out of meq128 eq\n");
+			IPAHAL_ERR_RL("ran out of meq128 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2180,7 +2180,7 @@
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32) || IPA_IS_RAN_OUT_OF_EQ(
 			ipa3_0_ihl_ofst_meq32, ihl_ofst_meq32 + 1)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2213,7 +2213,7 @@
 	if (attrib->attrib_mask & IPA_FLT_TCP_SYN) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2229,7 +2229,7 @@
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32) || IPA_IS_RAN_OUT_OF_EQ(
 			ipa3_0_ihl_ofst_meq32, ihl_ofst_meq32 + 1)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2271,7 +2271,7 @@
 
 	if (attrib->attrib_mask & IPA_FLT_MAC_ETHER_TYPE) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ofst_meq32, ofst_meq32)) {
-			IPAHAL_ERR("ran out of meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2287,7 +2287,7 @@
 	if (attrib->attrib_mask & IPA_FLT_TYPE) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2302,7 +2302,7 @@
 	if (attrib->attrib_mask & IPA_FLT_CODE) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2317,7 +2317,7 @@
 	if (attrib->attrib_mask & IPA_FLT_SPI) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_meq32,
 			ihl_ofst_meq32)) {
-			IPAHAL_ERR("ran out of ihl_meq32 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_meq32 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2342,7 +2342,7 @@
 	if (attrib->attrib_mask & IPA_FLT_SRC_PORT) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_rng16,
 				ihl_ofst_rng16)) {
-			IPAHAL_ERR("ran out of ihl_rng16 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_rng16 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2358,7 +2358,7 @@
 	if (attrib->attrib_mask & IPA_FLT_DST_PORT) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_rng16,
 				ihl_ofst_rng16)) {
-			IPAHAL_ERR("ran out of ihl_rng16 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_rng16 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2374,11 +2374,11 @@
 	if (attrib->attrib_mask & IPA_FLT_SRC_PORT_RANGE) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_rng16,
 				ihl_ofst_rng16)) {
-			IPAHAL_ERR("ran out of ihl_rng16 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_rng16 eq\n");
 			return -EPERM;
 		}
 		if (attrib->src_port_hi < attrib->src_port_lo) {
-			IPAHAL_ERR("bad src port range param\n");
+			IPAHAL_ERR_RL("bad src port range param\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2394,11 +2394,11 @@
 	if (attrib->attrib_mask & IPA_FLT_DST_PORT_RANGE) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_rng16,
 				ihl_ofst_rng16)) {
-			IPAHAL_ERR("ran out of ihl_rng16 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_rng16 eq\n");
 			return -EPERM;
 		}
 		if (attrib->dst_port_hi < attrib->dst_port_lo) {
-			IPAHAL_ERR("bad dst port range param\n");
+			IPAHAL_ERR_RL("bad dst port range param\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2414,7 +2414,7 @@
 	if (attrib->attrib_mask & IPA_FLT_TCP_SYN_L2TP) {
 		if (IPA_IS_RAN_OUT_OF_EQ(ipa3_0_ihl_ofst_rng16,
 				ihl_ofst_rng16)) {
-			IPAHAL_ERR("ran out of ihl_rng16 eq\n");
+			IPAHAL_ERR_RL("ran out of ihl_rng16 eq\n");
 			return -EPERM;
 		}
 		*en_rule |= IPA_GET_RULE_EQ_BIT_PTRN(
@@ -2713,7 +2713,7 @@
 		break;
 	default:
 		IPAHAL_ERR("Invalid Rule Action %d\n", rule_hdr->u.hdr.action);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		rule->rule.action = rule_hdr->u.hdr.action;
 	}
 
@@ -2760,7 +2760,7 @@
 		break;
 	default:
 		IPAHAL_ERR("Invalid Rule Action %d\n", rule_hdr->u.hdr.action);
-		WARN_ON(1);
+		WARN_ON_RATELIMIT_IPA(1);
 		rule->rule.action = rule_hdr->u.hdr.action;
 	}
 
@@ -3221,7 +3221,7 @@
 	obj = &ipahal_fltrt_objs[ipahal_ctx->hw_type];
 
 	if (!params) {
-		IPAHAL_ERR("Input error: params=%p\n", params);
+		IPAHAL_ERR_RL("Input error: params=%p\n", params);
 		return -EINVAL;
 	}
 
@@ -3230,7 +3230,7 @@
 		params->nhash_hdr.size,
 		&params->nhash_hdr.phys_base, GFP_KERNEL);
 	if (!params->nhash_hdr.base) {
-		IPAHAL_ERR("fail to alloc DMA buff of size %d\n",
+		IPAHAL_ERR_RL("fail to alloc DMA buff of size %d\n",
 			params->nhash_hdr.size);
 		goto nhash_alloc_fail;
 	}
@@ -3241,7 +3241,7 @@
 			params->hash_hdr.size, &params->hash_hdr.phys_base,
 			GFP_KERNEL);
 		if (!params->hash_hdr.base) {
-			IPAHAL_ERR("fail to alloc DMA buff of size %d\n",
+			IPAHAL_ERR_RL("fail to alloc DMA buff of size %d\n",
 				params->hash_hdr.size);
 			goto hash_alloc_fail;
 		}
@@ -3374,21 +3374,21 @@
 
 	/* Input validation */
 	if (!params) {
-		IPAHAL_ERR("Input err: no params\n");
+		IPAHAL_ERR_RL("Input err: no params\n");
 		return -EINVAL;
 	}
 	if (params->ipt >= IPA_IP_MAX) {
-		IPAHAL_ERR("Input err: Invalid ip type %d\n", params->ipt);
+		IPAHAL_ERR_RL("Input err: Invalid ip type %d\n", params->ipt);
 		return -EINVAL;
 	}
 
 	if (ipa_fltrt_alloc_init_tbl_hdr(params)) {
-		IPAHAL_ERR("fail to alloc and init tbl hdr\n");
+		IPAHAL_ERR_RL("fail to alloc and init tbl hdr\n");
 		return -ENOMEM;
 	}
 
 	if (ipa_fltrt_alloc_lcl_bdy(params)) {
-		IPAHAL_ERR("fail to alloc tbl bodies\n");
+		IPAHAL_ERR_RL("fail to alloc tbl bodies\n");
 		goto bdy_alloc_fail;
 	}
 
@@ -3649,12 +3649,12 @@
 	IPAHAL_DBG_LOW("Entry\n");
 
 	if (ipt >= IPA_IP_MAX) {
-		IPAHAL_ERR("Input err: Invalid ip type %d\n", ipt);
+		IPAHAL_ERR_RL("Input err: Invalid ip type %d\n", ipt);
 		return -EINVAL;
 	}
 
 	if (!attrib || !eq_atrb) {
-		IPAHAL_ERR("Input err: attrib=%p eq_atrb=%p\n",
+		IPAHAL_ERR_RL("Input err: attrib=%p eq_atrb=%p\n",
 			attrib, eq_atrb);
 		return -EINVAL;
 	}
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 5eb1aef..8f78d56 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -46,6 +46,16 @@
 			IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \
 	} while (0)
 
+#define IPAHAL_ERR_RL(fmt, args...) \
+		do { \
+			pr_err_ratelimited_ipa(IPAHAL_DRV_NAME " %s:%d " fmt, \
+			__func__, __LINE__, ## args); \
+			IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
+				IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \
+			IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
+				IPAHAL_DRV_NAME " %s:%d " fmt, ## args); \
+		} while (0)
+
 #define IPAHAL_MEM_ALLOC(__size, __is_atomic_ctx) \
 	(kzalloc((__size), ((__is_atomic_ctx) ? GFP_ATOMIC : GFP_KERNEL)))
 
@@ -125,10 +135,10 @@
  * struct ipa_imm_cmd_hw_ip_v4_nat_init - IP_V4_NAT_INIT command payload
  *  in H/W format.
  * Inits IPv4 NAT block. Initiate NAT table with it dimensions, location
- *  cache address abd itger related parameters.
+ *  cache address and other related parameters.
  * @ipv4_rules_addr: Addr in sys/shared mem where ipv4 NAT rules start
- * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expantion NAT
- *  table starts. IPv4 NAT rules that result in NAT collision are located
+ * @ipv4_expansion_rules_addr: Addr in sys/shared mem where expansion NAT
+ *  table starts. IPv4 NAT rules that result in hash collision are located
  *  in this table.
  * @index_table_addr: Addr in sys/shared mem where index table, which points
  *  to NAT table starts
@@ -143,11 +153,12 @@
  * @index_table_expansion_addr_type: index_table_expansion_addr in
  *  sys or shared mem
  * @size_base_tables: Num of entries in NAT tbl and idx tbl (each)
- * @size_expansion_tables: Num of entries in NAT expantion tbl and expantion
+ * @size_expansion_tables: Num of entries in NAT expansion tbl and expansion
  *  idx tbl (each)
  * @rsvd2: reserved
- * @public_ip_addr: public IP address. for IPAv4 this is the PDN config table
- *  offset in SMEM
+ * @public_addr_info: Public IP addresses info suitable to the IPA H/W version
+ *                    IPA H/W >= 4.0 - PDN config table offset in SMEM
+ *                    IPA H/W < 4.0  - The public IP address
  */
 struct ipa_imm_cmd_hw_ip_v4_nat_init {
 	u64 ipv4_rules_addr:64;
@@ -163,7 +174,38 @@
 	u64 size_base_tables:12;
 	u64 size_expansion_tables:10;
 	u64 rsvd2:2;
-	u64 public_ip_addr:32;
+	u64 public_addr_info:32;
+};
+
+/*
+ * struct ipa_imm_cmd_hw_ip_v6_ct_init - IP_V6_CONN_TRACK_INIT command payload
+ *  in H/W format.
+ * Inits IPv6CT block. Initiate IPv6CT table with it dimensions, location
+ *  cache address and other related parameters.
+ * @table_addr: Address in sys/shared mem where IPv6CT rules start
+ * @expansion_table_addr: Address in sys/shared mem where IPv6CT expansion
+ *  table starts. IPv6CT rules that result in hash collision are located
+ *  in this table.
+ * @table_index: For future support of multiple IPv6CT tables
+ * @rsvd1: reserved
+ * @table_addr_type: table_addr in sys or shared mem
+ * @expansion_table_addr_type: expansion_table_addr in sys or shared mem
+ * @rsvd2: reserved
+ * @size_base_tables: Number of entries in IPv6CT table
+ * @size_expansion_tables: Number of entries in IPv6CT expansion table
+ * @rsvd3: reserved
+ */
+struct ipa_imm_cmd_hw_ip_v6_ct_init {
+	u64 table_addr:64;
+	u64 expansion_table_addr:64;
+	u64 table_index:3;
+	u64 rsvd1:1;
+	u64 table_addr_type:1;
+	u64 expansion_table_addr_type:1;
+	u64 rsvd2:2;
+	u64 size_base_table:12;
+	u64 size_expansion_table:10;
+	u64 rsvd3:34;
 };
 
 /*
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
new file mode 100644
index 0000000..d335ba6
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.c
@@ -0,0 +1,360 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include "ipahal_nat.h"
+#include "ipahal_nat_i.h"
+#include "ipahal_i.h"
+
+#define IPA_64_LOW_32_MASK (0xFFFFFFFF)
+#define IPA_64_HIGH_32_MASK (0xFFFFFFFF00000000ULL)
+
+static const char *ipahal_nat_type_to_str[IPA_NAT_MAX] = {
+	__stringify(IPAHAL_NAT_IPV4),
+	__stringify(IPAHAL_NAT_IPV4_INDEX),
+	__stringify(IPAHAL_NAT_IPV4_PDN),
+	__stringify(IPAHAL_NAT_IPV6CT)
+};
+
+static size_t ipa_nat_ipv4_entry_size_v_3_0(void)
+{
+	return sizeof(struct ipa_nat_hw_ipv4_entry);
+}
+
+static size_t ipa_nat_ipv4_index_entry_size_v_3_0(void)
+{
+	return sizeof(struct ipa_nat_hw_indx_entry);
+}
+
+static size_t ipa_nat_ipv4_pdn_entry_size_v_4_0(void)
+{
+	return sizeof(struct ipa_nat_hw_pdn_entry);
+}
+
+static size_t ipa_nat_ipv6ct_entry_size_v_4_0(void)
+{
+	return sizeof(struct ipa_nat_hw_ipv6ct_entry);
+}
+
+static bool ipa_nat_ipv4_is_entry_zeroed_v_3_0(const void *entry)
+{
+	struct ipa_nat_hw_ipv4_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_index_entry_zeroed_v_3_0(const void *entry)
+{
+	struct ipa_nat_hw_indx_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0(const void *entry)
+{
+	struct ipa_nat_hw_pdn_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static bool ipa_nat_ipv6ct_is_entry_zeroed_v_4_0(const void *entry)
+{
+	struct ipa_nat_hw_ipv6ct_entry zero_entry = { 0 };
+
+	return (memcmp(&zero_entry, entry, sizeof(zero_entry))) ? false : true;
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_3_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_ipv4_entry *nat_entry =
+		(const struct ipa_nat_hw_ipv4_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"\t\tPrivate_IP=%pI4h  Target_IP=%pI4h\n"
+		"\t\tNext_Index=%d  Public_Port=%d\n"
+		"\t\tPrivate_Port=%d  Target_Port=%d\n"
+		"\t\tIP_CKSM_delta=0x%x  Enable=%s  Redirect=%s\n"
+		"\t\tTime_stamp=0x%x Proto=%d\n"
+		"\t\tPrev_Index=%d  Indx_tbl_entry=%d\n"
+		"\t\tTCP_UDP_cksum_delta=0x%x\n",
+		&nat_entry->private_ip, &nat_entry->target_ip,
+		nat_entry->next_index, nat_entry->public_port,
+		nat_entry->private_port, nat_entry->target_port,
+		nat_entry->ip_chksum,
+		(nat_entry->enable) ? "true" : "false",
+		(nat_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+		nat_entry->time_stamp, nat_entry->protocol,
+		nat_entry->prev_index, nat_entry->indx_tbl_entry,
+		nat_entry->tcp_udp_chksum);
+}
+
+static int ipa_nat_ipv4_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	int length;
+	const struct ipa_nat_hw_ipv4_entry *nat_entry =
+		(const struct ipa_nat_hw_ipv4_entry *)entry;
+
+	length = ipa_nat_ipv4_stringify_entry_v_3_0(entry, buff, buff_size);
+
+	length += scnprintf(buff + length, buff_size - length,
+		"\t\tPDN_Index=%d\n", nat_entry->pdn_index);
+
+	return length;
+}
+
+static int ipa_nat_ipv4_index_stringify_entry_v_3_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_indx_entry *index_entry =
+		(const struct ipa_nat_hw_indx_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"\t\tTable_Entry=%d  Next_Index=%d\n",
+		index_entry->tbl_entry, index_entry->next_index);
+}
+
+static int ipa_nat_ipv4_pdn_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	const struct ipa_nat_hw_pdn_entry *pdn_entry =
+		(const struct ipa_nat_hw_pdn_entry *)entry;
+
+	return scnprintf(buff, buff_size,
+		"ip=%pI4h src_metadata=0x%X, dst_metadata=0x%X\n",
+		&pdn_entry->public_ip,
+		pdn_entry->src_metadata, pdn_entry->dst_metadata);
+}
+
+static inline int ipa_nat_ipv6_stringify_addr(char *buff, size_t buff_size,
+	const char *msg, u64 lsb, u64 msb)
+{
+	struct in6_addr addr;
+
+	addr.s6_addr32[0] = cpu_to_be32((msb & IPA_64_HIGH_32_MASK) >> 32);
+	addr.s6_addr32[1] = cpu_to_be32(msb & IPA_64_LOW_32_MASK);
+	addr.s6_addr32[2] = cpu_to_be32((lsb & IPA_64_HIGH_32_MASK) >> 32);
+	addr.s6_addr32[3] = cpu_to_be32(lsb & IPA_64_LOW_32_MASK);
+
+	return scnprintf(buff, buff_size,
+		"\t\t%s_IPv6_Addr=%pI6c\n", msg, &addr);
+}
+
+static int ipa_nat_ipv6ct_stringify_entry_v_4_0(const void *entry,
+	char *buff, size_t buff_size)
+{
+	int length = 0;
+	const struct ipa_nat_hw_ipv6ct_entry *ipv6ct_entry =
+		(const struct ipa_nat_hw_ipv6ct_entry *)entry;
+
+	length += ipa_nat_ipv6_stringify_addr(
+		buff + length,
+		buff_size - length,
+		"Src",
+		ipv6ct_entry->src_ipv6_lsb,
+		ipv6ct_entry->src_ipv6_msb);
+
+	length += ipa_nat_ipv6_stringify_addr(
+		buff + length,
+		buff_size - length,
+		"Dest",
+		ipv6ct_entry->dest_ipv6_lsb,
+		ipv6ct_entry->dest_ipv6_msb);
+
+	length += scnprintf(buff + length, buff_size - length,
+		"\t\tEnable=%s Redirect=%s Time_Stamp=0x%x Proto=%d\n"
+		"\t\tNext_Index=%d Dest_Port=%d Src_Port=%d\n"
+		"\t\tDirection Settings: Out=%s In=%s\n"
+		"\t\tPrev_Index=%d\n",
+		(ipv6ct_entry->enable) ? "true" : "false",
+		(ipv6ct_entry->redirect) ? "Direct_To_APPS" : "Fwd_to_route",
+		ipv6ct_entry->time_stamp,
+		ipv6ct_entry->protocol,
+		ipv6ct_entry->next_index,
+		ipv6ct_entry->dest_port,
+		ipv6ct_entry->src_port,
+		(ipv6ct_entry->out_allowed) ? "Allow" : "Deny",
+		(ipv6ct_entry->in_allowed) ? "Allow" : "Deny",
+		ipv6ct_entry->prev_index);
+
+	return length;
+}
+
+/*
+ * struct ipahal_nat_obj - H/W information for specific IPA version
+ * @entry_size - CB to get the size of the entry
+ * @is_entry_zeroed - CB to determine whether an entry is definitely zero
+ * @stringify_entry - CB to create string that represents an entry
+ */
+struct ipahal_nat_obj {
+	size_t (*entry_size)(void);
+	bool (*is_entry_zeroed)(const void *entry);
+	int (*stringify_entry)(const void *entry, char *buff, size_t buff_size);
+};
+
+/*
+ * This table contains the info regard each NAT type for IPAv3 and later.
+ * Information like: get entry size and stringify entry functions.
+ * All the information on all the NAT types on IPAv3 are statically
+ * defined below. If information is missing regard some NAT type on some
+ * IPA version, the init function will fill it with the information from the
+ * previous IPA version.
+ * Information is considered missing if all of the fields are 0
+ */
+static struct ipahal_nat_obj ipahal_nat_objs[IPA_HW_MAX][IPA_NAT_MAX] = {
+	/* IPAv3 */
+	[IPA_HW_v3_0][IPAHAL_NAT_IPV4] = {
+			ipa_nat_ipv4_entry_size_v_3_0,
+			ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_stringify_entry_v_3_0
+		},
+	[IPA_HW_v3_0][IPAHAL_NAT_IPV4_INDEX] = {
+			ipa_nat_ipv4_index_entry_size_v_3_0,
+			ipa_nat_ipv4_is_index_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_index_stringify_entry_v_3_0
+		},
+
+	/* IPAv4 */
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV4] = {
+			ipa_nat_ipv4_entry_size_v_3_0,
+			ipa_nat_ipv4_is_entry_zeroed_v_3_0,
+			ipa_nat_ipv4_stringify_entry_v_4_0
+		},
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV4_PDN] = {
+			ipa_nat_ipv4_pdn_entry_size_v_4_0,
+			ipa_nat_ipv4_is_pdn_entry_zeroed_v_4_0,
+			ipa_nat_ipv4_pdn_stringify_entry_v_4_0
+		},
+	[IPA_HW_v4_0][IPAHAL_NAT_IPV6CT] = {
+			ipa_nat_ipv6ct_entry_size_v_4_0,
+			ipa_nat_ipv6ct_is_entry_zeroed_v_4_0,
+			ipa_nat_ipv6ct_stringify_entry_v_4_0
+		}
+};
+
+static void ipahal_nat_check_obj(struct ipahal_nat_obj *obj,
+	int nat_type, int ver)
+{
+	WARN(obj->entry_size == NULL, "%s missing entry_size for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+	WARN(obj->is_entry_zeroed == NULL,
+		"%s missing is_entry_zeroed for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+	WARN(obj->stringify_entry == NULL,
+		"%s missing stringify_entry for version %d\n",
+		ipahal_nat_type_str(nat_type), ver);
+}
+
+/*
+ * ipahal_nat_init() - Build the NAT information table
+ *  See ipahal_nat_objs[][] comments
+ */
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type)
+{
+	int i;
+	int j;
+	struct ipahal_nat_obj zero_obj, *next_obj;
+
+	IPAHAL_DBG("Entry - HW_TYPE=%d\n", ipa_hw_type);
+
+	memset(&zero_obj, 0, sizeof(zero_obj));
+
+	if ((ipa_hw_type < 0) || (ipa_hw_type >= IPA_HW_MAX)) {
+		IPAHAL_ERR("invalid IPA HW type (%d)\n", ipa_hw_type);
+		return -EINVAL;
+	}
+
+	for (i = IPA_HW_v3_0 ; i < ipa_hw_type ; ++i) {
+		for (j = 0; j < IPA_NAT_MAX; ++j) {
+			next_obj = &ipahal_nat_objs[i + 1][j];
+			if (!memcmp(next_obj, &zero_obj, sizeof(*next_obj))) {
+				memcpy(next_obj, &ipahal_nat_objs[i][j],
+					sizeof(*next_obj));
+			} else {
+				ipahal_nat_check_obj(next_obj, j, i + 1);
+			}
+		}
+	}
+
+	return 0;
+}
+
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type)
+{
+	if (nat_type < 0 || nat_type >= IPA_NAT_MAX) {
+		IPAHAL_ERR("requested NAT type %d is invalid\n", nat_type);
+		return "Invalid NAT type";
+	}
+
+	return ipahal_nat_type_to_str[nat_type];
+}
+
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size)
+{
+	if (WARN(entry_size == NULL, "entry_size is NULL\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Get the entry size for NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	*entry_size = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		entry_size();
+	IPAHAL_DBG("The entry size is %zu\n", *entry_size);
+
+	return 0;
+}
+
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+	bool *entry_zeroed)
+{
+	if (WARN(entry == NULL || entry_zeroed == NULL,
+		"NULL pointer received\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Determine whether the entry is zeroed for NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	*entry_zeroed = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		is_entry_zeroed(entry);
+	IPAHAL_DBG("The entry is %szeroed\n", (*entry_zeroed) ? "" : "not ");
+
+	return 0;
+}
+
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+	char *buff, size_t buff_size)
+{
+	int result;
+
+	if (WARN(entry == NULL || buff == NULL, "NULL pointer received\n"))
+		return -EINVAL;
+	if (WARN(!buff_size, "The output buff size is zero\n"))
+		return -EINVAL;
+	if (WARN(nat_type < 0 || nat_type >= IPA_NAT_MAX,
+		"requested NAT type %d is invalid\n", nat_type))
+		return -EINVAL;
+
+	IPAHAL_DBG("Create the string for the entry of NAT type=%s\n",
+		ipahal_nat_type_str(nat_type));
+	result = ipahal_nat_objs[ipahal_ctx->hw_type][nat_type].
+		stringify_entry(entry, buff, buff_size);
+	IPAHAL_DBG("The string successfully created with length %d\n",
+		result);
+
+	return result;
+}
+
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
new file mode 100644
index 0000000..f99c1a0
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_H_
+#define _IPAHAL_NAT_H_
+
+/*
+ * NAT types
+ *
+ * NOTE:: Any change to this enum, need to change to ipahal_nat_to_str
+ *	  array as well.
+ */
+enum ipahal_nat_type {
+	IPAHAL_NAT_IPV4,
+	IPAHAL_NAT_IPV4_INDEX,
+	IPAHAL_NAT_IPV4_PDN,
+	IPAHAL_NAT_IPV6CT,
+	IPA_NAT_MAX
+};
+
+/* NAT Function APIs */
+
+/*
+ * ipahal_nat_type_str() - returns string that represent the NAT type
+ * @nat_type: [in] NAT type
+ */
+const char *ipahal_nat_type_str(enum ipahal_nat_type nat_type);
+
+/*
+ * ipahal_nat_entry_size() - Gets the size of HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry_size: [out] The size of the HW NAT entry
+ */
+int ipahal_nat_entry_size(enum ipahal_nat_type nat_type, size_t *entry_size);
+
+/*
+ * ipahal_nat_is_entry_zeroed() - Determines whether HW NAT entry is
+ *                                definitely zero
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @entry_zeroed: [out] True if the received entry is definitely zero
+ */
+int ipahal_nat_is_entry_zeroed(enum ipahal_nat_type nat_type, void *entry,
+	bool *entry_zeroed);
+
+/*
+ * ipahal_nat_stringify_entry() - Creates a string for HW NAT entry
+ * @nat_type: [in] The type of the NAT entry
+ * @entry: [in] The NAT entry
+ * @buff: [out] Output buffer for the result string
+ * @buff_size: [in] The size of the output buffer
+ * @return the number of characters written into buff not including
+ *         the trailing '\0'
+ */
+int ipahal_nat_stringify_entry(enum ipahal_nat_type nat_type, void *entry,
+	char *buff, size_t buff_size);
+
+#endif /* _IPAHAL_NAT_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
new file mode 100644
index 0000000..83bd0f5
--- /dev/null
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_nat_i.h
@@ -0,0 +1,153 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPAHAL_NAT_I_H_
+#define _IPAHAL_NAT_I_H_
+
+#include <linux/msm_ipa.h>
+
+/* ----------------------- IPv4 NAT Table Entry  -------------------------
+ *
+ * -----------------------------------------------------------------------
+ * |  7  |     6    |   5    |   4   |    3        | 2 |    1    |    0  |
+ * -----------------------------------------------------------------------
+ * |           Target IP(4B)         |             Private IP(4B)        |
+ * -----------------------------------------------------------------------
+ * |Target Port(2B) |Private Port(2B)| Public Port(2B) | Next Index(2B)  |
+ * -----------------------------------------------------------------------
+ * |Proto|      TimeStamp(3B)        |     Flags(2B)   |IP check sum Diff|
+ * |(1B) |                           |EN|Redirect|Resv |        (2B)     |
+ * -----------------------------------------------------------------------
+ * |TCP/UDP checksum|  PDN info(2B)  |    SW Specific Parameters(4B)     |
+ * |    diff (2B)   |Info|Resv       |index table entry|  prev index     |
+ * -----------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv4_entry {
+	/* An IP address can't be bit-field, because its address is used */
+	u32 private_ip;
+	u32 target_ip;
+
+	u32 next_index : 16;
+	u32 public_port : 16;
+	u32 private_port : 16;
+	u32 target_port : 16;
+	u32 ip_chksum : 16;
+
+	u32 rsvd1 : 14;
+	u32 redirect : 1;
+	u32 enable : 1;
+
+	u32 time_stamp : 24;
+	u32 protocol : 8;
+
+	u32 prev_index : 16;
+	u32 indx_tbl_entry : 16;
+
+	u32 rsvd2 : 12;
+	u32 pdn_index : 4; /* IPA 4.0 and greater */
+
+	u32 tcp_udp_chksum : 16;
+};
+
+/*--- IPV4 NAT Index Table Entry --
+ *---------------------------------
+ *|   3   |   2   |   1   |   0   |
+ *---------------------------------
+ *|next index(2B) |table entry(2B)|
+ *---------------------------------
+ */
+struct ipa_nat_hw_indx_entry {
+	u16 tbl_entry;
+	u16 next_index;
+};
+
+/**
+ * struct ipa_nat_hw_pdn_entry - IPA PDN config table entry
+ * @public_ip: the PDN's public ip
+ * @src_metadata: the PDN's metadata to be replaced for source NAT
+ * @dst_metadata: the PDN's metadata to be replaced for destination NAT
+ * @resrvd: reserved field
+ * ---------------------------------
+ * |   3   |   2   |   1   |   0   |
+ * ---------------------------------
+ * |        public_ip (4B)         |
+ * ---------------------------------
+ * |      src_metadata (4B)        |
+ * ---------------------------------
+ * |      dst_metadata (4B)        |
+ * ---------------------------------
+ * |         resrvd (4B)           |
+ * ---------------------------------
+ */
+struct ipa_nat_hw_pdn_entry {
+	u32 public_ip;
+	u32 src_metadata;
+	u32 dst_metadata;
+	u32 resrvd;
+};
+
+/*-------------------------  IPV6CT Table Entry  ------------------------------
+ *-----------------------------------------------------------------------------
+ *|   7    |      6      |  5  |  4   |        3         |  2  |   1  |   0   |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Src IPv6 Address (8 LSB Bytes)                 |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Src IPv6 Address (8 MSB Bytes)                 |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Dest IPv6 Address (8 LSB Bytes)                |
+ *-----------------------------------------------------------------------------
+ *|                   Outbound Dest IPv6 Address (8 MSB Bytes)                |
+ *-----------------------------------------------------------------------------
+ *|Protocol|      TimeStamp (3B)      |       Flags (2B)       |Reserved (2B) |
+ *|  (1B)  |                          |Enable|Redirect|Resv    |              |
+ *-----------------------------------------------------------------------------
+ *|Reserved|Direction(1B)|Src Port(2B)|     Dest Port (2B)     |Next Index(2B)|
+ *|  (1B)  |IN|OUT|Resv  |            |                        |              |
+ *-----------------------------------------------------------------------------
+ *|    SW Specific Parameters(4B)     |                Reserved (4B)          |
+ *|    Prev Index (2B)   |Reserved(2B)|                                       |
+ *-----------------------------------------------------------------------------
+ *|                            Reserved (8B)                                  |
+ *-----------------------------------------------------------------------------
+ */
+struct ipa_nat_hw_ipv6ct_entry {
+	/* An IP address can't be bit-field, because its address is used */
+	u64 src_ipv6_lsb;
+	u64 src_ipv6_msb;
+	u64 dest_ipv6_lsb;
+	u64 dest_ipv6_msb;
+
+	u64 rsvd1 : 30;
+	u64 redirect : 1;
+	u64 enable : 1;
+
+	u64 time_stamp : 24;
+	u64 protocol : 8;
+
+	u64 next_index : 16;
+	u64 dest_port : 16;
+	u64 src_port : 16;
+	u64 rsvd2 : 6;
+	u64 out_allowed : 1;
+	u64 in_allowed : 1;
+	u64 rsvd3 : 8;
+
+	u64 rsvd4 : 48;
+	u64 prev_index : 16;
+
+	u64 rsvd5 : 64;
+};
+
+int ipahal_nat_init(enum ipa_hw_type ipa_hw_type);
+
+#endif /* _IPAHAL_NAT_I_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 dc71414..1d8eb13 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -1910,6 +1910,8 @@
 		return;
 	}
 
+	memset(valmask, 0, sizeof(struct ipahal_reg_valmask));
+
 	if (ipahal_ctx->hw_type <= IPA_HW_v3_1) {
 		shft = IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_SHFT;
 		bmsk = IPA_AGGR_FORCE_CLOSE_AGGR_FORCE_CLOSE_PIPE_BITMAP_BMSK;
@@ -1961,20 +1963,3 @@
 
 	valmask->mask = valmask->val;
 }
-
-void ipahal_get_status_ep_valmask(int pipe_num,
-	struct ipahal_reg_valmask *valmask)
-{
-	if (!valmask) {
-		IPAHAL_ERR("Input error\n");
-		return;
-	}
-
-	valmask->val =
-		(pipe_num & IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK) <<
-		IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT;
-
-	valmask->mask =
-		IPA_ENDP_STATUS_n_STATUS_ENDP_BMSK <<
-		IPA_ENDP_STATUS_n_STATUS_ENDP_SHFT;
-}
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 a2864cd..df3c976 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -541,8 +541,6 @@
 void ipahal_get_fltrt_hash_flush_valmask(
 	struct ipahal_reg_fltrt_hash_flush *flush,
 	struct ipahal_reg_valmask *valmask);
-void ipahal_get_status_ep_valmask(int pipe_num,
-	struct ipahal_reg_valmask *valmask);
 
 #endif /* _IPAHAL_REG_H_ */
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index b119a69..66d4b10 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -68,6 +68,9 @@
 
 #define IPA_WWAN_CONS_DESC_FIFO_SZ 256
 
+static void rmnet_ipa_free_msg(void *buff, u32 len, u32 type);
+static void rmnet_ipa_get_stats_and_update(void);
+
 static int ipa3_wwan_add_ul_flt_rule_to_ipa(void);
 static int ipa3_wwan_del_ul_flt_rule_to_ipa(void);
 static void ipa3_wwan_msg_free_cb(void*, u32, u32);
@@ -142,6 +145,13 @@
 	u32 ipa3_to_apps_hdl;
 	struct mutex pipe_handle_guard;
 	struct mutex add_mux_channel_lock;
+	u32 pm_hdl;
+	u32 q6_pm_hdl;
+	u32 q6_teth_pm_hdl;
+	struct mutex per_client_stats_guard;
+	struct ipa_tether_device_info
+		tether_device
+		[IPACM_MAX_CLIENT_DEVICE_TYPES];
 };
 
 static struct rmnet_ipa3_context *rmnet_ipa3_ctx;
@@ -423,6 +433,8 @@
 {
 	int i, j;
 
+	/* prevent multi-threads accessing rmnet_ipa3_ctx->num_q6_rules */
+	mutex_lock(&rmnet_ipa3_ctx->add_mux_channel_lock);
 	if (rule_req->filter_spec_ex_list_valid == true) {
 		rmnet_ipa3_ctx->num_q6_rules =
 			rule_req->filter_spec_ex_list_len;
@@ -431,6 +443,8 @@
 	} else {
 		rmnet_ipa3_ctx->num_q6_rules = 0;
 		IPAWANERR("got no UL rules from modem\n");
+		mutex_unlock(&rmnet_ipa3_ctx->
+					add_mux_channel_lock);
 		return -EINVAL;
 	}
 
@@ -633,9 +647,13 @@
 	rmnet_ipa3_ctx->num_q6_rules = 0;
 	memset(ipa3_qmi_ctx->q6_ul_filter_rule, 0,
 		sizeof(ipa3_qmi_ctx->q6_ul_filter_rule));
+	mutex_unlock(&rmnet_ipa3_ctx->
+		add_mux_channel_lock);
 	return -EINVAL;
 
 success:
+	mutex_unlock(&rmnet_ipa3_ctx->
+		add_mux_channel_lock);
 	return 0;
 }
 
@@ -666,6 +684,8 @@
 	param->global = false;
 	param->num_rules = (uint8_t)1;
 
+	memset(req, 0, sizeof(struct ipa_fltr_installed_notif_req_msg_v01));
+
 	for (i = 0; i < rmnet_ipa3_ctx->num_q6_rules; i++) {
 		param->ip = ipa3_qmi_ctx->q6_ul_filter_rule[i].ip;
 		memset(&flt_rule_entry, 0, sizeof(struct ipa_flt_rule_add));
@@ -1086,7 +1106,6 @@
 	int ret = 0;
 	bool qmap_check;
 	struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev);
-	struct ipa_tx_meta meta;
 
 	if (skb->protocol != htons(ETH_P_MAP)) {
 		IPAWANDBG_LOW
@@ -1127,8 +1146,14 @@
 
 send:
 	/* IPA_RM checking start */
-	ret = ipa_rm_inactivity_timer_request_resource(
-		IPA_RM_RESOURCE_WWAN_0_PROD);
+	if (ipa3_ctx->use_ipa_pm) {
+		/* activate the modem pm for clock scaling */
+		ipa_pm_activate(rmnet_ipa3_ctx->q6_pm_hdl);
+		ret = ipa_pm_activate(rmnet_ipa3_ctx->pm_hdl);
+	} else {
+		ret = ipa_rm_inactivity_timer_request_resource(
+			IPA_RM_RESOURCE_WWAN_0_PROD);
+	}
 	if (ret == -EINPROGRESS) {
 		netif_stop_queue(dev);
 		return NETDEV_TX_BUSY;
@@ -1142,17 +1167,11 @@
 	}
 	/* IPA_RM checking end */
 
-	if (RMNET_MAP_GET_CD_BIT(skb)) {
-		memset(&meta, 0, sizeof(meta));
-		meta.pkt_init_dst_ep_valid = true;
-		meta.pkt_init_dst_ep_remote = true;
-		meta.pkt_init_dst_ep =
-			ipa3_get_ep_mapping(IPA_CLIENT_Q6_WAN_CONS);
-		ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, &meta);
-	} else {
-		ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, NULL);
-	}
-
+	/*
+	 * both data packets and command will be routed to
+	 * IPA_CLIENT_Q6_WAN_CONS based on status configuration
+	 */
+	ret = ipa3_tx_dp(IPA_CLIENT_APPS_WAN_PROD, skb, NULL);
 	if (ret) {
 		ret = NETDEV_TX_BUSY;
 		goto out;
@@ -1163,15 +1182,25 @@
 	dev->stats.tx_bytes += skb->len;
 	ret = NETDEV_TX_OK;
 out:
-	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0)
-		ipa_rm_inactivity_timer_release_resource(
-			IPA_RM_RESOURCE_WWAN_0_PROD);
+	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0) {
+		if (ipa3_ctx->use_ipa_pm) {
+			ipa_pm_deferred_deactivate(rmnet_ipa3_ctx->pm_hdl);
+			ipa_pm_deferred_deactivate(rmnet_ipa3_ctx->q6_pm_hdl);
+		} else {
+			ipa_rm_inactivity_timer_release_resource(
+				IPA_RM_RESOURCE_WWAN_0_PROD);
+		}
+	}
 	return ret;
 }
 
 static void ipa3_wwan_tx_timeout(struct net_device *dev)
 {
-	IPAWANERR("[%s] ipa3_wwan_tx_timeout(), data stall in UL\n", dev->name);
+	struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev);
+
+	if (atomic_read(&wwan_ptr->outstanding_pkts) != 0)
+		IPAWANERR("[%s] data stall in UL, %d outstanding\n",
+			dev->name, atomic_read(&wwan_ptr->outstanding_pkts));
 }
 
 /**
@@ -1217,9 +1246,15 @@
 		netif_wake_queue(wwan_ptr->net);
 	}
 
-	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0)
-		ipa_rm_inactivity_timer_release_resource(
+	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0) {
+		if (ipa3_ctx->use_ipa_pm) {
+			ipa_pm_deferred_deactivate(rmnet_ipa3_ctx->pm_hdl);
+			ipa_pm_deferred_deactivate(rmnet_ipa3_ctx->q6_pm_hdl);
+		} else {
+			ipa_rm_inactivity_timer_release_resource(
 			IPA_RM_RESOURCE_WWAN_0_PROD);
+		}
+	}
 	__netif_tx_unlock_bh(netdev_get_tx_queue(dev, 0));
 	dev_kfree_skb_any(skb);
 }
@@ -1373,9 +1408,7 @@
 	}
 
 	if ((e->u.data) & RMNET_IOCTL_EGRESS_FORMAT_AGGREGATION) {
-		IPAWANERR("WAN UL Aggregation not supported!!\n");
-		WARN_ON(1);
-		return -EINVAL;
+		IPAWANDBG("WAN UL Aggregation enabled\n");
 		ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr_en = IPA_ENABLE_DEAGGR;
 		ipa_wan_ep_cfg->ipa_ep_cfg.aggr.aggr = IPA_QCMAP;
 
@@ -1429,8 +1462,13 @@
 
 	if (rmnet_ipa3_ctx->num_q6_rules != 0) {
 		/* already got Q6 UL filter rules*/
-		if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt == false)
+		if (ipa3_qmi_ctx->modem_cfg_emb_pipe_flt == false) {
+			/* prevent multi-threads accessing num_q6_rules */
+			mutex_lock(&rmnet_ipa3_ctx->add_mux_channel_lock);
 			rc = ipa3_wwan_add_ul_flt_rule_to_ipa();
+			mutex_unlock(&rmnet_ipa3_ctx->
+				add_mux_channel_lock);
+		}
 		if (rc)
 			IPAWANERR("install UL rules failed\n");
 		else
@@ -1614,8 +1652,8 @@
 		/*  Get driver name  */
 		case RMNET_IOCTL_GET_DRIVER_NAME:
 			memcpy(&extend_ioctl_data.u.if_name,
-				IPA_NETDEV()->name,
-							sizeof(IFNAMSIZ));
+				IPA_NETDEV()->name, IFNAMSIZ);
+			extend_ioctl_data.u.if_name[IFNAMSIZ - 1] = '\0';
 			if (copy_to_user((u8 *)ifr->ifr_ifru.ifru_data,
 					&extend_ioctl_data,
 					sizeof(struct rmnet_ioctl_extended_s)))
@@ -1640,6 +1678,8 @@
 					add_mux_channel_lock);
 				return -EFAULT;
 			}
+			extend_ioctl_data.u.rmnet_mux_val.vchannel_name
+				[IFNAMSIZ-1] = '\0';
 			IPAWANDBG("ADD_MUX_CHANNEL(%d, name: %s)\n",
 			extend_ioctl_data.u.rmnet_mux_val.mux_id,
 			extend_ioctl_data.u.rmnet_mux_val.vchannel_name);
@@ -1706,6 +1746,7 @@
 				sizeof(wan_msg->upstream_ifname);
 			strlcpy(wan_msg->upstream_ifname,
 				extend_ioctl_data.u.if_name, len);
+			wan_msg->upstream_ifname[len-1] = '\0';
 			memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
 			msg_meta.msg_type = WAN_XLAT_CONNECT;
 			msg_meta.msg_len = sizeof(struct ipa_wan_msg);
@@ -1854,6 +1895,85 @@
 		return;
 	}
 }
+
+int ipa3_wwan_set_modem_state(struct wan_ioctl_notify_wan_state *state)
+{
+	if (!state)
+		return -EINVAL;
+
+	if (!ipa_pm_is_used())
+		return 0;
+
+	if (state->up)
+		return ipa_pm_activate_sync(rmnet_ipa3_ctx->q6_teth_pm_hdl);
+	else
+		return ipa_pm_deactivate_sync(rmnet_ipa3_ctx->q6_teth_pm_hdl);
+}
+
+/**
+ * ipa3_q6_register_pm - Register modem clients for PM
+ *
+ * This function will register 2 client with IPA PM to represent modem
+ * in clock scaling calculation:
+ *	- "EMB MODEM" - this client will be activated with embedded traffic
+	- "TETH MODEM" - this client we be activated by IPACM on offload to
+	  modem.
+*/
+static int ipa3_q6_register_pm(void)
+{
+	int result;
+	struct ipa_pm_register_params pm_reg;
+
+	memset(&pm_reg, 0, sizeof(pm_reg));
+	pm_reg.name = "EMB MODEM";
+	pm_reg.group = IPA_PM_GROUP_MODEM;
+	pm_reg.skip_clk_vote = true;
+	result = ipa_pm_register(&pm_reg, &rmnet_ipa3_ctx->q6_pm_hdl);
+	if (result) {
+		IPAERR("failed to create IPA PM client %d\n", result);
+		return result;
+	}
+
+	pm_reg.name = "TETH MODEM";
+	pm_reg.group = IPA_PM_GROUP_MODEM;
+	pm_reg.skip_clk_vote = true;
+	result = ipa_pm_register(&pm_reg, &rmnet_ipa3_ctx->q6_teth_pm_hdl);
+	if (result) {
+		IPAERR("failed to create IPA PM client %d\n", result);
+		return result;
+	}
+
+	return 0;
+}
+
+static void ipa3_q6_deregister_pm(void)
+{
+	ipa_pm_deactivate_sync(rmnet_ipa3_ctx->q6_pm_hdl);
+	ipa_pm_deregister(rmnet_ipa3_ctx->q6_pm_hdl);
+}
+
+int ipa3_wwan_set_modem_perf_profile(int throughput)
+{
+	struct ipa_rm_perf_profile profile;
+	int ret;
+
+	if (ipa3_ctx->use_ipa_pm) {
+		ret = ipa_pm_set_perf_profile(rmnet_ipa3_ctx->q6_pm_hdl,
+			throughput);
+		if (ret)
+			return ret;
+		ret = ipa_pm_set_perf_profile(rmnet_ipa3_ctx->q6_teth_pm_hdl,
+			throughput);
+	} else {
+		memset(&profile, 0, sizeof(profile));
+		profile.max_supported_bandwidth_mbps = throughput;
+		ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_Q6_PROD,
+			&profile);
+	}
+
+	return ret;
+}
+
 static int ipa3_q6_initialize_rm(void)
 {
 	struct ipa_rm_create_params create_params;
@@ -2071,6 +2191,120 @@
 	schedule_work(&ipa3_scheduled_probe);
 }
 
+static void ipa_pm_wwan_pm_cb(void *p, enum ipa_pm_cb_event event)
+{
+	struct net_device *dev = (struct net_device *)p;
+	struct ipa3_wwan_private *wwan_ptr = netdev_priv(dev);
+
+	IPAWANDBG_LOW("event %d\n", event);
+	switch (event) {
+	case IPA_PM_CLIENT_ACTIVATED:
+		if (wwan_ptr->device_status == WWAN_DEVICE_INACTIVE) {
+			complete_all(&wwan_ptr->resource_granted_completion);
+			break;
+		}
+		ipa3_rm_resource_granted(dev);
+		break;
+	default:
+		pr_err("%s: unknown event %d\n", __func__, event);
+		break;
+	}
+}
+
+static int ipa3_wwan_register_netdev_pm_client(struct net_device *dev)
+{
+	int result;
+	struct ipa_pm_register_params pm_reg;
+
+	memset(&pm_reg, 0, sizeof(pm_reg));
+	pm_reg.name = IPA_NETDEV()->name;
+	pm_reg.user_data = dev;
+	pm_reg.callback = ipa_pm_wwan_pm_cb;
+	pm_reg.group = IPA_PM_GROUP_APPS;
+	result = ipa_pm_register(&pm_reg, &rmnet_ipa3_ctx->pm_hdl);
+	if (result) {
+		IPAERR("failed to create IPA PM client %d\n", result);
+			return result;
+	}
+	return 0;
+}
+
+static void ipa3_wwan_deregister_netdev_pm_client(void)
+{
+	ipa_pm_deactivate_sync(rmnet_ipa3_ctx->pm_hdl);
+	ipa_pm_deregister(rmnet_ipa3_ctx->pm_hdl);
+}
+
+static int ipa3_wwan_create_wwan_rm_resource(struct net_device *dev)
+{
+	struct ipa_rm_create_params ipa_rm_params;
+	struct ipa_rm_perf_profile profile;
+	int ret;
+
+	memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params));
+	ipa_rm_params.name = IPA_RM_RESOURCE_WWAN_0_PROD;
+	ipa_rm_params.reg_params.user_data = dev;
+	ipa_rm_params.reg_params.notify_cb = ipa3_rm_notify;
+	ret = ipa_rm_create_resource(&ipa_rm_params);
+	if (ret) {
+		pr_err("%s: unable to create resourse %d in IPA RM\n",
+			__func__, IPA_RM_RESOURCE_WWAN_0_PROD);
+		return ret;
+	}
+	ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WWAN_0_PROD,
+		IPA_RM_INACTIVITY_TIMER);
+	if (ret) {
+		pr_err("%s: ipa rm timer init failed %d on resourse %d\n",
+			__func__, ret, IPA_RM_RESOURCE_WWAN_0_PROD);
+		goto timer_init_err;
+	}
+	/* add dependency */
+	ret = ipa_rm_add_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
+		IPA_RM_RESOURCE_Q6_CONS);
+	if (ret)
+		goto add_dpnd_err;
+	/* setup Performance profile */
+	memset(&profile, 0, sizeof(profile));
+	profile.max_supported_bandwidth_mbps = IPA_APPS_MAX_BW_IN_MBPS;
+	ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WWAN_0_PROD,
+		&profile);
+	if (ret)
+		goto set_perf_err;
+
+	return 0;
+
+set_perf_err:
+	ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
+		IPA_RM_RESOURCE_Q6_CONS);
+add_dpnd_err:
+	ipa_rm_inactivity_timer_destroy(
+		IPA_RM_RESOURCE_WWAN_0_PROD); /* IPA_RM */
+timer_init_err:
+	ipa_rm_delete_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
+	return ret;
+}
+
+static void ipa3_wwan_delete_wwan_rm_resource(void)
+{
+	int ret;
+
+	ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
+		IPA_RM_RESOURCE_Q6_CONS);
+	if (ret < 0)
+		IPAWANERR("Error deleting dependency %d->%d, ret=%d\n",
+		IPA_RM_RESOURCE_WWAN_0_PROD, IPA_RM_RESOURCE_Q6_CONS,
+		ret);
+	ret = ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WWAN_0_PROD);
+	if (ret < 0)
+		IPAWANERR(
+		"Error ipa_rm_inactivity_timer_destroy resource %d, ret=%d\n",
+		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
+	ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
+	if (ret < 0)
+		IPAWANERR("Error deleting resource %d, ret=%d\n",
+		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
+}
+
 /**
  * ipa3_wwan_probe() - Initialized the module and registers as a
  * network interface to the network stack
@@ -2088,8 +2322,6 @@
 {
 	int ret, i;
 	struct net_device *dev;
-	struct ipa_rm_create_params ipa_rm_params;	/* IPA_RM */
-	struct ipa_rm_perf_profile profile;			/* IPA_RM */
 
 	pr_info("rmnet_ipa3 started initialization\n");
 
@@ -2183,7 +2415,10 @@
 
 	if (!atomic_read(&rmnet_ipa3_ctx->is_ssr)) {
 		/* IPA_RM configuration starts */
-		ret = ipa3_q6_initialize_rm();
+		if (ipa3_ctx->use_ipa_pm)
+			ret = ipa3_q6_register_pm();
+		else
+			ret = ipa3_q6_initialize_rm();
 		if (ret) {
 			IPAWANERR("%s: ipa3_q6_initialize_rm failed, ret: %d\n",
 				__func__, ret);
@@ -2191,36 +2426,14 @@
 		}
 	}
 
-	memset(&ipa_rm_params, 0, sizeof(struct ipa_rm_create_params));
-	ipa_rm_params.name = IPA_RM_RESOURCE_WWAN_0_PROD;
-	ipa_rm_params.reg_params.user_data = dev;
-	ipa_rm_params.reg_params.notify_cb = ipa3_rm_notify;
-	ret = ipa_rm_create_resource(&ipa_rm_params);
+	if (ipa3_ctx->use_ipa_pm)
+		ret = ipa3_wwan_register_netdev_pm_client(dev);
+	else
+		ret = ipa3_wwan_create_wwan_rm_resource(dev);
 	if (ret) {
-		pr_err("%s: unable to create resourse %d in IPA RM\n",
-		       __func__, IPA_RM_RESOURCE_WWAN_0_PROD);
-		goto create_rsrc_err;
+		IPAWANERR("fail to create/register pm resources\n");
+		goto fail_pm;
 	}
-	ret = ipa_rm_inactivity_timer_init(IPA_RM_RESOURCE_WWAN_0_PROD,
-					   IPA_RM_INACTIVITY_TIMER);
-	if (ret) {
-		pr_err("%s: ipa rm timer init failed %d on resourse %d\n",
-		       __func__, ret, IPA_RM_RESOURCE_WWAN_0_PROD);
-		goto timer_init_err;
-	}
-	/* add dependency */
-	ret = ipa_rm_add_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
-			IPA_RM_RESOURCE_Q6_CONS);
-	if (ret)
-		goto add_dpnd_err;
-	/* setup Performance profile */
-	memset(&profile, 0, sizeof(profile));
-	profile.max_supported_bandwidth_mbps = IPA_APPS_MAX_BW_IN_MBPS;
-	ret = ipa_rm_set_perf_profile(IPA_RM_RESOURCE_WWAN_0_PROD,
-			&profile);
-	if (ret)
-		goto set_perf_err;
-	/* IPA_RM configuration ends */
 
 	/* Enable SG support in netdevice. */
 	if (ipa3_rmnet_res.ipa_advertise_sg_support)
@@ -2256,28 +2469,18 @@
 		netif_napi_del(&(rmnet_ipa3_ctx->wwan_priv->napi));
 	unregister_netdev(dev);
 set_perf_err:
-	ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
-		IPA_RM_RESOURCE_Q6_CONS);
-	if (ret)
-		IPAWANERR("Error deleting dependency %d->%d, ret=%d\n",
-			IPA_RM_RESOURCE_WWAN_0_PROD, IPA_RM_RESOURCE_Q6_CONS,
-			ret);
-add_dpnd_err:
-	ret = ipa_rm_inactivity_timer_destroy(
-		IPA_RM_RESOURCE_WWAN_0_PROD); /* IPA_RM */
-	if (ret)
-		IPAWANERR("Error ipa_rm_inactivity_timer_destroy %d, ret=%d\n",
-		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
-timer_init_err:
-	ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
-	if (ret)
-		IPAWANERR("Error deleting resource %d, ret=%d\n",
-		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
-create_rsrc_err:
-
-	if (!atomic_read(&rmnet_ipa3_ctx->is_ssr))
-		ipa3_q6_deinitialize_rm();
-
+	if (ipa3_ctx->use_ipa_pm)
+		ipa3_wwan_deregister_netdev_pm_client();
+	else
+		ipa3_wwan_delete_wwan_rm_resource();
+fail_pm:
+	if (ipa3_ctx->use_ipa_pm) {
+		if (!atomic_read(&rmnet_ipa3_ctx->is_ssr))
+			ipa3_q6_deregister_pm();
+	} else {
+		if (!atomic_read(&rmnet_ipa3_ctx->is_ssr))
+			ipa3_q6_deinitialize_rm();
+	}
 q6_init_err:
 	free_netdev(dev);
 	rmnet_ipa3_ctx->wwan_priv = NULL;
@@ -2313,21 +2516,10 @@
 		netif_napi_del(&(rmnet_ipa3_ctx->wwan_priv->napi));
 	mutex_unlock(&rmnet_ipa3_ctx->pipe_handle_guard);
 	unregister_netdev(IPA_NETDEV());
-	ret = ipa_rm_delete_dependency(IPA_RM_RESOURCE_WWAN_0_PROD,
-		IPA_RM_RESOURCE_Q6_CONS);
-	if (ret < 0)
-		IPAWANERR("Error deleting dependency %d->%d, ret=%d\n",
-			IPA_RM_RESOURCE_WWAN_0_PROD, IPA_RM_RESOURCE_Q6_CONS,
-			ret);
-	ret = ipa_rm_inactivity_timer_destroy(IPA_RM_RESOURCE_WWAN_0_PROD);
-	if (ret < 0)
-		IPAWANERR(
-		"Error ipa_rm_inactivity_timer_destroy resource %d, ret=%d\n",
-		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
-	ret = ipa_rm_delete_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
-	if (ret < 0)
-		IPAWANERR("Error deleting resource %d, ret=%d\n",
-		IPA_RM_RESOURCE_WWAN_0_PROD, ret);
+	if (ipa3_ctx->use_ipa_pm)
+		ipa3_wwan_deregister_netdev_pm_client();
+	else
+		ipa3_wwan_delete_wwan_rm_resource();
 	cancel_work_sync(&ipa3_tx_wakequeue_work);
 	cancel_delayed_work(&ipa_tether_stats_poll_wakequeue_work);
 	if (IPA_NETDEV())
@@ -2381,23 +2573,26 @@
 	if (wwan_ptr == NULL) {
 		IPAWANERR("wwan_ptr is NULL.\n");
 		ret = 0;
-		goto unlock_and_bail;
+		netif_tx_unlock_bh(netdev);
+		goto bail;
 	}
 
 	/* Do not allow A7 to suspend in case there are oustanding packets */
 	if (atomic_read(&wwan_ptr->outstanding_pkts) != 0) {
 		IPAWANDBG("Outstanding packets, postponing AP suspend.\n");
 		ret = -EAGAIN;
-		goto unlock_and_bail;
+		netif_tx_unlock_bh(netdev);
+		goto bail;
 	}
 
 	/* Make sure that there is no Tx operation ongoing */
 	netif_stop_queue(netdev);
-	ipa_rm_release_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
-	ret = 0;
-
-unlock_and_bail:
 	netif_tx_unlock_bh(netdev);
+	if (ipa3_ctx->use_ipa_pm)
+		ipa_pm_deactivate_sync(rmnet_ipa3_ctx->pm_hdl);
+	else
+		ipa_rm_release_resource(IPA_RM_RESOURCE_WWAN_0_PROD);
+	ret = 0;
 bail:
 	IPAWANDBG("Exit with %d\n", ret);
 	return ret;
@@ -2551,9 +2746,11 @@
 	}
 
 	if (type != IPA_TETHERING_STATS_UPDATE_STATS &&
-		type != IPA_TETHERING_STATS_UPDATE_NETWORK_STATS) {
+			type != IPA_TETHERING_STATS_UPDATE_NETWORK_STATS &&
+			type != IPA_PER_CLIENT_STATS_CONNECT_EVENT &&
+			type != IPA_PER_CLIENT_STATS_DISCONNECT_EVENT) {
 		IPAWANERR("Wrong type given. buff %p type %d\n",
-			  buff, type);
+				buff, type);
 	}
 	kfree(buff);
 }
@@ -2799,6 +2996,9 @@
 	enum ipa_upstream_type upstream_type;
 	int rc = 0;
 
+	/* prevent string buffer overflows */
+	data->interface_name[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->interface_name);
 
@@ -3091,6 +3291,10 @@
 	enum ipa_upstream_type upstream_type;
 	int rc = 0;
 
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+	data->tetherIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
@@ -3125,6 +3329,10 @@
 	int rc = 0;
 
 	memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
+
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
@@ -3168,6 +3376,9 @@
 
 	memset(&tether_stats, 0, sizeof(struct wan_ioctl_query_tether_stats));
 
+	/* prevent string buffer overflows */
+	data->upstreamIface[IFNAMSIZ-1] = '\0';
+
 	/* get IPA backhaul type */
 	upstream_type = find_upstream_type(data->upstreamIface);
 
@@ -3297,8 +3508,488 @@
 	}
 }
 
+static inline bool rmnet_ipa3_check_any_client_inited
+(
+	enum ipacm_per_client_device_type device_type
+)
+{
+	int i = 0;
+
+	for (; i < IPA_MAX_NUM_HW_PATH_CLIENTS; i++) {
+		if (rmnet_ipa3_ctx->tether_device[device_type].
+		lan_client[i].client_idx != -1 &&
+		rmnet_ipa3_ctx->tether_device[device_type].
+		lan_client[i].inited) {
+			IPAWANERR("Found client index: %d which is inited\n",
+				 i);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static inline int rmnet_ipa3_get_lan_client_info
+(
+	enum ipacm_per_client_device_type device_type,
+	uint8_t mac[]
+)
+{
+	int i = 0;
+
+	IPAWANDBG("Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		mac[0], mac[1], mac[2],
+		mac[3], mac[4], mac[5]);
+
+	for (; i < IPA_MAX_NUM_HW_PATH_CLIENTS; i++) {
+		if (memcmp(
+		rmnet_ipa3_ctx->tether_device[device_type].
+		lan_client[i].mac,
+		mac,
+		IPA_MAC_ADDR_SIZE) == 0) {
+			IPAWANDBG("Matched client index: %d\n", i);
+			return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static inline int rmnet_ipa3_delete_lan_client_info
+(
+	enum ipacm_per_client_device_type device_type,
+	int lan_clnt_idx
+)
+{
+	struct ipa_lan_client *lan_client = NULL;
+	int i;
+
+	/* Check if the request is to clean up all clients. */
+	if (lan_clnt_idx == 0xffffffff) {
+		/* Reset the complete device info. */
+		memset(&rmnet_ipa3_ctx->tether_device[device_type], 0,
+				sizeof(struct ipa_tether_device_info));
+		rmnet_ipa3_ctx->tether_device[device_type].ul_src_pipe = -1;
+		for (i = 0; i < IPA_MAX_NUM_HW_PATH_CLIENTS; i++)
+			rmnet_ipa3_ctx->tether_device[device_type].
+				lan_client[i].client_idx = -1;
+	} else {
+		lan_client =
+			&rmnet_ipa3_ctx->tether_device[device_type].
+			lan_client[lan_clnt_idx];
+		/* Reset the client info before sending the message. */
+		memset(lan_client, 0, sizeof(struct ipa_lan_client));
+		lan_client->client_idx = -1;
+
+	}
+	return 0;
+}
+
+/* rmnet_ipa3_set_lan_client_info() -
+ * @data - IOCTL data
+ *
+ * This function handles WAN_IOC_SET_LAN_CLIENT_INFO.
+ * It is used to store LAN client information which
+ * is used to fetch the packet stats for a client.
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: Invalid args provided
+ */
+int rmnet_ipa3_set_lan_client_info(
+	struct wan_ioctl_lan_client_info *data)
+{
+
+	struct ipa_lan_client *lan_client = NULL;
+
+
+	IPAWANDBG("Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		data->mac[0], data->mac[1], data->mac[2],
+		data->mac[3], data->mac[4], data->mac[5]);
+
+	/* Check if Device type is valid. */
+	if (data->device_type >= IPACM_MAX_CLIENT_DEVICE_TYPES ||
+		data->device_type < 0) {
+		IPAWANERR("Invalid Device type: %d\n", data->device_type);
+		return -EINVAL;
+	}
+
+	/* Check if Client index is valid. */
+	if (data->client_idx >= IPA_MAX_NUM_HW_PATH_CLIENTS ||
+		data->client_idx < 0) {
+		IPAWANERR("Invalid Client Index: %d\n", data->client_idx);
+		return -EINVAL;
+	}
+
+	mutex_lock(&rmnet_ipa3_ctx->per_client_stats_guard);
+	if (data->client_init) {
+		/* check if the client is already inited. */
+		if (rmnet_ipa3_ctx->tether_device[data->device_type]
+			.lan_client[data->client_idx].inited) {
+			IPAWANERR("Client already inited: %d:%d\n",
+				data->device_type, data->client_idx);
+			mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+			return -EINVAL;
+		}
+	}
+
+	lan_client =
+	&rmnet_ipa3_ctx->tether_device[data->device_type].
+	lan_client[data->client_idx];
+
+	memcpy(lan_client->mac, data->mac, IPA_MAC_ADDR_SIZE);
+
+	lan_client->client_idx = data->client_idx;
+
+	/* Update the Source pipe. */
+	rmnet_ipa3_ctx->tether_device[data->device_type].ul_src_pipe =
+			ipa3_get_ep_mapping(data->ul_src_pipe);
+
+	/* Update the header length if not set. */
+	if (!rmnet_ipa3_ctx->tether_device[data->device_type].hdr_len)
+		rmnet_ipa3_ctx->tether_device[data->device_type].hdr_len =
+			data->hdr_len;
+
+	lan_client->inited = true;
+
+	rmnet_ipa3_ctx->tether_device[data->device_type].num_clients++;
+
+	IPAWANDBG("Set the lan client info: %d, %d, %d\n",
+		lan_client->client_idx,
+		rmnet_ipa3_ctx->tether_device[data->device_type].ul_src_pipe,
+		rmnet_ipa3_ctx->tether_device[data->device_type].num_clients);
+
+	mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+
+	return 0;
+}
+
+/* rmnet_ipa3_delete_lan_client_info() -
+ * @data - IOCTL data
+ *
+ * This function handles WAN_IOC_DELETE_LAN_CLIENT_INFO.
+ * It is used to delete LAN client information which
+ * is used to fetch the packet stats for a client.
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: Invalid args provided
+ */
+int rmnet_ipa3_clear_lan_client_info(
+	struct wan_ioctl_lan_client_info *data)
+{
+
+	struct ipa_lan_client *lan_client = NULL;
+
+
+	IPAWANDBG("Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		data->mac[0], data->mac[1], data->mac[2],
+		data->mac[3], data->mac[4], data->mac[5]);
+
+	/* Check if Device type is valid. */
+	if (data->device_type >= IPACM_MAX_CLIENT_DEVICE_TYPES ||
+		data->device_type < 0) {
+		IPAWANERR("Invalid Device type: %d\n", data->device_type);
+		return -EINVAL;
+	}
+
+	/* Check if Client index is valid. */
+	if (data->client_idx >= IPA_MAX_NUM_HW_PATH_CLIENTS ||
+		data->client_idx < 0) {
+		IPAWANERR("Invalid Client Index: %d\n", data->client_idx);
+		return -EINVAL;
+	}
+
+	mutex_lock(&rmnet_ipa3_ctx->per_client_stats_guard);
+	lan_client =
+	&rmnet_ipa3_ctx->tether_device[data->device_type].
+	lan_client[data->client_idx];
+
+	if (!data->client_init) {
+		/* check if the client is already de-inited. */
+		if (!lan_client->inited) {
+			IPAWANERR("Client already de-inited: %d:%d\n",
+				data->device_type, data->client_idx);
+			mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+			return -EINVAL;
+		}
+	}
+
+	lan_client->inited = false;
+	mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+
+	return 0;
+}
+
+
+/* rmnet_ipa3_send_lan_client_msg() -
+ * @data - IOCTL data
+ *
+ * This function handles WAN_IOC_SEND_LAN_CLIENT_MSG.
+ * It is used to send LAN client information to IPACM.
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: Invalid args provided
+ */
+int rmnet_ipa3_send_lan_client_msg(
+	struct wan_ioctl_send_lan_client_msg *data)
+{
+	struct ipa_msg_meta msg_meta;
+	int rc;
+	struct ipa_lan_client_msg *lan_client;
+
+	/* Notify IPACM to reset the client index. */
+	lan_client = kzalloc(sizeof(struct ipa_lan_client_msg),
+		       GFP_KERNEL);
+	if (!lan_client) {
+		IPAWANERR("Can't allocate memory for tether_info\n");
+		return -ENOMEM;
+	}
+	memset(&msg_meta, 0, sizeof(struct ipa_msg_meta));
+	memcpy(lan_client, &data->lan_client,
+		sizeof(struct ipa_lan_client_msg));
+	msg_meta.msg_type = data->client_event;
+	msg_meta.msg_len = sizeof(struct ipa_lan_client_msg);
+
+	rc = ipa_send_msg(&msg_meta, lan_client, rmnet_ipa_free_msg);
+	if (rc) {
+		IPAWANERR("ipa_send_msg failed: %d\n", rc);
+		kfree(lan_client);
+		return rc;
+	}
+	return 0;
+}
+
+/* rmnet_ipa3_enable_per_client_stats() -
+ * @data - IOCTL data
+ *
+ * This function handles WAN_IOC_ENABLE_PER_CLIENT_STATS.
+ * It is used to indicate Q6 to start capturing per client stats.
+ *
+ * Return codes:
+ * 0: Success
+ * -EINVAL: Invalid args provided
+ */
+int rmnet_ipa3_enable_per_client_stats(
+	bool *data)
+{
+	struct ipa_enable_per_client_stats_req_msg_v01 *req;
+	struct ipa_enable_per_client_stats_resp_msg_v01 *resp;
+	int rc;
+
+	req =
+	kzalloc(sizeof(struct ipa_enable_per_client_stats_req_msg_v01),
+			GFP_KERNEL);
+	if (!req) {
+		IPAWANERR("Can't allocate memory for stats message\n");
+		return -ENOMEM;
+	}
+	resp =
+	kzalloc(sizeof(struct ipa_enable_per_client_stats_resp_msg_v01),
+			GFP_KERNEL);
+	if (!resp) {
+		IPAWANERR("Can't allocate memory for stats message\n");
+		kfree(req);
+		return -ENOMEM;
+	}
+	memset(req, 0,
+		sizeof(struct ipa_enable_per_client_stats_req_msg_v01));
+	memset(resp, 0,
+		sizeof(struct ipa_enable_per_client_stats_resp_msg_v01));
+
+	if (*data)
+		req->enable_per_client_stats = 1;
+	else
+		req->enable_per_client_stats = 0;
+
+	rc = ipa3_qmi_enable_per_client_stats(req, resp);
+	if (rc) {
+		IPAWANERR("can't enable per client stats\n");
+		kfree(req);
+		kfree(resp);
+		return rc;
+	}
+
+	kfree(req);
+	kfree(resp);
+	return 0;
+}
+
+int rmnet_ipa3_query_per_client_stats(
+	struct wan_ioctl_query_per_client_stats *data)
+{
+	struct ipa_get_stats_per_client_req_msg_v01 *req;
+	struct ipa_get_stats_per_client_resp_msg_v01 *resp;
+	int rc, lan_clnt_idx, lan_clnt_idx1, i;
+	struct ipa_lan_client *lan_client = NULL;
+
+
+	IPAWANDBG("Client MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+		data->client_info[0].mac[0],
+		data->client_info[0].mac[1],
+		data->client_info[0].mac[2],
+		data->client_info[0].mac[3],
+		data->client_info[0].mac[4],
+		data->client_info[0].mac[5]);
+
+	/* Check if Device type is valid. */
+	if (data->device_type >= IPACM_MAX_CLIENT_DEVICE_TYPES ||
+		data->device_type < 0) {
+		IPAWANERR("Invalid Device type: %d\n", data->device_type);
+		return -EINVAL;
+	}
+
+	/* Check if num_clients is valid. */
+	if (data->num_clients != IPA_MAX_NUM_HW_PATH_CLIENTS &&
+		data->num_clients != 1) {
+		IPAWANERR("Invalid number of clients: %d\n", data->num_clients);
+		return -EINVAL;
+	}
+
+	mutex_lock(&rmnet_ipa3_ctx->per_client_stats_guard);
+
+	if (data->num_clients == 1) {
+		/* Check if the client info is valid.*/
+		lan_clnt_idx1 = rmnet_ipa3_get_lan_client_info(
+			data->device_type,
+			data->client_info[0].mac);
+		if (lan_clnt_idx1 < 0) {
+			IPAWANERR("Client info not available return.\n");
+			mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+			return -EINVAL;
+		}
+		lan_client =
+			&rmnet_ipa3_ctx->tether_device[data->device_type].
+			lan_client[lan_clnt_idx1];
+		/*
+		 * Check if disconnect flag is set and
+		 * see if all the clients info are cleared.
+		 */
+		if (data->disconnect_clnt &&
+			lan_client->inited) {
+			IPAWANERR("Client not inited. Try again.\n");
+			mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+			return -EAGAIN;
+		}
+
+	} else {
+		/* Max number of clients. */
+		/* Check if disconnect flag is set and
+		 * see if all the clients info are cleared.
+		 */
+		if (data->disconnect_clnt &&
+			rmnet_ipa3_check_any_client_inited(data->device_type)) {
+			IPAWANERR("CLient not inited. Try again.\n");
+			mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+			return -EAGAIN;
+		}
+		lan_clnt_idx1 = 0xffffffff;
+	}
+
+	req = kzalloc(sizeof(struct ipa_get_stats_per_client_req_msg_v01),
+			GFP_KERNEL);
+	if (!req) {
+		IPAWANERR("Can't allocate memory for stats message\n");
+		mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+		return -ENOMEM;
+	}
+	resp = kzalloc(sizeof(struct ipa_get_stats_per_client_resp_msg_v01),
+			GFP_KERNEL);
+	if (!resp) {
+		IPAWANERR("Can't allocate memory for stats message\n");
+		mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+		kfree(req);
+		return -ENOMEM;
+	}
+	memset(req, 0, sizeof(struct ipa_get_stats_per_client_req_msg_v01));
+	memset(resp, 0, sizeof(struct ipa_get_stats_per_client_resp_msg_v01));
+
+	if (data->reset_stats) {
+		req->reset_stats_valid = true;
+		req->reset_stats = true;
+		IPAWANDBG("fetch and reset the client stats\n");
+	}
+
+	req->client_id = lan_clnt_idx1;
+	req->src_pipe_id =
+		rmnet_ipa3_ctx->tether_device[data->device_type].ul_src_pipe;
+
+	IPAWANDBG("fetch the client stats for %d, %d\n", req->client_id,
+		req->src_pipe_id);
+
+	rc = ipa3_qmi_get_per_client_packet_stats(req, resp);
+	if (rc) {
+		IPAWANERR("can't get per client stats\n");
+		mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+		kfree(req);
+		kfree(resp);
+		return rc;
+	}
+
+	if (resp->per_client_stats_list_valid) {
+		for (i = 0; i < resp->per_client_stats_list_len
+				&& i < IPA_MAX_NUM_HW_PATH_CLIENTS; i++) {
+			/* Subtract the header bytes from the DL bytes. */
+			data->client_info[i].ipv4_rx_bytes =
+			(resp->per_client_stats_list[i].num_dl_ipv4_bytes) -
+			(rmnet_ipa3_ctx->
+			tether_device[data->device_type].hdr_len *
+			resp->per_client_stats_list[i].num_dl_ipv4_pkts);
+			/* UL header bytes are subtracted by Q6. */
+			data->client_info[i].ipv4_tx_bytes =
+			resp->per_client_stats_list[i].num_ul_ipv4_bytes;
+			/* Subtract the header bytes from the DL bytes. */
+			data->client_info[i].ipv6_rx_bytes =
+			(resp->per_client_stats_list[i].num_dl_ipv6_bytes) -
+			(rmnet_ipa3_ctx->
+			tether_device[data->device_type].hdr_len *
+			resp->per_client_stats_list[i].num_dl_ipv6_pkts);
+			/* UL header bytes are subtracted by Q6. */
+			data->client_info[i].ipv6_tx_bytes =
+			resp->per_client_stats_list[i].num_ul_ipv6_bytes;
+
+			IPAWANDBG("tx_b_v4(%lu)v6(%lu)rx_b_v4(%lu) v6(%lu)\n",
+			(unsigned long int) data->client_info[i].ipv4_tx_bytes,
+			(unsigned long	int) data->client_info[i].ipv6_tx_bytes,
+			(unsigned long int) data->client_info[i].ipv4_rx_bytes,
+			(unsigned long int) data->client_info[i].ipv6_rx_bytes);
+
+			/* Get the lan client index. */
+			lan_clnt_idx = resp->per_client_stats_list[i].client_id;
+			/* Check if lan_clnt_idx is valid. */
+			if (lan_clnt_idx < 0 ||
+				lan_clnt_idx >= IPA_MAX_NUM_HW_PATH_CLIENTS) {
+				IPAWANERR("Lan client index not valid.\n");
+				mutex_unlock(
+				&rmnet_ipa3_ctx->per_client_stats_guard);
+				kfree(req);
+				kfree(resp);
+				ipa_assert();
+				return -EINVAL;
+			}
+			memcpy(data->client_info[i].mac,
+				rmnet_ipa3_ctx->
+				tether_device[data->device_type].
+				lan_client[lan_clnt_idx].mac,
+				IPA_MAC_ADDR_SIZE);
+		}
+	}
+
+	if (data->disconnect_clnt) {
+		rmnet_ipa3_delete_lan_client_info(data->device_type,
+		lan_clnt_idx1);
+	}
+
+	mutex_unlock(&rmnet_ipa3_ctx->per_client_stats_guard);
+	kfree(req);
+	kfree(resp);
+	return 0;
+}
+
 static int __init ipa3_wwan_init(void)
 {
+	int i, j;
 	rmnet_ipa3_ctx = kzalloc(sizeof(*rmnet_ipa3_ctx), GFP_KERNEL);
 	if (!rmnet_ipa3_ctx) {
 		IPAWANERR("no memory\n");
@@ -3310,6 +4001,14 @@
 
 	mutex_init(&rmnet_ipa3_ctx->pipe_handle_guard);
 	mutex_init(&rmnet_ipa3_ctx->add_mux_channel_lock);
+	mutex_init(&rmnet_ipa3_ctx->per_client_stats_guard);
+	/* Reset the Lan Stats. */
+	for (i = 0; i < IPACM_MAX_CLIENT_DEVICE_TYPES; i++) {
+		rmnet_ipa3_ctx->tether_device[i].ul_src_pipe = -1;
+		for (j = 0; j < IPA_MAX_NUM_HW_PATH_CLIENTS; j++)
+			rmnet_ipa3_ctx->tether_device[i].
+				lan_client[j].client_idx = -1;
+	}
 	rmnet_ipa3_ctx->ipa3_to_apps_hdl = -1;
 	rmnet_ipa3_ctx->apps_to_ipa3_hdl = -1;
 
@@ -3332,6 +4031,7 @@
 	ipa3_qmi_cleanup();
 	mutex_destroy(&rmnet_ipa3_ctx->pipe_handle_guard);
 	mutex_destroy(&rmnet_ipa3_ctx->add_mux_channel_lock);
+	mutex_destroy(&rmnet_ipa3_ctx->per_client_stats_guard);
 	ret = subsys_notif_unregister_notifier(
 		rmnet_ipa3_ctx->subsys_notify_handle, &ipa3_ssr_notifier);
 	if (ret)
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
index c7a6186..0f85e12 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa_fd_ioctl.c
@@ -50,6 +50,18 @@
 #define WAN_IOC_QUERY_TETHER_STATS_ALL32 _IOWR(WAN_IOC_MAGIC, \
 		WAN_IOCTL_QUERY_TETHER_STATS_ALL, \
 		compat_uptr_t)
+#define WAN_IOC_NOTIFY_WAN_STATE32 _IOWR(WAN_IOC_MAGIC, \
+		WAN_IOCTL_NOTIFY_WAN_STATE, \
+		compat_uptr_t)
+#define WAN_IOCTL_ENABLE_PER_CLIENT_STATS32 _IOWR(WAN_IOC_MAGIC, \
+			WAN_IOCTL_ENABLE_PER_CLIENT_STATS, \
+			compat_uptr_t)
+#define WAN_IOCTL_QUERY_PER_CLIENT_STATS32 _IOWR(WAN_IOC_MAGIC, \
+			WAN_IOCTL_QUERY_PER_CLIENT_STATS, \
+			compat_uptr_t)
+#define WAN_IOCTL_SET_LAN_CLIENT_INFO32 _IOWR(WAN_IOC_MAGIC, \
+			WAN_IOCTL_SET_LAN_CLIENT_INFO, \
+			compat_uptr_t)
 #endif
 
 static unsigned int dev_num = 1;
@@ -125,6 +137,33 @@
 		}
 		break;
 
+	case WAN_IOC_ADD_UL_FLT_RULE:
+		IPAWANDBG("device %s got WAN_IOC_UL_ADD_FLT_RULE :>>>\n",
+		DRIVER_NAME);
+		pyld_sz =
+		sizeof(struct ipa_configure_ul_firewall_rules_req_msg_v01);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (ipa3_qmi_ul_filter_request_send(
+			(struct ipa_configure_ul_firewall_rules_req_msg_v01 *)
+			param)) {
+			IPAWANDBG("IPACM->Q6 add ul filter rule failed\n");
+			retval = -EFAULT;
+			break;
+		}
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	case WAN_IOC_ADD_FLT_RULE_INDEX:
 		IPAWANDBG("device %s got WAN_IOC_ADD_FLT_RULE_INDEX :>>>\n",
 		DRIVER_NAME);
@@ -316,6 +355,135 @@
 		}
 		break;
 
+	case WAN_IOC_NOTIFY_WAN_STATE:
+		IPAWANDBG_LOW("device %s got WAN_IOC_NOTIFY_WAN_STATE :>>>\n",
+			DRIVER_NAME);
+		pyld_sz = sizeof(struct wan_ioctl_notify_wan_state);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (u8 __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+
+		if (ipa3_wwan_set_modem_state(
+			(struct wan_ioctl_notify_wan_state *)param)) {
+			IPAWANERR("WAN_IOC_NOTIFY_WAN_STATE failed\n");
+			retval = -EFAULT;
+			break;
+		}
+
+		break;
+	case WAN_IOC_ENABLE_PER_CLIENT_STATS:
+		IPAWANDBG_LOW("got WAN_IOC_ENABLE_PER_CLIENT_STATS :>>>\n");
+		pyld_sz = sizeof(bool);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (rmnet_ipa3_enable_per_client_stats(
+			(bool *)param)) {
+			IPAWANERR("WAN_IOC_ENABLE_PER_CLIENT_STATS failed\n");
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	case WAN_IOC_QUERY_PER_CLIENT_STATS:
+		IPAWANDBG_LOW("got WAN_IOC_QUERY_PER_CLIENT_STATS :>>>\n");
+		pyld_sz = sizeof(struct wan_ioctl_query_per_client_stats);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+
+		retval = rmnet_ipa3_query_per_client_stats(
+			(struct wan_ioctl_query_per_client_stats *)param);
+		if (retval) {
+			IPAWANERR("WAN_IOC_QUERY_PER_CLIENT_STATS failed\n");
+			break;
+		}
+
+		if (copy_to_user((void __user *)arg, param, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case WAN_IOC_SET_LAN_CLIENT_INFO:
+		IPAWANDBG_LOW("got WAN_IOC_SET_LAN_CLIENT_INFO :>>>\n");
+		pyld_sz = sizeof(struct wan_ioctl_lan_client_info);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (rmnet_ipa3_set_lan_client_info(
+			(struct wan_ioctl_lan_client_info *)param)) {
+			IPAWANERR("WAN_IOC_SET_LAN_CLIENT_INFO failed\n");
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+	case WAN_IOC_CLEAR_LAN_CLIENT_INFO:
+		IPAWANDBG_LOW("got WAN_IOC_CLEAR_LAN_CLIENT_INFO :>>>\n");
+		pyld_sz = sizeof(struct wan_ioctl_lan_client_info);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (rmnet_ipa3_clear_lan_client_info(
+			(struct wan_ioctl_lan_client_info *)param)) {
+			IPAWANERR("WAN_IOC_CLEAR_LAN_CLIENT_INFO failed\n");
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
+
+	case WAN_IOC_SEND_LAN_CLIENT_MSG:
+		IPAWANDBG_LOW("got WAN_IOC_SEND_LAN_CLIENT_MSG :>>>\n");
+		pyld_sz = sizeof(struct wan_ioctl_send_lan_client_msg);
+		param = kzalloc(pyld_sz, GFP_KERNEL);
+		if (!param) {
+			retval = -ENOMEM;
+			break;
+		}
+		if (copy_from_user(param, (const void __user *)arg, pyld_sz)) {
+			retval = -EFAULT;
+			break;
+		}
+		if (rmnet_ipa3_send_lan_client_msg(
+			(struct wan_ioctl_send_lan_client_msg *)
+			param)) {
+			IPAWANERR("IOC_SEND_LAN_CLIENT_MSG failed\n");
+			retval = -EFAULT;
+			break;
+		}
+		break;
+
 	default:
 		retval = -ENOTTY;
 	}
diff --git a/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c b/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
index 297f932..7496f28 100644
--- a/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_v3/teth_bridge.c
@@ -50,6 +50,7 @@
 	dev_t dev_num;
 	struct device *dev;
 	struct cdev cdev;
+	u32 modem_pm_hdl;
 };
 static struct ipa3_teth_bridge_ctx *ipa3_teth_ctx;
 
@@ -118,14 +119,25 @@
 */
 int ipa3_teth_bridge_disconnect(enum ipa_client_type client)
 {
+	int res = 0;
+
 	TETH_DBG_FUNC_ENTRY();
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
-				 IPA_RM_RESOURCE_Q6_CONS);
-	ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
-				 IPA_RM_RESOURCE_USB_CONS);
+	if (ipa_pm_is_used()) {
+		res = ipa_pm_deactivate_sync(ipa3_teth_ctx->modem_pm_hdl);
+		if (res) {
+			TETH_ERR("fail to deactivate modem %d\n", res);
+			return res;
+		}
+		res = ipa_pm_destroy();
+	} else {
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_USB_PROD,
+					IPA_RM_RESOURCE_Q6_CONS);
+		ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+					IPA_RM_RESOURCE_USB_CONS);
+	}
 	TETH_DBG_FUNC_EXIT();
 
-	return 0;
+	return res;
 }
 
 /**
@@ -140,9 +152,27 @@
 int ipa3_teth_bridge_connect(struct teth_bridge_connect_params *connect_params)
 {
 	int res = 0;
+	struct ipa_pm_register_params reg_params;
+
+	memset(&reg_params, 0, sizeof(reg_params));
 
 	TETH_DBG_FUNC_ENTRY();
 
+	if (ipa_pm_is_used()) {
+		reg_params.name = "MODEM (USB RMNET)";
+		reg_params.group = IPA_PM_GROUP_MODEM;
+		reg_params.skip_clk_vote = true;
+		res = ipa_pm_register(&reg_params,
+			&ipa3_teth_ctx->modem_pm_hdl);
+		if (res) {
+			TETH_ERR("fail to register with PM %d\n", res);
+			return res;
+		}
+
+		res = ipa_pm_activate_sync(ipa3_teth_ctx->modem_pm_hdl);
+		goto bail;
+	}
+
 	/* Build the dependency graph, first add_dependency call is sync
 	 * in order to make sure the IPA clocks are up before we continue
 	 * and notify the USB driver it may continue.
@@ -234,6 +264,8 @@
 		res = -ENODEV;
 		goto fail_cdev_add;
 	}
+
+	ipa3_teth_ctx->modem_pm_hdl = ~0;
 	TETH_DBG("Tethering bridge driver init OK\n");
 
 	return 0;
diff --git a/drivers/platform/msm/ipa/test/Makefile b/drivers/platform/msm/ipa/test/Makefile
index af46bf2..82bee5d 100644
--- a/drivers/platform/msm/ipa/test/Makefile
+++ b/drivers/platform/msm/ipa/test/Makefile
@@ -1,2 +1,2 @@
 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_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_pm_ut.c b/drivers/platform/msm/ipa/test/ipa_pm_ut.c
new file mode 100644
index 0000000..e07040a
--- /dev/null
+++ b/drivers/platform/msm/ipa/test/ipa_pm_ut.c
@@ -0,0 +1,1758 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/ipa.h>
+#include "../ipa_v3/ipa_pm.h"
+#include "../ipa_v3/ipa_i.h"
+#include "ipa_ut_framework.h"
+#include <linux/delay.h>
+
+struct callback_param {
+	struct completion complete;
+	enum ipa_pm_cb_event evt;
+};
+
+static int ipa_pm_ut_setup(void **ppriv)
+{
+
+	IPA_UT_DBG("Start Setup\n");
+
+	/* decrement UT vote */
+	IPA_ACTIVE_CLIENTS_DEC_SPECIAL("IPA_UT");
+
+	return 0;
+}
+
+static int ipa_pm_ut_teardown(void *priv)
+{
+	IPA_UT_DBG("Start Teardown\n");
+
+	/* undo UT vote */
+	IPA_ACTIVE_CLIENTS_INC_SPECIAL("IPA_UT");
+	return 0;
+}
+
+/* pass completion struct as the user data/callback params */
+static void ipa_pm_call_back(void *user_data, enum ipa_pm_cb_event evt)
+{
+	struct callback_param *param;
+
+	param = (struct callback_param *) user_data;
+	param->evt = evt;
+
+	if (evt == IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_DBG("Activate callback called\n");
+		complete_all(&param->complete);
+	} else if (evt == IPA_PM_REQUEST_WAKEUP) {
+		IPA_UT_DBG("Request Wakeup callback called\n");
+		complete_all(&param->complete);
+	} else
+		IPA_UT_ERR("invalid callback - callback #%d\n", evt);
+}
+
+static int clean_up(int n, ...)
+{
+	va_list args;
+	int i, hdl, rc = 0;
+
+	va_start(args, n);
+
+	IPA_UT_DBG("n = %d\n", n);
+
+	IPA_UT_DBG("Clean up Started");
+
+	for (i = 0; i < n; i++) {
+		hdl = va_arg(args, int);
+
+		rc = ipa_pm_deactivate_sync(hdl);
+		if (rc) {
+			IPA_UT_ERR("fail to deactivate client - rc = %d\n", rc);
+			IPA_UT_TEST_FAIL_REPORT("deactivate failed");
+			return -EFAULT;
+		}
+		rc = ipa_pm_deregister(hdl);
+		if (rc) {
+			IPA_UT_ERR("fail to deregister client - rc = %d\n", rc);
+			IPA_UT_TEST_FAIL_REPORT("deregister failed");
+			return -EFAULT;
+		}
+	}
+	va_end(args);
+	rc = ipa_pm_destroy();
+	if (rc) {
+		IPA_UT_ERR("fail to destroy pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("destroy failed");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+
+/* test 1.1 */
+static int ipa_pm_ut_single_registration(void *priv)
+{
+	int rc = 0;
+	int hdl, vote;
+	struct callback_param user_data;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params register_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+	user_data.evt = IPA_PM_CB_EVENT_MAX;
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&register_params, &hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to register client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deregister(hdl);
+	if (rc == 0) {
+		IPA_UT_ERR("deregister was not unsuccesful - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deregister was not unsuccesful");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deferred deactivate client - rc = %d\n"
+			, rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to deferred deactivate client");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 0) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deregister(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deregister client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to deregister client");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc == 0) {
+		IPA_UT_ERR("activate was not unsuccesful- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate was not unsuccesful");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_destroy();
+	if (rc) {
+		IPA_UT_ERR("terminate failed - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("terminate_failed");
+	}
+
+	return 0;
+}
+
+/* test 1.1 */
+static int ipa_pm_ut_double_register_activate(void *priv)
+{
+	int rc = 0;
+	int hdl, hdl_test, vote;
+	struct callback_param user_data;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params register_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+	user_data.evt = IPA_PM_CB_EVENT_MAX;
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&register_params, &hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to register client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&register_params, &hdl_test);
+	if (rc != -EEXIST) {
+		IPA_UT_ERR("registered client with same name rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("did not to fail register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to do nothing - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("do nothing failed");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to do nothing on 2nd activate = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to not reactivate");
+		return -EFAULT;
+	}
+
+	msleep(200);
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deactivate_sync(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to deactivate client");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 0) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = clean_up(1, hdl);
+	return rc;
+}
+
+/* test 2 */
+static int ipa_pm_ut_deferred_deactivate(void *priv)
+{
+	int rc = 0;
+	int hdl, vote;
+	struct callback_param user_data;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params register_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+	user_data.evt = IPA_PM_CB_EVENT_MAX;
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&register_params, &hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to register client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client - rc = %d\n",
+		rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to reactivate client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("reactivate client failed");
+		return -EFAULT;
+	}
+
+	msleep(200);
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deactivate_sync(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate_sync client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deactivate sync failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = clean_up(1, hdl);
+	return rc;
+}
+
+
+/*test 3*/
+static int ipa_pm_ut_two_clients_activate(void *priv)
+{
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, vote;
+	u32 pipes;
+	struct callback_param user_data_USB;
+	struct callback_param user_data_WLAN;
+
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data_USB
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data_WLAN
+	};
+	user_data_USB.evt = IPA_PM_CB_EVENT_MAX;
+	user_data_WLAN.evt = IPA_PM_CB_EVENT_MAX;
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data_USB.complete);
+	init_completion(&user_data_WLAN.complete);
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_associate_ipa_cons_to_client(hdl_USB, IPA_CLIENT_USB_CONS);
+	if (rc) {
+		IPA_UT_ERR("fail to map client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to map client");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_associate_ipa_cons_to_client(hdl_WLAN,
+		IPA_CLIENT_WLAN1_CONS);
+	if (rc) {
+		IPA_UT_ERR("fail to map client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to map client");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_associate_ipa_cons_to_client(hdl_WLAN,
+		IPA_CLIENT_WLAN2_CONS);
+	if (rc) {
+		IPA_UT_ERR("fail to map client 2 to multiplt pipes rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to map client");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_USB);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work for client 1 - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work for client 2 - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data_USB.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback 1\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data_USB.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data_USB.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data_WLAN.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback 2\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data_WLAN.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data_WLAN.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	reinit_completion(&user_data_USB.complete);
+	reinit_completion(&user_data_WLAN.complete);
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client 1 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	msleep(200);
+
+	rc = ipa_pm_activate(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("no-block activate failed - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("no-block activate fail");
+		return -EFAULT;
+	}
+
+	pipes = 1 << ipa_get_ep_mapping(IPA_CLIENT_USB_CONS);
+	pipes |= 1 << ipa_get_ep_mapping(IPA_CLIENT_WLAN1_CONS);
+	pipes |= 1 << ipa_get_ep_mapping(IPA_CLIENT_WLAN2_CONS);
+
+	IPA_UT_DBG("pipes = %d\n", pipes);
+
+	rc = ipa_pm_handle_suspend(pipes);
+
+	if (!wait_for_completion_timeout(&user_data_USB.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for wakeup_callback 1\n");
+		IPA_UT_TEST_FAIL_REPORT("wakeup callback not called");
+		return -ETIME;
+	}
+
+	if (user_data_USB.evt != IPA_PM_REQUEST_WAKEUP) {
+		IPA_UT_ERR("Callback = %d\n", user_data_USB.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data_WLAN.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for wakeup_callback 2\n");
+		IPA_UT_TEST_FAIL_REPORT("wakeup callback not called");
+		return -ETIME;
+	}
+
+	if (user_data_WLAN.evt != IPA_PM_REQUEST_WAKEUP) {
+		IPA_UT_ERR("Callback = %d\n", user_data_WLAN.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	reinit_completion(&user_data_USB.complete);
+
+	rc = ipa_pm_deactivate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate_sync client 1 - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to deactivate_sync");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("no-block activate failed - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("no-block activate fail");
+		return -EFAULT;
+	}
+
+	pipes = 1 << ipa_get_ep_mapping(IPA_CLIENT_USB_CONS);
+
+	rc = ipa_pm_handle_suspend(pipes);
+
+	if (!wait_for_completion_timeout(&user_data_USB.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for wakeup_callback 1\n");
+		IPA_UT_TEST_FAIL_REPORT("wakeup callback not called");
+		return -ETIME;
+	}
+
+	if (user_data_USB.evt != IPA_PM_REQUEST_WAKEUP) {
+		IPA_UT_ERR("Callback = %d\n", user_data_USB.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	rc = clean_up(2, hdl_USB, hdl_WLAN);
+	return rc;
+}
+
+/* test 4 */
+static int ipa_pm_ut_deactivate_all_deferred(void *priv)
+{
+
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, hdl_MODEM, vote;
+	struct callback_param user_data;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params MODEM_params = {
+		.name = "MODEM",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+	user_data.evt = IPA_PM_CB_EVENT_MAX;
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rce %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_USB);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work for client 1 - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 2- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	if (!wait_for_completion_timeout(&user_data.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback 1\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_register(&MODEM_params, &hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 3 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to no-block activate - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("no-block-activate failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 3) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client 1 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_deactivate_all_deferred();
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("deactivate_all_deferred failed");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("clock vote went below 1");
+		return -EINVAL;
+	}
+
+	rc = clean_up(3, hdl_USB, hdl_WLAN, hdl_MODEM);
+	return rc;
+}
+
+/* test 5 */
+static int ipa_pm_ut_deactivate_after_activate(void *priv)
+{
+
+	int rc = 0;
+	int hdl, vote;
+	struct callback_param user_data;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rce %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&USB_params, &hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work for client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client - rc = %d\n",
+		rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	msleep(200);
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work for client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_deactivate_sync(hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate sync client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deactivate sync fail");
+		return -EFAULT;
+	}
+
+	msleep(200);
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = clean_up(1, hdl);
+	return rc;
+}
+
+/* test 6 */
+static int ipa_pm_ut_atomic_activate(void *priv)
+{
+	int rc = 0;
+	int hdl, vote;
+	struct callback_param user_data;
+	spinlock_t lock;
+	unsigned long flags;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params register_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+		.user_data = &user_data
+	};
+	user_data.evt = IPA_PM_CB_EVENT_MAX;
+
+
+	spin_lock_init(&lock);
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	init_completion(&user_data.complete);
+
+	rc = ipa_pm_register(&register_params, &hdl);
+	if (rc) {
+		IPA_UT_ERR("fail to register client rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	spin_lock_irqsave(&lock, flags);
+	rc = ipa_pm_activate(hdl);
+	if (rc != -EINPROGRESS) {
+		IPA_UT_ERR("fail to queue work - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("queue activate work failed");
+		spin_unlock_irqrestore(&lock, flags);
+		return -EFAULT;
+	}
+	spin_unlock_irqrestore(&lock, flags);
+
+	if (!wait_for_completion_timeout(&user_data.complete, HZ)) {
+		IPA_UT_ERR("timeout waiting for activate_callback\n");
+		IPA_UT_TEST_FAIL_REPORT("activate callback not called");
+		return -ETIME;
+	}
+
+	if (user_data.evt != IPA_PM_CLIENT_ACTIVATED) {
+		IPA_UT_ERR("Callback = %d\n", user_data.evt);
+		IPA_UT_TEST_FAIL_REPORT("wrong callback called");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = clean_up(1, hdl);
+	return rc;
+}
+
+/* test 7 */
+static int ipa_pm_ut_deactivate_loop(void *priv)
+{
+	int rc = 0;
+	int i, hdl_USB, hdl_WLAN, vote;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_USB, 1200);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 800);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 1- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	msleep(200);
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deferred_deactivate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to deffered deactivate client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+		return -EFAULT;
+	}
+
+	for (i = 0; i < 50; i++) {
+		IPA_UT_DBG("Loop iteration #%d\n", i);
+
+		vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+		if (vote != 2) {
+			IPA_UT_ERR("clock vote is at %d\n", vote);
+			IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+			return -EINVAL;
+		}
+
+		rc = ipa_pm_activate(hdl_WLAN);
+		if (rc) {
+			IPA_UT_ERR("fail to undo deactivate for client 2");
+			IPA_UT_ERR(" - rc = %d\n", rc);
+			IPA_UT_TEST_FAIL_REPORT("undo deactivate failed");
+			return -EFAULT;
+		}
+
+		rc = ipa_pm_deferred_deactivate(hdl_WLAN);
+		if (rc) {
+			IPA_UT_ERR("fail to deffered deactivate client");
+			IPA_UT_ERR(" - rc = %d\n", rc);
+			IPA_UT_TEST_FAIL_REPORT("deffered deactivate fail");
+			return -EFAULT;
+		}
+	}
+
+	msleep(200);
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+	rc = clean_up(2, hdl_USB, hdl_WLAN);
+	return rc;
+
+}
+
+
+/*test 8*/
+static int ipa_pm_ut_set_perf_profile(void *priv)
+{
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, vote, idx;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_USB, 1200);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 800);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 1- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 1) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 1200);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 3) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = clean_up(2, hdl_USB, hdl_WLAN);
+	return rc;
+}
+
+/*test 9*/
+static int ipa_pm_ut_group_tput(void *priv)
+{
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, hdl_MODEM, vote, idx;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_APPS,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_APPS,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params MODEM_params = {
+		.name = "MODEM",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_USB, 500);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 800);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 1- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 1) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 1) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_register(&MODEM_params, &hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 3 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_MODEM, 1000);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 3 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 3) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deactivate_sync(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deactivate failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = clean_up(3, hdl_USB, hdl_WLAN, hdl_MODEM);
+	return rc;
+
+}
+
+/*test 10*/
+static int ipa_pm_ut_skip_clk_vote_tput(void *priv)
+{
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, hdl_MODEM, vote, idx;
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000}
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_MODEM,
+		.skip_clk_vote = 1,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params MODEM_params = {
+		.name = "MODEM",
+		.group = IPA_PM_GROUP_MODEM,
+		.skip_clk_vote = 1,
+		.callback = ipa_pm_call_back,
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_USB, 1200);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 800);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 1- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 1) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_register(&MODEM_params, &hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 3 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_MODEM, 2000);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 3 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 1) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 3) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+
+	rc = ipa_pm_deactivate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deactivate failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 0) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	rc = clean_up(3, hdl_USB, hdl_WLAN, hdl_MODEM);
+	return rc;
+}
+
+/* Test 11 */
+static int ipa_pm_ut_simple_exception(void *priv)
+{
+	int rc = 0;
+	int hdl_USB, hdl_WLAN, hdl_MODEM, vote, idx;
+
+	struct ipa_pm_exception exceptions = {
+		.usecase = "USB",
+		.threshold = {1000, 1800},
+	};
+
+	struct ipa_pm_init_params init_params = {
+		.threshold_size = IPA_PM_THRESHOLD_MAX,
+		.default_threshold = {600, 1000},
+		.exception_size = 1,
+		.exceptions[0] = exceptions,
+	};
+
+	struct ipa_pm_register_params USB_params = {
+		.name = "USB",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params WLAN_params = {
+		.name = "WLAN",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	struct ipa_pm_register_params MODEM_params = {
+		.name = "MODEM",
+		.group = IPA_PM_GROUP_DEFAULT,
+		.skip_clk_vote = 0,
+		.callback = ipa_pm_call_back,
+	};
+
+	rc = ipa_pm_init(&init_params);
+	if (rc) {
+		IPA_UT_ERR("Fail to init ipa_pm - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to init params");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&USB_params, &hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_register(&WLAN_params, &hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_USB, 1200);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 1 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_WLAN, 2000);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to activate sync for client 1- rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("activate sync failed");
+		return -EFAULT;
+	}
+
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 1) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_activate(hdl_WLAN);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 2 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_register(&MODEM_params, &hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to register client 3 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to register");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_set_perf_profile(hdl_MODEM, 800);
+	if (rc) {
+		IPA_UT_ERR("fail to set tput for client 2 rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail to set perf profile");
+		return -EFAULT;
+	}
+
+	rc = ipa_pm_activate(hdl_MODEM);
+	if (rc) {
+		IPA_UT_ERR("fail to activate no block for client 3 - rc = %d\n",
+			rc);
+		IPA_UT_TEST_FAIL_REPORT("activate no block failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 3) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	msleep(200);
+	idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 3) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = ipa_pm_deactivate_sync(hdl_USB);
+	if (rc) {
+		IPA_UT_ERR("fail to deactivate client - rc = %d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("deactivate failed");
+		return -EFAULT;
+	}
+
+	vote = atomic_read(&ipa3_ctx->ipa3_active_clients.cnt);
+	if (vote != 2) {
+		IPA_UT_ERR("clock vote is at %d\n", vote);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock vote");
+		return -EINVAL;
+	}
+
+	 idx = ipa3_ctx->ipa3_active_clients.bus_vote_idx;
+	if (idx != 2) {
+		IPA_UT_ERR("clock plan is at %d\n", idx);
+		IPA_UT_TEST_FAIL_REPORT("wrong clock plan");
+		return -EINVAL;
+	}
+
+	rc = clean_up(3, hdl_USB, hdl_WLAN, hdl_MODEM);
+	return rc;
+}
+
+/* Suite definition block */
+IPA_UT_DEFINE_SUITE_START(pm, "PM for IPA",
+	ipa_pm_ut_setup, ipa_pm_ut_teardown)
+{
+	IPA_UT_ADD_TEST(single_registration,
+		"Single Registration/Basic Functions",
+		ipa_pm_ut_single_registration,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(double_register_activate,
+		"double register/activate",
+		ipa_pm_ut_double_register_activate,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(deferred_deactivate,
+		"Deferred_deactivate",
+		ipa_pm_ut_deferred_deactivate,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(two_clients_activate,
+		"Activate two clients",
+		ipa_pm_ut_two_clients_activate,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(deactivate_all_deferred,
+		"Deactivate all deferred",
+		ipa_pm_ut_deactivate_all_deferred,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(deactivate_after_activate,
+		"Deactivate after activate",
+		ipa_pm_ut_deactivate_after_activate,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(atomic_activate,
+		"Atomic activate",
+		ipa_pm_ut_atomic_activate,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(deactivate_loop,
+		"Deactivate Loop",
+		ipa_pm_ut_deactivate_loop,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(set_perf_profile,
+		"Set perf profile",
+		ipa_pm_ut_set_perf_profile,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(group_tput,
+		"Group throughputs",
+		ipa_pm_ut_group_tput,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(skip_clk_vote_tput,
+		"Skip clock vote and tput",
+		ipa_pm_ut_skip_clk_vote_tput,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(simple_exception,
+		"throughput while passing simple exception",
+		ipa_pm_ut_simple_exception,
+		true, IPA_HW_v4_0, IPA_HW_MAX),
+} IPA_UT_DEFINE_SUITE_END(pm);
diff --git a/drivers/platform/msm/ipa/test/ipa_test_dma.c b/drivers/platform/msm/ipa/test/ipa_test_dma.c
index 07be1c3..aa33412 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_dma.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_dma.c
@@ -364,11 +364,11 @@
 }
 
 /**
- * TEST: test control API - enable/disable dma
+ * TEST: test enable/disable dma
  *	1. enable dma
  *	2. disable dma
  */
-static int ipa_test_dma_control_api(void *priv)
+static int ipa_test_dma_enable_disable(void *priv)
 {
 	int rc;
 
@@ -392,6 +392,92 @@
 }
 
 /**
+ * TEST: test init/enable/disable/destroy dma
+ *	1. init dma
+ *	2. enable dma
+ *	3. disable dma
+ *	4. destroy dma
+ */
+static int ipa_test_dma_init_enbl_disable_destroy(void *priv)
+{
+	int rc;
+
+	IPA_UT_LOG("Test Start\n");
+
+	rc = ipa_dma_init();
+	if (rc) {
+		IPA_UT_LOG("DMA Init failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail init dma");
+		return rc;
+	}
+
+	rc = ipa_dma_enable();
+	if (rc) {
+		ipa_dma_destroy();
+		IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+		return rc;
+	}
+
+	rc = ipa_dma_disable();
+	if (rc) {
+		IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+		return rc;
+	}
+
+	ipa_dma_destroy();
+
+	return 0;
+}
+
+/**
+ * TEST: test enablex2/disablex2 dma
+ *	1. enable dma
+ *	2. enable dma
+ *	3. disable dma
+ *	4. disable dma
+ */
+static int ipa_test_dma_enblx2_disablex2(void *priv)
+{
+	int rc;
+
+	IPA_UT_LOG("Test Start\n");
+
+	rc = ipa_dma_enable();
+	if (rc) {
+		ipa_dma_destroy();
+		IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+		return rc;
+	}
+
+	rc = ipa_dma_enable();
+	if (rc) {
+		ipa_dma_destroy();
+		IPA_UT_LOG("DMA enable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail enable dma");
+		return rc;
+	}
+
+	rc = ipa_dma_disable();
+	if (rc) {
+		IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+		return rc;
+	}
+
+	rc = ipa_dma_disable();
+	if (rc) {
+		IPA_UT_LOG("DMA disable failed rc=%d\n", rc);
+		IPA_UT_TEST_FAIL_REPORT("fail disable dma");
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
  * TEST: memcpy before dma enable
  *
  *	1. sync memcpy - should fail
@@ -999,9 +1085,17 @@
 IPA_UT_DEFINE_SUITE_START(dma, "DMA for GSI",
 	ipa_test_dma_setup, ipa_test_dma_teardown)
 {
-	IPA_UT_ADD_TEST(control_api,
-		"Control API",
-		ipa_test_dma_control_api,
+	IPA_UT_ADD_TEST(init_enable_disable_destroy,
+		"Init->Enable->Disable->Destroy",
+		ipa_test_dma_enable_disable,
+		true, IPA_HW_v3_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(initx2_enable_disable_destroyx2,
+		"Initx2->Enable->Disable->Destroyx2",
+		ipa_test_dma_init_enbl_disable_destroy,
+		true, IPA_HW_v3_0, IPA_HW_MAX),
+	IPA_UT_ADD_TEST(init_enablex2_disablex2_destroy,
+		"Init->Enablex2->Disablex2->Destroy",
+		ipa_test_dma_enblx2_disablex2,
 		true, IPA_HW_v3_0, IPA_HW_MAX),
 	IPA_UT_ADD_TEST(memcpy_before_enable,
 		"Call memcpy before dma enable and expect it to fail",
diff --git a/drivers/platform/msm/ipa/test/ipa_test_mhi.c b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
index 3a89c7d..98861de 100644
--- a/drivers/platform/msm/ipa/test/ipa_test_mhi.c
+++ b/drivers/platform/msm/ipa/test/ipa_test_mhi.c
@@ -868,7 +868,7 @@
  *
  * To be run during tests
  * 1. MHI init (Ready state)
- * 2. Conditional MHO start and connect (M0 state)
+ * 2. Conditional MHI start and connect (M0 state)
  */
 static int ipa_mhi_test_initialize_driver(bool skip_start_and_conn)
 {
@@ -879,7 +879,6 @@
 	struct ipa_mhi_connect_params cons_params;
 	struct ipa_mhi_mmio_register_set *p_mmio;
 	struct ipa_mhi_channel_context_array *p_ch_ctx_array;
-	bool is_dma;
 	u64 phys_addr;
 
 	IPA_UT_LOG("Entry\n");
@@ -912,29 +911,6 @@
 		return -ETIME;
 	}
 
-	if (ipa_mhi_is_using_dma(&is_dma)) {
-		IPA_UT_LOG("is_dma checkign failed. Is MHI loaded?\n");
-		IPA_UT_TEST_FAIL_REPORT("failed checking using dma");
-		return -EPERM;
-	}
-
-	if (is_dma) {
-		IPA_UT_LOG("init ipa_dma\n");
-		rc = ipa_dma_init();
-		if (rc && rc != -EFAULT) {
-			IPA_UT_LOG("ipa_dma_init failed, %d\n", rc);
-			IPA_UT_TEST_FAIL_REPORT("failed init dma");
-			return rc;
-		}
-		IPA_UT_LOG("enable ipa_dma\n");
-		rc = ipa_dma_enable();
-		if (rc && rc != -EPERM) {
-			IPA_UT_LOG("ipa_dma_enable failed, %d\n", rc);
-			IPA_UT_TEST_FAIL_REPORT("failed enable dma");
-			return rc;
-		}
-	}
-
 	if (!skip_start_and_conn) {
 		memset(&start_params, 0, sizeof(start_params));
 		start_params.channel_context_array_addr = p_mmio->ccabap;
@@ -1383,11 +1359,11 @@
 	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), GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
-			event_ring_index + IPA_MHI_GSI_ER_START, 0));
+			event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
 	iowrite32(p_events[event_ring_index].wp,
 		test_mhi_ctx->gsi_mmio +
 		GSI_EE_n_EV_CH_k_DOORBELL_0_OFFS(
-			event_ring_index + IPA_MHI_GSI_ER_START, 0));
+			event_ring_index + ipa3_ctx->mhi_evid_limits[0], 0));
 
 	for (i = 0; i < buf_array_size; i++) {
 		/* calculate virtual pointer for current WP and RP */
@@ -1545,7 +1521,7 @@
 	}
 
 	if (!should_success && rc != -EAGAIN) {
-		IPA_UT_LOG("ipa_mhi_suspenddid not return -EAGAIN fail %d\n",
+		IPA_UT_LOG("ipa_mhi_suspend did not return -EAGAIN fail %d\n",
 			rc);
 		IPA_UT_TEST_FAIL_REPORT("suspend succeeded unexpectedly");
 		return -EFAULT;
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_framework.c b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
index 3bf9ac1..036902a 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_framework.c
+++ b/drivers/platform/msm/ipa/test/ipa_ut_framework.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -35,6 +35,7 @@
  * @ipa_dbgfs_root: IPA root debugfs folder
  * @test_dbgfs_root: UT root debugfs folder. Sub-folder of IPA root
  * @test_dbgfs_suites: Suites root debugfs folder. Sub-folder of UT root
+ * @wq: workqueue struct for write operations
  */
 struct ipa_ut_context {
 	bool inited;
@@ -43,6 +44,17 @@
 	struct dentry *ipa_dbgfs_root;
 	struct dentry *test_dbgfs_root;
 	struct dentry *test_dbgfs_suites;
+	struct workqueue_struct *wq;
+};
+
+/**
+ * struct ipa_ut_dbgfs_test_write_work_ctx - work_queue context
+ * @dbgfs: work_struct for the write_work
+ * @file: file  to be writen to
+ */
+struct ipa_ut_dbgfs_test_write_work_ctx {
+	struct work_struct dbgfs_work;
+	struct file *file;
 };
 
 static ssize_t ipa_ut_dbgfs_enable_read(struct file *file,
@@ -183,8 +195,9 @@
 }
 
 /**
- * ipa_ut_dbgfs_meta_test_write() - Debugfs write func for a for a meta test
- * @params: write fops
+ * ipa_ut_dbgfs_meta_test_write_work_func() - Debugfs write func for a
+ * for a meta test
+ * @params: work struct containing write fops and completion object
  *
  * Used to run all/regression tests in a suite
  * Create log buffer that the test can use to store ongoing logs
@@ -202,19 +215,28 @@
  *
  * Return: Negative in failure, given characters amount in success
  */
-static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
-	const char __user *buf, size_t count, loff_t *ppos)
+static void ipa_ut_dbgfs_meta_test_write_work_func(struct work_struct *work)
 {
+	struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
 	struct ipa_ut_suite *suite;
+	struct file *file;
 	int i;
 	enum ipa_hw_type ipa_ver;
 	int rc = 0;
 	long meta_type;
 	bool tst_fail = false;
 
+	write_work_ctx = container_of(work, struct
+		ipa_ut_dbgfs_test_write_work_ctx, dbgfs_work);
+
 	IPA_UT_DBG("Entry\n");
 
 	mutex_lock(&ipa_ut_ctx->lock);
+	file = write_work_ctx->file;
+	if (file == NULL) {
+		rc = -EFAULT;
+		goto unlock_mutex;
+	}
 	suite = file->f_inode->i_private;
 	ipa_assert_on(!suite);
 	meta_type = (long)(file->private_data);
@@ -328,7 +350,37 @@
 	_IPA_UT_TEST_LOG_BUF_NAME = NULL;
 unlock_mutex:
 	mutex_unlock(&ipa_ut_ctx->lock);
-	return ((!rc && !tst_fail) ? count : -EFAULT);
+	kfree(write_work_ctx);
+}
+
+/*
+ * ipa_ut_dbgfs_meta_test_write() - Debugfs write func for a for a meta test
+ * @params: write fops
+ *
+ * Run all tests in a suite using a work queue so it does not race with
+ * debugfs_remove_recursive
+ *
+ * Return: Negative if failure. Amount of characters written if success.
+ */
+static ssize_t ipa_ut_dbgfs_meta_test_write(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
+
+	write_work_ctx = kzalloc(sizeof(*write_work_ctx), GFP_KERNEL);
+	if (!write_work_ctx) {
+		IPA_UT_ERR("kzalloc err.\n");
+		return -ENOMEM;
+	}
+
+	write_work_ctx->file = file;
+
+	INIT_WORK(&write_work_ctx->dbgfs_work,
+		ipa_ut_dbgfs_meta_test_write_work_func);
+
+	queue_work(ipa_ut_ctx->wq, &write_work_ctx->dbgfs_work);
+
+	return count;
 }
 
 /**
@@ -458,18 +510,27 @@
  *
  * Return: Negative in failure, given characters amount in success
  */
-static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
-	const char __user *buf, size_t count, loff_t *ppos)
+static void ipa_ut_dbgfs_test_write_work_func(struct work_struct *work)
 {
+	struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
 	struct ipa_ut_test *test;
 	struct ipa_ut_suite *suite;
+	struct file *file;
 	bool tst_fail = false;
 	int rc = 0;
 	enum ipa_hw_type ipa_ver;
 
+	write_work_ctx = container_of(work, struct
+		ipa_ut_dbgfs_test_write_work_ctx, dbgfs_work);
+
 	IPA_UT_DBG("Entry\n");
 
 	mutex_lock(&ipa_ut_ctx->lock);
+	file = write_work_ctx->file;
+	if (file == NULL) {
+		rc = -EFAULT;
+		goto unlock_mutex;
+	}
 	test = file->f_inode->i_private;
 	ipa_assert_on(!test);
 
@@ -558,9 +619,29 @@
 	_IPA_UT_TEST_LOG_BUF_NAME = NULL;
 unlock_mutex:
 	mutex_unlock(&ipa_ut_ctx->lock);
-	return ((!rc && !tst_fail) ? count : -EFAULT);
+	kfree(write_work_ctx);
 }
 
+static ssize_t ipa_ut_dbgfs_test_write(struct file *file,
+	const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct ipa_ut_dbgfs_test_write_work_ctx *write_work_ctx;
+
+	write_work_ctx = kzalloc(sizeof(*write_work_ctx), GFP_KERNEL);
+	if (!write_work_ctx) {
+		IPA_UT_ERR("kzalloc err.\n");
+		return -ENOMEM;
+	}
+
+	write_work_ctx->file = file;
+
+	INIT_WORK(&write_work_ctx->dbgfs_work,
+		ipa_ut_dbgfs_test_write_work_func);
+
+	queue_work(ipa_ut_ctx->wq, &write_work_ctx->dbgfs_work);
+
+	return count;
+}
 /**
  * ipa_ut_dbgfs_test_read() - Debugfs read function for a test
  * @params: read fops
@@ -885,6 +966,13 @@
 		}
 	}
 
+	ipa_ut_ctx->wq = create_singlethread_workqueue("ipa_ut_dbgfs");
+	if (!ipa_ut_ctx->wq) {
+		IPA_UT_ERR("create workqueue failed\n");
+		ret = -ENOMEM;
+		goto unlock_mutex;
+	}
+
 	ipa_ut_ctx->test_dbgfs_root = debugfs_create_dir("test",
 		ipa_ut_ctx->ipa_dbgfs_root);
 	if (!ipa_ut_ctx->test_dbgfs_root ||
@@ -927,6 +1015,7 @@
 	IPA_UT_DBG("Entry\n");
 
 	mutex_lock(&ipa_ut_ctx->lock);
+	destroy_workqueue(ipa_ut_ctx->wq);
 	if (ipa_ut_ctx->enabled)
 		ipa_ut_framework_disable();
 	if (ipa_ut_ctx->inited)
diff --git a/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
index 823edcf..35f2878 100644
--- a/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
+++ b/drivers/platform/msm/ipa/test/ipa_ut_suite_list.h
@@ -22,6 +22,7 @@
  */
 IPA_UT_DECLARE_SUITE(mhi);
 IPA_UT_DECLARE_SUITE(dma);
+IPA_UT_DECLARE_SUITE(pm);
 IPA_UT_DECLARE_SUITE(example);
 IPA_UT_DECLARE_SUITE(hw_stats);
 
@@ -34,6 +35,7 @@
 {
 	IPA_UT_REGISTER_SUITE(mhi),
 	IPA_UT_REGISTER_SUITE(dma),
+	IPA_UT_REGISTER_SUITE(pm),
 	IPA_UT_REGISTER_SUITE(example),
 	IPA_UT_REGISTER_SUITE(hw_stats),
 } IPA_UT_DEFINE_ALL_SUITES_END;
diff --git a/drivers/platform/msm/msm_11ad/msm_11ad.c b/drivers/platform/msm/msm_11ad/msm_11ad.c
index e76ff14..f64e9de 100644
--- a/drivers/platform/msm/msm_11ad/msm_11ad.c
+++ b/drivers/platform/msm/msm_11ad/msm_11ad.c
@@ -126,6 +126,7 @@
 	struct cpumask boost_cpu;
 
 	bool keep_radio_on_during_sleep;
+	int features;
 };
 
 static LIST_HEAD(dev_list);
@@ -1085,6 +1086,10 @@
 	ctx->keep_radio_on_during_sleep = of_property_read_bool(of_node,
 		"qcom,keep-radio-on-during-sleep");
 	ctx->bus_scale = msm_bus_cl_get_pdata(pdev);
+	if (!ctx->bus_scale) {
+		dev_err(ctx->dev, "Unable to read bus-scaling from DT\n");
+		return -EINVAL;
+	}
 
 	ctx->smmu_s1_en = of_property_read_bool(of_node, "qcom,smmu-s1-en");
 	if (ctx->smmu_s1_en) {
@@ -1113,7 +1118,7 @@
 	rc = msm_11ad_init_vregs(ctx);
 	if (rc) {
 		dev_err(ctx->dev, "msm_11ad_init_vregs failed: %d\n", rc);
-		return rc;
+		goto out_bus_scale;
 	}
 	rc = msm_11ad_enable_vregs(ctx);
 	if (rc) {
@@ -1172,6 +1177,18 @@
 	}
 	ctx->pcidev = pcidev;
 
+	rc = msm_pcie_pm_control(MSM_PCIE_RESUME, pcidev->bus->number,
+				 pcidev, NULL, 0);
+	if (rc) {
+		dev_err(ctx->dev, "msm_pcie_pm_control(RESUME) failed:%d\n",
+			rc);
+		goto out_rc;
+	}
+
+	pci_set_power_state(pcidev, PCI_D0);
+
+	pci_restore_state(ctx->pcidev);
+
 	/* Read current state */
 	rc = pci_read_config_dword(pcidev,
 				   PCIE20_CAP_LINKCTRLSTATUS, &val);
@@ -1179,7 +1196,7 @@
 		dev_err(ctx->dev,
 			"reading PCIE20_CAP_LINKCTRLSTATUS failed:%d\n",
 			rc);
-		goto out_rc;
+		goto out_suspend;
 	}
 
 	ctx->l1_enabled_in_enum = val & PCI_EXP_LNKCTL_ASPM_L1;
@@ -1192,7 +1209,7 @@
 		if (rc) {
 			dev_err(ctx->dev,
 				"failed to disable L1, rc %d\n", rc);
-			goto out_rc;
+			goto out_suspend;
 		}
 	}
 
@@ -1212,7 +1229,7 @@
 	rc = msm_11ad_ssr_init(ctx);
 	if (rc) {
 		dev_err(ctx->dev, "msm_11ad_ssr_init failed: %d\n", rc);
-		goto out_rc;
+		goto out_suspend;
 	}
 
 	msm_11ad_init_cpu_boost(ctx);
@@ -1234,6 +1251,9 @@
 	msm_11ad_suspend_power_off(ctx);
 
 	return 0;
+out_suspend:
+	msm_pcie_pm_control(MSM_PCIE_SUSPEND, pcidev->bus->number,
+			    pcidev, NULL, 0);
 out_rc:
 	if (ctx->gpio_en >= 0)
 		gpio_direction_output(ctx->gpio_en, 0);
@@ -1247,6 +1267,8 @@
 	msm_11ad_release_clocks(ctx);
 	msm_11ad_disable_vregs(ctx);
 	msm_11ad_release_vregs(ctx);
+out_bus_scale:
+	msm_bus_cl_clear_pdata(ctx->bus_scale);
 
 	return rc;
 }
@@ -1261,7 +1283,6 @@
 		 ctx->pcidev);
 	kfree(ctx->pristine_state);
 
-	msm_bus_cl_clear_pdata(ctx->bus_scale);
 	pci_dev_put(ctx->pcidev);
 	if (ctx->gpio_en >= 0) {
 		gpio_direction_output(ctx->gpio_en, 0);
@@ -1422,6 +1443,7 @@
 		dev_info(ctx->dev, "SSR requested\n");
 		(void)msm_11ad_ssr_copy_ramdump(ctx);
 		ctx->recovery_in_progress = true;
+		subsys_set_crash_status(ctx->subsys, CRASH_STATUS_ERR_FATAL);
 		rc = subsystem_restart_dev(ctx->subsys);
 		if (rc) {
 			dev_err(ctx->dev,
@@ -1444,9 +1466,19 @@
 		break;
 	case WIL_PLATFORM_EVT_PRE_RESET:
 		/*
-		 * TODO: Enable rf_clk3 clock before resetting the device to
-		 * ensure stable ref clock during the device reset
+		 * Enable rf_clk3 clock before resetting the device to ensure
+		 * stable ref clock during the device reset
 		 */
+		if (ctx->features &
+		    BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL)) {
+			rc = msm_11ad_enable_clk(ctx, &ctx->rf_clk3);
+			if (rc) {
+				dev_err(ctx->dev,
+					"failed to enable clk, rc %d\n", rc);
+				break;
+			}
+		}
+
 		/* Re-enable L1 in case it was enabled in enumeration */
 		if (ctx->l1_enabled_in_enum) {
 			rc = msm_11ad_ctrl_aspm_l1(ctx, true);
@@ -1457,9 +1489,12 @@
 		break;
 	case WIL_PLATFORM_EVT_FW_RDY:
 		/*
-		 * TODO: Disable rf_clk3 clock after the device is up to allow
+		 * Disable rf_clk3 clock after the device is up to allow
 		 * the device to control it via its GPIO for power saving
 		 */
+		if (ctx->features &
+		    BIT(WIL_PLATFORM_FEATURE_FW_EXT_CLK_CONTROL))
+			msm_11ad_disable_clk(ctx, &ctx->rf_clk3);
 		break;
 	default:
 		pr_debug("%s: Unhandled event %d\n", __func__, evt);
@@ -1469,14 +1504,28 @@
 	return rc;
 }
 
-static bool ops_keep_radio_on_during_sleep(void *handle)
+static int ops_get_capa(void *handle)
 {
 	struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
+	int capa;
 
 	pr_debug("%s: keep radio on during sleep is %s\n", __func__,
 		 ctx->keep_radio_on_during_sleep ? "allowed" : "not allowed");
 
-	return ctx->keep_radio_on_during_sleep;
+	capa = (ctx->keep_radio_on_during_sleep ?
+			BIT(WIL_PLATFORM_CAPA_RADIO_ON_IN_SUSPEND) : 0) |
+		BIT(WIL_PLATFORM_CAPA_T_PWR_ON_0) |
+		BIT(WIL_PLATFORM_CAPA_EXT_CLK);
+
+	return capa;
+}
+
+static void ops_set_features(void *handle, int features)
+{
+	struct msm11ad_ctx *ctx = (struct msm11ad_ctx *)handle;
+
+	pr_debug("%s: features 0x%x\n", __func__, features);
+	ctx->features = features;
 }
 
 void *msm_11ad_dev_init(struct device *dev, struct wil_platform_ops *ops,
@@ -1518,7 +1567,8 @@
 	ops->resume = ops_resume;
 	ops->uninit = ops_uninit;
 	ops->notify = ops_notify;
-	ops->keep_radio_on_during_sleep = ops_keep_radio_on_during_sleep;
+	ops->get_capa = ops_get_capa;
+	ops->set_features = ops_set_features;
 
 	return ctx;
 }
diff --git a/drivers/platform/msm/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index d5fc867..24b8e2c 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -147,6 +147,12 @@
 	int ret = 0;
 	int state;
 
+	if (!ext_disp->ops) {
+		pr_err("codec not registered, skip notification\n");
+		ret = -EPERM;
+		goto end;
+	}
+
 	state = ext_disp->audio_sdev.state;
 	ret = extcon_set_state_sync(&ext_disp->audio_sdev,
 			ext_disp->current_disp, !!new_state);
@@ -155,7 +161,7 @@
 			ext_disp->audio_sdev.state == state ?
 			"is same" : "switched to",
 			ext_disp->audio_sdev.state);
-
+end:
 	return ret;
 }
 
@@ -218,15 +224,10 @@
 		goto end;
 	}
 
-	if (!ext_disp->ops) {
-		pr_err("codec ops not registered\n");
-		ret = -EINVAL;
-		goto end;
-	}
-
 	if (state == EXT_DISPLAY_CABLE_CONNECT) {
 		/* connect codec with interface */
-		*ext_disp->ops = data->codec_ops;
+		if (ext_disp->ops)
+			*ext_disp->ops = data->codec_ops;
 
 		/* update pdev for interface to use */
 		ext_disp->ext_disp_data.intf_pdev = data->pdev;
@@ -285,12 +286,41 @@
 	return ret;
 }
 
+static void msm_ext_disp_ready_for_display(struct msm_ext_disp *ext_disp)
+{
+	int ret;
+	struct msm_ext_disp_init_data *data = NULL;
+
+	if (!ext_disp) {
+		pr_err("invalid input\n");
+		return;
+	}
+
+	ret = msm_ext_disp_get_intf_data(ext_disp,
+		ext_disp->current_disp, &data);
+	if (ret) {
+		pr_err("%s not found\n",
+			msm_ext_disp_name(ext_disp->current_disp));
+		return;
+	}
+
+	*ext_disp->ops = data->codec_ops;
+	data->codec_ops.ready(ext_disp->pdev);
+}
+
 int msm_hdmi_register_audio_codec(struct platform_device *pdev,
 		struct msm_ext_disp_audio_codec_ops *ops)
 {
 	return msm_ext_disp_register_audio_codec(pdev, ops);
 }
 
+/**
+ * Register audio codec ops to display driver
+ * for HDMI/Display Port usecase support.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
 int msm_ext_disp_register_audio_codec(struct platform_device *pdev,
 		struct msm_ext_disp_audio_codec_ops *ops)
 {
@@ -327,12 +357,17 @@
 
 end:
 	mutex_unlock(&ext_disp->lock);
+	if (ext_disp->current_disp != EXT_DISPLAY_TYPE_MAX)
+		msm_ext_disp_ready_for_display(ext_disp);
 
 	return ret;
 }
+EXPORT_SYMBOL(msm_ext_disp_register_audio_codec);
 
 static int msm_ext_disp_validate_intf(struct msm_ext_disp_init_data *init_data)
 {
+	struct msm_ext_disp_audio_codec_ops *ops;
+
 	if (!init_data) {
 		pr_err("Invalid init_data\n");
 		return -EINVAL;
@@ -343,9 +378,15 @@
 		return -EINVAL;
 	}
 
-	if (!init_data->codec_ops.get_audio_edid_blk ||
-			!init_data->codec_ops.cable_status ||
-			!init_data->codec_ops.audio_info_setup) {
+	ops = &init_data->codec_ops;
+
+	if (!ops->audio_info_setup   ||
+	    !ops->get_audio_edid_blk ||
+	    !ops->cable_status       ||
+	    !ops->get_intf_id        ||
+	    !ops->teardown_done      ||
+	    !ops->acknowledge        ||
+	    !ops->ready) {
 		pr_err("Invalid codec operation pointers\n");
 		return -EINVAL;
 	}
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index aafb8fc..94736d4 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/msm-bus.h>
 #include <linux/msm-bus-board.h>
 #include <linux/of.h>
@@ -575,13 +576,6 @@
 }
 EXPORT_SYMBOL(se_config_packing);
 
-static void se_geni_clks_off(struct se_geni_rsc *rsc)
-{
-	clk_disable_unprepare(rsc->se_clk);
-	clk_disable_unprepare(rsc->s_ahb_clk);
-	clk_disable_unprepare(rsc->m_ahb_clk);
-}
-
 static bool geni_se_check_bus_bw(struct geni_se_device *geni_se_dev)
 {
 	int i;
@@ -641,6 +635,37 @@
 }
 
 /**
+ * se_geni_clks_off() - Turn off clocks associated with the serial
+ *                      engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_off(struct se_geni_rsc *rsc)
+{
+	int ret = 0;
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
+		return -ENODEV;
+
+	clk_disable_unprepare(rsc->se_clk);
+	clk_disable_unprepare(rsc->s_ahb_clk);
+	clk_disable_unprepare(rsc->m_ahb_clk);
+
+	ret = geni_se_rmv_ab_ib(geni_se_dev, rsc);
+	if (ret)
+		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
+			"%s: Error %d during bus_bw_update\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL(se_geni_clks_off);
+
+/**
  * se_geni_resources_off() - Turn off resources associated with the serial
  *                           engine
  * @rsc:	Handle to resources associated with the serial engine.
@@ -665,37 +690,14 @@
 			"%s: Error %d pinctrl_select_state\n", __func__, ret);
 		return ret;
 	}
-	se_geni_clks_off(rsc);
-	ret = geni_se_rmv_ab_ib(geni_se_dev, rsc);
+	ret = se_geni_clks_off(rsc);
 	if (ret)
 		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
-			"%s: Error %d during bus_bw_update\n", __func__, ret);
+			"%s: Error %d turning off clocks\n", __func__, ret);
 	return ret;
 }
 EXPORT_SYMBOL(se_geni_resources_off);
 
-static int se_geni_clks_on(struct se_geni_rsc *rsc)
-{
-	int ret;
-
-	ret = clk_prepare_enable(rsc->m_ahb_clk);
-	if (ret)
-		return ret;
-
-	ret = clk_prepare_enable(rsc->s_ahb_clk);
-	if (ret) {
-		clk_disable_unprepare(rsc->m_ahb_clk);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(rsc->se_clk);
-	if (ret) {
-		clk_disable_unprepare(rsc->s_ahb_clk);
-		clk_disable_unprepare(rsc->m_ahb_clk);
-	}
-	return ret;
-}
-
 static int geni_se_add_ab_ib(struct geni_se_device *geni_se_dev,
 			     struct se_geni_rsc *rsc)
 {
@@ -733,13 +735,13 @@
 }
 
 /**
- * se_geni_resources_on() - Turn on resources associated with the serial
- *                          engine
+ * se_geni_clks_on() - Turn on clocks associated with the serial
+ *                     engine
  * @rsc:	Handle to resources associated with the serial engine.
  *
  * Return:	0 on success, standard Linux error codes on failure/error.
  */
-int se_geni_resources_on(struct se_geni_rsc *rsc)
+int se_geni_clks_on(struct se_geni_rsc *rsc)
 {
 	int ret = 0;
 	struct geni_se_device *geni_se_dev;
@@ -758,11 +760,52 @@
 		return ret;
 	}
 
+	ret = clk_prepare_enable(rsc->m_ahb_clk);
+	if (ret)
+		goto clks_on_err1;
+
+	ret = clk_prepare_enable(rsc->s_ahb_clk);
+	if (ret)
+		goto clks_on_err2;
+
+	ret = clk_prepare_enable(rsc->se_clk);
+	if (ret)
+		goto clks_on_err3;
+	return 0;
+
+clks_on_err3:
+	clk_disable_unprepare(rsc->s_ahb_clk);
+clks_on_err2:
+	clk_disable_unprepare(rsc->m_ahb_clk);
+clks_on_err1:
+	geni_se_rmv_ab_ib(geni_se_dev, rsc);
+	return ret;
+}
+EXPORT_SYMBOL(se_geni_clks_on);
+
+/**
+ * se_geni_resources_on() - Turn on resources associated with the serial
+ *                          engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_resources_on(struct se_geni_rsc *rsc)
+{
+	int ret = 0;
+	struct geni_se_device *geni_se_dev;
+
+	if (unlikely(!rsc || !rsc->wrapper_dev))
+		return -EINVAL;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev))
+		return -EPROBE_DEFER;
+
 	ret = se_geni_clks_on(rsc);
 	if (ret) {
 		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
 			"%s: Error %d during clks_on\n", __func__, ret);
-		geni_se_rmv_ab_ib(geni_se_dev, rsc);
 		return ret;
 	}
 
@@ -771,7 +814,6 @@
 		GENI_SE_ERR(geni_se_dev->log_ctx, false, NULL,
 			"%s: Error %d pinctrl_select_state\n", __func__, ret);
 		se_geni_clks_off(rsc);
-		geni_se_rmv_ab_ib(geni_se_dev, rsc);
 	}
 	return ret;
 }
@@ -1247,6 +1289,76 @@
 }
 EXPORT_SYMBOL(geni_se_iommu_free_buf);
 
+/**
+ * geni_se_dump_dbg_regs() - Print relevant registers that capture most
+ *			accurately the state of an SE.
+ * @_dev:		Pointer to the SE's device.
+ * @iomem:		Base address of the SE's register space.
+ * @ipc:		IPC log context handle.
+ *
+ * This function is used to print out all the registers that capture the state
+ * of an SE to help debug any errors.
+ *
+ * Return:	None
+ */
+void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base,
+				void *ipc)
+{
+	u32 m_cmd0 = 0;
+	u32 m_irq_status = 0;
+	u32 geni_status = 0;
+	u32 geni_ios = 0;
+	u32 dma_rx_irq = 0;
+	u32 dma_tx_irq = 0;
+	u32 rx_fifo_status = 0;
+	u32 tx_fifo_status = 0;
+	u32 se_dma_dbg = 0;
+	u32 m_cmd_ctrl = 0;
+	u32 se_dma_rx_len = 0;
+	u32 se_dma_rx_len_in = 0;
+	u32 se_dma_tx_len = 0;
+	u32 se_dma_tx_len_in = 0;
+	struct geni_se_device *geni_se_dev;
+
+	if (!ipc)
+		return;
+
+	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
+	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
+		return;
+	if (unlikely(list_empty(&rsc->ab_list) || list_empty(&rsc->ib_list))) {
+		GENI_SE_DBG(ipc, false, NULL, "%s: Clocks not on\n", __func__);
+		return;
+	}
+	m_cmd0 = geni_read_reg(base, SE_GENI_M_CMD0);
+	m_irq_status = geni_read_reg(base, SE_GENI_M_IRQ_STATUS);
+	geni_status = geni_read_reg(base, SE_GENI_STATUS);
+	geni_ios = geni_read_reg(base, SE_GENI_IOS);
+	dma_rx_irq = geni_read_reg(base, SE_DMA_TX_IRQ_STAT);
+	dma_tx_irq = geni_read_reg(base, SE_DMA_RX_IRQ_STAT);
+	rx_fifo_status = geni_read_reg(base, SE_GENI_RX_FIFO_STATUS);
+	tx_fifo_status = geni_read_reg(base, SE_GENI_TX_FIFO_STATUS);
+	se_dma_dbg = geni_read_reg(base, SE_DMA_DEBUG_REG0);
+	m_cmd_ctrl = geni_read_reg(base, SE_GENI_M_CMD_CTRL_REG);
+	se_dma_rx_len = geni_read_reg(base, SE_DMA_RX_LEN);
+	se_dma_rx_len_in = geni_read_reg(base, SE_DMA_RX_LEN_IN);
+	se_dma_tx_len = geni_read_reg(base, SE_DMA_TX_LEN);
+	se_dma_tx_len_in = geni_read_reg(base, SE_DMA_TX_LEN_IN);
+
+	GENI_SE_DBG(ipc, false, NULL,
+	"%s: m_cmd0:0x%x, m_irq_status:0x%x, geni_status:0x%x, geni_ios:0x%x\n",
+	__func__, m_cmd0, m_irq_status, geni_status, geni_ios);
+	GENI_SE_DBG(ipc, false, NULL,
+	"dma_rx_irq:0x%x, dma_tx_irq:0x%x, rx_fifo_sts:0x%x, tx_fifo_sts:0x%x\n"
+	, dma_rx_irq, dma_tx_irq, rx_fifo_status, tx_fifo_status);
+	GENI_SE_DBG(ipc, false, NULL,
+	"se_dma_dbg:0x%x, m_cmd_ctrl:0x%x, dma_rxlen:0x%x, dma_rxlen_in:0x%x\n",
+	se_dma_dbg, m_cmd_ctrl, se_dma_rx_len, se_dma_rx_len_in);
+	GENI_SE_DBG(ipc, false, NULL,
+	"dma_txlen:0x%x, dma_txlen_in:0x%x\n", se_dma_tx_len, se_dma_tx_len_in);
+}
+EXPORT_SYMBOL(geni_se_dump_dbg_regs);
+
 static const struct of_device_id geni_se_dt_match[] = {
 	{ .compatible = "qcom,qupv3-geni-se", },
 	{ .compatible = "qcom,qupv3-geni-se-cb", },
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 92321ad..93121df 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -102,7 +102,6 @@
 struct usb_bam_ctx_type {
 	struct usb_bam_sps_type		usb_bam_sps;
 	struct resource			*io_res;
-	void __iomem			*regs;
 	int				irq;
 	struct platform_device		*usb_bam_pdev;
 	struct workqueue_struct		*usb_bam_wq;
@@ -112,6 +111,7 @@
 	u32				inactivity_timer_ms;
 	bool				is_bam_inactivity;
 	struct usb_bam_pipe_connect	*usb_bam_connections;
+	struct msm_usb_bam_data *usb_bam_data;
 	spinlock_t		usb_bam_lock;
 };
 
@@ -125,13 +125,13 @@
  * CI_CTRL & DWC3_CTRL shouldn't be used simultaneously
  * since both share the same prod & cons rm resourses
  */
-static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_prod[MAX_BAMS] = {
 	[CI_CTRL] = IPA_RM_RESOURCE_USB_PROD,
 	[HSIC_CTRL]  = IPA_RM_RESOURCE_HSIC_PROD,
 	[DWC3_CTRL] = IPA_RM_RESOURCE_USB_PROD,
 };
 
-static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
+static enum ipa_rm_resource_name ipa_rm_resource_cons[MAX_BAMS] = {
 	[CI_CTRL] = IPA_RM_RESOURCE_USB_CONS,
 	[HSIC_CTRL]  = IPA_RM_RESOURCE_HSIC_CONS,
 	[DWC3_CTRL] = IPA_RM_RESOURCE_USB_CONS,
@@ -234,10 +234,10 @@
 	if (dev) {
 		/* Hold the device until allowing lpm */
 		info[HSIC_CTRL].in_lpm = false;
-		log_event_dbg("%s: Getting hsic device %p\n", __func__, dev);
+		log_event_dbg("%s: Getting hsic device %pK\n", __func__, dev);
 		pm_runtime_get(dev);
 	} else if (host_info[HSIC_CTRL].dev) {
-		log_event_dbg("%s: Try Putting hsic device %p, lpm:%d\n",
+		log_event_dbg("%s: Try Putting hsic device %pK, lpm:%d\n",
 				__func__, host_info[HSIC_CTRL].dev,
 				info[HSIC_CTRL].in_lpm);
 		/* Just release previous device if not already done */
@@ -319,8 +319,6 @@
 {
 	int ret = 0;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type];
-	struct msm_usb_bam_platform_data *pdata =
-				ctx->usb_bam_pdev->dev.platform_data;
 	struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf);
 	struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf);
 
@@ -359,7 +357,7 @@
 		}
 
 		data_buf->phys_base = pipe_connect->data_fifo_base_offset +
-						pdata->usb_bam_fifo_baseaddr;
+				ctx->usb_bam_data->usb_bam_fifo_baseaddr;
 		data_buf->size = pipe_connect->data_fifo_size;
 		data_buf->base = ioremap(data_buf->phys_base, data_buf->size);
 		if (!data_buf->base) {
@@ -371,7 +369,7 @@
 		memset_io(data_buf->base, 0, data_buf->size);
 
 		desc_buf->phys_base = pipe_connect->desc_fifo_base_offset +
-						pdata->usb_bam_fifo_baseaddr;
+				ctx->usb_bam_data->usb_bam_fifo_baseaddr;
 		desc_buf->size = pipe_connect->desc_fifo_size;
 		desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size);
 		if (!desc_buf->base) {
@@ -824,7 +822,7 @@
 
 	/* Exit from "full suspend" in case of hsic host */
 	if (host_info[HSIC_CTRL].dev && info[HSIC_CTRL].in_lpm) {
-		log_event_dbg("%s: Getting hsic device %p\n", __func__,
+		log_event_dbg("%s: Getting hsic device %pK\n", __func__,
 			host_info[HSIC_CTRL].dev);
 		pm_runtime_get(host_info[HSIC_CTRL].dev);
 		info[HSIC_CTRL].in_lpm = false;
@@ -838,7 +836,7 @@
 	log_event_dbg("%s: enter\n", __func__);
 
 	if (host_info[HSIC_CTRL].dev && !info[HSIC_CTRL].in_lpm) {
-		log_event_dbg("%s: Putting hsic host device %p\n", __func__,
+		log_event_dbg("%s: Putting hsic host device %pK\n", __func__,
 			host_info[HSIC_CTRL].dev);
 		pm_runtime_put(host_info[HSIC_CTRL].dev);
 		info[HSIC_CTRL].in_lpm = true;
@@ -1068,7 +1066,6 @@
 	struct usb_bam_pipe_connect *pipe_connect =
 				&ctx->usb_bam_connections[idx];
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
-	struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
 	enum usb_bam_mode cur_mode;
 
 	if (pipe_connect->enabled) {
@@ -1094,7 +1091,7 @@
 
 	spin_lock(&ctx->usb_bam_lock);
 	/* Check if BAM requires RESET before connect and reset of first pipe */
-	if ((pdata->reset_on_connect == true) &&
+	if ((ctx->usb_bam_data->reset_on_connect == true) &&
 			    (ctx->pipes_enabled_per_bam == 0)) {
 		spin_unlock(&ctx->usb_bam_lock);
 
@@ -1595,8 +1592,6 @@
 static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam)
 {
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
-	struct msm_usb_bam_platform_data *pdata =
-					ctx->usb_bam_pdev->dev.platform_data;
 	struct ipa_rm_create_params usb_prod_create_params;
 	struct ipa_rm_create_params usb_cons_create_params;
 	int ret;
@@ -1605,7 +1600,8 @@
 	memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params));
 	usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
 	usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb;
-	usb_prod_create_params.reg_params.user_data = &pdata->bam_type;
+	usb_prod_create_params.reg_params.user_data
+					 = &ctx->usb_bam_data->bam_type;
 	usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS;
 	ret = ipa_rm_create_resource(&usb_prod_create_params);
 	if (ret) {
@@ -1628,6 +1624,22 @@
 	}
 }
 
+static void usb_bam_ipa_delete_resources(enum usb_ctrl cur_bam)
+{
+	int ret;
+
+	ret = ipa_rm_delete_resource(ipa_rm_resource_prod[cur_bam]);
+	if (ret)
+		log_event_err("%s: Failed to delete USB_PROD resource\n",
+							__func__);
+
+	ret = ipa_rm_delete_resource(ipa_rm_resource_cons[cur_bam]);
+	if (ret)
+		log_event_err("%s: Failed to delete USB_CONS resource\n",
+							__func__);
+
+}
+
 static void wait_for_prod_granted(enum usb_ctrl cur_bam)
 {
 	int ret;
@@ -1723,7 +1735,7 @@
 	/* If we have any remaints in the pipes we don't go to sleep */
 	prod_pipe = ctx->usb_bam_sps.sps_pipes[src_idx];
 	cons_pipe = ctx->usb_bam_sps.sps_pipes[dst_idx];
-	log_event_dbg("prod_pipe=%p, cons_pipe=%p\n", prod_pipe, cons_pipe);
+	log_event_dbg("prod_pipe=%pK, cons_pipe=%pK\n", prod_pipe, cons_pipe);
 
 	if (!cons_pipe || (!prod_pipe &&
 			prod_pipe_connect->pipe_type == USB_BAM_PIPE_BAM2BAM)) {
@@ -2089,7 +2101,7 @@
 		}
 
 		/* HSIC host will go now to lpm */
-		log_event_dbg("%s: vote for suspend hsic %p\n",
+		log_event_dbg("%s: vote for suspend hsic %pK\n",
 			__func__, host_info[bam_type].dev);
 
 		for (i = 0; i < ctx->max_connections; i++) {
@@ -2134,16 +2146,14 @@
 	int ret;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
 	struct ipa_rm_perf_profile ipa_rm_perf_prof;
-	struct msm_usb_bam_platform_data *pdata =
-					ctx->usb_bam_pdev->dev.platform_data;
 
 	if (usb_connection_speed == USB_SPEED_SUPER)
 		ipa_rm_perf_prof.max_supported_bandwidth_mbps =
-			pdata->max_mbps_superspeed;
+			ctx->usb_bam_data->max_mbps_superspeed;
 	else
 		/* Bam2Bam is supported only for SS and HS (HW limitation) */
 		ipa_rm_perf_prof.max_supported_bandwidth_mbps =
-			pdata->max_mbps_highspeed;
+			ctx->usb_bam_data->max_mbps_highspeed;
 
 	/*
 	 * Having a max mbps property in dtsi file is a must
@@ -2180,7 +2190,6 @@
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam];
 	struct usb_bam_pipe_connect *pipe_connect;
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
-	struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data;
 	int ret;
 	bool bam2bam, is_dpl;
 
@@ -2256,7 +2265,8 @@
 
 	/* Check if BAM requires RESET before connect and reset first pipe */
 	spin_lock(&ctx->usb_bam_lock);
-	if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) {
+	if (ctx->usb_bam_data->reset_on_connect &&
+		!ctx->pipes_enabled_per_bam) {
 		spin_unlock(&ctx->usb_bam_lock);
 		if (cur_bam == CI_CTRL)
 			msm_hw_bam_disable(1);
@@ -2450,7 +2460,7 @@
 			if (pipe_iter->bam_type == pipe_connect->bam_type &&
 			    pipe_iter->dir == PEER_PERIPHERAL_TO_USB &&
 			    pipe_iter->enabled) {
-				log_event_dbg("%s: Register wakeup on pipe %p\n",
+				log_event_dbg("%s: Register wakeup on pipe %pK\n",
 					__func__, pipe_iter);
 				__usb_bam_register_wake_cb(
 					pipe_connect->bam_type, i,
@@ -2606,8 +2616,6 @@
 	struct usb_bam_pipe_connect *pipe_connect;
 	struct device *bam_dev = &ctx->usb_bam_pdev->dev;
 	int ret;
-	struct msm_usb_bam_platform_data *pdata =
-				ctx->usb_bam_pdev->dev.platform_data;
 
 	pipe_connect = &ctx->usb_bam_connections[idx];
 
@@ -2635,7 +2643,8 @@
 
 	log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx);
 
-	if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) {
+	if (ctx->usb_bam_data->reset_on_disconnect
+				&& !ctx->pipes_enabled_per_bam) {
 		if (bam_type == CI_CTRL)
 			msm_hw_bam_disable(1);
 
@@ -2803,10 +2812,10 @@
 	}
 }
 
-static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
+static struct msm_usb_bam_data *usb_bam_dt_to_data(
 	struct platform_device *pdev, u32 usb_addr)
 {
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 	struct device_node *node = pdev->dev.of_node;
 	int rc = 0;
 	u8 i = 0;
@@ -2815,8 +2824,9 @@
 	u32 threshold, max_connections = 0;
 	static struct usb_bam_pipe_connect *usb_bam_connections;
 
-	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-	if (!pdata)
+	usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data),
+								GFP_KERNEL);
+	if (!usb_bam_data)
 		return NULL;
 
 	rc = of_property_read_u32(node, "qcom,bam-type", &bam);
@@ -2830,7 +2840,7 @@
 			__func__, bam);
 		return NULL;
 	}
-	pdata->bam_type = bam;
+	usb_bam_data->bam_type = bam;
 
 	rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode);
 	if (rc) {
@@ -2840,49 +2850,49 @@
 		bam_mode = USB_BAM_DEVICE;
 	}
 
-	pdata->reset_on_connect = of_property_read_bool(node,
+	usb_bam_data->reset_on_connect = of_property_read_bool(node,
 					"qcom,reset-bam-on-connect");
 
-	pdata->reset_on_disconnect = of_property_read_bool(node,
+	usb_bam_data->reset_on_disconnect = of_property_read_bool(node,
 					"qcom,reset-bam-on-disconnect");
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes",
-		&pdata->usb_bam_num_pipes);
+		&usb_bam_data->usb_bam_num_pipes);
 	if (rc) {
 		log_event_err("Invalid usb bam num pipes property\n");
 		return NULL;
 	}
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed",
-		&pdata->max_mbps_highspeed);
+		&usb_bam_data->max_mbps_highspeed);
 	if (rc)
-		pdata->max_mbps_highspeed = 0;
+		usb_bam_data->max_mbps_highspeed = 0;
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed",
-		&pdata->max_mbps_superspeed);
+		&usb_bam_data->max_mbps_superspeed);
 	if (rc)
-		pdata->max_mbps_superspeed = 0;
+		usb_bam_data->max_mbps_superspeed = 0;
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr);
 	if (rc)
 		pr_debug("%s: Invalid usb base address property\n", __func__);
 	else
-		pdata->usb_bam_fifo_baseaddr = addr;
+		usb_bam_data->usb_bam_fifo_baseaddr = addr;
 
-	pdata->ignore_core_reset_ack = of_property_read_bool(node,
+	usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node,
 		"qcom,ignore-core-reset-ack");
 
-	pdata->disable_clk_gating = of_property_read_bool(node,
+	usb_bam_data->disable_clk_gating = of_property_read_bool(node,
 		"qcom,disable-clk-gating");
 
 	rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold",
 			&threshold);
 	if (rc)
-		pdata->override_threshold = USB_THRESHOLD;
+		usb_bam_data->override_threshold = USB_THRESHOLD;
 	else
-		pdata->override_threshold = threshold;
+		usb_bam_data->override_threshold = threshold;
 
-	pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node,
+	usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node,
 		"qcom,enable-hsusb-bam-on-boot");
 
 	for_each_child_of_node(pdev->dev.of_node, node)
@@ -2918,7 +2928,7 @@
 			goto err;
 
 		if (usb_bam_connections[i].mem_type == OCI_MEM) {
-			if (!pdata->usb_bam_fifo_baseaddr) {
+			if (!usb_bam_data->usb_bam_fifo_baseaddr) {
 				log_event_err("%s: base address is missing\n",
 					__func__);
 				goto err;
@@ -3002,7 +3012,7 @@
 	msm_usb_bam[bam].usb_bam_connections = usb_bam_connections;
 	msm_usb_bam[bam].max_connections = max_connections;
 
-	return pdata;
+	return usb_bam_data;
 err:
 	log_event_err("%s: failed\n", __func__);
 	return NULL;
@@ -3011,9 +3021,8 @@
 static int usb_bam_init(struct platform_device *pdev)
 {
 	int ret;
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+	enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
 	struct sps_bam_props props;
 
 	memset(&props, 0, sizeof(props));
@@ -3022,23 +3031,22 @@
 		bam_enable_strings[bam_type]);
 
 	props.phys_addr = ctx->io_res->start;
-	props.virt_addr = ctx->regs;
 	props.virt_size = resource_size(ctx->io_res);
 	props.irq = ctx->irq;
-	props.summing_threshold = pdata->override_threshold;
-	props.event_threshold = pdata->override_threshold;
-	props.num_pipes = pdata->usb_bam_num_pipes;
+	props.summing_threshold = ctx->usb_bam_data->override_threshold;
+	props.event_threshold = ctx->usb_bam_data->override_threshold;
+	props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes;
 	props.callback = usb_bam_sps_events;
 	props.user = bam_enable_strings[bam_type];
 
 	/*
-	 * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs.
-	 * Hence let BAM to ignore acknowledge from USB while resetting PIPE.
-	 */
-	if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL)
+	* HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
+	* Hence, let BAM to ignore acknowledge from USB while resetting PIPE
+	*/
+	if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL)
 		props.options = SPS_BAM_NO_EXT_P_RST;
 
-	if (pdata->disable_clk_gating)
+	if (ctx->usb_bam_data->disable_clk_gating)
 		props.options |= SPS_BAM_NO_LOCAL_CLK_GATING;
 
 	/*
@@ -3046,7 +3054,8 @@
 	 * starting peripheral controller to avoid switching USB core mode
 	 * from legacy to BAM with ongoing data transfers.
 	 */
-	if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) {
+	if (ctx->usb_bam_data->enable_hsusb_bam_on_boot
+					&& bam_type == CI_CTRL) {
 		pr_debug("Register and enable HSUSB BAM\n");
 		props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT;
 	}
@@ -3063,9 +3072,8 @@
 static int enable_usb_bam(struct platform_device *pdev)
 {
 	int ret;
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
+	enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type;
 
 	ret = usb_bam_init(pdev);
 	if (ret) {
@@ -3132,14 +3140,19 @@
 			&usb_bam_panic_blk);
 }
 
+static void usb_bam_unregister_panic_hdlr(void)
+{
+	atomic_notifier_chain_unregister(&panic_notifier_list,
+			&usb_bam_panic_blk);
+}
+
 static int usb_bam_probe(struct platform_device *pdev)
 {
 	int ret, i, irq;
 	struct resource *io_res;
-	void __iomem *regs;
 	enum usb_ctrl bam_type;
 	struct usb_bam_ctx_type *ctx;
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 
 	dev_dbg(&pdev->dev, "usb_bam_probe\n");
 
@@ -3155,25 +3168,19 @@
 		return irq;
 	}
 
-	regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res));
-	if (!regs) {
-		log_event_err("%s: ioremap failed\n", __func__);
-		return -ENOMEM;
-	}
-
 	/* specify BAM physical address to be filled in BAM connections */
-	pdata = usb_bam_dt_to_pdata(pdev, io_res->start);
-	if (!pdata)
+	usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start);
+	if (!usb_bam_data)
 		return -EINVAL;
 
-	pdev->dev.platform_data = pdata;
-	bam_type = pdata->bam_type;
+	bam_type = usb_bam_data->bam_type;
 	ctx = &msm_usb_bam[bam_type];
+	dev_set_drvdata(&pdev->dev, ctx);
 
 	ctx->usb_bam_pdev = pdev;
 	ctx->irq = irq;
-	ctx->regs = regs;
 	ctx->io_res = io_res;
+	ctx->usb_bam_data = usb_bam_data;
 
 	for (i = 0; i < ctx->max_connections; i++) {
 		ctx->usb_bam_connections[i].enabled = 0;
@@ -3314,15 +3321,15 @@
 
 bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable)
 {
-	struct msm_usb_bam_platform_data *pdata;
+	struct msm_usb_bam_data *usb_bam_data;
 	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam];
 
 	if (!ctx->usb_bam_pdev)
 		return 0;
 
-	pdata = ctx->usb_bam_pdev->dev.platform_data;
+	usb_bam_data = ctx->usb_bam_data;
 	if ((bam != CI_CTRL) || !(bam_enable ||
-					pdata->enable_hsusb_bam_on_boot))
+					usb_bam_data->enable_hsusb_bam_on_boot))
 		return 0;
 
 	msm_hw_bam_disable(1);
@@ -3404,10 +3411,11 @@
 
 static int usb_bam_remove(struct platform_device *pdev)
 {
-	struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data;
-	enum usb_ctrl bam_type = pdata->bam_type;
-	struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type];
+	struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev);
 
+	usb_bam_ipa_delete_resources(ctx->usb_bam_data->bam_type);
+	usb_bam_unregister_panic_hdlr();
+	sps_deregister_bam_device(ctx->h_bam);
 	destroy_workqueue(ctx->usb_bam_wq);
 
 	return 0;
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 96ffda4..454cb2e 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -248,7 +248,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
 				       sizeof(state), sizeof(state));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return state;
 }
 
@@ -258,7 +258,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
 				       sizeof(state), sizeof(state));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return state;
 }
 
@@ -268,7 +268,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
 				       sizeof(state), sizeof(state));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return state;
 }
 
@@ -279,7 +279,7 @@
 				       sizeof(state), sizeof(state));
 
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 
 	return state & 0x1;
 }
@@ -290,7 +290,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
 				       sizeof(state), sizeof(state));
 	if (ret)
-		return ret;
+		return ret < 0 ? ret : -EINVAL;
 
 	return (state & 0x4) ? 1 : 0;
 }
@@ -323,7 +323,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &value,
 				       sizeof(value), 0);
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return 0;
 }
 
@@ -336,7 +336,7 @@
 	ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
 				   &query, sizeof(query), 0);
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return 0;
 }
 
@@ -428,7 +428,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 0, &state,
 				       sizeof(state), sizeof(state));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 	return state;
 }
 
@@ -494,7 +494,7 @@
 	int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
 				       sizeof(tmp), sizeof(tmp));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 
 	return count;
 }
@@ -515,7 +515,7 @@
 	ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 1, &tmp,
 				       sizeof(tmp), sizeof(tmp));
 	if (ret)
-		return -EINVAL;
+		return ret < 0 ? ret : -EINVAL;
 
 	return count;
 }
@@ -572,10 +572,12 @@
 
 	switch (event_id) {
 	case HPWMI_DOCK_EVENT:
-		input_report_switch(hp_wmi_input_dev, SW_DOCK,
-				    hp_wmi_dock_state());
-		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
-				    hp_wmi_tablet_state());
+		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
+			input_report_switch(hp_wmi_input_dev, SW_DOCK,
+					    hp_wmi_dock_state());
+		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
+			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+					    hp_wmi_tablet_state());
 		input_sync(hp_wmi_input_dev);
 		break;
 	case HPWMI_PARK_HDD:
@@ -644,6 +646,7 @@
 {
 	acpi_status status;
 	int err;
+	int val;
 
 	hp_wmi_input_dev = input_allocate_device();
 	if (!hp_wmi_input_dev)
@@ -654,17 +657,26 @@
 	hp_wmi_input_dev->id.bustype = BUS_HOST;
 
 	__set_bit(EV_SW, hp_wmi_input_dev->evbit);
-	__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
-	__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+
+	/* Dock */
+	val = hp_wmi_dock_state();
+	if (!(val < 0)) {
+		__set_bit(SW_DOCK, hp_wmi_input_dev->swbit);
+		input_report_switch(hp_wmi_input_dev, SW_DOCK, val);
+	}
+
+	/* Tablet mode */
+	val = hp_wmi_tablet_state();
+	if (!(val < 0)) {
+		__set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit);
+		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, val);
+	}
 
 	err = sparse_keymap_setup(hp_wmi_input_dev, hp_wmi_keymap, NULL);
 	if (err)
 		goto err_free_dev;
 
 	/* Set initial hardware state */
-	input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state());
-	input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
-			    hp_wmi_tablet_state());
 	input_sync(hp_wmi_input_dev);
 
 	if (!hp_wmi_bios_2009_later() && hp_wmi_bios_2008_later())
@@ -950,10 +962,12 @@
 	 * changed.
 	 */
 	if (hp_wmi_input_dev) {
-		input_report_switch(hp_wmi_input_dev, SW_DOCK,
-				    hp_wmi_dock_state());
-		input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
-				    hp_wmi_tablet_state());
+		if (test_bit(SW_DOCK, hp_wmi_input_dev->swbit))
+			input_report_switch(hp_wmi_input_dev, SW_DOCK,
+					    hp_wmi_dock_state());
+		if (test_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit))
+			input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE,
+					    hp_wmi_tablet_state());
 		input_sync(hp_wmi_input_dev);
 	}
 
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 9f713b8..5c768c4 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -550,6 +550,7 @@
 	{ "msic_thermal", 1 },
 	{ }
 };
+MODULE_DEVICE_TABLE(platform, therm_id_table);
 
 static struct platform_driver mid_thermal_driver = {
 	.driver = {
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 9f04957..c090b2a 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -37,6 +37,7 @@
 #define EMERGENCY_DLOAD_MAGIC1    0x322A4F99
 #define EMERGENCY_DLOAD_MAGIC2    0xC67E4350
 #define EMERGENCY_DLOAD_MAGIC3    0x77777777
+#define EMMC_DLOAD_TYPE		0x2
 
 #define SCM_IO_DISABLE_PMIC_ARBITER	1
 #define SCM_IO_DEASSERT_PS_HOLD		2
@@ -47,13 +48,14 @@
 
 
 static int restart_mode;
-void *restart_reason;
+static void __iomem *restart_reason, *dload_type_addr;
 static bool scm_pmic_arbiter_disable_supported;
 static bool scm_deassert_ps_hold_supported;
 /* Download mode master kill-switch */
 static void __iomem *msm_ps_hold;
 static phys_addr_t tcsr_boot_misc_detect;
 static int download_mode = 1;
+static struct kobject dload_kobj;
 
 #ifdef CONFIG_QCOM_DLOAD_MODE
 #define EDL_MODE_PROP "qcom,msm-imem-emergency_download_mode"
@@ -72,8 +74,23 @@
 static bool scm_dload_supported;
 
 static int dload_set(const char *val, struct kernel_param *kp);
+/* interface for exporting attributes */
+struct reset_attribute {
+	struct attribute        attr;
+	ssize_t (*show)(struct kobject *kobj, struct attribute *attr,
+			char *buf);
+	size_t (*store)(struct kobject *kobj, struct attribute *attr,
+			const char *buf, size_t count);
+};
+#define to_reset_attr(_attr) \
+	container_of(_attr, struct reset_attribute, attr)
+#define RESET_ATTR(_name, _mode, _show, _store)	\
+	static struct reset_attribute reset_attr_##_name = \
+			__ATTR(_name, _mode, _show, _store)
+
 module_param_call(download_mode, dload_set, param_get_int,
 			&download_mode, 0644);
+
 static int panic_prep_restart(struct notifier_block *this,
 			      unsigned long event, void *ptr)
 {
@@ -85,7 +102,7 @@
 	.notifier_call	= panic_prep_restart,
 };
 
-int scm_set_dload_mode(int arg1, int arg2)
+static int scm_set_dload_mode(int arg1, int arg2)
 {
 	struct scm_desc desc = {
 		.args[0] = arg1,
@@ -397,6 +414,84 @@
 	pr_err("Powering off has failed\n");
 }
 
+static ssize_t attr_show(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	struct reset_attribute *reset_attr = to_reset_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (reset_attr->show)
+		ret = reset_attr->show(kobj, attr, buf);
+
+	return ret;
+}
+
+static ssize_t attr_store(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t count)
+{
+	struct reset_attribute *reset_attr = to_reset_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (reset_attr->store)
+		ret = reset_attr->store(kobj, attr, buf, count);
+
+	return ret;
+}
+
+static const struct sysfs_ops reset_sysfs_ops = {
+	.show	= attr_show,
+	.store	= attr_store,
+};
+
+static struct kobj_type reset_ktype = {
+	.sysfs_ops	= &reset_sysfs_ops,
+};
+
+static ssize_t show_emmc_dload(struct kobject *kobj, struct attribute *attr,
+				char *buf)
+{
+	uint32_t read_val, show_val;
+
+	read_val = __raw_readl(dload_type_addr);
+	if (read_val == EMMC_DLOAD_TYPE)
+		show_val = 1;
+	else
+		show_val = 0;
+
+	return snprintf(buf, sizeof(show_val), "%u\n", show_val);
+}
+
+static size_t store_emmc_dload(struct kobject *kobj, struct attribute *attr,
+				const char *buf, size_t count)
+{
+	uint32_t enabled;
+	int ret;
+
+	ret = kstrtouint(buf, 0, &enabled);
+	if (ret < 0)
+		return ret;
+
+	if (!((enabled == 0) || (enabled == 1)))
+		return -EINVAL;
+
+	if (enabled == 1)
+		__raw_writel(EMMC_DLOAD_TYPE, dload_type_addr);
+	else
+		__raw_writel(0, dload_type_addr);
+
+	return count;
+}
+RESET_ATTR(emmc_dload, 0644, show_emmc_dload, store_emmc_dload);
+
+static struct attribute *reset_attrs[] = {
+	&reset_attr_emmc_dload.attr,
+	NULL
+};
+
+static struct attribute_group reset_attr_group = {
+	.attrs = reset_attrs,
+};
+
 static int msm_restart_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -438,7 +533,7 @@
 			pr_err("unable to map imem KASLR offset\n");
 	}
 
-	if (kaslr_imem_addr && scm_is_secure_device()) {
+	if (kaslr_imem_addr) {
 		__raw_writel(0xdead4ead, kaslr_imem_addr);
 		__raw_writel(KASLR_OFFSET_BIT_MASK &
 		(kimage_vaddr - KIMAGE_VADDR), kaslr_imem_addr + 4);
@@ -448,6 +543,33 @@
 		iounmap(kaslr_imem_addr);
 	}
 #endif
+	np = of_find_compatible_node(NULL, NULL,
+				"qcom,msm-imem-dload-type");
+	if (!np) {
+		pr_err("unable to find DT imem dload-type node\n");
+		goto skip_sysfs_create;
+	} else {
+		dload_type_addr = of_iomap(np, 0);
+		if (!dload_type_addr) {
+			pr_err("unable to map imem dload-type offset\n");
+			goto skip_sysfs_create;
+		}
+	}
+
+	ret = kobject_init_and_add(&dload_kobj, &reset_ktype,
+			kernel_kobj, "%s", "dload");
+	if (ret) {
+		pr_err("%s:Error in creation kobject_add\n", __func__);
+		kobject_put(&dload_kobj);
+		goto skip_sysfs_create;
+	}
+
+	ret = sysfs_create_group(&dload_kobj, &reset_attr_group);
+	if (ret) {
+		pr_err("%s:Error in creation sysfs_create_group\n", __func__);
+		kobject_del(&dload_kobj);
+	}
+skip_sysfs_create:
 #endif
 	np = of_find_compatible_node(NULL, NULL,
 				"qcom,msm-imem-restart_reason");
@@ -481,7 +603,6 @@
 	if (scm_is_call_available(SCM_SVC_PWR, SCM_IO_DEASSERT_PS_HOLD) > 0)
 		scm_deassert_ps_hold_supported = true;
 
-	download_mode = scm_is_secure_device();
 	set_dload_mode(download_mode);
 
 	return 0;
diff --git a/drivers/power/supply/axp288_fuel_gauge.c b/drivers/power/supply/axp288_fuel_gauge.c
index 5bdde69..f62f9df 100644
--- a/drivers/power/supply/axp288_fuel_gauge.c
+++ b/drivers/power/supply/axp288_fuel_gauge.c
@@ -169,8 +169,10 @@
 			break;
 	}
 
-	if (ret < 0)
+	if (ret < 0) {
 		dev_err(&info->pdev->dev, "axp288 reg read err:%d\n", ret);
+		return ret;
+	}
 
 	return val;
 }
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index a013edd..785cf23 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -261,6 +261,8 @@
 	POWER_SUPPLY_ATTR(low_power),
 	POWER_SUPPLY_ATTR(temp_cool),
 	POWER_SUPPLY_ATTR(temp_warm),
+	POWER_SUPPLY_ATTR(temp_cold),
+	POWER_SUPPLY_ATTR(temp_hot),
 	POWER_SUPPLY_ATTR(system_temp_level),
 	POWER_SUPPLY_ATTR(resistance),
 	POWER_SUPPLY_ATTR(resistance_capacitive),
@@ -317,6 +319,7 @@
 	POWER_SUPPLY_ATTR(pd_voltage_max),
 	POWER_SUPPLY_ATTR(pd_voltage_min),
 	POWER_SUPPLY_ATTR(sdp_current_max),
+	POWER_SUPPLY_ATTR(connector_type),
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_ATTR(charge_counter_ext),
 	/* Properties of type `const char *' */
diff --git a/drivers/power/supply/qcom/Kconfig b/drivers/power/supply/qcom/Kconfig
index 362375f..25ad740 100644
--- a/drivers/power/supply/qcom/Kconfig
+++ b/drivers/power/supply/qcom/Kconfig
@@ -1,5 +1,14 @@
 menu "Qualcomm Technologies Inc Charger and Fuel Gauge support"
 
+config QPNP_FG
+	tristate "QPNP fuel gauge driver"
+	depends on MFD_SPMI_PMIC
+	help
+	  Say Y here to enable the Fuel Gauge driver. This adds support for
+	  battery fuel gauging and state of charge of battery connected to the
+	  fuel gauge. The state of charge is reported through a BMS power
+	  supply property and also sends uevents when the capacity is updated.
+
 config QPNP_FG_GEN3
 	tristate "QPNP GEN3 fuel gauge driver"
 	depends on MFD_SPMI_PMIC
@@ -64,6 +73,15 @@
 	  The driver reports the charger status via the power supply framework.
 	  A charger status change triggers an IRQ via the device STAT pin.
 
+config QPNP_SMBCHARGER
+	tristate "QPNP SMB Charger driver"
+	depends on MFD_SPMI_PMIC
+	help
+	  Say Y here to enable the dual path switch mode battery charger which
+	  supports USB detection and battery charging up to 3A.
+	  The driver also offers relevant information to userspace via the
+	  power supply framework.
+
 config QPNP_QNOVO
 	bool "QPNP QNOVO driver"
 	depends on MFD_SPMI_PMIC
diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile
index 6cc83ab..21f63ee 100644
--- a/drivers/power/supply/qcom/Makefile
+++ b/drivers/power/supply/qcom/Makefile
@@ -1,7 +1,9 @@
+obj-$(CONFIG_QPNP_FG)		+= qpnp-fg.o
 obj-$(CONFIG_QPNP_FG_GEN3)     += qpnp-fg-gen3.o fg-memif.o fg-util.o
+obj-$(CONFIG_QPNP_SMBCHARGER)	+= qpnp-smbcharger.o pmic-voter.o
 obj-$(CONFIG_SMB135X_CHARGER)   += smb135x-charger.o pmic-voter.o
 obj-$(CONFIG_SMB1355_SLAVE_CHARGER)   += smb1355-charger.o pmic-voter.o
 obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o
 obj-$(CONFIG_QPNP_SMB2)		+= step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
-obj-$(CONFIG_SMB138X_CHARGER)	+= smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
+obj-$(CONFIG_SMB138X_CHARGER)	+= step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
 obj-$(CONFIG_QPNP_QNOVO)	+= qpnp-qnovo.o battery.o
diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c
index 7f6d346..4b900e2 100644
--- a/drivers/power/supply/qcom/battery.c
+++ b/drivers/power/supply/qcom/battery.c
@@ -26,12 +26,14 @@
 #include <linux/pm_wakeup.h>
 #include <linux/slab.h>
 #include <linux/pmic-voter.h>
+#include <linux/workqueue.h>
 #include "battery.h"
 
 #define DRV_MAJOR_VERSION	1
 #define DRV_MINOR_VERSION	0
 
 #define CHG_STATE_VOTER			"CHG_STATE_VOTER"
+#define TAPER_STEPPER_VOTER		"TAPER_STEPPER_VOTER"
 #define TAPER_END_VOTER			"TAPER_END_VOTER"
 #define PL_TAPER_EARLY_BAD_VOTER	"PL_TAPER_EARLY_BAD_VOTER"
 #define PARALLEL_PSY_VOTER		"PARALLEL_PSY_VOTER"
@@ -41,12 +43,11 @@
 #define ICL_CHANGE_VOTER		"ICL_CHANGE_VOTER"
 #define PL_INDIRECT_VOTER		"PL_INDIRECT_VOTER"
 #define USBIN_I_VOTER			"USBIN_I_VOTER"
-#define FCC_CHANGE_VOTER		"FCC_CHANGE_VOTER"
+#define PL_FCC_LOW_VOTER		"PL_FCC_LOW_VOTER"
 
 struct pl_data {
 	int			pl_mode;
 	int			slave_pct;
-	int			taper_pct;
 	int			slave_fcc_ua;
 	int			restricted_current;
 	bool			restricted_charging_enabled;
@@ -59,7 +60,9 @@
 	struct votable		*pl_enable_votable_indirect;
 	struct delayed_work	status_change_work;
 	struct work_struct	pl_disable_forever_work;
-	struct delayed_work	pl_taper_work;
+	struct work_struct	pl_taper_work;
+	struct delayed_work	pl_awake_work;
+	bool			taper_work_running;
 	struct power_supply	*main_psy;
 	struct power_supply	*pl_psy;
 	struct power_supply	*batt_psy;
@@ -342,67 +345,75 @@
 		*master_ua = max(0, total_ua);
 	else
 		*master_ua = max(0, total_ua - *slave_ua);
-
-	/* further reduce slave's share in accordance with taper reductions */
-	*slave_ua = (*slave_ua * chip->taper_pct) / 100;
 }
 
 #define MINIMUM_PARALLEL_FCC_UA		500000
-#define PL_TAPER_WORK_DELAY_MS		100
+#define PL_TAPER_WORK_DELAY_MS		500
 #define TAPER_RESIDUAL_PCT		90
+#define TAPER_REDUCTION_UA		200000
 static void pl_taper_work(struct work_struct *work)
 {
 	struct pl_data *chip = container_of(work, struct pl_data,
-						pl_taper_work.work);
+						pl_taper_work);
 	union power_supply_propval pval = {0, };
-	int total_fcc_ua, master_fcc_ua, slave_fcc_ua;
 	int rc;
+	int eff_fcc_ua;
+	int total_fcc_ua, master_fcc_ua, slave_fcc_ua = 0;
 
-	/* exit immediately if parallel is disabled */
-	if (get_effective_result(chip->pl_disable_votable)) {
-		pl_dbg(chip, PR_PARALLEL, "terminating parallel not in progress\n");
-		goto done;
+	chip->taper_work_running = true;
+	while (true) {
+		if (get_effective_result(chip->pl_disable_votable)) {
+			/*
+			 * if parallel's FCC share is low, simply disable
+			 * parallel with TAPER_END_VOTER
+			 */
+			total_fcc_ua = get_effective_result_locked(
+					chip->fcc_votable);
+			get_fcc_split(chip, total_fcc_ua, &master_fcc_ua,
+					&slave_fcc_ua);
+			if (slave_fcc_ua <= MINIMUM_PARALLEL_FCC_UA) {
+				pl_dbg(chip, PR_PARALLEL, "terminating: parallel's share is low\n");
+				vote(chip->pl_disable_votable, TAPER_END_VOTER,
+						true, 0);
+			} else {
+				pl_dbg(chip, PR_PARALLEL, "terminating: parallel disabled\n");
+			}
+			goto done;
+		}
+
+		rc = power_supply_get_property(chip->batt_psy,
+				       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
+		if (rc < 0) {
+			pr_err("Couldn't get batt charge type rc=%d\n", rc);
+			goto done;
+		}
+
+		chip->charge_type = pval.intval;
+		if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
+			eff_fcc_ua = get_effective_result(chip->fcc_votable);
+			if (eff_fcc_ua < 0) {
+				pr_err("Couldn't get fcc, exiting taper work\n");
+				goto done;
+			}
+			eff_fcc_ua = eff_fcc_ua - TAPER_REDUCTION_UA;
+			if (eff_fcc_ua < 0) {
+				pr_err("Can't reduce FCC any more\n");
+				goto done;
+			}
+
+			pl_dbg(chip, PR_PARALLEL, "master is taper charging; reducing FCC to %dua\n",
+					eff_fcc_ua);
+			vote(chip->fcc_votable, TAPER_STEPPER_VOTER,
+					true, eff_fcc_ua);
+		} else {
+			pl_dbg(chip, PR_PARALLEL, "master is fast charging; waiting for next taper\n");
+		}
+		/* wait for the charger state to deglitch after FCC change */
+		msleep(PL_TAPER_WORK_DELAY_MS);
 	}
-
-	total_fcc_ua = get_effective_result_locked(chip->fcc_votable);
-	get_fcc_split(chip, total_fcc_ua, &master_fcc_ua, &slave_fcc_ua);
-	if (slave_fcc_ua < MINIMUM_PARALLEL_FCC_UA) {
-		pl_dbg(chip, PR_PARALLEL, "terminating parallel's share lower than 500mA\n");
-		vote(chip->pl_disable_votable, TAPER_END_VOTER, true, 0);
-		goto done;
-	}
-
-	pl_dbg(chip, PR_PARALLEL, "entering parallel taper work slave_fcc = %d\n",
-		slave_fcc_ua);
-
-	rc = power_supply_get_property(chip->batt_psy,
-			       POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
-	if (rc < 0) {
-		pr_err("Couldn't get batt charge type rc=%d\n", rc);
-		goto done;
-	}
-
-	chip->charge_type = pval.intval;
-	if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
-		pl_dbg(chip, PR_PARALLEL, "master is taper charging; reducing slave FCC\n");
-
-		vote(chip->pl_awake_votable, TAPER_END_VOTER, true, 0);
-		/* Reduce the taper percent by 10 percent */
-		chip->taper_pct = chip->taper_pct * TAPER_RESIDUAL_PCT / 100;
-		rerun_election(chip->fcc_votable);
-		pl_dbg(chip, PR_PARALLEL, "taper entry scheduling work after %d ms\n",
-				PL_TAPER_WORK_DELAY_MS);
-		schedule_delayed_work(&chip->pl_taper_work,
-				msecs_to_jiffies(PL_TAPER_WORK_DELAY_MS));
-		return;
-	}
-
-	/*
-	 * Master back to Fast Charge, get out of this round of taper reduction
-	 */
-	pl_dbg(chip, PR_PARALLEL, "master is fast charging; waiting for next taper\n");
-
 done:
+	chip->taper_work_running = false;
+	vote(chip->fcc_votable, TAPER_STEPPER_VOTER, false, 0);
 	vote(chip->pl_awake_votable, TAPER_END_VOTER, false, 0);
 }
 
@@ -422,24 +433,19 @@
 		get_fcc_split(chip, total_fcc_ua, &master_fcc_ua,
 				&slave_fcc_ua);
 
-		if (slave_fcc_ua > 500000) {
+		if (slave_fcc_ua > MINIMUM_PARALLEL_FCC_UA) {
 			chip->slave_fcc_ua = slave_fcc_ua;
-			vote(chip->pl_disable_votable, FCC_CHANGE_VOTER,
+			vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER,
 							false, 0);
 		} else {
 			chip->slave_fcc_ua = 0;
-			vote(chip->pl_disable_votable, FCC_CHANGE_VOTER,
+			vote(chip->pl_disable_votable, PL_FCC_LOW_VOTER,
 							true, 0);
 		}
 	}
 
 	rerun_election(chip->pl_disable_votable);
 
-	pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
-		   master_fcc_ua, slave_fcc_ua,
-		   (master_fcc_ua * 100) / total_fcc_ua,
-		   (slave_fcc_ua * 100) / total_fcc_ua);
-
 	return 0;
 }
 
@@ -563,6 +569,14 @@
 		vote(chip->hvdcp_hw_inov_dis_votable, PL_VOTER, false, 0);
 }
 
+static void pl_awake_work(struct work_struct *work)
+{
+	struct pl_data *chip = container_of(work,
+			struct pl_data, pl_awake_work.work);
+
+	vote(chip->pl_awake_votable, PL_VOTER, false, 0);
+}
+
 static bool is_main_available(struct pl_data *chip)
 {
 	if (chip->main_psy)
@@ -590,6 +604,10 @@
 	total_fcc_ua = get_effective_result_locked(chip->fcc_votable);
 
 	if (chip->pl_mode != POWER_SUPPLY_PL_NONE && !pl_disable) {
+		/* keep system awake to talk to slave charger through i2c */
+		cancel_delayed_work_sync(&chip->pl_awake_work);
+		vote(chip->pl_awake_votable, PL_VOTER, true, 0);
+
 		 /* enable parallel charging */
 		rc = power_supply_get_property(chip->pl_psy,
 				POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
@@ -652,12 +670,21 @@
 		if (rc < 0) {
 			pr_err("Couldn't get batt charge type rc=%d\n", rc);
 		} else {
-			if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
+			if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER
+				&& !chip->taper_work_running) {
 				pl_dbg(chip, PR_PARALLEL,
 					"pl enabled in Taper scheduing work\n");
-				schedule_delayed_work(&chip->pl_taper_work, 0);
+				vote(chip->pl_awake_votable, TAPER_END_VOTER,
+						true, 0);
+				queue_work(system_long_wq,
+						&chip->pl_taper_work);
 			}
 		}
+
+		pl_dbg(chip, PR_PARALLEL, "master_fcc=%d slave_fcc=%d distribution=(%d/%d)\n",
+			master_fcc_ua, slave_fcc_ua,
+			(master_fcc_ua * 100) / total_fcc_ua,
+			(slave_fcc_ua * 100) / total_fcc_ua);
 	} else {
 		if ((chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN)
 			|| (chip->pl_mode == POWER_SUPPLY_PL_USBIN_USBIN_EXT))
@@ -684,6 +711,10 @@
 		}
 
 		rerun_election(chip->fv_votable);
+
+		cancel_delayed_work_sync(&chip->pl_awake_work);
+		schedule_delayed_work(&chip->pl_awake_work,
+						msecs_to_jiffies(5000));
 	}
 
 	pl_dbg(chip, PR_PARALLEL, "parallel charging %s\n",
@@ -788,7 +819,6 @@
 	if ((pval.intval != POWER_SUPPLY_CHARGE_TYPE_FAST)
 		&& (pval.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER)) {
 		vote(chip->pl_disable_votable, CHG_STATE_VOTER, true, 0);
-		chip->taper_pct = 100;
 		vote(chip->pl_disable_votable, TAPER_END_VOTER, false, 0);
 		vote(chip->pl_disable_votable, PL_TAPER_EARLY_BAD_VOTER,
 				false, 0);
@@ -800,8 +830,11 @@
 	if (chip->charge_type == POWER_SUPPLY_CHARGE_TYPE_FAST
 		&& (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER)) {
 		chip->charge_type = pval.intval;
-		pl_dbg(chip, PR_PARALLEL, "taper entry scheduling work\n");
-		schedule_delayed_work(&chip->pl_taper_work, 0);
+		if (!chip->taper_work_running) {
+			pl_dbg(chip, PR_PARALLEL, "taper entry scheduling work\n");
+			vote(chip->pl_awake_votable, TAPER_END_VOTER, true, 0);
+			queue_work(system_long_wq, &chip->pl_taper_work);
+		}
 		return;
 	}
 
@@ -1010,7 +1043,7 @@
 		goto release_wakeup_source;
 	}
 
-	chip->fv_votable = create_votable("FV", VOTE_MAX,
+	chip->fv_votable = create_votable("FV", VOTE_MIN,
 					pl_fv_vote_callback,
 					chip);
 	if (IS_ERR(chip->fv_votable)) {
@@ -1057,8 +1090,9 @@
 	vote(chip->pl_disable_votable, PL_INDIRECT_VOTER, true, 0);
 
 	INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
-	INIT_DELAYED_WORK(&chip->pl_taper_work, pl_taper_work);
+	INIT_WORK(&chip->pl_taper_work, pl_taper_work);
 	INIT_WORK(&chip->pl_disable_forever_work, pl_disable_forever_work);
+	INIT_DELAYED_WORK(&chip->pl_awake_work, pl_awake_work);
 
 	rc = pl_register_notifier(chip);
 	if (rc < 0) {
@@ -1082,8 +1116,6 @@
 		goto unreg_notifier;
 	}
 
-	chip->taper_pct = 100;
-
 	the_chip = chip;
 
 	return 0;
@@ -1112,8 +1144,9 @@
 		return;
 
 	cancel_delayed_work_sync(&chip->status_change_work);
-	cancel_delayed_work_sync(&chip->pl_taper_work);
+	cancel_work_sync(&chip->pl_taper_work);
 	cancel_work_sync(&chip->pl_disable_forever_work);
+	cancel_delayed_work_sync(&chip->pl_awake_work);
 
 	power_supply_unreg_notifier(&chip->nb);
 	destroy_votable(chip->pl_enable_votable_indirect);
diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index c793ee5..99120f4 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -57,6 +57,8 @@
 /* Battery missing irq votable reasons */
 #define BATT_MISS_IRQ_VOTER	"fg_batt_miss_irq"
 
+#define ESR_FCC_VOTER		"fg_esr_fcc"
+
 #define DEBUG_PRINT_BUFFER_SIZE		64
 /* 3 byte address + 1 space character */
 #define ADDR_LEN			4
@@ -105,7 +107,7 @@
 };
 
 /* JEITA */
-enum {
+enum jeita_levels {
 	JEITA_COLD = 0,
 	JEITA_COOL,
 	JEITA_WARM,
@@ -252,6 +254,7 @@
 struct fg_dt_props {
 	bool	force_load_profile;
 	bool	hold_soc_while_full;
+	bool	linearize_soc;
 	bool	auto_recharge_soc;
 	int	cutoff_volt_mv;
 	int	empty_volt_mv;
@@ -286,6 +289,7 @@
 	int	esr_pulse_thresh_ma;
 	int	esr_meas_curr_ma;
 	int	bmd_en_delay_ms;
+	int	ki_coeff_full_soc_dischg;
 	int	jeita_thresholds[NUM_JEITA_LEVELS];
 	int	ki_coeff_soc[KI_COEFF_SOC_LEVELS];
 	int	ki_coeff_med_dischg[KI_COEFF_SOC_LEVELS];
@@ -401,6 +405,7 @@
 	struct votable		*awake_votable;
 	struct votable		*delta_bsoc_irq_en_votable;
 	struct votable		*batt_miss_irq_en_votable;
+	struct votable		*pl_disable_votable;
 	struct fg_sram_param	*sp;
 	struct fg_dma_address	*addr_map;
 	struct fg_alg_flag	*alg_flags;
@@ -415,6 +420,7 @@
 	struct mutex		bus_lock;
 	struct mutex		sram_rw_lock;
 	struct mutex		charge_full_lock;
+	struct mutex		qnovo_esr_ctrl_lock;
 	u32			batt_soc_base;
 	u32			batt_info_base;
 	u32			mem_if_base;
@@ -449,13 +455,14 @@
 	bool			slope_limit_en;
 	bool			use_ima_single_mode;
 	bool			use_dma;
+	bool			qnovo_enable;
 	struct completion	soc_update;
 	struct completion	soc_ready;
-	struct completion	mem_grant;
 	struct delayed_work	profile_load_work;
 	struct work_struct	status_change_work;
 	struct delayed_work	ttf_work;
 	struct delayed_work	sram_dump_work;
+	struct delayed_work	pl_enable_work;
 };
 
 /* Debugfs data structures are below */
@@ -504,6 +511,7 @@
 extern int fg_read(struct fg_chip *chip, int addr, u8 *val, int len);
 extern int fg_write(struct fg_chip *chip, int addr, u8 *val, int len);
 extern int fg_masked_write(struct fg_chip *chip, int addr, u8 mask, u8 val);
+extern int fg_dump_regs(struct fg_chip *chip);
 extern int fg_ima_init(struct fg_chip *chip);
 extern int fg_dma_init(struct fg_chip *chip);
 extern int fg_clear_ima_errors_if_any(struct fg_chip *chip, bool check_hw_sts);
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index 0abc9df..d9b5ad7 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -746,15 +746,12 @@
 	return rc;
 }
 
-#define MEM_GRANT_WAIT_MS	200
+#define MEM_GNT_WAIT_TIME_US	10000
+#define MEM_GNT_RETRIES		20
 static int fg_direct_mem_request(struct fg_chip *chip, bool request)
 {
-	int rc, ret;
+	int rc, ret, i = 0;
 	u8 val, mask;
-	bool tried_again = false;
-
-	if (request)
-		reinit_completion(&chip->mem_grant);
 
 	mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
 	val = request ? MEM_ACCESS_REQ_BIT : 0;
@@ -769,7 +766,7 @@
 	rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask, val);
 	if (rc < 0) {
 		pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n", rc);
-		return rc;
+		goto release;
 	}
 
 	if (request)
@@ -780,42 +777,39 @@
 	if (!request)
 		return 0;
 
-wait:
-	ret = wait_for_completion_interruptible_timeout(
-		&chip->mem_grant, msecs_to_jiffies(MEM_GRANT_WAIT_MS));
-	/* If we were interrupted wait again one more time. */
-	if (ret <= 0) {
-		if ((ret == -ERESTARTSYS || ret == 0) && !tried_again) {
-			pr_debug("trying again, ret=%d\n", ret);
-			tried_again = true;
-			goto wait;
-		} else {
-			pr_err("wait for mem_grant timed out ret=%d\n",
-				ret);
+	while (i < MEM_GNT_RETRIES) {
+		rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &val, 1);
+		if (rc < 0) {
+			pr_err("Error in reading MEM_IF_INT_RT_STS, rc=%d\n",
+				rc);
+			goto release;
 		}
+
+		if (val & MEM_GNT_BIT)
+			return 0;
+
+		usleep_range(MEM_GNT_WAIT_TIME_US, MEM_GNT_WAIT_TIME_US + 1);
+		i++;
 	}
 
-	if (ret <= 0) {
-		val = 0;
-		mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
-		rc = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask,
-					val);
-		if (rc < 0) {
-			pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n",
-				rc);
-			return rc;
-		}
+	rc = -ETIMEDOUT;
+	pr_err("wait for mem_grant timed out, val=0x%x\n", val);
+	fg_dump_regs(chip);
 
-		mask = MEM_ARB_LO_LATENCY_EN_BIT | MEM_ARB_REQ_BIT;
-		rc = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask,
-					val);
-		if (rc < 0) {
-			pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n",
-				rc);
-			return rc;
-		}
+release:
+	val = 0;
+	mask = MEM_ACCESS_REQ_BIT | IACS_SLCT_BIT;
+	ret = fg_masked_write(chip, MEM_IF_MEM_INTF_CFG(chip), mask, val);
+	if (ret < 0) {
+		pr_err("failed to configure mem_if_mem_intf_cfg rc=%d\n", rc);
+		return ret;
+	}
 
-		return -ETIMEDOUT;
+	mask = MEM_ARB_LO_LATENCY_EN_BIT | MEM_ARB_REQ_BIT;
+	ret = fg_masked_write(chip, MEM_IF_MEM_ARB_CFG(chip), mask, val);
+	if (ret < 0) {
+		pr_err("failed to configure mem_if_mem_arb_cfg rc:%d\n", rc);
+		return ret;
 	}
 
 	return rc;
@@ -846,8 +840,8 @@
 static int fg_get_partition_count(struct fg_chip *chip, u16 sram_addr, int len,
 				int *count)
 {
-	int i, num = 0;
-	u16 end_addr, last_addr = 0;
+	int i, start_partn = 0, end_partn = 0;
+	u16 end_addr = 0;
 
 	end_addr = sram_addr + len / BYTES_PER_SRAM_WORD;
 	if (!(len % BYTES_PER_SRAM_WORD))
@@ -859,24 +853,24 @@
 	}
 
 	for (i = 0; i < NUM_PARTITIONS; i++) {
-		pr_debug("address: %d last_addr: %d\n", sram_addr, last_addr);
 		if (sram_addr >= chip->addr_map[i].partition_start
-			&& sram_addr <= chip->addr_map[i].partition_end
-			&& last_addr < end_addr) {
-			num++;
-			last_addr = chip->addr_map[i].partition_end;
-			sram_addr = chip->addr_map[i+1].partition_start;
-		}
+				&& sram_addr <= chip->addr_map[i].partition_end)
+			start_partn = i + 1;
+
+		if (end_addr >= chip->addr_map[i].partition_start
+				&& end_addr <= chip->addr_map[i].partition_end)
+			end_partn = i + 1;
 	}
 
-	if (num > 0) {
-		*count = num;
-		return 0;
+	if (!start_partn || !end_partn) {
+		pr_err("Couldn't find number of partitions for address %d\n",
+			sram_addr);
+		return -ENXIO;
 	}
 
-	pr_err("Couldn't find number of partitions for address %d\n",
-		sram_addr);
-	return -ENXIO;
+	*count = (end_partn - start_partn) + 1;
+
+	return 0;
 }
 
 static int fg_get_partition_avail_bytes(struct fg_chip *chip, u16 sram_addr,
diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c
index aed2062..622bfad 100644
--- a/drivers/power/supply/qcom/fg-util.c
+++ b/drivers/power/supply/qcom/fg-util.c
@@ -266,8 +266,7 @@
 int fg_sram_write(struct fg_chip *chip, u16 address, u8 offset,
 			u8 *val, int len, int flags)
 {
-	int rc = 0;
-	bool tried_again = false;
+	int rc = 0, tries = 0;
 	bool atomic_access = false;
 
 	if (!chip)
@@ -292,7 +291,7 @@
 		enable_irq(chip->irqs[SOC_UPDATE_IRQ].irq);
 		atomic_access = true;
 	}
-wait:
+
 	/*
 	 * Atomic access mean waiting upon SOC_UPDATE interrupt from
 	 * FG_ALG and do the transaction after that. This is to make
@@ -301,16 +300,20 @@
 	 * FG cycle (~1.47 seconds).
 	 */
 	if (atomic_access) {
-		/* Wait for SOC_UPDATE completion */
-		rc = wait_for_completion_interruptible_timeout(
-			&chip->soc_update,
-			msecs_to_jiffies(SOC_UPDATE_WAIT_MS));
+		for (tries = 0; tries < 2; tries++) {
+			/* Wait for SOC_UPDATE completion */
+			rc = wait_for_completion_interruptible_timeout(
+				&chip->soc_update,
+				msecs_to_jiffies(SOC_UPDATE_WAIT_MS));
+			if (rc > 0) {
+				rc = 0;
+				break;
+			} else if (!rc) {
+				rc = -ETIMEDOUT;
+			}
+		}
 
-		/* If we were interrupted wait again one more time. */
-		if (rc == -ERESTARTSYS && !tried_again) {
-			tried_again = true;
-			goto wait;
-		} else if (rc <= 0) {
+		if (rc < 0) {
 			pr_err("wait for soc_update timed out rc=%d\n", rc);
 			goto out;
 		}
@@ -492,6 +495,33 @@
 	return rc;
 }
 
+int fg_dump_regs(struct fg_chip *chip)
+{
+	int i, rc;
+	u8 buf[256];
+
+	if (!chip)
+		return -EINVAL;
+
+	rc = fg_read(chip, chip->batt_soc_base, buf, sizeof(buf));
+	if (rc < 0)
+		return rc;
+
+	pr_info("batt_soc_base registers:\n");
+	for (i = 0; i < sizeof(buf); i++)
+		pr_info("%04x:%02x\n", chip->batt_soc_base + i, buf[i]);
+
+	rc = fg_read(chip, chip->mem_if_base, buf, sizeof(buf));
+	if (rc < 0)
+		return rc;
+
+	pr_info("mem_if_base registers:\n");
+	for (i = 0; i < sizeof(buf); i++)
+		pr_info("%04x:%02x\n", chip->mem_if_base + i, buf[i]);
+
+	return 0;
+}
+
 int64_t twos_compliment_extend(int64_t val, int sign_bit_pos)
 {
 	int i, nbytes = DIV_ROUND_UP(sign_bit_pos, 8);
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index 6f615e4..8c53b2e 100644
--- a/drivers/power/supply/qcom/qpnp-fg-gen3.c
+++ b/drivers/power/supply/qcom/qpnp-fg-gen3.c
@@ -562,6 +562,21 @@
 	return 0;
 }
 
+#define BATT_SOC_32BIT	GENMASK(31, 0)
+static int fg_get_charge_counter_shadow(struct fg_chip *chip, int *val)
+{
+	int rc, batt_soc;
+
+	rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
+	if (rc < 0) {
+		pr_err("Error in getting BATT_SOC, rc=%d\n", rc);
+		return rc;
+	}
+
+	*val = div_u64((u32)batt_soc * chip->cl.learned_cc_uah, BATT_SOC_32BIT);
+	return 0;
+}
+
 static int fg_get_charge_counter(struct fg_chip *chip, int *val)
 {
 	int rc, cc_soc;
@@ -576,6 +591,41 @@
 	return 0;
 }
 
+static int fg_get_jeita_threshold(struct fg_chip *chip,
+				enum jeita_levels level, int *temp_decidegC)
+{
+	int rc;
+	u8 val;
+	u16 reg;
+
+	switch (level) {
+	case JEITA_COLD:
+		reg = BATT_INFO_JEITA_TOO_COLD(chip);
+		break;
+	case JEITA_COOL:
+		reg = BATT_INFO_JEITA_COLD(chip);
+		break;
+	case JEITA_WARM:
+		reg = BATT_INFO_JEITA_HOT(chip);
+		break;
+	case JEITA_HOT:
+		reg = BATT_INFO_JEITA_TOO_HOT(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rc = fg_read(chip, reg, &val, 1);
+	if (rc < 0) {
+		pr_err("Error in reading jeita level %d, rc=%d\n", level, rc);
+		return rc;
+	}
+
+	/* Resolution is 0.5C. Base is -30C. */
+	*temp_decidegC = (((5 * val) / 10) - 30) * 10;
+	return 0;
+}
+
 #define BATT_TEMP_NUMR		1
 #define BATT_TEMP_DENR		1
 static int fg_get_battery_temp(struct fg_chip *chip, int *val)
@@ -712,7 +762,19 @@
 	if (rc < 0)
 		return rc;
 
-	*msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW);
+	/*
+	 * To have better endpoints for 0 and 100, it is good to tune the
+	 * calculation discarding values 0 and 255 while rounding off. Rest
+	 * of the values 1-254 will be scaled to 1-99. DIV_ROUND_UP will not
+	 * be suitable here as it rounds up any value higher than 252 to 100.
+	 */
+	if (*msoc == FULL_SOC_RAW)
+		*msoc = 100;
+	else if (*msoc == 0)
+		*msoc = 0;
+	else
+		*msoc = DIV_ROUND_CLOSEST((*msoc - 1) * (FULL_CAPACITY - 2),
+				FULL_SOC_RAW - 2) + 1;
 	return 0;
 }
 
@@ -783,7 +845,7 @@
 {
 	int debug_batt_id[2], rc;
 
-	if (!chip->batt_id_ohms)
+	if (chip->batt_id_ohms < 0)
 		return false;
 
 	rc = fg_get_debug_batt_id(chip, debug_batt_id);
@@ -819,7 +881,7 @@
 		return 0;
 	}
 
-	if (chip->battery_missing) {
+	if (chip->battery_missing || !chip->soc_reporting_ready) {
 		*val = BATT_MISS_SOC;
 		return 0;
 	}
@@ -838,7 +900,7 @@
 	if (rc < 0)
 		return rc;
 
-	if (chip->delta_soc > 0)
+	if (chip->dt.linearize_soc && chip->delta_soc > 0)
 		*val = chip->maint_soc;
 	else
 		*val = msoc;
@@ -978,12 +1040,6 @@
 	return 0;
 }
 
-static inline void get_temp_setpoint(int threshold, u8 *val)
-{
-	/* Resolution is 0.5C. Base is -30C. */
-	*val = DIV_ROUND_CLOSEST((threshold + 30) * 10, 5);
-}
-
 static inline void get_batt_temp_delta(int delta, u8 *val)
 {
 	switch (delta) {
@@ -1119,7 +1175,7 @@
 		enable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
 	} else {
 		disable_irq_wake(chip->irqs[BATT_MISSING_IRQ].irq);
-		disable_irq(chip->irqs[BATT_MISSING_IRQ].irq);
+		disable_irq_nosync(chip->irqs[BATT_MISSING_IRQ].irq);
 	}
 
 	return 0;
@@ -1138,7 +1194,7 @@
 		enable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
 	} else {
 		disable_irq_wake(chip->irqs[BSOC_DELTA_IRQ].irq);
-		disable_irq(chip->irqs[BSOC_DELTA_IRQ].irq);
+		disable_irq_nosync(chip->irqs[BSOC_DELTA_IRQ].irq);
 	}
 
 	return 0;
@@ -1220,6 +1276,21 @@
 	return true;
 }
 
+static int fg_prime_cc_soc_sw(struct fg_chip *chip, int cc_soc_sw)
+{
+	int rc;
+
+	rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
+		chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
+		chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+	if (rc < 0)
+		pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
+	else
+		fg_dbg(chip, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw);
+
+	return rc;
+}
+
 static int fg_save_learned_cap_to_sram(struct fg_chip *chip)
 {
 	int16_t cc_mah;
@@ -1405,7 +1476,6 @@
 	return 0;
 }
 
-#define BATT_SOC_32BIT	GENMASK(31, 0)
 static int fg_cap_learning_begin(struct fg_chip *chip, u32 batt_soc)
 {
 	int rc, cc_soc_sw, batt_soc_msb;
@@ -1424,16 +1494,13 @@
 	/* Prime cc_soc_sw with battery SOC when capacity learning begins */
 	cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT,
 				BATT_SOC_32BIT);
-	rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
-		chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
-		chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+	rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
 	if (rc < 0) {
 		pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
 		goto out;
 	}
 
 	chip->cl.init_cc_soc_sw = cc_soc_sw;
-	chip->cl.active = true;
 	fg_dbg(chip, FG_CAP_LEARN, "Capacity learning started @ battery SOC %d init_cc_soc_sw:%d\n",
 		batt_soc_msb, chip->cl.init_cc_soc_sw);
 out:
@@ -1453,9 +1520,7 @@
 
 	/* Write a FULL value to cc_soc_sw */
 	cc_soc_sw = CC_SOC_30BIT;
-	rc = fg_sram_write(chip, chip->sp[FG_SRAM_CC_SOC_SW].addr_word,
-		chip->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
-		chip->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
+	rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
 	if (rc < 0) {
 		pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
 		goto out;
@@ -1468,8 +1533,9 @@
 
 static void fg_cap_learning_update(struct fg_chip *chip)
 {
-	int rc, batt_soc, batt_soc_msb;
+	int rc, batt_soc, batt_soc_msb, cc_soc_sw;
 	bool input_present = is_input_present(chip);
+	bool prime_cc = false;
 
 	mutex_lock(&chip->cl.lock);
 
@@ -1482,6 +1548,9 @@
 		goto out;
 	}
 
+	if (chip->charge_status == chip->prev_charge_status)
+		goto out;
+
 	rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &batt_soc);
 	if (rc < 0) {
 		pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
@@ -1497,8 +1566,12 @@
 		if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING) {
 			rc = fg_cap_learning_begin(chip, batt_soc);
 			chip->cl.active = (rc == 0);
+		} else {
+			if ((chip->charge_status ==
+					POWER_SUPPLY_STATUS_DISCHARGING) ||
+					chip->charge_done)
+				prime_cc = true;
 		}
-
 	} else {
 		if (chip->charge_done) {
 			rc = fg_cap_learning_done(chip);
@@ -1516,6 +1589,7 @@
 					 batt_soc_msb);
 				chip->cl.active = false;
 				chip->cl.init_cc_uah = 0;
+				prime_cc = true;
 			}
 		}
 
@@ -1532,10 +1606,29 @@
 					batt_soc_msb);
 				chip->cl.active = false;
 				chip->cl.init_cc_uah = 0;
+				prime_cc = true;
 			}
 		}
 	}
 
+	/*
+	 * Prime CC_SOC_SW when the device is not charging or during charge
+	 * termination when the capacity learning is not active.
+	 */
+
+	if (prime_cc) {
+		if (chip->charge_done)
+			cc_soc_sw = CC_SOC_30BIT;
+		else
+			cc_soc_sw = div_u64((u32)batt_soc *
+					CC_SOC_30BIT, BATT_SOC_32BIT);
+
+		rc = fg_prime_cc_soc_sw(chip, cc_soc_sw);
+		if (rc < 0)
+			pr_err("Error in writing cc_soc_sw, rc=%d\n",
+				rc);
+	}
+
 out:
 	mutex_unlock(&chip->cl.lock);
 }
@@ -1602,6 +1695,8 @@
 
 	if (batt_temp < 0)
 		ki_coeff_full_soc = 0;
+	else if (chip->charge_status == POWER_SUPPLY_STATUS_DISCHARGING)
+		ki_coeff_full_soc = chip->dt.ki_coeff_full_soc_dischg;
 	else
 		ki_coeff_full_soc = KI_COEFF_FULL_SOC_DEFAULT;
 
@@ -1658,12 +1753,38 @@
 	return 0;
 }
 
+static int fg_configure_full_soc(struct fg_chip *chip, int bsoc)
+{
+	int rc;
+	u8 full_soc[2] = {0xFF, 0xFF};
+
+	/*
+	 * Once SOC masking condition is cleared, FULL_SOC and MONOTONIC_SOC
+	 * needs to be updated to reflect the same. Write battery SOC to
+	 * FULL_SOC and write a full value to MONOTONIC_SOC.
+	 */
+	rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET,
+			(u8 *)&bsoc, 2, FG_IMA_ATOMIC);
+	if (rc < 0) {
+		pr_err("failed to write full_soc rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET,
+			full_soc, 2, FG_IMA_ATOMIC);
+	if (rc < 0) {
+		pr_err("failed to write monotonic_soc rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
 #define AUTO_RECHG_VOLT_LOW_LIMIT_MV	3700
 static int fg_charge_full_update(struct fg_chip *chip)
 {
 	union power_supply_propval prop = {0, };
 	int rc, msoc, bsoc, recharge_soc, msoc_raw;
-	u8 full_soc[2] = {0xFF, 0xFF};
 
 	if (!chip->dt.hold_soc_while_full)
 		return 0;
@@ -1693,12 +1814,12 @@
 
 	/* We need 2 most significant bytes here */
 	bsoc = (u32)bsoc >> 16;
-	rc = fg_get_msoc(chip, &msoc);
+	rc = fg_get_msoc_raw(chip, &msoc_raw);
 	if (rc < 0) {
-		pr_err("Error in getting msoc, rc=%d\n", rc);
+		pr_err("Error in getting msoc_raw, rc=%d\n", rc);
 		goto out;
 	}
-	msoc_raw = DIV_ROUND_CLOSEST(msoc * FULL_SOC_RAW, FULL_CAPACITY);
+	msoc = DIV_ROUND_CLOSEST(msoc_raw * FULL_CAPACITY, FULL_SOC_RAW);
 
 	fg_dbg(chip, FG_STATUS, "msoc: %d bsoc: %x health: %d status: %d full: %d\n",
 		msoc, bsoc, chip->health, chip->charge_status,
@@ -1722,24 +1843,25 @@
 			fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n",
 				msoc);
 		}
-	} else if (msoc_raw < recharge_soc && chip->charge_full) {
-		chip->delta_soc = FULL_CAPACITY - msoc;
+	} else if ((msoc_raw <= recharge_soc || !chip->charge_done)
+			&& chip->charge_full) {
+		if (chip->dt.linearize_soc) {
+			chip->delta_soc = FULL_CAPACITY - msoc;
 
-		/*
-		 * We're spreading out the delta SOC over every 10% change
-		 * in monotonic SOC. We cannot spread more than 9% in the
-		 * range of 0-100 skipping the first 10%.
-		 */
-		if (chip->delta_soc > 9) {
-			chip->delta_soc = 0;
-			chip->maint_soc = 0;
-		} else {
-			chip->maint_soc = FULL_CAPACITY;
-			chip->last_msoc = msoc;
+			/*
+			 * We're spreading out the delta SOC over every 10%
+			 * change in monotonic SOC. We cannot spread more than
+			 * 9% in the range of 0-100 skipping the first 10%.
+			 */
+			if (chip->delta_soc > 9) {
+				chip->delta_soc = 0;
+				chip->maint_soc = 0;
+			} else {
+				chip->maint_soc = FULL_CAPACITY;
+				chip->last_msoc = msoc;
+			}
 		}
 
-		chip->charge_full = false;
-
 		/*
 		 * Raise the recharge voltage so that VBAT_LT_RECHG signal
 		 * will be asserted soon as battery SOC had dropped below
@@ -1752,35 +1874,23 @@
 				rc);
 			goto out;
 		}
+
+		/*
+		 * If charge_done is still set, wait for recharging or
+		 * discharging to happen.
+		 */
+		if (chip->charge_done)
+			goto out;
+
+		rc = fg_configure_full_soc(chip, bsoc);
+		if (rc < 0)
+			goto out;
+
+		chip->charge_full = false;
 		fg_dbg(chip, FG_STATUS, "msoc_raw = %d bsoc: %d recharge_soc: %d delta_soc: %d\n",
 			msoc_raw, bsoc >> 8, recharge_soc, chip->delta_soc);
-	} else {
-		goto out;
 	}
 
-	if (!chip->charge_full)
-		goto out;
-
-	/*
-	 * During JEITA conditions, charge_full can happen early. FULL_SOC
-	 * and MONOTONIC_SOC needs to be updated to reflect the same. Write
-	 * battery SOC to FULL_SOC and write a full value to MONOTONIC_SOC.
-	 */
-	rc = fg_sram_write(chip, FULL_SOC_WORD, FULL_SOC_OFFSET, (u8 *)&bsoc, 2,
-			FG_IMA_ATOMIC);
-	if (rc < 0) {
-		pr_err("failed to write full_soc rc=%d\n", rc);
-		goto out;
-	}
-
-	rc = fg_sram_write(chip, MONOTONIC_SOC_WORD, MONOTONIC_SOC_OFFSET,
-			full_soc, 2, FG_IMA_ATOMIC);
-	if (rc < 0) {
-		pr_err("failed to write monotonic_soc rc=%d\n", rc);
-		goto out;
-	}
-
-	fg_dbg(chip, FG_STATUS, "Set charge_full to true @ soc %d\n", msoc);
 out:
 	mutex_unlock(&chip->charge_full_lock);
 	return rc;
@@ -1863,6 +1973,44 @@
 	return 0;
 }
 
+static int fg_set_jeita_threshold(struct fg_chip *chip,
+				enum jeita_levels level, int temp_decidegC)
+{
+	int rc;
+	u8 val;
+	u16 reg;
+
+	if (temp_decidegC < -300 || temp_decidegC > 970)
+		return -EINVAL;
+
+	/* Resolution is 0.5C. Base is -30C. */
+	val = DIV_ROUND_CLOSEST(((temp_decidegC / 10) + 30) * 10, 5);
+	switch (level) {
+	case JEITA_COLD:
+		reg = BATT_INFO_JEITA_TOO_COLD(chip);
+		break;
+	case JEITA_COOL:
+		reg = BATT_INFO_JEITA_COLD(chip);
+		break;
+	case JEITA_WARM:
+		reg = BATT_INFO_JEITA_HOT(chip);
+		break;
+	case JEITA_HOT:
+		reg = BATT_INFO_JEITA_TOO_HOT(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	rc = fg_write(chip, reg, &val, 1);
+	if (rc < 0) {
+		pr_err("Error in setting jeita level %d, rc=%d\n", level, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
 static int fg_set_constant_chg_voltage(struct fg_chip *chip, int volt_uv)
 {
 	u8 buf[2];
@@ -2419,6 +2567,11 @@
 		goto out;
 	}
 
+	if (!chip->soc_reporting_ready) {
+		fg_dbg(chip, FG_STATUS, "Profile load is not complete yet\n");
+		goto out;
+	}
+
 	rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_STATUS,
 			&prop);
 	if (rc < 0) {
@@ -2426,7 +2579,6 @@
 		goto out;
 	}
 
-	chip->prev_charge_status = chip->charge_status;
 	chip->charge_status = prop.intval;
 	rc = power_supply_get_property(chip->batt_psy,
 			POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
@@ -2481,9 +2633,9 @@
 	}
 
 	fg_ttf_update(chip);
-
+	chip->prev_charge_status = chip->charge_status;
 out:
-	fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
+	fg_dbg(chip, FG_STATUS, "charge_status:%d charge_type:%d charge_done:%d\n",
 		chip->charge_status, chip->charge_type, chip->charge_done);
 	pm_relax(chip->dev);
 }
@@ -2586,6 +2738,49 @@
 	return true;
 }
 
+static void fg_update_batt_profile(struct fg_chip *chip)
+{
+	int rc, offset;
+	u8 val;
+
+	rc = fg_sram_read(chip, PROFILE_INTEGRITY_WORD,
+			SW_CONFIG_OFFSET, &val, 1, FG_IMA_DEFAULT);
+	if (rc < 0) {
+		pr_err("Error in reading SW_CONFIG_OFFSET, rc=%d\n", rc);
+		return;
+	}
+
+	/*
+	 * If the RCONN had not been updated, no need to update battery
+	 * profile. Else, update the battery profile so that the profile
+	 * modified by bootloader or HLOS matches with the profile read
+	 * from device tree.
+	 */
+
+	if (!(val & RCONN_CONFIG_BIT))
+		return;
+
+	rc = fg_sram_read(chip, ESR_RSLOW_CHG_WORD,
+			ESR_RSLOW_CHG_OFFSET, &val, 1, FG_IMA_DEFAULT);
+	if (rc < 0) {
+		pr_err("Error in reading ESR_RSLOW_CHG_OFFSET, rc=%d\n", rc);
+		return;
+	}
+	offset = (ESR_RSLOW_CHG_WORD - PROFILE_LOAD_WORD) * 4
+			+ ESR_RSLOW_CHG_OFFSET;
+	chip->batt_profile[offset] = val;
+
+	rc = fg_sram_read(chip, ESR_RSLOW_DISCHG_WORD,
+			ESR_RSLOW_DISCHG_OFFSET, &val, 1, FG_IMA_DEFAULT);
+	if (rc < 0) {
+		pr_err("Error in reading ESR_RSLOW_DISCHG_OFFSET, rc=%d\n", rc);
+		return;
+	}
+	offset = (ESR_RSLOW_DISCHG_WORD - PROFILE_LOAD_WORD) * 4
+			+ ESR_RSLOW_DISCHG_OFFSET;
+	chip->batt_profile[offset] = val;
+}
+
 static void clear_battery_profile(struct fg_chip *chip)
 {
 	u8 val = 0;
@@ -2643,6 +2838,16 @@
 	return rc;
 }
 
+static void pl_enable_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				pl_enable_work.work);
+
+	vote(chip->pl_disable_votable, ESR_FCC_VOTER, false, 0);
+	vote(chip->awake_votable, ESR_FCC_VOTER, false, 0);
+}
+
 static void profile_load_work(struct work_struct *work)
 {
 	struct fg_chip *chip = container_of(work,
@@ -2669,6 +2874,8 @@
 	if (!chip->profile_available)
 		goto out;
 
+	fg_update_batt_profile(chip);
+
 	if (!is_profile_load_required(chip))
 		goto done;
 
@@ -2730,13 +2937,23 @@
 				rc);
 	}
 
+	rc = fg_rconn_config(chip);
+	if (rc < 0)
+		pr_err("Error in configuring Rconn, rc=%d\n", rc);
+
 	batt_psy_initialized(chip);
 	fg_notify_charger(chip);
 	chip->profile_loaded = true;
 	fg_dbg(chip, FG_STATUS, "profile loaded successfully");
 out:
 	chip->soc_reporting_ready = true;
+	vote(chip->awake_votable, ESR_FCC_VOTER, true, 0);
+	schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000));
 	vote(chip->awake_votable, PROFILE_LOAD, false, 0);
+	if (!work_pending(&chip->status_change_work)) {
+		pm_stay_awake(chip->dev);
+		schedule_work(&chip->status_change_work);
+	}
 }
 
 static void sram_dump_work(struct work_struct *work)
@@ -2862,6 +3079,9 @@
 		t_predicted_cv, t_predicted = 0;
 	s64 delta_ms;
 
+	if (!chip->soc_reporting_ready)
+		return -ENODATA;
+
 	if (chip->bp.float_volt_uv <= 0) {
 		pr_err("battery profile is not loaded\n");
 		return -ENODATA;
@@ -3137,6 +3357,9 @@
 {
 	int rc = 0, msoc;
 
+	if (!chip->dt.linearize_soc)
+		return 0;
+
 	mutex_lock(&chip->charge_full_lock);
 	if (chip->delta_soc <= 0)
 		goto out;
@@ -3213,20 +3436,21 @@
 	int rc;
 	int esr_uohms;
 
+	mutex_lock(&chip->qnovo_esr_ctrl_lock);
 	/* force esr extraction enable */
 	rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
 			ESR_EXTRACTION_ENABLE_OFFSET, BIT(0), BIT(0),
 			FG_IMA_DEFAULT);
 	if (rc < 0) {
 		pr_err("failed to enable esr extn rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
 	rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
 			LD_REG_CTRL_BIT, 0);
 	if (rc < 0) {
 		pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
 	rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
@@ -3234,24 +3458,36 @@
 			ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT);
 	if (rc < 0) {
 		pr_err("Error in configuring force ESR rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
+	/*
+	 * Release and grab the lock again after 1.5 seconds so that prepare
+	 * callback can succeed if the request comes in between.
+	 */
+	mutex_unlock(&chip->qnovo_esr_ctrl_lock);
+
 	/* wait 1.5 seconds for hw to measure ESR */
 	msleep(1500);
+
+	mutex_lock(&chip->qnovo_esr_ctrl_lock);
 	rc = fg_masked_write(chip, BATT_INFO_TM_MISC1(chip),
 			ESR_REQ_CTL_BIT | ESR_REQ_CTL_EN_BIT,
 			0);
 	if (rc < 0) {
 		pr_err("Error in restoring force ESR rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
+	/* If qnovo is disabled, then leave ESR extraction enabled */
+	if (!chip->qnovo_enable)
+		goto done;
+
 	rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
 			LD_REG_CTRL_BIT, LD_REG_CTRL_BIT);
 	if (rc < 0) {
 		pr_err("Error in restoring qnovo_cfg rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
 	/* force esr extraction disable */
@@ -3260,36 +3496,46 @@
 			FG_IMA_DEFAULT);
 	if (rc < 0) {
 		pr_err("failed to disable esr extn rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
 
+done:
 	fg_get_battery_resistance(chip, &esr_uohms);
 	fg_dbg(chip, FG_STATUS, "ESR uohms = %d\n", esr_uohms);
-
+out:
+	mutex_unlock(&chip->qnovo_esr_ctrl_lock);
 	return rc;
 }
 
 static int fg_prepare_for_qnovo(struct fg_chip *chip, int qnovo_enable)
 {
-	int rc;
+	int rc = 0;
 
+	mutex_lock(&chip->qnovo_esr_ctrl_lock);
 	/* force esr extraction disable when qnovo enables */
 	rc = fg_sram_masked_write(chip, ESR_EXTRACTION_ENABLE_WORD,
 			ESR_EXTRACTION_ENABLE_OFFSET,
 			BIT(0), qnovo_enable ? 0 : BIT(0),
 			FG_IMA_DEFAULT);
-	if (rc < 0)
+	if (rc < 0) {
 		pr_err("Error in configuring esr extraction rc=%d\n", rc);
+		goto out;
+	}
 
 	rc = fg_masked_write(chip, BATT_INFO_QNOVO_CFG(chip),
 			LD_REG_CTRL_BIT,
 			qnovo_enable ? LD_REG_CTRL_BIT : 0);
 	if (rc < 0) {
 		pr_err("Error in configuring qnovo_cfg rc=%d\n", rc);
-		return rc;
+		goto out;
 	}
-	fg_dbg(chip, FG_STATUS, "Prepared for Qnovo\n");
-	return 0;
+
+	fg_dbg(chip, FG_STATUS, "%s for Qnovo\n",
+		qnovo_enable ? "Prepared" : "Unprepared");
+	chip->qnovo_enable = qnovo_enable;
+out:
+	mutex_unlock(&chip->qnovo_esr_ctrl_lock);
+	return rc;
 }
 
 static void ttf_work(struct work_struct *work)
@@ -3365,6 +3611,9 @@
 	case POWER_SUPPLY_PROP_CAPACITY:
 		rc = fg_get_prop_capacity(chip, &pval->intval);
 		break;
+	case POWER_SUPPLY_PROP_CAPACITY_RAW:
+		rc = fg_get_msoc_raw(chip, &pval->intval);
+		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		if (chip->battery_missing)
 			pval->intval = 3700000;
@@ -3377,6 +3626,34 @@
 	case POWER_SUPPLY_PROP_TEMP:
 		rc = fg_get_battery_temp(chip, &pval->intval);
 		break;
+	case POWER_SUPPLY_PROP_COLD_TEMP:
+		rc = fg_get_jeita_threshold(chip, JEITA_COLD, &pval->intval);
+		if (rc < 0) {
+			pr_err("Error in reading jeita_cold, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+		rc = fg_get_jeita_threshold(chip, JEITA_COOL, &pval->intval);
+		if (rc < 0) {
+			pr_err("Error in reading jeita_cool, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+		rc = fg_get_jeita_threshold(chip, JEITA_WARM, &pval->intval);
+		if (rc < 0) {
+			pr_err("Error in reading jeita_warm, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_HOT_TEMP:
+		rc = fg_get_jeita_threshold(chip, JEITA_HOT, &pval->intval);
+		if (rc < 0) {
+			pr_err("Error in reading jeita_hot, rc=%d\n", rc);
+			return rc;
+		}
+		break;
 	case POWER_SUPPLY_PROP_RESISTANCE:
 		rc = fg_get_battery_resistance(chip, &pval->intval);
 		break;
@@ -3413,6 +3690,9 @@
 	case POWER_SUPPLY_PROP_CHARGE_COUNTER:
 		rc = fg_get_charge_counter(chip, &pval->intval);
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW:
+		rc = fg_get_charge_counter_shadow(chip, &pval->intval);
+		break;
 	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
 		rc = fg_get_time_to_full(chip, &pval->intval);
 		break;
@@ -3500,6 +3780,48 @@
 			return -EINVAL;
 		}
 		break;
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+		if (chip->cl.active) {
+			pr_warn("Capacity learning active!\n");
+			return 0;
+		}
+		if (pval->intval <= 0 || pval->intval > chip->cl.nom_cap_uah) {
+			pr_err("charge_full is out of bounds\n");
+			return -EINVAL;
+		}
+		chip->cl.learned_cc_uah = pval->intval;
+		rc = fg_save_learned_cap_to_sram(chip);
+		if (rc < 0)
+			pr_err("Error in saving learned_cc_uah, rc=%d\n", rc);
+		break;
+	case POWER_SUPPLY_PROP_COLD_TEMP:
+		rc = fg_set_jeita_threshold(chip, JEITA_COLD, pval->intval);
+		if (rc < 0) {
+			pr_err("Error in writing jeita_cold, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+		rc = fg_set_jeita_threshold(chip, JEITA_COOL, pval->intval);
+		if (rc < 0) {
+			pr_err("Error in writing jeita_cool, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+		rc = fg_set_jeita_threshold(chip, JEITA_WARM, pval->intval);
+		if (rc < 0) {
+			pr_err("Error in writing jeita_warm, rc=%d\n", rc);
+			return rc;
+		}
+		break;
+	case POWER_SUPPLY_PROP_HOT_TEMP:
+		rc = fg_set_jeita_threshold(chip, JEITA_HOT, pval->intval);
+		if (rc < 0) {
+			pr_err("Error in writing jeita_hot, rc=%d\n", rc);
+			return rc;
+		}
+		break;
 	default:
 		break;
 	}
@@ -3515,6 +3837,11 @@
 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
 	case POWER_SUPPLY_PROP_CC_STEP:
 	case POWER_SUPPLY_PROP_CC_STEP_SEL:
+	case POWER_SUPPLY_PROP_CHARGE_FULL:
+	case POWER_SUPPLY_PROP_COLD_TEMP:
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+	case POWER_SUPPLY_PROP_HOT_TEMP:
 		return 1;
 	default:
 		break;
@@ -3541,6 +3868,7 @@
 		return NOTIFY_OK;
 
 	if ((strcmp(psy->desc->name, "battery") == 0)
+		|| (strcmp(psy->desc->name, "parallel") == 0)
 		|| (strcmp(psy->desc->name, "usb") == 0)) {
 		/*
 		 * We cannot vote for awake votable here as that takes
@@ -3555,7 +3883,12 @@
 
 static enum power_supply_property fg_psy_props[] = {
 	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_RAW,
 	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_COLD_TEMP,
+	POWER_SUPPLY_PROP_COOL_TEMP,
+	POWER_SUPPLY_PROP_WARM_TEMP,
+	POWER_SUPPLY_PROP_HOT_TEMP,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
 	POWER_SUPPLY_PROP_VOLTAGE_OCV,
 	POWER_SUPPLY_PROP_CURRENT_NOW,
@@ -3570,6 +3903,7 @@
 	POWER_SUPPLY_PROP_CHARGE_NOW,
 	POWER_SUPPLY_PROP_CHARGE_FULL,
 	POWER_SUPPLY_PROP_CHARGE_COUNTER,
+	POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW,
 	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
 	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
 	POWER_SUPPLY_PROP_SOC_REPORTING_READY,
@@ -3734,29 +4068,29 @@
 		}
 	}
 
-	get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COLD], &val);
-	rc = fg_write(chip, BATT_INFO_JEITA_TOO_COLD(chip), &val, 1);
+	rc = fg_set_jeita_threshold(chip, JEITA_COLD,
+		chip->dt.jeita_thresholds[JEITA_COLD] * 10);
 	if (rc < 0) {
 		pr_err("Error in writing jeita_cold, rc=%d\n", rc);
 		return rc;
 	}
 
-	get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_COOL], &val);
-	rc = fg_write(chip, BATT_INFO_JEITA_COLD(chip), &val, 1);
+	rc = fg_set_jeita_threshold(chip, JEITA_COOL,
+		chip->dt.jeita_thresholds[JEITA_COOL] * 10);
 	if (rc < 0) {
 		pr_err("Error in writing jeita_cool, rc=%d\n", rc);
 		return rc;
 	}
 
-	get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_WARM], &val);
-	rc = fg_write(chip, BATT_INFO_JEITA_HOT(chip), &val, 1);
+	rc = fg_set_jeita_threshold(chip, JEITA_WARM,
+		chip->dt.jeita_thresholds[JEITA_WARM] * 10);
 	if (rc < 0) {
 		pr_err("Error in writing jeita_warm, rc=%d\n", rc);
 		return rc;
 	}
 
-	get_temp_setpoint(chip->dt.jeita_thresholds[JEITA_HOT], &val);
-	rc = fg_write(chip, BATT_INFO_JEITA_TOO_HOT(chip), &val, 1);
+	rc = fg_set_jeita_threshold(chip, JEITA_HOT,
+		chip->dt.jeita_thresholds[JEITA_HOT] * 10);
 	if (rc < 0) {
 		pr_err("Error in writing jeita_hot, rc=%d\n", rc);
 		return rc;
@@ -3807,12 +4141,6 @@
 		return rc;
 	}
 
-	rc = fg_rconn_config(chip);
-	if (rc < 0) {
-		pr_err("Error in configuring Rconn, rc=%d\n", rc);
-		return rc;
-	}
-
 	fg_encode(chip->sp, FG_SRAM_ESR_TIGHT_FILTER,
 		chip->dt.esr_tight_flt_upct, buf);
 	rc = fg_sram_write(chip, chip->sp[FG_SRAM_ESR_TIGHT_FILTER].addr_word,
@@ -3908,26 +4236,6 @@
 
 /* INTERRUPT HANDLERS STAY HERE */
 
-static irqreturn_t fg_dma_grant_irq_handler(int irq, void *data)
-{
-	struct fg_chip *chip = data;
-	u8 status;
-	int rc;
-
-	rc = fg_read(chip, MEM_IF_INT_RT_STS(chip), &status, 1);
-	if (rc < 0) {
-		pr_err("failed to read addr=0x%04x, rc=%d\n",
-			MEM_IF_INT_RT_STS(chip), rc);
-		return IRQ_HANDLED;
-	}
-
-	fg_dbg(chip, FG_IRQ, "irq %d triggered, status:%d\n", irq, status);
-	if (status & MEM_GNT_BIT)
-		complete_all(&chip->mem_grant);
-
-	return IRQ_HANDLED;
-}
-
 static irqreturn_t fg_mem_xcp_irq_handler(int irq, void *data)
 {
 	struct fg_chip *chip = data;
@@ -3986,6 +4294,9 @@
 		chip->profile_available = false;
 		chip->profile_loaded = false;
 		chip->soc_reporting_ready = false;
+		chip->batt_id_ohms = -EINVAL;
+		cancel_delayed_work_sync(&chip->pl_enable_work);
+		vote(chip->pl_disable_votable, ESR_FCC_VOTER, true, 0);
 		return IRQ_HANDLED;
 	}
 
@@ -4212,7 +4523,7 @@
 	/* MEM_IF irqs */
 	[DMA_GRANT_IRQ] = {
 		.name		= "dma-grant",
-		.handler	= fg_dma_grant_irq_handler,
+		.handler	= fg_dummy_irq_handler,
 		.wakeable	= true,
 	},
 	[MEM_XCP_IRQ] = {
@@ -4333,7 +4644,11 @@
 static int fg_parse_ki_coefficients(struct fg_chip *chip)
 {
 	struct device_node *node = chip->dev->of_node;
-	int rc, i;
+	int rc, i, temp;
+
+	rc = of_property_read_u32(node, "qcom,ki-coeff-full-dischg", &temp);
+	if (!rc)
+		chip->dt.ki_coeff_full_soc_dischg = temp;
 
 	rc = fg_parse_dt_property_u32_array(node, "qcom,ki-coeff-soc-dischg",
 		chip->dt.ki_coeff_soc, KI_COEFF_SOC_LEVELS);
@@ -4387,7 +4702,7 @@
 #define DEFAULT_BATT_TEMP_HOT		50
 #define DEFAULT_CL_START_SOC		15
 #define DEFAULT_CL_MIN_TEMP_DECIDEGC	150
-#define DEFAULT_CL_MAX_TEMP_DECIDEGC	450
+#define DEFAULT_CL_MAX_TEMP_DECIDEGC	500
 #define DEFAULT_CL_MAX_INC_DECIPERC	5
 #define DEFAULT_CL_MAX_DEC_DECIPERC	100
 #define DEFAULT_CL_MIN_LIM_DECIPERC	0
@@ -4680,6 +4995,9 @@
 	chip->dt.hold_soc_while_full = of_property_read_bool(node,
 					"qcom,hold-soc-while-full");
 
+	chip->dt.linearize_soc = of_property_read_bool(node,
+					"qcom,linearize-soc");
+
 	rc = fg_parse_ki_coefficients(chip);
 	if (rc < 0)
 		pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
@@ -4761,6 +5079,13 @@
 
 static void fg_cleanup(struct fg_chip *chip)
 {
+	int i;
+
+	for (i = 0; i < FG_IRQ_MAX; i++) {
+		if (fg_irqs[i].irq)
+			devm_free_irq(chip->dev, fg_irqs[i].irq, chip);
+	}
+
 	power_supply_unreg_notifier(&chip->nb);
 	debugfs_remove_recursive(chip->dfs_root);
 	if (chip->awake_votable)
@@ -4795,6 +5120,7 @@
 	chip->prev_charge_status = -EINVAL;
 	chip->ki_coeff_full_soc = -EINVAL;
 	chip->online_status = -EINVAL;
+	chip->batt_id_ohms = -EINVAL;
 	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
 	if (!chip->regmap) {
 		dev_err(chip->dev, "Parent regmap is unavailable\n");
@@ -4826,6 +5152,12 @@
 		}
 	}
 
+	chip->pl_disable_votable = find_votable("PL_DISABLE");
+	if (chip->pl_disable_votable == NULL) {
+		rc = -EPROBE_DEFER;
+		goto exit;
+	}
+
 	chip->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb,
 					chip);
 	if (IS_ERR(chip->awake_votable)) {
@@ -4865,10 +5197,11 @@
 	mutex_init(&chip->cl.lock);
 	mutex_init(&chip->ttf.lock);
 	mutex_init(&chip->charge_full_lock);
+	mutex_init(&chip->qnovo_esr_ctrl_lock);
 	init_completion(&chip->soc_update);
 	init_completion(&chip->soc_ready);
-	init_completion(&chip->mem_grant);
 	INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
+	INIT_DELAYED_WORK(&chip->pl_enable_work, pl_enable_work);
 	INIT_WORK(&chip->status_change_work, status_change_work);
 	INIT_DELAYED_WORK(&chip->ttf_work, ttf_work);
 	INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
@@ -4882,23 +5215,6 @@
 
 	platform_set_drvdata(pdev, chip);
 
-	rc = fg_register_interrupts(chip);
-	if (rc < 0) {
-		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
-			rc);
-		goto exit;
-	}
-
-	/* Keep SOC_UPDATE irq disabled until we require it */
-	if (fg_irqs[SOC_UPDATE_IRQ].irq)
-		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
-
-	/* Keep BSOC_DELTA_IRQ disabled until we require it */
-	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
-
-	/* Keep BATT_MISSING_IRQ disabled until we require it */
-	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);
-
 	rc = fg_hw_init(chip);
 	if (rc < 0) {
 		dev_err(chip->dev, "Error in initializing FG hardware, rc:%d\n",
@@ -4926,6 +5242,23 @@
 		goto exit;
 	}
 
+	rc = fg_register_interrupts(chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Error in registering interrupts, rc:%d\n",
+			rc);
+		goto exit;
+	}
+
+	/* Keep SOC_UPDATE irq disabled until we require it */
+	if (fg_irqs[SOC_UPDATE_IRQ].irq)
+		disable_irq_nosync(fg_irqs[SOC_UPDATE_IRQ].irq);
+
+	/* Keep BSOC_DELTA_IRQ disabled until we require it */
+	vote(chip->delta_bsoc_irq_en_votable, DELTA_BSOC_IRQ_VOTER, false, 0);
+
+	/* Keep BATT_MISSING_IRQ disabled until we require it */
+	vote(chip->batt_miss_irq_en_votable, BATT_MISS_IRQ_VOTER, false, 0);
+
 	rc = fg_debugfs_create(chip);
 	if (rc < 0) {
 		dev_err(chip->dev, "Error in creating debugfs entries, rc:%d\n",
@@ -4941,8 +5274,8 @@
 		rc = fg_get_battery_temp(chip, &batt_temp);
 
 	if (!rc) {
-		pr_info("battery SOC:%d voltage: %duV temp: %d id: %dKOhms\n",
-			msoc, volt_uv, batt_temp, chip->batt_id_ohms / 1000);
+		pr_info("battery SOC:%d voltage: %duV temp: %d\n",
+				msoc, volt_uv, batt_temp);
 		rc = fg_esr_filter_config(chip, batt_temp);
 		if (rc < 0)
 			pr_err("Error in configuring ESR filter rc:%d\n", rc);
@@ -5002,6 +5335,29 @@
 	return 0;
 }
 
+static void fg_gen3_shutdown(struct platform_device *pdev)
+{
+	struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+	int rc, bsoc;
+
+	if (chip->charge_full) {
+		rc = fg_get_sram_prop(chip, FG_SRAM_BATT_SOC, &bsoc);
+		if (rc < 0) {
+			pr_err("Error in getting BATT_SOC, rc=%d\n", rc);
+			return;
+		}
+
+		/* We need 2 most significant bytes here */
+		bsoc = (u32)bsoc >> 16;
+
+		rc = fg_configure_full_soc(chip, bsoc);
+		if (rc < 0) {
+			pr_err("Error in configuring full_soc, rc=%d\n", rc);
+			return;
+		}
+	}
+}
+
 static const struct of_device_id fg_gen3_match_table[] = {
 	{.compatible = FG_GEN3_DEV_NAME},
 	{},
@@ -5016,6 +5372,7 @@
 	},
 	.probe		= fg_gen3_probe,
 	.remove		= fg_gen3_remove,
+	.shutdown	= fg_gen3_shutdown,
 };
 
 static int __init fg_gen3_init(void)
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
new file mode 100644
index 0000000..a12b0ad
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -0,0 +1,7045 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"FG: %s: " fmt, __func__
+
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/init.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/ktime.h>
+#include <linux/power_supply.h>
+#include <linux/of_batterydata.h>
+#include <linux/string_helpers.h>
+#include <linux/alarmtimer.h>
+#include <linux/qpnp/qpnp-revid.h>
+
+/* Register offsets */
+
+/* Interrupt offsets */
+#define INT_RT_STS(base)			(base + 0x10)
+#define INT_EN_CLR(base)			(base + 0x16)
+
+/* SPMI Register offsets */
+#define SOC_MONOTONIC_SOC	0x09
+#define SOC_BOOT_MOD		0x50
+#define SOC_RESTART		0x51
+
+#define REG_OFFSET_PERP_SUBTYPE	0x05
+
+/* RAM register offsets */
+#define RAM_OFFSET		0x400
+
+/* Bit/Mask definitions */
+#define FULL_PERCENT		0xFF
+#define MAX_TRIES_SOC		5
+#define MA_MV_BIT_RES		39
+#define MSB_SIGN		BIT(7)
+#define IBAT_VBAT_MASK		0x7F
+#define NO_OTP_PROF_RELOAD	BIT(6)
+#define REDO_FIRST_ESTIMATE	BIT(3)
+#define RESTART_GO		BIT(0)
+#define THERM_DELAY_MASK	0xE0
+
+/* SUBTYPE definitions */
+#define FG_SOC			0x9
+#define FG_BATT			0xA
+#define FG_ADC			0xB
+#define FG_MEMIF		0xC
+
+#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
+#define MEM_IF_TIMEOUT_MS	5000
+#define BUCKET_COUNT		8
+#define BUCKET_SOC_PCT		(256 / BUCKET_COUNT)
+
+#define BCL_MA_TO_ADC(_current, _adc_val) {		\
+	_adc_val = (u8)((_current) * 100 / 976);	\
+}
+
+/* Debug Flag Definitions */
+enum {
+	FG_SPMI_DEBUG_WRITES		= BIT(0), /* Show SPMI writes */
+	FG_SPMI_DEBUG_READS		= BIT(1), /* Show SPMI reads */
+	FG_IRQS				= BIT(2), /* Show interrupts */
+	FG_MEM_DEBUG_WRITES		= BIT(3), /* Show SRAM writes */
+	FG_MEM_DEBUG_READS		= BIT(4), /* Show SRAM reads */
+	FG_POWER_SUPPLY			= BIT(5), /* Show POWER_SUPPLY */
+	FG_STATUS			= BIT(6), /* Show FG status changes */
+	FG_AGING			= BIT(7), /* Show FG aging algorithm */
+};
+
+/* PMIC REVISIONS */
+#define REVID_RESERVED			0
+#define REVID_VARIANT			1
+#define REVID_ANA_MAJOR			2
+#define REVID_DIG_MAJOR			3
+
+enum dig_major {
+	DIG_REV_1 = 0x1,
+	DIG_REV_2 = 0x2,
+	DIG_REV_3 = 0x3,
+};
+
+enum pmic_subtype {
+	PMI8994		= 10,
+	PMI8950		= 17,
+	PMI8996		= 19,
+	PMI8937		= 55,
+};
+
+enum wa_flags {
+	IADC_GAIN_COMP_WA = BIT(0),
+	USE_CC_SOC_REG = BIT(1),
+	PULSE_REQUEST_WA = BIT(2),
+	BCL_HI_POWER_FOR_CHGLED_WA = BIT(3)
+};
+
+enum current_sense_type {
+	INTERNAL_CURRENT_SENSE,
+	EXTERNAL_CURRENT_SENSE,
+};
+
+struct fg_mem_setting {
+	u16	address;
+	u8	offset;
+	int	value;
+};
+
+struct fg_mem_data {
+	u16	address;
+	u8	offset;
+	unsigned int len;
+	int	value;
+};
+
+struct fg_learning_data {
+	int64_t			cc_uah;
+	int64_t			learned_cc_uah;
+	int			init_cc_pc_val;
+	bool			active;
+	bool			feedback_on;
+	struct mutex		learning_lock;
+	ktime_t			time_stamp;
+	/* configuration properties */
+	int			max_start_soc;
+	int			max_increment;
+	int			max_decrement;
+	int			min_temp;
+	int			max_temp;
+	int			vbat_est_thr_uv;
+};
+
+struct fg_rslow_data {
+	u8			rslow_cfg;
+	u8			rslow_thr;
+	u8			rs_to_rslow[2];
+	u8			rslow_comp[4];
+	uint32_t		chg_rs_to_rslow;
+	uint32_t		chg_rslow_comp_c1;
+	uint32_t		chg_rslow_comp_c2;
+	uint32_t		chg_rslow_comp_thr;
+	bool			active;
+	struct mutex		lock;
+};
+
+struct fg_cyc_ctr_data {
+	bool			en;
+	bool			started[BUCKET_COUNT];
+	u16			count[BUCKET_COUNT];
+	u8			last_soc[BUCKET_COUNT];
+	int			id;
+	struct mutex		lock;
+};
+
+struct fg_iadc_comp_data {
+	u8			dfl_gain_reg[2];
+	bool			gain_active;
+	int64_t			dfl_gain;
+};
+
+struct fg_cc_soc_data {
+	int	init_sys_soc;
+	int	init_cc_soc;
+	int	full_capacity;
+	int	delta_soc;
+};
+
+/* FG_MEMIF setting index */
+enum fg_mem_setting_index {
+	FG_MEM_SOFT_COLD = 0,
+	FG_MEM_SOFT_HOT,
+	FG_MEM_HARD_COLD,
+	FG_MEM_HARD_HOT,
+	FG_MEM_RESUME_SOC,
+	FG_MEM_BCL_LM_THRESHOLD,
+	FG_MEM_BCL_MH_THRESHOLD,
+	FG_MEM_TERM_CURRENT,
+	FG_MEM_CHG_TERM_CURRENT,
+	FG_MEM_IRQ_VOLT_EMPTY,
+	FG_MEM_CUTOFF_VOLTAGE,
+	FG_MEM_VBAT_EST_DIFF,
+	FG_MEM_DELTA_SOC,
+	FG_MEM_BATT_LOW,
+	FG_MEM_THERM_DELAY,
+	FG_MEM_SETTING_MAX,
+};
+
+/* FG_MEMIF data index */
+enum fg_mem_data_index {
+	FG_DATA_BATT_TEMP = 0,
+	FG_DATA_OCV,
+	FG_DATA_VOLTAGE,
+	FG_DATA_CURRENT,
+	FG_DATA_BATT_ESR,
+	FG_DATA_BATT_ESR_COUNT,
+	FG_DATA_BATT_SOC,
+	FG_DATA_CC_CHARGE,
+	FG_DATA_VINT_ERR,
+	FG_DATA_CPRED_VOLTAGE,
+	/* values below this only gets read once per profile reload */
+	FG_DATA_BATT_ID,
+	FG_DATA_BATT_ID_INFO,
+	FG_DATA_MAX,
+};
+
+#define SETTING(_idx, _address, _offset, _value)	\
+	[FG_MEM_##_idx] = {				\
+		.address = _address,			\
+		.offset = _offset,			\
+		.value = _value,			\
+	}						\
+
+static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = {
+	/*       ID                    Address, Offset, Value*/
+	SETTING(SOFT_COLD,       0x454,   0,      100),
+	SETTING(SOFT_HOT,        0x454,   1,      400),
+	SETTING(HARD_COLD,       0x454,   2,      50),
+	SETTING(HARD_HOT,        0x454,   3,      450),
+	SETTING(RESUME_SOC,      0x45C,   1,      0),
+	SETTING(BCL_LM_THRESHOLD, 0x47C,   2,      50),
+	SETTING(BCL_MH_THRESHOLD, 0x47C,   3,      752),
+	SETTING(TERM_CURRENT,	 0x40C,   2,      250),
+	SETTING(CHG_TERM_CURRENT, 0x4F8,   2,      250),
+	SETTING(IRQ_VOLT_EMPTY,	 0x458,   3,      3100),
+	SETTING(CUTOFF_VOLTAGE,	 0x40C,   0,      3200),
+	SETTING(VBAT_EST_DIFF,	 0x000,   0,      30),
+	SETTING(DELTA_SOC,	 0x450,   3,      1),
+	SETTING(BATT_LOW,	 0x458,   0,      4200),
+	SETTING(THERM_DELAY,	 0x4AC,   3,      0),
+};
+
+#define DATA(_idx, _address, _offset, _length,  _value)	\
+	[FG_DATA_##_idx] = {				\
+		.address = _address,			\
+		.offset = _offset,			\
+		.len = _length,			\
+		.value = _value,			\
+	}						\
+
+static struct fg_mem_data fg_data[FG_DATA_MAX] = {
+	/*       ID           Address, Offset, Length, Value*/
+	DATA(BATT_TEMP,       0x550,   2,      2,     -EINVAL),
+	DATA(OCV,             0x588,   3,      2,     -EINVAL),
+	DATA(VOLTAGE,         0x5CC,   1,      2,     -EINVAL),
+	DATA(CURRENT,         0x5CC,   3,      2,     -EINVAL),
+	DATA(BATT_ESR,        0x554,   2,      2,     -EINVAL),
+	DATA(BATT_ESR_COUNT,  0x558,   2,      2,     -EINVAL),
+	DATA(BATT_SOC,        0x56C,   1,      3,     -EINVAL),
+	DATA(CC_CHARGE,       0x570,   0,      4,     -EINVAL),
+	DATA(VINT_ERR,        0x560,   0,      4,     -EINVAL),
+	DATA(CPRED_VOLTAGE,   0x540,   0,      2,     -EINVAL),
+	DATA(BATT_ID,         0x594,   1,      1,     -EINVAL),
+	DATA(BATT_ID_INFO,    0x594,   3,      1,     -EINVAL),
+};
+
+static int fg_debug_mask;
+module_param_named(
+	debug_mask, fg_debug_mask, int, 00600
+);
+
+static int fg_sense_type = -EINVAL;
+static int fg_restart;
+
+static int fg_est_dump;
+module_param_named(
+	first_est_dump, fg_est_dump, int, 00600
+);
+
+static char *fg_batt_type;
+module_param_named(
+	battery_type, fg_batt_type, charp, 00600
+);
+
+static int fg_sram_update_period_ms = 30000;
+module_param_named(
+	sram_update_period_ms, fg_sram_update_period_ms, int, 00600
+);
+
+struct fg_irq {
+	int			irq;
+	unsigned long		disabled;
+};
+
+enum fg_soc_irq {
+	HIGH_SOC,
+	LOW_SOC,
+	FULL_SOC,
+	EMPTY_SOC,
+	DELTA_SOC,
+	FIRST_EST_DONE,
+	SW_FALLBK_OCV,
+	SW_FALLBK_NEW_BATT,
+	FG_SOC_IRQ_COUNT,
+};
+
+enum fg_batt_irq {
+	JEITA_SOFT_COLD,
+	JEITA_SOFT_HOT,
+	VBATT_LOW,
+	BATT_IDENTIFIED,
+	BATT_ID_REQ,
+	BATTERY_UNKNOWN,
+	BATT_MISSING,
+	BATT_MATCH,
+	FG_BATT_IRQ_COUNT,
+};
+
+enum fg_mem_if_irq {
+	FG_MEM_AVAIL,
+	TA_RCVRY_SUG,
+	FG_MEM_IF_IRQ_COUNT,
+};
+
+enum fg_batt_aging_mode {
+	FG_AGING_NONE,
+	FG_AGING_ESR,
+	FG_AGING_CC,
+};
+
+enum register_type {
+	MEM_INTF_CFG,
+	MEM_INTF_CTL,
+	MEM_INTF_ADDR_LSB,
+	MEM_INTF_RD_DATA0,
+	MEM_INTF_WR_DATA0,
+	MAX_ADDRESS,
+};
+
+struct register_offset {
+	u16 address[MAX_ADDRESS];
+};
+
+static struct register_offset offset[] = {
+	[0] = {
+			 /* CFG   CTL   LSB   RD0   WD0 */
+		.address = {0x40, 0x41, 0x42, 0x4C, 0x48},
+	},
+	[1] = {
+			 /* CFG   CTL   LSB   RD0   WD0 */
+		.address = {0x50, 0x51, 0x61, 0x67, 0x63},
+	},
+};
+
+#define MEM_INTF_CFG(chip)	\
+		((chip)->mem_base + (chip)->offset[MEM_INTF_CFG])
+#define MEM_INTF_CTL(chip)	\
+		((chip)->mem_base + (chip)->offset[MEM_INTF_CTL])
+#define MEM_INTF_ADDR_LSB(chip) \
+		((chip)->mem_base + (chip)->offset[MEM_INTF_ADDR_LSB])
+#define MEM_INTF_RD_DATA0(chip) \
+		((chip)->mem_base + (chip)->offset[MEM_INTF_RD_DATA0])
+#define MEM_INTF_WR_DATA0(chip) \
+		((chip)->mem_base + (chip)->offset[MEM_INTF_WR_DATA0])
+
+struct fg_wakeup_source {
+	struct wakeup_source	source;
+	unsigned long		enabled;
+};
+
+static void fg_stay_awake(struct fg_wakeup_source *source)
+{
+	if (!__test_and_set_bit(0, &source->enabled)) {
+		__pm_stay_awake(&source->source);
+		pr_debug("enabled source %s\n", source->source.name);
+	}
+}
+
+static void fg_relax(struct fg_wakeup_source *source)
+{
+	if (__test_and_clear_bit(0, &source->enabled)) {
+		__pm_relax(&source->source);
+		pr_debug("disabled source %s\n", source->source.name);
+	}
+}
+
+#define THERMAL_COEFF_N_BYTES		6
+struct fg_chip {
+	struct device		*dev;
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	u8			pmic_subtype;
+	u8			pmic_revision[4];
+	u8			revision[4];
+	u16			soc_base;
+	u16			batt_base;
+	u16			mem_base;
+	u16			vbat_adc_addr;
+	u16			ibat_adc_addr;
+	u16			tp_rev_addr;
+	u32			wa_flag;
+	atomic_t		memif_user_cnt;
+	struct fg_irq		soc_irq[FG_SOC_IRQ_COUNT];
+	struct fg_irq		batt_irq[FG_BATT_IRQ_COUNT];
+	struct fg_irq		mem_irq[FG_MEM_IF_IRQ_COUNT];
+	struct completion	sram_access_granted;
+	struct completion	sram_access_revoked;
+	struct completion	batt_id_avail;
+	struct completion	first_soc_done;
+	struct power_supply	*bms_psy;
+	struct power_supply_desc	bms_psy_d;
+	struct mutex		rw_lock;
+	struct mutex		sysfs_restart_lock;
+	struct delayed_work	batt_profile_init;
+	struct work_struct	dump_sram;
+	struct work_struct	status_change_work;
+	struct work_struct	cycle_count_work;
+	struct work_struct	battery_age_work;
+	struct work_struct	update_esr_work;
+	struct work_struct	set_resume_soc_work;
+	struct work_struct	rslow_comp_work;
+	struct work_struct	sysfs_restart_work;
+	struct work_struct	init_work;
+	struct work_struct	charge_full_work;
+	struct work_struct	gain_comp_work;
+	struct work_struct	bcl_hi_power_work;
+	struct power_supply	*batt_psy;
+	struct power_supply	*usb_psy;
+	struct power_supply	*dc_psy;
+	struct fg_wakeup_source	memif_wakeup_source;
+	struct fg_wakeup_source	profile_wakeup_source;
+	struct fg_wakeup_source	empty_check_wakeup_source;
+	struct fg_wakeup_source	resume_soc_wakeup_source;
+	struct fg_wakeup_source	gain_comp_wakeup_source;
+	struct fg_wakeup_source	capacity_learning_wakeup_source;
+	bool			first_profile_loaded;
+	struct fg_wakeup_source	update_temp_wakeup_source;
+	struct fg_wakeup_source	update_sram_wakeup_source;
+	bool			fg_restarting;
+	bool			profile_loaded;
+	bool			use_otp_profile;
+	bool			battery_missing;
+	bool			power_supply_registered;
+	bool			sw_rbias_ctrl;
+	bool			use_thermal_coefficients;
+	bool			esr_strict_filter;
+	bool			soc_empty;
+	bool			charge_done;
+	bool			resume_soc_lowered;
+	bool			vbat_low_irq_enabled;
+	bool			charge_full;
+	bool			hold_soc_while_full;
+	bool			input_present;
+	bool			otg_present;
+	bool			safety_timer_expired;
+	bool			bad_batt_detection_en;
+	bool			bcl_lpm_disabled;
+	bool			charging_disabled;
+	struct delayed_work	update_jeita_setting;
+	struct delayed_work	update_sram_data;
+	struct delayed_work	update_temp_work;
+	struct delayed_work	check_empty_work;
+	char			*batt_profile;
+	u8			thermal_coefficients[THERMAL_COEFF_N_BYTES];
+	u32			cc_cv_threshold_mv;
+	unsigned int		batt_profile_len;
+	unsigned int		batt_max_voltage_uv;
+	const char		*batt_type;
+	const char		*batt_psy_name;
+	unsigned long		last_sram_update_time;
+	unsigned long		last_temp_update_time;
+	int64_t			ocv_coeffs[12];
+	int64_t			cutoff_voltage;
+	int			evaluation_current;
+	int			ocv_junction_p1p2;
+	int			ocv_junction_p2p3;
+	int			nom_cap_uah;
+	int			actual_cap_uah;
+	int			status;
+	int			prev_status;
+	int			health;
+	enum fg_batt_aging_mode	batt_aging_mode;
+	/* capacity learning */
+	struct fg_learning_data	learning_data;
+	struct alarm		fg_cap_learning_alarm;
+	struct work_struct	fg_cap_learning_work;
+	struct fg_cc_soc_data	sw_cc_soc_data;
+	/* rslow compensation */
+	struct fg_rslow_data	rslow_comp;
+	/* cycle counter */
+	struct fg_cyc_ctr_data	cyc_ctr;
+	/* iadc compensation */
+	struct fg_iadc_comp_data iadc_comp_data;
+	/* interleaved memory access */
+	u16			*offset;
+	bool			ima_supported;
+	bool			init_done;
+	/* jeita hysteresis */
+	bool			jeita_hysteresis_support;
+	bool			batt_hot;
+	bool			batt_cold;
+	int			cold_hysteresis;
+	int			hot_hysteresis;
+	/* ESR pulse tuning */
+	struct fg_wakeup_source	esr_extract_wakeup_source;
+	struct work_struct	esr_extract_config_work;
+	bool			esr_extract_disabled;
+	bool			imptr_pulse_slow_en;
+	bool			esr_pulse_tune_en;
+};
+
+/* FG_MEMIF DEBUGFS structures */
+#define ADDR_LEN	4	/* 3 byte address + 1 space character */
+#define CHARS_PER_ITEM	3	/* Format is 'XX ' */
+#define ITEMS_PER_LINE	4	/* 4 data items per line */
+#define MAX_LINE_LENGTH  (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
+#define MAX_REG_PER_TRANSACTION	(8)
+
+static const char *DFS_ROOT_NAME	= "fg_memif";
+static const mode_t DFS_MODE		= 00600;
+static const char *default_batt_type	= "Unknown Battery";
+static const char *loading_batt_type	= "Loading Battery Data";
+static const char *missing_batt_type	= "Disconnected Battery";
+
+/* Log buffer */
+struct fg_log_buffer {
+	size_t rpos;	/* Current 'read' position in buffer */
+	size_t wpos;	/* Current 'write' position in buffer */
+	size_t len;	/* Length of the buffer */
+	char data[0];	/* Log buffer */
+};
+
+/* transaction parameters */
+struct fg_trans {
+	u32 cnt;	/* Number of bytes to read */
+	u16 addr;	/* 12-bit address in SRAM */
+	u32 offset;	/* Offset of last read data + byte offset */
+	struct fg_chip *chip;
+	struct fg_log_buffer *log; /* log buffer */
+	u8 *data;	/* fg data that is read */
+	struct mutex memif_dfs_lock; /* Prevent thread concurrency */
+};
+
+struct fg_dbgfs {
+	u32 cnt;
+	u32 addr;
+	struct fg_chip *chip;
+	struct dentry *root;
+	struct mutex  lock;
+	struct debugfs_blob_wrapper help_msg;
+};
+
+static struct fg_dbgfs dbgfs_data = {
+	.lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
+	.help_msg = {
+	.data =
+"FG Debug-FS support\n"
+"\n"
+"Hierarchy schema:\n"
+"/sys/kernel/debug/fg_memif\n"
+"       /help            -- Static help text\n"
+"       /address  -- Starting register address for reads or writes\n"
+"       /count    -- Number of registers to read (only used for reads)\n"
+"       /data     -- Initiates the SRAM read (formatted output)\n"
+"\n",
+	},
+};
+
+static const struct of_device_id fg_match_table[] = {
+	{	.compatible = QPNP_FG_DEV_NAME, },
+	{}
+};
+
+static char *fg_supplicants[] = {
+	"battery",
+	"bcl",
+	"fg_adc"
+};
+
+#define DEBUG_PRINT_BUFFER_SIZE 64
+static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+	int pos = 0;
+	int i;
+
+	for (i = 0; i < buf_len; i++) {
+		pos += scnprintf(str + pos, str_len - pos, "%02X", buf[i]);
+		if (i < buf_len - 1)
+			pos += scnprintf(str + pos, str_len - pos, " ");
+	}
+}
+
+static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len)
+{
+	int rc = 0;
+	struct platform_device *pdev = chip->pdev;
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	if ((addr & 0xff00) == 0) {
+		pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+		return -EINVAL;
+	}
+
+	rc = regmap_bulk_write(chip->regmap, addr, val, len);
+	if (rc) {
+		pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+		return rc;
+	}
+
+	if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
+		pr_info("write(0x%04X), sid=%d, len=%d; %s\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, len,
+			str);
+	}
+
+	return rc;
+}
+
+static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len)
+{
+	int rc = 0;
+	struct platform_device *pdev = chip->pdev;
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	if ((addr & 0xff00) == 0) {
+		pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+		return -EINVAL;
+	}
+
+	rc = regmap_bulk_read(chip->regmap, addr, val, len);
+	if (rc) {
+		pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr,
+				to_spmi_device(pdev->dev.parent)->usid, rc);
+		return rc;
+	}
+
+	if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len);
+		pr_info("read(0x%04x), sid=%d, len=%d; %s\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, len,
+			str);
+	}
+
+	return rc;
+}
+
+static int fg_masked_write(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) {
+		pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+#define RIF_MEM_ACCESS_REQ	BIT(7)
+static int fg_check_rif_mem_access(struct fg_chip *chip, bool *status)
+{
+	int rc;
+	u8 mem_if_sts;
+
+	rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
+	if (rc) {
+		pr_err("failed to read rif_mem status rc=%d\n", rc);
+		return rc;
+	}
+
+	*status = mem_if_sts & RIF_MEM_ACCESS_REQ;
+	return 0;
+}
+
+static bool fg_check_sram_access(struct fg_chip *chip)
+{
+	int rc;
+	u8 mem_if_sts;
+	bool rif_mem_sts = false;
+
+	rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+	if (rc) {
+		pr_err("failed to read mem status rc=%d\n", rc);
+		return false;
+	}
+
+	if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0)
+		return false;
+
+	rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+	if (rc)
+		return false;
+
+	return rif_mem_sts;
+}
+
+static inline int fg_assert_sram_access(struct fg_chip *chip)
+{
+	int rc;
+	u8 mem_if_sts;
+
+	rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+	if (rc) {
+		pr_err("failed to read mem status rc=%d\n", rc);
+		return rc;
+	}
+
+	if ((mem_if_sts & BIT(FG_MEM_AVAIL)) == 0) {
+		pr_err("mem_avail not high: %02x\n", mem_if_sts);
+		return -EINVAL;
+	}
+
+	rc = fg_read(chip, &mem_if_sts, MEM_INTF_CFG(chip), 1);
+	if (rc) {
+		pr_err("failed to read mem status rc=%d\n", rc);
+		return rc;
+	}
+
+	if ((mem_if_sts & RIF_MEM_ACCESS_REQ) == 0) {
+		pr_err("mem_avail not high: %02x\n", mem_if_sts);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define INTF_CTL_BURST		BIT(7)
+#define INTF_CTL_WR_EN		BIT(6)
+static int fg_config_access(struct fg_chip *chip, bool write,
+		bool burst)
+{
+	int rc;
+	u8 intf_ctl = 0;
+
+	intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0);
+
+	rc = fg_write(chip, &intf_ctl, MEM_INTF_CTL(chip), 1);
+	if (rc) {
+		pr_err("failed to set mem access bit\n");
+		return -EIO;
+	}
+
+	return rc;
+}
+
+static int fg_req_and_wait_access(struct fg_chip *chip, int timeout)
+{
+	int rc = 0, ret = 0;
+	bool tried_again = false;
+
+	if (!fg_check_sram_access(chip)) {
+		rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+			RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1);
+		if (rc) {
+			pr_err("failed to set mem access bit\n");
+			return -EIO;
+		}
+		fg_stay_awake(&chip->memif_wakeup_source);
+	}
+
+wait:
+	/* Wait for MEM_AVAIL IRQ. */
+	ret = wait_for_completion_interruptible_timeout(
+			&chip->sram_access_granted,
+			msecs_to_jiffies(timeout));
+	/* If we were interrupted wait again one more time. */
+	if (ret == -ERESTARTSYS && !tried_again) {
+		tried_again = true;
+		goto wait;
+	} else if (ret <= 0) {
+		rc = -ETIMEDOUT;
+		pr_err("transaction timed out rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int fg_release_access(struct fg_chip *chip)
+{
+	int rc;
+
+	rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+			RIF_MEM_ACCESS_REQ, 0, 1);
+	fg_relax(&chip->memif_wakeup_source);
+	reinit_completion(&chip->sram_access_granted);
+
+	return rc;
+}
+
+static void fg_release_access_if_necessary(struct fg_chip *chip)
+{
+	mutex_lock(&chip->rw_lock);
+	if (atomic_sub_return(1, &chip->memif_user_cnt) <= 0)
+		fg_release_access(chip);
+	mutex_unlock(&chip->rw_lock);
+}
+
+/*
+ * fg_mem_lock disallows the fuel gauge to release access until it has been
+ * released.
+ *
+ * an equal number of calls must be made to fg_mem_release for the fuel gauge
+ * driver to release the sram access.
+ */
+static void fg_mem_lock(struct fg_chip *chip)
+{
+	mutex_lock(&chip->rw_lock);
+	atomic_add_return(1, &chip->memif_user_cnt);
+	mutex_unlock(&chip->rw_lock);
+}
+
+static void fg_mem_release(struct fg_chip *chip)
+{
+	fg_release_access_if_necessary(chip);
+}
+
+static int fg_set_ram_addr(struct fg_chip *chip, u16 *address)
+{
+	int rc;
+
+	rc = fg_write(chip, (u8 *) address,
+		chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], 2);
+	if (rc) {
+		pr_err("spmi write failed: addr=%03X, rc=%d\n",
+			chip->mem_base + chip->offset[MEM_INTF_ADDR_LSB], rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+#define BUF_LEN		4
+static int fg_sub_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
+		int offset)
+{
+	int rc, total_len;
+	u8 *rd_data = val;
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	rc = fg_config_access(chip, 0, (len > 4));
+	if (rc)
+		return rc;
+
+	rc = fg_set_ram_addr(chip, &address);
+	if (rc)
+		return rc;
+
+	if (fg_debug_mask & FG_MEM_DEBUG_READS)
+		pr_info("length %d addr=%02X\n", len, address);
+
+	total_len = len;
+	while (len > 0) {
+		if (!offset) {
+			rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip),
+							min(len, BUF_LEN));
+		} else {
+			rc = fg_read(chip, rd_data,
+				MEM_INTF_RD_DATA0(chip) + offset,
+				min(len, BUF_LEN - offset));
+
+			/* manually set address to allow continuous reads */
+			address += BUF_LEN;
+
+			rc = fg_set_ram_addr(chip, &address);
+			if (rc)
+				return rc;
+		}
+		if (rc) {
+			pr_err("spmi read failed: addr=%03x, rc=%d\n",
+				MEM_INTF_RD_DATA0(chip) + offset, rc);
+			return rc;
+		}
+		rd_data += (BUF_LEN - offset);
+		len -= (BUF_LEN - offset);
+		offset = 0;
+	}
+
+	if (fg_debug_mask & FG_MEM_DEBUG_READS) {
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
+		pr_info("data: %s\n", str);
+	}
+	return rc;
+}
+
+static int fg_conventional_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+		int len, int offset, bool keep_access)
+{
+	int rc = 0, user_cnt = 0, orig_address = address;
+
+	if (offset > 3) {
+		pr_err("offset too large %d\n", offset);
+		return -EINVAL;
+	}
+
+	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_READS)
+		pr_info("user_cnt %d\n", user_cnt);
+	mutex_lock(&chip->rw_lock);
+	if (!fg_check_sram_access(chip)) {
+		rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
+		if (rc)
+			goto out;
+	}
+
+	rc = fg_sub_mem_read(chip, val, address, len, offset);
+
+out:
+	user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
+	if (fg_debug_mask & FG_MEM_DEBUG_READS)
+		pr_info("user_cnt %d\n", user_cnt);
+
+	fg_assert_sram_access(chip);
+
+	if (!keep_access && (user_cnt == 0) && !rc) {
+		rc = fg_release_access(chip);
+		if (rc) {
+			pr_err("failed to set mem access bit\n");
+			rc = -EIO;
+		}
+	}
+
+	mutex_unlock(&chip->rw_lock);
+	return rc;
+}
+
+static int fg_conventional_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+		int len, int offset, bool keep_access)
+{
+	int rc = 0, user_cnt = 0, sublen;
+	bool access_configured = false;
+	u8 *wr_data = val, word[4];
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	if (address < RAM_OFFSET)
+		return -EINVAL;
+
+	if (offset > 3)
+		return -EINVAL;
+
+	address = ((address + offset) / 4) * 4;
+	offset = (address + offset) % 4;
+
+	user_cnt = atomic_add_return(1, &chip->memif_user_cnt);
+	if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+		pr_info("user_cnt %d\n", user_cnt);
+	mutex_lock(&chip->rw_lock);
+	if (!fg_check_sram_access(chip)) {
+		rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS);
+		if (rc)
+			goto out;
+	}
+
+	if (fg_debug_mask & FG_MEM_DEBUG_WRITES) {
+		pr_info("length %d addr=%02X offset=%d\n",
+				len, address, offset);
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, wr_data, len);
+		pr_info("writing: %s\n", str);
+	}
+
+	while (len > 0) {
+		if (offset != 0) {
+			sublen = min(4 - offset, len);
+			rc = fg_sub_mem_read(chip, word, address, 4, 0);
+			if (rc)
+				goto out;
+			memcpy(word + offset, wr_data, sublen);
+			/* configure access as burst if more to write */
+			rc = fg_config_access(chip, 1, (len - sublen) > 0);
+			if (rc)
+				goto out;
+			rc = fg_set_ram_addr(chip, &address);
+			if (rc)
+				goto out;
+			offset = 0;
+			access_configured = true;
+		} else if (len >= 4) {
+			if (!access_configured) {
+				rc = fg_config_access(chip, 1, len > 4);
+				if (rc)
+					goto out;
+				rc = fg_set_ram_addr(chip, &address);
+				if (rc)
+					goto out;
+				access_configured = true;
+			}
+			sublen = 4;
+			memcpy(word, wr_data, 4);
+		} else if (len > 0 && len < 4) {
+			sublen = len;
+			rc = fg_sub_mem_read(chip, word, address, 4, 0);
+			if (rc)
+				goto out;
+			memcpy(word, wr_data, sublen);
+			rc = fg_config_access(chip, 1, 0);
+			if (rc)
+				goto out;
+			rc = fg_set_ram_addr(chip, &address);
+			if (rc)
+				goto out;
+			access_configured = true;
+		} else {
+			pr_err("Invalid length: %d\n", len);
+			break;
+		}
+		rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip), 4);
+		if (rc) {
+			pr_err("spmi write failed: addr=%03x, rc=%d\n",
+					MEM_INTF_WR_DATA0(chip), rc);
+			goto out;
+		}
+		len -= sublen;
+		wr_data += sublen;
+		address += 4;
+	}
+
+out:
+	user_cnt = atomic_sub_return(1, &chip->memif_user_cnt);
+	if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+		pr_info("user_cnt %d\n", user_cnt);
+
+	fg_assert_sram_access(chip);
+
+	if (!keep_access && (user_cnt == 0) && !rc) {
+		rc = fg_release_access(chip);
+		if (rc) {
+			pr_err("failed to set mem access bit\n");
+			rc = -EIO;
+		}
+	}
+
+	mutex_unlock(&chip->rw_lock);
+	return rc;
+}
+
+#define MEM_INTF_IMA_CFG		0x52
+#define MEM_INTF_IMA_OPR_STS		0x54
+#define MEM_INTF_IMA_ERR_STS		0x5F
+#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)
+{
+	int rc = 0, ret = 0;
+	u8 err_sts, exp_sts = 0, hw_sts = 0;
+
+	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);
+		return rc;
+	}
+
+	if (err_sts & (IMA_ADDR_STBL_ERR | IMA_WR_ACS_ERR | IMA_RD_ACS_ERR)) {
+		u8 temp;
+
+		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);
+	}
+
+	return rc;
+}
+
+static int fg_check_iacs_ready(struct fg_chip *chip)
+{
+	int rc = 0, timeout = 250;
+	u8 ima_opr_sts = 0;
+
+	/*
+	 * Additional delay to make sure IACS ready bit is set after
+	 * Read/Write operation.
+	 */
+
+	usleep_range(30, 35);
+	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))
+			break;
+
+		if (!(--timeout) || rc)
+			break;
+		/* delay for iacs_ready to be asserted */
+		usleep_range(5000, 7000);
+	}
+
+	if (!timeout || rc) {
+		pr_err("IACS_RDY not set\n");
+		/* perform IACS_CLR sequence */
+		fg_check_ima_exception(chip);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+#define IACS_SLCT			BIT(5)
+static int __fg_interleaved_mem_write(struct fg_chip *chip, u8 *val,
+				u16 address, int offset, int len)
+{
+	int rc = 0, i;
+	u8 *word = val, byte_enable = 0, num_bytes = 0;
+
+	if (fg_debug_mask & FG_MEM_DEBUG_WRITES)
+		pr_info("length %d addr=%02X offset=%d\n",
+					len, address, offset);
+
+	while (len > 0) {
+		num_bytes = (offset + len) > BUF_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);
+		if (rc) {
+			pr_err("Unable to write to byte_en_reg rc=%d\n", rc);
+			return rc;
+		}
+			/* write data */
+		rc = fg_write(chip, word, MEM_INTF_WR_DATA0(chip) + offset,
+				num_bytes);
+		if (rc) {
+			pr_err("spmi write failed: addr=%03x, rc=%d\n",
+				MEM_INTF_WR_DATA0(chip) + offset, rc);
+			return rc;
+		}
+		/*
+		 * The last-byte WR_DATA3 starts the write transaction.
+		 * Write a dummy value to WR_DATA3 if it does not have
+		 * valid data. This dummy data is not written to the
+		 * SRAM as byte_en for WR_DATA3 is not set.
+		 */
+		if (!(byte_enable & BIT(3))) {
+			u8 dummy_byte = 0x0;
+
+			rc = fg_write(chip, &dummy_byte,
+				MEM_INTF_WR_DATA0(chip) + 3, 1);
+			if (rc) {
+				pr_err("Unable to write dummy-data to WR_DATA3 rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		rc = fg_check_iacs_ready(chip);
+		if (rc) {
+			pr_debug("IACS_RDY failed rc=%d\n", rc);
+			return rc;
+		}
+
+		/* check for error condition */
+		rc = fg_check_ima_exception(chip);
+		if (rc) {
+			pr_err("IMA transaction failed rc=%d", rc);
+			return rc;
+		}
+
+		word += num_bytes;
+		len -= num_bytes;
+		offset = byte_enable = 0;
+	}
+
+	return rc;
+}
+
+static int __fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+						int offset, int len)
+{
+	int rc = 0, total_len;
+	u8 *rd_data = val, num_bytes;
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	if (fg_debug_mask & FG_MEM_DEBUG_READS)
+		pr_info("length %d addr=%02X\n", len, address);
+
+	total_len = len;
+	while (len > 0) {
+		num_bytes = (offset + len) > BUF_LEN ? (BUF_LEN - offset) : len;
+		rc = fg_read(chip, rd_data, MEM_INTF_RD_DATA0(chip) + offset,
+								num_bytes);
+		if (rc) {
+			pr_err("spmi read failed: addr=%03x, rc=%d\n",
+				MEM_INTF_RD_DATA0(chip) + offset, rc);
+			return rc;
+		}
+
+		rd_data += num_bytes;
+		len -= num_bytes;
+		offset = 0;
+
+		rc = fg_check_iacs_ready(chip);
+		if (rc) {
+			pr_debug("IACS_RDY failed rc=%d\n", rc);
+			return rc;
+		}
+
+		/* check for error condition */
+		rc = fg_check_ima_exception(chip);
+		if (rc) {
+			pr_err("IMA transaction failed rc=%d", rc);
+			return rc;
+		}
+
+		if (len && (len + offset) < BUF_LEN) {
+			/* move to single mode */
+			u8 intr_ctl = 0;
+
+			rc = fg_write(chip, &intr_ctl, MEM_INTF_CTL(chip), 1);
+			if (rc) {
+				pr_err("failed to move to single mode rc=%d\n",
+									rc);
+				return -EIO;
+			}
+		}
+	}
+
+	if (fg_debug_mask & FG_MEM_DEBUG_READS) {
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
+		pr_info("data: %s\n", str);
+	}
+
+	return rc;
+}
+
+#define IMA_REQ_ACCESS		(IACS_SLCT | RIF_MEM_ACCESS_REQ)
+static int fg_interleaved_mem_config(struct fg_chip *chip, u8 *val,
+		u16 address, int len, int offset, int op)
+{
+	int rc = 0;
+	bool rif_mem_sts = true;
+	int time_count = 0;
+
+	while (1) {
+		rc = fg_check_rif_mem_access(chip, &rif_mem_sts);
+		if (rc)
+			return rc;
+
+		if (!rif_mem_sts)
+			break;
+
+		if (fg_debug_mask & (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+			pr_info("RIF_MEM_ACCESS_REQ is not clear yet for IMA_%s\n",
+				op ? "write" : "read");
+
+		/*
+		 * Try this no more than 4 times. If RIF_MEM_ACCESS_REQ is not
+		 * 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");
+			return -ETIMEDOUT;
+		}
+
+		/* Wait for 4ms before reading RIF_MEM_ACCESS_REQ again */
+		usleep_range(4000, 4100);
+		time_count++;
+	}
+
+	/* configure for IMA access */
+	rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+				IMA_REQ_ACCESS, IMA_REQ_ACCESS, 1);
+	if (rc) {
+		pr_err("failed to set mem access bit rc = %d\n", rc);
+		return rc;
+	}
+
+	/* configure for the read/write single/burst mode */
+	rc = fg_config_access(chip, op, (offset + len) > 4);
+	if (rc) {
+		pr_err("failed to set configure memory access rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = fg_check_iacs_ready(chip);
+	if (rc) {
+		pr_debug("IACS_RDY failed rc=%d\n", rc);
+		return rc;
+	}
+
+	/* write addresses to the register */
+	rc = fg_set_ram_addr(chip, &address);
+	if (rc) {
+		pr_err("failed to set SRAM address rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = fg_check_iacs_ready(chip);
+	if (rc)
+		pr_debug("IACS_RDY failed rc=%d\n", rc);
+
+	return rc;
+}
+
+#define MEM_INTF_FG_BEAT_COUNT		0x57
+#define BEAT_COUNT_MASK			0x0F
+#define RETRY_COUNT			3
+static int fg_interleaved_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+						int len, int offset)
+{
+	int rc = 0, orig_address = address;
+	u8 start_beat_count, end_beat_count, count = 0;
+	bool retry = false;
+
+	if (offset > 3) {
+		pr_err("offset too large %d\n", offset);
+		return -EINVAL;
+	}
+
+	fg_stay_awake(&chip->memif_wakeup_source);
+	address = ((orig_address + offset) / 4) * 4;
+	offset = (orig_address + offset) % 4;
+
+	if (address < RAM_OFFSET) {
+		/*
+		 * OTP memory reads need a conventional memory access, do a
+		 * conventional read when SRAM offset < RAM_OFFSET.
+		 */
+		rc = fg_conventional_mem_read(chip, val, address, len, offset,
+						0);
+		if (rc)
+			pr_err("Failed to read OTP memory %d\n", rc);
+		goto exit;
+	}
+
+	mutex_lock(&chip->rw_lock);
+
+retry:
+	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);
+		goto out;
+	}
+
+	/* read the start beat count */
+	rc = fg_read(chip, &start_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 out;
+	}
+
+	/* read data */
+	rc = __fg_interleaved_mem_read(chip, val, address, offset, len);
+	if (rc) {
+		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);
+			goto out;
+		}
+	}
+
+	/* read the end beat count */
+	rc = fg_read(chip, &end_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 out;
+	}
+
+	start_beat_count &= BEAT_COUNT_MASK;
+	end_beat_count &= BEAT_COUNT_MASK;
+	if (fg_debug_mask & FG_MEM_DEBUG_READS)
+		pr_info("Start beat_count = %x End beat_count = %x\n",
+				start_beat_count, end_beat_count);
+	if (start_beat_count != end_beat_count) {
+		if (fg_debug_mask & FG_MEM_DEBUG_READS)
+			pr_info("Beat count do not match - retry transaction\n");
+		retry = true;
+	}
+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);
+
+	if (retry) {
+		retry = false;
+		goto retry;
+	}
+	mutex_unlock(&chip->rw_lock);
+
+exit:
+	fg_relax(&chip->memif_wakeup_source);
+	return rc;
+}
+
+static int fg_interleaved_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+							int len, int offset)
+{
+	int rc = 0, orig_address = address;
+	u8 count = 0;
+
+	if (address < RAM_OFFSET)
+		return -EINVAL;
+
+	if (offset > 3) {
+		pr_err("offset too large %d\n", offset);
+		return -EINVAL;
+	}
+
+	fg_stay_awake(&chip->memif_wakeup_source);
+	address = ((orig_address + offset) / 4) * 4;
+	offset = (orig_address + offset) % 4;
+
+	mutex_lock(&chip->rw_lock);
+
+retry:
+	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);
+		goto out;
+	}
+
+	/* write data */
+	rc = __fg_interleaved_mem_write(chip, val, address, offset, len);
+	if (rc) {
+		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);
+			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);
+
+	mutex_unlock(&chip->rw_lock);
+	fg_relax(&chip->memif_wakeup_source);
+	return rc;
+}
+
+static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address,
+			int len, int offset, bool keep_access)
+{
+	if (chip->ima_supported)
+		return fg_interleaved_mem_read(chip, val, address,
+						len, offset);
+	else
+		return fg_conventional_mem_read(chip, val, address,
+					len, offset, keep_access);
+}
+
+static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address,
+		int len, int offset, bool keep_access)
+{
+	if (chip->ima_supported)
+		return fg_interleaved_mem_write(chip, val, address,
+						len, offset);
+	else
+		return fg_conventional_mem_write(chip, val, address,
+					len, offset, keep_access);
+}
+
+static int fg_mem_masked_write(struct fg_chip *chip, u16 addr,
+		u8 mask, u8 val, u8 offset)
+{
+	int rc = 0;
+	u8 reg[4];
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+
+	rc = fg_mem_read(chip, reg, addr, 4, 0, 1);
+	if (rc) {
+		pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+
+	reg[offset] &= ~mask;
+	reg[offset] |= val & mask;
+
+	str[0] = '\0';
+	fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4);
+	pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset);
+
+	rc = fg_mem_write(chip, reg, addr, 4, 0, 0);
+	if (rc) {
+		pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int soc_to_setpoint(int soc)
+{
+	return DIV_ROUND_CLOSEST(soc * 255, 100);
+}
+
+static void batt_to_setpoint_adc(int vbatt_mv, u8 *data)
+{
+	int val;
+	/* Battery voltage is an offset from 0 V and LSB is 1/2^15. */
+	val = DIV_ROUND_CLOSEST(vbatt_mv * 32768, 5000);
+	data[0] = val & 0xFF;
+	data[1] = val >> 8;
+}
+
+static u8 batt_to_setpoint_8b(int vbatt_mv)
+{
+	int val;
+	/* Battery voltage is an offset from 2.5 V and LSB is 5/2^9. */
+	val = (vbatt_mv - 2500) * 512 / 1000;
+	return DIV_ROUND_CLOSEST(val, 5);
+}
+
+static u8 therm_delay_to_setpoint(u32 delay_us)
+{
+	u8 val;
+
+	if (delay_us < 2560)
+		val = 0;
+	else if (delay_us > 163840)
+		val = 7;
+	else
+		val = ilog2(delay_us / 10) - 7;
+	return val << 5;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+	struct rtc_time tm;
+	struct rtc_device *rtc;
+	int rc;
+
+	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+	if (rtc == NULL) {
+		pr_err("%s: unable to open rtc device (%s)\n",
+			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+		return -EINVAL;
+	}
+
+	rc = rtc_read_time(rtc, &tm);
+	if (rc) {
+		pr_err("Error reading rtc device (%s) : %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto close_time;
+	}
+
+	rc = rtc_valid_tm(&tm);
+	if (rc) {
+		pr_err("Invalid RTC time (%s): %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto close_time;
+	}
+	rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+	rtc_class_close(rtc);
+	return rc;
+}
+
+#define BATTERY_SOC_REG		0x56C
+#define BATTERY_SOC_OFFSET	1
+#define FULL_PERCENT_3B		0xFFFFFF
+static int get_battery_soc_raw(struct fg_chip *chip)
+{
+	int rc;
+	u8 buffer[3];
+
+	rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
+	if (rc) {
+		pr_err("Unable to read battery soc: %d\n", rc);
+		return 0;
+	}
+	return (int)(buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
+}
+
+#define COUNTER_IMPTR_REG	0X558
+#define COUNTER_PULSE_REG	0X55C
+#define SOC_FULL_REG		0x564
+#define COUNTER_IMPTR_OFFSET	2
+#define COUNTER_PULSE_OFFSET	0
+#define SOC_FULL_OFFSET		3
+#define ESR_PULSE_RECONFIG_SOC	0xFFF971
+static int fg_configure_soc(struct fg_chip *chip)
+{
+	u32 batt_soc;
+	u8 cntr[2] = {0, 0};
+	int rc = 0;
+
+	mutex_lock(&chip->rw_lock);
+	atomic_add_return(1, &chip->memif_user_cnt);
+	mutex_unlock(&chip->rw_lock);
+
+	/* Read Battery SOC */
+	batt_soc = get_battery_soc_raw(chip);
+
+	if (batt_soc > ESR_PULSE_RECONFIG_SOC) {
+		if (fg_debug_mask & FG_POWER_SUPPLY)
+			pr_info("Configuring soc registers batt_soc: %x\n",
+				batt_soc);
+		batt_soc = ESR_PULSE_RECONFIG_SOC;
+		rc = fg_mem_write(chip, (u8 *)&batt_soc, BATTERY_SOC_REG, 3,
+				BATTERY_SOC_OFFSET, 1);
+		if (rc) {
+			pr_err("failed to write BATT_SOC rc=%d\n", rc);
+			goto out;
+		}
+
+		rc = fg_mem_write(chip, (u8 *)&batt_soc, SOC_FULL_REG, 3,
+				SOC_FULL_OFFSET, 1);
+		if (rc) {
+			pr_err("failed to write SOC_FULL rc=%d\n", rc);
+			goto out;
+		}
+
+		rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 2,
+				COUNTER_IMPTR_OFFSET, 1);
+		if (rc) {
+			pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+			goto out;
+		}
+
+		rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
+				COUNTER_PULSE_OFFSET, 0);
+		if (rc)
+			pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+	}
+out:
+	fg_release_access_if_necessary(chip);
+	return rc;
+}
+
+#define SOC_EMPTY	BIT(3)
+static bool fg_is_batt_empty(struct fg_chip *chip)
+{
+	u8 fg_soc_sts;
+	int rc;
+
+	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);
+		return false;
+	}
+
+	return (fg_soc_sts & SOC_EMPTY) != 0;
+}
+
+static int get_monotonic_soc_raw(struct fg_chip *chip)
+{
+	u8 cap[2];
+	int rc, tries = 0;
+
+	while (tries < MAX_TRIES_SOC) {
+		rc = fg_read(chip, cap,
+				chip->soc_base + SOC_MONOTONIC_SOC, 2);
+		if (rc) {
+			pr_err("spmi read failed: addr=%03x, rc=%d\n",
+				chip->soc_base + SOC_MONOTONIC_SOC, rc);
+			return rc;
+		}
+
+		if (cap[0] == cap[1])
+			break;
+
+		tries++;
+	}
+
+	if (tries == MAX_TRIES_SOC) {
+		pr_err("shadow registers do not match\n");
+		return -EINVAL;
+	}
+
+	if (fg_debug_mask & FG_POWER_SUPPLY)
+		pr_info_ratelimited("raw: 0x%02x\n", cap[0]);
+	return cap[0];
+}
+
+#define EMPTY_CAPACITY		0
+#define DEFAULT_CAPACITY	50
+#define MISSING_CAPACITY	100
+#define FULL_CAPACITY		100
+#define FULL_SOC_RAW		0xFF
+static int get_prop_capacity(struct fg_chip *chip)
+{
+	int msoc;
+
+	if (chip->battery_missing)
+		return MISSING_CAPACITY;
+	if (!chip->profile_loaded && !chip->use_otp_profile)
+		return DEFAULT_CAPACITY;
+	if (chip->charge_full)
+		return FULL_CAPACITY;
+	if (chip->soc_empty) {
+		if (fg_debug_mask & FG_POWER_SUPPLY)
+			pr_info_ratelimited("capacity: %d, EMPTY\n",
+					EMPTY_CAPACITY);
+		return EMPTY_CAPACITY;
+	}
+	msoc = get_monotonic_soc_raw(chip);
+	if (msoc == 0)
+		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;
+}
+
+#define HIGH_BIAS	3
+#define MED_BIAS	BIT(1)
+#define LOW_BIAS	BIT(0)
+static u8 bias_ua[] = {
+	[HIGH_BIAS] = 150,
+	[MED_BIAS] = 15,
+	[LOW_BIAS] = 5,
+};
+
+static int64_t get_batt_id(unsigned int battery_id_uv, u8 bid_info)
+{
+	u64 battery_id_ohm;
+
+	if ((bid_info & 0x3) == 0) {
+		pr_err("can't determine battery id 0x%02x\n", bid_info);
+		return -EINVAL;
+	}
+
+	battery_id_ohm = div_u64(battery_id_uv, bias_ua[bid_info & 0x3]);
+
+	return battery_id_ohm;
+}
+
+#define DEFAULT_TEMP_DEGC	250
+static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
+{
+	if (fg_debug_mask & FG_POWER_SUPPLY)
+		pr_info("addr 0x%02X, offset %d value %d\n",
+			fg_data[type].address, fg_data[type].offset,
+			fg_data[type].value);
+
+	if (type == FG_DATA_BATT_ID)
+		return get_batt_id(fg_data[type].value,
+				fg_data[FG_DATA_BATT_ID_INFO].value);
+
+	return fg_data[type].value;
+}
+
+#define MIN_TEMP_DEGC	-300
+#define MAX_TEMP_DEGC	970
+static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type)
+{
+	if (fg_debug_mask & FG_POWER_SUPPLY)
+		pr_info("addr 0x%02X, offset %d\n", settings[type].address,
+			settings[type].offset);
+
+	return settings[type].value;
+}
+
+static int set_prop_jeita_temp(struct fg_chip *chip,
+				unsigned int type, int decidegc)
+{
+	int rc = 0;
+
+	if (fg_debug_mask & FG_POWER_SUPPLY)
+		pr_info("addr 0x%02X, offset %d temp%d\n",
+			settings[type].address,
+			settings[type].offset, decidegc);
+
+	settings[type].value = decidegc;
+
+	cancel_delayed_work_sync(
+		&chip->update_jeita_setting);
+	schedule_delayed_work(
+		&chip->update_jeita_setting, 0);
+
+	return rc;
+}
+
+#define EXTERNAL_SENSE_SELECT		0x4AC
+#define EXTERNAL_SENSE_OFFSET		0x2
+#define EXTERNAL_SENSE_BIT		BIT(2)
+static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
+{
+	int rc;
+
+	rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+			EXTERNAL_SENSE_BIT,
+			ext_sense_type ? EXTERNAL_SENSE_BIT : 0,
+			EXTERNAL_SENSE_OFFSET);
+	if (rc) {
+		pr_err("failed to write profile rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+#define EXPONENT_MASK		0xF800
+#define MANTISSA_MASK		0x3FF
+#define SIGN			BIT(10)
+#define EXPONENT_SHIFT		11
+#define MICRO_UNIT		1000000ULL
+static int64_t float_decode(u16 reg)
+{
+	int64_t final_val, exponent_val, mantissa_val;
+	int exponent, mantissa, n;
+	bool sign;
+
+	exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
+	mantissa = (reg & MANTISSA_MASK);
+	sign = !!(reg & SIGN);
+
+	pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
+
+	mantissa_val = mantissa * MICRO_UNIT;
+
+	n = exponent - 15;
+	if (n < 0)
+		exponent_val = MICRO_UNIT >> -n;
+	else
+		exponent_val = MICRO_UNIT << n;
+
+	n = n - 10;
+	if (n < 0)
+		mantissa_val >>= -n;
+	else
+		mantissa_val <<= n;
+
+	final_val = exponent_val + mantissa_val;
+
+	if (sign)
+		final_val *= -1;
+
+	return final_val;
+}
+
+#define MIN_HALFFLOAT_EXP_N		-15
+#define MAX_HALFFLOAT_EXP_N		 16
+static int log2_floor(int64_t uval)
+{
+	int n = 0;
+	int64_t i = MICRO_UNIT;
+
+	if (uval > i) {
+		while (uval > i && n > MIN_HALFFLOAT_EXP_N) {
+			i <<= 1;
+			n += 1;
+		}
+		if (uval < i)
+			n -= 1;
+	} else if (uval < i) {
+		while (uval < i && n < MAX_HALFFLOAT_EXP_N) {
+			i >>= 1;
+			n -= 1;
+		}
+	}
+
+	return n;
+}
+
+static int64_t exp2_int(int64_t n)
+{
+	int p = n - 1;
+
+	if (p > 0)
+		return (2 * MICRO_UNIT) << p;
+	else
+		return (2 * MICRO_UNIT) >> abs(p);
+}
+
+static u16 float_encode(int64_t uval)
+{
+	int sign = 0, n, exp, mantissa;
+	u16 half = 0;
+
+	if (uval < 0) {
+		sign = 1;
+		uval = abs(uval);
+	}
+	n = log2_floor(uval);
+	exp = n + 15;
+	mantissa = div_s64(div_s64((uval - exp2_int(n)) * exp2_int(10 - n),
+				MICRO_UNIT) + MICRO_UNIT / 2, MICRO_UNIT);
+
+	half = (mantissa & MANTISSA_MASK) | ((sign << 10) & SIGN)
+		| ((exp << 11) & EXPONENT_MASK);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("uval = %lld, m = 0x%02x, sign = 0x%02x, exp = 0x%02x, half = 0x%04x\n",
+				uval, mantissa, sign, exp, half);
+	return half;
+}
+
+#define BATT_IDED	BIT(3)
+static int fg_is_batt_id_valid(struct fg_chip *chip)
+{
+	u8 fg_batt_sts;
+	int rc;
+
+	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 rc;
+	}
+
+	if (fg_debug_mask & FG_IRQS)
+		pr_info("fg batt sts 0x%x\n", fg_batt_sts);
+
+	return (fg_batt_sts & BATT_IDED) ? 1 : 0;
+}
+
+static int64_t twos_compliment_extend(int64_t val, int nbytes)
+{
+	int i;
+	int64_t mask;
+
+	mask = 0x80LL << ((nbytes - 1) * 8);
+	if (val & mask) {
+		for (i = 8; i > nbytes; i--) {
+			mask = 0xFFLL << ((i - 1) * 8);
+			val |= mask;
+		}
+	}
+
+	return val;
+}
+
+#define LSB_24B_NUMRTR		596046
+#define LSB_24B_DENMTR		1000000
+#define LSB_16B_NUMRTR		152587
+#define LSB_16B_DENMTR		1000
+#define LSB_8B		9800
+#define TEMP_LSB_16B	625
+#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)
+{
+	int i, j, rc = 0;
+	u8 reg[4];
+	int64_t temp;
+	int battid_valid = fg_is_batt_id_valid(chip);
+
+	fg_stay_awake(&chip->update_sram_wakeup_source);
+	if (chip->fg_restarting)
+		goto resched;
+
+	fg_mem_lock(chip);
+	for (i = 1; i < FG_DATA_MAX; i++) {
+		if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
+			continue;
+		rc = fg_mem_read(chip, reg, fg_data[i].address,
+			fg_data[i].len, fg_data[i].offset, 0);
+		if (rc) {
+			pr_err("Failed to update sram data\n");
+			break;
+		}
+
+		temp = 0;
+		for (j = 0; j < fg_data[i].len; j++)
+			temp |= reg[j] << (8 * j);
+
+		switch (i) {
+		case FG_DATA_OCV:
+		case FG_DATA_VOLTAGE:
+		case FG_DATA_CPRED_VOLTAGE:
+			fg_data[i].value = div_u64(
+					(u64)(u16)temp * LSB_16B_NUMRTR,
+					LSB_16B_DENMTR);
+			break;
+		case FG_DATA_CURRENT:
+			temp = twos_compliment_extend(temp, fg_data[i].len);
+			fg_data[i].value = div_s64(
+					(s64)temp * LSB_16B_NUMRTR,
+					LSB_16B_DENMTR);
+			break;
+		case FG_DATA_BATT_ESR:
+			fg_data[i].value = float_decode((u16) temp);
+			break;
+		case FG_DATA_BATT_ESR_COUNT:
+			fg_data[i].value = (u16)temp;
+			break;
+		case FG_DATA_BATT_ID:
+			if (battid_valid)
+				fg_data[i].value = reg[0] * LSB_8B;
+			break;
+		case FG_DATA_BATT_ID_INFO:
+			if (battid_valid)
+				fg_data[i].value = reg[0];
+			break;
+		case FG_DATA_BATT_SOC:
+			fg_data[i].value = div64_s64((temp * 10000),
+							FULL_PERCENT_3B);
+			break;
+		case FG_DATA_CC_CHARGE:
+			temp = twos_compliment_extend(temp, fg_data[i].len);
+			fg_data[i].value = div64_s64(
+					temp * (int64_t)chip->nom_cap_uah,
+					FULL_PERCENT_28BIT);
+			break;
+		case FG_DATA_VINT_ERR:
+			temp = twos_compliment_extend(temp, fg_data[i].len);
+			fg_data[i].value = div64_s64(temp * chip->nom_cap_uah,
+					FULL_PERCENT_3B);
+			break;
+		};
+
+		if (fg_debug_mask & FG_MEM_DEBUG_READS)
+			pr_info("%d %lld %d\n", i, temp, fg_data[i].value);
+	}
+	fg_mem_release(chip);
+
+	if (!rc)
+		get_current_time(&chip->last_sram_update_time);
+
+resched:
+	if (battid_valid) {
+		complete_all(&chip->batt_id_avail);
+		*resched_ms = fg_sram_update_period_ms;
+	} else {
+		*resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS;
+	}
+	fg_relax(&chip->update_sram_wakeup_source);
+}
+
+#define SRAM_TIMEOUT_MS			3000
+static void update_sram_data_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				update_sram_data.work);
+	int resched_ms = SRAM_PERIOD_NO_ID_UPDATE_MS, ret;
+	bool tried_again = false;
+
+wait:
+	/* Wait for MEMIF access revoked */
+	ret = wait_for_completion_interruptible_timeout(
+			&chip->sram_access_revoked,
+			msecs_to_jiffies(SRAM_TIMEOUT_MS));
+
+	/* If we were interrupted wait again one more time. */
+	if (ret == -ERESTARTSYS && !tried_again) {
+		tried_again = true;
+		goto wait;
+	} else if (ret <= 0) {
+		pr_err("transaction timed out ret=%d\n", ret);
+		goto out;
+	}
+	update_sram_data(chip, &resched_ms);
+
+out:
+	schedule_delayed_work(
+		&chip->update_sram_data,
+		msecs_to_jiffies(resched_ms));
+}
+
+#define BATT_TEMP_OFFSET	3
+#define BATT_TEMP_CNTRL_MASK	0x17
+#define DISABLE_THERM_BIT	BIT(0)
+#define TEMP_SENSE_ALWAYS_BIT	BIT(1)
+#define TEMP_SENSE_CHARGE_BIT	BIT(2)
+#define FORCE_RBIAS_ON_BIT	BIT(4)
+#define BATT_TEMP_OFF		DISABLE_THERM_BIT
+#define BATT_TEMP_ON		(FORCE_RBIAS_ON_BIT | TEMP_SENSE_ALWAYS_BIT | \
+				TEMP_SENSE_CHARGE_BIT)
+#define TEMP_PERIOD_UPDATE_MS		10000
+#define TEMP_PERIOD_TIMEOUT_MS		3000
+static void update_temp_data(struct work_struct *work)
+{
+	s16 temp;
+	u8 reg[2];
+	bool tried_again = false;
+	int rc, ret, timeout = TEMP_PERIOD_TIMEOUT_MS;
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				update_temp_work.work);
+
+	if (chip->fg_restarting)
+		goto resched;
+
+	fg_stay_awake(&chip->update_temp_wakeup_source);
+	if (chip->sw_rbias_ctrl) {
+		rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+				BATT_TEMP_CNTRL_MASK,
+				BATT_TEMP_ON,
+				BATT_TEMP_OFFSET);
+		if (rc) {
+			pr_err("failed to write BATT_TEMP_ON rc=%d\n", rc);
+			goto out;
+		}
+
+wait:
+		/* Wait for MEMIF access revoked */
+		ret = wait_for_completion_interruptible_timeout(
+				&chip->sram_access_revoked,
+				msecs_to_jiffies(timeout));
+
+		/* If we were interrupted wait again one more time. */
+		if (ret == -ERESTARTSYS && !tried_again) {
+			tried_again = true;
+			goto wait;
+		} else if (ret <= 0) {
+			rc = -ETIMEDOUT;
+			pr_err("transaction timed out ret=%d\n", ret);
+			goto out;
+		}
+	}
+
+	/* Read FG_DATA_BATT_TEMP now */
+	rc = fg_mem_read(chip, reg, fg_data[0].address,
+		fg_data[0].len, fg_data[0].offset,
+		chip->sw_rbias_ctrl ? 1 : 0);
+	if (rc) {
+		pr_err("Failed to update temp data\n");
+		goto out;
+	}
+
+	temp = reg[0] | (reg[1] << 8);
+	fg_data[0].value = (temp * TEMP_LSB_16B / 1000)
+		- DECIKELVIN;
+
+	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);
+
+out:
+	if (chip->sw_rbias_ctrl) {
+		rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+				BATT_TEMP_CNTRL_MASK,
+				BATT_TEMP_OFF,
+				BATT_TEMP_OFFSET);
+		if (rc)
+			pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
+	}
+	fg_relax(&chip->update_temp_wakeup_source);
+
+resched:
+	schedule_delayed_work(
+		&chip->update_temp_work,
+		msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
+}
+
+static void update_jeita_setting(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				update_jeita_setting.work);
+	u8 reg[4];
+	int i, rc;
+
+	for (i = 0; i < 4; i++)
+		reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30;
+
+	rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address,
+			4, settings[FG_MEM_SOFT_COLD].offset, 0);
+	if (rc)
+		pr_err("failed to update JEITA setting rc=%d\n", rc);
+}
+
+static int fg_set_resume_soc(struct fg_chip *chip, u8 threshold)
+{
+	u16 address;
+	int offset, rc;
+
+	address = settings[FG_MEM_RESUME_SOC].address;
+	offset = settings[FG_MEM_RESUME_SOC].offset;
+
+	rc = fg_mem_masked_write(chip, address, 0xFF, threshold, offset);
+
+	if (rc)
+		pr_err("write failed rc=%d\n", rc);
+	else
+		pr_debug("setting resume-soc to %x\n", threshold);
+
+	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)
+{
+	int rc = 0, i, address;
+	u8 data[2];
+
+	fg_mem_lock(chip);
+	for (i = 0; i < BUCKET_COUNT; i++) {
+		address = BATT_CYCLE_NUMBER_REG + i * 2;
+		rc = fg_mem_read(chip, (u8 *)&data, address, 2,
+				BATT_CYCLE_OFFSET, 0);
+		if (rc)
+			pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
+				i, rc);
+		else
+			chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
+	}
+	fg_mem_release(chip);
+}
+
+static void clear_cycle_counter(struct fg_chip *chip)
+{
+	int rc = 0, len, i;
+
+	if (!chip->cyc_ctr.en)
+		return;
+
+	len = sizeof(chip->cyc_ctr.count);
+	memset(chip->cyc_ctr.count, 0, len);
+	for (i = 0; i < BUCKET_COUNT; i++) {
+		chip->cyc_ctr.started[i] = false;
+		chip->cyc_ctr.last_soc[i] = 0;
+	}
+	rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
+			BATT_CYCLE_NUMBER_REG, len,
+			BATT_CYCLE_OFFSET, 0);
+	if (rc)
+		pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
+}
+
+static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
+{
+	int rc = 0, address;
+	u16 cyc_count;
+	u8 data[2];
+
+	if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
+		return 0;
+
+	cyc_count = chip->cyc_ctr.count[bucket];
+	cyc_count++;
+	data[0] = cyc_count & 0xFF;
+	data[1] = cyc_count >> 8;
+
+	address = BATT_CYCLE_NUMBER_REG + bucket * 2;
+
+	rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
+	if (rc)
+		pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
+			bucket, rc);
+	else
+		chip->cyc_ctr.count[bucket] = cyc_count;
+	return rc;
+}
+
+static void update_cycle_count(struct work_struct *work)
+{
+	int rc = 0, bucket, i;
+	u8 reg[3], batt_soc;
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				cycle_count_work);
+
+	mutex_lock(&chip->cyc_ctr.lock);
+	rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
+			BATTERY_SOC_OFFSET, 0);
+	if (rc) {
+		pr_err("Failed to read battery soc rc: %d\n", rc);
+		goto out;
+	}
+	batt_soc = reg[2];
+
+	if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+		/* Find out which bucket the SOC falls in */
+		bucket = batt_soc / BUCKET_SOC_PCT;
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);
+
+		/*
+		 * If we've started counting for the previous bucket,
+		 * then store the counter for that bucket if the
+		 * counter for current bucket is getting started.
+		 */
+		if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
+			!chip->cyc_ctr.started[bucket]) {
+			rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
+			if (rc) {
+				pr_err("Error in storing cycle_ctr rc: %d\n",
+					rc);
+				goto out;
+			} else {
+				chip->cyc_ctr.started[bucket - 1] = false;
+				chip->cyc_ctr.last_soc[bucket - 1] = 0;
+			}
+		}
+		if (!chip->cyc_ctr.started[bucket]) {
+			chip->cyc_ctr.started[bucket] = true;
+			chip->cyc_ctr.last_soc[bucket] = batt_soc;
+		}
+	} else {
+		for (i = 0; i < BUCKET_COUNT; i++) {
+			if (chip->cyc_ctr.started[i] &&
+				batt_soc > chip->cyc_ctr.last_soc[i]) {
+				rc = fg_inc_store_cycle_ctr(chip, i);
+				if (rc)
+					pr_err("Error in storing cycle_ctr rc: %d\n",
+						rc);
+				chip->cyc_ctr.last_soc[i] = 0;
+			}
+			chip->cyc_ctr.started[i] = false;
+		}
+	}
+out:
+	mutex_unlock(&chip->cyc_ctr.lock);
+}
+
+static int fg_get_cycle_count(struct fg_chip *chip)
+{
+	int count;
+
+	if (!chip->cyc_ctr.en)
+		return 0;
+
+	if ((chip->cyc_ctr.id <= 0) || (chip->cyc_ctr.id > BUCKET_COUNT))
+		return -EINVAL;
+
+	mutex_lock(&chip->cyc_ctr.lock);
+	count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
+	mutex_unlock(&chip->cyc_ctr.lock);
+	return count;
+}
+
+static void half_float_to_buffer(int64_t uval, u8 *buffer)
+{
+	u16 raw;
+
+	raw = float_encode(uval);
+	buffer[0] = (u8)(raw & 0xFF);
+	buffer[1] = (u8)((raw >> 8) & 0xFF);
+}
+
+static int64_t half_float(u8 *buffer)
+{
+	u16 val;
+
+	val = buffer[1] << 8 | buffer[0];
+	return float_decode(val);
+}
+
+static int voltage_2b(u8 *buffer)
+{
+	u16 val;
+
+	val = buffer[1] << 8 | buffer[0];
+	/* the range of voltage 2b is [-5V, 5V], so it will fit in an int */
+	return (int)div_u64(((u64)val) * LSB_16B_NUMRTR, LSB_16B_DENMTR);
+}
+
+static int bcap_uah_2b(u8 *buffer)
+{
+	u16 val;
+
+	val = buffer[1] << 8 | buffer[0];
+	return ((int)val) * 1000;
+}
+
+static int lookup_ocv_for_soc(struct fg_chip *chip, int soc)
+{
+	int64_t *coeffs;
+
+	if (soc > chip->ocv_junction_p1p2 * 10)
+		coeffs = chip->ocv_coeffs;
+	else if (soc > chip->ocv_junction_p2p3 * 10)
+		coeffs = chip->ocv_coeffs + 4;
+	else
+		coeffs = chip->ocv_coeffs + 8;
+	/* the range of ocv will fit in a 32 bit int */
+	return (int)(coeffs[0]
+		+ div_s64(coeffs[1] * soc, 1000LL)
+		+ div_s64(coeffs[2] * soc * soc, 1000000LL)
+		+ div_s64(coeffs[3] * soc * soc * soc, 1000000000LL));
+}
+
+static int lookup_soc_for_ocv(struct fg_chip *chip, int ocv)
+{
+	int64_t val;
+	int soc = -EINVAL;
+	/*
+	 * binary search variables representing the valid start and end
+	 * percentages to search
+	 */
+	int start = 0, end = 1000, mid;
+
+	if (fg_debug_mask & FG_AGING)
+		pr_info("target_ocv = %d\n", ocv);
+	/* do a binary search for the closest soc to match the ocv */
+	while (end - start > 1) {
+		mid = (start + end) / 2;
+		val = lookup_ocv_for_soc(chip, mid);
+		if (fg_debug_mask & FG_AGING)
+			pr_info("start = %d, mid = %d, end = %d, ocv = %lld\n",
+					start, mid, end, val);
+		if (ocv < val) {
+			end = mid;
+		} else if (ocv > val) {
+			start = mid;
+		} else {
+			soc = mid;
+			break;
+		}
+	}
+	/*
+	 * if the exact soc was not found and there are two or less values
+	 * remaining, just compare them and see which one is closest to the ocv
+	 */
+	if (soc == -EINVAL) {
+		if (abs(ocv - lookup_ocv_for_soc(chip, start))
+				> abs(ocv - lookup_ocv_for_soc(chip, end)))
+			soc = end;
+		else
+			soc = start;
+	}
+	if (fg_debug_mask & FG_AGING)
+		pr_info("closest = %d, target_ocv = %d, ocv_found = %d\n",
+				soc, ocv, lookup_ocv_for_soc(chip, soc));
+	return soc;
+}
+
+#define ESR_ACTUAL_REG		0x554
+#define BATTERY_ESR_REG		0x4F4
+#define TEMP_RS_TO_RSLOW_REG	0x514
+static int estimate_battery_age(struct fg_chip *chip, int *actual_capacity)
+{
+	int64_t ocv_cutoff_new, ocv_cutoff_aged, temp_rs_to_rslow;
+	int64_t esr_actual, battery_esr, val;
+	int soc_cutoff_aged, soc_cutoff_new, rc;
+	int battery_soc, unusable_soc, batt_temp;
+	u8 buffer[3];
+
+	if (chip->batt_aging_mode != FG_AGING_ESR)
+		return 0;
+
+	if (chip->nom_cap_uah == 0) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("ocv coefficients not loaded, aborting\n");
+		return 0;
+	}
+	fg_mem_lock(chip);
+
+	batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+	if (batt_temp < 150 || batt_temp > 400) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("Battery temp (%d) out of range, aborting\n",
+					(int)batt_temp);
+		rc = 0;
+		goto done;
+	}
+
+	battery_soc = get_battery_soc_raw(chip) * 100 / FULL_PERCENT_3B;
+	if (battery_soc < 25 || battery_soc > 75) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("Battery SoC (%d) out of range, aborting\n",
+					(int)battery_soc);
+		rc = 0;
+		goto done;
+	}
+
+	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);
+	battery_esr = half_float(buffer);
+
+	if (rc) {
+		goto error_done;
+	} else if (esr_actual < battery_esr) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("Batt ESR lower than ESR actual, aborting\n");
+		rc = 0;
+		goto done;
+	}
+	rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2, 0, 0);
+	temp_rs_to_rslow = half_float(buffer);
+
+	if (rc)
+		goto error_done;
+
+	fg_mem_release(chip);
+
+	if (fg_debug_mask & FG_AGING) {
+		pr_info("batt_soc = %d, cutoff_voltage = %lld, eval current = %d\n",
+				battery_soc, chip->cutoff_voltage,
+				chip->evaluation_current);
+		pr_info("temp_rs_to_rslow = %lld, batt_esr = %lld, esr_actual = %lld\n",
+				temp_rs_to_rslow, battery_esr, esr_actual);
+	}
+
+	/* calculate soc_cutoff_new */
+	val = (1000000LL + temp_rs_to_rslow) * battery_esr;
+	do_div(val, 1000000);
+	ocv_cutoff_new = div64_s64(chip->evaluation_current * val, 1000)
+		+ chip->cutoff_voltage;
+
+	/* calculate soc_cutoff_aged */
+	val = (1000000LL + temp_rs_to_rslow) * esr_actual;
+	do_div(val, 1000000);
+	ocv_cutoff_aged = div64_s64(chip->evaluation_current * val, 1000)
+		+ chip->cutoff_voltage;
+
+	if (fg_debug_mask & FG_AGING)
+		pr_info("ocv_cutoff_new = %lld, ocv_cutoff_aged = %lld\n",
+				ocv_cutoff_new, ocv_cutoff_aged);
+
+	soc_cutoff_new = lookup_soc_for_ocv(chip, ocv_cutoff_new);
+	soc_cutoff_aged = lookup_soc_for_ocv(chip, ocv_cutoff_aged);
+
+	if (fg_debug_mask & FG_AGING)
+		pr_info("aged soc = %d, new soc = %d\n",
+				soc_cutoff_aged, soc_cutoff_new);
+	unusable_soc = soc_cutoff_aged - soc_cutoff_new;
+
+	*actual_capacity = div64_s64(((int64_t)chip->nom_cap_uah)
+				* (1000 - unusable_soc), 1000);
+	if (fg_debug_mask & FG_AGING)
+		pr_info("nom cap = %d, actual cap = %d\n",
+				chip->nom_cap_uah, *actual_capacity);
+
+	return rc;
+
+error_done:
+	pr_err("some register reads failed: %d\n", rc);
+done:
+	fg_mem_release(chip);
+	return rc;
+}
+
+static void battery_age_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				battery_age_work);
+
+	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,
+	4410,
+	5880,
+	7350,
+	8820,
+	10290,
+	11760,
+	13230,
+	14700,
+	16170,
+	17640,
+	19110,
+	20580,
+	22050,
+	23520,
+	24990,
+	26460,
+	27930,
+	29400,
+	30870,
+	32340,
+	33810,
+	35280,
+	36750,
+	38220,
+	39690,
+	41160,
+	42630,
+	44100,
+	45570,
+	47040,
+};
+
+static int correction_factors[] = {
+	1000000,
+	1007874,
+	1015789,
+	1023745,
+	1031742,
+	1039780,
+	1047859,
+	1055979,
+	1064140,
+	1072342,
+	1080584,
+	1088868,
+	1097193,
+	1105558,
+	1113964,
+	1122411,
+	1130899,
+	1139427,
+	1147996,
+	1156606,
+	1165256,
+	1173947,
+	1182678,
+	1191450,
+	1200263,
+	1209115,
+	1218008,
+	1226942,
+	1235915,
+	1244929,
+	1253983,
+	1263076,
+};
+
+#define FG_CONVERSION_FACTOR	(64198531LL)
+static int iavg_3b_to_uah(u8 *buffer, int delta_ms)
+{
+	int64_t val, i_filtered;
+	int i, correction_factor;
+
+	for (i = 0; i < ARRAY_SIZE(correction_times); i++) {
+		if (correction_times[i] > delta_ms)
+			break;
+	}
+	if (i >= ARRAY_SIZE(correction_times)) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("fuel gauge took more than 32 cycles\n");
+		i = ARRAY_SIZE(correction_times) - 1;
+	}
+	correction_factor = correction_factors[i];
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("delta_ms = %d, cycles = %d, correction = %d\n",
+				delta_ms, i, correction_factor);
+	val = buffer[2] << 16 | buffer[1] << 8 | buffer[0];
+	/* convert val from signed 24b to signed 64b */
+	i_filtered = (val << 40) >> 40;
+	val = i_filtered * correction_factor;
+	val = div64_s64(val + FG_CONVERSION_FACTOR / 2, FG_CONVERSION_FACTOR);
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("i_filtered = 0x%llx/%lld, cc_uah = %lld\n",
+				i_filtered, i_filtered, val);
+
+	return val;
+}
+
+static bool fg_is_temperature_ok_for_learning(struct fg_chip *chip)
+{
+	int batt_temp = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
+
+	if (batt_temp > chip->learning_data.max_temp
+			|| batt_temp < chip->learning_data.min_temp) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("temp (%d) out of range [%d, %d], aborting\n",
+					batt_temp,
+					chip->learning_data.min_temp,
+					chip->learning_data.max_temp);
+		return false;
+	}
+	return true;
+}
+
+static void fg_cap_learning_stop(struct fg_chip *chip)
+{
+	chip->learning_data.cc_uah = 0;
+	chip->learning_data.active = false;
+}
+
+#define I_FILTERED_REG			0x584
+static void fg_cap_learning_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				fg_cap_learning_work);
+	u8 i_filtered[3], data[3];
+	int rc, cc_uah, delta_ms;
+	ktime_t now_kt, delta_kt;
+
+	mutex_lock(&chip->learning_data.learning_lock);
+	if (!chip->learning_data.active)
+		goto fail;
+	if (!fg_is_temperature_ok_for_learning(chip)) {
+		fg_cap_learning_stop(chip);
+		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;
+	}
+
+	fg_mem_lock(chip);
+
+	rc = fg_mem_read(chip, i_filtered, I_FILTERED_REG, 3, 0, 0);
+	if (rc) {
+		pr_err("Failed to read i_filtered: %d\n", rc);
+		fg_mem_release(chip);
+		goto fail;
+	}
+	memset(data, 0, 3);
+	rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
+	if (rc) {
+		pr_err("Failed to clear i_filtered: %d\n", rc);
+		fg_mem_release(chip);
+		goto fail;
+	}
+	fg_mem_release(chip);
+
+	now_kt = ktime_get_boottime();
+	delta_kt = ktime_sub(now_kt, chip->learning_data.time_stamp);
+	chip->learning_data.time_stamp = now_kt;
+
+	delta_ms = (int)div64_s64(ktime_to_ns(delta_kt), 1000000);
+
+	cc_uah = iavg_3b_to_uah(i_filtered, delta_ms);
+	chip->learning_data.cc_uah -= cc_uah;
+	if (fg_debug_mask & FG_AGING)
+		pr_info("total_cc_uah = %lld\n", chip->learning_data.cc_uah);
+
+fail:
+	mutex_unlock(&chip->learning_data.learning_lock);
+	return;
+
+}
+
+#define CC_SOC_BASE_REG		0x5BC
+#define CC_SOC_OFFSET		3
+#define CC_SOC_MAGNITUDE_MASK	0x1FFFFFFF
+#define CC_SOC_NEGATIVE_BIT	BIT(29)
+static int fg_get_cc_soc(struct fg_chip *chip, int *cc_soc)
+{
+	int rc;
+	u8 reg[4];
+	unsigned int temp, magnitude;
+
+	rc = fg_mem_read(chip, reg, CC_SOC_BASE_REG, 4, CC_SOC_OFFSET, 0);
+	if (rc) {
+		pr_err("Failed to read CC_SOC_REG rc=%d\n", rc);
+		return rc;
+	}
+
+	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;
+
+	return 0;
+}
+
+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;
+
+	if (!chip->learning_data.active)
+		goto fail;
+
+	if (!fg_is_temperature_ok_for_learning(chip)) {
+		fg_cap_learning_stop(chip);
+		goto fail;
+	}
+
+	rc = fg_get_cc_soc(chip, &cc_pc_val);
+	if (rc) {
+		pr_err("failed to get CC_SOC, stopping capacity learning\n");
+		fg_cap_learning_stop(chip);
+		goto fail;
+	}
+
+	cc_soc_delta_pc = DIV_ROUND_CLOSEST(
+			abs(cc_pc_val - chip->learning_data.init_cc_pc_val)
+			* 100, FULL_PERCENT_28BIT);
+
+	delta_cc_uah = div64_s64(
+			chip->learning_data.learned_cc_uah * cc_soc_delta_pc,
+			100);
+	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",
+				cc_pc_val, cc_soc_delta_pc,
+				chip->learning_data.cc_uah);
+
+	return 0;
+
+fail:
+	return rc;
+}
+
+#define FG_CAP_LEARNING_INTERVAL_NS	30000000000
+static enum alarmtimer_restart fg_cap_learning_alarm_cb(struct alarm *alarm,
+							ktime_t now)
+{
+	struct fg_chip *chip = container_of(alarm, struct fg_chip,
+					fg_cap_learning_alarm);
+
+	if (chip->learning_data.active) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("alarm fired\n");
+		schedule_work(&chip->fg_cap_learning_work);
+		alarm_forward_now(alarm,
+				ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
+		return ALARMTIMER_RESTART;
+	}
+	if (fg_debug_mask & FG_AGING)
+		pr_info("alarm misfired\n");
+	return ALARMTIMER_NORESTART;
+}
+
+#define FG_AGING_STORAGE_REG		0x5E4
+#define ACTUAL_CAPACITY_REG		0x578
+#define MAH_TO_SOC_CONV_REG		0x4A0
+#define CC_SOC_COEFF_OFFSET		0
+#define ACTUAL_CAPACITY_OFFSET		2
+#define MAH_TO_SOC_CONV_CS_OFFSET	0
+static int fg_calc_and_store_cc_soc_coeff(struct fg_chip *chip, int16_t cc_mah)
+{
+	int rc;
+	int64_t cc_to_soc_coeff, mah_to_soc;
+	u8 data[2];
+
+	rc = fg_mem_write(chip, (u8 *)&cc_mah, ACTUAL_CAPACITY_REG, 2,
+			ACTUAL_CAPACITY_OFFSET, 0);
+	if (rc) {
+		pr_err("Failed to store actual capacity: %d\n", rc);
+		return rc;
+	}
+
+	rc = fg_mem_read(chip, (u8 *)&data, MAH_TO_SOC_CONV_REG, 2,
+			MAH_TO_SOC_CONV_CS_OFFSET, 0);
+	if (rc) {
+		pr_err("Failed to read mah_to_soc_conv_cs: %d\n", rc);
+	} else {
+		mah_to_soc = data[1] << 8 | data[0];
+		mah_to_soc *= MICRO_UNIT;
+		cc_to_soc_coeff = div64_s64(mah_to_soc, cc_mah);
+		half_float_to_buffer(cc_to_soc_coeff, data);
+		rc = fg_mem_write(chip, (u8 *)data,
+				ACTUAL_CAPACITY_REG, 2,
+				CC_SOC_COEFF_OFFSET, 0);
+		if (rc)
+			pr_err("Failed to write cc_soc_coeff_offset: %d\n",
+				rc);
+		else if (fg_debug_mask & FG_AGING)
+			pr_info("new cc_soc_coeff %lld [%x %x] saved to sram\n",
+				cc_to_soc_coeff, data[0], data[1]);
+	}
+	return rc;
+}
+
+static void fg_cap_learning_load_data(struct fg_chip *chip)
+{
+	int16_t cc_mah;
+	int64_t old_cap = chip->learning_data.learned_cc_uah;
+	int rc;
+
+	rc = fg_mem_read(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
+	if (rc) {
+		pr_err("Failed to load aged capacity: %d\n", rc);
+	} else {
+		chip->learning_data.learned_cc_uah = cc_mah * 1000;
+		if (fg_debug_mask & FG_AGING)
+			pr_info("learned capacity %lld-> %lld/%x uah\n",
+					old_cap,
+					chip->learning_data.learned_cc_uah,
+					cc_mah);
+	}
+}
+
+static void fg_cap_learning_save_data(struct fg_chip *chip)
+{
+	int16_t cc_mah;
+	int rc;
+
+	cc_mah = div64_s64(chip->learning_data.learned_cc_uah, 1000);
+
+	rc = fg_mem_write(chip, (u8 *)&cc_mah, FG_AGING_STORAGE_REG, 2, 0, 0);
+	if (rc)
+		pr_err("Failed to store aged capacity: %d\n", rc);
+	else if (fg_debug_mask & FG_AGING)
+		pr_info("learned capacity %lld uah (%d/0x%x uah) saved to sram\n",
+				chip->learning_data.learned_cc_uah,
+				cc_mah, cc_mah);
+
+	if (chip->learning_data.feedback_on) {
+		rc = fg_calc_and_store_cc_soc_coeff(chip, cc_mah);
+		if (rc)
+			pr_err("Error in storing cc_soc_coeff, rc:%d\n", rc);
+	}
+}
+
+static void fg_cap_learning_post_process(struct fg_chip *chip)
+{
+	int64_t max_inc_val, min_dec_val, old_cap;
+
+	max_inc_val = chip->learning_data.learned_cc_uah
+			* (1000 + chip->learning_data.max_increment);
+	do_div(max_inc_val, 1000);
+
+	min_dec_val = chip->learning_data.learned_cc_uah
+			* (1000 - chip->learning_data.max_decrement);
+	do_div(min_dec_val, 1000);
+
+	old_cap = chip->learning_data.learned_cc_uah;
+	if (chip->learning_data.cc_uah > max_inc_val)
+		chip->learning_data.learned_cc_uah = max_inc_val;
+	else if (chip->learning_data.cc_uah < min_dec_val)
+		chip->learning_data.learned_cc_uah = min_dec_val;
+	else
+		chip->learning_data.learned_cc_uah =
+			chip->learning_data.cc_uah;
+
+	fg_cap_learning_save_data(chip);
+	if (fg_debug_mask & FG_AGING)
+		pr_info("final cc_uah = %lld, learned capacity %lld -> %lld uah\n",
+				chip->learning_data.cc_uah,
+				old_cap, chip->learning_data.learned_cc_uah);
+}
+
+static int get_vbat_est_diff(struct fg_chip *chip)
+{
+	return abs(fg_data[FG_DATA_VOLTAGE].value
+				- fg_data[FG_DATA_CPRED_VOLTAGE].value);
+}
+
+#define CBITS_INPUT_FILTER_REG		0x4B4
+#define IBATTF_TAU_MASK			0x38
+#define IBATTF_TAU_99_S			0x30
+static int fg_cap_learning_check(struct fg_chip *chip)
+{
+	u8 data[4];
+	int rc = 0, battery_soc, cc_pc_val;
+	int vbat_est_diff, vbat_est_thr_uv;
+	unsigned int cc_pc_100 = FULL_PERCENT_28BIT;
+
+	mutex_lock(&chip->learning_data.learning_lock);
+	if (chip->status == POWER_SUPPLY_STATUS_CHARGING
+				&& !chip->learning_data.active
+				&& chip->batt_aging_mode == FG_AGING_CC) {
+		if (chip->learning_data.learned_cc_uah == 0) {
+			if (fg_debug_mask & FG_AGING)
+				pr_info("no capacity, aborting\n");
+			goto fail;
+		}
+
+		if (!fg_is_temperature_ok_for_learning(chip))
+			goto fail;
+
+		fg_mem_lock(chip);
+		if (!chip->learning_data.feedback_on) {
+			vbat_est_diff = get_vbat_est_diff(chip);
+			vbat_est_thr_uv = chip->learning_data.vbat_est_thr_uv;
+			if (vbat_est_diff >= vbat_est_thr_uv &&
+					vbat_est_thr_uv > 0) {
+				if (fg_debug_mask & FG_AGING)
+					pr_info("vbat_est_diff (%d) < threshold (%d)\n",
+						vbat_est_diff, vbat_est_thr_uv);
+				fg_mem_release(chip);
+				fg_cap_learning_stop(chip);
+				goto fail;
+			}
+		}
+		battery_soc = get_battery_soc_raw(chip);
+		if (fg_debug_mask & FG_AGING)
+			pr_info("checking battery soc (%d vs %d)\n",
+				battery_soc * 100 / FULL_PERCENT_3B,
+				chip->learning_data.max_start_soc);
+		/* check if the battery is low enough to start soc learning */
+		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",
+					battery_soc * 100 / FULL_PERCENT_3B,
+					chip->learning_data.max_start_soc);
+			fg_mem_release(chip);
+			fg_cap_learning_stop(chip);
+			goto fail;
+		}
+
+		/* set the coulomb counter to a percentage of the capacity */
+		chip->learning_data.cc_uah = div64_s64(
+			(chip->learning_data.learned_cc_uah * battery_soc),
+				FULL_PERCENT_3B);
+
+		/* Use CC_SOC_REG based capacity learning */
+		if (chip->wa_flag & USE_CC_SOC_REG) {
+			fg_mem_release(chip);
+			/* SW_CC_SOC based capacity learning */
+			if (fg_get_cc_soc(chip, &cc_pc_val)) {
+				pr_err("failed to get CC_SOC, stop capacity learning\n");
+				fg_cap_learning_stop(chip);
+				goto fail;
+			}
+
+			chip->learning_data.init_cc_pc_val = cc_pc_val;
+			chip->learning_data.active = true;
+			if (fg_debug_mask & FG_AGING)
+				pr_info("SW_CC_SOC based learning init_CC_SOC=%d\n",
+					chip->learning_data.init_cc_pc_val);
+		} else {
+			rc = fg_mem_masked_write(chip, CBITS_INPUT_FILTER_REG,
+					IBATTF_TAU_MASK, IBATTF_TAU_99_S, 0);
+			if (rc) {
+				pr_err("Failed to write IF IBAT Tau: %d\n",
+								rc);
+				fg_mem_release(chip);
+				fg_cap_learning_stop(chip);
+				goto fail;
+			}
+
+			/* clear the i_filtered register */
+			memset(data, 0, 4);
+			rc = fg_mem_write(chip, data, I_FILTERED_REG, 3, 0, 0);
+			if (rc) {
+				pr_err("Failed to clear i_filtered: %d\n", rc);
+				fg_mem_release(chip);
+				fg_cap_learning_stop(chip);
+				goto fail;
+			}
+			fg_mem_release(chip);
+			chip->learning_data.time_stamp = ktime_get_boottime();
+			chip->learning_data.active = true;
+
+			if (fg_debug_mask & FG_AGING)
+				pr_info("cap learning started, soc = %d cc_uah = %lld\n",
+					battery_soc * 100 / FULL_PERCENT_3B,
+					chip->learning_data.cc_uah);
+			alarm_start_relative(&chip->fg_cap_learning_alarm,
+				ns_to_ktime(FG_CAP_LEARNING_INTERVAL_NS));
+		}
+	} else if ((chip->status != POWER_SUPPLY_STATUS_CHARGING)
+				&& chip->learning_data.active) {
+		if (fg_debug_mask & FG_AGING)
+			pr_info("capacity learning stopped\n");
+		if (!(chip->wa_flag & USE_CC_SOC_REG))
+			alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
+
+		if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+			if (chip->wa_flag & USE_CC_SOC_REG) {
+				rc = fg_cap_learning_process_full_data(chip);
+				if (rc) {
+					fg_cap_learning_stop(chip);
+					goto fail;
+				}
+				/* reset SW_CC_SOC register to 100% */
+				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);
+			}
+			fg_cap_learning_post_process(chip);
+		}
+
+		fg_cap_learning_stop(chip);
+	}
+
+fail:
+	mutex_unlock(&chip->learning_data.learning_lock);
+	return rc;
+}
+
+static bool is_usb_present(struct fg_chip *chip)
+{
+	union power_supply_propval prop = {0,};
+
+	if (!chip->usb_psy)
+		chip->usb_psy = power_supply_get_by_name("usb");
+
+	if (chip->usb_psy)
+		power_supply_get_property(chip->usb_psy,
+				POWER_SUPPLY_PROP_PRESENT, &prop);
+	return prop.intval != 0;
+}
+
+static bool is_dc_present(struct fg_chip *chip)
+{
+	union power_supply_propval prop = {0,};
+
+	if (!chip->dc_psy)
+		chip->dc_psy = power_supply_get_by_name("dc");
+
+	if (chip->dc_psy)
+		power_supply_get_property(chip->dc_psy,
+				POWER_SUPPLY_PROP_PRESENT, &prop);
+	return prop.intval != 0;
+}
+
+static bool is_input_present(struct fg_chip *chip)
+{
+	return is_usb_present(chip) || is_dc_present(chip);
+}
+
+static bool is_otg_present(struct fg_chip *chip)
+{
+	union power_supply_propval prop = {0,};
+
+	if (!chip->usb_psy)
+		chip->usb_psy = power_supply_get_by_name("usb");
+
+	if (chip->usb_psy)
+		power_supply_get_property(chip->usb_psy,
+				POWER_SUPPLY_PROP_USB_OTG, &prop);
+	return prop.intval != 0;
+}
+
+static bool is_charger_available(struct fg_chip *chip)
+{
+	if (!chip->batt_psy_name)
+		return false;
+
+	if (!chip->batt_psy)
+		chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
+
+	if (!chip->batt_psy)
+		return false;
+
+	return true;
+}
+
+static int set_prop_enable_charging(struct fg_chip *chip, bool enable)
+{
+	int rc = 0;
+	union power_supply_propval ret = {enable, };
+
+	if (!is_charger_available(chip)) {
+		pr_err("Charger not available yet!\n");
+		return -EINVAL;
+	}
+
+	rc = power_supply_set_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
+			&ret);
+	if (rc) {
+		pr_err("couldn't configure batt chg %d\n", rc);
+		return rc;
+	}
+
+	chip->charging_disabled = !enable;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("%sabling charging\n", enable ? "en" : "dis");
+
+	return rc;
+}
+
+#define MAX_BATTERY_CC_SOC_CAPACITY		150
+static void status_change_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				status_change_work);
+	unsigned long current_time = 0;
+	int cc_soc, rc, capacity = get_prop_capacity(chip);
+
+	if (chip->esr_pulse_tune_en) {
+		fg_stay_awake(&chip->esr_extract_wakeup_source);
+		schedule_work(&chip->esr_extract_config_work);
+	}
+
+	if (chip->status == POWER_SUPPLY_STATUS_FULL) {
+		if (capacity >= 99 && chip->hold_soc_while_full
+				&& chip->health == POWER_SUPPLY_HEALTH_GOOD) {
+			if (fg_debug_mask & FG_STATUS)
+				pr_info("holding soc at 100\n");
+			chip->charge_full = true;
+		} else if (fg_debug_mask & FG_STATUS) {
+			pr_info("terminated charging at %d/0x%02x\n",
+					capacity, get_monotonic_soc_raw(chip));
+		}
+	}
+	if (chip->status == POWER_SUPPLY_STATUS_FULL ||
+			chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+		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->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) {
+			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_cap_learning_check(chip);
+	schedule_work(&chip->update_esr_work);
+
+	if (chip->wa_flag & USE_CC_SOC_REG) {
+		if (fg_get_cc_soc(chip, &cc_soc)) {
+			pr_err("failed to get CC_SOC\n");
+			return;
+		}
+	}
+
+	if (chip->prev_status != chip->status && chip->last_sram_update_time) {
+		get_current_time(&current_time);
+		/*
+		 * When charging status changes, update SRAM parameters if it
+		 * was not updated before 5 seconds from now.
+		 */
+		if (chip->last_sram_update_time + 5 < current_time) {
+			cancel_delayed_work(&chip->update_sram_data);
+			schedule_delayed_work(&chip->update_sram_data,
+				msecs_to_jiffies(0));
+		}
+		if (chip->cyc_ctr.en)
+			schedule_work(&chip->cycle_count_work);
+		if ((chip->wa_flag & USE_CC_SOC_REG) &&
+				chip->bad_batt_detection_en &&
+				chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+			chip->sw_cc_soc_data.init_sys_soc = capacity;
+			chip->sw_cc_soc_data.init_cc_soc = cc_soc;
+			if (fg_debug_mask & FG_STATUS)
+				pr_info(" Init_sys_soc %d init_cc_soc %d\n",
+					chip->sw_cc_soc_data.init_sys_soc,
+					chip->sw_cc_soc_data.init_cc_soc);
+		}
+	}
+	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);
+		chip->sw_cc_soc_data.full_capacity =
+			chip->sw_cc_soc_data.delta_soc +
+			chip->sw_cc_soc_data.init_sys_soc;
+		pr_info("Init_sys_soc %d init_cc_soc %d cc_soc %d delta_soc %d full_capacity %d\n",
+				chip->sw_cc_soc_data.init_sys_soc,
+				chip->sw_cc_soc_data.init_cc_soc, cc_soc,
+				chip->sw_cc_soc_data.delta_soc,
+				chip->sw_cc_soc_data.full_capacity);
+		/*
+		 * If sw_cc_soc capacity greater than 150, then it's a bad
+		 * battery. else, reset timer and restart charging.
+		 */
+		if (chip->sw_cc_soc_data.full_capacity >
+				MAX_BATTERY_CC_SOC_CAPACITY) {
+			pr_info("Battery possibly damaged, do not restart charging\n");
+		} else {
+			pr_info("Reset safety-timer and restart charging\n");
+			rc = set_prop_enable_charging(chip, false);
+			if (rc) {
+				pr_err("failed to disable charging %d\n", rc);
+				return;
+			}
+
+			chip->safety_timer_expired = false;
+			msleep(200);
+
+			rc = set_prop_enable_charging(chip, true);
+			if (rc) {
+				pr_err("failed to enable charging %d\n", rc);
+				return;
+			}
+		}
+	}
+}
+
+/*
+ * Check for change in the status of input or OTG and schedule
+ * IADC gain compensation work.
+ */
+static void check_gain_compensation(struct fg_chip *chip)
+{
+	bool input_present = is_input_present(chip);
+	bool otg_present = is_otg_present(chip);
+
+	if ((chip->wa_flag & IADC_GAIN_COMP_WA)
+		&& ((chip->input_present ^ input_present)
+			|| (chip->otg_present ^ otg_present))) {
+		fg_stay_awake(&chip->gain_comp_wakeup_source);
+		chip->input_present = input_present;
+		chip->otg_present = otg_present;
+		cancel_work_sync(&chip->gain_comp_work);
+		schedule_work(&chip->gain_comp_work);
+	}
+}
+
+static void fg_hysteresis_config(struct fg_chip *chip)
+{
+	int hard_hot = 0, hard_cold = 0;
+
+	hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
+	hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
+	if (chip->health == POWER_SUPPLY_HEALTH_OVERHEAT && !chip->batt_hot) {
+		/* turn down the hard hot threshold */
+		chip->batt_hot = true;
+		set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+			hard_hot - chip->hot_hysteresis);
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("hard hot hysteresis: old hot=%d, new hot=%d\n",
+				hard_hot, hard_hot - chip->hot_hysteresis);
+	} else if (chip->health == POWER_SUPPLY_HEALTH_COLD &&
+		!chip->batt_cold) {
+		/* turn up the hard cold threshold */
+		chip->batt_cold = true;
+		set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+			hard_cold + chip->cold_hysteresis);
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("hard cold hysteresis: old cold=%d, new cold=%d\n",
+				hard_cold, hard_cold + chip->hot_hysteresis);
+	} else if (chip->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
+		chip->batt_hot) {
+		/* restore the hard hot threshold */
+		set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+			hard_hot + chip->hot_hysteresis);
+		chip->batt_hot = !chip->batt_hot;
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("restore hard hot threshold: old hot=%d, new hot=%d\n",
+				hard_hot,
+				hard_hot + chip->hot_hysteresis);
+	} else if (chip->health != POWER_SUPPLY_HEALTH_COLD &&
+		chip->batt_cold) {
+		/* restore the hard cold threshold */
+		set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+			hard_cold - chip->cold_hysteresis);
+		chip->batt_cold = !chip->batt_cold;
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("restore hard cold threshold: old cold=%d, new cold=%d\n",
+				hard_cold,
+				hard_cold - chip->cold_hysteresis);
+	}
+}
+
+#define BATT_INFO_STS(base)	(base + 0x09)
+#define JEITA_HARD_HOT_RT_STS	BIT(6)
+#define JEITA_HARD_COLD_RT_STS	BIT(5)
+static int fg_init_batt_temp_state(struct fg_chip *chip)
+{
+	int rc = 0;
+	u8 batt_info_sts;
+	int hard_hot = 0, hard_cold = 0;
+
+	/*
+	 * read the batt_info_sts register to parse battery's
+	 * initial status and do hysteresis config accordingly.
+	 */
+	rc = fg_read(chip, &batt_info_sts,
+		BATT_INFO_STS(chip->batt_base), 1);
+	if (rc) {
+		pr_err("failed to read batt info sts, rc=%d\n", rc);
+		return rc;
+	}
+
+	hard_hot = get_prop_jeita_temp(chip, FG_MEM_HARD_HOT);
+	hard_cold = get_prop_jeita_temp(chip, FG_MEM_HARD_COLD);
+	chip->batt_hot =
+		(batt_info_sts & JEITA_HARD_HOT_RT_STS) ? true : false;
+	chip->batt_cold =
+		(batt_info_sts & JEITA_HARD_COLD_RT_STS) ? true : false;
+	if (chip->batt_hot || chip->batt_cold) {
+		if (chip->batt_hot) {
+			chip->health = POWER_SUPPLY_HEALTH_OVERHEAT;
+			set_prop_jeita_temp(chip, FG_MEM_HARD_HOT,
+				hard_hot - chip->hot_hysteresis);
+		} else {
+			chip->health = POWER_SUPPLY_HEALTH_COLD;
+			set_prop_jeita_temp(chip, FG_MEM_HARD_COLD,
+				hard_cold + chip->cold_hysteresis);
+		}
+	}
+
+	return rc;
+}
+
+static int fg_power_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct fg_chip *chip = power_supply_get_drvdata(psy);
+	int rc = 0, unused;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+		rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_COLD, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+		rc = set_prop_jeita_temp(chip, FG_MEM_SOFT_HOT, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_UPDATE_NOW:
+		if (val->intval)
+			update_sram_data(chip, &unused);
+		break;
+	case POWER_SUPPLY_PROP_STATUS:
+		chip->prev_status = chip->status;
+		chip->status = val->intval;
+		schedule_work(&chip->status_change_work);
+		check_gain_compensation(chip);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		chip->health = val->intval;
+		if (chip->health == POWER_SUPPLY_HEALTH_GOOD) {
+			fg_stay_awake(&chip->resume_soc_wakeup_source);
+			schedule_work(&chip->set_resume_soc_work);
+		}
+
+		if (chip->jeita_hysteresis_support)
+			fg_hysteresis_config(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_DONE:
+		chip->charge_done = val->intval;
+		if (!chip->resume_soc_lowered) {
+			fg_stay_awake(&chip->resume_soc_wakeup_source);
+			schedule_work(&chip->set_resume_soc_work);
+		}
+		break;
+	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+		if ((val->intval > 0) && (val->intval <= BUCKET_COUNT)) {
+			chip->cyc_ctr.id = val->intval;
+		} else {
+			pr_err("rejecting invalid cycle_count_id = %d\n",
+								val->intval);
+			rc = -EINVAL;
+		}
+		break;
+	case POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED:
+		chip->safety_timer_expired = val->intval;
+		schedule_work(&chip->status_change_work);
+		break;
+	case POWER_SUPPLY_PROP_HI_POWER:
+		if (chip->wa_flag & BCL_HI_POWER_FOR_CHGLED_WA) {
+			chip->bcl_lpm_disabled = !!val->intval;
+			schedule_work(&chip->bcl_hi_power_work);
+		}
+		break;
+	default:
+		return -EINVAL;
+	};
+
+	return rc;
+};
+
+static int fg_property_is_writeable(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_COOL_TEMP:
+	case POWER_SUPPLY_PROP_WARM_TEMP:
+	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+#define SRAM_DUMP_START		0x400
+#define SRAM_DUMP_LEN		0x200
+static void dump_sram(struct work_struct *work)
+{
+	int i, rc;
+	u8 *buffer, rt_sts;
+	char str[16];
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				dump_sram);
+
+	buffer = devm_kzalloc(chip->dev, SRAM_DUMP_LEN, GFP_KERNEL);
+	if (buffer == NULL)
+		return;
+
+	rc = fg_read(chip, &rt_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);
+	else
+		pr_info("soc rt_sts: 0x%x\n", rt_sts);
+
+	rc = fg_read(chip, &rt_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
+		pr_info("batt rt_sts: 0x%x\n", rt_sts);
+
+	rc = fg_read(chip, &rt_sts, INT_RT_STS(chip->mem_base), 1);
+	if (rc)
+		pr_err("spmi read failed: addr=%03X, rc=%d\n",
+				INT_RT_STS(chip->mem_base), rc);
+	else
+		pr_info("memif rt_sts: 0x%x\n", rt_sts);
+
+	rc = fg_mem_read(chip, buffer, SRAM_DUMP_START, SRAM_DUMP_LEN, 0, 0);
+	if (rc) {
+		pr_err("dump failed: rc = %d\n", rc);
+		return;
+	}
+
+	for (i = 0; i < SRAM_DUMP_LEN; i += 4) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buffer + i, 4);
+		pr_info("%03X %s\n", SRAM_DUMP_START + i, str);
+	}
+	devm_kfree(chip->dev, buffer);
+}
+
+#define MAXRSCHANGE_REG		0x434
+#define ESR_VALUE_OFFSET	1
+#define ESR_STRICT_VALUE	0x4120391F391F3019
+#define ESR_DEFAULT_VALUE	0x58CD4A6761C34A67
+static void update_esr_value(struct work_struct *work)
+{
+	union power_supply_propval prop = {0, };
+	u64 esr_value;
+	int rc = 0;
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				update_esr_work);
+
+	if (!is_charger_available(chip))
+		return;
+
+	power_supply_get_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+
+	if (!chip->esr_strict_filter) {
+		if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
+				chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
+			(chip->status == POWER_SUPPLY_STATUS_FULL)) {
+			esr_value = ESR_STRICT_VALUE;
+			rc = fg_mem_write(chip, (u8 *)&esr_value,
+					MAXRSCHANGE_REG, 8,
+					ESR_VALUE_OFFSET, 0);
+			if (rc)
+				pr_err("failed to write strict ESR value rc=%d\n",
+					rc);
+			else
+				chip->esr_strict_filter = true;
+		}
+	} else if ((prop.intval != POWER_SUPPLY_CHARGE_TYPE_TAPER &&
+				chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
+			(chip->status == POWER_SUPPLY_STATUS_DISCHARGING)) {
+		esr_value = ESR_DEFAULT_VALUE;
+		rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
+				ESR_VALUE_OFFSET, 0);
+		if (rc)
+			pr_err("failed to write default ESR value rc=%d\n", rc);
+		else
+			chip->esr_strict_filter = false;
+	}
+}
+
+#define TEMP_COUNTER_REG	0x580
+#define VBAT_FILTERED_OFFSET	1
+#define GAIN_REG		0x424
+#define GAIN_OFFSET		1
+#define K_VCOR_REG		0x484
+#define DEF_GAIN_OFFSET		2
+#define PICO_UNIT		0xE8D4A51000LL
+#define ATTO_UNIT		0xDE0B6B3A7640000LL
+#define VBAT_REF		3800000
+
+/*
+ * IADC Gain compensation steps:
+ * If Input/OTG absent:
+ *	- read VBAT_FILTERED, KVCOR, GAIN
+ *	- calculate the gain compensation using following formula:
+ *	  gain = (1 + gain) * (1 + kvcor * (vbat_filtered - 3800000)) - 1;
+ * else
+ *	- reset to the default gain compensation
+ */
+static void iadc_gain_comp_work(struct work_struct *work)
+{
+	u8 reg[4];
+	int rc;
+	uint64_t vbat_filtered;
+	int64_t gain, kvcor, temp, numerator;
+	struct fg_chip *chip = container_of(work, struct fg_chip,
+							gain_comp_work);
+	bool input_present = is_input_present(chip);
+	bool otg_present = is_otg_present(chip);
+
+	if (!chip->init_done)
+		goto done;
+
+	if (!input_present && !otg_present) {
+		/* read VBAT_FILTERED */
+		rc = fg_mem_read(chip, reg, TEMP_COUNTER_REG, 3,
+						VBAT_FILTERED_OFFSET, 0);
+		if (rc) {
+			pr_err("Failed to read VBAT: rc=%d\n", rc);
+			goto done;
+		}
+		temp = (reg[2] << 16) | (reg[1] << 8) | reg[0];
+		vbat_filtered = div_u64((u64)temp * LSB_24B_NUMRTR,
+						LSB_24B_DENMTR);
+
+		/* read K_VCOR */
+		rc = fg_mem_read(chip, reg, K_VCOR_REG, 2, 0, 0);
+		if (rc) {
+			pr_err("Failed to KVCOR rc=%d\n", rc);
+			goto done;
+		}
+		kvcor = half_float(reg);
+
+		/* calculate gain */
+		numerator = (MICRO_UNIT + chip->iadc_comp_data.dfl_gain)
+			* (PICO_UNIT + kvcor * (vbat_filtered - VBAT_REF))
+			- ATTO_UNIT;
+		gain = div64_s64(numerator, PICO_UNIT);
+
+		/* write back gain */
+		half_float_to_buffer(gain, reg);
+		rc = fg_mem_write(chip, reg, GAIN_REG, 2, GAIN_OFFSET, 0);
+		if (rc) {
+			pr_err("Failed to write gain reg rc=%d\n", rc);
+			goto done;
+		}
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("IADC gain update [%x %x]\n", reg[1], reg[0]);
+		chip->iadc_comp_data.gain_active = true;
+	} else {
+		/* reset gain register */
+		rc = fg_mem_write(chip, chip->iadc_comp_data.dfl_gain_reg,
+						GAIN_REG, 2, GAIN_OFFSET, 0);
+		if (rc) {
+			pr_err("unable to write gain comp: %d\n", rc);
+			goto done;
+		}
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("IADC gain reset [%x %x]\n",
+					chip->iadc_comp_data.dfl_gain_reg[1],
+					chip->iadc_comp_data.dfl_gain_reg[0]);
+		chip->iadc_comp_data.gain_active = false;
+	}
+
+done:
+	fg_relax(&chip->gain_comp_wakeup_source);
+}
+
+#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;
+}
+
+#define SOC_FIRST_EST_DONE	BIT(5)
+static bool is_first_est_done(struct fg_chip *chip)
+{
+	int rc;
+	u8 fg_soc_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);
+		return false;
+	}
+
+	return (fg_soc_sts & SOC_FIRST_EST_DONE) ? true : false;
+}
+
+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);
+			goto out;
+		}
+		if (!vbatt_low_sts && chip->vbat_low_irq_enabled) {
+			if (fg_debug_mask & FG_IRQS)
+				pr_info("disabling vbatt_low irq\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;
+		}
+	}
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+out:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
+{
+	struct fg_chip *chip = _chip;
+	bool batt_missing = is_battery_missing(chip);
+
+	if (batt_missing) {
+		chip->battery_missing = true;
+		chip->profile_loaded = false;
+		chip->batt_type = default_batt_type;
+		mutex_lock(&chip->cyc_ctr.lock);
+		if (fg_debug_mask & FG_IRQS)
+			pr_info("battery missing, clearing cycle counters\n");
+		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 {
+			chip->battery_missing = false;
+		}
+	}
+
+	if (fg_debug_mask & FG_IRQS)
+		pr_info("batt-missing triggered: %s\n",
+				batt_missing ? "missing" : "present");
+
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip)
+{
+	struct fg_chip *chip = _chip;
+	u8 mem_if_sts;
+	int rc;
+
+	rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1);
+	if (rc) {
+		pr_err("failed to read mem status rc=%d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	if (fg_check_sram_access(chip)) {
+		if ((fg_debug_mask & FG_IRQS)
+				& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+			pr_info("sram access granted\n");
+		reinit_completion(&chip->sram_access_revoked);
+		complete_all(&chip->sram_access_granted);
+	} else {
+		if ((fg_debug_mask & FG_IRQS)
+				& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+			pr_info("sram access revoked\n");
+		complete_all(&chip->sram_access_revoked);
+	}
+
+	if (!rc && (fg_debug_mask & FG_IRQS)
+			& (FG_MEM_DEBUG_READS | FG_MEM_DEBUG_WRITES))
+		pr_info("mem_if sts 0x%02x\n", mem_if_sts);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
+{
+	struct fg_chip *chip = _chip;
+	u8 soc_rt_sts;
+	int rc;
+
+	rc = fg_read(chip, &soc_rt_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);
+	}
+
+	if (fg_debug_mask & FG_IRQS)
+		pr_info("triggered 0x%x\n", soc_rt_sts);
+
+	schedule_work(&chip->battery_age_work);
+
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+
+	if (chip->rslow_comp.chg_rs_to_rslow > 0 &&
+			chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
+			chip->rslow_comp.chg_rslow_comp_c2 > 0)
+		schedule_work(&chip->rslow_comp_work);
+	if (chip->cyc_ctr.en)
+		schedule_work(&chip->cycle_count_work);
+	schedule_work(&chip->update_esr_work);
+	if (chip->charge_full)
+		schedule_work(&chip->charge_full_work);
+	if (chip->wa_flag & IADC_GAIN_COMP_WA
+			&& chip->iadc_comp_data.gain_active) {
+		fg_stay_awake(&chip->gain_comp_wakeup_source);
+		schedule_work(&chip->gain_comp_work);
+	}
+
+	if (chip->wa_flag & USE_CC_SOC_REG
+			&& chip->learning_data.active) {
+		fg_stay_awake(&chip->capacity_learning_wakeup_source);
+		schedule_work(&chip->fg_cap_learning_work);
+	}
+
+	if (chip->esr_pulse_tune_en) {
+		fg_stay_awake(&chip->esr_extract_wakeup_source);
+		schedule_work(&chip->esr_extract_config_work);
+	}
+
+	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;
+	u8 soc_rt_sts;
+	int rc;
+
+	rc = fg_read(chip, &soc_rt_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);
+		goto done;
+	}
+
+	if (fg_debug_mask & FG_IRQS)
+		pr_info("triggered 0x%x\n", soc_rt_sts);
+	if (fg_is_batt_empty(chip)) {
+		fg_stay_awake(&chip->empty_check_wakeup_source);
+		schedule_delayed_work(&chip->check_empty_work,
+			msecs_to_jiffies(FG_EMPTY_DEBOUNCE_MS));
+	} else {
+		chip->soc_empty = false;
+	}
+
+done:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip)
+{
+	struct fg_chip *chip = _chip;
+
+	if (fg_debug_mask & FG_IRQS)
+		pr_info("triggered\n");
+
+	if (fg_est_dump)
+		schedule_work(&chip->dump_sram);
+
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+
+	complete_all(&chip->first_soc_done);
+
+	return IRQ_HANDLED;
+}
+
+static void fg_external_power_changed(struct power_supply *psy)
+{
+	struct fg_chip *chip = power_supply_get_drvdata(psy);
+
+	if (is_input_present(chip) && chip->rslow_comp.active &&
+			chip->rslow_comp.chg_rs_to_rslow > 0 &&
+			chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
+			chip->rslow_comp.chg_rslow_comp_c2 > 0)
+		schedule_work(&chip->rslow_comp_work);
+	if (!is_input_present(chip) && chip->resume_soc_lowered) {
+		fg_stay_awake(&chip->resume_soc_wakeup_source);
+		schedule_work(&chip->set_resume_soc_work);
+	}
+	if (!is_input_present(chip) && chip->charge_full)
+		schedule_work(&chip->charge_full_work);
+}
+
+static void set_resume_soc_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				set_resume_soc_work);
+	int rc, resume_soc_raw;
+
+	if (is_input_present(chip) && !chip->resume_soc_lowered) {
+		if (!chip->charge_done)
+			goto done;
+		resume_soc_raw = get_monotonic_soc_raw(chip)
+			- (0xFF - settings[FG_MEM_RESUME_SOC].value);
+		if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
+			rc = fg_set_resume_soc(chip, resume_soc_raw);
+			if (rc) {
+				pr_err("Couldn't set resume SOC for FG\n");
+				goto done;
+			}
+			if (fg_debug_mask & FG_STATUS) {
+				pr_info("resume soc lowered to 0x%02x\n",
+						resume_soc_raw);
+			}
+		} else if (settings[FG_MEM_RESUME_SOC].value > 0) {
+			pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
+		}
+		chip->charge_done = false;
+		chip->resume_soc_lowered = true;
+	} else if (chip->resume_soc_lowered && (!is_input_present(chip)
+				|| chip->health == POWER_SUPPLY_HEALTH_GOOD)) {
+		resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
+		if (resume_soc_raw > 0 && resume_soc_raw < FULL_SOC_RAW) {
+			rc = fg_set_resume_soc(chip, resume_soc_raw);
+			if (rc) {
+				pr_err("Couldn't set resume SOC for FG\n");
+				goto done;
+			}
+			if (fg_debug_mask & FG_STATUS) {
+				pr_info("resume soc set to 0x%02x\n",
+						resume_soc_raw);
+			}
+		} else if (settings[FG_MEM_RESUME_SOC].value > 0) {
+			pr_err("bad resume soc 0x%02x\n", resume_soc_raw);
+		}
+		chip->resume_soc_lowered = false;
+	}
+done:
+	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 RSLOW_COMP_REG			0x528
+#define RSLOW_COMP_C1_OFFSET		0
+#define RSLOW_COMP_C2_OFFSET		2
+static int populate_system_data(struct fg_chip *chip)
+{
+	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);
+	if (rc) {
+		pr_err("Failed to read ocv coefficients: %d\n", rc);
+		goto done;
+	}
+	for (i = 0; i < 12; i += 1)
+		chip->ocv_coeffs[i] = half_float(buffer + (i * 2));
+	if (fg_debug_mask & FG_AGING) {
+		pr_info("coeffs1 = %lld %lld %lld %lld\n",
+				chip->ocv_coeffs[0], chip->ocv_coeffs[1],
+				chip->ocv_coeffs[2], chip->ocv_coeffs[3]);
+		pr_info("coeffs2 = %lld %lld %lld %lld\n",
+				chip->ocv_coeffs[4], chip->ocv_coeffs[5],
+				chip->ocv_coeffs[6], chip->ocv_coeffs[7]);
+		pr_info("coeffs3 = %lld %lld %lld %lld\n",
+				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;
+	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);
+	if (rc) {
+		pr_err("Failed to read nominal capacitance: %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);
+		goto done;
+	}
+	chip->cutoff_voltage = voltage_2b(buffer);
+	if (fg_debug_mask & FG_AGING)
+		pr_info("cutoff_voltage = %lld, nom_cap_uah = %d p1p2 = %d, p2p3 = %d\n",
+				chip->cutoff_voltage, chip->nom_cap_uah,
+				chip->ocv_junction_p1p2,
+				chip->ocv_junction_p2p3);
+
+	rc = fg_mem_read(chip, buffer, RSLOW_CFG_REG, 1, RSLOW_CFG_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rslow cfg: %d\n", rc);
+		goto done;
+	}
+	chip->rslow_comp.rslow_cfg = buffer[0];
+	rc = fg_mem_read(chip, buffer, RSLOW_THRESH_REG, 1,
+			RSLOW_THRESH_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rslow thresh: %d\n", rc);
+		goto done;
+	}
+	chip->rslow_comp.rslow_thr = buffer[0];
+	rc = fg_mem_read(chip, buffer, TEMP_RS_TO_RSLOW_REG, 2,
+			RSLOW_THRESH_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rs to rslow: %d\n", rc);
+		goto done;
+	}
+	memcpy(chip->rslow_comp.rs_to_rslow, buffer, 2);
+	rc = fg_mem_read(chip, buffer, RSLOW_COMP_REG, 4,
+			RSLOW_COMP_C1_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to read rslow comp: %d\n", rc);
+		goto done;
+	}
+	memcpy(chip->rslow_comp.rslow_comp, buffer, 4);
+
+done:
+	fg_mem_release(chip);
+	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
+static int fg_rslow_charge_comp_set(struct fg_chip *chip)
+{
+	int rc;
+	u8 buffer[2];
+
+	mutex_lock(&chip->rslow_comp.lock);
+	fg_mem_lock(chip);
+
+	rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+			RSLOW_CFG_MASK, RSLOW_CFG_ON_VAL, RSLOW_CFG_OFFSET);
+	if (rc) {
+		pr_err("unable to write rslow cfg: %d\n", rc);
+		goto done;
+	}
+	rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
+			0xFF, RSLOW_THRESH_FULL_VAL, RSLOW_THRESH_OFFSET);
+	if (rc) {
+		pr_err("unable to write rslow thresh: %d\n", rc);
+		goto done;
+	}
+
+	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);
+	if (rc) {
+		pr_err("unable to write rs to rslow: %d\n", rc);
+		goto done;
+	}
+	half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c1, buffer);
+	rc = fg_mem_write(chip, buffer,
+			RSLOW_COMP_REG, 2, RSLOW_COMP_C1_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rslow comp: %d\n", rc);
+		goto done;
+	}
+	half_float_to_buffer(chip->rslow_comp.chg_rslow_comp_c2, buffer);
+	rc = fg_mem_write(chip, buffer,
+			RSLOW_COMP_REG, 2, RSLOW_COMP_C2_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rslow comp: %d\n", rc);
+		goto done;
+	}
+	chip->rslow_comp.active = true;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Activated rslow charge comp values\n");
+
+done:
+	fg_mem_release(chip);
+	mutex_unlock(&chip->rslow_comp.lock);
+	return rc;
+}
+
+#define RSLOW_CFG_ORIG_MASK	(BIT(4) | BIT(5))
+static int fg_rslow_charge_comp_clear(struct fg_chip *chip)
+{
+	u8 reg;
+	int rc;
+
+	mutex_lock(&chip->rslow_comp.lock);
+	fg_mem_lock(chip);
+
+	reg = chip->rslow_comp.rslow_cfg & RSLOW_CFG_ORIG_MASK;
+	rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+			RSLOW_CFG_MASK, reg, RSLOW_CFG_OFFSET);
+	if (rc) {
+		pr_err("unable to write rslow cfg: %d\n", rc);
+		goto done;
+	}
+	rc = fg_mem_masked_write(chip, RSLOW_THRESH_REG,
+			0xFF, chip->rslow_comp.rslow_thr, RSLOW_THRESH_OFFSET);
+	if (rc) {
+		pr_err("unable to write rslow thresh: %d\n", rc);
+		goto done;
+	}
+
+	rc = fg_mem_write(chip, chip->rslow_comp.rs_to_rslow,
+			TEMP_RS_TO_RSLOW_REG, 2, TEMP_RS_TO_RSLOW_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rs to rslow: %d\n", rc);
+		goto done;
+	}
+	rc = fg_mem_write(chip, chip->rslow_comp.rslow_comp,
+			RSLOW_COMP_REG, 4, RSLOW_COMP_C1_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write rslow comp: %d\n", rc);
+		goto done;
+	}
+	chip->rslow_comp.active = false;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Cleared rslow charge comp values\n");
+
+done:
+	fg_mem_release(chip);
+	mutex_unlock(&chip->rslow_comp.lock);
+	return rc;
+}
+
+static void rslow_comp_work(struct work_struct *work)
+{
+	int battery_soc_1b;
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				rslow_comp_work);
+
+	battery_soc_1b = get_battery_soc_raw(chip) >> 16;
+	if (battery_soc_1b > chip->rslow_comp.chg_rslow_comp_thr
+			&& chip->status == POWER_SUPPLY_STATUS_CHARGING) {
+		if (!chip->rslow_comp.active)
+			fg_rslow_charge_comp_set(chip);
+	} else {
+		if (chip->rslow_comp.active)
+			fg_rslow_charge_comp_clear(chip);
+	}
+}
+
+#define MICROUNITS_TO_ADC_RAW(units)	\
+			div64_s64(units * LSB_16B_DENMTR, LSB_16B_NUMRTR)
+static int update_chg_iterm(struct fg_chip *chip)
+{
+	u8 data[2];
+	u16 converted_current_raw;
+	s64 current_ma = -settings[FG_MEM_CHG_TERM_CURRENT].value;
+
+	converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
+	data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
+	data[1] = cpu_to_le16(converted_current_raw) >> 8;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
+			current_ma, converted_current_raw, data[0], data[1]);
+	return fg_mem_write(chip, data,
+			settings[FG_MEM_CHG_TERM_CURRENT].address,
+			2, settings[FG_MEM_CHG_TERM_CURRENT].offset, 0);
+}
+
+#define CC_CV_SETPOINT_REG	0x4F8
+#define CC_CV_SETPOINT_OFFSET	0
+static void update_cc_cv_setpoint(struct fg_chip *chip)
+{
+	int rc;
+	u8 tmp[2];
+
+	if (!chip->cc_cv_threshold_mv)
+		return;
+	batt_to_setpoint_adc(chip->cc_cv_threshold_mv, tmp);
+	rc = fg_mem_write(chip, tmp, CC_CV_SETPOINT_REG, 2,
+				CC_CV_SETPOINT_OFFSET, 0);
+	if (rc) {
+		pr_err("failed to write CC_CV_VOLT rc=%d\n", rc);
+		return;
+	}
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("Wrote %x %x to address %x for CC_CV setpoint\n",
+			tmp[0], tmp[1], CC_CV_SETPOINT_REG);
+}
+
+#define CBITS_INPUT_FILTER_REG		0x4B4
+#define CBITS_RMEAS1_OFFSET		1
+#define CBITS_RMEAS2_OFFSET		2
+#define CBITS_RMEAS1_DEFAULT_VAL	0x65
+#define CBITS_RMEAS2_DEFAULT_VAL	0x65
+#define IMPTR_FAST_TIME_SHIFT		1
+#define IMPTR_LONG_TIME_SHIFT		(1 << 4)
+#define IMPTR_PULSE_CTR_CHG		1
+#define IMPTR_PULSE_CTR_DISCHG		(1 << 4)
+static int fg_config_imptr_pulse(struct fg_chip *chip, bool slow)
+{
+	int rc;
+	u8 cntr[2] = {0, 0};
+	u8 val;
+
+	if (slow == chip->imptr_pulse_slow_en) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("imptr_pulse_slow is %sabled already\n",
+				slow ? "en" : "dis");
+		return 0;
+	}
+
+	fg_mem_lock(chip);
+
+	val = slow ? (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT) :
+		CBITS_RMEAS1_DEFAULT_VAL;
+	rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+			CBITS_RMEAS1_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write cbits_rmeas1_offset rc=%d\n", rc);
+		goto done;
+	}
+
+	val = slow ? (IMPTR_PULSE_CTR_CHG | IMPTR_PULSE_CTR_DISCHG) :
+		CBITS_RMEAS2_DEFAULT_VAL;
+	rc = fg_mem_write(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+			CBITS_RMEAS2_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write cbits_rmeas2_offset rc=%d\n", rc);
+		goto done;
+	}
+
+	if (slow) {
+		rc = fg_mem_write(chip, cntr, COUNTER_IMPTR_REG, 4,
+				COUNTER_IMPTR_OFFSET, 0);
+		if (rc) {
+			pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+			goto done;
+		}
+
+		rc = fg_mem_write(chip, cntr, COUNTER_PULSE_REG, 2,
+				COUNTER_PULSE_OFFSET, 0);
+		if (rc) {
+			pr_err("failed to write COUNTER_IMPTR rc=%d\n", rc);
+			goto done;
+		}
+	}
+
+	chip->imptr_pulse_slow_en = slow;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("imptr_pulse_slow is %sabled\n", slow ? "en" : "dis");
+done:
+	fg_mem_release(chip);
+	return rc;
+}
+
+#define CURRENT_DELTA_MIN_REG		0x42C
+#define CURRENT_DELTA_MIN_OFFSET	1
+#define SYS_CFG_1_REG			0x4AC
+#define SYS_CFG_1_OFFSET		0
+#define CURRENT_DELTA_MIN_DEFAULT	0x16
+#define CURRENT_DELTA_MIN_500MA		0xCD
+#define RSLOW_CFG_USE_FIX_RSER_VAL	BIT(7)
+#define ENABLE_ESR_PULSE_VAL		BIT(3)
+static int fg_config_esr_extract(struct fg_chip *chip, bool disable)
+{
+	int rc;
+	u8 val;
+
+	if (disable == chip->esr_extract_disabled) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("ESR extract already %sabled\n",
+				disable ? "dis" : "en");
+		return 0;
+	}
+
+	fg_mem_lock(chip);
+
+	val = disable ? CURRENT_DELTA_MIN_500MA :
+				CURRENT_DELTA_MIN_DEFAULT;
+	rc = fg_mem_write(chip, &val, CURRENT_DELTA_MIN_REG, 1,
+			CURRENT_DELTA_MIN_OFFSET, 0);
+	if (rc) {
+		pr_err("unable to write curr_delta_min rc=%d\n", rc);
+		goto done;
+	}
+
+	val = disable ? RSLOW_CFG_USE_FIX_RSER_VAL : 0;
+	rc = fg_mem_masked_write(chip, RSLOW_CFG_REG,
+			RSLOW_CFG_USE_FIX_RSER_VAL, val, RSLOW_CFG_OFFSET);
+	if (rc) {
+		pr_err("unable to write rslow cfg rc= %d\n", rc);
+		goto done;
+	}
+
+	val = disable ? 0 : ENABLE_ESR_PULSE_VAL;
+	rc = fg_mem_masked_write(chip, SYS_CFG_1_REG,
+			ENABLE_ESR_PULSE_VAL, val, SYS_CFG_1_OFFSET);
+	if (rc) {
+		pr_err("unable to write sys_cfg_1 rc= %d\n", rc);
+		goto done;
+	}
+
+	chip->esr_extract_disabled = disable;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("ESR extract is %sabled\n", disable ? "dis" : "en");
+done:
+	fg_mem_release(chip);
+	return rc;
+}
+
+#define ESR_EXTRACT_STOP_SOC		2
+#define IMPTR_PULSE_CONFIG_SOC		5
+static void esr_extract_config_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work, struct fg_chip,
+						esr_extract_config_work);
+	bool input_present = is_input_present(chip);
+	int capacity = get_prop_capacity(chip);
+
+	if (input_present && capacity <= ESR_EXTRACT_STOP_SOC) {
+		fg_config_esr_extract(chip, true);
+	} else if (capacity > ESR_EXTRACT_STOP_SOC) {
+		fg_config_esr_extract(chip, false);
+
+		if (capacity <= IMPTR_PULSE_CONFIG_SOC)
+			fg_config_imptr_pulse(chip, true);
+		else
+			fg_config_imptr_pulse(chip, false);
+	}
+
+	fg_relax(&chip->esr_extract_wakeup_source);
+}
+
+#define LOW_LATENCY			BIT(6)
+#define BATT_PROFILE_OFFSET		0x4C0
+#define PROFILE_INTEGRITY_REG		0x53C
+#define PROFILE_INTEGRITY_BIT		BIT(0)
+#define FIRST_EST_DONE_BIT		BIT(5)
+#define MAX_TRIES_FIRST_EST		3
+#define FIRST_EST_WAIT_MS		2000
+#define PROFILE_LOAD_TIMEOUT_MS		5000
+static int fg_do_restart(struct fg_chip *chip, bool write_profile)
+{
+	int rc, ibat_ua;
+	u8 reg = 0;
+	u8 buf[2];
+	bool tried_once = false;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("restarting fuel gauge...\n");
+
+try_again:
+	if (write_profile) {
+		if (!chip->charging_disabled) {
+			pr_err("Charging not yet disabled!\n");
+			return -EINVAL;
+		}
+
+		ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT);
+		if (ibat_ua == -EINVAL) {
+			pr_err("SRAM not updated yet!\n");
+			return ibat_ua;
+		}
+
+		if (ibat_ua < 0) {
+			pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua);
+
+			if (!tried_once) {
+				cancel_delayed_work(&chip->update_sram_data);
+				schedule_delayed_work(&chip->update_sram_data,
+					msecs_to_jiffies(0));
+				msleep(1000);
+				tried_once = true;
+				goto try_again;
+			}
+		}
+	}
+
+	chip->fg_restarting = true;
+	/*
+	 * save the temperature if the sw rbias control is active so that there
+	 * is no gap of time when there is no valid temperature read after the
+	 * restart
+	 */
+	if (chip->sw_rbias_ctrl) {
+		rc = fg_mem_read(chip, buf,
+				fg_data[FG_DATA_BATT_TEMP].address,
+				fg_data[FG_DATA_BATT_TEMP].len,
+				fg_data[FG_DATA_BATT_TEMP].offset, 0);
+		if (rc) {
+			pr_err("failed to read batt temp rc=%d\n", rc);
+			goto sub_and_fail;
+		}
+	}
+	/*
+	 * release the sram access and configure the correct settings
+	 * before re-requesting access.
+	 */
+	mutex_lock(&chip->rw_lock);
+	fg_release_access(chip);
+
+	rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+			NO_OTP_PROF_RELOAD, 0, 1);
+	if (rc) {
+		pr_err("failed to set no otp reload bit\n");
+		goto unlock_and_fail;
+	}
+
+	/* unset the restart bits so the fg doesn't continuously restart */
+	reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+	rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+			reg, 0, 1);
+	if (rc) {
+		pr_err("failed to unset fg restart: %d\n", rc);
+		goto unlock_and_fail;
+	}
+
+	rc = fg_masked_write(chip, MEM_INTF_CFG(chip),
+			LOW_LATENCY, LOW_LATENCY, 1);
+	if (rc) {
+		pr_err("failed to set low latency access bit\n");
+		goto unlock_and_fail;
+	}
+	mutex_unlock(&chip->rw_lock);
+
+	/* read once to get a fg cycle in */
+	rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
+	if (rc) {
+		pr_err("failed to read profile integrity rc=%d\n", rc);
+		goto fail;
+	}
+
+	/*
+	 * If this is not the first time a profile has been loaded, sleep for
+	 * 3 seconds to make sure the NO_OTP_RELOAD is cleared in memory
+	 */
+	if (chip->first_profile_loaded)
+		msleep(3000);
+
+	mutex_lock(&chip->rw_lock);
+	fg_release_access(chip);
+	rc = fg_masked_write(chip, MEM_INTF_CFG(chip), LOW_LATENCY, 0, 1);
+	if (rc) {
+		pr_err("failed to set low latency access bit\n");
+		goto unlock_and_fail;
+	}
+
+	atomic_add_return(1, &chip->memif_user_cnt);
+	mutex_unlock(&chip->rw_lock);
+
+	if (write_profile) {
+		/* write the battery profile */
+		rc = fg_mem_write(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
+				chip->batt_profile_len, 0, 1);
+		if (rc) {
+			pr_err("failed to write profile rc=%d\n", rc);
+			goto sub_and_fail;
+		}
+		/* write the integrity bits and release access */
+		rc = fg_mem_masked_write(chip, PROFILE_INTEGRITY_REG,
+				PROFILE_INTEGRITY_BIT,
+				PROFILE_INTEGRITY_BIT, 0);
+		if (rc) {
+			pr_err("failed to write profile rc=%d\n", rc);
+			goto sub_and_fail;
+		}
+	}
+
+	/* decrement the user count so that memory access can be released */
+	fg_release_access_if_necessary(chip);
+
+	/*
+	 * make sure that the first estimate has completed
+	 * in case of a hotswap
+	 */
+	rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
+			msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+	if (rc <= 0) {
+		pr_err("transaction timed out rc=%d\n", rc);
+		rc = -ETIMEDOUT;
+		goto fail;
+	}
+
+	/*
+	 * reinitialize the completion so that the driver knows when the restart
+	 * finishes
+	 */
+	reinit_completion(&chip->first_soc_done);
+
+	if (chip->esr_pulse_tune_en) {
+		fg_stay_awake(&chip->esr_extract_wakeup_source);
+		schedule_work(&chip->esr_extract_config_work);
+	}
+
+	/*
+	 * set the restart bits so that the next fg cycle will not reload
+	 * the profile
+	 */
+	rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+			NO_OTP_PROF_RELOAD, NO_OTP_PROF_RELOAD, 1);
+	if (rc) {
+		pr_err("failed to set no otp reload bit\n");
+		goto fail;
+	}
+
+	reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+	rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+			reg, reg, 1);
+	if (rc) {
+		pr_err("failed to set fg restart: %d\n", rc);
+		goto fail;
+	}
+
+	/* wait for the first estimate to complete */
+	rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
+			msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+	if (rc <= 0) {
+		pr_err("transaction timed out rc=%d\n", rc);
+		rc = -ETIMEDOUT;
+		goto fail;
+	}
+	rc = fg_read(chip, &reg, 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);
+		goto fail;
+	}
+	if ((reg & FIRST_EST_DONE_BIT) == 0)
+		pr_err("Battery profile reloading failed, no first estimate\n");
+
+	rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD,
+			NO_OTP_PROF_RELOAD, 0, 1);
+	if (rc) {
+		pr_err("failed to set no otp reload bit\n");
+		goto fail;
+	}
+	/* unset the restart bits so the fg doesn't continuously restart */
+	reg = REDO_FIRST_ESTIMATE | RESTART_GO;
+	rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART,
+			reg, 0, 1);
+	if (rc) {
+		pr_err("failed to unset fg restart: %d\n", rc);
+		goto fail;
+	}
+
+	/* restore the battery temperature reading here */
+	if (chip->sw_rbias_ctrl) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("reloaded 0x%02x%02x into batt temp",
+					buf[0], buf[1]);
+		rc = fg_mem_write(chip, buf,
+				fg_data[FG_DATA_BATT_TEMP].address,
+				fg_data[FG_DATA_BATT_TEMP].len,
+				fg_data[FG_DATA_BATT_TEMP].offset, 0);
+		if (rc) {
+			pr_err("failed to write batt temp rc=%d\n", rc);
+			goto fail;
+		}
+	}
+
+	/* Enable charging now as the first estimate is done now */
+	if (chip->charging_disabled) {
+		rc = set_prop_enable_charging(chip, true);
+		if (rc)
+			pr_err("Failed to enable charging, rc=%d\n", rc);
+		else
+			chip->charging_disabled = false;
+	}
+
+	chip->fg_restarting = false;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("done!\n");
+	return 0;
+
+unlock_and_fail:
+	mutex_unlock(&chip->rw_lock);
+	goto fail;
+sub_and_fail:
+	fg_release_access_if_necessary(chip);
+	goto fail;
+fail:
+	chip->fg_restarting = false;
+	return -EINVAL;
+}
+
+#define FG_PROFILE_LEN			128
+#define PROFILE_COMPARE_LEN		32
+#define THERMAL_COEFF_ADDR		0x444
+#define THERMAL_COEFF_OFFSET		0x2
+#define BATTERY_PSY_WAIT_MS		2000
+static int fg_batt_profile_init(struct fg_chip *chip)
+{
+	int rc = 0, ret, 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;
+	bool tried_again = false, vbat_in_range, profiles_same;
+	u8 reg = 0;
+
+wait:
+	fg_stay_awake(&chip->profile_wakeup_source);
+	ret = wait_for_completion_interruptible_timeout(&chip->batt_id_avail,
+			msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
+	/* If we were interrupted wait again one more time. */
+	if (ret == -ERESTARTSYS && !tried_again) {
+		tried_again = true;
+		pr_debug("interrupted, waiting again\n");
+		goto wait;
+	} else if (ret <= 0) {
+		rc = -ETIMEDOUT;
+		pr_err("profile loading timed out rc=%d\n", rc);
+		goto no_profile;
+	}
+
+	batt_node = of_find_node_by_name(node, "qcom,battery-data");
+	if (!batt_node) {
+		pr_warn("No available batterydata, using OTP defaults\n");
+		rc = 0;
+		goto no_profile;
+	}
+
+	batt_id = get_sram_prop_now(chip, FG_DATA_BATT_ID);
+	batt_id /= 1000;
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("battery id = %dKOhms\n", batt_id);
+
+	profile_node = of_batterydata_get_best_profile(batt_node, batt_id,
+							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;
+	}
+
+	/* read rslow compensation values if they're available */
+	rc = of_property_read_u32(profile_node, "qcom,chg-rs-to-rslow",
+					&chip->rslow_comp.chg_rs_to_rslow);
+	if (rc) {
+		chip->rslow_comp.chg_rs_to_rslow = -EINVAL;
+		if (rc != -EINVAL)
+			pr_err("Could not read rs to rslow: %d\n", rc);
+	}
+	rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c1",
+					&chip->rslow_comp.chg_rslow_comp_c1);
+	if (rc) {
+		chip->rslow_comp.chg_rslow_comp_c1 = -EINVAL;
+		if (rc != -EINVAL)
+			pr_err("Could not read rslow comp c1: %d\n", rc);
+	}
+	rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-c2",
+					&chip->rslow_comp.chg_rslow_comp_c2);
+	if (rc) {
+		chip->rslow_comp.chg_rslow_comp_c2 = -EINVAL;
+		if (rc != -EINVAL)
+			pr_err("Could not read rslow comp c2: %d\n", rc);
+	}
+	rc = of_property_read_u32(profile_node, "qcom,chg-rslow-comp-thr",
+					&chip->rslow_comp.chg_rslow_comp_thr);
+	if (rc) {
+		chip->rslow_comp.chg_rslow_comp_thr = -EINVAL;
+		if (rc != -EINVAL)
+			pr_err("Could not read rslow comp thr: %d\n", rc);
+	}
+
+	rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+					&chip->batt_max_voltage_uv);
+
+	if (rc)
+		pr_warn("couldn't find battery max voltage\n");
+
+	/*
+	 * Only configure from profile if fg-cc-cv-threshold-mv is not
+	 * defined in the charger device node.
+	 */
+	if (!of_find_property(chip->pdev->dev.of_node,
+				"qcom,fg-cc-cv-threshold-mv", NULL)) {
+		of_property_read_u32(profile_node,
+				"qcom,fg-cc-cv-threshold-mv",
+				&chip->cc_cv_threshold_mv);
+	}
+
+	data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
+	if (!data) {
+		pr_err("no battery profile loaded\n");
+		rc = 0;
+		goto no_profile;
+	}
+
+	if (len != FG_PROFILE_LEN) {
+		pr_err("battery profile incorrect size: %d\n", len);
+		rc = -EINVAL;
+		goto no_profile;
+	}
+
+	rc = of_property_read_string(profile_node, "qcom,battery-type",
+					&batt_type_str);
+	if (rc) {
+		pr_err("Could not find battery data type: %d\n", rc);
+		rc = 0;
+		goto no_profile;
+	}
+
+	if (!chip->batt_profile)
+		chip->batt_profile = devm_kzalloc(chip->dev,
+				sizeof(char) * len, GFP_KERNEL);
+
+	if (!chip->batt_profile) {
+		pr_err("out of memory\n");
+		rc = -ENOMEM;
+		goto no_profile;
+	}
+
+	rc = fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 1);
+	if (rc) {
+		pr_err("failed to read profile integrity rc=%d\n", rc);
+		goto no_profile;
+	}
+
+	rc = fg_mem_read(chip, chip->batt_profile, BATT_PROFILE_OFFSET,
+			len, 0, 1);
+	if (rc) {
+		pr_err("failed to read profile rc=%d\n", rc);
+		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;
+	profiles_same = memcmp(chip->batt_profile, data,
+					PROFILE_COMPARE_LEN) == 0;
+	if (reg & PROFILE_INTEGRITY_BIT) {
+		fg_cap_learning_load_data(chip);
+		if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) {
+			if (fg_debug_mask & FG_STATUS)
+				pr_info("Battery profiles same, using default\n");
+			if (fg_est_dump)
+				schedule_work(&chip->dump_sram);
+			goto done;
+		}
+	} else {
+		pr_info("Battery profile not same, clearing data\n");
+		clear_cycle_counter(chip);
+		chip->learning_data.learned_cc_uah = 0;
+	}
+
+	if (fg_est_dump)
+		dump_sram(&chip->dump_sram);
+
+	if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
+		pr_info("Vbat out of range: v_current_pred: %d, v:%d\n",
+				fg_data[FG_DATA_CPRED_VOLTAGE].value,
+				fg_data[FG_DATA_VOLTAGE].value);
+
+	if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip))
+		pr_info("battery empty\n");
+
+	if ((fg_debug_mask & FG_STATUS) && !profiles_same)
+		pr_info("profiles differ\n");
+
+	if (fg_debug_mask & FG_STATUS) {
+		pr_info("Using new profile\n");
+		print_hex_dump(KERN_INFO, "FG: loaded profile: ",
+				DUMP_PREFIX_NONE, 16, 1,
+				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)
+		print_hex_dump(KERN_INFO, "FG: new profile: ",
+				DUMP_PREFIX_NONE, 16, 1, chip->batt_profile,
+				chip->batt_profile_len, false);
+
+	rc = fg_do_restart(chip, true);
+	if (rc) {
+		pr_err("restart failed: %d\n", rc);
+		goto no_profile;
+	}
+
+	/*
+	 * Only configure from profile if thermal-coefficients is not
+	 * defined in the FG device node.
+	 */
+	if (!of_find_property(chip->pdev->dev.of_node,
+				"qcom,thermal-coefficients", NULL)) {
+		data = of_get_property(profile_node,
+				"qcom,thermal-coefficients", &len);
+		if (data && len == THERMAL_COEFF_N_BYTES) {
+			memcpy(chip->thermal_coefficients, data, len);
+			rc = fg_mem_write(chip, chip->thermal_coefficients,
+				THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
+				THERMAL_COEFF_OFFSET, 0);
+			if (rc)
+				pr_err("spmi write failed addr:%03x, ret:%d\n",
+						THERMAL_COEFF_ADDR, rc);
+			else if (fg_debug_mask & FG_STATUS)
+				pr_info("Battery thermal coefficients changed\n");
+		}
+	}
+
+done:
+	if (chip->charging_disabled) {
+		rc = set_prop_enable_charging(chip, true);
+		if (rc)
+			pr_err("Failed to enable charging, rc=%d\n", rc);
+		else
+			chip->charging_disabled = false;
+	}
+
+	if (fg_batt_type)
+		chip->batt_type = fg_batt_type;
+	else
+		chip->batt_type = batt_type_str;
+	chip->first_profile_loaded = true;
+	chip->profile_loaded = true;
+	chip->battery_missing = is_battery_missing(chip);
+	update_chg_iterm(chip);
+	update_cc_cv_setpoint(chip);
+	rc = populate_system_data(chip);
+	if (rc) {
+		pr_err("failed to read ocv properties=%d\n", rc);
+		return rc;
+	}
+	estimate_battery_age(chip, &chip->actual_cap_uah);
+	schedule_work(&chip->status_change_work);
+	if (chip->power_supply_registered)
+		power_supply_changed(chip->bms_psy);
+	fg_relax(&chip->profile_wakeup_source);
+	pr_info("Battery SOC: %d, V: %duV\n", get_prop_capacity(chip),
+		fg_data[FG_DATA_VOLTAGE].value);
+	return rc;
+no_profile:
+	if (chip->charging_disabled) {
+		rc = set_prop_enable_charging(chip, true);
+		if (rc)
+			pr_err("Failed to enable charging, rc=%d\n", rc);
+		else
+			chip->charging_disabled = false;
+	}
+
+	if (chip->power_supply_registered)
+		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));
+	cancel_delayed_work(&chip->update_sram_data);
+	schedule_delayed_work(
+		&chip->update_sram_data,
+		msecs_to_jiffies(0));
+	fg_relax(&chip->profile_wakeup_source);
+	return 0;
+}
+
+static void check_empty_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				check_empty_work.work);
+
+	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);
+	}
+	fg_relax(&chip->empty_check_wakeup_source);
+}
+
+static void batt_profile_init(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				batt_profile_init.work);
+
+	if (fg_batt_profile_init(chip))
+		pr_err("failed to initialize profile\n");
+}
+
+static void sysfs_restart_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				sysfs_restart_work);
+	int rc;
+
+	rc = fg_do_restart(chip, false);
+	if (rc)
+		pr_err("fg restart failed: %d\n", rc);
+	mutex_lock(&chip->sysfs_restart_lock);
+	fg_restart = 0;
+	mutex_unlock(&chip->sysfs_restart_lock);
+}
+
+#define SRAM_MONOTONIC_SOC_REG		0x574
+#define SRAM_MONOTONIC_SOC_OFFSET	2
+#define SRAM_RELEASE_TIMEOUT_MS		500
+static void charge_full_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+				struct fg_chip,
+				charge_full_work);
+	int rc;
+	u8 buffer[3];
+	int bsoc;
+	int resume_soc_raw = FULL_SOC_RAW - settings[FG_MEM_RESUME_SOC].value;
+	bool disable = false;
+	u8 reg;
+
+	if (chip->status != POWER_SUPPLY_STATUS_FULL) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("battery not full: %d\n", chip->status);
+		disable = true;
+	}
+
+	fg_mem_lock(chip);
+	rc = fg_mem_read(chip, buffer, BATTERY_SOC_REG, 3, 1, 0);
+	if (rc) {
+		pr_err("Unable to read battery soc: %d\n", rc);
+		goto out;
+	}
+	if (buffer[2] <= resume_soc_raw) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("bsoc = 0x%02x <= resume = 0x%02x\n",
+					buffer[2], resume_soc_raw);
+		disable = true;
+	}
+	if (!disable)
+		goto out;
+
+	rc = fg_mem_write(chip, buffer, SOC_FULL_REG, 3,
+			SOC_FULL_OFFSET, 0);
+	if (rc) {
+		pr_err("failed to write SOC_FULL rc=%d\n", rc);
+		goto out;
+	}
+	/* force a full soc value into the monotonic in order to display 100 */
+	buffer[0] = 0xFF;
+	buffer[1] = 0xFF;
+	rc = fg_mem_write(chip, buffer, SRAM_MONOTONIC_SOC_REG, 2,
+			SRAM_MONOTONIC_SOC_OFFSET, 0);
+	if (rc) {
+		pr_err("failed to write SOC_FULL rc=%d\n", rc);
+		goto out;
+	}
+	if (fg_debug_mask & FG_STATUS) {
+		bsoc = buffer[0] | buffer[1] << 8 | buffer[2] << 16;
+		pr_info("wrote %06x into soc full\n", bsoc);
+	}
+	fg_mem_release(chip);
+	/*
+	 * wait one cycle to make sure the soc is updated before clearing
+	 * the soc mask bit
+	 */
+	fg_mem_lock(chip);
+	fg_mem_read(chip, &reg, PROFILE_INTEGRITY_REG, 1, 0, 0);
+out:
+	fg_mem_release(chip);
+	if (disable)
+		chip->charge_full = false;
+}
+
+static void update_bcl_thresholds(struct fg_chip *chip)
+{
+	u8 data[4];
+	u8 mh_offset = 0, lm_offset = 0;
+	u16 address = 0;
+	int ret = 0;
+
+	address = settings[FG_MEM_BCL_MH_THRESHOLD].address;
+	mh_offset = settings[FG_MEM_BCL_MH_THRESHOLD].offset;
+	lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
+	ret = fg_mem_read(chip, data, address, 4, 0, 1);
+	if (ret)
+		pr_err("Error reading BCL LM & MH threshold rc:%d\n", ret);
+	else
+		pr_debug("Old BCL LM threshold:%x MH threshold:%x\n",
+			data[lm_offset], data[mh_offset]);
+	BCL_MA_TO_ADC(settings[FG_MEM_BCL_MH_THRESHOLD].value, data[mh_offset]);
+	BCL_MA_TO_ADC(settings[FG_MEM_BCL_LM_THRESHOLD].value, data[lm_offset]);
+
+	ret = fg_mem_write(chip, data, address, 4, 0, 0);
+	if (ret)
+		pr_err("spmi write failed. addr:%03x, ret:%d\n",
+			address, ret);
+	else
+		pr_debug("New BCL LM threshold:%x MH threshold:%x\n",
+			data[lm_offset], data[mh_offset]);
+}
+
+static int disable_bcl_lpm(struct fg_chip *chip)
+{
+	u8 data[4];
+	u8 lm_offset = 0;
+	u16 address = 0;
+	int rc = 0;
+
+	address = settings[FG_MEM_BCL_LM_THRESHOLD].address;
+	lm_offset = settings[FG_MEM_BCL_LM_THRESHOLD].offset;
+	rc = fg_mem_read(chip, data, address, 4, 0, 1);
+	if (rc) {
+		pr_err("Error reading BCL LM & MH threshold rc:%d\n", rc);
+		return rc;
+	}
+	pr_debug("Old BCL LM threshold:%x\n", data[lm_offset]);
+
+	/* Put BCL always above LPM */
+	BCL_MA_TO_ADC(0, data[lm_offset]);
+
+	rc = fg_mem_write(chip, data, address, 4, 0, 0);
+	if (rc)
+		pr_err("spmi write failed. addr:%03x, rc:%d\n",
+			address, rc);
+	else
+		pr_debug("New BCL LM threshold:%x\n", data[lm_offset]);
+
+	return rc;
+}
+
+static void bcl_hi_power_work(struct work_struct *work)
+{
+	struct fg_chip *chip = container_of(work,
+			struct fg_chip,
+			bcl_hi_power_work);
+	int rc;
+
+	if (chip->bcl_lpm_disabled) {
+		rc = disable_bcl_lpm(chip);
+		if (rc)
+			pr_err("failed to disable bcl low mode %d\n",
+					rc);
+	} else {
+		update_bcl_thresholds(chip);
+	}
+}
+
+#define VOLT_UV_TO_VOLTCMP8(volt_uv)	\
+			((volt_uv - 2500000) / 9766)
+static int update_irq_volt_empty(struct fg_chip *chip)
+{
+	u8 data;
+	int volt_mv = settings[FG_MEM_IRQ_VOLT_EMPTY].value;
+
+	data = (u8)VOLT_UV_TO_VOLTCMP8(volt_mv * 1000);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("voltage = %d, converted_raw = %04x\n", volt_mv, data);
+	return fg_mem_write(chip, &data,
+			settings[FG_MEM_IRQ_VOLT_EMPTY].address, 1,
+			settings[FG_MEM_IRQ_VOLT_EMPTY].offset, 0);
+}
+
+static int update_cutoff_voltage(struct fg_chip *chip)
+{
+	u8 data[2];
+	u16 converted_voltage_raw;
+	s64 voltage_mv = settings[FG_MEM_CUTOFF_VOLTAGE].value;
+
+	converted_voltage_raw = (s16)MICROUNITS_TO_ADC_RAW(voltage_mv * 1000);
+	data[0] = cpu_to_le16(converted_voltage_raw) & 0xFF;
+	data[1] = cpu_to_le16(converted_voltage_raw) >> 8;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("voltage = %lld, converted_raw = %04x, data = %02x %02x\n",
+			voltage_mv, converted_voltage_raw, data[0], data[1]);
+	return fg_mem_write(chip, data, settings[FG_MEM_CUTOFF_VOLTAGE].address,
+				2, settings[FG_MEM_CUTOFF_VOLTAGE].offset, 0);
+}
+
+static int update_iterm(struct fg_chip *chip)
+{
+	u8 data[2];
+	u16 converted_current_raw;
+	s64 current_ma = -settings[FG_MEM_TERM_CURRENT].value;
+
+	converted_current_raw = (s16)MICROUNITS_TO_ADC_RAW(current_ma * 1000);
+	data[0] = cpu_to_le16(converted_current_raw) & 0xFF;
+	data[1] = cpu_to_le16(converted_current_raw) >> 8;
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("current = %lld, converted_raw = %04x, data = %02x %02x\n",
+			current_ma, converted_current_raw, data[0], data[1]);
+	return fg_mem_write(chip, data, settings[FG_MEM_TERM_CURRENT].address,
+				2, settings[FG_MEM_TERM_CURRENT].offset, 0);
+}
+
+#define OF_READ_SETTING(type, qpnp_dt_property, retval, optional)	\
+do {									\
+	if (retval)							\
+		break;							\
+									\
+	retval = of_property_read_u32(chip->pdev->dev.of_node,		\
+					"qcom," qpnp_dt_property,	\
+					&settings[type].value);		\
+									\
+	if ((retval == -EINVAL) && optional)				\
+		retval = 0;						\
+	else if (retval)						\
+		pr_err("Error reading " #qpnp_dt_property		\
+				" property rc = %d\n", rc);		\
+} while (0)
+
+#define OF_READ_PROPERTY(store, qpnp_dt_property, retval, default_val)	\
+do {									\
+	if (retval)							\
+		break;							\
+									\
+	retval = of_property_read_u32(chip->pdev->dev.of_node,		\
+					"qcom," qpnp_dt_property,	\
+					&store);			\
+									\
+	if (retval == -EINVAL) {					\
+		retval = 0;						\
+		store = default_val;					\
+	} else if (retval) {						\
+		pr_err("Error reading " #qpnp_dt_property		\
+				" property rc = %d\n", rc);		\
+	}								\
+} while (0)
+
+#define DEFAULT_EVALUATION_CURRENT_MA	1000
+static int fg_of_init(struct fg_chip *chip)
+{
+	int rc = 0, sense_type, len = 0;
+	const char *data;
+	struct device_node *node = chip->pdev->dev.of_node;
+	u32 temp[2] = {0};
+
+	OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1);
+	OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1);
+	OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1);
+	OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1);
+
+	if (of_find_property(node, "qcom,cold-hot-jeita-hysteresis", NULL)) {
+		int hard_hot = 0, soft_hot = 0, hard_cold = 0, soft_cold = 0;
+
+		rc = of_property_read_u32_array(node,
+			"qcom,cold-hot-jeita-hysteresis", temp, 2);
+		if (rc) {
+			pr_err("Error reading cold-hot-jeita-hysteresis rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		chip->jeita_hysteresis_support = true;
+		chip->cold_hysteresis = temp[0];
+		chip->hot_hysteresis = temp[1];
+		hard_hot = settings[FG_MEM_HARD_HOT].value;
+		soft_hot = settings[FG_MEM_SOFT_HOT].value;
+		hard_cold = settings[FG_MEM_HARD_COLD].value;
+		soft_cold = settings[FG_MEM_SOFT_COLD].value;
+		if (((hard_hot - chip->hot_hysteresis) < soft_hot) ||
+			((hard_cold + chip->cold_hysteresis) > soft_cold)) {
+			chip->jeita_hysteresis_support = false;
+			pr_err("invalid hysteresis: hot_hysterresis = %d cold_hysteresis = %d\n",
+				chip->hot_hysteresis, chip->cold_hysteresis);
+		} else {
+			pr_debug("cold_hysteresis = %d, hot_hysteresis = %d\n",
+				chip->cold_hysteresis, chip->hot_hysteresis);
+		}
+	}
+
+	OF_READ_SETTING(FG_MEM_BCL_LM_THRESHOLD, "bcl-lm-threshold-ma",
+		rc, 1);
+	OF_READ_SETTING(FG_MEM_BCL_MH_THRESHOLD, "bcl-mh-threshold-ma",
+		rc, 1);
+	OF_READ_SETTING(FG_MEM_TERM_CURRENT, "fg-iterm-ma", rc, 1);
+	OF_READ_SETTING(FG_MEM_CHG_TERM_CURRENT, "fg-chg-iterm-ma", rc, 1);
+	OF_READ_SETTING(FG_MEM_CUTOFF_VOLTAGE, "fg-cutoff-voltage-mv", rc, 1);
+	data = of_get_property(chip->pdev->dev.of_node,
+			"qcom,thermal-coefficients", &len);
+	if (data && len == THERMAL_COEFF_N_BYTES) {
+		memcpy(chip->thermal_coefficients, data, len);
+		chip->use_thermal_coefficients = true;
+	}
+	OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc", rc, 1);
+	settings[FG_MEM_RESUME_SOC].value =
+		DIV_ROUND_CLOSEST(settings[FG_MEM_RESUME_SOC].value
+				* FULL_SOC_RAW, FULL_CAPACITY);
+	OF_READ_SETTING(FG_MEM_RESUME_SOC, "resume-soc-raw", rc, 1);
+	OF_READ_SETTING(FG_MEM_IRQ_VOLT_EMPTY, "irq-volt-empty-mv", rc, 1);
+	OF_READ_SETTING(FG_MEM_VBAT_EST_DIFF, "vbat-estimate-diff-mv", rc, 1);
+	OF_READ_SETTING(FG_MEM_DELTA_SOC, "fg-delta-soc", rc, 1);
+	OF_READ_SETTING(FG_MEM_BATT_LOW, "fg-vbatt-low-threshold", rc, 1);
+	OF_READ_SETTING(FG_MEM_THERM_DELAY, "fg-therm-delay-us", rc, 1);
+	OF_READ_PROPERTY(chip->learning_data.max_increment,
+			"cl-max-increment-deciperc", rc, 5);
+	OF_READ_PROPERTY(chip->learning_data.max_decrement,
+			"cl-max-decrement-deciperc", rc, 100);
+	OF_READ_PROPERTY(chip->learning_data.max_temp,
+			"cl-max-temp-decidegc", rc, 450);
+	OF_READ_PROPERTY(chip->learning_data.min_temp,
+			"cl-min-temp-decidegc", rc, 150);
+	OF_READ_PROPERTY(chip->learning_data.max_start_soc,
+			"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->evaluation_current,
+			"aging-eval-current-ma", rc,
+			DEFAULT_EVALUATION_CURRENT_MA);
+	OF_READ_PROPERTY(chip->cc_cv_threshold_mv,
+			"fg-cc-cv-threshold-mv", rc, 0);
+	if (of_property_read_bool(chip->pdev->dev.of_node,
+				"qcom,capacity-learning-on"))
+		chip->batt_aging_mode = FG_AGING_CC;
+	else if (of_property_read_bool(chip->pdev->dev.of_node,
+				"qcom,capacity-estimation-on"))
+		chip->batt_aging_mode = FG_AGING_ESR;
+	else
+		chip->batt_aging_mode = FG_AGING_NONE;
+	if (chip->batt_aging_mode == FG_AGING_CC) {
+		chip->learning_data.feedback_on
+			= of_property_read_bool(chip->pdev->dev.of_node,
+					"qcom,capacity-learning-feedback");
+	}
+	if (fg_debug_mask & FG_AGING)
+		pr_info("battery aging mode: %d\n", chip->batt_aging_mode);
+
+	/* Get the use-otp-profile property */
+	chip->use_otp_profile = of_property_read_bool(chip->pdev->dev.of_node,
+			"qcom,use-otp-profile");
+	chip->hold_soc_while_full
+		= of_property_read_bool(chip->pdev->dev.of_node,
+			"qcom,hold-soc-while-full");
+
+	sense_type = of_property_read_bool(chip->pdev->dev.of_node,
+					"qcom,ext-sense-type");
+	if (rc == 0) {
+		if (fg_sense_type < 0)
+			fg_sense_type = sense_type;
+
+		if (fg_debug_mask & FG_STATUS) {
+			if (fg_sense_type == INTERNAL_CURRENT_SENSE)
+				pr_info("Using internal sense\n");
+			else if (fg_sense_type == EXTERNAL_CURRENT_SENSE)
+				pr_info("Using external sense\n");
+			else
+				pr_info("Using default sense\n");
+		}
+	} else {
+		rc = 0;
+	}
+
+	chip->bad_batt_detection_en = of_property_read_bool(node,
+				"qcom,bad-battery-detection-enable");
+
+	chip->sw_rbias_ctrl = of_property_read_bool(node,
+				"qcom,sw-rbias-control");
+
+	chip->cyc_ctr.en = of_property_read_bool(node,
+				"qcom,cycle-counter-en");
+	if (chip->cyc_ctr.en)
+		chip->cyc_ctr.id = 1;
+
+	chip->esr_pulse_tune_en = of_property_read_bool(node,
+					"qcom,esr-pulse-tuning-en");
+
+	return rc;
+}
+
+static int fg_init_irqs(struct fg_chip *chip)
+{
+	int rc = 0;
+	unsigned int base;
+	struct device_node *child;
+	u8 subtype;
+	struct platform_device *pdev = chip->pdev;
+
+	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		return -ENXIO;
+	}
+
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			return rc;
+		}
+
+		if ((base == chip->vbat_adc_addr) ||
+				(base == chip->ibat_adc_addr) ||
+				(base == chip->tp_rev_addr))
+			continue;
+
+		rc = fg_read(chip, &subtype,
+				base + REG_OFFSET_PERP_SUBTYPE, 1);
+		if (rc) {
+			pr_err("Peripheral subtype read failed rc=%d\n", rc);
+			return rc;
+		}
+
+		switch (subtype) {
+		case FG_SOC:
+			chip->soc_irq[FULL_SOC].irq = of_irq_get_byname(child,
+							      "full-soc");
+			if (chip->soc_irq[FULL_SOC].irq < 0) {
+				pr_err("Unable to get full-soc irq\n");
+				return rc;
+			}
+			chip->soc_irq[EMPTY_SOC].irq = of_irq_get_byname(child,
+							       "empty-soc");
+			if (chip->soc_irq[EMPTY_SOC].irq < 0) {
+				pr_err("Unable to get low-soc irq\n");
+				return rc;
+			}
+			chip->soc_irq[DELTA_SOC].irq = of_irq_get_byname(child,
+							       "delta-soc");
+			if (chip->soc_irq[DELTA_SOC].irq < 0) {
+				pr_err("Unable to get delta-soc irq\n");
+				return rc;
+			}
+			chip->soc_irq[FIRST_EST_DONE].irq
+				= of_irq_get_byname(child, "first-est-done");
+			if (chip->soc_irq[FIRST_EST_DONE].irq < 0) {
+				pr_err("Unable to get first-est-done irq\n");
+				return rc;
+			}
+
+			rc = devm_request_irq(chip->dev,
+				chip->soc_irq[FULL_SOC].irq,
+				fg_soc_irq_handler, IRQF_TRIGGER_RISING,
+				"full-soc", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d full-soc: %d\n",
+					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;
+			}
+			rc = devm_request_irq(chip->dev,
+				chip->soc_irq[DELTA_SOC].irq,
+				fg_soc_irq_handler, IRQF_TRIGGER_RISING,
+				"delta-soc", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d delta-soc: %d\n",
+					chip->soc_irq[DELTA_SOC].irq, rc);
+				return rc;
+			}
+			rc = devm_request_irq(chip->dev,
+				chip->soc_irq[FIRST_EST_DONE].irq,
+				fg_first_soc_irq_handler, IRQF_TRIGGER_RISING,
+				"first-est-done", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d delta-soc: %d\n",
+					chip->soc_irq[FIRST_EST_DONE].irq, rc);
+				return rc;
+			}
+
+			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);
+			break;
+		case FG_MEMIF:
+			chip->mem_irq[FG_MEM_AVAIL].irq
+				= of_irq_get_byname(child, "mem-avail");
+			if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) {
+				pr_err("Unable to get mem-avail irq\n");
+				return rc;
+			}
+			rc = devm_request_irq(chip->dev,
+					chip->mem_irq[FG_MEM_AVAIL].irq,
+					fg_mem_avail_irq_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					"mem-avail", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d mem-avail: %d\n",
+					chip->mem_irq[FG_MEM_AVAIL].irq, rc);
+				return rc;
+			}
+			break;
+		case FG_BATT:
+			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;
+				return rc;
+			}
+			rc = devm_request_threaded_irq(chip->dev,
+					chip->batt_irq[BATT_MISSING].irq,
+					NULL,
+					fg_batt_missing_irq_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING |
+					IRQF_ONESHOT,
+					"batt-missing", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d batt-missing: %d\n",
+					chip->batt_irq[BATT_MISSING].irq, rc);
+				return rc;
+			}
+			chip->batt_irq[VBATT_LOW].irq
+				= of_irq_get_byname(child, "vbatt-low");
+			if (chip->batt_irq[VBATT_LOW].irq < 0) {
+				pr_err("Unable to get vbatt-low irq\n");
+				rc = -EINVAL;
+				return rc;
+			}
+			rc = devm_request_irq(chip->dev,
+					chip->batt_irq[VBATT_LOW].irq,
+					fg_vbatt_low_handler,
+					IRQF_TRIGGER_RISING |
+					IRQF_TRIGGER_FALLING,
+					"vbatt-low", chip);
+			if (rc < 0) {
+				pr_err("Can't request %d vbatt-low: %d\n",
+					chip->batt_irq[VBATT_LOW].irq, rc);
+				return rc;
+			}
+			disable_irq_nosync(chip->batt_irq[VBATT_LOW].irq);
+			chip->vbat_low_irq_enabled = false;
+			break;
+		case FG_ADC:
+			break;
+		default:
+			pr_err("subtype %d\n", subtype);
+			return -EINVAL;
+		}
+	}
+
+	return rc;
+}
+
+static void fg_cleanup(struct fg_chip *chip)
+{
+	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);
+	cancel_work_sync(&chip->rslow_comp_work);
+	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->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->esr_extract_config_work);
+	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);
+	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);
+	wakeup_source_trash(&chip->profile_wakeup_source.source);
+	wakeup_source_trash(&chip->update_temp_wakeup_source.source);
+	wakeup_source_trash(&chip->update_sram_wakeup_source.source);
+	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);
+}
+
+static int fg_remove(struct platform_device *pdev)
+{
+	struct fg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+	fg_cleanup(chip);
+	dev_set_drvdata(&pdev->dev, NULL);
+	return 0;
+}
+
+static int fg_memif_data_open(struct inode *inode, struct file *file)
+{
+	struct fg_log_buffer *log;
+	struct fg_trans *trans;
+	u8 *data_buf;
+
+	size_t logbufsize = SZ_4K;
+	size_t databufsize = SZ_4K;
+
+	if (!dbgfs_data.chip) {
+		pr_err("Not initialized data\n");
+		return -EINVAL;
+	}
+
+	/* Per file "transaction" data */
+	trans = kzalloc(sizeof(*trans), GFP_KERNEL);
+	if (!trans)
+		return -ENOMEM;
+
+	/* Allocate log buffer */
+	log = kzalloc(logbufsize, GFP_KERNEL);
+
+	if (!log) {
+		kfree(trans);
+		pr_err("Unable to allocate memory for log buffer\n");
+		return -ENOMEM;
+	}
+
+	log->rpos = 0;
+	log->wpos = 0;
+	log->len = logbufsize - sizeof(*log);
+
+	/* Allocate data buffer */
+	data_buf = kzalloc(databufsize, GFP_KERNEL);
+
+	if (!data_buf) {
+		kfree(trans);
+		kfree(log);
+		pr_err("Unable to allocate memory for data buffer\n");
+		return -ENOMEM;
+	}
+
+	trans->log = log;
+	trans->data = data_buf;
+	trans->cnt = dbgfs_data.cnt;
+	trans->addr = dbgfs_data.addr;
+	trans->chip = dbgfs_data.chip;
+	trans->offset = trans->addr;
+	mutex_init(&trans->memif_dfs_lock);
+
+	file->private_data = trans;
+	return 0;
+}
+
+static int fg_memif_dfs_close(struct inode *inode, struct file *file)
+{
+	struct fg_trans *trans = file->private_data;
+
+	if (trans && trans->log && trans->data) {
+		file->private_data = NULL;
+		mutex_destroy(&trans->memif_dfs_lock);
+		kfree(trans->log);
+		kfree(trans->data);
+		kfree(trans);
+	}
+
+	return 0;
+}
+
+/**
+ * print_to_log: format a string and place into the log buffer
+ * @log: The log buffer to place the result into.
+ * @fmt: The format string to use.
+ * @...: The arguments for the format string.
+ *
+ * The return value is the number of characters written to @log buffer
+ * not including the trailing '\0'.
+ */
+static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
+{
+	va_list args;
+	int cnt;
+	char *buf = &log->data[log->wpos];
+	size_t size = log->len - log->wpos;
+
+	va_start(args, fmt);
+	cnt = vscnprintf(buf, size, fmt, args);
+	va_end(args);
+
+	log->wpos += cnt;
+	return cnt;
+}
+
+/**
+ * write_next_line_to_log: Writes a single "line" of data into the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ * @offset: SRAM address offset to start reading from.
+ * @pcnt: Pointer to 'cnt' variable.  Indicates the number of bytes to read.
+ *
+ * The 'offset' is a 12-bit SRAM address.
+ *
+ * On a successful read, the pcnt is decremented by the number of data
+ * bytes read from the SRAM.  When the cnt reaches 0, all requested bytes have
+ * been read.
+ */
+static int
+write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt)
+{
+	int i, j;
+	u8 data[ITEMS_PER_LINE];
+	struct fg_log_buffer *log = trans->log;
+
+	int cnt = 0;
+	int padding = offset % ITEMS_PER_LINE;
+	int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
+	int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
+
+	/* Buffer needs enough space for an entire line */
+	if ((log->len - log->wpos) < MAX_LINE_LENGTH)
+		goto done;
+
+	memcpy(data, trans->data + (offset - trans->addr), items_to_read);
+
+	*pcnt -= items_to_read;
+
+	/* Each line starts with the aligned offset (12-bit address) */
+	cnt = print_to_log(log, "%3.3X ", offset & 0xfff);
+	if (cnt == 0)
+		goto done;
+
+	/* If the offset is unaligned, add padding to right justify items */
+	for (i = 0; i < padding; ++i) {
+		cnt = print_to_log(log, "-- ");
+		if (cnt == 0)
+			goto done;
+	}
+
+	/* Log the data items */
+	for (j = 0; i < items_to_log; ++i, ++j) {
+		cnt = print_to_log(log, "%2.2X ", data[j]);
+		if (cnt == 0)
+			goto done;
+	}
+
+	/* If the last character was a space, then replace it with a newline */
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+done:
+	return cnt;
+}
+
+/**
+ * get_log_data - reads data from SRAM and saves to the log buffer
+ * @trans: Pointer to SRAM transaction data.
+ *
+ * Returns the number of "items" read or SPMI error code for read failures.
+ */
+static int get_log_data(struct fg_trans *trans)
+{
+	int cnt, rc;
+	int last_cnt;
+	int items_read;
+	int total_items_read = 0;
+	u32 offset = trans->offset;
+	size_t item_cnt = trans->cnt;
+	struct fg_log_buffer *log = trans->log;
+
+	if (item_cnt == 0)
+		return 0;
+
+	if (item_cnt > SZ_4K) {
+		pr_err("Reading too many bytes\n");
+		return -EINVAL;
+	}
+
+	rc = fg_mem_read(trans->chip, trans->data,
+			trans->addr, trans->cnt, 0, 0);
+	if (rc) {
+		pr_err("dump failed: rc = %d\n", rc);
+		return rc;
+	}
+	/* Reset the log buffer 'pointers' */
+	log->wpos = log->rpos = 0;
+
+	/* Keep reading data until the log is full */
+	do {
+		last_cnt = item_cnt;
+		cnt = write_next_line_to_log(trans, offset, &item_cnt);
+		items_read = last_cnt - item_cnt;
+		offset += items_read;
+		total_items_read += items_read;
+	} while (cnt && item_cnt > 0);
+
+	/* Adjust the transaction offset and count */
+	trans->cnt = item_cnt;
+	trans->offset += total_items_read;
+
+	return total_items_read;
+}
+
+/**
+ * fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
+ *  byte array (coded as string)
+ * @file: file pointer
+ * @buf: where to put the result
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user bytes read, or negative error value
+ */
+static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct fg_trans *trans = file->private_data;
+	struct fg_log_buffer *log = trans->log;
+	size_t ret;
+	size_t len;
+
+	mutex_lock(&trans->memif_dfs_lock);
+	/* Is the the log buffer empty */
+	if (log->rpos >= log->wpos) {
+		if (get_log_data(trans) <= 0) {
+			len = 0;
+			goto unlock_mutex;
+		}
+	}
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret == len) {
+		pr_err("error copy sram register values to user\n");
+		len = -EFAULT;
+		goto unlock_mutex;
+	}
+
+	/* 'ret' is the number of bytes not copied */
+	len -= ret;
+
+	*ppos += len;
+	log->rpos += len;
+
+unlock_mutex:
+	mutex_unlock(&trans->memif_dfs_lock);
+	return len;
+}
+
+/**
+ * fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM.
+ * @file: file pointer
+ * @buf: user data to be written.
+ * @count: maximum space available in @buf
+ * @ppos: starting position
+ * @return number of user byte written, or negative error value
+ */
+static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf,
+			size_t count, loff_t *ppos)
+{
+	int bytes_read;
+	int data;
+	int pos = 0;
+	int cnt = 0;
+	u8  *values;
+	size_t ret = 0;
+	char *kbuf;
+	u32 offset;
+
+	struct fg_trans *trans = file->private_data;
+
+	mutex_lock(&trans->memif_dfs_lock);
+	offset = trans->offset;
+
+	/* Make a copy of the user data */
+	kbuf = kmalloc(count + 1, GFP_KERNEL);
+	if (!kbuf) {
+		ret = -ENOMEM;
+		goto unlock_mutex;
+	}
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (ret == count) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+
+	/* Override the text buffer with the raw data */
+	values = kbuf;
+
+	/* Parse the data in the buffer.  It should be a string of numbers */
+	while ((pos < count) &&
+		sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
+		/*
+		 * We shouldn't be receiving a string of characters that
+		 * exceeds a size of 5 to keep this functionally correct.
+		 * Also, we should make sure that pos never gets overflowed
+		 * beyond the limit.
+		 */
+		if (bytes_read > 5 || bytes_read > INT_MAX - pos) {
+			cnt = 0;
+			ret = -EINVAL;
+			break;
+		}
+		pos += bytes_read;
+		values[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	pr_info("address %x, count %d\n", offset, cnt);
+	/* Perform the write(s) */
+
+	ret = fg_mem_write(trans->chip, values, offset,
+				cnt, 0, 0);
+	if (ret) {
+		pr_err("SPMI write failed, err = %zu\n", ret);
+	} else {
+		ret = count;
+		trans->offset += cnt > 4 ? 4 : cnt;
+	}
+
+free_buf:
+	kfree(kbuf);
+unlock_mutex:
+	mutex_unlock(&trans->memif_dfs_lock);
+	return ret;
+}
+
+static const struct file_operations fg_memif_dfs_reg_fops = {
+	.open		= fg_memif_data_open,
+	.release	= fg_memif_dfs_close,
+	.read		= fg_memif_dfs_reg_read,
+	.write		= fg_memif_dfs_reg_write,
+};
+
+/**
+ * fg_dfs_create_fs: create debugfs file system.
+ * @return pointer to root directory or NULL if failed to create fs
+ */
+static struct dentry *fg_dfs_create_fs(void)
+{
+	struct dentry *root, *file;
+
+	pr_debug("Creating FG_MEM debugfs file-system\n");
+	root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
+	if (IS_ERR_OR_NULL(root)) {
+		pr_err("Error creating top level directory err:%ld",
+			(long)root);
+		if (PTR_ERR(root) == -ENODEV)
+			pr_err("debugfs is not enabled in the kernel");
+		return NULL;
+	}
+
+	dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
+
+	file = debugfs_create_blob("help", 0444, root, &dbgfs_data.help_msg);
+	if (!file) {
+		pr_err("error creating help entry\n");
+		goto err_remove_fs;
+	}
+	return root;
+
+err_remove_fs:
+	debugfs_remove_recursive(root);
+	return NULL;
+}
+
+/**
+ * fg_dfs_get_root: return a pointer to FG debugfs root directory.
+ * @return a pointer to the existing directory, or if no root
+ * directory exists then create one. Directory is created with file that
+ * configures SRAM transaction, namely: address, and count.
+ * @returns valid pointer on success or NULL
+ */
+struct dentry *fg_dfs_get_root(void)
+{
+	if (dbgfs_data.root)
+		return dbgfs_data.root;
+
+	if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
+		return NULL;
+	/* critical section */
+	if (!dbgfs_data.root) { /* double checking idiom */
+		dbgfs_data.root = fg_dfs_create_fs();
+	}
+	mutex_unlock(&dbgfs_data.lock);
+	return dbgfs_data.root;
+}
+
+/*
+ * fg_dfs_create: adds new fg_mem if debugfs entry
+ * @return zero on success
+ */
+int fg_dfs_create(struct fg_chip *chip)
+{
+	struct dentry *root;
+	struct dentry *file;
+
+	root = fg_dfs_get_root();
+	if (!root)
+		return -ENOENT;
+
+	dbgfs_data.chip = chip;
+
+	file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt));
+	if (!file) {
+		pr_err("error creating 'count' entry\n");
+		goto err_remove_fs;
+	}
+
+	file = debugfs_create_x32("address", DFS_MODE,
+			root, &(dbgfs_data.addr));
+	if (!file) {
+		pr_err("error creating 'address' entry\n");
+		goto err_remove_fs;
+	}
+
+	file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data,
+							&fg_memif_dfs_reg_fops);
+	if (!file) {
+		pr_err("error creating 'data' entry\n");
+		goto err_remove_fs;
+	}
+
+	return 0;
+
+err_remove_fs:
+	debugfs_remove_recursive(root);
+	return -ENOMEM;
+}
+
+#define EXTERNAL_SENSE_OFFSET_REG	0x41C
+#define EXT_OFFSET_TRIM_REG		0xF8
+#define SEC_ACCESS_REG			0xD0
+#define SEC_ACCESS_UNLOCK		0xA5
+#define BCL_TRIM_REV_FIXED		12
+static int bcl_trim_workaround(struct fg_chip *chip)
+{
+	u8 reg, rc;
+
+	if (chip->tp_rev_addr == 0)
+		return 0;
+
+	rc = fg_read(chip, &reg, chip->tp_rev_addr, 1);
+	if (rc) {
+		pr_err("Failed to read tp reg, rc = %d\n", rc);
+		return rc;
+	}
+	if (reg >= BCL_TRIM_REV_FIXED) {
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("workaround not applied, tp_rev = %d\n", reg);
+		return 0;
+	}
+
+	rc = fg_mem_read(chip, &reg, EXTERNAL_SENSE_OFFSET_REG, 1, 2, 0);
+	if (rc) {
+		pr_err("Failed to read ext sense offset trim, rc = %d\n", rc);
+		return rc;
+	}
+	rc = fg_masked_write(chip, chip->soc_base + SEC_ACCESS_REG,
+			SEC_ACCESS_UNLOCK, SEC_ACCESS_UNLOCK, 1);
+
+	rc |= fg_masked_write(chip, chip->soc_base + EXT_OFFSET_TRIM_REG,
+			0xFF, reg, 1);
+	if (rc) {
+		pr_err("Failed to write ext sense offset trim, rc = %d\n", rc);
+		return rc;
+	}
+
+	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 KI_COEFF_PRED_FULL_ADDR		0x408
+#define KI_COEFF_PRED_FULL_4_0_MSB	0x88
+#define KI_COEFF_PRED_FULL_4_0_LSB	0x00
+#define TEMP_FRAC_SHIFT_REG		0x4A4
+#define FG_ADC_CONFIG_REG		0x4B8
+#define FG_BCL_CONFIG_OFFSET		0x3
+#define BCL_FORCED_HPM_IN_CHARGE	BIT(2)
+static int fg_common_hw_init(struct fg_chip *chip)
+{
+	int rc;
+	int resume_soc_raw;
+	u8 val;
+
+	update_iterm(chip);
+	update_cutoff_voltage(chip);
+	update_irq_volt_empty(chip);
+	update_bcl_thresholds(chip);
+
+	resume_soc_raw = settings[FG_MEM_RESUME_SOC].value;
+	if (resume_soc_raw > 0) {
+		rc = fg_set_resume_soc(chip, resume_soc_raw);
+		if (rc) {
+			pr_err("Couldn't set resume SOC for FG\n");
+			return rc;
+		}
+	} else {
+		pr_info("FG auto recharge threshold not specified in DT\n");
+	}
+
+	if (fg_sense_type >= 0) {
+		rc = set_prop_sense_type(chip, fg_sense_type);
+		if (rc) {
+			pr_err("failed to config sense type %d rc=%d\n",
+					fg_sense_type, rc);
+			return rc;
+		}
+	}
+
+	rc = fg_mem_masked_write(chip, settings[FG_MEM_DELTA_SOC].address, 0xFF,
+			soc_to_setpoint(settings[FG_MEM_DELTA_SOC].value),
+			settings[FG_MEM_DELTA_SOC].offset);
+	if (rc) {
+		pr_err("failed to write delta soc rc=%d\n", rc);
+		return rc;
+	}
+
+	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);
+	if (rc) {
+		pr_err("failed to write Vbatt_low rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_mem_masked_write(chip, settings[FG_MEM_THERM_DELAY].address,
+		THERM_DELAY_MASK,
+		therm_delay_to_setpoint(settings[FG_MEM_THERM_DELAY].value),
+		settings[FG_MEM_THERM_DELAY].offset);
+	if (rc) {
+		pr_err("failed to write therm_delay rc=%d\n", rc);
+		return rc;
+	}
+
+	if (chip->use_thermal_coefficients) {
+		fg_mem_write(chip, chip->thermal_coefficients,
+			THERMAL_COEFF_ADDR, THERMAL_COEFF_N_BYTES,
+			THERMAL_COEFF_OFFSET, 0);
+	}
+
+	if (!chip->sw_rbias_ctrl) {
+		rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+				BATT_TEMP_CNTRL_MASK,
+				TEMP_SENSE_ALWAYS_BIT,
+				BATT_TEMP_OFFSET);
+		if (rc) {
+			pr_err("failed to write BATT_TEMP_OFFSET rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Read the cycle counter back from FG SRAM */
+	if (chip->cyc_ctr.en)
+		restore_cycle_counter(chip);
+
+	if (chip->esr_pulse_tune_en) {
+		rc = fg_mem_read(chip, &val, SYS_CFG_1_REG, 1, SYS_CFG_1_OFFSET,
+				0);
+		if (rc) {
+			pr_err("unable to read sys_cfg_1: %d\n", rc);
+			return rc;
+		}
+
+		if (!(val & ENABLE_ESR_PULSE_VAL))
+			chip->esr_extract_disabled = true;
+
+		if (fg_debug_mask & FG_STATUS)
+			pr_info("ESR extract is %sabled\n",
+				chip->esr_extract_disabled ? "dis" : "en");
+
+		rc = fg_mem_read(chip, &val, CBITS_INPUT_FILTER_REG, 1,
+				CBITS_RMEAS1_OFFSET, 0);
+		if (rc) {
+			pr_err("unable to read cbits_input_filter_reg: %d\n",
+				rc);
+			return rc;
+		}
+
+		if (val & (IMPTR_FAST_TIME_SHIFT | IMPTR_LONG_TIME_SHIFT))
+			chip->imptr_pulse_slow_en = true;
+
+		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;
+		}
+
+		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");
+	}
+
+	return 0;
+}
+
+static int fg_8994_hw_init(struct fg_chip *chip)
+{
+	int rc = 0;
+	u8 data[4];
+	u64 esr_value;
+
+	rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
+			PATCH_NEG_CURRENT_BIT,
+			PATCH_NEG_CURRENT_BIT,
+			EXTERNAL_SENSE_OFFSET);
+	if (rc) {
+		pr_err("failed to write patch current bit rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = bcl_trim_workaround(chip);
+	if (rc) {
+		pr_err("failed to redo bcl trim rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+			BCL_FORCED_HPM_IN_CHARGE,
+			BCL_FORCED_HPM_IN_CHARGE,
+			FG_BCL_CONFIG_OFFSET);
+	if (rc) {
+		pr_err("failed to force hpm in charge rc=%d\n", rc);
+		return rc;
+	}
+
+	fg_mem_masked_write(chip, FG_ALG_SYSCTL_1, I_TERM_QUAL_BIT, 0, 0);
+
+	data[0] = 0xA2;
+	data[1] = 0x12;
+
+	rc = fg_mem_write(chip, data, TEMP_FRAC_SHIFT_REG, 2, 2, 0);
+	if (rc) {
+		pr_err("failed to write temp ocv constants rc=%d\n", rc);
+		return rc;
+	}
+
+	data[0] = KI_COEFF_PRED_FULL_4_0_LSB;
+	data[1] = KI_COEFF_PRED_FULL_4_0_MSB;
+	fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0);
+
+	esr_value = ESR_DEFAULT_VALUE;
+	rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
+			ESR_VALUE_OFFSET, 0);
+	if (rc)
+		pr_err("failed to write default ESR value rc=%d\n", rc);
+	else
+		pr_debug("set default value to esr filter\n");
+
+	return 0;
+}
+
+#define FG_USBID_CONFIG_OFFSET		0x2
+#define DISABLE_USBID_DETECT_BIT	BIT(0)
+static int fg_8996_hw_init(struct fg_chip *chip)
+{
+	int rc;
+
+	rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+			BCL_FORCED_HPM_IN_CHARGE,
+			BCL_FORCED_HPM_IN_CHARGE,
+			FG_BCL_CONFIG_OFFSET);
+	if (rc) {
+		pr_err("failed to force hpm in charge rc=%d\n", rc);
+		return rc;
+	}
+
+	/* enable usbid conversions for PMi8996 V1.0 */
+	if (chip->pmic_revision[REVID_DIG_MAJOR] == 1
+			&& chip->pmic_revision[REVID_ANA_MAJOR] == 0) {
+		rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+				DISABLE_USBID_DETECT_BIT,
+				0, FG_USBID_CONFIG_OFFSET);
+		if (rc) {
+			pr_err("failed to enable usbid conversions: %d\n", rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int fg_8950_hw_init(struct fg_chip *chip)
+{
+	int rc;
+
+	rc = fg_mem_masked_write(chip, FG_ADC_CONFIG_REG,
+			BCL_FORCED_HPM_IN_CHARGE,
+			BCL_FORCED_HPM_IN_CHARGE,
+			FG_BCL_CONFIG_OFFSET);
+	if (rc)
+		pr_err("failed to force hpm in charge rc=%d\n", rc);
+
+	return rc;
+}
+
+static int fg_hw_init(struct fg_chip *chip)
+{
+	int rc = 0;
+
+	rc = fg_common_hw_init(chip);
+	if (rc) {
+		pr_err("Unable to initialize FG HW rc=%d\n", rc);
+		return rc;
+	}
+
+	/* add PMIC specific hw init */
+	switch (chip->pmic_subtype) {
+	case PMI8994:
+		rc = fg_8994_hw_init(chip);
+		chip->wa_flag |= PULSE_REQUEST_WA;
+		break;
+	case PMI8996:
+		rc = fg_8996_hw_init(chip);
+		/* 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)
+			chip->wa_flag |= USE_CC_SOC_REG;
+
+		break;
+	case PMI8950:
+	case PMI8937:
+		rc = fg_8950_hw_init(chip);
+		/* Setup workaround flag based on PMIC type */
+		chip->wa_flag |= BCL_HI_POWER_FOR_CHGLED_WA;
+		if (fg_sense_type == INTERNAL_CURRENT_SENSE)
+			chip->wa_flag |= IADC_GAIN_COMP_WA;
+		if (chip->pmic_revision[REVID_DIG_MAJOR] > 1)
+			chip->wa_flag |= USE_CC_SOC_REG;
+
+		break;
+	}
+	if (rc)
+		pr_err("Unable to initialize PMIC specific FG HW rc=%d\n", rc);
+
+	pr_debug("wa_flag=0x%x\n", chip->wa_flag);
+
+	return rc;
+}
+
+#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)
+{
+	int rc;
+
+	rc = fg_read(chip, chip->revision, chip->mem_base + DIG_MINOR, 4);
+	if (rc) {
+		pr_err("Unable to read FG revision rc=%d\n", rc);
+		return rc;
+	}
+
+	switch (chip->revision[DIG_MAJOR]) {
+	case DIG_REV_1:
+	case DIG_REV_2:
+		chip->offset = offset[0].address;
+		break;
+	case DIG_REV_3:
+		chip->offset = offset[1].address;
+		chip->ima_supported = true;
+		break;
+	default:
+		pr_err("Digital Major rev=%d not supported\n",
+				chip->revision[DIG_MAJOR]);
+		return -EINVAL;
+	}
+
+	if (chip->ima_supported) {
+		/*
+		 * Change the FG_MEM_INT interrupt to track IACS_READY
+		 * condition instead of end-of-transaction. This makes sure
+		 * that the next transaction starts only after the hw is ready.
+		 */
+		rc = fg_masked_write(chip,
+			chip->mem_base + MEM_INTF_IMA_CFG, IACS_INTR_SRC_SLCT,
+			IACS_INTR_SRC_SLCT, 1);
+		if (rc) {
+			pr_err("failed to configure interrupt source %d\n", rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int fg_detect_pmic_type(struct fg_chip *chip)
+{
+	struct pmic_revid_data *pmic_rev_id;
+	struct device_node *revid_dev_node;
+
+	revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
+					"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	pmic_rev_id = get_revid_data(revid_dev_node);
+	if (IS_ERR_OR_NULL(pmic_rev_id)) {
+		pr_err("Unable to get pmic_revid rc=%ld\n",
+				PTR_ERR(pmic_rev_id));
+		/*
+		 * the revid peripheral must be registered, any failure
+		 * here only indicates that the rev-id module has not
+		 * probed yet.
+		 */
+		return -EPROBE_DEFER;
+	}
+
+	switch (pmic_rev_id->pmic_subtype) {
+	case PMI8994:
+	case PMI8950:
+	case PMI8937:
+	case PMI8996:
+		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;
+		chip->pmic_revision[REVID_ANA_MAJOR]	= pmic_rev_id->rev3;
+		chip->pmic_revision[REVID_DIG_MAJOR]	= pmic_rev_id->rev4;
+		break;
+	default:
+		pr_err("PMIC subtype %d not supported\n",
+				pmic_rev_id->pmic_subtype);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#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,
+				init_work);
+
+	/* hold memory access until initialization finishes */
+	fg_mem_lock(chip);
+
+	rc = fg_hw_init(chip);
+	if (rc) {
+		pr_err("failed to hw init rc = %d\n", rc);
+		fg_mem_release(chip);
+		fg_cleanup(chip);
+		return;
+	}
+	/* release memory access before update_sram_data is called */
+	fg_mem_release(chip);
+
+	schedule_delayed_work(
+		&chip->update_jeita_setting,
+		msecs_to_jiffies(INIT_JEITA_DELAY_MS));
+
+	if (chip->last_sram_update_time == 0)
+		update_sram_data_work(&chip->update_sram_data.work);
+
+	if (chip->last_temp_update_time == 0)
+		update_temp_data(&chip->update_temp_work.work);
+
+	if (!chip->use_otp_profile)
+		schedule_delayed_work(&chip->batt_profile_init, 0);
+
+	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);
+			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);
+	}
+
+	pr_debug("FG: HW_init success\n");
+
+	return;
+done:
+	fg_cleanup(chip);
+}
+
+static int fg_probe(struct platform_device *pdev)
+{
+	struct device *dev = &(pdev->dev);
+	struct fg_chip *chip;
+	struct device_node *child;
+	unsigned int base;
+	u8 subtype, reg;
+	int rc = 0;
+	struct power_supply_config bms_psy_cfg;
+
+	if (!pdev) {
+		pr_err("no valid spmi pointer\n");
+		return -ENODEV;
+	}
+
+	if (!pdev->dev.of_node) {
+		pr_err("device node missing\n");
+		return -ENODEV;
+	}
+
+	chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL);
+	if (chip == NULL)
+		return -ENOMEM;
+
+	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	chip->pdev = pdev;
+	chip->dev = &(pdev->dev);
+
+	wakeup_source_init(&chip->empty_check_wakeup_source.source,
+			"qpnp_fg_empty_check");
+	wakeup_source_init(&chip->memif_wakeup_source.source,
+			"qpnp_fg_memaccess");
+	wakeup_source_init(&chip->profile_wakeup_source.source,
+			"qpnp_fg_profile");
+	wakeup_source_init(&chip->update_temp_wakeup_source.source,
+			"qpnp_fg_update_temp");
+	wakeup_source_init(&chip->update_sram_wakeup_source.source,
+			"qpnp_fg_update_sram");
+	wakeup_source_init(&chip->resume_soc_wakeup_source.source,
+			"qpnp_fg_set_resume_soc");
+	wakeup_source_init(&chip->gain_comp_wakeup_source.source,
+			"qpnp_fg_gain_comp");
+	wakeup_source_init(&chip->capacity_learning_wakeup_source.source,
+			"qpnp_fg_cap_learning");
+	wakeup_source_init(&chip->esr_extract_wakeup_source.source,
+			"qpnp_fg_esr_extract");
+	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);
+	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_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);
+	INIT_WORK(&chip->status_change_work, status_change_work);
+	INIT_WORK(&chip->cycle_count_work, update_cycle_count);
+	INIT_WORK(&chip->battery_age_work, battery_age_work);
+	INIT_WORK(&chip->update_esr_work, update_esr_value);
+	INIT_WORK(&chip->set_resume_soc_work, set_resume_soc_work);
+	INIT_WORK(&chip->sysfs_restart_work, sysfs_restart_work);
+	INIT_WORK(&chip->init_work, delayed_init_work);
+	INIT_WORK(&chip->charge_full_work, charge_full_work);
+	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);
+	alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
+			fg_cap_learning_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);
+	dev_set_drvdata(&pdev->dev, chip);
+
+	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		rc = -ENXIO;
+		goto of_init_fail;
+	}
+
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			goto of_init_fail;
+		}
+
+		if (strcmp("qcom,fg-adc-vbat", child->name) == 0) {
+			chip->vbat_adc_addr = base;
+			continue;
+		} else if (strcmp("qcom,fg-adc-ibat", child->name) == 0) {
+			chip->ibat_adc_addr = base;
+			continue;
+		} else if (strcmp("qcom,revid-tp-rev", child->name) == 0) {
+			chip->tp_rev_addr = base;
+			continue;
+		}
+
+		rc = fg_read(chip, &subtype,
+				base + REG_OFFSET_PERP_SUBTYPE, 1);
+		if (rc) {
+			pr_err("Peripheral subtype read failed rc=%d\n", rc);
+			goto of_init_fail;
+		}
+
+		switch (subtype) {
+		case FG_SOC:
+			chip->soc_base = base;
+			break;
+		case FG_MEMIF:
+			chip->mem_base = base;
+			break;
+		case FG_BATT:
+			chip->batt_base = base;
+			break;
+		default:
+			pr_err("Invalid peripheral subtype=0x%x\n", subtype);
+			rc = -EINVAL;
+		}
+	}
+
+	rc = fg_detect_pmic_type(chip);
+	if (rc) {
+		pr_err("Unable to detect PMIC type rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = fg_setup_memif_offset(chip);
+	if (rc) {
+		pr_err("Unable to setup mem_if offsets rc=%d\n", rc);
+		goto of_init_fail;
+	}
+
+	rc = fg_of_init(chip);
+	if (rc) {
+		pr_err("failed to parse devicetree rc%d\n", rc);
+		goto of_init_fail;
+	}
+
+	if (chip->jeita_hysteresis_support) {
+		rc = fg_init_batt_temp_state(chip);
+		if (rc) {
+			pr_err("failed to get battery status rc%d\n", rc);
+			goto of_init_fail;
+		}
+	}
+
+	/* check if the first estimate is already finished at this time */
+	if (is_first_est_done(chip))
+		complete_all(&chip->first_soc_done);
+
+	reg = 0xFF;
+	rc = fg_write(chip, &reg, INT_EN_CLR(chip->mem_base), 1);
+	if (rc) {
+		pr_err("failed to clear interrupts %d\n", rc);
+		goto of_init_fail;
+	}
+
+	rc = fg_init_irqs(chip);
+	if (rc) {
+		pr_err("failed to request interrupts %d\n", rc);
+		goto cancel_work;
+	}
+
+	chip->batt_type = default_batt_type;
+
+	chip->bms_psy_d.name = "bms";
+	chip->bms_psy_d.type = POWER_SUPPLY_TYPE_BMS;
+	chip->bms_psy_d.properties = fg_power_props;
+	chip->bms_psy_d.num_properties = ARRAY_SIZE(fg_power_props);
+	chip->bms_psy_d.get_property = fg_power_get_property;
+	chip->bms_psy_d.set_property = fg_power_set_property;
+	chip->bms_psy_d.external_power_changed = fg_external_power_changed;
+	chip->bms_psy_d.property_is_writeable = fg_property_is_writeable;
+
+	bms_psy_cfg.drv_data = chip;
+	bms_psy_cfg.supplied_to = fg_supplicants;
+	bms_psy_cfg.num_supplicants =  ARRAY_SIZE(fg_supplicants);
+	bms_psy_cfg.of_node = NULL;
+	chip->bms_psy = devm_power_supply_register(chip->dev,
+			&chip->bms_psy_d,
+			&bms_psy_cfg);
+	if (IS_ERR(chip->bms_psy)) {
+		pr_err("batt failed to register rc = %ld\n",
+				PTR_ERR(chip->bms_psy));
+		goto of_init_fail;
+	}
+	chip->power_supply_registered = true;
+	/*
+	 * Just initialize the batt_psy_name here. Power supply
+	 * will be obtained later.
+	 */
+	chip->batt_psy_name = "battery";
+
+	if (chip->mem_base) {
+		rc = fg_dfs_create(chip);
+		if (rc < 0) {
+			pr_err("failed to create debugfs rc = %d\n", rc);
+			goto cancel_work;
+		}
+	}
+
+	schedule_work(&chip->init_work);
+
+	pr_info("FG Probe success - FG Revision DIG:%d.%d ANA:%d.%d PMIC subtype=%d\n",
+		chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+		chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
+		chip->pmic_subtype);
+
+	return rc;
+
+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);
+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);
+	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);
+	wakeup_source_trash(&chip->profile_wakeup_source.source);
+	wakeup_source_trash(&chip->update_temp_wakeup_source.source);
+	wakeup_source_trash(&chip->update_sram_wakeup_source.source);
+	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);
+	return rc;
+}
+
+static void check_and_update_sram_data(struct fg_chip *chip)
+{
+	unsigned long current_time = 0, next_update_time, time_left;
+
+	get_current_time(&current_time);
+
+	next_update_time = chip->last_temp_update_time
+		+ (TEMP_PERIOD_UPDATE_MS / 1000);
+
+	if (next_update_time > current_time)
+		time_left = next_update_time - current_time;
+	else
+		time_left = 0;
+
+	schedule_delayed_work(
+		&chip->update_temp_work, msecs_to_jiffies(time_left * 1000));
+
+	next_update_time = chip->last_sram_update_time
+		+ (fg_sram_update_period_ms / 1000);
+
+	if (next_update_time > current_time)
+		time_left = next_update_time - current_time;
+	else
+		time_left = 0;
+
+	schedule_delayed_work(
+		&chip->update_sram_data, msecs_to_jiffies(time_left * 1000));
+}
+
+static int fg_suspend(struct device *dev)
+{
+	struct fg_chip *chip = dev_get_drvdata(dev);
+
+	if (!chip->sw_rbias_ctrl)
+		return 0;
+
+	cancel_delayed_work(&chip->update_temp_work);
+	cancel_delayed_work(&chip->update_sram_data);
+
+	return 0;
+}
+
+static int fg_resume(struct device *dev)
+{
+	struct fg_chip *chip = dev_get_drvdata(dev);
+
+	if (!chip->sw_rbias_ctrl)
+		return 0;
+
+	check_and_update_sram_data(chip);
+	return 0;
+}
+
+static const struct dev_pm_ops qpnp_fg_pm_ops = {
+	.suspend	= fg_suspend,
+	.resume		= fg_resume,
+};
+
+static int fg_sense_type_set(const char *val, const struct kernel_param *kp)
+{
+	int rc;
+	struct power_supply *bms_psy;
+	struct fg_chip *chip;
+	int old_fg_sense_type = fg_sense_type;
+
+	rc = param_set_int(val, kp);
+	if (rc) {
+		pr_err("Unable to set fg_sense_type: %d\n", rc);
+		return rc;
+	}
+
+	if (fg_sense_type != 0 && fg_sense_type != 1) {
+		pr_err("Bad value %d\n", fg_sense_type);
+		fg_sense_type = old_fg_sense_type;
+		return -EINVAL;
+	}
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("fg_sense_type set to %d\n", fg_sense_type);
+
+	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);
+	rc = set_prop_sense_type(chip, fg_sense_type);
+	return rc;
+}
+
+static struct kernel_param_ops fg_sense_type_ops = {
+	.set = fg_sense_type_set,
+	.get = param_get_int,
+};
+
+module_param_cb(sense_type, &fg_sense_type_ops, &fg_sense_type, 0644);
+
+static int fg_restart_set(const char *val, const struct kernel_param *kp)
+{
+	struct power_supply *bms_psy;
+	struct fg_chip *chip;
+
+	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);
+
+	mutex_lock(&chip->sysfs_restart_lock);
+	if (fg_restart != 0) {
+		mutex_unlock(&chip->sysfs_restart_lock);
+		return 0;
+	}
+	fg_restart = 1;
+	mutex_unlock(&chip->sysfs_restart_lock);
+
+	if (fg_debug_mask & FG_STATUS)
+		pr_info("fuel gauge restart initiated from sysfs...\n");
+
+	schedule_work(&chip->sysfs_restart_work);
+	return 0;
+}
+
+static struct kernel_param_ops fg_restart_ops = {
+	.set = fg_restart_set,
+	.get = param_get_int,
+};
+
+module_param_cb(restart, &fg_restart_ops, &fg_restart, 0644);
+
+static struct platform_driver fg_driver = {
+	.driver		= {
+		.name		= QPNP_FG_DEV_NAME,
+		.of_match_table	= fg_match_table,
+		.pm		= &qpnp_fg_pm_ops,
+	},
+	.probe		= fg_probe,
+	.remove		= fg_remove,
+};
+
+static int __init fg_init(void)
+{
+	return platform_driver_register(&fg_driver);
+}
+
+static void __exit fg_exit(void)
+{
+	return platform_driver_unregister(&fg_driver);
+}
+
+module_init(fg_init);
+module_exit(fg_exit);
+
+MODULE_DESCRIPTION("QPNP Fuel Gauge Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME);
diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c
index 5304405..ea78ddd3 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -188,6 +188,11 @@
 module_param_named(
 	weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600);
 
+static int __try_sink_enabled = 1;
+module_param_named(
+	try_sink_enabled, __try_sink_enabled, int, 0600
+);
+
 #define MICRO_1P5A		1500000
 #define MICRO_P1A		100000
 #define OTG_DEFAULT_DEGLITCH_TIME_MS	50
@@ -308,8 +313,6 @@
 	chip->dt.auto_recharge_soc = of_property_read_bool(node,
 						"qcom,auto-recharge-soc");
 
-	chg->micro_usb_mode = of_property_read_bool(node, "qcom,micro-usb");
-
 	chg->dcp_icl_ua = chip->dt.usb_icl_ua;
 
 	chg->suspend_input_on_debug_batt = of_property_read_bool(node,
@@ -351,6 +354,7 @@
 	POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
 	POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
 	POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONNECTOR_TYPE,
 };
 
 static int smb2_usb_get_prop(struct power_supply *psy,
@@ -373,9 +377,9 @@
 		if (!val->intval)
 			break;
 
-		if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ||
-			chg->micro_usb_mode) &&
-			chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
+		if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
+		   || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB))
+		   && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
 			val->intval = 0;
 		else
 			val->intval = 1;
@@ -404,7 +408,7 @@
 			val->intval = chg->real_charger_type;
 		break;
 	case POWER_SUPPLY_PROP_TYPEC_MODE:
-		if (chg->micro_usb_mode)
+		if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 			val->intval = POWER_SUPPLY_TYPEC_NONE;
 		else if (chip->bad_part)
 			val->intval = POWER_SUPPLY_TYPEC_SOURCE_DEFAULT;
@@ -412,13 +416,13 @@
 			val->intval = chg->typec_mode;
 		break;
 	case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE:
-		if (chg->micro_usb_mode)
+		if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 			val->intval = POWER_SUPPLY_TYPEC_PR_NONE;
 		else
 			rc = smblib_get_prop_typec_power_role(chg, val);
 		break;
 	case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION:
-		if (chg->micro_usb_mode)
+		if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 			val->intval = 0;
 		else
 			rc = smblib_get_prop_typec_cc_orientation(chg, val);
@@ -466,6 +470,9 @@
 		val->intval = get_client_vote(chg->usb_icl_votable,
 					      USB_PSY_VOTER);
 		break;
+	case POWER_SUPPLY_PROP_CONNECTOR_TYPE:
+		val->intval = chg->connector_type;
+		break;
 	default:
 		pr_err("get prop %d is not supported in usb\n", psp);
 		rc = -EINVAL;
@@ -604,9 +611,9 @@
 		if (!val->intval)
 			break;
 
-		if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT ||
-			chg->micro_usb_mode) &&
-			chg->real_charger_type == POWER_SUPPLY_TYPE_USB)
+		if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
+		   || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB))
+			&& (chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
 			val->intval = 1;
 		else
 			val->intval = 0;
@@ -795,6 +802,7 @@
  *************************/
 
 static enum power_supply_property smb2_dc_props[] = {
+	POWER_SUPPLY_PROP_INPUT_SUSPEND,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_ONLINE,
 	POWER_SUPPLY_PROP_CURRENT_MAX,
@@ -810,6 +818,9 @@
 	int rc = 0;
 
 	switch (psp) {
+	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
+		val->intval = get_effective_result(chg->dc_suspend_votable);
+		break;
 	case POWER_SUPPLY_PROP_PRESENT:
 		rc = smblib_get_prop_dc_present(chg, val);
 		break;
@@ -841,6 +852,10 @@
 	int rc = 0;
 
 	switch (psp) {
+	case POWER_SUPPLY_PROP_INPUT_SUSPEND:
+		rc = vote(chg->dc_suspend_votable, WBC_VOTER,
+				(bool)val->intval, 0);
+		break;
 	case POWER_SUPPLY_PROP_CURRENT_MAX:
 		rc = smblib_set_prop_dc_current_max(chg, val);
 		break;
@@ -1029,7 +1044,10 @@
 		val->intval = 0;
 		break;
 	case POWER_SUPPLY_PROP_DIE_HEALTH:
-		rc = smblib_get_prop_die_health(chg, val);
+		if (chg->die_health == -EINVAL)
+			rc = smblib_get_prop_die_health(chg, val);
+		else
+			val->intval = chg->die_health;
 		break;
 	case POWER_SUPPLY_PROP_DP_DM:
 		val->intval = chg->pulse_cnt;
@@ -1084,14 +1102,8 @@
 		rc = smblib_set_prop_charge_qnovo_enable(chg, val);
 		break;
 	case POWER_SUPPLY_PROP_VOLTAGE_QNOVO:
-		if (val->intval == -EINVAL) {
-			vote(chg->fv_votable, BATT_PROFILE_VOTER,
-					true, chg->batt_profile_fv_uv);
-			vote(chg->fv_votable, QNOVO_VOTER, false, 0);
-		} else {
-			vote(chg->fv_votable, QNOVO_VOTER, true, val->intval);
-			vote(chg->fv_votable, BATT_PROFILE_VOTER, false, 0);
-		}
+		vote(chg->fv_votable, QNOVO_VOTER,
+			(val->intval >= 0), val->intval);
 		break;
 	case POWER_SUPPLY_PROP_CURRENT_QNOVO:
 		vote(chg->pl_disable_votable, PL_QNOVO_VOTER,
@@ -1137,6 +1149,10 @@
 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
 		rc = smblib_set_prop_input_current_limited(chg, val);
 		break;
+	case POWER_SUPPLY_PROP_DIE_HEALTH:
+		chg->die_health = val->intval;
+		power_supply_changed(chg->batt_psy);
+		break;
 	default:
 		rc = -EINVAL;
 	}
@@ -1158,6 +1174,7 @@
 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
 	case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED:
 	case POWER_SUPPLY_PROP_SW_JEITA_ENABLED:
+	case POWER_SUPPLY_PROP_DIE_HEALTH:
 		return 1;
 	default:
 		break;
@@ -1199,7 +1216,7 @@
  * VBUS REGULATOR REGISTRATION *
  ******************************/
 
-struct regulator_ops smb2_vbus_reg_ops = {
+static struct regulator_ops smb2_vbus_reg_ops = {
 	.enable = smblib_vbus_regulator_enable,
 	.disable = smblib_vbus_regulator_disable,
 	.is_enabled = smblib_vbus_regulator_is_enabled,
@@ -1241,7 +1258,7 @@
  * VCONN REGULATOR REGISTRATION *
  ******************************/
 
-struct regulator_ops smb2_vconn_reg_ops = {
+static struct regulator_ops smb2_vconn_reg_ops = {
 	.enable = smblib_vconn_regulator_enable,
 	.disable = smblib_vconn_regulator_disable,
 	.is_enabled = smblib_vconn_regulator_is_enabled,
@@ -1253,7 +1270,7 @@
 	struct regulator_config cfg = {};
 	int rc = 0;
 
-	if (chg->micro_usb_mode)
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 		return 0;
 
 	chg->vconn_vreg = devm_kzalloc(chg->dev, sizeof(*chg->vconn_vreg),
@@ -1548,9 +1565,9 @@
 	vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
 			true, 0);
 	vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER,
-			chg->micro_usb_mode, 0);
+		(chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB), 0);
 	vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER,
-			chg->micro_usb_mode, 0);
+		(chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB), 0);
 
 	/*
 	 * AICL configuration:
@@ -1580,7 +1597,17 @@
 		return rc;
 	}
 
-	if (chg->micro_usb_mode)
+	/* Check USB connector type (typeC/microUSB) */
+	rc = smblib_read(chg, RID_CC_CONTROL_7_0_REG, &val);
+	if (rc < 0) {
+		dev_err(chg->dev, "Couldn't read RID_CC_CONTROL_7_0 rc=%d\n",
+			rc);
+		return rc;
+	}
+	chg->connector_type = (val & EN_MICRO_USB_MODE_BIT) ?
+					POWER_SUPPLY_CONNECTOR_MICRO_USB
+					: POWER_SUPPLY_CONNECTOR_TYPEC;
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 		rc = smb2_disable_typec(chg);
 	else
 		rc = smb2_configure_typec(chg);
@@ -1639,15 +1666,6 @@
 		return rc;
 	}
 
-	/* disable SW STAT override */
-	rc = smblib_masked_write(chg, STAT_CFG_REG,
-				 STAT_SW_OVERRIDE_CFG_BIT, 0);
-	if (rc < 0) {
-		dev_err(chg->dev, "Couldn't disable SW STAT override rc=%d\n",
-			rc);
-		return rc;
-	}
-
 	/* disable h/w autonomous parallel charging control */
 	rc = smblib_masked_write(chg, MISC_CFG_REG,
 				 STAT_PARALLEL_1400MA_EN_CFG_BIT, 0);
@@ -1658,6 +1676,18 @@
 		return rc;
 	}
 
+	/*
+	 * allow DRP.DFP time to exceed by tPDdebounce time.
+	 */
+	rc = smblib_masked_write(chg, TAPER_TIMER_SEL_CFG_REG,
+				TYPEC_DRP_DFP_TIME_CFG_BIT,
+				TYPEC_DRP_DFP_TIME_CFG_BIT);
+	if (rc < 0) {
+		dev_err(chg->dev, "Couldn't configure DRP.DFP time rc=%d\n",
+			rc);
+		return rc;
+	}
+
 	/* configure float charger options */
 	switch (chip->dt.float_option) {
 	case 1:
@@ -2236,9 +2266,11 @@
 	chg->dev = &pdev->dev;
 	chg->param = v1_params;
 	chg->debug_mask = &__debug_mask;
+	chg->try_sink_enabled = &__try_sink_enabled;
 	chg->weak_chg_icl_ua = &__weak_chg_icl_ua;
 	chg->mode = PARALLEL_MASTER;
 	chg->irq_info = smb2_irqs;
+	chg->die_health = -EINVAL;
 	chg->name = "PMI";
 
 	chg->regmap = dev_get_regmap(chg->dev->parent, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-smbcharger.c b/drivers/power/supply/qcom/qpnp-smbcharger.c
new file mode 100644
index 0000000..f9c755c
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-smbcharger.c
@@ -0,0 +1,8472 @@
+/* Copyright (c) 2014-2016 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) "SMBCHG: %s: " fmt, __func__
+
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/gpio.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/power_supply.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/leds.h>
+#include <linux/rtc.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/batterydata-lib.h>
+#include <linux/of_batterydata.h>
+#include <linux/msm_bcl.h>
+#include <linux/ktime.h>
+#include <linux/extcon.h>
+#include <linux/pmic-voter.h>
+
+/* Mask/Bit helpers */
+#define _SMB_MASK(BITS, POS) \
+	((unsigned char)(((1 << (BITS)) - 1) << (POS)))
+#define SMB_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
+		_SMB_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
+				(RIGHT_BIT_POS))
+/* Config registers */
+struct smbchg_regulator {
+	struct regulator_desc	rdesc;
+	struct regulator_dev	*rdev;
+};
+
+struct parallel_usb_cfg {
+	struct power_supply		*psy;
+	int				min_current_thr_ma;
+	int				min_9v_current_thr_ma;
+	int				allowed_lowering_ma;
+	int				current_max_ma;
+	bool				avail;
+	struct mutex			lock;
+	int				initial_aicl_ma;
+	ktime_t				last_disabled;
+	bool				enabled_once;
+};
+
+struct ilim_entry {
+	int vmin_uv;
+	int vmax_uv;
+	int icl_pt_ma;
+	int icl_lv_ma;
+	int icl_hv_ma;
+};
+
+struct ilim_map {
+	int			num;
+	struct ilim_entry	*entries;
+};
+
+struct smbchg_version_tables {
+	const int			*dc_ilim_ma_table;
+	int				dc_ilim_ma_len;
+	const int			*usb_ilim_ma_table;
+	int				usb_ilim_ma_len;
+	const int			*iterm_ma_table;
+	int				iterm_ma_len;
+	const int			*fcc_comp_table;
+	int				fcc_comp_len;
+	const int			*aicl_rerun_period_table;
+	int				aicl_rerun_period_len;
+	int				rchg_thr_mv;
+};
+
+struct smbchg_chip {
+	struct device			*dev;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	int				schg_version;
+
+	/* peripheral register address bases */
+	u16				chgr_base;
+	u16				bat_if_base;
+	u16				usb_chgpth_base;
+	u16				dc_chgpth_base;
+	u16				otg_base;
+	u16				misc_base;
+
+	int				fake_battery_soc;
+	u8				revision[4];
+
+	/* configuration parameters */
+	int				iterm_ma;
+	int				usb_max_current_ma;
+	int				typec_current_ma;
+	int				dc_max_current_ma;
+	int				dc_target_current_ma;
+	int				cfg_fastchg_current_ma;
+	int				fastchg_current_ma;
+	int				vfloat_mv;
+	int				fastchg_current_comp;
+	int				float_voltage_comp;
+	int				resume_delta_mv;
+	int				safety_time;
+	int				prechg_safety_time;
+	int				bmd_pin_src;
+	int				jeita_temp_hard_limit;
+	int				aicl_rerun_period_s;
+	bool				use_vfloat_adjustments;
+	bool				iterm_disabled;
+	bool				bmd_algo_disabled;
+	bool				soft_vfloat_comp_disabled;
+	bool				chg_enabled;
+	bool				charge_unknown_battery;
+	bool				chg_inhibit_en;
+	bool				chg_inhibit_source_fg;
+	bool				low_volt_dcin;
+	bool				cfg_chg_led_support;
+	bool				cfg_chg_led_sw_ctrl;
+	bool				vbat_above_headroom;
+	bool				force_aicl_rerun;
+	bool				hvdcp3_supported;
+	bool				restricted_charging;
+	bool				skip_usb_suspend_for_fake_battery;
+	bool				hvdcp_not_supported;
+	bool				otg_pinctrl;
+	u8				original_usbin_allowance;
+	struct parallel_usb_cfg		parallel;
+	struct delayed_work		parallel_en_work;
+	struct dentry			*debug_root;
+	struct smbchg_version_tables	tables;
+
+	/* wipower params */
+	struct ilim_map			wipower_default;
+	struct ilim_map			wipower_pt;
+	struct ilim_map			wipower_div2;
+	struct qpnp_vadc_chip		*vadc_dev;
+	bool				wipower_dyn_icl_avail;
+	struct ilim_entry		current_ilim;
+	struct mutex			wipower_config;
+	bool				wipower_configured;
+	struct qpnp_adc_tm_btm_param	param;
+
+	/* flash current prediction */
+	int				rpara_uohm;
+	int				rslow_uohm;
+	int				vled_max_uv;
+
+	/* vfloat adjustment */
+	int				max_vbat_sample;
+	int				n_vbat_samples;
+
+	/* status variables */
+	int				wake_reasons;
+	int				previous_soc;
+	int				usb_online;
+	bool				dc_present;
+	bool				usb_present;
+	bool				batt_present;
+	int				otg_retries;
+	ktime_t				otg_enable_time;
+	bool				aicl_deglitch_short;
+	bool				safety_timer_en;
+	bool				aicl_complete;
+	bool				usb_ov_det;
+	bool				otg_pulse_skip_dis;
+	const char			*battery_type;
+	enum power_supply_type		usb_supply_type;
+	bool				very_weak_charger;
+	bool				parallel_charger_detected;
+	bool				chg_otg_enabled;
+	bool				flash_triggered;
+	bool				flash_active;
+	bool				icl_disabled;
+	u32				wa_flags;
+	int				usb_icl_delta;
+	bool				typec_dfp;
+	unsigned int			usb_current_max;
+	unsigned int			usb_health;
+
+	/* jeita and temperature */
+	bool				batt_hot;
+	bool				batt_cold;
+	bool				batt_warm;
+	bool				batt_cool;
+	unsigned int			thermal_levels;
+	unsigned int			therm_lvl_sel;
+	unsigned int			*thermal_mitigation;
+
+	/* irqs */
+	int				batt_hot_irq;
+	int				batt_warm_irq;
+	int				batt_cool_irq;
+	int				batt_cold_irq;
+	int				batt_missing_irq;
+	int				vbat_low_irq;
+	int				chg_hot_irq;
+	int				chg_term_irq;
+	int				taper_irq;
+	bool				taper_irq_enabled;
+	struct mutex			taper_irq_lock;
+	int				recharge_irq;
+	int				fastchg_irq;
+	int				wdog_timeout_irq;
+	int				power_ok_irq;
+	int				dcin_uv_irq;
+	int				usbin_uv_irq;
+	int				usbin_ov_irq;
+	int				src_detect_irq;
+	int				otg_fail_irq;
+	int				otg_oc_irq;
+	int				aicl_done_irq;
+	int				usbid_change_irq;
+	int				chg_error_irq;
+	bool				enable_aicl_wake;
+
+	/* psy */
+	struct power_supply_desc	usb_psy_d;
+	struct power_supply		*usb_psy;
+	struct power_supply_desc	batt_psy_d;
+	struct power_supply		*batt_psy;
+	struct power_supply_desc	dc_psy_d;
+	struct power_supply		*dc_psy;
+	struct power_supply		*bms_psy;
+	struct power_supply		*typec_psy;
+	int				dc_psy_type;
+	const char			*bms_psy_name;
+	const char			*battery_psy_name;
+
+	struct regulator		*dpdm_reg;
+	struct smbchg_regulator		otg_vreg;
+	struct smbchg_regulator		ext_otg_vreg;
+	struct work_struct		usb_set_online_work;
+	struct delayed_work		vfloat_adjust_work;
+	struct delayed_work		hvdcp_det_work;
+	spinlock_t			sec_access_lock;
+	struct mutex			therm_lvl_lock;
+	struct mutex			usb_set_online_lock;
+	struct mutex			pm_lock;
+	/* aicl deglitch workaround */
+	unsigned long			first_aicl_seconds;
+	int				aicl_irq_count;
+	struct mutex			usb_status_lock;
+	bool				hvdcp_3_det_ignore_uv;
+	struct completion		src_det_lowered;
+	struct completion		src_det_raised;
+	struct completion		usbin_uv_lowered;
+	struct completion		usbin_uv_raised;
+	int				pulse_cnt;
+	struct led_classdev		led_cdev;
+	bool				skip_usb_notification;
+	u32				vchg_adc_channel;
+	struct qpnp_vadc_chip		*vchg_vadc_dev;
+
+	/* voters */
+	struct votable			*fcc_votable;
+	struct votable			*usb_icl_votable;
+	struct votable			*dc_icl_votable;
+	struct votable			*usb_suspend_votable;
+	struct votable			*dc_suspend_votable;
+	struct votable			*battchg_suspend_votable;
+	struct votable			*hw_aicl_rerun_disable_votable;
+	struct votable			*hw_aicl_rerun_enable_indirect_votable;
+	struct votable			*aicl_deglitch_short_votable;
+
+	/* extcon for VBUS / ID notification to USB */
+	struct extcon_dev		*extcon;
+};
+
+enum qpnp_schg {
+	QPNP_SCHG,
+	QPNP_SCHG_LITE,
+};
+
+static char *version_str[] = {
+	[QPNP_SCHG]		= "SCHG",
+	[QPNP_SCHG_LITE]	= "SCHG_LITE",
+};
+
+enum pmic_subtype {
+	PMI8994		= 10,
+	PMI8950		= 17,
+	PMI8996		= 19,
+	PMI8937		= 55,
+};
+
+enum smbchg_wa {
+	SMBCHG_AICL_DEGLITCH_WA = BIT(0),
+	SMBCHG_HVDCP_9V_EN_WA	= BIT(1),
+	SMBCHG_USB100_WA = BIT(2),
+	SMBCHG_BATT_OV_WA = BIT(3),
+	SMBCHG_CC_ESR_WA = BIT(4),
+	SMBCHG_FLASH_ICL_DISABLE_WA = BIT(5),
+	SMBCHG_RESTART_WA = BIT(6),
+	SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA = BIT(7),
+};
+
+enum print_reason {
+	PR_REGISTER	= BIT(0),
+	PR_INTERRUPT	= BIT(1),
+	PR_STATUS	= BIT(2),
+	PR_DUMP		= BIT(3),
+	PR_PM		= BIT(4),
+	PR_MISC		= BIT(5),
+	PR_WIPOWER	= BIT(6),
+	PR_TYPEC	= BIT(7),
+};
+
+enum wake_reason {
+	PM_PARALLEL_CHECK = BIT(0),
+	PM_REASON_VFLOAT_ADJUST = BIT(1),
+	PM_ESR_PULSE = BIT(2),
+	PM_PARALLEL_TAPER = BIT(3),
+	PM_DETECT_HVDCP = BIT(4),
+};
+
+/* fcc_voters */
+#define ESR_PULSE_FCC_VOTER	"ESR_PULSE_FCC_VOTER"
+#define BATT_TYPE_FCC_VOTER	"BATT_TYPE_FCC_VOTER"
+#define RESTRICTED_CHG_FCC_VOTER	"RESTRICTED_CHG_FCC_VOTER"
+
+/* ICL VOTERS */
+#define PSY_ICL_VOTER		"PSY_ICL_VOTER"
+#define THERMAL_ICL_VOTER	"THERMAL_ICL_VOTER"
+#define HVDCP_ICL_VOTER		"HVDCP_ICL_VOTER"
+#define USER_ICL_VOTER		"USER_ICL_VOTER"
+#define WEAK_CHARGER_ICL_VOTER	"WEAK_CHARGER_ICL_VOTER"
+#define SW_AICL_ICL_VOTER	"SW_AICL_ICL_VOTER"
+#define CHG_SUSPEND_WORKAROUND_ICL_VOTER "CHG_SUSPEND_WORKAROUND_ICL_VOTER"
+
+/* USB SUSPEND VOTERS */
+/* userspace has suspended charging altogether */
+#define USER_EN_VOTER		"USER_EN_VOTER"
+/*
+ * this specific path has been suspended through the power supply
+ * framework
+ */
+#define POWER_SUPPLY_EN_VOTER	"POWER_SUPPLY_EN_VOTER"
+/*
+ * the usb driver has suspended this path by setting a current limit
+ * of < 2MA
+ */
+#define USB_EN_VOTER		"USB_EN_VOTER"
+/*
+ * the thermal daemon can suspend a charge path when the system
+ * temperature levels rise
+ */
+#define THERMAL_EN_VOTER	"THERMAL_EN_VOTER"
+/*
+ * an external OTG supply is being used, suspend charge path so the
+ * charger does not accidentally try to charge from the external supply.
+ */
+#define OTG_EN_VOTER		"OTG_EN_VOTER"
+/*
+ * the charger is very weak, do not draw any current from it
+ */
+#define WEAK_CHARGER_EN_VOTER	"WEAK_CHARGER_EN_VOTER"
+/*
+ * fake battery voter, if battery id-resistance around 7.5 Kohm
+ */
+#define FAKE_BATTERY_EN_VOTER	"FAKE_BATTERY_EN_VOTER"
+
+/* battchg_enable_voters */
+	/* userspace has disabled battery charging */
+#define BATTCHG_USER_EN_VOTER	"BATTCHG_USER_EN_VOTER"
+	/* battery charging disabled while loading battery profiles */
+#define BATTCHG_UNKNOWN_BATTERY_EN_VOTER "BATTCHG_UNKNOWN_BATTERY_EN_VOTER"
+
+/* hw_aicl_rerun_enable_indirect_voters */
+/* enabled via device tree */
+#define DEFAULT_CONFIG_HW_AICL_VOTER	"DEFAULT_CONFIG_HW_AICL_VOTER"
+/* Varb workaround voter */
+#define VARB_WORKAROUND_VOTER		"VARB_WORKAROUND_VOTER"
+/* SHUTDOWN workaround voter */
+#define SHUTDOWN_WORKAROUND_VOTER	"SHUTDOWN_WORKAROUND_VOTER"
+
+/* hw_aicl_rerun_disable_voters */
+/* the results from enabling clients */
+#define HW_AICL_RERUN_ENABLE_INDIRECT_VOTER	\
+					"HW_AICL_RERUN_ENABLE_INDIRECT_VOTER"
+/* Weak charger voter */
+#define WEAK_CHARGER_HW_AICL_VOTER	"WEAK_CHARGER_HW_AICL_VOTER"
+
+/* aicl_short_deglitch_voters */
+/* Varb workaround voter */
+#define VARB_WORKAROUND_SHORT_DEGLITCH_VOTER		\
+					"VARB_WRKARND_SHORT_DEGLITCH_VOTER"
+/* QC 2.0 */
+#define HVDCP_SHORT_DEGLITCH_VOTER	"HVDCP_SHORT_DEGLITCH_VOTER"
+
+static const unsigned int smbchg_extcon_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_NONE,
+};
+
+static int smbchg_debug_mask;
+module_param_named(
+	debug_mask, smbchg_debug_mask, int, 00600
+);
+
+static int smbchg_parallel_en = 1;
+module_param_named(
+	parallel_en, smbchg_parallel_en, int, 00600
+);
+
+static int smbchg_main_chg_fcc_percent = 50;
+module_param_named(
+	main_chg_fcc_percent, smbchg_main_chg_fcc_percent,
+	int, 00600
+);
+
+static int smbchg_main_chg_icl_percent = 60;
+module_param_named(
+	main_chg_icl_percent, smbchg_main_chg_icl_percent,
+	int, 00600
+);
+
+static int smbchg_default_hvdcp_icl_ma = 1800;
+module_param_named(
+	default_hvdcp_icl_ma, smbchg_default_hvdcp_icl_ma,
+	int, 00600
+);
+
+static int smbchg_default_hvdcp3_icl_ma = 3000;
+module_param_named(
+	default_hvdcp3_icl_ma, smbchg_default_hvdcp3_icl_ma,
+	int, 00600
+);
+
+static int smbchg_default_dcp_icl_ma = 1800;
+module_param_named(
+	default_dcp_icl_ma, smbchg_default_dcp_icl_ma,
+	int, 00600
+);
+
+static int wipower_dyn_icl_en;
+module_param_named(
+	dynamic_icl_wipower_en, wipower_dyn_icl_en,
+	int, 00600
+);
+
+static int wipower_dcin_interval = ADC_MEAS1_INTERVAL_2P0MS;
+module_param_named(
+	wipower_dcin_interval, wipower_dcin_interval,
+	int, 00600
+);
+
+#define WIPOWER_DEFAULT_HYSTERISIS_UV	250000
+static int wipower_dcin_hyst_uv = WIPOWER_DEFAULT_HYSTERISIS_UV;
+module_param_named(
+	wipower_dcin_hyst_uv, wipower_dcin_hyst_uv,
+	int, 00600
+);
+
+#define pr_smb(reason, fmt, ...)				\
+	do {							\
+		if (smbchg_debug_mask & (reason))		\
+			pr_info(fmt, ##__VA_ARGS__);		\
+		else						\
+			pr_debug(fmt, ##__VA_ARGS__);		\
+	} while (0)
+
+#define pr_smb_rt(reason, fmt, ...)					\
+	do {								\
+		if (smbchg_debug_mask & (reason))			\
+			pr_info_ratelimited(fmt, ##__VA_ARGS__);	\
+		else							\
+			pr_debug(fmt, ##__VA_ARGS__);	\
+	} while (0)
+
+static int smbchg_read(struct smbchg_chip *chip, u8 *val,
+			u16 addr, int count)
+{
+	int rc = 0;
+	struct platform_device *pdev = chip->pdev;
+
+	if (addr == 0) {
+		dev_err(chip->dev, "addr cannot be zero addr=0x%02x sid=0x%02x rc=%d\n",
+			addr, to_spmi_device(pdev->dev.parent)->usid, rc);
+		return -EINVAL;
+	}
+
+	rc = regmap_bulk_read(chip->regmap, addr, val, count);
+	if (rc) {
+		dev_err(chip->dev, "spmi read failed addr=0x%02x sid=0x%02x rc=%d\n",
+				addr, to_spmi_device(pdev->dev.parent)->usid,
+			rc);
+		return rc;
+	}
+	return 0;
+}
+
+/*
+ * Writes a register to the specified by the base and limited by the bit mask
+ *
+ * Do not use this function for register writes if possible. Instead use the
+ * smbchg_masked_write function.
+ *
+ * The sec_access_lock must be held for all register writes and this function
+ * does not do that. If this function is used, please hold the spinlock or
+ * random secure access writes may fail.
+ */
+static int smbchg_masked_write_raw(struct smbchg_chip *chip, u16 base, u8 mask,
+									u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(chip->regmap, base, mask, val);
+	if (rc) {
+		dev_err(chip->dev, "spmi write failed: addr=%03X, rc=%d\n",
+				base, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Writes a register to the specified by the base and limited by the bit mask
+ *
+ * This function holds a spin lock to ensure secure access register writes goes
+ * through. If the secure access unlock register is armed, any old register
+ * write can unarm the secure access unlock, causing the next write to fail.
+ *
+ * Note: do not use this for sec_access registers. Instead use the function
+ * below: smbchg_sec_masked_write
+ */
+static int smbchg_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
+								u8 val)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&chip->sec_access_lock, flags);
+	rc = smbchg_masked_write_raw(chip, base, mask, val);
+	spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+
+	return rc;
+}
+
+/*
+ * Unlocks sec access and writes to the register specified.
+ *
+ * This function holds a spin lock to exclude other register writes while
+ * the two writes are taking place.
+ */
+#define SEC_ACCESS_OFFSET	0xD0
+#define SEC_ACCESS_VALUE	0xA5
+#define PERIPHERAL_MASK		0xFF
+static int smbchg_sec_masked_write(struct smbchg_chip *chip, u16 base, u8 mask,
+									u8 val)
+{
+	unsigned long flags;
+	int rc;
+	u16 peripheral_base = base & (~PERIPHERAL_MASK);
+
+	spin_lock_irqsave(&chip->sec_access_lock, flags);
+
+	rc = smbchg_masked_write_raw(chip, peripheral_base + SEC_ACCESS_OFFSET,
+				SEC_ACCESS_VALUE, SEC_ACCESS_VALUE);
+	if (rc) {
+		dev_err(chip->dev, "Unable to unlock sec_access: %d", rc);
+		goto out;
+	}
+
+	rc = smbchg_masked_write_raw(chip, base, mask, val);
+
+out:
+	spin_unlock_irqrestore(&chip->sec_access_lock, flags);
+	return rc;
+}
+
+static void smbchg_stay_awake(struct smbchg_chip *chip, int reason)
+{
+	int reasons;
+
+	mutex_lock(&chip->pm_lock);
+	reasons = chip->wake_reasons | reason;
+	if (reasons != 0 && chip->wake_reasons == 0) {
+		pr_smb(PR_PM, "staying awake: 0x%02x (bit %d)\n",
+				reasons, reason);
+		pm_stay_awake(chip->dev);
+	}
+	chip->wake_reasons = reasons;
+	mutex_unlock(&chip->pm_lock);
+}
+
+static void smbchg_relax(struct smbchg_chip *chip, int reason)
+{
+	int reasons;
+
+	mutex_lock(&chip->pm_lock);
+	reasons = chip->wake_reasons & (~reason);
+	if (reasons == 0 && chip->wake_reasons != 0) {
+		pr_smb(PR_PM, "relaxing: 0x%02x (bit %d)\n",
+				reasons, reason);
+		pm_relax(chip->dev);
+	}
+	chip->wake_reasons = reasons;
+	mutex_unlock(&chip->pm_lock);
+};
+
+enum pwr_path_type {
+	UNKNOWN = 0,
+	PWR_PATH_BATTERY = 1,
+	PWR_PATH_USB = 2,
+	PWR_PATH_DC = 3,
+};
+
+#define PWR_PATH		0x08
+#define PWR_PATH_MASK		0x03
+static enum pwr_path_type smbchg_get_pwr_path(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + PWR_PATH, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read PWR_PATH rc = %d\n", rc);
+		return PWR_PATH_BATTERY;
+	}
+
+	return reg & PWR_PATH_MASK;
+}
+
+#define RID_STS				0xB
+#define RID_MASK			0xF
+#define IDEV_STS			0x8
+#define RT_STS				0x10
+#define USBID_MSB			0xE
+#define USBIN_UV_BIT			BIT(0)
+#define USBIN_OV_BIT			BIT(1)
+#define USBIN_SRC_DET_BIT		BIT(2)
+#define FMB_STS_MASK			SMB_MASK(3, 0)
+#define USBID_GND_THRESHOLD		0x495
+static bool is_otg_present_schg(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+	u8 usbid_reg[2];
+	u16 usbid_val;
+	/*
+	 * After the falling edge of the usbid change interrupt occurs,
+	 * there may still be some time before the ADC conversion for USB RID
+	 * finishes in the fuel gauge. In the worst case, this could be up to
+	 * 15 ms.
+	 *
+	 * Sleep for 20 ms (minimum msleep time) to wait for the conversion to
+	 * finish and the USB RID status register to be updated before trying
+	 * to detect OTG insertions.
+	 */
+
+	msleep(20);
+
+	/*
+	 * There is a problem with USBID conversions on PMI8994 revisions
+	 * 2.0.0. As a workaround, check that the cable is not
+	 * detected as factory test before enabling OTG.
+	 */
+	rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read IDEV_STS rc = %d\n", rc);
+		return false;
+	}
+
+	if ((reg & FMB_STS_MASK) != 0) {
+		pr_smb(PR_STATUS, "IDEV_STS = %02x, not ground\n", reg);
+		return false;
+	}
+
+	rc = smbchg_read(chip, usbid_reg, chip->usb_chgpth_base + USBID_MSB, 2);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read USBID rc = %d\n", rc);
+		return false;
+	}
+	usbid_val = (usbid_reg[0] << 8) | usbid_reg[1];
+
+	if (usbid_val > USBID_GND_THRESHOLD) {
+		pr_smb(PR_STATUS, "USBID = 0x%04x, too high to be ground\n",
+				usbid_val);
+		return false;
+	}
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RID_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev,
+				"Couldn't read usb rid status rc = %d\n", rc);
+		return false;
+	}
+
+	pr_smb(PR_STATUS, "RID_STS = %02x\n", reg);
+
+	return (reg & RID_MASK) == 0;
+}
+
+#define RID_GND_DET_STS			BIT(2)
+static bool is_otg_present_schg_lite(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->otg_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't read otg RT status rc = %d\n", rc);
+		return false;
+	}
+
+	return !!(reg & RID_GND_DET_STS);
+}
+
+static bool is_otg_present(struct smbchg_chip *chip)
+{
+	if (chip->schg_version == QPNP_SCHG_LITE)
+		return is_otg_present_schg_lite(chip);
+
+	return is_otg_present_schg(chip);
+}
+
+#define USBIN_9V			BIT(5)
+#define USBIN_UNREG			BIT(4)
+#define USBIN_LV			BIT(3)
+#define DCIN_9V				BIT(2)
+#define DCIN_UNREG			BIT(1)
+#define DCIN_LV				BIT(0)
+#define INPUT_STS			0x0D
+#define DCIN_UV_BIT			BIT(0)
+#define DCIN_OV_BIT			BIT(1)
+static bool is_dc_present(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->dc_chgpth_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read dc status rc = %d\n", rc);
+		return false;
+	}
+
+	if ((reg & DCIN_UV_BIT) || (reg & DCIN_OV_BIT))
+		return false;
+
+	return true;
+}
+
+static bool is_usb_present(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+		return false;
+	}
+	if (!(reg & USBIN_SRC_DET_BIT) || (reg & USBIN_OV_BIT))
+		return false;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + INPUT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
+		return false;
+	}
+
+	return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
+}
+
+static char *usb_type_str[] = {
+	"SDP",		/* bit 0 */
+	"OTHER",	/* bit 1 */
+	"DCP",		/* bit 2 */
+	"CDP",		/* bit 3 */
+	"NONE",		/* bit 4 error case */
+};
+
+#define N_TYPE_BITS		4
+#define TYPE_BITS_OFFSET	4
+
+static int get_type(u8 type_reg)
+{
+	unsigned long type = type_reg;
+
+	type >>= TYPE_BITS_OFFSET;
+	return find_first_bit(&type, N_TYPE_BITS);
+}
+
+/* helper to return the string of USB type */
+static inline char *get_usb_type_name(int type)
+{
+	return usb_type_str[type];
+}
+
+static enum power_supply_type usb_type_enum[] = {
+	POWER_SUPPLY_TYPE_USB,		/* bit 0 */
+	POWER_SUPPLY_TYPE_USB_DCP,	/* bit 1 */
+	POWER_SUPPLY_TYPE_USB_DCP,	/* bit 2 */
+	POWER_SUPPLY_TYPE_USB_CDP,	/* bit 3 */
+	POWER_SUPPLY_TYPE_USB_DCP,	/* bit 4 error case, report DCP */
+};
+
+/* helper to return enum power_supply_type of USB type */
+static inline enum power_supply_type get_usb_supply_type(int type)
+{
+	return usb_type_enum[type];
+}
+
+static bool is_src_detect_high(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+		return false;
+	}
+	return reg &= USBIN_SRC_DET_BIT;
+}
+
+static void read_usb_type(struct smbchg_chip *chip, char **usb_type_name,
+				enum power_supply_type *usb_supply_type)
+{
+	int rc, type;
+	u8 reg;
+
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low\n");
+		*usb_type_name = "Absent";
+		*usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+		return;
+	}
+
+	rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
+		*usb_type_name = "Other";
+		*usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+		return;
+	}
+	type = get_type(reg);
+	*usb_type_name = get_usb_type_name(type);
+	*usb_supply_type = get_usb_supply_type(type);
+}
+
+#define CHGR_STS			0x0E
+#define BATT_LESS_THAN_2V		BIT(4)
+#define CHG_HOLD_OFF_BIT		BIT(3)
+#define CHG_TYPE_MASK			SMB_MASK(2, 1)
+#define CHG_TYPE_SHIFT			1
+#define BATT_NOT_CHG_VAL		0x0
+#define BATT_PRE_CHG_VAL		0x1
+#define BATT_FAST_CHG_VAL		0x2
+#define BATT_TAPER_CHG_VAL		0x3
+#define CHG_INHIBIT_BIT			BIT(1)
+#define BAT_TCC_REACHED_BIT		BIT(7)
+static int get_prop_batt_status(struct smbchg_chip *chip)
+{
+	int rc, status = POWER_SUPPLY_STATUS_DISCHARGING;
+	u8 reg = 0, chg_type;
+	bool charger_present, chg_inhibit;
+
+	charger_present = is_usb_present(chip) | is_dc_present(chip) |
+			  chip->hvdcp_3_det_ignore_uv;
+	if (!charger_present)
+		return POWER_SUPPLY_STATUS_DISCHARGING;
+
+	rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
+		return POWER_SUPPLY_STATUS_UNKNOWN;
+	}
+
+	if (reg & BAT_TCC_REACHED_BIT)
+		return POWER_SUPPLY_STATUS_FULL;
+
+	chg_inhibit = reg & CHG_INHIBIT_BIT;
+	if (chg_inhibit)
+		return POWER_SUPPLY_STATUS_FULL;
+
+	rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+		return POWER_SUPPLY_STATUS_UNKNOWN;
+	}
+
+	if (reg & CHG_HOLD_OFF_BIT) {
+		/*
+		 * when chg hold off happens the battery is
+		 * not charging
+		 */
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		goto out;
+	}
+
+	chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+
+	if (chg_type == BATT_NOT_CHG_VAL && !chip->hvdcp_3_det_ignore_uv)
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+	else
+		status = POWER_SUPPLY_STATUS_CHARGING;
+out:
+	pr_smb_rt(PR_MISC, "CHGR_STS = 0x%02x\n", reg);
+	return status;
+}
+
+#define BAT_PRES_STATUS			0x08
+#define BAT_PRES_BIT			BIT(7)
+static int get_prop_batt_present(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->bat_if_base + BAT_PRES_STATUS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+		return 0;
+	}
+
+	return !!(reg & BAT_PRES_BIT);
+}
+
+static int get_prop_charge_type(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg, chg_type;
+
+	rc = smbchg_read(chip, &reg, chip->chgr_base + CHGR_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to read CHGR_STS rc = %d\n", rc);
+		return 0;
+	}
+
+	chg_type = (reg & CHG_TYPE_MASK) >> CHG_TYPE_SHIFT;
+	if (chg_type == BATT_NOT_CHG_VAL)
+		return POWER_SUPPLY_CHARGE_TYPE_NONE;
+	else if (chg_type == BATT_TAPER_CHG_VAL)
+		return POWER_SUPPLY_CHARGE_TYPE_TAPER;
+	else if (chg_type == BATT_FAST_CHG_VAL)
+		return POWER_SUPPLY_CHARGE_TYPE_FAST;
+	else if (chg_type == BATT_PRE_CHG_VAL)
+		return POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+
+	return POWER_SUPPLY_CHARGE_TYPE_NONE;
+}
+
+static int set_property_on_fg(struct smbchg_chip *chip,
+		enum power_supply_property prop, int val)
+{
+	int rc;
+	union power_supply_propval ret = {0, };
+
+	if (!chip->bms_psy && chip->bms_psy_name)
+		chip->bms_psy =
+			power_supply_get_by_name((char *)chip->bms_psy_name);
+	if (!chip->bms_psy) {
+		pr_smb(PR_STATUS, "no bms psy found\n");
+		return -EINVAL;
+	}
+
+	ret.intval = val;
+	rc = power_supply_set_property(chip->bms_psy, prop, &ret);
+	if (rc)
+		pr_smb(PR_STATUS,
+			"bms psy does not allow updating prop %d rc = %d\n",
+			prop, rc);
+
+	return rc;
+}
+
+static int get_property_from_fg(struct smbchg_chip *chip,
+		enum power_supply_property prop, int *val)
+{
+	int rc;
+	union power_supply_propval ret = {0, };
+
+	if (!chip->bms_psy && chip->bms_psy_name)
+		chip->bms_psy =
+			power_supply_get_by_name((char *)chip->bms_psy_name);
+	if (!chip->bms_psy) {
+		pr_smb(PR_STATUS, "no bms psy found\n");
+		return -EINVAL;
+	}
+
+	rc = power_supply_get_property(chip->bms_psy, prop, &ret);
+	if (rc) {
+		pr_smb(PR_STATUS,
+			"bms psy doesn't support reading prop %d rc = %d\n",
+			prop, rc);
+		return rc;
+	}
+
+	*val = ret.intval;
+	return rc;
+}
+
+#define DEFAULT_BATT_CAPACITY	50
+static int get_prop_batt_capacity(struct smbchg_chip *chip)
+{
+	int capacity, rc;
+
+	if (chip->fake_battery_soc >= 0)
+		return chip->fake_battery_soc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
+		capacity = DEFAULT_BATT_CAPACITY;
+	}
+	return capacity;
+}
+
+#define DEFAULT_BATT_TEMP		200
+static int get_prop_batt_temp(struct smbchg_chip *chip)
+{
+	int temp, rc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
+		temp = DEFAULT_BATT_TEMP;
+	}
+	return temp;
+}
+
+#define DEFAULT_BATT_CURRENT_NOW	0
+static int get_prop_batt_current_now(struct smbchg_chip *chip)
+{
+	int ua, rc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
+		ua = DEFAULT_BATT_CURRENT_NOW;
+	}
+	return ua;
+}
+
+#define DEFAULT_BATT_VOLTAGE_NOW	0
+static int get_prop_batt_voltage_now(struct smbchg_chip *chip)
+{
+	int uv, rc;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
+		uv = DEFAULT_BATT_VOLTAGE_NOW;
+	}
+	return uv;
+}
+
+#define DEFAULT_BATT_VOLTAGE_MAX_DESIGN	4200000
+static int get_prop_batt_voltage_max_design(struct smbchg_chip *chip)
+{
+	int uv, rc;
+
+	rc = get_property_from_fg(chip,
+			POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, &uv);
+	if (rc) {
+		pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
+		uv = DEFAULT_BATT_VOLTAGE_MAX_DESIGN;
+	}
+	return uv;
+}
+
+static int get_prop_batt_health(struct smbchg_chip *chip)
+{
+	if (chip->batt_hot)
+		return POWER_SUPPLY_HEALTH_OVERHEAT;
+	else if (chip->batt_cold)
+		return POWER_SUPPLY_HEALTH_COLD;
+	else if (chip->batt_warm)
+		return POWER_SUPPLY_HEALTH_WARM;
+	else if (chip->batt_cool)
+		return POWER_SUPPLY_HEALTH_COOL;
+	else
+		return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static void get_property_from_typec(struct smbchg_chip *chip,
+				enum power_supply_property property,
+				union power_supply_propval *prop)
+{
+	int rc;
+
+	rc = power_supply_get_property(chip->typec_psy,
+			property, prop);
+	if (rc)
+		pr_smb(PR_TYPEC,
+			"typec psy doesn't support reading prop %d rc = %d\n",
+			property, rc);
+}
+
+static void update_typec_status(struct smbchg_chip *chip)
+{
+	union power_supply_propval type = {0, };
+	union power_supply_propval capability = {0, };
+
+	get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
+	if (type.intval != POWER_SUPPLY_TYPE_UNKNOWN) {
+		get_property_from_typec(chip,
+				POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
+				&capability);
+		chip->typec_current_ma = capability.intval;
+		pr_smb(PR_TYPEC, "SMB Type-C mode = %d, current=%d\n",
+				type.intval, capability.intval);
+	} else {
+		pr_smb(PR_TYPEC,
+			"typec detection not completed continuing with USB update\n");
+	}
+}
+
+/*
+ * finds the index of the closest value in the array. If there are two that
+ * are equally close, the lower index will be returned
+ */
+static int find_closest_in_array(const int *arr, int len, int val)
+{
+	int i, closest = 0;
+
+	if (len == 0)
+		return closest;
+	for (i = 0; i < len; i++)
+		if (abs(val - arr[i]) < abs(val - arr[closest]))
+			closest = i;
+
+	return closest;
+}
+
+/* finds the index of the closest smaller value in the array. */
+static int find_smaller_in_array(const int *table, int val, int len)
+{
+	int i;
+
+	for (i = len - 1; i >= 0; i--) {
+		if (val >= table[i])
+			break;
+	}
+
+	return i;
+}
+
+static const int iterm_ma_table_8994[] = {
+	300,
+	50,
+	100,
+	150,
+	200,
+	250,
+	500,
+	600
+};
+
+static const int iterm_ma_table_8996[] = {
+	300,
+	50,
+	100,
+	150,
+	200,
+	250,
+	400,
+	500
+};
+
+static const int usb_ilim_ma_table_8994[] = {
+	300,
+	400,
+	450,
+	475,
+	500,
+	550,
+	600,
+	650,
+	700,
+	900,
+	950,
+	1000,
+	1100,
+	1200,
+	1400,
+	1450,
+	1500,
+	1600,
+	1800,
+	1850,
+	1880,
+	1910,
+	1930,
+	1950,
+	1970,
+	2000,
+	2050,
+	2100,
+	2300,
+	2400,
+	2500,
+	3000
+};
+
+static const int usb_ilim_ma_table_8996[] = {
+	300,
+	400,
+	500,
+	600,
+	700,
+	800,
+	900,
+	1000,
+	1100,
+	1200,
+	1300,
+	1400,
+	1450,
+	1500,
+	1550,
+	1600,
+	1700,
+	1800,
+	1900,
+	1950,
+	2000,
+	2050,
+	2100,
+	2200,
+	2300,
+	2400,
+	2500,
+	2600,
+	2700,
+	2800,
+	2900,
+	3000
+};
+
+static int dc_ilim_ma_table_8994[] = {
+	300,
+	400,
+	450,
+	475,
+	500,
+	550,
+	600,
+	650,
+	700,
+	900,
+	950,
+	1000,
+	1100,
+	1200,
+	1400,
+	1450,
+	1500,
+	1600,
+	1800,
+	1850,
+	1880,
+	1910,
+	1930,
+	1950,
+	1970,
+	2000,
+};
+
+static int dc_ilim_ma_table_8996[] = {
+	300,
+	400,
+	500,
+	600,
+	700,
+	800,
+	900,
+	1000,
+	1100,
+	1200,
+	1300,
+	1400,
+	1450,
+	1500,
+	1550,
+	1600,
+	1700,
+	1800,
+	1900,
+	1950,
+	2000,
+	2050,
+	2100,
+	2200,
+	2300,
+	2400,
+};
+
+static const int fcc_comp_table_8994[] = {
+	250,
+	700,
+	900,
+	1200,
+};
+
+static const int fcc_comp_table_8996[] = {
+	250,
+	1100,
+	1200,
+	1500,
+};
+
+static const int aicl_rerun_period[] = {
+	45,
+	90,
+	180,
+	360,
+};
+
+static const int aicl_rerun_period_schg_lite[] = {
+	3,	/* 2.8s  */
+	6,	/* 5.6s  */
+	11,	/* 11.3s */
+	23,	/* 22.5s */
+	45,
+	90,
+	180,
+	360,
+};
+
+static void use_pmi8994_tables(struct smbchg_chip *chip)
+{
+	chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8994;
+	chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8994);
+	chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8994;
+	chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8994);
+	chip->tables.iterm_ma_table = iterm_ma_table_8994;
+	chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8994);
+	chip->tables.fcc_comp_table = fcc_comp_table_8994;
+	chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8994);
+	chip->tables.rchg_thr_mv = 200;
+	chip->tables.aicl_rerun_period_table = aicl_rerun_period;
+	chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
+}
+
+static void use_pmi8996_tables(struct smbchg_chip *chip)
+{
+	chip->tables.usb_ilim_ma_table = usb_ilim_ma_table_8996;
+	chip->tables.usb_ilim_ma_len = ARRAY_SIZE(usb_ilim_ma_table_8996);
+	chip->tables.dc_ilim_ma_table = dc_ilim_ma_table_8996;
+	chip->tables.dc_ilim_ma_len = ARRAY_SIZE(dc_ilim_ma_table_8996);
+	chip->tables.iterm_ma_table = iterm_ma_table_8996;
+	chip->tables.iterm_ma_len = ARRAY_SIZE(iterm_ma_table_8996);
+	chip->tables.fcc_comp_table = fcc_comp_table_8996;
+	chip->tables.fcc_comp_len = ARRAY_SIZE(fcc_comp_table_8996);
+	chip->tables.rchg_thr_mv = 150;
+	chip->tables.aicl_rerun_period_table = aicl_rerun_period;
+	chip->tables.aicl_rerun_period_len = ARRAY_SIZE(aicl_rerun_period);
+}
+
+#define CMD_CHG_REG	0x42
+#define EN_BAT_CHG_BIT		BIT(1)
+static int smbchg_charging_en(struct smbchg_chip *chip, bool en)
+{
+	/* The en bit is configured active low */
+	return smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+			EN_BAT_CHG_BIT, en ? 0 : EN_BAT_CHG_BIT);
+}
+
+#define CMD_IL			0x40
+#define USBIN_SUSPEND_BIT	BIT(4)
+#define CURRENT_100_MA		100
+#define CURRENT_150_MA		150
+#define CURRENT_500_MA		500
+#define CURRENT_900_MA		900
+#define CURRENT_1500_MA		1500
+#define SUSPEND_CURRENT_MA	2
+#define ICL_OVERRIDE_BIT	BIT(2)
+static int smbchg_usb_suspend(struct smbchg_chip *chip, bool suspend)
+{
+	int rc;
+
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+			USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0);
+	if (rc < 0)
+		dev_err(chip->dev, "Couldn't set usb suspend rc = %d\n", rc);
+	return rc;
+}
+
+#define DCIN_SUSPEND_BIT	BIT(3)
+static int smbchg_dc_suspend(struct smbchg_chip *chip, bool suspend)
+{
+	int rc = 0;
+
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+			DCIN_SUSPEND_BIT, suspend ? DCIN_SUSPEND_BIT : 0);
+	if (rc < 0)
+		dev_err(chip->dev, "Couldn't set dc suspend rc = %d\n", rc);
+	return rc;
+}
+
+#define IL_CFG			0xF2
+#define DCIN_INPUT_MASK	SMB_MASK(4, 0)
+static int smbchg_set_dc_current_max(struct smbchg_chip *chip, int current_ma)
+{
+	int i;
+	u8 dc_cur_val;
+
+	i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
+			current_ma, chip->tables.dc_ilim_ma_len);
+
+	if (i < 0) {
+		dev_err(chip->dev, "Cannot find %dma current_table\n",
+				current_ma);
+		return -EINVAL;
+	}
+
+	chip->dc_max_current_ma = chip->tables.dc_ilim_ma_table[i];
+	dc_cur_val = i & DCIN_INPUT_MASK;
+
+	pr_smb(PR_STATUS, "dc current set to %d mA\n",
+			chip->dc_max_current_ma);
+	return smbchg_sec_masked_write(chip, chip->dc_chgpth_base + IL_CFG,
+				DCIN_INPUT_MASK, dc_cur_val);
+}
+
+#define AICL_WL_SEL_CFG			0xF5
+#define AICL_WL_SEL_MASK		SMB_MASK(1, 0)
+#define AICL_WL_SEL_SCHG_LITE_MASK	SMB_MASK(2, 0)
+static int smbchg_set_aicl_rerun_period_s(struct smbchg_chip *chip,
+								int period_s)
+{
+	int i;
+	u8 reg, mask;
+
+	i = find_smaller_in_array(chip->tables.aicl_rerun_period_table,
+			period_s, chip->tables.aicl_rerun_period_len);
+
+	if (i < 0) {
+		dev_err(chip->dev, "Cannot find %ds in aicl rerun period\n",
+				period_s);
+		return -EINVAL;
+	}
+
+	if (chip->schg_version == QPNP_SCHG_LITE)
+		mask = AICL_WL_SEL_SCHG_LITE_MASK;
+	else
+		mask = AICL_WL_SEL_MASK;
+
+	reg = i & mask;
+
+	pr_smb(PR_STATUS, "aicl rerun period set to %ds\n",
+			chip->tables.aicl_rerun_period_table[i]);
+	return smbchg_sec_masked_write(chip,
+			chip->dc_chgpth_base + AICL_WL_SEL_CFG,
+			mask, reg);
+}
+
+static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
+{
+	if (!chip->parallel.avail)
+		return NULL;
+	if (chip->parallel.psy)
+		return chip->parallel.psy;
+	chip->parallel.psy = power_supply_get_by_name("usb-parallel");
+	if (!chip->parallel.psy)
+		pr_smb(PR_STATUS, "parallel charger not found\n");
+	return chip->parallel.psy;
+}
+
+static void smbchg_usb_update_online_work(struct work_struct *work)
+{
+	struct smbchg_chip *chip = container_of(work,
+				struct smbchg_chip,
+				usb_set_online_work);
+	bool user_enabled = !get_client_vote(chip->usb_suspend_votable,
+						USER_EN_VOTER);
+	int online;
+
+	online = user_enabled && chip->usb_present && !chip->very_weak_charger;
+
+	mutex_lock(&chip->usb_set_online_lock);
+	if (chip->usb_online != online) {
+		pr_smb(PR_MISC, "setting usb psy online = %d\n", online);
+		chip->usb_online = online;
+		power_supply_changed(chip->usb_psy);
+	}
+	mutex_unlock(&chip->usb_set_online_lock);
+}
+
+#define CHGPTH_CFG		0xF4
+#define CFG_USB_2_3_SEL_BIT	BIT(7)
+#define CFG_USB_2		0
+#define CFG_USB_3		BIT(7)
+#define USBIN_INPUT_MASK	SMB_MASK(4, 0)
+#define USBIN_MODE_CHG_BIT	BIT(0)
+#define USBIN_LIMITED_MODE	0
+#define USBIN_HC_MODE		BIT(0)
+#define USB51_MODE_BIT		BIT(1)
+#define USB51_100MA		0
+#define USB51_500MA		BIT(1)
+static int smbchg_set_high_usb_chg_current(struct smbchg_chip *chip,
+							int current_ma)
+{
+	int i, rc;
+	u8 usb_cur_val;
+
+	if (current_ma == CURRENT_100_MA) {
+		rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+		if (rc < 0) {
+			pr_err("Couldn't set CFG_USB_2 rc=%d\n", rc);
+			return rc;
+		}
+
+		rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+			USBIN_MODE_CHG_BIT | USB51_MODE_BIT | ICL_OVERRIDE_BIT,
+			USBIN_LIMITED_MODE | USB51_100MA | ICL_OVERRIDE_BIT);
+		if (rc < 0) {
+			pr_err("Couldn't set ICL_OVERRIDE rc=%d\n", rc);
+			return rc;
+		}
+
+		pr_smb(PR_STATUS,
+			"Forcing 100mA current limit\n");
+		chip->usb_max_current_ma = CURRENT_100_MA;
+		return rc;
+	}
+
+	i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
+			current_ma, chip->tables.usb_ilim_ma_len);
+	if (i < 0) {
+		dev_err(chip->dev,
+			"Cannot find %dma current_table using %d\n",
+			current_ma, CURRENT_150_MA);
+
+		rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+		rc |= smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+					USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+					USBIN_LIMITED_MODE | USB51_100MA);
+		if (rc < 0)
+			dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
+					CURRENT_150_MA, rc);
+		else
+			chip->usb_max_current_ma = 150;
+		return rc;
+	}
+
+	usb_cur_val = i & USBIN_INPUT_MASK;
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + IL_CFG,
+				USBIN_INPUT_MASK, usb_cur_val);
+	if (rc < 0) {
+		dev_err(chip->dev, "cannot write to config c rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+				USBIN_MODE_CHG_BIT, USBIN_HC_MODE);
+	if (rc < 0)
+		dev_err(chip->dev, "Couldn't write cfg 5 rc = %d\n", rc);
+	chip->usb_max_current_ma = chip->tables.usb_ilim_ma_table[i];
+	return rc;
+}
+
+/* if APSD results are used
+ *	if SDP is detected it will look at 500mA setting
+ *		if set it will draw 500mA
+ *		if unset it will draw 100mA
+ *	if CDP/DCP it will look at 0x0C setting
+ *		i.e. values in 0x41[1, 0] does not matter
+ */
+static int smbchg_set_usb_current_max(struct smbchg_chip *chip,
+							int current_ma)
+{
+	int rc = 0;
+
+	/*
+	 * if the battery is not present, do not allow the usb ICL to lower in
+	 * order to avoid browning out the device during a hotswap.
+	 */
+	if (!chip->batt_present && current_ma < chip->usb_max_current_ma) {
+		pr_info_ratelimited("Ignoring usb current->%d, battery is absent\n",
+				current_ma);
+		return 0;
+	}
+	pr_smb(PR_STATUS, "USB current_ma = %d\n", current_ma);
+
+	if (current_ma <= SUSPEND_CURRENT_MA) {
+		/* suspend the usb if current <= 2mA */
+		rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, true, 0);
+		chip->usb_max_current_ma = 0;
+		goto out;
+	} else {
+		rc = vote(chip->usb_suspend_votable, USB_EN_VOTER, false, 0);
+	}
+
+	switch (chip->usb_supply_type) {
+	case POWER_SUPPLY_TYPE_USB:
+		if ((current_ma < CURRENT_150_MA) &&
+				(chip->wa_flags & SMBCHG_USB100_WA))
+			current_ma = CURRENT_150_MA;
+
+		if (current_ma < CURRENT_150_MA) {
+			/* force 100mA */
+			rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+			if (rc < 0) {
+				pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+				goto out;
+			}
+			rc = smbchg_masked_write(chip,
+					chip->usb_chgpth_base + CMD_IL,
+					USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+					USBIN_LIMITED_MODE | USB51_100MA);
+			if (rc < 0) {
+				pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+				goto out;
+			}
+			chip->usb_max_current_ma = 100;
+		}
+		/* specific current values */
+		if (current_ma == CURRENT_150_MA) {
+			rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+			if (rc < 0) {
+				pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+				goto out;
+			}
+			rc = smbchg_masked_write(chip,
+					chip->usb_chgpth_base + CMD_IL,
+					USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+					USBIN_LIMITED_MODE | USB51_100MA);
+			if (rc < 0) {
+				pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+				goto out;
+			}
+			chip->usb_max_current_ma = 150;
+		}
+		if (current_ma == CURRENT_500_MA) {
+			rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_2);
+			if (rc < 0) {
+				pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+				goto out;
+			}
+			rc = smbchg_masked_write(chip,
+					chip->usb_chgpth_base + CMD_IL,
+					USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+					USBIN_LIMITED_MODE | USB51_500MA);
+			if (rc < 0) {
+				pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+				goto out;
+			}
+			chip->usb_max_current_ma = 500;
+		}
+		if (current_ma == CURRENT_900_MA) {
+			rc = smbchg_sec_masked_write(chip,
+					chip->usb_chgpth_base + CHGPTH_CFG,
+					CFG_USB_2_3_SEL_BIT, CFG_USB_3);
+			if (rc < 0) {
+				pr_err("Couldn't set CHGPTH_CFG rc = %d\n", rc);
+				goto out;
+			}
+			rc = smbchg_masked_write(chip,
+					chip->usb_chgpth_base + CMD_IL,
+					USBIN_MODE_CHG_BIT | USB51_MODE_BIT,
+					USBIN_LIMITED_MODE | USB51_500MA);
+			if (rc < 0) {
+				pr_err("Couldn't set CMD_IL rc = %d\n", rc);
+				goto out;
+			}
+			chip->usb_max_current_ma = 900;
+		}
+		break;
+	case POWER_SUPPLY_TYPE_USB_CDP:
+		if (current_ma < CURRENT_1500_MA) {
+			/* use override for CDP */
+			rc = smbchg_masked_write(chip,
+					chip->usb_chgpth_base + CMD_IL,
+					ICL_OVERRIDE_BIT, ICL_OVERRIDE_BIT);
+			if (rc < 0)
+				pr_err("Couldn't set override rc = %d\n", rc);
+		}
+		/* fall through */
+	default:
+		rc = smbchg_set_high_usb_chg_current(chip, current_ma);
+		if (rc < 0)
+			pr_err("Couldn't set %dmA rc = %d\n", current_ma, rc);
+		break;
+	}
+
+out:
+	pr_smb(PR_STATUS, "usb type = %d current set to %d mA\n",
+			chip->usb_supply_type, chip->usb_max_current_ma);
+	return rc;
+}
+
+#define USBIN_HVDCP_STS				0x0C
+#define USBIN_HVDCP_SEL_BIT			BIT(4)
+#define USBIN_HVDCP_SEL_9V_BIT			BIT(1)
+#define SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT	BIT(2)
+#define SCHG_LITE_USBIN_HVDCP_SEL_BIT		BIT(0)
+static int smbchg_get_min_parallel_current_ma(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg, hvdcp_sel, hvdcp_sel_9v;
+
+	rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb status rc = %d\n", rc);
+		return 0;
+	}
+	if (chip->schg_version == QPNP_SCHG_LITE) {
+		hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
+		hvdcp_sel_9v = SCHG_LITE_USBIN_HVDCP_SEL_9V_BIT;
+	} else {
+		hvdcp_sel = USBIN_HVDCP_SEL_BIT;
+		hvdcp_sel_9v = USBIN_HVDCP_SEL_9V_BIT;
+	}
+
+	if ((reg & hvdcp_sel) && (reg & hvdcp_sel_9v))
+		return chip->parallel.min_9v_current_thr_ma;
+	return chip->parallel.min_current_thr_ma;
+}
+
+static bool is_hvdcp_present(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg, hvdcp_sel;
+
+	rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+	if (rc < 0) {
+		pr_err("Couldn't read hvdcp status rc = %d\n", rc);
+		return false;
+	}
+
+	pr_smb(PR_STATUS, "HVDCP_STS = 0x%02x\n", reg);
+	/*
+	 * If a valid HVDCP is detected, notify it to the usb_psy only
+	 * if USB is still present.
+	 */
+	if (chip->schg_version == QPNP_SCHG_LITE)
+		hvdcp_sel = SCHG_LITE_USBIN_HVDCP_SEL_BIT;
+	else
+		hvdcp_sel = USBIN_HVDCP_SEL_BIT;
+
+	if ((reg & hvdcp_sel) && is_usb_present(chip))
+		return true;
+
+	return false;
+}
+
+#define FCC_CFG			0xF2
+#define FCC_500MA_VAL		0x4
+#define FCC_MASK		SMB_MASK(4, 0)
+static int smbchg_set_fastchg_current_raw(struct smbchg_chip *chip,
+							int current_ma)
+{
+	int i, rc;
+	u8 cur_val;
+
+	/* the fcc enumerations are the same as the usb currents */
+	i = find_smaller_in_array(chip->tables.usb_ilim_ma_table,
+			current_ma, chip->tables.usb_ilim_ma_len);
+	if (i < 0) {
+		dev_err(chip->dev,
+			"Cannot find %dma current_table using %d\n",
+			current_ma, CURRENT_500_MA);
+
+		rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
+					FCC_MASK,
+					FCC_500MA_VAL);
+		if (rc < 0)
+			dev_err(chip->dev, "Couldn't set %dmA rc=%d\n",
+					CURRENT_500_MA, rc);
+		else
+			chip->fastchg_current_ma = 500;
+		return rc;
+	}
+
+	if (chip->tables.usb_ilim_ma_table[i] == chip->fastchg_current_ma) {
+		pr_smb(PR_STATUS, "skipping fastchg current request: %d\n",
+				chip->fastchg_current_ma);
+		return 0;
+	}
+
+	cur_val = i & FCC_MASK;
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CFG,
+				FCC_MASK, cur_val);
+	if (rc < 0) {
+		dev_err(chip->dev, "cannot write to fcc cfg rc = %d\n", rc);
+		return rc;
+	}
+	pr_smb(PR_STATUS, "fastcharge current requested %d, set to %d\n",
+			current_ma, chip->tables.usb_ilim_ma_table[cur_val]);
+
+	chip->fastchg_current_ma = chip->tables.usb_ilim_ma_table[cur_val];
+	return rc;
+}
+
+#define ICL_STS_1_REG			0x7
+#define ICL_STS_2_REG			0x9
+#define ICL_STS_MASK			0x1F
+#define AICL_SUSP_BIT			BIT(6)
+#define AICL_STS_BIT			BIT(5)
+#define USBIN_SUSPEND_STS_BIT		BIT(3)
+#define USBIN_ACTIVE_PWR_SRC_BIT	BIT(1)
+#define DCIN_ACTIVE_PWR_SRC_BIT		BIT(0)
+#define PARALLEL_REENABLE_TIMER_MS	1000
+#define PARALLEL_CHG_THRESHOLD_CURRENT	1800
+static bool smbchg_is_usbin_active_pwr_src(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + ICL_STS_2_REG, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Could not read usb icl sts 2: %d\n", rc);
+		return false;
+	}
+
+	return !(reg & USBIN_SUSPEND_STS_BIT)
+		&& (reg & USBIN_ACTIVE_PWR_SRC_BIT);
+}
+
+static int smbchg_parallel_usb_charging_en(struct smbchg_chip *chip, bool en)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+
+	if (!parallel_psy || !chip->parallel_charger_detected)
+		return 0;
+
+	pval.intval = en;
+	return power_supply_set_property(parallel_psy,
+		POWER_SUPPLY_PROP_CHARGING_ENABLED, &pval);
+}
+
+#define ESR_PULSE_CURRENT_DELTA_MA	200
+static int smbchg_sw_esr_pulse_en(struct smbchg_chip *chip, bool en)
+{
+	int rc, fg_current_now, icl_ma;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW,
+						&fg_current_now);
+	if (rc) {
+		pr_smb(PR_STATUS, "bms psy does not support OCV\n");
+		return 0;
+	}
+
+	icl_ma = max(chip->iterm_ma + ESR_PULSE_CURRENT_DELTA_MA,
+				fg_current_now - ESR_PULSE_CURRENT_DELTA_MA);
+	rc = vote(chip->fcc_votable, ESR_PULSE_FCC_VOTER, en, icl_ma);
+	if (rc < 0) {
+		pr_err("Couldn't Vote FCC en = %d rc = %d\n", en, rc);
+		return rc;
+	}
+	rc = smbchg_parallel_usb_charging_en(chip, !en);
+	return rc;
+}
+
+#define USB_AICL_CFG				0xF3
+#define AICL_EN_BIT				BIT(2)
+static void smbchg_rerun_aicl(struct smbchg_chip *chip)
+{
+	pr_smb(PR_STATUS, "Rerunning AICL...\n");
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, 0);
+	/* Add a delay so that AICL successfully clears */
+	msleep(50);
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, AICL_EN_BIT);
+}
+
+static void taper_irq_en(struct smbchg_chip *chip, bool en)
+{
+	mutex_lock(&chip->taper_irq_lock);
+	if (en != chip->taper_irq_enabled) {
+		if (en) {
+			enable_irq(chip->taper_irq);
+			enable_irq_wake(chip->taper_irq);
+		} else {
+			disable_irq_wake(chip->taper_irq);
+			disable_irq_nosync(chip->taper_irq);
+		}
+		chip->taper_irq_enabled = en;
+	}
+	mutex_unlock(&chip->taper_irq_lock);
+}
+
+static int smbchg_get_aicl_level_ma(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + ICL_STS_1_REG, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Could not read usb icl sts 1: %d\n", rc);
+		return 0;
+	}
+	if (reg & AICL_SUSP_BIT) {
+		pr_warn("AICL suspended: %02x\n", reg);
+		return 0;
+	}
+	reg &= ICL_STS_MASK;
+	if (reg >= chip->tables.usb_ilim_ma_len) {
+		pr_warn("invalid AICL value: %02x\n", reg);
+		return 0;
+	}
+	return chip->tables.usb_ilim_ma_table[reg];
+}
+
+static void smbchg_parallel_usb_disable(struct smbchg_chip *chip)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	int fcc_ma, usb_icl_ma;
+
+	if (!parallel_psy || !chip->parallel_charger_detected)
+		return;
+	pr_smb(PR_STATUS, "disabling parallel charger\n");
+	chip->parallel.last_disabled = ktime_get_boottime();
+	taper_irq_en(chip, false);
+	chip->parallel.initial_aicl_ma = 0;
+	chip->parallel.current_max_ma = 0;
+	pval.intval = SUSPEND_CURRENT_MA * 1000;
+	power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+					&pval);
+
+	pval.intval = false;
+	power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
+					&pval);
+
+	fcc_ma = get_effective_result_locked(chip->fcc_votable);
+	usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+	if (fcc_ma < 0)
+		pr_err("no voters for fcc, skip it\n");
+	else
+		smbchg_set_fastchg_current_raw(chip, fcc_ma);
+
+	if (usb_icl_ma < 0)
+		pr_err("no voters for usb_icl, skip it\n");
+	else
+		smbchg_set_usb_current_max(chip, usb_icl_ma);
+
+	smbchg_rerun_aicl(chip);
+}
+
+#define PARALLEL_TAPER_MAX_TRIES		3
+#define PARALLEL_FCC_PERCENT_REDUCTION		75
+#define MINIMUM_PARALLEL_FCC_MA			500
+#define CHG_ERROR_BIT		BIT(0)
+#define BAT_TAPER_MODE_BIT	BIT(6)
+static void smbchg_parallel_usb_taper(struct smbchg_chip *chip)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	int parallel_fcc_ma, tries = 0;
+	u8 reg = 0;
+
+	if (!parallel_psy || !chip->parallel_charger_detected)
+		return;
+
+	smbchg_stay_awake(chip, PM_PARALLEL_TAPER);
+try_again:
+	mutex_lock(&chip->parallel.lock);
+	if (chip->parallel.current_max_ma == 0) {
+		pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
+		goto done;
+	}
+	power_supply_get_property(parallel_psy,
+			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+	tries += 1;
+	parallel_fcc_ma = pval.intval / 1000;
+	pr_smb(PR_STATUS, "try #%d parallel charger fcc = %d\n",
+			tries, parallel_fcc_ma);
+	if (parallel_fcc_ma < MINIMUM_PARALLEL_FCC_MA
+				|| tries > PARALLEL_TAPER_MAX_TRIES) {
+		smbchg_parallel_usb_disable(chip);
+		goto done;
+	}
+	pval.intval = ((parallel_fcc_ma
+			* PARALLEL_FCC_PERCENT_REDUCTION) / 100);
+	pr_smb(PR_STATUS, "reducing FCC of parallel charger to %d\n",
+		pval.intval);
+	/* Change it to uA */
+	pval.intval *= 1000;
+	power_supply_set_property(parallel_psy,
+			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+	/*
+	 * sleep here for 100 ms in order to make sure the charger has a chance
+	 * to go back into constant current charging
+	 */
+	mutex_unlock(&chip->parallel.lock);
+	msleep(100);
+
+	mutex_lock(&chip->parallel.lock);
+	if (chip->parallel.current_max_ma == 0) {
+		pr_smb(PR_STATUS, "Not parallel charging, skipping\n");
+		goto done;
+	}
+	smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
+	if (reg & BAT_TAPER_MODE_BIT) {
+		mutex_unlock(&chip->parallel.lock);
+		goto try_again;
+	}
+	taper_irq_en(chip, true);
+done:
+	mutex_unlock(&chip->parallel.lock);
+	smbchg_relax(chip, PM_PARALLEL_TAPER);
+}
+
+static void smbchg_parallel_usb_enable(struct smbchg_chip *chip,
+		int total_current_ma)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	int new_parallel_cl_ma, set_parallel_cl_ma, new_pmi_cl_ma, rc;
+	int current_table_index, target_icl_ma;
+	int fcc_ma, main_fastchg_current_ma;
+	int target_parallel_fcc_ma, supplied_parallel_fcc_ma;
+	int parallel_chg_fcc_percent;
+
+	if (!parallel_psy || !chip->parallel_charger_detected)
+		return;
+
+	pr_smb(PR_STATUS, "Attempting to enable parallel charger\n");
+	pval.intval = chip->vfloat_mv + 50;
+	rc = power_supply_set_property(parallel_psy,
+			POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't set Vflt on parallel psy rc: %d\n", rc);
+		return;
+	}
+	/* Set USB ICL */
+	target_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+	if (target_icl_ma < 0) {
+		pr_err("no voters for usb_icl, skip it\n");
+		return;
+	}
+	new_parallel_cl_ma = total_current_ma
+			* (100 - smbchg_main_chg_icl_percent) / 100;
+	taper_irq_en(chip, true);
+
+	pval.intval = true;
+	power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_PRESENT,
+			&pval);
+
+	pval.intval = new_parallel_cl_ma * 1000;
+	power_supply_set_property(parallel_psy, POWER_SUPPLY_PROP_CURRENT_MAX,
+			&pval);
+
+	/* read back the real amount of current we are getting */
+	power_supply_get_property(parallel_psy,
+			POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+	set_parallel_cl_ma = pval.intval / 1000;
+	chip->parallel.current_max_ma = new_parallel_cl_ma;
+	pr_smb(PR_MISC, "Requested ICL = %d from parallel, got %d\n",
+		new_parallel_cl_ma, set_parallel_cl_ma);
+	new_pmi_cl_ma = max(0, target_icl_ma - set_parallel_cl_ma);
+	pr_smb(PR_STATUS, "New Total USB current = %d[%d, %d]\n",
+		total_current_ma, new_pmi_cl_ma,
+		set_parallel_cl_ma);
+	smbchg_set_usb_current_max(chip, new_pmi_cl_ma);
+
+	/* begin splitting the fast charge current */
+	fcc_ma = get_effective_result_locked(chip->fcc_votable);
+	if (fcc_ma < 0) {
+		pr_err("no voters for fcc, skip it\n");
+		return;
+	}
+	parallel_chg_fcc_percent = 100 - smbchg_main_chg_fcc_percent;
+	target_parallel_fcc_ma = (fcc_ma * parallel_chg_fcc_percent) / 100;
+	pval.intval = target_parallel_fcc_ma * 1000;
+	power_supply_set_property(parallel_psy,
+			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+	/* check how much actual current is supplied by the parallel charger */
+	power_supply_get_property(parallel_psy,
+			POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &pval);
+	supplied_parallel_fcc_ma = pval.intval / 1000;
+	pr_smb(PR_MISC, "Requested FCC = %d from parallel, got %d\n",
+		target_parallel_fcc_ma, supplied_parallel_fcc_ma);
+
+	/* then for the main charger, use the left over FCC */
+	current_table_index = find_smaller_in_array(
+			chip->tables.usb_ilim_ma_table,
+			fcc_ma - supplied_parallel_fcc_ma,
+			chip->tables.usb_ilim_ma_len);
+	main_fastchg_current_ma =
+		chip->tables.usb_ilim_ma_table[current_table_index];
+	smbchg_set_fastchg_current_raw(chip, main_fastchg_current_ma);
+	pr_smb(PR_STATUS, "FCC = %d[%d, %d]\n", fcc_ma, main_fastchg_current_ma,
+					supplied_parallel_fcc_ma);
+
+	chip->parallel.enabled_once = true;
+}
+
+static bool smbchg_is_parallel_usb_ok(struct smbchg_chip *chip,
+		int *ret_total_current_ma)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	int min_current_thr_ma, rc, type;
+	int total_current_ma, current_limit_ma, parallel_cl_ma;
+	ktime_t kt_since_last_disable;
+	u8 reg;
+	int fcc_ma = get_effective_result_locked(chip->fcc_votable);
+	const char *fcc_voter
+		= get_effective_client_locked(chip->fcc_votable);
+	int usb_icl_ma = get_effective_result_locked(chip->usb_icl_votable);
+
+	if (!parallel_psy || !smbchg_parallel_en
+			|| !chip->parallel_charger_detected) {
+		pr_smb(PR_STATUS, "Parallel charging not enabled\n");
+		return false;
+	}
+
+	if (fcc_ma < 0) {
+		pr_err("no voters for fcc! Can't enable parallel\n");
+		return false;
+	}
+	if (usb_icl_ma < 0) {
+		pr_err("no voters for usb_icl, Can't enable parallel\n");
+		return false;
+	}
+
+	kt_since_last_disable = ktime_sub(ktime_get_boottime(),
+					chip->parallel.last_disabled);
+	if (chip->parallel.current_max_ma == 0
+		&& chip->parallel.enabled_once
+		&& ktime_to_ms(kt_since_last_disable)
+			< PARALLEL_REENABLE_TIMER_MS) {
+		pr_smb(PR_STATUS, "Only been %lld since disable, skipping\n",
+				ktime_to_ms(kt_since_last_disable));
+		return false;
+	}
+
+	/*
+	 * If the battery is not present, try not to change parallel charging
+	 * from OFF to ON or from ON to OFF, as it could cause the device to
+	 * brown out in the instant that the USB settings are changed.
+	 *
+	 * Only allow parallel charging check to report false (thereby turnin
+	 * off parallel charging) if the battery is still there, or if parallel
+	 * charging is disabled in the first place.
+	 */
+	if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST
+			&& (get_prop_batt_present(chip)
+				|| chip->parallel.current_max_ma == 0)) {
+		pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
+		return false;
+	}
+
+	if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
+		pr_smb(PR_STATUS, "JEITA active, skipping\n");
+		return false;
+	}
+
+	rc = smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
+		return false;
+	}
+
+	type = get_type(reg);
+	if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB_CDP) {
+		pr_smb(PR_STATUS, "CDP adapter, skipping\n");
+		return false;
+	}
+
+	if (get_usb_supply_type(type) == POWER_SUPPLY_TYPE_USB) {
+		pr_smb(PR_STATUS, "SDP adapter, skipping\n");
+		return false;
+	}
+
+	/*
+	 * If USBIN is suspended or not the active power source, do not enable
+	 * parallel charging. The device may be charging off of DCIN.
+	 */
+	if (!smbchg_is_usbin_active_pwr_src(chip)) {
+		pr_smb(PR_STATUS, "USB not active power source: %02x\n", reg);
+		return false;
+	}
+
+	min_current_thr_ma = smbchg_get_min_parallel_current_ma(chip);
+	if (min_current_thr_ma <= 0) {
+		pr_smb(PR_STATUS, "parallel charger unavailable for thr: %d\n",
+				min_current_thr_ma);
+		return false;
+	}
+
+	if (usb_icl_ma < min_current_thr_ma) {
+		pr_smb(PR_STATUS, "Weak USB chg skip enable: %d < %d\n",
+			usb_icl_ma, min_current_thr_ma);
+		return false;
+	}
+
+	if (!fcc_voter)
+		return false;
+	/*
+	 * Suspend the parallel charger if the charging current is < 1800 mA
+	 * and is not because of an ESR pulse.
+	 */
+	if ((strcmp(fcc_voter, ESR_PULSE_FCC_VOTER) == 0)
+			&& fcc_ma < PARALLEL_CHG_THRESHOLD_CURRENT) {
+		pr_smb(PR_STATUS, "FCC %d lower than %d\n",
+			fcc_ma,
+			PARALLEL_CHG_THRESHOLD_CURRENT);
+		return false;
+	}
+
+	current_limit_ma = smbchg_get_aicl_level_ma(chip);
+	if (current_limit_ma <= 0)
+		return false;
+
+	if (chip->parallel.initial_aicl_ma == 0) {
+		if (current_limit_ma < min_current_thr_ma) {
+			pr_smb(PR_STATUS, "Initial AICL very low: %d < %d\n",
+				current_limit_ma, min_current_thr_ma);
+			return false;
+		}
+		chip->parallel.initial_aicl_ma = current_limit_ma;
+	}
+
+	power_supply_get_property(parallel_psy,
+			POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
+	parallel_cl_ma = pval.intval / 1000;
+	/*
+	 * Read back the real amount of current we are getting
+	 * Treat 2mA as 0 because that is the suspend current setting
+	 */
+	if (parallel_cl_ma <= SUSPEND_CURRENT_MA)
+		parallel_cl_ma = 0;
+
+	/*
+	 * Set the parallel charge path's input current limit (ICL)
+	 * to the total current / 2
+	 */
+	total_current_ma = min(current_limit_ma + parallel_cl_ma, usb_icl_ma);
+
+	if (total_current_ma < chip->parallel.initial_aicl_ma
+			- chip->parallel.allowed_lowering_ma) {
+		pr_smb(PR_STATUS,
+			"Total current reduced a lot: %d (%d + %d) < %d - %d\n",
+			total_current_ma,
+			current_limit_ma, parallel_cl_ma,
+			chip->parallel.initial_aicl_ma,
+			chip->parallel.allowed_lowering_ma);
+		return false;
+	}
+
+	*ret_total_current_ma = total_current_ma;
+	return true;
+}
+
+#define PARALLEL_CHARGER_EN_DELAY_MS	500
+static void smbchg_parallel_usb_en_work(struct work_struct *work)
+{
+	struct smbchg_chip *chip = container_of(work,
+				struct smbchg_chip,
+				parallel_en_work.work);
+	int previous_aicl_ma, total_current_ma, aicl_ma;
+	bool in_progress;
+
+	/* do a check to see if the aicl is stable */
+	previous_aicl_ma = smbchg_get_aicl_level_ma(chip);
+	msleep(PARALLEL_CHARGER_EN_DELAY_MS);
+	aicl_ma = smbchg_get_aicl_level_ma(chip);
+	if (previous_aicl_ma == aicl_ma) {
+		pr_smb(PR_STATUS, "AICL at %d\n", aicl_ma);
+	} else {
+		pr_smb(PR_STATUS,
+			"AICL changed [%d -> %d], recheck %d ms\n",
+			previous_aicl_ma, aicl_ma,
+			PARALLEL_CHARGER_EN_DELAY_MS);
+		goto recheck;
+	}
+
+	mutex_lock(&chip->parallel.lock);
+	in_progress = (chip->parallel.current_max_ma != 0);
+	if (smbchg_is_parallel_usb_ok(chip, &total_current_ma)) {
+		smbchg_parallel_usb_enable(chip, total_current_ma);
+	} else {
+		if (in_progress) {
+			pr_smb(PR_STATUS, "parallel charging unavailable\n");
+			smbchg_parallel_usb_disable(chip);
+		}
+	}
+	mutex_unlock(&chip->parallel.lock);
+	smbchg_relax(chip, PM_PARALLEL_CHECK);
+	return;
+
+recheck:
+	schedule_delayed_work(&chip->parallel_en_work, 0);
+}
+
+static void smbchg_parallel_usb_check_ok(struct smbchg_chip *chip)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+
+	if (!parallel_psy || !chip->parallel_charger_detected)
+		return;
+
+	smbchg_stay_awake(chip, PM_PARALLEL_CHECK);
+	schedule_delayed_work(&chip->parallel_en_work, 0);
+}
+
+static int charging_suspend_vote_cb(struct votable *votable, void *data,
+						int suspend,
+						const char *client)
+{
+	int rc;
+	struct smbchg_chip *chip = data;
+
+	if (suspend < 0) {
+		pr_err("No voters\n");
+		suspend = false;
+	}
+
+	rc = smbchg_charging_en(chip, !suspend);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't configure batt chg: 0x%x rc = %d\n",
+			!suspend, rc);
+	}
+
+	return rc;
+}
+
+static int usb_suspend_vote_cb(struct votable *votable,
+						void *data,
+						int suspend,
+						const char *client)
+{
+	int rc;
+	struct smbchg_chip *chip = data;
+
+	if (suspend < 0) {
+		pr_err("No voters\n");
+		suspend = false;
+	}
+
+	rc = smbchg_usb_suspend(chip, suspend);
+	if (rc < 0)
+		return rc;
+
+	if ((strcmp(client, THERMAL_EN_VOTER) == 0)
+		|| (strcmp(client, POWER_SUPPLY_EN_VOTER) == 0)
+		|| (strcmp(client, USER_EN_VOTER) == 0)
+		|| (strcmp(client, FAKE_BATTERY_EN_VOTER) == 0))
+		smbchg_parallel_usb_check_ok(chip);
+
+	return rc;
+}
+
+static int dc_suspend_vote_cb(struct votable *votable,
+						void *data,
+						int suspend,
+						const char *client)
+{
+	int rc;
+	struct smbchg_chip *chip = data;
+
+	if (suspend < 0) {
+		pr_err("No voters\n");
+		suspend = false;
+	}
+
+	rc = smbchg_dc_suspend(chip, suspend);
+	if (rc < 0)
+		return rc;
+
+	if (chip->dc_psy_type != -EINVAL && chip->dc_psy)
+		power_supply_changed(chip->dc_psy);
+
+	return rc;
+}
+
+static int set_fastchg_current_vote_cb(struct votable *votable,
+						void *data,
+						int fcc_ma,
+						const char *client)
+{
+	struct smbchg_chip *chip = data;
+	int rc;
+
+	if (fcc_ma < 0) {
+		pr_err("No voters\n");
+		return 0;
+	}
+
+	if (chip->parallel.current_max_ma == 0) {
+		rc = smbchg_set_fastchg_current_raw(chip, fcc_ma);
+		if (rc < 0) {
+			pr_err("Can't set FCC fcc_ma=%d rc=%d\n", fcc_ma, rc);
+			return rc;
+		}
+	}
+	/*
+	 * check if parallel charging can be enabled, and if enabled,
+	 * distribute the fcc
+	 */
+	smbchg_parallel_usb_check_ok(chip);
+	return 0;
+}
+
+static int smbchg_set_fastchg_current_user(struct smbchg_chip *chip,
+							int current_ma)
+{
+	int rc = 0;
+
+	pr_smb(PR_STATUS, "User setting FCC to %d\n", current_ma);
+
+	rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true, current_ma);
+	if (rc < 0)
+		pr_err("Couldn't vote en rc %d\n", rc);
+	return rc;
+}
+
+static struct ilim_entry *smbchg_wipower_find_entry(struct smbchg_chip *chip,
+				struct ilim_map *map, int uv)
+{
+	int i;
+	struct ilim_entry *ret = &(chip->wipower_default.entries[0]);
+
+	for (i = 0; i < map->num; i++) {
+		if (is_between(map->entries[i].vmin_uv, map->entries[i].vmax_uv,
+			uv))
+			ret = &map->entries[i];
+	}
+	return ret;
+}
+
+#define ZIN_ICL_PT	0xFC
+#define ZIN_ICL_LV	0xFD
+#define ZIN_ICL_HV	0xFE
+#define ZIN_ICL_MASK	SMB_MASK(4, 0)
+static int smbchg_dcin_ilim_config(struct smbchg_chip *chip, int offset, int ma)
+{
+	int i, rc;
+
+	i = find_smaller_in_array(chip->tables.dc_ilim_ma_table,
+			ma, chip->tables.dc_ilim_ma_len);
+
+	if (i < 0)
+		i = 0;
+
+	rc = smbchg_sec_masked_write(chip, chip->bat_if_base + offset,
+			ZIN_ICL_MASK, i);
+	if (rc)
+		dev_err(chip->dev, "Couldn't write bat if offset %d value = %d rc = %d\n",
+				offset, i, rc);
+	return rc;
+}
+
+static int smbchg_wipower_ilim_config(struct smbchg_chip *chip,
+						struct ilim_entry *ilim)
+{
+	int rc = 0;
+
+	if (chip->current_ilim.icl_pt_ma != ilim->icl_pt_ma) {
+		rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_PT, ilim->icl_pt_ma);
+		if (rc)
+			dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+					ZIN_ICL_PT, ilim->icl_pt_ma, rc);
+		else
+			chip->current_ilim.icl_pt_ma =  ilim->icl_pt_ma;
+	}
+
+	if (chip->current_ilim.icl_lv_ma !=  ilim->icl_lv_ma) {
+		rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_LV, ilim->icl_lv_ma);
+		if (rc)
+			dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+					ZIN_ICL_LV, ilim->icl_lv_ma, rc);
+		else
+			chip->current_ilim.icl_lv_ma =  ilim->icl_lv_ma;
+	}
+
+	if (chip->current_ilim.icl_hv_ma !=  ilim->icl_hv_ma) {
+		rc = smbchg_dcin_ilim_config(chip, ZIN_ICL_HV, ilim->icl_hv_ma);
+		if (rc)
+			dev_err(chip->dev, "failed to write batif offset %d %dma rc = %d\n",
+					ZIN_ICL_HV, ilim->icl_hv_ma, rc);
+		else
+			chip->current_ilim.icl_hv_ma =  ilim->icl_hv_ma;
+	}
+	return rc;
+}
+
+static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx);
+static int smbchg_wipower_dcin_btm_configure(struct smbchg_chip *chip,
+		struct ilim_entry *ilim)
+{
+	int rc;
+
+	if (ilim->vmin_uv == chip->current_ilim.vmin_uv
+			&& ilim->vmax_uv == chip->current_ilim.vmax_uv)
+		return 0;
+
+	chip->param.channel = DCIN;
+	chip->param.btm_ctx = chip;
+	if (wipower_dcin_interval < ADC_MEAS1_INTERVAL_0MS)
+		wipower_dcin_interval = ADC_MEAS1_INTERVAL_0MS;
+
+	if (wipower_dcin_interval > ADC_MEAS1_INTERVAL_16S)
+		wipower_dcin_interval = ADC_MEAS1_INTERVAL_16S;
+
+	chip->param.timer_interval = wipower_dcin_interval;
+	chip->param.threshold_notification = &btm_notify_dcin;
+	chip->param.high_thr = ilim->vmax_uv + wipower_dcin_hyst_uv;
+	chip->param.low_thr = ilim->vmin_uv - wipower_dcin_hyst_uv;
+	chip->param.state_request = ADC_TM_HIGH_LOW_THR_ENABLE;
+	rc = qpnp_vadc_channel_monitor(chip->vadc_dev, &chip->param);
+	if (rc) {
+		dev_err(chip->dev, "Couldn't configure btm for dcin rc = %d\n",
+				rc);
+	} else {
+		chip->current_ilim.vmin_uv = ilim->vmin_uv;
+		chip->current_ilim.vmax_uv = ilim->vmax_uv;
+		pr_smb(PR_STATUS, "btm ilim = (%duV %duV %dmA %dmA %dmA)\n",
+			ilim->vmin_uv, ilim->vmax_uv,
+			ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+	}
+	return rc;
+}
+
+static int smbchg_wipower_icl_configure(struct smbchg_chip *chip,
+						int dcin_uv, bool div2)
+{
+	int rc = 0;
+	struct ilim_map *map = div2 ? &chip->wipower_div2 : &chip->wipower_pt;
+	struct ilim_entry *ilim = smbchg_wipower_find_entry(chip, map, dcin_uv);
+
+	rc = smbchg_wipower_ilim_config(chip, ilim);
+	if (rc) {
+		dev_err(chip->dev, "failed to config ilim rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
+			rc, dcin_uv, div2,
+			ilim->vmin_uv, ilim->vmax_uv,
+			ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+		return rc;
+	}
+
+	rc = smbchg_wipower_dcin_btm_configure(chip, ilim);
+	if (rc) {
+		dev_err(chip->dev, "failed to config btm rc = %d, dcin_uv = %d , div2 = %d, ilim = (%duV %duV %dmA %dmA %dmA)\n",
+			rc, dcin_uv, div2,
+			ilim->vmin_uv, ilim->vmax_uv,
+			ilim->icl_pt_ma, ilim->icl_lv_ma, ilim->icl_hv_ma);
+		return rc;
+	}
+	chip->wipower_configured = true;
+	return 0;
+}
+
+static void smbchg_wipower_icl_deconfigure(struct smbchg_chip *chip)
+{
+	int rc;
+	struct ilim_entry *ilim = &(chip->wipower_default.entries[0]);
+
+	if (!chip->wipower_configured)
+		return;
+
+	rc = smbchg_wipower_ilim_config(chip, ilim);
+	if (rc)
+		dev_err(chip->dev, "Couldn't config default ilim rc = %d\n",
+				rc);
+
+	rc = qpnp_vadc_end_channel_monitor(chip->vadc_dev);
+	if (rc)
+		dev_err(chip->dev, "Couldn't de configure btm for dcin rc = %d\n",
+				rc);
+
+	chip->wipower_configured = false;
+	chip->current_ilim.vmin_uv = 0;
+	chip->current_ilim.vmax_uv = 0;
+	chip->current_ilim.icl_pt_ma = ilim->icl_pt_ma;
+	chip->current_ilim.icl_lv_ma = ilim->icl_lv_ma;
+	chip->current_ilim.icl_hv_ma = ilim->icl_hv_ma;
+	pr_smb(PR_WIPOWER, "De config btm\n");
+}
+
+#define FV_STS		0x0C
+#define DIV2_ACTIVE	BIT(7)
+static void __smbchg_wipower_check(struct smbchg_chip *chip)
+{
+	int chg_type;
+	bool usb_present, dc_present;
+	int rc;
+	int dcin_uv;
+	bool div2;
+	struct qpnp_vadc_result adc_result;
+	u8 reg;
+
+	if (!wipower_dyn_icl_en) {
+		smbchg_wipower_icl_deconfigure(chip);
+		return;
+	}
+
+	chg_type = get_prop_charge_type(chip);
+	usb_present = is_usb_present(chip);
+	dc_present = is_dc_present(chip);
+	if (chg_type != POWER_SUPPLY_CHARGE_TYPE_NONE
+			 && !usb_present
+			&& dc_present
+			&& chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER) {
+		rc = qpnp_vadc_read(chip->vadc_dev, DCIN, &adc_result);
+		if (rc) {
+			pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
+			return;
+		}
+		dcin_uv = adc_result.physical;
+
+		/* check div_by_2 */
+		rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS, 1);
+		if (rc) {
+			pr_smb(PR_STATUS, "error DCIN read rc = %d\n", rc);
+			return;
+		}
+		div2 = !!(reg & DIV2_ACTIVE);
+
+		pr_smb(PR_WIPOWER,
+			"config ICL chg_type = %d usb = %d dc = %d dcin_uv(adc_code) = %d (0x%x) div2 = %d\n",
+			chg_type, usb_present, dc_present, dcin_uv,
+			adc_result.adc_code, div2);
+		smbchg_wipower_icl_configure(chip, dcin_uv, div2);
+	} else {
+		pr_smb(PR_WIPOWER,
+			"deconfig ICL chg_type = %d usb = %d dc = %d\n",
+			chg_type, usb_present, dc_present);
+		smbchg_wipower_icl_deconfigure(chip);
+	}
+}
+
+static void smbchg_wipower_check(struct smbchg_chip *chip)
+{
+	if (!chip->wipower_dyn_icl_avail)
+		return;
+
+	mutex_lock(&chip->wipower_config);
+	__smbchg_wipower_check(chip);
+	mutex_unlock(&chip->wipower_config);
+}
+
+static void btm_notify_dcin(enum qpnp_tm_state state, void *ctx)
+{
+	struct smbchg_chip *chip = ctx;
+
+	mutex_lock(&chip->wipower_config);
+	pr_smb(PR_WIPOWER, "%s state\n",
+			state  == ADC_TM_LOW_STATE ? "low" : "high");
+	chip->current_ilim.vmin_uv = 0;
+	chip->current_ilim.vmax_uv = 0;
+	__smbchg_wipower_check(chip);
+	mutex_unlock(&chip->wipower_config);
+}
+
+static int force_dcin_icl_write(void *data, u64 val)
+{
+	struct smbchg_chip *chip = data;
+
+	smbchg_wipower_check(chip);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(force_dcin_icl_ops, NULL,
+		force_dcin_icl_write, "0x%02llx\n");
+
+/*
+ * set the dc charge path's maximum allowed current draw
+ * that may be limited by the system's thermal level
+ */
+static int set_dc_current_limit_vote_cb(struct votable *votable,
+						void *data,
+						int icl_ma,
+						const char *client)
+{
+	struct smbchg_chip *chip = data;
+
+	if (icl_ma < 0) {
+		pr_err("No voters\n");
+		return 0;
+	}
+
+	return smbchg_set_dc_current_max(chip, icl_ma);
+}
+
+/*
+ * set the usb charge path's maximum allowed current draw
+ * that may be limited by the system's thermal level
+ */
+static int set_usb_current_limit_vote_cb(struct votable *votable,
+						void *data,
+						int icl_ma,
+						const char *client)
+{
+	struct smbchg_chip *chip = data;
+	int rc, aicl_ma;
+	const char *effective_id;
+
+	if (icl_ma < 0) {
+		pr_err("No voters\n");
+		return 0;
+	}
+	effective_id = get_effective_client_locked(chip->usb_icl_votable);
+
+	if (!effective_id)
+		return 0;
+
+	/* disable parallel charging if HVDCP is voting for 300mA */
+	if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
+		smbchg_parallel_usb_disable(chip);
+
+	if (chip->parallel.current_max_ma == 0) {
+		rc = smbchg_set_usb_current_max(chip, icl_ma);
+		if (rc) {
+			pr_err("Failed to set usb current max: %d\n", rc);
+			return rc;
+		}
+	}
+
+	/* skip the aicl rerun if hvdcp icl voter is active */
+	if (strcmp(effective_id, HVDCP_ICL_VOTER) == 0)
+		return 0;
+
+	aicl_ma = smbchg_get_aicl_level_ma(chip);
+	if (icl_ma > aicl_ma)
+		smbchg_rerun_aicl(chip);
+	smbchg_parallel_usb_check_ok(chip);
+	return 0;
+}
+
+static int smbchg_system_temp_level_set(struct smbchg_chip *chip,
+								int lvl_sel)
+{
+	int rc = 0;
+	int prev_therm_lvl;
+	int thermal_icl_ma;
+
+	if (!chip->thermal_mitigation) {
+		dev_err(chip->dev, "Thermal mitigation not supported\n");
+		return -EINVAL;
+	}
+
+	if (lvl_sel < 0) {
+		dev_err(chip->dev, "Unsupported level selected %d\n", lvl_sel);
+		return -EINVAL;
+	}
+
+	if (lvl_sel >= chip->thermal_levels) {
+		dev_err(chip->dev, "Unsupported level selected %d forcing %d\n",
+				lvl_sel, chip->thermal_levels - 1);
+		lvl_sel = chip->thermal_levels - 1;
+	}
+
+	if (lvl_sel == chip->therm_lvl_sel)
+		return 0;
+
+	mutex_lock(&chip->therm_lvl_lock);
+	prev_therm_lvl = chip->therm_lvl_sel;
+	chip->therm_lvl_sel = lvl_sel;
+	if (chip->therm_lvl_sel == (chip->thermal_levels - 1)) {
+		/*
+		 * Disable charging if highest value selected by
+		 * setting the DC and USB path in suspend
+		 */
+		rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, true, 0);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set dc suspend rc %d\n", rc);
+			goto out;
+		}
+		rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER, true, 0);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set usb suspend rc %d\n", rc);
+			goto out;
+		}
+		goto out;
+	}
+
+	if (chip->therm_lvl_sel == 0) {
+		rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, false, 0);
+		if (rc < 0)
+			pr_err("Couldn't disable USB thermal ICL vote rc=%d\n",
+				rc);
+
+		rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, false, 0);
+		if (rc < 0)
+			pr_err("Couldn't disable DC thermal ICL vote rc=%d\n",
+				rc);
+	} else {
+		thermal_icl_ma =
+			(int)chip->thermal_mitigation[chip->therm_lvl_sel];
+		rc = vote(chip->usb_icl_votable, THERMAL_ICL_VOTER, true,
+					thermal_icl_ma);
+		if (rc < 0)
+			pr_err("Couldn't vote for USB thermal ICL rc=%d\n", rc);
+
+		rc = vote(chip->dc_icl_votable, THERMAL_ICL_VOTER, true,
+					thermal_icl_ma);
+		if (rc < 0)
+			pr_err("Couldn't vote for DC thermal ICL rc=%d\n", rc);
+	}
+
+	if (prev_therm_lvl == chip->thermal_levels - 1) {
+		/*
+		 * If previously highest value was selected charging must have
+		 * been disabed. Enable charging by taking the DC and USB path
+		 * out of suspend.
+		 */
+		rc = vote(chip->dc_suspend_votable, THERMAL_EN_VOTER, false, 0);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set dc suspend rc %d\n", rc);
+			goto out;
+		}
+		rc = vote(chip->usb_suspend_votable, THERMAL_EN_VOTER,
+								false, 0);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set usb suspend rc %d\n", rc);
+			goto out;
+		}
+	}
+out:
+	mutex_unlock(&chip->therm_lvl_lock);
+	return rc;
+}
+
+static int smbchg_ibat_ocp_threshold_ua = 4500000;
+module_param(smbchg_ibat_ocp_threshold_ua, int, 0644);
+
+#define UCONV			1000000LL
+#define MCONV			1000LL
+#define FLASH_V_THRESHOLD	3000000
+#define FLASH_VDIP_MARGIN	100000
+#define VPH_FLASH_VDIP		(FLASH_V_THRESHOLD + FLASH_VDIP_MARGIN)
+#define BUCK_EFFICIENCY		800LL
+static int smbchg_calc_max_flash_current(struct smbchg_chip *chip)
+{
+	int ocv_uv, esr_uohm, rbatt_uohm, ibat_now, rc;
+	int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
+	int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv;
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
+	if (rc) {
+		pr_smb(PR_STATUS, "bms psy does not support OCV\n");
+		return 0;
+	}
+
+	rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_RESISTANCE,
+			&esr_uohm);
+	if (rc) {
+		pr_smb(PR_STATUS, "bms psy does not support resistance\n");
+		return 0;
+	}
+
+	rc = msm_bcl_read(BCL_PARAM_CURRENT, &ibat_now);
+	if (rc) {
+		pr_smb(PR_STATUS, "BCL current read failed: %d\n", rc);
+		return 0;
+	}
+
+	rbatt_uohm = esr_uohm + chip->rpara_uohm + chip->rslow_uohm;
+	/*
+	 * Calculate the maximum current that can pulled out of the battery
+	 * before the battery voltage dips below a safe threshold.
+	 */
+	ibat_safe_ua = div_s64((ocv_uv - VPH_FLASH_VDIP) * UCONV,
+				rbatt_uohm);
+
+	if (ibat_safe_ua <= smbchg_ibat_ocp_threshold_ua) {
+		/*
+		 * If the calculated current is below the OCP threshold, then
+		 * use it as the possible flash current.
+		 */
+		ibat_flash_ua = ibat_safe_ua - ibat_now;
+		vph_flash_uv = VPH_FLASH_VDIP;
+	} else {
+		/*
+		 * If the calculated current is above the OCP threshold, then
+		 * use the ocp threshold instead.
+		 *
+		 * Any higher current will be tripping the battery OCP.
+		 */
+		ibat_flash_ua = smbchg_ibat_ocp_threshold_ua - ibat_now;
+		vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
+				* smbchg_ibat_ocp_threshold_ua, UCONV);
+	}
+	/* Calculate the input voltage of the flash module. */
+	vin_flash_uv = max((chip->vled_max_uv + 500000LL),
+				div64_s64((vph_flash_uv * 1200), 1000));
+	/* Calculate the available power for the flash module. */
+	avail_flash_power_fw = BUCK_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
+	/*
+	 * Calculate the available amount of current the flash module can draw
+	 * before collapsing the battery. (available power/ flash input voltage)
+	 */
+	avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
+	pr_smb(PR_MISC,
+		"avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d\n",
+		avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm);
+	return (int)avail_flash_ua;
+}
+
+#define FCC_CMP_CFG	0xF3
+#define FCC_COMP_MASK	SMB_MASK(1, 0)
+static int smbchg_fastchg_current_comp_set(struct smbchg_chip *chip,
+					int comp_current)
+{
+	int rc;
+	u8 i;
+
+	for (i = 0; i < chip->tables.fcc_comp_len; i++)
+		if (comp_current == chip->tables.fcc_comp_table[i])
+			break;
+
+	if (i >= chip->tables.fcc_comp_len)
+		return -EINVAL;
+
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + FCC_CMP_CFG,
+			FCC_COMP_MASK, i);
+
+	if (rc)
+		dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
+			rc);
+
+	return rc;
+}
+
+#define CFG_TCC_REG			0xF9
+#define CHG_ITERM_MASK			SMB_MASK(2, 0)
+static int smbchg_iterm_set(struct smbchg_chip *chip, int iterm_ma)
+{
+	int rc;
+	u8 reg;
+
+	reg = find_closest_in_array(
+			chip->tables.iterm_ma_table,
+			chip->tables.iterm_ma_len,
+			iterm_ma);
+
+	rc = smbchg_sec_masked_write(chip,
+			chip->chgr_base + CFG_TCC_REG,
+			CHG_ITERM_MASK, reg);
+	if (rc) {
+		dev_err(chip->dev,
+			"Couldn't set iterm rc = %d\n", rc);
+		return rc;
+	}
+	pr_smb(PR_STATUS, "set tcc (%d) to 0x%02x\n",
+			iterm_ma, reg);
+	chip->iterm_ma = iterm_ma;
+
+	return 0;
+}
+
+#define FV_CMP_CFG	0xF5
+#define FV_COMP_MASK	SMB_MASK(5, 0)
+static int smbchg_float_voltage_comp_set(struct smbchg_chip *chip, int code)
+{
+	int rc;
+	u8 val;
+
+	val = code & FV_COMP_MASK;
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + FV_CMP_CFG,
+			FV_COMP_MASK, val);
+
+	if (rc)
+		dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
+			rc);
+
+	return rc;
+}
+
+#define VFLOAT_CFG_REG			0xF4
+#define MIN_FLOAT_MV			3600
+#define MAX_FLOAT_MV			4500
+#define VFLOAT_MASK			SMB_MASK(5, 0)
+
+#define MID_RANGE_FLOAT_MV_MIN		3600
+#define MID_RANGE_FLOAT_MIN_VAL		0x05
+#define MID_RANGE_FLOAT_STEP_MV		20
+
+#define HIGH_RANGE_FLOAT_MIN_MV		4340
+#define HIGH_RANGE_FLOAT_MIN_VAL	0x2A
+#define HIGH_RANGE_FLOAT_STEP_MV	10
+
+#define VHIGH_RANGE_FLOAT_MIN_MV	4360
+#define VHIGH_RANGE_FLOAT_MIN_VAL	0x2C
+#define VHIGH_RANGE_FLOAT_STEP_MV	20
+static int smbchg_float_voltage_set(struct smbchg_chip *chip, int vfloat_mv)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval prop;
+	int rc, delta;
+	u8 temp;
+
+	if ((vfloat_mv < MIN_FLOAT_MV) || (vfloat_mv > MAX_FLOAT_MV)) {
+		dev_err(chip->dev, "bad float voltage mv =%d asked to set\n",
+					vfloat_mv);
+		return -EINVAL;
+	}
+
+	if (vfloat_mv <= HIGH_RANGE_FLOAT_MIN_MV) {
+		/* mid range */
+		delta = vfloat_mv - MID_RANGE_FLOAT_MV_MIN;
+		temp = MID_RANGE_FLOAT_MIN_VAL + delta
+				/ MID_RANGE_FLOAT_STEP_MV;
+		vfloat_mv -= delta % MID_RANGE_FLOAT_STEP_MV;
+	} else if (vfloat_mv <= VHIGH_RANGE_FLOAT_MIN_MV) {
+		/* high range */
+		delta = vfloat_mv - HIGH_RANGE_FLOAT_MIN_MV;
+		temp = HIGH_RANGE_FLOAT_MIN_VAL + delta
+				/ HIGH_RANGE_FLOAT_STEP_MV;
+		vfloat_mv -= delta % HIGH_RANGE_FLOAT_STEP_MV;
+	} else {
+		/* very high range */
+		delta = vfloat_mv - VHIGH_RANGE_FLOAT_MIN_MV;
+		temp = VHIGH_RANGE_FLOAT_MIN_VAL + delta
+				/ VHIGH_RANGE_FLOAT_STEP_MV;
+		vfloat_mv -= delta % VHIGH_RANGE_FLOAT_STEP_MV;
+	}
+
+	if (parallel_psy) {
+		prop.intval = vfloat_mv + 50;
+		rc = power_supply_set_property(parallel_psy,
+				POWER_SUPPLY_PROP_VOLTAGE_MAX, &prop);
+		if (rc)
+			dev_err(chip->dev, "Couldn't set float voltage on parallel psy rc: %d\n",
+				rc);
+	}
+
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + VFLOAT_CFG_REG,
+			VFLOAT_MASK, temp);
+
+	if (rc)
+		dev_err(chip->dev, "Couldn't set float voltage rc = %d\n", rc);
+	else
+		chip->vfloat_mv = vfloat_mv;
+
+	return rc;
+}
+
+static int smbchg_float_voltage_get(struct smbchg_chip *chip)
+{
+	return chip->vfloat_mv;
+}
+
+#define SFT_CFG				0xFD
+#define SFT_EN_MASK			SMB_MASK(5, 4)
+#define SFT_TO_MASK			SMB_MASK(3, 2)
+#define PRECHG_SFT_TO_MASK		SMB_MASK(1, 0)
+#define SFT_TIMER_DISABLE_BIT		BIT(5)
+#define PRECHG_SFT_TIMER_DISABLE_BIT	BIT(4)
+#define SAFETY_TIME_MINUTES_SHIFT	2
+static int smbchg_safety_timer_enable(struct smbchg_chip *chip, bool enable)
+{
+	int rc;
+	u8 reg;
+
+	if (enable == chip->safety_timer_en)
+		return 0;
+
+	if (enable)
+		reg = 0;
+	else
+		reg = SFT_TIMER_DISABLE_BIT | PRECHG_SFT_TIMER_DISABLE_BIT;
+
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + SFT_CFG,
+			SFT_EN_MASK, reg);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't %s safety timer rc = %d\n",
+			enable ? "enable" : "disable", rc);
+		return rc;
+	}
+	chip->safety_timer_en = enable;
+	return 0;
+}
+
+enum skip_reason {
+	REASON_OTG_ENABLED	= BIT(0),
+	REASON_FLASH_ENABLED	= BIT(1)
+};
+
+#define BAT_IF_TRIM7_REG	0xF7
+#define CFG_750KHZ_BIT		BIT(1)
+#define MISC_CFG_NTC_VOUT_REG	0xF3
+#define CFG_NTC_VOUT_FSW_BIT	BIT(0)
+static int smbchg_switch_buck_frequency(struct smbchg_chip *chip,
+				bool flash_active)
+{
+	int rc;
+
+	if (!(chip->wa_flags & SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA))
+		return 0;
+
+	if (chip->flash_active == flash_active) {
+		pr_smb(PR_STATUS, "Fsw not changed, flash_active: %d\n",
+			flash_active);
+		return 0;
+	}
+
+	/*
+	 * As per the systems team recommendation, before the flash fires,
+	 * buck switching frequency(Fsw) needs to be increased to 1MHz. Once the
+	 * flash is disabled, Fsw needs to be set back to 750KHz.
+	 */
+	rc = smbchg_sec_masked_write(chip, chip->misc_base +
+				MISC_CFG_NTC_VOUT_REG, CFG_NTC_VOUT_FSW_BIT,
+				flash_active ? CFG_NTC_VOUT_FSW_BIT : 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set switching frequency multiplier rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BAT_IF_TRIM7_REG,
+			CFG_750KHZ_BIT, flash_active ? 0 : CFG_750KHZ_BIT);
+	if (rc < 0) {
+		dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_STATUS, "Fsw @ %sHz\n", flash_active ? "1M" : "750K");
+	chip->flash_active = flash_active;
+	return 0;
+}
+
+#define OTG_TRIM6		0xF6
+#define TR_ENB_SKIP_BIT		BIT(2)
+#define OTG_EN_BIT		BIT(0)
+static int smbchg_otg_pulse_skip_disable(struct smbchg_chip *chip,
+				enum skip_reason reason, bool disable)
+{
+	int rc;
+	bool disabled;
+
+	disabled = !!chip->otg_pulse_skip_dis;
+	pr_smb(PR_STATUS, "%s pulse skip, reason %d\n",
+			disable ? "disabling" : "enabling", reason);
+	if (disable)
+		chip->otg_pulse_skip_dis |= reason;
+	else
+		chip->otg_pulse_skip_dis &= ~reason;
+	if (disabled == !!chip->otg_pulse_skip_dis)
+		return 0;
+	disabled = !!chip->otg_pulse_skip_dis;
+
+	rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_TRIM6,
+			TR_ENB_SKIP_BIT, disabled ? TR_ENB_SKIP_BIT : 0);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't %s otg pulse skip rc = %d\n",
+			disabled ? "disable" : "enable", rc);
+		return rc;
+	}
+	pr_smb(PR_STATUS, "%s pulse skip\n", disabled ? "disabled" : "enabled");
+	return 0;
+}
+
+#define LOW_PWR_OPTIONS_REG	0xFF
+#define FORCE_TLIM_BIT		BIT(4)
+static int smbchg_force_tlim_en(struct smbchg_chip *chip, bool enable)
+{
+	int rc;
+
+	rc = smbchg_sec_masked_write(chip, chip->otg_base + LOW_PWR_OPTIONS_REG,
+			FORCE_TLIM_BIT, enable ? FORCE_TLIM_BIT : 0);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't %s otg force tlim rc = %d\n",
+			enable ? "enable" : "disable", rc);
+		return rc;
+	}
+	return rc;
+}
+
+static void smbchg_vfloat_adjust_check(struct smbchg_chip *chip)
+{
+	if (!chip->use_vfloat_adjustments)
+		return;
+
+	smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
+	pr_smb(PR_STATUS, "Starting vfloat adjustments\n");
+	schedule_delayed_work(&chip->vfloat_adjust_work, 0);
+}
+
+#define FV_STS_REG			0xC
+#define AICL_INPUT_STS_BIT		BIT(6)
+static bool smbchg_is_input_current_limited(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->chgr_base + FV_STS_REG, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read FV_STS rc=%d\n", rc);
+		return false;
+	}
+
+	return !!(reg & AICL_INPUT_STS_BIT);
+}
+
+#define SW_ESR_PULSE_MS			1500
+static void smbchg_cc_esr_wa_check(struct smbchg_chip *chip)
+{
+	int rc, esr_count;
+
+	if (!(chip->wa_flags & SMBCHG_CC_ESR_WA))
+		return;
+
+	if (!is_usb_present(chip) && !is_dc_present(chip)) {
+		pr_smb(PR_STATUS, "No inputs present, skipping\n");
+		return;
+	}
+
+	if (get_prop_charge_type(chip) != POWER_SUPPLY_CHARGE_TYPE_FAST) {
+		pr_smb(PR_STATUS, "Not in fast charge, skipping\n");
+		return;
+	}
+
+	if (!smbchg_is_input_current_limited(chip)) {
+		pr_smb(PR_STATUS, "Not input current limited, skipping\n");
+		return;
+	}
+
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
+	rc = get_property_from_fg(chip,
+			POWER_SUPPLY_PROP_ESR_COUNT, &esr_count);
+	if (rc) {
+		pr_smb(PR_STATUS,
+			"could not read ESR counter rc = %d\n", rc);
+		return;
+	}
+
+	/*
+	 * The esr_count is counting down the number of fuel gauge cycles
+	 * before a ESR pulse is needed.
+	 *
+	 * After a successful ESR pulse, this count is reset to some
+	 * high number like 28. If this reaches 0, then the fuel gauge
+	 * hardware should force a ESR pulse.
+	 *
+	 * However, if the device is in constant current charge mode while
+	 * being input current limited, the ESR pulse will not affect the
+	 * battery current, so the measurement will fail.
+	 *
+	 * As a failsafe, force a manual ESR pulse if this value is read as
+	 * 0.
+	 */
+	if (esr_count != 0) {
+		pr_smb(PR_STATUS, "ESR count is not zero, skipping\n");
+		return;
+	}
+
+	pr_smb(PR_STATUS, "Lowering charge current for ESR pulse\n");
+	smbchg_stay_awake(chip, PM_ESR_PULSE);
+	smbchg_sw_esr_pulse_en(chip, true);
+	msleep(SW_ESR_PULSE_MS);
+	pr_smb(PR_STATUS, "Raising charge current for ESR pulse\n");
+	smbchg_relax(chip, PM_ESR_PULSE);
+	smbchg_sw_esr_pulse_en(chip, false);
+}
+
+static void smbchg_soc_changed(struct smbchg_chip *chip)
+{
+	smbchg_cc_esr_wa_check(chip);
+}
+
+#define DC_AICL_CFG			0xF3
+#define MISC_TRIM_OPT_15_8		0xF5
+#define USB_AICL_DEGLITCH_MASK		(BIT(5) | BIT(4) | BIT(3))
+#define USB_AICL_DEGLITCH_SHORT		(BIT(5) | BIT(4) | BIT(3))
+#define USB_AICL_DEGLITCH_LONG		0
+#define DC_AICL_DEGLITCH_MASK		(BIT(5) | BIT(4) | BIT(3))
+#define DC_AICL_DEGLITCH_SHORT		(BIT(5) | BIT(4) | BIT(3))
+#define DC_AICL_DEGLITCH_LONG		0
+#define AICL_RERUN_MASK			(BIT(5) | BIT(4))
+#define AICL_RERUN_ON			(BIT(5) | BIT(4))
+#define AICL_RERUN_OFF			0
+
+static int smbchg_hw_aicl_rerun_enable_indirect_cb(struct votable *votable,
+						void *data,
+						int enable,
+						const char *client)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = data;
+
+	if (enable < 0) {
+		pr_err("No voters\n");
+		enable = 0;
+	}
+	/*
+	 * If the indirect voting result of all the clients is to enable hw aicl
+	 * rerun, then remove our vote to disable hw aicl rerun
+	 */
+	rc = vote(chip->hw_aicl_rerun_disable_votable,
+		HW_AICL_RERUN_ENABLE_INDIRECT_VOTER, !enable, 0);
+	if (rc < 0) {
+		pr_err("Couldn't vote for hw rerun rc= %d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int smbchg_hw_aicl_rerun_disable_cb(struct votable *votable, void *data,
+						int disable,
+						const char *client)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = data;
+
+	if (disable < 0) {
+		pr_err("No voters\n");
+		disable = 0;
+	}
+
+	rc = smbchg_sec_masked_write(chip,
+		chip->misc_base + MISC_TRIM_OPT_15_8,
+		AICL_RERUN_MASK, disable ? AICL_RERUN_OFF : AICL_RERUN_ON);
+	if (rc < 0)
+		pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n", rc);
+
+	return rc;
+}
+
+static int smbchg_aicl_deglitch_config_cb(struct votable *votable, void *data,
+						int shorter,
+						const char *client)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = data;
+
+	if (shorter < 0) {
+		pr_err("No voters\n");
+		shorter = 0;
+	}
+
+	rc = smbchg_sec_masked_write(chip,
+		chip->usb_chgpth_base + USB_AICL_CFG,
+		USB_AICL_DEGLITCH_MASK,
+		shorter ? USB_AICL_DEGLITCH_SHORT : USB_AICL_DEGLITCH_LONG);
+	if (rc < 0) {
+		pr_err("Couldn't write to USB_AICL_CFG rc=%d\n", rc);
+		return rc;
+	}
+	rc = smbchg_sec_masked_write(chip,
+		chip->dc_chgpth_base + DC_AICL_CFG,
+		DC_AICL_DEGLITCH_MASK,
+		shorter ? DC_AICL_DEGLITCH_SHORT : DC_AICL_DEGLITCH_LONG);
+	if (rc < 0) {
+		pr_err("Couldn't write to DC_AICL_CFG rc=%d\n", rc);
+		return rc;
+	}
+	return rc;
+}
+
+static void smbchg_aicl_deglitch_wa_en(struct smbchg_chip *chip, bool en)
+{
+	int rc;
+
+	rc = vote(chip->aicl_deglitch_short_votable,
+		VARB_WORKAROUND_VOTER, en, 0);
+	if (rc < 0) {
+		pr_err("Couldn't vote %s deglitch rc=%d\n",
+				en ? "short" : "long", rc);
+		return;
+	}
+	pr_smb(PR_STATUS, "AICL deglitch set to %s\n", en ? "short" : "long");
+
+	rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+			VARB_WORKAROUND_VOTER, en, 0);
+	if (rc < 0) {
+		pr_err("Couldn't vote hw aicl rerun rc= %d\n", rc);
+		return;
+	}
+	chip->aicl_deglitch_short = en;
+}
+
+static void smbchg_aicl_deglitch_wa_check(struct smbchg_chip *chip)
+{
+	union power_supply_propval prop = {0,};
+	int rc;
+	bool low_volt_chgr = true;
+
+	if (!(chip->wa_flags & SMBCHG_AICL_DEGLITCH_WA))
+		return;
+
+	if (!is_usb_present(chip) && !is_dc_present(chip)) {
+		pr_smb(PR_STATUS, "Charger removed\n");
+		smbchg_aicl_deglitch_wa_en(chip, false);
+		return;
+	}
+
+	if (!chip->bms_psy)
+		return;
+
+	if (is_usb_present(chip)) {
+		if (is_hvdcp_present(chip))
+			low_volt_chgr = false;
+	} else if (is_dc_present(chip)) {
+		if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
+			low_volt_chgr = false;
+		else
+			low_volt_chgr = chip->low_volt_dcin;
+	}
+
+	if (!low_volt_chgr) {
+		pr_smb(PR_STATUS, "High volt charger! Don't set deglitch\n");
+		smbchg_aicl_deglitch_wa_en(chip, false);
+		return;
+	}
+
+	/* It is possible that battery voltage went high above threshold
+	 * when the charger is inserted and can go low because of system
+	 * load. We shouldn't be reconfiguring AICL deglitch when this
+	 * happens as it will lead to oscillation again which is being
+	 * fixed here. Do it once when the battery voltage crosses the
+	 * threshold (e.g. 4.2 V) and clear it only when the charger
+	 * is removed.
+	 */
+	if (!chip->vbat_above_headroom) {
+		rc = power_supply_get_property(chip->bms_psy,
+				POWER_SUPPLY_PROP_VOLTAGE_MIN, &prop);
+		if (rc < 0) {
+			pr_err("could not read voltage_min, rc=%d\n", rc);
+			return;
+		}
+		chip->vbat_above_headroom = !prop.intval;
+	}
+	smbchg_aicl_deglitch_wa_en(chip, chip->vbat_above_headroom);
+}
+
+#define MISC_TEST_REG		0xE2
+#define BB_LOOP_DISABLE_ICL	BIT(2)
+static int smbchg_icl_loop_disable_check(struct smbchg_chip *chip)
+{
+	bool icl_disabled = !chip->chg_otg_enabled && chip->flash_triggered;
+	int rc = 0;
+
+	if ((chip->wa_flags & SMBCHG_FLASH_ICL_DISABLE_WA)
+			&& icl_disabled != chip->icl_disabled) {
+		rc = smbchg_sec_masked_write(chip,
+				chip->misc_base + MISC_TEST_REG,
+				BB_LOOP_DISABLE_ICL,
+				icl_disabled ? BB_LOOP_DISABLE_ICL : 0);
+		chip->icl_disabled = icl_disabled;
+	}
+
+	return rc;
+}
+
+#define UNKNOWN_BATT_TYPE	"Unknown Battery"
+#define LOADING_BATT_TYPE	"Loading Battery Data"
+static int smbchg_config_chg_battery_type(struct smbchg_chip *chip)
+{
+	int rc = 0, max_voltage_uv = 0, fastchg_ma = 0, ret = 0, iterm_ua = 0;
+	struct device_node *batt_node, *profile_node;
+	struct device_node *node = chip->pdev->dev.of_node;
+	union power_supply_propval prop = {0,};
+
+	rc = power_supply_get_property(chip->bms_psy,
+			POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
+	if (rc) {
+		pr_smb(PR_STATUS, "Unable to read battery-type rc=%d\n", rc);
+		return 0;
+	}
+	if (!strcmp(prop.strval, UNKNOWN_BATT_TYPE) ||
+		!strcmp(prop.strval, LOADING_BATT_TYPE)) {
+		pr_smb(PR_MISC, "Battery-type not identified\n");
+		return 0;
+	}
+	/* quit if there is no change in the battery-type from previous */
+	if (chip->battery_type && !strcmp(prop.strval, chip->battery_type))
+		return 0;
+
+	chip->battery_type = prop.strval;
+	batt_node = of_parse_phandle(node, "qcom,battery-data", 0);
+	if (!batt_node) {
+		pr_smb(PR_MISC, "No batterydata available\n");
+		return 0;
+	}
+
+	rc = power_supply_get_property(chip->bms_psy,
+			POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
+	if (rc < 0) {
+		pr_smb(PR_STATUS, "Unable to read battery-id rc=%d\n", rc);
+		return 0;
+	}
+
+	profile_node = of_batterydata_get_best_profile(batt_node,
+				prop.intval / 1000, NULL);
+	if (IS_ERR_OR_NULL(profile_node)) {
+		rc = PTR_ERR(profile_node);
+		pr_err("couldn't find profile handle %d\n", rc);
+		return rc;
+	}
+
+	/* change vfloat */
+	rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+						&max_voltage_uv);
+	if (rc) {
+		pr_warn("couldn't find battery max voltage rc=%d\n", rc);
+		ret = rc;
+	} else {
+		if (chip->vfloat_mv != (max_voltage_uv / 1000)) {
+			pr_info("Vfloat changed from %dmV to %dmV for battery-type %s\n",
+				chip->vfloat_mv, (max_voltage_uv / 1000),
+				chip->battery_type);
+			rc = smbchg_float_voltage_set(chip,
+						(max_voltage_uv / 1000));
+			if (rc < 0) {
+				dev_err(chip->dev,
+				"Couldn't set float voltage rc = %d\n", rc);
+				return rc;
+			}
+		}
+	}
+
+	/* change chg term */
+	rc = of_property_read_u32(profile_node, "qcom,chg-term-ua",
+						&iterm_ua);
+	if (rc && rc != -EINVAL) {
+		pr_warn("couldn't read battery term current=%d\n", rc);
+		ret = rc;
+	} else if (!rc) {
+		if (chip->iterm_ma != (iterm_ua / 1000)
+				&& !chip->iterm_disabled) {
+			pr_info("Term current changed from %dmA to %dmA for battery-type %s\n",
+				chip->iterm_ma, (iterm_ua / 1000),
+				chip->battery_type);
+			rc = smbchg_iterm_set(chip,
+						(iterm_ua / 1000));
+			if (rc < 0) {
+				dev_err(chip->dev,
+				"Couldn't set iterm rc = %d\n", rc);
+				return rc;
+			}
+		}
+		chip->iterm_ma = iterm_ua / 1000;
+	}
+
+	/*
+	 * Only configure from profile if fastchg-ma is not defined in the
+	 * charger device node.
+	 */
+	if (!of_find_property(chip->pdev->dev.of_node,
+				"qcom,fastchg-current-ma", NULL)) {
+		rc = of_property_read_u32(profile_node,
+				"qcom,fastchg-current-ma", &fastchg_ma);
+		if (rc) {
+			ret = rc;
+		} else {
+			pr_smb(PR_MISC,
+				"fastchg-ma changed from to %dma for battery-type %s\n",
+				fastchg_ma, chip->battery_type);
+			rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
+							fastchg_ma);
+			if (rc < 0) {
+				dev_err(chip->dev,
+					"Couldn't vote for fastchg current rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	return ret;
+}
+
+#define MAX_INV_BATT_ID		7700
+#define MIN_INV_BATT_ID		7300
+static void check_battery_type(struct smbchg_chip *chip)
+{
+	union power_supply_propval prop = {0,};
+	bool en;
+
+	if (!chip->bms_psy && chip->bms_psy_name)
+		chip->bms_psy =
+			power_supply_get_by_name((char *)chip->bms_psy_name);
+	if (chip->bms_psy) {
+		power_supply_get_property(chip->bms_psy,
+				POWER_SUPPLY_PROP_BATTERY_TYPE, &prop);
+		en = (strcmp(prop.strval, UNKNOWN_BATT_TYPE) != 0
+				|| chip->charge_unknown_battery)
+			&& (strcmp(prop.strval, LOADING_BATT_TYPE) != 0);
+		vote(chip->battchg_suspend_votable,
+				BATTCHG_UNKNOWN_BATTERY_EN_VOTER, !en, 0);
+
+		if (!chip->skip_usb_suspend_for_fake_battery) {
+			power_supply_get_property(chip->bms_psy,
+				POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
+			/* suspend USB path for invalid battery-id */
+			en = (prop.intval <= MAX_INV_BATT_ID &&
+				prop.intval >= MIN_INV_BATT_ID) ? 1 : 0;
+			vote(chip->usb_suspend_votable, FAKE_BATTERY_EN_VOTER,
+				en, 0);
+		}
+	}
+}
+
+static void smbchg_external_power_changed(struct power_supply *psy)
+{
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+	union power_supply_propval prop = {0,};
+	int rc, current_limit = 0, soc;
+	enum power_supply_type usb_supply_type;
+	char *usb_type_name = "null";
+
+	if (chip->bms_psy_name)
+		chip->bms_psy =
+			power_supply_get_by_name((char *)chip->bms_psy_name);
+
+	smbchg_aicl_deglitch_wa_check(chip);
+	if (chip->bms_psy) {
+		check_battery_type(chip);
+		soc = get_prop_batt_capacity(chip);
+		if (chip->previous_soc != soc) {
+			chip->previous_soc = soc;
+			smbchg_soc_changed(chip);
+		}
+
+		rc = smbchg_config_chg_battery_type(chip);
+		if (rc)
+			pr_smb(PR_MISC,
+				"Couldn't update charger configuration rc=%d\n",
+									rc);
+	}
+
+	rc = power_supply_get_property(chip->usb_psy,
+				POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop);
+	if (rc == 0)
+		vote(chip->usb_suspend_votable, POWER_SUPPLY_EN_VOTER,
+				!prop.intval, 0);
+
+	current_limit = chip->usb_current_max / 1000;
+
+	/* Override if type-c charger used */
+	if (chip->typec_current_ma > 500 &&
+			current_limit < chip->typec_current_ma)
+		current_limit = chip->typec_current_ma;
+
+	read_usb_type(chip, &usb_type_name, &usb_supply_type);
+
+	if (usb_supply_type != POWER_SUPPLY_TYPE_USB)
+		goto  skip_current_for_non_sdp;
+
+	pr_smb(PR_MISC, "usb type = %s current_limit = %d\n",
+			usb_type_name, current_limit);
+
+	rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
+				current_limit);
+	if (rc < 0)
+		pr_err("Couldn't update USB PSY ICL vote rc=%d\n", rc);
+
+skip_current_for_non_sdp:
+	smbchg_vfloat_adjust_check(chip);
+
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+}
+
+static int smbchg_otg_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	chip->otg_retries = 0;
+	chip->chg_otg_enabled = true;
+	smbchg_icl_loop_disable_check(chip);
+	smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, true);
+
+	/* If pin control mode then return from here */
+	if (chip->otg_pinctrl)
+		return rc;
+
+	/* sleep to make sure the pulse skip is actually disabled */
+	msleep(20);
+	rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+			OTG_EN_BIT, OTG_EN_BIT);
+	if (rc < 0)
+		dev_err(chip->dev, "Couldn't enable OTG mode rc=%d\n", rc);
+	else
+		chip->otg_enable_time = ktime_get();
+	pr_smb(PR_STATUS, "Enabling OTG Boost\n");
+	return rc;
+}
+
+static int smbchg_otg_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	if (!chip->otg_pinctrl) {
+		rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+				OTG_EN_BIT, 0);
+		if (rc < 0)
+			dev_err(chip->dev, "Couldn't disable OTG mode rc=%d\n",
+					rc);
+	}
+
+	chip->chg_otg_enabled = false;
+	smbchg_otg_pulse_skip_disable(chip, REASON_OTG_ENABLED, false);
+	smbchg_icl_loop_disable_check(chip);
+	pr_smb(PR_STATUS, "Disabling OTG Boost\n");
+	return rc;
+}
+
+static int smbchg_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	u8 reg = 0;
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	rc = smbchg_read(chip, &reg, chip->bat_if_base + CMD_CHG_REG, 1);
+	if (rc < 0) {
+		dev_err(chip->dev,
+				"Couldn't read OTG enable bit rc=%d\n", rc);
+		return rc;
+	}
+
+	return (reg & OTG_EN_BIT) ? 1 : 0;
+}
+
+struct regulator_ops smbchg_otg_reg_ops = {
+	.enable		= smbchg_otg_regulator_enable,
+	.disable	= smbchg_otg_regulator_disable,
+	.is_enabled	= smbchg_otg_regulator_is_enable,
+};
+
+#define USBIN_CHGR_CFG			0xF1
+#define ADAPTER_ALLOWANCE_MASK		0x7
+#define USBIN_ADAPTER_9V		0x3
+#define USBIN_ADAPTER_5V_9V_CONT	0x2
+#define USBIN_ADAPTER_5V_UNREGULATED_9V	0x5
+#define HVDCP_EN_BIT			BIT(3)
+static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, true, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = smbchg_read(chip, &chip->original_usbin_allowance,
+			chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * To disallow source detect and usbin_uv interrupts, set the adapter
+	 * allowance to 9V, so that the audio boost operating in reverse never
+	 * gets detected as a valid input
+	 */
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_EN_BIT, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + USBIN_CHGR_CFG,
+				0xFF, USBIN_ADAPTER_9V);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_STATUS, "Enabling OTG Boost\n");
+	return rc;
+}
+
+static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	rc = vote(chip->usb_suspend_votable, OTG_EN_VOTER, false, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Reenable HVDCP and set the adapter allowance back to the original
+	 * value in order to allow normal USBs to be recognized as a valid
+	 * input.
+	 */
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_EN_BIT, HVDCP_EN_BIT);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + USBIN_CHGR_CFG,
+				0xFF, chip->original_usbin_allowance);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_STATUS, "Disabling OTG Boost\n");
+	return rc;
+}
+
+static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev)
+{
+	struct smbchg_chip *chip = rdev_get_drvdata(rdev);
+
+	return get_client_vote(chip->usb_suspend_votable, OTG_EN_VOTER);
+}
+
+struct regulator_ops smbchg_external_otg_reg_ops = {
+	.enable		= smbchg_external_otg_regulator_enable,
+	.disable	= smbchg_external_otg_regulator_disable,
+	.is_enabled	= smbchg_external_otg_regulator_is_enable,
+};
+
+static int smbchg_regulator_init(struct smbchg_chip *chip)
+{
+	int rc = 0;
+	struct regulator_config cfg = {};
+	struct device_node *regulator_node;
+
+	cfg.dev = chip->dev;
+	cfg.driver_data = chip;
+
+	chip->otg_vreg.rdesc.owner = THIS_MODULE;
+	chip->otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+	chip->otg_vreg.rdesc.ops = &smbchg_otg_reg_ops;
+	chip->otg_vreg.rdesc.of_match = "qcom,smbcharger-boost-otg";
+	chip->otg_vreg.rdesc.name = "qcom,smbcharger-boost-otg";
+
+	chip->otg_vreg.rdev = devm_regulator_register(chip->dev,
+					&chip->otg_vreg.rdesc, &cfg);
+	if (IS_ERR(chip->otg_vreg.rdev)) {
+		rc = PTR_ERR(chip->otg_vreg.rdev);
+		chip->otg_vreg.rdev = NULL;
+		if (rc != -EPROBE_DEFER)
+			dev_err(chip->dev,
+				"OTG reg failed, rc=%d\n", rc);
+	}
+	if (rc)
+		return rc;
+
+	regulator_node = of_get_child_by_name(chip->dev->of_node,
+			"qcom,smbcharger-external-otg");
+	if (!regulator_node) {
+		dev_dbg(chip->dev, "external-otg node absent\n");
+		return 0;
+	}
+
+	chip->ext_otg_vreg.rdesc.owner = THIS_MODULE;
+	chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
+	chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops;
+	chip->ext_otg_vreg.rdesc.of_match =  "qcom,smbcharger-external-otg";
+	chip->ext_otg_vreg.rdesc.name = "qcom,smbcharger-external-otg";
+	if (of_get_property(chip->dev->of_node, "otg-parent-supply", NULL))
+		chip->ext_otg_vreg.rdesc.supply_name = "otg-parent";
+	cfg.dev = chip->dev;
+	cfg.driver_data = chip;
+
+	chip->ext_otg_vreg.rdev = devm_regulator_register(chip->dev,
+					&chip->ext_otg_vreg.rdesc,
+					&cfg);
+	if (IS_ERR(chip->ext_otg_vreg.rdev)) {
+		rc = PTR_ERR(chip->ext_otg_vreg.rdev);
+		chip->ext_otg_vreg.rdev = NULL;
+		if (rc != -EPROBE_DEFER)
+			dev_err(chip->dev,
+				"external OTG reg failed, rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+#define CMD_CHG_LED_REG		0x43
+#define CHG_LED_CTRL_BIT		BIT(0)
+#define LED_SW_CTRL_BIT		0x1
+#define LED_CHG_CTRL_BIT		0x0
+#define CHG_LED_ON		0x03
+#define CHG_LED_OFF		0x00
+#define LED_BLINKING_PATTERN1		0x01
+#define LED_BLINKING_PATTERN2		0x02
+#define LED_BLINKING_CFG_MASK		SMB_MASK(2, 1)
+#define CHG_LED_SHIFT		1
+static int smbchg_chg_led_controls(struct smbchg_chip *chip)
+{
+	u8 reg, mask;
+	int rc;
+
+	if (chip->cfg_chg_led_sw_ctrl) {
+		/* turn-off LED by default for software control */
+		mask = CHG_LED_CTRL_BIT | LED_BLINKING_CFG_MASK;
+		reg = LED_SW_CTRL_BIT;
+	} else {
+		mask = CHG_LED_CTRL_BIT;
+		reg = LED_CHG_CTRL_BIT;
+	}
+
+	rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_LED_REG,
+			mask, reg);
+	if (rc < 0)
+		dev_err(chip->dev,
+				"Couldn't write LED_CTRL_BIT rc=%d\n", rc);
+	return rc;
+}
+
+static void smbchg_chg_led_brightness_set(struct led_classdev *cdev,
+		enum led_brightness value)
+{
+	struct smbchg_chip *chip = container_of(cdev,
+			struct smbchg_chip, led_cdev);
+	union power_supply_propval pval = {0, };
+	u8 reg;
+	int rc;
+
+	reg = (value > LED_OFF) ? CHG_LED_ON << CHG_LED_SHIFT :
+		CHG_LED_OFF << CHG_LED_SHIFT;
+	pval.intval = value > LED_OFF ? 1 : 0;
+	power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
+			&pval);
+	pr_smb(PR_STATUS,
+			"set the charger led brightness to value=%d\n",
+			value);
+	rc = smbchg_sec_masked_write(chip,
+			chip->bat_if_base + CMD_CHG_LED_REG,
+			LED_BLINKING_CFG_MASK, reg);
+	if (rc)
+		dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
+				rc);
+}
+
+static enum
+led_brightness smbchg_chg_led_brightness_get(struct led_classdev *cdev)
+{
+	struct smbchg_chip *chip = container_of(cdev,
+			struct smbchg_chip, led_cdev);
+	u8 reg_val, chg_led_sts;
+	int rc;
+
+	rc = smbchg_read(chip, &reg_val, chip->bat_if_base + CMD_CHG_LED_REG,
+			1);
+	if (rc < 0) {
+		dev_err(chip->dev,
+				"Couldn't read CHG_LED_REG sts rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	chg_led_sts = (reg_val & LED_BLINKING_CFG_MASK) >> CHG_LED_SHIFT;
+
+	pr_smb(PR_STATUS, "chg_led_sts = %02x\n", chg_led_sts);
+
+	return (chg_led_sts == CHG_LED_OFF) ? LED_OFF : LED_FULL;
+}
+
+static void smbchg_chg_led_blink_set(struct smbchg_chip *chip,
+		unsigned long blinking)
+{
+	union power_supply_propval pval = {0, };
+	u8 reg;
+	int rc;
+
+	pval.intval = (blinking == 0) ? 0 : 1;
+	power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_HI_POWER,
+			&pval);
+
+	if (blinking == 0) {
+		reg = CHG_LED_OFF << CHG_LED_SHIFT;
+	} else {
+		if (blinking == 1)
+			reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
+		else if (blinking == 2)
+			reg = LED_BLINKING_PATTERN2 << CHG_LED_SHIFT;
+		else
+			reg = LED_BLINKING_PATTERN1 << CHG_LED_SHIFT;
+	}
+
+	rc = smbchg_sec_masked_write(chip,
+			chip->bat_if_base + CMD_CHG_LED_REG,
+			LED_BLINKING_CFG_MASK, reg);
+	if (rc)
+		dev_err(chip->dev, "Couldn't write CHG_LED rc=%d\n",
+				rc);
+}
+
+static ssize_t smbchg_chg_led_blink_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct led_classdev *cdev = dev_get_drvdata(dev);
+	struct smbchg_chip *chip = container_of(cdev, struct smbchg_chip,
+			led_cdev);
+	unsigned long blinking;
+	ssize_t rc = -EINVAL;
+
+	rc = kstrtoul(buf, 10, &blinking);
+	if (rc)
+		return rc;
+
+	smbchg_chg_led_blink_set(chip, blinking);
+
+	return len;
+}
+
+static DEVICE_ATTR(blink, 0664, NULL, smbchg_chg_led_blink_store);
+
+static struct attribute *led_blink_attributes[] = {
+	&dev_attr_blink.attr,
+	NULL,
+};
+
+static struct attribute_group smbchg_led_attr_group = {
+	.attrs = led_blink_attributes
+};
+
+static int smbchg_register_chg_led(struct smbchg_chip *chip)
+{
+	int rc;
+
+	chip->led_cdev.name = "red";
+	chip->led_cdev.brightness_set = smbchg_chg_led_brightness_set;
+	chip->led_cdev.brightness_get = smbchg_chg_led_brightness_get;
+
+	rc = led_classdev_register(chip->dev, &chip->led_cdev);
+	if (rc) {
+		dev_err(chip->dev, "unable to register charger led, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = sysfs_create_group(&chip->led_cdev.dev->kobj,
+			&smbchg_led_attr_group);
+	if (rc) {
+		dev_err(chip->dev, "led sysfs rc: %d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int vf_adjust_low_threshold = 5;
+module_param(vf_adjust_low_threshold, int, 0644);
+
+static int vf_adjust_high_threshold = 7;
+module_param(vf_adjust_high_threshold, int, 0644);
+
+static int vf_adjust_n_samples = 10;
+module_param(vf_adjust_n_samples, int, 0644);
+
+static int vf_adjust_max_delta_mv = 40;
+module_param(vf_adjust_max_delta_mv, int, 0644);
+
+static int vf_adjust_trim_steps_per_adjust = 1;
+module_param(vf_adjust_trim_steps_per_adjust, int, 0644);
+
+#define CENTER_TRIM_CODE		7
+#define MAX_LIN_CODE			14
+#define MAX_TRIM_CODE			15
+#define SCALE_SHIFT			4
+#define VF_TRIM_OFFSET_MASK		SMB_MASK(3, 0)
+#define VF_STEP_SIZE_MV			10
+#define SCALE_LSB_MV			17
+static int smbchg_trim_add_steps(int prev_trim, int delta_steps)
+{
+	int scale_steps;
+	int linear_offset, linear_scale;
+	int offset_code = prev_trim & VF_TRIM_OFFSET_MASK;
+	int scale_code = (prev_trim & ~VF_TRIM_OFFSET_MASK) >> SCALE_SHIFT;
+
+	if (abs(delta_steps) > 1) {
+		pr_smb(PR_STATUS,
+			"Cant trim multiple steps delta_steps = %d\n",
+			delta_steps);
+		return prev_trim;
+	}
+	if (offset_code <= CENTER_TRIM_CODE)
+		linear_offset = offset_code + CENTER_TRIM_CODE;
+	else if (offset_code > CENTER_TRIM_CODE)
+		linear_offset = MAX_TRIM_CODE - offset_code;
+
+	if (scale_code <= CENTER_TRIM_CODE)
+		linear_scale = scale_code + CENTER_TRIM_CODE;
+	else if (scale_code > CENTER_TRIM_CODE)
+		linear_scale = scale_code - (CENTER_TRIM_CODE + 1);
+
+	/* check if we can accommodate delta steps with just the offset */
+	if (linear_offset + delta_steps >= 0
+			&& linear_offset + delta_steps <= MAX_LIN_CODE) {
+		linear_offset += delta_steps;
+
+		if (linear_offset > CENTER_TRIM_CODE)
+			offset_code = linear_offset - CENTER_TRIM_CODE;
+		else
+			offset_code = MAX_TRIM_CODE - linear_offset;
+
+		return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
+	}
+
+	/* changing offset cannot satisfy delta steps, change the scale bits */
+	scale_steps = delta_steps > 0 ? 1 : -1;
+
+	if (linear_scale + scale_steps < 0
+			|| linear_scale + scale_steps > MAX_LIN_CODE) {
+		pr_smb(PR_STATUS,
+			"Cant trim scale_steps = %d delta_steps = %d\n",
+			scale_steps, delta_steps);
+		return prev_trim;
+	}
+
+	linear_scale += scale_steps;
+
+	if (linear_scale > CENTER_TRIM_CODE)
+		scale_code = linear_scale - CENTER_TRIM_CODE;
+	else
+		scale_code = linear_scale + (CENTER_TRIM_CODE + 1);
+	prev_trim = (prev_trim & VF_TRIM_OFFSET_MASK)
+		| scale_code << SCALE_SHIFT;
+
+	/*
+	 * now that we have changed scale which is a 17mV jump, change the
+	 * offset bits (10mV) too so the effective change is just 7mV
+	 */
+	delta_steps = -1 * delta_steps;
+
+	linear_offset = clamp(linear_offset + delta_steps, 0, MAX_LIN_CODE);
+	if (linear_offset > CENTER_TRIM_CODE)
+		offset_code = linear_offset - CENTER_TRIM_CODE;
+	else
+		offset_code = MAX_TRIM_CODE - linear_offset;
+
+	return (prev_trim & ~VF_TRIM_OFFSET_MASK) | offset_code;
+}
+
+#define TRIM_14		0xFE
+#define VF_TRIM_MASK	0xFF
+static int smbchg_adjust_vfloat_mv_trim(struct smbchg_chip *chip,
+						int delta_mv)
+{
+	int sign, delta_steps, rc = 0;
+	u8 prev_trim, new_trim;
+	int i;
+
+	sign = delta_mv > 0 ? 1 : -1;
+	delta_steps = (delta_mv + sign * VF_STEP_SIZE_MV / 2)
+			/ VF_STEP_SIZE_MV;
+
+	rc = smbchg_read(chip, &prev_trim, chip->misc_base + TRIM_14, 1);
+	if (rc) {
+		dev_err(chip->dev, "Unable to read trim 14: %d\n", rc);
+		return rc;
+	}
+
+	for (i = 1; i <= abs(delta_steps)
+			&& i <= vf_adjust_trim_steps_per_adjust; i++) {
+		new_trim = (u8)smbchg_trim_add_steps(prev_trim,
+				delta_steps > 0 ? 1 : -1);
+		if (new_trim == prev_trim) {
+			pr_smb(PR_STATUS,
+				"VFloat trim unchanged from %02x\n", prev_trim);
+			/* treat no trim change as an error */
+			return -EINVAL;
+		}
+
+		rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_14,
+				VF_TRIM_MASK, new_trim);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't change vfloat trim rc=%d\n", rc);
+		}
+		pr_smb(PR_STATUS,
+			"VFlt trim %02x to %02x, delta steps: %d\n",
+			prev_trim, new_trim, delta_steps);
+		prev_trim = new_trim;
+	}
+
+	return rc;
+}
+
+#define VFLOAT_RESAMPLE_DELAY_MS	10000
+static void smbchg_vfloat_adjust_work(struct work_struct *work)
+{
+	struct smbchg_chip *chip = container_of(work,
+				struct smbchg_chip,
+				vfloat_adjust_work.work);
+	int vbat_uv, vbat_mv, ibat_ua, rc, delta_vfloat_mv;
+	bool taper, enable;
+
+	smbchg_stay_awake(chip, PM_REASON_VFLOAT_ADJUST);
+	taper = (get_prop_charge_type(chip)
+		== POWER_SUPPLY_CHARGE_TYPE_TAPER);
+	enable = taper && (chip->parallel.current_max_ma == 0);
+
+	if (!enable) {
+		pr_smb(PR_MISC,
+			"Stopping vfloat adj taper=%d parallel_ma = %d\n",
+			taper, chip->parallel.current_max_ma);
+		goto stop;
+	}
+
+	if (get_prop_batt_health(chip) != POWER_SUPPLY_HEALTH_GOOD) {
+		pr_smb(PR_STATUS, "JEITA active, skipping\n");
+		goto stop;
+	}
+
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_UPDATE_NOW, 1);
+	rc = get_property_from_fg(chip,
+			POWER_SUPPLY_PROP_VOLTAGE_NOW, &vbat_uv);
+	if (rc) {
+		pr_smb(PR_STATUS,
+			"bms psy does not support voltage rc = %d\n", rc);
+		goto stop;
+	}
+	vbat_mv = vbat_uv / 1000;
+
+	if ((vbat_mv - chip->vfloat_mv) < -1 * vf_adjust_max_delta_mv) {
+		pr_smb(PR_STATUS, "Skip vbat out of range: %d\n", vbat_mv);
+		goto reschedule;
+	}
+
+	rc = get_property_from_fg(chip,
+			POWER_SUPPLY_PROP_CURRENT_NOW, &ibat_ua);
+	if (rc) {
+		pr_smb(PR_STATUS,
+			"bms psy does not support current_now rc = %d\n", rc);
+		goto stop;
+	}
+
+	if (ibat_ua / 1000 > -chip->iterm_ma) {
+		pr_smb(PR_STATUS, "Skip ibat too high: %d\n", ibat_ua);
+		goto reschedule;
+	}
+
+	pr_smb(PR_STATUS, "sample number = %d vbat_mv = %d ibat_ua = %d\n",
+		chip->n_vbat_samples,
+		vbat_mv,
+		ibat_ua);
+
+	chip->max_vbat_sample = max(chip->max_vbat_sample, vbat_mv);
+	chip->n_vbat_samples += 1;
+	if (chip->n_vbat_samples < vf_adjust_n_samples) {
+		pr_smb(PR_STATUS, "Skip %d samples; max = %d\n",
+			chip->n_vbat_samples, chip->max_vbat_sample);
+		goto reschedule;
+	}
+	/* if max vbat > target vfloat, delta_vfloat_mv could be negative */
+	delta_vfloat_mv = chip->vfloat_mv - chip->max_vbat_sample;
+	pr_smb(PR_STATUS, "delta_vfloat_mv = %d, samples = %d, mvbat = %d\n",
+		delta_vfloat_mv, chip->n_vbat_samples, chip->max_vbat_sample);
+	/*
+	 * enough valid samples has been collected, adjust trim codes
+	 * based on maximum of collected vbat samples if necessary
+	 */
+	if (delta_vfloat_mv > vf_adjust_high_threshold
+			|| delta_vfloat_mv < -1 * vf_adjust_low_threshold) {
+		rc = smbchg_adjust_vfloat_mv_trim(chip, delta_vfloat_mv);
+		if (rc) {
+			pr_smb(PR_STATUS,
+				"Stopping vfloat adj after trim adj rc = %d\n",
+				 rc);
+			goto stop;
+		}
+		chip->max_vbat_sample = 0;
+		chip->n_vbat_samples = 0;
+		goto reschedule;
+	}
+
+stop:
+	chip->max_vbat_sample = 0;
+	chip->n_vbat_samples = 0;
+	smbchg_relax(chip, PM_REASON_VFLOAT_ADJUST);
+	return;
+
+reschedule:
+	schedule_delayed_work(&chip->vfloat_adjust_work,
+			msecs_to_jiffies(VFLOAT_RESAMPLE_DELAY_MS));
+}
+
+static int smbchg_charging_status_change(struct smbchg_chip *chip)
+{
+	smbchg_vfloat_adjust_check(chip);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+			get_prop_batt_status(chip));
+	return 0;
+}
+
+#define BB_CLMP_SEL		0xF8
+#define BB_CLMP_MASK		SMB_MASK(1, 0)
+#define BB_CLMP_VFIX_3338MV	0x1
+#define BB_CLMP_VFIX_3512MV	0x2
+static int smbchg_set_optimal_charging_mode(struct smbchg_chip *chip, int type)
+{
+	int rc;
+	bool hvdcp2 = (type == POWER_SUPPLY_TYPE_USB_HVDCP
+			&& smbchg_is_usbin_active_pwr_src(chip));
+
+	/*
+	 * Set the charger switching freq to 1MHZ if HVDCP 2.0,
+	 * or 750KHZ otherwise
+	 */
+	rc = smbchg_sec_masked_write(chip,
+			chip->bat_if_base + BAT_IF_TRIM7_REG,
+			CFG_750KHZ_BIT, hvdcp2 ? 0 : CFG_750KHZ_BIT);
+	if (rc) {
+		dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Set the charger switch frequency clamp voltage threshold to 3.338V
+	 * if HVDCP 2.0, or 3.512V otherwise.
+	 */
+	rc = smbchg_sec_masked_write(chip, chip->bat_if_base + BB_CLMP_SEL,
+			BB_CLMP_MASK,
+			hvdcp2 ? BB_CLMP_VFIX_3338MV : BB_CLMP_VFIX_3512MV);
+	if (rc) {
+		dev_err(chip->dev, "Cannot set switching freq: %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+#define DEFAULT_SDP_MA		100
+#define DEFAULT_CDP_MA		1500
+static int smbchg_change_usb_supply_type(struct smbchg_chip *chip,
+						enum power_supply_type type)
+{
+	int rc, current_limit_ma;
+
+	/*
+	 * if the type is not unknown, set the type before changing ICL vote
+	 * in order to ensure that the correct current limit registers are
+	 * used
+	 */
+	if (type != POWER_SUPPLY_TYPE_UNKNOWN)
+		chip->usb_supply_type = type;
+
+	/*
+	 * Type-C only supports STD(900), MEDIUM(1500) and HIGH(3000) current
+	 * modes, skip all BC 1.2 current if external typec is supported.
+	 * Note: for SDP supporting current based on USB notifications.
+	 */
+	if (chip->typec_psy && (type != POWER_SUPPLY_TYPE_USB))
+		current_limit_ma = chip->typec_current_ma;
+	else if (type == POWER_SUPPLY_TYPE_USB)
+		current_limit_ma = DEFAULT_SDP_MA;
+	else if (type == POWER_SUPPLY_TYPE_USB_CDP)
+		current_limit_ma = DEFAULT_CDP_MA;
+	else if (type == POWER_SUPPLY_TYPE_USB_HVDCP)
+		current_limit_ma = smbchg_default_hvdcp_icl_ma;
+	else if (type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
+		current_limit_ma = smbchg_default_hvdcp3_icl_ma;
+	else
+		current_limit_ma = smbchg_default_dcp_icl_ma;
+
+	pr_smb(PR_STATUS, "Type %d: setting mA = %d\n",
+		type, current_limit_ma);
+	rc = vote(chip->usb_icl_votable, PSY_ICL_VOTER, true,
+				current_limit_ma);
+	if (rc < 0) {
+		pr_err("Couldn't vote for new USB ICL rc=%d\n", rc);
+		goto out;
+	}
+
+	/* otherwise if it is unknown, set type after the vote */
+	if (type == POWER_SUPPLY_TYPE_UNKNOWN)
+		chip->usb_supply_type = type;
+
+	if (!chip->skip_usb_notification)
+		power_supply_changed(chip->usb_psy);
+
+	/* set the correct buck switching frequency */
+	rc = smbchg_set_optimal_charging_mode(chip, type);
+	if (rc < 0)
+		pr_err("Couldn't set charger optimal mode rc=%d\n", rc);
+
+out:
+	return rc;
+}
+
+#define HVDCP_ADAPTER_SEL_MASK	SMB_MASK(5, 4)
+#define HVDCP_5V		0x00
+#define HVDCP_9V		0x10
+#define USB_CMD_HVDCP_1		0x42
+#define FORCE_HVDCP_2p0		BIT(3)
+
+static int force_9v_hvdcp(struct smbchg_chip *chip)
+{
+	int rc;
+
+	/* Force 5V HVDCP */
+	rc = smbchg_sec_masked_write(chip,
+			chip->usb_chgpth_base + CHGPTH_CFG,
+			HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+	if (rc) {
+		pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Force QC2.0 */
+	rc = smbchg_masked_write(chip,
+			chip->usb_chgpth_base + USB_CMD_HVDCP_1,
+			FORCE_HVDCP_2p0, FORCE_HVDCP_2p0);
+	rc |= smbchg_masked_write(chip,
+			chip->usb_chgpth_base + USB_CMD_HVDCP_1,
+			FORCE_HVDCP_2p0, 0);
+	if (rc < 0) {
+		pr_err("Couldn't force QC2.0 rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Delay to switch into HVDCP 2.0 and avoid UV */
+	msleep(500);
+
+	/* Force 9V HVDCP */
+	rc = smbchg_sec_masked_write(chip,
+			chip->usb_chgpth_base + CHGPTH_CFG,
+			HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+	if (rc)
+		pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n", rc);
+
+	return rc;
+}
+
+static void smbchg_hvdcp_det_work(struct work_struct *work)
+{
+	struct smbchg_chip *chip = container_of(work,
+				struct smbchg_chip,
+				hvdcp_det_work.work);
+	int rc;
+
+	if (is_hvdcp_present(chip)) {
+		if (!chip->hvdcp3_supported &&
+			(chip->wa_flags & SMBCHG_HVDCP_9V_EN_WA)) {
+			/* force HVDCP 2.0 */
+			rc = force_9v_hvdcp(chip);
+			if (rc)
+				pr_err("could not force 9V HVDCP continuing rc=%d\n",
+						rc);
+		}
+		smbchg_change_usb_supply_type(chip,
+				POWER_SUPPLY_TYPE_USB_HVDCP);
+		if (chip->batt_psy)
+			power_supply_changed(chip->batt_psy);
+		smbchg_aicl_deglitch_wa_check(chip);
+	}
+	smbchg_relax(chip, PM_DETECT_HVDCP);
+}
+
+static int set_usb_psy_dp_dm(struct smbchg_chip *chip, int state)
+{
+	int rc;
+	u8 reg;
+	union power_supply_propval pval = {0, };
+
+	/*
+	 * ensure that we are not in the middle of an insertion where usbin_uv
+	 * is low and src_detect hasnt gone high. If so force dp=F dm=F
+	 * which guarantees proper type detection
+	 */
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (!rc && !(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
+		pr_smb(PR_MISC, "overwriting state = %d with %d\n",
+				state, POWER_SUPPLY_DP_DM_DPF_DMF);
+		if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+			return regulator_enable(chip->dpdm_reg);
+	}
+	pr_smb(PR_MISC, "setting usb psy dp dm = %d\n", state);
+	pval.intval = state;
+	return power_supply_set_property(chip->usb_psy,
+				POWER_SUPPLY_PROP_DP_DM, &pval);
+}
+
+#define APSD_CFG		0xF5
+#define AUTO_SRC_DETECT_EN_BIT	BIT(0)
+#define APSD_TIMEOUT_MS		1500
+static void restore_from_hvdcp_detection(struct smbchg_chip *chip)
+{
+	int rc;
+
+	pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+	/* switch to 9V HVDCP */
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+	if (rc < 0)
+		pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
+
+	/* enable HVDCP */
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_EN_BIT, HVDCP_EN_BIT);
+	if (rc < 0)
+		pr_err("Couldn't enable HVDCP rc=%d\n", rc);
+
+	/* enable APSD */
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + APSD_CFG,
+				AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
+	if (rc < 0)
+		pr_err("Couldn't enable APSD rc=%d\n", rc);
+
+	/* Reset back to 5V unregulated */
+	rc = smbchg_sec_masked_write(chip,
+		chip->usb_chgpth_base + USBIN_CHGR_CFG,
+		ADAPTER_ALLOWANCE_MASK, USBIN_ADAPTER_5V_UNREGULATED_9V);
+	if (rc < 0)
+		pr_err("Couldn't write usb allowance rc=%d\n", rc);
+
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, AICL_EN_BIT);
+	if (rc < 0)
+		pr_err("Couldn't enable AICL rc=%d\n", rc);
+
+	chip->hvdcp_3_det_ignore_uv = false;
+	chip->pulse_cnt = 0;
+}
+
+#define RESTRICTED_CHG_FCC_PERCENT	50
+static int smbchg_restricted_charging(struct smbchg_chip *chip, bool enable)
+{
+	int current_table_index, fastchg_current;
+	int rc = 0;
+
+	/* If enable, set the fcc to the set point closest
+	 * to 50% of the configured fcc while remaining below it
+	 */
+	current_table_index = find_smaller_in_array(
+			chip->tables.usb_ilim_ma_table,
+			chip->cfg_fastchg_current_ma
+				* RESTRICTED_CHG_FCC_PERCENT / 100,
+			chip->tables.usb_ilim_ma_len);
+	fastchg_current =
+		chip->tables.usb_ilim_ma_table[current_table_index];
+	rc = vote(chip->fcc_votable, RESTRICTED_CHG_FCC_VOTER, enable,
+			fastchg_current);
+
+	pr_smb(PR_STATUS, "restricted_charging set to %d\n", enable);
+	chip->restricted_charging = enable;
+
+	return rc;
+}
+
+static void handle_usb_removal(struct smbchg_chip *chip)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	int rc;
+
+	pr_smb(PR_STATUS, "triggered\n");
+	smbchg_aicl_deglitch_wa_check(chip);
+	/* Clear the OV detected status set before */
+	if (chip->usb_ov_det)
+		chip->usb_ov_det = false;
+	/* Clear typec current status */
+	if (chip->typec_psy)
+		chip->typec_current_ma = 0;
+	smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_UNKNOWN);
+	extcon_set_cable_state_(chip->extcon, EXTCON_USB, chip->usb_present);
+	if (chip->dpdm_reg)
+		regulator_disable(chip->dpdm_reg);
+	schedule_work(&chip->usb_set_online_work);
+
+	pr_smb(PR_MISC, "setting usb psy health UNKNOWN\n");
+	chip->usb_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+	power_supply_changed(chip->usb_psy);
+
+	if (parallel_psy && chip->parallel_charger_detected) {
+		pval.intval = false;
+		power_supply_set_property(parallel_psy,
+				POWER_SUPPLY_PROP_PRESENT, &pval);
+	}
+	if (chip->parallel.avail && chip->aicl_done_irq
+			&& chip->enable_aicl_wake) {
+		disable_irq_wake(chip->aicl_done_irq);
+		chip->enable_aicl_wake = false;
+	}
+	chip->parallel.enabled_once = false;
+	chip->vbat_above_headroom = false;
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+			ICL_OVERRIDE_BIT, 0);
+	if (rc < 0)
+		pr_err("Couldn't set override rc = %d\n", rc);
+
+	vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER, false, 0);
+	chip->usb_icl_delta = 0;
+	vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, false, 0);
+	vote(chip->aicl_deglitch_short_votable,
+		HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
+	if (!chip->hvdcp_not_supported)
+		restore_from_hvdcp_detection(chip);
+}
+
+static bool is_usbin_uv_high(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+		return false;
+	}
+	return reg &= USBIN_UV_BIT;
+}
+
+#define HVDCP_NOTIFY_MS		2500
+static void handle_usb_insertion(struct smbchg_chip *chip)
+{
+	struct power_supply *parallel_psy = get_parallel_psy(chip);
+	union power_supply_propval pval = {0, };
+	enum power_supply_type usb_supply_type;
+	int rc;
+	char *usb_type_name = "null";
+
+	pr_smb(PR_STATUS, "triggered\n");
+	/* usb inserted */
+	read_usb_type(chip, &usb_type_name, &usb_supply_type);
+	pr_smb(PR_STATUS,
+		"inserted type = %d (%s)", usb_supply_type, usb_type_name);
+
+	smbchg_aicl_deglitch_wa_check(chip);
+	if (chip->typec_psy)
+		update_typec_status(chip);
+	smbchg_change_usb_supply_type(chip, usb_supply_type);
+
+	/* Only notify USB if it's not a charger */
+	if (usb_supply_type == POWER_SUPPLY_TYPE_USB ||
+			usb_supply_type == POWER_SUPPLY_TYPE_USB_CDP)
+		extcon_set_cable_state_(chip->extcon, EXTCON_USB,
+				chip->usb_present);
+
+	/* Notify the USB psy if OV condition is not present */
+	if (!chip->usb_ov_det) {
+		/*
+		 * Note that this could still be a very weak charger
+		 * if the handle_usb_insertion was triggered from
+		 * the falling edge of an USBIN_OV interrupt
+		 */
+		pr_smb(PR_MISC, "setting usb psy health %s\n",
+				chip->very_weak_charger
+				? "UNSPEC_FAILURE" : "GOOD");
+		chip->usb_health = chip->very_weak_charger
+				? POWER_SUPPLY_HEALTH_UNSPEC_FAILURE
+				: POWER_SUPPLY_HEALTH_GOOD;
+		power_supply_changed(chip->usb_psy);
+	}
+	schedule_work(&chip->usb_set_online_work);
+
+	if (!chip->hvdcp_not_supported &&
+			(usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP)) {
+		cancel_delayed_work_sync(&chip->hvdcp_det_work);
+		smbchg_stay_awake(chip, PM_DETECT_HVDCP);
+		schedule_delayed_work(&chip->hvdcp_det_work,
+					msecs_to_jiffies(HVDCP_NOTIFY_MS));
+	}
+
+	if (parallel_psy) {
+		pval.intval = true;
+		rc = power_supply_set_property(parallel_psy,
+				POWER_SUPPLY_PROP_PRESENT, &pval);
+		chip->parallel_charger_detected = rc ? false : true;
+		if (rc)
+			pr_debug("parallel-charger absent rc=%d\n", rc);
+	}
+
+	if (chip->parallel.avail && chip->aicl_done_irq
+			&& !chip->enable_aicl_wake) {
+		rc = enable_irq_wake(chip->aicl_done_irq);
+		chip->enable_aicl_wake = true;
+	}
+}
+
+void update_usb_status(struct smbchg_chip *chip, bool usb_present, bool force)
+{
+	mutex_lock(&chip->usb_status_lock);
+	if (force) {
+		chip->usb_present = usb_present;
+		chip->usb_present ? handle_usb_insertion(chip)
+			: handle_usb_removal(chip);
+		goto unlock;
+	}
+	if (!chip->usb_present && usb_present) {
+		chip->usb_present = usb_present;
+		handle_usb_insertion(chip);
+	} else if (chip->usb_present && !usb_present) {
+		chip->usb_present = usb_present;
+		handle_usb_removal(chip);
+	}
+
+	/* update FG */
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+			get_prop_batt_status(chip));
+unlock:
+	mutex_unlock(&chip->usb_status_lock);
+}
+
+static int otg_oc_reset(struct smbchg_chip *chip)
+{
+	int rc;
+
+	rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+						OTG_EN_BIT, 0);
+	if (rc)
+		pr_err("Failed to disable OTG rc=%d\n", rc);
+
+	msleep(20);
+
+	/*
+	 * There is a possibility that an USBID interrupt might have
+	 * occurred notifying USB power supply to disable OTG. We
+	 * should not enable OTG in such cases.
+	 */
+	if (!is_otg_present(chip)) {
+		pr_smb(PR_STATUS,
+			"OTG is not present, not enabling OTG_EN_BIT\n");
+		goto out;
+	}
+
+	rc = smbchg_masked_write(chip, chip->bat_if_base + CMD_CHG_REG,
+						OTG_EN_BIT, OTG_EN_BIT);
+	if (rc)
+		pr_err("Failed to re-enable OTG rc=%d\n", rc);
+
+out:
+	return rc;
+}
+
+static int get_current_time(unsigned long *now_tm_sec)
+{
+	struct rtc_time tm;
+	struct rtc_device *rtc;
+	int rc;
+
+	rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+	if (rtc == NULL) {
+		pr_err("%s: unable to open rtc device (%s)\n",
+			__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
+		return -EINVAL;
+	}
+
+	rc = rtc_read_time(rtc, &tm);
+	if (rc) {
+		pr_err("Error reading rtc device (%s) : %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto close_time;
+	}
+
+	rc = rtc_valid_tm(&tm);
+	if (rc) {
+		pr_err("Invalid RTC time (%s): %d\n",
+			CONFIG_RTC_HCTOSYS_DEVICE, rc);
+		goto close_time;
+	}
+	rtc_tm_to_time(&tm, now_tm_sec);
+
+close_time:
+	rtc_class_close(rtc);
+	return rc;
+}
+
+#define AICL_IRQ_LIMIT_SECONDS	60
+#define AICL_IRQ_LIMIT_COUNT	25
+static void increment_aicl_count(struct smbchg_chip *chip)
+{
+	bool bad_charger = false;
+	int max_aicl_count, rc;
+	u8 reg;
+	long elapsed_seconds;
+	unsigned long now_seconds;
+
+	pr_smb(PR_INTERRUPT, "aicl count c:%d dgltch:%d first:%ld\n",
+			chip->aicl_irq_count, chip->aicl_deglitch_short,
+			chip->first_aicl_seconds);
+
+	rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + ICL_STS_1_REG, 1);
+	if (!rc)
+		chip->aicl_complete = reg & AICL_STS_BIT;
+	else
+		chip->aicl_complete = false;
+
+	if (chip->aicl_deglitch_short || chip->force_aicl_rerun) {
+		if (!chip->aicl_irq_count)
+			get_current_time(&chip->first_aicl_seconds);
+		get_current_time(&now_seconds);
+		elapsed_seconds = now_seconds
+				- chip->first_aicl_seconds;
+
+		if (elapsed_seconds > AICL_IRQ_LIMIT_SECONDS) {
+			pr_smb(PR_INTERRUPT,
+				"resetting: elp:%ld first:%ld now:%ld c=%d\n",
+				elapsed_seconds, chip->first_aicl_seconds,
+				now_seconds, chip->aicl_irq_count);
+			chip->aicl_irq_count = 1;
+			get_current_time(&chip->first_aicl_seconds);
+			return;
+		}
+		/*
+		 * Double the amount of AICLs allowed if parallel charging is
+		 * enabled.
+		 */
+		max_aicl_count = AICL_IRQ_LIMIT_COUNT
+			* (chip->parallel.avail ? 2 : 1);
+		chip->aicl_irq_count++;
+
+		if (chip->aicl_irq_count > max_aicl_count) {
+			pr_smb(PR_INTERRUPT, "elp:%ld first:%ld now:%ld c=%d\n",
+				elapsed_seconds, chip->first_aicl_seconds,
+				now_seconds, chip->aicl_irq_count);
+			pr_smb(PR_INTERRUPT, "Disable AICL rerun\n");
+			chip->very_weak_charger = true;
+			bad_charger = true;
+
+			/*
+			 * Disable AICL rerun since many interrupts were
+			 * triggered in a short time
+			 */
+			/* disable hw aicl */
+			rc = vote(chip->hw_aicl_rerun_disable_votable,
+				WEAK_CHARGER_HW_AICL_VOTER, true, 0);
+			if (rc < 0) {
+				pr_err("Couldn't disable hw aicl rerun rc=%d\n",
+					rc);
+				return;
+			}
+
+			/* Vote 100mA current limit */
+			rc = vote(chip->usb_icl_votable, WEAK_CHARGER_ICL_VOTER,
+					true, CURRENT_100_MA);
+			if (rc < 0) {
+				pr_err("Can't vote %d current limit rc=%d\n",
+					CURRENT_100_MA, rc);
+			}
+
+			chip->aicl_irq_count = 0;
+		} else if ((get_prop_charge_type(chip) ==
+				POWER_SUPPLY_CHARGE_TYPE_FAST) &&
+					(reg & AICL_SUSP_BIT)) {
+			/*
+			 * If the AICL_SUSP_BIT is on, then AICL reruns have
+			 * already been disabled. Set the very weak charger
+			 * flag so that the driver reports a bad charger
+			 * and does not reenable AICL reruns.
+			 */
+			chip->very_weak_charger = true;
+			bad_charger = true;
+		}
+		if (bad_charger) {
+			pr_smb(PR_MISC,
+				"setting usb psy health UNSPEC_FAILURE\n");
+			chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+			power_supply_changed(chip->usb_psy);
+			schedule_work(&chip->usb_set_online_work);
+		}
+	}
+}
+
+static int wait_for_usbin_uv(struct smbchg_chip *chip, bool high)
+{
+	int rc;
+	int tries = 3;
+	struct completion *completion = &chip->usbin_uv_lowered;
+	bool usbin_uv;
+
+	if (high)
+		completion = &chip->usbin_uv_raised;
+
+	while (tries--) {
+		rc = wait_for_completion_interruptible_timeout(
+				completion,
+				msecs_to_jiffies(APSD_TIMEOUT_MS));
+		if (rc >= 0)
+			break;
+	}
+
+	usbin_uv = is_usbin_uv_high(chip);
+
+	if (high == usbin_uv)
+		return 0;
+
+	pr_err("usbin uv didnt go to a %s state, still at %s, tries = %d, rc = %d\n",
+			high ? "risen" : "lowered",
+			usbin_uv ? "high" : "low",
+			tries, rc);
+	return -EINVAL;
+}
+
+static int wait_for_src_detect(struct smbchg_chip *chip, bool high)
+{
+	int rc;
+	int tries = 3;
+	struct completion *completion = &chip->src_det_lowered;
+	bool src_detect;
+
+	if (high)
+		completion = &chip->src_det_raised;
+
+	while (tries--) {
+		rc = wait_for_completion_interruptible_timeout(
+				completion,
+				msecs_to_jiffies(APSD_TIMEOUT_MS));
+		if (rc >= 0)
+			break;
+	}
+
+	src_detect = is_src_detect_high(chip);
+
+	if (high == src_detect)
+		return 0;
+
+	pr_err("src detect didn't go to a %s state, still at %s, tries = %d, rc = %d\n",
+			high ? "risen" : "lowered",
+			src_detect ? "high" : "low",
+			tries, rc);
+	return -EINVAL;
+}
+
+static int fake_insertion_removal(struct smbchg_chip *chip, bool insertion)
+{
+	int rc;
+	bool src_detect;
+	bool usbin_uv;
+
+	if (insertion) {
+		reinit_completion(&chip->src_det_raised);
+		reinit_completion(&chip->usbin_uv_lowered);
+	} else {
+		reinit_completion(&chip->src_det_lowered);
+		reinit_completion(&chip->usbin_uv_raised);
+	}
+
+	/* ensure that usbin uv real time status is in the right state */
+	usbin_uv = is_usbin_uv_high(chip);
+	if (usbin_uv != insertion) {
+		pr_err("Skip faking, usbin uv is already %d\n", usbin_uv);
+		return -EINVAL;
+	}
+
+	/* ensure that src_detect real time status is in the right state */
+	src_detect = is_src_detect_high(chip);
+	if (src_detect == insertion) {
+		pr_err("Skip faking, src detect is already %d\n", src_detect);
+		return -EINVAL;
+	}
+
+	pr_smb(PR_MISC, "Allow only %s charger\n",
+			insertion ? "5-9V" : "9V only");
+	rc = smbchg_sec_masked_write(chip,
+			chip->usb_chgpth_base + USBIN_CHGR_CFG,
+			ADAPTER_ALLOWANCE_MASK,
+			insertion ?
+			USBIN_ADAPTER_5V_9V_CONT : USBIN_ADAPTER_9V);
+	if (rc < 0) {
+		pr_err("Couldn't write usb allowance rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on %s usbin uv\n",
+			insertion ? "falling" : "rising");
+	rc = wait_for_usbin_uv(chip, !insertion);
+	if (rc < 0) {
+		pr_err("wait for usbin uv failed rc = %d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on %s src det\n",
+			insertion ? "rising" : "falling");
+	rc = wait_for_src_detect(chip, insertion);
+	if (rc < 0) {
+		pr_err("wait for src detect failed rc = %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int smbchg_prepare_for_pulsing(struct smbchg_chip *chip)
+{
+	int rc = 0;
+	u8 reg;
+
+	/* switch to 5V HVDCP */
+	pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+	if (rc < 0) {
+		pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+		goto out;
+	}
+
+	/* wait for HVDCP to lower to 5V */
+	msleep(500);
+	/*
+	 * Check if the same hvdcp session is in progress. src_det should be
+	 * high and that we are still in 5V hvdcp
+	 */
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+		goto out;
+	}
+
+	/* disable HVDCP */
+	pr_smb(PR_MISC, "Disable HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+			HVDCP_EN_BIT, 0);
+	if (rc < 0) {
+		pr_err("Couldn't disable HVDCP rc=%d\n", rc);
+		goto out;
+	}
+
+	pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
+	if (rc < 0) {
+		pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
+		goto out;
+	}
+
+	pr_smb(PR_MISC, "Disable AICL\n");
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, 0);
+
+	chip->hvdcp_3_det_ignore_uv = true;
+	/* fake a removal */
+	pr_smb(PR_MISC, "Faking Removal\n");
+	rc = fake_insertion_removal(chip, false);
+	if (rc < 0) {
+		pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
+		goto handle_removal;
+	}
+
+	/* disable APSD */
+	pr_smb(PR_MISC, "Disabling APSD\n");
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + APSD_CFG,
+				AUTO_SRC_DETECT_EN_BIT, 0);
+	if (rc < 0) {
+		pr_err("Couldn't disable APSD rc=%d\n", rc);
+		goto out;
+	}
+
+	/* fake an insertion */
+	pr_smb(PR_MISC, "Faking Insertion\n");
+	rc = fake_insertion_removal(chip, true);
+	if (rc < 0) {
+		pr_err("Couldn't fake insertion rc=%d\n", rc);
+		goto handle_removal;
+	}
+	chip->hvdcp_3_det_ignore_uv = false;
+
+	pr_smb(PR_MISC, "Enable AICL\n");
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, AICL_EN_BIT);
+
+	set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DMF);
+	/*
+	 * DCP will switch to HVDCP in this time by removing the short
+	 * between DP DM
+	 */
+	msleep(HVDCP_NOTIFY_MS);
+	/*
+	 * Check if the same hvdcp session is in progress. src_det should be
+	 * high and the usb type should be none since APSD was disabled
+	 */
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low after 2s sleep\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	smbchg_read(chip, &reg, chip->misc_base + IDEV_STS, 1);
+	if ((reg >> TYPE_BITS_OFFSET) != 0) {
+		pr_smb(PR_MISC, "type bits set after 2s sleep - abort\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	set_usb_psy_dp_dm(chip, POWER_SUPPLY_DP_DM_DP0P6_DM3P3);
+	/* Wait 60mS after entering continuous mode */
+	msleep(60);
+
+	return 0;
+out:
+	chip->hvdcp_3_det_ignore_uv = false;
+	restore_from_hvdcp_detection(chip);
+	return rc;
+handle_removal:
+	chip->hvdcp_3_det_ignore_uv = false;
+	update_usb_status(chip, 0, 0);
+	return rc;
+}
+
+static int smbchg_unprepare_for_pulsing(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+		rc = regulator_enable(chip->dpdm_reg);
+	if (rc < 0) {
+		pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+		return rc;
+	}
+
+	/* switch to 9V HVDCP */
+	pr_smb(PR_MISC, "Switch to 9V HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+	if (rc < 0) {
+		pr_err("Couldn't configure HVDCP 9V rc=%d\n", rc);
+		return rc;
+	}
+
+	/* enable HVDCP */
+	pr_smb(PR_MISC, "Enable HVDCP\n");
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_EN_BIT, HVDCP_EN_BIT);
+	if (rc < 0) {
+		pr_err("Couldn't enable HVDCP rc=%d\n", rc);
+		return rc;
+	}
+
+	/* enable APSD */
+	pr_smb(PR_MISC, "Enabling APSD\n");
+	rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + APSD_CFG,
+				AUTO_SRC_DETECT_EN_BIT, AUTO_SRC_DETECT_EN_BIT);
+	if (rc < 0) {
+		pr_err("Couldn't enable APSD rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Disable AICL */
+	pr_smb(PR_MISC, "Disable AICL\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, 0);
+	if (rc < 0) {
+		pr_err("Couldn't disable AICL rc=%d\n", rc);
+		return rc;
+	}
+
+	/* fake a removal */
+	chip->hvdcp_3_det_ignore_uv = true;
+	pr_smb(PR_MISC, "Faking Removal\n");
+	rc = fake_insertion_removal(chip, false);
+	if (rc < 0) {
+		pr_err("Couldn't fake removal rc=%d\n", rc);
+		goto out;
+	}
+
+	/*
+	 * reset the enabled once flag for parallel charging so
+	 * parallel charging can immediately restart after the HVDCP pulsing
+	 * is complete
+	 */
+	chip->parallel.enabled_once = false;
+
+	/* fake an insertion */
+	pr_smb(PR_MISC, "Faking Insertion\n");
+	rc = fake_insertion_removal(chip, true);
+	if (rc < 0) {
+		pr_err("Couldn't fake insertion rc=%d\n", rc);
+		goto out;
+	}
+	chip->hvdcp_3_det_ignore_uv = false;
+
+	/* Enable AICL */
+	pr_smb(PR_MISC, "Enable AICL\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, 0);
+	if (rc < 0) {
+		pr_err("Couldn't enable AICL rc=%d\n", rc);
+		return rc;
+	}
+
+out:
+	/*
+	 * There are many QC 2.0 chargers that collapse before the aicl deglitch
+	 * timer can mitigate. Hence set the aicl deglitch time to a shorter
+	 * period.
+	 */
+
+	rc = vote(chip->aicl_deglitch_short_votable,
+		HVDCP_SHORT_DEGLITCH_VOTER, true, 0);
+	if (rc < 0)
+		pr_err("Couldn't reduce aicl deglitch rc=%d\n", rc);
+
+	pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+	chip->hvdcp_3_det_ignore_uv = false;
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "HVDCP removed\n");
+		update_usb_status(chip, 0, 0);
+	}
+	return rc;
+}
+
+#define USB_CMD_APSD		0x41
+#define APSD_RERUN		BIT(0)
+static int rerun_apsd(struct smbchg_chip *chip)
+{
+	int rc;
+
+	reinit_completion(&chip->src_det_raised);
+	reinit_completion(&chip->usbin_uv_lowered);
+	reinit_completion(&chip->src_det_lowered);
+	reinit_completion(&chip->usbin_uv_raised);
+
+	/* re-run APSD */
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + USB_CMD_APSD,
+					APSD_RERUN, APSD_RERUN);
+	if (rc) {
+		pr_err("Couldn't re-run APSD rc=%d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on rising usbin uv\n");
+	rc = wait_for_usbin_uv(chip, true);
+	if (rc < 0) {
+		pr_err("wait for usbin uv failed rc = %d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on falling src det\n");
+	rc = wait_for_src_detect(chip, false);
+	if (rc < 0) {
+		pr_err("wait for src detect failed rc = %d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on falling usbin uv\n");
+	rc = wait_for_usbin_uv(chip, false);
+	if (rc < 0) {
+		pr_err("wait for usbin uv failed rc = %d\n", rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Waiting on rising src det\n");
+	rc = wait_for_src_detect(chip, true);
+	if (rc < 0) {
+		pr_err("wait for src detect failed rc = %d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+#define SCHG_LITE_USBIN_HVDCP_5_9V		0x8
+#define SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK	0x38
+#define SCHG_LITE_USBIN_HVDCP_SEL_IDLE		BIT(3)
+static bool is_hvdcp_5v_cont_mode(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg = 0;
+
+	rc = smbchg_read(chip, &reg,
+		chip->usb_chgpth_base + USBIN_HVDCP_STS, 1);
+	if (rc) {
+		pr_err("Unable to read HVDCP status rc=%d\n", rc);
+		return false;
+	}
+
+	pr_smb(PR_STATUS, "HVDCP status = %x\n", reg);
+
+	if (reg & SCHG_LITE_USBIN_HVDCP_SEL_IDLE) {
+		rc = smbchg_read(chip, &reg,
+			chip->usb_chgpth_base + INPUT_STS, 1);
+		if (rc) {
+			pr_err("Unable to read INPUT status rc=%d\n", rc);
+			return false;
+		}
+		pr_smb(PR_STATUS, "INPUT status = %x\n", reg);
+		if ((reg & SCHG_LITE_USBIN_HVDCP_5_9V_SEL_MASK) ==
+					SCHG_LITE_USBIN_HVDCP_5_9V)
+			return true;
+	}
+	return false;
+}
+
+static int smbchg_prepare_for_pulsing_lite(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	/* check if HVDCP is already in 5V continuous mode */
+	if (is_hvdcp_5v_cont_mode(chip)) {
+		pr_smb(PR_MISC, "HVDCP by default is in 5V continuous mode\n");
+		return 0;
+	}
+
+	/* switch to 5V HVDCP */
+	pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+	if (rc < 0) {
+		pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+		goto out;
+	}
+
+	/* wait for HVDCP to lower to 5V */
+	msleep(500);
+	/*
+	 * Check if the same hvdcp session is in progress. src_det should be
+	 * high and that we are still in 5V hvdcp
+	 */
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+		goto out;
+	}
+
+	pr_smb(PR_MISC, "HVDCP voting for 300mA ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, true, 300);
+	if (rc < 0) {
+		pr_err("Couldn't vote for 300mA HVDCP ICL rc=%d\n", rc);
+		goto out;
+	}
+
+	pr_smb(PR_MISC, "Disable AICL\n");
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, 0);
+
+	chip->hvdcp_3_det_ignore_uv = true;
+
+	/* re-run APSD */
+	rc = rerun_apsd(chip);
+	if (rc) {
+		pr_err("APSD rerun failed\n");
+		goto out;
+	}
+
+	chip->hvdcp_3_det_ignore_uv = false;
+
+	pr_smb(PR_MISC, "Enable AICL\n");
+	smbchg_sec_masked_write(chip, chip->usb_chgpth_base + USB_AICL_CFG,
+			AICL_EN_BIT, AICL_EN_BIT);
+	/*
+	 * DCP will switch to HVDCP in this time by removing the short
+	 * between DP DM
+	 */
+	msleep(HVDCP_NOTIFY_MS);
+	/*
+	 * Check if the same hvdcp session is in progress. src_det should be
+	 * high and the usb type should be none since APSD was disabled
+	 */
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low after 2s sleep\n");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	/* We are set if HVDCP in 5V continuous mode */
+	if (!is_hvdcp_5v_cont_mode(chip)) {
+		pr_err("HVDCP could not be set in 5V continuous mode\n");
+		goto out;
+	}
+
+	return 0;
+out:
+	chip->hvdcp_3_det_ignore_uv = false;
+	restore_from_hvdcp_detection(chip);
+	return rc;
+}
+
+static int smbchg_unprepare_for_pulsing_lite(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	pr_smb(PR_MISC, "Forcing 9V HVDCP 2.0\n");
+	rc = force_9v_hvdcp(chip);
+	if (rc) {
+		pr_err("Failed to force 9V HVDCP=%d\n",	rc);
+		return rc;
+	}
+
+	pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+	return rc;
+}
+
+#define CMD_HVDCP_2		0x43
+#define SINGLE_INCREMENT	BIT(0)
+#define SINGLE_DECREMENT	BIT(1)
+static int smbchg_dp_pulse_lite(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	pr_smb(PR_MISC, "Increment DP\n");
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
+				SINGLE_INCREMENT, SINGLE_INCREMENT);
+	if (rc)
+		pr_err("Single-increment failed rc=%d\n", rc);
+
+	return rc;
+}
+
+static int smbchg_dm_pulse_lite(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	pr_smb(PR_MISC, "Decrement DM\n");
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_HVDCP_2,
+				SINGLE_DECREMENT, SINGLE_DECREMENT);
+	if (rc)
+		pr_err("Single-decrement failed rc=%d\n", rc);
+
+	return rc;
+}
+
+static int smbchg_hvdcp3_confirmed(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	/*
+	 * reset the enabled once flag for parallel charging because this is
+	 * effectively a new insertion.
+	 */
+	chip->parallel.enabled_once = false;
+
+	pr_smb(PR_MISC, "Retracting HVDCP vote for ICL\n");
+	rc = vote(chip->usb_icl_votable, HVDCP_ICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't retract HVDCP ICL vote rc=%d\n", rc);
+
+	smbchg_change_usb_supply_type(chip, POWER_SUPPLY_TYPE_USB_HVDCP_3);
+
+	return rc;
+}
+
+static int smbchg_dp_dm(struct smbchg_chip *chip, int val)
+{
+	int rc = 0;
+	int target_icl_vote_ma;
+
+	switch (val) {
+	case POWER_SUPPLY_DP_DM_PREPARE:
+		if (!is_hvdcp_present(chip)) {
+			pr_err("No pulsing unless HVDCP\n");
+			return -ENODEV;
+		}
+		if (chip->schg_version == QPNP_SCHG_LITE)
+			rc = smbchg_prepare_for_pulsing_lite(chip);
+		else
+			rc = smbchg_prepare_for_pulsing(chip);
+		break;
+	case POWER_SUPPLY_DP_DM_UNPREPARE:
+		if (chip->schg_version == QPNP_SCHG_LITE)
+			rc = smbchg_unprepare_for_pulsing_lite(chip);
+		else
+			rc = smbchg_unprepare_for_pulsing(chip);
+		break;
+	case POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3:
+		rc = smbchg_hvdcp3_confirmed(chip);
+		break;
+	case POWER_SUPPLY_DP_DM_DP_PULSE:
+		if (chip->schg_version == QPNP_SCHG)
+			rc = set_usb_psy_dp_dm(chip,
+					POWER_SUPPLY_DP_DM_DP_PULSE);
+		else
+			rc = smbchg_dp_pulse_lite(chip);
+		if (!rc)
+			chip->pulse_cnt++;
+		pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
+		break;
+	case POWER_SUPPLY_DP_DM_DM_PULSE:
+		if (chip->schg_version == QPNP_SCHG)
+			rc = set_usb_psy_dp_dm(chip,
+					POWER_SUPPLY_DP_DM_DM_PULSE);
+		else
+			rc = smbchg_dm_pulse_lite(chip);
+		if (!rc && chip->pulse_cnt)
+			chip->pulse_cnt--;
+		pr_smb(PR_MISC, "pulse_cnt = %d\n", chip->pulse_cnt);
+		break;
+	case POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED:
+		chip->hvdcp3_supported = true;
+		pr_smb(PR_MISC, "HVDCP3 supported\n");
+		break;
+	case POWER_SUPPLY_DP_DM_ICL_DOWN:
+		chip->usb_icl_delta -= 100;
+		target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
+						PSY_ICL_VOTER);
+		vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
+				target_icl_vote_ma + chip->usb_icl_delta);
+		break;
+	case POWER_SUPPLY_DP_DM_ICL_UP:
+		chip->usb_icl_delta += 100;
+		target_icl_vote_ma = get_client_vote(chip->usb_icl_votable,
+						PSY_ICL_VOTER);
+		vote(chip->usb_icl_votable, SW_AICL_ICL_VOTER, true,
+				target_icl_vote_ma + chip->usb_icl_delta);
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+static void update_typec_capability_status(struct smbchg_chip *chip,
+					const union power_supply_propval *val)
+{
+	pr_smb(PR_TYPEC, "typec capability = %dma\n", val->intval);
+
+	pr_debug("changing ICL from %dma to %dma\n", chip->typec_current_ma,
+			val->intval);
+	chip->typec_current_ma = val->intval;
+	smbchg_change_usb_supply_type(chip, chip->usb_supply_type);
+}
+
+static void update_typec_otg_status(struct smbchg_chip *chip, int mode,
+					bool force)
+{
+	union power_supply_propval pval = {0, };
+
+	pr_smb(PR_TYPEC, "typec mode = %d\n", mode);
+
+	if (mode == POWER_SUPPLY_TYPE_DFP) {
+		chip->typec_dfp = true;
+		pval.intval = 1;
+		extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
+				chip->typec_dfp);
+		/* update FG */
+		set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+				get_prop_batt_status(chip));
+	} else if (force || chip->typec_dfp) {
+		chip->typec_dfp = false;
+		pval.intval = 0;
+		extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST,
+				chip->typec_dfp);
+		/* update FG */
+		set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+				get_prop_batt_status(chip));
+	}
+}
+
+static int smbchg_usb_get_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = chip->usb_current_max;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = chip->usb_present;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = chip->usb_online;
+		break;
+	case POWER_SUPPLY_PROP_TYPE:
+		val->intval = chip->usb_supply_type;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = chip->usb_health;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int smbchg_usb_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		chip->usb_current_max = val->intval;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		chip->usb_online = val->intval;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	power_supply_changed(psy);
+	return 0;
+}
+
+static int
+smbchg_usb_is_writeable(struct power_supply *psy,
+			enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+
+static char *smbchg_usb_supplicants[] = {
+	"battery",
+	"bms",
+};
+
+static enum power_supply_property smbchg_usb_properties[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+};
+
+#define CHARGE_OUTPUT_VTG_RATIO		840
+static int smbchg_get_iusb(struct smbchg_chip *chip)
+{
+	int rc, iusb_ua = -EINVAL;
+	struct qpnp_vadc_result adc_result;
+
+	if (!is_usb_present(chip) && !is_dc_present(chip))
+		return 0;
+
+	if (chip->vchg_vadc_dev && chip->vchg_adc_channel != -EINVAL) {
+		rc = qpnp_vadc_read(chip->vchg_vadc_dev,
+				chip->vchg_adc_channel, &adc_result);
+		if (rc) {
+			pr_smb(PR_STATUS,
+				"error in VCHG (channel-%d) read rc = %d\n",
+						chip->vchg_adc_channel, rc);
+			return 0;
+		}
+		iusb_ua = div_s64(adc_result.physical * 1000,
+						CHARGE_OUTPUT_VTG_RATIO);
+	}
+
+	return iusb_ua;
+}
+
+static enum power_supply_property smbchg_battery_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
+	POWER_SUPPLY_PROP_CHARGING_ENABLED,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
+	POWER_SUPPLY_PROP_FLASH_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_NOW,
+	POWER_SUPPLY_PROP_FLASH_ACTIVE,
+	POWER_SUPPLY_PROP_FLASH_TRIGGER,
+	POWER_SUPPLY_PROP_DP_DM,
+	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
+	POWER_SUPPLY_PROP_RERUN_AICL,
+	POWER_SUPPLY_PROP_RESTRICTED_CHARGING,
+};
+
+static int smbchg_battery_set_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       const union power_supply_propval *val)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		vote(chip->battchg_suspend_votable, BATTCHG_USER_EN_VOTER,
+				!val->intval, 0);
+		break;
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		rc = vote(chip->usb_suspend_votable, USER_EN_VOTER,
+				!val->intval, 0);
+		rc = vote(chip->dc_suspend_votable, USER_EN_VOTER,
+				!val->intval, 0);
+		chip->chg_enabled = val->intval;
+		schedule_work(&chip->usb_set_online_work);
+		break;
+	case POWER_SUPPLY_PROP_CAPACITY:
+		chip->fake_battery_soc = val->intval;
+		if (chip->batt_psy)
+			power_supply_changed(chip->batt_psy);
+		break;
+	case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+		smbchg_system_temp_level_set(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		rc = smbchg_set_fastchg_current_user(chip, val->intval / 1000);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		rc = smbchg_float_voltage_set(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+		rc = smbchg_safety_timer_enable(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+		rc = smbchg_switch_buck_frequency(chip, val->intval);
+		if (rc) {
+			pr_err("Couldn't switch buck frequency, rc=%d\n", rc);
+			/*
+			 * Trigger a panic if there is an error while switching
+			 * buck frequency. This will prevent LS FET damage.
+			 */
+			WARN_ON(1);
+		}
+
+		rc = smbchg_otg_pulse_skip_disable(chip,
+				REASON_FLASH_ENABLED, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_FLASH_TRIGGER:
+		chip->flash_triggered = !!val->intval;
+		smbchg_icl_loop_disable_check(chip);
+		break;
+	case POWER_SUPPLY_PROP_FORCE_TLIM:
+		rc = smbchg_force_tlim_en(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_DP_DM:
+		rc = smbchg_dp_dm(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_RERUN_AICL:
+		smbchg_rerun_aicl(chip);
+		break;
+	case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+		rc = smbchg_restricted_charging(chip, val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
+		if (chip->typec_psy)
+			update_typec_capability_status(chip, val);
+		break;
+	case POWER_SUPPLY_PROP_TYPEC_MODE:
+		if (chip->typec_psy)
+			update_typec_otg_status(chip, val->intval, false);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int smbchg_battery_is_writeable(struct power_supply *psy,
+				       enum power_supply_property prop)
+{
+	int rc;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+	case POWER_SUPPLY_PROP_CAPACITY:
+	case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+	case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+	case POWER_SUPPLY_PROP_DP_DM:
+	case POWER_SUPPLY_PROP_RERUN_AICL:
+	case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+		rc = 1;
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	return rc;
+}
+
+static int smbchg_battery_get_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       union power_supply_propval *val)
+{
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = get_prop_batt_status(chip);
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = get_prop_batt_present(chip);
+		break;
+	case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED:
+		val->intval
+			= get_effective_result(chip->battchg_suspend_votable);
+		if (val->intval < 0) /* no votes */
+			val->intval = 1;
+		else
+			val->intval = !val->intval;
+		break;
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		val->intval = chip->chg_enabled;
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		val->intval = get_prop_charge_type(chip);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+		val->intval = smbchg_float_voltage_get(chip);
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = get_prop_batt_health(chip);
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_FLASH_CURRENT_MAX:
+		val->intval = smbchg_calc_max_flash_current(chip);
+		break;
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		val->intval = chip->fastchg_current_ma * 1000;
+		break;
+	case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL:
+		val->intval = chip->therm_lvl_sel;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
+		val->intval = smbchg_get_aicl_level_ma(chip) * 1000;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED:
+		val->intval = (int)chip->aicl_complete;
+		break;
+	case POWER_SUPPLY_PROP_RESTRICTED_CHARGING:
+		val->intval = (int)chip->restricted_charging;
+		break;
+	/* properties from fg */
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = get_prop_batt_capacity(chip);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = get_prop_batt_current_now(chip);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		val->intval = get_prop_batt_voltage_now(chip);
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		val->intval = get_prop_batt_temp(chip);
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		val->intval = get_prop_batt_voltage_max_design(chip);
+		break;
+	case POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE:
+		val->intval = chip->safety_timer_en;
+		break;
+	case POWER_SUPPLY_PROP_FLASH_ACTIVE:
+		val->intval = chip->otg_pulse_skip_dis;
+		break;
+	case POWER_SUPPLY_PROP_FLASH_TRIGGER:
+		val->intval = chip->flash_triggered;
+		break;
+	case POWER_SUPPLY_PROP_DP_DM:
+		val->intval = chip->pulse_cnt;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED:
+		val->intval = smbchg_is_input_current_limited(chip);
+		break;
+	case POWER_SUPPLY_PROP_RERUN_AICL:
+		val->intval = 0;
+		break;
+	case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW:
+		val->intval = smbchg_get_iusb(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static char *smbchg_dc_supplicants[] = {
+	"bms",
+};
+
+static enum power_supply_property smbchg_dc_properties[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CHARGING_ENABLED,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+};
+
+static int smbchg_dc_set_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       const union power_supply_propval *val)
+{
+	int rc = 0;
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		rc = vote(chip->dc_suspend_votable, POWER_SUPPLY_EN_VOTER,
+					!val->intval, 0);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		rc = vote(chip->dc_icl_votable, USER_ICL_VOTER, true,
+				val->intval / 1000);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int smbchg_dc_get_property(struct power_supply *psy,
+				       enum power_supply_property prop,
+				       union power_supply_propval *val)
+{
+	struct smbchg_chip *chip = power_supply_get_drvdata(psy);
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = is_dc_present(chip);
+		break;
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+		val->intval = get_effective_result(chip->dc_suspend_votable);
+		if (val->intval < 0) /* no votes */
+			val->intval = 1;
+		else
+			val->intval = !val->intval;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		/* return if dc is charging the battery */
+		val->intval = (smbchg_get_pwr_path(chip) == PWR_PATH_DC)
+				&& (get_prop_batt_status(chip)
+					== POWER_SUPPLY_STATUS_CHARGING);
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = chip->dc_max_current_ma * 1000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int smbchg_dc_is_writeable(struct power_supply *psy,
+				       enum power_supply_property prop)
+{
+	int rc;
+
+	switch (prop) {
+	case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		rc = 1;
+		break;
+	default:
+		rc = 0;
+		break;
+	}
+	return rc;
+}
+
+#define HOT_BAT_HARD_BIT	BIT(0)
+#define HOT_BAT_SOFT_BIT	BIT(1)
+#define COLD_BAT_HARD_BIT	BIT(2)
+#define COLD_BAT_SOFT_BIT	BIT(3)
+#define BAT_OV_BIT		BIT(4)
+#define BAT_LOW_BIT		BIT(5)
+#define BAT_MISSING_BIT		BIT(6)
+#define BAT_TERM_MISSING_BIT	BIT(7)
+static irqreturn_t batt_hot_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	chip->batt_hot = !!(reg & HOT_BAT_HARD_BIT);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	smbchg_wipower_check(chip);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+			get_prop_batt_health(chip));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_cold_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	chip->batt_cold = !!(reg & COLD_BAT_HARD_BIT);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	smbchg_wipower_check(chip);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+			get_prop_batt_health(chip));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_warm_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	chip->batt_warm = !!(reg & HOT_BAT_SOFT_BIT);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+			get_prop_batt_health(chip));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_cool_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	chip->batt_cool = !!(reg & COLD_BAT_SOFT_BIT);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+			get_prop_batt_health(chip));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t batt_pres_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	chip->batt_present = !(reg & BAT_MISSING_BIT);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_HEALTH,
+			get_prop_batt_health(chip));
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t vbat_low_handler(int irq, void *_chip)
+{
+	pr_warn_ratelimited("vbat low\n");
+	return IRQ_HANDLED;
+}
+
+#define CHG_COMP_SFT_BIT	BIT(3)
+static irqreturn_t chg_error_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	int rc = 0;
+	u8 reg;
+
+	pr_smb(PR_INTERRUPT, "chg-error triggered\n");
+
+	rc = smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to read RT_STS rc = %d\n", rc);
+	} else {
+		pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+		if (reg & CHG_COMP_SFT_BIT)
+			set_property_on_fg(chip,
+					POWER_SUPPLY_PROP_SAFETY_TIMER_EXPIRED,
+					1);
+	}
+
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	smbchg_wipower_check(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fastchg_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+
+	pr_smb(PR_INTERRUPT, "p2f triggered\n");
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	smbchg_wipower_check(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_hot_handler(int irq, void *_chip)
+{
+	pr_warn_ratelimited("chg hot\n");
+	smbchg_wipower_check(_chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t chg_term_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+
+	pr_smb(PR_INTERRUPT, "tcc triggered\n");
+	/*
+	 * Charge termination is a pulse and not level triggered. That means,
+	 * TCC bit in RT_STS can get cleared by the time this interrupt is
+	 * handled. Instead of relying on that to determine whether the
+	 * charge termination had happened, we've to simply notify the FG
+	 * about this as long as the interrupt is handled.
+	 */
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_CHARGE_DONE, 1);
+
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t taper_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	taper_irq_en(chip, false);
+	smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_taper(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	smbchg_wipower_check(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t recharge_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->chgr_base + RT_STS, 1);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	smbchg_parallel_usb_check_ok(chip);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wdog_timeout_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
+	pr_warn_ratelimited("wdog timeout rt_stat = 0x%02x\n", reg);
+	if (chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+	smbchg_charging_status_change(chip);
+	return IRQ_HANDLED;
+}
+
+/**
+ * power_ok_handler() - called when the switcher turns on or turns off
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating switcher turning on or off
+ */
+static irqreturn_t power_ok_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	u8 reg = 0;
+
+	smbchg_read(chip, &reg, chip->misc_base + RT_STS, 1);
+	pr_smb(PR_INTERRUPT, "triggered: 0x%02x\n", reg);
+	return IRQ_HANDLED;
+}
+
+/**
+ * dcin_uv_handler() - called when the dc voltage crosses the uv threshold
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating whether dc voltage is uv
+ */
+#define DCIN_UNSUSPEND_DELAY_MS		1000
+static irqreturn_t dcin_uv_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	bool dc_present = is_dc_present(chip);
+
+	pr_smb(PR_STATUS, "chip->dc_present = %d dc_present = %d\n",
+			chip->dc_present, dc_present);
+
+	if (chip->dc_present != dc_present) {
+		/* dc changed */
+		chip->dc_present = dc_present;
+		if (chip->dc_psy_type != -EINVAL && chip->batt_psy)
+			power_supply_changed(chip->dc_psy);
+		smbchg_charging_status_change(chip);
+		smbchg_aicl_deglitch_wa_check(chip);
+		chip->vbat_above_headroom = false;
+	}
+
+	smbchg_wipower_check(chip);
+	return IRQ_HANDLED;
+}
+
+/**
+ * usbin_ov_handler() - this is called when an overvoltage condition occurs
+ * @chip: pointer to smbchg_chip chip
+ */
+static irqreturn_t usbin_ov_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	int rc;
+	u8 reg;
+	bool usb_present;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read usb rt status rc = %d\n", rc);
+		goto out;
+	}
+
+	/* OV condition is detected. Notify it to USB psy */
+	if (reg & USBIN_OV_BIT) {
+		chip->usb_ov_det = true;
+		pr_smb(PR_MISC, "setting usb psy health OV\n");
+		chip->usb_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
+		power_supply_changed(chip->usb_psy);
+	} else {
+		chip->usb_ov_det = false;
+		/* If USB is present, then handle the USB insertion */
+		usb_present = is_usb_present(chip);
+		if (usb_present)
+			update_usb_status(chip, usb_present, false);
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+/**
+ * usbin_uv_handler() - this is called when USB charger is removed
+ * @chip: pointer to smbchg_chip chip
+ * @rt_stat: the status bit indicating chg insertion/removal
+ */
+#define ICL_MODE_MASK		SMB_MASK(5, 4)
+#define ICL_MODE_HIGH_CURRENT	0
+static irqreturn_t usbin_uv_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	int aicl_level = smbchg_get_aicl_level_ma(chip);
+	int rc;
+	u8 reg;
+
+	rc = smbchg_read(chip, &reg, chip->usb_chgpth_base + RT_STS, 1);
+	if (rc) {
+		pr_err("could not read rt sts: %d", rc);
+		goto out;
+	}
+
+	pr_smb(PR_STATUS,
+		"%s chip->usb_present = %d rt_sts = 0x%02x hvdcp_3_det_ignore_uv = %d aicl = %d\n",
+		chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
+		chip->usb_present, reg, chip->hvdcp_3_det_ignore_uv,
+		aicl_level);
+
+	/*
+	 * set usb_psy's dp=f dm=f if this is a new insertion, i.e. it is
+	 * not already src_detected and usbin_uv is seen falling
+	 */
+	if (!(reg & USBIN_UV_BIT) && !(reg & USBIN_SRC_DET_BIT)) {
+		pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
+		if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+			rc = regulator_enable(chip->dpdm_reg);
+		if (rc < 0) {
+			pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (reg & USBIN_UV_BIT)
+		complete_all(&chip->usbin_uv_raised);
+	else
+		complete_all(&chip->usbin_uv_lowered);
+
+	if (chip->hvdcp_3_det_ignore_uv)
+		goto out;
+
+	if ((reg & USBIN_UV_BIT) && (reg & USBIN_SRC_DET_BIT)) {
+		pr_smb(PR_STATUS, "Very weak charger detected\n");
+		chip->very_weak_charger = true;
+		rc = smbchg_read(chip, &reg,
+				chip->usb_chgpth_base + ICL_STS_2_REG, 1);
+		if (rc) {
+			dev_err(chip->dev, "Could not read usb icl sts 2: %d\n",
+					rc);
+			goto out;
+		}
+		if ((reg & ICL_MODE_MASK) != ICL_MODE_HIGH_CURRENT) {
+			/*
+			 * If AICL is not even enabled, this is either an
+			 * SDP or a grossly out of spec charger. Do not
+			 * draw any current from it.
+			 */
+			rc = vote(chip->usb_suspend_votable,
+					WEAK_CHARGER_EN_VOTER, true, 0);
+			if (rc < 0)
+				pr_err("could not disable charger: %d", rc);
+		} else if (aicl_level == chip->tables.usb_ilim_ma_table[0]) {
+			/*
+			 * we are in a situation where the adapter is not able
+			 * to supply even 300mA. Disable hw aicl reruns else it
+			 * is only a matter of time when we get back here again
+			 */
+			rc = vote(chip->hw_aicl_rerun_disable_votable,
+				WEAK_CHARGER_HW_AICL_VOTER, true, 0);
+			if (rc < 0)
+				pr_err("Couldn't disable hw aicl rerun rc=%d\n",
+						rc);
+		}
+		pr_smb(PR_MISC, "setting usb psy health UNSPEC_FAILURE\n");
+		chip->usb_health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+		power_supply_changed(chip->usb_psy);
+		schedule_work(&chip->usb_set_online_work);
+	}
+
+	smbchg_wipower_check(chip);
+out:
+	return IRQ_HANDLED;
+}
+
+/**
+ * src_detect_handler() - this is called on rising edge when USB charger type
+ *			is detected and on falling edge when USB voltage falls
+ *			below the coarse detect voltage(1V), use it for
+ *			handling USB charger insertion and removal.
+ * @chip: pointer to smbchg_chip
+ * @rt_stat: the status bit indicating chg insertion/removal
+ */
+static irqreturn_t src_detect_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	bool usb_present = is_usb_present(chip);
+	bool src_detect = is_src_detect_high(chip);
+	int rc;
+
+	pr_smb(PR_STATUS,
+		"%s chip->usb_present = %d usb_present = %d src_detect = %d hvdcp_3_det_ignore_uv=%d\n",
+		chip->hvdcp_3_det_ignore_uv ? "Ignoring":"",
+		chip->usb_present, usb_present, src_detect,
+		chip->hvdcp_3_det_ignore_uv);
+
+	if (src_detect)
+		complete_all(&chip->src_det_raised);
+	else
+		complete_all(&chip->src_det_lowered);
+
+	if (chip->hvdcp_3_det_ignore_uv)
+		goto out;
+
+	/*
+	 * When VBAT is above the AICL threshold (4.25V) - 180mV (4.07V),
+	 * an input collapse due to AICL will actually cause an USBIN_UV
+	 * interrupt to fire as well.
+	 *
+	 * Handle USB insertions and removals in the source detect handler
+	 * instead of the USBIN_UV handler since the latter is untrustworthy
+	 * when the battery voltage is high.
+	 */
+	chip->very_weak_charger = false;
+	/*
+	 * a src detect marks a new insertion or a real removal,
+	 * vote for enable aicl hw reruns
+	 */
+	rc = vote(chip->hw_aicl_rerun_disable_votable,
+		WEAK_CHARGER_HW_AICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't enable hw aicl rerun rc=%d\n", rc);
+
+	rc = vote(chip->usb_suspend_votable, WEAK_CHARGER_EN_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("could not enable charger: %d\n", rc);
+
+	if (src_detect) {
+		update_usb_status(chip, usb_present, 0);
+	} else {
+		update_usb_status(chip, 0, false);
+		chip->aicl_irq_count = 0;
+	}
+out:
+	return IRQ_HANDLED;
+}
+
+/**
+ * otg_oc_handler() - called when the usb otg goes over current
+ */
+#define NUM_OTG_RETRIES			5
+#define OTG_OC_RETRY_DELAY_US		50000
+static irqreturn_t otg_oc_handler(int irq, void *_chip)
+{
+	int rc;
+	struct smbchg_chip *chip = _chip;
+	s64 elapsed_us = ktime_us_delta(ktime_get(), chip->otg_enable_time);
+
+	pr_smb(PR_INTERRUPT, "triggered\n");
+
+	if (chip->schg_version == QPNP_SCHG_LITE) {
+		pr_warn("OTG OC triggered - OTG disabled\n");
+		return IRQ_HANDLED;
+	}
+
+	if (elapsed_us > OTG_OC_RETRY_DELAY_US)
+		chip->otg_retries = 0;
+
+	/*
+	 * Due to a HW bug in the PMI8994 charger, the current inrush that
+	 * occurs when connecting certain OTG devices can cause the OTG
+	 * overcurrent protection to trip.
+	 *
+	 * The work around is to try reenabling the OTG when getting an
+	 * overcurrent interrupt once.
+	 */
+	if (chip->otg_retries < NUM_OTG_RETRIES) {
+		chip->otg_retries += 1;
+		pr_smb(PR_STATUS,
+			"Retrying OTG enable. Try #%d, elapsed_us %lld\n",
+						chip->otg_retries, elapsed_us);
+		rc = otg_oc_reset(chip);
+		if (rc)
+			pr_err("Failed to reset OTG OC state rc=%d\n", rc);
+		chip->otg_enable_time = ktime_get();
+	}
+	return IRQ_HANDLED;
+}
+
+/**
+ * otg_fail_handler() - called when the usb otg fails
+ * (when vbat < OTG UVLO threshold)
+ */
+static irqreturn_t otg_fail_handler(int irq, void *_chip)
+{
+	pr_smb(PR_INTERRUPT, "triggered\n");
+	return IRQ_HANDLED;
+}
+
+/**
+ * aicl_done_handler() - called when the usb AICL algorithm is finished
+ *			and a current is set.
+ */
+static irqreturn_t aicl_done_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	bool usb_present = is_usb_present(chip);
+	int aicl_level = smbchg_get_aicl_level_ma(chip);
+
+	pr_smb(PR_INTERRUPT, "triggered, aicl: %d\n", aicl_level);
+
+	increment_aicl_count(chip);
+
+	if (usb_present)
+		smbchg_parallel_usb_check_ok(chip);
+
+	if (chip->aicl_complete && chip->batt_psy)
+		power_supply_changed(chip->batt_psy);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * usbid_change_handler() - called when the usb RID changes.
+ * This is used mostly for detecting OTG
+ */
+static irqreturn_t usbid_change_handler(int irq, void *_chip)
+{
+	struct smbchg_chip *chip = _chip;
+	bool otg_present;
+
+	pr_smb(PR_INTERRUPT, "triggered\n");
+
+	otg_present = is_otg_present(chip);
+	pr_smb(PR_MISC, "setting usb psy OTG = %d\n",
+			otg_present ? 1 : 0);
+
+	extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, otg_present);
+
+	if (otg_present)
+		pr_smb(PR_STATUS, "OTG detected\n");
+
+	/* update FG */
+	set_property_on_fg(chip, POWER_SUPPLY_PROP_STATUS,
+			get_prop_batt_status(chip));
+
+	return IRQ_HANDLED;
+}
+
+static int determine_initial_status(struct smbchg_chip *chip)
+{
+	union power_supply_propval type = {0, };
+
+	/*
+	 * It is okay to read the interrupt status here since
+	 * interrupts aren't requested. reading interrupt status
+	 * clears the interrupt so be careful to read interrupt
+	 * status only in interrupt handling code
+	 */
+
+	batt_pres_handler(0, chip);
+	batt_hot_handler(0, chip);
+	batt_warm_handler(0, chip);
+	batt_cool_handler(0, chip);
+	batt_cold_handler(0, chip);
+	if (chip->typec_psy) {
+		get_property_from_typec(chip, POWER_SUPPLY_PROP_TYPE, &type);
+		update_typec_otg_status(chip, type.intval, true);
+	} else {
+		usbid_change_handler(0, chip);
+	}
+	src_detect_handler(0, chip);
+
+	chip->usb_present = is_usb_present(chip);
+	chip->dc_present = is_dc_present(chip);
+
+	if (chip->usb_present) {
+		int rc = 0;
+
+		pr_smb(PR_MISC, "setting usb dp=f dm=f\n");
+		if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg))
+			rc = regulator_enable(chip->dpdm_reg);
+		if (rc < 0) {
+			pr_err("Couldn't enable DP/DM for pulsing rc=%d\n", rc);
+			return rc;
+		}
+		handle_usb_insertion(chip);
+	} else {
+		handle_usb_removal(chip);
+	}
+
+	return 0;
+}
+
+static int prechg_time[] = {
+	24,
+	48,
+	96,
+	192,
+};
+static int chg_time[] = {
+	192,
+	384,
+	768,
+	1536,
+};
+
+enum bpd_type {
+	BPD_TYPE_BAT_NONE,
+	BPD_TYPE_BAT_ID,
+	BPD_TYPE_BAT_THM,
+	BPD_TYPE_BAT_THM_BAT_ID,
+	BPD_TYPE_DEFAULT,
+};
+
+static const char * const bpd_label[] = {
+	[BPD_TYPE_BAT_NONE]		= "bpd_none",
+	[BPD_TYPE_BAT_ID]		= "bpd_id",
+	[BPD_TYPE_BAT_THM]		= "bpd_thm",
+	[BPD_TYPE_BAT_THM_BAT_ID]	= "bpd_thm_id",
+};
+
+static inline int get_bpd(const char *name)
+{
+	int i = 0;
+
+	for (i = 0; i < ARRAY_SIZE(bpd_label); i++) {
+		if (strcmp(bpd_label[i], name) == 0)
+			return i;
+	}
+	return -EINVAL;
+}
+
+#define REVISION1_REG			0x0
+#define DIG_MINOR			0
+#define DIG_MAJOR			1
+#define ANA_MINOR			2
+#define ANA_MAJOR			3
+#define CHGR_CFG1			0xFB
+#define RECHG_THRESHOLD_SRC_BIT		BIT(1)
+#define TERM_I_SRC_BIT			BIT(2)
+#define TERM_SRC_FG			BIT(2)
+#define CHG_INHIB_CFG_REG		0xF7
+#define CHG_INHIBIT_50MV_VAL		0x00
+#define CHG_INHIBIT_100MV_VAL		0x01
+#define CHG_INHIBIT_200MV_VAL		0x02
+#define CHG_INHIBIT_300MV_VAL		0x03
+#define CHG_INHIBIT_MASK		0x03
+#define USE_REGISTER_FOR_CURRENT	BIT(2)
+#define CHGR_CFG2			0xFC
+#define CHG_EN_SRC_BIT			BIT(7)
+#define CHG_EN_POLARITY_BIT		BIT(6)
+#define P2F_CHG_TRAN			BIT(5)
+#define CHG_BAT_OV_ECC			BIT(4)
+#define I_TERM_BIT			BIT(3)
+#define AUTO_RECHG_BIT			BIT(2)
+#define CHARGER_INHIBIT_BIT		BIT(0)
+#define USB51_COMMAND_POL		BIT(2)
+#define USB51AC_CTRL			BIT(1)
+#define TR_8OR32B			0xFE
+#define BUCK_8_16_FREQ_BIT		BIT(0)
+#define BM_CFG				0xF3
+#define BATT_MISSING_ALGO_BIT		BIT(2)
+#define BMD_PIN_SRC_MASK		SMB_MASK(1, 0)
+#define PIN_SRC_SHIFT			0
+#define CHGR_CFG			0xFF
+#define RCHG_LVL_BIT			BIT(0)
+#define VCHG_EN_BIT			BIT(1)
+#define VCHG_INPUT_CURRENT_BIT		BIT(3)
+#define CFG_AFVC			0xF6
+#define VFLOAT_COMP_ENABLE_MASK		SMB_MASK(2, 0)
+#define TR_RID_REG			0xFA
+#define FG_INPUT_FET_DELAY_BIT		BIT(3)
+#define TRIM_OPTIONS_7_0		0xF6
+#define INPUT_MISSING_POLLER_EN_BIT	BIT(3)
+#define CHGR_CCMP_CFG			0xFA
+#define JEITA_TEMP_HARD_LIMIT_BIT	BIT(5)
+#define HVDCP_ADAPTER_SEL_MASK		SMB_MASK(5, 4)
+#define HVDCP_ADAPTER_SEL_9V_BIT	BIT(4)
+#define HVDCP_AUTH_ALG_EN_BIT		BIT(6)
+#define CMD_APSD			0x41
+#define APSD_RERUN_BIT			BIT(0)
+#define OTG_CFG				0xF1
+#define HICCUP_ENABLED_BIT		BIT(6)
+#define OTG_PIN_POLARITY_BIT		BIT(4)
+#define OTG_PIN_ACTIVE_LOW		BIT(4)
+#define OTG_EN_CTRL_MASK		SMB_MASK(3, 2)
+#define OTG_PIN_CTRL_RID_DIS		0x04
+#define OTG_CMD_CTRL_RID_EN		0x08
+#define AICL_ADC_BIT			BIT(6)
+static void batt_ov_wa_check(struct smbchg_chip *chip)
+{
+	int rc;
+	u8 reg;
+
+	/* disable-'battery OV disables charging' feature */
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
+			CHG_BAT_OV_ECC, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+		return;
+	}
+
+	/*
+	 * if battery OV is set:
+	 * restart charging by disable/enable charging
+	 */
+	rc = smbchg_read(chip, &reg, chip->bat_if_base + RT_STS, 1);
+	if (rc < 0) {
+		dev_err(chip->dev,
+			"Couldn't read Battery RT status rc = %d\n", rc);
+		return;
+	}
+
+	if (reg & BAT_OV_BIT) {
+		rc = smbchg_charging_en(chip, false);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't disable charging: rc = %d\n", rc);
+			return;
+		}
+
+		/* delay for charging-disable to take affect */
+		msleep(200);
+
+		rc = smbchg_charging_en(chip, true);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't enable charging: rc = %d\n", rc);
+			return;
+		}
+	}
+}
+
+static int smbchg_hw_init(struct smbchg_chip *chip)
+{
+	int rc, i;
+	u8 reg, mask;
+
+	rc = smbchg_read(chip, chip->revision,
+			chip->misc_base + REVISION1_REG, 4);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't read revision rc=%d\n",
+				rc);
+		return rc;
+	}
+	pr_smb(PR_STATUS, "Charger Revision DIG: %d.%d; ANA: %d.%d\n",
+			chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+			chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
+
+	/* Setup 9V HVDCP */
+	if (!chip->hvdcp_not_supported) {
+		rc = smbchg_sec_masked_write(chip,
+				chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_9V);
+		if (rc < 0) {
+			pr_err("Couldn't set hvdcp config in chgpath_chg rc=%d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	if (chip->aicl_rerun_period_s > 0) {
+		rc = smbchg_set_aicl_rerun_period_s(chip,
+				chip->aicl_rerun_period_s);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set AICL rerun timer rc=%d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + TR_RID_REG,
+			FG_INPUT_FET_DELAY_BIT, FG_INPUT_FET_DELAY_BIT);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't disable fg input fet delay rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = smbchg_sec_masked_write(chip, chip->misc_base + TRIM_OPTIONS_7_0,
+			INPUT_MISSING_POLLER_EN_BIT,
+			INPUT_MISSING_POLLER_EN_BIT);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't enable input missing poller rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	/*
+	 * Do not force using current from the register i.e. use auto
+	 * power source detect (APSD) mA ratings for the initial current values.
+	 *
+	 * If this is set, AICL will not rerun at 9V for HVDCPs
+	 */
+	rc = smbchg_masked_write(chip, chip->usb_chgpth_base + CMD_IL,
+			USE_REGISTER_FOR_CURRENT, 0);
+
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set input limit cmd rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * set chg en by cmd register, set chg en by writing bit 1,
+	 * enable auto pre to fast, enable auto recharge by default.
+	 * enable current termination and charge inhibition based on
+	 * the device tree configuration.
+	 */
+	rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG2,
+			CHG_EN_SRC_BIT | CHG_EN_POLARITY_BIT | P2F_CHG_TRAN
+			| I_TERM_BIT | AUTO_RECHG_BIT | CHARGER_INHIBIT_BIT,
+			CHG_EN_POLARITY_BIT
+			| (chip->chg_inhibit_en ? CHARGER_INHIBIT_BIT : 0)
+			| (chip->iterm_disabled ? I_TERM_BIT : 0));
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * enable battery charging to make sure it hasn't been changed earlier
+	 * by the bootloader.
+	 */
+	rc = smbchg_charging_en(chip, true);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't enable battery charging=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Based on the configuration, use the analog sensors or the fuelgauge
+	 * adc for recharge threshold source.
+	 */
+
+	if (chip->chg_inhibit_source_fg)
+		rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
+			TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT,
+			TERM_SRC_FG | RECHG_THRESHOLD_SRC_BIT);
+	else
+		rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG1,
+			TERM_I_SRC_BIT | RECHG_THRESHOLD_SRC_BIT, 0);
+
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set chgr_cfg2 rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * control USB suspend via command bits and set correct 100/500mA
+	 * polarity on the usb current
+	 */
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+		USB51_COMMAND_POL | USB51AC_CTRL, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set usb_chgpth cfg rc=%d\n", rc);
+		return rc;
+	}
+
+	check_battery_type(chip);
+
+	/* set the float voltage */
+	if (chip->vfloat_mv != -EINVAL) {
+		rc = smbchg_float_voltage_set(chip, chip->vfloat_mv);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set float voltage rc = %d\n", rc);
+			return rc;
+		}
+		pr_smb(PR_STATUS, "set vfloat to %d\n", chip->vfloat_mv);
+	}
+
+	/* set the fast charge current compensation */
+	if (chip->fastchg_current_comp != -EINVAL) {
+		rc = smbchg_fastchg_current_comp_set(chip,
+			chip->fastchg_current_comp);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set fastchg current comp rc = %d\n",
+				rc);
+			return rc;
+		}
+		pr_smb(PR_STATUS, "set fastchg current comp to %d\n",
+			chip->fastchg_current_comp);
+	}
+
+	/* set the float voltage compensation */
+	if (chip->float_voltage_comp != -EINVAL) {
+		rc = smbchg_float_voltage_comp_set(chip,
+			chip->float_voltage_comp);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set float voltage comp rc = %d\n",
+				rc);
+			return rc;
+		}
+		pr_smb(PR_STATUS, "set float voltage comp to %d\n",
+			chip->float_voltage_comp);
+	}
+
+	/* set iterm */
+	if (chip->iterm_ma != -EINVAL) {
+		if (chip->iterm_disabled) {
+			dev_err(chip->dev, "Error: Both iterm_disabled and iterm_ma set\n");
+			return -EINVAL;
+		}
+
+		smbchg_iterm_set(chip, chip->iterm_ma);
+	}
+
+	/* set the safety time voltage */
+	if (chip->safety_time != -EINVAL) {
+		reg = (chip->safety_time > 0 ? 0 : SFT_TIMER_DISABLE_BIT) |
+			(chip->prechg_safety_time > 0
+			? 0 : PRECHG_SFT_TIMER_DISABLE_BIT);
+
+		for (i = 0; i < ARRAY_SIZE(chg_time); i++) {
+			if (chip->safety_time <= chg_time[i]) {
+				reg |= i << SAFETY_TIME_MINUTES_SHIFT;
+				break;
+			}
+		}
+		for (i = 0; i < ARRAY_SIZE(prechg_time); i++) {
+			if (chip->prechg_safety_time <= prechg_time[i]) {
+				reg |= i;
+				break;
+			}
+		}
+
+		rc = smbchg_sec_masked_write(chip,
+				chip->chgr_base + SFT_CFG,
+				SFT_EN_MASK | SFT_TO_MASK |
+				(chip->prechg_safety_time > 0
+				? PRECHG_SFT_TO_MASK : 0), reg);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set safety timer rc = %d\n",
+				rc);
+			return rc;
+		}
+		chip->safety_timer_en = true;
+	} else {
+		rc = smbchg_read(chip, &reg, chip->chgr_base + SFT_CFG, 1);
+		if (rc < 0)
+			dev_err(chip->dev, "Unable to read SFT_CFG rc = %d\n",
+				rc);
+		else if (!(reg & SFT_EN_MASK))
+			chip->safety_timer_en = true;
+	}
+
+	/* configure jeita temperature hard limit */
+	if (chip->jeita_temp_hard_limit >= 0) {
+		rc = smbchg_sec_masked_write(chip,
+			chip->chgr_base + CHGR_CCMP_CFG,
+			JEITA_TEMP_HARD_LIMIT_BIT,
+			chip->jeita_temp_hard_limit
+			? 0 : JEITA_TEMP_HARD_LIMIT_BIT);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't set jeita temp hard limit rc = %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	/* make the buck switch faster to prevent some vbus oscillation */
+	rc = smbchg_sec_masked_write(chip,
+			chip->usb_chgpth_base + TR_8OR32B,
+			BUCK_8_16_FREQ_BIT, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set buck frequency rc = %d\n", rc);
+		return rc;
+	}
+
+	/* battery missing detection */
+	mask =  BATT_MISSING_ALGO_BIT;
+	reg = chip->bmd_algo_disabled ? 0 : BATT_MISSING_ALGO_BIT;
+	if (chip->bmd_pin_src < BPD_TYPE_DEFAULT) {
+		mask |= BMD_PIN_SRC_MASK;
+		reg |= chip->bmd_pin_src << PIN_SRC_SHIFT;
+	}
+	rc = smbchg_sec_masked_write(chip,
+			chip->bat_if_base + BM_CFG, mask, reg);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't set batt_missing config = %d\n",
+									rc);
+		return rc;
+	}
+
+	if (chip->vchg_adc_channel != -EINVAL) {
+		/* configure and enable VCHG */
+		rc = smbchg_sec_masked_write(chip, chip->chgr_base + CHGR_CFG,
+				VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT,
+				VCHG_INPUT_CURRENT_BIT | VCHG_EN_BIT);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	smbchg_charging_status_change(chip);
+
+	vote(chip->usb_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
+	vote(chip->dc_suspend_votable, USER_EN_VOTER, !chip->chg_enabled, 0);
+	/* resume threshold */
+	if (chip->resume_delta_mv != -EINVAL) {
+
+		/*
+		 * Configure only if the recharge threshold source is not
+		 * fuel gauge ADC.
+		 */
+		if (!chip->chg_inhibit_source_fg) {
+			if (chip->resume_delta_mv < 100)
+				reg = CHG_INHIBIT_50MV_VAL;
+			else if (chip->resume_delta_mv < 200)
+				reg = CHG_INHIBIT_100MV_VAL;
+			else if (chip->resume_delta_mv < 300)
+				reg = CHG_INHIBIT_200MV_VAL;
+			else
+				reg = CHG_INHIBIT_300MV_VAL;
+
+			rc = smbchg_sec_masked_write(chip,
+					chip->chgr_base + CHG_INHIB_CFG_REG,
+					CHG_INHIBIT_MASK, reg);
+			if (rc < 0) {
+				dev_err(chip->dev, "Couldn't set inhibit val rc = %d\n",
+						rc);
+				return rc;
+			}
+		}
+
+		rc = smbchg_sec_masked_write(chip,
+				chip->chgr_base + CHGR_CFG,
+				RCHG_LVL_BIT,
+				(chip->resume_delta_mv
+				 < chip->tables.rchg_thr_mv)
+				? 0 : RCHG_LVL_BIT);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set recharge rc = %d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	/* DC path current settings */
+	if (chip->dc_psy_type != -EINVAL) {
+		rc = vote(chip->dc_icl_votable, PSY_ICL_VOTER, true,
+					chip->dc_target_current_ma);
+		if (rc < 0) {
+			dev_err(chip->dev,
+				"Couldn't vote for initial DC ICL rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+
+	/*
+	 * on some devices the battery is powered via external sources which
+	 * could raise its voltage above the float voltage. smbchargers go
+	 * in to reverse boost in such a situation and the workaround is to
+	 * disable float voltage compensation (note that the battery will appear
+	 * hot/cold when powered via external source).
+	 */
+	if (chip->soft_vfloat_comp_disabled) {
+		rc = smbchg_sec_masked_write(chip, chip->chgr_base + CFG_AFVC,
+				VFLOAT_COMP_ENABLE_MASK, 0);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't disable soft vfloat rc = %d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	rc = vote(chip->fcc_votable, BATT_TYPE_FCC_VOTER, true,
+			chip->cfg_fastchg_current_ma);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't vote fastchg ma rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = smbchg_read(chip, &chip->original_usbin_allowance,
+			chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
+	if (rc < 0)
+		dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
+
+	if (chip->wipower_dyn_icl_avail) {
+		rc = smbchg_wipower_ilim_config(chip,
+				&(chip->wipower_default.entries[0]));
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set default wipower ilim = %d\n",
+				rc);
+			return rc;
+		}
+	}
+	/* unsuspend dc path, it could be suspended by the bootloader */
+	rc = smbchg_dc_suspend(chip, 0);
+	if (rc < 0) {
+		dev_err(chip->dev, "Couldn't unsuspend dc path= %d\n", rc);
+		return rc;
+	}
+
+	if (chip->force_aicl_rerun) {
+		/* vote to enable hw aicl */
+		rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+			DEFAULT_CONFIG_HW_AICL_VOTER, true, 0);
+		if (rc < 0) {
+			pr_err("Couldn't vote enable hw aicl rerun rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (chip->schg_version == QPNP_SCHG_LITE) {
+		/* enable OTG hiccup mode */
+		rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
+					HICCUP_ENABLED_BIT, HICCUP_ENABLED_BIT);
+		if (rc < 0)
+			dev_err(chip->dev, "Couldn't set OTG OC config rc = %d\n",
+				rc);
+	}
+
+	if (chip->otg_pinctrl) {
+		/* configure OTG enable to pin control active low */
+		rc = smbchg_sec_masked_write(chip, chip->otg_base + OTG_CFG,
+				OTG_PIN_POLARITY_BIT | OTG_EN_CTRL_MASK,
+				OTG_PIN_ACTIVE_LOW | OTG_PIN_CTRL_RID_DIS);
+		if (rc < 0) {
+			dev_err(chip->dev, "Couldn't set OTG EN config rc = %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (chip->wa_flags & SMBCHG_BATT_OV_WA)
+		batt_ov_wa_check(chip);
+
+	/* turn off AICL adc for improved accuracy */
+	rc = smbchg_sec_masked_write(chip,
+		chip->misc_base + MISC_TRIM_OPT_15_8, AICL_ADC_BIT, 0);
+	if (rc)
+		pr_err("Couldn't write to MISC_TRIM_OPTIONS_15_8 rc=%d\n",
+			rc);
+
+	return rc;
+}
+
+static const struct of_device_id smbchg_match_table[] = {
+	{
+		.compatible     = "qcom,qpnp-smbcharger",
+	},
+	{ },
+};
+
+#define DC_MA_MIN 300
+#define DC_MA_MAX 2000
+#define OF_PROP_READ(chip, prop, dt_property, retval, optional)		\
+do {									\
+	if (retval)							\
+		break;							\
+	if (optional)							\
+		prop = -EINVAL;						\
+									\
+	retval = of_property_read_u32(chip->pdev->dev.of_node,		\
+					"qcom," dt_property,	\
+					&prop);				\
+									\
+	if ((retval == -EINVAL) && optional)				\
+		retval = 0;						\
+	else if (retval)						\
+		dev_err(chip->dev, "Error reading " #dt_property	\
+				" property rc = %d\n", rc);		\
+} while (0)
+
+#define ILIM_ENTRIES		3
+#define VOLTAGE_RANGE_ENTRIES	2
+#define RANGE_ENTRY		(ILIM_ENTRIES + VOLTAGE_RANGE_ENTRIES)
+static int smb_parse_wipower_map_dt(struct smbchg_chip *chip,
+		struct ilim_map *map, char *property)
+{
+	struct device_node *node = chip->dev->of_node;
+	int total_elements, size;
+	struct property *prop;
+	const __be32 *data;
+	int num, i;
+
+	prop = of_find_property(node, property, &size);
+	if (!prop) {
+		dev_err(chip->dev, "%s missing\n", property);
+		return -EINVAL;
+	}
+
+	total_elements = size / sizeof(int);
+	if (total_elements % RANGE_ENTRY) {
+		dev_err(chip->dev, "%s table not in multiple of %d, total elements = %d\n",
+				property, RANGE_ENTRY, total_elements);
+		return -EINVAL;
+	}
+
+	data = prop->value;
+	num = total_elements / RANGE_ENTRY;
+	map->entries = devm_kzalloc(chip->dev,
+			num * sizeof(struct ilim_entry), GFP_KERNEL);
+	if (!map->entries)
+		return -ENOMEM;
+
+	for (i = 0; i < num; i++) {
+		map->entries[i].vmin_uv =  be32_to_cpup(data++);
+		map->entries[i].vmax_uv =  be32_to_cpup(data++);
+		map->entries[i].icl_pt_ma =  be32_to_cpup(data++);
+		map->entries[i].icl_lv_ma =  be32_to_cpup(data++);
+		map->entries[i].icl_hv_ma =  be32_to_cpup(data++);
+	}
+	map->num = num;
+	return 0;
+}
+
+static int smb_parse_wipower_dt(struct smbchg_chip *chip)
+{
+	int rc = 0;
+
+	chip->wipower_dyn_icl_avail = false;
+
+	if (!chip->vadc_dev)
+		goto err;
+
+	rc = smb_parse_wipower_map_dt(chip, &chip->wipower_default,
+					"qcom,wipower-default-ilim-map");
+	if (rc) {
+		dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
+				rc);
+		goto err;
+	}
+
+	rc = smb_parse_wipower_map_dt(chip, &chip->wipower_pt,
+					"qcom,wipower-pt-ilim-map");
+	if (rc) {
+		dev_err(chip->dev, "failed to parse wipower-pt-ilim-map rc = %d\n",
+				rc);
+		goto err;
+	}
+
+	rc = smb_parse_wipower_map_dt(chip, &chip->wipower_div2,
+					"qcom,wipower-div2-ilim-map");
+	if (rc) {
+		dev_err(chip->dev, "failed to parse wipower-div2-ilim-map rc = %d\n",
+				rc);
+		goto err;
+	}
+	chip->wipower_dyn_icl_avail = true;
+	return 0;
+err:
+	chip->wipower_default.num = 0;
+	chip->wipower_pt.num = 0;
+	chip->wipower_default.num = 0;
+	if (chip->wipower_default.entries)
+		devm_kfree(chip->dev, chip->wipower_default.entries);
+	if (chip->wipower_pt.entries)
+		devm_kfree(chip->dev, chip->wipower_pt.entries);
+	if (chip->wipower_div2.entries)
+		devm_kfree(chip->dev, chip->wipower_div2.entries);
+	chip->wipower_default.entries = NULL;
+	chip->wipower_pt.entries = NULL;
+	chip->wipower_div2.entries = NULL;
+	chip->vadc_dev = NULL;
+	return rc;
+}
+
+#define DEFAULT_VLED_MAX_UV		3500000
+#define DEFAULT_FCC_MA			2000
+static int smb_parse_dt(struct smbchg_chip *chip)
+{
+	int rc = 0, ocp_thresh = -EINVAL;
+	struct device_node *node = chip->dev->of_node;
+	const char *dc_psy_type, *bpd;
+
+	if (!node) {
+		dev_err(chip->dev, "device tree info. missing\n");
+		return -EINVAL;
+	}
+
+	/* read optional u32 properties */
+	OF_PROP_READ(chip, ocp_thresh,
+			"ibat-ocp-threshold-ua", rc, 1);
+	if (ocp_thresh >= 0)
+		smbchg_ibat_ocp_threshold_ua = ocp_thresh;
+	OF_PROP_READ(chip, chip->iterm_ma, "iterm-ma", rc, 1);
+	OF_PROP_READ(chip, chip->cfg_fastchg_current_ma,
+			"fastchg-current-ma", rc, 1);
+	if (chip->cfg_fastchg_current_ma == -EINVAL)
+		chip->cfg_fastchg_current_ma = DEFAULT_FCC_MA;
+	OF_PROP_READ(chip, chip->vfloat_mv, "float-voltage-mv", rc, 1);
+	OF_PROP_READ(chip, chip->safety_time, "charging-timeout-mins", rc, 1);
+	OF_PROP_READ(chip, chip->vled_max_uv, "vled-max-uv", rc, 1);
+	if (chip->vled_max_uv < 0)
+		chip->vled_max_uv = DEFAULT_VLED_MAX_UV;
+	OF_PROP_READ(chip, chip->rpara_uohm, "rparasitic-uohm", rc, 1);
+	if (chip->rpara_uohm < 0)
+		chip->rpara_uohm = 0;
+	OF_PROP_READ(chip, chip->prechg_safety_time, "precharging-timeout-mins",
+			rc, 1);
+	OF_PROP_READ(chip, chip->fastchg_current_comp, "fastchg-current-comp",
+			rc, 1);
+	OF_PROP_READ(chip, chip->float_voltage_comp, "float-voltage-comp",
+			rc, 1);
+	if (chip->safety_time != -EINVAL &&
+		(chip->safety_time > chg_time[ARRAY_SIZE(chg_time) - 1])) {
+		dev_err(chip->dev, "Bad charging-timeout-mins %d\n",
+						chip->safety_time);
+		return -EINVAL;
+	}
+	if (chip->prechg_safety_time != -EINVAL &&
+		(chip->prechg_safety_time >
+		 prechg_time[ARRAY_SIZE(prechg_time) - 1])) {
+		dev_err(chip->dev, "Bad precharging-timeout-mins %d\n",
+						chip->prechg_safety_time);
+		return -EINVAL;
+	}
+	OF_PROP_READ(chip, chip->resume_delta_mv, "resume-delta-mv", rc, 1);
+	OF_PROP_READ(chip, chip->parallel.min_current_thr_ma,
+			"parallel-usb-min-current-ma", rc, 1);
+	OF_PROP_READ(chip, chip->parallel.min_9v_current_thr_ma,
+			"parallel-usb-9v-min-current-ma", rc, 1);
+	OF_PROP_READ(chip, chip->parallel.allowed_lowering_ma,
+			"parallel-allowed-lowering-ma", rc, 1);
+	if (chip->parallel.min_current_thr_ma != -EINVAL
+			&& chip->parallel.min_9v_current_thr_ma != -EINVAL)
+		chip->parallel.avail = true;
+	/*
+	 * use the dt values if they exist, otherwise do not touch the params
+	 */
+	of_property_read_u32(node, "qcom,parallel-main-chg-fcc-percent",
+					&smbchg_main_chg_fcc_percent);
+	of_property_read_u32(node, "qcom,parallel-main-chg-icl-percent",
+					&smbchg_main_chg_icl_percent);
+	pr_smb(PR_STATUS, "parallel usb thr: %d, 9v thr: %d\n",
+			chip->parallel.min_current_thr_ma,
+			chip->parallel.min_9v_current_thr_ma);
+	OF_PROP_READ(chip, chip->jeita_temp_hard_limit,
+			"jeita-temp-hard-limit", rc, 1);
+	OF_PROP_READ(chip, chip->aicl_rerun_period_s,
+			"aicl-rerun-period-s", rc, 1);
+	OF_PROP_READ(chip, chip->vchg_adc_channel,
+			"vchg-adc-channel-id", rc, 1);
+
+	/* read boolean configuration properties */
+	chip->use_vfloat_adjustments = of_property_read_bool(node,
+						"qcom,autoadjust-vfloat");
+	chip->bmd_algo_disabled = of_property_read_bool(node,
+						"qcom,bmd-algo-disabled");
+	chip->iterm_disabled = of_property_read_bool(node,
+						"qcom,iterm-disabled");
+	chip->soft_vfloat_comp_disabled = of_property_read_bool(node,
+					"qcom,soft-vfloat-comp-disabled");
+	chip->chg_enabled = !(of_property_read_bool(node,
+						"qcom,charging-disabled"));
+	chip->charge_unknown_battery = of_property_read_bool(node,
+						"qcom,charge-unknown-battery");
+	chip->chg_inhibit_en = of_property_read_bool(node,
+					"qcom,chg-inhibit-en");
+	chip->chg_inhibit_source_fg = of_property_read_bool(node,
+						"qcom,chg-inhibit-fg");
+	chip->low_volt_dcin = of_property_read_bool(node,
+					"qcom,low-volt-dcin");
+	chip->force_aicl_rerun = of_property_read_bool(node,
+					"qcom,force-aicl-rerun");
+	chip->skip_usb_suspend_for_fake_battery = of_property_read_bool(node,
+				"qcom,skip-usb-suspend-for-fake-battery");
+
+	/* parse the battery missing detection pin source */
+	rc = of_property_read_string(chip->pdev->dev.of_node,
+		"qcom,bmd-pin-src", &bpd);
+	if (rc) {
+		/* Select BAT_THM as default BPD scheme */
+		chip->bmd_pin_src = BPD_TYPE_DEFAULT;
+		rc = 0;
+	} else {
+		chip->bmd_pin_src = get_bpd(bpd);
+		if (chip->bmd_pin_src < 0) {
+			dev_err(chip->dev,
+				"failed to determine bpd schema %d\n", rc);
+			return rc;
+		}
+	}
+
+	/* parse the dc power supply configuration */
+	rc = of_property_read_string(node, "qcom,dc-psy-type", &dc_psy_type);
+	if (rc) {
+		chip->dc_psy_type = -EINVAL;
+		rc = 0;
+	} else {
+		if (strcmp(dc_psy_type, "Mains") == 0)
+			chip->dc_psy_type = POWER_SUPPLY_TYPE_MAINS;
+		else if (strcmp(dc_psy_type, "Wireless") == 0)
+			chip->dc_psy_type = POWER_SUPPLY_TYPE_WIRELESS;
+		else if (strcmp(dc_psy_type, "Wipower") == 0)
+			chip->dc_psy_type = POWER_SUPPLY_TYPE_WIPOWER;
+	}
+	if (chip->dc_psy_type != -EINVAL) {
+		OF_PROP_READ(chip, chip->dc_target_current_ma,
+				"dc-psy-ma", rc, 0);
+		if (rc)
+			return rc;
+		if (chip->dc_target_current_ma < DC_MA_MIN
+				|| chip->dc_target_current_ma > DC_MA_MAX) {
+			dev_err(chip->dev, "Bad dc mA %d\n",
+					chip->dc_target_current_ma);
+			return -EINVAL;
+		}
+	}
+
+	if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIPOWER)
+		smb_parse_wipower_dt(chip);
+
+	/* read the bms power supply name */
+	rc = of_property_read_string(node, "qcom,bms-psy-name",
+						&chip->bms_psy_name);
+	if (rc)
+		chip->bms_psy_name = NULL;
+
+	/* read the battery power supply name */
+	rc = of_property_read_string(node, "qcom,battery-psy-name",
+						&chip->battery_psy_name);
+	if (rc)
+		chip->battery_psy_name = "battery";
+
+	/* Get the charger led support property */
+	chip->cfg_chg_led_sw_ctrl =
+		of_property_read_bool(node, "qcom,chg-led-sw-controls");
+	chip->cfg_chg_led_support =
+		of_property_read_bool(node, "qcom,chg-led-support");
+
+	if (of_find_property(node, "qcom,thermal-mitigation",
+					&chip->thermal_levels)) {
+		chip->thermal_mitigation = devm_kzalloc(chip->dev,
+			chip->thermal_levels,
+			GFP_KERNEL);
+
+		if (chip->thermal_mitigation == NULL) {
+			dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n");
+			return -ENOMEM;
+		}
+
+		chip->thermal_levels /= sizeof(int);
+		rc = of_property_read_u32_array(node,
+				"qcom,thermal-mitigation",
+				chip->thermal_mitigation, chip->thermal_levels);
+		if (rc) {
+			dev_err(chip->dev,
+				"Couldn't read threm limits rc = %d\n", rc);
+			return rc;
+		}
+	}
+
+	chip->skip_usb_notification
+		= of_property_read_bool(node,
+				"qcom,skip-usb-notification");
+
+	chip->otg_pinctrl = of_property_read_bool(node, "qcom,otg-pinctrl");
+
+	return 0;
+}
+
+#define SUBTYPE_REG			0x5
+#define SMBCHG_CHGR_SUBTYPE		0x1
+#define SMBCHG_OTG_SUBTYPE		0x8
+#define SMBCHG_BAT_IF_SUBTYPE		0x3
+#define SMBCHG_USB_CHGPTH_SUBTYPE	0x4
+#define SMBCHG_DC_CHGPTH_SUBTYPE	0x5
+#define SMBCHG_MISC_SUBTYPE		0x7
+#define SMBCHG_LITE_CHGR_SUBTYPE	0x51
+#define SMBCHG_LITE_OTG_SUBTYPE		0x58
+#define SMBCHG_LITE_BAT_IF_SUBTYPE	0x53
+#define SMBCHG_LITE_USB_CHGPTH_SUBTYPE	0x54
+#define SMBCHG_LITE_DC_CHGPTH_SUBTYPE	0x55
+#define SMBCHG_LITE_MISC_SUBTYPE	0x57
+static int smbchg_request_irq(struct smbchg_chip *chip,
+				struct device_node *child,
+				int irq_num, char *irq_name,
+				irqreturn_t (irq_handler)(int irq, void *_chip),
+				int flags)
+{
+	int rc;
+
+	irq_num = of_irq_get_byname(child, irq_name);
+	if (irq_num < 0) {
+		dev_err(chip->dev, "Unable to get %s irqn", irq_name);
+		rc = -ENXIO;
+	}
+	rc = devm_request_threaded_irq(chip->dev,
+			irq_num, NULL, irq_handler, flags, irq_name,
+			chip);
+	if (rc < 0) {
+		dev_err(chip->dev, "Unable to request %s irq: %dn",
+				irq_name, rc);
+		rc = -ENXIO;
+	}
+	return 0;
+}
+
+static int smbchg_request_irqs(struct smbchg_chip *chip)
+{
+	int rc = 0;
+	unsigned int base;
+	struct device_node *child;
+	u8 subtype;
+	unsigned long flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+							| IRQF_ONESHOT;
+
+	if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		return -ENXIO;
+	}
+
+	for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			rc = 0;
+			continue;
+		}
+
+		rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
+		if (rc) {
+			dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
+					rc);
+			return rc;
+		}
+
+		switch (subtype) {
+		case SMBCHG_CHGR_SUBTYPE:
+		case SMBCHG_LITE_CHGR_SUBTYPE:
+			rc = smbchg_request_irq(chip, child,
+				chip->chg_error_irq, "chg-error",
+				chg_error_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child, chip->taper_irq,
+				"chg-taper-thr", taper_handler,
+				(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+			if (rc < 0)
+				return rc;
+			disable_irq_nosync(chip->taper_irq);
+			rc = smbchg_request_irq(chip, child, chip->chg_term_irq,
+				"chg-tcc-thr", chg_term_handler,
+				(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child, chip->recharge_irq,
+				"chg-rechg-thr", recharge_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child, chip->fastchg_irq,
+				"chg-p2f-thr", fastchg_handler, flags);
+			if (rc < 0)
+				return rc;
+			enable_irq_wake(chip->chg_term_irq);
+			enable_irq_wake(chip->chg_error_irq);
+			enable_irq_wake(chip->fastchg_irq);
+			break;
+		case SMBCHG_BAT_IF_SUBTYPE:
+		case SMBCHG_LITE_BAT_IF_SUBTYPE:
+			rc = smbchg_request_irq(chip, child, chip->batt_hot_irq,
+				"batt-hot", batt_hot_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->batt_warm_irq,
+				"batt-warm", batt_warm_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->batt_cool_irq,
+				"batt-cool", batt_cool_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->batt_cold_irq,
+				"batt-cold", batt_cold_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->batt_missing_irq,
+				"batt-missing", batt_pres_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->vbat_low_irq,
+				"batt-low", vbat_low_handler, flags);
+			if (rc < 0)
+				return rc;
+
+			enable_irq_wake(chip->batt_hot_irq);
+			enable_irq_wake(chip->batt_warm_irq);
+			enable_irq_wake(chip->batt_cool_irq);
+			enable_irq_wake(chip->batt_cold_irq);
+			enable_irq_wake(chip->batt_missing_irq);
+			enable_irq_wake(chip->vbat_low_irq);
+			break;
+		case SMBCHG_USB_CHGPTH_SUBTYPE:
+		case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
+			rc = smbchg_request_irq(chip, child,
+				chip->usbin_uv_irq,
+				"usbin-uv", usbin_uv_handler,
+				flags | IRQF_EARLY_RESUME);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->usbin_ov_irq,
+				"usbin-ov", usbin_ov_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->src_detect_irq,
+				"usbin-src-det",
+				src_detect_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->aicl_done_irq,
+				"aicl-done",
+				aicl_done_handler,
+				(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+			if (rc < 0)
+				return rc;
+
+			if (chip->schg_version != QPNP_SCHG_LITE) {
+				rc = smbchg_request_irq(chip, child,
+					chip->otg_fail_irq, "otg-fail",
+					otg_fail_handler, flags);
+				if (rc < 0)
+					return rc;
+				rc = smbchg_request_irq(chip, child,
+					chip->otg_oc_irq, "otg-oc",
+					otg_oc_handler,
+					(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+				if (rc < 0)
+					return rc;
+				rc = smbchg_request_irq(chip, child,
+					chip->usbid_change_irq, "usbid-change",
+					usbid_change_handler,
+					(IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
+				if (rc < 0)
+					return rc;
+				enable_irq_wake(chip->otg_oc_irq);
+				enable_irq_wake(chip->usbid_change_irq);
+				enable_irq_wake(chip->otg_fail_irq);
+			}
+			enable_irq_wake(chip->usbin_uv_irq);
+			enable_irq_wake(chip->usbin_ov_irq);
+			enable_irq_wake(chip->src_detect_irq);
+			if (chip->parallel.avail && chip->usb_present) {
+				rc = enable_irq_wake(chip->aicl_done_irq);
+				chip->enable_aicl_wake = true;
+			}
+			break;
+		case SMBCHG_DC_CHGPTH_SUBTYPE:
+		case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
+			rc = smbchg_request_irq(chip, child, chip->dcin_uv_irq,
+				"dcin-uv", dcin_uv_handler, flags);
+			if (rc < 0)
+				return rc;
+			enable_irq_wake(chip->dcin_uv_irq);
+			break;
+		case SMBCHG_MISC_SUBTYPE:
+		case SMBCHG_LITE_MISC_SUBTYPE:
+			rc = smbchg_request_irq(chip, child, chip->power_ok_irq,
+				"power-ok", power_ok_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child, chip->chg_hot_irq,
+				"temp-shutdown", chg_hot_handler, flags);
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->wdog_timeout_irq, "wdog-timeout",
+				wdog_timeout_handler, flags);
+			if (rc < 0)
+				return rc;
+			enable_irq_wake(chip->chg_hot_irq);
+			enable_irq_wake(chip->wdog_timeout_irq);
+			break;
+		case SMBCHG_OTG_SUBTYPE:
+			break;
+		case SMBCHG_LITE_OTG_SUBTYPE:
+			rc = smbchg_request_irq(chip, child,
+				chip->usbid_change_irq, "usbid-change",
+				usbid_change_handler,
+				(IRQF_TRIGGER_FALLING | IRQF_ONESHOT));
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->otg_oc_irq, "otg-oc",
+				otg_oc_handler,
+				(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+			if (rc < 0)
+				return rc;
+			rc = smbchg_request_irq(chip, child,
+				chip->otg_fail_irq, "otg-fail",
+				otg_fail_handler, flags);
+			if (rc < 0)
+				return rc;
+			enable_irq_wake(chip->usbid_change_irq);
+			enable_irq_wake(chip->otg_oc_irq);
+			enable_irq_wake(chip->otg_fail_irq);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+#define REQUIRE_BASE(chip, base, rc)					\
+do {									\
+	if (!rc && !chip->base) {					\
+		dev_err(chip->dev, "Missing " #base "\n");		\
+		rc = -EINVAL;						\
+	}								\
+} while (0)
+
+static int smbchg_parse_peripherals(struct smbchg_chip *chip)
+{
+	int rc = 0;
+	unsigned int base;
+	struct device_node *child;
+	u8 subtype;
+
+	if (of_get_available_child_count(chip->pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		return -ENXIO;
+	}
+
+	for_each_available_child_of_node(chip->pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			rc = 0;
+			continue;
+		}
+
+		rc = smbchg_read(chip, &subtype, base + SUBTYPE_REG, 1);
+		if (rc) {
+			dev_err(chip->dev, "Peripheral subtype read failed rc=%d\n",
+					rc);
+			return rc;
+		}
+
+		switch (subtype) {
+		case SMBCHG_CHGR_SUBTYPE:
+		case SMBCHG_LITE_CHGR_SUBTYPE:
+			chip->chgr_base = base;
+			break;
+		case SMBCHG_BAT_IF_SUBTYPE:
+		case SMBCHG_LITE_BAT_IF_SUBTYPE:
+			chip->bat_if_base = base;
+			break;
+		case SMBCHG_USB_CHGPTH_SUBTYPE:
+		case SMBCHG_LITE_USB_CHGPTH_SUBTYPE:
+			chip->usb_chgpth_base = base;
+			break;
+		case SMBCHG_DC_CHGPTH_SUBTYPE:
+		case SMBCHG_LITE_DC_CHGPTH_SUBTYPE:
+			chip->dc_chgpth_base = base;
+			break;
+		case SMBCHG_MISC_SUBTYPE:
+		case SMBCHG_LITE_MISC_SUBTYPE:
+			chip->misc_base = base;
+			break;
+		case SMBCHG_OTG_SUBTYPE:
+		case SMBCHG_LITE_OTG_SUBTYPE:
+			chip->otg_base = base;
+			break;
+		}
+	}
+
+	REQUIRE_BASE(chip, chgr_base, rc);
+	REQUIRE_BASE(chip, bat_if_base, rc);
+	REQUIRE_BASE(chip, usb_chgpth_base, rc);
+	REQUIRE_BASE(chip, dc_chgpth_base, rc);
+	REQUIRE_BASE(chip, misc_base, rc);
+
+	return rc;
+}
+
+static inline void dump_reg(struct smbchg_chip *chip, u16 addr,
+		const char *name)
+{
+	u8 reg;
+
+	smbchg_read(chip, &reg, addr, 1);
+	pr_smb(PR_DUMP, "%s - %04X = %02X\n", name, addr, reg);
+}
+
+/* dumps useful registers for debug */
+static void dump_regs(struct smbchg_chip *chip)
+{
+	u16 addr;
+
+	/* charger peripheral */
+	for (addr = 0xB; addr <= 0x10; addr++)
+		dump_reg(chip, chip->chgr_base + addr, "CHGR Status");
+	for (addr = 0xF0; addr <= 0xFF; addr++)
+		dump_reg(chip, chip->chgr_base + addr, "CHGR Config");
+	/* battery interface peripheral */
+	dump_reg(chip, chip->bat_if_base + RT_STS, "BAT_IF Status");
+	dump_reg(chip, chip->bat_if_base + CMD_CHG_REG, "BAT_IF Command");
+	for (addr = 0xF0; addr <= 0xFB; addr++)
+		dump_reg(chip, chip->bat_if_base + addr, "BAT_IF Config");
+	/* usb charge path peripheral */
+	for (addr = 0x7; addr <= 0x10; addr++)
+		dump_reg(chip, chip->usb_chgpth_base + addr, "USB Status");
+	dump_reg(chip, chip->usb_chgpth_base + CMD_IL, "USB Command");
+	for (addr = 0xF0; addr <= 0xF5; addr++)
+		dump_reg(chip, chip->usb_chgpth_base + addr, "USB Config");
+	/* dc charge path peripheral */
+	dump_reg(chip, chip->dc_chgpth_base + RT_STS, "DC Status");
+	for (addr = 0xF0; addr <= 0xF6; addr++)
+		dump_reg(chip, chip->dc_chgpth_base + addr, "DC Config");
+	/* misc peripheral */
+	dump_reg(chip, chip->misc_base + IDEV_STS, "MISC Status");
+	dump_reg(chip, chip->misc_base + RT_STS, "MISC Status");
+	for (addr = 0xF0; addr <= 0xF3; addr++)
+		dump_reg(chip, chip->misc_base + addr, "MISC CFG");
+}
+
+static int create_debugfs_entries(struct smbchg_chip *chip)
+{
+	struct dentry *ent;
+
+	chip->debug_root = debugfs_create_dir("qpnp-smbcharger", NULL);
+	if (!chip->debug_root) {
+		dev_err(chip->dev, "Couldn't create debug dir\n");
+		return -EINVAL;
+	}
+
+	ent = debugfs_create_file("force_dcin_icl_check",
+				  00100644, chip->debug_root, chip,
+				  &force_dcin_icl_ops);
+	if (!ent) {
+		dev_err(chip->dev,
+			"Couldn't create force dcin icl check file\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int smbchg_check_chg_version(struct smbchg_chip *chip)
+{
+	struct pmic_revid_data *pmic_rev_id;
+	struct device_node *revid_dev_node;
+	int rc;
+
+	revid_dev_node = of_parse_phandle(chip->pdev->dev.of_node,
+					"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	pmic_rev_id = get_revid_data(revid_dev_node);
+	if (IS_ERR(pmic_rev_id)) {
+		rc = PTR_ERR(revid_dev_node);
+		if (rc != -EPROBE_DEFER)
+			pr_err("Unable to get pmic_revid rc=%d\n", rc);
+		return rc;
+	}
+
+	switch (pmic_rev_id->pmic_subtype) {
+	case PMI8994:
+		chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA
+				| SMBCHG_BATT_OV_WA
+				| SMBCHG_CC_ESR_WA
+				| SMBCHG_RESTART_WA;
+		use_pmi8994_tables(chip);
+		chip->schg_version = QPNP_SCHG;
+		break;
+	case PMI8950:
+	case PMI8937:
+		chip->wa_flags |= SMBCHG_BATT_OV_WA;
+		if (pmic_rev_id->rev4 < 2) /* PMI8950 1.0 */ {
+			chip->wa_flags |= SMBCHG_AICL_DEGLITCH_WA;
+		} else	{ /* rev > PMI8950 v1.0 */
+			chip->wa_flags |= SMBCHG_HVDCP_9V_EN_WA
+					| SMBCHG_USB100_WA;
+		}
+		use_pmi8994_tables(chip);
+		chip->tables.aicl_rerun_period_table =
+				aicl_rerun_period_schg_lite;
+		chip->tables.aicl_rerun_period_len =
+			ARRAY_SIZE(aicl_rerun_period_schg_lite);
+
+		chip->schg_version = QPNP_SCHG_LITE;
+		if (pmic_rev_id->pmic_subtype == PMI8937)
+			chip->hvdcp_not_supported = true;
+		break;
+	case PMI8996:
+		chip->wa_flags |= SMBCHG_CC_ESR_WA
+				| SMBCHG_FLASH_ICL_DISABLE_WA
+				| SMBCHG_RESTART_WA
+				| SMBCHG_FLASH_BUCK_SWITCH_FREQ_WA;
+		use_pmi8996_tables(chip);
+		chip->schg_version = QPNP_SCHG;
+		break;
+	default:
+		pr_err("PMIC subtype %d not supported, WA flags not set\n",
+				pmic_rev_id->pmic_subtype);
+	}
+
+	pr_smb(PR_STATUS, "pmic=%s, wa_flags=0x%x, hvdcp_supported=%s\n",
+			pmic_rev_id->pmic_name, chip->wa_flags,
+			chip->hvdcp_not_supported ? "false" : "true");
+
+	return 0;
+}
+
+static void rerun_hvdcp_det_if_necessary(struct smbchg_chip *chip)
+{
+	enum power_supply_type usb_supply_type;
+	char *usb_type_name;
+	int rc;
+
+	if (!(chip->wa_flags & SMBCHG_RESTART_WA))
+		return;
+
+	read_usb_type(chip, &usb_type_name, &usb_supply_type);
+	if (usb_supply_type == POWER_SUPPLY_TYPE_USB_DCP
+		&& !is_hvdcp_present(chip)) {
+		pr_smb(PR_STATUS, "DCP found rerunning APSD\n");
+		rc = vote(chip->usb_icl_votable,
+				CHG_SUSPEND_WORKAROUND_ICL_VOTER, true, 300);
+		if (rc < 0)
+			pr_err("Couldn't vote for 300mA for suspend wa, going ahead rc=%d\n",
+					rc);
+
+		pr_smb(PR_STATUS, "Faking Removal\n");
+		fake_insertion_removal(chip, false);
+		msleep(500);
+		pr_smb(PR_STATUS, "Faking Insertion\n");
+		fake_insertion_removal(chip, true);
+
+		read_usb_type(chip, &usb_type_name, &usb_supply_type);
+		if (usb_supply_type != POWER_SUPPLY_TYPE_USB_DCP) {
+			msleep(500);
+			pr_smb(PR_STATUS, "Fake Removal again as type!=DCP\n");
+			fake_insertion_removal(chip, false);
+			msleep(500);
+			pr_smb(PR_STATUS, "Fake Insert again as type!=DCP\n");
+			fake_insertion_removal(chip, true);
+		}
+
+		rc = vote(chip->usb_icl_votable,
+				CHG_SUSPEND_WORKAROUND_ICL_VOTER, false, 0);
+		if (rc < 0)
+			pr_err("Couldn't vote for 0 for suspend wa, going ahead rc=%d\n",
+					rc);
+	}
+}
+
+static int smbchg_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct smbchg_chip *chip;
+	struct power_supply *typec_psy = NULL;
+	struct qpnp_vadc_chip *vadc_dev, *vchg_vadc_dev;
+	const char *typec_psy_name;
+	struct power_supply_config usb_psy_cfg = {};
+	struct power_supply_config batt_psy_cfg = {};
+	struct power_supply_config dc_psy_cfg = {};
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,external-typec")) {
+		/* read the type power supply name */
+		rc = of_property_read_string(pdev->dev.of_node,
+				"qcom,typec-psy-name", &typec_psy_name);
+		if (rc) {
+			pr_err("failed to get prop typec-psy-name rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		typec_psy = power_supply_get_by_name(typec_psy_name);
+		if (!typec_psy) {
+			pr_smb(PR_STATUS,
+				"Type-C supply not found, deferring probe\n");
+			return -EPROBE_DEFER;
+		}
+	}
+
+	vadc_dev = NULL;
+	if (of_find_property(pdev->dev.of_node, "qcom,dcin-vadc", NULL)) {
+		vadc_dev = qpnp_get_vadc(&pdev->dev, "dcin");
+		if (IS_ERR(vadc_dev)) {
+			rc = PTR_ERR(vadc_dev);
+			if (rc != -EPROBE_DEFER)
+				dev_err(&pdev->dev,
+					"Couldn't get vadc rc=%d\n",
+						rc);
+			return rc;
+		}
+	}
+
+	vchg_vadc_dev = NULL;
+	if (of_find_property(pdev->dev.of_node, "qcom,vchg_sns-vadc", NULL)) {
+		vchg_vadc_dev = qpnp_get_vadc(&pdev->dev, "vchg_sns");
+		if (IS_ERR(vchg_vadc_dev)) {
+			rc = PTR_ERR(vchg_vadc_dev);
+			if (rc != -EPROBE_DEFER)
+				dev_err(&pdev->dev, "Couldn't get vadc 'vchg' rc=%d\n",
+						rc);
+			return rc;
+		}
+	}
+
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	chip->fcc_votable = create_votable("BATT_FCC",
+			VOTE_MIN,
+			set_fastchg_current_vote_cb, chip);
+	if (IS_ERR(chip->fcc_votable)) {
+		rc = PTR_ERR(chip->fcc_votable);
+		goto votables_cleanup;
+	}
+
+	chip->usb_icl_votable = create_votable("USB_ICL",
+			VOTE_MIN,
+			set_usb_current_limit_vote_cb, chip);
+	if (IS_ERR(chip->usb_icl_votable)) {
+		rc = PTR_ERR(chip->usb_icl_votable);
+		goto votables_cleanup;
+	}
+
+	chip->dc_icl_votable = create_votable("DCIN_ICL",
+			VOTE_MIN,
+			set_dc_current_limit_vote_cb, chip);
+	if (IS_ERR(chip->dc_icl_votable)) {
+		rc = PTR_ERR(chip->dc_icl_votable);
+		goto votables_cleanup;
+	}
+
+	chip->usb_suspend_votable = create_votable("USB_SUSPEND",
+			VOTE_SET_ANY,
+			usb_suspend_vote_cb, chip);
+	if (IS_ERR(chip->usb_suspend_votable)) {
+		rc = PTR_ERR(chip->usb_suspend_votable);
+		goto votables_cleanup;
+	}
+
+	chip->dc_suspend_votable = create_votable("DC_SUSPEND",
+			VOTE_SET_ANY,
+			dc_suspend_vote_cb, chip);
+	if (IS_ERR(chip->dc_suspend_votable)) {
+		rc = PTR_ERR(chip->dc_suspend_votable);
+		goto votables_cleanup;
+	}
+
+	chip->battchg_suspend_votable = create_votable("BATTCHG_SUSPEND",
+			VOTE_SET_ANY,
+			charging_suspend_vote_cb, chip);
+	if (IS_ERR(chip->battchg_suspend_votable)) {
+		rc = PTR_ERR(chip->battchg_suspend_votable);
+		goto votables_cleanup;
+	}
+
+	chip->hw_aicl_rerun_disable_votable = create_votable("HWAICL_DISABLE",
+			VOTE_SET_ANY,
+			smbchg_hw_aicl_rerun_disable_cb, chip);
+	if (IS_ERR(chip->hw_aicl_rerun_disable_votable)) {
+		rc = PTR_ERR(chip->hw_aicl_rerun_disable_votable);
+		goto votables_cleanup;
+	}
+
+	chip->hw_aicl_rerun_enable_indirect_votable = create_votable(
+			"HWAICL_ENABLE_INDIRECT",
+			VOTE_SET_ANY,
+			smbchg_hw_aicl_rerun_enable_indirect_cb, chip);
+	if (IS_ERR(chip->hw_aicl_rerun_enable_indirect_votable)) {
+		rc = PTR_ERR(chip->hw_aicl_rerun_enable_indirect_votable);
+		goto votables_cleanup;
+	}
+
+	chip->aicl_deglitch_short_votable = create_votable(
+			"HWAICL_SHORT_DEGLITCH",
+			VOTE_SET_ANY,
+			smbchg_aicl_deglitch_config_cb, chip);
+	if (IS_ERR(chip->aicl_deglitch_short_votable)) {
+		rc = PTR_ERR(chip->aicl_deglitch_short_votable);
+		goto votables_cleanup;
+	}
+
+	INIT_WORK(&chip->usb_set_online_work, smbchg_usb_update_online_work);
+	INIT_DELAYED_WORK(&chip->parallel_en_work,
+			smbchg_parallel_usb_en_work);
+	INIT_DELAYED_WORK(&chip->vfloat_adjust_work, smbchg_vfloat_adjust_work);
+	INIT_DELAYED_WORK(&chip->hvdcp_det_work, smbchg_hvdcp_det_work);
+	init_completion(&chip->src_det_lowered);
+	init_completion(&chip->src_det_raised);
+	init_completion(&chip->usbin_uv_lowered);
+	init_completion(&chip->usbin_uv_raised);
+	chip->vadc_dev = vadc_dev;
+	chip->vchg_vadc_dev = vchg_vadc_dev;
+	chip->pdev = pdev;
+	chip->dev = &pdev->dev;
+
+	chip->typec_psy = typec_psy;
+	chip->fake_battery_soc = -EINVAL;
+	chip->usb_online = -EINVAL;
+	dev_set_drvdata(&pdev->dev, chip);
+
+	spin_lock_init(&chip->sec_access_lock);
+	mutex_init(&chip->therm_lvl_lock);
+	mutex_init(&chip->usb_set_online_lock);
+	mutex_init(&chip->parallel.lock);
+	mutex_init(&chip->taper_irq_lock);
+	mutex_init(&chip->pm_lock);
+	mutex_init(&chip->wipower_config);
+	mutex_init(&chip->usb_status_lock);
+	device_init_wakeup(chip->dev, true);
+
+	rc = smbchg_parse_peripherals(chip);
+	if (rc) {
+		dev_err(chip->dev, "Error parsing DT peripherals: %d\n", rc);
+		goto votables_cleanup;
+	}
+
+	rc = smbchg_check_chg_version(chip);
+	if (rc) {
+		pr_err("Unable to check schg version rc=%d\n", rc);
+		goto votables_cleanup;
+	}
+
+	rc = smb_parse_dt(chip);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Unable to parse DT nodes: %d\n", rc);
+		goto votables_cleanup;
+	}
+
+	rc = smbchg_regulator_init(chip);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"Couldn't initialize regulator rc=%d\n", rc);
+		goto votables_cleanup;
+	}
+
+	chip->extcon = devm_extcon_dev_allocate(chip->dev, smbchg_extcon_cable);
+	if (IS_ERR(chip->extcon)) {
+		dev_err(chip->dev, "failed to allocate extcon device\n");
+		rc = PTR_ERR(chip->extcon);
+		goto votables_cleanup;
+	}
+
+	rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+	if (rc) {
+		dev_err(chip->dev, "failed to register extcon device\n");
+		goto votables_cleanup;
+	}
+
+	chip->usb_psy_d.name = "usb";
+	chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+	chip->usb_psy_d.get_property = smbchg_usb_get_property;
+	chip->usb_psy_d.set_property = smbchg_usb_set_property;
+	chip->usb_psy_d.properties = smbchg_usb_properties;
+	chip->usb_psy_d.num_properties = ARRAY_SIZE(smbchg_usb_properties);
+	chip->usb_psy_d.property_is_writeable = smbchg_usb_is_writeable;
+
+	usb_psy_cfg.drv_data = chip;
+	usb_psy_cfg.supplied_to = smbchg_usb_supplicants;
+	usb_psy_cfg.num_supplicants = ARRAY_SIZE(smbchg_usb_supplicants);
+
+	chip->usb_psy = devm_power_supply_register(chip->dev,
+				&chip->usb_psy_d, &usb_psy_cfg);
+	if (IS_ERR(chip->usb_psy)) {
+		dev_err(&pdev->dev, "Unable to register usb_psy rc = %ld\n",
+			PTR_ERR(chip->usb_psy));
+		rc = PTR_ERR(chip->usb_psy);
+		goto votables_cleanup;
+	}
+
+	if (of_find_property(chip->dev->of_node, "dpdm-supply", NULL)) {
+		chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
+		if (IS_ERR(chip->dpdm_reg)) {
+			rc = PTR_ERR(chip->dpdm_reg);
+			goto votables_cleanup;
+		}
+	}
+
+	rc = smbchg_hw_init(chip);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Unable to initialize hardware rc = %d\n", rc);
+		goto out;
+	}
+
+	rc = determine_initial_status(chip);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Unable to determine init status rc = %d\n", rc);
+		goto out;
+	}
+
+	chip->previous_soc = -EINVAL;
+	chip->batt_psy_d.name		= chip->battery_psy_name;
+	chip->batt_psy_d.type		= POWER_SUPPLY_TYPE_BATTERY;
+	chip->batt_psy_d.get_property	= smbchg_battery_get_property;
+	chip->batt_psy_d.set_property	= smbchg_battery_set_property;
+	chip->batt_psy_d.properties	= smbchg_battery_properties;
+	chip->batt_psy_d.num_properties	= ARRAY_SIZE(smbchg_battery_properties);
+	chip->batt_psy_d.external_power_changed = smbchg_external_power_changed;
+	chip->batt_psy_d.property_is_writeable = smbchg_battery_is_writeable;
+
+	batt_psy_cfg.drv_data = chip;
+	batt_psy_cfg.num_supplicants = 0;
+	chip->batt_psy = devm_power_supply_register(chip->dev,
+			&chip->batt_psy_d,
+			&batt_psy_cfg);
+	if (IS_ERR(chip->batt_psy)) {
+		dev_err(&pdev->dev,
+			"Unable to register batt_psy rc = %ld\n",
+			PTR_ERR(chip->batt_psy));
+		goto out;
+	}
+
+	if (chip->dc_psy_type != -EINVAL) {
+		chip->dc_psy_d.name = "dc";
+		chip->dc_psy_d.type = chip->dc_psy_type;
+		chip->dc_psy_d.get_property = smbchg_dc_get_property;
+		chip->dc_psy_d.set_property = smbchg_dc_set_property;
+		chip->dc_psy_d.property_is_writeable = smbchg_dc_is_writeable;
+		chip->dc_psy_d.properties = smbchg_dc_properties;
+		chip->dc_psy_d.num_properties
+			= ARRAY_SIZE(smbchg_dc_properties);
+
+		dc_psy_cfg.drv_data = chip;
+		dc_psy_cfg.num_supplicants
+			= ARRAY_SIZE(smbchg_dc_supplicants);
+		dc_psy_cfg.supplied_to = smbchg_dc_supplicants;
+
+		chip->dc_psy = devm_power_supply_register(chip->dev,
+				&chip->dc_psy_d,
+				&dc_psy_cfg);
+		if (IS_ERR(chip->dc_psy)) {
+			dev_err(&pdev->dev,
+				"Unable to register dc_psy rc = %ld\n",
+				PTR_ERR(chip->dc_psy));
+			goto out;
+		}
+	}
+
+	if (chip->cfg_chg_led_support &&
+			chip->schg_version == QPNP_SCHG_LITE) {
+		rc = smbchg_register_chg_led(chip);
+		if (rc) {
+			dev_err(chip->dev,
+					"Unable to register charger led: %d\n",
+					rc);
+			goto out;
+		}
+
+		rc = smbchg_chg_led_controls(chip);
+		if (rc) {
+			dev_err(chip->dev,
+					"Failed to set charger led controld bit: %d\n",
+					rc);
+			goto unregister_led_class;
+		}
+	}
+
+	rc = smbchg_request_irqs(chip);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "Unable to request irqs rc = %d\n", rc);
+		goto unregister_led_class;
+	}
+
+	rerun_hvdcp_det_if_necessary(chip);
+
+	dump_regs(chip);
+	create_debugfs_entries(chip);
+	dev_info(chip->dev,
+		"SMBCHG successfully probe Charger version=%s Revision DIG:%d.%d ANA:%d.%d batt=%d dc=%d usb=%d\n",
+			version_str[chip->schg_version],
+			chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
+			chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR],
+			get_prop_batt_present(chip),
+			chip->dc_present, chip->usb_present);
+	return 0;
+
+unregister_led_class:
+	if (chip->cfg_chg_led_support && chip->schg_version == QPNP_SCHG_LITE)
+		led_classdev_unregister(&chip->led_cdev);
+out:
+	handle_usb_removal(chip);
+votables_cleanup:
+	if (chip->aicl_deglitch_short_votable)
+		destroy_votable(chip->aicl_deglitch_short_votable);
+	if (chip->hw_aicl_rerun_enable_indirect_votable)
+		destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
+	if (chip->hw_aicl_rerun_disable_votable)
+		destroy_votable(chip->hw_aicl_rerun_disable_votable);
+	if (chip->battchg_suspend_votable)
+		destroy_votable(chip->battchg_suspend_votable);
+	if (chip->dc_suspend_votable)
+		destroy_votable(chip->dc_suspend_votable);
+	if (chip->usb_suspend_votable)
+		destroy_votable(chip->usb_suspend_votable);
+	if (chip->dc_icl_votable)
+		destroy_votable(chip->dc_icl_votable);
+	if (chip->usb_icl_votable)
+		destroy_votable(chip->usb_icl_votable);
+	if (chip->fcc_votable)
+		destroy_votable(chip->fcc_votable);
+	return rc;
+}
+
+static int smbchg_remove(struct platform_device *pdev)
+{
+	struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
+
+	debugfs_remove_recursive(chip->debug_root);
+
+	destroy_votable(chip->aicl_deglitch_short_votable);
+	destroy_votable(chip->hw_aicl_rerun_enable_indirect_votable);
+	destroy_votable(chip->hw_aicl_rerun_disable_votable);
+	destroy_votable(chip->battchg_suspend_votable);
+	destroy_votable(chip->dc_suspend_votable);
+	destroy_votable(chip->usb_suspend_votable);
+	destroy_votable(chip->dc_icl_votable);
+	destroy_votable(chip->usb_icl_votable);
+	destroy_votable(chip->fcc_votable);
+
+	return 0;
+}
+
+static void smbchg_shutdown(struct platform_device *pdev)
+{
+	struct smbchg_chip *chip = dev_get_drvdata(&pdev->dev);
+	int rc;
+
+	if (!(chip->wa_flags & SMBCHG_RESTART_WA))
+		return;
+
+	if (!is_hvdcp_present(chip))
+		return;
+
+	pr_smb(PR_MISC, "Disable Parallel\n");
+	mutex_lock(&chip->parallel.lock);
+	smbchg_parallel_en = 0;
+	smbchg_parallel_usb_disable(chip);
+	mutex_unlock(&chip->parallel.lock);
+
+	pr_smb(PR_MISC, "Disable all interrupts\n");
+	disable_irq(chip->aicl_done_irq);
+	disable_irq(chip->batt_cold_irq);
+	disable_irq(chip->batt_cool_irq);
+	disable_irq(chip->batt_hot_irq);
+	disable_irq(chip->batt_missing_irq);
+	disable_irq(chip->batt_warm_irq);
+	disable_irq(chip->chg_error_irq);
+	disable_irq(chip->chg_hot_irq);
+	disable_irq(chip->chg_term_irq);
+	disable_irq(chip->dcin_uv_irq);
+	disable_irq(chip->fastchg_irq);
+	disable_irq(chip->otg_fail_irq);
+	disable_irq(chip->otg_oc_irq);
+	disable_irq(chip->power_ok_irq);
+	disable_irq(chip->recharge_irq);
+	disable_irq(chip->src_detect_irq);
+	disable_irq(chip->taper_irq);
+	disable_irq(chip->usbid_change_irq);
+	disable_irq(chip->usbin_ov_irq);
+	disable_irq(chip->usbin_uv_irq);
+	disable_irq(chip->vbat_low_irq);
+	disable_irq(chip->wdog_timeout_irq);
+
+	/* remove all votes for short deglitch */
+	vote(chip->aicl_deglitch_short_votable,
+			VARB_WORKAROUND_SHORT_DEGLITCH_VOTER, false, 0);
+	vote(chip->aicl_deglitch_short_votable,
+			HVDCP_SHORT_DEGLITCH_VOTER, false, 0);
+
+	/* vote to ensure AICL rerun is enabled */
+	rc = vote(chip->hw_aicl_rerun_enable_indirect_votable,
+			SHUTDOWN_WORKAROUND_VOTER, true, 0);
+	if (rc < 0)
+		pr_err("Couldn't vote to enable indirect AICL rerun\n");
+	rc = vote(chip->hw_aicl_rerun_disable_votable,
+		WEAK_CHARGER_HW_AICL_VOTER, false, 0);
+	if (rc < 0)
+		pr_err("Couldn't vote to enable AICL rerun\n");
+
+	/* switch to 5V HVDCP */
+	pr_smb(PR_MISC, "Switch to 5V HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+				HVDCP_ADAPTER_SEL_MASK, HVDCP_5V);
+	if (rc < 0) {
+		pr_err("Couldn't configure HVDCP 5V rc=%d\n", rc);
+		return;
+	}
+
+	pr_smb(PR_MISC, "Wait 500mS to lower to 5V\n");
+	/* wait for HVDCP to lower to 5V */
+	msleep(500);
+	/*
+	 * Check if the same hvdcp session is in progress. src_det should be
+	 * high and that we are still in 5V hvdcp
+	 */
+	if (!is_src_detect_high(chip)) {
+		pr_smb(PR_MISC, "src det low after 500mS sleep\n");
+		return;
+	}
+
+	/* disable HVDCP */
+	pr_smb(PR_MISC, "Disable HVDCP\n");
+	rc = smbchg_sec_masked_write(chip, chip->usb_chgpth_base + CHGPTH_CFG,
+			HVDCP_EN_BIT, 0);
+	if (rc < 0)
+		pr_err("Couldn't disable HVDCP rc=%d\n", rc);
+
+	chip->hvdcp_3_det_ignore_uv = true;
+	/* fake a removal */
+	pr_smb(PR_MISC, "Faking Removal\n");
+	rc = fake_insertion_removal(chip, false);
+	if (rc < 0)
+		pr_err("Couldn't fake removal HVDCP Removed rc=%d\n", rc);
+
+	/* fake an insertion */
+	pr_smb(PR_MISC, "Faking Insertion\n");
+	rc = fake_insertion_removal(chip, true);
+	if (rc < 0)
+		pr_err("Couldn't fake insertion rc=%d\n", rc);
+
+	pr_smb(PR_MISC, "Wait 1S to settle\n");
+	msleep(1000);
+	chip->hvdcp_3_det_ignore_uv = false;
+
+	pr_smb(PR_STATUS, "wrote power off configurations\n");
+}
+
+static const struct dev_pm_ops smbchg_pm_ops = {
+};
+
+MODULE_DEVICE_TABLE(spmi, smbchg_id);
+
+static struct platform_driver smbchg_driver = {
+	.driver		= {
+		.name		= "qpnp-smbcharger",
+		.owner		= THIS_MODULE,
+		.of_match_table	= smbchg_match_table,
+		.pm		= &smbchg_pm_ops,
+	},
+	.probe		= smbchg_probe,
+	.remove		= smbchg_remove,
+	.shutdown	= smbchg_shutdown,
+};
+
+static int __init smbchg_init(void)
+{
+	return platform_driver_register(&smbchg_driver);
+}
+
+static void __exit smbchg_exit(void)
+{
+	return platform_driver_unregister(&smbchg_driver);
+}
+
+module_init(smbchg_init);
+module_exit(smbchg_exit);
+
+MODULE_DESCRIPTION("QPNP SMB Charger");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qpnp-smbcharger");
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index 48e689f..0012a92 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -143,6 +143,23 @@
 	return rc;
 }
 
+int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override)
+{
+	int rc;
+
+	/* override  = 1, SW STAT override; override = 0, HW auto mode */
+	rc = smblib_masked_write(chg, STAT_CFG_REG,
+				STAT_SW_OVERRIDE_CFG_BIT,
+				override ? STAT_SW_OVERRIDE_CFG_BIT : 0);
+	if (rc < 0) {
+		dev_err(chg->dev, "Couldn't configure SW STAT override rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	return rc;
+}
+
 /********************
  * REGISTER GETTERS *
  ********************/
@@ -584,8 +601,10 @@
 			schedule_work(&chg->bms_update_work);
 	}
 
-	if (!chg->pl.psy && !strcmp(psy->desc->name, "parallel"))
+	if (!chg->pl.psy && !strcmp(psy->desc->name, "parallel")) {
 		chg->pl.psy = psy;
+		schedule_work(&chg->pl_update_work);
+	}
 
 	return NOTIFY_OK;
 }
@@ -891,7 +910,7 @@
 	bool override;
 
 	/* suspend and return if 25mA or less is requested */
-	if (icl_ua < USBIN_25MA)
+	if (icl_ua <= USBIN_25MA)
 		return smblib_set_usb_suspend(chg, true);
 
 	if (icl_ua == INT_MAX)
@@ -960,8 +979,8 @@
 	u8 load_cfg;
 	bool override;
 
-	if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
-		|| chg->micro_usb_mode)
+	if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
+		|| (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB))
 		&& (chg->usb_psy_desc.type == POWER_SUPPLY_TYPE_USB)) {
 		rc = get_sdp_current(chg, icl_ua);
 		if (rc < 0) {
@@ -1017,7 +1036,7 @@
 		icl_ua = 0;
 	}
 
-	suspend = (icl_ua < USBIN_25MA);
+	suspend = (icl_ua <= USBIN_25MA);
 	if (suspend)
 		goto suspend;
 
@@ -2035,6 +2054,18 @@
 	return rc;
 }
 
+static int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val)
+{
+	int rc;
+
+	rc = smblib_masked_write(chg, CMD_HVDCP_2_REG, val, val);
+	if (rc < 0)
+		smblib_err(chg, "Couldn't write to CMD_HVDCP_2_REG rc=%d\n",
+				rc);
+
+	return rc;
+}
+
 int smblib_dp_dm(struct smb_charger *chg, int val)
 {
 	int target_icl_ua, rc = 0;
@@ -2086,6 +2117,21 @@
 		smblib_dbg(chg, PR_PARALLEL, "ICL DOWN ICL=%d reduction=%d\n",
 				target_icl_ua, chg->usb_icl_delta_ua);
 		break;
+	case POWER_SUPPLY_DP_DM_FORCE_5V:
+		rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT);
+		if (rc < 0)
+			pr_err("Failed to force 5V\n");
+		break;
+	case POWER_SUPPLY_DP_DM_FORCE_9V:
+		rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT);
+		if (rc < 0)
+			pr_err("Failed to force 9V\n");
+		break;
+	case POWER_SUPPLY_DP_DM_FORCE_12V:
+		rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT);
+		if (rc < 0)
+			pr_err("Failed to force 12V\n");
+		break;
 	case POWER_SUPPLY_DP_DM_ICL_UP:
 	default:
 		break;
@@ -2250,12 +2296,6 @@
 int smblib_get_prop_usb_voltage_now(struct smb_charger *chg,
 				    union power_supply_propval *val)
 {
-	int rc = 0;
-
-	rc = smblib_get_prop_usb_present(chg, val);
-	if (rc < 0 || !val->intval)
-		return rc;
-
 	if (!chg->iio.usbin_v_chan ||
 		PTR_ERR(chg->iio.usbin_v_chan) == -EPROBE_DEFER)
 		chg->iio.usbin_v_chan = iio_channel_get(chg->dev, "usbin_v");
@@ -2499,23 +2539,16 @@
 		return rc;
 	}
 
-	/* TEMP_RANGE bits are mutually exclusive */
-	switch (stat & TEMP_RANGE_MASK) {
-	case TEMP_BELOW_RANGE_BIT:
-		val->intval = POWER_SUPPLY_HEALTH_COOL;
-		break;
-	case TEMP_WITHIN_RANGE_BIT:
-		val->intval = POWER_SUPPLY_HEALTH_WARM;
-		break;
-	case TEMP_ABOVE_RANGE_BIT:
-		val->intval = POWER_SUPPLY_HEALTH_HOT;
-		break;
-	case ALERT_LEVEL_BIT:
+	if (stat & ALERT_LEVEL_BIT)
 		val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
-		break;
-	default:
+	else if (stat & TEMP_ABOVE_RANGE_BIT)
+		val->intval = POWER_SUPPLY_HEALTH_HOT;
+	else if (stat & TEMP_WITHIN_RANGE_BIT)
+		val->intval = POWER_SUPPLY_HEALTH_WARM;
+	else if (stat & TEMP_BELOW_RANGE_BIT)
+		val->intval = POWER_SUPPLY_HEALTH_COOL;
+	else
 		val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
-	}
 
 	return 0;
 }
@@ -2727,17 +2760,13 @@
 	return rc;
 }
 
-int smblib_set_prop_pd_active(struct smb_charger *chg,
-			      const union power_supply_propval *val)
+static int __smblib_set_prop_pd_active(struct smb_charger *chg, bool pd_active)
 {
 	int rc;
 	bool orientation, sink_attached, hvdcp;
 	u8 stat;
 
-	if (!get_effective_result(chg->pd_allowed_votable))
-		return -EINVAL;
-
-	chg->pd_active = val->intval;
+	chg->pd_active = pd_active;
 	if (chg->pd_active) {
 		vote(chg->apsd_disable_votable, PD_VOTER, true, 0);
 		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
@@ -2798,7 +2827,7 @@
 		hvdcp = stat & QC_CHARGER_BIT;
 		vote(chg->apsd_disable_votable, PD_VOTER, false, 0);
 		vote(chg->pd_allowed_votable, PD_VOTER, true, 0);
-		vote(chg->usb_irq_enable_votable, PD_VOTER, true, 0);
+		vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
 		vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER,
 								false, 0);
 
@@ -2825,6 +2854,15 @@
 	return rc;
 }
 
+int smblib_set_prop_pd_active(struct smb_charger *chg,
+			      const union power_supply_propval *val)
+{
+	if (!get_effective_result(chg->pd_allowed_votable))
+		return -EINVAL;
+
+	return __smblib_set_prop_pd_active(chg, val->intval);
+}
+
 int smblib_set_prop_ship_mode(struct smb_charger *chg,
 				const union power_supply_propval *val)
 {
@@ -3046,6 +3084,11 @@
 {
 	int rc, jeita_cc_delta_ua = 0;
 
+	if (chg->sw_jeita_enabled) {
+		val->intval = 0;
+		return 0;
+	}
+
 	rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
 	if (rc < 0) {
 		smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
@@ -3358,6 +3401,10 @@
 		vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
 		schedule_delayed_work(&chg->pl_enable_work,
 					msecs_to_jiffies(PL_DELAY_MS));
+		/* vbus rising when APSD was disabled and PD_ACTIVE = 0 */
+		if (get_effective_result(chg->apsd_disable_votable) &&
+				!chg->pd_active)
+			pr_err("APSD disabled on vbus rising without PD\n");
 	} else {
 		if (chg->wa_flags & BOOST_BACK_WA) {
 			data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
@@ -3377,7 +3424,7 @@
 			smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
 	}
 
-	if (chg->micro_usb_mode)
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 		smblib_micro_usb_plugin(chg, vbus_rising);
 
 	power_supply_changed(chg->usb_psy);
@@ -3539,16 +3586,6 @@
 
 	/* the APSD done handler will set the USB supply type */
 	apsd_result = smblib_get_apsd_result(chg);
-	if (get_effective_result(chg->hvdcp_hw_inov_dis_votable)) {
-		if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
-			/* force HVDCP2 to 9V if INOV is disabled */
-			rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
-					FORCE_9V_BIT, FORCE_9V_BIT);
-			if (rc < 0)
-				smblib_err(chg,
-					"Couldn't force 9V HVDCP rc=%d\n", rc);
-		}
-	}
 
 	smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
 		   apsd_result->name);
@@ -3576,6 +3613,13 @@
 			/* enforce DCP ICL if specified */
 			vote(chg->usb_icl_votable, DCP_VOTER,
 				chg->dcp_icl_ua != -EINVAL, chg->dcp_icl_ua);
+
+		/*
+		 * if pd is not allowed, then set pd_active = false right here,
+		 * so that it starts the hvdcp engine
+		 */
+		if (!get_effective_result(chg->pd_allowed_votable))
+			__smblib_set_prop_pd_active(chg, 0);
 	}
 
 	smblib_dbg(chg, PR_INTERRUPT, "IRQ: smblib_handle_hvdcp_check_timeout %s\n",
@@ -3642,6 +3686,37 @@
 	}
 }
 
+static void smblib_notify_extcon_props(struct smb_charger *chg, int id)
+{
+	union extcon_property_value val;
+	union power_supply_propval prop_val;
+
+	smblib_get_prop_typec_cc_orientation(chg, &prop_val);
+	val.intval = ((prop_val.intval == 2) ? 1 : 0);
+	extcon_set_property(chg->extcon, id,
+				EXTCON_PROP_USB_TYPEC_POLARITY, val);
+
+	val.intval = true;
+	extcon_set_property(chg->extcon, id,
+				EXTCON_PROP_USB_SS, val);
+}
+
+static void smblib_notify_device_mode(struct smb_charger *chg, bool enable)
+{
+	if (enable)
+		smblib_notify_extcon_props(chg, EXTCON_USB);
+
+	extcon_set_state_sync(chg->extcon, EXTCON_USB, enable);
+}
+
+static void smblib_notify_usb_host(struct smb_charger *chg, bool enable)
+{
+	if (enable)
+		smblib_notify_extcon_props(chg, EXTCON_USB_HOST);
+
+	extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable);
+}
+
 #define HVDCP_DET_MS 2500
 static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
 {
@@ -3658,12 +3733,14 @@
 	switch (apsd_result->bit) {
 	case SDP_CHARGER_BIT:
 	case CDP_CHARGER_BIT:
-		if (chg->micro_usb_mode)
+		if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 			extcon_set_cable_state_(chg->extcon, EXTCON_USB,
 					true);
 		/* if not DCP then no hvdcp timeout happens. Enable pd here */
 		vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
 				false, 0);
+		if (chg->use_extcon)
+			smblib_notify_device_mode(chg, true);
 		break;
 	case OCP_CHARGER_BIT:
 	case FLOAT_CHARGER_BIT:
@@ -3698,7 +3775,8 @@
 	}
 	smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat);
 
-	if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT)
+	if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
+			&& (stat & APSD_DTC_STATUS_DONE_BIT)
 			&& !chg->uusb_apsd_rerun_done) {
 		/*
 		 * Force re-run APSD to handle slow insertion related
@@ -3742,13 +3820,174 @@
 	return IRQ_HANDLED;
 }
 
+static int typec_try_sink(struct smb_charger *chg)
+{
+	union power_supply_propval val;
+	bool debounce_done, vbus_detected, sink;
+	u8 stat;
+	int exit_mode = ATTACHED_SRC, rc;
+
+	/* ignore typec interrupt while try.snk WIP */
+	chg->try_sink_active = true;
+
+	/* force SNK mode */
+	val.intval = POWER_SUPPLY_TYPEC_PR_SINK;
+	rc = smblib_set_prop_typec_power_role(chg, &val);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't set UFP mode rc=%d\n", rc);
+		goto try_sink_exit;
+	}
+
+	/* reduce Tccdebounce time to ~20ms */
+	rc = smblib_masked_write(chg, MISC_CFG_REG,
+			TCC_DEBOUNCE_20MS_BIT, TCC_DEBOUNCE_20MS_BIT);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't set MISC_CFG_REG rc=%d\n", rc);
+		goto try_sink_exit;
+	}
+
+	/*
+	 * give opportunity to the other side to be a SRC,
+	 * for tDRPTRY + Tccdebounce time
+	 */
+	msleep(120);
+
+	rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+				rc);
+		goto try_sink_exit;
+	}
+
+	debounce_done = stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT;
+
+	if (!debounce_done)
+		/*
+		 * The other side didn't switch to source, either it
+		 * is an adamant sink or is removed go back to showing Rp
+		 */
+		goto try_wait_src;
+
+	/*
+	 * We are in force sink mode and the other side has switched to
+	 * showing Rp. Config DRP in case the other side removes Rp so we
+	 * can quickly (20ms) switch to showing our Rp. Note that the spec
+	 * needs us to show Rp for 80mS while the drp DFP residency is just
+	 * 54mS. But 54mS is plenty time for us to react and force Rp for
+	 * the remaining 26mS.
+	 */
+	val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+	rc = smblib_set_prop_typec_power_role(chg, &val);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't set DFP mode rc=%d\n",
+				rc);
+		goto try_sink_exit;
+	}
+
+	/*
+	 * while other side is Rp, wait for VBUS from it; exit if other side
+	 * removes Rp
+	 */
+	do {
+		rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+		if (rc < 0) {
+			smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n",
+					rc);
+			goto try_sink_exit;
+		}
+
+		debounce_done = stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT;
+		vbus_detected = stat & TYPEC_VBUS_STATUS_BIT;
+
+		/* Successfully transitioned to ATTACHED.SNK */
+		if (vbus_detected && debounce_done) {
+			exit_mode = ATTACHED_SINK;
+			goto try_sink_exit;
+		}
+
+		/*
+		 * Ensure sink since drp may put us in source if other
+		 * side switches back to Rd
+		 */
+		sink = !(stat &  UFP_DFP_MODE_STATUS_BIT);
+
+		usleep_range(1000, 2000);
+	} while (debounce_done && sink);
+
+try_wait_src:
+	/*
+	 * Transition to trywait.SRC state. check if other side still wants
+	 * to be SNK or has been removed.
+	 */
+	val.intval = POWER_SUPPLY_TYPEC_PR_SOURCE;
+	rc = smblib_set_prop_typec_power_role(chg, &val);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't set UFP mode rc=%d\n", rc);
+		goto try_sink_exit;
+	}
+
+	/* Need to be in this state for tDRPTRY time, 75ms~150ms */
+	msleep(80);
+
+	rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+	if (rc < 0) {
+		smblib_err(chg, "Couldn't read TYPE_C_STATUS_4 rc=%d\n", rc);
+		goto try_sink_exit;
+	}
+
+	debounce_done = stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT;
+
+	if (debounce_done)
+		/* the other side wants to be a sink */
+		exit_mode = ATTACHED_SRC;
+	else
+		/* the other side is detached */
+		exit_mode = UNATTACHED_SINK;
+
+try_sink_exit:
+	/* release forcing of SRC/SNK mode */
+	val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+	rc = smblib_set_prop_typec_power_role(chg, &val);
+	if (rc < 0)
+		smblib_err(chg, "Couldn't set DFP mode rc=%d\n", rc);
+
+	/* revert Tccdebounce time back to ~120ms */
+	rc = smblib_masked_write(chg, MISC_CFG_REG, TCC_DEBOUNCE_20MS_BIT, 0);
+	if (rc < 0)
+		smblib_err(chg, "Couldn't set MISC_CFG_REG rc=%d\n", rc);
+
+	chg->try_sink_active = false;
+
+	return exit_mode;
+}
+
 static void typec_sink_insertion(struct smb_charger *chg)
 {
+	int exit_mode;
+
+	/*
+	 * Try.SNK entry status - ATTACHWAIT.SRC state and detected Rd-open
+	 * or RD-Ra for TccDebounce time.
+	 */
+
+	if (*chg->try_sink_enabled) {
+		exit_mode = typec_try_sink(chg);
+
+		if (exit_mode != ATTACHED_SRC) {
+			smblib_usb_typec_change(chg);
+			return;
+		}
+	}
+
 	/* when a sink is inserted we should not wait on hvdcp timeout to
 	 * enable pd
 	 */
 	vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
 			false, 0);
+	if (chg->use_extcon) {
+		smblib_notify_usb_host(chg, true);
+		chg->otg_present = true;
+	}
 }
 
 static void typec_sink_removal(struct smb_charger *chg)
@@ -3796,6 +4035,7 @@
 	vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
 	vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
 	vote(chg->usb_icl_votable, OTG_VOTER, false, 0);
+	vote(chg->usb_icl_votable, CTM_VOTER, false, 0);
 
 	/* reset hvdcp voters */
 	vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
@@ -3812,7 +4052,7 @@
 
 	/* reset parallel voters */
 	vote(chg->pl_disable_votable, PL_DELAY_VOTER, true, 0);
-	vote(chg->pl_disable_votable, FCC_CHANGE_VOTER, false, 0);
+	vote(chg->pl_disable_votable, PL_FCC_LOW_VOTER, false, 0);
 	vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
 	vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
 	vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
@@ -3899,6 +4139,14 @@
 
 	typec_sink_removal(chg);
 	smblib_update_usb_type(chg);
+
+	if (chg->use_extcon) {
+		if (chg->otg_present)
+			smblib_notify_usb_host(chg, false);
+		else
+			smblib_notify_device_mode(chg, false);
+	}
+	chg->otg_present = false;
 }
 
 static void smblib_handle_typec_insertion(struct smb_charger *chg)
@@ -3998,7 +4246,7 @@
 				smblib_typec_mode_name[chg->typec_mode]);
 }
 
-static void smblib_usb_typec_change(struct smb_charger *chg)
+void smblib_usb_typec_change(struct smb_charger *chg)
 {
 	int rc;
 
@@ -4025,7 +4273,7 @@
 	struct smb_irq_data *irq_data = data;
 	struct smb_charger *chg = irq_data->parent_data;
 
-	if (chg->micro_usb_mode) {
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) {
 		cancel_delayed_work_sync(&chg->uusb_otg_work);
 		vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0);
 		smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n");
@@ -4034,8 +4282,9 @@
 		return IRQ_HANDLED;
 	}
 
-	if (chg->cc2_detach_wa_active || chg->typec_en_dis_active) {
-		smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
+	if (chg->cc2_detach_wa_active || chg->typec_en_dis_active ||
+					 chg->try_sink_active) {
+		smblib_dbg(chg, PR_MISC | PR_INTERRUPT, "Ignoring since %s active\n",
 			chg->cc2_detach_wa_active ?
 			"cc2_detach_wa" : "typec_en_dis");
 		return IRQ_HANDLED;
@@ -4068,6 +4317,14 @@
 	struct smb_charger *chg = irq_data->parent_data;
 
 	chg->is_hdc = true;
+	/*
+	 * Disable usb IRQs after the flag set and re-enable IRQs after
+	 * the flag cleared in the delayed work queue, to avoid any IRQ
+	 * storming during the delays
+	 */
+	if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
+		disable_irq_nosync(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
+
 	schedule_delayed_work(&chg->clear_hdc_work, msecs_to_jiffies(60));
 
 	return IRQ_HANDLED;
@@ -4104,7 +4361,7 @@
 
 	/* skip suspending input if its already suspended by some other voter */
 	usb_icl = get_effective_result(chg->usb_icl_votable);
-	if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl < USBIN_25MA)
+	if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl <= USBIN_25MA)
 		return IRQ_HANDLED;
 
 	if (stat & USE_DCIN_BIT)
@@ -4239,12 +4496,22 @@
 		power_supply_changed(chg->batt_psy);
 }
 
+static void pl_update_work(struct work_struct *work)
+{
+	struct smb_charger *chg = container_of(work, struct smb_charger,
+						pl_update_work);
+
+	smblib_stat_sw_override_cfg(chg, false);
+}
+
 static void clear_hdc_work(struct work_struct *work)
 {
 	struct smb_charger *chg = container_of(work, struct smb_charger,
 						clear_hdc_work.work);
 
 	chg->is_hdc = 0;
+	if (chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq)
+		enable_irq(chg->irq_info[HIGH_DUTY_CYCLE_IRQ].irq);
 }
 
 static void rdstd_cc2_detach_work(struct work_struct *work)
@@ -4418,7 +4685,7 @@
 	int rc, i;
 	u8 stat;
 
-	if (chg->micro_usb_mode)
+	if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)
 		return;
 
 	smblib_err(chg, "over-current detected on VCONN\n");
@@ -4542,7 +4809,9 @@
 		smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
 
 	/* wait for the adapter to turn off VBUS */
-	msleep(500);
+	msleep(1000);
+
+	smblib_dbg(chg, PR_MISC, "legacy workaround enabling typec\n");
 
 	rc = smblib_masked_write(chg,
 				TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
@@ -4551,7 +4820,7 @@
 		smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
 
 	/* wait for type-c detection to complete */
-	msleep(100);
+	msleep(400);
 
 	rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
 	if (rc < 0) {
@@ -4563,6 +4832,8 @@
 	vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
 	legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
 	rp_high = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH;
+	smblib_dbg(chg, PR_MISC, "legacy workaround done legacy = %d rp_high = %d\n",
+			legacy, rp_high);
 	if (!legacy || !rp_high)
 		vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
 								false, 0);
@@ -4769,6 +5040,7 @@
 	mutex_init(&chg->otg_oc_lock);
 	mutex_init(&chg->vconn_oc_lock);
 	INIT_WORK(&chg->bms_update_work, bms_update_work);
+	INIT_WORK(&chg->pl_update_work, pl_update_work);
 	INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work);
 	INIT_DELAYED_WORK(&chg->hvdcp_detect_work, smblib_hvdcp_detect_work);
 	INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work);
@@ -4793,7 +5065,7 @@
 			return rc;
 		}
 
-		rc = qcom_step_chg_init(chg->step_chg_enabled,
+		rc = qcom_step_chg_init(chg->dev, chg->step_chg_enabled,
 						chg->sw_jeita_enabled);
 		if (rc < 0) {
 			smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
@@ -4817,6 +5089,14 @@
 
 		chg->bms_psy = power_supply_get_by_name("bms");
 		chg->pl.psy = power_supply_get_by_name("parallel");
+		if (chg->pl.psy) {
+			rc = smblib_stat_sw_override_cfg(chg, false);
+			if (rc < 0) {
+				smblib_err(chg,
+					"Couldn't config stat sw rc=%d\n", rc);
+				return rc;
+			}
+		}
 		break;
 	case PARALLEL_SLAVE:
 		break;
@@ -4833,6 +5113,7 @@
 	switch (chg->mode) {
 	case PARALLEL_MASTER:
 		cancel_work_sync(&chg->bms_update_work);
+		cancel_work_sync(&chg->pl_update_work);
 		cancel_work_sync(&chg->rdstd_cc2_detach_work);
 		cancel_delayed_work_sync(&chg->hvdcp_detect_work);
 		cancel_delayed_work_sync(&chg->clear_hdc_work);
diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h
index 704a8db..351a0e9 100644
--- a/drivers/power/supply/qcom/smb-lib.h
+++ b/drivers/power/supply/qcom/smb-lib.h
@@ -66,7 +66,8 @@
 #define USBIN_I_VOTER			"USBIN_I_VOTER"
 #define WEAK_CHARGER_VOTER		"WEAK_CHARGER_VOTER"
 #define OTG_VOTER			"OTG_VOTER"
-#define FCC_CHANGE_VOTER		"FCC_CHANGE_VOTER"
+#define PL_FCC_LOW_VOTER		"PL_FCC_LOW_VOTER"
+#define WBC_VOTER			"WBC_VOTER"
 
 #define VCONN_MAX_ATTEMPTS	3
 #define OTG_MAX_ATTEMPTS	3
@@ -129,6 +130,12 @@
 	SMB_IRQ_MAX,
 };
 
+enum try_sink_exit_mode {
+	ATTACHED_SRC = 0,
+	ATTACHED_SINK,
+	UNATTACHED_SINK,
+};
+
 struct smb_irq_info {
 	const char			*name;
 	const irq_handler_t		handler;
@@ -144,6 +151,9 @@
 	EXTCON_NONE,
 };
 
+/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
+static const u32 smblib_extcon_exclusive[] = {0x3, 0};
+
 struct smb_regulator {
 	struct regulator_dev	*rdev;
 	struct regulator_desc	rdesc;
@@ -228,6 +238,7 @@
 	struct smb_params	param;
 	struct smb_iio		iio;
 	int			*debug_mask;
+	int			*try_sink_enabled;
 	enum smb_mode		mode;
 	struct smb_chg_freq	chg_freq;
 	int			smb_version;
@@ -283,6 +294,7 @@
 
 	/* work */
 	struct work_struct	bms_update_work;
+	struct work_struct	pl_update_work;
 	struct work_struct	rdstd_cc2_detach_work;
 	struct delayed_work	hvdcp_detect_work;
 	struct delayed_work	ps_change_timeout_work;
@@ -312,7 +324,7 @@
 	bool			sw_jeita_enabled;
 	bool			is_hdc;
 	bool			chg_done;
-	bool			micro_usb_mode;
+	bool			connector_type;
 	bool			otg_en;
 	bool			vconn_en;
 	bool			suspend_input_on_debug_batt;
@@ -331,11 +343,14 @@
 	int			usb_icl_change_irq_enabled;
 	u32			jeita_status;
 	u8			float_cfg;
+	bool			use_extcon;
+	bool			otg_present;
 
 	/* workaround flag */
 	u32			wa_flags;
 	bool			cc2_detach_wa_active;
 	bool			typec_en_dis_active;
+	bool			try_sink_active;
 	int			boost_current_ua;
 	int			temp_speed_reading_count;
 
@@ -349,6 +364,8 @@
 	/* qnovo */
 	int			usb_icl_delta_ua;
 	int			pulse_cnt;
+
+	int			die_health;
 };
 
 int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -517,6 +534,8 @@
 				union power_supply_propval *val);
 int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
 				const union power_supply_propval *val);
+int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override);
+void smblib_usb_typec_change(struct smb_charger *chg);
 
 int smblib_init(struct smb_charger *chg);
 int smblib_deinit(struct smb_charger *chg);
diff --git a/drivers/power/supply/qcom/smb-reg.h b/drivers/power/supply/qcom/smb-reg.h
index 80573fc..d40d6fd 100644
--- a/drivers/power/supply/qcom/smb-reg.h
+++ b/drivers/power/supply/qcom/smb-reg.h
@@ -624,6 +624,7 @@
 
 #define TAPER_TIMER_SEL_CFG_REG			(USBIN_BASE + 0x64)
 #define TYPEC_SPARE_CFG_BIT			BIT(7)
+#define TYPEC_DRP_DFP_TIME_CFG_BIT		BIT(5)
 #define TAPER_TIMER_SEL_MASK			GENMASK(1, 0)
 
 #define USBIN_LOAD_CFG_REG			(USBIN_BASE + 0x65)
diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c
index a464a81..4b2e9c8 100644
--- a/drivers/power/supply/qcom/smb1351-charger.c
+++ b/drivers/power/supply/qcom/smb1351-charger.c
@@ -861,7 +861,7 @@
 	return (reg & CMD_OTG_EN_BIT) ? 1 : 0;
 }
 
-struct regulator_ops smb1351_chg_otg_reg_ops = {
+static struct regulator_ops smb1351_chg_otg_reg_ops = {
 	.enable		= smb1351_chg_otg_regulator_enable,
 	.disable	= smb1351_chg_otg_regulator_disable,
 	.is_enabled	= smb1351_chg_otg_regulator_is_enable,
diff --git a/drivers/power/supply/qcom/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c
index 4e1bb17..59f2466 100644
--- a/drivers/power/supply/qcom/smb1355-charger.c
+++ b/drivers/power/supply/qcom/smb1355-charger.c
@@ -62,10 +62,19 @@
 #define CHGR_BATTOV_CFG_REG			(CHGR_BASE + 0x70)
 #define BATTOV_SETTING_MASK			GENMASK(7, 0)
 
+#define CHGR_PRE_TO_FAST_THRESHOLD_CFG_REG	(CHGR_BASE + 0x74)
+#define PRE_TO_FAST_CHARGE_THRESHOLD_MASK	GENMASK(2, 0)
+
 #define POWER_MODE_HICCUP_CFG			(BATIF_BASE + 0x72)
 #define MAX_HICCUP_DUETO_BATDIS_MASK		GENMASK(5, 2)
 #define HICCUP_TIMEOUT_CFG_MASK			GENMASK(1, 0)
 
+#define BATIF_CFG_SMISC_BATID_REG		(BATIF_BASE + 0x73)
+#define CFG_SMISC_RBIAS_EXT_CTRL_BIT		BIT(2)
+
+#define BATIF_ENG_SCMISC_SPARE1_REG		(BATIF_BASE + 0xC2)
+#define EXT_BIAS_PIN_BIT			BIT(2)
+
 #define TEMP_COMP_STATUS_REG			(MISC_BASE + 0x07)
 #define SKIN_TEMP_RST_HOT_BIT			BIT(6)
 #define SKIN_TEMP_UB_HOT_BIT			BIT(5)
@@ -92,6 +101,14 @@
 #define BARK_WDOG_TIMEOUT_MASK			GENMASK(3, 2)
 #define BITE_WDOG_TIMEOUT_MASK			GENMASK(1, 0)
 
+#define MISC_THERMREG_SRC_CFG_REG		(MISC_BASE + 0x70)
+#define BYP_THERM_CHG_CURR_ADJUST_BIT		BIT(2)
+#define THERMREG_SKIN_CMP_SRC_EN_BIT		BIT(1)
+#define THERMREG_DIE_CMP_SRC_EN_BIT		BIT(0)
+
+#define MISC_CHGR_TRIM_OPTIONS_REG		(MISC_BASE + 0x55)
+#define CMD_RBIAS_EN_BIT			BIT(2)
+
 struct smb_chg_param {
 	const char	*name;
 	u16		reg;
@@ -134,11 +151,16 @@
 	struct iio_channel	*temp_max_chan;
 };
 
+struct smb_dt_props {
+	bool	disable_ctm;
+};
+
 struct smb1355 {
 	struct device		*dev;
 	char			*name;
 	struct regmap		*regmap;
 
+	struct smb_dt_props	dt;
 	struct smb_params	param;
 	struct smb_iio		iio;
 
@@ -146,6 +168,8 @@
 
 	struct power_supply	*parallel_psy;
 	struct pmic_revid_data	*pmic_rev_id;
+
+	int			c_health;
 };
 
 static bool is_secure(struct smb1355 *chip, int addr)
@@ -268,6 +292,38 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t smb1355_handle_temperature_change(int irq, void *data)
+{
+	struct smb1355 *chip = data;
+
+	if (chip->parallel_psy)
+		power_supply_changed(chip->parallel_psy);
+
+	return IRQ_HANDLED;
+}
+
+static int smb1355_determine_initial_status(struct smb1355 *chip)
+{
+	smb1355_handle_temperature_change(0, chip);
+	return 0;
+}
+
+static int smb1355_parse_dt(struct smb1355 *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	int rc = 0;
+
+	if (!node) {
+		pr_err("device tree node missing\n");
+		return -EINVAL;
+	}
+
+	chip->dt.disable_ctm =
+		of_property_read_bool(node, "qcom,disable-ctm");
+
+	return rc;
+}
+
 /*****************************
  * PARALLEL PSY REGISTRATION *
  *****************************/
@@ -434,7 +490,10 @@
 		val->intval = POWER_SUPPLY_PL_USBMID_USBMID;
 		break;
 	case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
-		val->intval = smb1355_get_prop_connector_health(chip);
+		if (chip->c_health == -EINVAL)
+			val->intval = smb1355_get_prop_connector_health(chip);
+		else
+			val->intval = chip->c_health;
 		break;
 	default:
 		pr_err_ratelimited("parallel psy get prop %d not supported\n",
@@ -497,6 +556,10 @@
 		rc = smb1355_set_charge_param(chip, &chip->param.fcc,
 						val->intval);
 		break;
+	case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
+		chip->c_health = val->intval;
+		power_supply_changed(chip->parallel_psy);
+		break;
 	default:
 		pr_debug("parallel power supply set prop %d not supported\n",
 			prop);
@@ -509,6 +572,13 @@
 static int smb1355_parallel_prop_is_writeable(struct power_supply *psy,
 					      enum power_supply_property prop)
 {
+	switch (prop) {
+	case POWER_SUPPLY_PROP_CONNECTOR_HEALTH:
+		return 1;
+	default:
+		break;
+	}
+
 	return 0;
 }
 
@@ -547,6 +617,92 @@
  * HARDWARE INITIALIZATION *
  ***************************/
 
+static int smb1355_tskin_sensor_config(struct smb1355 *chip)
+{
+	int rc;
+
+	if (chip->dt.disable_ctm) {
+		/*
+		 * the TSKIN sensor with external resistor needs a bias,
+		 * disable it here.
+		 */
+		rc = smb1355_masked_write(chip, BATIF_ENG_SCMISC_SPARE1_REG,
+					 EXT_BIAS_PIN_BIT, 0);
+		if (rc < 0) {
+			pr_err("Couldn't enable ext bias pin path rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = smb1355_masked_write(chip, BATIF_CFG_SMISC_BATID_REG,
+					CFG_SMISC_RBIAS_EXT_CTRL_BIT, 0);
+		if (rc < 0) {
+			pr_err("Couldn't set  BATIF_CFG_SMISC_BATID rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = smb1355_masked_write(chip, MISC_CHGR_TRIM_OPTIONS_REG,
+					CMD_RBIAS_EN_BIT, 0);
+		if (rc < 0) {
+			pr_err("Couldn't set MISC_CHGR_TRIM_OPTIONS rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		/* disable skin temperature comparator source */
+		rc = smb1355_masked_write(chip, MISC_THERMREG_SRC_CFG_REG,
+			THERMREG_SKIN_CMP_SRC_EN_BIT, 0);
+		if (rc < 0) {
+			pr_err("Couldn't set Skin temp comparator src rc=%d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		/*
+		 * the TSKIN sensor with external resistor needs a bias,
+		 * enable it here.
+		 */
+		rc = smb1355_masked_write(chip, BATIF_ENG_SCMISC_SPARE1_REG,
+					 EXT_BIAS_PIN_BIT, EXT_BIAS_PIN_BIT);
+		if (rc < 0) {
+			pr_err("Couldn't enable ext bias pin path rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = smb1355_masked_write(chip, BATIF_CFG_SMISC_BATID_REG,
+					CFG_SMISC_RBIAS_EXT_CTRL_BIT,
+					CFG_SMISC_RBIAS_EXT_CTRL_BIT);
+		if (rc < 0) {
+			pr_err("Couldn't set  BATIF_CFG_SMISC_BATID rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = smb1355_masked_write(chip, MISC_CHGR_TRIM_OPTIONS_REG,
+					CMD_RBIAS_EN_BIT,
+					CMD_RBIAS_EN_BIT);
+		if (rc < 0) {
+			pr_err("Couldn't set MISC_CHGR_TRIM_OPTIONS rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		/* Enable skin temperature comparator source */
+		rc = smb1355_masked_write(chip, MISC_THERMREG_SRC_CFG_REG,
+			THERMREG_SKIN_CMP_SRC_EN_BIT,
+			THERMREG_SKIN_CMP_SRC_EN_BIT);
+		if (rc < 0) {
+			pr_err("Couldn't set Skin temp comparator src rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
 static int smb1355_init_hw(struct smb1355 *chip)
 {
 	int rc;
@@ -589,7 +745,7 @@
 			HICCUP_TIMEOUT_CFG_MASK | MAX_HICCUP_DUETO_BATDIS_MASK,
 			0);
 	if (rc < 0) {
-		pr_err("Couldn't enable parallel current sensing rc=%d\n",
+		pr_err("Couldn't set HICCUP interval rc=%d\n",
 			rc);
 		return rc;
 	}
@@ -603,6 +759,34 @@
 		return rc;
 	}
 
+	/* set Pre-to-Fast Charging Threshold 2.6V */
+	rc = smb1355_masked_write(chip, CHGR_PRE_TO_FAST_THRESHOLD_CFG_REG,
+				 PRE_TO_FAST_CHARGE_THRESHOLD_MASK, 0);
+	if (rc < 0) {
+		pr_err("Couldn't set PRE_TO_FAST_CHARGE_THRESHOLD rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/*
+	 * Disable thermal Die temperature comparator source and hw mitigation
+	 * for skin/die
+	 */
+	rc = smb1355_masked_write(chip, MISC_THERMREG_SRC_CFG_REG,
+		THERMREG_DIE_CMP_SRC_EN_BIT | BYP_THERM_CHG_CURR_ADJUST_BIT,
+		BYP_THERM_CHG_CURR_ADJUST_BIT);
+	if (rc < 0) {
+		pr_err("Couldn't set Skin temperature comparator src rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = smb1355_tskin_sensor_config(chip);
+	if (rc < 0) {
+		pr_err("Couldn't configure tskin regs rc=%d\n", rc);
+		return rc;
+	}
+
 	return 0;
 }
 
@@ -613,12 +797,17 @@
 	[0] = {
 		.name		= "wdog-bark",
 		.handler	= smb1355_handle_wdog_bark,
+		.wake		= true,
 	},
 	[1] = {
 		.name		= "chg-state-change",
 		.handler	= smb1355_handle_chg_state_change,
 		.wake		= true,
 	},
+	[2] = {
+		.name		= "temperature-change",
+		.handler	= smb1355_handle_temperature_change,
+	},
 };
 
 static int smb1355_get_irq_index_byname(const char *irq_name)
@@ -713,6 +902,7 @@
 
 	chip->dev = &pdev->dev;
 	chip->param = v1_params;
+	chip->c_health = -EINVAL;
 	chip->name = "smb1355";
 	mutex_init(&chip->write_lock);
 
@@ -730,6 +920,12 @@
 
 	platform_set_drvdata(pdev, chip);
 
+	rc = smb1355_parse_dt(chip);
+	if (rc < 0) {
+		pr_err("Couldn't parse device tree rc=%d\n", rc);
+		goto cleanup;
+	}
+
 	rc = smb1355_init_hw(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize hardware rc=%d\n", rc);
@@ -742,6 +938,13 @@
 		goto cleanup;
 	}
 
+	rc = smb1355_determine_initial_status(chip);
+	if (rc < 0) {
+		pr_err("Couldn't determine initial status rc=%d\n",
+			rc);
+		goto cleanup;
+	}
+
 	rc = smb1355_request_interrupts(chip);
 	if (rc < 0) {
 		pr_err("Couldn't request interrupts rc=%d\n", rc);
@@ -762,14 +965,26 @@
 	return 0;
 }
 
+static void smb1355_shutdown(struct platform_device *pdev)
+{
+	struct smb1355 *chip = platform_get_drvdata(pdev);
+	int rc;
+
+	/* disable parallel charging path */
+	rc = smb1355_set_parallel_charging(chip, true);
+	if (rc < 0)
+		pr_err("Couldn't disable parallel path rc=%d\n", rc);
+}
+
 static struct platform_driver smb1355_driver = {
 	.driver	= {
 		.name		= "qcom,smb1355-charger",
 		.owner		= THIS_MODULE,
 		.of_match_table	= match_table,
 	},
-	.probe	= smb1355_probe,
-	.remove	= smb1355_remove,
+	.probe		= smb1355_probe,
+	.remove		= smb1355_remove,
+	.shutdown	= smb1355_shutdown,
 };
 module_platform_driver(smb1355_driver);
 
diff --git a/drivers/power/supply/qcom/smb135x-charger.c b/drivers/power/supply/qcom/smb135x-charger.c
index 803dd6e..edc0998 100644
--- a/drivers/power/supply/qcom/smb135x-charger.c
+++ b/drivers/power/supply/qcom/smb135x-charger.c
@@ -2218,7 +2218,7 @@
 	return  (reg & OTG_EN) ? 1 : 0;
 }
 
-struct regulator_ops smb135x_chg_otg_reg_ops = {
+static struct regulator_ops smb135x_chg_otg_reg_ops = {
 	.enable		= smb135x_chg_otg_regulator_enable,
 	.disable	= smb135x_chg_otg_regulator_disable,
 	.is_enabled	= smb135x_chg_otg_regulator_is_enable,
diff --git a/drivers/power/supply/qcom/smb138x-charger.c b/drivers/power/supply/qcom/smb138x-charger.c
index 911dd69..28c3512 100644
--- a/drivers/power/supply/qcom/smb138x-charger.c
+++ b/drivers/power/supply/qcom/smb138x-charger.c
@@ -111,7 +111,7 @@
 	debug_mask, __debug_mask, int, 0600
 );
 
-irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data)
+static irqreturn_t smb138x_handle_slave_chg_state_change(int irq, void *data)
 {
 	struct smb_irq_data *irq_data = data;
 	struct smb138x *chip = irq_data->parent_data;
@@ -170,6 +170,9 @@
 	chip->dt.suspend_input = of_property_read_bool(node,
 				"qcom,suspend-input");
 
+	chg->use_extcon = of_property_read_bool(node,
+				"qcom,use-extcon");
+
 	rc = of_property_read_u32(node,
 				"qcom,fcc-max-ua", &chip->dt.fcc_ua);
 	if (rc < 0)
@@ -727,7 +730,7 @@
  * VBUS REGULATOR REGISTRATION *
  ******************************/
 
-struct regulator_ops smb138x_vbus_reg_ops = {
+static struct regulator_ops smb138x_vbus_reg_ops = {
 	.enable		= smblib_vbus_regulator_enable,
 	.disable	= smblib_vbus_regulator_disable,
 	.is_enabled	= smblib_vbus_regulator_is_enabled,
@@ -769,7 +772,7 @@
  * VCONN REGULATOR REGISTRATION *
  ******************************/
 
-struct regulator_ops smb138x_vconn_reg_ops = {
+static struct regulator_ops smb138x_vconn_reg_ops = {
 	.enable		= smblib_vconn_regulator_enable,
 	.disable	= smblib_vconn_regulator_disable,
 	.is_enabled	= smblib_vconn_regulator_is_enabled,
@@ -1405,55 +1408,95 @@
 	rc = smb138x_parse_dt(chip);
 	if (rc < 0) {
 		pr_err("Couldn't parse device tree rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_vbus_regulator(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize vbus regulator rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_vconn_regulator(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize vconn regulator rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
+	}
+
+	if (chg->use_extcon) {
+		/* extcon registration */
+		chg->extcon = devm_extcon_dev_allocate(chg->dev,
+							smblib_extcon_cable);
+		if (IS_ERR(chg->extcon)) {
+			rc = PTR_ERR(chg->extcon);
+			dev_err(chg->dev, "failed to allocate extcon device rc=%d\n",
+					rc);
+			goto cleanup;
+		}
+
+		chg->extcon->mutually_exclusive = smblib_extcon_exclusive;
+		rc = devm_extcon_dev_register(chg->dev, chg->extcon);
+		if (rc < 0) {
+			dev_err(chg->dev, "failed to register extcon device rc=%d\n",
+						rc);
+			goto cleanup;
+		}
+
+		/* Support reporting polarity and speed via properties */
+		rc = extcon_set_property_capability(chg->extcon,
+				EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB, EXTCON_PROP_USB_SS);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB_HOST,
+				EXTCON_PROP_USB_TYPEC_POLARITY);
+		rc |= extcon_set_property_capability(chg->extcon,
+				EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
+		if (rc < 0) {
+			dev_err(chg->dev,
+				"failed to configure extcon capabilities\n");
+			goto cleanup;
+		}
 	}
 
 	rc = smb138x_init_usb_psy(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize usb psy rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_batt_psy(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize batt psy rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_init_hw(chip);
 	if (rc < 0) {
 		pr_err("Couldn't initialize hardware rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_determine_initial_status(chip);
 	if (rc < 0) {
 		pr_err("Couldn't determine initial status rc=%d\n",
 			rc);
-		return rc;
+		goto cleanup;
 	}
 
 	rc = smb138x_request_interrupts(chip);
 	if (rc < 0) {
 		pr_err("Couldn't request interrupts rc=%d\n", rc);
-		return rc;
+		goto cleanup;
 	}
 
 	return rc;
+
+cleanup:
+	smblib_deinit(chg);
+	return rc;
 }
 
 static int smb138x_slave_probe(struct smb138x *chip)
diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c
index 06ecc7e..a75cbbb 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.c
+++ b/drivers/power/supply/qcom/step-chg-jeita.c
@@ -13,6 +13,8 @@
 
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_batterydata.h>
 #include <linux/power_supply.h>
 #include <linux/slab.h>
 #include <linux/pmic-voter.h>
@@ -56,19 +58,32 @@
 };
 
 struct step_chg_info {
+	struct device		*dev;
 	ktime_t			step_last_update_time;
 	ktime_t			jeita_last_update_time;
 	bool			step_chg_enable;
 	bool			sw_jeita_enable;
+	bool			config_is_read;
+	bool			step_chg_cfg_valid;
+	bool			sw_jeita_cfg_valid;
+	bool			soc_based_step_chg;
+	bool			batt_missing;
 	int			jeita_fcc_index;
 	int			jeita_fv_index;
 	int			step_index;
+	int			get_config_retry_count;
+
+	struct step_chg_cfg	*step_chg_config;
+	struct jeita_fcc_cfg	*jeita_fcc_config;
+	struct jeita_fv_cfg	*jeita_fv_config;
 
 	struct votable		*fcc_votable;
 	struct votable		*fv_votable;
 	struct wakeup_source	*step_chg_ws;
 	struct power_supply	*batt_psy;
+	struct power_supply	*bms_psy;
 	struct delayed_work	status_change_work;
+	struct delayed_work	get_config_work;
 	struct notifier_block	nb;
 };
 
@@ -76,69 +91,10 @@
 
 #define STEP_CHG_HYSTERISIS_DELAY_US		5000000 /* 5 secs */
 
-/*
- * Step Charging Configuration
- * Update the table based on the battery profile
- * Supports VBATT and SOC based source
- * range data must be in increasing ranges and shouldn't overlap
- */
-static struct step_chg_cfg step_chg_config = {
-	.psy_prop	= POWER_SUPPLY_PROP_VOLTAGE_NOW,
-	.prop_name	= "VBATT",
-	.hysteresis	= 100000, /* 100mV */
-	.fcc_cfg	= {
-		/* VBAT_LOW	VBAT_HIGH	FCC */
-		{3600000,	4000000,	3000000},
-		{4001000,	4200000,	2800000},
-		{4201000,	4400000,	2000000},
-	},
-	/*
-	 *	SOC STEP-CHG configuration example.
-	 *
-	 *	.psy_prop = POWER_SUPPLY_PROP_CAPACITY,
-	 *	.prop_name = "SOC",
-	 *	.fcc_cfg	= {
-	 *		//SOC_LOW	SOC_HIGH	FCC
-	 *		{20,		70,		3000000},
-	 *		{70,		90,		2750000},
-	 *		{90,		100,		2500000},
-	 *	},
-	 */
-};
-
-/*
- * Jeita Charging Configuration
- * Update the table based on the battery profile
- * Please ensure that the TEMP ranges are programmed in the hw so that
- * an interrupt is issued and a consequent psy changed will cause us to
- * react immediately.
- * range data must be in increasing ranges and shouldn't overlap.
- * Gaps are okay
- */
-static struct jeita_fcc_cfg jeita_fcc_config = {
-	.psy_prop	= POWER_SUPPLY_PROP_TEMP,
-	.prop_name	= "BATT_TEMP",
-	.hysteresis	= 10, /* 1degC hysteresis */
-	.fcc_cfg	= {
-		/* TEMP_LOW	TEMP_HIGH	FCC */
-		{0,		100,		600000},
-		{101,		200,		2000000},
-		{201,		450,		3000000},
-		{451,		550,		600000},
-	},
-};
-
-static struct jeita_fv_cfg jeita_fv_config = {
-	.psy_prop	= POWER_SUPPLY_PROP_TEMP,
-	.prop_name	= "BATT_TEMP",
-	.hysteresis	= 10, /* 1degC hysteresis */
-	.fv_cfg		= {
-		/* TEMP_LOW	TEMP_HIGH	FCC */
-		{0,		100,		4200000},
-		{101,		450,		4400000},
-		{451,		550,		4200000},
-	},
-};
+#define BATT_HOT_DECIDEGREE_MAX			600
+#define GET_CONFIG_DELAY_MS		2000
+#define GET_CONFIG_RETRY_COUNT		50
+#define WAIT_BATT_ID_READY_MS		200
 
 static bool is_batt_available(struct step_chg_info *chip)
 {
@@ -151,6 +107,240 @@
 	return true;
 }
 
+static bool is_bms_available(struct step_chg_info *chip)
+{
+	if (!chip->bms_psy)
+		chip->bms_psy = power_supply_get_by_name("bms");
+
+	if (!chip->bms_psy)
+		return false;
+
+	return true;
+}
+
+static int read_range_data_from_node(struct device_node *node,
+		const char *prop_str, struct range_data *ranges,
+		u32 max_threshold, u32 max_value)
+{
+	int rc = 0, i, length, per_tuple_length, tuples;
+
+	rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32));
+	if (rc < 0) {
+		pr_err("Count %s failed, rc=%d\n", prop_str, rc);
+		return rc;
+	}
+
+	length = rc;
+	per_tuple_length = sizeof(struct range_data) / sizeof(u32);
+	if (length % per_tuple_length) {
+		pr_err("%s length (%d) should be multiple of %d\n",
+				prop_str, length, per_tuple_length);
+		return -EINVAL;
+	}
+	tuples = length / per_tuple_length;
+
+	if (tuples > MAX_STEP_CHG_ENTRIES) {
+		pr_err("too many entries(%d), only %d allowed\n",
+				tuples, MAX_STEP_CHG_ENTRIES);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_array(node, prop_str,
+			(u32 *)ranges, length);
+	if (rc) {
+		pr_err("Read %s failed, rc=%d", prop_str, rc);
+		return rc;
+	}
+
+	for (i = 0; i < tuples; i++) {
+		if (ranges[i].low_threshold >
+				ranges[i].high_threshold) {
+			pr_err("%s thresholds should be in ascendant ranges\n",
+						prop_str);
+			rc = -EINVAL;
+			goto clean;
+		}
+
+		if (i != 0) {
+			if (ranges[i - 1].high_threshold >
+					ranges[i].low_threshold) {
+				pr_err("%s thresholds should be in ascendant ranges\n",
+							prop_str);
+				rc = -EINVAL;
+				goto clean;
+			}
+		}
+
+		if (ranges[i].low_threshold > max_threshold)
+			ranges[i].low_threshold = max_threshold;
+		if (ranges[i].high_threshold > max_threshold)
+			ranges[i].high_threshold = max_threshold;
+		if (ranges[i].value > max_value)
+			ranges[i].value = max_value;
+	}
+
+	return rc;
+clean:
+	memset(ranges, 0, tuples * sizeof(struct range_data));
+	return rc;
+}
+
+static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip)
+{
+	struct device_node *batt_node, *profile_node;
+	u32 max_fv_uv, max_fcc_ma;
+	const char *batt_type_str;
+	const __be32 *handle;
+	int batt_id_ohms, rc;
+	union power_supply_propval prop = {0, };
+
+	handle = of_get_property(chip->dev->of_node,
+			"qcom,battery-data", NULL);
+	if (!handle) {
+		pr_debug("ignore getting sw-jeita/step charging settings from profile\n");
+		return 0;
+	}
+
+	batt_node = of_find_node_by_phandle(be32_to_cpup(handle));
+	if (!batt_node) {
+		pr_err("Get battery data node failed\n");
+		return -EINVAL;
+	}
+
+	if (!is_bms_available(chip))
+		return -ENODEV;
+
+	power_supply_get_property(chip->bms_psy,
+			POWER_SUPPLY_PROP_RESISTANCE_ID, &prop);
+	batt_id_ohms = prop.intval;
+
+	/* bms_psy has not yet read the batt_id */
+	if (batt_id_ohms < 0)
+		return -EBUSY;
+
+	profile_node = of_batterydata_get_best_profile(batt_node,
+					batt_id_ohms / 1000, NULL);
+	if (IS_ERR(profile_node))
+		return PTR_ERR(profile_node);
+
+	if (!profile_node) {
+		pr_err("Couldn't find profile\n");
+		return -ENODATA;
+	}
+
+	rc = of_property_read_string(profile_node, "qcom,battery-type",
+					&batt_type_str);
+	if (rc < 0) {
+		pr_err("battery type unavailable, rc:%d\n", rc);
+		return rc;
+	}
+	pr_debug("battery: %s detected, getting sw-jeita/step charging settings\n",
+					batt_type_str);
+
+	rc = of_property_read_u32(profile_node, "qcom,max-voltage-uv",
+					&max_fv_uv);
+	if (rc < 0) {
+		pr_err("max-voltage_uv reading failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(profile_node, "qcom,fastchg-current-ma",
+					&max_fcc_ma);
+	if (rc < 0) {
+		pr_err("max-fastchg-current-ma reading failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	chip->soc_based_step_chg =
+		of_property_read_bool(profile_node, "qcom,soc-based-step-chg");
+	if (chip->soc_based_step_chg) {
+		chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_CAPACITY,
+		chip->step_chg_config->prop_name = "SOC";
+		chip->step_chg_config->hysteresis = 0;
+	}
+
+	chip->step_chg_cfg_valid = true;
+	rc = read_range_data_from_node(profile_node,
+			"qcom,step-chg-ranges",
+			chip->step_chg_config->fcc_cfg,
+			chip->soc_based_step_chg ? 100 : max_fv_uv,
+			max_fcc_ma * 1000);
+	if (rc < 0) {
+		pr_debug("Read qcom,step-chg-ranges failed from battery profile, rc=%d\n",
+					rc);
+		chip->step_chg_cfg_valid = false;
+	}
+
+	chip->sw_jeita_cfg_valid = true;
+	rc = read_range_data_from_node(profile_node,
+			"qcom,jeita-fcc-ranges",
+			chip->jeita_fcc_config->fcc_cfg,
+			BATT_HOT_DECIDEGREE_MAX, max_fcc_ma * 1000);
+	if (rc < 0) {
+		pr_debug("Read qcom,jeita-fcc-ranges failed from battery profile, rc=%d\n",
+					rc);
+		chip->sw_jeita_cfg_valid = false;
+	}
+
+	rc = read_range_data_from_node(profile_node,
+			"qcom,jeita-fv-ranges",
+			chip->jeita_fv_config->fv_cfg,
+			BATT_HOT_DECIDEGREE_MAX, max_fv_uv);
+	if (rc < 0) {
+		pr_debug("Read qcom,jeita-fv-ranges failed from battery profile, rc=%d\n",
+					rc);
+		chip->sw_jeita_cfg_valid = false;
+	}
+
+	return rc;
+}
+
+static void get_config_work(struct work_struct *work)
+{
+	struct step_chg_info *chip = container_of(work,
+			struct step_chg_info, get_config_work.work);
+	int i, rc;
+
+	chip->config_is_read = false;
+	rc = get_step_chg_jeita_setting_from_profile(chip);
+
+	if (rc < 0) {
+		if (rc == -ENODEV || rc == -EBUSY) {
+			if (chip->get_config_retry_count++
+					< GET_CONFIG_RETRY_COUNT) {
+				pr_debug("bms_psy is not ready, retry: %d\n",
+						chip->get_config_retry_count);
+				goto reschedule;
+			}
+		}
+	}
+
+	chip->config_is_read = true;
+
+	for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
+		pr_debug("step-chg-cfg: %duV(SoC) ~ %duV(SoC), %duA\n",
+			chip->step_chg_config->fcc_cfg[i].low_threshold,
+			chip->step_chg_config->fcc_cfg[i].high_threshold,
+			chip->step_chg_config->fcc_cfg[i].value);
+	for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
+		pr_debug("jeita-fcc-cfg: %ddecidegree ~ %ddecidegre, %duA\n",
+			chip->jeita_fcc_config->fcc_cfg[i].low_threshold,
+			chip->jeita_fcc_config->fcc_cfg[i].high_threshold,
+			chip->jeita_fcc_config->fcc_cfg[i].value);
+	for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
+		pr_debug("jeita-fv-cfg: %ddecidegree ~ %ddecidegre, %duV\n",
+			chip->jeita_fv_config->fv_cfg[i].low_threshold,
+			chip->jeita_fv_config->fv_cfg[i].high_threshold,
+			chip->jeita_fv_config->fv_cfg[i].value);
+
+	return;
+
+reschedule:
+	schedule_delayed_work(&chip->get_config_work,
+			msecs_to_jiffies(GET_CONFIG_DELAY_MS));
+
+}
+
 static int get_val(struct range_data *range, int hysteresis, int current_index,
 		int threshold,
 		int *new_index, int *val)
@@ -220,21 +410,22 @@
 	else
 		chip->step_chg_enable = pval.intval;
 
-	if (!chip->step_chg_enable) {
+	if (!chip->step_chg_enable || !chip->step_chg_cfg_valid) {
 		if (chip->fcc_votable)
 			vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
 		goto update_time;
 	}
 
 	rc = power_supply_get_property(chip->batt_psy,
-				step_chg_config.psy_prop, &pval);
+			chip->step_chg_config->psy_prop, &pval);
 	if (rc < 0) {
 		pr_err("Couldn't read %s property rc=%d\n",
-				step_chg_config.prop_name, rc);
+			chip->step_chg_config->prop_name, rc);
 		return rc;
 	}
 
-	rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis,
+	rc = get_val(chip->step_chg_config->fcc_cfg,
+			chip->step_chg_config->hysteresis,
 			chip->step_index,
 			pval.intval,
 			&chip->step_index,
@@ -254,7 +445,7 @@
 	vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);
 
 	pr_debug("%s = %d Step-FCC = %duA\n",
-		step_chg_config.prop_name, pval.intval, fcc_ua);
+		chip->step_chg_config->prop_name, pval.intval, fcc_ua);
 
 update_time:
 	chip->step_last_update_time = ktime_get();
@@ -278,7 +469,7 @@
 	else
 		chip->sw_jeita_enable = pval.intval;
 
-	if (!chip->sw_jeita_enable) {
+	if (!chip->sw_jeita_enable || !chip->sw_jeita_cfg_valid) {
 		if (chip->fcc_votable)
 			vote(chip->fcc_votable, JEITA_VOTER, false, 0);
 		if (chip->fv_votable)
@@ -291,14 +482,15 @@
 		goto reschedule;
 
 	rc = power_supply_get_property(chip->batt_psy,
-				jeita_fcc_config.psy_prop, &pval);
+			chip->jeita_fcc_config->psy_prop, &pval);
 	if (rc < 0) {
 		pr_err("Couldn't read %s property rc=%d\n",
-				step_chg_config.prop_name, rc);
+				chip->jeita_fcc_config->prop_name, rc);
 		return rc;
 	}
 
-	rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis,
+	rc = get_val(chip->jeita_fcc_config->fcc_cfg,
+			chip->jeita_fcc_config->hysteresis,
 			chip->jeita_fcc_index,
 			pval.intval,
 			&chip->jeita_fcc_index,
@@ -318,7 +510,8 @@
 
 	vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);
 
-	rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis,
+	rc = get_val(chip->jeita_fv_config->fv_cfg,
+			chip->jeita_fv_config->hysteresis,
 			chip->jeita_fv_index,
 			pval.intval,
 			&chip->jeita_fv_index,
@@ -337,7 +530,7 @@
 	vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);
 
 	pr_debug("%s = %d FCC = %duA FV = %duV\n",
-		step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv);
+		chip->jeita_fcc_config->prop_name, pval.intval, fcc_ua, fv_uv);
 
 update_time:
 	chip->jeita_last_update_time = ktime_get();
@@ -348,6 +541,39 @@
 	return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
 }
 
+static int handle_battery_insertion(struct step_chg_info *chip)
+{
+	int rc;
+	union power_supply_propval pval = {0, };
+
+	rc = power_supply_get_property(chip->batt_psy,
+			POWER_SUPPLY_PROP_PRESENT, &pval);
+	if (rc < 0) {
+		pr_err("Get battery present status failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (chip->batt_missing != (!pval.intval)) {
+		chip->batt_missing = !pval.intval;
+		pr_debug("battery %s detected\n",
+				chip->batt_missing ? "removal" : "insertion");
+		if (chip->batt_missing) {
+			chip->step_chg_cfg_valid = false;
+			chip->sw_jeita_cfg_valid = false;
+			chip->get_config_retry_count = 0;
+		} else {
+			/*
+			 * Get config for the new inserted battery, delay
+			 * to make sure BMS has read out the batt_id.
+			 */
+			schedule_delayed_work(&chip->get_config_work,
+				msecs_to_jiffies(WAIT_BATT_ID_READY_MS));
+		}
+	}
+
+	return rc;
+}
+
 static void status_change_work(struct work_struct *work)
 {
 	struct step_chg_info *chip = container_of(work,
@@ -360,6 +586,7 @@
 	if (!is_batt_available(chip))
 		return;
 
+	handle_battery_insertion(chip);
 	/* skip elapsed_us debounce for handling battery temperature */
 	rc = handle_jeita(chip);
 	if (rc > 0)
@@ -395,6 +622,13 @@
 		schedule_delayed_work(&chip->status_change_work, 0);
 	}
 
+	if ((strcmp(psy->desc->name, "bms") == 0)) {
+		if (chip->bms_psy == NULL)
+			chip->bms_psy = psy;
+		if (!chip->config_is_read)
+			schedule_delayed_work(&chip->get_config_work, 0);
+	}
+
 	return NOTIFY_OK;
 }
 
@@ -412,7 +646,8 @@
 	return 0;
 }
 
-int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable)
+int qcom_step_chg_init(struct device *dev,
+		bool step_chg_enable, bool sw_jeita_enable)
 {
 	int rc;
 	struct step_chg_info *chip;
@@ -422,48 +657,46 @@
 		return -EINVAL;
 	}
 
-	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
 
 	chip->step_chg_ws = wakeup_source_register("qcom-step-chg");
-	if (!chip->step_chg_ws) {
-		rc = -EINVAL;
-		goto cleanup;
-	}
+	if (!chip->step_chg_ws)
+		return -EINVAL;
 
+	chip->dev = dev;
 	chip->step_chg_enable = step_chg_enable;
 	chip->sw_jeita_enable = sw_jeita_enable;
-
 	chip->step_index = -EINVAL;
 	chip->jeita_fcc_index = -EINVAL;
 	chip->jeita_fv_index = -EINVAL;
 
-	if (step_chg_enable && (!step_chg_config.psy_prop ||
-				!step_chg_config.prop_name)) {
-		/* fail if step-chg configuration is invalid */
-		pr_err("Step-chg configuration not defined - fail\n");
-		rc = -ENODATA;
-		goto release_wakeup_source;
-	}
+	chip->step_chg_config = devm_kzalloc(dev,
+			sizeof(struct step_chg_cfg), GFP_KERNEL);
+	if (!chip->step_chg_config)
+		return -ENOMEM;
 
-	if (sw_jeita_enable && (!jeita_fcc_config.psy_prop ||
-				!jeita_fcc_config.prop_name)) {
-		/* fail if step-chg configuration is invalid */
-		pr_err("Jeita TEMP configuration not defined - fail\n");
-		rc = -ENODATA;
-		goto release_wakeup_source;
-	}
+	chip->step_chg_config->psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW;
+	chip->step_chg_config->prop_name = "VBATT";
+	chip->step_chg_config->hysteresis = 100000;
 
-	if (sw_jeita_enable && (!jeita_fv_config.psy_prop ||
-				!jeita_fv_config.prop_name)) {
-		/* fail if step-chg configuration is invalid */
-		pr_err("Jeita TEMP configuration not defined - fail\n");
-		rc = -ENODATA;
-		goto release_wakeup_source;
-	}
+	chip->jeita_fcc_config = devm_kzalloc(dev,
+			sizeof(struct jeita_fcc_cfg), GFP_KERNEL);
+	chip->jeita_fv_config = devm_kzalloc(dev,
+			sizeof(struct jeita_fv_cfg), GFP_KERNEL);
+	if (!chip->jeita_fcc_config || !chip->jeita_fv_config)
+		return -ENOMEM;
+
+	chip->jeita_fcc_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
+	chip->jeita_fcc_config->prop_name = "BATT_TEMP";
+	chip->jeita_fcc_config->hysteresis = 10;
+	chip->jeita_fv_config->psy_prop = POWER_SUPPLY_PROP_TEMP;
+	chip->jeita_fv_config->prop_name = "BATT_TEMP";
+	chip->jeita_fv_config->hysteresis = 10;
 
 	INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);
+	INIT_DELAYED_WORK(&chip->get_config_work, get_config_work);
 
 	rc = step_chg_register_notifier(chip);
 	if (rc < 0) {
@@ -471,18 +704,15 @@
 		goto release_wakeup_source;
 	}
 
-	the_chip = chip;
+	schedule_delayed_work(&chip->get_config_work,
+			msecs_to_jiffies(GET_CONFIG_DELAY_MS));
 
-	if (step_chg_enable)
-		pr_info("Step charging enabled. Using %s source\n",
-				step_chg_config.prop_name);
+	the_chip = chip;
 
 	return 0;
 
 release_wakeup_source:
 	wakeup_source_unregister(chip->step_chg_ws);
-cleanup:
-	kfree(chip);
 	return rc;
 }
 
@@ -494,8 +724,8 @@
 		return;
 
 	cancel_delayed_work_sync(&chip->status_change_work);
+	cancel_delayed_work_sync(&chip->get_config_work);
 	power_supply_unreg_notifier(&chip->nb);
 	wakeup_source_unregister(chip->step_chg_ws);
 	the_chip = NULL;
-	kfree(chip);
 }
diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h
index 5bb2b99..2404b86 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.h
+++ b/drivers/power/supply/qcom/step-chg-jeita.h
@@ -12,6 +12,7 @@
 
 #ifndef __STEP_CHG_H__
 #define __STEP_CHG_H__
-int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable);
+int qcom_step_chg_init(struct device *dev,
+		bool step_chg_enable, bool sw_jeita_enable);
 void qcom_step_chg_deinit(void);
 #endif /* __STEP_CHG_H__ */
diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c
index 86f08d2..e7267de 100644
--- a/drivers/pwm/pwm-qpnp.c
+++ b/drivers/pwm/pwm-qpnp.c
@@ -317,6 +317,7 @@
 	struct pwm_period_config	period;
 	int				supported_sizes;
 	int				force_pwm_size;
+	bool				update_period;
 };
 
 /* Public facing structure */
@@ -327,6 +328,7 @@
 	bool			enabled;
 	struct _qpnp_pwm_config	pwm_config;
 	struct	qpnp_lpg_config	lpg_config;
+	enum pm_pwm_mode	pwm_mode;
 	spinlock_t		lpg_lock;
 	enum qpnp_lpg_revision	revision;
 	u8			sub_type;
@@ -1211,28 +1213,32 @@
 	rc = qpnp_lpg_save_pwm_value(chip);
 	if (rc)
 		goto out;
-	rc = qpnp_lpg_configure_pwm(chip);
-	if (rc)
-		goto out;
-	rc = qpnp_configure_pwm_control(chip);
-	if (rc)
-		goto out;
 
-	if (!rc && chip->enabled) {
-		rc = qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_ENABLE);
-		if (rc) {
-			pr_err("Error in configuring pwm state, rc=%d\n", rc);
-			return rc;
-		}
+	if (pwm_config->update_period) {
+		rc = qpnp_lpg_configure_pwm(chip);
+		if (rc)
+			goto out;
+		rc = qpnp_configure_pwm_control(chip);
+		if (rc)
+			goto out;
+		if (!rc && chip->enabled) {
+			rc = qpnp_lpg_configure_pwm_state(chip,
+					QPNP_PWM_ENABLE);
+			if (rc) {
+				pr_err("Error in configuring pwm state, rc=%d\n",
+						rc);
+				return rc;
+			}
 
-		/* Enable the glitch removal after PWM is enabled */
-		rc = qpnp_lpg_glitch_removal(chip, true);
-		if (rc) {
-			pr_err("Error in enabling glitch control, rc=%d\n", rc);
-			return rc;
+			/* Enable the glitch removal after PWM is enabled */
+			rc = qpnp_lpg_glitch_removal(chip, true);
+			if (rc) {
+				pr_err("Error in enabling glitch control, rc=%d\n",
+						rc);
+				return rc;
+			}
 		}
 	}
-
 	pr_debug("duty/period=%u/%u %s: pwm_value=%d (of %d)\n",
 		 (unsigned int)duty_value, (unsigned int)period_value,
 		 (tm_lvl == LVL_USEC) ? "usec" : "nsec",
@@ -1309,12 +1315,10 @@
 	return rc;
 }
 
+/* lpg_lock should be held while calling _pwm_enable() */
 static int _pwm_enable(struct qpnp_pwm_chip *chip)
 {
 	int rc = 0;
-	unsigned long flags;
-
-	spin_lock_irqsave(&chip->lpg_lock, flags);
 
 	if (QPNP_IS_PWM_CONFIG_SELECTED(
 		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
@@ -1327,8 +1331,21 @@
 	if (!rc)
 		chip->enabled = true;
 
-	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+	return rc;
+}
 
+/* lpg_lock should be held while calling _pwm_change_mode() */
+static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode)
+{
+	int rc;
+
+	if (mode == PM_PWM_MODE_LPG)
+		rc = qpnp_configure_lpg_control(chip);
+	else
+		rc = qpnp_configure_pwm_control(chip);
+
+	if (rc)
+		pr_err("Failed to change the mode\n");
 	return rc;
 }
 
@@ -1375,12 +1392,14 @@
 
 	spin_lock_irqsave(&chip->lpg_lock, flags);
 
+	chip->pwm_config.update_period = false;
 	if (prev_period_us > INT_MAX / NSEC_PER_USEC ||
 			prev_period_us * NSEC_PER_USEC != period_ns) {
 		qpnp_lpg_calc_period(LVL_NSEC, period_ns, chip);
 		qpnp_lpg_save_period(chip);
 		pwm->state.period = period_ns;
 		chip->pwm_config.pwm_period = period_ns / NSEC_PER_USEC;
+		chip->pwm_config.update_period = true;
 	}
 
 	rc = _pwm_config(chip, LVL_NSEC, duty_ns, period_ns);
@@ -1403,11 +1422,15 @@
 {
 	int rc;
 	struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+	unsigned long flags;
 
+	spin_lock_irqsave(&chip->lpg_lock, flags);
 	rc = _pwm_enable(chip);
 	if (rc)
 		pr_err("Failed to enable PWM channel: %d\n", chip->channel_id);
 
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
 	return rc;
 }
 
@@ -1445,20 +1468,6 @@
 					chip->channel_id);
 }
 
-static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode)
-{
-	int rc;
-
-	if (mode)
-		rc = qpnp_configure_lpg_control(chip);
-	else
-		rc = qpnp_configure_pwm_control(chip);
-
-	if (rc)
-		pr_err("Failed to change the mode\n");
-	return rc;
-}
-
 /**
  * pwm_change_mode - Change the PWM mode configuration
  * @pwm: the PWM device
@@ -1466,7 +1475,7 @@
  */
 int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
 {
-	int rc;
+	int rc = 0;
 	unsigned long flags;
 	struct qpnp_pwm_chip *chip;
 
@@ -1483,7 +1492,22 @@
 	chip = qpnp_pwm_from_pwm_dev(pwm);
 
 	spin_lock_irqsave(&chip->lpg_lock, flags);
-	rc = _pwm_change_mode(chip, mode);
+	if (chip->pwm_mode != mode) {
+		rc = _pwm_change_mode(chip, mode);
+		if (rc) {
+			pr_err("Failed to change mode: %d, rc=%d\n", mode, rc);
+			goto unlock;
+		}
+		chip->pwm_mode = mode;
+		if (chip->enabled) {
+			rc = _pwm_enable(chip);
+			if (rc) {
+				pr_err("Failed to enable PWM, rc=%d\n", rc);
+				goto unlock;
+			}
+		}
+	}
+unlock:
 	spin_unlock_irqrestore(&chip->lpg_lock, flags);
 
 	return rc;
@@ -1619,6 +1643,7 @@
 
 	spin_lock_irqsave(&chip->lpg_lock, flags);
 
+	chip->pwm_config.update_period = false;
 	if (chip->pwm_config.pwm_period != period_us) {
 		qpnp_lpg_calc_period(LVL_USEC, period_us, chip);
 		qpnp_lpg_save_period(chip);
@@ -1629,6 +1654,7 @@
 		else
 			pwm->state.period
 				= (unsigned int)period_us * NSEC_PER_USEC;
+		chip->pwm_config.update_period = true;
 	}
 
 	rc = _pwm_config(chip, LVL_USEC, duty_us, period_us);
@@ -1735,6 +1761,7 @@
 	qpnp_lpg_calc_period(LVL_USEC, period, chip);
 	qpnp_lpg_save_period(chip);
 	chip->pwm_config.pwm_period = period;
+	chip->pwm_config.update_period = true;
 
 	rc = _pwm_config(chip, LVL_USEC, chip->pwm_config.pwm_duty, period);
 
@@ -1885,7 +1912,7 @@
 static int qpnp_parse_dt_config(struct platform_device *pdev,
 					struct qpnp_pwm_chip *chip)
 {
-	int			rc, enable, lut_entry_size, list_size, i;
+	int			rc, mode, lut_entry_size, list_size, i;
 	const char		*label;
 	const __be32		*prop;
 	u32			size;
@@ -2069,18 +2096,20 @@
 		}
 	}
 
-	rc = of_property_read_u32(of_node, "qcom,mode-select", &enable);
+	rc = of_property_read_u32(of_node, "qcom,mode-select", &mode);
 	if (rc)
 		goto read_opt_props;
 
-	if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
-		(enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
+	if (mode > PM_PWM_MODE_LPG ||
+		(mode == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
+		(mode == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
 		dev_err(&pdev->dev, "%s: Invalid mode select\n", __func__);
 		rc = -EINVAL;
 		goto out;
 	}
 
-	_pwm_change_mode(chip, enable);
+	chip->pwm_mode = mode;
+	_pwm_change_mode(chip, mode);
 	_pwm_enable(chip);
 
 read_opt_props:
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 9d19b9a..315a4be 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -37,8 +37,8 @@
 #include "tsi721.h"
 
 #ifdef DEBUG
-u32 dbg_level;
-module_param(dbg_level, uint, S_IWUSR | S_IRUGO);
+u32 tsi_dbg_level;
+module_param_named(dbg_level, tsi_dbg_level, uint, S_IWUSR | S_IRUGO);
 MODULE_PARM_DESC(dbg_level, "Debugging output level (default 0 = none)");
 #endif
 
diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h
index 5941437..957eadc 100644
--- a/drivers/rapidio/devices/tsi721.h
+++ b/drivers/rapidio/devices/tsi721.h
@@ -40,11 +40,11 @@
 };
 
 #ifdef DEBUG
-extern u32 dbg_level;
+extern u32 tsi_dbg_level;
 
 #define tsi_debug(level, dev, fmt, arg...)				\
 	do {								\
-		if (DBG_##level & dbg_level)				\
+		if (DBG_##level & tsi_dbg_level)				\
 			dev_dbg(dev, "%s: " fmt "\n", __func__, ##arg);	\
 	} while (0)
 #else
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index d75f157..e463117 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4197,6 +4197,10 @@
 	const struct regulator_ops *ops;
 	mode_t mode;
 
+	/* Check if debugfs directory already exists */
+	if (rdev->debugfs)
+		return;
+
 	/* Avoid duplicate debugfs directory names */
 	if (parent && rname == rdev->desc->name) {
 		snprintf(name, sizeof(name), "%s-%s", dev_name(parent),
@@ -4221,6 +4225,7 @@
 
 	regulator = regulator_get(NULL, rdev_get_name(rdev));
 	if (IS_ERR(regulator)) {
+		debugfs_remove_recursive(rdev->debugfs);
 		rdev_err(rdev, "regulator get failed, ret=%ld\n",
 			PTR_ERR(regulator));
 		return;
@@ -4291,6 +4296,8 @@
 
 	if (regulator_resolve_supply(rdev))
 		rdev_dbg(rdev, "unable to resolve supply\n");
+	else
+		rdev_init_debugfs(rdev);
 
 	return 0;
 }
@@ -4905,6 +4912,16 @@
 	if (of_have_populated_dt())
 		has_full_constraints = true;
 
+	/*
+	 * Regulators may had failed to resolve their input supplies
+	 * when were registered, either because the input supply was
+	 * not registered yet or because its parent device was not
+	 * bound yet. So attempt to resolve the input supplies for
+	 * pending regulators before trying to disable unused ones.
+	 */
+	class_for_each_device(&regulator_class, NULL, NULL,
+			      regulator_register_resolve_supply);
+
 	/* If we have a full configuration then disable any regulators
 	 * we have permission to change the status for and which are
 	 * not in use or always_on.  This is effectively the default
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
index a315e46..778f482 100644
--- a/drivers/regulator/cpr3-regulator.h
+++ b/drivers/regulator/cpr3-regulator.h
@@ -949,6 +949,8 @@
 void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg);
 int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg,
 			int *fuse_volt_adjust);
+int cpr3_parse_fuse_combo_map(struct cpr3_regulator *vreg, u64 *fuse_val,
+			int fuse_count);
 
 #else
 
@@ -1133,6 +1135,12 @@
 	return 0;
 }
 
+static int cpr3_parse_fuse_combo_map(struct cpr3_regulator *vreg, u64 *fuse_val,
+			int fuse_count)
+{
+	return -EPERM;
+}
+
 #endif /* CONFIG_REGULATOR_CPR3 */
 
 #endif /* __REGULATOR_CPR_REGULATOR_H__ */
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
index 3035155..39ee3c5 100644
--- a/drivers/regulator/cpr3-util.c
+++ b/drivers/regulator/cpr3-util.c
@@ -562,32 +562,41 @@
 		return -EINVAL;
 	}
 
-	rc = of_property_read_u32(node, "qcom,cpr-fuse-combos",
-				&max_fuse_combos);
-	if (rc) {
-		cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n",
-			rc);
-		return rc;
-	}
-
 	/*
-	 * Sanity check against arbitrarily large value to avoid excessive
-	 * memory allocation.
+	 * Check if CPR3 regulator's fuse_combos_supported element is already
+	 * populated by fuse-combo-map logic. If not populated, then parse the
+	 * qcom,cpr-fuse-combos property.
 	 */
-	if (max_fuse_combos > 100 || max_fuse_combos == 0) {
-		cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n",
-			max_fuse_combos);
-		return -EINVAL;
-	}
+	if (vreg->fuse_combos_supported)
+		max_fuse_combos = vreg->fuse_combos_supported;
+	else {
+		rc = of_property_read_u32(node, "qcom,cpr-fuse-combos",
+					&max_fuse_combos);
+		if (rc) {
+			cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n",
+				rc);
+			return rc;
+		}
 
-	if (vreg->fuse_combo >= max_fuse_combos) {
-		cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n",
-			max_fuse_combos - 1, vreg->fuse_combo);
-		BUG_ON(1);
-		return -EINVAL;
-	}
+		/*
+		 * Sanity check against arbitrarily large value to avoid
+		 * excessive memory allocation.
+		 */
+		if (max_fuse_combos > 100 || max_fuse_combos == 0) {
+			cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n",
+				max_fuse_combos);
+			return -EINVAL;
+		}
 
-	vreg->fuse_combos_supported = max_fuse_combos;
+		if (vreg->fuse_combo >= max_fuse_combos) {
+			cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n",
+				max_fuse_combos - 1, vreg->fuse_combo);
+			WARN_ON(1);
+			return -EINVAL;
+		}
+
+		vreg->fuse_combos_supported = max_fuse_combos;
+	}
 
 	of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins);
 
@@ -2414,3 +2423,76 @@
 	kfree(ro_scale);
 	return rc;
 }
+
+/**
+ * cpr3_parse_fuse_combo_map() - parse fuse combo map data for a CPR3 regulator
+ *		from device tree.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse_val:		Array of selection fuse parameter values
+ * @fuse_count:		Number of selection fuse parameters used in fuse combo
+ *			map
+ *
+ * This function reads the qcom,cpr-fuse-combo-map device tree property and
+ * populates the fuse_combo element of CPR3 regulator with the row number of
+ * fuse combo map data that matches with the data in fuse_val input array.
+ *
+ * Return: 0 on success, -ENODEV if qcom,cpr-fuse-combo-map property is not
+ *		specified in device node, other errno on failure
+ */
+int cpr3_parse_fuse_combo_map(struct cpr3_regulator *vreg, u64 *fuse_val,
+			int fuse_count)
+{
+	struct device_node *node = vreg->of_node;
+	int i, j, len, num_fuse_combos, row_size, rc = 0;
+	u32 *tmp;
+
+	if (!of_find_property(node, "qcom,cpr-fuse-combo-map", &len)) {
+		/* property not specified */
+		return -ENODEV;
+	}
+
+	row_size = fuse_count * 2;
+	if (len == 0 || len % (sizeof(u32) * row_size)) {
+		cpr3_err(vreg, "qcom,cpr-fuse-combo-map length=%d is invalid\n",
+			len);
+		return -EINVAL;
+	}
+
+	num_fuse_combos = len / (sizeof(u32) * row_size);
+	vreg->fuse_combos_supported = num_fuse_combos;
+
+	tmp = kzalloc(len, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(node, "qcom,cpr-fuse-combo-map",
+			tmp, num_fuse_combos * row_size);
+	if (rc) {
+		cpr3_err(vreg, "could not read qcom,cpr-fuse-combo-map, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 0; i < num_fuse_combos; i++) {
+		for (j = 0; j < fuse_count; j++) {
+			if (tmp[i * row_size + j * 2] > fuse_val[j]
+			      || tmp[i * row_size + j * 2 + 1] < fuse_val[j])
+				break;
+		}
+		if (j == fuse_count) {
+			vreg->fuse_combo = i;
+			break;
+		}
+	}
+
+	if (i >= num_fuse_combos) {
+		cpr3_err(vreg, "No matching CPR fuse combo found!\n");
+		WARN_ON(1);
+		rc = -EINVAL;
+		goto done;
+	}
+
+done:
+	kfree(tmp);
+	return rc;
+}
diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
index cfc09ba..a9602cb 100644
--- a/drivers/regulator/cpr4-apss-regulator.c
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -51,9 +51,13 @@
  * @speed_bin:		Application processor speed bin fuse parameter value for
  *			the given chip
  * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @foundry_id:		Foundry identifier fuse parameter value for the given
+ *			chip
  * @boost_cfg:		CPR boost configuration fuse parameter value
  * @boost_voltage:	CPR boost voltage fuse parameter value (raw, not
  *			converted to a voltage)
+ * @aging_init_quot_diff:	Initial quotient difference between CPR aging
+ *			min and max sensors measured at time of manufacturing
  *
  * This struct holds the values for all of the fuses read from memory.
  */
@@ -64,9 +68,11 @@
 	u64	quot_offset[MSM8953_APSS_FUSE_CORNERS];
 	u64	speed_bin;
 	u64	cpr_fusing_rev;
+	u64	foundry_id;
 	u64	boost_cfg;
 	u64	boost_voltage;
 	u64	misc;
+	u64	aging_init_quot_diff;
 };
 
 /*
@@ -146,6 +152,11 @@
 	{},
 };
 
+static const struct cpr3_fuse_param msm8953_apss_foundry_id_param[] = {
+	{37, 40, 42},
+	{},
+};
+
 static const struct cpr3_fuse_param msm8953_cpr_boost_fuse_cfg_param[] = {
 	{36, 43, 45},
 	{},
@@ -161,6 +172,21 @@
 	{},
 };
 
+static const struct cpr3_fuse_param msm8953_apss_aging_init_quot_diff_param[]
+= {
+	{72, 0, 7},
+	{},
+};
+
+/*
+ * The maximum number of fuse combinations possible for the selected fuse
+ * parameters in fuse combo map logic.
+ * Here, possible speed-bin values = 8, fuse revision values = 8, and foundry
+ * identifier values = 8. Total number of combinations = 512 (i.e., 8 * 8 * 8)
+ */
+#define CPR4_MSM8953_APSS_FUSE_COMBO_MAP_MAX_COUNT	512
+
+
 /*
  * The number of possible values for misc fuse is
  * 2^(#bits defined for misc fuse)
@@ -206,6 +232,14 @@
  */
 static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1};
 
+/* CPR Aging parameters for msm8953 */
+#define MSM8953_APSS_AGING_INIT_QUOT_DIFF_SCALE	1
+#define MSM8953_APSS_AGING_INIT_QUOT_DIFF_SIZE	8
+#define MSM8953_APSS_AGING_SENSOR_ID		6
+
+/* Use a very high value for max aging margin to be applied */
+#define MSM8953_APSS_AGING_MAX_AGE_MARGIN_QUOT	(-1000)
+
 /**
  * cpr4_msm8953_apss_read_fuse_data() - load APSS specific fuse parameter values
  * @vreg:		Pointer to the CPR3 regulator
@@ -243,6 +277,14 @@
 	}
 	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
 
+	rc = cpr3_read_fuse_param(base, msm8953_apss_foundry_id_param,
+				&fuse->foundry_id);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read foundry id fuse, rc=%d\n", rc);
+		return rc;
+	}
+	cpr3_info(vreg, "foundry id = %llu\n", fuse->foundry_id);
+
 	rc = cpr3_read_fuse_param(base, msm8953_misc_fuse_volt_adj_param,
 				&fuse->misc);
 	if (rc) {
@@ -257,6 +299,14 @@
 		return -EINVAL;
 	}
 
+	rc = cpr3_read_fuse_param(base, msm8953_apss_aging_init_quot_diff_param,
+				&fuse->aging_init_quot_diff);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
 	for (i = 0; i < MSM8953_APSS_FUSE_CORNERS; i++) {
 		rc = cpr3_read_fuse_param(base,
 				msm8953_apss_init_voltage_param[i],
@@ -1120,6 +1170,58 @@
 	return rc;
 }
 
+/*
+ * Constants which define the selection fuse parameters used in fuse combo map
+ * logic.
+ */
+enum cpr4_msm8953_apss_fuse_combo_parameters {
+	MSM8953_APSS_SPEED_BIN = 0,
+	MSM8953_APSS_CPR_FUSE_REV,
+	MSM8953_APSS_FOUNDRY_ID,
+	MSM8953_APSS_FUSE_COMBO_PARAM_COUNT,
+};
+
+/**
+ * cpr4_parse_fuse_combo_map() - parse APSS fuse combo map data from device tree
+ *		properties of the CPR3 regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_parse_fuse_combo_map(struct cpr3_regulator *vreg)
+{
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	u64 *fuse_val;
+	int rc;
+
+	fuse_val = kcalloc(MSM8953_APSS_FUSE_COMBO_PARAM_COUNT,
+			sizeof(*fuse_val), GFP_KERNEL);
+	if (!fuse_val)
+		return -ENOMEM;
+
+	fuse_val[MSM8953_APSS_SPEED_BIN] = fuse->speed_bin;
+	fuse_val[MSM8953_APSS_CPR_FUSE_REV] = fuse->cpr_fusing_rev;
+	fuse_val[MSM8953_APSS_FOUNDRY_ID] = fuse->foundry_id;
+	rc = cpr3_parse_fuse_combo_map(vreg, fuse_val,
+			MSM8953_APSS_FUSE_COMBO_PARAM_COUNT);
+	if (rc == -ENODEV) {
+		cpr3_debug(vreg, "using legacy fuse combo logic, rc=%d\n",
+			rc);
+		rc = 0;
+	} else if (rc < 0) {
+		cpr3_err(vreg, "error reading fuse combo map data, rc=%d\n",
+			rc);
+	} else if (vreg->fuse_combo >=
+			CPR4_MSM8953_APSS_FUSE_COMBO_MAP_MAX_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
+			vreg->fuse_combo);
+		rc = -EINVAL;
+	}
+
+	kfree(fuse_val);
+	return rc;
+}
+
 /**
  * cpr4_apss_init_regulator() - perform all steps necessary to initialize the
  *		configuration data for a CPR3 regulator
@@ -1140,6 +1242,13 @@
 
 	fuse = vreg->platform_fuses;
 
+	rc = cpr4_parse_fuse_combo_map(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error while parsing fuse combo map, rc=%d\n",
+			rc);
+		return rc;
+	}
+
 	rc = cpr4_apss_parse_corner_data(vreg);
 	if (rc) {
 		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
@@ -1212,6 +1321,81 @@
 }
 
 /**
+ * cpr4_apss_init_aging() - perform APSS CPR4 controller specific
+ *		aging initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_init_aging(struct cpr3_controller *ctrl)
+{
+	struct cpr4_msm8953_apss_fuses *fuse = NULL;
+	struct cpr3_regulator *vreg = NULL;
+	u32 aging_ro_scale;
+	int i, j, rc;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			if (ctrl->thread[i].vreg[j].aging_allowed) {
+				ctrl->aging_required = true;
+				vreg = &ctrl->thread[i].vreg[j];
+				fuse = vreg->platform_fuses;
+				break;
+			}
+		}
+	}
+
+	if (!ctrl->aging_required || !fuse)
+		return 0;
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+					1, &aging_ro_scale);
+	if (rc)
+		return rc;
+
+	if (aging_ro_scale == 0) {
+		cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+			aging_ro_scale);
+		return -EINVAL;
+	}
+
+	ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+	ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+	ctrl->aging_sensor_count = 1;
+	ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
+	if (!ctrl->aging_sensor)
+		return -ENOMEM;
+
+	ctrl->aging_sensor->sensor_id = MSM8953_APSS_AGING_SENSOR_ID;
+	ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+	ctrl->aging_sensor->init_quot_diff
+		= cpr3_convert_open_loop_voltage_fuse(0,
+			MSM8953_APSS_AGING_INIT_QUOT_DIFF_SCALE,
+			fuse->aging_init_quot_diff,
+			MSM8953_APSS_AGING_INIT_QUOT_DIFF_SIZE);
+
+	if (ctrl->aging_sensor->init_quot_diff == 0) {
+		/*
+		 * Initial quotient difference value '0' has a special meaning
+		 * in MSM8953 fusing scheme. Use max age margin quotient
+		 * difference to consider full aging margin of 15 mV.
+		 */
+		ctrl->aging_sensor->init_quot_diff
+			= MSM8953_APSS_AGING_MAX_AGE_MARGIN_QUOT;
+		cpr3_debug(ctrl, "Init quotient diff = 0, use max age margin quotient\n");
+	}
+
+	cpr3_info(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
+		ctrl->aging_sensor->sensor_id,
+		ctrl->aging_sensor->init_quot_diff,
+		ctrl->aging_sensor->ro_scale);
+
+	return 0;
+}
+
+/**
  * cpr4_apss_init_controller() - perform APSS CPR4 controller specific
  *		initializations
  * @ctrl:		Pointer to the CPR3 controller
@@ -1390,6 +1574,13 @@
 		}
 	}
 
+	rc = cpr4_apss_init_aging(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+			rc);
+		return rc;
+	}
+
 	platform_set_drvdata(pdev, ctrl);
 
 	return cpr3_regulator_register(pdev, ctrl);
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 13d53a3..589167e 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -491,7 +491,10 @@
 		.name = "fan53555",
 		.driver_data = FAN53555_VENDOR_FAIRCHILD
 	}, {
-		.name = "syr82x",
+		.name = "syr827",
+		.driver_data = FAN53555_VENDOR_SILERGY
+	}, {
+		.name = "syr828",
 		.driver_data = FAN53555_VENDOR_SILERGY
 	}, {
 		.name = "hl7509",
diff --git a/drivers/regulator/mem-acc-regulator.c b/drivers/regulator/mem-acc-regulator.c
index 4c03dec..e22a259 100644
--- a/drivers/regulator/mem-acc-regulator.c
+++ b/drivers/regulator/mem-acc-regulator.c
@@ -108,6 +108,8 @@
 	u32			*phys_reg_addr_list;
 	void __iomem		**remap_reg_addr_list;
 	struct corner_acc_reg_config	*corner_acc_reg_config;
+	u32			*override_acc_range_fuse_list;
+	int			override_acc_range_fuse_num;
 };
 
 static DEFINE_MUTEX(mem_acc_memory_mutex);
@@ -549,9 +551,8 @@
 	return 0;
 }
 
-static int override_mem_acc_custom_data(struct platform_device *pdev,
-				 struct mem_acc_regulator *mem_acc_vreg,
-				 int mem_type)
+static int override_mem_acc_custom_data(struct mem_acc_regulator *mem_acc_vreg,
+		 int mem_type)
 {
 	char *custom_apc_data_str;
 	int len, rc = 0, i;
@@ -647,27 +648,48 @@
 
 }
 
-static int mem_acc_find_override_map_match(struct platform_device *pdev,
-				 struct mem_acc_regulator *mem_acc_vreg)
+static void mem_acc_read_efuse_param(struct mem_acc_regulator *mem_acc_vreg,
+		u32 *fuse_sel, int *val)
 {
-	struct device_node *of_node = pdev->dev.of_node;
+	u64 fuse_bits;
+
+	fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
+					   fuse_sel[3]);
+	/*
+	 * fuse_sel[1] = LSB position in row (shift)
+	 * fuse_sel[2] = num of bits (mask)
+	 */
+	*val = (fuse_bits >> fuse_sel[1]) & ((1 << fuse_sel[2]) - 1);
+}
+
+#define FUSE_TUPLE_SIZE 4
+static int mem_acc_parse_override_fuse_version_map(
+			 struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
 	int i, rc, tuple_size;
 	int len = 0;
 	u32 *tmp;
-	char *prop_str = "qcom,override-fuse-version-map";
+	u32 fuse_sel[4];
+	char *prop_str;
 
-	/* Specify default no match case. */
-	mem_acc_vreg->override_map_match = FUSE_MAP_NO_MATCH;
-	mem_acc_vreg->override_map_count = 0;
-
-	if (!of_find_property(of_node, prop_str, &len)) {
-		/* No mapping present. */
-		return 0;
+	prop_str = "qcom,override-acc-fuse-sel";
+	rc = of_property_read_u32_array(of_node, prop_str, fuse_sel,
+					FUSE_TUPLE_SIZE);
+	if (rc < 0) {
+		pr_err("Read failed - %s rc=%d\n", prop_str, rc);
+		return rc;
 	}
 
+	mem_acc_read_efuse_param(mem_acc_vreg, fuse_sel,
+				 &mem_acc_vreg->override_fuse_value);
+
+	prop_str = "qcom,override-fuse-version-map";
+	if (!of_find_property(of_node, prop_str, &len))
+		return -EINVAL;
+
 	tuple_size = 1;
 	mem_acc_vreg->override_map_count = len / (sizeof(u32) * tuple_size);
-
 	if (len == 0 || len % (sizeof(u32) * tuple_size)) {
 		pr_err("%s length=%d is invalid\n", prop_str, len);
 		return -EINVAL;
@@ -695,8 +717,9 @@
 	}
 
 	if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH)
-		pr_debug("%s tuple match found: %d\n", prop_str,
-				mem_acc_vreg->override_map_match);
+		pr_info("override_fuse_val=%d, %s tuple match found: %d\n",
+			mem_acc_vreg->override_fuse_value, prop_str,
+			mem_acc_vreg->override_map_match);
 	else
 		pr_err("%s tuple match not found\n", prop_str);
 
@@ -705,6 +728,121 @@
 	return rc;
 }
 
+static int mem_acc_parse_override_fuse_version_range(
+			 struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	int i, j, rc, size, row_size;
+	int num_fuse_sel, len = 0;
+	u32 *tmp = NULL;
+	char *prop_str;
+	u32 *fuse_val, *fuse_sel;
+	char *buf = NULL;
+	int pos = 0, buflen;
+
+	prop_str = "qcom,override-acc-range-fuse-list";
+	if (!of_find_property(of_node, prop_str, &len)) {
+		pr_err("%s property is missing\n", prop_str);
+		return -EINVAL;
+	}
+
+	size = len / sizeof(u32);
+	if (len == 0 || (size % FUSE_TUPLE_SIZE)) {
+		pr_err("%s property length (%d) is invalid\n", prop_str, len);
+		return -EINVAL;
+	}
+
+	num_fuse_sel = size / FUSE_TUPLE_SIZE;
+	fuse_val = devm_kcalloc(mem_acc_vreg->dev, num_fuse_sel,
+				sizeof(*fuse_val), GFP_KERNEL);
+	if (!fuse_val)
+		return -ENOMEM;
+	mem_acc_vreg->override_acc_range_fuse_list = fuse_val;
+	mem_acc_vreg->override_acc_range_fuse_num = num_fuse_sel;
+
+	fuse_sel = kzalloc(len, GFP_KERNEL);
+	if (!fuse_sel) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = of_property_read_u32_array(of_node, prop_str, fuse_sel,
+					size);
+	if (rc) {
+		pr_err("%s read failed, rc=%d\n", prop_str, rc);
+		goto done;
+	}
+
+	for (i = 0; i < num_fuse_sel; i++) {
+		mem_acc_read_efuse_param(mem_acc_vreg, &fuse_sel[i * 4],
+					 &fuse_val[i]);
+	}
+
+	prop_str = "qcom,override-fuse-range-map";
+	if (!of_find_property(of_node, prop_str, &len))
+		goto done;
+
+	row_size = num_fuse_sel * 2;
+	mem_acc_vreg->override_map_count = len / (sizeof(u32) * row_size);
+
+	if (len == 0 || len % (sizeof(u32) * row_size)) {
+		pr_err("%s length=%d is invalid\n", prop_str, len);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	tmp = kzalloc(len, GFP_KERNEL);
+	if (!tmp) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = of_property_read_u32_array(of_node, prop_str, tmp,
+				mem_acc_vreg->override_map_count * row_size);
+	if (rc) {
+		pr_err("could not read %s rc=%d\n", prop_str, rc);
+		goto done;
+	}
+
+	for (i = 0; i < mem_acc_vreg->override_map_count; i++) {
+		for (j = 0; j < num_fuse_sel; j++) {
+			if (tmp[i * row_size + j * 2] > fuse_val[j]
+				|| tmp[i * row_size + j * 2 + 1] < fuse_val[j])
+				break;
+		}
+
+		if (j == num_fuse_sel) {
+			mem_acc_vreg->override_map_match = i;
+			break;
+		}
+	}
+
+	/*
+	 * Log register and value mapping since they are useful for
+	 * baseline MEM ACC logging.
+	 */
+	buflen = num_fuse_sel * sizeof("fuse_selxxxx = XXXX ");
+	buf = kzalloc(buflen, GFP_KERNEL);
+	if (!buf)
+		goto done;
+
+	for (j = 0; j < num_fuse_sel; j++)
+		pos += scnprintf(buf + pos, buflen - pos, "fuse_sel%d = %d ",
+				 j, fuse_val[j]);
+	buf[pos] = '\0';
+	if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH)
+		pr_info("%s %s tuple match found: %d\n", buf, prop_str,
+			mem_acc_vreg->override_map_match);
+	else
+		pr_err("%s %s tuple match not found\n", buf, prop_str);
+
+done:
+	kfree(fuse_sel);
+	kfree(tmp);
+	kfree(buf);
+	return rc;
+}
+
 #define MAX_CHARS_PER_INT	20
 
 static int mem_acc_reg_addr_val_dump(struct mem_acc_regulator *mem_acc_vreg,
@@ -789,6 +927,150 @@
 	return rc;
 }
 
+static int mem_acc_override_reg_addr_val_init(
+			struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	struct corner_acc_reg_config *corner_acc_reg_config;
+	struct acc_reg_value *override_reg_config_list;
+	int i, tuple_count, tuple_match, len = 0, rc = 0;
+	u32 list_size, override_max_reg_config_len;
+	char prop_str[40];
+	struct property *prop;
+	int num_corners = mem_acc_vreg->num_corners;
+
+	if (!mem_acc_vreg->corner_acc_reg_config)
+		return 0;
+
+	if (mem_acc_vreg->override_map_count) {
+		if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
+			return 0;
+		tuple_count = mem_acc_vreg->override_map_count;
+		tuple_match = mem_acc_vreg->override_map_match;
+	} else {
+		tuple_count = 1;
+		tuple_match = 0;
+	}
+
+	corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config;
+	for (i = 1; i <= num_corners; i++) {
+		snprintf(prop_str, sizeof(prop_str),
+			 "qcom,override-corner%d-addr-val-map", i);
+		prop = of_find_property(of_node, prop_str, &len);
+		list_size = len / (tuple_count * sizeof(u32));
+		if (!prop) {
+			pr_debug("%s property not specified\n", prop_str);
+			continue;
+		}
+
+		if ((!list_size) || list_size < (num_corners * 2)) {
+			pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n",
+			i, len);
+			return -EINVAL;
+		}
+
+		override_max_reg_config_len = list_size / (num_corners * 2);
+		override_reg_config_list =
+				corner_acc_reg_config[i].reg_config_list;
+
+		if (corner_acc_reg_config[i].max_reg_config_len
+					!= override_max_reg_config_len) {
+			/* Free already allocate memory */
+			devm_kfree(mem_acc_vreg->dev, override_reg_config_list);
+
+			/* Allocated memory for new requirement */
+			override_reg_config_list =
+				devm_kcalloc(mem_acc_vreg->dev,
+				override_max_reg_config_len * num_corners,
+				sizeof(*override_reg_config_list), GFP_KERNEL);
+			if (!override_reg_config_list)
+				return -ENOMEM;
+
+			corner_acc_reg_config[i].max_reg_config_len =
+						override_max_reg_config_len;
+			corner_acc_reg_config[i].reg_config_list =
+						override_reg_config_list;
+		}
+
+		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
+					override_reg_config_list, tuple_match,
+					list_size, mem_acc_vreg->num_acc_reg);
+		if (rc) {
+			pr_err("Failed to read %s property: rc=%d\n",
+				prop_str, rc);
+			return rc;
+		}
+
+		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
+						&corner_acc_reg_config[i], i);
+		if (rc) {
+			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int mem_acc_parse_override_config(struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	int i, rc = 0;
+
+	/* Specify default no match case. */
+	mem_acc_vreg->override_map_match = FUSE_MAP_NO_MATCH;
+	mem_acc_vreg->override_map_count = 0;
+
+	if (of_find_property(of_node, "qcom,override-fuse-range-map",
+			     NULL)) {
+		rc = mem_acc_parse_override_fuse_version_range(mem_acc_vreg);
+		if (rc) {
+			pr_err("parsing qcom,override-fuse-range-map property failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	} else if (of_find_property(of_node, "qcom,override-fuse-version-map",
+				    NULL)) {
+		rc = mem_acc_parse_override_fuse_version_map(mem_acc_vreg);
+		if (rc) {
+			pr_err("parsing qcom,override-fuse-version-map property failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		/* No override fuse configuration defined in device node */
+		return 0;
+	}
+
+	if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
+		return 0;
+
+	rc = mem_acc_override_corner_map(mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to override corner map rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to override reg_config_list init rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	for (i = 0; i < MEMORY_MAX; i++) {
+		rc = override_mem_acc_custom_data(mem_acc_vreg, i);
+		if (rc) {
+			pr_err("Unable to override custom data for mem_type=%d rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
 static int mem_acc_init_reg_config(struct mem_acc_regulator *mem_acc_vreg)
 {
 	struct device_node *of_node = mem_acc_vreg->dev->of_node;
@@ -965,92 +1247,6 @@
 	return rc;
 }
 
-static int mem_acc_override_reg_addr_val_init(
-			struct mem_acc_regulator *mem_acc_vreg)
-{
-	struct device_node *of_node = mem_acc_vreg->dev->of_node;
-	struct corner_acc_reg_config *corner_acc_reg_config;
-	struct acc_reg_value *override_reg_config_list;
-	int i, tuple_count, tuple_match, len = 0, rc = 0;
-	u32 list_size, override_max_reg_config_len;
-	char prop_str[40];
-	struct property *prop;
-	int num_corners = mem_acc_vreg->num_corners;
-
-	if (!mem_acc_vreg->corner_acc_reg_config)
-		return 0;
-
-	if (mem_acc_vreg->override_map_count) {
-		if (mem_acc_vreg->override_map_match ==	FUSE_MAP_NO_MATCH)
-			return 0;
-		tuple_count = mem_acc_vreg->override_map_count;
-		tuple_match = mem_acc_vreg->override_map_match;
-	} else {
-		tuple_count = 1;
-		tuple_match = 0;
-	}
-
-	corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config;
-	for (i = 1; i <= num_corners; i++) {
-		snprintf(prop_str, sizeof(prop_str),
-				"qcom,override-corner%d-addr-val-map", i);
-		prop = of_find_property(of_node, prop_str, &len);
-		list_size = len / (tuple_count * sizeof(u32));
-		if (!prop) {
-			pr_debug("%s property not specified\n", prop_str);
-			continue;
-		}
-
-		if ((!list_size) || list_size < (num_corners * 2)) {
-			pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n",
-			i, len);
-			return -EINVAL;
-		}
-
-		override_max_reg_config_len = list_size / (num_corners * 2);
-		override_reg_config_list =
-				corner_acc_reg_config[i].reg_config_list;
-
-		if (corner_acc_reg_config[i].max_reg_config_len
-					!= override_max_reg_config_len) {
-			/* Free already allocate memory */
-			devm_kfree(mem_acc_vreg->dev, override_reg_config_list);
-
-			/* Allocated memory for new requirement */
-			override_reg_config_list =
-				devm_kcalloc(mem_acc_vreg->dev,
-				override_max_reg_config_len * num_corners,
-				sizeof(*override_reg_config_list), GFP_KERNEL);
-			if (!override_reg_config_list)
-				return -ENOMEM;
-
-			corner_acc_reg_config[i].max_reg_config_len =
-						override_max_reg_config_len;
-			corner_acc_reg_config[i].reg_config_list =
-						override_reg_config_list;
-		}
-
-		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
-					override_reg_config_list, tuple_match,
-					list_size, mem_acc_vreg->num_acc_reg);
-		if (rc) {
-			pr_err("Failed to read %s property: rc=%d\n",
-				prop_str, rc);
-			return rc;
-		}
-
-		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
-						&corner_acc_reg_config[i], i);
-		if (rc) {
-			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
-				i, rc);
-			return rc;
-		}
-	}
-
-	return rc;
-}
-
 #define MEM_TYPE_STRING_LEN	20
 static int mem_acc_init(struct platform_device *pdev,
 		struct mem_acc_regulator *mem_acc_vreg)
@@ -1058,8 +1254,6 @@
 	struct device_node *of_node = pdev->dev.of_node;
 	struct resource *res;
 	int len, rc, i, j;
-	u32 fuse_sel[4];
-	u64 fuse_bits;
 	bool acc_type_present = false;
 	char tmps[MEM_TYPE_STRING_LEN];
 
@@ -1201,59 +1395,12 @@
 		}
 	}
 
-	if (of_find_property(mem_acc_vreg->dev->of_node,
-				"qcom,override-acc-fuse-sel", NULL)) {
-		rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
-			"qcom,override-acc-fuse-sel", fuse_sel, 4);
-		if (rc < 0) {
-			pr_err("Read failed - qcom,override-acc-fuse-sel rc=%d\n",
-					rc);
-			return rc;
-		}
-
-		fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
-								fuse_sel[3]);
-		/*
-		 * fuse_sel[1] = LSB position in row (shift)
-		 * fuse_sel[2] = num of bits (mask)
-		 */
-		mem_acc_vreg->override_fuse_value = (fuse_bits >> fuse_sel[1]) &
-						((1 << fuse_sel[2]) - 1);
-
-		rc = mem_acc_find_override_map_match(pdev, mem_acc_vreg);
-		if (rc) {
-			pr_err("Unable to find fuse map match rc=%d\n", rc);
-			return rc;
-		}
-
-		pr_debug("override_fuse_val=%d override_map_match=%d\n",
-					mem_acc_vreg->override_fuse_value,
-					mem_acc_vreg->override_map_match);
-
-		rc = mem_acc_override_corner_map(mem_acc_vreg);
-		if (rc) {
-			pr_err("Unable to override corner map rc=%d\n", rc);
-			return rc;
-		}
-
-		rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg);
-		if (rc) {
-			pr_err("Unable to override reg_config_list init rc=%d\n",
-				rc);
-			return rc;
-		}
-
-		for (i = 0; i < MEMORY_MAX; i++) {
-			rc = override_mem_acc_custom_data(pdev,
-							mem_acc_vreg, i);
-			if (rc) {
-				pr_err("Unable to override custom data for mem_type=%d rc=%d\n",
-					i, rc);
-				return rc;
-			}
-		}
+	rc = mem_acc_parse_override_config(mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to parse mem acc override configuration, rc=%d\n",
+			rc);
+		return rc;
 	}
-
 	if (acc_type_present) {
 		mem_acc_vreg->mem_acc_type_data = devm_kzalloc(
 			mem_acc_vreg->dev, mem_acc_vreg->num_corners *
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index dbe2a08..d672d5f 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -65,6 +65,7 @@
 #define REG_LAB_PRECHARGE_CTL		0x5E
 #define REG_LAB_SOFT_START_CTL		0x5F
 #define REG_LAB_SPARE_CTL		0x60
+#define REG_LAB_MISC_CTL		0x60 /* PMI8998/PM660A */
 #define REG_LAB_PFM_CTL			0x62
 
 /* LAB registers for PM660A */
@@ -137,6 +138,9 @@
 #define LAB_SPARE_TOUCH_WAKE_BIT	BIT(3)
 #define LAB_SPARE_DISABLE_SCP_BIT	BIT(0)
 
+/* REG_LAB_MISC_CTL */
+#define LAB_AUTO_GM_BIT			BIT(4)
+
 /* REG_LAB_PFM_CTL */
 #define LAB_PFM_EN_BIT			BIT(7)
 
@@ -1226,6 +1230,7 @@
 	return rc;
 }
 
+/* For PMI8998 and earlier PMICs */
 static const struct ibb_ver_ops ibb_ops_v1 = {
 	.set_default_voltage	= qpnp_ibb_set_default_voltage_v1,
 	.set_voltage		= qpnp_ibb_set_voltage_v1,
@@ -1237,6 +1242,7 @@
 	.voltage_at_one_pulse	= qpnp_ibb_output_voltage_at_one_pulse_v1,
 };
 
+/* For PM660A and later PMICs */
 static const struct ibb_ver_ops ibb_ops_v2 = {
 	.set_default_voltage	= qpnp_ibb_set_default_voltage_v2,
 	.set_voltage		= qpnp_ibb_set_voltage_v2,
@@ -1346,8 +1352,9 @@
 				u32 thresh, bool enable)
 {
 	int rc = 0;
-	u8 val;
+	u8 val, mask;
 
+	mask = LAB_PS_CTL_EN;
 	if (enable) {
 		for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v2); val++)
 			if (lab_ps_thresh_table_v2[val] == thresh)
@@ -1359,13 +1366,13 @@
 		}
 
 		val |= LAB_PS_CTL_EN;
+		mask |= LAB_PS_THRESH_MASK;
 	} else {
 		val = 0;
 	}
 
-	rc = qpnp_labibb_write(labibb, labibb->lab_base +
-			 REG_LAB_PS_CTL, &val, 1);
-
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+			 REG_LAB_PS_CTL, mask, val);
 	if (rc < 0)
 		pr_err("write register %x failed rc = %d\n",
 				REG_LAB_PS_CTL, rc);
@@ -1373,12 +1380,18 @@
 	return rc;
 }
 
+/* For PMI8996 and earlier PMICs */
 static const struct lab_ver_ops lab_ops_v1 = {
 	.set_default_voltage	= qpnp_lab_set_default_voltage_v1,
 	.ps_ctl			= qpnp_lab_ps_ctl_v1,
 };
 
-static const struct lab_ver_ops lab_ops_v2 = {
+static const struct lab_ver_ops pmi8998_lab_ops = {
+	.set_default_voltage	= qpnp_lab_set_default_voltage_v1,
+	.ps_ctl			= qpnp_lab_ps_ctl_v2,
+};
+
+static const struct lab_ver_ops pm660_lab_ops = {
 	.set_default_voltage	= qpnp_lab_set_default_voltage_v2,
 	.ps_ctl			= qpnp_lab_ps_ctl_v2,
 };
@@ -1845,7 +1858,7 @@
 static int qpnp_labibb_ttw_enter_ibb_common(struct qpnp_labibb *labibb)
 {
 	int rc = 0;
-	u8 val;
+	u8 val, mask;
 
 	val = 0;
 	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL,
@@ -1865,10 +1878,16 @@
 		return rc;
 	}
 
-	val = IBB_WAIT_MBG_OK;
+	if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+		val = 0;
+		mask = IBB_DIS_DLY_MASK;
+	} else {
+		val = IBB_WAIT_MBG_OK;
+		mask = IBB_DIS_DLY_MASK | IBB_WAIT_MBG_OK;
+	}
+
 	rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,
-				REG_IBB_PWRUP_PWRDN_CTL_2,
-				IBB_DIS_DLY_MASK | IBB_WAIT_MBG_OK, val);
+				REG_IBB_PWRUP_PWRDN_CTL_2, mask, val);
 	if (rc < 0) {
 		pr_err("write to register %x failed rc = %d\n",
 			REG_IBB_PWRUP_PWRDN_CTL_2, rc);
@@ -1944,7 +1963,7 @@
 static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
 {
 	int rc = 0;
-	u8 val;
+	u8 val, reg;
 
 	/* Save the IBB settings before they get modified for TTW mode */
 	if (!labibb->ibb_settings_saved) {
@@ -2006,10 +2025,17 @@
 		}
 
 		val = LAB_SPARE_DISABLE_SCP_BIT;
+
 		if (labibb->pmic_rev_id->pmic_subtype != PMI8950_SUBTYPE)
 			val |= LAB_SPARE_TOUCH_WAKE_BIT;
-		rc = qpnp_labibb_write(labibb, labibb->lab_base +
-				REG_LAB_SPARE_CTL, &val, 1);
+
+		if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+			reg = REG_LAB_MISC_CTL;
+			val |= LAB_AUTO_GM_BIT;
+		} else {
+			reg = REG_LAB_SPARE_CTL;
+		}
+		rc = qpnp_labibb_write(labibb, labibb->lab_base + reg, &val, 1);
 		if (rc < 0) {
 			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
 				REG_LAB_SPARE_CTL, rc);
@@ -2039,7 +2065,15 @@
 	case PMI8950_SUBTYPE:
 		rc = qpnp_labibb_ttw_enter_ibb_pmi8950(labibb);
 		break;
+	case PMI8998_SUBTYPE:
+		rc = labibb->lab_ver_ops->ps_ctl(labibb, 70, true);
+		if (rc < 0)
+			break;
+
+		rc = qpnp_ibb_ps_config(labibb, true);
+		break;
 	}
+
 	if (rc < 0) {
 		pr_err("Failed to configure TTW-enter for IBB rc=%d\n", rc);
 		return rc;
@@ -2072,7 +2106,7 @@
 static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
 {
 	int rc = 0;
-	u8 val;
+	u8 val, reg;
 
 	if (!labibb->ibb_settings_saved) {
 		pr_err("IBB settings are not saved!\n");
@@ -2106,8 +2140,14 @@
 		}
 
 		val = 0;
-		rc = qpnp_labibb_write(labibb, labibb->lab_base +
-					REG_LAB_SPARE_CTL, &val, 1);
+		if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+			reg = REG_LAB_MISC_CTL;
+			val |= LAB_AUTO_GM_BIT;
+		} else {
+			reg = REG_LAB_SPARE_CTL;
+		}
+
+		rc = qpnp_labibb_write(labibb, labibb->lab_base + reg, &val, 1);
 		if (rc < 0) {
 			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
 					REG_LAB_SPARE_CTL, rc);
@@ -3003,7 +3043,7 @@
 				struct device_node *of_node)
 {
 	int rc = 0;
-	u32 i, tmp = 0;
+	u32 i = 0, tmp = 0;
 	u8 val, mask;
 
 	/*
@@ -3037,37 +3077,48 @@
 	rc = of_property_read_u32(of_node,
 		"qcom,qpnp-ibb-lab-pwrdn-delay", &tmp);
 	if (!rc) {
-		for (val = 0; val < ARRAY_SIZE(ibb_pwrdn_dly_table); val++)
-			if (ibb_pwrdn_dly_table[val] == tmp)
-				break;
+		if (tmp > 0) {
+			for (i = 0; i < ARRAY_SIZE(ibb_pwrdn_dly_table); i++) {
+				if (ibb_pwrdn_dly_table[i] == tmp)
+					break;
+			}
 
-		if (val == ARRAY_SIZE(ibb_pwrdn_dly_table)) {
-			pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n");
-			return -EINVAL;
+			if (i == ARRAY_SIZE(ibb_pwrdn_dly_table)) {
+				pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n");
+				return -EINVAL;
+			}
 		}
 
 		labibb->ibb_vreg.pwrdn_dly = tmp;
-		val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+
+		if (tmp > 0)
+			val = i | IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+
 		mask |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
 	}
 
 	rc = of_property_read_u32(of_node,
 			"qcom,qpnp-ibb-lab-pwrup-delay", &tmp);
 	if (!rc) {
-		for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++)
-			if (ibb_pwrup_dly_table[i] == tmp)
-				break;
+		if (tmp > 0) {
+			for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++) {
+				if (ibb_pwrup_dly_table[i] == tmp)
+					break;
+			}
 
-		if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) {
-			pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n");
-			return -EINVAL;
+			if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) {
+				pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n");
+				return -EINVAL;
+			}
 		}
 
 		labibb->ibb_vreg.pwrup_dly = tmp;
 
+		if (tmp > 0)
+			val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY1;
+
 		val |= (i << IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT);
-		val |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
-			IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
+		val |= IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK;
 		mask |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
 			IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK |
 			IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
@@ -3672,6 +3723,9 @@
 	case PMI8950_SUBTYPE:
 		/* TTW supported for all revisions */
 		break;
+	case PMI8998_SUBTYPE:
+		/* TTW supported for all revisions */
+		break;
 	default:
 		pr_info("TTW mode not supported for PMIC-subtype = %d\n",
 					labibb->pmic_rev_id->pmic_subtype);
@@ -3723,7 +3777,10 @@
 
 	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
 		labibb->ibb_ver_ops = &ibb_ops_v2;
-		labibb->lab_ver_ops = &lab_ops_v2;
+		labibb->lab_ver_ops = &pm660_lab_ops;
+	} else if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+		labibb->ibb_ver_ops = &ibb_ops_v1;
+		labibb->lab_ver_ops = &pmi8998_lab_ops;
 	} else {
 		labibb->ibb_ver_ops = &ibb_ops_v1;
 		labibb->lab_ver_ops = &lab_ops_v1;
diff --git a/drivers/regulator/refgen.c b/drivers/regulator/refgen.c
index 629fee0..830e1b0 100644
--- a/drivers/regulator/refgen.c
+++ b/drivers/regulator/refgen.c
@@ -31,7 +31,7 @@
 #define REFGEN_BIAS_EN_DISABLE			0x6
 
 #define REFGEN_REG_BG_CTRL			0x14
-#define REFGEN_BG_CTRL_MASK			GENMASK(2, 0)
+#define REFGEN_BG_CTRL_MASK			GENMASK(2, 1)
 #define REFGEN_BG_CTRL_ENABLE			0x6
 #define REFGEN_BG_CTRL_DISABLE			0x4
 
@@ -41,11 +41,21 @@
 	void __iomem		*addr;
 };
 
+static void masked_writel(u32 val, u32 mask, void __iomem *addr)
+{
+	u32 reg;
+
+	reg = readl_relaxed(addr);
+	reg = (reg & ~mask) | (val & mask);
+	writel_relaxed(reg, addr);
+}
+
 static int refgen_enable(struct regulator_dev *rdev)
 {
 	struct refgen *vreg = rdev_get_drvdata(rdev);
 
-	writel_relaxed(REFGEN_BG_CTRL_ENABLE, vreg->addr + REFGEN_REG_BG_CTRL);
+	masked_writel(REFGEN_BG_CTRL_ENABLE, REFGEN_BG_CTRL_MASK,
+			vreg->addr + REFGEN_REG_BG_CTRL);
 	writel_relaxed(REFGEN_BIAS_EN_ENABLE, vreg->addr + REFGEN_REG_BIAS_EN);
 
 	return 0;
@@ -56,7 +66,8 @@
 	struct refgen *vreg = rdev_get_drvdata(rdev);
 
 	writel_relaxed(REFGEN_BIAS_EN_DISABLE, vreg->addr + REFGEN_REG_BIAS_EN);
-	writel_relaxed(REFGEN_BG_CTRL_DISABLE, vreg->addr + REFGEN_REG_BG_CTRL);
+	masked_writel(REFGEN_BG_CTRL_DISABLE, REFGEN_BG_CTRL_MASK,
+			vreg->addr + REFGEN_REG_BG_CTRL);
 
 	return 0;
 }
diff --git a/drivers/regulator/rpmh-regulator.c b/drivers/regulator/rpmh-regulator.c
index 562b05a..1de08d4 100644
--- a/drivers/regulator/rpmh-regulator.c
+++ b/drivers/regulator/rpmh-regulator.c
@@ -36,10 +36,13 @@
  * %RPMH_REGULATOR_TYPE_ARC:	RPMh ARC accelerator which supports voting on
  *				the CPR managed voltage level of LDO and SMPS
  *				type PMIC regulators.
+ * %RPMH_REGULATOR_TYPE_XOB:	RPMh XOB accelerator which supports voting on
+ *				the enable state of PMIC regulators.
  */
 enum rpmh_regulator_type {
 	RPMH_REGULATOR_TYPE_VRM,
 	RPMH_REGULATOR_TYPE_ARC,
+	RPMH_REGULATOR_TYPE_XOB,
 };
 
 /**
@@ -52,6 +55,7 @@
  *					for enable voting.  Instead, ARC level
  *					0 corresponds to "disabled" for a given
  *					ARC regulator resource if supported.
+ * %RPMH_REGULATOR_REG_XOB_ENABLE:	XOB enable voting register index
  * %RPMH_REGULATOR_REG_ENABLE:		Common enable index used in callback
  *					functions for both ARC and VRM.
  * %RPMH_REGULATOR_REG_VRM_MODE:	VRM regulator mode voting register index
@@ -61,6 +65,8 @@
  *					register indices
  * %RPMH_REGULATOR_REG_ARC_MAX:		Exclusive upper limit of ARC register
  *					indices
+ * %RPMH_REGULATOR_REG_XOB_MAX:		Exclusive upper limit of XOB register
+ *					indices
  * %RPMH_REGULATOR_REG_VRM_MAX:		Exclusive upper limit of VRM register
  *					indices
  * %RPMH_REGULATOR_REG_MAX:		Combined exclusive upper limit of ARC
@@ -73,11 +79,13 @@
 	RPMH_REGULATOR_REG_ARC_LEVEL		= 0,
 	RPMH_REGULATOR_REG_VRM_ENABLE		= 1,
 	RPMH_REGULATOR_REG_ARC_PSEUDO_ENABLE	= RPMH_REGULATOR_REG_VRM_ENABLE,
+	RPMH_REGULATOR_REG_XOB_ENABLE		= RPMH_REGULATOR_REG_VRM_ENABLE,
 	RPMH_REGULATOR_REG_ENABLE		= RPMH_REGULATOR_REG_VRM_ENABLE,
 	RPMH_REGULATOR_REG_VRM_MODE		= 2,
 	RPMH_REGULATOR_REG_VRM_HEADROOM		= 3,
 	RPMH_REGULATOR_REG_ARC_REAL_MAX		= 1,
 	RPMH_REGULATOR_REG_ARC_MAX		= 2,
+	RPMH_REGULATOR_REG_XOB_MAX		= 2,
 	RPMH_REGULATOR_REG_VRM_MAX		= 4,
 	RPMH_REGULATOR_REG_MAX			= 4,
 };
@@ -104,6 +112,9 @@
 #define RPMH_VRM_MODE_MIN		0
 #define RPMH_VRM_MODE_MAX		7
 
+/* XOB voting registers are found in the VRM hardware module */
+#define CMD_DB_HW_XOB			CMD_DB_HW_VRM
+
 /*
  * Mapping from RPMh VRM accelerator modes to regulator framework modes
  * Assumes that SMPS PFM mode == LDO LPM mode and SMPS PWM mode == LDO HPM mode
@@ -297,6 +308,10 @@
 	[RPMH_REGULATOR_REG_ARC_LEVEL]		= "hlvl",
 };
 
+static const char *const rpmh_regulator_xob_param_names[] = {
+	[RPMH_REGULATOR_REG_XOB_ENABLE]		= "en",
+};
+
 /**
  * rpmh_regulator_req() - print the rpmh regulator request to the kernel log
  * @vreg:		Pointer to the RPMh regulator
@@ -323,12 +338,22 @@
 	u32 valid;
 	bool first;
 
-	max_reg_index = aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM
-					? RPMH_REGULATOR_REG_VRM_MAX
-					: RPMH_REGULATOR_REG_ARC_REAL_MAX;
-	param_name = aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM
-					? rpmh_regulator_vrm_param_names
-					: rpmh_regulator_arc_param_names;
+	switch (aggr_vreg->regulator_type) {
+	case RPMH_REGULATOR_TYPE_VRM:
+		max_reg_index = RPMH_REGULATOR_REG_VRM_MAX;
+		param_name =  rpmh_regulator_vrm_param_names;
+		break;
+	case RPMH_REGULATOR_TYPE_ARC:
+		max_reg_index = RPMH_REGULATOR_REG_ARC_REAL_MAX;
+		param_name =  rpmh_regulator_arc_param_names;
+		break;
+	case RPMH_REGULATOR_TYPE_XOB:
+		max_reg_index = RPMH_REGULATOR_REG_XOB_MAX;
+		param_name =  rpmh_regulator_xob_param_names;
+		break;
+	default:
+		return;
+	}
 
 	pos += scnprintf(buf + pos, buflen - pos,
 			"%s (%s), addr=0x%05X: s=%s; sent: ",
@@ -438,9 +463,20 @@
 	enum rpmh_state state;
 	u32 sent_mask;
 
-	max_reg_index = aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM
-						? RPMH_REGULATOR_REG_VRM_MAX
-						: RPMH_REGULATOR_REG_ARC_MAX;
+	switch (aggr_vreg->regulator_type) {
+	case RPMH_REGULATOR_TYPE_VRM:
+		max_reg_index = RPMH_REGULATOR_REG_VRM_MAX;
+		break;
+	case RPMH_REGULATOR_TYPE_ARC:
+		max_reg_index = RPMH_REGULATOR_REG_ARC_MAX;
+		break;
+	case RPMH_REGULATOR_TYPE_XOB:
+		max_reg_index = RPMH_REGULATOR_REG_XOB_MAX;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	/*
 	 * Perform max aggregration of each register value across all regulators
 	 * which use this RPMh resource.
@@ -1005,9 +1041,16 @@
 	.list_voltage		= rpmh_regulator_arc_list_voltage,
 };
 
+static const struct regulator_ops rpmh_regulator_xob_ops = {
+	.enable			= rpmh_regulator_enable,
+	.disable		= rpmh_regulator_disable,
+	.is_enabled		= rpmh_regulator_is_enabled,
+};
+
 static const struct regulator_ops *rpmh_regulator_ops[] = {
 	[RPMH_REGULATOR_TYPE_VRM]	= &rpmh_regulator_vrm_ops,
 	[RPMH_REGULATOR_TYPE_ARC]	= &rpmh_regulator_arc_ops,
+	[RPMH_REGULATOR_TYPE_XOB]	= &rpmh_regulator_xob_ops,
 };
 
 /**
@@ -1322,6 +1365,13 @@
 		rc = of_property_read_u32(vreg->of_node, prop, &temp);
 		if (!rc)
 			vreg->rdesc.min_dropout_uV = temp;
+	} else if (type == RPMH_REGULATOR_TYPE_XOB) {
+		prop = "qcom,init-enable";
+		rc = of_property_read_u32(vreg->of_node, prop, &temp);
+		if (!rc)
+			rpmh_regulator_set_reg(vreg,
+						RPMH_REGULATOR_REG_XOB_ENABLE,
+						!!temp);
 	}
 
 	return 0;
@@ -1408,6 +1458,10 @@
 		init_data->constraints.valid_ops_mask
 			|= REGULATOR_CHANGE_VOLTAGE;
 
+	if (type == RPMH_REGULATOR_TYPE_XOB
+	    && init_data->constraints.min_uV == init_data->constraints.max_uV)
+		vreg->rdesc.fixed_uV = init_data->constraints.min_uV;
+
 	if (vreg->aggr_vreg->mode_count) {
 		init_data->constraints.valid_ops_mask
 			|= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
@@ -1434,8 +1488,19 @@
 		init_data->constraints.valid_ops_mask
 			|= REGULATOR_CHANGE_STATUS;
 
-	vreg->rdesc.n_voltages = type == RPMH_REGULATOR_TYPE_ARC ?
-					vreg->aggr_vreg->level_count : 2;
+	switch (type) {
+	case RPMH_REGULATOR_TYPE_VRM:
+		vreg->rdesc.n_voltages = 2;
+		break;
+	case RPMH_REGULATOR_TYPE_ARC:
+		vreg->rdesc.n_voltages = vreg->aggr_vreg->level_count;
+		break;
+	case RPMH_REGULATOR_TYPE_XOB:
+		vreg->rdesc.n_voltages = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	rc = of_property_read_u32(vreg->of_node, "qcom,set", &set);
 	if (rc) {
@@ -1493,6 +1558,10 @@
 		.compatible = "qcom,rpmh-arc-regulator",
 		.data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_ARC,
 	},
+	{
+		.compatible = "qcom,rpmh-xob-regulator",
+		.data = (void *)(uintptr_t)RPMH_REGULATOR_TYPE_XOB,
+	},
 	{}
 };
 
@@ -1570,11 +1639,15 @@
 	if ((aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
 			&& sid != CMD_DB_HW_ARC)
 	    || (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_VRM
-			&& sid != CMD_DB_HW_VRM)) {
+			&& sid != CMD_DB_HW_VRM)
+	    || (aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_XOB
+			&& sid != CMD_DB_HW_XOB)) {
 		aggr_vreg_err(aggr_vreg, "RPMh slave ID mismatch; config=%d (%s) != cmd-db=%d\n",
 			aggr_vreg->regulator_type,
 			aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
-				? "ARC" : "VRM",
+				? "ARC" : (aggr_vreg->regulator_type
+						== RPMH_REGULATOR_TYPE_VRM
+					  ? "VRM" : "XOB"),
 			sid);
 		return -EINVAL;
 	}
@@ -1643,7 +1716,10 @@
 	aggr_vreg_debug(aggr_vreg, "successfully probed; addr=0x%05X, type=%s\n",
 			aggr_vreg->addr,
 			aggr_vreg->regulator_type == RPMH_REGULATOR_TYPE_ARC
-				? "ARC" : "VRM");
+				? "ARC"
+				: (aggr_vreg->regulator_type
+						== RPMH_REGULATOR_TYPE_VRM
+					? "VRM" : "XOB"));
 
 	return rc;
 
diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c
index 47f0ffd..1799fd4 100644
--- a/drivers/reset/reset-ti-syscon.c
+++ b/drivers/reset/reset-ti-syscon.c
@@ -154,8 +154,8 @@
 	if (ret)
 		return ret;
 
-	return (reset_state & BIT(control->status_bit)) &&
-			(control->flags & STATUS_SET);
+	return !(reset_state & BIT(control->status_bit)) ==
+		!(control->flags & STATUS_SET);
 }
 
 static struct reset_control_ops ti_syscon_reset_ops = {
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 84a52db..f1d4ca2 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -372,6 +372,14 @@
 }
 EXPORT_SYMBOL_GPL(rtc_set_alarm);
 
+static void rtc_alarm_disable(struct rtc_device *rtc)
+{
+	if (!rtc->ops || !rtc->ops->alarm_irq_enable)
+		return;
+
+	rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
+}
+
 /* Called once per device from rtc_device_register */
 int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
 {
@@ -399,7 +407,11 @@
 
 		rtc->aie_timer.enabled = 1;
 		timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+	} else if (alarm->enabled && (rtc_tm_to_ktime(now).tv64 >=
+			rtc->aie_timer.node.expires.tv64)){
+		rtc_alarm_disable(rtc);
 	}
+
 	mutex_unlock(&rtc->ops_lock);
 	return err;
 }
@@ -790,14 +802,6 @@
 	return 0;
 }
 
-static void rtc_alarm_disable(struct rtc_device *rtc)
-{
-	if (!rtc->ops || !rtc->ops->alarm_irq_enable)
-		return;
-
-	rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
-}
-
 /**
  * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue
  * @rtc rtc device
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index a2c004e..4152086 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -599,9 +599,6 @@
 		goto fail_rtc_enable;
 	}
 
-	/* Init power_on_alarm after adding rtc device */
-	power_on_alarm_init();
-
 	/* Request the alarm IRQ */
 	rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
 				 qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 1de0890..5ecd408 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -1704,8 +1704,11 @@
 	/* check for for attention message */
 	if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) {
 		device = dasd_device_from_cdev_locked(cdev);
-		device->discipline->check_attention(device, irb->esw.esw1.lpum);
-		dasd_put_device(device);
+		if (!IS_ERR(device)) {
+			device->discipline->check_attention(device,
+							    irb->esw.esw1.lpum);
+			dasd_put_device(device);
+		}
 	}
 
 	if (!cqr)
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index f3756ca..d55e643 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -921,7 +921,6 @@
 int qeth_core_hardsetup_card(struct qeth_card *);
 void qeth_print_status_message(struct qeth_card *);
 int qeth_init_qdio_queues(struct qeth_card *);
-int qeth_send_startlan(struct qeth_card *);
 int qeth_send_ipa_cmd(struct qeth_card *, struct qeth_cmd_buffer *,
 		  int (*reply_cb)
 		  (struct qeth_card *, struct qeth_reply *, unsigned long),
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index e8c4830..21ef802 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2944,7 +2944,7 @@
 }
 EXPORT_SYMBOL_GPL(qeth_send_ipa_cmd);
 
-int qeth_send_startlan(struct qeth_card *card)
+static int qeth_send_startlan(struct qeth_card *card)
 {
 	int rc;
 	struct qeth_cmd_buffer *iob;
@@ -2957,7 +2957,6 @@
 	rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
 	return rc;
 }
-EXPORT_SYMBOL_GPL(qeth_send_startlan);
 
 static int qeth_default_setadapterparms_cb(struct qeth_card *card,
 		struct qeth_reply *reply, unsigned long data)
@@ -5091,6 +5090,20 @@
 		goto out;
 	}
 
+	rc = qeth_send_startlan(card);
+	if (rc) {
+		QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+		if (rc == IPA_RC_LAN_OFFLINE) {
+			dev_warn(&card->gdev->dev,
+				"The LAN is offline\n");
+			card->lan_online = 0;
+		} else {
+			rc = -ENODEV;
+			goto out;
+		}
+	} else
+		card->lan_online = 1;
+
 	card->options.ipa4.supported_funcs = 0;
 	card->options.ipa6.supported_funcs = 0;
 	card->options.adp.supported_funcs = 0;
@@ -5102,14 +5115,14 @@
 	if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) {
 		rc = qeth_query_setadapterparms(card);
 		if (rc < 0) {
-			QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc);
+			QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc);
 			goto out;
 		}
 	}
 	if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) {
 		rc = qeth_query_setdiagass(card);
 		if (rc < 0) {
-			QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc);
+			QETH_DBF_TEXT_(SETUP, 2, "8err%d", rc);
 			goto out;
 		}
 	}
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 5d010aa..8530477 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -1204,21 +1204,6 @@
 	/* softsetup */
 	QETH_DBF_TEXT(SETUP, 2, "softsetp");
 
-	rc = qeth_send_startlan(card);
-	if (rc) {
-		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
-		if (rc == 0xe080) {
-			dev_warn(&card->gdev->dev,
-				"The LAN is offline\n");
-			card->lan_online = 0;
-			goto contin;
-		}
-		rc = -ENODEV;
-		goto out_remove;
-	} else
-		card->lan_online = 1;
-
-contin:
 	if ((card->info.type == QETH_CARD_TYPE_OSD) ||
 	    (card->info.type == QETH_CARD_TYPE_OSX)) {
 		rc = qeth_l2_start_ipassists(card);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 171be5e..03a2619 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -3230,21 +3230,6 @@
 	/* softsetup */
 	QETH_DBF_TEXT(SETUP, 2, "softsetp");
 
-	rc = qeth_send_startlan(card);
-	if (rc) {
-		QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
-		if (rc == 0xe080) {
-			dev_warn(&card->gdev->dev,
-				"The LAN is offline\n");
-			card->lan_online = 0;
-			goto contin;
-		}
-		rc = -ENODEV;
-		goto out_remove;
-	} else
-		card->lan_online = 1;
-
-contin:
 	rc = qeth_l3_setadapter_parms(card);
 	if (rc)
 		QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc);
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index 0e00a5c..cffe42f 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -692,15 +692,15 @@
 			enum qeth_prot_versions proto)
 {
 	struct qeth_ipaddr *ipaddr;
-	struct hlist_node  *tmp;
 	char addr_str[40];
+	int str_len = 0;
 	int entry_len; /* length of 1 entry string, differs between v4 and v6 */
-	int i = 0;
+	int i;
 
 	entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
 	entry_len += 2; /* \n + terminator */
 	spin_lock_bh(&card->ip_lock);
-	hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) {
+	hash_for_each(card->ip_htable, i, ipaddr, hnode) {
 		if (ipaddr->proto != proto)
 			continue;
 		if (ipaddr->type != QETH_IP_TYPE_VIPA)
@@ -708,16 +708,17 @@
 		/* String must not be longer than PAGE_SIZE. So we check if
 		 * string length gets near PAGE_SIZE. Then we can savely display
 		 * the next IPv6 address (worst case, compared to IPv4) */
-		if ((PAGE_SIZE - i) <= entry_len)
+		if ((PAGE_SIZE - str_len) <= entry_len)
 			break;
 		qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
 			addr_str);
-		i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n",
+				    addr_str);
 	}
 	spin_unlock_bh(&card->ip_lock);
-	i += snprintf(buf + i, PAGE_SIZE - i, "\n");
+	str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n");
 
-	return i;
+	return str_len;
 }
 
 static ssize_t qeth_l3_dev_vipa_add4_show(struct device *dev,
@@ -854,15 +855,15 @@
 		       enum qeth_prot_versions proto)
 {
 	struct qeth_ipaddr *ipaddr;
-	struct hlist_node *tmp;
 	char addr_str[40];
+	int str_len = 0;
 	int entry_len; /* length of 1 entry string, differs between v4 and v6 */
-	int i = 0;
+	int i;
 
 	entry_len = (proto == QETH_PROT_IPV4)? 12 : 40;
 	entry_len += 2; /* \n + terminator */
 	spin_lock_bh(&card->ip_lock);
-	hash_for_each_safe(card->ip_htable, i, tmp, ipaddr, hnode) {
+	hash_for_each(card->ip_htable, i, ipaddr, hnode) {
 		if (ipaddr->proto != proto)
 			continue;
 		if (ipaddr->type != QETH_IP_TYPE_RXIP)
@@ -870,16 +871,17 @@
 		/* String must not be longer than PAGE_SIZE. So we check if
 		 * string length gets near PAGE_SIZE. Then we can savely display
 		 * the next IPv6 address (worst case, compared to IPv4) */
-		if ((PAGE_SIZE - i) <= entry_len)
+		if ((PAGE_SIZE - str_len) <= entry_len)
 			break;
 		qeth_l3_ipaddr_to_string(proto, (const u8 *)&ipaddr->u,
 			addr_str);
-		i += snprintf(buf + i, PAGE_SIZE - i, "%s\n", addr_str);
+		str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "%s\n",
+				    addr_str);
 	}
 	spin_unlock_bh(&card->ip_lock);
-	i += snprintf(buf + i, PAGE_SIZE - i, "\n");
+	str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "\n");
 
-	return i;
+	return str_len;
 }
 
 static ssize_t qeth_l3_dev_rxip_add4_show(struct device *dev,
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index bcc8f3d..b3f9243 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -358,6 +358,8 @@
 
 	adapter->next_port_scan = jiffies;
 
+	adapter->erp_action.adapter = adapter;
+
 	if (zfcp_qdio_setup(adapter))
 		goto failed;
 
@@ -514,6 +516,9 @@
 	port->dev.groups = zfcp_port_attr_groups;
 	port->dev.release = zfcp_port_release;
 
+	port->erp_action.adapter = adapter;
+	port->erp_action.port = port;
+
 	if (dev_set_name(&port->dev, "0x%016llx", (unsigned long long)wwpn)) {
 		kfree(port);
 		goto err_out;
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index d5bf36e..34367d1 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
  *
  * Debug traces for zfcp.
  *
- * Copyright IBM Corp. 2002, 2016
+ * Copyright IBM Corp. 2002, 2017
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -447,6 +447,7 @@
 	struct fc_ct_hdr *reqh = sg_virt(ct_els->req);
 	struct fc_ns_gid_ft *reqn = (struct fc_ns_gid_ft *)(reqh + 1);
 	struct scatterlist *resp_entry = ct_els->resp;
+	struct fc_ct_hdr *resph;
 	struct fc_gpn_ft_resp *acc;
 	int max_entries, x, last = 0;
 
@@ -473,6 +474,13 @@
 		return len; /* not GPN_FT response so do not cap */
 
 	acc = sg_virt(resp_entry);
+
+	/* cap all but accept CT responses to at least the CT header */
+	resph = (struct fc_ct_hdr *)acc;
+	if ((ct_els->status) ||
+	    (resph->ct_cmd != cpu_to_be16(FC_FS_ACC)))
+		return max(FC_CT_HDR_LEN, ZFCP_DBF_SAN_MAX_PAYLOAD);
+
 	max_entries = (reqh->ct_mr_size * 4 / sizeof(struct fc_gpn_ft_resp))
 		+ 1 /* zfcp_fc_scan_ports: bytes correct, entries off-by-one
 		     * to account for header as 1st pseudo "entry" */;
@@ -555,8 +563,8 @@
 	rec->scsi_retries = sc->retries;
 	rec->scsi_allowed = sc->allowed;
 	rec->scsi_id = sc->device->id;
-	/* struct zfcp_dbf_scsi needs to be updated to handle 64bit LUNs */
 	rec->scsi_lun = (u32)sc->device->lun;
+	rec->scsi_lun_64_hi = (u32)(sc->device->lun >> 32);
 	rec->host_scribble = (unsigned long)sc->host_scribble;
 
 	memcpy(rec->scsi_opcode, sc->cmnd,
@@ -564,19 +572,32 @@
 
 	if (fsf) {
 		rec->fsf_req_id = fsf->req_id;
+		rec->pl_len = FCP_RESP_WITH_EXT;
 		fcp_rsp = (struct fcp_resp_with_ext *)
 				&(fsf->qtcb->bottom.io.fcp_rsp);
+		/* mandatory parts of FCP_RSP IU in this SCSI record */
 		memcpy(&rec->fcp_rsp, fcp_rsp, FCP_RESP_WITH_EXT);
 		if (fcp_rsp->resp.fr_flags & FCP_RSP_LEN_VAL) {
 			fcp_rsp_info = (struct fcp_resp_rsp_info *) &fcp_rsp[1];
 			rec->fcp_rsp_info = fcp_rsp_info->rsp_code;
+			rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_rsp_len);
 		}
 		if (fcp_rsp->resp.fr_flags & FCP_SNS_LEN_VAL) {
-			rec->pl_len = min((u16)SCSI_SENSE_BUFFERSIZE,
-					  (u16)ZFCP_DBF_PAY_MAX_REC);
-			zfcp_dbf_pl_write(dbf, sc->sense_buffer, rec->pl_len,
-					  "fcp_sns", fsf->req_id);
+			rec->pl_len += be32_to_cpu(fcp_rsp->ext.fr_sns_len);
 		}
+		/* complete FCP_RSP IU in associated PAYload record
+		 * but only if there are optional parts
+		 */
+		if (fcp_rsp->resp.fr_flags != 0)
+			zfcp_dbf_pl_write(
+				dbf, fcp_rsp,
+				/* at least one full PAY record
+				 * but not beyond hardware response field
+				 */
+				min_t(u16, max_t(u16, rec->pl_len,
+						 ZFCP_DBF_PAY_MAX_REC),
+				      FSF_FCP_RSP_SIZE),
+				"fcp_riu", fsf->req_id);
 	}
 
 	debug_event(dbf->scsi, level, rec, sizeof(*rec));
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index db186d4..b60667c 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
  * zfcp device driver
  * debug feature declarations
  *
- * Copyright IBM Corp. 2008, 2016
+ * Copyright IBM Corp. 2008, 2017
  */
 
 #ifndef ZFCP_DBF_H
@@ -204,7 +204,7 @@
  * @id: unique number of recovery record type
  * @tag: identifier string specifying the location of initiation
  * @scsi_id: scsi device id
- * @scsi_lun: scsi device logical unit number
+ * @scsi_lun: scsi device logical unit number, low part of 64 bit, old 32 bit
  * @scsi_result: scsi result
  * @scsi_retries: current retry number of scsi request
  * @scsi_allowed: allowed retries
@@ -214,6 +214,7 @@
  * @host_scribble: LLD specific data attached to SCSI request
  * @pl_len: length of paload stored as zfcp_dbf_pay
  * @fsf_rsp: response for fsf request
+ * @scsi_lun_64_hi: scsi device logical unit number, high part of 64 bit
  */
 struct zfcp_dbf_scsi {
 	u8 id;
@@ -230,6 +231,7 @@
 	u64 host_scribble;
 	u16 pl_len;
 	struct fcp_resp_with_ext fcp_rsp;
+	u32 scsi_lun_64_hi;
 } __packed;
 
 /**
@@ -323,7 +325,11 @@
 {
 	struct fsf_qtcb *qtcb = req->qtcb;
 
-	if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
+	if (unlikely(req->status & (ZFCP_STATUS_FSFREQ_DISMISSED |
+				    ZFCP_STATUS_FSFREQ_ERROR))) {
+		zfcp_dbf_hba_fsf_resp("fs_rerr", 3, req);
+
+	} else if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
 	    (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
 		zfcp_dbf_hba_fsf_resp("fs_perr", 1, req);
 
@@ -401,7 +407,8 @@
  * @flag: indicates type of reset (Target Reset, Logical Unit Reset)
  */
 static inline
-void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag)
+void zfcp_dbf_scsi_devreset(char *tag, struct scsi_cmnd *scmnd, u8 flag,
+			    struct zfcp_fsf_req *fsf_req)
 {
 	char tmp_tag[ZFCP_DBF_TAG_LEN];
 
@@ -411,7 +418,7 @@
 		memcpy(tmp_tag, "lr_", 3);
 
 	memcpy(&tmp_tag[3], tag, 4);
-	_zfcp_dbf_scsi(tmp_tag, 1, scmnd, NULL);
+	_zfcp_dbf_scsi(tmp_tag, 1, scmnd, fsf_req);
 }
 
 /**
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 7ccfce5..3b23d675 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -193,9 +193,8 @@
 		atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE,
 				&zfcp_sdev->status);
 		erp_action = &zfcp_sdev->erp_action;
-		memset(erp_action, 0, sizeof(struct zfcp_erp_action));
-		erp_action->port = port;
-		erp_action->sdev = sdev;
+		WARN_ON_ONCE(erp_action->port != port);
+		WARN_ON_ONCE(erp_action->sdev != sdev);
 		if (!(atomic_read(&zfcp_sdev->status) &
 		      ZFCP_STATUS_COMMON_RUNNING))
 			act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
@@ -208,8 +207,8 @@
 		zfcp_erp_action_dismiss_port(port);
 		atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status);
 		erp_action = &port->erp_action;
-		memset(erp_action, 0, sizeof(struct zfcp_erp_action));
-		erp_action->port = port;
+		WARN_ON_ONCE(erp_action->port != port);
+		WARN_ON_ONCE(erp_action->sdev != NULL);
 		if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING))
 			act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
 		break;
@@ -219,7 +218,8 @@
 		zfcp_erp_action_dismiss_adapter(adapter);
 		atomic_or(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status);
 		erp_action = &adapter->erp_action;
-		memset(erp_action, 0, sizeof(struct zfcp_erp_action));
+		WARN_ON_ONCE(erp_action->port != NULL);
+		WARN_ON_ONCE(erp_action->sdev != NULL);
 		if (!(atomic_read(&adapter->status) &
 		      ZFCP_STATUS_COMMON_RUNNING))
 			act_status |= ZFCP_STATUS_ERP_CLOSE_ONLY;
@@ -229,7 +229,11 @@
 		return NULL;
 	}
 
-	erp_action->adapter = adapter;
+	WARN_ON_ONCE(erp_action->adapter != adapter);
+	memset(&erp_action->list, 0, sizeof(erp_action->list));
+	memset(&erp_action->timer, 0, sizeof(erp_action->timer));
+	erp_action->step = ZFCP_ERP_STEP_UNINITIALIZED;
+	erp_action->fsf_req_id = 0;
 	erp_action->action = need;
 	erp_action->status = act_status;
 
diff --git a/drivers/s390/scsi/zfcp_fc.h b/drivers/s390/scsi/zfcp_fc.h
index df2b541..a227582 100644
--- a/drivers/s390/scsi/zfcp_fc.h
+++ b/drivers/s390/scsi/zfcp_fc.h
@@ -4,7 +4,7 @@
  * Fibre Channel related definitions and inline functions for the zfcp
  * device driver
  *
- * Copyright IBM Corp. 2009
+ * Copyright IBM Corp. 2009, 2017
  */
 
 #ifndef ZFCP_FC_H
@@ -279,6 +279,10 @@
 		     !(rsp_flags & FCP_SNS_LEN_VAL) &&
 		     fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
 			set_host_byte(scsi, DID_ERROR);
+	} else if (unlikely(rsp_flags & FCP_RESID_OVER)) {
+		/* FCP_DL was not sufficient for SCSI data length */
+		if (fcp_rsp->resp.fr_status == SAM_STAT_GOOD)
+			set_host_byte(scsi, DID_ERROR);
 	}
 }
 
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 27ff38f..1964391 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -928,8 +928,8 @@
 
 	switch (header->fsf_status) {
         case FSF_GOOD:
-		zfcp_dbf_san_res("fsscth2", req);
 		ct->status = 0;
+		zfcp_dbf_san_res("fsscth2", req);
 		break;
         case FSF_SERVICE_CLASS_NOT_SUPPORTED:
 		zfcp_fsf_class_not_supp(req);
@@ -1109,8 +1109,8 @@
 
 	switch (header->fsf_status) {
 	case FSF_GOOD:
-		zfcp_dbf_san_res("fsselh1", req);
 		send_els->status = 0;
+		zfcp_dbf_san_res("fsselh1", req);
 		break;
 	case FSF_SERVICE_CLASS_NOT_SUPPORTED:
 		zfcp_fsf_class_not_supp(req);
@@ -2258,7 +2258,8 @@
 	fcp_cmnd = (struct fcp_cmnd *) &req->qtcb->bottom.io.fcp_cmnd;
 	zfcp_fc_scsi_to_fcp(fcp_cmnd, scsi_cmnd, 0);
 
-	if (scsi_prot_sg_count(scsi_cmnd)) {
+	if ((scsi_get_prot_op(scsi_cmnd) != SCSI_PROT_NORMAL) &&
+	    scsi_prot_sg_count(scsi_cmnd)) {
 		zfcp_qdio_set_data_div(qdio, &req->qdio_req,
 				       scsi_prot_sg_count(scsi_cmnd));
 		retval = zfcp_qdio_sbals_from_sg(qdio, &req->qdio_req,
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 07ffdbb..a9b8104 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -3,7 +3,7 @@
  *
  * Interface to Linux SCSI midlayer.
  *
- * Copyright IBM Corp. 2002, 2016
+ * Copyright IBM Corp. 2002, 2017
  */
 
 #define KMSG_COMPONENT "zfcp"
@@ -115,10 +115,15 @@
 	struct zfcp_unit *unit;
 	int npiv = adapter->connection_features & FSF_FEATURE_NPIV_MODE;
 
+	zfcp_sdev->erp_action.adapter = adapter;
+	zfcp_sdev->erp_action.sdev = sdev;
+
 	port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
 	if (!port)
 		return -ENXIO;
 
+	zfcp_sdev->erp_action.port = port;
+
 	unit = zfcp_unit_find(port, zfcp_scsi_dev_lun(sdev));
 	if (unit)
 		put_device(&unit->dev);
@@ -273,25 +278,29 @@
 
 		zfcp_erp_wait(adapter);
 		ret = fc_block_scsi_eh(scpnt);
-		if (ret)
+		if (ret) {
+			zfcp_dbf_scsi_devreset("fiof", scpnt, tm_flags, NULL);
 			return ret;
+		}
 
 		if (!(atomic_read(&adapter->status) &
 		      ZFCP_STATUS_COMMON_RUNNING)) {
-			zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags);
+			zfcp_dbf_scsi_devreset("nres", scpnt, tm_flags, NULL);
 			return SUCCESS;
 		}
 	}
-	if (!fsf_req)
+	if (!fsf_req) {
+		zfcp_dbf_scsi_devreset("reqf", scpnt, tm_flags, NULL);
 		return FAILED;
+	}
 
 	wait_for_completion(&fsf_req->completion);
 
 	if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
-		zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags);
+		zfcp_dbf_scsi_devreset("fail", scpnt, tm_flags, fsf_req);
 		retval = FAILED;
 	} else {
-		zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags);
+		zfcp_dbf_scsi_devreset("okay", scpnt, tm_flags, fsf_req);
 		zfcp_scsi_forget_cmnds(zfcp_sdev, tm_flags);
 	}
 
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 6678d1f..065f11a 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -2954,16 +2954,11 @@
 		return;
 
 	BUG_ON(fibptr == NULL);
+
 	dev = fibptr->dev;
 
-	scsi_dma_unmap(scsicmd);
-
-	/* expose physical device if expose_physicald flag is on */
-	if (scsicmd->cmnd[0] == INQUIRY && !(scsicmd->cmnd[1] & 0x01)
-	  && expose_physicals > 0)
-		aac_expose_phy_device(scsicmd);
-
 	srbreply = (struct aac_srb_reply *) fib_data(fibptr);
+
 	scsicmd->sense_buffer[0] = '\0';  /* Initialize sense valid flag to false */
 
 	if (fibptr->flags & FIB_CONTEXT_FLAG_FASTRESP) {
@@ -2976,158 +2971,176 @@
 		 */
 		scsi_set_resid(scsicmd, scsi_bufflen(scsicmd)
 				   - le32_to_cpu(srbreply->data_xfer_length));
-		/*
-		 * First check the fib status
-		 */
+	}
 
-		if (le32_to_cpu(srbreply->status) != ST_OK) {
-			int len;
 
-			printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
-			len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
-				    SCSI_SENSE_BUFFERSIZE);
+	scsi_dma_unmap(scsicmd);
+
+	/* expose physical device if expose_physicald flag is on */
+	if (scsicmd->cmnd[0] == INQUIRY && !(scsicmd->cmnd[1] & 0x01)
+	  && expose_physicals > 0)
+		aac_expose_phy_device(scsicmd);
+
+	/*
+	 * First check the fib status
+	 */
+
+	if (le32_to_cpu(srbreply->status) != ST_OK) {
+		int len;
+
+		pr_warn("aac_srb_callback: srb failed, status = %d\n",
+				le32_to_cpu(srbreply->status));
+		len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
+			    SCSI_SENSE_BUFFERSIZE);
+		scsicmd->result = DID_ERROR << 16
+				| COMMAND_COMPLETE << 8
+				| SAM_STAT_CHECK_CONDITION;
+		memcpy(scsicmd->sense_buffer,
+				srbreply->sense_data, len);
+	}
+
+	/*
+	 * Next check the srb status
+	 */
+	switch ((le32_to_cpu(srbreply->srb_status))&0x3f) {
+	case SRB_STATUS_ERROR_RECOVERY:
+	case SRB_STATUS_PENDING:
+	case SRB_STATUS_SUCCESS:
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		break;
+	case SRB_STATUS_DATA_OVERRUN:
+		switch (scsicmd->cmnd[0]) {
+		case  READ_6:
+		case  WRITE_6:
+		case  READ_10:
+		case  WRITE_10:
+		case  READ_12:
+		case  WRITE_12:
+		case  READ_16:
+		case  WRITE_16:
+			if (le32_to_cpu(srbreply->data_xfer_length)
+						< scsicmd->underflow)
+				pr_warn("aacraid: SCSI CMD underflow\n");
+			else
+				pr_warn("aacraid: SCSI CMD Data Overrun\n");
 			scsicmd->result = DID_ERROR << 16
-						| COMMAND_COMPLETE << 8
-						| SAM_STAT_CHECK_CONDITION;
-			memcpy(scsicmd->sense_buffer,
-					srbreply->sense_data, len);
-		}
-
-		/*
-		 * Next check the srb status
-		 */
-		switch ((le32_to_cpu(srbreply->srb_status))&0x3f) {
-		case SRB_STATUS_ERROR_RECOVERY:
-		case SRB_STATUS_PENDING:
-		case SRB_STATUS_SUCCESS:
+					| COMMAND_COMPLETE << 8;
+			break;
+		case INQUIRY:
+			scsicmd->result = DID_OK << 16
+					| COMMAND_COMPLETE << 8;
+			break;
+		default:
 			scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
 			break;
-		case SRB_STATUS_DATA_OVERRUN:
-			switch (scsicmd->cmnd[0]) {
-			case  READ_6:
-			case  WRITE_6:
-			case  READ_10:
-			case  WRITE_10:
-			case  READ_12:
-			case  WRITE_12:
-			case  READ_16:
-			case  WRITE_16:
-				if (le32_to_cpu(srbreply->data_xfer_length)
-							< scsicmd->underflow)
-					printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
-				else
-					printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
-				scsicmd->result = DID_ERROR << 16
-							| COMMAND_COMPLETE << 8;
-				break;
-			case INQUIRY: {
-				scsicmd->result = DID_OK << 16
-							| COMMAND_COMPLETE << 8;
-				break;
-			}
-			default:
-				scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
-				break;
-			}
-			break;
-		case SRB_STATUS_ABORTED:
-			scsicmd->result = DID_ABORT << 16 | ABORT << 8;
-			break;
-		case SRB_STATUS_ABORT_FAILED:
-			/*
-			 * Not sure about this one - but assuming the
-			 * hba was trying to abort for some reason
-			 */
-			scsicmd->result = DID_ERROR << 16 | ABORT << 8;
-			break;
-		case SRB_STATUS_PARITY_ERROR:
-			scsicmd->result = DID_PARITY << 16
-						| MSG_PARITY_ERROR << 8;
-			break;
-		case SRB_STATUS_NO_DEVICE:
-		case SRB_STATUS_INVALID_PATH_ID:
-		case SRB_STATUS_INVALID_TARGET_ID:
-		case SRB_STATUS_INVALID_LUN:
-		case SRB_STATUS_SELECTION_TIMEOUT:
-			scsicmd->result = DID_NO_CONNECT << 16
-						| COMMAND_COMPLETE << 8;
-			break;
+		}
+		break;
+	case SRB_STATUS_ABORTED:
+		scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+		break;
+	case SRB_STATUS_ABORT_FAILED:
+		/*
+		 * Not sure about this one - but assuming the
+		 * hba was trying to abort for some reason
+		 */
+		scsicmd->result = DID_ERROR << 16 | ABORT << 8;
+		break;
+	case SRB_STATUS_PARITY_ERROR:
+		scsicmd->result = DID_PARITY << 16
+				| MSG_PARITY_ERROR << 8;
+		break;
+	case SRB_STATUS_NO_DEVICE:
+	case SRB_STATUS_INVALID_PATH_ID:
+	case SRB_STATUS_INVALID_TARGET_ID:
+	case SRB_STATUS_INVALID_LUN:
+	case SRB_STATUS_SELECTION_TIMEOUT:
+		scsicmd->result = DID_NO_CONNECT << 16
+				| COMMAND_COMPLETE << 8;
+		break;
 
-		case SRB_STATUS_COMMAND_TIMEOUT:
-		case SRB_STATUS_TIMEOUT:
-			scsicmd->result = DID_TIME_OUT << 16
-						| COMMAND_COMPLETE << 8;
-			break;
+	case SRB_STATUS_COMMAND_TIMEOUT:
+	case SRB_STATUS_TIMEOUT:
+		scsicmd->result = DID_TIME_OUT << 16
+				| COMMAND_COMPLETE << 8;
+		break;
 
-		case SRB_STATUS_BUSY:
-			scsicmd->result = DID_BUS_BUSY << 16
-						| COMMAND_COMPLETE << 8;
-			break;
+	case SRB_STATUS_BUSY:
+		scsicmd->result = DID_BUS_BUSY << 16
+				| COMMAND_COMPLETE << 8;
+		break;
 
-		case SRB_STATUS_BUS_RESET:
-			scsicmd->result = DID_RESET << 16
-						| COMMAND_COMPLETE << 8;
-			break;
+	case SRB_STATUS_BUS_RESET:
+		scsicmd->result = DID_RESET << 16
+				| COMMAND_COMPLETE << 8;
+		break;
 
-		case SRB_STATUS_MESSAGE_REJECTED:
-			scsicmd->result = DID_ERROR << 16
-						| MESSAGE_REJECT << 8;
-			break;
-		case SRB_STATUS_REQUEST_FLUSHED:
-		case SRB_STATUS_ERROR:
-		case SRB_STATUS_INVALID_REQUEST:
-		case SRB_STATUS_REQUEST_SENSE_FAILED:
-		case SRB_STATUS_NO_HBA:
-		case SRB_STATUS_UNEXPECTED_BUS_FREE:
-		case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
-		case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
-		case SRB_STATUS_DELAYED_RETRY:
-		case SRB_STATUS_BAD_FUNCTION:
-		case SRB_STATUS_NOT_STARTED:
-		case SRB_STATUS_NOT_IN_USE:
-		case SRB_STATUS_FORCE_ABORT:
-		case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
-		default:
+	case SRB_STATUS_MESSAGE_REJECTED:
+		scsicmd->result = DID_ERROR << 16
+				| MESSAGE_REJECT << 8;
+		break;
+	case SRB_STATUS_REQUEST_FLUSHED:
+	case SRB_STATUS_ERROR:
+	case SRB_STATUS_INVALID_REQUEST:
+	case SRB_STATUS_REQUEST_SENSE_FAILED:
+	case SRB_STATUS_NO_HBA:
+	case SRB_STATUS_UNEXPECTED_BUS_FREE:
+	case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
+	case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
+	case SRB_STATUS_DELAYED_RETRY:
+	case SRB_STATUS_BAD_FUNCTION:
+	case SRB_STATUS_NOT_STARTED:
+	case SRB_STATUS_NOT_IN_USE:
+	case SRB_STATUS_FORCE_ABORT:
+	case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
+	default:
 #ifdef AAC_DETAILED_STATUS_INFO
-			printk(KERN_INFO "aacraid: SRB ERROR(%u) %s scsi cmd 0x%x - scsi status 0x%x\n",
-				le32_to_cpu(srbreply->srb_status) & 0x3F,
-				aac_get_status_string(
-					le32_to_cpu(srbreply->srb_status) & 0x3F),
-				scsicmd->cmnd[0],
-				le32_to_cpu(srbreply->scsi_status));
+		pr_info("aacraid: SRB ERROR(%u) %s scsi cmd 0x%x -scsi status 0x%x\n",
+			le32_to_cpu(srbreply->srb_status) & 0x3F,
+			aac_get_status_string(
+				le32_to_cpu(srbreply->srb_status) & 0x3F),
+			scsicmd->cmnd[0],
+			le32_to_cpu(srbreply->scsi_status));
 #endif
-			if ((scsicmd->cmnd[0] == ATA_12)
-				|| (scsicmd->cmnd[0] == ATA_16)) {
-					if (scsicmd->cmnd[2] & (0x01 << 5)) {
-						scsicmd->result = DID_OK << 16
-							| COMMAND_COMPLETE << 8;
-				break;
-				} else {
-					scsicmd->result = DID_ERROR << 16
-						| COMMAND_COMPLETE << 8;
-					break;
-				}
+		/*
+		 * When the CC bit is SET by the host in ATA pass thru CDB,
+		 *  driver is supposed to return DID_OK
+		 *
+		 * When the CC bit is RESET by the host, driver should
+		 *  return DID_ERROR
+		 */
+		if ((scsicmd->cmnd[0] == ATA_12)
+			|| (scsicmd->cmnd[0] == ATA_16)) {
+
+			if (scsicmd->cmnd[2] & (0x01 << 5)) {
+				scsicmd->result = DID_OK << 16
+					| COMMAND_COMPLETE << 8;
+			break;
 			} else {
 				scsicmd->result = DID_ERROR << 16
 					| COMMAND_COMPLETE << 8;
-				break;
+			break;
 			}
-		}
-		if (le32_to_cpu(srbreply->scsi_status)
-				== SAM_STAT_CHECK_CONDITION) {
-			int len;
-
-			scsicmd->result |= SAM_STAT_CHECK_CONDITION;
-			len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
-				    SCSI_SENSE_BUFFERSIZE);
-#ifdef AAC_DETAILED_STATUS_INFO
-			printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n",
-						le32_to_cpu(srbreply->status), len);
-#endif
-			memcpy(scsicmd->sense_buffer,
-					srbreply->sense_data, len);
+		} else {
+			scsicmd->result = DID_ERROR << 16
+				| COMMAND_COMPLETE << 8;
+			break;
 		}
 	}
+	if (le32_to_cpu(srbreply->scsi_status)
+			== SAM_STAT_CHECK_CONDITION) {
+		int len;
+
+		scsicmd->result |= SAM_STAT_CHECK_CONDITION;
+		len = min_t(u32, le32_to_cpu(srbreply->sense_data_size),
+			    SCSI_SENSE_BUFFERSIZE);
+#ifdef AAC_DETAILED_STATUS_INFO
+		pr_warn("aac_srb_callback: check condition, status = %d len=%d\n",
+					le32_to_cpu(srbreply->status), len);
+#endif
+		memcpy(scsicmd->sense_buffer,
+				srbreply->sense_data, len);
+	}
+
 	/*
 	 * OR in the scsi status (already shifted up a bit)
 	 */
diff --git a/drivers/scsi/be2iscsi/be_iscsi.c b/drivers/scsi/be2iscsi/be_iscsi.c
index ba25821..963c732 100644
--- a/drivers/scsi/be2iscsi/be_iscsi.c
+++ b/drivers/scsi/be2iscsi/be_iscsi.c
@@ -166,33 +166,6 @@
 }
 
 /**
- * beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
- * @beiscsi_conn: The pointer to  beiscsi_conn structure
- * @phba: The phba instance
- * @cid: The cid to free
- */
-static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
-				struct beiscsi_conn *beiscsi_conn,
-				unsigned int cid)
-{
-	uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
-
-	if (phba->conn_table[cri_index]) {
-		beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
-			    "BS_%d : Connection table already occupied. Detected clash\n");
-
-		return -EINVAL;
-	} else {
-		beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
-			    "BS_%d : phba->conn_table[%d]=%p(beiscsi_conn)\n",
-			    cri_index, beiscsi_conn);
-
-		phba->conn_table[cri_index] = beiscsi_conn;
-	}
-	return 0;
-}
-
-/**
  * beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
  * @cls_session: pointer to iscsi cls session
  * @cls_conn: pointer to iscsi cls conn
@@ -212,6 +185,7 @@
 	struct hwi_wrb_context *pwrb_context;
 	struct beiscsi_endpoint *beiscsi_ep;
 	struct iscsi_endpoint *ep;
+	uint16_t cri_index;
 
 	ep = iscsi_lookup_endpoint(transport_fd);
 	if (!ep)
@@ -229,20 +203,34 @@
 
 		return -EEXIST;
 	}
-
-	pwrb_context = &phwi_ctrlr->wrb_context[BE_GET_CRI_FROM_CID(
-						beiscsi_ep->ep_cid)];
+	cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+	if (phba->conn_table[cri_index]) {
+		if (beiscsi_conn != phba->conn_table[cri_index] ||
+		    beiscsi_ep != phba->conn_table[cri_index]->ep) {
+			__beiscsi_log(phba, KERN_ERR,
+				      "BS_%d : conn_table not empty at %u: cid %u conn %p:%p\n",
+				      cri_index,
+				      beiscsi_ep->ep_cid,
+				      beiscsi_conn,
+				      phba->conn_table[cri_index]);
+			return -EINVAL;
+		}
+	}
 
 	beiscsi_conn->beiscsi_conn_cid = beiscsi_ep->ep_cid;
 	beiscsi_conn->ep = beiscsi_ep;
 	beiscsi_ep->conn = beiscsi_conn;
+	/**
+	 * Each connection is associated with a WRBQ kept in wrb_context.
+	 * Store doorbell offset for transmit path.
+	 */
+	pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
 	beiscsi_conn->doorbell_offset = pwrb_context->doorbell_offset;
-
 	beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
-		    "BS_%d : beiscsi_conn=%p conn=%p ep_cid=%d\n",
-		    beiscsi_conn, conn, beiscsi_ep->ep_cid);
-
-	return beiscsi_bindconn_cid(phba, beiscsi_conn, beiscsi_ep->ep_cid);
+		    "BS_%d : cid %d phba->conn_table[%u]=%p\n",
+		    beiscsi_ep->ep_cid, cri_index, beiscsi_conn);
+	phba->conn_table[cri_index] = beiscsi_conn;
+	return 0;
 }
 
 static int beiscsi_iface_create_ipv4(struct beiscsi_hba *phba)
@@ -973,9 +961,9 @@
  */
 static int beiscsi_get_cid(struct beiscsi_hba *phba)
 {
-	unsigned short cid = 0xFFFF, cid_from_ulp;
-	struct ulp_cid_info *cid_info = NULL;
 	uint16_t cid_avlbl_ulp0, cid_avlbl_ulp1;
+	unsigned short cid, cid_from_ulp;
+	struct ulp_cid_info *cid_info;
 
 	/* Find the ULP which has more CID available */
 	cid_avlbl_ulp0 = (phba->cid_array_info[BEISCSI_ULP0]) ?
@@ -984,20 +972,27 @@
 			  BEISCSI_ULP1_AVLBL_CID(phba) : 0;
 	cid_from_ulp = (cid_avlbl_ulp0 > cid_avlbl_ulp1) ?
 			BEISCSI_ULP0 : BEISCSI_ULP1;
+	/**
+	 * If iSCSI protocol is loaded only on ULP 0, and when cid_avlbl_ulp
+	 * is ZERO for both, ULP 1 is returned.
+	 * Check if ULP is loaded before getting new CID.
+	 */
+	if (!test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported))
+		return BE_INVALID_CID;
 
-	if (test_bit(cid_from_ulp, (void *)&phba->fw_config.ulp_supported)) {
-		cid_info = phba->cid_array_info[cid_from_ulp];
-		if (!cid_info->avlbl_cids)
-			return cid;
-
-		cid = cid_info->cid_array[cid_info->cid_alloc++];
-
-		if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(
-					   phba, cid_from_ulp))
-			cid_info->cid_alloc = 0;
-
-		cid_info->avlbl_cids--;
+	cid_info = phba->cid_array_info[cid_from_ulp];
+	cid = cid_info->cid_array[cid_info->cid_alloc];
+	if (!cid_info->avlbl_cids || cid == BE_INVALID_CID) {
+		__beiscsi_log(phba, KERN_ERR,
+				"BS_%d : failed to get cid: available %u:%u\n",
+				cid_info->avlbl_cids, cid_info->cid_free);
+		return BE_INVALID_CID;
 	}
+	/* empty the slot */
+	cid_info->cid_array[cid_info->cid_alloc++] = BE_INVALID_CID;
+	if (cid_info->cid_alloc == BEISCSI_GET_CID_COUNT(phba, cid_from_ulp))
+		cid_info->cid_alloc = 0;
+	cid_info->avlbl_cids--;
 	return cid;
 }
 
@@ -1008,22 +1003,28 @@
  */
 static void beiscsi_put_cid(struct beiscsi_hba *phba, unsigned short cid)
 {
-	uint16_t cid_post_ulp;
-	struct hwi_controller *phwi_ctrlr;
-	struct hwi_wrb_context *pwrb_context;
-	struct ulp_cid_info *cid_info = NULL;
 	uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
+	struct hwi_wrb_context *pwrb_context;
+	struct hwi_controller *phwi_ctrlr;
+	struct ulp_cid_info *cid_info;
+	uint16_t cid_post_ulp;
 
 	phwi_ctrlr = phba->phwi_ctrlr;
 	pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
 	cid_post_ulp = pwrb_context->ulp_num;
 
 	cid_info = phba->cid_array_info[cid_post_ulp];
-	cid_info->avlbl_cids++;
-
+	/* fill only in empty slot */
+	if (cid_info->cid_array[cid_info->cid_free] != BE_INVALID_CID) {
+		__beiscsi_log(phba, KERN_ERR,
+			      "BS_%d : failed to put cid %u: available %u:%u\n",
+			      cid, cid_info->avlbl_cids, cid_info->cid_free);
+		return;
+	}
 	cid_info->cid_array[cid_info->cid_free++] = cid;
 	if (cid_info->cid_free == BEISCSI_GET_CID_COUNT(phba, cid_post_ulp))
 		cid_info->cid_free = 0;
+	cid_info->avlbl_cids++;
 }
 
 /**
@@ -1037,8 +1038,8 @@
 
 	beiscsi_put_cid(phba, beiscsi_ep->ep_cid);
 	beiscsi_ep->phba = NULL;
-	phba->ep_array[BE_GET_CRI_FROM_CID
-		       (beiscsi_ep->ep_cid)] = NULL;
+	/* clear this to track freeing in beiscsi_ep_disconnect */
+	phba->ep_array[BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid)] = NULL;
 
 	/**
 	 * Check if any connection resource allocated by driver
@@ -1049,6 +1050,11 @@
 		return;
 
 	beiscsi_conn = beiscsi_ep->conn;
+	/**
+	 * Break ep->conn link here so that completions after
+	 * this are ignored.
+	 */
+	beiscsi_ep->conn = NULL;
 	if (beiscsi_conn->login_in_progress) {
 		beiscsi_free_mgmt_task_handles(beiscsi_conn,
 					       beiscsi_conn->task);
@@ -1079,7 +1085,7 @@
 		    "BS_%d : In beiscsi_open_conn\n");
 
 	beiscsi_ep->ep_cid = beiscsi_get_cid(phba);
-	if (beiscsi_ep->ep_cid == 0xFFFF) {
+	if (beiscsi_ep->ep_cid == BE_INVALID_CID) {
 		beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_CONFIG,
 			    "BS_%d : No free cid available\n");
 		return ret;
@@ -1285,26 +1291,6 @@
 }
 
 /**
- * beiscsi_unbind_conn_to_cid - Unbind the beiscsi_conn from phba conn table
- * @phba: The phba instance
- * @cid: The cid to free
- */
-static int beiscsi_unbind_conn_to_cid(struct beiscsi_hba *phba,
-				      unsigned int cid)
-{
-	uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);
-
-	if (phba->conn_table[cri_index])
-		phba->conn_table[cri_index] = NULL;
-	else {
-		beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
-			    "BS_%d : Connection table Not occupied.\n");
-		return -EINVAL;
-	}
-	return 0;
-}
-
-/**
  * beiscsi_ep_disconnect - Tears down the TCP connection
  * @ep:	endpoint to be used
  *
@@ -1318,13 +1304,23 @@
 	unsigned int tag;
 	uint8_t mgmt_invalidate_flag, tcp_upload_flag;
 	unsigned short savecfg_flag = CMD_ISCSI_SESSION_SAVE_CFG_ON_FLASH;
+	uint16_t cri_index;
 
 	beiscsi_ep = ep->dd_data;
 	phba = beiscsi_ep->phba;
 	beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
-		    "BS_%d : In beiscsi_ep_disconnect for ep_cid = %d\n",
+		    "BS_%d : In beiscsi_ep_disconnect for ep_cid = %u\n",
 		    beiscsi_ep->ep_cid);
 
+	cri_index = BE_GET_CRI_FROM_CID(beiscsi_ep->ep_cid);
+	if (!phba->ep_array[cri_index]) {
+		__beiscsi_log(phba, KERN_ERR,
+			      "BS_%d : ep_array at %u cid %u empty\n",
+			      cri_index,
+			      beiscsi_ep->ep_cid);
+		return;
+	}
+
 	if (beiscsi_ep->conn) {
 		beiscsi_conn = beiscsi_ep->conn;
 		iscsi_suspend_queue(beiscsi_conn->conn);
@@ -1356,7 +1352,12 @@
 free_ep:
 	msleep(BEISCSI_LOGOUT_SYNC_DELAY);
 	beiscsi_free_ep(beiscsi_ep);
-	beiscsi_unbind_conn_to_cid(phba, beiscsi_ep->ep_cid);
+	if (!phba->conn_table[cri_index])
+		__beiscsi_log(phba, KERN_ERR,
+				"BS_%d : conn_table empty at %u: cid %u\n",
+				cri_index,
+				beiscsi_ep->ep_cid);
+	phba->conn_table[cri_index] = NULL;
 	iscsi_destroy_endpoint(beiscsi_ep->openiscsi_ep);
 }
 
diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c
index d9239c2..741cc96 100644
--- a/drivers/scsi/be2iscsi/be_main.c
+++ b/drivers/scsi/be2iscsi/be_main.c
@@ -4085,9 +4085,10 @@
 			}
 
 			/* Allocate memory for CID array */
-			ptr_cid_info->cid_array = kzalloc(sizeof(void *) *
-						  BEISCSI_GET_CID_COUNT(phba,
-						  ulp_num), GFP_KERNEL);
+			ptr_cid_info->cid_array =
+				kcalloc(BEISCSI_GET_CID_COUNT(phba, ulp_num),
+					sizeof(*ptr_cid_info->cid_array),
+					GFP_KERNEL);
 			if (!ptr_cid_info->cid_array) {
 				beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
 					    "BM_%d : Failed to allocate memory"
diff --git a/drivers/scsi/be2iscsi/be_main.h b/drivers/scsi/be2iscsi/be_main.h
index 6376657..02d00ab 100644
--- a/drivers/scsi/be2iscsi/be_main.h
+++ b/drivers/scsi/be2iscsi/be_main.h
@@ -358,6 +358,7 @@
 	unsigned int age;
 	struct list_head hba_queue;
 #define BE_MAX_SESSION 2048
+#define BE_INVALID_CID 0xffff
 #define BE_SET_CID_TO_CRI(cri_index, cid) \
 			  (phba->cid_to_cri_map[cid] = cri_index)
 #define BE_GET_CRI_FROM_CID(cid) (phba->cid_to_cri_map[cid])
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 375d818..d5f6fbf 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -461,7 +461,7 @@
 static int clariion_std_inquiry(struct scsi_device *sdev,
 				struct clariion_dh_data *csdev)
 {
-	int err;
+	int err = SCSI_DH_OK;
 	char *sp_model;
 
 	err = send_inquiry_cmd(sdev, 0, csdev);
diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c
index 1910100..00602ab 100644
--- a/drivers/scsi/isci/remote_node_context.c
+++ b/drivers/scsi/isci/remote_node_context.c
@@ -66,6 +66,9 @@
 {
 	static const char * const strings[] = RNC_STATES;
 
+	if (state >= ARRAY_SIZE(strings))
+		return "UNKNOWN";
+
 	return strings[state];
 }
 #undef C
diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
index d8b1fbd..35cbd36 100644
--- a/drivers/scsi/megaraid/megaraid_sas_base.c
+++ b/drivers/scsi/megaraid/megaraid_sas_base.c
@@ -1901,9 +1901,12 @@
 			if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) {
 				cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx];
 				if (cmd_mfi->sync_cmd &&
-					cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)
+				    (cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)) {
+					cmd_mfi->frame->hdr.cmd_status =
+							MFI_STAT_WRONG_STATE;
 					megasas_complete_cmd(instance,
 							     cmd_mfi, DID_OK);
+				}
 			}
 		}
 	} else {
@@ -5290,7 +5293,8 @@
 		instance->throttlequeuedepth =
 				MEGASAS_THROTTLE_QUEUE_DEPTH;
 
-	if (resetwaittime > MEGASAS_RESET_WAIT_TIME)
+	if ((resetwaittime < 1) ||
+	    (resetwaittime > MEGASAS_RESET_WAIT_TIME))
 		resetwaittime = MEGASAS_RESET_WAIT_TIME;
 
 	if ((scmd_timeout < 10) || (scmd_timeout > MEGASAS_DEFAULT_CMD_TIMEOUT))
@@ -5459,6 +5463,14 @@
 		prev_aen.word =
 			le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]);
 
+		if ((curr_aen.members.class < MFI_EVT_CLASS_DEBUG) ||
+		    (curr_aen.members.class > MFI_EVT_CLASS_DEAD)) {
+			dev_info(&instance->pdev->dev,
+				 "%s %d out of range class %d send by application\n",
+				 __func__, __LINE__, curr_aen.members.class);
+			return 0;
+		}
+
 		/*
 		 * A class whose enum value is smaller is inclusive of all
 		 * higher values. If a PROGRESS (= -1) was previously
diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
index bd04bd0..a156451 100644
--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
+++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
@@ -1960,7 +1960,8 @@
  */
 static void
 megasas_build_syspd_fusion(struct megasas_instance *instance,
-	struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd, u8 fp_possible)
+	struct scsi_cmnd *scmd, struct megasas_cmd_fusion *cmd,
+	bool fp_possible)
 {
 	u32 device_id;
 	struct MPI2_RAID_SCSI_IO_REQUEST *io_request;
@@ -2064,6 +2065,8 @@
 	u16 sge_count;
 	u8  cmd_type;
 	struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request;
+	struct MR_PRIV_DEVICE *mr_device_priv_data;
+	mr_device_priv_data = scp->device->hostdata;
 
 	/* Zero out some fields so they don't get reused */
 	memset(io_request->LUN, 0x0, 8);
@@ -2092,12 +2095,14 @@
 		megasas_build_ld_nonrw_fusion(instance, scp, cmd);
 		break;
 	case READ_WRITE_SYSPDIO:
+		megasas_build_syspd_fusion(instance, scp, cmd, true);
+		break;
 	case NON_READ_WRITE_SYSPDIO:
-		if (instance->secure_jbod_support &&
-			(cmd_type == NON_READ_WRITE_SYSPDIO))
-			megasas_build_syspd_fusion(instance, scp, cmd, 0);
+		if (instance->secure_jbod_support ||
+		    mr_device_priv_data->is_tm_capable)
+			megasas_build_syspd_fusion(instance, scp, cmd, false);
 		else
-			megasas_build_syspd_fusion(instance, scp, cmd, 1);
+			megasas_build_syspd_fusion(instance, scp, cmd, true);
 		break;
 	default:
 		break;
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index ad33238..9a34afc 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -243,12 +243,15 @@
 	struct qla_hw_data *ha = vha->hw;
 	ssize_t rval = 0;
 
-	if (ha->optrom_state != QLA_SREADING)
-		return 0;
-
 	mutex_lock(&ha->optrom_mutex);
+
+	if (ha->optrom_state != QLA_SREADING)
+		goto out;
+
 	rval = memory_read_from_buffer(buf, count, &off, ha->optrom_buffer,
 	    ha->optrom_region_size);
+
+out:
 	mutex_unlock(&ha->optrom_mutex);
 
 	return rval;
@@ -263,14 +266,19 @@
 	    struct device, kobj)));
 	struct qla_hw_data *ha = vha->hw;
 
-	if (ha->optrom_state != QLA_SWRITING)
+	mutex_lock(&ha->optrom_mutex);
+
+	if (ha->optrom_state != QLA_SWRITING) {
+		mutex_unlock(&ha->optrom_mutex);
 		return -EINVAL;
-	if (off > ha->optrom_region_size)
+	}
+	if (off > ha->optrom_region_size) {
+		mutex_unlock(&ha->optrom_mutex);
 		return -ERANGE;
+	}
 	if (off + count > ha->optrom_region_size)
 		count = ha->optrom_region_size - off;
 
-	mutex_lock(&ha->optrom_mutex);
 	memcpy(&ha->optrom_buffer[off], buf, count);
 	mutex_unlock(&ha->optrom_mutex);
 
@@ -310,6 +318,8 @@
 		return -EINVAL;
 	if (start > ha->optrom_size)
 		return -EINVAL;
+	if (size > ha->optrom_size - start)
+		size = ha->optrom_size - start;
 
 	mutex_lock(&ha->optrom_mutex);
 	switch (val) {
@@ -335,8 +345,7 @@
 		}
 
 		ha->optrom_region_start = start;
-		ha->optrom_region_size = start + size > ha->optrom_size ?
-		    ha->optrom_size - start : size;
+		ha->optrom_region_size = start + size;
 
 		ha->optrom_state = QLA_SREADING;
 		ha->optrom_buffer = vmalloc(ha->optrom_region_size);
@@ -409,8 +418,7 @@
 		}
 
 		ha->optrom_region_start = start;
-		ha->optrom_region_size = start + size > ha->optrom_size ?
-		    ha->optrom_size - start : size;
+		ha->optrom_region_size = start + size;
 
 		ha->optrom_state = QLA_SWRITING;
 		ha->optrom_buffer = vmalloc(ha->optrom_region_size);
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index 3dfb54a..f8ae704 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -74,7 +74,7 @@
 	 * ensures no active vp_list traversal while the vport is removed
 	 * from the queue)
 	 */
-	wait_event_timeout(vha->vref_waitq, atomic_read(&vha->vref_count),
+	wait_event_timeout(vha->vref_waitq, !atomic_read(&vha->vref_count),
 	    10*HZ);
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 42bca61..c39551b 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -3696,7 +3696,7 @@
 		uint32_t group;
 
 		nlh = nlmsg_hdr(skb);
-		if (nlh->nlmsg_len < sizeof(*nlh) ||
+		if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) ||
 		    skb->len < nlh->nlmsg_len) {
 			break;
 		}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index b9290e7..02823a7 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2770,8 +2770,6 @@
 		sd_read_write_same(sdkp, buffer);
 	}
 
-	sdkp->first_scan = 0;
-
 	/*
 	 * We now have all cache related info, determine how we deal
 	 * with flush requests.
@@ -2786,7 +2784,7 @@
 	q->limits.max_dev_sectors = logical_to_sectors(sdp, dev_max);
 
 	/*
-	 * Use the device's preferred I/O size for reads and writes
+	 * Determine the device's preferred I/O size for reads and writes
 	 * unless the reported value is unreasonably small, large, or
 	 * garbage.
 	 */
@@ -2800,8 +2798,19 @@
 		rw_max = min_not_zero(logical_to_sectors(sdp, dev_max),
 				      (sector_t)BLK_DEF_MAX_SECTORS);
 
-	/* Combine with controller limits */
-	q->limits.max_sectors = min(rw_max, queue_max_hw_sectors(q));
+	/* Do not exceed controller limit */
+	rw_max = min(rw_max, queue_max_hw_sectors(q));
+
+	/*
+	 * Only update max_sectors if previously unset or if the current value
+	 * exceeds the capabilities of the hardware.
+	 */
+	if (sdkp->first_scan ||
+	    q->limits.max_sectors > q->limits.max_dev_sectors ||
+	    q->limits.max_sectors > q->limits.max_hw_sectors)
+		q->limits.max_sectors = rw_max;
+
+	sdkp->first_scan = 0;
 
 	set_capacity(disk, logical_to_sectors(sdp, sdkp->capacity));
 	sd_config_write_same(sdkp);
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 44c466b..9965135 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -122,7 +122,7 @@
 struct sg_fd;
 
 typedef struct sg_request {	/* SG_MAX_QUEUE requests outstanding per file */
-	struct sg_request *nextrp;	/* NULL -> tail request (slist) */
+	struct list_head entry;	/* list entry */
 	struct sg_fd *parentfp;	/* NULL -> not in use */
 	Sg_scatter_hold data;	/* hold buffer, perhaps scatter list */
 	sg_io_hdr_t header;	/* scsi command+info, see <scsi/sg.h> */
@@ -142,11 +142,11 @@
 	struct sg_device *parentdp;	/* owning device */
 	wait_queue_head_t read_wait;	/* queue read until command done */
 	rwlock_t rq_list_lock;	/* protect access to list in req_arr */
+	struct mutex f_mutex;	/* protect against changes in this fd */
 	int timeout;		/* defaults to SG_DEFAULT_TIMEOUT      */
 	int timeout_user;	/* defaults to SG_DEFAULT_TIMEOUT_USER */
 	Sg_scatter_hold reserve;	/* buffer held for this file descriptor */
-	unsigned save_scat_len;	/* original length of trunc. scat. element */
-	Sg_request *headrp;	/* head of request slist, NULL->empty */
+	struct list_head rq_list; /* head of request list */
 	struct fasync_struct *async_qp;	/* used by asynchronous notification */
 	Sg_request req_arr[SG_MAX_QUEUE];	/* used as singly-linked list */
 	char low_dma;		/* as in parent but possibly overridden to 1 */
@@ -155,6 +155,7 @@
 	unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */
 	char keep_orphan;	/* 0 -> drop orphan (def), 1 -> keep for read() */
 	char mmap_called;	/* 0 -> mmap() never called on this fd */
+	char res_in_use;	/* 1 -> 'reserve' array in use */
 	struct kref f_ref;
 	struct execute_work ew;
 } Sg_fd;
@@ -198,7 +199,6 @@
 static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id);
 static Sg_request *sg_add_request(Sg_fd * sfp);
 static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
-static int sg_res_in_use(Sg_fd * sfp);
 static Sg_device *sg_get_dev(int dev);
 static void sg_device_destroy(struct kref *kref);
 
@@ -499,7 +499,7 @@
 		old_hdr->result = EIO;
 		break;
 	case DID_ERROR:
-		old_hdr->result = (srp->sense_b[0] == 0 && 
+		old_hdr->result = (srp->sense_b[0] == 0 &&
 				  hp->masked_status == GOOD) ? 0 : EIO;
 		break;
 	default:
@@ -614,6 +614,7 @@
 	}
 	buf += SZ_SG_HEADER;
 	__get_user(opcode, buf);
+	mutex_lock(&sfp->f_mutex);
 	if (sfp->next_cmd_len > 0) {
 		cmd_size = sfp->next_cmd_len;
 		sfp->next_cmd_len = 0;	/* reset so only this write() effected */
@@ -622,6 +623,7 @@
 		if ((opcode >= 0xc0) && old_hdr.twelve_byte)
 			cmd_size = 12;
 	}
+	mutex_unlock(&sfp->f_mutex);
 	SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp,
 		"sg_write:   scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size));
 /* Determine buffer size.  */
@@ -721,7 +723,7 @@
 			sg_remove_request(sfp, srp);
 			return -EINVAL;	/* either MMAP_IO or DIRECT_IO (not both) */
 		}
-		if (sg_res_in_use(sfp)) {
+		if (sfp->res_in_use) {
 			sg_remove_request(sfp, srp);
 			return -EBUSY;	/* reserve buffer already being used */
 		}
@@ -830,6 +832,39 @@
 	return max_sectors << 9;
 }
 
+static void
+sg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo)
+{
+	Sg_request *srp;
+	int val;
+	unsigned int ms;
+
+	val = 0;
+	list_for_each_entry(srp, &sfp->rq_list, entry) {
+		if (val >= SG_MAX_QUEUE)
+			break;
+		rinfo[val].req_state = srp->done + 1;
+		rinfo[val].problem =
+			srp->header.masked_status &
+			srp->header.host_status &
+			srp->header.driver_status;
+		if (srp->done)
+			rinfo[val].duration =
+				srp->header.duration;
+		else {
+			ms = jiffies_to_msecs(jiffies);
+			rinfo[val].duration =
+				(ms > srp->header.duration) ?
+				(ms - srp->header.duration) : 0;
+		}
+		rinfo[val].orphan = srp->orphan;
+		rinfo[val].sg_io_owned = srp->sg_io_owned;
+		rinfo[val].pack_id = srp->header.pack_id;
+		rinfo[val].usr_ptr = srp->header.usr_ptr;
+		val++;
+	}
+}
+
 static long
 sg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg)
 {
@@ -856,8 +891,10 @@
 			return -ENXIO;
 		if (!access_ok(VERIFY_WRITE, p, SZ_SG_IO_HDR))
 			return -EFAULT;
+		mutex_lock(&sfp->parentdp->open_rel_lock);
 		result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
 				 1, read_only, 1, &srp);
+		mutex_unlock(&sfp->parentdp->open_rel_lock);
 		if (result < 0)
 			return result;
 		result = wait_event_interruptible(sfp->read_wait,
@@ -896,10 +933,12 @@
 			return result;
 		if (val) {
 			sfp->low_dma = 1;
-			if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {
+			if ((0 == sfp->low_dma) && !sfp->res_in_use) {
 				val = (int) sfp->reserve.bufflen;
+				mutex_lock(&sfp->parentdp->open_rel_lock);
 				sg_remove_scat(sfp, &sfp->reserve);
 				sg_build_reserve(sfp, val);
+				mutex_unlock(&sfp->parentdp->open_rel_lock);
 			}
 		} else {
 			if (atomic_read(&sdp->detaching))
@@ -942,7 +981,7 @@
 		if (!access_ok(VERIFY_WRITE, ip, sizeof (int)))
 			return -EFAULT;
 		read_lock_irqsave(&sfp->rq_list_lock, iflags);
-		for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+		list_for_each_entry(srp, &sfp->rq_list, entry) {
 			if ((1 == srp->done) && (!srp->sg_io_owned)) {
 				read_unlock_irqrestore(&sfp->rq_list_lock,
 						       iflags);
@@ -955,7 +994,8 @@
 		return 0;
 	case SG_GET_NUM_WAITING:
 		read_lock_irqsave(&sfp->rq_list_lock, iflags);
-		for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) {
+		val = 0;
+		list_for_each_entry(srp, &sfp->rq_list, entry) {
 			if ((1 == srp->done) && (!srp->sg_io_owned))
 				++val;
 		}
@@ -967,16 +1007,23 @@
 		result = get_user(val, ip);
 		if (result)
 			return result;
-                if (val < 0)
-                        return -EINVAL;
+		if (val < 0)
+			return -EINVAL;
 		val = min_t(int, val,
 			    max_sectors_bytes(sdp->device->request_queue));
+		mutex_lock(&sfp->f_mutex);
 		if (val != sfp->reserve.bufflen) {
-			if (sg_res_in_use(sfp) || sfp->mmap_called)
+			if (sfp->mmap_called ||
+			    sfp->res_in_use) {
+				mutex_unlock(&sfp->f_mutex);
 				return -EBUSY;
+			}
+			mutex_lock(&sfp->parentdp->open_rel_lock);
 			sg_remove_scat(sfp, &sfp->reserve);
 			sg_build_reserve(sfp, val);
+			mutex_unlock(&sfp->parentdp->open_rel_lock);
 		}
+		mutex_unlock(&sfp->f_mutex);
 		return 0;
 	case SG_GET_RESERVED_SIZE:
 		val = min_t(int, sfp->reserve.bufflen,
@@ -1017,42 +1064,15 @@
 			return -EFAULT;
 		else {
 			sg_req_info_t *rinfo;
-			unsigned int ms;
 
-			rinfo = kmalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE,
-								GFP_KERNEL);
+			rinfo = kzalloc(SZ_SG_REQ_INFO * SG_MAX_QUEUE,
+					GFP_KERNEL);
 			if (!rinfo)
 				return -ENOMEM;
 			read_lock_irqsave(&sfp->rq_list_lock, iflags);
-			for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE;
-			     ++val, srp = srp ? srp->nextrp : srp) {
-				memset(&rinfo[val], 0, SZ_SG_REQ_INFO);
-				if (srp) {
-					rinfo[val].req_state = srp->done + 1;
-					rinfo[val].problem =
-					    srp->header.masked_status & 
-					    srp->header.host_status & 
-					    srp->header.driver_status;
-					if (srp->done)
-						rinfo[val].duration =
-							srp->header.duration;
-					else {
-						ms = jiffies_to_msecs(jiffies);
-						rinfo[val].duration =
-						    (ms > srp->header.duration) ?
-						    (ms - srp->header.duration) : 0;
-					}
-					rinfo[val].orphan = srp->orphan;
-					rinfo[val].sg_io_owned =
-							srp->sg_io_owned;
-					rinfo[val].pack_id =
-							srp->header.pack_id;
-					rinfo[val].usr_ptr =
-							srp->header.usr_ptr;
-				}
-			}
+			sg_fill_request_table(sfp, rinfo);
 			read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-			result = __copy_to_user(p, rinfo, 
+			result = __copy_to_user(p, rinfo,
 						SZ_SG_REQ_INFO * SG_MAX_QUEUE);
 			result = result ? -EFAULT : 0;
 			kfree(rinfo);
@@ -1128,14 +1148,14 @@
 		return -ENXIO;
 
 	sdev = sdp->device;
-	if (sdev->host->hostt->compat_ioctl) { 
+	if (sdev->host->hostt->compat_ioctl) {
 		int ret;
 
 		ret = sdev->host->hostt->compat_ioctl(sdev, cmd_in, (void __user *)arg);
 
 		return ret;
 	}
-	
+
 	return -ENOIOCTLCMD;
 }
 #endif
@@ -1158,7 +1178,7 @@
 		return POLLERR;
 	poll_wait(filp, &sfp->read_wait, wait);
 	read_lock_irqsave(&sfp->rq_list_lock, iflags);
-	for (srp = sfp->headrp; srp; srp = srp->nextrp) {
+	list_for_each_entry(srp, &sfp->rq_list, entry) {
 		/* if any read waiting, flag it */
 		if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned))
 			res = POLLIN | POLLRDNORM;
@@ -1239,6 +1259,7 @@
 	unsigned long req_sz, len, sa;
 	Sg_scatter_hold *rsv_schp;
 	int k, length;
+	int ret = 0;
 
 	if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data)))
 		return -ENXIO;
@@ -1249,8 +1270,11 @@
 	if (vma->vm_pgoff)
 		return -EINVAL;	/* want no offset */
 	rsv_schp = &sfp->reserve;
-	if (req_sz > rsv_schp->bufflen)
-		return -ENOMEM;	/* cannot map more than reserved buffer */
+	mutex_lock(&sfp->f_mutex);
+	if (req_sz > rsv_schp->bufflen) {
+		ret = -ENOMEM;	/* cannot map more than reserved buffer */
+		goto out;
+	}
 
 	sa = vma->vm_start;
 	length = 1 << (PAGE_SHIFT + rsv_schp->page_order);
@@ -1264,7 +1288,9 @@
 	vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
 	vma->vm_private_data = sfp;
 	vma->vm_ops = &sg_mmap_vm_ops;
-	return 0;
+out:
+	mutex_unlock(&sfp->f_mutex);
+	return ret;
 }
 
 static void
@@ -1619,7 +1645,7 @@
 	else
 		def_reserved_size = sg_big_buff;
 
-	rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), 
+	rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0),
 				    SG_MAX_DEVS, "sg");
 	if (rc)
 		return rc;
@@ -1728,13 +1754,25 @@
 		md = &map_data;
 
 	if (md) {
-		if (!sg_res_in_use(sfp) && dxfer_len <= rsv_schp->bufflen)
+		mutex_lock(&sfp->f_mutex);
+		if (dxfer_len <= rsv_schp->bufflen &&
+		    !sfp->res_in_use) {
+			sfp->res_in_use = 1;
 			sg_link_reserve(sfp, srp, dxfer_len);
-		else {
+		} else if (hp->flags & SG_FLAG_MMAP_IO) {
+			res = -EBUSY; /* sfp->res_in_use == 1 */
+			if (dxfer_len > rsv_schp->bufflen)
+				res = -ENOMEM;
+			mutex_unlock(&sfp->f_mutex);
+			return res;
+		} else {
 			res = sg_build_indirect(req_schp, sfp, dxfer_len);
-			if (res)
+			if (res) {
+				mutex_unlock(&sfp->f_mutex);
 				return res;
+			}
 		}
+		mutex_unlock(&sfp->f_mutex);
 
 		md->pages = req_schp->pages;
 		md->page_order = req_schp->page_order;
@@ -2023,8 +2061,9 @@
 	req_schp->pages = NULL;
 	req_schp->page_order = 0;
 	req_schp->sglist_len = 0;
-	sfp->save_scat_len = 0;
 	srp->res_used = 0;
+	/* Called without mutex lock to avoid deadlock */
+	sfp->res_in_use = 0;
 }
 
 static Sg_request *
@@ -2034,7 +2073,7 @@
 	unsigned long iflags;
 
 	write_lock_irqsave(&sfp->rq_list_lock, iflags);
-	for (resp = sfp->headrp; resp; resp = resp->nextrp) {
+	list_for_each_entry(resp, &sfp->rq_list, entry) {
 		/* look for requests that are ready + not SG_IO owned */
 		if ((1 == resp->done) && (!resp->sg_io_owned) &&
 		    ((-1 == pack_id) || (resp->header.pack_id == pack_id))) {
@@ -2052,70 +2091,45 @@
 {
 	int k;
 	unsigned long iflags;
-	Sg_request *resp;
 	Sg_request *rp = sfp->req_arr;
 
 	write_lock_irqsave(&sfp->rq_list_lock, iflags);
-	resp = sfp->headrp;
-	if (!resp) {
-		memset(rp, 0, sizeof (Sg_request));
-		rp->parentfp = sfp;
-		resp = rp;
-		sfp->headrp = resp;
-	} else {
-		if (0 == sfp->cmd_q)
-			resp = NULL;	/* command queuing disallowed */
-		else {
-			for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
-				if (!rp->parentfp)
-					break;
-			}
-			if (k < SG_MAX_QUEUE) {
-				memset(rp, 0, sizeof (Sg_request));
-				rp->parentfp = sfp;
-				while (resp->nextrp)
-					resp = resp->nextrp;
-				resp->nextrp = rp;
-				resp = rp;
-			} else
-				resp = NULL;
+	if (!list_empty(&sfp->rq_list)) {
+		if (!sfp->cmd_q)
+			goto out_unlock;
+
+		for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) {
+			if (!rp->parentfp)
+				break;
 		}
+		if (k >= SG_MAX_QUEUE)
+			goto out_unlock;
 	}
-	if (resp) {
-		resp->nextrp = NULL;
-		resp->header.duration = jiffies_to_msecs(jiffies);
-	}
+	memset(rp, 0, sizeof (Sg_request));
+	rp->parentfp = sfp;
+	rp->header.duration = jiffies_to_msecs(jiffies);
+	list_add_tail(&rp->entry, &sfp->rq_list);
 	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-	return resp;
+	return rp;
+out_unlock:
+	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
+	return NULL;
 }
 
 /* Return of 1 for found; 0 for not found */
 static int
 sg_remove_request(Sg_fd * sfp, Sg_request * srp)
 {
-	Sg_request *prev_rp;
-	Sg_request *rp;
 	unsigned long iflags;
 	int res = 0;
 
-	if ((!sfp) || (!srp) || (!sfp->headrp))
+	if (!sfp || !srp || list_empty(&sfp->rq_list))
 		return res;
 	write_lock_irqsave(&sfp->rq_list_lock, iflags);
-	prev_rp = sfp->headrp;
-	if (srp == prev_rp) {
-		sfp->headrp = prev_rp->nextrp;
-		prev_rp->parentfp = NULL;
+	if (!list_empty(&srp->entry)) {
+		list_del(&srp->entry);
+		srp->parentfp = NULL;
 		res = 1;
-	} else {
-		while ((rp = prev_rp->nextrp)) {
-			if (srp == rp) {
-				prev_rp->nextrp = rp->nextrp;
-				rp->parentfp = NULL;
-				res = 1;
-				break;
-			}
-			prev_rp = rp;
-		}
 	}
 	write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
 	return res;
@@ -2134,8 +2148,9 @@
 
 	init_waitqueue_head(&sfp->read_wait);
 	rwlock_init(&sfp->rq_list_lock);
-
+	INIT_LIST_HEAD(&sfp->rq_list);
 	kref_init(&sfp->f_ref);
+	mutex_init(&sfp->f_mutex);
 	sfp->timeout = SG_DEFAULT_TIMEOUT;
 	sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER;
 	sfp->force_packid = SG_DEF_FORCE_PACK_ID;
@@ -2174,10 +2189,13 @@
 {
 	struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work);
 	struct sg_device *sdp = sfp->parentdp;
+	Sg_request *srp;
 
 	/* Cleanup any responses which were never read(). */
-	while (sfp->headrp)
-		sg_finish_rem_req(sfp->headrp);
+	while (!list_empty(&sfp->rq_list)) {
+		srp = list_first_entry(&sfp->rq_list, Sg_request, entry);
+		sg_finish_rem_req(srp);
+	}
 
 	if (sfp->reserve.bufflen > 0) {
 		SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp,
@@ -2211,20 +2229,6 @@
 	schedule_work(&sfp->ew.work);
 }
 
-static int
-sg_res_in_use(Sg_fd * sfp)
-{
-	const Sg_request *srp;
-	unsigned long iflags;
-
-	read_lock_irqsave(&sfp->rq_list_lock, iflags);
-	for (srp = sfp->headrp; srp; srp = srp->nextrp)
-		if (srp->res_used)
-			break;
-	read_unlock_irqrestore(&sfp->rq_list_lock, iflags);
-	return srp ? 1 : 0;
-}
-
 #ifdef CONFIG_SCSI_PROC_FS
 static int
 sg_idr_max_id(int id, void *p, void *data)
@@ -2299,7 +2303,7 @@
 };
 
 static int sg_proc_single_open_dressz(struct inode *inode, struct file *file);
-static ssize_t sg_proc_write_dressz(struct file *filp, 
+static ssize_t sg_proc_write_dressz(struct file *filp,
 		const char __user *buffer, size_t count, loff_t *off);
 static const struct file_operations dressz_fops = {
 	.owner = THIS_MODULE,
@@ -2439,7 +2443,7 @@
 	return single_open(file, sg_proc_seq_show_int, &sg_allow_dio);
 }
 
-static ssize_t 
+static ssize_t
 sg_proc_write_adio(struct file *filp, const char __user *buffer,
 		   size_t count, loff_t *off)
 {
@@ -2460,7 +2464,7 @@
 	return single_open(file, sg_proc_seq_show_int, &sg_big_buff);
 }
 
-static ssize_t 
+static ssize_t
 sg_proc_write_dressz(struct file *filp, const char __user *buffer,
 		     size_t count, loff_t *off)
 {
@@ -2594,7 +2598,7 @@
 /* must be called while holding sg_index_lock */
 static void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp)
 {
-	int k, m, new_interface, blen, usg;
+	int k, new_interface, blen, usg;
 	Sg_request *srp;
 	Sg_fd *fp;
 	const sg_io_hdr_t *hp;
@@ -2614,13 +2618,11 @@
 		seq_printf(s, "   cmd_q=%d f_packid=%d k_orphan=%d closed=0\n",
 			   (int) fp->cmd_q, (int) fp->force_packid,
 			   (int) fp->keep_orphan);
-		for (m = 0, srp = fp->headrp;
-				srp != NULL;
-				++m, srp = srp->nextrp) {
+		list_for_each_entry(srp, &fp->rq_list, entry) {
 			hp = &srp->header;
 			new_interface = (hp->interface_id == '\0') ? 0 : 1;
 			if (srp->res_used) {
-				if (new_interface && 
+				if (new_interface &&
 				    (SG_FLAG_MMAP_IO & hp->flags))
 					cp = "     mmap>> ";
 				else
@@ -2651,7 +2653,7 @@
 			seq_printf(s, "ms sgat=%d op=0x%02x\n", usg,
 				   (int) srp->data.cmd_opcode);
 		}
-		if (0 == m)
+		if (list_empty(&fp->rq_list))
 			seq_puts(s, "     No requests active\n");
 		read_unlock(&fp->rq_list_lock);
 	}
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index c5ab1b0..2bf96d3 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -1559,6 +1559,8 @@
 	ret = storvsc_do_io(dev, cmd_request);
 
 	if (ret == -EAGAIN) {
+		if (payload_sz > sizeof(cmd_request->mpb))
+			kfree(payload);
 		/* no more space */
 		return SCSI_MLQUEUE_DEVICE_BUSY;
 	}
diff --git a/drivers/scsi/ufs/ufs-debugfs.c b/drivers/scsi/ufs/ufs-debugfs.c
index 557ca19..11e11e4 100644
--- a/drivers/scsi/ufs/ufs-debugfs.c
+++ b/drivers/scsi/ufs/ufs-debugfs.c
@@ -950,6 +950,10 @@
 	seq_printf(file, "hba->saved_err = 0x%x\n", hba->saved_err);
 	seq_printf(file, "hba->saved_uic_err = 0x%x\n", hba->saved_uic_err);
 
+	seq_printf(file, "power_mode_change_cnt = %d\n",
+			hba->ufs_stats.power_mode_change_cnt);
+	seq_printf(file, "hibern8_exit_cnt = %d\n",
+			hba->ufs_stats.hibern8_exit_cnt);
 	return 0;
 }
 
diff --git a/drivers/scsi/ufs/ufs-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c
index 4547a6d..db4ecec 100644
--- a/drivers/scsi/ufs/ufs-qcom-debugfs.c
+++ b/drivers/scsi/ufs/ufs-qcom-debugfs.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Linux Foundation. All rights reserved.
+ * Copyright (c) 2015,2017, 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
@@ -111,20 +111,25 @@
 				loff_t *ppos)
 {
 	struct ufs_qcom_host *host = file->f_mapping->host->i_private;
-	char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {0};
+	char configuration[TESTBUS_CFG_BUFF_LINE_SIZE] = {'\0'};
 	loff_t buff_pos = 0;
 	char *comma;
 	int ret = 0;
 	int major;
 	int minor;
+	unsigned long flags;
+	struct ufs_hba *hba = host->hba;
 
-	ret = simple_write_to_buffer(configuration, TESTBUS_CFG_BUFF_LINE_SIZE,
+
+	ret = simple_write_to_buffer(configuration,
+		TESTBUS_CFG_BUFF_LINE_SIZE - 1,
 		&buff_pos, ubuf, cnt);
 	if (ret < 0) {
 		dev_err(host->hba->dev, "%s: failed to read user data\n",
 			__func__);
 		goto out;
 	}
+	configuration[ret] = '\0';
 
 	comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ',');
 	if (!comma || comma == configuration) {
@@ -142,8 +147,15 @@
 		goto out;
 	}
 
+	if (!ufs_qcom_testbus_cfg_is_ok(host, major, minor)) {
+		ret = -EPERM;
+		goto out;
+	}
+
+	spin_lock_irqsave(hba->host->host_lock, flags);
 	host->testbus.select_major = (u8)major;
 	host->testbus.select_minor = (u8)minor;
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
 
 	/*
 	 * Sanity check of the {major, minor} tuple is done in the
diff --git a/drivers/scsi/ufs/ufs-qcom-ice.c b/drivers/scsi/ufs/ufs-qcom-ice.c
index 0c86263..84765b1 100644
--- a/drivers/scsi/ufs/ufs-qcom-ice.c
+++ b/drivers/scsi/ufs/ufs-qcom-ice.c
@@ -170,17 +170,15 @@
 static void ufs_qcom_ice_cfg_work(struct work_struct *work)
 {
 	unsigned long flags;
-	struct ice_data_setting ice_set;
 	struct ufs_qcom_host *qcom_host =
 		container_of(work, struct ufs_qcom_host, ice_cfg_work);
-	struct request *req_pending = NULL;
 
 	if (!qcom_host->ice.vops->config_start)
 		return;
 
 	spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
-	req_pending = qcom_host->req_pending;
-	if (!req_pending) {
+	if (!qcom_host->req_pending) {
+		qcom_host->work_pending = false;
 		spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
 		return;
 	}
@@ -189,24 +187,15 @@
 	/*
 	 * config_start is called again as previous attempt returned -EAGAIN,
 	 * this call shall now take care of the necessary key setup.
-	 * 'ice_set' will not actually be used, instead the next call to
-	 * config_start() for this request, in the normal call flow, will
-	 * succeed as the key has now been setup.
 	 */
 	qcom_host->ice.vops->config_start(qcom_host->ice.pdev,
-		qcom_host->req_pending, &ice_set, false);
+		qcom_host->req_pending, NULL, false);
 
 	spin_lock_irqsave(&qcom_host->ice_work_lock, flags);
 	qcom_host->req_pending = NULL;
+	qcom_host->work_pending = false;
 	spin_unlock_irqrestore(&qcom_host->ice_work_lock, flags);
 
-	/*
-	 * Resume with requests processing. We assume config_start has been
-	 * successful, but even if it wasn't we still must resume in order to
-	 * allow for the request to be retried.
-	 */
-	ufshcd_scsi_unblock_requests(qcom_host->hba);
-
 }
 
 /**
@@ -285,18 +274,14 @@
 			 * requires a non-atomic context, this means we should
 			 * call the function again from the worker thread to do
 			 * the configuration. For this request the error will
-			 * propagate so it will be re-queued and until the
-			 * configuration is is completed we block further
-			 * request processing.
+			 * propagate so it will be re-queued.
 			 */
 			if (err == -EAGAIN) {
 				dev_dbg(qcom_host->hba->dev,
 					"%s: scheduling task for ice setup\n",
 					__func__);
 
-				if (!qcom_host->req_pending) {
-					ufshcd_scsi_block_requests(
-						qcom_host->hba);
+				if (!qcom_host->work_pending) {
 					qcom_host->req_pending = cmd->request;
 
 					if (!schedule_work(
@@ -307,10 +292,9 @@
 						&qcom_host->ice_work_lock,
 						flags);
 
-						ufshcd_scsi_unblock_requests(
-							qcom_host->hba);
 						return err;
 					}
+					qcom_host->work_pending = true;
 				}
 
 			} else {
@@ -409,9 +393,7 @@
 			 * requires a non-atomic context, this means we should
 			 * call the function again from the worker thread to do
 			 * the configuration. For this request the error will
-			 * propagate so it will be re-queued and until the
-			 * configuration is is completed we block further
-			 * request processing.
+			 * propagate so it will be re-queued.
 			 */
 			if (err == -EAGAIN) {
 
@@ -419,9 +401,8 @@
 					"%s: scheduling task for ice setup\n",
 					__func__);
 
-				if (!qcom_host->req_pending) {
-					ufshcd_scsi_block_requests(
-						qcom_host->hba);
+				if (!qcom_host->work_pending) {
+
 					qcom_host->req_pending = cmd->request;
 					if (!schedule_work(
 						&qcom_host->ice_cfg_work)) {
@@ -431,10 +412,9 @@
 						&qcom_host->ice_work_lock,
 						flags);
 
-						ufshcd_scsi_unblock_requests(
-							qcom_host->hba);
 						return err;
 					}
+					qcom_host->work_pending = true;
 				}
 
 			} else {
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index ee23fc7..1ad191e 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -1987,6 +1987,18 @@
 }
 #endif /* CONFIG_SMP */
 
+#define	ANDROID_BOOT_DEV_MAX	30
+static char android_boot_dev[ANDROID_BOOT_DEV_MAX];
+
+#ifndef MODULE
+static int __init get_android_boot_dev(char *str)
+{
+	strlcpy(android_boot_dev, str, ANDROID_BOOT_DEV_MAX);
+	return 1;
+}
+__setup("androidboot.bootdevice=", get_android_boot_dev);
+#endif
+
 /*
  * ufs_qcom_parse_lpm - read from DTS whether LPM modes should be disabled.
  */
@@ -2454,12 +2466,13 @@
 	host->testbus.select_minor = 37;
 }
 
-static bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host)
+bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host,
+		u8 select_major, u8 select_minor)
 {
-	if (host->testbus.select_major >= TSTBUS_MAX) {
+	if (select_major >= TSTBUS_MAX) {
 		dev_err(host->hba->dev,
 			"%s: UFS_CFG1[TEST_BUS_SEL} may not equal 0x%05X\n",
-			__func__, host->testbus.select_major);
+			__func__, select_major);
 		return false;
 	}
 
@@ -2468,10 +2481,10 @@
 	 * mappings of select_minor, since there is no harm in
 	 * configuring a non-existent select_minor
 	 */
-	if (host->testbus.select_minor > 0xFF) {
+	if (select_minor > 0xFF) {
 		dev_err(host->hba->dev,
 			"%s: 0x%05X is not a legal testbus option\n",
-			__func__, host->testbus.select_minor);
+			__func__, select_minor);
 		return false;
 	}
 
@@ -2485,16 +2498,16 @@
  */
 int ufs_qcom_testbus_config(struct ufs_qcom_host *host)
 {
-	int reg;
-	int offset;
+	int reg = 0;
+	int offset, ret = 0, testbus_sel_offset = 19;
 	u32 mask = TEST_BUS_SUB_SEL_MASK;
+	unsigned long flags;
+	struct ufs_hba *hba;
 
 	if (!host)
 		return -EINVAL;
-
-	if (!ufs_qcom_testbus_cfg_is_ok(host))
-		return -EPERM;
-
+	hba = host->hba;
+	spin_lock_irqsave(hba->host->host_lock, flags);
 	switch (host->testbus.select_major) {
 	case TSTBUS_UAWM:
 		reg = UFS_TEST_BUS_CTRL_0;
@@ -2553,20 +2566,27 @@
 	}
 	mask <<= offset;
 
-	ufshcd_rmwl(host->hba, TEST_BUS_SEL,
-		    (u32)host->testbus.select_major << 19,
+	spin_unlock_irqrestore(hba->host->host_lock, flags);
+	if (reg) {
+		ufshcd_rmwl(host->hba, TEST_BUS_SEL,
+		    (u32)host->testbus.select_major << testbus_sel_offset,
 		    REG_UFS_CFG1);
-	ufshcd_rmwl(host->hba, mask,
+		ufshcd_rmwl(host->hba, mask,
 		    (u32)host->testbus.select_minor << offset,
 		    reg);
+	} else {
+		dev_err(hba->dev, "%s: Problem setting minor\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
 	ufs_qcom_enable_test_bus(host);
 	/*
 	 * Make sure the test bus configuration is
 	 * committed before returning.
 	 */
 	mb();
-
-	return 0;
+out:
+	return ret;
 }
 
 static void ufs_qcom_testbus_read(struct ufs_hba *hba)
@@ -2676,6 +2696,24 @@
 {
 	int err;
 	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+
+	/*
+	 * On qcom platforms, bootdevice is the primary storage
+	 * device. This device can either be eMMC or UFS.
+	 * The type of device connected is detected at runtime.
+	 * So, if an eMMC device is connected, and this function
+	 * is invoked, it would turn-off the regulator if it detects
+	 * that the storage device is not ufs.
+	 * These regulators are turned ON by the bootloaders & turning
+	 * them off without sending PON may damage the connected device.
+	 * Hence, check for the connected device early-on & don't turn-off
+	 * the regulators.
+	 */
+	if (of_property_read_bool(np, "non-removable") &&
+	    strlen(android_boot_dev) &&
+	    strcmp(android_boot_dev, dev_name(dev)))
+		return -ENODEV;
 
 	/* Perform generic probe */
 	err = ufshcd_pltfrm_init(pdev, &ufs_hba_qcom_variant);
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 792ae42..9da3d19 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -21,6 +21,7 @@
 #define MAX_UFS_QCOM_HOSTS	2
 #define MAX_U32                 (~(u32)0)
 #define MPHY_TX_FSM_STATE       0x41
+#define MPHY_RX_FSM_STATE       0xC1
 #define TX_FSM_HIBERN8          0x1
 #define HBRN8_POLL_TOUT_MS      100
 #define DEFAULT_CLK_RATE_HZ     1000000
@@ -99,7 +100,7 @@
 /* bit definitions for REG_UFS_CFG1 register */
 #define QUNIPRO_SEL	UFS_BIT(0)
 #define TEST_BUS_EN		BIT(18)
-#define TEST_BUS_SEL		GENMASK(22, 19)
+#define TEST_BUS_SEL		0x780000
 #define UFS_REG_TEST_BUS_EN	BIT(30)
 
 /* bit definitions for REG_UFS_CFG2 register */
@@ -374,6 +375,7 @@
 	struct work_struct ice_cfg_work;
 	struct request *req_pending;
 	struct ufs_vreg *vddp_ref_clk;
+	bool work_pending;
 };
 
 static inline u32
@@ -389,6 +391,8 @@
 #define ufs_qcom_is_link_active(hba) ufshcd_is_link_active(hba)
 #define ufs_qcom_is_link_hibern8(hba) ufshcd_is_link_hibern8(hba)
 
+bool ufs_qcom_testbus_cfg_is_ok(struct ufs_qcom_host *host, u8 select_major,
+		u8 select_minor);
 int ufs_qcom_testbus_config(struct ufs_qcom_host *host);
 void ufs_qcom_print_hw_debug_reg_all(struct ufs_hba *hba, void *priv,
 		void (*print_fn)(struct ufs_hba *hba, int offset, int num_regs,
diff --git a/drivers/scsi/ufs/ufs_quirks.c b/drivers/scsi/ufs/ufs_quirks.c
index da2bfd5..a2b98fb 100644
--- a/drivers/scsi/ufs/ufs_quirks.c
+++ b/drivers/scsi/ufs/ufs_quirks.c
@@ -18,8 +18,6 @@
 	/* UFS cards deviations table */
 	UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL, UFS_DEVICE_NO_VCCQ),
 	UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
-		UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS),
-	UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
 		UFS_DEVICE_NO_FASTAUTO),
 	UFS_FIX(UFS_VENDOR_SAMSUNG, UFS_ANY_MODEL,
 		UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE),
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 7c5a1bc..f5a6736 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -144,10 +144,11 @@
 static int ufshcd_populate_vreg(struct device *dev, const char *name,
 		struct ufs_vreg **out_vreg)
 {
-	int ret = 0;
+	int len, ret = 0;
 	char prop_name[MAX_PROP_SIZE];
 	struct ufs_vreg *vreg = NULL;
 	struct device_node *np = dev->of_node;
+	const __be32 *prop;
 
 	if (!np) {
 		dev_err(dev, "%s: non DT initialization\n", __func__);
@@ -186,8 +187,16 @@
 			vreg->min_uV = UFS_VREG_VCC_1P8_MIN_UV;
 			vreg->max_uV = UFS_VREG_VCC_1P8_MAX_UV;
 		} else {
-			vreg->min_uV = UFS_VREG_VCC_MIN_UV;
-			vreg->max_uV = UFS_VREG_VCC_MAX_UV;
+			prop = of_get_property(np, "vcc-voltage-level", &len);
+			if (!prop || (len != (2 * sizeof(__be32)))) {
+				dev_warn(dev, "%s vcc-voltage-level property.\n",
+					prop ? "invalid format" : "no");
+				vreg->min_uV = UFS_VREG_VCC_MIN_UV;
+				vreg->max_uV = UFS_VREG_VCC_MAX_UV;
+			} else {
+				vreg->min_uV = be32_to_cpup(&prop[0]);
+				vreg->max_uV = be32_to_cpup(&prop[1]);
+			}
 		}
 	} else if (!strcmp(name, "vccq")) {
 		vreg->min_uV = UFS_VREG_VCCQ_MIN_UV;
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c132dbc..a6bc1da 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -172,6 +172,9 @@
 }
 #endif
 
+#define PWR_INFO_MASK	0xF
+#define PWR_RX_OFFSET	4
+
 #define UFSHCD_REQ_SENSE_SIZE	18
 
 #define UFSHCD_ENABLE_INTRS	(UTP_TRANSFER_REQ_COMPL |\
@@ -408,8 +411,8 @@
 
 #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
 static struct devfreq_simple_ondemand_data ufshcd_ondemand_data = {
-	.upthreshold = 35,
-	.downdifferential = 30,
+	.upthreshold = 70,
+	.downdifferential = 65,
 	.simple_scaling = 1,
 };
 
@@ -419,7 +422,7 @@
 #endif
 
 static struct devfreq_dev_profile ufs_devfreq_profile = {
-	.polling_ms	= 40,
+	.polling_ms	= 60,
 	.target		= ufshcd_devfreq_target,
 	.get_dev_status	= ufshcd_devfreq_get_dev_status,
 };
@@ -546,19 +549,19 @@
 
 #ifdef CONFIG_TRACEPOINTS
 static inline void ufshcd_add_command_trace(struct ufs_hba *hba,
-			struct ufshcd_cmd_log_entry *entry, u8 opcode)
+			struct ufshcd_cmd_log_entry *entry)
 {
 	if (trace_ufshcd_command_enabled()) {
 		u32 intr = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
 
 		trace_ufshcd_command(dev_name(hba->dev), entry->str, entry->tag,
 				     entry->doorbell, entry->transfer_len, intr,
-				     entry->lba, opcode);
+				     entry->lba, entry->cmd_id);
 	}
 }
 #else
 static inline void ufshcd_add_command_trace(struct ufs_hba *hba,
-			struct ufshcd_cmd_log_entry *entry, u8 opcode)
+			struct ufshcd_cmd_log_entry *entry)
 {
 }
 #endif
@@ -579,7 +582,7 @@
 
 static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type,
 			     unsigned int tag, u8 cmd_id, u8 idn, u8 lun,
-			     sector_t lba, int transfer_len, u8 opcode)
+			     sector_t lba, int transfer_len)
 {
 	struct ufshcd_cmd_log_entry *entry;
 
@@ -603,19 +606,18 @@
 	hba->cmd_log.pos =
 			(hba->cmd_log.pos + 1) % UFSHCD_MAX_CMD_LOGGING;
 
-	ufshcd_add_command_trace(hba, entry, opcode);
+	ufshcd_add_command_trace(hba, entry);
 }
 
 static void ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type,
 	unsigned int tag, u8 cmd_id, u8 idn)
 {
-	__ufshcd_cmd_log(hba, str, cmd_type, tag, cmd_id, idn,
-			 0xff, (sector_t)-1, -1, -1);
+	__ufshcd_cmd_log(hba, str, cmd_type, tag, cmd_id, idn, 0, 0, 0);
 }
 
 static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
 {
-	ufshcd_cmd_log(hba, str, "dme", 0xff, cmd_id, 0xff);
+	ufshcd_cmd_log(hba, str, "dme", 0, cmd_id, 0);
 }
 
 static void ufshcd_print_cmd_log(struct ufs_hba *hba)
@@ -650,7 +652,7 @@
 
 static void __ufshcd_cmd_log(struct ufs_hba *hba, char *str, char *cmd_type,
 			     unsigned int tag, u8 cmd_id, u8 idn, u8 lun,
-			     sector_t lba, int transfer_len, u8 opcode)
+			     sector_t lba, int transfer_len)
 {
 	struct ufshcd_cmd_log_entry entry;
 
@@ -660,7 +662,7 @@
 	entry.doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 	entry.tag = tag;
 
-	ufshcd_add_command_trace(hba, &entry, opcode);
+	ufshcd_add_command_trace(hba, &entry);
 }
 
 static void ufshcd_dme_cmd_log(struct ufs_hba *hba, char *str, u8 cmd_id)
@@ -680,8 +682,8 @@
 	char *cmd_type = NULL;
 	u8 opcode = 0;
 	u8 cmd_id = 0, idn = 0;
-	sector_t lba = -1;
-	int transfer_len = -1;
+	sector_t lba = 0;
+	int transfer_len = 0;
 
 	lrbp = &hba->lrb[tag];
 
@@ -715,7 +717,7 @@
 	}
 
 	__ufshcd_cmd_log(hba, (char *) str, cmd_type, tag, cmd_id, idn,
-			 lrbp->lun, lba, transfer_len, opcode);
+			 lrbp->lun, lba, transfer_len);
 }
 #else
 static inline void ufshcd_cond_add_cmd_trace(struct ufs_hba *hba,
@@ -864,6 +866,24 @@
 	}
 }
 
+static void ufshcd_print_fsm_state(struct ufs_hba *hba)
+{
+	int err = 0, tx_fsm_val = 0, rx_fsm_val = 0;
+
+	err = ufshcd_dme_get(hba,
+			UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
+			UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
+			&tx_fsm_val);
+	dev_err(hba->dev, "%s: TX_FSM_STATE = %u, err = %d\n", __func__,
+			tx_fsm_val, err);
+	err = ufshcd_dme_get(hba,
+			UIC_ARG_MIB_SEL(MPHY_RX_FSM_STATE,
+			UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)),
+			&rx_fsm_val);
+	dev_err(hba->dev, "%s: RX_FSM_STATE = %u, err = %d\n", __func__,
+			rx_fsm_val, err);
+}
+
 static void ufshcd_print_host_state(struct ufs_hba *hba)
 {
 	if (!(hba->ufshcd_dbg_print & UFSHCD_DBG_PRINT_HOST_STATE_EN))
@@ -2344,7 +2364,8 @@
 	ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
 	/* Make sure that doorbell is committed immediately */
 	wmb();
-	ufshcd_cond_add_cmd_trace(hba, task_tag, "send");
+	ufshcd_cond_add_cmd_trace(hba, task_tag,
+			hba->lrb[task_tag].cmd ? "scsi_send" : "dev_cmd_send");
 	ufshcd_update_tag_stats(hba, task_tag);
 	return ret;
 }
@@ -2461,7 +2482,7 @@
 
 	hba->active_uic_cmd = uic_cmd;
 
-	ufshcd_dme_cmd_log(hba, "send", hba->active_uic_cmd->command);
+	ufshcd_dme_cmd_log(hba, "dme_send", hba->active_uic_cmd->command);
 	/* Write Args */
 	ufshcd_writel(hba, uic_cmd->argument1, REG_UIC_COMMAND_ARG_1);
 	ufshcd_writel(hba, uic_cmd->argument2, REG_UIC_COMMAND_ARG_2);
@@ -2495,7 +2516,7 @@
 	if (ret)
 		ufsdbg_set_err_state(hba);
 
-	ufshcd_dme_cmd_log(hba, "cmp1", hba->active_uic_cmd->command);
+	ufshcd_dme_cmd_log(hba, "dme_cmpl_1", hba->active_uic_cmd->command);
 
 	spin_lock_irqsave(hba->host->host_lock, flags);
 	hba->active_uic_cmd = NULL;
@@ -4428,7 +4449,7 @@
 			cmd->command, status);
 		ret = (status != PWR_OK) ? status : -1;
 	}
-	ufshcd_dme_cmd_log(hba, "cmp2", hba->active_uic_cmd->command);
+	ufshcd_dme_cmd_log(hba, "dme_cmpl_2", hba->active_uic_cmd->command);
 
 out:
 	if (ret) {
@@ -4783,8 +4804,9 @@
 	int ret = 0;
 
 	/* if already configured to the requested pwr_mode */
-	if (pwr_mode->gear_rx == hba->pwr_info.gear_rx &&
-	    pwr_mode->gear_tx == hba->pwr_info.gear_tx &&
+	if (!hba->restore_needed &&
+		pwr_mode->gear_rx == hba->pwr_info.gear_rx &&
+		pwr_mode->gear_tx == hba->pwr_info.gear_tx &&
 	    pwr_mode->lane_rx == hba->pwr_info.lane_rx &&
 	    pwr_mode->lane_tx == hba->pwr_info.lane_tx &&
 	    pwr_mode->pwr_rx == hba->pwr_info.pwr_rx &&
@@ -4856,6 +4878,7 @@
 
 		memcpy(&hba->pwr_info, pwr_mode,
 			sizeof(struct ufs_pa_layer_attr));
+		hba->ufs_stats.power_mode_change_cnt++;
 	}
 
 	return ret;
@@ -5671,7 +5694,7 @@
 		lrbp = &hba->lrb[index];
 		cmd = lrbp->cmd;
 		if (cmd) {
-			ufshcd_cond_add_cmd_trace(hba, index, "failed");
+			ufshcd_cond_add_cmd_trace(hba, index, "scsi_failed");
 			ufshcd_update_error_stats(hba,
 					UFS_ERR_INT_FATAL_ERRORS);
 			scsi_dma_unmap(cmd);
@@ -5701,7 +5724,7 @@
 		} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) {
 			if (hba->dev_cmd.complete) {
 				ufshcd_cond_add_cmd_trace(hba, index,
-							"dev_failed");
+							"dev_cmd_failed");
 				ufshcd_outstanding_req_clear(hba, index);
 				complete(hba->dev_cmd.complete);
 			}
@@ -5729,7 +5752,7 @@
 		lrbp = &hba->lrb[index];
 		cmd = lrbp->cmd;
 		if (cmd) {
-			ufshcd_cond_add_cmd_trace(hba, index, "complete");
+			ufshcd_cond_add_cmd_trace(hba, index, "scsi_cmpl");
 			ufshcd_update_tag_stats_completion(hba, cmd);
 			result = ufshcd_transfer_rsp_status(hba, lrbp);
 			scsi_dma_unmap(cmd);
@@ -5775,7 +5798,7 @@
 		} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) {
 			if (hba->dev_cmd.complete) {
 				ufshcd_cond_add_cmd_trace(hba, index,
-						"dcmp");
+						"dev_cmd_cmpl");
 				complete(hba->dev_cmd.complete);
 			}
 		}
@@ -6451,6 +6474,52 @@
 	reg_hist->pos = (reg_hist->pos + 1) % UIC_ERR_REG_HIST_LENGTH;
 }
 
+static void ufshcd_rls_handler(struct work_struct *work)
+{
+	struct ufs_hba *hba;
+	int ret = 0;
+	u32 mode;
+
+	hba = container_of(work, struct ufs_hba, rls_work);
+	ufshcd_scsi_block_requests(hba);
+	pm_runtime_get_sync(hba->dev);
+	ret = ufshcd_wait_for_doorbell_clr(hba, U64_MAX);
+	if (ret) {
+		dev_err(hba->dev,
+			"Timed out (%d) waiting for DB to clear\n",
+			ret);
+		goto out;
+	}
+
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PWRMODE), &mode);
+	if (hba->pwr_info.pwr_rx != ((mode >> PWR_RX_OFFSET) & PWR_INFO_MASK))
+		hba->restore_needed = true;
+
+	if (hba->pwr_info.pwr_tx != (mode & PWR_INFO_MASK))
+		hba->restore_needed = true;
+
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_RXGEAR), &mode);
+	if (hba->pwr_info.gear_rx != mode)
+		hba->restore_needed = true;
+
+	ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TXGEAR), &mode);
+	if (hba->pwr_info.gear_tx != mode)
+		hba->restore_needed = true;
+
+	if (hba->restore_needed)
+		ret = ufshcd_config_pwr_mode(hba, &(hba->pwr_info));
+
+	if (ret)
+		dev_err(hba->dev, "%s: Failed setting power mode, err = %d\n",
+			__func__, ret);
+	else
+		hba->restore_needed = false;
+
+out:
+	ufshcd_scsi_unblock_requests(hba);
+	pm_runtime_put_sync(hba->dev);
+}
+
 /**
  * ufshcd_update_uic_error - check and set fatal UIC error flags.
  * @hba: per-adapter instance
@@ -6490,6 +6559,8 @@
 					hba->full_init_linereset = true;
 				}
 			}
+			if (!hba->full_init_linereset)
+				schedule_work(&hba->rls_work);
 		}
 		retval |= IRQ_HANDLED;
 	}
@@ -6961,6 +7032,7 @@
 	 */
 	scsi_print_command(cmd);
 	if (!hba->req_abort_count) {
+		ufshcd_print_fsm_state(hba);
 		ufshcd_print_host_regs(hba);
 		ufshcd_print_host_state(hba);
 		ufshcd_print_pwr_info(hba);
@@ -7151,6 +7223,12 @@
 	} while (err && --retries);
 
 	/*
+	 * There is no point proceeding even after failing
+	 * to recover after multiple retries.
+	 */
+	if (err && ufshcd_is_embedded_dev(hba))
+		BUG();
+	/*
 	 * After reset the door-bell might be cleared, complete
 	 * outstanding requests in s/w here.
 	 */
@@ -7615,9 +7693,6 @@
 {
 	int err_reg_hist_size = sizeof(struct ufs_uic_err_reg_hist);
 
-	hba->ufs_stats.hibern8_exit_cnt = 0;
-	hba->ufs_stats.last_hibern8_exit_tstamp = ktime_set(0, 0);
-
 	memset(&hba->ufs_stats.pa_err, 0, err_reg_hist_size);
 	memset(&hba->ufs_stats.dl_err, 0, err_reg_hist_size);
 	memset(&hba->ufs_stats.nl_err, 0, err_reg_hist_size);
@@ -9689,6 +9764,8 @@
 
 static void ufshcd_shutdown_clkscaling(struct ufs_hba *hba)
 {
+	if (!ufshcd_is_clkscaling_supported(hba))
+		return;
 	__ufshcd_shutdown_clkscaling(hba);
 	device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
 }
@@ -10424,6 +10501,7 @@
 	INIT_WORK(&hba->eh_work, ufshcd_err_handler);
 	INIT_WORK(&hba->eeh_work, ufshcd_exception_event_handler);
 	INIT_WORK(&hba->card_detect_work, ufshcd_card_detect_handler);
+	INIT_WORK(&hba->rls_work, ufshcd_rls_handler);
 
 	/* Initialize UIC command mutex */
 	mutex_init(&hba->uic_cmd_mutex);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index fc855db..1f5c404 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -644,6 +644,7 @@
 	struct ufshcd_clk_ctx clk_rel;
 	u32 hibern8_exit_cnt;
 	ktime_t last_hibern8_exit_tstamp;
+	u32 power_mode_change_cnt;
 	struct ufs_uic_err_reg_hist pa_err;
 	struct ufs_uic_err_reg_hist dl_err;
 	struct ufs_uic_err_reg_hist nl_err;
@@ -872,6 +873,7 @@
 	/* Work Queues */
 	struct work_struct eh_work;
 	struct work_struct eeh_work;
+	struct work_struct rls_work;
 
 	/* HBA Errors */
 	u32 errors;
@@ -986,6 +988,7 @@
 	int latency_hist_enabled;
 	struct io_latency_state io_lat_s;
 	struct ufs_desc_size desc_size;
+	bool restore_needed;
 };
 
 static inline void ufshcd_mark_shutdown_ongoing(struct ufs_hba *hba)
@@ -1198,6 +1201,14 @@
 		pwr_info->pwr_tx == FASTAUTO_MODE);
 }
 
+static inline bool ufshcd_is_embedded_dev(struct ufs_hba *hba)
+{
+	if ((hba->dev_info.b_device_sub_class == UFS_DEV_EMBEDDED_BOOTABLE) ||
+	    (hba->dev_info.b_device_sub_class == UFS_DEV_EMBEDDED_NON_BOOTABLE))
+		return true;
+	return false;
+}
+
 #ifdef CONFIG_DEBUG_FS
 static inline void ufshcd_init_req_stats(struct ufs_hba *hba)
 {
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index ca56462..5fb0c29 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -422,9 +422,11 @@
 	u8 txn_mt;
 	u16 txn_mc = txn->mc;
 	u8 wbuf[SLIM_MSGQ_BUF_LEN];
+	const u8 *old_wbuf = NULL;
 	bool report_sat = false;
 	bool sync_wr = true;
 
+	memset(wbuf, 0, sizeof(wbuf));
 	if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
 		return -EPROTONOSUPPORT;
 
@@ -590,6 +592,7 @@
 			goto ngd_xfer_err;
 		}
 		txn->len = i;
+		old_wbuf = txn->wbuf;
 		txn->wbuf = wbuf;
 		txn->rl = txn->len + 4;
 	}
@@ -758,13 +761,18 @@
 			ctrl->txnt[txn->tid] = NULL;
 			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
 		}
-		return ret ? ret : dev->err;
+		goto ngd_xfer_ret;
 	}
 ngd_xfer_err:
 	if (!report_sat) {
 		mutex_unlock(&dev->tx_lock);
 		msm_slim_put_ctrl(dev);
 	}
+ngd_xfer_ret:
+	if (txn->wbuf == wbuf)
+		txn->wbuf = old_wbuf;
+	if (txn->comp == &done)
+		txn->comp = NULL;
 	return ret ? ret : dev->err;
 }
 
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index 567f290..f34467b 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -333,6 +333,20 @@
 	}
 }
 
+static void slim_device_reset(struct work_struct *work)
+{
+	struct slim_driver *sbdrv;
+	struct slim_device *sbdev =
+			container_of(work, struct slim_device, device_reset);
+
+	if (!sbdev->dev.driver)
+		return;
+
+	sbdrv = to_slim_driver(sbdev->dev.driver);
+	if (sbdrv && sbdrv->reset_device)
+		sbdrv->reset_device(sbdev);
+}
+
 /*
  * slim_add_device: Add a new device without register board info.
  * @ctrl: Controller to which this device is to be added to.
@@ -353,6 +367,7 @@
 	INIT_LIST_HEAD(&sbdev->mark_suspend);
 	INIT_LIST_HEAD(&sbdev->mark_removal);
 	INIT_WORK(&sbdev->wd, slim_report);
+	INIT_WORK(&sbdev->device_reset, slim_device_reset);
 	mutex_lock(&ctrl->m_ctrl);
 	list_add_tail(&sbdev->dev_list, &ctrl->devs);
 	mutex_unlock(&ctrl->m_ctrl);
@@ -684,16 +699,9 @@
 	mutex_unlock(&ctrl->sched.m_reconf);
 	mutex_lock(&ctrl->m_ctrl);
 	list_for_each_safe(pos, next, &ctrl->devs) {
-		struct slim_driver *sbdrv;
-
 		sbdev = list_entry(pos, struct slim_device, dev_list);
-		mutex_unlock(&ctrl->m_ctrl);
-		if (sbdev && sbdev->dev.driver) {
-			sbdrv = to_slim_driver(sbdev->dev.driver);
-			if (sbdrv->reset_device)
-				sbdrv->reset_device(sbdev);
-		}
-		mutex_lock(&ctrl->m_ctrl);
+		if (sbdev)
+			queue_work(ctrl->wq, &sbdev->device_reset);
 	}
 	mutex_unlock(&ctrl->m_ctrl);
 }
@@ -2397,6 +2405,8 @@
 		bool opensl1valid = false;
 		int maxctrlw1, maxctrlw3, i;
 
+		/* intitalize array to zero */
+		memset(opensl1, 0x0, sizeof(opensl1));
 		finalexp = (ctrl->sched.chc3[last3])->rootexp;
 		if (last1 >= 0) {
 			slc1 = ctrl->sched.chc1[coeff1];
@@ -2510,7 +2520,7 @@
 				}
 			}
 			/* schedule 4k family channels */
-			while (coeff1 < ctrl->sched.num_cc1 &&
+			while (coeff1 < ctrl->sched.num_cc1 && slc1 &&
 				curexp == (int)slc1->rootexp + expshft) {
 				/* searchorder effective when opensl valid */
 				static const int srcho[] = { 5, 2, 4, 1, 3, 0 };
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index a8fb8b6..18aaacc 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -71,6 +71,16 @@
 	  deadlock detection mode AMON will trigger an interrupt if some LLCC request
 	  ages out.
 
+config QCOM_LLCC_PERFMON
+	tristate "Qualcomm Technologies, Inc. LLCC Perfmon driver"
+	depends on QCOM_LLCC
+	help
+	  This option enables driver for LLCC Performance monitor block. Using
+	  this various events in different LLCC sub block ports can be monitored.
+	  This is used for performance and debug activity and exports SYSFS
+	  interface. SYSFS interface used for configure and dump the LLCC
+	  performance events.
+
 config QCOM_PM
 	bool "Qualcomm Power Management"
 	depends on ARCH_QCOM && !ARM64
@@ -375,16 +385,6 @@
 	  32-bit values by specifying a unique string and
 	  remote processor ID.
 
-config MSM_SMP2P_TEST
-	bool "SMSM Point-to-Point Test"
-	depends on MSM_SMP2P
-	help
-	  Enables loopback and unit testing support for
-	  SMP2P. Loopback support is used by other
-	  processors to do unit testing. Unit tests
-	  are used to verify the local and remote
-	  implementations.
-
 config MSM_IPC_ROUTER_SMD_XPRT
 	depends on MSM_SMD
 	depends on IPC_ROUTER
@@ -537,16 +537,6 @@
 	  online at any given point in time. This module can also restrict
 	  max freq or min freq of cpu cluster
 
-config MSM_PERFORMANCE_HOTPLUG_ON
-	bool "Hotplug functionality through msm_performance turned on"
-	depends on MSM_PERFORMANCE
-	default y
-	help
-	  If some other core-control driver is present turn off the core-control
-	  capability of msm_performance driver. Setting this flag to false will
-	  compile out the nodes needed for core-control functionality through
-	  msm_performance.
-
 config MSM_CDSP_LOADER
 	tristate "CDSP loader support"
 	depends on MSM_GLINK
@@ -600,13 +590,6 @@
 	  trusted apps, unloading them and marshalling buffers to the
 	  trusted fingerprint app.
 
-config APSS_CORE_EA
-	depends on CPU_FREQ && PM_OPP
-	bool "Qualcomm Technology Inc specific power aware driver"
-	help
-	  Platform specific power aware driver to provide power
-	  and temperature information to the scheduler.
-
 if MSM_PM
 menuconfig MSM_IDLE_STATS
 	bool "Collect idle statistics"
@@ -703,3 +686,11 @@
 	  and ETM registers are saved and restored across power collapse.
 	  If unsure, say 'N' here to avoid potential power, performance and
 	  memory penalty.
+
+config QCOM_QDSS_BRIDGE
+	bool "Configure bridge driver for QTI/Qualcomm Technologies, Inc. MDM"
+	depends on MSM_MHI
+	help
+	  The driver will help route diag traffic from modem side over the QDSS
+	  sub-system to USB on APSS side. The driver acts as a bridge between the
+	  MHI and USB interface. If unsure, say N.
\ No newline at end of file
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 6eef58f..bb08357 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_QCOM_LLCC) += llcc-core.o llcc-slice.o
 obj-$(CONFIG_QCOM_SDM845_LLCC) += llcc-sdm845.o
 obj-$(CONFIG_QCOM_SDM670_LLCC) += llcc-sdm670.o
+obj-$(CONFIG_QCOM_LLCC_PERFMON) += llcc_perfmon.o
 obj-$(CONFIG_QCOM_LLCC_AMON) += llcc-amon.o
 obj-$(CONFIG_QPNP_PBS) += qpnp-pbs.o
 obj-$(CONFIG_QCOM_PM)	+=	spm.o
@@ -42,8 +43,7 @@
 obj-$(CONFIG_QTI_SYSTEM_PM) += system_pm.o
 obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
 obj-$(CONFIG_MSM_SERVICE_LOCATOR) += service-locator.o
-obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_debug.o smp2p_sleepstate.o
-obj-$(CONFIG_MSM_SMP2P_TEST) += smp2p_loopback.o smp2p_test.o smp2p_spinlock_test.o
+obj-$(CONFIG_MSM_SMP2P) += msm_smp2p.o smp2p_loopback.o smp2p_debug.o smp2p_sleepstate.o
 obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o
 obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o
 obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o
@@ -54,7 +54,7 @@
 obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR)	+=	system_health_monitor_v01.o
 obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR)	+=	system_health_monitor.o
 obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
-obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o icnss_utils.o
+obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o
 
 obj-$(CONFIG_MEM_SHARE_QMI_SERVICE)		+= memshare/
 obj-$(CONFIG_MSM_PIL_SSR_GENERIC) += subsys-pil-tz.o
@@ -73,10 +73,10 @@
 obj-$(CONFIG_MSM_QBT1000) += qbt1000.o
 obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
 obj-$(CONFIG_MSM_IDLE_STATS)	+= lpm-stats.o
-obj-$(CONFIG_APSS_CORE_EA)	+= msm-core.o debug_core.o
 obj-$(CONFIG_QCOM_DCC_V2) += dcc_v2.o
 obj-$(CONFIG_QTI_RPM_STATS_LOG) += rpm_stats.o
 obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o
 obj-$(CONFIG_QMP_DEBUGFS_CLIENT) += qmp-debugfs-client.o
 obj-$(CONFIG_MSM_REMOTEQDSS) += remoteqdss.o
 obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o
+obj-$(CONFIG_QCOM_QDSS_BRIDGE) += qdss_bridge.o
\ No newline at end of file
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index 252bd21..72abf50 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -197,6 +197,7 @@
 			len);
 	return len;
 }
+EXPORT_SYMBOL(cmd_db_get_aux_data);
 
 int cmd_db_get_aux_data_len(const char *resource_id)
 {
@@ -208,6 +209,7 @@
 
 	return ret < 0 ? 0 : ent.len;
 }
+EXPORT_SYMBOL(cmd_db_get_aux_data_len);
 
 u16 cmd_db_get_version(const char *resource_id)
 {
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index 2a23ba7..cff407e 100644
--- a/drivers/soc/qcom/dcc_v2.c
+++ b/drivers/soc/qcom/dcc_v2.c
@@ -22,6 +22,7 @@
 #include <linux/uaccess.h>
 #include <soc/qcom/memory_dump.h>
 #include <soc/qcom/scm.h>
+#include <dt-bindings/soc/qcom,dcc_v2.h>
 
 #define TIMEOUT_US		(100)
 
@@ -148,11 +149,6 @@
 	struct class		*sram_class;
 	struct list_head	cfg_head[DCC_MAX_LINK_LIST];
 	uint32_t		nr_config[DCC_MAX_LINK_LIST];
-	void			*reg_buf;
-	struct msm_dump_data	reg_data;
-	bool			save_reg;
-	void			*sram_buf;
-	struct msm_dump_data	sram_data;
 	uint8_t			curr_list;
 	uint8_t			cti_trig;
 };
@@ -490,39 +486,6 @@
 	return ret;
 }
 
-static void __dcc_reg_dump(struct dcc_drvdata *drvdata)
-{
-	uint32_t *reg_buf;
-	uint8_t i = 0;
-	uint8_t j;
-
-	if (!drvdata->reg_buf)
-		return;
-
-	drvdata->reg_data.version = DCC_REG_DUMP_VER;
-
-	reg_buf = drvdata->reg_buf;
-
-	reg_buf[i++] = dcc_readl(drvdata, DCC_HW_VERSION);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_HW_INFO);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_EXEC_CTRL);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_STATUS);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_CFG);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_FDA_CURR);
-	reg_buf[i++] = dcc_readl(drvdata, DCC_LLA_CURR);
-
-	for (j = 0; j < DCC_MAX_LINK_LIST; j++)
-		reg_buf[i++] = dcc_readl(drvdata, DCC_LL_LOCK(j));
-	for (j = 0; j < DCC_MAX_LINK_LIST; j++)
-		reg_buf[i++] = dcc_readl(drvdata, DCC_LL_CFG(j));
-	for (j = 0; j < DCC_MAX_LINK_LIST; j++)
-		reg_buf[i++] = dcc_readl(drvdata, DCC_LL_BASE(j));
-	for (j = 0; j < DCC_MAX_LINK_LIST; j++)
-		reg_buf[i++] = dcc_readl(drvdata, DCC_FD_BASE(j));
-
-	drvdata->reg_data.magic = DCC_REG_DUMP_MAGIC_V2;
-}
-
 static void __dcc_first_crc(struct dcc_drvdata *drvdata)
 {
 	int i;
@@ -574,7 +537,7 @@
 
 	mutex_lock(&drvdata->mutex);
 
-	memset_io(drvdata->ram_base, 0, drvdata->ram_size);
+	memset_io(drvdata->ram_base, 0xDE, drvdata->ram_size);
 
 	for (list = 0; list < DCC_MAX_LINK_LIST; list++) {
 
@@ -592,14 +555,12 @@
 			goto err;
 		}
 
-		/* 3. If in capture mode program DCC_RAM_CFG reg */
-		if (drvdata->func_type[list] == DCC_FUNC_TYPE_CAPTURE) {
-			dcc_writel(drvdata, ram_cfg_base +
-				   drvdata->ram_offset/4, DCC_LL_BASE(list));
-			dcc_writel(drvdata, drvdata->ram_start +
-				   drvdata->ram_offset/4, DCC_FD_BASE(list));
-			dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
-		}
+		/* 3. program DCC_RAM_CFG reg */
+		dcc_writel(drvdata, ram_cfg_base +
+			   drvdata->ram_offset/4, DCC_LL_BASE(list));
+		dcc_writel(drvdata, drvdata->ram_start +
+			   drvdata->ram_offset/4, DCC_FD_BASE(list));
+		dcc_writel(drvdata, 0xFFF, DCC_LL_TIMEOUT(list));
 
 		/* 4. Configure trigger, data sink and function type */
 		dcc_writel(drvdata, BIT(9) | ((drvdata->cti_trig << 8) |
@@ -626,9 +587,6 @@
 					   DCC_LL_INT_ENABLE(list));
 		}
 	}
-	/* Save DCC registers */
-	if (drvdata->save_reg)
-		__dcc_reg_dump(drvdata);
 
 err:
 	mutex_unlock(&drvdata->mutex);
@@ -653,9 +611,6 @@
 	}
 	drvdata->ram_cfg = 0;
 	drvdata->ram_start = 0;
-	/* Save DCC registers */
-	if (drvdata->save_reg)
-		__dcc_reg_dump(drvdata);
 
 	mutex_unlock(&drvdata->mutex);
 }
@@ -857,6 +812,9 @@
 
 	buf[0] = '\0';
 
+	if (drvdata->curr_list >= DCC_MAX_LINK_LIST)
+		return -EINVAL;
+
 	mutex_lock(&drvdata->mutex);
 	list_for_each_entry(entry,
 			    &drvdata->cfg_head[drvdata->curr_list], list) {
@@ -1132,14 +1090,30 @@
 static DEVICE_ATTR(interrupt_disable, 0644,
 		   dcc_show_interrupt_disable, dcc_store_interrupt_disable);
 
+static int dcc_add_loop(struct dcc_drvdata *drvdata, unsigned long loop_cnt)
+{
+	struct dcc_config_entry *entry;
+
+	entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT);
+	entry->index = drvdata->nr_config[drvdata->curr_list]++;
+	entry->desc_type = DCC_LOOP_TYPE;
+	INIT_LIST_HEAD(&entry->list);
+	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+
+	return 0;
+}
+
 static ssize_t dcc_store_loop(struct device *dev,
 				    struct device_attribute *attr,
 				    const char *buf, size_t size)
 {
-	int ret = size;
+	int ret;
 	unsigned long loop_cnt;
 	struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
-	struct dcc_config_entry *entry;
 
 	mutex_lock(&drvdata->mutex);
 
@@ -1154,18 +1128,12 @@
 		goto err;
 	}
 
-	entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
-	if (!entry) {
-		ret = -ENOMEM;
+	ret = dcc_add_loop(drvdata, loop_cnt);
+	if (ret)
 		goto err;
-	}
 
-	entry->loop_cnt = min_t(uint32_t, loop_cnt, MAX_LOOP_CNT);
-	entry->index = drvdata->nr_config[drvdata->curr_list]++;
-	entry->desc_type = DCC_LOOP_TYPE;
-	INIT_LIST_HEAD(&entry->list);
-	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
-
+	mutex_unlock(&drvdata->mutex);
+	return size;
 err:
 	mutex_unlock(&drvdata->mutex);
 	return ret;
@@ -1215,16 +1183,37 @@
 }
 static DEVICE_ATTR(rd_mod_wr, 0200, NULL, dcc_rd_mod_wr);
 
+static int dcc_add_write(struct dcc_drvdata *drvdata, unsigned int addr,
+			 unsigned int write_val, int apb_bus)
+{
+	struct dcc_config_entry *entry;
+
+	entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->desc_type = DCC_WRITE_TYPE;
+	entry->base = addr & BM(4, 31);
+	entry->offset = addr - entry->base;
+	entry->write_val = write_val;
+	entry->index = drvdata->nr_config[drvdata->curr_list]++;
+	entry->len = 1;
+	entry->apb_bus = apb_bus;
+	INIT_LIST_HEAD(&entry->list);
+	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+
+	return 0;
+}
+
 static ssize_t dcc_write(struct device *dev,
 				    struct device_attribute *attr,
 				    const char *buf, size_t size)
 {
-	int ret = size;
+	int ret;
 	int nval;
 	unsigned int addr, write_val;
-	int apb_bus;
+	int apb_bus = 0;
 	struct dcc_drvdata *drvdata = dev_get_drvdata(dev);
-	struct dcc_config_entry *entry;
 
 	mutex_lock(&drvdata->mutex);
 
@@ -1241,23 +1230,15 @@
 		goto err;
 	}
 
-	entry = devm_kzalloc(drvdata->dev, sizeof(*entry), GFP_KERNEL);
-	if (!entry) {
-		ret = -ENOMEM;
+	if (nval == 3 && apb_bus != 0)
+		apb_bus = 1;
+
+	ret = dcc_add_write(drvdata, addr, write_val, apb_bus);
+	if (ret)
 		goto err;
-	}
 
-	if (nval == 3)
-		entry->apb_bus = true;
-
-	entry->desc_type = DCC_WRITE_TYPE;
-	entry->base = addr & BM(4, 31);
-	entry->offset = addr - entry->base;
-	entry->write_val = write_val;
-	entry->index = drvdata->nr_config[drvdata->curr_list]++;
-	entry->len = 1;
-	INIT_LIST_HEAD(&entry->list);
-	list_add_tail(&entry->list, &drvdata->cfg_head[drvdata->curr_list]);
+	mutex_unlock(&drvdata->mutex);
+	return size;
 err:
 	mutex_unlock(&drvdata->mutex);
 	return ret;
@@ -1462,44 +1443,61 @@
 	dcc_sram_dev_deregister(drvdata);
 }
 
-static void dcc_allocate_dump_mem(struct dcc_drvdata *drvdata)
+static void dcc_configure_list(struct dcc_drvdata *drvdata,
+			       struct device_node *np)
 {
-	int ret;
-	struct device *dev = drvdata->dev;
-	struct msm_dump_entry reg_dump_entry, sram_dump_entry;
+	int ret, i;
+	const __be32 *prop;
+	uint32_t len, entry, val1, val2, apb_bus;
+	uint32_t curr_link_list;
 
-	/* Allocate memory for dcc reg dump */
-	drvdata->reg_buf = devm_kzalloc(dev, drvdata->reg_size, GFP_KERNEL);
-	if (drvdata->reg_buf) {
-		drvdata->reg_data.addr = virt_to_phys(drvdata->reg_buf);
-		drvdata->reg_data.len = drvdata->reg_size;
-		reg_dump_entry.id = MSM_DUMP_DATA_DCC_REG;
-		reg_dump_entry.addr = virt_to_phys(&drvdata->reg_data);
-		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
-					     &reg_dump_entry);
-		if (ret) {
-			dev_err(dev, "DCC REG dump setup failed\n");
-			devm_kfree(dev, drvdata->reg_buf);
-		}
-	} else {
-		dev_err(dev, "DCC REG dump allocation failed\n");
+	ret = of_property_read_u32(np, "qcom,curr-link-list",
+				   &curr_link_list);
+	if (ret)
+		return;
+
+	if (curr_link_list >= DCC_MAX_LINK_LIST) {
+		dev_err(drvdata->dev, "List configuration failed");
+		return;
 	}
+	drvdata->curr_list = curr_link_list;
 
-	/* Allocate memory for dcc sram dump */
-	drvdata->sram_buf = devm_kzalloc(dev, drvdata->ram_size, GFP_KERNEL);
-	if (drvdata->sram_buf) {
-		drvdata->sram_data.addr = virt_to_phys(drvdata->sram_buf);
-		drvdata->sram_data.len = drvdata->ram_size;
-		sram_dump_entry.id = MSM_DUMP_DATA_DCC_SRAM;
-		sram_dump_entry.addr = virt_to_phys(&drvdata->sram_data);
-		ret = msm_dump_data_register(MSM_DUMP_TABLE_APPS,
-					     &sram_dump_entry);
-		if (ret) {
-			dev_err(dev, "DCC SRAM dump setup failed\n");
-			devm_kfree(dev, drvdata->sram_buf);
+	prop = of_get_property(np, "qcom,link-list", &len);
+	if (prop) {
+		len /= sizeof(__be32);
+		i = 0;
+		while (i < len) {
+			entry = be32_to_cpu(prop[i++]);
+			val1 = be32_to_cpu(prop[i++]);
+			val2 = be32_to_cpu(prop[i++]);
+			apb_bus = be32_to_cpu(prop[i++]);
+
+			switch (entry) {
+			case DCC_READ:
+				ret = dcc_config_add(drvdata, val1,
+						     val2, apb_bus);
+				break;
+			case DCC_WRITE:
+				ret = dcc_add_write(drvdata, val1,
+						    val2, apb_bus);
+				break;
+			case DCC_LOOP:
+				ret = dcc_add_loop(drvdata, val1);
+				break;
+			default:
+				ret = -EINVAL;
+			}
+
+			if (ret) {
+				dev_err(drvdata->dev,
+					"DCC init time config failed err:%d\n",
+					ret);
+				break;
+			}
 		}
-	} else {
-		dev_err(dev, "DCC SRAM dump allocation failed\n");
+
+		if (!ret)
+			dcc_enable(drvdata);
 	}
 }
 
@@ -1542,9 +1540,6 @@
 	if (ret)
 		return -EINVAL;
 
-	drvdata->save_reg = of_property_read_bool(pdev->dev.of_node,
-						  "qcom,save-reg");
-
 	mutex_init(&drvdata->mutex);
 
 	for (i = 0; i < DCC_MAX_LINK_LIST; i++) {
@@ -1580,7 +1575,8 @@
 	if (ret)
 		goto err;
 
-	dcc_allocate_dump_mem(drvdata);
+	dcc_configure_list(drvdata, pdev->dev.of_node);
+
 	return 0;
 err:
 	return ret;
@@ -1614,15 +1610,17 @@
 
 static int __init dcc_init(void)
 {
+	int ret;
+
+	ret = scm_is_secure_device();
+	if (ret == 0) {
+		pr_info("DCC is not available\n");
+		return -ENODEV;
+	}
+
 	return platform_driver_register(&dcc_driver);
 }
-module_init(dcc_init);
-
-static void __exit dcc_exit(void)
-{
-	platform_driver_unregister(&dcc_driver);
-}
-module_exit(dcc_exit);
+pure_initcall(dcc_init);
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("MSM data capture and compare engine");
diff --git a/drivers/soc/qcom/debug_core.c b/drivers/soc/qcom/debug_core.c
deleted file mode 100644
index 164a866..0000000
--- a/drivers/soc/qcom/debug_core.c
+++ /dev/null
@@ -1,330 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/string.h>
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/cpu.h>
-#include "soc/qcom/msm-core.h"
-
-#define MAX_PSTATES 50
-#define NUM_OF_PENTRY 3 /* number of variables for ptable node */
-#define NUM_OF_EENTRY 2 /* number of variables for enable node */
-
-enum arg_offset {
-	CPU_OFFSET,
-	FREQ_OFFSET,
-	POWER_OFFSET,
-};
-
-struct core_debug {
-	int cpu;
-	struct cpu_pstate_pwr *head;
-	int enabled;
-	int len;
-	struct cpu_pwr_stats *ptr;
-	struct cpu_pstate_pwr *driver_data;
-	int driver_len;
-};
-
-static DEFINE_PER_CPU(struct core_debug, c_dgfs);
-static struct cpu_pwr_stats *msm_core_data;
-static struct debugfs_blob_wrapper help_msg = {
-	.data =
-"MSM CORE Debug-FS Support\n"
-"\n"
-"Hierarchy schema\n"
-"/sys/kernel/debug/msm_core\n"
-"  /help        - Static help text\n"
-"  /ptable      - write to p-state table\n"
-"  /enable      - enable the written p-state table\n"
-"  /ptable_dump - Dump the debug ptable\n"
-"\n"
-"Usage\n"
-" Input test frequency and power information in ptable:\n"
-" echo \"0 300000 120\" > ptable\n"
-" format: <cpu> <frequency in khz> <power>\n"
-"\n"
-" Enable the ptable for the cpu:\n"
-" echo \"0 1\" > enable\n"
-" format: <cpu> <1 to enable, 0 to disable>\n"
-" Note: Writing 0 to disable will reset/clear the ptable\n"
-"\n"
-" Dump the entire ptable:\n"
-" cat ptable\n"
-" -----  CPU0 - Enabled ---------\n"
-"     Freq       Power\n"
-"     700000       120\n"
-"-----  CPU0 - Live numbers -----\n"
-"   Freq       Power\n"
-"   300000      218\n"
-" -----  CPU1 - Written ---------\n"
-"     Freq       Power\n"
-"     700000       120\n"
-" Ptable dump will dump the status of the table as well\n"
-" It shows:\n"
-" Enabled -> for a cpu that debug ptable enabled\n"
-" Written -> for a cpu that has debug ptable values written\n"
-"            but not enabled\n"
-"\n",
-
-};
-
-static void add_to_ptable(unsigned int *arg)
-{
-	struct core_debug *node;
-	int i, cpu = arg[CPU_OFFSET];
-	uint32_t freq = arg[FREQ_OFFSET];
-	uint32_t power = arg[POWER_OFFSET];
-
-	if (!cpu_possible(cpu))
-		return;
-
-	if ((freq == 0) || (power == 0)) {
-		pr_warn("Incorrect power data\n");
-		return;
-	}
-
-	node = &per_cpu(c_dgfs, cpu);
-
-	if (node->len >= MAX_PSTATES) {
-		pr_warn("Dropped ptable update - no space left.\n");
-		return;
-	}
-
-	if (!node->head) {
-		node->head = kzalloc(sizeof(struct cpu_pstate_pwr) *
-				     (MAX_PSTATES + 1),
-					GFP_KERNEL);
-		if (!node->head)
-			return;
-	}
-
-	for (i = 0; i < node->len; i++) {
-		if (node->head[i].freq == freq) {
-			node->head[i].power = power;
-			return;
-		}
-	}
-
-	/*
-	 * Insert a new frequency (may need to move things around to
-	 * keep in ascending order).
-	 */
-	for (i = MAX_PSTATES - 1; i > 0; i--) {
-		if (node->head[i-1].freq > freq) {
-			node->head[i].freq = node->head[i-1].freq;
-			node->head[i].power = node->head[i-1].power;
-		} else if (node->head[i-1].freq != 0) {
-			break;
-		}
-	}
-
-	if (node->len < MAX_PSTATES) {
-		node->head[i].freq = freq;
-		node->head[i].power = power;
-		node->len++;
-	}
-
-	if (node->ptr)
-		node->ptr->len = node->len;
-}
-
-static int split_ptable_args(char *line, unsigned int *arg, uint32_t n)
-{
-	char *args;
-	int i;
-	int ret = 0;
-
-	for (i = 0; i < n; i++) {
-		if (!line)
-			break;
-		args = strsep(&line, " ");
-		ret = kstrtouint(args, 10, &arg[i]);
-		if (ret)
-			return ret;
-	}
-	return ret;
-}
-
-static ssize_t msm_core_ptable_write(struct file *file,
-		const char __user *ubuf, size_t len, loff_t *offp)
-{
-	char *kbuf;
-	int ret;
-	unsigned int arg[3];
-
-	if (len == 0)
-		return 0;
-
-	kbuf = kzalloc(len + 1, GFP_KERNEL);
-	if (!kbuf)
-		return -ENOMEM;
-
-	if (copy_from_user(kbuf, ubuf, len)) {
-		ret = -EFAULT;
-		goto done;
-	}
-	kbuf[len] = '\0';
-	ret = split_ptable_args(kbuf, arg, NUM_OF_PENTRY);
-	if (!ret) {
-		add_to_ptable(arg);
-		ret = len;
-	}
-done:
-	kfree(kbuf);
-	return ret;
-}
-
-static void print_table(struct seq_file *m, struct cpu_pstate_pwr *c_n,
-		int len)
-{
-	int i;
-
-	seq_puts(m, "   Freq       Power\n");
-	for (i = 0; i < len; i++)
-		seq_printf(m, "  %d       %u\n", c_n[i].freq,
-				c_n[i].power);
-
-}
-
-static int msm_core_ptable_read(struct seq_file *m, void *data)
-{
-	int cpu;
-	struct core_debug *node;
-
-	for_each_possible_cpu(cpu) {
-		node = &per_cpu(c_dgfs, cpu);
-		if (node->head) {
-			seq_printf(m, "-----  CPU%d - %s - Debug -------\n",
-			cpu, node->enabled == 1 ? "Enabled" : "Written");
-			print_table(m, node->head, node->len);
-		}
-		if (msm_core_data[cpu].ptable) {
-			seq_printf(m, "--- CPU%d - Live numbers at %ldC---\n",
-			cpu, node->ptr->temp);
-			print_table(m, msm_core_data[cpu].ptable,
-					node->driver_len);
-		}
-	}
-	return 0;
-}
-
-static ssize_t msm_core_enable_write(struct file *file,
-		const char __user *ubuf, size_t len, loff_t *offp)
-{
-	char *kbuf;
-	int ret;
-	unsigned int arg[3];
-	int cpu;
-
-	if (len == 0)
-		return 0;
-
-	kbuf = kzalloc(len + 1, GFP_KERNEL);
-	if (!kbuf)
-		return -ENOMEM;
-
-	if (copy_from_user(kbuf, ubuf, len)) {
-		ret = -EFAULT;
-		goto done;
-	}
-	kbuf[len] = '\0';
-	ret = split_ptable_args(kbuf, arg, NUM_OF_EENTRY);
-	if (ret)
-		goto done;
-	cpu = arg[CPU_OFFSET];
-
-	if (cpu_possible(cpu)) {
-		struct core_debug *node = &per_cpu(c_dgfs, cpu);
-
-		if (arg[FREQ_OFFSET]) {
-			msm_core_data[cpu].ptable = node->head;
-			msm_core_data[cpu].len = node->len;
-		} else {
-			msm_core_data[cpu].ptable = node->driver_data;
-			msm_core_data[cpu].len = node->driver_len;
-			node->len = 0;
-		}
-		node->enabled = arg[FREQ_OFFSET];
-	}
-	ret = len;
-	blocking_notifier_call_chain(
-			get_power_update_notifier(), cpu, NULL);
-
-done:
-	kfree(kbuf);
-	return ret;
-}
-
-static const struct file_operations msm_core_enable_ops = {
-	.write = msm_core_enable_write,
-};
-
-static int msm_core_dump_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, msm_core_ptable_read, inode->i_private);
-}
-
-static const struct file_operations msm_core_ptable_ops = {
-	.open = msm_core_dump_open,
-	.read = seq_read,
-	.write = msm_core_ptable_write,
-	.llseek = seq_lseek,
-	.release = single_release,
-};
-
-int msm_core_debug_init(void)
-{
-	struct dentry *dir = NULL;
-	struct dentry *file = NULL;
-	int i;
-
-	msm_core_data = get_cpu_pwr_stats();
-	if (!msm_core_data)
-		goto fail;
-
-	dir = debugfs_create_dir("msm_core", NULL);
-	if (IS_ERR_OR_NULL(dir))
-		return PTR_ERR(dir);
-
-	file = debugfs_create_file("enable", 0660, dir, NULL,
-			&msm_core_enable_ops);
-	if (IS_ERR_OR_NULL(file))
-		goto fail;
-
-	file = debugfs_create_file("ptable", 0660, dir, NULL,
-			&msm_core_ptable_ops);
-	if (IS_ERR_OR_NULL(file))
-		goto fail;
-
-	help_msg.size = strlen(help_msg.data);
-	file = debugfs_create_blob("help", 0444, dir, &help_msg);
-	if (IS_ERR_OR_NULL(file))
-		goto fail;
-
-	for (i = 0; i < num_possible_cpus(); i++) {
-		per_cpu(c_dgfs, i).ptr = &msm_core_data[i];
-		per_cpu(c_dgfs, i).driver_data = msm_core_data[i].ptable;
-		per_cpu(c_dgfs, i).driver_len = msm_core_data[i].len;
-	}
-	return 0;
-fail:
-	debugfs_remove(dir);
-	return PTR_ERR(file);
-}
-late_initcall(msm_core_debug_init);
diff --git a/drivers/soc/qcom/eud.c b/drivers/soc/qcom/eud.c
index 51c08c6..3c4238c 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -27,6 +27,11 @@
 #include <linux/serial.h>
 #include <linux/workqueue.h>
 #include <linux/power_supply.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
 
 #define EUD_ENABLE_CMD 1
 #define EUD_DISABLE_CMD 0
@@ -71,6 +76,11 @@
 	struct uart_port		port;
 	struct work_struct		eud_work;
 	struct power_supply		*batt_psy;
+	struct clk			*cfg_ahb_clk;
+
+	/* regulator and notifier chain for it */
+	struct regulator		*vdda33;
+	struct notifier_block		vdda33_nb;
 };
 
 static const unsigned int eud_extcon_cable[] = {
@@ -119,7 +129,7 @@
 		/* write into CSR to enable EUD */
 		writel_relaxed(BIT(0), priv->eud_reg_base + EUD_REG_CSR_EUD_EN);
 		/* Enable vbus, chgr & safe mode warning interrupts */
-		writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR | EUD_INT_SAFE_MODE,
+		writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR,
 				priv->eud_reg_base + EUD_REG_INT1_EN_MASK);
 
 		/* Ensure Register Writes Complete */
@@ -448,7 +458,11 @@
 {
 	struct eud_chip *chip = data;
 	u32 reg;
-	u32 int_mask_en1 = readl_relaxed(chip->eud_reg_base +
+	u32 int_mask_en1;
+
+	clk_prepare_enable(chip->cfg_ahb_clk);
+
+	int_mask_en1 = readl_relaxed(chip->eud_reg_base +
 					EUD_REG_INT1_EN_MASK);
 
 	/* read status register and find out which interrupt triggered */
@@ -472,18 +486,55 @@
 		pet_eud(chip);
 	} else {
 		dev_dbg(chip->dev, "Unknown/spurious EUD Interrupt!\n");
+		clk_disable_unprepare(chip->cfg_ahb_clk);
 		return IRQ_NONE;
 	}
 
+	clk_disable_unprepare(chip->cfg_ahb_clk);
 	return IRQ_HANDLED;
 }
 
+static int vdda33_notifier_block_cb(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct eud_chip *chip = container_of(nb, struct eud_chip, vdda33_nb);
+	int attach_det  = 0;
+
+	switch (event) {
+	case REGULATOR_EVENT_ENABLE:
+		attach_det = 1;
+		/* fall throuhg */
+	case REGULATOR_EVENT_DISABLE:
+		clk_prepare_enable(chip->cfg_ahb_clk);
+
+		/* eud does not retain interrupt mask when ldo24
+		 * is turned off. Set the interrupt mask when
+		 * ldo24 is turned on
+		 */
+		if (attach_det)
+			writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR,
+				chip->eud_reg_base + EUD_REG_INT1_EN_MASK);
+		writel_relaxed(attach_det,
+			chip->eud_reg_base + EUD_REG_SW_ATTACH_DET);
+		clk_disable_unprepare(chip->cfg_ahb_clk);
+
+		dev_dbg(chip->dev, "%s(): %s\n", __func__,
+			attach_det ? "enable" : "disable");
+		break;
+	default:
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
 static int msm_eud_probe(struct platform_device *pdev)
 {
 	struct eud_chip *chip;
 	struct uart_port *port;
 	struct resource *res;
 	int ret;
+	int pet;
 
 	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip) {
@@ -492,6 +543,7 @@
 	}
 
 	platform_set_drvdata(pdev, chip);
+	chip->dev = &pdev->dev;
 
 	chip->extcon = devm_extcon_dev_allocate(&pdev->dev, eud_extcon_cable);
 	if (IS_ERR(chip->extcon)) {
@@ -517,10 +569,25 @@
 	if (IS_ERR(chip->eud_reg_base))
 		return PTR_ERR(chip->eud_reg_base);
 
+	if (of_property_match_string(pdev->dev.of_node,
+				"clock-names", "cfg_ahb_clk") >= 0) {
+		chip->cfg_ahb_clk = devm_clk_get(&pdev->dev, "cfg_ahb_clk");
+		if (IS_ERR(chip->cfg_ahb_clk)) {
+			ret = PTR_ERR(chip->cfg_ahb_clk);
+			if (ret != -EPROBE_DEFER)
+				dev_err(chip->dev,
+				"clk get failed for cfg_ahb_clk ret %d\n",
+				ret);
+			return ret;
+		}
+	}
+
 	chip->eud_irq = platform_get_irq_byname(pdev, "eud_irq");
 
-	ret = devm_request_irq(&pdev->dev, chip->eud_irq, handle_eud_irq,
-				IRQF_TRIGGER_HIGH, "eud_irq", chip);
+	ret = devm_request_threaded_irq(&pdev->dev, chip->eud_irq,
+					NULL, handle_eud_irq,
+					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+					"eud_irq", chip);
 	if (ret) {
 		dev_err(chip->dev, "request failed for eud irq\n");
 		return ret;
@@ -550,6 +617,24 @@
 
 	eud_private = pdev;
 
+	chip->vdda33 = devm_regulator_get(&pdev->dev, "vdda33");
+	if (IS_ERR(chip->vdda33)) {
+		dev_err(chip->dev, "%s: failed to get eud 3p1 regulator\n",
+					__func__);
+		return PTR_ERR(chip->vdda33);
+	}
+	chip->vdda33_nb.notifier_call = vdda33_notifier_block_cb;
+	regulator_register_notifier(chip->vdda33, &chip->vdda33_nb);
+
+	clk_prepare_enable(chip->cfg_ahb_clk);
+
+	pet = regulator_is_enabled(chip->vdda33) ? 1 : 0;
+	writel_relaxed(pet, chip->eud_reg_base + EUD_REG_SW_ATTACH_DET);
+
+	dev_dbg(chip->dev, "%s:%s pet\n", __func__, pet ? "Attach" : "Detach");
+
+	clk_disable_unprepare(chip->cfg_ahb_clk);
+
 	/* Enable EUD */
 	if (enable)
 		enable_eud(pdev);
@@ -562,6 +647,8 @@
 	struct eud_chip *chip = platform_get_drvdata(pdev);
 	struct uart_port *port = &chip->port;
 
+	regulator_unregister_notifier(chip->vdda33, &chip->vdda33_nb);
+
 	uart_remove_one_port(&eud_uart_driver, port);
 	device_init_wakeup(chip->dev, false);
 
diff --git a/drivers/soc/qcom/gladiator_erp.c b/drivers/soc/qcom/gladiator_erp.c
index 835d4b0..3b70ca4 100644
--- a/drivers/soc/qcom/gladiator_erp.c
+++ b/drivers/soc/qcom/gladiator_erp.c
@@ -163,6 +163,7 @@
 	DISCONNECT_ERROR,
 	DIRECTORY_ERROR,
 	PARITY_ERROR,
+	PHYSICAL_ADDRESS_ERROR,
 };
 
 static void clear_gladiator_error(void __iomem *gladiator_virt_base,
@@ -200,12 +201,14 @@
 
 static inline void print_gld_errtype(unsigned int errtype)
 {
+	char *errors = "Disconnect, Directory, Parity, Physical address";
+
 	if (errtype == 0)
 		pr_alert("Error type: Snoop data transfer\n");
 	else if (errtype == 1)
 		pr_alert("Error type: DVM error\n");
 	else if (errtype == 3)
-		pr_alert("Error type: Disconnect, directory, or parity error\n");
+		pr_alert("Error type: %s\n", errors);
 	else
 		pr_alert("Error type: Unknown; value:%u\n", errtype);
 }
@@ -288,7 +291,7 @@
 
 	log_err_type = (err_reg5 & mask_shifts->gld_errlog5_error_type_mask)
 		>> mask_shifts->gld_errlog5_error_type_shift;
-	for (i = 0 ; i <= 6 ; i++) {
+	for (i = 0 ; i <= 7 ; i++) {
 		value = log_err_type & 0x1;
 		switch (i) {
 		case DATA_TRANSFER_ERROR:
@@ -337,7 +340,14 @@
 					mask_shifts);
 			decode_index_parity(err_reg5, mask_shifts);
 			break;
+		case PHYSICAL_ADDRESS_ERROR:
+			if (value == 0)
+				continue;
+			pr_alert("Error type: Physical address error\n");
+			pr_alert("Address is greater than SoC address range\n");
+			break;
 		}
+
 		log_err_type = log_err_type >> 1;
 	}
 }
diff --git a/drivers/soc/qcom/glink.c b/drivers/soc/qcom/glink.c
index 43f255e..e6fd52e 100644
--- a/drivers/soc/qcom/glink.c
+++ b/drivers/soc/qcom/glink.c
@@ -604,10 +604,11 @@
 static unsigned long glink_qos_calc_rate_kBps(size_t pkt_size,
 				       unsigned long interval_us)
 {
-	unsigned long rate_kBps, rem;
+	unsigned long rem;
+	uint64_t rate_kBps;
 
 	rate_kBps = pkt_size * USEC_PER_SEC;
-	rem = do_div(rate_kBps, (interval_us * 1024));
+	rem = do_div(rate_kBps, interval_us * 1024);
 	return rate_kBps;
 }
 
@@ -1668,6 +1669,8 @@
 				&ctx->local_rx_intent_list, list) {
 		ctx->notify_rx_abort(ctx, ctx->user_priv,
 				ptr_intent->pkt_priv);
+		ctx->transport_ptr->ops->deallocate_rx_intent(
+					ctx->transport_ptr->ops, ptr_intent);
 		list_del(&ptr_intent->list);
 		kfree(ptr_intent);
 	}
@@ -3766,6 +3769,8 @@
 	GLINK_INFO("%s: freeing transport [%s->%s]context\n", __func__,
 				xprt_ctx->name,
 				xprt_ctx->edge);
+	kfree(xprt_ctx->ops);
+	xprt_ctx->ops = NULL;
 	kfree(xprt_ctx);
 }
 
@@ -4069,6 +4074,7 @@
 		kfree(xprt_ptr);
 		return -ENOMEM;
 	}
+	cfg->tx_task = xprt_ptr->tx_task;
 	ret = glink_core_init_xprt_qos_cfg(xprt_ptr, cfg);
 	if (ret < 0) {
 		kfree(xprt_ptr);
@@ -4156,6 +4162,7 @@
 	rwref_write_get(&xprt_ptr->xprt_state_lhb0);
 	xprt_ptr->next_lcid = 1;
 	xprt_ptr->local_state = GLINK_XPRT_DOWN;
+	xprt_ptr->curr_qos_rate_kBps = 0;
 	xprt_ptr->local_version_idx = xprt_ptr->versions_entries - 1;
 	xprt_ptr->remote_version_idx = xprt_ptr->versions_entries - 1;
 	xprt_ptr->l_features =
@@ -4290,6 +4297,12 @@
 	rwref_read_get(&xprt_ptr->xprt_state_lhb0);
 	ctx = get_first_ch_ctx(xprt_ptr);
 	while (ctx) {
+		spin_lock_irqsave(&xprt_ptr->tx_ready_lock_lhb3, flags);
+		spin_lock(&ctx->tx_lists_lock_lhc3);
+		if (!list_empty(&ctx->tx_active))
+			glink_qos_done_ch_tx(ctx);
+		spin_unlock(&ctx->tx_lists_lock_lhc3);
+		spin_unlock_irqrestore(&xprt_ptr->tx_ready_lock_lhb3, flags);
 		rwref_write_get_atomic(&ctx->ch_state_lhb2, true);
 		if (ctx->local_open_state == GLINK_CHANNEL_OPENED ||
 			ctx->local_open_state == GLINK_CHANNEL_OPENING) {
@@ -5454,8 +5467,8 @@
 static void glink_scheduler_eval_prio(struct channel_ctx *ctx,
 			struct glink_core_xprt_ctx *xprt_ctx)
 {
-	unsigned long token_end_time;
-	unsigned long token_consume_time, rem;
+	unsigned long token_end_time, rem;
+	uint64_t token_consume_time;
 	unsigned long obs_rate_kBps;
 
 	if (ctx->initial_priority == 0)
diff --git a/drivers/soc/qcom/glink_core_if.h b/drivers/soc/qcom/glink_core_if.h
index 1411330..704171f 100644
--- a/drivers/soc/qcom/glink_core_if.h
+++ b/drivers/soc/qcom/glink_core_if.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
 
 #include <linux/of.h>
 #include <linux/types.h>
+#include <linux/sched.h>
 #include "glink_private.h"
 
 /* Local Channel state */
@@ -105,6 +106,7 @@
  * @versions_entries:	Number of entries in @versions.
  * @max_cid:		Maximum number of channel identifiers supported.
  * @max_iid:		Maximum number of intent identifiers supported.
+ * @tx_task:		Task structure for tx thread.
  * @mtu:		MTU supported by this transport.
  * @num_flows:		Number of traffic flows/priority buckets.
  * @flow_info:		Information about each flow/priority.
@@ -117,6 +119,7 @@
 	size_t versions_entries;
 	uint32_t max_cid;
 	uint32_t max_iid;
+	struct task_struct *tx_task;
 
 	size_t mtu;
 	uint32_t num_flows;
diff --git a/drivers/soc/qcom/glink_loopback_server.c b/drivers/soc/qcom/glink_loopback_server.c
index 4e9b118..94a3d8c 100644
--- a/drivers/soc/qcom/glink_loopback_server.c
+++ b/drivers/soc/qcom/glink_loopback_server.c
@@ -195,7 +195,7 @@
 	resp_pkt->response = response;
 
 	return glink_tx(handle, (void *)LINEAR, (void *)resp_pkt,
-			sizeof(struct resp), 0);
+			sizeof(struct resp), GLINK_TX_REQ_INTENT);
 }
 
 static uint32_t calc_delay_ms(uint32_t random_delay, uint32_t delay_ms)
@@ -1143,7 +1143,7 @@
 			return;
 		}
 
-		flags = 0;
+		flags = GLINK_TX_REQ_INTENT;
 		if (tmp_work_info->tracer_pkt) {
 			flags |= GLINK_TX_TRACER_PKT;
 			tracer_pkt_log_event(tmp_work_info->data,
diff --git a/drivers/soc/qcom/glink_smem_native_xprt.c b/drivers/soc/qcom/glink_smem_native_xprt.c
index 384347d..ea7374f 100644
--- a/drivers/soc/qcom/glink_smem_native_xprt.c
+++ b/drivers/soc/qcom/glink_smem_native_xprt.c
@@ -33,6 +33,7 @@
 #include <linux/spinlock.h>
 #include <linux/srcu.h>
 #include <linux/wait.h>
+#include <linux/cpumask.h>
 #include <soc/qcom/smem.h>
 #include <soc/qcom/tracer_pkt.h>
 #include "glink_core_if.h"
@@ -226,6 +227,7 @@
 	spinlock_t rt_vote_lock;
 	uint32_t rt_votes;
 	uint32_t num_pw_states;
+	uint32_t readback;
 	unsigned long *ramp_time_us;
 	struct mailbox_config_info *mailbox;
 };
@@ -270,6 +272,7 @@
 	 * Any data associated with this event must be visable to the remote
 	 * before the interrupt is triggered
 	 */
+	einfo->readback = einfo->tx_ch_desc->write_index;
 	wmb();
 	writel_relaxed(einfo->out_irq_mask, einfo->out_irq_reg);
 	if (einfo->remote_proc_id != SMEM_SPSS)
@@ -2321,17 +2324,40 @@
 	return -ENODEV;
 }
 
+static void glink_set_affinity(struct edge_info *einfo, u32 *arr, size_t size)
+{
+	struct cpumask cpumask;
+	pid_t pid;
+	int i;
+
+	cpumask_clear(&cpumask);
+	for (i = 0; i < size; i++) {
+		if (arr[i] < num_possible_cpus())
+			cpumask_set_cpu(arr[i], &cpumask);
+	}
+	if (irq_set_affinity(einfo->irq_line, &cpumask))
+		pr_err("%s: Failed to set irq affinity\n", __func__);
+
+	if (sched_setaffinity(einfo->task->pid, &cpumask))
+		pr_err("%s: Failed to set rx cpu affinity\n", __func__);
+
+	pid = einfo->xprt_cfg.tx_task->pid;
+	if (sched_setaffinity(pid, &cpumask))
+		pr_err("%s: Failed to set tx cpu affinity\n", __func__);
+}
+
 static int glink_smem_native_probe(struct platform_device *pdev)
 {
 	struct device_node *node;
 	struct device_node *phandle_node;
 	struct edge_info *einfo;
-	int rc;
+	int rc, cpu_size;
 	char *key;
 	const char *subsys_name;
 	uint32_t irq_line;
 	uint32_t irq_mask;
 	struct resource *r;
+	u32 *cpu_array;
 
 	node = pdev->dev.of_node;
 
@@ -2478,6 +2504,20 @@
 		pr_err("%s: enable_irq_wake() failed on %d\n", __func__,
 								irq_line);
 
+	key = "cpu-affinity";
+	cpu_size = of_property_count_u32_elems(node, key);
+	if (cpu_size > 0) {
+		cpu_array = kmalloc_array(cpu_size, sizeof(u32), GFP_KERNEL);
+		if (!cpu_array) {
+			rc = -ENOMEM;
+			goto request_irq_fail;
+		}
+		rc = of_property_read_u32_array(node, key, cpu_array, cpu_size);
+		if (!rc)
+			glink_set_affinity(einfo, cpu_array, cpu_size);
+		kfree(cpu_array);
+	}
+
 	register_debugfs_info(einfo);
 	/* fake an interrupt on this edge to see if the remote side is up */
 	irq_handler(0, einfo);
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 209209b..e391cd1 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -73,6 +73,8 @@
 #define ICNSS_THRESHOLD_LOW		3450000
 #define ICNSS_THRESHOLD_GUARD		20000
 
+#define ICNSS_MAX_PROBE_CNT		2
+
 #define icnss_ipc_log_string(_x...) do {				\
 	if (icnss_ipc_log_context)					\
 		ipc_log_string(icnss_ipc_log_context, _x);		\
@@ -152,6 +154,8 @@
 #define ICNSS_QMI_ASSERT() do { } while (0)
 #endif
 
+#define QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED 0x77
+
 enum icnss_debug_quirks {
 	HW_ALWAYS_ON,
 	HW_DEBUG_ENABLE,
@@ -196,7 +200,6 @@
 enum icnss_msa_perm {
 	ICNSS_MSA_PERM_HLOS_ALL = 0,
 	ICNSS_MSA_PERM_WLAN_HW_RW = 1,
-	ICNSS_MSA_PERM_DUMP_COLLECT = 2,
 	ICNSS_MSA_PERM_MAX,
 };
 
@@ -229,13 +232,6 @@
 		.nelems = 2,
 	},
 
-	[ICNSS_MSA_PERM_DUMP_COLLECT] = {
-		.vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_HLOS},
-		.perms = {PERM_READ | PERM_WRITE,
-			PERM_READ | PERM_WRITE,
-			PERM_READ},
-		.nelems = 3,
-	},
 };
 
 struct icnss_msa_perm_list_t msa_perm_list[ICNSS_MSA_PERM_MAX] = {
@@ -253,14 +249,6 @@
 		.nelems = 3,
 	},
 
-	[ICNSS_MSA_PERM_DUMP_COLLECT] = {
-		.vmids = {VMID_MSS_MSA, VMID_WLAN, VMID_WLAN_CE, VMID_HLOS},
-		.perms = {PERM_READ | PERM_WRITE,
-			PERM_READ | PERM_WRITE,
-			PERM_READ | PERM_WRITE,
-			PERM_READ},
-		.nelems = 4,
-	},
 };
 
 struct icnss_event_pd_service_down_data {
@@ -395,12 +383,6 @@
 	uint32_t rejuvenate_ack_err;
 };
 
-#define MAX_NO_OF_MAC_ADDR 4
-struct icnss_wlan_mac_addr {
-	u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
-	uint32_t no_of_mac_addr_set;
-};
-
 enum icnss_pdr_cause_index {
 	ICNSS_FW_CRASH,
 	ICNSS_ROOT_PD_CRASH,
@@ -477,8 +459,6 @@
 	uint64_t vph_pwr;
 	atomic_t pm_count;
 	struct ramdump_device *msa0_dump_dev;
-	bool is_wlan_mac_set;
-	struct icnss_wlan_mac_addr wlan_mac_addr;
 	bool bypass_s1_smmu;
 	u8 cause_for_rejuvenation;
 	u8 requesting_sub_system;
@@ -740,7 +720,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI vbatt request rejected, result:%d error:%d\n",
 			resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	priv->stats.vbatt_resp++;
@@ -1224,7 +1204,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI MSA Mem info request rejected, result:%d error:%d\n",
 			resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 
@@ -1296,7 +1276,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI MSA ready request rejected: result:%d error:%d\n",
 			resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	penv->stats.msa_ready_resp++;
@@ -1359,7 +1339,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI indication register request rejected, resut:%d error:%d\n",
 		       resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	penv->stats.ind_register_resp++;
@@ -1406,7 +1386,9 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI capability request rejected, result:%d error:%d\n",
 		       resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
+		if (resp.resp.error == QMI_ERR_PLAT_CCPM_CLK_INIT_FAILED)
+			icnss_pr_err("RF card Not present");
 		goto out;
 	}
 
@@ -1489,7 +1471,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI mode request rejected, mode:%d result:%d error:%d\n",
 			     mode, resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	penv->stats.mode_resp++;
@@ -1539,7 +1521,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI config request rejected, result:%d error:%d\n",
 		       resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	penv->stats.cfg_resp++;
@@ -1592,7 +1574,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
 			     fw_log_mode, resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	penv->stats.ini_resp++;
@@ -1652,7 +1634,7 @@
 	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI athdiag read request rejected, result:%d error:%d\n",
 			     resp->resp.result, resp->resp.error);
-		ret = resp->resp.result;
+		ret = -resp->resp.result;
 		goto out;
 	}
 
@@ -1718,7 +1700,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI athdiag write request rejected, result:%d error:%d\n",
 			     resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 out:
@@ -1813,7 +1795,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
 			     resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 	priv->stats.rejuvenate_ack_resp++;
@@ -1874,7 +1856,7 @@
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
 		icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
 			     resp.resp.result, resp.resp.error);
-		ret = resp.resp.result;
+		ret = -resp.resp.result;
 		goto out;
 	}
 
@@ -2107,7 +2089,8 @@
 
 static int icnss_call_driver_probe(struct icnss_priv *priv)
 {
-	int ret;
+	int ret = 0;
+	int probe_cnt = 0;
 
 	if (!priv->ops || !priv->ops->probe)
 		return 0;
@@ -2119,10 +2102,15 @@
 
 	icnss_hw_power_on(priv);
 
-	ret = priv->ops->probe(&priv->pdev->dev);
+	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+		ret = priv->ops->probe(&priv->pdev->dev);
+		probe_cnt++;
+		if (ret != -EPROBE_DEFER)
+			break;
+	}
 	if (ret < 0) {
-		icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
-			     ret, priv->state);
+		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+			     ret, priv->state, probe_cnt);
 		goto out;
 	}
 
@@ -2230,6 +2218,7 @@
 static int icnss_driver_event_register_driver(void *data)
 {
 	int ret = 0;
+	int probe_cnt = 0;
 
 	if (penv->ops)
 		return -EEXIST;
@@ -2249,11 +2238,15 @@
 	if (ret)
 		goto out;
 
-	ret = penv->ops->probe(&penv->pdev->dev);
-
+	while (probe_cnt < ICNSS_MAX_PROBE_CNT) {
+		ret = penv->ops->probe(&penv->pdev->dev);
+		probe_cnt++;
+		if (ret != -EPROBE_DEFER)
+			break;
+	}
 	if (ret) {
-		icnss_pr_err("Driver probe failed: %d, state: 0x%lx\n",
-			     ret, penv->state);
+		icnss_pr_err("Driver probe failed: %d, state: 0x%lx, probe_cnt: %d\n",
+			     ret, penv->state, probe_cnt);
 		goto power_off;
 	}
 
@@ -2336,7 +2329,7 @@
 	if (!test_bit(ICNSS_WLFW_EXISTS, &priv->state))
 		goto out;
 
-	if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+	if (test_bit(ICNSS_PD_RESTART, &priv->state) && event_data->crashed) {
 		icnss_pr_err("PD Down while recovery inprogress, crashed: %d, state: 0x%lx\n",
 			     event_data->crashed, priv->state);
 		ICNSS_ASSERT(0);
@@ -2482,9 +2475,10 @@
 
 	icnss_pr_vdbg("Modem-Notify: event %lu\n", code);
 
-	if (code == SUBSYS_AFTER_SHUTDOWN) {
+	if (code == SUBSYS_AFTER_SHUTDOWN &&
+	    notif->crashed == CRASH_STATUS_ERR_FATAL) {
 		ret = icnss_assign_msa_perm_all(priv,
-						ICNSS_MSA_PERM_DUMP_COLLECT);
+						ICNSS_MSA_PERM_HLOS_ALL);
 		if (!ret) {
 			icnss_pr_info("Collecting msa0 segment dump\n");
 			icnss_msa0_ramdump(priv);
@@ -2500,8 +2494,17 @@
 	if (code != SUBSYS_BEFORE_SHUTDOWN)
 		return NOTIFY_OK;
 
-	if (test_bit(ICNSS_PDR_REGISTERED, &priv->state))
+	if (test_bit(ICNSS_PDR_REGISTERED, &priv->state)) {
+		set_bit(ICNSS_FW_DOWN, &priv->state);
+		icnss_ignore_qmi_timeout(true);
+
+		fw_down_data.crashed = !!notif->crashed;
+		if (test_bit(ICNSS_FW_READY, &priv->state))
+			icnss_call_driver_uevent(priv,
+						 ICNSS_UEVENT_FW_DOWN,
+						 &fw_down_data);
 		return NOTIFY_OK;
+	}
 
 	icnss_pr_info("Modem went down, state: 0x%lx, crashed: %d\n",
 		      priv->state, notif->crashed);
@@ -2635,12 +2638,18 @@
 	icnss_pr_info("PD service down, pd_state: %d, state: 0x%lx: cause: %s\n",
 		      *state, priv->state, icnss_pdr_cause[cause]);
 event_post:
-	set_bit(ICNSS_FW_DOWN, &priv->state);
-	icnss_ignore_qmi_timeout(true);
-	clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
+	if (!test_bit(ICNSS_FW_DOWN, &priv->state)) {
+		set_bit(ICNSS_FW_DOWN, &priv->state);
+		icnss_ignore_qmi_timeout(true);
 
-	fw_down_data.crashed = event_data->crashed;
-	icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN, &fw_down_data);
+		fw_down_data.crashed = event_data->crashed;
+		if (test_bit(ICNSS_FW_READY, &priv->state))
+			icnss_call_driver_uevent(priv,
+						 ICNSS_UEVENT_FW_DOWN,
+						 &fw_down_data);
+	}
+
+	clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
 	icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
 				ICNSS_EVENT_SYNC, event_data);
 done:
@@ -3275,78 +3284,6 @@
 }
 EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
 
-int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
-{
-	struct icnss_priv *priv = penv;
-	uint32_t no_of_mac_addr;
-	struct icnss_wlan_mac_addr *addr = NULL;
-	int iter;
-	u8 *temp = NULL;
-
-	if (!priv) {
-		icnss_pr_err("Priv data is NULL\n");
-		return -EINVAL;
-	}
-
-	if (priv->is_wlan_mac_set) {
-		icnss_pr_dbg("WLAN MAC address is already set\n");
-		return 0;
-	}
-
-	if (len == 0 || (len % ETH_ALEN) != 0) {
-		icnss_pr_err("Invalid length %d\n", len);
-		return -EINVAL;
-	}
-
-	no_of_mac_addr = len / ETH_ALEN;
-	if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
-		icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
-			     MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
-		return -EINVAL;
-	}
-
-	priv->is_wlan_mac_set = true;
-	addr = &priv->wlan_mac_addr;
-	addr->no_of_mac_addr_set = no_of_mac_addr;
-	temp = &addr->mac_addr[0][0];
-
-	for (iter = 0; iter < no_of_mac_addr;
-	     ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
-		ether_addr_copy(temp, in);
-		icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
-			     temp[0], temp[1], temp[2],
-			     temp[3], temp[4], temp[5]);
-	}
-
-	return 0;
-}
-EXPORT_SYMBOL(icnss_set_wlan_mac_address);
-
-u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
-{
-	struct icnss_priv *priv = dev_get_drvdata(dev);
-	struct icnss_wlan_mac_addr *addr = NULL;
-
-	if (priv->magic != ICNSS_MAGIC) {
-		icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
-			     dev, priv, priv->magic);
-		goto out;
-	}
-
-	if (!priv->is_wlan_mac_set) {
-		icnss_pr_dbg("WLAN MAC address is not set\n");
-		goto out;
-	}
-
-	addr = &priv->wlan_mac_addr;
-	*num = addr->no_of_mac_addr_set;
-	return &addr->mac_addr[0][0];
-out:
-	*num = 0;
-	return NULL;
-}
-EXPORT_SYMBOL(icnss_get_wlan_mac_address);
-
 int icnss_trigger_recovery(struct device *dev)
 {
 	int ret = 0;
diff --git a/drivers/soc/qcom/icnss_utils.c b/drivers/soc/qcom/icnss_utils.c
deleted file mode 100644
index 6974146..0000000
--- a/drivers/soc/qcom/icnss_utils.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * 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/slab.h>
-#include <soc/qcom/icnss.h>
-
-#define ICNSS_MAX_CH_NUM 45
-
-static DEFINE_MUTEX(unsafe_channel_list_lock);
-static DEFINE_SPINLOCK(dfs_nol_info_lock);
-static int driver_load_cnt;
-
-static struct icnss_unsafe_channel_list {
-	u16 unsafe_ch_count;
-	u16 unsafe_ch_list[ICNSS_MAX_CH_NUM];
-} unsafe_channel_list;
-
-static struct icnss_dfs_nol_info {
-	void *dfs_nol_info;
-	u16 dfs_nol_info_len;
-} dfs_nol_info;
-
-int icnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
-{
-	mutex_lock(&unsafe_channel_list_lock);
-	if ((!unsafe_ch_list) || (ch_count > ICNSS_MAX_CH_NUM)) {
-		mutex_unlock(&unsafe_channel_list_lock);
-		return -EINVAL;
-	}
-
-	unsafe_channel_list.unsafe_ch_count = ch_count;
-
-	if (ch_count != 0) {
-		memcpy(
-		       (char *)unsafe_channel_list.unsafe_ch_list,
-		       (char *)unsafe_ch_list, ch_count * sizeof(u16));
-	}
-	mutex_unlock(&unsafe_channel_list_lock);
-
-	return 0;
-}
-EXPORT_SYMBOL(icnss_set_wlan_unsafe_channel);
-
-int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list,
-				  u16 *ch_count, u16 buf_len)
-{
-	mutex_lock(&unsafe_channel_list_lock);
-	if (!unsafe_ch_list || !ch_count) {
-		mutex_unlock(&unsafe_channel_list_lock);
-		return -EINVAL;
-	}
-
-	if (buf_len < (unsafe_channel_list.unsafe_ch_count * sizeof(u16))) {
-		mutex_unlock(&unsafe_channel_list_lock);
-		return -ENOMEM;
-	}
-
-	*ch_count = unsafe_channel_list.unsafe_ch_count;
-	memcpy(
-		(char *)unsafe_ch_list,
-		(char *)unsafe_channel_list.unsafe_ch_list,
-		unsafe_channel_list.unsafe_ch_count * sizeof(u16));
-	mutex_unlock(&unsafe_channel_list_lock);
-
-	return 0;
-}
-EXPORT_SYMBOL(icnss_get_wlan_unsafe_channel);
-
-int icnss_wlan_set_dfs_nol(const void *info, u16 info_len)
-{
-	void *temp;
-	void *old_nol_info;
-	struct icnss_dfs_nol_info *dfs_info;
-
-	if (!info || !info_len)
-		return -EINVAL;
-
-	temp = kmalloc(info_len, GFP_ATOMIC);
-	if (!temp)
-		return -ENOMEM;
-
-	memcpy(temp, info, info_len);
-	spin_lock_bh(&dfs_nol_info_lock);
-	dfs_info = &dfs_nol_info;
-	old_nol_info = dfs_info->dfs_nol_info;
-	dfs_info->dfs_nol_info = temp;
-	dfs_info->dfs_nol_info_len = info_len;
-	spin_unlock_bh(&dfs_nol_info_lock);
-	kfree(old_nol_info);
-
-	return 0;
-}
-EXPORT_SYMBOL(icnss_wlan_set_dfs_nol);
-
-int icnss_wlan_get_dfs_nol(void *info, u16 info_len)
-{
-	int len;
-	struct icnss_dfs_nol_info *dfs_info;
-
-	if (!info || !info_len)
-		return -EINVAL;
-
-	spin_lock_bh(&dfs_nol_info_lock);
-
-	dfs_info = &dfs_nol_info;
-	if (dfs_info->dfs_nol_info == NULL ||
-	    dfs_info->dfs_nol_info_len == 0) {
-		spin_unlock_bh(&dfs_nol_info_lock);
-		return -ENOENT;
-	}
-
-	len = min(info_len, dfs_info->dfs_nol_info_len);
-	memcpy(info, dfs_info->dfs_nol_info, len);
-	spin_unlock_bh(&dfs_nol_info_lock);
-
-	return len;
-}
-EXPORT_SYMBOL(icnss_wlan_get_dfs_nol);
-
-void icnss_increment_driver_load_cnt(void)
-{
-	++driver_load_cnt;
-}
-EXPORT_SYMBOL(icnss_increment_driver_load_cnt);
-
-int icnss_get_driver_load_cnt(void)
-{
-	return driver_load_cnt;
-}
-EXPORT_SYMBOL(icnss_get_driver_load_cnt);
diff --git a/drivers/soc/qcom/jtagv8-etm.c b/drivers/soc/qcom/jtagv8-etm.c
index ff8cc99..3f4b8bc 100644
--- a/drivers/soc/qcom/jtagv8-etm.c
+++ b/drivers/soc/qcom/jtagv8-etm.c
@@ -183,24 +183,28 @@
 #define HW_SOC_ID_M8953		(293)
 
 #define etm_writel(etm, val, off)	\
+		   writel_relaxed_no_log(val, etm->base + off)
+#define etm_writel_log(etm, val, off)	\
 		   __raw_writel(val, etm->base + off)
+
 #define etm_readl(etm, off)		\
-		  __raw_readl(etm->base + off)
+		   readl_relaxed_no_log(etm->base + off)
 
 #define etm_writeq(etm, val, off)	\
-		   __raw_writeq(val, etm->base + off)
+		   writeq_relaxed_no_log(val, etm->base + off)
+
 #define etm_readq(etm, off)		\
-		  __raw_readq(etm->base + off)
+		   readq_relaxed_no_log(etm->base + off)
 
 #define ETM_LOCK(base)							\
 do {									\
 	mb(); /* ensure configuration take effect before we lock it */	\
-	etm_writel(base, 0x0, CORESIGHT_LAR);				\
+	etm_writel_log(base, 0x0, CORESIGHT_LAR);			\
 } while (0)
 
 #define ETM_UNLOCK(base)						\
 do {									\
-	etm_writel(base, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
+	etm_writel_log(base, CORESIGHT_UNLOCK, CORESIGHT_LAR);		\
 	mb(); /* ensure unlock take effect before we configure */	\
 } while (0)
 
diff --git a/drivers/soc/qcom/llcc-core.c b/drivers/soc/qcom/llcc-core.c
index 3d6b002..a5e5b56 100644
--- a/drivers/soc/qcom/llcc-core.c
+++ b/drivers/soc/qcom/llcc-core.c
@@ -35,6 +35,15 @@
 #define DRP0_INTERRUPT_ENABLE	BIT(6)
 #define SB_DB_DRP_INTERRUPT_ENABLE	0x3
 
+#ifdef CONFIG_EDAC_QCOM_LLCC
+#define ENABLE_ECC_INTR 1
+#else
+#define ENABLE_ECC_INTR 0
+#endif
+
+static int enable_ecc_intr = ENABLE_ECC_INTR;
+module_param(enable_ecc_intr, int, 0444);
+
 static void qcom_llcc_core_setup(struct regmap *llcc_regmap, uint32_t b_off)
 {
 	u32 sb_err_threshold;
@@ -81,7 +90,8 @@
 		return -EINVAL;
 	}
 
-	qcom_llcc_core_setup(llcc_regmap, b_off);
+	if (enable_ecc_intr)
+		qcom_llcc_core_setup(llcc_regmap, b_off);
 
 	return 0;
 }
diff --git a/drivers/soc/qcom/llcc-sdm670.c b/drivers/soc/qcom/llcc-sdm670.c
index 68ad755..aaed9ee 100644
--- a/drivers/soc/qcom/llcc-sdm670.c
+++ b/drivers/soc/qcom/llcc-sdm670.c
@@ -57,13 +57,14 @@
 	}
 
 static struct llcc_slice_config sdm670_data[] =  {
-	SCT_ENTRY("cpuss", 1, 1, 512, 1, 1, 0xF,  0x0, 0, 0, 0, 1, 1),
-	SCT_ENTRY("vidsc0", 2, 2, 64,  2, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("vidsc1", 3, 3, 64,  2, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("rotator", 4, 4, 384, 2, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("modem",  8, 8, 512, 1, 0, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("gpuhtw", 11, 11, 128, 1, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
-	SCT_ENTRY("gpu",    12, 12, 384, 1, 1, 0xF,  0x0, 0, 0, 0, 1, 0),
+	SCT_ENTRY("cpuss", 1, 1, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 1),
+	SCT_ENTRY("rotator", 4, 4, 384, 2, 1, 0x0, 0xE, 2, 0, 1, 1, 0),
+	SCT_ENTRY("voice", 5, 5, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("audio", 6, 6, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("modem",  8, 8, 512, 1, 0, 0xF,  0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("gpu", 12, 12, 384, 1, 1, 0x0, 0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("mmuhwt", 13, 13, 512, 1, 0, 0xF, 0x0, 0, 0, 1, 0, 1),
+	SCT_ENTRY("audiohw", 22, 22, 512, 1, 1, 0xF, 0x0, 0, 0, 1, 1, 0),
 };
 
 static int sdm670_qcom_llcc_probe(struct platform_device *pdev)
diff --git a/drivers/soc/qcom/llcc-sdm845.c b/drivers/soc/qcom/llcc-sdm845.c
index 739c053..5234580 100644
--- a/drivers/soc/qcom/llcc-sdm845.c
+++ b/drivers/soc/qcom/llcc-sdm845.c
@@ -57,23 +57,24 @@
 	}
 
 static struct llcc_slice_config sdm845_data[] =  {
-	SCT_ENTRY("cpuss",       1, 1, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 1),
+	SCT_ENTRY("cpuss",       1, 1, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 1),
 	SCT_ENTRY("vidsc0",      2, 2, 512, 2, 1, 0x0,  0x0F0, 0, 0, 1, 1, 0),
 	SCT_ENTRY("vidsc1",      3, 3, 512, 2, 1, 0x0,  0x0F0, 0, 0, 1, 1, 0),
-	SCT_ENTRY("rotator",     4, 4, 563, 2, 1, 0x0,  0x00F, 2, 0, 1, 1, 0),
-	SCT_ENTRY("voice",       5, 5, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("audio",       6, 6, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("modemhp_grow", 7, 7, 1024, 2, 0, 0x0F0, 0xF0F, 0, 0, 1, 1, 0),
-	SCT_ENTRY("modem",       8, 8, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("compute",     10, 10, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("gpuhtw",      11, 11, 512, 1, 1, 0x0,  0xC, 0, 0, 1, 1, 0),
-	SCT_ENTRY("gpu",         12, 12, 2560, 1, 0, 0xFF0, 0x3, 0, 0, 1, 1, 0),
-	SCT_ENTRY("mmuhwt",      13, 13, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 0, 1),
-	SCT_ENTRY("compute_dma", 15, 15, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("display",     16, 16, 3072, 1, 0, 0xFF0, 0xF, 0, 0, 1, 1, 0),
+	SCT_ENTRY("rotator",     4, 4, 563, 2, 1, 0x0,  0x00e, 2, 0, 1, 1, 0),
+	SCT_ENTRY("voice",       5, 5, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("audio",       6, 6, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("modemhp_grow", 7, 7, 1024, 2, 0, 0x0FC, 0xF00, 0, 0, 1, 1, 0),
+	SCT_ENTRY("modem",       8, 8, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("compute",     10, 10, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("gpuhtw",      11, 11, 512, 1, 1, 0xC,  0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("gpu",         12, 12, 2304, 1, 0, 0xFF0, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("mmuhwt",      13, 13, 256, 2, 0, 0x0,  0x1, 0, 0, 1, 0, 1),
+	SCT_ENTRY("compute_dma", 15, 15, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("display",     16, 16, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
+	SCT_ENTRY("videofw",     17, 17, 2816, 1, 0, 0xFFC, 0x2, 0, 0, 1, 1, 0),
 	SCT_ENTRY("modemhp_fix", 20, 20, 1024, 2, 1, 0x0,  0xF00, 0, 0, 1, 1, 0),
-	SCT_ENTRY("modem_paging", 21, 21, 1024, 0, 1, 0x0,  0xF, 0, 0, 1, 1, 0),
-	SCT_ENTRY("audiohw",     22, 22, 1024, 1, 1, 0xFF0, 0xF, 0, 0, 1, 1, 0),
+	SCT_ENTRY("modem_paging", 21, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0),
+	SCT_ENTRY("audiohw",     22, 22, 1024, 1, 1, 0xFFC, 0x2, 0, 0, 1, 1, 0),
 };
 
 static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
diff --git a/drivers/soc/qcom/llcc_events.h b/drivers/soc/qcom/llcc_events.h
new file mode 100644
index 0000000..28f4594
--- /dev/null
+++ b/drivers/soc/qcom/llcc_events.h
@@ -0,0 +1,301 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SOC_QCOM_LLCC_EVENTS_H_
+#define _SOC_QCOM_LLCC_EVENTS_H_
+
+enum event_port_select {
+	EVENT_PORT_FEAC,
+	EVENT_PORT_FERC,
+	EVENT_PORT_FEWC,
+	EVENT_PORT_BEAC,
+	EVENT_PORT_BERC,
+	EVENT_PORT_TRP,
+	EVENT_PORT_DRP,
+	EVENT_PORT_PMGR,
+	EVENT_PORT_TENURE,
+	EVENT_PORT_TLAT,
+};
+
+enum feac_events {
+	FEAC_ANY_ACCESS,
+	FEAC_READ_INCR,
+	FEAC_WRITE_INCR,
+	FEAC_WRITE_ORDERED,
+	FEAC_READE_EXCL,
+	FEAC_WRITE_EXCL,
+	FEAC_CMO,
+	FEAC_CMO_CLEAN,
+	FEAC_CMO_INVAL,
+	FEAC_CMO_CLEANINVAL,
+	FEAC_CMO_DCPLD,
+	FEAC_READ_NOALLOC,
+	FEAC_WRITE_NOALLOC,
+	FEAC_PREFETCH,
+	FEAC_RD_BYTES,
+	FEAC_RD_BEATS,
+	FEAC_WR_BYTES,
+	FEAC_WR_BEATS,
+	FEAC_FC_READ,
+	FEAC_EWD_ACCESS,
+	FEAC_TCM_ACCESS,
+	FEAC_GM_HIT,
+	FEAC_GM_MISS,
+	FEAC_GM_UNAVAILABLE,
+	FEAC_XPU_ERROR,
+	FEAC_READ_HAZARD,
+	FEAC_WRITE_HAZARD,
+	FEAC_GRANULE_READ,
+	FEAC_GRANULE_WRITE,
+	FEAC_RIFB_ALLOC,
+	FEAC_WIFB_ALLOC,
+	FEAC_RIFB_DEALLOC,
+	FEAC_WIFB_DEALLOC,
+	FEAC_RESERVED,
+	FEAC_RESERVED1,
+	FEAC_FEAC2TRP_LP_TX,
+	FEAC_TRP_LP_BUSY,
+	FEAC_FEAC2TRP_HP_TX,
+	FEAC_TRP_HP_BUSY,
+	FEAC_FEAC2FEWC_TX,
+	FEAC_BEAC_LP_BUSY,
+	FEAC_BEAC_HP_BUSY,
+	FEAC_RIFB_FULL,
+	FEAC_WIFB_FULL,
+	FEAC_RD_CRDT_TX,
+	FEAC_WR_CRDT_TX,
+	FEAC_PROMOTION,
+	FEAC_FEAC2TRP_LP_PRESSURE,
+	FEAC_FEAC2TRP_HP_PRESSURE,
+	FEAC_FEAC2FEWC_PRESSURE,
+	FEAC_FEAC2BEAC_LP_PRESSURE,
+	FEAC_FEAC2BEAC_HP_PRESSURE,
+	FEAC_WR_THROUGH,
+};
+
+enum ferc_events {
+	FERC_BERC_CMD,
+	FERC_BERC_BEAT,
+	FERC_DRP_CMD,
+	FERC_DRP_BEAT,
+	FERC_RD_CTRL_RSP_TX,
+	FERC_WR_CTRL_RSP_TX,
+	FERC_RD_DATA_TX,
+	FERC_MISS_TRUMPS_HIT,
+	FERC_HIT_TRUMPS_WRSP,
+	FERC_RD_INTRA_RSP_IDLE,
+};
+
+enum fewc_events {
+	FEWC_WR_CMD,
+	FEWC_WR_DATA_BEAT,
+	FEWC_WR_LAST,
+	FEWC_WBUF_DEALLOC,
+	FEWC_WR_HIT,
+	FEWC_WR_MISS,
+	FEWC_NC_RMW,
+	FEWC_WR_DOWNGRADE,
+	FEWC_BEAC_WR_CMD,
+	FEWC_BEAC_WR_BEAT,
+	FEWC_BEAC_RD_CMD,
+	FEWC_BERC_FILL_BEAT,
+	FEWC_DRP_WR_CMD,
+	FEWC_DRP_WR_BEAT,
+	FEWC_DRP_RD_BEAT,
+	FEWC_TRP_TAG_LOOKUP,
+	FEWC_TRP_TAG_UPDATE,
+	FEWC_TRP_UNSTALL,
+	FEWC_WBUFFS_FULL,
+	FEWC_DRP_BUSY,
+	FEWC_BEAC_WR_BUSY,
+	FEWC_BEAC_RD_BUSY,
+	FEWC_TRP_TAG_LOOKUP_BUSY,
+	FEWC_TRP_TAG_UPDATE_BUSY,
+	FEWC_C_RMW,
+	FEWC_NC_ALLOC_RMW,
+	FEWC_NC_NO_ALLOC_RMW,
+	FEWC_NC_RMW_DEALLOC,
+	FEWC_C_RMW_DEALLOC,
+	FEWC_STALLED_BY_EVICT,
+};
+
+enum beac_events {
+	BEAC_RD_TX,
+	BEAC_WR_TX,
+	BEAC_RD_GRANULE,
+	BEAC_WR_GRANULE,
+	BEAC_WR_BEAT_TX,
+	BEAC_RD_CRDT_ZERO,
+	BEAC_WR_CRDT_ZERO,
+	BEAC_WDATA_CRDT_ZERO,
+	BEAC_IFCMD_CRDT_ZERO,
+	BEAC_IFWDATA_CRDT_ZERO,
+	BEAC_PCT_ENTRY_ALLOC,
+	BEAC_PCT_ENTRY_FREE,
+	BEAC_PCT_FULL,
+	BEAC_RD_PROMOTION_TX,
+	BEAC_WR_PROMOTION_TX,
+	BEAC_RD_PRESSURE_TX,
+	BEAC_WR_PRESSURE_TX,
+};
+
+enum berc_events {
+	BERC_RD_CMD,
+	BERC_ERROR_CMD,
+	BERC_PCT_ENTRY_DEALLOC,
+	BERC_RD_RSP_RX,
+	BERC_RD_RSP_BEAT_RX,
+	BERC_RD_LA_RX,
+	BERC_UNSTALL_RX,
+	BERC_TX_RD_CMD,
+	BERC_TX_ERR_CMD,
+	BERC_TX_RD_BEAT,
+	BERC_TX_ERR_BEAT,
+	BERC_RESERVED,
+	BERC_RESERVED1,
+	BERC_CMO_RX,
+	BERC_CMO_TX,
+	BERC_DRP_WR_TX,
+	BERC_DRP_WR_BEAT_TX,
+	BERC_FEWC_WR_TX,
+	BERC_FEWC_WR_BEAT_TX,
+	BERC_LBUFFS_FULL,
+	BERC_DRP_BUSY,
+	BERC_FEWC_BUSY,
+	BERC_LBUFF_STALLED,
+};
+
+enum trp_events {
+	TRP_ANY_ACCESS,
+	TRP_INCR_RD,
+	TRP_INCR_WR,
+	TRP_ANY_HIT,
+	TRP_RD_HIT,
+	TRP_WR_HIT,
+	TRP_RD_MISS,
+	TRP_WR_MISS,
+	TRP_RD_HIT_MISS,
+	TRP_WR_HIT_MISS,
+	TRP_EVICT,
+	TRP_GRANULE_EVICT,
+	TRP_RD_EVICT,
+	TRP_WR_EVICT,
+	TRP_LINE_FILL,
+	TRP_GRANULE_FILL,
+	TRP_WSC_WRITE,
+	TRP_WSC_EVICT,
+	TRP_SUBCACHE_ACT,
+	TRP_SUBCACHE_DEACT,
+	TRP_RD_DEACTIVE_SUBCACHE,
+	TRP_WR_DEACTIVE_SUBCACHE,
+	TRP_INVALID_LINE_ALLOC,
+	TRP_DEACTIVE_LINE_ALLOC,
+	TRP_SELF_EVICTION_ALLOC,
+	TRP_UC_SUBCACHE_ALLOC,
+	TRP_FC_SELF_EVICTION_ALLOC,
+	TRP_LP_SUBCACHE_VICTIM,
+	TRP_OC_SUBCACHE_VICTIM,
+	TRP_MRU_ROLLOVER,
+	TRP_NC_DOWNGRADE,
+	TRP_TAGRAM_CORR_ERR,
+	TRP_TAGRAM_UNCORR_ERR,
+	TRP_RD_MISS_FC,
+	TRP_CPU_WRITE_EWD_LINE,
+	TRP_CLIENT_WRITE_EWD_LINE,
+	TRP_CLIENT_READ_EWD_LINE,
+	TRP_CMO_I_EWD_LINE,
+	TRP_CMO_I_DIRTY_LINE,
+	TRP_DRP_RD_NOTIFICATION,
+	TRP_DRP_WR_NOTIFICATION,
+	TRP_LINEFILL_TAG_UPDATE,
+	TRP_FEWC_TAG_UPDATE,
+	TRP_ET_FULL,
+	TRP_NAWT_FULL,
+	TRP_HITQ_FULL,
+	TRP_ET_ALLOC,
+	TRP_ET_DEALLOC,
+	TRP_NAWT_ALLOC,
+	TRP_NAWT_DEALLOC,
+	TRP_RD_REPLAY,
+	TRP_WR_ECC_RD,
+	TRP_ET_LP_FULL,
+	TRP_ET_HP_FULL,
+	TRP_SOEH,
+};
+
+enum drp_events {
+	DRP_TRP_RD_NOTIFICATION,
+	DRP_TRP_WR_NOTIFICATION,
+	DRP_BIST_WR_NOTIFICATION,
+	DRP_DRIE_WR_NOTIFICATION,
+	DRP_ECC_CORR_ERR,
+	DRP_ECC_UNCORR_ERR,
+	DRP_FERC_RD_TX,
+	DRP_FEWC_RD_TX,
+	DRP_EVICT_LINE_TX,
+	DRP_EVICT_GRANULE_TX,
+	DRP_BIST_TX,
+	DRP_FERC_RD_BEAT,
+	DRP_FEWC_RD_BEAT,
+	DRP_BIST_RD_BEAT,
+	DRP_EVICT_RD_BEAT,
+	DRP_BERC_WR_BEAT,
+	DRP_FEWC_WR_BEAT,
+	DRP_BIST_WR_BEAT,
+	DRP_DRIE_WR_BEAT,
+	DRP_BERC_UNSTALL,
+	DRP_FEWC_UNSTALL,
+	DRP_LB_RD,
+	DRP_LB_WR,
+	DRP_BANK_CONFLICT,
+	DRP_FILL_TRUMPS_RD,
+	DRP_RD_TRUMPS_WR,
+	DRP_LB_SLP_RET,
+	DRP_LB_SLP_NRET,
+	DRP_LB_WAKEUP,
+	DRP_TRP_EARLY_WAKEUP,
+	DRP_PCB_IDLE,
+	DRP_EVICT_RDFIFO_FULL,
+	DRP_FEWC_RDFIFO_FULL,
+	DRP_FERC_RDFIFO_FULL,
+	DRP_FERC_RD,
+	DRP_FEWC_RD,
+	DRP_LINE_EVICT,
+	DRP_GRANULE_EVICT,
+	DRP_BIST_RD,
+	DRP_FEWC_WR,
+	DRP_LINE_FILL,
+	DRP_GRANULE_FILL,
+	DRP_BIST_WR,
+	DRP_DRIE_WR,
+};
+
+enum pmgr_events {
+	PMGR_Q_RUN_STATE,
+	PMGR_Q_DENIED_STATE,
+	PMGR_Q_STOPEED_TO_Q_RUN,
+	PMGR_Q_RUN_TO_Q_FENCED,
+	PMGR_Q_RUN_TO_Q_DENIED,
+	PMGR_Q_DENIED_TO_Q_RUN,
+	PMGR_Q_FENCED_TO_Q_STOPPED,
+	PMGR_Q_FENCED_TO_Q_DENIED,
+};
+
+enum filter_type {
+	SCID,
+	MID,
+	PROFILING_TAG,
+	WAY_ID,
+	UNKNOWN,
+};
+
+#endif /* _SOC_QCOM_LLCC_EVENTS_H_ */
diff --git a/drivers/soc/qcom/llcc_perfmon.c b/drivers/soc/qcom/llcc_perfmon.c
new file mode 100644
index 0000000..8c86e7d
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.c
@@ -0,0 +1,1197 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/hrtimer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/soc/qcom/llcc-qcom.h>
+#include <linux/module.h>
+#include "llcc_events.h"
+#include "llcc_perfmon.h"
+
+#define LLCC_PERFMON_NAME		"llcc_perfmon"
+#define LLCC_PERFMON_COUNTER_MAX	16
+#define MAX_NUMBER_OF_PORTS		8
+#define NUM_CHANNELS			16
+#define MAX_STRING_SIZE			20
+#define DELIM_CHAR			" "
+
+/**
+ * struct llcc_perfmon_counter_map	- llcc perfmon counter map info
+ * @port_sel:		Port selected for configured counter
+ * @event_sel:		Event selected for configured counter
+ * @counter_dump:	Cumulative counter dump
+ */
+struct llcc_perfmon_counter_map {
+	unsigned int port_sel;
+	unsigned int event_sel;
+	unsigned long long counter_dump;
+};
+
+struct llcc_perfmon_private;
+/**
+ * struct event_port_ops		- event port operation
+ * @event_config:		Counter config support for port &  event
+ * @event_enable:		Counter enable support for port
+ * @event_filter_config:	Port filter config support
+ */
+struct event_port_ops {
+	void (*event_config)(struct llcc_perfmon_private *,
+			unsigned int, unsigned int, bool);
+	void (*event_enable)(struct llcc_perfmon_private *, bool);
+	void (*event_filter_config)(struct llcc_perfmon_private *,
+			enum filter_type, unsigned long, bool);
+};
+
+/**
+ * struct llcc_perfmon_private	- llcc perfmon private
+ * @llcc_map:		llcc register address space map
+ * @broadcast_off:	Offset of llcc broadcast address space
+ * @bank_off:		Offset of llcc banks
+ * @num_banks:		Number of banks supported
+ * @port_ops:		struct event_port_ops
+ * @configured:		Mapping of configured event counters
+ * @configured_counters:
+ *			Count of configured counters.
+ * @enables_port:	Port enabled for perfmon configuration
+ * @filtered_ports:	Port filter enabled
+ * @port_configd:	Number of perfmon port configuration supported
+ * @mutex:		mutex to protect this structure
+ * @hrtimer:		hrtimer instance for timer functionality
+ * @expires:		timer expire time in nano seconds
+ */
+struct llcc_perfmon_private {
+	struct regmap *llcc_map;
+	unsigned int broadcast_off;
+	unsigned int bank_off[NUM_CHANNELS];
+	unsigned int num_banks;
+	struct event_port_ops *port_ops[MAX_NUMBER_OF_PORTS];
+	struct llcc_perfmon_counter_map configured[LLCC_PERFMON_COUNTER_MAX];
+	unsigned int configured_counters;
+	unsigned int enables_port;
+	unsigned int filtered_ports;
+	unsigned int port_configd;
+	struct mutex mutex;
+	struct hrtimer hrtimer;
+	ktime_t expires;
+};
+
+static inline void llcc_bcast_write(struct llcc_perfmon_private *llcc_priv,
+			unsigned int offset, uint32_t val)
+{
+	regmap_write(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+			val);
+}
+
+static inline void llcc_bcast_read(struct llcc_perfmon_private *llcc_priv,
+		unsigned int offset, uint32_t *val)
+{
+	regmap_read(llcc_priv->llcc_map, llcc_priv->broadcast_off + offset,
+			val);
+}
+
+static void llcc_bcast_modify(struct llcc_perfmon_private *llcc_priv,
+		unsigned int offset, uint32_t val, uint32_t mask)
+{
+	uint32_t readval;
+
+	llcc_bcast_read(llcc_priv, offset, &readval);
+	readval &= ~mask;
+	readval |= val & mask;
+	llcc_bcast_write(llcc_priv, offset, readval);
+}
+
+static void perfmon_counter_dump(struct llcc_perfmon_private *llcc_priv)
+{
+	uint32_t val;
+	unsigned int i, j;
+	unsigned long long total;
+
+	if (!llcc_priv->configured_counters)
+		return;
+
+	llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+	for (i = 0; i < llcc_priv->configured_counters; i++) {
+		total = 0;
+		for (j = 0; j < llcc_priv->num_banks; j++) {
+			regmap_read(llcc_priv->llcc_map, llcc_priv->bank_off[j]
+					+ LLCC_COUNTER_n_VALUE(i), &val);
+			total += val;
+		}
+
+		llcc_priv->configured[i].counter_dump += total;
+	}
+}
+
+static ssize_t perfmon_counter_dump_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val;
+	unsigned int i, j;
+	unsigned long long total;
+	ssize_t cnt = 0, print;
+
+	if (llcc_priv->configured_counters == 0) {
+		pr_err("counters not configured\n");
+		return cnt;
+	}
+
+	if (llcc_priv->expires.tv64) {
+		perfmon_counter_dump(llcc_priv);
+		for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+			print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+					llcc_priv->configured[i].port_sel);
+			buf += print;
+			cnt += print;
+			print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+					llcc_priv->configured[i].event_sel);
+			buf += print;
+			cnt += print;
+
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+				       llcc_priv->configured[i].counter_dump);
+			buf += print;
+			cnt += print;
+			llcc_priv->configured[i].counter_dump = 0;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+		buf += print;
+		cnt += print;
+		print = snprintf(buf, MAX_STRING_SIZE, "0x%016llx\n",
+			llcc_priv->configured[i].counter_dump);
+		buf += print;
+		cnt += print;
+		llcc_priv->configured[i].counter_dump = 0;
+		hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	} else {
+		llcc_bcast_write(llcc_priv, PERFMON_DUMP, MONITOR_DUMP);
+
+		for (i = 0; i < llcc_priv->configured_counters - 1; i++) {
+			print = snprintf(buf, MAX_STRING_SIZE, "Port %02d,",
+					llcc_priv->configured[i].port_sel);
+			buf += print;
+			cnt += print;
+			print = snprintf(buf, MAX_STRING_SIZE, "Event %02d,",
+					llcc_priv->configured[i].event_sel);
+			buf += print;
+			cnt += print;
+			total = 0;
+			for (j = 0; j < llcc_priv->num_banks; j++) {
+				regmap_read(llcc_priv->llcc_map,
+						llcc_priv->bank_off[j]
+						+ LLCC_COUNTER_n_VALUE(i),
+						&val);
+				print = snprintf(buf, MAX_STRING_SIZE,
+						"0x%08x,", val);
+				buf += print;
+				cnt += print;
+				total += val;
+			}
+
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n",
+					total);
+			buf += print;
+			cnt += print;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "CYCLE COUNT, ,");
+		buf += print;
+		cnt += print;
+		total = 0;
+		for (j = 0; j < llcc_priv->num_banks; j++) {
+			regmap_read(llcc_priv->llcc_map,
+					llcc_priv->bank_off[j] +
+					LLCC_COUNTER_n_VALUE(i), &val);
+			print = snprintf(buf, MAX_STRING_SIZE, "0x%08x,", val);
+			buf += print;
+			cnt += print;
+			total += val;
+		}
+
+		print = snprintf(buf, MAX_STRING_SIZE, "0x%09llx\n", total);
+		buf += print;
+		cnt += print;
+	}
+
+	return cnt;
+}
+
+static ssize_t perfmon_configure_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned int j;
+	unsigned long port_sel, event_sel;
+	uint32_t val;
+	char *token, *delim = DELIM_CHAR;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (llcc_priv->configured_counters) {
+		pr_err("Counters configured already, remove & try again\n");
+		mutex_unlock(&llcc_priv->mutex);
+		return -EINVAL;
+	}
+
+	llcc_priv->configured_counters = 0;
+	j = 0;
+	token = strsep((char **)&buf, delim);
+
+	while (token != NULL) {
+		if (kstrtoul(token, 10, &port_sel))
+			break;
+
+		if (port_sel >= llcc_priv->port_configd)
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &event_sel))
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (event_sel >= EVENT_NUM_MAX) {
+			pr_err("unsupported event num %ld\n", event_sel);
+			continue;
+		}
+
+		llcc_priv->configured[j].port_sel = port_sel;
+		llcc_priv->configured[j].event_sel = event_sel;
+		port_ops = llcc_priv->port_ops[port_sel];
+		pr_info("counter %d configured for event %ld from port %ld\n",
+				j, event_sel, port_sel);
+		port_ops->event_config(llcc_priv, event_sel, j++, true);
+		if (!(llcc_priv->enables_port & (1 << port_sel)))
+			if (port_ops->event_enable)
+				port_ops->event_enable(llcc_priv, true);
+
+		llcc_priv->enables_port |= (1 << port_sel);
+
+		/* Last perfmon counter for cycle counter */
+		if (llcc_priv->configured_counters++ ==
+				(LLCC_PERFMON_COUNTER_MAX - 2))
+			break;
+	}
+
+	/* configure clock event */
+	val = COUNT_CLOCK_EVENT | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), val);
+
+	llcc_priv->configured_counters++;
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_remove_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned int j, counter_remove = 0;
+	unsigned long port_sel, event_sel;
+	char *token, *delim = DELIM_CHAR;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (!llcc_priv->configured_counters) {
+		pr_err("Counters not configured\n");
+		mutex_unlock(&llcc_priv->mutex);
+		return -EINVAL;
+	}
+
+	j = 0;
+	token = strsep((char **)&buf, delim);
+
+	while (token != NULL) {
+		if (kstrtoul(token, 10, &port_sel))
+			break;
+
+		if (port_sel >= llcc_priv->port_configd)
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &event_sel))
+			break;
+
+		token = strsep((char **)&buf, delim);
+		if (event_sel >= EVENT_NUM_MAX) {
+			pr_err("unsupported event num %ld\n", event_sel);
+			continue;
+		}
+
+		/* put dummy values */
+		llcc_priv->configured[j].port_sel = MAX_NUMBER_OF_PORTS;
+		llcc_priv->configured[j].event_sel = 100;
+		port_ops = llcc_priv->port_ops[port_sel];
+		pr_info("removed counter %d for event %ld from port %ld\n",
+				j, event_sel, port_sel);
+
+		port_ops->event_config(llcc_priv, event_sel, j++, false);
+		if (llcc_priv->enables_port & (1 << port_sel))
+			if (port_ops->event_enable)
+				port_ops->event_enable(llcc_priv, false);
+
+		llcc_priv->enables_port &= ~(1 << port_sel);
+
+		/* Last perfmon counter for cycle counter */
+		if (counter_remove++ == (LLCC_PERFMON_COUNTER_MAX - 2))
+			break;
+	}
+
+	/* remove clock event */
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(j), 0);
+
+	llcc_priv->configured_counters = 0;
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static enum filter_type find_filter_type(char *filter)
+{
+	enum filter_type ret = UNKNOWN;
+
+	if (!strcmp(filter, "SCID"))
+		ret = SCID;
+	else if (!strcmp(filter, "MID"))
+		ret = MID;
+	else if (!strcmp(filter, "PROFILING_TAG"))
+		ret = PROFILING_TAG;
+	else if (!strcmp(filter, "WAY_ID"))
+		ret = WAY_ID;
+
+	return ret;
+}
+
+static ssize_t perfmon_filter_config_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	unsigned long port, val;
+	struct event_port_ops *port_ops;
+	char *token, *delim = DELIM_CHAR;
+	enum filter_type filter = UNKNOWN;
+
+	mutex_lock(&llcc_priv->mutex);
+
+	token = strsep((char **)&buf, delim);
+	if (token != NULL)
+		filter = find_filter_type(token);
+
+	if (filter == UNKNOWN) {
+		pr_err("filter configuration failed, Unsupported filter\n");
+		goto filter_config_free;
+	}
+
+	token = strsep((char **)&buf, delim);
+	if (token == NULL) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_config_free;
+	}
+
+	if (kstrtoul(token, 10, &val)) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_config_free;
+	}
+
+	if ((filter == SCID) && (val >= SCID_MAX)) {
+		pr_err("filter configuration failed, SCID above MAX value\n");
+		goto filter_config_free;
+	}
+
+
+	while (token != NULL) {
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &port))
+			break;
+
+		llcc_priv->filtered_ports |= 1 << port;
+		port_ops = llcc_priv->port_ops[port];
+		if (port_ops->event_filter_config)
+			port_ops->event_filter_config(llcc_priv, filter, val,
+					true);
+	}
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+
+filter_config_free:
+	mutex_unlock(&llcc_priv->mutex);
+	return -EINVAL;
+}
+
+static ssize_t perfmon_filter_remove_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	struct event_port_ops *port_ops;
+	unsigned long port, val;
+	char *token, *delim = DELIM_CHAR;
+	enum filter_type filter = UNKNOWN;
+
+	mutex_lock(&llcc_priv->mutex);
+	token = strsep((char **)&buf, delim);
+	if (token != NULL)
+		filter = find_filter_type(token);
+
+	if (filter == UNKNOWN) {
+		pr_err("filter configuration failed, Unsupported filter\n");
+		goto filter_remove_free;
+	}
+
+	token = strsep((char **)&buf, delim);
+	if (token == NULL) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_remove_free;
+	}
+
+	if (kstrtoul(token, 10, &val)) {
+		pr_err("filter configuration failed, Wrong input\n");
+		goto filter_remove_free;
+	}
+
+	if ((filter == SCID) && (val >= SCID_MAX)) {
+		pr_err("filter configuration failed, SCID above MAX value\n");
+		goto filter_remove_free;
+	}
+
+	while (token != NULL) {
+		token = strsep((char **)&buf, delim);
+		if (token == NULL)
+			break;
+
+		if (kstrtoul(token, 10, &port))
+			break;
+
+		llcc_priv->filtered_ports &= ~(1 << port);
+		port_ops = llcc_priv->port_ops[port];
+		if (port_ops->event_filter_config)
+			port_ops->event_filter_config(llcc_priv, filter, val,
+					false);
+	}
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+
+filter_remove_free:
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_start_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val = 0, mask;
+	unsigned long start;
+
+	if (kstrtoul(buf, 10, &start))
+		return -EINVAL;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (start) {
+		if (!llcc_priv->configured_counters)
+			pr_err("start failed. perfmon not configured\n");
+
+		val = MANUAL_MODE | MONITOR_EN;
+		if (llcc_priv->expires.tv64) {
+			if (hrtimer_is_queued(&llcc_priv->hrtimer))
+				hrtimer_forward_now(&llcc_priv->hrtimer,
+						llcc_priv->expires);
+			else
+				hrtimer_start(&llcc_priv->hrtimer,
+						llcc_priv->expires,
+						HRTIMER_MODE_REL_PINNED);
+		}
+
+	} else {
+		if (llcc_priv->expires.tv64)
+			hrtimer_cancel(&llcc_priv->hrtimer);
+
+		if (!llcc_priv->configured_counters)
+			pr_err("stop failed. perfmon not configured\n");
+	}
+
+	mask = PERFMON_MODE_MONITOR_MODE_MASK | PERFMON_MODE_MONITOR_EN_MASK;
+	llcc_bcast_modify(llcc_priv, PERFMON_MODE, val, mask);
+
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_ns_periodic_dump_store(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+
+	if (kstrtos64(buf, 10, &llcc_priv->expires.tv64))
+		return -EINVAL;
+
+	mutex_lock(&llcc_priv->mutex);
+	if (!llcc_priv->expires.tv64) {
+		hrtimer_cancel(&llcc_priv->hrtimer);
+		mutex_unlock(&llcc_priv->mutex);
+		return count;
+	}
+
+	if (hrtimer_is_queued(&llcc_priv->hrtimer))
+		hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	else
+		hrtimer_start(&llcc_priv->hrtimer, llcc_priv->expires,
+			      HRTIMER_MODE_REL_PINNED);
+
+	mutex_unlock(&llcc_priv->mutex);
+	return count;
+}
+
+static ssize_t perfmon_scid_status_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct llcc_perfmon_private *llcc_priv = dev_get_drvdata(dev);
+	uint32_t val;
+	unsigned int i, offset;
+	ssize_t cnt = 0, print;
+
+	for (i = 0; i < SCID_MAX; i++) {
+		offset = TRP_SCID_n_STATUS(i);
+		llcc_bcast_read(llcc_priv, offset, &val);
+		if (val & TRP_SCID_STATUS_ACTIVE_MASK)
+			print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+					i, "ACTIVE");
+		else
+			print = snprintf(buf, MAX_STRING_SIZE, "SCID %02d %10s",
+					i, "DEACTIVE");
+
+		buf += print;
+		cnt += print;
+
+		val = (val & TRP_SCID_STATUS_CURRENT_CAP_MASK)
+			>> TRP_SCID_STATUS_CURRENT_CAP_SHIFT;
+		print = snprintf(buf, MAX_STRING_SIZE, ",0x%08x\n", val);
+		buf += print;
+		cnt += print;
+	}
+
+	return cnt;
+}
+
+static DEVICE_ATTR_RO(perfmon_counter_dump);
+static DEVICE_ATTR_WO(perfmon_configure);
+static DEVICE_ATTR_WO(perfmon_remove);
+static DEVICE_ATTR_WO(perfmon_filter_config);
+static DEVICE_ATTR_WO(perfmon_filter_remove);
+static DEVICE_ATTR_WO(perfmon_start);
+static DEVICE_ATTR_RO(perfmon_scid_status);
+static DEVICE_ATTR_WO(perfmon_ns_periodic_dump);
+
+static struct attribute *llcc_perfmon_attrs[] = {
+	&dev_attr_perfmon_counter_dump.attr,
+	&dev_attr_perfmon_configure.attr,
+	&dev_attr_perfmon_remove.attr,
+	&dev_attr_perfmon_filter_config.attr,
+	&dev_attr_perfmon_filter_remove.attr,
+	&dev_attr_perfmon_start.attr,
+	&dev_attr_perfmon_scid_status.attr,
+	&dev_attr_perfmon_ns_periodic_dump.attr,
+	NULL,
+};
+
+static struct attribute_group llcc_perfmon_group = {
+	.attrs	= llcc_perfmon_attrs,
+};
+
+static void perfmon_counter_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int port, unsigned int event_counter_num)
+{
+	uint32_t val;
+
+	val = (port & PERFMON_PORT_SELECT_MASK) |
+		((event_counter_num << EVENT_SELECT_SHIFT) &
+		PERFMON_EVENT_SELECT_MASK) | CLEAR_ON_ENABLE | CLEAR_ON_DUMP;
+	llcc_bcast_write(llcc_priv, PERFMON_COUNTER_n_CONFIG(event_counter_num),
+			val);
+}
+
+static void feac_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FEAC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FEAC, counter_num);
+}
+
+static void feac_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable) {
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+			val |= (FILTER_0 << FEAC_SCALING_FILTER_SEL_SHIFT) |
+			       FEAC_SCALING_FILTER_EN;
+	}
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEAC))
+		mask |= FEAC_SCALING_FILTER_SEL_MASK |
+			FEAC_SCALING_FILTER_EN_MASK;
+
+	llcc_bcast_modify(llcc_priv, FEAC_PROF_CFG, val, mask);
+}
+
+static void feac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter == SCID) {
+		if (enable)
+			val = (match << SCID_MATCH_SHIFT)
+				| SCID_MASK_MASK;
+
+		mask = SCID_MATCH_MASK | SCID_MASK_MASK;
+		llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG6, val,
+				mask);
+	} else if (filter == MID) {
+		if (enable)
+			val = (match << MID_MATCH_SHIFT)
+				| MID_MASK_MASK;
+
+		mask = MID_MATCH_MASK | MID_MASK_MASK;
+		llcc_bcast_modify(llcc_priv, FEAC_PROF_FILTER_0_CFG5, val,
+				mask);
+	} else {
+		pr_err("unknown filter/not supported\n");
+	}
+}
+
+static struct event_port_ops feac_port_ops = {
+	.event_config	= feac_event_config,
+	.event_enable	= feac_event_enable,
+	.event_filter_config	= feac_event_filter_config,
+};
+
+static void ferc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = event_type << EVENT_SEL_SHIFT;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FERC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FERC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FERC, counter_num);
+}
+
+static void ferc_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, FERC_PROF_CFG, val, mask);
+}
+
+static void ferc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, FERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops ferc_port_ops = {
+	.event_config	= ferc_event_config,
+	.event_enable	= ferc_event_enable,
+	.event_filter_config	= ferc_event_filter_config,
+};
+
+static void fewc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_FEWC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, FEWC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_FEWC, counter_num);
+}
+
+static void fewc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, FEWC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops fewc_port_ops = {
+	.event_config	= fewc_event_config,
+	.event_filter_config	= fewc_event_filter_config,
+};
+
+static void beac_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BEAC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_BEAC, counter_num);
+}
+
+static void beac_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static void beac_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << BEAC_PROFTAG_MATCH_SHIFT)
+			| FILTER_0_MASK << BEAC_PROFTAG_MASK_SHIFT;
+
+	mask = BEAC_PROFTAG_MASK_MASK | BEAC_PROFTAG_MATCH_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_FILTER_0_CFG5, val, mask);
+
+	if (enable)
+		val = match << BEAC_MC_PROFTAG_SHIFT;
+
+	mask = BEAC_MC_PROFTAG_MASK;
+	llcc_bcast_modify(llcc_priv, BEAC_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops beac_port_ops = {
+	.event_config	= beac_event_config,
+	.event_enable	= beac_event_enable,
+	.event_filter_config	= beac_event_filter_config,
+};
+
+static void berc_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_BERC))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, BERC_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_BERC, counter_num);
+}
+
+static void berc_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BYTE_SCALING << BYTE_SCALING_SHIFT) |
+			(BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_BYTE_SCALING_MASK
+		| PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, BERC_PROF_CFG, val, mask);
+}
+
+static void berc_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter != PROFILING_TAG) {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	if (enable)
+		val = (match << PROFTAG_MATCH_SHIFT) |
+		       FILTER_0_MASK << PROFTAG_MASK_SHIFT;
+
+	mask = PROFTAG_MATCH_MASK | PROFTAG_MASK_MASK;
+	llcc_bcast_modify(llcc_priv, BERC_PROF_FILTER_0_CFG0, val, mask);
+}
+
+static struct event_port_ops berc_port_ops = {
+	.event_config	= berc_event_config,
+	.event_enable	= berc_event_enable,
+	.event_filter_config	= berc_event_filter_config,
+};
+
+static void trp_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_TRP))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, TRP_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_TRP, counter_num);
+}
+
+static void trp_event_filter_config(struct llcc_perfmon_private *llcc_priv,
+		enum filter_type filter, unsigned long match, bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (filter == SCID) {
+		if (enable)
+			val = (match << TRP_SCID_MATCH_SHIFT)
+				| TRP_SCID_MASK_MASK;
+
+		mask = TRP_SCID_MATCH_MASK | TRP_SCID_MASK_MASK;
+	} else if (filter == WAY_ID) {
+		if (enable)
+			val = (match << TRP_WAY_ID_MATCH_SHIFT)
+				| TRP_WAY_ID_MASK_MASK;
+
+		mask = TRP_WAY_ID_MATCH_MASK |
+			TRP_WAY_ID_MASK_MASK;
+	} else if (filter == PROFILING_TAG) {
+		if (enable)
+			val = (match << TRP_PROFTAG_MATCH_SHIFT)
+				| FILTER_0_MASK << TRP_PROFTAG_MASK_SHIFT;
+
+		mask = TRP_PROFTAG_MATCH_MASK | TRP_PROFTAG_MASK_MASK;
+	} else {
+		pr_err("unknown filter/not supported\n");
+		return;
+	}
+
+	llcc_bcast_modify(llcc_priv, TRP_PROF_FILTER_0_CFG1, val, mask);
+}
+
+static struct event_port_ops  trp_port_ops = {
+	.event_config	= trp_event_config,
+	.event_filter_config	= trp_event_filter_config,
+};
+
+static void drp_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_DRP))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, DRP_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_DRP, counter_num);
+}
+
+static void drp_event_enable(struct llcc_perfmon_private *llcc_priv,
+		bool enable)
+{
+	uint32_t val = 0, mask;
+
+	if (enable)
+		val = (BEAT_SCALING << BEAT_SCALING_SHIFT) | PROF_EN;
+
+	mask = PROF_CFG_BEAT_SCALING_MASK | PROF_CFG_EN_MASK;
+	llcc_bcast_modify(llcc_priv, DRP_PROF_CFG, val, mask);
+}
+
+static struct event_port_ops drp_port_ops = {
+	.event_config	= drp_event_config,
+	.event_enable	= drp_event_enable,
+};
+
+static void pmgr_event_config(struct llcc_perfmon_private *llcc_priv,
+		unsigned int event_type, unsigned int event_counter_num,
+		bool enable)
+{
+	uint32_t val = 0, mask, counter_num = 0;
+
+	mask = EVENT_SEL_MASK;
+	if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+		mask |= FILTER_SEL_MASK | FILTER_EN_MASK;
+
+	if (enable) {
+		val = (event_type << EVENT_SEL_SHIFT) & EVENT_SEL_MASK;
+		if (llcc_priv->filtered_ports & (1 << EVENT_PORT_PMGR))
+			val |= (FILTER_0 << FILTER_SEL_SHIFT) | FILTER_EN;
+
+		counter_num = event_counter_num;
+	}
+
+	llcc_bcast_modify(llcc_priv, PMGR_PROF_EVENT_n_CFG(event_counter_num),
+			val, mask);
+	perfmon_counter_config(llcc_priv, EVENT_PORT_PMGR, counter_num);
+}
+
+static struct event_port_ops pmgr_port_ops = {
+	.event_config	= pmgr_event_config,
+};
+
+static void llcc_register_event_port(struct llcc_perfmon_private *llcc_priv,
+		struct event_port_ops *ops, unsigned int event_port_num)
+{
+	if (llcc_priv->port_configd >= MAX_NUMBER_OF_PORTS) {
+		pr_err("Register port Failure!");
+		return;
+	}
+
+	llcc_priv->port_configd = llcc_priv->port_configd + 1;
+	llcc_priv->port_ops[event_port_num] = ops;
+}
+
+static enum hrtimer_restart llcc_perfmon_timer_handler(struct hrtimer *hrtimer)
+{
+	struct llcc_perfmon_private *llcc_priv = container_of(hrtimer,
+			struct llcc_perfmon_private, hrtimer);
+
+	perfmon_counter_dump(llcc_priv);
+	hrtimer_forward_now(&llcc_priv->hrtimer, llcc_priv->expires);
+	return HRTIMER_RESTART;
+}
+
+static int llcc_perfmon_probe(struct platform_device *pdev)
+{
+	int result = 0;
+	struct llcc_perfmon_private *llcc_priv;
+	struct device *dev = &pdev->dev;
+	uint32_t val;
+
+	llcc_priv = devm_kzalloc(&pdev->dev, sizeof(*llcc_priv), GFP_KERNEL);
+	if (llcc_priv == NULL)
+		return -ENOMEM;
+
+	llcc_priv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
+	if (IS_ERR(llcc_priv->llcc_map))
+		return PTR_ERR(llcc_priv->llcc_map);
+
+	result = of_property_read_u32(pdev->dev.parent->of_node,
+			"qcom,llcc-broadcast-off", &llcc_priv->broadcast_off);
+	if (result) {
+		pr_err("Invalid qcom,broadcast-off entry\n");
+		return result;
+	}
+
+	llcc_bcast_read(llcc_priv, LLCC_COMMON_STATUS0, &val);
+
+	llcc_priv->num_banks = (val & LB_CNT_MASK) >> LB_CNT_SHIFT;
+	result = of_property_read_variable_u32_array(pdev->dev.parent->of_node,
+			"qcom,llcc-banks-off", (u32 *)&llcc_priv->bank_off,
+			1, llcc_priv->num_banks);
+	if (result < 0) {
+		pr_err("Invalid qcom,llcc-banks-off entry\n");
+		return result;
+	}
+
+	result = sysfs_create_group(&pdev->dev.kobj, &llcc_perfmon_group);
+	if (result) {
+		pr_err("Unable to create sysfs version group\n");
+		return result;
+	}
+
+	mutex_init(&llcc_priv->mutex);
+	platform_set_drvdata(pdev, llcc_priv);
+	llcc_register_event_port(llcc_priv, &feac_port_ops, EVENT_PORT_FEAC);
+	llcc_register_event_port(llcc_priv, &ferc_port_ops, EVENT_PORT_FERC);
+	llcc_register_event_port(llcc_priv, &fewc_port_ops, EVENT_PORT_FEWC);
+	llcc_register_event_port(llcc_priv, &beac_port_ops, EVENT_PORT_BEAC);
+	llcc_register_event_port(llcc_priv, &berc_port_ops, EVENT_PORT_BERC);
+	llcc_register_event_port(llcc_priv, &trp_port_ops, EVENT_PORT_TRP);
+	llcc_register_event_port(llcc_priv, &drp_port_ops, EVENT_PORT_DRP);
+	llcc_register_event_port(llcc_priv, &pmgr_port_ops, EVENT_PORT_PMGR);
+	hrtimer_init(&llcc_priv->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	llcc_priv->hrtimer.function = llcc_perfmon_timer_handler;
+	llcc_priv->expires.tv64 = 0;
+	return 0;
+}
+
+static int llcc_perfmon_remove(struct platform_device *pdev)
+{
+	struct llcc_perfmon_private *llcc_priv = platform_get_drvdata(pdev);
+
+	while (hrtimer_active(&llcc_priv->hrtimer))
+		hrtimer_cancel(&llcc_priv->hrtimer);
+
+	mutex_destroy(&llcc_priv->mutex);
+	sysfs_remove_group(&pdev->dev.kobj, &llcc_perfmon_group);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static const struct of_device_id of_match_llcc[] = {
+	{
+		.compatible = "qcom,llcc-perfmon",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, of_match_llcc);
+
+static struct platform_driver llcc_perfmon_driver = {
+	.probe = llcc_perfmon_probe,
+	.remove	= llcc_perfmon_remove,
+	.driver	= {
+		.name = LLCC_PERFMON_NAME,
+		.of_match_table = of_match_llcc,
+	}
+};
+module_platform_driver(llcc_perfmon_driver);
+
+MODULE_DESCRIPTION("QCOM LLCC PMU MONITOR");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/llcc_perfmon.h b/drivers/soc/qcom/llcc_perfmon.h
new file mode 100644
index 0000000..11dd2f8
--- /dev/null
+++ b/drivers/soc/qcom/llcc_perfmon.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SOC_QCOM_LLCC_PERFMON_H_
+#define _SOC_QCOM_LLCC_PERFMON_H_
+
+#define LLCC_COMMON_STATUS0		(0x3000C)
+/* FEAC */
+#define FEAC_PROF_FILTER_0_CFG5		(0x037014)
+#define FEAC_PROF_FILTER_0_CFG6		(0x037018)
+#define FEAC_PROF_EVENT_n_CFG(n)	(0x037060 + 4 * n)
+#define FEAC_PROF_CFG			(0x0370A0)
+
+/* FERC */
+#define FERC_PROF_FILTER_0_CFG0		(0x03B000)
+#define FERC_PROF_EVENT_n_CFG(n)	(0x03B020 + 4 * n)
+#define FERC_PROF_CFG			(0x03B060)
+
+/* FEWC */
+#define FEWC_PROF_FILTER_0_CFG0		(0x033000)
+#define FEWC_PROF_EVENT_n_CFG(n)	(0x033020 + 4 * n)
+
+/* BEAC */
+#define BEAC_PROF_FILTER_0_CFG5		(0x049014)
+#define BEAC_PROF_EVENT_n_CFG(n)	(0x049040 + 4 * n)
+#define BEAC_PROF_CFG			(0x049080)
+
+/* BERC */
+#define BERC_PROF_FILTER_0_CFG0		(0x039000)
+#define BERC_PROF_EVENT_n_CFG(n)	(0x039020 + 4 * n)
+#define BERC_PROF_CFG			(0x039060)
+
+/* TRP */
+#define TRP_PROF_FILTER_0_CFG1		(0x024004)
+#define TRP_PROF_EVENT_n_CFG(n)		(0x024020 + 4 * n)
+#define TRP_SCID_n_STATUS(n)		(0x000004 + 0x1000 * n)
+
+/* DRP */
+#define DRP_PROF_EVENT_n_CFG(n)		(0x044010 + 4 * n)
+#define DRP_PROF_CFG			(0x044050)
+
+/* PMGR */
+#define PMGR_PROF_EVENT_n_CFG(n)	(0x03F000 + 4 * n)
+
+#define PERFMON_COUNTER_n_CONFIG(n)	(0x031020 + 4 * n)
+#define PERFMON_MODE			(0x03100C)
+#define PERFMON_DUMP			(0x031010)
+#define BROADCAST_COUNTER_n_VALUE(n)	(0x031060 + 4 * n)
+
+#define LLCC_COUNTER_n_VALUE(n)		(0x031060 + 4 * n)
+
+#define EVENT_NUM_MAX			(64)
+#define SCID_MAX			(32)
+
+/* Perfmon */
+#define CLEAR_ON_ENABLE			BIT(31)
+#define CLEAR_ON_DUMP			BIT(30)
+#define FREEZE_ON_SATURATE		BIT(29)
+#define CHAINING_EN			BIT(28)
+#define COUNT_CLOCK_EVENT		BIT(24)
+
+#define EVENT_SELECT_SHIFT		(16)
+#define PERFMON_EVENT_SELECT_MASK	GENMASK(EVENT_SELECT_SHIFT + 4,\
+						EVENT_SELECT_SHIFT)
+#define PORT_SELECT_SHIFT		(0)
+#define PERFMON_PORT_SELECT_MASK	GENMASK(PORT_SELECT_SHIFT + 3,\
+						PORT_SELECT_SHIFT)
+
+#define MANUAL_MODE			(0)
+#define TIMED_MODE			(1)
+#define TRIGGER_MODE			(2)
+#define MONITOR_EN_SHIFT		(15)
+#define MONITOR_EN			BIT(MONITOR_EN_SHIFT)
+#define PERFMON_MODE_MONITOR_EN_MASK	GENMASK(MONITOR_EN_SHIFT + 0,\
+						MONITOR_EN_SHIFT)
+#define MONITOR_MODE_SHIFT		(0)
+#define PERFMON_MODE_MONITOR_MODE_MASK	GENMASK(MONITOR_MODE_SHIFT + 0,\
+						MONITOR_MODE_SHIFT)
+
+#define MONITOR_DUMP			BIT(0)
+
+/* COMMON */
+#define BYTE_SCALING			(1024)
+#define BEAT_SCALING			(32)
+#define LB_CNT_SHIFT			(28)
+#define LB_CNT_MASK			GENMASK(LB_CNT_SHIFT + 3, \
+						LB_CNT_SHIFT)
+
+#define BYTE_SCALING_SHIFT		(16)
+#define PROF_CFG_BYTE_SCALING_MASK	GENMASK(BYTE_SCALING_SHIFT + 11,\
+						BYTE_SCALING_SHIFT)
+#define BEAT_SCALING_SHIFT		(8)
+#define PROF_CFG_BEAT_SCALING_MASK	GENMASK(BEAT_SCALING_SHIFT + 7,\
+						BEAT_SCALING_SHIFT)
+#define PROF_EN_SHIFT			(0)
+#define PROF_EN				BIT(PROF_EN_SHIFT)
+#define PROF_CFG_EN_MASK		GENMASK(PROF_EN_SHIFT + 0,\
+						PROF_EN_SHIFT)
+
+#define FILTER_EN_SHIFT			(31)
+#define FILTER_EN			BIT(FILTER_EN_SHIFT)
+#define FILTER_EN_MASK			GENMASK(FILTER_EN_SHIFT + 0,\
+						FILTER_EN_SHIFT)
+#define FILTER_0			(0)
+#define FILTER_0_MASK			GENMASK(FILTER_0 + 0, \
+						FILTER_0)
+#define FILTER_1			(1)
+#define FILTER_1_MASK			GENMASK(FILTER_1 + 0, \
+						FILTER_1)
+
+#define FILTER_SEL_SHIFT		(16)
+#define FILTER_SEL_MASK			GENMASK(FILTER_SEL_SHIFT + 0,\
+						FILTER_SEL_SHIFT)
+#define EVENT_SEL_SHIFT			(0)
+#define EVENT_SEL_MASK			GENMASK(EVENT_SEL_SHIFT + 5,\
+						EVENT_SEL_SHIFT)
+
+#define MID_MASK_SHIFT			(16)
+#define MID_MASK_MASK			GENMASK(MID_MASK_SHIFT + 15, \
+						MID_MASK_SHIFT)
+#define MID_MATCH_SHIFT			(0)
+#define MID_MATCH_MASK			GENMASK(MID_MATCH_SHIFT + 15, \
+						MID_MATCH_SHIFT)
+#define SCID_MASK_SHIFT			(16)
+#define SCID_MASK_MASK			GENMASK(SCID_MASK_SHIFT + 15, \
+						SCID_MASK_SHIFT)
+#define SCID_MATCH_SHIFT		(0)
+#define SCID_MATCH_MASK			GENMASK(SCID_MATCH_SHIFT + 15, \
+						SCID_MATCH_SHIFT)
+#define PROFTAG_MASK_SHIFT		(2)
+#define PROFTAG_MASK_MASK		GENMASK(PROFTAG_MASK_SHIFT + 1,\
+						PROFTAG_MASK_SHIFT)
+#define PROFTAG_MATCH_SHIFT		(0)
+#define PROFTAG_MATCH_MASK		GENMASK(PROFTAG_MATCH_SHIFT + 1,\
+						PROFTAG_MATCH_SHIFT)
+/* FEAC */
+#define FEAC_SCALING_FILTER_SEL_SHIFT	(2)
+#define FEAC_SCALING_FILTER_SEL_MASK	GENMASK(FEAC_SCALING_FILTER_SEL_SHIFT \
+					+ 0, \
+					FEAC_SCALING_FILTER_SEL_SHIFT)
+#define FEAC_SCALING_FILTER_EN_SHIFT	(1)
+#define FEAC_SCALING_FILTER_EN		BIT(FEAC_SCALING_FILTER_EN_SHIFT)
+#define FEAC_SCALING_FILTER_EN_MASK	GENMASK(FEAC_SCALING_FILTER_EN_SHIFT \
+					+ 0, \
+					FEAC_SCALING_FILTER_EN_SHIFT)
+/* BEAC */
+#define BEAC_PROFTAG_MASK_SHIFT		(14)
+#define BEAC_PROFTAG_MASK_MASK		GENMASK(BEAC_PROFTAG_MASK_SHIFT + 1,\
+						BEAC_PROFTAG_MASK_SHIFT)
+#define BEAC_PROFTAG_MATCH_SHIFT	(12)
+#define BEAC_PROFTAG_MATCH_MASK		GENMASK(BEAC_PROFTAG_MATCH_SHIFT + 1,\
+						BEAC_PROFTAG_MATCH_SHIFT)
+#define BEAC_MC_PROFTAG_SHIFT		(1)
+#define BEAC_MC_PROFTAG_MASK		GENMASK(BEAC_MC_PROFTAG_SHIFT + 1,\
+					BEAC_MC_PROFTAG_SHIFT)
+/* TRP */
+#define TRP_SCID_MATCH_SHIFT		(0)
+#define TRP_SCID_MATCH_MASK		GENMASK(TRP_SCID_MATCH_SHIFT + 4,\
+						TRP_SCID_MATCH_SHIFT)
+#define TRP_SCID_MASK_SHIFT		(8)
+#define TRP_SCID_MASK_MASK		GENMASK(TRP_SCID_MASK_SHIFT + 4,\
+						TRP_SCID_MASK_SHIFT)
+#define TRP_WAY_ID_MATCH_SHIFT		(16)
+#define TRP_WAY_ID_MATCH_MASK		GENMASK(TRP_WAY_ID_MATCH_SHIFT + 3,\
+						TRP_WAY_ID_MATCH_SHIFT)
+#define TRP_WAY_ID_MASK_SHIFT		(20)
+#define TRP_WAY_ID_MASK_MASK		GENMASK(TRP_WAY_ID_MASK_SHIFT + 3,\
+						TRP_WAY_ID_MASK_SHIFT)
+#define TRP_PROFTAG_MATCH_SHIFT		(24)
+#define TRP_PROFTAG_MATCH_MASK		GENMASK(TRP_PROFTAG_MATCH_SHIFT + 1,\
+						TRP_PROFTAG_MATCH_SHIFT)
+#define TRP_PROFTAG_MASK_SHIFT		(28)
+#define TRP_PROFTAG_MASK_MASK		GENMASK(TRP_PROFTAG_MASK_SHIFT + 1,\
+						TRP_PROFTAG_MASK_SHIFT)
+
+#define TRP_SCID_STATUS_ACTIVE_SHIFT		(0)
+#define TRP_SCID_STATUS_ACTIVE_MASK		GENMASK( \
+						TRP_SCID_STATUS_ACTIVE_SHIFT \
+						+ 0, \
+						TRP_SCID_STATUS_ACTIVE_SHIFT)
+#define TRP_SCID_STATUS_DEACTIVE_SHIFT		(1)
+#define TRP_SCID_STATUS_CURRENT_CAP_SHIFT	(16)
+#define TRP_SCID_STATUS_CURRENT_CAP_MASK	GENMASK( \
+					TRP_SCID_STATUS_CURRENT_CAP_SHIFT \
+					+ 13, \
+					TRP_SCID_STATUS_CURRENT_CAP_SHIFT)
+
+#endif /* _SOC_QCOM_LLCC_PERFMON_H_ */
diff --git a/drivers/soc/qcom/lpm-stats.c b/drivers/soc/qcom/lpm-stats.c
index ee68433..4a41eee 100644
--- a/drivers/soc/qcom/lpm-stats.c
+++ b/drivers/soc/qcom/lpm-stats.c
@@ -21,6 +21,7 @@
 #include <linux/debugfs.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/smp.h>
 #include <linux/suspend.h>
 #include <soc/qcom/spm.h>
 #include <soc/qcom/pm.h>
@@ -45,7 +46,7 @@
 	int64_t max_time[CONFIG_MSM_IDLE_STATS_BUCKET_COUNT];
 	int success_count;
 	int failed_count;
-	int64_t total_time;
+	uint64_t total_time;
 	uint64_t enter_time;
 };
 
@@ -104,7 +105,7 @@
 	int i = 0;
 	int64_t bucket_time = 0;
 	char seqs[MAX_STR_LEN] = {0};
-	int64_t s = stats->total_time;
+	uint64_t s = stats->total_time;
 	uint32_t ns = do_div(s, NSEC_PER_SEC);
 
 	snprintf(seqs, MAX_STR_LEN,
@@ -255,6 +256,15 @@
 	return count;
 }
 
+static void reset_cpu_stats(void *info)
+{
+	struct lpm_stats *stats = &(*this_cpu_ptr(&(cpu_stats)));
+	int i;
+
+	for (i = 0; i < stats->num_levels; i++)
+		level_stats_reset(&stats->time_stats[i]);
+}
+
 static ssize_t lpm_stats_file_write(struct file *file,
 	const char __user *buffer, size_t count, loff_t *off)
 {
@@ -276,6 +286,12 @@
 		return -EINVAL;
 
 	level_stats_reset_all(stats);
+	/*
+	 * Wake up each CPU and reset the stats from that CPU,
+	 * for that CPU, so we could have better timestamp for
+	 * accounting.
+	 */
+	on_each_cpu(reset_cpu_stats, NULL, 1);
 
 	return count;
 }
diff --git a/drivers/soc/qcom/memory_dump_v2.c b/drivers/soc/qcom/memory_dump_v2.c
index 5ed66bf..5873f5c 100644
--- a/drivers/soc/qcom/memory_dump_v2.c
+++ b/drivers/soc/qcom/memory_dump_v2.c
@@ -38,7 +38,18 @@
 	struct msm_dump_table *table;
 };
 
+struct dump_vaddr_entry {
+	uint32_t id;
+	void *dump_vaddr;
+};
+
+struct msm_mem_dump_vaddr_tbl {
+	uint8_t num_node;
+	struct dump_vaddr_entry *entries;
+};
+
 static struct msm_memory_dump memdump;
+static struct msm_mem_dump_vaddr_tbl vaddr_tbl;
 
 uint32_t msm_dump_table_version(void)
 {
@@ -113,6 +124,28 @@
 }
 EXPORT_SYMBOL(msm_dump_data_register);
 
+void *get_msm_dump_ptr(enum msm_dump_data_ids id)
+{
+	int i;
+
+	if (!vaddr_tbl.entries)
+		return NULL;
+
+	if (id > MSM_DUMP_DATA_MAX)
+		return NULL;
+
+	for (i = 0; i < vaddr_tbl.num_node; i++) {
+		if (vaddr_tbl.entries[i].id == id)
+			break;
+	}
+
+	if (i == vaddr_tbl.num_node)
+		return NULL;
+
+	return (void *)vaddr_tbl.entries[i].dump_vaddr;
+}
+EXPORT_SYMBOL(get_msm_dump_ptr);
+
 static int __init init_memory_dump(void)
 {
 	struct msm_dump_table *table;
@@ -209,6 +242,14 @@
 	struct msm_dump_entry dump_entry;
 	int ret;
 	u32 size, id;
+	int i = 0;
+
+	vaddr_tbl.num_node = of_get_child_count(node);
+	vaddr_tbl.entries = devm_kcalloc(&pdev->dev, vaddr_tbl.num_node,
+				 sizeof(struct dump_vaddr_entry),
+				 GFP_KERNEL);
+	if (!vaddr_tbl.entries)
+		dev_err(&pdev->dev, "Unable to allocate mem for ptr addr\n");
 
 	for_each_available_child_of_node(node, child_node) {
 		ret = of_property_read_u32(child_node, "qcom,dump-size", &size);
@@ -254,6 +295,10 @@
 			dma_free_coherent(&pdev->dev, size, dump_vaddr,
 					dump_addr);
 			devm_kfree(&pdev->dev, dump_data);
+		} else if (vaddr_tbl.entries) {
+			vaddr_tbl.entries[i].id = id;
+			vaddr_tbl.entries[i].dump_vaddr = dump_vaddr;
+			i++;
 		}
 	}
 	return 0;
diff --git a/drivers/soc/qcom/msm-core.c b/drivers/soc/qcom/msm-core.c
deleted file mode 100644
index f8103de..0000000
--- a/drivers/soc/qcom/msm-core.c
+++ /dev/null
@@ -1,902 +0,0 @@
-/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/cpu.h>
-#include <linux/cpufreq.h>
-#include <linux/err.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/kthread.h>
-#include <linux/kernel.h>
-#include <linux/miscdevice.h>
-#include <linux/module.h>
-#include <linux/msm-core-interface.h>
-#include <linux/mutex.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/pm_opp.h>
-#include <linux/platform_device.h>
-#include <linux/pm_opp.h>
-#include <linux/slab.h>
-#include <linux/suspend.h>
-#include <linux/thermal.h>
-#include <linux/types.h>
-#include <linux/uaccess.h>
-#include <linux/uio_driver.h>
-#include <asm/smp_plat.h>
-#include <asm/cputype.h>
-#include <stdbool.h>
-#define CREATE_TRACE_POINTS
-#include <trace/events/trace_msm_core.h>
-
-#define TEMP_BASE_POINT 35
-#define TEMP_MAX_POINT 95
-#define CPU_HOTPLUG_LIMIT 80
-#define CPU_BIT_MASK(cpu) BIT(cpu)
-#define DEFAULT_TEMP 40
-#define DEFAULT_LOW_HYST_TEMP 10
-#define DEFAULT_HIGH_HYST_TEMP 5
-#define MAX_CORES_PER_CLUSTER 4
-#define MAX_NUM_OF_CLUSTERS 2
-#define NUM_OF_CORNERS 10
-#define DEFAULT_SCALING_FACTOR 1
-
-#define ALLOCATE_2D_ARRAY(type) \
-static type **allocate_2d_array_##type(int idx)\
-{\
-	int i;\
-	type **ptr = NULL;\
-	if (!idx) \
-		return ERR_PTR(-EINVAL);\
-	ptr = kzalloc(sizeof(*ptr) * TEMP_DATA_POINTS, \
-				GFP_KERNEL);\
-	if (!ptr) { \
-		return ERR_PTR(-ENOMEM); \
-	} \
-	for (i = 0; i < TEMP_DATA_POINTS; i++) { \
-		ptr[i] = kzalloc(sizeof(*ptr[i]) * \
-					idx, GFP_KERNEL);\
-		if (!ptr[i]) {\
-			goto done;\
-		} \
-	} \
-	return ptr;\
-done:\
-	for (i = 0; i < TEMP_DATA_POINTS; i++) \
-		kfree(ptr[i]);\
-	kfree(ptr);\
-	return ERR_PTR(-ENOMEM);\
-}
-
-struct cpu_activity_info {
-	int cpu;
-	int mpidr;
-	long temp;
-	int sensor_id;
-	struct cpu_static_info *sp;
-};
-
-struct cpu_static_info {
-	uint32_t **power;
-	cpumask_t mask;
-	struct cpufreq_frequency_table *table;
-	uint32_t *voltage;
-	uint32_t num_of_freqs;
-};
-
-static DEFINE_MUTEX(policy_update_mutex);
-static DEFINE_MUTEX(kthread_update_mutex);
-static DEFINE_SPINLOCK(update_lock);
-static struct delayed_work sampling_work;
-static struct completion sampling_completion;
-static struct task_struct *sampling_task;
-static int low_hyst_temp;
-static int high_hyst_temp;
-static struct platform_device *msm_core_pdev;
-static struct cpu_activity_info activity[NR_CPUS];
-DEFINE_PER_CPU(struct cpu_pstate_pwr *, ptable);
-static struct cpu_pwr_stats cpu_stats[NR_CPUS];
-ALLOCATE_2D_ARRAY(uint32_t);
-
-static int poll_ms;
-module_param_named(polling_interval, poll_ms, int, 0664);
-
-static int disabled;
-module_param_named(disabled, disabled, int, 0664);
-
-static bool in_suspend;
-static bool activate_power_table;
-static int max_throttling_temp = 80; /* in C */
-module_param_named(throttling_temp, max_throttling_temp, int, 0664);
-
-static void samplequeue_handle(struct work_struct *work)
-{
-	complete(&sampling_completion);
-}
-
-static void repopulate_stats(int cpu)
-{
-	int i;
-	struct cpu_activity_info *cpu_node = &activity[cpu];
-	int temp_point;
-	struct cpu_pstate_pwr *pt =  per_cpu(ptable, cpu);
-
-	if (!pt)
-		return;
-
-	if (cpu_node->temp < TEMP_BASE_POINT)
-		temp_point = 0;
-	else if (cpu_node->temp > TEMP_MAX_POINT)
-		temp_point = TEMP_DATA_POINTS - 1;
-	else
-		temp_point = (cpu_node->temp - TEMP_BASE_POINT) / 5;
-
-	cpu_stats[cpu].temp = cpu_node->temp;
-	for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
-		pt[i].power = cpu_node->sp->power[temp_point][i];
-
-	trace_cpu_stats(cpu, cpu_stats[cpu].temp, pt[0].power,
-			pt[cpu_node->sp->num_of_freqs-1].power);
-};
-
-void trigger_cpu_pwr_stats_calc(void)
-{
-	int cpu;
-	static long prev_temp[NR_CPUS];
-	struct cpu_activity_info *cpu_node;
-
-	if (disabled)
-		return;
-
-	spin_lock(&update_lock);
-
-	for_each_online_cpu(cpu) {
-		cpu_node = &activity[cpu];
-		if (cpu_node->sensor_id < 0)
-			continue;
-
-		prev_temp[cpu] = cpu_node->temp;
-
-		/*
-		 * Do not populate/update stats before policy and ptable have
-		 * been updated.
-		 */
-		if (activate_power_table && cpu_stats[cpu].ptable
-			&& cpu_node->sp->table)
-			repopulate_stats(cpu);
-	}
-	spin_unlock(&update_lock);
-}
-EXPORT_SYMBOL(trigger_cpu_pwr_stats_calc);
-
-void set_cpu_throttled(cpumask_t *mask, bool throttling)
-{
-	int cpu;
-
-	if (!mask)
-		return;
-
-	spin_lock(&update_lock);
-	for_each_cpu(cpu, mask)
-		cpu_stats[cpu].throttling = throttling;
-	spin_unlock(&update_lock);
-}
-EXPORT_SYMBOL(set_cpu_throttled);
-
-static void update_related_freq_table(struct cpufreq_policy *policy)
-{
-	int cpu, num_of_freqs;
-	struct cpufreq_frequency_table *table;
-
-	table = policy->freq_table;
-	if (!table) {
-		pr_err("Couldn't get freq table for cpu%d\n",
-				policy->cpu);
-		return;
-	}
-
-	for (num_of_freqs = 0; table[num_of_freqs].frequency !=
-			CPUFREQ_TABLE_END;)
-		num_of_freqs++;
-
-	/*
-	 * Synchronous cores within cluster have the same
-	 * policy. Since these cores do not have the cpufreq
-	 * table initialized for all of them, copy the same
-	 * table to all the related cpus.
-	 */
-	for_each_cpu(cpu, policy->related_cpus) {
-		activity[cpu].sp->table = table;
-		activity[cpu].sp->num_of_freqs = num_of_freqs;
-	}
-}
-
-static __ref int do_sampling(void *data)
-{
-	int cpu;
-	struct cpu_activity_info *cpu_node;
-	static int prev_temp[NR_CPUS];
-
-	while (!kthread_should_stop()) {
-		wait_for_completion(&sampling_completion);
-		cancel_delayed_work(&sampling_work);
-
-		mutex_lock(&kthread_update_mutex);
-		if (in_suspend)
-			goto unlock;
-
-		trigger_cpu_pwr_stats_calc();
-
-		for_each_online_cpu(cpu) {
-			cpu_node = &activity[cpu];
-			if (prev_temp[cpu] != cpu_node->temp) {
-				prev_temp[cpu] = cpu_node->temp;
-			}
-		}
-		if (!poll_ms)
-			goto unlock;
-
-		schedule_delayed_work(&sampling_work,
-			msecs_to_jiffies(poll_ms));
-unlock:
-		mutex_unlock(&kthread_update_mutex);
-	}
-	return 0;
-}
-
-static void clear_static_power(struct cpu_static_info *sp)
-{
-	int i;
-
-	if (!sp)
-		return;
-
-	if (cpumask_first(&sp->mask) < num_possible_cpus())
-		return;
-
-	for (i = 0; i < TEMP_DATA_POINTS; i++)
-		kfree(sp->power[i]);
-	kfree(sp->power);
-	kfree(sp);
-}
-
-BLOCKING_NOTIFIER_HEAD(msm_core_stats_notifier_list);
-
-struct blocking_notifier_head *get_power_update_notifier(void)
-{
-	return &msm_core_stats_notifier_list;
-}
-
-int register_cpu_pwr_stats_ready_notifier(struct notifier_block *nb)
-{
-	return blocking_notifier_chain_register(&msm_core_stats_notifier_list,
-						nb);
-}
-
-static int update_userspace_power(struct sched_params __user *argp)
-{
-	int i;
-	int ret;
-	int cpu = -1;
-	struct cpu_activity_info *node;
-	struct cpu_static_info *sp, *clear_sp;
-	int cpumask, cluster;
-	bool pdata_valid[NR_CPUS] = {0};
-	bool cpu_found = false;
-
-	get_user(cpumask, &argp->cpumask);
-	get_user(cluster, &argp->cluster);
-
-	pr_debug("%s: cpumask %d, cluster: %d\n", __func__, cpumask,
-					cluster);
-	for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
-		if (!(cpumask & 0x01))
-			continue;
-
-		for_each_possible_cpu(cpu) {
-			if ((cpu_topology[cpu].core_id != i) ||
-				(cpu_topology[cpu].cluster_id != cluster))
-				continue;
-
-			cpu_found = true;
-			break;
-		}
-		if (cpu_found)
-			break;
-	}
-
-	if ((cpu < 0) || (cpu >= num_possible_cpus()))
-		return -EINVAL;
-
-	node = &activity[cpu];
-	/* Allocate new memory to copy cpumask specific power
-	 * information.
-	 */
-	sp = kzalloc(sizeof(*sp), GFP_KERNEL);
-	if (!sp)
-		return -ENOMEM;
-
-
-	sp->power = allocate_2d_array_uint32_t(node->sp->num_of_freqs);
-	if (IS_ERR_OR_NULL(sp->power)) {
-		ret = PTR_ERR(sp->power);
-		kfree(sp);
-		return ret;
-	}
-	sp->num_of_freqs = node->sp->num_of_freqs;
-	sp->voltage = node->sp->voltage;
-	sp->table = node->sp->table;
-
-	for (i = 0; i < TEMP_DATA_POINTS; i++) {
-		ret = copy_from_user(sp->power[i], &argp->power[i][0],
-			sizeof(sp->power[i][0]) * node->sp->num_of_freqs);
-		if (ret)
-			goto failed;
-	}
-
-	/* Copy the same power values for all the cpus in the cpumask
-	 * argp->cpumask within the cluster (argp->cluster)
-	 */
-	get_user(cpumask, &argp->cpumask);
-	spin_lock(&update_lock);
-	for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
-		if (!(cpumask & 0x01))
-			continue;
-		for_each_possible_cpu(cpu) {
-			if (((cpu_topology[cpu].core_id != i) ||
-				(cpu_topology[cpu].cluster_id != cluster)))
-				continue;
-
-			node = &activity[cpu];
-			clear_sp = node->sp;
-			node->sp = sp;
-			cpumask_set_cpu(cpu, &sp->mask);
-			if (clear_sp) {
-				cpumask_clear_cpu(cpu, &clear_sp->mask);
-				clear_static_power(clear_sp);
-			}
-			cpu_stats[cpu].ptable = per_cpu(ptable, cpu);
-			repopulate_stats(cpu);
-			pdata_valid[cpu] = true;
-		}
-	}
-	spin_unlock(&update_lock);
-
-	for_each_possible_cpu(cpu) {
-		if (!pdata_valid[cpu])
-			continue;
-
-		blocking_notifier_call_chain(
-			&msm_core_stats_notifier_list, cpu, NULL);
-	}
-
-	activate_power_table = true;
-	return 0;
-
-failed:
-	for (i = 0; i < TEMP_DATA_POINTS; i++)
-		kfree(sp->power[i]);
-	kfree(sp->power);
-	kfree(sp);
-	return ret;
-}
-
-static long msm_core_ioctl(struct file *file, unsigned int cmd,
-		unsigned long arg)
-{
-	long ret = 0;
-	struct cpu_activity_info *node = NULL;
-	struct sched_params __user *argp = (struct sched_params __user *)arg;
-	int i, cpu = num_possible_cpus();
-	int cluster, cpumask;
-	bool cpu_found = false;
-
-	if (!argp)
-		return -EINVAL;
-
-	get_user(cluster, &argp->cluster);
-	get_user(cpumask, &argp->cpumask);
-
-	switch (cmd) {
-	case EA_LEAKAGE:
-		ret = update_userspace_power(argp);
-		if (ret)
-			pr_err("Userspace power update failed with %ld\n", ret);
-		break;
-	case EA_VOLT:
-		for (i = 0; cpumask > 0; i++, cpumask >>= 1) {
-			for_each_possible_cpu(cpu) {
-				if (((cpu_topology[cpu].core_id != i) ||
-				(cpu_topology[cpu].cluster_id != cluster)))
-					continue;
-
-				cpu_found = true;
-				break;
-			}
-			if (cpu_found)
-				break;
-		}
-		if (cpu >= num_possible_cpus())
-			break;
-
-		mutex_lock(&policy_update_mutex);
-		node = &activity[cpu];
-		if (!node->sp->table) {
-			ret = -EINVAL;
-			goto unlock;
-		}
-		ret = copy_to_user((void __user *)&argp->voltage[0],
-				node->sp->voltage,
-				sizeof(uint32_t) * node->sp->num_of_freqs);
-		if (ret)
-			break;
-		for (i = 0; i < node->sp->num_of_freqs; i++) {
-			ret = copy_to_user((void __user *)&argp->freq[i],
-					&node->sp->table[i].frequency,
-					sizeof(uint32_t));
-			if (ret)
-				break;
-		}
-unlock:
-		mutex_unlock(&policy_update_mutex);
-		break;
-	default:
-		break;
-	}
-
-	return ret;
-}
-
-#ifdef CONFIG_COMPAT
-static long msm_core_compat_ioctl(struct file *file, unsigned int cmd,
-		unsigned long arg)
-{
-	arg = (unsigned long)compat_ptr(arg);
-	return msm_core_ioctl(file, cmd, arg);
-}
-#endif
-
-static int msm_core_open(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int msm_core_release(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int msm_core_stats_init(struct device *dev, int cpu)
-{
-	int i;
-	struct cpu_activity_info *cpu_node;
-	struct cpu_pstate_pwr *pstate = NULL;
-
-	cpu_node = &activity[cpu];
-	cpu_stats[cpu].cpu = cpu;
-	cpu_stats[cpu].temp = cpu_node->temp;
-	cpu_stats[cpu].throttling = false;
-
-	cpu_stats[cpu].len = cpu_node->sp->num_of_freqs;
-	pstate = devm_kzalloc(dev,
-		sizeof(*pstate) * cpu_node->sp->num_of_freqs,
-		GFP_KERNEL);
-	if (!pstate)
-		return -ENOMEM;
-
-	for (i = 0; i < cpu_node->sp->num_of_freqs; i++)
-		pstate[i].freq = cpu_node->sp->table[i].frequency;
-
-	per_cpu(ptable, cpu) = pstate;
-
-	return 0;
-}
-
-static int msm_core_task_init(struct device *dev)
-{
-	init_completion(&sampling_completion);
-	sampling_task = kthread_run(do_sampling, NULL, "msm-core:sampling");
-	if (IS_ERR(sampling_task)) {
-		pr_err("Failed to create do_sampling err: %ld\n",
-				PTR_ERR(sampling_task));
-		return PTR_ERR(sampling_task);
-	}
-	return 0;
-}
-
-struct cpu_pwr_stats *get_cpu_pwr_stats(void)
-{
-	return cpu_stats;
-}
-EXPORT_SYMBOL(get_cpu_pwr_stats);
-
-static int msm_get_power_values(int cpu, struct cpu_static_info *sp)
-{
-	int i = 0, j;
-	int ret = 0;
-	uint64_t power;
-
-	/* Calculate dynamic power spent for every frequency using formula:
-	 * Power = V * V * f
-	 * where V = voltage for frequency
-	 *       f = frequency
-	 */
-	sp->power = allocate_2d_array_uint32_t(sp->num_of_freqs);
-	if (IS_ERR_OR_NULL(sp->power))
-		return PTR_ERR(sp->power);
-
-	for (i = 0; i < TEMP_DATA_POINTS; i++) {
-		for (j = 0; j < sp->num_of_freqs; j++) {
-			power = sp->voltage[j] *
-						sp->table[j].frequency;
-			do_div(power, 1000);
-			do_div(power, 1000);
-			power *= sp->voltage[j];
-			do_div(power, 1000);
-			sp->power[i][j] = power;
-		}
-	}
-	return ret;
-}
-
-static int msm_get_voltage_levels(struct device *dev, int cpu,
-		struct cpu_static_info *sp)
-{
-	unsigned int *voltage;
-	int i;
-	int corner;
-	struct dev_pm_opp *opp;
-	struct device *cpu_dev = get_cpu_device(cpu);
-	/*
-	 * Convert cpr corner voltage to average voltage of both
-	 * a53 and a57 votlage value
-	 */
-	int average_voltage[NUM_OF_CORNERS] = {0, 746, 841, 843, 940, 953, 976,
-			1024, 1090, 1100};
-
-	if (!cpu_dev)
-		return -ENODEV;
-
-	voltage = devm_kzalloc(dev,
-			sizeof(*voltage) * sp->num_of_freqs, GFP_KERNEL);
-
-	if (!voltage)
-		return -ENOMEM;
-
-	rcu_read_lock();
-	for (i = 0; i < sp->num_of_freqs; i++) {
-		opp = dev_pm_opp_find_freq_exact(cpu_dev,
-				sp->table[i].frequency * 1000, true);
-		corner = dev_pm_opp_get_voltage(opp);
-
-		if (corner > 400000)
-			voltage[i] = corner / 1000;
-		else if (corner > 0 && corner < ARRAY_SIZE(average_voltage))
-			voltage[i] = average_voltage[corner];
-		else
-			voltage[i]
-			     = average_voltage[ARRAY_SIZE(average_voltage) - 1];
-	}
-	rcu_read_unlock();
-
-	sp->voltage = voltage;
-	return 0;
-}
-
-static int msm_core_dyn_pwr_init(struct platform_device *pdev,
-				int cpu)
-{
-	int ret = 0;
-
-	if (!activity[cpu].sp->table)
-		return 0;
-
-	ret = msm_get_voltage_levels(&pdev->dev, cpu, activity[cpu].sp);
-	if (ret)
-		return ret;
-
-	ret = msm_get_power_values(cpu, activity[cpu].sp);
-
-	return ret;
-}
-
-static int msm_core_mpidr_init(struct device_node *phandle)
-{
-	int ret = 0;
-	char *key = NULL;
-	int mpidr;
-
-	key = "reg";
-	ret = of_property_read_u32(phandle, key,
-				&mpidr);
-	if (ret) {
-		pr_err("%s: Cannot read mpidr\n", __func__);
-		return ret;
-	}
-	return mpidr;
-}
-
-static int msm_core_cpu_policy_handler(struct notifier_block *nb,
-		unsigned long val, void *data)
-{
-	struct cpufreq_policy *policy = data;
-	struct cpu_activity_info *cpu_info = &activity[policy->cpu];
-	int cpu;
-	int ret;
-
-	if (cpu_info->sp->table)
-		return NOTIFY_OK;
-
-	switch (val) {
-	case CPUFREQ_CREATE_POLICY:
-		mutex_lock(&policy_update_mutex);
-		update_related_freq_table(policy);
-
-		for_each_cpu(cpu, policy->related_cpus) {
-			ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
-			if (ret)
-				pr_debug("voltage-pwr table update failed\n");
-
-			ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
-			if (ret)
-				pr_debug("Stats table update failed\n");
-		}
-		mutex_unlock(&policy_update_mutex);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-struct notifier_block cpu_policy = {
-	.notifier_call = msm_core_cpu_policy_handler
-};
-
-static int system_suspend_handler(struct notifier_block *nb,
-				unsigned long val, void *data)
-{
-	int cpu;
-
-	mutex_lock(&kthread_update_mutex);
-	switch (val) {
-	case PM_POST_HIBERNATION:
-	case PM_POST_SUSPEND:
-	case PM_POST_RESTORE:
-		/*
-		 * Set completion event to read temperature and repopulate
-		 * stats
-		 */
-		in_suspend = 0;
-		complete(&sampling_completion);
-		break;
-	case PM_HIBERNATION_PREPARE:
-	case PM_SUSPEND_PREPARE:
-		/*
-		 * cancel delayed work to be able to restart immediately
-		 * after system resume
-		 */
-		in_suspend = 1;
-		cancel_delayed_work(&sampling_work);
-		/*
-		 * cancel TSENS interrupts as we do not want to wake up from
-		 * suspend to take care of repopulate stats while the system is
-		 * in suspend
-		 */
-		for_each_possible_cpu(cpu) {
-			if (activity[cpu].sensor_id < 0)
-				continue;
-		}
-		break;
-	default:
-		break;
-	}
-	mutex_unlock(&kthread_update_mutex);
-
-	return NOTIFY_OK;
-}
-
-static int msm_core_freq_init(void)
-{
-	int cpu;
-	struct cpufreq_policy *policy;
-
-	for_each_possible_cpu(cpu) {
-		activity[cpu].sp = kzalloc(sizeof(*(activity[cpu].sp)),
-				GFP_KERNEL);
-		if (!activity[cpu].sp)
-			return -ENOMEM;
-	}
-
-	for_each_online_cpu(cpu) {
-		if (activity[cpu].sp->table)
-			continue;
-
-		policy = cpufreq_cpu_get(cpu);
-		if (!policy)
-			continue;
-
-		update_related_freq_table(policy);
-		cpufreq_cpu_put(policy);
-	}
-
-	return 0;
-}
-
-static int msm_core_params_init(struct platform_device *pdev)
-{
-	int ret = 0;
-	unsigned long cpu = 0;
-	struct device_node *child_node = NULL;
-	int mpidr;
-
-	for_each_possible_cpu(cpu) {
-		child_node = of_get_cpu_node(cpu, NULL);
-
-		if (!child_node)
-			continue;
-
-		mpidr = msm_core_mpidr_init(child_node);
-		if (mpidr < 0)
-			return mpidr;
-
-		activity[cpu].mpidr = mpidr;
-
-		if (!activity[cpu].sp->table)
-			continue;
-
-		ret = msm_core_dyn_pwr_init(msm_core_pdev, cpu);
-		if (ret)
-			pr_debug("voltage-pwr table update failed\n");
-
-		ret = msm_core_stats_init(&msm_core_pdev->dev, cpu);
-		if (ret)
-			pr_debug("Stats table update failed\n");
-	}
-
-	return 0;
-}
-
-static const struct file_operations msm_core_ops = {
-	.owner = THIS_MODULE,
-	.unlocked_ioctl = msm_core_ioctl,
-#ifdef CONFIG_COMPAT
-	.compat_ioctl = msm_core_compat_ioctl,
-#endif
-	.open = msm_core_open,
-	.release = msm_core_release,
-};
-
-static struct miscdevice msm_core_device = {
-	.minor = MISC_DYNAMIC_MINOR,
-	.name = "pta",
-	.fops = &msm_core_ops
-};
-
-static void free_dyn_memory(void)
-{
-	int i, cpu;
-
-	for_each_possible_cpu(cpu) {
-		if (activity[cpu].sp) {
-			for (i = 0; i < TEMP_DATA_POINTS; i++) {
-				if (!activity[cpu].sp->power)
-					break;
-
-				kfree(activity[cpu].sp->power[i]);
-			}
-		}
-		kfree(activity[cpu].sp);
-	}
-}
-
-static int msm_core_dev_probe(struct platform_device *pdev)
-{
-	int ret = 0;
-	char *key = NULL;
-	struct device_node *node;
-	struct uio_info *info;
-
-	if (!pdev)
-		return -ENODEV;
-
-	msm_core_pdev = pdev;
-	node = pdev->dev.of_node;
-	if (!node)
-		return -ENODEV;
-
-	key = "qcom,low-hyst-temp";
-	ret = of_property_read_u32(node, key, &low_hyst_temp);
-	if (ret)
-		low_hyst_temp = DEFAULT_LOW_HYST_TEMP;
-
-	key = "qcom,high-hyst-temp";
-	ret = of_property_read_u32(node, key, &high_hyst_temp);
-	if (ret)
-		high_hyst_temp = DEFAULT_HIGH_HYST_TEMP;
-
-	key = "qcom,polling-interval";
-	ret = of_property_read_u32(node, key, &poll_ms);
-	if (ret)
-		pr_info("msm-core initialized without polling period\n");
-
-	key = "qcom,throttling-temp";
-	ret = of_property_read_u32(node, key, &max_throttling_temp);
-
-	ret = msm_core_freq_init();
-	if (ret)
-		goto failed;
-
-	ret = misc_register(&msm_core_device);
-	if (ret) {
-		pr_err("%s: Error registering device %d\n", __func__, ret);
-		goto failed;
-	}
-
-	ret = msm_core_params_init(pdev);
-	if (ret)
-		goto failed;
-
-	ret = msm_core_task_init(&pdev->dev);
-	if (ret)
-		goto failed;
-
-	INIT_DEFERRABLE_WORK(&sampling_work, samplequeue_handle);
-	schedule_delayed_work(&sampling_work, msecs_to_jiffies(0));
-	cpufreq_register_notifier(&cpu_policy, CPUFREQ_POLICY_NOTIFIER);
-	pm_notifier(system_suspend_handler, 0);
-	return 0;
-failed:
-	info = dev_get_drvdata(&pdev->dev);
-	uio_unregister_device(info);
-	free_dyn_memory();
-	return ret;
-}
-
-static int msm_core_remove(struct platform_device *pdev)
-{
-	int cpu;
-	struct uio_info *info = dev_get_drvdata(&pdev->dev);
-
-	uio_unregister_device(info);
-
-	for_each_possible_cpu(cpu) {
-		if (activity[cpu].sensor_id < 0)
-			continue;
-	}
-	free_dyn_memory();
-	misc_deregister(&msm_core_device);
-	return 0;
-}
-
-static const struct of_device_id msm_core_match_table[] = {
-	{.compatible = "qcom,apss-core-ea"},
-	{},
-};
-
-static struct platform_driver msm_core_driver = {
-	.probe = msm_core_dev_probe,
-	.driver = {
-		.name = "msm_core",
-		.owner = THIS_MODULE,
-		.of_match_table = msm_core_match_table,
-		},
-	.remove = msm_core_remove,
-};
-
-static int __init msm_core_init(void)
-{
-	return platform_driver_register(&msm_core_driver);
-}
-late_initcall(msm_core_init);
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
index a40efcb..437984c 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_arb_rpmh.c
@@ -1294,7 +1294,7 @@
 		src = pdata->usecase->vectors[i].src;
 		dest = pdata->usecase->vectors[i].dst;
 
-		if ((src < 0) || (dest < 0)) {
+		if ((src < 0) || (dest < 0) || (src == dest)) {
 			MSM_BUS_ERR("%s:Invalid src/dst.src %d dest %d",
 				__func__, src, dest);
 			goto exit_invalid_data;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_bimc_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_bimc_rpmh.c
index c1e8feb..dafae4c 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_bimc_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_bimc_rpmh.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -242,7 +242,7 @@
 	(M_BKE_GC_GC_BMSK >> \
 	(M_BKE_GC_GC_SHFT + 1))
 
-static int bimc_div(int64_t *a, uint32_t b)
+static int bimc_div(uint64_t *a, uint32_t b)
 {
 	if ((*a > 0) && (*a < b)) {
 		*a = 0;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
index ea01076..5a110bb 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_fabric_rpmh.c
@@ -563,12 +563,14 @@
 		return ret;
 
 	list_for_each_entry_safe(node, node_tmp, clist, link) {
-		if (unlikely(node->node_info->defer_qos))
-			msm_bus_dev_init_qos(&node->dev, NULL);
-
 		bcm_clist_add(node);
 	}
 
+	if (!cur_rsc) {
+		MSM_BUS_ERR("%s: Error for cur_rsc is NULL.\n", __func__);
+		return ret;
+	}
+
 	cur_mbox = cur_rsc->rscdev->mbox;
 	cur_bcm_clist = cur_rsc->rscdev->bcm_clist;
 
@@ -668,6 +670,12 @@
 	kfree(cmdlist_active);
 	kfree(n_active);
 
+
+	list_for_each_entry_safe(node, node_tmp, clist, link) {
+		if (unlikely(node->node_info->defer_qos))
+			msm_bus_dev_init_qos(&node->dev, NULL);
+	}
+
 exit_msm_bus_commit_data:
 	list_for_each_entry_safe(node, node_tmp, clist, link) {
 		bcm_clist_clean(node);
@@ -962,6 +970,12 @@
 
 	MSM_BUS_DBG("Device = %d", node_dev->node_info->id);
 
+	if (node_dev->node_info->qos_params.defer_init_qos) {
+		node_dev->node_info->qos_params.defer_init_qos = false;
+		node_dev->node_info->defer_qos = true;
+		goto exit_init_qos;
+	}
+
 	if (node_dev->ap_owned) {
 		struct msm_bus_node_device_type *bus_node_info;
 
@@ -995,6 +1009,7 @@
 
 				bus_node_info->fabdev->noc_ops.qos_init(
 					node_dev,
+					bus_node_info,
 					bus_node_info->fabdev->qos_base,
 					bus_node_info->fabdev->base_offset,
 					bus_node_info->fabdev->qos_off,
@@ -1290,6 +1305,8 @@
 				pdata_node_info->qos_params.reg_mode.write;
 	node_info->qos_params.urg_fwd_en =
 				pdata_node_info->qos_params.urg_fwd_en;
+	node_info->qos_params.defer_init_qos =
+				pdata_node_info->qos_params.defer_init_qos;
 	node_info->agg_params.buswidth = pdata_node_info->agg_params.buswidth;
 	node_info->agg_params.agg_scheme =
 					pdata_node_info->agg_params.agg_scheme;
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
index 996c719..282f2ac 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
@@ -15,10 +15,14 @@
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/msm-bus-board.h>
+#include <linux/msm-bus.h>
+#include <linux/spinlock.h>
 #include "msm_bus_core.h"
 #include "msm_bus_noc.h"
 #include "msm_bus_rpmh.h"
 
+static DEFINE_SPINLOCK(noc_lock);
+
 /* NOC_QOS generic */
 #define __CLZ(x) ((8 * sizeof(uint32_t)) - 1 - __fls(x))
 #define SAT_SCALE 16	/* 16 bytes minimum for saturation */
@@ -29,6 +33,8 @@
 #define MAX_SAT_FIELD (NOC_QOS_SATn_SAT_BMSK >> NOC_QOS_SATn_SAT_SHFT)
 #define MIN_SAT_FIELD	1
 #define MIN_BW_FIELD	1
+#define QM_BASE	0x010B8000
+#define MSM_BUS_FAB_MEM_NOC 6152
 
 #define NOC_QOS_REG_BASE(b, o)		((b) + (o))
 
@@ -102,6 +108,18 @@
 	NOC_QOS_SATn_SAT_SHFT		= 0x0,
 };
 
+#define QM_CLn_TH_LVL_MUX_ADDR(b, o, n, d)	\
+	(NOC_QOS_REG_BASE(b, o) + 0x8C0 + (d) * (n))
+enum qm_cl_id_th_lvl_mux_cfg {
+	QM_CLn_TH_LVL_SW_OVERRD_BMSK	= 0x80000000,
+	QM_CLn_TH_LVL_SW_OVERRD_SHFT	= 0x1F,
+	QM_CLn_TH_LVL_SW_BMSK		= 0x00000007,
+	QM_CLn_TH_LVL_SW_SHFT		= 0x0,
+};
+
+static void __iomem *qm_base;
+static void __iomem *memnoc_qos_base;
+
 static int noc_div(uint64_t *a, uint32_t b)
 {
 	if ((*a > 0) && (*a < b)) {
@@ -152,6 +170,18 @@
 }
 #define MAX_WS(bw, timebase) noc_ws((bw), MAX_SAT_FIELD, (timebase))
 
+static void noc_set_qm_th_lvl_cfg(void __iomem *base, uint32_t off,
+		uint32_t n, uint32_t delta,
+		uint32_t override_val, uint32_t override)
+{
+	writel_relaxed(((override << QM_CLn_TH_LVL_SW_OVERRD_SHFT) |
+		(override_val & QM_CLn_TH_LVL_SW_BMSK)),
+		QM_CLn_TH_LVL_MUX_ADDR(base, off, n, delta));
+
+	/* Ensure QM CFG is set before exiting */
+	wmb();
+}
+
 static void noc_set_qos_dflt_prio(void __iomem *base, uint32_t qos_off,
 		uint32_t mport, uint32_t qos_delta,
 		uint32_t prio)
@@ -169,38 +199,10 @@
 	wmb();
 }
 
-static void noc_set_qos_limiter(void __iomem *base, uint32_t qos_off,
-		uint32_t mport, uint32_t qos_delta,
-		struct msm_bus_noc_limiter *lim, uint32_t lim_en)
+static void noc_enable_qos_limiter(void __iomem *base, uint32_t qos_off,
+		uint32_t mport, uint32_t qos_delta, uint32_t lim_en)
 {
 	uint32_t reg_val, val;
-
-	reg_val = readl_relaxed(NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport,
-		qos_delta));
-
-	writel_relaxed((reg_val & (~(NOC_QOS_MCTL_LIMIT_ENn_BMSK))),
-		NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, qos_delta));
-
-	/* Ensure we disable limiter before config*/
-	wmb();
-
-	reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport,
-		qos_delta));
-	val = lim->bw << NOC_QOS_LIMITBW_BWn_SHFT;
-	writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_BWn_BMSK))) |
-		(val & NOC_QOS_LIMITBW_BWn_BMSK)),
-		NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta));
-
-	reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport,
-		qos_delta));
-	val = lim->sat << NOC_QOS_LIMITBW_SATn_SHFT;
-	writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_SATn_BMSK))) |
-		(val & NOC_QOS_LIMITBW_SATn_BMSK)),
-		NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta));
-
-	/* Ensure qos limiter settings in place before possibly enabling */
-	wmb();
-
 	reg_val = readl_relaxed(NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport,
 		qos_delta));
 	val = lim_en << NOC_QOS_MCTL_LIMIT_ENn_SHFT;
@@ -208,9 +210,50 @@
 		(val & NOC_QOS_MCTL_LIMIT_ENn_BMSK)),
 		NOC_QOS_MAINCTL_LOWn_ADDR(base, qos_off, mport, qos_delta));
 
+	/* Ensure we disable/enable limiter before exiting*/
 	wmb();
 }
 
+static void noc_set_qos_limit_bw(void __iomem *base, uint32_t qos_off,
+		uint32_t mport, uint32_t qos_delta, uint32_t bw)
+{
+	uint32_t reg_val, val;
+	reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport,
+		qos_delta));
+	val = bw << NOC_QOS_LIMITBW_BWn_SHFT;
+	writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_BWn_BMSK))) |
+		(val & NOC_QOS_LIMITBW_BWn_BMSK)),
+		NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta));
+
+	/* Ensure we set limiter bw before exiting*/
+	wmb();
+}
+
+static void noc_set_qos_limit_sat(void __iomem *base, uint32_t qos_off,
+		uint32_t mport, uint32_t qos_delta, uint32_t sat)
+{
+	uint32_t reg_val, val;
+	reg_val = readl_relaxed(NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport,
+		qos_delta));
+	val = sat << NOC_QOS_LIMITBW_SATn_SHFT;
+	writel_relaxed(((reg_val & (~(NOC_QOS_LIMITBW_SATn_BMSK))) |
+		(val & NOC_QOS_LIMITBW_SATn_BMSK)),
+		NOC_QOS_LIMITBWn_ADDR(base, qos_off, mport, qos_delta));
+
+	/* Ensure we set limiter sat before exiting*/
+	wmb();
+}
+
+static void noc_set_qos_limiter(void __iomem *base, uint32_t qos_off,
+		uint32_t mport, uint32_t qos_delta,
+		struct msm_bus_noc_limiter *lim, uint32_t lim_en)
+{
+	noc_enable_qos_limiter(base, qos_off, mport, qos_delta, 0);
+	noc_set_qos_limit_bw(base, qos_off, mport, qos_delta, lim->bw);
+	noc_set_qos_limit_sat(base, qos_off, mport, qos_delta, lim->sat);
+	noc_enable_qos_limiter(base, qos_off, mport, qos_delta, lim_en);
+}
+
 static void noc_set_qos_regulator(void __iomem *base, uint32_t qos_off,
 		uint32_t mport, uint32_t qos_delta,
 		struct msm_bus_noc_regulator *reg,
@@ -317,6 +360,7 @@
 }
 
 static int msm_bus_noc_qos_init(struct msm_bus_node_device_type *info,
+				struct msm_bus_node_device_type *fabdev,
 				void __iomem *qos_base,
 				uint32_t qos_off, uint32_t qos_delta,
 				uint32_t qos_freq)
@@ -324,6 +368,7 @@
 	struct msm_bus_noc_qos_params *qos_params;
 	int ret = 0;
 	int i;
+	unsigned long flags;
 
 	qos_params = &info->node_info->qos_params;
 
@@ -333,6 +378,21 @@
 		goto err_qos_init;
 	}
 
+	if (!qm_base) {
+		qm_base = ioremap_nocache(QM_BASE, 0x4000);
+		if (!qm_base) {
+			MSM_BUS_ERR("%s: Error remapping address 0x%zx",
+				__func__, (size_t)QM_BASE);
+			ret = -ENOMEM;
+			goto err_qos_init;
+		}
+	}
+
+	spin_lock_irqsave(&noc_lock, flags);
+
+	if (fabdev->node_info->id == MSM_BUS_FAB_MEM_NOC)
+		memnoc_qos_base = qos_base;
+
 	for (i = 0; i < info->node_info->num_qports; i++) {
 		noc_set_qos_dflt_prio(qos_base, qos_off,
 					info->node_info->qport[i],
@@ -356,10 +416,59 @@
 					qos_delta,
 					qos_params->urg_fwd_en);
 	}
+	spin_unlock_irqrestore(&noc_lock, flags);
+
 err_qos_init:
 	return ret;
 }
 
+int msm_bus_noc_throttle_wa(bool enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&noc_lock, flags);
+
+	if (!qm_base) {
+		MSM_BUS_ERR("QM CFG base address not found!");
+		goto noc_throttle_exit;
+	}
+
+	if (enable) {
+		noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0x3, 0x1);
+		noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0x3, 0x1);
+	} else {
+		noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 8, 0x4, 0, 0);
+		noc_set_qm_th_lvl_cfg(qm_base, 0x1000, 9, 0x4, 0, 0);
+	}
+
+noc_throttle_exit:
+	spin_unlock_irqrestore(&noc_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(msm_bus_noc_throttle_wa);
+
+int msm_bus_noc_priority_wa(bool enable)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&noc_lock, flags);
+	if (!memnoc_qos_base) {
+		MSM_BUS_ERR("Memnoc QoS Base address not found!");
+		goto noc_priority_exit;
+	}
+
+	if (enable)
+		noc_set_qos_dflt_prio(memnoc_qos_base, 0x10000, 0,
+								0x1000, 7);
+	else
+		noc_set_qos_dflt_prio(memnoc_qos_base, 0x10000, 0,
+								0x1000, 6);
+noc_priority_exit:
+	spin_unlock_irqrestore(&noc_lock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(msm_bus_noc_priority_wa);
+
 int msm_bus_noc_set_ops(struct msm_bus_node_device_type *bus_dev)
 {
 	if (!bus_dev)
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
index 77cbbf1..32b6adf 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_of_rpmh.c
@@ -241,6 +241,8 @@
 	node_info->qos_params.urg_fwd_en = of_property_read_bool(dev_node,
 						"qcom,forwarding");
 
+	node_info->qos_params.defer_init_qos = of_property_read_bool(dev_node,
+						"qcom,defer-init-qos");
 }
 
 static int msm_bus_of_parse_clk_array(struct device_node *dev_node,
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
index ad04fef..8929959 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rpmh.h
@@ -42,6 +42,7 @@
 /* New types introduced for adhoc topology */
 struct msm_bus_noc_ops {
 	int (*qos_init)(struct msm_bus_node_device_type *dev,
+			struct msm_bus_node_device_type *fabdev,
 			void __iomem *qos_base, uint32_t qos_off,
 			uint32_t qos_delta, uint32_t qos_freq);
 	int (*set_bw)(struct msm_bus_node_device_type *dev,
@@ -136,6 +137,7 @@
 	struct msm_bus_noc_regulator reg;
 	struct msm_bus_noc_regulator_mode reg_mode;
 	bool urg_fwd_en;
+	bool defer_init_qos;
 };
 
 struct node_util_levels_type {
diff --git a/drivers/soc/qcom/msm_bus/msm_bus_rules.c b/drivers/soc/qcom/msm_bus/msm_bus_rules.c
index 03042fa..4cff9f2 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_rules.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_rules.c
@@ -410,8 +410,10 @@
 {
 	struct rule_node_info *node_it = NULL;
 
+	mutex_lock(&msm_bus_rules_lock);
 	list_for_each_entry(node_it, &node_list, link)
 		print_rules(node_it);
+	mutex_unlock(&msm_bus_rules_lock);
 }
 
 void print_rules_buf(char *buf, int max_buf)
@@ -421,6 +423,7 @@
 	int i;
 	int cnt = 0;
 
+	mutex_lock(&msm_bus_rules_lock);
 	list_for_each_entry(node_it, &node_list, link) {
 		cnt += scnprintf(buf + cnt, max_buf - cnt,
 			"\n Now printing rules for Node %d cur_rule %d\n",
@@ -452,6 +455,7 @@
 					node_rule->rule_ops.mode);
 		}
 	}
+	mutex_unlock(&msm_bus_rules_lock);
 }
 
 static int copy_rule(struct bus_rule_type *src, struct rules_def *node_rule,
@@ -716,11 +720,12 @@
 {
 	bool ret = false;
 
+	mutex_lock(&msm_bus_rules_lock);
 	if (list_empty(&node_list))
 		ret = false;
 	else
 		ret = true;
-
+	mutex_unlock(&msm_bus_rules_lock);
 	return ret;
 }
 
diff --git a/drivers/soc/qcom/msm_glink_pkt.c b/drivers/soc/qcom/msm_glink_pkt.c
index 519bb611..25099bb 100644
--- a/drivers/soc/qcom/msm_glink_pkt.c
+++ b/drivers/soc/qcom/msm_glink_pkt.c
@@ -33,7 +33,8 @@
 #include <linux/termios.h>
 
 #include <soc/qcom/glink.h>
-
+/* This Limit ensures that auto queue will not exhaust memory on remote side */
+#define MAX_PENDING_GLINK_PKT 5
 #define MODULE_NAME "msm_glinkpkt"
 #define DEVICE_NAME "glinkpkt"
 #define WAKEUPSOURCE_TIMEOUT (2000) /* two seconds */
@@ -136,6 +137,7 @@
 	struct glink_link_info link_info;
 	void *link_state_handle;
 	bool link_up;
+	bool auto_intent_enabled;
 };
 
 /**
@@ -445,9 +447,26 @@
 bool glink_pkt_rmt_rx_intent_req_cb(void *handle, const void *priv, size_t sz)
 {
 	struct queue_rx_intent_work *work_item;
+	int pending_pkt_count = 0;
+	struct glink_rx_pkt *pkt = NULL;
+	unsigned long flags;
+	struct glink_pkt_dev *devp = (struct glink_pkt_dev *)priv;
 
 	GLINK_PKT_INFO("%s(): QUEUE RX INTENT to receive size[%zu]\n",
 		   __func__, sz);
+	if (devp->auto_intent_enabled) {
+		spin_lock_irqsave(&devp->pkt_list_lock, flags);
+		list_for_each_entry(pkt, &devp->pkt_list, list)
+			pending_pkt_count++;
+		spin_unlock_irqrestore(&devp->pkt_list_lock, flags);
+		if (pending_pkt_count > MAX_PENDING_GLINK_PKT) {
+			GLINK_PKT_ERR("%s failed, max limit reached\n",
+					__func__);
+			return false;
+		}
+	} else {
+		return false;
+	}
 
 	work_item = kzalloc(sizeof(*work_item), GFP_ATOMIC);
 	if (!work_item) {
@@ -456,7 +475,7 @@
 	}
 
 	work_item->intent_size = sz;
-	work_item->devp = (struct glink_pkt_dev *)priv;
+	work_item->devp = devp;
 	INIT_WORK(&work_item->work, glink_pkt_queue_rx_intent_worker);
 	queue_work(glink_pkt_wq, &work_item->work);
 
@@ -626,10 +645,11 @@
 	}
 
 	mutex_lock(&devp->ch_lock);
-	if (!glink_rx_intent_exists(devp->handle, count)) {
+	if (!glink_pkt_read_avail(devp) &&
+				!glink_rx_intent_exists(devp->handle, count)) {
 		ret  = glink_queue_rx_intent(devp->handle, devp, count);
 		if (ret) {
-			GLINK_PKT_ERR("%s: failed to queue_rx_intent ret[%d]\n",
+			GLINK_PKT_ERR("%s: failed to queue intent ret[%d]\n",
 					__func__, ret);
 			mutex_unlock(&devp->ch_lock);
 			return ret;
@@ -914,6 +934,7 @@
 	case GLINK_PKT_IOCTL_QUEUE_RX_INTENT:
 		ret = get_user(size, (uint32_t *)arg);
 		GLINK_PKT_INFO("%s: intent size[%d]\n", __func__, size);
+		devp->auto_intent_enabled = false;
 		ret  = glink_queue_rx_intent(devp->handle, devp, size);
 		if (ret) {
 			GLINK_PKT_ERR("%s: failed to QUEUE_RX_INTENT ret[%d]\n",
@@ -1182,6 +1203,7 @@
 				glink_pkt_link_state_cb;
 	devp->i = i;
 	devp->poll_mode = 0;
+	devp->auto_intent_enabled = true;
 	devp->ws_locked = 0;
 	devp->ch_state = GLINK_LOCAL_DISCONNECTED;
 	/* Default timeout for open wait is 120sec */
diff --git a/drivers/soc/qcom/msm_performance.c b/drivers/soc/qcom/msm_performance.c
index 979c628..b5ce753 100644
--- a/drivers/soc/qcom/msm_performance.c
+++ b/drivers/soc/qcom/msm_performance.c
@@ -25,7 +25,6 @@
 #include <linux/module.h>
 #include <linux/input.h>
 #include <linux/kthread.h>
-#include <soc/qcom/msm-core.h>
 
 static struct mutex managed_cpus_lock;
 
@@ -33,10 +32,6 @@
 static unsigned int num_clusters;
 struct cluster {
 	cpumask_var_t cpus;
-	/* Number of CPUs to maintain online */
-	int max_cpu_request;
-	/* To track CPUs that the module decides to offline */
-	cpumask_var_t offlined_cpus;
 	/* stats for load detection */
 	/* IO */
 	u64 last_io_check_ts;
@@ -84,8 +79,6 @@
 static struct cluster **managed_clusters;
 static bool clusters_inited;
 
-/* Work to evaluate the onlining/offlining CPUs */
-static struct delayed_work evaluate_hotplug_work;
 
 /* To handle cpufreq min/max request */
 struct cpu_status {
@@ -94,11 +87,8 @@
 };
 static DEFINE_PER_CPU(struct cpu_status, cpu_stats);
 
-static unsigned int num_online_managed(struct cpumask *mask);
 static int init_cluster_control(void);
-static int rm_high_pwr_cost_cpus(struct cluster *cl);
 static int init_events_group(void);
-static DEFINE_PER_CPU(unsigned int, cpu_power_cost);
 struct events {
 	spinlock_t cpu_hotplug_lock;
 	bool cpu_hotplug;
@@ -214,65 +204,6 @@
 };
 device_param_cb(num_clusters, &param_ops_num_clusters, NULL, 0644);
 
-static int set_max_cpus(const char *buf, const struct kernel_param *kp)
-{
-	unsigned int i, ntokens = 0;
-	const char *cp = buf;
-	int val;
-
-	if (!clusters_inited)
-		return -EINVAL;
-
-	while ((cp = strpbrk(cp + 1, ":")))
-		ntokens++;
-
-	if (ntokens != (num_clusters - 1))
-		return -EINVAL;
-
-	cp = buf;
-	for (i = 0; i < num_clusters; i++) {
-
-		if (sscanf(cp, "%d\n", &val) != 1)
-			return -EINVAL;
-		if (val > (int)cpumask_weight(managed_clusters[i]->cpus))
-			return -EINVAL;
-
-		managed_clusters[i]->max_cpu_request = val;
-
-		cp = strnchr(cp, strlen(cp), ':');
-		cp++;
-		trace_set_max_cpus(cpumask_bits(managed_clusters[i]->cpus)[0],
-								val);
-	}
-
-	schedule_delayed_work(&evaluate_hotplug_work, 0);
-
-	return 0;
-}
-
-static int get_max_cpus(char *buf, const struct kernel_param *kp)
-{
-	int i, cnt = 0;
-
-	if (!clusters_inited)
-		return cnt;
-
-	for (i = 0; i < num_clusters; i++)
-		cnt += snprintf(buf + cnt, PAGE_SIZE - cnt,
-				"%d:", managed_clusters[i]->max_cpu_request);
-	cnt--;
-	cnt += snprintf(buf + cnt, PAGE_SIZE - cnt, " ");
-	return cnt;
-}
-
-static const struct kernel_param_ops param_ops_max_cpus = {
-	.set = set_max_cpus,
-	.get = get_max_cpus,
-};
-
-#ifdef CONFIG_MSM_PERFORMANCE_HOTPLUG_ON
-device_param_cb(max_cpus, &param_ops_max_cpus, NULL, 0644);
-#endif
 
 static int set_managed_cpus(const char *buf, const struct kernel_param *kp)
 {
@@ -291,7 +222,6 @@
 		if (cpumask_empty(managed_clusters[i]->cpus)) {
 			mutex_lock(&managed_cpus_lock);
 			cpumask_copy(managed_clusters[i]->cpus, &tmp_mask);
-			cpumask_clear(managed_clusters[i]->offlined_cpus);
 			mutex_unlock(&managed_cpus_lock);
 			break;
 		}
@@ -337,53 +267,6 @@
 };
 device_param_cb(managed_cpus, &param_ops_managed_cpus, NULL, 0644);
 
-/* Read-only node: To display all the online managed CPUs */
-static int get_managed_online_cpus(char *buf, const struct kernel_param *kp)
-{
-	int i, cnt = 0, total_cnt = 0;
-	char tmp[MAX_LENGTH_CPU_STRING] = "";
-	struct cpumask tmp_mask;
-	struct cluster *i_cl;
-
-	if (!clusters_inited)
-		return cnt;
-
-	for (i = 0; i < num_clusters; i++) {
-		i_cl = managed_clusters[i];
-
-		cpumask_clear(&tmp_mask);
-		cpumask_complement(&tmp_mask, i_cl->offlined_cpus);
-		cpumask_and(&tmp_mask, i_cl->cpus, &tmp_mask);
-
-		cnt = cpumap_print_to_pagebuf(true, buf, &tmp_mask);
-		if ((i + 1) < num_clusters &&
-		    (total_cnt + cnt + 1) <= MAX_LENGTH_CPU_STRING) {
-			snprintf(tmp + total_cnt, cnt, "%s", buf);
-			tmp[cnt-1] = ':';
-			tmp[cnt] = '\0';
-			total_cnt += cnt;
-		} else if ((i + 1) == num_clusters &&
-			   (total_cnt + cnt) <= MAX_LENGTH_CPU_STRING) {
-			snprintf(tmp + total_cnt, cnt, "%s", buf);
-			total_cnt += cnt;
-		} else {
-			pr_err("invalid string for managed_cpu:%s%s\n", tmp,
-				buf);
-			break;
-		}
-	}
-	snprintf(buf, PAGE_SIZE, "%s", tmp);
-	return total_cnt;
-}
-
-static const struct kernel_param_ops param_ops_managed_online_cpus = {
-	.get = get_managed_online_cpus,
-};
-
-#ifdef CONFIG_MSM_PERFORMANCE_HOTPLUG_ON
-device_param_cb(managed_online_cpus, &param_ops_managed_online_cpus,
-							NULL, 0444);
-#endif
 /*
  * Userspace sends cpu#:min_freq_value to vote for min_freq_value as the new
  * scaling_min. To withdraw its vote it needs to enter cpu#:0
@@ -2274,15 +2157,6 @@
 };
 /*******************************sysfs ends************************************/
 
-static unsigned int num_online_managed(struct cpumask *mask)
-{
-	struct cpumask tmp_mask;
-
-	cpumask_clear(&tmp_mask);
-	cpumask_and(&tmp_mask, mask, cpu_online_mask);
-
-	return cpumask_weight(&tmp_mask);
-}
 
 static int perf_adjust_notify(struct notifier_block *nb, unsigned long val,
 							void *data)
@@ -2359,185 +2233,6 @@
 
 	return 0;
 }
-
-/*
- * Attempt to offline CPUs based on their power cost.
- * CPUs with higher power costs are offlined first.
- */
-static int __ref rm_high_pwr_cost_cpus(struct cluster *cl)
-{
-	unsigned int cpu, i;
-	struct cpu_pwr_stats *per_cpu_info = get_cpu_pwr_stats();
-	struct cpu_pstate_pwr *costs;
-	unsigned int *pcpu_pwr;
-	unsigned int max_cost_cpu, max_cost;
-	int any_cpu = -1;
-
-	if (!per_cpu_info)
-		return -EAGAIN;
-
-	for_each_cpu(cpu, cl->cpus) {
-		costs = per_cpu_info[cpu].ptable;
-		if (!costs || !costs[0].freq)
-			continue;
-
-		i = 1;
-		while (costs[i].freq)
-			i++;
-
-		pcpu_pwr = &per_cpu(cpu_power_cost, cpu);
-		*pcpu_pwr = costs[i - 1].power;
-		any_cpu = (int)cpu;
-		pr_debug("msm_perf: CPU:%d Power:%u\n", cpu, *pcpu_pwr);
-	}
-
-	if (any_cpu < 0)
-		return -EAGAIN;
-
-	for (i = 0; i < cpumask_weight(cl->cpus); i++) {
-		max_cost = 0;
-		max_cost_cpu = cpumask_first(cl->cpus);
-
-		for_each_cpu(cpu, cl->cpus) {
-			pcpu_pwr = &per_cpu(cpu_power_cost, cpu);
-			if (max_cost < *pcpu_pwr) {
-				max_cost = *pcpu_pwr;
-				max_cost_cpu = cpu;
-			}
-		}
-
-		if (!cpu_online(max_cost_cpu))
-			goto end;
-
-		pr_debug("msm_perf: Offlining CPU%d Power:%d\n", max_cost_cpu,
-								max_cost);
-		cpumask_set_cpu(max_cost_cpu, cl->offlined_cpus);
-		lock_device_hotplug();
-		if (device_offline(get_cpu_device(max_cost_cpu))) {
-			cpumask_clear_cpu(max_cost_cpu, cl->offlined_cpus);
-			pr_debug("msm_perf: Offlining CPU%d failed\n",
-								max_cost_cpu);
-		}
-		unlock_device_hotplug();
-
-end:
-		pcpu_pwr = &per_cpu(cpu_power_cost, max_cost_cpu);
-		*pcpu_pwr = 0;
-		if (num_online_managed(cl->cpus) <= cl->max_cpu_request)
-			break;
-	}
-
-	if (num_online_managed(cl->cpus) > cl->max_cpu_request)
-		return -EAGAIN;
-	else
-		return 0;
-}
-
-/*
- * try_hotplug tries to online/offline cores based on the current requirement.
- * It loops through the currently managed CPUs and tries to online/offline
- * them until the max_cpu_request criteria is met.
- */
-static void __ref try_hotplug(struct cluster *data)
-{
-	unsigned int i;
-
-	if (!clusters_inited)
-		return;
-
-	pr_debug("msm_perf: Trying hotplug...%d:%d\n",
-			num_online_managed(data->cpus),	num_online_cpus());
-
-	mutex_lock(&managed_cpus_lock);
-	if (num_online_managed(data->cpus) > data->max_cpu_request) {
-		if (!rm_high_pwr_cost_cpus(data)) {
-			mutex_unlock(&managed_cpus_lock);
-			return;
-		}
-
-		/*
-		 * If power aware offlining fails due to power cost info
-		 * being unavaiable fall back to original implementation
-		 */
-		for (i = num_present_cpus() - 1; i >= 0 &&
-						i < num_present_cpus(); i--) {
-			if (!cpumask_test_cpu(i, data->cpus) ||	!cpu_online(i))
-				continue;
-
-			pr_debug("msm_perf: Offlining CPU%d\n", i);
-			cpumask_set_cpu(i, data->offlined_cpus);
-			lock_device_hotplug();
-			if (device_offline(get_cpu_device(i))) {
-				cpumask_clear_cpu(i, data->offlined_cpus);
-				pr_debug("msm_perf: Offlining CPU%d failed\n",
-									i);
-				unlock_device_hotplug();
-				continue;
-			}
-			unlock_device_hotplug();
-			if (num_online_managed(data->cpus) <=
-							data->max_cpu_request)
-				break;
-		}
-	} else {
-		for_each_cpu(i, data->cpus) {
-			if (cpu_online(i))
-				continue;
-			pr_debug("msm_perf: Onlining CPU%d\n", i);
-			lock_device_hotplug();
-			if (device_online(get_cpu_device(i))) {
-				pr_debug("msm_perf: Onlining CPU%d failed\n",
-									i);
-				unlock_device_hotplug();
-				continue;
-			}
-			unlock_device_hotplug();
-			cpumask_clear_cpu(i, data->offlined_cpus);
-			if (num_online_managed(data->cpus) >=
-							data->max_cpu_request)
-				break;
-		}
-	}
-	mutex_unlock(&managed_cpus_lock);
-}
-
-static void __ref release_cluster_control(struct cpumask *off_cpus)
-{
-	int cpu;
-
-	for_each_cpu(cpu, off_cpus) {
-		pr_debug("msm_perf: Release CPU %d\n", cpu);
-		lock_device_hotplug();
-		if (!device_online(get_cpu_device(cpu)))
-			cpumask_clear_cpu(cpu, off_cpus);
-		unlock_device_hotplug();
-	}
-}
-
-/* Work to evaluate current online CPU status and hotplug CPUs as per need */
-static void check_cluster_status(struct work_struct *work)
-{
-	int i;
-	struct cluster *i_cl;
-
-	for (i = 0; i < num_clusters; i++) {
-		i_cl = managed_clusters[i];
-
-		if (cpumask_empty(i_cl->cpus))
-			continue;
-
-		if (i_cl->max_cpu_request < 0) {
-			if (!cpumask_empty(i_cl->offlined_cpus))
-				release_cluster_control(i_cl->offlined_cpus);
-			continue;
-		}
-
-		if (num_online_managed(i_cl->cpus) !=
-					i_cl->max_cpu_request)
-			try_hotplug(i_cl);
-	}
-}
-
 static int __ref msm_performance_cpu_callback(struct notifier_block *nfb,
 		unsigned long action, void *hcpu)
 {
@@ -2559,43 +2254,6 @@
 		}
 	}
 
-	if (i_cl == NULL)
-		return NOTIFY_OK;
-
-	if (action == CPU_UP_PREPARE || action == CPU_UP_PREPARE_FROZEN) {
-		/*
-		 * Prevent onlining of a managed CPU if max_cpu criteria is
-		 * already satisfied
-		 */
-		if (i_cl->offlined_cpus == NULL)
-			return NOTIFY_OK;
-		if (i_cl->max_cpu_request <=
-					num_online_managed(i_cl->cpus)) {
-			pr_debug("msm_perf: Prevent CPU%d onlining\n", cpu);
-			cpumask_set_cpu(cpu, i_cl->offlined_cpus);
-			return NOTIFY_BAD;
-		}
-		cpumask_clear_cpu(cpu, i_cl->offlined_cpus);
-
-	} else if (action == CPU_DEAD) {
-		if (i_cl->offlined_cpus == NULL)
-			return NOTIFY_OK;
-		if (cpumask_test_cpu(cpu, i_cl->offlined_cpus))
-			return NOTIFY_OK;
-		/*
-		 * Schedule a re-evaluation to check if any more CPUs can be
-		 * brought online to meet the max_cpu_request requirement. This
-		 * work is delayed to account for CPU hotplug latencies
-		 */
-		if (schedule_delayed_work(&evaluate_hotplug_work, 0)) {
-			trace_reevaluate_hotplug(cpumask_bits(i_cl->cpus)[0],
-							i_cl->max_cpu_request);
-			pr_debug("msm_perf: Re-evaluation scheduled %d\n", cpu);
-		} else {
-			pr_debug("msm_perf: Work scheduling failed %d\n", cpu);
-		}
-	}
-
 	return NOTIFY_OK;
 }
 
@@ -2626,13 +2284,7 @@
 			ret = -ENOMEM;
 			goto error;
 		}
-		if (!alloc_cpumask_var(&managed_clusters[i]->offlined_cpus,
-		     GFP_KERNEL)) {
-			ret = -ENOMEM;
-			goto error;
-		}
 
-		managed_clusters[i]->max_cpu_request = -1;
 		managed_clusters[i]->single_enter_load = DEF_SINGLE_ENT;
 		managed_clusters[i]->single_exit_load = DEF_SINGLE_EX;
 		managed_clusters[i]->single_enter_cycles
@@ -2669,7 +2321,6 @@
 			perf_cl_peak_mod_exit_timer;
 	}
 
-	INIT_DELAYED_WORK(&evaluate_hotplug_work, check_cluster_status);
 	mutex_init(&managed_cpus_lock);
 
 	ip_evts = kcalloc(1, sizeof(struct input_events), GFP_KERNEL);
@@ -2707,8 +2358,6 @@
 	for (i = 0; i < num_clusters; i++) {
 		if (!managed_clusters[i])
 			break;
-		if (managed_clusters[i]->offlined_cpus)
-			free_cpumask_var(managed_clusters[i]->offlined_cpus);
 		if (managed_clusters[i]->cpus)
 			free_cpumask_var(managed_clusters[i]->cpus);
 		kfree(managed_clusters[i]);
diff --git a/drivers/soc/qcom/peripheral-loader.c b/drivers/soc/qcom/peripheral-loader.c
index a443239..9d22925 100644
--- a/drivers/soc/qcom/peripheral-loader.c
+++ b/drivers/soc/qcom/peripheral-loader.c
@@ -56,6 +56,7 @@
 
 #define PIL_NUM_DESC		10
 #define NUM_OF_ENCRYPTED_KEY	3
+#define MAX_LEN 96
 static void __iomem *pil_info_base;
 static void __iomem *pil_minidump_base;
 
@@ -836,6 +837,23 @@
 	return 0;
 }
 
+static int pil_notify_aop(struct pil_desc *desc, char *status)
+{
+	struct qmp_pkt pkt;
+	char mbox_msg[MAX_LEN];
+
+	if (!desc->signal_aop)
+		return 0;
+
+	snprintf(mbox_msg, MAX_LEN,
+		"{class: image, res: load_state, name: %s, val: %s}",
+		desc->name, status);
+	pkt.size = MAX_LEN;
+	pkt.data = mbox_msg;
+
+	return mbox_send_message(desc->mbox, &pkt);
+}
+
 /* Synchronize request_firmware() with suspend */
 static DECLARE_RWSEM(pil_pm_rwsem);
 
@@ -857,6 +875,12 @@
 	bool mem_protect = false;
 	bool hyp_assign = false;
 
+	ret = pil_notify_aop(desc, "on");
+	if (ret < 0) {
+		pil_err(desc, "Failed to send ON message to AOP rc:%d\n", ret);
+		return ret;
+	}
+
 	if (desc->shutdown_fail)
 		pil_err(desc, "Subsystem shutdown failed previously!\n");
 
@@ -1015,6 +1039,7 @@
 			priv->region = NULL;
 		}
 		pil_release_mmap(desc);
+		pil_notify_aop(desc, "off");
 	}
 	return ret;
 }
@@ -1026,6 +1051,7 @@
  */
 void pil_shutdown(struct pil_desc *desc)
 {
+	int ret;
 	struct pil_priv *priv = desc->priv;
 
 	if (desc->ops->shutdown) {
@@ -1043,6 +1069,9 @@
 		pil_proxy_unvote(desc, 1);
 	else
 		flush_delayed_work(&priv->proxy);
+	ret = pil_notify_aop(desc, "off");
+	if (ret < 0)
+		pr_warn("pil: failed to send OFF message to AOP rc:%d\n", ret);
 	desc->modem_ssr = true;
 }
 EXPORT_SYMBOL(pil_shutdown);
diff --git a/drivers/soc/qcom/peripheral-loader.h b/drivers/soc/qcom/peripheral-loader.h
index daa4533..f09adf5 100644
--- a/drivers/soc/qcom/peripheral-loader.h
+++ b/drivers/soc/qcom/peripheral-loader.h
@@ -12,6 +12,9 @@
 #ifndef __MSM_PERIPHERAL_LOADER_H
 #define __MSM_PERIPHERAL_LOADER_H
 
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/qmp.h>
+
 struct device;
 struct module;
 struct pil_priv;
@@ -57,6 +60,9 @@
 	bool modem_ssr;
 	bool clear_fw_region;
 	u32 subsys_vmid;
+	bool signal_aop;
+	struct mbox_client cl;
+	struct mbox_chan *mbox;
 };
 
 /**
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index a3cf11d..a3eb551 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -77,9 +77,6 @@
 
 #define MSS_MAGIC			0XAABADEAD
 
-#define MSS_PDC_OFFSET			8
-#define MSS_PDC_MASK			BIT(MSS_PDC_OFFSET)
-
 /* Timeout value for MBA boot when minidump is enabled */
 #define MBA_ENCRYPTION_TIMEOUT	3000
 enum scm_cmd {
@@ -127,13 +124,6 @@
 	int ret = 0;
 	u32 regval;
 
-	if (drv->vreg) {
-		ret = regulator_enable(drv->vreg);
-		if (ret)
-			dev_err(drv->desc.dev, "Failed to enable modem regulator(rc:%d)\n",
-									ret);
-	}
-
 	if (drv->cxrail_bhs) {
 		regval = readl_relaxed(drv->cxrail_bhs);
 		regval |= EXTERNAL_BHS_ON;
@@ -156,9 +146,6 @@
 		writel_relaxed(regval, drv->cxrail_bhs);
 	}
 
-	if (drv->vreg)
-		return regulator_disable(drv->vreg);
-
 	return 0;
 }
 
@@ -214,13 +201,14 @@
 static void pil_mss_pdc_sync(struct q6v5_data *drv, bool pdc_sync)
 {
 	u32 val = 0;
+	u32 mss_pdc_mask = BIT(drv->mss_pdc_offset);
 
 	if (drv->pdc_sync) {
 		val = readl_relaxed(drv->pdc_sync);
 		if (pdc_sync)
-			val |= MSS_PDC_MASK;
+			val |= mss_pdc_mask;
 		else
-			val &= ~MSS_PDC_MASK;
+			val &= ~mss_pdc_mask;
 		writel_relaxed(val, drv->pdc_sync);
 		/* Ensure PDC is written before next write */
 		wmb();
@@ -269,7 +257,7 @@
 	return ret;
 }
 
-static int pil_mss_assert_resets(struct q6v5_data *drv)
+int pil_mss_assert_resets(struct q6v5_data *drv)
 {
 	int ret = 0;
 
@@ -280,7 +268,7 @@
 	return ret;
 }
 
-static int pil_mss_deassert_resets(struct q6v5_data *drv)
+int pil_mss_deassert_resets(struct q6v5_data *drv)
 {
 	int ret = 0;
 
@@ -458,7 +446,44 @@
 		return ret;
 	}
 
+	if (drv->vreg) {
+		ret = of_property_read_u32(pil->dev->of_node, "vdd_mss-uV",
+								&uv);
+		if (ret) {
+			dev_err(pil->dev,
+				"missing vdd_mss-uV property(rc:%d)\n", ret);
+			goto out;
+		}
+
+		ret = regulator_set_voltage(drv->vreg, uv,
+						INT_MAX);
+		if (ret) {
+			dev_err(pil->dev, "Failed to set vreg voltage(rc:%d)\n",
+									ret);
+			goto out;
+		}
+
+		ret = regulator_set_load(drv->vreg, 100000);
+		if (ret < 0) {
+			dev_err(pil->dev, "Failed to set vreg mode(rc:%d)\n",
+									ret);
+			goto out;
+		}
+		ret = regulator_enable(drv->vreg);
+		if (ret) {
+			dev_err(pil->dev, "Failed to enable vreg(rc:%d)\n",
+				ret);
+			regulator_set_voltage(drv->vreg, 0, INT_MAX);
+			goto out;
+		}
+	}
+
 	ret = pil_q6v5_make_proxy_votes(pil);
+	if (ret && drv->vreg) {
+		regulator_disable(drv->vreg);
+		regulator_set_voltage(drv->vreg, 0, INT_MAX);
+	}
+out:
 	if (ret) {
 		regulator_disable(drv->vreg_mx);
 		regulator_set_voltage(drv->vreg_mx, 0, INT_MAX);
@@ -474,6 +499,10 @@
 	pil_q6v5_remove_proxy_votes(pil);
 	regulator_disable(drv->vreg_mx);
 	regulator_set_voltage(drv->vreg_mx, 0, INT_MAX);
+	if (drv->vreg) {
+		regulator_disable(drv->vreg);
+		regulator_set_voltage(drv->vreg, 0, INT_MAX);
+	}
 }
 
 static int pil_mss_mem_setup(struct pil_desc *pil,
@@ -617,7 +646,7 @@
 	char *fw_name_p;
 	void *mba_dp_virt;
 	dma_addr_t mba_dp_phys, mba_dp_phys_end;
-	int ret, count;
+	int ret;
 	const u8 *data;
 	struct device *dma_dev = md->mba_mem_dev_fixed ?: &md->mba_mem_dev;
 
@@ -683,10 +712,9 @@
 					&mba_dp_phys, &mba_dp_phys_end);
 
 	/* Load the MBA image into memory */
-	count = fw->size;
-	if (count <= SZ_1M) {
+	if (fw->size <= SZ_1M) {
 		/* Ensures memcpy is done for max 1MB fw size */
-		memcpy(mba_dp_virt, data, count);
+		memcpy(mba_dp_virt, data, fw->size);
 	} else {
 		dev_err(pil->dev, "%s fw image loading into memory is failed due to fw size overflow\n",
 			__func__);
diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h
index 1789ba3..0f1e75b 100644
--- a/drivers/soc/qcom/pil-msa.h
+++ b/drivers/soc/qcom/pil-msa.h
@@ -17,8 +17,6 @@
 
 #include "peripheral-loader.h"
 
-#define VDD_MSS_UV	1000000
-
 struct modem_data {
 	struct q6v5_data *q6;
 	struct subsys_device *subsys;
@@ -46,4 +44,6 @@
 int pil_mss_shutdown(struct pil_desc *pil);
 int pil_mss_deinit_image(struct pil_desc *pil);
 int __pil_mss_deinit_image(struct pil_desc *pil, bool err_path);
+int pil_mss_assert_resets(struct q6v5_data *drv);
+int pil_mss_deassert_resets(struct q6v5_data *drv);
 #endif
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 7984dfe..0477064 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -35,7 +35,6 @@
 #include "pil-q6v5.h"
 #include "pil-msa.h"
 
-#define MAX_VDD_MSS_UV		1150000
 #define PROXY_TIMEOUT_MS	10000
 #define MAX_SSR_REASON_LEN	256U
 #define STOP_ACK_TIMEOUT_MS	1000
@@ -217,6 +216,22 @@
 	drv->subsys_desc.wdog_bite_handler = modem_wdog_bite_intr_handler;
 
 	drv->q6->desc.modem_ssr = false;
+	drv->q6->desc.signal_aop = of_property_read_bool(pdev->dev.of_node,
+						"qcom,signal-aop");
+	if (drv->q6->desc.signal_aop) {
+		drv->q6->desc.cl.dev = &pdev->dev;
+		drv->q6->desc.cl.tx_block = true;
+		drv->q6->desc.cl.tx_tout = 1000;
+		drv->q6->desc.cl.knows_txdone = false;
+		drv->q6->desc.mbox = mbox_request_channel(&drv->q6->desc.cl, 0);
+		if (IS_ERR(drv->q6->desc.mbox)) {
+			ret = PTR_ERR(drv->q6->desc.mbox);
+			dev_err(&pdev->dev, "Failed to get mailbox channel %pK %d\n",
+				drv->q6->desc.mbox, ret);
+			goto err_subsys;
+		}
+	}
+
 	drv->subsys = subsys_register(&drv->subsys_desc);
 	if (IS_ERR(drv->subsys)) {
 		ret = PTR_ERR(drv->subsys);
@@ -293,6 +308,13 @@
 	if (res) {
 		q6->pdc_sync = devm_ioremap(&pdev->dev,
 						res->start, resource_size(res));
+		if (of_property_read_u32(pdev->dev.of_node,
+			"qcom,mss_pdc_offset", &q6->mss_pdc_offset)) {
+			dev_err(&pdev->dev,
+				"Offset for MSS PDC not specified\n");
+			return -EINVAL;
+		}
+
 	}
 
 	q6->alt_reset = NULL;
@@ -309,19 +331,6 @@
 		q6->vreg = devm_regulator_get(&pdev->dev, "vdd_mss");
 		if (IS_ERR(q6->vreg))
 			return PTR_ERR(q6->vreg);
-
-		ret = regulator_set_voltage(q6->vreg, VDD_MSS_UV,
-						MAX_VDD_MSS_UV);
-		if (ret)
-			dev_err(&pdev->dev, "Failed to set vreg voltage(rc:%d)\n",
-									ret);
-
-		ret = regulator_set_load(q6->vreg, 100000);
-		if (ret < 0) {
-			dev_err(&pdev->dev, "Failed to set vreg mode(rc:%d)\n",
-									ret);
-			return ret;
-		}
 	}
 
 	q6->vreg_mx = devm_regulator_get(&pdev->dev, "vdd_mx");
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index 49dd0be..d23b050 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -23,6 +23,7 @@
 #include <trace/events/trace_msm_pil_event.h>
 
 #include "peripheral-loader.h"
+#include "pil-msa.h"
 #include "pil-q6v5.h"
 
 /* QDSP6SS Register Offsets */
@@ -83,10 +84,10 @@
 /* QDSP6v65 parameters */
 #define QDSP6SS_BOOT_CORE_START		(0x400)
 #define QDSP6SS_BOOT_CMD		(0x404)
-#define QDSP6SS_BOOT_STATUS		(0x408)
+#define MSS_STATUS			(0x40)
 #define QDSP6SS_SLEEP			(0x3C)
 #define SLEEP_CHECK_MAX_LOOPS		(200)
-#define BOOT_FSM_TIMEOUT		(10)
+#define BOOT_FSM_TIMEOUT		(10000)
 
 #define QDSP6SS_ACC_OVERRIDE_VAL	0x20
 
@@ -384,7 +385,7 @@
 {
 	struct q6v5_data *drv = container_of(pil, struct q6v5_data, desc);
 	u32 val, count;
-	unsigned long timeout;
+	int ret;
 
 	val = readl_relaxed(drv->reg_base + QDSP6SS_SLEEP);
 	val |= 0x1;
@@ -409,15 +410,19 @@
 	writel_relaxed(1, drv->reg_base + QDSP6SS_BOOT_CMD);
 
 	/* Wait for boot FSM to complete */
-	timeout = jiffies + usecs_to_jiffies(BOOT_FSM_TIMEOUT);
-	while (time_before(jiffies, timeout)) {
-		val = readl_relaxed(drv->reg_base + QDSP6SS_BOOT_STATUS);
-		if (val & BIT(0))
-			return 0;
+	ret = readl_poll_timeout(drv->rmb_base + MSS_STATUS, val,
+			(val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT);
+
+	if (ret) {
+		dev_err(drv->desc.dev, "Boot FSM failed to complete.\n");
+		/* Reset the modem so that boot FSM is in reset state */
+		pil_mss_assert_resets(drv);
+		/* Wait 6 32kHz sleep cycles for reset */
+		udelay(200);
+		pil_mss_deassert_resets(drv);
 	}
 
-	dev_err(drv->desc.dev, "Boot FSM failed to complete.\n");
-	return -ETIMEDOUT;
+	return ret;
 }
 
 static int __pil_q6v55_reset(struct pil_desc *pil)
diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h
index 9b4c811..4961b1f 100644
--- a/drivers/soc/qcom/pil-q6v5.h
+++ b/drivers/soc/qcom/pil-q6v5.h
@@ -74,6 +74,7 @@
 	bool restart_reg_sec;
 	bool override_acc;
 	int override_acc_1;
+	int mss_pdc_offset;
 	bool ahb_clk_vote;
 	bool mx_spike_wa;
 };
diff --git a/drivers/soc/qcom/qbt1000.c b/drivers/soc/qcom/qbt1000.c
index e4ada03..86f314a 100644
--- a/drivers/soc/qcom/qbt1000.c
+++ b/drivers/soc/qcom/qbt1000.c
@@ -150,18 +150,17 @@
 	uint32_t *rsp_len)
 {
 	/* 64 bytes alignment for QSEECOM */
-	*cmd_len = ALIGN(*cmd_len, 64);
-	*rsp_len = ALIGN(*rsp_len, 64);
+	uint64_t aligned_cmd_len = ALIGN((uint64_t)*cmd_len, 64);
+	uint64_t aligned_rsp_len = ALIGN((uint64_t)*rsp_len, 64);
 
-	if (((uint64_t)*rsp_len + (uint64_t)*cmd_len)
-			> (uint64_t)g_app_buf_size) {
-		pr_err("buffer too small to hold cmd=%d and rsp=%d\n",
-			*cmd_len, *rsp_len);
+	if ((aligned_rsp_len + aligned_cmd_len) > (uint64_t)g_app_buf_size)
 		return -ENOMEM;
-	}
 
 	*cmd = hdl->sbuf;
+	*cmd_len = aligned_cmd_len;
 	*rsp = hdl->sbuf + *cmd_len;
+	*rsp_len = aligned_rsp_len;
+
 	return 0;
 }
 
@@ -318,6 +317,12 @@
 
 	drvdata = file->private_data;
 
+	if (IS_ERR(priv_arg)) {
+		dev_err(drvdata->dev, "%s: invalid user space pointer %lu\n",
+			__func__, arg);
+		return -EINVAL;
+	}
+
 	mutex_lock(&drvdata->mutex);
 
 	pr_debug("qbt1000_ioctl %d\n", cmd);
@@ -362,6 +367,7 @@
 		}
 
 		pr_debug("app %s load before\n", app.name);
+		app.name[MAX_NAME_SIZE - 1] = '\0';
 
 		/* start the TZ app */
 		rc = qseecom_start_app(
@@ -375,7 +381,8 @@
 				pr_err("App %s failed to set bw\n", app.name);
 			}
 		} else {
-			pr_err("app %s failed to load\n", app.name);
+			dev_err(drvdata->dev, "%s: Fingerprint Trusted App failed to load\n",
+				__func__);
 			goto end;
 		}
 
diff --git a/drivers/soc/qcom/qdss_bridge.c b/drivers/soc/qcom/qdss_bridge.c
new file mode 100644
index 0000000..8668155
--- /dev/null
+++ b/drivers/soc/qcom/qdss_bridge.c
@@ -0,0 +1,463 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define KMSG_COMPONENT "QDSS diag bridge"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/msm_mhi.h>
+#include <linux/usb/usb_qdss.h>
+#include "qdss_bridge.h"
+
+#define MODULE_NAME "qdss_bridge"
+
+#define QDSS_BUF_SIZE		(16*1024)
+#define MHI_CLIENT_QDSS_IN	9
+
+/* Max number of objects needed */
+static int poolsize = 32;
+module_param(poolsize, int, 0644);
+
+/* Size of single buffer */
+static int itemsize = QDSS_BUF_SIZE;
+module_param(itemsize, int, 0644);
+
+static int qdss_destroy_buf_tbl(struct qdss_bridge_drvdata *drvdata)
+{
+	struct list_head *start, *temp;
+	struct qdss_buf_tbl_lst *entry = NULL;
+
+	list_for_each_safe(start, temp, &drvdata->buf_tbl) {
+		entry = list_entry(start, struct qdss_buf_tbl_lst, link);
+		list_del(&entry->link);
+		kfree(entry->buf);
+		kfree(entry->usb_req);
+		kfree(entry);
+	}
+
+	return 0;
+}
+
+static int qdss_create_buf_tbl(struct qdss_bridge_drvdata *drvdata)
+{
+	struct qdss_buf_tbl_lst *entry;
+	void *buf;
+	struct qdss_request *usb_req;
+	int i;
+
+	for (i = 0; i < poolsize; i++) {
+		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+		if (!entry)
+			goto err;
+
+		buf = kzalloc(QDSS_BUF_SIZE, GFP_KERNEL);
+		usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL);
+
+		entry->buf = buf;
+		entry->usb_req = usb_req;
+		atomic_set(&entry->available, 1);
+		list_add_tail(&entry->link, &drvdata->buf_tbl);
+
+		if (!buf || !usb_req)
+			goto err;
+	}
+
+	return 0;
+err:
+	qdss_destroy_buf_tbl(drvdata);
+	return -ENOMEM;
+}
+
+struct qdss_buf_tbl_lst *qdss_get_buf_tbl_entry(
+					struct qdss_bridge_drvdata *drvdata,
+					void *buf)
+{
+	struct qdss_buf_tbl_lst *entry;
+
+	list_for_each_entry(entry, &drvdata->buf_tbl, link) {
+		if (atomic_read(&entry->available))
+			continue;
+		if (entry->buf == buf)
+			return entry;
+	}
+
+	return NULL;
+}
+
+struct qdss_buf_tbl_lst *qdss_get_entry(struct qdss_bridge_drvdata *drvdata)
+{
+	struct qdss_buf_tbl_lst *item;
+
+	list_for_each_entry(item, &drvdata->buf_tbl, link)
+		if (atomic_cmpxchg(&item->available, 1, 0) == 1)
+			return item;
+
+	return NULL;
+}
+
+static void qdss_buf_tbl_remove(struct qdss_bridge_drvdata *drvdata,
+				void *buf)
+{
+	struct qdss_buf_tbl_lst *entry = NULL;
+
+	list_for_each_entry(entry, &drvdata->buf_tbl, link) {
+		if (entry->buf != buf)
+			continue;
+		atomic_set(&entry->available, 1);
+		return;
+	}
+
+	pr_err_ratelimited("Failed to find buffer for removal\n");
+}
+
+static void mhi_ch_close(struct qdss_bridge_drvdata *drvdata)
+{
+	flush_workqueue(drvdata->mhi_wq);
+	qdss_destroy_buf_tbl(drvdata);
+	mhi_close_channel(drvdata->hdl);
+}
+
+static void mhi_close_work_fn(struct work_struct *work)
+{
+	struct qdss_bridge_drvdata *drvdata =
+				container_of(work,
+					     struct qdss_bridge_drvdata,
+					     close_work);
+
+	usb_qdss_close(drvdata->usb_ch);
+	mhi_ch_close(drvdata);
+}
+
+static void mhi_read_work_fn(struct work_struct *work)
+{
+	int err = 0;
+	enum MHI_FLAGS mhi_flags = MHI_EOT;
+	struct qdss_buf_tbl_lst *entry;
+
+	struct qdss_bridge_drvdata *drvdata =
+				container_of(work,
+					     struct qdss_bridge_drvdata,
+					     read_work);
+
+	do {
+		if (!drvdata->opened)
+			break;
+		entry = qdss_get_entry(drvdata);
+		if (!entry)
+			break;
+
+		err = mhi_queue_xfer(drvdata->hdl, entry->buf, QDSS_BUF_SIZE,
+				      mhi_flags);
+		if (err) {
+			pr_err_ratelimited("Unable to read from MHI buffer err:%d",
+					   err);
+			goto fail;
+		}
+	} while (entry);
+
+	return;
+fail:
+	qdss_buf_tbl_remove(drvdata, entry->buf);
+	queue_work(drvdata->mhi_wq, &drvdata->read_work);
+}
+
+static int mhi_queue_read(struct qdss_bridge_drvdata *drvdata)
+{
+	queue_work(drvdata->mhi_wq, &(drvdata->read_work));
+	return 0;
+}
+
+static int usb_write(struct qdss_bridge_drvdata *drvdata,
+			     struct mhi_result *result)
+{
+	int ret = 0;
+	struct qdss_buf_tbl_lst *entry;
+
+	entry = qdss_get_buf_tbl_entry(drvdata, result->buf_addr);
+	if (!entry)
+		return -EINVAL;
+
+	entry->usb_req->buf = result->buf_addr;
+	entry->usb_req->length = result->bytes_xferd;
+	ret = usb_qdss_data_write(drvdata->usb_ch, entry->usb_req);
+
+	return ret;
+}
+
+static void mhi_read_done_work_fn(struct work_struct *work)
+{
+	unsigned char *buf = NULL;
+	struct mhi_result result;
+	int err = 0;
+	struct qdss_bridge_drvdata *drvdata =
+				container_of(work,
+					     struct qdss_bridge_drvdata,
+					     read_done_work);
+
+	do {
+		err = mhi_poll_inbound(drvdata->hdl, &result);
+		if (err) {
+			pr_debug("MHI poll failed err:%d\n", err);
+			break;
+		}
+		buf = result.buf_addr;
+		if (!buf)
+			break;
+		err = usb_write(drvdata, &result);
+		if (err)
+			qdss_buf_tbl_remove(drvdata, buf);
+	} while (1);
+}
+
+static void usb_write_done(struct qdss_bridge_drvdata *drvdata,
+				   struct qdss_request *d_req)
+{
+	if (d_req->status) {
+		pr_err_ratelimited("USB write failed err:%d\n", d_req->status);
+		mhi_queue_read(drvdata);
+		return;
+	}
+	qdss_buf_tbl_remove(drvdata, d_req->buf);
+	mhi_queue_read(drvdata);
+}
+
+static void usb_notifier(void *priv, unsigned int event,
+			struct qdss_request *d_req, struct usb_qdss_ch *ch)
+{
+	struct qdss_bridge_drvdata *drvdata = priv;
+
+	if (!drvdata)
+		return;
+
+	switch (event) {
+	case USB_QDSS_CONNECT:
+		usb_qdss_alloc_req(drvdata->usb_ch, poolsize, 0);
+		mhi_queue_read(drvdata);
+		break;
+
+	case USB_QDSS_DISCONNECT:
+		/* Leave MHI/USB open.Only close on MHI disconnect */
+		break;
+
+	case USB_QDSS_DATA_WRITE_DONE:
+		usb_write_done(drvdata, d_req);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static int mhi_ch_open(struct qdss_bridge_drvdata *drvdata)
+{
+	int ret;
+
+	if (drvdata->opened)
+		return 0;
+
+	ret = mhi_open_channel(drvdata->hdl);
+	if (ret) {
+		pr_err("Unable to open MHI channel\n");
+		return ret;
+	}
+
+	ret = mhi_get_free_desc(drvdata->hdl);
+	if (ret <= 0)
+		return -EIO;
+
+	drvdata->opened = 1;
+	return 0;
+}
+
+static void qdss_bridge_open_work_fn(struct work_struct *work)
+{
+	struct qdss_bridge_drvdata *drvdata =
+				container_of(work,
+					     struct qdss_bridge_drvdata,
+					     open_work);
+	int ret;
+
+	ret = mhi_ch_open(drvdata);
+	if (ret)
+		goto err_open;
+
+	ret = qdss_create_buf_tbl(drvdata);
+	if (ret)
+		goto err;
+
+	drvdata->usb_ch = usb_qdss_open("qdss_mdm", drvdata, usb_notifier);
+	if (IS_ERR_OR_NULL(drvdata->usb_ch)) {
+		ret = PTR_ERR(drvdata->usb_ch);
+		goto err;
+	}
+
+	return;
+err:
+	mhi_ch_close(drvdata);
+err_open:
+	pr_err("Open work failed with err:%d\n", ret);
+}
+
+static void mhi_notifier(struct mhi_cb_info *cb_info)
+{
+	struct mhi_result *result;
+	struct qdss_bridge_drvdata *drvdata;
+
+	if (!cb_info)
+		return;
+
+	result = cb_info->result;
+	if (!result) {
+		pr_err_ratelimited("Failed to obtain MHI result\n");
+		return;
+	}
+
+	drvdata = (struct qdss_bridge_drvdata *)cb_info->result->user_data;
+	if (!drvdata) {
+		pr_err_ratelimited("MHI returned invalid drvdata\n");
+		return;
+	}
+
+	switch (cb_info->cb_reason) {
+	case MHI_CB_MHI_ENABLED:
+		queue_work(drvdata->mhi_wq, &drvdata->open_work);
+		break;
+
+	case MHI_CB_XFER:
+		if (!drvdata->opened)
+			break;
+
+		queue_work(drvdata->mhi_wq, &drvdata->read_done_work);
+		break;
+
+	case MHI_CB_MHI_DISABLED:
+		if (!drvdata->opened)
+			break;
+
+		drvdata->opened = 0;
+		queue_work(drvdata->mhi_wq, &drvdata->close_work);
+		break;
+
+	default:
+		pr_err_ratelimited("MHI returned invalid cb reason 0x%x\n",
+		       cb_info->cb_reason);
+		break;
+	}
+}
+
+static int qdss_mhi_register_ch(struct qdss_bridge_drvdata *drvdata)
+{
+	struct mhi_client_info_t *client_info;
+	int ret;
+	struct mhi_client_info_t *mhi_info;
+
+	client_info = devm_kzalloc(drvdata->dev, sizeof(*client_info),
+				   GFP_KERNEL);
+	if (!client_info)
+		return -ENOMEM;
+
+	client_info->mhi_client_cb = mhi_notifier;
+	drvdata->client_info = client_info;
+
+	mhi_info = client_info;
+	mhi_info->chan = MHI_CLIENT_QDSS_IN;
+	mhi_info->dev = drvdata->dev;
+	mhi_info->node_name = "qcom,mhi";
+	mhi_info->user_data = drvdata;
+
+	ret = mhi_register_channel(&drvdata->hdl, mhi_info);
+	return ret;
+}
+
+int qdss_mhi_init(struct qdss_bridge_drvdata *drvdata)
+{
+	int ret;
+
+	drvdata->mhi_wq = create_singlethread_workqueue(MODULE_NAME);
+	if (!drvdata->mhi_wq)
+		return -ENOMEM;
+
+	INIT_WORK(&(drvdata->read_work), mhi_read_work_fn);
+	INIT_WORK(&(drvdata->read_done_work), mhi_read_done_work_fn);
+	INIT_WORK(&(drvdata->open_work), qdss_bridge_open_work_fn);
+	INIT_WORK(&(drvdata->close_work), mhi_close_work_fn);
+	INIT_LIST_HEAD(&drvdata->buf_tbl);
+	drvdata->opened = 0;
+
+	ret = qdss_mhi_register_ch(drvdata);
+	if (ret) {
+		destroy_workqueue(drvdata->mhi_wq);
+		pr_err("Unable to register MHI read channel err:%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int qdss_mhi_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device *dev = &pdev->dev;
+	struct qdss_bridge_drvdata *drvdata;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata) {
+		ret = -ENOMEM;
+		return ret;
+	}
+
+	drvdata->dev = &pdev->dev;
+	platform_set_drvdata(pdev, drvdata);
+
+	ret = qdss_mhi_init(drvdata);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	pr_err("Device probe failed err:%d\n", ret);
+	return ret;
+}
+
+static const struct of_device_id qdss_mhi_table[] = {
+	{.compatible = "qcom,qdss-mhi"},
+	{},
+};
+
+static struct platform_driver qdss_mhi_driver = {
+	.probe = qdss_mhi_probe,
+	.driver = {
+		.name = MODULE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = qdss_mhi_table,
+	},
+};
+
+static int __init qdss_bridge_init(void)
+{
+	return platform_driver_register(&qdss_mhi_driver);
+}
+
+static void __exit qdss_bridge_exit(void)
+{
+	platform_driver_unregister(&qdss_mhi_driver);
+}
+
+module_init(qdss_bridge_init);
+module_exit(qdss_bridge_exit);
+MODULE_LICENSE("GPL v2")
+MODULE_DESCRIPTION("QDSS Bridge driver");
diff --git a/drivers/soc/qcom/qdss_bridge.h b/drivers/soc/qcom/qdss_bridge.h
new file mode 100644
index 0000000..97b9c40
--- /dev/null
+++ b/drivers/soc/qcom/qdss_bridge.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _QDSS_BRIDGE_H
+#define _QDSS_BRIDGE_H
+
+struct qdss_buf_tbl_lst {
+	struct list_head link;
+	unsigned char *buf;
+	struct qdss_request *usb_req;
+	atomic_t available;
+};
+
+struct qdss_bridge_drvdata {
+	struct device *dev;
+	bool opened;
+	struct work_struct read_work;
+	struct work_struct read_done_work;
+	struct work_struct open_work;
+	struct work_struct close_work;
+	struct workqueue_struct *mhi_wq;
+	struct mhi_client_handle *hdl;
+	struct mhi_client_info_t *client_info;
+	struct list_head buf_tbl;
+	struct usb_qdss_ch *usb_ch;
+};
+
+#endif
diff --git a/drivers/soc/qcom/rpm_stats.c b/drivers/soc/qcom/rpm_stats.c
index a3fe7b3..e60a7ad 100644
--- a/drivers/soc/qcom/rpm_stats.c
+++ b/drivers/soc/qcom/rpm_stats.c
@@ -173,7 +173,7 @@
 	if (prvdata.read_idx < prvdata.num_records)
 		prvdata.len = msm_rpmstats_copy_stats(&prvdata);
 
-	return snprintf(buf, prvdata.len, prvdata.buf);
+	return snprintf(buf, prvdata.len, "%s", prvdata.buf);
 }
 
 static int msm_rpmstats_create_sysfs(struct msm_rpmstats_platform_data *pd)
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index f4c35ab..f7902e1 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -563,7 +563,7 @@
 	spin_lock_irqsave(&rpm->lock, flags);
 	for (i = 0; rpm->passthru_cache[i]; i++) {
 		rpm_msg = rpm->passthru_cache[i];
-		ret = mbox_send_controller_data(rc->chan, &rpm_msg->msg);
+		ret = mbox_write_controller_data(rc->chan, &rpm_msg->msg);
 		if (ret)
 			goto fail;
 	}
@@ -762,7 +762,7 @@
 	rpm_msg.msg.is_control = true;
 	rpm_msg.msg.is_complete = false;
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 EXPORT_SYMBOL(rpmh_write_control);
 
@@ -797,7 +797,7 @@
 	rpm->dirty = true;
 	spin_unlock_irqrestore(&rpm->lock, flags);
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 EXPORT_SYMBOL(rpmh_invalidate);
 
@@ -886,7 +886,7 @@
 	rpm_msg.msg.num_payload = 1;
 	rpm_msg.msg.is_complete = false;
 
-	return mbox_send_controller_data(rc->chan, &rpm_msg.msg);
+	return mbox_write_controller_data(rc->chan, &rpm_msg.msg);
 }
 
 /**
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index ac5cc54..492b68c 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -764,7 +764,7 @@
 		return scm_remap_error(ret);
 	return ret;
 }
-
+EXPORT_SYMBOL(scm_call2_atomic);
 /**
  * scm_call() - Send an SCM command
  * @svc_id: service identifier
diff --git a/drivers/soc/qcom/secure_buffer.c b/drivers/soc/qcom/secure_buffer.c
index 6553ac0..5289cd0 100644
--- a/drivers/soc/qcom/secure_buffer.c
+++ b/drivers/soc/qcom/secure_buffer.c
@@ -212,6 +212,7 @@
 	kfree(source_vm_copy);
 	return ret;
 }
+EXPORT_SYMBOL(hyp_assign_table);
 
 int hyp_assign_phys(phys_addr_t addr, u64 size, u32 *source_vm_list,
 			int source_nelems, int *dest_vmids,
diff --git a/drivers/soc/qcom/service-locator.c b/drivers/soc/qcom/service-locator.c
index 57f38d3..9dfe281 100644
--- a/drivers/soc/qcom/service-locator.c
+++ b/drivers/soc/qcom/service-locator.c
@@ -149,11 +149,10 @@
 
 	do {
 		pr_debug("Notified about a Receive event\n");
-		ret = qmi_recv_msg(service_locator.clnt_handle);
-		if (ret < 0)
-			pr_err("Error receiving message rc:%d. Retrying...\n",
-								ret);
-	} while (ret == 0);
+	} while ((ret = qmi_recv_msg(service_locator.clnt_handle)) == 0);
+
+	if (ret != -ENOMSG)
+		pr_err("Error receiving message rc:%d\n", ret);
 
 }
 
@@ -190,7 +189,7 @@
 	 */
 	rc = qmi_send_req_wait(service_locator.clnt_handle, req_desc, req,
 		sizeof(*req), resp_desc, resp, sizeof(*resp),
-		msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_TIMEOUT));
+		QMI_SERVREG_LOC_SERVER_TIMEOUT);
 	if (rc < 0) {
 		pr_err("QMI send req failed for client %s, ret - %d\n",
 			pd->client_name, rc);
diff --git a/drivers/soc/qcom/smp2p_spinlock_test.c b/drivers/soc/qcom/smp2p_spinlock_test.c
deleted file mode 100644
index d1e5090..0000000
--- a/drivers/soc/qcom/smp2p_spinlock_test.c
+++ /dev/null
@@ -1,820 +0,0 @@
-/* drivers/soc/qcom/smp2p_spinlock_test.c
- *
- * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#include <linux/debugfs.h>
-#include <linux/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <linux/remote_spinlock.h>
-#include <soc/qcom/smem.h>
-#include "smem_private.h"
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-#define RS_END_THIEF_PID_BIT 20
-#define RS_END_THIEF_MASK 0x00f00000
-
-/* Spinlock commands used for testing Apps<->RPM spinlocks. */
-enum RPM_SPINLOCK_CMDS {
-	RPM_CMD_INVALID,
-	RPM_CMD_START,
-	RPM_CMD_LOCKED,
-	RPM_CMD_UNLOCKED,
-	RPM_CMD_END,
-};
-
-/* Shared structure for testing Apps<->RPM spinlocks. */
-struct rpm_spinlock_test {
-	uint32_t apps_cmd;
-	uint32_t apps_lock_count;
-	uint32_t rpm_cmd;
-	uint32_t rpm_lock_count;
-};
-
-static uint32_t ut_remote_spinlock_run_time = 1;
-
-/**
- * smp2p_ut_remote_spinlock_core - Verify remote spinlock.
- *
- * @s:           Pointer to output file
- * @remote_pid:  Remote processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- *               remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_core(struct seq_file *s, int remote_pid,
-		bool use_trylock)
-{
-	int failed = 0;
-	unsigned int lock_count = 0;
-	struct msm_smp2p_out *handle = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response;
-	struct mock_cb_data cb_out;
-	struct mock_cb_data cb_in;
-	unsigned long flags;
-	unsigned int n;
-	bool have_lock;
-	bool timeout;
-	int failed_tmp;
-	int spinlock_owner;
-	remote_spinlock_t *smem_spinlock;
-	unsigned long end;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	cb_out.initialized = false;
-	cb_in.initialized = false;
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Send start */
-		mock_cb_data_reset(&cb_in);
-		mock_cb_data_reset(&cb_out);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		test_response = SMP2P_GET_RMT_CMD(test_response);
-		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
-				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
-			/* invalid response from remote - abort test */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
-					test_response);
-		}
-
-		/* Run spinlock test */
-		if (use_trylock)
-			seq_puts(s, "\tUsing remote_spin_trylock\n");
-		else
-			seq_puts(s, "\tUsing remote_spin_lock\n");
-
-		flags = 0;
-		have_lock = false;
-		timeout = false;
-		spinlock_owner = 0;
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		end = jiffies + (ut_remote_spinlock_run_time * HZ);
-		if (ut_remote_spinlock_run_time < 300) {
-			seq_printf(s, "\tRunning test for %u seconds; ",
-				ut_remote_spinlock_run_time);
-			seq_puts(s,
-				"on physical hardware please run >= 300 seconds by doing 'echo 300 >  ut_remote_spinlock_time'\n");
-		}
-		while (time_is_after_jiffies(end)) {
-			/* try to acquire spinlock */
-			if (use_trylock) {
-				unsigned long j_start = jiffies;
-
-				while (!remote_spin_trylock_irqsave(
-						smem_spinlock, flags)) {
-					if (jiffies_to_msecs(jiffies - j_start)
-							> 1000) {
-						seq_puts(s,
-							"\tFail: Timeout trying to get the lock\n");
-						timeout = true;
-						break;
-					}
-				}
-				if (timeout)
-					break;
-			} else {
-				remote_spin_lock_irqsave(smem_spinlock, flags);
-			}
-			have_lock = true;
-			++lock_count;
-
-			/* tell the remote side that we have the lock */
-			SMP2P_SET_RMT_DATA(test_request, lock_count);
-			SMP2P_SET_RMT_CMD(test_request,
-					SMP2P_LB_CMD_RSPIN_LOCKED);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_INT(ret, ==, 0);
-
-			/* verify the other side doesn't say it has the lock */
-			for (n = 0; n < 1000; ++n) {
-				spinlock_owner =
-					remote_spin_owner(smem_spinlock);
-				if (spinlock_owner != SMEM_APPS) {
-					/* lock stolen by remote side */
-					seq_puts(s, "\tFail: Remote side: ");
-					seq_printf(s, "%d stole lock pid: %d\n",
-						remote_pid, spinlock_owner);
-					failed = true;
-					break;
-				}
-				spinlock_owner = 0;
-
-				ret = msm_smp2p_in_read(remote_pid,
-					SMP2P_RLPB_ENTRY_NAME, &test_response);
-				UT_ASSERT_INT(ret, ==, 0);
-				test_response =
-					SMP2P_GET_RMT_CMD(test_response);
-				UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_UNLOCKED, ==,
-					test_response);
-			}
-			if (failed)
-				break;
-
-			/* tell remote side we are unlocked and release lock */
-			SMP2P_SET_RMT_CMD(test_request,
-					SMP2P_LB_CMD_RSPIN_UNLOCKED);
-			(void)msm_smp2p_out_write(handle, test_request);
-			have_lock = false;
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-		}
-		if (have_lock)
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
-		/* End test */
-		mock_cb_data_reset(&cb_in);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-		SMP2P_SET_RMT_DATA(test_request, lock_count |
-				(spinlock_owner << RS_END_THIEF_PID_BIT));
-		(void)msm_smp2p_out_write(handle, test_request);
-
-		failed_tmp = failed;
-		failed = false;
-		do {
-			UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-				>, 0);
-			reinit_completion(&cb_in.cb_completion);
-			ret = msm_smp2p_in_read(remote_pid,
-					SMP2P_RLPB_ENTRY_NAME, &test_response);
-			UT_ASSERT_INT(ret, ==, 0);
-		} while (!failed &&
-			SMP2P_GET_RMT_CMD(test_response) !=
-			SMP2P_LB_CMD_RSPIN_END);
-		if (failed)
-			break;
-		failed = failed_tmp;
-
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		seq_puts(s, "\tLocked spinlock ");
-		seq_printf(s, "local %u times; remote %u times",
-			lock_count,
-			test_response & ((1 << RS_END_THIEF_PID_BIT) - 1)
-			);
-		if (test_response & RS_END_THIEF_MASK) {
-			seq_puts(s, "Remote side reporting lock stolen by ");
-			seq_printf(s, "pid %d.\n",
-				SMP2P_GET_BITS(test_response,
-					RS_END_THIEF_MASK,
-					RS_END_THIEF_PID_BIT));
-			failed = 1;
-		}
-		seq_puts(s, "\n");
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, NULL);
-		ret = msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		if (!failed && !timeout)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle) {
-			/* send end command */
-			test_request = 0;
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, lock_count);
-			(void)msm_smp2p_out_write(handle, test_request);
-			(void)msm_smp2p_out_close(&handle);
-		}
-		(void)msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_pid - Verify remote spinlock for a processor.
- *
- * @s:           Pointer to output file
- * @pid:         Processor to test
- * @use_trylock: Use trylock to prevent an Apps deadlock if the
- *               remote spinlock fails.
- */
-static void smp2p_ut_remote_spinlock_pid(struct seq_file *s, int pid,
-		bool use_trylock)
-{
-	struct smp2p_interrupt_config *int_cfg;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
-		return;
-
-	msm_smp2p_deinit_rmt_lpb_proc(pid);
-	smp2p_ut_remote_spinlock_core(s, pid, use_trylock);
-	msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s:   pointer to output file
- */
-static void smp2p_ut_remote_spinlock(struct seq_file *s)
-{
-	int pid;
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
-		smp2p_ut_remote_spinlock_pid(s, pid, false);
-}
-
-/**
- * smp2p_ut_remote_spin_trylock - Verify remote trylock for all processors.
- *
- * @s:   Pointer to output file
- */
-static void smp2p_ut_remote_spin_trylock(struct seq_file *s)
-{
-	int pid;
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid)
-		smp2p_ut_remote_spinlock_pid(s, pid, true);
-}
-
-/**
- * smp2p_ut_remote_spinlock - Verify remote spinlock for all processors.
- *
- * @s:   pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_spinlock_modem(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_MODEM_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_adsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_AUDIO_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_dsps(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_SENSOR_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_wcnss(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_WIRELESS_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_cdsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_CDSP_PROC, false);
-}
-
-static void smp2p_ut_remote_spinlock_tz(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_pid(s, SMP2P_TZ_PROC, false);
-}
-
-/**
- * smp2p_ut_remote_spinlock_rpm - Verify remote spinlock.
- *
- * @s:   pointer to output file
- * @remote_pid:  Remote processor to test
- */
-static void smp2p_ut_remote_spinlock_rpm(struct seq_file *s)
-{
-	int failed = 0;
-	unsigned long flags;
-	unsigned int n;
-	unsigned int test_num;
-	struct rpm_spinlock_test *data_ptr;
-	remote_spinlock_t *smem_spinlock;
-	bool have_lock;
-
-	seq_printf(s, "Running %s for Apps<->RPM Test\n",
-		   __func__);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		data_ptr = smem_alloc(SMEM_ID_VENDOR0,
-				sizeof(struct rpm_spinlock_test), 0,
-				SMEM_ANY_HOST_FLAG);
-		UT_ASSERT_PTR(0, !=, data_ptr);
-
-		/* Send start */
-		writel_relaxed(0, &data_ptr->apps_lock_count);
-		writel_relaxed(RPM_CMD_START, &data_ptr->apps_cmd);
-
-		seq_puts(s, "\tWaiting for RPM to start test\n");
-		for (n = 0; n < 1000; ++n) {
-			if (readl_relaxed(&data_ptr->rpm_cmd) !=
-					RPM_CMD_INVALID)
-				break;
-			usleep_range(1000, 1200);
-		}
-		if (readl_relaxed(&data_ptr->rpm_cmd) == RPM_CMD_INVALID) {
-			/* timeout waiting for RPM */
-			writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
-			UT_ASSERT_INT(RPM_CMD_LOCKED, !=, RPM_CMD_INVALID);
-		}
-
-		/* Run spinlock test */
-		flags = 0;
-		have_lock = false;
-		for (test_num = 0; !failed && test_num < 10000; ++test_num) {
-			/* acquire spinlock */
-			remote_spin_lock_irqsave(smem_spinlock, flags);
-			have_lock = true;
-			data_ptr->apps_lock_count++;
-			writel_relaxed(data_ptr->apps_lock_count,
-				&data_ptr->apps_lock_count);
-			writel_relaxed(RPM_CMD_LOCKED, &data_ptr->apps_cmd);
-			/*
-			 * Ensure that the remote side sees our lock has
-			 * been acquired before we start polling their status.
-			 */
-			wmb();
-
-			/* verify the other side doesn't say it has the lock */
-			for (n = 0; n < 1000; ++n) {
-				UT_ASSERT_HEX(RPM_CMD_UNLOCKED, ==,
-					readl_relaxed(&data_ptr->rpm_cmd));
-			}
-			if (failed)
-				break;
-
-			/* release spinlock */
-			have_lock = false;
-			writel_relaxed(RPM_CMD_UNLOCKED, &data_ptr->apps_cmd);
-			/*
-			 * Ensure that our status-update write was committed
-			 * before we unlock the spinlock.
-			 */
-			wmb();
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-		}
-		if (have_lock)
-			remote_spin_unlock_irqrestore(smem_spinlock, flags);
-
-		/* End test */
-		writel_relaxed(RPM_CMD_INVALID, &data_ptr->apps_cmd);
-		seq_printf(s, "\tLocked spinlock local %u remote %u\n",
-				readl_relaxed(&data_ptr->apps_lock_count),
-				readl_relaxed(&data_ptr->rpm_lock_count));
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-struct rmt_spinlock_work_item {
-	struct work_struct work;
-	struct completion try_lock;
-	struct completion locked;
-	bool has_locked;
-};
-
-static void ut_remote_spinlock_ssr_worker(struct work_struct *work)
-{
-	remote_spinlock_t *smem_spinlock;
-	unsigned long flags;
-	struct rmt_spinlock_work_item *work_item =
-		container_of(work, struct rmt_spinlock_work_item, work);
-
-	work_item->has_locked = false;
-	complete(&work_item->try_lock);
-	smem_spinlock = smem_get_remote_spinlock();
-	if (!smem_spinlock) {
-		pr_err("%s Failed\n", __func__);
-		return;
-	}
-
-	remote_spin_lock_irqsave(smem_spinlock, flags);
-	remote_spin_unlock_irqrestore(smem_spinlock, flags);
-	work_item->has_locked = true;
-	complete(&work_item->locked);
-}
-
-/**
- * smp2p_ut_remote_spinlock_ssr - Verify remote spinlock.
- *
- * @s:   pointer to output file
- */
-static void smp2p_ut_remote_spinlock_ssr(struct seq_file *s)
-{
-	int failed = 0;
-	unsigned long flags;
-	remote_spinlock_t *smem_spinlock;
-	int spinlock_owner = 0;
-
-	struct workqueue_struct *ws = NULL;
-	struct rmt_spinlock_work_item work_item = { .has_locked = false };
-
-	seq_printf(s, " Running %s Test\n",
-		   __func__);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		ws = create_singlethread_workqueue("ut_remote_spinlock_ssr");
-		UT_ASSERT_PTR(ws, !=, NULL);
-		INIT_WORK(&work_item.work, ut_remote_spinlock_ssr_worker);
-		init_completion(&work_item.try_lock);
-		init_completion(&work_item.locked);
-
-		remote_spin_lock_irqsave(smem_spinlock, flags);
-		/* Unlock local spin lock and hold HW spinlock */
-		spin_unlock_irqrestore(&((smem_spinlock)->local), flags);
-
-		queue_work(ws, &work_item.work);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&work_item.try_lock, HZ * 2), >, 0);
-		UT_ASSERT_INT((int)work_item.has_locked, ==, 0);
-		spinlock_owner = remote_spin_owner(smem_spinlock);
-		UT_ASSERT_INT(spinlock_owner, ==, SMEM_APPS);
-		remote_spin_release_all(SMEM_APPS);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&work_item.locked, HZ * 2), >, 0);
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_track_core - Verify remote spinlock.
- *
- * @s:           Pointer to output file
- * @remote_pid:  Remote processor to test
- *
- * This test has the remote subsystem grab the lock, and then has the local
- * subsystem attempt to grab the lock using the trylock() API. It then verifies
- * that the ID in the hw_spinlocks array matches the owner of the lock.
- */
-static void smp2p_ut_remote_spinlock_track_core(struct seq_file *s,
-		int remote_pid)
-{
-	int failed = 0;
-	struct msm_smp2p_out *handle = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response;
-	struct mock_cb_data cb_out;
-	struct mock_cb_data cb_in;
-	unsigned long flags;
-	int stored_value;
-	remote_spinlock_t *smem_spinlock;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	cb_out.initialized = false;
-	cb_in.initialized = false;
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		smem_spinlock = smem_get_remote_spinlock();
-		UT_ASSERT_PTR(smem_spinlock, !=, NULL);
-
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Send start */
-		mock_cb_data_reset(&cb_in);
-		mock_cb_data_reset(&cb_out);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_START);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ * 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		ret = msm_smp2p_in_read(remote_pid, SMP2P_RLPB_ENTRY_NAME,
-				&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		test_response = SMP2P_GET_RMT_CMD(test_response);
-		if (test_response != SMP2P_LB_CMD_RSPIN_LOCKED &&
-				test_response != SMP2P_LB_CMD_RSPIN_UNLOCKED) {
-			/* invalid response from remote - abort test */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			ret = msm_smp2p_out_write(handle, test_request);
-			UT_ASSERT_HEX(SMP2P_LB_CMD_RSPIN_LOCKED, ==,
-					test_response);
-		}
-
-		/* Run spinlock test */
-		flags = 0;
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE_REQ(test_request);
-
-		/* try to acquire spinlock */
-		remote_spin_trylock_irqsave(smem_spinlock, flags);
-		/*
-		 * Need to check against the locking token (PID + 1)
-		 * because the remote_spin_owner() API only returns the
-		 * PID.
-		 */
-		stored_value = remote_spin_get_hw_spinlocks_element(
-				smem_spinlock);
-		UT_ASSERT_INT(stored_value, ==,
-			remote_spin_owner(smem_spinlock) + 1);
-		UT_ASSERT_INT(stored_value, ==, remote_pid + 1);
-
-		/* End test */
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-		SMP2P_SET_RMT_DATA(test_request, 0x0);
-		(void)msm_smp2p_out_write(handle, test_request);
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, NULL);
-		ret = msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		if (!failed)
-			seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle) {
-			/* send end command */
-			test_request = 0x0;
-			SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_RSPIN_END);
-			SMP2P_SET_RMT_DATA(test_request, 0x0);
-			(void)msm_smp2p_out_write(handle, test_request);
-			(void)msm_smp2p_out_close(&handle);
-		}
-		(void)msm_smp2p_in_unregister(remote_pid,
-				SMP2P_RLPB_ENTRY_NAME, &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for modem.
- *
- * @s:	Pointer to output file
- * @pid:		The processor to test
- */
-static void smp2p_ut_remote_spinlock_track(struct seq_file *s, int pid)
-{
-	struct smp2p_interrupt_config *int_cfg;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	if (pid >= SMP2P_NUM_PROCS || !int_cfg[pid].is_configured)
-		return;
-
-	msm_smp2p_deinit_rmt_lpb_proc(pid);
-	smp2p_ut_remote_spinlock_track_core(s, pid);
-	msm_smp2p_init_rmt_lpb_proc(pid);
-}
-
-/**
- * smp2p_ut_remote_spinlock_track - Verify PID tracking for all processors.
- *
- * @s:	Pointer to output file
- *
- * This test verifies PID tracking for all configured remote processors.
- */
-static void smp2p_ut_remote_spinlock_track_modem(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_MODEM_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_adsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_AUDIO_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_dsps(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_SENSOR_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_wcnss(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_WIRELESS_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_cdsp(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_CDSP_PROC);
-}
-
-static void smp2p_ut_remote_spinlock_track_tz(struct seq_file *s)
-{
-	smp2p_ut_remote_spinlock_track(s, SMP2P_TZ_PROC);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
-	/*
-	 * Add Unit Test entries.
-	 *
-	 * The idea with unit tests is that you can run all of them
-	 * from ADB shell by doing:
-	 *  adb shell
-	 *  cat ut*
-	 *
-	 * And if particular tests fail, you can then repeatedly run the
-	 * failing tests as you debug and resolve the failing test.
-	 */
-	smp2p_debug_create("ut_remote_spinlock",
-		smp2p_ut_remote_spinlock);
-	smp2p_debug_create("ut_remote_spin_trylock",
-		smp2p_ut_remote_spin_trylock);
-	smp2p_debug_create("ut_remote_spinlock_modem",
-		smp2p_ut_remote_spinlock_modem);
-	smp2p_debug_create("ut_remote_spinlock_adsp",
-		smp2p_ut_remote_spinlock_adsp);
-	smp2p_debug_create("ut_remote_spinlock_dsps",
-		smp2p_ut_remote_spinlock_dsps);
-	smp2p_debug_create("ut_remote_spinlock_wcnss",
-		smp2p_ut_remote_spinlock_wcnss);
-	smp2p_debug_create("ut_remote_spinlock_cdsp",
-		smp2p_ut_remote_spinlock_cdsp);
-	smp2p_debug_create("ut_remote_spinlock_tz",
-		smp2p_ut_remote_spinlock_tz);
-	smp2p_debug_create("ut_remote_spinlock_rpm",
-		smp2p_ut_remote_spinlock_rpm);
-	smp2p_debug_create_u32("ut_remote_spinlock_time",
-		&ut_remote_spinlock_run_time);
-	smp2p_debug_create("ut_remote_spinlock_ssr",
-		&smp2p_ut_remote_spinlock_ssr);
-	smp2p_debug_create("ut_remote_spinlock_track_modem",
-		&smp2p_ut_remote_spinlock_track_modem);
-	smp2p_debug_create("ut_remote_spinlock_track_adsp",
-		&smp2p_ut_remote_spinlock_track_adsp);
-	smp2p_debug_create("ut_remote_spinlock_track_dsps",
-		&smp2p_ut_remote_spinlock_track_dsps);
-	smp2p_debug_create("ut_remote_spinlock_track_wcnss",
-		&smp2p_ut_remote_spinlock_track_wcnss);
-	smp2p_debug_create("ut_remote_spinlock_track_cdsp",
-		&smp2p_ut_remote_spinlock_track_cdsp);
-	smp2p_debug_create("ut_remote_spinlock_track_tz",
-		&smp2p_ut_remote_spinlock_track_tz);
-	return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test.c b/drivers/soc/qcom/smp2p_test.c
deleted file mode 100644
index aa8d0c8..0000000
--- a/drivers/soc/qcom/smp2p_test.c
+++ /dev/null
@@ -1,1324 +0,0 @@
-/* drivers/soc/qcom/smp2p_test.c
- *
- * Copyright (c) 2013-2016, 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/ctype.h>
-#include <linux/jiffies.h>
-#include <linux/delay.h>
-#include <linux/completion.h>
-#include <linux/module.h>
-#include <soc/qcom/subsystem_restart.h>
-#include "smp2p_private.h"
-#include "smp2p_test_common.h"
-
-/**
- * smp2p_ut_local_basic - Basic sanity test using local loopback.
- *
- * @s: pointer to output file
- *
- * This test simulates a simple write and read
- * when remote processor does not exist.
- */
-static void smp2p_ut_local_basic(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge and start opening */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-
-		msm_smp2p_set_remote_mock_exists(false);
-
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
-		rmp->rx_interrupt_count = 0;
-
-		/* simulate response from remote side */
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-					SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-					SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify port was opened */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2), >, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_local_late_open - Verify post-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries after negotiation is complete.
- */
-static void smp2p_ut_local_late_open(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-			rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-			rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-			rmp->remote_item.header.valid_total_ent,
-			SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-
-		msm_smp2p_set_remote_mock_exists(true);
-
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* verify port was opened */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_local_early_open - Verify pre-negotiation opening.
- *
- * @s: pointer to output file
- *
- * Verify entry creation for opening entries before negotiation is complete.
- */
-static void smp2p_ut_local_early_open(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_out *smp2p_obj;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	struct smp2p_smem *outbound_item;
-	int negotiation_state;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_data;
-
-	seq_printf(s, "Running %s\n", __func__);
-	mock_cb_data_init(&cb_data);
-	do {
-		/* initialize mock edge, but don't enable, yet */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-
-		msm_smp2p_set_remote_mock_exists(false);
-		UT_ASSERT_PTR(NULL, ==,
-				smp2p_get_in_item(SMP2P_REMOTE_MOCK_PROC));
-
-		/* initiate open, but verify it doesn't complete */
-		ret = msm_smp2p_out_open(SMP2P_REMOTE_MOCK_PROC, "smp2p",
-			&cb_data.nb, &smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 8),
-			==, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 0);
-		UT_ASSERT_INT(cb_data.event_open, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		outbound_item = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-				&negotiation_state);
-		UT_ASSERT_PTR(outbound_item, !=, NULL);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENING);
-		UT_ASSERT_INT(0, ==,
-			SMP2P_GET_ENT_VALID(outbound_item->valid_total_ent));
-
-		/* verify that read/write don't work yet */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0x0;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, -ENODEV);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, -ENODEV);
-
-		/* allocate remote entry and verify open */
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_data.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_data.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_data.event_open, ==, 1);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* do write (test outbound entries) */
-		rmp->rx_interrupt_count = 0;
-		test_request = 0xC0DE;
-		ret = msm_smp2p_out_write(smp2p_obj, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* do read (test inbound entries) */
-		ret = msm_smp2p_out_read(smp2p_obj, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(test_request, ==, test_response);
-
-		ret = msm_smp2p_out_close(&smp2p_obj);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(smp2p_obj, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		(void)msm_smp2p_out_close(&smp2p_obj);
-	}
-}
-
-/**
- * smp2p_ut_mock_loopback - Exercise the remote loopback using remote mock.
- *
- * @s: pointer to output file
- *
- * This test exercises the remote loopback code using
- * remote mock object. The remote mock object simulates the remote
- * processor sending remote loopback commands to the local processor.
- */
-static void smp2p_ut_mock_loopback(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	uint32_t test_request = 0;
-	uint32_t test_response = 0;
-	struct msm_smp2p_out  *local;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		/* Initialize the mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 1);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create test entry and attach loopback server */
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		strlcpy(rmp->remote_item.entries[0].name, "smp2p",
-							SMP2P_MAX_ENTRY_NAME);
-		rmp->remote_item.entries[0].entry = 0;
-		rmp->tx_interrupt();
-
-		local = msm_smp2p_init_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 2);
-
-		/* Send Echo Command */
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify Echo Response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local,
-							&test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 10);
-
-		/* Send PINGPONG command */
-		test_request = 0;
-		test_response = 0;
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify PINGPONG Response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 9);
-
-		/* Send CLEARALL command */
-		test_request = 0;
-		test_response = 0;
-		rmp->rx_interrupt_count = 0;
-		reinit_completion(&rmp->cb_completion);
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
-		SMP2P_SET_RMT_DATA(test_request, 10);
-		rmp->remote_item.entries[0].entry = test_request;
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&rmp->cb_completion, HZ / 2),
-			>, 0);
-
-		/* Verify CLEARALL response */
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-		ret = msm_smp2p_out_read(local, &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		test_response = SMP2P_GET_RMT_DATA(test_response);
-		UT_ASSERT_INT(test_response, ==, 0);
-
-		ret = msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-		UT_ASSERT_INT(ret, ==, 0);
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-		msm_smp2p_deinit_rmt_lpb_proc(SMP2P_REMOTE_MOCK_PROC);
-	}
-}
-
-/**
- * smp2p_ut_remote_inout_core - Verify inbound/outbound functionality.
- *
- * @s: pointer to output file
- * @remote_pid:  Remote processor to test
- *
- * This test verifies inbound/outbound functionality for the remote processor.
- */
-static void smp2p_ut_remote_inout_core(struct seq_file *s, int remote_pid)
-{
-	int failed = 0;
-	struct msm_smp2p_out *handle;
-	int ret;
-	uint32_t test_request;
-	uint32_t test_response = 0;
-	static struct mock_cb_data cb_out;
-	static struct mock_cb_data cb_in;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-	mock_cb_data_init(&cb_out);
-	mock_cb_data_init(&cb_in);
-	do {
-		/* Open output entry */
-		ret = msm_smp2p_out_open(remote_pid, "smp2p",
-			&cb_out.nb, &handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_out.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_out.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_out.event_open, ==, 1);
-
-		/* Open inbound entry */
-		ret = msm_smp2p_in_register(remote_pid, "smp2p",
-				&cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_open, ==, 1);
-
-		/* Write an echo request */
-		mock_cb_data_reset(&cb_out);
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_ECHO);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0xAA55);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(SMP2P_LB_CMD_ECHO, ==,
-				SMP2P_GET_RMT_CMD(test_response));
-		UT_ASSERT_INT(0xAA55, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Write a clear all request */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_CLEARALL);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0x0000);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(0x0000, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Write a decrement request */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_LB_CMD_PINGPONG);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		/* Verify inbound reply */
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			>, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 1);
-		UT_ASSERT_INT(SMP2P_GET_RMT_DATA(
-			    cb_in.entry_data.current_value), ==, 0xAA54);
-
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RMT_CMD_TYPE(test_response));
-		UT_ASSERT_INT(SMP2P_LB_CMD_PINGPONG, ==,
-				SMP2P_GET_RMT_CMD(test_response));
-		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Test the ignore flag */
-		mock_cb_data_reset(&cb_in);
-		test_request = 0x0;
-		SMP2P_SET_RMT_CMD_TYPE(test_request, 1);
-		SMP2P_SET_RMT_CMD(test_request, SMP2P_RLPB_IGNORE);
-		SMP2P_SET_RMT_DATA(test_request, 0xAA55);
-		ret = msm_smp2p_out_write(handle, test_request);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		UT_ASSERT_INT(
-			(int)wait_for_completion_timeout(
-					&cb_in.cb_completion, HZ / 2),
-			==, 0);
-		UT_ASSERT_INT(cb_in.cb_count, ==, 0);
-		UT_ASSERT_INT(cb_in.event_entry_update, ==, 0);
-		ret = msm_smp2p_in_read(remote_pid, "smp2p", &test_response);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(0xAA54, ==, SMP2P_GET_RMT_DATA(test_response));
-
-		/* Cleanup */
-		ret = msm_smp2p_out_close(&handle);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_PTR(handle, ==, 0);
-		ret = msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		if (handle)
-			(void)msm_smp2p_out_close(&handle);
-		(void)msm_smp2p_in_unregister(remote_pid, "smp2p", &cb_in.nb);
-
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_inout - Verify inbound/outbound functionality for all.
- *
- * @s: pointer to output file
- *
- * This test verifies inbound and outbound functionality for all
- * configured remote processor.
- */
-static void smp2p_ut_remote_inout(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		msm_smp2p_deinit_rmt_lpb_proc(pid);
-		smp2p_ut_remote_inout_core(s, pid);
-		msm_smp2p_init_rmt_lpb_proc(pid);
-	}
-}
-
-/**
- * smp2p_ut_remote_out_max_entries_core - Verify open functionality.
- *
- * @s: pointer to output file
- * @remote_pid:  Remote processor for which the test is executed.
- *
- * This test verifies open functionality by creating maximum outbound entries.
- */
-static void smp2p_ut_remote_out_max_entries_core(struct seq_file *s,
-	int remote_pid)
-{
-	int j = 0;
-	int failed = 0;
-	struct msm_smp2p_out *handle[SMP2P_MAX_ENTRY];
-	int ret;
-	static struct mock_cb_data cb_out[SMP2P_MAX_ENTRY];
-	char entry_name[SMP2P_MAX_ENTRY_NAME];
-	int num_created;
-
-	seq_printf(s, "Running %s for '%s' remote pid %d\n",
-		   __func__, smp2p_pid_to_name(remote_pid), remote_pid);
-
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-		handle[j] = NULL;
-		mock_cb_data_init(&cb_out[j]);
-	}
-
-	do {
-		num_created = 0;
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			/* Open as many output entries as possible */
-			scnprintf((char *)entry_name, SMP2P_MAX_ENTRY_NAME,
-				"smp2p%d", j);
-			ret = msm_smp2p_out_open(remote_pid, entry_name,
-				&cb_out[j].nb, &handle[j]);
-			if (ret == -ENOMEM)
-				/* hit max number */
-				break;
-			UT_ASSERT_INT(ret, ==, 0);
-			++num_created;
-		}
-		if (failed)
-			break;
-
-		/* verify we created more than 1 entry */
-		UT_ASSERT_INT(num_created, <=, SMP2P_MAX_ENTRY);
-		UT_ASSERT_INT(num_created, >, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-
-	/* cleanup */
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-		ret = msm_smp2p_out_close(&handle[j]);
-}
-
-/**
- * smp2p_ut_remote_out_max_entries - Verify open for all configured processors.
- *
- * @s: pointer to output file
- *
- * This test verifies creating max number of entries for
- * all configured remote processor.
- */
-static void smp2p_ut_remote_out_max_entries(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s, "Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		smp2p_ut_remote_out_max_entries_core(s, pid);
-	}
-}
-
-/**
- * smp2p_ut_local_in_max_entries - Verify registering and unregistering.
- *
- * @s: pointer to output file
- *
- * This test verifies registering and unregistering for inbound entries using
- * the remote mock processor.
- */
-static void smp2p_ut_local_in_max_entries(struct seq_file *s)
-{
-	int j = 0;
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	static struct mock_cb_data cb_in[SMP2P_MAX_ENTRY];
-	static struct mock_cb_data cb_out;
-
-	seq_printf(s, "Running %s\n", __func__);
-
-	for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-		mock_cb_data_init(&cb_in[j]);
-
-	mock_cb_data_init(&cb_out);
-
-	do {
-		/* Initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create Max Entries in the remote mock object */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			scnprintf(rmp->remote_item.entries[j].name,
-				SMP2P_MAX_ENTRY_NAME, "smp2p%d", j);
-			rmp->remote_item.entries[j].entry = 0;
-			rmp->tx_interrupt();
-		}
-
-		/* Register for in entries */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-			UT_ASSERT_INT(ret, ==, 0);
-			UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-					&(cb_in[j].cb_completion), HZ / 2),
-				>, 0);
-			UT_ASSERT_INT(cb_in[j].cb_count, ==, 1);
-			UT_ASSERT_INT(cb_in[j].event_entry_update, ==, 0);
-		}
-		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
-		/* Unregister */
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++) {
-			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-		    UT_ASSERT_INT(ret, ==, 0);
-		}
-		UT_ASSERT_INT(j, ==, SMP2P_MAX_ENTRY);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-
-		for (j = 0; j < SMP2P_MAX_ENTRY; j++)
-			ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[j].name,
-				&(cb_in[j].nb));
-	}
-}
-
-/**
- * smp2p_ut_local_in_multiple - Verify Multiple Inbound Registration.
- *
- * @s: pointer to output file
- *
- * This test verifies multiple clients registering for same inbound entries
- * using the remote mock processor.
- */
-static void smp2p_ut_local_in_multiple(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-	static struct mock_cb_data cb_in_1;
-	static struct mock_cb_data cb_in_2;
-	static struct mock_cb_data cb_out;
-
-	seq_printf(s, "Running %s\n", __func__);
-
-	mock_cb_data_init(&cb_in_1);
-	mock_cb_data_init(&cb_in_2);
-	mock_cb_data_init(&cb_out);
-
-	do {
-		/* Initialize mock edge */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0,
-			sizeof(struct smp2p_smem_item));
-		rmp->remote_item.header.magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(
-		rmp->remote_item.header.rem_loc_proc_id,
-						SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(
-		rmp->remote_item.header.feature_version, 1);
-		SMP2P_SET_FEATURES(
-		rmp->remote_item.header.feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(
-		rmp->remote_item.header.valid_total_ent, 1);
-		SMP2P_SET_ENT_VALID(
-		rmp->remote_item.header.valid_total_ent, 0);
-		rmp->remote_item.header.flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-
-		/* Create an Entry in the remote mock object */
-		scnprintf(rmp->remote_item.entries[0].name,
-				SMP2P_MAX_ENTRY_NAME, "smp2p%d", 1);
-		rmp->remote_item.entries[0].entry = 0;
-		rmp->tx_interrupt();
-
-		/* Register multiple clients for the inbound entry */
-		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&cb_in_1.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-				&(cb_in_1.cb_completion), HZ / 2),
-				>, 0);
-		UT_ASSERT_INT(cb_in_1.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in_1.event_entry_update, ==, 0);
-
-		ret = msm_smp2p_in_register(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&cb_in_2.nb);
-		UT_ASSERT_INT(ret, ==, 0);
-		UT_ASSERT_INT(
-				(int)wait_for_completion_timeout(
-				&(cb_in_2.cb_completion), HZ / 2),
-				>, 0);
-		UT_ASSERT_INT(cb_in_2.cb_count, ==, 1);
-		UT_ASSERT_INT(cb_in_2.event_entry_update, ==, 0);
-
-
-		/* Unregister the clients */
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_1.nb));
-		UT_ASSERT_INT(ret, ==, 0);
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_2.nb));
-		UT_ASSERT_INT(ret, ==, 0);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_1.nb));
-
-		ret = msm_smp2p_in_unregister(SMP2P_REMOTE_MOCK_PROC,
-				rmp->remote_item.entries[0].name,
-				&(cb_in_2.nb));
-	}
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- */
-static void smp2p_ut_local_ssr_ack(struct seq_file *s)
-{
-	int failed = 0;
-	struct msm_smp2p_remote_mock *rmp = NULL;
-	int ret;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		struct smp2p_smem *rhdr;
-		struct smp2p_smem *lhdr;
-		int negotiation_state;
-
-		/* initialize v1 without SMP2P_FEATURE_SSR_ACK enabled */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-		rhdr = &rmp->remote_item.header;
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
-		rhdr->magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
-				SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(rhdr->feature_version, 1);
-		SMP2P_SET_FEATURES(rhdr->feature_version, 0);
-		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
-		rhdr->flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify edge is open */
-		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-					&negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* verify no response to ack feature */
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 0);
-
-		/* initialize v1 with SMP2P_FEATURE_SSR_ACK enabled */
-		ret = smp2p_reset_mock_edge();
-		UT_ASSERT_INT(ret, ==, 0);
-		rmp = msm_smp2p_get_remote_mock();
-		UT_ASSERT_PTR(rmp, !=, NULL);
-		rhdr = &rmp->remote_item.header;
-
-		rmp->rx_interrupt_count = 0;
-		memset(&rmp->remote_item, 0, sizeof(struct smp2p_smem_item));
-		rhdr->magic = SMP2P_MAGIC;
-		SMP2P_SET_LOCAL_PID(rhdr->rem_loc_proc_id,
-				SMP2P_REMOTE_MOCK_PROC);
-		SMP2P_SET_REMOTE_PID(rhdr->rem_loc_proc_id, SMP2P_APPS_PROC);
-		SMP2P_SET_VERSION(rhdr->feature_version, 1);
-		SMP2P_SET_FEATURES(rhdr->feature_version,
-				SMP2P_FEATURE_SSR_ACK);
-		SMP2P_SET_ENT_TOTAL(rhdr->valid_total_ent, SMP2P_MAX_ENTRY);
-		SMP2P_SET_ENT_VALID(rhdr->valid_total_ent, 0);
-		rmp->rx_interrupt_count = 0;
-		rhdr->flags = 0x0;
-		msm_smp2p_set_remote_mock_exists(true);
-		rmp->tx_interrupt();
-
-		/* verify edge is open */
-		lhdr = smp2p_get_out_item(SMP2P_REMOTE_MOCK_PROC,
-					&negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(negotiation_state, ==, SMP2P_EDGE_STATE_OPENED);
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		/* verify response to ack feature */
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 1);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(1, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		rmp->rx_interrupt_count = 0;
-		SMP2P_SET_RESTART_DONE(rhdr->flags, 0);
-		rmp->tx_interrupt();
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_DONE(lhdr->flags));
-		UT_ASSERT_INT(0, ==, SMP2P_GET_RESTART_ACK(lhdr->flags));
-		UT_ASSERT_INT(rmp->rx_interrupt_count, ==, 1);
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * get_ssr_name_for_proc - Retrieve an SSR name from the provided list
- *
- * @names:	List of possible processor names
- * @name_len:	The length of @names
- * @index:	Index into @names
- *
- * Return: Pointer to the next processor name, NULL in error conditions
- */
-static char *get_ssr_name_for_proc(char * const names[], size_t name_len,
-				   int index)
-{
-	if (index >= name_len) {
-		pr_err("%s: SSR failed; check subsys name table\n",
-				__func__);
-		return NULL;
-	}
-
-	return names[index];
-}
-
-/**
- * smp2p_ut_local_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- * @rpid: Remote processor ID
- * @int_cfg: Interrupt config
- */
-static void smp2p_ut_remotesubsys_ssr_ack(struct seq_file *s, uint32_t rpid,
-		struct smp2p_interrupt_config *int_cfg)
-{
-	int failed = 0;
-
-	seq_printf(s, "Running %s\n", __func__);
-	do {
-		struct smp2p_smem *rhdr;
-		struct smp2p_smem *lhdr;
-		int negotiation_state;
-		int name_index;
-		int ret;
-		uint32_t ssr_done_start;
-		bool ssr_ack_enabled = false;
-		bool ssr_success = false;
-		char *name = NULL;
-
-		static char * const mpss_names[] = {"modem", "mpss"};
-		static char * const lpass_names[] = {"adsp", "lpass"};
-		static char * const sensor_names[] = {"slpi", "dsps"};
-		static char * const wcnss_names[] = {"wcnss"};
-
-		lhdr = smp2p_get_out_item(rpid, &negotiation_state);
-		UT_ASSERT_PTR(NULL, !=, lhdr);
-		UT_ASSERT_INT(SMP2P_EDGE_STATE_OPENED, ==, negotiation_state);
-
-		rhdr = smp2p_get_in_item(rpid);
-		UT_ASSERT_PTR(NULL, !=, rhdr);
-
-		/* get initial state of SSR flags */
-		if (SMP2P_GET_FEATURES(rhdr->feature_version)
-				& SMP2P_FEATURE_SSR_ACK)
-			ssr_ack_enabled = true;
-		else
-			ssr_ack_enabled = false;
-
-		ssr_done_start = SMP2P_GET_RESTART_DONE(rhdr->flags);
-		UT_ASSERT_INT(ssr_done_start, ==,
-				SMP2P_GET_RESTART_ACK(lhdr->flags));
-
-		/* trigger restart */
-		name_index = 0;
-		while (!ssr_success) {
-
-			switch (rpid) {
-			case SMP2P_MODEM_PROC:
-				name = get_ssr_name_for_proc(mpss_names,
-						ARRAY_SIZE(mpss_names),
-						name_index);
-				break;
-			case SMP2P_AUDIO_PROC:
-				name = get_ssr_name_for_proc(lpass_names,
-						ARRAY_SIZE(lpass_names),
-						name_index);
-				break;
-			case SMP2P_SENSOR_PROC:
-				name = get_ssr_name_for_proc(sensor_names,
-						ARRAY_SIZE(sensor_names),
-						name_index);
-				break;
-			case SMP2P_WIRELESS_PROC:
-				name = get_ssr_name_for_proc(wcnss_names,
-						ARRAY_SIZE(wcnss_names),
-						name_index);
-				break;
-			default:
-				pr_err("%s: Invalid proc ID %d given for ssr\n",
-						__func__, rpid);
-			}
-
-			if (!name) {
-				seq_puts(s, "\tSSR failed; check subsys name table\n");
-				failed = true;
-				break;
-			}
-
-			seq_printf(s, "Restarting '%s'\n", name);
-			ret = subsystem_restart(name);
-			if (ret == -ENODEV) {
-				seq_puts(s, "\tSSR call failed\n");
-				++name_index;
-				continue;
-			}
-			ssr_success = true;
-		}
-		if (failed)
-			break;
-
-		msleep(10*1000);
-
-		/* verify ack signaling */
-		if (ssr_ack_enabled) {
-			ssr_done_start ^= 1;
-			UT_ASSERT_INT(ssr_done_start, ==,
-					SMP2P_GET_RESTART_ACK(lhdr->flags));
-			UT_ASSERT_INT(ssr_done_start, ==,
-					SMP2P_GET_RESTART_DONE(rhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(lhdr->flags));
-			seq_puts(s, "\tSSR ACK Enabled and Toggled\n");
-		} else {
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(lhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_ACK(lhdr->flags));
-
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_DONE(rhdr->flags));
-			UT_ASSERT_INT(0, ==,
-					SMP2P_GET_RESTART_ACK(rhdr->flags));
-			seq_puts(s, "\tSSR ACK Disabled\n");
-		}
-
-		seq_puts(s, "\tOK\n");
-	} while (0);
-
-	if (failed) {
-		pr_err("%s: Failed\n", __func__);
-		seq_puts(s, "\tFailed\n");
-	}
-}
-
-/**
- * smp2p_ut_remote_ssr_ack - Verify SSR Done/ACK Feature
- *
- * @s: pointer to output file
- *
- * Triggers SSR for each subsystem.
- */
-static void smp2p_ut_remote_ssr_ack(struct seq_file *s)
-{
-	struct smp2p_interrupt_config *int_cfg;
-	int pid;
-
-	int_cfg = smp2p_get_interrupt_config();
-	if (!int_cfg) {
-		seq_puts(s,
-			"Remote processor config unavailable\n");
-		return;
-	}
-
-	for (pid = 0; pid < SMP2P_NUM_PROCS; ++pid) {
-		if (!int_cfg[pid].is_configured)
-			continue;
-
-		msm_smp2p_deinit_rmt_lpb_proc(pid);
-		smp2p_ut_remotesubsys_ssr_ack(s, pid, &int_cfg[pid]);
-		msm_smp2p_init_rmt_lpb_proc(pid);
-	}
-}
-
-static struct dentry *dent;
-
-static int debugfs_show(struct seq_file *s, void *data)
-{
-	void (*show)(struct seq_file *) = s->private;
-
-	show(s);
-
-	return 0;
-}
-
-static int debug_open(struct inode *inode, struct file *file)
-{
-	return single_open(file, debugfs_show, inode->i_private);
-}
-
-static const struct file_operations debug_ops = {
-	.open = debug_open,
-	.release = single_release,
-	.read = seq_read,
-	.llseek = seq_lseek,
-};
-
-void smp2p_debug_create(const char *name,
-			 void (*show)(struct seq_file *))
-{
-	struct dentry *file;
-
-	file = debugfs_create_file(name, 0444, dent, show, &debug_ops);
-	if (!file)
-		pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-void smp2p_debug_create_u32(const char *name, uint32_t *value)
-{
-	struct dentry *file;
-
-	file = debugfs_create_u32(name, 0644, dent, value);
-	if (!file)
-		pr_err("%s: unable to create file '%s'\n", __func__, name);
-}
-
-static int __init smp2p_debugfs_init(void)
-{
-	dent = debugfs_create_dir("smp2p_test", 0);
-	if (IS_ERR(dent))
-		return PTR_ERR(dent);
-
-	/*
-	 * Add Unit Test entries.
-	 *
-	 * The idea with unit tests is that you can run all of them
-	 * from ADB shell by doing:
-	 *  adb shell
-	 *  cat ut*
-	 *
-	 * And if particular tests fail, you can then repeatedly run the
-	 * failing tests as you debug and resolve the failing test.
-	 */
-	smp2p_debug_create("ut_local_basic",
-			smp2p_ut_local_basic);
-	smp2p_debug_create("ut_local_late_open",
-			smp2p_ut_local_late_open);
-	smp2p_debug_create("ut_local_early_open",
-			smp2p_ut_local_early_open);
-	smp2p_debug_create("ut_mock_loopback",
-			smp2p_ut_mock_loopback);
-	smp2p_debug_create("ut_remote_inout",
-			smp2p_ut_remote_inout);
-	smp2p_debug_create("ut_local_in_max_entries",
-		smp2p_ut_local_in_max_entries);
-	smp2p_debug_create("ut_remote_out_max_entries",
-			smp2p_ut_remote_out_max_entries);
-	smp2p_debug_create("ut_local_in_multiple",
-			smp2p_ut_local_in_multiple);
-	smp2p_debug_create("ut_local_ssr_ack",
-			smp2p_ut_local_ssr_ack);
-	smp2p_debug_create("ut_remote_ssr_ack",
-			smp2p_ut_remote_ssr_ack);
-
-	return 0;
-}
-module_init(smp2p_debugfs_init);
diff --git a/drivers/soc/qcom/smp2p_test_common.h b/drivers/soc/qcom/smp2p_test_common.h
deleted file mode 100644
index 0d22fec..0000000
--- a/drivers/soc/qcom/smp2p_test_common.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/* drivers/soc/qcom/smp2p_test_common.h
- *
- * Copyright (c) 2013-2014,2016 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 _SMP2P_TEST_COMMON_H_
-#define _SMP2P_TEST_COMMON_H_
-
-#include <linux/debugfs.h>
-
-/**
- * Unit test assertion for logging test cases.
- *
- * @a lval
- * @b rval
- * @cmp comparison operator
- *
- * Assertion fails if (@a cmp @b) is not true which then
- * logs the function and line number where the error occurred
- * along with the values of @a and @b.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT(a, cmp, b) \
-	{ \
-	int a_tmp = (a); \
-	int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%d) " #cmp " " #b "(%d)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_PTR(a, cmp, b) \
-	{ \
-	void *a_tmp = (a); \
-	void *b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%pK) " #cmp \
-				" " #b "(%pK)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_UINT(a, cmp, b) \
-	{ \
-	unsigned int a_tmp = (a); \
-	unsigned int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%u) " #cmp " " #b "(%u)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-#define UT_ASSERT_HEX(a, cmp, b) \
-	{ \
-	unsigned int a_tmp = (a); \
-	unsigned int b_tmp = (b); \
-	if (!((a_tmp)cmp(b_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%x) " #cmp " " #b "(%x)\n", \
-				__func__, __LINE__, \
-				a_tmp, b_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-/**
- * In-range unit test assertion for test cases.
- *
- * @a lval
- * @minv Minimum value
- * @maxv Maximum value
- *
- * Assertion fails if @a is not on the exclusive range minv, maxv
- * ((@a < @minv) or (@a > @maxv)).  In the failure case, the macro
- * logs the function and line number where the error occurred along
- * with the values of @a and @minv, @maxv.
- *
- * Assumes that the following local variables exist:
- * @s - sequential output file pointer
- * @failed - set to true if test fails
- */
-#define UT_ASSERT_INT_IN_RANGE(a, minv, maxv) \
-	{ \
-	int a_tmp = (a); \
-	int minv_tmp = (minv); \
-	int maxv_tmp = (maxv); \
-	if (((a_tmp) < (minv_tmp)) || ((a_tmp) > (maxv_tmp))) { \
-		seq_printf(s, "%s:%d Fail: " #a "(%d) < " #minv "(%d) or " \
-				 #a "(%d) > " #maxv "(%d)\n", \
-				__func__, __LINE__, \
-				a_tmp, minv_tmp, a_tmp, maxv_tmp); \
-		failed = 1; \
-		break; \
-	} \
-	}
-
-/* Structure to track state changes for the notifier callback. */
-struct mock_cb_data {
-	bool initialized;
-	spinlock_t lock;
-	struct notifier_block nb;
-
-	/* events */
-	struct completion cb_completion;
-	int cb_count;
-	int event_open;
-	int event_entry_update;
-	struct msm_smp2p_update_notif entry_data;
-};
-
-void smp2p_debug_create(const char *name, void (*show)(struct seq_file *));
-void smp2p_debug_create_u32(const char *name, uint32_t *value);
-static inline int smp2p_test_notify(struct notifier_block *self,
-	unsigned long event, void *data);
-
-/**
- * Reset mock callback data to default values.
- *
- * @cb:  Mock callback data
- */
-static inline void mock_cb_data_reset(struct mock_cb_data *cb)
-{
-	reinit_completion(&cb->cb_completion);
-	cb->cb_count = 0;
-	cb->event_open = 0;
-	cb->event_entry_update = 0;
-	memset(&cb->entry_data, 0,
-		sizeof(struct msm_smp2p_update_notif));
-}
-
-
-/**
- * Initialize mock callback data.
- *
- * @cb:  Mock callback data
- */
-static inline void mock_cb_data_init(struct mock_cb_data *cb)
-{
-	if (!cb->initialized) {
-		init_completion(&cb->cb_completion);
-		spin_lock_init(&cb->lock);
-		cb->initialized = true;
-		cb->nb.notifier_call = smp2p_test_notify;
-		memset(&cb->entry_data, 0,
-			sizeof(struct msm_smp2p_update_notif));
-	}
-	mock_cb_data_reset(cb);
-}
-
-/**
- * Notifier function passed into SMP2P for testing.
- *
- * @self:       Pointer to calling notifier block
- * @event:	    Event
- * @data:       Event-specific data
- * @returns:    0
- */
-static inline int smp2p_test_notify(struct notifier_block *self,
-		unsigned long event, void *data)
-{
-	struct mock_cb_data *cb_data_ptr;
-	unsigned long flags;
-
-	cb_data_ptr = container_of(self, struct mock_cb_data, nb);
-
-	spin_lock_irqsave(&cb_data_ptr->lock, flags);
-
-	switch (event) {
-	case SMP2P_OPEN:
-		++cb_data_ptr->event_open;
-		if (data) {
-			cb_data_ptr->entry_data =
-			*(struct msm_smp2p_update_notif *)(data);
-		}
-		break;
-	case SMP2P_ENTRY_UPDATE:
-		++cb_data_ptr->event_entry_update;
-		if (data) {
-			cb_data_ptr->entry_data =
-			*(struct msm_smp2p_update_notif *)(data);
-		}
-		break;
-	default:
-		pr_err("%s Unknown event\n", __func__);
-		break;
-	}
-
-	++cb_data_ptr->cb_count;
-	complete(&cb_data_ptr->cb_completion);
-	spin_unlock_irqrestore(&cb_data_ptr->lock, flags);
-	return 0;
-}
-#endif /* _SMP2P_TEST_COMMON_H_ */
diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c
index 417b0b2..9af39e1 100644
--- a/drivers/soc/qcom/socinfo.c
+++ b/drivers/soc/qcom/socinfo.c
@@ -66,6 +66,7 @@
 	HW_PLATFORM_RCM	= 21,
 	HW_PLATFORM_STP = 23,
 	HW_PLATFORM_SBC = 24,
+	HW_PLATFORM_HDK = 31,
 	HW_PLATFORM_INVALID
 };
 
@@ -86,6 +87,7 @@
 	[HW_PLATFORM_DTV] = "DTV",
 	[HW_PLATFORM_STP] = "STP",
 	[HW_PLATFORM_SBC] = "SBC",
+	[HW_PLATFORM_HDK] = "HDK",
 };
 
 enum {
@@ -572,6 +574,19 @@
 	/* SDM670 ID */
 	[336] = {MSM_CPU_SDM670, "SDM670"},
 
+	/* QCS605 ID */
+	[347] = {MSM_CPU_QCS605, "QCS605"},
+
+	/* SDA670 ID */
+	[337] = {MSM_CPU_SDA670, "SDA670"},
+
+	/* 8953 ID */
+	[293] = {MSM_CPU_8953, "MSM8953"},
+	[304] = {MSM_CPU_8953, "APQ8053"},
+
+	/* SDM450 ID */
+	[338] = {MSM_CPU_SDM450, "SDM450"},
+
 	/* Uninitialized IDs are not known to run Linux.
 	 * MSM_CPU_UNKNOWN is set to 0 to ensure these IDs are
 	 * considered as unknown CPU.
@@ -1434,10 +1449,26 @@
 		dummy_socinfo.id = 336;
 		strlcpy(dummy_socinfo.build_id, "sdm670 - ",
 			sizeof(dummy_socinfo.build_id));
+	} else if (early_machine_is_sda670()) {
+		dummy_socinfo.id = 337;
+		strlcpy(dummy_socinfo.build_id, "sda670 - ",
+			sizeof(dummy_socinfo.build_id));
+	} else if (early_machine_is_qcs605()) {
+		dummy_socinfo.id = 347;
+		strlcpy(dummy_socinfo.build_id, "qcs605 - ",
+			sizeof(dummy_socinfo.build_id));
 	} else if (early_machine_is_sdxpoorwills()) {
 		dummy_socinfo.id = 334;
 		strlcpy(dummy_socinfo.build_id, "sdxpoorwills - ",
 			sizeof(dummy_socinfo.build_id));
+	} else if (early_machine_is_msm8953()) {
+		dummy_socinfo.id = 293;
+		strlcpy(dummy_socinfo.build_id, "msm8953 - ",
+			sizeof(dummy_socinfo.build_id));
+	} else if (early_machine_is_sdm450()) {
+		dummy_socinfo.id = 338;
+		strlcpy(dummy_socinfo.build_id, "sdm450 - ",
+			sizeof(dummy_socinfo.build_id));
 	}
 
 	strlcat(dummy_socinfo.build_id, "Dummy socinfo",
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
index de5f0ff..119a788 100644
--- a/drivers/soc/qcom/spcom.c
+++ b/drivers/soc/qcom/spcom.c
@@ -206,10 +206,8 @@
 	 * Only one rx/tx transaction at a time (request + response).
 	 */
 	int ref_count;
-	u32 pid;
 
-	/* link UP/DOWN callback */
-	void (*notify_link_state_cb)(bool up);
+	u32 pid; /* debug only to find user space application */
 
 	/* abort flags */
 	bool rx_abort;
@@ -493,13 +491,10 @@
 
 		ch->glink_state = event;
 
-		/*
-		 * if spcom_notify_state() is called within glink_open()
-		 * then ch->glink_handle is not updated yet.
-		 */
-		if (!ch->glink_handle) {
-			pr_debug("update glink_handle, ch [%s].\n", ch->name);
-			ch->glink_handle = handle;
+		if (!handle) {
+			pr_err("inavlid glink_handle, ch [%s].\n", ch->name);
+			mutex_unlock(&ch->lock);
+			return;
 		}
 
 		/* signal before unlock mutex & before calling glink */
@@ -511,9 +506,9 @@
 		 * We do it here, ASAP, to allow rx data.
 		 */
 
+		ch->rx_abort = false; /* cleanup from previouse close */
 		pr_debug("call glink_queue_rx_intent() ch [%s].\n", ch->name);
-		ret = glink_queue_rx_intent(ch->glink_handle,
-					    ch, ch->rx_buf_size);
+		ret = glink_queue_rx_intent(handle, ch, ch->rx_buf_size);
 		if (ret) {
 			pr_err("glink_queue_rx_intent() err [%d]\n", ret);
 		} else {
@@ -585,7 +580,10 @@
  * spcom_notify_rx_abort() - glink callback on aborting rx pending buffer.
  *
  * Rx abort may happen if channel is closed by remote side, while rx buffer is
- * pending in the queue.
+ * pending in the queue, like upon SP reset (SSR).
+ *
+ * More common scenario, is when rx intent is queud (for next transfer),
+ * and the channel is closed locally.
  */
 static void spcom_notify_rx_abort(void *handle, const void *priv,
 				  const void *pkt_priv)
@@ -599,7 +597,10 @@
 
 	pr_debug("ch [%s] pending rx aborted.\n", ch->name);
 
-	if (spcom_is_channel_open(ch) && (!ch->rx_abort)) {
+	/* ignore rx-abort after local channel disconect,
+	 * so check that the channel is connected.
+	 */
+	if (spcom_is_channel_connected(ch) && (!ch->rx_abort)) {
 		ch->rx_abort = true;
 		complete_all(&ch->rx_done);
 	}
@@ -736,6 +737,7 @@
 	long timeleft;
 	const char *name;
 	void *handle;
+	u32 pid = current_pid();
 
 	mutex_lock(&ch->lock);
 	name = ch->name;
@@ -749,7 +751,7 @@
 	}
 
 	pr_debug("ch [%s] opened by PID [%d], count [%d]\n",
-		 name, ch->pid, ch->ref_count);
+		 name, pid, ch->ref_count);
 
 	pr_debug("Open channel [%s] timeout_msec [%d].\n", name, timeout_msec);
 
@@ -777,7 +779,7 @@
 	/* init channel context after successful open */
 	ch->glink_handle = handle;
 	ch->ref_count++;
-	ch->pid = current_pid();
+	ch->pid = pid;
 	ch->txn_id = INITIAL_TXN_ID;
 
 	mutex_unlock(&ch->lock);
@@ -958,6 +960,7 @@
 		return -ETIMEDOUT;
 	} else if (ch->rx_abort) {
 		mutex_unlock(&ch->lock);
+		pr_err("rx_abort, probably remote side reset (SSR).\n");
 		return -ERESTART; /* probably SSR */
 	} else if (ch->actual_rx_size) {
 		pr_debug("actual_rx_size is [%zu]\n", ch->actual_rx_size);
@@ -1026,10 +1029,12 @@
 			 ch->name, ch->actual_rx_size);
 		goto exit_ready;
 	}
+	mutex_unlock(&ch->lock); /* unlock while waiting */
 
 	pr_debug("Wait for Rx Done, ch [%s].\n", ch->name);
 	wait_for_completion(&ch->rx_done);
 
+	mutex_lock(&ch->lock); /* re-lock after waiting */
 	/* Check Rx Abort on SP reset */
 	if (ch->rx_abort) {
 		pr_err("rx aborted.\n");
@@ -2027,6 +2032,7 @@
 				      void *buf,
 				      uint32_t size)
 {
+	int ret = -1;
 	uint32_t next_req_size = 0;
 
 	if (size < sizeof(next_req_size)) {
@@ -2034,7 +2040,10 @@
 		return -EINVAL;
 	}
 
-	next_req_size = spcom_get_next_request_size(ch);
+	ret = spcom_get_next_request_size(ch);
+	if (ret < 0)
+		return ret;
+	next_req_size = (uint32_t) ret;
 
 	memcpy(buf, &next_req_size, sizeof(next_req_size));
 	pr_debug("next_req_size [%d].\n", next_req_size);
@@ -2139,18 +2148,20 @@
 			      void *buf,
 			      uint32_t size)
 {
+	int ret = -1;
+
 	if (size == SPCOM_GET_NEXT_REQUEST_SIZE) {
 		pr_debug("get next request size, ch [%s].\n", ch->name);
 		ch->is_server = true;
-		size = spcom_handle_get_req_size(ch, buf, size);
+		ret = spcom_handle_get_req_size(ch, buf, size);
 	} else {
 		pr_debug("get request/response, ch [%s].\n", ch->name);
-		size = spcom_handle_read_req_resp(ch, buf, size);
+		ret = spcom_handle_read_req_resp(ch, buf, size);
 	}
 
 	pr_debug("ch [%s] , size = %d.\n", ch->name, size);
 
-	return size;
+	return ret;
 }
 
 /*======================================================================*/
@@ -2302,6 +2313,7 @@
 	char *buf;
 	struct spcom_channel *ch;
 	const char *name = file_to_filename(filp);
+	int buf_size = 0;
 
 	pr_debug("Write file [%s] size [%d] pos [%d].\n",
 		 name, (int) size, (int) *f_pos);
@@ -2328,6 +2340,7 @@
 			   (int) size, (int) SPCOM_MAX_COMMAND_SIZE);
 		return -EINVAL;
 	}
+	buf_size = size; /* explicit casting size_t to int */
 
 	if (*f_pos != 0) {
 		pr_err("offset should be zero, no sparse buffer.\n");
@@ -2345,7 +2358,7 @@
 		return -EFAULT;
 	}
 
-	ret = spcom_handle_write(ch, buf, size);
+	ret = spcom_handle_write(ch, buf, buf_size);
 	if (ret) {
 		pr_err("handle command error [%d].\n", ret);
 		kfree(buf);
@@ -2373,6 +2386,7 @@
 	char *buf;
 	struct spcom_channel *ch;
 	const char *name = file_to_filename(filp);
+	uint32_t buf_size = 0;
 
 	pr_debug("Read file [%s], size = %d bytes.\n", name, (int) size);
 
@@ -2381,6 +2395,7 @@
 		pr_err("invalid parameters.\n");
 		return -EINVAL;
 	}
+	buf_size = size; /* explicit casting size_t to uint32_t */
 
 	ch = filp->private_data;
 
@@ -2398,7 +2413,7 @@
 	if (buf == NULL)
 		return -ENOMEM;
 
-	ret = spcom_handle_read(ch, buf, size);
+	ret = spcom_handle_read(ch, buf, buf_size);
 	if (ret < 0) {
 		pr_err("read error [%d].\n", ret);
 		kfree(buf);
@@ -2481,9 +2496,14 @@
 		done = (spcom_dev->link_state == GLINK_LINK_STATE_UP);
 		break;
 	case SPCOM_POLL_CH_CONNECT:
+		/*
+		 * ch is not expected to be NULL since user must call open()
+		 * to get FD before it can call poll().
+		 * open() will fail if no ch related to the char-device.
+		 */
 		if (ch == NULL) {
 			pr_err("invalid ch pointer, file [%s].\n", name);
-			return -EINVAL;
+			return POLLERR;
 		}
 		pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name);
 		if (wait) {
@@ -2784,7 +2804,7 @@
 {
 	int ret;
 
-	pr_info("spcom driver version 1.1 17-July-2017.\n");
+	pr_info("spcom driver version 1.2 23-Aug-2017.\n");
 
 	ret = platform_driver_register(&spcom_driver);
 	if (ret)
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index 01eb260..d65756c 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -1127,23 +1127,55 @@
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"sp2soc_irq_status");
 		d->irq_status = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(d->irq_status)) {
+			dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_status\n");
+			rc = PTR_ERR(d->irq_status);
+			goto err_ramdump;
+		}
+
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"sp2soc_irq_clr");
 		d->irq_clear = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(d->irq_clear)) {
+			dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_clr\n");
+			rc = PTR_ERR(d->irq_clear);
+			goto err_ramdump;
+		}
+
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"sp2soc_irq_mask");
 		d->irq_mask = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(d->irq_mask)) {
+			dev_err(&pdev->dev, "Invalid resource for sp2soc_irq_mask\n");
+			rc = PTR_ERR(d->irq_mask);
+			goto err_ramdump;
+		}
+
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"rmb_err");
 		d->err_status = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(d->err_status)) {
+			dev_err(&pdev->dev, "Invalid resource for rmb_err\n");
+			rc = PTR_ERR(d->err_status);
+			goto err_ramdump;
+		}
+
 		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"rmb_err_spare2");
 		d->err_status_spare = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(d->err_status_spare)) {
+			dev_err(&pdev->dev, "Invalid resource for rmb_err_spare2\n");
+			rc = PTR_ERR(d->err_status_spare);
+			goto err_ramdump;
+		}
+
 		rc = of_property_read_u32_array(pdev->dev.of_node,
 		       "qcom,spss-scsr-bits", d->bits_arr, sizeof(d->bits_arr)/
 							sizeof(d->bits_arr[0]));
-		if (rc)
+		if (rc) {
 			dev_err(&pdev->dev, "Failed to read qcom,spss-scsr-bits");
+			goto err_ramdump;
+		}
 		mask_scsr_irqs(d);
 
 	} else {
@@ -1152,6 +1184,22 @@
 		d->subsys_desc.wdog_bite_handler = subsys_wdog_bite_irq_handler;
 		d->subsys_desc.stop_ack_handler = subsys_stop_ack_intr_handler;
 	}
+	d->desc.signal_aop = of_property_read_bool(pdev->dev.of_node,
+						"qcom,signal-aop");
+	if (d->desc.signal_aop) {
+		d->desc.cl.dev = &pdev->dev;
+		d->desc.cl.tx_block = true;
+		d->desc.cl.tx_tout = 1000;
+		d->desc.cl.knows_txdone = false;
+		d->desc.mbox = mbox_request_channel(&d->desc.cl, 0);
+		if (IS_ERR(d->desc.mbox)) {
+			rc = PTR_ERR(d->desc.mbox);
+			dev_err(&pdev->dev, "Failed to get mailbox channel %pK %d\n",
+				d->desc.mbox, rc);
+			goto err_ramdump;
+		}
+	}
+
 	d->ramdump_dev = create_ramdump_device(d->subsys_desc.name,
 								&pdev->dev);
 	if (!d->ramdump_dev) {
@@ -1170,6 +1218,7 @@
 	destroy_ramdump_device(d->ramdump_dev);
 err_ramdump:
 	pil_desc_release(&d->desc);
+	platform_set_drvdata(pdev, NULL);
 
 	return rc;
 }
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index 55cb604..110cdf7 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -655,13 +655,16 @@
 	if (ret < 0) {
 		notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
 								NULL);
-		if (!dev->desc->ignore_ssr_failure) {
+		if (system_state == SYSTEM_RESTART
+			|| system_state == SYSTEM_POWER_OFF)
+			WARN(1, "SSR aborted: %s, system reboot/shutdown is under way\n",
+				name);
+		else if (!dev->desc->ignore_ssr_failure)
 			panic("[%s:%d]: Powerup error: %s!",
 				current->comm, current->pid, name);
-		} else {
+		else
 			pr_err("Powerup failure on %s\n", name);
-			return ret;
-		}
+		return ret;
 	}
 	enable_all_irqs(dev);
 
@@ -1174,6 +1177,7 @@
 {
 	dev->crashed = crashed;
 }
+EXPORT_SYMBOL(subsys_set_crash_status);
 
 enum crash_status subsys_get_crash_status(struct subsys_device *dev)
 {
diff --git a/drivers/soc/qcom/system_pm.c b/drivers/soc/qcom/system_pm.c
index 2ecbf15..3d978f7 100644
--- a/drivers/soc/qcom/system_pm.c
+++ b/drivers/soc/qcom/system_pm.c
@@ -13,6 +13,7 @@
 
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
+#include <asm/arch_timer.h>
 
 #include <soc/qcom/rpmh.h>
 #include <soc/qcom/system_pm.h>
diff --git a/drivers/soc/qcom/tracer_pkt.c b/drivers/soc/qcom/tracer_pkt.c
index e233055..b699f2f 100644
--- a/drivers/soc/qcom/tracer_pkt.c
+++ b/drivers/soc/qcom/tracer_pkt.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -100,7 +100,7 @@
 	pkt_hdr->reserved = 0;
 	pkt_hdr->id_valid = 0;
 	pkt_hdr->qdss_tracing = qdss_tracing ? true : false;
-	if (pkt_priv_len > MAX_CC_WLEN * sizeof(uint32_t))
+	if (pkt_priv_len >= MAX_CC_WLEN * sizeof(uint32_t))
 		pkt_hdr->ccl = MAX_CC_WLEN;
 	else
 		pkt_hdr->ccl = pkt_priv_len/sizeof(uint32_t) +
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index 8bf5659..9aea6db 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -50,7 +50,7 @@
 #define SCM_SET_REGSAVE_CMD	0x2
 #define SCM_SVC_SEC_WDOG_DIS	0x7
 #define MAX_CPU_CTX_SIZE	2048
-#define MAX_CPU_SCANDUMP_SIZE	0x10000
+#define MAX_CPU_SCANDUMP_SIZE	0x10100
 
 static struct msm_watchdog_data *wdog_data;
 
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
index 2b1456e..c1eafbd 100644
--- a/drivers/spi/spi-axi-spi-engine.c
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -494,7 +494,8 @@
 			SPI_ENGINE_VERSION_MAJOR(version),
 			SPI_ENGINE_VERSION_MINOR(version),
 			SPI_ENGINE_VERSION_PATCH(version));
-		return -ENODEV;
+		ret = -ENODEV;
+		goto err_put_master;
 	}
 
 	spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c
index 14f9dea..7d629b4 100644
--- a/drivers/spi/spi-bcm-qspi.c
+++ b/drivers/spi/spi-bcm-qspi.c
@@ -1215,7 +1215,7 @@
 			goto qspi_probe_err;
 		}
 	} else {
-		goto qspi_probe_err;
+		goto qspi_resource_err;
 	}
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi");
@@ -1237,7 +1237,7 @@
 		qspi->base[CHIP_SELECT]  = devm_ioremap_resource(dev, res);
 		if (IS_ERR(qspi->base[CHIP_SELECT])) {
 			ret = PTR_ERR(qspi->base[CHIP_SELECT]);
-			goto qspi_probe_err;
+			goto qspi_resource_err;
 		}
 	}
 
@@ -1245,7 +1245,7 @@
 				GFP_KERNEL);
 	if (!qspi->dev_ids) {
 		ret = -ENOMEM;
-		goto qspi_probe_err;
+		goto qspi_resource_err;
 	}
 
 	for (val = 0; val < num_irqs; val++) {
@@ -1334,8 +1334,9 @@
 	bcm_qspi_hw_uninit(qspi);
 	clk_disable_unprepare(qspi->clk);
 qspi_probe_err:
-	spi_master_put(master);
 	kfree(qspi->dev_ids);
+qspi_resource_err:
+	spi_master_put(master);
 	return ret;
 }
 /* probe function to be called by SoC specific platform driver probe */
diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 6acb731..7aaf08b 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -24,6 +24,7 @@
 #include <linux/qcom-geni-se.h>
 #include <linux/msm_gpi.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/spi-geni-qcom.h>
 
 #define SPI_NUM_CHIPSELECT	(4)
 #define SPI_XFER_TIMEOUT_MS	(250)
@@ -67,6 +68,11 @@
 /* SPI_TX/SPI_RX_TRANS_LEN fields */
 #define TRANS_LEN_MSK		(GENMASK(23, 0))
 
+/* SE_SPI_DELAY_COUNTERS */
+#define SPI_INTER_WORDS_DELAY_MSK	(GENMASK(9, 0))
+#define SPI_CS_CLK_DELAY_MSK		(GENMASK(19, 10))
+#define SPI_CS_CLK_DELAY_SHFT		(10)
+
 /* M_CMD OP codes for SPI */
 #define SPI_TX_ONLY		(1)
 #define SPI_RX_ONLY		(2)
@@ -192,16 +198,26 @@
 static void spi_setup_word_len(struct spi_geni_master *mas, u32 mode,
 						int bits_per_word)
 {
-	int pack_words = 0;
+	int pack_words = 1;
 	bool msb_first = (mode & SPI_LSB_FIRST) ? false : true;
 	u32 word_len = geni_read_reg(mas->base, SE_SPI_WORD_LEN);
+	unsigned long cfg0, cfg1;
 
+	/*
+	 * If bits_per_word isn't a byte aligned value, set the packing to be
+	 * 1 SPI word per FIFO word.
+	 */
 	if (!(mas->tx_fifo_width % bits_per_word))
 		pack_words = mas->tx_fifo_width / bits_per_word;
 	word_len &= ~WORD_LEN_MSK;
 	word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK);
 	se_config_packing(mas->base, bits_per_word, pack_words, msb_first);
 	geni_write_reg(word_len, mas->base, SE_SPI_WORD_LEN);
+	se_get_packing_config(bits_per_word, pack_words, msb_first,
+							&cfg0, &cfg1);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s: cfg0 %lu cfg1 %lu bpw %d pack_words %d\n", __func__,
+		cfg0, cfg1, bits_per_word, pack_words);
 }
 
 static int setup_fifo_params(struct spi_device *spi_slv,
@@ -219,6 +235,8 @@
 	int ret = 0;
 	int idx;
 	int div;
+	struct spi_geni_qcom_ctrl_data *delay_params = NULL;
+	u32 spi_delay_params = 0;
 
 	loopback_cfg &= ~LOOPBACK_MSK;
 	cpol &= ~CPOL;
@@ -236,6 +254,22 @@
 	if (spi_slv->mode & SPI_CS_HIGH)
 		demux_output_inv |= BIT(spi_slv->chip_select);
 
+	if (spi_slv->controller_data) {
+		u32 cs_clk_delay = 0;
+		u32 inter_words_delay = 0;
+
+		delay_params =
+		(struct spi_geni_qcom_ctrl_data *) spi_slv->controller_data;
+		cs_clk_delay =
+		(delay_params->spi_cs_clk_delay << SPI_CS_CLK_DELAY_SHFT)
+							& SPI_CS_CLK_DELAY_MSK;
+		inter_words_delay =
+			delay_params->spi_inter_words_delay &
+						SPI_INTER_WORDS_DELAY_MSK;
+		spi_delay_params =
+		(inter_words_delay | cs_clk_delay);
+	}
+
 	demux_sel = spi_slv->chip_select;
 	mas->cur_speed_hz = spi_slv->max_speed_hz;
 	mas->cur_word_len = spi_slv->bits_per_word;
@@ -257,6 +291,13 @@
 	geni_write_reg(demux_output_inv, mas->base, SE_SPI_DEMUX_OUTPUT_INV);
 	geni_write_reg(clk_sel, mas->base, SE_GENI_CLK_SEL);
 	geni_write_reg(m_clk_cfg, mas->base, GENI_SER_M_CLK_CFG);
+	geni_write_reg(spi_delay_params, mas->base, SE_SPI_DELAY_COUNTERS);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s:Loopback%d demux_sel0x%x demux_op_inv 0x%x clk_cfg 0x%x\n",
+		__func__, loopback_cfg, demux_sel, demux_output_inv, m_clk_cfg);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s:clk_sel 0x%x cpol %d cpha %d delay 0x%x\n", __func__,
+					clk_sel, cpol, cpha, spi_delay_params);
 	/* Ensure message level attributes are written before returning */
 	mb();
 setup_fifo_params_exit:
@@ -282,7 +323,7 @@
 	 */
 	if (fifo_disable && !dma_chan_valid)
 		mode = -EINVAL;
-	else if (fifo_disable)
+	else if (dma_chan_valid)
 		mode = GSI_DMA;
 	else
 		mode = FIFO_MODE;
@@ -290,7 +331,8 @@
 }
 
 static struct msm_gpi_tre *setup_config0_tre(struct spi_transfer *xfer,
-				struct spi_geni_master *mas, u16 mode)
+				struct spi_geni_master *mas, u16 mode,
+				u32 cs_clk_delay, u32 inter_words_delay)
 {
 	struct msm_gpi_tre *c0_tre = &mas->gsi[mas->num_xfers].config0_tre;
 	u8 flags = 0;
@@ -316,10 +358,7 @@
 		flags |= GSI_CS_TOGGLE;
 
 	word_len = xfer->bits_per_word - MIN_WORD_LEN;
-	if (mas->tx_fifo_width % xfer->bits_per_word)
-		pack = 0;
-	else
-		pack |= (GSI_TX_PACK_EN | GSI_RX_PACK_EN);
+	pack |= (GSI_TX_PACK_EN | GSI_RX_PACK_EN);
 	ret = get_spi_clk_cfg(mas->cur_speed_hz, mas, &idx, &div);
 	if (ret) {
 		dev_err(mas->dev, "%s:Err setting clks:%d\n", __func__, ret);
@@ -327,12 +366,16 @@
 	}
 	c0_tre->dword[0] = MSM_GPI_SPI_CONFIG0_TRE_DWORD0(pack, flags,
 								word_len);
-	c0_tre->dword[1] = MSM_GPI_SPI_CONFIG0_TRE_DWORD1(0, 0, 0);
+	c0_tre->dword[1] = MSM_GPI_SPI_CONFIG0_TRE_DWORD1(0, cs_clk_delay,
+							inter_words_delay);
 	c0_tre->dword[2] = MSM_GPI_SPI_CONFIG0_TRE_DWORD2(idx, div);
 	c0_tre->dword[3] = MSM_GPI_SPI_CONFIG0_TRE_DWORD3(0, 0, 0, 1);
 	GENI_SE_DBG(mas->ipc, false, mas->dev,
 		"%s: flags 0x%x word %d pack %d idx %d div %d\n",
 		__func__, flags, word_len, pack, idx, div);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s: cs_clk_delay %d inter_words_delay %d\n", __func__,
+				 cs_clk_delay, inter_words_delay);
 	return c0_tre;
 }
 
@@ -490,13 +533,27 @@
 	u32 rx_len = 0;
 	int go_flags = 0;
 	unsigned long flags = DMA_PREP_INTERRUPT | DMA_CTRL_ACK;
+	struct spi_geni_qcom_ctrl_data *delay_params = NULL;
+	u32 cs_clk_delay = 0;
+	u32 inter_words_delay = 0;
+
+	if (spi_slv->controller_data) {
+		delay_params =
+		(struct spi_geni_qcom_ctrl_data *) spi_slv->controller_data;
+
+		cs_clk_delay =
+			delay_params->spi_cs_clk_delay;
+		inter_words_delay =
+			delay_params->spi_inter_words_delay;
+	}
 
 	if ((xfer->bits_per_word != mas->cur_word_len) ||
 		(xfer->speed_hz != mas->cur_speed_hz)) {
 		mas->cur_word_len = xfer->bits_per_word;
 		mas->cur_speed_hz = xfer->speed_hz;
 		tx_nent++;
-		c0_tre = setup_config0_tre(xfer, mas, spi_slv->mode);
+		c0_tre = setup_config0_tre(xfer, mas, spi_slv->mode,
+					cs_clk_delay, inter_words_delay);
 		if (IS_ERR_OR_NULL(c0_tre)) {
 			dev_err(mas->dev, "%s:Err setting c0tre:%d\n",
 							__func__, ret);
@@ -504,19 +561,26 @@
 		}
 	}
 
+	if (!(mas->cur_word_len % MIN_WORD_LEN)) {
+		rx_len = ((xfer->len << 3) / mas->cur_word_len);
+	} else {
+		int bytes_per_word = (mas->cur_word_len / BITS_PER_BYTE) + 1;
+
+		rx_len = (xfer->len / bytes_per_word);
+	}
+
 	if (xfer->tx_buf && xfer->rx_buf) {
 		cmd = SPI_FULL_DUPLEX;
 		tx_nent += 2;
 		rx_nent++;
-		rx_len = ((xfer->len << 3) / mas->cur_word_len);
 	} else if (xfer->tx_buf) {
 		cmd = SPI_TX_ONLY;
 		tx_nent += 2;
+		rx_len = 0;
 	} else if (xfer->rx_buf) {
 		cmd = SPI_RX_ONLY;
 		tx_nent++;
 		rx_nent++;
-		rx_len = ((xfer->len << 3) / mas->cur_word_len);
 	}
 
 	cs |= spi_slv->chip_select;
@@ -597,30 +661,32 @@
 				struct spi_message *msg)
 {
 	struct spi_transfer *xfer;
-	struct device *gsi_dev = mas->dev;
+	int ret = 0;
 
 	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 		if (xfer->rx_buf) {
-			xfer->rx_dma = dma_map_single(gsi_dev, xfer->rx_buf,
+			ret = geni_se_iommu_map_buf(mas->wrapper_dev,
+						&xfer->rx_dma, xfer->rx_buf,
 						xfer->len, DMA_FROM_DEVICE);
-			if (dma_mapping_error(mas->dev, xfer->rx_dma)) {
-				dev_err(mas->dev, "Err mapping buf\n");
-				return -ENOMEM;
+			if (ret) {
+				GENI_SE_ERR(mas->ipc, true, mas->dev,
+				"%s: Mapping Rx buffer %d\n", __func__, ret);
+				return ret;
 			}
 		}
 
 		if (xfer->tx_buf) {
-			xfer->tx_dma = dma_map_single(gsi_dev,
-				(void *)xfer->tx_buf, xfer->len, DMA_TO_DEVICE);
-			if (dma_mapping_error(gsi_dev, xfer->tx_dma)) {
-				dev_err(mas->dev, "Err mapping buf\n");
-				dma_unmap_single(gsi_dev, xfer->rx_dma,
-						xfer->len, DMA_FROM_DEVICE);
-				return -ENOMEM;
+			ret = geni_se_iommu_map_buf(mas->wrapper_dev,
+						&xfer->tx_dma,
+						(void *)xfer->tx_buf,
+						xfer->len, DMA_TO_DEVICE);
+			if (ret) {
+				GENI_SE_ERR(mas->ipc, true, mas->dev,
+				"%s: Mapping Tx buffer %d\n", __func__, ret);
+				return ret;
 			}
 		}
 	};
-
 	return 0;
 }
 
@@ -628,14 +694,13 @@
 				struct spi_message *msg)
 {
 	struct spi_transfer *xfer;
-	struct device *gsi_dev = mas->dev;
 
 	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
 		if (xfer->rx_buf)
-			dma_unmap_single(gsi_dev, xfer->rx_dma,
+			geni_se_iommu_unmap_buf(mas->wrapper_dev, &xfer->rx_dma,
 						xfer->len, DMA_FROM_DEVICE);
 		if (xfer->tx_buf)
-			dma_unmap_single(gsi_dev, xfer->tx_dma,
+			geni_se_iommu_unmap_buf(mas->wrapper_dev, &xfer->tx_dma,
 						xfer->len, DMA_TO_DEVICE);
 	};
 }
@@ -651,7 +716,7 @@
 	if (mas->cur_xfer_mode == FIFO_MODE) {
 		geni_se_select_mode(mas->base, FIFO_MODE);
 		reinit_completion(&mas->xfer_done);
-		setup_fifo_params(spi_msg->spi, spi);
+		ret = setup_fifo_params(spi_msg->spi, spi);
 	} else if (mas->cur_xfer_mode == GSI_DMA) {
 		mas->num_tx_eot = 0;
 		mas->num_rx_eot = 0;
@@ -689,7 +754,12 @@
 {
 	struct spi_geni_master *mas = spi_master_get_devdata(spi);
 	int ret = 0;
+	u32 max_speed = spi->cur_msg->spi->max_speed_hz;
+	struct se_geni_rsc *rsc = &mas->spi_rsc;
 
+	/* Adjust the AB/IB based on the max speed of the slave.*/
+	rsc->ib = max_speed * DEFAULT_BUS_WIDTH;
+	rsc->ab = max_speed * DEFAULT_BUS_WIDTH;
 	ret = pm_runtime_get_sync(mas->dev);
 	if (ret < 0) {
 		dev_err(mas->dev, "Error enabling SE resources\n");
@@ -844,7 +914,14 @@
 	spi_tx_cfg &= ~CS_TOGGLE;
 	if (xfer->cs_change)
 		spi_tx_cfg |= CS_TOGGLE;
-	trans_len = ((xfer->len << 3) / mas->cur_word_len) & TRANS_LEN_MSK;
+	if (!(mas->cur_word_len % MIN_WORD_LEN)) {
+		trans_len =
+			((xfer->len << 3) / mas->cur_word_len) & TRANS_LEN_MSK;
+	} else {
+		int bytes_per_word = (mas->cur_word_len / BITS_PER_BYTE) + 1;
+
+		trans_len = (xfer->len / bytes_per_word) & TRANS_LEN_MSK;
+	}
 	if (!list_is_last(&xfer->transfer_list, &spi->cur_msg->transfers))
 		m_param |= FRAGMENTATION;
 
@@ -860,6 +937,9 @@
 	}
 	geni_write_reg(spi_tx_cfg, mas->base, SE_SPI_TRANS_CFG);
 	geni_setup_m_cmd(mas->base, m_cmd, m_param);
+	GENI_SE_DBG(mas->ipc, false, mas->dev,
+		"%s: trans_len %d xferlen%d tx_cfg 0x%x cmd 0x%x\n",
+		__func__, trans_len, xfer->len, spi_tx_cfg, m_cmd);
 	if (m_cmd & SPI_TX_ONLY)
 		geni_write_reg(mas->tx_wm, mas->base, SE_GENI_TX_WATERMARK_REG);
 	/* Ensure all writes are done before the WM interrupt */
@@ -870,8 +950,10 @@
 {
 	unsigned long timeout;
 
+	geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
 	reinit_completion(&mas->xfer_done);
 	geni_cancel_m_cmd(mas->base);
+	geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
 	/* Ensure cmd cancel is written */
 	mb();
 	timeout = wait_for_completion_timeout(&mas->xfer_done, HZ);
@@ -955,6 +1037,7 @@
 	}
 	return ret;
 err_gsi_geni_transfer_one:
+	geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc);
 	dmaengine_terminate_all(mas->tx);
 	return ret;
 err_fifo_geni_transfer_one:
@@ -966,12 +1049,25 @@
 {
 	int i = 0;
 	int tx_fifo_width = (mas->tx_fifo_width >> 3);
-	int max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * tx_fifo_width;
+	int max_bytes = 0;
 	const u8 *tx_buf = NULL;
 
 	if (!mas->cur_xfer)
 		return;
 
+	/*
+	 * For non-byte aligned bits-per-word values:
+	 * Assumption is that each SPI word will be accomodated in
+	 * ceil (bits_per_word / bits_per_byte)
+	 * and the next SPI word starts at the next byte.
+	 * In such cases, we can fit 1 SPI word per FIFO word so adjust the
+	 * max byte that can be sent per IRQ accordingly.
+	 */
+	if ((mas->tx_fifo_width % mas->cur_word_len))
+		max_bytes = (mas->tx_fifo_depth - mas->tx_wm) *
+				((mas->cur_word_len / BITS_PER_BYTE) + 1);
+	else
+		max_bytes = (mas->tx_fifo_depth - mas->tx_wm) * tx_fifo_width;
 	tx_buf = mas->cur_xfer->tx_buf;
 	tx_buf += (mas->cur_xfer->len - mas->tx_rem_bytes);
 	max_bytes = min_t(int, mas->tx_rem_bytes, max_bytes);
@@ -979,8 +1075,13 @@
 		int j;
 		u32 fifo_word = 0;
 		u8 *fifo_byte;
-		int bytes_to_write = min_t(int, (max_bytes - i), tx_fifo_width);
+		int bytes_per_fifo = tx_fifo_width;
+		int bytes_to_write = 0;
 
+		if ((mas->tx_fifo_width % mas->cur_word_len))
+			bytes_per_fifo =
+				(mas->cur_word_len / BITS_PER_BYTE) + 1;
+		bytes_to_write = min_t(int, (max_bytes - i), bytes_per_fifo);
 		fifo_byte = (u8 *)&fifo_word;
 		for (j = 0; j < bytes_to_write; j++)
 			fifo_byte[j] = tx_buf[i++];
@@ -1019,15 +1120,24 @@
 			rx_bytes += rx_last_byte_valid;
 		}
 	}
-	rx_bytes += rx_wc * fifo_width;
+	if (!(mas->tx_fifo_width % mas->cur_word_len))
+		rx_bytes += rx_wc * fifo_width;
+	else
+		rx_bytes += rx_wc *
+			((mas->cur_word_len / BITS_PER_BYTE) + 1);
 	rx_bytes = min_t(int, mas->rx_rem_bytes, rx_bytes);
 	rx_buf += (mas->cur_xfer->len - mas->rx_rem_bytes);
 	while (i < rx_bytes) {
 		u32 fifo_word = 0;
 		u8 *fifo_byte;
-		int read_bytes = min_t(int, (rx_bytes - i), fifo_width);
+		int bytes_per_fifo = fifo_width;
+		int read_bytes = 0;
 		int j;
 
+		if ((mas->tx_fifo_width % mas->cur_word_len))
+			bytes_per_fifo =
+				(mas->cur_word_len / BITS_PER_BYTE) + 1;
+		read_bytes = min_t(int, (rx_bytes - i), bytes_per_fifo);
 		fifo_word = geni_read_reg(mas->base, SE_GENI_RX_FIFOn);
 		fifo_byte = (u8 *)&fifo_word;
 		for (j = 0; j < read_bytes; j++)
@@ -1039,8 +1149,14 @@
 static irqreturn_t geni_spi_irq(int irq, void *dev)
 {
 	struct spi_geni_master *mas = dev;
-	u32 m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
+	u32 m_irq = 0;
 
+	if (pm_runtime_status_suspended(dev)) {
+		GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s: device is suspended\n", __func__);
+		goto exit_geni_spi_irq;
+	}
+	m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS);
 	if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN))
 		geni_spi_handle_rx(mas);
 
@@ -1050,7 +1166,26 @@
 	if ((m_irq & M_CMD_DONE_EN) || (m_irq & M_CMD_CANCEL_EN) ||
 		(m_irq & M_CMD_ABORT_EN)) {
 		complete(&mas->xfer_done);
+		/*
+		 * If this happens, then a CMD_DONE came before all the buffer
+		 * bytes were sent out. This is unusual, log this condition and
+		 * disable the WM interrupt to prevent the system from stalling
+		 * due an interrupt storm.
+		 * If this happens when all Rx bytes haven't been received, log
+		 * the condition.
+		 */
+		if (mas->tx_rem_bytes) {
+			geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG);
+			GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s:Premature Done.tx_rem%d bpw%d\n",
+				__func__, mas->tx_rem_bytes, mas->cur_word_len);
+		}
+		if (mas->rx_rem_bytes)
+			GENI_SE_DBG(mas->ipc, false, mas->dev,
+				"%s:Premature Done.rx_rem%d bpw%d\n",
+				__func__, mas->rx_rem_bytes, mas->cur_word_len);
 	}
+exit_geni_spi_irq:
 	geni_write_reg(m_irq, mas->base, SE_GENI_M_IRQ_CLEAR);
 	return IRQ_HANDLED;
 }
@@ -1064,6 +1199,7 @@
 	struct resource *res;
 	struct platform_device *wrapper_pdev;
 	struct device_node *wrapper_ph_node;
+	bool rt_pri;
 
 	spi = spi_alloc_master(&pdev->dev, sizeof(struct spi_geni_master));
 	if (!spi) {
@@ -1158,6 +1294,10 @@
 		goto spi_geni_probe_err;
 	}
 
+	rt_pri = of_property_read_bool(pdev->dev.of_node, "qcom,rt");
+	if (rt_pri)
+		spi->rt = true;
+
 	geni_mas->phys_addr = res->start;
 	geni_mas->size = resource_size(res);
 	geni_mas->base = devm_ioremap(&pdev->dev, res->start,
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index d6239fa..3f3751e 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1458,6 +1458,10 @@
 	{ PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP },
 	{ PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP },
 	{ PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP },
+	/* GLK */
+	{ PCI_VDEVICE(INTEL, 0x31c2), LPSS_BXT_SSP },
+	{ PCI_VDEVICE(INTEL, 0x31c4), LPSS_BXT_SSP },
+	{ PCI_VDEVICE(INTEL, 0x31c6), LPSS_BXT_SSP },
 	/* APL */
 	{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },
 	{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP },
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 7d9a2e8..d6089aa 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -59,6 +59,8 @@
 #define SPMI_OWNERSHIP_TABLE_REG(N)	(0x0700 + (4 * (N)))
 #define SPMI_OWNERSHIP_PERIPH2OWNER(X)	((X) & 0x7)
 
+#define SPMI_PROTOCOL_IRQ_STATUS	0x6000
+
 /* Channel Status fields */
 enum pmic_arb_chnl_status {
 	PMIC_ARB_STATUS_DONE	= BIT(0),
@@ -148,6 +150,8 @@
  * @ver_ops:		version dependent operations.
  * @ppid_to_apid	in-memory copy of PPID -> channel (APID) mapping table.
  *			v2 only.
+ * @ahb_bus_wa:		Use AHB bus workaround to avoid write transaction
+ *			corruption on some PMIC arbiter v5 platforms.
  */
 struct spmi_pmic_arb {
 	void __iomem		*rd_base;
@@ -172,6 +176,7 @@
 	u16			*ppid_to_apid;
 	u16			last_apid;
 	struct apid_data	apid_data[PMIC_ARB_MAX_PERIPHS];
+	bool			ahb_bus_wa;
 };
 
 /**
@@ -217,6 +222,16 @@
 static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa,
 				       u32 offset, u32 val)
 {
+	if (pa->ahb_bus_wa) {
+		/* AHB bus register dummy read for workaround. */
+		readl_relaxed(pa->cnfg + SPMI_PROTOCOL_IRQ_STATUS);
+		/*
+		 * Ensure that the read completes before initiating the
+		 * subsequent register write.
+		 */
+		mb();
+	}
+
 	writel_relaxed(val, pa->wr_base + offset);
 }
 
@@ -929,8 +944,8 @@
 	 * multiple EE's to write to a single PPID in arbiter version 5, there
 	 * is more than one APID mapped to each PPID.  The owner field for each
 	 * of these mappings specifies the EE which is allowed to write to the
-	 * APID.  The owner of the last (highest) APID for a given PPID will
-	 * receive interrupts from the PPID.
+	 * APID.  The owner of the last (highest) APID which has the IRQ owner
+	 * bit set for a given PPID will receive interrupts from the PPID.
 	 */
 	for (apid = 0; apid < pa->max_periph; apid++) {
 		offset = pa->ver_ops->channel_map_offset(apid);
@@ -954,7 +969,10 @@
 		valid = pa->ppid_to_apid[ppid] & PMIC_ARB_CHAN_VALID;
 		prev_apid = pa->ppid_to_apid[ppid] & ~PMIC_ARB_CHAN_VALID;
 
-		if (valid && is_irq_owner &&
+		if (!valid || pa->apid_data[apid].write_owner == pa->ee) {
+			/* First PPID mapping or one for this EE */
+			pa->ppid_to_apid[ppid] = apid | PMIC_ARB_CHAN_VALID;
+		} else if (valid && is_irq_owner &&
 		    pa->apid_data[prev_apid].write_owner == pa->ee) {
 			/*
 			 * Duplicate PPID mapping after the one for this EE;
@@ -962,9 +980,6 @@
 			 */
 			pa->apid_data[prev_apid].irq_owner
 				= pa->apid_data[apid].irq_owner;
-		} else if (!valid || is_irq_owner) {
-			/* First PPID mapping or duplicate for another EE */
-			pa->ppid_to_apid[ppid] = apid | PMIC_ARB_CHAN_VALID;
 		}
 
 		pa->apid_data[apid].ppid = ppid;
@@ -1343,6 +1358,9 @@
 
 	pa->ee = ee;
 
+	pa->ahb_bus_wa = of_property_read_bool(pdev->dev.of_node,
+					"qcom,enable-ahb-bus-workaround");
+
 	pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1,
 					sizeof(*pa->mapping_table), GFP_KERNEL);
 	if (!pa->mapping_table) {
diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c
index ce9dc7e..192661b 100644
--- a/drivers/staging/android/fiq_debugger/fiq_debugger.c
+++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c
@@ -401,7 +401,7 @@
 		cmd += 6;
 		while (*cmd == ' ')
 			cmd++;
-		if ((cmd != '\0') && sysrq_on())
+		if ((*cmd != '\0') && sysrq_on())
 			kernel_restart(cmd);
 		else
 			kernel_restart(NULL);
diff --git a/drivers/staging/android/ion/ion.c b/drivers/staging/android/ion/ion.c
index 0948c22..720ac31 100644
--- a/drivers/staging/android/ion/ion.c
+++ b/drivers/staging/android/ion/ion.c
@@ -215,7 +215,6 @@
 
 	buffer->dev = dev;
 	buffer->size = len;
-	buffer->flags = flags;
 	INIT_LIST_HEAD(&buffer->vmas);
 
 	table = heap->ops->map_dma(heap, buffer);
diff --git a/drivers/staging/android/ion/ion_cma_heap.c b/drivers/staging/android/ion/ion_cma_heap.c
index b264ec2..72f2b6a 100644
--- a/drivers/staging/android/ion/ion_cma_heap.c
+++ b/drivers/staging/android/ion/ion_cma_heap.c
@@ -23,6 +23,7 @@
 #include <linux/err.h>
 #include <linux/dma-mapping.h>
 #include <linux/msm_ion.h>
+#include <linux/of.h>
 
 #include <asm/cacheflush.h>
 #include <soc/qcom/secure_buffer.h>
@@ -56,9 +57,22 @@
 		return ret;
 
 	sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
+	sg_dma_address(sgt->sgl) = sg_phys(sgt->sgl);
 	return 0;
 }
 
+static bool ion_cma_has_kernel_mapping(struct ion_heap *heap)
+{
+	struct device *dev = heap->priv;
+	struct device_node *mem_region;
+
+	mem_region = of_parse_phandle(dev->of_node, "memory-region", 0);
+	if (IS_ERR(mem_region))
+		return false;
+
+	return !of_property_read_bool(mem_region, "no-map");
+}
+
 /* ION CMA heap operations functions */
 static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
 			    unsigned long len, unsigned long align,
@@ -73,14 +87,20 @@
 	if (!info)
 		return ION_CMA_ALLOCATE_FAILED;
 
+	/* Override flags if cached-mappings are not supported */
+	if (!ion_cma_has_kernel_mapping(heap)) {
+		flags &= ~((unsigned long)ION_FLAG_CACHED);
+		buffer->flags = flags;
+	}
+
 	if (!ION_IS_CACHED(flags))
 		info->cpu_addr = dma_alloc_writecombine(dev, len,
 							&info->handle,
 							GFP_KERNEL);
 	else
-		info->cpu_addr = dma_alloc_nonconsistent(dev, len,
-							 &info->handle,
-							 GFP_KERNEL);
+		info->cpu_addr = dma_alloc_attrs(dev, len, &info->handle,
+						GFP_KERNEL,
+						DMA_ATTR_FORCE_COHERENT);
 
 	if (!info->cpu_addr) {
 		dev_err(dev, "Fail to allocate buffer\n");
@@ -96,6 +116,11 @@
 	ion_cma_get_sgtable(dev,
 			    info->table, info->cpu_addr, info->handle, len);
 
+	/* Ensure memory is dma-ready - refer to ion_buffer_create() */
+	if (info->is_cached)
+		dma_sync_sg_for_device(dev, info->table->sgl,
+				       info->table->nents, DMA_BIDIRECTIONAL);
+
 	/* keep this for memory release */
 	buffer->priv_virt = info;
 	dev_dbg(dev, "Allocate buffer %pK\n", buffer);
@@ -110,10 +135,13 @@
 {
 	struct device *dev = buffer->heap->priv;
 	struct ion_cma_buffer_info *info = buffer->priv_virt;
+	unsigned long attrs = 0;
 
 	dev_dbg(dev, "Release buffer %pK\n", buffer);
 	/* release memory */
-	dma_free_coherent(dev, buffer->size, info->cpu_addr, info->handle);
+	if (info->is_cached)
+		attrs |= DMA_ATTR_FORCE_COHERENT;
+	dma_free_attrs(dev, buffer->size, info->cpu_addr, info->handle, attrs);
 	sg_free_table(info->table);
 	/* release sg table */
 	kfree(info->table);
@@ -156,8 +184,9 @@
 	struct ion_cma_buffer_info *info = buffer->priv_virt;
 
 	if (info->is_cached)
-		return dma_mmap_nonconsistent(dev, vma, info->cpu_addr,
-				info->handle, buffer->size);
+		return dma_mmap_attrs(dev, vma, info->cpu_addr,
+				info->handle, buffer->size,
+				DMA_ATTR_FORCE_COHERENT);
 	else
 		return dma_mmap_writecombine(dev, vma, info->cpu_addr,
 				info->handle, buffer->size);
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 616375a..8d67f76 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -48,6 +48,7 @@
 #include <linux/fs.h>
 #include <linux/cpuset.h>
 #include <linux/vmpressure.h>
+#include <linux/freezer.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/almk.h>
@@ -116,6 +117,10 @@
 static int vmpressure_file_min;
 module_param_named(vmpressure_file_min, vmpressure_file_min, int, 0644);
 
+/* User knob to enable/disable oom reaping feature */
+static int oom_reaper;
+module_param_named(oom_reaper, oom_reaper, int, 0644);
+
 enum {
 	VMPRESSURE_NO_ADJUST = 0,
 	VMPRESSURE_ADJUST_ENCROACH,
@@ -406,6 +411,14 @@
 	}
 }
 
+static void mark_lmk_victim(struct task_struct *tsk)
+{
+	struct mm_struct *mm = tsk->mm;
+
+	if (!cmpxchg(&tsk->signal->oom_mm, NULL, mm))
+		atomic_inc(&tsk->signal->oom_mm->mm_count);
+}
+
 static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
 {
 	struct task_struct *tsk;
@@ -524,7 +537,11 @@
 		send_sig(SIGKILL, selected, 0);
 		if (selected->mm)
 			task_set_lmk_waiting(selected);
+		if (oom_reaper)
+			mark_lmk_victim(selected);
 		task_unlock(selected);
+		if (oom_reaper)
+			wake_oom_reaper(selected);
 		trace_lowmemory_kill(selected, cache_size, cache_limit, free);
 		lowmem_print(1, "Killing '%s' (%d) (tgid %d), adj %hd,\n"
 			"to free %ldkB on behalf of '%s' (%d) because\n"
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index ec99790..7458df4 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -2385,6 +2385,7 @@
 			continue;
 		}
 
+		set_current_state(TASK_RUNNING);
 		wp = async->buf_write_ptr;
 		n1 = min(n, async->prealloc_bufsz - wp);
 		n2 = n - n1;
@@ -2517,6 +2518,8 @@
 			}
 			continue;
 		}
+
+		set_current_state(TASK_RUNNING);
 		rp = async->buf_read_ptr;
 		n1 = min(n, async->prealloc_bufsz - rp);
 		n2 = n - n1;
diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-msi.c b/drivers/staging/fsl-mc/bus/fsl-mc-msi.c
index 3d46b1b..7de992c 100644
--- a/drivers/staging/fsl-mc/bus/fsl-mc-msi.c
+++ b/drivers/staging/fsl-mc/bus/fsl-mc-msi.c
@@ -17,6 +17,7 @@
 #include <linux/irqdomain.h>
 #include <linux/msi.h>
 #include "../include/mc-bus.h"
+#include "fsl-mc-private.h"
 
 /*
  * Generate a unique ID identifying the interrupt (only used within the MSI
diff --git a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c
index 7a6ac64..eaeb3c5 100644
--- a/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c
+++ b/drivers/staging/fsl-mc/bus/irq-gic-v3-its-fsl-mc-msi.c
@@ -17,6 +17,7 @@
 #include <linux/of.h>
 #include <linux/of_irq.h>
 #include "../include/mc-bus.h"
+#include "fsl-mc-private.h"
 
 static struct irq_chip its_msi_irq_chip = {
 	.name = "fsl-mc-bus-msi",
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 1cf6b79..eeacb0e 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -222,11 +222,9 @@
 	struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi);
 	unsigned long long scale_uv;
 	int i, ret, id;
-	u8 ones[6];
 
 	/* reset the serial interface */
-	memset(&ones, 0xFF, 6);
-	ret = spi_write(st->sd.spi, &ones, 6);
+	ret = ad_sd_reset(&st->sd, 48);
 	if (ret < 0)
 		goto out;
 	usleep_range(500, 1000); /* Wait for at least 500us */
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
index 6b99263..598f0fa 100644
--- a/drivers/staging/iio/resolver/ad2s1210.c
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -472,7 +472,7 @@
 			     long m)
 {
 	struct ad2s1210_state *st = iio_priv(indio_dev);
-	bool negative;
+	u16 negative;
 	int ret = 0;
 	u16 pos;
 	s16 vel;
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
index 38dca69..ce500a5 100644
--- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
@@ -260,7 +260,7 @@
 out1:
 	iio_trigger_unregister(st->trig);
 out:
-	iio_trigger_put(st->trig);
+	iio_trigger_free(st->trig);
 	return ret;
 }
 
@@ -273,7 +273,7 @@
 		peripheral_free(st->t->pin);
 	free_irq(st->irq, st);
 	iio_trigger_unregister(st->trig);
-	iio_trigger_put(st->trig);
+	iio_trigger_free(st->trig);
 
 	return 0;
 }
diff --git a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
index 6fc9855..e533088 100644
--- a/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
+++ b/drivers/staging/lustre/lustre/include/lustre/lustre_user.h
@@ -1213,23 +1213,21 @@
  * \retval buffer
  */
 static inline char *hai_dump_data_field(struct hsm_action_item *hai,
-					char *buffer, int len)
+					char *buffer, size_t len)
 {
-	int i, sz, data_len;
+	int i, data_len;
 	char *ptr;
 
 	ptr = buffer;
-	sz = len;
 	data_len = hai->hai_len - sizeof(*hai);
-	for (i = 0 ; (i < data_len) && (sz > 0) ; i++) {
-		int cnt;
-
-		cnt = snprintf(ptr, sz, "%.2X",
-			       (unsigned char)hai->hai_data[i]);
-		ptr += cnt;
-		sz -= cnt;
+	for (i = 0; (i < data_len) && (len > 2); i++) {
+		snprintf(ptr, 3, "%02X", (unsigned char)hai->hai_data[i]);
+		ptr += 2;
+		len -= 2;
 	}
+
 	*ptr = '\0';
+
 	return buffer;
 }
 
diff --git a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
index 3c48b4f..d18ab3f 100644
--- a/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
+++ b/drivers/staging/lustre/lustre/ldlm/ldlm_lock.c
@@ -546,6 +546,13 @@
 	if (!lock)
 		return NULL;
 
+	if (lock->l_export && lock->l_export->exp_failed) {
+		CDEBUG(D_INFO, "lock export failed: lock %p, exp %p\n",
+		       lock, lock->l_export);
+		LDLM_LOCK_PUT(lock);
+		return NULL;
+	}
+
 	/* It's unlikely but possible that someone marked the lock as
 	 * destroyed after we did handle2object on it
 	 */
diff --git a/drivers/staging/lustre/lustre/llite/rw26.c b/drivers/staging/lustre/lustre/llite/rw26.c
index 26f3a37..0cb70c3 100644
--- a/drivers/staging/lustre/lustre/llite/rw26.c
+++ b/drivers/staging/lustre/lustre/llite/rw26.c
@@ -354,6 +354,10 @@
 	if (!lli->lli_has_smd)
 		return -EBADF;
 
+	/* Check EOF by ourselves */
+	if (iov_iter_rw(iter) == READ && file_offset >= i_size_read(inode))
+		return 0;
+
 	/* FIXME: io smaller than PAGE_SIZE is broken on ia64 ??? */
 	if ((file_offset & ~PAGE_MASK) || (count & ~PAGE_MASK))
 		return -EINVAL;
diff --git a/drivers/staging/lustre/lustre/lmv/lmv_obd.c b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
index 7dbb2b9..cd19ce8 100644
--- a/drivers/staging/lustre/lustre/lmv/lmv_obd.c
+++ b/drivers/staging/lustre/lustre/lmv/lmv_obd.c
@@ -744,16 +744,18 @@
 	/* count how many requests must be sent to the given target */
 	for (i = 0; i < hur->hur_request.hr_itemcount; i++) {
 		curr_tgt = lmv_find_target(lmv, &hur->hur_user_item[i].hui_fid);
+		if (IS_ERR(curr_tgt))
+			return PTR_ERR(curr_tgt);
 		if (obd_uuid_equals(&curr_tgt->ltd_uuid, &tgt_mds->ltd_uuid))
 			nr++;
 	}
 	return nr;
 }
 
-static void lmv_hsm_req_build(struct lmv_obd *lmv,
-			      struct hsm_user_request *hur_in,
-			      const struct lmv_tgt_desc *tgt_mds,
-			      struct hsm_user_request *hur_out)
+static int lmv_hsm_req_build(struct lmv_obd *lmv,
+			     struct hsm_user_request *hur_in,
+			     const struct lmv_tgt_desc *tgt_mds,
+			     struct hsm_user_request *hur_out)
 {
 	int			i, nr_out;
 	struct lmv_tgt_desc    *curr_tgt;
@@ -764,6 +766,8 @@
 	for (i = 0; i < hur_in->hur_request.hr_itemcount; i++) {
 		curr_tgt = lmv_find_target(lmv,
 					   &hur_in->hur_user_item[i].hui_fid);
+		if (IS_ERR(curr_tgt))
+			return PTR_ERR(curr_tgt);
 		if (obd_uuid_equals(&curr_tgt->ltd_uuid, &tgt_mds->ltd_uuid)) {
 			hur_out->hur_user_item[nr_out] =
 				hur_in->hur_user_item[i];
@@ -773,6 +777,8 @@
 	hur_out->hur_request.hr_itemcount = nr_out;
 	memcpy(hur_data(hur_out), hur_data(hur_in),
 	       hur_in->hur_request.hr_data_len);
+
+	return 0;
 }
 
 static int lmv_hsm_ct_unregister(struct lmv_obd *lmv, unsigned int cmd, int len,
@@ -1052,15 +1058,17 @@
 		} else {
 			/* split fid list to their respective MDS */
 			for (i = 0; i < count; i++) {
-				unsigned int		nr, reqlen;
-				int			rc1;
 				struct hsm_user_request *req;
+				size_t reqlen;
+				int nr, rc1;
 
 				tgt = lmv->tgts[i];
 				if (!tgt || !tgt->ltd_exp)
 					continue;
 
 				nr = lmv_hsm_req_count(lmv, hur, tgt);
+				if (nr < 0)
+					return nr;
 				if (nr == 0) /* nothing for this MDS */
 					continue;
 
@@ -1072,10 +1080,13 @@
 				if (!req)
 					return -ENOMEM;
 
-				lmv_hsm_req_build(lmv, hur, tgt, req);
+				rc1 = lmv_hsm_req_build(lmv, hur, tgt, req);
+				if (rc1 < 0)
+					goto hsm_req_err;
 
 				rc1 = obd_iocontrol(cmd, tgt->ltd_exp, reqlen,
 						    req, uarg);
+hsm_req_err:
 				if (rc1 != 0 && rc == 0)
 					rc = rc1;
 				kvfree(req);
diff --git a/drivers/staging/lustre/lustre/ptlrpc/service.c b/drivers/staging/lustre/lustre/ptlrpc/service.c
index 72f3930..9d34848 100644
--- a/drivers/staging/lustre/lustre/ptlrpc/service.c
+++ b/drivers/staging/lustre/lustre/ptlrpc/service.c
@@ -1264,20 +1264,15 @@
 		 */
 		if (req->rq_ops->hpreq_check) {
 			rc = req->rq_ops->hpreq_check(req);
-			/**
-			 * XXX: Out of all current
-			 * ptlrpc_hpreq_ops::hpreq_check(), only
-			 * ldlm_cancel_hpreq_check() can return an error code;
-			 * other functions assert in similar places, which seems
-			 * odd. What also does not seem right is that handlers
-			 * for those RPCs do not assert on the same checks, but
-			 * rather handle the error cases. e.g. see
-			 * ost_rw_hpreq_check(), and ost_brw_read(),
-			 * ost_brw_write().
+			if (rc == -ESTALE) {
+				req->rq_status = rc;
+				ptlrpc_error(req);
+			}
+			/** can only return error,
+			 * 0 for normal request,
+			 *  or 1 for high priority request
 			 */
-			if (rc < 0)
-				return rc;
-			LASSERT(rc == 0 || rc == 1);
+			LASSERT(rc <= 1);
 		}
 
 		spin_lock_bh(&req->rq_export->exp_rpc_lock);
diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
index b432153..0f63a36 100644
--- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c
+++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c
@@ -45,6 +45,7 @@
 	{USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */
 	{USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */
 	{USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */
+	{USB_DEVICE(USB_VENDER_ID_REALTEK, 0xffef)}, /* Rosewill RNX-N150NUB */
 	{}	/* Terminating entry */
 };
 
diff --git a/drivers/staging/rtl8712/ieee80211.h b/drivers/staging/rtl8712/ieee80211.h
index 67ab580..68fd65e 100644
--- a/drivers/staging/rtl8712/ieee80211.h
+++ b/drivers/staging/rtl8712/ieee80211.h
@@ -138,51 +138,51 @@
 };
 
 struct ieee80211_hdr {
-	u16 frame_ctl;
-	u16 duration_id;
+	__le16 frame_ctl;
+	__le16 duration_id;
 	u8 addr1[ETH_ALEN];
 	u8 addr2[ETH_ALEN];
 	u8 addr3[ETH_ALEN];
-	u16 seq_ctl;
+	__le16 seq_ctl;
 	u8 addr4[ETH_ALEN];
-} __packed;
+}  __packed __aligned(2);
 
 struct ieee80211_hdr_3addr {
-	u16 frame_ctl;
-	u16 duration_id;
+	__le16 frame_ctl;
+	__le16 duration_id;
 	u8 addr1[ETH_ALEN];
 	u8 addr2[ETH_ALEN];
 	u8 addr3[ETH_ALEN];
-	u16 seq_ctl;
-} __packed;
+	__le16 seq_ctl;
+}  __packed __aligned(2);
 
 struct	ieee80211_hdr_qos {
-	u16 frame_ctl;
-	u16 duration_id;
+	__le16 frame_ctl;
+	__le16 duration_id;
 	u8 addr1[ETH_ALEN];
 	u8 addr2[ETH_ALEN];
 	u8 addr3[ETH_ALEN];
-	u16 seq_ctl;
+	__le16 seq_ctl;
 	u8 addr4[ETH_ALEN];
-	u16	qc;
-}  __packed;
+	__le16	qc;
+}   __packed __aligned(2);
 
 struct  ieee80211_hdr_3addr_qos {
-	u16 frame_ctl;
-	u16 duration_id;
+	__le16 frame_ctl;
+	__le16 duration_id;
 	u8  addr1[ETH_ALEN];
 	u8  addr2[ETH_ALEN];
 	u8  addr3[ETH_ALEN];
-	u16 seq_ctl;
-	u16 qc;
+	__le16 seq_ctl;
+	__le16 qc;
 }  __packed;
 
 struct eapol {
 	u8 snap[6];
-	u16 ethertype;
+	__be16 ethertype;
 	u8 version;
 	u8 type;
-	u16 length;
+	__le16 length;
 } __packed;
 
 enum eap_type {
@@ -514,13 +514,13 @@
  */
 
 struct ieee80211_header_data {
-	u16 frame_ctl;
-	u16 duration_id;
+	__le16 frame_ctl;
+	__le16 duration_id;
 	u8 addr1[6];
 	u8 addr2[6];
 	u8 addr3[6];
-	u16 seq_ctrl;
-};
+	__le16 seq_ctrl;
+} __packed __aligned(2);
 
 #define BEACON_PROBE_SSID_ID_POSITION 12
 
@@ -552,18 +552,18 @@
 /*
  * These are the data types that can make up management packets
  *
-	u16 auth_algorithm;
-	u16 auth_sequence;
-	u16 beacon_interval;
-	u16 capability;
+	__le16 auth_algorithm;
+	__le16 auth_sequence;
+	__le16 beacon_interval;
+	__le16 capability;
 	u8 current_ap[ETH_ALEN];
-	u16 listen_interval;
+	__le16 listen_interval;
 	struct {
 		u16 association_id:14, reserved:2;
 	} __packed;
-	u32 time_stamp[2];
-	u16 reason;
-	u16 status;
+	__le32 time_stamp[2];
+	__le16 reason;
+	__le16 status;
 */
 
 #define IEEE80211_DEFAULT_TX_ESSID "Penguin"
@@ -571,16 +571,16 @@
 
 struct ieee80211_authentication {
 	struct ieee80211_header_data header;
-	u16 algorithm;
-	u16 transaction;
-	u16 status;
+	__le16 algorithm;
+	__le16 transaction;
+	__le16 status;
 } __packed;
 
 struct ieee80211_probe_response {
 	struct ieee80211_header_data header;
-	u32 time_stamp[2];
-	u16 beacon_interval;
-	u16 capability;
+	__le32 time_stamp[2];
+	__le16 beacon_interval;
+	__le16 capability;
 	struct ieee80211_info_element info_element;
 } __packed;
 
@@ -590,16 +590,16 @@
 
 struct ieee80211_assoc_request_frame {
 	struct ieee80211_hdr_3addr header;
-	u16 capability;
-	u16 listen_interval;
+	__le16 capability;
+	__le16 listen_interval;
 	struct ieee80211_info_element_hdr info_element;
 } __packed;
 
 struct ieee80211_assoc_response_frame {
 	struct ieee80211_hdr_3addr header;
-	u16 capability;
-	u16 status;
-	u16 aid;
+	__le16 capability;
+	__le16 status;
+	__le16 aid;
 } __packed;
 
 struct ieee80211_txb {
diff --git a/drivers/staging/rtl8712/rtl871x_xmit.c b/drivers/staging/rtl8712/rtl871x_xmit.c
index be38364..c478639 100644
--- a/drivers/staging/rtl8712/rtl871x_xmit.c
+++ b/drivers/staging/rtl8712/rtl871x_xmit.c
@@ -344,7 +344,8 @@
 	 * some settings above.
 	 */
 	if (check_fwstate(pmlmepriv, WIFI_MP_STATE))
-		pattrib->priority = (txdesc.txdw1 >> QSEL_SHT) & 0x1f;
+		pattrib->priority =
+		    (le32_to_cpu(txdesc.txdw1) >> QSEL_SHT) & 0x1f;
 	return _SUCCESS;
 }
 
@@ -485,7 +486,7 @@
 	struct ieee80211_hdr *pwlanhdr = (struct ieee80211_hdr *)hdr;
 	struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
 	struct qos_priv *pqospriv = &pmlmepriv->qospriv;
-	u16 *fctrl = &pwlanhdr->frame_ctl;
+	__le16 *fctrl = &pwlanhdr->frame_ctl;
 
 	memset(hdr, 0, WLANHDR_OFFSET);
 	SetFrameSubType(fctrl, pattrib->subtype);
@@ -574,7 +575,7 @@
 	snap->oui[0] = oui[0];
 	snap->oui[1] = oui[1];
 	snap->oui[2] = oui[2];
-	*(u16 *)(data + SNAP_SIZE) = htons(h_proto);
+	*(__be16 *)(data + SNAP_SIZE) = htons(h_proto);
 	return SNAP_SIZE + sizeof(u16);
 }
 
diff --git a/drivers/staging/rts5208/rtsx_scsi.c b/drivers/staging/rts5208/rtsx_scsi.c
index 01438fa..f50076d 100644
--- a/drivers/staging/rts5208/rtsx_scsi.c
+++ b/drivers/staging/rts5208/rtsx_scsi.c
@@ -414,7 +414,7 @@
 	sense->ascq = ascq;
 	if (sns_key_info0 != 0) {
 		sense->sns_key_info[0] = SKSV | sns_key_info0;
-		sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 8;
+		sense->sns_key_info[1] = (sns_key_info1 & 0xf0) >> 4;
 		sense->sns_key_info[2] = sns_key_info1 & 0x0f;
 	}
 }
diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
index 1091b9f..f72eebc 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c
@@ -106,8 +106,14 @@
 
 	g_virt_to_bus_offset = virt_to_dma(dev, (void *)0);
 
-	(void)of_property_read_u32(dev->of_node, "cache-line-size",
+	err = of_property_read_u32(dev->of_node, "cache-line-size",
 				   &g_cache_line_size);
+
+	if (err) {
+		dev_err(dev, "Missing cache-line-size property\n");
+		return -ENODEV;
+	}
+
 	g_fragments_size = 2 * g_cache_line_size;
 
 	/* Allocate space for the channels in coherent memory */
@@ -538,18 +544,20 @@
 			if (head_bytes > actual)
 				head_bytes = actual;
 
-			memcpy((char *)page_address(pages[0]) +
+			memcpy((char *)kmap(pages[0]) +
 				pagelist->offset,
 				fragments,
 				head_bytes);
+			kunmap(pages[0]);
 		}
 		if ((actual >= 0) && (head_bytes < actual) &&
 			(tail_bytes != 0)) {
-			memcpy((char *)page_address(pages[num_pages - 1]) +
+			memcpy((char *)kmap(pages[num_pages - 1]) +
 				((pagelist->offset + actual) &
 				(PAGE_SIZE - 1) & ~(g_cache_line_size - 1)),
 				fragments + g_cache_line_size,
 				tail_bytes);
+			kunmap(pages[num_pages - 1]);
 		}
 
 		down(&g_free_fragments_mutex);
diff --git a/drivers/staging/wilc1000/linux_wlan.c b/drivers/staging/wilc1000/linux_wlan.c
index 6370a5e..defffa7 100644
--- a/drivers/staging/wilc1000/linux_wlan.c
+++ b/drivers/staging/wilc1000/linux_wlan.c
@@ -269,23 +269,12 @@
 
 int wilc_wlan_set_bssid(struct net_device *wilc_netdev, u8 *bssid, u8 mode)
 {
-	int i = 0;
-	int ret = -1;
-	struct wilc_vif *vif;
-	struct wilc *wilc;
+	struct wilc_vif *vif = netdev_priv(wilc_netdev);
 
-	vif = netdev_priv(wilc_netdev);
-	wilc = vif->wilc;
+	memcpy(vif->bssid, bssid, 6);
+	vif->mode = mode;
 
-	for (i = 0; i < wilc->vif_num; i++)
-		if (wilc->vif[i]->ndev == wilc_netdev) {
-			memcpy(wilc->vif[i]->bssid, bssid, 6);
-			wilc->vif[i]->mode = mode;
-			ret = 0;
-			break;
-		}
-
-	return ret;
+	return 0;
 }
 
 int wilc_wlan_get_num_conn_ifcs(struct wilc *wilc)
@@ -1212,16 +1201,11 @@
 
 void wilc_netdev_cleanup(struct wilc *wilc)
 {
-	int i = 0;
-	struct wilc_vif *vif[NUM_CONCURRENT_IFC];
+	int i;
 
-	if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) {
+	if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev))
 		unregister_inetaddr_notifier(&g_dev_notifier);
 
-		for (i = 0; i < NUM_CONCURRENT_IFC; i++)
-			vif[i] = netdev_priv(wilc->vif[i]->ndev);
-	}
-
 	if (wilc && wilc->firmware) {
 		release_firmware(wilc->firmware);
 		wilc->firmware = NULL;
@@ -1230,7 +1214,7 @@
 	if (wilc && (wilc->vif[0]->ndev || wilc->vif[1]->ndev)) {
 		for (i = 0; i < NUM_CONCURRENT_IFC; i++)
 			if (wilc->vif[i]->ndev)
-				if (vif[i]->mac_opened)
+				if (wilc->vif[i]->mac_opened)
 					wilc_mac_close(wilc->vif[i]->ndev);
 
 		for (i = 0; i < NUM_CONCURRENT_IFC; i++) {
@@ -1278,9 +1262,9 @@
 
 		vif->idx = wl->vif_num;
 		vif->wilc = *wilc;
+		vif->ndev = ndev;
 		wl->vif[i] = vif;
-		wl->vif[wl->vif_num]->ndev = ndev;
-		wl->vif_num++;
+		wl->vif_num = i;
 		ndev->netdev_ops = &wilc_netdev_ops;
 
 		{
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
index 182b2d5..165c46f 100644
--- a/drivers/staging/wlan-ng/cfg80211.c
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -666,8 +666,11 @@
 
 void prism2_roamed(struct wlandevice *wlandev)
 {
-	cfg80211_roamed(wlandev->netdev, NULL, wlandev->bssid,
-		NULL, 0, NULL, 0, GFP_KERNEL);
+	struct cfg80211_roam_info roam_info = {
+		.bssid = wlandev->bssid,
+	};
+
+	cfg80211_roamed(wlandev->netdev, &roam_info, GFP_KERNEL);
 }
 
 /* Structures for declaring wiphy interface */
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 155fe0e..e49fcd5 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -418,6 +418,7 @@
 		return 0;
 	}
 	np->np_thread_state = ISCSI_NP_THREAD_RESET;
+	atomic_inc(&np->np_reset_count);
 
 	if (np->np_thread) {
 		spin_unlock_bh(&np->np_thread_lock);
@@ -2177,6 +2178,7 @@
 	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
 	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
 	cmd->data_direction	= DMA_NONE;
+	kfree(cmd->text_in_ptr);
 	cmd->text_in_ptr	= NULL;
 
 	return 0;
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index efc453e..ab92a1b 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -44,10 +44,8 @@
 	 */
 	if (cmd->unsolicited_data) {
 		cmd->seq_start_offset = cmd->write_data_done;
-		cmd->seq_end_offset = (cmd->write_data_done +
-			((cmd->se_cmd.data_length >
-			  conn->sess->sess_ops->FirstBurstLength) ?
-			 conn->sess->sess_ops->FirstBurstLength : cmd->se_cmd.data_length));
+		cmd->seq_end_offset = min(cmd->se_cmd.data_length,
+					conn->sess->sess_ops->FirstBurstLength);
 		return;
 	}
 
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 6128e8e..9ccd5da 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -1233,9 +1233,11 @@
 	flush_signals(current);
 
 	spin_lock_bh(&np->np_thread_lock);
-	if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+	if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
 		np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
+		spin_unlock_bh(&np->np_thread_lock);
 		complete(&np->np_restart_comp);
+		return 1;
 	} else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) {
 		spin_unlock_bh(&np->np_thread_lock);
 		goto exit;
@@ -1268,7 +1270,8 @@
 		goto exit;
 	} else if (rc < 0) {
 		spin_lock_bh(&np->np_thread_lock);
-		if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
+		if (atomic_dec_if_positive(&np->np_reset_count) >= 0) {
+			np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
 			spin_unlock_bh(&np->np_thread_lock);
 			complete(&np->np_restart_comp);
 			iscsit_put_transport(conn->conn_transport);
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index 6693d7c..e8efb42 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -490,14 +490,60 @@
 
 static int iscsi_target_do_login(struct iscsi_conn *, struct iscsi_login *);
 
-static bool iscsi_target_sk_state_check(struct sock *sk)
+static bool __iscsi_target_sk_check_close(struct sock *sk)
 {
 	if (sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) {
-		pr_debug("iscsi_target_sk_state_check: TCP_CLOSE_WAIT|TCP_CLOSE,"
+		pr_debug("__iscsi_target_sk_check_close: TCP_CLOSE_WAIT|TCP_CLOSE,"
 			"returning FALSE\n");
-		return false;
+		return true;
 	}
-	return true;
+	return false;
+}
+
+static bool iscsi_target_sk_check_close(struct iscsi_conn *conn)
+{
+	bool state = false;
+
+	if (conn->sock) {
+		struct sock *sk = conn->sock->sk;
+
+		read_lock_bh(&sk->sk_callback_lock);
+		state = (__iscsi_target_sk_check_close(sk) ||
+			 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
+		read_unlock_bh(&sk->sk_callback_lock);
+	}
+	return state;
+}
+
+static bool iscsi_target_sk_check_flag(struct iscsi_conn *conn, unsigned int flag)
+{
+	bool state = false;
+
+	if (conn->sock) {
+		struct sock *sk = conn->sock->sk;
+
+		read_lock_bh(&sk->sk_callback_lock);
+		state = test_bit(flag, &conn->login_flags);
+		read_unlock_bh(&sk->sk_callback_lock);
+	}
+	return state;
+}
+
+static bool iscsi_target_sk_check_and_clear(struct iscsi_conn *conn, unsigned int flag)
+{
+	bool state = false;
+
+	if (conn->sock) {
+		struct sock *sk = conn->sock->sk;
+
+		write_lock_bh(&sk->sk_callback_lock);
+		state = (__iscsi_target_sk_check_close(sk) ||
+			 test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags));
+		if (!state)
+			clear_bit(flag, &conn->login_flags);
+		write_unlock_bh(&sk->sk_callback_lock);
+	}
+	return state;
 }
 
 static void iscsi_target_login_drop(struct iscsi_conn *conn, struct iscsi_login *login)
@@ -537,6 +583,20 @@
 
 	pr_debug("entering iscsi_target_do_login_rx, conn: %p, %s:%d\n",
 			conn, current->comm, current->pid);
+	/*
+	 * If iscsi_target_do_login_rx() has been invoked by ->sk_data_ready()
+	 * before initial PDU processing in iscsi_target_start_negotiation()
+	 * has completed, go ahead and retry until it's cleared.
+	 *
+	 * Otherwise if the TCP connection drops while this is occuring,
+	 * iscsi_target_start_negotiation() will detect the failure, call
+	 * cancel_delayed_work_sync(&conn->login_work), and cleanup the
+	 * remaining iscsi connection resources from iscsi_np process context.
+	 */
+	if (iscsi_target_sk_check_flag(conn, LOGIN_FLAGS_INITIAL_PDU)) {
+		schedule_delayed_work(&conn->login_work, msecs_to_jiffies(10));
+		return;
+	}
 
 	spin_lock(&tpg->tpg_state_lock);
 	state = (tpg->tpg_state == TPG_STATE_ACTIVE);
@@ -544,26 +604,12 @@
 
 	if (!state) {
 		pr_debug("iscsi_target_do_login_rx: tpg_state != TPG_STATE_ACTIVE\n");
-		iscsi_target_restore_sock_callbacks(conn);
-		iscsi_target_login_drop(conn, login);
-		iscsit_deaccess_np(np, tpg, tpg_np);
-		return;
+		goto err;
 	}
 
-	if (conn->sock) {
-		struct sock *sk = conn->sock->sk;
-
-		read_lock_bh(&sk->sk_callback_lock);
-		state = iscsi_target_sk_state_check(sk);
-		read_unlock_bh(&sk->sk_callback_lock);
-
-		if (!state) {
-			pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
-			iscsi_target_restore_sock_callbacks(conn);
-			iscsi_target_login_drop(conn, login);
-			iscsit_deaccess_np(np, tpg, tpg_np);
-			return;
-		}
+	if (iscsi_target_sk_check_close(conn)) {
+		pr_debug("iscsi_target_do_login_rx, TCP state CLOSE\n");
+		goto err;
 	}
 
 	conn->login_kworker = current;
@@ -581,34 +627,29 @@
 	flush_signals(current);
 	conn->login_kworker = NULL;
 
-	if (rc < 0) {
-		iscsi_target_restore_sock_callbacks(conn);
-		iscsi_target_login_drop(conn, login);
-		iscsit_deaccess_np(np, tpg, tpg_np);
-		return;
-	}
+	if (rc < 0)
+		goto err;
 
 	pr_debug("iscsi_target_do_login_rx after rx_login_io, %p, %s:%d\n",
 			conn, current->comm, current->pid);
 
 	rc = iscsi_target_do_login(conn, login);
 	if (rc < 0) {
-		iscsi_target_restore_sock_callbacks(conn);
-		iscsi_target_login_drop(conn, login);
-		iscsit_deaccess_np(np, tpg, tpg_np);
+		goto err;
 	} else if (!rc) {
-		if (conn->sock) {
-			struct sock *sk = conn->sock->sk;
-
-			write_lock_bh(&sk->sk_callback_lock);
-			clear_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags);
-			write_unlock_bh(&sk->sk_callback_lock);
-		}
+		if (iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_READ_ACTIVE))
+			goto err;
 	} else if (rc == 1) {
 		iscsi_target_nego_release(conn);
 		iscsi_post_login_handler(np, conn, zero_tsih);
 		iscsit_deaccess_np(np, tpg, tpg_np);
 	}
+	return;
+
+err:
+	iscsi_target_restore_sock_callbacks(conn);
+	iscsi_target_login_drop(conn, login);
+	iscsit_deaccess_np(np, tpg, tpg_np);
 }
 
 static void iscsi_target_do_cleanup(struct work_struct *work)
@@ -656,31 +697,54 @@
 		orig_state_change(sk);
 		return;
 	}
+	state = __iscsi_target_sk_check_close(sk);
+	pr_debug("__iscsi_target_sk_close_change: state: %d\n", state);
+
 	if (test_bit(LOGIN_FLAGS_READ_ACTIVE, &conn->login_flags)) {
 		pr_debug("Got LOGIN_FLAGS_READ_ACTIVE=1 sk_state_change"
 			 " conn: %p\n", conn);
+		if (state)
+			set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
 		write_unlock_bh(&sk->sk_callback_lock);
 		orig_state_change(sk);
 		return;
 	}
-	if (test_and_set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
+	if (test_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags)) {
 		pr_debug("Got LOGIN_FLAGS_CLOSED=1 sk_state_change conn: %p\n",
 			 conn);
 		write_unlock_bh(&sk->sk_callback_lock);
 		orig_state_change(sk);
 		return;
 	}
-
-	state = iscsi_target_sk_state_check(sk);
-	write_unlock_bh(&sk->sk_callback_lock);
-
-	pr_debug("iscsi_target_sk_state_change: state: %d\n", state);
-
-	if (!state) {
+	/*
+	 * If the TCP connection has dropped, go ahead and set LOGIN_FLAGS_CLOSED,
+	 * but only queue conn->login_work -> iscsi_target_do_login_rx()
+	 * processing if LOGIN_FLAGS_INITIAL_PDU has already been cleared.
+	 *
+	 * When iscsi_target_do_login_rx() runs, iscsi_target_sk_check_close()
+	 * will detect the dropped TCP connection from delayed workqueue context.
+	 *
+	 * If LOGIN_FLAGS_INITIAL_PDU is still set, which means the initial
+	 * iscsi_target_start_negotiation() is running, iscsi_target_do_login()
+	 * via iscsi_target_sk_check_close() or iscsi_target_start_negotiation()
+	 * via iscsi_target_sk_check_and_clear() is responsible for detecting the
+	 * dropped TCP connection in iscsi_np process context, and cleaning up
+	 * the remaining iscsi connection resources.
+	 */
+	if (state) {
 		pr_debug("iscsi_target_sk_state_change got failed state\n");
-		schedule_delayed_work(&conn->login_cleanup_work, 0);
+		set_bit(LOGIN_FLAGS_CLOSED, &conn->login_flags);
+		state = test_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
+		write_unlock_bh(&sk->sk_callback_lock);
+
+		orig_state_change(sk);
+
+		if (!state)
+			schedule_delayed_work(&conn->login_work, 0);
 		return;
 	}
+	write_unlock_bh(&sk->sk_callback_lock);
+
 	orig_state_change(sk);
 }
 
@@ -945,6 +1009,15 @@
 			if (iscsi_target_handle_csg_one(conn, login) < 0)
 				return -1;
 			if (login_rsp->flags & ISCSI_FLAG_LOGIN_TRANSIT) {
+				/*
+				 * Check to make sure the TCP connection has not
+				 * dropped asynchronously while session reinstatement
+				 * was occuring in this kthread context, before
+				 * transitioning to full feature phase operation.
+				 */
+				if (iscsi_target_sk_check_close(conn))
+					return -1;
+
 				login->tsih = conn->sess->tsih;
 				login->login_complete = 1;
 				iscsi_target_restore_sock_callbacks(conn);
@@ -971,21 +1044,6 @@
 		break;
 	}
 
-	if (conn->sock) {
-		struct sock *sk = conn->sock->sk;
-		bool state;
-
-		read_lock_bh(&sk->sk_callback_lock);
-		state = iscsi_target_sk_state_check(sk);
-		read_unlock_bh(&sk->sk_callback_lock);
-
-		if (!state) {
-			pr_debug("iscsi_target_do_login() failed state for"
-				 " conn: %p\n", conn);
-			return -1;
-		}
-	}
-
 	return 0;
 }
 
@@ -1252,13 +1310,25 @@
        if (conn->sock) {
                struct sock *sk = conn->sock->sk;
 
-               write_lock_bh(&sk->sk_callback_lock);
-               set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
-               write_unlock_bh(&sk->sk_callback_lock);
-       }
+		write_lock_bh(&sk->sk_callback_lock);
+		set_bit(LOGIN_FLAGS_READY, &conn->login_flags);
+		set_bit(LOGIN_FLAGS_INITIAL_PDU, &conn->login_flags);
+		write_unlock_bh(&sk->sk_callback_lock);
+	}
+	/*
+	 * If iscsi_target_do_login returns zero to signal more PDU
+	 * exchanges are required to complete the login, go ahead and
+	 * clear LOGIN_FLAGS_INITIAL_PDU but only if the TCP connection
+	 * is still active.
+	 *
+	 * Otherwise if TCP connection dropped asynchronously, go ahead
+	 * and perform connection cleanup now.
+	 */
+	ret = iscsi_target_do_login(conn, login);
+	if (!ret && iscsi_target_sk_check_and_clear(conn, LOGIN_FLAGS_INITIAL_PDU))
+		ret = -1;
 
-       ret = iscsi_target_do_login(conn, login);
-       if (ret < 0) {
+	if (ret < 0) {
 		cancel_delayed_work_sync(&conn->login_work);
 		cancel_delayed_work_sync(&conn->login_cleanup_work);
 		iscsi_target_restore_sock_callbacks(conn);
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 1949f50..0e2e71f 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -364,7 +364,7 @@
 	mutex_lock(&tpg->acl_node_mutex);
 	if (acl->dynamic_node_acl)
 		acl->dynamic_node_acl = 0;
-	list_del(&acl->acl_list);
+	list_del_init(&acl->acl_list);
 	mutex_unlock(&tpg->acl_node_mutex);
 
 	target_shutdown_sessions(acl);
@@ -540,7 +540,7 @@
 	 * in transport_deregister_session().
 	 */
 	list_for_each_entry_safe(nacl, nacl_tmp, &node_list, acl_list) {
-		list_del(&nacl->acl_list);
+		list_del_init(&nacl->acl_list);
 
 		core_tpg_wait_for_nacl_pr_ref(nacl);
 		core_free_device_list_for_node(nacl, se_tpg);
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index e8a1f5c..bacfa8f 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -465,7 +465,7 @@
 	}
 
 	mutex_lock(&se_tpg->acl_node_mutex);
-	list_del(&nacl->acl_list);
+	list_del_init(&nacl->acl_list);
 	mutex_unlock(&se_tpg->acl_node_mutex);
 
 	core_tpg_wait_for_nacl_pr_ref(nacl);
@@ -537,7 +537,7 @@
 			spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags);
 
 			if (se_nacl->dynamic_stop)
-				list_del(&se_nacl->acl_list);
+				list_del_init(&se_nacl->acl_list);
 		}
 		mutex_unlock(&se_tpg->acl_node_mutex);
 
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
index 09f7f20..f25bade 100644
--- a/drivers/thermal/cpu_cooling.c
+++ b/drivers/thermal/cpu_cooling.c
@@ -32,6 +32,7 @@
 #include <linux/cpu_cooling.h>
 #include <linux/sched.h>
 #include <linux/of_device.h>
+#include <linux/suspend.h>
 
 #include <trace/events/thermal.h>
 
@@ -117,10 +118,12 @@
 static DEFINE_IDR(cpufreq_idr);
 static DEFINE_MUTEX(cooling_cpufreq_lock);
 
+static atomic_t in_suspend;
 static unsigned int cpufreq_dev_count;
 static int8_t cpuhp_registered;
 static struct work_struct cpuhp_register_work;
 static struct cpumask cpus_pending_online;
+static struct cpumask cpus_isolated_by_thermal;
 static DEFINE_MUTEX(core_isolate_lock);
 
 static DEFINE_MUTEX(cooling_list_lock);
@@ -218,6 +221,51 @@
 }
 EXPORT_SYMBOL_GPL(cpufreq_cooling_get_level);
 
+static int cpufreq_cooling_pm_notify(struct notifier_block *nb,
+				unsigned long mode, void *_unused)
+{
+	struct cpufreq_cooling_device *cpufreq_dev;
+	unsigned int cpu;
+
+	switch (mode) {
+	case PM_HIBERNATION_PREPARE:
+	case PM_RESTORE_PREPARE:
+	case PM_SUSPEND_PREPARE:
+		atomic_set(&in_suspend, 1);
+		break;
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+	case PM_POST_SUSPEND:
+		mutex_lock(&cooling_list_lock);
+		mutex_lock(&core_isolate_lock);
+		list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
+			if (cpufreq_dev->cpufreq_state ==
+				cpufreq_dev->max_level) {
+				cpu = cpumask_any(&cpufreq_dev->allowed_cpus);
+				if (cpu_online(cpu) &&
+					!cpumask_test_and_set_cpu(cpu,
+					&cpus_isolated_by_thermal)) {
+					if (sched_isolate_cpu(cpu))
+						cpumask_clear_cpu(cpu,
+						&cpus_isolated_by_thermal);
+				}
+			}
+		}
+		mutex_unlock(&core_isolate_lock);
+		mutex_unlock(&cooling_list_lock);
+
+		atomic_set(&in_suspend, 0);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct notifier_block cpufreq_cooling_pm_nb = {
+	.notifier_call = cpufreq_cooling_pm_notify,
+};
+
 static int cpufreq_hp_offline(unsigned int offline_cpu)
 {
 	struct cpufreq_cooling_device *cpufreq_dev;
@@ -228,7 +276,9 @@
 			continue;
 
 		mutex_lock(&core_isolate_lock);
-		if (cpufreq_dev->cpufreq_state == cpufreq_dev->max_level)
+		if ((cpufreq_dev->cpufreq_state == cpufreq_dev->max_level) &&
+			(cpumask_test_and_clear_cpu(offline_cpu,
+			&cpus_isolated_by_thermal)))
 			sched_unisolate_cpu_unlocked(offline_cpu);
 		mutex_unlock(&core_isolate_lock);
 		break;
@@ -243,6 +293,9 @@
 	struct cpufreq_cooling_device *cpufreq_dev;
 	int ret = 0;
 
+	if (atomic_read(&in_suspend))
+		return 0;
+
 	mutex_lock(&cooling_list_lock);
 	list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
 		if (!cpumask_test_cpu(online_cpu, &cpufreq_dev->allowed_cpus))
@@ -677,8 +730,13 @@
 	cpufreq_device->cpufreq_state = state;
 	/* If state is the last, isolate the CPU */
 	if (state == cpufreq_device->max_level) {
-		if (cpu_online(cpu))
-			sched_isolate_cpu(cpu);
+		if (cpu_online(cpu) &&
+			(!cpumask_test_and_set_cpu(cpu,
+			&cpus_isolated_by_thermal))) {
+			if (sched_isolate_cpu(cpu))
+				cpumask_clear_cpu(cpu,
+					&cpus_isolated_by_thermal);
+		}
 		mutex_unlock(&core_isolate_lock);
 		return ret;
 	} else if ((prev_state == cpufreq_device->max_level)
@@ -695,8 +753,10 @@
 			if (ret)
 				pr_err("CPU:%d online error:%d\n", cpu, ret);
 			goto update_frequency;
-		} else
+		} else if (cpumask_test_and_clear_cpu(cpu,
+			&cpus_isolated_by_thermal)) {
 			sched_unisolate_cpu(cpu);
+		}
 	}
 	mutex_unlock(&core_isolate_lock);
 update_frequency:
@@ -1105,12 +1165,14 @@
 	mutex_unlock(&cooling_list_lock);
 
 	/* Register the notifier for first cpufreq cooling device */
-	if (!cpufreq_dev_count++)
+	if (!cpufreq_dev_count++ && !cpufreq_dev->plat_ops)
 		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
 					  CPUFREQ_POLICY_NOTIFIER);
 	if (!cpuhp_registered) {
 		cpuhp_registered = 1;
+		register_pm_notifier(&cpufreq_cooling_pm_nb);
 		cpumask_clear(&cpus_pending_online);
+		cpumask_clear(&cpus_isolated_by_thermal);
 		INIT_WORK(&cpuhp_register_work, register_cdev);
 		queue_work(system_wq, &cpuhp_register_work);
 	}
@@ -1285,9 +1347,13 @@
 
 	/* Unregister the notifier for the last cpufreq cooling device */
 	mutex_lock(&cooling_cpufreq_lock);
-	if (!--cpufreq_dev_count)
-		cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
-					    CPUFREQ_POLICY_NOTIFIER);
+	if (!--cpufreq_dev_count) {
+		unregister_pm_notifier(&cpufreq_cooling_pm_nb);
+		if (!cpufreq_dev->plat_ops)
+			cpufreq_unregister_notifier(
+				&thermal_cpufreq_notifier_block,
+				CPUFREQ_POLICY_NOTIFIER);
+	}
 
 	mutex_lock(&cooling_list_lock);
 	list_del(&cpufreq_dev->node);
diff --git a/drivers/thermal/gov_low_limits.c b/drivers/thermal/gov_low_limits.c
index b75abf7..278869c 100644
--- a/drivers/thermal/gov_low_limits.c
+++ b/drivers/thermal/gov_low_limits.c
@@ -47,7 +47,7 @@
 
 		if ((tz->temperature <= trip_temp) ||
 			(instance->target != THERMAL_NO_TARGET
-				&& tz->temperature <= trip_hyst))
+				&& tz->temperature < trip_hyst))
 			throttle = true;
 		else
 			throttle = false;
diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c
index 101e51b..bb5f706 100644
--- a/drivers/thermal/of-thermal.c
+++ b/drivers/thermal/of-thermal.c
@@ -863,7 +863,7 @@
 	tzd = thermal_zone_get_zone_by_name(
 				sensor_data->virt_zone_name);
 	if (IS_ERR(tzd)) {
-		dev_err(dev, "sens:%s not available err: %ld\n",
+		dev_dbg(dev, "sens:%s not available err: %ld\n",
 				sensor_data->virt_zone_name,
 				PTR_ERR(tzd));
 		return tzd;
diff --git a/drivers/thermal/qcom/msm_lmh_dcvs.c b/drivers/thermal/qcom/msm_lmh_dcvs.c
index d590d24..94c93b5 100644
--- a/drivers/thermal/qcom/msm_lmh_dcvs.c
+++ b/drivers/thermal/qcom/msm_lmh_dcvs.c
@@ -58,8 +58,7 @@
 #define LIMITS_CLUSTER_0            0x6370302D
 #define LIMITS_CLUSTER_1            0x6370312D
 
-#define LIMITS_DOMAIN_MAX           0x444D4158
-#define LIMITS_DOMAIN_MIN           0x444D494E
+#define LIMITS_FREQ_CAP             0x46434150
 
 #define LIMITS_TEMP_DEFAULT         75000
 #define LIMITS_TEMP_HIGH_THRESH_MAX 120000
@@ -225,31 +224,36 @@
 }
 
 static int limits_dcvs_write(uint32_t node_id, uint32_t fn,
-			      uint32_t setting, uint32_t val)
+			      uint32_t setting, uint32_t val, uint32_t val1,
+			      bool enable_val1)
 {
 	int ret;
 	struct scm_desc desc_arg;
 	uint32_t *payload = NULL;
+	uint32_t payload_len;
 
-	payload = kzalloc(sizeof(uint32_t) * 5, GFP_KERNEL);
+	payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t);
+	payload = kzalloc(payload_len, GFP_KERNEL);
 	if (!payload)
 		return -ENOMEM;
 
 	payload[0] = fn; /* algorithm */
 	payload[1] = 0; /* unused sub-algorithm */
 	payload[2] = setting;
-	payload[3] = 1; /* number of values */
+	payload[3] = enable_val1 ? 2 : 1; /* number of values */
 	payload[4] = val;
+	if (enable_val1)
+		payload[5] = val1;
 
 	desc_arg.args[0] = SCM_BUFFER_PHYS(payload);
-	desc_arg.args[1] = sizeof(uint32_t) * 5;
+	desc_arg.args[1] = payload_len;
 	desc_arg.args[2] = LIMITS_NODE_DCVS;
 	desc_arg.args[3] = node_id;
 	desc_arg.args[4] = 0; /* version */
 	desc_arg.arginfo = SCM_ARGS(5, SCM_RO, SCM_VAL, SCM_VAL,
 					SCM_VAL, SCM_VAL);
 
-	dmac_flush_range(payload, (void *)payload + 5 * (sizeof(uint32_t)));
+	dmac_flush_range(payload, (void *)payload + payload_len);
 	ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, LIMITS_DCVSH), &desc_arg);
 
 	kfree(payload);
@@ -288,16 +292,17 @@
 	hw->temp_limits[LIMITS_TRIP_ARM] = (uint32_t)low;
 
 	ret =  limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
-				  LIMITS_ARM_THRESHOLD, low);
+				  LIMITS_ARM_THRESHOLD, low, 0, 0);
 	if (ret)
 		return ret;
 	ret =  limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
-				  LIMITS_HI_THRESHOLD, high);
+				  LIMITS_HI_THRESHOLD, high, 0, 0);
 	if (ret)
 		return ret;
 	ret =  limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
 				  LIMITS_LOW_THRESHOLD,
-				  high - LIMITS_LOW_THRESHOLD_OFFSET);
+				  high - LIMITS_LOW_THRESHOLD_OFFSET,
+				  0, 0);
 	if (ret)
 		return ret;
 
@@ -354,13 +359,20 @@
 	mutex_lock(&hw->access_lock);
 	for_each_cpu(cpu_idx, &hw->core_map) {
 		if (cpu_idx == cpu)
-			hw->cdev_data[idx].max_freq = freq;
+		/*
+		 * If there is no limits restriction for CPU scaling max
+		 * frequency, vote for a very high value. This will allow
+		 * the CPU to use the boost frequencies.
+		 */
+			hw->cdev_data[idx].max_freq =
+				(freq == hw->max_freq) ? U32_MAX : freq;
 		if (max_freq > hw->cdev_data[idx].max_freq)
 			max_freq = hw->cdev_data[idx].max_freq;
 		idx++;
 	}
-	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_GENERAL,
-				  LIMITS_DOMAIN_MAX, max_freq);
+	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
+				  LIMITS_FREQ_CAP, max_freq,
+				  (max_freq == U32_MAX) ? 0 : 1, 1);
 	mutex_unlock(&hw->access_lock);
 	lmh_dcvs_notify(hw);
 
@@ -550,22 +562,22 @@
 
 	/* Enable the thermal algorithm early */
 	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_THERMAL,
-		 LIMITS_ALGO_MODE_ENABLE, 1);
+		 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
 	if (ret)
 		return ret;
 	/* Enable the LMH outer loop algorithm */
 	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_CRNT,
-		 LIMITS_ALGO_MODE_ENABLE, 1);
+		 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
 	if (ret)
 		return ret;
 	/* Enable the Reliability algorithm */
 	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_REL,
-		 LIMITS_ALGO_MODE_ENABLE, 1);
+		 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
 	if (ret)
 		return ret;
 	/* Enable the BCL algorithm */
 	ret = limits_dcvs_write(hw->affinity, LIMITS_SUB_FN_BCL,
-		 LIMITS_ALGO_MODE_ENABLE, 1);
+		 LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
 	if (ret)
 		return ret;
 	ret = enable_lmh();
diff --git a/drivers/thermal/qcom/qmi_cooling.c b/drivers/thermal/qcom/qmi_cooling.c
index af82030..e1a01d8 100644
--- a/drivers/thermal/qcom/qmi_cooling.c
+++ b/drivers/thermal/qcom/qmi_cooling.c
@@ -85,6 +85,10 @@
 		.type = QMI_CDEV_MAX_LIMIT_TYPE,
 	},
 	{
+		.dev_name = "modem_skin",
+		.type = QMI_CDEV_MAX_LIMIT_TYPE,
+	},
+	{
 		.dev_name = "modem_bw",
 		.type = QMI_CDEV_MAX_LIMIT_TYPE,
 	},
diff --git a/drivers/thermal/qcom/qti_virtual_sensor.c b/drivers/thermal/qcom/qti_virtual_sensor.c
index 923680a..8cb7dc3 100644
--- a/drivers/thermal/qcom/qti_virtual_sensor.c
+++ b/drivers/thermal/qcom/qti_virtual_sensor.c
@@ -46,6 +46,24 @@
 				"cpu3-gold-usr"},
 		.logic = VIRT_MAXIMUM,
 	},
+	{
+		.virt_zone_name = "hexa-silv-max-step",
+		.num_sensors = 6,
+		.sensor_names = {"cpu0-silver-usr",
+				"cpu1-silver-usr",
+				"cpu2-silver-usr",
+				"cpu3-silver-usr",
+				"cpu4-silver-usr",
+				"cpu5-silver-usr"},
+		.logic = VIRT_MAXIMUM,
+	},
+	{
+		.virt_zone_name = "dual-gold-max-step",
+		.num_sensors = 2,
+		.sensor_names = {"cpu0-gold-usr",
+				"cpu1-gold-usr"},
+		.logic = VIRT_MAXIMUM,
+	},
 };
 
 int qti_virtual_sensor_register(struct device *dev)
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
index 04320d8..411588e 100644
--- a/drivers/thermal/qpnp-adc-tm.c
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -1730,6 +1730,7 @@
 	param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
 	param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
 	param->adc_tm_hc = chip->adc_tm_hc;
+	param->full_scale_code = chip->adc->adc_prop->full_scale_code;
 	chip->adc->amux_prop->amux_channel = channel;
 	chip->adc->amux_prop->decimation =
 			chip->adc->adc_channels[dt_index].adc_decimation;
diff --git a/drivers/thermal/step_wise.c b/drivers/thermal/step_wise.c
index f6f30a0..1fff359 100644
--- a/drivers/thermal/step_wise.c
+++ b/drivers/thermal/step_wise.c
@@ -177,7 +177,7 @@
 		 * temperature.
 		 */
 		if (tz->temperature >= trip_temp ||
-			(tz->temperature >= hyst_temp &&
+			(tz->temperature > hyst_temp &&
 			 old_target != THERMAL_NO_TARGET))
 			throttle = true;
 		else
diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c
index 3cacc0d..7663e3c 100644
--- a/drivers/thermal/thermal_core.c
+++ b/drivers/thermal/thermal_core.c
@@ -522,6 +522,7 @@
 		if (!ret && *temp < crit_temp)
 			*temp = tz->emul_temperature;
 	}
+	trace_thermal_query_temp(tz, *temp);
 	mutex_unlock(&tz->lock);
 exit:
 	return ret;
@@ -2586,9 +2587,11 @@
 	case PM_POST_SUSPEND:
 		atomic_set(&in_suspend, 0);
 		list_for_each_entry(tz, &thermal_tz_list, node) {
+			mutex_lock(&tz->lock);
 			thermal_zone_device_reset(tz);
-			thermal_zone_device_update(tz,
-						   THERMAL_EVENT_UNSPECIFIED);
+			mod_delayed_work(system_freezable_power_efficient_wq,
+						&tz->poll_queue, 0);
+			mutex_unlock(&tz->lock);
 		}
 		break;
 	default:
diff --git a/drivers/thermal/tsens-dbg.c b/drivers/thermal/tsens-dbg.c
index 9b10a1b..2e795b1 100644
--- a/drivers/thermal/tsens-dbg.c
+++ b/drivers/thermal/tsens-dbg.c
@@ -77,11 +77,10 @@
 	pr_debug("%d %d\n", id, dbg_type);
 	tmdev = data;
 	/* debug */
-	idx = tmdev->tsens_dbg.tsens_thread_iq_dbg.idx;
-	tmdev->tsens_dbg.tsens_thread_iq_dbg.dbg_count[idx%10]++;
-	tmdev->tsens_dbg.tsens_thread_iq_dbg.time_stmp[idx%10] =
+	idx = tmdev->tsens_dbg.irq_idx;
+	tmdev->tsens_dbg.irq_time_stmp[idx%10] =
 							sched_clock();
-	tmdev->tsens_dbg.tsens_thread_iq_dbg.idx++;
+	tmdev->tsens_dbg.irq_idx++;
 
 	return 0;
 }
diff --git a/drivers/thermal/tsens.h b/drivers/thermal/tsens.h
index a695d57..ec2d592 100644
--- a/drivers/thermal/tsens.h
+++ b/drivers/thermal/tsens.h
@@ -48,7 +48,6 @@
 #endif
 
 struct tsens_dbg {
-	u32				dbg_count[DEBUG_SIZE];
 	u32				idx;
 	unsigned long long		time_stmp[DEBUG_SIZE];
 	unsigned long			temp[DEBUG_SIZE];
@@ -56,9 +55,10 @@
 
 struct tsens_dbg_context {
 	struct tsens_device		*tmdev;
-	struct tsens_dbg		tsens_thread_iq_dbg;
 	struct tsens_dbg		sensor_dbg_info[TSENS_MAX_SENSORS];
 	int				tsens_critical_wd_cnt;
+	u32				irq_idx;
+	unsigned long long		irq_time_stmp[DEBUG_SIZE];
 	struct delayed_work		tsens_critical_poll_test;
 };
 
@@ -120,7 +120,6 @@
 	struct device			*dev;
 	struct platform_device		*pdev;
 	struct list_head		list;
-	u32				num_sensors;
 	struct regmap			*map;
 	struct regmap_field		*status_field;
 	void __iomem			*tsens_srot_addr;
diff --git a/drivers/thermal/tsens2xxx.c b/drivers/thermal/tsens2xxx.c
index de9f27f..fd625ae 100644
--- a/drivers/thermal/tsens2xxx.c
+++ b/drivers/thermal/tsens2xxx.c
@@ -94,7 +94,7 @@
 
 	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
 		msm_tsens_convert_temp(last_temp, temp);
-		return 0;
+		goto dbg;
 	}
 
 	code = readl_relaxed_no_log(sensor_addr +
@@ -103,7 +103,7 @@
 	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
 		last_temp = last_temp2;
 		msm_tsens_convert_temp(last_temp, temp);
-		return 0;
+		goto dbg;
 	}
 
 	code = readl_relaxed_no_log(sensor_addr +
@@ -113,7 +113,7 @@
 	if (code & TSENS_TM_SN_STATUS_VALID_BIT) {
 		last_temp = last_temp3;
 		msm_tsens_convert_temp(last_temp, temp);
-		return 0;
+		goto dbg;
 	}
 
 	if (last_temp == last_temp2)
@@ -123,6 +123,7 @@
 
 	msm_tsens_convert_temp(last_temp, temp);
 
+dbg:
 	if (tmdev->ops->dbg)
 		tmdev->ops->dbg(tmdev, (u32) sensor->hw_id,
 					TSENS_DBG_LOG_TEMP_READS, temp);
diff --git a/drivers/tty/goldfish.c b/drivers/tty/goldfish.c
index 3fc9123..996bd47 100644
--- a/drivers/tty/goldfish.c
+++ b/drivers/tty/goldfish.c
@@ -300,7 +300,7 @@
 	return 0;
 
 err_tty_register_device_failed:
-	free_irq(irq, pdev);
+	free_irq(irq, qtty);
 err_request_irq_failed:
 	goldfish_tty_current_line_count--;
 	if (goldfish_tty_current_line_count == 0)
diff --git a/drivers/tty/serial/8250/8250_moxa.c b/drivers/tty/serial/8250/8250_moxa.c
index 26eb539..d5069b2 100644
--- a/drivers/tty/serial/8250/8250_moxa.c
+++ b/drivers/tty/serial/8250/8250_moxa.c
@@ -68,6 +68,7 @@
 			   sizeof(unsigned int) * nr_ports, GFP_KERNEL);
 	if (!brd)
 		return -ENOMEM;
+	brd->num_ports = nr_ports;
 
 	memset(&uart, 0, sizeof(struct uart_8250_port));
 
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index f24d303..1ef31e3 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1751,8 +1751,6 @@
 	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
 		uart_write_wakeup(port);
 
-	pr_debug("%s: THRE\n", __func__);
-
 	/*
 	 * With RPM enabled, we have to wait until the FIFO is empty before the
 	 * HW can go idle. So we get here once again with empty FIFO and disable
@@ -1817,8 +1815,6 @@
 
 	status = serial_port_in(port, UART_LSR);
 
-	pr_debug("%s: status = %x\n", __func__, status);
-
 	if (status & (UART_LSR_DR | UART_LSR_BI)) {
 		if (!up->dma || handle_rx_dma(up, iir))
 			status = serial8250_rx_chars(up, status);
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index bac9975..626cfdc 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1091,6 +1091,18 @@
 	select SERIAL_CORE_CONSOLE
 	select SERIAL_EARLYCON
 
+config SERIAL_MSM_HS
+	tristate "MSM UART High Speed: Serial Driver"
+	depends on ARCH_QCOM
+	select SERIAL_CORE
+	help
+	  If you have a machine based on MSM family of SoCs, you
+	  can enable its onboard high speed serial port by enabling
+	  this option.
+
+	  Choose M here to compile it as a module. The module will be
+	  called msm_serial_hs.
+
 config SERIAL_VT8500
 	bool "VIA VT8500 on-chip serial port support"
 	depends on ARCH_VT8500
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index b39165b..1bdc7f8 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -62,6 +62,7 @@
 obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o
 obj-$(CONFIG_SERIAL_MSM) += msm_serial.o
 obj-$(CONFIG_SERIAL_MSM_GENI) += msm_geni_serial.o
+obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
 obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
 obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
 obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index f9054cb..b142869 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -98,7 +98,9 @@
 #define UART_PARAM		(0x1)
 
 /* UART DMA Rx GP_IRQ_BITS */
-#define UART_DMA_RX_ERRS	(GENMASK(5, 8))
+#define UART_DMA_RX_PARITY_ERR	BIT(5)
+#define UART_DMA_RX_ERRS	(GENMASK(5, 6))
+#define UART_DMA_RX_BREAK	(GENMASK(7, 8))
 
 #define UART_OVERSAMPLING	(32)
 #define STALE_TIMEOUT		(16)
@@ -115,7 +117,7 @@
 #define WAIT_XFER_MAX_TIMEOUT_US	(10000)
 #define WAIT_XFER_MIN_TIMEOUT_US	(9000)
 #define IPC_LOG_PWR_PAGES	(6)
-#define IPC_LOG_MISC_PAGES	(6)
+#define IPC_LOG_MISC_PAGES	(10)
 #define IPC_LOG_TX_RX_PAGES	(8)
 #define DATA_BYTES_PER_LINE	(32)
 
@@ -125,6 +127,7 @@
 } while (0)
 
 #define DMA_RX_BUF_SIZE		(2048)
+#define CONSOLE_YIELD_LEN	(8 * 1024)
 struct msm_geni_serial_port {
 	struct uart_port uport;
 	char name[20];
@@ -141,7 +144,8 @@
 	int (*handle_rx)(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 	struct device *wrapper_dev;
 	struct se_geni_rsc serial_rsc;
 	dma_addr_t tx_dma;
@@ -159,6 +163,7 @@
 	unsigned int cur_baud;
 	int ioctl_count;
 	int edge_count;
+	unsigned int tx_yield_count;
 };
 
 static const struct uart_ops msm_geni_serial_pops;
@@ -167,16 +172,21 @@
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 static int handle_rx_hs(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last);
+			unsigned int rx_last,
+			bool drop_rx);
 static unsigned int msm_geni_serial_tx_empty(struct uart_port *port);
 static int msm_geni_serial_power_on(struct uart_port *uport);
 static void msm_geni_serial_power_off(struct uart_port *uport);
 static int msm_geni_serial_poll_bit(struct uart_port *uport,
 				int offset, int bit_field, bool set);
+static void msm_geni_serial_stop_rx(struct uart_port *uport);
+static int msm_geni_serial_runtime_resume(struct device *dev);
+static int msm_geni_serial_runtime_suspend(struct device *dev);
 
 static atomic_t uart_line_id = ATOMIC_INIT(0);
 
@@ -234,26 +244,40 @@
 					(unsigned int)addr, size, buf);
 }
 
+static bool device_pending_suspend(struct uart_port *uport)
+{
+	int usage_count = atomic_read(&uport->dev->power.usage_count);
+
+	return (pm_runtime_status_suspended(uport->dev) || !usage_count);
+}
+
 static bool check_transfers_inflight(struct uart_port *uport)
 {
 	bool xfer_on = false;
 	bool tx_active = false;
-	bool tx_empty = false;
+	bool tx_fifo_status = false;
 	bool m_cmd_active = false;
 	bool rx_active = false;
 	u32 rx_fifo_status = 0;
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 	u32 geni_status = geni_read_reg_nolog(uport->membase,
 						SE_GENI_STATUS);
+	struct circ_buf *xmit = &uport->state->xmit;
+
 	/* Possible stop tx is called multiple times. */
 	m_cmd_active = geni_status & M_GENI_CMD_ACTIVE;
-	tx_empty = msm_geni_serial_tx_empty(uport);
-	tx_active = m_cmd_active || !tx_empty;
+	if (port->xfer_mode == SE_DMA)
+		tx_fifo_status = port->tx_dma ? 1 : 0;
+	else
+		tx_fifo_status = geni_read_reg_nolog(uport->membase,
+						SE_GENI_TX_FIFO_STATUS);
+	tx_active = m_cmd_active || tx_fifo_status;
 	rx_fifo_status = geni_read_reg_nolog(uport->membase,
 						SE_GENI_RX_FIFO_STATUS);
 	if (rx_fifo_status)
 		rx_active = true;
 
-	if (rx_active || tx_active)
+	if (rx_active || tx_active || !uart_circ_empty(xmit))
 		xfer_on = true;
 
 	return xfer_on;
@@ -289,26 +313,24 @@
 static int vote_clock_on(struct uart_port *uport)
 {
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+	int usage_count = atomic_read(&uport->dev->power.usage_count);
 	int ret = 0;
 
-	if (!pm_runtime_enabled(uport->dev)) {
-		dev_err(uport->dev, "RPM not available.Can't enable clocks\n");
-		return -EPERM;
-	}
 	ret = msm_geni_serial_power_on(uport);
 	if (ret) {
 		dev_err(uport->dev, "Failed to vote clock on\n");
 		return ret;
 	}
 	port->ioctl_count++;
-	IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d\n", __func__,
-					current->comm, port->ioctl_count);
+	IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d usage_count %d\n",
+		__func__, current->comm, port->ioctl_count, usage_count);
 	return 0;
 }
 
 static int vote_clock_off(struct uart_port *uport)
 {
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+	int usage_count = atomic_read(&uport->dev->power.usage_count);
 
 	if (!pm_runtime_enabled(uport->dev)) {
 		dev_err(uport->dev, "RPM not available.Can't enable clocks\n");
@@ -322,10 +344,11 @@
 				__func__, port->ioctl_count);
 		return -EPERM;
 	}
+	wait_for_transfers_inflight(uport);
 	port->ioctl_count--;
 	msm_geni_serial_power_off(uport);
-	IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d\n", __func__,
-				current->comm, port->ioctl_count);
+	IPC_LOG_MSG(port->ipc_log_pwr, "%s%s ioctl %d usage_count %d\n",
+		__func__, current->comm, port->ioctl_count, usage_count);
 	return 0;
 };
 
@@ -355,8 +378,13 @@
 
 static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl)
 {
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev))
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
+		IPC_LOG_MSG(port->ipc_log_misc,
+				"%s.Device is suspended.\n", __func__);
 		return;
+	}
 
 	if (ctl) {
 		wait_for_transfers_inflight(uport);
@@ -377,9 +405,13 @@
 {
 	u32 geni_ios = 0;
 	unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (pm_runtime_status_suspended(uport->dev))
+	if (device_pending_suspend(uport)) {
+		IPC_LOG_MSG(port->ipc_log_misc,
+				"%s.Device is suspended.\n", __func__);
 		return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+	}
 
 	geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS);
 	if (!(geni_ios & IO2_DATA_IN))
@@ -397,9 +429,13 @@
 							unsigned int mctrl)
 {
 	u32 uart_manual_rfr = 0;
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (pm_runtime_status_suspended(uport->dev))
+	if (device_pending_suspend(uport)) {
+		IPC_LOG_MSG(port->ipc_log_misc,
+				"%s.Device is suspended.\n", __func__);
 		return;
+	}
 	if (!(mctrl & TIOCM_RTS))
 		uart_manual_rfr |= (UART_MANUAL_RFR_EN | UART_RFR_NOT_READY);
 	geni_write_reg_nolog(uart_manual_rfr, uport->membase,
@@ -436,19 +472,51 @@
 	int ret = 0;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	ret = pm_runtime_get_sync(uport->dev);
-	if (ret < 0) {
-		IPC_LOG_MSG(port->ipc_log_pwr, "%s Err\n", __func__);
-		WARN_ON_ONCE(1);
-		pm_runtime_put_noidle(uport->dev);
-		pm_runtime_set_suspended(uport->dev);
-		return ret;
+	if (!pm_runtime_enabled(uport->dev)) {
+		if (pm_runtime_status_suspended(uport->dev)) {
+			struct uart_state *state = uport->state;
+			struct tty_port *tport = &state->port;
+			int lock = mutex_trylock(&tport->mutex);
+
+			IPC_LOG_MSG(port->ipc_log_pwr,
+					"%s:Manual resume\n", __func__);
+			pm_runtime_disable(uport->dev);
+			ret = msm_geni_serial_runtime_resume(uport->dev);
+			if (ret) {
+				IPC_LOG_MSG(port->ipc_log_pwr,
+					"%s:Manual RPM CB failed %d\n",
+								__func__, ret);
+			} else {
+				pm_runtime_get_noresume(uport->dev);
+				pm_runtime_set_active(uport->dev);
+			}
+			pm_runtime_enable(uport->dev);
+			if (lock)
+				mutex_unlock(&tport->mutex);
+		}
+	} else {
+		ret = pm_runtime_get_sync(uport->dev);
+		if (ret < 0) {
+			IPC_LOG_MSG(port->ipc_log_pwr, "%s Err\n", __func__);
+			WARN_ON_ONCE(1);
+			pm_runtime_put_noidle(uport->dev);
+			pm_runtime_set_suspended(uport->dev);
+			return ret;
+		}
 	}
 	return 0;
 }
 
 static void msm_geni_serial_power_off(struct uart_port *uport)
 {
+	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+	int usage_count = atomic_read(&uport->dev->power.usage_count);
+
+	if (!usage_count) {
+		IPC_LOG_MSG(port->ipc_log_pwr, "%s: Usage Count is already 0\n",
+								__func__);
+		return;
+	}
 	pm_runtime_mark_last_busy(uport->dev);
 	pm_runtime_put_autosuspend(uport->dev);
 }
@@ -534,6 +602,7 @@
 	msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
 					S_GENI_CMD_ABORT, false);
 	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+	geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG);
 }
 
 #ifdef CONFIG_CONSOLE_POLL
@@ -681,7 +750,8 @@
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	int i, c;
 	unsigned char *rx_char;
@@ -694,6 +764,8 @@
 
 		*(msm_port->rx_fifo) =
 			geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn);
+		if (drop_rx)
+			continue;
 		rx_char = (unsigned char *)msm_port->rx_fifo;
 
 		if (i == (rx_fifo_wc - 1)) {
@@ -710,14 +782,16 @@
 				tty_insert_flip_char(tport, rx_char[c], flag);
 		}
 	}
-	tty_flip_buffer_push(tport);
+	if (!drop_rx)
+		tty_flip_buffer_push(tport);
 	return 0;
 }
 #else
 static int handle_rx_console(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	return -EPERM;
 }
@@ -777,11 +851,17 @@
 	unsigned int geni_status;
 	unsigned int geni_ios;
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
-		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
+	if (!uart_console(uport) && !pm_runtime_active(uport->dev)) {
 		IPC_LOG_MSG(msm_port->ipc_log_misc,
-				"%s.Device is suspended.\n", __func__);
-		return;
+				"%s.Putting in async RPM vote\n", __func__);
+		pm_runtime_get(uport->dev);
+		goto exit_start_tx;
+	}
+
+	if (!uart_console(uport)) {
+		IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.Power on.\n", __func__);
+		pm_runtime_get(uport->dev);
 	}
 
 	if (msm_port->xfer_mode == FIFO_MODE) {
@@ -816,6 +896,9 @@
 	if (!(geni_ios & IO2_DATA_IN))
 		IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ios: 0x%08x\n",
 							__func__, geni_ios);
+exit_start_tx:
+	if (!uart_console(uport))
+		msm_geni_serial_power_off(uport);
 }
 
 static void msm_geni_serial_tx_fsm_rst(struct uart_port *uport)
@@ -843,7 +926,7 @@
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
 		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
 		IPC_LOG_MSG(port->ipc_log_misc,
 				"%s.Device is suspended.\n", __func__);
@@ -895,7 +978,7 @@
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 	int ret;
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
 		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
 		IPC_LOG_MSG(port->ipc_log_misc,
 				"%s.Device is suspended.\n", __func__);
@@ -904,7 +987,8 @@
 
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
 	if (geni_status & S_GENI_CMD_ACTIVE)
-		msm_geni_serial_abort_rx(uport);
+		msm_geni_serial_stop_rx(uport);
+
 	geni_setup_s_cmd(uport->membase, UART_START_READ, 0);
 
 	if (port->xfer_mode == FIFO_MODE) {
@@ -926,8 +1010,8 @@
 		if (ret) {
 			dev_err(uport->dev, "%s: RX Prep dma failed %d\n",
 				__func__, ret);
-			msm_geni_serial_abort_rx(uport);
-			return;
+			msm_geni_serial_stop_rx(uport);
+			goto exit_geni_serial_start_rx;
 		}
 	}
 	/*
@@ -936,6 +1020,7 @@
 	 */
 	mb();
 	geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS);
+exit_geni_serial_start_rx:
 	IPC_LOG_MSG(port->ipc_log_misc, "%s 0x%x\n", __func__, geni_status);
 }
 
@@ -964,9 +1049,9 @@
 	unsigned int geni_m_irq_en;
 	unsigned int geni_status;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
+	u32 irq_clear = S_CMD_DONE_EN;
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
-		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
 		IPC_LOG_MSG(port->ipc_log_misc,
 				"%s.Device is suspended.\n", __func__);
 		return;
@@ -995,13 +1080,24 @@
 	/* Possible stop rx is called multiple times. */
 	if (!(geni_status & S_GENI_CMD_ACTIVE))
 		return;
-	msm_geni_serial_abort_rx(uport);
+	geni_cancel_s_cmd(uport->membase);
+	/*
+	 * Ensure that the cancel goes through before polling for the
+	 * cancel control bit.
+	 */
+	mb();
+	msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG,
+					S_GENI_CMD_CANCEL, false);
+	geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR);
+	if ((geni_status & S_GENI_CMD_ACTIVE))
+		msm_geni_serial_abort_rx(uport);
 }
 
 static int handle_rx_hs(struct uart_port *uport,
 			unsigned int rx_fifo_wc,
 			unsigned int rx_last_byte_valid,
-			unsigned int rx_last)
+			unsigned int rx_last,
+			bool drop_rx)
 {
 	unsigned char *rx_char;
 	struct tty_port *tport;
@@ -1016,6 +1112,8 @@
 	tport = &uport->state->port;
 	ioread32_rep((uport->membase + SE_GENI_RX_FIFOn), msm_port->rx_fifo,
 								rx_fifo_wc);
+	if (drop_rx)
+		return 0;
 
 	rx_char = (unsigned char *)msm_port->rx_fifo;
 	ret = tty_insert_flip_string(tport, rx_char, rx_bytes);
@@ -1031,7 +1129,7 @@
 	return ret;
 }
 
-static int msm_geni_serial_handle_rx(struct uart_port *uport)
+static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx)
 {
 	int ret = 0;
 	unsigned int rx_fifo_status;
@@ -1050,7 +1148,7 @@
 	rx_last = rx_fifo_status & RX_LAST;
 	if (rx_fifo_wc)
 		port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid,
-								rx_last);
+							rx_last, drop_rx);
 	return ret;
 }
 
@@ -1070,9 +1168,26 @@
 
 	xmit->tail = (xmit->tail + msm_port->xmit_size) & (UART_XMIT_SIZE - 1);
 	msm_port->xmit_size = 0;
+	if (uart_console(uport) &&
+	    (uport->icount.tx - msm_port->tx_yield_count) > CONSOLE_YIELD_LEN) {
+		msm_port->tx_yield_count = uport->icount.tx;
+		msm_geni_serial_stop_tx(uport);
+		uart_write_wakeup(uport);
+		goto exit_handle_tx;
+	}
+
 	tx_fifo_status = geni_read_reg_nolog(uport->membase,
 					SE_GENI_TX_FIFO_STATUS);
 	if (uart_circ_empty(xmit) && !tx_fifo_status) {
+		/*
+		 * This will balance out the power vote put in during start_tx
+		 * allowing the device to suspend.
+		 */
+		if (!uart_console(uport)) {
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.Power Off.\n", __func__);
+			msm_geni_serial_power_off(uport);
+		}
 		msm_geni_serial_stop_tx(uport);
 		goto exit_handle_tx;
 	}
@@ -1128,13 +1243,12 @@
 	} else {
 		msm_port->xmit_size = xmit_size;
 	}
-	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-		uart_write_wakeup(uport);
 exit_handle_tx:
+	uart_write_wakeup(uport);
 	return ret;
 }
 
-static int msm_geni_serial_handle_dma_rx(struct uart_port *uport)
+static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx)
 {
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
 	unsigned int rx_bytes = 0;
@@ -1160,6 +1274,8 @@
 					__func__, rx_bytes);
 		goto exit_handle_dma_rx;
 	}
+	if (drop_rx)
+		goto exit_handle_dma_rx;
 
 	tport = &uport->state->port;
 	ret = tty_insert_flip_string(tport, (unsigned char *)(msm_port->rx_buf),
@@ -1195,8 +1311,18 @@
 
 	if (!uart_circ_empty(xmit))
 		msm_geni_serial_prep_dma_tx(uport);
-	else
+	else {
+		/*
+		 * This will balance out the power vote put in during start_tx
+		 * allowing the device to suspend.
+		 */
+		if (!uart_console(uport)) {
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.Power Off.\n", __func__);
+			msm_geni_serial_power_off(uport);
+		}
 		uart_write_wakeup(uport);
+	}
 	return 0;
 }
 
@@ -1211,6 +1337,7 @@
 	unsigned long flags;
 	unsigned int m_irq_en;
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+	bool drop_rx = false;
 
 	spin_lock_irqsave(&uport->lock, flags);
 	if (uart_console(uport) && uport->suspended)
@@ -1238,22 +1365,37 @@
 		goto exit_geni_serial_isr;
 	}
 
-	if (!dma) {
-		if ((s_irq_status & S_GP_IRQ_0_EN) ||
-			(s_irq_status & S_GP_IRQ_1_EN) ||
-			(s_irq_status & S_GP_IRQ_2_EN) ||
-			(s_irq_status & S_GP_IRQ_3_EN)) {
-			IPC_LOG_MSG(msm_port->ipc_log_misc,
-				"%s.sirq 0x%x.\n", __func__, s_irq_status);
-			goto exit_geni_serial_isr;
-		}
-		if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
-			(s_irq_status & S_RX_FIFO_LAST_EN))
-			msm_geni_serial_handle_rx(uport);
+	if (s_irq_status & S_RX_FIFO_WR_ERR_EN) {
+		uport->icount.buf_overrun++;
+		IPC_LOG_MSG(msm_port->ipc_log_misc,
+			"%s.sirq 0x%x buf_overrun:%d\n",
+			__func__, s_irq_status, uport->icount.buf_overrun);
+	}
 
+	if (!dma) {
 		if ((m_irq_status & m_irq_en) &
 		    (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN))
 			msm_geni_serial_handle_tx(uport);
+
+		if ((s_irq_status & S_GP_IRQ_0_EN) ||
+			(s_irq_status & S_GP_IRQ_1_EN)) {
+			if (s_irq_status & S_GP_IRQ_0_EN)
+				uport->icount.parity++;
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.sirq 0x%x parity:%d\n",
+				__func__, s_irq_status, uport->icount.parity);
+			drop_rx = true;
+		} else if ((s_irq_status & S_GP_IRQ_2_EN) ||
+			(s_irq_status & S_GP_IRQ_3_EN)) {
+			uport->icount.brk++;
+			IPC_LOG_MSG(msm_port->ipc_log_misc,
+				"%s.sirq 0x%x break:%d\n",
+				__func__, s_irq_status, uport->icount.brk);
+		}
+
+		if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) ||
+			(s_irq_status & S_RX_FIFO_LAST_EN))
+			msm_geni_serial_handle_rx(uport, drop_rx);
 	} else {
 		if (dma_tx_status) {
 			geni_write_reg_nolog(dma_tx_status, uport->membase,
@@ -1272,13 +1414,22 @@
 				goto exit_geni_serial_isr;
 			}
 			if (dma_rx_status & UART_DMA_RX_ERRS) {
+				if (dma_rx_status & UART_DMA_RX_PARITY_ERR)
+					uport->icount.parity++;
 				IPC_LOG_MSG(msm_port->ipc_log_misc,
-					"%s.Rx Errors.  0x%x.\n",
-						__func__, dma_rx_status);
-				goto exit_geni_serial_isr;
+					"%s.Rx Errors.  0x%x parity:%d\n",
+					__func__, dma_rx_status,
+					uport->icount.parity);
+				drop_rx = true;
+			} else if (dma_rx_status & UART_DMA_RX_BREAK) {
+				uport->icount.brk++;
+				IPC_LOG_MSG(msm_port->ipc_log_misc,
+					"%s.Rx Errors.  0x%x break:%d\n",
+					__func__, dma_rx_status,
+					uport->icount.brk);
 			}
 			if (dma_rx_status & RX_DMA_DONE)
-				msm_geni_serial_handle_dma_rx(uport);
+				msm_geni_serial_handle_dma_rx(uport, drop_rx);
 		}
 	}
 
@@ -1378,9 +1529,18 @@
 	msm_geni_serial_stop_rx(uport);
 	spin_unlock_irqrestore(&uport->lock, flags);
 
-	if (uart_console(uport)) {
-		se_geni_resources_off(&msm_port->serial_rsc);
-	} else {
+	if (!uart_console(uport)) {
+		if (msm_port->ioctl_count) {
+			int i;
+
+			for (i = 0; i < msm_port->ioctl_count; i++) {
+				IPC_LOG_MSG(msm_port->ipc_log_pwr,
+				"%s IOCTL vote present. Forcing off\n",
+								__func__);
+				msm_geni_serial_power_off(uport);
+			}
+			msm_port->ioctl_count = 0;
+		}
 		msm_geni_serial_power_off(uport);
 		if (msm_port->wakeup_irq > 0) {
 			irq_set_irq_wake(msm_port->wakeup_irq, 0);
@@ -1463,47 +1623,6 @@
 	 * framework.
 	 */
 	mb();
-	if (!uart_console(uport)) {
-		char name[30];
-
-		memset(name, 0, sizeof(name));
-		if (!msm_port->ipc_log_rx) {
-			scnprintf(name, sizeof(name), "%s%s",
-					dev_name(uport->dev), "_rx");
-			msm_port->ipc_log_rx = ipc_log_context_create(
-					IPC_LOG_TX_RX_PAGES, name, 0);
-			if (!msm_port->ipc_log_rx)
-				dev_info(uport->dev, "Err in Rx IPC Log\n");
-		}
-		memset(name, 0, sizeof(name));
-		if (!msm_port->ipc_log_tx) {
-			scnprintf(name, sizeof(name), "%s%s",
-					dev_name(uport->dev), "_tx");
-			msm_port->ipc_log_tx = ipc_log_context_create(
-					IPC_LOG_TX_RX_PAGES, name, 0);
-			if (!msm_port->ipc_log_tx)
-				dev_info(uport->dev, "Err in Tx IPC Log\n");
-		}
-		memset(name, 0, sizeof(name));
-		if (!msm_port->ipc_log_pwr) {
-			scnprintf(name, sizeof(name), "%s%s",
-					dev_name(uport->dev), "_pwr");
-			msm_port->ipc_log_pwr = ipc_log_context_create(
-					IPC_LOG_PWR_PAGES, name, 0);
-			if (!msm_port->ipc_log_pwr)
-				dev_info(uport->dev, "Err in Pwr IPC Log\n");
-		}
-		memset(name, 0, sizeof(name));
-		if (!msm_port->ipc_log_misc) {
-			scnprintf(name, sizeof(name), "%s%s",
-					dev_name(uport->dev), "_misc");
-			msm_port->ipc_log_misc = ipc_log_context_create(
-					IPC_LOG_MISC_PAGES, name, 0);
-			if (!msm_port->ipc_log_misc)
-				dev_info(uport->dev, "Err in Misc IPC Log\n");
-		}
-
-	}
 exit_portsetup:
 	return ret;
 }
@@ -1538,7 +1657,6 @@
 			goto exit_startup;
 	}
 
-	msm_geni_serial_start_rx(uport);
 	/*
 	 * Ensure that all the port configuration writes complete
 	 * before returning to the framework.
@@ -1659,11 +1777,17 @@
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 	unsigned long clk_rate;
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
-		IPC_LOG_MSG(port->ipc_log_pwr,
-			"%s Device suspended,vote clocks on.\n", __func__);
-		return;
+	if (!uart_console(uport)) {
+		int ret = msm_geni_serial_power_on(uport);
+
+		if (ret) {
+			IPC_LOG_MSG(port->ipc_log_misc,
+				"%s: Failed to vote clock on:%d\n",
+							__func__, ret);
+			return;
+		}
 	}
+	msm_geni_serial_stop_rx(uport);
 	/* baud rate */
 	baud = uart_get_baud_rate(uport, termios, old, 300, 4000000);
 	port->cur_baud = baud;
@@ -1752,6 +1876,9 @@
 	IPC_LOG_MSG(port->ipc_log_misc, "BitsChar%d stop bit%d\n",
 				bits_per_char, stop_bit_len);
 exit_set_termios:
+	msm_geni_serial_start_rx(uport);
+	if (!uart_console(uport))
+		msm_geni_serial_power_off(uport);
 	return;
 
 }
@@ -1762,11 +1889,8 @@
 	unsigned int is_tx_empty = 1;
 	struct msm_geni_serial_port *port = GET_DEV_PORT(uport);
 
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
-		IPC_LOG_MSG(port->ipc_log_pwr,
-			"%s Device suspended,vote clocks on.\n", __func__);
-		return 1;
-	}
+	if (!uart_console(uport) && device_pending_suspend(uport))
+		return 0;
 
 	if (port->xfer_mode == SE_DMA)
 		tx_fifo_status = port->tx_dma ? 1 : 0;
@@ -2021,13 +2145,69 @@
 }
 #endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */
 
-static void msm_geni_serial_debug_init(struct uart_port *uport)
+static void msm_geni_serial_debug_init(struct uart_port *uport, bool console)
 {
 	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
 
 	msm_port->dbg = debugfs_create_dir(dev_name(uport->dev), NULL);
 	if (IS_ERR_OR_NULL(msm_port->dbg))
 		dev_err(uport->dev, "Failed to create dbg dir\n");
+
+	if (!console) {
+		char name[30];
+
+		memset(name, 0, sizeof(name));
+		if (!msm_port->ipc_log_rx) {
+			scnprintf(name, sizeof(name), "%s%s",
+					dev_name(uport->dev), "_rx");
+			msm_port->ipc_log_rx = ipc_log_context_create(
+					IPC_LOG_TX_RX_PAGES, name, 0);
+			if (!msm_port->ipc_log_rx)
+				dev_info(uport->dev, "Err in Rx IPC Log\n");
+		}
+		memset(name, 0, sizeof(name));
+		if (!msm_port->ipc_log_tx) {
+			scnprintf(name, sizeof(name), "%s%s",
+					dev_name(uport->dev), "_tx");
+			msm_port->ipc_log_tx = ipc_log_context_create(
+					IPC_LOG_TX_RX_PAGES, name, 0);
+			if (!msm_port->ipc_log_tx)
+				dev_info(uport->dev, "Err in Tx IPC Log\n");
+		}
+		memset(name, 0, sizeof(name));
+		if (!msm_port->ipc_log_pwr) {
+			scnprintf(name, sizeof(name), "%s%s",
+					dev_name(uport->dev), "_pwr");
+			msm_port->ipc_log_pwr = ipc_log_context_create(
+					IPC_LOG_PWR_PAGES, name, 0);
+			if (!msm_port->ipc_log_pwr)
+				dev_info(uport->dev, "Err in Pwr IPC Log\n");
+		}
+		memset(name, 0, sizeof(name));
+		if (!msm_port->ipc_log_misc) {
+			scnprintf(name, sizeof(name), "%s%s",
+					dev_name(uport->dev), "_misc");
+			msm_port->ipc_log_misc = ipc_log_context_create(
+					IPC_LOG_MISC_PAGES, name, 0);
+			if (!msm_port->ipc_log_misc)
+				dev_info(uport->dev, "Err in Misc IPC Log\n");
+		}
+	}
+}
+
+static void msm_geni_serial_cons_pm(struct uart_port *uport,
+		unsigned int new_state, unsigned int old_state)
+{
+	struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport);
+
+	if (unlikely(!uart_console(uport)))
+		return;
+
+	if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF)
+		se_geni_resources_on(&msm_port->serial_rsc);
+	else if (new_state == UART_PM_STATE_OFF &&
+			old_state == UART_PM_STATE_ON)
+		se_geni_resources_off(&msm_port->serial_rsc);
 }
 
 static const struct uart_ops msm_geni_console_pops = {
@@ -2046,6 +2226,7 @@
 	.poll_get_char	= msm_geni_serial_get_char,
 	.poll_put_char	= msm_geni_serial_poll_put_char,
 #endif
+	.pm = msm_geni_serial_cons_pm,
 };
 
 static const struct uart_ops msm_geni_serial_pops = {
@@ -2255,7 +2436,7 @@
 				line, uport->fifosize, is_console);
 	device_create_file(uport->dev, &dev_attr_loopback);
 	device_create_file(uport->dev, &dev_attr_xfer_mode);
-	msm_geni_serial_debug_init(uport);
+	msm_geni_serial_debug_init(uport, is_console);
 	dev_port->port_setup = false;
 	return uart_add_one_port(drv, uport);
 
@@ -2283,12 +2464,12 @@
 	int ret = 0;
 
 	wait_for_transfers_inflight(&port->uport);
+	disable_irq(port->uport.irq);
 	ret = se_geni_resources_off(&port->serial_rsc);
 	if (ret) {
 		dev_err(dev, "%s: Error ret %d\n", __func__, ret);
 		goto exit_runtime_suspend;
 	}
-	disable_irq(port->uport.irq);
 	if (port->wakeup_irq > 0) {
 		struct se_geni_rsc *rsc = &port->serial_rsc;
 
@@ -2370,7 +2551,6 @@
 
 	if (uart_console(uport) &&
 	    console_suspend_enabled && uport->suspended) {
-		se_geni_resources_on(&port->serial_rsc);
 		uart_resume_port((struct uart_driver *)uport->private_data,
 									uport);
 		disable_irq(uport->irq);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
new file mode 100644
index 0000000..792fb3b
--- /dev/null
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -0,0 +1,3807 @@
+/* drivers/serial/msm_serial_hs.c
+ *
+ * MSM 7k High speed uart driver
+ *
+ * Copyright (c) 2008 Google Inc.
+ * Copyright (c) 2007-2017, The Linux Foundation. All rights reserved.
+ * Modified: Nick Pelly <npelly@google.com>
+ *
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * Has optional support for uart power management independent of linux
+ * suspend/resume:
+ *
+ * RX wakeup.
+ * UART wakeup can be triggered by RX activity (using a wakeup GPIO on the
+ * UART RX pin). This should only be used if there is not a wakeup
+ * GPIO on the UART CTS, and the first RX byte is known (for example, with the
+ * Bluetooth Texas Instruments HCILL protocol), since the first RX byte will
+ * always be lost. RTS will be asserted even while the UART is off in this mode
+ * of operation. See msm_serial_hs_platform_data.rx_wakeup_irq.
+ */
+
+#include <linux/module.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/atomic.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/tty_flip.h>
+#include <linux/wait.h>
+#include <linux/sysfs.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/ipc_logging.h>
+#include <asm/irq.h>
+#include <linux/kthread.h>
+
+#include <linux/msm-sps.h>
+#include <linux/platform_data/msm_serial_hs.h>
+#include <linux/msm-bus.h>
+
+#include "msm_serial_hs_hwreg.h"
+#define UART_SPS_CONS_PERIPHERAL 0
+#define UART_SPS_PROD_PERIPHERAL 1
+
+#define IPC_MSM_HS_LOG_STATE_PAGES 2
+#define IPC_MSM_HS_LOG_USER_PAGES 2
+#define IPC_MSM_HS_LOG_DATA_PAGES 3
+#define UART_DMA_DESC_NR 8
+#define BUF_DUMP_SIZE 32
+
+/* If the debug_mask gets set to FATAL_LEV,
+ * a fatal error has happened and further IPC logging
+ * is disabled so that this problem can be detected
+ */
+enum {
+	FATAL_LEV = 0U,
+	ERR_LEV = 1U,
+	WARN_LEV = 2U,
+	INFO_LEV = 3U,
+	DBG_LEV = 4U,
+};
+
+#define MSM_HS_DBG(x...) do { \
+	if (msm_uport->ipc_debug_mask >= DBG_LEV) { \
+		if (msm_uport->ipc_msm_hs_log_ctxt) \
+			ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
+	} \
+} while (0)
+
+#define MSM_HS_INFO(x...) do { \
+	if (msm_uport->ipc_debug_mask >= INFO_LEV) {\
+		if (msm_uport->ipc_msm_hs_log_ctxt) \
+			ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
+	} \
+} while (0)
+
+/* warnings and errors show up on console always */
+#define MSM_HS_WARN(x...) do { \
+	pr_warn(x); \
+	if (msm_uport->ipc_msm_hs_log_ctxt && \
+			msm_uport->ipc_debug_mask >= WARN_LEV) \
+		ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
+} while (0)
+
+/* ERROR condition in the driver sets the hs_serial_debug_mask
+ * to ERR_FATAL level, so that this message can be seen
+ * in IPC logging. Further errors continue to log on the console
+ */
+#define MSM_HS_ERR(x...) do { \
+	pr_err(x); \
+	if (msm_uport->ipc_msm_hs_log_ctxt && \
+			msm_uport->ipc_debug_mask >= ERR_LEV) { \
+		ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \
+		msm_uport->ipc_debug_mask = FATAL_LEV; \
+	} \
+} while (0)
+
+#define LOG_USR_MSG(ctx, x...) do { \
+	if (ctx) \
+		ipc_log_string(ctx, x); \
+} while (0)
+
+/*
+ * There are 3 different kind of UART Core available on MSM.
+ * High Speed UART (i.e. Legacy HSUART), GSBI based HSUART
+ * and BSLP based HSUART.
+ */
+enum uart_core_type {
+	LEGACY_HSUART,
+	GSBI_HSUART,
+	BLSP_HSUART,
+};
+
+enum flush_reason {
+	FLUSH_NONE,
+	FLUSH_DATA_READY,
+	FLUSH_DATA_INVALID,  /* values after this indicate invalid data */
+	FLUSH_IGNORE,
+	FLUSH_STOP,
+	FLUSH_SHUTDOWN,
+};
+
+/*
+ * SPS data structures to support HSUART with BAM
+ * @sps_pipe - This struct defines BAM pipe descriptor
+ * @sps_connect - This struct defines a connection's end point
+ * @sps_register - This struct defines a event registration parameters
+ */
+struct msm_hs_sps_ep_conn_data {
+	struct sps_pipe *pipe_handle;
+	struct sps_connect config;
+	struct sps_register_event event;
+};
+
+struct msm_hs_tx {
+	bool dma_in_flight;    /* tx dma in progress */
+	enum flush_reason flush;
+	wait_queue_head_t wait;
+	int tx_count;
+	dma_addr_t dma_base;
+	struct kthread_work kwork;
+	struct kthread_worker kworker;
+	struct task_struct *task;
+	struct msm_hs_sps_ep_conn_data cons;
+	struct timer_list tx_timeout_timer;
+	void *ipc_tx_ctxt;
+};
+
+struct msm_hs_rx {
+	enum flush_reason flush;
+	wait_queue_head_t wait;
+	dma_addr_t rbuffer;
+	unsigned char *buffer;
+	unsigned int buffer_pending;
+	struct delayed_work flip_insert_work;
+	struct kthread_work kwork;
+	struct kthread_worker kworker;
+	struct task_struct *task;
+	struct msm_hs_sps_ep_conn_data prod;
+	unsigned long queued_flag;
+	unsigned long pending_flag;
+	int rx_inx;
+	struct sps_iovec iovec[UART_DMA_DESC_NR]; /* track descriptors */
+	void *ipc_rx_ctxt;
+};
+enum buffer_states {
+	NONE_PENDING = 0x0,
+	FIFO_OVERRUN = 0x1,
+	PARITY_ERROR = 0x2,
+	CHARS_NORMAL = 0x4,
+};
+
+enum msm_hs_pm_state {
+	MSM_HS_PM_ACTIVE,
+	MSM_HS_PM_SUSPENDED,
+	MSM_HS_PM_SYS_SUSPENDED,
+};
+
+/* optional low power wakeup, typically on a GPIO RX irq */
+struct msm_hs_wakeup {
+	int irq;  /* < 0 indicates low power wakeup disabled */
+	unsigned char ignore;  /* bool */
+
+	/* bool: inject char into rx tty on wakeup */
+	bool inject_rx;
+	unsigned char rx_to_inject;
+	bool enabled;
+	bool freed;
+};
+
+struct msm_hs_port {
+	struct uart_port uport;
+	unsigned long imr_reg;  /* shadow value of UARTDM_IMR */
+	struct clk *clk;
+	struct clk *pclk;
+	struct msm_hs_tx tx;
+	struct msm_hs_rx rx;
+	atomic_t resource_count;
+	struct msm_hs_wakeup wakeup;
+
+	struct dentry *loopback_dir;
+	struct work_struct clock_off_w; /* work for actual clock off */
+	struct workqueue_struct *hsuart_wq; /* hsuart workqueue */
+	struct mutex mtx; /* resource access mutex */
+	enum uart_core_type uart_type;
+	unsigned long bam_handle;
+	resource_size_t bam_mem;
+	int bam_irq;
+	unsigned char __iomem *bam_base;
+	unsigned int bam_tx_ep_pipe_index;
+	unsigned int bam_rx_ep_pipe_index;
+	/* struct sps_event_notify is an argument passed when triggering a
+	 * callback event object registered for an SPS connection end point.
+	 */
+	struct sps_event_notify notify;
+	/* bus client handler */
+	u32 bus_perf_client;
+	/* BLSP UART required BUS Scaling data */
+	struct msm_bus_scale_pdata *bus_scale_table;
+	bool rx_bam_inprogress;
+	wait_queue_head_t bam_disconnect_wait;
+	bool use_pinctrl;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *gpio_state_active;
+	struct pinctrl_state *gpio_state_suspend;
+	bool flow_control;
+	enum msm_hs_pm_state pm_state;
+	atomic_t client_count;
+	bool obs; /* out of band sleep flag */
+	atomic_t client_req_state;
+	void *ipc_msm_hs_log_ctxt;
+	void *ipc_msm_hs_pwr_ctxt;
+	int ipc_debug_mask;
+};
+
+static const struct of_device_id msm_hs_match_table[] = {
+	{ .compatible = "qcom,msm-hsuart-v14"},
+	{}
+};
+
+
+#define MSM_UARTDM_BURST_SIZE 16   /* DM burst size (in bytes) */
+#define UARTDM_TX_BUF_SIZE UART_XMIT_SIZE
+#define UARTDM_RX_BUF_SIZE 512
+#define RETRY_TIMEOUT 5
+#define UARTDM_NR 256
+#define BAM_PIPE_MIN 0
+#define BAM_PIPE_MAX 11
+#define BUS_SCALING 1
+#define BUS_RESET 0
+#define RX_FLUSH_COMPLETE_TIMEOUT 300 /* In jiffies */
+#define BLSP_UART_CLK_FMAX 63160000
+
+static struct dentry *debug_base;
+static struct platform_driver msm_serial_hs_platform_driver;
+static struct uart_driver msm_hs_driver;
+static const struct uart_ops msm_hs_ops;
+static void msm_hs_start_rx_locked(struct uart_port *uport);
+static void msm_serial_hs_rx_work(struct kthread_work *work);
+static void flip_insert_work(struct work_struct *work);
+static void msm_hs_bus_voting(struct msm_hs_port *msm_uport, unsigned int vote);
+static struct msm_hs_port *msm_hs_get_hs_port(int port_index);
+static void msm_hs_queue_rx_desc(struct msm_hs_port *msm_uport);
+static int disconnect_rx_endpoint(struct msm_hs_port *msm_uport);
+static int msm_hs_pm_resume(struct device *dev);
+
+#define UARTDM_TO_MSM(uart_port) \
+	container_of((uart_port), struct msm_hs_port, uport)
+
+static int msm_hs_ioctl(struct uart_port *uport, unsigned int cmd,
+						unsigned long arg)
+{
+	int ret = 0, state = 1;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (!msm_uport)
+		return -ENODEV;
+
+	switch (cmd) {
+	case MSM_ENABLE_UART_CLOCK: {
+		ret = msm_hs_request_clock_on(&msm_uport->uport);
+		break;
+	}
+	case MSM_DISABLE_UART_CLOCK: {
+		ret = msm_hs_request_clock_off(&msm_uport->uport);
+		break;
+	}
+	case MSM_GET_UART_CLOCK_STATUS: {
+		/* Return value 0 - UART CLOCK is OFF
+		 * Return value 1 - UART CLOCK is ON
+		 */
+
+		if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
+			state = 0;
+		ret = state;
+		MSM_HS_INFO("%s():GET UART CLOCK STATUS: cmd=%d state=%d\n",
+			__func__, cmd, state);
+		break;
+	}
+	default: {
+		MSM_HS_INFO("%s():Unknown cmd specified: cmd=%d\n", __func__,
+			   cmd);
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+	}
+
+	return ret;
+}
+
+/*
+ * This function is called initially during probe and then
+ * through the runtime PM framework. The function directly calls
+ * resource APIs to enable them.
+ */
+
+static int msm_hs_clk_bus_vote(struct msm_hs_port *msm_uport)
+{
+	int rc = 0;
+
+	msm_hs_bus_voting(msm_uport, BUS_SCALING);
+	/* Turn on core clk and iface clk */
+	if (msm_uport->pclk) {
+		rc = clk_prepare_enable(msm_uport->pclk);
+		if (rc) {
+			dev_err(msm_uport->uport.dev,
+				"%s: Could not turn on pclk [%d]\n",
+				__func__, rc);
+			goto busreset;
+		}
+	}
+	rc = clk_prepare_enable(msm_uport->clk);
+	if (rc) {
+		dev_err(msm_uport->uport.dev,
+			"%s: Could not turn on core clk [%d]\n",
+			__func__, rc);
+		goto core_unprepare;
+	}
+	MSM_HS_DBG("%s: Clock ON successful\n", __func__);
+	return rc;
+core_unprepare:
+	clk_disable_unprepare(msm_uport->pclk);
+busreset:
+	msm_hs_bus_voting(msm_uport, BUS_RESET);
+	return rc;
+}
+
+/*
+ * This function is called initially during probe and then
+ * through the runtime PM framework. The function directly calls
+ * resource apis to disable them.
+ */
+static void msm_hs_clk_bus_unvote(struct msm_hs_port *msm_uport)
+{
+	clk_disable_unprepare(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_disable_unprepare(msm_uport->pclk);
+	msm_hs_bus_voting(msm_uport, BUS_RESET);
+	MSM_HS_DBG("%s: Clock OFF successful\n", __func__);
+}
+
+ /* Remove vote for resources when done */
+static void msm_hs_resource_unvote(struct msm_hs_port *msm_uport)
+{
+	struct uart_port *uport = &(msm_uport->uport);
+	int rc = atomic_read(&msm_uport->resource_count);
+
+	MSM_HS_DBG("%s(): power usage count %d", __func__, rc);
+	if (rc <= 0) {
+		MSM_HS_WARN("%s(): rc zero, bailing\n", __func__);
+		WARN_ON(1);
+		return;
+	}
+	atomic_dec(&msm_uport->resource_count);
+	pm_runtime_mark_last_busy(uport->dev);
+	pm_runtime_put_autosuspend(uport->dev);
+}
+
+ /* Vote for resources before accessing them */
+static void msm_hs_resource_vote(struct msm_hs_port *msm_uport)
+{
+	int ret;
+	struct uart_port *uport = &(msm_uport->uport);
+
+	ret = pm_runtime_get_sync(uport->dev);
+	if (ret < 0 || msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s:%s runtime callback not invoked ret:%d st:%d",
+			__func__, dev_name(uport->dev), ret,
+					msm_uport->pm_state);
+		msm_hs_pm_resume(uport->dev);
+	}
+	atomic_inc(&msm_uport->resource_count);
+}
+
+/* Check if the uport line number matches with user id stored in pdata.
+ * User id information is stored during initialization. This function
+ * ensues that the same device is selected
+ */
+
+static struct msm_hs_port *get_matching_hs_port(struct platform_device *pdev)
+{
+	struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+	struct msm_hs_port *msm_uport = msm_hs_get_hs_port(pdev->id);
+
+	if ((!msm_uport) || (msm_uport->uport.line != pdev->id
+	   && msm_uport->uport.line != pdata->userid)) {
+		pr_err("uport line number mismatch!");
+		WARN_ON(1);
+		return NULL;
+	}
+
+	return msm_uport;
+}
+
+static ssize_t show_clock(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	int state = 1;
+	ssize_t ret = 0;
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+
+	/* This check should not fail */
+	if (msm_uport) {
+		if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
+			state = 0;
+		ret = snprintf(buf, PAGE_SIZE, "%d\n", state);
+	}
+	return ret;
+}
+
+static ssize_t set_clock(struct device *dev, struct device_attribute *attr,
+			 const char *buf, size_t count)
+{
+	int state;
+	ssize_t ret = 0;
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+
+	/* This check should not fail */
+	if (msm_uport) {
+		state = buf[0] - '0';
+		switch (state) {
+		case 0:
+			MSM_HS_DBG("%s: Request clock OFF\n", __func__);
+			msm_hs_request_clock_off(&msm_uport->uport);
+			ret = count;
+			break;
+		case 1:
+			MSM_HS_DBG("%s: Request clock ON\n", __func__);
+			msm_hs_request_clock_on(&msm_uport->uport);
+			ret = count;
+			break;
+		default:
+			ret = -EINVAL;
+		}
+	}
+	return ret;
+}
+
+static DEVICE_ATTR(clock, 0644, show_clock, set_clock);
+
+static ssize_t show_debug_mask(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t ret = 0;
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+
+	/* This check should not fail */
+	if (msm_uport)
+		ret = snprintf(buf, sizeof(int), "%u\n",
+					msm_uport->ipc_debug_mask);
+	return ret;
+}
+
+static ssize_t set_debug_mask(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct platform_device *pdev = container_of(dev, struct
+						    platform_device, dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+
+	/* This check should not fail */
+	if (msm_uport) {
+		msm_uport->ipc_debug_mask = buf[0] - '0';
+		if (msm_uport->ipc_debug_mask < FATAL_LEV ||
+				msm_uport->ipc_debug_mask > DBG_LEV) {
+			/* set to default level */
+			msm_uport->ipc_debug_mask = INFO_LEV;
+			MSM_HS_ERR("Range is 0 to 4;Set to default level 3\n");
+			return -EINVAL;
+		}
+	}
+	return count;
+}
+
+static DEVICE_ATTR(debug_mask, 0644, show_debug_mask,
+							set_debug_mask);
+
+static inline bool is_use_low_power_wakeup(struct msm_hs_port *msm_uport)
+{
+	return msm_uport->wakeup.irq > 0;
+}
+
+static void msm_hs_bus_voting(struct msm_hs_port *msm_uport, unsigned int vote)
+{
+	int ret;
+
+	if (msm_uport->bus_perf_client) {
+		MSM_HS_DBG("Bus voting:%d\n", vote);
+		ret = msm_bus_scale_client_update_request(
+				msm_uport->bus_perf_client, vote);
+		if (ret)
+			MSM_HS_ERR("%s(): Failed for Bus voting: %d\n",
+							__func__, vote);
+	}
+}
+
+static inline unsigned int msm_hs_read(struct uart_port *uport,
+				       unsigned int index)
+{
+	return readl_relaxed(uport->membase + index);
+}
+
+static inline void msm_hs_write(struct uart_port *uport, unsigned int index,
+				 unsigned int value)
+{
+	writel_relaxed(value, uport->membase + index);
+}
+
+static int sps_rx_disconnect(struct sps_pipe *sps_pipe_handler)
+{
+	struct sps_connect config;
+	int ret;
+
+	ret = sps_get_config(sps_pipe_handler, &config);
+	if (ret) {
+		pr_err("%s: sps_get_config() failed ret %d\n", __func__, ret);
+		return ret;
+	}
+	config.options |= SPS_O_POLL;
+	ret = sps_set_config(sps_pipe_handler, &config);
+	if (ret) {
+		pr_err("%s: sps_set_config() failed ret %d\n", __func__, ret);
+		return ret;
+	}
+	return sps_disconnect(sps_pipe_handler);
+}
+
+static void hex_dump_ipc(struct msm_hs_port *msm_uport, void *ipc_ctx,
+			char *prefix, char *string, u64 addr, int size)
+
+{
+	char buf[(BUF_DUMP_SIZE * 3) + 2];
+	int len = 0;
+
+	len = min(size, BUF_DUMP_SIZE);
+	/*
+	 * Print upto 32 data bytes, 32 bytes per line, 1 byte at a time and
+	 * don't include the ASCII text at the end of the buffer.
+	 */
+	hex_dump_to_buffer(string, len, 32, 1, buf, sizeof(buf), false);
+	ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix,
+					(unsigned int)addr, size, buf);
+}
+
+/*
+ * This API read and provides UART Core registers information.
+ */
+static void dump_uart_hs_registers(struct msm_hs_port *msm_uport)
+{
+	struct uart_port *uport = &(msm_uport->uport);
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_INFO("%s:Failed clocks are off, resource_count %d",
+			__func__, atomic_read(&msm_uport->resource_count));
+		return;
+	}
+
+	MSM_HS_DBG(
+	"MR1:%x MR2:%x TFWR:%x RFWR:%x DMEN:%x IMR:%x MISR:%x NCF_TX:%x\n",
+	msm_hs_read(uport, UART_DM_MR1),
+	msm_hs_read(uport, UART_DM_MR2),
+	msm_hs_read(uport, UART_DM_TFWR),
+	msm_hs_read(uport, UART_DM_RFWR),
+	msm_hs_read(uport, UART_DM_DMEN),
+	msm_hs_read(uport, UART_DM_IMR),
+	msm_hs_read(uport, UART_DM_MISR),
+	msm_hs_read(uport, UART_DM_NCF_TX));
+	MSM_HS_INFO("SR:%x ISR:%x DMRX:%x RX_SNAP:%x TXFS:%x RXFS:%x\n",
+	msm_hs_read(uport, UART_DM_SR),
+	msm_hs_read(uport, UART_DM_ISR),
+	msm_hs_read(uport, UART_DM_DMRX),
+	msm_hs_read(uport, UART_DM_RX_TOTAL_SNAP),
+	msm_hs_read(uport, UART_DM_TXFS),
+	msm_hs_read(uport, UART_DM_RXFS));
+	MSM_HS_DBG("rx.flush:%u\n", msm_uport->rx.flush);
+}
+
+static int msm_serial_loopback_enable_set(void *data, u64 val)
+{
+	struct msm_hs_port *msm_uport = data;
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned long flags;
+	int ret = 0;
+
+	msm_hs_resource_vote(msm_uport);
+
+	if (val) {
+		spin_lock_irqsave(&uport->lock, flags);
+		ret = msm_hs_read(uport, UART_DM_MR2);
+		ret |= (UARTDM_MR2_LOOP_MODE_BMSK |
+			UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+		msm_hs_write(uport, UART_DM_MR2, ret);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	} else {
+		spin_lock_irqsave(&uport->lock, flags);
+		ret = msm_hs_read(uport, UART_DM_MR2);
+		ret &= ~(UARTDM_MR2_LOOP_MODE_BMSK |
+			UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK);
+		msm_hs_write(uport, UART_DM_MR2, ret);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	}
+	/* Calling CLOCK API. Hence mb() requires here. */
+	mb();
+
+	msm_hs_resource_unvote(msm_uport);
+	return 0;
+}
+
+static int msm_serial_loopback_enable_get(void *data, u64 *val)
+{
+	struct msm_hs_port *msm_uport = data;
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned long flags;
+	int ret = 0;
+
+	msm_hs_resource_vote(msm_uport);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	ret = msm_hs_read(&msm_uport->uport, UART_DM_MR2);
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	msm_hs_resource_unvote(msm_uport);
+
+	*val = (ret & UARTDM_MR2_LOOP_MODE_BMSK) ? 1 : 0;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(loopback_enable_fops, msm_serial_loopback_enable_get,
+			msm_serial_loopback_enable_set, "%llu\n");
+
+/*
+ * msm_serial_hs debugfs node: <debugfs_root>/msm_serial_hs/loopback.<id>
+ * writing 1 turns on internal loopback mode in HW. Useful for automation
+ * test scripts.
+ * writing 0 disables the internal loopback mode. Default is disabled.
+ */
+static void msm_serial_debugfs_init(struct msm_hs_port *msm_uport,
+					   int id)
+{
+	char node_name[15];
+
+	snprintf(node_name, sizeof(node_name), "loopback.%d", id);
+	msm_uport->loopback_dir = debugfs_create_file(node_name,
+						0644,
+						debug_base,
+						msm_uport,
+						&loopback_enable_fops);
+
+	if (IS_ERR_OR_NULL(msm_uport->loopback_dir))
+		MSM_HS_ERR("%s(): Cannot create loopback.%d debug entry",
+							__func__, id);
+}
+
+static int msm_hs_remove(struct platform_device *pdev)
+{
+
+	struct msm_hs_port *msm_uport;
+	struct device *dev;
+
+	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+		pr_err("Invalid plaform device ID = %d\n", pdev->id);
+		return -EINVAL;
+	}
+
+	msm_uport = get_matching_hs_port(pdev);
+	if (!msm_uport)
+		return -EINVAL;
+
+	dev = msm_uport->uport.dev;
+	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_clock.attr);
+	sysfs_remove_file(&pdev->dev.kobj, &dev_attr_debug_mask.attr);
+	debugfs_remove(msm_uport->loopback_dir);
+
+	dma_free_coherent(msm_uport->uport.dev,
+			UART_DMA_DESC_NR * UARTDM_RX_BUF_SIZE,
+			msm_uport->rx.buffer, msm_uport->rx.rbuffer);
+
+	msm_uport->rx.buffer = NULL;
+	msm_uport->rx.rbuffer = 0;
+
+	destroy_workqueue(msm_uport->hsuart_wq);
+	mutex_destroy(&msm_uport->mtx);
+
+	uart_remove_one_port(&msm_hs_driver, &msm_uport->uport);
+	clk_put(msm_uport->clk);
+	if (msm_uport->pclk)
+		clk_put(msm_uport->pclk);
+
+	iounmap(msm_uport->uport.membase);
+
+	return 0;
+}
+
+
+/* Connect a UART peripheral's SPS endpoint(consumer endpoint)
+ *
+ * Also registers a SPS callback function for the consumer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_tx(struct msm_hs_port *msm_uport)
+{
+	int ret;
+	struct uart_port *uport = &msm_uport->uport;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct sps_pipe *sps_pipe_handle = tx->cons.pipe_handle;
+	struct sps_connect *sps_config = &tx->cons.config;
+	struct sps_register_event *sps_event = &tx->cons.event;
+	unsigned long flags;
+	unsigned int data;
+
+	if (tx->flush != FLUSH_SHUTDOWN) {
+		MSM_HS_ERR("%s:Invalid flush state:%d\n", __func__, tx->flush);
+		return 0;
+	}
+
+	/* Establish connection between peripheral and memory endpoint */
+	ret = sps_connect(sps_pipe_handle, sps_config);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: sps_connect() failed for tx!!\n"
+		"pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
+		return ret;
+	}
+	/* Register callback event for EOT (End of transfer) event. */
+	ret = sps_register_event(sps_pipe_handle, sps_event);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: sps_connect() failed for tx!!\n"
+		"pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
+		goto reg_event_err;
+	}
+
+	spin_lock_irqsave(&(msm_uport->uport.lock), flags);
+	msm_uport->tx.flush = FLUSH_STOP;
+	spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
+
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	/* Enable UARTDM Tx BAM Interface */
+	data |= UARTDM_TX_BAM_ENABLE_BMSK;
+	msm_hs_write(uport, UART_DM_DMEN, data);
+
+	msm_hs_write(uport, UART_DM_CR, RESET_TX);
+	msm_hs_write(uport, UART_DM_CR, START_TX_BAM_IFC);
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_EN_BMSK);
+
+	MSM_HS_DBG("%s(): TX Connect", __func__);
+	return 0;
+
+reg_event_err:
+	sps_disconnect(sps_pipe_handle);
+	return ret;
+}
+
+/* Connect a UART peripheral's SPS endpoint(producer endpoint)
+ *
+ * Also registers a SPS callback function for the producer
+ * process with the SPS driver
+ *
+ * @uport - Pointer to uart uport structure
+ *
+ * @return - 0 if successful else negative value.
+ *
+ */
+
+static int msm_hs_spsconnect_rx(struct uart_port *uport)
+{
+	int ret;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+	struct sps_connect *sps_config = &rx->prod.config;
+	struct sps_register_event *sps_event = &rx->prod.event;
+	unsigned long flags;
+
+	/* Establish connection between peripheral and memory endpoint */
+	ret = sps_connect(sps_pipe_handle, sps_config);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: sps_connect() failed for rx!!\n"
+		"pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
+		return ret;
+	}
+	/* Register callback event for DESC_DONE event. */
+	ret = sps_register_event(sps_pipe_handle, sps_event);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: sps_connect() failed for rx!!\n"
+		"pipe_handle=0x%p ret=%d", sps_pipe_handle, ret);
+		goto reg_event_err;
+	}
+	spin_lock_irqsave(&uport->lock, flags);
+	if (msm_uport->rx.pending_flag)
+		MSM_HS_WARN("%s(): Buffers may be pending 0x%lx",
+		__func__, msm_uport->rx.pending_flag);
+	msm_uport->rx.queued_flag = 0;
+	msm_uport->rx.pending_flag = 0;
+	msm_uport->rx.rx_inx = 0;
+	msm_uport->rx.flush = FLUSH_STOP;
+	spin_unlock_irqrestore(&uport->lock, flags);
+	MSM_HS_DBG("%s(): RX Connect\n", __func__);
+	return 0;
+
+reg_event_err:
+	sps_disconnect(sps_pipe_handle);
+	return ret;
+}
+
+/*
+ * programs the UARTDM_CSR register with correct bit rates
+ *
+ * Interrupts should be disabled before we are called, as
+ * we modify Set Baud rate
+ * Set receive stale interrupt level, dependent on Bit Rate
+ * Goal is to have around 8 ms before indicate stale.
+ * roundup (((Bit Rate * .008) / 10) + 1
+ */
+static void msm_hs_set_bps_locked(struct uart_port *uport,
+			       unsigned int bps)
+{
+	unsigned long rxstale;
+	unsigned long data;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	switch (bps) {
+	case 300:
+		msm_hs_write(uport, UART_DM_CSR, 0x00);
+		rxstale = 1;
+		break;
+	case 600:
+		msm_hs_write(uport, UART_DM_CSR, 0x11);
+		rxstale = 1;
+		break;
+	case 1200:
+		msm_hs_write(uport, UART_DM_CSR, 0x22);
+		rxstale = 1;
+		break;
+	case 2400:
+		msm_hs_write(uport, UART_DM_CSR, 0x33);
+		rxstale = 1;
+		break;
+	case 4800:
+		msm_hs_write(uport, UART_DM_CSR, 0x44);
+		rxstale = 1;
+		break;
+	case 9600:
+		msm_hs_write(uport, UART_DM_CSR, 0x55);
+		rxstale = 2;
+		break;
+	case 14400:
+		msm_hs_write(uport, UART_DM_CSR, 0x66);
+		rxstale = 3;
+		break;
+	case 19200:
+		msm_hs_write(uport, UART_DM_CSR, 0x77);
+		rxstale = 4;
+		break;
+	case 28800:
+		msm_hs_write(uport, UART_DM_CSR, 0x88);
+		rxstale = 6;
+		break;
+	case 38400:
+		msm_hs_write(uport, UART_DM_CSR, 0x99);
+		rxstale = 8;
+		break;
+	case 57600:
+		msm_hs_write(uport, UART_DM_CSR, 0xaa);
+		rxstale = 16;
+		break;
+	case 76800:
+		msm_hs_write(uport, UART_DM_CSR, 0xbb);
+		rxstale = 16;
+		break;
+	case 115200:
+		msm_hs_write(uport, UART_DM_CSR, 0xcc);
+		rxstale = 31;
+		break;
+	case 230400:
+		msm_hs_write(uport, UART_DM_CSR, 0xee);
+		rxstale = 31;
+		break;
+	case 460800:
+		msm_hs_write(uport, UART_DM_CSR, 0xff);
+		rxstale = 31;
+		break;
+	case 4000000:
+	case 3686400:
+	case 3200000:
+	case 3500000:
+	case 3000000:
+	case 2500000:
+	case 2000000:
+	case 1500000:
+	case 1152000:
+	case 1000000:
+	case 921600:
+		msm_hs_write(uport, UART_DM_CSR, 0xff);
+		rxstale = 31;
+		break;
+	default:
+		msm_hs_write(uport, UART_DM_CSR, 0xff);
+		/* default to 9600 */
+		bps = 9600;
+		rxstale = 2;
+		break;
+	}
+	/*
+	 * uart baud rate depends on CSR and MND Values
+	 * we are updating CSR before and then calling
+	 * clk_set_rate which updates MND Values. Hence
+	 * dsb requires here.
+	 */
+	mb();
+	if (bps > 460800) {
+		uport->uartclk = bps * 16;
+		/* BLSP based UART supports maximum clock frequency
+		 * of 63.16 Mhz. With this (63.16 Mhz) clock frequency
+		 * UART can support baud rate of 3.94 Mbps which is
+		 * equivalent to 4 Mbps.
+		 * UART hardware is robust enough to handle this
+		 * deviation to achieve baud rate ~4 Mbps.
+		 */
+		if (bps == 4000000)
+			uport->uartclk = BLSP_UART_CLK_FMAX;
+	} else {
+		uport->uartclk = 7372800;
+	}
+
+	if (clk_set_rate(msm_uport->clk, uport->uartclk)) {
+		MSM_HS_WARN("Error setting clock rate on UART\n");
+		WARN_ON(1);
+	}
+
+	data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
+	data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+
+	msm_hs_write(uport, UART_DM_IPR, data);
+	/*
+	 * It is suggested to do reset of transmitter and receiver after
+	 * changing any protocol configuration. Here Baud rate and stale
+	 * timeout are getting updated. Hence reset transmitter and receiver.
+	 */
+	msm_hs_write(uport, UART_DM_CR, RESET_TX);
+	msm_hs_write(uport, UART_DM_CR, RESET_RX);
+}
+
+
+static void msm_hs_set_std_bps_locked(struct uart_port *uport,
+			       unsigned int bps)
+{
+	unsigned long rxstale;
+	unsigned long data;
+
+	switch (bps) {
+	case 9600:
+		msm_hs_write(uport, UART_DM_CSR, 0x99);
+		rxstale = 2;
+		break;
+	case 14400:
+		msm_hs_write(uport, UART_DM_CSR, 0xaa);
+		rxstale = 3;
+		break;
+	case 19200:
+		msm_hs_write(uport, UART_DM_CSR, 0xbb);
+		rxstale = 4;
+		break;
+	case 28800:
+		msm_hs_write(uport, UART_DM_CSR, 0xcc);
+		rxstale = 6;
+		break;
+	case 38400:
+		msm_hs_write(uport, UART_DM_CSR, 0xdd);
+		rxstale = 8;
+		break;
+	case 57600:
+		msm_hs_write(uport, UART_DM_CSR, 0xee);
+		rxstale = 16;
+		break;
+	case 115200:
+		msm_hs_write(uport, UART_DM_CSR, 0xff);
+		rxstale = 31;
+		break;
+	default:
+		msm_hs_write(uport, UART_DM_CSR, 0x99);
+		/* default to 9600 */
+		bps = 9600;
+		rxstale = 2;
+		break;
+	}
+
+	data = rxstale & UARTDM_IPR_STALE_LSB_BMSK;
+	data |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
+
+	msm_hs_write(uport, UART_DM_IPR, data);
+}
+
+static void msm_hs_enable_flow_control(struct uart_port *uport, bool override)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int data;
+
+	if (msm_uport->flow_control || override) {
+		/* Enable RFR line */
+		msm_hs_write(uport, UART_DM_CR, RFR_LOW);
+		/* Enable auto RFR */
+		data = msm_hs_read(uport, UART_DM_MR1);
+		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UART_DM_MR1, data);
+		/* Ensure register IO completion */
+		mb();
+	}
+}
+
+static void msm_hs_disable_flow_control(struct uart_port *uport, bool override)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	unsigned int data;
+
+	/*
+	 * Clear the Rx Ready Ctl bit - This ensures that
+	 * flow control lines stop the other side from sending
+	 * data while we change the parameters
+	 */
+
+	if (msm_uport->flow_control || override) {
+		data = msm_hs_read(uport, UART_DM_MR1);
+		/* disable auto ready-for-receiving */
+		data &= ~UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_hs_write(uport, UART_DM_MR1, data);
+		/* Disable RFR line */
+		msm_hs_write(uport, UART_DM_CR, RFR_HIGH);
+		/* Ensure register IO completion */
+		mb();
+	}
+}
+
+/*
+ * termios :  new ktermios
+ * oldtermios:  old ktermios previous setting
+ *
+ * Configure the serial port
+ */
+static void msm_hs_set_termios(struct uart_port *uport,
+				   struct ktermios *termios,
+				   struct ktermios *oldtermios)
+{
+	unsigned int bps;
+	unsigned long data;
+	unsigned int c_cflag = termios->c_cflag;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	/**
+	 * set_termios can be invoked from the framework when
+	 * the clocks are off and the client has not had a chance
+	 * to turn them on. Make sure that they are on
+	 */
+	msm_hs_resource_vote(msm_uport);
+	mutex_lock(&msm_uport->mtx);
+	msm_hs_write(uport, UART_DM_IMR, 0);
+
+	msm_hs_disable_flow_control(uport, true);
+
+	/*
+	 * Disable Rx channel of UARTDM
+	 * DMA Rx Stall happens if enqueue and flush of Rx command happens
+	 * concurrently. Hence before changing the baud rate/protocol
+	 * configuration and sending flush command to ADM, disable the Rx
+	 * channel of UARTDM.
+	 * Note: should not reset the receiver here immediately as it is not
+	 * suggested to do disable/reset or reset/disable at the same time.
+	 */
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	/* Disable UARTDM RX BAM Interface */
+	data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+	msm_hs_write(uport, UART_DM_DMEN, data);
+
+	/*
+	 * Reset RX and TX.
+	 * Resetting the RX enables it, therefore we must reset and disable.
+	 */
+	msm_hs_write(uport, UART_DM_CR, RESET_RX);
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_DISABLE_BMSK);
+	msm_hs_write(uport, UART_DM_CR, RESET_TX);
+
+	/* 300 is the minimum baud support by the driver  */
+	bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000);
+
+	/* Temporary remapping  200 BAUD to 3.2 mbps */
+	if (bps == 200)
+		bps = 3200000;
+
+	uport->uartclk = clk_get_rate(msm_uport->clk);
+	if (!uport->uartclk)
+		msm_hs_set_std_bps_locked(uport, bps);
+	else
+		msm_hs_set_bps_locked(uport, bps);
+
+	data = msm_hs_read(uport, UART_DM_MR2);
+	data &= ~UARTDM_MR2_PARITY_MODE_BMSK;
+	/* set parity */
+	if (c_cflag & PARENB) {
+		if (c_cflag & PARODD)
+			data |= ODD_PARITY;
+		else if (c_cflag & CMSPAR)
+			data |= SPACE_PARITY;
+		else
+			data |= EVEN_PARITY;
+	}
+
+	/* Set bits per char */
+	data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK;
+
+	switch (c_cflag & CSIZE) {
+	case CS5:
+		data |= FIVE_BPC;
+		break;
+	case CS6:
+		data |= SIX_BPC;
+		break;
+	case CS7:
+		data |= SEVEN_BPC;
+		break;
+	default:
+		data |= EIGHT_BPC;
+		break;
+	}
+	/* stop bits */
+	if (c_cflag & CSTOPB) {
+		data |= STOP_BIT_TWO;
+	} else {
+		/* otherwise 1 stop bit */
+		data |= STOP_BIT_ONE;
+	}
+	data |= UARTDM_MR2_ERROR_MODE_BMSK;
+	/* write parity/bits per char/stop bit configuration */
+	msm_hs_write(uport, UART_DM_MR2, data);
+
+	uport->ignore_status_mask = termios->c_iflag & INPCK;
+	uport->ignore_status_mask |= termios->c_iflag & IGNPAR;
+	uport->ignore_status_mask |= termios->c_iflag & IGNBRK;
+
+	uport->read_status_mask = (termios->c_cflag & CREAD);
+
+	/* Set Transmit software time out */
+	uart_update_timeout(uport, c_cflag, bps);
+
+	/* Enable UARTDM Rx BAM Interface */
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	data |= UARTDM_RX_BAM_ENABLE_BMSK;
+	msm_hs_write(uport, UART_DM_DMEN, data);
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_EN_BMSK);
+	/* Issue TX,RX BAM Start IFC command */
+	msm_hs_write(uport, UART_DM_CR, START_TX_BAM_IFC);
+	msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
+	/* Ensure Register Writes Complete */
+	mb();
+
+	/* Configure HW flow control
+	 * UART Core would see status of CTS line when it is sending data
+	 * to remote uart to confirm that it can receive or not.
+	 * UART Core would trigger RFR if it is not having any space with
+	 * RX FIFO.
+	 */
+	/* Pulling RFR line high */
+	msm_hs_write(uport, UART_DM_CR, RFR_LOW);
+	data = msm_hs_read(uport, UART_DM_MR1);
+	data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK);
+	if (c_cflag & CRTSCTS) {
+		data |= UARTDM_MR1_CTS_CTL_BMSK;
+		data |= UARTDM_MR1_RX_RDY_CTL_BMSK;
+		msm_uport->flow_control = true;
+	}
+	msm_hs_write(uport, UART_DM_MR1, data);
+	MSM_HS_INFO("%s: Cflags 0x%x Baud %u\n", __func__, c_cflag, bps);
+
+	mutex_unlock(&msm_uport->mtx);
+
+	msm_hs_resource_unvote(msm_uport);
+}
+
+/*
+ *  Standard API, Transmitter
+ *  Any character in the transmit shift register is sent
+ */
+unsigned int msm_hs_tx_empty(struct uart_port *uport)
+{
+	unsigned int data;
+	unsigned int isr;
+	unsigned int ret = 0;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_resource_vote(msm_uport);
+	data = msm_hs_read(uport, UART_DM_SR);
+	isr = msm_hs_read(uport, UART_DM_ISR);
+	msm_hs_resource_unvote(msm_uport);
+	MSM_HS_INFO("%s(): SR:0x%x ISR:0x%x ", __func__, data, isr);
+
+	if (data & UARTDM_SR_TXEMT_BMSK) {
+		ret = TIOCSER_TEMT;
+	} else
+		/*
+		 * Add an extra sleep here because sometimes the framework's
+		 * delay (based on baud rate) isn't good enough.
+		 * Note that this won't happen during every port close, only
+		 * on select occassions when the userspace does back to back
+		 * write() and close().
+		 */
+		usleep_range(5000, 7000);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_hs_tx_empty);
+
+/*
+ *  Standard API, Stop transmitter.
+ *  Any character in the transmit shift register is sent as
+ *  well as the current data mover transfer .
+ */
+static void msm_hs_stop_tx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+
+	tx->flush = FLUSH_STOP;
+}
+
+static int disconnect_rx_endpoint(struct msm_hs_port *msm_uport)
+{
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	struct sps_pipe *sps_pipe_handle = rx->prod.pipe_handle;
+	int ret = 0;
+
+	ret = sps_rx_disconnect(sps_pipe_handle);
+
+	if (msm_uport->rx.pending_flag)
+		MSM_HS_WARN("%s(): Buffers may be pending 0x%lx",
+		__func__, msm_uport->rx.pending_flag);
+	MSM_HS_DBG("%s(): clearing desc usage flag", __func__);
+	msm_uport->rx.queued_flag = 0;
+	msm_uport->rx.pending_flag = 0;
+	msm_uport->rx.rx_inx = 0;
+
+	if (ret)
+		MSM_HS_ERR("%s(): sps_disconnect failed\n", __func__);
+	msm_uport->rx.flush = FLUSH_SHUTDOWN;
+	MSM_HS_DBG("%s: Calling Completion\n", __func__);
+	wake_up(&msm_uport->bam_disconnect_wait);
+	MSM_HS_DBG("%s: Done Completion\n", __func__);
+	wake_up(&msm_uport->rx.wait);
+	return ret;
+}
+
+static int sps_tx_disconnect(struct msm_hs_port *msm_uport)
+{
+	struct uart_port *uport = &msm_uport->uport;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct sps_pipe *tx_pipe = tx->cons.pipe_handle;
+	unsigned long flags;
+	int ret = 0;
+
+	if (msm_uport->tx.flush == FLUSH_SHUTDOWN) {
+		MSM_HS_DBG("%s(): pipe already disonnected", __func__);
+		return ret;
+	}
+
+	ret = sps_disconnect(tx_pipe);
+
+	if (ret) {
+		MSM_HS_ERR("%s(): sps_disconnect failed %d", __func__, ret);
+		return ret;
+	}
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_uport->tx.flush = FLUSH_SHUTDOWN;
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	MSM_HS_DBG("%s(): TX Disconnect", __func__);
+	return ret;
+}
+
+static void msm_hs_disable_rx(struct uart_port *uport)
+{
+	unsigned int data;
+
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	data &= ~UARTDM_RX_BAM_ENABLE_BMSK;
+	msm_hs_write(uport, UART_DM_DMEN, data);
+}
+
+/*
+ *  Standard API, Stop receiver as soon as possible.
+ *
+ *  Function immediately terminates the operation of the
+ *  channel receiver and any incoming characters are lost. None
+ *  of the receiver status bits are affected by this command and
+ *  characters that are already in the receive FIFO there.
+ */
+static void msm_hs_stop_rx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE)
+		MSM_HS_WARN("%s(): Clocks are off\n", __func__);
+	else
+		msm_hs_disable_rx(uport);
+
+	if (msm_uport->rx.flush == FLUSH_NONE)
+		msm_uport->rx.flush = FLUSH_STOP;
+}
+
+static void msm_hs_disconnect_rx(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_disable_rx(uport);
+	/* Disconnect the BAM RX pipe */
+	if (msm_uport->rx.flush == FLUSH_NONE)
+		msm_uport->rx.flush = FLUSH_STOP;
+	disconnect_rx_endpoint(msm_uport);
+	MSM_HS_DBG("%s(): rx->flush %d", __func__, msm_uport->rx.flush);
+}
+
+/* Tx timeout callback function */
+void tx_timeout_handler(unsigned long arg)
+{
+	struct msm_hs_port *msm_uport = (struct msm_hs_port *) arg;
+	struct uart_port *uport = &msm_uport->uport;
+	int isr;
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s(): clocks are off", __func__);
+		return;
+	}
+
+	isr = msm_hs_read(uport, UART_DM_ISR);
+	if (UARTDM_ISR_CURRENT_CTS_BMSK & isr)
+		MSM_HS_WARN("%s(): CTS Disabled, ISR 0x%x", __func__, isr);
+	dump_uart_hs_registers(msm_uport);
+}
+
+/*  Transmit the next chunk of data */
+static void msm_hs_submit_tx_locked(struct uart_port *uport)
+{
+	int left;
+	int tx_count;
+	int aligned_tx_count;
+	dma_addr_t src_addr;
+	dma_addr_t aligned_src_addr;
+	u32 flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_INT;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct circ_buf *tx_buf = &msm_uport->uport.state->xmit;
+	struct sps_pipe *sps_pipe_handle;
+	int ret;
+
+	if (uart_circ_empty(tx_buf) || uport->state->port.tty->stopped) {
+		tx->dma_in_flight = false;
+		msm_hs_stop_tx_locked(uport);
+		return;
+	}
+
+	tx_count = uart_circ_chars_pending(tx_buf);
+
+	if (tx_count > UARTDM_TX_BUF_SIZE)
+		tx_count = UARTDM_TX_BUF_SIZE;
+
+	left = UART_XMIT_SIZE - tx_buf->tail;
+
+	if (tx_count > left)
+		tx_count = left;
+
+	src_addr = tx->dma_base + tx_buf->tail;
+	/* Mask the src_addr to align on a cache
+	 * and add those bytes to tx_count
+	 */
+	aligned_src_addr = src_addr & ~(dma_get_cache_alignment() - 1);
+	aligned_tx_count = tx_count + src_addr - aligned_src_addr;
+
+	dma_sync_single_for_device(uport->dev, aligned_src_addr,
+			aligned_tx_count, DMA_TO_DEVICE);
+
+	tx->tx_count = tx_count;
+
+	hex_dump_ipc(msm_uport, tx->ipc_tx_ctxt, "Tx",
+			&tx_buf->buf[tx_buf->tail], (u64)src_addr, tx_count);
+	sps_pipe_handle = tx->cons.pipe_handle;
+
+	/* Set 1 second timeout */
+	mod_timer(&tx->tx_timeout_timer,
+		jiffies + msecs_to_jiffies(MSEC_PER_SEC));
+	/* Queue transfer request to SPS */
+	ret = sps_transfer_one(sps_pipe_handle, src_addr, tx_count,
+				msm_uport, flags);
+
+	MSM_HS_DBG("%s:Enqueue Tx Cmd, ret %d\n", __func__, ret);
+}
+
+/* This function queues the rx descriptor for BAM transfer */
+static void msm_hs_post_rx_desc(struct msm_hs_port *msm_uport, int inx)
+{
+	u32 flags = SPS_IOVEC_FLAG_INT;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	int ret;
+
+	phys_addr_t rbuff_addr = rx->rbuffer + (UARTDM_RX_BUF_SIZE * inx);
+	u8 *virt_addr = rx->buffer + (UARTDM_RX_BUF_SIZE * inx);
+
+	MSM_HS_DBG("%s: %d:Queue desc %d, 0x%llx, base 0x%llx virtaddr %p",
+		__func__, msm_uport->uport.line, inx,
+		(u64)rbuff_addr, (u64)rx->rbuffer, virt_addr);
+
+	rx->iovec[inx].size = 0;
+	ret = sps_transfer_one(rx->prod.pipe_handle, rbuff_addr,
+		UARTDM_RX_BUF_SIZE, msm_uport, flags);
+
+	if (ret)
+		MSM_HS_ERR("Error processing descriptor %d", ret);
+}
+
+/* Update the rx descriptor index to specify the next one to be processed */
+static void msm_hs_mark_next(struct msm_hs_port *msm_uport, int inx)
+{
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	int prev;
+
+	inx %= UART_DMA_DESC_NR;
+	MSM_HS_DBG("%s(): inx %d, pending 0x%lx", __func__, inx,
+		rx->pending_flag);
+
+	if (!inx)
+		prev = UART_DMA_DESC_NR - 1;
+	else
+		prev = inx - 1;
+
+	if (!test_bit(prev, &rx->pending_flag))
+		msm_uport->rx.rx_inx = inx;
+	MSM_HS_DBG("%s(): prev %d pending flag 0x%lx, next %d", __func__,
+		prev, rx->pending_flag, msm_uport->rx.rx_inx);
+}
+
+/*
+ *	Queue the rx descriptor that has just been processed or
+ *	all of them if queueing for the first time
+ */
+static void msm_hs_queue_rx_desc(struct msm_hs_port *msm_uport)
+{
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	int i, flag = 0;
+
+	/* At first, queue all, if not, queue only one */
+	if (rx->queued_flag || rx->pending_flag) {
+		if (!test_bit(rx->rx_inx, &rx->queued_flag) &&
+		    !test_bit(rx->rx_inx, &rx->pending_flag)) {
+			msm_hs_post_rx_desc(msm_uport, rx->rx_inx);
+			set_bit(rx->rx_inx, &rx->queued_flag);
+			MSM_HS_DBG("%s(): Set Queued Bit %d",
+				__func__, rx->rx_inx);
+		} else
+			MSM_HS_ERR("%s(): rx_inx pending or queued", __func__);
+		return;
+	}
+
+	for (i = 0; i < UART_DMA_DESC_NR; i++) {
+		if (!test_bit(i, &rx->queued_flag) &&
+		!test_bit(i, &rx->pending_flag)) {
+			MSM_HS_DBG("%s(): Calling post rx %d", __func__, i);
+			msm_hs_post_rx_desc(msm_uport, i);
+			set_bit(i, &rx->queued_flag);
+			flag = 1;
+		}
+	}
+
+	if (!flag)
+		MSM_HS_ERR("%s(): error queueing descriptor", __func__);
+}
+
+/* Start to receive the next chunk of data */
+static void msm_hs_start_rx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	unsigned int buffer_pending = msm_uport->rx.buffer_pending;
+	unsigned int data;
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s(): Clocks are off\n", __func__);
+		return;
+	}
+	if (rx->pending_flag) {
+		MSM_HS_INFO("%s: Rx Cmd got executed, wait for rx_tlet\n",
+								 __func__);
+		rx->flush = FLUSH_IGNORE;
+		return;
+	}
+	if (buffer_pending)
+		MSM_HS_ERR("Error: rx started in buffer state =%x",
+			buffer_pending);
+
+	msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
+	msm_hs_write(uport, UART_DM_DMRX, UARTDM_RX_BUF_SIZE);
+	msm_hs_write(uport, UART_DM_CR, STALE_EVENT_ENABLE);
+	/*
+	 * Enable UARTDM Rx Interface as previously it has been
+	 * disable in set_termios before configuring baud rate.
+	 */
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	/* Enable UARTDM Rx BAM Interface */
+	data |= UARTDM_RX_BAM_ENABLE_BMSK;
+
+	msm_hs_write(uport, UART_DM_DMEN, data);
+	msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
+	/* Calling next DMOV API. Hence mb() here. */
+	mb();
+
+	/*
+	 * RX-transfer will be automatically re-activated
+	 * after last data of previous transfer was read.
+	 */
+	data = (RX_STALE_AUTO_RE_EN | RX_TRANS_AUTO_RE_ACTIVATE |
+				RX_DMRX_CYCLIC_EN);
+	msm_hs_write(uport, UART_DM_RX_TRANS_CTRL, data);
+	/* Issue RX BAM Start IFC command */
+	msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
+	/* Ensure register IO completion */
+	mb();
+
+	msm_uport->rx.flush = FLUSH_NONE;
+	msm_uport->rx_bam_inprogress = true;
+	msm_hs_queue_rx_desc(msm_uport);
+	msm_uport->rx_bam_inprogress = false;
+	wake_up(&msm_uport->rx.wait);
+	MSM_HS_DBG("%s:Enqueue Rx Cmd\n", __func__);
+}
+
+static void flip_insert_work(struct work_struct *work)
+{
+	unsigned long flags;
+	int retval;
+	struct msm_hs_port *msm_uport =
+		container_of(work, struct msm_hs_port,
+			     rx.flip_insert_work.work);
+	struct tty_struct *tty = msm_uport->uport.state->port.tty;
+
+	spin_lock_irqsave(&msm_uport->uport.lock, flags);
+	if (!tty || msm_uport->rx.flush == FLUSH_SHUTDOWN) {
+		dev_err(msm_uport->uport.dev,
+			"%s:Invalid driver state flush %d\n",
+				__func__, msm_uport->rx.flush);
+		MSM_HS_ERR("%s:Invalid driver state flush %d\n",
+				__func__, msm_uport->rx.flush);
+		spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+		return;
+	}
+
+	if (msm_uport->rx.buffer_pending == NONE_PENDING) {
+		MSM_HS_ERR("Error: No buffer pending in %s", __func__);
+		spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+		return;
+	}
+	if (msm_uport->rx.buffer_pending & FIFO_OVERRUN) {
+		retval = tty_insert_flip_char(tty->port, 0, TTY_OVERRUN);
+		if (retval)
+			msm_uport->rx.buffer_pending &= ~FIFO_OVERRUN;
+	}
+	if (msm_uport->rx.buffer_pending & PARITY_ERROR) {
+		retval = tty_insert_flip_char(tty->port, 0, TTY_PARITY);
+		if (retval)
+			msm_uport->rx.buffer_pending &= ~PARITY_ERROR;
+	}
+	if (msm_uport->rx.buffer_pending & CHARS_NORMAL) {
+		int rx_count, rx_offset;
+
+		rx_count = (msm_uport->rx.buffer_pending & 0xFFFF0000) >> 16;
+		rx_offset = (msm_uport->rx.buffer_pending & 0xFFD0) >> 5;
+		retval = tty_insert_flip_string(tty->port,
+			msm_uport->rx.buffer +
+			(msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)
+			+ rx_offset, rx_count);
+		msm_uport->rx.buffer_pending &= (FIFO_OVERRUN |
+						 PARITY_ERROR);
+		if (retval != rx_count)
+			msm_uport->rx.buffer_pending |= CHARS_NORMAL |
+				retval << 8 | (rx_count - retval) << 16;
+	}
+	if (msm_uport->rx.buffer_pending) {
+		schedule_delayed_work(&msm_uport->rx.flip_insert_work,
+				      msecs_to_jiffies(RETRY_TIMEOUT));
+	} else if (msm_uport->rx.flush <= FLUSH_IGNORE) {
+		MSM_HS_WARN("Pending buffers cleared, restarting");
+		clear_bit(msm_uport->rx.rx_inx,
+			&msm_uport->rx.pending_flag);
+		msm_hs_start_rx_locked(&msm_uport->uport);
+		msm_hs_mark_next(msm_uport, msm_uport->rx.rx_inx+1);
+	}
+	spin_unlock_irqrestore(&msm_uport->uport.lock, flags);
+	tty_flip_buffer_push(tty->port);
+}
+
+static void msm_serial_hs_rx_work(struct kthread_work *work)
+{
+	int retval;
+	int rx_count = 0;
+	unsigned long status;
+	unsigned long flags;
+	unsigned int error_f = 0;
+	struct uart_port *uport;
+	struct msm_hs_port *msm_uport;
+	unsigned int flush = FLUSH_DATA_INVALID;
+	struct tty_struct *tty;
+	struct sps_event_notify *notify;
+	struct msm_hs_rx *rx;
+	struct sps_pipe *sps_pipe_handle;
+	struct platform_device *pdev;
+	const struct msm_serial_hs_platform_data *pdata;
+
+	msm_uport = container_of((struct kthread_work *) work,
+				 struct msm_hs_port, rx.kwork);
+	msm_hs_resource_vote(msm_uport);
+	uport = &msm_uport->uport;
+	tty = uport->state->port.tty;
+	notify = &msm_uport->notify;
+	rx = &msm_uport->rx;
+	pdev = to_platform_device(uport->dev);
+	pdata = pdev->dev.platform_data;
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	if (!tty || rx->flush == FLUSH_SHUTDOWN) {
+		dev_err(uport->dev, "%s:Invalid driver state flush %d\n",
+				__func__, rx->flush);
+		MSM_HS_ERR("%s:Invalid driver state flush %d\n",
+				__func__, rx->flush);
+		spin_unlock_irqrestore(&uport->lock, flags);
+		msm_hs_resource_unvote(msm_uport);
+		return;
+	}
+
+	/*
+	 * Process all pending descs or if nothing is
+	 * queued - called from termios
+	 */
+	while (!rx->buffer_pending &&
+		(rx->pending_flag || !rx->queued_flag)) {
+		MSM_HS_DBG("%s(): Loop P 0x%lx Q 0x%lx", __func__,
+			rx->pending_flag, rx->queued_flag);
+
+		status = msm_hs_read(uport, UART_DM_SR);
+
+		MSM_HS_DBG("In %s\n", __func__);
+
+		/* overflow is not connect to data in a FIFO */
+		if (unlikely((status & UARTDM_SR_OVERRUN_BMSK) &&
+			     (uport->read_status_mask & CREAD))) {
+			retval = tty_insert_flip_char(tty->port,
+							0, TTY_OVERRUN);
+			MSM_HS_WARN("%s(): RX Buffer Overrun Detected\n",
+				__func__);
+			if (!retval)
+				msm_uport->rx.buffer_pending |= TTY_OVERRUN;
+			uport->icount.buf_overrun++;
+			error_f = 1;
+		}
+
+		if (!(uport->ignore_status_mask & INPCK))
+			status = status & ~(UARTDM_SR_PAR_FRAME_BMSK);
+
+		if (unlikely(status & UARTDM_SR_PAR_FRAME_BMSK)) {
+			/* Can not tell diff between parity & frame error */
+			MSM_HS_WARN("msm_serial_hs: parity error\n");
+			uport->icount.parity++;
+			error_f = 1;
+			if (!(uport->ignore_status_mask & IGNPAR)) {
+				retval = tty_insert_flip_char(tty->port,
+							0, TTY_PARITY);
+				if (!retval)
+					msm_uport->rx.buffer_pending
+								|= TTY_PARITY;
+			}
+		}
+
+		if (unlikely(status & UARTDM_SR_RX_BREAK_BMSK)) {
+			MSM_HS_DBG("msm_serial_hs: Rx break\n");
+			uport->icount.brk++;
+			error_f = 1;
+			if (!(uport->ignore_status_mask & IGNBRK)) {
+				retval = tty_insert_flip_char(tty->port,
+								0, TTY_BREAK);
+				if (!retval)
+					msm_uport->rx.buffer_pending
+								|= TTY_BREAK;
+			}
+		}
+
+		if (error_f)
+			msm_hs_write(uport, UART_DM_CR,	RESET_ERROR_STATUS);
+		flush = msm_uport->rx.flush;
+		if (flush == FLUSH_IGNORE)
+			if (!msm_uport->rx.buffer_pending) {
+				MSM_HS_DBG("%s: calling start_rx_locked\n",
+					__func__);
+				msm_hs_start_rx_locked(uport);
+			}
+		if (flush >= FLUSH_DATA_INVALID)
+			goto out;
+
+		rx_count = msm_uport->rx.iovec[msm_uport->rx.rx_inx].size;
+		hex_dump_ipc(msm_uport, rx->ipc_rx_ctxt, "Rx",
+			(msm_uport->rx.buffer +
+			(msm_uport->rx.rx_inx * UARTDM_RX_BUF_SIZE)),
+			msm_uport->rx.iovec[msm_uport->rx.rx_inx].addr,
+			rx_count);
+
+		 /*
+		  * We are in a spin locked context, spin lock taken at
+		  * other places where these flags are updated
+		  */
+		if (0 != (uport->read_status_mask & CREAD)) {
+			if (!test_bit(msm_uport->rx.rx_inx,
+				&msm_uport->rx.pending_flag) &&
+			    !test_bit(msm_uport->rx.rx_inx,
+				&msm_uport->rx.queued_flag))
+				MSM_HS_ERR("%s: RX INX not set", __func__);
+			else if (test_bit(msm_uport->rx.rx_inx,
+					&msm_uport->rx.pending_flag) &&
+				!test_bit(msm_uport->rx.rx_inx,
+					&msm_uport->rx.queued_flag)) {
+				MSM_HS_DBG("%s(): Clear Pending Bit %d",
+					__func__, msm_uport->rx.rx_inx);
+
+				retval = tty_insert_flip_string(tty->port,
+					msm_uport->rx.buffer +
+					(msm_uport->rx.rx_inx *
+					UARTDM_RX_BUF_SIZE),
+					rx_count);
+
+				if (retval != rx_count) {
+					MSM_HS_INFO("%s(): ret %d rx_count %d",
+						__func__, retval, rx_count);
+					msm_uport->rx.buffer_pending |=
+					CHARS_NORMAL | retval << 5 |
+					(rx_count - retval) << 16;
+				}
+			} else
+				MSM_HS_ERR("%s: Error in inx %d", __func__,
+					msm_uport->rx.rx_inx);
+		}
+
+		if (!msm_uport->rx.buffer_pending) {
+			msm_uport->rx.flush = FLUSH_NONE;
+			msm_uport->rx_bam_inprogress = true;
+			sps_pipe_handle = rx->prod.pipe_handle;
+			MSM_HS_DBG("Queing bam descriptor\n");
+			/* Queue transfer request to SPS */
+			clear_bit(msm_uport->rx.rx_inx,
+				&msm_uport->rx.pending_flag);
+			msm_hs_queue_rx_desc(msm_uport);
+			msm_hs_mark_next(msm_uport, msm_uport->rx.rx_inx+1);
+			msm_hs_write(uport, UART_DM_CR, START_RX_BAM_IFC);
+			msm_uport->rx_bam_inprogress = false;
+			wake_up(&msm_uport->rx.wait);
+		} else
+			break;
+
+	}
+out:
+	if (msm_uport->rx.buffer_pending) {
+		MSM_HS_WARN("%s: tty buffer exhausted. Stalling\n", __func__);
+		schedule_delayed_work(&msm_uport->rx.flip_insert_work
+				      , msecs_to_jiffies(RETRY_TIMEOUT));
+	}
+	/* tty_flip_buffer_push() might call msm_hs_start(), so unlock */
+	spin_unlock_irqrestore(&uport->lock, flags);
+	if (flush < FLUSH_DATA_INVALID)
+		tty_flip_buffer_push(tty->port);
+	msm_hs_resource_unvote(msm_uport);
+}
+
+static void msm_hs_start_tx_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+
+	/* Bail if transfer in progress */
+	if (tx->flush < FLUSH_STOP || tx->dma_in_flight) {
+		MSM_HS_INFO("%s(): retry, flush %d, dma_in_flight %d\n",
+			__func__, tx->flush, tx->dma_in_flight);
+		return;
+	}
+
+	if (!tx->dma_in_flight) {
+		tx->dma_in_flight = true;
+		kthread_queue_work(&msm_uport->tx.kworker,
+			&msm_uport->tx.kwork);
+	}
+}
+
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_tx_callback(struct sps_event_notify *notify)
+{
+	struct msm_hs_port *msm_uport =
+		(struct msm_hs_port *)
+		((struct sps_event_notify *)notify)->user;
+	phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
+		notify->data.transfer.iovec.addr);
+
+	msm_uport->notify = *notify;
+	MSM_HS_INFO("tx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
+		&addr, notify->data.transfer.iovec.size,
+		notify->data.transfer.iovec.flags);
+
+	del_timer(&msm_uport->tx.tx_timeout_timer);
+	MSM_HS_DBG("%s(): Queue kthread work", __func__);
+	kthread_queue_work(&msm_uport->tx.kworker, &msm_uport->tx.kwork);
+}
+
+static void msm_serial_hs_tx_work(struct kthread_work *work)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport =
+			container_of((struct kthread_work *)work,
+			struct msm_hs_port, tx.kwork);
+	struct uart_port *uport = &msm_uport->uport;
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+
+	/*
+	 * Do the work buffer related work in BAM
+	 * mode that is equivalent to legacy mode
+	 */
+	msm_hs_resource_vote(msm_uport);
+	if (tx->flush >= FLUSH_STOP) {
+		spin_lock_irqsave(&(msm_uport->uport.lock), flags);
+		tx->flush = FLUSH_NONE;
+		MSM_HS_DBG("%s(): calling submit_tx", __func__);
+		msm_hs_submit_tx_locked(uport);
+		spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
+		msm_hs_resource_unvote(msm_uport);
+		return;
+	}
+
+	spin_lock_irqsave(&(msm_uport->uport.lock), flags);
+	if (!uart_circ_empty(tx_buf))
+		tx_buf->tail = (tx_buf->tail +
+		tx->tx_count) & ~UART_XMIT_SIZE;
+	else
+		MSM_HS_DBG("%s:circ buffer is empty\n", __func__);
+
+	wake_up(&msm_uport->tx.wait);
+
+	uport->icount.tx += tx->tx_count;
+
+	/*
+	 * Calling to send next chunk of data
+	 * If the circ buffer is empty, we stop
+	 * If the clock off was requested, the clock
+	 * off sequence is kicked off
+	 */
+	 MSM_HS_DBG("%s(): calling submit_tx", __func__);
+	 msm_hs_submit_tx_locked(uport);
+
+	if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
+		uart_write_wakeup(uport);
+
+	spin_unlock_irqrestore(&(msm_uport->uport.lock), flags);
+	msm_hs_resource_unvote(msm_uport);
+}
+
+static void
+msm_hs_mark_proc_rx_desc(struct msm_hs_port *msm_uport,
+			struct sps_event_notify *notify)
+{
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
+		notify->data.transfer.iovec.addr);
+	/* divide by UARTDM_RX_BUF_SIZE */
+	int inx = (addr - rx->rbuffer) >> 9;
+
+	set_bit(inx, &rx->pending_flag);
+	clear_bit(inx, &rx->queued_flag);
+	rx->iovec[inx] = notify->data.transfer.iovec;
+	MSM_HS_DBG("Clear Q, Set P Bit %d, Q 0x%lx P 0x%lx",
+		inx, rx->queued_flag, rx->pending_flag);
+}
+
+/**
+ * Callback notification from SPS driver
+ *
+ * This callback function gets triggered called from
+ * SPS driver when requested SPS data transfer is
+ * completed.
+ *
+ */
+
+static void msm_hs_sps_rx_callback(struct sps_event_notify *notify)
+{
+
+	struct msm_hs_port *msm_uport =
+		(struct msm_hs_port *)
+		((struct sps_event_notify *)notify)->user;
+	struct uart_port *uport;
+	unsigned long flags;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	phys_addr_t addr = DESC_FULL_ADDR(notify->data.transfer.iovec.flags,
+		notify->data.transfer.iovec.addr);
+	/* divide by UARTDM_RX_BUF_SIZE */
+	int inx = (addr - rx->rbuffer) >> 9;
+
+	uport = &(msm_uport->uport);
+	msm_uport->notify = *notify;
+	MSM_HS_INFO("rx_cb: addr=0x%pa, size=0x%x, flags=0x%x\n",
+		&addr, notify->data.transfer.iovec.size,
+		notify->data.transfer.iovec.flags);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_mark_proc_rx_desc(msm_uport, notify);
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	if (msm_uport->rx.flush == FLUSH_NONE) {
+		/* Test if others are queued */
+		if (msm_uport->rx.pending_flag & ~(1 << inx)) {
+			MSM_HS_DBG("%s(): inx 0x%x, 0x%lx not processed",
+			__func__, inx,
+			msm_uport->rx.pending_flag & ~(1<<inx));
+		}
+		kthread_queue_work(&msm_uport->rx.kworker,
+				&msm_uport->rx.kwork);
+		MSM_HS_DBG("%s(): Scheduled rx_tlet", __func__);
+	}
+}
+
+/*
+ *  Standard API, Current states of modem control inputs
+ *
+ * Since CTS can be handled entirely by HARDWARE we always
+ * indicate clear to send and count on the TX FIFO to block when
+ * it fills up.
+ *
+ * - TIOCM_DCD
+ * - TIOCM_CTS
+ * - TIOCM_DSR
+ * - TIOCM_RI
+ *  (Unsupported) DCD and DSR will return them high. RI will return low.
+ */
+static unsigned int msm_hs_get_mctrl_locked(struct uart_port *uport)
+{
+	return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;
+}
+
+/*
+ *  Standard API, Set or clear RFR_signal
+ *
+ * Set RFR high, (Indicate we are not ready for data), we disable auto
+ * ready for receiving and then set RFR_N high. To set RFR to low we just turn
+ * back auto ready for receiving and it should lower RFR signal
+ * when hardware is ready
+ */
+void msm_hs_set_mctrl_locked(struct uart_port *uport,
+				    unsigned int mctrl)
+{
+	unsigned int set_rts;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s(): Clocks are off\n", __func__);
+		return;
+	}
+	/* RTS is active low */
+	set_rts = TIOCM_RTS & mctrl ? 0 : 1;
+	MSM_HS_INFO("%s: set_rts %d\n", __func__, set_rts);
+
+	if (set_rts)
+		msm_hs_disable_flow_control(uport, false);
+	else
+		msm_hs_enable_flow_control(uport, false);
+}
+
+void msm_hs_set_mctrl(struct uart_port *uport,
+				    unsigned int mctrl)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_resource_vote(msm_uport);
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_set_mctrl_locked(uport, mctrl);
+	spin_unlock_irqrestore(&uport->lock, flags);
+	msm_hs_resource_unvote(msm_uport);
+}
+EXPORT_SYMBOL(msm_hs_set_mctrl);
+
+/* Standard API, Enable modem status (CTS) interrupt  */
+static void msm_hs_enable_ms_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (msm_uport->pm_state != MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s(): Clocks are off\n", __func__);
+		return;
+	}
+
+	/* Enable DELTA_CTS Interrupt */
+	msm_uport->imr_reg |= UARTDM_ISR_DELTA_CTS_BMSK;
+	msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
+	/* Ensure register IO completion */
+	mb();
+
+}
+
+/*
+ *  Standard API, Break Signal
+ *
+ * Control the transmission of a break signal. ctl eq 0 => break
+ * signal terminate ctl ne 0 => start break signal
+ */
+static void msm_hs_break_ctl(struct uart_port *uport, int ctl)
+{
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_resource_vote(msm_uport);
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_hs_write(uport, UART_DM_CR, ctl ? START_BREAK : STOP_BREAK);
+	/* Ensure register IO completion */
+	mb();
+	spin_unlock_irqrestore(&uport->lock, flags);
+	msm_hs_resource_unvote(msm_uport);
+}
+
+static void msm_hs_config_port(struct uart_port *uport, int cfg_flags)
+{
+	if (cfg_flags & UART_CONFIG_TYPE)
+		uport->type = PORT_MSM;
+
+}
+
+/*  Handle CTS changes (Called from interrupt handler) */
+static void msm_hs_handle_delta_cts_locked(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_hs_resource_vote(msm_uport);
+	/* clear interrupt */
+	msm_hs_write(uport, UART_DM_CR, RESET_CTS);
+	/* Calling CLOCK API. Hence mb() requires here. */
+	mb();
+	uport->icount.cts++;
+
+	/* clear the IOCTL TIOCMIWAIT if called */
+	wake_up_interruptible(&uport->state->port.delta_msr_wait);
+	msm_hs_resource_unvote(msm_uport);
+}
+
+static irqreturn_t msm_hs_isr(int irq, void *dev)
+{
+	unsigned long flags;
+	unsigned int isr_status;
+	struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
+	struct uart_port *uport = &msm_uport->uport;
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	isr_status = msm_hs_read(uport, UART_DM_MISR);
+	MSM_HS_INFO("%s: DM_ISR: 0x%x\n", __func__, isr_status);
+	dump_uart_hs_registers(msm_uport);
+
+	/* Uart RX starting */
+	if (isr_status & UARTDM_ISR_RXLEV_BMSK) {
+		MSM_HS_DBG("%s:UARTDM_ISR_RXLEV_BMSK\n", __func__);
+		msm_uport->imr_reg &= ~UARTDM_ISR_RXLEV_BMSK;
+		msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
+		/* Complete device write for IMR. Hence mb() requires. */
+		mb();
+	}
+	/* Stale rx interrupt */
+	if (isr_status & UARTDM_ISR_RXSTALE_BMSK) {
+		msm_hs_write(uport, UART_DM_CR, STALE_EVENT_DISABLE);
+		msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
+		/*
+		 * Complete device write before calling DMOV API. Hence
+		 * mb() requires here.
+		 */
+		mb();
+		MSM_HS_DBG("%s:Stal Interrupt\n", __func__);
+	}
+	/* tx ready interrupt */
+	if (isr_status & UARTDM_ISR_TX_READY_BMSK) {
+		MSM_HS_DBG("%s: ISR_TX_READY Interrupt\n", __func__);
+		/* Clear  TX Ready */
+		msm_hs_write(uport, UART_DM_CR, CLEAR_TX_READY);
+
+		/*
+		 * Complete both writes before starting new TX.
+		 * Hence mb() requires here.
+		 */
+		mb();
+		/* Complete DMA TX transactions and submit new transactions */
+
+		/* Do not update tx_buf.tail if uart_flush_buffer already
+		 * called in serial core
+		 */
+		if (!uart_circ_empty(tx_buf))
+			tx_buf->tail = (tx_buf->tail +
+					tx->tx_count) & ~UART_XMIT_SIZE;
+
+		tx->dma_in_flight = false;
+
+		uport->icount.tx += tx->tx_count;
+
+		if (uart_circ_chars_pending(tx_buf) < WAKEUP_CHARS)
+			uart_write_wakeup(uport);
+	}
+	if (isr_status & UARTDM_ISR_TXLEV_BMSK) {
+		/* TX FIFO is empty */
+		msm_uport->imr_reg &= ~UARTDM_ISR_TXLEV_BMSK;
+		msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
+		MSM_HS_DBG("%s: TXLEV Interrupt\n", __func__);
+		/*
+		 * Complete device write before starting clock_off request.
+		 * Hence mb() requires here.
+		 */
+		mb();
+		queue_work(msm_uport->hsuart_wq, &msm_uport->clock_off_w);
+	}
+
+	/* Change in CTS interrupt */
+	if (isr_status & UARTDM_ISR_DELTA_CTS_BMSK)
+		msm_hs_handle_delta_cts_locked(uport);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+/* The following two functions provide interfaces to get the underlying
+ * port structure (struct uart_port or struct msm_hs_port) given
+ * the port index. msm_hs_get_uart port is called by clients.
+ * The function msm_hs_get_hs_port is for internal use
+ */
+
+struct uart_port *msm_hs_get_uart_port(int port_index)
+{
+	struct uart_state *state = msm_hs_driver.state + port_index;
+
+	/* The uart_driver structure stores the states in an array.
+	 * Thus the corresponding offset from the drv->state returns
+	 * the state for the uart_port that is requested
+	 */
+	if (port_index == state->uart_port->line)
+		return state->uart_port;
+
+	return NULL;
+}
+EXPORT_SYMBOL(msm_hs_get_uart_port);
+
+static struct msm_hs_port *msm_hs_get_hs_port(int port_index)
+{
+	struct uart_port *uport = msm_hs_get_uart_port(port_index);
+
+	if (uport)
+		return UARTDM_TO_MSM(uport);
+	return NULL;
+}
+
+void enable_wakeup_interrupt(struct msm_hs_port *msm_uport)
+{
+	unsigned long flags;
+	struct uart_port *uport = &(msm_uport->uport);
+
+	if (!is_use_low_power_wakeup(msm_uport))
+		return;
+	if (msm_uport->wakeup.freed)
+		return;
+
+	if (!(msm_uport->wakeup.enabled)) {
+		spin_lock_irqsave(&uport->lock, flags);
+		msm_uport->wakeup.ignore = 1;
+		msm_uport->wakeup.enabled = true;
+		spin_unlock_irqrestore(&uport->lock, flags);
+		disable_irq(uport->irq);
+		enable_irq(msm_uport->wakeup.irq);
+	} else {
+		MSM_HS_WARN("%s:Wake up IRQ already enabled", __func__);
+	}
+}
+
+void disable_wakeup_interrupt(struct msm_hs_port *msm_uport)
+{
+	unsigned long flags;
+	struct uart_port *uport = &(msm_uport->uport);
+
+	if (!is_use_low_power_wakeup(msm_uport))
+		return;
+	if (msm_uport->wakeup.freed)
+		return;
+
+	if (msm_uport->wakeup.enabled) {
+		disable_irq_nosync(msm_uport->wakeup.irq);
+		enable_irq(uport->irq);
+		spin_lock_irqsave(&uport->lock, flags);
+		msm_uport->wakeup.enabled = false;
+		spin_unlock_irqrestore(&uport->lock, flags);
+	} else {
+		MSM_HS_WARN("%s:Wake up IRQ already disabled", __func__);
+	}
+}
+
+void msm_hs_resource_off(struct msm_hs_port *msm_uport)
+{
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned int data;
+
+	MSM_HS_DBG("%s(): begin", __func__);
+	msm_hs_disable_flow_control(uport, false);
+	if (msm_uport->rx.flush == FLUSH_NONE)
+		msm_hs_disconnect_rx(uport);
+
+	/* disable dlink */
+	if (msm_uport->tx.flush == FLUSH_NONE)
+		wait_event_timeout(msm_uport->tx.wait,
+			msm_uport->tx.flush == FLUSH_STOP, 500);
+
+	if (msm_uport->tx.flush != FLUSH_SHUTDOWN) {
+		data = msm_hs_read(uport, UART_DM_DMEN);
+		data &= ~UARTDM_TX_BAM_ENABLE_BMSK;
+		msm_hs_write(uport, UART_DM_DMEN, data);
+		sps_tx_disconnect(msm_uport);
+	}
+	if (!atomic_read(&msm_uport->client_req_state))
+		msm_hs_enable_flow_control(uport, false);
+}
+
+void msm_hs_resource_on(struct msm_hs_port *msm_uport)
+{
+	struct uart_port *uport = &(msm_uport->uport);
+	unsigned int data;
+	unsigned long flags;
+
+	if (msm_uport->rx.flush == FLUSH_SHUTDOWN ||
+	msm_uport->rx.flush == FLUSH_STOP) {
+		msm_hs_write(uport, UART_DM_CR, RESET_RX);
+		data = msm_hs_read(uport, UART_DM_DMEN);
+		data |= UARTDM_RX_BAM_ENABLE_BMSK;
+		msm_hs_write(uport, UART_DM_DMEN, data);
+	}
+
+	msm_hs_spsconnect_tx(msm_uport);
+	if (msm_uport->rx.flush == FLUSH_SHUTDOWN) {
+		msm_hs_spsconnect_rx(uport);
+		spin_lock_irqsave(&uport->lock, flags);
+		msm_hs_start_rx_locked(uport);
+		spin_unlock_irqrestore(&uport->lock, flags);
+	}
+}
+
+/* Request to turn off uart clock once pending TX is flushed */
+int msm_hs_request_clock_off(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	int ret = 0;
+	int client_count = 0;
+
+	mutex_lock(&msm_uport->mtx);
+	/*
+	 * If we're in the middle of a system suspend, don't process these
+	 * userspace/kernel API commands.
+	 */
+	if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
+		MSM_HS_WARN("%s:Can't process clk request during suspend",
+			__func__);
+		ret = -EIO;
+	}
+	mutex_unlock(&msm_uport->mtx);
+	if (ret)
+		goto exit_request_clock_off;
+
+	if (atomic_read(&msm_uport->client_count) <= 0) {
+		MSM_HS_WARN("%s(): ioctl count -ve, client check voting",
+			__func__);
+		ret = -EPERM;
+		goto exit_request_clock_off;
+	}
+	/* Set the flag to disable flow control and wakeup irq */
+	if (msm_uport->obs)
+		atomic_set(&msm_uport->client_req_state, 1);
+	msm_hs_resource_unvote(msm_uport);
+	atomic_dec(&msm_uport->client_count);
+	client_count = atomic_read(&msm_uport->client_count);
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+			"%s: Client_Count %d\n", __func__,
+			client_count);
+exit_request_clock_off:
+	return ret;
+}
+EXPORT_SYMBOL(msm_hs_request_clock_off);
+
+int msm_hs_request_clock_on(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	int client_count;
+	int ret = 0;
+
+	mutex_lock(&msm_uport->mtx);
+	/*
+	 * If we're in the middle of a system suspend, don't process these
+	 * userspace/kernel API commands.
+	 */
+	if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED) {
+		MSM_HS_WARN("%s:Can't process clk request during suspend",
+			__func__);
+		ret = -EIO;
+	}
+	mutex_unlock(&msm_uport->mtx);
+	if (ret)
+		goto exit_request_clock_on;
+
+	msm_hs_resource_vote(UARTDM_TO_MSM(uport));
+	atomic_inc(&msm_uport->client_count);
+	client_count = atomic_read(&msm_uport->client_count);
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+			"%s: Client_Count %d\n", __func__,
+			client_count);
+
+	/* Clear the flag */
+	if (msm_uport->obs)
+		atomic_set(&msm_uport->client_req_state, 0);
+exit_request_clock_on:
+	return ret;
+}
+EXPORT_SYMBOL(msm_hs_request_clock_on);
+
+static irqreturn_t msm_hs_wakeup_isr(int irq, void *dev)
+{
+	unsigned int wakeup = 0;
+	unsigned long flags;
+	struct msm_hs_port *msm_uport = (struct msm_hs_port *)dev;
+	struct uart_port *uport = &msm_uport->uport;
+	struct tty_struct *tty = NULL;
+
+	spin_lock_irqsave(&uport->lock, flags);
+
+	if (msm_uport->wakeup.ignore)
+		msm_uport->wakeup.ignore = 0;
+	else
+		wakeup = 1;
+
+	if (wakeup) {
+		/*
+		 * Port was clocked off during rx, wake up and
+		 * optionally inject char into tty rx
+		 */
+		if (msm_uport->wakeup.inject_rx) {
+			tty = uport->state->port.tty;
+			tty_insert_flip_char(tty->port,
+					     msm_uport->wakeup.rx_to_inject,
+					     TTY_NORMAL);
+			hex_dump_ipc(msm_uport, msm_uport->rx.ipc_rx_ctxt,
+				"Rx Inject",
+				&msm_uport->wakeup.rx_to_inject, 0, 1);
+			MSM_HS_INFO("Wakeup ISR.Ignore%d\n",
+						msm_uport->wakeup.ignore);
+		}
+	}
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	if (wakeup && msm_uport->wakeup.inject_rx)
+		tty_flip_buffer_push(tty->port);
+	return IRQ_HANDLED;
+}
+
+static const char *msm_hs_type(struct uart_port *port)
+{
+	return "MSM HS UART";
+}
+
+/**
+ * msm_hs_unconfig_uart_gpios: Unconfigures UART GPIOs
+ * @uport: uart port
+ */
+static void msm_hs_unconfig_uart_gpios(struct uart_port *uport)
+{
+	struct platform_device *pdev = to_platform_device(uport->dev);
+	const struct msm_serial_hs_platform_data *pdata =
+					pdev->dev.platform_data;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	int ret;
+
+	if (msm_uport->use_pinctrl) {
+		ret = pinctrl_select_state(msm_uport->pinctrl,
+				msm_uport->gpio_state_suspend);
+		if (ret)
+			MSM_HS_ERR("%s():Failed to pinctrl set_state",
+				__func__);
+	} else if (pdata) {
+		if (gpio_is_valid(pdata->uart_tx_gpio))
+			gpio_free(pdata->uart_tx_gpio);
+		if (gpio_is_valid(pdata->uart_rx_gpio))
+			gpio_free(pdata->uart_rx_gpio);
+		if (gpio_is_valid(pdata->uart_cts_gpio))
+			gpio_free(pdata->uart_cts_gpio);
+		if (gpio_is_valid(pdata->uart_rfr_gpio))
+			gpio_free(pdata->uart_rfr_gpio);
+	} else
+		MSM_HS_ERR("Error:Pdata is NULL.\n");
+}
+
+/**
+ * msm_hs_config_uart_gpios - Configures UART GPIOs
+ * @uport: uart port
+ */
+static int msm_hs_config_uart_gpios(struct uart_port *uport)
+{
+	struct platform_device *pdev = to_platform_device(uport->dev);
+	const struct msm_serial_hs_platform_data *pdata =
+					pdev->dev.platform_data;
+	int ret = 0;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	if (!IS_ERR_OR_NULL(msm_uport->pinctrl)) {
+		MSM_HS_DBG("%s(): Using Pinctrl", __func__);
+		msm_uport->use_pinctrl = true;
+		ret = pinctrl_select_state(msm_uport->pinctrl,
+				msm_uport->gpio_state_active);
+		if (ret)
+			MSM_HS_ERR("%s(): Failed to pinctrl set_state",
+				__func__);
+		return ret;
+	} else if (pdata) {
+		/* Fall back to using gpio lib */
+		if (gpio_is_valid(pdata->uart_tx_gpio)) {
+			ret = gpio_request(pdata->uart_tx_gpio,
+							"UART_TX_GPIO");
+			if (unlikely(ret)) {
+				MSM_HS_ERR("gpio request failed for:%d\n",
+					pdata->uart_tx_gpio);
+				goto exit_uart_config;
+			}
+		}
+
+		if (gpio_is_valid(pdata->uart_rx_gpio)) {
+			ret = gpio_request(pdata->uart_rx_gpio,
+							"UART_RX_GPIO");
+			if (unlikely(ret)) {
+				MSM_HS_ERR("gpio request failed for:%d\n",
+					pdata->uart_rx_gpio);
+				goto uart_tx_unconfig;
+			}
+		}
+
+		if (gpio_is_valid(pdata->uart_cts_gpio)) {
+			ret = gpio_request(pdata->uart_cts_gpio,
+							"UART_CTS_GPIO");
+			if (unlikely(ret)) {
+				MSM_HS_ERR("gpio request failed for:%d\n",
+					pdata->uart_cts_gpio);
+				goto uart_rx_unconfig;
+			}
+		}
+
+		if (gpio_is_valid(pdata->uart_rfr_gpio)) {
+			ret = gpio_request(pdata->uart_rfr_gpio,
+							"UART_RFR_GPIO");
+			if (unlikely(ret)) {
+				MSM_HS_ERR("gpio request failed for:%d\n",
+					pdata->uart_rfr_gpio);
+				goto uart_cts_unconfig;
+			}
+		}
+	} else {
+		MSM_HS_ERR("Pdata is NULL.\n");
+		ret = -EINVAL;
+	}
+	return ret;
+
+uart_cts_unconfig:
+	if (gpio_is_valid(pdata->uart_cts_gpio))
+		gpio_free(pdata->uart_cts_gpio);
+uart_rx_unconfig:
+	if (gpio_is_valid(pdata->uart_rx_gpio))
+		gpio_free(pdata->uart_rx_gpio);
+uart_tx_unconfig:
+	if (gpio_is_valid(pdata->uart_tx_gpio))
+		gpio_free(pdata->uart_tx_gpio);
+exit_uart_config:
+	return ret;
+}
+
+
+static void msm_hs_get_pinctrl_configs(struct uart_port *uport)
+{
+	struct pinctrl_state *set_state;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	msm_uport->pinctrl = devm_pinctrl_get(uport->dev);
+	if (IS_ERR_OR_NULL(msm_uport->pinctrl)) {
+		MSM_HS_DBG("%s(): Pinctrl not defined", __func__);
+	} else {
+		MSM_HS_DBG("%s(): Using Pinctrl", __func__);
+		msm_uport->use_pinctrl = true;
+
+		set_state = pinctrl_lookup_state(msm_uport->pinctrl,
+						PINCTRL_STATE_DEFAULT);
+		if (IS_ERR_OR_NULL(set_state)) {
+			dev_err(uport->dev,
+				"pinctrl lookup failed for default state");
+			goto pinctrl_fail;
+		}
+
+		MSM_HS_DBG("%s(): Pinctrl state active %p\n", __func__,
+			set_state);
+		msm_uport->gpio_state_active = set_state;
+
+		set_state = pinctrl_lookup_state(msm_uport->pinctrl,
+						PINCTRL_STATE_SLEEP);
+		if (IS_ERR_OR_NULL(set_state)) {
+			dev_err(uport->dev,
+				"pinctrl lookup failed for sleep state");
+			goto pinctrl_fail;
+		}
+
+		MSM_HS_DBG("%s(): Pinctrl state sleep %p\n", __func__,
+			set_state);
+		msm_uport->gpio_state_suspend = set_state;
+		return;
+	}
+pinctrl_fail:
+	msm_uport->pinctrl = NULL;
+}
+
+/* Called when port is opened */
+static int msm_hs_startup(struct uart_port *uport)
+{
+	int ret;
+	int rfr_level;
+	unsigned long flags;
+	unsigned int data;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+	struct sps_pipe *sps_pipe_handle_tx = tx->cons.pipe_handle;
+	struct sps_pipe *sps_pipe_handle_rx = rx->prod.pipe_handle;
+
+	rfr_level = uport->fifosize;
+	if (rfr_level > 16)
+		rfr_level -= 16;
+
+	tx->dma_base = dma_map_single(uport->dev, tx_buf->buf, UART_XMIT_SIZE,
+				      DMA_TO_DEVICE);
+
+	/* turn on uart clk */
+	msm_hs_resource_vote(msm_uport);
+
+	if (is_use_low_power_wakeup(msm_uport)) {
+		ret = request_threaded_irq(msm_uport->wakeup.irq, NULL,
+					msm_hs_wakeup_isr,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					"msm_hs_wakeup", msm_uport);
+		if (unlikely(ret)) {
+			MSM_HS_ERR("%s():Err getting uart wakeup_irq %d\n",
+				  __func__, ret);
+			goto unvote_exit;
+		}
+
+		msm_uport->wakeup.freed = false;
+		disable_irq(msm_uport->wakeup.irq);
+		msm_uport->wakeup.enabled = false;
+
+		ret = irq_set_irq_wake(msm_uport->wakeup.irq, 1);
+		if (unlikely(ret)) {
+			MSM_HS_ERR("%s():Err setting wakeup irq\n", __func__);
+			goto free_uart_irq;
+		}
+	}
+
+	ret = msm_hs_config_uart_gpios(uport);
+	if (ret) {
+		MSM_HS_ERR("Uart GPIO request failed\n");
+		goto free_uart_irq;
+	}
+
+	msm_hs_write(uport, UART_DM_DMEN, 0);
+
+	/* Connect TX */
+	sps_tx_disconnect(msm_uport);
+	ret = msm_hs_spsconnect_tx(msm_uport);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: SPS connect failed for TX");
+		goto unconfig_uart_gpios;
+	}
+
+	/* Connect RX */
+	kthread_flush_worker(&msm_uport->rx.kworker);
+	if (rx->flush != FLUSH_SHUTDOWN)
+		disconnect_rx_endpoint(msm_uport);
+	ret = msm_hs_spsconnect_rx(uport);
+	if (ret) {
+		MSM_HS_ERR("msm_serial_hs: SPS connect failed for RX");
+		goto sps_disconnect_tx;
+	}
+
+	data = (UARTDM_BCR_TX_BREAK_DISABLE | UARTDM_BCR_STALE_IRQ_EMPTY |
+		UARTDM_BCR_RX_DMRX_LOW_EN | UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL |
+		UARTDM_BCR_RX_DMRX_1BYTE_RES_EN);
+	msm_hs_write(uport, UART_DM_BCR, data);
+
+	/* Set auto RFR Level */
+	data = msm_hs_read(uport, UART_DM_MR1);
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK;
+	data &= ~UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK;
+	data |= (UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK & (rfr_level << 2));
+	data |= (UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK & rfr_level);
+	msm_hs_write(uport, UART_DM_MR1, data);
+
+	/* Make sure RXSTALE count is non-zero */
+	data = msm_hs_read(uport, UART_DM_IPR);
+	if (!data) {
+		data |= 0x1f & UARTDM_IPR_STALE_LSB_BMSK;
+		msm_hs_write(uport, UART_DM_IPR, data);
+	}
+
+	/* Assume no flow control, unless termios sets it */
+	msm_uport->flow_control = false;
+	msm_hs_disable_flow_control(uport, true);
+
+
+	/* Reset TX */
+	msm_hs_write(uport, UART_DM_CR, RESET_TX);
+	msm_hs_write(uport, UART_DM_CR, RESET_RX);
+	msm_hs_write(uport, UART_DM_CR, RESET_ERROR_STATUS);
+	msm_hs_write(uport, UART_DM_CR, RESET_BREAK_INT);
+	msm_hs_write(uport, UART_DM_CR, RESET_STALE_INT);
+	msm_hs_write(uport, UART_DM_CR, RESET_CTS);
+	msm_hs_write(uport, UART_DM_CR, RFR_LOW);
+	/* Turn on Uart Receiver */
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_EN_BMSK);
+
+	/* Turn on Uart Transmitter */
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_EN_BMSK);
+
+	tx->dma_in_flight = false;
+	MSM_HS_DBG("%s():desc usage flag 0x%lx", __func__, rx->queued_flag);
+	setup_timer(&(tx->tx_timeout_timer),
+			tx_timeout_handler,
+			(unsigned long) msm_uport);
+
+	/* Enable reading the current CTS, no harm even if CTS is ignored */
+	msm_uport->imr_reg |= UARTDM_ISR_CURRENT_CTS_BMSK;
+
+	/* TXLEV on empty TX fifo */
+	msm_hs_write(uport, UART_DM_TFWR, 4);
+	/*
+	 * Complete all device write related configuration before
+	 * queuing RX request. Hence mb() requires here.
+	 */
+	mb();
+
+	ret = request_irq(uport->irq, msm_hs_isr, IRQF_TRIGGER_HIGH,
+			  "msm_hs_uart", msm_uport);
+	if (unlikely(ret)) {
+		MSM_HS_ERR("%s():Error %d getting uart irq\n", __func__, ret);
+		goto sps_disconnect_rx;
+	}
+
+
+	spin_lock_irqsave(&uport->lock, flags);
+	atomic_set(&msm_uport->client_count, 0);
+	atomic_set(&msm_uport->client_req_state, 0);
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+			"%s: Client_Count 0\n", __func__);
+	msm_hs_start_rx_locked(uport);
+
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	msm_hs_resource_unvote(msm_uport);
+	return 0;
+
+sps_disconnect_rx:
+	sps_disconnect(sps_pipe_handle_rx);
+sps_disconnect_tx:
+	sps_disconnect(sps_pipe_handle_tx);
+unconfig_uart_gpios:
+	msm_hs_unconfig_uart_gpios(uport);
+free_uart_irq:
+	free_irq(uport->irq, msm_uport);
+unvote_exit:
+	msm_hs_resource_unvote(msm_uport);
+	MSM_HS_ERR("%s(): Error return\n", __func__);
+	return ret;
+}
+
+/* Initialize tx and rx data structures */
+static int uartdm_init_port(struct uart_port *uport)
+{
+	int ret = 0;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct msm_hs_tx *tx = &msm_uport->tx;
+	struct msm_hs_rx *rx = &msm_uport->rx;
+
+	init_waitqueue_head(&rx->wait);
+	init_waitqueue_head(&tx->wait);
+	init_waitqueue_head(&msm_uport->bam_disconnect_wait);
+
+	/* Init kernel threads for tx and rx */
+
+	kthread_init_worker(&rx->kworker);
+	rx->task = kthread_run(kthread_worker_fn,
+			&rx->kworker, "msm_serial_hs_%d_rx_work", uport->line);
+	if (IS_ERR(rx->task)) {
+		MSM_HS_ERR("%s(): error creating task", __func__);
+		goto exit_lh_init;
+	}
+	kthread_init_work(&rx->kwork, msm_serial_hs_rx_work);
+
+	kthread_init_worker(&tx->kworker);
+	tx->task = kthread_run(kthread_worker_fn,
+			&tx->kworker, "msm_serial_hs_%d_tx_work", uport->line);
+	if (IS_ERR(rx->task)) {
+		MSM_HS_ERR("%s(): error creating task", __func__);
+		goto exit_lh_init;
+	}
+
+	kthread_init_work(&tx->kwork, msm_serial_hs_tx_work);
+
+	rx->buffer = dma_alloc_coherent(uport->dev,
+				UART_DMA_DESC_NR * UARTDM_RX_BUF_SIZE,
+				 &rx->rbuffer, GFP_KERNEL);
+	if (!rx->buffer) {
+		MSM_HS_ERR("%s(): cannot allocate rx->buffer", __func__);
+		ret = -ENOMEM;
+		goto exit_lh_init;
+	}
+
+	/* Set up Uart Receive */
+	msm_hs_write(uport, UART_DM_RFWR, 32);
+	/* Write to BADR explicitly to set up FIFO sizes */
+	msm_hs_write(uport, UARTDM_BADR_ADDR, 64);
+
+	INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
+
+	return ret;
+exit_lh_init:
+	kthread_stop(rx->task);
+	rx->task = NULL;
+	kthread_stop(tx->task);
+	tx->task = NULL;
+	return ret;
+}
+
+struct msm_serial_hs_platform_data
+	*msm_hs_dt_to_pdata(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct msm_serial_hs_platform_data *pdata;
+	u32 rx_to_inject;
+	int ret;
+
+	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdev->id = of_alias_get_id(pdev->dev.of_node, "uart");
+	/* UART TX GPIO */
+	pdata->uart_tx_gpio = of_get_named_gpio(node,
+					"qcom,tx-gpio", 0);
+	if (pdata->uart_tx_gpio < 0)
+		pr_err("uart_tx_gpio is not available\n");
+
+	/* UART RX GPIO */
+	pdata->uart_rx_gpio = of_get_named_gpio(node,
+					"qcom,rx-gpio", 0);
+	if (pdata->uart_rx_gpio < 0)
+		pr_err("uart_rx_gpio is not available\n");
+
+	/* UART CTS GPIO */
+	pdata->uart_cts_gpio = of_get_named_gpio(node,
+					"qcom,cts-gpio", 0);
+	if (pdata->uart_cts_gpio < 0)
+		pr_err("uart_cts_gpio is not available\n");
+
+	/* UART RFR GPIO */
+	pdata->uart_rfr_gpio = of_get_named_gpio(node,
+					"qcom,rfr-gpio", 0);
+	if (pdata->uart_rfr_gpio < 0)
+		pr_err("uart_rfr_gpio is not available\n");
+
+	pdata->no_suspend_delay = of_property_read_bool(node,
+				"qcom,no-suspend-delay");
+
+	pdata->obs = of_property_read_bool(node,
+				"qcom,msm-obs");
+	if (pdata->obs)
+		pr_err("%s:Out of Band sleep flag is set\n", __func__);
+
+	pdata->inject_rx_on_wakeup = of_property_read_bool(node,
+				"qcom,inject-rx-on-wakeup");
+
+	if (pdata->inject_rx_on_wakeup) {
+		ret = of_property_read_u32(node, "qcom,rx-char-to-inject",
+						&rx_to_inject);
+		if (ret < 0) {
+			pr_err("Error: Rx_char_to_inject not specified.\n");
+			return ERR_PTR(ret);
+		}
+		pdata->rx_to_inject = (u8)rx_to_inject;
+	}
+
+	ret = of_property_read_u32(node, "qcom,bam-tx-ep-pipe-index",
+				&pdata->bam_tx_ep_pipe_index);
+	if (ret < 0) {
+		pr_err("Error: Getting UART BAM TX EP Pipe Index.\n");
+		return ERR_PTR(ret);
+	}
+
+	if (!(pdata->bam_tx_ep_pipe_index >= BAM_PIPE_MIN &&
+		pdata->bam_tx_ep_pipe_index <= BAM_PIPE_MAX)) {
+		pr_err("Error: Invalid UART BAM TX EP Pipe Index.\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ret = of_property_read_u32(node, "qcom,bam-rx-ep-pipe-index",
+					&pdata->bam_rx_ep_pipe_index);
+	if (ret < 0) {
+		pr_err("Error: Getting UART BAM RX EP Pipe Index.\n");
+		return ERR_PTR(ret);
+	}
+
+	if (!(pdata->bam_rx_ep_pipe_index >= BAM_PIPE_MIN &&
+		pdata->bam_rx_ep_pipe_index <= BAM_PIPE_MAX)) {
+		pr_err("Error: Invalid UART BAM RX EP Pipe Index.\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	pr_debug("tx_ep_pipe_index:%d rx_ep_pipe_index:%d\n"
+		"tx_gpio:%d rx_gpio:%d rfr_gpio:%d cts_gpio:%d",
+		pdata->bam_tx_ep_pipe_index, pdata->bam_rx_ep_pipe_index,
+		pdata->uart_tx_gpio, pdata->uart_rx_gpio, pdata->uart_cts_gpio,
+		pdata->uart_rfr_gpio);
+
+	return pdata;
+}
+
+
+/**
+ * Deallocate UART peripheral's SPS endpoint
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ */
+
+static void msm_hs_exit_ep_conn(struct msm_hs_port *msm_uport,
+				struct msm_hs_sps_ep_conn_data *ep)
+{
+	struct sps_pipe *sps_pipe_handle = ep->pipe_handle;
+	struct sps_connect *sps_config = &ep->config;
+
+	dma_free_coherent(msm_uport->uport.dev,
+			sps_config->desc.size,
+			&sps_config->desc.phys_base,
+			GFP_KERNEL);
+	sps_free_endpoint(sps_pipe_handle);
+}
+
+
+/**
+ * Allocate UART peripheral's SPS endpoint
+ *
+ * This function allocates endpoint context
+ * by calling appropriate SPS driver APIs.
+ *
+ * @msm_uport - Pointer to msm_hs_port structure
+ * @ep - Pointer to sps endpoint data structure
+ * @is_produce - 1 means Producer endpoint
+ *             - 0 means Consumer endpoint
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init_ep_conn(struct msm_hs_port *msm_uport,
+				struct msm_hs_sps_ep_conn_data *ep,
+				bool is_producer)
+{
+	int rc = 0;
+	struct sps_pipe *sps_pipe_handle;
+	struct sps_connect *sps_config = &ep->config;
+	struct sps_register_event *sps_event = &ep->event;
+
+	/* Allocate endpoint context */
+	sps_pipe_handle = sps_alloc_endpoint();
+	if (!sps_pipe_handle) {
+		MSM_HS_ERR("%s(): sps_alloc_endpoint() failed!!\n"
+			"is_producer=%d", __func__, is_producer);
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	/* Get default connection configuration for an endpoint */
+	rc = sps_get_config(sps_pipe_handle, sps_config);
+	if (rc) {
+		MSM_HS_ERR("%s(): failed! pipe_handle=0x%p rc=%d",
+			__func__, sps_pipe_handle, rc);
+		goto get_config_err;
+	}
+
+	/* Modify the default connection configuration */
+	if (is_producer) {
+		/* For UART producer transfer, source is UART peripheral
+		 * where as destination is system memory
+		 */
+		sps_config->source = msm_uport->bam_handle;
+		sps_config->destination = SPS_DEV_HANDLE_MEM;
+		sps_config->mode = SPS_MODE_SRC;
+		sps_config->src_pipe_index = msm_uport->bam_rx_ep_pipe_index;
+		sps_config->dest_pipe_index = 0;
+		sps_event->callback = msm_hs_sps_rx_callback;
+	} else {
+		/* For UART consumer transfer, source is system memory
+		 * where as destination is UART peripheral
+		 */
+		sps_config->source = SPS_DEV_HANDLE_MEM;
+		sps_config->destination = msm_uport->bam_handle;
+		sps_config->mode = SPS_MODE_DEST;
+		sps_config->src_pipe_index = 0;
+		sps_config->dest_pipe_index = msm_uport->bam_tx_ep_pipe_index;
+		sps_event->callback = msm_hs_sps_tx_callback;
+	}
+
+	sps_config->options = SPS_O_EOT | SPS_O_DESC_DONE | SPS_O_AUTO_ENABLE;
+	sps_config->event_thresh = 0x10;
+
+	/* Allocate maximum descriptor fifo size */
+	sps_config->desc.size =
+		(1 + UART_DMA_DESC_NR) * sizeof(struct sps_iovec);
+	sps_config->desc.base = dma_alloc_coherent(msm_uport->uport.dev,
+						sps_config->desc.size,
+						&sps_config->desc.phys_base,
+						GFP_KERNEL);
+	if (!sps_config->desc.base) {
+		rc = -ENOMEM;
+		MSM_HS_ERR("msm_serial_hs: dma_alloc_coherent() failed!!\n");
+		goto get_config_err;
+	}
+	memset(sps_config->desc.base, 0x00, sps_config->desc.size);
+
+	sps_event->mode = SPS_TRIGGER_CALLBACK;
+
+	sps_event->options = SPS_O_DESC_DONE | SPS_O_EOT;
+	sps_event->user = (void *)msm_uport;
+
+	/* Now save the sps pipe handle */
+	ep->pipe_handle = sps_pipe_handle;
+	MSM_HS_DBG("msm_serial_hs: success !! %s: pipe_handle=0x%p\n"
+		"desc_fifo.phys_base=0x%pa\n",
+		is_producer ? "READ" : "WRITE",
+		sps_pipe_handle, &sps_config->desc.phys_base);
+	return 0;
+
+get_config_err:
+	sps_free_endpoint(sps_pipe_handle);
+out:
+	return rc;
+}
+
+/**
+ * Initialize SPS HW connected with UART core
+ *
+ * This function register BAM HW resources with
+ * SPS driver and then initialize 2 SPS endpoints
+ *
+ * msm_uport - Pointer to msm_hs_port structure
+ *
+ * @return - 0 if successful else negative value
+ */
+
+static int msm_hs_sps_init(struct msm_hs_port *msm_uport)
+{
+	int rc = 0;
+	struct sps_bam_props bam = {0};
+	unsigned long bam_handle;
+
+	rc = sps_phy2h(msm_uport->bam_mem, &bam_handle);
+	if (rc || !bam_handle) {
+		bam.phys_addr = msm_uport->bam_mem;
+		bam.virt_addr = msm_uport->bam_base;
+		/*
+		 * This event thresold value is only significant for BAM-to-BAM
+		 * transfer. It's ignored for BAM-to-System mode transfer.
+		 */
+		bam.event_threshold = 0x10;	/* Pipe event threshold */
+		bam.summing_threshold = 1;	/* BAM event threshold */
+
+		/* SPS driver wll handle the UART BAM IRQ */
+		bam.irq = (u32)msm_uport->bam_irq;
+		bam.manage = SPS_BAM_MGR_DEVICE_REMOTE;
+
+		MSM_HS_DBG("msm_serial_hs: bam physical base=0x%pa\n",
+							&bam.phys_addr);
+		MSM_HS_DBG("msm_serial_hs: bam virtual base=0x%p\n",
+							bam.virt_addr);
+
+		/* Register UART Peripheral BAM device to SPS driver */
+		rc = sps_register_bam_device(&bam, &bam_handle);
+		if (rc) {
+			MSM_HS_ERR("%s: BAM device register failed\n",
+				  __func__);
+			return rc;
+		}
+		MSM_HS_DBG("%s:BAM device registered. bam_handle=0x%lx",
+			   __func__, msm_uport->bam_handle);
+	}
+	msm_uport->bam_handle = bam_handle;
+
+	rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->rx.prod,
+				UART_SPS_PROD_PERIPHERAL);
+	if (rc) {
+		MSM_HS_ERR("%s: Failed to Init Producer BAM-pipe", __func__);
+		goto deregister_bam;
+	}
+
+	rc = msm_hs_sps_init_ep_conn(msm_uport, &msm_uport->tx.cons,
+				UART_SPS_CONS_PERIPHERAL);
+	if (rc) {
+		MSM_HS_ERR("%s: Failed to Init Consumer BAM-pipe", __func__);
+		goto deinit_ep_conn_prod;
+	}
+	return 0;
+
+deinit_ep_conn_prod:
+	msm_hs_exit_ep_conn(msm_uport, &msm_uport->rx.prod);
+deregister_bam:
+	sps_deregister_bam_device(msm_uport->bam_handle);
+	return rc;
+}
+
+
+static bool deviceid[UARTDM_NR] = {0};
+/*
+ * The mutex synchronizes grabbing next free device number
+ * both in case of an alias being used or not. When alias is
+ * used, the msm_hs_dt_to_pdata gets it and the boolean array
+ * is accordingly updated with device_id_set_used. If no alias
+ * is used, then device_id_grab_next_free sets that array.
+ */
+static DEFINE_MUTEX(mutex_next_device_id);
+
+static int device_id_grab_next_free(void)
+{
+	int i;
+	int ret = -ENODEV;
+
+	mutex_lock(&mutex_next_device_id);
+	for (i = 0; i < UARTDM_NR; i++)
+		if (!deviceid[i]) {
+			ret = i;
+			deviceid[i] = true;
+			break;
+		}
+	mutex_unlock(&mutex_next_device_id);
+	return ret;
+}
+
+static int device_id_set_used(int index)
+{
+	int ret = 0;
+
+	mutex_lock(&mutex_next_device_id);
+	if (deviceid[index])
+		ret = -ENODEV;
+	else
+		deviceid[index] = true;
+	mutex_unlock(&mutex_next_device_id);
+	return ret;
+}
+
+static void obs_manage_irq(struct msm_hs_port *msm_uport, bool en)
+{
+	struct uart_port *uport = &(msm_uport->uport);
+
+	if (msm_uport->obs) {
+		if (en)
+			enable_irq(uport->irq);
+		else
+			disable_irq(uport->irq);
+	}
+}
+
+static void msm_hs_pm_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+	int ret;
+	int client_count = 0;
+
+	if (!msm_uport)
+		goto err_suspend;
+	mutex_lock(&msm_uport->mtx);
+
+	client_count = atomic_read(&msm_uport->client_count);
+	msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+	msm_hs_resource_off(msm_uport);
+	obs_manage_irq(msm_uport, false);
+	msm_hs_clk_bus_unvote(msm_uport);
+
+	/* For OBS, don't use wakeup interrupt, set gpio to suspended state */
+	if (msm_uport->obs) {
+		ret = pinctrl_select_state(msm_uport->pinctrl,
+			msm_uport->gpio_state_suspend);
+		if (ret)
+			MSM_HS_ERR("%s():Error selecting pinctrl suspend state",
+				__func__);
+	}
+
+	if (!atomic_read(&msm_uport->client_req_state))
+		enable_wakeup_interrupt(msm_uport);
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+		"%s: PM State Suspended client_count %d\n", __func__,
+								client_count);
+	mutex_unlock(&msm_uport->mtx);
+	return;
+err_suspend:
+	pr_err("%s(): invalid uport", __func__);
+}
+
+static int msm_hs_pm_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+	int ret = 0;
+	int client_count = 0;
+
+	if (!msm_uport) {
+		dev_err(dev, "%s:Invalid uport\n", __func__);
+		return -ENODEV;
+	}
+
+	mutex_lock(&msm_uport->mtx);
+	client_count = atomic_read(&msm_uport->client_count);
+	if (msm_uport->pm_state == MSM_HS_PM_ACTIVE)
+		goto exit_pm_resume;
+	if (!atomic_read(&msm_uport->client_req_state))
+		disable_wakeup_interrupt(msm_uport);
+
+	/* For OBS, don't use wakeup interrupt, set gpio to active state */
+	if (msm_uport->obs) {
+		ret = pinctrl_select_state(msm_uport->pinctrl,
+				msm_uport->gpio_state_active);
+		if (ret)
+			MSM_HS_ERR("%s():Error selecting active state",
+				 __func__);
+	}
+
+	ret = msm_hs_clk_bus_vote(msm_uport);
+	if (ret) {
+		MSM_HS_ERR("%s:Failed clock vote %d\n", __func__, ret);
+		dev_err(dev, "%s:Failed clock vote %d\n", __func__, ret);
+		goto exit_pm_resume;
+	}
+	obs_manage_irq(msm_uport, true);
+	msm_uport->pm_state = MSM_HS_PM_ACTIVE;
+	msm_hs_resource_on(msm_uport);
+
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+		"%s:PM State:Active client_count %d\n", __func__, client_count);
+exit_pm_resume:
+	mutex_unlock(&msm_uport->mtx);
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int msm_hs_pm_sys_suspend_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+	enum msm_hs_pm_state prev_pwr_state;
+	int clk_cnt, client_count, ret = 0;
+
+	if (IS_ERR_OR_NULL(msm_uport))
+		return -ENODEV;
+
+	mutex_lock(&msm_uport->mtx);
+
+	/*
+	 * If there is an active clk request or an impending userspace request
+	 * fail the suspend callback.
+	 */
+	clk_cnt = atomic_read(&msm_uport->resource_count);
+	client_count = atomic_read(&msm_uport->client_count);
+	if (msm_uport->pm_state == MSM_HS_PM_ACTIVE) {
+		MSM_HS_WARN("%s:Fail Suspend.clk_cnt:%d,clnt_count:%d\n",
+				 __func__, clk_cnt, client_count);
+		ret = -EBUSY;
+		goto exit_suspend_noirq;
+	}
+
+	prev_pwr_state = msm_uport->pm_state;
+	msm_uport->pm_state = MSM_HS_PM_SYS_SUSPENDED;
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+		"%s:PM State:Sys-Suspended client_count %d\n", __func__,
+								client_count);
+exit_suspend_noirq:
+	mutex_unlock(&msm_uport->mtx);
+	return ret;
+};
+
+static int msm_hs_pm_sys_resume_noirq(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_hs_port *msm_uport = get_matching_hs_port(pdev);
+
+	if (IS_ERR_OR_NULL(msm_uport))
+		return -ENODEV;
+	/*
+	 * Note system-pm resume and update the state
+	 * variable. Resource activation will be done
+	 * when transfer is requested.
+	 */
+
+	mutex_lock(&msm_uport->mtx);
+	if (msm_uport->pm_state == MSM_HS_PM_SYS_SUSPENDED)
+		msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+	LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+		"%s:PM State: Suspended\n", __func__);
+	mutex_unlock(&msm_uport->mtx);
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static void  msm_serial_hs_rt_init(struct uart_port *uport)
+{
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+
+	MSM_HS_INFO("%s(): Enabling runtime pm", __func__);
+	pm_runtime_set_suspended(uport->dev);
+	pm_runtime_set_autosuspend_delay(uport->dev, 100);
+	pm_runtime_use_autosuspend(uport->dev);
+	mutex_lock(&msm_uport->mtx);
+	msm_uport->pm_state = MSM_HS_PM_SUSPENDED;
+	mutex_unlock(&msm_uport->mtx);
+	pm_runtime_enable(uport->dev);
+}
+
+static int msm_hs_runtime_suspend(struct device *dev)
+{
+	msm_hs_pm_suspend(dev);
+	return 0;
+}
+
+static int msm_hs_runtime_resume(struct device *dev)
+{
+	return msm_hs_pm_resume(dev);
+}
+#else
+static void  msm_serial_hs_rt_init(struct uart_port *uport) {}
+static int msm_hs_runtime_suspend(struct device *dev) {}
+static int msm_hs_runtime_resume(struct device *dev) {}
+#endif
+
+
+static int msm_hs_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct uart_port *uport;
+	struct msm_hs_port *msm_uport;
+	struct resource *core_resource;
+	struct resource *bam_resource;
+	int core_irqres, bam_irqres, wakeup_irqres;
+	struct msm_serial_hs_platform_data *pdata = pdev->dev.platform_data;
+	unsigned long data;
+	char name[30];
+
+	if (pdev->dev.of_node) {
+		dev_dbg(&pdev->dev, "device tree enabled\n");
+		pdata = msm_hs_dt_to_pdata(pdev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+
+		if (pdev->id < 0) {
+			pdev->id = device_id_grab_next_free();
+			if (pdev->id < 0) {
+				dev_err(&pdev->dev,
+					"Error grabbing next free device id");
+				return pdev->id;
+			}
+		} else {
+			ret = device_id_set_used(pdev->id);
+			if (ret < 0) {
+				dev_err(&pdev->dev, "%d alias taken",
+					pdev->id);
+				return ret;
+			}
+		}
+		pdev->dev.platform_data = pdata;
+	}
+
+	if (pdev->id < 0 || pdev->id >= UARTDM_NR) {
+		dev_err(&pdev->dev, "Invalid plaform device ID = %d\n",
+								pdev->id);
+		return -EINVAL;
+	}
+
+	msm_uport = devm_kzalloc(&pdev->dev, sizeof(struct msm_hs_port),
+			GFP_KERNEL);
+	if (!msm_uport)
+		return -ENOMEM;
+
+	msm_uport->uport.type = PORT_UNKNOWN;
+	uport = &msm_uport->uport;
+	uport->dev = &pdev->dev;
+
+	if (pdev->dev.of_node)
+		msm_uport->uart_type = BLSP_HSUART;
+
+	msm_hs_get_pinctrl_configs(uport);
+	/* Get required resources for BAM HSUART */
+	core_resource = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "core_mem");
+	if (!core_resource) {
+		dev_err(&pdev->dev, "Invalid core HSUART Resources.\n");
+		return -ENXIO;
+	}
+	bam_resource = platform_get_resource_byname(pdev,
+				IORESOURCE_MEM, "bam_mem");
+	if (!bam_resource) {
+		dev_err(&pdev->dev, "Invalid BAM HSUART Resources.\n");
+		return -ENXIO;
+	}
+	core_irqres = platform_get_irq_byname(pdev, "core_irq");
+	if (core_irqres < 0) {
+		dev_err(&pdev->dev, "Error %d, invalid core irq resources.\n",
+			core_irqres);
+		return -ENXIO;
+	}
+	bam_irqres = platform_get_irq_byname(pdev, "bam_irq");
+	if (bam_irqres < 0) {
+		dev_err(&pdev->dev, "Error %d, invalid bam irq resources.\n",
+			bam_irqres);
+		return -ENXIO;
+	}
+	wakeup_irqres = platform_get_irq_byname(pdev, "wakeup_irq");
+	if (wakeup_irqres < 0) {
+		wakeup_irqres = -1;
+		pr_info("Wakeup irq not specified.\n");
+	}
+
+	uport->mapbase = core_resource->start;
+
+	uport->membase = ioremap(uport->mapbase,
+				resource_size(core_resource));
+	if (unlikely(!uport->membase)) {
+		dev_err(&pdev->dev, "UART Resource ioremap Failed.\n");
+		return -ENOMEM;
+	}
+	msm_uport->bam_mem = bam_resource->start;
+	msm_uport->bam_base = ioremap(msm_uport->bam_mem,
+				resource_size(bam_resource));
+	if (unlikely(!msm_uport->bam_base)) {
+		dev_err(&pdev->dev, "UART BAM Resource ioremap Failed.\n");
+		iounmap(uport->membase);
+		return -ENOMEM;
+	}
+
+	memset(name, 0, sizeof(name));
+	scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+									"_state");
+	msm_uport->ipc_msm_hs_log_ctxt =
+			ipc_log_context_create(IPC_MSM_HS_LOG_STATE_PAGES,
+								name, 0);
+	if (!msm_uport->ipc_msm_hs_log_ctxt) {
+		dev_err(&pdev->dev, "%s: error creating logging context",
+								__func__);
+	} else {
+		msm_uport->ipc_debug_mask = INFO_LEV;
+		ret = sysfs_create_file(&pdev->dev.kobj,
+				&dev_attr_debug_mask.attr);
+		if (unlikely(ret))
+			MSM_HS_WARN("%s: Failed to create dev. attr", __func__);
+	}
+
+	uport->irq = core_irqres;
+	msm_uport->bam_irq = bam_irqres;
+	pdata->wakeup_irq = wakeup_irqres;
+
+	msm_uport->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+	if (!msm_uport->bus_scale_table) {
+		MSM_HS_ERR("BLSP UART: Bus scaling is disabled.\n");
+	} else {
+		msm_uport->bus_perf_client =
+			msm_bus_scale_register_client
+				(msm_uport->bus_scale_table);
+		if (IS_ERR(&msm_uport->bus_perf_client)) {
+			MSM_HS_ERR("%s():Bus client register failed\n",
+				   __func__);
+			ret = -EINVAL;
+			goto unmap_memory;
+		}
+	}
+
+	msm_uport->wakeup.irq = pdata->wakeup_irq;
+	msm_uport->wakeup.ignore = 1;
+	msm_uport->wakeup.inject_rx = pdata->inject_rx_on_wakeup;
+	msm_uport->wakeup.rx_to_inject = pdata->rx_to_inject;
+	msm_uport->obs = pdata->obs;
+
+	msm_uport->bam_tx_ep_pipe_index =
+			pdata->bam_tx_ep_pipe_index;
+	msm_uport->bam_rx_ep_pipe_index =
+			pdata->bam_rx_ep_pipe_index;
+	msm_uport->wakeup.enabled = true;
+
+	uport->iotype = UPIO_MEM;
+	uport->fifosize = 64;
+	uport->ops = &msm_hs_ops;
+	uport->flags = UPF_BOOT_AUTOCONF;
+	uport->uartclk = 7372800;
+	msm_uport->imr_reg = 0x0;
+
+	msm_uport->clk = clk_get(&pdev->dev, "core_clk");
+	if (IS_ERR(msm_uport->clk)) {
+		ret = PTR_ERR(msm_uport->clk);
+		goto deregister_bus_client;
+	}
+
+	msm_uport->pclk = clk_get(&pdev->dev, "iface_clk");
+	/*
+	 * Some configurations do not require explicit pclk control so
+	 * do not flag error on pclk get failure.
+	 */
+	if (IS_ERR(msm_uport->pclk))
+		msm_uport->pclk = NULL;
+
+	msm_uport->hsuart_wq = alloc_workqueue("k_hsuart",
+					WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+	if (!msm_uport->hsuart_wq) {
+		MSM_HS_ERR("%s(): Unable to create workqueue hsuart_wq\n",
+								__func__);
+		ret =  -ENOMEM;
+		goto put_clk;
+	}
+
+	mutex_init(&msm_uport->mtx);
+
+	/* Initialize SPS HW connected with UART core */
+	ret = msm_hs_sps_init(msm_uport);
+	if (unlikely(ret)) {
+		MSM_HS_ERR("SPS Initialization failed ! err=%d", ret);
+		goto destroy_mutex;
+	}
+
+	msm_uport->tx.flush = FLUSH_SHUTDOWN;
+	msm_uport->rx.flush = FLUSH_SHUTDOWN;
+
+	memset(name, 0, sizeof(name));
+	scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+									"_tx");
+	msm_uport->tx.ipc_tx_ctxt =
+		ipc_log_context_create(IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
+	if (!msm_uport->tx.ipc_tx_ctxt)
+		dev_err(&pdev->dev, "%s: error creating tx logging context",
+								__func__);
+
+	memset(name, 0, sizeof(name));
+	scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+									"_rx");
+	msm_uport->rx.ipc_rx_ctxt = ipc_log_context_create(
+					IPC_MSM_HS_LOG_DATA_PAGES, name, 0);
+	if (!msm_uport->rx.ipc_rx_ctxt)
+		dev_err(&pdev->dev, "%s: error creating rx logging context",
+								__func__);
+
+	memset(name, 0, sizeof(name));
+	scnprintf(name, sizeof(name), "%s%s", dev_name(msm_uport->uport.dev),
+									"_pwr");
+	msm_uport->ipc_msm_hs_pwr_ctxt = ipc_log_context_create(
+					IPC_MSM_HS_LOG_USER_PAGES, name, 0);
+	if (!msm_uport->ipc_msm_hs_pwr_ctxt)
+		dev_err(&pdev->dev, "%s: error creating usr logging context",
+								__func__);
+
+	uport->irq = core_irqres;
+	msm_uport->bam_irq = bam_irqres;
+
+	clk_set_rate(msm_uport->clk, msm_uport->uport.uartclk);
+	msm_hs_clk_bus_vote(msm_uport);
+	ret = uartdm_init_port(uport);
+	if (unlikely(ret))
+		goto err_clock;
+
+	/* configure the CR Protection to Enable */
+	msm_hs_write(uport, UART_DM_CR, CR_PROTECTION_EN);
+
+	/*
+	 * Enable Command register protection before going ahead as this hw
+	 * configuration makes sure that issued cmd to CR register gets complete
+	 * before next issued cmd start. Hence mb() requires here.
+	 */
+	mb();
+
+	/*
+	 * Set RX_BREAK_ZERO_CHAR_OFF and RX_ERROR_CHAR_OFF
+	 * so any rx_break and character having parity of framing
+	 * error don't enter inside UART RX FIFO.
+	 */
+	data = msm_hs_read(uport, UART_DM_MR2);
+	data |= (UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF |
+			UARTDM_MR2_RX_ERROR_CHAR_OFF);
+	msm_hs_write(uport, UART_DM_MR2, data);
+	/* Ensure register IO completion */
+	mb();
+
+	ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_clock.attr);
+	if (unlikely(ret)) {
+		MSM_HS_ERR("Probe Failed as sysfs failed\n");
+		goto err_clock;
+	}
+
+	msm_serial_debugfs_init(msm_uport, pdev->id);
+	msm_hs_unconfig_uart_gpios(uport);
+
+	uport->line = pdev->id;
+	if (pdata->userid && pdata->userid <= UARTDM_NR)
+		uport->line = pdata->userid;
+	ret = uart_add_one_port(&msm_hs_driver, uport);
+	if (!ret) {
+		msm_hs_clk_bus_unvote(msm_uport);
+		msm_serial_hs_rt_init(uport);
+		return ret;
+	}
+
+err_clock:
+	msm_hs_clk_bus_unvote(msm_uport);
+
+destroy_mutex:
+	mutex_destroy(&msm_uport->mtx);
+	destroy_workqueue(msm_uport->hsuart_wq);
+
+put_clk:
+	if (msm_uport->pclk)
+		clk_put(msm_uport->pclk);
+
+	if (msm_uport->clk)
+		clk_put(msm_uport->clk);
+
+deregister_bus_client:
+	msm_bus_scale_unregister_client(msm_uport->bus_perf_client);
+unmap_memory:
+	iounmap(uport->membase);
+	iounmap(msm_uport->bam_base);
+
+	return ret;
+}
+
+static int __init msm_serial_hs_init(void)
+{
+	int ret;
+
+	ret = uart_register_driver(&msm_hs_driver);
+	if (unlikely(ret)) {
+		pr_err("%s failed to load\n", __func__);
+		return ret;
+	}
+	debug_base = debugfs_create_dir("msm_serial_hs", NULL);
+	if (IS_ERR_OR_NULL(debug_base))
+		pr_err("msm_serial_hs: Cannot create debugfs dir\n");
+
+	ret = platform_driver_register(&msm_serial_hs_platform_driver);
+	if (ret) {
+		pr_err("%s failed to load\n", __func__);
+		debugfs_remove_recursive(debug_base);
+		uart_unregister_driver(&msm_hs_driver);
+		return ret;
+	}
+
+	pr_info("msm_serial_hs module loaded\n");
+	return ret;
+}
+
+/*
+ *  Called by the upper layer when port is closed.
+ *     - Disables the port
+ *     - Unhook the ISR
+ */
+static void msm_hs_shutdown(struct uart_port *uport)
+{
+	int ret, rc;
+	struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
+	struct circ_buf *tx_buf = &uport->state->xmit;
+	int data;
+	unsigned long flags;
+
+	if (is_use_low_power_wakeup(msm_uport))
+		irq_set_irq_wake(msm_uport->wakeup.irq, 0);
+
+	if (msm_uport->wakeup.enabled)
+		disable_irq(msm_uport->wakeup.irq);
+	else
+		disable_irq(uport->irq);
+
+	spin_lock_irqsave(&uport->lock, flags);
+	msm_uport->wakeup.enabled = false;
+	msm_uport->wakeup.ignore = 1;
+	spin_unlock_irqrestore(&uport->lock, flags);
+
+	/* Free the interrupt */
+	free_irq(uport->irq, msm_uport);
+	if (is_use_low_power_wakeup(msm_uport)) {
+		free_irq(msm_uport->wakeup.irq, msm_uport);
+		MSM_HS_DBG("%s(): wakeup irq freed", __func__);
+	}
+	msm_uport->wakeup.freed = true;
+
+	/* make sure tx lh finishes */
+	kthread_flush_worker(&msm_uport->tx.kworker);
+	ret = wait_event_timeout(msm_uport->tx.wait,
+			uart_circ_empty(tx_buf), 500);
+	if (!ret)
+		MSM_HS_WARN("Shutdown called when tx buff not empty");
+
+	msm_hs_resource_vote(msm_uport);
+	/* Stop remote side from sending data */
+	msm_hs_disable_flow_control(uport, false);
+	/* make sure rx lh finishes */
+	kthread_flush_worker(&msm_uport->rx.kworker);
+
+	if (msm_uport->rx.flush != FLUSH_SHUTDOWN) {
+		/* disable and disconnect rx */
+		ret = wait_event_timeout(msm_uport->rx.wait,
+				!msm_uport->rx.pending_flag, 500);
+		if (!ret)
+			MSM_HS_WARN("%s(): rx disconnect not complete",
+				__func__);
+		msm_hs_disconnect_rx(uport);
+	}
+
+	cancel_delayed_work_sync(&msm_uport->rx.flip_insert_work);
+	flush_workqueue(msm_uport->hsuart_wq);
+
+	/* BAM Disconnect for TX */
+	data = msm_hs_read(uport, UART_DM_DMEN);
+	data &= ~UARTDM_TX_BAM_ENABLE_BMSK;
+	msm_hs_write(uport, UART_DM_DMEN, data);
+	ret = sps_tx_disconnect(msm_uport);
+	if (ret)
+		MSM_HS_ERR("%s(): sps_disconnect failed\n",
+					__func__);
+	msm_uport->tx.flush = FLUSH_SHUTDOWN;
+	/* Disable the transmitter */
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_TX_DISABLE_BMSK);
+	/* Disable the receiver */
+	msm_hs_write(uport, UART_DM_CR, UARTDM_CR_RX_DISABLE_BMSK);
+
+	msm_uport->imr_reg = 0;
+	msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
+	/*
+	 * Complete all device write before actually disabling uartclk.
+	 * Hence mb() requires here.
+	 */
+	mb();
+
+	msm_uport->rx.buffer_pending = NONE_PENDING;
+	MSM_HS_DBG("%s(): tx, rx events complete", __func__);
+
+	dma_unmap_single(uport->dev, msm_uport->tx.dma_base,
+			 UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+	msm_hs_resource_unvote(msm_uport);
+	rc = atomic_read(&msm_uport->resource_count);
+	if (rc) {
+		atomic_set(&msm_uport->resource_count, 1);
+		MSM_HS_WARN("%s(): removing extra vote\n", __func__);
+		msm_hs_resource_unvote(msm_uport);
+	}
+	if (atomic_read(&msm_uport->client_req_state)) {
+		MSM_HS_WARN("%s: Client clock vote imbalance\n", __func__);
+		atomic_set(&msm_uport->client_req_state, 0);
+	}
+	if (atomic_read(&msm_uport->client_count)) {
+		MSM_HS_WARN("%s: Client vote on, forcing to 0\n", __func__);
+		atomic_set(&msm_uport->client_count, 0);
+		LOG_USR_MSG(msm_uport->ipc_msm_hs_pwr_ctxt,
+			"%s: Client_Count 0\n", __func__);
+	}
+	msm_hs_unconfig_uart_gpios(uport);
+	MSM_HS_INFO("%s:UART port closed successfully\n", __func__);
+}
+
+static void __exit msm_serial_hs_exit(void)
+{
+	pr_info("msm_serial_hs module removed\n");
+	debugfs_remove_recursive(debug_base);
+	platform_driver_unregister(&msm_serial_hs_platform_driver);
+	uart_unregister_driver(&msm_hs_driver);
+}
+
+static const struct dev_pm_ops msm_hs_dev_pm_ops = {
+	.runtime_suspend = msm_hs_runtime_suspend,
+	.runtime_resume = msm_hs_runtime_resume,
+	.runtime_idle = NULL,
+	.suspend_noirq = msm_hs_pm_sys_suspend_noirq,
+	.resume_noirq = msm_hs_pm_sys_resume_noirq,
+};
+
+static struct platform_driver msm_serial_hs_platform_driver = {
+	.probe	= msm_hs_probe,
+	.remove = msm_hs_remove,
+	.driver = {
+		.name = "msm_serial_hs",
+		.pm   = &msm_hs_dev_pm_ops,
+		.of_match_table = msm_hs_match_table,
+	},
+};
+
+static struct uart_driver msm_hs_driver = {
+	.owner = THIS_MODULE,
+	.driver_name = "msm_serial_hs",
+	.dev_name = "ttyHS",
+	.nr = UARTDM_NR,
+	.cons = 0,
+};
+
+static const struct uart_ops msm_hs_ops = {
+	.tx_empty = msm_hs_tx_empty,
+	.set_mctrl = msm_hs_set_mctrl_locked,
+	.get_mctrl = msm_hs_get_mctrl_locked,
+	.stop_tx = msm_hs_stop_tx_locked,
+	.start_tx = msm_hs_start_tx_locked,
+	.stop_rx = msm_hs_stop_rx_locked,
+	.enable_ms = msm_hs_enable_ms_locked,
+	.break_ctl = msm_hs_break_ctl,
+	.startup = msm_hs_startup,
+	.shutdown = msm_hs_shutdown,
+	.set_termios = msm_hs_set_termios,
+	.type = msm_hs_type,
+	.config_port = msm_hs_config_port,
+	.flush_buffer = NULL,
+	.ioctl = msm_hs_ioctl,
+};
+
+module_init(msm_serial_hs_init);
+module_exit(msm_serial_hs_exit);
+MODULE_DESCRIPTION("High Speed UART Driver for the MSM chipset");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/tty/serial/msm_serial_hs_hwreg.h b/drivers/tty/serial/msm_serial_hs_hwreg.h
new file mode 100644
index 0000000..d5ce41f
--- /dev/null
+++ b/drivers/tty/serial/msm_serial_hs_hwreg.h
@@ -0,0 +1,283 @@
+/* drivers/serial/msm_serial_hs_hwreg.h
+ *
+ * Copyright (c) 2007-2009, 2012-2014,The Linux Foundation. All rights reserved.
+ *
+ * All source code in this file is licensed under the following license
+ * except where indicated.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org
+ */
+
+#ifndef MSM_SERIAL_HS_HWREG_H
+#define MSM_SERIAL_HS_HWREG_H
+
+#define GSBI_CONTROL_ADDR              0x0
+#define GSBI_PROTOCOL_CODE_MASK        0x30
+#define GSBI_PROTOCOL_I2C_UART         0x60
+#define GSBI_PROTOCOL_UART             0x40
+#define GSBI_PROTOCOL_IDLE             0x0
+
+#define TCSR_ADM_1_A_CRCI_MUX_SEL      0x78
+#define TCSR_ADM_1_B_CRCI_MUX_SEL      0x7C
+#define ADM1_CRCI_GSBI6_RX_SEL         0x800
+#define ADM1_CRCI_GSBI6_TX_SEL         0x400
+
+#define MSM_ENABLE_UART_CLOCK TIOCPMGET
+#define MSM_DISABLE_UART_CLOCK TIOCPMPUT
+#define MSM_GET_UART_CLOCK_STATUS TIOCPMACT
+
+enum msm_hsl_regs {
+	UARTDM_MR1,
+	UARTDM_MR2,
+	UARTDM_IMR,
+	UARTDM_SR,
+	UARTDM_CR,
+	UARTDM_CSR,
+	UARTDM_IPR,
+	UARTDM_ISR,
+	UARTDM_RX_TOTAL_SNAP,
+	UARTDM_RFWR,
+	UARTDM_TFWR,
+	UARTDM_RF,
+	UARTDM_TF,
+	UARTDM_MISR,
+	UARTDM_DMRX,
+	UARTDM_NCF_TX,
+	UARTDM_DMEN,
+	UARTDM_BCR,
+	UARTDM_TXFS,
+	UARTDM_RXFS,
+	UARTDM_LAST,
+};
+
+enum msm_hs_regs {
+	UART_DM_MR1 = 0x0,
+	UART_DM_MR2 = 0x4,
+	UART_DM_IMR = 0xb0,
+	UART_DM_SR = 0xa4,
+	UART_DM_CR = 0xa8,
+	UART_DM_CSR = 0xa0,
+	UART_DM_IPR = 0x18,
+	UART_DM_ISR = 0xb4,
+	UART_DM_RX_TOTAL_SNAP = 0xbc,
+	UART_DM_TFWR = 0x1c,
+	UART_DM_RFWR = 0x20,
+	UART_DM_RF = 0x140,
+	UART_DM_TF = 0x100,
+	UART_DM_MISR = 0xac,
+	UART_DM_DMRX = 0x34,
+	UART_DM_NCF_TX = 0x40,
+	UART_DM_DMEN = 0x3c,
+	UART_DM_TXFS = 0x4c,
+	UART_DM_RXFS = 0x50,
+	UART_DM_RX_TRANS_CTRL = 0xcc,
+	UART_DM_BCR = 0xc8,
+};
+
+#define UARTDM_MR1_ADDR 0x0
+#define UARTDM_MR2_ADDR 0x4
+
+/* Backward Compatibility Register for UARTDM Core v1.4 */
+#define UARTDM_BCR_ADDR	0xc8
+
+/*
+ * UARTDM Core v1.4 STALE_IRQ_EMPTY bit defination
+ * Stale interrupt will fire if bit is set when RX-FIFO is empty
+ */
+#define UARTDM_BCR_TX_BREAK_DISABLE	0x1
+#define UARTDM_BCR_STALE_IRQ_EMPTY	0x2
+#define UARTDM_BCR_RX_DMRX_LOW_EN	0x4
+#define UARTDM_BCR_RX_STAL_IRQ_DMRX_EQL	0x10
+#define UARTDM_BCR_RX_DMRX_1BYTE_RES_EN	0x20
+
+/* TRANSFER_CONTROL Register for UARTDM Core v1.4 */
+#define UARTDM_RX_TRANS_CTRL_ADDR      0xcc
+
+/* TRANSFER_CONTROL Register bits */
+#define RX_STALE_AUTO_RE_EN		0x1
+#define RX_TRANS_AUTO_RE_ACTIVATE	0x2
+#define RX_DMRX_CYCLIC_EN		0x4
+
+/* write only register */
+#define UARTDM_CSR_115200 0xFF
+#define UARTDM_CSR_57600  0xEE
+#define UARTDM_CSR_38400  0xDD
+#define UARTDM_CSR_28800  0xCC
+#define UARTDM_CSR_19200  0xBB
+#define UARTDM_CSR_14400  0xAA
+#define UARTDM_CSR_9600   0x99
+#define UARTDM_CSR_7200   0x88
+#define UARTDM_CSR_4800   0x77
+#define UARTDM_CSR_3600   0x66
+#define UARTDM_CSR_2400   0x55
+#define UARTDM_CSR_1200   0x44
+#define UARTDM_CSR_600    0x33
+#define UARTDM_CSR_300    0x22
+#define UARTDM_CSR_150    0x11
+#define UARTDM_CSR_75     0x00
+
+/* write only register */
+#define UARTDM_IPR_ADDR 0x18
+#define UARTDM_TFWR_ADDR 0x1c
+#define UARTDM_RFWR_ADDR 0x20
+#define UARTDM_HCR_ADDR 0x24
+#define UARTDM_DMRX_ADDR 0x34
+#define UARTDM_DMEN_ADDR 0x3c
+
+/* UART_DM_NO_CHARS_FOR_TX */
+#define UARTDM_NCF_TX_ADDR 0x40
+
+#define UARTDM_BADR_ADDR 0x44
+
+#define UARTDM_SIM_CFG_ADDR 0x80
+
+/* Read Only register */
+#define UARTDM_TXFS_ADDR 0x4C
+#define UARTDM_RXFS_ADDR 0x50
+
+/* Register field Mask Mapping */
+#define UARTDM_SR_RX_BREAK_BMSK	        BIT(6)
+#define UARTDM_SR_PAR_FRAME_BMSK	BIT(5)
+#define UARTDM_SR_OVERRUN_BMSK		BIT(4)
+#define UARTDM_SR_TXEMT_BMSK		BIT(3)
+#define UARTDM_SR_TXRDY_BMSK		BIT(2)
+#define UARTDM_SR_RXRDY_BMSK		BIT(0)
+
+#define UARTDM_CR_TX_DISABLE_BMSK	BIT(3)
+#define UARTDM_CR_RX_DISABLE_BMSK	BIT(1)
+#define UARTDM_CR_TX_EN_BMSK		BIT(2)
+#define UARTDM_CR_RX_EN_BMSK		BIT(0)
+
+/* UARTDM_CR channel_comman bit value (register field is bits 8:4) */
+#define RESET_RX		0x10
+#define RESET_TX		0x20
+#define RESET_ERROR_STATUS	0x30
+#define RESET_BREAK_INT		0x40
+#define START_BREAK		0x50
+#define STOP_BREAK		0x60
+#define RESET_CTS		0x70
+#define RESET_STALE_INT		0x80
+#define RFR_LOW			0xD0
+#define RFR_HIGH		0xE0
+#define CR_PROTECTION_EN	0x100
+#define STALE_EVENT_ENABLE	0x500
+#define STALE_EVENT_DISABLE	0x600
+#define FORCE_STALE_EVENT	0x400
+#define CLEAR_TX_READY		0x300
+#define RESET_TX_ERROR		0x800
+#define RESET_TX_DONE		0x810
+
+/*
+ * UARTDM_CR BAM IFC comman bit value
+ * for UARTDM Core v1.4
+ */
+#define START_RX_BAM_IFC	0x850
+#define START_TX_BAM_IFC	0x860
+
+#define UARTDM_MR1_AUTO_RFR_LEVEL1_BMSK 0xffffff00
+#define UARTDM_MR1_AUTO_RFR_LEVEL0_BMSK 0x3f
+#define UARTDM_MR1_CTS_CTL_BMSK 0x40
+#define UARTDM_MR1_RX_RDY_CTL_BMSK 0x80
+
+/*
+ * UARTDM Core v1.4 MR2_RFR_CTS_LOOP bitmask
+ * Enables internal loopback between RFR_N of
+ * RX channel and CTS_N of TX channel.
+ */
+#define UARTDM_MR2_RFR_CTS_LOOP_MODE_BMSK	0x400
+
+#define UARTDM_MR2_LOOP_MODE_BMSK		0x80
+#define UARTDM_MR2_ERROR_MODE_BMSK		0x40
+#define UARTDM_MR2_BITS_PER_CHAR_BMSK		0x30
+#define UARTDM_MR2_RX_ZERO_CHAR_OFF		0x100
+#define UARTDM_MR2_RX_ERROR_CHAR_OFF		0x200
+#define UARTDM_MR2_RX_BREAK_ZERO_CHAR_OFF	0x100
+
+#define UARTDM_MR2_BITS_PER_CHAR_8	(0x3 << 4)
+
+/* bits per character configuration */
+#define FIVE_BPC  (0 << 4)
+#define SIX_BPC   (1 << 4)
+#define SEVEN_BPC (2 << 4)
+#define EIGHT_BPC (3 << 4)
+
+#define UARTDM_MR2_STOP_BIT_LEN_BMSK 0xc
+#define STOP_BIT_ONE (1 << 2)
+#define STOP_BIT_TWO (3 << 2)
+
+#define UARTDM_MR2_PARITY_MODE_BMSK 0x3
+
+/* Parity configuration */
+#define NO_PARITY 0x0
+#define EVEN_PARITY 0x2
+#define ODD_PARITY 0x1
+#define SPACE_PARITY 0x3
+
+#define UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK 0xffffff80
+#define UARTDM_IPR_STALE_LSB_BMSK 0x1f
+
+/* These can be used for both ISR and IMR register */
+#define UARTDM_ISR_TX_READY_BMSK	BIT(7)
+#define UARTDM_ISR_CURRENT_CTS_BMSK	BIT(6)
+#define UARTDM_ISR_DELTA_CTS_BMSK	BIT(5)
+#define UARTDM_ISR_RXLEV_BMSK		BIT(4)
+#define UARTDM_ISR_RXSTALE_BMSK		BIT(3)
+#define UARTDM_ISR_RXBREAK_BMSK		BIT(2)
+#define UARTDM_ISR_RXHUNT_BMSK		BIT(1)
+#define UARTDM_ISR_TXLEV_BMSK		BIT(0)
+
+/* Field definitions for UART_DM_DMEN*/
+#define UARTDM_TX_DM_EN_BMSK 0x1
+#define UARTDM_RX_DM_EN_BMSK 0x2
+
+/*
+ * UARTDM Core v1.4 bitmask
+ * Bitmasks for enabling Rx and Tx BAM Interface
+ */
+#define UARTDM_TX_BAM_ENABLE_BMSK 0x4
+#define UARTDM_RX_BAM_ENABLE_BMSK 0x8
+
+/* Register offsets for UART Core v13 */
+
+/* write only register */
+#define UARTDM_CSR_ADDR    0x8
+
+/* write only register */
+#define UARTDM_TF_ADDR   0x70
+#define UARTDM_TF2_ADDR  0x74
+#define UARTDM_TF3_ADDR  0x78
+#define UARTDM_TF4_ADDR  0x7c
+
+/* write only register */
+#define UARTDM_CR_ADDR 0x10
+/* write only register */
+#define UARTDM_IMR_ADDR 0x14
+#define UARTDM_IRDA_ADDR 0x38
+
+/* Read Only register */
+#define UARTDM_SR_ADDR 0x8
+
+/* Read Only register */
+#define UARTDM_RF_ADDR   0x70
+#define UARTDM_RF2_ADDR  0x74
+#define UARTDM_RF3_ADDR  0x78
+#define UARTDM_RF4_ADDR  0x7c
+
+/* Read Only register */
+#define UARTDM_MISR_ADDR 0x10
+
+/* Read Only register */
+#define UARTDM_ISR_ADDR 0x14
+#define UARTDM_RX_TOTAL_SNAP_ADDR 0x38
+
+#endif /* MSM_SERIAL_HS_HWREG_H */
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 7e97a1c..15eaea5 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -193,18 +193,17 @@
 	},
 
 	/*
-	 * Common definitions for legacy IrDA ports, dependent on
-	 * regshift value.
+	 * Common definitions for legacy IrDA ports.
 	 */
 	[SCIx_IRDA_REGTYPE] = {
 		[SCSMR]		= { 0x00,  8 },
-		[SCBRR]		= { 0x01,  8 },
-		[SCSCR]		= { 0x02,  8 },
-		[SCxTDR]	= { 0x03,  8 },
-		[SCxSR]		= { 0x04,  8 },
-		[SCxRDR]	= { 0x05,  8 },
-		[SCFCR]		= { 0x06,  8 },
-		[SCFDR]		= { 0x07, 16 },
+		[SCBRR]		= { 0x02,  8 },
+		[SCSCR]		= { 0x04,  8 },
+		[SCxTDR]	= { 0x06,  8 },
+		[SCxSR]		= { 0x08, 16 },
+		[SCxRDR]	= { 0x0a,  8 },
+		[SCFCR]		= { 0x0c,  8 },
+		[SCFDR]		= { 0x0e, 16 },
 		[SCTFDR]	= sci_reg_invalid,
 		[SCRFDR]	= sci_reg_invalid,
 		[SCSPTR]	= sci_reg_invalid,
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 4e603d0..59828d8 100644
--- a/drivers/tty/serial/sunhv.c
+++ b/drivers/tty/serial/sunhv.c
@@ -398,6 +398,12 @@
 
 static struct uart_port *sunhv_port;
 
+void sunhv_migrate_hvcons_irq(int cpu)
+{
+	/* Migrate hvcons irq to param cpu */
+	irq_force_affinity(sunhv_port->irq, cpumask_of(cpu));
+}
+
 /* Copy 's' into the con_write_page, decoding "\n" into
  * "\r\n" along the way.  We have to return two lengths
  * because the caller needs to know how much to advance
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index aa80dc9..c220c2c 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -362,6 +362,32 @@
 EXPORT_SYMBOL(tty_insert_flip_string_flags);
 
 /**
+ *	__tty_insert_flip_char   -	Add one character to the tty buffer
+ *	@port: tty port
+ *	@ch: character
+ *	@flag: flag byte
+ *
+ *	Queue a single byte to the tty buffering, with an optional flag.
+ *	This is the slow path of tty_insert_flip_char.
+ */
+int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
+{
+	struct tty_buffer *tb;
+	int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
+
+	if (!__tty_buffer_request_room(port, 1, flags))
+		return 0;
+
+	tb = port->buf.tail;
+	if (~tb->flags & TTYB_NORMAL)
+		*flag_buf_ptr(tb, tb->used) = flag;
+	*char_buf_ptr(tb, tb->used++) = ch;
+
+	return 1;
+}
+EXPORT_SYMBOL(__tty_insert_flip_char);
+
+/**
  *	tty_schedule_flip	-	push characters to ldisc
  *	@port: tty port to push from
  *
diff --git a/drivers/uio/msm_sharedmem/msm_sharedmem.c b/drivers/uio/msm_sharedmem/msm_sharedmem.c
index b25f55a..8be3e36 100644
--- a/drivers/uio/msm_sharedmem/msm_sharedmem.c
+++ b/drivers/uio/msm_sharedmem/msm_sharedmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -107,10 +107,12 @@
 	struct resource *clnt_res = NULL;
 	u32 client_id = ((u32)~0U);
 	u32 shared_mem_size = 0;
+	u32 shared_mem_tot_sz = 0;
 	void *shared_mem = NULL;
 	phys_addr_t shared_mem_pyhsical = 0;
 	bool is_addr_dynamic = false;
 	struct sharemem_qmi_entry qmi_entry;
+	bool guard_memory = false;
 
 	/* Get the addresses from platform-data */
 	if (!pdev->dev.of_node) {
@@ -145,13 +147,30 @@
 
 	if (shared_mem_pyhsical == 0) {
 		is_addr_dynamic = true;
-		shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_size,
+
+		/*
+		 * If guard_memory is set, then the shared memory region
+		 * will be guarded by SZ_4K at the start and at the end.
+		 * This is needed to overcome the XPU limitation on few
+		 * MSM HW, so as to make this memory not contiguous with
+		 * other allocations that may possibly happen from other
+		 * clients in the system.
+		 */
+		guard_memory = of_property_read_bool(pdev->dev.of_node,
+				"qcom,guard-memory");
+
+		shared_mem_tot_sz = guard_memory ? shared_mem_size + SZ_8K :
+					shared_mem_size;
+
+		shared_mem = dma_alloc_coherent(&pdev->dev, shared_mem_tot_sz,
 					&shared_mem_pyhsical, GFP_KERNEL);
 		if (shared_mem == NULL) {
 			pr_err("Shared mem alloc client=%s, size=%u\n",
 				clnt_res->name, shared_mem_size);
 			return -ENOMEM;
 		}
+		if (guard_memory)
+			shared_mem_pyhsical += SZ_4K;
 	}
 
 	/* Set up the permissions for the shared ram that was allocated. */
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index 0cf149e..f36a1ac3 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -134,9 +134,9 @@
 	if (!ci->is_otg)
 		return;
 
-	if (hw_read_otgsc(ci, OTGSC_BSV))
+	if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
 		usb_gadget_vbus_connect(&ci->gadget);
-	else
+	else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
 		usb_gadget_vbus_disconnect(&ci->gadget);
 }
 
@@ -175,14 +175,21 @@
 
 		ci_role_stop(ci);
 
-		if (role == CI_ROLE_GADGET)
+		if (role == CI_ROLE_GADGET &&
+				IS_ERR(ci->platdata->vbus_extcon.edev))
 			/*
-			 * wait vbus lower than OTGSC_BSV before connecting
-			 * to host
+			 * Wait vbus lower than OTGSC_BSV before connecting
+			 * to host. If connecting status is from an external
+			 * connector instead of register, we don't need to
+			 * care vbus on the board, since it will not affect
+			 * external connector status.
 			 */
 			hw_wait_vbus_lower_bsv(ci);
 
 		ci_role_start(ci, role);
+		/* vbus change may have already occurred */
+		if (role == CI_ROLE_GADGET)
+			ci_handle_vbus_change(ci);
 	}
 }
 /**
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index f16491c..ea20b2c 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1773,6 +1773,9 @@
 	{ USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */
 	.driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */
 	},
+	{ USB_DEVICE(0x09d8, 0x0320), /* Elatec GmbH TWN3 */
+	.driver_info = NO_UNION_NORMAL, /* has misplaced union descriptor */
+	},
 
 	{ USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */
 	.driver_info = CLEAR_HALT_CONDITIONS,
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 0b845e5..9f00165 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -194,8 +194,10 @@
 	/*
 	 * only set a new error if there is no previous error.
 	 * Errors are only cleared during read/open
+	 * Avoid propagating -EPIPE (stall) to userspace since it is
+	 * better handled as an empty read
 	 */
-	if (desc->rerr  == 0)
+	if (desc->rerr == 0 && status != -EPIPE)
 		desc->rerr = status;
 
 	if (length + desc->length > desc->wMaxCommand) {
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 3fd2b54..701d9f7 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -638,15 +638,23 @@
 
 		} else if (header->bDescriptorType ==
 				USB_DT_INTERFACE_ASSOCIATION) {
+			struct usb_interface_assoc_descriptor *d;
+
+			d = (struct usb_interface_assoc_descriptor *)header;
+			if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) {
+				dev_warn(ddev,
+					 "config %d has an invalid interface association descriptor of length %d, skipping\n",
+					 cfgno, d->bLength);
+				continue;
+			}
+
 			if (iad_num == USB_MAXIADS) {
 				dev_warn(ddev, "found more Interface "
 					       "Association Descriptors "
 					       "than allocated for in "
 					       "configuration %d\n", cfgno);
 			} else {
-				config->intf_assoc[iad_num] =
-					(struct usb_interface_assoc_descriptor
-					*)header;
+				config->intf_assoc[iad_num] = d;
 				iad_num++;
 			}
 
@@ -847,7 +855,7 @@
 		}
 
 		if (dev->quirks & USB_QUIRK_DELAY_INIT)
-			msleep(100);
+			msleep(200);
 
 		result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
 		    bigbuffer, length);
@@ -947,10 +955,12 @@
 	for (i = 0; i < num; i++) {
 		buffer += length;
 		cap = (struct usb_dev_cap_header *)buffer;
-		length = cap->bLength;
 
-		if (total_len < length)
+		if (total_len < sizeof(*cap) || total_len < cap->bLength) {
+			dev->bos->desc->bNumDeviceCaps = i;
 			break;
+		}
+		length = cap->bLength;
 		total_len -= length;
 
 		if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 840930b0..c8075eb 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -629,6 +629,8 @@
 	if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
 			as->status != -ENOENT)
 		cancel_bulk_urbs(ps, as->bulk_addr);
+
+	wake_up(&ps->wait);
 	spin_unlock(&ps->lock);
 
 	if (signr) {
@@ -636,8 +638,6 @@
 		put_pid(pid);
 		put_cred(cred);
 	}
-
-	wake_up(&ps->wait);
 }
 
 static void destroy_async(struct usb_dev_state *ps, struct list_head *list)
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e07fa76..035f03b 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1878,7 +1878,7 @@
 	/* No more submits can occur */
 	spin_lock_irq(&hcd_urb_list_lock);
 rescan:
-	list_for_each_entry (urb, &ep->urb_list, urb_list) {
+	list_for_each_entry_reverse(urb, &ep->urb_list, urb_list) {
 		int	is_in;
 
 		if (urb->unlinked)
@@ -2256,39 +2256,38 @@
 
 /*-------------------------------------------------------------------------*/
 
-dma_addr_t
-usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev,
-	unsigned int intr_num)
+phys_addr_t
+usb_hcd_get_sec_event_ring_phys_addr(struct usb_device *udev,
+	unsigned int intr_num, dma_addr_t *dma)
 {
 	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);
 
 	if (!HCD_RH_RUNNING(hcd))
 		return 0;
 
-	return hcd->driver->get_sec_event_ring_dma_addr(hcd, intr_num);
+	return hcd->driver->get_sec_event_ring_phys_addr(hcd, intr_num, dma);
 }
 
-dma_addr_t
-usb_hcd_get_dcba_dma_addr(struct usb_device *udev)
+phys_addr_t
+usb_hcd_get_xfer_ring_phys_addr(struct usb_device *udev,
+		struct usb_host_endpoint *ep, dma_addr_t *dma)
 {
 	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);
 
 	if (!HCD_RH_RUNNING(hcd))
 		return 0;
 
-	return hcd->driver->get_dcba_dma_addr(hcd, udev);
+	return hcd->driver->get_xfer_ring_phys_addr(hcd, udev, ep, dma);
 }
 
-dma_addr_t
-usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev,
-		struct usb_host_endpoint *ep)
+int usb_hcd_get_controller_id(struct usb_device *udev)
 {
 	struct usb_hcd	*hcd = bus_to_hcd(udev->bus);
 
 	if (!HCD_RH_RUNNING(hcd))
-		return 0;
+		return -EINVAL;
 
-	return hcd->driver->get_xfer_ring_dma_addr(hcd, udev, ep);
+	return hcd->driver->get_core_id(hcd);
 }
 
 #ifdef	CONFIG_PM
@@ -2532,6 +2531,8 @@
 	}
 	if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) {
 		hcd = hcd->shared_hcd;
+		clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags);
+		set_bit(HCD_FLAG_DEAD, &hcd->flags);
 		if (hcd->rh_registered) {
 			clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
 
@@ -3092,6 +3093,7 @@
 	}
 
 	usb_put_invalidate_rhdev(hcd);
+	hcd->flags = 0;
 }
 EXPORT_SYMBOL_GPL(usb_remove_hcd);
 
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 50679bc..50a6f2f 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2715,13 +2715,16 @@
 	if (!(portstatus & USB_PORT_STAT_CONNECTION))
 		return -ENOTCONN;
 
-	/* bomb out completely if the connection bounced.  A USB 3.0
-	 * connection may bounce if multiple warm resets were issued,
+	/* Retry if connect change is set but status is still connected.
+	 * A USB 3.0 connection may bounce if multiple warm resets were issued,
 	 * but the device may have successfully re-connected. Ignore it.
 	 */
 	if (!hub_is_superspeed(hub->hdev) &&
-			(portchange & USB_PORT_STAT_C_CONNECTION))
-		return -ENOTCONN;
+	    (portchange & USB_PORT_STAT_C_CONNECTION)) {
+		usb_clear_port_feature(hub->hdev, port1,
+				       USB_PORT_FEAT_C_CONNECTION);
+		return -EAGAIN;
+	}
 
 	if (!(portstatus & USB_PORT_STAT_ENABLE))
 		return -EBUSY;
@@ -4741,13 +4744,15 @@
 static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 		u16 portchange)
 {
-	int status, i;
+	int ret, status = -ENODEV;
+	int i;
 	unsigned unit_load;
 	struct usb_device *hdev = hub->hdev;
 	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_device *udev = port_dev->child;
 	static int unreliable_port = -1;
+	enum usb_device_speed dev_speed = USB_SPEED_UNKNOWN;
 
 	/* Disconnect any existing devices under this port */
 	if (udev) {
@@ -4802,6 +4807,7 @@
 	else
 		unit_load = 100;
 
+retry_enum:
 	status = 0;
 	for (i = 0; i < SET_CONFIG_TRIES; i++) {
 
@@ -4839,8 +4845,15 @@
 		if (status < 0)
 			goto loop;
 
+		dev_speed = udev->speed;
+		if (udev->speed > USB_SPEED_UNKNOWN &&
+				udev->speed <= USB_SPEED_HIGH && hcd->usb_phy
+				&& hcd->usb_phy->disable_chirp)
+			hcd->usb_phy->disable_chirp(hcd->usb_phy,
+					false);
+
 		if (udev->quirks & USB_QUIRK_DELAY_INIT)
-			msleep(1000);
+			msleep(2000);
 
 		/* consecutive bus-powered hubs aren't reliable; they can
 		 * violate the voltage drop budget.  if the new child has
@@ -4941,13 +4954,27 @@
 		if (status != -ENOTCONN && status != -ENODEV)
 			dev_err(&port_dev->dev,
 					"unable to enumerate USB device\n");
+		if (!hub->hdev->parent && dev_speed == USB_SPEED_UNKNOWN
+			&& hcd->usb_phy && hcd->usb_phy->disable_chirp) {
+			ret = hcd->usb_phy->disable_chirp(hcd->usb_phy, true);
+			if (!ret) {
+				dev_dbg(&port_dev->dev,
+					"chirp disabled re-try enum\n");
+				goto retry_enum;
+			} else {
+				/* bail out and re-enable chirping */
+				hcd->usb_phy->disable_chirp(hcd->usb_phy,
+						false);
+			}
+		}
 	}
 
 done:
 	hub_port_disable(hub, port1, 1);
-	if (hcd->driver->relinquish_port && !hub->hdev->parent)
-		hcd->driver->relinquish_port(hcd, port1);
-
+	if (hcd->driver->relinquish_port && !hub->hdev->parent) {
+		if (status != -ENOTCONN && status != -ENODEV)
+			hcd->driver->relinquish_port(hcd, port1);
+	}
 }
 
 /* Handle physical or logical connection change events.
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 41a9845..4f8221e 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -2237,6 +2237,10 @@
 			elength = 1;
 			goto next_desc;
 		}
+		if ((buflen < elength) || (elength < 3)) {
+			dev_err(&intf->dev, "invalid descriptor buffer length\n");
+			break;
+		}
 		if (buffer[1] != USB_DT_CS_INTERFACE) {
 			dev_err(&intf->dev, "skipping garbage\n");
 			goto next_desc;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 3116edf..a6aaf2f 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -57,8 +57,9 @@
 	/* Microsoft LifeCam-VX700 v2.0 */
 	{ USB_DEVICE(0x045e, 0x0770), .driver_info = USB_QUIRK_RESET_RESUME },
 
-	/* Logitech HD Pro Webcams C920 and C930e */
+	/* Logitech HD Pro Webcams C920, C920-C and C930e */
 	{ USB_DEVICE(0x046d, 0x082d), .driver_info = USB_QUIRK_DELAY_INIT },
+	{ USB_DEVICE(0x046d, 0x0841), .driver_info = USB_QUIRK_DELAY_INIT },
 	{ USB_DEVICE(0x046d, 0x0843), .driver_info = USB_QUIRK_DELAY_INIT },
 
 	/* Logitech ConferenceCam CC3000e */
@@ -150,6 +151,9 @@
 	/* appletouch */
 	{ USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
 
+	/* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */
+	{ USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM },
+
 	/* Avision AV600U */
 	{ USB_DEVICE(0x0638, 0x0a13), .driver_info =
 	  USB_QUIRK_STRING_FETCH_255 },
@@ -214,6 +218,13 @@
 	{ USB_DEVICE(0x1a0a, 0x0200), .driver_info =
 			USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL },
 
+	/* Corsair Strafe RGB */
+	{ USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT },
+
+	/* MIDI keyboard WORLDE MINI */
+	{ USB_DEVICE(0x1c75, 0x0204), .driver_info =
+			USB_QUIRK_CONFIG_INTF_STRINGS },
+
 	/* Acer C120 LED Projector */
 	{ USB_DEVICE(0x1de1, 0xc102), .driver_info = USB_QUIRK_NO_LPM },
 
@@ -249,6 +260,7 @@
 	{ USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
 	{ USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
 	{ USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+	{ USB_DEVICE(0x03f0, 0x2b4a), .driver_info = USB_QUIRK_RESET_RESUME },
 
 	/* Logitech Optical Mouse M90/M100 */
 	{ USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 2776cfe..ef9cf4a 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -127,6 +127,22 @@
  */
 #define USB_ACPI_LOCATION_VALID (1 << 31)
 
+static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent,
+					      int raw)
+{
+	struct acpi_device *adev;
+
+	if (!parent)
+		return NULL;
+
+	list_for_each_entry(adev, &parent->children, node) {
+		if (acpi_device_adr(adev) == raw)
+			return adev;
+	}
+
+	return acpi_find_child_device(parent, raw, false);
+}
+
 static struct acpi_device *usb_acpi_find_companion(struct device *dev)
 {
 	struct usb_device *udev;
@@ -174,8 +190,10 @@
 			int raw;
 
 			raw = usb_hcd_find_raw_port_number(hcd, port1);
-			adev = acpi_find_child_device(ACPI_COMPANION(&udev->dev),
-					raw, false);
+
+			adev = usb_acpi_find_port(ACPI_COMPANION(&udev->dev),
+						  raw);
+
 			if (!adev)
 				return NULL;
 		} else {
@@ -186,7 +204,9 @@
 				return NULL;
 
 			acpi_bus_get_device(parent_handle, &adev);
-			adev = acpi_find_child_device(adev, port1, false);
+
+			adev = usb_acpi_find_port(adev, port1);
+
 			if (!adev)
 				return NULL;
 		}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 92e5d13..d745733 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -704,36 +704,35 @@
 }
 EXPORT_SYMBOL(usb_sec_event_ring_cleanup);
 
-dma_addr_t
-usb_get_sec_event_ring_dma_addr(struct usb_device *dev,
-	unsigned int intr_num)
+phys_addr_t
+usb_get_sec_event_ring_phys_addr(struct usb_device *dev,
+	unsigned int intr_num, dma_addr_t *dma)
 {
 	if (dev->state == USB_STATE_NOTATTACHED)
 		return 0;
 
-	return usb_hcd_get_sec_event_ring_dma_addr(dev, intr_num);
+	return usb_hcd_get_sec_event_ring_phys_addr(dev, intr_num, dma);
 }
-EXPORT_SYMBOL(usb_get_sec_event_ring_dma_addr);
+EXPORT_SYMBOL(usb_get_sec_event_ring_phys_addr);
 
-dma_addr_t
-usb_get_dcba_dma_addr(struct usb_device *dev)
+phys_addr_t usb_get_xfer_ring_phys_addr(struct usb_device *dev,
+	struct usb_host_endpoint *ep, dma_addr_t *dma)
 {
 	if (dev->state == USB_STATE_NOTATTACHED)
 		return 0;
 
-	return usb_hcd_get_dcba_dma_addr(dev);
+	return usb_hcd_get_xfer_ring_phys_addr(dev, ep, dma);
 }
-EXPORT_SYMBOL(usb_get_dcba_dma_addr);
+EXPORT_SYMBOL(usb_get_xfer_ring_phys_addr);
 
-dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev,
-	struct usb_host_endpoint *ep)
+int usb_get_controller_id(struct usb_device *dev)
 {
 	if (dev->state == USB_STATE_NOTATTACHED)
-		return 0;
+		return -EINVAL;
 
-	return usb_hcd_get_xfer_ring_dma_addr(dev, ep);
+	return usb_hcd_get_controller_id(dev);
 }
-EXPORT_SYMBOL(usb_get_xfer_ring_dma_addr);
+EXPORT_SYMBOL(usb_get_controller_id);
 
 /*-------------------------------------------------------------------*/
 /*
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 3e459b0..a5e050a 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -428,6 +428,9 @@
 {
 	struct dwc3_event_buffer	*evt;
 
+	if (!dwc->ev_buf)
+		return;
+
 	evt = dwc->ev_buf;
 
 	evt->lpos = 0;
@@ -687,13 +690,9 @@
 {
 	dwc3_event_buffers_cleanup(dwc);
 
-	usb_phy_shutdown(dwc->usb2_phy);
-	usb_phy_shutdown(dwc->usb3_phy);
 	phy_exit(dwc->usb2_generic_phy);
 	phy_exit(dwc->usb3_generic_phy);
 
-	usb_phy_set_suspend(dwc->usb2_phy, 1);
-	usb_phy_set_suspend(dwc->usb3_phy, 1);
 	phy_power_off(dwc->usb2_generic_phy);
 	phy_power_off(dwc->usb3_generic_phy);
 }
@@ -976,41 +975,14 @@
 	struct device *dev = dwc->dev;
 	int ret;
 
-	switch (dwc->dr_mode) {
-	case USB_DR_MODE_PERIPHERAL:
+	if (dwc->dr_mode == USB_DR_MODE_OTG ||
+			dwc->dr_mode == USB_DR_MODE_PERIPHERAL) {
 		ret = dwc3_gadget_init(dwc);
 		if (ret) {
 			if (ret != -EPROBE_DEFER)
 				dev_err(dev, "failed to initialize gadget\n");
 			return ret;
 		}
-		break;
-	case USB_DR_MODE_HOST:
-		ret = dwc3_host_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize host\n");
-			return ret;
-		}
-		break;
-	case USB_DR_MODE_OTG:
-		ret = dwc3_host_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize host\n");
-			return ret;
-		}
-
-		ret = dwc3_gadget_init(dwc);
-		if (ret) {
-			if (ret != -EPROBE_DEFER)
-				dev_err(dev, "failed to initialize gadget\n");
-			return ret;
-		}
-		break;
-	default:
-		dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
-		return -EINVAL;
 	}
 
 	return 0;
@@ -1018,21 +990,9 @@
 
 static void dwc3_core_exit_mode(struct dwc3 *dwc)
 {
-	switch (dwc->dr_mode) {
-	case USB_DR_MODE_PERIPHERAL:
+	if (dwc->dr_mode == USB_DR_MODE_PERIPHERAL ||
+			dwc->dr_mode == USB_DR_MODE_OTG)
 		dwc3_gadget_exit(dwc);
-		break;
-	case USB_DR_MODE_HOST:
-		dwc3_host_exit(dwc);
-		break;
-	case USB_DR_MODE_OTG:
-		dwc3_host_exit(dwc);
-		dwc3_gadget_exit(dwc);
-		break;
-	default:
-		/* do nothing */
-		break;
-	}
 }
 
 /* XHCI reset, resets other CORE registers as well, re-init those */
@@ -1209,6 +1169,10 @@
 				"snps,is-utmi-l1-suspend");
 	device_property_read_u8(dev, "snps,hird-threshold",
 				&hird_threshold);
+
+	device_property_read_u32(dev, "snps,xhci-imod-value",
+			&dwc->xhci_imod_value);
+
 	dwc->usb3_lpm_capable = device_property_read_bool(dev,
 				"snps,usb3_lpm_capable");
 
@@ -1254,7 +1218,8 @@
 				 &dwc->fladj);
 	dwc->disable_clk_gating = device_property_read_bool(dev,
 				"snps,disable-clk-gating");
-
+	dwc->enable_bus_suspend = device_property_read_bool(dev,
+					"snps,bus-suspend-enable");
 	if (dwc->enable_bus_suspend) {
 		pm_runtime_set_autosuspend_delay(dev, 500);
 		pm_runtime_use_autosuspend(dev);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b042152..f511055 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -568,7 +568,6 @@
  * @started_list: list of started requests on this endpoint
  * @lock: spinlock for endpoint request queue traversal
  * @regs: pointer to first endpoint register
- * @trb_dma_pool: dma pool used to get aligned trb memory pool
  * @trb_pool: array of transaction buffers
  * @trb_pool_dma: dma address of @trb_pool
  * @num_trbs: num of trbs in the trb dma pool
@@ -600,7 +599,6 @@
 	spinlock_t		lock;
 	void __iomem		*regs;
 
-	struct dma_pool		*trb_dma_pool;
 	struct dwc3_trb		*trb_pool;
 	dma_addr_t		trb_pool_dma;
 	u32			num_trbs;
@@ -951,6 +949,8 @@
  * @last_fifo_depth: total TXFIFO depth of all enabled USB IN/INT endpoints
  * @imod_interval: set the interrupt moderation interval in 250ns
  *			increments or 0 to disable.
+ * @create_reg_debugfs: create debugfs entry to allow dwc3 register dump
+ * @xhci_imod_value: imod value to use with xhci
  */
 struct dwc3 {
 	struct usb_ctrlrequest	*ctrl_req;
@@ -1145,6 +1145,8 @@
 	void			*dwc_ipc_log_ctxt;
 	int			last_fifo_depth;
 	struct dwc3_gadget_events	dbg_gadget_events;
+	bool			create_reg_debugfs;
+	u32			xhci_imod_value;
 };
 
 /* -------------------------------------------------------------------------- */
diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c
index 260092c..aebad09 100644
--- a/drivers/usb/dwc3/debugfs.c
+++ b/drivers/usb/dwc3/debugfs.c
@@ -291,6 +291,11 @@
 	unsigned long		flags;
 	u32			reg;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_GCTL);
 	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -326,6 +331,11 @@
 	u32			mode = 0;
 	char buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		dev_err(dwc->dev, "USB device is powered off\n");
+		return count;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -360,6 +370,12 @@
 	unsigned long		flags;
 	u32			reg;
 
+
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 	reg &= DWC3_DCTL_TSTCTRL_MASK;
@@ -406,6 +422,11 @@
 	u32			testmode = 0;
 	char			buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -444,6 +465,11 @@
 	enum dwc3_link_state	state;
 	u32			reg;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 	state = DWC3_DSTS_USBLNKST(reg);
@@ -513,6 +539,11 @@
 	enum dwc3_link_state	state = 0;
 	char			buf[32] = {};
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 
@@ -558,6 +589,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXFIFOQ);
 	seq_printf(s, "%u\n", val);
@@ -573,6 +609,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXFIFOQ);
 	seq_printf(s, "%u\n", val);
@@ -588,6 +629,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_TXREQQ);
 	seq_printf(s, "%u\n", val);
@@ -603,6 +649,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
 	seq_printf(s, "%u\n", val);
@@ -618,6 +669,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
 	seq_printf(s, "%u\n", val);
@@ -633,6 +689,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
 	seq_printf(s, "%u\n", val);
@@ -648,6 +709,11 @@
 	unsigned long		flags;
 	u32			val;
 
+	if (atomic_read(&dwc->in_lpm)) {
+		seq_puts(s, "USB device is powered off\n");
+		return 0;
+	}
+
 	spin_lock_irqsave(&dwc->lock, flags);
 	val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
 	seq_printf(s, "%u\n", val);
@@ -1044,9 +1110,12 @@
 	dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
 	dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
 
-	file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
-	if (!file)
-		dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+	if (dwc->create_reg_debugfs) {
+		file = debugfs_create_regset32("regdump", 0444,
+						root, dwc->regset);
+		if (!file)
+			dev_dbg(dwc->dev, "Can't create debugfs regdump\n");
+	}
 
 	if (IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)) {
 		file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 1c8d8dc..b6ad39b 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -266,6 +266,7 @@
 	struct pm_qos_request pm_qos_req_dma;
 	struct delayed_work perf_vote_work;
 	struct delayed_work sdp_check;
+	struct mutex suspend_resume_mutex;
 };
 
 #define USB_HSPHY_3P3_VOL_MIN		3050000 /* uV */
@@ -1051,25 +1052,17 @@
 	int num_trbs = (dep->direction) ? (2 * (req->num_bufs) + 2)
 					: (req->num_bufs + 2);
 
-	dep->trb_dma_pool = dma_pool_create(ep->name, dwc->sysdev,
-					num_trbs * sizeof(struct dwc3_trb),
-					num_trbs * sizeof(struct dwc3_trb), 0);
-	if (!dep->trb_dma_pool) {
+	dep->trb_pool = dma_zalloc_coherent(dwc->sysdev,
+				num_trbs * sizeof(struct dwc3_trb),
+				&dep->trb_pool_dma, GFP_KERNEL);
+
+	if (!dep->trb_pool) {
 		dev_err(dep->dwc->dev, "failed to alloc trb dma pool for %s\n",
 				dep->name);
 		return -ENOMEM;
 	}
 
 	dep->num_trbs = num_trbs;
-
-	dep->trb_pool = dma_pool_alloc(dep->trb_dma_pool,
-					   GFP_KERNEL, &dep->trb_pool_dma);
-	if (!dep->trb_pool) {
-		dev_err(dep->dwc->dev, "failed to allocate trb pool for %s\n",
-				dep->name);
-		return -ENOMEM;
-	}
-
 	/* IN direction */
 	if (dep->direction) {
 		for (i = 0; i < num_trbs ; i++) {
@@ -1159,18 +1152,19 @@
 static void gsi_free_trbs(struct usb_ep *ep)
 {
 	struct dwc3_ep *dep = to_dwc3_ep(ep);
+	struct dwc3 *dwc = dep->dwc;
 
 	if (dep->endpoint.ep_type == EP_TYPE_NORMAL)
 		return;
 
 	/*  Free TRBs and TRB pool for EP */
-	if (dep->trb_dma_pool) {
-		dma_pool_free(dep->trb_dma_pool, dep->trb_pool,
-						dep->trb_pool_dma);
-		dma_pool_destroy(dep->trb_dma_pool);
+	if (dep->trb_pool_dma) {
+		dma_free_coherent(dwc->sysdev,
+			dep->num_trbs * sizeof(struct dwc3_trb),
+			dep->trb_pool,
+			dep->trb_pool_dma);
 		dep->trb_pool = NULL;
 		dep->trb_pool_dma = 0;
-		dep->trb_dma_pool = NULL;
 	}
 }
 /*
@@ -1852,7 +1846,7 @@
 				break;
 			evt->dwc	= dwc;
 			evt->length	= DWC3_EVENT_BUFFERS_SIZE;
-			evt->buf	= dma_alloc_coherent(dwc->dev,
+			evt->buf	= dma_alloc_coherent(dwc->sysdev,
 						DWC3_EVENT_BUFFERS_SIZE,
 						&evt->dma, GFP_KERNEL);
 			if (!evt->buf) {
@@ -1867,6 +1861,9 @@
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_SETUP\n");
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
+			if (!evt)
+				break;
+
 			dev_dbg(mdwc->dev, "Evt buf %p dma %08llx length %d\n",
 				evt->buf, (unsigned long long) evt->dma,
 				evt->length);
@@ -1892,6 +1889,9 @@
 		break;
 	case DWC3_GSI_EVT_BUF_CLEANUP:
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_CLEANUP\n");
+		if (!mdwc->gsi_ev_buff)
+			break;
+
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
 			evt->lpos = 0;
@@ -1911,10 +1911,13 @@
 		break;
 	case DWC3_GSI_EVT_BUF_FREE:
 		dev_dbg(mdwc->dev, "DWC3_GSI_EVT_BUF_FREE\n");
+		if (!mdwc->gsi_ev_buff)
+			break;
+
 		for (i = 0; i < mdwc->num_gsi_event_buffers; i++) {
 			evt = mdwc->gsi_ev_buff[i];
 			if (evt)
-				dma_free_coherent(dwc->dev, evt->length,
+				dma_free_coherent(dwc->sysdev, evt->length,
 							evt->buf, evt->dma);
 		}
 		break;
@@ -2148,8 +2151,10 @@
 	struct dwc3_event_buffer *evt;
 	struct usb_irq *uirq;
 
+	mutex_lock(&mdwc->suspend_resume_mutex);
 	if (atomic_read(&dwc->in_lpm)) {
 		dev_dbg(mdwc->dev, "%s: Already suspended\n", __func__);
+		mutex_unlock(&mdwc->suspend_resume_mutex);
 		return 0;
 	}
 
@@ -2162,6 +2167,7 @@
 			dev_dbg(mdwc->dev,
 				"%s: %d device events pending, abort suspend\n",
 				__func__, evt->count / 4);
+			mutex_unlock(&mdwc->suspend_resume_mutex);
 			return -EBUSY;
 		}
 	}
@@ -2180,6 +2186,7 @@
 		dev_dbg(mdwc->dev,
 			"%s: cable disconnected while not in idle otg state\n",
 			__func__);
+		mutex_unlock(&mdwc->suspend_resume_mutex);
 		return -EBUSY;
 	}
 
@@ -2193,12 +2200,15 @@
 		pr_err("%s(): Trying to go in LPM with state:%d\n",
 					__func__, dwc->gadget.state);
 		pr_err("%s(): LPM is not performed.\n", __func__);
+		mutex_unlock(&mdwc->suspend_resume_mutex);
 		return -EBUSY;
 	}
 
 	ret = dwc3_msm_prepare_suspend(mdwc);
-	if (ret)
+	if (ret) {
+		mutex_unlock(&mdwc->suspend_resume_mutex);
 		return ret;
+	}
 
 	/* Disable core irq */
 	if (dwc->irq)
@@ -2306,6 +2316,7 @@
 	}
 
 	dev_info(mdwc->dev, "DWC3 in low power mode\n");
+	mutex_unlock(&mdwc->suspend_resume_mutex);
 	return 0;
 }
 
@@ -2318,8 +2329,10 @@
 
 	dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__);
 
+	mutex_lock(&mdwc->suspend_resume_mutex);
 	if (!atomic_read(&dwc->in_lpm)) {
 		dev_dbg(mdwc->dev, "%s: Already resumed\n", __func__);
+		mutex_unlock(&mdwc->suspend_resume_mutex);
 		return 0;
 	}
 
@@ -2470,6 +2483,8 @@
 			msecs_to_jiffies(1000 * PM_QOS_SAMPLE_SEC));
 
 	dbg_event(0xFF, "Ctl Res", atomic_read(&dwc->in_lpm));
+	mutex_unlock(&mdwc->suspend_resume_mutex);
+
 	return 0;
 }
 
@@ -2857,8 +2872,7 @@
 }
 
 /*
- * Handle EUD based soft detach/attach event, and force USB high speed mode
- * functionality on receiving soft attach event.
+ * Handle EUD based soft detach/attach event
  *
  * @nb - notifier handler
  * @event - event information i.e. soft detach/attach event
@@ -2877,9 +2891,6 @@
 	if (mdwc->vbus_active == event)
 		return NOTIFY_DONE;
 
-	/* Force USB High-Speed enumeration Only */
-	dwc->maximum_speed = USB_SPEED_HIGH;
-	dbg_event(0xFF, "Speed", dwc->maximum_speed);
 	mdwc->vbus_active = event;
 	if (dwc->is_drd && !mdwc->in_restart)
 		queue_work(mdwc->dwc3_wq, &mdwc->resume_work);
@@ -2978,8 +2989,8 @@
 	return ret;
 }
 
-#define SMMU_BASE	0x10000000 /* Device address range base */
-#define SMMU_SIZE	0x40000000 /* Device address range size */
+#define SMMU_BASE	0x60000000 /* Device address range base */
+#define SMMU_SIZE	0x90000000 /* Device address range size */
 
 static int dwc3_msm_init_iommu(struct dwc3_msm *mdwc)
 {
@@ -3149,7 +3160,7 @@
 	ret = dwc3_msm_get_clk_gdsc(mdwc);
 	if (ret) {
 		dev_err(&pdev->dev, "error getting clock or gdsc.\n");
-		return ret;
+		goto err;
 	}
 
 	mdwc->id_state = DWC3_ID_FLOAT;
@@ -3417,6 +3428,7 @@
 			POWER_SUPPLY_PROP_PRESENT, &pval);
 	}
 
+	mutex_init(&mdwc->suspend_resume_mutex);
 	/* Update initial VBUS/ID state from extcon */
 	if (mdwc->extcon_vbus && extcon_get_state(mdwc->extcon_vbus,
 							EXTCON_USB))
@@ -3445,24 +3457,20 @@
 	return 0;
 
 put_dwc3:
-	platform_device_put(mdwc->dwc3);
 	if (mdwc->bus_perf_client)
 		msm_bus_scale_unregister_client(mdwc->bus_perf_client);
+
 uninit_iommu:
 	if (mdwc->iommu_map) {
 		arm_iommu_detach_device(mdwc->dev);
 		arm_iommu_release_mapping(mdwc->iommu_map);
 	}
+	of_platform_depopulate(&pdev->dev);
 err:
+	destroy_workqueue(mdwc->dwc3_wq);
 	return ret;
 }
 
-static int dwc3_msm_remove_children(struct device *dev, void *data)
-{
-	device_unregister(dev);
-	return 0;
-}
-
 static int dwc3_msm_remove(struct platform_device *pdev)
 {
 	struct dwc3_msm	*mdwc = platform_get_drvdata(pdev);
@@ -3499,8 +3507,7 @@
 
 	if (mdwc->hs_phy)
 		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
-	platform_device_put(mdwc->dwc3);
-	device_for_each_child(&pdev->dev, NULL, dwc3_msm_remove_children);
+	of_platform_depopulate(&pdev->dev);
 
 	dbg_event(0xFF, "Remov put", 0);
 	pm_runtime_disable(mdwc->dev);
@@ -3667,9 +3674,6 @@
 	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
 	int ret = 0;
 
-	if (!dwc->xhci)
-		return -EINVAL;
-
 	/*
 	 * The vbus_reg pointer could have multiple values
 	 * NULL: regulator_get() hasn't been called, or was previously deferred
@@ -3720,15 +3724,7 @@
 
 		mdwc->usbdev_nb.notifier_call = msm_dwc3_usbdev_notify;
 		usb_register_atomic_notify(&mdwc->usbdev_nb);
-		/*
-		 * FIXME If micro A cable is disconnected during system suspend,
-		 * xhci platform device will be removed before runtime pm is
-		 * enabled for xhci device. Due to this, disable_depth becomes
-		 * greater than one and runtimepm is not enabled for next microA
-		 * connect. Fix this by calling pm_runtime_init for xhci device.
-		 */
-		pm_runtime_init(&dwc->xhci->dev);
-		ret = platform_device_add(dwc->xhci);
+		ret = dwc3_host_init(dwc);
 		if (ret) {
 			dev_err(mdwc->dev,
 				"%s: failed to add XHCI pdev ret=%d\n",
@@ -3796,14 +3792,13 @@
 		}
 
 		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
-		platform_device_del(dwc->xhci);
+		dwc3_host_exit(dwc);
 		usb_unregister_notify(&mdwc->host_nb);
 
 		dwc3_usb3_phy_suspend(dwc, false);
 		mdwc->in_host_mode = false;
 
-		pm_runtime_mark_last_busy(mdwc->dev);
-		pm_runtime_put_sync_autosuspend(mdwc->dev);
+		pm_runtime_put_sync_suspend(mdwc->dev);
 		dbg_event(0xFF, "StopHost psync",
 			atomic_read(&mdwc->dev->power.usage_count));
 	}
@@ -3908,6 +3903,8 @@
 	if (dwc->maximum_speed == usb_speed)
 		goto err;
 
+	dbg_event(0xFF, "fw_restarthost", 0);
+	flush_delayed_work(&mdwc->sm_work);
 	dbg_event(0xFF, "stop_host_mode", dwc->maximum_speed);
 	ret = dwc3_otg_start_host(mdwc, 0);
 	if (ret)
@@ -3956,20 +3953,20 @@
 	union power_supply_propval pval = {0};
 	int ret, psy_type;
 
-	if (mdwc->max_power == mA)
-		return 0;
-
 	psy_type = get_psy_type(mdwc);
-	if (psy_type == POWER_SUPPLY_TYPE_USB) {
-		dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
-		/* Set max current limit in uA */
-		pval.intval = 1000 * mA;
-	} else if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
+	if (psy_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
 		pval.intval = -ETIMEDOUT;
-	} else {
-		return 0;
+		goto set_prop;
 	}
 
+	if (mdwc->max_power == mA || psy_type != POWER_SUPPLY_TYPE_USB)
+		return 0;
+
+	dev_info(mdwc->dev, "Avail curr from USB = %u\n", mA);
+	/* Set max current limit in uA */
+	pval.intval = 1000 * mA;
+
+set_prop:
 	ret = power_supply_set_property(mdwc->usb_psy,
 				POWER_SUPPLY_PROP_SDP_CURRENT_MAX, &pval);
 	if (ret) {
@@ -4073,7 +4070,7 @@
 			 * which was incremented upon cable connect in
 			 * OTG_STATE_B_IDLE state
 			 */
-			pm_runtime_put_sync(mdwc->dev);
+			pm_runtime_put_sync_suspend(mdwc->dev);
 			dbg_event(0xFF, "!BSV psync",
 				atomic_read(&mdwc->dev->power.usage_count));
 			work = 1;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index ec9ffc1..4e7de00 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -599,22 +599,30 @@
 		return -EINVAL;
 
 	case USB_STATE_ADDRESS:
-		/* Read ep0IN related TXFIFO size */
-		dwc->last_fifo_depth = (dwc3_readl(dwc->regs,
-					DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
-		/* Clear existing allocated TXFIFO for all IN eps except ep0 */
-		for (num = 0; num < dwc->num_in_eps; num++) {
-			dep = dwc->eps[(num << 1) | 1];
-			if (num) {
-				dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), 0);
-				dep->fifo_depth = 0;
-			} else {
-				dep->fifo_depth = dwc->last_fifo_depth;
-			}
+		/*
+		 * If tx-fifo-resize flag is not set for the controller, then
+		 * do not clear existing allocated TXFIFO since we do not
+		 * allocate it again in dwc3_gadget_resize_tx_fifos
+		 */
+		if (dwc->needs_fifo_resize) {
+			/* Read ep0IN related TXFIFO size */
+			dwc->last_fifo_depth = (dwc3_readl(dwc->regs,
+						DWC3_GTXFIFOSIZ(0)) & 0xFFFF);
+			/* Clear existing TXFIFO for all IN eps except ep0 */
+			for (num = 0; num < dwc->num_in_eps; num++) {
+				dep = dwc->eps[(num << 1) | 1];
+				if (num) {
+					dwc3_writel(dwc->regs,
+						DWC3_GTXFIFOSIZ(num), 0);
+					dep->fifo_depth = 0;
+				} else {
+					dep->fifo_depth = dwc->last_fifo_depth;
+				}
 
-			dev_dbg(dwc->dev, "%s(): %s dep->fifo_depth:%x\n",
+				dev_dbg(dwc->dev, "%s(): %s fifo_depth:%x\n",
 					__func__, dep->name, dep->fifo_depth);
-			dbg_event(0xFF, "fifo_reset", dep->number);
+				dbg_event(0xFF, "fifo_reset", dep->number);
+			}
 		}
 
 		ret = dwc3_ep0_delegate_req(dwc, ctrl);
@@ -878,10 +886,7 @@
 
 		dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
 
-		if (r)
-			dwc3_gadget_giveback(ep0, r, -ECONNRESET);
-
-		return;
+		dbg_event(0x0, "SETUPPEND", status);
 	}
 
 	ur = &r->request;
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 9d247b8..c12fbf3 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -230,7 +230,8 @@
 	tmp = ((max_packet + mdwidth) * mult) + mdwidth;
 	fifo_size = DIV_ROUND_UP(tmp, mdwidth);
 	dep->fifo_depth = fifo_size;
-	fifo_size |= (dwc->last_fifo_depth << 16);
+	fifo_size |= (dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(0)) & 0xffff0000)
+						+ (dwc->last_fifo_depth << 16);
 	dwc->last_fifo_depth += (fifo_size & 0xffff);
 
 	dev_dbg(dwc->dev, "%s ep_num:%d last_fifo_depth:%04x fifo_depth:%d\n",
@@ -950,9 +951,42 @@
 		if (!node) {
 			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
 
+			/*
+			 * USB Specification 2.0 Section 5.9.2 states that: "If
+			 * there is only a single transaction in the microframe,
+			 * only a DATA0 data packet PID is used.  If there are
+			 * two transactions per microframe, DATA1 is used for
+			 * the first transaction data packet and DATA0 is used
+			 * for the second transaction data packet.  If there are
+			 * three transactions per microframe, DATA2 is used for
+			 * the first transaction data packet, DATA1 is used for
+			 * the second, and DATA0 is used for the third."
+			 *
+			 * IOW, we should satisfy the following cases:
+			 *
+			 * 1) length <= maxpacket
+			 *	- DATA0
+			 *
+			 * 2) maxpacket < length <= (2 * maxpacket)
+			 *	- DATA1, DATA0
+			 *
+			 * 3) (2 * maxpacket) < length <= (3 * maxpacket)
+			 *	- DATA2, DATA1, DATA0
+			 */
 			if (speed == USB_SPEED_HIGH) {
 				struct usb_ep *ep = &dep->endpoint;
-				trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1);
+				unsigned int mult = ep->mult - 1;
+				unsigned int maxp;
+
+				maxp = usb_endpoint_maxp(ep->desc) & 0x07ff;
+
+				if (length <= (2 * maxp))
+					mult--;
+
+				if (length <= maxp)
+					mult--;
+
+				trb->size |= DWC3_TRB_SIZE_PCM1(mult);
 			}
 		} else {
 			trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
@@ -1403,6 +1437,48 @@
 		if (r == req) {
 			/* wait until it is processed */
 			dwc3_stop_active_transfer(dwc, dep->number, true);
+
+			/*
+			 * If request was already started, this means we had to
+			 * stop the transfer. With that we also need to ignore
+			 * all TRBs used by the request, however TRBs can only
+			 * be modified after completion of END_TRANSFER
+			 * command. So what we do here is that we wait for
+			 * END_TRANSFER completion and only after that, we jump
+			 * over TRBs by clearing HWO and incrementing dequeue
+			 * pointer.
+			 *
+			 * Note that we have 2 possible types of transfers here:
+			 *
+			 * i) Linear buffer request
+			 * ii) SG-list based request
+			 *
+			 * SG-list based requests will have r->num_pending_sgs
+			 * set to a valid number (> 0). Linear requests,
+			 * normally use a single TRB.
+			 *
+			 * All of these cases need to be taken into
+			 * consideration so we don't mess up our TRB ring
+			 * pointers.
+			 */
+			if (!r->trb)
+				goto out1;
+
+			if (r->num_pending_sgs) {
+				struct dwc3_trb *trb;
+				int i = 0;
+
+				for (i = 0; i < r->num_pending_sgs; i++) {
+					trb = r->trb + i;
+					trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+					dwc3_ep_inc_deq(dep);
+				}
+			} else {
+				struct dwc3_trb *trb = r->trb;
+
+				trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
+				dwc3_ep_inc_deq(dep);
+			}
 			goto out1;
 		}
 		dev_err(dwc->dev, "request %pK was not queued to %s\n",
@@ -1414,6 +1490,7 @@
 out1:
 	dbg_event(dep->number, "DEQUEUE", 0);
 	/* giveback the request */
+	dep->queued_requests--;
 	dwc3_gadget_giveback(dep, req, -ECONNRESET);
 
 out0:
@@ -2256,6 +2333,10 @@
 	dwc->gadget_driver = NULL;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
+	dbg_event(0xFF, "fwq_started", 0);
+	flush_workqueue(dwc->dwc_wq);
+	dbg_event(0xFF, "fwq_completed", 0);
+
 	return 0;
 }
 
@@ -2750,43 +2831,55 @@
 
 static void dwc3_disconnect_gadget(struct dwc3 *dwc)
 {
+	struct usb_gadget_driver *gadget_driver;
+
 	if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
+		gadget_driver = dwc->gadget_driver;
 		spin_unlock(&dwc->lock);
 		dbg_event(0xFF, "DISCONNECT", 0);
-		dwc->gadget_driver->disconnect(&dwc->gadget);
+		gadget_driver->disconnect(&dwc->gadget);
 		spin_lock(&dwc->lock);
 	}
 }
 
 static void dwc3_suspend_gadget(struct dwc3 *dwc)
 {
+	struct usb_gadget_driver *gadget_driver;
+
 	if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+		gadget_driver = dwc->gadget_driver;
 		spin_unlock(&dwc->lock);
 		dbg_event(0xFF, "SUSPEND", 0);
-		dwc->gadget_driver->suspend(&dwc->gadget);
+		gadget_driver->suspend(&dwc->gadget);
 		spin_lock(&dwc->lock);
 	}
 }
 
 static void dwc3_resume_gadget(struct dwc3 *dwc)
 {
+	struct usb_gadget_driver *gadget_driver;
+
 	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
+		gadget_driver = dwc->gadget_driver;
 		spin_unlock(&dwc->lock);
 		dbg_event(0xFF, "RESUME", 0);
-		dwc->gadget_driver->resume(&dwc->gadget);
+		gadget_driver->resume(&dwc->gadget);
 		spin_lock(&dwc->lock);
 	}
 }
 
 static void dwc3_reset_gadget(struct dwc3 *dwc)
 {
+	struct usb_gadget_driver *gadget_driver;
+
 	if (!dwc->gadget_driver)
 		return;
 
 	if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+		gadget_driver = dwc->gadget_driver;
 		spin_unlock(&dwc->lock);
 		dbg_event(0xFF, "UDC RESET", 0);
-		usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
+		usb_gadget_udc_reset(&dwc->gadget, gadget_driver);
 		spin_lock(&dwc->lock);
 	}
 }
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index af3ce4f..3f79aa4 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -52,14 +52,16 @@
 	return irq;
 }
 
+#define NUMBER_OF_PROPS	4
 int dwc3_host_init(struct dwc3 *dwc)
 {
-	struct property_entry	props[3];
+	struct property_entry	props[NUMBER_OF_PROPS];
 	struct platform_device	*xhci;
 	int			ret, irq;
 	struct resource		*res;
 	struct platform_device	*dwc3_pdev = to_platform_device(dwc->dev);
 	int			prop_idx = 0;
+	struct property_entry	imod_prop;
 
 	irq = dwc3_host_get_irq(dwc);
 	if (irq < 0)
@@ -101,6 +103,15 @@
 	if (dwc->usb3_lpm_capable)
 		props[prop_idx++].name = "usb3-lpm-capable";
 
+	if (dwc->xhci_imod_value) {
+		imod_prop.name  = "xhci-imod-value";
+		imod_prop.length  = sizeof(u32);
+		imod_prop.is_string = false;
+		imod_prop.is_array = false;
+		imod_prop.value.u32_data = dwc->xhci_imod_value;
+		props[prop_idx++] = imod_prop;
+	}
+
 	/**
 	 * WORKAROUND: dwc3 revisions <=3.00a have a limitation
 	 * where Port Disable command doesn't work.
@@ -126,7 +137,11 @@
 	phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
 			  dev_name(dwc->dev));
 
-	/* Platform device gets added as part of state machine */
+	ret = platform_device_add(xhci);
+	if (ret) {
+		dev_err(dwc->dev, "failed to register xHCI device\n");
+		goto err1;
+	}
 
 	return 0;
 err1:
@@ -140,6 +155,5 @@
 			  dev_name(dwc->dev));
 	phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
 			  dev_name(dwc->dev));
-	if (!dwc->is_drd)
-		platform_device_unregister(dwc->xhci);
+	platform_device_unregister(dwc->xhci);
 }
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 8a6ae0b..98509f2 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2013,8 +2013,9 @@
 				if (value < 0) {
 					DBG(cdev, "ep_queue --> %d\n", value);
 					req->status = 0;
-					composite_setup_complete(gadget->ep0,
-								 req);
+					if (value != -ESHUTDOWN)
+						composite_setup_complete(
+							gadget->ep0, req);
 				}
 			}
 			return value;
@@ -2108,7 +2109,8 @@
 		if (value < 0) {
 			DBG(cdev, "ep_queue --> %d\n", value);
 			req->status = 0;
-			composite_setup_complete(gadget->ep0, req);
+			if (value != -ESHUTDOWN)
+				composite_setup_complete(gadget->ep0, req);
 		}
 	} else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
 		WARN(cdev,
@@ -2155,13 +2157,15 @@
 	struct usb_gadget *gadget = dev_to_usb_gadget(dev);
 	struct usb_composite_dev *cdev = get_gadget_data(gadget);
 
-	return sprintf(buf, "%d\n", cdev->suspended);
+	return snprintf(buf, PAGE_SIZE, "%d\n", cdev->suspended);
 }
 static DEVICE_ATTR_RO(suspended);
 
 static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
 {
 	struct usb_composite_dev	*cdev = get_gadget_data(gadget);
+	struct usb_gadget_strings	*gstr = cdev->driver->strings[0];
+	struct usb_string		*dev_str = gstr->strings;
 
 	/* composite_disconnect() must already have been called
 	 * by the underlying peripheral controller driver!
@@ -2181,6 +2185,9 @@
 
 	composite_dev_cleanup(cdev);
 
+	if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
+		dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";
+
 	kfree(cdev->def_manufacturer);
 	kfree(cdev);
 	set_gadget_data(gadget, NULL);
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index f910990..f915e55 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -14,11 +14,16 @@
 #include <linux/kdev_t.h>
 #include <linux/usb/ch9.h>
 
+#ifdef CONFIG_USB_F_NCM
+#include <function/u_ncm.h>
+#endif
+
 #ifdef CONFIG_USB_CONFIGFS_F_ACC
 extern int acc_ctrlrequest(struct usb_composite_dev *cdev,
 				const struct usb_ctrlrequest *ctrl);
 void acc_disconnect(void);
 #endif
+
 static struct class *android_class;
 static struct device *android_device;
 static int index;
@@ -84,6 +89,7 @@
 	struct usb_composite_driver composite;
 	struct usb_composite_dev cdev;
 	bool use_os_desc;
+	bool unbinding;
 	char b_vendor_code;
 	char qw_sign[OS_STRING_QW_SIGN_LEN];
 #ifdef CONFIG_USB_CONFIGFS_UEVENT
@@ -281,9 +287,12 @@
 	if (!gi->composite.gadget_driver.udc_name)
 		return -ENODEV;
 
+	gi->unbinding = true;
 	ret = usb_gadget_unregister_driver(&gi->composite.gadget_driver);
 	if (ret)
 		return ret;
+
+	gi->unbinding = false;
 	kfree(gi->composite.gadget_driver.udc_name);
 	gi->composite.gadget_driver.udc_name = NULL;
 	return 0;
@@ -1179,11 +1188,12 @@
 	NULL
 };
 
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
-				   int n_interf,
-				   struct usb_os_desc **desc,
-				   char **names,
-				   struct module *owner)
+struct config_group *usb_os_desc_prepare_interf_dir(
+		struct config_group *parent,
+		int n_interf,
+		struct usb_os_desc **desc,
+		char **names,
+		struct module *owner)
 {
 	struct config_group *os_desc_group;
 	struct config_item_type *os_desc_type, *interface_type;
@@ -1195,7 +1205,7 @@
 
 	char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
 	if (!vlabuf)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 
 	os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
 	os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
@@ -1220,7 +1230,7 @@
 		configfs_add_default_group(&d->group, os_desc_group);
 	}
 
-	return 0;
+	return os_desc_group;
 }
 EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
 
@@ -1503,6 +1513,18 @@
 		}
 	}
 
+#ifdef CONFIG_USB_F_NCM
+	if (value < 0)
+		value = ncm_ctrlrequest(cdev, c);
+
+	/*
+	 * for mirror link command case, if it already been handled,
+	 * do not pass to composite_setup
+	 */
+	if (value == 0)
+		return value;
+#endif
+
 #ifdef CONFIG_USB_CONFIGFS_F_ACC
 	if (value < 0)
 		value = acc_ctrlrequest(cdev, c);
@@ -1533,6 +1555,18 @@
 
 	gi = container_of(cdev, struct gadget_info, cdev);
 
+	/* FIXME: There's a race between usb_gadget_udc_stop() which is likely
+	 * to set the gadget driver to NULL in the udc driver and this drivers
+	 * gadget disconnect fn which likely checks for the gadget driver to
+	 * be a null ptr. It happens that unbind (doing set_gadget_data(NULL))
+	 * is called before the gadget driver is set to NULL and the udc driver
+	 * calls disconnect fn which results in cdev being a null ptr.
+	 */
+	if (cdev == NULL) {
+		WARN(1, "%s: gadget driver already disconnected\n", __func__);
+		return;
+	}
+
 	/* accessory HID support can be active while the
 		accessory function is not actually enabled,
 		so we need to inform it when we are disconnected.
@@ -1542,7 +1576,8 @@
 	acc_disconnect();
 #endif
 	gi->connected = 0;
-	schedule_work(&gi->work);
+	if (!gi->unbinding)
+		schedule_work(&gi->work);
 	composite_disconnect(gadget);
 }
 #endif
diff --git a/drivers/usb/gadget/configfs.h b/drivers/usb/gadget/configfs.h
index 36c468c..540d5e9 100644
--- a/drivers/usb/gadget/configfs.h
+++ b/drivers/usb/gadget/configfs.h
@@ -5,11 +5,12 @@
 
 void unregister_gadget_item(struct config_item *item);
 
-int usb_os_desc_prepare_interf_dir(struct config_group *parent,
-				   int n_interf,
-				   struct usb_os_desc **desc,
-				   char **names,
-				   struct module *owner);
+struct config_group *usb_os_desc_prepare_interf_dir(
+		struct config_group *parent,
+		int n_interf,
+		struct usb_os_desc **desc,
+		char **names,
+		struct module *owner);
 
 static inline struct usb_os_desc *to_usb_os_desc(struct config_item *item)
 {
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index a7cb586..9240956 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -864,6 +864,14 @@
 	.probe = acc_hid_probe,
 };
 
+static void acc_complete_setup_noop(struct usb_ep *ep, struct usb_request *req)
+{
+	/*
+	 * Default no-op function when nothing needs to be done for the
+	 * setup request
+	 */
+}
+
 int acc_ctrlrequest(struct usb_composite_dev *cdev,
 				const struct usb_ctrlrequest *ctrl)
 {
@@ -891,6 +899,7 @@
 			schedule_delayed_work(
 				&dev->start_work, msecs_to_jiffies(10));
 			value = 0;
+			cdev->req->complete = acc_complete_setup_noop;
 		} else if (b_request == ACCESSORY_SEND_STRING) {
 			dev->string_index = w_index;
 			cdev->gadget->ep0->driver_data = dev;
@@ -899,10 +908,13 @@
 		} else if (b_request == ACCESSORY_SET_AUDIO_MODE &&
 				w_index == 0 && w_length == 0) {
 			dev->audio_mode = w_value;
+			cdev->req->complete = acc_complete_setup_noop;
 			value = 0;
 		} else if (b_request == ACCESSORY_REGISTER_HID) {
+			cdev->req->complete = acc_complete_setup_noop;
 			value = acc_register_hid(dev, w_value, w_index);
 		} else if (b_request == ACCESSORY_UNREGISTER_HID) {
+			cdev->req->complete = acc_complete_setup_noop;
 			value = acc_unregister_hid(dev, w_value);
 		} else if (b_request == ACCESSORY_SET_HID_REPORT_DESC) {
 			spin_lock_irqsave(&dev->lock, flags);
@@ -937,7 +949,7 @@
 		if (b_request == ACCESSORY_GET_PROTOCOL) {
 			*((u16 *)cdev->req->buf) = PROTOCOL_VERSION;
 			value = sizeof(u16);
-
+			cdev->req->complete = acc_complete_setup_noop;
 			/* clear any string left over from a previous session */
 			memset(dev->manufacturer, 0, sizeof(dev->manufacturer));
 			memset(dev->model, 0, sizeof(dev->model));
diff --git a/drivers/usb/gadget/function/f_audio_source.c b/drivers/usb/gadget/function/f_audio_source.c
index 1590927..9f7a29a 100644
--- a/drivers/usb/gadget/function/f_audio_source.c
+++ b/drivers/usb/gadget/function/f_audio_source.c
@@ -755,11 +755,11 @@
 	struct audio_dev *audio = substream->private_data;
 	unsigned long flags;
 
-	spin_lock_irqsave(&audio->lock, flags);
-
 	/* Remove the QoS request */
 	pm_qos_remove_request(&audio->pm_qos);
 
+	spin_lock_irqsave(&audio->lock, flags);
+
 	audio->substream = NULL;
 	spin_unlock_irqrestore(&audio->lock, flags);
 
diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c
index 7817f1c..dbf72ab 100644
--- a/drivers/usb/gadget/function/f_ccid.c
+++ b/drivers/usb/gadget/function/f_ccid.c
@@ -206,6 +206,71 @@
 	NULL,
 };
 
+/* Super speed support: */
+static struct usb_endpoint_descriptor ccid_ss_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		CCID_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_notify_comp_desc = {
+	.bLength =		sizeof(ccid_ss_notify_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor ccid_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_in_comp_desc = {
+	.bLength =		sizeof(ccid_ss_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor ccid_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ccid_ss_out_comp_desc = {
+	.bLength =		sizeof(ccid_ss_out_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_descriptor_header *ccid_ss_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_ss_notify_desc,
+	(struct usb_descriptor_header *) &ccid_ss_notify_comp_desc,
+	(struct usb_descriptor_header *) &ccid_ss_in_desc,
+	(struct usb_descriptor_header *) &ccid_ss_in_comp_desc,
+	(struct usb_descriptor_header *) &ccid_ss_out_desc,
+	(struct usb_descriptor_header *) &ccid_ss_out_comp_desc,
+	NULL,
+};
+
 static inline struct f_ccid *func_to_ccid(struct usb_function *f)
 {
 	return container_of(f, struct f_ccid, function);
@@ -503,10 +568,7 @@
 static void ccid_function_unbind(struct usb_configuration *c,
 					struct usb_function *f)
 {
-	if (gadget_is_dualspeed(c->cdev->gadget))
-		usb_free_descriptors(f->hs_descriptors);
-	usb_free_descriptors(f->fs_descriptors);
-
+	usb_free_all_descriptors(f);
 }
 
 static int ccid_function_bind(struct usb_configuration *c,
@@ -551,24 +613,27 @@
 	ccid_dev->out = ep;
 	ep->driver_data = cdev;
 
-	f->fs_descriptors = usb_copy_descriptors(ccid_fs_descs);
-	if (!f->fs_descriptors)
+	/*
+	 * support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	ccid_hs_in_desc.bEndpointAddress = ccid_fs_in_desc.bEndpointAddress;
+	ccid_hs_out_desc.bEndpointAddress = ccid_fs_out_desc.bEndpointAddress;
+	ccid_hs_notify_desc.bEndpointAddress =
+					ccid_fs_notify_desc.bEndpointAddress;
+
+
+	ccid_ss_in_desc.bEndpointAddress = ccid_fs_in_desc.bEndpointAddress;
+	ccid_ss_out_desc.bEndpointAddress = ccid_fs_out_desc.bEndpointAddress;
+	ccid_ss_notify_desc.bEndpointAddress =
+					ccid_fs_notify_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, ccid_fs_descs, ccid_hs_descs,
+						ccid_ss_descs, NULL);
+	if (ret)
 		goto ep_auto_out_fail;
 
-	if (gadget_is_dualspeed(cdev->gadget)) {
-		ccid_hs_in_desc.bEndpointAddress =
-				ccid_fs_in_desc.bEndpointAddress;
-		ccid_hs_out_desc.bEndpointAddress =
-				ccid_fs_out_desc.bEndpointAddress;
-		ccid_hs_notify_desc.bEndpointAddress =
-				ccid_fs_notify_desc.bEndpointAddress;
-
-		/* copy descriptors, and track endpoint copies */
-		f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs);
-		if (!f->hs_descriptors)
-			goto ep_auto_out_fail;
-	}
-
 	pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__,
 			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
 			ccid_dev->in->name, ccid_dev->out->name);
@@ -972,6 +1037,7 @@
 	ccid_dev->function.name = FUNCTION_NAME;
 	ccid_dev->function.fs_descriptors = ccid_fs_descs;
 	ccid_dev->function.hs_descriptors = ccid_hs_descs;
+	ccid_dev->function.ss_descriptors = ccid_ss_descs;
 	ccid_dev->function.bind = ccid_function_bind;
 	ccid_dev->function.unbind = ccid_function_unbind;
 	ccid_dev->function.set_alt = ccid_function_set_alt;
diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h
index 42a7ebb..935308c 100644
--- a/drivers/usb/gadget/function/f_ccid.h
+++ b/drivers/usb/gadget/function/f_ccid.h
@@ -55,29 +55,29 @@
 #define CCID_READ_DTR		_IOR('C', 3, int)
 
 struct usb_ccid_notification {
-	unsigned char buf[4];
+	__u8 buf[4];
 } __packed;
 
 struct ccid_bulk_in_header {
-	unsigned char bMessageType;
-	unsigned long wLength;
-	unsigned char bSlot;
-	unsigned char bSeq;
-	unsigned char bStatus;
-	unsigned char bError;
-	unsigned char bSpecific;
-	unsigned char abData[ABDATA_SIZE];
-	unsigned char bSizeToSend;
+	__u8 bMessageType;
+	__u32 wLength;
+	__u8 bSlot;
+	__u8 bSeq;
+	__u8 bStatus;
+	__u8 bError;
+	__u8 bSpecific;
+	__u8 abData[ABDATA_SIZE];
+	__u8 bSizeToSend;
 } __packed;
 
 struct ccid_bulk_out_header {
-	unsigned char bMessageType;
-	unsigned long wLength;
-	unsigned char bSlot;
-	unsigned char bSeq;
-	unsigned char bSpecific_0;
-	unsigned char bSpecific_1;
-	unsigned char bSpecific_2;
-	unsigned char APDU[ABDATA_SIZE];
+	__u8 bMessageType;
+	__u32 wLength;
+	__u8 bSlot;
+	__u8 bSeq;
+	__u8 bSpecific_0;
+	__u8 bSpecific_1;
+	__u8 bSpecific_2;
+	__u8 APDU[ABDATA_SIZE];
 } __packed;
 #endif
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c
index e908ecf..be22de048 100644
--- a/drivers/usb/gadget/function/f_diag.c
+++ b/drivers/usb/gadget/function/f_diag.c
@@ -386,9 +386,11 @@
 	ch->priv = priv;
 	ch->notify = notify;
 
-	spin_lock_irqsave(&ch_lock, flags);
-	list_add_tail(&ch->list, &usb_diag_ch_list);
-	spin_unlock_irqrestore(&ch_lock, flags);
+	if (!found) {
+		spin_lock_irqsave(&ch_lock, flags);
+		list_add_tail(&ch->list, &usb_diag_ch_list);
+		spin_unlock_irqrestore(&ch_lock, flags);
+	}
 
 	return ch;
 }
@@ -863,6 +865,7 @@
 	struct diag_context *dev;
 	struct usb_diag_ch *_ch;
 	int found = 0;
+	unsigned long flags;
 
 	pr_debug("%s\n", __func__);
 
@@ -872,9 +875,19 @@
 			break;
 		}
 	}
+
 	if (!found) {
-		pr_err("%s: unable to get diag usb channel\n", __func__);
-		return ERR_PTR(-ENODEV);
+		pr_warn("%s: unable to get diag usb channel\n", __func__);
+
+		_ch = kzalloc(sizeof(*_ch), GFP_KERNEL);
+		if (_ch == NULL)
+			return ERR_PTR(-ENOMEM);
+
+		_ch->name = name;
+
+		spin_lock_irqsave(&ch_lock, flags);
+		list_add_tail(&_ch->list, &usb_diag_ch_list);
+		spin_unlock_irqrestore(&ch_lock, flags);
 	}
 
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index ccf9dac..a26d6df 100644
--- a/drivers/usb/gadget/function/f_gsi.c
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -19,7 +19,14 @@
 MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
 
 static struct workqueue_struct *ipa_usb_wq;
+static struct gsi_inst_status {
+	struct mutex gsi_lock;
+	bool inst_exist;
+	struct gsi_opts *opts;
+} inst_status[IPA_USB_MAX_TETH_PROT_SIZE];
 
+/* Deregister misc device and free instance structures */
+static void gsi_inst_clean(struct gsi_opts *opts);
 static void gsi_rndis_ipa_reset_trigger(struct gsi_data_port *d_port);
 static void ipa_disconnect_handler(struct gsi_data_port *d_port);
 static int gsi_ctrl_send_notification(struct f_gsi *gsi);
@@ -919,40 +926,69 @@
 	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
 						struct gsi_ctrl_port,
 						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct f_gsi *gsi;
+	struct gsi_inst_status *inst_cur;
 
 	if (!c_port) {
-		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		pr_err_ratelimited("%s: gsi ctrl port %p", __func__, c_port);
 		return -ENODEV;
 	}
 
+	gsi = container_of(c_port, struct f_gsi, c_port);
+	inst_cur = &inst_status[gsi->prot_id];
 	log_event_dbg("%s: open ctrl dev %s", __func__, c_port->name);
 
+	mutex_lock(&inst_cur->gsi_lock);
+
+	fp->private_data = &gsi->prot_id;
+
+	if (!inst_cur->inst_exist) {
+		mutex_unlock(&inst_cur->gsi_lock);
+		log_event_err("%s: [prot_id = %d], GSI instance freed already\n",
+				__func__, gsi->prot_id);
+		return -ENODEV;
+	}
+
 	if (c_port->is_open) {
-		log_event_err("%s: Already opened", __func__);
+		mutex_unlock(&inst_cur->gsi_lock);
+		log_event_err("%s: Already opened\n", __func__);
 		return -EBUSY;
 	}
 
 	c_port->is_open = true;
 
+	mutex_unlock(&inst_cur->gsi_lock);
+
 	return 0;
 }
 
 static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
 {
-	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
-						struct gsi_ctrl_port,
-						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	enum ipa_usb_teth_prot prot_id =
+		*(enum ipa_usb_teth_prot *)(fp->private_data);
+	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+	struct f_gsi *gsi;
 
-	if (!c_port) {
-		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+	mutex_lock(&inst_cur->gsi_lock);
+
+	if (unlikely(inst_cur->inst_exist == false)) {
+		if (inst_cur->opts) {
+			/* GSI instance clean up */
+			gsi_inst_clean(inst_cur->opts);
+			inst_cur->opts = NULL;
+		}
+		mutex_unlock(&inst_cur->gsi_lock);
+		pr_err_ratelimited("%s: prot_id:%d: delayed free memory\n",
+			__func__, prot_id);
 		return -ENODEV;
 	}
 
-	log_event_dbg("close ctrl dev %s", c_port->name);
+	inst_cur->opts->gsi->c_port.is_open = false;
+	gsi = inst_cur->opts->gsi;
+	mutex_unlock(&inst_cur->gsi_lock);
 
-	c_port->is_open = false;
+	log_event_dbg("close ctrl dev %s\n",
+			inst_cur->opts->gsi->c_port.name);
 
 	return 0;
 }
@@ -960,16 +996,28 @@
 static ssize_t
 gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
 {
-	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
-						struct gsi_ctrl_port,
-						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct gsi_ctrl_port *c_port;
 	struct gsi_ctrl_pkt *cpkt = NULL;
+	enum ipa_usb_teth_prot prot_id =
+		*(enum ipa_usb_teth_prot *)(fp->private_data);
+	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+	struct f_gsi *gsi;
 	unsigned long flags;
 	int ret = 0;
 
-	log_event_dbg("%s: Enter %zu", __func__, count);
+	pr_debug("%s: Enter %zu", __func__, count);
 
+	mutex_lock(&inst_cur->gsi_lock);
+	if (unlikely(inst_cur->inst_exist == false)) {
+		mutex_unlock(&inst_cur->gsi_lock);
+		pr_err_ratelimited("%s: free_inst is called and being freed\n",
+								__func__);
+		return -ENODEV;
+	}
+	mutex_unlock(&inst_cur->gsi_lock);
+
+	gsi = inst_cur->opts->gsi;
+	c_port = &inst_cur->opts->gsi->c_port;
 	if (!c_port) {
 		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
 		return -ENODEV;
@@ -1037,13 +1085,27 @@
 	int ret = 0;
 	unsigned long flags;
 	struct gsi_ctrl_pkt *cpkt;
-	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
-						struct gsi_ctrl_port,
-						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
-	struct usb_request *req = c_port->notify_req;
+	struct gsi_ctrl_port *c_port;
+	struct usb_request *req;
+	enum ipa_usb_teth_prot prot_id =
+		*(enum ipa_usb_teth_prot *)(fp->private_data);
+	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+	struct f_gsi *gsi;
 
-	log_event_dbg("Enter %zu", count);
+	pr_debug("Enter %zu", count);
+
+	mutex_lock(&inst_cur->gsi_lock);
+	if (unlikely(inst_cur->inst_exist == false)) {
+		mutex_unlock(&inst_cur->gsi_lock);
+		pr_err_ratelimited("%s: free_inst is called and being freed\n",
+								__func__);
+		return -ENODEV;
+	}
+	mutex_unlock(&inst_cur->gsi_lock);
+
+	gsi = inst_cur->opts->gsi;
+	c_port = &gsi->c_port;
+	req = c_port->notify_req;
 
 	if (!c_port || !req || !req->buf) {
 		log_event_err("%s: c_port %p req %p req->buf %p",
@@ -1101,15 +1163,28 @@
 static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,
 		unsigned long arg)
 {
-	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
-						struct gsi_ctrl_port,
-						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct gsi_ctrl_port *c_port;
+	struct f_gsi *gsi;
 	struct gsi_ctrl_pkt *cpkt;
 	struct ep_info info;
+	enum ipa_usb_teth_prot prot_id =
+		*(enum ipa_usb_teth_prot *)(fp->private_data);
+	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
 	int val, ret = 0;
 	unsigned long flags;
 
+	mutex_lock(&inst_cur->gsi_lock);
+	if (unlikely(inst_cur->inst_exist == false)) {
+		mutex_unlock(&inst_cur->gsi_lock);
+		pr_err_ratelimited("%s: free_inst is called and being freed\n",
+								__func__);
+		return -ENODEV;
+	}
+	mutex_unlock(&inst_cur->gsi_lock);
+
+	gsi = inst_cur->opts->gsi;
+	c_port = &gsi->c_port;
+
 	if (!c_port) {
 		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
 		return -ENODEV;
@@ -1230,13 +1305,25 @@
 
 static unsigned int gsi_ctrl_dev_poll(struct file *fp, poll_table *wait)
 {
-	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
-						struct gsi_ctrl_port,
-						ctrl_device);
-	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct gsi_ctrl_port *c_port;
+	enum ipa_usb_teth_prot prot_id =
+		*(enum ipa_usb_teth_prot *)(fp->private_data);
+	struct gsi_inst_status *inst_cur = &inst_status[prot_id];
+	struct f_gsi *gsi;
 	unsigned long flags;
 	unsigned int mask = 0;
 
+	mutex_lock(&inst_cur->gsi_lock);
+	if (unlikely(inst_cur->inst_exist == false)) {
+		mutex_unlock(&inst_cur->gsi_lock);
+		pr_err_ratelimited("%s: free_inst is called and being freed\n",
+								__func__);
+		return -ENODEV;
+	}
+	mutex_unlock(&inst_cur->gsi_lock);
+
+	gsi = inst_cur->opts->gsi;
+	c_port = &inst_cur->opts->gsi->c_port;
 	if (!c_port) {
 		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
 		return -ENODEV;
@@ -1565,12 +1652,22 @@
 		struct usb_request *req)
 {
 	struct f_gsi *gsi = req->context;
+	rndis_init_msg_type *buf;
 	int status;
 
 	status = rndis_msg_parser(gsi->params, (u8 *) req->buf);
 	if (status < 0)
 		log_event_err("RNDIS command error %d, %d/%d",
 			status, req->actual, req->length);
+
+	buf = (rndis_init_msg_type *)req->buf;
+	if (buf->MessageType == RNDIS_MSG_INIT) {
+		gsi->d_port.in_aggr_size = min_t(u32, gsi->d_port.in_aggr_size,
+						gsi->params->dl_max_xfer_size);
+		log_event_dbg("RNDIS host dl_aggr_size:%d in_aggr_size:%d\n",
+				gsi->params->dl_max_xfer_size,
+				gsi->d_port.in_aggr_size);
+	}
 }
 
 static void
@@ -2618,10 +2715,10 @@
 		info.string_defs = qdss_gsi_string_defs;
 		info.data_desc = &qdss_gsi_data_intf_desc;
 		info.data_str_idx = 0;
-		info.fs_in_desc = &qdss_gsi_hs_data_desc;
+		info.fs_in_desc = &qdss_gsi_fs_data_desc;
 		info.hs_in_desc = &qdss_gsi_hs_data_desc;
 		info.ss_in_desc = &qdss_gsi_ss_data_desc;
-		info.fs_desc_hdr = qdss_gsi_hs_data_only_desc;
+		info.fs_desc_hdr = qdss_gsi_fs_data_only_desc;
 		info.hs_desc_hdr = qdss_gsi_hs_data_only_desc;
 		info.ss_desc_hdr = qdss_gsi_ss_data_only_desc;
 		info.in_epname = "gsi-epin";
@@ -2669,8 +2766,12 @@
 			f->name,
 			gadget_is_superspeed(c->cdev->gadget) ? "super" :
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
-			gsi->d_port.in_ep->name, gsi->d_port.out_ep->name,
-			gsi->c_port.notify->name);
+			(gsi->d_port.in_ep == NULL ? "NULL" :
+					gsi->d_port.in_ep->name),
+			(gsi->d_port.out_ep == NULL ? "NULL" :
+					gsi->d_port.out_ep->name),
+			(gsi->c_port.notify == NULL ? "NULL" :
+					gsi->c_port.notify->name));
 	return 0;
 
 dereg_rndis:
@@ -2817,7 +2918,11 @@
 static void gsi_opts_release(struct config_item *item)
 {
 	struct gsi_opts *opts = to_gsi_opts(item);
+	struct f_gsi *gsi;
 
+	gsi = opts->gsi;
+	log_event_dbg("%s: releasing %s instance\n",
+			__func__, gsi->function.name);
 	usb_put_function_instance(&opts->func_inst);
 }
 
@@ -2981,26 +3086,52 @@
 	.ct_owner	= THIS_MODULE,
 };
 
+static void gsi_inst_clean(struct gsi_opts *opts)
+{
+	if (opts->gsi->c_port.ctrl_device.fops)
+		misc_deregister(&opts->gsi->c_port.ctrl_device);
+
+	kfree(opts->gsi);
+	kfree(opts);
+}
+
 static int gsi_set_inst_name(struct usb_function_instance *fi,
 	const char *name)
 {
-	int ret, name_len;
+	int prot_id, name_len;
 	struct f_gsi *gsi;
 	char gsi_inst_name[MAX_INST_NAME_LEN + sizeof("gsi.") + 1];
-	struct gsi_opts *opts = container_of(fi, struct gsi_opts, func_inst);
 	void *ipc_log_ctxt;
+	struct gsi_opts *opts, *opts_prev;
+
+	opts = container_of(fi, struct gsi_opts, func_inst);
 
 	name_len = strlen(name) + 1;
 	if (name_len > MAX_INST_NAME_LEN)
 		return -ENAMETOOLONG;
 
-	ret = name_to_prot_id(name);
-	if (ret < 0) {
+	prot_id = name_to_prot_id(name);
+	if (prot_id < 0) {
 		pr_err("%s: failed to find prot id for %s instance\n",
-		__func__, name);
+						__func__, name);
 		return -EINVAL;
 	}
 
+	mutex_lock(&inst_status[prot_id].gsi_lock);
+	opts_prev = inst_status[prot_id].opts;
+	if (opts_prev) {
+		mutex_unlock(&inst_status[prot_id].gsi_lock);
+		pr_err("%s: prot_id = %d, prev inst do not freed yet\n",
+						__func__, prot_id);
+		return -EBUSY;
+	}
+	mutex_unlock(&inst_status[prot_id].gsi_lock);
+
+	gsi = gsi_function_init(prot_id);
+	if (IS_ERR(gsi))
+		return PTR_ERR(gsi);
+
+	opts->gsi = gsi;
 	/*
 	 * create instance name with prefixing "gsi." to differentiate
 	 * ipc log debugfs entry
@@ -3010,31 +3141,45 @@
 	if (!ipc_log_ctxt)
 		pr_err("%s: Err allocating ipc_log_ctxt for prot:%s\n",
 						__func__, gsi_inst_name);
-
-	gsi = gsi_function_init(ret);
-	if (IS_ERR(gsi)) {
-		ipc_log_context_destroy(ipc_log_ctxt);
-		return PTR_ERR(gsi);
-	}
-
-	opts->gsi = gsi;
 	opts->gsi->ipc_log_ctxt = ipc_log_ctxt;
+
+	/* Set instance status */
+	mutex_lock(&inst_status[prot_id].gsi_lock);
+	inst_status[prot_id].inst_exist = true;
+	inst_status[prot_id].opts = opts;
+	mutex_unlock(&inst_status[prot_id].gsi_lock);
+
 	return 0;
 }
 
 static void gsi_free_inst(struct usb_function_instance *f)
 {
 	struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
+	enum ipa_usb_teth_prot prot_id;
+	struct f_gsi *gsi;
 
 	if (!opts->gsi)
 		return;
 
-	if (opts->gsi->c_port.ctrl_device.fops)
-		misc_deregister(&opts->gsi->c_port.ctrl_device);
+	prot_id = opts->gsi->prot_id;
+	gsi = opts->gsi;
+	mutex_lock(&inst_status[prot_id].gsi_lock);
+	if (opts->gsi->c_port.is_open) {
+		/* Mark instance exist as false */
+		inst_status[prot_id].inst_exist = false;
+		mutex_unlock(&inst_status[prot_id].gsi_lock);
+		log_event_err(
+			"%s: [prot_id = %d] Dev is open, free mem when dev close\n",
+			__func__, prot_id);
+		return;
+	}
 
 	ipc_log_context_destroy(opts->gsi->ipc_log_ctxt);
-	kfree(opts->gsi);
-	kfree(opts);
+	/* Clear instance status */
+	gsi_inst_clean(opts);
+	inst_status[prot_id].inst_exist = false;
+	inst_status[prot_id].opts = NULL;
+	mutex_unlock(&inst_status[prot_id].gsi_lock);
 }
 
 static struct usb_function_instance *gsi_alloc_inst(void)
@@ -3048,7 +3193,7 @@
 	opts->func_inst.set_inst_name = gsi_set_inst_name;
 	opts->func_inst.free_func_inst = gsi_free_inst;
 	config_group_init_type_name(&opts->func_inst.group, "",
-				    &gsi_func_type);
+					&gsi_func_type);
 
 	return &opts->func_inst;
 }
@@ -3073,6 +3218,8 @@
 
 static int fgsi_init(void)
 {
+	int i;
+
 	ipa_usb_wq = alloc_workqueue("k_ipa_usb",
 				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
 	if (!ipa_usb_wq) {
@@ -3080,6 +3227,9 @@
 		return -ENOMEM;
 	}
 
+	for (i = 0; i < IPA_USB_MAX_TETH_PROT_SIZE; i++)
+		mutex_init(&inst_status[i].gsi_lock);
+
 	return usb_function_register(&gsiusb_func);
 }
 module_init(fgsi_init);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
index 0fe3665..bdd0dfa 100644
--- a/drivers/usb/gadget/function/f_gsi.h
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -586,7 +586,7 @@
 };
 
 static struct usb_descriptor_header *gsi_eth_fs_function[] = {
-	(struct usb_descriptor_header *) &gsi_eth_fs_function,
+	(struct usb_descriptor_header *) &rndis_gsi_iad_descriptor,
 	/* control interface matches ACM, not Ethernet */
 	(struct usb_descriptor_header *) &rndis_gsi_control_intf,
 	(struct usb_descriptor_header *) &rndis_gsi_header_desc,
@@ -1340,6 +1340,14 @@
 	.bInterfaceProtocol =	0xff,
 };
 
+static struct usb_endpoint_descriptor qdss_gsi_fs_data_desc = {
+	.bLength              =	 USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType      =	 USB_DT_ENDPOINT,
+	.bEndpointAddress     =	 USB_DIR_IN,
+	.bmAttributes         =	 USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize       =	 cpu_to_le16(64),
+};
+
 static struct usb_endpoint_descriptor qdss_gsi_hs_data_desc = {
 	.bLength              =	 USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType      =	 USB_DT_ENDPOINT,
@@ -1364,6 +1372,12 @@
 	.wBytesPerInterval    =	 0,
 };
 
+static struct usb_descriptor_header *qdss_gsi_fs_data_only_desc[] = {
+	(struct usb_descriptor_header *) &qdss_gsi_data_intf_desc,
+	(struct usb_descriptor_header *) &qdss_gsi_fs_data_desc,
+	NULL,
+};
+
 static struct usb_descriptor_header *qdss_gsi_hs_data_only_desc[] = {
 	(struct usb_descriptor_header *) &qdss_gsi_data_intf_desc,
 	(struct usb_descriptor_header *) &qdss_gsi_hs_data_desc,
@@ -1379,7 +1393,7 @@
 
 /* string descriptors: */
 static struct usb_string qdss_gsi_string_defs[] = {
-	[0].s = "QDSS DATA",
+	[0].s = "DPL Data",
 	{}, /* end of list */
 };
 
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 0b758236..33ed64f 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -306,8 +306,6 @@
 	struct completion	thread_notifier;
 	struct task_struct	*thread_task;
 
-	/* Callback functions. */
-	const struct fsg_operations	*ops;
 	/* Gadget's private data. */
 	void			*private_data;
 
@@ -2540,6 +2538,7 @@
 static int fsg_main_thread(void *common_)
 {
 	struct fsg_common	*common = common_;
+	int			i;
 
 	/*
 	 * Allow the thread to be killed by a signal, but set the signal mask
@@ -2601,21 +2600,16 @@
 	common->thread_task = NULL;
 	spin_unlock_irq(&common->lock);
 
-	if (!common->ops || !common->ops->thread_exits
-	 || common->ops->thread_exits(common) < 0) {
-		int i;
+	/* Eject media from all LUNs */
 
-		down_write(&common->filesem);
-		for (i = 0; i < ARRAY_SIZE(common->luns); --i) {
-			struct fsg_lun *curlun = common->luns[i];
-			if (!curlun || !fsg_lun_is_open(curlun))
-				continue;
+	down_write(&common->filesem);
+	for (i = 0; i < ARRAY_SIZE(common->luns); i++) {
+		struct fsg_lun *curlun = common->luns[i];
 
+		if (curlun && fsg_lun_is_open(curlun))
 			fsg_lun_close(curlun);
-			curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
-		}
-		up_write(&common->filesem);
 	}
+	up_write(&common->filesem);
 
 	/* Let fsg_unbind() know the thread has exited */
 	complete_and_exit(&common->thread_notifier, 0);
@@ -2805,13 +2799,6 @@
 }
 EXPORT_SYMBOL_GPL(fsg_common_remove_luns);
 
-void fsg_common_set_ops(struct fsg_common *common,
-			const struct fsg_operations *ops)
-{
-	common->ops = ops;
-}
-EXPORT_SYMBOL_GPL(fsg_common_set_ops);
-
 void fsg_common_free_buffers(struct fsg_common *common)
 {
 	_fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers);
diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index d390231..dc05ca0 100644
--- a/drivers/usb/gadget/function/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
@@ -60,17 +60,6 @@
 struct fsg_common;
 
 /* FSF callback functions */
-struct fsg_operations {
-	/*
-	 * Callback function to call when thread exits.  If no
-	 * callback is set or it returns value lower then zero MSF
-	 * will force eject all LUNs it operates on (including those
-	 * marked as non-removable or with prevent_medium_removal flag
-	 * set).
-	 */
-	int (*thread_exits)(struct fsg_common *common);
-};
-
 struct fsg_lun_opts {
 	struct config_group group;
 	struct fsg_lun *lun;
@@ -142,9 +131,6 @@
 
 void fsg_common_remove_luns(struct fsg_common *common);
 
-void fsg_common_set_ops(struct fsg_common *common,
-			const struct fsg_operations *ops);
-
 int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg,
 			  unsigned int id, const char *name,
 			  const char **name_pfx);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index d2fbed7..98e353d 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1605,10 +1605,57 @@
 	.ct_owner	= THIS_MODULE,
 };
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+
+struct ncm_setup_desc {
+	struct work_struct work;
+	struct device *device;
+	uint8_t major; // Mirror Link major version
+	uint8_t minor; // Mirror Link minor version
+};
+
+static struct ncm_setup_desc *_ncm_setup_desc;
+
+#define MIRROR_LINK_STRING_LENGTH_MAX 32
+static void ncm_setup_work(struct work_struct *data)
+{
+	char mirror_link_string[MIRROR_LINK_STRING_LENGTH_MAX];
+	char *envp[2] = { mirror_link_string, NULL };
+
+	snprintf(mirror_link_string, MIRROR_LINK_STRING_LENGTH_MAX,
+		"MirrorLink=V%d.%d",
+		_ncm_setup_desc->major, _ncm_setup_desc->minor);
+	kobject_uevent_env(&_ncm_setup_desc->device->kobj, KOBJ_CHANGE, envp);
+}
+
+int ncm_ctrlrequest(struct usb_composite_dev *cdev,
+			const struct usb_ctrlrequest *ctrl)
+{
+	int value = -EOPNOTSUPP;
+
+	if (ctrl->bRequestType == 0x40 && ctrl->bRequest == 0xF0) {
+		_ncm_setup_desc->minor = (uint8_t)(ctrl->wValue >> 8);
+		_ncm_setup_desc->major = (uint8_t)(ctrl->wValue & 0xFF);
+		schedule_work(&_ncm_setup_desc->work);
+		value = 0;
+	}
+
+	return value;
+}
+#endif
+
 static void ncm_free_inst(struct usb_function_instance *f)
 {
 	struct f_ncm_opts *opts;
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	/* release _ncm_setup_desc related resource */
+	device_destroy(_ncm_setup_desc->device->class,
+		_ncm_setup_desc->device->devt);
+	cancel_work(&_ncm_setup_desc->work);
+	kfree(_ncm_setup_desc);
+#endif
+
 	opts = container_of(f, struct f_ncm_opts, func_inst);
 	if (opts->bound)
 		gether_cleanup(netdev_priv(opts->net));
@@ -1627,6 +1674,14 @@
 
 	config_group_init_type_name(&opts->func_inst.group, "", &ncm_func_type);
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+	_ncm_setup_desc = kzalloc(sizeof(*_ncm_setup_desc), GFP_KERNEL);
+	if (!_ncm_setup_desc)
+		return ERR_PTR(-ENOMEM);
+	INIT_WORK(&_ncm_setup_desc->work, ncm_setup_work);
+	_ncm_setup_desc->device = create_function_device("f_ncm");
+#endif
+
 	return &opts->func_inst;
 }
 
diff --git a/drivers/usb/gadget/function/f_qdss.c b/drivers/usb/gadget/function/f_qdss.c
index 40a7acf..a0fecb2 100644
--- a/drivers/usb/gadget/function/f_qdss.c
+++ b/drivers/usb/gadget/function/f_qdss.c
@@ -338,7 +338,7 @@
 	struct usb_gadget *gadget = c->cdev->gadget;
 	struct f_qdss *qdss = func_to_qdss(f);
 	struct usb_ep *ep;
-	int iface;
+	int iface, id;
 
 	pr_debug("qdss_bind\n");
 
@@ -356,6 +356,12 @@
 	qdss_data_intf_desc.bInterfaceNumber = iface;
 	qdss->data_iface_id = iface;
 
+	id = usb_string_id(c->cdev);
+	if (id < 0)
+		return id;
+	qdss_string_defs[QDSS_DATA_IDX].id = id;
+	qdss_data_intf_desc.iInterface = id;
+
 	if (qdss->debug_inface_enabled) {
 		/* Allocate ctrl I/F */
 		iface = usb_interface_id(c, f);
@@ -365,6 +371,11 @@
 		}
 		qdss_ctrl_intf_desc.bInterfaceNumber = iface;
 		qdss->ctrl_iface_id = iface;
+		id = usb_string_id(c->cdev);
+		if (id < 0)
+			return id;
+		qdss_string_defs[QDSS_CTRL_IDX].id = id;
+		qdss_ctrl_intf_desc.iInterface = id;
 	}
 
 	ep = usb_ep_autoconfig_ss(gadget, &qdss_ss_data_desc,
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 41e1b47..56a8e1b 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -920,6 +920,7 @@
 			free_netdev(opts->net);
 	}
 
+	kfree(opts->rndis_interf_group);	/* single VLA chunk */
 	kfree(opts);
 }
 
@@ -928,6 +929,7 @@
 	struct f_rndis_opts *opts;
 	struct usb_os_desc *descs[1];
 	char *names[1];
+	struct config_group *rndis_interf_group;
 
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
@@ -948,8 +950,14 @@
 	names[0] = "rndis";
 	config_group_init_type_name(&opts->func_inst.group, "",
 				    &rndis_func_type);
-	usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
-				       names, THIS_MODULE);
+	rndis_interf_group =
+		usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
+					       names, THIS_MODULE);
+	if (IS_ERR(rndis_interf_group)) {
+		rndis_free_inst(&opts->func_inst);
+		return ERR_CAST(rndis_interf_group);
+	}
+	opts->rndis_interf_group = rndis_interf_group;
 
 	return &opts->func_inst;
 }
diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c
index 38d58f3..ac2231a 100644
--- a/drivers/usb/gadget/function/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
@@ -813,8 +813,17 @@
 	/* For USB: responses may take up to 10 seconds */
 	switch (MsgType) {
 	case RNDIS_MSG_INIT:
-		pr_debug("%s: RNDIS_MSG_INIT\n",
-			__func__);
+		pr_debug("%s: RNDIS_MSG_INIT\n", __func__);
+		tmp++; /* to get RequestID */
+		params->host_rndis_major_ver = get_unaligned_le32(tmp++);
+		params->host_rndis_minor_ver = get_unaligned_le32(tmp++);
+		params->dl_max_xfer_size = get_unaligned_le32(tmp++);
+
+		pr_debug("%s(): RNDIS Host Major:%d Minor:%d version\n",
+					__func__, params->host_rndis_major_ver,
+					params->host_rndis_minor_ver);
+		pr_debug("%s(): DL Max Transfer size:%x\n",
+				__func__, params->dl_max_xfer_size);
 		params->state = RNDIS_INITIALIZED;
 		return rndis_init_response(params, (rndis_init_msg_type *)buf);
 
diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h
index 939c3be..4ffc282 100644
--- a/drivers/usb/gadget/function/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
@@ -191,6 +191,9 @@
 
 	u32			vendorID;
 	u8			max_pkt_per_xfer;
+	u32			host_rndis_major_ver;
+	u32			host_rndis_minor_ver;
+	u32			dl_max_xfer_size;
 	const char		*vendorDescr;
 	u8			pkt_alignment_factor;
 	void			(*resp_avail)(void *v);
diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index ce0f3a7..b4541e2 100644
--- a/drivers/usb/gadget/function/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
@@ -33,4 +33,8 @@
 	int				refcnt;
 };
 
+extern struct device *create_function_device(char *name);
+int ncm_ctrlrequest(struct usb_composite_dev *cdev,
+		const struct usb_ctrlrequest *ctrl);
+
 #endif /* U_NCM_H */
diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index 4eafd50..4e2ad04 100644
--- a/drivers/usb/gadget/function/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
@@ -26,6 +26,7 @@
 	bool				bound;
 	bool				borrowed_net;
 
+	struct config_group		*rndis_interf_group;
 	struct usb_os_desc		rndis_os_desc;
 	char				rndis_ext_compat_id[16];
 
diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c
index f959c42..f69dbd4 100644
--- a/drivers/usb/gadget/legacy/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
@@ -27,7 +27,7 @@
 #include <linux/mmu_context.h>
 #include <linux/aio.h>
 #include <linux/uio.h>
-
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/moduleparam.h>
 
@@ -116,6 +116,7 @@
 struct dev_data {
 	spinlock_t			lock;
 	atomic_t			count;
+	int				udc_usage;
 	enum ep0_state			state;		/* P: lock */
 	struct usb_gadgetfs_event	event [N_EVENT];
 	unsigned			ev_next;
@@ -513,9 +514,9 @@
 		INIT_WORK(&priv->work, ep_user_copy_worker);
 		schedule_work(&priv->work);
 	}
-	spin_unlock(&epdata->dev->lock);
 
 	usb_ep_free_request(ep, req);
+	spin_unlock(&epdata->dev->lock);
 	put_ep(epdata);
 }
 
@@ -939,9 +940,11 @@
 			struct usb_request	*req = dev->req;
 
 			if ((retval = setup_req (ep, req, 0)) == 0) {
+				++dev->udc_usage;
 				spin_unlock_irq (&dev->lock);
 				retval = usb_ep_queue (ep, req, GFP_KERNEL);
 				spin_lock_irq (&dev->lock);
+				--dev->udc_usage;
 			}
 			dev->state = STATE_DEV_CONNECTED;
 
@@ -983,11 +986,14 @@
 				retval = -EIO;
 			else {
 				len = min (len, (size_t)dev->req->actual);
-// FIXME don't call this with the spinlock held ...
+				++dev->udc_usage;
+				spin_unlock_irq(&dev->lock);
 				if (copy_to_user (buf, dev->req->buf, len))
 					retval = -EFAULT;
 				else
 					retval = len;
+				spin_lock_irq(&dev->lock);
+				--dev->udc_usage;
 				clean_req (dev->gadget->ep0, dev->req);
 				/* NOTE userspace can't yet choose to stall */
 			}
@@ -1131,6 +1137,7 @@
 			retval = setup_req (dev->gadget->ep0, dev->req, len);
 			if (retval == 0) {
 				dev->state = STATE_DEV_CONNECTED;
+				++dev->udc_usage;
 				spin_unlock_irq (&dev->lock);
 				if (copy_from_user (dev->req->buf, buf, len))
 					retval = -EFAULT;
@@ -1142,6 +1149,7 @@
 						GFP_KERNEL);
 				}
 				spin_lock_irq(&dev->lock);
+				--dev->udc_usage;
 				if (retval < 0) {
 					clean_req (dev->gadget->ep0, dev->req);
 				} else
@@ -1243,9 +1251,21 @@
 	struct usb_gadget	*gadget = dev->gadget;
 	long ret = -ENOTTY;
 
-	if (gadget->ops->ioctl)
+	spin_lock_irq(&dev->lock);
+	if (dev->state == STATE_DEV_OPENED ||
+			dev->state == STATE_DEV_UNBOUND) {
+		/* Not bound to a UDC */
+	} else if (gadget->ops->ioctl) {
+		++dev->udc_usage;
+		spin_unlock_irq(&dev->lock);
+
 		ret = gadget->ops->ioctl (gadget, code, value);
 
+		spin_lock_irq(&dev->lock);
+		--dev->udc_usage;
+	}
+	spin_unlock_irq(&dev->lock);
+
 	return ret;
 }
 
@@ -1463,10 +1483,12 @@
 				if (value < 0)
 					break;
 
+				++dev->udc_usage;
 				spin_unlock (&dev->lock);
 				value = usb_ep_queue (gadget->ep0, dev->req,
 							GFP_KERNEL);
 				spin_lock (&dev->lock);
+				--dev->udc_usage;
 				if (value < 0) {
 					clean_req (gadget->ep0, dev->req);
 					break;
@@ -1490,8 +1512,12 @@
 		req->length = value;
 		req->zero = value < w_length;
 
+		++dev->udc_usage;
 		spin_unlock (&dev->lock);
 		value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
+		spin_lock(&dev->lock);
+		--dev->udc_usage;
+		spin_unlock(&dev->lock);
 		if (value < 0) {
 			DBG (dev, "ep_queue --> %d\n", value);
 			req->status = 0;
@@ -1518,21 +1544,24 @@
 		/* break link to FS */
 		ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
 		list_del_init (&ep->epfiles);
+		spin_unlock_irq (&dev->lock);
+
 		dentry = ep->dentry;
 		ep->dentry = NULL;
 		parent = d_inode(dentry->d_parent);
 
 		/* break link to controller */
+		mutex_lock(&ep->lock);
 		if (ep->state == STATE_EP_ENABLED)
 			(void) usb_ep_disable (ep->ep);
 		ep->state = STATE_EP_UNBOUND;
 		usb_ep_free_request (ep->ep, ep->req);
 		ep->ep = NULL;
+		mutex_unlock(&ep->lock);
+
 		wake_up (&ep->wait);
 		put_ep (ep);
 
-		spin_unlock_irq (&dev->lock);
-
 		/* break link to dcache */
 		inode_lock(parent);
 		d_delete (dentry);
@@ -1603,6 +1632,11 @@
 
 	spin_lock_irq (&dev->lock);
 	dev->state = STATE_DEV_UNBOUND;
+	while (dev->udc_usage > 0) {
+		spin_unlock_irq(&dev->lock);
+		usleep_range(1000, 2000);
+		spin_lock_irq(&dev->lock);
+	}
 	spin_unlock_irq (&dev->lock);
 
 	destroy_ep_files (dev);
diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index 125974f..fcba597 100644
--- a/drivers/usb/gadget/legacy/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
@@ -107,15 +107,6 @@
 
 FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
 
-static unsigned long msg_registered;
-static void msg_cleanup(void);
-
-static int msg_thread_exits(struct fsg_common *common)
-{
-	msg_cleanup();
-	return 0;
-}
-
 static int msg_do_config(struct usb_configuration *c)
 {
 	struct fsg_opts *opts;
@@ -154,9 +145,6 @@
 
 static int msg_bind(struct usb_composite_dev *cdev)
 {
-	static const struct fsg_operations ops = {
-		.thread_exits = msg_thread_exits,
-	};
 	struct fsg_opts *opts;
 	struct fsg_config config;
 	int status;
@@ -173,8 +161,6 @@
 	if (status)
 		goto fail;
 
-	fsg_common_set_ops(opts->common, &ops);
-
 	status = fsg_common_set_cdev(opts->common, cdev, config.can_stall);
 	if (status)
 		goto fail_set_cdev;
@@ -210,7 +196,6 @@
 	usb_composite_overwrite_options(cdev, &coverwrite);
 	dev_info(&cdev->gadget->dev,
 		 DRIVER_DESC ", version: " DRIVER_VERSION "\n");
-	set_bit(0, &msg_registered);
 	return 0;
 
 fail_otg_desc:
@@ -261,9 +246,8 @@
 }
 module_init(msg_init);
 
-static void msg_cleanup(void)
+static void __exit msg_cleanup(void)
 {
-	if (test_and_clear_bit(0, &msg_registered))
-		usb_composite_unregister(&msg_driver);
+	usb_composite_unregister(&msg_driver);
 }
 module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index a95b3e7..ad84029 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -28,6 +28,8 @@
 #include <linux/of_gpio.h>
 
 #include "atmel_usba_udc.h"
+#define USBA_VBUS_IRQFLAGS (IRQF_ONESHOT \
+			   | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)
 
 #ifdef CONFIG_USB_GADGET_DEBUG_FS
 #include <linux/debugfs.h>
@@ -2172,7 +2174,7 @@
 					IRQ_NOAUTOEN);
 			ret = devm_request_threaded_irq(&pdev->dev,
 					gpio_to_irq(udc->vbus_pin), NULL,
-					usba_vbus_irq_thread, IRQF_ONESHOT,
+					usba_vbus_irq_thread, USBA_VBUS_IRQFLAGS,
 					"atmel_usba_udc", udc);
 			if (ret) {
 				udc->vbus_pin = -ENODEV;
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 94c8a9f..b62a3de 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -237,6 +237,8 @@
 
 	struct usb_device		*udev;
 	struct list_head		urbp_list;
+	struct urbp			*next_frame_urbp;
+
 	u32				stream_en_ep;
 	u8				num_stream[30 / 2];
 
@@ -253,11 +255,13 @@
 	 */
 	struct dummy_ep			ep[DUMMY_ENDPOINTS];
 	int				address;
+	int				callback_usage;
 	struct usb_gadget		gadget;
 	struct usb_gadget_driver	*driver;
 	struct dummy_request		fifo_req;
 	u8				fifo_buf[FIFO_SIZE];
 	u16				devstatus;
+	unsigned			ints_enabled:1;
 	unsigned			udc_suspended:1;
 	unsigned			pullup:1;
 
@@ -416,6 +420,7 @@
 static void set_link_state(struct dummy_hcd *dum_hcd)
 {
 	struct dummy *dum = dum_hcd->dum;
+	unsigned int power_bit;
 
 	dum_hcd->active = 0;
 	if (dum->pullup)
@@ -426,32 +431,43 @@
 			return;
 
 	set_link_state_by_speed(dum_hcd);
+	power_bit = (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 ?
+			USB_SS_PORT_STAT_POWER : USB_PORT_STAT_POWER);
 
 	if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
 	     dum_hcd->active)
 		dum_hcd->resuming = 0;
 
 	/* Currently !connected or in reset */
-	if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
+	if ((dum_hcd->port_status & power_bit) == 0 ||
 			(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
-		unsigned disconnect = USB_PORT_STAT_CONNECTION &
+		unsigned int disconnect = power_bit &
 				dum_hcd->old_status & (~dum_hcd->port_status);
-		unsigned reset = USB_PORT_STAT_RESET &
+		unsigned int reset = USB_PORT_STAT_RESET &
 				(~dum_hcd->old_status) & dum_hcd->port_status;
 
 		/* Report reset and disconnect events to the driver */
-		if (dum->driver && (disconnect || reset)) {
+		if (dum->ints_enabled && (disconnect || reset)) {
 			stop_activity(dum);
+			++dum->callback_usage;
+			spin_unlock(&dum->lock);
 			if (reset)
 				usb_gadget_udc_reset(&dum->gadget, dum->driver);
 			else
 				dum->driver->disconnect(&dum->gadget);
+			spin_lock(&dum->lock);
+			--dum->callback_usage;
 		}
-	} else if (dum_hcd->active != dum_hcd->old_active) {
+	} else if (dum_hcd->active != dum_hcd->old_active &&
+			dum->ints_enabled) {
+		++dum->callback_usage;
+		spin_unlock(&dum->lock);
 		if (dum_hcd->old_active && dum->driver->suspend)
 			dum->driver->suspend(&dum->gadget);
 		else if (!dum_hcd->old_active &&  dum->driver->resume)
 			dum->driver->resume(&dum->gadget);
+		spin_lock(&dum->lock);
+		--dum->callback_usage;
 	}
 
 	dum_hcd->old_status = dum_hcd->port_status;
@@ -965,8 +981,11 @@
 	 * can't enumerate without help from the driver we're binding.
 	 */
 
+	spin_lock_irq(&dum->lock);
 	dum->devstatus = 0;
 	dum->driver = driver;
+	dum->ints_enabled = 1;
+	spin_unlock_irq(&dum->lock);
 
 	return 0;
 }
@@ -977,6 +996,16 @@
 	struct dummy		*dum = dum_hcd->dum;
 
 	spin_lock_irq(&dum->lock);
+	dum->ints_enabled = 0;
+	stop_activity(dum);
+
+	/* emulate synchronize_irq(): wait for callbacks to finish */
+	while (dum->callback_usage > 0) {
+		spin_unlock_irq(&dum->lock);
+		usleep_range(1000, 2000);
+		spin_lock_irq(&dum->lock);
+	}
+
 	dum->driver = NULL;
 	spin_unlock_irq(&dum->lock);
 
@@ -1030,7 +1059,12 @@
 	memzero_explicit(&dum->gadget, sizeof(struct usb_gadget));
 	dum->gadget.name = gadget_name;
 	dum->gadget.ops = &dummy_ops;
-	dum->gadget.max_speed = USB_SPEED_SUPER;
+	if (mod_data.is_super_speed)
+		dum->gadget.max_speed = USB_SPEED_SUPER;
+	else if (mod_data.is_high_speed)
+		dum->gadget.max_speed = USB_SPEED_HIGH;
+	else
+		dum->gadget.max_speed = USB_SPEED_FULL;
 
 	dum->gadget.dev.parent = &pdev->dev;
 	init_dummy_udc_hw(dum);
@@ -1239,6 +1273,8 @@
 
 	list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
 	urb->hcpriv = urbp;
+	if (!dum_hcd->next_frame_urbp)
+		dum_hcd->next_frame_urbp = urbp;
 	if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
 		urb->error_count = 1;		/* mark as a new urb */
 
@@ -1515,6 +1551,8 @@
 	if (!is_active((dum->gadget.speed == USB_SPEED_SUPER ?
 			dum->ss_hcd : dum->hs_hcd)))
 		return NULL;
+	if (!dum->ints_enabled)
+		return NULL;
 	if ((address & ~USB_DIR_IN) == 0)
 		return &dum->ep[0];
 	for (i = 1; i < DUMMY_ENDPOINTS; i++) {
@@ -1756,6 +1794,7 @@
 		spin_unlock_irqrestore(&dum->lock, flags);
 		return;
 	}
+	dum_hcd->next_frame_urbp = NULL;
 
 	for (i = 0; i < DUMMY_ENDPOINTS; i++) {
 		if (!ep_info[i].name)
@@ -1772,6 +1811,10 @@
 		int			type;
 		int			status = -EINPROGRESS;
 
+		/* stop when we reach URBs queued after the timer interrupt */
+		if (urbp == dum_hcd->next_frame_urbp)
+			break;
+
 		urb = urbp->urb;
 		if (urb->unlinked)
 			goto return_urb;
@@ -1851,10 +1894,12 @@
 			 * until setup() returns; no reentrancy issues etc.
 			 */
 			if (value > 0) {
+				++dum->callback_usage;
 				spin_unlock(&dum->lock);
 				value = dum->driver->setup(&dum->gadget,
 						&setup);
 				spin_lock(&dum->lock);
+				--dum->callback_usage;
 
 				if (value >= 0) {
 					/* no delays (max 64KB data stage) */
@@ -2559,8 +2604,6 @@
 	.product_desc =		"Dummy host controller",
 	.hcd_priv_size =	sizeof(struct dummy_hcd),
 
-	.flags =		HCD_USB3 | HCD_SHARED,
-
 	.reset =		dummy_setup,
 	.start =		dummy_start,
 	.stop =			dummy_stop,
@@ -2589,8 +2632,12 @@
 	dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
 	dum = *((void **)dev_get_platdata(&pdev->dev));
 
-	if (!mod_data.is_super_speed)
+	if (mod_data.is_super_speed)
+		dummy_hcd.flags = HCD_USB3 | HCD_SHARED;
+	else if (mod_data.is_high_speed)
 		dummy_hcd.flags = HCD_USB2;
+	else
+		dummy_hcd.flags = HCD_USB11;
 	hs_hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
 	if (!hs_hcd)
 		return -ENOMEM;
diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c
index ba78e3f..bb89e24 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -685,21 +685,32 @@
 	return usb3_req;
 }
 
+static void __usb3_request_done(struct renesas_usb3_ep *usb3_ep,
+				struct renesas_usb3_request *usb3_req,
+				int status)
+{
+	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
+
+	dev_dbg(usb3_to_dev(usb3), "giveback: ep%2d, %u, %u, %d\n",
+		usb3_ep->num, usb3_req->req.length, usb3_req->req.actual,
+		status);
+	usb3_req->req.status = status;
+	usb3_ep->started = false;
+	list_del_init(&usb3_req->queue);
+	spin_unlock(&usb3->lock);
+	usb_gadget_giveback_request(&usb3_ep->ep, &usb3_req->req);
+	spin_lock(&usb3->lock);
+}
+
 static void usb3_request_done(struct renesas_usb3_ep *usb3_ep,
 			      struct renesas_usb3_request *usb3_req, int status)
 {
 	struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep);
 	unsigned long flags;
 
-	dev_dbg(usb3_to_dev(usb3), "giveback: ep%2d, %u, %u, %d\n",
-		usb3_ep->num, usb3_req->req.length, usb3_req->req.actual,
-		status);
-	usb3_req->req.status = status;
 	spin_lock_irqsave(&usb3->lock, flags);
-	usb3_ep->started = false;
-	list_del_init(&usb3_req->queue);
+	__usb3_request_done(usb3_ep, usb3_req, status);
 	spin_unlock_irqrestore(&usb3->lock, flags);
-	usb_gadget_giveback_request(&usb3_ep->ep, &usb3_req->req);
 }
 
 static void usb3_irq_epc_pipe0_status_end(struct renesas_usb3 *usb3)
@@ -868,7 +879,7 @@
 			usb3_ep->ep.maxpacket);
 	u8 *buf = usb3_req->req.buf + usb3_req->req.actual;
 	u32 tmp = 0;
-	bool is_last;
+	bool is_last = !len ? true : false;
 
 	if (usb3_wait_pipe_status(usb3_ep, PX_STA_BUFSTS) < 0)
 		return -EBUSY;
@@ -889,7 +900,8 @@
 		usb3_write(usb3, tmp, fifo_reg);
 	}
 
-	is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
+	if (!is_last)
+		is_last = usb3_is_transfer_complete(usb3_ep, usb3_req);
 	/* Send the data */
 	usb3_set_px_con_send(usb3_ep, len, is_last);
 
@@ -980,7 +992,8 @@
 		usb3_set_p0_con_for_ctrl_read_data(usb3);
 	} else {
 		usb3_clear_bit(usb3, P0_MOD_DIR, USB3_P0_MOD);
-		usb3_set_p0_con_for_ctrl_write_data(usb3);
+		if (usb3_req->req.length)
+			usb3_set_p0_con_for_ctrl_write_data(usb3);
 	}
 
 	usb3_p0_xfer(usb3_ep, usb3_req);
@@ -1557,7 +1570,16 @@
 static u32 usb3_calc_rammap_val(struct renesas_usb3_ep *usb3_ep,
 				const struct usb_endpoint_descriptor *desc)
 {
-	return usb3_ep->rammap_val | PN_RAMMAP_MPKT(usb_endpoint_maxp(desc));
+	int i;
+	const u32 max_packet_array[] = {8, 16, 32, 64, 512};
+	u32 mpkt = PN_RAMMAP_MPKT(1024);
+
+	for (i = 0; i < ARRAY_SIZE(max_packet_array); i++) {
+		if (usb_endpoint_maxp(desc) <= max_packet_array[i])
+			mpkt = PN_RAMMAP_MPKT(max_packet_array[i]);
+	}
+
+	return usb3_ep->rammap_val | mpkt;
 }
 
 static int usb3_enable_pipe_n(struct renesas_usb3_ep *usb3_ep,
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 0b80cee3..eb121b2 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -45,9 +45,9 @@
 	  If unsure, say N.
 
 config USB_XHCI_MTK
-	tristate "xHCI support for Mediatek MT65xx"
+	tristate "xHCI support for Mediatek MT65xx/MT7621"
 	select MFD_SYSCON
-	depends on ARCH_MEDIATEK || COMPILE_TEST
+	depends on (MIPS && SOC_MT7621) || ARCH_MEDIATEK || COMPILE_TEST
 	---help---
 	  Say 'Y' to enable the support for the xHCI host controller
 	  found in Mediatek MT65xx SoCs.
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index c8989c6..ee213c5 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -98,6 +98,7 @@
 	AMD_CHIPSET_HUDSON2,
 	AMD_CHIPSET_BOLTON,
 	AMD_CHIPSET_YANGTZE,
+	AMD_CHIPSET_TAISHAN,
 	AMD_CHIPSET_UNKNOWN,
 };
 
@@ -145,20 +146,26 @@
 		pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
 				PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
 
-		if (!pinfo->smbus_dev) {
-			pinfo->sb_type.gen = NOT_AMD_CHIPSET;
-			return 0;
+		if (pinfo->smbus_dev) {
+			rev = pinfo->smbus_dev->revision;
+			if (rev >= 0x11 && rev <= 0x14)
+				pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
+			else if (rev >= 0x15 && rev <= 0x18)
+				pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
+			else if (rev >= 0x39 && rev <= 0x3a)
+				pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
+		} else {
+			pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+							  0x145c, NULL);
+			if (pinfo->smbus_dev) {
+				rev = pinfo->smbus_dev->revision;
+				pinfo->sb_type.gen = AMD_CHIPSET_TAISHAN;
+			} else {
+				pinfo->sb_type.gen = NOT_AMD_CHIPSET;
+				return 0;
+			}
 		}
-
-		rev = pinfo->smbus_dev->revision;
-		if (rev >= 0x11 && rev <= 0x14)
-			pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
-		else if (rev >= 0x15 && rev <= 0x18)
-			pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
-		else if (rev >= 0x39 && rev <= 0x3a)
-			pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
 	}
-
 	pinfo->sb_type.rev = rev;
 	return 1;
 }
@@ -260,11 +267,12 @@
 {
 	/* Make sure amd chipset type has already been initialized */
 	usb_amd_find_chipset_info();
-	if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE)
-		return 0;
-
-	dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
-	return 1;
+	if (amd_chipset.sb_type.gen == AMD_CHIPSET_YANGTZE ||
+	    amd_chipset.sb_type.gen == AMD_CHIPSET_TAISHAN) {
+		dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
+		return 1;
+	}
+	return 0;
 }
 EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk);
 
@@ -439,7 +447,7 @@
 		if ((value & ASMT_CONTROL_WRITE_BIT) == 0)
 			return 0;
 
-		usleep_range(40, 60);
+		udelay(50);
 	}
 
 	dev_warn(&pdev->dev, "%s: check_write_ready timeout", __func__);
@@ -1014,7 +1022,7 @@
  *
  * Takes care of the handoff between the Pre-OS (i.e. BIOS) and the OS.
  * It signals to the BIOS that the OS wants control of the host controller,
- * and then waits 5 seconds for the BIOS to hand over control.
+ * and then waits 1 second for the BIOS to hand over control.
  * If we timeout, assume the BIOS is broken and take control anyway.
  */
 static void quirk_usb_handoff_xhci(struct pci_dev *pdev)
@@ -1061,9 +1069,9 @@
 	if (val & XHCI_HC_BIOS_OWNED) {
 		writel(val | XHCI_HC_OS_OWNED, base + ext_cap_offset);
 
-		/* Wait for 5 seconds with 10 microsecond polling interval */
+		/* Wait for 1 second with 10 microsecond polling interval */
 		timeout = handshake(base + ext_cap_offset, XHCI_HC_BIOS_OWNED,
-				0, 5000, 10);
+				0, 1000000, 10);
 
 		/* Assume a buggy BIOS and take HC ownership anyway */
 		if (timeout) {
@@ -1092,7 +1100,7 @@
 	 * operational or runtime registers.  Wait 5 seconds and no more.
 	 */
 	timeout = handshake(op_reg_base + XHCI_STS_OFFSET, XHCI_STS_CNR, 0,
-			5000, 10);
+			5000000, 10);
 	/* Assume a buggy HC and start HC initialization anyway */
 	if (timeout) {
 		val = readl(op_reg_base + XHCI_STS_OFFSET);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 93aa6a0..5434902 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -112,7 +112,7 @@
 
 	/* If PSI table exists, add the custom speed attributes from it */
 	if (usb3_1 && xhci->usb3_rhub.psi_count) {
-		u32 ssp_cap_base, bm_attrib, psi;
+		u32 ssp_cap_base, bm_attrib, psi, psi_mant, psi_exp;
 		int offset;
 
 		ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
@@ -139,6 +139,15 @@
 		for (i = 0; i < xhci->usb3_rhub.psi_count; i++) {
 			psi = xhci->usb3_rhub.psi[i];
 			psi &= ~USB_SSP_SUBLINK_SPEED_RSVD;
+			psi_exp = XHCI_EXT_PORT_PSIE(psi);
+			psi_mant = XHCI_EXT_PORT_PSIM(psi);
+
+			/* Shift to Gbps and set SSP Link BIT(14) if 10Gpbs */
+			for (; psi_exp < 3; psi_exp++)
+				psi_mant /= 1000;
+			if (psi_mant >= 10)
+				psi |= BIT(14);
+
 			if ((psi & PLT_MASK) == PLT_SYM) {
 			/* Symmetric, create SSA RX and TX from one PSI entry */
 				put_unaligned_le32(psi, &buf[offset]);
@@ -357,7 +366,7 @@
 
 	slot_id = 0;
 	for (i = 0; i < MAX_HC_SLOTS; i++) {
-		if (!xhci->devs[i])
+		if (!xhci->devs[i] || !xhci->devs[i]->udev)
 			continue;
 		speed = xhci->devs[i]->udev->speed;
 		if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3))
@@ -406,25 +415,25 @@
 						     GFP_NOWAIT);
 			if (!command) {
 				spin_unlock_irqrestore(&xhci->lock, flags);
-				xhci_free_command(xhci, cmd);
-				return -ENOMEM;
-
+				ret = -ENOMEM;
+				goto cmd_cleanup;
 			}
 
 			ret = xhci_queue_stop_endpoint(xhci, command, slot_id,
-					i, suspend);
+						       i, suspend);
 			if (ret) {
 				spin_unlock_irqrestore(&xhci->lock, flags);
 				xhci_free_command(xhci, command);
-				goto err_cmd_queue;
+				goto cmd_cleanup;
 			}
 		}
 	}
 	ret = xhci_queue_stop_endpoint(xhci, cmd, slot_id, 0, suspend);
 	if (ret) {
 		spin_unlock_irqrestore(&xhci->lock, flags);
-		goto err_cmd_queue;
+		goto cmd_cleanup;
 	}
+
 	xhci_ring_cmd_db(xhci);
 	spin_unlock_irqrestore(&xhci->lock, flags);
 
@@ -436,7 +445,7 @@
 		ret = -ETIME;
 	}
 
-err_cmd_queue:
+cmd_cleanup:
 	xhci_free_command(xhci, cmd);
 	return ret;
 }
@@ -1571,9 +1580,6 @@
 				t2 |= PORT_WKOC_E | PORT_WKCONN_E;
 				t2 &= ~PORT_WKDISC_E;
 			}
-			if ((xhci->quirks & XHCI_U2_DISABLE_WAKE) &&
-			    (hcd->speed < HCD_USB3))
-				t2 &= ~PORT_WAKE_BITS;
 		} else
 			t2 &= ~PORT_WAKE_BITS;
 
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 2383344..c87ef38 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -54,11 +54,6 @@
 #define PCI_DEVICE_ID_INTEL_APL_XHCI			0x5aa8
 #define PCI_DEVICE_ID_INTEL_DNV_XHCI			0x19d0
 
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_4			0x43b9
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_3			0x43ba
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_2			0x43bb
-#define PCI_DEVICE_ID_AMD_PROMONTORYA_1			0x43bc
-
 #define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI		0x1142
 
 static const char hcd_name[] = "xhci_hcd";
@@ -142,13 +137,6 @@
 	if (pdev->vendor == PCI_VENDOR_ID_AMD)
 		xhci->quirks |= XHCI_TRUST_TX_LENGTH;
 
-	if ((pdev->vendor == PCI_VENDOR_ID_AMD) &&
-		((pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4) ||
-		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_3) ||
-		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_2) ||
-		(pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_1)))
-		xhci->quirks |= XHCI_U2_DISABLE_WAKE;
-
 	if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
 		xhci->quirks |= XHCI_LPM_SUPPORT;
 		xhci->quirks |= XHCI_INTEL_HOST;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 6cb5ab3..588546a 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -307,15 +307,18 @@
 
 	hcd_to_bus(xhci->shared_hcd)->skip_resume = true;
 
-	if (device_property_read_bool(sysdev, "usb3-lpm-capable"))
+	if (device_property_read_bool(&pdev->dev, "usb3-lpm-capable"))
 		xhci->quirks |= XHCI_LPM_SUPPORT;
 
 	if (device_property_read_bool(&pdev->dev, "quirk-broken-port-ped"))
 		xhci->quirks |= XHCI_BROKEN_PORT_PED;
 
-	if (device_property_read_u32(sysdev, "snps,xhci-imod-value", &imod))
+	if (device_property_read_u32(&pdev->dev, "xhci-imod-value", &imod))
 		imod = 0;
 
+	if (device_property_read_u32(sysdev, "usb-core-id", &xhci->core_id))
+		xhci->core_id = -EINVAL;
+
 	hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
 	if (IS_ERR(hcd->usb_phy)) {
 		ret = PTR_ERR(hcd->usb_phy);
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 15bf308..1332057 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -4871,7 +4871,8 @@
 		 */
 		hcd->has_tt = 1;
 	} else {
-		if (xhci->sbrn == 0x31) {
+		/* Some 3.1 hosts return sbrn 0x30, can't rely on sbrn alone */
+		if (xhci->sbrn == 0x31 || xhci->usb3_rhub.min_rev >= 1) {
 			xhci_info(xhci, "Host supports USB 3.1 Enhanced SuperSpeed\n");
 			hcd->speed = HCD_USB31;
 			hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
@@ -4964,10 +4965,13 @@
 }
 EXPORT_SYMBOL_GPL(xhci_gen_setup);
 
-dma_addr_t xhci_get_sec_event_ring_dma_addr(struct usb_hcd *hcd,
-	unsigned int intr_num)
+static phys_addr_t xhci_get_sec_event_ring_phys_addr(struct usb_hcd *hcd,
+	unsigned int intr_num, dma_addr_t *dma)
 {
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	struct device *dev = hcd->self.sysdev;
+	struct sg_table sgt;
+	phys_addr_t pa;
 
 	if (intr_num >= xhci->max_interrupters) {
 		xhci_err(xhci, "intr num %d >= max intrs %d\n", intr_num,
@@ -4977,31 +4981,34 @@
 
 	if (!(xhci->xhc_state & XHCI_STATE_HALTED) &&
 		xhci->sec_event_ring && xhci->sec_event_ring[intr_num]
-		&& xhci->sec_event_ring[intr_num]->first_seg)
-		return xhci->sec_event_ring[intr_num]->first_seg->dma;
+		&& xhci->sec_event_ring[intr_num]->first_seg) {
+
+		dma_get_sgtable(dev, &sgt,
+			xhci->sec_event_ring[intr_num]->first_seg->trbs,
+			xhci->sec_event_ring[intr_num]->first_seg->dma,
+			TRB_SEGMENT_SIZE);
+
+		*dma = xhci->sec_event_ring[intr_num]->first_seg->dma;
+
+		pa = page_to_phys(sg_page(sgt.sgl));
+		sg_free_table(&sgt);
+
+		return pa;
+	}
 
 	return 0;
 }
 
-dma_addr_t xhci_get_dcba_dma_addr(struct usb_hcd *hcd,
-	struct usb_device *udev)
-{
-	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-
-	if (!(xhci->xhc_state & XHCI_STATE_HALTED) && xhci->dcbaa)
-		return xhci->dcbaa->dev_context_ptrs[udev->slot_id];
-
-	return 0;
-}
-
-dma_addr_t xhci_get_xfer_ring_dma_addr(struct usb_hcd *hcd,
-	struct usb_device *udev, struct usb_host_endpoint *ep)
+static phys_addr_t xhci_get_xfer_ring_phys_addr(struct usb_hcd *hcd,
+	struct usb_device *udev, struct usb_host_endpoint *ep, dma_addr_t *dma)
 {
 	int ret;
 	unsigned int ep_index;
 	struct xhci_virt_device *virt_dev;
-
+	struct device *dev = hcd->self.sysdev;
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	struct sg_table sgt;
+	phys_addr_t pa;
 
 	ret = xhci_check_args(hcd, udev, ep, 1, true, __func__);
 	if (ret <= 0) {
@@ -5013,12 +5020,31 @@
 	ep_index = xhci_get_endpoint_index(&ep->desc);
 
 	if (virt_dev->eps[ep_index].ring &&
-		virt_dev->eps[ep_index].ring->first_seg)
-		return virt_dev->eps[ep_index].ring->first_seg->dma;
+		virt_dev->eps[ep_index].ring->first_seg) {
+
+		dma_get_sgtable(dev, &sgt,
+			virt_dev->eps[ep_index].ring->first_seg->trbs,
+			virt_dev->eps[ep_index].ring->first_seg->dma,
+			TRB_SEGMENT_SIZE);
+
+		*dma = virt_dev->eps[ep_index].ring->first_seg->dma;
+
+		pa = page_to_phys(sg_page(sgt.sgl));
+		sg_free_table(&sgt);
+
+		return pa;
+	}
 
 	return 0;
 }
 
+int xhci_get_core_id(struct usb_hcd *hcd)
+{
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+	return xhci->core_id;
+}
+
 static const struct hc_driver xhci_hc_driver = {
 	.description =		"xhci-hcd",
 	.product_desc =		"xHCI Host Controller",
@@ -5080,9 +5106,9 @@
 	.find_raw_port_number =	xhci_find_raw_port_number,
 	.sec_event_ring_setup =		xhci_sec_event_ring_setup,
 	.sec_event_ring_cleanup =	xhci_sec_event_ring_cleanup,
-	.get_sec_event_ring_dma_addr =	xhci_get_sec_event_ring_dma_addr,
-	.get_xfer_ring_dma_addr =	xhci_get_xfer_ring_dma_addr,
-	.get_dcba_dma_addr =		xhci_get_dcba_dma_addr,
+	.get_sec_event_ring_phys_addr =	xhci_get_sec_event_ring_phys_addr,
+	.get_xfer_ring_phys_addr =	xhci_get_xfer_ring_phys_addr,
+	.get_core_id =			xhci_get_core_id,
 };
 
 void xhci_init_driver(struct hc_driver *drv,
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 757d045..c11eab1 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1509,7 +1509,7 @@
 
 static inline unsigned int hcd_index(struct usb_hcd *hcd)
 {
-	if (hcd->speed == HCD_USB3)
+	if (hcd->speed >= HCD_USB3)
 		return 0;
 	else
 		return 1;
@@ -1538,6 +1538,8 @@
 	/* secondary interrupter */
 	struct	xhci_intr_reg __iomem **sec_ir_set;
 
+	int		core_id;
+
 	/* Cached register copies of read-only HC data */
 	__u32		hcs_params1;
 	__u32		hcs_params2;
@@ -1668,7 +1670,7 @@
 /* For controller with a broken Port Disable implementation */
 #define XHCI_BROKEN_PORT_PED	(1 << 25)
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7	(1 << 26)
-#define XHCI_U2_DISABLE_WAKE	(1 << 27)
+/* Reserved. It was XHCI_U2_DISABLE_WAKE */
 #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL	(1 << 28)
 
 	unsigned int		num_active_eps;
@@ -1977,6 +1979,7 @@
 		char *buf, u16 wLength);
 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
 int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
+int xhci_get_core_id(struct usb_hcd *hcd);
 
 #ifdef CONFIG_PM
 int xhci_bus_suspend(struct usb_hcd *hcd);
diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c
index 2020009..934e10f 100644
--- a/drivers/usb/misc/lvstest.c
+++ b/drivers/usb/misc/lvstest.c
@@ -184,10 +184,13 @@
 	struct usb_interface *intf = to_usb_interface(dev);
 	struct usb_device *hdev = interface_to_usbdev(intf);
 	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	int port;
 	int ret;
 
-	ret = lvs_rh_set_port_feature(hdev, lvs->portnum,
-			USB_PORT_FEAT_BH_PORT_RESET);
+	if (kstrtoint(buf, 0, &port) || port < 1 || port > 255)
+		port = lvs->portnum;
+
+	ret = lvs_rh_set_port_feature(hdev, port, USB_PORT_FEAT_BH_PORT_RESET);
 	if (ret < 0) {
 		dev_err(dev, "can't issue warm reset %d\n", ret);
 		return ret;
@@ -299,10 +302,14 @@
 	struct usb_interface *intf = to_usb_interface(dev);
 	struct usb_device *hdev = interface_to_usbdev(intf);
 	struct lvs_rh *lvs = usb_get_intfdata(intf);
+	int port;
 	int ret;
 
+	if (kstrtoint(buf, 0, &port) || port < 1 || port > 255)
+		port = lvs->portnum;
+
 	ret = lvs_rh_set_port_feature(hdev,
-			lvs->portnum | USB_SS_PORT_LS_COMP_MOD << 3,
+			port | (USB_SS_PORT_LS_COMP_MOD << 3),
 			USB_PORT_FEAT_LINK_STATE);
 	if (ret < 0) {
 		dev_err(dev, "can't enable compliance mode %d\n", ret);
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 261ed2c..aac28d9 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -890,7 +890,7 @@
 	 */
 	if (int_usb & MUSB_INTR_RESET) {
 		handled = IRQ_HANDLED;
-		if (devctl & MUSB_DEVCTL_HM) {
+		if (is_host_active(musb)) {
 			/*
 			 * When BABBLE happens what we can depends on which
 			 * platform MUSB is running, because some platforms
@@ -900,9 +900,7 @@
 			 * drop the session.
 			 */
 			dev_err(musb->controller, "Babble\n");
-
-			if (is_host_active(musb))
-				musb_recover_from_babble(musb);
+			musb_recover_from_babble(musb);
 		} else {
 			musb_dbg(musb, "BUS RESET as %s",
 				usb_otg_state_string(musb->xceiv->otg->state));
@@ -2655,6 +2653,13 @@
 {
 	struct musb	*musb = dev_to_musb(dev);
 	unsigned long	flags;
+	int ret;
+
+	ret = pm_runtime_get_sync(dev);
+	if (ret < 0) {
+		pm_runtime_put_noidle(dev);
+		return ret;
+	}
 
 	musb_platform_disable(musb);
 	musb_generic_disable(musb);
@@ -2703,14 +2708,6 @@
 	if ((devctl & mask) != (musb->context.devctl & mask))
 		musb->port1_status = 0;
 
-	/*
-	 * The USB HUB code expects the device to be in RPM_ACTIVE once it came
-	 * out of suspend
-	 */
-	pm_runtime_disable(dev);
-	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
-
 	musb_start(musb);
 
 	spin_lock_irqsave(&musb->lock, flags);
@@ -2720,6 +2717,9 @@
 			error);
 	spin_unlock_irqrestore(&musb->lock, flags);
 
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
 	return 0;
 }
 
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 99beda9..55c624f 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -139,6 +139,7 @@
 				"Could not flush host TX%d fifo: csr: %04x\n",
 				ep->epnum, csr))
 			return;
+		mdelay(1);
 	}
 }
 
diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c
index 1408245..3e1f3da 100644
--- a/drivers/usb/musb/sunxi.c
+++ b/drivers/usb/musb/sunxi.c
@@ -313,6 +313,8 @@
 	if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags))
 		sunxi_sram_release(musb->controller->parent);
 
+	devm_usb_put_phy(glue->dev, glue->xceiv);
+
 	return 0;
 }
 
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index 5a96299..44ab6d6 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -368,6 +368,7 @@
 
 	enum usbpd_state	current_state;
 	bool			hard_reset_recvd;
+	ktime_t			hard_reset_recvd_time;
 	struct list_head	rx_q;
 	spinlock_t		rx_lock;
 	struct rx_msg		*rx_ext_msg;
@@ -430,6 +431,7 @@
 	int			num_svids;
 	struct vdm_tx		*vdm_tx;
 	struct vdm_tx		*vdm_tx_retry;
+	struct mutex		svid_handler_lock;
 	struct list_head	svid_handlers;
 
 	struct list_head	instance;
@@ -581,10 +583,15 @@
 {
 	struct usbpd_svid_handler *handler;
 
-	list_for_each_entry(handler, &pd->svid_handlers, entry)
-		if (svid == handler->svid)
+	mutex_lock(&pd->svid_handler_lock);
+	list_for_each_entry(handler, &pd->svid_handlers, entry) {
+		if (svid == handler->svid) {
+			mutex_unlock(&pd->svid_handler_lock);
 			return handler;
+		}
+	}
 
+	mutex_unlock(&pd->svid_handler_lock);
 	return NULL;
 }
 
@@ -608,6 +615,9 @@
 	int ret;
 	u16 hdr;
 
+	if (pd->hard_reset_recvd)
+		return -EBUSY;
+
 	hdr = PD_MSG_HDR(msg_type, pd->current_dr, pd->current_pr,
 			pd->tx_msgid, num_data, pd->spec_rev);
 
@@ -799,11 +809,13 @@
 		return;
 	}
 
-	usbpd_dbg(&pd->dev, "hard reset received\n");
+	pd->hard_reset_recvd = true;
+	pd->hard_reset_recvd_time = ktime_get();
+
+	usbpd_err(&pd->dev, "hard reset received\n");
 
 	/* Force CC logic to source/sink to keep Rp/Rd unchanged */
 	set_power_role(pd, pd->current_pr);
-	pd->hard_reset_recvd = true;
 	power_supply_set_property(pd->usb_psy,
 			POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
 
@@ -1032,6 +1044,16 @@
 static void phy_shutdown(struct usbpd *pd)
 {
 	usbpd_dbg(&pd->dev, "shutdown");
+
+	if (pd->vconn_enabled) {
+		regulator_disable(pd->vconn);
+		pd->vconn_enabled = false;
+	}
+
+	if (pd->vbus_enabled) {
+		regulator_disable(pd->vbus);
+		pd->vbus_enabled = false;
+	}
 }
 
 static enum hrtimer_restart pd_timeout(struct hrtimer *timer)
@@ -1058,6 +1080,9 @@
 	unsigned long flags;
 	int ret;
 
+	if (pd->hard_reset_recvd) /* let usbpd_sm handle it */
+		return;
+
 	usbpd_dbg(&pd->dev, "%s -> %s\n",
 			usbpd_state_strings[pd->current_state],
 			usbpd_state_strings[next_state]);
@@ -1205,14 +1230,13 @@
 
 	case PE_SRC_READY:
 		pd->in_explicit_contract = true;
-		if (pd->current_dr == DR_DFP) {
-			/* don't start USB host until after SVDM discovery */
-			if (pd->vdm_state == VDM_NONE)
-				usbpd_send_svdm(pd, USBPD_SID,
-						USBPD_SVDM_DISCOVER_IDENTITY,
-						SVDM_CMD_TYPE_INITIATOR, 0,
-						NULL, 0);
-		}
+
+		if (pd->vdm_tx)
+			kick_sm(pd, 0);
+		else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE)
+			usbpd_send_svdm(pd, USBPD_SID,
+					USBPD_SVDM_DISCOVER_IDENTITY,
+					SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
 
 		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
 		complete(&pd->is_ready);
@@ -1352,6 +1376,14 @@
 
 	case PE_SNK_READY:
 		pd->in_explicit_contract = true;
+
+		if (pd->vdm_tx)
+			kick_sm(pd, 0);
+		else if (pd->current_dr == DR_DFP && pd->vdm_state == VDM_NONE)
+			usbpd_send_svdm(pd, USBPD_SID,
+					USBPD_SVDM_DISCOVER_IDENTITY,
+					SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
+
 		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
 		complete(&pd->is_ready);
 		dual_role_instance_changed(pd->dual_role);
@@ -1412,9 +1444,11 @@
 		return -EINVAL;
 	}
 
-	usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid);
-
+	usbpd_dbg(&pd->dev, "registered handler(%pK) for SVID 0x%04x\n",
+							hdlr, hdlr->svid);
+	mutex_lock(&pd->svid_handler_lock);
 	list_add_tail(&hdlr->entry, &pd->svid_handlers);
+	mutex_unlock(&pd->svid_handler_lock);
 	hdlr->request_usb_ss_lane = usbpd_release_ss_lane;
 
 	/* already connected with this SVID discovered? */
@@ -1436,7 +1470,12 @@
 
 void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
 {
+
+	usbpd_dbg(&pd->dev, "unregistered handler(%pK) for SVID 0x%04x\n",
+							hdlr, hdlr->svid);
+	mutex_lock(&pd->svid_handler_lock);
 	list_del_init(&hdlr->entry);
+	mutex_unlock(&pd->svid_handler_lock);
 }
 EXPORT_SYMBOL(usbpd_unregister_svid);
 
@@ -1747,6 +1786,7 @@
 {
 	struct usbpd_svid_handler *handler;
 
+	mutex_lock(&pd->svid_handler_lock);
 	list_for_each_entry(handler, &pd->svid_handlers, entry) {
 		if (handler->discovered) {
 			handler->disconnect(handler);
@@ -1754,6 +1794,7 @@
 		}
 	}
 
+	mutex_unlock(&pd->svid_handler_lock);
 	pd->vdm_state = VDM_NONE;
 	kfree(pd->vdm_tx_retry);
 	pd->vdm_tx_retry = NULL;
@@ -2012,8 +2053,13 @@
 		if (pd->current_pr == PR_SINK) {
 			usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
 		} else {
+			s64 delta = ktime_ms_delta(ktime_get(),
+					pd->hard_reset_recvd_time);
 			pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT;
-			kick_sm(pd, PS_HARD_RESET_TIME);
+			if (delta >= PS_HARD_RESET_TIME)
+				kick_sm(pd, 0);
+			else
+				kick_sm(pd, PS_HARD_RESET_TIME - (int)delta);
 		}
 
 		goto sm_done;
@@ -2031,6 +2077,10 @@
 
 	switch (pd->current_state) {
 	case PE_UNKNOWN:
+		val.intval = 0;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
 		if (pd->current_pr == PR_SINK) {
 			usbpd_set_state(pd, PE_SNK_STARTUP);
 		} else if (pd->current_pr == PR_SRC) {
@@ -2193,8 +2243,11 @@
 	case PE_SRC_TRANSITION_TO_DEFAULT:
 		if (pd->vconn_enabled)
 			regulator_disable(pd->vconn);
+		pd->vconn_enabled = false;
+
 		if (pd->vbus_enabled)
 			regulator_disable(pd->vbus);
+		pd->vbus_enabled = false;
 
 		if (pd->current_dr != DR_DFP) {
 			extcon_set_state_sync(pd->extcon, EXTCON_USB, 0);
@@ -2202,24 +2255,9 @@
 			pd_phy_update_roles(pd->current_dr, pd->current_pr);
 		}
 
-		msleep(SRC_RECOVER_TIME);
-
-		pd->vbus_enabled = false;
-		enable_vbus(pd);
-
-		if (pd->vconn_enabled) {
-			ret = regulator_enable(pd->vconn);
-			if (ret) {
-				usbpd_err(&pd->dev, "Unable to enable vconn\n");
-				pd->vconn_enabled = false;
-			}
-		}
-
-		val.intval = 0;
-		power_supply_set_property(pd->usb_psy,
-				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
-
-		usbpd_set_state(pd, PE_SRC_STARTUP);
+		/* PE_UNKNOWN will turn on VBUS and go back to PE_SRC_STARTUP */
+		pd->current_state = PE_UNKNOWN;
+		kick_sm(pd, SRC_RECOVER_TIME);
 		break;
 
 	case PE_SRC_HARD_RESET:
@@ -2278,8 +2316,11 @@
 					&val);
 
 			/* save the PDOs so userspace can further evaluate */
-			memcpy(&pd->received_pdos, rx_msg->payload,
+			memset(&pd->received_pdos, 0,
 					sizeof(pd->received_pdos));
+			memcpy(&pd->received_pdos, rx_msg->payload,
+					min_t(size_t, rx_msg->data_len,
+						sizeof(pd->received_pdos)));
 			pd->src_cap_id++;
 
 			usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
@@ -2387,8 +2428,11 @@
 	case PE_SNK_READY:
 		if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
 			/* save the PDOs so userspace can further evaluate */
-			memcpy(&pd->received_pdos, rx_msg->payload,
+			memset(&pd->received_pdos, 0,
 					sizeof(pd->received_pdos));
+			memcpy(&pd->received_pdos, rx_msg->payload,
+					min_t(size_t, rx_msg->data_len,
+						sizeof(pd->received_pdos)));
 			pd->src_cap_id++;
 
 			usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
@@ -3853,6 +3897,7 @@
 	hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	pd->timer.function = pd_timeout;
 	mutex_init(&pd->swap_lock);
+	mutex_init(&pd->svid_handler_lock);
 
 	pd->usb_psy = power_supply_get_by_name("usb");
 	if (!pd->usb_psy) {
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 735774a..8a4f3d4 100644
--- a/drivers/usb/pd/qpnp-pdphy.c
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -112,7 +112,6 @@
 	int tx_status;
 	u8 frame_filter_val;
 	bool in_test_data_mode;
-	bool rx_busy;
 
 	enum data_role data_role;
 	enum power_role power_role;
@@ -490,7 +489,7 @@
 	}
 
 	ret = pdphy_reg_read(pdphy, &val, USB_PDPHY_RX_ACKNOWLEDGE, 1);
-	if (ret || val || pdphy->rx_busy) {
+	if (ret || val) {
 		dev_err(pdphy->dev, "%s: RX message pending\n", __func__);
 		return -EBUSY;
 	}
@@ -583,6 +582,10 @@
 {
 	struct usb_pdphy *pdphy = data;
 
+	/* TX already aborted by received signal */
+	if (pdphy->tx_status != -EINPROGRESS)
+		return IRQ_HANDLED;
+
 	if (irq == pdphy->msg_tx_irq) {
 		pdphy->msg_tx_cnt++;
 		pdphy->tx_status = 0;
@@ -636,6 +639,10 @@
 	if (pdphy->signal_cb)
 		pdphy->signal_cb(pdphy->usbpd, frame_type);
 
+	if (pdphy->tx_status == -EINPROGRESS) {
+		pdphy->tx_status = -EBUSY;
+		wake_up(&pdphy->tx_waitq);
+	}
 done:
 	return IRQ_HANDLED;
 }
@@ -668,15 +675,6 @@
 			BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE);
 }
 
-static irqreturn_t pdphy_msg_rx_irq(int irq, void *data)
-{
-	struct usb_pdphy *pdphy = data;
-
-	pdphy->rx_busy = true;
-
-	return IRQ_WAKE_THREAD;
-}
-
 static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
 {
 	u8 size, rx_status, frame_type;
@@ -733,7 +731,6 @@
 		false);
 	pdphy->rx_bytes += size + 1;
 done:
-	pdphy->rx_busy = false;
 	return IRQ_HANDLED;
 }
 
@@ -819,7 +816,7 @@
 		return ret;
 
 	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
-		&pdphy->msg_rx_irq, "msg-rx", pdphy_msg_rx_irq,
+		&pdphy->msg_rx_irq, "msg-rx", NULL,
 		pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
 	if (ret < 0)
 		return ret;
diff --git a/drivers/usb/phy/phy-msm-qusb-v2.c b/drivers/usb/phy/phy-msm-qusb-v2.c
index 6103172..81c39a3 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -26,6 +26,7 @@
 #include <linux/regulator/machine.h>
 #include <linux/usb/phy.h>
 #include <linux/reset.h>
+#include <linux/debugfs.h>
 
 /* QUSB2PHY_PWR_CTRL1 register related bits */
 #define PWR_CTRL1_POWR_DOWN		BIT(0)
@@ -63,9 +64,13 @@
 #define LINESTATE_DP			BIT(0)
 #define LINESTATE_DM			BIT(1)
 
-unsigned int phy_tune1;
-module_param(phy_tune1, uint, 0644);
-MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1");
+#define BIAS_CTRL_2_OVERRIDE_VAL	0x28
+
+#define SQ_CTRL1_CHIRP_DISABLE		0x20
+#define SQ_CTRL2_CHIRP_DISABLE		0x80
+
+/* PERIPH_SS_PHY_REFGEN_NORTH_BG_CTRL register bits */
+#define BANDGAP_BYPASS			BIT(0)
 
 enum qusb_phy_reg {
 	PORT_TUNE1,
@@ -74,6 +79,9 @@
 	INTR_CTRL,
 	PLL_CORE_INPUT_OVERRIDE,
 	TEST1,
+	BIAS_CTRL_2,
+	SQ_CTRL1,
+	SQ_CTRL2,
 	USB2_PHY_REG_MAX,
 };
 
@@ -82,6 +90,7 @@
 	struct mutex		lock;
 	void __iomem		*base;
 	void __iomem		*efuse_reg;
+	void __iomem		*refgen_north_bg_reg;
 
 	struct clk		*ref_clk_src;
 	struct clk		*ref_clk;
@@ -113,6 +122,10 @@
 	struct regulator_desc	dpdm_rdesc;
 	struct regulator_dev	*dpdm_rdev;
 
+	u32			sq_ctrl1_default;
+	u32			sq_ctrl2_default;
+	bool			chirp_disable;
+
 	/* emulation targets specific */
 	void __iomem		*emu_phy_base;
 	bool			emulation;
@@ -122,6 +135,10 @@
 	int			phy_pll_reset_seq_len;
 	int			*emu_dcm_reset_seq;
 	int			emu_dcm_reset_seq_len;
+
+	/* override TUNEX registers value */
+	struct dentry		*root;
+	u8			tune[5];
 };
 
 static void qusb_phy_enable_clocks(struct qusb_phy *qphy, bool on)
@@ -403,7 +420,7 @@
 static int qusb_phy_init(struct usb_phy *phy)
 {
 	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
-	int ret;
+	int ret, p_index;
 	u8 reg;
 
 	dev_dbg(phy->dev, "%s\n", __func__);
@@ -458,14 +475,19 @@
 				qphy->base + qphy->phy_reg[PORT_TUNE1]);
 	}
 
-	/* If phy_tune1 modparam set, override tune1 value */
-	if (phy_tune1) {
-		pr_debug("%s(): (modparam) TUNE1 val:0x%02x\n",
-						__func__, phy_tune1);
-		writel_relaxed(phy_tune1,
-				qphy->base + qphy->phy_reg[PORT_TUNE1]);
+	/* if debugfs based tunex params are set, use that value. */
+	for (p_index = 0; p_index < 5; p_index++) {
+		if (qphy->tune[p_index])
+			writel_relaxed(qphy->tune[p_index],
+				qphy->base + qphy->phy_reg[PORT_TUNE1] +
+							(4 * p_index));
 	}
 
+	if (qphy->refgen_north_bg_reg)
+		if (readl_relaxed(qphy->refgen_north_bg_reg) & BANDGAP_BYPASS)
+			writel_relaxed(BIAS_CTRL_2_OVERRIDE_VAL,
+				qphy->base + qphy->phy_reg[BIAS_CTRL_2]);
+
 	/* ensure above writes are completed before re-enabling PHY */
 	wmb();
 
@@ -495,17 +517,8 @@
 
 	dev_dbg(phy->dev, "%s\n", __func__);
 
-	qusb_phy_enable_clocks(qphy, true);
+	qusb_phy_enable_power(qphy, false);
 
-	/* Disable the PHY */
-	writel_relaxed(readl_relaxed(qphy->base + qphy->phy_reg[PWR_CTRL1]) |
-			PWR_CTRL1_POWR_DOWN,
-			qphy->base + qphy->phy_reg[PWR_CTRL1]);
-
-	/* Makes sure that above write goes through */
-	wmb();
-
-	qusb_phy_enable_clocks(qphy, false);
 }
 
 static u32 qusb_phy_get_linestate(struct qusb_phy *qphy)
@@ -648,6 +661,52 @@
 	return 0;
 }
 
+static int qusb_phy_disable_chirp(struct usb_phy *phy, bool disable)
+{
+	struct qusb_phy *qphy = container_of(phy, struct qusb_phy, phy);
+	int ret = 0;
+
+	dev_dbg(phy->dev, "%s qphy chirp disable %d disable %d\n", __func__,
+			qphy->chirp_disable, disable);
+
+	mutex_lock(&qphy->lock);
+
+	if (qphy->chirp_disable == disable) {
+		ret = -EALREADY;
+		goto done;
+	}
+
+	qphy->chirp_disable = disable;
+
+	if (disable) {
+		qphy->sq_ctrl1_default =
+			readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL1]);
+		qphy->sq_ctrl2_default =
+			readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL2]);
+
+		writel_relaxed(SQ_CTRL1_CHIRP_DISABLE,
+				qphy->base + qphy->phy_reg[SQ_CTRL1]);
+		readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL1]);
+
+		writel_relaxed(SQ_CTRL1_CHIRP_DISABLE,
+				qphy->base + qphy->phy_reg[SQ_CTRL2]);
+		readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL2]);
+
+		goto done;
+	}
+
+	writel_relaxed(qphy->sq_ctrl1_default,
+			qphy->base + qphy->phy_reg[SQ_CTRL1]);
+	readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL1]);
+
+	writel_relaxed(qphy->sq_ctrl2_default,
+			qphy->base + qphy->phy_reg[SQ_CTRL2]);
+	readl_relaxed(qphy->base + qphy->phy_reg[SQ_CTRL2]);
+done:
+	mutex_unlock(&qphy->lock);
+	return ret;
+}
+
 static int qusb_phy_dpdm_regulator_enable(struct regulator_dev *rdev)
 {
 	int ret = 0;
@@ -733,6 +792,38 @@
 	return 0;
 }
 
+static int qusb_phy_create_debugfs(struct qusb_phy *qphy)
+{
+	struct dentry *file;
+	int ret = 0, i;
+	char name[6];
+
+	qphy->root = debugfs_create_dir(dev_name(qphy->phy.dev), NULL);
+	if (IS_ERR_OR_NULL(qphy->root)) {
+		dev_err(qphy->phy.dev,
+			"can't create debugfs root for %s\n",
+					dev_name(qphy->phy.dev));
+		ret = -ENOMEM;
+		goto create_err;
+	}
+
+	for (i = 0; i < 5; i++) {
+		snprintf(name, sizeof(name), "tune%d", (i + 1));
+		file = debugfs_create_x8(name, 0644, qphy->root,
+						&qphy->tune[i]);
+		if (IS_ERR_OR_NULL(file)) {
+			dev_err(qphy->phy.dev,
+				"can't create debugfs entry for %s\n", name);
+			debugfs_remove_recursive(qphy->root);
+			ret = ENOMEM;
+			goto create_err;
+		}
+	}
+
+create_err:
+	return ret;
+}
+
 static int qusb_phy_probe(struct platform_device *pdev)
 {
 	struct qusb_phy *qphy;
@@ -784,6 +875,12 @@
 		}
 	}
 
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					"refgen_north_bg_reg_addr");
+	if (res)
+		qphy->refgen_north_bg_reg = devm_ioremap(dev, res->start,
+						resource_size(res));
+
 	/* ref_clk_src is needed irrespective of SE_CLK or DIFF_CLK usage */
 	qphy->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
 	if (IS_ERR(qphy->ref_clk_src)) {
@@ -901,7 +998,7 @@
 		if (qphy->phy_reg) {
 			qphy->qusb_phy_reg_offset_cnt =
 				size / sizeof(*qphy->phy_reg);
-			if (qphy->qusb_phy_reg_offset_cnt > USB2_PHY_REG_MAX) {
+			if (qphy->qusb_phy_reg_offset_cnt != USB2_PHY_REG_MAX) {
 				dev_err(dev, "invalid reg offset count\n");
 				return -EINVAL;
 			}
@@ -995,6 +1092,7 @@
 	qphy->phy.type			= USB_PHY_TYPE_USB2;
 	qphy->phy.notify_connect        = qusb_phy_notify_connect;
 	qphy->phy.notify_disconnect     = qusb_phy_notify_disconnect;
+	qphy->phy.disable_chirp		= qusb_phy_disable_chirp;
 
 	ret = usb_add_phy_dev(&qphy->phy);
 	if (ret)
@@ -1004,6 +1102,8 @@
 	if (ret)
 		usb_remove_phy(&qphy->phy);
 
+	qusb_phy_create_debugfs(qphy);
+
 	return ret;
 }
 
@@ -1014,6 +1114,7 @@
 	usb_remove_phy(&qphy->phy);
 	qusb_phy_enable_clocks(qphy, false);
 	qusb_phy_enable_power(qphy, false);
+	debugfs_remove_recursive(qphy->root);
 
 	return 0;
 }
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index 59f5379..7e7c76c 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -623,10 +623,7 @@
 	}
 
 	if (suspend) {
-		if (!phy->cable_connected)
-			writel_relaxed(0x00,
-			phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
-		else
+		if (phy->cable_connected)
 			msm_ssusb_qmp_enable_autonomous(phy, 1);
 
 		/* Make sure above write completed with PHY */
@@ -674,6 +671,10 @@
 	struct msm_ssphy_qmp *phy = container_of(uphy, struct msm_ssphy_qmp,
 					phy);
 
+	writel_relaxed(0x00,
+		phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
+	readl_relaxed(phy->base + phy->phy_reg[USB3_PHY_POWER_DOWN_CONTROL]);
+
 	dev_dbg(uphy->dev, "QMP phy disconnect notification\n");
 	dev_dbg(uphy->dev, " cable_connected=%d\n", phy->cable_connected);
 	phy->cable_connected = false;
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 857e783..6c6a3a8 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -285,11 +285,26 @@
 			      struct usbhs_fifo *fifo)
 {
 	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int ret = 0;
 
-	if (!usbhs_pipe_is_dcp(pipe))
-		usbhsf_fifo_barrier(priv, fifo);
+	if (!usbhs_pipe_is_dcp(pipe)) {
+		/*
+		 * This driver checks the pipe condition first to avoid -EBUSY
+		 * from usbhsf_fifo_barrier() with about 10 msec delay in
+		 * the interrupt handler if the pipe is RX direction and empty.
+		 */
+		if (usbhs_pipe_is_dir_in(pipe))
+			ret = usbhs_pipe_is_accessible(pipe);
+		if (!ret)
+			ret = usbhsf_fifo_barrier(priv, fifo);
+	}
 
-	usbhs_write(priv, fifo->ctr, BCLR);
+	/*
+	 * if non-DCP pipe, this driver should set BCLR when
+	 * usbhsf_fifo_barrier() returns 0.
+	 */
+	if (!ret)
+		usbhs_write(priv, fifo->ctr, BCLR);
 }
 
 static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
@@ -845,9 +860,9 @@
 		fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
 
 	usbhs_pipe_running(pipe, 1);
-	usbhsf_dma_start(pipe, fifo);
 	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
 	dma_async_issue_pending(chan);
+	usbhsf_dma_start(pipe, fifo);
 	usbhs_pipe_enable(pipe);
 
 xfer_work_end:
diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c
index d544b33..02b67ab 100644
--- a/drivers/usb/renesas_usbhs/rcar3.c
+++ b/drivers/usb/renesas_usbhs/rcar3.c
@@ -20,9 +20,13 @@
 /* Low Power Status register (LPSTS) */
 #define LPSTS_SUSPM	0x4000
 
-/* USB General control register 2 (UGCTRL2), bit[31:6] should be 0 */
+/*
+ * USB General control register 2 (UGCTRL2)
+ * Remarks: bit[31:11] and bit[9:6] should be 0
+ */
 #define UGCTRL2_RESERVED_3	0x00000001	/* bit[3:0] should be B'0001 */
 #define UGCTRL2_USB0SEL_OTG	0x00000030
+#define UGCTRL2_VBUSSEL		0x00000400
 
 static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data)
 {
@@ -34,7 +38,8 @@
 {
 	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
 
-	usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG);
+	usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG |
+		      UGCTRL2_VBUSSEL);
 
 	if (enable) {
 		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index b6f1ade..76062ce 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -186,6 +186,7 @@
 	tty_kref_put(tty);
  reset_open_count:
 	port->port.count = 0;
+	info->port = NULL;
 	usb_autopm_put_interface(serial->interface);
  error_get_interface:
 	usb_serial_put(serial);
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 84b444f..11ee55e 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -136,6 +136,7 @@
 	{ USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */
 	{ USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */
 	{ USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */
+	{ USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */
 	{ USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */
 	{ USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */
 	{ USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */
@@ -170,6 +171,7 @@
 	{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
 	{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
 	{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
+	{ USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
 	{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
 	{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
 	{ USB_DEVICE(0x1901, 0x0194) },	/* GE Healthcare Remote Alarm Box */
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 1939496..3249f42 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -1015,6 +1015,8 @@
 	{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
 	{ USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
 		.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+	{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
+	{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
 	{ }					/* Terminating entry */
 };
 
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 4fcf1ce..f9d15bd 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -610,6 +610,13 @@
 #define ADI_GNICEPLUS_PID	0xF001
 
 /*
+ * Cypress WICED USB UART
+ */
+#define CYPRESS_VID			0x04B4
+#define CYPRESS_WICED_BT_USB_PID	0x009B
+#define CYPRESS_WICED_WL_USB_PID	0xF900
+
+/*
  * Microchip Technology, Inc.
  *
  * MICROCHIP_VID (0x04D8) and MICROCHIP_USB_BOARD_PID (0x000A) are
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index 39e6830..45182c6 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -45,6 +45,7 @@
 static const struct usb_device_id id_table[] = {
 	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_BI) },
 	{ USB_DEVICE(FOCUS_VENDOR_ID, FOCUS_PRODUCT_ID_UNI) },
+	{ USB_DEVICE_INTERFACE_CLASS(0x0c2e, 0x0730, 0xff) },	/* MS7820 */
 	{ }, /* Terminating entry. */
 };
 MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 136ff5e..135eb04 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -234,11 +234,16 @@
 
 	status = usb_control_msg(usbdev, pipe, request, requesttype, value,
 				     index, buf, 1, MOS_WDR_TIMEOUT);
-	if (status == 1)
+	if (status == 1) {
 		*data = *buf;
-	else if (status < 0)
+	} else {
 		dev_err(&usbdev->dev,
 			"mos7720: usb_control_msg() failed: %d\n", status);
+		if (status >= 0)
+			status = -EIO;
+		*data = 0;
+	}
+
 	kfree(buf);
 
 	return status;
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 5c4fc3a..6baacf6 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -285,9 +285,15 @@
 	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
 			      MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
 			      MOS_WDR_TIMEOUT);
+	if (ret < VENDOR_READ_LENGTH) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
+
 	*val = buf[0];
 	dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
-
+out:
 	kfree(buf);
 	return ret;
 }
@@ -353,8 +359,13 @@
 	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
 			      MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
 			      MOS_WDR_TIMEOUT);
+	if (ret < VENDOR_READ_LENGTH) {
+		if (ret >= 0)
+			ret = -EIO;
+		goto out;
+	}
 	*val = buf[0];
-
+out:
 	kfree(buf);
 	return ret;
 }
@@ -1490,10 +1501,10 @@
 		return -ENODEV;
 
 	status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
-	if (status != 1)
+	if (status < 0)
 		return -EIO;
 	status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
-	if (status != 1)
+	if (status < 0)
 		return -EIO;
 	result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
 	    | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index ebe51f11..db3d34c 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -522,6 +522,7 @@
 
 /* TP-LINK Incorporated products */
 #define TPLINK_VENDOR_ID			0x2357
+#define TPLINK_PRODUCT_LTE			0x000D
 #define TPLINK_PRODUCT_MA180			0x0201
 
 /* Changhong products */
@@ -2011,6 +2012,7 @@
 	{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
 	{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) },
 	{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) },	/* TP-Link LTE Module */
 	{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
 	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
 	{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000),					/* TP-Link MA260 */
@@ -2023,8 +2025,11 @@
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x02, 0x01) },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x7d03, 0xff, 0x00, 0x00) },
 	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) },			/* D-Link DWM-158 */
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d0e, 0xff) },			/* D-Link DWM-157 C1 */
 	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff),			/* D-Link DWM-221 B1 */
 	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+	{ USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff),			/* D-Link DWM-222 */
+	  .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */
 	{ USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 1db4b61..a51b283 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -49,6 +49,7 @@
 	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID) },
 	{ USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) },
 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID) },
+	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485) },
 	{ USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) },
 	{ USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) },
 	{ USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) },
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 09d9be8..3b5a15d 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -27,6 +27,7 @@
 #define ATEN_VENDOR_ID		0x0557
 #define ATEN_VENDOR_ID2		0x0547
 #define ATEN_PRODUCT_ID		0x2008
+#define ATEN_PRODUCT_UC485	0x2021
 #define ATEN_PRODUCT_ID2	0x2118
 
 #define IODATA_VENDOR_ID	0x04bb
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 652b433..e1c1e32 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -174,6 +174,10 @@
 	{DEVICE_SWI(0x413c, 0x81b3)},	/* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
 	{DEVICE_SWI(0x413c, 0x81b5)},	/* Dell Wireless 5811e QDL */
 	{DEVICE_SWI(0x413c, 0x81b6)},	/* Dell Wireless 5811e QDL */
+	{DEVICE_SWI(0x413c, 0x81cf)},   /* Dell Wireless 5819 */
+	{DEVICE_SWI(0x413c, 0x81d0)},   /* Dell Wireless 5819 */
+	{DEVICE_SWI(0x413c, 0x81d1)},   /* Dell Wireless 5818 */
+	{DEVICE_SWI(0x413c, 0x81d2)},   /* Dell Wireless 5818 */
 
 	/* Huawei devices */
 	{DEVICE_HWI(0x03f0, 0x581d)},	/* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 1a59f33..a3ccb89 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -834,13 +834,25 @@
 			if (result == USB_STOR_TRANSPORT_GOOD) {
 				srb->result = SAM_STAT_GOOD;
 				srb->sense_buffer[0] = 0x0;
+			}
+
+			/*
+			 * ATA-passthru commands use sense data to report
+			 * the command completion status, and often devices
+			 * return Check Condition status when nothing is
+			 * wrong.
+			 */
+			else if (srb->cmnd[0] == ATA_16 ||
+					srb->cmnd[0] == ATA_12) {
+				/* leave the data alone */
+			}
 
 			/*
 			 * If there was a problem, report an unspecified
 			 * hardware error to prevent the higher layers from
 			 * entering an infinite retry loop.
 			 */
-			} else {
+			else {
 				srb->result = DID_ERROR << 16;
 				if ((sshdr.response_code & 0x72) == 0x72)
 					srb->sense_buffer[1] = HARDWARE_ERROR;
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
index f58caa9..a155cd0 100644
--- a/drivers/usb/storage/uas-detect.h
+++ b/drivers/usb/storage/uas-detect.h
@@ -9,7 +9,8 @@
 		intf->desc.bInterfaceProtocol == USB_PR_UAS);
 }
 
-static int uas_find_uas_alt_setting(struct usb_interface *intf)
+static struct usb_host_interface *uas_find_uas_alt_setting(
+		struct usb_interface *intf)
 {
 	int i;
 
@@ -17,10 +18,10 @@
 		struct usb_host_interface *alt = &intf->altsetting[i];
 
 		if (uas_is_interface(alt))
-			return alt->desc.bAlternateSetting;
+			return alt;
 	}
 
-	return -ENODEV;
+	return NULL;
 }
 
 static int uas_find_endpoints(struct usb_host_interface *alt,
@@ -58,14 +59,14 @@
 	struct usb_device *udev = interface_to_usbdev(intf);
 	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
 	unsigned long flags = id->driver_info;
-	int r, alt;
-
+	struct usb_host_interface *alt;
+	int r;
 
 	alt = uas_find_uas_alt_setting(intf);
-	if (alt < 0)
+	if (!alt)
 		return 0;
 
-	r = uas_find_endpoints(&intf->altsetting[alt], eps);
+	r = uas_find_endpoints(alt, eps);
 	if (r < 0)
 		return 0;
 
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 5ef014b..9876af4 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -873,14 +873,14 @@
 static int uas_switch_interface(struct usb_device *udev,
 				struct usb_interface *intf)
 {
-	int alt;
+	struct usb_host_interface *alt;
 
 	alt = uas_find_uas_alt_setting(intf);
-	if (alt < 0)
-		return alt;
+	if (!alt)
+		return -ENODEV;
 
-	return usb_set_interface(udev,
-			intf->altsetting[0].desc.bInterfaceNumber, alt);
+	return usb_set_interface(udev, alt->desc.bInterfaceNumber,
+			alt->desc.bAlternateSetting);
 }
 
 static int uas_configure_endpoints(struct uas_dev_info *devinfo)
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 9129f6c..2572fd5 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1459,6 +1459,13 @@
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
 		US_FL_SANE_SENSE ),
 
+/* Reported by Kris Lindgren <kris.lindgren@gmail.com> */
+UNUSUAL_DEV( 0x0bc2, 0x3332, 0x0000, 0x9999,
+		"Seagate",
+		"External",
+		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+		US_FL_NO_WP_DETECT ),
+
 UNUSUAL_DEV(  0x0d49, 0x7310, 0x0000, 0x9999,
 		"Maxtor",
 		"USB to SATA",
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
index cbea9f3..cde1153 100644
--- a/drivers/usb/storage/unusual_uas.h
+++ b/drivers/usb/storage/unusual_uas.h
@@ -124,9 +124,9 @@
 /* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */
 UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999,
 		"Initio Corporation",
-		"",
+		"INIC-3069",
 		USB_SC_DEVICE, USB_PR_DEVICE, NULL,
-		US_FL_NO_ATA_1X),
+		US_FL_NO_ATA_1X | US_FL_IGNORE_RESIDUE),
 
 /* Reported-by: Tom Arild Naess <tanaess@gmail.com> */
 UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999,
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
index 35a1e77..9a53912 100644
--- a/drivers/uwb/hwa-rc.c
+++ b/drivers/uwb/hwa-rc.c
@@ -825,6 +825,8 @@
 
 	if (iface->cur_altsetting->desc.bNumEndpoints < 1)
 		return -ENODEV;
+	if (!usb_endpoint_xfer_int(&iface->cur_altsetting->endpoint[0].desc))
+		return -ENODEV;
 
 	result = -ENOMEM;
 	uwb_rc = uwb_rc_alloc();
diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c
index 01c20a2..39dd4ef 100644
--- a/drivers/uwb/uwbd.c
+++ b/drivers/uwb/uwbd.c
@@ -302,18 +302,22 @@
 /** Start the UWB daemon */
 void uwbd_start(struct uwb_rc *rc)
 {
-	rc->uwbd.task = kthread_run(uwbd, rc, "uwbd");
-	if (rc->uwbd.task == NULL)
+	struct task_struct *task = kthread_run(uwbd, rc, "uwbd");
+	if (IS_ERR(task)) {
+		rc->uwbd.task = NULL;
 		printk(KERN_ERR "UWB: Cannot start management daemon; "
 		       "UWB won't work\n");
-	else
+	} else {
+		rc->uwbd.task = task;
 		rc->uwbd.pid = rc->uwbd.task->pid;
+	}
 }
 
 /* Stop the UWB daemon and free any unprocessed events */
 void uwbd_stop(struct uwb_rc *rc)
 {
-	kthread_stop(rc->uwbd.task);
+	if (rc->uwbd.task)
+		kthread_stop(rc->uwbd.task);
 	uwbd_flush(rc);
 }
 
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 5dc128a..96a0661 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -537,8 +537,13 @@
 
 		preempt_enable();
 
-		if (vhost_enable_notify(&net->dev, vq))
+		if (!vhost_vq_avail_empty(&net->dev, vq))
 			vhost_poll_queue(&vq->poll);
+		else if (unlikely(vhost_enable_notify(&net->dev, vq))) {
+			vhost_disable_notify(&net->dev, vq);
+			vhost_poll_queue(&vq->poll);
+		}
+
 		mutex_unlock(&vq->mutex);
 
 		len = peek_head_len(sk);
diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c
index 11026e7..81367cf 100644
--- a/drivers/video/fbdev/aty/atyfb_base.c
+++ b/drivers/video/fbdev/aty/atyfb_base.c
@@ -1861,7 +1861,7 @@
 #if defined(DEBUG) && defined(CONFIG_FB_ATY_CT)
 	case ATYIO_CLKR:
 		if (M64_HAS(INTEGRATED)) {
-			struct atyclk clk;
+			struct atyclk clk = { 0 };
 			union aty_pll *pll = &par->pll;
 			u32 dsp_config = pll->ct.dsp_config;
 			u32 dsp_on_off = pll->ct.dsp_on_off;
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index 76c1ad9..f8a3839 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1097,6 +1097,13 @@
 	void __user *argp = (void __user *)arg;
 	long ret = 0;
 
+	memset(&var, 0, sizeof(var));
+	memset(&fix, 0, sizeof(fix));
+	memset(&con2fb, 0, sizeof(con2fb));
+	memset(&cmap_from, 0, sizeof(cmap_from));
+	memset(&cmap, 0, sizeof(cmap));
+	memset(&event, 0, sizeof(event));
+
 	switch (cmd) {
 	case FBIOGET_VSCREENINFO:
 		if (!lock_fb_info(info))
diff --git a/drivers/video/fbdev/pmag-ba-fb.c b/drivers/video/fbdev/pmag-ba-fb.c
index 5872bc4..df02fb4 100644
--- a/drivers/video/fbdev/pmag-ba-fb.c
+++ b/drivers/video/fbdev/pmag-ba-fb.c
@@ -129,7 +129,7 @@
 /*
  * Turn the hardware cursor off.
  */
-static void __init pmagbafb_erase_cursor(struct fb_info *info)
+static void pmagbafb_erase_cursor(struct fb_info *info)
 {
 	struct pmagbafb_par *par = info->par;
 
diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c
index 8e302d0..3efa295 100644
--- a/drivers/watchdog/kempld_wdt.c
+++ b/drivers/watchdog/kempld_wdt.c
@@ -140,12 +140,19 @@
 					unsigned int timeout)
 {
 	struct kempld_device_data *pld = wdt_data->pld;
-	u32 prescaler = kempld_prescaler[PRESCALER_21];
+	u32 prescaler;
 	u64 stage_timeout64;
 	u32 stage_timeout;
 	u32 remainder;
 	u8 stage_cfg;
 
+#if GCC_VERSION < 40400
+	/* work around a bug compiling do_div() */
+	prescaler = READ_ONCE(kempld_prescaler[PRESCALER_21]);
+#else
+	prescaler = kempld_prescaler[PRESCALER_21];
+#endif
+
 	if (!stage)
 		return -EINVAL;
 
diff --git a/drivers/xen/biomerge.c b/drivers/xen/biomerge.c
index 4da69db..1bdd02a 100644
--- a/drivers/xen/biomerge.c
+++ b/drivers/xen/biomerge.c
@@ -10,8 +10,7 @@
 	unsigned long bfn1 = pfn_to_bfn(page_to_pfn(vec1->bv_page));
 	unsigned long bfn2 = pfn_to_bfn(page_to_pfn(vec2->bv_page));
 
-	return __BIOVEC_PHYS_MERGEABLE(vec1, vec2) &&
-		((bfn1 == bfn2) || ((bfn1+1) == bfn2));
+	return bfn1 + PFN_DOWN(vec1->bv_offset + vec1->bv_len) == bfn2;
 #else
 	/*
 	 * XXX: Add support for merging bio_vec when using different page
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 2ef2b61..79b8ab4 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -1030,6 +1030,7 @@
 	mutex_unlock(&priv->lock);
 
 	if (use_ptemod) {
+		map->pages_vm_start = vma->vm_start;
 		err = apply_to_page_range(vma->vm_mm, vma->vm_start,
 					  vma->vm_end - vma->vm_start,
 					  find_grant_ptes, map);
@@ -1067,7 +1068,6 @@
 					    set_grant_ptes_as_special, NULL);
 		}
 #endif
-		map->pages_vm_start = vma->vm_start;
 	}
 
 	return 0;
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 26e5e85..9122ba2 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -277,8 +277,16 @@
 	err = xenbus_transaction_start(&xbt);
 	if (err)
 		return;
-	if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) {
-		pr_err("Unable to read sysrq code in control/sysrq\n");
+	err = xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key);
+	if (err < 0) {
+		/*
+		 * The Xenstore watch fires directly after registering it and
+		 * after a suspend/resume cycle. So ENOENT is no error but
+		 * might happen in those cases.
+		 */
+		if (err != -ENOENT)
+			pr_err("Error %d reading sysrq code in control/sysrq\n",
+			       err);
 		xenbus_transaction_end(xbt, 1);
 		return;
 	}
diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 679f79f..b68ced5 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -680,3 +680,22 @@
 	return 0;
 }
 EXPORT_SYMBOL_GPL(xen_swiotlb_set_dma_mask);
+
+/*
+ * Create userspace mapping for the DMA-coherent memory.
+ * This function should be called with the pages from the current domain only,
+ * passing pages mapped from other domains would lead to memory corruption.
+ */
+int
+xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma,
+		     void *cpu_addr, dma_addr_t dma_addr, size_t size,
+		     unsigned long attrs)
+{
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+	if (__generic_dma_ops(dev)->mmap)
+		return __generic_dma_ops(dev)->mmap(dev, vma, cpu_addr,
+						    dma_addr, size, attrs);
+#endif
+	return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
+}
+EXPORT_SYMBOL_GPL(xen_swiotlb_dma_mmap);
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 07e46b7..cb936c9 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -450,10 +450,12 @@
 
 	set_page_writeback(page);
 	result = ops->rw_page(bdev, sector + get_start_sect(bdev), page, true);
-	if (result)
+	if (result) {
 		end_page_writeback(page);
-	else
+	} else {
+		clean_page_buffers(page);
 		unlock_page(page);
+	}
 	blk_queue_exit(bdev->bd_queue);
 	return result;
 }
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 14a37ff..705bb5f 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4759,10 +4759,6 @@
 		else
 			flush = BTRFS_RESERVE_NO_FLUSH;
 		spin_lock(&space_info->lock);
-		if (can_overcommit(root, space_info, orig, flush)) {
-			spin_unlock(&space_info->lock);
-			break;
-		}
 		if (list_empty(&space_info->tickets) &&
 		    list_empty(&space_info->priority_tickets)) {
 			spin_unlock(&space_info->lock);
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 8a05fa7..f089d7d 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -8050,8 +8050,10 @@
 
 		start += sectorsize;
 
-		if (nr_sectors--) {
+		nr_sectors--;
+		if (nr_sectors) {
 			pgoff += sectorsize;
+			ASSERT(pgoff < PAGE_SIZE);
 			goto next_block_or_try_again;
 		}
 	}
@@ -8157,8 +8159,10 @@
 
 		ASSERT(nr_sectors);
 
-		if (--nr_sectors) {
+		nr_sectors--;
+		if (nr_sectors) {
 			pgoff += sectorsize;
+			ASSERT(pgoff < PAGE_SIZE);
 			goto next_block;
 		}
 	}
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index 1782804..0fe346c 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3052,7 +3052,7 @@
 out:
 	if (ret)
 		btrfs_cmp_data_free(cmp);
-	return 0;
+	return ret;
 }
 
 static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,
@@ -4082,6 +4082,10 @@
 		ret = PTR_ERR(new_root);
 		goto out;
 	}
+	if (!is_fstree(new_root->objectid)) {
+		ret = -ENOENT;
+		goto out;
+	}
 
 	path = btrfs_alloc_path();
 	if (!path) {
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 2cf5e14..04c61bc 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -2367,11 +2367,11 @@
 	while (!list_empty(list)) {
 		reloc_root = list_entry(list->next, struct btrfs_root,
 					root_list);
+		__del_reloc_root(reloc_root);
 		free_extent_buffer(reloc_root->node);
 		free_extent_buffer(reloc_root->commit_root);
 		reloc_root->node = NULL;
 		reloc_root->commit_root = NULL;
-		__del_reloc_root(reloc_root);
 	}
 }
 
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index 71261b4..77f9efc 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1680,6 +1680,9 @@
 {
 	int ret;
 
+	if (ino == BTRFS_FIRST_FREE_OBJECTID)
+		return 1;
+
 	ret = get_cur_inode_state(sctx, ino, gen);
 	if (ret < 0)
 		goto out;
@@ -1865,7 +1868,7 @@
 	 * not deleted and then re-created, if it was then we have no overwrite
 	 * and we can just unlink this entry.
 	 */
-	if (sctx->parent_root) {
+	if (sctx->parent_root && dir != BTRFS_FIRST_FREE_OBJECTID) {
 		ret = get_inode_info(sctx->parent_root, dir, NULL, &gen, NULL,
 				     NULL, NULL, NULL);
 		if (ret < 0 && ret != -ENOENT)
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 74ed5aa..f6e1119 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1834,6 +1834,8 @@
 			goto restore;
 		}
 
+		btrfs_qgroup_rescan_resume(fs_info);
+
 		if (!fs_info->uuid_root) {
 			btrfs_info(fs_info, "creating UUID tree");
 			ret = btrfs_create_uuid_tree(fs_info);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 71a60cc..06a77e4 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6226,7 +6226,7 @@
 	for (dev_nr = 0; dev_nr < total_devs; dev_nr++) {
 		dev = bbio->stripes[dev_nr].dev;
 		if (!dev || !dev->bdev ||
-		    (bio_op(bio) == REQ_OP_WRITE && !dev->writeable)) {
+		    (bio_op(first_bio) == REQ_OP_WRITE && !dev->writeable)) {
 			bbio_error(bbio, first_bio, logical);
 			continue;
 		}
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index 900ffaf..7b79a54 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -188,7 +188,7 @@
 /*
  * read a single page, without unlocking it.
  */
-static int readpage_nounlock(struct file *filp, struct page *page)
+static int ceph_do_readpage(struct file *filp, struct page *page)
 {
 	struct inode *inode = file_inode(filp);
 	struct ceph_inode_info *ci = ceph_inode(inode);
@@ -218,7 +218,7 @@
 
 	err = ceph_readpage_from_fscache(inode, page);
 	if (err == 0)
-		goto out;
+		return -EINPROGRESS;
 
 	dout("readpage inode %p file %p page %p index %lu\n",
 	     inode, filp, page, page->index);
@@ -248,8 +248,11 @@
 
 static int ceph_readpage(struct file *filp, struct page *page)
 {
-	int r = readpage_nounlock(filp, page);
-	unlock_page(page);
+	int r = ceph_do_readpage(filp, page);
+	if (r != -EINPROGRESS)
+		unlock_page(page);
+	else
+		r = 0;
 	return r;
 }
 
@@ -1235,7 +1238,7 @@
 			goto retry_locked;
 		r = writepage_nounlock(page, NULL);
 		if (r < 0)
-			goto fail_nosnap;
+			goto fail_unlock;
 		goto retry_locked;
 	}
 
@@ -1263,11 +1266,14 @@
 	}
 
 	/* we need to read it. */
-	r = readpage_nounlock(file, page);
-	if (r < 0)
-		goto fail_nosnap;
+	r = ceph_do_readpage(file, page);
+	if (r < 0) {
+		if (r == -EINPROGRESS)
+			return -EAGAIN;
+		goto fail_unlock;
+	}
 	goto retry_locked;
-fail_nosnap:
+fail_unlock:
 	unlock_page(page);
 	return r;
 }
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
index 5bc5d37..a2d7997 100644
--- a/fs/ceph/cache.c
+++ b/fs/ceph/cache.c
@@ -240,13 +240,7 @@
 	}
 }
 
-static void ceph_vfs_readpage_complete(struct page *page, void *data, int error)
-{
-	if (!error)
-		SetPageUptodate(page);
-}
-
-static void ceph_vfs_readpage_complete_unlock(struct page *page, void *data, int error)
+static void ceph_readpage_from_fscache_complete(struct page *page, void *data, int error)
 {
 	if (!error)
 		SetPageUptodate(page);
@@ -274,7 +268,7 @@
 		return -ENOBUFS;
 
 	ret = fscache_read_or_alloc_page(ci->fscache, page,
-					 ceph_vfs_readpage_complete, NULL,
+					 ceph_readpage_from_fscache_complete, NULL,
 					 GFP_KERNEL);
 
 	switch (ret) {
@@ -303,7 +297,7 @@
 		return -ENOBUFS;
 
 	ret = fscache_read_or_alloc_pages(ci->fscache, mapping, pages, nr_pages,
-					  ceph_vfs_readpage_complete_unlock,
+					  ceph_readpage_from_fscache_complete,
 					  NULL, mapping_gfp_mask(mapping));
 
 	switch (ret) {
diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
index 03951f9..3e1c136 100644
--- a/fs/ceph/caps.c
+++ b/fs/ceph/caps.c
@@ -1900,6 +1900,7 @@
 retry:
 	spin_lock(&ci->i_ceph_lock);
 	if (ci->i_ceph_flags & CEPH_I_NOFLUSH) {
+		spin_unlock(&ci->i_ceph_lock);
 		dout("try_flush_caps skipping %p I_NOFLUSH set\n", inode);
 		goto out;
 	}
@@ -1917,8 +1918,10 @@
 			mutex_lock(&session->s_mutex);
 			goto retry;
 		}
-		if (cap->session->s_state < CEPH_MDS_SESSION_OPEN)
+		if (cap->session->s_state < CEPH_MDS_SESSION_OPEN) {
+			spin_unlock(&ci->i_ceph_lock);
 			goto out;
+		}
 
 		flushing = __mark_caps_flushing(inode, session, true,
 						&flush_tid, &oldest_flush_tid);
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 953275b..4a6df2c 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -1323,8 +1323,8 @@
 				ceph_dir_clear_ordered(dir);
 				dout("d_delete %p\n", dn);
 				d_delete(dn);
-			} else {
-				if (have_lease && d_unhashed(dn))
+			} else if (have_lease) {
+				if (d_unhashed(dn))
 					d_add(dn, NULL);
 				update_dentry_lease(dn, rinfo->dlease,
 						    session,
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index 7d752d5..4c9c72f 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -25,7 +25,7 @@
 		l.stripe_count = ci->i_layout.stripe_count;
 		l.object_size = ci->i_layout.object_size;
 		l.data_pool = ci->i_layout.pool_id;
-		l.preferred_osd = (s32)-1;
+		l.preferred_osd = -1;
 		if (copy_to_user(arg, &l, sizeof(l)))
 			return -EFAULT;
 	}
@@ -97,7 +97,7 @@
 		nl.data_pool = ci->i_layout.pool_id;
 
 	/* this is obsolete, and always -1 */
-	nl.preferred_osd = le64_to_cpu(-1);
+	nl.preferred_osd = -1;
 
 	err = __validate_layout(mdsc, &nl);
 	if (err)
diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
index e3e1a80..c0f52c4 100644
--- a/fs/ceph/mds_client.c
+++ b/fs/ceph/mds_client.c
@@ -1782,13 +1782,18 @@
 			     int *pfreepath)
 {
 	char *path;
+	struct inode *dir;
 
-	if (ceph_snap(d_inode(dentry->d_parent)) == CEPH_NOSNAP) {
-		*pino = ceph_ino(d_inode(dentry->d_parent));
+	rcu_read_lock();
+	dir = d_inode_rcu(dentry->d_parent);
+	if (dir && ceph_snap(dir) == CEPH_NOSNAP) {
+		*pino = ceph_ino(dir);
+		rcu_read_unlock();
 		*ppath = dentry->d_name.name;
 		*ppathlen = dentry->d_name.len;
 		return 0;
 	}
+	rcu_read_unlock();
 	path = ceph_mdsc_build_path(dentry, ppathlen, pino, 1);
 	if (IS_ERR(path))
 		return PTR_ERR(path);
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index c0c2530..87658f6 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1360,7 +1360,7 @@
 	exit_cifs_idmap();
 #endif
 #ifdef CONFIG_CIFS_UPCALL
-	unregister_key_type(&cifs_spnego_key_type);
+	exit_cifs_spnego();
 #endif
 	cifs_destroy_request_bufs();
 	cifs_destroy_mids();
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 48ef401..7b496a4 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -365,6 +365,8 @@
 	unsigned int (*calc_smb_size)(void *);
 	/* check for STATUS_PENDING and process it in a positive case */
 	bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
+	/* check for STATUS_NETWORK_SESSION_EXPIRED */
+	bool (*is_session_expired)(char *);
 	/* send oplock break response */
 	int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
 			       struct cifsInodeInfo *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 1f91c9d..cc420d6 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1457,6 +1457,13 @@
 		return length;
 	server->total_read += length;
 
+	if (server->ops->is_session_expired &&
+	    server->ops->is_session_expired(buf)) {
+		cifs_reconnect(server);
+		wake_up(&server->response_q);
+		return -1;
+	}
+
 	if (server->ops->is_status_pending &&
 	    server->ops->is_status_pending(buf, server, 0)) {
 		discard_remaining_data(server);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 1a54569..580b3a4 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -796,6 +796,13 @@
 		cifs_dump_mem("Bad SMB: ", buf,
 			min_t(unsigned int, server->total_read, 48));
 
+	if (server->ops->is_session_expired &&
+	    server->ops->is_session_expired(buf)) {
+		cifs_reconnect(server);
+		wake_up(&server->response_q);
+		return -1;
+	}
+
 	if (server->ops->is_status_pending &&
 	    server->ops->is_status_pending(buf, server, length))
 		return -1;
@@ -4071,6 +4078,14 @@
 	cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
 		 server->sec_mode, server->capabilities, server->timeAdj);
 
+	if (ses->auth_key.response) {
+		cifs_dbg(VFS, "Free previous auth_key.response = %p\n",
+			 ses->auth_key.response);
+		kfree(ses->auth_key.response);
+		ses->auth_key.response = NULL;
+		ses->auth_key.len = 0;
+	}
+
 	if (server->ops->sess_setup)
 		rc = server->ops->sess_setup(xid, ses, nls_info);
 
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 789ff1d..d9cbda2 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -183,15 +183,21 @@
 }
 
 /*
+ * Don't allow path components longer than the server max.
  * Don't allow the separator character in a path component.
  * The VFS will not allow "/", but "\" is allowed by posix.
  */
 static int
-check_name(struct dentry *direntry)
+check_name(struct dentry *direntry, struct cifs_tcon *tcon)
 {
 	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
 	int i;
 
+	if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength &&
+		     direntry->d_name.len >
+		     le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength)))
+		return -ENAMETOOLONG;
+
 	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
 		for (i = 0; i < direntry->d_name.len; i++) {
 			if (direntry->d_name.name[i] == '\\') {
@@ -489,10 +495,6 @@
 		return finish_no_open(file, res);
 	}
 
-	rc = check_name(direntry);
-	if (rc)
-		return rc;
-
 	xid = get_xid();
 
 	cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
@@ -505,6 +507,11 @@
 	}
 
 	tcon = tlink_tcon(tlink);
+
+	rc = check_name(direntry, tcon);
+	if (rc)
+		goto out;
+
 	server = tcon->ses->server;
 
 	if (server->ops->new_lease_key)
@@ -765,7 +772,7 @@
 	}
 	pTcon = tlink_tcon(tlink);
 
-	rc = check_name(direntry);
+	rc = check_name(direntry, pTcon);
 	if (rc)
 		goto lookup_out;
 
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 3925758..cf192f9 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -224,6 +224,13 @@
 	if (backup_cred(cifs_sb))
 		create_options |= CREATE_OPEN_BACKUP_INTENT;
 
+	/* O_SYNC also has bit for O_DSYNC so following check picks up either */
+	if (f_flags & O_SYNC)
+		create_options |= CREATE_WRITE_THROUGH;
+
+	if (f_flags & O_DIRECT)
+		create_options |= CREATE_NO_BUFFER;
+
 	oparms.tcon = tcon;
 	oparms.cifs_sb = cifs_sb;
 	oparms.desired_access = desired_access;
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index b696824..812e488 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1018,6 +1018,18 @@
 	return true;
 }
 
+static bool
+smb2_is_session_expired(char *buf)
+{
+	struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+
+	if (hdr->Status != STATUS_NETWORK_SESSION_EXPIRED)
+		return false;
+
+	cifs_dbg(FYI, "Session expired\n");
+	return true;
+}
+
 static int
 smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
 		     struct cifsInodeInfo *cinode)
@@ -1609,6 +1621,7 @@
 	.close_dir = smb2_close_dir,
 	.calc_smb_size = smb2_calc_size,
 	.is_status_pending = smb2_is_status_pending,
+	.is_session_expired = smb2_is_session_expired,
 	.oplock_response = smb2_oplock_response,
 	.queryfs = smb2_queryfs,
 	.mand_lock = smb2_mand_lock,
@@ -1690,6 +1703,7 @@
 	.close_dir = smb2_close_dir,
 	.calc_smb_size = smb2_calc_size,
 	.is_status_pending = smb2_is_status_pending,
+	.is_session_expired = smb2_is_session_expired,
 	.oplock_response = smb2_oplock_response,
 	.queryfs = smb2_queryfs,
 	.mand_lock = smb2_mand_lock,
@@ -1773,6 +1787,7 @@
 	.close_dir = smb2_close_dir,
 	.calc_smb_size = smb2_calc_size,
 	.is_status_pending = smb2_is_status_pending,
+	.is_session_expired = smb2_is_session_expired,
 	.oplock_response = smb2_oplock_response,
 	.queryfs = smb2_queryfs,
 	.mand_lock = smb2_mand_lock,
@@ -1862,6 +1877,7 @@
 	.close_dir = smb2_close_dir,
 	.calc_smb_size = smb2_calc_size,
 	.is_status_pending = smb2_is_status_pending,
+	.is_session_expired = smb2_is_session_expired,
 	.oplock_response = smb2_oplock_response,
 	.queryfs = smb2_queryfs,
 	.mand_lock = smb2_mand_lock,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 7c1c6c3..69b610ad 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -366,7 +366,7 @@
 	build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
 	req->NegotiateContextOffset = cpu_to_le32(OFFSET_OF_NEG_CONTEXT);
 	req->NegotiateContextCount = cpu_to_le16(2);
-	inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context) + 2
+	inc_rfc1001_len(req, 4 + sizeof(struct smb2_preauth_neg_context)
 			+ sizeof(struct smb2_encryption_neg_context)); /* calculate hash */
 }
 #else
@@ -531,15 +531,22 @@
 
 	/*
 	 * validation ioctl must be signed, so no point sending this if we
-	 * can not sign it.  We could eventually change this to selectively
+	 * can not sign it (ie are not known user).  Even if signing is not
+	 * required (enabled but not negotiated), in those cases we selectively
 	 * sign just this, the first and only signed request on a connection.
-	 * This is good enough for now since a user who wants better security
-	 * would also enable signing on the mount. Having validation of
-	 * negotiate info for signed connections helps reduce attack vectors
+	 * Having validation of negotiate info  helps reduce attack vectors.
 	 */
-	if (tcon->ses->server->sign == false)
+	if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
 		return 0; /* validation requires signing */
 
+	if (tcon->ses->user_name == NULL) {
+		cifs_dbg(FYI, "Can't validate negotiate: null user mount\n");
+		return 0; /* validation requires signing */
+	}
+
+	if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
+		cifs_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
+
 	vneg_inbuf.Capabilities =
 			cpu_to_le32(tcon->ses->server->vals->req_capabilities);
 	memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid,
@@ -1010,6 +1017,8 @@
 	while (sess_data->func)
 		sess_data->func(sess_data);
 
+	if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
+		cifs_dbg(VFS, "signing requested but authenticated as guest\n");
 	rc = sess_data->result;
 out:
 	kfree(sess_data);
@@ -2930,8 +2939,8 @@
 	kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
 			  le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
 	kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
-	kst->f_bfree  = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits);
-	kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
+	kst->f_bfree  = kst->f_bavail =
+			le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
 	return;
 }
 
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index dc0d141..1e1449a 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -84,8 +84,8 @@
 
 #define NUMBER_OF_SMB2_COMMANDS	0x0013
 
-/* BB FIXME - analyze following length BB */
-#define MAX_SMB2_HDR_SIZE 0x78 /* 4 len + 64 hdr + (2*24 wct) + 2 bct + 2 pad */
+/* 4 len + 52 transform hdr + 64 hdr + 56 create rsp */
+#define MAX_SMB2_HDR_SIZE 0x00b0
 
 #define SMB2_PROTO_NUMBER cpu_to_le32(0x424d53fe)
 #define SMB2_TRANSFORM_PROTO_NUM cpu_to_le32(0x424d53fd)
diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile
index f17684c..facf63c 100644
--- a/fs/crypto/Makefile
+++ b/fs/crypto/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_FS_ENCRYPTION)	+= fscrypto.o
 
+ccflags-y += -Ifs/ext4
 fscrypto-y := crypto.o fname.o policy.o keyinfo.o
diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c
index 61cfcce..5c24071 100644
--- a/fs/crypto/crypto.c
+++ b/fs/crypto/crypto.c
@@ -28,6 +28,7 @@
 #include <linux/dcache.h>
 #include <linux/namei.h>
 #include <linux/fscrypto.h>
+#include "ext4_ice.h"
 
 static unsigned int num_prealloc_crypto_pages = 32;
 static unsigned int num_prealloc_crypto_ctxs = 128;
@@ -406,6 +407,9 @@
 
 	bio_for_each_segment_all(bv, bio, i) {
 		struct page *page = bv->bv_page;
+	if (ext4_is_ice_enabled())
+		SetPageUptodate(page);
+	else {
 		int ret = fscrypt_decrypt_page(page);
 
 		if (ret) {
@@ -414,6 +418,7 @@
 		} else {
 			SetPageUptodate(page);
 		}
+	}
 		unlock_page(page);
 	}
 	fscrypt_release_ctx(ctx);
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index bb46063..106e55c 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -11,6 +11,7 @@
 #include <keys/user-type.h>
 #include <linux/scatterlist.h>
 #include <linux/fscrypto.h>
+#include "ext4_ice.h"
 
 static void derive_crypt_complete(struct crypto_async_request *req, int rc)
 {
@@ -108,6 +109,11 @@
 		goto out;
 	}
 	ukp = user_key_payload(keyring_key);
+	if (!ukp) {
+		/* key was revoked before we acquired its semaphore */
+		res = -EKEYREVOKED;
+		goto out;
+	}
 	if (ukp->datalen != sizeof(struct fscrypt_key)) {
 		res = -EINVAL;
 		goto out;
@@ -130,13 +136,17 @@
 }
 
 static int determine_cipher_type(struct fscrypt_info *ci, struct inode *inode,
-				 const char **cipher_str_ret, int *keysize_ret)
+		const char **cipher_str_ret, int *keysize_ret, int *fname)
 {
 	if (S_ISREG(inode->i_mode)) {
 		if (ci->ci_data_mode == FS_ENCRYPTION_MODE_AES_256_XTS) {
 			*cipher_str_ret = "xts(aes)";
 			*keysize_ret = FS_AES_256_XTS_KEY_SIZE;
 			return 0;
+		} else if (ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE) {
+			*cipher_str_ret = "bugon";
+			*keysize_ret = FS_AES_256_XTS_KEY_SIZE;
+			return 0;
 		}
 		pr_warn_once("fscrypto: unsupported contents encryption mode "
 			     "%d for inode %lu\n",
@@ -148,6 +158,7 @@
 		if (ci->ci_filename_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
 			*cipher_str_ret = "cts(cbc(aes))";
 			*keysize_ret = FS_AES_256_CTS_KEY_SIZE;
+			*fname = 1;
 			return 0;
 		}
 		pr_warn_once("fscrypto: unsupported filenames encryption mode "
@@ -167,9 +178,26 @@
 		return;
 
 	crypto_free_skcipher(ci->ci_ctfm);
+	memzero_explicit(ci->ci_raw_key,
+		sizeof(ci->ci_raw_key));
 	kmem_cache_free(fscrypt_info_cachep, ci);
 }
 
+static int fs_data_encryption_mode(void)
+{
+	return ext4_is_ice_enabled() ? FS_ENCRYPTION_MODE_PRIVATE :
+		FS_ENCRYPTION_MODE_AES_256_XTS;
+}
+
+int fs_using_hardware_encryption(struct inode *inode)
+{
+	struct fscrypt_info *ci = inode->i_crypt_info;
+
+	return S_ISREG(inode->i_mode) && ci &&
+		ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE;
+}
+EXPORT_SYMBOL(fs_using_hardware_encryption);
+
 int fscrypt_get_encryption_info(struct inode *inode)
 {
 	struct fscrypt_info *crypt_info;
@@ -177,8 +205,8 @@
 	struct crypto_skcipher *ctfm;
 	const char *cipher_str;
 	int keysize;
-	u8 *raw_key = NULL;
 	int res;
+	int fname = 0;
 
 	if (inode->i_crypt_info)
 		return 0;
@@ -195,7 +223,7 @@
 		if (!fscrypt_dummy_context_enabled(inode))
 			return res;
 		ctx.format = FS_ENCRYPTION_CONTEXT_FORMAT_V1;
-		ctx.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
+		ctx.contents_encryption_mode = fs_data_encryption_mode();
 		ctx.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
 		ctx.flags = 0;
 	} else if (res != sizeof(ctx)) {
@@ -219,7 +247,8 @@
 	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
 				sizeof(crypt_info->ci_master_key));
 
-	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize);
+	res = determine_cipher_type(crypt_info, inode, &cipher_str, &keysize,
+				&fname);
 	if (res)
 		goto out;
 
@@ -228,24 +257,21 @@
 	 * crypto API as part of key derivation.
 	 */
 	res = -ENOMEM;
-	raw_key = kmalloc(FS_MAX_KEY_SIZE, GFP_NOFS);
-	if (!raw_key)
-		goto out;
 
 	if (fscrypt_dummy_context_enabled(inode)) {
-		memset(raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
+		memset(crypt_info->ci_raw_key, 0x42, FS_AES_256_XTS_KEY_SIZE);
 		goto got_key;
 	}
 
-	res = validate_user_key(crypt_info, &ctx, raw_key,
+	res = validate_user_key(crypt_info, &ctx, crypt_info->ci_raw_key,
 			FS_KEY_DESC_PREFIX, FS_KEY_DESC_PREFIX_SIZE);
 	if (res && inode->i_sb->s_cop->key_prefix) {
 		u8 *prefix = NULL;
 		int prefix_size, res2;
 
 		prefix_size = inode->i_sb->s_cop->key_prefix(inode, &prefix);
-		res2 = validate_user_key(crypt_info, &ctx, raw_key,
-							prefix, prefix_size);
+		res2 = validate_user_key(crypt_info, &ctx,
+				crypt_info->ci_raw_key,	prefix, prefix_size);
 		if (res2) {
 			if (res2 == -ENOKEY)
 				res = -ENOKEY;
@@ -255,28 +281,33 @@
 		goto out;
 	}
 got_key:
-	ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
-	if (!ctfm || IS_ERR(ctfm)) {
-		res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
-		printk(KERN_DEBUG
-		       "%s: error %d (inode %u) allocating crypto tfm\n",
-		       __func__, res, (unsigned) inode->i_ino);
+	if (crypt_info->ci_data_mode != FS_ENCRYPTION_MODE_PRIVATE || fname) {
+		ctfm = crypto_alloc_skcipher(cipher_str, 0, 0);
+		if (!ctfm || IS_ERR(ctfm)) {
+			res = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
+			pr_err("%s: error %d inode %u allocating crypto tfm\n",
+				__func__, res, (unsigned int) inode->i_ino);
+			goto out;
+		}
+		crypt_info->ci_ctfm = ctfm;
+		crypto_skcipher_clear_flags(ctfm, ~0);
+		crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
+		res = crypto_skcipher_setkey(ctfm, crypt_info->ci_raw_key,
+						keysize);
+		if (res)
+			goto out;
+	} else if (!ext4_is_ice_enabled()) {
+		pr_warn("%s: ICE support not available\n",
+			__func__);
+		res = -EINVAL;
 		goto out;
 	}
-	crypt_info->ci_ctfm = ctfm;
-	crypto_skcipher_clear_flags(ctfm, ~0);
-	crypto_skcipher_set_flags(ctfm, CRYPTO_TFM_REQ_WEAK_KEY);
-	res = crypto_skcipher_setkey(ctfm, raw_key, keysize);
-	if (res)
-		goto out;
-
 	if (cmpxchg(&inode->i_crypt_info, NULL, crypt_info) == NULL)
 		crypt_info = NULL;
 out:
 	if (res == -ENOKEY)
 		res = 0;
 	put_crypt_info(crypt_info);
-	kzfree(raw_key);
 	return res;
 }
 EXPORT_SYMBOL(fscrypt_get_encryption_info);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index c60756e..bf03a92 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -411,6 +411,7 @@
 	if (dio->is_async && dio->op == REQ_OP_READ && dio->should_dirty)
 		bio_set_pages_dirty(bio);
 
+	bio->bi_dio_inode = dio->inode;
 	dio->bio_bdev = bio->bi_bdev;
 
 	if (sdio->submit_io) {
@@ -424,6 +425,18 @@
 	sdio->logical_offset_in_bio = 0;
 }
 
+struct inode *dio_bio_get_inode(struct bio *bio)
+{
+	struct inode *inode = NULL;
+
+	if (bio == NULL)
+		return NULL;
+
+	inode = bio->bi_dio_inode;
+
+	return inode;
+}
+EXPORT_SYMBOL(dio_bio_get_inode);
 /*
  * Release any resources in case of a failure
  */
@@ -835,7 +848,8 @@
 	 */
 	if (sdio->boundary) {
 		ret = dio_send_cur_page(dio, sdio, map_bh);
-		dio_bio_submit(dio, sdio);
+		if (sdio->bio)
+			dio_bio_submit(dio, sdio);
 		put_page(sdio->cur_page);
 		sdio->cur_page = NULL;
 	}
diff --git a/fs/dlm/user.c b/fs/dlm/user.c
index 58c2f4a..9ac6591 100644
--- a/fs/dlm/user.c
+++ b/fs/dlm/user.c
@@ -355,6 +355,10 @@
 	error = misc_register(&ls->ls_device);
 	if (error) {
 		kfree(ls->ls_device.name);
+		/* this has to be set to NULL
+		 * to avoid a double-free in dlm_device_deregister
+		 */
+		ls->ls_device.name = NULL;
 	}
 fail:
 	return error;
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 599a292..a896e46 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -84,11 +84,16 @@
 static inline struct ecryptfs_auth_tok *
 ecryptfs_get_encrypted_key_payload_data(struct key *key)
 {
-	if (key->type == &key_type_encrypted)
-		return (struct ecryptfs_auth_tok *)
-			(&((struct encrypted_key_payload *)key->payload.data[0])->payload_data);
-	else
+	struct encrypted_key_payload *payload;
+
+	if (key->type != &key_type_encrypted)
 		return NULL;
+
+	payload = key->payload.data[0];
+	if (!payload)
+		return ERR_PTR(-EKEYREVOKED);
+
+	return (struct ecryptfs_auth_tok *)payload->payload_data;
 }
 
 static inline struct key *ecryptfs_get_encrypted_key(char *sig)
@@ -114,12 +119,17 @@
 ecryptfs_get_key_payload_data(struct key *key)
 {
 	struct ecryptfs_auth_tok *auth_tok;
+	const struct user_key_payload *ukp;
 
 	auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
-	if (!auth_tok)
-		return (struct ecryptfs_auth_tok *)user_key_payload(key)->data;
-	else
+	if (auth_tok)
 		return auth_tok;
+
+	ukp = user_key_payload(key);
+	if (!ukp)
+		return ERR_PTR(-EKEYREVOKED);
+
+	return (struct ecryptfs_auth_tok *)ukp->data;
 }
 
 #define ECRYPTFS_MAX_KEYSET_SIZE 1024
diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c
index 3cf1546..fa218cd 100644
--- a/fs/ecryptfs/keystore.c
+++ b/fs/ecryptfs/keystore.c
@@ -459,7 +459,8 @@
  * @auth_tok_key: key containing the authentication token
  * @auth_tok: authentication token
  *
- * Returns zero on valid auth tok; -EINVAL otherwise
+ * Returns zero on valid auth tok; -EINVAL if the payload is invalid; or
+ * -EKEYREVOKED if the key was revoked before we acquired its semaphore.
  */
 static int
 ecryptfs_verify_auth_tok_from_key(struct key *auth_tok_key,
@@ -468,6 +469,12 @@
 	int rc = 0;
 
 	(*auth_tok) = ecryptfs_get_key_payload_data(auth_tok_key);
+	if (IS_ERR(*auth_tok)) {
+		rc = PTR_ERR(*auth_tok);
+		*auth_tok = NULL;
+		goto out;
+	}
+
 	if (ecryptfs_verify_version((*auth_tok)->version)) {
 		printk(KERN_ERR "Data structure version mismatch. Userspace "
 		       "tools must match eCryptfs kernel module with major "
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index dc4a34f..5b96ba7 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -524,8 +524,13 @@
 	wait_queue_head_t *whead;
 
 	rcu_read_lock();
-	/* If it is cleared by POLLFREE, it should be rcu-safe */
-	whead = rcu_dereference(pwq->whead);
+	/*
+	 * If it is cleared by POLLFREE, it should be rcu-safe.
+	 * If we read NULL we need a barrier paired with
+	 * smp_store_release() in ep_poll_callback(), otherwise
+	 * we rely on whead->lock.
+	 */
+	whead = smp_load_acquire(&pwq->whead);
 	if (whead)
 		remove_wait_queue(whead, &pwq->wait);
 	rcu_read_unlock();
@@ -1010,17 +1015,6 @@
 	struct eventpoll *ep = epi->ep;
 	int ewake = 0;
 
-	if ((unsigned long)key & POLLFREE) {
-		ep_pwq_from_wait(wait)->whead = NULL;
-		/*
-		 * whead = NULL above can race with ep_remove_wait_queue()
-		 * which can do another remove_wait_queue() after us, so we
-		 * can't use __remove_wait_queue(). whead->lock is held by
-		 * the caller.
-		 */
-		list_del_init(&wait->task_list);
-	}
-
 	spin_lock_irqsave(&ep->lock, flags);
 
 	/*
@@ -1102,10 +1096,26 @@
 	if (pwake)
 		ep_poll_safewake(&ep->poll_wait);
 
-	if (epi->event.events & EPOLLEXCLUSIVE)
-		return ewake;
+	if (!(epi->event.events & EPOLLEXCLUSIVE))
+		ewake = 1;
 
-	return 1;
+	if ((unsigned long)key & POLLFREE) {
+		/*
+		 * If we race with ep_remove_wait_queue() it can miss
+		 * ->whead = NULL and do another remove_wait_queue() after
+		 * us, so we can't use __remove_wait_queue().
+		 */
+		list_del_init(&wait->task_list);
+		/*
+		 * ->whead != NULL protects us from the race with ep_free()
+		 * or ep_remove(), ep_remove_wait_queue() takes whead->lock
+		 * held by the caller. Once we nullify it, nothing protects
+		 * ep/epi or even wait.
+		 */
+		smp_store_release(&ep_pwq_from_wait(wait)->whead, NULL);
+	}
+
+	return ewake;
 }
 
 /*
diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig
index e38039f..e9232a0 100644
--- a/fs/ext4/Kconfig
+++ b/fs/ext4/Kconfig
@@ -109,10 +109,16 @@
 	  decrypted pages in the page cache.
 
 config EXT4_FS_ENCRYPTION
-	bool
-	default y
+	bool "Ext4 FS Encryption"
+	default n
 	depends on EXT4_ENCRYPTION
 
+config EXT4_FS_ICE_ENCRYPTION
+	bool "Ext4 Encryption with ICE support"
+	default n
+	depends on EXT4_FS_ENCRYPTION
+	depends on PFK
+
 config EXT4_DEBUG
 	bool "EXT4 debugging support"
 	depends on EXT4_FS
diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 354103f..d9e563a 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -1,6 +1,7 @@
 #
 # Makefile for the linux ext4-filesystem routines.
 #
+ccflags-y += -Ifs/crypto
 
 obj-$(CONFIG_EXT4_FS) += ext4.o
 
@@ -12,3 +13,4 @@
 
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
+ext4-$(CONFIG_EXT4_FS_ICE_ENCRYPTION)   += ext4_ice.o
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index dfa5199..dfd01ca 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -192,13 +192,6 @@
 	switch (type) {
 	case ACL_TYPE_ACCESS:
 		name_index = EXT4_XATTR_INDEX_POSIX_ACL_ACCESS;
-		if (acl) {
-			error = posix_acl_update_mode(inode, &inode->i_mode, &acl);
-			if (error)
-				return error;
-			inode->i_ctime = ext4_current_time(inode);
-			ext4_mark_inode_dirty(handle, inode);
-		}
 		break;
 
 	case ACL_TYPE_DEFAULT:
@@ -231,6 +224,8 @@
 {
 	handle_t *handle;
 	int error, retries = 0;
+	umode_t mode = inode->i_mode;
+	int update_mode = 0;
 
 retry:
 	handle = ext4_journal_start(inode, EXT4_HT_XATTR,
@@ -238,7 +233,20 @@
 	if (IS_ERR(handle))
 		return PTR_ERR(handle);
 
+	if ((type == ACL_TYPE_ACCESS) && acl) {
+		error = posix_acl_update_mode(inode, &mode, &acl);
+		if (error)
+			goto out_stop;
+		update_mode = 1;
+	}
+
 	error = __ext4_set_acl(handle, inode, type, acl);
+	if (!error && update_mode) {
+		inode->i_mode = mode;
+		inode->i_ctime = ext4_current_time(inode);
+		ext4_mark_inode_dirty(handle, inode);
+	}
+out_stop:
 	ext4_journal_stop(handle);
 	if (error == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
 		goto retry;
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 20ee0e4..9b67de7 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2352,6 +2352,7 @@
 #define fscrypt_fname_free_buffer	fscrypt_notsupp_fname_free_buffer
 #define fscrypt_fname_disk_to_usr	fscrypt_notsupp_fname_disk_to_usr
 #define fscrypt_fname_usr_to_disk	fscrypt_notsupp_fname_usr_to_disk
+#define fs_using_hardware_encryption fs_notsupp_using_hardware_encryption
 #endif
 
 /* dir.c */
diff --git a/fs/ext4/ext4_ice.c b/fs/ext4/ext4_ice.c
new file mode 100644
index 0000000..25f79ae
--- /dev/null
+++ b/fs/ext4/ext4_ice.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "ext4_ice.h"
+
+/*
+ * Retrieves encryption key from the inode
+ */
+char *ext4_get_ice_encryption_key(const struct inode *inode)
+{
+	struct fscrypt_info *ci = NULL;
+
+	if (!inode)
+		return NULL;
+
+	ci = inode->i_crypt_info;
+	if (!ci)
+		return NULL;
+
+	return &(ci->ci_raw_key[0]);
+}
+
+/*
+ * Retrieves encryption salt from the inode
+ */
+char *ext4_get_ice_encryption_salt(const struct inode *inode)
+{
+	struct fscrypt_info *ci = NULL;
+
+	if (!inode)
+		return NULL;
+
+	ci = inode->i_crypt_info;
+	if (!ci)
+		return NULL;
+
+	return &(ci->ci_raw_key[ext4_get_ice_encryption_key_size(inode)]);
+}
+
+/*
+ * returns true if the cipher mode in inode is AES XTS
+ */
+int ext4_is_aes_xts_cipher(const struct inode *inode)
+{
+	struct fscrypt_info *ci = NULL;
+
+	ci = inode->i_crypt_info;
+	if (!ci)
+		return 0;
+
+	return (ci->ci_data_mode == FS_ENCRYPTION_MODE_PRIVATE);
+}
+
+/*
+ * returns true if encryption info in both inodes is equal
+ */
+int ext4_is_ice_encryption_info_equal(const struct inode *inode1,
+	const struct inode *inode2)
+{
+	char *key1 = NULL;
+	char *key2 = NULL;
+	char *salt1 = NULL;
+	char *salt2 = NULL;
+
+	if (!inode1 || !inode2)
+		return 0;
+
+	if (inode1 == inode2)
+		return 1;
+
+	/* both do not belong to ice, so we don't care, they are equal for us */
+	if (!ext4_should_be_processed_by_ice(inode1) &&
+		!ext4_should_be_processed_by_ice(inode2))
+		return 1;
+
+	/* one belongs to ice, the other does not -> not equal */
+	if (ext4_should_be_processed_by_ice(inode1) ^
+		ext4_should_be_processed_by_ice(inode2))
+		return 0;
+
+	key1 = ext4_get_ice_encryption_key(inode1);
+	key2 = ext4_get_ice_encryption_key(inode2);
+	salt1 = ext4_get_ice_encryption_salt(inode1);
+	salt2 = ext4_get_ice_encryption_salt(inode2);
+
+	/* key and salt should not be null by this point */
+	if (!key1 || !key2 || !salt1 || !salt2 ||
+		(ext4_get_ice_encryption_key_size(inode1) !=
+		 ext4_get_ice_encryption_key_size(inode2)) ||
+		(ext4_get_ice_encryption_salt_size(inode1) !=
+		 ext4_get_ice_encryption_salt_size(inode2)))
+		return 0;
+
+	return ((memcmp(key1, key2,
+			ext4_get_ice_encryption_key_size(inode1)) == 0) &&
+		(memcmp(salt1, salt2,
+			ext4_get_ice_encryption_salt_size(inode1)) == 0));
+}
diff --git a/fs/ext4/ext4_ice.h b/fs/ext4/ext4_ice.h
new file mode 100644
index 0000000..04e09bf
--- /dev/null
+++ b/fs/ext4/ext4_ice.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _EXT4_ICE_H
+#define _EXT4_ICE_H
+
+#include "ext4.h"
+#include <linux/fscrypto.h>
+
+#ifdef CONFIG_EXT4_FS_ICE_ENCRYPTION
+static inline int ext4_should_be_processed_by_ice(const struct inode *inode)
+{
+	if (!ext4_encrypted_inode((struct inode *)inode))
+		return 0;
+
+	return fs_using_hardware_encryption((struct inode *)inode);
+}
+
+static inline int ext4_is_ice_enabled(void)
+{
+	return 1;
+}
+
+int ext4_is_aes_xts_cipher(const struct inode *inode);
+
+char *ext4_get_ice_encryption_key(const struct inode *inode);
+char *ext4_get_ice_encryption_salt(const struct inode *inode);
+
+int ext4_is_ice_encryption_info_equal(const struct inode *inode1,
+	const struct inode *inode2);
+
+static inline size_t ext4_get_ice_encryption_key_size(
+	const struct inode *inode)
+{
+	return FS_AES_256_XTS_KEY_SIZE / 2;
+}
+
+static inline size_t ext4_get_ice_encryption_salt_size(
+	const struct inode *inode)
+{
+	return FS_AES_256_XTS_KEY_SIZE / 2;
+}
+
+#else
+static inline int ext4_should_be_processed_by_ice(const struct inode *inode)
+{
+	return 0;
+}
+static inline int ext4_is_ice_enabled(void)
+{
+	return 0;
+}
+
+static inline char *ext4_get_ice_encryption_key(const struct inode *inode)
+{
+	return NULL;
+}
+
+static inline char *ext4_get_ice_encryption_salt(const struct inode *inode)
+{
+	return NULL;
+}
+
+static inline size_t ext4_get_ice_encryption_key_size(
+	const struct inode *inode)
+{
+	return 0;
+}
+
+static inline size_t ext4_get_ice_encryption_salt_size(
+	const struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_is_xts_cipher(const struct inode *inode)
+{
+	return 0;
+}
+
+static inline int ext4_is_ice_encryption_info_equal(
+	const struct inode *inode1,
+	const struct inode *inode2)
+{
+	return 0;
+}
+
+static inline int ext4_is_aes_xts_cipher(const struct inode *inode)
+{
+	return 0;
+}
+
+#endif
+
+#endif	/* _EXT4_ICE_H */
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 9e77c08..510e664 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -469,6 +469,8 @@
 				lastoff = page_offset(page);
 				bh = head = page_buffers(page);
 				do {
+					if (lastoff + bh->b_size <= startoff)
+						goto next;
 					if (buffer_uptodate(bh) ||
 					    buffer_unwritten(bh)) {
 						if (whence == SEEK_DATA)
@@ -483,6 +485,7 @@
 						unlock_page(page);
 						goto out;
 					}
+next:
 					lastoff += bh->b_size;
 					bh = bh->b_this_page;
 				} while (bh != head);
@@ -524,7 +527,7 @@
 	inode_lock(inode);
 
 	isize = i_size_read(inode);
-	if (offset >= isize) {
+	if (offset < 0 || offset >= isize) {
 		inode_unlock(inode);
 		return -ENXIO;
 	}
@@ -587,7 +590,7 @@
 	inode_lock(inode);
 
 	isize = i_size_read(inode);
-	if (offset >= isize) {
+	if (offset < 0 || offset >= isize) {
 		inode_unlock(inode);
 		return -ENXIO;
 	}
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index de47a29..dcb9669 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -42,6 +42,7 @@
 #include "xattr.h"
 #include "acl.h"
 #include "truncate.h"
+#include "ext4_ice.h"
 
 #include <trace/events/ext4.h>
 #include <trace/events/android_fs.h>
@@ -1152,7 +1153,8 @@
 			ll_rw_block(REQ_OP_READ, 0, 1, &bh);
 			*wait_bh++ = bh;
 			decrypt = ext4_encrypted_inode(inode) &&
-				S_ISREG(inode->i_mode);
+				S_ISREG(inode->i_mode) &&
+				!ext4_is_ice_enabled();
 		}
 	}
 	/*
@@ -2120,15 +2122,29 @@
 static int mpage_submit_page(struct mpage_da_data *mpd, struct page *page)
 {
 	int len;
-	loff_t size = i_size_read(mpd->inode);
+	loff_t size;
 	int err;
 
 	BUG_ON(page->index != mpd->first_page);
+	clear_page_dirty_for_io(page);
+	/*
+	 * We have to be very careful here!  Nothing protects writeback path
+	 * against i_size changes and the page can be writeably mapped into
+	 * page tables. So an application can be growing i_size and writing
+	 * data through mmap while writeback runs. clear_page_dirty_for_io()
+	 * write-protects our page in page tables and the page cannot get
+	 * written to again until we release page lock. So only after
+	 * clear_page_dirty_for_io() we are safe to sample i_size for
+	 * ext4_bio_write_page() to zero-out tail of the written page. We rely
+	 * on the barrier provided by TestClearPageDirty in
+	 * clear_page_dirty_for_io() to make sure i_size is really sampled only
+	 * after page tables are updated.
+	 */
+	size = i_size_read(mpd->inode);
 	if (page->index == size >> PAGE_SHIFT)
 		len = size & ~PAGE_MASK;
 	else
 		len = PAGE_SIZE;
-	clear_page_dirty_for_io(page);
 	err = ext4_bio_write_page(&mpd->io_submit, page, len, mpd->wbc, false);
 	if (!err)
 		mpd->wbc->nr_to_write--;
@@ -3495,7 +3511,8 @@
 		get_block_func = ext4_dio_get_block_unwritten_async;
 		dio_flags = DIO_LOCKING;
 	}
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && \
+!defined(CONFIG_EXT4_FS_ICE_ENCRYPTION)
 	BUG_ON(ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode));
 #endif
 	if (IS_DAX(inode)) {
@@ -3609,7 +3626,8 @@
 	ssize_t ret;
 	int rw = iov_iter_rw(iter);
 
-#ifdef CONFIG_EXT4_FS_ENCRYPTION
+#if defined(CONFIG_EXT4_FS_ENCRYPTION) && \
+!defined(CONFIG_EXT4_FS_ICE_ENCRYPTION)
 	if (ext4_encrypted_inode(inode) && S_ISREG(inode->i_mode))
 		return 0;
 #endif
@@ -3806,7 +3824,8 @@
 		if (!buffer_uptodate(bh))
 			goto unlock;
 		if (S_ISREG(inode->i_mode) &&
-		    ext4_encrypted_inode(inode)) {
+		    ext4_encrypted_inode(inode) &&
+		    !fs_using_hardware_encryption(inode)) {
 			/* We expect the key to be set. */
 			BUG_ON(!fscrypt_has_encryption_key(inode));
 			BUG_ON(blocksize != PAGE_SIZE);
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index cec9280..1ddceb6 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -773,10 +773,6 @@
 	case EXT4_IOC_SET_ENCRYPTION_POLICY: {
 #ifdef CONFIG_EXT4_FS_ENCRYPTION
 		struct fscrypt_policy policy;
-
-		if (!ext4_has_feature_encrypt(sb))
-			return -EOPNOTSUPP;
-
 		if (copy_from_user(&policy,
 				   (struct fscrypt_policy __user *)arg,
 				   sizeof(policy)))
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index df8168f..e5e99a7 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -2136,8 +2136,10 @@
 	 * We search using buddy data only if the order of the request
 	 * is greater than equal to the sbi_s_mb_order2_reqs
 	 * You can tune it via /sys/fs/ext4/<partition>/mb_order2_req
+	 * We also support searching for power-of-two requests only for
+	 * requests upto maximum buddy size we have constructed.
 	 */
-	if (i >= sbi->s_mb_order2_reqs) {
+	if (i >= sbi->s_mb_order2_reqs && i <= sb->s_blocksize_bits + 2) {
 		/*
 		 * This should tell if fe_len is exactly power of 2
 		 */
@@ -2207,7 +2209,7 @@
 			}
 
 			ac->ac_groups_scanned++;
-			if (cr == 0 && ac->ac_2order < sb->s_blocksize_bits+2)
+			if (cr == 0)
 				ext4_mb_simple_scan_group(ac, &e4b);
 			else if (cr == 1 && sbi->s_stripe &&
 					!(ac->ac_g_ex.fe_len % sbi->s_stripe))
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index 423a21c..00b8a5a 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3527,6 +3527,12 @@
 			EXT4_I(old_dentry->d_inode)->i_projid)))
 		return -EXDEV;
 
+	if ((ext4_encrypted_inode(old_dir) &&
+	     !fscrypt_has_encryption_key(old_dir)) ||
+	    (ext4_encrypted_inode(new_dir) &&
+	     !fscrypt_has_encryption_key(new_dir)))
+		return -ENOKEY;
+
 	retval = dquot_initialize(old.dir);
 	if (retval)
 		return retval;
@@ -3726,6 +3732,12 @@
 	u8 new_file_type;
 	int retval;
 
+	if ((ext4_encrypted_inode(old_dir) &&
+	     !fscrypt_has_encryption_key(old_dir)) ||
+	    (ext4_encrypted_inode(new_dir) &&
+	     !fscrypt_has_encryption_key(new_dir)))
+		return -ENOKEY;
+
 	if ((ext4_encrypted_inode(old_dir) ||
 	     ext4_encrypted_inode(new_dir)) &&
 	    (old_dir != new_dir) &&
diff --git a/fs/ext4/page-io.c b/fs/ext4/page-io.c
index 0094923..d8a0770 100644
--- a/fs/ext4/page-io.c
+++ b/fs/ext4/page-io.c
@@ -29,6 +29,7 @@
 #include "ext4_jbd2.h"
 #include "xattr.h"
 #include "acl.h"
+#include "ext4_ice.h"
 
 static struct kmem_cache *io_end_cachep;
 
@@ -470,6 +471,7 @@
 		gfp_t gfp_flags = GFP_NOFS;
 
 	retry_encrypt:
+		if (!fs_using_hardware_encryption(inode))
 		data_page = fscrypt_encrypt_page(inode, page, gfp_flags);
 		if (IS_ERR(data_page)) {
 			ret = PTR_ERR(data_page);
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index cf68100..95bf466 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1926,7 +1926,8 @@
 			n_desc_blocks = o_desc_blocks +
 				le16_to_cpu(es->s_reserved_gdt_blocks);
 			n_group = n_desc_blocks * EXT4_DESC_PER_BLOCK(sb);
-			n_blocks_count = n_group * EXT4_BLOCKS_PER_GROUP(sb);
+			n_blocks_count = (ext4_fsblk_t)n_group *
+				EXT4_BLOCKS_PER_GROUP(sb);
 			n_group--; /* set to last group number */
 		}
 
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 5fa9ba1..1f58179 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -2334,6 +2334,7 @@
 	unsigned int s_flags = sb->s_flags;
 	int nr_orphans = 0, nr_truncates = 0;
 #ifdef CONFIG_QUOTA
+	int quota_update = 0;
 	int i;
 #endif
 	if (!es->s_last_orphan) {
@@ -2372,14 +2373,32 @@
 #ifdef CONFIG_QUOTA
 	/* Needed for iput() to work correctly and not trash data */
 	sb->s_flags |= MS_ACTIVE;
-	/* Turn on quotas so that they are updated correctly */
+
+	/*
+	 * Turn on quotas which were not enabled for read-only mounts if
+	 * filesystem has quota feature, so that they are updated correctly.
+	 */
+	if (ext4_has_feature_quota(sb) && (s_flags & MS_RDONLY)) {
+		int ret = ext4_enable_quotas(sb);
+
+		if (!ret)
+			quota_update = 1;
+		else
+			ext4_msg(sb, KERN_ERR,
+				"Cannot turn on quotas: error %d", ret);
+	}
+
+	/* Turn on journaled quotas used for old sytle */
 	for (i = 0; i < EXT4_MAXQUOTAS; i++) {
 		if (EXT4_SB(sb)->s_qf_names[i]) {
 			int ret = ext4_quota_on_mount(sb, i);
-			if (ret < 0)
+
+			if (!ret)
+				quota_update = 1;
+			else
 				ext4_msg(sb, KERN_ERR,
 					"Cannot turn on journaled "
-					"quota: error %d", ret);
+					"quota: type %d: error %d", i, ret);
 		}
 	}
 #endif
@@ -2438,10 +2457,12 @@
 		ext4_msg(sb, KERN_INFO, "%d truncate%s cleaned up",
 		       PLURAL(nr_truncates));
 #ifdef CONFIG_QUOTA
-	/* Turn quotas off */
-	for (i = 0; i < EXT4_MAXQUOTAS; i++) {
-		if (sb_dqopt(sb)->files[i])
-			dquot_quota_off(sb, i);
+	/* Turn off quotas if they were enabled for orphan cleanup */
+	if (quota_update) {
+		for (i = 0; i < EXT4_MAXQUOTAS; i++) {
+			if (sb_dqopt(sb)->files[i])
+				dquot_quota_off(sb, i);
+		}
 	}
 #endif
 	sb->s_flags = s_flags; /* Restore MS_RDONLY status */
@@ -2607,9 +2628,9 @@
 
 	if (sbi->s_stripe && sbi->s_stripe <= sbi->s_blocks_per_group)
 		ret = sbi->s_stripe;
-	else if (stripe_width <= sbi->s_blocks_per_group)
+	else if (stripe_width && stripe_width <= sbi->s_blocks_per_group)
 		ret = stripe_width;
-	else if (stride <= sbi->s_blocks_per_group)
+	else if (stride && stride <= sbi->s_blocks_per_group)
 		ret = stride;
 	else
 		ret = 0;
@@ -5365,6 +5386,9 @@
 				DQUOT_USAGE_ENABLED |
 				(quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0));
 			if (err) {
+				for (type--; type >= 0; type--)
+					dquot_quota_off(sb, type);
+
 				ext4_warning(sb,
 					"Failed to enable quota tracking "
 					"(type=%d, err=%d). Please run "
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
index 2c5ae0b..08b3f62 100644
--- a/fs/f2fs/data.c
+++ b/fs/f2fs/data.c
@@ -1630,7 +1630,12 @@
 			goto fail;
 	}
 repeat:
-	page = grab_cache_page_write_begin(mapping, index, flags);
+	/*
+	 * Do not use grab_cache_page_write_begin() to avoid deadlock due to
+	 * wait_for_stable_page. Will wait that below with our IO control.
+	 */
+	page = pagecache_get_page(mapping, index,
+				FGP_LOCK | FGP_WRITE | FGP_CREAT, GFP_NOFS);
 	if (!page) {
 		err = -ENOMEM;
 		goto fail;
diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c
index 489fa0d..08d7dc9 100644
--- a/fs/f2fs/namei.c
+++ b/fs/f2fs/namei.c
@@ -663,6 +663,12 @@
 	bool is_old_inline = f2fs_has_inline_dentry(old_dir);
 	int err = -ENOENT;
 
+	if ((f2fs_encrypted_inode(old_dir) &&
+			!fscrypt_has_encryption_key(old_dir)) ||
+			(f2fs_encrypted_inode(new_dir) &&
+			!fscrypt_has_encryption_key(new_dir)))
+		return -ENOKEY;
+
 	if ((old_dir != new_dir) && f2fs_encrypted_inode(new_dir) &&
 			!fscrypt_has_permitted_context(new_dir, old_inode)) {
 		err = -EPERM;
@@ -843,6 +849,12 @@
 	int old_nlink = 0, new_nlink = 0;
 	int err = -ENOENT;
 
+	if ((f2fs_encrypted_inode(old_dir) &&
+			!fscrypt_has_encryption_key(old_dir)) ||
+			(f2fs_encrypted_inode(new_dir) &&
+			!fscrypt_has_encryption_key(new_dir)))
+		return -ENOKEY;
+
 	if ((f2fs_encrypted_inode(old_dir) || f2fs_encrypted_inode(new_dir)) &&
 			(old_dir != new_dir) &&
 			(!fscrypt_has_permitted_context(new_dir, old_inode) ||
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c
index 2fc84a9..98c1a63 100644
--- a/fs/f2fs/recovery.c
+++ b/fs/f2fs/recovery.c
@@ -316,7 +316,7 @@
 		return 0;
 
 	/* Get the previous summary */
-	for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) {
+	for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) {
 		struct curseg_info *curseg = CURSEG_I(sbi, i);
 		if (curseg->segno == segno) {
 			sum = curseg->sum_blk->entries[blkoff];
@@ -626,8 +626,6 @@
 	}
 
 	clear_sbi_flag(sbi, SBI_POR_DOING);
-	if (err)
-		set_ckpt_flags(sbi, CP_ERROR_FLAG);
 	mutex_unlock(&sbi->cp_mutex);
 
 	/* let's drop all the directory inodes for clean checkpoint */
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 74a2b44..e10f616 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -1263,7 +1263,7 @@
 	struct curseg_info *curseg = CURSEG_I(sbi, type);
 	const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops;
 
-	if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0, 0))
+	if (IS_NODESEG(type))
 		return v_ops->get_victim(sbi,
 				&(curseg)->next_segno, BG_GC, type, SSR);
 
diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c
index 5d5ddaa..37e0c31d 100644
--- a/fs/fscache/object-list.c
+++ b/fs/fscache/object-list.c
@@ -330,6 +330,13 @@
 	rcu_read_lock();
 
 	confkey = user_key_payload(key);
+	if (!confkey) {
+		/* key was revoked */
+		rcu_read_unlock();
+		key_put(key);
+		goto no_config;
+	}
+
 	buf = confkey->data;
 
 	for (len = confkey->datalen - 1; len >= 0; len--) {
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index c7c3c96..1693308 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -1361,7 +1361,8 @@
 			*/
 			over = !dir_emit(ctx, dirent->name, dirent->namelen,
 				       dirent->ino, dirent->type);
-			ctx->pos = dirent->off;
+			if (!over)
+				ctx->pos = dirent->off;
 		}
 
 		buf += reclen;
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 75c95659..21d829b 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -55,7 +55,7 @@
 {
 	struct fuse_file *ff;
 
-	ff = kmalloc(sizeof(struct fuse_file), GFP_KERNEL);
+	ff = kzalloc(sizeof(struct fuse_file), GFP_KERNEL);
 	if (unlikely(!ff))
 		return NULL;
 
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 7bff6f4..7a8b1d7 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -1820,29 +1820,27 @@
 
 static void gfs2_glock_iter_next(struct gfs2_glock_iter *gi)
 {
-	do {
-		gi->gl = rhashtable_walk_next(&gi->hti);
+	while ((gi->gl = rhashtable_walk_next(&gi->hti))) {
 		if (IS_ERR(gi->gl)) {
 			if (PTR_ERR(gi->gl) == -EAGAIN)
 				continue;
 			gi->gl = NULL;
+			return;
 		}
-	/* Skip entries for other sb and dead entries */
-	} while ((gi->gl) && ((gi->sdp != gi->gl->gl_name.ln_sbd) ||
-			      __lockref_is_dead(&gi->gl->gl_lockref)));
+		/* Skip entries for other sb and dead entries */
+		if (gi->sdp == gi->gl->gl_name.ln_sbd &&
+		    !__lockref_is_dead(&gi->gl->gl_lockref))
+			return;
+	}
 }
 
 static void *gfs2_glock_seq_start(struct seq_file *seq, loff_t *pos)
 {
 	struct gfs2_glock_iter *gi = seq->private;
 	loff_t n = *pos;
-	int ret;
 
-	if (gi->last_pos <= *pos)
-		n = (*pos - gi->last_pos);
-
-	ret = rhashtable_walk_start(&gi->hti);
-	if (ret)
+	rhashtable_walk_enter(&gl_hash_table, &gi->hti);
+	if (rhashtable_walk_start(&gi->hti) != 0)
 		return NULL;
 
 	do {
@@ -1850,6 +1848,7 @@
 	} while (gi->gl && n--);
 
 	gi->last_pos = *pos;
+
 	return gi->gl;
 }
 
@@ -1861,6 +1860,7 @@
 	(*pos)++;
 	gi->last_pos = *pos;
 	gfs2_glock_iter_next(gi);
+
 	return gi->gl;
 }
 
@@ -1870,6 +1870,7 @@
 
 	gi->gl = NULL;
 	rhashtable_walk_stop(&gi->hti);
+	rhashtable_walk_exit(&gi->hti);
 }
 
 static int gfs2_glock_seq_show(struct seq_file *seq, void *iter_ptr)
@@ -1932,12 +1933,10 @@
 		struct gfs2_glock_iter *gi = seq->private;
 
 		gi->sdp = inode->i_private;
-		gi->last_pos = 0;
 		seq->buf = kmalloc(GFS2_SEQ_GOODSIZE, GFP_KERNEL | __GFP_NOWARN);
 		if (seq->buf)
 			seq->size = GFS2_SEQ_GOODSIZE;
 		gi->gl = NULL;
-		ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
 	}
 	return ret;
 }
@@ -1948,7 +1947,6 @@
 	struct gfs2_glock_iter *gi = seq->private;
 
 	gi->gl = NULL;
-	rhashtable_walk_exit(&gi->hti);
 	return seq_release_private(inode, file);
 }
 
@@ -1960,12 +1958,10 @@
 		struct seq_file *seq = file->private_data;
 		struct gfs2_glock_iter *gi = seq->private;
 		gi->sdp = inode->i_private;
-		gi->last_pos = 0;
 		seq->buf = kmalloc(GFS2_SEQ_GOODSIZE, GFP_KERNEL | __GFP_NOWARN);
 		if (seq->buf)
 			seq->size = GFS2_SEQ_GOODSIZE;
 		gi->gl = NULL;
-		ret = rhashtable_walk_init(&gl_hash_table, &gi->hti, GFP_KERNEL);
 	}
 	return ret;
 }
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 704fa0b..2c2f182 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -695,14 +695,11 @@
 
 	inode = new_inode(sb);
 	if (inode) {
-		struct hugetlbfs_inode_info *info;
 		inode->i_ino = get_next_ino();
 		inode->i_mode = S_IFDIR | config->mode;
 		inode->i_uid = config->uid;
 		inode->i_gid = config->gid;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
-		info = HUGETLBFS_I(inode);
-		mpol_shared_policy_init(&info->policy, NULL);
 		inode->i_op = &hugetlbfs_dir_inode_operations;
 		inode->i_fop = &simple_dir_operations;
 		/* directory inodes start off with i_nlink == 2 (for "." entry) */
@@ -733,7 +730,6 @@
 
 	inode = new_inode(sb);
 	if (inode) {
-		struct hugetlbfs_inode_info *info;
 		inode->i_ino = get_next_ino();
 		inode_init_owner(inode, dir, mode);
 		lockdep_set_class(&inode->i_mapping->i_mmap_rwsem,
@@ -741,15 +737,6 @@
 		inode->i_mapping->a_ops = &hugetlbfs_aops;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
 		inode->i_mapping->private_data = resv_map;
-		info = HUGETLBFS_I(inode);
-		/*
-		 * The policy is initialized here even if we are creating a
-		 * private inode because initialization simply creates an
-		 * an empty rb tree and calls rwlock_init(), later when we
-		 * call mpol_free_shared_policy() it will just return because
-		 * the rb tree will still be empty.
-		 */
-		mpol_shared_policy_init(&info->policy, NULL);
 		switch (mode & S_IFMT) {
 		default:
 			init_special_inode(inode, mode, dev);
@@ -937,6 +924,18 @@
 		hugetlbfs_inc_free_inodes(sbinfo);
 		return NULL;
 	}
+
+	/*
+	 * Any time after allocation, hugetlbfs_destroy_inode can be called
+	 * for the inode.  mpol_free_shared_policy is unconditionally called
+	 * as part of hugetlbfs_destroy_inode.  So, initialize policy here
+	 * in case of a quick call to destroy.
+	 *
+	 * Note that the policy is initialized even if we are creating a
+	 * private inode.  This simplifies hugetlbfs_destroy_inode.
+	 */
+	mpol_shared_policy_init(&p->policy, NULL);
+
 	return &p->vfs_inode;
 }
 
diff --git a/fs/inode.c b/fs/inode.c
index 0aaebd1..3844c31 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -637,6 +637,7 @@
 
 	dispose_list(&dispose);
 }
+EXPORT_SYMBOL_GPL(evict_inodes);
 
 /**
  * invalidate_inodes	- attempt to free all inodes on a superblock
diff --git a/fs/internal.h b/fs/internal.h
index 15fe2aa..3e58863 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -138,7 +138,6 @@
 extern void inode_io_list_del(struct inode *inode);
 
 extern long get_nr_dirty_inodes(void);
-extern void evict_inodes(struct super_block *);
 extern int invalidate_inodes(struct super_block *, bool);
 
 /*
diff --git a/fs/iomap.c b/fs/iomap.c
index 798c291..a49db88 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -281,7 +281,7 @@
 		unsigned long bytes;	/* Bytes to write to page */
 
 		offset = (pos & (PAGE_SIZE - 1));
-		bytes = min_t(unsigned long, PAGE_SIZE - offset, length);
+		bytes = min_t(loff_t, PAGE_SIZE - offset, length);
 
 		rpage = __iomap_read_page(inode, pos);
 		if (IS_ERR(rpage))
@@ -376,7 +376,7 @@
 		unsigned offset, bytes;
 
 		offset = pos & (PAGE_SIZE - 1); /* Within page */
-		bytes = min_t(unsigned, PAGE_SIZE - offset, count);
+		bytes = min_t(loff_t, PAGE_SIZE - offset, count);
 
 		if (IS_DAX(inode))
 			status = iomap_dax_zero(pos, offset, bytes, iomap);
diff --git a/fs/mpage.c b/fs/mpage.c
index 1193d43..d4e17c8 100644
--- a/fs/mpage.c
+++ b/fs/mpage.c
@@ -502,6 +502,16 @@
 		try_to_free_buffers(page);
 }
 
+/*
+ * For situations where we want to clean all buffers attached to a page.
+ * We don't need to calculate how many buffers are attached to the page,
+ * we just need to specify a number larger than the maximum number of buffers.
+ */
+void clean_page_buffers(struct page *page)
+{
+	clean_buffers(page, ~0U);
+}
+
 static int __mpage_writepage(struct page *page, struct writeback_control *wbc,
 		      void *data)
 {
@@ -640,10 +650,8 @@
 	if (bio == NULL) {
 		if (first_unmapped == blocks_per_page) {
 			if (!bdev_write_page(bdev, blocks[0] << (blkbits - 9),
-								page, wbc)) {
-				clean_buffers(page, first_unmapped);
+								page, wbc))
 				goto out;
-			}
 		}
 		bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
 				BIO_MAX_PAGES, GFP_NOFS|__GFP_HIGH);
diff --git a/fs/namei.c b/fs/namei.c
index e10895c..2af3818 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2903,6 +2903,11 @@
 	if (error)
 		return error;
 	error = dir->i_op->create(dir, dentry, mode, want_excl);
+	if (error)
+		return error;
+	error = security_inode_post_create(dir, dentry, mode);
+	if (error)
+		return error;
 	if (!error)
 		fsnotify_create(dir, dentry);
 	return error;
@@ -3002,10 +3007,16 @@
 
 static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode)
 {
+	struct user_namespace *s_user_ns;
 	int error = security_path_mknod(dir, dentry, mode, 0);
 	if (error)
 		return error;
 
+	s_user_ns = dir->dentry->d_sb->s_user_ns;
+	if (!kuid_has_mapping(s_user_ns, current_fsuid()) ||
+	    !kgid_has_mapping(s_user_ns, current_fsgid()))
+		return -EOVERFLOW;
+
 	error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
 	if (error)
 		return error;
@@ -3712,6 +3723,13 @@
 		return error;
 
 	error = dir->i_op->mknod(dir, dentry, mode, dev);
+	if (error)
+		return error;
+
+	error = security_inode_post_create(dir, dentry, mode);
+	if (error)
+		return error;
+
 	if (!error)
 		fsnotify_create(dir, dentry);
 	return error;
diff --git a/fs/namespace.c b/fs/namespace.c
index 7731f77..2160bb9 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -227,6 +227,7 @@
 		mnt->mnt_count = 1;
 		mnt->mnt_writers = 0;
 #endif
+		mnt->mnt.data = NULL;
 
 		INIT_HLIST_NODE(&mnt->mnt_hash);
 		INIT_LIST_HEAD(&mnt->mnt_child);
@@ -976,7 +977,6 @@
 	if (!mnt)
 		return ERR_PTR(-ENOMEM);
 
-	mnt->mnt.data = NULL;
 	if (type->alloc_mnt_data) {
 		mnt->mnt.data = type->alloc_mnt_data();
 		if (!mnt->mnt.data) {
@@ -990,7 +990,6 @@
 
 	root = mount_fs(type, flags, name, &mnt->mnt, data);
 	if (IS_ERR(root)) {
-		kfree(mnt->mnt.data);
 		mnt_free_id(mnt);
 		free_vfsmnt(mnt);
 		return ERR_CAST(root);
@@ -1109,7 +1108,6 @@
 	return mnt;
 
  out_free:
-	kfree(mnt->mnt.data);
 	mnt_free_id(mnt);
 	free_vfsmnt(mnt);
 	return ERR_PTR(err);
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index f31fd0d..b1daeaf 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -121,6 +121,7 @@
 config PNFS_BLOCK
 	tristate
 	depends on NFS_V4_1 && BLK_DEV_DM
+	depends on 64BIT || LBDAF
 	default NFS_V4
 
 config PNFS_OBJLAYOUT
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 0a21150..af84a92 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -75,7 +75,10 @@
 
 	set_freezable();
 
-	while (!kthread_should_stop()) {
+	while (!kthread_freezable_should_stop(NULL)) {
+
+		if (signal_pending(current))
+			flush_signals(current);
 		/*
 		 * Listen for a request on the socket
 		 */
@@ -84,6 +87,8 @@
 			continue;
 		svc_process(rqstp);
 	}
+	svc_exit_thread(rqstp);
+	module_put_and_exit(0);
 	return 0;
 }
 
@@ -102,9 +107,10 @@
 
 	set_freezable();
 
-	while (!kthread_should_stop()) {
-		if (try_to_freeze())
-			continue;
+	while (!kthread_freezable_should_stop(NULL)) {
+
+		if (signal_pending(current))
+			flush_signals(current);
 
 		prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
 		spin_lock_bh(&serv->sv_cb_lock);
@@ -120,11 +126,13 @@
 				error);
 		} else {
 			spin_unlock_bh(&serv->sv_cb_lock);
-			schedule();
+			if (!kthread_should_stop())
+				schedule();
 			finish_wait(&serv->sv_cb_waitq, &wq);
 		}
-		flush_signals(current);
 	}
+	svc_exit_thread(rqstp);
+	module_put_and_exit(0);
 	return 0;
 }
 
@@ -220,23 +228,23 @@
 static struct svc_serv_ops nfs40_cb_sv_ops = {
 	.svo_function		= nfs4_callback_svc,
 	.svo_enqueue_xprt	= svc_xprt_do_enqueue,
-	.svo_setup		= svc_set_num_threads,
+	.svo_setup		= svc_set_num_threads_sync,
 	.svo_module		= THIS_MODULE,
 };
 #if defined(CONFIG_NFS_V4_1)
 static struct svc_serv_ops nfs41_cb_sv_ops = {
 	.svo_function		= nfs41_callback_svc,
 	.svo_enqueue_xprt	= svc_xprt_do_enqueue,
-	.svo_setup		= svc_set_num_threads,
+	.svo_setup		= svc_set_num_threads_sync,
 	.svo_module		= THIS_MODULE,
 };
 
-struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+static struct svc_serv_ops *nfs4_cb_sv_ops[] = {
 	[0] = &nfs40_cb_sv_ops,
 	[1] = &nfs41_cb_sv_ops,
 };
 #else
-struct svc_serv_ops *nfs4_cb_sv_ops[] = {
+static struct svc_serv_ops *nfs4_cb_sv_ops[] = {
 	[0] = &nfs40_cb_sv_ops,
 	[1] = NULL,
 };
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index 84c1cb9..1eec947 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -636,11 +636,11 @@
 	if (result <= 0)
 		goto out;
 
-	result = generic_write_sync(iocb, result);
-	if (result < 0)
-		goto out;
 	written = result;
 	iocb->ki_pos += written;
+	result = generic_write_sync(iocb, written);
+	if (result < 0)
+		goto out;
 
 	/* Return error values */
 	if (nfs_need_check_write(file, inode)) {
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index f7a3f6b..9009989 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -30,6 +30,7 @@
 {
 	nfs4_print_deviceid(&mirror_ds->id_node.deviceid);
 	nfs4_pnfs_ds_put(mirror_ds->ds);
+	kfree(mirror_ds->ds_versions);
 	kfree_rcu(mirror_ds, id_node.rcu);
 }
 
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 80bcc0b..52ea41b 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -248,7 +248,6 @@
 extern const struct nfs_pageio_ops nfs_pgio_rw_ops;
 struct nfs_pgio_header *nfs_pgio_header_alloc(const struct nfs_rw_ops *);
 void nfs_pgio_header_free(struct nfs_pgio_header *);
-void nfs_pgio_data_destroy(struct nfs_pgio_header *);
 int nfs_generic_pgio(struct nfs_pageio_descriptor *, struct nfs_pgio_header *);
 int nfs_initiate_pgio(struct rpc_clnt *clnt, struct nfs_pgio_header *hdr,
 		      struct rpc_cred *cred, const struct nfs_rpc_ops *rpc_ops,
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 46ca788..a53b8e0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -7410,7 +7410,7 @@
 			cdata->res.server_scope = NULL;
 		}
 		/* Save the EXCHANGE_ID verifier session trunk tests */
-		memcpy(clp->cl_confirm.data, cdata->args.verifier->data,
+		memcpy(clp->cl_confirm.data, cdata->args.verifier.data,
 		       sizeof(clp->cl_confirm.data));
 	}
 out:
@@ -7447,7 +7447,6 @@
 static int _nfs4_proc_exchange_id(struct nfs_client *clp, struct rpc_cred *cred,
 			u32 sp4_how, struct rpc_xprt *xprt)
 {
-	nfs4_verifier verifier;
 	struct rpc_message msg = {
 		.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_EXCHANGE_ID],
 		.rpc_cred = cred,
@@ -7470,8 +7469,7 @@
 	if (!calldata)
 		goto out;
 
-	if (!xprt)
-		nfs4_init_boot_verifier(clp, &verifier);
+	nfs4_init_boot_verifier(clp, &calldata->args.verifier);
 
 	status = nfs4_init_uniform_client_string(clp);
 	if (status)
@@ -7516,9 +7514,8 @@
 		task_setup_data.rpc_xprt = xprt;
 		task_setup_data.flags =
 				RPC_TASK_SOFT|RPC_TASK_SOFTCONN|RPC_TASK_ASYNC;
-		calldata->args.verifier = &clp->cl_confirm;
-	} else {
-		calldata->args.verifier = &verifier;
+		memcpy(calldata->args.verifier.data, clp->cl_confirm.data,
+				sizeof(calldata->args.verifier.data));
 	}
 	calldata->args.client = clp;
 #ifdef CONFIG_NFS_V4_1_MIGRATION
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index c9c4d985..5e2724a 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1761,7 +1761,7 @@
 	int len = 0;
 
 	encode_op_hdr(xdr, OP_EXCHANGE_ID, decode_exchange_id_maxsz, hdr);
-	encode_nfs4_verifier(xdr, args->verifier);
+	encode_nfs4_verifier(xdr, &args->verifier);
 
 	encode_string(xdr, strlen(args->client->cl_owner_id),
 			args->client->cl_owner_id);
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 142a74f..3d17fc8 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -497,16 +497,6 @@
 }
 EXPORT_SYMBOL_GPL(nfs_pgio_header_alloc);
 
-/*
- * nfs_pgio_header_free - Free a read or write header
- * @hdr: The header to free
- */
-void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
-{
-	hdr->rw_ops->rw_free_header(hdr);
-}
-EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
-
 /**
  * nfs_pgio_data_destroy - make @hdr suitable for reuse
  *
@@ -515,14 +505,24 @@
  *
  * @hdr: A header that has had nfs_generic_pgio called
  */
-void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
+static void nfs_pgio_data_destroy(struct nfs_pgio_header *hdr)
 {
 	if (hdr->args.context)
 		put_nfs_open_context(hdr->args.context);
 	if (hdr->page_array.pagevec != hdr->page_array.page_array)
 		kfree(hdr->page_array.pagevec);
 }
-EXPORT_SYMBOL_GPL(nfs_pgio_data_destroy);
+
+/*
+ * nfs_pgio_header_free - Free a read or write header
+ * @hdr: The header to free
+ */
+void nfs_pgio_header_free(struct nfs_pgio_header *hdr)
+{
+	nfs_pgio_data_destroy(hdr);
+	hdr->rw_ops->rw_free_header(hdr);
+}
+EXPORT_SYMBOL_GPL(nfs_pgio_header_free);
 
 /**
  * nfs_pgio_rpcsetup - Set up arguments for a pageio call
@@ -636,7 +636,6 @@
 static void nfs_pgio_error(struct nfs_pgio_header *hdr)
 {
 	set_bit(NFS_IOHDR_REDO, &hdr->flags);
-	nfs_pgio_data_destroy(hdr);
 	hdr->completion_ops->completion(hdr);
 }
 
@@ -647,7 +646,6 @@
 static void nfs_pgio_release(void *calldata)
 {
 	struct nfs_pgio_header *hdr = calldata;
-	nfs_pgio_data_destroy(hdr);
 	hdr->completion_ops->completion(hdr);
 }
 
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 415d7e6..b7a07ba 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -2145,7 +2145,6 @@
 		nfs_pageio_reset_write_mds(desc);
 		mirror->pg_recoalesce = 1;
 	}
-	nfs_pgio_data_destroy(hdr);
 	hdr->release(hdr);
 }
 
@@ -2257,7 +2256,6 @@
 		nfs_pageio_reset_read_mds(desc);
 		mirror->pg_recoalesce = 1;
 	}
-	nfs_pgio_data_destroy(hdr);
 	hdr->release(hdr);
 }
 
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 211dc2a..3069cd4 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -753,6 +753,14 @@
 	return 0;
 }
 
+void cleanup_callback_cred(void)
+{
+	if (callback_cred) {
+		put_rpccred(callback_cred);
+		callback_cred = NULL;
+	}
+}
+
 static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses)
 {
 	if (clp->cl_minorversion == 0) {
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a0dee8a..d35eb07 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7012,23 +7012,24 @@
 
 	ret = set_callback_cred();
 	if (ret)
-		return -ENOMEM;
+		return ret;
+
 	laundry_wq = alloc_workqueue("%s", WQ_UNBOUND, 0, "nfsd4");
 	if (laundry_wq == NULL) {
 		ret = -ENOMEM;
-		goto out_recovery;
+		goto out_cleanup_cred;
 	}
 	ret = nfsd4_create_callback_queue();
 	if (ret)
 		goto out_free_laundry;
 
 	set_max_delegations();
-
 	return 0;
 
 out_free_laundry:
 	destroy_workqueue(laundry_wq);
-out_recovery:
+out_cleanup_cred:
+	cleanup_callback_cred();
 	return ret;
 }
 
@@ -7086,6 +7087,7 @@
 {
 	destroy_workqueue(laundry_wq);
 	nfsd4_destroy_callback_queue();
+	cleanup_callback_cred();
 }
 
 static void
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 4e7a56a..2c4f7a2 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -129,7 +129,7 @@
 	argp->p = page_address(argp->pagelist[0]);
 	argp->pagelist++;
 	if (argp->pagelen < PAGE_SIZE) {
-		argp->end = argp->p + (argp->pagelen>>2);
+		argp->end = argp->p + XDR_QUADLEN(argp->pagelen);
 		argp->pagelen = 0;
 	} else {
 		argp->end = argp->p + (PAGE_SIZE>>2);
@@ -1246,9 +1246,7 @@
 		argp->pagelen -= pages * PAGE_SIZE;
 		len -= pages * PAGE_SIZE;
 
-		argp->p = (__be32 *)page_address(argp->pagelist[0]);
-		argp->pagelist++;
-		argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE);
+		next_decode_page(argp);
 	}
 	argp->p += XDR_QUADLEN(len);
 
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 4516e8b..005c911 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -615,6 +615,7 @@
 extern __be32 nfs4_check_open_reclaim(clientid_t *clid,
 		struct nfsd4_compound_state *cstate, struct nfsd_net *nn);
 extern int set_callback_cred(void);
+extern void cleanup_callback_cred(void);
 extern void nfsd4_probe_callback(struct nfs4_client *clp);
 extern void nfsd4_probe_callback_sync(struct nfs4_client *clp);
 extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *);
diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c
index f72712f..06089be 100644
--- a/fs/ocfs2/alloc.c
+++ b/fs/ocfs2/alloc.c
@@ -7310,13 +7310,24 @@
 
 static int ocfs2_trim_extent(struct super_block *sb,
 			     struct ocfs2_group_desc *gd,
-			     u32 start, u32 count)
+			     u64 group, u32 start, u32 count)
 {
 	u64 discard, bcount;
+	struct ocfs2_super *osb = OCFS2_SB(sb);
 
 	bcount = ocfs2_clusters_to_blocks(sb, count);
-	discard = le64_to_cpu(gd->bg_blkno) +
-			ocfs2_clusters_to_blocks(sb, start);
+	discard = ocfs2_clusters_to_blocks(sb, start);
+
+	/*
+	 * For the first cluster group, the gd->bg_blkno is not at the start
+	 * of the group, but at an offset from the start. If we add it while
+	 * calculating discard for first group, we will wrongly start fstrim a
+	 * few blocks after the desried start block and the range can cross
+	 * over into the next cluster group. So, add it only if this is not
+	 * the first cluster group.
+	 */
+	if (group != osb->first_cluster_group_blkno)
+		discard += le64_to_cpu(gd->bg_blkno);
 
 	trace_ocfs2_trim_extent(sb, (unsigned long long)discard, bcount);
 
@@ -7324,7 +7335,7 @@
 }
 
 static int ocfs2_trim_group(struct super_block *sb,
-			    struct ocfs2_group_desc *gd,
+			    struct ocfs2_group_desc *gd, u64 group,
 			    u32 start, u32 max, u32 minbits)
 {
 	int ret = 0, count = 0, next;
@@ -7343,7 +7354,7 @@
 		next = ocfs2_find_next_bit(bitmap, max, start);
 
 		if ((next - start) >= minbits) {
-			ret = ocfs2_trim_extent(sb, gd,
+			ret = ocfs2_trim_extent(sb, gd, group,
 						start, next - start);
 			if (ret < 0) {
 				mlog_errno(ret);
@@ -7441,7 +7452,8 @@
 		}
 
 		gd = (struct ocfs2_group_desc *)gd_bh->b_data;
-		cnt = ocfs2_trim_group(sb, gd, first_bit, last_bit, minlen);
+		cnt = ocfs2_trim_group(sb, gd, group,
+				       first_bit, last_bit, minlen);
 		brelse(gd_bh);
 		gd_bh = NULL;
 		if (cnt < 0) {
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 77d1632..8dce409 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -532,6 +532,7 @@
 	init_waitqueue_head(&res->l_event);
 	INIT_LIST_HEAD(&res->l_blocked_list);
 	INIT_LIST_HEAD(&res->l_mask_waiters);
+	INIT_LIST_HEAD(&res->l_holders);
 }
 
 void ocfs2_inode_lock_res_init(struct ocfs2_lock_res *res,
@@ -749,6 +750,50 @@
 	res->l_flags = 0UL;
 }
 
+/*
+ * Keep a list of processes who have interest in a lockres.
+ * Note: this is now only uesed for check recursive cluster locking.
+ */
+static inline void ocfs2_add_holder(struct ocfs2_lock_res *lockres,
+				   struct ocfs2_lock_holder *oh)
+{
+	INIT_LIST_HEAD(&oh->oh_list);
+	oh->oh_owner_pid = get_pid(task_pid(current));
+
+	spin_lock(&lockres->l_lock);
+	list_add_tail(&oh->oh_list, &lockres->l_holders);
+	spin_unlock(&lockres->l_lock);
+}
+
+static inline void ocfs2_remove_holder(struct ocfs2_lock_res *lockres,
+				       struct ocfs2_lock_holder *oh)
+{
+	spin_lock(&lockres->l_lock);
+	list_del(&oh->oh_list);
+	spin_unlock(&lockres->l_lock);
+
+	put_pid(oh->oh_owner_pid);
+}
+
+static inline int ocfs2_is_locked_by_me(struct ocfs2_lock_res *lockres)
+{
+	struct ocfs2_lock_holder *oh;
+	struct pid *pid;
+
+	/* look in the list of holders for one with the current task as owner */
+	spin_lock(&lockres->l_lock);
+	pid = task_pid(current);
+	list_for_each_entry(oh, &lockres->l_holders, oh_list) {
+		if (oh->oh_owner_pid == pid) {
+			spin_unlock(&lockres->l_lock);
+			return 1;
+		}
+	}
+	spin_unlock(&lockres->l_lock);
+
+	return 0;
+}
+
 static inline void ocfs2_inc_holders(struct ocfs2_lock_res *lockres,
 				     int level)
 {
@@ -2333,8 +2378,9 @@
 		goto getbh;
 	}
 
-	if (ocfs2_mount_local(osb))
-		goto local;
+	if ((arg_flags & OCFS2_META_LOCK_GETBH) ||
+	    ocfs2_mount_local(osb))
+		goto update;
 
 	if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
 		ocfs2_wait_for_recovery(osb);
@@ -2363,7 +2409,7 @@
 	if (!(arg_flags & OCFS2_META_LOCK_RECOVERY))
 		ocfs2_wait_for_recovery(osb);
 
-local:
+update:
 	/*
 	 * We only see this flag if we're being called from
 	 * ocfs2_read_locked_inode(). It means we're locking an inode
@@ -2497,6 +2543,59 @@
 		ocfs2_cluster_unlock(OCFS2_SB(inode->i_sb), lockres, level);
 }
 
+/*
+ * This _tracker variantes are introduced to deal with the recursive cluster
+ * locking issue. The idea is to keep track of a lock holder on the stack of
+ * the current process. If there's a lock holder on the stack, we know the
+ * task context is already protected by cluster locking. Currently, they're
+ * used in some VFS entry routines.
+ *
+ * return < 0 on error, return == 0 if there's no lock holder on the stack
+ * before this call, return == 1 if this call would be a recursive locking.
+ */
+int ocfs2_inode_lock_tracker(struct inode *inode,
+			     struct buffer_head **ret_bh,
+			     int ex,
+			     struct ocfs2_lock_holder *oh)
+{
+	int status;
+	int arg_flags = 0, has_locked;
+	struct ocfs2_lock_res *lockres;
+
+	lockres = &OCFS2_I(inode)->ip_inode_lockres;
+	has_locked = ocfs2_is_locked_by_me(lockres);
+	/* Just get buffer head if the cluster lock has been taken */
+	if (has_locked)
+		arg_flags = OCFS2_META_LOCK_GETBH;
+
+	if (likely(!has_locked || ret_bh)) {
+		status = ocfs2_inode_lock_full(inode, ret_bh, ex, arg_flags);
+		if (status < 0) {
+			if (status != -ENOENT)
+				mlog_errno(status);
+			return status;
+		}
+	}
+	if (!has_locked)
+		ocfs2_add_holder(lockres, oh);
+
+	return has_locked;
+}
+
+void ocfs2_inode_unlock_tracker(struct inode *inode,
+				int ex,
+				struct ocfs2_lock_holder *oh,
+				int had_lock)
+{
+	struct ocfs2_lock_res *lockres;
+
+	lockres = &OCFS2_I(inode)->ip_inode_lockres;
+	if (!had_lock) {
+		ocfs2_remove_holder(lockres, oh);
+		ocfs2_inode_unlock(inode, ex);
+	}
+}
+
 int ocfs2_orphan_scan_lock(struct ocfs2_super *osb, u32 *seqno)
 {
 	struct ocfs2_lock_res *lockres;
diff --git a/fs/ocfs2/dlmglue.h b/fs/ocfs2/dlmglue.h
index d293a22..a7fc18b 100644
--- a/fs/ocfs2/dlmglue.h
+++ b/fs/ocfs2/dlmglue.h
@@ -70,6 +70,11 @@
 	__be32	lvb_os_seqno;
 };
 
+struct ocfs2_lock_holder {
+	struct list_head oh_list;
+	struct pid *oh_owner_pid;
+};
+
 /* ocfs2_inode_lock_full() 'arg_flags' flags */
 /* don't wait on recovery. */
 #define OCFS2_META_LOCK_RECOVERY	(0x01)
@@ -77,6 +82,8 @@
 #define OCFS2_META_LOCK_NOQUEUE		(0x02)
 /* don't block waiting for the downconvert thread, instead return -EAGAIN */
 #define OCFS2_LOCK_NONBLOCK		(0x04)
+/* just get back disk inode bh if we've got cluster lock. */
+#define OCFS2_META_LOCK_GETBH		(0x08)
 
 /* Locking subclasses of inode cluster lock */
 enum {
@@ -170,4 +177,15 @@
 
 /* To set the locking protocol on module initialization */
 void ocfs2_set_locking_protocol(void);
+
+/* The _tracker pair is used to avoid cluster recursive locking */
+int ocfs2_inode_lock_tracker(struct inode *inode,
+			     struct buffer_head **ret_bh,
+			     int ex,
+			     struct ocfs2_lock_holder *oh);
+void ocfs2_inode_unlock_tracker(struct inode *inode,
+				int ex,
+				struct ocfs2_lock_holder *oh,
+				int had_lock);
+
 #endif	/* DLMGLUE_H */
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index e63af7d..594575e 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -172,6 +172,7 @@
 
 	struct list_head         l_blocked_list;
 	struct list_head         l_mask_waiters;
+	struct list_head	 l_holders;
 
 	unsigned long		 l_flags;
 	char                     l_name[OCFS2_LOCK_ID_MAX_LEN];
diff --git a/fs/orangefs/acl.c b/fs/orangefs/acl.c
index 7a37544..9409aac 100644
--- a/fs/orangefs/acl.c
+++ b/fs/orangefs/acl.c
@@ -61,9 +61,9 @@
 	return acl;
 }
 
-int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+static int __orangefs_set_acl(struct inode *inode, struct posix_acl *acl,
+			      int type)
 {
-	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
 	int error = 0;
 	void *value = NULL;
 	size_t size = 0;
@@ -72,22 +72,6 @@
 	switch (type) {
 	case ACL_TYPE_ACCESS:
 		name = XATTR_NAME_POSIX_ACL_ACCESS;
-		if (acl) {
-			umode_t mode;
-
-			error = posix_acl_update_mode(inode, &mode, &acl);
-			if (error) {
-				gossip_err("%s: posix_acl_update_mode err: %d\n",
-					   __func__,
-					   error);
-				return error;
-			}
-
-			if (inode->i_mode != mode)
-				SetModeFlag(orangefs_inode);
-			inode->i_mode = mode;
-			mark_inode_dirty_sync(inode);
-		}
 		break;
 	case ACL_TYPE_DEFAULT:
 		name = XATTR_NAME_POSIX_ACL_DEFAULT;
@@ -132,6 +116,29 @@
 	return error;
 }
 
+int orangefs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
+{
+	int error;
+
+	if (type == ACL_TYPE_ACCESS && acl) {
+		umode_t mode;
+
+		error = posix_acl_update_mode(inode, &mode, &acl);
+		if (error) {
+			gossip_err("%s: posix_acl_update_mode err: %d\n",
+				   __func__,
+				   error);
+			return error;
+		}
+
+		if (inode->i_mode != mode)
+			SetModeFlag(ORANGEFS_I(inode));
+		inode->i_mode = mode;
+		mark_inode_dirty_sync(inode);
+	}
+	return __orangefs_set_acl(inode, acl, type);
+}
+
 int orangefs_init_acl(struct inode *inode, struct inode *dir)
 {
 	struct orangefs_inode_s *orangefs_inode = ORANGEFS_I(inode);
@@ -146,13 +153,14 @@
 		return error;
 
 	if (default_acl) {
-		error = orangefs_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
+		error = __orangefs_set_acl(inode, default_acl,
+					   ACL_TYPE_DEFAULT);
 		posix_acl_release(default_acl);
 	}
 
 	if (acl) {
 		if (!error)
-			error = orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
+			error = __orangefs_set_acl(inode, acl, ACL_TYPE_ACCESS);
 		posix_acl_release(acl);
 	}
 
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 81818ad..c932ec4 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -60,6 +60,7 @@
 #include <linux/tty.h>
 #include <linux/string.h>
 #include <linux/mman.h>
+#include <linux/sched.h>
 #include <linux/proc_fs.h>
 #include <linux/ioport.h>
 #include <linux/uaccess.h>
@@ -416,7 +417,15 @@
 		 * esp and eip are intentionally zeroed out.  There is no
 		 * non-racy way to read them without freezing the task.
 		 * Programs that need reliable values can use ptrace(2).
+		 *
+		 * The only exception is if the task is core dumping because
+		 * a program is not able to use ptrace(2) in that case. It is
+		 * safe because the task has stopped executing permanently.
 		 */
+		if (permitted && (task->flags & PF_DUMPCORE)) {
+			eip = KSTK_EIP(task);
+			esp = KSTK_ESP(task);
+		}
 	}
 
 	get_task_comm(tcomm, task);
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 18f7612..1370a4e 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2896,6 +2896,52 @@
 }
 #endif /* CONFIG_TASK_IO_ACCOUNTING */
 
+#ifdef CONFIG_DETECT_HUNG_TASK
+static ssize_t proc_hung_task_detection_enabled_read(struct file *file,
+				char __user *buf, size_t count, loff_t *ppos)
+{
+	struct task_struct *task = get_proc_task(file_inode(file));
+	char buffer[PROC_NUMBUF];
+	size_t len;
+	bool hang_detection_enabled;
+
+	if (!task)
+		return -ESRCH;
+	hang_detection_enabled = task->hang_detection_enabled;
+	put_task_struct(task);
+
+	len = snprintf(buffer, sizeof(buffer), "%d\n", hang_detection_enabled);
+
+	return simple_read_from_buffer(buf, sizeof(buffer), ppos, buffer, len);
+}
+
+static ssize_t proc_hung_task_detection_enabled_write(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos)
+{
+	struct task_struct *task;
+	bool hang_detection_enabled;
+	int rv;
+
+	rv = kstrtobool_from_user(buf, count, &hang_detection_enabled);
+	if (rv < 0)
+		return rv;
+
+	task = get_proc_task(file_inode(file));
+	if (!task)
+		return -ESRCH;
+	task->hang_detection_enabled = hang_detection_enabled;
+	put_task_struct(task);
+
+	return count;
+}
+
+static const struct file_operations proc_hung_task_detection_enabled_operations = {
+	.read		= proc_hung_task_detection_enabled_read,
+	.write		= proc_hung_task_detection_enabled_write,
+	.llseek		= generic_file_llseek,
+};
+#endif
+
 #ifdef CONFIG_USER_NS
 static int proc_id_map_open(struct inode *inode, struct file *file,
 	const struct seq_operations *seq_ops)
@@ -3138,6 +3184,10 @@
 #ifdef CONFIG_HARDWALL
 	ONE("hardwall",   S_IRUGO, proc_pid_hardwall),
 #endif
+#ifdef CONFIG_DETECT_HUNG_TASK
+	REG("hang_detection_enabled", 0666,
+		proc_hung_task_detection_enabled_operations),
+#endif
 #ifdef CONFIG_USER_NS
 	REG("uid_map",    S_IRUGO|S_IWUSR, proc_uid_map_operations),
 	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
@@ -3526,6 +3576,10 @@
 #ifdef CONFIG_HARDWALL
 	ONE("hardwall",   S_IRUGO, proc_pid_hardwall),
 #endif
+#ifdef CONFIG_DETECT_HUNG_TASK
+	REG("hang_detection_enabled", 0666,
+		proc_hung_task_detection_enabled_operations),
+#endif
 #ifdef CONFIG_USER_NS
 	REG("uid_map",    S_IRUGO|S_IWUSR, proc_uid_map_operations),
 	REG("gid_map",    S_IRUGO|S_IWUSR, proc_gid_map_operations),
diff --git a/fs/read_write.c b/fs/read_write.c
index e479e24..ba28059 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -114,7 +114,7 @@
 		 * In the generic case the entire file is data, so as long as
 		 * offset isn't at the end of the file then the offset is data.
 		 */
-		if (offset >= eof)
+		if ((unsigned long long)offset >= eof)
 			return -ENXIO;
 		break;
 	case SEEK_HOLE:
@@ -122,7 +122,7 @@
 		 * There is a virtual hole at the end of the file, so as long as
 		 * offset isn't i_size or larger, return i_size.
 		 */
-		if (offset >= eof)
+		if ((unsigned long long)offset >= eof)
 			return -ENXIO;
 		offset = eof;
 		break;
@@ -1518,6 +1518,11 @@
 	if (flags != 0)
 		return -EINVAL;
 
+	if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
+		return -EISDIR;
+	if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
+		return -EINVAL;
+
 	ret = rw_verify_area(READ, file_in, &pos_in, len);
 	if (unlikely(ret))
 		return ret;
diff --git a/fs/sdcardfs/derived_perm.c b/fs/sdcardfs/derived_perm.c
index 1239d1c..fffaad4 100644
--- a/fs/sdcardfs/derived_perm.c
+++ b/fs/sdcardfs/derived_perm.c
@@ -176,6 +176,9 @@
 	gid_t gid = sbi->options.fs_low_gid;
 	struct iattr newattrs;
 
+	if (!sbi->options.gid_derivation)
+		return;
+
 	info = SDCARDFS_I(d_inode(dentry));
 	info_d = info->data;
 	perm = info_d->perm;
diff --git a/fs/sdcardfs/file.c b/fs/sdcardfs/file.c
index 6076c34..5ac0b0b 100644
--- a/fs/sdcardfs/file.c
+++ b/fs/sdcardfs/file.c
@@ -104,12 +104,19 @@
 {
 	long err = -ENOTTY;
 	struct file *lower_file;
+	const struct cred *saved_cred = NULL;
+	struct dentry *dentry = file->f_path.dentry;
+	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 
 	lower_file = sdcardfs_lower_file(file);
 
 	/* XXX: use vfs_ioctl if/when VFS exports it */
 	if (!lower_file || !lower_file->f_op)
 		goto out;
+
+	/* save current_cred and override it */
+	OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
+
 	if (lower_file->f_op->unlocked_ioctl)
 		err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg);
 
@@ -117,6 +124,7 @@
 	if (!err)
 		sdcardfs_copy_and_fix_attrs(file_inode(file),
 				      file_inode(lower_file));
+	REVERT_CRED(saved_cred);
 out:
 	return err;
 }
@@ -127,15 +135,23 @@
 {
 	long err = -ENOTTY;
 	struct file *lower_file;
+	const struct cred *saved_cred = NULL;
+	struct dentry *dentry = file->f_path.dentry;
+	struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb);
 
 	lower_file = sdcardfs_lower_file(file);
 
 	/* XXX: use vfs_ioctl if/when VFS exports it */
 	if (!lower_file || !lower_file->f_op)
 		goto out;
+
+	/* save current_cred and override it */
+	OVERRIDE_CRED(sbi, saved_cred, SDCARDFS_I(file_inode(file)));
+
 	if (lower_file->f_op->compat_ioctl)
 		err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg);
 
+	REVERT_CRED(saved_cred);
 out:
 	return err;
 }
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c
index 683b492..4a971e2 100644
--- a/fs/sdcardfs/inode.c
+++ b/fs/sdcardfs/inode.c
@@ -34,10 +34,14 @@
 	if (!cred)
 		return NULL;
 
-	if (data->under_obb)
-		uid = AID_MEDIA_OBB;
-	else
-		uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
+	if (sbi->options.gid_derivation) {
+		if (data->under_obb)
+			uid = AID_MEDIA_OBB;
+		else
+			uid = multiuser_get_uid(data->userid, sbi->options.fs_low_uid);
+	} else {
+		uid = sbi->options.fs_low_uid;
+	}
 	cred->fsuid = make_kuid(&init_user_ns, uid);
 	cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid);
 
diff --git a/fs/sdcardfs/main.c b/fs/sdcardfs/main.c
index 80825b2..0a2b516 100644
--- a/fs/sdcardfs/main.c
+++ b/fs/sdcardfs/main.c
@@ -32,6 +32,7 @@
 	Opt_multiuser,
 	Opt_userid,
 	Opt_reserved_mb,
+	Opt_gid_derivation,
 	Opt_err,
 };
 
@@ -43,6 +44,7 @@
 	{Opt_mask, "mask=%u"},
 	{Opt_userid, "userid=%d"},
 	{Opt_multiuser, "multiuser"},
+	{Opt_gid_derivation, "derive_gid"},
 	{Opt_reserved_mb, "reserved_mb=%u"},
 	{Opt_err, NULL}
 };
@@ -64,6 +66,8 @@
 	vfsopts->gid = 0;
 	/* by default, 0MB is reserved */
 	opts->reserved_mb = 0;
+	/* by default, gid derivation is off */
+	opts->gid_derivation = false;
 
 	*debug = 0;
 
@@ -115,6 +119,9 @@
 				return 0;
 			opts->reserved_mb = option;
 			break;
+		case Opt_gid_derivation:
+			opts->gid_derivation = true;
+			break;
 		/* unknown option */
 		default:
 			if (!silent)
diff --git a/fs/sdcardfs/sdcardfs.h b/fs/sdcardfs/sdcardfs.h
index 4e0ce49..d1d8bab 100644
--- a/fs/sdcardfs/sdcardfs.h
+++ b/fs/sdcardfs/sdcardfs.h
@@ -219,6 +219,7 @@
 	gid_t fs_low_gid;
 	userid_t fs_user_id;
 	bool multiuser;
+	bool gid_derivation;
 	unsigned int reserved_mb;
 };
 
diff --git a/fs/sdcardfs/super.c b/fs/sdcardfs/super.c
index 7f4539b..b89947d 100644
--- a/fs/sdcardfs/super.c
+++ b/fs/sdcardfs/super.c
@@ -302,6 +302,8 @@
 		seq_printf(m, ",mask=%u", vfsopts->mask);
 	if (opts->fs_user_id)
 		seq_printf(m, ",userid=%u", opts->fs_user_id);
+	if (opts->gid_derivation)
+		seq_puts(m, ",derive_gid");
 	if (opts->reserved_mb != 0)
 		seq_printf(m, ",reserved=%uMB", opts->reserved_mb);
 
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig
index ffb093e..6dd158a 100644
--- a/fs/squashfs/Kconfig
+++ b/fs/squashfs/Kconfig
@@ -26,34 +26,6 @@
 	  If unsure, say N.
 
 choice
-	prompt "File decompression options"
-	depends on SQUASHFS
-	help
-	  Squashfs now supports two options for decompressing file
-	  data.  Traditionally Squashfs has decompressed into an
-	  intermediate buffer and then memcopied it into the page cache.
-	  Squashfs now supports the ability to decompress directly into
-	  the page cache.
-
-	  If unsure, select "Decompress file data into an intermediate buffer"
-
-config SQUASHFS_FILE_CACHE
-	bool "Decompress file data into an intermediate buffer"
-	help
-	  Decompress file data into an intermediate buffer and then
-	  memcopy it into the page cache.
-
-config SQUASHFS_FILE_DIRECT
-	bool "Decompress files directly into the page cache"
-	help
-	  Directly decompress file data into the page cache.
-	  Doing so can significantly improve performance because
-	  it eliminates a memcpy and it also removes the lock contention
-	  on the single buffer.
-
-endchoice
-
-choice
 	prompt "Decompressor parallelisation options"
 	depends on SQUASHFS
 	help
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile
index 246a6f3..fe51f15 100644
--- a/fs/squashfs/Makefile
+++ b/fs/squashfs/Makefile
@@ -5,8 +5,7 @@
 obj-$(CONFIG_SQUASHFS) += squashfs.o
 squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
 squashfs-y += namei.o super.o symlink.o decompressor.o
-squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o
-squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o
+squashfs-y += file_direct.o page_actor.o
 squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o
 squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o
 squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c
index ce62a38..7077476 100644
--- a/fs/squashfs/block.c
+++ b/fs/squashfs/block.c
@@ -28,9 +28,12 @@
 
 #include <linux/fs.h>
 #include <linux/vfs.h>
+#include <linux/bio.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/pagemap.h>
 #include <linux/buffer_head.h>
+#include <linux/workqueue.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -38,45 +41,382 @@
 #include "decompressor.h"
 #include "page_actor.h"
 
-/*
- * Read the metadata block length, this is stored in the first two
- * bytes of the metadata block.
- */
-static struct buffer_head *get_block_length(struct super_block *sb,
-			u64 *cur_index, int *offset, int *length)
+static struct workqueue_struct *squashfs_read_wq;
+
+struct squashfs_read_request {
+	struct super_block *sb;
+	u64 index;
+	int length;
+	int compressed;
+	int offset;
+	u64 read_end;
+	struct squashfs_page_actor *output;
+	enum {
+		SQUASHFS_COPY,
+		SQUASHFS_DECOMPRESS,
+		SQUASHFS_METADATA,
+	} data_processing;
+	bool synchronous;
+
+	/*
+	 * If the read is synchronous, it is possible to retrieve information
+	 * about the request by setting these pointers.
+	 */
+	int *res;
+	int *bytes_read;
+	int *bytes_uncompressed;
+
+	int nr_buffers;
+	struct buffer_head **bh;
+	struct work_struct offload;
+};
+
+struct squashfs_bio_request {
+	struct buffer_head **bh;
+	int nr_buffers;
+};
+
+static int squashfs_bio_submit(struct squashfs_read_request *req);
+
+int squashfs_init_read_wq(void)
 {
-	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	struct buffer_head *bh;
+	squashfs_read_wq = create_workqueue("SquashFS read wq");
+	return !!squashfs_read_wq;
+}
 
-	bh = sb_bread(sb, *cur_index);
-	if (bh == NULL)
-		return NULL;
+void squashfs_destroy_read_wq(void)
+{
+	flush_workqueue(squashfs_read_wq);
+	destroy_workqueue(squashfs_read_wq);
+}
 
-	if (msblk->devblksize - *offset == 1) {
-		*length = (unsigned char) bh->b_data[*offset];
-		put_bh(bh);
-		bh = sb_bread(sb, ++(*cur_index));
-		if (bh == NULL)
-			return NULL;
-		*length |= (unsigned char) bh->b_data[0] << 8;
-		*offset = 1;
-	} else {
-		*length = (unsigned char) bh->b_data[*offset] |
-			(unsigned char) bh->b_data[*offset + 1] << 8;
-		*offset += 2;
+static void free_read_request(struct squashfs_read_request *req, int error)
+{
+	if (!req->synchronous)
+		squashfs_page_actor_free(req->output, error);
+	if (req->res)
+		*(req->res) = error;
+	kfree(req->bh);
+	kfree(req);
+}
 
-		if (*offset == msblk->devblksize) {
-			put_bh(bh);
-			bh = sb_bread(sb, ++(*cur_index));
-			if (bh == NULL)
-				return NULL;
-			*offset = 0;
+static void squashfs_process_blocks(struct squashfs_read_request *req)
+{
+	int error = 0;
+	int bytes, i, length;
+	struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+	struct squashfs_page_actor *actor = req->output;
+	struct buffer_head **bh = req->bh;
+	int nr_buffers = req->nr_buffers;
+
+	for (i = 0; i < nr_buffers; ++i) {
+		if (!bh[i])
+			continue;
+		wait_on_buffer(bh[i]);
+		if (!buffer_uptodate(bh[i]))
+			error = -EIO;
+	}
+	if (error)
+		goto cleanup;
+
+	if (req->data_processing == SQUASHFS_METADATA) {
+		/* Extract the length of the metadata block */
+		if (req->offset != msblk->devblksize - 1) {
+			length = le16_to_cpup((__le16 *)
+					(bh[0]->b_data + req->offset));
+		} else {
+			length = (unsigned char)bh[0]->b_data[req->offset];
+			length |= (unsigned char)bh[1]->b_data[0] << 8;
+		}
+		req->compressed = SQUASHFS_COMPRESSED(length);
+		req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+						       : SQUASHFS_COPY;
+		length = SQUASHFS_COMPRESSED_SIZE(length);
+		if (req->index + length + 2 > req->read_end) {
+			for (i = 0; i < nr_buffers; ++i)
+				put_bh(bh[i]);
+			kfree(bh);
+			req->length = length;
+			req->index += 2;
+			squashfs_bio_submit(req);
+			return;
+		}
+		req->length = length;
+		req->offset = (req->offset + 2) % PAGE_SIZE;
+		if (req->offset < 2) {
+			put_bh(bh[0]);
+			++bh;
+			--nr_buffers;
+		}
+	}
+	if (req->bytes_read)
+		*(req->bytes_read) = req->length;
+
+	if (req->data_processing == SQUASHFS_COPY) {
+		squashfs_bh_to_actor(bh, nr_buffers, req->output, req->offset,
+			req->length, msblk->devblksize);
+	} else if (req->data_processing == SQUASHFS_DECOMPRESS) {
+		req->length = squashfs_decompress(msblk, bh, nr_buffers,
+			req->offset, req->length, actor);
+		if (req->length < 0) {
+			error = -EIO;
+			goto cleanup;
 		}
 	}
 
-	return bh;
+	/* Last page may have trailing bytes not filled */
+	bytes = req->length % PAGE_SIZE;
+	if (bytes && actor->page[actor->pages - 1])
+		zero_user_segment(actor->page[actor->pages - 1], bytes,
+				  PAGE_SIZE);
+
+cleanup:
+	if (req->bytes_uncompressed)
+		*(req->bytes_uncompressed) = req->length;
+	if (error) {
+		for (i = 0; i < nr_buffers; ++i)
+			if (bh[i])
+				put_bh(bh[i]);
+	}
+	free_read_request(req, error);
 }
 
+static void read_wq_handler(struct work_struct *work)
+{
+	squashfs_process_blocks(container_of(work,
+		    struct squashfs_read_request, offload));
+}
+
+static void squashfs_bio_end_io(struct bio *bio)
+{
+	int i;
+	int error = bio->bi_error;
+	struct squashfs_bio_request *bio_req = bio->bi_private;
+
+	bio_put(bio);
+
+	for (i = 0; i < bio_req->nr_buffers; ++i) {
+		if (!bio_req->bh[i])
+			continue;
+		if (!error)
+			set_buffer_uptodate(bio_req->bh[i]);
+		else
+			clear_buffer_uptodate(bio_req->bh[i]);
+		unlock_buffer(bio_req->bh[i]);
+	}
+	kfree(bio_req);
+}
+
+static int bh_is_optional(struct squashfs_read_request *req, int idx)
+{
+	int start_idx, end_idx;
+	struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+	start_idx = (idx * msblk->devblksize - req->offset) >> PAGE_SHIFT;
+	end_idx = ((idx + 1) * msblk->devblksize - req->offset + 1) >> PAGE_SHIFT;
+	if (start_idx >= req->output->pages)
+		return 1;
+	if (start_idx < 0)
+		start_idx = end_idx;
+	if (end_idx >= req->output->pages)
+		end_idx = start_idx;
+	return !req->output->page[start_idx] && !req->output->page[end_idx];
+}
+
+static int actor_getblks(struct squashfs_read_request *req, u64 block)
+{
+	int i;
+
+	req->bh = kmalloc_array(req->nr_buffers, sizeof(*(req->bh)), GFP_NOIO);
+	if (!req->bh)
+		return -ENOMEM;
+
+	for (i = 0; i < req->nr_buffers; ++i) {
+		/*
+		 * When dealing with an uncompressed block, the actor may
+		 * contains NULL pages. There's no need to read the buffers
+		 * associated with these pages.
+		 */
+		if (!req->compressed && bh_is_optional(req, i)) {
+			req->bh[i] = NULL;
+			continue;
+		}
+		req->bh[i] = sb_getblk(req->sb, block + i);
+		if (!req->bh[i]) {
+			while (--i) {
+				if (req->bh[i])
+					put_bh(req->bh[i]);
+			}
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int squashfs_bio_submit(struct squashfs_read_request *req)
+{
+	struct bio *bio = NULL;
+	struct buffer_head *bh;
+	struct squashfs_bio_request *bio_req = NULL;
+	int b = 0, prev_block = 0;
+	struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+	u64 read_start = round_down(req->index, msblk->devblksize);
+	u64 read_end = round_up(req->index + req->length, msblk->devblksize);
+	sector_t block = read_start >> msblk->devblksize_log2;
+	sector_t block_end = read_end >> msblk->devblksize_log2;
+	int offset = read_start - round_down(req->index, PAGE_SIZE);
+	int nr_buffers = block_end - block;
+	int blksz = msblk->devblksize;
+	int bio_max_pages = nr_buffers > BIO_MAX_PAGES ? BIO_MAX_PAGES
+						       : nr_buffers;
+
+	/* Setup the request */
+	req->read_end = read_end;
+	req->offset = req->index - read_start;
+	req->nr_buffers = nr_buffers;
+	if (actor_getblks(req, block) < 0)
+		goto getblk_failed;
+
+	/* Create and submit the BIOs */
+	for (b = 0; b < nr_buffers; ++b, offset += blksz) {
+		bh = req->bh[b];
+		if (!bh || !trylock_buffer(bh))
+			continue;
+		if (buffer_uptodate(bh)) {
+			unlock_buffer(bh);
+			continue;
+		}
+		offset %= PAGE_SIZE;
+
+		/* Append the buffer to the current BIO if it is contiguous */
+		if (bio && bio_req && prev_block + 1 == b) {
+			if (bio_add_page(bio, bh->b_page, blksz, offset)) {
+				bio_req->nr_buffers += 1;
+				prev_block = b;
+				continue;
+			}
+		}
+
+		/* Otherwise, submit the current BIO and create a new one */
+		if (bio)
+			submit_bio(bio);
+		bio_req = kcalloc(1, sizeof(struct squashfs_bio_request),
+				  GFP_NOIO);
+		if (!bio_req)
+			goto req_alloc_failed;
+		bio_req->bh = &req->bh[b];
+		bio = bio_alloc(GFP_NOIO, bio_max_pages);
+		if (!bio)
+			goto bio_alloc_failed;
+		bio->bi_bdev = req->sb->s_bdev;
+		bio->bi_iter.bi_sector = (block + b)
+				       << (msblk->devblksize_log2 - 9);
+		bio_set_op_attrs(bio, REQ_OP_READ, 0);
+		bio->bi_private = bio_req;
+		bio->bi_end_io = squashfs_bio_end_io;
+
+		bio_add_page(bio, bh->b_page, blksz, offset);
+		bio_req->nr_buffers += 1;
+		prev_block = b;
+	}
+	if (bio)
+		submit_bio(bio);
+
+	if (req->synchronous)
+		squashfs_process_blocks(req);
+	else {
+		INIT_WORK(&req->offload, read_wq_handler);
+		schedule_work(&req->offload);
+	}
+	return 0;
+
+bio_alloc_failed:
+	kfree(bio_req);
+req_alloc_failed:
+	unlock_buffer(bh);
+	while (--nr_buffers >= b)
+		if (req->bh[nr_buffers])
+			put_bh(req->bh[nr_buffers]);
+	while (--b >= 0)
+		if (req->bh[b])
+			wait_on_buffer(req->bh[b]);
+getblk_failed:
+	free_read_request(req, -ENOMEM);
+	return -ENOMEM;
+}
+
+static int read_metadata_block(struct squashfs_read_request *req,
+			       u64 *next_index)
+{
+	int ret, error, bytes_read = 0, bytes_uncompressed = 0;
+	struct squashfs_sb_info *msblk = req->sb->s_fs_info;
+
+	if (req->index + 2 > msblk->bytes_used) {
+		free_read_request(req, -EINVAL);
+		return -EINVAL;
+	}
+	req->length = 2;
+
+	/* Do not read beyond the end of the device */
+	if (req->index + req->length > msblk->bytes_used)
+		req->length = msblk->bytes_used - req->index;
+	req->data_processing = SQUASHFS_METADATA;
+
+	/*
+	 * Reading metadata is always synchronous because we don't know the
+	 * length in advance and the function is expected to update
+	 * 'next_index' and return the length.
+	 */
+	req->synchronous = true;
+	req->res = &error;
+	req->bytes_read = &bytes_read;
+	req->bytes_uncompressed = &bytes_uncompressed;
+
+	TRACE("Metadata block @ 0x%llx, %scompressed size %d, src size %d\n",
+	      req->index, req->compressed ? "" : "un", bytes_read,
+	      req->output->length);
+
+	ret = squashfs_bio_submit(req);
+	if (ret)
+		return ret;
+	if (error)
+		return error;
+	if (next_index)
+		*next_index += 2 + bytes_read;
+	return bytes_uncompressed;
+}
+
+static int read_data_block(struct squashfs_read_request *req, int length,
+			   u64 *next_index, bool synchronous)
+{
+	int ret, error = 0, bytes_uncompressed = 0, bytes_read = 0;
+
+	req->compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+	req->length = length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+	req->data_processing = req->compressed ? SQUASHFS_DECOMPRESS
+					       : SQUASHFS_COPY;
+
+	req->synchronous = synchronous;
+	if (synchronous) {
+		req->res = &error;
+		req->bytes_read = &bytes_read;
+		req->bytes_uncompressed = &bytes_uncompressed;
+	}
+
+	TRACE("Data block @ 0x%llx, %scompressed size %d, src size %d\n",
+	      req->index, req->compressed ? "" : "un", req->length,
+	      req->output->length);
+
+	ret = squashfs_bio_submit(req);
+	if (ret)
+		return ret;
+	if (synchronous)
+		ret = error ? error : bytes_uncompressed;
+	if (next_index)
+		*next_index += length;
+	return ret;
+}
 
 /*
  * Read and decompress a metadata block or datablock.  Length is non-zero
@@ -87,128 +427,50 @@
  * generated a larger block - this does occasionally happen with compression
  * algorithms).
  */
-int squashfs_read_data(struct super_block *sb, u64 index, int length,
-		u64 *next_index, struct squashfs_page_actor *output)
+static int __squashfs_read_data(struct super_block *sb, u64 index, int length,
+	u64 *next_index, struct squashfs_page_actor *output, bool sync)
 {
-	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	struct buffer_head **bh;
-	int offset = index & ((1 << msblk->devblksize_log2) - 1);
-	u64 cur_index = index >> msblk->devblksize_log2;
-	int bytes, compressed, b = 0, k = 0, avail, i;
+	struct squashfs_read_request *req;
 
-	bh = kcalloc(((output->length + msblk->devblksize - 1)
-		>> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL);
-	if (bh == NULL)
+	req = kcalloc(1, sizeof(struct squashfs_read_request), GFP_KERNEL);
+	if (!req) {
+		if (!sync)
+			squashfs_page_actor_free(output, -ENOMEM);
 		return -ENOMEM;
-
-	if (length) {
-		/*
-		 * Datablock.
-		 */
-		bytes = -offset;
-		compressed = SQUASHFS_COMPRESSED_BLOCK(length);
-		length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
-		if (next_index)
-			*next_index = index + length;
-
-		TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
-			index, compressed ? "" : "un", length, output->length);
-
-		if (length < 0 || length > output->length ||
-				(index + length) > msblk->bytes_used)
-			goto read_failure;
-
-		for (b = 0; bytes < length; b++, cur_index++) {
-			bh[b] = sb_getblk(sb, cur_index);
-			if (bh[b] == NULL)
-				goto block_release;
-			bytes += msblk->devblksize;
-		}
-		ll_rw_block(REQ_OP_READ, 0, b, bh);
-	} else {
-		/*
-		 * Metadata block.
-		 */
-		if ((index + 2) > msblk->bytes_used)
-			goto read_failure;
-
-		bh[0] = get_block_length(sb, &cur_index, &offset, &length);
-		if (bh[0] == NULL)
-			goto read_failure;
-		b = 1;
-
-		bytes = msblk->devblksize - offset;
-		compressed = SQUASHFS_COMPRESSED(length);
-		length = SQUASHFS_COMPRESSED_SIZE(length);
-		if (next_index)
-			*next_index = index + length + 2;
-
-		TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
-				compressed ? "" : "un", length);
-
-		if (length < 0 || length > output->length ||
-					(index + length) > msblk->bytes_used)
-			goto block_release;
-
-		for (; bytes < length; b++) {
-			bh[b] = sb_getblk(sb, ++cur_index);
-			if (bh[b] == NULL)
-				goto block_release;
-			bytes += msblk->devblksize;
-		}
-		ll_rw_block(REQ_OP_READ, 0, b - 1, bh + 1);
 	}
 
-	for (i = 0; i < b; i++) {
-		wait_on_buffer(bh[i]);
-		if (!buffer_uptodate(bh[i]))
-			goto block_release;
+	req->sb = sb;
+	req->index = index;
+	req->output = output;
+
+	if (next_index)
+		*next_index = index;
+
+	if (length)
+		length = read_data_block(req, length, next_index, sync);
+	else
+		length = read_metadata_block(req, next_index);
+
+	if (length < 0) {
+		ERROR("squashfs_read_data failed to read block 0x%llx\n",
+		      (unsigned long long)index);
+		return -EIO;
 	}
 
-	if (compressed) {
-		length = squashfs_decompress(msblk, bh, b, offset, length,
-			output);
-		if (length < 0)
-			goto read_failure;
-	} else {
-		/*
-		 * Block is uncompressed.
-		 */
-		int in, pg_offset = 0;
-		void *data = squashfs_first_page(output);
-
-		for (bytes = length; k < b; k++) {
-			in = min(bytes, msblk->devblksize - offset);
-			bytes -= in;
-			while (in) {
-				if (pg_offset == PAGE_SIZE) {
-					data = squashfs_next_page(output);
-					pg_offset = 0;
-				}
-				avail = min_t(int, in, PAGE_SIZE -
-						pg_offset);
-				memcpy(data + pg_offset, bh[k]->b_data + offset,
-						avail);
-				in -= avail;
-				pg_offset += avail;
-				offset += avail;
-			}
-			offset = 0;
-			put_bh(bh[k]);
-		}
-		squashfs_finish_page(output);
-	}
-
-	kfree(bh);
 	return length;
+}
 
-block_release:
-	for (; k < b; k++)
-		put_bh(bh[k]);
+int squashfs_read_data(struct super_block *sb, u64 index, int length,
+	u64 *next_index, struct squashfs_page_actor *output)
+{
+	return __squashfs_read_data(sb, index, length, next_index, output,
+				    true);
+}
 
-read_failure:
-	ERROR("squashfs_read_data failed to read block 0x%llx\n",
-					(unsigned long long) index);
-	kfree(bh);
-	return -EIO;
+int squashfs_read_data_async(struct super_block *sb, u64 index, int length,
+	u64 *next_index, struct squashfs_page_actor *output)
+{
+
+	return __squashfs_read_data(sb, index, length, next_index, output,
+				    false);
 }
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c
index 23813c0..05e4244 100644
--- a/fs/squashfs/cache.c
+++ b/fs/squashfs/cache.c
@@ -209,17 +209,14 @@
  */
 void squashfs_cache_delete(struct squashfs_cache *cache)
 {
-	int i, j;
+	int i;
 
 	if (cache == NULL)
 		return;
 
 	for (i = 0; i < cache->entries; i++) {
-		if (cache->entry[i].data) {
-			for (j = 0; j < cache->pages; j++)
-				kfree(cache->entry[i].data[j]);
-			kfree(cache->entry[i].data);
-		}
+		if (cache->entry[i].page)
+			free_page_array(cache->entry[i].page, cache->pages);
 		kfree(cache->entry[i].actor);
 	}
 
@@ -236,7 +233,7 @@
 struct squashfs_cache *squashfs_cache_init(char *name, int entries,
 	int block_size)
 {
-	int i, j;
+	int i;
 	struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
 
 	if (cache == NULL) {
@@ -268,22 +265,13 @@
 		init_waitqueue_head(&cache->entry[i].wait_queue);
 		entry->cache = cache;
 		entry->block = SQUASHFS_INVALID_BLK;
-		entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
-		if (entry->data == NULL) {
+		entry->page = alloc_page_array(cache->pages, GFP_KERNEL);
+		if (!entry->page) {
 			ERROR("Failed to allocate %s cache entry\n", name);
 			goto cleanup;
 		}
-
-		for (j = 0; j < cache->pages; j++) {
-			entry->data[j] = kmalloc(PAGE_SIZE, GFP_KERNEL);
-			if (entry->data[j] == NULL) {
-				ERROR("Failed to allocate %s buffer\n", name);
-				goto cleanup;
-			}
-		}
-
-		entry->actor = squashfs_page_actor_init(entry->data,
-						cache->pages, 0);
+		entry->actor = squashfs_page_actor_init(entry->page,
+			cache->pages, 0, NULL);
 		if (entry->actor == NULL) {
 			ERROR("Failed to allocate %s cache entry\n", name);
 			goto cleanup;
@@ -314,18 +302,20 @@
 		return min(length, entry->length - offset);
 
 	while (offset < entry->length) {
-		void *buff = entry->data[offset / PAGE_SIZE]
-				+ (offset % PAGE_SIZE);
+		void *buff = kmap_atomic(entry->page[offset / PAGE_SIZE])
+			     + (offset % PAGE_SIZE);
 		int bytes = min_t(int, entry->length - offset,
 				PAGE_SIZE - (offset % PAGE_SIZE));
 
 		if (bytes >= remaining) {
 			memcpy(buffer, buff, remaining);
+			kunmap_atomic(buff);
 			remaining = 0;
 			break;
 		}
 
 		memcpy(buffer, buff, bytes);
+		kunmap_atomic(buff);
 		buffer += bytes;
 		remaining -= bytes;
 		offset += bytes;
@@ -416,43 +406,38 @@
 void *squashfs_read_table(struct super_block *sb, u64 block, int length)
 {
 	int pages = (length + PAGE_SIZE - 1) >> PAGE_SHIFT;
-	int i, res;
-	void *table, *buffer, **data;
+	struct page **page;
+	void *buff;
+	int res;
 	struct squashfs_page_actor *actor;
 
-	table = buffer = kmalloc(length, GFP_KERNEL);
-	if (table == NULL)
+	page = alloc_page_array(pages, GFP_KERNEL);
+	if (!page)
 		return ERR_PTR(-ENOMEM);
 
-	data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
-	if (data == NULL) {
+	actor = squashfs_page_actor_init(page, pages, length, NULL);
+	if (actor == NULL) {
 		res = -ENOMEM;
 		goto failed;
 	}
 
-	actor = squashfs_page_actor_init(data, pages, length);
-	if (actor == NULL) {
-		res = -ENOMEM;
-		goto failed2;
-	}
-
-	for (i = 0; i < pages; i++, buffer += PAGE_SIZE)
-		data[i] = buffer;
-
 	res = squashfs_read_data(sb, block, length |
 		SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor);
 
-	kfree(data);
-	kfree(actor);
-
 	if (res < 0)
-		goto failed;
+		goto failed2;
 
-	return table;
+	buff = kmalloc(length, GFP_KERNEL);
+	if (!buff)
+		goto failed2;
+	squashfs_actor_to_buf(actor, buff, length);
+	squashfs_page_actor_free(actor, 0);
+	free_page_array(page, pages);
+	return buff;
 
 failed2:
-	kfree(data);
+	squashfs_page_actor_free(actor, 0);
 failed:
-	kfree(table);
+	free_page_array(page, pages);
 	return ERR_PTR(res);
 }
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c
index d2bc136..7de35bf 100644
--- a/fs/squashfs/decompressor.c
+++ b/fs/squashfs/decompressor.c
@@ -24,7 +24,8 @@
 #include <linux/types.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
-#include <linux/buffer_head.h>
+#include <linux/highmem.h>
+#include <linux/fs.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -94,40 +95,44 @@
 static void *get_comp_opts(struct super_block *sb, unsigned short flags)
 {
 	struct squashfs_sb_info *msblk = sb->s_fs_info;
-	void *buffer = NULL, *comp_opts;
+	void *comp_opts, *buffer = NULL;
+	struct page *page;
 	struct squashfs_page_actor *actor = NULL;
 	int length = 0;
 
+	if (!SQUASHFS_COMP_OPTS(flags))
+		return squashfs_comp_opts(msblk, buffer, length);
+
 	/*
 	 * Read decompressor specific options from file system if present
 	 */
-	if (SQUASHFS_COMP_OPTS(flags)) {
-		buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
-		if (buffer == NULL) {
-			comp_opts = ERR_PTR(-ENOMEM);
-			goto out;
-		}
 
-		actor = squashfs_page_actor_init(&buffer, 1, 0);
-		if (actor == NULL) {
-			comp_opts = ERR_PTR(-ENOMEM);
-			goto out;
-		}
+	page = alloc_page(GFP_KERNEL);
+	if (!page)
+		return ERR_PTR(-ENOMEM);
 
-		length = squashfs_read_data(sb,
-			sizeof(struct squashfs_super_block), 0, NULL, actor);
-
-		if (length < 0) {
-			comp_opts = ERR_PTR(length);
-			goto out;
-		}
+	actor = squashfs_page_actor_init(&page, 1, 0, NULL);
+	if (actor == NULL) {
+		comp_opts = ERR_PTR(-ENOMEM);
+		goto actor_error;
 	}
 
-	comp_opts = squashfs_comp_opts(msblk, buffer, length);
+	length = squashfs_read_data(sb,
+		sizeof(struct squashfs_super_block), 0, NULL, actor);
 
-out:
-	kfree(actor);
-	kfree(buffer);
+	if (length < 0) {
+		comp_opts = ERR_PTR(length);
+		goto read_error;
+	}
+
+	buffer = kmap_atomic(page);
+	comp_opts = squashfs_comp_opts(msblk, buffer, length);
+	kunmap_atomic(buffer);
+
+read_error:
+	squashfs_page_actor_free(actor, 0);
+actor_error:
+	__free_page(page);
 	return comp_opts;
 }
 
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c
index 13d8094..bb2e77e 100644
--- a/fs/squashfs/file.c
+++ b/fs/squashfs/file.c
@@ -47,6 +47,7 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mutex.h>
+#include <linux/mm_inline.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -438,6 +439,21 @@
 	return res;
 }
 
+static int squashfs_readpages_fragment(struct page *page,
+	struct list_head *readahead_pages, struct address_space *mapping)
+{
+	if (!page) {
+		page = lru_to_page(readahead_pages);
+		list_del(&page->lru);
+		if (add_to_page_cache_lru(page, mapping, page->index,
+			mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+			put_page(page);
+			return 0;
+		}
+	}
+	return squashfs_readpage_fragment(page);
+}
+
 static int squashfs_readpage_sparse(struct page *page, int index, int file_end)
 {
 	struct inode *inode = page->mapping->host;
@@ -450,54 +466,105 @@
 	return 0;
 }
 
-static int squashfs_readpage(struct file *file, struct page *page)
+static int squashfs_readpages_sparse(struct page *page,
+	struct list_head *readahead_pages, int index, int file_end,
+	struct address_space *mapping)
 {
-	struct inode *inode = page->mapping->host;
+	if (!page) {
+		page = lru_to_page(readahead_pages);
+		list_del(&page->lru);
+		if (add_to_page_cache_lru(page, mapping, page->index,
+			mapping_gfp_constraint(mapping, GFP_KERNEL))) {
+			put_page(page);
+			return 0;
+		}
+	}
+	return squashfs_readpage_sparse(page, index, file_end);
+}
+
+static int __squashfs_readpages(struct file *file, struct page *page,
+	struct list_head *readahead_pages, unsigned int nr_pages,
+	struct address_space *mapping)
+{
+	struct inode *inode = mapping->host;
 	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
-	int index = page->index >> (msblk->block_log - PAGE_SHIFT);
 	int file_end = i_size_read(inode) >> msblk->block_log;
 	int res;
-	void *pageaddr;
+
+	do {
+		struct page *cur_page = page ? page
+					     : lru_to_page(readahead_pages);
+		int page_index = cur_page->index;
+		int index = page_index >> (msblk->block_log - PAGE_SHIFT);
+
+		if (page_index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
+						PAGE_SHIFT))
+			return 1;
+
+		if (index < file_end || squashfs_i(inode)->fragment_block ==
+						SQUASHFS_INVALID_BLK) {
+			u64 block = 0;
+			int bsize = read_blocklist(inode, index, &block);
+
+			if (bsize < 0)
+				return -1;
+
+			if (bsize == 0) {
+				res = squashfs_readpages_sparse(page,
+					readahead_pages, index, file_end,
+					mapping);
+			} else {
+				res = squashfs_readpages_block(page,
+					readahead_pages, &nr_pages, mapping,
+					page_index, block, bsize);
+			}
+		} else {
+			res = squashfs_readpages_fragment(page,
+				readahead_pages, mapping);
+		}
+		if (res)
+			return 0;
+		page = NULL;
+	} while (readahead_pages && !list_empty(readahead_pages));
+
+	return 0;
+}
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+	int ret;
 
 	TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
-				page->index, squashfs_i(inode)->start);
+	      page->index, squashfs_i(page->mapping->host)->start);
 
-	if (page->index >= ((i_size_read(inode) + PAGE_SIZE - 1) >>
-					PAGE_SHIFT))
-		goto out;
+	get_page(page);
 
-	if (index < file_end || squashfs_i(inode)->fragment_block ==
-					SQUASHFS_INVALID_BLK) {
-		u64 block = 0;
-		int bsize = read_blocklist(inode, index, &block);
-		if (bsize < 0)
-			goto error_out;
-
-		if (bsize == 0)
-			res = squashfs_readpage_sparse(page, index, file_end);
+	ret = __squashfs_readpages(file, page, NULL, 1, page->mapping);
+	if (ret) {
+		flush_dcache_page(page);
+		if (ret < 0)
+			SetPageError(page);
 		else
-			res = squashfs_readpage_block(page, block, bsize);
-	} else
-		res = squashfs_readpage_fragment(page);
+			SetPageUptodate(page);
+		zero_user_segment(page, 0, PAGE_SIZE);
+		unlock_page(page);
+		put_page(page);
+	}
 
-	if (!res)
-		return 0;
+	return 0;
+}
 
-error_out:
-	SetPageError(page);
-out:
-	pageaddr = kmap_atomic(page);
-	memset(pageaddr, 0, PAGE_SIZE);
-	kunmap_atomic(pageaddr);
-	flush_dcache_page(page);
-	if (!PageError(page))
-		SetPageUptodate(page);
-	unlock_page(page);
-
+static int squashfs_readpages(struct file *file, struct address_space *mapping,
+			      struct list_head *pages, unsigned int nr_pages)
+{
+	TRACE("Entered squashfs_readpages, %u pages, first page index %lx\n",
+		nr_pages, lru_to_page(pages)->index);
+	__squashfs_readpages(file, NULL, pages, nr_pages, mapping);
 	return 0;
 }
 
 
 const struct address_space_operations squashfs_aops = {
-	.readpage = squashfs_readpage
+	.readpage = squashfs_readpage,
+	.readpages = squashfs_readpages,
 };
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c
deleted file mode 100644
index f2310d2..0000000
--- a/fs/squashfs/file_cache.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (c) 2013
- * Phillip Lougher <phillip@squashfs.org.uk>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- */
-
-#include <linux/fs.h>
-#include <linux/vfs.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/pagemap.h>
-#include <linux/mutex.h>
-
-#include "squashfs_fs.h"
-#include "squashfs_fs_sb.h"
-#include "squashfs_fs_i.h"
-#include "squashfs.h"
-
-/* Read separately compressed datablock and memcopy into page cache */
-int squashfs_readpage_block(struct page *page, u64 block, int bsize)
-{
-	struct inode *i = page->mapping->host;
-	struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
-		block, bsize);
-	int res = buffer->error;
-
-	if (res)
-		ERROR("Unable to read page, block %llx, size %x\n", block,
-			bsize);
-	else
-		squashfs_copy_cache(page, buffer, buffer->length, 0);
-
-	squashfs_cache_put(buffer);
-	return res;
-}
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c
index cb485d8..dc87f77 100644
--- a/fs/squashfs/file_direct.c
+++ b/fs/squashfs/file_direct.c
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/pagemap.h>
 #include <linux/mutex.h>
+#include <linux/mm_inline.h>
 
 #include "squashfs_fs.h"
 #include "squashfs_fs_sb.h"
@@ -20,157 +21,136 @@
 #include "squashfs.h"
 #include "page_actor.h"
 
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
-	int pages, struct page **page);
-
-/* Read separately compressed datablock directly into page cache */
-int squashfs_readpage_block(struct page *target_page, u64 block, int bsize)
-
+static void release_actor_pages(struct page **page, int pages, int error)
 {
-	struct inode *inode = target_page->mapping->host;
-	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	int i;
 
-	int file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
-	int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
-	int start_index = target_page->index & ~mask;
-	int end_index = start_index | mask;
-	int i, n, pages, missing_pages, bytes, res = -ENOMEM;
+	for (i = 0; i < pages; i++) {
+		if (!page[i])
+			continue;
+		flush_dcache_page(page[i]);
+		if (!error)
+			SetPageUptodate(page[i]);
+		else {
+			SetPageError(page[i]);
+			zero_user_segment(page[i], 0, PAGE_SIZE);
+		}
+		unlock_page(page[i]);
+		put_page(page[i]);
+	}
+	kfree(page);
+}
+
+/*
+ * Create a "page actor" which will kmap and kunmap the
+ * page cache pages appropriately within the decompressor
+ */
+static struct squashfs_page_actor *actor_from_page_cache(
+	unsigned int actor_pages, struct page *target_page,
+	struct list_head *rpages, unsigned int *nr_pages, int start_index,
+	struct address_space *mapping)
+{
 	struct page **page;
 	struct squashfs_page_actor *actor;
-	void *pageaddr;
+	int i, n;
+	gfp_t gfp = mapping_gfp_constraint(mapping, GFP_KERNEL);
 
-	if (end_index > file_end)
-		end_index = file_end;
+	page = kmalloc_array(actor_pages, sizeof(void *), GFP_KERNEL);
+	if (!page)
+		return NULL;
 
-	pages = end_index - start_index + 1;
+	for (i = 0, n = start_index; i < actor_pages; i++, n++) {
+		if (target_page == NULL && rpages && !list_empty(rpages)) {
+			struct page *cur_page = lru_to_page(rpages);
 
-	page = kmalloc_array(pages, sizeof(void *), GFP_KERNEL);
-	if (page == NULL)
-		return res;
+			if (cur_page->index < start_index + actor_pages) {
+				list_del(&cur_page->lru);
+				--(*nr_pages);
+				if (add_to_page_cache_lru(cur_page, mapping,
+							  cur_page->index, gfp))
+					put_page(cur_page);
+				else
+					target_page = cur_page;
+			} else
+				rpages = NULL;
+		}
 
-	/*
-	 * Create a "page actor" which will kmap and kunmap the
-	 * page cache pages appropriately within the decompressor
-	 */
-	actor = squashfs_page_actor_init_special(page, pages, 0);
-	if (actor == NULL)
-		goto out;
-
-	/* Try to grab all the pages covered by the Squashfs block */
-	for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) {
-		page[i] = (n == target_page->index) ? target_page :
-			grab_cache_page_nowait(target_page->mapping, n);
-
-		if (page[i] == NULL) {
-			missing_pages++;
-			continue;
+		if (target_page && target_page->index == n) {
+			page[i] = target_page;
+			target_page = NULL;
+		} else {
+			page[i] = grab_cache_page_nowait(mapping, n);
+			if (page[i] == NULL)
+				continue;
 		}
 
 		if (PageUptodate(page[i])) {
 			unlock_page(page[i]);
 			put_page(page[i]);
 			page[i] = NULL;
-			missing_pages++;
 		}
 	}
 
-	if (missing_pages) {
-		/*
-		 * Couldn't get one or more pages, this page has either
-		 * been VM reclaimed, but others are still in the page cache
-		 * and uptodate, or we're racing with another thread in
-		 * squashfs_readpage also trying to grab them.  Fall back to
-		 * using an intermediate buffer.
-		 */
-		res = squashfs_read_cache(target_page, block, bsize, pages,
-								page);
-		if (res < 0)
-			goto mark_errored;
-
-		goto out;
+	actor = squashfs_page_actor_init(page, actor_pages, 0,
+			release_actor_pages);
+	if (!actor) {
+		release_actor_pages(page, actor_pages, -ENOMEM);
+		kfree(page);
+		return NULL;
 	}
-
-	/* Decompress directly into the page cache buffers */
-	res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor);
-	if (res < 0)
-		goto mark_errored;
-
-	/* Last page may have trailing bytes not filled */
-	bytes = res % PAGE_SIZE;
-	if (bytes) {
-		pageaddr = kmap_atomic(page[pages - 1]);
-		memset(pageaddr + bytes, 0, PAGE_SIZE - bytes);
-		kunmap_atomic(pageaddr);
-	}
-
-	/* Mark pages as uptodate, unlock and release */
-	for (i = 0; i < pages; i++) {
-		flush_dcache_page(page[i]);
-		SetPageUptodate(page[i]);
-		unlock_page(page[i]);
-		if (page[i] != target_page)
-			put_page(page[i]);
-	}
-
-	kfree(actor);
-	kfree(page);
-
-	return 0;
-
-mark_errored:
-	/* Decompression failed, mark pages as errored.  Target_page is
-	 * dealt with by the caller
-	 */
-	for (i = 0; i < pages; i++) {
-		if (page[i] == NULL || page[i] == target_page)
-			continue;
-		flush_dcache_page(page[i]);
-		SetPageError(page[i]);
-		unlock_page(page[i]);
-		put_page(page[i]);
-	}
-
-out:
-	kfree(actor);
-	kfree(page);
-	return res;
+	return actor;
 }
 
+int squashfs_readpages_block(struct page *target_page,
+			     struct list_head *readahead_pages,
+			     unsigned int *nr_pages,
+			     struct address_space *mapping,
+			     int page_index, u64 block, int bsize)
 
-static int squashfs_read_cache(struct page *target_page, u64 block, int bsize,
-	int pages, struct page **page)
 {
-	struct inode *i = target_page->mapping->host;
-	struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb,
-						 block, bsize);
-	int bytes = buffer->length, res = buffer->error, n, offset = 0;
-	void *pageaddr;
+	struct squashfs_page_actor *actor;
+	struct inode *inode = mapping->host;
+	struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+	int start_index, end_index, file_end, actor_pages, res;
+	int mask = (1 << (msblk->block_log - PAGE_SHIFT)) - 1;
 
-	if (res) {
-		ERROR("Unable to read page, block %llx, size %x\n", block,
-			bsize);
-		goto out;
+	/*
+	 * If readpage() is called on an uncompressed datablock, we can just
+	 * read the pages instead of fetching the whole block.
+	 * This greatly improves the performance when a process keep doing
+	 * random reads because we only fetch the necessary data.
+	 * The readahead algorithm will take care of doing speculative reads
+	 * if necessary.
+	 * We can't read more than 1 block even if readahead provides use more
+	 * pages because we don't know yet if the next block is compressed or
+	 * not.
+	 */
+	if (bsize && !SQUASHFS_COMPRESSED_BLOCK(bsize)) {
+		u64 block_end = block + msblk->block_size;
+
+		block += (page_index & mask) * PAGE_SIZE;
+		actor_pages = (block_end - block) / PAGE_SIZE;
+		if (*nr_pages < actor_pages)
+			actor_pages = *nr_pages;
+		start_index = page_index;
+		bsize = min_t(int, bsize, (PAGE_SIZE * actor_pages)
+					  | SQUASHFS_COMPRESSED_BIT_BLOCK);
+	} else {
+		file_end = (i_size_read(inode) - 1) >> PAGE_SHIFT;
+		start_index = page_index & ~mask;
+		end_index = start_index | mask;
+		if (end_index > file_end)
+			end_index = file_end;
+		actor_pages = end_index - start_index + 1;
 	}
 
-	for (n = 0; n < pages && bytes > 0; n++,
-			bytes -= PAGE_SIZE, offset += PAGE_SIZE) {
-		int avail = min_t(int, bytes, PAGE_SIZE);
+	actor = actor_from_page_cache(actor_pages, target_page,
+				      readahead_pages, nr_pages, start_index,
+				      mapping);
+	if (!actor)
+		return -ENOMEM;
 
-		if (page[n] == NULL)
-			continue;
-
-		pageaddr = kmap_atomic(page[n]);
-		squashfs_copy_data(pageaddr, buffer, offset, avail);
-		memset(pageaddr + avail, 0, PAGE_SIZE - avail);
-		kunmap_atomic(pageaddr);
-		flush_dcache_page(page[n]);
-		SetPageUptodate(page[n]);
-		unlock_page(page[n]);
-		if (page[n] != target_page)
-			put_page(page[n]);
-	}
-
-out:
-	squashfs_cache_put(buffer);
-	return res;
+	res = squashfs_read_data_async(inode->i_sb, block, bsize, NULL,
+				       actor);
+	return res < 0 ? res : 0;
 }
diff --git a/fs/squashfs/lz4_wrapper.c b/fs/squashfs/lz4_wrapper.c
index ff4468b..df4fa3c 100644
--- a/fs/squashfs/lz4_wrapper.c
+++ b/fs/squashfs/lz4_wrapper.c
@@ -94,39 +94,17 @@
 	struct buffer_head **bh, int b, int offset, int length,
 	struct squashfs_page_actor *output)
 {
-	struct squashfs_lz4 *stream = strm;
-	void *buff = stream->input, *data;
-	int avail, i, bytes = length, res;
+	int res;
 	size_t dest_len = output->length;
+	struct squashfs_lz4 *stream = strm;
 
-	for (i = 0; i < b; i++) {
-		avail = min(bytes, msblk->devblksize - offset);
-		memcpy(buff, bh[i]->b_data + offset, avail);
-		buff += avail;
-		bytes -= avail;
-		offset = 0;
-		put_bh(bh[i]);
-	}
-
+	squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+		msblk->devblksize);
 	res = lz4_decompress_unknownoutputsize(stream->input, length,
 					stream->output, &dest_len);
 	if (res)
 		return -EIO;
-
-	bytes = dest_len;
-	data = squashfs_first_page(output);
-	buff = stream->output;
-	while (data) {
-		if (bytes <= PAGE_SIZE) {
-			memcpy(data, buff, bytes);
-			break;
-		}
-		memcpy(data, buff, PAGE_SIZE);
-		buff += PAGE_SIZE;
-		bytes -= PAGE_SIZE;
-		data = squashfs_next_page(output);
-	}
-	squashfs_finish_page(output);
+	squashfs_buf_to_actor(stream->output, output, dest_len);
 
 	return dest_len;
 }
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c
index 934c17e..2c844d5 100644
--- a/fs/squashfs/lzo_wrapper.c
+++ b/fs/squashfs/lzo_wrapper.c
@@ -79,45 +79,19 @@
 	struct buffer_head **bh, int b, int offset, int length,
 	struct squashfs_page_actor *output)
 {
-	struct squashfs_lzo *stream = strm;
-	void *buff = stream->input, *data;
-	int avail, i, bytes = length, res;
+	int res;
 	size_t out_len = output->length;
+	struct squashfs_lzo *stream = strm;
 
-	for (i = 0; i < b; i++) {
-		avail = min(bytes, msblk->devblksize - offset);
-		memcpy(buff, bh[i]->b_data + offset, avail);
-		buff += avail;
-		bytes -= avail;
-		offset = 0;
-		put_bh(bh[i]);
-	}
-
+	squashfs_bh_to_buf(bh, b, stream->input, offset, length,
+		msblk->devblksize);
 	res = lzo1x_decompress_safe(stream->input, (size_t)length,
 					stream->output, &out_len);
 	if (res != LZO_E_OK)
-		goto failed;
+		return -EIO;
+	squashfs_buf_to_actor(stream->output, output, out_len);
 
-	res = bytes = (int)out_len;
-	data = squashfs_first_page(output);
-	buff = stream->output;
-	while (data) {
-		if (bytes <= PAGE_SIZE) {
-			memcpy(data, buff, bytes);
-			break;
-		} else {
-			memcpy(data, buff, PAGE_SIZE);
-			buff += PAGE_SIZE;
-			bytes -= PAGE_SIZE;
-			data = squashfs_next_page(output);
-		}
-	}
-	squashfs_finish_page(output);
-
-	return res;
-
-failed:
-	return -EIO;
+	return out_len;
 }
 
 const struct squashfs_decompressor squashfs_lzo_comp_ops = {
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c
index 9b7b1b6..e348f56 100644
--- a/fs/squashfs/page_actor.c
+++ b/fs/squashfs/page_actor.c
@@ -9,79 +9,11 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/pagemap.h>
+#include <linux/buffer_head.h>
 #include "page_actor.h"
 
-/*
- * This file contains implementations of page_actor for decompressing into
- * an intermediate buffer, and for decompressing directly into the
- * page cache.
- *
- * Calling code should avoid sleeping between calls to squashfs_first_page()
- * and squashfs_finish_page().
- */
-
-/* Implementation of page_actor for decompressing into intermediate buffer */
-static void *cache_first_page(struct squashfs_page_actor *actor)
-{
-	actor->next_page = 1;
-	return actor->buffer[0];
-}
-
-static void *cache_next_page(struct squashfs_page_actor *actor)
-{
-	if (actor->next_page == actor->pages)
-		return NULL;
-
-	return actor->buffer[actor->next_page++];
-}
-
-static void cache_finish_page(struct squashfs_page_actor *actor)
-{
-	/* empty */
-}
-
-struct squashfs_page_actor *squashfs_page_actor_init(void **buffer,
-	int pages, int length)
-{
-	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
-
-	if (actor == NULL)
-		return NULL;
-
-	actor->length = length ? : pages * PAGE_SIZE;
-	actor->buffer = buffer;
-	actor->pages = pages;
-	actor->next_page = 0;
-	actor->squashfs_first_page = cache_first_page;
-	actor->squashfs_next_page = cache_next_page;
-	actor->squashfs_finish_page = cache_finish_page;
-	return actor;
-}
-
-/* Implementation of page_actor for decompressing directly into page cache. */
-static void *direct_first_page(struct squashfs_page_actor *actor)
-{
-	actor->next_page = 1;
-	return actor->pageaddr = kmap_atomic(actor->page[0]);
-}
-
-static void *direct_next_page(struct squashfs_page_actor *actor)
-{
-	if (actor->pageaddr)
-		kunmap_atomic(actor->pageaddr);
-
-	return actor->pageaddr = actor->next_page == actor->pages ? NULL :
-		kmap_atomic(actor->page[actor->next_page++]);
-}
-
-static void direct_finish_page(struct squashfs_page_actor *actor)
-{
-	if (actor->pageaddr)
-		kunmap_atomic(actor->pageaddr);
-}
-
-struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page,
-	int pages, int length)
+struct squashfs_page_actor *squashfs_page_actor_init(struct page **page,
+	int pages, int length, void (*release_pages)(struct page **, int, int))
 {
 	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
 
@@ -93,8 +25,129 @@
 	actor->pages = pages;
 	actor->next_page = 0;
 	actor->pageaddr = NULL;
-	actor->squashfs_first_page = direct_first_page;
-	actor->squashfs_next_page = direct_next_page;
-	actor->squashfs_finish_page = direct_finish_page;
+	actor->release_pages = release_pages;
 	return actor;
 }
+
+void squashfs_page_actor_free(struct squashfs_page_actor *actor, int error)
+{
+	if (!actor)
+		return;
+
+	if (actor->release_pages)
+		actor->release_pages(actor->page, actor->pages, error);
+	kfree(actor);
+}
+
+void squashfs_actor_to_buf(struct squashfs_page_actor *actor, void *buf,
+	int length)
+{
+	void *pageaddr;
+	int pos = 0, avail, i;
+
+	for (i = 0; i < actor->pages && pos < length; ++i) {
+		avail = min_t(int, length - pos, PAGE_SIZE);
+		if (actor->page[i]) {
+			pageaddr = kmap_atomic(actor->page[i]);
+			memcpy(buf + pos, pageaddr, avail);
+			kunmap_atomic(pageaddr);
+		}
+		pos += avail;
+	}
+}
+
+void squashfs_buf_to_actor(void *buf, struct squashfs_page_actor *actor,
+	int length)
+{
+	void *pageaddr;
+	int pos = 0, avail, i;
+
+	for (i = 0; i < actor->pages && pos < length; ++i) {
+		avail = min_t(int, length - pos, PAGE_SIZE);
+		if (actor->page[i]) {
+			pageaddr = kmap_atomic(actor->page[i]);
+			memcpy(pageaddr, buf + pos, avail);
+			kunmap_atomic(pageaddr);
+		}
+		pos += avail;
+	}
+}
+
+void squashfs_bh_to_actor(struct buffer_head **bh, int nr_buffers,
+	struct squashfs_page_actor *actor, int offset, int length, int blksz)
+{
+	void *kaddr = NULL;
+	int bytes = 0, pgoff = 0, b = 0, p = 0, avail, i;
+
+	while (bytes < length) {
+		if (actor->page[p]) {
+			kaddr = kmap_atomic(actor->page[p]);
+			while (pgoff < PAGE_SIZE && bytes < length) {
+				avail = min_t(int, blksz - offset,
+						PAGE_SIZE - pgoff);
+				memcpy(kaddr + pgoff, bh[b]->b_data + offset,
+				       avail);
+				pgoff += avail;
+				bytes += avail;
+				offset = (offset + avail) % blksz;
+				if (!offset) {
+					put_bh(bh[b]);
+					++b;
+				}
+			}
+			kunmap_atomic(kaddr);
+			pgoff = 0;
+		} else {
+			for (i = 0; i < PAGE_SIZE / blksz; ++i) {
+				if (bh[b])
+					put_bh(bh[b]);
+				++b;
+			}
+			bytes += PAGE_SIZE;
+		}
+		++p;
+	}
+}
+
+void squashfs_bh_to_buf(struct buffer_head **bh, int nr_buffers, void *buf,
+	int offset, int length, int blksz)
+{
+	int i, avail, bytes = 0;
+
+	for (i = 0; i < nr_buffers && bytes < length; ++i) {
+		avail = min_t(int, length - bytes, blksz - offset);
+		if (bh[i]) {
+			memcpy(buf + bytes, bh[i]->b_data + offset, avail);
+			put_bh(bh[i]);
+		}
+		bytes += avail;
+		offset = 0;
+	}
+}
+
+void free_page_array(struct page **page, int nr_pages)
+{
+	int i;
+
+	for (i = 0; i < nr_pages; ++i)
+		__free_page(page[i]);
+	kfree(page);
+}
+
+struct page **alloc_page_array(int nr_pages, int gfp_mask)
+{
+	int i;
+	struct page **page;
+
+	page = kcalloc(nr_pages, sizeof(struct page *), gfp_mask);
+	if (!page)
+		return NULL;
+	for (i = 0; i < nr_pages; ++i) {
+		page[i] = alloc_page(gfp_mask);
+		if (!page[i]) {
+			free_page_array(page, i);
+			return NULL;
+		}
+	}
+	return page;
+}
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h
index 98537ea..aa1ed79 100644
--- a/fs/squashfs/page_actor.h
+++ b/fs/squashfs/page_actor.h
@@ -5,77 +5,61 @@
  * Phillip Lougher <phillip@squashfs.org.uk>
  *
  * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
+ * the COPYING file in the top-level squashfsory.
  */
 
-#ifndef CONFIG_SQUASHFS_FILE_DIRECT
 struct squashfs_page_actor {
-	void	**page;
+	struct page	**page;
+	void	*pageaddr;
 	int	pages;
 	int	length;
 	int	next_page;
+	void	(*release_pages)(struct page **, int, int);
 };
 
-static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page,
-	int pages, int length)
-{
-	struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL);
+extern struct squashfs_page_actor *squashfs_page_actor_init(struct page **,
+	int, int, void (*)(struct page **, int, int));
+extern void squashfs_page_actor_free(struct squashfs_page_actor *, int);
 
-	if (actor == NULL)
-		return NULL;
+extern void squashfs_actor_to_buf(struct squashfs_page_actor *, void *, int);
+extern void squashfs_buf_to_actor(void *, struct squashfs_page_actor *, int);
+extern void squashfs_bh_to_actor(struct buffer_head **, int,
+	struct squashfs_page_actor *, int, int, int);
+extern void squashfs_bh_to_buf(struct buffer_head **, int, void *, int, int,
+	int);
 
-	actor->length = length ? : pages * PAGE_SIZE;
-	actor->page = page;
-	actor->pages = pages;
-	actor->next_page = 0;
-	return actor;
-}
-
+/*
+ * Calling code should avoid sleeping between calls to squashfs_first_page()
+ * and squashfs_finish_page().
+ */
 static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
 {
 	actor->next_page = 1;
-	return actor->page[0];
+	return actor->pageaddr = actor->page[0] ? kmap_atomic(actor->page[0])
+						: NULL;
 }
 
 static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
 {
-	return actor->next_page == actor->pages ? NULL :
-		actor->page[actor->next_page++];
+	if (!IS_ERR_OR_NULL(actor->pageaddr))
+		kunmap_atomic(actor->pageaddr);
+
+	if (actor->next_page == actor->pages)
+		return actor->pageaddr = ERR_PTR(-ENODATA);
+
+	actor->pageaddr = actor->page[actor->next_page] ?
+	    kmap_atomic(actor->page[actor->next_page]) : NULL;
+	++actor->next_page;
+	return actor->pageaddr;
 }
 
 static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
 {
-	/* empty */
+	if (!IS_ERR_OR_NULL(actor->pageaddr))
+		kunmap_atomic(actor->pageaddr);
 }
-#else
-struct squashfs_page_actor {
-	union {
-		void		**buffer;
-		struct page	**page;
-	};
-	void	*pageaddr;
-	void    *(*squashfs_first_page)(struct squashfs_page_actor *);
-	void    *(*squashfs_next_page)(struct squashfs_page_actor *);
-	void    (*squashfs_finish_page)(struct squashfs_page_actor *);
-	int	pages;
-	int	length;
-	int	next_page;
-};
 
-extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int);
-extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page
-							 **, int, int);
-static inline void *squashfs_first_page(struct squashfs_page_actor *actor)
-{
-	return actor->squashfs_first_page(actor);
-}
-static inline void *squashfs_next_page(struct squashfs_page_actor *actor)
-{
-	return actor->squashfs_next_page(actor);
-}
-static inline void squashfs_finish_page(struct squashfs_page_actor *actor)
-{
-	actor->squashfs_finish_page(actor);
-}
-#endif
+extern struct page **alloc_page_array(int, int);
+extern void free_page_array(struct page **, int);
+
 #endif
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h
index 887d6d2..f4faab5 100644
--- a/fs/squashfs/squashfs.h
+++ b/fs/squashfs/squashfs.h
@@ -28,8 +28,12 @@
 #define WARNING(s, args...)	pr_warn("SQUASHFS: "s, ## args)
 
 /* block.c */
+extern int squashfs_init_read_wq(void);
+extern void squashfs_destroy_read_wq(void);
 extern int squashfs_read_data(struct super_block *, u64, int, u64 *,
 				struct squashfs_page_actor *);
+extern int squashfs_read_data_async(struct super_block *, u64, int, u64 *,
+				struct squashfs_page_actor *);
 
 /* cache.c */
 extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
@@ -70,8 +74,9 @@
 void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int,
 				int);
 
-/* file_xxx.c */
-extern int squashfs_readpage_block(struct page *, u64, int);
+/* file_direct.c */
+extern int squashfs_readpages_block(struct page *, struct list_head *,
+	unsigned int *, struct address_space *, int, u64, int);
 
 /* id.c */
 extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
index 1da565c..8a6995d 100644
--- a/fs/squashfs/squashfs_fs_sb.h
+++ b/fs/squashfs/squashfs_fs_sb.h
@@ -49,7 +49,7 @@
 	int			num_waiters;
 	wait_queue_head_t	wait_queue;
 	struct squashfs_cache	*cache;
-	void			**data;
+	struct page		**page;
 	struct squashfs_page_actor	*actor;
 };
 
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c
index cf01e15..e2a0a73 100644
--- a/fs/squashfs/super.c
+++ b/fs/squashfs/super.c
@@ -444,9 +444,15 @@
 	if (err)
 		return err;
 
+	if (!squashfs_init_read_wq()) {
+		destroy_inodecache();
+		return -ENOMEM;
+	}
+
 	err = register_filesystem(&squashfs_fs_type);
 	if (err) {
 		destroy_inodecache();
+		squashfs_destroy_read_wq();
 		return err;
 	}
 
@@ -460,6 +466,7 @@
 {
 	unregister_filesystem(&squashfs_fs_type);
 	destroy_inodecache();
+	squashfs_destroy_read_wq();
 }
 
 
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c
index 6bfaef7..2f7be1f 100644
--- a/fs/squashfs/xz_wrapper.c
+++ b/fs/squashfs/xz_wrapper.c
@@ -55,7 +55,7 @@
 	struct comp_opts *opts;
 	int err = 0, n;
 
-	opts = kmalloc(sizeof(*opts), GFP_KERNEL);
+	opts = kmalloc(sizeof(*opts), GFP_ATOMIC);
 	if (opts == NULL) {
 		err = -ENOMEM;
 		goto out2;
@@ -136,6 +136,7 @@
 	enum xz_ret xz_err;
 	int avail, total = 0, k = 0;
 	struct squashfs_xz *stream = strm;
+	void *buf = NULL;
 
 	xz_dec_reset(stream->state);
 	stream->buf.in_pos = 0;
@@ -156,12 +157,20 @@
 
 		if (stream->buf.out_pos == stream->buf.out_size) {
 			stream->buf.out = squashfs_next_page(output);
-			if (stream->buf.out != NULL) {
+			if (!IS_ERR(stream->buf.out)) {
 				stream->buf.out_pos = 0;
 				total += PAGE_SIZE;
 			}
 		}
 
+		if (!stream->buf.out) {
+			if (!buf) {
+				buf = kmalloc(PAGE_SIZE, GFP_ATOMIC);
+				if (!buf)
+					goto out;
+			}
+			stream->buf.out = buf;
+		}
 		xz_err = xz_dec_run(stream->state, &stream->buf);
 
 		if (stream->buf.in_pos == stream->buf.in_size && k < b)
@@ -173,11 +182,13 @@
 	if (xz_err != XZ_STREAM_END || k < b)
 		goto out;
 
+	kfree(buf);
 	return total + stream->buf.out_pos;
 
 out:
 	for (; k < b; k++)
 		put_bh(bh[k]);
+	kfree(buf);
 
 	return -EIO;
 }
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c
index 2ec24d1..d917c72 100644
--- a/fs/squashfs/zlib_wrapper.c
+++ b/fs/squashfs/zlib_wrapper.c
@@ -66,6 +66,7 @@
 	struct buffer_head **bh, int b, int offset, int length,
 	struct squashfs_page_actor *output)
 {
+	void *buf = NULL;
 	int zlib_err, zlib_init = 0, k = 0;
 	z_stream *stream = strm;
 
@@ -84,10 +85,19 @@
 
 		if (stream->avail_out == 0) {
 			stream->next_out = squashfs_next_page(output);
-			if (stream->next_out != NULL)
+			if (!IS_ERR(stream->next_out))
 				stream->avail_out = PAGE_SIZE;
 		}
 
+		if (!stream->next_out) {
+			if (!buf) {
+				buf = kmalloc(PAGE_SIZE, GFP_ATOMIC);
+				if (!buf)
+					goto out;
+			}
+			stream->next_out = buf;
+		}
+
 		if (!zlib_init) {
 			zlib_err = zlib_inflateInit(stream);
 			if (zlib_err != Z_OK) {
@@ -115,11 +125,13 @@
 	if (k < b)
 		goto out;
 
+	kfree(buf);
 	return stream->total_out;
 
 out:
 	for (; k < b; k++)
 		put_bh(bh[k]);
+	kfree(buf);
 
 	return -EIO;
 }
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 7ec77f8..ab8dd15 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -50,8 +50,7 @@
 static inline bool isalarm(struct timerfd_ctx *ctx)
 {
 	return ctx->clockid == CLOCK_REALTIME_ALARM ||
-		ctx->clockid == CLOCK_BOOTTIME_ALARM ||
-		ctx->clockid == CLOCK_POWEROFF_ALARM;
+		ctx->clockid == CLOCK_BOOTTIME_ALARM;
 }
 
 /*
@@ -143,8 +142,7 @@
 {
 	spin_lock(&ctx->cancel_lock);
 	if ((ctx->clockid == CLOCK_REALTIME ||
-	     ctx->clockid == CLOCK_REALTIME_ALARM ||
-	     ctx->clockid == CLOCK_POWEROFF_ALARM) &&
+	     ctx->clockid == CLOCK_REALTIME_ALARM) &&
 	    (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
 		if (!ctx->might_cancel) {
 			ctx->might_cancel = true;
@@ -176,7 +174,6 @@
 	enum hrtimer_mode htmode;
 	ktime_t texp;
 	int clockid = ctx->clockid;
-	enum alarmtimer_type type;
 
 	htmode = (flags & TFD_TIMER_ABSTIME) ?
 		HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
@@ -187,8 +184,10 @@
 	ctx->tintv = timespec_to_ktime(ktmr->it_interval);
 
 	if (isalarm(ctx)) {
-		type = clock2alarm(ctx->clockid);
-		alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
+		alarm_init(&ctx->t.alarm,
+			   ctx->clockid == CLOCK_REALTIME_ALARM ?
+			   ALARM_REALTIME : ALARM_BOOTTIME,
+			   timerfd_alarmproc);
 	} else {
 		hrtimer_init(&ctx->t.tmr, clockid, htmode);
 		hrtimer_set_expires(&ctx->t.tmr, texp);
@@ -388,7 +387,6 @@
 {
 	int ufd;
 	struct timerfd_ctx *ctx;
-	enum alarmtimer_type type;
 
 	/* Check the TFD_* constants for consistency.  */
 	BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
@@ -399,8 +397,7 @@
 	     clockid != CLOCK_REALTIME &&
 	     clockid != CLOCK_REALTIME_ALARM &&
 	     clockid != CLOCK_BOOTTIME &&
-	     clockid != CLOCK_BOOTTIME_ALARM &&
-	     clockid != CLOCK_POWEROFF_ALARM))
+	     clockid != CLOCK_BOOTTIME_ALARM))
 		return -EINVAL;
 
 	if (!capable(CAP_WAKE_ALARM) &&
@@ -416,12 +413,13 @@
 	spin_lock_init(&ctx->cancel_lock);
 	ctx->clockid = clockid;
 
-	if (isalarm(ctx)) {
-		type = clock2alarm(ctx->clockid);
-		alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
-	} else {
+	if (isalarm(ctx))
+		alarm_init(&ctx->t.alarm,
+			   ctx->clockid == CLOCK_REALTIME_ALARM ?
+			   ALARM_REALTIME : ALARM_BOOTTIME,
+			   timerfd_alarmproc);
+	else
 		hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
-	}
 
 	ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
 
@@ -498,10 +496,6 @@
 	ret = timerfd_setup(ctx, flags, new);
 
 	spin_unlock_irq(&ctx->wqh.lock);
-
-	if (ctx->clockid == CLOCK_POWEROFF_ALARM)
-		set_power_on_alarm();
-
 	fdput(f);
 	return ret;
 }
diff --git a/fs/xattr.c b/fs/xattr.c
index ed8c374..932b906 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -249,7 +249,7 @@
 	}
 	memcpy(value, buffer, len);
 out:
-	security_release_secctx(buffer, len);
+	kfree(buffer);
 out_noalloc:
 	return len;
 }
diff --git a/fs/xfs/kmem.c b/fs/xfs/kmem.c
index 339c696..bb2beae 100644
--- a/fs/xfs/kmem.c
+++ b/fs/xfs/kmem.c
@@ -24,24 +24,6 @@
 #include "kmem.h"
 #include "xfs_message.h"
 
-/*
- * Greedy allocation.  May fail and may return vmalloced memory.
- */
-void *
-kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize)
-{
-	void		*ptr;
-	size_t		kmsize = maxsize;
-
-	while (!(ptr = vzalloc(kmsize))) {
-		if ((kmsize >>= 1) <= minsize)
-			kmsize = minsize;
-	}
-	if (ptr)
-		*size = kmsize;
-	return ptr;
-}
-
 void *
 kmem_alloc(size_t size, xfs_km_flags_t flags)
 {
diff --git a/fs/xfs/kmem.h b/fs/xfs/kmem.h
index 689f746..f0fc84f 100644
--- a/fs/xfs/kmem.h
+++ b/fs/xfs/kmem.h
@@ -69,8 +69,6 @@
 }
 
 
-extern void *kmem_zalloc_greedy(size_t *, size_t, size_t);
-
 static inline void *
 kmem_zalloc(size_t size, xfs_km_flags_t flags)
 {
diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c
index 33db69b..eed8f58 100644
--- a/fs/xfs/libxfs/xfs_ag_resv.c
+++ b/fs/xfs/libxfs/xfs_ag_resv.c
@@ -157,7 +157,8 @@
 	trace_xfs_ag_resv_free(pag, type, 0);
 
 	resv = xfs_perag_resv(pag, type);
-	pag->pag_mount->m_ag_max_usable += resv->ar_asked;
+	if (pag->pag_agno == 0)
+		pag->pag_mount->m_ag_max_usable += resv->ar_asked;
 	/*
 	 * AGFL blocks are always considered "free", so whatever
 	 * was reserved at mount time must be given back at umount.
@@ -217,7 +218,14 @@
 		return error;
 	}
 
-	mp->m_ag_max_usable -= ask;
+	/*
+	 * Reduce the maximum per-AG allocation length by however much we're
+	 * trying to reserve for an AG.  Since this is a filesystem-wide
+	 * counter, we only make the adjustment for AG 0.  This assumes that
+	 * there aren't any AGs hungrier for per-AG reservation than AG 0.
+	 */
+	if (pag->pag_agno == 0)
+		mp->m_ag_max_usable -= ask;
 
 	resv = xfs_perag_resv(pag, type);
 	resv->ar_asked = ask;
diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c
index 9f06a21..c3702cd 100644
--- a/fs/xfs/libxfs/xfs_alloc.c
+++ b/fs/xfs/libxfs/xfs_alloc.c
@@ -1579,6 +1579,10 @@
 
 				bp = xfs_btree_get_bufs(args->mp, args->tp,
 					args->agno, fbno, 0);
+				if (!bp) {
+					error = -EFSCORRUPTED;
+					goto error0;
+				}
 				xfs_trans_binval(args->tp, bp);
 			}
 			args->len = 1;
@@ -2136,6 +2140,10 @@
 		if (error)
 			goto out_agbp_relse;
 		bp = xfs_btree_get_bufs(mp, tp, args->agno, bno, 0);
+		if (!bp) {
+			error = -EFSCORRUPTED;
+			goto out_agbp_relse;
+		}
 		xfs_trans_binval(tp, bp);
 	}
 
diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c
index 2852521..c6c15e5 100644
--- a/fs/xfs/libxfs/xfs_attr_leaf.c
+++ b/fs/xfs/libxfs/xfs_attr_leaf.c
@@ -351,7 +351,7 @@
 
 	err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
 				XFS_ATTR_FORK, &xfs_attr3_leaf_buf_ops);
-	if (!err && tp)
+	if (!err && tp && *bpp)
 		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_ATTR_LEAF_BUF);
 	return err;
 }
diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 2a8cbd1..7eb9970 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -579,7 +579,7 @@
 
 #else
 #define xfs_bmap_check_leaf_extents(cur, ip, whichfork)		do { } while (0)
-#define	xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)
+#define	xfs_bmap_validate_ret(bno,len,flags,mval,onmap,nmap)	do { } while (0)
 #endif /* DEBUG */
 
 /*
@@ -4057,6 +4057,17 @@
 	}
 }
 
+/* trim extent to within eof */
+void
+xfs_trim_extent_eof(
+	struct xfs_bmbt_irec	*irec,
+	struct xfs_inode	*ip)
+
+{
+	xfs_trim_extent(irec, 0, XFS_B_TO_FSB(ip->i_mount,
+					      i_size_read(VFS_I(ip))));
+}
+
 /*
  * Trim the returned map to the required bounds
  */
@@ -5555,6 +5566,8 @@
 	int			whichfork;	/* data or attribute fork */
 	xfs_fsblock_t		sum;
 	xfs_filblks_t		len = *rlen;	/* length to unmap in file */
+	xfs_fileoff_t		max_len;
+	xfs_agnumber_t		prev_agno = NULLAGNUMBER, agno;
 
 	trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
@@ -5576,6 +5589,16 @@
 	ASSERT(len > 0);
 	ASSERT(nexts >= 0);
 
+	/*
+	 * Guesstimate how many blocks we can unmap without running the risk of
+	 * blowing out the transaction with a mix of EFIs and reflink
+	 * adjustments.
+	 */
+	if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
+		max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
+	else
+		max_len = len;
+
 	if (!(ifp->if_flags & XFS_IFEXTENTS) &&
 	    (error = xfs_iread_extents(tp, ip, whichfork)))
 		return error;
@@ -5621,7 +5644,7 @@
 
 	extno = 0;
 	while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
-	       (nexts == 0 || extno < nexts)) {
+	       (nexts == 0 || extno < nexts) && max_len > 0) {
 		/*
 		 * Is the found extent after a hole in which bno lives?
 		 * Just back up to the previous extent, if so.
@@ -5647,6 +5670,17 @@
 		ASSERT(ep != NULL);
 		del = got;
 		wasdel = isnullstartblock(del.br_startblock);
+
+		/*
+		 * Make sure we don't touch multiple AGF headers out of order
+		 * in a single transaction, as that could cause AB-BA deadlocks.
+		 */
+		if (!wasdel) {
+			agno = XFS_FSB_TO_AGNO(mp, del.br_startblock);
+			if (prev_agno != NULLAGNUMBER && prev_agno > agno)
+				break;
+			prev_agno = agno;
+		}
 		if (got.br_startoff < start) {
 			del.br_startoff = start;
 			del.br_blockcount -= start - got.br_startoff;
@@ -5655,6 +5689,15 @@
 		}
 		if (del.br_startoff + del.br_blockcount > bno + 1)
 			del.br_blockcount = bno + 1 - del.br_startoff;
+
+		/* How much can we safely unmap? */
+		if (max_len < del.br_blockcount) {
+			del.br_startoff += del.br_blockcount - max_len;
+			if (!wasdel)
+				del.br_startblock += del.br_blockcount - max_len;
+			del.br_blockcount = max_len;
+		}
+
 		sum = del.br_startblock + del.br_blockcount;
 		if (isrt &&
 		    (mod = do_mod(sum, mp->m_sb.sb_rextsize))) {
@@ -5835,6 +5878,7 @@
 		if (!isrt && wasdel)
 			xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount, false);
 
+		max_len -= del.br_blockcount;
 		bno = del.br_startoff - 1;
 nodelete:
 		/*
@@ -6604,25 +6648,33 @@
 	int				whichfork,
 	xfs_fileoff_t			startoff,
 	xfs_fsblock_t			startblock,
-	xfs_filblks_t			blockcount,
+	xfs_filblks_t			*blockcount,
 	xfs_exntst_t			state)
 {
 	struct xfs_bmbt_irec		bmap;
 	int				nimaps = 1;
 	xfs_fsblock_t			firstfsb;
 	int				flags = XFS_BMAPI_REMAP;
-	int				done;
 	int				error = 0;
 
 	bmap.br_startblock = startblock;
 	bmap.br_startoff = startoff;
-	bmap.br_blockcount = blockcount;
+	bmap.br_blockcount = *blockcount;
 	bmap.br_state = state;
 
+	/*
+	 * firstfsb is tied to the transaction lifetime and is used to
+	 * ensure correct AG locking order and schedule work item
+	 * continuations.  XFS_BUI_MAX_FAST_EXTENTS (== 1) restricts us
+	 * to only making one bmap call per transaction, so it should
+	 * be safe to have it as a local variable here.
+	 */
+	firstfsb = NULLFSBLOCK;
+
 	trace_xfs_bmap_deferred(tp->t_mountp,
 			XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
 			XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
-			ip->i_ino, whichfork, startoff, blockcount, state);
+			ip->i_ino, whichfork, startoff, *blockcount, state);
 
 	if (whichfork != XFS_DATA_FORK && whichfork != XFS_ATTR_FORK)
 		return -EFSCORRUPTED;
@@ -6641,12 +6693,11 @@
 					bmap.br_blockcount, flags, &firstfsb,
 					bmap.br_blockcount, &bmap, &nimaps,
 					dfops);
+		*blockcount = 0;
 		break;
 	case XFS_BMAP_UNMAP:
-		error = xfs_bunmapi(tp, ip, bmap.br_startoff,
-				bmap.br_blockcount, flags, 1, &firstfsb,
-				dfops, &done);
-		ASSERT(done);
+		error = __xfs_bunmapi(tp, ip, startoff, blockcount,
+				XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
 		break;
 	default:
 		ASSERT(0);
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index e7d40b3..f1446d1 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -196,6 +196,7 @@
 
 void	xfs_trim_extent(struct xfs_bmbt_irec *irec, xfs_fileoff_t bno,
 		xfs_filblks_t len);
+void	xfs_trim_extent_eof(struct xfs_bmbt_irec *, struct xfs_inode *);
 int	xfs_bmap_add_attrfork(struct xfs_inode *ip, int size, int rsvd);
 void	xfs_bmap_local_to_extents_empty(struct xfs_inode *ip, int whichfork);
 void	xfs_bmap_add_free(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
@@ -265,7 +266,7 @@
 int	xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
 		struct xfs_inode *ip, enum xfs_bmap_intent_type type,
 		int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
-		xfs_filblks_t blockcount, xfs_exntst_t state);
+		xfs_filblks_t *blockcount, xfs_exntst_t state);
 int	xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
 		struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
 int	xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c
index 5c39186..9968a74 100644
--- a/fs/xfs/libxfs/xfs_bmap_btree.c
+++ b/fs/xfs/libxfs/xfs_bmap_btree.c
@@ -888,6 +888,7 @@
 	cur = xfs_bmbt_init_cursor(ip->i_mount, tp, ip, whichfork);
 	if (!cur)
 		return -ENOMEM;
+	cur->bc_private.b.flags |= XFS_BTCUR_BPRV_INVALID_OWNER;
 
 	error = xfs_btree_change_owner(cur, new_owner, buffer_list);
 	xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR);
diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c
index 91c6891..4ad1e21 100644
--- a/fs/xfs/libxfs/xfs_btree.c
+++ b/fs/xfs/libxfs/xfs_btree.c
@@ -714,7 +714,8 @@
 	 * Get the block pointer for this level.
 	 */
 	block = xfs_btree_get_block(cur, level, &bp);
-	xfs_btree_check_block(cur, block, level, bp);
+	if (xfs_btree_check_block(cur, block, level, bp))
+		return 0;
 	/*
 	 * It's empty, there is no such record.
 	 */
@@ -743,7 +744,8 @@
 	 * Get the block pointer for this level.
 	 */
 	block = xfs_btree_get_block(cur, level, &bp);
-	xfs_btree_check_block(cur, block, level, bp);
+	if (xfs_btree_check_block(cur, block, level, bp))
+		return 0;
 	/*
 	 * It's empty, there is no such record.
 	 */
@@ -1772,6 +1774,7 @@
 
 	/* Check the inode owner since the verifiers don't. */
 	if (xfs_sb_version_hascrc(&cur->bc_mp->m_sb) &&
+	    !(cur->bc_private.b.flags & XFS_BTCUR_BPRV_INVALID_OWNER) &&
 	    (cur->bc_flags & XFS_BTREE_LONG_PTRS) &&
 	    be64_to_cpu((*blkp)->bb_u.l.bb_owner) !=
 			cur->bc_private.b.ip->i_ino)
@@ -4432,10 +4435,15 @@
 
 	/* modify the owner */
 	block = xfs_btree_get_block(cur, level, &bp);
-	if (cur->bc_flags & XFS_BTREE_LONG_PTRS)
+	if (cur->bc_flags & XFS_BTREE_LONG_PTRS) {
+		if (block->bb_u.l.bb_owner == cpu_to_be64(bbcoi->new_owner))
+			return 0;
 		block->bb_u.l.bb_owner = cpu_to_be64(bbcoi->new_owner);
-	else
+	} else {
+		if (block->bb_u.s.bb_owner == cpu_to_be32(bbcoi->new_owner))
+			return 0;
 		block->bb_u.s.bb_owner = cpu_to_be32(bbcoi->new_owner);
+	}
 
 	/*
 	 * If the block is a root block hosted in an inode, we might not have a
@@ -4444,16 +4452,19 @@
 	 * block is formatted into the on-disk inode fork. We still change it,
 	 * though, so everything is consistent in memory.
 	 */
-	if (bp) {
-		if (cur->bc_tp) {
-			xfs_trans_ordered_buf(cur->bc_tp, bp);
-			xfs_btree_log_block(cur, bp, XFS_BB_OWNER);
-		} else {
-			xfs_buf_delwri_queue(bp, bbcoi->buffer_list);
-		}
-	} else {
+	if (!bp) {
 		ASSERT(cur->bc_flags & XFS_BTREE_ROOT_IN_INODE);
 		ASSERT(level == cur->bc_nlevels - 1);
+		return 0;
+	}
+
+	if (cur->bc_tp) {
+		if (!xfs_trans_ordered_buf(cur->bc_tp, bp)) {
+			xfs_btree_log_block(cur, bp, XFS_BB_OWNER);
+			return -EAGAIN;
+		}
+	} else {
+		xfs_buf_delwri_queue(bp, bbcoi->buffer_list);
 	}
 
 	return 0;
diff --git a/fs/xfs/libxfs/xfs_btree.h b/fs/xfs/libxfs/xfs_btree.h
index 3b0fc1a..33c7be2 100644
--- a/fs/xfs/libxfs/xfs_btree.h
+++ b/fs/xfs/libxfs/xfs_btree.h
@@ -268,7 +268,8 @@
 			short		forksize;	/* fork's inode space */
 			char		whichfork;	/* data or attr fork */
 			char		flags;		/* flags */
-#define	XFS_BTCUR_BPRV_WASDEL	1			/* was delayed */
+#define	XFS_BTCUR_BPRV_WASDEL		(1<<0)		/* was delayed */
+#define	XFS_BTCUR_BPRV_INVALID_OWNER	(1<<1)		/* for ext swap */
 		} b;
 	}		bc_private;	/* per-btree type data */
 } xfs_btree_cur_t;
diff --git a/fs/xfs/libxfs/xfs_da_btree.c b/fs/xfs/libxfs/xfs_da_btree.c
index 1bdf288..b305dbf 100644
--- a/fs/xfs/libxfs/xfs_da_btree.c
+++ b/fs/xfs/libxfs/xfs_da_btree.c
@@ -263,7 +263,7 @@
 
 	err = xfs_da_read_buf(tp, dp, bno, mappedbno, bpp,
 					which_fork, &xfs_da3_node_buf_ops);
-	if (!err && tp) {
+	if (!err && tp && *bpp) {
 		struct xfs_da_blkinfo	*info = (*bpp)->b_addr;
 		int			type;
 
diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c
index aa17cb7..43c902f 100644
--- a/fs/xfs/libxfs/xfs_dir2_block.c
+++ b/fs/xfs/libxfs/xfs_dir2_block.c
@@ -139,7 +139,7 @@
 
 	err = xfs_da_read_buf(tp, dp, mp->m_dir_geo->datablk, -1, bpp,
 				XFS_DATA_FORK, &xfs_dir3_block_buf_ops);
-	if (!err && tp)
+	if (!err && tp && *bpp)
 		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_BLOCK_BUF);
 	return err;
 }
diff --git a/fs/xfs/libxfs/xfs_dir2_leaf.c b/fs/xfs/libxfs/xfs_dir2_leaf.c
index b887fb2..f2e342e 100644
--- a/fs/xfs/libxfs/xfs_dir2_leaf.c
+++ b/fs/xfs/libxfs/xfs_dir2_leaf.c
@@ -268,7 +268,7 @@
 
 	err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
 				XFS_DATA_FORK, &xfs_dir3_leaf1_buf_ops);
-	if (!err && tp)
+	if (!err && tp && *bpp)
 		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAF1_BUF);
 	return err;
 }
@@ -285,7 +285,7 @@
 
 	err = xfs_da_read_buf(tp, dp, fbno, mappedbno, bpp,
 				XFS_DATA_FORK, &xfs_dir3_leafn_buf_ops);
-	if (!err && tp)
+	if (!err && tp && *bpp)
 		xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_DIR_LEAFN_BUF);
 	return err;
 }
diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c
index a2818f6..42fef07 100644
--- a/fs/xfs/libxfs/xfs_ialloc.c
+++ b/fs/xfs/libxfs/xfs_ialloc.c
@@ -368,8 +368,6 @@
 				 * transaction and pin the log appropriately.
 				 */
 				xfs_trans_ordered_buf(tp, fbuf);
-				xfs_trans_log_buf(tp, fbuf, 0,
-						  BBTOB(fbuf->b_length) - 1);
 			}
 		} else {
 			fbuf->b_flags |= XBF_DONE;
@@ -1123,6 +1121,7 @@
 	int			error;
 	int			offset;
 	int			i, j;
+	int			searchdistance = 10;
 
 	pag = xfs_perag_get(mp, agno);
 
@@ -1149,7 +1148,6 @@
 	if (pagno == agno) {
 		int		doneleft;	/* done, to the left */
 		int		doneright;	/* done, to the right */
-		int		searchdistance = 10;
 
 		error = xfs_inobt_lookup(cur, pagino, XFS_LOOKUP_LE, &i);
 		if (error)
@@ -1210,21 +1208,9 @@
 		/*
 		 * Loop until we find an inode chunk with a free inode.
 		 */
-		while (!doneleft || !doneright) {
+		while (--searchdistance > 0 && (!doneleft || !doneright)) {
 			int	useleft;  /* using left inode chunk this time */
 
-			if (!--searchdistance) {
-				/*
-				 * Not in range - save last search
-				 * location and allocate a new inode
-				 */
-				xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
-				pag->pagl_leftrec = trec.ir_startino;
-				pag->pagl_rightrec = rec.ir_startino;
-				pag->pagl_pagino = pagino;
-				goto newino;
-			}
-
 			/* figure out the closer block if both are valid. */
 			if (!doneleft && !doneright) {
 				useleft = pagino -
@@ -1236,13 +1222,13 @@
 
 			/* free inodes to the left? */
 			if (useleft && trec.ir_freecount) {
-				rec = trec;
 				xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
 				cur = tcur;
 
 				pag->pagl_leftrec = trec.ir_startino;
 				pag->pagl_rightrec = rec.ir_startino;
 				pag->pagl_pagino = pagino;
+				rec = trec;
 				goto alloc_inode;
 			}
 
@@ -1268,26 +1254,37 @@
 				goto error1;
 		}
 
-		/*
-		 * We've reached the end of the btree. because
-		 * we are only searching a small chunk of the
-		 * btree each search, there is obviously free
-		 * inodes closer to the parent inode than we
-		 * are now. restart the search again.
-		 */
-		pag->pagl_pagino = NULLAGINO;
-		pag->pagl_leftrec = NULLAGINO;
-		pag->pagl_rightrec = NULLAGINO;
-		xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
-		xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
-		goto restart_pagno;
+		if (searchdistance <= 0) {
+			/*
+			 * Not in range - save last search
+			 * location and allocate a new inode
+			 */
+			xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+			pag->pagl_leftrec = trec.ir_startino;
+			pag->pagl_rightrec = rec.ir_startino;
+			pag->pagl_pagino = pagino;
+
+		} else {
+			/*
+			 * We've reached the end of the btree. because
+			 * we are only searching a small chunk of the
+			 * btree each search, there is obviously free
+			 * inodes closer to the parent inode than we
+			 * are now. restart the search again.
+			 */
+			pag->pagl_pagino = NULLAGINO;
+			pag->pagl_leftrec = NULLAGINO;
+			pag->pagl_rightrec = NULLAGINO;
+			xfs_btree_del_cursor(tcur, XFS_BTREE_NOERROR);
+			xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+			goto restart_pagno;
+		}
 	}
 
 	/*
 	 * In a different AG from the parent.
 	 * See if the most recently allocated block has any free.
 	 */
-newino:
 	if (agi->agi_newino != cpu_to_be32(NULLAGINO)) {
 		error = xfs_inobt_lookup(cur, be32_to_cpu(agi->agi_newino),
 					 XFS_LOOKUP_EQ, &i);
diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
index 8a37efe..4e30448 100644
--- a/fs/xfs/libxfs/xfs_inode_fork.c
+++ b/fs/xfs/libxfs/xfs_inode_fork.c
@@ -1539,14 +1539,11 @@
 	xfs_ifork_t	*ifp,		/* inode fork pointer */
 	int		new_size)	/* new indirection array size */
 {
-	int		nlists;		/* number of irec's (ex lists) */
-	int		size;		/* current indirection array size */
-
 	ASSERT(ifp->if_flags & XFS_IFEXTIREC);
-	nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
-	size = nlists * sizeof(xfs_ext_irec_t);
 	ASSERT(ifp->if_real_bytes);
-	ASSERT((new_size >= 0) && (new_size != size));
+	ASSERT((new_size >= 0) &&
+	       (new_size != ((ifp->if_real_bytes / XFS_IEXT_BUFSZ) *
+			     sizeof(xfs_ext_irec_t))));
 	if (new_size == 0) {
 		xfs_iext_destroy(ifp);
 	} else {
diff --git a/fs/xfs/libxfs/xfs_log_format.h b/fs/xfs/libxfs/xfs_log_format.h
index 083cdd6..ce6958b 100644
--- a/fs/xfs/libxfs/xfs_log_format.h
+++ b/fs/xfs/libxfs/xfs_log_format.h
@@ -270,6 +270,7 @@
 	__uint32_t		ilf_fields;	/* flags for fields logged */
 	__uint16_t		ilf_asize;	/* size of attr d/ext/root */
 	__uint16_t		ilf_dsize;	/* size of data/ext/root */
+	__uint32_t		ilf_pad;	/* pad for 64 bit boundary */
 	__uint64_t		ilf_ino;	/* inode number */
 	union {
 		__uint32_t	ilfu_rdev;	/* rdev value for dev inode*/
@@ -280,7 +281,12 @@
 	__int32_t		ilf_boffset;	/* off of inode in buffer */
 } xfs_inode_log_format_t;
 
-typedef struct xfs_inode_log_format_32 {
+/*
+ * Old 32 bit systems will log in this format without the 64 bit
+ * alignment padding. Recovery will detect this and convert it to the
+ * correct format.
+ */
+struct xfs_inode_log_format_32 {
 	__uint16_t		ilf_type;	/* inode log item type */
 	__uint16_t		ilf_size;	/* size of this item */
 	__uint32_t		ilf_fields;	/* flags for fields logged */
@@ -294,24 +300,7 @@
 	__int64_t		ilf_blkno;	/* blkno of inode buffer */
 	__int32_t		ilf_len;	/* len of inode buffer */
 	__int32_t		ilf_boffset;	/* off of inode in buffer */
-} __attribute__((packed)) xfs_inode_log_format_32_t;
-
-typedef struct xfs_inode_log_format_64 {
-	__uint16_t		ilf_type;	/* inode log item type */
-	__uint16_t		ilf_size;	/* size of this item */
-	__uint32_t		ilf_fields;	/* flags for fields logged */
-	__uint16_t		ilf_asize;	/* size of attr d/ext/root */
-	__uint16_t		ilf_dsize;	/* size of data/ext/root */
-	__uint32_t		ilf_pad;	/* pad for 64 bit boundary */
-	__uint64_t		ilf_ino;	/* inode number */
-	union {
-		__uint32_t	ilfu_rdev;	/* rdev value for dev inode*/
-		uuid_t		ilfu_uuid;	/* mount point value */
-	} ilf_u;
-	__int64_t		ilf_blkno;	/* blkno of inode buffer */
-	__int32_t		ilf_len;	/* len of inode buffer */
-	__int32_t		ilf_boffset;	/* off of inode in buffer */
-} xfs_inode_log_format_64_t;
+} __attribute__((packed));
 
 
 /*
diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c
index 82a38d8..d71cb63 100644
--- a/fs/xfs/libxfs/xfs_refcount.c
+++ b/fs/xfs/libxfs/xfs_refcount.c
@@ -784,14 +784,6 @@
 }
 
 /*
- * While we're adjusting the refcounts records of an extent, we have
- * to keep an eye on the number of extents we're dirtying -- run too
- * many in a single transaction and we'll exceed the transaction's
- * reservation and crash the fs.  Each record adds 12 bytes to the
- * log (plus any key updates) so we'll conservatively assume 24 bytes
- * per record.  We must also leave space for btree splits on both ends
- * of the range and space for the CUD and a new CUI.
- *
  * XXX: This is a pretty hand-wavy estimate.  The penalty for guessing
  * true incorrectly is a shutdown FS; the penalty for guessing false
  * incorrectly is more transaction rolls than might be necessary.
@@ -822,7 +814,7 @@
 	else if (overhead > cur->bc_tp->t_log_res)
 		return false;
 	return  cur->bc_tp->t_log_res - overhead >
-		cur->bc_private.a.priv.refc.nr_ops * 32;
+		cur->bc_private.a.priv.refc.nr_ops * XFS_REFCOUNT_ITEM_OVERHEAD;
 }
 
 /*
@@ -1648,6 +1640,10 @@
 	error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp);
 	if (error)
 		goto out_trans;
+	if (!agbp) {
+		error = -ENOMEM;
+		goto out_trans;
+	}
 	cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL);
 
 	/* Find all the leftover CoW staging extents. */
diff --git a/fs/xfs/libxfs/xfs_refcount.h b/fs/xfs/libxfs/xfs_refcount.h
index 098dc668..eafb9d1 100644
--- a/fs/xfs/libxfs/xfs_refcount.h
+++ b/fs/xfs/libxfs/xfs_refcount.h
@@ -67,4 +67,20 @@
 extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
 		xfs_agnumber_t agno);
 
+/*
+ * While we're adjusting the refcounts records of an extent, we have
+ * to keep an eye on the number of extents we're dirtying -- run too
+ * many in a single transaction and we'll exceed the transaction's
+ * reservation and crash the fs.  Each record adds 12 bytes to the
+ * log (plus any key updates) so we'll conservatively assume 32 bytes
+ * per record.  We must also leave space for btree splits on both ends
+ * of the range and space for the CUD and a new CUI.
+ */
+#define XFS_REFCOUNT_ITEM_OVERHEAD	32
+
+static inline xfs_fileoff_t xfs_refcount_max_unmap(int log_res)
+{
+	return (log_res * 3 / 4) / XFS_REFCOUNT_ITEM_OVERHEAD;
+}
+
 #endif	/* __XFS_REFCOUNT_H__ */
diff --git a/fs/xfs/xfs_acl.c b/fs/xfs/xfs_acl.c
index b2d55a3..710a131 100644
--- a/fs/xfs/xfs_acl.c
+++ b/fs/xfs/xfs_acl.c
@@ -247,6 +247,8 @@
 int
 xfs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 {
+	umode_t mode;
+	bool set_mode = false;
 	int error = 0;
 
 	if (!acl)
@@ -257,18 +259,27 @@
 		return error;
 
 	if (type == ACL_TYPE_ACCESS) {
-		umode_t mode;
 		struct posix_acl *old_acl = acl;
 		error = posix_acl_update_mode(inode, &mode, &acl);
 		if (!acl)
 			posix_acl_release(old_acl);
 		if (error)
 			return error;
-		error = xfs_set_mode(inode, mode);
-		if (error)
-			return error;
+		set_mode = true;
 	}
 
  set_acl:
-	return __xfs_set_acl(inode, acl, type);
+	error =  __xfs_set_acl(inode, acl, type);
+	if (error)
+		return error;
+
+	/*
+	 * We set the mode after successfully updating the ACL xattr because the
+	 * xattr update can fail at ENOSPC and we don't want to change the mode
+	 * if the ACL update hasn't been applied.
+	 */
+	if (set_mode)
+		error = xfs_set_mode(inode, mode);
+
+	return error;
 }
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 5789814..d31cd1e 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -90,11 +90,11 @@
  * associated buffer_heads, paying attention to the start and end offsets that
  * we need to process on the page.
  *
- * Landmine Warning: bh->b_end_io() will call end_page_writeback() on the last
- * buffer in the IO. Once it does this, it is unsafe to access the bufferhead or
- * the page at all, as we may be racing with memory reclaim and it can free both
- * the bufferhead chain and the page as it will see the page as clean and
- * unused.
+ * Note that we open code the action in end_buffer_async_write here so that we
+ * only have to iterate over the buffers attached to the page once.  This is not
+ * only more efficient, but also ensures that we only calls end_page_writeback
+ * at the end of the iteration, and thus avoids the pitfall of having the page
+ * and buffers potentially freed after every call to end_buffer_async_write.
  */
 static void
 xfs_finish_page_writeback(
@@ -102,29 +102,45 @@
 	struct bio_vec		*bvec,
 	int			error)
 {
-	unsigned int		end = bvec->bv_offset + bvec->bv_len - 1;
-	struct buffer_head	*head, *bh, *next;
+	struct buffer_head	*head = page_buffers(bvec->bv_page), *bh = head;
+	bool			busy = false;
 	unsigned int		off = 0;
-	unsigned int		bsize;
+	unsigned long		flags;
 
 	ASSERT(bvec->bv_offset < PAGE_SIZE);
 	ASSERT((bvec->bv_offset & (i_blocksize(inode) - 1)) == 0);
-	ASSERT(end < PAGE_SIZE);
+	ASSERT(bvec->bv_offset + bvec->bv_len <= PAGE_SIZE);
 	ASSERT((bvec->bv_len & (i_blocksize(inode) - 1)) == 0);
 
-	bh = head = page_buffers(bvec->bv_page);
-
-	bsize = bh->b_size;
+	local_irq_save(flags);
+	bit_spin_lock(BH_Uptodate_Lock, &head->b_state);
 	do {
-		if (off > end)
-			break;
-		next = bh->b_this_page;
-		if (off < bvec->bv_offset)
-			goto next_bh;
-		bh->b_end_io(bh, !error);
-next_bh:
-		off += bsize;
-	} while ((bh = next) != head);
+		if (off >= bvec->bv_offset &&
+		    off < bvec->bv_offset + bvec->bv_len) {
+			ASSERT(buffer_async_write(bh));
+			ASSERT(bh->b_end_io == NULL);
+
+			if (error) {
+				mapping_set_error(bvec->bv_page->mapping, -EIO);
+				set_buffer_write_io_error(bh);
+				clear_buffer_uptodate(bh);
+				SetPageError(bvec->bv_page);
+			} else {
+				set_buffer_uptodate(bh);
+			}
+			clear_buffer_async_write(bh);
+			unlock_buffer(bh);
+		} else if (buffer_async_write(bh)) {
+			ASSERT(buffer_locked(bh));
+			busy = true;
+		}
+		off += bh->b_size;
+	} while ((bh = bh->b_this_page) != head);
+	bit_spin_unlock(BH_Uptodate_Lock, &head->b_state);
+	local_irq_restore(flags);
+
+	if (!busy)
+		end_page_writeback(bvec->bv_page);
 }
 
 /*
@@ -138,8 +154,10 @@
 	int			error)
 {
 	struct inode		*inode = ioend->io_inode;
-	struct bio		*last = ioend->io_bio;
-	struct bio		*bio, *next;
+	struct bio		*bio = &ioend->io_inline_bio;
+	struct bio		*last = ioend->io_bio, *next;
+	u64			start = bio->bi_iter.bi_sector;
+	bool			quiet = bio_flagged(bio, BIO_QUIET);
 
 	for (bio = &ioend->io_inline_bio; bio; bio = next) {
 		struct bio_vec	*bvec;
@@ -160,6 +178,11 @@
 
 		bio_put(bio);
 	}
+
+	if (unlikely(error && !quiet)) {
+		xfs_err_ratelimited(XFS_I(inode)->i_mount,
+			"writeback error on sector %llu", start);
+	}
 }
 
 /*
@@ -312,7 +335,8 @@
 		error = xfs_reflink_end_cow(ip, offset, size);
 		break;
 	case XFS_IO_UNWRITTEN:
-		error = xfs_iomap_write_unwritten(ip, offset, size);
+		/* writeback should never update isize */
+		error = xfs_iomap_write_unwritten(ip, offset, size, false);
 		break;
 	default:
 		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
@@ -414,6 +438,19 @@
 {
 	offset >>= inode->i_blkbits;
 
+	/*
+	 * We have to make sure the cached mapping is within EOF to protect
+	 * against eofblocks trimming on file release leaving us with a stale
+	 * mapping. Otherwise, a page for a subsequent file extending buffered
+	 * write could get picked up by this writeback cycle and written to the
+	 * wrong blocks.
+	 *
+	 * Note that what we really want here is a generic mapping invalidation
+	 * mechanism to protect us from arbitrary extent modifying contexts, not
+	 * just eofblocks.
+	 */
+	xfs_trim_extent_eof(imap, XFS_I(inode));
+
 	return offset >= imap->br_startoff &&
 		offset < imap->br_startoff + imap->br_blockcount;
 }
@@ -427,7 +464,8 @@
 	ASSERT(!buffer_delay(bh));
 	ASSERT(!buffer_unwritten(bh));
 
-	mark_buffer_async_write(bh);
+	bh->b_end_io = NULL;
+	set_buffer_async_write(bh);
 	set_buffer_uptodate(bh);
 	clear_buffer_dirty(bh);
 }
@@ -701,6 +739,14 @@
 {
 	trace_xfs_invalidatepage(page->mapping->host, page, offset,
 				 length);
+
+	/*
+	 * If we are invalidating the entire page, clear the dirty state from it
+	 * so that we can check for attempts to release dirty cached pages in
+	 * xfs_vm_releasepage().
+	 */
+	if (offset == 0 && length >= PAGE_SIZE)
+		cancel_dirty_page(page);
 	block_invalidatepage(page, offset, length);
 }
 
@@ -1156,25 +1202,27 @@
 	 * mm accommodates an old ext3 case where clean pages might not have had
 	 * the dirty bit cleared. Thus, it can send actual dirty pages to
 	 * ->releasepage() via shrink_active_list(). Conversely,
-	 * block_invalidatepage() can send pages that are still marked dirty
-	 * but otherwise have invalidated buffers.
+	 * block_invalidatepage() can send pages that are still marked dirty but
+	 * otherwise have invalidated buffers.
 	 *
 	 * We want to release the latter to avoid unnecessary buildup of the
-	 * LRU, skip the former and warn if we've left any lingering
-	 * delalloc/unwritten buffers on clean pages. Skip pages with delalloc
-	 * or unwritten buffers and warn if the page is not dirty. Otherwise
-	 * try to release the buffers.
+	 * LRU, so xfs_vm_invalidatepage() clears the page dirty flag on pages
+	 * that are entirely invalidated and need to be released.  Hence the
+	 * only time we should get dirty pages here is through
+	 * shrink_active_list() and so we can simply skip those now.
+	 *
+	 * warn if we've left any lingering delalloc/unwritten buffers on clean
+	 * or invalidated pages we are about to release.
 	 */
+	if (PageDirty(page))
+		return 0;
+
 	xfs_count_page_state(page, &delalloc, &unwritten);
 
-	if (delalloc) {
-		WARN_ON_ONCE(!PageDirty(page));
+	if (WARN_ON_ONCE(delalloc))
 		return 0;
-	}
-	if (unwritten) {
-		WARN_ON_ONCE(!PageDirty(page));
+	if (WARN_ON_ONCE(unwritten))
 		return 0;
-	}
 
 	return try_to_free_buffers(page);
 }
@@ -1508,6 +1556,21 @@
 		return 0;
 	}
 
+	if (flags & XFS_DIO_FLAG_COW)
+		error = xfs_reflink_end_cow(ip, offset, size);
+
+	/*
+	 * Unwritten conversion updates the in-core isize after extent
+	 * conversion but before updating the on-disk size. Updating isize any
+	 * earlier allows a racing dio read to find unwritten extents before
+	 * they are converted.
+	 */
+	if (flags & XFS_DIO_FLAG_UNWRITTEN) {
+		trace_xfs_end_io_direct_write_unwritten(ip, offset, size);
+
+		return xfs_iomap_write_unwritten(ip, offset, size, true);
+	}
+
 	/*
 	 * We need to update the in-core inode size here so that we don't end up
 	 * with the on-disk inode size being outside the in-core inode size. We
@@ -1524,13 +1587,6 @@
 		i_size_write(inode, offset + size);
 	spin_unlock(&ip->i_flags_lock);
 
-	if (flags & XFS_DIO_FLAG_COW)
-		error = xfs_reflink_end_cow(ip, offset, size);
-	if (flags & XFS_DIO_FLAG_UNWRITTEN) {
-		trace_xfs_end_io_direct_write_unwritten(ip, offset, size);
-
-		error = xfs_iomap_write_unwritten(ip, offset, size);
-	}
 	if (flags & XFS_DIO_FLAG_APPEND) {
 		trace_xfs_end_io_direct_write_append(ip, offset, size);
 
@@ -1566,9 +1622,12 @@
 	 * The swap code (ab-)uses ->bmap to get a block mapping and then
 	 * bypasseѕ the file system for actual I/O.  We really can't allow
 	 * that on reflinks inodes, so we have to skip out here.  And yes,
-	 * 0 is the magic code for a bmap error..
+	 * 0 is the magic code for a bmap error.
+	 *
+	 * Since we don't pass back blockdev info, we can't return bmap
+	 * information for rt files either.
 	 */
-	if (xfs_is_reflink_inode(ip)) {
+	if (xfs_is_reflink_inode(ip) || XFS_IS_REALTIME_INODE(ip)) {
 		xfs_iunlock(ip, XFS_IOLOCK_SHARED);
 		return 0;
 	}
diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c
index be0b79d..c664300 100644
--- a/fs/xfs/xfs_attr_inactive.c
+++ b/fs/xfs/xfs_attr_inactive.c
@@ -302,6 +302,8 @@
 						 &bp, XFS_ATTR_FORK);
 			if (error)
 				return error;
+			node = bp->b_addr;
+			btree = dp->d_ops->node_tree_p(node);
 			child_fsb = be32_to_cpu(btree[i + 1].before);
 			xfs_trans_brelse(*trans, bp);
 		}
diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
index c4b90e7..5a54dcd 100644
--- a/fs/xfs/xfs_bmap_item.c
+++ b/fs/xfs/xfs_bmap_item.c
@@ -395,6 +395,7 @@
 	struct xfs_map_extent		*bmap;
 	xfs_fsblock_t			startblock_fsb;
 	xfs_fsblock_t			inode_fsb;
+	xfs_filblks_t			count;
 	bool				op_ok;
 	struct xfs_bud_log_item		*budp;
 	enum xfs_bmap_intent_type	type;
@@ -403,6 +404,7 @@
 	struct xfs_trans		*tp;
 	struct xfs_inode		*ip = NULL;
 	struct xfs_defer_ops		dfops;
+	struct xfs_bmbt_irec		irec;
 	xfs_fsblock_t			firstfsb;
 
 	ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
@@ -480,13 +482,24 @@
 	}
 	xfs_trans_ijoin(tp, ip, 0);
 
+	count = bmap->me_len;
 	error = xfs_trans_log_finish_bmap_update(tp, budp, &dfops, type,
 			ip, whichfork, bmap->me_startoff,
-			bmap->me_startblock, bmap->me_len,
-			state);
+			bmap->me_startblock, &count, state);
 	if (error)
 		goto err_dfops;
 
+	if (count > 0) {
+		ASSERT(type == XFS_BMAP_UNMAP);
+		irec.br_startblock = bmap->me_startblock;
+		irec.br_blockcount = count;
+		irec.br_startoff = bmap->me_startoff;
+		irec.br_state = state;
+		error = xfs_bmap_unmap_extent(tp->t_mountp, &dfops, ip, &irec);
+		if (error)
+			goto err_dfops;
+	}
+
 	/* Finish transaction, free inodes. */
 	error = xfs_defer_finish(&tp, &dfops, NULL);
 	if (error)
diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c
index 87b495e..cb62871 100644
--- a/fs/xfs/xfs_bmap_util.c
+++ b/fs/xfs/xfs_bmap_util.c
@@ -84,6 +84,7 @@
 		GFP_NOFS, true);
 }
 
+#ifdef CONFIG_XFS_RT
 int
 xfs_bmap_rtalloc(
 	struct xfs_bmalloca	*ap)	/* bmap alloc argument struct */
@@ -195,6 +196,7 @@
 	}
 	return 0;
 }
+#endif /* CONFIG_XFS_RT */
 
 /*
  * Check if the endoff is outside the last extent. If so the caller will grow
@@ -1445,7 +1447,19 @@
 		return error;
 
 	/*
-	 * The extent shiting code works on extent granularity. So, if
+	 * Clean out anything hanging around in the cow fork now that
+	 * we've flushed all the dirty data out to disk to avoid having
+	 * CoW extents at the wrong offsets.
+	 */
+	if (xfs_is_reflink_inode(ip)) {
+		error = xfs_reflink_cancel_cow_range(ip, offset, NULLFILEOFF,
+				true);
+		if (error)
+			return error;
+	}
+
+	/*
+	 * The extent shifting code works on extent granularity. So, if
 	 * stop_fsb is not the starting block of extent, we need to split
 	 * the extent at stop_fsb.
 	 */
@@ -1825,29 +1839,18 @@
 	}
 
 	/*
-	 * Before we've swapped the forks, lets set the owners of the forks
-	 * appropriately. We have to do this as we are demand paging the btree
-	 * buffers, and so the validation done on read will expect the owner
-	 * field to be correctly set. Once we change the owners, we can swap the
-	 * inode forks.
+	 * Btree format (v3) inodes have the inode number stamped in the bmbt
+	 * block headers. We can't start changing the bmbt blocks until the
+	 * inode owner change is logged so recovery does the right thing in the
+	 * event of a crash. Set the owner change log flags now and leave the
+	 * bmbt scan as the last step.
 	 */
 	if (ip->i_d.di_version == 3 &&
-	    ip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
+	    ip->i_d.di_format == XFS_DINODE_FMT_BTREE)
 		(*target_log_flags) |= XFS_ILOG_DOWNER;
-		error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK,
-					      tip->i_ino, NULL);
-		if (error)
-			return error;
-	}
-
 	if (tip->i_d.di_version == 3 &&
-	    tip->i_d.di_format == XFS_DINODE_FMT_BTREE) {
+	    tip->i_d.di_format == XFS_DINODE_FMT_BTREE)
 		(*src_log_flags) |= XFS_ILOG_DOWNER;
-		error = xfs_bmbt_change_owner(tp, tip, XFS_DATA_FORK,
-					      ip->i_ino, NULL);
-		if (error)
-			return error;
-	}
 
 	/*
 	 * Swap the data forks of the inodes
@@ -1925,6 +1928,48 @@
 	return 0;
 }
 
+/*
+ * Fix up the owners of the bmbt blocks to refer to the current inode. The
+ * change owner scan attempts to order all modified buffers in the current
+ * transaction. In the event of ordered buffer failure, the offending buffer is
+ * physically logged as a fallback and the scan returns -EAGAIN. We must roll
+ * the transaction in this case to replenish the fallback log reservation and
+ * restart the scan. This process repeats until the scan completes.
+ */
+static int
+xfs_swap_change_owner(
+	struct xfs_trans	**tpp,
+	struct xfs_inode	*ip,
+	struct xfs_inode	*tmpip)
+{
+	int			error;
+	struct xfs_trans	*tp = *tpp;
+
+	do {
+		error = xfs_bmbt_change_owner(tp, ip, XFS_DATA_FORK, ip->i_ino,
+					      NULL);
+		/* success or fatal error */
+		if (error != -EAGAIN)
+			break;
+
+		error = xfs_trans_roll(tpp, NULL);
+		if (error)
+			break;
+		tp = *tpp;
+
+		/*
+		 * Redirty both inodes so they can relog and keep the log tail
+		 * moving forward.
+		 */
+		xfs_trans_ijoin(tp, ip, 0);
+		xfs_trans_ijoin(tp, tmpip, 0);
+		xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+		xfs_trans_log_inode(tp, tmpip, XFS_ILOG_CORE);
+	} while (true);
+
+	return error;
+}
+
 int
 xfs_swap_extents(
 	struct xfs_inode	*ip,	/* target inode */
@@ -1938,8 +1983,8 @@
 	int			error = 0;
 	int			lock_flags;
 	struct xfs_ifork	*cowfp;
-	__uint64_t		f;
-	int			resblks;
+	uint64_t		f;
+	int			resblks = 0;
 
 	/*
 	 * Lock the inodes against other IO, page faults and truncate to
@@ -1987,11 +2032,8 @@
 			  XFS_SWAP_RMAP_SPACE_RES(mp,
 				XFS_IFORK_NEXTENTS(tip, XFS_DATA_FORK),
 				XFS_DATA_FORK);
-		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks,
-				0, 0, &tp);
-	} else
-		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0,
-				0, 0, &tp);
+	}
+	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0, &tp);
 	if (error)
 		goto out_unlock;
 
@@ -2066,17 +2108,54 @@
 		ip->i_d.di_flags2 |= tip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK;
 		tip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
 		tip->i_d.di_flags2 |= f & XFS_DIFLAG2_REFLINK;
+	}
+
+	/* Swap the cow forks. */
+	if (xfs_sb_version_hasreflink(&mp->m_sb)) {
+		xfs_extnum_t	extnum;
+
+		ASSERT(ip->i_cformat == XFS_DINODE_FMT_EXTENTS);
+		ASSERT(tip->i_cformat == XFS_DINODE_FMT_EXTENTS);
+
+		extnum = ip->i_cnextents;
+		ip->i_cnextents = tip->i_cnextents;
+		tip->i_cnextents = extnum;
+
 		cowfp = ip->i_cowfp;
 		ip->i_cowfp = tip->i_cowfp;
 		tip->i_cowfp = cowfp;
-		xfs_inode_set_cowblocks_tag(ip);
-		xfs_inode_set_cowblocks_tag(tip);
+
+		if (ip->i_cowfp && ip->i_cnextents)
+			xfs_inode_set_cowblocks_tag(ip);
+		else
+			xfs_inode_clear_cowblocks_tag(ip);
+		if (tip->i_cowfp && tip->i_cnextents)
+			xfs_inode_set_cowblocks_tag(tip);
+		else
+			xfs_inode_clear_cowblocks_tag(tip);
 	}
 
 	xfs_trans_log_inode(tp, ip,  src_log_flags);
 	xfs_trans_log_inode(tp, tip, target_log_flags);
 
 	/*
+	 * The extent forks have been swapped, but crc=1,rmapbt=0 filesystems
+	 * have inode number owner values in the bmbt blocks that still refer to
+	 * the old inode. Scan each bmbt to fix up the owner values with the
+	 * inode number of the current inode.
+	 */
+	if (src_log_flags & XFS_ILOG_DOWNER) {
+		error = xfs_swap_change_owner(&tp, ip, tip);
+		if (error)
+			goto out_trans_cancel;
+	}
+	if (target_log_flags & XFS_ILOG_DOWNER) {
+		error = xfs_swap_change_owner(&tp, tip, ip);
+		if (error)
+			goto out_trans_cancel;
+	}
+
+	/*
 	 * If this is a synchronous mount, make sure that the
 	 * transaction goes to disk before returning to the user.
 	 */
diff --git a/fs/xfs/xfs_bmap_util.h b/fs/xfs/xfs_bmap_util.h
index f100539..ce330f0 100644
--- a/fs/xfs/xfs_bmap_util.h
+++ b/fs/xfs/xfs_bmap_util.h
@@ -28,7 +28,20 @@
 struct xfs_trans;
 struct xfs_bmalloca;
 
+#ifdef CONFIG_XFS_RT
 int	xfs_bmap_rtalloc(struct xfs_bmalloca *ap);
+#else /* !CONFIG_XFS_RT */
+/*
+ * Attempts to allocate RT extents when RT is disable indicates corruption and
+ * should trigger a shutdown.
+ */
+static inline int
+xfs_bmap_rtalloc(struct xfs_bmalloca *ap)
+{
+	return -EFSCORRUPTED;
+}
+#endif /* CONFIG_XFS_RT */
+
 int	xfs_bmap_eof(struct xfs_inode *ip, xfs_fileoff_t endoff,
 		     int whichfork, int *eof);
 int	xfs_bmap_punch_delalloc_range(struct xfs_inode *ip,
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index 1626927..eca7bae 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -116,7 +116,7 @@
 __xfs_buf_ioacct_dec(
 	struct xfs_buf	*bp)
 {
-	ASSERT(spin_is_locked(&bp->b_lock));
+	lockdep_assert_held(&bp->b_lock);
 
 	if (bp->b_state & XFS_BSTATE_IN_FLIGHT) {
 		bp->b_state &= ~XFS_BSTATE_IN_FLIGHT;
@@ -2022,6 +2022,66 @@
 	return error;
 }
 
+/*
+ * Push a single buffer on a delwri queue.
+ *
+ * The purpose of this function is to submit a single buffer of a delwri queue
+ * and return with the buffer still on the original queue. The waiting delwri
+ * buffer submission infrastructure guarantees transfer of the delwri queue
+ * buffer reference to a temporary wait list. We reuse this infrastructure to
+ * transfer the buffer back to the original queue.
+ *
+ * Note the buffer transitions from the queued state, to the submitted and wait
+ * listed state and back to the queued state during this call. The buffer
+ * locking and queue management logic between _delwri_pushbuf() and
+ * _delwri_queue() guarantee that the buffer cannot be queued to another list
+ * before returning.
+ */
+int
+xfs_buf_delwri_pushbuf(
+	struct xfs_buf		*bp,
+	struct list_head	*buffer_list)
+{
+	LIST_HEAD		(submit_list);
+	int			error;
+
+	ASSERT(bp->b_flags & _XBF_DELWRI_Q);
+
+	trace_xfs_buf_delwri_pushbuf(bp, _RET_IP_);
+
+	/*
+	 * Isolate the buffer to a new local list so we can submit it for I/O
+	 * independently from the rest of the original list.
+	 */
+	xfs_buf_lock(bp);
+	list_move(&bp->b_list, &submit_list);
+	xfs_buf_unlock(bp);
+
+	/*
+	 * Delwri submission clears the DELWRI_Q buffer flag and returns with
+	 * the buffer on the wait list with an associated reference. Rather than
+	 * bounce the buffer from a local wait list back to the original list
+	 * after I/O completion, reuse the original list as the wait list.
+	 */
+	xfs_buf_delwri_submit_buffers(&submit_list, buffer_list);
+
+	/*
+	 * The buffer is now under I/O and wait listed as during typical delwri
+	 * submission. Lock the buffer to wait for I/O completion. Rather than
+	 * remove the buffer from the wait list and release the reference, we
+	 * want to return with the buffer queued to the original list. The
+	 * buffer already sits on the original list with a wait list reference,
+	 * however. If we let the queue inherit that wait list reference, all we
+	 * need to do is reset the DELWRI_Q flag.
+	 */
+	xfs_buf_lock(bp);
+	error = bp->b_error;
+	bp->b_flags |= _XBF_DELWRI_Q;
+	xfs_buf_unlock(bp);
+
+	return error;
+}
+
 int __init
 xfs_buf_init(void)
 {
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index ad514a8..f961b19 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -333,6 +333,7 @@
 extern bool xfs_buf_delwri_queue(struct xfs_buf *, struct list_head *);
 extern int xfs_buf_delwri_submit(struct list_head *);
 extern int xfs_buf_delwri_submit_nowait(struct list_head *);
+extern int xfs_buf_delwri_pushbuf(struct xfs_buf *, struct list_head *);
 
 /* Buffer Daemon Setup Routines */
 extern int xfs_buf_init(void);
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 0306168..e0a0af0 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -29,6 +29,7 @@
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_log.h"
+#include "xfs_inode.h"
 
 
 kmem_zone_t	*xfs_buf_item_zone;
@@ -322,6 +323,8 @@
 	ASSERT((bip->bli_flags & XFS_BLI_STALE) ||
 	       (xfs_blft_from_flags(&bip->__bli_format) > XFS_BLFT_UNKNOWN_BUF
 	        && xfs_blft_from_flags(&bip->__bli_format) < XFS_BLFT_MAX_BUF));
+	ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED) ||
+	       (bip->bli_flags & XFS_BLI_STALE));
 
 
 	/*
@@ -346,16 +349,6 @@
 		bip->bli_flags &= ~XFS_BLI_INODE_BUF;
 	}
 
-	if ((bip->bli_flags & (XFS_BLI_ORDERED|XFS_BLI_STALE)) ==
-							XFS_BLI_ORDERED) {
-		/*
-		 * The buffer has been logged just to order it.  It is not being
-		 * included in the transaction commit, so don't format it.
-		 */
-		trace_xfs_buf_item_format_ordered(bip);
-		return;
-	}
-
 	for (i = 0; i < bip->bli_format_count; i++) {
 		xfs_buf_item_format_segment(bip, lv, &vecp, offset,
 					    &bip->bli_formats[i]);
@@ -574,26 +567,20 @@
 {
 	struct xfs_buf_log_item	*bip = BUF_ITEM(lip);
 	struct xfs_buf		*bp = bip->bli_buf;
-	bool			clean;
-	bool			aborted;
-	int			flags;
+	bool			aborted = !!(lip->li_flags & XFS_LI_ABORTED);
+	bool			hold = !!(bip->bli_flags & XFS_BLI_HOLD);
+	bool			dirty = !!(bip->bli_flags & XFS_BLI_DIRTY);
+#if defined(DEBUG) || defined(XFS_WARN)
+	bool			ordered = !!(bip->bli_flags & XFS_BLI_ORDERED);
+#endif
 
 	/* Clear the buffer's association with this transaction. */
 	bp->b_transp = NULL;
 
 	/*
-	 * If this is a transaction abort, don't return early.  Instead, allow
-	 * the brelse to happen.  Normally it would be done for stale
-	 * (cancelled) buffers at unpin time, but we'll never go through the
-	 * pin/unpin cycle if we abort inside commit.
+	 * The per-transaction state has been copied above so clear it from the
+	 * bli.
 	 */
-	aborted = (lip->li_flags & XFS_LI_ABORTED) ? true : false;
-	/*
-	 * Before possibly freeing the buf item, copy the per-transaction state
-	 * so we can reference it safely later after clearing it from the
-	 * buffer log item.
-	 */
-	flags = bip->bli_flags;
 	bip->bli_flags &= ~(XFS_BLI_LOGGED | XFS_BLI_HOLD | XFS_BLI_ORDERED);
 
 	/*
@@ -601,7 +588,7 @@
 	 * unlock the buffer and free the buf item when the buffer is unpinned
 	 * for the last time.
 	 */
-	if (flags & XFS_BLI_STALE) {
+	if (bip->bli_flags & XFS_BLI_STALE) {
 		trace_xfs_buf_item_unlock_stale(bip);
 		ASSERT(bip->__bli_format.blf_flags & XFS_BLF_CANCEL);
 		if (!aborted) {
@@ -619,40 +606,34 @@
 	 * regardless of whether it is dirty or not. A dirty abort implies a
 	 * shutdown, anyway.
 	 *
-	 * Ordered buffers are dirty but may have no recorded changes, so ensure
-	 * we only release clean items here.
+	 * The bli dirty state should match whether the blf has logged segments
+	 * except for ordered buffers, where only the bli should be dirty.
 	 */
-	clean = (flags & XFS_BLI_DIRTY) ? false : true;
-	if (clean) {
-		int i;
-		for (i = 0; i < bip->bli_format_count; i++) {
-			if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
-				     bip->bli_formats[i].blf_map_size)) {
-				clean = false;
-				break;
-			}
-		}
-	}
+	ASSERT((!ordered && dirty == xfs_buf_item_dirty_format(bip)) ||
+	       (ordered && dirty && !xfs_buf_item_dirty_format(bip)));
 
 	/*
 	 * Clean buffers, by definition, cannot be in the AIL. However, aborted
-	 * buffers may be dirty and hence in the AIL. Therefore if we are
-	 * aborting a buffer and we've just taken the last refernce away, we
-	 * have to check if it is in the AIL before freeing it. We need to free
-	 * it in this case, because an aborted transaction has already shut the
-	 * filesystem down and this is the last chance we will have to do so.
+	 * buffers may be in the AIL regardless of dirty state. An aborted
+	 * transaction that invalidates a buffer already in the AIL may have
+	 * marked it stale and cleared the dirty state, for example.
+	 *
+	 * Therefore if we are aborting a buffer and we've just taken the last
+	 * reference away, we have to check if it is in the AIL before freeing
+	 * it. We need to free it in this case, because an aborted transaction
+	 * has already shut the filesystem down and this is the last chance we
+	 * will have to do so.
 	 */
 	if (atomic_dec_and_test(&bip->bli_refcount)) {
-		if (clean)
-			xfs_buf_item_relse(bp);
-		else if (aborted) {
+		if (aborted) {
 			ASSERT(XFS_FORCED_SHUTDOWN(lip->li_mountp));
 			xfs_trans_ail_remove(lip, SHUTDOWN_LOG_IO_ERROR);
 			xfs_buf_item_relse(bp);
-		}
+		} else if (!dirty)
+			xfs_buf_item_relse(bp);
 	}
 
-	if (!(flags & XFS_BLI_HOLD))
+	if (!hold)
 		xfs_buf_relse(bp);
 }
 
@@ -942,14 +923,22 @@
 
 
 /*
- * Return 1 if the buffer has been logged or ordered in a transaction (at any
- * point, not just the current transaction) and 0 if not.
+ * Return true if the buffer has any ranges logged/dirtied by a transaction,
+ * false otherwise.
  */
-uint
-xfs_buf_item_dirty(
-	xfs_buf_log_item_t	*bip)
+bool
+xfs_buf_item_dirty_format(
+	struct xfs_buf_log_item	*bip)
 {
-	return (bip->bli_flags & XFS_BLI_DIRTY);
+	int			i;
+
+	for (i = 0; i < bip->bli_format_count; i++) {
+		if (!xfs_bitmap_empty(bip->bli_formats[i].blf_data_map,
+			     bip->bli_formats[i].blf_map_size))
+			return true;
+	}
+
+	return false;
 }
 
 STATIC void
@@ -1051,6 +1040,31 @@
 	}
 }
 
+/*
+ * Invoke the error state callback for each log item affected by the failed I/O.
+ *
+ * If a metadata buffer write fails with a non-permanent error, the buffer is
+ * eventually resubmitted and so the completion callbacks are not run. The error
+ * state may need to be propagated to the log items attached to the buffer,
+ * however, so the next AIL push of the item knows hot to handle it correctly.
+ */
+STATIC void
+xfs_buf_do_callbacks_fail(
+	struct xfs_buf		*bp)
+{
+	struct xfs_log_item	*next;
+	struct xfs_log_item	*lip = bp->b_fspriv;
+	struct xfs_ail		*ailp = lip->li_ailp;
+
+	spin_lock(&ailp->xa_lock);
+	for (; lip; lip = next) {
+		next = lip->li_bio_list;
+		if (lip->li_ops->iop_error)
+			lip->li_ops->iop_error(lip, bp);
+	}
+	spin_unlock(&ailp->xa_lock);
+}
+
 static bool
 xfs_buf_iodone_callback_error(
 	struct xfs_buf		*bp)
@@ -1120,7 +1134,11 @@
 	if ((mp->m_flags & XFS_MOUNT_UNMOUNTING) && mp->m_fail_unmount)
 		goto permanent_error;
 
-	/* still a transient error, higher layers will retry */
+	/*
+	 * Still a transient error, run IO completion failure callbacks and let
+	 * the higher layers retry the buffer.
+	 */
+	xfs_buf_do_callbacks_fail(bp);
 	xfs_buf_ioerror(bp, 0);
 	xfs_buf_relse(bp);
 	return true;
@@ -1201,3 +1219,31 @@
 	xfs_trans_ail_delete(ailp, lip, SHUTDOWN_CORRUPT_INCORE);
 	xfs_buf_item_free(BUF_ITEM(lip));
 }
+
+/*
+ * Requeue a failed buffer for writeback
+ *
+ * Return true if the buffer has been re-queued properly, false otherwise
+ */
+bool
+xfs_buf_resubmit_failed_buffers(
+	struct xfs_buf		*bp,
+	struct xfs_log_item	*lip,
+	struct list_head	*buffer_list)
+{
+	struct xfs_log_item	*next;
+
+	/*
+	 * Clear XFS_LI_FAILED flag from all items before resubmit
+	 *
+	 * XFS_LI_FAILED set/clear is protected by xa_lock, caller  this
+	 * function already have it acquired
+	 */
+	for (; lip; lip = next) {
+		next = lip->li_bio_list;
+		xfs_clear_li_failed(lip);
+	}
+
+	/* Add this buffer back to the delayed write list */
+	return xfs_buf_delwri_queue(bp, buffer_list);
+}
diff --git a/fs/xfs/xfs_buf_item.h b/fs/xfs/xfs_buf_item.h
index f7eba99..9690ce6 100644
--- a/fs/xfs/xfs_buf_item.h
+++ b/fs/xfs/xfs_buf_item.h
@@ -64,12 +64,15 @@
 int	xfs_buf_item_init(struct xfs_buf *, struct xfs_mount *);
 void	xfs_buf_item_relse(struct xfs_buf *);
 void	xfs_buf_item_log(xfs_buf_log_item_t *, uint, uint);
-uint	xfs_buf_item_dirty(xfs_buf_log_item_t *);
+bool	xfs_buf_item_dirty_format(struct xfs_buf_log_item *);
 void	xfs_buf_attach_iodone(struct xfs_buf *,
 			      void(*)(struct xfs_buf *, xfs_log_item_t *),
 			      xfs_log_item_t *);
 void	xfs_buf_iodone_callbacks(struct xfs_buf *);
 void	xfs_buf_iodone(struct xfs_buf *, struct xfs_log_item *);
+bool	xfs_buf_resubmit_failed_buffers(struct xfs_buf *,
+					struct xfs_log_item *,
+					struct list_head *);
 
 extern kmem_zone_t	*xfs_buf_item_zone;
 
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index ed7ee4e..bcf7297 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -167,7 +167,7 @@
 {
 	struct xfs_mount *mp = bp->b_target->bt_mount;
 
-	xfs_alert(mp, "Metadata %s detected at %pF, %s block 0x%llx",
+	xfs_alert(mp, "Metadata %s detected at %pS, %s block 0x%llx",
 		  bp->b_error == -EFSBADCRC ? "CRC error" : "corruption",
 		  __return_address, bp->b_ops->name, bp->b_bn);
 
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index df206cf..362c6b4 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -92,7 +92,7 @@
 	xfs_off_t		count,
 	bool			*did_zero)
 {
-	return iomap_zero_range(VFS_I(ip), pos, count, NULL, &xfs_iomap_ops);
+	return iomap_zero_range(VFS_I(ip), pos, count, did_zero, &xfs_iomap_ops);
 }
 
 int
@@ -729,6 +729,7 @@
 		xfs_rw_iunlock(ip, iolock);
 		eofb.eof_flags = XFS_EOF_FLAGS_SYNC;
 		xfs_icache_free_eofblocks(ip->i_mount, &eofb);
+		xfs_icache_free_cowblocks(ip->i_mount, &eofb);
 		goto write_retry;
 	}
 
@@ -1139,29 +1140,8 @@
 		want = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1;
 		nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index,
 					  want);
-		/*
-		 * No page mapped into given range.  If we are searching holes
-		 * and if this is the first time we got into the loop, it means
-		 * that the given offset is landed in a hole, return it.
-		 *
-		 * If we have already stepped through some block buffers to find
-		 * holes but they all contains data.  In this case, the last
-		 * offset is already updated and pointed to the end of the last
-		 * mapped page, if it does not reach the endpoint to search,
-		 * that means there should be a hole between them.
-		 */
-		if (nr_pages == 0) {
-			/* Data search found nothing */
-			if (type == DATA_OFF)
-				break;
-
-			ASSERT(type == HOLE_OFF);
-			if (lastoff == startoff || lastoff < endoff) {
-				found = true;
-				*offset = lastoff;
-			}
+		if (nr_pages == 0)
 			break;
-		}
 
 		for (i = 0; i < nr_pages; i++) {
 			struct page	*page = pvec.pages[i];
@@ -1227,21 +1207,20 @@
 
 		/*
 		 * The number of returned pages less than our desired, search
-		 * done.  In this case, nothing was found for searching data,
-		 * but we found a hole behind the last offset.
+		 * done.
 		 */
-		if (nr_pages < want) {
-			if (type == HOLE_OFF) {
-				*offset = lastoff;
-				found = true;
-			}
+		if (nr_pages < want)
 			break;
-		}
 
 		index = pvec.pages[i - 1]->index + 1;
 		pagevec_release(&pvec);
 	} while (index <= end);
 
+	/* No page at lastoff and we are not done - we found a hole. */
+	if (type == HOLE_OFF && lastoff < endoff) {
+		*offset = lastoff;
+		found = true;
+	}
 out:
 	pagevec_release(&pvec);
 	return found;
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index 74304b6..86a4911 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -66,7 +66,6 @@
 
 	XFS_STATS_INC(mp, vn_active);
 	ASSERT(atomic_read(&ip->i_pincount) == 0);
-	ASSERT(!spin_is_locked(&ip->i_flags_lock));
 	ASSERT(!xfs_isiflocked(ip));
 	ASSERT(ip->i_ino == 0);
 
@@ -192,7 +191,7 @@
 {
 	struct xfs_mount	*mp = pag->pag_mount;
 
-	ASSERT(spin_is_locked(&pag->pag_ici_lock));
+	lockdep_assert_held(&pag->pag_ici_lock);
 	if (pag->pag_ici_reclaimable++)
 		return;
 
@@ -214,7 +213,7 @@
 {
 	struct xfs_mount	*mp = pag->pag_mount;
 
-	ASSERT(spin_is_locked(&pag->pag_ici_lock));
+	lockdep_assert_held(&pag->pag_ici_lock);
 	if (--pag->pag_ici_reclaimable)
 		return;
 
@@ -1079,11 +1078,11 @@
 	 * Because we use RCU freeing we need to ensure the inode always appears
 	 * to be reclaimed with an invalid inode number when in the free state.
 	 * We do this as early as possible under the ILOCK so that
-	 * xfs_iflush_cluster() can be guaranteed to detect races with us here.
-	 * By doing this, we guarantee that once xfs_iflush_cluster has locked
-	 * XFS_ILOCK that it will see either a valid, flushable inode that will
-	 * serialise correctly, or it will see a clean (and invalid) inode that
-	 * it can skip.
+	 * xfs_iflush_cluster() and xfs_ifree_cluster() can be guaranteed to
+	 * detect races with us here. By doing this, we guarantee that once
+	 * xfs_iflush_cluster() or xfs_ifree_cluster() has locked XFS_ILOCK that
+	 * it will see either a valid inode that will serialise correctly, or it
+	 * will see an invalid inode that it can skip.
 	 */
 	spin_lock(&ip->i_flags_lock);
 	ip->i_flags = XFS_IRECLAIM;
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index 7a0b4ee..fe9a9a1 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -881,7 +881,6 @@
 	case S_IFREG:
 	case S_IFDIR:
 		if (pip && (pip->i_d.di_flags & XFS_DIFLAG_ANY)) {
-			uint64_t	di_flags2 = 0;
 			uint		di_flags = 0;
 
 			if (S_ISDIR(mode)) {
@@ -918,20 +917,23 @@
 				di_flags |= XFS_DIFLAG_NODEFRAG;
 			if (pip->i_d.di_flags & XFS_DIFLAG_FILESTREAM)
 				di_flags |= XFS_DIFLAG_FILESTREAM;
-			if (pip->i_d.di_flags2 & XFS_DIFLAG2_DAX)
-				di_flags2 |= XFS_DIFLAG2_DAX;
 
 			ip->i_d.di_flags |= di_flags;
-			ip->i_d.di_flags2 |= di_flags2;
 		}
 		if (pip &&
 		    (pip->i_d.di_flags2 & XFS_DIFLAG2_ANY) &&
 		    pip->i_d.di_version == 3 &&
 		    ip->i_d.di_version == 3) {
+			uint64_t	di_flags2 = 0;
+
 			if (pip->i_d.di_flags2 & XFS_DIFLAG2_COWEXTSIZE) {
-				ip->i_d.di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
+				di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
 				ip->i_d.di_cowextsize = pip->i_d.di_cowextsize;
 			}
+			if (pip->i_d.di_flags2 & XFS_DIFLAG2_DAX)
+				di_flags2 |= XFS_DIFLAG2_DAX;
+
+			ip->i_d.di_flags2 |= di_flags2;
 		}
 		/* FALLTHROUGH */
 	case S_IFLNK:
@@ -1630,10 +1632,12 @@
 		goto out;
 
 	/*
-	 * Clear the reflink flag if we truncated everything.
+	 * Clear the reflink flag if there are no data fork blocks and
+	 * there are no extents staged in the cow fork.
 	 */
-	if (ip->i_d.di_nblocks == 0 && xfs_is_reflink_inode(ip)) {
-		ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
+	if (xfs_is_reflink_inode(ip) && ip->i_cnextents == 0) {
+		if (ip->i_d.di_nblocks == 0)
+			ip->i_d.di_flags2 &= ~XFS_DIFLAG2_REFLINK;
 		xfs_inode_clear_cowblocks_tag(ip);
 	}
 
@@ -2366,11 +2370,24 @@
 			 * already marked stale. If we can't lock it, back off
 			 * and retry.
 			 */
-			if (ip != free_ip &&
-			    !xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
-				rcu_read_unlock();
-				delay(1);
-				goto retry;
+			if (ip != free_ip) {
+				if (!xfs_ilock_nowait(ip, XFS_ILOCK_EXCL)) {
+					rcu_read_unlock();
+					delay(1);
+					goto retry;
+				}
+
+				/*
+				 * Check the inode number again in case we're
+				 * racing with freeing in xfs_reclaim_inode().
+				 * See the comments in that function for more
+				 * information as to why the initial check is
+				 * not sufficient.
+				 */
+				if (ip->i_ino != inum + i) {
+					xfs_iunlock(ip, XFS_ILOCK_EXCL);
+					continue;
+				}
 			}
 			rcu_read_unlock();
 
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index d90e781..d0a3c4b 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -27,6 +27,7 @@
 #include "xfs_error.h"
 #include "xfs_trace.h"
 #include "xfs_trans_priv.h"
+#include "xfs_buf_item.h"
 #include "xfs_log.h"
 
 
@@ -363,6 +364,9 @@
 	to->di_dmstate = from->di_dmstate;
 	to->di_flags = from->di_flags;
 
+	/* log a dummy value to ensure log structure is fully initialised */
+	to->di_next_unlinked = NULLAGINO;
+
 	if (from->di_version == 3) {
 		to->di_changecount = inode->i_version;
 		to->di_crtime.t_sec = from->di_crtime.t_sec;
@@ -403,6 +407,11 @@
  * the second with the on-disk inode structure, and a possible third and/or
  * fourth with the inode data/extents/b-tree root and inode attributes
  * data/extents/b-tree root.
+ *
+ * Note: Always use the 64 bit inode log format structure so we don't
+ * leave an uninitialised hole in the format item on 64 bit systems. Log
+ * recovery on 32 bit systems handles this just fine, so there's no reason
+ * for not using an initialising the properly padded structure all the time.
  */
 STATIC void
 xfs_inode_item_format(
@@ -411,8 +420,8 @@
 {
 	struct xfs_inode_log_item *iip = INODE_ITEM(lip);
 	struct xfs_inode	*ip = iip->ili_inode;
-	struct xfs_inode_log_format *ilf;
 	struct xfs_log_iovec	*vecp = NULL;
+	struct xfs_inode_log_format *ilf;
 
 	ASSERT(ip->i_d.di_version > 1);
 
@@ -424,7 +433,17 @@
 	ilf->ilf_boffset = ip->i_imap.im_boffset;
 	ilf->ilf_fields = XFS_ILOG_CORE;
 	ilf->ilf_size = 2; /* format + core */
-	xlog_finish_iovec(lv, vecp, sizeof(struct xfs_inode_log_format));
+
+	/*
+	 * make sure we don't leak uninitialised data into the log in the case
+	 * when we don't log every field in the inode.
+	 */
+	ilf->ilf_dsize = 0;
+	ilf->ilf_asize = 0;
+	ilf->ilf_pad = 0;
+	memset(&ilf->ilf_u.ilfu_uuid, 0, sizeof(ilf->ilf_u.ilfu_uuid));
+
+	xlog_finish_iovec(lv, vecp, sizeof(*ilf));
 
 	xfs_inode_item_format_core(ip, lv, &vecp);
 	xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp);
@@ -475,6 +494,23 @@
 		wake_up_bit(&ip->i_flags, __XFS_IPINNED_BIT);
 }
 
+/*
+ * Callback used to mark a buffer with XFS_LI_FAILED when items in the buffer
+ * have been failed during writeback
+ *
+ * This informs the AIL that the inode is already flush locked on the next push,
+ * and acquires a hold on the buffer to ensure that it isn't reclaimed before
+ * dirty data makes it to disk.
+ */
+STATIC void
+xfs_inode_item_error(
+	struct xfs_log_item	*lip,
+	struct xfs_buf		*bp)
+{
+	ASSERT(xfs_isiflocked(INODE_ITEM(lip)->ili_inode));
+	xfs_set_li_failed(lip, bp);
+}
+
 STATIC uint
 xfs_inode_item_push(
 	struct xfs_log_item	*lip,
@@ -484,13 +520,28 @@
 {
 	struct xfs_inode_log_item *iip = INODE_ITEM(lip);
 	struct xfs_inode	*ip = iip->ili_inode;
-	struct xfs_buf		*bp = NULL;
+	struct xfs_buf		*bp = lip->li_buf;
 	uint			rval = XFS_ITEM_SUCCESS;
 	int			error;
 
 	if (xfs_ipincount(ip) > 0)
 		return XFS_ITEM_PINNED;
 
+	/*
+	 * The buffer containing this item failed to be written back
+	 * previously. Resubmit the buffer for IO.
+	 */
+	if (lip->li_flags & XFS_LI_FAILED) {
+		if (!xfs_buf_trylock(bp))
+			return XFS_ITEM_LOCKED;
+
+		if (!xfs_buf_resubmit_failed_buffers(bp, lip, buffer_list))
+			rval = XFS_ITEM_FLUSHING;
+
+		xfs_buf_unlock(bp);
+		return rval;
+	}
+
 	if (!xfs_ilock_nowait(ip, XFS_ILOCK_SHARED))
 		return XFS_ITEM_LOCKED;
 
@@ -622,7 +673,8 @@
 	.iop_unlock	= xfs_inode_item_unlock,
 	.iop_committed	= xfs_inode_item_committed,
 	.iop_push	= xfs_inode_item_push,
-	.iop_committing = xfs_inode_item_committing
+	.iop_committing = xfs_inode_item_committing,
+	.iop_error	= xfs_inode_item_error
 };
 
 
@@ -710,7 +762,8 @@
 		 * the AIL lock.
 		 */
 		iip = INODE_ITEM(blip);
-		if (iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn)
+		if ((iip->ili_logged && blip->li_lsn == iip->ili_flush_lsn) ||
+		    (blip->li_flags & XFS_LI_FAILED))
 			need_ail++;
 
 		blip = next;
@@ -718,7 +771,8 @@
 
 	/* make sure we capture the state of the initial inode. */
 	iip = INODE_ITEM(lip);
-	if (iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn)
+	if ((iip->ili_logged && lip->li_lsn == iip->ili_flush_lsn) ||
+	    lip->li_flags & XFS_LI_FAILED)
 		need_ail++;
 
 	/*
@@ -731,22 +785,30 @@
 	 * holding the lock before removing the inode from the AIL.
 	 */
 	if (need_ail) {
-		struct xfs_log_item *log_items[need_ail];
-		int i = 0;
+		bool			mlip_changed = false;
+
+		/* this is an opencoded batch version of xfs_trans_ail_delete */
 		spin_lock(&ailp->xa_lock);
 		for (blip = lip; blip; blip = blip->li_bio_list) {
-			iip = INODE_ITEM(blip);
-			if (iip->ili_logged &&
-			    blip->li_lsn == iip->ili_flush_lsn) {
-				log_items[i++] = blip;
+			if (INODE_ITEM(blip)->ili_logged &&
+			    blip->li_lsn == INODE_ITEM(blip)->ili_flush_lsn)
+				mlip_changed |= xfs_ail_delete_one(ailp, blip);
+			else {
+				xfs_clear_li_failed(blip);
 			}
-			ASSERT(i <= need_ail);
 		}
-		/* xfs_trans_ail_delete_bulk() drops the AIL lock. */
-		xfs_trans_ail_delete_bulk(ailp, log_items, i,
-					  SHUTDOWN_CORRUPT_INCORE);
-	}
 
+		if (mlip_changed) {
+			if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
+				xlog_assign_tail_lsn_locked(ailp->xa_mount);
+			if (list_empty(&ailp->xa_ail))
+				wake_up_all(&ailp->xa_empty);
+		}
+		spin_unlock(&ailp->xa_lock);
+
+		if (mlip_changed)
+			xfs_log_space_wake(ailp->xa_mount);
+	}
 
 	/*
 	 * clean up and unlock the flush lock now we are done. We can clear the
@@ -811,48 +873,30 @@
 }
 
 /*
- * convert an xfs_inode_log_format struct from either 32 or 64 bit versions
- * (which can have different field alignments) to the native version
+ * convert an xfs_inode_log_format struct from the old 32 bit version
+ * (which can have different field alignments) to the native 64 bit version
  */
 int
 xfs_inode_item_format_convert(
-	xfs_log_iovec_t		*buf,
-	xfs_inode_log_format_t	*in_f)
+	struct xfs_log_iovec		*buf,
+	struct xfs_inode_log_format	*in_f)
 {
-	if (buf->i_len == sizeof(xfs_inode_log_format_32_t)) {
-		xfs_inode_log_format_32_t *in_f32 = buf->i_addr;
+	struct xfs_inode_log_format_32	*in_f32 = buf->i_addr;
 
-		in_f->ilf_type = in_f32->ilf_type;
-		in_f->ilf_size = in_f32->ilf_size;
-		in_f->ilf_fields = in_f32->ilf_fields;
-		in_f->ilf_asize = in_f32->ilf_asize;
-		in_f->ilf_dsize = in_f32->ilf_dsize;
-		in_f->ilf_ino = in_f32->ilf_ino;
-		/* copy biggest field of ilf_u */
-		memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
-		       in_f32->ilf_u.ilfu_uuid.__u_bits,
-		       sizeof(uuid_t));
-		in_f->ilf_blkno = in_f32->ilf_blkno;
-		in_f->ilf_len = in_f32->ilf_len;
-		in_f->ilf_boffset = in_f32->ilf_boffset;
-		return 0;
-	} else if (buf->i_len == sizeof(xfs_inode_log_format_64_t)){
-		xfs_inode_log_format_64_t *in_f64 = buf->i_addr;
+	if (buf->i_len != sizeof(*in_f32))
+		return -EFSCORRUPTED;
 
-		in_f->ilf_type = in_f64->ilf_type;
-		in_f->ilf_size = in_f64->ilf_size;
-		in_f->ilf_fields = in_f64->ilf_fields;
-		in_f->ilf_asize = in_f64->ilf_asize;
-		in_f->ilf_dsize = in_f64->ilf_dsize;
-		in_f->ilf_ino = in_f64->ilf_ino;
-		/* copy biggest field of ilf_u */
-		memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
-		       in_f64->ilf_u.ilfu_uuid.__u_bits,
-		       sizeof(uuid_t));
-		in_f->ilf_blkno = in_f64->ilf_blkno;
-		in_f->ilf_len = in_f64->ilf_len;
-		in_f->ilf_boffset = in_f64->ilf_boffset;
-		return 0;
-	}
-	return -EFSCORRUPTED;
+	in_f->ilf_type = in_f32->ilf_type;
+	in_f->ilf_size = in_f32->ilf_size;
+	in_f->ilf_fields = in_f32->ilf_fields;
+	in_f->ilf_asize = in_f32->ilf_asize;
+	in_f->ilf_dsize = in_f32->ilf_dsize;
+	in_f->ilf_ino = in_f32->ilf_ino;
+	/* copy biggest field of ilf_u */
+	memcpy(in_f->ilf_u.ilfu_uuid.__u_bits,
+	       in_f32->ilf_u.ilfu_uuid.__u_bits, sizeof(uuid_t));
+	in_f->ilf_blkno = in_f32->ilf_blkno;
+	in_f->ilf_len = in_f32->ilf_len;
+	in_f->ilf_boffset = in_f32->ilf_boffset;
+	return 0;
 }
diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index 73cfc71..6c95812 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -928,16 +928,15 @@
 	return 0;
 }
 
-STATIC void
-xfs_set_diflags(
+STATIC uint16_t
+xfs_flags2diflags(
 	struct xfs_inode	*ip,
 	unsigned int		xflags)
 {
-	unsigned int		di_flags;
-	uint64_t		di_flags2;
-
 	/* can't set PREALLOC this way, just preserve it */
-	di_flags = (ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+	uint16_t		di_flags =
+		(ip->i_d.di_flags & XFS_DIFLAG_PREALLOC);
+
 	if (xflags & FS_XFLAG_IMMUTABLE)
 		di_flags |= XFS_DIFLAG_IMMUTABLE;
 	if (xflags & FS_XFLAG_APPEND)
@@ -967,19 +966,24 @@
 		if (xflags & FS_XFLAG_EXTSIZE)
 			di_flags |= XFS_DIFLAG_EXTSIZE;
 	}
-	ip->i_d.di_flags = di_flags;
 
-	/* diflags2 only valid for v3 inodes. */
-	if (ip->i_d.di_version < 3)
-		return;
+	return di_flags;
+}
 
-	di_flags2 = (ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
+STATIC uint64_t
+xfs_flags2diflags2(
+	struct xfs_inode	*ip,
+	unsigned int		xflags)
+{
+	uint64_t		di_flags2 =
+		(ip->i_d.di_flags2 & XFS_DIFLAG2_REFLINK);
+
 	if (xflags & FS_XFLAG_DAX)
 		di_flags2 |= XFS_DIFLAG2_DAX;
 	if (xflags & FS_XFLAG_COWEXTSIZE)
 		di_flags2 |= XFS_DIFLAG2_COWEXTSIZE;
 
-	ip->i_d.di_flags2 = di_flags2;
+	return di_flags2;
 }
 
 STATIC void
@@ -1005,11 +1009,12 @@
 		inode->i_flags |= S_NOATIME;
 	else
 		inode->i_flags &= ~S_NOATIME;
+#if 0	/* disabled until the flag switching races are sorted out */
 	if (xflags & FS_XFLAG_DAX)
 		inode->i_flags |= S_DAX;
 	else
 		inode->i_flags &= ~S_DAX;
-
+#endif
 }
 
 static int
@@ -1019,6 +1024,7 @@
 	struct fsxattr		*fa)
 {
 	struct xfs_mount	*mp = ip->i_mount;
+	uint64_t		di_flags2;
 
 	/* Can't change realtime flag if any extents are allocated. */
 	if ((ip->i_d.di_nextents || ip->i_delayed_blks) &&
@@ -1049,7 +1055,14 @@
 	    !capable(CAP_LINUX_IMMUTABLE))
 		return -EPERM;
 
-	xfs_set_diflags(ip, fa->fsx_xflags);
+	/* diflags2 only valid for v3 inodes. */
+	di_flags2 = xfs_flags2diflags2(ip, fa->fsx_xflags);
+	if (di_flags2 && ip->i_d.di_version < 3)
+		return -EINVAL;
+
+	ip->i_d.di_flags = xfs_flags2diflags(ip, fa->fsx_xflags);
+	ip->i_d.di_flags2 = di_flags2;
+
 	xfs_diflags_to_linux(ip);
 	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_CHG);
 	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
@@ -1072,6 +1085,7 @@
 	int			*join_flags)
 {
 	struct inode		*inode = VFS_I(ip);
+	struct super_block	*sb = inode->i_sb;
 	int			error;
 
 	*join_flags = 0;
@@ -1084,7 +1098,7 @@
 	if (fa->fsx_xflags & FS_XFLAG_DAX) {
 		if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
 			return -EINVAL;
-		if (ip->i_mount->m_sb.sb_blocksize != PAGE_SIZE)
+		if (bdev_dax_supported(sb, sb->s_blocksize) < 0)
 			return -EINVAL;
 	}
 
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 65740d1..f286f63 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -836,7 +836,8 @@
 xfs_iomap_write_unwritten(
 	xfs_inode_t	*ip,
 	xfs_off_t	offset,
-	xfs_off_t	count)
+	xfs_off_t	count,
+	bool		update_isize)
 {
 	xfs_mount_t	*mp = ip->i_mount;
 	xfs_fileoff_t	offset_fsb;
@@ -847,6 +848,7 @@
 	xfs_trans_t	*tp;
 	xfs_bmbt_irec_t imap;
 	struct xfs_defer_ops dfops;
+	struct inode	*inode = VFS_I(ip);
 	xfs_fsize_t	i_size;
 	uint		resblks;
 	int		error;
@@ -906,7 +908,8 @@
 		i_size = XFS_FSB_TO_B(mp, offset_fsb + count_fsb);
 		if (i_size > offset + count)
 			i_size = offset + count;
-
+		if (update_isize && i_size > i_size_read(inode))
+			i_size_write(inode, i_size);
 		i_size = xfs_new_eof(ip, i_size);
 		if (i_size) {
 			ip->i_d.di_size = i_size;
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 6d45cf0..d71703a 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -27,7 +27,7 @@
 			struct xfs_bmbt_irec *, int);
 int xfs_iomap_write_allocate(struct xfs_inode *, int, xfs_off_t,
 			struct xfs_bmbt_irec *);
-int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t);
+int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t, bool);
 
 void xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
 		struct xfs_bmbt_irec *);
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c
index a1247c3..5b81f7f 100644
--- a/fs/xfs/xfs_iops.c
+++ b/fs/xfs/xfs_iops.c
@@ -802,7 +802,7 @@
  * Caution: The caller of this function is responsible for calling
  * setattr_prepare() or otherwise verifying the change is fine.
  */
-int
+STATIC int
 xfs_setattr_size(
 	struct xfs_inode	*ip,
 	struct iattr		*iattr)
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index d8a77db..26d67ce 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -361,7 +361,6 @@
 	xfs_agino_t		agino;	/* inode # in allocation group */
 	xfs_agnumber_t		agno;	/* allocation group number */
 	xfs_btree_cur_t		*cur;	/* btree cursor for ialloc btree */
-	size_t			irbsize; /* size of irec buffer in bytes */
 	xfs_inobt_rec_incore_t	*irbuf;	/* start of irec buffer */
 	int			nirbuf;	/* size of irbuf */
 	int			ubcount; /* size of user's buffer */
@@ -388,11 +387,10 @@
 	*ubcountp = 0;
 	*done = 0;
 
-	irbuf = kmem_zalloc_greedy(&irbsize, PAGE_SIZE, PAGE_SIZE * 4);
+	irbuf = kmem_zalloc_large(PAGE_SIZE * 4, KM_SLEEP);
 	if (!irbuf)
 		return -ENOMEM;
-
-	nirbuf = irbsize / sizeof(*irbuf);
+	nirbuf = (PAGE_SIZE * 4) / sizeof(*irbuf);
 
 	/*
 	 * Loop over the allocation groups, starting from the last
diff --git a/fs/xfs/xfs_linux.h b/fs/xfs/xfs_linux.h
index 1455b2520..3ebed16 100644
--- a/fs/xfs/xfs_linux.h
+++ b/fs/xfs/xfs_linux.h
@@ -363,7 +363,14 @@
 #endif /* DEBUG */
 
 #ifdef CONFIG_XFS_RT
-#define XFS_IS_REALTIME_INODE(ip) ((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME)
+
+/*
+ * make sure we ignore the inode flag if the filesystem doesn't have a
+ * configured realtime device.
+ */
+#define XFS_IS_REALTIME_INODE(ip)			\
+	(((ip)->i_d.di_flags & XFS_DIFLAG_REALTIME) &&	\
+	 (ip)->i_mount->m_rtdev_targp)
 #else
 #define XFS_IS_REALTIME_INODE(ip) (0)
 #endif
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index b57ab34..33c9a3a 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -743,15 +743,45 @@
 	struct xfs_mount	*mp)
 {
 	int	error = 0;
+	bool	readonly = (mp->m_flags & XFS_MOUNT_RDONLY);
 
 	if (mp->m_flags & XFS_MOUNT_NORECOVERY) {
 		ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
 		return 0;
+	} else if (readonly) {
+		/* Allow unlinked processing to proceed */
+		mp->m_flags &= ~XFS_MOUNT_RDONLY;
 	}
 
+	/*
+	 * During the second phase of log recovery, we need iget and
+	 * iput to behave like they do for an active filesystem.
+	 * xfs_fs_drop_inode needs to be able to prevent the deletion
+	 * of inodes before we're done replaying log items on those
+	 * inodes.  Turn it off immediately after recovery finishes
+	 * so that we don't leak the quota inodes if subsequent mount
+	 * activities fail.
+	 *
+	 * We let all inodes involved in redo item processing end up on
+	 * the LRU instead of being evicted immediately so that if we do
+	 * something to an unlinked inode, the irele won't cause
+	 * premature truncation and freeing of the inode, which results
+	 * in log recovery failure.  We have to evict the unreferenced
+	 * lru inodes after clearing MS_ACTIVE because we don't
+	 * otherwise clean up the lru if there's a subsequent failure in
+	 * xfs_mountfs, which leads to us leaking the inodes if nothing
+	 * else (e.g. quotacheck) references the inodes before the
+	 * mount failure occurs.
+	 */
+	mp->m_super->s_flags |= MS_ACTIVE;
 	error = xlog_recover_finish(mp->m_log);
 	if (!error)
 		xfs_log_work_queue(mp);
+	mp->m_super->s_flags &= ~MS_ACTIVE;
+	evict_inodes(mp->m_super);
+
+	if (readonly)
+		mp->m_flags |= XFS_MOUNT_RDONLY;
 
 	return error;
 }
@@ -801,11 +831,14 @@
 	int		 error;
 
 	/*
-	 * Don't write out unmount record on read-only mounts.
+	 * Don't write out unmount record on norecovery mounts or ro devices.
 	 * Or, if we are doing a forced umount (typically because of IO errors).
 	 */
-	if (mp->m_flags & XFS_MOUNT_RDONLY)
+	if (mp->m_flags & XFS_MOUNT_NORECOVERY ||
+	    xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) {
+		ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
 		return 0;
+	}
 
 	error = _xfs_log_force(mp, XFS_LOG_SYNC, NULL);
 	ASSERT(error || !(XLOG_FORCED_SHUTDOWN(log)));
@@ -3304,8 +3337,6 @@
 		 */
 		if (iclog->ic_state & XLOG_STATE_IOERROR)
 			return -EIO;
-		if (log_flushed)
-			*log_flushed = 1;
 	} else {
 
 no_sleep:
@@ -3409,8 +3440,6 @@
 
 				xlog_wait(&iclog->ic_prev->ic_write_wait,
 							&log->l_icloglock);
-				if (log_flushed)
-					*log_flushed = 1;
 				already_slept = 1;
 				goto try_again;
 			}
@@ -3444,9 +3473,6 @@
 			 */
 			if (iclog->ic_state & XLOG_STATE_IOERROR)
 				return -EIO;
-
-			if (log_flushed)
-				*log_flushed = 1;
 		} else {		/* just return */
 			spin_unlock(&log->l_icloglock);
 		}
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index 9b3d7c7..0590926 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1029,61 +1029,106 @@
 }
 
 /*
- * Check the log tail for torn writes. This is required when torn writes are
- * detected at the head and the head had to be walked back to a previous record.
- * The tail of the previous record must now be verified to ensure the torn
- * writes didn't corrupt the previous tail.
+ * Calculate distance from head to tail (i.e., unused space in the log).
+ */
+static inline int
+xlog_tail_distance(
+	struct xlog	*log,
+	xfs_daddr_t	head_blk,
+	xfs_daddr_t	tail_blk)
+{
+	if (head_blk < tail_blk)
+		return tail_blk - head_blk;
+
+	return tail_blk + (log->l_logBBsize - head_blk);
+}
+
+/*
+ * Verify the log tail. This is particularly important when torn or incomplete
+ * writes have been detected near the front of the log and the head has been
+ * walked back accordingly.
  *
- * Return an error if CRC verification fails as recovery cannot proceed.
+ * We also have to handle the case where the tail was pinned and the head
+ * blocked behind the tail right before a crash. If the tail had been pushed
+ * immediately prior to the crash and the subsequent checkpoint was only
+ * partially written, it's possible it overwrote the last referenced tail in the
+ * log with garbage. This is not a coherency problem because the tail must have
+ * been pushed before it can be overwritten, but appears as log corruption to
+ * recovery because we have no way to know the tail was updated if the
+ * subsequent checkpoint didn't write successfully.
+ *
+ * Therefore, CRC check the log from tail to head. If a failure occurs and the
+ * offending record is within max iclog bufs from the head, walk the tail
+ * forward and retry until a valid tail is found or corruption is detected out
+ * of the range of a possible overwrite.
  */
 STATIC int
 xlog_verify_tail(
 	struct xlog		*log,
 	xfs_daddr_t		head_blk,
-	xfs_daddr_t		tail_blk)
+	xfs_daddr_t		*tail_blk,
+	int			hsize)
 {
 	struct xlog_rec_header	*thead;
 	struct xfs_buf		*bp;
 	xfs_daddr_t		first_bad;
-	int			count;
 	int			error = 0;
 	bool			wrapped;
-	xfs_daddr_t		tmp_head;
+	xfs_daddr_t		tmp_tail;
+	xfs_daddr_t		orig_tail = *tail_blk;
 
 	bp = xlog_get_bp(log, 1);
 	if (!bp)
 		return -ENOMEM;
 
 	/*
-	 * Seek XLOG_MAX_ICLOGS + 1 records past the current tail record to get
-	 * a temporary head block that points after the last possible
-	 * concurrently written record of the tail.
+	 * Make sure the tail points to a record (returns positive count on
+	 * success).
 	 */
-	count = xlog_seek_logrec_hdr(log, head_blk, tail_blk,
-				     XLOG_MAX_ICLOGS + 1, bp, &tmp_head, &thead,
-				     &wrapped);
-	if (count < 0) {
-		error = count;
+	error = xlog_seek_logrec_hdr(log, head_blk, *tail_blk, 1, bp,
+			&tmp_tail, &thead, &wrapped);
+	if (error < 0)
 		goto out;
+	if (*tail_blk != tmp_tail)
+		*tail_blk = tmp_tail;
+
+	/*
+	 * Run a CRC check from the tail to the head. We can't just check
+	 * MAX_ICLOGS records past the tail because the tail may point to stale
+	 * blocks cleared during the search for the head/tail. These blocks are
+	 * overwritten with zero-length records and thus record count is not a
+	 * reliable indicator of the iclog state before a crash.
+	 */
+	first_bad = 0;
+	error = xlog_do_recovery_pass(log, head_blk, *tail_blk,
+				      XLOG_RECOVER_CRCPASS, &first_bad);
+	while ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
+		int	tail_distance;
+
+		/*
+		 * Is corruption within range of the head? If so, retry from
+		 * the next record. Otherwise return an error.
+		 */
+		tail_distance = xlog_tail_distance(log, head_blk, first_bad);
+		if (tail_distance > BTOBB(XLOG_MAX_ICLOGS * hsize))
+			break;
+
+		/* skip to the next record; returns positive count on success */
+		error = xlog_seek_logrec_hdr(log, head_blk, first_bad, 2, bp,
+				&tmp_tail, &thead, &wrapped);
+		if (error < 0)
+			goto out;
+
+		*tail_blk = tmp_tail;
+		first_bad = 0;
+		error = xlog_do_recovery_pass(log, head_blk, *tail_blk,
+					      XLOG_RECOVER_CRCPASS, &first_bad);
 	}
 
-	/*
-	 * If the call above didn't find XLOG_MAX_ICLOGS + 1 records, we ran
-	 * into the actual log head. tmp_head points to the start of the record
-	 * so update it to the actual head block.
-	 */
-	if (count < XLOG_MAX_ICLOGS + 1)
-		tmp_head = head_blk;
-
-	/*
-	 * We now have a tail and temporary head block that covers at least
-	 * XLOG_MAX_ICLOGS records from the tail. We need to verify that these
-	 * records were completely written. Run a CRC verification pass from
-	 * tail to head and return the result.
-	 */
-	error = xlog_do_recovery_pass(log, tmp_head, tail_blk,
-				      XLOG_RECOVER_CRCPASS, &first_bad);
-
+	if (!error && *tail_blk != orig_tail)
+		xfs_warn(log->l_mp,
+		"Tail block (0x%llx) overwrite detected. Updated to 0x%llx",
+			 orig_tail, *tail_blk);
 out:
 	xlog_put_bp(bp);
 	return error;
@@ -1143,7 +1188,7 @@
 	 */
 	error = xlog_do_recovery_pass(log, *head_blk, tmp_rhead_blk,
 				      XLOG_RECOVER_CRCPASS, &first_bad);
-	if (error == -EFSBADCRC) {
+	if ((error == -EFSBADCRC || error == -EFSCORRUPTED) && first_bad) {
 		/*
 		 * We've hit a potential torn write. Reset the error and warn
 		 * about it.
@@ -1183,31 +1228,12 @@
 			ASSERT(0);
 			return 0;
 		}
-
-		/*
-		 * Now verify the tail based on the updated head. This is
-		 * required because the torn writes trimmed from the head could
-		 * have been written over the tail of a previous record. Return
-		 * any errors since recovery cannot proceed if the tail is
-		 * corrupt.
-		 *
-		 * XXX: This leaves a gap in truly robust protection from torn
-		 * writes in the log. If the head is behind the tail, the tail
-		 * pushes forward to create some space and then a crash occurs
-		 * causing the writes into the previous record's tail region to
-		 * tear, log recovery isn't able to recover.
-		 *
-		 * How likely is this to occur? If possible, can we do something
-		 * more intelligent here? Is it safe to push the tail forward if
-		 * we can determine that the tail is within the range of the
-		 * torn write (e.g., the kernel can only overwrite the tail if
-		 * it has actually been pushed forward)? Alternatively, could we
-		 * somehow prevent this condition at runtime?
-		 */
-		error = xlog_verify_tail(log, *head_blk, *tail_blk);
 	}
+	if (error)
+		return error;
 
-	return error;
+	return xlog_verify_tail(log, *head_blk, tail_blk,
+				be32_to_cpu((*rhead)->h_size));
 }
 
 /*
@@ -4152,7 +4178,7 @@
 
 	#define XLOG_RECOVER_COMMIT_QUEUE_MAX 100
 
-	hlist_del(&trans->r_list);
+	hlist_del_init(&trans->r_list);
 
 	error = xlog_recover_reorder_trans(log, trans, pass);
 	if (error)
@@ -4354,6 +4380,8 @@
 	xlog_recover_item_t	*item, *n;
 	int			i;
 
+	hlist_del_init(&trans->r_list);
+
 	list_for_each_entry_safe(item, n, &trans->r_itemq, ri_list) {
 		/* Free the regions in the item. */
 		list_del(&item->ri_list);
@@ -4799,12 +4827,16 @@
 	int			error = 0;
 	struct xfs_ail_cursor	cur;
 	struct xfs_ail		*ailp;
+#if defined(DEBUG) || defined(XFS_WARN)
 	xfs_lsn_t		last_lsn;
+#endif
 
 	ailp = log->l_ailp;
 	spin_lock(&ailp->xa_lock);
 	lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+#if defined(DEBUG) || defined(XFS_WARN)
 	last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);
+#endif
 	while (lip != NULL) {
 		/*
 		 * We're done when we see something other than an intent.
@@ -5214,7 +5246,7 @@
 	xfs_daddr_t		*first_bad)	/* out: first bad log rec */
 {
 	xlog_rec_header_t	*rhead;
-	xfs_daddr_t		blk_no;
+	xfs_daddr_t		blk_no, rblk_no;
 	xfs_daddr_t		rhead_blk;
 	char			*offset;
 	xfs_buf_t		*hbp, *dbp;
@@ -5222,11 +5254,15 @@
 	int			error2 = 0;
 	int			bblks, split_bblks;
 	int			hblks, split_hblks, wrapped_hblks;
+	int			i;
 	struct hlist_head	rhash[XLOG_RHASH_SIZE];
 	LIST_HEAD		(buffer_list);
 
 	ASSERT(head_blk != tail_blk);
-	rhead_blk = 0;
+	blk_no = rhead_blk = tail_blk;
+
+	for (i = 0; i < XLOG_RHASH_SIZE; i++)
+		INIT_HLIST_HEAD(&rhash[i]);
 
 	/*
 	 * Read the header of the tail block and get the iclog buffer size from
@@ -5301,7 +5337,6 @@
 	}
 
 	memset(rhash, 0, sizeof(rhash));
-	blk_no = rhead_blk = tail_blk;
 	if (tail_blk > head_blk) {
 		/*
 		 * Perform recovery around the end of the physical log.
@@ -5363,9 +5398,19 @@
 			bblks = (int)BTOBB(be32_to_cpu(rhead->h_len));
 			blk_no += hblks;
 
-			/* Read in data for log record */
-			if (blk_no + bblks <= log->l_logBBsize) {
-				error = xlog_bread(log, blk_no, bblks, dbp,
+			/*
+			 * Read the log record data in multiple reads if it
+			 * wraps around the end of the log. Note that if the
+			 * header already wrapped, blk_no could point past the
+			 * end of the log. The record data is contiguous in
+			 * that case.
+			 */
+			if (blk_no + bblks <= log->l_logBBsize ||
+			    blk_no >= log->l_logBBsize) {
+				/* mod blk_no in case the header wrapped and
+				 * pushed it beyond the end of the log */
+				rblk_no = do_mod(blk_no, log->l_logBBsize);
+				error = xlog_bread(log, rblk_no, bblks, dbp,
 						   &offset);
 				if (error)
 					goto bread_err2;
@@ -5464,6 +5509,19 @@
 	if (error && first_bad)
 		*first_bad = rhead_blk;
 
+	/*
+	 * Transactions are freed at commit time but transactions without commit
+	 * records on disk are never committed. Free any that may be left in the
+	 * hash table.
+	 */
+	for (i = 0; i < XLOG_RHASH_SIZE; i++) {
+		struct hlist_node	*tmp;
+		struct xlog_recover	*trans;
+
+		hlist_for_each_entry_safe(trans, tmp, &rhash[i], r_list)
+			xlog_recover_free_trans(trans);
+	}
+
 	return error ? error : error2;
 }
 
@@ -5542,6 +5600,8 @@
 	xfs_buf_t	*bp;
 	xfs_sb_t	*sbp;
 
+	trace_xfs_log_recover(log, head_blk, tail_blk);
+
 	/*
 	 * First replay the images in the log.
 	 */
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index 13796f2..d4ce8d2 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -925,15 +925,6 @@
 	}
 
 	/*
-	 * During the second phase of log recovery, we need iget and
-	 * iput to behave like they do for an active filesystem.
-	 * xfs_fs_drop_inode needs to be able to prevent the deletion
-	 * of inodes before we're done replaying log items on those
-	 * inodes.
-	 */
-	mp->m_super->s_flags |= MS_ACTIVE;
-
-	/*
 	 * Finish recovering the file system.  This part needed to be delayed
 	 * until after the root and real-time bitmap inodes were consistently
 	 * read in.
@@ -1008,12 +999,13 @@
  out_quota:
 	xfs_qm_unmount_quotas(mp);
  out_rtunmount:
-	mp->m_super->s_flags &= ~MS_ACTIVE;
 	xfs_rtunmount_inodes(mp);
  out_rele_rip:
 	IRELE(rip);
 	cancel_delayed_work_sync(&mp->m_reclaim_work);
 	xfs_reclaim_inodes(mp, SYNC_WAIT);
+	/* Clean out dquots that might be in memory after quotacheck. */
+	xfs_qm_unmount(mp);
  out_log_dealloc:
 	mp->m_flags |= XFS_MOUNT_UNMOUNTING;
 	xfs_log_mount_cancel(mp);
diff --git a/fs/xfs/xfs_ondisk.h b/fs/xfs/xfs_ondisk.h
index 0c381d7..0492436 100644
--- a/fs/xfs/xfs_ondisk.h
+++ b/fs/xfs/xfs_ondisk.h
@@ -134,7 +134,7 @@
 	XFS_CHECK_STRUCT_SIZE(struct xfs_icreate_log,		28);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_ictimestamp,		8);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format_32,	52);
-	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format_64,	56);
+	XFS_CHECK_STRUCT_SIZE(struct xfs_inode_log_format,	56);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_qoff_logformat,	20);
 	XFS_CHECK_STRUCT_SIZE(struct xfs_trans_header,		16);
 }
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index 93a7aaf..cecd375 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -279,7 +279,7 @@
 					(end - 1) >> PAGE_SHIFT);
 		WARN_ON_ONCE(error);
 
-		error = xfs_iomap_write_unwritten(ip, start, length);
+		error = xfs_iomap_write_unwritten(ip, start, length, false);
 		if (error)
 			goto out_drop_iolock;
 	}
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c
index 8b9a9f1..1fdd3fa 100644
--- a/fs/xfs/xfs_qm.c
+++ b/fs/xfs/xfs_qm.c
@@ -111,6 +111,9 @@
 			skipped = 0;
 			break;
 		}
+		/* we're done if id overflows back to zero */
+		if (!next_index)
+			break;
 	}
 
 	if (skipped) {
@@ -1247,6 +1250,7 @@
 	struct xfs_dquot	*dqp,
 	void			*data)
 {
+	struct xfs_mount	*mp = dqp->q_mount;
 	struct list_head	*buffer_list = data;
 	struct xfs_buf		*bp = NULL;
 	int			error = 0;
@@ -1257,7 +1261,32 @@
 	if (!XFS_DQ_IS_DIRTY(dqp))
 		goto out_unlock;
 
-	xfs_dqflock(dqp);
+	/*
+	 * The only way the dquot is already flush locked by the time quotacheck
+	 * gets here is if reclaim flushed it before the dqadjust walk dirtied
+	 * it for the final time. Quotacheck collects all dquot bufs in the
+	 * local delwri queue before dquots are dirtied, so reclaim can't have
+	 * possibly queued it for I/O. The only way out is to push the buffer to
+	 * cycle the flush lock.
+	 */
+	if (!xfs_dqflock_nowait(dqp)) {
+		/* buf is pinned in-core by delwri list */
+		DEFINE_SINGLE_BUF_MAP(map, dqp->q_blkno,
+				      mp->m_quotainfo->qi_dqchunklen);
+		bp = _xfs_buf_find(mp->m_ddev_targp, &map, 1, 0, NULL);
+		if (!bp) {
+			error = -EINVAL;
+			goto out_unlock;
+		}
+		xfs_buf_unlock(bp);
+
+		xfs_buf_delwri_pushbuf(bp, buffer_list);
+		xfs_buf_rele(bp);
+
+		error = -EAGAIN;
+		goto out_unlock;
+	}
+
 	error = xfs_qm_dqflush(dqp, &bp);
 	if (error)
 		goto out_unlock;
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 29a75ec..17d3c96 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -169,6 +169,8 @@
 	error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp);
 	if (error)
 		return error;
+	if (!agbp)
+		return -ENOMEM;
 
 	cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL);
 
@@ -333,7 +335,7 @@
 	struct xfs_defer_ops		*dfops)
 {
 	struct xfs_bmbt_irec		irec = *imap;
-	xfs_fsblock_t			first_block;
+	xfs_fsblock_t			first_block = NULLFSBLOCK;
 	int				nimaps = 1;
 
 	if (imap->br_state == XFS_EXT_NORM)
@@ -765,7 +767,13 @@
 
 	/* If there is a hole at end_fsb - 1 go to the previous extent */
 	if (eof || got.br_startoff > end_fsb) {
-		ASSERT(idx > 0);
+		/*
+		 * In case of racing, overlapping AIO writes no COW extents
+		 * might be left by the time I/O completes for the loser of
+		 * the race.  In that case we are done.
+		 */
+		if (idx <= 0)
+			goto out_cancel;
 		xfs_bmbt_get_all(xfs_iext_get_ext(ifp, --idx), &got);
 	}
 
@@ -839,6 +847,7 @@
 
 out_defer:
 	xfs_defer_cancel(&dfops);
+out_cancel:
 	xfs_trans_cancel(tp);
 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 out:
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 882fb85..67d589e 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -1214,7 +1214,7 @@
 	tmp_mp->m_super = sb;
 	error = xfs_parseargs(tmp_mp, options);
 	xfs_free_fsname(tmp_mp);
-	kfree(tmp_mp);
+	kmem_free(tmp_mp);
 
 	return error;
 }
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 828f383..bdf69e1 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -366,6 +366,7 @@
 DEFINE_BUF_EVENT(xfs_buf_delwri_queue);
 DEFINE_BUF_EVENT(xfs_buf_delwri_queued);
 DEFINE_BUF_EVENT(xfs_buf_delwri_split);
+DEFINE_BUF_EVENT(xfs_buf_delwri_pushbuf);
 DEFINE_BUF_EVENT(xfs_buf_get_uncached);
 DEFINE_BUF_EVENT(xfs_bdstrat_shut);
 DEFINE_BUF_EVENT(xfs_buf_item_relse);
@@ -519,7 +520,6 @@
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_size_stale);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format);
-DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_format_stale);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_ordered);
 DEFINE_BUF_ITEM_EVENT(xfs_buf_item_pin);
@@ -1990,6 +1990,24 @@
 DEFINE_SWAPEXT_EVENT(xfs_swap_extent_before);
 DEFINE_SWAPEXT_EVENT(xfs_swap_extent_after);
 
+TRACE_EVENT(xfs_log_recover,
+	TP_PROTO(struct xlog *log, xfs_daddr_t headblk, xfs_daddr_t tailblk),
+	TP_ARGS(log, headblk, tailblk),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(xfs_daddr_t, headblk)
+		__field(xfs_daddr_t, tailblk)
+	),
+	TP_fast_assign(
+		__entry->dev = log->l_mp->m_super->s_dev;
+		__entry->headblk = headblk;
+		__entry->tailblk = tailblk;
+	),
+	TP_printk("dev %d:%d headblk 0x%llx tailblk 0x%llx",
+		  MAJOR(__entry->dev), MINOR(__entry->dev), __entry->headblk,
+		  __entry->tailblk)
+)
+
 TRACE_EVENT(xfs_log_recover_record,
 	TP_PROTO(struct xlog *log, struct xlog_rec_header *rhead, int pass),
 	TP_ARGS(log, rhead, pass),
diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
index 98024cb..5669cf0 100644
--- a/fs/xfs/xfs_trans.h
+++ b/fs/xfs/xfs_trans.h
@@ -50,6 +50,7 @@
 	struct xfs_ail			*li_ailp;	/* ptr to AIL */
 	uint				li_type;	/* item type */
 	uint				li_flags;	/* misc flags */
+	struct xfs_buf			*li_buf;	/* real buffer pointer */
 	struct xfs_log_item		*li_bio_list;	/* buffer item list */
 	void				(*li_cb)(struct xfs_buf *,
 						 struct xfs_log_item *);
@@ -65,11 +66,13 @@
 } xfs_log_item_t;
 
 #define	XFS_LI_IN_AIL	0x1
-#define XFS_LI_ABORTED	0x2
+#define	XFS_LI_ABORTED	0x2
+#define	XFS_LI_FAILED	0x4
 
 #define XFS_LI_FLAGS \
 	{ XFS_LI_IN_AIL,	"IN_AIL" }, \
-	{ XFS_LI_ABORTED,	"ABORTED" }
+	{ XFS_LI_ABORTED,	"ABORTED" }, \
+	{ XFS_LI_FAILED,	"FAILED" }
 
 struct xfs_item_ops {
 	void (*iop_size)(xfs_log_item_t *, int *, int *);
@@ -80,6 +83,7 @@
 	void (*iop_unlock)(xfs_log_item_t *);
 	xfs_lsn_t (*iop_committed)(xfs_log_item_t *, xfs_lsn_t);
 	void (*iop_committing)(xfs_log_item_t *, xfs_lsn_t);
+	void (*iop_error)(xfs_log_item_t *, xfs_buf_t *);
 };
 
 void	xfs_log_item_init(struct xfs_mount *mp, struct xfs_log_item *item,
@@ -213,12 +217,14 @@
 void		xfs_trans_binval(xfs_trans_t *, struct xfs_buf *);
 void		xfs_trans_inode_buf(xfs_trans_t *, struct xfs_buf *);
 void		xfs_trans_stale_inode_buf(xfs_trans_t *, struct xfs_buf *);
-void		xfs_trans_ordered_buf(xfs_trans_t *, struct xfs_buf *);
+bool		xfs_trans_ordered_buf(xfs_trans_t *, struct xfs_buf *);
 void		xfs_trans_dquot_buf(xfs_trans_t *, struct xfs_buf *, uint);
 void		xfs_trans_inode_alloc_buf(xfs_trans_t *, struct xfs_buf *);
 void		xfs_trans_ichgtime(struct xfs_trans *, struct xfs_inode *, int);
 void		xfs_trans_ijoin(struct xfs_trans *, struct xfs_inode *, uint);
-void		xfs_trans_log_buf(xfs_trans_t *, struct xfs_buf *, uint, uint);
+void		xfs_trans_log_buf(struct xfs_trans *, struct xfs_buf *, uint,
+				  uint);
+void		xfs_trans_dirty_buf(struct xfs_trans *, struct xfs_buf *);
 void		xfs_trans_log_inode(xfs_trans_t *, struct xfs_inode *, uint);
 
 void		xfs_extent_free_init_defer_op(void);
@@ -277,6 +283,6 @@
 		struct xfs_bud_log_item *rudp, struct xfs_defer_ops *dfops,
 		enum xfs_bmap_intent_type type, struct xfs_inode *ip,
 		int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
-		xfs_filblks_t blockcount, xfs_exntst_t state);
+		xfs_filblks_t *blockcount, xfs_exntst_t state);
 
 #endif	/* __XFS_TRANS_H__ */
diff --git a/fs/xfs/xfs_trans_ail.c b/fs/xfs/xfs_trans_ail.c
index d6c9c3e..70f5ab0 100644
--- a/fs/xfs/xfs_trans_ail.c
+++ b/fs/xfs/xfs_trans_ail.c
@@ -684,8 +684,24 @@
 	}
 }
 
-/*
- * xfs_trans_ail_delete_bulk - remove multiple log items from the AIL
+bool
+xfs_ail_delete_one(
+	struct xfs_ail		*ailp,
+	struct xfs_log_item	*lip)
+{
+	struct xfs_log_item	*mlip = xfs_ail_min(ailp);
+
+	trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
+	xfs_ail_delete(ailp, lip);
+	xfs_clear_li_failed(lip);
+	lip->li_flags &= ~XFS_LI_IN_AIL;
+	lip->li_lsn = 0;
+
+	return mlip == lip;
+}
+
+/**
+ * Remove a log items from the AIL
  *
  * @xfs_trans_ail_delete_bulk takes an array of log items that all need to
  * removed from the AIL. The caller is already holding the AIL lock, and done
@@ -706,52 +722,36 @@
  * before returning.
  */
 void
-xfs_trans_ail_delete_bulk(
+xfs_trans_ail_delete(
 	struct xfs_ail		*ailp,
-	struct xfs_log_item	**log_items,
-	int			nr_items,
+	struct xfs_log_item	*lip,
 	int			shutdown_type) __releases(ailp->xa_lock)
 {
-	xfs_log_item_t		*mlip;
-	int			mlip_changed = 0;
-	int			i;
+	struct xfs_mount	*mp = ailp->xa_mount;
+	bool			mlip_changed;
 
-	mlip = xfs_ail_min(ailp);
-
-	for (i = 0; i < nr_items; i++) {
-		struct xfs_log_item *lip = log_items[i];
-		if (!(lip->li_flags & XFS_LI_IN_AIL)) {
-			struct xfs_mount	*mp = ailp->xa_mount;
-
-			spin_unlock(&ailp->xa_lock);
-			if (!XFS_FORCED_SHUTDOWN(mp)) {
-				xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
-		"%s: attempting to delete a log item that is not in the AIL",
-						__func__);
-				xfs_force_shutdown(mp, shutdown_type);
-			}
-			return;
+	if (!(lip->li_flags & XFS_LI_IN_AIL)) {
+		spin_unlock(&ailp->xa_lock);
+		if (!XFS_FORCED_SHUTDOWN(mp)) {
+			xfs_alert_tag(mp, XFS_PTAG_AILDELETE,
+	"%s: attempting to delete a log item that is not in the AIL",
+					__func__);
+			xfs_force_shutdown(mp, shutdown_type);
 		}
-
-		trace_xfs_ail_delete(lip, mlip->li_lsn, lip->li_lsn);
-		xfs_ail_delete(ailp, lip);
-		lip->li_flags &= ~XFS_LI_IN_AIL;
-		lip->li_lsn = 0;
-		if (mlip == lip)
-			mlip_changed = 1;
+		return;
 	}
 
+	mlip_changed = xfs_ail_delete_one(ailp, lip);
 	if (mlip_changed) {
-		if (!XFS_FORCED_SHUTDOWN(ailp->xa_mount))
-			xlog_assign_tail_lsn_locked(ailp->xa_mount);
+		if (!XFS_FORCED_SHUTDOWN(mp))
+			xlog_assign_tail_lsn_locked(mp);
 		if (list_empty(&ailp->xa_ail))
 			wake_up_all(&ailp->xa_empty);
-		spin_unlock(&ailp->xa_lock);
-
-		xfs_log_space_wake(ailp->xa_mount);
-	} else {
-		spin_unlock(&ailp->xa_lock);
 	}
+
+	spin_unlock(&ailp->xa_lock);
+	if (mlip_changed)
+		xfs_log_space_wake(ailp->xa_mount);
 }
 
 int
diff --git a/fs/xfs/xfs_trans_bmap.c b/fs/xfs/xfs_trans_bmap.c
index 6408e7d..14543d9 100644
--- a/fs/xfs/xfs_trans_bmap.c
+++ b/fs/xfs/xfs_trans_bmap.c
@@ -63,7 +63,7 @@
 	int				whichfork,
 	xfs_fileoff_t			startoff,
 	xfs_fsblock_t			startblock,
-	xfs_filblks_t			blockcount,
+	xfs_filblks_t			*blockcount,
 	xfs_exntst_t			state)
 {
 	int				error;
@@ -196,16 +196,23 @@
 	void				**state)
 {
 	struct xfs_bmap_intent		*bmap;
+	xfs_filblks_t			count;
 	int				error;
 
 	bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+	count = bmap->bi_bmap.br_blockcount;
 	error = xfs_trans_log_finish_bmap_update(tp, done_item, dop,
 			bmap->bi_type,
 			bmap->bi_owner, bmap->bi_whichfork,
 			bmap->bi_bmap.br_startoff,
 			bmap->bi_bmap.br_startblock,
-			bmap->bi_bmap.br_blockcount,
+			&count,
 			bmap->bi_bmap.br_state);
+	if (!error && count > 0) {
+		ASSERT(bmap->bi_type == XFS_BMAP_UNMAP);
+		bmap->bi_bmap.br_blockcount = count;
+		return -EAGAIN;
+	}
 	kmem_free(bmap);
 	return error;
 }
diff --git a/fs/xfs/xfs_trans_buf.c b/fs/xfs/xfs_trans_buf.c
index 8ee29ca..3ba7a96 100644
--- a/fs/xfs/xfs_trans_buf.c
+++ b/fs/xfs/xfs_trans_buf.c
@@ -356,6 +356,7 @@
 		 xfs_buf_t	*bp)
 {
 	xfs_buf_log_item_t	*bip;
+	int			freed;
 
 	/*
 	 * Default to a normal brelse() call if the tp is NULL.
@@ -419,16 +420,22 @@
 	/*
 	 * Drop our reference to the buf log item.
 	 */
-	atomic_dec(&bip->bli_refcount);
+	freed = atomic_dec_and_test(&bip->bli_refcount);
 
 	/*
-	 * If the buf item is not tracking data in the log, then
-	 * we must free it before releasing the buffer back to the
-	 * free pool.  Before releasing the buffer to the free pool,
-	 * clear the transaction pointer in b_fsprivate2 to dissolve
-	 * its relation to this transaction.
+	 * If the buf item is not tracking data in the log, then we must free it
+	 * before releasing the buffer back to the free pool.
+	 *
+	 * If the fs has shutdown and we dropped the last reference, it may fall
+	 * on us to release a (possibly dirty) bli if it never made it to the
+	 * AIL (e.g., the aborted unpin already happened and didn't release it
+	 * due to our reference). Since we're already shutdown and need xa_lock,
+	 * just force remove from the AIL and release the bli here.
 	 */
-	if (!xfs_buf_item_dirty(bip)) {
+	if (XFS_FORCED_SHUTDOWN(tp->t_mountp) && freed) {
+		xfs_trans_ail_remove(&bip->bli_item, SHUTDOWN_LOG_IO_ERROR);
+		xfs_buf_item_relse(bp);
+	} else if (!(bip->bli_flags & XFS_BLI_DIRTY)) {
 /***
 		ASSERT(bp->b_pincount == 0);
 ***/
@@ -486,25 +493,17 @@
 }
 
 /*
- * This is called to mark bytes first through last inclusive of the given
- * buffer as needing to be logged when the transaction is committed.
- * The buffer must already be associated with the given transaction.
- *
- * First and last are numbers relative to the beginning of this buffer,
- * so the first byte in the buffer is numbered 0 regardless of the
- * value of b_blkno.
+ * Mark a buffer dirty in the transaction.
  */
 void
-xfs_trans_log_buf(xfs_trans_t	*tp,
-		  xfs_buf_t	*bp,
-		  uint		first,
-		  uint		last)
+xfs_trans_dirty_buf(
+	struct xfs_trans	*tp,
+	struct xfs_buf		*bp)
 {
-	xfs_buf_log_item_t	*bip = bp->b_fspriv;
+	struct xfs_buf_log_item	*bip = bp->b_fspriv;
 
 	ASSERT(bp->b_transp == tp);
 	ASSERT(bip != NULL);
-	ASSERT(first <= last && last < BBTOB(bp->b_length));
 	ASSERT(bp->b_iodone == NULL ||
 	       bp->b_iodone == xfs_buf_iodone_callbacks);
 
@@ -524,8 +523,6 @@
 	bp->b_iodone = xfs_buf_iodone_callbacks;
 	bip->bli_item.li_cb = xfs_buf_iodone;
 
-	trace_xfs_trans_log_buf(bip);
-
 	/*
 	 * If we invalidated the buffer within this transaction, then
 	 * cancel the invalidation now that we're dirtying the buffer
@@ -538,17 +535,37 @@
 		bp->b_flags &= ~XBF_STALE;
 		bip->__bli_format.blf_flags &= ~XFS_BLF_CANCEL;
 	}
+	bip->bli_flags |= XFS_BLI_DIRTY | XFS_BLI_LOGGED;
 
 	tp->t_flags |= XFS_TRANS_DIRTY;
 	bip->bli_item.li_desc->lid_flags |= XFS_LID_DIRTY;
+}
 
-	/*
-	 * If we have an ordered buffer we are not logging any dirty range but
-	 * it still needs to be marked dirty and that it has been logged.
-	 */
-	bip->bli_flags |= XFS_BLI_DIRTY | XFS_BLI_LOGGED;
-	if (!(bip->bli_flags & XFS_BLI_ORDERED))
-		xfs_buf_item_log(bip, first, last);
+/*
+ * This is called to mark bytes first through last inclusive of the given
+ * buffer as needing to be logged when the transaction is committed.
+ * The buffer must already be associated with the given transaction.
+ *
+ * First and last are numbers relative to the beginning of this buffer,
+ * so the first byte in the buffer is numbered 0 regardless of the
+ * value of b_blkno.
+ */
+void
+xfs_trans_log_buf(
+	struct xfs_trans	*tp,
+	struct xfs_buf		*bp,
+	uint			first,
+	uint			last)
+{
+	struct xfs_buf_log_item	*bip = bp->b_fspriv;
+
+	ASSERT(first <= last && last < BBTOB(bp->b_length));
+	ASSERT(!(bip->bli_flags & XFS_BLI_ORDERED));
+
+	xfs_trans_dirty_buf(tp, bp);
+
+	trace_xfs_trans_log_buf(bip);
+	xfs_buf_item_log(bip, first, last);
 }
 
 
@@ -701,14 +718,13 @@
 }
 
 /*
- * Mark the buffer as ordered for this transaction. This means
- * that the contents of the buffer are not recorded in the transaction
- * but it is tracked in the AIL as though it was. This allows us
- * to record logical changes in transactions rather than the physical
- * changes we make to the buffer without changing writeback ordering
- * constraints of metadata buffers.
+ * Mark the buffer as ordered for this transaction. This means that the contents
+ * of the buffer are not recorded in the transaction but it is tracked in the
+ * AIL as though it was. This allows us to record logical changes in
+ * transactions rather than the physical changes we make to the buffer without
+ * changing writeback ordering constraints of metadata buffers.
  */
-void
+bool
 xfs_trans_ordered_buf(
 	struct xfs_trans	*tp,
 	struct xfs_buf		*bp)
@@ -719,8 +735,18 @@
 	ASSERT(bip != NULL);
 	ASSERT(atomic_read(&bip->bli_refcount) > 0);
 
+	if (xfs_buf_item_dirty_format(bip))
+		return false;
+
 	bip->bli_flags |= XFS_BLI_ORDERED;
 	trace_xfs_buf_item_ordered(bip);
+
+	/*
+	 * We don't log a dirty range of an ordered buffer but it still needs
+	 * to be marked dirty and that it has been logged.
+	 */
+	xfs_trans_dirty_buf(tp, bp);
+	return true;
 }
 
 /*
diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h
index 49931b7..b317a36 100644
--- a/fs/xfs/xfs_trans_priv.h
+++ b/fs/xfs/xfs_trans_priv.h
@@ -106,18 +106,9 @@
 	xfs_trans_ail_update_bulk(ailp, NULL, &lip, 1, lsn);
 }
 
-void	xfs_trans_ail_delete_bulk(struct xfs_ail *ailp,
-				struct xfs_log_item **log_items, int nr_items,
-				int shutdown_type)
-				__releases(ailp->xa_lock);
-static inline void
-xfs_trans_ail_delete(
-	struct xfs_ail	*ailp,
-	xfs_log_item_t	*lip,
-	int		shutdown_type) __releases(ailp->xa_lock)
-{
-	xfs_trans_ail_delete_bulk(ailp, &lip, 1, shutdown_type);
-}
+bool xfs_ail_delete_one(struct xfs_ail *ailp, struct xfs_log_item *lip);
+void xfs_trans_ail_delete(struct xfs_ail *ailp, struct xfs_log_item *lip,
+		int shutdown_type) __releases(ailp->xa_lock);
 
 static inline void
 xfs_trans_ail_remove(
@@ -173,4 +164,35 @@
 	*dst = *src;
 }
 #endif
+
+static inline void
+xfs_clear_li_failed(
+	struct xfs_log_item	*lip)
+{
+	struct xfs_buf	*bp = lip->li_buf;
+
+	ASSERT(lip->li_flags & XFS_LI_IN_AIL);
+	lockdep_assert_held(&lip->li_ailp->xa_lock);
+
+	if (lip->li_flags & XFS_LI_FAILED) {
+		lip->li_flags &= ~XFS_LI_FAILED;
+		lip->li_buf = NULL;
+		xfs_buf_rele(bp);
+	}
+}
+
+static inline void
+xfs_set_li_failed(
+	struct xfs_log_item	*lip,
+	struct xfs_buf		*bp)
+{
+	lockdep_assert_held(&lip->li_ailp->xa_lock);
+
+	if (!(lip->li_flags & XFS_LI_FAILED)) {
+		xfs_buf_hold(bp);
+		lip->li_flags |= XFS_LI_FAILED;
+		lip->li_buf = bp;
+	}
+}
+
 #endif	/* __XFS_TRANS_PRIV_H__ */
diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h
index 0504ef8..976f8ac 100644
--- a/include/asm-generic/percpu.h
+++ b/include/asm-generic/percpu.h
@@ -115,15 +115,35 @@
 	(__ret);							\
 })
 
-#define this_cpu_generic_read(pcp)					\
+#define __this_cpu_generic_read_nopreempt(pcp)				\
 ({									\
 	typeof(pcp) __ret;						\
 	preempt_disable_notrace();					\
-	__ret = raw_cpu_generic_read(pcp);				\
+	__ret = READ_ONCE(*raw_cpu_ptr(&(pcp)));			\
 	preempt_enable_notrace();					\
 	__ret;								\
 })
 
+#define __this_cpu_generic_read_noirq(pcp)				\
+({									\
+	typeof(pcp) __ret;						\
+	unsigned long __flags;						\
+	raw_local_irq_save(__flags);					\
+	__ret = raw_cpu_generic_read(pcp);				\
+	raw_local_irq_restore(__flags);					\
+	__ret;								\
+})
+
+#define this_cpu_generic_read(pcp)					\
+({									\
+	typeof(pcp) __ret;						\
+	if (__native_word(pcp))						\
+		__ret = __this_cpu_generic_read_nopreempt(pcp);		\
+	else								\
+		__ret = __this_cpu_generic_read_noirq(pcp);		\
+	__ret;								\
+})
+
 #define this_cpu_generic_to_op(pcp, val, op)				\
 do {									\
 	unsigned long __flags;						\
diff --git a/include/asm-generic/topology.h b/include/asm-generic/topology.h
index fc824e2..5d2add1 100644
--- a/include/asm-generic/topology.h
+++ b/include/asm-generic/topology.h
@@ -48,7 +48,11 @@
 #define parent_node(node)	((void)(node),0)
 #endif
 #ifndef cpumask_of_node
-#define cpumask_of_node(node)	((void)node, cpu_online_mask)
+  #ifdef CONFIG_NEED_MULTIPLE_NODES
+    #define cpumask_of_node(node)	((node) == 0 ? cpu_online_mask : cpu_none_mask)
+  #else
+    #define cpumask_of_node(node)	((void)node, cpu_online_mask)
+  #endif
 #endif
 #ifndef pcibus_to_node
 #define pcibus_to_node(bus)	((void)(bus), -1)
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 31e1d63..dc81e52 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -60,6 +60,22 @@
 #define ALIGN_FUNCTION()  . = ALIGN(8)
 
 /*
+ * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections, which
+ * generates .data.identifier sections, which need to be pulled in with
+ * .data. We don't want to pull in .data..other sections, which Linux
+ * has defined. Same for text and bss.
+ */
+#ifdef CONFIG_LD_DEAD_CODE_DATA_ELIMINATION
+#define TEXT_MAIN .text .text.[0-9a-zA-Z_]*
+#define DATA_MAIN .data .data.[0-9a-zA-Z_]*
+#define BSS_MAIN .bss .bss.[0-9a-zA-Z_]*
+#else
+#define TEXT_MAIN .text
+#define DATA_MAIN .data
+#define BSS_MAIN .bss
+#endif
+
+/*
  * Align to a 32 byte boundary equal to the
  * alignment gcc 4.5 uses for a struct
  */
@@ -198,12 +214,9 @@
 
 /*
  * .data section
- * LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections generates
- * .data.identifier which needs to be pulled in with .data, but don't want to
- * pull in .data..stuff which has its own requirements. Same for bss.
  */
 #define DATA_DATA							\
-	*(.data .data.[0-9a-zA-Z_]*)					\
+	*(DATA_MAIN)							\
 	*(.ref.data)							\
 	*(.data..shared_aligned) /* percpu related */			\
 	MEM_KEEP(init.data)						\
@@ -436,16 +449,17 @@
 		VMLINUX_SYMBOL(__security_initcall_end) = .;		\
 	}
 
-/* .text section. Map to function alignment to avoid address changes
+/*
+ * .text section. Map to function alignment to avoid address changes
  * during second ld run in second ld pass when generating System.map
- * LD_DEAD_CODE_DATA_ELIMINATION option enables -ffunction-sections generates
- * .text.identifier which needs to be pulled in with .text , but some
- * architectures define .text.foo which is not intended to be pulled in here.
- * Those enabling LD_DEAD_CODE_DATA_ELIMINATION must ensure they don't have
- * conflicting section names, and must pull in .text.[0-9a-zA-Z_]* */
+ *
+ * TEXT_MAIN here will match .text.fixup and .text.unlikely if dead
+ * code elimination is enabled, so these sections should be converted
+ * to use ".." first.
+ */
 #define TEXT_TEXT							\
 		ALIGN_FUNCTION();					\
-		*(.text.hot .text .text.fixup .text.unlikely)		\
+		*(.text.hot TEXT_MAIN .text.fixup .text.unlikely)	\
 		*(.ref.text)						\
 	MEM_KEEP(init.text)						\
 	MEM_KEEP(exit.text)						\
@@ -613,7 +627,7 @@
 		BSS_FIRST_SECTIONS					\
 		*(.bss..page_aligned)					\
 		*(.dynbss)						\
-		*(.bss .bss.[0-9a-zA-Z_]*)				\
+		*(BSS_MAIN)						\
 		*(COMMON)						\
 	}
 
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 453a63f..50810be 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -491,7 +491,9 @@
 # define DP_TEST_PHY_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT 0x2
 # define DP_TEST_PHY_PATTERN_PRBS7			0x3
 # define DP_TEST_PHY_PATTERN_80_BIT_CUSTOM_PATTERN	0x4
-# define DP_TEST_PHY_PATTERN_HBR2_CTS_EYE_PATTERN	0x5
+# define DP_TEST_PHY_PATTERN_CP2520_PATTERN_1		0x5
+# define DP_TEST_PHY_PATTERN_CP2520_PATTERN_2		0x6
+# define DP_TEST_PHY_PATTERN_CP2520_PATTERN_3		0x7
 
 #define DP_TEST_RESPONSE		    0x260
 # define DP_TEST_ACK			    (1 << 0)
diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h
index f5678aa..0dbddb3 100644
--- a/include/drm/drm_mipi_dsi.h
+++ b/include/drm/drm_mipi_dsi.h
@@ -23,6 +23,8 @@
 #define MIPI_DSI_MSG_USE_LPM	BIT(1)
 /* read mipi_dsi_msg.ctrl and unicast to only that ctrls */
 #define MIPI_DSI_MSG_UNICAST	BIT(2)
+/* Stack all commands until lastcommand bit and trigger all in one go */
+#define MIPI_DSI_MSG_LASTCOMMAND BIT(3)
 
 /**
  * struct mipi_dsi_msg - read/write DSI buffer
diff --git a/include/dt-bindings/clock/exynos5433.h b/include/dt-bindings/clock/exynos5433.h
index 4fa6bb2..be39d23 100644
--- a/include/dt-bindings/clock/exynos5433.h
+++ b/include/dt-bindings/clock/exynos5433.h
@@ -771,7 +771,10 @@
 
 #define CLK_PCLK_DECON					113
 
-#define DISP_NR_CLK					114
+#define CLK_PHYCLK_MIPIDPHY0_BITCLKDIV8_PHY		114
+#define CLK_PHYCLK_MIPIDPHY0_RXCLKESC0_PHY		115
+
+#define DISP_NR_CLK					116
 
 /* CMU_AUD */
 #define CLK_MOUT_AUD_PLL_USER				1
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/include/dt-bindings/clock/qcom,cpu-a7.h
similarity index 61%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to include/dt-bindings/clock/qcom,cpu-a7.h
index c06b806..9b89030 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/include/dt-bindings/clock/qcom,cpu-a7.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -10,14 +11,11 @@
  * GNU General Public License for more details.
  */
 
+#ifndef _DT_BINDINGS_CLK_MSM_CPU_A7_H
+#define _DT_BINDINGS_CLK_MSM_CPU_A7_H
 
-/dts-v1/;
+#define SYS_APC0_AUX_CLK	0
+#define APCS_CPU_PLL		1
+#define APCS_CLK		2
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
-};
+#endif
diff --git a/include/dt-bindings/clock/qcom,cpucc-sdm845.h b/include/dt-bindings/clock/qcom,cpucc-sdm845.h
index db3c940..f039284 100644
--- a/include/dt-bindings/clock/qcom,cpucc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,cpucc-sdm845.h
@@ -27,5 +27,8 @@
 #define CPU5_PERFCL_CLK						10
 #define CPU6_PERFCL_CLK						11
 #define CPU7_PERFCL_CLK						12
+#define L3_MISC_VOTE_CLK					13
+#define CPU4_PWRCL_CLK						14
+#define CPU5_PWRCL_CLK						15
 
 #endif
diff --git a/include/dt-bindings/clock/qcom,gcc-sdm845.h b/include/dt-bindings/clock/qcom,gcc-sdm845.h
index 339d470..c8696df 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdm845.h
@@ -212,6 +212,7 @@
 #define GCC_VS_CTRL_CLK_SRC					194
 #define GCC_VSENSOR_CLK_SRC					195
 #define GPLL4							196
+#define GPLL6							197
 
 /* GCC reset clocks */
 #define GCC_MMSS_BCR						0
diff --git a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
index e773848..950811f 100644
--- a/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
+++ b/include/dt-bindings/clock/qcom,gcc-sdxpoorwills.h
@@ -48,59 +48,60 @@
 #define GCC_CPUSS_AHB_CLK					30
 #define GCC_CPUSS_AHB_CLK_SRC					31
 #define GCC_CPUSS_GNOC_CLK					32
-#define GCC_CPUSS_GPLL0_CLK_SRC					33
-#define GCC_CPUSS_RBCPR_CLK					34
-#define GCC_CPUSS_RBCPR_CLK_SRC					35
-#define GCC_GP1_CLK						36
-#define GCC_GP1_CLK_SRC						37
-#define GCC_GP2_CLK						38
-#define GCC_GP2_CLK_SRC						39
-#define GCC_GP3_CLK						40
-#define GCC_GP3_CLK_SRC						41
-#define GCC_MSS_CFG_AHB_CLK					42
-#define GCC_MSS_GPLL0_DIV_CLK_SRC				43
-#define GCC_MSS_SNOC_AXI_CLK					44
-#define GCC_PCIE_AUX_CLK					45
-#define GCC_PCIE_AUX_PHY_CLK_SRC				46
-#define GCC_PCIE_CFG_AHB_CLK					47
-#define GCC_PCIE_0_CLKREF_EN					48
-#define GCC_PCIE_MSTR_AXI_CLK					49
-#define GCC_PCIE_PHY_REFGEN_CLK					50
-#define GCC_PCIE_PHY_REFGEN_CLK_SRC				51
-#define GCC_PCIE_PIPE_CLK					52
-#define GCC_PCIE_SLEEP_CLK					53
-#define GCC_PCIE_SLV_AXI_CLK					54
-#define GCC_PCIE_SLV_Q2A_AXI_CLK				55
-#define GCC_PDM2_CLK						56
-#define GCC_PDM2_CLK_SRC					57
-#define GCC_PDM_AHB_CLK						58
-#define GCC_PDM_XO4_CLK						59
-#define GCC_PRNG_AHB_CLK					60
-#define GCC_SDCC1_AHB_CLK					61
-#define GCC_SDCC1_APPS_CLK					62
-#define GCC_SDCC1_APPS_CLK_SRC					63
-#define GCC_SPMI_FETCHER_AHB_CLK				64
-#define GCC_SPMI_FETCHER_CLK					65
-#define GCC_SPMI_FETCHER_CLK_SRC				66
-#define GCC_SYS_NOC_CPUSS_AHB_CLK				67
-#define GCC_SYS_NOC_USB3_CLK					68
-#define GCC_USB30_MASTER_CLK					69
-#define GCC_USB30_MASTER_CLK_SRC				70
-#define GCC_USB30_MOCK_UTMI_CLK					71
-#define GCC_USB30_MOCK_UTMI_CLK_SRC				72
-#define GCC_USB30_SLEEP_CLK					73
-#define GCC_USB3_PRIM_CLKREF_CLK				74
-#define GCC_USB3_PHY_AUX_CLK					75
-#define GCC_USB3_PHY_AUX_CLK_SRC				76
-#define GCC_USB3_PHY_PIPE_CLK					77
-#define GCC_USB_PHY_CFG_AHB2PHY_CLK				78
-#define GCC_XO_DIV4_CLK						79
-#define GPLL0							80
-#define GPLL0_OUT_EVEN						81
-
-/* GDSCs */
-#define PCIE_GDSC						0
-#define USB30_GDSC						1
+#define GCC_CPUSS_RBCPR_CLK					33
+#define GCC_CPUSS_RBCPR_CLK_SRC					34
+#define GCC_EMAC_CLK_SRC					35
+#define GCC_EMAC_PTP_CLK_SRC					36
+#define GCC_ETH_AXI_CLK						37
+#define GCC_ETH_PTP_CLK						38
+#define GCC_ETH_RGMII_CLK					39
+#define GCC_ETH_SLAVE_AHB_CLK					40
+#define GCC_GP1_CLK						41
+#define GCC_GP1_CLK_SRC						42
+#define GCC_GP2_CLK						43
+#define GCC_GP2_CLK_SRC						44
+#define GCC_GP3_CLK						45
+#define GCC_GP3_CLK_SRC						46
+#define GCC_MSS_CFG_AHB_CLK					47
+#define GCC_MSS_GPLL0_DIV_CLK_SRC				48
+#define GCC_MSS_SNOC_AXI_CLK					49
+#define GCC_PCIE_AUX_CLK					50
+#define GCC_PCIE_AUX_PHY_CLK_SRC				51
+#define GCC_PCIE_CFG_AHB_CLK					52
+#define GCC_PCIE_MSTR_AXI_CLK					53
+#define GCC_PCIE_PHY_REFGEN_CLK					54
+#define GCC_PCIE_PHY_REFGEN_CLK_SRC				55
+#define GCC_PCIE_PIPE_CLK					56
+#define GCC_PCIE_SLEEP_CLK					57
+#define GCC_PCIE_SLV_AXI_CLK					58
+#define GCC_PCIE_SLV_Q2A_AXI_CLK				59
+#define GCC_PDM2_CLK						60
+#define GCC_PDM2_CLK_SRC					61
+#define GCC_PDM_AHB_CLK						62
+#define GCC_PDM_XO4_CLK						63
+#define GCC_PRNG_AHB_CLK					64
+#define GCC_SDCC1_AHB_CLK					65
+#define GCC_SDCC1_APPS_CLK					66
+#define GCC_SDCC1_APPS_CLK_SRC					67
+#define GCC_SPMI_FETCHER_AHB_CLK				68
+#define GCC_SPMI_FETCHER_CLK					69
+#define GCC_SPMI_FETCHER_CLK_SRC				70
+#define GCC_SYS_NOC_CPUSS_AHB_CLK				71
+#define GCC_SYS_NOC_USB3_CLK					72
+#define GCC_USB30_MASTER_CLK					73
+#define GCC_USB30_MASTER_CLK_SRC				74
+#define GCC_USB30_MOCK_UTMI_CLK					75
+#define GCC_USB30_MOCK_UTMI_CLK_SRC				76
+#define GCC_USB30_SLEEP_CLK					77
+#define GCC_USB3_PHY_AUX_CLK					78
+#define GCC_USB3_PHY_AUX_CLK_SRC				79
+#define GCC_USB3_PHY_PIPE_CLK					80
+#define GCC_USB_PHY_CFG_AHB2PHY_CLK				81
+#define GPLL0							82
+#define GPLL0_OUT_EVEN						83
+#define GPLL4							84
+#define GPLL4_OUT_EVEN						85
+#define GCC_USB3_PRIM_CLKREF_CLK				86
 
 /* CPU clocks */
 #define CLOCK_A7SS						0
@@ -125,5 +126,6 @@
 #define GCC_USB3PHY_PHY_BCR					16
 #define GCC_QUSB2PHY_BCR					17
 #define GCC_USB_PHY_CFG_AHB2PHY_BCR				18
+#define GCC_EMAC_BCR						19
 
 #endif
diff --git a/include/dt-bindings/clock/qcom,gpucc-sdm845.h b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
index a20d4f0..a82a630 100644
--- a/include/dt-bindings/clock/qcom,gpucc-sdm845.h
+++ b/include/dt-bindings/clock/qcom,gpucc-sdm845.h
@@ -17,33 +17,32 @@
 /* GPUCC clock registers */
 #define GPU_CC_ACD_AHB_CLK					0
 #define GPU_CC_ACD_CXO_CLK					1
-#define GPU_CC_AHB_CLK						2
-#define GPU_CC_CRC_AHB_CLK					3
-#define GPU_CC_CX_APB_CLK					4
-#define GPU_CC_CX_GMU_CLK					5
-#define GPU_CC_CX_QDSS_AT_CLK					6
-#define GPU_CC_CX_QDSS_TRIG_CLK					7
-#define GPU_CC_CX_QDSS_TSCTR_CLK				8
-#define GPU_CC_CX_SNOC_DVM_CLK					9
-#define GPU_CC_CXO_AON_CLK					10
-#define GPU_CC_CXO_CLK						11
-#define GPU_CC_GX_GMU_CLK					12
-#define GPU_CC_GX_QDSS_TSCTR_CLK				13
-#define GPU_CC_GX_VSENSE_CLK					14
-#define GPU_CC_PLL0_OUT_MAIN					15
-#define GPU_CC_PLL0_OUT_ODD					16
-#define GPU_CC_PLL0_OUT_TEST					17
-#define GPU_CC_PLL1						18
-#define GPU_CC_PLL1_OUT_EVEN					19
-#define GPU_CC_PLL1_OUT_MAIN					20
-#define GPU_CC_PLL1_OUT_ODD					21
-#define GPU_CC_PLL1_OUT_TEST					22
-#define GPU_CC_PLL_TEST_CLK					23
-#define GPU_CC_SLEEP_CLK					24
-#define GPU_CC_GMU_CLK_SRC					25
-#define GPU_CC_CX_GFX3D_CLK					26
-#define GPU_CC_CX_GFX3D_SLV_CLK					27
-#define GPU_CC_PLL0						28
+#define GPU_CC_CRC_AHB_CLK					2
+#define GPU_CC_CX_APB_CLK					3
+#define GPU_CC_CX_GMU_CLK					4
+#define GPU_CC_CX_QDSS_AT_CLK					5
+#define GPU_CC_CX_QDSS_TRIG_CLK					6
+#define GPU_CC_CX_QDSS_TSCTR_CLK				7
+#define GPU_CC_CX_SNOC_DVM_CLK					8
+#define GPU_CC_CXO_AON_CLK					9
+#define GPU_CC_CXO_CLK						10
+#define GPU_CC_GX_GMU_CLK					11
+#define GPU_CC_GX_QDSS_TSCTR_CLK				12
+#define GPU_CC_GX_VSENSE_CLK					13
+#define GPU_CC_PLL0_OUT_MAIN					14
+#define GPU_CC_PLL0_OUT_ODD					15
+#define GPU_CC_PLL0_OUT_TEST					16
+#define GPU_CC_PLL1						17
+#define GPU_CC_PLL1_OUT_EVEN					18
+#define GPU_CC_PLL1_OUT_MAIN					19
+#define GPU_CC_PLL1_OUT_ODD					20
+#define GPU_CC_PLL1_OUT_TEST					21
+#define GPU_CC_PLL_TEST_CLK					22
+#define GPU_CC_SLEEP_CLK					23
+#define GPU_CC_GMU_CLK_SRC					24
+#define GPU_CC_CX_GFX3D_CLK					25
+#define GPU_CC_CX_GFX3D_SLV_CLK					26
+#define GPU_CC_PLL0						27
 
 /* GPUCC reset clock registers */
 #define GPUCC_GPU_CC_ACD_BCR					0
diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h
index bc87beb..ffa4bb0 100644
--- a/include/dt-bindings/msm/msm-bus-ids.h
+++ b/include/dt-bindings/msm/msm-bus-ids.h
@@ -89,6 +89,13 @@
 #define	MSM_BUS_BCM_ACV 7037
 #define	MSM_BUS_BCM_ALC 7038
 #define	MSM_BUS_BCM_QUP0 7039
+#define	MSM_BUS_BCM_CE 7040
+#define	MSM_BUS_BCM_PN0 7041
+#define	MSM_BUS_BCM_PN1 7042
+#define	MSM_BUS_BCM_PN2 7043
+#define	MSM_BUS_BCM_PN3 7044
+#define	MSM_BUS_BCM_PN4 7045
+#define	MSM_BUS_BCM_PN5 7046
 
 #define	MSM_BUS_RSC_APPS 8000
 #define	MSM_BUS_RSC_DISP 8001
@@ -251,7 +258,11 @@
 #define	MSM_BUS_MASTER_CAMNOC_HF1_UNCOMP 147
 #define	MSM_BUS_MASTER_CAMNOC_SF_UNCOMP 148
 #define	MSM_BUS_MASTER_GIC 149
-#define	MSM_BUS_MASTER_MASTER_LAST 150
+#define	MSM_BUS_MASTER_EMMC 150
+#define	MSM_BUS_MASTER_SPMI_FETCHER 151
+#define	MSM_BUS_MASTER_ANOC_SNOC 152
+#define	MSM_BUS_MASTER_ANOC_IPA 153
+#define	MSM_BUS_MASTER_MASTER_LAST 154
 
 #define MSM_BUS_MASTER_LLCC_DISPLAY 20000
 #define MSM_BUS_MASTER_MNOC_HF_MEM_NOC_DISPLAY 20001
@@ -592,7 +603,11 @@
 #define	MSM_BUS_SLAVE_MEM_NOC_SNOC 776
 #define	MSM_BUS_SLAVE_IPA_CORE 777
 #define	MSM_BUS_SLAVE_CAMNOC_UNCOMP 778
-#define	MSM_BUS_SLAVE_LAST 779
+#define	MSM_BUS_SLAVE_ANOC_SNOC 779
+#define	MSM_BUS_SLAVE_ANOC_IPA 780
+#define	MSM_BUS_SLAVE_EMAC_CFG 781
+#define	MSM_BUS_SLAVE_EMMC_CFG 782
+#define	MSM_BUS_SLAVE_LAST 783
 
 #define	MSM_BUS_SLAVE_EBI_CH0_DISPLAY 20512
 #define	MSM_BUS_SLAVE_LLCC_DISPLAY 20513
@@ -767,6 +782,7 @@
 #define	ICBID_MASTER_CNOC_A2NOC 146
 #define	ICBID_MASTER_WLAN 147
 #define	ICBID_MASTER_MSS_CE 148
+#define	ICBID_MASTER_EMMC 149
 
 #define	ICBID_SLAVE_EBI1 0
 #define	ICBID_SLAVE_APPSS_L2 1
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/include/dt-bindings/soc/qcom,dcc_v2.h
similarity index 70%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to include/dt-bindings/soc/qcom,dcc_v2.h
index c06b806..fb4ed6d 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/include/dt-bindings/soc/qcom,dcc_v2.h
@@ -10,14 +10,11 @@
  * GNU General Public License for more details.
  */
 
+#ifndef __DT_BINDINGS_QCOM_DCC_V2_H
+#define __DT_BINDINGS_QCOM_DCC_V2_H
 
-/dts-v1/;
+#define DCC_READ	0
+#define DCC_WRITE	1
+#define DCC_LOOP	2
 
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
-};
+#endif
diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h
index 8a30cb5..9d80312 100644
--- a/include/linux/alarmtimer.h
+++ b/include/linux/alarmtimer.h
@@ -5,12 +5,10 @@
 #include <linux/hrtimer.h>
 #include <linux/timerqueue.h>
 #include <linux/rtc.h>
-#include <linux/types.h>
 
 enum alarmtimer_type {
 	ALARM_REALTIME,
 	ALARM_BOOTTIME,
-	ALARM_POWEROFF_REALTIME,
 
 	ALARM_NUMTYPE,
 };
@@ -50,9 +48,6 @@
 void alarm_restart(struct alarm *alarm);
 int alarm_try_to_cancel(struct alarm *alarm);
 int alarm_cancel(struct alarm *alarm);
-void set_power_on_alarm(void);
-void power_on_alarm_init(void);
-enum alarmtimer_type clock2alarm(clockid_t clockid);
 
 u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval);
 u64 alarm_forward_now(struct alarm *alarm, ktime_t interval);
@@ -60,8 +55,5 @@
 
 /* Provide way to access the rtc device being used by alarmtimers */
 struct rtc_device *alarmtimer_get_rtcdev(void);
-#ifdef CONFIG_RTC_DRV_QPNP
-extern bool poweron_alarm;
-#endif
 
 #endif
diff --git a/include/linux/arm-smmu-errata.h b/include/linux/arm-smmu-errata.h
new file mode 100644
index 0000000..3d36a52
--- /dev/null
+++ b/include/linux/arm-smmu-errata.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __ARM_SMMU_ERRATA_H__
+#define __ARM_SMMU_ERRATA_H__
+
+#define ARM_SMMU_MIN_IOVA_ALIGN SZ_16K
+#define ARM_SMMU_GUARD_PROT (IOMMU_READ | IOMMU_WRITE | IOMMU_GUARD)
+
+#ifdef CONFIG_ARM_SMMU
+
+struct page *arm_smmu_errata_get_guard_page(int vmid);
+#else
+
+static inline struct page *arm_smmu_errata_get_guard_page(
+				int vmid)
+{
+	return NULL;
+}
+#endif
+#endif
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9d4443f..2be99b2 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -387,6 +387,20 @@
 		return __audit_socketcall(nargs, args);
 	return 0;
 }
+
+static inline int audit_socketcall_compat(int nargs, u32 *args)
+{
+	unsigned long a[AUDITSC_ARGS];
+	int i;
+
+	if (audit_dummy_context())
+		return 0;
+
+	for (i = 0; i < nargs; i++)
+		a[i] = (unsigned long)args[i];
+	return __audit_socketcall(nargs, a);
+}
+
 static inline int audit_sockaddr(int len, void *addr)
 {
 	if (unlikely(!audit_dummy_context()))
@@ -513,6 +527,12 @@
 {
 	return 0;
 }
+
+static inline int audit_socketcall_compat(int nargs, u32 *args)
+{
+	return 0;
+}
+
 static inline void audit_fd_pair(int fd1, int fd2)
 { }
 static inline int audit_sockaddr(int len, void *addr)
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 2b8b6e0..8a7a15c 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -81,6 +81,12 @@
 	struct bio_set		*bi_pool;
 
 	/*
+	 * When using dircet-io (O_DIRECT), we can't get the inode from a bio
+	 * by walking bio->bi_io_vec->bv_page->mapping->host
+	 * since the page is anon.
+	 */
+	struct inode		*bi_dio_inode;
+	/*
 	 * We can inline a number of vecs at the end of the bio, to avoid
 	 * double allocations for a small number of bio_vecs. This member
 	 * MUST obviously be kept at the very end of the bio.
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index a13b031..3101141 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -40,6 +40,7 @@
 	 */
 	s64 min_value;
 	u64 max_value;
+	bool value_from_signed;
 };
 
 enum bpf_stack_slot_type {
diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h
index ebbacd1..447a915 100644
--- a/include/linux/buffer_head.h
+++ b/include/linux/buffer_head.h
@@ -226,6 +226,7 @@
 				loff_t, unsigned, unsigned,
 				struct page *, void *);
 void page_zero_new_buffers(struct page *page, unsigned from, unsigned to);
+void clean_page_buffers(struct page *page);
 int cont_write_begin(struct file *, struct address_space *, loff_t,
 			unsigned, unsigned, struct page **, void **,
 			get_block_t *, loff_t *);
diff --git a/include/linux/ccp.h b/include/linux/ccp.h
index edc5d04..1cfe5ef 100644
--- a/include/linux/ccp.h
+++ b/include/linux/ccp.h
@@ -1,7 +1,7 @@
 /*
  * AMD Cryptographic Coprocessor (CCP) driver
  *
- * Copyright (C) 2013,2016 Advanced Micro Devices, Inc.
+ * Copyright (C) 2013,2017 Advanced Micro Devices, Inc.
  *
  * Author: Tom Lendacky <thomas.lendacky@amd.com>
  * Author: Gary R Hook <gary.hook@amd.com>
@@ -222,6 +222,7 @@
  * AES operation the new IV overwrites the old IV.
  */
 struct ccp_xts_aes_engine {
+	enum ccp_aes_type type;
 	enum ccp_aes_action action;
 	enum ccp_xts_aes_unit_size unit_size;
 
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index a8003cc7..32c3d42 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -267,7 +267,7 @@
 static inline void coresight_abort(void) {}
 #endif
 
-#ifdef CONFIG_OF
+#if defined(CONFIG_OF) && defined(CONFIG_CORESIGHT)
 extern struct coresight_platform_data *of_get_coresight_platform_data(
 				struct device *dev, struct device_node *node);
 extern struct coresight_cti_data *of_get_coresight_cti_data(
@@ -275,7 +275,7 @@
 #else
 static inline struct coresight_platform_data *of_get_coresight_platform_data(
 	struct device *dev, struct device_node *node) { return NULL; }
-static inlint struct coresight_cti_data *of_get_coresight_cti_data(
+static inline struct coresight_cti_data *of_get_coresight_cti_data(
 		struct device *dev, struct device_node *node) { return NULL; }
 #endif
 
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 40b66f9..3484287 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -28,21 +28,6 @@
 	struct device dev;
 };
 
-struct cpu_pstate_pwr {
-	unsigned int freq;
-	uint32_t power;
-};
-
-struct cpu_pwr_stats {
-	int cpu;
-	long temp;
-	struct cpu_pstate_pwr *ptable;
-	bool throttling;
-	int len;
-};
-
-extern struct cpu_pwr_stats *get_cpu_pwr_stats(void);
-
 extern void boot_cpu_init(void);
 extern void boot_cpu_state_init(void);
 
@@ -194,6 +179,7 @@
 extern void cpu_hotplug_begin(void);
 extern void cpu_hotplug_done(void);
 extern void get_online_cpus(void);
+extern void cpu_hotplug_mutex_held(void);
 extern void put_online_cpus(void);
 extern void cpu_hotplug_disable(void);
 extern void cpu_hotplug_enable(void);
@@ -216,6 +202,7 @@
 #define cpu_hotplug_enable()	do { } while (0)
 #define hotcpu_notifier(fn, pri)	do { (void)(fn); } while (0)
 #define __hotcpu_notifier(fn, pri)	do { (void)(fn); } while (0)
+#define cpu_hotplug_mutex_held()	do { } while (0)
 /* These aren't inline functions due to a GCC bug. */
 #define register_hotcpu_notifier(nb)	({ (void)(nb); 0; })
 #define __register_hotcpu_notifier(nb)	({ (void)(nb); 0; })
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 9ddaf05..d921206 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -10,7 +10,6 @@
 	CPUHP_PERF_X86_PREPARE,
 	CPUHP_PERF_X86_UNCORE_PREP,
 	CPUHP_PERF_X86_AMD_UNCORE_PREP,
-	CPUHP_PERF_X86_RAPL_PREP,
 	CPUHP_PERF_BFIN,
 	CPUHP_PERF_POWER,
 	CPUHP_PERF_SUPERH,
@@ -60,6 +59,7 @@
 	CPUHP_AP_OFFLINE,
 	CPUHP_AP_SCHED_STARTING,
 	CPUHP_AP_RCUTREE_DYING,
+	CPUHP_AP_KMAP_DYING,
 	CPUHP_AP_IRQ_GIC_STARTING,
 	CPUHP_AP_IRQ_GICV3_STARTING,
 	CPUHP_AP_IRQ_HIP04_STARTING,
@@ -129,6 +129,7 @@
 	CPUHP_AP_WORKQUEUE_ONLINE,
 	CPUHP_AP_RCUTREE_ONLINE,
 	CPUHP_AP_NOTIFY_ONLINE,
+	CPUHP_AP_NOTIFY_PERF_ONLINE,
 	CPUHP_AP_ONLINE_DYN,
 	CPUHP_AP_ONLINE_DYN_END		= CPUHP_AP_ONLINE_DYN + 30,
 	CPUHP_AP_X86_HPET_ONLINE,
diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h
index bfc204e..d807fa9 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -16,6 +16,19 @@
 
 #ifdef CONFIG_CPUSETS
 
+/*
+ * Static branch rewrites can happen in an arbitrary order for a given
+ * key. In code paths where we need to loop with read_mems_allowed_begin() and
+ * read_mems_allowed_retry() to get a consistent view of mems_allowed, we need
+ * to ensure that begin() always gets rewritten before retry() in the
+ * disabled -> enabled transition. If not, then if local irqs are disabled
+ * around the loop, we can deadlock since retry() would always be
+ * comparing the latest value of the mems_allowed seqcount against 0 as
+ * begin() still would see cpusets_enabled() as false. The enabled -> disabled
+ * transition should happen in reverse order for the same reasons (want to stop
+ * looking at real value of mems_allowed.sequence in retry() first).
+ */
+extern struct static_key_false cpusets_pre_enable_key;
 extern struct static_key_false cpusets_enabled_key;
 static inline bool cpusets_enabled(void)
 {
@@ -30,17 +43,21 @@
 
 static inline void cpuset_inc(void)
 {
+	static_branch_inc(&cpusets_pre_enable_key);
 	static_branch_inc(&cpusets_enabled_key);
 }
 
 static inline void cpuset_dec(void)
 {
 	static_branch_dec(&cpusets_enabled_key);
+	static_branch_dec(&cpusets_pre_enable_key);
 }
 
 extern int cpuset_init(void);
 extern void cpuset_init_smp(void);
+extern void cpuset_force_rebuild(void);
 extern void cpuset_update_active_cpus(bool cpu_online);
+extern void cpuset_wait_for_hotplug(void);
 extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask);
 extern void cpuset_cpus_allowed_fallback(struct task_struct *p);
 extern nodemask_t cpuset_mems_allowed(struct task_struct *p);
@@ -113,7 +130,7 @@
  */
 static inline unsigned int read_mems_allowed_begin(void)
 {
-	if (!cpusets_enabled())
+	if (!static_branch_unlikely(&cpusets_pre_enable_key))
 		return 0;
 
 	return read_seqcount_begin(&current->mems_allowed_seq);
@@ -127,7 +144,7 @@
  */
 static inline bool read_mems_allowed_retry(unsigned int seq)
 {
-	if (!cpusets_enabled())
+	if (!static_branch_unlikely(&cpusets_enabled_key))
 		return false;
 
 	return read_seqcount_retry(&current->mems_allowed_seq, seq);
@@ -153,11 +170,15 @@
 static inline int cpuset_init(void) { return 0; }
 static inline void cpuset_init_smp(void) {}
 
+static inline void cpuset_force_rebuild(void) { }
+
 static inline void cpuset_update_active_cpus(bool cpu_online)
 {
 	partition_sched_domains(1, NULL, NULL);
 }
 
+static inline void cpuset_wait_for_hotplug(void) { }
+
 static inline void cpuset_cpus_allowed(struct task_struct *p,
 				       struct cpumask *mask)
 {
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 7f7e9a7..8dce6fd 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -20,6 +20,7 @@
 #include <asm/errno.h>
 
 #ifdef CONFIG_IOMMU_DMA
+#include <linux/dma-mapping.h>
 #include <linux/iommu.h>
 #include <linux/msi.h>
 
diff --git a/include/linux/dma-mapping-fast.h b/include/linux/dma-mapping-fast.h
index 560f047..e9dabab 100644
--- a/include/linux/dma-mapping-fast.h
+++ b/include/linux/dma-mapping-fast.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -25,6 +25,9 @@
 	size_t		 size;
 	size_t		 num_4k_pages;
 
+	u32		min_iova_align;
+	struct page	*guard_page;
+
 	unsigned int	bitmap_size;
 	unsigned long	*bitmap;
 	unsigned long	next_start;
@@ -41,19 +44,17 @@
 };
 
 #ifdef CONFIG_IOMMU_IO_PGTABLE_FAST
-int fast_smmu_attach_device(struct device *dev,
+int fast_smmu_init_mapping(struct device *dev,
 			    struct dma_iommu_mapping *mapping);
-void fast_smmu_detach_device(struct device *dev,
-			     struct dma_iommu_mapping *mapping);
+void fast_smmu_release_mapping(struct kref *kref);
 #else
-static inline int fast_smmu_attach_device(struct device *dev,
+static inline int fast_smmu_init_mapping(struct device *dev,
 					  struct dma_iommu_mapping *mapping)
 {
 	return -ENODEV;
 }
 
-static inline void fast_smmu_detach_device(struct device *dev,
-					   struct dma_iommu_mapping *mapping)
+static inline void fast_smmu_release_mapping(struct kref *kref)
 {
 }
 #endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 026aa0a..4f6ec47 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -941,9 +941,9 @@
 /* Page cache limit. The filesystems should put that into their s_maxbytes 
    limits, otherwise bad things can happen in VM. */ 
 #if BITS_PER_LONG==32
-#define MAX_LFS_FILESIZE	(((loff_t)PAGE_SIZE << (BITS_PER_LONG-1))-1)
+#define MAX_LFS_FILESIZE	((loff_t)ULONG_MAX << PAGE_SHIFT)
 #elif BITS_PER_LONG==64
-#define MAX_LFS_FILESIZE 	((loff_t)0x7fffffffffffffffLL)
+#define MAX_LFS_FILESIZE 	((loff_t)LLONG_MAX)
 #endif
 
 #define FL_POSIX	1
@@ -2782,6 +2782,7 @@
 #endif
 extern void unlock_new_inode(struct inode *);
 extern unsigned int get_next_ino(void);
+extern void evict_inodes(struct super_block *sb);
 
 extern void __iget(struct inode * inode);
 extern void iget_failed(struct inode *);
@@ -2924,6 +2925,8 @@
 		wake_up_bit(&inode->i_state, __I_DIO_WAKEUP);
 }
 
+struct inode *dio_bio_get_inode(struct bio *bio);
+
 extern void inode_set_flags(struct inode *inode, unsigned int flags,
 			    unsigned int mask);
 
diff --git a/include/linux/fscrypto.h b/include/linux/fscrypto.h
index f6dfc29..9b57c19 100644
--- a/include/linux/fscrypto.h
+++ b/include/linux/fscrypto.h
@@ -34,6 +34,7 @@
 #define FS_ENCRYPTION_MODE_AES_256_GCM		2
 #define FS_ENCRYPTION_MODE_AES_256_CBC		3
 #define FS_ENCRYPTION_MODE_AES_256_CTS		4
+#define FS_ENCRYPTION_MODE_PRIVATE          127
 
 /**
  * Encryption context for inode
@@ -80,6 +81,7 @@
 	u8 ci_flags;
 	struct crypto_skcipher *ci_ctfm;
 	u8 ci_master_key[FS_KEY_DESCRIPTOR_SIZE];
+	u8 ci_raw_key[FS_MAX_KEY_SIZE];
 };
 
 #define FS_CTX_REQUIRES_FREE_ENCRYPT_FL		0x00000001
@@ -176,7 +178,8 @@
 
 static inline bool fscrypt_valid_contents_enc_mode(u32 mode)
 {
-	return (mode == FS_ENCRYPTION_MODE_AES_256_XTS);
+	return (mode == FS_ENCRYPTION_MODE_AES_256_XTS ||
+		mode == FS_ENCRYPTION_MODE_PRIVATE);
 }
 
 static inline bool fscrypt_valid_filenames_enc_mode(u32 mode)
@@ -257,6 +260,7 @@
 /* keyinfo.c */
 extern int fscrypt_get_encryption_info(struct inode *);
 extern void fscrypt_put_encryption_info(struct inode *, struct fscrypt_info *);
+extern int fs_using_hardware_encryption(struct inode *inode);
 
 /* fname.c */
 extern int fscrypt_setup_filename(struct inode *, const struct qstr *,
@@ -354,6 +358,11 @@
 	return;
 }
 
+static inline int fs_notsupp_using_hardware_encryption(struct inode *inode)
+{
+	return -EOPNOTSUPP;
+}
+
  /* fname.c */
 static inline int fscrypt_notsupp_setup_filename(struct inode *dir,
 			const struct qstr *iname,
diff --git a/include/linux/gfp.h b/include/linux/gfp.h
index 46cd745..16ef407 100644
--- a/include/linux/gfp.h
+++ b/include/linux/gfp.h
@@ -189,7 +189,7 @@
 #define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE)
 
 /* Room for N __GFP_FOO bits */
-#define __GFP_BITS_SHIFT 26
+#define __GFP_BITS_SHIFT 27
 #define __GFP_BITS_MASK ((__force gfp_t)((1 << __GFP_BITS_SHIFT) - 1))
 
 /*
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 61aff32..657b565 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -41,6 +41,7 @@
 
 #ifdef CONFIG_ARCH_WANT_KMAP_ATOMIC_FLUSH
 void kmap_atomic_flush_unused(void);
+int kmap_remove_unused_cpu(unsigned int cpu);
 #else
 static inline void kmap_atomic_flush_unused(void) { }
 #endif
@@ -91,6 +92,10 @@
 
 #endif /* CONFIG_HIGHMEM */
 
+#if !defined(CONFIG_HIGHMEM) || !defined(CONFIG_ARCH_WANT_KMAP_ATOMIC_FLUSH)
+static inline int kmap_remove_unused_cpu(unsigned int cpu) { return 0; }
+#endif
+
 #if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32)
 
 DECLARE_PER_CPU(int, __kmap_atomic_idx);
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index d596a07..8cc99de 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -1521,11 +1521,11 @@
 
 	cur_write_sz = hv_get_bytes_to_write(rbi);
 
-	if (cur_write_sz < pending_sz)
+	if (cur_write_sz <= pending_sz)
 		return;
 
 	cached_write_sz = hv_get_cached_bytes_to_write(rbi);
-	if (cached_write_sz < pending_sz)
+	if (cached_write_sz <= pending_sz)
 		vmbus_setevent(channel);
 
 	return;
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
index c6587c0..6638a22 100644
--- a/include/linux/if_bridge.h
+++ b/include/linux/if_bridge.h
@@ -53,6 +53,8 @@
 
 typedef int br_should_route_hook_t(struct sk_buff *skb);
 extern br_should_route_hook_t __rcu *br_should_route_hook;
+extern struct net_device *br_port_dev_get(struct net_device *dev,
+						unsigned char *addr);
 
 #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_IGMP_SNOOPING)
 int br_multicast_list_adjacent(struct net_device *dev,
diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h
index e7fdec4..6cc48ac 100644
--- a/include/linux/iio/adc/ad_sigma_delta.h
+++ b/include/linux/iio/adc/ad_sigma_delta.h
@@ -111,6 +111,9 @@
 int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
 	unsigned int size, unsigned int *val);
 
+int ad_sd_reset(struct ad_sigma_delta *sigma_delta,
+	unsigned int reset_length);
+
 int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
 	const struct iio_chan_spec *chan, int *val);
 int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 0b8aedf..f25acfc 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -19,12 +19,12 @@
 #ifndef __LINUX_IOMMU_H
 #define __LINUX_IOMMU_H
 
+#include <linux/scatterlist.h>
+#include <linux/device.h>
+#include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/err.h>
 #include <linux/of.h>
-#include <linux/types.h>
-#include <linux/scatterlist.h>
-#include <trace/events/iommu.h>
 
 #define IOMMU_READ	(1 << 0)
 #define IOMMU_WRITE	(1 << 1)
@@ -32,6 +32,7 @@
 #define IOMMU_NOEXEC	(1 << 3)
 #define IOMMU_MMIO	(1 << 4) /* e.g. things like MSI doorbells */
 #define IOMMU_PRIV	(1 << 5)
+#define IOMMU_GUARD	(1 << 28) /* Guard Page */
 /* Use upstream device's bus attribute */
 #define IOMMU_USE_UPSTREAM_HINT	(1 << 6)
 
@@ -87,6 +88,8 @@
 #define IOMMU_DOMAIN_DMA	(__IOMMU_DOMAIN_PAGING |	\
 				 __IOMMU_DOMAIN_DMA_API)
 
+
+#define IOMMU_DOMAIN_NAME_LEN 32
 struct iommu_domain {
 	unsigned type;
 	const struct iommu_ops *ops;
@@ -95,6 +98,7 @@
 	void *handler_token;
 	struct iommu_domain_geometry geometry;
 	void *iova_cookie;
+	char name[IOMMU_DOMAIN_NAME_LEN];
 };
 
 enum iommu_cap {
@@ -142,6 +146,8 @@
 	DOMAIN_ATTR_PAGE_TABLE_IS_COHERENT,
 	DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
 	DOMAIN_ATTR_CB_STALL_DISABLE,
+	DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR,
+	DOMAIN_ATTR_MMU500_ERRATA_MIN_ALIGN,
 	DOMAIN_ATTR_MAX,
 };
 
@@ -238,10 +244,6 @@
 	/* Get the number of windows per domain */
 	u32 (*domain_get_windows)(struct iommu_domain *domain);
 	void (*trigger_fault)(struct iommu_domain *domain, unsigned long flags);
-	unsigned long (*reg_read)(struct iommu_domain *domain,
-				  unsigned long offset);
-	void (*reg_write)(struct iommu_domain *domain, unsigned long val,
-			  unsigned long offset);
 	void (*tlbi_domain)(struct iommu_domain *domain);
 	int (*enable_config_clocks)(struct iommu_domain *domain);
 	void (*disable_config_clocks)(struct iommu_domain *domain);
@@ -278,6 +280,9 @@
 		     phys_addr_t paddr, size_t size, int prot);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
 		       size_t size);
+extern size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
+				struct scatterlist *sg, unsigned int nents,
+				int prot);
 extern size_t default_iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
 				struct scatterlist *sg,unsigned int nents,
 				int prot);
@@ -337,58 +342,9 @@
 
 extern uint64_t iommu_iova_to_pte(struct iommu_domain *domain,
 	    dma_addr_t iova);
-/**
- * report_iommu_fault() - report about an IOMMU fault to the IOMMU framework
- * @domain: the iommu domain where the fault has happened
- * @dev: the device where the fault has happened
- * @iova: the faulting address
- * @flags: mmu fault flags (e.g. IOMMU_FAULT_READ/IOMMU_FAULT_WRITE/...)
- *
- * This function should be called by the low-level IOMMU implementations
- * whenever IOMMU faults happen, to allow high-level users, that are
- * interested in such events, to know about them.
- *
- * This event may be useful for several possible use cases:
- * - mere logging of the event
- * - dynamic TLB/PTE loading
- * - if restarting of the faulting device is required
- *
- * Returns 0 on success and an appropriate error code otherwise (if dynamic
- * PTE/TLB loading will one day be supported, implementations will be able
- * to tell whether it succeeded or not according to this return value).
- *
- * Specifically, -ENOSYS is returned if a fault handler isn't installed
- * (though fault handlers can also return -ENOSYS, in case they want to
- * elicit the default behavior of the IOMMU drivers).
 
- * Client fault handler returns -EBUSY to signal to the IOMMU driver
- * that the client will take responsibility for any further fault
- * handling, including clearing fault status registers or retrying
- * the faulting transaction.
- */
-static inline int report_iommu_fault(struct iommu_domain *domain,
-		struct device *dev, unsigned long iova, int flags)
-{
-	int ret = -ENOSYS;
-
-	/*
-	 * if upper layers showed interest and installed a fault handler,
-	 * invoke it.
-	 */
-	if (domain->handler)
-		ret = domain->handler(domain, dev, iova, flags,
-						domain->handler_token);
-
-	trace_io_page_fault(dev, iova, flags);
-	return ret;
-}
-
-static inline size_t iommu_map_sg(struct iommu_domain *domain,
-				  unsigned long iova, struct scatterlist *sg,
-				  unsigned int nents, int prot)
-{
-	return domain->ops->map_sg(domain, iova, sg, nents, prot);
-}
+extern int report_iommu_fault(struct iommu_domain *domain, struct device *dev,
+			      unsigned long iova, int flags);
 
 extern void iommu_trigger_fault(struct iommu_domain *domain,
 				unsigned long flags);
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index f8e7e8c..dd6849d 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -40,8 +40,8 @@
 };
 
 /**
-* enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point
-*/
+ * enum ipa_ipv6ct_en_type - IPv6CT setting type in IPA end-point
+ */
 enum ipa_ipv6ct_en_type {
 	IPA_BYPASS_IPV6CT,
 	IPA_ENABLE_IPV6CT,
@@ -130,7 +130,7 @@
  * struct ipa_ep_cfg_conn_track - IPv6 Connection tracking configuration in
  *	IPA end-point
  * @conn_track_en: Defines speculative conn_track action, means if specific
- *		   pipe needs to have UL/DL IPv6 Connection Tracking or Bybass
+ *		   pipe needs to have UL/DL IPv6 Connection Tracking or Bypass
  *		   IPv6 Connection Tracking. 0: Bypass IPv6 Connection Tracking
  *					     1: IPv6 UL/DL Connection Tracking.
  *		  Valid for Input Pipes only (IPA consumer)
@@ -407,8 +407,8 @@
 
 /**
  * struct ipa_ep_cfg - configuration of IPA end-point
- * @nat:		NAT parmeters
- * @conn_track:		IPv6CT parmeters
+ * @nat:		NAT parameters
+ * @conn_track:		IPv6CT parameters
  * @hdr:		Header parameters
  * @hdr_ext:		Extended header parameters
  * @mode:		Mode parameters
@@ -1165,6 +1165,16 @@
 	int ee;
 };
 
+/**
+ * struct ipa_tz_unlock_reg_info - Used in order unlock regions of memory by TZ
+ * @reg_addr - Physical address of the start of the region
+ * @size - Size of the region in bytes
+ */
+struct ipa_tz_unlock_reg_info {
+	u64 reg_addr;
+	u64 size;
+};
+
 #if defined CONFIG_IPA || defined CONFIG_IPA3
 
 /*
@@ -1282,15 +1292,24 @@
 int ipa_reset_flt(enum ipa_ip_type ip);
 
 /*
- * NAT
+ * NAT\IPv6CT
  */
-int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem);
+int ipa_allocate_nat_table(struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
+int ipa_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc);
 
 int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init);
+int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init);
 
 int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
+int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma);
 
 int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del);
+int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+int ipa_del_ipv6ct_table(struct ipa_ioc_nat_ipv6ct_table_del *del);
+
+int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn);
 
 /*
  * Messaging
@@ -1530,6 +1549,21 @@
 int ipa_register_ipa_ready_cb(void (*ipa_ready_cb)(void *user_data),
 			      void *user_data);
 
+/**
+ * ipa_tz_unlock_reg - Unlocks memory regions so that they become accessible
+ *	from AP.
+ * @reg_info - Pointer to array of memory regions to unlock
+ * @num_regs - Number of elements in the array
+ *
+ * Converts the input array of regions to a struct that TZ understands and
+ * issues an SCM call.
+ * Also flushes the memory cache to DDR in order to make sure that TZ sees the
+ * correct data structure.
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info, u16 num_regs);
+
 #else /* (CONFIG_IPA || CONFIG_IPA3) */
 
 /*
@@ -1776,29 +1810,64 @@
 /*
  * NAT
  */
-static inline int allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
+static inline int ipa_allocate_nat_device(struct ipa_ioc_nat_alloc_mem *mem)
 {
 	return -EPERM;
 }
 
+static inline int ipa_allocate_nat_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	return -EPERM;
+}
+
+static inline int ipa_allocate_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_alloc *table_alloc)
+{
+	return -EPERM;
+}
 
 static inline int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
 {
 	return -EPERM;
 }
 
+static inline int ipa_ipv6ct_init_cmd(struct ipa_ioc_ipv6ct_init *init)
+{
+	return -EPERM;
+}
 
 static inline int ipa_nat_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
 {
 	return -EPERM;
 }
 
+static inline int ipa_table_dma_cmd(struct ipa_ioc_nat_dma_cmd *dma)
+{
+	return -EPERM;
+}
 
 static inline int ipa_nat_del_cmd(struct ipa_ioc_v4_nat_del *del)
 {
 	return -EPERM;
 }
 
+static inline int ipa_del_nat_table(struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	return -EPERM;
+}
+
+static inline int ipa_del_ipv6ct_table(
+	struct ipa_ioc_nat_ipv6ct_table_del *del)
+{
+	return -EPERM;
+}
+
+static inline int ipa_nat_mdfy_pdn(struct ipa_ioc_nat_pdn_entry *mdfy_pdn)
+{
+	return -EPERM;
+}
+
 /*
  * Messaging
  */
@@ -2276,6 +2345,12 @@
 	return -EPERM;
 }
 
+static inline int ipa_tz_unlock_reg(struct ipa_tz_unlock_reg_info *reg_info,
+	u16 num_regs)
+{
+	return -EPERM;
+}
+
 #endif /* (CONFIG_IPA || CONFIG_IPA3) */
 
 #endif /* _IPA_H_ */
diff --git a/include/linux/ipa_wdi3.h b/include/linux/ipa_wdi3.h
new file mode 100644
index 0000000..aed8c59
--- /dev/null
+++ b/include/linux/ipa_wdi3.h
@@ -0,0 +1,254 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _IPA_WDI3_H_
+#define _IPA_WDI3_H_
+
+#include <linux/ipa.h>
+
+#define IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE 32
+#define IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE 8
+
+#define IPA_HW_WDI3_MAX_ER_DESC_SIZE \
+	(((IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE) > \
+	(IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE)) ?  \
+	(IPA_HW_WDI3_TCL_DATA_CMD_ER_DESC_SIZE) : \
+	(IPA_HW_WDI3_IPA2FW_ER_DESC_SIZE))
+
+/**
+ * struct ipa_wdi3_hdr_info - Header to install on IPA HW
+ *
+ * @hdr: header to install on IPA HW
+ * @hdr_len: length of header
+ * @dst_mac_addr_offset: destination mac address offset
+ * @hdr_type: layer two header type
+ */
+struct ipa_wdi3_hdr_info {
+	u8 *hdr;
+	u8 hdr_len;
+	u8 dst_mac_addr_offset;
+	enum ipa_hdr_l2_type hdr_type;
+};
+
+/**
+ * struct ipa_wdi3_reg_intf_in_params - parameters for uC offload
+ *	interface registration
+ *
+ * @netdev_name: network interface name
+ * @hdr_info: header information
+ * @is_meta_data_valid: if meta data is valid
+ * @meta_data: meta data if any
+ * @meta_data_mask: meta data mask
+ */
+struct ipa_wdi3_reg_intf_in_params {
+	const char *netdev_name;
+	struct ipa_wdi3_hdr_info hdr_info[IPA_IP_MAX];
+	u8 is_meta_data_valid;
+	u32 meta_data;
+	u32 meta_data_mask;
+};
+
+/**
+ * struct  ipa_wdi3_setup_info - WDI3 TX/Rx configuration
+ * @ipa_ep_cfg: ipa endpoint configuration
+ * @client: type of "client"
+ * @transfer_ring_base_pa:  physical address of the base of the transfer ring
+ * @transfer_ring_size:  size of the transfer ring
+ * @transfer_ring_doorbell_pa:  physical address of the doorbell that
+	IPA uC will update the tailpointer of the transfer ring
+ * @event_ring_base_pa:  physical address of the base of the event ring
+ * @event_ring_size:  event ring size
+ * @event_ring_doorbell_pa:  physical address of the doorbell that IPA uC
+	will update the headpointer of the event ring
+ * @num_pkt_buffers:  Number of pkt buffers allocated. The size of the event
+	ring and the transfer ring has to be atleast ( num_pkt_buffers + 1)
+ * @pkt_offset: packet offset (wdi3 header length)
+ * @desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE]:  Holds a cached
+	template of the desc format
+ */
+struct ipa_wdi3_setup_info {
+	struct ipa_ep_cfg ipa_ep_cfg;
+	enum ipa_client_type client;
+	dma_addr_t  transfer_ring_base_pa;
+	u32  transfer_ring_size;
+	dma_addr_t  transfer_ring_doorbell_pa;
+
+	dma_addr_t  event_ring_base_pa;
+	u32  event_ring_size;
+	dma_addr_t  event_ring_doorbell_pa;
+	u16  num_pkt_buffers;
+
+	u16 pkt_offset;
+
+	u32  desc_format_template[IPA_HW_WDI3_MAX_ER_DESC_SIZE];
+};
+
+/**
+ * struct  ipa_wdi3_conn_in_params - information provided by
+ *		uC offload client
+ * @notify: client callback function
+ * @priv: client cookie
+ * @tx: parameters to connect TX pipe(from IPA to WLAN)
+ * @rx: parameters to connect RX pipe(from WLAN to IPA)
+ */
+struct ipa_wdi3_conn_in_params {
+	ipa_notify_cb notify;
+	void *priv;
+	struct ipa_wdi3_setup_info tx;
+	struct ipa_wdi3_setup_info rx;
+};
+
+/**
+ * struct  ipa_wdi3_conn_out_params - information provided
+ *				to WLAN driver
+ * @tx_uc_db_pa: physical address of IPA uC doorbell for TX
+ * @tx_uc_db_va: virtual address of IPA uC doorbell for TX
+ * @rx_uc_db_pa: physical address of IPA uC doorbell for RX
+ */
+struct ipa_wdi3_conn_out_params {
+	dma_addr_t tx_uc_db_pa;
+	void __iomem *tx_uc_db_va;
+	dma_addr_t rx_uc_db_pa;
+};
+
+/**
+ * struct  ipa_wdi3_perf_profile - To set BandWidth profile
+ *
+ * @client: type of client
+ * @max_supported_bw_mbps: maximum bandwidth needed (in Mbps)
+ */
+struct ipa_wdi3_perf_profile {
+	enum ipa_client_type client;
+	u32 max_supported_bw_mbps;
+};
+
+#if defined CONFIG_IPA || defined CONFIG_IPA3
+
+/**
+ * ipa_wdi3_reg_intf - Client should call this function to
+ * init WDI3 IPA offload data path
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_reg_intf(
+	struct ipa_wdi3_reg_intf_in_params *in);
+
+/**
+ * ipa_wdi3_dereg_intf - Client Driver should call this
+ * function to deregister before unload and after disconnect
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_dereg_intf(const char *netdev_name);
+
+/**
+ * ipa_wdi3_conn_pipes - Client should call this
+ * function to connect pipes
+ *
+ * @in:	[in] input parameters from client
+ * @out: [out] output params to client
+ *
+ * Note: Should not be called from atomic context and only
+ * after checking IPA readiness using ipa_register_ipa_ready_cb()
+ *
+ * @Return 0 on success, negative on failure
+ */
+int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+			struct ipa_wdi3_conn_out_params *out);
+
+/**
+ * ipa_wdi3_disconn_pipes() - Client should call this
+ *		function to disconnect pipes
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_disconn_pipes(void);
+
+/**
+ * ipa_wdi3_enable_pipes() - Client should call this
+ *		function to enable IPA offload data path
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_enable_pipes(void);
+
+/**
+ * ipa_wdi3_disable_pipes() - Client should call this
+ *		function to disable IPA offload data path
+ *
+ * Note: Should not be called from atomic context
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_disable_pipes(void);
+
+/**
+ * ipa_wdi3_set_perf_profile() - Client should call this function to
+ *		set IPA clock bandwidth based on data rates
+ *
+ * @profile: [in] BandWidth profile to use
+ *
+ * Returns: 0 on success, negative on failure
+ */
+int ipa_wdi3_set_perf_profile(struct ipa_wdi3_perf_profile *profile);
+
+
+#else /* (CONFIG_IPA || CONFIG_IPA3) */
+
+static inline int ipa_wdi3_reg_intf(
+	struct ipa_wdi3_reg_intf_in_params *in)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_dereg_intf(const char *netdev_name)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_conn_pipes(struct ipa_wdi3_conn_in_params *in,
+			struct ipa_wdi3_conn_out_params *out)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_disconn_pipes(void)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_enable_pipes(void)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_disable_pipes(void)
+{
+	return -EPERM;
+}
+
+static inline int ipa_wdi3_set_perf_profile(
+	struct ipa_wdi3_perf_profile *profile)
+{
+	return -EPERM;
+}
+
+#endif /* CONFIG_IPA3 */
+
+#endif /* _IPA_WDI3_H_ */
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index b7e3431..c122409 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -450,6 +450,8 @@
 	return !!(val & ICC_SRE_EL1_SRE);
 }
 
+void gic_show_pending_irqs(void);
+unsigned int get_gic_highpri_irq(void);
 #endif
 
 #endif
diff --git a/include/linux/key.h b/include/linux/key.h
index 7229147..ed9b44f 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -126,6 +126,11 @@
 	return (unsigned long) key_ref & 1UL;
 }
 
+enum key_state {
+	KEY_IS_UNINSTANTIATED,
+	KEY_IS_POSITIVE,		/* Positively instantiated */
+};
+
 /*****************************************************************************/
 /*
  * authentication token / access credential / keyring
@@ -157,6 +162,7 @@
 						 * - may not match RCU dereferenced payload
 						 * - payload should contain own length
 						 */
+	short			state;		/* Key state (+) or rejection error (-) */
 
 #ifdef KEY_DEBUGGING
 	unsigned		magic;
@@ -165,17 +171,16 @@
 #endif
 
 	unsigned long		flags;		/* status flags (change with bitops) */
-#define KEY_FLAG_INSTANTIATED	0	/* set if key has been instantiated */
-#define KEY_FLAG_DEAD		1	/* set if key type has been deleted */
-#define KEY_FLAG_REVOKED	2	/* set if key had been revoked */
-#define KEY_FLAG_IN_QUOTA	3	/* set if key consumes quota */
-#define KEY_FLAG_USER_CONSTRUCT	4	/* set if key is being constructed in userspace */
-#define KEY_FLAG_NEGATIVE	5	/* set if key is negative */
-#define KEY_FLAG_ROOT_CAN_CLEAR	6	/* set if key can be cleared by root without permission */
-#define KEY_FLAG_INVALIDATED	7	/* set if key has been invalidated */
-#define KEY_FLAG_BUILTIN	8	/* set if key is built in to the kernel */
-#define KEY_FLAG_ROOT_CAN_INVAL	9	/* set if key can be invalidated by root without permission */
-#define KEY_FLAG_KEEP		10	/* set if key should not be removed */
+#define KEY_FLAG_DEAD		0	/* set if key type has been deleted */
+#define KEY_FLAG_REVOKED	1	/* set if key had been revoked */
+#define KEY_FLAG_IN_QUOTA	2	/* set if key consumes quota */
+#define KEY_FLAG_USER_CONSTRUCT	3	/* set if key is being constructed in userspace */
+#define KEY_FLAG_ROOT_CAN_CLEAR	4	/* set if key can be cleared by root without permission */
+#define KEY_FLAG_INVALIDATED	5	/* set if key has been invalidated */
+#define KEY_FLAG_BUILTIN	6	/* set if key is built in to the kernel */
+#define KEY_FLAG_ROOT_CAN_INVAL	7	/* set if key can be invalidated by root without permission */
+#define KEY_FLAG_KEEP		8	/* set if key should not be removed */
+#define KEY_FLAG_UID_KEYRING	9	/* set if key is a user or user session keyring */
 
 	/* the key type and key description string
 	 * - the desc is used to match a key against search criteria
@@ -201,7 +206,6 @@
 			struct list_head name_link;
 			struct assoc_array keys;
 		};
-		int reject_error;
 	};
 
 	/* This is set on a keyring to restrict the addition of a link to a key
@@ -235,6 +239,7 @@
 #define KEY_ALLOC_NOT_IN_QUOTA		0x0002	/* not in quota */
 #define KEY_ALLOC_BUILT_IN		0x0004	/* Key is built into kernel */
 #define KEY_ALLOC_BYPASS_RESTRICTION	0x0008	/* Override the check on restricted keyrings */
+#define KEY_ALLOC_UID_KEYRING		0x0010	/* allocating a user or user session keyring */
 
 extern void key_revoke(struct key *key);
 extern void key_invalidate(struct key *key);
@@ -341,17 +346,27 @@
 #define	KEY_NEED_SETATTR 0x20	/* Require permission to change attributes */
 #define	KEY_NEED_ALL	0x3f	/* All the above permissions */
 
+static inline short key_read_state(const struct key *key)
+{
+	/* Barrier versus mark_key_instantiated(). */
+	return smp_load_acquire(&key->state);
+}
+
 /**
- * key_is_instantiated - Determine if a key has been positively instantiated
+ * key_is_positive - Determine if a key has been positively instantiated
  * @key: The key to check.
  *
  * Return true if the specified key has been positively instantiated, false
  * otherwise.
  */
-static inline bool key_is_instantiated(const struct key *key)
+static inline bool key_is_positive(const struct key *key)
 {
-	return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
-		!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
+	return key_read_state(key) == KEY_IS_POSITIVE;
+}
+
+static inline bool key_is_negative(const struct key *key)
+{
+	return key_read_state(key) < 0;
 }
 
 #define rcu_dereference_key(KEY)					\
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 8f5af30..580cc10 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1419,6 +1419,8 @@
 					size_t *len);
 	int (*inode_create)(struct inode *dir, struct dentry *dentry,
 				umode_t mode);
+	int (*inode_post_create)(struct inode *dir, struct dentry *dentry,
+				umode_t mode);
 	int (*inode_link)(struct dentry *old_dentry, struct inode *dir,
 				struct dentry *new_dentry);
 	int (*inode_unlink)(struct inode *dir, struct dentry *dentry);
@@ -1706,6 +1708,7 @@
 	struct list_head inode_free_security;
 	struct list_head inode_init_security;
 	struct list_head inode_create;
+	struct list_head inode_post_create;
 	struct list_head inode_link;
 	struct list_head inode_unlink;
 	struct list_head inode_symlink;
diff --git a/include/linux/mailbox_client.h b/include/linux/mailbox_client.h
index 86a2dc6..073391b 100644
--- a/include/linux/mailbox_client.h
+++ b/include/linux/mailbox_client.h
@@ -44,7 +44,7 @@
 					      const char *name);
 struct mbox_chan *mbox_request_channel(struct mbox_client *cl, int index);
 int mbox_send_message(struct mbox_chan *chan, void *mssg);
-int mbox_send_controller_data(struct mbox_chan *chan, void *mssg);
+int mbox_write_controller_data(struct mbox_chan *chan, void *mssg);
 void mbox_client_txdone(struct mbox_chan *chan, int r); /* atomic */
 bool mbox_client_peek_data(struct mbox_chan *chan); /* atomic */
 void mbox_free_channel(struct mbox_chan *chan); /* may sleep */
diff --git a/include/linux/mailbox_controller.h b/include/linux/mailbox_controller.h
index 7827c68..751b354 100644
--- a/include/linux/mailbox_controller.h
+++ b/include/linux/mailbox_controller.h
@@ -24,8 +24,8 @@
  *		transmission of data is reported by the controller via
  *		mbox_chan_txdone (if it has some TX ACK irq). It must not
  *		sleep.
- * @send_controller_data:
- *		Send data for the controller driver. This could be data to
+ * @write_controller_data:
+ *		Write data for the controller driver. This could be data to
  *		configure the controller or data that may be cached in the
  *		controller and not transmitted immediately. There is no ACK
  *		for this request and the request is not buffered in the
@@ -54,7 +54,7 @@
  */
 struct mbox_chan_ops {
 	int (*send_data)(struct mbox_chan *chan, void *data);
-	int (*send_controller_data)(struct mbox_chan *chan, void *data);
+	int (*write_controller_data)(struct mbox_chan *chan, void *data);
 	int (*startup)(struct mbox_chan *chan);
 	void (*shutdown)(struct mbox_chan *chan);
 	bool (*last_tx_done)(struct mbox_chan *chan);
diff --git a/include/linux/mbus.h b/include/linux/mbus.h
index 2931aa4..f70420e 100644
--- a/include/linux/mbus.h
+++ b/include/linux/mbus.h
@@ -31,8 +31,8 @@
 	struct mbus_dram_window {
 		u8	cs_index;
 		u8	mbus_attr;
-		u32	base;
-		u32	size;
+		u64	base;
+		u64	size;
 	} cs[4];
 };
 
diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index 37e5178..600c905 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -64,6 +64,7 @@
 #ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
 #define __init_memblock __meminit
 #define __initdata_memblock __meminitdata
+void memblock_discard(void);
 #else
 #define __init_memblock
 #define __initdata_memblock
@@ -77,8 +78,6 @@
 					int nid, ulong flags);
 phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end,
 				   phys_addr_t size, phys_addr_t align);
-phys_addr_t get_allocated_memblock_reserved_regions_info(phys_addr_t *addr);
-phys_addr_t get_allocated_memblock_memory_regions_info(phys_addr_t *addr);
 void memblock_allow_resize(void);
 int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid);
 int memblock_add(phys_addr_t base, phys_addr_t size);
@@ -114,6 +113,9 @@
 void __next_reserved_mem_region(u64 *idx, phys_addr_t *out_start,
 				phys_addr_t *out_end);
 
+void __memblock_free_early(phys_addr_t base, phys_addr_t size);
+void __memblock_free_late(phys_addr_t base, phys_addr_t size);
+
 /**
  * for_each_mem_range - iterate through memblock areas from type_a and not
  * included in type_b. Or just type_a if type_b is NULL.
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 3139ea4..5942478 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -515,6 +515,10 @@
 	 */
 	bool tlb_flush_pending;
 #endif
+#ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
+	/* See flush_tlb_batched_pending() */
+	bool tlb_flush_batched;
+#endif
 	struct uprobes_state uprobes_state;
 #ifdef CONFIG_X86_INTEL_MPX
 	/* address of the bounds directory */
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 72dd7ba..7425430 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -436,7 +436,6 @@
 	struct mmc_wr_pack_stats wr_pack_stats; /* packed commands stats*/
 	struct notifier_block        reboot_notify;
 	enum mmc_pon_type pon_type;
-	u8 *cached_ext_csd;
 	bool cmdq_init;
 	struct mmc_bkops_info bkops;
 	bool err_in_sdr104;
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index b718105..c5a4a25 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -180,6 +180,7 @@
 extern int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
 extern int mmc_set_auto_bkops(struct mmc_card *card, bool enable);
 extern int mmc_suspend_clk_scaling(struct mmc_host *host);
+extern void mmc_flush_detect_work(struct mmc_host *host);
 
 #define MMC_ERASE_ARG		0x00000000
 #define MMC_SECURE_ERASE_ARG	0x80000000
@@ -233,7 +234,7 @@
 	bool lock_needed);
 extern void mmc_cmdq_clk_scaling_stop_busy(struct mmc_host *host,
 	bool lock_needed, bool is_cmdq_dcmd);
-extern void mmc_recovery_fallback_lower_speed(struct mmc_host *host);
+extern int mmc_recovery_fallback_lower_speed(struct mmc_host *host);
 
 /**
  *	mmc_claim_host - exclusively claim a host
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index f214b0c..089c18b 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -96,6 +96,23 @@
 	MMC_LOAD_LOW,
 };
 
+enum {
+	MMC_ERR_CMD_TIMEOUT,
+	MMC_ERR_CMD_CRC,
+	MMC_ERR_DAT_TIMEOUT,
+	MMC_ERR_DAT_CRC,
+	MMC_ERR_AUTO_CMD,
+	MMC_ERR_ADMA,
+	MMC_ERR_TUNING,
+	MMC_ERR_CMDQ_RED,
+	MMC_ERR_CMDQ_GCE,
+	MMC_ERR_CMDQ_ICCE,
+	MMC_ERR_REQ_TIMEOUT,
+	MMC_ERR_CMDQ_REQ_TIMEOUT,
+	MMC_ERR_ICE_CFG,
+	MMC_ERR_MAX,
+};
+
 struct mmc_cmdq_host_ops {
 	int (*init)(struct mmc_host *host);
 	int (*enable)(struct mmc_host *host);
@@ -226,6 +243,7 @@
 	unsigned int		resp_arg;
 	unsigned int		dev_pend_tasks;
 	bool			resp_err;
+	bool			skip_err_handling;
 	int			tag; /* used for command queuing */
 	u8			ctx_id;
 };
@@ -543,6 +561,7 @@
 	unsigned int		bus_resume_flags;
 #define MMC_BUSRESUME_MANUAL_RESUME	(1 << 0)
 #define MMC_BUSRESUME_NEEDS_RESUME	(1 << 1)
+	bool ignore_bus_resume_flags;
 
 	unsigned int		sdio_irqs;
 	struct task_struct	*sdio_irq_thread;
@@ -561,6 +580,7 @@
 	struct dentry		*debugfs_root;
 
 	bool			err_occurred;
+	u32			err_stats[MMC_ERR_MAX];
 
 	struct mmc_async_req	*areq;		/* active async req */
 	struct mmc_context_info	context_info;	/* async synchronization info */
diff --git a/include/linux/mmc/sdio_func.h b/include/linux/mmc/sdio_func.h
index d0a69e7..f563bcf 100644
--- a/include/linux/mmc/sdio_func.h
+++ b/include/linux/mmc/sdio_func.h
@@ -61,7 +61,7 @@
 	unsigned int		state;		/* function state */
 #define SDIO_STATE_PRESENT	(1<<0)		/* present in sysfs */
 
-	u8			tmpbuf[4];	/* DMA:able scratch buffer */
+	u8			*tmpbuf;	/* DMA:able scratch buffer */
 
 	unsigned		num_info;	/* number of info strings */
 	const char		**info;		/* info strings */
diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h
index a1a210d..25c0dc3 100644
--- a/include/linux/mmu_notifier.h
+++ b/include/linux/mmu_notifier.h
@@ -419,6 +419,11 @@
 
 #else /* CONFIG_MMU_NOTIFIER */
 
+static inline int mm_has_notifiers(struct mm_struct *mm)
+{
+	return 0;
+}
+
 static inline void mmu_notifier_release(struct mm_struct *mm)
 {
 }
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ed0099c9..07e1acb 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -158,7 +158,6 @@
 	NR_UNEVICTABLE,		/*  "     "     "   "       "         */
 	NR_ISOLATED_ANON,	/* Temporary isolated pages from anon lru */
 	NR_ISOLATED_FILE,	/* Temporary isolated pages from file lru */
-	NR_PAGES_SCANNED,	/* pages scanned since last reclaim */
 	WORKINGSET_REFAULT,
 	WORKINGSET_ACTIVATE,
 	WORKINGSET_NODERECLAIM,
@@ -645,6 +644,8 @@
 	int kswapd_order;
 	enum zone_type kswapd_classzone_idx;
 
+	int kswapd_failures;		/* Number of 'reclaimed == 0' runs */
+
 #ifdef CONFIG_COMPACTION
 	int kcompactd_max_order;
 	enum zone_type kcompactd_classzone_idx;
diff --git a/include/linux/msm-bus.h b/include/linux/msm-bus.h
index c298666..a584e0a 100644
--- a/include/linux/msm-bus.h
+++ b/include/linux/msm-bus.h
@@ -130,6 +130,8 @@
 					uint32_t cl, unsigned int index);
 int msm_bus_scale_query_tcs_cmd_all(struct msm_bus_tcs_handle *tcs_handle,
 					uint32_t cl);
+int msm_bus_noc_throttle_wa(bool enable);
+int msm_bus_noc_priority_wa(bool enable);
 
 /* AXI Port configuration APIs */
 int msm_bus_axi_porthalt(int master_port);
@@ -211,6 +213,16 @@
 	return 0;
 }
 
+static inline int msm_bus_noc_throttle_wa(bool enable)
+{
+	return 0;
+}
+
+static inline int msm_bus_noc_priority_wa(bool enable)
+{
+	return 0;
+}
+
 #endif
 
 #if defined(CONFIG_OF) && defined(CONFIG_QCOM_BUS_SCALING)
diff --git a/include/linux/msm_bcl.h b/include/linux/msm_bcl.h
new file mode 100644
index 0000000..3b84f37
--- /dev/null
+++ b/include/linux/msm_bcl.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014, 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 __MSM_BCL_H
+#define __MSM_BCL_H
+
+#define BCL_NAME_MAX_LEN 20
+
+enum bcl_trip_type {
+	BCL_HIGH_TRIP,
+	BCL_LOW_TRIP,
+	BCL_TRIP_MAX,
+};
+
+enum bcl_param {
+	BCL_PARAM_VOLTAGE,
+	BCL_PARAM_CURRENT,
+	BCL_PARAM_MAX,
+};
+
+struct bcl_threshold {
+	int                     trip_value;
+	enum bcl_trip_type      type;
+	void                    *trip_data;
+	void (*trip_notify)     (enum bcl_trip_type, int, void *);
+};
+struct bcl_param_data;
+struct bcl_driver_ops {
+	int (*read)             (int *);
+	int (*set_high_trip)    (int);
+	int (*get_high_trip)    (int *);
+	int (*set_low_trip)     (int);
+	int (*get_low_trip)     (int *);
+	int (*disable)          (void);
+	int (*enable)           (void);
+	int (*notify)           (struct bcl_param_data *, int,
+					enum bcl_trip_type);
+};
+
+struct bcl_param_data {
+	char                    name[BCL_NAME_MAX_LEN];
+	struct device           device;
+	struct bcl_driver_ops   *ops;
+	int                     high_trip;
+	int                     low_trip;
+	int                     last_read_val;
+	bool                    registered;
+	struct kobj_attribute   val_attr;
+	struct kobj_attribute   high_trip_attr;
+	struct kobj_attribute   low_trip_attr;
+	struct attribute_group  bcl_attr_gp;
+	struct bcl_threshold    *thresh[BCL_TRIP_MAX];
+};
+
+#ifdef CONFIG_MSM_BCL_CTL
+struct bcl_param_data *msm_bcl_register_param(enum bcl_param,
+	struct bcl_driver_ops *, char *);
+int msm_bcl_unregister_param(struct bcl_param_data *);
+int msm_bcl_enable(void);
+int msm_bcl_disable(void);
+int msm_bcl_set_threshold(enum bcl_param, enum bcl_trip_type,
+	struct bcl_threshold *);
+int msm_bcl_read(enum bcl_param, int *);
+#else
+static inline struct bcl_param_data *msm_bcl_register_param(
+	enum bcl_param param_type, struct bcl_driver_ops *ops, char *name)
+{
+	return NULL;
+}
+static inline int msm_bcl_unregister_param(struct bcl_param_data *data)
+{
+	return -ENOSYS;
+}
+static inline int msm_bcl_enable(void)
+{
+	return -ENOSYS;
+}
+static inline int msm_bcl_disable(void)
+{
+	return -ENOSYS;
+}
+static inline int msm_bcl_set_threshold(enum bcl_param param_type,
+	enum bcl_trip_type type,
+	struct bcl_threshold *inp_thresh)
+{
+	return -ENOSYS;
+}
+static inline int msm_bcl_read(enum bcl_param param_type, int *vbat_value)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif /*__MSM_BCL_H*/
diff --git a/include/linux/msm_drm_notify.h b/include/linux/msm_drm_notify.h
new file mode 100644
index 0000000..924ba85
--- /dev/null
+++ b/include/linux/msm_drm_notify.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_DRM_NOTIFY_H_
+#define _MSM_DRM_NOTIFY_H_
+
+#include <linux/notifier.h>
+
+/* A hardware display blank change occurred */
+#define MSM_DRM_EVENT_BLANK			0x01
+/* A hardware display blank early change occurred */
+#define MSM_DRM_EARLY_EVENT_BLANK		0x02
+
+enum {
+	/* panel: power on */
+	MSM_DRM_BLANK_UNBLANK,
+	/* panel: power off */
+	MSM_DRM_BLANK_POWERDOWN,
+};
+
+enum msm_drm_display_id {
+	/* primary display */
+	MSM_DRM_PRIMARY_DISPLAY,
+	/* external display */
+	MSM_DRM_EXTERNAL_DISPLAY,
+	MSM_DRM_DISPLAY_MAX
+};
+
+struct msm_drm_notifier {
+	enum msm_drm_display_id id;
+	void *data;
+};
+
+int msm_drm_register_client(struct notifier_block *nb);
+int msm_drm_unregister_client(struct notifier_block *nb);
+#endif
diff --git a/include/linux/msm_ext_display.h b/include/linux/msm_ext_display.h
index 08e0def..e34f468 100644
--- a/include/linux/msm_ext_display.h
+++ b/include/linux/msm_ext_display.h
@@ -117,6 +117,7 @@
  *  @get_intf_id: id of connected interface
  *  @teardown_done: audio session teardown done by qdsp
  *  @acknowledge: acknowledge audio status received by user modules
+ *  @ready: notify audio when codec driver is ready.
  */
 struct msm_ext_disp_audio_codec_ops {
 	int (*audio_info_setup)(struct platform_device *pdev,
@@ -127,6 +128,7 @@
 	int (*get_intf_id)(struct platform_device *pdev);
 	void (*teardown_done)(struct platform_device *pdev);
 	int (*acknowledge)(struct platform_device *pdev, u32 ack);
+	int (*ready)(struct platform_device *pdev);
 };
 
 /**
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index ebca446..6e0b439 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -82,6 +82,8 @@
  * @irq:        IRQ number
  * @phys_addr:  physical address of GSI block
  * @size:       register size of GSI block
+ * @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
  * @req_clk_cb: callback to request peripheral clock
  *		granted should be set to true if request is completed
@@ -105,6 +107,8 @@
 	unsigned int irq;
 	phys_addr_t phys_addr;
 	unsigned long size;
+	bool mhi_er_id_limits_valid;
+	uint32_t mhi_er_id_limits[2];
 	void (*notify_cb)(struct gsi_per_notify *notify);
 	void (*req_clk_cb)(void *user_data, bool *granted);
 	int (*rel_clk_cb)(void *user_data);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d92d9a6..c92ed22 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2450,6 +2450,7 @@
 int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *newskb);
 int dev_queue_xmit(struct sk_buff *skb);
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv);
+int dev_queue_xmit_list(struct sk_buff *skb);
 int register_netdevice(struct net_device *dev);
 void unregister_netdevice_queue(struct net_device *dev, struct list_head *head);
 void unregister_netdevice_many(struct list_head *head);
@@ -3363,6 +3364,10 @@
 struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *dev);
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
 				    struct netdev_queue *txq, int *ret);
+struct sk_buff *dev_hard_start_xmit_list(struct sk_buff *skb,
+				struct net_device *dev,
+				struct netdev_queue *txq,
+				int *ret);
 int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 int dev_forward_skb(struct net_device *dev, struct sk_buff *skb);
 bool is_skb_forwardable(const struct net_device *dev,
@@ -3905,6 +3910,8 @@
 	     updev; \
 	     updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)))
 
+bool netdev_has_any_upper_dev(struct net_device *dev);
+
 void *netdev_lower_get_next_private(struct net_device *dev,
 				    struct list_head **iter);
 void *netdev_lower_get_next_private_rcu(struct net_device *dev,
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index beb1e10..3bf867a 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1199,7 +1199,7 @@
 
 struct nfs41_exchange_id_args {
 	struct nfs_client		*client;
-	nfs4_verifier			*verifier;
+	nfs4_verifier			verifier;
 	u32				flags;
 	struct nfs41_state_protection	state_protect;
 };
diff --git a/include/linux/nmi.h b/include/linux/nmi.h
index 3ca2526..4443a29 100644
--- a/include/linux/nmi.h
+++ b/include/linux/nmi.h
@@ -24,6 +24,9 @@
 #define NMI_WATCHDOG_ENABLED      (1 << NMI_WATCHDOG_ENABLED_BIT)
 #define SOFT_WATCHDOG_ENABLED     (1 << SOFT_WATCHDOG_ENABLED_BIT)
 
+DECLARE_PER_CPU(unsigned long, hrtimer_interrupts);
+DECLARE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
+
 /**
  * touch_nmi_watchdog - restart NMI watchdog timeout.
  *
@@ -31,8 +34,11 @@
  * may be used to reset the timeout - for code which intentionally
  * disables interrupts for a long time. This call is stateless.
  */
-#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR_NMI)
 #include <asm/nmi.h>
+#endif
+
+#if defined(CONFIG_HAVE_NMI_WATCHDOG) || defined(CONFIG_HARDLOCKUP_DETECTOR)
 extern void touch_nmi_watchdog(void);
 #else
 static inline void touch_nmi_watchdog(void)
@@ -130,6 +136,7 @@
 #define sysctl_softlockup_all_cpu_backtrace 0
 #define sysctl_hardlockup_all_cpu_backtrace 0
 #endif
+
 extern bool is_hardlockup(void);
 struct ctl_table;
 extern int proc_watchdog(struct ctl_table *, int ,
diff --git a/include/linux/oom.h b/include/linux/oom.h
index b986840..5b5d4c7 100644
--- a/include/linux/oom.h
+++ b/include/linux/oom.h
@@ -82,6 +82,8 @@
 extern void dump_tasks(struct mem_cgroup *memcg,
 		const nodemask_t *nodemask);
 
+extern void wake_oom_reaper(struct task_struct *tsk);
+
 /* sysctls */
 extern int sysctl_oom_dump_tasks;
 extern int sysctl_oom_kill_allocating_task;
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 3e5dbbe..4308204 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -574,6 +574,7 @@
 #define PCI_DEVICE_ID_AMD_CS5536_EHC    0x2095
 #define PCI_DEVICE_ID_AMD_CS5536_UDC    0x2096
 #define PCI_DEVICE_ID_AMD_CS5536_UOC    0x2097
+#define PCI_DEVICE_ID_AMD_CS5536_DEV_IDE    0x2092
 #define PCI_DEVICE_ID_AMD_CS5536_IDE    0x209A
 #define PCI_DEVICE_ID_AMD_LX_VIDEO  0x2081
 #define PCI_DEVICE_ID_AMD_LX_AES    0x2082
diff --git a/include/linux/pfk.h b/include/linux/pfk.h
new file mode 100644
index 0000000..82ee741
--- /dev/null
+++ b/include/linux/pfk.h
@@ -0,0 +1,57 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 PFK_H_
+#define PFK_H_
+
+#include <linux/bio.h>
+
+struct ice_crypto_setting;
+
+#ifdef CONFIG_PFK
+
+int pfk_load_key_start(const struct bio *bio,
+		struct ice_crypto_setting *ice_setting, bool *is_pfe, bool);
+int pfk_load_key_end(const struct bio *bio, bool *is_pfe);
+int pfk_remove_key(const unsigned char *key, size_t key_size);
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2);
+void pfk_clear_on_reset(void);
+
+#else
+static inline int pfk_load_key_start(const struct bio *bio,
+	struct ice_crypto_setting *ice_setting, bool *is_pfe, bool async)
+{
+	return -ENODEV;
+}
+
+static inline int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
+{
+	return -ENODEV;
+}
+
+static inline int pfk_remove_key(const unsigned char *key, size_t key_size)
+{
+	return -ENODEV;
+}
+
+static inline bool pfk_allow_merge_bio(const struct bio *bio1,
+		const struct bio *bio2)
+{
+	return true;
+}
+
+static inline void pfk_clear_on_reset(void)
+{}
+
+#endif /* CONFIG_PFK */
+
+#endif /* PFK_H */
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 8431c8c..a04d69a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -142,11 +142,7 @@
 /* Used when trying to connect to a specific phy (mii bus id:phy device id) */
 #define PHY_ID_FMT "%s:%02x"
 
-/*
- * Need to be a little smaller than phydev->dev.bus_id to leave room
- * for the ":%02x"
- */
-#define MII_BUS_ID_SIZE	(20 - 3)
+#define MII_BUS_ID_SIZE	61
 
 /* Or MII_ADDR_C45 into regnum for read/write on mii_bus to enable the 21 bit
    IEEE 802.3ae clause 45 addressing mode used by 10GIGE phy chips. */
@@ -602,7 +598,7 @@
 /* A Structure for boards to register fixups with the PHY Lib */
 struct phy_fixup {
 	struct list_head list;
-	char bus_id[20];
+	char bus_id[MII_BUS_ID_SIZE + 3];
 	u32 phy_uid;
 	u32 phy_uid_mask;
 	int (*run)(struct phy_device *phydev);
diff --git a/include/linux/platform_data/msm_serial_hs.h b/include/linux/platform_data/msm_serial_hs.h
new file mode 100644
index 0000000..72c76e5
--- /dev/null
+++ b/include/linux/platform_data/msm_serial_hs.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2010-2014, The Linux Foundation. All rights reserved.
+ * Author: Nick Pelly <npelly@google.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 __ASM_ARCH_MSM_SERIAL_HS_H
+#define __ASM_ARCH_MSM_SERIAL_HS_H
+
+#include <linux/serial_core.h>
+
+/**
+ * struct msm_serial_hs_platform_data - platform device data
+ *					for msm hsuart device
+ * @wakeup_irq : IRQ line to be configured as Wakeup source.
+ * @inject_rx_on_wakeup : Set 1 if specific character to be inserted on wakeup
+ * @rx_to_inject : Character to be inserted on wakeup
+ * @gpio_config : Configure gpios that are used for uart communication
+ * @userid : User-defined number to be used to enumerate device as tty<userid>
+ * @uart_tx_gpio: GPIO number for UART Tx Line.
+ * @uart_rx_gpio: GPIO number for UART Rx Line.
+ * @uart_cts_gpio: GPIO number for UART CTS Line.
+ * @uart_rfr_gpio: GPIO number for UART RFR Line.
+ * @bam_tx_ep_pipe_index : BAM TX Endpoint Pipe Index for HSUART
+ * @bam_tx_ep_pipe_index : BAM RX Endpoint Pipe Index for HSUART
+ * @no_suspend_delay : Flag used to make system go to suspend
+ * immediately or not
+ * @obs: Flag to enable out of band sleep feature support
+ */
+struct msm_serial_hs_platform_data {
+	int wakeup_irq;  /* wakeup irq */
+	bool inject_rx_on_wakeup;
+	u8 rx_to_inject;
+	int (*gpio_config)(int);
+	int userid;
+
+	int uart_tx_gpio;
+	int uart_rx_gpio;
+	int uart_cts_gpio;
+	int uart_rfr_gpio;
+	unsigned int bam_tx_ep_pipe_index;
+	unsigned int bam_rx_ep_pipe_index;
+	bool no_suspend_delay;
+	bool obs;
+};
+
+/* return true when tx is empty */
+unsigned int msm_hs_tx_empty(struct uart_port *uport);
+int msm_hs_request_clock_off(struct uart_port *uport);
+int msm_hs_request_clock_on(struct uart_port *uport);
+struct uart_port *msm_hs_get_uart_port(int port_index);
+void msm_hs_set_mctrl(struct uart_port *uport,
+				    unsigned int mctrl);
+#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 7c5b2b4..d253ca6 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -103,6 +103,9 @@
 	POWER_SUPPLY_DP_DM_HVDCP3_SUPPORTED = 10,
 	POWER_SUPPLY_DP_DM_ICL_DOWN = 11,
 	POWER_SUPPLY_DP_DM_ICL_UP = 12,
+	POWER_SUPPLY_DP_DM_FORCE_5V = 13,
+	POWER_SUPPLY_DP_DM_FORCE_9V = 14,
+	POWER_SUPPLY_DP_DM_FORCE_12V = 15,
 };
 
 enum {
@@ -112,6 +115,11 @@
 	POWER_SUPPLY_PL_USBMID_USBMID,
 };
 
+enum {
+	POWER_SUPPLY_CONNECTOR_TYPEC,
+	POWER_SUPPLY_CONNECTOR_MICRO_USB,
+};
+
 enum power_supply_property {
 	/* Properties of type `int' */
 	POWER_SUPPLY_PROP_STATUS = 0,
@@ -199,6 +207,8 @@
 	POWER_SUPPLY_PROP_LOW_POWER,
 	POWER_SUPPLY_PROP_COOL_TEMP,
 	POWER_SUPPLY_PROP_WARM_TEMP,
+	POWER_SUPPLY_PROP_COLD_TEMP,
+	POWER_SUPPLY_PROP_HOT_TEMP,
 	POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL,
 	POWER_SUPPLY_PROP_RESISTANCE,
 	POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE,
@@ -255,6 +265,7 @@
 	POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
 	POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
 	POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONNECTOR_TYPE,
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
 	/* Properties of type `const char *' */
diff --git a/include/linux/preempt.h b/include/linux/preempt.h
index 75e4e30..7eeceac 100644
--- a/include/linux/preempt.h
+++ b/include/linux/preempt.h
@@ -65,19 +65,24 @@
 
 /*
  * Are we doing bottom half or hardware interrupt processing?
- * Are we in a softirq context? Interrupt context?
- * in_softirq - Are we currently processing softirq or have bh disabled?
- * in_serving_softirq - Are we currently processing softirq?
+ *
+ * in_irq()       - We're in (hard) IRQ context
+ * in_softirq()   - We have BH disabled, or are processing softirqs
+ * in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
+ * in_serving_softirq() - We're in softirq context
+ * in_nmi()       - We're in NMI context
+ * in_task()	  - We're in task context
+ *
+ * Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really
+ *       should not be used in new code.
  */
 #define in_irq()		(hardirq_count())
 #define in_softirq()		(softirq_count())
 #define in_interrupt()		(irq_count())
 #define in_serving_softirq()	(softirq_count() & SOFTIRQ_OFFSET)
-
-/*
- * Are we in NMI context?
- */
-#define in_nmi()	(preempt_count() & NMI_MASK)
+#define in_nmi()		(preempt_count() & NMI_MASK)
+#define in_task()		(!(preempt_count() & \
+				   (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))
 
 /*
  * The preempt_count offset after preempt_disable();
diff --git a/include/linux/property.h b/include/linux/property.h
index 856e50b..338f9b7 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -33,6 +33,8 @@
 	DEV_DMA_COHERENT,
 };
 
+struct fwnode_handle *dev_fwnode(struct device *dev);
+
 bool device_property_present(struct device *dev, const char *propname);
 int device_property_read_u8_array(struct device *dev, const char *propname,
 				  u8 *val, size_t nval);
diff --git a/include/linux/ptr_ring.h b/include/linux/ptr_ring.h
index 6c70444..b83507c 100644
--- a/include/linux/ptr_ring.h
+++ b/include/linux/ptr_ring.h
@@ -340,9 +340,9 @@
 	__PTR_RING_PEEK_CALL_v; \
 })
 
-static inline void **__ptr_ring_init_queue_alloc(int size, gfp_t gfp)
+static inline void **__ptr_ring_init_queue_alloc(unsigned int size, gfp_t gfp)
 {
-	return kzalloc(ALIGN(size * sizeof(void *), SMP_CACHE_BYTES), gfp);
+	return kcalloc(size, sizeof(void *), gfp);
 }
 
 static inline int ptr_ring_init(struct ptr_ring *r, int size, gfp_t gfp)
@@ -417,7 +417,8 @@
  * In particular if you consume ring in interrupt or BH context, you must
  * disable interrupts/BH when doing so.
  */
-static inline int ptr_ring_resize_multiple(struct ptr_ring **rings, int nrings,
+static inline int ptr_ring_resize_multiple(struct ptr_ring **rings,
+					   unsigned int nrings,
 					   int size,
 					   gfp_t gfp, void (*destroy)(void *))
 {
@@ -425,7 +426,7 @@
 	void ***queues;
 	int i;
 
-	queues = kmalloc(nrings * sizeof *queues, gfp);
+	queues = kmalloc_array(nrings, sizeof(*queues), gfp);
 	if (!queues)
 		goto noqueues;
 
diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h
index aa4c1ed..fc02ece 100644
--- a/include/linux/qcom-geni-se.h
+++ b/include/linux/qcom-geni-se.h
@@ -117,6 +117,7 @@
 #define SE_HW_PARAM_0			(0xE24)
 #define SE_HW_PARAM_1			(0xE28)
 #define SE_DMA_GENERAL_CFG		(0xE30)
+#define SE_DMA_DEBUG_REG0		(0xE40)
 
 /* GENI_OUTPUT_CTRL fields */
 #define DEFAULT_IO_OUTPUT_CTRL_MSK	(GENMASK(6, 0))
@@ -546,6 +547,24 @@
 		       bool msb_to_lsb);
 
 /**
+ * se_geni_clks_off() - Turn off clocks associated with the serial
+ *                      engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_off(struct se_geni_rsc *rsc);
+
+/**
+ * se_geni_clks_on() - Turn on clocks associated with the serial
+ *                     engine
+ * @rsc:	Handle to resources associated with the serial engine.
+ *
+ * Return:	0 on success, standard Linux error codes on failure/error.
+ */
+int se_geni_clks_on(struct se_geni_rsc *rsc);
+
+/**
  * se_geni_resources_off() - Turn off resources associated with the serial
  *                           engine
  * @rsc:	Handle to resources associated with the serial engine.
@@ -736,6 +755,22 @@
 int geni_se_iommu_free_buf(struct device *wrapper_dev, dma_addr_t *iova,
 			   void *buf, size_t size);
 
+
+/**
+ * geni_se_dump_dbg_regs() - Print relevant registers that capture most
+ *			accurately the state of an SE; meant to be called
+ *			in case of errors to help debug.
+ * @_dev:		Pointer to the SE's device.
+ * @iomem:		Base address of the SE's register space.
+ * @ipc:		IPC log context handle.
+ *
+ * This function is used to print out all the registers that capture the state
+ * of an SE to help debug any errors.
+ *
+ * Return:	None
+ */
+void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base,
+				void *ipc);
 #else
 static inline unsigned int geni_read_reg_nolog(void __iomem *base, int offset)
 {
@@ -825,6 +860,16 @@
 {
 }
 
+static inline int se_geni_clks_on(struct se_geni_rsc *rsc)
+{
+	return -ENXIO;
+}
+
+static inline int se_geni_clks_off(struct se_geni_rsc *rsc)
+{
+	return -ENXIO;
+}
+
 static inline int se_geni_resources_on(struct se_geni_rsc *rsc)
 {
 	return -ENXIO;
@@ -907,5 +952,10 @@
 	return -ENXIO;
 }
 
+static void geni_se_dump_dbg_regs(struct se_geni_rsc *rsc, void __iomem *base,
+				void *ipc)
+{
+}
+
 #endif
 #endif
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
index 0e4586f..3e060d9 100644
--- a/include/linux/qpnp/qpnp-adc.h
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -968,6 +968,7 @@
  * @PMIC_THERM inputs the units in millidegC.
  */
 struct qpnp_adc_tm_btm_param {
+	uint32_t				full_scale_code;
 	int32_t					high_temp;
 	int32_t					low_temp;
 	int32_t					high_thr;
@@ -1026,13 +1027,13 @@
 /**
  * struct qpnp_adc_properties - Represent the ADC properties.
  * @adc_reference: Reference voltage for QPNP ADC.
- * @bitresolution: ADC bit resolution for QPNP ADC.
+ * @full_scale_code: Full scale value with intrinsic offset removed.
  * @biploar: Polarity for QPNP ADC.
  * @adc_hc: Represents using HC variant of the ADC controller.
  */
 struct qpnp_adc_properties {
 	uint32_t	adc_vdd_reference;
-	uint32_t	bitresolution;
+	uint32_t	full_scale_code;
 	bool		bipolar;
 	bool		adc_hc;
 };
diff --git a/include/linux/sched.h b/include/linux/sched.h
index e1345ec..0d4035a 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -184,6 +184,8 @@
 				     unsigned int *big_max_nr);
 extern unsigned int sched_get_cpu_util(int cpu);
 extern u64 sched_get_cpu_last_busy_time(int cpu);
+extern u32 sched_get_wake_up_idle(struct task_struct *p);
+extern int sched_set_wake_up_idle(struct task_struct *p, int wake_up_idle);
 #else
 static inline void sched_update_nr_prod(int cpu, long delta, bool inc)
 {
@@ -201,6 +203,15 @@
 {
 	return 0;
 }
+static inline u32 sched_get_wake_up_idle(struct task_struct *p)
+{
+	return 0;
+}
+static inline int sched_set_wake_up_idle(struct task_struct *p,
+					 int wake_up_idle)
+{
+	return 0;
+}
 #endif
 
 extern void calc_global_load(unsigned long ticks);
@@ -928,6 +939,16 @@
 
 #define SIGNAL_UNKILLABLE	0x00000040 /* for init: ignore fatal signals */
 
+#define SIGNAL_STOP_MASK (SIGNAL_CLD_MASK | SIGNAL_STOP_STOPPED | \
+			  SIGNAL_STOP_CONTINUED)
+
+static inline void signal_set_stop_flags(struct signal_struct *sig,
+					 unsigned int flags)
+{
+	WARN_ON(sig->flags & (SIGNAL_GROUP_EXIT|SIGNAL_GROUP_COREDUMP));
+	sig->flags = (sig->flags & ~SIGNAL_STOP_MASK) | flags;
+}
+
 /* If true, all threads except ->group_exit_task have pending SIGKILL */
 static inline int signal_group_exit(const struct signal_struct *sig)
 {
@@ -1936,6 +1957,7 @@
 #ifdef CONFIG_DETECT_HUNG_TASK
 /* hung task detection */
 	unsigned long last_switch_count;
+	bool hang_detection_enabled;
 #endif
 /* filesystem information */
 	struct fs_struct *fs;
@@ -2380,23 +2402,6 @@
 }
 
 static inline int pid_alive(const struct task_struct *p);
-static inline pid_t task_tgid_nr_ns(struct task_struct *tsk, struct pid_namespace *ns);
-static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_namespace *ns)
-{
-	pid_t pid = 0;
-
-	rcu_read_lock();
-	if (pid_alive(tsk))
-		pid = task_tgid_nr_ns(rcu_dereference(tsk->real_parent), ns);
-	rcu_read_unlock();
-
-	return pid;
-}
-
-static inline pid_t task_ppid_nr(const struct task_struct *tsk)
-{
-	return task_ppid_nr_ns(tsk, &init_pid_ns);
-}
 
 static inline pid_t task_pgrp_nr_ns(struct task_struct *tsk,
 					struct pid_namespace *ns)
@@ -2431,6 +2436,23 @@
 	return __task_pid_nr_ns(tsk, __PIDTYPE_TGID, NULL);
 }
 
+static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_namespace *ns)
+{
+	pid_t pid = 0;
+
+	rcu_read_lock();
+	if (pid_alive(tsk))
+		pid = task_tgid_nr_ns(rcu_dereference(tsk->real_parent), ns);
+	rcu_read_unlock();
+
+	return pid;
+}
+
+static inline pid_t task_ppid_nr(const struct task_struct *tsk)
+{
+	return task_ppid_nr_ns(tsk, &init_pid_ns);
+}
+
 /* obsolete, do not use */
 static inline pid_t task_pgrp_nr(struct task_struct *tsk)
 {
@@ -2688,6 +2710,7 @@
 
 extern int set_cpus_allowed_ptr(struct task_struct *p,
 				const struct cpumask *new_mask);
+extern bool cpupri_check_rt(void);
 #else
 static inline void do_set_cpus_allowed(struct task_struct *p,
 				      const struct cpumask *new_mask)
@@ -2700,6 +2723,10 @@
 		return -EINVAL;
 	return 0;
 }
+static inline bool cpupri_check_rt(void)
+{
+	return false;
+}
 #endif
 
 struct sched_load {
@@ -2708,9 +2735,6 @@
 	unsigned long predicted_load;
 };
 
-extern int sched_set_wake_up_idle(struct task_struct *p, int wake_up_idle);
-extern u32 sched_get_wake_up_idle(struct task_struct *p);
-
 struct cpu_cycle_counter_cb {
 	u64 (*get_cpu_cycle_counter)(int cpu);
 };
@@ -3950,6 +3974,7 @@
 #define SCHED_CPUFREQ_INTERCLUSTER_MIG (1U << 3)
 #define SCHED_CPUFREQ_WALT (1U << 4)
 #define SCHED_CPUFREQ_PL	(1U << 5)
+#define SCHED_CPUFREQ_EARLY_DET	(1U << 6)
 
 #define SCHED_CPUFREQ_RT_DL	(SCHED_CPUFREQ_RT | SCHED_CPUFREQ_DL)
 
@@ -3964,4 +3989,6 @@
 void cpufreq_remove_update_util_hook(int cpu);
 #endif /* CONFIG_CPU_FREQ */
 
+extern DEFINE_PER_CPU_READ_MOSTLY(int, sched_load_boost);
+
 #endif
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 3597d55..12bd032 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -6,6 +6,7 @@
 extern unsigned int  sysctl_hung_task_panic;
 extern unsigned long sysctl_hung_task_timeout_secs;
 extern int sysctl_hung_task_warnings;
+extern int sysctl_hung_task_selective_monitoring;
 extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write,
 					 void __user *buffer,
 					 size_t *lenp, loff_t *ppos);
diff --git a/include/linux/sde_rsc.h b/include/linux/sde_rsc.h
index 19e76db..cda2654 100644
--- a/include/linux/sde_rsc.h
+++ b/include/linux/sde_rsc.h
@@ -259,7 +259,8 @@
 
 static inline int sde_rsc_client_state_update(struct sde_rsc_client *client,
 	enum sde_rsc_state state,
-	struct sde_rsc_cmd_config *config, int crtc_id)
+	struct sde_rsc_cmd_config *config, int crtc_id,
+	int *wait_vblank_crtc_id)
 {
 	return 0;
 }
diff --git a/include/linux/security.h b/include/linux/security.h
index c2125e9..02e05de 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -30,6 +30,7 @@
 #include <linux/string.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/bio.h>
 
 struct linux_binprm;
 struct cred;
@@ -256,6 +257,8 @@
 				     const struct qstr *qstr, const char **name,
 				     void **value, size_t *len);
 int security_inode_create(struct inode *dir, struct dentry *dentry, umode_t mode);
+int security_inode_post_create(struct inode *dir, struct dentry *dentry,
+					umode_t mode);
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 			 struct dentry *new_dentry);
 int security_inode_unlink(struct inode *dir, struct dentry *dentry);
@@ -304,6 +307,7 @@
 				 struct fown_struct *fown, int sig);
 int security_file_receive(struct file *file);
 int security_file_open(struct file *file, const struct cred *cred);
+
 int security_task_create(unsigned long clone_flags);
 void security_task_free(struct task_struct *task);
 int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
@@ -637,6 +641,13 @@
 	return 0;
 }
 
+static inline int security_inode_post_create(struct inode *dir,
+					struct dentry *dentry,
+					umode_t mode)
+{
+	return 0;
+}
+
 static inline int security_inode_link(struct dentry *old_dentry,
 				       struct inode *dir,
 				       struct dentry *new_dentry)
diff --git a/include/linux/skb_array.h b/include/linux/skb_array.h
index f4dfade..be8b902 100644
--- a/include/linux/skb_array.h
+++ b/include/linux/skb_array.h
@@ -162,7 +162,8 @@
 }
 
 static inline int skb_array_resize_multiple(struct skb_array **rings,
-					    int nrings, int size, gfp_t gfp)
+					    int nrings, unsigned int size,
+					    gfp_t gfp)
 {
 	BUILD_BUG_ON(offsetof(struct skb_array, ring));
 	return ptr_ring_resize_multiple((struct ptr_ring **)rings,
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 32810f2..b1a09c5 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -738,7 +738,11 @@
 #endif
 	__u8			ipvs_property:1;
 	__u8			inner_protocol_type:1;
+	__u8			fast_forwarded:1;
 	__u8			remcsum_offload:1;
+
+	 /*4 or 6 bit hole */
+
 #ifdef CONFIG_NET_SWITCHDEV
 	__u8			offload_fwd_mark:1;
 #endif
diff --git a/include/linux/slab.h b/include/linux/slab.h
index 084b12b..4c53635 100644
--- a/include/linux/slab.h
+++ b/include/linux/slab.h
@@ -226,7 +226,7 @@
  * (PAGE_SIZE*2).  Larger requests are passed to the page allocator.
  */
 #define KMALLOC_SHIFT_HIGH	(PAGE_SHIFT + 1)
-#define KMALLOC_SHIFT_MAX	(MAX_ORDER + PAGE_SHIFT)
+#define KMALLOC_SHIFT_MAX	(MAX_ORDER + PAGE_SHIFT - 1)
 #ifndef KMALLOC_SHIFT_LOW
 #define KMALLOC_SHIFT_LOW	3
 #endif
@@ -239,7 +239,7 @@
  * be allocated from the same page.
  */
 #define KMALLOC_SHIFT_HIGH	PAGE_SHIFT
-#define KMALLOC_SHIFT_MAX	30
+#define KMALLOC_SHIFT_MAX	(MAX_ORDER + PAGE_SHIFT - 1)
 #ifndef KMALLOC_SHIFT_LOW
 #define KMALLOC_SHIFT_LOW	3
 #endif
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
index f1b1a7f..53af941 100644
--- a/include/linux/slimbus/slimbus.h
+++ b/include/linux/slimbus/slimbus.h
@@ -684,6 +684,7 @@
  *	first time it has reported present.
  *  @dev_list: List of devices on a controller
  *  @wd: Work structure associated with workqueue for presence notification
+ *  @device_reset: Work structure for device reset notification
  *  @sldev_reconf: Mutex to protect the pending data-channel lists.
  *  @pending_msgsl: Message bandwidth reservation request by this client in
  *	slots that's pending reconfiguration.
@@ -706,6 +707,7 @@
 	bool			notified;
 	struct list_head	dev_list;
 	struct work_struct	wd;
+	struct work_struct	device_reset;
 	struct mutex		sldev_reconf;
 	u32			pending_msgsl;
 	u32			cur_msgsl;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts b/include/linux/spi/spi-geni-qcom.h
similarity index 62%
copy from arch/arm64/boot/dts/qcom/sdm845-qvr.dts
copy to include/linux/spi/spi-geni-qcom.h
index c06b806..8aee88a 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dts
+++ b/include/linux/spi/spi-geni-qcom.h
@@ -1,4 +1,5 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -8,16 +9,15 @@
  * 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 __SPI_GENI_QCOM_HEADER___
+#define __SPI_GENI_QCOM_HEADER___
 
-/dts-v1/;
-
-#include "sdm845-v2.dtsi"
-#include "sdm845-qvr.dtsi"
-
-/ {
-	model = "Qualcomm Technologies, Inc. SDM845 QVR";
-	compatible = "qcom,sdm845-qvr", "qcom,sdm845", "qcom,qvr";
-	qcom,board-id = <0x01000B 0x20>;
+struct spi_geni_qcom_ctrl_data {
+	u32 spi_cs_clk_delay;
+	u32 spi_inter_words_delay;
 };
+
+#endif /*__SPI_GENI_QCOM_HEADER___*/
diff --git a/include/linux/stacktrace.h b/include/linux/stacktrace.h
index 0a34489..17a33f3 100644
--- a/include/linux/stacktrace.h
+++ b/include/linux/stacktrace.h
@@ -23,6 +23,8 @@
 extern int snprint_stack_trace(char *buf, size_t size,
 			struct stack_trace *trace, int spaces);
 
+#define BACKPORTED_EXPORT_SAVE_STACK_TRACE_TSK_ARM
+
 #ifdef CONFIG_USER_STACKTRACE_SUPPORT
 extern void save_stack_trace_user(struct stack_trace *trace);
 #else
diff --git a/include/linux/string.h b/include/linux/string.h
index 4e510df..0463dfb 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -178,17 +178,6 @@
 void __write_overflow(void) __compiletime_error("detected write beyond size of object passed as 1st parameter");
 
 #if !defined(__NO_FORTIFY) && defined(__OPTIMIZE__) && defined(CONFIG_FORTIFY_SOURCE)
-__FORTIFY_INLINE char *strcpy(char *p, const char *q)
-{
-	size_t p_size = __builtin_object_size(p, 0);
-	size_t q_size = __builtin_object_size(q, 0);
-	if (p_size == (size_t)-1 && q_size == (size_t)-1)
-		return __builtin_strcpy(p, q);
-	if (strscpy(p, q, p_size < q_size ? p_size : q_size) < 0)
-		fortify_panic(__func__);
-	return p;
-}
-
 __FORTIFY_INLINE char *strncpy(char *p, const char *q, __kernel_size_t size)
 {
 	size_t p_size = __builtin_object_size(p, 0);
@@ -367,6 +356,18 @@
 		fortify_panic(__func__);
 	return __real_kmemdup(p, size, gfp);
 }
+
+/* defined after fortified strlen and memcpy to reuse them */
+__FORTIFY_INLINE char *strcpy(char *p, const char *q)
+{
+	size_t p_size = __builtin_object_size(p, 0);
+	size_t q_size = __builtin_object_size(q, 0);
+	if (p_size == (size_t)-1 && q_size == (size_t)-1)
+		return __builtin_strcpy(p, q);
+	memcpy(p, q, strlen(q) + 1);
+	return p;
+}
+
 #endif
 
 #endif /* _LINUX_STRING_H_ */
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 7321ae9..102c84d 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -470,6 +470,7 @@
 struct svc_serv *  svc_create_pooled(struct svc_program *, unsigned int,
 			struct svc_serv_ops *);
 int		   svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
+int		   svc_set_num_threads_sync(struct svc_serv *, struct svc_pool *, int);
 int		   svc_pool_stats_open(struct svc_serv *serv, struct file *file);
 void		   svc_destroy(struct svc_serv *);
 void		   svc_shutdown_net(struct svc_serv *, struct net *);
diff --git a/include/linux/sync_file.h b/include/linux/sync_file.h
index aa17ccf..c97fac8 100644
--- a/include/linux/sync_file.h
+++ b/include/linux/sync_file.h
@@ -43,9 +43,10 @@
 
 	struct fence		*fence;
 	struct fence_cb cb;
+	unsigned long flags;
 };
 
-#define POLL_ENABLED FENCE_FLAG_USER_BITS
+#define POLL_ENABLED 0
 
 struct sync_file *sync_file_create(struct fence *fence);
 struct fence *sync_file_get_fence(int fd);
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 6eb24cc..a700e5f 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -47,7 +47,7 @@
 #define THERMAL_WEIGHT_DEFAULT 0
 
 /* Max sensors that can be used for a single virtual thermalzone */
-#define THERMAL_MAX_VIRT_SENSORS 5
+#define THERMAL_MAX_VIRT_SENSORS 8
 
 /* use value, which < 0K, to indicate an invalid/uninitialized temperature */
 #define THERMAL_TEMP_INVALID	-274000
diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h
index 41d81fb..751a510 100644
--- a/include/linux/trace_events.h
+++ b/include/linux/trace_events.h
@@ -275,6 +275,7 @@
 	int				perf_refcount;
 	struct hlist_head __percpu	*perf_events;
 	struct bpf_prog			*prog;
+	struct perf_event		*bpf_prog_owner;
 
 	int	(*perf_perm)(struct trace_event_call *,
 			     struct perf_event *);
diff --git a/include/linux/tty_flip.h b/include/linux/tty_flip.h
index c28dd52..d43837f 100644
--- a/include/linux/tty_flip.h
+++ b/include/linux/tty_flip.h
@@ -12,6 +12,7 @@
 		unsigned char **chars, size_t size);
 extern void tty_flip_buffer_push(struct tty_port *port);
 void tty_schedule_flip(struct tty_port *port);
+int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag);
 
 static inline int tty_insert_flip_char(struct tty_port *port,
 					unsigned char ch, char flag)
@@ -26,7 +27,7 @@
 		*char_buf_ptr(tb, tb->used++) = ch;
 		return 1;
 	}
-	return tty_insert_flip_string_flags(port, &ch, &flag, 1);
+	return __tty_insert_flip_char(port, ch, flag);
 }
 
 static inline int tty_insert_flip_string(struct tty_port *port,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index ef20e16..232c3e0 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -751,11 +751,11 @@
 extern int usb_sec_event_ring_cleanup(struct usb_device *dev,
 	unsigned int intr_num);
 
-extern dma_addr_t usb_get_sec_event_ring_dma_addr(struct usb_device *dev,
-	unsigned int intr_num);
-extern dma_addr_t usb_get_dcba_dma_addr(struct usb_device *dev);
-extern dma_addr_t usb_get_xfer_ring_dma_addr(struct usb_device *dev,
-	struct usb_host_endpoint *ep);
+extern phys_addr_t usb_get_sec_event_ring_phys_addr(
+	struct usb_device *dev, unsigned int intr_num, dma_addr_t *dma);
+extern phys_addr_t usb_get_xfer_ring_phys_addr(struct usb_device *dev,
+	struct usb_host_endpoint *ep, dma_addr_t *dma);
+extern int usb_get_controller_id(struct usb_device *dev);
 
 /* Sets up a group of bulk endpoints to support multiple stream IDs. */
 extern int usb_alloc_streams(struct usb_interface *interface,
diff --git a/include/linux/usb/ccid_desc.h b/include/linux/usb/ccid_desc.h
index 9a0c726..2e6dbb5 100644
--- a/include/linux/usb/ccid_desc.h
+++ b/include/linux/usb/ccid_desc.h
@@ -86,27 +86,27 @@
  * Table 5.1-1 Smart Card Device Class descriptors
  */
 struct usb_ccid_class_descriptor {
-	unsigned char  bLength;
-	unsigned char  bDescriptorType;
-	unsigned short bcdCCID;
-	unsigned char  bMaxSlotIndex;
-	unsigned char  bVoltageSupport;
-	unsigned long  dwProtocols;
-	unsigned long  dwDefaultClock;
-	unsigned long  dwMaximumClock;
-	unsigned char  bNumClockSupported;
-	unsigned long  dwDataRate;
-	unsigned long  dwMaxDataRate;
-	unsigned char  bNumDataRatesSupported;
-	unsigned long  dwMaxIFSD;
-	unsigned long  dwSynchProtocols;
-	unsigned long  dwMechanical;
-	unsigned long  dwFeatures;
-	unsigned long  dwMaxCCIDMessageLength;
-	unsigned char  bClassGetResponse;
-	unsigned char  bClassEnvelope;
-	unsigned short wLcdLayout;
-	unsigned char  bPINSupport;
-	unsigned char  bMaxCCIDBusySlots;
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u16 bcdCCID;
+	__u8  bMaxSlotIndex;
+	__u8  bVoltageSupport;
+	__u32  dwProtocols;
+	__u32  dwDefaultClock;
+	__u32  dwMaximumClock;
+	__u8  bNumClockSupported;
+	__u32  dwDataRate;
+	__u32  dwMaxDataRate;
+	__u8  bNumDataRatesSupported;
+	__u32  dwMaxIFSD;
+	__u32  dwSynchProtocols;
+	__u32  dwMechanical;
+	__u32  dwFeatures;
+	__u32  dwMaxCCIDMessageLength;
+	__u8  bClassGetResponse;
+	__u8  bClassEnvelope;
+	__u16 wLcdLayout;
+	__u8  bPINSupport;
+	__u8  bMaxCCIDBusySlots;
 } __packed;
 #endif
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 00d2324..b0fad11 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -83,6 +83,7 @@
 /* Driver flags */
 #define CDC_NCM_FLAG_NDP_TO_END			0x02	/* NDP is placed at end of frame */
 #define CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE	0x04	/* Avoid altsetting toggle during init */
+#define CDC_NCM_FLAG_RESET_NTB16 0x08	/* set NDP16 one more time after altsetting switch */
 
 #define cdc_ncm_comm_intf_is_mbim(x)  ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
 				       (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index b305b0e..1699d2b 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -401,12 +401,12 @@
 	int (*sec_event_ring_setup)(struct usb_hcd *hcd, unsigned int intr_num);
 	int (*sec_event_ring_cleanup)(struct usb_hcd *hcd,
 			unsigned int intr_num);
-	dma_addr_t (*get_sec_event_ring_dma_addr)(struct usb_hcd *hcd,
-			unsigned int intr_num);
-	dma_addr_t (*get_xfer_ring_dma_addr)(struct usb_hcd *hcd,
-			struct usb_device *udev, struct usb_host_endpoint *ep);
-	dma_addr_t (*get_dcba_dma_addr)(struct usb_hcd *hcd,
-			struct usb_device *udev);
+	phys_addr_t (*get_sec_event_ring_phys_addr)(struct usb_hcd *hcd,
+			unsigned int intr_num, dma_addr_t *dma);
+	phys_addr_t (*get_xfer_ring_phys_addr)(struct usb_hcd *hcd,
+			struct usb_device *udev, struct usb_host_endpoint *ep,
+			dma_addr_t *dma);
+	int (*get_core_id)(struct usb_hcd *hcd);
 };
 
 static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -449,11 +449,11 @@
 	unsigned int intr_num);
 extern int usb_hcd_sec_event_ring_cleanup(struct usb_device *udev,
 	unsigned int intr_num);
-extern dma_addr_t usb_hcd_get_sec_event_ring_dma_addr(struct usb_device *udev,
-		unsigned int intr_num);
-extern dma_addr_t usb_hcd_get_dcba_dma_addr(struct usb_device *udev);
-extern dma_addr_t usb_hcd_get_xfer_ring_dma_addr(struct usb_device *udev,
-	struct usb_host_endpoint *ep);
+extern phys_addr_t usb_hcd_get_sec_event_ring_phys_addr(
+	struct usb_device *udev, unsigned int intr_num, dma_addr_t *dma);
+extern phys_addr_t usb_hcd_get_xfer_ring_phys_addr(
+	struct usb_device *udev, struct usb_host_endpoint *ep, dma_addr_t *dma);
+extern int usb_hcd_get_controller_id(struct usb_device *udev);
 
 struct usb_hcd *__usb_create_hcd(const struct hc_driver *driver,
 		struct device *sysdev, struct device *dev, const char *bus_name,
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index ffb6393..092c32e 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -138,6 +138,7 @@
 
 	/* reset the PHY clocks */
 	int     (*reset)(struct usb_phy *x);
+	int	(*disable_chirp)(struct usb_phy *x, bool disable);
 };
 
 /**
diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h
index 62fd4e4..1b0ca4a 100644
--- a/include/linux/usb_bam.h
+++ b/include/linux/usb_bam.h
@@ -198,7 +198,7 @@
 };
 
 /**
- * struct msm_usb_bam_platform_data: pipe connection information
+ * struct msm_usb_bam_data: pipe connection information
  * between USB/HSIC BAM and another BAM. USB/HSIC BAM can be
  * either src BAM or dst BAM
  * @usb_bam_num_pipes: max number of pipes to use.
@@ -218,7 +218,7 @@
  *		can work at in bam2bam mode when connected to SS host.
  * @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself
  */
-struct msm_usb_bam_platform_data {
+struct msm_usb_bam_data {
 	u8 max_connections;
 	int usb_bam_num_pipes;
 	phys_addr_t usb_bam_fifo_baseaddr;
diff --git a/include/linux/wakelock.h b/include/linux/wakelock.h
deleted file mode 100644
index f4a698a..0000000
--- a/include/linux/wakelock.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/* include/linux/wakelock.h
- *
- * Copyright (C) 2007-2012 Google, Inc.
- *
- * 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 _LINUX_WAKELOCK_H
-#define _LINUX_WAKELOCK_H
-
-#include <linux/ktime.h>
-#include <linux/device.h>
-
-/* A wake_lock prevents the system from entering suspend or other low power
- * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock
- * prevents a full system suspend.
- */
-
-enum {
-	WAKE_LOCK_SUSPEND, /* Prevent suspend */
-	WAKE_LOCK_TYPE_COUNT
-};
-
-struct wake_lock {
-	struct wakeup_source ws;
-};
-
-static inline void wake_lock_init(struct wake_lock *lock, int type,
-				  const char *name)
-{
-	wakeup_source_init(&lock->ws, name);
-}
-
-static inline void wake_lock_destroy(struct wake_lock *lock)
-{
-	wakeup_source_trash(&lock->ws);
-}
-
-static inline void wake_lock(struct wake_lock *lock)
-{
-	__pm_stay_awake(&lock->ws);
-}
-
-static inline void wake_lock_timeout(struct wake_lock *lock, long timeout)
-{
-	__pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout));
-}
-
-static inline void wake_unlock(struct wake_lock *lock)
-{
-	__pm_relax(&lock->ws);
-}
-
-static inline int wake_lock_active(struct wake_lock *lock)
-{
-	return lock->ws.active;
-}
-
-#endif
diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
index fc6e221..1061add 100644
--- a/include/linux/workqueue.h
+++ b/include/linux/workqueue.h
@@ -312,6 +312,7 @@
 	__WQ_DRAINING		= 1 << 16, /* internal: workqueue is draining */
 	__WQ_ORDERED		= 1 << 17, /* internal: workqueue is ordered */
 	__WQ_LEGACY		= 1 << 18, /* internal: create*_workqueue() */
+	__WQ_ORDERED_EXPLICIT	= 1 << 19, /* internal: alloc_ordered_workqueue() */
 
 	WQ_MAX_ACTIVE		= 512,	  /* I like 512, better ideas? */
 	WQ_MAX_UNBOUND_PER_CPU	= 4,	  /* 4 * #cpus for unbound wq */
@@ -409,7 +410,8 @@
  * Pointer to the allocated workqueue on success, %NULL on failure.
  */
 #define alloc_ordered_workqueue(fmt, flags, args...)			\
-	alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)
+	alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED |		\
+			__WQ_ORDERED_EXPLICIT | (flags), 1, ##args)
 
 #define create_workqueue(name)						\
 	alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 82b4b53..73da337 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -49,6 +49,13 @@
 #define CFG80211_REKEY_DATA_KEK_LEN 1
 
 /**
+ * Indicate backport support for the new cfg80211_roamed event which unifies the
+ * old APIs cfg80211_roamed and cfg80211_roamed_bss and takes a structure to
+ * update roam information to the kernel.
+ */
+#define CFG80211_ROAMED_API_UNIFIED 1
+
+/**
  * DOC: Introduction
  *
  * cfg80211 is the configuration API for 802.11 devices in Linux. It bridges
@@ -2661,8 +2668,7 @@
  *	indication of requesting reassociation.
  *	In both the driver-initiated and new connect() call initiated roaming
  *	cases, the result of roaming is indicated with a call to
- *	cfg80211_roamed() or cfg80211_roamed_bss().
- *	(invoked with the wireless_dev mutex held)
+ *	cfg80211_roamed(). (invoked with the wireless_dev mutex held)
  * @update_connect_params: Update the connect parameters while connected to a
  *	BSS. The updated parameters can be used by driver/firmware for
  *	subsequent BSS selection (roaming) decisions and to form the
@@ -5301,51 +5307,50 @@
 }
 
 /**
+ * struct cfg80211_roam_info - driver initiated roaming information
+ *
+ * @channel: the channel of the new AP
+ * @bss: entry of bss to which STA got roamed (may be %NULL if %bssid is set)
+ * @bssid: the BSSID of the new AP (may be %NULL if %bss is set)
+ * @req_ie: association request IEs (maybe be %NULL)
+ * @req_ie_len: association request IEs length
+ * @resp_ie: association response IEs (may be %NULL)
+ * @resp_ie_len: assoc response IEs length
+ * @authorized: true if the 802.1X authentication was done by the driver or is
+ *	not needed (e.g., when Fast Transition protocol was used), false
+ *	otherwise. Ignored for networks that don't use 802.1X authentication.
+ */
+struct cfg80211_roam_info {
+	struct ieee80211_channel *channel;
+	struct cfg80211_bss *bss;
+	const u8 *bssid;
+	const u8 *req_ie;
+	size_t req_ie_len;
+	const u8 *resp_ie;
+	size_t resp_ie_len;
+	bool authorized;
+};
+
+/**
  * cfg80211_roamed - notify cfg80211 of roaming
  *
  * @dev: network device
- * @channel: the channel of the new AP
- * @bssid: the BSSID of the new AP
- * @req_ie: association request IEs (maybe be %NULL)
- * @req_ie_len: association request IEs length
- * @resp_ie: association response IEs (may be %NULL)
- * @resp_ie_len: assoc response IEs length
+ * @info: information about the new BSS. struct &cfg80211_roam_info.
  * @gfp: allocation flags
  *
- * It should be called by the underlying driver whenever it roamed
- * from one AP to another while connected.
- */
-void cfg80211_roamed(struct net_device *dev,
-		     struct ieee80211_channel *channel,
-		     const u8 *bssid,
-		     const u8 *req_ie, size_t req_ie_len,
-		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp);
-
-/**
- * cfg80211_roamed_bss - notify cfg80211 of roaming
- *
- * @dev: network device
- * @bss: entry of bss to which STA got roamed
- * @req_ie: association request IEs (maybe be %NULL)
- * @req_ie_len: association request IEs length
- * @resp_ie: association response IEs (may be %NULL)
- * @resp_ie_len: assoc response IEs length
- * @gfp: allocation flags
- *
- * This is just a wrapper to notify cfg80211 of roaming event with driver
- * passing bss to avoid a race in timeout of the bss entry. It should be
- * called by the underlying driver whenever it roamed from one AP to another
- * while connected. Drivers which have roaming implemented in firmware
- * may use this function to avoid a race in bss entry timeout where the bss
- * entry of the new AP is seen in the driver, but gets timed out by the time
- * it is accessed in __cfg80211_roamed() due to delay in scheduling
+ * This function may be called with the driver passing either the BSSID of the
+ * new AP or passing the bss entry to avoid a race in timeout of the bss entry.
+ * It should be called by the underlying driver whenever it roamed from one AP
+ * to another while connected. Drivers which have roaming implemented in
+ * firmware should pass the bss entry to avoid a race in bss entry timeout where
+ * the bss entry of the new AP is seen in the driver, but gets timed out by the
+ * time it is accessed in __cfg80211_roamed() due to delay in scheduling
  * rdev->event_work. In case of any failures, the reference is released
- * either in cfg80211_roamed_bss() or in __cfg80211_romed(), Otherwise,
- * it will be released while diconneting from the current bss.
+ * either in cfg80211_roamed() or in __cfg80211_romed(), Otherwise, it will be
+ * released while diconneting from the current bss.
  */
-void cfg80211_roamed_bss(struct net_device *dev, struct cfg80211_bss *bss,
-			 const u8 *req_ie, size_t req_ie_len,
-			 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp);
+void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
+		     gfp_t gfp);
 
 /**
  * cfg80211_disconnected - notify cfg80211 that connection was dropped
diff --git a/include/net/cnss_nl.h b/include/net/cnss_nl.h
index 86c2fcc..b8a7cfd 100644
--- a/include/net/cnss_nl.h
+++ b/include/net/cnss_nl.h
@@ -23,12 +23,16 @@
  * @CLD80211_ATTR_VENDOR_DATA: Embed all other attributes in this nested
  *	attribute.
  * @CLD80211_ATTR_DATA: Embed complete data in this attribute
+ * @CLD80211_ATTR_META_DATA: Embed meta data for above data. This will help
+ * wlan driver to peek into request message packet without opening up definition
+ * of complete request message.
  *
  * Any new message in future can be added as another attribute
  */
 enum cld80211_attr {
 	CLD80211_ATTR_VENDOR_DATA = 1,
 	CLD80211_ATTR_DATA,
+	CLD80211_ATTR_META_DATA,
 	/* add new attributes above here */
 
 	__CLD80211_ATTR_AFTER_LAST,
diff --git a/include/net/cnss_utils.h b/include/net/cnss_utils.h
index 6ff0fd0..77d14d1 100644
--- a/include/net/cnss_utils.h
+++ b/include/net/cnss_utils.h
@@ -33,6 +33,9 @@
 extern void cnss_utils_increment_driver_load_cnt(struct device *dev);
 extern int cnss_utils_set_wlan_mac_address(const u8 *in, uint32_t len);
 extern u8 *cnss_utils_get_wlan_mac_address(struct device *dev, uint32_t *num);
+extern int cnss_utils_set_wlan_derived_mac_address(const u8 *in, uint32_t len);
+extern u8 *cnss_utils_get_wlan_derived_mac_address(struct device *dev,
+							uint32_t *num);
 extern void cnss_utils_set_cc_source(struct device *dev,
 				     enum cnss_utils_cc_src cc_source);
 extern enum cnss_utils_cc_src cnss_utils_get_cc_source(struct device *dev);
diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h
index 261202f..8dbfdf7 100644
--- a/include/net/fib_rules.h
+++ b/include/net/fib_rules.h
@@ -95,8 +95,6 @@
 	[FRA_FWMARK]	= { .type = NLA_U32 }, \
 	[FRA_FWMASK]	= { .type = NLA_U32 }, \
 	[FRA_TABLE]     = { .type = NLA_U32 }, \
-	[FRA_UID_START]	= { .type = NLA_U32 }, \
-	[FRA_UID_END]	= { .type = NLA_U32 }, \
 	[FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \
 	[FRA_SUPPRESS_IFGROUP] = { .type = NLA_U32 }, \
 	[FRA_GOTO]	= { .type = NLA_U32 }, \
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h
index 909972a..634d192 100644
--- a/include/net/inet_frag.h
+++ b/include/net/inet_frag.h
@@ -1,14 +1,9 @@
 #ifndef __NET_FRAG_H__
 #define __NET_FRAG_H__
 
-#include <linux/percpu_counter.h>
-
 struct netns_frags {
-	/* The percpu_counter "mem" need to be cacheline aligned.
-	 *  mem.count must not share cacheline with other writers
-	 */
-	struct percpu_counter   mem ____cacheline_aligned_in_smp;
-
+	/* Keep atomic mem on separate cachelines in structs that include it */
+	atomic_t		mem ____cacheline_aligned_in_smp;
 	/* sysctls */
 	int			timeout;
 	int			high_thresh;
@@ -108,15 +103,10 @@
 int inet_frags_init(struct inet_frags *);
 void inet_frags_fini(struct inet_frags *);
 
-static inline int inet_frags_init_net(struct netns_frags *nf)
+static inline void inet_frags_init_net(struct netns_frags *nf)
 {
-	return percpu_counter_init(&nf->mem, 0, GFP_KERNEL);
+	atomic_set(&nf->mem, 0);
 }
-static inline void inet_frags_uninit_net(struct netns_frags *nf)
-{
-	percpu_counter_destroy(&nf->mem);
-}
-
 void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f);
 
 void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f);
@@ -140,37 +130,24 @@
 
 /* Memory Tracking Functions. */
 
-/* The default percpu_counter batch size is not big enough to scale to
- * fragmentation mem acct sizes.
- * The mem size of a 64K fragment is approx:
- *  (44 fragments * 2944 truesize) + frag_queue struct(200) = 129736 bytes
- */
-static unsigned int frag_percpu_counter_batch = 130000;
-
 static inline int frag_mem_limit(struct netns_frags *nf)
 {
-	return percpu_counter_read(&nf->mem);
+	return atomic_read(&nf->mem);
 }
 
 static inline void sub_frag_mem_limit(struct netns_frags *nf, int i)
 {
-	__percpu_counter_add(&nf->mem, -i, frag_percpu_counter_batch);
+	atomic_sub(i, &nf->mem);
 }
 
 static inline void add_frag_mem_limit(struct netns_frags *nf, int i)
 {
-	__percpu_counter_add(&nf->mem, i, frag_percpu_counter_batch);
+	atomic_add(i, &nf->mem);
 }
 
-static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf)
+static inline int sum_frag_mem_limit(struct netns_frags *nf)
 {
-	unsigned int res;
-
-	local_bh_disable();
-	res = percpu_counter_sum_positive(&nf->mem);
-	local_bh_enable();
-
-	return res;
+	return atomic_read(&nf->mem);
 }
 
 /* RFC 3168 support :
diff --git a/include/net/ip.h b/include/net/ip.h
index 9816365..4ef6792 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -342,7 +342,7 @@
 	    !forwarding)
 		return dst_mtu(dst);
 
-	return min(dst->dev->mtu, IP_MAX_MTU);
+	return min(READ_ONCE(dst->dev->mtu), IP_MAX_MTU);
 }
 
 static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
@@ -354,7 +354,7 @@
 		return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
 	}
 
-	return min(skb_dst(skb)->dev->mtu, IP_MAX_MTU);
+	return min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU);
 }
 
 u32 ip_idents_reserve(u32 hash, int segs);
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index a74e2aa..a6bcb18 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -68,6 +68,7 @@
 	__u16			fn_flags;
 	int			fn_sernum;
 	struct rt6_info		*rr_ptr;
+	struct rcu_head		rcu;
 };
 
 #ifndef CONFIG_IPV6_SUBTREES
@@ -102,7 +103,7 @@
 	 * the same cache line.
 	 */
 	struct fib6_table		*rt6i_table;
-	struct fib6_node		*rt6i_node;
+	struct fib6_node __rcu		*rt6i_node;
 
 	struct in6_addr			rt6i_gateway;
 
@@ -165,13 +166,40 @@
 	rt0->rt6i_flags |= RTF_EXPIRES;
 }
 
+/* Function to safely get fn->sernum for passed in rt
+ * and store result in passed in cookie.
+ * Return true if we can get cookie safely
+ * Return false if not
+ */
+static inline bool rt6_get_cookie_safe(const struct rt6_info *rt,
+				       u32 *cookie)
+{
+	struct fib6_node *fn;
+	bool status = false;
+
+	rcu_read_lock();
+	fn = rcu_dereference(rt->rt6i_node);
+
+	if (fn) {
+		*cookie = fn->fn_sernum;
+		status = true;
+	}
+
+	rcu_read_unlock();
+	return status;
+}
+
 static inline u32 rt6_get_cookie(const struct rt6_info *rt)
 {
+	u32 cookie = 0;
+
 	if (rt->rt6i_flags & RTF_PCPU ||
 	    (unlikely(rt->dst.flags & DST_NOCACHE) && rt->dst.from))
 		rt = (struct rt6_info *)(rt->dst.from);
 
-	return rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0;
+	rt6_get_cookie_safe(rt, &cookie);
+
+	return cookie;
 }
 
 static inline void ip6_rt_put(struct rt6_info *rt)
diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h
index e0f4109..c2aa73e 100644
--- a/include/net/iw_handler.h
+++ b/include/net/iw_handler.h
@@ -556,7 +556,8 @@
 		memcpy(stream + lcp_len,
 		       ((char *) &iwe->u) + IW_EV_POINT_OFF,
 		       IW_EV_POINT_PK_LEN - IW_EV_LCP_PK_LEN);
-		memcpy(stream + point_len, extra, iwe->u.data.length);
+		if (iwe->u.data.length && extra)
+			memcpy(stream + point_len, extra, iwe->u.data.length);
 		stream += event_len;
 	}
 	return stream;
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index e2dba93..2c7d876 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -902,21 +902,10 @@
 				unsigned long jiffies;
 			};
 			/* NB: vif can be NULL for injected frames */
-			union {
-				/* NB: vif can be NULL for injected frames */
-				struct ieee80211_vif *vif;
-
-				/* When packets are enqueued on txq it's easy
-				 * to re-construct the vif pointer. There's no
-				 * more space in tx_info so it can be used to
-				 * store the necessary enqueue time for packet
-				 * sojourn time computation.
-				 */
-				codel_time_t enqueue_time;
-			};
+			struct ieee80211_vif *vif;
 			struct ieee80211_key_conf *hw_key;
 			u32 flags;
-			/* 4 bytes free */
+			codel_time_t enqueue_time;
 		} control;
 		struct {
 			u64 cookie;
diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 8b68384..ddfb742 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -62,6 +62,7 @@
 	NEIGH_VAR_GC_THRESH1,
 	NEIGH_VAR_GC_THRESH2,
 	NEIGH_VAR_GC_THRESH3,
+	NEIGH_VAR_PROBE,
 	NEIGH_VAR_MAX
 };
 
diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h
index d9d52c0..4260d3c 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -71,6 +71,11 @@
 #include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
 #include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
 
+/* Handle NATTYPE Stuff,only if NATTYPE module was defined */
+#ifdef CONFIG_IP_NF_TARGET_NATTYPE_MODULE
+#include <linux/netfilter_ipv4/ipt_NATTYPE.h>
+#endif
+
 struct nf_conn {
 	/* Usage count in here is 1 for hash table, 1 per skb,
 	 * plus 1 for any connection(s) we are `master' for
@@ -120,6 +125,12 @@
 	/* Extensions */
 	struct nf_ct_ext *ext;
 
+	void *sfe_entry;
+
+#ifdef CONFIG_IP_NF_TARGET_NATTYPE_MODULE
+	unsigned long nattype_entry;
+#endif
+
 	/* Storage reserved for other modules, must be the last member */
 	union nf_conntrack_proto proto;
 };
@@ -314,6 +325,7 @@
 extern unsigned int nf_conntrack_htable_size;
 extern seqcount_t nf_conntrack_generation;
 extern unsigned int nf_conntrack_max;
+extern unsigned int nf_conntrack_pkt_threshold;
 
 /* must be called with rcu read lock held */
 static inline void
diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h
index 62e17d1..a0043c7 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -50,6 +50,8 @@
 			const struct nf_conntrack_tuple *orig,
 			const struct nf_conntrack_l3proto *l3proto,
 			const struct nf_conntrack_l4proto *l4proto);
+extern void (*delete_sfe_entry)(struct nf_conn *ct);
+extern bool (*nattype_refresh_timer)(unsigned long nattype);
 
 /* Find a connection corresponding to a tuple. */
 struct nf_conntrack_tuple_hash *
diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h
index d150b50..97983d1 100644
--- a/include/net/netfilter/nf_tables_ipv6.h
+++ b/include/net/netfilter/nf_tables_ipv6.h
@@ -9,12 +9,13 @@
 		     struct sk_buff *skb,
 		     const struct nf_hook_state *state)
 {
+	unsigned int flags = IP6_FH_F_AUTH;
 	int protohdr, thoff = 0;
 	unsigned short frag_off;
 
 	nft_set_pktinfo(pkt, skb, state);
 
-	protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+	protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
 	if (protohdr < 0) {
 		nft_set_pktinfo_proto_unspec(pkt, skb);
 		return;
@@ -32,6 +33,7 @@
 				const struct nf_hook_state *state)
 {
 #if IS_ENABLED(CONFIG_IPV6)
+	unsigned int flags = IP6_FH_F_AUTH;
 	struct ipv6hdr *ip6h, _ip6h;
 	unsigned int thoff = 0;
 	unsigned short frag_off;
@@ -50,7 +52,7 @@
 	if (pkt_len + sizeof(*ip6h) > skb->len)
 		return -1;
 
-	protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL);
+	protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, &flags);
 	if (protohdr < 0)
 		return -1;
 
diff --git a/include/net/netlink.h b/include/net/netlink.h
index 254a0fc..42adccd 100644
--- a/include/net/netlink.h
+++ b/include/net/netlink.h
@@ -756,7 +756,10 @@
  */
 static inline int nla_put_u8(struct sk_buff *skb, int attrtype, u8 value)
 {
-	return nla_put(skb, attrtype, sizeof(u8), &value);
+	/* temporary variables to work around GCC PR81715 with asan-stack=1 */
+	u8 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(u8), &tmp);
 }
 
 /**
@@ -767,7 +770,9 @@
  */
 static inline int nla_put_u16(struct sk_buff *skb, int attrtype, u16 value)
 {
-	return nla_put(skb, attrtype, sizeof(u16), &value);
+	u16 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(u16), &tmp);
 }
 
 /**
@@ -778,7 +783,9 @@
  */
 static inline int nla_put_be16(struct sk_buff *skb, int attrtype, __be16 value)
 {
-	return nla_put(skb, attrtype, sizeof(__be16), &value);
+	__be16 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(__be16), &tmp);
 }
 
 /**
@@ -789,7 +796,9 @@
  */
 static inline int nla_put_net16(struct sk_buff *skb, int attrtype, __be16 value)
 {
-	return nla_put_be16(skb, attrtype | NLA_F_NET_BYTEORDER, value);
+	__be16 tmp = value;
+
+	return nla_put_be16(skb, attrtype | NLA_F_NET_BYTEORDER, tmp);
 }
 
 /**
@@ -800,7 +809,9 @@
  */
 static inline int nla_put_le16(struct sk_buff *skb, int attrtype, __le16 value)
 {
-	return nla_put(skb, attrtype, sizeof(__le16), &value);
+	__le16 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(__le16), &tmp);
 }
 
 /**
@@ -811,7 +822,9 @@
  */
 static inline int nla_put_u32(struct sk_buff *skb, int attrtype, u32 value)
 {
-	return nla_put(skb, attrtype, sizeof(u32), &value);
+	u32 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(u32), &tmp);
 }
 
 /**
@@ -822,7 +835,9 @@
  */
 static inline int nla_put_be32(struct sk_buff *skb, int attrtype, __be32 value)
 {
-	return nla_put(skb, attrtype, sizeof(__be32), &value);
+	__be32 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(__be32), &tmp);
 }
 
 /**
@@ -833,7 +848,9 @@
  */
 static inline int nla_put_net32(struct sk_buff *skb, int attrtype, __be32 value)
 {
-	return nla_put_be32(skb, attrtype | NLA_F_NET_BYTEORDER, value);
+	__be32 tmp = value;
+
+	return nla_put_be32(skb, attrtype | NLA_F_NET_BYTEORDER, tmp);
 }
 
 /**
@@ -844,7 +861,9 @@
  */
 static inline int nla_put_le32(struct sk_buff *skb, int attrtype, __le32 value)
 {
-	return nla_put(skb, attrtype, sizeof(__le32), &value);
+	__le32 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(__le32), &tmp);
 }
 
 /**
@@ -857,7 +876,9 @@
 static inline int nla_put_u64_64bit(struct sk_buff *skb, int attrtype,
 				    u64 value, int padattr)
 {
-	return nla_put_64bit(skb, attrtype, sizeof(u64), &value, padattr);
+	u64 tmp = value;
+
+	return nla_put_64bit(skb, attrtype, sizeof(u64), &tmp, padattr);
 }
 
 /**
@@ -870,7 +891,9 @@
 static inline int nla_put_be64(struct sk_buff *skb, int attrtype, __be64 value,
 			       int padattr)
 {
-	return nla_put_64bit(skb, attrtype, sizeof(__be64), &value, padattr);
+	__be64 tmp = value;
+
+	return nla_put_64bit(skb, attrtype, sizeof(__be64), &tmp, padattr);
 }
 
 /**
@@ -883,7 +906,9 @@
 static inline int nla_put_net64(struct sk_buff *skb, int attrtype, __be64 value,
 				int padattr)
 {
-	return nla_put_be64(skb, attrtype | NLA_F_NET_BYTEORDER, value,
+	__be64 tmp = value;
+
+	return nla_put_be64(skb, attrtype | NLA_F_NET_BYTEORDER, tmp,
 			    padattr);
 }
 
@@ -897,7 +922,9 @@
 static inline int nla_put_le64(struct sk_buff *skb, int attrtype, __le64 value,
 			       int padattr)
 {
-	return nla_put_64bit(skb, attrtype, sizeof(__le64), &value, padattr);
+	__le64 tmp = value;
+
+	return nla_put_64bit(skb, attrtype, sizeof(__le64), &tmp, padattr);
 }
 
 /**
@@ -908,7 +935,9 @@
  */
 static inline int nla_put_s8(struct sk_buff *skb, int attrtype, s8 value)
 {
-	return nla_put(skb, attrtype, sizeof(s8), &value);
+	s8 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(s8), &tmp);
 }
 
 /**
@@ -919,7 +948,9 @@
  */
 static inline int nla_put_s16(struct sk_buff *skb, int attrtype, s16 value)
 {
-	return nla_put(skb, attrtype, sizeof(s16), &value);
+	s16 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(s16), &tmp);
 }
 
 /**
@@ -930,7 +961,9 @@
  */
 static inline int nla_put_s32(struct sk_buff *skb, int attrtype, s32 value)
 {
-	return nla_put(skb, attrtype, sizeof(s32), &value);
+	s32 tmp = value;
+
+	return nla_put(skb, attrtype, sizeof(s32), &tmp);
 }
 
 /**
@@ -943,7 +976,9 @@
 static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value,
 			      int padattr)
 {
-	return nla_put_64bit(skb, attrtype, sizeof(s64), &value, padattr);
+	s64 tmp = value;
+
+	return nla_put_64bit(skb, attrtype, sizeof(s64), &tmp, padattr);
 }
 
 /**
@@ -993,7 +1028,9 @@
 static inline int nla_put_in_addr(struct sk_buff *skb, int attrtype,
 				  __be32 addr)
 {
-	return nla_put_be32(skb, attrtype, addr);
+	__be32 tmp = addr;
+
+	return nla_put_be32(skb, attrtype, tmp);
 }
 
 /**
diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
index e6aa0a2..f18fc1a 100644
--- a/include/net/sch_generic.h
+++ b/include/net/sch_generic.h
@@ -768,8 +768,11 @@
 	old = *pold;
 	*pold = new;
 	if (old != NULL) {
-		qdisc_tree_reduce_backlog(old, old->q.qlen, old->qstats.backlog);
+		unsigned int qlen = old->q.qlen;
+		unsigned int backlog = old->qstats.backlog;
+
 		qdisc_reset(old);
+		qdisc_tree_reduce_backlog(old, qlen, backlog);
 	}
 	sch_tree_unlock(sch);
 
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
index 31acc3f..61d9ce8 100644
--- a/include/net/sctp/sctp.h
+++ b/include/net/sctp/sctp.h
@@ -460,6 +460,8 @@
 
 #define _sctp_walk_params(pos, chunk, end, member)\
 for (pos.v = chunk->member;\
+     (pos.v + offsetof(struct sctp_paramhdr, length) + sizeof(pos.p->length) <=\
+      (void *)chunk + end) &&\
      pos.v <= (void *)chunk + end - ntohs(pos.p->length) &&\
      ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\
      pos.v += SCTP_PAD4(ntohs(pos.p->length)))
@@ -470,6 +472,8 @@
 #define _sctp_walk_errors(err, chunk_hdr, end)\
 for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
 	    sizeof(sctp_chunkhdr_t));\
+     ((void *)err + offsetof(sctp_errhdr_t, length) + sizeof(err->length) <=\
+      (void *)chunk_hdr + end) &&\
      (void *)err <= (void *)chunk_hdr + end - ntohs(err->length) &&\
      ntohs(err->length) >= sizeof(sctp_errhdr_t); \
      err = (sctp_errhdr_t *)((void *)err + SCTP_PAD4(ntohs(err->length))))
diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h
index 2c098cd..231df4f 100644
--- a/include/net/sctp/ulpevent.h
+++ b/include/net/sctp/ulpevent.h
@@ -141,8 +141,12 @@
 static inline int sctp_ulpevent_type_enabled(__u16 sn_type,
 					     struct sctp_event_subscribe *mask)
 {
+	int offset = sn_type - SCTP_SN_TYPE_BASE;
 	char *amask = (char *) mask;
-	return amask[sn_type - SCTP_SN_TYPE_BASE];
+
+	if (offset >= sizeof(struct sctp_event_subscribe))
+		return 0;
+	return amask[offset];
 }
 
 /* Given an event subscription, is this event enabled? */
diff --git a/include/net/udp.h b/include/net/udp.h
index 4948790..a32c58e 100644
--- a/include/net/udp.h
+++ b/include/net/udp.h
@@ -247,6 +247,7 @@
 
 /* net/ipv4/udp.c */
 void udp_v4_early_demux(struct sk_buff *skb);
+bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst);
 int udp_get_port(struct sock *sk, unsigned short snum,
 		 int (*saddr_cmp)(const struct sock *,
 				  const struct sock *));
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index 835c30e..9b6e6a4 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -155,6 +155,7 @@
 		int		header_len;
 		int		trailer_len;
 		u32		extra_flags;
+		u32		output_mark;
 	} props;
 
 	struct xfrm_lifetime_cfg lft;
@@ -284,10 +285,12 @@
 	struct dst_entry	*(*dst_lookup)(struct net *net,
 					       int tos, int oif,
 					       const xfrm_address_t *saddr,
-					       const xfrm_address_t *daddr);
+					       const xfrm_address_t *daddr,
+					       u32 mark);
 	int			(*get_saddr)(struct net *net, int oif,
 					     xfrm_address_t *saddr,
-					     xfrm_address_t *daddr);
+					     xfrm_address_t *daddr,
+					     u32 mark);
 	void			(*decode_session)(struct sk_buff *skb,
 						  struct flowi *fl,
 						  int reverse);
diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h
index e2c72d1..3c2aff3 100644
--- a/include/soc/qcom/cmd-db.h
+++ b/include/soc/qcom/cmd-db.h
@@ -110,17 +110,18 @@
 	return 0;
 }
 
-bool cmd_db_get_priority(u32 addr, u8 drv_id)
+static inline bool cmd_db_get_priority(u32 addr, u8 drv_id)
 {
 	return false;
 }
 
-int cmd_db_get_aux_data(const char *resource_id, u8 *data, int len)
+static inline int cmd_db_get_aux_data(const char *resource_id,
+		u8 *data, int len)
 {
 	return -ENODEV;
 }
 
-int cmd_db_get_aux_data_len(const char *resource_id)
+static inline int cmd_db_get_aux_data_len(const char *resource_id)
 {
 	return -ENODEV;
 }
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 3527c35..e58a522 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -127,16 +127,7 @@
 extern int icnss_smmu_map(struct device *dev, phys_addr_t paddr,
 			  uint32_t *iova_addr, size_t size);
 extern unsigned int icnss_socinfo_get_serial_number(struct device *dev);
-extern int icnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count);
-extern int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count,
-					 u16 buf_len);
-extern int icnss_wlan_set_dfs_nol(const void *info, u16 info_len);
-extern int icnss_wlan_get_dfs_nol(void *info, u16 info_len);
 extern bool icnss_is_qmi_disable(void);
 extern bool icnss_is_fw_ready(void);
-extern int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len);
-extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num);
 extern int icnss_trigger_recovery(struct device *dev);
-extern int icnss_get_driver_load_cnt(void);
-extern void icnss_increment_driver_load_cnt(void);
 #endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/memory_dump.h b/include/soc/qcom/memory_dump.h
index 50e4b8c..b4733d7 100644
--- a/include/soc/qcom/memory_dump.h
+++ b/include/soc/qcom/memory_dump.h
@@ -83,6 +83,8 @@
 	MSM_DUMP_DATA_RPM = 0xEA,
 	MSM_DUMP_DATA_SCANDUMP = 0xEB,
 	MSM_DUMP_DATA_RPMH = 0xEC,
+	MSM_DUMP_DATA_FCM = 0xEE,
+	MSM_DUMP_DATA_POWER_REGS = 0xED,
 	MSM_DUMP_DATA_TMC_ETF = 0xF0,
 	MSM_DUMP_DATA_TMC_REG = 0x100,
 	MSM_DUMP_DATA_LOG_BUF = 0x110,
@@ -121,12 +123,19 @@
 #ifdef CONFIG_QCOM_MEMORY_DUMP_V2
 extern int msm_dump_data_register(enum msm_dump_table_ids id,
 				  struct msm_dump_entry *entry);
+
+extern void *get_msm_dump_ptr(enum msm_dump_data_ids id);
 #else
 static inline int msm_dump_data_register(enum msm_dump_table_ids id,
 					 struct msm_dump_entry *entry)
 {
 	return -EINVAL;
 }
+
+static inline void *get_msm_dump_ptr(enum msm_dump_data_ids id)
+{
+	return NULL;
+}
 #endif
 
 #endif
diff --git a/include/soc/qcom/msm-core.h b/include/soc/qcom/msm-core.h
deleted file mode 100644
index f1c06a6..0000000
--- a/include/soc/qcom/msm-core.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (c) 2014-2015,2017 The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __ARCH_ARM_MACH_MSM_CORE_H
-#define __ARCH_ARM_MACH_MSM_CORE_H
-#ifdef CONFIG_APSS_CORE_EA
-void set_cpu_throttled(struct cpumask *mask, bool throttling);
-struct blocking_notifier_head *get_power_update_notifier(void);
-void trigger_cpu_pwr_stats_calc(void);
-struct cpu_pwr_stats *get_cpu_pwr_stats(void);
-#else
-static inline void set_cpu_throttled(struct cpumask *mask, bool throttling) {}
-struct blocking_notifier_head *get_power_update_notifier(void) {return NULL; }
-static inline void trigger_cpu_pwr_stats_calc(void) {}
-struct cpu_pwr_stats *get_cpu_pwr_stats(void) {return NULL; }
-#endif
-#endif
diff --git a/include/soc/qcom/socinfo.h b/include/soc/qcom/socinfo.h
index f196d40..9e91e4b 100644
--- a/include/soc/qcom/socinfo.h
+++ b/include/soc/qcom/socinfo.h
@@ -102,6 +102,14 @@
 	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm845")
 #define early_machine_is_sdm670()	\
 	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm670")
+#define early_machine_is_qcs605()	\
+	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,qcs605")
+#define early_machine_is_sda670()	\
+	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sda670")
+#define early_machine_is_msm8953()	\
+	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,msm8953")
+#define early_machine_is_sdm450()	\
+	of_flat_dt_is_compatible(of_get_flat_dt_root(), "qcom,sdm450")
 #else
 #define of_board_is_sim()		0
 #define of_board_is_rumi()		0
@@ -142,6 +150,10 @@
 #define early_machine_is_sdxpoorwills()	0
 #define early_machine_is_sdm845()	0
 #define early_machine_is_sdm670()	0
+#define early_machine_is_qcs605()	0
+#define early_machine_is_sda670()	0
+#define early_machine_is_msm8953()	0
+#define early_machine_is_sdm450()	0
 #endif
 
 #define PLATFORM_SUBTYPE_MDM	1
@@ -204,6 +216,10 @@
 	SDX_CPU_SDXPOORWILLS,
 	MSM_CPU_SDM845,
 	MSM_CPU_SDM670,
+	MSM_CPU_QCS605,
+	MSM_CPU_SDA670,
+	MSM_CPU_8953,
+	MSM_CPU_SDM450,
 };
 
 struct msm_soc_info {
diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h
index 1ed100b..d0f76d0 100644
--- a/include/soc/qcom/spm.h
+++ b/include/soc/qcom/spm.h
@@ -125,6 +125,27 @@
 	return false;
 }
 
+static inline int msm_spm_is_avs_enabled(unsigned int cpu)
+{
+	return -ENODEV;
+}
+
+static inline int msm_spm_avs_enable(unsigned int cpu)
+{
+	return -ENODEV;
+}
+
+static inline int msm_spm_avs_disable(unsigned int cpu)
+{
+	return -ENODEV;
+}
+
+static inline int msm_spm_avs_set_limit(unsigned int cpu, uint32_t min_lvl,
+		uint32_t max_lvl)
+{
+	return -ENODEV;
+}
+
 static inline int msm_spm_avs_enable_irq(unsigned int cpu,
 		enum msm_spm_avs_irq irq)
 {
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 8495824..348d958 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -106,7 +106,7 @@
 #endif
 
 #define SNDRV_PCM_IOCTL1_RESET		0
-#define SNDRV_PCM_IOCTL1_INFO		1
+/* 1 is absent slot. */
 #define SNDRV_PCM_IOCTL1_CHANNEL_INFO	2
 #define SNDRV_PCM_IOCTL1_GSTATE		3
 #define SNDRV_PCM_IOCTL1_FIFO_SIZE	4
@@ -466,6 +466,7 @@
 	const struct snd_pcm_ops *ops;
 	/* -- runtime information -- */
 	struct snd_pcm_runtime *runtime;
+	spinlock_t runtime_lock;
         /* -- timer section -- */
 	struct snd_timer *timer;		/* timer */
 	unsigned timer_running: 1;	/* time is running */
diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h
index feb58d4..4b9ee30 100644
--- a/include/sound/seq_kernel.h
+++ b/include/sound/seq_kernel.h
@@ -49,7 +49,8 @@
 #define SNDRV_SEQ_DEFAULT_CLIENT_EVENTS	200
 
 /* max delivery path length */
-#define SNDRV_SEQ_MAX_HOPS		10
+/* NOTE: this shouldn't be greater than MAX_LOCKDEP_SUBCLASSES */
+#define SNDRV_SEQ_MAX_HOPS		8
 
 /* max size of event size */
 #define SNDRV_SEQ_MAX_EVENT_LEN		0x3fffffff
diff --git a/include/sound/seq_virmidi.h b/include/sound/seq_virmidi.h
index a03acd0..695257a 100644
--- a/include/sound/seq_virmidi.h
+++ b/include/sound/seq_virmidi.h
@@ -60,6 +60,7 @@
 	int port;			/* created/attached port */
 	unsigned int flags;		/* SNDRV_VIRMIDI_* */
 	rwlock_t filelist_lock;
+	struct rw_semaphore filelist_sem;
 	struct list_head filelist;
 };
 
diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h
index 33b2e75..6021c3a 100644
--- a/include/target/iscsi/iscsi_target_core.h
+++ b/include/target/iscsi/iscsi_target_core.h
@@ -563,6 +563,7 @@
 #define LOGIN_FLAGS_READ_ACTIVE		1
 #define LOGIN_FLAGS_CLOSED		2
 #define LOGIN_FLAGS_READY		4
+#define LOGIN_FLAGS_INITIAL_PDU		8
 	unsigned long		login_flags;
 	struct delayed_work	login_work;
 	struct delayed_work	login_cleanup_work;
@@ -784,6 +785,7 @@
 	int			np_sock_type;
 	enum np_thread_state_table np_thread_state;
 	bool                    enabled;
+	atomic_t		np_reset_count;
 	enum iscsi_timer_flags_table np_login_timer_flags;
 	u32			np_exports;
 	enum np_flags_table	np_flags;
diff --git a/include/trace/events/cpuhp.h b/include/trace/events/cpuhp.h
index 996953d..7f4eba4 100644
--- a/include/trace/events/cpuhp.h
+++ b/include/trace/events/cpuhp.h
@@ -88,6 +88,34 @@
 		  __entry->cpu, __entry->state, __entry->idx,  __entry->ret)
 );
 
+TRACE_EVENT(cpuhp_latency,
+
+	TP_PROTO(unsigned int cpu, unsigned int state,
+		 u64 start_time,  int ret),
+
+	TP_ARGS(cpu, state, start_time, ret),
+
+	TP_STRUCT__entry(
+		__field(unsigned int,	cpu)
+		__field(unsigned int,	state)
+		__field(u64,		time)
+		__field(int,		ret)
+	),
+
+	TP_fast_assign(
+		__entry->cpu	= cpu;
+		__entry->state	= state;
+		__entry->time	= div64_u64(sched_clock() - start_time, 1000);
+		__entry->ret	= ret;
+	),
+
+	TP_printk(" cpu:%d state:%s latency:%llu USEC ret: %d",
+		__entry->cpu, __entry->state ? "online" : "offline",
+		__entry->time, __entry->ret)
+);
+
+
+
 #endif
 
 /* This part must be outside protection */
diff --git a/include/trace/events/iommu.h b/include/trace/events/iommu.h
index 2c7befb..255e228 100644
--- a/include/trace/events/iommu.h
+++ b/include/trace/events/iommu.h
@@ -12,8 +12,10 @@
 
 #include <linux/tracepoint.h>
 #include <linux/pci.h>
+#include <linux/iommu.h>
 
 struct device;
+struct iommu_domain;
 
 DECLARE_EVENT_CLASS(iommu_group_event,
 
@@ -85,47 +87,84 @@
 
 TRACE_EVENT(map,
 
-	TP_PROTO(unsigned long iova, phys_addr_t paddr, size_t size),
+	TP_PROTO(struct iommu_domain *domain, unsigned long iova,
+		 phys_addr_t paddr, size_t size, int prot),
 
-	TP_ARGS(iova, paddr, size),
+	TP_ARGS(domain, iova, paddr, size, prot),
 
 	TP_STRUCT__entry(
+		__string(name, domain->name)
 		__field(u64, iova)
 		__field(u64, paddr)
 		__field(size_t, size)
+		__field(int, prot)
 	),
 
 	TP_fast_assign(
+		__assign_str(name, domain->name);
 		__entry->iova = iova;
 		__entry->paddr = paddr;
 		__entry->size = size;
+		__entry->prot = prot;
 	),
 
-	TP_printk("IOMMU: iova=0x%016llx paddr=0x%016llx size=%zu",
-			__entry->iova, __entry->paddr, __entry->size
+	TP_printk("IOMMU:%s iova=0x%016llx paddr=0x%016llx size=0x%zx prot=0x%x",
+			__get_str(name), __entry->iova, __entry->paddr,
+			__entry->size, __entry->prot
 	)
 );
 
 TRACE_EVENT(unmap,
 
-	TP_PROTO(unsigned long iova, size_t size, size_t unmapped_size),
+	TP_PROTO(struct iommu_domain *domain, unsigned long iova, size_t size,
+			size_t unmapped_size),
 
-	TP_ARGS(iova, size, unmapped_size),
+	TP_ARGS(domain, iova, size, unmapped_size),
 
 	TP_STRUCT__entry(
+		__string(name, domain->name)
 		__field(u64, iova)
 		__field(size_t, size)
 		__field(size_t, unmapped_size)
 	),
 
 	TP_fast_assign(
+		__assign_str(name, domain->name);
 		__entry->iova = iova;
 		__entry->size = size;
 		__entry->unmapped_size = unmapped_size;
 	),
 
-	TP_printk("IOMMU: iova=0x%016llx size=%zu unmapped_size=%zu",
-			__entry->iova, __entry->size, __entry->unmapped_size
+	TP_printk("IOMMU:%s iova=0x%016llx size=0x%zx unmapped_size=0x%zx",
+			__get_str(name), __entry->iova, __entry->size,
+			__entry->unmapped_size
+	)
+);
+
+TRACE_EVENT(map_sg,
+
+	TP_PROTO(struct iommu_domain *domain, unsigned long iova, size_t size,
+		int prot),
+
+	TP_ARGS(domain, iova, size, prot),
+
+	TP_STRUCT__entry(
+		__string(name, domain->name)
+		__field(u64, iova)
+		__field(size_t, size)
+		__field(int, prot)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, domain->name);
+		__entry->iova = iova;
+		__entry->size = size;
+		__entry->prot = prot;
+	),
+
+	TP_printk("IOMMU:%s iova=0x%016llx size=0x%zx prot=0x%x",
+			__get_str(name), __entry->iova, __entry->size,
+			__entry->prot
 	)
 );
 
@@ -161,6 +200,80 @@
 
 	TP_ARGS(dev, iova, flags)
 );
+
+DECLARE_EVENT_CLASS(iommu_errata_tlbi,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time),
+
+	TP_STRUCT__entry(
+		__string(device, dev_name(dev))
+		__field(u64, time)
+	),
+
+	TP_fast_assign(
+		__assign_str(device, dev_name(dev));
+		__entry->time = time;
+	),
+
+	TP_printk("IOMMU:%s %lld us",
+			__get_str(device), __entry->time
+	)
+);
+
+DEFINE_EVENT(iommu_errata_tlbi, errata_tlbi_start,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time)
+);
+
+DEFINE_EVENT(iommu_errata_tlbi, errata_tlbi_end,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time)
+);
+
+DEFINE_EVENT(iommu_errata_tlbi, errata_throttle_start,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time)
+);
+
+DEFINE_EVENT(iommu_errata_tlbi, errata_throttle_end,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time)
+);
+
+DEFINE_EVENT(iommu_errata_tlbi, errata_failed,
+
+	TP_PROTO(struct device *dev, u64 time),
+
+	TP_ARGS(dev, time)
+);
+
+TRACE_EVENT(smmu_init,
+
+	TP_PROTO(u64 time),
+
+	TP_ARGS(time),
+
+	TP_STRUCT__entry(
+		__field(u64, time)
+	),
+
+	TP_fast_assign(
+		__entry->time = time;
+	),
+
+	TP_printk("ARM SMMU init latency: %lld us", __entry->time)
+);
+
 #endif /* _TRACE_IOMMU_H */
 
 /* This part must be outside protection */
diff --git a/include/trace/events/power.h b/include/trace/events/power.h
index cecfb8f..408fa57 100644
--- a/include/trace/events/power.h
+++ b/include/trace/events/power.h
@@ -702,9 +702,10 @@
 TRACE_EVENT(memlat_dev_meas,
 
 	TP_PROTO(const char *name, unsigned int dev_id, unsigned long inst,
-		 unsigned long mem, unsigned long freq, unsigned int ratio),
+		 unsigned long mem, unsigned long freq, unsigned int stall,
+		 unsigned int ratio),
 
-	TP_ARGS(name, dev_id, inst, mem, freq, ratio),
+	TP_ARGS(name, dev_id, inst, mem, freq, stall, ratio),
 
 	TP_STRUCT__entry(
 		__string(name, name)
@@ -712,6 +713,7 @@
 		__field(unsigned long, inst)
 		__field(unsigned long, mem)
 		__field(unsigned long, freq)
+		__field(unsigned int, stall)
 		__field(unsigned int, ratio)
 	),
 
@@ -721,15 +723,17 @@
 		__entry->inst = inst;
 		__entry->mem = mem;
 		__entry->freq = freq;
+		__entry->stall = stall;
 		__entry->ratio = ratio;
 	),
 
-	TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, ratio=%u",
+	TP_printk("dev: %s, id=%u, inst=%lu, mem=%lu, freq=%lu, stall=%u, ratio=%u",
 		__get_str(name),
 		__entry->dev_id,
 		__entry->inst,
 		__entry->mem,
 		__entry->freq,
+		__entry->stall,
 		__entry->ratio)
 );
 
@@ -797,6 +801,29 @@
 		      __entry->pl, __entry->flags)
 );
 
+TRACE_EVENT(sugov_next_freq,
+	    TP_PROTO(unsigned int cpu, unsigned long util, unsigned long max,
+		     unsigned int freq),
+	    TP_ARGS(cpu, util, max, freq),
+	    TP_STRUCT__entry(
+		    __field(	unsigned int,	cpu)
+		    __field(	unsigned long,	util)
+		    __field(	unsigned long,	max)
+		    __field(	unsigned int,	freq)
+	    ),
+	    TP_fast_assign(
+		    __entry->cpu = cpu;
+		    __entry->util = util;
+		    __entry->max = max;
+		    __entry->freq = freq;
+	    ),
+	    TP_printk("cpu=%u util=%lu max=%lu freq=%u",
+		      __entry->cpu,
+		      __entry->util,
+		      __entry->max,
+		      __entry->freq)
+);
+
 DECLARE_EVENT_CLASS(kpm_module,
 
 	TP_PROTO(unsigned int managed_cpus, unsigned int max_cpus),
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index cd61786..a3b01c6 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -262,9 +262,9 @@
 
 TRACE_EVENT(sched_get_task_cpu_cycles,
 
-	TP_PROTO(int cpu, int event, u64 cycles, u64 exec_time),
+	TP_PROTO(int cpu, int event, u64 cycles, u64 exec_time, struct task_struct *p),
 
-	TP_ARGS(cpu, event, cycles, exec_time),
+	TP_ARGS(cpu, event, cycles, exec_time, p),
 
 	TP_STRUCT__entry(
 		__field(int,		cpu		)
@@ -273,6 +273,8 @@
 		__field(u64,		exec_time	)
 		__field(u32,		freq		)
 		__field(u32,		legacy_freq	)
+		__field(pid_t,		pid		)
+		__array(char,	comm,   TASK_COMM_LEN	)
 	),
 
 	TP_fast_assign(
@@ -282,11 +284,13 @@
 		__entry->exec_time	= exec_time;
 		__entry->freq		= cpu_cycles_to_freq(cycles, exec_time);
 		__entry->legacy_freq	= cpu_cur_freq(cpu);
+		__entry->pid            = p->pid;
+		memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
 	),
 
-	TP_printk("cpu=%d event=%d cycles=%llu exec_time=%llu freq=%u legacy_freq=%u",
+	TP_printk("cpu=%d event=%d cycles=%llu exec_time=%llu freq=%u legacy_freq=%u task=%d (%s)",
 		  __entry->cpu, __entry->event, __entry->cycles,
-		  __entry->exec_time, __entry->freq, __entry->legacy_freq)
+		  __entry->exec_time, __entry->freq, __entry->legacy_freq, __entry->pid, __entry->comm)
 );
 
 TRACE_EVENT(sched_update_task_ravg,
@@ -1775,16 +1779,18 @@
 		__field(u32, busy)
 		__field(u32, old_is_busy)
 		__field(u32, is_busy)
+		__field(bool, high_irqload)
 	),
 	TP_fast_assign(
 		__entry->cpu = cpu;
 		__entry->busy = busy;
 		__entry->old_is_busy = old_is_busy;
 		__entry->is_busy = is_busy;
+		__entry->high_irqload = sched_cpu_high_irqload(cpu);
 	),
-	TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u",
+	TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u high_irqload=%d",
 		  __entry->cpu, __entry->busy, __entry->old_is_busy,
-		  __entry->is_busy)
+		  __entry->is_busy, __entry->high_irqload)
 );
 
 TRACE_EVENT(core_ctl_set_boost,
diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h
index c0475a2..94ee287 100644
--- a/include/trace/events/thermal.h
+++ b/include/trace/events/thermal.h
@@ -20,6 +20,29 @@
 			 { THERMAL_TRIP_PASSIVE,  "PASSIVE"},	\
 			 { THERMAL_TRIP_ACTIVE,   "ACTIVE"})
 
+TRACE_EVENT(thermal_query_temp,
+
+	TP_PROTO(struct thermal_zone_device *tz, int temp),
+
+	TP_ARGS(tz, temp),
+
+	TP_STRUCT__entry(
+		__string(thermal_zone, tz->type)
+		__field(int, id)
+		__field(int, temp)
+	),
+
+	TP_fast_assign(
+		__assign_str(thermal_zone, tz->type);
+		__entry->id = tz->id;
+		__entry->temp = temp;
+	),
+
+	TP_printk("thermal_zone=%s id=%d temp=%d",
+		__get_str(thermal_zone), __entry->id,
+		__entry->temp)
+);
+
 TRACE_EVENT(thermal_temperature,
 
 	TP_PROTO(struct thermal_zone_device *tz),
diff --git a/include/trace/events/trace_msm_core.h b/include/trace/events/trace_msm_core.h
deleted file mode 100644
index 45747f7..0000000
--- a/include/trace/events/trace_msm_core.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/* Copyright (c) 2014,2016 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.
- */
-
-#undef TRACE_SYSTEM
-#define TRACE_SYSTEM msm_core
-
-#if !defined(_TRACE_MSM_CORE_H) || defined(TRACE_HEADER_MULTI_READ)
-#define _TRACE_MSM_CORE_H
-
-#include <linux/tracepoint.h>
-#include <linux/thermal.h>
-
-TRACE_EVENT(cpu_stats,
-
-	TP_PROTO(unsigned int cpu, long temp,
-	uint64_t min_power, uint64_t max_power),
-
-	TP_ARGS(cpu, temp, min_power, max_power),
-
-	TP_STRUCT__entry(
-		__field(unsigned int, cpu)
-		__field(long, temp)
-		__field(uint64_t, min_power)
-		__field(uint64_t, max_power)
-	),
-
-	TP_fast_assign(
-		__entry->cpu = cpu;
-		__entry->temp = temp;
-		__entry->min_power = min_power;
-		__entry->max_power = max_power;
-	),
-
-	TP_printk("Cpu%d: temp:%ld power@minfreq:%llu power@maxfreq:%llu",
-		__entry->cpu, __entry->temp, __entry->min_power,
-		__entry->max_power)
-);
-
-TRACE_EVENT(temp_threshold,
-
-	TP_PROTO(unsigned int cpu, long temp,
-		long hi_thresh, long low_thresh),
-
-	TP_ARGS(cpu, temp, hi_thresh, low_thresh),
-
-	TP_STRUCT__entry(
-		__field(unsigned int, cpu)
-		__field(long, temp)
-		__field(long, hi_thresh)
-		__field(long, low_thresh)
-	),
-
-	TP_fast_assign(
-		__entry->cpu = cpu;
-		__entry->temp = temp;
-		__entry->hi_thresh = hi_thresh;
-		__entry->low_thresh = low_thresh;
-	),
-
-	TP_printk("Cpu%d: temp:%ld hi_thresh:%ld low_thresh:%ld",
-		__entry->cpu, __entry->temp, __entry->hi_thresh,
-		__entry->low_thresh)
-);
-
-TRACE_EVENT(temp_notification,
-
-	TP_PROTO(unsigned int sensor_id, enum thermal_trip_type type,
-		int temp, int prev_temp),
-
-	TP_ARGS(sensor_id, type, temp, prev_temp),
-
-	TP_STRUCT__entry(
-		__field(unsigned int, sensor_id)
-		__field(enum thermal_trip_type, type)
-		__field(int, temp)
-		__field(int, prev_temp)
-	),
-
-	TP_fast_assign(
-		__entry->sensor_id = sensor_id;
-		__entry->type = type;
-		__entry->temp = temp;
-		__entry->prev_temp = prev_temp;
-	),
-
-	TP_printk("Sensor_id%d: %s threshold triggered temp:%d(previous:%d)",
-		__entry->sensor_id,
-		__entry->type == THERMAL_TRIP_CONFIGURABLE_HI ? "High" : "Low",
-		__entry->temp, __entry->prev_temp)
-);
-
-#endif
-#define TRACE_INCLUDE_FILE trace_msm_core
-#include <trace/define_trace.h>
diff --git a/include/trace/events/ufs.h b/include/trace/events/ufs.h
index 6dc4735..aaa5cf7 100644
--- a/include/trace/events/ufs.h
+++ b/include/trace/events/ufs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -225,10 +225,10 @@
 	),
 
 	TP_printk(
-		"%s: %s: tag: %u, DB: 0x%x, size: %d, IS: %u, LBA: %llu, opcode: 0x%x",
-		__get_str(str), __get_str(dev_name), __entry->tag,
-		__entry->doorbell, __entry->transfer_len,
-		__entry->intr, __entry->lba, (u32)__entry->opcode
+		"%s: %14s: tag: %-2u cmd: 0x%-2x lba: %-9llu size: %-7d DB: 0x%-8x IS: 0x%x",
+		__get_str(dev_name), __get_str(str), __entry->tag,
+		(u32)__entry->opcode, __entry->lba, __entry->transfer_len,
+		__entry->doorbell, __entry->intr
 	)
 );
 
diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h
index c88fd09..2f8536b 100644
--- a/include/trace/events/vmscan.h
+++ b/include/trace/events/vmscan.h
@@ -269,23 +269,24 @@
 		__entry->retval)
 );
 
-DECLARE_EVENT_CLASS(mm_vmscan_lru_isolate_template,
-
+TRACE_EVENT(mm_vmscan_lru_isolate,
 	TP_PROTO(int classzone_idx,
 		int order,
 		unsigned long nr_requested,
 		unsigned long nr_scanned,
+		unsigned long nr_skipped,
 		unsigned long nr_taken,
 		isolate_mode_t isolate_mode,
 		int file),
 
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file),
+	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_skipped, nr_taken, isolate_mode, file),
 
 	TP_STRUCT__entry(
 		__field(int, classzone_idx)
 		__field(int, order)
 		__field(unsigned long, nr_requested)
 		__field(unsigned long, nr_scanned)
+		__field(unsigned long, nr_skipped)
 		__field(unsigned long, nr_taken)
 		__field(isolate_mode_t, isolate_mode)
 		__field(int, file)
@@ -296,49 +297,23 @@
 		__entry->order = order;
 		__entry->nr_requested = nr_requested;
 		__entry->nr_scanned = nr_scanned;
+		__entry->nr_skipped = nr_skipped;
 		__entry->nr_taken = nr_taken;
 		__entry->isolate_mode = isolate_mode;
 		__entry->file = file;
 	),
 
-	TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_taken=%lu file=%d",
+	TP_printk("isolate_mode=%d classzone=%d order=%d nr_requested=%lu nr_scanned=%lu nr_skipped=%lu nr_taken=%lu file=%d",
 		__entry->isolate_mode,
 		__entry->classzone_idx,
 		__entry->order,
 		__entry->nr_requested,
 		__entry->nr_scanned,
+		__entry->nr_skipped,
 		__entry->nr_taken,
 		__entry->file)
 );
 
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_lru_isolate,
-
-	TP_PROTO(int classzone_idx,
-		int order,
-		unsigned long nr_requested,
-		unsigned long nr_scanned,
-		unsigned long nr_taken,
-		isolate_mode_t isolate_mode,
-		int file),
-
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
-DEFINE_EVENT(mm_vmscan_lru_isolate_template, mm_vmscan_memcg_isolate,
-
-	TP_PROTO(int classzone_idx,
-		int order,
-		unsigned long nr_requested,
-		unsigned long nr_scanned,
-		unsigned long nr_taken,
-		isolate_mode_t isolate_mode,
-		int file),
-
-	TP_ARGS(classzone_idx, order, nr_requested, nr_scanned, nr_taken, isolate_mode, file)
-
-);
-
 TRACE_EVENT(mm_vmscan_writepage,
 
 	TP_PROTO(struct page *page),
diff --git a/include/uapi/drm/drm_fourcc.h b/include/uapi/drm/drm_fourcc.h
index 7846ec8..e6ff4cc 100644
--- a/include/uapi/drm/drm_fourcc.h
+++ b/include/uapi/drm/drm_fourcc.h
@@ -154,6 +154,7 @@
 
 /* Vendor Ids: */
 #define DRM_FORMAT_MOD_NONE           0
+#define DRM_FORMAT_MOD_VENDOR_NONE    0
 #define DRM_FORMAT_MOD_VENDOR_INTEL   0x01
 #define DRM_FORMAT_MOD_VENDOR_AMD     0x02
 #define DRM_FORMAT_MOD_VENDOR_NV      0x03
diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h
index 6ff08de..6f33a4a 100644
--- a/include/uapi/drm/msm_drm.h
+++ b/include/uapi/drm/msm_drm.h
@@ -61,6 +61,44 @@
 	__s64 tv_nsec;         /* nanoseconds */
 };
 
+/*
+ * HDR Metadata
+ * These are defined as per EDID spec and shall be used by the sink
+ * to set the HDR metadata for playback from userspace.
+ */
+
+#define HDR_PRIMARIES_COUNT   3
+
+#define DRM_MSM_EXT_HDR_METADATA
+struct drm_msm_ext_hdr_metadata {
+	__u32 hdr_state;        /* HDR state */
+	__u32 eotf;             /* electro optical transfer function */
+	__u32 hdr_supported;    /* HDR supported */
+	__u32 display_primaries_x[HDR_PRIMARIES_COUNT]; /* Primaries x */
+	__u32 display_primaries_y[HDR_PRIMARIES_COUNT]; /* Primaries y */
+	__u32 white_point_x;    /* white_point_x */
+	__u32 white_point_y;    /* white_point_y */
+	__u32 max_luminance;    /* Max luminance */
+	__u32 min_luminance;    /* Min Luminance */
+	__u32 max_content_light_level; /* max content light level */
+	__u32 max_average_light_level; /* max average light level */
+};
+
+/**
+ * HDR sink properties
+ * These are defined as per EDID spec and shall be used by the userspace
+ * to determine the HDR properties to be set to the sink.
+ */
+#define DRM_MSM_EXT_HDR_PROPERTIES
+struct drm_msm_ext_hdr_properties {
+	__u8 hdr_metadata_type_one;   /* static metadata type one */
+	__u32 hdr_supported;          /* HDR supported */
+	__u32 hdr_eotf;               /* electro optical transfer function */
+	__u32 hdr_max_luminance;      /* Max luminance */
+	__u32 hdr_avg_luminance;      /* Avg luminance */
+	__u32 hdr_min_luminance;      /* Min Luminance */
+};
+
 #define MSM_PARAM_GPU_ID     0x01
 #define MSM_PARAM_GMEM_SIZE  0x02
 #define MSM_PARAM_CHIP_ID    0x03
@@ -324,6 +362,7 @@
 #define DRM_EVENT_SYS_BACKLIGHT 0x80000003
 #define DRM_EVENT_SDE_POWER 0x80000004
 #define DRM_EVENT_IDLE_NOTIFY 0x80000005
+#define DRM_EVENT_PANEL_DEAD 0x80000006 /* ESD event */
 
 #define DRM_IOCTL_MSM_GET_PARAM        DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param)
 #define DRM_IOCTL_MSM_GEM_NEW          DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new)
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 5f70a57..fcf84e3 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -69,7 +69,39 @@
 	__u32 val[PA_VLUT_SIZE];
 };
 
-/* struct drm_msm_memcol - Memory color feature strucuture.
+#define PA_HSIC_HUE_ENABLE (1 << 0)
+#define PA_HSIC_SAT_ENABLE (1 << 1)
+#define PA_HSIC_VAL_ENABLE (1 << 2)
+#define PA_HSIC_CONT_ENABLE (1 << 3)
+/**
+ * struct drm_msm_pa_hsic - pa hsic feature structure
+ * @flags: flags for the feature customization, values can be:
+ *         - PA_HSIC_HUE_ENABLE: Enable hue adjustment
+ *         - PA_HSIC_SAT_ENABLE: Enable saturation adjustment
+ *         - PA_HSIC_VAL_ENABLE: Enable value adjustment
+ *         - PA_HSIC_CONT_ENABLE: Enable contrast adjustment
+ *
+ * @hue: hue setting
+ * @saturation: saturation setting
+ * @value: value setting
+ * @contrast: contrast setting
+ */
+#define DRM_MSM_PA_HSIC
+struct drm_msm_pa_hsic {
+	__u64 flags;
+	__u32 hue;
+	__u32 saturation;
+	__u32 value;
+	__u32 contrast;
+};
+
+#define MEMCOL_PROT_HUE (1 << 0)
+#define MEMCOL_PROT_SAT (1 << 1)
+#define MEMCOL_PROT_VAL (1 << 2)
+#define MEMCOL_PROT_CONT (1 << 3)
+#define MEMCOL_PROT_SIXZONE (1 << 4)
+#define MEMCOL_PROT_BLEND (1 << 5)
+/* struct drm_msm_memcol - Memory color feature structure.
  *                         Skin, sky, foliage features are supported.
  * @prot_flags: Bit mask for enabling protection feature.
  * @color_adjust_p0: Adjustment curve.
@@ -96,6 +128,42 @@
 	__u32 val_region;
 };
 
+#define DRM_MSM_SIXZONE
+#define SIXZONE_LUT_SIZE 384
+#define SIXZONE_HUE_ENABLE (1 << 0)
+#define SIXZONE_SAT_ENABLE (1 << 1)
+#define SIXZONE_VAL_ENABLE (1 << 2)
+/* struct drm_msm_sixzone_curve - Sixzone HSV adjustment curve structure.
+ * @p0: Hue adjustment.
+ * @p1: Saturation/Value adjustment.
+ */
+struct drm_msm_sixzone_curve {
+	__u32 p1;
+	__u32 p0;
+};
+
+/* struct drm_msm_sixzone - Sixzone feature structure.
+ * @flags: for feature customization, values can be:
+ *         - SIXZONE_HUE_ENABLE: Enable hue adjustment
+ *         - SIXZONE_SAT_ENABLE: Enable saturation adjustment
+ *         - SIXZONE_VAL_ENABLE: Enable value adjustment
+ * @threshold: threshold qualifier.
+ * @adjust_p0: Adjustment curve.
+ * @adjust_p1: Adjustment curve.
+ * @sat_hold: Saturation hold info.
+ * @val_hold: Value hold info.
+ * @curve: HSV adjustment curve lut.
+ */
+struct drm_msm_sixzone {
+	__u64 flags;
+	__u32 threshold;
+	__u32 adjust_p0;
+	__u32 adjust_p1;
+	__u32 sat_hold;
+	__u32 val_hold;
+	struct drm_msm_sixzone_curve curve[SIXZONE_LUT_SIZE];
+};
+
 #define GAMUT_3D_MODE_17 1
 #define GAMUT_3D_MODE_5 2
 #define GAMUT_3D_MODE_13 3
@@ -174,6 +242,17 @@
 	__u32 strength;
 };
 
+#define HIST_V_SIZE 256
+/**
+ * struct drm_msm_hist - histogram feature structure
+ * @flags: for customizing operations
+ * @data: histogram data
+ */
+struct drm_msm_hist {
+	__u64 flags;
+	__u32 data[HIST_V_SIZE];
+};
+
 #define AD4_LUT_GRP0_SIZE 33
 #define AD4_LUT_GRP1_SIZE 32
 /*
diff --git a/include/uapi/drm/sde_drm.h b/include/uapi/drm/sde_drm.h
index 285508a..1a43659 100644
--- a/include/uapi/drm/sde_drm.h
+++ b/include/uapi/drm/sde_drm.h
@@ -295,6 +295,44 @@
 	struct sde_drm_de_v1 de;
 };
 
+/* Number of dest scalers supported */
+#define SDE_MAX_DS_COUNT 2
+
+/*
+ * Destination scaler flag config
+ */
+#define SDE_DRM_DESTSCALER_ENABLE           0x1
+#define SDE_DRM_DESTSCALER_SCALE_UPDATE     0x2
+#define SDE_DRM_DESTSCALER_ENHANCER_UPDATE  0x4
+#define SDE_DRM_DESTSCALER_PU_ENABLE        0x8
+
+/**
+ * struct sde_drm_dest_scaler_cfg - destination scaler config structure
+ * @flags:      Flag to switch between mode for destination scaler
+ *              refer to destination scaler flag config
+ * @index:      Destination scaler selection index
+ * @lm_width:   Layer mixer width configuration
+ * @lm_height:  Layer mixer height configuration
+ * @scaler_cfg: The scaling parameters for all the mode except disable
+ *              Userspace pointer to struct sde_drm_scaler_v2
+ */
+struct sde_drm_dest_scaler_cfg {
+	uint32_t flags;
+	uint32_t index;
+	uint32_t lm_width;
+	uint32_t lm_height;
+	uint64_t scaler_cfg;
+};
+
+/**
+ * struct sde_drm_dest_scaler_data - destination scaler data struct
+ * @num_dest_scaler: Number of dest scalers to be configured
+ * @ds_cfg:          Destination scaler block configuration
+ */
+struct sde_drm_dest_scaler_data {
+	uint32_t num_dest_scaler;
+	struct sde_drm_dest_scaler_cfg ds_cfg[SDE_MAX_DS_COUNT];
+};
 
 /*
  * Define constants for struct sde_drm_csc
diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h
index 4201c95..45f3222 100644
--- a/include/uapi/linux/esoc_ctrl.h
+++ b/include/uapi/linux/esoc_ctrl.h
@@ -9,6 +9,7 @@
 #define ESOC_WAIT_FOR_REQ	_IOR(ESOC_CODE, 2, unsigned int)
 #define ESOC_NOTIFY		_IOW(ESOC_CODE, 3, unsigned int)
 #define ESOC_GET_STATUS		_IOR(ESOC_CODE, 4, unsigned int)
+#define ESOC_GET_ERR_FATAL	_IOR(ESOC_CODE, 5, unsigned int)
 #define ESOC_WAIT_FOR_CRASH	_IOR(ESOC_CODE, 6, unsigned int)
 #define ESOC_REG_REQ_ENG	_IO(ESOC_CODE, 7)
 #define ESOC_REG_CMD_ENG	_IO(ESOC_CODE, 8)
@@ -17,6 +18,7 @@
 #define HSIC		"HSIC"
 #define HSICPCIe	"HSIC+PCIe"
 #define PCIe		"PCIe"
+#define ESOC_REQ_SEND_SHUTDOWN	ESOC_REQ_SEND_SHUTDOWN
 
 enum esoc_evt {
 	ESOC_RUN_STATE = 0x1,
@@ -57,6 +59,7 @@
 	ESOC_REQ_IMG = 1,
 	ESOC_REQ_DEBUG,
 	ESOC_REQ_SHUTDOWN,
+	ESOC_REQ_SEND_SHUTDOWN,
 };
 
 #ifdef __KERNEL__
diff --git a/include/uapi/linux/fib_rules.h b/include/uapi/linux/fib_rules.h
index a66c4ba..bbf02a6 100644
--- a/include/uapi/linux/fib_rules.h
+++ b/include/uapi/linux/fib_rules.h
@@ -54,8 +54,6 @@
 	FRA_TABLE,	/* Extended table id */
 	FRA_FWMASK,	/* mask for netfilter mark */
 	FRA_OIFNAME,
-	FRA_UID_START,	/* UID range */
-	FRA_UID_END,
 	FRA_PAD,
 	FRA_L3MDEV,	/* iif or oif is l3mdev goto its table */
 	FRA_UID_RANGE,	/* UID range */
diff --git a/include/uapi/linux/ipa_qmi_service_v01.h b/include/uapi/linux/ipa_qmi_service_v01.h
index 1ba819b..1917c0d 100644
--- a/include/uapi/linux/ipa_qmi_service_v01.h
+++ b/include/uapi/linux/ipa_qmi_service_v01.h
@@ -47,6 +47,12 @@
 #define QMI_IPA_MAX_FILTERS_EX_V01 128
 #define QMI_IPA_MAX_PIPES_V01 20
 #define QMI_IPA_MAX_APN_V01 8
+#define QMI_IPA_MAX_PER_CLIENTS_V01 64
+/* Currently max we can use is only 1. But for scalability purpose
+ * we are having max value as 8.
+ */
+#define QMI_IPA_MAX_CLIENT_DST_PIPES_V01 8
+#define QMI_IPA_MAX_UL_FIREWALL_RULES_V01 64
 
 #define IPA_INT_MAX	((int)(~0U>>1))
 #define IPA_INT_MIN	(-IPA_INT_MAX - 1)
@@ -989,6 +995,16 @@
 	 *	failure, the Rule Ids in this list must be set to a reserved
 	 *	index (255).
 	 */
+
+	/* Optional */
+	/*	List of destination pipe IDs. */
+	uint8_t dst_pipe_id_valid;
+	/* Must be set to true if dst_pipe_id is being passed. */
+	uint32_t dst_pipe_id_len;
+	/* Must be set to # of elements in dst_pipe_id. */
+	uint32_t dst_pipe_id[QMI_IPA_MAX_CLIENT_DST_PIPES_V01];
+	/* Provides the list of destination pipe IDs for a source pipe. */
+
 };  /* Message */
 
 /* Response Message; This is the message that is exchanged between the
@@ -1626,6 +1642,273 @@
 	 */
 };  /* Message */
 
+/*
+ * Request Message; Requests the modem IPA driver to enable or
+ * disable collection of per client statistics.
+ */
+struct ipa_enable_per_client_stats_req_msg_v01 {
+
+	/* Mandatory */
+	/* Collect statistics per client; */
+	uint8_t enable_per_client_stats;
+	/*
+	 * Indicates whether to start or stop collecting
+	 * per client statistics.
+	 */
+};  /* Message */
+
+/*
+ * Response Message; Requests the modem IPA driver to enable or disable
+ * collection of per client statistics.
+ */
+struct ipa_enable_per_client_stats_resp_msg_v01 {
+
+	/* Mandatory */
+	/*  Result Code */
+	struct ipa_qmi_response_type_v01 resp;
+	/* Standard response type. */
+};  /* Message */
+
+struct ipa_per_client_stats_info_type_v01 {
+
+	uint32_t client_id;
+	/*
+	 * Id of the client on APPS processor side for which Modem processor
+	 * needs to send uplink/downlink statistics.
+	 */
+
+	uint32_t src_pipe_id;
+	/*
+	 * IPA consumer pipe on which client on APPS side sent uplink
+	 * data to modem.
+	 */
+
+	uint64_t num_ul_ipv4_bytes;
+	/*
+	 * Accumulated number of uplink IPv4 bytes for a client.
+	 */
+
+	uint64_t num_ul_ipv6_bytes;
+	/*
+	 * Accumulated number of uplink IPv6 bytes for a client.
+	 */
+
+	uint64_t num_dl_ipv4_bytes;
+	/*
+	 * Accumulated number of downlink IPv4 bytes for a client.
+	 */
+
+	uint64_t num_dl_ipv6_bytes;
+	/*
+	 * Accumulated number of downlink IPv6 byes for a client.
+	 */
+
+
+	uint32_t num_ul_ipv4_pkts;
+	/*
+	 * Accumulated number of uplink IPv4 packets for a client.
+	 */
+
+	uint32_t num_ul_ipv6_pkts;
+	/*
+	 * Accumulated number of uplink IPv6 packets for a client.
+	 */
+
+	uint32_t num_dl_ipv4_pkts;
+	/*
+	 * Accumulated number of downlink IPv4 packets for a client.
+	 */
+
+	uint32_t num_dl_ipv6_pkts;
+	/*
+	 * Accumulated number of downlink IPv6 packets for a client.
+	 */
+};  /* Type */
+
+/*
+ * Request Message; Requests the modem IPA driver to provide statistics
+ * for a givenclient.
+ */
+struct ipa_get_stats_per_client_req_msg_v01 {
+
+	/* Mandatory */
+	/*  Client id */
+	uint32_t client_id;
+	/*
+	 * Id of the client on APPS processor side for which Modem processor
+	 * needs to send uplink/downlink statistics. if client id is specified
+	 * as 0xffffffff, then Q6 will send the stats for all the clients of
+	 * the specified source pipe.
+	 */
+
+	/* Mandatory */
+	/*  Source pipe id */
+	uint32_t src_pipe_id;
+	/*
+	 * IPA consumer pipe on which client on APPS side sent uplink
+	 * data to modem. In future, this implementation can be extended
+	 * to provide 0xffffffff as the source pipe id, where Q6 will send
+	 * the stats of all the clients across all different tethered-pipes.
+	 */
+
+	/* Optional */
+	/*  Reset client statistics. */
+	uint8_t reset_stats_valid;
+	/* Must be set to true if reset_stats is being passed. */
+	uint8_t reset_stats;
+	/*
+	 * Option to reset the statistics currently collected by modem for this
+	 * particular client.
+	 */
+};  /* Message */
+
+/*
+ * Response Message; Requests the modem IPA driver to provide statistics
+ * for a given client.
+ */
+struct ipa_get_stats_per_client_resp_msg_v01 {
+
+	/* Mandatory */
+	/*  Result Code */
+	struct ipa_qmi_response_type_v01 resp;
+	/* Standard response type. */
+
+	/* Optional */
+	/*  Per clients Statistics List */
+	uint8_t per_client_stats_list_valid;
+	/* Must be set to true if per_client_stats_list is being passed. */
+	uint32_t per_client_stats_list_len;
+	/* Must be set to # of elements in per_client_stats_list. */
+	struct ipa_per_client_stats_info_type_v01
+		per_client_stats_list[QMI_IPA_MAX_PER_CLIENTS_V01];
+	/*
+	 * List of all per client statistics that are retrieved.
+	 */
+};  /* Message */
+
+struct ipa_ul_firewall_rule_type_v01 {
+
+	enum ipa_ip_type_enum_v01 ip_type;
+	/*
+	 * IP type for which this rule is applicable.
+	 * The driver must identify the filter table (v6 or v4), and this
+	 * field is essential for that. Values:
+	 * - QMI_IPA_IP_TYPE_INVALID (0) --  Invalid IP type identifier
+	 * - QMI_IPA_IP_TYPE_V4 (1) --  IPv4 type
+	 * - QMI_IPA_IP_TYPE_V6 (2) --  IPv6 type
+	 */
+
+	struct ipa_filter_rule_type_v01 filter_rule;
+	/*
+	 * Rules in the filter specification. These rules are the
+	 * ones that are matched against fields in the packet.
+	 * Currently we only send IPv6 whitelist rules to Q6.
+	 */
+};  /* Type */
+
+/*
+ * Request Message; Requestes remote IPA driver to install uplink
+ * firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_req_msg_v01 {
+
+	/* Optional */
+	/*  Uplink Firewall Specification  */
+	uint32_t firewall_rules_list_len;
+	/* Must be set to # of elements in firewall_rules_list. */
+	struct ipa_ul_firewall_rule_type_v01
+		firewall_rules_list[QMI_IPA_MAX_UL_FIREWALL_RULES_V01];
+	/*
+	 * List of uplink firewall specifications of filters that must be
+	 * installed.
+	 */
+
+	uint32_t mux_id;
+	/*
+	 * QMAP Mux ID. As a part of the QMAP protocol,
+	 * several data calls may be multiplexed over the same physical
+	 * transport channel. This identifier is used to identify one
+	 * such data call. The maximum value for this identifier is 255.
+	 */
+
+	/* Optional */
+	uint8_t disable_valid;
+	/* Must be set to true if enable is being passed. */
+	uint8_t disable;
+	/*
+	 * Indicates whether uplink firewall needs to be enabled or disabled.
+	 */
+
+	/* Optional */
+	uint8_t are_blacklist_filters_valid;
+	/* Must be set to true if are_blacklist_filters is being passed. */
+	uint8_t are_blacklist_filters;
+	/*
+	 * Indicates whether the filters received as part of this message are
+	 * blacklist filters. i.e. drop uplink packets matching these rules.
+	 */
+};  /* Message */
+
+/*
+ * Response Message; Requestes remote IPA driver to install
+ * uplink firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_resp_msg_v01 {
+
+	/* Mandatory */
+	/* Result Code */
+	struct ipa_qmi_response_type_v01 resp;
+	/*
+	 * Standard response type.
+	 * Standard response type. Contains the following data members:
+	 * qmi_result_type -- QMI_RESULT_SUCCESS or QMI_RESULT_FAILURE
+	 * qmi_error_type  -- Error code. Possible error code values are
+	 * described in the error codes section of each message definition.
+	 */
+};  /* Message */
+
+enum ipa_ul_firewall_status_enum_v01 {
+	IPA_UL_FIREWALL_STATUS_ENUM_MIN_ENUM_VAL_V01 = -2147483647,
+	/* To force a 32 bit signed enum.  Do not change or use*/
+	QMI_IPA_UL_FIREWALL_STATUS_SUCCESS_V01 = 0,
+	/* Indicates that the uplink firewall rules
+	 * are configured successfully.
+	 */
+	QMI_IPA_UL_FIREWALL_STATUS_FAILURE_V01 = 1,
+	/* Indicates that the uplink firewall rules
+	 * are not configured successfully.
+	 */
+	IPA_UL_FIREWALL_STATUS_ENUM_MAX_ENUM_VAL_V01 = 2147483647
+	/* To force a 32 bit signed enum.  Do not change or use*/
+};
+
+struct ipa_ul_firewall_config_result_type_v01 {
+
+	enum ipa_ul_firewall_status_enum_v01 is_success;
+	/*
+	 * Indicates whether the uplink firewall rules are configured
+	 * successfully.
+	 */
+
+	uint32_t mux_id;
+	/*
+	 * QMAP Mux ID. As a part of the QMAP protocol,
+	 * several data calls may be multiplexed over the same physical
+	 * transport channel. This identifier is used to identify one
+	 * such data call. The maximum value for this identifier is 255.
+	 */
+};
+
+/*
+ * Indication Message; Requestes remote IPA driver to install
+ * uplink firewall rules.
+ */
+struct ipa_configure_ul_firewall_rules_ind_msg_v01 {
+
+	 struct ipa_ul_firewall_config_result_type_v01 result;
+};  /* Message */
+
+
 /*Service Message Definition*/
 #define QMI_IPA_INDICATION_REGISTER_REQ_V01 0x0020
 #define QMI_IPA_INDICATION_REGISTER_RESP_V01 0x0020
@@ -1659,6 +1942,13 @@
 #define QMI_IPA_INIT_MODEM_DRIVER_CMPLT_RESP_V01 0x0035
 #define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_V01 0x0037
 #define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_V01 0x0037
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_V01 0x0038
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_V01 0x0038
+#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_V01 0x0039
+#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_V01 0x0039
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_V01 0x003A
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_V01 0x003A
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_V01 0x003A
 
 /* add for max length*/
 #define QMI_IPA_INIT_MODEM_DRIVER_REQ_MAX_MSG_LEN_V01 134
@@ -1667,7 +1957,7 @@
 #define QMI_IPA_INDICATION_REGISTER_RESP_MAX_MSG_LEN_V01 7
 #define QMI_IPA_INSTALL_FILTER_RULE_REQ_MAX_MSG_LEN_V01 22369
 #define QMI_IPA_INSTALL_FILTER_RULE_RESP_MAX_MSG_LEN_V01 783
-#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 834
+#define QMI_IPA_FILTER_INSTALLED_NOTIF_REQ_MAX_MSG_LEN_V01 870
 #define QMI_IPA_FILTER_INSTALLED_NOTIF_RESP_MAX_MSG_LEN_V01 7
 #define QMI_IPA_MASTER_DRIVER_INIT_COMPLETE_IND_MAX_MSG_LEN_V01 7
 #define QMI_IPA_DATA_USAGE_QUOTA_REACHED_IND_MAX_MSG_LEN_V01 15
@@ -1700,6 +1990,15 @@
 #define QMI_IPA_INSTALL_FILTER_RULE_EX_REQ_MAX_MSG_LEN_V01 22685
 #define QMI_IPA_INSTALL_FILTER_RULE_EX_RESP_MAX_MSG_LEN_V01 523
 
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_REQ_MAX_MSG_LEN_V01 4
+#define QMI_IPA_ENABLE_PER_CLIENT_STATS_RESP_MAX_MSG_LEN_V01 7
+
+#define QMI_IPA_GET_STATS_PER_CLIENT_REQ_MAX_MSG_LEN_V01 18
+#define QMI_IPA_GET_STATS_PER_CLIENT_RESP_MAX_MSG_LEN_V01 3595
+
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_REQ_MAX_MSG_LEN_V01 9875
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_RESP_MAX_MSG_LEN_V01 7
+#define QMI_IPA_INSTALL_UL_FIREWALL_RULES_IND_MAX_MSG_LEN_V01 11
 /* Service Object Accessor */
 
 #endif/* IPA_QMI_SERVICE_V01_H */
diff --git a/include/uapi/linux/mroute6.h b/include/uapi/linux/mroute6.h
index 5062fb5..ed57211 100644
--- a/include/uapi/linux/mroute6.h
+++ b/include/uapi/linux/mroute6.h
@@ -4,6 +4,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/sockios.h>
+#include <linux/in6.h>		/* For struct sockaddr_in6. */
 
 /*
  *	Based on the MROUTING 3.5 defines primarily to keep
diff --git a/include/uapi/linux/msm_ipa.h b/include/uapi/linux/msm_ipa.h
index 51c0165..d3b9a33 100644
--- a/include/uapi/linux/msm_ipa.h
+++ b/include/uapi/linux/msm_ipa.h
@@ -16,65 +16,85 @@
 #define IPA_IOC_MAGIC 0xCF
 
 /**
+ * IPA device full path
+ */
+#define IPA_DEV_NAME "/dev/ipa"
+
+/**
+ * IPA NAT table character device name
+ */
+#define IPA_NAT_DEV_NAME "ipaNatTable"
+
+/**
+ * IPA IPv6CT table character device name
+ */
+#define IPA_IPV6CT_DEV_NAME "ipaIpv6CTTable"
+
+ /**
  * name of the default routing tables for v4 and v6
  */
 #define IPA_DFLT_RT_TBL_NAME "ipa_dflt_rt"
 
 /**
- *   the commands supported by IPA driver
+ * commands supported by IPA driver
  */
-#define IPA_IOCTL_ADD_HDR            0
-#define IPA_IOCTL_DEL_HDR            1
-#define IPA_IOCTL_ADD_RT_RULE        2
-#define IPA_IOCTL_DEL_RT_RULE        3
-#define IPA_IOCTL_ADD_FLT_RULE       4
-#define IPA_IOCTL_DEL_FLT_RULE       5
-#define IPA_IOCTL_COMMIT_HDR         6
-#define IPA_IOCTL_RESET_HDR          7
-#define IPA_IOCTL_COMMIT_RT          8
-#define IPA_IOCTL_RESET_RT           9
-#define IPA_IOCTL_COMMIT_FLT        10
-#define IPA_IOCTL_RESET_FLT         11
-#define IPA_IOCTL_DUMP              12
-#define IPA_IOCTL_GET_RT_TBL        13
-#define IPA_IOCTL_PUT_RT_TBL        14
-#define IPA_IOCTL_COPY_HDR          15
-#define IPA_IOCTL_QUERY_INTF        16
-#define IPA_IOCTL_QUERY_INTF_TX_PROPS 17
-#define IPA_IOCTL_QUERY_INTF_RX_PROPS 18
-#define IPA_IOCTL_GET_HDR           19
-#define IPA_IOCTL_PUT_HDR           20
-#define IPA_IOCTL_SET_FLT        21
-#define IPA_IOCTL_ALLOC_NAT_MEM  22
-#define IPA_IOCTL_V4_INIT_NAT    23
-#define IPA_IOCTL_NAT_DMA        24
-#define IPA_IOCTL_V4_DEL_NAT     26
-#define IPA_IOCTL_PULL_MSG       27
-#define IPA_IOCTL_GET_NAT_OFFSET 28
-#define IPA_IOCTL_RM_ADD_DEPENDENCY 29
-#define IPA_IOCTL_RM_DEL_DEPENDENCY 30
-#define IPA_IOCTL_GENERATE_FLT_EQ 31
-#define IPA_IOCTL_QUERY_INTF_EXT_PROPS 32
-#define IPA_IOCTL_QUERY_EP_MAPPING 33
-#define IPA_IOCTL_QUERY_RT_TBL_INDEX 34
-#define IPA_IOCTL_WRITE_QMAPID 35
-#define IPA_IOCTL_MDFY_FLT_RULE 36
-#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD	37
-#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL	38
-#define IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED	39
-#define IPA_IOCTL_ADD_HDR_PROC_CTX 40
-#define IPA_IOCTL_DEL_HDR_PROC_CTX 41
-#define IPA_IOCTL_MDFY_RT_RULE 42
-#define IPA_IOCTL_ADD_RT_RULE_AFTER 43
-#define IPA_IOCTL_ADD_FLT_RULE_AFTER 44
-#define IPA_IOCTL_GET_HW_VERSION 45
-#define IPA_IOCTL_ADD_RT_RULE_EXT 46
-#define IPA_IOCTL_ADD_VLAN_IFACE 47
-#define IPA_IOCTL_DEL_VLAN_IFACE 48
-#define IPA_IOCTL_ADD_L2TP_VLAN_MAPPING 49
-#define IPA_IOCTL_DEL_L2TP_VLAN_MAPPING 50
-#define IPA_IOCTL_NAT_MODIFY_PDN 51
-#define IPA_IOCTL_MAX 52
+#define IPA_IOCTL_ADD_HDR                       0
+#define IPA_IOCTL_DEL_HDR                       1
+#define IPA_IOCTL_ADD_RT_RULE                   2
+#define IPA_IOCTL_DEL_RT_RULE                   3
+#define IPA_IOCTL_ADD_FLT_RULE                  4
+#define IPA_IOCTL_DEL_FLT_RULE                  5
+#define IPA_IOCTL_COMMIT_HDR                    6
+#define IPA_IOCTL_RESET_HDR                     7
+#define IPA_IOCTL_COMMIT_RT                     8
+#define IPA_IOCTL_RESET_RT                      9
+#define IPA_IOCTL_COMMIT_FLT                    10
+#define IPA_IOCTL_RESET_FLT                     11
+#define IPA_IOCTL_DUMP                          12
+#define IPA_IOCTL_GET_RT_TBL                    13
+#define IPA_IOCTL_PUT_RT_TBL                    14
+#define IPA_IOCTL_COPY_HDR                      15
+#define IPA_IOCTL_QUERY_INTF                    16
+#define IPA_IOCTL_QUERY_INTF_TX_PROPS           17
+#define IPA_IOCTL_QUERY_INTF_RX_PROPS           18
+#define IPA_IOCTL_GET_HDR                       19
+#define IPA_IOCTL_PUT_HDR                       20
+#define IPA_IOCTL_SET_FLT                       21
+#define IPA_IOCTL_ALLOC_NAT_MEM                 22
+#define IPA_IOCTL_V4_INIT_NAT                   23
+#define IPA_IOCTL_TABLE_DMA_CMD                 24
+#define IPA_IOCTL_NAT_DMA                       IPA_IOCTL_TABLE_DMA_CMD
+#define IPA_IOCTL_INIT_IPV6CT_TABLE             25
+#define IPA_IOCTL_V4_DEL_NAT                    26
+#define IPA_IOCTL_PULL_MSG                      27
+#define IPA_IOCTL_GET_NAT_OFFSET                28
+#define IPA_IOCTL_RM_ADD_DEPENDENCY             29
+#define IPA_IOCTL_RM_DEL_DEPENDENCY             30
+#define IPA_IOCTL_GENERATE_FLT_EQ               31
+#define IPA_IOCTL_QUERY_INTF_EXT_PROPS          32
+#define IPA_IOCTL_QUERY_EP_MAPPING              33
+#define IPA_IOCTL_QUERY_RT_TBL_INDEX            34
+#define IPA_IOCTL_WRITE_QMAPID                  35
+#define IPA_IOCTL_MDFY_FLT_RULE                 36
+#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_ADD 37
+#define IPA_IOCTL_NOTIFY_WAN_UPSTREAM_ROUTE_DEL 38
+#define IPA_IOCTL_NOTIFY_WAN_EMBMS_CONNECTED    39
+#define IPA_IOCTL_ADD_HDR_PROC_CTX              40
+#define IPA_IOCTL_DEL_HDR_PROC_CTX              41
+#define IPA_IOCTL_MDFY_RT_RULE                  42
+#define IPA_IOCTL_ADD_RT_RULE_AFTER             43
+#define IPA_IOCTL_ADD_FLT_RULE_AFTER            44
+#define IPA_IOCTL_GET_HW_VERSION                45
+#define IPA_IOCTL_ADD_RT_RULE_EXT               46
+#define IPA_IOCTL_ADD_VLAN_IFACE                47
+#define IPA_IOCTL_DEL_VLAN_IFACE                48
+#define IPA_IOCTL_ADD_L2TP_VLAN_MAPPING         49
+#define IPA_IOCTL_DEL_L2TP_VLAN_MAPPING         50
+#define IPA_IOCTL_NAT_MODIFY_PDN                51
+#define IPA_IOCTL_ALLOC_NAT_TABLE               52
+#define IPA_IOCTL_ALLOC_IPV6CT_TABLE            53
+#define IPA_IOCTL_DEL_NAT_TABLE                 54
+#define IPA_IOCTL_DEL_IPV6CT_TABLE              55
 
 /**
  * max size of the header to be inserted
@@ -107,6 +127,17 @@
 #define IPA_WAN_MSG_IPv6_ADDR_GW_LEN 4
 
 /**
+ * max number of lan clients supported per device type
+ * for LAN stats via HW.
+ */
+#define IPA_MAX_NUM_HW_PATH_CLIENTS 16
+
+/**
+ * max number of destination pipes possible for a client.
+ */
+#define QMI_IPA_MAX_CLIENT_DST_PIPES 4
+
+/**
  * the attributes of the rule (routing or filtering)
  */
 #define IPA_FLT_TOS			(1ul << 0)
@@ -450,6 +481,7 @@
 	IPA_TETHERING_STATS_EVENT_MAX,
 };
 
+
 enum ipa_quota_event {
 	IPA_QUOTA_REACH = IPA_TETHERING_STATS_EVENT_MAX,
 	IPA_QUOTA_EVENT_MAX,
@@ -469,7 +501,13 @@
 	IPA_VLAN_L2TP_EVENT_MAX,
 };
 
-#define IPA_EVENT_MAX_NUM (IPA_VLAN_L2TP_EVENT_MAX)
+enum ipa_per_client_stats_event {
+	IPA_PER_CLIENT_STATS_CONNECT_EVENT = IPA_VLAN_L2TP_EVENT_MAX,
+	IPA_PER_CLIENT_STATS_DISCONNECT_EVENT,
+	IPA_PER_CLIENT_STATS_EVENT_MAX
+};
+
+#define IPA_EVENT_MAX_NUM (IPA_PER_CLIENT_STATS_EVENT_MAX)
 #define IPA_EVENT_MAX ((int)IPA_EVENT_MAX_NUM)
 
 /**
@@ -1155,6 +1193,48 @@
 };
 
 /**
+ * struct ipa_rt_rule_add_ext - routing rule descriptor includes in
+ * and out parameters
+ * @rule: actual rule to be added
+ * @at_rear:	add at back of routing table, it is NOT possible to add rules at
+ *		the rear of the "default" routing tables
+ * @rt_rule_hdl: output parameter, handle to rule, valid when status is 0
+ * @status:	output parameter, status of routing rule add operation,
+ * @rule_id: rule_id to be assigned to the routing rule. In case client
+ *  specifies rule_id as 0 the driver will assign a new rule_id
+ *		0 for success,
+ *		-1 for failure
+ */
+struct ipa_rt_rule_add_ext {
+	struct ipa_rt_rule rule;
+	uint8_t at_rear;
+	uint32_t rt_rule_hdl;
+	int status;
+	uint16_t rule_id;
+};
+
+/**
+ * struct ipa_ioc_add_rt_rule - routing rule addition parameters (supports
+ * multiple rules and commit with rule_id);
+ *
+ * all rules MUST be added to same table
+ * @commit: should rules be written to IPA HW also?
+ * @ip: IP family of rule
+ * @rt_tbl_name: name of routing table resource
+ * @num_rules: number of routing rules that follow
+ * @ipa_rt_rule_add_ext rules: all rules need to go back to back here,
+ *  no pointers
+ */
+struct ipa_ioc_add_rt_rule_ext {
+	uint8_t commit;
+	enum ipa_ip_type ip;
+	char rt_tbl_name[IPA_RESOURCE_NAME_MAX];
+	uint8_t num_rules;
+	struct ipa_rt_rule_add_ext rules[0];
+};
+
+
+/**
  * struct ipa_ioc_del_rt_rule - routing rule deletion parameters (supports
  * multiple headers and commit)
  * @commit: should rules be removed from IPA HW also?
@@ -1442,15 +1522,26 @@
 };
 
 /**
- * struct ipa_ioc_v4_nat_init - nat table initialization
- * parameters
+ * struct ipa_ioc_nat_ipv6ct_table_alloc - NAT/IPv6CT table memory allocation
+ * properties
+ * @size: input parameter, size of table in bytes
+ * @offset: output parameter, offset into page in case of system memory
+ */
+struct ipa_ioc_nat_ipv6ct_table_alloc {
+	size_t size;
+	off_t offset;
+};
+
+/**
+ * struct ipa_ioc_v4_nat_init - nat table initialization parameters
  * @tbl_index: input parameter, index of the table
  * @ipv4_rules_offset: input parameter, ipv4 rules address offset
  * @expn_rules_offset: input parameter, ipv4 expansion rules address offset
  * @index_offset: input parameter, index rules offset
  * @index_expn_offset: input parameter, index expansion rules offset
- * @table_entries: input parameter, ipv4 rules table size in entries
- * @expn_table_entries: input parameter, ipv4 expansion rules table size
+ * @table_entries: input parameter, ipv4 rules table number of entries
+ * @expn_table_entries: input parameter, ipv4 expansion rules table number of
+ *                      entries
  * @ip_addr: input parameter, public ip address
  */
 struct ipa_ioc_v4_nat_init {
@@ -1467,6 +1558,23 @@
 };
 
 /**
+ * struct ipa_ioc_ipv6ct_init - IPv6CT table initialization parameters
+ * @base_table_offset: input parameter, IPv6CT base table address offset
+ * @expn_table_offset: input parameter, IPv6CT expansion table address offset
+ * @table_entries: input parameter, IPv6CT table number of entries
+ * @expn_table_entries: input parameter, IPv6CT expansion table number of
+ *                      entries
+ * @tbl_index: input parameter, index of the table
+ */
+struct ipa_ioc_ipv6ct_init {
+	uint32_t base_table_offset;
+	uint32_t expn_table_offset;
+	uint16_t table_entries;
+	uint16_t expn_table_entries;
+	uint8_t tbl_index;
+};
+
+/**
  * struct ipa_ioc_v4_nat_del - nat table delete parameter
  * @table_index: input parameter, index of the table
  * @public_ip_addr: input parameter, public ip address
@@ -1477,7 +1585,15 @@
 };
 
 /**
- * struct ipa_ioc_nat_dma_one - nat dma command parameter
+ * struct ipa_ioc_nat_ipv6ct_table_del - NAT/IPv6CT table delete parameter
+ * @table_index: input parameter, index of the table
+ */
+struct ipa_ioc_nat_ipv6ct_table_del {
+	uint8_t table_index;
+};
+
+/**
+ * struct ipa_ioc_nat_dma_one - nat/ipv6ct dma command parameter
  * @table_index: input parameter, index of the table
  * @base_addr:	type of table, from which the base address of the table
  *		can be inferred
@@ -1494,7 +1610,7 @@
 };
 
 /**
- * struct ipa_ioc_nat_dma_cmd - To hold multiple nat dma commands
+ * struct ipa_ioc_nat_dma_cmd - To hold multiple nat/ipv6ct dma commands
  * @entries: number of dma commands in use
  * @dma: data pointer to the dma commands
  */
@@ -1505,12 +1621,12 @@
 };
 
 /**
-* struct ipa_ioc_nat_pdn_entry - PDN entry modification data
-* @pdn_index: index of the entry in the PDN config table to be changed
-* @public_ip: PDN's public ip
-* @src_metadata: PDN's source NAT metadata for metadata replacement
-* @dst_metadata: PDN's destination NAT metadata for metadata replacement
-*/
+ * struct ipa_ioc_nat_pdn_entry - PDN entry modification data
+ * @pdn_index: index of the entry in the PDN config table to be changed
+ * @public_ip: PDN's public ip
+ * @src_metadata: PDN's source NAT metadata for metadata replacement
+ * @dst_metadata: PDN's destination NAT metadata for metadata replacement
+ */
 struct ipa_ioc_nat_pdn_entry {
 	uint8_t pdn_index;
 	uint32_t public_ip;
@@ -1677,6 +1793,52 @@
 	IPACM_CLIENT_WLAN,
 	IPACM_CLIENT_MAX
 };
+
+enum ipacm_per_client_device_type {
+	IPACM_CLIENT_DEVICE_TYPE_USB = 0,
+	IPACM_CLIENT_DEVICE_TYPE_WLAN = 1,
+	IPACM_CLIENT_DEVICE_TYPE_ETH = 2
+};
+
+/**
+ * max number of device types supported.
+ */
+#define IPACM_MAX_CLIENT_DEVICE_TYPES 3
+
+/**
+ * @lanIface - Name of the lan interface
+ * @mac: Mac address of the client.
+ */
+struct ipa_lan_client_msg {
+	char lanIface[IPA_RESOURCE_NAME_MAX];
+	uint8_t mac[IPA_MAC_ADDR_SIZE];
+};
+
+/**
+ * struct ipa_lan_client - lan client data
+ * @mac: MAC Address of the client.
+ * @client_idx: Client Index.
+ * @inited: Bool to indicate whether client info is set.
+ */
+struct ipa_lan_client {
+	uint8_t mac[IPA_MAC_ADDR_SIZE];
+	int8_t client_idx;
+	uint8_t inited;
+};
+
+/**
+ * struct ipa_tether_device_info - tether device info indicated from IPACM
+ * @ul_src_pipe: Source pipe of the lan client.
+ * @hdr_len: Header length of the client.
+ * @num_clients: Number of clients connected.
+ */
+struct ipa_tether_device_info {
+	int32_t ul_src_pipe;
+	uint8_t hdr_len;
+	uint32_t num_clients;
+	struct ipa_lan_client lan_client[IPA_MAX_NUM_HW_PATH_CLIENTS];
+};
+
 /**
  *   actual IOCTLs supported by IPA driver
  */
@@ -1689,6 +1851,9 @@
 #define IPA_IOC_ADD_RT_RULE _IOWR(IPA_IOC_MAGIC, \
 					IPA_IOCTL_ADD_RT_RULE, \
 					struct ipa_ioc_add_rt_rule *)
+#define IPA_IOC_ADD_RT_RULE_EXT _IOWR(IPA_IOC_MAGIC, \
+					IPA_IOCTL_ADD_RT_RULE_EXT, \
+					struct ipa_ioc_add_rt_rule_ext *)
 #define IPA_IOC_ADD_RT_RULE_AFTER _IOWR(IPA_IOC_MAGIC, \
 					IPA_IOCTL_ADD_RT_RULE_AFTER, \
 					struct ipa_ioc_add_rt_rule_after *)
@@ -1752,15 +1917,33 @@
 #define IPA_IOC_ALLOC_NAT_MEM _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_ALLOC_NAT_MEM, \
 				struct ipa_ioc_nat_alloc_mem *)
+#define IPA_IOC_ALLOC_NAT_TABLE _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_NAT_TABLE, \
+				struct ipa_ioc_nat_ipv6ct_table_alloc *)
+#define IPA_IOC_ALLOC_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_ALLOC_IPV6CT_TABLE, \
+				struct ipa_ioc_nat_ipv6ct_table_alloc *)
 #define IPA_IOC_V4_INIT_NAT _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_V4_INIT_NAT, \
 				struct ipa_ioc_v4_nat_init *)
+#define IPA_IOC_INIT_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_INIT_IPV6CT_TABLE, \
+				struct ipa_ioc_ipv6ct_init *)
 #define IPA_IOC_NAT_DMA _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_NAT_DMA, \
 				struct ipa_ioc_nat_dma_cmd *)
+#define IPA_IOC_TABLE_DMA_CMD _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_TABLE_DMA_CMD, \
+				struct ipa_ioc_nat_dma_cmd *)
 #define IPA_IOC_V4_DEL_NAT _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_V4_DEL_NAT, \
 				struct ipa_ioc_v4_nat_del *)
+#define IPA_IOC_DEL_NAT_TABLE _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_NAT_TABLE, \
+				struct ipa_ioc_nat_ipv6ct_table_del *)
+#define IPA_IOC_DEL_IPV6CT_TABLE _IOWR(IPA_IOC_MAGIC, \
+				IPA_IOCTL_DEL_IPV6CT_TABLE, \
+				struct ipa_ioc_nat_ipv6ct_table_del *)
 #define IPA_IOC_GET_NAT_OFFSET _IOWR(IPA_IOC_MAGIC, \
 				IPA_IOCTL_GET_NAT_OFFSET, \
 				uint32_t *)
diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h
index 6d074d1..1d7cd67 100644
--- a/include/uapi/linux/netfilter/nf_conntrack_common.h
+++ b/include/uapi/linux/netfilter/nf_conntrack_common.h
@@ -113,8 +113,11 @@
 	IPCT_NATSEQADJ = IPCT_SEQADJ,
 	IPCT_SECMARK,		/* new security mark has been set */
 	IPCT_LABEL,		/* new connlabel has been set */
+	IPCT_COUNTER,		/* Packet counters have matched. */
 };
 
+#define IPCT_COUNTER IPCT_COUNTER
+
 enum ip_conntrack_expect_events {
 	IPEXP_NEW,		/* new expectation */
 	IPEXP_DESTROY,		/* destroyed expectation */
diff --git a/include/uapi/linux/netfilter_ipv4/Kbuild b/include/uapi/linux/netfilter_ipv4/Kbuild
index ecb291d..7391cdc 100644
--- a/include/uapi/linux/netfilter_ipv4/Kbuild
+++ b/include/uapi/linux/netfilter_ipv4/Kbuild
@@ -8,3 +8,4 @@
 header-y += ipt_ah.h
 header-y += ipt_ecn.h
 header-y += ipt_ttl.h
+header-y += ipt_NATTYPE.h
diff --git a/include/uapi/linux/netfilter_ipv4/ipt_NATTYPE.h b/include/uapi/linux/netfilter_ipv4/ipt_NATTYPE.h
new file mode 100644
index 0000000..b612290
--- /dev/null
+++ b/include/uapi/linux/netfilter_ipv4/ipt_NATTYPE.h
@@ -0,0 +1,25 @@
+#ifndef _IPT_NATTYPE_H_target
+#define _IPT_NATTYPE_H_target
+
+#define NATTYPE_TIMEOUT 300
+
+enum nattype_mode {
+	MODE_DNAT,
+	MODE_FORWARD_IN,
+	MODE_FORWARD_OUT
+};
+
+enum nattype_type {
+	TYPE_PORT_ADDRESS_RESTRICTED,
+	TYPE_ENDPOINT_INDEPENDENT,
+	TYPE_ADDRESS_RESTRICTED
+};
+
+
+struct ipt_nattype_info {
+	u_int16_t mode;
+	u_int16_t type;
+};
+
+#endif /*_IPT_NATTYPE_H_target*/
+
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 8c0fc7b..9fbdc11 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -546,6 +546,12 @@
  *	well to remain backwards compatible.
  * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
  *	sent as an event when the card/driver roamed by itself.
+ *	When used as an event, and the driver roamed in a network that requires
+ *	802.1X authentication, %NL80211_ATTR_PORT_AUTHORIZED should be set
+ *	if the 802.1X authentication was done by the driver or if roaming was
+ *	done using Fast Transition protocol (in which case 802.1X authentication
+ *	is not needed). If %NL80211_ATTR_PORT_AUTHORIZED is not set, user space
+ *	is responsible for the 802.1X authentication.
  * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
  *	userspace that a connection was dropped by the AP or due to other
  *	reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
@@ -2066,6 +2072,10 @@
  *
  * @NL80211_ATTR_PMK: PMK for the PMKSA identified by %NL80211_ATTR_PMKID.
  *	This is used with @NL80211_CMD_SET_PMKSA.
+ * @NL80211_ATTR_PORT_AUTHORIZED: flag attribute used in %NL80211_CMD_ROAMED
+ *	notification indicating that that 802.1X authentication was done by
+ *	the driver or is not needed (because roaming used the Fast Transition
+ *	protocol).
  *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2486,6 +2496,13 @@
 
 	NL80211_ATTR_PMK,
 
+	NL80211_ATTR_SCHED_SCAN_MULTI,
+	NL80211_ATTR_SCHED_SCAN_MAX_REQS,
+
+	NL80211_ATTR_WANT_1X_4WAY_HS,
+	NL80211_ATTR_PMKR0_NAME,
+	NL80211_ATTR_PORT_AUTHORIZED,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h
index e5a2e68..2c5810a 100644
--- a/include/uapi/linux/pci_regs.h
+++ b/include/uapi/linux/pci_regs.h
@@ -674,6 +674,7 @@
 #define PCI_EXT_CAP_ID_PMUX	0x1A	/* Protocol Multiplexing */
 #define PCI_EXT_CAP_ID_PASID	0x1B	/* Process Address Space ID */
 #define PCI_EXT_CAP_ID_DPC	0x1D	/* Downstream Port Containment */
+#define PCI_EXT_CAP_ID_L1SS	0x1E	/* L1 PM Substates */
 #define PCI_EXT_CAP_ID_PTM	0x1F	/* Precision Time Measurement */
 #define PCI_EXT_CAP_ID_MAX	PCI_EXT_CAP_ID_PTM
 
@@ -977,4 +978,19 @@
 #define  PCI_PTM_CTRL_ENABLE		0x00000001  /* PTM enable */
 #define  PCI_PTM_CTRL_ROOT		0x00000002  /* Root select */
 
+/* L1 PM Substates */
+#define PCI_L1SS_CAP		    4	/* capability register */
+#define  PCI_L1SS_CAP_PCIPM_L1_2	 1	/* PCI PM L1.2 Support */
+#define  PCI_L1SS_CAP_PCIPM_L1_1	 2	/* PCI PM L1.1 Support */
+#define  PCI_L1SS_CAP_ASPM_L1_2		 4	/* ASPM L1.2 Support */
+#define  PCI_L1SS_CAP_ASPM_L1_1		 8	/* ASPM L1.1 Support */
+#define  PCI_L1SS_CAP_L1_PM_SS		16	/* L1 PM Substates Support */
+#define PCI_L1SS_CTL1		    8	/* Control Register 1 */
+#define  PCI_L1SS_CTL1_PCIPM_L1_2	1	/* PCI PM L1.2 Enable */
+#define  PCI_L1SS_CTL1_PCIPM_L1_1	2	/* PCI PM L1.1 Support */
+#define  PCI_L1SS_CTL1_ASPM_L1_2	4	/* ASPM L1.2 Support */
+#define  PCI_L1SS_CTL1_ASPM_L1_1	8	/* ASPM L1.1 Support */
+#define  PCI_L1SS_CTL1_L1SS_MASK	0x0000000F
+#define PCI_L1SS_CTL2		    0xC	/* Control Register 2 */
+
 #endif /* LINUX_PCI_REGS_H */
diff --git a/include/uapi/linux/rds.h b/include/uapi/linux/rds.h
index 0f9265c..7af20a1 100644
--- a/include/uapi/linux/rds.h
+++ b/include/uapi/linux/rds.h
@@ -35,6 +35,7 @@
 #define _LINUX_RDS_H
 
 #include <linux/types.h>
+#include <linux/socket.h>		/* For __kernel_sockaddr_storage. */
 
 #define RDS_IB_ABI_VERSION		0x301
 
@@ -223,7 +224,7 @@
 };
 
 struct rds_get_mr_for_dest_args {
-	struct sockaddr_storage	dest_addr;
+	struct __kernel_sockaddr_storage dest_addr;
 	struct rds_iovec 	vec;
 	uint64_t		cookie_addr;
 	uint64_t		flags;
diff --git a/include/uapi/linux/rmnet_ipa_fd_ioctl.h b/include/uapi/linux/rmnet_ipa_fd_ioctl.h
index f04ac49..2992e2c 100644
--- a/include/uapi/linux/rmnet_ipa_fd_ioctl.h
+++ b/include/uapi/linux/rmnet_ipa_fd_ioctl.h
@@ -33,6 +33,13 @@
 #define WAN_IOCTL_QUERY_DL_FILTER_STATS  8
 #define WAN_IOCTL_ADD_FLT_RULE_EX        9
 #define WAN_IOCTL_QUERY_TETHER_STATS_ALL  10
+#define WAN_IOCTL_NOTIFY_WAN_STATE  11
+#define WAN_IOCTL_ADD_UL_FLT_RULE          12
+#define WAN_IOCTL_ENABLE_PER_CLIENT_STATS    13
+#define WAN_IOCTL_QUERY_PER_CLIENT_STATS     14
+#define WAN_IOCTL_SET_LAN_CLIENT_INFO        15
+#define WAN_IOCTL_CLEAR_LAN_CLIENT_INFO      16
+#define WAN_IOCTL_SEND_LAN_CLIENT_MSG        17
 
 /* User space may not have this defined. */
 #ifndef IFNAMSIZ
@@ -126,6 +133,60 @@
 	uint32_t index;
 };
 
+struct wan_ioctl_notify_wan_state {
+	uint8_t up;
+};
+struct wan_ioctl_send_lan_client_msg {
+	/* Lan client info. */
+	struct ipa_lan_client_msg lan_client;
+	/* Event to indicate whether client is
+	 * connected or disconnected.
+	 */
+	enum ipa_per_client_stats_event client_event;
+};
+
+struct wan_ioctl_lan_client_info {
+	/* Device type of the client. */
+	enum ipacm_per_client_device_type device_type;
+	/* MAC Address of the client. */
+	uint8_t mac[IPA_MAC_ADDR_SIZE];
+	/* Init client. */
+	uint8_t client_init;
+	/* Client Index */
+	int8_t client_idx;
+	/* Header length of the client. */
+	uint8_t hdr_len;
+	/* Source pipe of the lan client. */
+	enum ipa_client_type ul_src_pipe;
+};
+
+struct wan_ioctl_per_client_info {
+	/* MAC Address of the client. */
+	uint8_t mac[IPA_MAC_ADDR_SIZE];
+	/* Ipv4 UL traffic bytes. */
+	uint64_t ipv4_tx_bytes;
+	/* Ipv4 DL traffic bytes. */
+	uint64_t ipv4_rx_bytes;
+	/* Ipv6 UL traffic bytes. */
+	uint64_t ipv6_tx_bytes;
+	/* Ipv6 DL traffic bytes. */
+	uint64_t ipv6_rx_bytes;
+};
+
+struct wan_ioctl_query_per_client_stats {
+	/* Device type of the client. */
+	enum ipacm_per_client_device_type device_type;
+	/* Indicate whether to reset the stats (use 1) or not */
+	uint8_t reset_stats;
+	/* Indicates whether client is disconnected. */
+	uint8_t disconnect_clnt;
+	/* Number of clients. */
+	uint8_t num_clients;
+	/* Client information. */
+	struct wan_ioctl_per_client_info
+		client_info[IPA_MAX_NUM_HW_PATH_CLIENTS];
+};
+
 #define WAN_IOC_ADD_FLT_RULE _IOWR(WAN_IOC_MAGIC, \
 		WAN_IOCTL_ADD_FLT_RULE, \
 		struct ipa_install_fltr_rule_req_msg_v01 *)
@@ -170,4 +231,31 @@
 		WAN_IOCTL_QUERY_TETHER_STATS_ALL, \
 		struct wan_ioctl_query_tether_stats_all *)
 
+#define WAN_IOC_NOTIFY_WAN_STATE _IOWR(WAN_IOC_MAGIC, \
+		WAN_IOCTL_NOTIFY_WAN_STATE, \
+		struct wan_ioctl_notify_wan_state *)
+
+#define WAN_IOC_ADD_UL_FLT_RULE _IOWR(WAN_IOC_MAGIC, \
+		WAN_IOCTL_ADD_UL_FLT_RULE, \
+		struct ipa_configure_ul_firewall_rules_req_msg_v01 *)
+
+#define WAN_IOC_ENABLE_PER_CLIENT_STATS _IOWR(WAN_IOC_MAGIC, \
+		WAN_IOCTL_ENABLE_PER_CLIENT_STATS, \
+		bool *)
+
+#define WAN_IOC_QUERY_PER_CLIENT_STATS _IOWR(WAN_IOC_MAGIC, \
+		WAN_IOCTL_QUERY_PER_CLIENT_STATS, \
+		struct wan_ioctl_query_per_client_stats *)
+
+#define WAN_IOC_SET_LAN_CLIENT_INFO _IOWR(WAN_IOC_MAGIC, \
+			WAN_IOCTL_SET_LAN_CLIENT_INFO, \
+			struct wan_ioctl_lan_client_info *)
+
+#define WAN_IOC_SEND_LAN_CLIENT_MSG _IOWR(WAN_IOC_MAGIC, \
+				WAN_IOCTL_SEND_LAN_CLIENT_MSG, \
+				struct wan_ioctl_send_lan_client_msg *)
+
+#define WAN_IOC_CLEAR_LAN_CLIENT_INFO _IOWR(WAN_IOC_MAGIC, \
+			WAN_IOCTL_CLEAR_LAN_CLIENT_INFO, \
+			struct wan_ioctl_lan_client_info *)
 #endif /* _RMNET_IPA_FD_IOCTL_H */
diff --git a/include/uapi/linux/spi/spidev.h b/include/uapi/linux/spi/spidev.h
index dd5f21e..856de39 100644
--- a/include/uapi/linux/spi/spidev.h
+++ b/include/uapi/linux/spi/spidev.h
@@ -23,6 +23,7 @@
 #define SPIDEV_H
 
 #include <linux/types.h>
+#include <linux/ioctl.h>
 
 /* User space versions of kernel symbols for SPI clocking modes,
  * matching <linux/spi/spi.h>
diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h
index 7fe799e..e75e1b6 100644
--- a/include/uapi/linux/time.h
+++ b/include/uapi/linux/time.h
@@ -56,7 +56,6 @@
 #define CLOCK_BOOTTIME_ALARM		9
 #define CLOCK_SGI_CYCLE			10	/* Hardware specific */
 #define CLOCK_TAI			11
-#define CLOCK_POWEROFF_ALARM		12
 
 #define MAX_CLOCKS			16
 #define CLOCKS_MASK			(CLOCK_REALTIME | CLOCK_MONOTONIC)
diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h
index 0e5ce0d..0d69769 100644
--- a/include/uapi/linux/usb/ch9.h
+++ b/include/uapi/linux/usb/ch9.h
@@ -759,6 +759,7 @@
 	__u8  iFunction;
 } __attribute__ ((packed));
 
+#define USB_DT_INTERFACE_ASSOCIATION_SIZE	8
 
 /*-------------------------------------------------------------------------*/
 
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 26506d5..1df8c41 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1126,6 +1126,45 @@
 #define V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_CUSTOM_MATRIX \
 		(V4L2_CID_MPEG_MSM_VIDC_BASE + 114)
 
+#define V4L2_CID_MPEG_VIDC_VIDEO_FLIP (V4L2_CID_MPEG_MSM_VIDC_BASE + 115)
+enum v4l2_mpeg_vidc_video_flip {
+	V4L2_CID_MPEG_VIDC_VIDEO_FLIP_NONE = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_FLIP_HORI = 1,
+	V4L2_CID_MPEG_VIDC_VIDEO_FLIP_VERT = 2,
+	V4L2_CID_MPEG_VIDC_VIDEO_FLIP_BOTH = 3,
+};
+
+/* HDR SEI INFO related control IDs and definitions*/
+#define V4L2_MPEG_VIDC_VENC_HDR_INFO_ENABLED 1
+#define V4L2_MPEG_VIDC_VENC_HDR_INFO_DISABLED 0
+
+#define V4L2_CID_MPEG_VIDC_VENC_HDR_INFO \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 116)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_00 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 117)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_01 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 118)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_10 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 119)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_11 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 120)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_20 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 121)
+#define V4L2_CID_MPEG_VIDC_VENC_RGB_PRIMARY_21 \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 122)
+#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_X \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 123)
+#define V4L2_CID_MPEG_VIDC_VENC_WHITEPOINT_Y \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 124)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_DISP_LUM \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 125)
+#define V4L2_CID_MPEG_VIDC_VENC_MIN_DISP_LUM \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 126)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_CLL \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 127)
+#define V4L2_CID_MPEG_VIDC_VENC_MAX_FLL \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 128)
+
 /*  Camera class control IDs */
 
 #define V4L2_CID_CAMERA_CLASS_BASE 	(V4L2_CTRL_CLASS_CAMERA | 0x900)
diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h
index 1fc62b2..7d75e56 100644
--- a/include/uapi/linux/xfrm.h
+++ b/include/uapi/linux/xfrm.h
@@ -303,6 +303,8 @@
 	XFRMA_PROTO,		/* __u8 */
 	XFRMA_ADDRESS_FILTER,	/* struct xfrm_address_filter */
 	XFRMA_PAD,
+	XFRMA_OFFLOAD_DEV,	/* struct xfrm_state_offload */
+	XFRMA_OUTPUT_MARK,	/* __u32 */
 	__XFRMA_MAX
 
 #define XFRMA_MAX (__XFRMA_MAX - 1)
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
index e72a1f0..1e087a1 100644
--- a/include/uapi/media/Kbuild
+++ b/include/uapi/media/Kbuild
@@ -14,3 +14,4 @@
 header-y += msm_sde_rotator.h
 header-y += radio-iris.h
 header-y += radio-iris-commands.h
+header-y += cam_lrme.h
diff --git a/include/uapi/media/cam_isp.h b/include/uapi/media/cam_isp.h
index 9253bc7..afd109f 100644
--- a/include/uapi/media/cam_isp.h
+++ b/include/uapi/media/cam_isp.h
@@ -73,8 +73,10 @@
 #define CAM_ISP_PACKET_META_DMI_COMMON          6
 #define CAM_ISP_PACKET_META_CLOCK               7
 #define CAM_ISP_PACKET_META_CSID                8
-#define CAM_ISP_PACKET_META_GENERIC_BLOB        10
-#define CAM_ISP_PACKET_META_MAX                 11
+#define CAM_ISP_PACKET_META_DUAL_CONFIG         9
+#define CAM_ISP_PACKET_META_GENERIC_BLOB_LEFT   10
+#define CAM_ISP_PACKET_META_GENERIC_BLOB_RIGHT  11
+#define CAM_ISP_PACKET_META_GENERIC_BLOB_COMMON 12
 
 /* DSP mode */
 #define CAM_ISP_DSP_MODE_NONE                   0
@@ -82,7 +84,9 @@
 #define CAM_ISP_DSP_MODE_ROUND                  2
 
 /* ISP Generic Cmd Buffer Blob types */
-#define CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG    0
+#define CAM_ISP_GENERIC_BLOB_TYPE_HFR_CONFIG      0
+#define CAM_ISP_GENERIC_BLOB_TYPE_CLOCK_CONFIG    1
+#define CAM_ISP_GENERIC_BLOB_TYPE_BW_CONFIG       2
 
 /* Query devices */
 /**
@@ -246,19 +250,129 @@
 	uint32_t                       framedrop_pattern;
 	uint32_t                       framedrop_period;
 	uint32_t                       reserved;
-};
+} __attribute__((packed));
 
 /**
  * struct cam_isp_resource_hfr_config - Resource HFR configuration
  *
- * @num_io_configs:             Number of ports
+ * @num_ports:                  Number of ports
  * @reserved:                   Reserved for alignment
- * @io_hfr_config:              HFR configuration for each IO port
+ * @port_hfr_config:            HFR configuration for each IO port
  */
 struct cam_isp_resource_hfr_config {
-	uint32_t                       num_io_configs;
+	uint32_t                       num_ports;
 	uint32_t                       reserved;
-	struct cam_isp_port_hfr_config io_hfr_config[1];
+	struct cam_isp_port_hfr_config port_hfr_config[1];
+} __attribute__((packed));
+
+/**
+ * struct cam_isp_dual_split_params - dual isp spilt parameters
+ *
+ * @split_point:                Split point information x, where (0 < x < width)
+ *                              left ISP's input ends at x + righ padding and
+ *                              Right ISP's input starts at x - left padding
+ * @right_padding:              Padding added past the split point for left
+ *                              ISP's input
+ * @left_padding:               Padding added before split point for right
+ *                              ISP's input
+ * @reserved:                   Reserved filed for alignment
+ *
+ */
+struct cam_isp_dual_split_params {
+	uint32_t                       split_point;
+	uint32_t                       right_padding;
+	uint32_t                       left_padding;
+	uint32_t                       reserved;
 };
 
+/**
+ * struct cam_isp_dual_stripe_config - stripe config per bus client
+ *
+ * @offset:                     Start horizontal offset relative to
+ *                              output buffer
+ *                              In UBWC mode, this value indicates the H_INIT
+ *                              value in pixel
+ * @width:                      Width of the stripe in bytes
+ * @tileconfig                  Ubwc meta tile config. Contain the partial
+ *                              tile info
+ * @port_id:                    port id of ISP output
+ *
+ */
+struct cam_isp_dual_stripe_config {
+	uint32_t                       offset;
+	uint32_t                       width;
+	uint32_t                       tileconfig;
+	uint32_t                       port_id;
+};
+
+/**
+ * struct cam_isp_dual_config - dual isp configuration
+ *
+ * @num_ports                   Number of isp output ports
+ * @reserved                    Reserved field for alignment
+ * @split_params:               Inpput split parameters
+ * @stripes:                    Stripe information
+ *
+ */
+struct cam_isp_dual_config {
+	uint32_t                           num_ports;
+	uint32_t                           reserved;
+	struct cam_isp_dual_split_params   split_params;
+	struct cam_isp_dual_stripe_config  stripes[1];
+} __attribute__((packed));
+
+/**
+ * struct cam_isp_clock_config - Clock configuration
+ *
+ * @usage_type:                 Usage type (Single/Dual)
+ * @num_rdi:                    Number of RDI votes
+ * @left_pix_hz:                Pixel Clock for Left ISP
+ * @right_pix_hz:               Pixel Clock for Right ISP, valid only if Dual
+ * @rdi_hz:                     RDI Clock. ISP clock will be max of RDI and
+ *                              PIX clocks. For a particular context which ISP
+ *                              HW the RDI is allocated to is not known to UMD.
+ *                              Hence pass the clock and let KMD decide.
+ */
+struct cam_isp_clock_config {
+	uint32_t                       usage_type;
+	uint32_t                       num_rdi;
+	uint64_t                       left_pix_hz;
+	uint64_t                       right_pix_hz;
+	uint64_t                       rdi_hz[1];
+} __attribute__((packed));
+
+/**
+ * struct cam_isp_bw_vote - Bandwidth vote information
+ *
+ * @resource_id:                Resource ID
+ * @reserved:                   Reserved field for alignment
+ * @cam_bw_bps:                 Bandwidth vote for CAMNOC
+ * @ext_bw_bps:                 Bandwidth vote for path-to-DDR after CAMNOC
+ */
+
+struct cam_isp_bw_vote {
+	uint32_t                       resource_id;
+	uint32_t                       reserved;
+	uint64_t                       cam_bw_bps;
+	uint64_t                       ext_bw_bps;
+} __attribute__((packed));
+
+/**
+ * struct cam_isp_bw_config - Bandwidth configuration
+ *
+ * @usage_type:                 Usage type (Single/Dual)
+ * @num_rdi:                    Number of RDI votes
+ * @left_pix_vote:              Bandwidth vote for left ISP
+ * @right_pix_vote:             Bandwidth vote for right ISP
+ * @rdi_vote:                   RDI bandwidth requirements
+ */
+
+struct cam_isp_bw_config {
+	uint32_t                       usage_type;
+	uint32_t                       num_rdi;
+	struct cam_isp_bw_vote         left_pix_vote;
+	struct cam_isp_bw_vote         right_pix_vote;
+	struct cam_isp_bw_vote         rdi_vote[1];
+} __attribute__((packed));
+
 #endif /* __UAPI_CAM_ISP_H__ */
diff --git a/include/uapi/media/cam_lrme.h b/include/uapi/media/cam_lrme.h
new file mode 100644
index 0000000..97d9578
--- /dev/null
+++ b/include/uapi/media/cam_lrme.h
@@ -0,0 +1,65 @@
+#ifndef __UAPI_CAM_LRME_H__
+#define __UAPI_CAM_LRME_H__
+
+#include "cam_defs.h"
+
+/* LRME Resource Types */
+
+enum CAM_LRME_IO_TYPE {
+	CAM_LRME_IO_TYPE_TAR,
+	CAM_LRME_IO_TYPE_REF,
+	CAM_LRME_IO_TYPE_RES,
+	CAM_LRME_IO_TYPE_DS2,
+};
+
+#define CAM_LRME_INPUT_PORT_TYPE_TAR (1 << 0)
+#define CAM_LRME_INPUT_PORT_TYPE_REF (1 << 1)
+
+#define CAM_LRME_OUTPUT_PORT_TYPE_DS2 (1 << 0)
+#define CAM_LRME_OUTPUT_PORT_TYPE_RES (1 << 1)
+
+#define CAM_LRME_DEV_MAX 1
+
+
+struct cam_lrme_hw_version {
+	uint32_t gen;
+	uint32_t rev;
+	uint32_t step;
+};
+
+struct cam_lrme_dev_cap {
+	struct cam_lrme_hw_version clc_hw_version;
+	struct cam_lrme_hw_version bus_rd_hw_version;
+	struct cam_lrme_hw_version bus_wr_hw_version;
+	struct cam_lrme_hw_version top_hw_version;
+	struct cam_lrme_hw_version top_titan_version;
+};
+
+/**
+ * struct cam_lrme_query_cap_cmd - LRME query device capability payload
+ *
+ * @dev_iommu_handle: LRME iommu handles for secure/non secure
+ *      modes
+ * @cdm_iommu_handle: Iommu handles for secure/non secure modes
+ * @num_devices: number of hardware devices
+ * @dev_caps: Returned device capability array
+ */
+struct cam_lrme_query_cap_cmd {
+	struct cam_iommu_handle device_iommu;
+	struct cam_iommu_handle cdm_iommu;
+	uint32_t num_devices;
+	struct cam_lrme_dev_cap dev_caps[CAM_LRME_DEV_MAX];
+};
+
+struct cam_lrme_soc_info {
+	uint64_t clock_rate;
+	uint64_t bandwidth;
+	uint64_t reserved[4];
+};
+
+struct cam_lrme_acquire_args {
+	struct cam_lrme_soc_info lrme_soc_info;
+};
+
+#endif /* __UAPI_CAM_LRME_H__ */
+
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
index 23a8ccf..233d84e 100644
--- a/include/uapi/media/cam_req_mgr.h
+++ b/include/uapi/media/cam_req_mgr.h
@@ -23,6 +23,7 @@
 #define CAM_CCI_DEVICE_TYPE       (CAM_DEVICE_TYPE_BASE + 10)
 #define CAM_FLASH_DEVICE_TYPE     (CAM_DEVICE_TYPE_BASE + 11)
 #define CAM_EEPROM_DEVICE_TYPE    (CAM_DEVICE_TYPE_BASE + 12)
+#define CAM_OIS_DEVICE_TYPE       (CAM_DEVICE_TYPE_BASE + 13)
 
 /* cam_req_mgr hdl info */
 #define CAM_REQ_MGR_HDL_IDX_POS           8
@@ -215,7 +216,8 @@
 /* Maximum allowed buffers in existence */
 #define CAM_MEM_BUFQ_MAX                        1024
 
-#define CAM_MEM_MGR_HDL_IDX_SIZE                16
+#define CAM_MEM_MGR_SECURE_BIT_POS              15
+#define CAM_MEM_MGR_HDL_IDX_SIZE                15
 #define CAM_MEM_MGR_HDL_FD_SIZE                 16
 #define CAM_MEM_MGR_HDL_IDX_END_POS             16
 #define CAM_MEM_MGR_HDL_FD_END_POS              32
@@ -223,11 +225,19 @@
 #define CAM_MEM_MGR_HDL_IDX_MASK      ((1 << CAM_MEM_MGR_HDL_IDX_SIZE) - 1)
 
 #define GET_MEM_HANDLE(idx, fd) \
-	((idx << (CAM_MEM_MGR_HDL_IDX_END_POS - CAM_MEM_MGR_HDL_IDX_SIZE)) | \
+	((idx & CAM_MEM_MGR_HDL_IDX_MASK) | \
 	(fd << (CAM_MEM_MGR_HDL_FD_END_POS - CAM_MEM_MGR_HDL_FD_SIZE))) \
 
 #define CAM_MEM_MGR_GET_HDL_IDX(hdl) (hdl & CAM_MEM_MGR_HDL_IDX_MASK)
 
+#define CAM_MEM_MGR_SET_SECURE_HDL(hdl, flag) \
+	((flag) ? (hdl |= (1 << CAM_MEM_MGR_SECURE_BIT_POS)) : \
+	((hdl) &= ~(1 << CAM_MEM_MGR_SECURE_BIT_POS)))
+
+#define CAM_MEM_MGR_IS_SECURE_HDL(hdl) \
+	(((hdl) & \
+	(1<<CAM_MEM_MGR_SECURE_BIT_POS)) >> CAM_MEM_MGR_SECURE_BIT_POS)
+
 /**
  * memory allocation type
  */
@@ -345,14 +355,14 @@
  * @error_type: type of error
  * @request_id: request id of frame
  * @device_hdl: device handle
- * @reserved: reserved field
+ * @linke_hdl: link_hdl
  * @resource_size: size of the resource
  */
 struct cam_req_mgr_error_msg {
 	uint32_t error_type;
 	uint32_t request_id;
 	int32_t device_hdl;
-	int32_t reserved;
+	int32_t link_hdl;
 	uint64_t resource_size;
 };
 
diff --git a/include/uapi/media/cam_sensor.h b/include/uapi/media/cam_sensor.h
index ac370ba..87f25b0 100644
--- a/include/uapi/media/cam_sensor.h
+++ b/include/uapi/media/cam_sensor.h
@@ -7,6 +7,8 @@
 
 #define CAM_SENSOR_PROBE_CMD   (CAM_COMMON_OPCODE_MAX + 1)
 #define CAM_FLASH_MAX_LED_TRIGGERS 3
+#define MAX_OIS_NAME_SIZE 32
+#define CAM_CSIPHY_SECURE_MODE_ENABLED 1
 /**
  * struct cam_sensor_query_cap - capabilities info for sensor
  *
@@ -75,6 +77,16 @@
 } __attribute__((packed));
 
 /**
+ * struct cam_ois_query_cap_t - capabilities info for ois
+ *
+ * @slot_info                  :  Indicates about the slotId or cell Index
+ */
+struct cam_ois_query_cap_t {
+	uint32_t            slot_info;
+	uint16_t            reserved;
+} __attribute__((packed));
+
+/**
  * struct cam_cmd_i2c_info - Contains slave I2C related info
  *
  * @slave_addr      :    Slave address
@@ -88,6 +100,42 @@
 } __attribute__((packed));
 
 /**
+ * struct cam_ois_opcode - Contains OIS opcode
+ *
+ * @prog            :    OIS FW prog register address
+ * @coeff           :    OIS FW coeff register address
+ * @pheripheral     :    OIS pheripheral
+ * @memory          :    OIS memory
+ */
+struct cam_ois_opcode {
+	uint32_t prog;
+	uint32_t coeff;
+	uint32_t pheripheral;
+	uint32_t memory;
+} __attribute__((packed));
+
+/**
+ * struct cam_cmd_ois_info - Contains OIS slave info
+ *
+ * @slave_addr            :    OIS i2c slave address
+ * @i2c_freq_mode         :    i2c frequency mode
+ * @ois_fw_flag           :    indicates if fw is present or not
+ * @is_ois_calib          :    indicates the calibration data is available
+ * @ois_name              :    OIS name
+ * @opcode                :    opcode
+ * @cmd_type              :    Explains type of command
+ */
+struct cam_cmd_ois_info {
+	uint16_t              slave_addr;
+	uint8_t               i2c_freq_mode;
+	uint8_t               ois_fw_flag;
+	uint8_t               is_ois_calib;
+	char                  ois_name[MAX_OIS_NAME_SIZE];
+	struct cam_ois_opcode opcode;
+	uint8_t               cmd_type;
+} __attribute__((packed));
+
+/**
  * struct cam_cmd_probe - Contains sensor slave info
  *
  * @data_type       :   Slave register data type
@@ -269,15 +317,15 @@
 
 /**
  * cam_csiphy_info: Provides cmdbuffer structre
- * @lane_mask     :  Lane mask details
- * @lane_assign   :  Lane sensor will be using
- * @csiphy_3phase :  Total number of lanes
- * @combo_mode    :  Info regarding combo_mode is enable / disable
- * @lane_cnt      :  Total number of lanes
- * @reserved
- * @3phase        :  Details whether 3Phase / 2Phase operation
- * @settle_time   :  Settling time in ms
- * @data_rate     :  Data rate
+ * @lane_mask     : Lane mask details
+ * @lane_assign   : Lane sensor will be using
+ * @csiphy_3phase : Total number of lanes
+ * @combo_mode    : Info regarding combo_mode is enable / disable
+ * @lane_cnt      : Total number of lanes
+ * @secure_mode   : Secure mode flag to enable / disable
+ * @3phase        : Details whether 3Phase / 2Phase operation
+ * @settle_time   : Settling time in ms
+ * @data_rate     : Data rate
  *
  */
 struct cam_csiphy_info {
@@ -286,7 +334,7 @@
 	uint8_t     csiphy_3phase;
 	uint8_t     combo_mode;
 	uint8_t     lane_cnt;
-	uint8_t     reserved;
+	uint8_t     secure_mode;
 	uint64_t    settle_time;
 	uint64_t    data_rate;
 } __attribute__((packed));
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
index 4fe325d..63fd555 100644
--- a/include/uapi/media/msm_vidc.h
+++ b/include/uapi/media/msm_vidc.h
@@ -6,6 +6,7 @@
 #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12	0x2
 #define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC	0x8002
 #define MSM_VIDC_4x_1 0x1
+#define MSM_VIDC_EXTRADATA_FRAME_QP_ADV 0x1
 
 struct msm_vidc_extradata_header {
 	unsigned int size;
@@ -137,6 +138,10 @@
 
 struct msm_vidc_frame_qp_payload {
 	unsigned int frame_qp;
+	unsigned int qp_sum;
+	unsigned int skip_qp_sum;
+	unsigned int skip_num_blocks;
+	unsigned int total_num_blocks;
 };
 
 struct msm_vidc_frame_bits_info_payload {
diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h
index 7c35e27..683057f 100644
--- a/include/xen/swiotlb-xen.h
+++ b/include/xen/swiotlb-xen.h
@@ -58,4 +58,9 @@
 
 extern int
 xen_swiotlb_set_dma_mask(struct device *dev, u64 dma_mask);
+
+extern int
+xen_swiotlb_dma_mmap(struct device *dev, struct vm_area_struct *vma,
+		     void *cpu_addr, dma_addr_t dma_addr, size_t size,
+		     unsigned long attrs);
 #endif /* __LINUX_SWIOTLB_XEN_H */
diff --git a/init/Kconfig b/init/Kconfig
index af000c7..7b3006a 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1051,23 +1051,6 @@
 config PAGE_COUNTER
        bool
 
-config CGROUP_SCHEDTUNE
-	bool "CFS tasks boosting cgroup subsystem (EXPERIMENTAL)"
-	depends on SCHED_TUNE
-	help
-	  This option provides the "schedtune" controller which improves the
-	  flexibility of the task boosting mechanism by introducing the support
-	  to define "per task" boost values.
-
-	  This new controller:
-	  1. allows only a two layers hierarchy, where the root defines the
-	     system-wide boost value and its direct childrens define each one a
-	     different "class of tasks" to be boosted with a different value
-	  2. supports up to 16 different task classes, each one which could be
-	     configured with a different boost value
-
-	  Say N if unsure.
-
 config MEMCG
 	bool "Memory controller"
 	select PAGE_COUNTER
@@ -1276,13 +1259,6 @@
 
 endif # CGROUPS
 
-config SCHED_WALT
-	bool "WALT"
-	depends on SMP && FAIR_GROUP_SCHED
-	help
-	  Use Window-Assisted Load Tracking (WALT) as an alternative or
-	  additional load tracking scheme in lieu of or along with PELT.
-
 config SCHED_CORE_CTL
 	bool "QTI Core Control"
 	depends on SMP
@@ -1293,6 +1269,16 @@
 
 	  If unsure, say N here.
 
+config SCHED_CORE_ROTATE
+	bool "Scheduler core rotation"
+	depends on SMP
+	help
+	  This options enables the core rotation functionality in
+	  the scheduler. Scheduler with core rotation aims to utilize
+	  CPUs evenly.
+
+	  If unsure, say N here.
+
 config CHECKPOINT_RESTORE
 	bool "Checkpoint/restore support" if EXPERT
 	select PROC_CHILDREN
diff --git a/init/initramfs.c b/init/initramfs.c
index f8ce812..d0b53f49 100644
--- a/init/initramfs.c
+++ b/init/initramfs.c
@@ -19,6 +19,7 @@
 #include <linux/syscalls.h>
 #include <linux/utime.h>
 #include <linux/initramfs.h>
+#include <linux/file.h>
 
 static ssize_t __init xwrite(int fd, const char *p, size_t count)
 {
@@ -664,6 +665,7 @@
 			printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err);
 		free_initrd();
 #endif
+		flush_delayed_fput();
 		/*
 		 * Try loading default modules from initramfs.  This gives
 		 * us a chance to load before device_initcalls.
diff --git a/init/main.c b/init/main.c
index aca8f3e..674bc77 100644
--- a/init/main.c
+++ b/init/main.c
@@ -70,7 +70,6 @@
 #include <linux/shmem_fs.h>
 #include <linux/slab.h>
 #include <linux/perf_event.h>
-#include <linux/file.h>
 #include <linux/ptrace.h>
 #include <linux/blkdev.h>
 #include <linux/elevator.h>
@@ -946,8 +945,6 @@
 	system_state = SYSTEM_RUNNING;
 	numa_default_policy();
 
-	flush_delayed_fput();
-
 	rcu_end_inkernel_boot();
 
 	if (ramdisk_execute_command) {
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index 0d302a8..690e1e3 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -457,13 +457,15 @@
 	list_del(&krule->rlist);
 
 	if (list_empty(&watch->rules)) {
+		/*
+		 * audit_remove_watch() drops our reference to 'parent' which
+		 * can get freed. Grab our own reference to be safe.
+		 */
+		audit_get_parent(parent);
 		audit_remove_watch(watch);
-
-		if (list_empty(&parent->watches)) {
-			audit_get_parent(parent);
+		if (list_empty(&parent->watches))
 			fsnotify_destroy_mark(&parent->mark, audit_watch_group);
-			audit_put_parent(parent);
-		}
+		audit_put_parent(parent);
 	}
 }
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8ce679d..372454a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -139,7 +139,7 @@
 	struct bpf_verifier_stack_elem *next;
 };
 
-#define BPF_COMPLEXITY_LIMIT_INSNS	65536
+#define BPF_COMPLEXITY_LIMIT_INSNS	98304
 #define BPF_COMPLEXITY_LIMIT_STACK	1024
 
 struct bpf_call_arg_meta {
@@ -682,12 +682,13 @@
 	return -EACCES;
 }
 
-static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
+static bool __is_pointer_value(bool allow_ptr_leaks,
+			       const struct bpf_reg_state *reg)
 {
-	if (env->allow_ptr_leaks)
+	if (allow_ptr_leaks)
 		return false;
 
-	switch (env->cur_state.regs[regno].type) {
+	switch (reg->type) {
 	case UNKNOWN_VALUE:
 	case CONST_IMM:
 		return false;
@@ -696,6 +697,11 @@
 	}
 }
 
+static bool is_pointer_value(struct bpf_verifier_env *env, int regno)
+{
+	return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]);
+}
+
 static int check_ptr_alignment(struct bpf_verifier_env *env,
 			       struct bpf_reg_state *reg, int off, int size)
 {
@@ -1467,6 +1473,65 @@
 	return 0;
 }
 
+static int evaluate_reg_imm_alu_unknown(struct bpf_verifier_env *env,
+					struct bpf_insn *insn)
+{
+	struct bpf_reg_state *regs = env->cur_state.regs;
+	struct bpf_reg_state *dst_reg = &regs[insn->dst_reg];
+	struct bpf_reg_state *src_reg = &regs[insn->src_reg];
+	u8 opcode = BPF_OP(insn->code);
+	s64 imm_log2 = __ilog2_u64((long long)dst_reg->imm);
+
+	/* BPF_X code with src_reg->type UNKNOWN_VALUE here. */
+	if (src_reg->imm > 0 && dst_reg->imm) {
+		switch (opcode) {
+		case BPF_ADD:
+			/* dreg += sreg
+			 * where both have zero upper bits. Adding them
+			 * can only result making one more bit non-zero
+			 * in the larger value.
+			 * Ex. 0xffff (imm=48) + 1 (imm=63) = 0x10000 (imm=47)
+			 *     0xffff (imm=48) + 0xffff = 0x1fffe (imm=47)
+			 */
+			dst_reg->imm = min(src_reg->imm, 63 - imm_log2);
+			dst_reg->imm--;
+			break;
+		case BPF_AND:
+			/* dreg &= sreg
+			 * AND can not extend zero bits only shrink
+			 * Ex.  0x00..00ffffff
+			 *    & 0x0f..ffffffff
+			 *     ----------------
+			 *      0x00..00ffffff
+			 */
+			dst_reg->imm = max(src_reg->imm, 63 - imm_log2);
+			break;
+		case BPF_OR:
+			/* dreg |= sreg
+			 * OR can only extend zero bits
+			 * Ex.  0x00..00ffffff
+			 *    | 0x0f..ffffffff
+			 *     ----------------
+			 *      0x0f..00ffffff
+			 */
+			dst_reg->imm = min(src_reg->imm, 63 - imm_log2);
+			break;
+		case BPF_SUB:
+		case BPF_MUL:
+		case BPF_RSH:
+		case BPF_LSH:
+			/* These may be flushed out later */
+		default:
+			mark_reg_unknown_value(regs, insn->dst_reg);
+		}
+	} else {
+		mark_reg_unknown_value(regs, insn->dst_reg);
+	}
+
+	dst_reg->type = UNKNOWN_VALUE;
+	return 0;
+}
+
 static int evaluate_reg_imm_alu(struct bpf_verifier_env *env,
 				struct bpf_insn *insn)
 {
@@ -1475,6 +1540,9 @@
 	struct bpf_reg_state *src_reg = &regs[insn->src_reg];
 	u8 opcode = BPF_OP(insn->code);
 
+	if (BPF_SRC(insn->code) == BPF_X && src_reg->type == UNKNOWN_VALUE)
+		return evaluate_reg_imm_alu_unknown(env, insn);
+
 	/* dst_reg->type == CONST_IMM here, simulate execution of 'add' insn.
 	 * Don't care about overflow or negative values, just add them
 	 */
@@ -1530,10 +1598,24 @@
 	}
 
 	/* We don't know anything about what was done to this register, mark it
-	 * as unknown.
+	 * as unknown. Also, if both derived bounds came from signed/unsigned
+	 * mixed compares and one side is unbounded, we cannot really do anything
+	 * with them as boundaries cannot be trusted. Thus, arithmetic of two
+	 * regs of such kind will get invalidated bounds on the dst side.
 	 */
-	if (min_val == BPF_REGISTER_MIN_RANGE &&
-	    max_val == BPF_REGISTER_MAX_RANGE) {
+	if ((min_val == BPF_REGISTER_MIN_RANGE &&
+	     max_val == BPF_REGISTER_MAX_RANGE) ||
+	    (BPF_SRC(insn->code) == BPF_X &&
+	     ((min_val != BPF_REGISTER_MIN_RANGE &&
+	       max_val == BPF_REGISTER_MAX_RANGE) ||
+	      (min_val == BPF_REGISTER_MIN_RANGE &&
+	       max_val != BPF_REGISTER_MAX_RANGE) ||
+	      (dst_reg->min_value != BPF_REGISTER_MIN_RANGE &&
+	       dst_reg->max_value == BPF_REGISTER_MAX_RANGE) ||
+	      (dst_reg->min_value == BPF_REGISTER_MIN_RANGE &&
+	       dst_reg->max_value != BPF_REGISTER_MAX_RANGE)) &&
+	     regs[insn->dst_reg].value_from_signed !=
+	     regs[insn->src_reg].value_from_signed)) {
 		reset_reg_range_values(regs, insn->dst_reg);
 		return;
 	}
@@ -1542,10 +1624,12 @@
 	 * do our normal operations to the register, we need to set the values
 	 * to the min/max since they are undefined.
 	 */
-	if (min_val == BPF_REGISTER_MIN_RANGE)
-		dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
-	if (max_val == BPF_REGISTER_MAX_RANGE)
-		dst_reg->max_value = BPF_REGISTER_MAX_RANGE;
+	if (opcode != BPF_SUB) {
+		if (min_val == BPF_REGISTER_MIN_RANGE)
+			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
+		if (max_val == BPF_REGISTER_MAX_RANGE)
+			dst_reg->max_value = BPF_REGISTER_MAX_RANGE;
+	}
 
 	switch (opcode) {
 	case BPF_ADD:
@@ -1555,10 +1639,17 @@
 			dst_reg->max_value += max_val;
 		break;
 	case BPF_SUB:
+		/* If one of our values was at the end of our ranges, then the
+		 * _opposite_ value in the dst_reg goes to the end of our range.
+		 */
+		if (min_val == BPF_REGISTER_MIN_RANGE)
+			dst_reg->max_value = BPF_REGISTER_MAX_RANGE;
+		if (max_val == BPF_REGISTER_MAX_RANGE)
+			dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
 		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
-			dst_reg->min_value -= min_val;
+			dst_reg->min_value -= max_val;
 		if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
-			dst_reg->max_value -= max_val;
+			dst_reg->max_value -= min_val;
 		break;
 	case BPF_MUL:
 		if (dst_reg->min_value != BPF_REGISTER_MIN_RANGE)
@@ -1629,7 +1720,8 @@
 			}
 		} else {
 			if (insn->src_reg != BPF_REG_0 || insn->off != 0 ||
-			    (insn->imm != 16 && insn->imm != 32 && insn->imm != 64)) {
+			    (insn->imm != 16 && insn->imm != 32 && insn->imm != 64) ||
+			    BPF_CLASS(insn->code) == BPF_ALU64) {
 				verbose("BPF_END uses reserved fields\n");
 				return -EINVAL;
 			}
@@ -1808,6 +1900,7 @@
 		 * register as unknown.
 		 */
 		if (env->allow_ptr_leaks &&
+		    BPF_CLASS(insn->code) == BPF_ALU64 && opcode == BPF_ADD &&
 		    (dst_reg->type == PTR_TO_MAP_VALUE ||
 		     dst_reg->type == PTR_TO_MAP_VALUE_ADJ))
 			dst_reg->type = PTR_TO_MAP_VALUE_ADJ;
@@ -1876,38 +1969,63 @@
 			    struct bpf_reg_state *false_reg, u64 val,
 			    u8 opcode)
 {
+	bool value_from_signed = true;
+	bool is_range = true;
+
 	switch (opcode) {
 	case BPF_JEQ:
 		/* If this is false then we know nothing Jon Snow, but if it is
 		 * true then we know for sure.
 		 */
 		true_reg->max_value = true_reg->min_value = val;
+		is_range = false;
 		break;
 	case BPF_JNE:
 		/* If this is true we know nothing Jon Snow, but if it is false
 		 * we know the value for sure;
 		 */
 		false_reg->max_value = false_reg->min_value = val;
+		is_range = false;
 		break;
 	case BPF_JGT:
-		/* Unsigned comparison, the minimum value is 0. */
-		false_reg->min_value = 0;
+		value_from_signed = false;
+		/* fallthrough */
 	case BPF_JSGT:
+		if (true_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(true_reg, 0);
+		if (false_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(false_reg, 0);
+		if (opcode == BPF_JGT) {
+			/* Unsigned comparison, the minimum value is 0. */
+			false_reg->min_value = 0;
+		}
 		/* If this is false then we know the maximum val is val,
 		 * otherwise we know the min val is val+1.
 		 */
 		false_reg->max_value = val;
+		false_reg->value_from_signed = value_from_signed;
 		true_reg->min_value = val + 1;
+		true_reg->value_from_signed = value_from_signed;
 		break;
 	case BPF_JGE:
-		/* Unsigned comparison, the minimum value is 0. */
-		false_reg->min_value = 0;
+		value_from_signed = false;
+		/* fallthrough */
 	case BPF_JSGE:
+		if (true_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(true_reg, 0);
+		if (false_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(false_reg, 0);
+		if (opcode == BPF_JGE) {
+			/* Unsigned comparison, the minimum value is 0. */
+			false_reg->min_value = 0;
+		}
 		/* If this is false then we know the maximum value is val - 1,
 		 * otherwise we know the mimimum value is val.
 		 */
 		false_reg->max_value = val - 1;
+		false_reg->value_from_signed = value_from_signed;
 		true_reg->min_value = val;
+		true_reg->value_from_signed = value_from_signed;
 		break;
 	default:
 		break;
@@ -1915,6 +2033,12 @@
 
 	check_reg_overflow(false_reg);
 	check_reg_overflow(true_reg);
+	if (is_range) {
+		if (__is_pointer_value(false, false_reg))
+			reset_reg_range_values(false_reg, 0);
+		if (__is_pointer_value(false, true_reg))
+			reset_reg_range_values(true_reg, 0);
+	}
 }
 
 /* Same as above, but for the case that dst_reg is a CONST_IMM reg and src_reg
@@ -1924,39 +2048,64 @@
 				struct bpf_reg_state *false_reg, u64 val,
 				u8 opcode)
 {
+	bool value_from_signed = true;
+	bool is_range = true;
+
 	switch (opcode) {
 	case BPF_JEQ:
 		/* If this is false then we know nothing Jon Snow, but if it is
 		 * true then we know for sure.
 		 */
 		true_reg->max_value = true_reg->min_value = val;
+		is_range = false;
 		break;
 	case BPF_JNE:
 		/* If this is true we know nothing Jon Snow, but if it is false
 		 * we know the value for sure;
 		 */
 		false_reg->max_value = false_reg->min_value = val;
+		is_range = false;
 		break;
 	case BPF_JGT:
-		/* Unsigned comparison, the minimum value is 0. */
-		true_reg->min_value = 0;
+		value_from_signed = false;
+		/* fallthrough */
 	case BPF_JSGT:
+		if (true_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(true_reg, 0);
+		if (false_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(false_reg, 0);
+		if (opcode == BPF_JGT) {
+			/* Unsigned comparison, the minimum value is 0. */
+			true_reg->min_value = 0;
+		}
 		/*
 		 * If this is false, then the val is <= the register, if it is
 		 * true the register <= to the val.
 		 */
 		false_reg->min_value = val;
+		false_reg->value_from_signed = value_from_signed;
 		true_reg->max_value = val - 1;
+		true_reg->value_from_signed = value_from_signed;
 		break;
 	case BPF_JGE:
-		/* Unsigned comparison, the minimum value is 0. */
-		true_reg->min_value = 0;
+		value_from_signed = false;
+		/* fallthrough */
 	case BPF_JSGE:
+		if (true_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(true_reg, 0);
+		if (false_reg->value_from_signed != value_from_signed)
+			reset_reg_range_values(false_reg, 0);
+		if (opcode == BPF_JGE) {
+			/* Unsigned comparison, the minimum value is 0. */
+			true_reg->min_value = 0;
+		}
 		/* If this is false then constant < register, if it is true then
 		 * the register < constant.
 		 */
 		false_reg->min_value = val + 1;
+		false_reg->value_from_signed = value_from_signed;
 		true_reg->max_value = val;
+		true_reg->value_from_signed = value_from_signed;
 		break;
 	default:
 		break;
@@ -1964,6 +2113,12 @@
 
 	check_reg_overflow(false_reg);
 	check_reg_overflow(true_reg);
+	if (is_range) {
+		if (__is_pointer_value(false, false_reg))
+			reset_reg_range_values(false_reg, 0);
+		if (__is_pointer_value(false, true_reg))
+			reset_reg_range_values(true_reg, 0);
+	}
 }
 
 static void mark_map_reg(struct bpf_reg_state *regs, u32 regno, u32 id,
@@ -2390,6 +2545,7 @@
 				env->explored_states[t + 1] = STATE_LIST_MARK;
 		} else {
 			/* conditional jump with two edges */
+			env->explored_states[t] = STATE_LIST_MARK;
 			ret = push_insn(t, t + 1, FALLTHROUGH, env);
 			if (ret == 1)
 				goto peek_stack;
@@ -2548,6 +2704,12 @@
 		     rcur->type != NOT_INIT))
 			continue;
 
+		/* Don't care about the reg->id in this case. */
+		if (rold->type == PTR_TO_MAP_VALUE_OR_NULL &&
+		    rcur->type == PTR_TO_MAP_VALUE_OR_NULL &&
+		    rold->map_ptr == rcur->map_ptr)
+			continue;
+
 		if (rold->type == PTR_TO_PACKET && rcur->type == PTR_TO_PACKET &&
 		    compare_ptrs_to_packet(rold, rcur))
 			continue;
@@ -2682,6 +2844,9 @@
 			goto process_bpf_exit;
 		}
 
+		if (need_resched())
+			cond_resched();
+
 		if (log_level && do_print_state) {
 			verbose("\nfrom %d to %d:", prev_insn_idx, insn_idx);
 			print_verifier_state(&env->cur_state);
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 7bb21fd..26c624e 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -3508,11 +3508,11 @@
 	cgrp->subtree_control &= ~disable;
 
 	ret = cgroup_apply_control(cgrp);
-
 	cgroup_finalize_control(cgrp, ret);
+	if (ret)
+		goto out_unlock;
 
 	kernfs_activate(cgrp->kn);
-	ret = 0;
 out_unlock:
 	cgroup_kn_unlock(of->kn);
 	return ret ?: nbytes;
@@ -5744,6 +5744,10 @@
 
 		if (ss->bind)
 			ss->bind(init_css_set.subsys[ssid]);
+
+		mutex_lock(&cgroup_mutex);
+		css_populate_dir(init_css_set.subsys[ssid]);
+		mutex_unlock(&cgroup_mutex);
 	}
 
 	/* init_css_set.subsys[] has been updated, re-hash */
diff --git a/kernel/configs/README.android b/kernel/configs/README.android
deleted file mode 100644
index 2e2d7c0..0000000
--- a/kernel/configs/README.android
+++ /dev/null
@@ -1,15 +0,0 @@
-The android-*.config files in this directory are meant to be used as a base
-for an Android kernel config. All devices should have the options in
-android-base.config enabled. While not mandatory, the options in
-android-recommended.config enable advanced Android features.
-
-Assuming you already have a minimalist defconfig for your device, a possible
-way to enable these options would be:
-
-     ARCH=<arch> scripts/kconfig/merge_config.sh <path_to>/<device>_defconfig kernel/configs/android-base.config kernel/configs/android-recommended.config
-
-This will generate a .config that can then be used to save a new defconfig or
-compile a new kernel with Android features enabled.
-
-Because there is no tool to consistently generate these config fragments,
-lets keep them alphabetically sorted instead of random.
diff --git a/kernel/configs/android-base-arm64.cfg b/kernel/configs/android-base-arm64.cfg
deleted file mode 100644
index 43f23d6..0000000
--- a/kernel/configs/android-base-arm64.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-#  KEEP ALPHABETICALLY SORTED
-CONFIG_ARMV8_DEPRECATED=y
-CONFIG_CP15_BARRIER_EMULATION=y
-CONFIG_SETEND_EMULATION=y
-CONFIG_SWP_EMULATION=y
diff --git a/kernel/configs/android-base.config b/kernel/configs/android-base.config
deleted file mode 100644
index 80df048..0000000
--- a/kernel/configs/android-base.config
+++ /dev/null
@@ -1,170 +0,0 @@
-#  KEEP ALPHABETICALLY SORTED
-# CONFIG_DEVKMEM is not set
-# CONFIG_DEVMEM is not set
-# CONFIG_FHANDLE is not set
-# CONFIG_INET_LRO is not set
-# CONFIG_NFSD is not set
-# CONFIG_NFS_FS is not set
-# CONFIG_OABI_COMPAT is not set
-# CONFIG_SYSVIPC is not set
-# CONFIG_USELIB is not set
-CONFIG_ANDROID=y
-CONFIG_ANDROID_BINDER_DEVICES=binder,hwbinder,vndbinder
-CONFIG_ANDROID_BINDER_IPC=y
-CONFIG_ANDROID_LOW_MEMORY_KILLER=y
-CONFIG_ASHMEM=y
-CONFIG_AUDIT=y
-CONFIG_BLK_DEV_INITRD=y
-CONFIG_CGROUPS=y
-CONFIG_CGROUP_CPUACCT=y
-CONFIG_CGROUP_FREEZER=y
-CONFIG_CGROUP_SCHED=y
-CONFIG_CGROUP_BPF=y
-CONFIG_DEFAULT_SECURITY_SELINUX=y
-CONFIG_EMBEDDED=y
-CONFIG_FB=y
-CONFIG_HARDENED_USERCOPY=y
-CONFIG_HIGH_RES_TIMERS=y
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_INET6_AH=y
-CONFIG_INET6_ESP=y
-CONFIG_INET6_IPCOMP=y
-CONFIG_INET=y
-CONFIG_INET_DIAG_DESTROY=y
-CONFIG_INET_ESP=y
-CONFIG_INET_XFRM_MODE_TUNNEL=y
-CONFIG_IP6_NF_FILTER=y
-CONFIG_IP6_NF_IPTABLES=y
-CONFIG_IP6_NF_MANGLE=y
-CONFIG_IP6_NF_RAW=y
-CONFIG_IP6_NF_TARGET_REJECT=y
-CONFIG_IPV6=y
-CONFIG_IPV6_MIP6=y
-CONFIG_IPV6_MULTIPLE_TABLES=y
-CONFIG_IPV6_OPTIMISTIC_DAD=y
-CONFIG_IPV6_ROUTER_PREF=y
-CONFIG_IPV6_ROUTE_INFO=y
-CONFIG_IP_ADVANCED_ROUTER=y
-CONFIG_IP_MULTICAST=y
-CONFIG_IP_MULTIPLE_TABLES=y
-CONFIG_IP_NF_ARPFILTER=y
-CONFIG_IP_NF_ARPTABLES=y
-CONFIG_IP_NF_ARP_MANGLE=y
-CONFIG_IP_NF_FILTER=y
-CONFIG_IP_NF_IPTABLES=y
-CONFIG_IP_NF_MANGLE=y
-CONFIG_IP_NF_MATCH_AH=y
-CONFIG_IP_NF_MATCH_ECN=y
-CONFIG_IP_NF_MATCH_TTL=y
-CONFIG_IP_NF_NAT=y
-CONFIG_IP_NF_RAW=y
-CONFIG_IP_NF_SECURITY=y
-CONFIG_IP_NF_TARGET_MASQUERADE=y
-CONFIG_IP_NF_TARGET_NETMAP=y
-CONFIG_IP_NF_TARGET_REDIRECT=y
-CONFIG_IP_NF_TARGET_REJECT=y
-CONFIG_MODULES=y
-CONFIG_MODULE_UNLOAD=y
-CONFIG_MODVERSIONS=y
-CONFIG_NET=y
-CONFIG_NETDEVICES=y
-CONFIG_NETFILTER=y
-CONFIG_NETFILTER_XT_MATCH_COMMENT=y
-CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
-CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
-CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
-CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
-CONFIG_NETFILTER_XT_MATCH_HELPER=y
-CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
-CONFIG_NETFILTER_XT_MATCH_LENGTH=y
-CONFIG_NETFILTER_XT_MATCH_LIMIT=y
-CONFIG_NETFILTER_XT_MATCH_MAC=y
-CONFIG_NETFILTER_XT_MATCH_MARK=y
-CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
-CONFIG_NETFILTER_XT_MATCH_POLICY=y
-CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
-CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
-CONFIG_NETFILTER_XT_MATCH_QUOTA=y
-CONFIG_NETFILTER_XT_MATCH_SOCKET=y
-CONFIG_NETFILTER_XT_MATCH_STATE=y
-CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
-CONFIG_NETFILTER_XT_MATCH_STRING=y
-CONFIG_NETFILTER_XT_MATCH_TIME=y
-CONFIG_NETFILTER_XT_MATCH_U32=y
-CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
-CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
-CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
-CONFIG_NETFILTER_XT_TARGET_IDLETIMER=y
-CONFIG_NETFILTER_XT_TARGET_MARK=y
-CONFIG_NETFILTER_XT_TARGET_NFLOG=y
-CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
-CONFIG_NETFILTER_XT_TARGET_SECMARK=y
-CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
-CONFIG_NETFILTER_XT_TARGET_TPROXY=y
-CONFIG_NETFILTER_XT_TARGET_TRACE=y
-CONFIG_NET_CLS_ACT=y
-CONFIG_NET_CLS_U32=y
-CONFIG_NET_EMATCH=y
-CONFIG_NET_EMATCH_U32=y
-CONFIG_NET_KEY=y
-CONFIG_NET_SCHED=y
-CONFIG_NET_SCH_HTB=y
-CONFIG_NF_CONNTRACK=y
-CONFIG_NF_CONNTRACK_AMANDA=y
-CONFIG_NF_CONNTRACK_EVENTS=y
-CONFIG_NF_CONNTRACK_FTP=y
-CONFIG_NF_CONNTRACK_H323=y
-CONFIG_NF_CONNTRACK_IPV4=y
-CONFIG_NF_CONNTRACK_IPV6=y
-CONFIG_NF_CONNTRACK_IRC=y
-CONFIG_NF_CONNTRACK_NETBIOS_NS=y
-CONFIG_NF_CONNTRACK_PPTP=y
-CONFIG_NF_CONNTRACK_SANE=y
-CONFIG_NF_CONNTRACK_SECMARK=y
-CONFIG_NF_CONNTRACK_TFTP=y
-CONFIG_NF_CT_NETLINK=y
-CONFIG_NF_CT_PROTO_DCCP=y
-CONFIG_NF_CT_PROTO_SCTP=y
-CONFIG_NF_CT_PROTO_UDPLITE=y
-CONFIG_NF_NAT=y
-CONFIG_NO_HZ=y
-CONFIG_PACKET=y
-CONFIG_PM_AUTOSLEEP=y
-CONFIG_PM_WAKELOCKS=y
-CONFIG_PPP=y
-CONFIG_PPPOLAC=y
-CONFIG_PPPOPNS=y
-CONFIG_PPP_BSDCOMP=y
-CONFIG_PPP_DEFLATE=y
-CONFIG_PPP_MPPE=y
-CONFIG_PREEMPT=y
-CONFIG_PROFILING=y
-CONFIG_QFMT_V2=y
-CONFIG_QUOTA=y
-CONFIG_QUOTACTL=y
-CONFIG_QUOTA_NETLINK_INTERFACE=y
-CONFIG_QUOTA_TREE=y
-CONFIG_RANDOMIZE_BASE=y
-CONFIG_RTC_CLASS=y
-CONFIG_RT_GROUP_SCHED=y
-CONFIG_SECCOMP=y
-CONFIG_SECURITY=y
-CONFIG_SECURITY_NETWORK=y
-CONFIG_SECURITY_PERF_EVENTS_RESTRICT=y
-CONFIG_SECURITY_SELINUX=y
-CONFIG_STAGING=y
-CONFIG_SYNC=y
-CONFIG_TUN=y
-CONFIG_UID_SYS_STATS=y
-CONFIG_UNIX=y
-CONFIG_USB_CONFIGFS=y
-CONFIG_USB_CONFIGFS_F_ACC=y
-CONFIG_USB_CONFIGFS_F_AUDIO_SRC=y
-CONFIG_USB_CONFIGFS_F_FS=y
-CONFIG_USB_CONFIGFS_F_MIDI=y
-CONFIG_USB_CONFIGFS_F_MTP=y
-CONFIG_USB_CONFIGFS_F_PTP=y
-CONFIG_USB_CONFIGFS_UEVENT=y
-CONFIG_USB_GADGET=y
-CONFIG_XFRM_USER=y
diff --git a/kernel/configs/android-fetch-configs.sh b/kernel/configs/android-fetch-configs.sh
new file mode 100755
index 0000000..a5b56d4
--- /dev/null
+++ b/kernel/configs/android-fetch-configs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+curl https://android.googlesource.com/kernel/configs/+archive/master/android-4.9.tar.gz | tar xzv
+
diff --git a/kernel/configs/android-recommended.config b/kernel/configs/android-recommended.config
deleted file mode 100644
index 36ec6c1..0000000
--- a/kernel/configs/android-recommended.config
+++ /dev/null
@@ -1,133 +0,0 @@
-#  KEEP ALPHABETICALLY SORTED
-# CONFIG_AIO is not set
-# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
-# CONFIG_INPUT_MOUSE is not set
-# CONFIG_LEGACY_PTYS is not set
-# CONFIG_NF_CONNTRACK_SIP is not set
-# CONFIG_PM_WAKELOCKS_GC is not set
-# CONFIG_VT is not set
-CONFIG_ARM64_SW_TTBR0_PAN=y
-CONFIG_BACKLIGHT_LCD_SUPPORT=y
-CONFIG_BLK_DEV_DM=y
-CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=8192
-CONFIG_CC_STACKPROTECTOR_STRONG=y
-CONFIG_COMPACTION=y
-CONFIG_CPU_SW_DOMAIN_PAN=y
-CONFIG_DEBUG_RODATA=y
-CONFIG_DM_CRYPT=y
-CONFIG_DM_UEVENT=y
-CONFIG_DM_VERITY=y
-CONFIG_DM_VERITY_FEC=y
-CONFIG_DRAGONRISE_FF=y
-CONFIG_ENABLE_DEFAULT_TRACERS=y
-CONFIG_EXT4_FS=y
-CONFIG_EXT4_FS_SECURITY=y
-CONFIG_FUSE_FS=y
-CONFIG_GREENASIA_FF=y
-CONFIG_HIDRAW=y
-CONFIG_HID_A4TECH=y
-CONFIG_HID_ACRUX=y
-CONFIG_HID_ACRUX_FF=y
-CONFIG_HID_APPLE=y
-CONFIG_HID_BELKIN=y
-CONFIG_HID_CHERRY=y
-CONFIG_HID_CHICONY=y
-CONFIG_HID_CYPRESS=y
-CONFIG_HID_DRAGONRISE=y
-CONFIG_HID_ELECOM=y
-CONFIG_HID_EMS_FF=y
-CONFIG_HID_EZKEY=y
-CONFIG_HID_GREENASIA=y
-CONFIG_HID_GYRATION=y
-CONFIG_HID_HOLTEK=y
-CONFIG_HID_KENSINGTON=y
-CONFIG_HID_KEYTOUCH=y
-CONFIG_HID_KYE=y
-CONFIG_HID_LCPOWER=y
-CONFIG_HID_LOGITECH=y
-CONFIG_HID_LOGITECH_DJ=y
-CONFIG_HID_MAGICMOUSE=y
-CONFIG_HID_MICROSOFT=y
-CONFIG_HID_MONTEREY=y
-CONFIG_HID_MULTITOUCH=y
-CONFIG_HID_NTRIG=y
-CONFIG_HID_ORTEK=y
-CONFIG_HID_PANTHERLORD=y
-CONFIG_HID_PETALYNX=y
-CONFIG_HID_PICOLCD=y
-CONFIG_HID_PRIMAX=y
-CONFIG_HID_PRODIKEYS=y
-CONFIG_HID_ROCCAT=y
-CONFIG_HID_SAITEK=y
-CONFIG_HID_SAMSUNG=y
-CONFIG_HID_SMARTJOYPLUS=y
-CONFIG_HID_SONY=y
-CONFIG_HID_SPEEDLINK=y
-CONFIG_HID_SUNPLUS=y
-CONFIG_HID_THRUSTMASTER=y
-CONFIG_HID_TIVO=y
-CONFIG_HID_TOPSEED=y
-CONFIG_HID_TWINHAN=y
-CONFIG_HID_UCLOGIC=y
-CONFIG_HID_WACOM=y
-CONFIG_HID_WALTOP=y
-CONFIG_HID_WIIMOTE=y
-CONFIG_HID_ZEROPLUS=y
-CONFIG_HID_ZYDACRON=y
-CONFIG_INPUT_EVDEV=y
-CONFIG_INPUT_GPIO=y
-CONFIG_INPUT_JOYSTICK=y
-CONFIG_INPUT_KEYCHORD=y
-CONFIG_INPUT_KEYRESET=y
-CONFIG_INPUT_MISC=y
-CONFIG_INPUT_TABLET=y
-CONFIG_INPUT_UINPUT=y
-CONFIG_ION=y
-CONFIG_JOYSTICK_XPAD=y
-CONFIG_JOYSTICK_XPAD_FF=y
-CONFIG_JOYSTICK_XPAD_LEDS=y
-CONFIG_KALLSYMS_ALL=y
-CONFIG_KSM=y
-CONFIG_LOGIG940_FF=y
-CONFIG_LOGIRUMBLEPAD2_FF=y
-CONFIG_LOGITECH_FF=y
-CONFIG_MD=y
-CONFIG_MEDIA_SUPPORT=y
-CONFIG_MEMORY_STATE_TIME=y
-CONFIG_MSDOS_FS=y
-CONFIG_PANIC_TIMEOUT=5
-CONFIG_PANTHERLORD_FF=y
-CONFIG_PERF_EVENTS=y
-CONFIG_PM_DEBUG=y
-CONFIG_PM_RUNTIME=y
-CONFIG_PM_WAKELOCKS_LIMIT=0
-CONFIG_POWER_SUPPLY=y
-CONFIG_PSTORE=y
-CONFIG_PSTORE_CONSOLE=y
-CONFIG_PSTORE_RAM=y
-CONFIG_SCHEDSTATS=y
-CONFIG_SMARTJOYPLUS_FF=y
-CONFIG_SND=y
-CONFIG_SOUND=y
-CONFIG_SUSPEND_TIME=y
-CONFIG_TABLET_USB_ACECAD=y
-CONFIG_TABLET_USB_AIPTEK=y
-CONFIG_TABLET_USB_GTCO=y
-CONFIG_TABLET_USB_HANWANG=y
-CONFIG_TABLET_USB_KBTAB=y
-CONFIG_TASKSTATS=y
-CONFIG_TASK_DELAY_ACCT=y
-CONFIG_TASK_IO_ACCOUNTING=y
-CONFIG_TASK_XACCT=y
-CONFIG_TIMER_STATS=y
-CONFIG_TMPFS=y
-CONFIG_TMPFS_POSIX_ACL=y
-CONFIG_UHID=y
-CONFIG_MEMORY_STATE_TIME=y
-CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_EHCI_HCD=y
-CONFIG_USB_HIDDEV=y
-CONFIG_USB_USBNET=y
-CONFIG_VFAT_FS=y
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 69dc428..915e750 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -25,6 +25,7 @@
 #include <linux/smpboot.h>
 #include <linux/relay.h>
 #include <linux/slab.h>
+#include <linux/highmem.h>
 
 #include <trace/events/power.h>
 #define CREATE_TRACE_POINTS
@@ -239,6 +240,11 @@
 #define cpuhp_lock_acquire()      lock_map_acquire(&cpu_hotplug.dep_map)
 #define cpuhp_lock_release()      lock_map_release(&cpu_hotplug.dep_map)
 
+void cpu_hotplug_mutex_held(void)
+{
+	lockdep_assert_held(&cpu_hotplug.lock);
+}
+EXPORT_SYMBOL(cpu_hotplug_mutex_held);
 
 void get_online_cpus(void)
 {
@@ -893,6 +899,7 @@
 	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
 	int prev_state, ret = 0;
 	bool hasdied = false;
+	u64 start_time = 0;
 
 	if (num_online_cpus() == 1)
 		return -EBUSY;
@@ -904,6 +911,8 @@
 		return -EBUSY;
 
 	cpu_hotplug_begin();
+	if (trace_cpuhp_latency_enabled())
+		start_time = sched_clock();
 
 	cpuhp_tasks_frozen = tasks_frozen;
 
@@ -942,6 +951,7 @@
 
 	hasdied = prev_state != st->state && st->state == CPUHP_OFFLINE;
 out:
+	trace_cpuhp_latency(cpu, 0, start_time, ret);
 	cpu_hotplug_done();
 	/* This post dead nonsense must die */
 	if (!ret && hasdied)
@@ -1015,8 +1025,11 @@
 	struct cpuhp_cpu_state *st = per_cpu_ptr(&cpuhp_state, cpu);
 	struct task_struct *idle;
 	int ret = 0;
+	u64 start_time = 0;
 
 	cpu_hotplug_begin();
+	if (trace_cpuhp_latency_enabled())
+		start_time = sched_clock();
 
 	if (!cpu_present(cpu)) {
 		ret = -EINVAL;
@@ -1064,6 +1077,7 @@
 	target = min((int)target, CPUHP_BRINGUP_CPU);
 	ret = cpuhp_up_callbacks(cpu, st, target);
 out:
+	trace_cpuhp_latency(cpu, 1, start_time, ret);
 	cpu_hotplug_done();
 	return ret;
 }
@@ -1420,6 +1434,11 @@
 		.startup.single		= NULL,
 		.teardown.single	= rcutree_dying_cpu,
 	},
+	[CPUHP_AP_KMAP_DYING] = {
+		.name			= "KMAP:dying",
+		.startup.single		= NULL,
+		.teardown.single	= kmap_remove_unused_cpu,
+	},
 	/* Entry state on starting. Interrupts enabled from here on. Transient
 	 * state for synchronsization */
 	[CPUHP_AP_ONLINE] = {
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index a99cd8d..af9159a 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -61,6 +61,7 @@
 #include <linux/cgroup.h>
 #include <linux/wait.h>
 
+DEFINE_STATIC_KEY_FALSE(cpusets_pre_enable_key);
 DEFINE_STATIC_KEY_FALSE(cpusets_enabled_key);
 
 /* See "Frequency meter" comments, below. */
@@ -807,16 +808,15 @@
  * 'cpus' is removed, then call this routine to rebuild the
  * scheduler's dynamic sched domains.
  *
- * Call with cpuset_mutex held.  Takes get_online_cpus().
  */
-static void rebuild_sched_domains_locked(void)
+static void rebuild_sched_domains_unlocked(void)
 {
 	struct sched_domain_attr *attr;
 	cpumask_var_t *doms;
 	int ndoms;
 
+	cpu_hotplug_mutex_held();
 	lockdep_assert_held(&cpuset_mutex);
-	get_online_cpus();
 
 	/*
 	 * We have raced with CPU hotplug. Don't do anything to avoid
@@ -824,27 +824,27 @@
 	 * Anyways, hotplug work item will rebuild sched domains.
 	 */
 	if (!cpumask_equal(top_cpuset.effective_cpus, cpu_active_mask))
-		goto out;
+		return;
 
 	/* Generate domain masks and attrs */
 	ndoms = generate_sched_domains(&doms, &attr);
 
 	/* Have scheduler rebuild the domains */
 	partition_sched_domains(ndoms, doms, attr);
-out:
-	put_online_cpus();
 }
 #else /* !CONFIG_SMP */
-static void rebuild_sched_domains_locked(void)
+static void rebuild_sched_domains_unlocked(void)
 {
 }
 #endif /* CONFIG_SMP */
 
 void rebuild_sched_domains(void)
 {
+	get_online_cpus();
 	mutex_lock(&cpuset_mutex);
-	rebuild_sched_domains_locked();
+	rebuild_sched_domains_unlocked();
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 }
 
 /**
@@ -876,7 +876,6 @@
  *
  * On legacy hierachy, effective_cpus will be the same with cpu_allowed.
  *
- * Called with cpuset_mutex held
  */
 static void update_cpumasks_hier(struct cpuset *cs, struct cpumask *new_cpus)
 {
@@ -931,7 +930,7 @@
 	rcu_read_unlock();
 
 	if (need_rebuild_sched_domains)
-		rebuild_sched_domains_locked();
+		rebuild_sched_domains_unlocked();
 }
 
 /**
@@ -1281,7 +1280,7 @@
 		cs->relax_domain_level = val;
 		if (!cpumask_empty(cs->cpus_allowed) &&
 		    is_sched_load_balance(cs))
-			rebuild_sched_domains_locked();
+			rebuild_sched_domains_unlocked();
 	}
 
 	return 0;
@@ -1312,7 +1311,6 @@
  * cs:		the cpuset to update
  * turning_on: 	whether the flag is being set or cleared
  *
- * Call with cpuset_mutex held.
  */
 
 static int update_flag(cpuset_flagbits_t bit, struct cpuset *cs,
@@ -1347,7 +1345,7 @@
 	spin_unlock_irq(&callback_lock);
 
 	if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed)
-		rebuild_sched_domains_locked();
+		rebuild_sched_domains_unlocked();
 
 	if (spread_flag_changed)
 		update_tasks_flags(cs);
@@ -1615,6 +1613,7 @@
 	cpuset_filetype_t type = cft->private;
 	int retval = 0;
 
+	get_online_cpus();
 	mutex_lock(&cpuset_mutex);
 	if (!is_cpuset_online(cs)) {
 		retval = -ENODEV;
@@ -1652,6 +1651,7 @@
 	}
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 	return retval;
 }
 
@@ -1662,6 +1662,7 @@
 	cpuset_filetype_t type = cft->private;
 	int retval = -ENODEV;
 
+	get_online_cpus();
 	mutex_lock(&cpuset_mutex);
 	if (!is_cpuset_online(cs))
 		goto out_unlock;
@@ -1676,6 +1677,7 @@
 	}
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 	return retval;
 }
 
@@ -1714,6 +1716,7 @@
 	kernfs_break_active_protection(of->kn);
 	flush_work(&cpuset_hotplug_work);
 
+	get_online_cpus();
 	mutex_lock(&cpuset_mutex);
 	if (!is_cpuset_online(cs))
 		goto out_unlock;
@@ -1739,6 +1742,7 @@
 	free_trial_cpuset(trialcs);
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 	kernfs_unbreak_active_protection(of->kn);
 	css_put(&cs->css);
 	flush_workqueue(cpuset_migrate_mm_wq);
@@ -1907,6 +1911,7 @@
 	{
 		.name = "memory_pressure",
 		.read_u64 = cpuset_read_u64,
+		.private = FILE_MEMORY_PRESSURE,
 	},
 
 	{
@@ -2044,13 +2049,14 @@
 /*
  * If the cpuset being removed has its flag 'sched_load_balance'
  * enabled, then simulate turning sched_load_balance off, which
- * will call rebuild_sched_domains_locked().
+ * will call rebuild_sched_domains_unlocked().
  */
 
 static void cpuset_css_offline(struct cgroup_subsys_state *css)
 {
 	struct cpuset *cs = css_cs(css);
 
+	get_online_cpus();
 	mutex_lock(&cpuset_mutex);
 
 	if (is_sched_load_balance(cs))
@@ -2060,6 +2066,7 @@
 	clear_bit(CS_ONLINE, &cs->flags);
 
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 }
 
 static void cpuset_css_free(struct cgroup_subsys_state *css)
@@ -2305,6 +2312,13 @@
 	mutex_unlock(&cpuset_mutex);
 }
 
+static bool force_rebuild;
+
+void cpuset_force_rebuild(void)
+{
+	force_rebuild = true;
+}
+
 /**
  * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset
  *
@@ -2379,8 +2393,10 @@
 	}
 
 	/* rebuild sched domains if cpus_allowed has changed */
-	if (cpus_updated)
+	if (cpus_updated || force_rebuild) {
+		force_rebuild = false;
 		rebuild_sched_domains();
+	}
 }
 
 void cpuset_update_active_cpus(bool cpu_online)
@@ -2399,6 +2415,11 @@
 	schedule_work(&cpuset_hotplug_work);
 }
 
+void cpuset_wait_for_hotplug(void)
+{
+	flush_work(&cpuset_hotplug_work);
+}
+
 /*
  * Keep top_cpuset.mems_allowed tracking node_states[N_MEMORY].
  * Call this routine anytime after node_states[N_MEMORY] changes.
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 0d4a401..b784662 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -7963,6 +7963,7 @@
 		}
 	}
 	event->tp_event->prog = prog;
+	event->tp_event->bpf_prog_owner = event;
 
 	return 0;
 }
@@ -7977,7 +7978,7 @@
 		return;
 
 	prog = event->tp_event->prog;
-	if (prog) {
+	if (prog && event->tp_event->bpf_prog_owner == event) {
 		event->tp_event->prog = NULL;
 		bpf_prog_put(prog);
 	}
@@ -9894,28 +9895,27 @@
 			goto err_context;
 
 		/*
-		 * Do not allow to attach to a group in a different
-		 * task or CPU context:
+		 * Make sure we're both events for the same CPU;
+		 * grouping events for different CPUs is broken; since
+		 * you can never concurrently schedule them anyhow.
 		 */
-		if (move_group) {
-			/*
-			 * Make sure we're both on the same task, or both
-			 * per-cpu events.
-			 */
-			if (group_leader->ctx->task != ctx->task)
-				goto err_context;
+		if (group_leader->cpu != event->cpu)
+			goto err_context;
 
-			/*
-			 * Make sure we're both events for the same CPU;
-			 * grouping events for different CPUs is broken; since
-			 * you can never concurrently schedule them anyhow.
-			 */
-			if (group_leader->cpu != event->cpu)
-				goto err_context;
-		} else {
-			if (group_leader->ctx != ctx)
-				goto err_context;
-		}
+		/*
+		 * Make sure we're both on the same task, or both
+		 * per-CPU events.
+		 */
+		if (group_leader->ctx->task != ctx->task)
+			goto err_context;
+
+		/*
+		 * Do not allow to attach to a group in a different task
+		 * or CPU context. If we're moving SW events, we'll fix
+		 * this up later, so allow that.
+		 */
+		if (!move_group && group_leader->ctx != ctx)
+			goto err_context;
 
 		/*
 		 * Only a group leader can be exclusive or pinned
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index f9ec9ad..a1de021 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1254,8 +1254,6 @@
 
 void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
 {
-	newmm->uprobes_state.xol_area = NULL;
-
 	if (test_bit(MMF_HAS_UPROBES, &oldmm->flags)) {
 		set_bit(MMF_HAS_UPROBES, &newmm->flags);
 		/* unconditionally, dup_mmap() skips VM_DONTCOPY vmas */
diff --git a/kernel/fork.c b/kernel/fork.c
index 39c0709..610aded 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -745,6 +745,13 @@
 #endif
 }
 
+static void mm_init_uprobes_state(struct mm_struct *mm)
+{
+#ifdef CONFIG_UPROBES
+	mm->uprobes_state.xol_area = NULL;
+#endif
+}
+
 static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
 	struct user_namespace *user_ns)
 {
@@ -766,11 +773,13 @@
 	mm_init_cpumask(mm);
 	mm_init_aio(mm);
 	mm_init_owner(mm, p);
+	RCU_INIT_POINTER(mm->exe_file, NULL);
 	mmu_notifier_mm_init(mm);
 	clear_tlb_flush_pending(mm);
 #if defined(CONFIG_TRANSPARENT_HUGEPAGE) && !USE_SPLIT_PMD_PTLOCKS
 	mm->pmd_huge_pte = NULL;
 #endif
+	mm_init_uprobes_state(mm);
 
 	if (current->mm) {
 		mm->flags = current->mm->flags & MMF_INIT_MASK;
diff --git a/kernel/futex.c b/kernel/futex.c
index 4c6b6e6..88bad86 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -668,13 +668,14 @@
 		 * this reference was taken by ihold under the page lock
 		 * pinning the inode in place so i_lock was unnecessary. The
 		 * only way for this check to fail is if the inode was
-		 * truncated in parallel so warn for now if this happens.
+		 * truncated in parallel which is almost certainly an
+		 * application bug. In such a case, just retry.
 		 *
 		 * We are not calling into get_futex_key_refs() in file-backed
 		 * cases, therefore a successful atomic_inc return below will
 		 * guarantee that get_futex_key() will still imply smp_mb(); (B).
 		 */
-		if (WARN_ON_ONCE(!atomic_inc_not_zero(&inode->i_count))) {
+		if (!atomic_inc_not_zero(&inode->i_count)) {
 			rcu_read_unlock();
 			put_page(page);
 
diff --git a/kernel/gcov/base.c b/kernel/gcov/base.c
index 2f9df37..c51a49c 100644
--- a/kernel/gcov/base.c
+++ b/kernel/gcov/base.c
@@ -98,6 +98,12 @@
 }
 EXPORT_SYMBOL(__gcov_merge_icall_topn);
 
+void __gcov_exit(void)
+{
+	/* Unused. */
+}
+EXPORT_SYMBOL(__gcov_exit);
+
 /**
  * gcov_enable_events - enable event reporting through gcov_event()
  *
diff --git a/kernel/gcov/gcc_4_7.c b/kernel/gcov/gcc_4_7.c
index 6a5c239..46a18e7 100644
--- a/kernel/gcov/gcc_4_7.c
+++ b/kernel/gcov/gcc_4_7.c
@@ -18,7 +18,9 @@
 #include <linux/vmalloc.h>
 #include "gcov.h"
 
-#if (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
+#if (__GNUC__ >= 7)
+#define GCOV_COUNTERS			9
+#elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
 #define GCOV_COUNTERS			10
 #elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9
 #define GCOV_COUNTERS			9
diff --git a/kernel/hung_task.c b/kernel/hung_task.c
index 2b59c82..b94d3d1 100644
--- a/kernel/hung_task.c
+++ b/kernel/hung_task.c
@@ -17,6 +17,7 @@
 #include <linux/sysctl.h>
 #include <linux/utsname.h>
 #include <trace/events/sched.h>
+#include <linux/sched/sysctl.h>
 
 /*
  * The number of tasks checked:
@@ -24,6 +25,14 @@
 int __read_mostly sysctl_hung_task_check_count = PID_MAX_LIMIT;
 
 /*
+ * Selective monitoring of hung tasks.
+ *
+ * if set to 1, khungtaskd skips monitoring tasks, which has
+ * task_struct->hang_detection_enabled value not set, else monitors all tasks.
+ */
+int sysctl_hung_task_selective_monitoring = 1;
+
+/*
  * Limit number of tasks checked in a batch.
  *
  * This value controls the preemptibility of khungtaskd since preemption
@@ -179,7 +188,10 @@
 		}
 		/* use "==" to skip the TASK_KILLABLE tasks waiting on NFS */
 		if (t->state == TASK_UNINTERRUPTIBLE)
-			check_hung_task(t, timeout);
+			/* Check for selective monitoring */
+			if (!sysctl_hung_task_selective_monitoring ||
+			    t->hang_detection_enabled)
+				check_hung_task(t, timeout);
 	}
  unlock:
 	rcu_read_unlock();
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 077c87f..f30110e 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -895,13 +895,15 @@
 
 void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
 {
-	unsigned long flags;
+	unsigned long flags, trigger, tmp;
 	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
 	if (!desc)
 		return;
 	irq_settings_clr_and_set(desc, clr, set);
 
+	trigger = irqd_get_trigger_type(&desc->irq_data);
+
 	irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU |
 		   IRQD_TRIGGER_MASK | IRQD_LEVEL | IRQD_MOVE_PCNTXT);
 	if (irq_settings_has_no_balance_set(desc))
@@ -913,7 +915,11 @@
 	if (irq_settings_is_level(desc))
 		irqd_set(&desc->irq_data, IRQD_LEVEL);
 
-	irqd_set(&desc->irq_data, irq_settings_get_trigger_mask(desc));
+	tmp = irq_settings_get_trigger_mask(desc);
+	if (tmp != IRQ_TYPE_NONE)
+		trigger = tmp;
+
+	irqd_set(&desc->irq_data, trigger);
 
 	irq_put_desc_unlock(desc, flags);
 }
diff --git a/kernel/irq/ipi.c b/kernel/irq/ipi.c
index 1a9abc1..259a22a 100644
--- a/kernel/irq/ipi.c
+++ b/kernel/irq/ipi.c
@@ -165,7 +165,7 @@
 	struct irq_data *data = irq_get_irq_data(irq);
 	struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL;
 
-	if (!data || !ipimask || cpu > nr_cpu_ids)
+	if (!data || !ipimask || cpu >= nr_cpu_ids)
 		return INVALID_HWIRQ;
 
 	if (!cpumask_test_cpu(cpu, ipimask))
@@ -195,7 +195,7 @@
 	if (!chip->ipi_send_single && !chip->ipi_send_mask)
 		return -EINVAL;
 
-	if (cpu > nr_cpu_ids)
+	if (cpu >= nr_cpu_ids)
 		return -EINVAL;
 
 	if (dest) {
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 00bb0ae..77977f55df 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -405,10 +405,8 @@
 	 * The sysfs entry must be serialized against a concurrent
 	 * irq_sysfs_init() as well.
 	 */
-	mutex_lock(&sparse_irq_lock);
 	kobject_del(&desc->kobj);
 	delete_irq_desc(irq);
-	mutex_unlock(&sparse_irq_lock);
 
 	/*
 	 * We free the descriptor, masks and stat fields via RCU. That
@@ -446,20 +444,15 @@
 		desc = alloc_desc(start + i, node, flags, mask, owner);
 		if (!desc)
 			goto err;
-		mutex_lock(&sparse_irq_lock);
 		irq_insert_desc(start + i, desc);
 		irq_sysfs_add(start + i, desc);
-		mutex_unlock(&sparse_irq_lock);
 	}
+	bitmap_set(allocated_irqs, start, cnt);
 	return start;
 
 err:
 	for (i--; i >= 0; i--)
 		free_desc(start + i);
-
-	mutex_lock(&sparse_irq_lock);
-	bitmap_clear(allocated_irqs, start, cnt);
-	mutex_unlock(&sparse_irq_lock);
 	return -ENOMEM;
 }
 
@@ -558,6 +551,7 @@
 
 		desc->owner = owner;
 	}
+	bitmap_set(allocated_irqs, start, cnt);
 	return start;
 }
 
@@ -653,10 +647,10 @@
 	if (from >= nr_irqs || (from + cnt) > nr_irqs)
 		return;
 
+	mutex_lock(&sparse_irq_lock);
 	for (i = 0; i < cnt; i++)
 		free_desc(from + i);
 
-	mutex_lock(&sparse_irq_lock);
 	bitmap_clear(allocated_irqs, from, cnt);
 	mutex_unlock(&sparse_irq_lock);
 }
@@ -703,19 +697,15 @@
 					   from, cnt, 0);
 	ret = -EEXIST;
 	if (irq >=0 && start != irq)
-		goto err;
+		goto unlock;
 
 	if (start + cnt > nr_irqs) {
 		ret = irq_expand_nr_irqs(start + cnt);
 		if (ret)
-			goto err;
+			goto unlock;
 	}
-
-	bitmap_set(allocated_irqs, start, cnt);
-	mutex_unlock(&sparse_irq_lock);
-	return alloc_descs(start, cnt, node, affinity, owner);
-
-err:
+	ret = alloc_descs(start, cnt, node, affinity, owner);
+unlock:
 	mutex_unlock(&sparse_irq_lock);
 	return ret;
 }
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 3cbb0c8..f8f3f4c 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -14,6 +14,7 @@
 #include <linux/debugfs.h>
 #include <linux/uaccess.h>
 #include <linux/kcov.h>
+#include <asm/setup.h>
 
 /*
  * kcov descriptor (one per opened debugfs file).
@@ -68,6 +69,11 @@
 	if (mode == KCOV_MODE_TRACE) {
 		unsigned long *area;
 		unsigned long pos;
+		unsigned long ip = _RET_IP_;
+
+#ifdef CONFIG_RANDOMIZE_BASE
+		ip -= kaslr_offset();
+#endif
 
 		/*
 		 * There is some code that runs in interrupts but for which
@@ -81,7 +87,7 @@
 		/* The first word is number of subsequent PCs. */
 		pos = READ_ONCE(area[0]) + 1;
 		if (likely(pos < t->kcov_size)) {
-			area[pos] = _RET_IP_;
+			area[pos] = ip;
 			WRITE_ONCE(area[0], pos);
 		}
 	}
diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
index 4d7ffc0..6599c7f 100644
--- a/kernel/locking/lockdep.c
+++ b/kernel/locking/lockdep.c
@@ -3260,10 +3260,17 @@
 	if (depth) {
 		hlock = curr->held_locks + depth - 1;
 		if (hlock->class_idx == class_idx && nest_lock) {
-			if (hlock->references)
+			if (hlock->references) {
+				/*
+				 * Check: unsigned int references:12, overflow.
+				 */
+				if (DEBUG_LOCKS_WARN_ON(hlock->references == (1 << 12)-1))
+					return 0;
+
 				hlock->references++;
-			else
+			} else {
 				hlock->references = 2;
+			}
 
 			return 1;
 		}
diff --git a/kernel/locking/locktorture.c b/kernel/locking/locktorture.c
index f8c5af5..d3de04b 100644
--- a/kernel/locking/locktorture.c
+++ b/kernel/locking/locktorture.c
@@ -780,6 +780,10 @@
 	else
 		lock_torture_print_module_parms(cxt.cur_ops,
 						"End of test: SUCCESS");
+
+	kfree(cxt.lwsa);
+	kfree(cxt.lrsa);
+
 end:
 	torture_cleanup_end();
 }
@@ -924,6 +928,8 @@
 				       GFP_KERNEL);
 		if (reader_tasks == NULL) {
 			VERBOSE_TOROUT_ERRSTRING("reader_tasks: Out of memory");
+			kfree(writer_tasks);
+			writer_tasks = NULL;
 			firsterr = -ENOMEM;
 			goto unwind;
 		}
diff --git a/kernel/locking/osq_lock.c b/kernel/locking/osq_lock.c
index 05a3785..0befa20 100644
--- a/kernel/locking/osq_lock.c
+++ b/kernel/locking/osq_lock.c
@@ -1,6 +1,7 @@
 #include <linux/percpu.h>
 #include <linux/sched.h>
 #include <linux/osq_lock.h>
+#include <linux/sched/rt.h>
 
 /*
  * An MCS like lock especially tailored for optimistic spinning for sleeping
@@ -85,6 +86,7 @@
 {
 	struct optimistic_spin_node *node = this_cpu_ptr(&osq_node);
 	struct optimistic_spin_node *prev, *next;
+	struct task_struct *task = current;
 	int curr = encode_cpu(smp_processor_id());
 	int old;
 
@@ -104,6 +106,19 @@
 
 	prev = decode_cpu(old);
 	node->prev = prev;
+
+	/*
+	 * osq_lock()			unqueue
+	 *
+	 * node->prev = prev		osq_wait_next()
+	 * WMB				MB
+	 * prev->next = node		next->prev = prev // unqueue-C
+	 *
+	 * Here 'node->prev' and 'next->prev' are the same variable and we need
+	 * to ensure these stores happen in-order to avoid corrupting the list.
+	 */
+	smp_wmb();
+
 	WRITE_ONCE(prev->next, node);
 
 	/*
@@ -118,8 +133,13 @@
 	while (!READ_ONCE(node->locked)) {
 		/*
 		 * If we need to reschedule bail... so we can block.
+		 * If a task spins on owner on a CPU after acquiring
+		 * osq_lock while a RT task spins on another CPU  to
+		 * acquire osq_lock, it will starve the owner from
+		 * completing if owner is to be scheduled on the same CPU.
+		 * It will be a live lock.
 		 */
-		if (need_resched())
+		if (need_resched() || rt_task(task))
 			goto unqueue;
 
 		cpu_relax_lowlatency();
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 2337b4b..a4112df 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -574,6 +574,33 @@
 	WAKE_Q(wake_q);
 
 	/*
+	* __rwsem_down_write_failed_common(sem)
+	*   rwsem_optimistic_spin(sem)
+	*     osq_unlock(sem->osq)
+	*   ...
+	*   atomic_long_add_return(&sem->count)
+	*
+	*      - VS -
+	*
+	*              __up_write()
+	*                if (atomic_long_sub_return_release(&sem->count) < 0)
+	*                  rwsem_wake(sem)
+	*                    osq_is_locked(&sem->osq)
+	*
+	* And __up_write() must observe !osq_is_locked() when it observes the
+	* atomic_long_add_return() in order to not miss a wakeup.
+	*
+	* This boils down to:
+	*
+	* [S.rel] X = 1                [RmW] r0 = (Y += 0)
+	*         MB                         RMB
+	* [RmW]   Y += 1               [L]   r1 = X
+	*
+	* exists (r0=1 /\ r1=0)
+	*/
+	smp_rmb();
+
+	/*
 	 * If a spinner is present, it is not necessary to do the wakeup.
 	 * Try to do wakeup only if the trylock succeeds to minimize
 	 * spinlock contention which may introduce too much delay in the
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 6b00a29..f4330a2 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -29,15 +29,6 @@
 	  of suspend, or they are content with invoking sync() from
 	  user-space before invoking suspend.  Say Y if that's your case.
 
-config WAKELOCK
-	bool "Android's method of preventing suspend"
-	default y
-	---help---
-	  This allows applications to prevent the CPU from suspending while
-	  they need it.
-
-	  Say Y if you are running an android userspace.
-
 config HIBERNATE_CALLBACKS
 	bool
 
diff --git a/kernel/power/process.c b/kernel/power/process.c
index 68d27ae..9a12c83 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -19,8 +19,9 @@
 #include <linux/kmod.h>
 #include <trace/events/power.h>
 #include <linux/wakeup_reason.h>
+#include <linux/cpuset.h>
 
-/* 
+/*
  * Timeout for stopping processes
  */
 unsigned int __read_mostly freeze_timeout_msecs = 20 * MSEC_PER_SEC;
@@ -210,6 +211,8 @@
 	__usermodehelper_set_disable_depth(UMH_FREEZING);
 	thaw_workqueues();
 
+	cpuset_wait_for_hotplug();
+
 	read_lock(&tasklist_lock);
 	for_each_process_thread(g, p) {
 		/* No other threads should have PF_SUSPEND_TASK set */
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 009f788..5183134 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -267,7 +267,8 @@
 	.release        = single_release,
 };
 
-static inline void pm_qos_set_value_for_cpus(struct pm_qos_constraints *c)
+static inline void pm_qos_set_value_for_cpus(struct pm_qos_constraints *c,
+		struct cpumask *cpus)
 {
 	struct pm_qos_request *req = NULL;
 	int cpu;
@@ -294,8 +295,11 @@
 		}
 	}
 
-	for_each_possible_cpu(cpu)
+	for_each_possible_cpu(cpu) {
+		if (c->target_per_cpu[cpu] != qos_val[cpu])
+			cpumask_set_cpu(cpu, cpus);
 		c->target_per_cpu[cpu] = qos_val[cpu];
+	}
 }
 
 /**
@@ -316,6 +320,7 @@
 	unsigned long flags;
 	int prev_value, curr_value, new_value;
 	struct plist_node *node = &req->node;
+	struct cpumask cpus;
 	int ret;
 
 	spin_lock_irqsave(&pm_qos_lock, flags);
@@ -346,18 +351,24 @@
 	}
 
 	curr_value = pm_qos_get_value(c);
+	cpumask_clear(&cpus);
 	pm_qos_set_value(c, curr_value);
-	pm_qos_set_value_for_cpus(c);
+	pm_qos_set_value_for_cpus(c, &cpus);
 
 	spin_unlock_irqrestore(&pm_qos_lock, flags);
 
 	trace_pm_qos_update_target(action, prev_value, curr_value);
-	if (prev_value != curr_value) {
+
+	/*
+	 * if cpu mask bits are set, call the notifier call chain
+	 * to update the new qos restriction for the cores
+	 */
+
+	if (!cpumask_empty(&cpus)) {
 		ret = 1;
 		if (c->notifiers)
 			blocking_notifier_call_chain(c->notifiers,
-						     (unsigned long)curr_value,
-						     NULL);
+				     (unsigned long)curr_value, &cpus);
 	} else {
 		ret = 0;
 	}
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 4f8f9c1..084c41c 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -792,8 +792,13 @@
 	long long oldval;
 	struct rcu_dynticks *rdtp;
 
-	RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!");
 	rdtp = this_cpu_ptr(&rcu_dynticks);
+
+	/* Page faults can happen in NMI handlers, so check... */
+	if (READ_ONCE(rdtp->dynticks_nmi_nesting))
+		return;
+
+	RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!");
 	oldval = rdtp->dynticks_nesting;
 	rdtp->dynticks_nesting--;
 	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
@@ -930,8 +935,13 @@
 	struct rcu_dynticks *rdtp;
 	long long oldval;
 
-	RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!");
 	rdtp = this_cpu_ptr(&rcu_dynticks);
+
+	/* Page faults can happen in NMI handlers, so check... */
+	if (READ_ONCE(rdtp->dynticks_nmi_nesting))
+		return;
+
+	RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!");
 	oldval = rdtp->dynticks_nesting;
 	rdtp->dynticks_nesting++;
 	WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) &&
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index f6cce95..4b87c4e 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -29,4 +29,3 @@
 obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o
 obj-$(CONFIG_SCHED_CORE_CTL) += core_ctl.o
 obj-$(CONFIG_CPU_FREQ_GOV_SCHED) += cpufreq_sched.o
-obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 4573d45..bbe783e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -525,8 +525,7 @@
 	struct rq *rq = cpu_rq(cpu);
 	unsigned long flags;
 
-	if (!raw_spin_trylock_irqsave(&rq->lock, flags))
-		return;
+	raw_spin_lock_irqsave(&rq->lock, flags);
 	resched_curr(rq);
 	raw_spin_unlock_irqrestore(&rq->lock, flags);
 }
@@ -1170,6 +1169,7 @@
 	cpumask_t allowed_mask;
 
 	rq = task_rq_lock(p, &rf);
+	update_rq_clock(rq);
 
 	if (p->flags & PF_KTHREAD) {
 		/*
@@ -1219,7 +1219,6 @@
 	if (cpumask_test_cpu(task_cpu(p), &allowed_mask))
 		goto out;
 
-	dest_cpu = cpumask_any_and(cpu_valid_mask, new_mask);
 	if (task_running(rq, p) || p->state == TASK_WAKING) {
 		struct migration_arg arg = { p, dest_cpu };
 		/* Need help from migration thread: drop lock and wait. */
@@ -3232,9 +3231,9 @@
 
 static inline
 unsigned long sum_capacity_reqs(unsigned long cfs_cap,
-				struct sched_capacity_reqs *scr)
+				struct sched_capacity_reqs *scr, int cpu)
 {
-	unsigned long total = add_capacity_margin(cfs_cap + scr->rt);
+	unsigned long total = add_capacity_margin(cfs_cap + scr->rt, cpu);
 	return total += scr->dl;
 }
 
@@ -3246,7 +3245,7 @@
 	struct sched_capacity_reqs *scr;
 
 	scr = &per_cpu(cpu_sched_capacity_reqs, cpu);
-	if (sum_capacity_reqs(cpu_utilization, scr) < capacity_curr)
+	if (sum_capacity_reqs(cpu_utilization, scr, cpu) < capacity_curr)
 		return;
 
 	/*
@@ -3305,6 +3304,7 @@
 	bool early_notif;
 	u32 old_load;
 	struct related_thread_group *grp;
+	unsigned int flag = 0;
 
 	sched_clock_tick();
 
@@ -3321,10 +3321,12 @@
 	cpu_load_update_active(rq);
 	calc_global_load_tick(rq);
 	sched_freq_tick(cpu);
-	cpufreq_update_util(rq, 0);
 
 	early_notif = early_detection_notify(rq, wallclock);
+	if (early_notif)
+		flag = SCHED_CPUFREQ_WALT | SCHED_CPUFREQ_EARLY_DET;
 
+	cpufreq_update_util(rq, flag);
 	raw_spin_unlock(&rq->lock);
 
 	if (early_notif)
@@ -7958,16 +7960,15 @@
 		 * operation in the resume sequence, just build a single sched
 		 * domain, ignoring cpusets.
 		 */
-		num_cpus_frozen--;
-		if (likely(num_cpus_frozen)) {
-			partition_sched_domains(1, NULL, NULL);
+		partition_sched_domains(1, NULL, NULL);
+		if (--num_cpus_frozen)
 			return;
-		}
 		/*
 		 * This is the last CPU online operation. So fall through and
 		 * restore the original sched domains by considering the
 		 * cpuset configurations.
 		 */
+		cpuset_force_rebuild();
 	}
 	cpuset_update_active_cpus(true);
 }
@@ -8659,6 +8660,7 @@
 	struct rq *rq;
 
 	rq = task_rq_lock(tsk, &rf);
+	update_rq_clock(rq);
 
 	running = task_current(rq, tsk);
 	queued = task_on_rq_queued(tsk);
@@ -9620,4 +9622,51 @@
 }
 #endif /* CONFIG_SCHED_WALT */
 
-__read_mostly bool sched_predl;
+__read_mostly bool sched_predl = 1;
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+int
+find_first_cpu_bit(struct task_struct *p, const cpumask_t *search_cpus,
+		   struct sched_group *sg_target, bool *avoid_prev_cpu,
+		   bool *do_rotate, struct find_first_cpu_bit_env *env)
+{
+	int i = -1;
+	unsigned long mcc;
+	int cpu = smp_processor_id();
+
+	mcc = cpu_rq(cpu)->rd->max_cpu_capacity.val;
+
+	/* do rotation only for big CPUs. */
+	*do_rotate = (cpumask_first(search_cpus) < nr_cpu_ids &&
+		     capacity_orig_of(cpumask_first(search_cpus)) == mcc);
+
+	if (*do_rotate) {
+		if (time_before_eq(jiffies, *env->avoid_prev_cpu_last +
+				   env->interval))
+			return *env->rotate_cpu_start;
+
+		spin_lock(env->rotate_lock);
+		if (time_after(jiffies, *env->avoid_prev_cpu_last +
+					env->interval)) {
+			cpumask_t tmpmask;
+
+			*env->avoid_prev_cpu_last = jiffies;
+			*avoid_prev_cpu = true;
+
+			cpumask_copy(&tmpmask, sched_group_cpus(sg_target));
+			cpumask_andnot(&tmpmask, &tmpmask, cpu_isolated_mask);
+
+			i = cpumask_next(*env->rotate_cpu_start, &tmpmask);
+			if (i >= nr_cpu_ids)
+				i = cpumask_first(&tmpmask) - 1;
+			/* Change start CPU every interval. */
+			*env->rotate_cpu_start = i;
+		} else {
+			i = *env->rotate_cpu_start;
+		}
+		spin_unlock(env->rotate_lock);
+	}
+
+	return i;
+}
+#endif
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index e56af41..cc5a97c 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -19,8 +19,11 @@
 #include <linux/kthread.h>
 #include <linux/sched.h>
 #include <linux/sched/rt.h>
+#include <linux/syscore_ops.h>
 
 #include <trace/events/sched.h>
+#include "sched.h"
+#include "walt.h"
 
 #define MAX_CPUS_PER_CLUSTER 6
 #define MAX_CLUSTERS 2
@@ -35,6 +38,11 @@
 	unsigned int active_cpus;
 	unsigned int num_cpus;
 	unsigned int nr_isolated_cpus;
+	unsigned int nr_not_preferred_cpus;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	unsigned long set_max;
+	unsigned long set_cur;
+#endif
 	cpumask_t cpu_mask;
 	unsigned int need_cpus;
 	unsigned int task_thres;
@@ -76,6 +84,7 @@
 static bool initialized;
 
 static unsigned int get_active_cpu_count(const struct cluster_data *cluster);
+static void cpuset_next(struct cluster_data *cluster);
 
 /* ========================= sysfs interface =========================== */
 
@@ -88,6 +97,7 @@
 		return -EINVAL;
 
 	state->min_cpus = min(val, state->max_cpus);
+	cpuset_next(state);
 	wake_up_core_ctl_thread(state);
 
 	return count;
@@ -109,6 +119,7 @@
 	val = min(val, state->num_cpus);
 	state->max_cpus = val;
 	state->min_cpus = min(state->min_cpus, state->max_cpus);
+	cpuset_next(state);
 	wake_up_core_ctl_thread(state);
 
 	return count;
@@ -340,6 +351,7 @@
 	unsigned int val[MAX_CPUS_PER_CLUSTER];
 	unsigned long flags;
 	int ret;
+	int not_preferred_count = 0;
 
 	ret = sscanf(buf, "%u %u %u %u %u %u\n",
 			&val[0], &val[1], &val[2], &val[3],
@@ -351,7 +363,9 @@
 	for (i = 0; i < state->num_cpus; i++) {
 		c = &per_cpu(cpu_state, i + state->first_cpu);
 		c->not_preferred = val[i];
+		not_preferred_count += !!val[i];
 	}
+	state->nr_not_preferred_cpus = not_preferred_count;
 	spin_unlock_irqrestore(&state_lock, flags);
 
 	return count;
@@ -464,16 +478,19 @@
 	int max_nr, big_max_nr;
 	struct cluster_data *cluster;
 	unsigned int index = 0;
+	unsigned long flags;
 
 	sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg,
 				 &max_nr, &big_max_nr);
 
+	spin_lock_irqsave(&state_lock, flags);
 	for_each_cluster(cluster, index) {
 		if (!cluster->inited)
 			continue;
 		cluster->nrrun = cluster->is_big_cluster ? big_avg : avg;
 		cluster->max_nr = cluster->is_big_cluster ? big_max_nr : max_nr;
 	}
+	spin_unlock_irqrestore(&state_lock, flags);
 }
 
 #define MAX_NR_THRESHOLD	4
@@ -547,10 +564,16 @@
 		cluster->active_cpus = get_active_cpu_count(cluster);
 		thres_idx = cluster->active_cpus ? cluster->active_cpus - 1 : 0;
 		list_for_each_entry(c, &cluster->lru, sib) {
-			if (c->busy >= cluster->busy_up_thres[thres_idx])
+			bool old_is_busy = c->is_busy;
+
+			if (c->busy >= cluster->busy_up_thres[thres_idx] ||
+			    sched_cpu_high_irqload(c->cpu))
 				c->is_busy = true;
 			else if (c->busy < cluster->busy_down_thres[thres_idx])
 				c->is_busy = false;
+
+			trace_core_ctl_set_busy(c->cpu, c->busy, old_is_busy,
+						c->is_busy);
 			need_cpus += c->is_busy;
 		}
 		need_cpus = apply_task_need(cluster, need_cpus);
@@ -591,17 +614,6 @@
 		wake_up_core_ctl_thread(cluster);
 }
 
-static void core_ctl_set_busy(struct cpu_data *c, unsigned int busy)
-{
-	unsigned int old_is_busy = c->is_busy;
-
-	if (c->busy == busy)
-		return;
-
-	c->busy = busy;
-	trace_core_ctl_set_busy(c->cpu, busy, old_is_busy, c->is_busy);
-}
-
 /* ========================= core count enforcement ==================== */
 
 static void wake_up_core_ctl_thread(struct cluster_data *cluster)
@@ -629,26 +641,27 @@
 
 	spin_lock_irqsave(&state_lock, flags);
 	for_each_cluster(cluster, index) {
-		if (cluster->is_big_cluster) {
-			if (boost) {
-				boost_state_changed = !cluster->boost;
-				++cluster->boost;
+		if (boost) {
+			boost_state_changed = !cluster->boost;
+			++cluster->boost;
+		} else {
+			if (!cluster->boost) {
+				pr_err("Error turning off boost. Boost already turned off\n");
+				ret = -EINVAL;
+				break;
 			} else {
-				if (!cluster->boost) {
-					pr_err("Error turning off boost. Boost already turned off\n");
-					ret = -EINVAL;
-				} else {
-					--cluster->boost;
-					boost_state_changed = !cluster->boost;
-				}
+				--cluster->boost;
+				boost_state_changed = !cluster->boost;
 			}
-			break;
 		}
 	}
 	spin_unlock_irqrestore(&state_lock, flags);
 
-	if (boost_state_changed)
-		apply_need(cluster);
+	if (boost_state_changed) {
+		index = 0;
+		for_each_cluster(cluster, index)
+			apply_need(cluster);
+	}
 
 	trace_core_ctl_set_boost(cluster->boost, ret);
 
@@ -659,10 +672,10 @@
 void core_ctl_check(u64 window_start)
 {
 	int cpu;
-	unsigned int busy;
 	struct cpu_data *c;
 	struct cluster_data *cluster;
 	unsigned int index = 0;
+	unsigned long flags;
 
 	if (unlikely(!initialized))
 		return;
@@ -672,6 +685,7 @@
 
 	core_ctl_check_timestamp = window_start;
 
+	spin_lock_irqsave(&state_lock, flags);
 	for_each_possible_cpu(cpu) {
 
 		c = &per_cpu(cpu_state, cpu);
@@ -680,9 +694,9 @@
 		if (!cluster || !cluster->inited)
 			continue;
 
-		busy = sched_get_cpu_util(cpu);
-		core_ctl_set_busy(c, busy);
+		c->busy = sched_get_cpu_util(cpu);
 	}
+	spin_unlock_irqrestore(&state_lock, flags);
 
 	update_running_avg();
 
@@ -702,6 +716,67 @@
 	spin_unlock_irqrestore(&state_lock, flags);
 }
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static void cpuset_next(struct cluster_data *cluster)
+{
+	int cpus_needed = cluster->num_cpus - cluster->min_cpus;
+
+	cluster->set_cur++;
+	cluster->set_cur = min(cluster->set_cur, cluster->set_max);
+
+	/*
+	 * This loop generates bit sets from 0 to pow(num_cpus, 2) - 1.
+	 * We start loop from set_cur to set_cur - 1 and break when weight of
+	 * set_cur equals to cpus_needed.
+	 */
+
+	while (1) {
+		if (bitmap_weight(&cluster->set_cur, BITS_PER_LONG) ==
+		    cpus_needed) {
+			break;
+		}
+		cluster->set_cur++;
+		cluster->set_cur = min(cluster->set_cur, cluster->set_max);
+		if (cluster->set_cur == cluster->set_max)
+			/* roll over */
+			cluster->set_cur = 0;
+	};
+
+	pr_debug("first_cpu=%d cpus_needed=%d set_cur=0x%lx\n",
+		 cluster->first_cpu, cpus_needed, cluster->set_cur);
+}
+
+static bool should_we_isolate(int cpu, struct cluster_data *cluster)
+{
+	/* cpu should be part of cluster */
+	return !!(cluster->set_cur & (1 << (cpu - cluster->first_cpu)));
+}
+
+static void core_ctl_resume(void)
+{
+	unsigned int i = 0;
+	struct cluster_data *cluster;
+
+	/* move to next isolation cpu set */
+	for_each_cluster(cluster, i)
+		cpuset_next(cluster);
+}
+
+static struct syscore_ops core_ctl_syscore_ops = {
+	.resume	= core_ctl_resume,
+};
+
+#else
+
+static void cpuset_next(struct cluster_data *cluster) { }
+
+static bool should_we_isolate(int cpu, struct cluster_data *cluster)
+{
+	return true;
+}
+
+#endif
+
 static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
 {
 	struct cpu_data *c, *tmp;
@@ -722,10 +797,21 @@
 			continue;
 		if (cluster->active_cpus == need)
 			break;
-		/* Don't offline busy CPUs. */
+		/* Don't isolate busy CPUs. */
 		if (c->is_busy)
 			continue;
 
+		/*
+		 * We isolate only the not_preferred CPUs. If none
+		 * of the CPUs are selected as not_preferred, then
+		 * all CPUs are eligible for isolation.
+		 */
+		if (cluster->nr_not_preferred_cpus && !c->not_preferred)
+			continue;
+
+		if (!should_we_isolate(c->cpu, cluster))
+			continue;
+
 		spin_unlock_irqrestore(&state_lock, flags);
 
 		pr_debug("Trying to isolate CPU%u\n", c->cpu);
@@ -1029,7 +1115,13 @@
 	cluster->offline_delay_ms = 100;
 	cluster->task_thres = UINT_MAX;
 	cluster->nrrun = cluster->num_cpus;
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	cluster->set_max = cluster->num_cpus * cluster->num_cpus;
+	/* by default mark all cpus as eligible */
+	cluster->set_cur = cluster->set_max - 1;
+#endif
 	cluster->enable = true;
+	cluster->nr_not_preferred_cpus = 0;
 	INIT_LIST_HEAD(&cluster->lru);
 	spin_lock_init(&cluster->pending_lock);
 
@@ -1065,6 +1157,10 @@
 	if (should_skip(cpu_possible_mask))
 		return 0;
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+	register_syscore_ops(&core_ctl_syscore_ops);
+#endif
+
 	cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
 			"core_ctl/isolation:online",
 			core_ctl_isolation_online_cpu, NULL);
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 116ffe4..32b67eb 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -28,6 +28,7 @@
 struct sugov_tunables {
 	struct gov_attr_set attr_set;
 	unsigned int rate_limit_us;
+	unsigned int hispeed_load;
 	unsigned int hispeed_freq;
 	bool pl;
 };
@@ -84,6 +85,8 @@
 };
 
 static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu);
+static unsigned int stale_ns;
+static DEFINE_PER_CPU(struct sugov_tunables *, cached_tunables);
 
 /************************ Governor internals ***********************/
 
@@ -160,6 +163,7 @@
 				policy->cpuinfo.max_freq : policy->cur;
 
 	freq = (freq + (freq >> 2)) * util / max;
+	trace_sugov_next_freq(policy->cpu, util, max, freq);
 
 	if (freq == sg_policy->cached_raw_freq && sg_policy->next_freq != UINT_MAX)
 		return sg_policy->next_freq;
@@ -231,7 +235,9 @@
 
 	/* Track cycles in current window */
 	delta_ns = upto - sg_policy->last_cyc_update_time;
-	cycles = (prev_freq * delta_ns) / (NSEC_PER_SEC / KHZ);
+	delta_ns *= prev_freq;
+	do_div(delta_ns, (NSEC_PER_SEC / KHZ));
+	cycles = delta_ns;
 	sg_policy->curr_cycles += cycles;
 	sg_policy->last_cyc_update_time = upto;
 }
@@ -265,7 +271,7 @@
 }
 
 #define NL_RATIO 75
-#define HISPEED_LOAD 90
+#define DEFAULT_HISPEED_LOAD 90
 static void sugov_walt_adjust(struct sugov_cpu *sg_cpu, unsigned long *util,
 			      unsigned long *max)
 {
@@ -279,7 +285,7 @@
 		return;
 
 	is_hiload = (cpu_util >= mult_frac(sg_policy->avg_cap,
-					   HISPEED_LOAD,
+					   sg_policy->tunables->hispeed_load,
 					   100));
 
 	if (is_hiload && !is_migration)
@@ -311,10 +317,15 @@
 	struct sugov_cpu *sg_cpu = container_of(hook, struct sugov_cpu, update_util);
 	struct sugov_policy *sg_policy = sg_cpu->sg_policy;
 	struct cpufreq_policy *policy = sg_policy->policy;
-	unsigned long util, max;
+	unsigned long util, max, hs_util;
 	unsigned int next_f;
 	bool busy;
 
+	flags &= ~SCHED_CPUFREQ_RT_DL;
+
+	if (!sg_policy->tunables->pl && flags & SCHED_CPUFREQ_PL)
+		return;
+
 	sugov_set_iowait_boost(sg_cpu, time, flags);
 	sg_cpu->last_update = time;
 
@@ -322,15 +333,30 @@
 		return;
 
 	busy = sugov_cpu_is_busy(sg_cpu);
-	flags &= ~SCHED_CPUFREQ_RT_DL;
 
+	raw_spin_lock(&sg_policy->update_lock);
 	if (flags & SCHED_CPUFREQ_RT_DL) {
 		next_f = policy->cpuinfo.max_freq;
 	} else {
 		sugov_get_util(&util, &max, sg_cpu->cpu);
-		sugov_iowait_boost(sg_cpu, &util, &max);
+		if (sg_policy->max != max) {
+			sg_policy->max = max;
+			hs_util = freq_to_util(sg_policy,
+					sg_policy->tunables->hispeed_freq);
+			hs_util = mult_frac(hs_util, TARGET_LOAD, 100);
+			sg_policy->hispeed_util = hs_util;
+		}
+
+		sg_cpu->util = util;
+		sg_cpu->max = max;
+		sg_cpu->flags = flags;
 		sugov_calc_avg_cap(sg_policy, sg_cpu->walt_load.ws,
 				   sg_policy->policy->cur);
+		trace_sugov_util_update(sg_cpu->cpu, sg_cpu->util,
+					sg_policy->avg_cap,
+					max, sg_cpu->walt_load.nl,
+					sg_cpu->walt_load.pl, flags);
+		sugov_iowait_boost(sg_cpu, &util, &max);
 		sugov_walt_adjust(sg_cpu, &util, &max);
 		next_f = get_next_freq(sg_policy, util, max);
 		/*
@@ -341,6 +367,7 @@
 			next_f = sg_policy->next_freq;
 	}
 	sugov_update_commit(sg_policy, time, next_f);
+	raw_spin_unlock(&sg_policy->update_lock);
 }
 
 static unsigned int sugov_next_freq_shared(struct sugov_cpu *sg_cpu)
@@ -364,7 +391,7 @@
 		 * idle now (and clear iowait_boost for it).
 		 */
 		delta_ns = last_freq_update_time - j_sg_cpu->last_update;
-		if (delta_ns > sched_ravg_window) {
+		if (delta_ns > stale_ns) {
 			j_sg_cpu->iowait_boost = 0;
 			continue;
 		}
@@ -373,7 +400,7 @@
 
 		j_util = j_sg_cpu->util;
 		j_max = j_sg_cpu->max;
-		if (j_util * max > j_max * util) {
+		if (j_util * max >= j_max * util) {
 			util = j_util;
 			max = j_max;
 		}
@@ -393,6 +420,9 @@
 	unsigned long util, max, hs_util;
 	unsigned int next_f;
 
+	if (!sg_policy->tunables->pl && flags & SCHED_CPUFREQ_PL)
+		return;
+
 	sugov_get_util(&util, &max, sg_cpu->cpu);
 
 	flags &= ~SCHED_CPUFREQ_RT_DL;
@@ -507,11 +537,31 @@
 	return count;
 }
 
+static ssize_t hispeed_load_show(struct gov_attr_set *attr_set, char *buf)
+{
+	struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->hispeed_load);
+}
+
+static ssize_t hispeed_load_store(struct gov_attr_set *attr_set,
+				  const char *buf, size_t count)
+{
+	struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
+
+	if (kstrtouint(buf, 10, &tunables->hispeed_load))
+		return -EINVAL;
+
+	tunables->hispeed_load = min(100U, tunables->hispeed_load);
+
+	return count;
+}
+
 static ssize_t hispeed_freq_show(struct gov_attr_set *attr_set, char *buf)
 {
 	struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
 
-	return sprintf(buf, "%u\n", tunables->hispeed_freq);
+	return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->hispeed_freq);
 }
 
 static ssize_t hispeed_freq_store(struct gov_attr_set *attr_set,
@@ -543,7 +593,7 @@
 {
 	struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
 
-	return sprintf(buf, "%u\n", tunables->pl);
+	return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->pl);
 }
 
 static ssize_t pl_store(struct gov_attr_set *attr_set, const char *buf,
@@ -558,11 +608,13 @@
 }
 
 static struct governor_attr rate_limit_us = __ATTR_RW(rate_limit_us);
+static struct governor_attr hispeed_load = __ATTR_RW(hispeed_load);
 static struct governor_attr hispeed_freq = __ATTR_RW(hispeed_freq);
 static struct governor_attr pl = __ATTR_RW(pl);
 
 static struct attribute *sugov_attributes[] = {
 	&rate_limit_us.attr,
+	&hispeed_load.attr,
 	&hispeed_freq.attr,
 	&pl.attr,
 	NULL
@@ -657,6 +709,31 @@
 	return tunables;
 }
 
+static void sugov_tunables_save(struct cpufreq_policy *policy,
+		struct sugov_tunables *tunables)
+{
+	int cpu;
+	struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);
+
+	if (!have_governor_per_policy())
+		return;
+
+	if (!cached) {
+		cached = kzalloc(sizeof(*tunables), GFP_KERNEL);
+		if (!cached) {
+			pr_warn("Couldn't allocate tunables for caching\n");
+			return;
+		}
+		for_each_cpu(cpu, policy->related_cpus)
+			per_cpu(cached_tunables, cpu) = cached;
+	}
+
+	cached->pl = tunables->pl;
+	cached->hispeed_load = tunables->hispeed_load;
+	cached->hispeed_freq = tunables->hispeed_freq;
+	cached->rate_limit_us = tunables->rate_limit_us;
+}
+
 static void sugov_tunables_free(struct sugov_tunables *tunables)
 {
 	if (!have_governor_per_policy())
@@ -665,6 +742,23 @@
 	kfree(tunables);
 }
 
+static void sugov_tunables_restore(struct cpufreq_policy *policy)
+{
+	struct sugov_policy *sg_policy = policy->governor_data;
+	struct sugov_tunables *tunables = sg_policy->tunables;
+	struct sugov_tunables *cached = per_cpu(cached_tunables, policy->cpu);
+
+	if (!cached)
+		return;
+
+	tunables->pl = cached->pl;
+	tunables->hispeed_load = cached->hispeed_load;
+	tunables->hispeed_freq = cached->hispeed_freq;
+	tunables->rate_limit_us = cached->rate_limit_us;
+	sg_policy->freq_update_delay_ns =
+		tunables->rate_limit_us * NSEC_PER_USEC;
+}
+
 static int sugov_init(struct cpufreq_policy *policy)
 {
 	struct sugov_policy *sg_policy;
@@ -709,6 +803,7 @@
 	}
 
 	tunables->rate_limit_us = LATENCY_MULTIPLIER;
+	tunables->hispeed_load = DEFAULT_HISPEED_LOAD;
 	tunables->hispeed_freq = 0;
 	lat = policy->cpuinfo.transition_latency / NSEC_PER_USEC;
 	if (lat)
@@ -716,6 +811,9 @@
 
 	policy->governor_data = sg_policy;
 	sg_policy->tunables = tunables;
+	stale_ns = sched_ravg_window + (sched_ravg_window >> 3);
+
+	sugov_tunables_restore(policy);
 
 	ret = kobject_init_and_add(&tunables->attr_set.kobj, &sugov_tunables_ktype,
 				   get_governor_parent_kobj(policy), "%s",
@@ -756,8 +854,10 @@
 
 	count = gov_attr_set_put(&tunables->attr_set, &sg_policy->tunables_hook);
 	policy->governor_data = NULL;
-	if (!count)
+	if (!count) {
+		sugov_tunables_save(policy, tunables);
 		sugov_tunables_free(tunables);
+	}
 
 	mutex_unlock(&global_tunables_lock);
 
diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index ba5e3e2..87bea1e 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -279,3 +279,14 @@
 	for (i = 0; i < CPUPRI_NR_PRIORITIES; i++)
 		free_cpumask_var(cp->pri_to_cpu[i].mask);
 }
+
+/*
+ * cpupri_check_rt - check if CPU has a RT task
+ * should be called from rcu-sched read section.
+ */
+bool cpupri_check_rt(void)
+{
+	int cpu = raw_smp_processor_id();
+
+	return cpu_rq(cpu)->rd->cpupri.cpu_to_pri[cpu] > CPUPRI_NORMAL;
+}
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 0d8cc06..e4f48f1 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -71,12 +71,12 @@
 	else
 		account = false;
 
+	u64_stats_update_end(&irqtime->sync);
+
 	if (account)
 		sched_account_irqtime(cpu, curr, delta, wallclock);
 	else if (curr != this_cpu_ksoftirqd())
 		sched_account_irqstart(cpu, curr, wallclock);
-
-	u64_stats_update_end(&irqtime->sync);
 }
 EXPORT_SYMBOL_GPL(irqtime_account_irq);
 
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index fde0639..42be34a 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -89,6 +89,7 @@
 unsigned int sysctl_sched_sync_hint_enable = 1;
 unsigned int sysctl_sched_initial_task_util = 0;
 unsigned int sysctl_sched_cstate_aware = 1;
+DEFINE_PER_CPU_READ_MOSTLY(int, sched_load_boost);
 
 #ifdef CONFIG_SCHED_WALT
 unsigned int sysctl_sched_use_walt_cpu_util = 1;
@@ -2768,6 +2769,7 @@
 
 	return !!enabled;
 }
+EXPORT_SYMBOL(sched_get_wake_up_idle);
 
 int sched_set_wake_up_idle(struct task_struct *p, int wake_up_idle)
 {
@@ -2780,6 +2782,7 @@
 
 	return 0;
 }
+EXPORT_SYMBOL(sched_set_wake_up_idle);
 
 /* Precomputed fixed inverse multiplies for multiplication by y^n */
 static const u32 runnable_avg_yN_inv[] = {
@@ -3222,6 +3225,36 @@
 	return 1;
 }
 
+/*
+ * Check if we need to update the load and the utilization of a blocked
+ * group_entity:
+ */
+static inline bool skip_blocked_update(struct sched_entity *se)
+{
+	struct cfs_rq *gcfs_rq = group_cfs_rq(se);
+
+	/*
+	 * If sched_entity still have not zero load or utilization, we have to
+	 * decay it:
+	 */
+	if (se->avg.load_avg || se->avg.util_avg)
+		return false;
+
+	/*
+	 * If there is a pending propagation, we have to update the load and
+	 * the utilization of the sched_entity:
+	 */
+	if (gcfs_rq->propagate_avg)
+		return false;
+
+	/*
+	 * Otherwise, the load and the utilization of the sched_entity is
+	 * already zero and there is no pending propagation, so it will be a
+	 * waste of time to try to decay it:
+	 */
+	return true;
+}
+
 #else /* CONFIG_FAIR_GROUP_SCHED */
 
 static inline void update_tg_load_avg(struct cfs_rq *cfs_rq, int force) {}
@@ -5033,6 +5066,19 @@
 DEFINE_PER_CPU(cpumask_var_t, load_balance_mask);
 DEFINE_PER_CPU(cpumask_var_t, select_idle_mask);
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static int rotate_cpu_start;
+static DEFINE_SPINLOCK(rotate_lock);
+static unsigned long avoid_prev_cpu_last;
+
+static struct find_first_cpu_bit_env first_cpu_bit_env = {
+	.avoid_prev_cpu_last = &avoid_prev_cpu_last,
+	.rotate_cpu_start = &rotate_cpu_start,
+	.interval = HZ,
+	.rotate_lock = &rotate_lock,
+};
+#endif
+
 #ifdef CONFIG_NO_HZ_COMMON
 /*
  * per rq 'load' arrray crap; XXX kill this.
@@ -5657,10 +5703,10 @@
 
 	for (idx = 0; idx < sge->nr_cap_states; idx++) {
 		if (sge->cap_states[idx].cap >= util)
-			break;
+			return idx;
 	}
 
-	return idx;
+	return (sge->nr_cap_states - 1);
 }
 
 static int find_new_capacity(struct energy_env *eenv,
@@ -6160,15 +6206,15 @@
 	return __task_fits(p, cpu, cpu_util(cpu));
 }
 
-static bool __cpu_overutilized(int cpu, int delta)
+bool __cpu_overutilized(int cpu, unsigned long util)
 {
-	return (capacity_orig_of(cpu) * 1024) <
-	       ((cpu_util(cpu) + delta) * sysctl_sched_capacity_margin);
+	return (capacity_orig_of(cpu) * 1024 <
+		util * sysctl_sched_capacity_margin);
 }
 
 bool cpu_overutilized(int cpu)
 {
-	return __cpu_overutilized(cpu, 0);
+	return __cpu_overutilized(cpu, cpu_util(cpu));
 }
 
 #ifdef CONFIG_SCHED_TUNE
@@ -6901,8 +6947,12 @@
 	enum sched_boost_policy placement_boost = task_sched_boost(p) ?
 				sched_boost_policy() : SCHED_BOOST_NONE;
 	struct related_thread_group *grp;
+	cpumask_t search_cpus;
+	int prev_cpu = task_cpu(p);
+	bool do_rotate = false;
+	bool avoid_prev_cpu = false;
 
-	sd = rcu_dereference(per_cpu(sd_ea, task_cpu(p)));
+	sd = rcu_dereference(per_cpu(sd_ea, prev_cpu));
 
 	if (!sd)
 		return target;
@@ -6914,14 +6964,15 @@
 
 	curr_util = boosted_task_util(cpu_rq(cpu)->curr);
 
-	need_idle = wake_to_idle(p);
-
+	need_idle = wake_to_idle(p) || schedtune_prefer_idle(p);
+	if (need_idle)
+		sync = 0;
 	grp = task_related_thread_group(p);
 	if (grp && grp->preferred_cluster)
 		rtg_target = &grp->preferred_cluster->cpus;
 
 	if (sync && bias_to_waker_cpu(p, cpu, rtg_target)) {
-		trace_sched_task_util_bias_to_waker(p, task_cpu(p),
+		trace_sched_task_util_bias_to_waker(p, prev_cpu,
 					task_util(p), cpu, cpu, 0, need_idle);
 		return cpu;
 	}
@@ -6984,18 +7035,32 @@
 
 		target_cpu = -1;
 
+		cpumask_copy(&search_cpus, tsk_cpus_allowed(p));
+		cpumask_and(&search_cpus, &search_cpus,
+			    sched_group_cpus(sg_target));
+
+		i = find_first_cpu_bit(p, &search_cpus, sg_target,
+				       &avoid_prev_cpu, &do_rotate,
+				       &first_cpu_bit_env);
+
+retry:
 		/* Find cpu with sufficient capacity */
-		for_each_cpu_and(i, tsk_cpus_allowed(p), sched_group_cpus(sg_target)) {
+		while ((i = cpumask_next(i, &search_cpus)) < nr_cpu_ids) {
+			cpumask_clear_cpu(i, &search_cpus);
+
 			if (cpu_isolated(i))
 				continue;
 
 			if (isolated_candidate == -1)
 				isolated_candidate = i;
 
+			if (avoid_prev_cpu && i == prev_cpu)
+				continue;
+
 			if (is_reserved(i))
 				continue;
 
-			if (sched_cpu_high_irqload(cpu))
+			if (sched_cpu_high_irqload(i))
 				continue;
 
 			/*
@@ -7043,7 +7108,7 @@
 			cpu_idle_idx = idle_get_state_idx(cpu_rq(i));
 
 			if (!need_idle &&
-			    add_capacity_margin(new_util_cum) <
+			    add_capacity_margin(new_util_cum, i) <
 			    capacity_curr_of(i)) {
 				if (sysctl_sched_cstate_aware) {
 					if (cpu_idle_idx < min_idle_idx) {
@@ -7060,9 +7125,9 @@
 						    new_util ||
 						    (target_cpu_util ==
 						     new_util &&
-						     (i == task_cpu(p) ||
+						     (i == prev_cpu ||
 						      (target_cpu !=
-						       task_cpu(p) &&
+						       prev_cpu &&
 						       target_cpu_new_util_cum >
 						       new_util_cum))))) {
 						min_idle_idx_cpu = i;
@@ -7074,6 +7139,7 @@
 					}
 				} else if (cpu_rq(i)->nr_running) {
 					target_cpu = i;
+					do_rotate = false;
 					break;
 				}
 			} else if (!need_idle) {
@@ -7113,6 +7179,17 @@
 			}
 		}
 
+		if (do_rotate) {
+			/*
+			 * We started iteration somewhere in the middle of
+			 * cpumask.  Iterate once again from bit 0 to the
+			 * previous starting point bit.
+			 */
+			do_rotate = false;
+			i = -1;
+			goto retry;
+		}
+
 		if (target_cpu == -1 ||
 		    (target_cpu != min_util_cpu && !safe_to_pack &&
 		     !is_packing_eligible(p, task_util_boosted, sg_target,
@@ -7147,7 +7224,8 @@
 		}
 	}
 
-	if (target_cpu != task_cpu(p) && !cpu_isolated(task_cpu(p))) {
+	if (target_cpu != task_cpu(p) && !avoid_prev_cpu &&
+	    !cpu_isolated(task_cpu(p))) {
 		struct energy_env eenv = {
 			.util_delta	= task_util(p),
 			.src_cpu	= task_cpu(p),
@@ -7187,7 +7265,9 @@
 		task_util_boosted = 0;
 #endif
 		/* Not enough spare capacity on previous cpu */
-		if (__cpu_overutilized(task_cpu(p), task_util_boosted)) {
+		if (__cpu_overutilized(task_cpu(p),
+				       cpu_util(task_cpu(p)) +
+						task_util_boosted)) {
 			trace_sched_task_util_overutilzed(p, task_cpu(p),
 						task_util(p), target_cpu,
 						target_cpu, 0, need_idle);
@@ -8333,6 +8413,8 @@
 	 * list_add_leaf_cfs_rq() for details.
 	 */
 	for_each_leaf_cfs_rq(rq, cfs_rq) {
+		struct sched_entity *se;
+
 		/* throttled entities do not contribute to load */
 		if (throttled_hierarchy(cfs_rq))
 			continue;
@@ -8340,9 +8422,10 @@
 		if (update_cfs_rq_load_avg(cfs_rq_clock_task(cfs_rq), cfs_rq, true))
 			update_tg_load_avg(cfs_rq, 0);
 
-		/* Propagate pending load changes to the parent */
-		if (cfs_rq->tg->se[cpu])
-			update_load_avg(cfs_rq->tg->se[cpu], 0);
+		/* Propagate pending load changes to the parent, if any: */
+		se = cfs_rq->tg->se[cpu];
+		if (se && !skip_blocked_update(se))
+			update_load_avg(se, 0);
 	}
 	raw_spin_unlock_irqrestore(&rq->lock, flags);
 }
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 6b935e7..c97b779 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -914,15 +914,30 @@
 	char *pos = buf;
 	char *end = buf + sizeof(buf);
 	int idx;
+	struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
 
 	pos += snprintf(pos, sizeof(buf),
-		"sched: RT throttling activated for rt_rq %p (cpu %d)\n",
+		"sched: RT throttling activated for rt_rq %pK (cpu %d)\n",
 		rt_rq, cpu_of(rq_of_rt_rq(rt_rq)));
 
+	pos += snprintf(pos, end - pos,
+			"rt_period_timer: expires=%lld now=%llu period=%llu\n",
+			hrtimer_get_expires_ns(&rt_b->rt_period_timer),
+			ktime_get_ns(), sched_rt_period(rt_rq));
+
 	if (bitmap_empty(array->bitmap, MAX_RT_PRIO))
 		goto out;
 
 	pos += snprintf(pos, end - pos, "potential CPU hogs:\n");
+#ifdef CONFIG_SCHED_INFO
+	if (sched_info_on())
+		pos += snprintf(pos, end - pos,
+				"current %s (%d) is running for %llu nsec\n",
+				current->comm, current->pid,
+				rq_clock(rq_of_rt_rq(rt_rq)) -
+				current->sched_info.last_arrival);
+#endif
+
 	idx = sched_find_first_bit(array->bitmap);
 	while (idx < MAX_RT_PRIO) {
 		list_for_each_entry(rt_se, array->queue + idx, run_list) {
@@ -1711,6 +1726,19 @@
 	return NULL;
 }
 
+#ifdef CONFIG_SCHED_CORE_ROTATE
+static int rotate_cpu_start;
+static DEFINE_SPINLOCK(rotate_lock);
+static unsigned long avoid_prev_cpu_last;
+
+static struct find_first_cpu_bit_env first_cpu_bit_env = {
+	.avoid_prev_cpu_last = &avoid_prev_cpu_last,
+	.rotate_cpu_start = &rotate_cpu_start,
+	.interval = HZ,
+	.rotate_lock = &rotate_lock,
+};
+#endif
+
 static DEFINE_PER_CPU(cpumask_var_t, local_cpu_mask);
 
 static int find_lowest_rq(struct task_struct *task)
@@ -1719,17 +1747,19 @@
 	struct sched_group *sg, *sg_target;
 	struct cpumask *lowest_mask = this_cpu_cpumask_var_ptr(local_cpu_mask);
 	int this_cpu = smp_processor_id();
-	int cpu, best_cpu;
+	int cpu = -1, best_cpu;
 	struct cpumask search_cpu, backup_search_cpu;
 	unsigned long cpu_capacity;
 	unsigned long best_capacity;
 	unsigned long util, best_cpu_util = ULONG_MAX;
+	unsigned long best_cpu_util_cum = ULONG_MAX;
+	unsigned long util_cum;
+	unsigned long tutil = task_util(task);
 	int best_cpu_idle_idx = INT_MAX;
 	int cpu_idle_idx = -1;
-	long new_util_cum;
-	int max_spare_cap_cpu = -1;
-	long max_spare_cap = -LONG_MAX;
 	bool placement_boost;
+	bool do_rotate = false;
+	bool avoid_prev_cpu = false;
 
 	/* Make sure the mask is initialized first */
 	if (unlikely(!lowest_mask))
@@ -1761,6 +1791,10 @@
 
 		sg = sd->groups;
 		do {
+			if (!cpumask_intersects(lowest_mask,
+						sched_group_cpus(sg)))
+				continue;
+
 			cpu = group_first_cpu(sg);
 			cpu_capacity = capacity_orig_of(cpu);
 
@@ -1778,71 +1812,98 @@
 		} while (sg = sg->next, sg != sd->groups);
 		rcu_read_unlock();
 
-		cpumask_and(&search_cpu, lowest_mask,
-			    sched_group_cpus(sg_target));
-		cpumask_copy(&backup_search_cpu, lowest_mask);
-		cpumask_andnot(&backup_search_cpu, &backup_search_cpu,
-			       &search_cpu);
+		if (sg_target) {
+			cpumask_and(&search_cpu, lowest_mask,
+				    sched_group_cpus(sg_target));
+			cpumask_copy(&backup_search_cpu, lowest_mask);
+			cpumask_andnot(&backup_search_cpu, &backup_search_cpu,
+				       &search_cpu);
+
+			cpu = find_first_cpu_bit(task, &search_cpu, sg_target,
+						 &avoid_prev_cpu, &do_rotate,
+						 &first_cpu_bit_env);
+		} else {
+			cpumask_copy(&search_cpu, lowest_mask);
+			cpumask_clear(&backup_search_cpu);
+			cpu = -1;
+		}
 
 retry:
-		for_each_cpu(cpu, &search_cpu) {
+		while ((cpu = cpumask_next(cpu, &search_cpu)) < nr_cpu_ids) {
+			cpumask_clear_cpu(cpu, &search_cpu);
+
 			/*
 			 * Don't use capcity_curr_of() since it will
 			 * double count rt task load.
 			 */
 			util = cpu_util(cpu);
-			if (!cpu_overutilized(cpu)) {
-				if (cpu_isolated(cpu))
+
+			if (avoid_prev_cpu && cpu == task_cpu(task))
+				continue;
+
+			if (__cpu_overutilized(cpu, util + tutil))
+				continue;
+
+			if (cpu_isolated(cpu))
+				continue;
+
+			if (sched_cpu_high_irqload(cpu))
+				continue;
+
+			/* Find the least loaded CPU */
+			if (util > best_cpu_util)
+				continue;
+
+			/*
+			 * If the previous CPU has same load, keep it as
+			 * best_cpu.
+			 */
+			if (best_cpu_util == util && best_cpu == task_cpu(task))
+				continue;
+
+			/*
+			 * If candidate CPU is the previous CPU, select it.
+			 * Otherwise, if its load is same with best_cpu and in
+			 * a shallower C-state, select it.  If all above
+			 * conditions are same, select the least cumulative
+			 * window demand CPU.
+			 */
+			if (sysctl_sched_cstate_aware)
+				cpu_idle_idx = idle_get_state_idx(cpu_rq(cpu));
+
+			util_cum = cpu_util_cum(cpu, 0);
+			if (cpu != task_cpu(task) && best_cpu_util == util) {
+				if (best_cpu_idle_idx < cpu_idle_idx)
 					continue;
 
-				if (sched_cpu_high_irqload(cpu))
+				if (best_cpu_idle_idx == cpu_idle_idx &&
+				    best_cpu_util_cum < util_cum)
 					continue;
-
-				new_util_cum = cpu_util_cum(cpu, 0);
-
-				if (!task_in_cum_window_demand(cpu_rq(cpu),
-							       task))
-					new_util_cum += task_util(task);
-
-				trace_sched_cpu_util(task, cpu, task_util(task),
-						     0, new_util_cum, 0);
-
-				if (sysctl_sched_cstate_aware)
-					cpu_idle_idx =
-					     idle_get_state_idx(cpu_rq(cpu));
-
-				if (add_capacity_margin(new_util_cum) <
-				    capacity_curr_of(cpu)) {
-					if (cpu_idle_idx < best_cpu_idle_idx ||
-					    (best_cpu != task_cpu(task) &&
-					     (best_cpu_idle_idx ==
-					      cpu_idle_idx &&
-					      best_cpu_util > util))) {
-						best_cpu_util = util;
-						best_cpu = cpu;
-						best_cpu_idle_idx =
-						    cpu_idle_idx;
-					}
-				} else {
-					long spare_cap = capacity_of(cpu) -
-							 util;
-
-					if (spare_cap > 0 &&
-					    max_spare_cap < spare_cap) {
-						max_spare_cap_cpu = cpu;
-						max_spare_cap = spare_cap;
-					}
-				}
 			}
+
+			best_cpu_idle_idx = cpu_idle_idx;
+			best_cpu_util_cum = util_cum;
+			best_cpu_util = util;
+			best_cpu = cpu;
+		}
+
+		if (do_rotate) {
+			/*
+			 * We started iteration somewhere in the middle of
+			 * cpumask.  Iterate once again from bit 0 to the
+			 * previous starting point bit.
+			 */
+			do_rotate = false;
+			cpu = -1;
+			goto retry;
 		}
 
 		if (best_cpu != -1) {
 			return best_cpu;
-		} else if (max_spare_cap_cpu != -1) {
-			return max_spare_cap_cpu;
 		} else if (!cpumask_empty(&backup_search_cpu)) {
 			cpumask_copy(&search_cpu, &backup_search_cpu);
 			cpumask_clear(&backup_search_cpu);
+			cpu = -1;
 			goto retry;
 		}
 	}
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 72b79bb..494ab14 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -29,6 +29,7 @@
 
 #ifdef CONFIG_SCHED_WALT
 extern unsigned int sched_ravg_window;
+extern unsigned int walt_cpu_util_freq_divisor;
 
 struct walt_sched_stats {
 	int nr_big_tasks;
@@ -1456,6 +1457,7 @@
 
 extern void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask);
 
+bool __cpu_overutilized(int cpu, unsigned long util);
 bool cpu_overutilized(int cpu);
 
 #endif
@@ -1805,47 +1807,87 @@
 #endif
 
 static inline unsigned long
-cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+cpu_util_freq_pelt(int cpu)
 {
 	struct rq *rq = cpu_rq(cpu);
 	u64 util = rq->cfs.avg.util_avg;
 	unsigned long capacity = capacity_orig_of(cpu);
 
-#ifdef CONFIG_SCHED_WALT
-	if (!walt_disabled && sysctl_sched_use_walt_cpu_util) {
+	util *= (100 + per_cpu(sched_load_boost, cpu));
+	do_div(util, 100);
 
-		util = freq_policy_load(rq);
-		util = div64_u64(util,
-				 sched_ravg_window >> SCHED_CAPACITY_SHIFT);
-
-		if (walt_load) {
-			u64 nl = cpu_rq(cpu)->nt_prev_runnable_sum +
-				rq->grp_time.nt_prev_runnable_sum;
-			u64 pl = rq->walt_stats.pred_demands_sum;
-
-			nl = div64_u64(nl, sched_ravg_window >>
-						SCHED_CAPACITY_SHIFT);
-			pl = div64_u64(pl, sched_ravg_window >>
-						SCHED_CAPACITY_SHIFT);
-
-			walt_load->prev_window_util = util;
-			walt_load->nl = nl;
-			walt_load->pl = pl;
-			rq->old_busy_time = util;
-			rq->old_estimated_time = pl;
-			walt_load->ws = rq->window_start;
-		}
-	}
-#endif
 	return (util >= capacity) ? capacity : util;
 }
+
+#ifdef CONFIG_SCHED_WALT
+static inline unsigned long
+cpu_util_freq_walt(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	u64 util, util_unboosted;
+	struct rq *rq = cpu_rq(cpu);
+	unsigned long capacity = capacity_orig_of(cpu);
+	int boost;
+
+	if (walt_disabled || !sysctl_sched_use_walt_cpu_util)
+		return cpu_util_freq_pelt(cpu);
+
+	boost = per_cpu(sched_load_boost, cpu);
+	util_unboosted = util = freq_policy_load(rq);
+	util = div64_u64(util * (100 + boost),
+			 walt_cpu_util_freq_divisor);
+
+	if (walt_load) {
+		u64 nl = cpu_rq(cpu)->nt_prev_runnable_sum +
+			rq->grp_time.nt_prev_runnable_sum;
+		u64 pl = rq->walt_stats.pred_demands_sum;
+
+		/* do_pl_notif() needs unboosted signals */
+		rq->old_busy_time = div64_u64(util_unboosted,
+					      sched_ravg_window >>
+					      SCHED_CAPACITY_SHIFT);
+		rq->old_estimated_time = div64_u64(pl, sched_ravg_window >>
+						       SCHED_CAPACITY_SHIFT);
+
+		nl = div64_u64(nl * (100 + boost),
+			       walt_cpu_util_freq_divisor);
+		pl = div64_u64(pl * (100 + boost),
+			       walt_cpu_util_freq_divisor);
+
+		walt_load->prev_window_util = util;
+		walt_load->nl = nl;
+		walt_load->pl = pl;
+		walt_load->ws = rq->window_start;
+	}
+
+	return (util >= capacity) ? capacity : util;
+}
+
+static inline unsigned long
+cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	return cpu_util_freq_walt(cpu, walt_load);
+}
+
+#else
+
+static inline unsigned long
+cpu_util_freq(int cpu, struct sched_walt_cpu_load *walt_load)
+{
+	return cpu_util_freq_pelt(cpu);
+}
+
+#endif /* CONFIG_SCHED_WALT */
+
 #endif
 
 extern unsigned int capacity_margin_freq;
 
-static inline unsigned long add_capacity_margin(unsigned long cpu_capacity)
+static inline unsigned long
+add_capacity_margin(unsigned long cpu_capacity, int cpu)
 {
-	cpu_capacity  = cpu_capacity * capacity_margin_freq;
+	cpu_capacity  = cpu_capacity * capacity_margin_freq *
+			(100 + per_cpu(sched_load_boost, cpu));
+	cpu_capacity /= 100;
 	cpu_capacity /= SCHED_CAPACITY_SCALE;
 	return cpu_capacity;
 }
@@ -2250,7 +2292,7 @@
 
 #ifdef CONFIG_SCHED_WALT
 	unsigned int exception_flags = SCHED_CPUFREQ_INTERCLUSTER_MIG |
-						SCHED_CPUFREQ_PL;
+				SCHED_CPUFREQ_PL | SCHED_CPUFREQ_EARLY_DET;
 
 	/*
 	 * Skip if we've already reported, but not if this is an inter-cluster
@@ -2307,7 +2349,6 @@
 };
 
 extern struct list_head cluster_head;
-extern int num_clusters;
 extern struct sched_cluster *sched_cluster[NR_CPUS];
 
 #define for_each_sched_cluster(cluster) \
@@ -2799,3 +2840,19 @@
 {
 	return sched_feat(ENERGY_AWARE);
 }
+
+#ifdef CONFIG_SCHED_CORE_ROTATE
+struct find_first_cpu_bit_env {
+	unsigned long *avoid_prev_cpu_last;
+	int *rotate_cpu_start;
+	int interval;
+	spinlock_t *rotate_lock;
+};
+
+int
+find_first_cpu_bit(struct task_struct *p, const cpumask_t *search_cpus,
+		   struct sched_group *sg_target, bool *avoid_prev_cpu,
+		   bool *do_rotate, struct find_first_cpu_bit_env *env);
+#else
+#define find_first_cpu_bit(...) -1
+#endif
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index 166c643..10d7f1b 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -123,7 +123,7 @@
 EXPORT_SYMBOL(sched_get_nr_running_avg);
 
 #define BUSY_NR_RUN		3
-#define BUSY_LOAD_FACTOR	2
+#define BUSY_LOAD_FACTOR	10
 static inline void update_last_busy_time(int cpu, bool dequeue,
 				unsigned long prev_nr_run, u64 curr_time)
 {
@@ -206,7 +206,7 @@
 	raw_spin_unlock_irqrestore(&rq->lock, flags);
 
 	util = (util >= capacity) ? capacity : util;
-	busy = (util * 100) / capacity;
+	busy = div64_ul((util * 100), capacity);
 	return busy;
 }
 
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
index 217bafe..93643ba 100644
--- a/kernel/sched/tune.c
+++ b/kernel/sched/tune.c
@@ -838,7 +838,6 @@
 		bg = &per_cpu(cpu_boost_groups, cpu);
 		bg->group[st->idx].boost = 0;
 		bg->group[st->idx].tasks = 0;
-		raw_spin_lock_init(&bg->lock);
 	}
 
 	return 0;
diff --git a/kernel/sched/tune.h b/kernel/sched/tune.h
index 4f64417..d1b4c72 100644
--- a/kernel/sched/tune.h
+++ b/kernel/sched/tune.h
@@ -28,6 +28,7 @@
 
 #define schedtune_cpu_boost(cpu)  get_sysctl_sched_cfs_boost()
 #define schedtune_task_boost(tsk) get_sysctl_sched_cfs_boost()
+#define schedtune_prefer_idle(tsk) 0
 
 #define schedtune_exit_task(task) do { } while (0)
 
@@ -44,6 +45,7 @@
 
 #define schedtune_cpu_boost(cpu)  0
 #define schedtune_task_boost(tsk) 0
+#define schedtune_prefer_idle(tsk) 0
 
 #define schedtune_exit_task(task) do { } while (0)
 
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 60249e4..da7c0f0 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -109,6 +109,12 @@
 /* Window size (in ns) */
 __read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
 
+/*
+ * A after-boot constant divisor for cpu_util_freq_walt() to apply the load
+ * boost.
+ */
+__read_mostly unsigned int walt_cpu_util_freq_divisor;
+
 /* Initial task load. Newly created tasks are assigned this load. */
 unsigned int __read_mostly sched_init_task_load_windows;
 unsigned int __read_mostly sysctl_sched_init_task_load_pct = 15;
@@ -1861,7 +1867,7 @@
 
 	p->cpu_cycles = cur_cycles;
 
-	trace_sched_get_task_cpu_cycles(cpu, event, rq->cc.cycles, rq->cc.time);
+	trace_sched_get_task_cpu_cycles(cpu, event, rq->cc.cycles, rq->cc.time, p);
 }
 
 static inline void run_walt_irq_work(u64 old_window_start, struct rq *rq)
@@ -2827,14 +2833,11 @@
 		rcu_read_unlock();
 	}
 
-	if (cpu_max_table_freq[cpu] &&
-	    unlikely(thermal_max_freq && thermal_max_freq
-		!= cpu_max_table_freq[cpu])) {
+	if (cpu_max_table_freq[cpu])
 		return div64_ul(thermal_max_freq * max_cap[cpu],
 				cpu_max_table_freq[cpu]);
-	} else {
+	else
 		return rq->cpu_capacity_orig;
-	}
 }
 
 static DEFINE_SPINLOCK(cpu_freq_min_max_lock);
@@ -3107,4 +3110,11 @@
 		clear_top_tasks_bitmap(rq->top_tasks_bitmap[j]);
 	}
 	rq->cum_window_demand = 0;
+
+	walt_cpu_util_freq_divisor =
+	    (sched_ravg_window >> SCHED_CAPACITY_SHIFT) * 100;
+
+	sched_init_task_load_windows =
+		div64_u64((u64)sysctl_sched_init_task_load_pct *
+			  (u64)sched_ravg_window, 100);
 }
diff --git a/kernel/sched/walt.h b/kernel/sched/walt.h
index 86d5bfd..10f3e84 100644
--- a/kernel/sched/walt.h
+++ b/kernel/sched/walt.h
@@ -219,7 +219,7 @@
 	return sched_ravg_window;
 }
 
-static inline u32 cpu_cycles_to_freq(u64 cycles, u32 period)
+static inline u32 cpu_cycles_to_freq(u64 cycles, u64 period)
 {
 	return div64_u64(cycles, period);
 }
diff --git a/kernel/seccomp.c b/kernel/seccomp.c
index 0db7c8a..af182a6 100644
--- a/kernel/seccomp.c
+++ b/kernel/seccomp.c
@@ -457,14 +457,19 @@
 	return 0;
 }
 
+void __get_seccomp_filter(struct seccomp_filter *filter)
+{
+	/* Reference count is bounded by the number of total processes. */
+	atomic_inc(&filter->usage);
+}
+
 /* get_seccomp_filter - increments the reference count of the filter on @tsk */
 void get_seccomp_filter(struct task_struct *tsk)
 {
 	struct seccomp_filter *orig = tsk->seccomp.filter;
 	if (!orig)
 		return;
-	/* Reference count is bounded by the number of total processes. */
-	atomic_inc(&orig->usage);
+	__get_seccomp_filter(orig);
 }
 
 static inline void seccomp_filter_free(struct seccomp_filter *filter)
@@ -475,10 +480,8 @@
 	}
 }
 
-/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
-void put_seccomp_filter(struct task_struct *tsk)
+static void __put_seccomp_filter(struct seccomp_filter *orig)
 {
-	struct seccomp_filter *orig = tsk->seccomp.filter;
 	/* Clean up single-reference branches iteratively. */
 	while (orig && atomic_dec_and_test(&orig->usage)) {
 		struct seccomp_filter *freeme = orig;
@@ -487,6 +490,12 @@
 	}
 }
 
+/* put_seccomp_filter - decrements the ref count of tsk->seccomp.filter */
+void put_seccomp_filter(struct task_struct *tsk)
+{
+	__put_seccomp_filter(tsk->seccomp.filter);
+}
+
 /**
  * seccomp_send_sigsys - signals the task to allow in-process syscall emulation
  * @syscall: syscall number to send to userland
@@ -892,13 +901,13 @@
 	if (!data)
 		goto out;
 
-	get_seccomp_filter(task);
+	__get_seccomp_filter(filter);
 	spin_unlock_irq(&task->sighand->siglock);
 
 	if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
 		ret = -EFAULT;
 
-	put_seccomp_filter(task);
+	__put_seccomp_filter(filter);
 	return ret;
 
 out:
diff --git a/kernel/signal.c b/kernel/signal.c
index deb04d5..e48668c 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -346,7 +346,7 @@
 	 * fresh group stop.  Read comment in do_signal_stop() for details.
 	 */
 	if (!sig->group_stop_count && !(sig->flags & SIGNAL_STOP_STOPPED)) {
-		sig->flags = SIGNAL_STOP_STOPPED;
+		signal_set_stop_flags(sig, SIGNAL_STOP_STOPPED);
 		return true;
 	}
 	return false;
@@ -845,7 +845,7 @@
 			 * will take ->siglock, notice SIGNAL_CLD_MASK, and
 			 * notify its parent. See get_signal_to_deliver().
 			 */
-			signal->flags = why | SIGNAL_STOP_CONTINUED;
+			signal_set_stop_flags(signal, why | SIGNAL_STOP_CONTINUED);
 			signal->group_stop_count = 0;
 			signal->group_exit_code = 0;
 		}
diff --git a/kernel/smp.c b/kernel/smp.c
index ee80cc8..313d9a8 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -15,6 +15,7 @@
 #include <linux/cpu.h>
 #include <linux/sched.h>
 #include <linux/hypervisor.h>
+#include <linux/suspend.h>
 
 #include "smpboot.h"
 
@@ -751,7 +752,8 @@
 	for_each_online_cpu(cpu) {
 		if (cpu == smp_processor_id())
 			continue;
-		if (!cpu_isolated(cpu))
+		if (suspend_freeze_state == FREEZE_STATE_ENTER ||
+		    !cpu_isolated(cpu))
 			wake_up_if_idle(cpu);
 	}
 	preempt_enable();
diff --git a/kernel/softirq.c b/kernel/softirq.c
index bde8e33..6833ffa 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -245,6 +245,8 @@
 static inline void lockdep_softirq_end(bool in_hardirq) { }
 #endif
 
+#define long_softirq_pending()	(local_softirq_pending() & LONG_SOFTIRQ_MASK)
+#define defer_for_rt()		(long_softirq_pending() && cpupri_check_rt())
 asmlinkage __visible void __softirq_entry __do_softirq(void)
 {
 	unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
@@ -308,6 +310,7 @@
 	pending = local_softirq_pending();
 	if (pending) {
 		if (time_before(jiffies, end) && !need_resched() &&
+		    !defer_for_rt() &&
 		    --max_restart)
 			goto restart;
 
@@ -363,7 +366,7 @@
 	if (ksoftirqd_running())
 		return;
 
-	if (!force_irqthreads) {
+	if (!force_irqthreads && !defer_for_rt()) {
 #ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
 		/*
 		 * We can safely execute softirq on the current stack if
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 2c4cd17..29bb99c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1186,6 +1186,16 @@
 		.proc_handler	= proc_dointvec_minmax,
 		.extra1		= &neg_one,
 	},
+	{
+		.procname	= "hung_task_selective_monitoring",
+		.data		= &sysctl_hung_task_selective_monitoring,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+
 #endif
 #ifdef CONFIG_RT_MUTEXES
 	{
@@ -1292,6 +1302,8 @@
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= timer_migration_handler,
+		.extra1		= &zero,
+		.extra2		= &one,
 	},
 #endif
 #ifdef CONFIG_BPF_SYSCALL
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index b9b881eb..7251e3c 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -11,5 +11,3 @@
 obj-$(CONFIG_TICK_ONESHOT)			+= tick-oneshot.o tick-sched.o
 obj-$(CONFIG_DEBUG_FS)				+= timekeeping_debug.o
 obj-$(CONFIG_TEST_UDELAY)			+= test_udelay.o
-
-ccflags-y += -Idrivers/cpuidle
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 842928a..b2df539 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -26,11 +26,6 @@
 #include <linux/workqueue.h>
 #include <linux/freezer.h>
 
-#ifdef CONFIG_MSM_PM
-#include "lpm-levels.h"
-#endif
-#include <linux/workqueue.h>
-
 /**
  * struct alarm_base - Alarm timer bases
  * @lock:		Lock for syncrhonized access to the base
@@ -50,116 +45,12 @@
 static DEFINE_SPINLOCK(freezer_delta_lock);
 
 static struct wakeup_source *ws;
-static struct delayed_work work;
-static struct workqueue_struct *power_off_alarm_workqueue;
 
 #ifdef CONFIG_RTC_CLASS
 /* rtc timer and device for setting alarm wakeups at suspend */
 static struct rtc_timer		rtctimer;
 static struct rtc_device	*rtcdev;
 static DEFINE_SPINLOCK(rtcdev_lock);
-static struct mutex power_on_alarm_lock;
-static struct alarm init_alarm;
-
-/**
- * power_on_alarm_init - Init power on alarm value
- *
- * Read rtc alarm value after device booting up and add this alarm
- * into alarm queue.
- */
-void power_on_alarm_init(void)
-{
-	struct rtc_wkalrm rtc_alarm;
-	struct rtc_time rt;
-	unsigned long alarm_time;
-	struct rtc_device *rtc;
-	ktime_t alarm_ktime;
-
-	rtc = alarmtimer_get_rtcdev();
-
-	if (!rtc)
-		return;
-
-	rtc_read_alarm(rtc, &rtc_alarm);
-	rt = rtc_alarm.time;
-
-	rtc_tm_to_time(&rt, &alarm_time);
-
-	if (alarm_time) {
-		alarm_ktime = ktime_set(alarm_time, 0);
-		alarm_init(&init_alarm, ALARM_POWEROFF_REALTIME, NULL);
-		alarm_start(&init_alarm, alarm_ktime);
-	}
-}
-
-/**
- * set_power_on_alarm - set power on alarm value into rtc register
- *
- * Get the soonest power off alarm timer and set the alarm value into rtc
- * register.
- */
-void set_power_on_alarm(void)
-{
-	int rc;
-	struct timespec wall_time, alarm_ts;
-	long alarm_secs = 0l;
-	long rtc_secs, alarm_time, alarm_delta;
-	struct rtc_time rtc_time;
-	struct rtc_wkalrm alarm;
-	struct rtc_device *rtc;
-	struct timerqueue_node *next;
-	unsigned long flags;
-	struct alarm_base *base = &alarm_bases[ALARM_POWEROFF_REALTIME];
-
-	rc = mutex_lock_interruptible(&power_on_alarm_lock);
-	if (rc != 0)
-		return;
-
-	spin_lock_irqsave(&base->lock, flags);
-	next = timerqueue_getnext(&base->timerqueue);
-	spin_unlock_irqrestore(&base->lock, flags);
-
-	if (next) {
-		alarm_ts = ktime_to_timespec(next->expires);
-		alarm_secs = alarm_ts.tv_sec;
-	}
-
-	if (!alarm_secs)
-		goto disable_alarm;
-
-	getnstimeofday(&wall_time);
-
-	/*
-	 * alarm_secs have to be bigger than "wall_time +1".
-	 * It is to make sure that alarm time will be always
-	 * bigger than wall time.
-	 */
-	if (alarm_secs <= wall_time.tv_sec + 1)
-		goto disable_alarm;
-
-	rtc = alarmtimer_get_rtcdev();
-	if (!rtc)
-		goto exit;
-
-	rtc_read_time(rtc, &rtc_time);
-	rtc_tm_to_time(&rtc_time, &rtc_secs);
-	alarm_delta = wall_time.tv_sec - rtc_secs;
-	alarm_time = alarm_secs - alarm_delta;
-
-	rtc_time_to_tm(alarm_time, &alarm.time);
-	alarm.enabled = 1;
-	rc = rtc_set_alarm(rtcdev, &alarm);
-	if (rc)
-		goto disable_alarm;
-
-	mutex_unlock(&power_on_alarm_lock);
-	return;
-
-disable_alarm:
-	rtc_alarm_irq_enable(rtcdev, 0);
-exit:
-	mutex_unlock(&power_on_alarm_lock);
-}
 
 static void alarmtimer_triggered_func(void *p)
 {
@@ -231,8 +122,6 @@
 
 static inline void alarmtimer_rtc_timer_init(void)
 {
-	mutex_init(&power_on_alarm_lock);
-
 	rtc_timer_init(&rtctimer, NULL, NULL);
 }
 
@@ -259,14 +148,8 @@
 static inline int alarmtimer_rtc_interface_setup(void) { return 0; }
 static inline void alarmtimer_rtc_interface_remove(void) { }
 static inline void alarmtimer_rtc_timer_init(void) { }
-void set_power_on_alarm(void) { }
 #endif
 
-static void alarm_work_func(struct work_struct *unused)
-{
-	set_power_on_alarm();
-}
-
 /**
  * alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue
  * @base: pointer to the base where the timer is being run
@@ -336,10 +219,6 @@
 	}
 	spin_unlock_irqrestore(&base->lock, flags);
 
-	/* set next power off alarm */
-	if (alarm->type == ALARM_POWEROFF_REALTIME)
-		queue_delayed_work(power_off_alarm_workqueue, &work, 0);
-
 	return ret;
 
 }
@@ -362,70 +241,6 @@
  * set an rtc timer to fire that far into the future, which
  * will wake us from suspend.
  */
-#if defined(CONFIG_RTC_DRV_QPNP) && defined(CONFIG_MSM_PM)
-static int alarmtimer_suspend(struct device *dev)
-{
-	struct rtc_time tm;
-	ktime_t min, now;
-	unsigned long flags;
-	struct rtc_device *rtc;
-	int i;
-	int ret = 0;
-
-	spin_lock_irqsave(&freezer_delta_lock, flags);
-	min = freezer_delta;
-	freezer_delta = ktime_set(0, 0);
-	spin_unlock_irqrestore(&freezer_delta_lock, flags);
-
-	rtc = alarmtimer_get_rtcdev();
-	/* If we have no rtcdev, just return */
-	if (!rtc)
-		return 0;
-
-	/* Find the soonest timer to expire*/
-	for (i = 0; i < ALARM_NUMTYPE; i++) {
-		struct alarm_base *base = &alarm_bases[i];
-		struct timerqueue_node *next;
-		ktime_t delta;
-
-		spin_lock_irqsave(&base->lock, flags);
-		next = timerqueue_getnext(&base->timerqueue);
-		spin_unlock_irqrestore(&base->lock, flags);
-		if (!next)
-			continue;
-		delta = ktime_sub(next->expires, base->gettime());
-		if (!min.tv64 || (delta.tv64 < min.tv64))
-			min = delta;
-	}
-	if (min.tv64 == 0)
-		return 0;
-
-	if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
-		__pm_wakeup_event(ws, 2 * MSEC_PER_SEC);
-		return -EBUSY;
-	}
-
-	/* Setup a timer to fire that far in the future */
-	rtc_timer_cancel(rtc, &rtctimer);
-	rtc_read_time(rtc, &tm);
-	now = rtc_tm_to_ktime(tm);
-	now = ktime_add(now, min);
-	if (poweron_alarm) {
-		struct rtc_time tm_val;
-		unsigned long secs;
-
-		tm_val = rtc_ktime_to_tm(min);
-		rtc_tm_to_time(&tm_val, &secs);
-		lpm_suspend_wake_time(secs);
-	} else {
-		/* Set alarm, if in the past reject suspend briefly to handle */
-		ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
-		if (ret < 0)
-			__pm_wakeup_event(ws, MSEC_PER_SEC);
-	}
-	return ret;
-}
-#else
 static int alarmtimer_suspend(struct device *dev)
 {
 	struct rtc_time tm;
@@ -435,8 +250,6 @@
 	int i;
 	int ret;
 
-	cancel_delayed_work_sync(&work);
-
 	spin_lock_irqsave(&freezer_delta_lock, flags);
 	min = freezer_delta;
 	freezer_delta = ktime_set(0, 0);
@@ -482,7 +295,7 @@
 		__pm_wakeup_event(ws, MSEC_PER_SEC);
 	return ret;
 }
-#endif
+
 static int alarmtimer_resume(struct device *dev)
 {
 	struct rtc_device *rtc;
@@ -490,8 +303,6 @@
 	rtc = alarmtimer_get_rtcdev();
 	if (rtc)
 		rtc_timer_cancel(rtc, &rtctimer);
-
-	queue_delayed_work(power_off_alarm_workqueue, &work, 0);
 	return 0;
 }
 
@@ -672,14 +483,12 @@
  * clock2alarm - helper that converts from clockid to alarmtypes
  * @clockid: clockid.
  */
-enum alarmtimer_type clock2alarm(clockid_t clockid)
+static enum alarmtimer_type clock2alarm(clockid_t clockid)
 {
 	if (clockid == CLOCK_REALTIME_ALARM)
 		return ALARM_REALTIME;
 	if (clockid == CLOCK_BOOTTIME_ALARM)
 		return ALARM_BOOTTIME;
-	if (clockid == CLOCK_POWEROFF_ALARM)
-		return ALARM_POWEROFF_REALTIME;
 	return -1;
 }
 
@@ -1073,13 +882,10 @@
 
 	posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock);
 	posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock);
-	posix_timers_register_clock(CLOCK_POWEROFF_ALARM, &alarm_clock);
 
 	/* Initialize alarm bases */
 	alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
 	alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real;
-	alarm_bases[ALARM_POWEROFF_REALTIME].base_clockid = CLOCK_REALTIME;
-	alarm_bases[ALARM_POWEROFF_REALTIME].gettime = &ktime_get_real;
 	alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME;
 	alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime;
 	for (i = 0; i < ALARM_NUMTYPE; i++) {
@@ -1101,24 +907,8 @@
 		goto out_drv;
 	}
 	ws = wakeup_source_register("alarmtimer");
-	if (!ws) {
-		error = -ENOMEM;
-		goto out_ws;
-	}
-
-	INIT_DELAYED_WORK(&work, alarm_work_func);
-	power_off_alarm_workqueue =
-		create_singlethread_workqueue("power_off_alarm");
-	if (!power_off_alarm_workqueue) {
-		error = -ENOMEM;
-		goto out_wq;
-	}
-
 	return 0;
-out_wq:
-	wakeup_source_unregister(ws);
-out_ws:
-	platform_device_unregister(pdev);
+
 out_drv:
 	platform_driver_unregister(&alarmtimer_driver);
 out_if:
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index 2aef653..a01a71f 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -94,17 +94,15 @@
 };
 
 static const int hrtimer_clock_to_base_table[MAX_CLOCKS] = {
+	/* Make sure we catch unsupported clockids */
+	[0 ... MAX_CLOCKS - 1]	= HRTIMER_MAX_CLOCK_BASES,
+
 	[CLOCK_REALTIME]	= HRTIMER_BASE_REALTIME,
 	[CLOCK_MONOTONIC]	= HRTIMER_BASE_MONOTONIC,
 	[CLOCK_BOOTTIME]	= HRTIMER_BASE_BOOTTIME,
 	[CLOCK_TAI]		= HRTIMER_BASE_TAI,
 };
 
-static inline int hrtimer_clockid_to_base(clockid_t clock_id)
-{
-	return hrtimer_clock_to_base_table[clock_id];
-}
-
 /*
  * Functions and macros which are different for UP/SMP systems are kept in a
  * single place
@@ -1091,6 +1089,18 @@
 }
 #endif
 
+static inline int hrtimer_clockid_to_base(clockid_t clock_id)
+{
+	if (likely(clock_id < MAX_CLOCKS)) {
+		int base = hrtimer_clock_to_base_table[clock_id];
+
+		if (likely(base != HRTIMER_MAX_CLOCK_BASES))
+			return base;
+	}
+	WARN(1, "Invalid clockid %d. Using MONOTONIC\n", clock_id);
+	return HRTIMER_BASE_MONOTONIC;
+}
+
 static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
 			   enum hrtimer_mode mode)
 {
diff --git a/kernel/time/tick-oneshot.c b/kernel/time/tick-oneshot.c
index b513446..05ae01e 100644
--- a/kernel/time/tick-oneshot.c
+++ b/kernel/time/tick-oneshot.c
@@ -33,6 +33,7 @@
 		 * We don't need the clock event device any more, stop it.
 		 */
 		clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT_STOPPED);
+		dev->next_event.tv64 = KTIME_MAX;
 		return 0;
 	}
 
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index e671a32..4c0b001 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -201,6 +201,7 @@
 	bool			migration_enabled;
 	bool			nohz_active;
 	bool			is_idle;
+	bool			must_forward_clk;
 	DECLARE_BITMAP(pending_map, WHEEL_SIZE);
 	struct hlist_head	vectors[WHEEL_SIZE];
 } ____cacheline_aligned;
@@ -244,7 +245,7 @@
 	int ret;
 
 	mutex_lock(&mutex);
-	ret = proc_dointvec(table, write, buffer, lenp, ppos);
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
 	if (!ret && write)
 		timers_update_migration(false);
 	mutex_unlock(&mutex);
@@ -874,13 +875,19 @@
 
 static inline void forward_timer_base(struct timer_base *base)
 {
-	unsigned long jnow = READ_ONCE(jiffies);
+	unsigned long jnow;
 
 	/*
-	 * We only forward the base when it's idle and we have a delta between
-	 * base clock and jiffies.
+	 * We only forward the base when we are idle or have just come out of
+	 * idle (must_forward_clk logic), and have a delta between base clock
+	 * and jiffies. In the common case, run_timers will take care of it.
 	 */
-	if (!base->is_idle || (long) (jnow - base->clk) < 2)
+	if (likely(!base->must_forward_clk))
+		return;
+
+	jnow = READ_ONCE(jiffies);
+	base->must_forward_clk = base->is_idle;
+	if ((long)(jnow - base->clk) < 2)
 		return;
 
 	/*
@@ -956,6 +963,11 @@
 	 * same array bucket then just return:
 	 */
 	if (timer_pending(timer)) {
+		/*
+		 * The downside of this optimization is that it can result in
+		 * larger granularity than you would get from adding a new
+		 * timer with this expiry.
+		 */
 		if (timer->expires == expires)
 			return 1;
 
@@ -966,6 +978,7 @@
 		 * dequeue/enqueue dance.
 		 */
 		base = lock_timer_base(timer, &flags);
+		forward_timer_base(base);
 
 		clk = base->clk;
 		idx = calc_wheel_index(expires, clk);
@@ -982,6 +995,7 @@
 		}
 	} else {
 		base = lock_timer_base(timer, &flags);
+		forward_timer_base(base);
 	}
 
 	ret = detach_if_pending(timer, base, false);
@@ -1009,12 +1023,10 @@
 			spin_lock(&base->lock);
 			WRITE_ONCE(timer->flags,
 				   (timer->flags & ~TIMER_BASEMASK) | base->cpu);
+			forward_timer_base(base);
 		}
 	}
 
-	/* Try to forward a stale timer base clock */
-	forward_timer_base(base);
-
 	timer->expires = expires;
 	/*
 	 * If 'idx' was calculated above and the base time did not advance
@@ -1130,6 +1142,7 @@
 		WRITE_ONCE(timer->flags,
 			   (timer->flags & ~TIMER_BASEMASK) | cpu);
 	}
+	forward_timer_base(base);
 
 	debug_activate(timer, timer->expires);
 	internal_add_timer(base, timer);
@@ -1372,8 +1385,8 @@
 
 	pos = find_next_bit(base->pending_map, start, offset);
 	pos_down = pos < start ? pos + LVL_SIZE - start : -1;
-	if (((pos_up + base->clk) << LVL_SHIFT(lvl)) >
-		((pos_down + base->clk) << LVL_SHIFT(lvl)))
+	if (((pos_up + (u64)base->clk) << LVL_SHIFT(lvl)) >
+		((pos_down + (u64)base->clk) << LVL_SHIFT(lvl)))
 		return pos_down;
 	return pos_up;
 }
@@ -1544,12 +1557,18 @@
 		base->is_idle = false;
 	} else {
 		if (!is_max_delta)
-			expires = basem + (nextevt - basej) * TICK_NSEC;
+			expires = basem + (u64)(nextevt - basej) * TICK_NSEC;
 		/*
-		 * If we expect to sleep more than a tick, mark the base idle:
+		 * If we expect to sleep more than a tick, mark the base idle.
+		 * Also the tick is stopped so any added timer must forward
+		 * the base clk itself to keep granularity small. This idle
+		 * logic is only maintained for the BASE_STD base, deferrable
+		 * timers may still see large granularity skew (by design).
 		 */
-		if ((expires - basem) > TICK_NSEC)
+		if ((expires - basem) > TICK_NSEC) {
+			base->must_forward_clk = true;
 			base->is_idle = true;
+		}
 	}
 	spin_unlock(&base->lock);
 
@@ -1659,6 +1678,19 @@
 {
 	struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);
 
+	/*
+	 * must_forward_clk must be cleared before running timers so that any
+	 * timer functions that call mod_timer will not try to forward the
+	 * base. idle trcking / clock forwarding logic is only used with
+	 * BASE_STD timers.
+	 *
+	 * The deferrable base does not do idle tracking at all, so we do
+	 * not forward it. This can result in very large variations in
+	 * granularity for deferrable timers, but they can be deferred for
+	 * long periods due to idle.
+	 */
+	base->must_forward_clk = false;
+
 	__run_timers(base);
 	if (IS_ENABLED(CONFIG_NO_HZ_COMMON) && base->nohz_active)
 		__run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 5dcb992..41805fb 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -203,10 +203,36 @@
 		fmt_cnt++;
 	}
 
-	return __trace_printk(1/* fake ip will not be printed */, fmt,
-			      mod[0] == 2 ? arg1 : mod[0] == 1 ? (long) arg1 : (u32) arg1,
-			      mod[1] == 2 ? arg2 : mod[1] == 1 ? (long) arg2 : (u32) arg2,
-			      mod[2] == 2 ? arg3 : mod[2] == 1 ? (long) arg3 : (u32) arg3);
+/* Horrid workaround for getting va_list handling working with different
+ * argument type combinations generically for 32 and 64 bit archs.
+ */
+#define __BPF_TP_EMIT()	__BPF_ARG3_TP()
+#define __BPF_TP(...)							\
+	__trace_printk(1 /* Fake ip will not be printed. */,		\
+		       fmt, ##__VA_ARGS__)
+
+#define __BPF_ARG1_TP(...)						\
+	((mod[0] == 2 || (mod[0] == 1 && __BITS_PER_LONG == 64))	\
+	  ? __BPF_TP(arg1, ##__VA_ARGS__)				\
+	  : ((mod[0] == 1 || (mod[0] == 0 && __BITS_PER_LONG == 32))	\
+	      ? __BPF_TP((long)arg1, ##__VA_ARGS__)			\
+	      : __BPF_TP((u32)arg1, ##__VA_ARGS__)))
+
+#define __BPF_ARG2_TP(...)						\
+	((mod[1] == 2 || (mod[1] == 1 && __BITS_PER_LONG == 64))	\
+	  ? __BPF_ARG1_TP(arg2, ##__VA_ARGS__)				\
+	  : ((mod[1] == 1 || (mod[1] == 0 && __BITS_PER_LONG == 32))	\
+	      ? __BPF_ARG1_TP((long)arg2, ##__VA_ARGS__)		\
+	      : __BPF_ARG1_TP((u32)arg2, ##__VA_ARGS__)))
+
+#define __BPF_ARG3_TP(...)						\
+	((mod[2] == 2 || (mod[2] == 1 && __BITS_PER_LONG == 64))	\
+	  ? __BPF_ARG2_TP(arg3, ##__VA_ARGS__)				\
+	  : ((mod[2] == 1 || (mod[2] == 0 && __BITS_PER_LONG == 32))	\
+	      ? __BPF_ARG2_TP((long)arg3, ##__VA_ARGS__)		\
+	      : __BPF_ARG2_TP((u32)arg3, ##__VA_ARGS__)))
+
+	return __BPF_TP_EMIT();
 }
 
 static const struct bpf_func_proto bpf_trace_printk_proto = {
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 4f7ea84..5b8d718 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -876,6 +876,10 @@
 
 	function_profile_call(trace->func, 0, NULL, NULL);
 
+	/* If function graph is shutting down, ret_stack can be NULL */
+	if (!current->ret_stack)
+		return 0;
+
 	if (index >= 0 && index < FTRACE_RETFUNC_DEPTH)
 		current->ret_stack[index].subtime = 0;
 
@@ -2743,13 +2747,14 @@
 
 	if (!command || !ftrace_enabled) {
 		/*
-		 * If these are per_cpu ops, they still need their
-		 * per_cpu field freed. Since, function tracing is
+		 * If these are dynamic or per_cpu ops, they still
+		 * need their data freed. Since, function tracing is
 		 * not currently active, we can just free them
 		 * without synchronizing all CPUs.
 		 */
-		if (ops->flags & FTRACE_OPS_FL_PER_CPU)
-			per_cpu_ops_free(ops);
+		if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU))
+			goto free_ops;
+
 		return 0;
 	}
 
@@ -2804,6 +2809,7 @@
 	if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) {
 		schedule_on_each_cpu(ftrace_sync);
 
+ free_ops:
 		arch_ftrace_trampoline_free(ops);
 
 		if (ops->flags & FTRACE_OPS_FL_PER_CPU)
@@ -4375,9 +4381,6 @@
 static char ftrace_graph_notrace_buf[FTRACE_FILTER_SIZE] __initdata;
 static int ftrace_set_func(unsigned long *array, int *idx, int size, char *buffer);
 
-static unsigned long save_global_trampoline;
-static unsigned long save_global_flags;
-
 static int __init set_graph_function(char *str)
 {
 	strlcpy(ftrace_graph_buf, str, FTRACE_FILTER_SIZE);
@@ -5975,17 +5978,6 @@
 	unregister_pm_notifier(&ftrace_suspend_notifier);
 	unregister_trace_sched_switch(ftrace_graph_probe_sched_switch, NULL);
 
-#ifdef CONFIG_DYNAMIC_FTRACE
-	/*
-	 * Function graph does not allocate the trampoline, but
-	 * other global_ops do. We need to reset the ALLOC_TRAMP flag
-	 * if one was used.
-	 */
-	global_ops.trampoline = save_global_trampoline;
-	if (save_global_flags & FTRACE_OPS_FL_ALLOC_TRAMP)
-		global_ops.flags |= FTRACE_OPS_FL_ALLOC_TRAMP;
-#endif
-
  out:
 	mutex_unlock(&ftrace_lock);
 }
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 66b0714..e3aae88 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -2416,11 +2416,17 @@
 	if (!buffer || buffer->nesting >= 4)
 		return NULL;
 
-	return &buffer->buffer[buffer->nesting++][0];
+	buffer->nesting++;
+
+	/* Interrupts must see nesting incremented before we use the buffer */
+	barrier();
+	return &buffer->buffer[buffer->nesting][0];
 }
 
 static void put_trace_buf(void)
 {
+	/* Don't let the decrement of nesting leak before this */
+	barrier();
 	this_cpu_dec(trace_percpu_buffer->nesting);
 }
 
@@ -3636,11 +3642,17 @@
 	/* If this file was open for write, then erase contents */
 	if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC)) {
 		int cpu = tracing_get_cpu(inode);
+		struct trace_buffer *trace_buf = &tr->trace_buffer;
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+		if (tr->current_trace->print_max)
+			trace_buf = &tr->max_buffer;
+#endif
 
 		if (cpu == RING_BUFFER_ALL_CPUS)
-			tracing_reset_online_cpus(&tr->trace_buffer);
+			tracing_reset_online_cpus(trace_buf);
 		else
-			tracing_reset(&tr->trace_buffer, cpu);
+			tracing_reset(trace_buf, cpu);
 	}
 
 	if (file->f_mode & FMODE_READ) {
@@ -5275,7 +5287,7 @@
 		 *
 		 * iter->pos will be 0 if we haven't read anything.
 		 */
-		if (!tracing_is_on() && iter->pos)
+		if (!tracer_tracing_is_on(iter->tr) && iter->pos)
 			break;
 
 		mutex_unlock(&iter->mutex);
@@ -5814,7 +5826,7 @@
 	tracing_reset_online_cpus(&tr->trace_buffer);
 
 #ifdef CONFIG_TRACER_MAX_TRACE
-	if (tr->flags & TRACE_ARRAY_FL_GLOBAL && tr->max_buffer.buffer)
+	if (tr->max_buffer.buffer)
 		ring_buffer_set_clock(tr->max_buffer.buffer, trace_clocks[i].func);
 	tracing_reset_online_cpus(&tr->max_buffer);
 #endif
@@ -7926,4 +7938,4 @@
 }
 
 fs_initcall(tracer_init_tracefs);
-late_initcall(clear_boot_tracer);
+late_initcall_sync(clear_boot_tracer);
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 9daa9b3..0193f58 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -1926,6 +1926,10 @@
 		if (err && set_str)
 			append_filter_err(ps, filter);
 	}
+	if (err && !set_str) {
+		free_event_filter(filter);
+		filter = NULL;
+	}
 	create_filter_finish(ps);
 
 	*filterp = filter;
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index b0f86ea..ca70d11 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -272,7 +272,7 @@
 		goto out_free;
 	if (cnt > 1) {
 		if (trace_selftest_test_global_cnt == 0)
-			goto out;
+			goto out_free;
 	}
 	if (trace_selftest_test_dyn_cnt == 0)
 		goto out_free;
diff --git a/kernel/trace/tracing_map.c b/kernel/trace/tracing_map.c
index 0a689bb..305039b 100644
--- a/kernel/trace/tracing_map.c
+++ b/kernel/trace/tracing_map.c
@@ -221,16 +221,19 @@
 	if (!a)
 		return;
 
-	if (!a->pages) {
-		kfree(a);
-		return;
-	}
+	if (!a->pages)
+		goto free;
 
 	for (i = 0; i < a->n_pages; i++) {
 		if (!a->pages[i])
 			break;
 		free_page((unsigned long)a->pages[i]);
 	}
+
+	kfree(a->pages);
+
+ free:
+	kfree(a);
 }
 
 struct tracing_map_array *tracing_map_array_alloc(unsigned int n_elts,
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index cffb5f2..cfbf027 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -81,10 +81,10 @@
 static DEFINE_PER_CPU(unsigned int, watchdog_en);
 static DEFINE_PER_CPU(bool, softlockup_touch_sync);
 static DEFINE_PER_CPU(bool, soft_watchdog_warn);
-static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
 static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt);
 static DEFINE_PER_CPU(struct task_struct *, softlockup_task_ptr_saved);
-static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
+DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
+DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
 static unsigned long soft_lockup_nmi_warn;
 
 unsigned int __read_mostly softlockup_panic =
@@ -254,6 +254,10 @@
 {
 }
 
+void __weak watchdog_check_hardlockup_other_cpu(void)
+{
+}
+
 static int watchdog_enable_all_cpus(void);
 static void watchdog_disable_all_cpus(void);
 
@@ -271,6 +275,9 @@
 	/* kick the hardlockup detector */
 	watchdog_interrupt_count();
 
+	/* test for hardlockups on the next cpu */
+	watchdog_check_hardlockup_other_cpu();
+
 	/* kick the softlockup detector */
 	wake_up_process(__this_cpu_read(softlockup_watchdog));
 
diff --git a/kernel/watchdog_hld.c b/kernel/watchdog_hld.c
index 12b8dd6..5b2c127 100644
--- a/kernel/watchdog_hld.c
+++ b/kernel/watchdog_hld.c
@@ -18,7 +18,11 @@
 
 static DEFINE_PER_CPU(bool, hard_watchdog_warn);
 static DEFINE_PER_CPU(bool, watchdog_nmi_touch);
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static cpumask_t __read_mostly watchdog_cpus;
+#else
 static DEFINE_PER_CPU(struct perf_event *, watchdog_ev);
+#endif
 
 /* boot commands */
 /*
@@ -26,7 +30,10 @@
  */
 unsigned int __read_mostly hardlockup_panic =
 			CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE;
+
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_NMI
 static unsigned long hardlockup_allcpu_dumped;
+#endif
 /*
  * We may not want to enable hard lockup detection by default in all cases,
  * for example when running the kernel as a guest on a hypervisor. In these
@@ -68,6 +75,108 @@
 }
 EXPORT_SYMBOL(touch_nmi_watchdog);
 
+#ifdef CONFIG_HARDLOCKUP_DETECTOR_OTHER_CPU
+static unsigned int watchdog_next_cpu(unsigned int cpu)
+{
+	cpumask_t cpus = watchdog_cpus;
+	unsigned int next_cpu;
+
+	next_cpu = cpumask_next(cpu, &cpus);
+	if (next_cpu >= nr_cpu_ids)
+		next_cpu = cpumask_first(&cpus);
+
+	if (next_cpu == cpu)
+		return nr_cpu_ids;
+
+	return next_cpu;
+}
+
+static int is_hardlockup_other_cpu(unsigned int cpu)
+{
+	unsigned long hrint = per_cpu(hrtimer_interrupts, cpu);
+
+	if (per_cpu(hrtimer_interrupts_saved, cpu) == hrint)
+		return 1;
+
+	per_cpu(hrtimer_interrupts_saved, cpu) = hrint;
+	return 0;
+}
+
+void watchdog_check_hardlockup_other_cpu(void)
+{
+	unsigned int next_cpu;
+
+	/*
+	 * Test for hardlockups every 3 samples.  The sample period is
+	 *  watchdog_thresh * 2 / 5, so 3 samples gets us back to slightly over
+	 *  watchdog_thresh (over by 20%).
+	 */
+	if (__this_cpu_read(hrtimer_interrupts) % 3 != 0)
+		return;
+
+	/* check for a hardlockup on the next cpu */
+	next_cpu = watchdog_next_cpu(smp_processor_id());
+	if (next_cpu >= nr_cpu_ids)
+		return;
+
+	smp_rmb();
+
+	if (per_cpu(watchdog_nmi_touch, next_cpu) == true) {
+		per_cpu(watchdog_nmi_touch, next_cpu) = false;
+		return;
+	}
+
+	if (is_hardlockup_other_cpu(next_cpu)) {
+		/* only warn once */
+		if (per_cpu(hard_watchdog_warn, next_cpu) == true)
+			return;
+
+		if (hardlockup_panic)
+			panic("Watchdog detected hard LOCKUP on cpu %u",
+				next_cpu);
+		else
+			WARN(1, "Watchdog detected hard LOCKUP on cpu %u",
+				next_cpu);
+
+		per_cpu(hard_watchdog_warn, next_cpu) = true;
+	} else {
+		per_cpu(hard_watchdog_warn, next_cpu) = false;
+	}
+}
+
+int watchdog_nmi_enable(unsigned int cpu)
+{
+	/*
+	 * The new cpu will be marked online before the first hrtimer interrupt
+	 * runs on it.  If another cpu tests for a hardlockup on the new cpu
+	 * before it has run its first hrtimer, it will get a false positive.
+	 * Touch the watchdog on the new cpu to delay the first check for at
+	 * least 3 sampling periods to guarantee one hrtimer has run on the new
+	 * cpu.
+	 */
+	per_cpu(watchdog_nmi_touch, cpu) = true;
+	smp_wmb();
+	cpumask_set_cpu(cpu, &watchdog_cpus);
+	return 0;
+}
+
+void watchdog_nmi_disable(unsigned int cpu)
+{
+	unsigned int next_cpu = watchdog_next_cpu(cpu);
+
+	/*
+	 * Offlining this cpu will cause the cpu before this one to start
+	 * checking the one after this one.  If this cpu just finished checking
+	 * the next cpu and updating hrtimer_interrupts_saved, and then the
+	 * previous cpu checks it within one sample period, it will trigger a
+	 * false positive.  Touch the watchdog on the next cpu to prevent it.
+	 */
+	if (next_cpu < nr_cpu_ids)
+		per_cpu(watchdog_nmi_touch, next_cpu) = true;
+	smp_wmb();
+	cpumask_clear_cpu(cpu, &watchdog_cpus);
+}
+#else
 static struct perf_event_attr wd_hw_attr = {
 	.type		= PERF_TYPE_HARDWARE,
 	.config		= PERF_COUNT_HW_CPU_CYCLES,
@@ -228,3 +337,4 @@
 		cpu0_err = 0;
 	}
 }
+#endif
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 812b8f8..3630826 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -70,6 +70,7 @@
 	 * attach_mutex to avoid changing binding state while
 	 * worker_attach_to_pool() is in progress.
 	 */
+	POOL_MANAGER_ACTIVE	= 1 << 0,	/* being managed */
 	POOL_DISASSOCIATED	= 1 << 2,	/* cpu can't serve workers */
 
 	/* worker flags */
@@ -167,7 +168,6 @@
 						/* L: hash of busy workers */
 
 	/* see manage_workers() for details on the two manager mutexes */
-	struct mutex		manager_arb;	/* manager arbitration */
 	struct worker		*manager;	/* L: purely informational */
 	struct mutex		attach_mutex;	/* attach/detach exclusion */
 	struct list_head	workers;	/* A: attached workers */
@@ -299,6 +299,7 @@
 
 static DEFINE_MUTEX(wq_pool_mutex);	/* protects pools and workqueues list */
 static DEFINE_SPINLOCK(wq_mayday_lock);	/* protects wq->maydays list */
+static DECLARE_WAIT_QUEUE_HEAD(wq_manager_wait); /* wait for manager to go away */
 
 static LIST_HEAD(workqueues);		/* PR: list of all workqueues */
 static bool workqueue_freezing;		/* PL: have wqs started freezing? */
@@ -801,7 +802,7 @@
 /* Do we have too many workers and should some go away? */
 static bool too_many_workers(struct worker_pool *pool)
 {
-	bool managing = mutex_is_locked(&pool->manager_arb);
+	bool managing = pool->flags & POOL_MANAGER_ACTIVE;
 	int nr_idle = pool->nr_idle + managing; /* manager is considered idle */
 	int nr_busy = pool->nr_workers - nr_idle;
 
@@ -1985,24 +1986,17 @@
 {
 	struct worker_pool *pool = worker->pool;
 
-	/*
-	 * Anyone who successfully grabs manager_arb wins the arbitration
-	 * and becomes the manager.  mutex_trylock() on pool->manager_arb
-	 * failure while holding pool->lock reliably indicates that someone
-	 * else is managing the pool and the worker which failed trylock
-	 * can proceed to executing work items.  This means that anyone
-	 * grabbing manager_arb is responsible for actually performing
-	 * manager duties.  If manager_arb is grabbed and released without
-	 * actual management, the pool may stall indefinitely.
-	 */
-	if (!mutex_trylock(&pool->manager_arb))
+	if (pool->flags & POOL_MANAGER_ACTIVE)
 		return false;
+
+	pool->flags |= POOL_MANAGER_ACTIVE;
 	pool->manager = worker;
 
 	maybe_create_worker(pool);
 
 	pool->manager = NULL;
-	mutex_unlock(&pool->manager_arb);
+	pool->flags &= ~POOL_MANAGER_ACTIVE;
+	wake_up(&wq_manager_wait);
 	return true;
 }
 
@@ -3210,7 +3204,6 @@
 	setup_timer(&pool->mayday_timer, pool_mayday_timeout,
 		    (unsigned long)pool);
 
-	mutex_init(&pool->manager_arb);
 	mutex_init(&pool->attach_mutex);
 	INIT_LIST_HEAD(&pool->workers);
 
@@ -3280,13 +3273,15 @@
 	hash_del(&pool->hash_node);
 
 	/*
-	 * Become the manager and destroy all workers.  Grabbing
-	 * manager_arb prevents @pool's workers from blocking on
-	 * attach_mutex.
+	 * Become the manager and destroy all workers.  This prevents
+	 * @pool's workers from blocking on attach_mutex.  We're the last
+	 * manager and @pool gets freed with the flag set.
 	 */
-	mutex_lock(&pool->manager_arb);
-
 	spin_lock_irq(&pool->lock);
+	wait_event_lock_irq(wq_manager_wait,
+			    !(pool->flags & POOL_MANAGER_ACTIVE), pool->lock);
+	pool->flags |= POOL_MANAGER_ACTIVE;
+
 	while ((worker = first_idle_worker(pool)))
 		destroy_worker(worker);
 	WARN_ON(pool->nr_workers || pool->nr_idle);
@@ -3300,8 +3295,6 @@
 	if (pool->detach_completion)
 		wait_for_completion(pool->detach_completion);
 
-	mutex_unlock(&pool->manager_arb);
-
 	/* shut down the timers */
 	del_timer_sync(&pool->idle_timer);
 	del_timer_sync(&pool->mayday_timer);
@@ -3737,8 +3730,12 @@
 		return -EINVAL;
 
 	/* creating multiple pwqs breaks ordering guarantee */
-	if (WARN_ON((wq->flags & __WQ_ORDERED) && !list_empty(&wq->pwqs)))
-		return -EINVAL;
+	if (!list_empty(&wq->pwqs)) {
+		if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
+			return -EINVAL;
+
+		wq->flags &= ~__WQ_ORDERED;
+	}
 
 	ctx = apply_wqattrs_prepare(wq, attrs);
 	if (!ctx)
@@ -3922,6 +3919,16 @@
 	struct workqueue_struct *wq;
 	struct pool_workqueue *pwq;
 
+	/*
+	 * Unbound && max_active == 1 used to imply ordered, which is no
+	 * longer the case on NUMA machines due to per-node pools.  While
+	 * alloc_ordered_workqueue() is the right way to create an ordered
+	 * workqueue, keep the previous behavior to avoid subtle breakages
+	 * on NUMA.
+	 */
+	if ((flags & WQ_UNBOUND) && max_active == 1)
+		flags |= __WQ_ORDERED;
+
 	/* see the comment above the definition of WQ_POWER_EFFICIENT */
 	if ((flags & WQ_POWER_EFFICIENT) && wq_power_efficient)
 		flags |= WQ_UNBOUND;
@@ -4110,13 +4117,14 @@
 	struct pool_workqueue *pwq;
 
 	/* disallow meddling with max_active for ordered workqueues */
-	if (WARN_ON(wq->flags & __WQ_ORDERED))
+	if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
 		return;
 
 	max_active = wq_clamp_max_active(max_active, wq->flags, wq->name);
 
 	mutex_lock(&wq->mutex);
 
+	wq->flags &= ~__WQ_ORDERED;
 	wq->saved_max_active = max_active;
 
 	for_each_pwq(pwq, wq)
@@ -5221,7 +5229,7 @@
 	 * attributes breaks ordering guarantee.  Disallow exposing ordered
 	 * workqueues.
 	 */
-	if (WARN_ON(wq->flags & __WQ_ORDERED))
+	if (WARN_ON(wq->flags & __WQ_ORDERED_EXPLICIT))
 		return -EINVAL;
 
 	wq->wq_dev = wq_dev = kzalloc(sizeof(*wq_dev), GFP_KERNEL);
diff --git a/kernel/workqueue_internal.h b/kernel/workqueue_internal.h
index 8635417..29fa81f 100644
--- a/kernel/workqueue_internal.h
+++ b/kernel/workqueue_internal.h
@@ -9,6 +9,7 @@
 
 #include <linux/workqueue.h>
 #include <linux/kthread.h>
+#include <linux/preempt.h>
 
 struct worker_pool;
 
@@ -59,7 +60,7 @@
  */
 static inline struct worker *current_wq_worker(void)
 {
-	if (current->flags & PF_WQ_WORKER)
+	if (in_task() && (current->flags & PF_WQ_WORKER))
 		return kthread_data(current);
 	return NULL;
 }
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2f9f7aa..2812580 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -145,7 +145,7 @@
 
 config DEBUG_INFO_SPLIT
 	bool "Produce split debuginfo in .dwo files"
-	depends on DEBUG_INFO
+	depends on DEBUG_INFO && !FRV
 	help
 	  Generate debug info into separate .dwo files. This significantly
 	  reduces the build directory size for builds with DEBUG_INFO,
@@ -779,11 +779,20 @@
 	  The frequency of hrtimer and NMI events and the soft and hard lockup
 	  thresholds can be controlled through the sysctl watchdog_thresh.
 
-config HARDLOCKUP_DETECTOR
+config HARDLOCKUP_DETECTOR_NMI
 	def_bool y
 	depends on LOCKUP_DETECTOR && !HAVE_NMI_WATCHDOG
 	depends on PERF_EVENTS && HAVE_PERF_EVENTS_NMI
 
+config HARDLOCKUP_DETECTOR_OTHER_CPU
+       def_bool y
+       depends on LOCKUP_DETECTOR && SMP
+       depends on !HARDLOCKUP_DETECTOR_NMI && !HAVE_NMI_WATCHDOG
+
+config HARDLOCKUP_DETECTOR
+       def_bool y
+       depends on HARDLOCKUP_DETECTOR_NMI || HARDLOCKUP_DETECTOR_OTHER_CPU
+
 config BOOTPARAM_HARDLOCKUP_PANIC
 	bool "Panic (Reboot) On Hard Lockups"
 	depends on HARDLOCKUP_DETECTOR
diff --git a/lib/asn1_decoder.c b/lib/asn1_decoder.c
index 0bd8a61..1ef0cec 100644
--- a/lib/asn1_decoder.c
+++ b/lib/asn1_decoder.c
@@ -228,7 +228,7 @@
 		hdr = 2;
 
 		/* Extract a tag from the data */
-		if (unlikely(dp >= datalen - 1))
+		if (unlikely(datalen - dp < 2))
 			goto data_overrun_error;
 		tag = data[dp++];
 		if (unlikely((tag & 0x1f) == ASN1_LONG_TAG))
@@ -274,7 +274,7 @@
 				int n = len - 0x80;
 				if (unlikely(n > 2))
 					goto length_too_long;
-				if (unlikely(dp >= datalen - n))
+				if (unlikely(n > datalen - dp))
 					goto data_overrun_error;
 				hdr += n;
 				for (len = 0; n > 0; n--) {
@@ -284,6 +284,9 @@
 				if (unlikely(len > datalen - dp))
 					goto data_overrun_error;
 			}
+		} else {
+			if (unlikely(len > datalen - dp))
+				goto data_overrun_error;
 		}
 
 		if (flags & FLAG_CONS) {
diff --git a/lib/assoc_array.c b/lib/assoc_array.c
index 59fd7c0..5cd0935 100644
--- a/lib/assoc_array.c
+++ b/lib/assoc_array.c
@@ -598,21 +598,31 @@
 		if ((edit->segment_cache[ASSOC_ARRAY_FAN_OUT] ^ base_seg) == 0)
 			goto all_leaves_cluster_together;
 
-		/* Otherwise we can just insert a new node ahead of the old
-		 * one.
+		/* Otherwise all the old leaves cluster in the same slot, but
+		 * the new leaf wants to go into a different slot - so we
+		 * create a new node (n0) to hold the new leaf and a pointer to
+		 * a new node (n1) holding all the old leaves.
+		 *
+		 * This can be done by falling through to the node splitting
+		 * path.
 		 */
-		goto present_leaves_cluster_but_not_new_leaf;
+		pr_devel("present leaves cluster but not new leaf\n");
 	}
 
 split_node:
 	pr_devel("split node\n");
 
-	/* We need to split the current node; we know that the node doesn't
-	 * simply contain a full set of leaves that cluster together (it
-	 * contains meta pointers and/or non-clustering leaves).
+	/* We need to split the current node.  The node must contain anything
+	 * from a single leaf (in the one leaf case, this leaf will cluster
+	 * with the new leaf) and the rest meta-pointers, to all leaves, some
+	 * of which may cluster.
+	 *
+	 * It won't contain the case in which all the current leaves plus the
+	 * new leaves want to cluster in the same slot.
 	 *
 	 * We need to expel at least two leaves out of a set consisting of the
-	 * leaves in the node and the new leaf.
+	 * leaves in the node and the new leaf.  The current meta pointers can
+	 * just be copied as they shouldn't cluster with any of the leaves.
 	 *
 	 * We need a new node (n0) to replace the current one and a new node to
 	 * take the expelled nodes (n1).
@@ -717,33 +727,6 @@
 	pr_devel("<--%s() = ok [split node]\n", __func__);
 	return true;
 
-present_leaves_cluster_but_not_new_leaf:
-	/* All the old leaves cluster in the same slot, but the new leaf wants
-	 * to go into a different slot, so we create a new node to hold the new
-	 * leaf and a pointer to a new node holding all the old leaves.
-	 */
-	pr_devel("present leaves cluster but not new leaf\n");
-
-	new_n0->back_pointer = node->back_pointer;
-	new_n0->parent_slot = node->parent_slot;
-	new_n0->nr_leaves_on_branch = node->nr_leaves_on_branch;
-	new_n1->back_pointer = assoc_array_node_to_ptr(new_n0);
-	new_n1->parent_slot = edit->segment_cache[0];
-	new_n1->nr_leaves_on_branch = node->nr_leaves_on_branch;
-	edit->adjust_count_on = new_n0;
-
-	for (i = 0; i < ASSOC_ARRAY_FAN_OUT; i++)
-		new_n1->slots[i] = node->slots[i];
-
-	new_n0->slots[edit->segment_cache[0]] = assoc_array_node_to_ptr(new_n0);
-	edit->leaf_p = &new_n0->slots[edit->segment_cache[ASSOC_ARRAY_FAN_OUT]];
-
-	edit->set[0].ptr = &assoc_array_ptr_to_node(node->back_pointer)->slots[node->parent_slot];
-	edit->set[0].to = assoc_array_node_to_ptr(new_n0);
-	edit->excised_meta[0] = assoc_array_node_to_ptr(node);
-	pr_devel("<--%s() = ok [insert node before]\n", __func__);
-	return true;
-
 all_leaves_cluster_together:
 	/* All the leaves, new and old, want to cluster together in this node
 	 * in the same slot, so we have to replace this node with a shortcut to
diff --git a/lib/digsig.c b/lib/digsig.c
index 55b8b2f..a876156 100644
--- a/lib/digsig.c
+++ b/lib/digsig.c
@@ -87,6 +87,12 @@
 	down_read(&key->sem);
 	ukp = user_key_payload(key);
 
+	if (!ukp) {
+		/* key was revoked before we acquired its semaphore */
+		err = -EKEYREVOKED;
+		goto err1;
+	}
+
 	if (ukp->datalen < sizeof(*pkh))
 		goto err1;
 
diff --git a/lib/lz4/lz4hc_compress.c b/lib/lz4/lz4hc_compress.c
index f344f76..6b2e046 100644
--- a/lib/lz4/lz4hc_compress.c
+++ b/lib/lz4/lz4hc_compress.c
@@ -131,7 +131,7 @@
 #endif
 	int nbattempts = MAX_NB_ATTEMPTS;
 	size_t repl = 0, ml = 0;
-	u16 delta;
+	u16 delta = 0;
 
 	/* HC4 match finder */
 	lz4hc_insert(hc4, ip);
diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c
index 5a0f75a..eead4b3 100644
--- a/lib/mpi/mpicoder.c
+++ b/lib/mpi/mpicoder.c
@@ -364,11 +364,11 @@
 	}
 
 	miter.consumed = lzeros;
-	sg_miter_stop(&miter);
 
 	nbytes -= lzeros;
 	nbits = nbytes * 8;
 	if (nbits > MAX_EXTERN_MPI_BITS) {
+		sg_miter_stop(&miter);
 		pr_info("MPI: mpi too large (%u bits)\n", nbits);
 		return NULL;
 	}
@@ -376,6 +376,8 @@
 	if (nbytes > 0)
 		nbits -= count_leading_zeros(*buff) - (BITS_PER_LONG - 8);
 
+	sg_miter_stop(&miter);
+
 	nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
 	val = mpi_alloc(nlimbs);
 	if (!val)
diff --git a/lib/ratelimit.c b/lib/ratelimit.c
index 08f8043..d01f471 100644
--- a/lib/ratelimit.c
+++ b/lib/ratelimit.c
@@ -48,7 +48,9 @@
 	if (time_is_before_jiffies(rs->begin + rs->interval)) {
 		if (rs->missed) {
 			if (!(rs->flags & RATELIMIT_MSG_ON_RELEASE)) {
-				pr_warn("%s: %d callbacks suppressed\n", func, rs->missed);
+				printk_deferred(KERN_WARNING
+						"%s: %d callbacks suppressed\n",
+						func, rs->missed);
 				rs->missed = 0;
 			}
 		}
diff --git a/mm/internal.h b/mm/internal.h
index 537ac99..0ee4f54 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -74,11 +74,16 @@
 extern unsigned long highest_memmap_pfn;
 
 /*
+ * Maximum number of reclaim retries without progress before the OOM
+ * killer is consider the only way forward.
+ */
+#define MAX_RECLAIM_RETRIES 16
+
+/*
  * in mm/vmscan.c:
  */
 extern int isolate_lru_page(struct page *page);
 extern void putback_lru_page(struct page *page);
-extern bool pgdat_reclaimable(struct pglist_data *pgdat);
 
 /*
  * in mm/rmap.c:
@@ -472,6 +477,7 @@
 #ifdef CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH
 void try_to_unmap_flush(void);
 void try_to_unmap_flush_dirty(void);
+void flush_tlb_batched_pending(struct mm_struct *mm);
 #else
 static inline void try_to_unmap_flush(void)
 {
@@ -479,7 +485,9 @@
 static inline void try_to_unmap_flush_dirty(void)
 {
 }
-
+static inline void flush_tlb_batched_pending(struct mm_struct *mm)
+{
+}
 #endif /* CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH */
 
 extern const struct trace_print_flags pageflag_names[];
diff --git a/mm/madvise.c b/mm/madvise.c
index 279627a..088a5b22 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -21,6 +21,7 @@
 #include <linux/swap.h>
 #include <linux/swapops.h>
 #include <linux/mmu_notifier.h>
+#include "internal.h"
 
 #include <asm/tlb.h>
 
@@ -282,6 +283,7 @@
 		return 0;
 
 	orig_pte = pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
+	flush_tlb_batched_pending(mm);
 	arch_enter_lazy_mmu_mode();
 	for (; addr != end; pte++, addr += PAGE_SIZE) {
 		ptent = *pte;
@@ -329,8 +331,8 @@
 				pte_offset_map_lock(mm, pmd, addr, &ptl);
 				goto out;
 			}
-			put_page(page);
 			unlock_page(page);
+			put_page(page);
 			pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
 			pte--;
 			addr -= PAGE_SIZE;
@@ -531,6 +533,8 @@
 static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)
 {
 	struct page *p;
+	struct zone *zone;
+
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
 	for (; start < end; start += PAGE_SIZE <<
@@ -559,6 +563,11 @@
 		if (ret)
 			return ret;
 	}
+
+	/* Ensure that all poisoned pages are removed from per-cpu lists */
+	for_each_populated_zone(zone)
+		drain_all_pages(zone);
+
 	return 0;
 }
 #endif
diff --git a/mm/memblock.c b/mm/memblock.c
index f1eabcc..3b7d23c 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -301,31 +301,27 @@
 }
 
 #ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
-
-phys_addr_t __init_memblock get_allocated_memblock_reserved_regions_info(
-					phys_addr_t *addr)
+/**
+ * Discard memory and reserved arrays if they were allocated
+ */
+void __init memblock_discard(void)
 {
-	if (memblock.reserved.regions == memblock_reserved_init_regions)
-		return 0;
+	phys_addr_t addr, size;
 
-	*addr = __pa(memblock.reserved.regions);
+	if (memblock.reserved.regions != memblock_reserved_init_regions) {
+		addr = __pa(memblock.reserved.regions);
+		size = PAGE_ALIGN(sizeof(struct memblock_region) *
+				  memblock.reserved.max);
+		__memblock_free_late(addr, size);
+	}
 
-	return PAGE_ALIGN(sizeof(struct memblock_region) *
-			  memblock.reserved.max);
+	if (memblock.memory.regions != memblock_memory_init_regions) {
+		addr = __pa(memblock.memory.regions);
+		size = PAGE_ALIGN(sizeof(struct memblock_region) *
+				  memblock.memory.max);
+		__memblock_free_late(addr, size);
+	}
 }
-
-phys_addr_t __init_memblock get_allocated_memblock_memory_regions_info(
-					phys_addr_t *addr)
-{
-	if (memblock.memory.regions == memblock_memory_init_regions)
-		return 0;
-
-	*addr = __pa(memblock.memory.regions);
-
-	return PAGE_ALIGN(sizeof(struct memblock_region) *
-			  memblock.memory.max);
-}
-
 #endif
 
 /**
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 3b38b73..fce6c48 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -462,6 +462,8 @@
 	struct mem_cgroup_tree_per_node *mctz;
 
 	mctz = soft_limit_tree_from_page(page);
+	if (!mctz)
+		return;
 	/*
 	 * Necessary to update all ancestors when hierarchy is used.
 	 * because their event counter is not touched.
@@ -499,7 +501,8 @@
 	for_each_node(nid) {
 		mz = mem_cgroup_nodeinfo(memcg, nid);
 		mctz = soft_limit_tree_node(nid);
-		mem_cgroup_remove_exceeded(mz, mctz);
+		if (mctz)
+			mem_cgroup_remove_exceeded(mz, mctz);
 	}
 }
 
@@ -2565,7 +2568,7 @@
 	 * is empty. Do it lockless to prevent lock bouncing. Races
 	 * are acceptable as soft limit is best effort anyway.
 	 */
-	if (RB_EMPTY_ROOT(&mctz->rb_root))
+	if (!mctz || RB_EMPTY_ROOT(&mctz->rb_root))
 		return 0;
 
 	/*
diff --git a/mm/memory.c b/mm/memory.c
index 49d9b42..378ebc0 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1124,6 +1124,7 @@
 	init_rss_vec(rss);
 	start_pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
 	pte = start_pte;
+	flush_tlb_batched_pending(mm);
 	arch_enter_lazy_mmu_mode();
 	do {
 		pte_t ptent = *pte;
@@ -3595,6 +3596,11 @@
 	/* do counter updates before entering really critical section. */
 	check_sync_rss_stat(current);
 
+	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
+					    flags & FAULT_FLAG_INSTRUCTION,
+					    flags & FAULT_FLAG_REMOTE))
+		return VM_FAULT_SIGSEGV;
+
 	/*
 	 * Enable the memcg OOM handling for faults triggered in user
 	 * space.  Kernel faults are handled more gracefully.
@@ -3602,11 +3608,6 @@
 	if (flags & FAULT_FLAG_USER)
 		mem_cgroup_oom_enable();
 
-	if (!arch_vma_access_permitted(vma, flags & FAULT_FLAG_WRITE,
-					    flags & FAULT_FLAG_INSTRUCTION,
-					    flags & FAULT_FLAG_REMOTE))
-		return VM_FAULT_SIGSEGV;
-
 	if (unlikely(is_vm_hugetlb_page(vma)))
 		ret = hugetlb_fault(vma->vm_mm, vma, address, flags);
 	else
@@ -3634,8 +3635,18 @@
 	 * further.
 	 */
 	if (unlikely((current->flags & PF_KTHREAD) && !(ret & VM_FAULT_ERROR)
-				&& test_bit(MMF_UNSTABLE, &vma->vm_mm->flags)))
+				&& test_bit(MMF_UNSTABLE, &vma->vm_mm->flags))) {
+
+		/*
+		 * We are going to enforce SIGBUS but the PF path might have
+		 * dropped the mmap_sem already so take it again so that
+		 * we do not break expectations of all arch specific PF paths
+		 * and g-u-p
+		 */
+		if (ret & VM_FAULT_RETRY)
+			down_read(&vma->vm_mm->mmap_sem);
 		ret = VM_FAULT_SIGBUS;
+	}
 
 	return ret;
 }
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index ede13734..0dab426 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -179,7 +179,7 @@
 void get_page_bootmem(unsigned long info,  struct page *page,
 		      unsigned long type)
 {
-	page->lru.next = (struct list_head *) type;
+	page->freelist = (void *)type;
 	SetPagePrivate(page);
 	set_page_private(page, info);
 	page_ref_inc(page);
@@ -189,11 +189,12 @@
 {
 	unsigned long type;
 
-	type = (unsigned long) page->lru.next;
+	type = (unsigned long) page->freelist;
 	BUG_ON(type < MEMORY_HOTPLUG_MIN_BOOTMEM_TYPE ||
 	       type > MEMORY_HOTPLUG_MAX_BOOTMEM_TYPE);
 
 	if (page_ref_dec_return(page) == 1) {
+		page->freelist = NULL;
 		ClearPagePrivate(page);
 		set_page_private(page, 0);
 		INIT_LIST_HEAD(&page->lru);
@@ -1201,7 +1202,11 @@
 
 		arch_refresh_nodedata(nid, pgdat);
 	} else {
-		/* Reset the nr_zones, order and classzone_idx before reuse */
+		/*
+		 * Reset the nr_zones, order and classzone_idx before reuse.
+		 * Note that kswapd will init kswapd_classzone_idx properly
+		 * when it starts in the near future.
+		 */
 		pgdat->nr_zones = 0;
 		pgdat->kswapd_order = 0;
 		pgdat->kswapd_classzone_idx = 0;
diff --git a/mm/mempolicy.c b/mm/mempolicy.c
index 9ff5657..9547583 100644
--- a/mm/mempolicy.c
+++ b/mm/mempolicy.c
@@ -927,11 +927,6 @@
 		*policy |= (pol->flags & MPOL_MODE_FLAGS);
 	}
 
-	if (vma) {
-		up_read(&current->mm->mmap_sem);
-		vma = NULL;
-	}
-
 	err = 0;
 	if (nmask) {
 		if (mpol_store_user_nodemask(pol)) {
diff --git a/mm/migrate.c b/mm/migrate.c
index f0b786d..eb1f043 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -40,6 +40,7 @@
 #include <linux/mmu_notifier.h>
 #include <linux/page_idle.h>
 #include <linux/page_owner.h>
+#include <linux/ptrace.h>
 
 #include <asm/tlbflush.h>
 
@@ -1665,7 +1666,6 @@
 		const int __user *, nodes,
 		int __user *, status, int, flags)
 {
-	const struct cred *cred = current_cred(), *tcred;
 	struct task_struct *task;
 	struct mm_struct *mm;
 	int err;
@@ -1689,14 +1689,9 @@
 
 	/*
 	 * Check if this process has the right to modify the specified
-	 * process. The right exists if the process has administrative
-	 * capabilities, superuser privileges or the same
-	 * userid as the target process.
+	 * process. Use the regular "ptrace_may_access()" checks.
 	 */
-	tcred = __task_cred(task);
-	if (!uid_eq(cred->euid, tcred->suid) && !uid_eq(cred->euid, tcred->uid) &&
-	    !uid_eq(cred->uid,  tcred->suid) && !uid_eq(cred->uid,  tcred->uid) &&
-	    !capable(CAP_SYS_NICE)) {
+	if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) {
 		rcu_read_unlock();
 		err = -EPERM;
 		goto out;
@@ -1738,9 +1733,6 @@
 {
 	int z;
 
-	if (!pgdat_reclaimable(pgdat))
-		return false;
-
 	for (z = pgdat->nr_zones - 1; z >= 0; z--) {
 		struct zone *zone = pgdat->node_zones + z;
 
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 2c40836..1f2c969 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -74,6 +74,7 @@
 	if (!pte)
 		return 0;
 
+	flush_tlb_batched_pending(vma->vm_mm);
 	arch_enter_lazy_mmu_mode();
 	do {
 		oldpte = *pte;
diff --git a/mm/mremap.c b/mm/mremap.c
index 30d7d24..1597671 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -142,6 +142,7 @@
 	new_ptl = pte_lockptr(mm, new_pmd);
 	if (new_ptl != old_ptl)
 		spin_lock_nested(new_ptl, SINGLE_DEPTH_NESTING);
+	flush_tlb_batched_pending(vma->vm_mm);
 	arch_enter_lazy_mmu_mode();
 
 	for (; old_addr < old_end; old_pte++, old_addr += PAGE_SIZE,
diff --git a/mm/nobootmem.c b/mm/nobootmem.c
index e1e8c63..aa59572 100644
--- a/mm/nobootmem.c
+++ b/mm/nobootmem.c
@@ -146,22 +146,6 @@
 				NULL)
 		count += __free_memory_core(start, end);
 
-#ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
-	{
-		phys_addr_t size;
-
-		/* Free memblock.reserved array if it was allocated */
-		size = get_allocated_memblock_reserved_regions_info(&start);
-		if (size)
-			count += __free_memory_core(start, start + size);
-
-		/* Free memblock.memory array if it was allocated */
-		size = get_allocated_memblock_memory_regions_info(&start);
-		if (size)
-			count += __free_memory_core(start, start + size);
-	}
-#endif
-
 	return count;
 }
 
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 3a22b14..1f13413 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -38,6 +38,7 @@
 #include <linux/kthread.h>
 #include <linux/init.h>
 #include <linux/show_mem_notifier.h>
+#include <linux/mmu_notifier.h>
 
 #include <asm/tlb.h>
 #include "internal.h"
@@ -495,6 +496,21 @@
 	}
 
 	/*
+	 * If the mm has notifiers then we would need to invalidate them around
+	 * unmap_page_range and that is risky because notifiers can sleep and
+	 * what they do is basically undeterministic.  So let's have a short
+	 * sleep to give the oom victim some more time.
+	 * TODO: we really want to get rid of this ugly hack and make sure that
+	 * notifiers cannot block for unbounded amount of time and add
+	 * mmu_notifier_invalidate_range_{start,end} around unmap_page_range
+	 */
+	if (mm_has_notifiers(mm)) {
+		up_read(&mm->mmap_sem);
+		schedule_timeout_idle(HZ);
+		goto unlock_oom;
+	}
+
+	/*
 	 * increase mm_users only after we know we will reap something so
 	 * that the mmput_async is called only when we have reaped something
 	 * and delayed __mmput doesn't matter that much
@@ -608,18 +624,23 @@
 	return 0;
 }
 
-static void wake_oom_reaper(struct task_struct *tsk)
+void wake_oom_reaper(struct task_struct *tsk)
 {
 	if (!oom_reaper_th)
 		return;
 
+	/* move the lock here to avoid scenario of queuing
+	 * the same task by both OOM killer and LMK.
+	 */
+	spin_lock(&oom_reaper_lock);
 	/* tsk is already queued? */
-	if (tsk == oom_reaper_list || tsk->oom_reaper_list)
+	if (tsk == oom_reaper_list || tsk->oom_reaper_list) {
+		spin_unlock(&oom_reaper_lock);
 		return;
+	}
 
 	get_task_struct(tsk);
 
-	spin_lock(&oom_reaper_lock);
 	tsk->oom_reaper_list = oom_reaper_list;
 	oom_reaper_list = tsk;
 	spin_unlock(&oom_reaper_lock);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 44085b2..acf411c 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1099,14 +1099,10 @@
 {
 	int migratetype = 0;
 	int batch_free = 0;
-	unsigned long nr_scanned;
 	bool isolated_pageblocks;
 
 	spin_lock(&zone->lock);
 	isolated_pageblocks = has_isolate_pageblock(zone);
-	nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
-	if (nr_scanned)
-		__mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
 
 	while (count) {
 		struct page *page;
@@ -1159,12 +1155,7 @@
 				unsigned int order,
 				int migratetype)
 {
-	unsigned long nr_scanned;
 	spin_lock(&zone->lock);
-	nr_scanned = node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED);
-	if (nr_scanned)
-		__mod_node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED, -nr_scanned);
-
 	if (unlikely(has_isolate_pageblock(zone) ||
 		is_migrate_isolate(migratetype))) {
 		migratetype = get_pfnblock_migratetype(page, pfn);
@@ -1588,6 +1579,10 @@
 	/* Reinit limits that are based on free pages after the kernel is up */
 	files_maxfiles_init();
 #endif
+#ifdef CONFIG_ARCH_DISCARD_MEMBLOCK
+	/* Discard memblock private memory */
+	memblock_discard();
+#endif
 
 	for_each_populated_zone(zone)
 		set_zone_contiguous(zone);
@@ -1874,14 +1869,14 @@
 #endif
 
 	for (page = start_page; page <= end_page;) {
-		/* Make sure we are not inadvertently changing nodes */
-		VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
-
 		if (!pfn_valid_within(page_to_pfn(page))) {
 			page++;
 			continue;
 		}
 
+		/* Make sure we are not inadvertently changing nodes */
+		VM_BUG_ON_PAGE(page_to_nid(page) != zone_to_nid(zone), page);
+
 		if (!PageBuddy(page)) {
 			page++;
 			continue;
@@ -3501,19 +3496,12 @@
 }
 
 /*
- * Maximum number of reclaim retries without any progress before OOM killer
- * is consider as the only way to move forward.
- */
-#define MAX_RECLAIM_RETRIES 16
-
-/*
  * Checks whether it makes sense to retry the reclaim to make a forward progress
  * for the given allocation request.
- * The reclaim feedback represented by did_some_progress (any progress during
- * the last reclaim round) and no_progress_loops (number of reclaim rounds without
- * any progress in a row) is considered as well as the reclaimable pages on the
- * applicable zone list (with a backoff mechanism which is a function of
- * no_progress_loops).
+ *
+ * We give up when we either have tried MAX_RECLAIM_RETRIES in a row
+ * without success, or when we couldn't even meet the watermark if we
+ * reclaimed all remaining pages on the LRU lists.
  *
  * Returns true if a retry is viable or false to enter the oom path.
  */
@@ -3556,13 +3544,11 @@
 		unsigned long reclaimable;
 
 		available = reclaimable = zone_reclaimable_pages(zone);
-		available -= DIV_ROUND_UP((*no_progress_loops) * available,
-					  MAX_RECLAIM_RETRIES);
 		available += zone_page_state_snapshot(zone, NR_FREE_PAGES);
 
 		/*
-		 * Would the allocation succeed if we reclaimed the whole
-		 * available?
+		 * Would the allocation succeed if we reclaimed all
+		 * reclaimable pages?
 		 */
 		if (__zone_watermark_ok(zone, order, min_wmark_pages(zone),
 				ac_classzone_idx(ac), alloc_flags, available)) {
@@ -4442,7 +4428,6 @@
 #endif
 			" writeback_tmp:%lukB"
 			" unstable:%lukB"
-			" pages_scanned:%lu"
 			" all_unreclaimable? %s"
 			"\n",
 			pgdat->node_id,
@@ -4465,8 +4450,8 @@
 #endif
 			K(node_page_state(pgdat, NR_WRITEBACK_TEMP)),
 			K(node_page_state(pgdat, NR_UNSTABLE_NFS)),
-			node_page_state(pgdat, NR_PAGES_SCANNED),
-			!pgdat_reclaimable(pgdat) ? "yes" : "no");
+			pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES ?
+				"yes" : "no");
 	}
 
 	for_each_populated_zone(zone) {
@@ -6544,8 +6529,8 @@
 	}
 
 	if (pages && s)
-		pr_info("Freeing %s memory: %ldK (%p - %p)\n",
-			s, pages << (PAGE_SHIFT - 10), start, end);
+		pr_info("Freeing %s memory: %ldK\n",
+			s, pages << (PAGE_SHIFT - 10));
 
 	return pages;
 }
@@ -7435,7 +7420,7 @@
 
 	/* Make sure the range is really isolated. */
 	if (test_pages_isolated(outer_start, end, false)) {
-		pr_info("%s: [%lx, %lx) PFNs busy\n",
+		pr_info_ratelimited("%s: [%lx, %lx) PFNs busy\n",
 			__func__, outer_start, end);
 		ret = -EBUSY;
 		goto done;
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 3c99874..c4381d93 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -143,7 +143,7 @@
 		.nr_entries = 0,
 		.entries = entries,
 		.max_entries = PAGE_OWNER_STACK_DEPTH,
-		.skip = 0
+		.skip = 2
 	};
 	depot_stack_handle_t handle;
 
diff --git a/mm/rmap.c b/mm/rmap.c
index dfb19f0..4d19dd1 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -617,6 +617,13 @@
 	tlb_ubc->flush_required = true;
 
 	/*
+	 * Ensure compiler does not re-order the setting of tlb_flush_batched
+	 * before the PTE is cleared.
+	 */
+	barrier();
+	mm->tlb_flush_batched = true;
+
+	/*
 	 * If the PTE was dirty then it's best to assume it's writable. The
 	 * caller must use try_to_unmap_flush_dirty() or try_to_unmap_flush()
 	 * before the page is queued for IO.
@@ -643,6 +650,35 @@
 
 	return should_defer;
 }
+
+/*
+ * Reclaim unmaps pages under the PTL but do not flush the TLB prior to
+ * releasing the PTL if TLB flushes are batched. It's possible for a parallel
+ * operation such as mprotect or munmap to race between reclaim unmapping
+ * the page and flushing the page. If this race occurs, it potentially allows
+ * access to data via a stale TLB entry. Tracking all mm's that have TLB
+ * batching in flight would be expensive during reclaim so instead track
+ * whether TLB batching occurred in the past and if so then do a flush here
+ * if required. This will cost one additional flush per reclaim cycle paid
+ * by the first operation at risk such as mprotect and mumap.
+ *
+ * This must be called under the PTL so that an access to tlb_flush_batched
+ * that is potentially a "reclaim vs mprotect/munmap/etc" race will synchronise
+ * via the PTL.
+ */
+void flush_tlb_batched_pending(struct mm_struct *mm)
+{
+	if (mm->tlb_flush_batched) {
+		flush_tlb_mm(mm);
+
+		/*
+		 * Do not allow the compiler to re-order the clearing of
+		 * tlb_flush_batched before the tlb is flushed.
+		 */
+		barrier();
+		mm->tlb_flush_batched = false;
+	}
+}
 #else
 static void set_tlb_ubc_flush_pending(struct mm_struct *mm,
 		struct page *page, bool writable)
diff --git a/mm/shmem.c b/mm/shmem.c
index 142887f..7a74b6d 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -1007,7 +1007,11 @@
 			 */
 			if (IS_ENABLED(CONFIG_TRANSPARENT_HUGE_PAGECACHE)) {
 				spin_lock(&sbinfo->shrinklist_lock);
-				if (list_empty(&info->shrinklist)) {
+				/*
+				 * _careful to defend against unlocked access to
+				 * ->shrink_list in shmem_unused_huge_shrink()
+				 */
+				if (list_empty_careful(&info->shrinklist)) {
 					list_add_tail(&info->shrinklist,
 							&sbinfo->shrinklist);
 					sbinfo->shrinklist_len++;
@@ -1774,7 +1778,11 @@
 			 * to shrink under memory pressure.
 			 */
 			spin_lock(&sbinfo->shrinklist_lock);
-			if (list_empty(&info->shrinklist)) {
+			/*
+			 * _careful to defend against unlocked access to
+			 * ->shrink_list in shmem_unused_huge_shrink()
+			 */
+			if (list_empty_careful(&info->shrinklist)) {
 				list_add_tail(&info->shrinklist,
 						&sbinfo->shrinklist);
 				sbinfo->shrinklist_len++;
@@ -3802,7 +3810,7 @@
 	}
 
 #ifdef CONFIG_TRANSPARENT_HUGE_PAGECACHE
-	if (has_transparent_hugepage() && shmem_huge < SHMEM_HUGE_DENY)
+	if (has_transparent_hugepage() && shmem_huge > SHMEM_HUGE_DENY)
 		SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge;
 	else
 		shmem_huge = 0; /* just in case it was patched */
@@ -3863,7 +3871,7 @@
 		return -EINVAL;
 
 	shmem_huge = huge;
-	if (shmem_huge < SHMEM_HUGE_DENY)
+	if (shmem_huge > SHMEM_HUGE_DENY)
 		SHMEM_SB(shm_mnt->mnt_sb)->huge = shmem_huge;
 	return count;
 }
diff --git a/mm/slab_common.c b/mm/slab_common.c
index 5d2f24f..622f6b6 100644
--- a/mm/slab_common.c
+++ b/mm/slab_common.c
@@ -255,7 +255,7 @@
 {
 	struct kmem_cache *s;
 
-	if (slab_nomerge || (flags & SLAB_NEVER_MERGE))
+	if (slab_nomerge)
 		return NULL;
 
 	if (ctor)
@@ -266,6 +266,9 @@
 	size = ALIGN(size, align);
 	flags = kmem_cache_flags(size, flags, name, NULL);
 
+	if (flags & SLAB_NEVER_MERGE)
+		return NULL;
+
 	list_for_each_entry_reverse(s, &slab_caches, list) {
 		if (slab_unmergeable(s))
 			continue;
diff --git a/mm/sparse.c b/mm/sparse.c
index 1e168bf..8c4c82e 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -662,7 +662,7 @@
 		>> PAGE_SHIFT;
 
 	for (i = 0; i < nr_pages; i++, page++) {
-		magic = (unsigned long) page->lru.next;
+		magic = (unsigned long) page->freelist;
 
 		BUG_ON(magic == NODE_INFO);
 
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7b5848cf..bb18b47 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -235,12 +235,6 @@
 	return nr;
 }
 
-bool pgdat_reclaimable(struct pglist_data *pgdat)
-{
-	return node_page_state_snapshot(pgdat, NR_PAGES_SCANNED) <
-		pgdat_reclaimable_pages(pgdat) * 6;
-}
-
 /**
  * lruvec_lru_size -  Returns the number of pages on the given LRU list.
  * @lruvec: lru vector
@@ -1495,7 +1489,7 @@
  *
  * Appropriate locks must be held before calling this function.
  *
- * @nr_to_scan:	The number of pages to look through on the list.
+ * @nr_to_scan:	The number of eligible pages to look through on the list.
  * @lruvec:	The LRU vector to pull pages from.
  * @dst:	The temp list to put pages on to.
  * @nr_scanned:	The number of pages that were scanned.
@@ -1514,11 +1508,14 @@
 	unsigned long nr_taken = 0;
 	unsigned long nr_zone_taken[MAX_NR_ZONES] = { 0 };
 	unsigned long nr_skipped[MAX_NR_ZONES] = { 0, };
-	unsigned long scan, nr_pages;
+	unsigned long skipped = 0;
+	unsigned long scan, total_scan, nr_pages;
 	LIST_HEAD(pages_skipped);
 
-	for (scan = 0; scan < nr_to_scan && nr_taken < nr_to_scan &&
-					!list_empty(src);) {
+	scan = 0;
+	for (total_scan = 0;
+	     scan < nr_to_scan && nr_taken < nr_to_scan && !list_empty(src);
+	     total_scan++) {
 		struct page *page;
 
 		page = lru_to_page(src);
@@ -1533,11 +1530,12 @@
 		}
 
 		/*
-		 * Account for scanned and skipped separetly to avoid the pgdat
-		 * being prematurely marked unreclaimable by pgdat_reclaimable.
+		 * Do not count skipped pages because that makes the function
+		 * return with no isolated pages if the LRU mostly contains
+		 * ineligible pages.  This causes the VM to not reclaim any
+		 * pages, triggering a premature OOM.
 		 */
 		scan++;
-
 		switch (__isolate_lru_page(page, mode)) {
 		case 0:
 			nr_pages = hpage_nr_pages(page);
@@ -1565,28 +1563,20 @@
 	 */
 	if (!list_empty(&pages_skipped)) {
 		int zid;
-		unsigned long total_skipped = 0;
 
+		list_splice(&pages_skipped, src);
 		for (zid = 0; zid < MAX_NR_ZONES; zid++) {
 			if (!nr_skipped[zid])
 				continue;
 
 			__count_zid_vm_events(PGSCAN_SKIP, zid, nr_skipped[zid]);
-			total_skipped += nr_skipped[zid];
+			skipped += nr_skipped[zid];
 		}
-
-		/*
-		 * Account skipped pages as a partial scan as the pgdat may be
-		 * close to unreclaimable. If the LRU list is empty, account
-		 * skipped pages as a full scan.
-		 */
-		scan += list_empty(src) ? total_skipped : total_skipped >> 2;
-
-		list_splice(&pages_skipped, src);
 	}
-	*nr_scanned = scan;
-	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan, scan,
-				    nr_taken, mode, is_file_lru(lru));
+	*nr_scanned = total_scan;
+	trace_mm_vmscan_lru_isolate(sc->reclaim_idx, sc->order, nr_to_scan,
+					total_scan, skipped, nr_taken, mode,
+					is_file_lru(lru));
 	update_lru_sizes(lruvec, lru, nr_zone_taken);
 	return nr_taken;
 }
@@ -1849,7 +1839,6 @@
 	reclaim_stat->recent_scanned[file] += nr_taken;
 
 	if (global_reclaim(sc)) {
-		__mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
 		if (current_is_kswapd())
 			__count_vm_events(PGSCAN_KSWAPD, nr_scanned);
 		else
@@ -2038,8 +2027,6 @@
 	__mod_node_page_state(pgdat, NR_ISOLATED_ANON + file, nr_taken);
 	reclaim_stat->recent_scanned[file] += nr_taken;
 
-	if (global_reclaim(sc))
-		__mod_node_page_state(pgdat, NR_PAGES_SCANNED, nr_scanned);
 	__count_vm_events(PGREFILL, nr_scanned);
 
 	spin_unlock_irq(&pgdat->lru_lock);
@@ -2199,30 +2186,8 @@
 	unsigned long anon_prio, file_prio;
 	enum scan_balance scan_balance;
 	unsigned long anon, file;
-	bool force_scan = false;
 	unsigned long ap, fp;
 	enum lru_list lru;
-	bool some_scanned;
-	int pass;
-
-	/*
-	 * If the zone or memcg is small, nr[l] can be 0.  This
-	 * results in no scanning on this priority and a potential
-	 * priority drop.  Global direct reclaim can go to the next
-	 * zone and tends to have no problems. Global kswapd is for
-	 * zone balancing and it needs to scan a minimum amount. When
-	 * reclaiming for a memcg, a priority drop can cause high
-	 * latencies, so it's better to scan a minimum amount there as
-	 * well.
-	 */
-	if (current_is_kswapd()) {
-		if (!pgdat_reclaimable(pgdat))
-			force_scan = true;
-		if (!mem_cgroup_online(memcg))
-			force_scan = true;
-	}
-	if (!global_reclaim(sc))
-		force_scan = true;
 
 	/* If we have no swap space, do not bother scanning anon pages. */
 	if (!sc->may_swap || mem_cgroup_get_nr_swap_pages(memcg) <= 0) {
@@ -2354,55 +2319,48 @@
 	fraction[1] = fp;
 	denominator = ap + fp + 1;
 out:
-	some_scanned = false;
-	/* Only use force_scan on second pass. */
-	for (pass = 0; !some_scanned && pass < 2; pass++) {
-		*lru_pages = 0;
-		for_each_evictable_lru(lru) {
-			int file = is_file_lru(lru);
-			unsigned long size;
-			unsigned long scan;
+	*lru_pages = 0;
+	for_each_evictable_lru(lru) {
+		int file = is_file_lru(lru);
+		unsigned long size;
+		unsigned long scan;
 
-			size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
-			scan = size >> sc->priority;
+		size = lruvec_lru_size(lruvec, lru, sc->reclaim_idx);
+		scan = size >> sc->priority;
+		/*
+		 * If the cgroup's already been deleted, make sure to
+		 * scrape out the remaining cache.
+		 */
+		if (!scan && !mem_cgroup_online(memcg))
+			scan = min(size, SWAP_CLUSTER_MAX);
 
-			if (!scan && pass && force_scan)
-				scan = min(size, SWAP_CLUSTER_MAX);
-
-			switch (scan_balance) {
-			case SCAN_EQUAL:
-				/* Scan lists relative to size */
-				break;
-			case SCAN_FRACT:
-				/*
-				 * Scan types proportional to swappiness and
-				 * their relative recent reclaim efficiency.
-				 */
-				scan = div64_u64(scan * fraction[file],
-							denominator);
-				break;
-			case SCAN_FILE:
-			case SCAN_ANON:
-				/* Scan one type exclusively */
-				if ((scan_balance == SCAN_FILE) != file) {
-					size = 0;
-					scan = 0;
-				}
-				break;
-			default:
-				/* Look ma, no brain */
-				BUG();
-			}
-
-			*lru_pages += size;
-			nr[lru] = scan;
-
+		switch (scan_balance) {
+		case SCAN_EQUAL:
+			/* Scan lists relative to size */
+			break;
+		case SCAN_FRACT:
 			/*
-			 * Skip the second pass and don't force_scan,
-			 * if we found something to scan.
+			 * Scan types proportional to swappiness and
+			 * their relative recent reclaim efficiency.
 			 */
-			some_scanned |= !!scan;
+			scan = div64_u64(scan * fraction[file],
+					 denominator);
+			break;
+		case SCAN_FILE:
+		case SCAN_ANON:
+			/* Scan one type exclusively */
+			if ((scan_balance == SCAN_FILE) != file) {
+				size = 0;
+				scan = 0;
+			}
+			break;
+		default:
+			/* Look ma, no brain */
+			BUG();
 		}
+
+		*lru_pages += size;
+		nr[lru] = scan;
 	}
 }
 
@@ -2704,6 +2662,15 @@
 	} while (should_continue_reclaim(pgdat, sc->nr_reclaimed - nr_reclaimed,
 					 sc->nr_scanned - nr_scanned, sc));
 
+	/*
+	 * Kswapd gives up on balancing particular nodes after too
+	 * many failures to reclaim anything from them and goes to
+	 * sleep. On reclaim progress, reset the failure counter. A
+	 * successful direct reclaim run will revive a dormant kswapd.
+	 */
+	if (reclaimable)
+		pgdat->kswapd_failures = 0;
+
 	return reclaimable;
 }
 
@@ -2778,10 +2745,6 @@
 						 GFP_KERNEL | __GFP_HARDWALL))
 				continue;
 
-			if (sc->priority != DEF_PRIORITY &&
-			    !pgdat_reclaimable(zone->zone_pgdat))
-				continue;	/* Let kswapd poll it */
-
 			/*
 			 * If we already have plenty of memory free for
 			 * compaction in this zone, don't free any more.
@@ -2918,7 +2881,7 @@
 	return 0;
 }
 
-static bool pfmemalloc_watermark_ok(pg_data_t *pgdat)
+static bool allow_direct_reclaim(pg_data_t *pgdat)
 {
 	struct zone *zone;
 	unsigned long pfmemalloc_reserve = 0;
@@ -2926,10 +2889,15 @@
 	int i;
 	bool wmark_ok;
 
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return true;
+
 	for (i = 0; i <= ZONE_NORMAL; i++) {
 		zone = &pgdat->node_zones[i];
-		if (!managed_zone(zone) ||
-		    pgdat_reclaimable_pages(pgdat) == 0)
+		if (!managed_zone(zone))
+			continue;
+
+		if (!zone_reclaimable_pages(zone))
 			continue;
 
 		pfmemalloc_reserve += min_wmark_pages(zone);
@@ -3006,7 +2974,7 @@
 
 		/* Throttle based on the first usable node */
 		pgdat = zone->zone_pgdat;
-		if (pfmemalloc_watermark_ok(pgdat))
+		if (allow_direct_reclaim(pgdat))
 			goto out;
 		break;
 	}
@@ -3028,14 +2996,14 @@
 	 */
 	if (!(gfp_mask & __GFP_FS)) {
 		wait_event_interruptible_timeout(pgdat->pfmemalloc_wait,
-			pfmemalloc_watermark_ok(pgdat), HZ);
+			allow_direct_reclaim(pgdat), HZ);
 
 		goto check_pending;
 	}
 
 	/* Throttle until kswapd wakes the process */
 	wait_event_killable(zone->zone_pgdat->pfmemalloc_wait,
-		pfmemalloc_watermark_ok(pgdat));
+		allow_direct_reclaim(pgdat));
 
 check_pending:
 	if (fatal_signal_pending(current))
@@ -3185,21 +3153,44 @@
 	} while (memcg);
 }
 
-static bool zone_balanced(struct zone *zone, int order, int classzone_idx)
+/*
+ * Returns true if there is an eligible zone balanced for the request order
+ * and classzone_idx
+ */
+static bool pgdat_balanced(pg_data_t *pgdat, int order, int classzone_idx)
 {
-	unsigned long mark = high_wmark_pages(zone);
+	int i;
+	unsigned long mark = -1;
+	struct zone *zone;
 
-	if (!zone_watermark_ok_safe(zone, order, mark, classzone_idx))
-		return false;
+	for (i = 0; i <= classzone_idx; i++) {
+		zone = pgdat->node_zones + i;
+
+		if (!managed_zone(zone))
+			continue;
+
+		mark = high_wmark_pages(zone);
+		if (zone_watermark_ok_safe(zone, order, mark, classzone_idx))
+			return true;
+	}
 
 	/*
-	 * If any eligible zone is balanced then the node is not considered
-	 * to be congested or dirty
+	 * If a node has no populated zone within classzone_idx, it does not
+	 * need balancing by definition. This can happen if a zone-restricted
+	 * allocation tries to wake a remote kswapd.
 	 */
-	clear_bit(PGDAT_CONGESTED, &zone->zone_pgdat->flags);
-	clear_bit(PGDAT_DIRTY, &zone->zone_pgdat->flags);
+	if (mark == -1)
+		return true;
 
-	return true;
+	return false;
+}
+
+/* Clear pgdat state for congested, dirty or under writeback. */
+static void clear_pgdat_congested(pg_data_t *pgdat)
+{
+	clear_bit(PGDAT_CONGESTED, &pgdat->flags);
+	clear_bit(PGDAT_DIRTY, &pgdat->flags);
+	clear_bit(PGDAT_WRITEBACK, &pgdat->flags);
 }
 
 /*
@@ -3210,11 +3201,9 @@
  */
 static bool prepare_kswapd_sleep(pg_data_t *pgdat, int order, int classzone_idx)
 {
-	int i;
-
 	/*
 	 * The throttled processes are normally woken up in balance_pgdat() as
-	 * soon as pfmemalloc_watermark_ok() is true. But there is a potential
+	 * soon as allow_direct_reclaim() is true. But there is a potential
 	 * race between when kswapd checks the watermarks and a process gets
 	 * throttled. There is also a potential race if processes get
 	 * throttled, kswapd wakes, a large process exits thereby balancing the
@@ -3228,17 +3217,16 @@
 	if (waitqueue_active(&pgdat->pfmemalloc_wait))
 		wake_up_all(&pgdat->pfmemalloc_wait);
 
-	for (i = 0; i <= classzone_idx; i++) {
-		struct zone *zone = pgdat->node_zones + i;
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return true;
 
-		if (!managed_zone(zone))
-			continue;
-
-		if (!zone_balanced(zone, order, classzone_idx))
-			return false;
+	if (pgdat_balanced(pgdat, order, classzone_idx)) {
+		clear_pgdat_congested(pgdat);
+		return true;
 	}
 
-	return true;
+	return false;
 }
 
 /*
@@ -3314,9 +3302,9 @@
 	count_vm_event(PAGEOUTRUN);
 
 	do {
+		unsigned long nr_reclaimed = sc.nr_reclaimed;
 		bool raise_priority = true;
 
-		sc.nr_reclaimed = 0;
 		sc.reclaim_idx = classzone_idx;
 
 		/*
@@ -3341,23 +3329,12 @@
 		}
 
 		/*
-		 * Only reclaim if there are no eligible zones. Check from
-		 * high to low zone as allocations prefer higher zones.
-		 * Scanning from low to high zone would allow congestion to be
-		 * cleared during a very small window when a small low
-		 * zone was balanced even under extreme pressure when the
-		 * overall node may be congested. Note that sc.reclaim_idx
-		 * is not used as buffer_heads_over_limit may have adjusted
-		 * it.
+		 * Only reclaim if there are no eligible zones. Note that
+		 * sc.reclaim_idx is not used as buffer_heads_over_limit may
+		 * have adjusted it.
 		 */
-		for (i = classzone_idx; i >= 0; i--) {
-			zone = pgdat->node_zones + i;
-			if (!managed_zone(zone))
-				continue;
-
-			if (zone_balanced(zone, sc.order, classzone_idx))
-				goto out;
-		}
+		if (pgdat_balanced(pgdat, sc.order, classzone_idx))
+			goto out;
 
 		/*
 		 * Do some background aging of the anon list, to give
@@ -3371,7 +3348,7 @@
 		 * If we're getting trouble reclaiming, start doing writepage
 		 * even in laptop mode.
 		 */
-		if (sc.priority < DEF_PRIORITY - 2 || !pgdat_reclaimable(pgdat))
+		if (sc.priority < DEF_PRIORITY - 2)
 			sc.may_writepage = 1;
 
 		/* Call soft limit reclaim before calling shrink_node. */
@@ -3395,7 +3372,7 @@
 		 * able to safely make forward progress. Wake them
 		 */
 		if (waitqueue_active(&pgdat->pfmemalloc_wait) &&
-				pfmemalloc_watermark_ok(pgdat))
+				allow_direct_reclaim(pgdat))
 			wake_up_all(&pgdat->pfmemalloc_wait);
 
 		/* Check if kswapd should be suspending */
@@ -3406,10 +3383,14 @@
 		 * Raise priority if scanning rate is too low or there was no
 		 * progress in reclaiming pages
 		 */
-		if (raise_priority || !sc.nr_reclaimed)
+		nr_reclaimed = sc.nr_reclaimed - nr_reclaimed;
+		if (raise_priority || !nr_reclaimed)
 			sc.priority--;
 	} while (sc.priority >= 1);
 
+	if (!sc.nr_reclaimed)
+		pgdat->kswapd_failures++;
+
 out:
 	/*
 	 * Return the order kswapd stopped reclaiming at as
@@ -3420,6 +3401,22 @@
 	return sc.order;
 }
 
+/*
+ * pgdat->kswapd_classzone_idx is the highest zone index that a recent
+ * allocation request woke kswapd for. When kswapd has not woken recently,
+ * the value is MAX_NR_ZONES which is not a valid index. This compares a
+ * given classzone and returns it or the highest classzone index kswapd
+ * was recently woke for.
+ */
+static enum zone_type kswapd_classzone_idx(pg_data_t *pgdat,
+					   enum zone_type classzone_idx)
+{
+	if (pgdat->kswapd_classzone_idx == MAX_NR_ZONES)
+		return classzone_idx;
+
+	return max(pgdat->kswapd_classzone_idx, classzone_idx);
+}
+
 static void kswapd_try_to_sleep(pg_data_t *pgdat, int alloc_order, int reclaim_order,
 				unsigned int classzone_idx)
 {
@@ -3431,7 +3428,13 @@
 
 	prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
 
-	/* Try to sleep for a short interval */
+	/*
+	 * Try to sleep for a short interval. Note that kcompactd will only be
+	 * woken if it is possible to sleep for a short interval. This is
+	 * deliberate on the assumption that if reclaim cannot keep an
+	 * eligible zone balanced that it's also unlikely that compaction will
+	 * succeed.
+	 */
 	if (prepare_kswapd_sleep(pgdat, reclaim_order, classzone_idx)) {
 		/*
 		 * Compaction records what page blocks it recently failed to
@@ -3455,7 +3458,7 @@
 		 * the previous request that slept prematurely.
 		 */
 		if (remaining) {
-			pgdat->kswapd_classzone_idx = max(pgdat->kswapd_classzone_idx, classzone_idx);
+			pgdat->kswapd_classzone_idx = kswapd_classzone_idx(pgdat, classzone_idx);
 			pgdat->kswapd_order = max(pgdat->kswapd_order, reclaim_order);
 		}
 
@@ -3509,7 +3512,8 @@
  */
 static int kswapd(void *p)
 {
-	unsigned int alloc_order, reclaim_order, classzone_idx;
+	unsigned int alloc_order, reclaim_order;
+	unsigned int classzone_idx = MAX_NR_ZONES - 1;
 	pg_data_t *pgdat = (pg_data_t*)p;
 	struct task_struct *tsk = current;
 
@@ -3539,20 +3543,23 @@
 	tsk->flags |= PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD;
 	set_freezable();
 
-	pgdat->kswapd_order = alloc_order = reclaim_order = 0;
-	pgdat->kswapd_classzone_idx = classzone_idx = 0;
+	pgdat->kswapd_order = 0;
+	pgdat->kswapd_classzone_idx = MAX_NR_ZONES;
 	for ( ; ; ) {
 		bool ret;
 
+		alloc_order = reclaim_order = pgdat->kswapd_order;
+		classzone_idx = kswapd_classzone_idx(pgdat, classzone_idx);
+
 kswapd_try_sleep:
 		kswapd_try_to_sleep(pgdat, alloc_order, reclaim_order,
 					classzone_idx);
 
 		/* Read the new order and classzone_idx */
 		alloc_order = reclaim_order = pgdat->kswapd_order;
-		classzone_idx = pgdat->kswapd_classzone_idx;
+		classzone_idx = kswapd_classzone_idx(pgdat, 0);
 		pgdat->kswapd_order = 0;
-		pgdat->kswapd_classzone_idx = 0;
+		pgdat->kswapd_classzone_idx = MAX_NR_ZONES;
 
 		ret = try_to_freeze();
 		if (kthread_should_stop())
@@ -3578,9 +3585,6 @@
 		reclaim_order = balance_pgdat(pgdat, alloc_order, classzone_idx);
 		if (reclaim_order < alloc_order)
 			goto kswapd_try_sleep;
-
-		alloc_order = reclaim_order = pgdat->kswapd_order;
-		classzone_idx = pgdat->kswapd_classzone_idx;
 	}
 
 	tsk->flags &= ~(PF_MEMALLOC | PF_SWAPWRITE | PF_KSWAPD);
@@ -3596,7 +3600,6 @@
 void wakeup_kswapd(struct zone *zone, int order, enum zone_type classzone_idx)
 {
 	pg_data_t *pgdat;
-	int z;
 
 	if (!managed_zone(zone))
 		return;
@@ -3604,22 +3607,20 @@
 	if (!cpuset_zone_allowed(zone, GFP_KERNEL | __GFP_HARDWALL))
 		return;
 	pgdat = zone->zone_pgdat;
-	pgdat->kswapd_classzone_idx = max(pgdat->kswapd_classzone_idx, classzone_idx);
+	pgdat->kswapd_classzone_idx = kswapd_classzone_idx(pgdat,
+							   classzone_idx);
 	pgdat->kswapd_order = max(pgdat->kswapd_order, order);
 	if (!waitqueue_active(&pgdat->kswapd_wait))
 		return;
 
-	/* Only wake kswapd if all zones are unbalanced */
-	for (z = 0; z <= classzone_idx; z++) {
-		zone = pgdat->node_zones + z;
-		if (!managed_zone(zone))
-			continue;
+	/* Hopeless node, leave it to direct reclaim */
+	if (pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES)
+		return;
 
-		if (zone_balanced(zone, order, classzone_idx))
-			return;
-	}
+	if (pgdat_balanced(pgdat, order, classzone_idx))
+		return;
 
-	trace_mm_vmscan_wakeup_kswapd(pgdat->node_id, zone_idx(zone), order);
+	trace_mm_vmscan_wakeup_kswapd(pgdat->node_id, classzone_idx, order);
 	wake_up_interruptible(&pgdat->kswapd_wait);
 }
 
@@ -3879,9 +3880,6 @@
 	    sum_zone_node_page_state(pgdat->node_id, NR_SLAB_RECLAIMABLE) <= pgdat->min_slab_pages)
 		return NODE_RECLAIM_FULL;
 
-	if (!pgdat_reclaimable(pgdat))
-		return NODE_RECLAIM_FULL;
-
 	/*
 	 * Do not scan if the allocation should not be delayed.
 	 */
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 513c37a..2ab7973 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -954,7 +954,6 @@
 	"nr_unevictable",
 	"nr_isolated_anon",
 	"nr_isolated_file",
-	"nr_pages_scanned",
 	"workingset_refault",
 	"workingset_activate",
 	"workingset_nodereclaim",
@@ -1379,7 +1378,6 @@
 		   "\n        min      %lu"
 		   "\n        low      %lu"
 		   "\n        high     %lu"
-		   "\n   node_scanned  %lu"
 		   "\n        spanned  %lu"
 		   "\n        present  %lu"
 		   "\n        managed  %lu",
@@ -1387,7 +1385,6 @@
 		   min_wmark_pages(zone),
 		   low_wmark_pages(zone),
 		   high_wmark_pages(zone),
-		   node_page_state(zone->zone_pgdat, NR_PAGES_SCANNED),
 		   zone->spanned_pages,
 		   zone->present_pages,
 		   zone->managed_pages);
@@ -1426,7 +1423,7 @@
 		   "\n  node_unreclaimable:  %u"
 		   "\n  start_pfn:           %lu"
 		   "\n  node_inactive_ratio: %u",
-		   !pgdat_reclaimable(zone->zone_pgdat),
+		   pgdat->kswapd_failures >= MAX_RECLAIM_RETRIES,
 		   zone->zone_start_pfn,
 		   zone->zone_pgdat->inactive_ratio);
 	seq_putc(m, '\n');
@@ -1588,22 +1585,9 @@
 	for (i = 0; i < NR_VM_ZONE_STAT_ITEMS; i++) {
 		val = atomic_long_read(&vm_zone_stat[i]);
 		if (val < 0) {
-			switch (i) {
-			case NR_PAGES_SCANNED:
-				/*
-				 * This is often seen to go negative in
-				 * recent kernels, but not to go permanently
-				 * negative.  Whilst it would be nicer not to
-				 * have exceptions, rooting them out would be
-				 * another task, of rather low priority.
-				 */
-				break;
-			default:
-				pr_warn("%s: %s %ld\n",
-					__func__, vmstat_text[i], val);
-				err = -EINVAL;
-				break;
-			}
+			pr_warn("%s: %s %ld\n",
+				__func__, vmstat_text[i], val);
+			err = -EINVAL;
 		}
 	}
 	if (err)
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index fbf251f..4d6b94d 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -484,16 +484,16 @@
 	struct net_device *dev = s->dev;
 	struct sock *sk = s->sock->sk;
 	struct sk_buff *skb;
-	wait_queue_t wait;
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 
 	BT_DBG("");
 
 	set_user_nice(current, -15);
 
-	init_waitqueue_entry(&wait, current);
 	add_wait_queue(sk_sleep(sk), &wait);
 	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
+		/* Ensure session->terminate is updated */
+		smp_mb__before_atomic();
 
 		if (atomic_read(&s->terminate))
 			break;
@@ -515,9 +515,8 @@
 				break;
 		netif_wake_queue(dev);
 
-		schedule();
+		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 	}
-	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
 
 	/* Cleanup session */
@@ -666,7 +665,7 @@
 	s = __bnep_get_session(req->dst);
 	if (s) {
 		atomic_inc(&s->terminate);
-		wake_up_process(s->task);
+		wake_up_interruptible(sk_sleep(s->sock->sk));
 	} else
 		err = -ENOENT;
 
diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
index 9e59b66..1152ce3 100644
--- a/net/bluetooth/cmtp/core.c
+++ b/net/bluetooth/cmtp/core.c
@@ -280,16 +280,16 @@
 	struct cmtp_session *session = arg;
 	struct sock *sk = session->sock->sk;
 	struct sk_buff *skb;
-	wait_queue_t wait;
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 
 	BT_DBG("session %p", session);
 
 	set_user_nice(current, -15);
 
-	init_waitqueue_entry(&wait, current);
 	add_wait_queue(sk_sleep(sk), &wait);
 	while (1) {
-		set_current_state(TASK_INTERRUPTIBLE);
+		/* Ensure session->terminate is updated */
+		smp_mb__before_atomic();
 
 		if (atomic_read(&session->terminate))
 			break;
@@ -306,9 +306,8 @@
 
 		cmtp_process_transmit(session);
 
-		schedule();
+		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 	}
-	__set_current_state(TASK_RUNNING);
 	remove_wait_queue(sk_sleep(sk), &wait);
 
 	down_write(&cmtp_session_sem);
@@ -393,7 +392,7 @@
 		err = cmtp_attach_device(session);
 		if (err < 0) {
 			atomic_inc(&session->terminate);
-			wake_up_process(session->task);
+			wake_up_interruptible(sk_sleep(session->sock->sk));
 			up_write(&cmtp_session_sem);
 			return err;
 		}
@@ -431,7 +430,11 @@
 
 		/* Stop session thread */
 		atomic_inc(&session->terminate);
-		wake_up_process(session->task);
+
+		/* Ensure session->terminate is updated */
+		smp_mb__after_atomic();
+
+		wake_up_interruptible(sk_sleep(session->sock->sk));
 	} else
 		err = -ENOENT;
 
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 0bec458..1fc0764 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -36,6 +36,7 @@
 #define VERSION "1.2"
 
 static DECLARE_RWSEM(hidp_session_sem);
+static DECLARE_WAIT_QUEUE_HEAD(hidp_session_wq);
 static LIST_HEAD(hidp_session_list);
 
 static unsigned char hidp_keycode[256] = {
@@ -1068,12 +1069,12 @@
  * Wake up session thread and notify it to stop. This is asynchronous and
  * returns immediately. Call this whenever a runtime error occurs and you want
  * the session to stop.
- * Note: wake_up_process() performs any necessary memory-barriers for us.
+ * Note: wake_up_interruptible() performs any necessary memory-barriers for us.
  */
 static void hidp_session_terminate(struct hidp_session *session)
 {
 	atomic_inc(&session->terminate);
-	wake_up_process(session->task);
+	wake_up_interruptible(&hidp_session_wq);
 }
 
 /*
@@ -1180,7 +1181,9 @@
 	struct sock *ctrl_sk = session->ctrl_sock->sk;
 	struct sock *intr_sk = session->intr_sock->sk;
 	struct sk_buff *skb;
+	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 
+	add_wait_queue(&hidp_session_wq, &wait);
 	for (;;) {
 		/*
 		 * This thread can be woken up two ways:
@@ -1188,12 +1191,10 @@
 		 *    session->terminate flag and wakes this thread up.
 		 *  - Via modifying the socket state of ctrl/intr_sock. This
 		 *    thread is woken up by ->sk_state_changed().
-		 *
-		 * Note: set_current_state() performs any necessary
-		 * memory-barriers for us.
 		 */
-		set_current_state(TASK_INTERRUPTIBLE);
 
+		/* Ensure session->terminate is updated */
+		smp_mb__before_atomic();
 		if (atomic_read(&session->terminate))
 			break;
 
@@ -1227,11 +1228,22 @@
 		hidp_process_transmit(session, &session->ctrl_transmit,
 				      session->ctrl_sock);
 
-		schedule();
+		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
 	}
+	remove_wait_queue(&hidp_session_wq, &wait);
 
 	atomic_inc(&session->terminate);
-	set_current_state(TASK_RUNNING);
+
+	/* Ensure session->terminate is updated */
+	smp_mb__after_atomic();
+}
+
+static int hidp_session_wake_function(wait_queue_t *wait,
+				      unsigned int mode,
+				      int sync, void *key)
+{
+	wake_up_interruptible(&hidp_session_wq);
+	return false;
 }
 
 /*
@@ -1244,7 +1256,8 @@
 static int hidp_session_thread(void *arg)
 {
 	struct hidp_session *session = arg;
-	wait_queue_t ctrl_wait, intr_wait;
+	DEFINE_WAIT_FUNC(ctrl_wait, hidp_session_wake_function);
+	DEFINE_WAIT_FUNC(intr_wait, hidp_session_wake_function);
 
 	BT_DBG("session %p", session);
 
@@ -1254,8 +1267,6 @@
 	set_user_nice(current, -15);
 	hidp_set_timer(session);
 
-	init_waitqueue_entry(&ctrl_wait, current);
-	init_waitqueue_entry(&intr_wait, current);
 	add_wait_queue(sk_sleep(session->ctrl_sock->sk), &ctrl_wait);
 	add_wait_queue(sk_sleep(session->intr_sock->sk), &intr_wait);
 	/* This memory barrier is paired with wq_has_sleeper(). See
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 577f1c0..ffd09c1 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -58,7 +58,7 @@
 				       u8 code, u8 ident, u16 dlen, void *data);
 static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
 			   void *data);
-static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data);
+static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size);
 static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err);
 
 static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control,
@@ -1473,7 +1473,7 @@
 
 			set_bit(CONF_REQ_SENT, &chan->conf_state);
 			l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-				       l2cap_build_conf_req(chan, buf), buf);
+				       l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
 			chan->num_conf_req++;
 		}
 
@@ -2977,12 +2977,15 @@
 	return len;
 }
 
-static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
+static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val, size_t size)
 {
 	struct l2cap_conf_opt *opt = *ptr;
 
 	BT_DBG("type 0x%2.2x len %u val 0x%lx", type, len, val);
 
+	if (size < L2CAP_CONF_OPT_SIZE + len)
+		return;
+
 	opt->type = type;
 	opt->len  = len;
 
@@ -3007,7 +3010,7 @@
 	*ptr += L2CAP_CONF_OPT_SIZE + len;
 }
 
-static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan)
+static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan, size_t size)
 {
 	struct l2cap_conf_efs efs;
 
@@ -3035,7 +3038,7 @@
 	}
 
 	l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs),
-			   (unsigned long) &efs);
+			   (unsigned long) &efs, size);
 }
 
 static void l2cap_ack_timeout(struct work_struct *work)
@@ -3181,11 +3184,12 @@
 	chan->ack_win = chan->tx_win;
 }
 
-static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data)
+static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
 {
 	struct l2cap_conf_req *req = data;
 	struct l2cap_conf_rfc rfc = { .mode = chan->mode };
 	void *ptr = req->data;
+	void *endptr = data + data_size;
 	u16 size;
 
 	BT_DBG("chan %p", chan);
@@ -3210,7 +3214,7 @@
 
 done:
 	if (chan->imtu != L2CAP_DEFAULT_MTU)
-		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
+		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
 
 	switch (chan->mode) {
 	case L2CAP_MODE_BASIC:
@@ -3229,7 +3233,7 @@
 		rfc.max_pdu_size    = 0;
 
 		l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
-				   (unsigned long) &rfc);
+				   (unsigned long) &rfc, endptr - ptr);
 		break;
 
 	case L2CAP_MODE_ERTM:
@@ -3249,21 +3253,21 @@
 				       L2CAP_DEFAULT_TX_WINDOW);
 
 		l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
-				   (unsigned long) &rfc);
+				   (unsigned long) &rfc, endptr - ptr);
 
 		if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
-			l2cap_add_opt_efs(&ptr, chan);
+			l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
 
 		if (test_bit(FLAG_EXT_CTRL, &chan->flags))
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
-					   chan->tx_win);
+					   chan->tx_win, endptr - ptr);
 
 		if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
 			if (chan->fcs == L2CAP_FCS_NONE ||
 			    test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
 				chan->fcs = L2CAP_FCS_NONE;
 				l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
-						   chan->fcs);
+						   chan->fcs, endptr - ptr);
 			}
 		break;
 
@@ -3281,17 +3285,17 @@
 		rfc.max_pdu_size = cpu_to_le16(size);
 
 		l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
-				   (unsigned long) &rfc);
+				   (unsigned long) &rfc, endptr - ptr);
 
 		if (test_bit(FLAG_EFS_ENABLE, &chan->flags))
-			l2cap_add_opt_efs(&ptr, chan);
+			l2cap_add_opt_efs(&ptr, chan, endptr - ptr);
 
 		if (chan->conn->feat_mask & L2CAP_FEAT_FCS)
 			if (chan->fcs == L2CAP_FCS_NONE ||
 			    test_bit(CONF_RECV_NO_FCS, &chan->conf_state)) {
 				chan->fcs = L2CAP_FCS_NONE;
 				l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1,
-						   chan->fcs);
+						   chan->fcs, endptr - ptr);
 			}
 		break;
 	}
@@ -3302,10 +3306,11 @@
 	return ptr - data;
 }
 
-static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
+static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data, size_t data_size)
 {
 	struct l2cap_conf_rsp *rsp = data;
 	void *ptr = rsp->data;
+	void *endptr = data + data_size;
 	void *req = chan->conf_req;
 	int len = chan->conf_len;
 	int type, hint, olen;
@@ -3407,7 +3412,7 @@
 			return -ECONNREFUSED;
 
 		l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
-				   (unsigned long) &rfc);
+				   (unsigned long) &rfc, endptr - ptr);
 	}
 
 	if (result == L2CAP_CONF_SUCCESS) {
@@ -3420,7 +3425,7 @@
 			chan->omtu = mtu;
 			set_bit(CONF_MTU_DONE, &chan->conf_state);
 		}
-		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu);
+		l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu, endptr - ptr);
 
 		if (remote_efs) {
 			if (chan->local_stype != L2CAP_SERV_NOTRAFIC &&
@@ -3434,7 +3439,7 @@
 
 				l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
 						   sizeof(efs),
-						   (unsigned long) &efs);
+						   (unsigned long) &efs, endptr - ptr);
 			} else {
 				/* Send PENDING Conf Rsp */
 				result = L2CAP_CONF_PENDING;
@@ -3467,7 +3472,7 @@
 			set_bit(CONF_MODE_DONE, &chan->conf_state);
 
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
-					   sizeof(rfc), (unsigned long) &rfc);
+					   sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
 
 			if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) {
 				chan->remote_id = efs.id;
@@ -3481,7 +3486,7 @@
 					le32_to_cpu(efs.sdu_itime);
 				l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS,
 						   sizeof(efs),
-						   (unsigned long) &efs);
+						   (unsigned long) &efs, endptr - ptr);
 			}
 			break;
 
@@ -3495,7 +3500,7 @@
 			set_bit(CONF_MODE_DONE, &chan->conf_state);
 
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
-					   (unsigned long) &rfc);
+					   (unsigned long) &rfc, endptr - ptr);
 
 			break;
 
@@ -3517,10 +3522,11 @@
 }
 
 static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len,
-				void *data, u16 *result)
+				void *data, size_t size, u16 *result)
 {
 	struct l2cap_conf_req *req = data;
 	void *ptr = req->data;
+	void *endptr = data + size;
 	int type, olen;
 	unsigned long val;
 	struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC };
@@ -3538,13 +3544,13 @@
 				chan->imtu = L2CAP_DEFAULT_MIN_MTU;
 			} else
 				chan->imtu = val;
-			l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu);
+			l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->imtu, endptr - ptr);
 			break;
 
 		case L2CAP_CONF_FLUSH_TO:
 			chan->flush_to = val;
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
-					   2, chan->flush_to);
+					   2, chan->flush_to, endptr - ptr);
 			break;
 
 		case L2CAP_CONF_RFC:
@@ -3558,13 +3564,13 @@
 			chan->fcs = 0;
 
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
-					   sizeof(rfc), (unsigned long) &rfc);
+					   sizeof(rfc), (unsigned long) &rfc, endptr - ptr);
 			break;
 
 		case L2CAP_CONF_EWS:
 			chan->ack_win = min_t(u16, val, chan->ack_win);
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2,
-					   chan->tx_win);
+					   chan->tx_win, endptr - ptr);
 			break;
 
 		case L2CAP_CONF_EFS:
@@ -3577,7 +3583,7 @@
 				return -ECONNREFUSED;
 
 			l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs),
-					   (unsigned long) &efs);
+					   (unsigned long) &efs, endptr - ptr);
 			break;
 
 		case L2CAP_CONF_FCS:
@@ -3682,7 +3688,7 @@
 		return;
 
 	l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-		       l2cap_build_conf_req(chan, buf), buf);
+		       l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
 	chan->num_conf_req++;
 }
 
@@ -3890,7 +3896,7 @@
 		u8 buf[128];
 		set_bit(CONF_REQ_SENT, &chan->conf_state);
 		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-			       l2cap_build_conf_req(chan, buf), buf);
+			       l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
 		chan->num_conf_req++;
 	}
 
@@ -3968,7 +3974,7 @@
 			break;
 
 		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-			       l2cap_build_conf_req(chan, req), req);
+			       l2cap_build_conf_req(chan, req, sizeof(req)), req);
 		chan->num_conf_req++;
 		break;
 
@@ -4080,7 +4086,7 @@
 	}
 
 	/* Complete config. */
-	len = l2cap_parse_conf_req(chan, rsp);
+	len = l2cap_parse_conf_req(chan, rsp, sizeof(rsp));
 	if (len < 0) {
 		l2cap_send_disconn_req(chan, ECONNRESET);
 		goto unlock;
@@ -4114,7 +4120,7 @@
 	if (!test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) {
 		u8 buf[64];
 		l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
-			       l2cap_build_conf_req(chan, buf), buf);
+			       l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
 		chan->num_conf_req++;
 	}
 
@@ -4174,7 +4180,7 @@
 			char buf[64];
 
 			len = l2cap_parse_conf_rsp(chan, rsp->data, len,
-						   buf, &result);
+						   buf, sizeof(buf), &result);
 			if (len < 0) {
 				l2cap_send_disconn_req(chan, ECONNRESET);
 				goto done;
@@ -4204,7 +4210,7 @@
 			/* throw out any old stored conf requests */
 			result = L2CAP_CONF_SUCCESS;
 			len = l2cap_parse_conf_rsp(chan, rsp->data, len,
-						   req, &result);
+						   req, sizeof(req), &result);
 			if (len < 0) {
 				l2cap_send_disconn_req(chan, ECONNRESET);
 				goto done;
@@ -4781,7 +4787,7 @@
 			set_bit(CONF_REQ_SENT, &chan->conf_state);
 			l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn),
 				       L2CAP_CONF_REQ,
-				       l2cap_build_conf_req(chan, buf), buf);
+				       l2cap_build_conf_req(chan, buf, sizeof(buf)), buf);
 			chan->num_conf_req++;
 		}
 	}
@@ -7457,7 +7463,7 @@
 				set_bit(CONF_REQ_SENT, &chan->conf_state);
 				l2cap_send_cmd(conn, l2cap_get_ident(conn),
 					       L2CAP_CONF_REQ,
-					       l2cap_build_conf_req(chan, buf),
+					       l2cap_build_conf_req(chan, buf, sizeof(buf)),
 					       buf);
 				chan->num_conf_req++;
 			}
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index fcaa484..04eea2f 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -48,6 +48,9 @@
 		return NETDEV_TX_OK;
 	}
 
+#ifdef CONFIG_NET_SWITCHDEV
+	skb->offload_fwd_mark = 0;
+#endif
 	BR_INPUT_SKB_CB(skb)->brdev = dev;
 
 	skb_reset_mac_header(skb);
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index ed0dd33..9218931 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -655,3 +655,34 @@
 	if (mask & BR_AUTO_MASK)
 		nbp_update_port_count(br);
 }
+
+/* br_port_dev_get()
+ * Using the given addr, identify the port to which it is reachable,
+ * returing a reference to the net device associated with that port.
+ *
+ * NOTE: Return NULL if given dev is not a bridge or
+ *       the mac has no associated port
+ */
+struct net_device *br_port_dev_get(struct net_device *dev, unsigned char *addr)
+{
+	struct net_bridge_fdb_entry *fdbe;
+	struct net_bridge *br;
+	struct net_device *netdev = NULL;
+
+	/* Is this a bridge? */
+	if (!(dev->priv_flags & IFF_EBRIDGE))
+		return NULL;
+
+	br = netdev_priv(dev);
+
+	/* Lookup the fdb entry and get reference to the port dev */
+	rcu_read_lock();
+	fdbe = __br_fdb_get(br, addr, 0);
+	if (fdbe && fdbe->dst) {
+		netdev = fdbe->dst->dev; /* port device */
+		dev_hold(netdev);
+	}
+	rcu_read_unlock();
+	return netdev;
+}
+EXPORT_SYMBOL(br_port_dev_get);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 7625ec8..5d4006e 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -1098,11 +1098,14 @@
 		spin_unlock_bh(&br->lock);
 	}
 
-	err = br_changelink(dev, tb, data);
+	err = register_netdevice(dev);
 	if (err)
 		return err;
 
-	return register_netdevice(dev);
+	err = br_changelink(dev, tb, data);
+	if (err)
+		unregister_netdevice(dev);
+	return err;
 }
 
 static size_t br_get_size(const struct net_device *brdev)
diff --git a/net/compat.c b/net/compat.c
index 1cd2ec0..a96fd2f 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -22,6 +22,7 @@
 #include <linux/filter.h>
 #include <linux/compat.h>
 #include <linux/security.h>
+#include <linux/audit.h>
 #include <linux/export.h>
 
 #include <net/scm.h>
@@ -781,14 +782,24 @@
 
 COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args)
 {
-	int ret;
-	u32 a[6];
+	u32 a[AUDITSC_ARGS];
+	unsigned int len;
 	u32 a0, a1;
+	int ret;
 
 	if (call < SYS_SOCKET || call > SYS_SENDMMSG)
 		return -EINVAL;
-	if (copy_from_user(a, args, nas[call]))
+	len = nas[call];
+	if (len > sizeof(a))
+		return -EINVAL;
+
+	if (copy_from_user(a, args, len))
 		return -EFAULT;
+
+	ret = audit_socketcall_compat(len / sizeof(a[0]), a);
+	if (ret)
+		return ret;
+
 	a0 = a[0];
 	a1 = a[1];
 
diff --git a/net/core/datagram.c b/net/core/datagram.c
index 58dfa23..4fa4011 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -351,7 +351,7 @@
 	if (flags & MSG_PEEK) {
 		err = -ENOENT;
 		spin_lock_bh(&sk->sk_receive_queue.lock);
-		if (skb == skb_peek(&sk->sk_receive_queue)) {
+		if (skb->next) {
 			__skb_unlink(skb, &sk->sk_receive_queue);
 			atomic_dec(&skb->users);
 			err = 0;
diff --git a/net/core/dev.c b/net/core/dev.c
index 610e5f8..18de74e 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2357,6 +2357,9 @@
 {
 	unsigned long flags;
 
+	if (unlikely(!skb))
+		return;
+
 	if (likely(atomic_read(&skb->users) == 1)) {
 		smp_rmb();
 		atomic_set(&skb->users, 0);
@@ -2705,7 +2708,7 @@
 {
 	if (tx_path)
 		return skb->ip_summed != CHECKSUM_PARTIAL &&
-		       skb->ip_summed != CHECKSUM_NONE;
+		       skb->ip_summed != CHECKSUM_UNNECESSARY;
 
 	return skb->ip_summed == CHECKSUM_NONE;
 }
@@ -2949,6 +2952,37 @@
 	return rc;
 }
 
+static int xmit_list(struct sk_buff *list, struct net_device *dev,
+		     struct netdev_queue *txq)
+{
+	unsigned int len;
+	int rc;
+	struct sk_buff *skb = list, *head = list;
+
+	/* Call the taps for individual skb's in the list. */
+	if (!list_empty(&ptype_all)) {
+		while (skb) {
+			struct sk_buff *next = skb->next;
+
+			skb->next = NULL;
+
+			dev_queue_xmit_nit(skb, dev);
+
+			skb = next;
+			/* Keep the original list intact. */
+			head->next = skb;
+			head = head->next;
+		}
+	}
+
+	len = list->len;
+	trace_net_dev_start_xmit(list, dev);
+	rc = netdev_start_xmit(list, dev, txq, false);
+	trace_net_dev_xmit(list, rc, dev, len);
+
+	return rc;
+}
+
 struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
 				    struct netdev_queue *txq, int *ret)
 {
@@ -2977,6 +3011,25 @@
 	return skb;
 }
 
+struct sk_buff *dev_hard_start_xmit_list(struct sk_buff *first,
+					 struct net_device *dev,
+					struct netdev_queue *txq, int *ret)
+{
+	struct sk_buff *skb = first;
+	int rc = NETDEV_TX_OK;
+
+	if (skb) {
+		rc = xmit_list(skb, dev, txq);
+		if (unlikely(!dev_xmit_complete(rc)))
+			goto out;
+		skb = NULL;
+	}
+
+out:
+	*ret = rc;
+	return skb;
+}
+
 static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
 					  netdev_features_t features)
 {
@@ -3327,6 +3380,7 @@
  *	__dev_queue_xmit - transmit a buffer
  *	@skb: buffer to transmit
  *	@accel_priv: private data used for L2 forwarding offload
+ *	@skb_list: Boolean used for skb list processing.
  *
  *	Queue a buffer for transmission to a network device. The caller must
  *	have set the device and priority and built the buffer before calling
@@ -3349,7 +3403,8 @@
  *      the BH enable code must have IRQs enabled so that it will not deadlock.
  *          --BLG
  */
-static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv)
+static int __dev_queue_xmit(struct sk_buff *skb, void *accel_priv,
+			    bool skb_list)
 {
 	struct net_device *dev = skb->dev;
 	struct netdev_queue *txq;
@@ -3424,7 +3479,14 @@
 
 			if (!netif_xmit_stopped(txq)) {
 				__this_cpu_inc(xmit_recursion);
-				skb = dev_hard_start_xmit(skb, dev, txq, &rc);
+				if (likely(!skb_list))
+					skb = dev_hard_start_xmit(skb, dev,
+								  txq, &rc);
+				else
+					skb = dev_hard_start_xmit_list(skb,
+								       dev,
+								       txq,
+								       &rc);
 				__this_cpu_dec(xmit_recursion);
 				if (dev_xmit_complete(rc)) {
 					HARD_TX_UNLOCK(dev, txq);
@@ -3457,16 +3519,22 @@
 
 int dev_queue_xmit(struct sk_buff *skb)
 {
-	return __dev_queue_xmit(skb, NULL);
+	return __dev_queue_xmit(skb, NULL, false);
 }
 EXPORT_SYMBOL(dev_queue_xmit);
 
 int dev_queue_xmit_accel(struct sk_buff *skb, void *accel_priv)
 {
-	return __dev_queue_xmit(skb, accel_priv);
+	return __dev_queue_xmit(skb, accel_priv, false);
 }
 EXPORT_SYMBOL(dev_queue_xmit_accel);
 
+int dev_queue_xmit_list(struct sk_buff *skb)
+{
+	return __dev_queue_xmit(skb, NULL, true);
+}
+EXPORT_SYMBOL(dev_queue_xmit_list);
+
 
 /*=======================================================================
 			Receiver routines
@@ -4103,6 +4171,12 @@
 	return 0;
 }
 
+int (*athrs_fast_nat_recv)(struct sk_buff *skb) __rcu __read_mostly;
+EXPORT_SYMBOL(athrs_fast_nat_recv);
+
+int (*embms_tm_multicast_recv)(struct sk_buff *skb) __rcu __read_mostly;
+EXPORT_SYMBOL(embms_tm_multicast_recv);
+
 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
 {
 	struct packet_type *ptype, *pt_prev;
@@ -4111,6 +4185,8 @@
 	bool deliver_exact = false;
 	int ret = NET_RX_DROP;
 	__be16 type;
+	int (*fast_recv)(struct sk_buff *skb);
+	int (*embms_recv)(struct sk_buff *skb);
 
 	net_timestamp_check(!netdev_tstamp_prequeue, skb);
 
@@ -4170,6 +4246,18 @@
 			goto out;
 	}
 #endif
+	fast_recv = rcu_dereference(athrs_fast_nat_recv);
+	if (fast_recv) {
+		if (fast_recv(skb)) {
+			ret = NET_RX_SUCCESS;
+			goto out;
+		}
+	}
+
+	embms_recv = rcu_dereference(embms_tm_multicast_recv);
+	if (embms_recv)
+		embms_recv(skb);
+
 #ifdef CONFIG_NET_CLS_ACT
 	skb->tc_verd = 0;
 ncls:
@@ -4843,6 +4931,24 @@
 }
 EXPORT_SYMBOL(__skb_gro_checksum_complete);
 
+static void net_rps_send_ipi(struct softnet_data *remsd)
+{
+#ifdef CONFIG_RPS
+	while (remsd) {
+		struct softnet_data *next = remsd->rps_ipi_next;
+
+		if (cpu_online(remsd->cpu)) {
+			smp_call_function_single_async(remsd->cpu, &remsd->csd);
+		} else {
+			rps_lock(remsd);
+			remsd->backlog.state = 0;
+			rps_unlock(remsd);
+		}
+		remsd = next;
+	}
+#endif
+}
+
 /*
  * net_rps_action_and_irq_enable sends any pending IPI's for rps.
  * Note: called with local irq disabled, but exits with local irq enabled.
@@ -4858,20 +4964,7 @@
 		local_irq_enable();
 
 		/* Send pending IPI's to kick RPS processing on remote cpus. */
-		while (remsd) {
-			struct softnet_data *next = remsd->rps_ipi_next;
-
-			if (cpu_online(remsd->cpu)) {
-				smp_call_function_single_async(remsd->cpu,
-							   &remsd->csd);
-			} else {
-				pr_err("%s() cpu offline\n", __func__);
-				rps_lock(remsd);
-				remsd->backlog.state = 0;
-				rps_unlock(remsd);
-			}
-			remsd = next;
-		}
+		net_rps_send_ipi(remsd);
 	} else
 #endif
 		local_irq_enable();
@@ -5367,12 +5460,13 @@
  * Find out if a device is linked to an upper device and return true in case
  * it is. The caller must hold the RTNL lock.
  */
-static bool netdev_has_any_upper_dev(struct net_device *dev)
+bool netdev_has_any_upper_dev(struct net_device *dev)
 {
 	ASSERT_RTNL();
 
 	return !list_empty(&dev->all_adj_list.upper);
 }
+EXPORT_SYMBOL(netdev_has_any_upper_dev);
 
 /**
  * netdev_master_upper_dev_get - Get master upper device
@@ -8027,7 +8121,7 @@
 	struct sk_buff **list_skb;
 	struct sk_buff *skb;
 	unsigned int cpu, oldcpu = (unsigned long)ocpu;
-	struct softnet_data *sd, *oldsd;
+	struct softnet_data *sd, *oldsd, *remsd;
 
 	if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
 		return NOTIFY_OK;
@@ -8071,6 +8165,13 @@
 	raise_softirq_irqoff(NET_TX_SOFTIRQ);
 	local_irq_enable();
 
+#ifdef CONFIG_RPS
+	remsd = oldsd->rps_ipi_list;
+	oldsd->rps_ipi_list = NULL;
+#endif
+	/* send out pending IPI's on offline CPU */
+	net_rps_send_ipi(remsd);
+
 	/* Process offline CPU's input_pkt_queue */
 	while ((skb = __skb_dequeue(&oldsd->process_queue))) {
 		netif_rx_ni(skb);
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index b94b1d2..151e047 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -28,6 +28,7 @@
 
 	if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
 		return -EFAULT;
+	ifr.ifr_name[IFNAMSIZ-1] = 0;
 
 	error = netdev_get_name(net, ifr.ifr_name, ifr.ifr_ifindex);
 	if (error)
diff --git a/net/core/dst.c b/net/core/dst.c
index 39cc119..b5de366 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -349,8 +349,15 @@
 
 	new = ((unsigned long) &dst_default_metrics) | DST_METRICS_READ_ONLY;
 	prev = cmpxchg(&dst->_metrics, old, new);
-	if (prev == old)
-		kfree(__DST_METRICS_PTR(old));
+	if (prev == old) {
+		struct dst_metrics *old_p = (struct dst_metrics *)
+					    __DST_METRICS_PTR(old);
+
+		if (prev & DST_METRICS_REFCOUNTED) {
+			if (atomic_dec_and_test(&old_p->refcnt))
+				kfree(old_p);
+		}
+	}
 }
 EXPORT_SYMBOL(__dst_destroy_metrics_generic);
 
diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c
index b6791d9..31c4041 100644
--- a/net/core/fib_rules.c
+++ b/net/core/fib_rules.c
@@ -425,6 +425,7 @@
 	if (tb[FRA_TUN_ID])
 		rule->tun_id = nla_get_be64(tb[FRA_TUN_ID]);
 
+	err = -EINVAL;
 	if (tb[FRA_L3MDEV]) {
 #ifdef CONFIG_NET_L3_MASTER_DEV
 		rule->l3mdev = nla_get_u8(tb[FRA_L3MDEV]);
@@ -446,7 +447,6 @@
 	else
 		rule->suppress_ifgroup = -1;
 
-	err = -EINVAL;
 	if (tb[FRA_GOTO]) {
 		if (rule->action != FR_ACT_GOTO)
 			goto errout_free;
@@ -576,8 +576,10 @@
 
 	if (tb[FRA_UID_RANGE]) {
 		range = nla_get_kuid_range(tb);
-		if (!uid_range_set(&range))
+		if (!uid_range_set(&range)) {
+			err = -EINVAL;
 			goto errout;
+		}
 	} else {
 		range = fib_kuid_range_unset;
 	}
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 227c249..b91cecc 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -56,6 +56,7 @@
 static void neigh_update_notify(struct neighbour *neigh);
 static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
 
+static unsigned int neigh_probe_enable;
 #ifdef CONFIG_PROC_FS
 static const struct file_operations neigh_stat_seq_fops;
 #endif
@@ -940,7 +941,11 @@
 		if (!mod_timer(&neigh->timer, next))
 			neigh_hold(neigh);
 	}
-	if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
+
+	if (neigh_probe_enable) {
+		if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE | NUD_STALE))
+			neigh_probe(neigh);
+	} else if (neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) {
 		neigh_probe(neigh);
 	} else {
 out:
@@ -1258,9 +1263,20 @@
 {
 	struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
 						 lladdr || !dev->addr_len);
-	if (neigh)
-		neigh_update(neigh, lladdr, NUD_STALE,
-			     NEIGH_UPDATE_F_OVERRIDE);
+	if (neigh) {
+		if (neigh_probe_enable) {
+			if (!(neigh->nud_state == NUD_REACHABLE)) {
+				neigh_update(neigh, lladdr, NUD_STALE,
+					     NEIGH_UPDATE_F_OVERRIDE);
+				write_lock(&neigh->lock);
+				neigh_probe(neigh);
+				neigh_update_notify(neigh);
+			}
+		} else {
+			neigh_update(neigh, lladdr, NUD_STALE,
+				     NEIGH_UPDATE_F_OVERRIDE);
+		}
+	}
 	return neigh;
 }
 EXPORT_SYMBOL(neigh_event_ns);
@@ -3107,6 +3123,12 @@
 			.extra2		= &int_max,
 			.proc_handler	= proc_dointvec_minmax,
 		},
+		[NEIGH_VAR_PROBE] = {
+			.procname	= "neigh_probe",
+			.maxlen		= sizeof(int),
+			.mode		= 0644,
+			.proc_handler	= proc_dointvec,
+		},
 		{},
 	},
 };
@@ -3142,6 +3164,7 @@
 		t->neigh_vars[NEIGH_VAR_GC_THRESH1].data = &tbl->gc_thresh1;
 		t->neigh_vars[NEIGH_VAR_GC_THRESH2].data = &tbl->gc_thresh2;
 		t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = &tbl->gc_thresh3;
+		t->neigh_vars[NEIGH_VAR_PROBE].data  = &neigh_probe_enable;
 	}
 
 	if (handler) {
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 9c6fd7f..c2339b8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1965,7 +1965,8 @@
 		struct sockaddr *sa;
 		int len;
 
-		len = sizeof(sa_family_t) + dev->addr_len;
+		len = sizeof(sa_family_t) + max_t(size_t, dev->addr_len,
+						  sizeof(*sa));
 		sa = kmalloc(len, GFP_KERNEL);
 		if (!sa) {
 			err = -ENOMEM;
@@ -3757,6 +3758,9 @@
 		return -EMSGSIZE;
 
 	ifsm = nlmsg_data(nlh);
+	ifsm->family = PF_UNSPEC;
+	ifsm->pad1 = 0;
+	ifsm->pad2 = 0;
 	ifsm->ifindex = dev->ifindex;
 	ifsm->filter_mask = filter_mask;
 
diff --git a/net/core/sock.c b/net/core/sock.c
index f07eaea..c6f42ee 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1501,6 +1501,8 @@
 
 		sock_copy(newsk, sk);
 
+		newsk->sk_prot_creator = sk->sk_prot;
+
 		/* SANITY */
 		if (likely(newsk->sk_net_refcnt))
 			get_net(sock_net(newsk));
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index 1704948..f227f00 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -1471,9 +1471,12 @@
 	 * singleton values (which always leads to failure).
 	 * These settings can still (later) be overridden via sockopts.
 	 */
-	if (ccid_get_builtin_ccids(&tx.val, &tx.len) ||
-	    ccid_get_builtin_ccids(&rx.val, &rx.len))
+	if (ccid_get_builtin_ccids(&tx.val, &tx.len))
 		return -ENOBUFS;
+	if (ccid_get_builtin_ccids(&rx.val, &rx.len)) {
+		kfree(tx.val);
+		return -ENOBUFS;
+	}
 
 	if (!dccp_feat_prefer(sysctl_dccp_tx_ccid, tx.val, tx.len) ||
 	    !dccp_feat_prefer(sysctl_dccp_rx_ccid, rx.val, rx.len))
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 86b0933..8fc1600 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -637,6 +637,7 @@
 		goto drop_and_free;
 
 	inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+	reqsk_put(req);
 	return 0;
 
 drop_and_free:
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 2ac9d2a..28e8252 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -380,6 +380,7 @@
 		goto drop_and_free;
 
 	inet_csk_reqsk_queue_hash_add(sk, req, DCCP_TIMEOUT_INIT);
+	reqsk_put(req);
 	return 0;
 
 drop_and_free:
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 9fe25bf..b68168f 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -24,6 +24,7 @@
 #include <net/checksum.h>
 
 #include <net/inet_sock.h>
+#include <net/inet_common.h>
 #include <net/sock.h>
 #include <net/xfrm.h>
 
@@ -170,6 +171,15 @@
 
 EXPORT_SYMBOL_GPL(dccp_packet_name);
 
+static void dccp_sk_destruct(struct sock *sk)
+{
+	struct dccp_sock *dp = dccp_sk(sk);
+
+	ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
+	dp->dccps_hc_tx_ccid = NULL;
+	inet_sock_destruct(sk);
+}
+
 int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
 {
 	struct dccp_sock *dp = dccp_sk(sk);
@@ -179,6 +189,7 @@
 	icsk->icsk_syn_retries	= sysctl_dccp_request_retries;
 	sk->sk_state		= DCCP_CLOSED;
 	sk->sk_write_space	= dccp_write_space;
+	sk->sk_destruct		= dccp_sk_destruct;
 	icsk->icsk_sync_mss	= dccp_sync_mss;
 	dp->dccps_mss_cache	= 536;
 	dp->dccps_rate_last	= jiffies;
@@ -201,10 +212,7 @@
 {
 	struct dccp_sock *dp = dccp_sk(sk);
 
-	/*
-	 * DCCP doesn't use sk_write_queue, just sk_send_head
-	 * for retransmissions
-	 */
+	__skb_queue_purge(&sk->sk_write_queue);
 	if (sk->sk_send_head != NULL) {
 		kfree_skb(sk->sk_send_head);
 		sk->sk_send_head = NULL;
@@ -222,8 +230,7 @@
 		dp->dccps_hc_rx_ackvec = NULL;
 	}
 	ccid_hc_rx_delete(dp->dccps_hc_rx_ccid, sk);
-	ccid_hc_tx_delete(dp->dccps_hc_tx_ccid, sk);
-	dp->dccps_hc_rx_ccid = dp->dccps_hc_tx_ccid = NULL;
+	dp->dccps_hc_rx_ccid = NULL;
 
 	/* clean up feature negotiation state */
 	dccp_feat_list_purge(&dp->dccps_featneg);
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index 8737412..e1d4d89 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -224,7 +224,7 @@
 static void dns_resolver_describe(const struct key *key, struct seq_file *m)
 {
 	seq_puts(m, key->description);
-	if (key_is_instantiated(key)) {
+	if (key_is_positive(key)) {
 		int err = PTR_ERR(key->payload.data[dns_key_error]);
 
 		if (err)
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index 96e47c5..39bb5b3 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -1,12 +1,13 @@
 config HAVE_NET_DSA
 	def_bool y
-	depends on NETDEVICES && !S390
+	depends on INET && NETDEVICES && !S390
 
 # Drivers must select NET_DSA and the appropriate tagging format
 
 config NET_DSA
 	tristate "Distributed Switch Architecture"
-	depends on HAVE_NET_DSA && NET_SWITCHDEV
+	depends on HAVE_NET_DSA
+	select NET_SWITCHDEV
 	select PHYLIB
 	---help---
 	  Say Y if you want to enable support for the hardware switches supported
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 079d76b..5000e6f 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -1269,26 +1269,32 @@
 	p->old_duplex = -1;
 
 	ds->ports[port].netdev = slave_dev;
-	ret = register_netdev(slave_dev);
-	if (ret) {
-		netdev_err(master, "error %d registering interface %s\n",
-			   ret, slave_dev->name);
-		ds->ports[port].netdev = NULL;
-		free_netdev(slave_dev);
-		return ret;
-	}
 
 	netif_carrier_off(slave_dev);
 
 	ret = dsa_slave_phy_setup(p, slave_dev);
 	if (ret) {
 		netdev_err(master, "error %d setting up slave phy\n", ret);
-		unregister_netdev(slave_dev);
-		free_netdev(slave_dev);
-		return ret;
+		goto out_free;
+	}
+
+	ret = register_netdev(slave_dev);
+	if (ret) {
+		netdev_err(master, "error %d registering interface %s\n",
+			   ret, slave_dev->name);
+		goto out_phy;
 	}
 
 	return 0;
+
+out_phy:
+	phy_disconnect(p->phy);
+	if (of_phy_is_fixed_link(ds->ports[port].dn))
+		of_phy_deregister_fixed_link(ds->ports[port].dn);
+out_free:
+	free_netdev(slave_dev);
+	ds->ports[port].netdev = NULL;
+	return ret;
 }
 
 void dsa_slave_destroy(struct net_device *slave_dev)
diff --git a/net/embms_kernel/Makefile b/net/embms_kernel/Makefile
new file mode 100644
index 0000000..c21480e
--- /dev/null
+++ b/net/embms_kernel/Makefile
@@ -0,0 +1,22 @@
+#
+# Makefile for Embms Kernel module.
+#
+
+KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build
+
+obj-m += embms_kernel.o
+
+ccflags-y += -D__CHECK_ENDIAN__
+
+CDEFINES += -D__CHECK_ENDIAN__
+
+KBUILD_CPPFLAGS += $(CDEFINES)
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules
+modules_install:
+	$(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install
+
+clean:
+	$(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean
+
diff --git a/net/embms_kernel/embms_kernel.c b/net/embms_kernel/embms_kernel.c
new file mode 100644
index 0000000..3bbe51b
--- /dev/null
+++ b/net/embms_kernel/embms_kernel.c
@@ -0,0 +1,1031 @@
+/*************************************************************************
+ * -----------------------------------------------------------------------
+ * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ * -----------------------------------------------------------------------
+
+ * DESCRIPTION
+ * Main file for eMBMs Tunneling Module in kernel.
+ *************************************************************************
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <net/ip.h>
+#include <linux/uaccess.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/etherdevice.h>
+
+#include <linux/inetdevice.h>
+#include <linux/netfilter.h>
+#include <net/arp.h>
+#include <net/neighbour.h>
+
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#include <linux/in.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <linux/miscdevice.h>
+#include "embms_kernel.h"
+
+struct embms_info_internal embms_conf;
+
+/* Global structures used for tunneling. These include
+ * iphdr and udphdr which are appended to skbs for
+ * tunneling, net_device and tunnleing related
+ * structs and params
+ */
+
+unsigned char hdr_buff[sizeof(struct iphdr) + sizeof(struct udphdr)];
+struct iphdr *iph_global;
+struct udphdr *udph_global;
+struct net_device *dev_global;
+
+static struct tmgi_to_clnt_info tmgi_to_clnt_map_tbl;
+
+/* handle_multicast_stream - packet forwarding
+ * function for multicast stream
+ * Main use case is for EMBMS Over Softap feature
+ */
+
+static int handle_multicast_stream(struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	struct udphdr *udph;
+	unsigned char *tmp_ptr = NULL;
+	struct sk_buff *skb_new = NULL;
+	struct sk_buff *skb_cpy = NULL;
+	struct clnt_info *temp_client = NULL;
+	struct tmgi_to_clnt_info *temp_tmgi = NULL;
+	struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
+	struct list_head *clnt_ptr, *prev_clnt_ptr;
+	int hdr_size = sizeof(*udph) + sizeof(*iph) + ETH_HLEN;
+
+	/* only IP packets */
+	if (htons(ETH_P_IP) != skb->protocol) {
+		embms_error("Not an IP packet\n");
+		return 0;
+	}
+
+	if (embms_conf.embms_tunneling_status == TUNNELING_OFF) {
+		embms_debug("Tunneling Disabled. Can't process packets\n");
+		return 0;
+	}
+
+	if (unlikely(memcmp(skb->dev->name, embms_conf.embms_iface,
+			    strlen(embms_conf.embms_iface)) != 0)) {
+		embms_error("Packet received on %s iface. NOT an EMBMS Iface\n",
+			    skb->dev->name);
+		return 0;
+	}
+
+	/* Check if dst ip of packet is same as multicast ip of any tmgi*/
+
+	iph = (struct iphdr *)skb->data;
+	udph = (struct udphdr *)(skb->data + sizeof(struct iphdr));
+
+	spin_lock_bh(&embms_conf.lock);
+
+	list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
+			   &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
+		temp_tmgi = list_entry(tmgi_entry_ptr,
+				       struct tmgi_to_clnt_info,
+				       tmgi_list_ptr);
+
+		if ((temp_tmgi->tmgi_multicast_addr == iph->daddr) &&
+		    (temp_tmgi->tmgi_port == udph->dest))
+			break;
+	}
+
+	if (tmgi_entry_ptr == &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
+		embms_error("handle_multicast_stream:");
+		embms_error("could not find matchin tmgi entry\n");
+		spin_unlock_bh(&embms_conf.lock);
+		return 0;
+	}
+
+	/* Found a matching tmgi entry. Realloc headroom to
+	 * accommodate new Ethernet, IP and UDP header
+	 */
+
+	skb_new = skb_realloc_headroom(skb, hdr_size);
+	if (unlikely(!skb_new)) {
+		embms_error("Can't allocate headroom\n");
+		spin_unlock_bh(&embms_conf.lock);
+		return 0;
+	}
+
+	/* push skb->data and copy IP and UDP headers*/
+
+	tmp_ptr = skb_push(skb_new,
+			   sizeof(struct udphdr) + sizeof(struct iphdr));
+
+	iph = (struct iphdr *)tmp_ptr;
+	udph = (struct udphdr *)(tmp_ptr + sizeof(struct iphdr));
+
+	memcpy(tmp_ptr, hdr_buff, hdr_size - ETH_HLEN);
+	udph->len = htons(skb_new->len - sizeof(struct iphdr));
+	iph->tot_len = htons(skb_new->len);
+
+	list_for_each_safe(clnt_ptr, prev_clnt_ptr,
+			   &temp_tmgi->client_list_head) {
+		temp_client = list_entry(clnt_ptr,
+					 struct clnt_info,
+					 client_list_ptr);
+
+		/* Make a copy of skb_new with new IP and UDP header.
+		 * We can't use skb_new or its clone here since we need to
+		 * constantly change dst ip and dst port which is not possible
+		 * for shared memory as is the case with skb_new.
+		 */
+
+		skb_cpy = skb_copy(skb_new, GFP_ATOMIC);
+		if (unlikely(!skb_cpy)) {
+			embms_error("Can't copy skb\n");
+			kfree_skb(skb_new);
+			return 0;
+		}
+
+		iph = (struct iphdr *)skb_cpy->data;
+		udph = (struct udphdr *)(skb_cpy->data + sizeof(struct iphdr));
+
+		iph->id = htons(atomic_inc_return(&embms_conf.ip_ident));
+
+		/* Calculate checksum for new IP and UDP header*/
+
+		udph->dest = temp_client->port;
+		skb_cpy->csum = csum_partial((char *)udph,
+					     ntohs(udph->len),
+					     skb_cpy->csum);
+
+		iph->daddr = temp_client->addr;
+		ip_send_check(iph);
+
+		udph->check = 0;
+		udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
+						ntohs(udph->len),
+						IPPROTO_UDP,
+						skb_cpy->csum);
+
+		if (udph->check == 0)
+			udph->check = CSUM_MANGLED_0;
+
+		if (unlikely(!dev_global)) {
+			embms_error("Global device NULL\n");
+			kfree_skb(skb_cpy);
+			kfree_skb(skb_new);
+			return 0;
+		}
+
+		/* update device info and add MAC header*/
+
+		skb_cpy->dev = dev_global;
+
+		skb_cpy->dev->header_ops->create(skb_cpy, skb_cpy->dev,
+						ETH_P_IP, temp_client->dmac,
+						NULL, skb_cpy->len);
+		dev_queue_xmit(skb_cpy);
+	}
+
+	spin_unlock_bh(&embms_conf.lock);
+	kfree_skb(skb_new);
+	return 1;
+}
+
+static int check_embms_device(atomic_t *use_count)
+{
+	int ret;
+
+	if (atomic_inc_return(use_count) == 1) {
+		ret = 0;
+	} else {
+		atomic_dec(use_count);
+		ret = -EBUSY;
+	}
+	return ret;
+}
+
+static int embms_device_open(struct inode *inode, struct file *file)
+{
+	/*Check if the device is busy*/
+	if (check_embms_device(&embms_conf.device_under_use)) {
+		embms_error("embms_tm_open : EMBMS device busy\n");
+		return -EBUSY;
+	}
+
+	try_module_get(THIS_MODULE);
+	return SUCCESS;
+}
+
+static int embms_device_release(struct inode *inode, struct file *file)
+{
+	/* Reduce device use count before leaving*/
+	embms_debug("Releasing EMBMS device..\n");
+	atomic_dec(&embms_conf.device_under_use);
+	embms_conf.embms_tunneling_status = TUNNELING_OFF;
+	module_put(THIS_MODULE);
+	return SUCCESS;
+}
+
+static struct tmgi_to_clnt_info *check_for_tmgi_entry(u32 addr,
+						      u16 port)
+{
+	struct list_head *tmgi_ptr, *prev_tmgi_ptr;
+	struct tmgi_to_clnt_info *temp_tmgi = NULL;
+
+	embms_debug("check_for_tmgi_entry: mcast addr :%pI4, port %u\n",
+		    &addr, ntohs(port));
+
+	list_for_each_safe(tmgi_ptr,
+			   prev_tmgi_ptr,
+			   &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
+		temp_tmgi = list_entry(tmgi_ptr,
+				       struct tmgi_to_clnt_info,
+				       tmgi_list_ptr);
+
+		if ((temp_tmgi->tmgi_multicast_addr == addr) &&
+		    (temp_tmgi->tmgi_port == port)) {
+			embms_debug("check_for_tmgi_entry:TMGI entry found\n");
+			return temp_tmgi;
+		}
+	}
+	return NULL;
+}
+
+static struct clnt_info *chk_clnt_entry(struct tmgi_to_clnt_info *tmgi,
+					struct tmgi_to_clnt_info_update *clnt)
+{
+	struct list_head *clnt_ptr, *prev_clnt_ptr;
+	struct clnt_info *temp_client = NULL;
+
+	embms_debug("check_for_client_entry: clnt addr :%pI4, port %u\n",
+		    &clnt->client_addr, ntohs(clnt->client_port));
+
+	list_for_each_safe(clnt_ptr,
+			   prev_clnt_ptr,
+			   &tmgi->client_list_head) {
+		temp_client = list_entry(clnt_ptr,
+					 struct clnt_info,
+					 client_list_ptr);
+		if ((temp_client->addr == clnt->client_addr) &&
+		    (temp_client->port == clnt->client_port)) {
+			embms_debug("Clnt entry present\n");
+			return temp_client;
+		}
+	}
+	return NULL;
+}
+
+static int add_new_tmgi_entry(struct tmgi_to_clnt_info_update *info_update,
+			      struct clnt_info *clnt)
+{
+	struct tmgi_to_clnt_info *new_tmgi = NULL;
+
+	embms_debug("add_new_tmgi_entry:Enter\n");
+
+	new_tmgi = kzalloc(sizeof(*new_tmgi),
+			   GFP_ATOMIC);
+	if (!new_tmgi) {
+		embms_error("add_new_tmgi_entry: mem alloc failed\n");
+		return -ENOMEM;
+	}
+
+	memset(new_tmgi, 0, sizeof(struct tmgi_to_clnt_info));
+
+	new_tmgi->tmgi_multicast_addr = info_update->multicast_addr;
+	new_tmgi->tmgi_port = info_update->multicast_port;
+
+	embms_debug("add_new_tmgi_entry:");
+	embms_debug("New tmgi multicast addr :%pI4 , port %u\n",
+		    &info_update->multicast_addr,
+		    ntohs(info_update->multicast_port));
+
+	embms_debug("add_new_tmgi_entry:Adding client entry\n");
+
+	spin_lock_bh(&embms_conf.lock);
+
+	INIT_LIST_HEAD(&new_tmgi->client_list_head);
+	list_add(&clnt->client_list_ptr,
+		 &new_tmgi->client_list_head);
+	new_tmgi->no_of_clients++;
+
+	/* Once above steps are done successfully,
+	 * we add tmgi entry to our local table
+	 */
+
+	list_add(&new_tmgi->tmgi_list_ptr,
+		 &tmgi_to_clnt_map_tbl.tmgi_list_ptr);
+	embms_conf.no_of_tmgi_sessions++;
+
+	spin_unlock_bh(&embms_conf.lock);
+
+	return SUCCESS;
+}
+
+static void print_tmgi_to_client_table(void)
+{
+	int i, j;
+	struct clnt_info *temp_client = NULL;
+	struct tmgi_to_clnt_info *temp_tmgi = NULL;
+	struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
+	struct list_head *clnt_ptr, *prev_clnt_ptr;
+
+	embms_debug("====================================================\n");
+	embms_debug("Printing TMGI to Client Table :\n");
+	embms_debug("No of Active TMGIs : %d\n",
+		    embms_conf.no_of_tmgi_sessions);
+	embms_debug("====================================================\n\n");
+
+	if (embms_conf.no_of_tmgi_sessions > 0) {
+		i = 1;
+		list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
+				   &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
+			temp_tmgi = list_entry(tmgi_entry_ptr,
+					       struct tmgi_to_clnt_info,
+					       tmgi_list_ptr);
+
+			embms_debug("TMGI entry %d :\n", i);
+			embms_debug("TMGI multicast addr : %pI4 , port %u\n\n",
+				    &temp_tmgi->tmgi_multicast_addr,
+				    ntohs(temp_tmgi->tmgi_port));
+			embms_debug("No of clients : %d\n",
+				    temp_tmgi->no_of_clients);
+			j = 1;
+
+			list_for_each_safe(clnt_ptr, prev_clnt_ptr,
+					   &temp_tmgi->client_list_head) {
+				temp_client = list_entry(clnt_ptr,
+							 struct clnt_info,
+							 client_list_ptr);
+				embms_debug("Client entry %d :\n", j);
+				embms_debug("client addr : %pI4 , port %u\n\n",
+					    &temp_client->addr,
+					    ntohs(temp_client->port));
+				j++;
+			}
+			i++;
+			embms_debug("===========================================\n\n");
+		}
+	} else {
+		embms_debug("No TMGI entries to Display\n");
+	}
+	embms_debug("==================================================================\n\n");
+}
+
+/**
+ * delete_tmgi_entry_from_table() - deletes tmgi from global tmgi-client table
+ * @buffer:	Buffer containing TMGI info for deletion.
+ *
+ * This function completely removes the TMGI from
+ * global TMGI-client table, along with the client list
+ * so that no packets for this TMGI are processed
+ *
+ * Return: Success on deleting TMGI entry, error otherwise.
+ */
+
+int delete_tmgi_entry_from_table(char *buffer)
+{
+	struct tmgi_to_clnt_info_update *info_update;
+	struct clnt_info *temp_client = NULL;
+	struct tmgi_to_clnt_info *temp_tmgi = NULL;
+	struct list_head *clnt_ptr, *prev_clnt_ptr;
+
+	embms_debug("delete_tmgi_entry_from_table: Enter\n");
+
+	info_update = (struct tmgi_to_clnt_info_update *)buffer;
+
+	if (!info_update) {
+		embms_error("delete_tmgi_entry_from_table:");
+		embms_error("NULL arguments passed\n");
+		return -EBADPARAM;
+	}
+
+	/* This function is used to delete a specific TMGI entry
+	 * when that particular TMGI goes down
+	 * Search for the TMGI entry in our local table
+	 */
+	if (embms_conf.no_of_tmgi_sessions == 0) {
+		embms_error("TMGI count 0. Nothing to delete\n");
+		return SUCCESS;
+	}
+
+	temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr,
+					 info_update->multicast_port);
+
+	if (!temp_tmgi) {
+		/* TMGI entry was not found in our local table*/
+		embms_error("delete_client_entry_from_table :");
+		embms_error("Desired TMGI entry not found\n");
+		return -EBADPARAM;
+	}
+
+	spin_lock_bh(&embms_conf.lock);
+
+	/* We need to free memory allocated to client entries
+	 * for a particular TMGI entry
+	 */
+
+	list_for_each_safe(clnt_ptr, prev_clnt_ptr,
+			   &temp_tmgi->client_list_head) {
+		temp_client = list_entry(clnt_ptr,
+					 struct clnt_info,
+					 client_list_ptr);
+		embms_debug("delete_tmgi_entry_from_table :");
+		embms_debug("Client addr to delete :%pI4 , port %u\n",
+			    &temp_client->addr, ntohs(temp_client->port));
+		list_del(&temp_client->client_list_ptr);
+		temp_tmgi->no_of_clients--;
+		kfree(temp_client);
+	}
+
+	/* Free memory allocated to tmgi entry*/
+
+	list_del(&temp_tmgi->tmgi_list_ptr);
+	kfree(temp_tmgi);
+	embms_conf.no_of_tmgi_sessions--;
+
+	spin_unlock_bh(&embms_conf.lock);
+
+	embms_debug("delete_tmgi_entry_from_table : TMGI Entry deleted.\n");
+
+	return SUCCESS;
+}
+
+/**
+ * delete_client_entry_from_all_tmgi() - deletes client from all tmgi lists
+ * @buffer:	Buffer containing client info for deletion.
+ *
+ * This function completely removes a client from
+ * all TMGIs in global TMGI-client table. Also delets TMGI
+ * entries if no more clients are there
+ *
+ * Return: Success on deleting client entry, error otherwise.
+ */
+int delete_client_entry_from_all_tmgi(char *buffer)
+{
+	struct tmgi_to_clnt_info_update *info_update;
+	struct clnt_info *temp_client = NULL;
+	struct tmgi_to_clnt_info *tmgi = NULL;
+	struct list_head *tmgi_entry_ptr, *prev_tmgi_entry_ptr;
+
+	/* We use this function when we want to delete any
+	 * client entry from all TMGI entries. This scenario
+	 * happens when any client disconnects and hence
+	 * we need to clean all realted client entries
+	 * in our mapping table
+	 */
+
+	embms_debug("del_clnt_from_all_tmgi: Enter\n");
+
+	info_update = (struct tmgi_to_clnt_info_update *)buffer;
+
+	if (!info_update) {
+		embms_error("del_clnt_from_all_tmgi:");
+		embms_error("NULL arguments passed\n");
+		return -EBADPARAM;
+	}
+
+	/* We start checking from first TMGI entry and if client
+	 * entry is found in client entries of any TMGI, we clean
+	 * up that client entry from that TMGI entry
+	 */
+	if (embms_conf.no_of_tmgi_sessions == 0)
+		return SUCCESS;
+
+	list_for_each_safe(tmgi_entry_ptr, prev_tmgi_entry_ptr,
+			   &tmgi_to_clnt_map_tbl.tmgi_list_ptr) {
+		tmgi = list_entry(tmgi_entry_ptr,
+				  struct tmgi_to_clnt_info,
+				  tmgi_list_ptr);
+
+		temp_client = chk_clnt_entry(tmgi, info_update);
+		if (!temp_client)
+			continue;
+
+		spin_lock_bh(&embms_conf.lock);
+
+		list_del(&temp_client->client_list_ptr);
+		tmgi->no_of_clients--;
+		kfree(temp_client);
+
+		spin_unlock_bh(&embms_conf.lock);
+
+		temp_client = NULL;
+
+		if (tmgi->no_of_clients == 0) {
+			/* Deleted clnt was the only clnt for
+			 * that TMGI we need to delete TMGI
+			 * entry from table
+			 */
+			embms_debug("del_clnt_from_all_tmgi:");
+			embms_debug("Deleted client was ");
+			embms_debug("last client for tmgi\n");
+			embms_debug("del_clnt_from_all_tmgi:");
+			embms_debug("Delting tmgi as it has ");
+			embms_debug("zero clients.TMGI IP ");
+			embms_debug(":%pI4 , port %u\n",
+				    &tmgi->tmgi_multicast_addr,
+				    ntohs(tmgi->tmgi_port));
+
+			spin_lock_bh(&embms_conf.lock);
+
+			list_del(&tmgi->tmgi_list_ptr);
+			embms_conf.no_of_tmgi_sessions--;
+			kfree(tmgi);
+
+			spin_unlock_bh(&embms_conf.lock);
+
+			embms_debug("del_clnt_from_all_tmgi:");
+			embms_debug("TMGI entry deleted\n");
+		}
+	}
+
+	embms_debug("del_clnt_from_all_tmgi Successful\n");
+	return SUCCESS;
+}
+
+/**
+ * add_client_entry_to_table() - add client entry to specified TMGI
+ * @buffer:	Buffer containing client info for addition.
+ *
+ * This function adds a client to the specified TMGI in
+ * the global TMGI-client table. If TMGI entry is not
+ * present, it adds a new TMGI entry and adds client
+ * entry to it.
+ *
+ * Return: Success on adding client entry, error otherwise.
+ */
+int add_client_entry_to_table(char *buffer)
+{
+	int ret;
+	struct tmgi_to_clnt_info_update *info_update;
+	struct clnt_info *new_client = NULL;
+	struct tmgi_to_clnt_info *tmgi = NULL;
+	struct neighbour *neigh_entry;
+
+	embms_debug("add_client_entry_to_table: Enter\n");
+
+	info_update = (struct tmgi_to_clnt_info_update *)buffer;
+
+	if (!info_update) {
+		embms_error("add_client_entry_to_table:");
+		embms_error("NULL arguments passed\n");
+		return -EBADPARAM;
+	}
+
+	new_client = kzalloc(sizeof(*new_client), GFP_ATOMIC);
+	if (!new_client) {
+		embms_error("add_client_entry_to_table:");
+		embms_error("Cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	new_client->addr = info_update->client_addr;
+	new_client->port = info_update->client_port;
+
+	neigh_entry = __ipv4_neigh_lookup(dev_global,
+					  (u32)(new_client->addr));
+	if (!neigh_entry) {
+		embms_error("add_client_entry_to_table :");
+		embms_error("Can't find neighbour entry\n");
+		kfree(new_client);
+		return -EBADPARAM;
+	}
+
+	ether_addr_copy(new_client->dmac, neigh_entry->ha);
+
+	embms_debug("DMAC of client : %pM\n", new_client->dmac);
+
+	embms_debug("add_client_entry_to_table:");
+	embms_debug("New client addr :%pI4 , port %u\n",
+		    &info_update->client_addr,
+		    ntohs(info_update->client_port));
+
+	if (embms_conf.no_of_tmgi_sessions == 0) {
+		/* TMGI Client mapping table is empty.
+		 * First client entry is being added
+		 */
+
+		embms_debug("tmgi_to_clnt_map_tbl is empty\n");
+
+		ret = add_new_tmgi_entry(info_update, new_client);
+		if (ret != SUCCESS) {
+			kfree(new_client);
+			new_client = NULL;
+		}
+
+		goto exit_add;
+	}
+
+	/* In this case, table already has some entries
+	 * and we need to search for the specific tmgi entry
+	 * for which client entry is to be added
+	 */
+
+	tmgi = check_for_tmgi_entry(info_update->multicast_addr,
+				    info_update->multicast_port);
+	if (tmgi) {
+		if (chk_clnt_entry(tmgi, info_update)) {
+			kfree(new_client);
+			return -ENOEFFECT;
+		}
+
+		/* Adding client to the client list
+		 * for the specified TMGI
+		 */
+
+		spin_lock_bh(&embms_conf.lock);
+
+		list_add(&new_client->client_list_ptr,
+			 &tmgi->client_list_head);
+			tmgi->no_of_clients++;
+
+		spin_unlock_bh(&embms_conf.lock);
+
+		ret = SUCCESS;
+	} else {
+		/* TMGI specified in the message was not found in
+		 * mapping table.Hence, we need to add a new entry
+		 * for this TMGI and add the specified client to the client
+		 * list
+		 */
+
+		embms_debug("TMGI entry not present. Adding tmgi entry\n");
+
+		ret = add_new_tmgi_entry(info_update, new_client);
+		if (ret != SUCCESS) {
+			kfree(new_client);
+			new_client = NULL;
+		}
+	}
+
+exit_add:
+	return ret;
+}
+
+/**
+ * delete_client_entry_from_table() - delete client entry from specified TMGI
+ * @buffer:	Buffer containing client info for deletion.
+ *
+ * This function deletes a client from the specified TMGI in
+ * the global TMGI-client table. If this was the last client
+ * entry, it also deletes the TMGI entry.
+ *
+ * Return: Success on deleting client entry, error otherwise.
+ */
+int delete_client_entry_from_table(char *buffer)
+{
+	struct tmgi_to_clnt_info_update *info_update;
+	struct clnt_info *temp_client = NULL;
+	struct tmgi_to_clnt_info *temp_tmgi = NULL;
+
+	embms_debug("delete_client_entry_from_table: Enter\n");
+
+	info_update = (struct tmgi_to_clnt_info_update *)buffer;
+
+	if (!info_update) {
+		embms_error("delete_client_entry_from_table:");
+		embms_error("NULL arguments passed\n");
+		return -EBADPARAM;
+	}
+
+	/* Search for the TMGI entry*/
+	if (embms_conf.no_of_tmgi_sessions == 0)
+		return SUCCESS;
+
+	temp_tmgi = check_for_tmgi_entry(info_update->multicast_addr,
+					 info_update->multicast_port);
+
+	if (!temp_tmgi) {
+		embms_error("delete_client_entry_from_table:TMGI not found\n");
+		return -EBADPARAM;
+	}
+	/* Delete client entry for a specific tmgi*/
+
+	embms_debug("delete_client_entry_from_table:clnt addr :%pI4,port %u\n",
+		    &info_update->client_addr,
+		    ntohs(info_update->client_port));
+
+	temp_client = chk_clnt_entry(temp_tmgi, info_update);
+
+	if (!temp_client) {
+		/* Specified client entry was not found in client list
+		 * of specified TMGI
+		 */
+		embms_error("delete_client_entry_from_table:Clnt not found\n");
+		return -EBADPARAM;
+	}
+
+	spin_lock_bh(&embms_conf.lock);
+
+	list_del(&temp_client->client_list_ptr);
+	temp_tmgi->no_of_clients--;
+
+	spin_unlock_bh(&embms_conf.lock);
+
+	kfree(temp_client);
+	temp_client = NULL;
+
+	embms_debug("delete_client_entry_from_table:Client entry deleted\n");
+
+	if (temp_tmgi->no_of_clients == 0) {
+		/* If deleted client was the only client for that TMGI
+		 * we need to delete TMGI entry from table
+		 */
+		embms_debug("delete_client_entry_from_table:");
+		embms_debug("Deleted client was the last client for tmgi\n");
+		embms_debug("delete_client_entry_from_table:");
+		embms_debug("Deleting tmgi since it has zero clients\n");
+
+		spin_lock_bh(&embms_conf.lock);
+
+		list_del(&temp_tmgi->tmgi_list_ptr);
+		embms_conf.no_of_tmgi_sessions--;
+		kfree(temp_tmgi);
+
+		spin_unlock_bh(&embms_conf.lock);
+
+		embms_debug("delete_client_entry_from_table: TMGI deleted\n");
+	}
+
+	if (embms_conf.no_of_tmgi_sessions == 0)
+		embms_conf.embms_tunneling_status = TUNNELING_OFF;
+
+	return SUCCESS;
+}
+
+/**
+ * embms_device_ioctl() - handle IOCTL calls to device
+ * @file:	File descriptor of file opened from userspace process
+ * @ioctl_num:	IOCTL to use
+ * @ioctl_param:	IOCTL parameters/arguments
+ *
+ * This function is called whenever a process tries to do
+ * an ioctl on our device file. As per the IOCTL number,
+ * it calls various functions to manipulate global
+ * TMGI-client table
+ *
+ * Return: Success if functoin call returns SUCCESS, error otherwise.
+ */
+
+long embms_device_ioctl(struct file *file, unsigned int ioctl_num,
+			unsigned long ioctl_param)
+{
+	int ret;
+	char buffer[BUF_LEN];
+	struct in_device *iface_dev;
+	struct in_ifaddr *iface_info;
+	struct tmgi_to_clnt_info_update *info_update;
+	char __user *argp = (char __user *)ioctl_param;
+
+	memset(buffer, 0, BUF_LEN);
+
+	/* Switch according to the ioctl called*/
+	switch (ioctl_num) {
+	case ADD_EMBMS_TUNNEL:
+		if (copy_from_user(buffer, argp,
+				   sizeof(struct tmgi_to_clnt_info_update)))
+			return -EFAULT;
+
+		ret = add_client_entry_to_table(buffer);
+		print_tmgi_to_client_table();
+		break;
+
+	case DEL_EMBMS_TUNNEL:
+		if (copy_from_user(buffer, argp,
+				   sizeof(struct tmgi_to_clnt_info_update)))
+			return -EFAULT;
+
+		ret = delete_client_entry_from_table(buffer);
+		print_tmgi_to_client_table();
+		break;
+
+	case TMGI_DEACTIVATE:
+		if (copy_from_user(buffer, argp,
+				   sizeof(struct tmgi_to_clnt_info_update)))
+			return -EFAULT;
+
+		ret = delete_tmgi_entry_from_table(buffer);
+		print_tmgi_to_client_table();
+		break;
+
+	case CLIENT_DEACTIVATE:
+		if (copy_from_user(buffer, argp,
+				   sizeof(struct tmgi_to_clnt_info_update)))
+			return -EFAULT;
+
+		ret = delete_client_entry_from_all_tmgi(buffer);
+		print_tmgi_to_client_table();
+		break;
+
+	case GET_EMBMS_TUNNELING_STATUS:
+		/* This ioctl is both input (ioctl_param) and
+		 * output (the return value of this function)
+		 */
+		embms_debug("Sending tunneling status : %d\n",
+			    embms_conf.embms_tunneling_status);
+		ret = embms_conf.embms_tunneling_status;
+		break;
+
+	case START_EMBMS_TUNNEL:
+
+		if (copy_from_user(buffer, argp,
+				   sizeof(struct tmgi_to_clnt_info_update)))
+			return -EFAULT;
+
+		info_update = (struct tmgi_to_clnt_info_update *)buffer;
+		embms_conf.embms_data_port = info_update->data_port;
+		udph_global->source = embms_conf.embms_data_port;
+
+		memset(embms_conf.embms_iface, 0, EMBMS_MAX_IFACE_NAME);
+		memcpy(embms_conf.embms_iface, info_update->iface_name,
+		       EMBMS_MAX_IFACE_NAME);
+
+		embms_conf.embms_tunneling_status = TUNNELING_ON;
+		embms_debug("Starting Tunneling. Embms_data_port  = %d\n",
+			    ntohs(embms_conf.embms_data_port));
+		embms_debug("Embms Data Iface = %s\n", embms_conf.embms_iface);
+		ret = SUCCESS;
+
+		/*Initialise dev_global to bridge device*/
+		dev_global = __dev_get_by_name(&init_net, BRIDGE_IFACE);
+		if (!dev_global) {
+			embms_error("Error in getting device info\n");
+			ret = FAILURE;
+		} else {
+			iface_dev = (struct in_device *)dev_global->ip_ptr;
+			iface_info = iface_dev->ifa_list;
+			while (iface_info) {
+				if (memcmp(iface_info->ifa_label,
+					   BRIDGE_IFACE,
+					   strlen(BRIDGE_IFACE)) == 0)
+					break;
+
+				iface_info = iface_info->ifa_next;
+			}
+			if (iface_info) {
+				embms_debug("IP address of %s iface is %pI4\n",
+					    BRIDGE_IFACE,
+					    &iface_info->ifa_address);
+				/*Populate source addr for header*/
+				iph_global->saddr = iface_info->ifa_address;
+				ret = SUCCESS;
+			} else {
+				embms_debug("Could not find iface address\n");
+				ret = FAILURE;
+			}
+		}
+
+		break;
+
+	case STOP_EMBMS_TUNNEL:
+
+		embms_conf.embms_tunneling_status = TUNNELING_OFF;
+		embms_debug("Stopped Tunneling..\n");
+		ret = SUCCESS;
+		break;
+	}
+
+	return ret;
+}
+
+/* Module Declarations
+ * This structure will hold the functions to be called
+ * when a process does something to the device we
+ * created. Since a pointer to this structure is kept in
+ * the devices table, it can't be local to
+ * init_module. NULL is for unimplemented functions.
+ */
+static const struct file_operations embms_device_fops = {
+	.owner = THIS_MODULE,
+	.open = embms_device_open,
+	.release = embms_device_release,
+	.read = NULL,
+	.write = NULL,
+	.unlocked_ioctl = embms_device_ioctl,
+};
+
+static int embms_ioctl_init(void)
+{
+	int ret;
+	struct device *dev;
+
+	ret = alloc_chrdev_region(&device, 0, dev_num, EMBMS_DEVICE_NAME);
+	if (ret) {
+		embms_error("device_alloc err\n");
+		goto dev_alloc_err;
+	}
+
+	embms_class = class_create(THIS_MODULE, EMBMS_DEVICE_NAME);
+	if (IS_ERR(embms_class)) {
+		embms_error("class_create err\n");
+		goto class_err;
+	}
+
+	dev = device_create(embms_class, NULL, device,
+			    &embms_conf, EMBMS_DEVICE_NAME);
+	if (IS_ERR(dev)) {
+		embms_error("device_create err\n");
+		goto device_err;
+	}
+
+	cdev_init(&embms_device, &embms_device_fops);
+	ret = cdev_add(&embms_device, device, dev_num);
+	if (ret) {
+		embms_error("cdev_add err\n");
+		goto cdev_add_err;
+	}
+
+	embms_debug("ioctl init OK!!\n");
+	return 0;
+
+cdev_add_err:
+	device_destroy(embms_class, device);
+device_err:
+	class_destroy(embms_class);
+class_err:
+	unregister_chrdev_region(device, dev_num);
+dev_alloc_err:
+	return -ENODEV;
+}
+
+static void embms_ioctl_deinit(void)
+{
+	cdev_del(&embms_device);
+	device_destroy(embms_class, device);
+	class_destroy(embms_class);
+	unregister_chrdev_region(device, dev_num);
+}
+
+/*Initialize the module - Register the misc device*/
+static int __init start_embms(void)
+{
+	int ret = 0;
+
+	iph_global = (struct iphdr *)hdr_buff;
+	udph_global = (struct udphdr *)(hdr_buff + sizeof(struct iphdr));
+
+	embms_conf.embms_tunneling_status = TUNNELING_OFF;
+	embms_conf.no_of_tmgi_sessions = 0;
+	embms_conf.embms_data_port = 0;
+	atomic_set(&embms_conf.device_under_use, 0);
+	atomic_set(&embms_conf.ip_ident, 0);
+	spin_lock_init(&embms_conf.lock);
+
+	embms_debug("Registering embms device\n");
+
+	ret = embms_ioctl_init();
+	if (ret) {
+		embms_error("embms device failed to register");
+		goto fail_init;
+	}
+
+	INIT_LIST_HEAD(&tmgi_to_clnt_map_tbl.tmgi_list_ptr);
+
+	memset(hdr_buff, 0, sizeof(struct udphdr) + sizeof(struct iphdr));
+	udph_global->check = UDP_CHECKSUM;
+	iph_global->version = IP_VERSION;
+	iph_global->ihl = IP_IHL;
+	iph_global->tos = IP_TOS;
+	iph_global->frag_off = IP_FRAG_OFFSET;
+	iph_global->ttl = IP_TTL;
+	iph_global->protocol = IPPROTO_UDP;
+
+	dev_global = NULL;
+
+	if (!embms_tm_multicast_recv)
+		RCU_INIT_POINTER(embms_tm_multicast_recv,
+				 handle_multicast_stream);
+
+	return ret;
+
+fail_init:
+	embms_ioctl_deinit();
+	return ret;
+}
+
+/*Cleanup - unregister the appropriate file from proc*/
+
+static void __exit stop_embms(void)
+{
+	embms_ioctl_deinit();
+
+	if (rcu_dereference(embms_tm_multicast_recv))
+		RCU_INIT_POINTER(embms_tm_multicast_recv, NULL);
+
+	embms_debug("unregister_chrdev done\n");
+}
+
+module_init(start_embms);
+module_exit(stop_embms);
+MODULE_LICENSE("GPL v2");
diff --git a/net/embms_kernel/embms_kernel.h b/net/embms_kernel/embms_kernel.h
new file mode 100644
index 0000000..c8248ce
--- /dev/null
+++ b/net/embms_kernel/embms_kernel.h
@@ -0,0 +1,233 @@
+/******************************************************************
+ * Copyright (c) 2013-2015,2017, The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *---------------------------------------------------------------
+
+ * DESCRIPTION
+ * Header file for eMBMs Tunneling Module in kernel.
+ *******************************************************************
+ */
+
+#ifndef EMBMS_H
+#define EMBMS_H
+
+#include <linux/ioctl.h>
+#include <stdbool.h>
+#include <linux/if_addr.h>
+#include <linux/list.h>
+#include <linux/ip.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/cdev.h>
+
+#define EMBMS_MAX_IFACE_NAME    20
+
+/* Defining IP and UDP header related macros*/
+
+#define UDP_CHECKSUM                0
+#define IP_VERSION                  4
+#define IP_IHL                      5
+#define IP_TOS                      0
+#define IP_ID                       1
+#define IP_FRAG_OFFSET              htons(0x4000)
+#define IP_TTL                      64
+#define BRIDGE_IFACE                "bridge0"
+
+#define BUF_LEN 1024
+#define TUNNELING_ON 1
+#define TUNNELING_OFF 0
+
+// definitions required for IOCTL
+static unsigned int dev_num = 1;
+/* Embms device used for communication*/
+struct cdev embms_device;
+static struct class *embms_class;
+static dev_t device;
+#define EMBMS_IOC_MAGIC 0x64
+
+#define embms_debug pr_debug
+#define embms_error pr_debug
+
+/* The name of the device file*/
+#define EMBMS_DEVICE_NAME "embms_tm_device"
+
+extern int (*embms_tm_multicast_recv)(struct sk_buff *skb);
+
+/**
+ * enum embms_action_type - Describes action to perform
+ * @ADD_CLIENT_ENTRY: add client entry to TMGI
+ * @DELETE_CLIENT_ENTRY: deelte client entry from TMGI
+ * @TMGI_DEACTIVATE: Delete TMGI entry
+ * @CLIENT_ACTIVATE_ALL_TMGI: Add client to all TMGI
+ * @CLIENT_DEACTIVATE_ALL_TMGI: Delete client from all TMGI
+ * @SESSION_DEACTIVATE: Stop session
+ * @SOCK_INFO: Socket information like V4 addr, port etc
+ *
+ * This enum defines the types of action which are
+ * supported by this module.
+ */
+
+enum {
+	ADD_CLIENT_ENTRY = 0,
+	DELETE_CLIENT_ENTRY,
+	TMGI_DEACTIVATE,
+	CLIENT_ACTIVATE_ALL_TMGI,
+	CLIENT_DEACTIVATE_ALL_TMGI,
+	SESSION_DEACTIVATE,
+	SOCK_INFO
+} embms_action_type;
+
+/**
+ * struct tmgi_to_clnt_info_update - information for addition/deletion
+ * @multicast_addr: TMGI multicast IP to receive data
+ * @multicast_port: TMGI multicast port to receive date
+ * @client_addr: Client IPV4 address for sending data
+ * @client_port: Client port for sending data
+ * @data_port: port used to send data to client
+ * @action_type: Action to be performed
+ * @iface_name: iface to listen to for data
+ *
+ * This structure contains information as to what action
+ * needs to be performed on TMGI-client table. It is
+ * sent as a parameter during an IOCTL call
+ */
+
+struct tmgi_to_clnt_info_update {
+	u32 multicast_addr;
+	u16 multicast_port;
+	u32 client_addr;
+	u16 client_port;
+	u16 data_port;
+	u32 action_type;
+	char iface_name[EMBMS_MAX_IFACE_NAME];
+};
+
+/**
+ * struct clnt_info - contains client information
+ * @addr: Client IPV4 address for sending packets
+ * @port: Client port for sending packets
+ * @dmac: Client DMAC address
+ * @client_list_ptr : list ptr used to maintain client list
+ *
+ * This structure maintains complete client information
+ * to be used when sending packets to client
+ */
+
+struct clnt_info {
+	u32 addr;
+	u16 port;
+	u8 dmac[ETH_ALEN];
+	struct list_head client_list_ptr;
+};
+
+/**
+ * struct tmgi_to_clnt_info - contains TMGI information
+ * @tmgi_multicast_addr: TMGI IPV4 address to listen for packets
+ * @tmgi_port: Client port to listen for packets
+ * @no_of_clients: No of clients for a TMGI
+ * @client_list_head : list head for client list
+ * @tmgi_list_ptr : list ptr to maintain tmgi list
+ *
+ * This structure maintains complete client information
+ * to be used when sending data to client
+ */
+
+struct tmgi_to_clnt_info {
+	u32 tmgi_multicast_addr;
+	u16 tmgi_port;
+	u16 no_of_clients;
+	struct list_head client_list_head;
+	struct list_head tmgi_list_ptr;
+};
+
+/**
+ * struct embms_info_internal - stores module specific params
+ * @device_under_use: Used to prevent concurent access to the same device
+ * @embms_data_port: Source Data port used for tunnelled packets
+ * @embms_iface: Iface to receive embms traffic
+ * @embms_tunneling_status : Current EMBMS Status
+ * @no_of_tmgi_sessions : Number of current active TMGI sessions
+ * @lock : Lock for concurrency scenarios
+ * @ip_ident : IP identification number to be used for sent packets
+ *
+ * This tructure holds module specific information which is
+ * used throughout the module to maintain consistency
+ */
+
+struct embms_info_internal {
+	atomic_t device_under_use;
+	int embms_data_port;
+	char embms_iface[EMBMS_MAX_IFACE_NAME];
+	int embms_tunneling_status;
+	int no_of_tmgi_sessions;
+	/*lock to prevent concurrent access*/
+	spinlock_t lock;
+	atomic_t ip_ident;
+};
+
+/* This ioctl is used to add a new client entry to tunneling module.
+ * Entry params are populated in the struct used for ioctl
+ */
+
+#define ADD_EMBMS_TUNNEL _IOW(EMBMS_IOC_MAGIC, 0, \
+		struct tmgi_to_clnt_info_update)
+
+/* This ioctl is used to delete a client entry for a particular
+ * TMGI from tunneling module.
+ * Entry params are populated in the struct used for ioctl
+ */
+
+#define DEL_EMBMS_TUNNEL _IOW(EMBMS_IOC_MAGIC, 1, \
+		struct tmgi_to_clnt_info_update)
+
+/* This ioctl is used to delete a TMGI entry completely
+ * from tunneling module.
+ * Entry params are populated in the struct used for ioctl
+ */
+
+#define TMGI_DEACTIVATE _IOW(EMBMS_IOC_MAGIC, 2, \
+		struct tmgi_to_clnt_info_update)
+
+/* This ioctl is used to delete client entry completely
+ * from tunneling module.
+ * Entry params are populated in the struct used for ioctl
+ */
+
+#define CLIENT_DEACTIVATE _IOW(EMBMS_IOC_MAGIC, 3, \
+		struct tmgi_to_clnt_info_update)
+
+/* Gets the ON/OFF status of Tunneling module*/
+
+#define GET_EMBMS_TUNNELING_STATUS _IO(EMBMS_IOC_MAGIC, 4)
+
+/* Used to start tunneling. Argument is the port
+ * number to be used to send
+ * data to clients
+ */
+
+#define START_EMBMS_TUNNEL _IOW(EMBMS_IOC_MAGIC, 5, \
+		struct tmgi_to_clnt_info_update)
+
+/* Used to stop tunnleing*/
+
+#define STOP_EMBMS_TUNNEL _IO(EMBMS_IOC_MAGIC, 6)
+
+/* Return values indicating error status*/
+#define SUCCESS               0         /* Successful operation*/
+#define FAILURE               -1         /* Unsuccessful operation*/
+
+/* Error Condition Values*/
+#define ENOMEM                -2        /* Out of memory*/
+#define EBADPARAM             -3        /* Incorrect parameters passed*/
+#define ENOEFFECT             -4        /* No Effect*/
+
+#endif
+
diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c
index 30d875d..f85b08b 100644
--- a/net/ieee802154/6lowpan/reassembly.c
+++ b/net/ieee802154/6lowpan/reassembly.c
@@ -580,19 +580,14 @@
 {
 	struct netns_ieee802154_lowpan *ieee802154_lowpan =
 		net_ieee802154_lowpan(net);
-	int res;
 
 	ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
 	ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH;
 	ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT;
 
-	res = inet_frags_init_net(&ieee802154_lowpan->frags);
-	if (res)
-		return res;
-	res = lowpan_frags_ns_sysctl_register(net);
-	if (res)
-		inet_frags_uninit_net(&ieee802154_lowpan->frags);
-	return res;
+	inet_frags_init_net(&ieee802154_lowpan->frags);
+
+	return lowpan_frags_ns_sysctl_register(net);
 }
 
 static void __net_exit lowpan_frags_exit_net(struct net *net)
diff --git a/net/ipc_router/ipc_router_core.c b/net/ipc_router/ipc_router_core.c
index a28b1af..d38157d 100644
--- a/net/ipc_router/ipc_router_core.c
+++ b/net/ipc_router/ipc_router_core.c
@@ -2925,6 +2925,10 @@
 	}
 
 	temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
+	if (!temp_skb) {
+		IPC_RTR_ERR("%s: Empty skb\n", __func__);
+		return -EINVAL;
+	}
 	align_size = ALIGN_SIZE(pkt->length);
 	skb_put(temp_skb, align_size);
 	pkt->length += align_size;
@@ -3089,6 +3093,11 @@
 	}
 
 	temp_skb = skb_peek_tail(pkt->pkt_fragment_q);
+	if (!temp_skb) {
+		IPC_RTR_ERR("%s: Abort invalid pkt\n", __func__);
+		ret = -EINVAL;
+		goto out_write_pkt;
+	}
 	align_size = ALIGN_SIZE(pkt->length);
 	skb_put(temp_skb, align_size);
 	pkt->length += align_size;
@@ -3408,7 +3417,8 @@
 	align_size = ALIGN_SIZE(data_len);
 	if (align_size) {
 		temp_skb = skb_peek_tail((*pkt)->pkt_fragment_q);
-		skb_trim(temp_skb, (temp_skb->len - align_size));
+		if (temp_skb)
+			skb_trim(temp_skb, (temp_skb->len - align_size));
 	}
 	return data_len;
 }
diff --git a/net/ipc_router/ipc_router_socket.c b/net/ipc_router/ipc_router_socket.c
index 02242a1..a758a09 100644
--- a/net/ipc_router/ipc_router_socket.c
+++ b/net/ipc_router/ipc_router_socket.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -141,6 +141,10 @@
 	hdr = &pkt->hdr;
 	if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
 		temp = skb_peek(pkt->pkt_fragment_q);
+		if (!temp || !temp->data) {
+			IPC_RTR_ERR("%s: Invalid skb\n", __func__);
+			return -EINVAL;
+		}
 		ctl_msg = (union rr_control_msg *)(temp->data);
 		addr->family = AF_MSM_IPC;
 		addr->address.addrtype = MSM_IPC_ADDR_ID;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index ceddf42..2b887c5 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1714,6 +1714,13 @@
 	net->ipv4.sysctl_ip_dynaddr = 0;
 	net->ipv4.sysctl_ip_early_demux = 1;
 
+	/* Some igmp sysctl, whose values are always used */
+	net->ipv4.sysctl_igmp_max_memberships = 20;
+	net->ipv4.sysctl_igmp_max_msf = 10;
+	/* IGMP reports for link-local multicast groups are enabled by default */
+	net->ipv4.sysctl_igmp_llm_reports = 1;
+	net->ipv4.sysctl_igmp_qrv = 2;
+
 	return 0;
 }
 
diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c
index f2a7102..22377c8 100644
--- a/net/ipv4/ah4.c
+++ b/net/ipv4/ah4.c
@@ -270,6 +270,9 @@
 	int ihl = ip_hdrlen(skb);
 	int ah_hlen = (ah->hdrlen + 2) << 2;
 
+	if (err)
+		goto out;
+
 	work_iph = AH_SKB_CB(skb)->tmp;
 	auth_data = ah_tmp_auth(work_iph, ihl);
 	icv = ah_tmp_icv(ahp->ahash, auth_data, ahp->icv_trunc_len);
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index 37f4578..2ec005c 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -319,7 +319,7 @@
 	int ret, no_addr;
 	struct fib_result res;
 	struct flowi4 fl4;
-	struct net *net;
+	struct net *net = dev_net(dev);
 	bool dev_match;
 
 	fl4.flowi4_oif = 0;
@@ -332,6 +332,7 @@
 	fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
 	fl4.flowi4_tun_key.tun_id = 0;
 	fl4.flowi4_flags = 0;
+	fl4.flowi4_uid = sock_net_uid(net, NULL);
 
 	no_addr = idev->ifa_list == NULL;
 
@@ -339,13 +340,12 @@
 
 	trace_fib_validate_source(dev, &fl4);
 
-	net = dev_net(dev);
 	if (fib_lookup(net, &fl4, &res, 0))
 		goto last_resort;
 	if (res.type != RTN_UNICAST &&
 	    (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
 		goto e_inval;
-	if (!rpf && !fib_num_tclassid_users(dev_net(dev)) &&
+	if (!rpf && !fib_num_tclassid_users(net) &&
 	    (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev)))
 		goto last_resort;
 	fib_combine_itag(itag, &res);
@@ -1320,13 +1320,14 @@
 
 void __init ip_fib_init(void)
 {
-	rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
-	rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
-	rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
+	fib_trie_init();
 
 	register_pernet_subsys(&fib_net_ops);
+
 	register_netdevice_notifier(&fib_netdev_notifier);
 	register_inetaddr_notifier(&fib_inetaddr_notifier);
 
-	fib_trie_init();
+	rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
+	rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
+	rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
 }
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 7563831..38c1c97 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -1044,15 +1044,17 @@
 	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
 	if (!fi)
 		goto failure;
-	fib_info_cnt++;
 	if (cfg->fc_mx) {
 		fi->fib_metrics = kzalloc(sizeof(*fi->fib_metrics), GFP_KERNEL);
-		if (!fi->fib_metrics)
-			goto failure;
+		if (unlikely(!fi->fib_metrics)) {
+			kfree(fi);
+			return ERR_PTR(err);
+		}
 		atomic_set(&fi->fib_metrics->refcnt, 1);
-	} else
+	} else {
 		fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics;
-
+	}
+	fib_info_cnt++;
 	fi->fib_net = net;
 	fi->fib_protocol = cfg->fc_protocol;
 	fi->fib_scope = cfg->fc_scope;
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 19930da..08575e3 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -2974,12 +2974,6 @@
 		goto out_sock;
 	}
 
-	/* Sysctl initialization */
-	net->ipv4.sysctl_igmp_max_memberships = 20;
-	net->ipv4.sysctl_igmp_max_msf = 10;
-	/* IGMP reports for link-local multicast groups are enabled by default */
-	net->ipv4.sysctl_igmp_llm_reports = 1;
-	net->ipv4.sysctl_igmp_qrv = 2;
 	return 0;
 
 out_sock:
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index b5e9317..631c0d0 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -234,10 +234,8 @@
 	cond_resched();
 
 	if (read_seqretry(&f->rnd_seqlock, seq) ||
-	    percpu_counter_sum(&nf->mem))
+	    sum_frag_mem_limit(nf))
 		goto evict_again;
-
-	percpu_counter_destroy(&nf->mem);
 }
 EXPORT_SYMBOL(inet_frags_exit_net);
 
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index bbe7f72..453db95 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -835,8 +835,6 @@
 
 static int __net_init ipv4_frags_init_net(struct net *net)
 {
-	int res;
-
 	/* Fragment cache limits.
 	 *
 	 * The fragment memory accounting code, (tries to) account for
@@ -862,13 +860,9 @@
 
 	net->ipv4.frags.max_dist = 64;
 
-	res = inet_frags_init_net(&net->ipv4.frags);
-	if (res)
-		return res;
-	res = ip4_frags_ns_ctl_register(net);
-	if (res)
-		inet_frags_uninit_net(&net->ipv4.frags);
-	return res;
+	inet_frags_init_net(&net->ipv4.frags);
+
+	return ip4_frags_ns_ctl_register(net);
 }
 
 static void __net_exit ipv4_frags_exit_net(struct net *net)
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 2c18bcf..e60f9fa 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -958,10 +958,12 @@
 		csummode = CHECKSUM_PARTIAL;
 
 	cork->length += length;
-	if (((length > mtu) || (skb && skb_is_gso(skb))) &&
+	if ((skb && skb_is_gso(skb)) ||
+	    ((length > mtu) &&
+	    (skb_queue_len(queue) <= 1) &&
 	    (sk->sk_protocol == IPPROTO_UDP) &&
 	    (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
-	    (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) {
+	    (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx)) {
 		err = ip_ufo_append_data(sk, queue, getfrag, from, length,
 					 hh_len, fragheaderlen, transhdrlen,
 					 maxfraglen, flags);
@@ -1277,6 +1279,7 @@
 		return -EINVAL;
 
 	if ((size + skb->len > mtu) &&
+	    (skb_queue_len(&sk->sk_write_queue) == 1) &&
 	    (sk->sk_protocol == IPPROTO_UDP) &&
 	    (rt->dst.dev->features & NETIF_F_UFO)) {
 		if (skb->ip_summed != CHECKSUM_PARTIAL)
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 5719d6b..bd7f183 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -609,8 +609,8 @@
 		ip_rt_put(rt);
 		goto tx_dropped;
 	}
-	iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, proto, key->tos,
-		      key->ttl, df, !net_eq(tunnel->net, dev_net(dev)));
+	iptunnel_xmit(NULL, rt, skb, fl4.saddr, fl4.daddr, proto, tos, ttl,
+		      df, !net_eq(tunnel->net, dev_net(dev)));
 	return;
 tx_error:
 	dev->stats.tx_errors++;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 5d7944f..b120b9b 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -168,6 +168,7 @@
 	struct ip_tunnel_parm *parms = &tunnel->parms;
 	struct dst_entry *dst = skb_dst(skb);
 	struct net_device *tdev;	/* Device to other host */
+	int pkt_len = skb->len;
 	int err;
 	int mtu;
 
@@ -229,7 +230,7 @@
 
 	err = dst_output(tunnel->net, skb->sk, skb);
 	if (net_xmit_eval(err) == 0)
-		err = skb->len;
+		err = pkt_len;
 	iptunnel_xmit_stats(dev, err);
 	return NETDEV_TX_OK;
 
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index d613309..af9fa59 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -282,6 +282,17 @@
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config IP_NF_TARGET_NATTYPE_MODULE
+	tristate "NATTYPE target support"
+	depends on NF_NAT
+	default m if NETFILTER_ADVANCED=n
+	help
+	  NATTYPE is a special case of NAT: used to support FULL Cone NAT
+	  and ADDRESS Restricted Cone NAT. All incoming connections are
+	  allowed if there is an outgoing connection using that port.
+
+	  To compile it as a module, choose M here.  If unsure, say N.
+
 config IP_NF_TARGET_NETMAP
 	tristate "NETMAP target support"
 	depends on NETFILTER_ADVANCED
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index 853328f..1429845 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -57,6 +57,7 @@
 obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
 obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
 obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
+obj-$(CONFIG_IP_NF_TARGET_NATTYPE_MODULE) += ipt_NATTYPE.o
 obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
 obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o
 
diff --git a/net/ipv4/netfilter/ipt_NATTYPE.c b/net/ipv4/netfilter/ipt_NATTYPE.c
new file mode 100644
index 0000000..b8d93e9
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_NATTYPE.c
@@ -0,0 +1,612 @@
+/* netfilter NATTYPE
+ * net/ipv4/netfilter/ipt_NATTYPE.c
+ * Endpoint Independent, Address Restricted and Port-Address Restricted
+ * NAT types' kernel side implementation.
+ *
+ * (C) Copyright 2011, Ubicom, Inc.
+ *
+ * This file is part of the Ubicom32 Linux Kernel Port.
+ *
+ * The Ubicom32 Linux Kernel Port is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The Ubicom32 Linux Kernel Port 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the Ubicom32 Linux Kernel Port.  If not,
+ * see <http://www.gnu.org/licenses/>.
+ *
+ * Ubicom32 implementation derived from
+ * Cameo's implementation(with many thanks):
+ */
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/udp.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/module.h>
+#include <net/protocol.h>
+#include <net/checksum.h>
+#include <net/ip.h>
+#include <linux/tcp.h>
+#include <net/netfilter/nf_conntrack.h>
+#include <net/netfilter/nf_conntrack_core.h>
+#include <net/netfilter/nf_nat_rule.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ipt_NATTYPE.h>
+#include <linux/atomic.h>
+
+#if !defined(NATTYPE_DEBUG)
+#define DEBUGP(type, args...)
+#else
+static const char * const types[] = {"TYPE_PORT_ADDRESS_RESTRICTED",
+			"TYPE_ENDPOINT_INDEPENDENT",
+			"TYPE_ADDRESS_RESTRICTED"};
+static const char * const modes[] = {"MODE_DNAT", "MODE_FORWARD_IN",
+			"MODE_FORWARD_OUT"};
+#define DEBUGP(args...) pr_debug(args)
+#endif
+
+/* netfilter NATTYPE TODO:
+ * Add magic value checks to data structure.
+ */
+struct ipt_nattype {
+	struct list_head list;
+	struct timer_list timeout;
+	unsigned char is_valid;
+	unsigned short proto;		/* Protocol: TCP or UDP */
+	struct nf_nat_ipv4_range range;	/* LAN side src info*/
+	unsigned short nat_port;	/* Routed NAT port */
+	unsigned int dest_addr;	/* Original egress packets dst addr */
+	unsigned short dest_port;/* Original egress packets destination port */
+};
+
+/* TODO: It might be better to use a hash table for performance in
+ * heavy traffic.
+ */
+static LIST_HEAD(nattype_list);
+static DEFINE_SPINLOCK(nattype_lock);
+
+/* netfilter NATTYPE
+ * nattype_nte_debug_print()
+ */
+static void nattype_nte_debug_print(const struct ipt_nattype *nte,
+				    const char *s)
+{
+	DEBUGP("%p: %s - proto[%d], src[%pI4:%d], nat[<x>:%d], dest[%pI4:%d]\n",
+	       nte, s, nte->proto,
+		&nte->range.min_ip, ntohs(nte->range.min.all),
+		ntohs(nte->nat_port),
+		&nte->dest_addr, ntohs(nte->dest_port));
+}
+
+/* netfilter NATTYPE nattype_free()
+ * Free the object.
+ */
+static void nattype_free(struct ipt_nattype *nte)
+{
+	nattype_nte_debug_print(nte, "free");
+	kfree(nte);
+}
+
+/* netfilter NATTYPE nattype_refresh_timer()
+ * Refresh the timer for this object.
+ */
+bool nattype_refresh_timer_impl(unsigned long nat_type)
+{
+	struct ipt_nattype *nte = (struct ipt_nattype *)nat_type;
+
+	if (!nte)
+		return false;
+	spin_lock_bh(&nattype_lock);
+	if (!nte->is_valid) {
+		spin_unlock_bh(&nattype_lock);
+		return false;
+	}
+	if (del_timer(&nte->timeout)) {
+		nte->timeout.expires = jiffies + NATTYPE_TIMEOUT * HZ;
+		add_timer(&nte->timeout);
+		spin_unlock_bh(&nattype_lock);
+		return true;
+	}
+	spin_unlock_bh(&nattype_lock);
+	return false;
+}
+
+/* netfilter NATTYPE nattype_timer_timeout()
+ * The timer has gone off, self-destruct
+ */
+static void nattype_timer_timeout(unsigned long in_nattype)
+{
+	struct ipt_nattype *nte = (void *)in_nattype;
+
+	/* netfilter NATTYPE
+	 * The race with list deletion is solved by ensuring
+	 * that either this code or the list deletion code
+	 * but not both will remove the oject.
+	 */
+	nattype_nte_debug_print(nte, "timeout");
+	spin_lock_bh(&nattype_lock);
+	list_del(&nte->list);
+	memset(nte, 0, sizeof(struct ipt_nattype));
+	spin_unlock_bh(&nattype_lock);
+	nattype_free(nte);
+}
+
+/* netfilter NATTYPE nattype_packet_in_match()
+ * Ingress packet, try to match with this nattype entry.
+ */
+static bool nattype_packet_in_match(const struct ipt_nattype *nte,
+				    struct sk_buff *skb,
+				    const struct ipt_nattype_info *info)
+{
+	const struct iphdr *iph = ip_hdr(skb);
+	u16 dst_port = 0;
+
+	/* If the protocols are not the same, no sense in looking
+	 * further.
+	 */
+	if (nte->proto != iph->protocol) {
+		DEBUGP("nattype_packet_in_match: protocol failed: nte proto:"
+		DEBUGP(" %d, packet proto: %d\n",
+		       nte->proto, iph->protocol);
+		return false;
+	}
+
+	 /* In ADDRESS_RESTRICT, the egress destination must match the source
+	  * of this ingress packet.
+	  */
+	if (info->type == TYPE_ADDRESS_RESTRICTED) {
+		if (nte->dest_addr != iph->saddr) {
+			DEBUGP("nattype_packet_in_match: dest/src check");
+			DEBUGP(" failed: dest_addr: %pI4, src dest: %pI4\n",
+			       &nte->dest_addr, &iph->saddr);
+			return false;
+		}
+	}
+
+	/* Obtain the destination port value for TCP or UDP.  The nattype
+	 * entries are stored in native (not host).
+	 */
+	if (iph->protocol == IPPROTO_TCP) {
+		struct tcphdr _tcph;
+		struct tcphdr *tcph;
+
+		tcph = skb_header_pointer(skb, ip_hdrlen(skb),
+					  sizeof(_tcph), &_tcph);
+		if (!tcph)
+			return false;
+		dst_port = tcph->dest;
+	} else if (iph->protocol == IPPROTO_UDP) {
+		struct udphdr _udph;
+		struct udphdr *udph;
+
+		udph = skb_header_pointer(skb, ip_hdrlen(skb),
+					  sizeof(_udph), &_udph);
+		if (!udph)
+			return false;
+		dst_port = udph->dest;
+	}
+
+	/* Our NAT port must match the ingress pacekt's
+	 * destination packet.
+	 */
+	if (nte->nat_port != dst_port) {
+		DEBUGP("nattype_packet_in_match fail: ");
+		DEBUGP(" nat port: %d,dest_port: %d\n",
+		       ntohs(nte->nat_port), ntohs(dst_port));
+		return false;
+	}
+
+	/* In either EI or AR mode, the ingress packet's src port
+	 * can be anything.
+	 */
+	nattype_nte_debug_print(nte, "INGRESS MATCH");
+	return true;
+}
+
+/* netfilter NATTYPE nattype_compare
+ * Compare two entries, return true if relevant fields are the same.
+ */
+static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2)
+{
+	/* netfilter NATTYPE Protocol
+	 * compare.
+	 */
+	if (n1->proto != n2->proto) {
+		DEBUGP("nattype_compare: protocol mismatch: %d:%d\n",
+		       n1->proto, n2->proto);
+		return false;
+	}
+
+	 /* netfilter NATTYPE LAN Source compare.
+	  * Since we always keep min/max values the same,
+	  * just compare the min values.
+	  */
+	if (n1->range.min_ip != n2->range.min_ip) {
+		DEBUGP("nattype_compare: r.min_ip mismatch: %pI4:%pI4\n",
+		       &n1->range.min_ip, &n2->range.min_ip);
+		return false;
+	}
+
+	if (n1->range.min.all != n2->range.min.all) {
+		DEBUGP("nattype_compare: r.min mismatch: %d:%d\n",
+		       ntohs(n1->range.min.all),
+		       ntohs(n2->range.min.all));
+		return false;
+	}
+
+	/* netfilter NATTYPE
+	 * NAT port
+	 */
+	if (n1->nat_port != n2->nat_port) {
+		DEBUGP("nattype_compare: nat_port mistmatch: %d:%d\n",
+		       ntohs(n1->nat_port), ntohs(n2->nat_port));
+		return false;
+	}
+
+	/* netfilter NATTYPE
+	 * Destination compare
+	 */
+	if (n1->dest_addr != n2->dest_addr) {
+		DEBUGP("nattype_compare: dest_addr mismatch: %pI4:%pI4\n",
+		       &n1->dest_addr, &n2->dest_addr);
+		return false;
+	}
+
+	if (n1->dest_port != n2->dest_port) {
+		DEBUGP("nattype_compare: dest_port mismatch: %d:%d\n",
+		       ntohs(n1->dest_port), ntohs(n2->dest_port));
+		return false;
+	}
+	return true;
+}
+
+ /**
+  *  netfilter NATTYPE nattype_nat()
+  * Ingress packet on PRE_ROUTING hook, find match, update conntrack
+  * to allow
+  **/
+static unsigned int nattype_nat(struct sk_buff *skb,
+				const struct xt_action_param *par)
+{
+	struct ipt_nattype *nte;
+
+	if (par->hooknum != NF_INET_PRE_ROUTING)
+		return XT_CONTINUE;
+	spin_lock_bh(&nattype_lock);
+	list_for_each_entry(nte, &nattype_list, list) {
+		struct nf_conn *ct;
+		enum ip_conntrack_info ctinfo;
+		struct nf_nat_ipv4_range newrange;
+		unsigned int ret;
+
+		if (!nattype_packet_in_match(nte, skb, par->targinfo))
+			continue;
+
+		/* Copy the LAN source data into the ingress' pacekts
+		 * conntrack in the reply direction.
+		 */
+		newrange = nte->range;
+		spin_unlock_bh(&nattype_lock);
+
+		/* netfilter NATTYPE Find the
+		 * ingress packet's conntrack.
+		 */
+		ct = nf_ct_get(skb, &ctinfo);
+		if (!ct) {
+			DEBUGP("ingress packet conntrack not found\n");
+			return XT_CONTINUE;
+		}
+
+		/* Expand the ingress conntrack
+		 * to include the reply as source
+		 */
+		DEBUGP("Expand ingress conntrack=%p, type=%d, src[%pI4:%d]\n",
+			ct, ctinfo, &newrange.min_ip, ntohs(newrange.min.all));
+		ct->nattype_entry = (unsigned long)nte;
+		ret = nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
+		DEBUGP("Expand returned: %d\n", ret);
+		return ret;
+	}
+	spin_unlock_bh(&nattype_lock);
+	return XT_CONTINUE;
+}
+
+/* netfilter NATTYPE nattype_forward()
+ * Ingress and Egress packet forwarding hook
+ */
+static unsigned int nattype_forward(struct sk_buff *skb,
+				    const struct xt_action_param *par)
+{
+	const struct iphdr *iph = ip_hdr(skb);
+	void *protoh = (void *)iph + iph->ihl * 4;
+	struct ipt_nattype *nte;
+	struct ipt_nattype *nte2;
+	struct nf_conn *ct;
+	enum ip_conntrack_info ctinfo;
+	const struct ipt_nattype_info *info = par->targinfo;
+	u16 nat_port;
+
+	if (par->hooknum != NF_INET_FORWARD)
+		return XT_CONTINUE;
+
+	/* Ingress packet,
+	 * refresh the timer if we find an entry.
+	 */
+	if (info->mode == MODE_FORWARD_IN) {
+		spin_lock_bh(&nattype_lock);
+		list_for_each_entry(nte, &nattype_list, list) {
+			/* netfilter NATTYPE
+			 * Compare the ingress packet with the existing
+			 * entries looking for a match.
+			 */
+			if (!nattype_packet_in_match(nte, skb, info))
+				continue;
+
+			spin_unlock_bh(&nattype_lock);
+			/* netfilter NATTYPE
+			 * Refresh the timer, if we fail, break
+			 * out and forward fail as though we never
+			 * found the entry.
+			 */
+			if (!nattype_refresh_timer((unsigned long)nte))
+				break;
+
+			/* netfilter NATTYPE
+			 * The entry is found and refreshed, the
+			 * entry values should not change so print
+			 * them outside the lock.
+			 */
+			nattype_nte_debug_print(nte, "refresh");
+			DEBUGP("FORWARD_IN_ACCEPT\n");
+			return NF_ACCEPT;
+		}
+		spin_unlock_bh(&nattype_lock);
+		DEBUGP("FORWARD_IN_FAIL\n");
+		return XT_CONTINUE;
+	}
+
+	/* netfilter NATTYPE
+	 * Egress packet, create a new rule in our list.  If conntrack does
+	 * not have an entry, skip this packet.
+	 */
+	ct = nf_ct_get(skb, &ctinfo);
+	if (!ct || (ctinfo == IP_CT_NEW && ctinfo == IP_CT_RELATED))
+		return XT_CONTINUE;
+
+	nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all;
+
+	/* netfilter NATTYPE
+	 * Allocate a new entry
+	 */
+	nte = kzalloc(sizeof(*nte), GFP_ATOMIC | __GFP_NOWARN);
+	if (!nte) {
+		DEBUGP("kernel malloc fail\n");
+		return XT_CONTINUE;
+	}
+
+	INIT_LIST_HEAD(&nte->list);
+
+	nte->proto = iph->protocol;
+	nte->nat_port = nat_port;
+	nte->dest_addr = iph->daddr;
+	nte->range.min_ip = iph->saddr;
+	nte->range.max_ip = nte->range.min_ip;
+
+	/* netfilter NATTYPE
+	 * TOOD: Would it be better to get this information from the
+	 * conntrack instead of the headers.
+	 */
+	if (iph->protocol == IPPROTO_TCP) {
+		nte->range.min.tcp.port = ((struct tcphdr *)protoh)->source;
+		nte->range.max.tcp.port = nte->range.min.tcp.port;
+		nte->dest_port = ((struct tcphdr *)protoh)->dest;
+	} else if (iph->protocol == IPPROTO_UDP) {
+		nte->range.min.udp.port = ((struct udphdr *)protoh)->source;
+		nte->range.max.udp.port = nte->range.min.udp.port;
+		nte->dest_port = ((struct udphdr *)protoh)->dest;
+	}
+	nte->range.flags = (NF_NAT_RANGE_MAP_IPS |
+			NF_NAT_RANGE_PROTO_SPECIFIED);
+
+	/* netfilter NATTYPE
+	 * Initialize the self-destruct timer.
+	 */
+	init_timer(&nte->timeout);
+	nte->timeout.data = (unsigned long)nte;
+	nte->timeout.function = nattype_timer_timeout;
+
+	/* netfilter NATTYPE
+	 * We have created the new nte; however, it might not be unique.
+	 * Search the list for a matching entry.  If found, throw away
+	 * the new entry and refresh the old.  If not found, atomically
+	 * insert the new entry on the list.
+	 */
+	spin_lock_bh(&nattype_lock);
+	list_for_each_entry(nte2, &nattype_list, list) {
+		if (!nattype_compare(nte, nte2))
+			continue;
+		spin_unlock_bh(&nattype_lock);
+		/* netfilter NATTYPE
+		 * If we can not refresh this entry, insert our new
+		 * entry as this one is timed out and will be removed
+		 * from the list shortly.
+		 */
+		if (!nattype_refresh_timer((unsigned long)nte2))
+			break;
+
+		/* netfilter NATTYPE
+		 * Found and refreshed an existing entry.  Its values
+		 * do not change so print the values outside of the lock.
+		 *
+		 * Free up the new entry.
+		 */
+		nattype_nte_debug_print(nte2, "refresh");
+		nattype_free(nte);
+		return XT_CONTINUE;
+	}
+
+	/* netfilter NATTYPE
+	 * Add the new entry to the list.
+	 */
+	nte->timeout.expires = jiffies + (NATTYPE_TIMEOUT  * HZ);
+	add_timer(&nte->timeout);
+	list_add(&nte->list, &nattype_list);
+	ct->nattype_entry = (unsigned long)nte;
+	nte->is_valid = 1;
+	spin_unlock_bh(&nattype_lock);
+	nattype_nte_debug_print(nte, "ADD");
+	return XT_CONTINUE;
+}
+
+/* netfilter NATTYPE
+ * nattype_target()
+ *	One of the iptables hooks has a packet for us to analyze, do so.
+ */
+static unsigned int nattype_target(struct sk_buff *skb,
+				   const struct xt_action_param *par)
+{
+	const struct ipt_nattype_info *info = par->targinfo;
+	const struct iphdr *iph = ip_hdr(skb);
+
+	/* netfilter NATTYPE
+	 * The default behavior for Linux is PORT and ADDRESS restricted. So
+	 * we do not need to create rules/entries if we are in that mode.
+	 */
+	if (info->type == TYPE_PORT_ADDRESS_RESTRICTED)
+		return XT_CONTINUE;
+
+	/* netfilter NATTYPE
+	 * Check if we have enough data in the skb.
+	 */
+	if (skb->len < ip_hdrlen(skb))
+		return XT_CONTINUE;
+
+	/* netfilter NATTYPE
+	 * We can not perform endpoint filtering on anything but UDP and TCP.
+	 */
+	if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
+		return XT_CONTINUE;
+
+	/* netfilter NATTYPE
+	 * Check for LAND attack and ignore.
+	 */
+	if (iph->daddr == iph->saddr)
+		return XT_CONTINUE;
+
+	/* netfilter NATTYPE
+	 * Check that we have valid source and destination addresses.
+	 */
+	if ((iph->daddr == (__be32)0) || (iph->saddr == (__be32)0))
+		return XT_CONTINUE;
+
+	DEBUGP("nattype_target: type = %s, mode = %s\n",
+	       types[info->type], modes[info->mode]);
+
+	/* netfilter NATTYPE
+	 * TODO: why have mode at all since par->hooknum provides
+	 * this information?
+	 */
+	switch (info->mode) {
+	case MODE_DNAT:
+		return nattype_nat(skb, par);
+	case MODE_FORWARD_OUT:
+	case MODE_FORWARD_IN:
+		return nattype_forward(skb, par);
+	}
+	return XT_CONTINUE;
+}
+
+/* netfilter NATTYPE
+ * nattype_check()
+ *	check info (mode/type) set by iptables.
+ */
+static int nattype_check(const struct xt_tgchk_param *par)
+{
+	const struct ipt_nattype_info *info = par->targinfo;
+	struct list_head *cur, *tmp;
+
+	if ((info->type != TYPE_PORT_ADDRESS_RESTRICTED) &&
+	    (info->type != TYPE_ENDPOINT_INDEPENDENT) &&
+		(info->type != TYPE_ADDRESS_RESTRICTED)) {
+		DEBUGP("nattype_check: unknown type: %d\n", info->type);
+		return -EINVAL;
+	}
+
+	if (info->mode != MODE_DNAT && info->mode != MODE_FORWARD_IN &&
+	    info->mode != MODE_FORWARD_OUT) {
+		DEBUGP("nattype_check: unknown mode - %d.\n", info->mode);
+		return -EINVAL;
+	}
+
+	DEBUGP("nattype_check: type = %s, mode = %s\n",
+	       types[info->type], modes[info->mode]);
+
+	if (par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
+		(1 << NF_INET_FORWARD))) {
+		DEBUGP("nattype_check: bad hooks %x.\n", par->hook_mask);
+		return -EINVAL;
+	}
+
+	/* netfilter NATTYPE
+	 * Remove all entries from the nattype list.
+	 */
+drain:
+	spin_lock_bh(&nattype_lock);
+	list_for_each_safe(cur, tmp, &nattype_list) {
+		struct ipt_nattype *nte = (void *)cur;
+
+		/* netfilter NATTYPE
+		 * If the timeout is in process, it will tear
+		 * us down.  Since it is waiting on the spinlock
+		 * we have to give up the spinlock to give the
+		 * timeout on another CPU a chance to run.
+		 */
+		if (!del_timer(&nte->timeout)) {
+			spin_unlock_bh(&nattype_lock);
+			goto drain;
+		}
+
+		DEBUGP("%p: removing from list\n", nte);
+		list_del(&nte->list);
+		spin_unlock_bh(&nattype_lock);
+		nattype_free(nte);
+		goto drain;
+	}
+	spin_unlock_bh(&nattype_lock);
+	return 0;
+}
+
+static struct xt_target nattype = {
+	.name		= "NATTYPE",
+	.family		= NFPROTO_IPV4,
+	.target		= nattype_target,
+	.checkentry	= nattype_check,
+	.targetsize	= sizeof(struct ipt_nattype_info),
+	.hooks		= ((1 << NF_INET_PRE_ROUTING) |
+				(1 << NF_INET_FORWARD)),
+	.me		= THIS_MODULE,
+};
+
+static int __init init(void)
+{
+	WARN_ON(nattype_refresh_timer);
+	RCU_INIT_POINTER(nattype_refresh_timer, nattype_refresh_timer_impl);
+	return xt_register_target(&nattype);
+}
+
+static void __exit fini(void)
+{
+	xt_unregister_target(&nattype);
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_LICENSE("GPL");
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index c9b52c3..5a8f7c3 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -1304,6 +1304,7 @@
 static void __exit nf_nat_snmp_basic_fini(void)
 {
 	RCU_INIT_POINTER(nf_nat_snmp_hook, NULL);
+	synchronize_rcu();
 	nf_conntrack_helper_unregister(&snmp_trap_helper);
 }
 
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index fd82202..146d861 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -126,6 +126,8 @@
 	/* ip_route_me_harder expects skb->dst to be set */
 	skb_dst_set_noref(nskb, skb_dst(oldskb));
 
+	nskb->mark = IP4_REPLY_MARK(net, oldskb->mark);
+
 	skb_reserve(nskb, LL_MAX_HEADER);
 	niph = nf_reject_iphdr_put(nskb, oldskb, IPPROTO_TCP,
 				   ip4_dst_hoplimit(skb_dst(nskb)));
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 18c6e79..cd632e6 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1253,7 +1253,7 @@
 	if (mtu)
 		return mtu;
 
-	mtu = dst->dev->mtu;
+	mtu = READ_ONCE(dst->dev->mtu);
 
 	if (unlikely(dst_metric_locked(dst, RTAX_MTU))) {
 		if (rt->rt_uses_gateway && mtu > 576)
@@ -1860,6 +1860,7 @@
 	fl4.flowi4_flags = 0;
 	fl4.daddr = daddr;
 	fl4.saddr = saddr;
+	fl4.flowi4_uid = sock_net_uid(net, NULL);
 	err = fib_lookup(net, &fl4, &res, 0);
 	if (err != 0) {
 		if (!IN_DEV_FORWARD(in_dev))
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 0dc6286..f56a668 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -334,6 +334,7 @@
 	treq = tcp_rsk(req);
 	treq->rcv_isn		= ntohl(th->seq) - 1;
 	treq->snt_isn		= cookie;
+	treq->txhash		= net_tx_rndhash();
 	req->mss		= mss;
 	ireq->ir_num		= ntohs(th->dest);
 	ireq->ir_rmt_port	= th->source;
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 51ac77e..ccc484a 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -1010,7 +1010,7 @@
 		.data		= &init_net.ipv4.sysctl_tcp_notsent_lowat,
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_douintvec,
 	},
 #ifdef CONFIG_IP_ROUTE_MULTIPATH
 	{
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 7c90130..4946e8f 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2306,6 +2306,10 @@
 	tcp_set_ca_state(sk, TCP_CA_Open);
 	tcp_clear_retrans(tp);
 	inet_csk_delack_init(sk);
+	/* Initialize rcv_mss to TCP_MIN_MSS to avoid division by 0
+	 * issue in __tcp_select_window()
+	 */
+	icsk->icsk_ack.rcv_mss = TCP_MIN_MSS;
 	tcp_init_send_head(sk);
 	memset(&tp->rx_opt, 0, sizeof(tp->rx_opt));
 	__sk_dst_reset(sk);
diff --git a/net/ipv4/tcp_bbr.c b/net/ipv4/tcp_bbr.c
index 0ea66c2..cb8db34 100644
--- a/net/ipv4/tcp_bbr.c
+++ b/net/ipv4/tcp_bbr.c
@@ -83,7 +83,8 @@
 		cwnd_gain:10,	/* current gain for setting cwnd */
 		full_bw_cnt:3,	/* number of rounds without large bw gains */
 		cycle_idx:3,	/* current index in pacing_gain cycle array */
-		unused_b:6;
+		has_seen_rtt:1, /* have we seen an RTT sample yet? */
+		unused_b:5;
 	u32	prior_cwnd;	/* prior cwnd upon entering loss recovery */
 	u32	full_bw;	/* recent bw, to estimate if pipe is full */
 };
@@ -182,6 +183,35 @@
 	return rate >> BW_SCALE;
 }
 
+/* Convert a BBR bw and gain factor to a pacing rate in bytes per second. */
+static u32 bbr_bw_to_pacing_rate(struct sock *sk, u32 bw, int gain)
+{
+	u64 rate = bw;
+
+	rate = bbr_rate_bytes_per_sec(sk, rate, gain);
+	rate = min_t(u64, rate, sk->sk_max_pacing_rate);
+	return rate;
+}
+
+/* Initialize pacing rate to: high_gain * init_cwnd / RTT. */
+static void bbr_init_pacing_rate_from_rtt(struct sock *sk)
+{
+	struct tcp_sock *tp = tcp_sk(sk);
+	struct bbr *bbr = inet_csk_ca(sk);
+	u64 bw;
+	u32 rtt_us;
+
+	if (tp->srtt_us) {		/* any RTT sample yet? */
+		rtt_us = max(tp->srtt_us >> 3, 1U);
+		bbr->has_seen_rtt = 1;
+	} else {			 /* no RTT sample yet */
+		rtt_us = USEC_PER_MSEC;	 /* use nominal default RTT */
+	}
+	bw = (u64)tp->snd_cwnd * BW_UNIT;
+	do_div(bw, rtt_us);
+	sk->sk_pacing_rate = bbr_bw_to_pacing_rate(sk, bw, bbr_high_gain);
+}
+
 /* Pace using current bw estimate and a gain factor. In order to help drive the
  * network toward lower queues while maintaining high utilization and low
  * latency, the average pacing rate aims to be slightly (~1%) lower than the
@@ -191,12 +221,13 @@
  */
 static void bbr_set_pacing_rate(struct sock *sk, u32 bw, int gain)
 {
+	struct tcp_sock *tp = tcp_sk(sk);
 	struct bbr *bbr = inet_csk_ca(sk);
-	u64 rate = bw;
+	u32 rate = bbr_bw_to_pacing_rate(sk, bw, gain);
 
-	rate = bbr_rate_bytes_per_sec(sk, rate, gain);
-	rate = min_t(u64, rate, sk->sk_max_pacing_rate);
-	if (bbr->mode != BBR_STARTUP || rate > sk->sk_pacing_rate)
+	if (unlikely(!bbr->has_seen_rtt && tp->srtt_us))
+		bbr_init_pacing_rate_from_rtt(sk);
+	if (bbr_full_bw_reached(sk) || rate > sk->sk_pacing_rate)
 		sk->sk_pacing_rate = rate;
 }
 
@@ -769,7 +800,6 @@
 {
 	struct tcp_sock *tp = tcp_sk(sk);
 	struct bbr *bbr = inet_csk_ca(sk);
-	u64 bw;
 
 	bbr->prior_cwnd = 0;
 	bbr->tso_segs_goal = 0;	 /* default segs per skb until first ACK */
@@ -785,11 +815,8 @@
 
 	minmax_reset(&bbr->bw, bbr->rtt_cnt, 0);  /* init max bw to 0 */
 
-	/* Initialize pacing rate to: high_gain * init_cwnd / RTT. */
-	bw = (u64)tp->snd_cwnd * BW_UNIT;
-	do_div(bw, (tp->srtt_us >> 3) ? : USEC_PER_MSEC);
-	sk->sk_pacing_rate = 0;		/* force an update of sk_pacing_rate */
-	bbr_set_pacing_rate(sk, bw, bbr_high_gain);
+	bbr->has_seen_rtt = 0;
+	bbr_init_pacing_rate_from_rtt(sk);
 
 	bbr->restore_cwnd = 0;
 	bbr->round_start = 0;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 3d980d6..491b03a 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -2561,8 +2561,8 @@
 		return;
 
 	/* Reset cwnd to ssthresh in CWR or Recovery (unless it's undone) */
-	if (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR ||
-	    (tp->undo_marker && tp->snd_ssthresh < TCP_INFINITE_SSTHRESH)) {
+	if (tp->snd_ssthresh < TCP_INFINITE_SSTHRESH &&
+	    (inet_csk(sk)->icsk_ca_state == TCP_CA_CWR || tp->undo_marker)) {
 		tp->snd_cwnd = tp->snd_ssthresh;
 		tp->snd_cwnd_stamp = tcp_time_stamp;
 	}
@@ -3037,8 +3037,7 @@
 			/* delta may not be positive if the socket is locked
 			 * when the retrans timer fires and is rescheduled.
 			 */
-			if (delta > 0)
-				rto = delta;
+			rto = max(delta, 1);
 		}
 		inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, rto,
 					  TCP_RTO_MAX);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index e6bf011..dd08d16 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -914,6 +914,7 @@
 	struct tcp_skb_cb *tcb;
 	struct tcp_out_options opts;
 	unsigned int tcp_options_size, tcp_header_size;
+	struct sk_buff *oskb = NULL;
 	struct tcp_md5sig_key *md5;
 	struct tcphdr *th;
 	int err;
@@ -922,11 +923,9 @@
 	tp = tcp_sk(sk);
 
 	if (clone_it) {
-		skb_mstamp_get(&skb->skb_mstamp);
 		TCP_SKB_CB(skb)->tx.in_flight = TCP_SKB_CB(skb)->end_seq
 			- tp->snd_una;
-		tcp_rate_skb_sent(sk, skb);
-
+		oskb = skb;
 		if (unlikely(skb_cloned(skb)))
 			skb = pskb_copy(skb, gfp_mask);
 		else
@@ -934,6 +933,7 @@
 		if (unlikely(!skb))
 			return -ENOBUFS;
 	}
+	skb_mstamp_get(&skb->skb_mstamp);
 
 	inet = inet_sk(sk);
 	tcb = TCP_SKB_CB(skb);
@@ -1035,12 +1035,15 @@
 
 	err = icsk->icsk_af_ops->queue_xmit(sk, skb, &inet->cork.fl);
 
-	if (likely(err <= 0))
-		return err;
-
-	tcp_enter_cwr(sk);
-
-	return net_xmit_eval(err);
+	if (unlikely(err > 0)) {
+		tcp_enter_cwr(sk);
+		err = net_xmit_eval(err);
+	}
+	if (!err && oskb) {
+		skb_mstamp_get(&oskb->skb_mstamp);
+		tcp_rate_skb_sent(sk, oskb);
+	}
+	return err;
 }
 
 /* This routine just queues the buffer for sending.
@@ -2709,10 +2712,11 @@
 		     skb_headroom(skb) >= 0xFFFF)) {
 		struct sk_buff *nskb;
 
-		skb_mstamp_get(&skb->skb_mstamp);
 		nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
 		err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
 			     -ENOBUFS;
+		if (!err)
+			skb_mstamp_get(&skb->skb_mstamp);
 	} else {
 		err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
 	}
@@ -3325,6 +3329,10 @@
 		goto done;
 	}
 
+	/* data was not sent, this is our new send_head */
+	sk->sk_send_head = syn_data;
+	tp->packets_out -= tcp_skb_pcount(syn_data);
+
 fallback:
 	/* Send a regular SYN with Fast Open cookie request option */
 	if (fo->cookie.len > 0)
@@ -3344,6 +3352,9 @@
 	struct sk_buff *buff;
 	int err;
 
+	if (inet_csk(sk)->icsk_af_ops->rebuild_header(sk))
+		return -EHOSTUNREACH; /* Routing failure or similar. */
+
 	tcp_connect_init(sk);
 
 	if (unlikely(tp->repair)) {
@@ -3371,6 +3382,11 @@
 	 */
 	tp->snd_nxt = tp->write_seq;
 	tp->pushed_seq = tp->write_seq;
+	buff = tcp_send_head(sk);
+	if (unlikely(buff)) {
+		tp->snd_nxt	= TCP_SKB_CB(buff)->seq;
+		tp->pushed_seq	= TCP_SKB_CB(buff)->seq;
+	}
 	TCP_INC_STATS(sock_net(sk), TCP_MIB_ACTIVEOPENS);
 
 	/* Timer for repeating the SYN until an answer. */
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 732060d..d3d3ef6 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -686,7 +686,8 @@
 		goto death;
 	}
 
-	if (!sock_flag(sk, SOCK_KEEPOPEN) || sk->sk_state == TCP_CLOSE)
+	if (!sock_flag(sk, SOCK_KEEPOPEN) ||
+	    ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)))
 		goto out;
 
 	elapsed = keepalive_time_when(tp);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index fe24424..200c9b6 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -818,7 +818,7 @@
 	if (is_udplite)  				 /*     UDP-Lite      */
 		csum = udplite_csum(skb);
 
-	else if (sk->sk_no_check_tx) {   /* UDP csum disabled */
+	else if (sk->sk_no_check_tx && !skb_is_gso(skb)) {   /* UDP csum off */
 
 		skb->ip_summed = CHECKSUM_NONE;
 		goto send;
@@ -1627,14 +1627,18 @@
 /* For TCP sockets, sk_rx_dst is protected by socket lock
  * For UDP, we use xchg() to guard against concurrent changes.
  */
-static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+bool udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
 {
 	struct dst_entry *old;
 
-	dst_hold(dst);
-	old = xchg(&sk->sk_rx_dst, dst);
-	dst_release(old);
+	if (dst_hold_safe(dst)) {
+		old = xchg(&sk->sk_rx_dst, dst);
+		dst_release(old);
+		return old != dst;
+	}
+	return false;
 }
+EXPORT_SYMBOL(udp_sk_rx_dst_set);
 
 /*
  *	Multicasts and broadcasts go to each listener.
@@ -1957,13 +1961,11 @@
 	if (dst)
 		dst = dst_check(dst, 0);
 	if (dst) {
-		/* DST_NOCACHE can not be used without taking a reference */
-		if (dst->flags & DST_NOCACHE) {
-			if (likely(atomic_inc_not_zero(&dst->__refcnt)))
-				skb_dst_set(skb, dst);
-		} else {
-			skb_dst_set_noref(skb, dst);
-		}
+		/* set noref for now.
+		 * any place which wants to hold dst has to call
+		 * dst_hold_safe()
+		 */
+		skb_dst_set_noref(skb, dst);
 	}
 }
 
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index b2be1d9..0932c85 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -29,6 +29,7 @@
 	u16 mac_len = skb->mac_len;
 	int udp_offset, outer_hlen;
 	__wsum partial;
+	bool need_ipsec;
 
 	if (unlikely(!pskb_may_pull(skb, tnl_hlen)))
 		goto out;
@@ -62,8 +63,10 @@
 
 	ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP);
 
+	need_ipsec = skb_dst(skb) && dst_xfrm(skb_dst(skb));
 	/* Try to offload checksum if possible */
 	offload_csum = !!(need_csum &&
+			  !need_ipsec &&
 			  (skb->dev->features &
 			   (is_ipv6 ? (NETIF_F_HW_CSUM | NETIF_F_IPV6_CSUM) :
 				      (NETIF_F_HW_CSUM | NETIF_F_IP_CSUM))));
@@ -232,7 +235,7 @@
 	if (uh->check == 0)
 		uh->check = CSUM_MANGLED_0;
 
-	skb->ip_summed = CHECKSUM_NONE;
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
 
 	/* If there is no outer header we can fake a checksum offload
 	 * due to the fact that we have already done the checksum in
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 6a7ff69..7f9a8df 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -22,7 +22,8 @@
 static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4,
 					    int tos, int oif,
 					    const xfrm_address_t *saddr,
-					    const xfrm_address_t *daddr)
+					    const xfrm_address_t *daddr,
+					    u32 mark)
 {
 	struct rtable *rt;
 
@@ -30,6 +31,7 @@
 	fl4->daddr = daddr->a4;
 	fl4->flowi4_tos = tos;
 	fl4->flowi4_oif = l3mdev_master_ifindex_by_index(net, oif);
+	fl4->flowi4_mark = mark;
 	if (saddr)
 		fl4->saddr = saddr->a4;
 
@@ -44,20 +46,22 @@
 
 static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif,
 					  const xfrm_address_t *saddr,
-					  const xfrm_address_t *daddr)
+					  const xfrm_address_t *daddr,
+					  u32 mark)
 {
 	struct flowi4 fl4;
 
-	return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr);
+	return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark);
 }
 
 static int xfrm4_get_saddr(struct net *net, int oif,
-			   xfrm_address_t *saddr, xfrm_address_t *daddr)
+			   xfrm_address_t *saddr, xfrm_address_t *daddr,
+			   u32 mark)
 {
 	struct dst_entry *dst;
 	struct flowi4 fl4;
 
-	dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr);
+	dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark);
 	if (IS_ERR(dst))
 		return -EHOSTUNREACH;
 
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index fe5305a..371312b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -5485,7 +5485,7 @@
 		 * our DAD process, so we don't need
 		 * to do it again
 		 */
-		if (!(ifp->rt->rt6i_node))
+		if (!rcu_access_pointer(ifp->rt->rt6i_node))
 			ip6_ins_rt(ifp->rt);
 		if (ifp->idev->cnf.forwarding)
 			addrconf_join_anycast(ifp);
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index edf15f0..1778af9 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -250,8 +250,14 @@
 	 */
 
 	err = ip6_datagram_dst_update(sk, true);
-	if (err)
+	if (err) {
+		/* Reset daddr and dport so that udp_v6_early_demux()
+		 * fails to find this socket
+		 */
+		memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr));
+		inet->inet_dport = 0;
 		goto out;
+	}
 
 	sk->sk_state = TCP_ESTABLISHED;
 	sk_set_txhash(sk);
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 4345ee3..7ba9a6e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -148,11 +148,23 @@
 	return fn;
 }
 
-static void node_free(struct fib6_node *fn)
+static void node_free_immediate(struct fib6_node *fn)
 {
 	kmem_cache_free(fib6_node_kmem, fn);
 }
 
+static void node_free_rcu(struct rcu_head *head)
+{
+	struct fib6_node *fn = container_of(head, struct fib6_node, rcu);
+
+	kmem_cache_free(fib6_node_kmem, fn);
+}
+
+static void node_free(struct fib6_node *fn)
+{
+	call_rcu(&fn->rcu, node_free_rcu);
+}
+
 static void rt6_rcu_free(struct rt6_info *rt)
 {
 	call_rcu(&rt->dst.rcu_head, dst_rcu_free);
@@ -189,6 +201,12 @@
 	}
 }
 
+static void fib6_free_table(struct fib6_table *table)
+{
+	inetpeer_invalidate_tree(&table->tb6_peers);
+	kfree(table);
+}
+
 static void fib6_link_table(struct net *net, struct fib6_table *tb)
 {
 	unsigned int h;
@@ -589,9 +607,9 @@
 
 		if (!in || !ln) {
 			if (in)
-				node_free(in);
+				node_free_immediate(in);
 			if (ln)
-				node_free(ln);
+				node_free_immediate(ln);
 			return ERR_PTR(-ENOMEM);
 		}
 
@@ -862,7 +880,7 @@
 
 		rt->dst.rt6_next = iter;
 		*ins = rt;
-		rt->rt6i_node = fn;
+		rcu_assign_pointer(rt->rt6i_node, fn);
 		atomic_inc(&rt->rt6i_ref);
 		inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags);
 		info->nl_net->ipv6.rt6_stats->fib_rt_entries++;
@@ -887,7 +905,7 @@
 			return err;
 
 		*ins = rt;
-		rt->rt6i_node = fn;
+		rcu_assign_pointer(rt->rt6i_node, fn);
 		rt->dst.rt6_next = iter->dst.rt6_next;
 		atomic_inc(&rt->rt6i_ref);
 		inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE);
@@ -896,7 +914,10 @@
 			fn->fn_flags |= RTN_RTINFO;
 		}
 		nsiblings = iter->rt6i_nsiblings;
+		iter->rt6i_node = NULL;
 		fib6_purge_rt(iter, fn, info->nl_net);
+		if (fn->rr_ptr == iter)
+			fn->rr_ptr = NULL;
 		rt6_release(iter);
 
 		if (nsiblings) {
@@ -908,7 +929,10 @@
 					break;
 				if (rt6_qualify_for_ecmp(iter)) {
 					*ins = iter->dst.rt6_next;
+					iter->rt6i_node = NULL;
 					fib6_purge_rt(iter, fn, info->nl_net);
+					if (fn->rr_ptr == iter)
+						fn->rr_ptr = NULL;
 					rt6_release(iter);
 					nsiblings--;
 				} else {
@@ -997,7 +1021,7 @@
 			/* Create subtree root node */
 			sfn = node_alloc();
 			if (!sfn)
-				goto st_failure;
+				goto failure;
 
 			sfn->leaf = info->nl_net->ipv6.ip6_null_entry;
 			atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
@@ -1013,12 +1037,12 @@
 
 			if (IS_ERR(sn)) {
 				/* If it is failed, discard just allocated
-				   root, and then (in st_failure) stale node
+				   root, and then (in failure) stale node
 				   in main tree.
 				 */
-				node_free(sfn);
+				node_free_immediate(sfn);
 				err = PTR_ERR(sn);
-				goto st_failure;
+				goto failure;
 			}
 
 			/* Now link new subtree to main tree */
@@ -1032,7 +1056,7 @@
 
 			if (IS_ERR(sn)) {
 				err = PTR_ERR(sn);
-				goto st_failure;
+				goto failure;
 			}
 		}
 
@@ -1074,22 +1098,22 @@
 			atomic_inc(&pn->leaf->rt6i_ref);
 		}
 #endif
-		if (!(rt->dst.flags & DST_NOCACHE))
-			dst_free(&rt->dst);
+		goto failure;
 	}
 	return err;
 
-#ifdef CONFIG_IPV6_SUBTREES
-	/* Subtree creation failed, probably main tree node
-	   is orphan. If it is, shoot it.
+failure:
+	/* fn->leaf could be NULL if fn is an intermediate node and we
+	 * failed to add the new route to it in both subtree creation
+	 * failure and fib6_add_rt2node() failure case.
+	 * In both cases, fib6_repair_tree() should be called to fix
+	 * fn->leaf.
 	 */
-st_failure:
 	if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT)))
 		fib6_repair_tree(info->nl_net, fn);
 	if (!(rt->dst.flags & DST_NOCACHE))
 		dst_free(&rt->dst);
 	return err;
-#endif
 }
 
 /*
@@ -1443,8 +1467,9 @@
 
 int fib6_del(struct rt6_info *rt, struct nl_info *info)
 {
+	struct fib6_node *fn = rcu_dereference_protected(rt->rt6i_node,
+				    lockdep_is_held(&rt->rt6i_table->tb6_lock));
 	struct net *net = info->nl_net;
-	struct fib6_node *fn = rt->rt6i_node;
 	struct rt6_info **rtp;
 
 #if RT6_DEBUG >= 2
@@ -1633,7 +1658,9 @@
 			if (res) {
 #if RT6_DEBUG >= 2
 				pr_debug("%s: del failed: rt=%p@%p err=%d\n",
-					 __func__, rt, rt->rt6i_node, res);
+					 __func__, rt,
+					 rcu_access_pointer(rt->rt6i_node),
+					 res);
 #endif
 				continue;
 			}
@@ -1874,15 +1901,22 @@
 
 static void fib6_net_exit(struct net *net)
 {
+	unsigned int i;
+
 	rt6_ifdown(net, NULL);
 	del_timer_sync(&net->ipv6.ip6_fib_timer);
 
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
-	inetpeer_invalidate_tree(&net->ipv6.fib6_local_tbl->tb6_peers);
-	kfree(net->ipv6.fib6_local_tbl);
-#endif
-	inetpeer_invalidate_tree(&net->ipv6.fib6_main_tbl->tb6_peers);
-	kfree(net->ipv6.fib6_main_tbl);
+	for (i = 0; i < FIB6_TABLE_HASHSZ; i++) {
+		struct hlist_head *head = &net->ipv6.fib_table_hash[i];
+		struct hlist_node *tmp;
+		struct fib6_table *tb;
+
+		hlist_for_each_entry_safe(tb, tmp, head, tb6_hlist) {
+			hlist_del(&tb->tb6_hlist);
+			fib6_free_table(tb);
+		}
+	}
+
 	kfree(net->ipv6.fib_table_hash);
 	kfree(net->ipv6.rt6_stats);
 }
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index c329a15..48e6e75 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -432,7 +432,9 @@
 		}
 		break;
 	case ICMPV6_PKT_TOOBIG:
-		mtu = be32_to_cpu(info) - offset;
+		mtu = be32_to_cpu(info) - offset - t->tun_hlen;
+		if (t->dev->type == ARPHRD_ETHER)
+			mtu -= ETH_HLEN;
 		if (mtu < IPV6_MIN_MTU)
 			mtu = IPV6_MIN_MTU;
 		t->dev->mtu = mtu;
@@ -938,24 +940,25 @@
 }
 
 static int ip6gre_header(struct sk_buff *skb, struct net_device *dev,
-			unsigned short type,
-			const void *daddr, const void *saddr, unsigned int len)
+			 unsigned short type, const void *daddr,
+			 const void *saddr, unsigned int len)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
-	struct ipv6hdr *ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen);
-	__be16 *p = (__be16 *)(ipv6h+1);
+	struct ipv6hdr *ipv6h;
+	__be16 *p;
 
-	ip6_flow_hdr(ipv6h, 0,
-		     ip6_make_flowlabel(dev_net(dev), skb,
-					t->fl.u.ip6.flowlabel, true,
-					&t->fl.u.ip6));
+	ipv6h = (struct ipv6hdr *)skb_push(skb, t->hlen + sizeof(*ipv6h));
+	ip6_flow_hdr(ipv6h, 0, ip6_make_flowlabel(dev_net(dev), skb,
+						  t->fl.u.ip6.flowlabel,
+						  true, &t->fl.u.ip6));
 	ipv6h->hop_limit = t->parms.hop_limit;
 	ipv6h->nexthdr = NEXTHDR_GRE;
 	ipv6h->saddr = t->parms.laddr;
 	ipv6h->daddr = t->parms.raddr;
 
-	p[0]		= t->parms.o_flags;
-	p[1]		= htons(type);
+	p = (__be16 *)(ipv6h + 1);
+	p[0] = t->parms.o_flags;
+	p[1] = htons(type);
 
 	/*
 	 *	Set the source hardware address.
@@ -1299,6 +1302,7 @@
 	dev->features |= NETIF_F_NETNS_LOCAL;
 	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+	netif_keep_dst(dev);
 }
 
 static bool ip6gre_netlink_encap_parms(struct nlattr *data[],
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 4403260..821aa0b 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -671,8 +671,6 @@
 		*prevhdr = NEXTHDR_FRAGMENT;
 		tmp_hdr = kmemdup(skb_network_header(skb), hlen, GFP_ATOMIC);
 		if (!tmp_hdr) {
-			IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-				      IPSTATS_MIB_FRAGFAILS);
 			err = -ENOMEM;
 			goto fail;
 		}
@@ -791,8 +789,6 @@
 		frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
 				 hroom + troom, GFP_ATOMIC);
 		if (!frag) {
-			IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
-				      IPSTATS_MIB_FRAGFAILS);
 			err = -ENOMEM;
 			goto fail;
 		}
@@ -1384,11 +1380,12 @@
 	 */
 
 	cork->length += length;
-	if ((((length + fragheaderlen) > mtu) ||
-	     (skb && skb_is_gso(skb))) &&
+	if ((skb && skb_is_gso(skb)) ||
+	    (((length + fragheaderlen) > mtu) &&
+	    (skb_queue_len(queue) <= 1) &&
 	    (sk->sk_protocol == IPPROTO_UDP) &&
 	    (rt->dst.dev->features & NETIF_F_UFO) && !rt->dst.header_len &&
-	    (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) {
+	    (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk))) {
 		err = ip6_ufo_append_data(sk, queue, getfrag, from, length,
 					  hh_len, fragheaderlen, exthdrlen,
 					  transhdrlen, mtu, flags, fl6);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index b44e9f5..64aefc2 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1042,6 +1042,7 @@
 	struct dst_entry *dst = NULL, *ndst = NULL;
 	struct net_device *tdev;
 	int mtu;
+	unsigned int eth_hlen = t->dev->type == ARPHRD_ETHER ? ETH_HLEN : 0;
 	unsigned int psh_hlen = sizeof(struct ipv6hdr) + t->encap_hlen;
 	unsigned int max_headroom = psh_hlen;
 	bool use_cache = false;
@@ -1120,7 +1121,7 @@
 				     t->parms.name);
 		goto tx_err_dst_release;
 	}
-	mtu = dst_mtu(dst) - psh_hlen - t->tun_hlen;
+	mtu = dst_mtu(dst) - eth_hlen - psh_hlen - t->tun_hlen;
 	if (encap_limit >= 0) {
 		max_headroom += 8;
 		mtu -= 8;
@@ -1129,7 +1130,7 @@
 		mtu = IPV6_MIN_MTU;
 	if (skb_dst(skb) && !t->parms.collect_md)
 		skb_dst(skb)->ops->update_pmtu(skb_dst(skb), NULL, skb, mtu);
-	if (skb->len - t->tun_hlen > mtu && !skb_is_gso(skb)) {
+	if (skb->len - t->tun_hlen - eth_hlen > mtu && !skb_is_gso(skb)) {
 		*pmtu = mtu;
 		err = -EMSGSIZE;
 		goto tx_err_dst_release;
@@ -2235,6 +2236,9 @@
 {
 	int  err;
 
+	if (!ipv6_mod_enabled())
+		return -EOPNOTSUPP;
+
 	err = register_pernet_device(&ip6_tnl_net_ops);
 	if (err < 0)
 		goto out_pernet;
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index bbeedff..da64b20 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -445,6 +445,7 @@
 	struct dst_entry *dst = skb_dst(skb);
 	struct net_device *tdev;
 	struct xfrm_state *x;
+	int pkt_len = skb->len;
 	int err = -1;
 	int mtu;
 
@@ -498,7 +499,7 @@
 		struct pcpu_sw_netstats *tstats = this_cpu_ptr(dev->tstats);
 
 		u64_stats_update_begin(&tstats->syncp);
-		tstats->tx_bytes += skb->len;
+		tstats->tx_bytes += pkt_len;
 		tstats->tx_packets++;
 		u64_stats_update_end(&tstats->syncp);
 	} else {
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index e10a04c..cf336d6 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -135,6 +135,18 @@
 
 if IP6_NF_IPTABLES
 
+config IP6_NF_IPTABLES_128
+	tristate "128 bit arithmetic for iptables matching"
+	depends on IP6_NF_IPTABLES
+	help
+	  This enables 128 bit matching in ip6tables to help optimize cases
+          where there is no match required. ip6tables matching for ipv6 always
+	  has a mask if an address is specified for match. Adding a check for
+	  mask prior to that helps to improve performance as it avoids the
+	  masked comparison.
+
+	  Note that this feature depends on the architecture. If unsure, say N.
+
 # The simple matches.
 config IP6_NF_MATCH_AH
 	tristate '"ah" match support'
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index 55aacea..1cee193 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -72,14 +72,23 @@
 {
 	unsigned long ret;
 	const struct ipv6hdr *ipv6 = ipv6_hdr(skb);
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+	const __uint128_t *ulm1 = (const __uint128_t *)&ip6info->smsk;
+	const __uint128_t *ulm2 = (const __uint128_t *)&ip6info->dmsk;
+#endif
 
-	if (NF_INVF(ip6info, IP6T_INV_SRCIP,
-		    ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
-					 &ip6info->src)) ||
-	    NF_INVF(ip6info, IP6T_INV_DSTIP,
-		    ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
-					 &ip6info->dst)))
-		return false;
+#if IS_ENABLED(IP6_NF_IPTABLES_128)
+	if (*ulm1 || *ulm2)
+#endif
+	{
+		if (NF_INVF(ip6info, IP6T_INV_SRCIP,
+			    ipv6_masked_addr_cmp(&ipv6->saddr, &ip6info->smsk,
+						 &ip6info->src)) ||
+		    NF_INVF(ip6info, IP6T_INV_DSTIP,
+			    ipv6_masked_addr_cmp(&ipv6->daddr, &ip6info->dmsk,
+						 &ip6info->dst)))
+			return false;
+	}
 
 	ret = ifname_compare_aligned(indev, ip6info->iniface, ip6info->iniface_mask);
 
diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c
index 986d4ca..b263bf3 100644
--- a/net/ipv6/netfilter/nf_conntrack_reasm.c
+++ b/net/ipv6/netfilter/nf_conntrack_reasm.c
@@ -622,18 +622,12 @@
 
 static int nf_ct_net_init(struct net *net)
 {
-	int res;
-
 	net->nf_frag.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
 	net->nf_frag.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
 	net->nf_frag.frags.timeout = IPV6_FRAG_TIMEOUT;
-	res = inet_frags_init_net(&net->nf_frag.frags);
-	if (res)
-		return res;
-	res = nf_ct_frag6_sysctl_register(net);
-	if (res)
-		inet_frags_uninit_net(&net->nf_frag.frags);
-	return res;
+	inet_frags_init_net(&net->nf_frag.frags);
+
+	return nf_ct_frag6_sysctl_register(net);
 }
 
 static void nf_ct_net_exit(struct net *net)
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index 1009040..eedee5d 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -157,6 +157,7 @@
 	fl6.fl6_sport = otcph->dest;
 	fl6.fl6_dport = otcph->source;
 	fl6.flowi6_oif = l3mdev_master_ifindex(skb_dst(oldskb)->dev);
+	fl6.flowi6_mark = IP6_REPLY_MARK(net, oldskb->mark);
 	security_skb_classify_flow(oldskb, flowi6_to_flowi(&fl6));
 	dst = ip6_route_output(net, NULL, &fl6);
 	if (dst->error) {
@@ -180,6 +181,8 @@
 
 	skb_dst_set(nskb, dst);
 
+	nskb->mark = fl6.flowi6_mark;
+
 	skb_reserve(nskb, hh_len + dst->header_len);
 	ip6h = nf_reject_ip6hdr_put(nskb, oldskb, IPPROTO_TCP,
 				    ip6_dst_hoplimit(dst));
diff --git a/net/ipv6/output_core.c b/net/ipv6/output_core.c
index e9065b8..a338bbc 100644
--- a/net/ipv6/output_core.c
+++ b/net/ipv6/output_core.c
@@ -78,7 +78,7 @@
 
 int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
 {
-	u16 offset = sizeof(struct ipv6hdr);
+	unsigned int offset = sizeof(struct ipv6hdr);
 	unsigned int packet_len = skb_tail_pointer(skb) -
 		skb_network_header(skb);
 	int found_rhdr = 0;
@@ -112,6 +112,8 @@
 		exthdr = (struct ipv6_opt_hdr *)(skb_network_header(skb) +
 						 offset);
 		offset += ipv6_optlen(exthdr);
+		if (offset > IPV6_MAXPLEN)
+			return -EINVAL;
 		*nexthdr = &exthdr->nexthdr;
 	}
 
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 3815e85..e585c0a 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -709,19 +709,13 @@
 
 static int __net_init ipv6_frags_init_net(struct net *net)
 {
-	int res;
-
 	net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH;
 	net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH;
 	net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT;
 
-	res = inet_frags_init_net(&net->ipv6.frags);
-	if (res)
-		return res;
-	res = ip6_frags_ns_sysctl_register(net);
-	if (res)
-		inet_frags_uninit_net(&net->ipv6.frags);
-	return res;
+	inet_frags_init_net(&net->ipv6.frags);
+
+	return ip6_frags_ns_sysctl_register(net);
 }
 
 static void __net_exit ipv6_frags_exit_net(struct net *net)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index d8123f6..5acd855 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1261,7 +1261,9 @@
 
 static struct dst_entry *rt6_check(struct rt6_info *rt, u32 cookie)
 {
-	if (!rt->rt6i_node || (rt->rt6i_node->fn_sernum != cookie))
+	u32 rt_cookie = 0;
+
+	if (!rt6_get_cookie_safe(rt, &rt_cookie) || rt_cookie != cookie)
 		return NULL;
 
 	if (rt6_check_expired(rt))
@@ -1329,8 +1331,14 @@
 		if (rt->rt6i_flags & RTF_CACHE) {
 			dst_hold(&rt->dst);
 			ip6_del_rt(rt);
-		} else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) {
-			rt->rt6i_node->fn_sernum = -1;
+		} else {
+			struct fib6_node *fn;
+
+			rcu_read_lock();
+			fn = rcu_dereference(rt->rt6i_node);
+			if (fn && (rt->rt6i_flags & RTF_DEFAULT))
+				fn->fn_sernum = -1;
+			rcu_read_unlock();
 		}
 	}
 }
@@ -1347,7 +1355,8 @@
 static bool rt6_cache_allowed_for_pmtu(const struct rt6_info *rt)
 {
 	return !(rt->rt6i_flags & RTF_CACHE) &&
-		(rt->rt6i_flags & RTF_PCPU || rt->rt6i_node);
+		(rt->rt6i_flags & RTF_PCPU ||
+		 rcu_access_pointer(rt->rt6i_node));
 }
 
 static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk,
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 97830a6..a67174e 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -209,6 +209,7 @@
 	treq->snt_synack.v64	= 0;
 	treq->rcv_isn = ntohl(th->seq) - 1;
 	treq->snt_isn = cookie;
+	treq->txhash = net_tx_rndhash();
 
 	/*
 	 * We need to lookup the dst_entry to get the correct window size.
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index c925fd9..2db6808 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -46,6 +46,7 @@
 #include <net/tcp_states.h>
 #include <net/ip6_checksum.h>
 #include <net/xfrm.h>
+#include <net/inet_hashtables.h>
 #include <net/inet6_hashtables.h>
 #include <net/busy_poll.h>
 #include <net/sock_reuseport.h>
@@ -277,11 +278,7 @@
 					  struct udp_table *udptable)
 {
 	const struct ipv6hdr *iph = ipv6_hdr(skb);
-	struct sock *sk;
 
-	sk = skb_steal_sock(skb);
-	if (unlikely(sk))
-		return sk;
 	return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport,
 				 &iph->daddr, dport, inet6_iif(skb),
 				 udptable, skb);
@@ -757,6 +754,15 @@
 	return 0;
 }
 
+static void udp6_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
+{
+	if (udp_sk_rx_dst_set(sk, dst)) {
+		const struct rt6_info *rt = (const struct rt6_info *)dst;
+
+		inet6_sk(sk)->rx_dst_cookie = rt6_get_cookie(rt);
+	}
+}
+
 int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
 		   int proto)
 {
@@ -799,6 +805,24 @@
 	if (udp6_csum_init(skb, uh, proto))
 		goto csum_error;
 
+	/* Check if the socket is already available, e.g. due to early demux */
+	sk = skb_steal_sock(skb);
+	if (sk) {
+		struct dst_entry *dst = skb_dst(skb);
+		int ret;
+
+		if (unlikely(sk->sk_rx_dst != dst))
+			udp6_sk_rx_dst_set(sk, dst);
+
+		ret = udpv6_queue_rcv_skb(sk, skb);
+		sock_put(sk);
+
+		/* a return value > 0 means to resubmit the input */
+		if (ret > 0)
+			return ret;
+		return 0;
+	}
+
 	/*
 	 *	Multicast receive code
 	 */
@@ -807,11 +831,6 @@
 				saddr, daddr, udptable, proto);
 
 	/* Unicast */
-
-	/*
-	 * check socket cache ... must talk to Alan about his plans
-	 * for sock caches... i'll skip this for now.
-	 */
 	sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
 	if (sk) {
 		int ret;
@@ -866,6 +885,71 @@
 	return 0;
 }
 
+static struct sock *__udp6_lib_demux_lookup(struct net *net,
+					    __be16 loc_port,
+					    const struct in6_addr *loc_addr,
+					    __be16 rmt_port,
+					    const struct in6_addr *rmt_addr,
+					    int dif)
+{
+	unsigned short hnum = ntohs(loc_port);
+	unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum);
+	unsigned int slot2 = hash2 & udp_table.mask;
+	struct udp_hslot *hslot2 = &udp_table.hash2[slot2];
+
+	const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum);
+	struct sock *sk;
+
+	udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) {
+		if (sk->sk_state == TCP_ESTABLISHED &&
+		    INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif))
+			return sk;
+		/* Only check first socket in chain */
+		break;
+	}
+	return NULL;
+}
+
+static void udp_v6_early_demux(struct sk_buff *skb)
+{
+	struct net *net = dev_net(skb->dev);
+	const struct udphdr *uh;
+	struct sock *sk;
+	struct dst_entry *dst;
+	int dif = skb->dev->ifindex;
+
+	if (!pskb_may_pull(skb, skb_transport_offset(skb) +
+	    sizeof(struct udphdr)))
+		return;
+
+	uh = udp_hdr(skb);
+
+	if (skb->pkt_type == PACKET_HOST)
+		sk = __udp6_lib_demux_lookup(net, uh->dest,
+					     &ipv6_hdr(skb)->daddr,
+					     uh->source, &ipv6_hdr(skb)->saddr,
+					     dif);
+	else
+		return;
+
+	if (!sk || !atomic_inc_not_zero_hint(&sk->sk_refcnt, 2))
+		return;
+
+	skb->sk = sk;
+	skb->destructor = sock_efree;
+	dst = READ_ONCE(sk->sk_rx_dst);
+
+	if (dst)
+		dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
+	if (dst) {
+		/* set noref for now.
+		 * any place which wants to hold dst has to call
+		 * dst_hold_safe()
+		 */
+		skb_dst_set_noref(skb, dst);
+	}
+}
+
 static __inline__ int udpv6_rcv(struct sk_buff *skb)
 {
 	return __udp6_lib_rcv(skb, &udp_table, IPPROTO_UDP);
@@ -915,6 +999,7 @@
 		 */
 		offset = skb_transport_offset(skb);
 		skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
+		csum = skb->csum;
 
 		skb->ip_summed = CHECKSUM_NONE;
 
@@ -1380,6 +1465,7 @@
 #endif
 
 static const struct inet6_protocol udpv6_protocol = {
+	.early_demux	=	udp_v6_early_demux,
 	.handler	=	udpv6_rcv,
 	.err_handler	=	udpv6_err,
 	.flags		=	INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index a2267f8..e7d378c 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -72,7 +72,7 @@
 		if (uh->check == 0)
 			uh->check = CSUM_MANGLED_0;
 
-		skb->ip_summed = CHECKSUM_NONE;
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
 
 		/* If there is no outer header we can fake a checksum offload
 		 * due to the fact that we have already done the checksum in
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index e0f71c0..4003b28 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -29,7 +29,8 @@
 
 static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif,
 					  const xfrm_address_t *saddr,
-					  const xfrm_address_t *daddr)
+					  const xfrm_address_t *daddr,
+					  u32 mark)
 {
 	struct flowi6 fl6;
 	struct dst_entry *dst;
@@ -38,6 +39,7 @@
 	memset(&fl6, 0, sizeof(fl6));
 	fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif);
 	fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF;
+	fl6.flowi6_mark = mark;
 	memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr));
 	if (saddr)
 		memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr));
@@ -54,12 +56,13 @@
 }
 
 static int xfrm6_get_saddr(struct net *net, int oif,
-			   xfrm_address_t *saddr, xfrm_address_t *daddr)
+			   xfrm_address_t *saddr, xfrm_address_t *daddr,
+			   u32 mark)
 {
 	struct dst_entry *dst;
 	struct net_device *dev;
 
-	dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr);
+	dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark);
 	if (IS_ERR(dst))
 		return -EHOSTUNREACH;
 
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 391c3cb..101ed6c 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -2223,7 +2223,7 @@
 {
 	struct sock *sk = sock->sk;
 	struct irda_sock *self = irda_sk(sk);
-	struct irda_device_list list;
+	struct irda_device_list list = { 0 };
 	struct irda_device_info *discoveries;
 	struct irda_ias_set *	ias_opt;	/* IAS get/query params */
 	struct ias_object *	ias_obj;	/* Object in IAS */
diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c
index fecad10..7eb0e8f 100644
--- a/net/kcm/kcmsock.c
+++ b/net/kcm/kcmsock.c
@@ -1381,6 +1381,10 @@
 	if (!csk)
 		return -EINVAL;
 
+	/* We must prevent loops or risk deadlock ! */
+	if (csk->sk_family == PF_KCM)
+		return -EOPNOTSUPP;
+
 	psock = kmem_cache_zalloc(kcm_psockp, GFP_KERNEL);
 	if (!psock)
 		return -ENOMEM;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 2e1050e..94bf810 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -228,7 +228,7 @@
 #define BROADCAST_ONE		1
 #define BROADCAST_REGISTERED	2
 #define BROADCAST_PROMISC_ONLY	4
-static int pfkey_broadcast(struct sk_buff *skb,
+static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation,
 			   int broadcast_flags, struct sock *one_sk,
 			   struct net *net)
 {
@@ -278,7 +278,7 @@
 	rcu_read_unlock();
 
 	if (one_sk != NULL)
-		err = pfkey_broadcast_one(skb, &skb2, GFP_KERNEL, one_sk);
+		err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk);
 
 	kfree_skb(skb2);
 	kfree_skb(skb);
@@ -311,7 +311,7 @@
 		hdr = (struct sadb_msg *) pfk->dump.skb->data;
 		hdr->sadb_msg_seq = 0;
 		hdr->sadb_msg_errno = rc;
-		pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
 				&pfk->sk, sock_net(&pfk->sk));
 		pfk->dump.skb = NULL;
 	}
@@ -355,7 +355,7 @@
 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) /
 			     sizeof(uint64_t));
 
-	pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk));
+	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk));
 
 	return 0;
 }
@@ -1396,7 +1396,7 @@
 
 	xfrm_state_put(x);
 
-	pfkey_broadcast(resp_skb, BROADCAST_ONE, sk, net);
+	pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net);
 
 	return 0;
 }
@@ -1483,7 +1483,7 @@
 	hdr->sadb_msg_seq = c->seq;
 	hdr->sadb_msg_pid = c->portid;
 
-	pfkey_broadcast(skb, BROADCAST_ALL, NULL, xs_net(x));
+	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x));
 
 	return 0;
 }
@@ -1596,7 +1596,7 @@
 	out_hdr->sadb_msg_reserved = 0;
 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
-	pfkey_broadcast(out_skb, BROADCAST_ONE, sk, sock_net(sk));
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk));
 
 	return 0;
 }
@@ -1701,8 +1701,8 @@
 		return -ENOBUFS;
 	}
 
-	pfkey_broadcast(supp_skb, BROADCAST_REGISTERED, sk, sock_net(sk));
-
+	pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk,
+			sock_net(sk));
 	return 0;
 }
 
@@ -1720,7 +1720,8 @@
 	hdr->sadb_msg_errno = (uint8_t) 0;
 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
 
-	return pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk));
+	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk,
+			       sock_net(sk));
 }
 
 static int key_notify_sa_flush(const struct km_event *c)
@@ -1741,7 +1742,7 @@
 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
 	hdr->sadb_msg_reserved = 0;
 
-	pfkey_broadcast(skb, BROADCAST_ALL, NULL, c->net);
+	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
 
 	return 0;
 }
@@ -1798,7 +1799,7 @@
 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
 
 	if (pfk->dump.skb)
-		pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
 				&pfk->sk, sock_net(&pfk->sk));
 	pfk->dump.skb = out_skb;
 
@@ -1886,7 +1887,7 @@
 		new_hdr->sadb_msg_errno = 0;
 	}
 
-	pfkey_broadcast(skb, BROADCAST_ALL, NULL, sock_net(sk));
+	pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk));
 	return 0;
 }
 
@@ -2219,7 +2220,7 @@
 	out_hdr->sadb_msg_errno = 0;
 	out_hdr->sadb_msg_seq = c->seq;
 	out_hdr->sadb_msg_pid = c->portid;
-	pfkey_broadcast(out_skb, BROADCAST_ALL, NULL, xp_net(xp));
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp));
 	return 0;
 
 }
@@ -2439,7 +2440,7 @@
 	out_hdr->sadb_msg_errno = 0;
 	out_hdr->sadb_msg_seq = hdr->sadb_msg_seq;
 	out_hdr->sadb_msg_pid = hdr->sadb_msg_pid;
-	pfkey_broadcast(out_skb, BROADCAST_ONE, sk, xp_net(xp));
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp));
 	err = 0;
 
 out:
@@ -2695,7 +2696,7 @@
 	out_hdr->sadb_msg_pid = pfk->dump.msg_portid;
 
 	if (pfk->dump.skb)
-		pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE,
+		pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE,
 				&pfk->sk, sock_net(&pfk->sk));
 	pfk->dump.skb = out_skb;
 
@@ -2752,7 +2753,7 @@
 	hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC;
 	hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t));
 	hdr->sadb_msg_reserved = 0;
-	pfkey_broadcast(skb_out, BROADCAST_ALL, NULL, c->net);
+	pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net);
 	return 0;
 
 }
@@ -2814,7 +2815,7 @@
 	void *ext_hdrs[SADB_EXT_MAX];
 	int err;
 
-	pfkey_broadcast(skb_clone(skb, GFP_KERNEL),
+	pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL,
 			BROADCAST_PROMISC_ONLY, NULL, sock_net(sk));
 
 	memset(ext_hdrs, 0, sizeof(ext_hdrs));
@@ -3036,7 +3037,8 @@
 	out_hdr->sadb_msg_seq = 0;
 	out_hdr->sadb_msg_pid = 0;
 
-	pfkey_broadcast(out_skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+	pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+			xs_net(x));
 	return 0;
 }
 
@@ -3226,7 +3228,8 @@
 		       xfrm_ctx->ctx_len);
 	}
 
-	return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+			       xs_net(x));
 }
 
 static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt,
@@ -3424,7 +3427,8 @@
 	n_port->sadb_x_nat_t_port_port = sport;
 	n_port->sadb_x_nat_t_port_reserved = 0;
 
-	return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x));
+	return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL,
+			       xs_net(x));
 }
 
 #ifdef CONFIG_NET_KEY_MIGRATE
@@ -3616,7 +3620,7 @@
 	}
 
 	/* broadcast migrate message to sockets */
-	pfkey_broadcast(skb, BROADCAST_ALL, NULL, &init_net);
+	pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net);
 
 	return 0;
 
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 3bce651..b06acd0 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1415,6 +1415,9 @@
 	struct sock *sk = NULL;
 
 	tunnel = container_of(work, struct l2tp_tunnel, del_work);
+
+	l2tp_tunnel_closeall(tunnel);
+
 	sk = l2tp_tunnel_sock_lookup(tunnel);
 	if (!sk)
 		goto out;
@@ -1734,15 +1737,12 @@
 
 /* This function is used by the netlink TUNNEL_DELETE command.
  */
-int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
+void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel)
 {
-	l2tp_tunnel_inc_refcount(tunnel);
-	l2tp_tunnel_closeall(tunnel);
-	if (false == queue_work(l2tp_wq, &tunnel->del_work)) {
-		l2tp_tunnel_dec_refcount(tunnel);
-		return 1;
+	if (!test_and_set_bit(0, &tunnel->dead)) {
+		l2tp_tunnel_inc_refcount(tunnel);
+		queue_work(l2tp_wq, &tunnel->del_work);
 	}
-	return 0;
 }
 EXPORT_SYMBOL_GPL(l2tp_tunnel_delete);
 
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 0095012..42419f1 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -169,6 +169,9 @@
 
 struct l2tp_tunnel {
 	int			magic;		/* Should be L2TP_TUNNEL_MAGIC */
+
+	unsigned long		dead;
+
 	struct rcu_head rcu;
 	rwlock_t		hlist_lock;	/* protect session_hlist */
 	struct hlist_head	session_hlist[L2TP_HASH_SIZE];
@@ -257,7 +260,7 @@
 		       u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg,
 		       struct l2tp_tunnel **tunnelp);
 void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
-int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
+void l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
 struct l2tp_session *l2tp_session_create(int priv_size,
 					 struct l2tp_tunnel *tunnel,
 					 u32 session_id, u32 peer_session_id,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 37bec0f..a7aa54f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -791,6 +791,7 @@
 static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
 			      bool going_down)
 {
+	struct ieee80211_sub_if_data *txq_sdata = sdata;
 	struct ieee80211_local *local = sdata->local;
 	struct fq *fq = &local->fq;
 	unsigned long flags;
@@ -931,6 +932,9 @@
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
+		txq_sdata = container_of(sdata->bss,
+					 struct ieee80211_sub_if_data, u.ap);
+
 		mutex_lock(&local->mtx);
 		list_del(&sdata->u.vlan.list);
 		mutex_unlock(&local->mtx);
@@ -1001,8 +1005,17 @@
 	}
 	spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
 
-	if (sdata->vif.txq) {
-		struct txq_info *txqi = to_txq_info(sdata->vif.txq);
+	if (txq_sdata->vif.txq) {
+		struct txq_info *txqi = to_txq_info(txq_sdata->vif.txq);
+
+		/*
+		 * FIXME FIXME
+		 *
+		 * We really shouldn't purge the *entire* txqi since that
+		 * contains frames for the other AP_VLANs (and possibly
+		 * the AP itself) as well, but there's no API in FQ now
+		 * to be able to filter.
+		 */
 
 		spin_lock_bh(&fq->lock);
 		ieee80211_txq_purge(local, txqi);
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index eede5c6..30bba53 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -707,6 +707,8 @@
 	if (!cookie)
 		return -ENOENT;
 
+	flush_work(&local->hw_roc_start);
+
 	mutex_lock(&local->mtx);
 	list_for_each_entry_safe(roc, tmp, &local->roc_list, list) {
 		if (!mgmt_tx && roc->cookie != cookie)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index b2c823ff..348700b4 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -688,7 +688,7 @@
 	}
 
 	/* No need to do anything if the driver does all */
-	if (ieee80211_hw_check(&local->hw, AP_LINK_PS))
+	if (ieee80211_hw_check(&local->hw, AP_LINK_PS) && !local->ops->set_tim)
 		return;
 
 	if (sta->dead)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index dd190ff..274c564 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -1277,11 +1277,6 @@
 	IEEE80211_SKB_CB(skb)->control.enqueue_time = codel_get_time();
 }
 
-static void ieee80211_set_skb_vif(struct sk_buff *skb, struct txq_info *txqi)
-{
-	IEEE80211_SKB_CB(skb)->control.vif = txqi->txq.vif;
-}
-
 static u32 codel_skb_len_func(const struct sk_buff *skb)
 {
 	return skb->len;
@@ -3388,6 +3383,7 @@
 	struct ieee80211_tx_info *info;
 	struct ieee80211_tx_data tx;
 	ieee80211_tx_result r;
+	struct ieee80211_vif *vif;
 
 	spin_lock_bh(&fq->lock);
 
@@ -3404,8 +3400,6 @@
 	if (!skb)
 		goto out;
 
-	ieee80211_set_skb_vif(skb, txqi);
-
 	hdr = (struct ieee80211_hdr *)skb->data;
 	info = IEEE80211_SKB_CB(skb);
 
@@ -3462,6 +3456,34 @@
 		}
 	}
 
+	switch (tx.sdata->vif.type) {
+	case NL80211_IFTYPE_MONITOR:
+		if (tx.sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
+			vif = &tx.sdata->vif;
+			break;
+		}
+		tx.sdata = rcu_dereference(local->monitor_sdata);
+		if (tx.sdata) {
+			vif = &tx.sdata->vif;
+			info->hw_queue =
+				vif->hw_queue[skb_get_queue_mapping(skb)];
+		} else if (ieee80211_hw_check(&local->hw, QUEUE_CONTROL)) {
+			ieee80211_free_txskb(&local->hw, skb);
+			goto begin;
+		} else {
+			vif = NULL;
+		}
+		break;
+	case NL80211_IFTYPE_AP_VLAN:
+		tx.sdata = container_of(tx.sdata->bss,
+					struct ieee80211_sub_if_data, u.ap);
+		/* fall through */
+	default:
+		vif = &tx.sdata->vif;
+		break;
+	}
+
+	IEEE80211_SKB_CB(skb)->control.vif = vif;
 out:
 	spin_unlock_bh(&fq->lock);
 
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index ff2d32e..c348c40 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -72,6 +72,9 @@
 struct hlist_nulls_head *nf_conntrack_hash __read_mostly;
 EXPORT_SYMBOL_GPL(nf_conntrack_hash);
 
+bool (*nattype_refresh_timer)(unsigned long nattype) __rcu __read_mostly;
+EXPORT_SYMBOL(nattype_refresh_timer);
+
 struct conntrack_gc_work {
 	struct delayed_work	dwork;
 	u32			last_bucket;
@@ -95,19 +98,26 @@
 
 void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)
 {
+	/* 1) Acquire the lock */
 	spin_lock(lock);
-	while (unlikely(nf_conntrack_locks_all)) {
-		spin_unlock(lock);
 
-		/*
-		 * Order the 'nf_conntrack_locks_all' load vs. the
-		 * spin_unlock_wait() loads below, to ensure
-		 * that 'nf_conntrack_locks_all_lock' is indeed held:
-		 */
-		smp_rmb(); /* spin_lock(&nf_conntrack_locks_all_lock) */
-		spin_unlock_wait(&nf_conntrack_locks_all_lock);
-		spin_lock(lock);
-	}
+	/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics
+	 * It pairs with the smp_store_release() in nf_conntrack_all_unlock()
+	 */
+	if (likely(smp_load_acquire(&nf_conntrack_locks_all) == false))
+		return;
+
+	/* fast path failed, unlock */
+	spin_unlock(lock);
+
+	/* Slow path 1) get global lock */
+	spin_lock(&nf_conntrack_locks_all_lock);
+
+	/* Slow path 2) get the lock we want */
+	spin_lock(lock);
+
+	/* Slow path 3) release the global lock */
+	spin_unlock(&nf_conntrack_locks_all_lock);
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_lock);
 
@@ -148,28 +158,27 @@
 	int i;
 
 	spin_lock(&nf_conntrack_locks_all_lock);
+
 	nf_conntrack_locks_all = true;
 
-	/*
-	 * Order the above store of 'nf_conntrack_locks_all' against
-	 * the spin_unlock_wait() loads below, such that if
-	 * nf_conntrack_lock() observes 'nf_conntrack_locks_all'
-	 * we must observe nf_conntrack_locks[] held:
-	 */
-	smp_mb(); /* spin_lock(&nf_conntrack_locks_all_lock) */
-
 	for (i = 0; i < CONNTRACK_LOCKS; i++) {
-		spin_unlock_wait(&nf_conntrack_locks[i]);
+		spin_lock(&nf_conntrack_locks[i]);
+
+		/* This spin_unlock provides the "release" to ensure that
+		 * nf_conntrack_locks_all==true is visible to everyone that
+		 * acquired spin_lock(&nf_conntrack_locks[]).
+		 */
+		spin_unlock(&nf_conntrack_locks[i]);
 	}
 }
 
 static void nf_conntrack_all_unlock(void)
 {
-	/*
-	 * All prior stores must be complete before we clear
+	/* All prior stores must be complete before we clear
 	 * 'nf_conntrack_locks_all'. Otherwise nf_conntrack_lock()
 	 * might observe the false value but not the entire
-	 * critical section:
+	 * critical section.
+	 * It pairs with the smp_load_acquire() in nf_conntrack_lock()
 	 */
 	smp_store_release(&nf_conntrack_locks_all, false);
 	spin_unlock(&nf_conntrack_locks_all_lock);
@@ -181,6 +190,9 @@
 unsigned int nf_conntrack_max __read_mostly;
 seqcount_t nf_conntrack_generation __read_mostly;
 
+unsigned int nf_conntrack_pkt_threshold __read_mostly;
+EXPORT_SYMBOL(nf_conntrack_pkt_threshold);
+
 DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked);
 EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked);
 
@@ -378,11 +390,15 @@
 }
 EXPORT_SYMBOL_GPL(nf_ct_tmpl_free);
 
+void (*delete_sfe_entry)(struct nf_conn *ct) __rcu __read_mostly;
+EXPORT_SYMBOL(delete_sfe_entry);
+
 static void
 destroy_conntrack(struct nf_conntrack *nfct)
 {
 	struct nf_conn *ct = (struct nf_conn *)nfct;
 	struct nf_conntrack_l4proto *l4proto;
+	void (*delete_entry)(struct nf_conn *ct);
 
 	pr_debug("destroy_conntrack(%pK)\n", ct);
 	NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
@@ -391,6 +407,17 @@
 		nf_ct_tmpl_free(ct);
 		return;
 	}
+
+	if (ct->sfe_entry) {
+		delete_entry = rcu_dereference(delete_sfe_entry);
+		if (delete_entry)
+			delete_entry(ct);
+	}
+
+	/* To make sure we don't get any weird locking issues here:
+	 * destroy_conntrack() MUST NOT be called with a write lock
+	 * to nf_conntrack_lock!!! -HW
+	 */
 	rcu_read_lock();
 	l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct));
 	if (l4proto->destroy)
@@ -1196,6 +1223,10 @@
 #ifdef CONFIG_NF_CONNTRACK_SECMARK
 			ct->secmark = exp->master->secmark;
 #endif
+/* Initialize the NAT type entry. */
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+		ct->nattype_entry = 0;
+#endif
 			NF_CT_STAT_INC(net, expect_new);
 		}
 		spin_unlock(&nf_conntrack_expect_lock);
@@ -1434,6 +1465,12 @@
 			  unsigned long extra_jiffies,
 			  int do_acct)
 {
+	struct nf_conn_acct *acct;
+	u64 pkts;
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	bool (*nattype_ref_timer)(unsigned long nattype);
+#endif
+
 	NF_CT_ASSERT(skb);
 
 	/* Only update if this is not a fixed timeout */
@@ -1445,9 +1482,35 @@
 		extra_jiffies += nfct_time_stamp;
 
 	ct->timeout = extra_jiffies;
+/* Refresh the NAT type entry. */
+#if defined(CONFIG_IP_NF_TARGET_NATTYPE_MODULE)
+	nattype_ref_timer = rcu_dereference(nattype_refresh_timer);
+	if (nattype_ref_timer)
+		nattype_ref_timer(ct->nattype_entry);
+#endif
+
 acct:
-	if (do_acct)
-		nf_ct_acct_update(ct, ctinfo, skb->len);
+	if (do_acct) {
+		acct = nf_conn_acct_find(ct);
+		if (acct) {
+			struct nf_conn_counter *counter = acct->counter;
+
+			atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets);
+			atomic64_add(skb->len, &counter
+					[CTINFO2DIR(ctinfo)].bytes);
+
+			pkts =
+			atomic64_read(&counter[CTINFO2DIR(ctinfo)].packets) +
+			atomic64_read(&counter[!CTINFO2DIR(ctinfo)].packets);
+			/* Report if the packet threshold is reached. */
+			if ((nf_conntrack_pkt_threshold > 0) &&
+			    (pkts == nf_conntrack_pkt_threshold)) {
+				nf_conntrack_event_cache(IPCT_COUNTER, ct);
+				nf_conntrack_event_cache(IPCT_PROTOINFO, ct);
+				nf_ct_deliver_cached_events(ct);
+			}
+		}
+	}
 }
 EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);
 
diff --git a/net/netfilter/nf_conntrack_ecache.c b/net/netfilter/nf_conntrack_ecache.c
index da9df2d..22fc321 100644
--- a/net/netfilter/nf_conntrack_ecache.c
+++ b/net/netfilter/nf_conntrack_ecache.c
@@ -290,6 +290,7 @@
 	BUG_ON(notify != new);
 	RCU_INIT_POINTER(net->ct.nf_conntrack_event_cb, NULL);
 	mutex_unlock(&nf_ct_ecache_mutex);
+	/* synchronize_rcu() is called from ctnetlink_exit. */
 }
 EXPORT_SYMBOL_GPL(nf_conntrack_unregister_notifier);
 
@@ -326,6 +327,7 @@
 	BUG_ON(notify != new);
 	RCU_INIT_POINTER(net->ct.nf_expect_event_cb, NULL);
 	mutex_unlock(&nf_ct_ecache_mutex);
+	/* synchronize_rcu() is called from ctnetlink_exit. */
 }
 EXPORT_SYMBOL_GPL(nf_ct_expect_unregister_notifier);
 
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index f8dbacf..0d6c72d 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -411,7 +411,7 @@
 	struct net *net = nf_ct_exp_net(expect);
 	struct hlist_node *next;
 	unsigned int h;
-	int ret = 1;
+	int ret = 0;
 
 	if (!master_help) {
 		ret = -ESHUTDOWN;
@@ -461,7 +461,7 @@
 
 	spin_lock_bh(&nf_conntrack_expect_lock);
 	ret = __nf_ct_expect_check(expect);
-	if (ret <= 0)
+	if (ret < 0)
 		goto out;
 
 	ret = nf_ct_expect_insert(expect);
diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c
index 02bcf00..008299b 100644
--- a/net/netfilter/nf_conntrack_extend.c
+++ b/net/netfilter/nf_conntrack_extend.c
@@ -53,7 +53,11 @@
 
 	rcu_read_lock();
 	t = rcu_dereference(nf_ct_ext_types[id]);
-	BUG_ON(t == NULL);
+	if (!t) {
+		rcu_read_unlock();
+		return NULL;
+	}
+
 	off = ALIGN(sizeof(struct nf_ct_ext), t->align);
 	len = off + t->len + var_alloc_len;
 	alloc_size = t->alloc_size + var_alloc_len;
@@ -88,7 +92,10 @@
 
 	rcu_read_lock();
 	t = rcu_dereference(nf_ct_ext_types[id]);
-	BUG_ON(t == NULL);
+	if (!t) {
+		rcu_read_unlock();
+		return NULL;
+	}
 
 	newoff = ALIGN(old->len, t->align);
 	newlen = newoff + t->len + var_alloc_len;
@@ -175,6 +182,6 @@
 	RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL);
 	update_alloc_size(type);
 	mutex_unlock(&nf_ct_ext_type_mutex);
-	rcu_barrier(); /* Wait for completion of call_rcu()'s */
+	synchronize_rcu();
 }
 EXPORT_SYMBOL_GPL(nf_ct_extend_unregister);
diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c
index 1972a14..b97caa1 100644
--- a/net/netfilter/nf_conntrack_irc.c
+++ b/net/netfilter/nf_conntrack_irc.c
@@ -19,6 +19,7 @@
 #include <linux/tcp.h>
 #include <linux/netfilter.h>
 #include <linux/slab.h>
+#include <linux/list.h>
 
 #include <net/netfilter/nf_conntrack.h>
 #include <net/netfilter/nf_conntrack_expect.h>
@@ -32,6 +33,18 @@
 static unsigned int dcc_timeout __read_mostly = 300;
 /* This is slow, but it's simple. --RR */
 static char *irc_buffer;
+struct irc_client_info {
+	char *nickname;
+	bool conn_to_server;
+	int nickname_len;
+	__be32 server_ip;
+	__be32 client_ip;
+	struct list_head ptr;
+	};
+
+static struct irc_client_info client_list;
+
+static unsigned int no_of_clients;
 static DEFINE_SPINLOCK(irc_buffer_lock);
 
 unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb,
@@ -61,7 +74,7 @@
 };
 
 #define MINMATCHLEN	5
-
+#define MINLENNICK	1
 /* tries to get the ip_addr and port out of a dcc command
  * return value: -1 on failure, 0 on success
  *	data		pointer to first byte of DCC command data
@@ -71,6 +84,23 @@
  *	ad_beg_p	returns pointer to first byte of addr data
  *	ad_end_p	returns pointer to last byte of addr data
  */
+static struct irc_client_info *search_client_by_ip
+(
+	struct nf_conntrack_tuple *tuple
+)
+{
+	struct irc_client_info *temp, *ret = NULL;
+	struct list_head *obj_ptr, *prev_obj_ptr;
+
+	list_for_each_safe(obj_ptr, prev_obj_ptr, &client_list.ptr) {
+		temp = list_entry(obj_ptr, struct irc_client_info, ptr);
+		if ((temp->client_ip == tuple->src.u3.ip) &&
+		    (temp->server_ip == tuple->dst.u3.ip))
+			ret = temp;
+	}
+	return ret;
+}
+
 static int parse_dcc(char *data, const char *data_end, __be32 *ip,
 		     u_int16_t *port, char **ad_beg_p, char **ad_end_p)
 {
@@ -105,6 +135,106 @@
 	return 0;
 }
 
+static bool mangle_ip(struct nf_conn *ct,
+		      int dir, char *nick_start)
+{
+	char *nick_end;
+	struct nf_conntrack_tuple *tuple;
+	struct irc_client_info *temp;
+	struct list_head *obj_ptr, *prev_obj_ptr;
+
+	tuple = &ct->tuplehash[dir].tuple;
+	nick_end = nick_start;
+	while (*nick_end != ' ')
+		nick_end++;
+	list_for_each_safe(obj_ptr, prev_obj_ptr,
+			   &client_list.ptr) {
+		temp = list_entry(obj_ptr,
+				  struct irc_client_info, ptr);
+		/*If it is an internal client,
+		 *do not mangle the DCC Server IP
+		 */
+		if ((temp->server_ip == tuple->dst.u3.ip) &&
+		    (temp->nickname_len == (nick_end - nick_start))) {
+			if (memcmp(nick_start, temp->nickname,
+				   temp->nickname_len) == 0)
+				return false;
+		}
+	}
+	return true;
+}
+
+static int handle_nickname(struct nf_conn *ct,
+			   int dir, char *nick_start)
+{
+	char *nick_end;
+	struct nf_conntrack_tuple *tuple;
+	struct irc_client_info *temp;
+	int i, j;
+	bool add_entry = true;
+
+	nick_end = nick_start;
+	i = 0;
+	while (*nick_end != '\n') {
+		nick_end++;
+		i++;
+	}
+	tuple = &ct->tuplehash[dir].tuple;
+	/*Check if the entry is already
+	 * present for that client
+	 */
+	temp = search_client_by_ip(tuple);
+	if (temp) {
+		add_entry = false;
+		/*Update nickname if the client is not already
+		 * connected to the server.If the client is
+		 * connected, wait for server to confirm
+		 * if nickname is valid
+		 */
+		if (!temp->conn_to_server) {
+			kfree(temp->nickname);
+			temp->nickname =
+				kmalloc(i, GFP_ATOMIC);
+			if (temp->nickname) {
+				temp->nickname_len = i;
+				memcpy(temp->nickname,
+				       nick_start, temp->nickname_len);
+			} else {
+				list_del(&temp->ptr);
+				no_of_clients--;
+				kfree(temp);
+			}
+		}
+	}
+	/*Add client entry if not already present*/
+	if (add_entry) {
+		j = sizeof(struct irc_client_info);
+		temp = kmalloc(j, GFP_ATOMIC);
+		if (temp) {
+			no_of_clients++;
+			tuple = &ct->tuplehash[dir].tuple;
+			temp->nickname_len = i;
+			temp->nickname =
+				kmalloc(temp->nickname_len, GFP_ATOMIC);
+			if (!temp->nickname) {
+				kfree(temp);
+				return NF_DROP;
+			}
+			memcpy(temp->nickname, nick_start,
+			       temp->nickname_len);
+			memcpy(&temp->client_ip,
+			       &tuple->src.u3.ip, sizeof(__be32));
+			memcpy(&temp->server_ip,
+			       &tuple->dst.u3.ip, sizeof(__be32));
+			temp->conn_to_server = false;
+			list_add(&temp->ptr,
+				 &client_list.ptr);
+		} else {
+			return NF_DROP;
+		}
+	}
+	return NF_ACCEPT;
+}
 static int help(struct sk_buff *skb, unsigned int protoff,
 		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
 {
@@ -113,7 +243,7 @@
 	const struct tcphdr *th;
 	struct tcphdr _tcph;
 	const char *data_limit;
-	char *data, *ib_ptr;
+	char *data, *ib_ptr, *for_print, *nick_end;
 	int dir = CTINFO2DIR(ctinfo);
 	struct nf_conntrack_expect *exp;
 	struct nf_conntrack_tuple *tuple;
@@ -123,10 +253,8 @@
 	int i, ret = NF_ACCEPT;
 	char *addr_beg_p, *addr_end_p;
 	typeof(nf_nat_irc_hook) nf_nat_irc;
-
-	/* If packet is coming from IRC server */
-	if (dir == IP_CT_DIR_REPLY)
-		return NF_ACCEPT;
+	struct irc_client_info *temp;
+	bool mangle = true;
 
 	/* Until there's been traffic both ways, don't look in packets. */
 	if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -150,80 +278,223 @@
 	data = ib_ptr;
 	data_limit = ib_ptr + skb->len - dataoff;
 
-	/* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
-	 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
-	while (data < data_limit - (19 + MINMATCHLEN)) {
-		if (memcmp(data, "\1DCC ", 5)) {
-			data++;
-			continue;
+	/* If packet is coming from IRC server
+	 * parse the packet for different type of
+	 * messages (MOTD,NICK etc) and process
+	 * accordingly
+	 */
+	if (dir == IP_CT_DIR_REPLY) {
+		/* strlen("NICK xxxxxx")
+		 * 5+strlen("xxxxxx")=1 (minimum length of nickname)
+		 */
+
+		while (data < data_limit - 6) {
+			if (memcmp(data, " MOTD ", 6)) {
+				data++;
+				continue;
+			}
+			/* MOTD message signifies successful
+			 * registration with server
+			 */
+			tuple = &ct->tuplehash[!dir].tuple;
+			temp = search_client_by_ip(tuple);
+			if (temp && !temp->conn_to_server)
+				temp->conn_to_server = true;
+			ret = NF_ACCEPT;
+			goto out;
 		}
-		data += 5;
-		/* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
 
-		iph = ip_hdr(skb);
-		pr_debug("DCC found in master %pI4:%u %pI4:%u\n",
-			 &iph->saddr, ntohs(th->source),
-			 &iph->daddr, ntohs(th->dest));
-
-		for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
-			if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
-				/* no match */
+		/* strlen("NICK :xxxxxx")
+		 * 6+strlen("xxxxxx")=1 (minimum length of nickname)
+		 * Parsing the server reply to get nickname
+		 * of the client
+		 */
+		data = ib_ptr;
+		data_limit = ib_ptr + skb->len - dataoff;
+		while (data < data_limit - (6 + MINLENNICK)) {
+			if (memcmp(data, "NICK :", 6)) {
+				data++;
 				continue;
 			}
-			data += strlen(dccprotos[i]);
-			pr_debug("DCC %s detected\n", dccprotos[i]);
-
-			/* we have at least
-			 * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
-			 * data left (== 14/13 bytes) */
-			if (parse_dcc(data, data_limit, &dcc_ip,
-				       &dcc_port, &addr_beg_p, &addr_end_p)) {
-				pr_debug("unable to parse dcc command\n");
-				continue;
-			}
-
-			pr_debug("DCC bound ip/port: %pI4:%u\n",
-				 &dcc_ip, dcc_port);
-
-			/* dcc_ip can be the internal OR external (NAT'ed) IP */
-			tuple = &ct->tuplehash[dir].tuple;
-			if (tuple->src.u3.ip != dcc_ip &&
-			    tuple->dst.u3.ip != dcc_ip) {
-				net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
-						     &tuple->src.u3.ip,
-						     &dcc_ip, dcc_port);
-				continue;
-			}
-
-			exp = nf_ct_expect_alloc(ct);
-			if (exp == NULL) {
-				nf_ct_helper_log(skb, ct,
-						 "cannot alloc expectation");
-				ret = NF_DROP;
-				goto out;
+			data += 6;
+			nick_end = data;
+			i = 0;
+			while ((*nick_end != 0x0d) &&
+			       (*(nick_end + 1) != '\n')) {
+				nick_end++;
+				i++;
 			}
 			tuple = &ct->tuplehash[!dir].tuple;
-			port = htons(dcc_port);
-			nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
-					  tuple->src.l3num,
-					  NULL, &tuple->dst.u3,
-					  IPPROTO_TCP, NULL, &port);
-
-			nf_nat_irc = rcu_dereference(nf_nat_irc_hook);
-			if (nf_nat_irc && ct->status & IPS_NAT_MASK)
-				ret = nf_nat_irc(skb, ctinfo, protoff,
-						 addr_beg_p - ib_ptr,
-						 addr_end_p - addr_beg_p,
-						 exp);
-			else if (nf_ct_expect_related(exp) != 0) {
-				nf_ct_helper_log(skb, ct,
-						 "cannot add expectation");
-				ret = NF_DROP;
+			temp = search_client_by_ip(tuple);
+			if (temp && temp->nickname) {
+				kfree(temp->nickname);
+				temp->nickname = kmalloc(i, GFP_ATOMIC);
+				if (temp->nickname) {
+					temp->nickname_len = i;
+					memcpy(temp->nickname, data,
+					       temp->nickname_len);
+					temp->conn_to_server = true;
+				} else {
+					list_del(&temp->ptr);
+					no_of_clients--;
+					kfree(temp);
+					ret = NF_ACCEPT;
+				}
 			}
-			nf_ct_expect_put(exp);
+			/*NICK during registration*/
+			ret = NF_ACCEPT;
 			goto out;
 		}
 	}
+
+	else{
+		/*Parsing NICK command from client to create an entry
+		 * strlen("NICK xxxxxx")
+		 * 5+strlen("xxxxxx")=1 (minimum length of nickname)
+		 */
+		data = ib_ptr;
+		data_limit = ib_ptr + skb->len - dataoff;
+		while (data < data_limit - (5 + MINLENNICK)) {
+			if (memcmp(data, "NICK ", 5)) {
+				data++;
+				continue;
+			}
+			data += 5;
+			ret = handle_nickname(ct, dir, data);
+			goto out;
+		}
+
+		data = ib_ptr;
+		while (data < data_limit - 6) {
+			if (memcmp(data, "QUIT :", 6)) {
+				data++;
+				continue;
+			}
+			/* Parsing QUIT to free the list entry
+			 */
+			tuple = &ct->tuplehash[dir].tuple;
+			temp = search_client_by_ip(tuple);
+			if (temp) {
+				list_del(&temp->ptr);
+				no_of_clients--;
+				kfree(temp->nickname);
+				kfree(temp);
+			}
+			ret = NF_ACCEPT;
+			goto out;
+		}
+		/* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
+		 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14
+		 */
+		data = ib_ptr;
+		while (data < data_limit - (19 + MINMATCHLEN)) {
+			if (memcmp(data, "\1DCC ", 5)) {
+				data++;
+				continue;
+			}
+			data += 5;
+			/* we have at least (19+MINMATCHLEN)-5
+			 *bytes valid data left
+			 */
+			iph = ip_hdr(skb);
+			pr_debug("DCC found in master %pI4:%u %pI4:%u\n",
+				 &iph->saddr, ntohs(th->source),
+				 &iph->daddr, ntohs(th->dest));
+
+			for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
+				if (memcmp(data, dccprotos[i],
+					   strlen(dccprotos[i]))) {
+					/* no match */
+					continue;
+				}
+				data += strlen(dccprotos[i]);
+				pr_debug("DCC %s detected\n", dccprotos[i]);
+
+				/* we have at least
+				 * (19+MINMATCHLEN)-5-dccprotos[i].matchlen
+				 *bytes valid data left (== 14/13 bytes)
+				 */
+				if (parse_dcc(data, data_limit, &dcc_ip,
+					      &dcc_port, &addr_beg_p,
+					      &addr_end_p)) {
+					pr_debug("unable to parse dcc command\n");
+					continue;
+				}
+
+				pr_debug("DCC bound ip/port: %pI4:%u\n",
+					 &dcc_ip, dcc_port);
+
+				/* dcc_ip can be the internal OR
+				 *external (NAT'ed) IP
+				 */
+				tuple = &ct->tuplehash[dir].tuple;
+				if (tuple->src.u3.ip != dcc_ip &&
+				    tuple->dst.u3.ip != dcc_ip) {
+					net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
+							     &tuple->src.u3.ip,
+							     &dcc_ip, dcc_port);
+					continue;
+				}
+
+				exp = nf_ct_expect_alloc(ct);
+				if (!exp) {
+					nf_ct_helper_log(skb, ct,
+							 "cannot alloc expectation");
+					ret = NF_DROP;
+					goto out;
+				}
+				tuple = &ct->tuplehash[!dir].tuple;
+				port = htons(dcc_port);
+				nf_ct_expect_init(exp,
+						  NF_CT_EXPECT_CLASS_DEFAULT,
+						  tuple->src.l3num,
+						  NULL, &tuple->dst.u3,
+						  IPPROTO_TCP, NULL, &port);
+
+				nf_nat_irc = rcu_dereference(nf_nat_irc_hook);
+
+				tuple = &ct->tuplehash[dir].tuple;
+				for_print = ib_ptr;
+				/* strlen("PRIVMSG xxxx :\1DCC
+				 *SENT t AAAAAAAA P\1\n")=26
+				 * 8+strlen(xxxx) = 1(min length)+7+
+				 *MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14
+				 *Parsing DCC command to get client name and
+				 *check whether it is an internal client
+				 */
+				while (for_print <
+				       data_limit - (25 + MINMATCHLEN)) {
+					if (memcmp(for_print, "PRIVMSG ", 8)) {
+						for_print++;
+						continue;
+					}
+					for_print += 8;
+					mangle = mangle_ip(ct,
+							   dir, for_print);
+					break;
+				}
+				if (mangle &&
+				    nf_nat_irc &&
+				    ct->status & IPS_NAT_MASK)
+					ret = nf_nat_irc(skb, ctinfo,
+							 protoff,
+							 addr_beg_p - ib_ptr,
+							 addr_end_p
+							 - addr_beg_p,
+							 exp);
+
+				else if (mangle &&
+					 nf_ct_expect_related(exp)
+					 != 0) {
+					nf_ct_helper_log(skb, ct,
+							 "cannot add expectation");
+					ret = NF_DROP;
+				}
+				nf_ct_expect_put(exp);
+				goto out;
+			}
+		}
+	}
  out:
 	spin_unlock_bh(&irc_buffer_lock);
 	return ret;
@@ -266,7 +537,8 @@
 		kfree(irc_buffer);
 		return ret;
 	}
-
+	no_of_clients = 0;
+	INIT_LIST_HEAD(&client_list.ptr);
 	return 0;
 }
 
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 04111c1..6bd58eea 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -729,6 +729,10 @@
 		if (events & (1 << IPCT_SEQADJ) &&
 		    ctnetlink_dump_ct_seq_adj(skb, ct) < 0)
 			goto nla_put_failure;
+
+		if (events & (1 << IPCT_COUNTER) &&
+		    ctnetlink_dump_acct(skb, ct, 0) < 0)
+			goto nla_put_failure;
 	}
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
@@ -3413,6 +3417,7 @@
 #ifdef CONFIG_NETFILTER_NETLINK_GLUE_CT
 	RCU_INIT_POINTER(nfnl_ct_hook, NULL);
 #endif
+	synchronize_rcu();
 }
 
 module_init(ctnetlink_init);
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 3a8dc39..f132ef9 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -49,13 +49,28 @@
 MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar "
 					"only (default 1)");
 
-static int sip_direct_media __read_mostly = 1;
-module_param(sip_direct_media, int, 0600);
-MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling "
-				   "endpoints only (default 1)");
-
 const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
 EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
+static struct ctl_table_header *sip_sysctl_header;
+static unsigned int nf_ct_disable_sip_alg;
+static int sip_direct_media = 1;
+static struct ctl_table sip_sysctl_tbl[] = {
+	{
+		.procname     = "nf_conntrack_disable_sip_alg",
+		.data         = &nf_ct_disable_sip_alg,
+		.maxlen       = sizeof(unsigned int),
+		.mode         = 0644,
+		.proc_handler = proc_dointvec,
+	},
+	{
+		.procname     = "nf_conntrack_sip_direct_media",
+		.data         = &sip_direct_media,
+		.maxlen       = sizeof(int),
+		.mode         = 0644,
+		.proc_handler = proc_dointvec,
+	},
+	{}
+};
 
 static int string_len(const struct nf_conn *ct, const char *dptr,
 		      const char *limit, int *shift)
@@ -1467,6 +1482,8 @@
 	const struct nf_nat_sip_hooks *hooks;
 	int ret;
 
+	if (nf_ct_disable_sip_alg)
+		return NF_ACCEPT;
 	if (strncasecmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
 		ret = process_sip_request(skb, protoff, dataoff, dptr, datalen);
 	else
@@ -1626,6 +1643,16 @@
 {
 	int i, ret;
 
+	sip_sysctl_header = register_net_sysctl(&init_net, "net/netfilter",
+						sip_sysctl_tbl);
+	if (!sip_sysctl_header)
+		pr_debug("nf_ct_sip:Unable to register SIP systbl\n");
+
+	if (nf_ct_disable_sip_alg)
+		pr_debug("nf_ct_sip: SIP ALG disabled\n");
+	else
+		pr_debug("nf_ct_sip: SIP ALG enabled\n");
+
 	if (ports_c == 0)
 		ports[ports_c++] = SIP_PORT;
 
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 5f446cd..2f1014e 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -517,6 +517,14 @@
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+	{
+		.procname	= "nf_conntrack_pkt_threshold",
+		.data		= &nf_conntrack_pkt_threshold,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+
 	{ }
 };
 
diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c
index 5b9c884..2916f48 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -225,20 +225,21 @@
 		.tuple = tuple,
 		.zone = zone
 	};
-	struct rhlist_head *hl;
+	struct rhlist_head *hl, *h;
 
 	hl = rhltable_lookup(&nf_nat_bysource_table, &key,
 			     nf_nat_bysource_params);
-	if (!hl)
-		return 0;
 
-	ct = container_of(hl, typeof(*ct), nat_bysource);
+	rhl_for_each_entry_rcu(ct, h, hl, nat_bysource) {
+		nf_ct_invert_tuplepr(result,
+				     &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
+		result->dst = tuple->dst;
 
-	nf_ct_invert_tuplepr(result,
-			     &ct->tuplehash[IP_CT_DIR_REPLY].tuple);
-	result->dst = tuple->dst;
+		if (in_range(l3proto, l4proto, result, range))
+			return 1;
+	}
 
-	return in_range(l3proto, l4proto, result, range);
+	return 0;
 }
 
 /* For [FUTURE] fragmentation handling, we want the least-used
@@ -891,6 +892,8 @@
 #ifdef CONFIG_XFRM
 	RCU_INIT_POINTER(nf_nat_decode_session_hook, NULL);
 #endif
+	synchronize_rcu();
+
 	for (i = 0; i < NFPROTO_NUMPROTO; i++)
 		kfree(nf_nat_l4protos[i]);
 
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index 791fac4..2f2414e2 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -111,13 +111,26 @@
 		newaddr = ct->tuplehash[!dir].tuple.src.u3;
 		newport = ct_sip_info->forced_dport ? :
 			  ct->tuplehash[!dir].tuple.src.u.udp.port;
+	} else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, addr) &&
+	    ct->tuplehash[dir].tuple.src.u.udp.port != port) {
+		newaddr = ct->tuplehash[!dir].tuple.dst.u3;
+		newport = 0;
+	} else if (nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, addr) &&
+		   ct->tuplehash[dir].tuple.dst.u.udp.port != port) {
+		newaddr = ct->tuplehash[!dir].tuple.src.u3;
+		newport = 0;
 	} else
 		return 1;
 
 	if (nf_inet_addr_cmp(&newaddr, addr) && newport == port)
 		return 1;
 
-	buflen = sip_sprintf_addr_port(ct, buffer, &newaddr, ntohs(newport));
+	if (newport == 0)
+		buflen = sip_sprintf_addr(ct, buffer, &newaddr, false);
+	else
+		buflen = sip_sprintf_addr_port(ct, buffer, &newaddr,
+					       ntohs(newport));
+
 	return mangle_packet(skb, protoff, dataoff, dptr, datalen,
 			     matchoff, matchlen, buffer, buflen);
 }
diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 3b79f34..b1fcfa0 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -161,6 +161,7 @@
 	int i, ret;
 	struct nf_conntrack_expect_policy *expect_policy;
 	struct nlattr *tb[NFCTH_POLICY_SET_MAX+1];
+	unsigned int class_max;
 
 	ret = nla_parse_nested(tb, NFCTH_POLICY_SET_MAX, attr,
 			       nfnl_cthelper_expect_policy_set);
@@ -170,19 +171,18 @@
 	if (!tb[NFCTH_POLICY_SET_NUM])
 		return -EINVAL;
 
-	helper->expect_class_max =
-		ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
-
-	if (helper->expect_class_max != 0 &&
-	    helper->expect_class_max > NF_CT_MAX_EXPECT_CLASSES)
+	class_max = ntohl(nla_get_be32(tb[NFCTH_POLICY_SET_NUM]));
+	if (class_max == 0)
+		return -EINVAL;
+	if (class_max > NF_CT_MAX_EXPECT_CLASSES)
 		return -EOVERFLOW;
 
 	expect_policy = kzalloc(sizeof(struct nf_conntrack_expect_policy) *
-				helper->expect_class_max, GFP_KERNEL);
+				class_max, GFP_KERNEL);
 	if (expect_policy == NULL)
 		return -ENOMEM;
 
-	for (i=0; i<helper->expect_class_max; i++) {
+	for (i = 0; i < class_max; i++) {
 		if (!tb[NFCTH_POLICY_SET+i])
 			goto err;
 
@@ -191,6 +191,8 @@
 		if (ret < 0)
 			goto err;
 	}
+
+	helper->expect_class_max = class_max - 1;
 	helper->expect_policy = expect_policy;
 	return 0;
 err:
@@ -377,10 +379,10 @@
 		goto nla_put_failure;
 
 	if (nla_put_be32(skb, NFCTH_POLICY_SET_NUM,
-			 htonl(helper->expect_class_max)))
+			 htonl(helper->expect_class_max + 1)))
 		goto nla_put_failure;
 
-	for (i=0; i<helper->expect_class_max; i++) {
+	for (i = 0; i < helper->expect_class_max + 1; i++) {
 		nest_parms2 = nla_nest_start(skb,
 				(NFCTH_POLICY_SET+i) | NLA_F_NESTED);
 		if (nest_parms2 == NULL)
diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c
index 139e086..47d6656 100644
--- a/net/netfilter/nfnetlink_cttimeout.c
+++ b/net/netfilter/nfnetlink_cttimeout.c
@@ -646,8 +646,8 @@
 #ifdef CONFIG_NF_CONNTRACK_TIMEOUT
 	RCU_INIT_POINTER(nf_ct_timeout_find_get_hook, NULL);
 	RCU_INIT_POINTER(nf_ct_timeout_put_hook, NULL);
+	synchronize_rcu();
 #endif /* CONFIG_NF_CONNTRACK_TIMEOUT */
-	rcu_barrier();
 }
 
 module_init(cttimeout_init);
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 6c1e024..7c33955 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -159,8 +159,34 @@
 			else
 				*dest = PACKET_BROADCAST;
 			break;
+		case NFPROTO_NETDEV:
+			switch (skb->protocol) {
+			case htons(ETH_P_IP): {
+				int noff = skb_network_offset(skb);
+				struct iphdr *iph, _iph;
+
+				iph = skb_header_pointer(skb, noff,
+							 sizeof(_iph), &_iph);
+				if (!iph)
+					goto err;
+
+				if (ipv4_is_multicast(iph->daddr))
+					*dest = PACKET_MULTICAST;
+				else
+					*dest = PACKET_BROADCAST;
+
+				break;
+			}
+			case htons(ETH_P_IPV6):
+				*dest = PACKET_MULTICAST;
+				break;
+			default:
+				WARN_ON_ONCE(1);
+				goto err;
+			}
+			break;
 		default:
-			WARN_ON(1);
+			WARN_ON_ONCE(1);
 			goto err;
 		}
 		break;
diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 1fbe4b6..822be06 100644
--- a/net/netfilter/xt_qtaguid.c
+++ b/net/netfilter/xt_qtaguid.c
@@ -1174,6 +1174,38 @@
 	spin_unlock_bh(&iface_stat_list_lock);
 }
 
+/* Guarantied to return a net_device that has a name */
+static void get_dev_and_dir(const struct sk_buff *skb,
+			    struct xt_action_param *par,
+			    enum ifs_tx_rx *direction,
+			    const struct net_device **el_dev)
+{
+	BUG_ON(!direction || !el_dev);
+
+	if (par->in) {
+		*el_dev = par->in;
+		*direction = IFS_RX;
+	} else if (par->out) {
+		*el_dev = par->out;
+		*direction = IFS_TX;
+	} else {
+		pr_err("qtaguid[%d]: %s(): no par->in/out?!!\n",
+		       par->hooknum, __func__);
+		BUG();
+	}
+	if (unlikely(!(*el_dev)->name)) {
+		pr_err("qtaguid[%d]: %s(): no dev->name?!!\n",
+		       par->hooknum, __func__);
+		BUG();
+	}
+	if (skb->dev && *el_dev != skb->dev) {
+		MT_DEBUG("qtaguid[%d]: skb->dev=%pK %s vs par->%s=%pK %s\n",
+			 par->hooknum, skb->dev, skb->dev->name,
+			 *direction == IFS_RX ? "in" : "out",  *el_dev,
+			 (*el_dev)->name);
+	}
+}
+
 /*
  * Update stats for the specified interface from the skb.
  * Do nothing if the entry
@@ -1185,46 +1217,27 @@
 {
 	struct iface_stat *entry;
 	const struct net_device *el_dev;
-	enum ifs_tx_rx direction = par->in ? IFS_RX : IFS_TX;
+	enum ifs_tx_rx direction;
 	int bytes = skb->len;
 	int proto;
 
-	if (!skb->dev) {
-		MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
-		el_dev = par->in ? : par->out;
-	} else {
-		const struct net_device *other_dev;
-		el_dev = skb->dev;
-		other_dev = par->in ? : par->out;
-		if (el_dev != other_dev) {
-			MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
-				 "par->(in/out)=%p %s\n",
-				 par->hooknum, el_dev, el_dev->name, other_dev,
-				 other_dev->name);
-		}
-	}
-
-	if (unlikely(!el_dev)) {
-		pr_err_ratelimited("qtaguid[%d]: %s(): no par->in/out?!!\n",
-				   par->hooknum, __func__);
-		BUG();
-	} else {
-		proto = ipx_proto(skb, par);
-		MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
-			 par->hooknum, el_dev->name, el_dev->type,
-			 par->family, proto);
-	}
+	get_dev_and_dir(skb, par, &direction, &el_dev);
+	proto = ipx_proto(skb, par);
+	MT_DEBUG("qtaguid[%d]: iface_stat: %s(%s): "
+		 "type=%d fam=%d proto=%d dir=%d\n",
+		 par->hooknum, __func__, el_dev->name, el_dev->type,
+		 par->family, proto, direction);
 
 	spin_lock_bh(&iface_stat_list_lock);
 	entry = get_iface_entry(el_dev->name);
 	if (entry == NULL) {
-		IF_DEBUG("qtaguid: iface_stat: %s(%s): not tracked\n",
-			 __func__, el_dev->name);
+		IF_DEBUG("qtaguid[%d]: iface_stat: %s(%s): not tracked\n",
+			 par->hooknum, __func__, el_dev->name);
 		spin_unlock_bh(&iface_stat_list_lock);
 		return;
 	}
 
-	IF_DEBUG("qtaguid: %s(%s): entry=%p\n", __func__,
+	IF_DEBUG("qtaguid[%d]: %s(%s): entry=%p\n", par->hooknum,  __func__,
 		 el_dev->name, entry);
 
 	data_counters_update(&entry->totals_via_skb, 0, direction, proto,
@@ -1289,14 +1302,14 @@
 	spin_lock_bh(&iface_stat_list_lock);
 	iface_entry = get_iface_entry(ifname);
 	if (!iface_entry) {
-		pr_err_ratelimited("qtaguid: iface_stat: stat_update() "
+		pr_err_ratelimited("qtaguid: tag_stat: stat_update() "
 				   "%s not found\n", ifname);
 		spin_unlock_bh(&iface_stat_list_lock);
 		return;
 	}
 	/* It is ok to process data when an iface_entry is inactive */
 
-	MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n",
+	MT_DEBUG("qtaguid: tag_stat: stat_update() dev=%s entry=%pK\n",
 		 ifname, iface_entry);
 
 	/*
@@ -1313,8 +1326,8 @@
 		tag = combine_atag_with_uid(acct_tag, uid);
 		uid_tag = make_tag_from_uid(uid);
 	}
-	MT_DEBUG("qtaguid: iface_stat: stat_update(): "
-		 " looking for tag=0x%llx (uid=%u) in ife=%p\n",
+	MT_DEBUG("qtaguid: tag_stat: stat_update(): "
+		 " looking for tag=0x%llx (uid=%u) in ife=%pK\n",
 		 tag, get_uid_from_tag(tag), iface_entry);
 	/* Loop over tag list under this interface for {acct_tag,uid_tag} */
 	spin_lock_bh(&iface_entry->tag_stat_list_lock);
@@ -1573,8 +1586,8 @@
 	struct sock *sk;
 	unsigned int hook_mask = (1 << par->hooknum);
 
-	MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb,
-		 par->hooknum, par->family);
+	MT_DEBUG("qtaguid[%d]: find_sk(skb=%pK) family=%d\n",
+		 par->hooknum, skb, par->family);
 
 	/*
 	 * Let's not abuse the the xt_socket_get*_sk(), or else it will
@@ -1595,16 +1608,8 @@
 	}
 
 	if (sk) {
-		MT_DEBUG("qtaguid: %p->sk_proto=%u "
-			 "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
-		/*
-		 * When in TCP_TIME_WAIT the sk is not a "struct sock" but
-		 * "struct inet_timewait_sock" which is missing fields.
-		 */
-		if (!sk_fullsock(sk) || sk->sk_state  == TCP_TIME_WAIT) {
-			sock_gen_put(sk);
-			sk = NULL;
-		}
+		MT_DEBUG("qtaguid[%d]: %pK->sk_proto=%u->sk_state=%d\n",
+			 par->hooknum, sk, sk->sk_protocol, sk->sk_state);
 	}
 	return sk;
 }
@@ -1614,35 +1619,19 @@
 			    struct xt_action_param *par)
 {
 	const struct net_device *el_dev;
+	enum ifs_tx_rx direction;
+	int proto;
 
-	if (!skb->dev) {
-		MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum);
-		el_dev = par->in ? : par->out;
-	} else {
-		const struct net_device *other_dev;
-		el_dev = skb->dev;
-		other_dev = par->in ? : par->out;
-		if (el_dev != other_dev) {
-			MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs "
-				"par->(in/out)=%p %s\n",
-				par->hooknum, el_dev, el_dev->name, other_dev,
-				other_dev->name);
-		}
-	}
+	get_dev_and_dir(skb, par, &direction, &el_dev);
+	proto = ipx_proto(skb, par);
+	MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d dir=%d\n",
+		 par->hooknum, el_dev->name, el_dev->type,
+		 par->family, proto, direction);
 
-	if (unlikely(!el_dev)) {
-		pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum);
-	} else {
-		int proto = ipx_proto(skb, par);
-		MT_DEBUG("qtaguid[%d]: dev name=%s type=%d fam=%d proto=%d\n",
-			 par->hooknum, el_dev->name, el_dev->type,
-			 par->family, proto);
-
-		if_tag_stat_update(el_dev->name, uid,
-				skb->sk ? skb->sk : alternate_sk,
-				par->in ? IFS_RX : IFS_TX,
-				proto, skb->len);
-	}
+	if_tag_stat_update(el_dev->name, uid,
+			   skb->sk ? skb->sk : alternate_sk,
+			   direction,
+			   proto, skb->len);
 }
 
 static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par)
@@ -1654,6 +1643,11 @@
 	kuid_t sock_uid;
 	bool res;
 	bool set_sk_callback_lock = false;
+	/*
+	 * TODO: unhack how to force just accounting.
+	 * For now we only do tag stats when the uid-owner is not requested
+	 */
+	bool do_tag_stat = !(info->match & XT_QTAGUID_UID);
 
 	if (unlikely(module_passive))
 		return (info->match ^ info->invert) == 0;
@@ -1697,10 +1691,25 @@
 		 */
 		sk = qtaguid_find_sk(skb, par);
 		/*
-		 * If we got the socket from the find_sk(), we will need to put
-		 * it back, as nf_tproxy_get_sock_v4() got it.
+		 * TCP_NEW_SYN_RECV are not "struct sock" but "struct request_sock"
+		 * where we can get a pointer to a full socket to retrieve uid/gid.
+		 * When in TCP_TIME_WAIT, sk is a struct inet_timewait_sock
+		 * which is missing fields and does not contain any reference
+		 * to a full socket, so just ignore the socket.
 		 */
-		got_sock = sk;
+		if (sk && sk->sk_state == TCP_NEW_SYN_RECV) {
+			sock_gen_put(sk);
+			sk = sk_to_full_sk(sk);
+		} else if (sk && (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT)) {
+			sock_gen_put(sk);
+			sk = NULL;
+		} else {
+			/*
+			 * If we got the socket from the find_sk(), we will need to put
+			 * it back, as nf_tproxy_get_sock_v4() got it.
+			 */
+			got_sock = sk;
+		}
 		if (sk)
 			atomic64_inc(&qtu_events.match_found_sk_in_ct);
 		else
@@ -1710,33 +1719,16 @@
 	}
 	MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d fam=%d proto=%d\n",
 		 par->hooknum, sk, got_sock, par->family, ipx_proto(skb, par));
-	if (sk != NULL) {
-		set_sk_callback_lock = true;
-		read_lock_bh(&sk->sk_callback_lock);
-		MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n",
-			par->hooknum, sk, sk->sk_socket,
-			sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
-		filp = sk->sk_socket ? sk->sk_socket->file : NULL;
-		MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
-			par->hooknum, filp ? from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
-	}
 
-	if (sk == NULL || sk->sk_socket == NULL) {
+	if (!sk) {
 		/*
 		 * Here, the qtaguid_find_sk() using connection tracking
 		 * couldn't find the owner, so for now we just count them
 		 * against the system.
 		 */
-		/*
-		 * TODO: unhack how to force just accounting.
-		 * For now we only do iface stats when the uid-owner is not
-		 * requested.
-		 */
-		if (!(info->match & XT_QTAGUID_UID))
+		if (do_tag_stat)
 			account_for_uid(skb, sk, 0, par);
-		MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n",
-			par->hooknum,
-			sk ? sk->sk_socket : NULL);
+		MT_DEBUG("qtaguid[%d]: leaving (sk=NULL)\n", par->hooknum);
 		res = (info->match ^ info->invert) == 0;
 		atomic64_inc(&qtu_events.match_no_sk);
 		goto put_sock_ret_res;
@@ -1744,22 +1736,10 @@
 		res = false;
 		goto put_sock_ret_res;
 	}
-	filp = sk->sk_socket->file;
-	if (filp == NULL) {
-		MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum);
-		account_for_uid(skb, sk, 0, par);
-		res = ((info->match ^ info->invert) &
-			(XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0;
-		atomic64_inc(&qtu_events.match_no_sk_file);
-		goto put_sock_ret_res;
-	}
-	sock_uid = filp->f_cred->fsuid;
-	/*
-	 * TODO: unhack how to force just accounting.
-	 * For now we only do iface stats when the uid-owner is not requested
-	 */
-	if (!(info->match & XT_QTAGUID_UID))
-		account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid), par);
+	sock_uid = sk->sk_uid;
+	if (do_tag_stat)
+		account_for_uid(skb, sk, from_kuid(&init_user_ns, sock_uid),
+				par);
 
 	/*
 	 * The following two tests fail the match when:
@@ -1771,8 +1751,8 @@
 		kuid_t uid_min = make_kuid(&init_user_ns, info->uid_min);
 		kuid_t uid_max = make_kuid(&init_user_ns, info->uid_max);
 
-		if ((uid_gte(filp->f_cred->fsuid, uid_min) &&
-		     uid_lte(filp->f_cred->fsuid, uid_max)) ^
+		if ((uid_gte(sock_uid, uid_min) &&
+		     uid_lte(sock_uid, uid_max)) ^
 		    !(info->invert & XT_QTAGUID_UID)) {
 			MT_DEBUG("qtaguid[%d]: leaving uid not matching\n",
 				 par->hooknum);
@@ -1783,7 +1763,21 @@
 	if (info->match & XT_QTAGUID_GID) {
 		kgid_t gid_min = make_kgid(&init_user_ns, info->gid_min);
 		kgid_t gid_max = make_kgid(&init_user_ns, info->gid_max);
-
+		set_sk_callback_lock = true;
+		read_lock_bh(&sk->sk_callback_lock);
+		MT_DEBUG("qtaguid[%d]: sk=%pK->sk_socket=%pK->file=%pK\n",
+			 par->hooknum, sk, sk->sk_socket,
+			 sk->sk_socket ? sk->sk_socket->file : (void *)-1LL);
+		filp = sk->sk_socket ? sk->sk_socket->file : NULL;
+		if (!filp) {
+			res = ((info->match ^ info->invert) &
+			       XT_QTAGUID_GID) == 0;
+			atomic64_inc(&qtu_events.match_no_sk_gid);
+			goto put_sock_ret_res;
+		}
+		MT_DEBUG("qtaguid[%d]: filp...uid=%u\n",
+			 par->hooknum, filp ?
+			 from_kuid(&init_user_ns, filp->f_cred->fsuid) : -1);
 		if ((gid_gte(filp->f_cred->fsgid, gid_min) &&
 				gid_lte(filp->f_cred->fsgid, gid_max)) ^
 			!(info->invert & XT_QTAGUID_GID)) {
@@ -1955,7 +1949,7 @@
 			   "match_found_sk_in_ct=%llu "
 			   "match_found_no_sk_in_ct=%llu "
 			   "match_no_sk=%llu "
-			   "match_no_sk_file=%llu\n",
+			   "match_no_sk_gid=%llu\n",
 			   (u64)atomic64_read(&qtu_events.sockets_tagged),
 			   (u64)atomic64_read(&qtu_events.sockets_untagged),
 			   (u64)atomic64_read(&qtu_events.counter_set_changes),
@@ -1967,7 +1961,7 @@
 			   (u64)atomic64_read(&qtu_events.match_found_sk_in_ct),
 			   (u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct),
 			   (u64)atomic64_read(&qtu_events.match_no_sk),
-			   (u64)atomic64_read(&qtu_events.match_no_sk_file));
+			   (u64)atomic64_read(&qtu_events.match_no_sk_gid));
 
 		/* Count the following as part of the last item_index. No need
 		 * to lock the sock_tag_list here since it is already locked when
diff --git a/net/netfilter/xt_qtaguid_internal.h b/net/netfilter/xt_qtaguid_internal.h
index 8178fbd..c705270 100644
--- a/net/netfilter/xt_qtaguid_internal.h
+++ b/net/netfilter/xt_qtaguid_internal.h
@@ -289,10 +289,10 @@
 	 */
 	atomic64_t match_no_sk;
 	/*
-	 * The file ptr in the sk_socket wasn't there.
+	 * The file ptr in the sk_socket wasn't there and we couldn't get GID.
 	 * This might happen for traffic while the socket is being closed.
 	 */
-	atomic64_t match_no_sk_file;
+	atomic64_t match_no_sk_gid;
 };
 
 /* Track the set active_set for the given tag. */
diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c
index a52fbaf..ec87467 100644
--- a/net/netfilter/xt_socket.c
+++ b/net/netfilter/xt_socket.c
@@ -244,7 +244,7 @@
 			transparent = xt_socket_sk_is_transparent(sk);
 
 		if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard &&
-		    transparent)
+		    transparent && sk_fullsock(sk))
 			pskb->mark = sk->sk_mark;
 
 		sock_gen_put(sk);
@@ -433,7 +433,7 @@
 			transparent = xt_socket_sk_is_transparent(sk);
 
 		if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard &&
-		    transparent)
+		    transparent && sk_fullsock(sk))
 			pskb->mark = sk->sk_mark;
 
 		if (sk != skb->sk)
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 246f29d..2a5775f 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2211,10 +2211,13 @@
 
 	mutex_unlock(nlk->cb_mutex);
 
+	ret = 0;
 	if (cb->start)
-		cb->start(cb);
+		ret = cb->start(cb);
 
-	ret = netlink_dump(sk);
+	if (!ret)
+		ret = netlink_dump(sk);
+
 	sock_put(sk);
 
 	if (ret)
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 2b0f0ac..5a58f9f 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -209,6 +209,11 @@
 		}
 		create_info = (struct hci_create_pipe_resp *)skb->data;
 
+		if (create_info->pipe >= NFC_HCI_MAX_PIPES) {
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
+		}
+
 		/* Save the new created pipe and bind with local gate,
 		 * the description for skb->data[3] is destination gate id
 		 * but since we received this cmd from host controller, we
@@ -232,6 +237,11 @@
 		}
 		delete_info = (struct hci_delete_pipe_noti *)skb->data;
 
+		if (delete_info->pipe >= NFC_HCI_MAX_PIPES) {
+			status = NFC_HCI_ANY_E_NOK;
+			goto exit;
+		}
+
 		hdev->pipes[delete_info->pipe].gate = NFC_HCI_INVALID_GATE;
 		hdev->pipes[delete_info->pipe].dest_host = NFC_HCI_INVALID_HOST;
 		break;
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 4e03f64..05d9f42 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -1240,6 +1240,7 @@
 		goto out;
 	}
 
+	OVS_CB(skb)->acts_origlen = acts->orig_len;
 	err = do_execute_actions(dp, skb, key,
 				 acts->actions, acts->actions_len);
 
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
index 48386bf..b28e45b 100644
--- a/net/openvswitch/conntrack.c
+++ b/net/openvswitch/conntrack.c
@@ -1088,8 +1088,8 @@
 
 	nla_for_each_nested(a, attr, rem) {
 		int type = nla_type(a);
-		int maxlen = ovs_ct_attr_lens[type].maxlen;
-		int minlen = ovs_ct_attr_lens[type].minlen;
+		int maxlen;
+		int minlen;
 
 		if (type > OVS_CT_ATTR_MAX) {
 			OVS_NLERR(log,
@@ -1097,6 +1097,9 @@
 				  type, OVS_CT_ATTR_MAX);
 			return -EINVAL;
 		}
+
+		maxlen = ovs_ct_attr_lens[type].maxlen;
+		minlen = ovs_ct_attr_lens[type].minlen;
 		if (nla_len(a) < minlen || nla_len(a) > maxlen) {
 			OVS_NLERR(log,
 				  "Conntrack attr type has unexpected length (type=%d, length=%d, expected=%d)",
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index 4d67ea8..453f806 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -383,7 +383,7 @@
 }
 
 static size_t upcall_msg_size(const struct dp_upcall_info *upcall_info,
-			      unsigned int hdrlen)
+			      unsigned int hdrlen, int actions_attrlen)
 {
 	size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
 		+ nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */
@@ -400,7 +400,7 @@
 
 	/* OVS_PACKET_ATTR_ACTIONS */
 	if (upcall_info->actions_len)
-		size += nla_total_size(upcall_info->actions_len);
+		size += nla_total_size(actions_attrlen);
 
 	/* OVS_PACKET_ATTR_MRU */
 	if (upcall_info->mru)
@@ -467,7 +467,8 @@
 	else
 		hlen = skb->len;
 
-	len = upcall_msg_size(upcall_info, hlen - cutlen);
+	len = upcall_msg_size(upcall_info, hlen - cutlen,
+			      OVS_CB(skb)->acts_origlen);
 	user_skb = genlmsg_new(len, GFP_ATOMIC);
 	if (!user_skb) {
 		err = -ENOMEM;
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index ab85c1c..e19ace4 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -100,12 +100,14 @@
  * @input_vport: The original vport packet came in on. This value is cached
  * when a packet is received by OVS.
  * @mru: The maximum received fragement size; 0 if the packet is not
+ * @acts_origlen: The netlink size of the flow actions applied to this skb.
  * @cutlen: The number of bytes from the packet end to be removed.
  * fragmented.
  */
 struct ovs_skb_cb {
 	struct vport		*input_vport;
 	u16			mru;
+	u16			acts_origlen;
 	u32			cutlen;
 };
 #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 6a563e6..b17f909 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1648,10 +1648,6 @@
 
 	mutex_lock(&fanout_mutex);
 
-	err = -EINVAL;
-	if (!po->running)
-		goto out;
-
 	err = -EALREADY;
 	if (po->fanout)
 		goto out;
@@ -1700,7 +1696,10 @@
 		list_add(&match->list, &fanout_list);
 	}
 	err = -EINVAL;
-	if (match->type == type &&
+
+	spin_lock(&po->bind_lock);
+	if (po->running &&
+	    match->type == type &&
 	    match->prot_hook.type == po->prot_hook.type &&
 	    match->prot_hook.dev == po->prot_hook.dev) {
 		err = -ENOSPC;
@@ -1712,6 +1711,13 @@
 			err = 0;
 		}
 	}
+	spin_unlock(&po->bind_lock);
+
+	if (err && !atomic_read(&match->sk_ref)) {
+		list_del(&match->list);
+		kfree(match);
+	}
+
 out:
 	if (err && rollover) {
 		kfree(rollover);
@@ -2151,6 +2157,7 @@
 	struct timespec ts;
 	__u32 ts_status;
 	bool is_drop_n_account = false;
+	bool do_vnet = false;
 
 	/* struct tpacket{2,3}_hdr is aligned to a multiple of TPACKET_ALIGNMENT.
 	 * We may add members to them until current aligned size without forcing
@@ -2201,8 +2208,10 @@
 		netoff = TPACKET_ALIGN(po->tp_hdrlen +
 				       (maclen < 16 ? 16 : maclen)) +
 				       po->tp_reserve;
-		if (po->has_vnet_hdr)
+		if (po->has_vnet_hdr) {
 			netoff += sizeof(struct virtio_net_hdr);
+			do_vnet = true;
+		}
 		macoff = netoff - maclen;
 	}
 	if (po->tp_version <= TPACKET_V2) {
@@ -2219,8 +2228,10 @@
 					skb_set_owner_r(copy_skb, sk);
 			}
 			snaplen = po->rx_ring.frame_size - macoff;
-			if ((int)snaplen < 0)
+			if ((int)snaplen < 0) {
 				snaplen = 0;
+				do_vnet = false;
+			}
 		}
 	} else if (unlikely(macoff + snaplen >
 			    GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len)) {
@@ -2233,6 +2244,7 @@
 		if (unlikely((int)snaplen < 0)) {
 			snaplen = 0;
 			macoff = GET_PBDQC_FROM_RB(&po->rx_ring)->max_frame_len;
+			do_vnet = false;
 		}
 	}
 	spin_lock(&sk->sk_receive_queue.lock);
@@ -2258,7 +2270,7 @@
 	}
 	spin_unlock(&sk->sk_receive_queue.lock);
 
-	if (po->has_vnet_hdr) {
+	if (do_vnet) {
 		if (__packet_rcv_vnet(skb, h.raw + macoff -
 					   sizeof(struct virtio_net_hdr))) {
 			spin_lock(&sk->sk_receive_queue.lock);
@@ -2826,6 +2838,7 @@
 	struct virtio_net_hdr vnet_hdr = { 0 };
 	int offset = 0;
 	struct packet_sock *po = pkt_sk(sk);
+	bool has_vnet_hdr = false;
 	int hlen, tlen, linear;
 	int extra_len = 0;
 
@@ -2869,6 +2882,7 @@
 		err = packet_snd_vnet_parse(msg, &len, &vnet_hdr);
 		if (err)
 			goto out_unlock;
+		has_vnet_hdr = true;
 	}
 
 	if (unlikely(sock_flag(sk, SOCK_NOFCS))) {
@@ -2929,7 +2943,7 @@
 
 	packet_pick_tx_queue(dev, skb);
 
-	if (po->has_vnet_hdr) {
+	if (has_vnet_hdr) {
 		err = packet_snd_vnet_gso(skb, &vnet_hdr);
 		if (err)
 			goto out_free;
@@ -3057,13 +3071,15 @@
 	int ret = 0;
 	bool unlisted = false;
 
-	if (po->fanout)
-		return -EINVAL;
-
 	lock_sock(sk);
 	spin_lock(&po->bind_lock);
 	rcu_read_lock();
 
+	if (po->fanout) {
+		ret = -EINVAL;
+		goto out_unlock;
+	}
+
 	if (name) {
 		dev = dev_get_by_name_rcu(sock_net(sk), name);
 		if (!dev) {
@@ -3698,14 +3714,19 @@
 
 		if (optlen != sizeof(val))
 			return -EINVAL;
-		if (po->rx_ring.pg_vec || po->tx_ring.pg_vec)
-			return -EBUSY;
 		if (copy_from_user(&val, optval, sizeof(val)))
 			return -EFAULT;
 		if (val > INT_MAX)
 			return -EINVAL;
-		po->tp_reserve = val;
-		return 0;
+		lock_sock(sk);
+		if (po->rx_ring.pg_vec || po->tx_ring.pg_vec) {
+			ret = -EBUSY;
+		} else {
+			po->tp_reserve = val;
+			ret = 0;
+		}
+		release_sock(sk);
+		return ret;
 	}
 	case PACKET_LOSS:
 	{
@@ -3873,6 +3894,8 @@
 	case PACKET_HDRLEN:
 		if (len > sizeof(int))
 			len = sizeof(int);
+		if (len < sizeof(int))
+			return -EINVAL;
 		if (copy_from_user(&val, optval, len))
 			return -EFAULT;
 		switch (val) {
@@ -4322,7 +4345,7 @@
 		register_prot_hook(sk);
 	}
 	spin_unlock(&po->bind_lock);
-	if (closing && (po->tp_version > TPACKET_V2)) {
+	if (pg_vec && (po->tp_version > TPACKET_V2)) {
 		/* Because we don't support block-based V3 on tx-ring */
 		if (!tx_ring)
 			prb_shutdown_retire_blk_timer(po, rb_queue);
diff --git a/net/rds/ib_cm.c b/net/rds/ib_cm.c
index 5b2ab95..169156c 100644
--- a/net/rds/ib_cm.c
+++ b/net/rds/ib_cm.c
@@ -405,7 +405,7 @@
 		ret = PTR_ERR(ic->i_send_cq);
 		ic->i_send_cq = NULL;
 		rdsdebug("ib_create_cq send failed: %d\n", ret);
-		goto out;
+		goto rds_ibdev_out;
 	}
 
 	cq_attr.cqe = ic->i_recv_ring.w_nr;
@@ -416,19 +416,19 @@
 		ret = PTR_ERR(ic->i_recv_cq);
 		ic->i_recv_cq = NULL;
 		rdsdebug("ib_create_cq recv failed: %d\n", ret);
-		goto out;
+		goto send_cq_out;
 	}
 
 	ret = ib_req_notify_cq(ic->i_send_cq, IB_CQ_NEXT_COMP);
 	if (ret) {
 		rdsdebug("ib_req_notify_cq send failed: %d\n", ret);
-		goto out;
+		goto recv_cq_out;
 	}
 
 	ret = ib_req_notify_cq(ic->i_recv_cq, IB_CQ_SOLICITED);
 	if (ret) {
 		rdsdebug("ib_req_notify_cq recv failed: %d\n", ret);
-		goto out;
+		goto recv_cq_out;
 	}
 
 	/* XXX negotiate max send/recv with remote? */
@@ -453,7 +453,7 @@
 	ret = rdma_create_qp(ic->i_cm_id, ic->i_pd, &attr);
 	if (ret) {
 		rdsdebug("rdma_create_qp failed: %d\n", ret);
-		goto out;
+		goto recv_cq_out;
 	}
 
 	ic->i_send_hdrs = ib_dma_alloc_coherent(dev,
@@ -463,7 +463,7 @@
 	if (!ic->i_send_hdrs) {
 		ret = -ENOMEM;
 		rdsdebug("ib_dma_alloc_coherent send failed\n");
-		goto out;
+		goto qp_out;
 	}
 
 	ic->i_recv_hdrs = ib_dma_alloc_coherent(dev,
@@ -473,7 +473,7 @@
 	if (!ic->i_recv_hdrs) {
 		ret = -ENOMEM;
 		rdsdebug("ib_dma_alloc_coherent recv failed\n");
-		goto out;
+		goto send_hdrs_dma_out;
 	}
 
 	ic->i_ack = ib_dma_alloc_coherent(dev, sizeof(struct rds_header),
@@ -481,7 +481,7 @@
 	if (!ic->i_ack) {
 		ret = -ENOMEM;
 		rdsdebug("ib_dma_alloc_coherent ack failed\n");
-		goto out;
+		goto recv_hdrs_dma_out;
 	}
 
 	ic->i_sends = vzalloc_node(ic->i_send_ring.w_nr * sizeof(struct rds_ib_send_work),
@@ -489,7 +489,7 @@
 	if (!ic->i_sends) {
 		ret = -ENOMEM;
 		rdsdebug("send allocation failed\n");
-		goto out;
+		goto ack_dma_out;
 	}
 
 	ic->i_recvs = vzalloc_node(ic->i_recv_ring.w_nr * sizeof(struct rds_ib_recv_work),
@@ -497,7 +497,7 @@
 	if (!ic->i_recvs) {
 		ret = -ENOMEM;
 		rdsdebug("recv allocation failed\n");
-		goto out;
+		goto sends_out;
 	}
 
 	rds_ib_recv_init_ack(ic);
@@ -505,8 +505,33 @@
 	rdsdebug("conn %p pd %p cq %p %p\n", conn, ic->i_pd,
 		 ic->i_send_cq, ic->i_recv_cq);
 
-out:
+	return ret;
+
+sends_out:
+	vfree(ic->i_sends);
+ack_dma_out:
+	ib_dma_free_coherent(dev, sizeof(struct rds_header),
+			     ic->i_ack, ic->i_ack_dma);
+recv_hdrs_dma_out:
+	ib_dma_free_coherent(dev, ic->i_recv_ring.w_nr *
+					sizeof(struct rds_header),
+					ic->i_recv_hdrs, ic->i_recv_hdrs_dma);
+send_hdrs_dma_out:
+	ib_dma_free_coherent(dev, ic->i_send_ring.w_nr *
+					sizeof(struct rds_header),
+					ic->i_send_hdrs, ic->i_send_hdrs_dma);
+qp_out:
+	rdma_destroy_qp(ic->i_cm_id);
+recv_cq_out:
+	if (!ib_destroy_cq(ic->i_recv_cq))
+		ic->i_recv_cq = NULL;
+send_cq_out:
+	if (!ib_destroy_cq(ic->i_send_cq))
+		ic->i_send_cq = NULL;
+rds_ibdev_out:
+	rds_ib_remove_conn(rds_ibdev, conn);
 	rds_ib_dev_put(rds_ibdev);
+
 	return ret;
 }
 
diff --git a/net/rds/ib_send.c b/net/rds/ib_send.c
index 84d90c9..1910981 100644
--- a/net/rds/ib_send.c
+++ b/net/rds/ib_send.c
@@ -69,16 +69,6 @@
 	complete(rm, notify_status);
 }
 
-static void rds_ib_send_unmap_data(struct rds_ib_connection *ic,
-				   struct rm_data_op *op,
-				   int wc_status)
-{
-	if (op->op_nents)
-		ib_dma_unmap_sg(ic->i_cm_id->device,
-				op->op_sg, op->op_nents,
-				DMA_TO_DEVICE);
-}
-
 static void rds_ib_send_unmap_rdma(struct rds_ib_connection *ic,
 				   struct rm_rdma_op *op,
 				   int wc_status)
@@ -139,6 +129,21 @@
 		rds_ib_stats_inc(s_ib_atomic_fadd);
 }
 
+static void rds_ib_send_unmap_data(struct rds_ib_connection *ic,
+				   struct rm_data_op *op,
+				   int wc_status)
+{
+	struct rds_message *rm = container_of(op, struct rds_message, data);
+
+	if (op->op_nents)
+		ib_dma_unmap_sg(ic->i_cm_id->device,
+				op->op_sg, op->op_nents,
+				DMA_TO_DEVICE);
+
+	if (rm->rdma.op_active && rm->data.op_notify)
+		rds_ib_send_unmap_rdma(ic, &rm->rdma, wc_status);
+}
+
 /*
  * Unmap the resources associated with a struct send_work.
  *
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 4c93bad..8d3a851 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -626,6 +626,16 @@
 		}
 		op->op_notifier->n_user_token = args->user_token;
 		op->op_notifier->n_status = RDS_RDMA_SUCCESS;
+
+		/* Enable rmda notification on data operation for composite
+		 * rds messages and make sure notification is enabled only
+		 * for the data operation which follows it so that application
+		 * gets notified only after full message gets delivered.
+		 */
+		if (rm->data.op_sg) {
+			rm->rdma.op_notify = 0;
+			rm->data.op_notify = !!(args->flags & RDS_RDMA_NOTIFY_ME);
+		}
 	}
 
 	/* The cookie contains the R_Key of the remote memory region, and
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 67ba67c..f107a96 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -414,6 +414,7 @@
 		} rdma;
 		struct rm_data_op {
 			unsigned int		op_active:1;
+			unsigned int		op_notify:1;
 			unsigned int		op_nents;
 			unsigned int		op_count;
 			unsigned int		op_dmasg;
diff --git a/net/rds/send.c b/net/rds/send.c
index 896626b..f28651b 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -475,12 +475,14 @@
 	struct rm_rdma_op *ro;
 	struct rds_notifier *notifier;
 	unsigned long flags;
+	unsigned int notify = 0;
 
 	spin_lock_irqsave(&rm->m_rs_lock, flags);
 
+	notify =  rm->rdma.op_notify | rm->data.op_notify;
 	ro = &rm->rdma;
 	if (test_bit(RDS_MSG_ON_SOCK, &rm->m_flags) &&
-	    ro->op_active && ro->op_notify && ro->op_notifier) {
+	    ro->op_active && notify && ro->op_notifier) {
 		notifier = ro->op_notifier;
 		rs = rm->m_rs;
 		sock_hold(rds_rs_to_sk(rs));
diff --git a/net/rmnet_data/rmnet_data_config.h b/net/rmnet_data/rmnet_data_config.h
index 5ce4600..aa8a0b5 100644
--- a/net/rmnet_data/rmnet_data_config.h
+++ b/net/rmnet_data/rmnet_data_config.h
@@ -34,12 +34,14 @@
  *            parmeter depends on the rmnet_mode
  */
 struct rmnet_logical_ep_conf_s {
+	struct net_device *egress_dev;
+	struct timespec last_flush_time;
+	long curr_time_limit;
+	unsigned int flush_byte_count;
+	unsigned int curr_byte_threshold;
 	u8 refcount;
 	u8 rmnet_mode;
 	u8 mux_id;
-	struct timespec flush_time;
-	unsigned int flush_byte_count;
-	struct net_device *egress_dev;
 };
 
 /**
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index 35be79e..a5b22c4 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -41,26 +41,30 @@
 MODULE_PARM_DESC(dump_pkt_tx, "Dump packets exiting egress handler");
 #endif /* CONFIG_RMNET_DATA_DEBUG_PKT */
 
-/* Time in nano seconds. This number must be less that a second. */
-long gro_flush_time __read_mostly = 10000L;
-module_param(gro_flush_time, long, 0644);
-MODULE_PARM_DESC(gro_flush_time, "Flush GRO when spaced more than this");
+static bool gro_flush_logic_on __read_mostly = 1;
+module_param(gro_flush_logic_on, bool, 0644);
+MODULE_PARM_DESC(gro_flush_logic_on, "If off let GRO determine flushing");
 
-unsigned int gro_min_byte_thresh __read_mostly = 7500;
-module_param(gro_min_byte_thresh, uint, 0644);
-MODULE_PARM_DESC(gro_min_byte_thresh, "Min byte thresh to change flush time");
-
-unsigned int dynamic_gro_on __read_mostly = 1;
-module_param(dynamic_gro_on, uint, 0644);
+static bool dynamic_gro_on __read_mostly = 1;
+module_param(dynamic_gro_on, bool, 0644);
 MODULE_PARM_DESC(dynamic_gro_on, "Toggle to turn on dynamic gro logic");
 
+/* Time in nano seconds. This number must be less that a second. */
+static long lower_flush_time __read_mostly = 10000L;
+module_param(lower_flush_time, long, 0644);
+MODULE_PARM_DESC(lower_flush_time, "Min time value for flushing GRO");
+
+static unsigned int lower_byte_limit __read_mostly = 7500;
+module_param(lower_byte_limit, uint, 0644);
+MODULE_PARM_DESC(lower_byte_limit, "Min byte count for flushing GRO");
+
 unsigned int upper_flush_time __read_mostly = 15000;
 module_param(upper_flush_time, uint, 0644);
-MODULE_PARM_DESC(upper_flush_time, "Upper limit on flush time");
+MODULE_PARM_DESC(upper_flush_time, "Max time value for flushing GRO");
 
 unsigned int upper_byte_limit __read_mostly = 10500;
 module_param(upper_byte_limit, uint, 0644);
-MODULE_PARM_DESC(upper_byte_limit, "Upper byte limit");
+MODULE_PARM_DESC(upper_byte_limit, "Max byte count for flushing GRO");
 
 #define RMNET_DATA_IP_VERSION_4 0x40
 #define RMNET_DATA_IP_VERSION_6 0x60
@@ -184,19 +188,35 @@
 	return RX_HANDLER_CONSUMED;
 }
 
-#ifdef NET_SKBUFF_DATA_USES_OFFSET
-static void rmnet_reset_mac_header(struct sk_buff *skb)
+/* RX/TX Fixup */
+
+/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
+ * @skb:        Socket buffer ("packet") to modify
+ * @dev:        Virtual network device
+ *
+ * Additional VND specific packet processing for ingress packets
+ *
+ * Return: void
+ */
+static void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
 {
-	skb->mac_header = 0;
-	skb->mac_len = 0;
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += skb->len;
 }
-#else
-static void rmnet_reset_mac_header(struct sk_buff *skb)
+
+/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
+ * @skb:      Socket buffer ("packet") to modify
+ * @dev:      Virtual network device
+ *
+ * Additional VND specific packet processing for egress packets
+ *
+ * Return: void
+ */
+static void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
 {
-	skb->mac_header = skb->network_header;
-	skb->mac_len = 0;
+	dev->stats.tx_packets++;
+	dev->stats.tx_bytes += skb->len;
 }
-#endif /*NET_SKBUFF_DATA_USES_OFFSET*/
 
 /* rmnet_check_skb_can_gro() - Check is skb can be passed through GRO handler
  *
@@ -242,62 +262,64 @@
 {
 	struct timespec curr_time, diff;
 
-	if (!gro_flush_time)
+	if (!gro_flush_logic_on)
 		return;
 
-	if (unlikely(ep->flush_time.tv_sec == 0)) {
-		getnstimeofday(&ep->flush_time);
+	if (unlikely(ep->last_flush_time.tv_sec == 0)) {
+		getnstimeofday(&ep->last_flush_time);
 		ep->flush_byte_count = 0;
+		ep->curr_time_limit = lower_flush_time;
+		ep->curr_byte_threshold = lower_byte_limit;
 	} else {
 		getnstimeofday(&(curr_time));
-		diff = timespec_sub(curr_time, ep->flush_time);
+		diff = timespec_sub(curr_time, ep->last_flush_time);
 		ep->flush_byte_count += skb_size;
 
 		if (dynamic_gro_on) {
 			if ((!(diff.tv_sec > 0) || diff.tv_nsec <=
-					gro_flush_time) &&
+					ep->curr_time_limit) &&
 					ep->flush_byte_count >=
-					gro_min_byte_thresh) {
+					ep->curr_byte_threshold) {
 				/* Processed many bytes in a small time window.
 				 * No longer need to flush so often and we can
 				 * increase our byte limit
 				 */
-				gro_flush_time = upper_flush_time;
-				gro_min_byte_thresh = upper_byte_limit;
+				ep->curr_time_limit = upper_flush_time;
+				ep->curr_byte_threshold = upper_byte_limit;
 			} else if ((diff.tv_sec > 0 ||
-					diff.tv_nsec > gro_flush_time) &&
+					diff.tv_nsec > ep->curr_time_limit) &&
 					ep->flush_byte_count <
-					gro_min_byte_thresh) {
+					ep->curr_byte_threshold) {
 				/* We have not hit our time limit and we are not
 				 * receive many bytes. Demote ourselves to the
 				 * lowest limits and flush
 				 */
 				napi_gro_flush(napi, false);
-				getnstimeofday(&ep->flush_time);
+				ep->last_flush_time = curr_time;
 				ep->flush_byte_count = 0;
-				gro_flush_time = 10000L;
-				gro_min_byte_thresh = 7500L;
+				ep->curr_time_limit = lower_flush_time;
+				ep->curr_byte_threshold = lower_byte_limit;
 			} else if ((diff.tv_sec > 0 ||
-					diff.tv_nsec > gro_flush_time) &&
+					diff.tv_nsec > ep->curr_time_limit) &&
 					ep->flush_byte_count >=
-					gro_min_byte_thresh) {
+					ep->curr_byte_threshold) {
 				/* Above byte and time limt, therefore we can
 				 * move/maintain our limits to be the max
 				 * and flush
 				 */
 				napi_gro_flush(napi, false);
-				getnstimeofday(&ep->flush_time);
+				ep->last_flush_time = curr_time;
 				ep->flush_byte_count = 0;
-				gro_flush_time = upper_flush_time;
-				gro_min_byte_thresh = upper_byte_limit;
+				ep->curr_time_limit = upper_flush_time;
+				ep->curr_byte_threshold = upper_byte_limit;
 			}
 			/* else, below time limit and below
 			 * byte thresh, so change nothing
 			 */
 		} else if (diff.tv_sec > 0 ||
-				diff.tv_nsec >= gro_flush_time) {
+				diff.tv_nsec >= lower_flush_time) {
 			napi_gro_flush(napi, false);
-			getnstimeofday(&ep->flush_time);
+			ep->last_flush_time = curr_time;
 			ep->flush_byte_count = 0;
 		}
 	}
@@ -321,42 +343,33 @@
 
 	trace___rmnet_deliver_skb(skb);
 	switch (ep->rmnet_mode) {
+	case RMNET_EPMODE_VND:
+		skb_reset_transport_header(skb);
+		skb_reset_network_header(skb);
+		rmnet_vnd_rx_fixup(skb, skb->dev);
+
+		skb->pkt_type = PACKET_HOST;
+		skb_set_mac_header(skb, 0);
+
+		if (rmnet_check_skb_can_gro(skb) &&
+		    (skb->dev->features & NETIF_F_GRO)) {
+			napi = get_current_napi_context();
+
+			skb_size = skb->len;
+			gro_res = napi_gro_receive(napi, skb);
+			trace_rmnet_gro_downlink(gro_res);
+			rmnet_optional_gro_flush(napi, ep, skb_size);
+		} else{
+			netif_receive_skb(skb);
+		}
+		return RX_HANDLER_CONSUMED;
+
 	case RMNET_EPMODE_NONE:
 		return RX_HANDLER_PASS;
 
 	case RMNET_EPMODE_BRIDGE:
 		return rmnet_bridge_handler(skb, ep);
 
-	case RMNET_EPMODE_VND:
-		skb_reset_transport_header(skb);
-		skb_reset_network_header(skb);
-		switch (rmnet_vnd_rx_fixup(skb, skb->dev)) {
-		case RX_HANDLER_CONSUMED:
-			return RX_HANDLER_CONSUMED;
-
-		case RX_HANDLER_PASS:
-			skb->pkt_type = PACKET_HOST;
-			rmnet_reset_mac_header(skb);
-			if (rmnet_check_skb_can_gro(skb) &&
-			    (skb->dev->features & NETIF_F_GRO)) {
-				napi = get_current_napi_context();
-				if (napi) {
-					skb_size = skb->len;
-					gro_res = napi_gro_receive(napi, skb);
-					trace_rmnet_gro_downlink(gro_res);
-					rmnet_optional_gro_flush(napi, ep,
-								 skb_size);
-				} else {
-					WARN_ONCE(1, "current napi is NULL\n");
-					netif_receive_skb(skb);
-				}
-			} else {
-				netif_receive_skb(skb);
-			}
-			return RX_HANDLER_CONSUMED;
-		}
-		return RX_HANDLER_PASS;
-
 	default:
 		LOGD("Unknown ep mode %d", ep->rmnet_mode);
 		rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_DELIVER_NO_EP);
@@ -441,16 +454,7 @@
 
 	ep = &config->muxed_ep[mux_id];
 
-	if (!ep->refcount) {
-		LOGD("Packet on %s:%d; has no logical endpoint config",
-		     skb->dev->name, mux_id);
-
-		rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP);
-		return RX_HANDLER_CONSUMED;
-	}
-
-	if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING)
-		skb->dev = ep->egress_dev;
+	skb->dev = ep->egress_dev;
 
 	if ((config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV3) ||
 	    (config->ingress_data_format & RMNET_INGRESS_FORMAT_MAP_CKSUMV4)) {
@@ -462,6 +466,7 @@
 			skb->ip_summed |= CHECKSUM_UNNECESSARY;
 		else if (ckresult !=
 			 RMNET_MAP_CHECKSUM_ERR_UNKNOWN_IP_VERSION &&
+			 ckresult != RMNET_MAP_CHECKSUM_VALIDATION_FAILED &&
 			 ckresult != RMNET_MAP_CHECKSUM_ERR_UNKNOWN_TRANSPORT &&
 			 ckresult != RMNET_MAP_CHECKSUM_VALID_FLAG_NOT_SET &&
 			 ckresult != RMNET_MAP_CHECKSUM_FRAGMENTED_PACKET) {
@@ -495,17 +500,13 @@
 	(struct sk_buff *skb, struct rmnet_phys_ep_config *config)
 {
 	struct sk_buff *skbn;
-	int rc, co = 0;
+	int rc;
 
 	if (config->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) {
 		trace_rmnet_start_deaggregation(skb);
 		while ((skbn = rmnet_map_deaggregate(skb, config)) != 0) {
 			_rmnet_map_ingress_handler(skbn, config);
-			co++;
 		}
-		trace_rmnet_end_deaggregation(skb, co);
-		LOGD("De-aggregated %d packets", co);
-		rmnet_stats_deagg_pkts(co);
 		rmnet_kfree_skb(skb, RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF);
 		rc = RX_HANDLER_CONSUMED;
 	} else {
@@ -537,12 +538,16 @@
 {
 	int required_headroom, additional_header_length, ckresult;
 	struct rmnet_map_header_s *map_header;
+	int non_linear_skb;
+	int csum_required = (config->egress_data_format &
+			     RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
+			    (config->egress_data_format &
+			     RMNET_EGRESS_FORMAT_MAP_CKSUMV4);
 
 	additional_header_length = 0;
 
 	required_headroom = sizeof(struct rmnet_map_header_s);
-	if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
-	    (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
+	if (csum_required) {
 		required_headroom +=
 			sizeof(struct rmnet_map_ul_checksum_header_s);
 		additional_header_length +=
@@ -557,17 +562,19 @@
 		return 1;
 	}
 
-	if ((config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV3) ||
-	    (config->egress_data_format & RMNET_EGRESS_FORMAT_MAP_CKSUMV4)) {
+	if (csum_required) {
 		ckresult = rmnet_map_checksum_uplink_packet
 				(skb, orig_dev, config->egress_data_format);
 		trace_rmnet_map_checksum_uplink_packet(orig_dev, ckresult);
 		rmnet_stats_ul_checksum(ckresult);
 	}
 
+	non_linear_skb = (orig_dev->features & NETIF_F_GSO) &&
+			 skb_is_nonlinear(skb);
+
 	if ((!(config->egress_data_format &
-	    RMNET_EGRESS_FORMAT_AGGREGATION)) ||
-	    ((orig_dev->features & NETIF_F_GSO) && skb_is_nonlinear(skb)))
+	    RMNET_EGRESS_FORMAT_AGGREGATION)) || csum_required ||
+	    non_linear_skb)
 		map_header = rmnet_map_add_map_header
 		(skb, additional_header_length, RMNET_MAP_NO_PAD_BYTES);
 	else
@@ -590,6 +597,13 @@
 	skb->protocol = htons(ETH_P_MAP);
 
 	if (config->egress_data_format & RMNET_EGRESS_FORMAT_AGGREGATION) {
+		if (rmnet_ul_aggregation_skip(skb, required_headroom))
+			return RMNET_MAP_SUCCESS;
+
+		if (non_linear_skb)
+			if (unlikely(__skb_linearize(skb)))
+				return RMNET_MAP_SUCCESS;
+
 		rmnet_map_aggregate(skb, config);
 		return RMNET_MAP_CONSUMED;
 	}
diff --git a/net/rmnet_data/rmnet_data_stats.c b/net/rmnet_data/rmnet_data_stats.c
index f4aa492..db74c4f 100644
--- a/net/rmnet_data/rmnet_data_stats.c
+++ b/net/rmnet_data/rmnet_data_stats.c
@@ -41,11 +41,6 @@
 module_param_array(queue_xmit, ulong, 0, 0444);
 MODULE_PARM_DESC(queue_xmit, "SKBs queued for transmit");
 
-static DEFINE_SPINLOCK(rmnet_deagg_count);
-unsigned long int deagg_count[RMNET_STATS_AGG_MAX];
-module_param_array(deagg_count, ulong, 0, 0444);
-MODULE_PARM_DESC(deagg_count, "SKBs De-aggregated");
-
 static DEFINE_SPINLOCK(rmnet_agg_count);
 unsigned long int agg_count[RMNET_STATS_AGG_MAX];
 module_param_array(agg_count, ulong, 0, 0444);
@@ -72,16 +67,7 @@
 	skb_free[reason]++;
 	spin_unlock_irqrestore(&rmnet_skb_free_lock, flags);
 
-	if (likely(skb)) {
-		struct rmnet_phys_ep_conf_s *config;
-
-		config = (struct rmnet_phys_ep_conf_s *)rcu_dereference
-			 (skb->dev->rx_handler_data);
-		if (likely(config))
-			config->recycle(skb);
-		else
-			kfree_skb(skb);
-	}
+	kfree_skb(skb);
 }
 
 void rmnet_stats_queue_xmit(int rc, unsigned int reason)
@@ -108,16 +94,6 @@
 	spin_unlock_irqrestore(&rmnet_agg_count, flags);
 }
 
-void rmnet_stats_deagg_pkts(int aggcount)
-{
-	unsigned long flags;
-
-	spin_lock_irqsave(&rmnet_deagg_count, flags);
-	deagg_count[RMNET_STATS_AGG_BUFF]++;
-	deagg_count[RMNET_STATS_AGG_PKT] += aggcount;
-	spin_unlock_irqrestore(&rmnet_deagg_count, flags);
-}
-
 void rmnet_stats_dl_checksum(unsigned int rc)
 {
 	unsigned long flags;
diff --git a/net/rmnet_data/rmnet_data_stats.h b/net/rmnet_data/rmnet_data_stats.h
index e3350ef..366e486 100644
--- a/net/rmnet_data/rmnet_data_stats.h
+++ b/net/rmnet_data/rmnet_data_stats.h
@@ -24,7 +24,6 @@
 	RMNET_STATS_SKBFREE_DELIVER_NO_EP,
 	RMNET_STATS_SKBFREE_IPINGRESS_NO_EP,
 	RMNET_STATS_SKBFREE_MAPINGRESS_BAD_MUX,
-	RMNET_STATS_SKBFREE_MAPINGRESS_MUX_NO_EP,
 	RMNET_STATS_SKBFREE_MAPINGRESS_AGGBUF,
 	RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPD,
 	RMNET_STATS_SKBFREE_INGRESS_NOT_EXPECT_MAPC,
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 72f3c3b..c4ef460 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -101,55 +101,6 @@
 	}
 }
 
-/* RX/TX Fixup */
-
-/* rmnet_vnd_rx_fixup() - Virtual Network Device receive fixup hook
- * @skb:        Socket buffer ("packet") to modify
- * @dev:        Virtual network device
- *
- * Additional VND specific packet processing for ingress packets
- *
- * Return:
- *      - RX_HANDLER_PASS if packet should continue to process in stack
- *      - RX_HANDLER_CONSUMED if packet should not be processed in stack
- *
- */
-int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev)
-{
-	if (unlikely(!dev || !skb))
-		return RX_HANDLER_CONSUMED;
-
-	dev->stats.rx_packets++;
-	dev->stats.rx_bytes += skb->len;
-
-	return RX_HANDLER_PASS;
-}
-
-/* rmnet_vnd_tx_fixup() - Virtual Network Device transmic fixup hook
- * @skb:      Socket buffer ("packet") to modify
- * @dev:      Virtual network device
- *
- * Additional VND specific packet processing for egress packets
- *
- * Return:
- *      - RX_HANDLER_PASS if packet should continue to be transmitted
- *      - RX_HANDLER_CONSUMED if packet should not be transmitted by stack
- */
-int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev)
-{
-	struct rmnet_vnd_private_s *dev_conf;
-
-	dev_conf = (struct rmnet_vnd_private_s *)netdev_priv(dev);
-
-	if (unlikely(!dev || !skb))
-		return RX_HANDLER_CONSUMED;
-
-	dev->stats.tx_packets++;
-	dev->stats.tx_bytes += skb->len;
-
-	return RX_HANDLER_PASS;
-}
-
 /* Network Device Operations */
 
 /* rmnet_vnd_start_xmit() - Transmit NDO callback
@@ -220,12 +171,16 @@
 
 	switch (cmd) {
 	case RMNET_IOCTL_SET_QOS_ENABLE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		LOGM("RMNET_IOCTL_SET_QOS_ENABLE on %s", dev->name);
 		if (!dev_conf->qos_version)
 			dev_conf->qos_version = RMNET_IOCTL_QOS_MODE_6;
 		break;
 
 	case RMNET_IOCTL_SET_QOS_DISABLE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		LOGM("RMNET_IOCTL_SET_QOS_DISABLE on %s", dev->name);
 		dev_conf->qos_version = 0;
 		break;
@@ -240,6 +195,8 @@
 		break;
 
 	case RMNET_IOCTL_FLOW_ENABLE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		LOGL("RMNET_IOCTL_FLOW_ENABLE on %s", dev->name);
 		if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data,
 				   sizeof(struct rmnet_ioctl_data_s))) {
@@ -252,6 +209,8 @@
 		break;
 
 	case RMNET_IOCTL_FLOW_DISABLE:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		LOGL("RMNET_IOCTL_FLOW_DISABLE on %s", dev->name);
 		if (copy_from_user(&ioctl_data, ifr->ifr_ifru.ifru_data,
 				   sizeof(struct rmnet_ioctl_data_s))) {
@@ -367,6 +326,8 @@
 		break;
 
 	case RMNET_IOCTL_SET_QOS_VERSION:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		if (ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_6 ||
 		    ext_cmd.u.data == RMNET_IOCTL_QOS_MODE_8 ||
 		    ext_cmd.u.data == 0) {
diff --git a/net/rmnet_data/rmnet_data_vnd.h b/net/rmnet_data/rmnet_data_vnd.h
index 9d8eb54..1d3a63b 100644
--- a/net/rmnet_data/rmnet_data_vnd.h
+++ b/net/rmnet_data/rmnet_data_vnd.h
@@ -27,8 +27,6 @@
 int rmnet_vnd_create_dev(int id, struct net_device **new_device,
 			 const char *prefix, int use_name);
 int rmnet_vnd_free_dev(int id);
-int rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev);
-int rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev);
 int rmnet_vnd_is_vnd(struct net_device *dev);
 int rmnet_vnd_add_tc_flow(u32 id, u32 map_flow, u32 tc_flow);
 int rmnet_vnd_del_tc_flow(u32 id, u32 map_flow, u32 tc_flow);
diff --git a/net/rmnet_data/rmnet_map.h b/net/rmnet_data/rmnet_map.h
index f597f1b..3bab6d9 100644
--- a/net/rmnet_data/rmnet_map.h
+++ b/net/rmnet_data/rmnet_map.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -146,5 +146,5 @@
 int rmnet_map_checksum_uplink_packet(struct sk_buff *skb,
 				     struct net_device *orig_dev,
 				     u32 egress_data_format);
-
+int rmnet_ul_aggregation_skip(struct sk_buff *skb, int offset);
 #endif /* _RMNET_MAP_H_ */
diff --git a/net/rmnet_data/rmnet_map_data.c b/net/rmnet_data/rmnet_map_data.c
index d7e420b..1c0f1060 100644
--- a/net/rmnet_data/rmnet_map_data.c
+++ b/net/rmnet_data/rmnet_map_data.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -234,16 +234,9 @@
 
 	if (!skb || !config)
 		return;
-	size = config->egress_agg_size - skb->len;
-
-	if (size < 2000) {
-		LOGL("Invalid length %d", size);
-		return;
-	}
 
 new_packet:
 	spin_lock_irqsave(&config->agg_lock, flags);
-
 	memcpy(&last, &config->agg_last, sizeof(struct timespec));
 	getnstimeofday(&config->agg_last);
 
@@ -265,6 +258,7 @@
 			return;
 		}
 
+		size = config->egress_agg_size - skb->len;
 		config->agg_skb = skb_copy_expand(skb, 0, size, GFP_ATOMIC);
 		if (!config->agg_skb) {
 			config->agg_skb = 0;
@@ -748,3 +742,31 @@
 done:
 	return ret;
 }
+
+int rmnet_ul_aggregation_skip(struct sk_buff *skb, int offset)
+{
+	unsigned char *packet_start = skb->data + offset;
+	int is_icmp = 0;
+
+	if ((skb->data[offset]) >> 4 == 0x04) {
+		struct iphdr *ip4h = (struct iphdr *)(packet_start);
+
+		if (ip4h->protocol == IPPROTO_ICMP)
+			is_icmp = 1;
+	} else if ((skb->data[offset]) >> 4 == 0x06) {
+		struct ipv6hdr *ip6h = (struct ipv6hdr *)(packet_start);
+
+		if (ip6h->nexthdr == IPPROTO_ICMPV6) {
+			is_icmp = 1;
+		} else if (ip6h->nexthdr == NEXTHDR_FRAGMENT) {
+			struct frag_hdr *frag;
+
+			frag = (struct frag_hdr *)(packet_start
+						   + sizeof(struct ipv6hdr));
+			if (frag->nexthdr == IPPROTO_ICMPV6)
+				is_icmp = 1;
+		}
+	}
+
+	return is_icmp;
+}
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index c651cfc..f311732 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -141,7 +141,7 @@
 		hlist_for_each_entry_safe(p, n, head, tcfa_head) {
 			ret = __tcf_hash_release(p, false, true);
 			if (ret == ACT_P_DELETED) {
-				module_put(p->ops->owner);
+				module_put(ops->owner);
 				n_i++;
 			} else if (ret < 0)
 				goto nla_put_failure;
@@ -450,13 +450,15 @@
 
 int tcf_action_destroy(struct list_head *actions, int bind)
 {
+	const struct tc_action_ops *ops;
 	struct tc_action *a, *tmp;
 	int ret = 0;
 
 	list_for_each_entry_safe(a, tmp, actions, list) {
+		ops = a->ops;
 		ret = __tcf_hash_release(a, bind, true);
 		if (ret == ACT_P_DELETED)
-			module_put(a->ops->owner);
+			module_put(ops->owner);
 		else if (ret < 0)
 			return ret;
 	}
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 378c1c9..5003051 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -41,6 +41,7 @@
 {
 	struct xt_tgchk_param par;
 	struct xt_target *target;
+	struct ipt_entry e = {};
 	int ret = 0;
 
 	target = xt_request_find_target(AF_INET, t->u.user.name,
@@ -49,8 +50,9 @@
 		return PTR_ERR(target);
 
 	t->u.kernel.target = target;
+	memset(&par, 0, sizeof(par));
 	par.table     = table;
-	par.entryinfo = NULL;
+	par.entryinfo = &e;
 	par.target    = target;
 	par.targinfo  = t->data;
 	par.hook_mask = hook;
diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c
index b12bc2a..e75fb65 100644
--- a/net/sched/cls_matchall.c
+++ b/net/sched/cls_matchall.c
@@ -32,6 +32,7 @@
 	if (tc_skip_sw(head->flags))
 		return -1;
 
+	*res = head->res;
 	return tcf_exts_exec(skb, &head->exts, res);
 }
 
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index daf6624..a74d32e 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -277,9 +277,6 @@
 void qdisc_hash_add(struct Qdisc *q)
 {
 	if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS)) {
-		struct Qdisc *root = qdisc_dev(q)->qdisc;
-
-		WARN_ON_ONCE(root == &noop_qdisc);
 		ASSERT_RTNL();
 		hash_add_rcu(qdisc_dev(q)->qdisc_hash, &q->hash, q->handle);
 	}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 6cfb6e9..b8031aa 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -178,8 +178,13 @@
 
 	if (likely(skb)) {
 		HARD_TX_LOCK(dev, txq, smp_processor_id());
-		if (!netif_xmit_frozen_or_stopped(txq))
-			skb = dev_hard_start_xmit(skb, dev, txq, &ret);
+		if (!netif_xmit_frozen_or_stopped(txq)) {
+			if (unlikely(skb->fast_forwarded))
+				skb = dev_hard_start_xmit_list(skb, dev,
+							       txq, &ret);
+			else
+				skb = dev_hard_start_xmit(skb, dev, txq, &ret);
+		}
 
 		HARD_TX_UNLOCK(dev, txq);
 	} else {
@@ -681,6 +686,7 @@
 		qdisc->gso_skb = NULL;
 	}
 	qdisc->q.qlen = 0;
+	qdisc->qstats.backlog = 0;
 }
 EXPORT_SYMBOL(qdisc_reset);
 
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index bc5e995..ea8a56f 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -434,6 +434,7 @@
 		qdisc_drop(head, sch, to_free);
 
 		slot_queue_add(slot, skb);
+		qdisc_tree_reduce_backlog(sch, 0, delta);
 		return NET_XMIT_CN;
 	}
 
@@ -465,8 +466,10 @@
 	/* Return Congestion Notification only if we dropped a packet
 	 * from this flow.
 	 */
-	if (qlen != slot->qlen)
+	if (qlen != slot->qlen) {
+		qdisc_tree_reduce_backlog(sch, 0, dropped - qdisc_pkt_len(skb));
 		return NET_XMIT_CN;
+	}
 
 	/* As we dropped a packet, better let upper stack know this */
 	qdisc_tree_reduce_backlog(sch, 1, dropped);
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 0c09060..ca4a63e 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -512,7 +512,9 @@
 {
 	addr->sa.sa_family = AF_INET6;
 	addr->v6.sin6_port = port;
+	addr->v6.sin6_flowinfo = 0;
 	addr->v6.sin6_addr = *saddr;
+	addr->v6.sin6_scope_id = 0;
 }
 
 /* Compare addresses exactly.
diff --git a/net/sctp/sctp_diag.c b/net/sctp/sctp_diag.c
index 048954e..e8f56b7 100644
--- a/net/sctp/sctp_diag.c
+++ b/net/sctp/sctp_diag.c
@@ -70,7 +70,8 @@
 
 	info = nla_data(attr);
 	list_for_each_entry_rcu(laddr, address_list, list) {
-		memcpy(info, &laddr->a, addrlen);
+		memcpy(info, &laddr->a, sizeof(laddr->a));
+		memset(info + sizeof(laddr->a), 0, addrlen - sizeof(laddr->a));
 		info += addrlen;
 	}
 
@@ -93,7 +94,9 @@
 	info = nla_data(attr);
 	list_for_each_entry(from, &asoc->peer.transport_addr_list,
 			    transports) {
-		memcpy(info, &from->ipaddr, addrlen);
+		memcpy(info, &from->ipaddr, sizeof(from->ipaddr));
+		memset(info + sizeof(from->ipaddr), 0,
+		       addrlen - sizeof(from->ipaddr));
 		info += addrlen;
 	}
 
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 9647e31..3ef7252 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4373,8 +4373,7 @@
 	info->sctpi_ictrlchunks = asoc->stats.ictrlchunks;
 
 	prim = asoc->peer.primary_path;
-	memcpy(&info->sctpi_p_address, &prim->ipaddr,
-	       sizeof(struct sockaddr_storage));
+	memcpy(&info->sctpi_p_address, &prim->ipaddr, sizeof(prim->ipaddr));
 	info->sctpi_p_state = prim->state;
 	info->sctpi_p_cwnd = prim->cwnd;
 	info->sctpi_p_srtt = prim->srtt;
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 84d0fda..d3cfbf2 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -265,7 +265,8 @@
 		sctp_ulpq_clear_pd(ulpq);
 
 	if (queue == &sk->sk_receive_queue && !sp->data_ready_signalled) {
-		sp->data_ready_signalled = 1;
+		if (!sock_owned_by_user(sk))
+			sp->data_ready_signalled = 1;
 		sk->sk_data_ready(sk);
 	}
 	return 1;
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 75f290b..272c345 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -702,6 +702,65 @@
 	return task;
 }
 
+/* create new threads */
+static int
+svc_start_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+	struct svc_rqst	*rqstp;
+	struct task_struct *task;
+	struct svc_pool *chosen_pool;
+	unsigned int state = serv->sv_nrthreads-1;
+	int node;
+
+	do {
+		nrservs--;
+		chosen_pool = choose_pool(serv, pool, &state);
+
+		node = svc_pool_map_get_node(chosen_pool->sp_id);
+		rqstp = svc_prepare_thread(serv, chosen_pool, node);
+		if (IS_ERR(rqstp))
+			return PTR_ERR(rqstp);
+
+		__module_get(serv->sv_ops->svo_module);
+		task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
+					      node, "%s", serv->sv_name);
+		if (IS_ERR(task)) {
+			module_put(serv->sv_ops->svo_module);
+			svc_exit_thread(rqstp);
+			return PTR_ERR(task);
+		}
+
+		rqstp->rq_task = task;
+		if (serv->sv_nrpools > 1)
+			svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
+
+		svc_sock_update_bufs(serv);
+		wake_up_process(task);
+	} while (nrservs > 0);
+
+	return 0;
+}
+
+
+/* destroy old threads */
+static int
+svc_signal_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+	struct task_struct *task;
+	unsigned int state = serv->sv_nrthreads-1;
+
+	/* destroy old threads */
+	do {
+		task = choose_victim(serv, pool, &state);
+		if (task == NULL)
+			break;
+		send_sig(SIGINT, task, 1);
+		nrservs++;
+	} while (nrservs < 0);
+
+	return 0;
+}
+
 /*
  * Create or destroy enough new threads to make the number
  * of threads the given number.  If `pool' is non-NULL, applies
@@ -719,13 +778,6 @@
 int
 svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
 {
-	struct svc_rqst	*rqstp;
-	struct task_struct *task;
-	struct svc_pool *chosen_pool;
-	int error = 0;
-	unsigned int state = serv->sv_nrthreads-1;
-	int node;
-
 	if (pool == NULL) {
 		/* The -1 assumes caller has done a svc_get() */
 		nrservs -= (serv->sv_nrthreads-1);
@@ -735,46 +787,52 @@
 		spin_unlock_bh(&pool->sp_lock);
 	}
 
-	/* create new threads */
-	while (nrservs > 0) {
-		nrservs--;
-		chosen_pool = choose_pool(serv, pool, &state);
-
-		node = svc_pool_map_get_node(chosen_pool->sp_id);
-		rqstp = svc_prepare_thread(serv, chosen_pool, node);
-		if (IS_ERR(rqstp)) {
-			error = PTR_ERR(rqstp);
-			break;
-		}
-
-		__module_get(serv->sv_ops->svo_module);
-		task = kthread_create_on_node(serv->sv_ops->svo_function, rqstp,
-					      node, "%s", serv->sv_name);
-		if (IS_ERR(task)) {
-			error = PTR_ERR(task);
-			module_put(serv->sv_ops->svo_module);
-			svc_exit_thread(rqstp);
-			break;
-		}
-
-		rqstp->rq_task = task;
-		if (serv->sv_nrpools > 1)
-			svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
-
-		svc_sock_update_bufs(serv);
-		wake_up_process(task);
-	}
-	/* destroy old threads */
-	while (nrservs < 0 &&
-	       (task = choose_victim(serv, pool, &state)) != NULL) {
-		send_sig(SIGINT, task, 1);
-		nrservs++;
-	}
-
-	return error;
+	if (nrservs > 0)
+		return svc_start_kthreads(serv, pool, nrservs);
+	if (nrservs < 0)
+		return svc_signal_kthreads(serv, pool, nrservs);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(svc_set_num_threads);
 
+/* destroy old threads */
+static int
+svc_stop_kthreads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+	struct task_struct *task;
+	unsigned int state = serv->sv_nrthreads-1;
+
+	/* destroy old threads */
+	do {
+		task = choose_victim(serv, pool, &state);
+		if (task == NULL)
+			break;
+		kthread_stop(task);
+		nrservs++;
+	} while (nrservs < 0);
+	return 0;
+}
+
+int
+svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
+{
+	if (pool == NULL) {
+		/* The -1 assumes caller has done a svc_get() */
+		nrservs -= (serv->sv_nrthreads-1);
+	} else {
+		spin_lock_bh(&pool->sp_lock);
+		nrservs -= pool->sp_nrthreads;
+		spin_unlock_bh(&pool->sp_lock);
+	}
+
+	if (nrservs > 0)
+		return svc_start_kthreads(serv, pool, nrservs);
+	if (nrservs < 0)
+		return svc_stop_kthreads(serv, pool, nrservs);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(svc_set_num_threads_sync);
+
 /*
  * Called from a server thread as it's exiting. Caller must hold the "service
  * mutex" for the service.
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index a4bc982..266a30c 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -408,6 +408,9 @@
 		dprintk("svc: socket %p(inet %p), busy=%d\n",
 			svsk, sk,
 			test_bit(XPT_BUSY, &svsk->sk_xprt.xpt_flags));
+
+		/* Refer to svc_setup_socket() for details. */
+		rmb();
 		svsk->sk_odata(sk);
 		if (!test_and_set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags))
 			svc_xprt_enqueue(&svsk->sk_xprt);
@@ -424,6 +427,9 @@
 	if (svsk) {
 		dprintk("svc: socket %p(inet %p), write_space busy=%d\n",
 			svsk, sk, test_bit(XPT_BUSY, &svsk->sk_xprt.xpt_flags));
+
+		/* Refer to svc_setup_socket() for details. */
+		rmb();
 		svsk->sk_owspace(sk);
 		svc_xprt_enqueue(&svsk->sk_xprt);
 	}
@@ -748,8 +754,12 @@
 	dprintk("svc: socket %p TCP (listen) state change %d\n",
 		sk, sk->sk_state);
 
-	if (svsk)
+	if (svsk) {
+		/* Refer to svc_setup_socket() for details. */
+		rmb();
 		svsk->sk_odata(sk);
+	}
+
 	/*
 	 * This callback may called twice when a new connection
 	 * is established as a child socket inherits everything
@@ -782,6 +792,8 @@
 	if (!svsk)
 		printk("svc: socket %p: no user data\n", sk);
 	else {
+		/* Refer to svc_setup_socket() for details. */
+		rmb();
 		svsk->sk_ostate(sk);
 		if (sk->sk_state != TCP_ESTABLISHED) {
 			set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags);
@@ -1368,12 +1380,18 @@
 		return ERR_PTR(err);
 	}
 
-	inet->sk_user_data = svsk;
 	svsk->sk_sock = sock;
 	svsk->sk_sk = inet;
 	svsk->sk_ostate = inet->sk_state_change;
 	svsk->sk_odata = inet->sk_data_ready;
 	svsk->sk_owspace = inet->sk_write_space;
+	/*
+	 * This barrier is necessary in order to prevent race condition
+	 * with svc_data_ready(), svc_listen_data_ready() and others
+	 * when calling callbacks above.
+	 */
+	wmb();
+	inet->sk_user_data = svsk;
 
 	/* Initialize the socket */
 	if (sock->type == SOCK_DGRAM)
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 56ea0ad..912f1fb 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -547,7 +547,7 @@
 		return false;
 	if (msg_errcode(msg))
 		return false;
-	*err = -TIPC_ERR_NO_NAME;
+	*err = TIPC_ERR_NO_NAME;
 	if (skb_linearize(skb))
 		return false;
 	msg = buf_msg(skb);
diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c
index 1fd4647..aedc476 100644
--- a/net/tipc/netlink_compat.c
+++ b/net/tipc/netlink_compat.c
@@ -258,13 +258,15 @@
 	arg = nlmsg_new(0, GFP_KERNEL);
 	if (!arg) {
 		kfree_skb(msg->rep);
+		msg->rep = NULL;
 		return -ENOMEM;
 	}
 
 	err = __tipc_nl_compat_dumpit(cmd, msg, arg);
-	if (err)
+	if (err) {
 		kfree_skb(msg->rep);
-
+		msg->rep = NULL;
+	}
 	kfree_skb(arg);
 
 	return err;
diff --git a/net/wireless/core.h b/net/wireless/core.h
index cf7063a..c40f3de 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -221,13 +221,7 @@
 
 	union {
 		struct cfg80211_connect_resp_params cr;
-		struct {
-			const u8 *req_ie;
-			const u8 *resp_ie;
-			size_t req_ie_len;
-			size_t resp_ie_len;
-			struct cfg80211_bss *bss;
-		} rm;
+		struct cfg80211_roam_info rm;
 		struct {
 			const u8 *ie;
 			size_t ie_len;
@@ -385,9 +379,7 @@
 			struct net_device *dev, u16 reason,
 			bool wextev);
 void __cfg80211_roamed(struct wireless_dev *wdev,
-		       struct cfg80211_bss *bss,
-		       const u8 *req_ie, size_t req_ie_len,
-		       const u8 *resp_ie, size_t resp_ie_len);
+		       struct cfg80211_roam_info *info);
 int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
 			      struct wireless_dev *wdev);
 
diff --git a/net/wireless/db.txt b/net/wireless/db.txt
index 413deff..ff9887f 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -223,17 +223,16 @@
 	(5490 - 5710 @ 160), (30), DFS
 
 country BZ:
-	(2402 - 2482 @ 40), (36)
-	(5170 - 5330 @ 160), (27)
-	(5490 - 5730 @ 160), (36)
-	(5735 - 5835 @ 80), (36)
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5330 @ 160), (23)
+	(5490 - 5730 @ 160), (30)
+	(5735 - 5835 @ 80), (30)
 
 country CA: DFS-FCC
 	(2402 - 2472 @ 40), (30)
 	(5170 - 5250 @ 80), (24), AUTO-BW
 	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
-	(5490 - 5590 @ 80), (24), DFS
-	(5650 - 5730 @ 80), (24), DFS
+	(5490 - 5730 @ 160), (24), DFS
 	(5735 - 5835 @ 80), (30)
 	# 60 gHz band channels 1-3
 	(57240 - 63720 @ 2160), (40)
@@ -682,7 +681,13 @@
 country IN:
 	(2402 - 2482 @ 40), (20)
 	(5170 - 5330 @ 160), (23)
-	(5735 - 5835 @ 80), (30)
+	(5735 - 5835 @ 80), (33)
+
+country IQ: DFS-ETSI
+	(2402 - 2482 @ 40), (20)
+	(5170 - 5250 @ 80), (23), AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5490 - 5710 @ 160), (30), DFS
 
 country IS: DFS-ETSI
 	(2402 - 2482 @ 40), (20)
@@ -732,11 +737,10 @@
 	(5170 - 5250 @ 80), (23)
 	(5735 - 5835 @ 80), (23)
 	# 60 gHz band channels 1-4, ref: Etsi En 302 567
-	((57000 - 66000 @ 2160), (40)
+	(57000 - 66000 @ 2160), (40)
 
 country JP: DFS-JP
 	(2402 - 2482 @ 40), (20)
-	(2474 - 2494 @ 20), (20), NO-OFDM
 	(5170 - 5250 @ 80), (20), AUTO-BW, NO-OUTDOOR
 	(5250 - 5330 @ 80), (20), DFS, AUTO-BW, NO-OUTDOOR
 	(5490 - 5710 @ 160), (20), DFS
@@ -758,7 +762,7 @@
 country KN: DFS-FCC
 	(2402 - 2482 @ 40), (20)
 	(5170 - 5250 @ 80), (23), AUTO-BW
-	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
+	(5250 - 5330 @ 80), (30), DFS, AUTO-BW
 	(5490 - 5710 @ 160), (30), DFS
 	(5735 - 5815 @ 80), (30)
 
@@ -1009,7 +1013,7 @@
 	(5170 - 5250 @ 80), (24), AUTO-BW
 	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
 	(5490 - 5650 @ 160), (24), DFS
-	(5735 - 5815 @ 80), (24)
+	(5735 - 5835 @ 80), (24)
 	# 60 gHz band channels 1-3
 	(57240 - 63720 @ 2160), (40)
 
@@ -1089,7 +1093,7 @@
 	(5490 - 5710 @ 160), (30), DFS
 
 country PA:
-	(2402 - 2472 @ 40), (30)
+	(2402 - 2472 @ 40), (36)
 	(5170 - 5250 @ 80), (23), AUT0-BW
 	(5250 - 5330 @ 80), (30), AUTO-BW
 	(5735 - 5835 @ 80), (36)
@@ -1374,9 +1378,9 @@
 
 country TT:
 	(2402 - 2482 @ 40), (20)
-	(5170 - 5330 @ 160), (27)
-	(5490 - 5730 @ 160), (36)
-	(5735 - 5835 @ 80), (36)
+	(5170 - 5330 @ 160), (24)
+	(5490 - 5730 @ 160), (24)
+	(5735 - 5835 @ 80), (30)
 	# 60 gHz band channels 1-3, FCC
 	(57240 - 63720 @ 2160), (40)
 
@@ -1450,7 +1454,7 @@
 country UZ: DFS-ETSI
 	(2402 - 2482 @ 40), (20)
 	(5170 - 5250 @ 80), (23), AUTO-BW
-	(5250 - 5330 @ 80), (20), DFS, AUTO-BW
+	(5250 - 5330 @ 80), (23), DFS, AUTO-BW
 
 country VC: DFS-ETSI
 	(2402 - 2482 @ 40), (20)
@@ -1473,9 +1477,9 @@
 
 country VN: DFS-FCC
 	(2402 - 2482 @ 40), (20)
-	(5170 - 5250 @ 80), (24), AUTO-BW
-	(5250 - 5330 @ 80), (24), DFS, AUTO-BW
-	(5490 - 5730 @ 160), (24), DFS
+	(5170 - 5250 @ 80), (24)
+	(5250 - 5330 @ 80), (24), DFS
+	(5490 - 5730 @ 80), (24), DFS
 	(5735 - 5835 @ 80), (30)
 	# 60 gHz band channels 1-4
 	(57240 - 65880 @ 2160), (40)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index ebd9a4b..d8387b1 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -560,6 +560,14 @@
 	[NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED },
 };
 
+/* policy for packet pattern attributes */
+static const struct nla_policy
+nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = {
+	[NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, },
+	[NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, },
+	[NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
+};
+
 static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
 				     struct netlink_callback *cb,
 				     struct cfg80211_registered_device **rdev,
@@ -9692,6 +9700,9 @@
 		if (err)
 			return err;
 
+		if (!setup.chandef.chan)
+			return -EINVAL;
+
 		err = validate_beacon_tx_rate(rdev, setup.chandef.chan->band,
 					      &setup.beacon_rate);
 		if (err)
@@ -10233,7 +10244,7 @@
 			u8 *mask_pat;
 
 			nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
-				  nla_len(pat), NULL);
+				  nla_len(pat), nl80211_packet_pattern_policy);
 			err = -EINVAL;
 			if (!pat_tb[NL80211_PKTPAT_MASK] ||
 			    !pat_tb[NL80211_PKTPAT_PATTERN])
@@ -10483,7 +10494,7 @@
 		u8 *mask_pat;
 
 		nla_parse(pat_tb, MAX_NL80211_PKTPAT, nla_data(pat),
-			  nla_len(pat), NULL);
+			  nla_len(pat), nl80211_packet_pattern_policy);
 		if (!pat_tb[NL80211_PKTPAT_MASK] ||
 		    !pat_tb[NL80211_PKTPAT_PATTERN])
 			return -EINVAL;
@@ -13415,14 +13426,14 @@
 }
 
 void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
-			 struct net_device *netdev, const u8 *bssid,
-			 const u8 *req_ie, size_t req_ie_len,
-			 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
+			 struct net_device *netdev,
+			 struct cfg80211_roam_info *info, gfp_t gfp)
 {
 	struct sk_buff *msg;
 	void *hdr;
+	const u8 *bssid = info->bss ? info->bss->bssid : info->bssid;
 
-	msg = nlmsg_new(100 + req_ie_len + resp_ie_len, gfp);
+	msg = nlmsg_new(100 + info->req_ie_len + info->resp_ie_len, gfp);
 	if (!msg)
 		return;
 
@@ -13435,10 +13446,14 @@
 	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
 	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
-	    (req_ie &&
-	     nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
-	    (resp_ie &&
-	     nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
+	    (info->req_ie &&
+	     nla_put(msg, NL80211_ATTR_REQ_IE, info->req_ie_len,
+		     info->req_ie)) ||
+	    (info->resp_ie &&
+	     nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len,
+		     info->resp_ie)) ||
+	    (info->authorized &&
+	     nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 2a84d18..c9d4805 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -59,9 +59,8 @@
 				 struct cfg80211_connect_resp_params *params,
 				 gfp_t gfp);
 void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
-			 struct net_device *netdev, const u8 *bssid,
-			 const u8 *req_ie, size_t req_ie_len,
-			 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp);
+			 struct net_device *netdev,
+			 struct cfg80211_roam_info *info, gfp_t gfp);
 void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
 			       struct net_device *netdev, u16 reason,
 			       const u8 *ie, size_t ie_len, bool from_ap);
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index d7e6abc..d414049 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -5,6 +5,7 @@
  *
  * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
  * Copyright (C) 2009   Intel Corporation. All rights reserved.
+ * Copyright 2017	Intel Deutschland GmbH
  */
 
 #include <linux/etherdevice.h>
@@ -544,11 +545,6 @@
 		return -EOPNOTSUPP;
 
 	if (wdev->current_bss) {
-		if (!prev_bssid)
-			return -EALREADY;
-		if (prev_bssid &&
-		    !ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
-			return -ENOTCONN;
 		cfg80211_unhold_bss(wdev->current_bss);
 		cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
 		wdev->current_bss = NULL;
@@ -893,9 +889,7 @@
 
 /* Consumes bss object one way or another */
 void __cfg80211_roamed(struct wireless_dev *wdev,
-		       struct cfg80211_bss *bss,
-		       const u8 *req_ie, size_t req_ie_len,
-		       const u8 *resp_ie, size_t resp_ie_len)
+		       struct cfg80211_roam_info *info)
 {
 #ifdef CONFIG_CFG80211_WEXT
 	union iwreq_data wrqu;
@@ -913,97 +907,85 @@
 	cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub);
 	wdev->current_bss = NULL;
 
-	cfg80211_hold_bss(bss_from_pub(bss));
-	wdev->current_bss = bss_from_pub(bss);
+	if (WARN_ON(!info->bss))
+		return;
+
+	cfg80211_hold_bss(bss_from_pub(info->bss));
+	wdev->current_bss = bss_from_pub(info->bss);
 
 	nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
-			    wdev->netdev, bss->bssid,
-			    req_ie, req_ie_len, resp_ie, resp_ie_len,
-			    GFP_KERNEL);
+			    wdev->netdev, info, GFP_KERNEL);
 
 #ifdef CONFIG_CFG80211_WEXT
-	if (req_ie) {
+	if (info->req_ie) {
 		memset(&wrqu, 0, sizeof(wrqu));
-		wrqu.data.length = req_ie_len;
+		wrqu.data.length = info->req_ie_len;
 		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
-				    &wrqu, req_ie);
+				    &wrqu, info->req_ie);
 	}
 
-	if (resp_ie) {
+	if (info->resp_ie) {
 		memset(&wrqu, 0, sizeof(wrqu));
-		wrqu.data.length = resp_ie_len;
+		wrqu.data.length = info->resp_ie_len;
 		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
-				    &wrqu, resp_ie);
+				    &wrqu, info->resp_ie);
 	}
 
 	memset(&wrqu, 0, sizeof(wrqu));
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
-	memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
-	memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN);
+	memcpy(wrqu.ap_addr.sa_data, info->bss->bssid, ETH_ALEN);
+	memcpy(wdev->wext.prev_bssid, info->bss->bssid, ETH_ALEN);
 	wdev->wext.prev_bssid_valid = true;
 	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
 #endif
 
 	return;
 out:
-	cfg80211_put_bss(wdev->wiphy, bss);
+	cfg80211_put_bss(wdev->wiphy, info->bss);
 }
 
-void cfg80211_roamed(struct net_device *dev,
-		     struct ieee80211_channel *channel,
-		     const u8 *bssid,
-		     const u8 *req_ie, size_t req_ie_len,
-		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
-{
-	struct wireless_dev *wdev = dev->ieee80211_ptr;
-	struct cfg80211_bss *bss;
-
-	bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid,
-			       wdev->ssid_len,
-			       wdev->conn_bss_type, IEEE80211_PRIVACY_ANY);
-	if (WARN_ON(!bss))
-		return;
-
-	cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie,
-			    resp_ie_len, gfp);
-}
-EXPORT_SYMBOL(cfg80211_roamed);
-
-/* Consumes bss object one way or another */
-void cfg80211_roamed_bss(struct net_device *dev,
-			 struct cfg80211_bss *bss, const u8 *req_ie,
-			 size_t req_ie_len, const u8 *resp_ie,
-			 size_t resp_ie_len, gfp_t gfp)
+/* Consumes info->bss object one way or another */
+void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
+		     gfp_t gfp)
 {
 	struct wireless_dev *wdev = dev->ieee80211_ptr;
 	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
 	struct cfg80211_event *ev;
 	unsigned long flags;
 
-	if (WARN_ON(!bss))
+	if (!info->bss) {
+		info->bss = cfg80211_get_bss(wdev->wiphy, info->channel,
+					     info->bssid, wdev->ssid,
+					     wdev->ssid_len,
+					     wdev->conn_bss_type,
+					     IEEE80211_PRIVACY_ANY);
+	}
+
+	if (WARN_ON(!info->bss))
 		return;
 
-	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
+	ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len, gfp);
 	if (!ev) {
-		cfg80211_put_bss(wdev->wiphy, bss);
+		cfg80211_put_bss(wdev->wiphy, info->bss);
 		return;
 	}
 
 	ev->type = EVENT_ROAMED;
 	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
-	ev->rm.req_ie_len = req_ie_len;
-	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
-	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
-	ev->rm.resp_ie_len = resp_ie_len;
-	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
-	ev->rm.bss = bss;
+	ev->rm.req_ie_len = info->req_ie_len;
+	memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
+	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + info->req_ie_len;
+	ev->rm.resp_ie_len = info->resp_ie_len;
+	memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len);
+	ev->rm.bss = info->bss;
+	ev->rm.authorized = info->authorized;
 
 	spin_lock_irqsave(&wdev->event_lock, flags);
 	list_add_tail(&ev->list, &wdev->event_list);
 	spin_unlock_irqrestore(&wdev->event_lock, flags);
 	queue_work(cfg80211_wq, &rdev->event_work);
 }
-EXPORT_SYMBOL(cfg80211_roamed_bss);
+EXPORT_SYMBOL(cfg80211_roamed);
 
 void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
 			     size_t ie_len, u16 reason, bool from_ap)
@@ -1098,11 +1080,35 @@
 
 	ASSERT_WDEV_LOCK(wdev);
 
-	if (WARN_ON(wdev->connect_keys)) {
-		kzfree(wdev->connect_keys);
-		wdev->connect_keys = NULL;
+	/*
+	 * If we have an ssid_len, we're trying to connect or are
+	 * already connected, so reject a new SSID unless it's the
+	 * same (which is the case for re-association.)
+	 */
+	if (wdev->ssid_len &&
+	    (wdev->ssid_len != connect->ssid_len ||
+	     memcmp(wdev->ssid, connect->ssid, wdev->ssid_len)))
+		return -EALREADY;
+
+	/*
+	 * If connected, reject (re-)association unless prev_bssid
+	 * matches the current BSSID.
+	 */
+	if (wdev->current_bss) {
+		if (!prev_bssid)
+			return -EALREADY;
+		if (!ether_addr_equal(prev_bssid, wdev->current_bss->pub.bssid))
+			return -ENOTCONN;
 	}
 
+	/*
+	 * Reject if we're in the process of connecting with WEP,
+	 * this case isn't very interesting and trying to handle
+	 * it would make the code much more complex.
+	 */
+	if (wdev->connect_keys)
+		return -EINPROGRESS;
+
 	cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
 				  rdev->wiphy.ht_capa_mod_mask);
 
@@ -1153,7 +1159,12 @@
 
 	if (err) {
 		wdev->connect_keys = NULL;
-		wdev->ssid_len = 0;
+		/*
+		 * This could be reassoc getting refused, don't clear
+		 * ssid_len in that case.
+		 */
+		if (!wdev->current_bss)
+			wdev->ssid_len = 0;
 		return err;
 	}
 
@@ -1178,5 +1189,13 @@
 	else if (wdev->current_bss)
 		err = rdev_disconnect(rdev, dev, reason);
 
+	/*
+	 * Clear ssid_len unless we actually were fully connected,
+	 * in which case cfg80211_disconnected() will take care of
+	 * this later.
+	 */
+	if (!wdev->current_bss)
+		wdev->ssid_len = 0;
+
 	return err;
 }
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 8ac413f..a4ab20a 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -953,9 +953,7 @@
 				ev->cr.status == WLAN_STATUS_SUCCESS);
 			break;
 		case EVENT_ROAMED:
-			__cfg80211_roamed(wdev, ev->rm.bss, ev->rm.req_ie,
-					  ev->rm.req_ie_len, ev->rm.resp_ie,
-					  ev->rm.resp_ie_len);
+			__cfg80211_roamed(wdev, &ev->rm);
 			break;
 		case EVENT_DISCONNECTED:
 			__cfg80211_disconnected(wdev->netdev,
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index 637387b..d864a6d 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -66,6 +66,9 @@
 			goto error_nolock;
 		}
 
+		if (x->props.output_mark)
+			skb->mark = x->props.output_mark;
+
 		err = x->outer_mode->output(x, skb);
 		if (err) {
 			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index e26b515..77fbfbd 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -125,7 +125,7 @@
 						  int tos, int oif,
 						  const xfrm_address_t *saddr,
 						  const xfrm_address_t *daddr,
-						  int family)
+						  int family, u32 mark)
 {
 	struct xfrm_policy_afinfo *afinfo;
 	struct dst_entry *dst;
@@ -134,7 +134,7 @@
 	if (unlikely(afinfo == NULL))
 		return ERR_PTR(-EAFNOSUPPORT);
 
-	dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr);
+	dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr, mark);
 
 	xfrm_policy_put_afinfo(afinfo);
 
@@ -145,7 +145,7 @@
 						int tos, int oif,
 						xfrm_address_t *prev_saddr,
 						xfrm_address_t *prev_daddr,
-						int family)
+						int family, u32 mark)
 {
 	struct net *net = xs_net(x);
 	xfrm_address_t *saddr = &x->props.saddr;
@@ -161,7 +161,7 @@
 		daddr = x->coaddr;
 	}
 
-	dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family);
+	dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family, mark);
 
 	if (!IS_ERR(dst)) {
 		if (prev_saddr != saddr)
@@ -1427,14 +1427,14 @@
 
 static int
 xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local,
-	       xfrm_address_t *remote, unsigned short family)
+	       xfrm_address_t *remote, unsigned short family, u32 mark)
 {
 	int err;
 	struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
 
 	if (unlikely(afinfo == NULL))
 		return -EINVAL;
-	err = afinfo->get_saddr(net, oif, local, remote);
+	err = afinfo->get_saddr(net, oif, local, remote, mark);
 	xfrm_policy_put_afinfo(afinfo);
 	return err;
 }
@@ -1465,7 +1465,7 @@
 			if (xfrm_addr_any(local, tmpl->encap_family)) {
 				error = xfrm_get_saddr(net, fl->flowi_oif,
 						       &tmp, remote,
-						       tmpl->encap_family);
+						       tmpl->encap_family, 0);
 				if (error)
 					goto fail;
 				local = &tmp;
@@ -1744,7 +1744,8 @@
 		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
 			family = xfrm[i]->props.family;
 			dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
-					      &saddr, &daddr, family);
+					      &saddr, &daddr, family,
+					      xfrm[i]->props.output_mark);
 			err = PTR_ERR(dst);
 			if (IS_ERR(dst))
 				goto put_states;
@@ -3308,9 +3309,15 @@
 	struct xfrm_state *x_new[XFRM_MAX_DEPTH];
 	struct xfrm_migrate *mp;
 
+	/* Stage 0 - sanity checks */
 	if ((err = xfrm_migrate_check(m, num_migrate)) < 0)
 		goto out;
 
+	if (dir >= XFRM_POLICY_MAX) {
+		err = -EINVAL;
+		goto out;
+	}
+
 	/* Stage 1 - find policy */
 	if ((pol = xfrm_migrate_policy_find(sel, dir, type, net)) == NULL) {
 		err = -ENOENT;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index a7e27e1..2cade02 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -584,6 +584,9 @@
 
 	xfrm_mark_get(attrs, &x->mark);
 
+	if (attrs[XFRMA_OUTPUT_MARK])
+		x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]);
+
 	err = __xfrm_init_state(x, false);
 	if (err)
 		goto error;
@@ -871,6 +874,11 @@
 		goto out;
 	if (x->security)
 		ret = copy_sec_ctx(x->security, skb);
+	if (x->props.output_mark) {
+		ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark);
+		if (ret)
+			goto out;
+	}
 out:
 	return ret;
 }
@@ -1656,32 +1664,34 @@
 
 static int xfrm_dump_policy_done(struct netlink_callback *cb)
 {
-	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
 	struct net *net = sock_net(cb->skb->sk);
 
 	xfrm_policy_walk_done(walk, net);
 	return 0;
 }
 
+static int xfrm_dump_policy_start(struct netlink_callback *cb)
+{
+	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
+
+	BUILD_BUG_ON(sizeof(*walk) > sizeof(cb->args));
+
+	xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
+	return 0;
+}
+
 static int xfrm_dump_policy(struct sk_buff *skb, struct netlink_callback *cb)
 {
 	struct net *net = sock_net(skb->sk);
-	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *) &cb->args[1];
+	struct xfrm_policy_walk *walk = (struct xfrm_policy_walk *)cb->args;
 	struct xfrm_dump_info info;
 
-	BUILD_BUG_ON(sizeof(struct xfrm_policy_walk) >
-		     sizeof(cb->args) - sizeof(cb->args[0]));
-
 	info.in_skb = cb->skb;
 	info.out_skb = skb;
 	info.nlmsg_seq = cb->nlh->nlmsg_seq;
 	info.nlmsg_flags = NLM_F_MULTI;
 
-	if (!cb->args[0]) {
-		cb->args[0] = 1;
-		xfrm_policy_walk_init(walk, XFRM_POLICY_TYPE_ANY);
-	}
-
 	(void) xfrm_policy_walk(net, walk, dump_one_policy, &info);
 
 	return skb->len;
@@ -1695,6 +1705,10 @@
 	struct sk_buff *skb;
 	int err;
 
+	err = verify_policy_dir(dir);
+	if (err)
+		return ERR_PTR(err);
+
 	skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 	if (!skb)
 		return ERR_PTR(-ENOMEM);
@@ -2216,6 +2230,10 @@
 	int n = 0;
 	struct net *net = sock_net(skb->sk);
 
+	err = verify_policy_dir(pi->dir);
+	if (err)
+		return err;
+
 	if (attrs[XFRMA_MIGRATE] == NULL)
 		return -EINVAL;
 
@@ -2331,6 +2349,11 @@
 {
 	struct net *net = &init_net;
 	struct sk_buff *skb;
+	int err;
+
+	err = verify_policy_dir(dir);
+	if (err)
+		return err;
 
 	skb = nlmsg_new(xfrm_migrate_msgsize(num_migrate, !!k), GFP_ATOMIC);
 	if (skb == NULL)
@@ -2406,6 +2429,7 @@
 	[XFRMA_SA_EXTRA_FLAGS]	= { .type = NLA_U32 },
 	[XFRMA_PROTO]		= { .type = NLA_U8 },
 	[XFRMA_ADDRESS_FILTER]	= { .len = sizeof(struct xfrm_address_filter) },
+	[XFRMA_OUTPUT_MARK]	= { .len = NLA_U32 },
 };
 
 static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = {
@@ -2415,6 +2439,7 @@
 
 static const struct xfrm_link {
 	int (*doit)(struct sk_buff *, struct nlmsghdr *, struct nlattr **);
+	int (*start)(struct netlink_callback *);
 	int (*dump)(struct sk_buff *, struct netlink_callback *);
 	int (*done)(struct netlink_callback *);
 	const struct nla_policy *nla_pol;
@@ -2428,6 +2453,7 @@
 	[XFRM_MSG_NEWPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_add_policy    },
 	[XFRM_MSG_DELPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy    },
 	[XFRM_MSG_GETPOLICY   - XFRM_MSG_BASE] = { .doit = xfrm_get_policy,
+						   .start = xfrm_dump_policy_start,
 						   .dump = xfrm_dump_policy,
 						   .done = xfrm_dump_policy_done },
 	[XFRM_MSG_ALLOCSPI    - XFRM_MSG_BASE] = { .doit = xfrm_alloc_userspi },
@@ -2479,6 +2505,7 @@
 
 		{
 			struct netlink_dump_control c = {
+				.start = link->start,
 				.dump = link->dump,
 				.done = link->done,
 			};
@@ -2622,6 +2649,8 @@
 		l += nla_total_size(sizeof(*x->coaddr));
 	if (x->props.extra_flags)
 		l += nla_total_size(sizeof(x->props.extra_flags));
+	if (x->props.output_mark)
+		l += nla_total_size(sizeof(x->props.output_mark));
 
 	/* Must count x->lastused as it may become non-zero behind our back. */
 	l += nla_total_size_64bit(sizeof(u64));
@@ -2985,6 +3014,11 @@
 
 static int xfrm_send_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
 {
+	int err;
+
+	err = verify_policy_dir(dir);
+	if (err)
+		return err;
 
 	switch (c->event) {
 	case XFRM_MSG_NEWPOLICY:
diff --git a/samples/trace_events/trace-events-sample.c b/samples/trace_events/trace-events-sample.c
index 880a7d1..4ccff66 100644
--- a/samples/trace_events/trace-events-sample.c
+++ b/samples/trace_events/trace-events-sample.c
@@ -78,28 +78,36 @@
 }
 
 static DEFINE_MUTEX(thread_mutex);
+static int simple_thread_cnt;
 
 void foo_bar_reg(void)
 {
+	mutex_lock(&thread_mutex);
+	if (simple_thread_cnt++)
+		goto out;
+
 	pr_info("Starting thread for foo_bar_fn\n");
 	/*
 	 * We shouldn't be able to start a trace when the module is
 	 * unloading (there's other locks to prevent that). But
 	 * for consistency sake, we still take the thread_mutex.
 	 */
-	mutex_lock(&thread_mutex);
 	simple_tsk_fn = kthread_run(simple_thread_fn, NULL, "event-sample-fn");
+ out:
 	mutex_unlock(&thread_mutex);
 }
 
 void foo_bar_unreg(void)
 {
-	pr_info("Killing thread for foo_bar_fn\n");
-	/* protect against module unloading */
 	mutex_lock(&thread_mutex);
+	if (--simple_thread_cnt)
+		goto out;
+
+	pr_info("Killing thread for foo_bar_fn\n");
 	if (simple_tsk_fn)
 		kthread_stop(simple_tsk_fn);
 	simple_tsk_fn = NULL;
+ out:
 	mutex_unlock(&thread_mutex);
 }
 
diff --git a/scripts/Makefile.dtbo b/scripts/Makefile.dtbo
index db4a0f4..b298f4a 100644
--- a/scripts/Makefile.dtbo
+++ b/scripts/Makefile.dtbo
@@ -10,7 +10,7 @@
 ifneq ($(DTC_OVERLAY_TEST_EXT),)
 DTC_OVERLAY_TEST = $(DTC_OVERLAY_TEST_EXT)
 quiet_cmd_dtbo_verify	= VERIFY  $@
-cmd_dtbo_verify = $(DTC_OVERLAY_TEST) $(addprefix $(obj)/,$($(@F)-base)) $@ $(dot-target).dtb
+cmd_dtbo_verify = $(DTC_OVERLAY_TEST) $(addprefix $(obj)/,$($(@F)-base)) $@ $(dot-target).tmp
 else
 cmd_dtbo_verify = true
 endif
diff --git a/security/Kconfig b/security/Kconfig
index 5693989..4415de2 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -6,6 +6,11 @@
 
 source security/keys/Kconfig
 
+if ARCH_QCOM
+source security/pfe/Kconfig
+endif
+
+
 config SECURITY_DMESG_RESTRICT
 	bool "Restrict unprivileged access to the kernel syslog"
 	default n
diff --git a/security/Makefile b/security/Makefile
index f2d71cd..79166ba 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -9,6 +9,7 @@
 subdir-$(CONFIG_SECURITY_APPARMOR)	+= apparmor
 subdir-$(CONFIG_SECURITY_YAMA)		+= yama
 subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
+subdir-$(CONFIG_ARCH_QCOM)	+= pfe
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -24,6 +25,7 @@
 obj-$(CONFIG_SECURITY_APPARMOR)		+= apparmor/
 obj-$(CONFIG_SECURITY_YAMA)		+= yama/
 obj-$(CONFIG_SECURITY_LOADPIN)		+= loadpin/
+obj-$(CONFIG_ARCH_QCOM)				+= pfe/
 obj-$(CONFIG_CGROUP_DEVICE)		+= device_cgroup.o
 
 # Object integrity file lists
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 57bc405..935752c 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -671,9 +671,9 @@
 module_param_call(mode, param_set_mode, param_get_mode,
 		  &aa_g_profile_mode, S_IRUSR | S_IWUSR);
 
-#ifdef CONFIG_SECURITY_APPARMOR_HASH
 /* whether policy verification hashing is enabled */
 bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT);
+#ifdef CONFIG_SECURITY_APPARMOR_HASH
 module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR);
 #endif
 
diff --git a/security/commoncap.c b/security/commoncap.c
index 3e44d01..b5aca42 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -58,7 +58,7 @@
 }
 
 /**
- * cap_capable - Determine whether a task has a particular effective capability
+ * __cap_capable - Determine whether a task has a particular effective capability
  * @cred: The credentials to use
  * @ns:  The user namespace in which we need the capability
  * @cap: The capability to check for
@@ -72,18 +72,11 @@
  * cap_has_capability() returns 0 when a task has a capability, but the
  * kernel's capable() and has_capability() returns 1 for this case.
  */
-int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
+int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
 		int cap, int audit)
 {
 	struct user_namespace *ns = targ_ns;
 
-#ifdef CONFIG_ANDROID_PARANOID_NETWORK
-	if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW))
-		return 0;
-	if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN))
-		return 0;
-#endif
-
 	/* See if cred has the capability in the target user namespace
 	 * by examining the target user namespace and all of the target
 	 * user namespace's parents.
@@ -114,6 +107,27 @@
 	/* We never get here */
 }
 
+int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
+		int cap, int audit)
+{
+	int ret = __cap_capable(cred, targ_ns, cap, audit);
+
+#ifdef CONFIG_ANDROID_PARANOID_NETWORK
+	if (ret != 0 && cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) {
+		printk("Process %s granted CAP_NET_RAW from Android group net_raw.\n", current->comm);
+		printk("  Please update the .rc file to explictly set 'capabilities NET_RAW'\n");
+		printk("  Implicit grants are deprecated and will be removed in the future.\n");
+		return 0;
+	}
+	if (ret != 0 && cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) {
+		printk("Process %s granted CAP_NET_ADMIN from Android group net_admin.\n", current->comm);
+		printk("  Please update the .rc file to explictly set 'capabilities NET_ADMIN'\n");
+		printk("  Implicit grants are deprecated and will be removed in the future.\n");
+		return 0;
+	}
+#endif
+	return ret;
+}
 /**
  * cap_settime - Determine whether the current process may set the system clock
  * @ts: The time to set
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index d942c7c..e0a3978 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -41,10 +41,8 @@
 	bool "Large payload keys"
 	depends on KEYS
 	depends on TMPFS
-	depends on (CRYPTO_ANSI_CPRNG = y || CRYPTO_DRBG = y)
 	select CRYPTO_AES
-	select CRYPTO_ECB
-	select CRYPTO_RNG
+	select CRYPTO_GCM
 	help
 	  This option provides support for holding large keys within the kernel
 	  (for example Kerberos ticket caches).  The data may be stored out to
diff --git a/security/keys/big_key.c b/security/keys/big_key.c
index 835c1ab..e628817 100644
--- a/security/keys/big_key.c
+++ b/security/keys/big_key.c
@@ -1,5 +1,6 @@
 /* Large capacity key type
  *
+ * Copyright (C) 2017 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
  * Copyright (C) 2013 Red Hat, Inc. All Rights Reserved.
  * Written by David Howells (dhowells@redhat.com)
  *
@@ -16,10 +17,10 @@
 #include <linux/shmem_fs.h>
 #include <linux/err.h>
 #include <linux/scatterlist.h>
+#include <linux/random.h>
 #include <keys/user-type.h>
 #include <keys/big_key-type.h>
-#include <crypto/rng.h>
-#include <crypto/skcipher.h>
+#include <crypto/aead.h>
 
 /*
  * Layout of key payload words.
@@ -49,7 +50,12 @@
 /*
  * Key size for big_key data encryption
  */
-#define ENC_KEY_SIZE	16
+#define ENC_KEY_SIZE 32
+
+/*
+ * Authentication tag length
+ */
+#define ENC_AUTHTAG_SIZE 16
 
 /*
  * big_key defined keys take an arbitrary string as the description and an
@@ -64,57 +70,62 @@
 	.destroy		= big_key_destroy,
 	.describe		= big_key_describe,
 	.read			= big_key_read,
+	/* no ->update(); don't add it without changing big_key_crypt() nonce */
 };
 
 /*
- * Crypto names for big_key data encryption
+ * Crypto names for big_key data authenticated encryption
  */
-static const char big_key_rng_name[] = "stdrng";
-static const char big_key_alg_name[] = "ecb(aes)";
+static const char big_key_alg_name[] = "gcm(aes)";
 
 /*
- * Crypto algorithms for big_key data encryption
+ * Crypto algorithms for big_key data authenticated encryption
  */
-static struct crypto_rng *big_key_rng;
-static struct crypto_skcipher *big_key_skcipher;
+static struct crypto_aead *big_key_aead;
 
 /*
- * Generate random key to encrypt big_key data
+ * Since changing the key affects the entire object, we need a mutex.
  */
-static inline int big_key_gen_enckey(u8 *key)
-{
-	return crypto_rng_get_bytes(big_key_rng, key, ENC_KEY_SIZE);
-}
+static DEFINE_MUTEX(big_key_aead_lock);
 
 /*
  * Encrypt/decrypt big_key data
  */
 static int big_key_crypt(enum big_key_op op, u8 *data, size_t datalen, u8 *key)
 {
-	int ret = -EINVAL;
+	int ret;
 	struct scatterlist sgio;
-	SKCIPHER_REQUEST_ON_STACK(req, big_key_skcipher);
+	struct aead_request *aead_req;
+	/* We always use a zero nonce. The reason we can get away with this is
+	 * because we're using a different randomly generated key for every
+	 * different encryption. Notably, too, key_type_big_key doesn't define
+	 * an .update function, so there's no chance we'll wind up reusing the
+	 * key to encrypt updated data. Simply put: one key, one encryption.
+	 */
+	u8 zero_nonce[crypto_aead_ivsize(big_key_aead)];
 
-	if (crypto_skcipher_setkey(big_key_skcipher, key, ENC_KEY_SIZE)) {
+	aead_req = aead_request_alloc(big_key_aead, GFP_KERNEL);
+	if (!aead_req)
+		return -ENOMEM;
+
+	memset(zero_nonce, 0, sizeof(zero_nonce));
+	sg_init_one(&sgio, data, datalen + (op == BIG_KEY_ENC ? ENC_AUTHTAG_SIZE : 0));
+	aead_request_set_crypt(aead_req, &sgio, &sgio, datalen, zero_nonce);
+	aead_request_set_callback(aead_req, CRYPTO_TFM_REQ_MAY_SLEEP, NULL, NULL);
+	aead_request_set_ad(aead_req, 0);
+
+	mutex_lock(&big_key_aead_lock);
+	if (crypto_aead_setkey(big_key_aead, key, ENC_KEY_SIZE)) {
 		ret = -EAGAIN;
 		goto error;
 	}
-
-	skcipher_request_set_tfm(req, big_key_skcipher);
-	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_SLEEP,
-				      NULL, NULL);
-
-	sg_init_one(&sgio, data, datalen);
-	skcipher_request_set_crypt(req, &sgio, &sgio, datalen, NULL);
-
 	if (op == BIG_KEY_ENC)
-		ret = crypto_skcipher_encrypt(req);
+		ret = crypto_aead_encrypt(aead_req);
 	else
-		ret = crypto_skcipher_decrypt(req);
-
-	skcipher_request_zero(req);
-
+		ret = crypto_aead_decrypt(aead_req);
 error:
+	mutex_unlock(&big_key_aead_lock);
+	aead_request_free(aead_req);
 	return ret;
 }
 
@@ -146,15 +157,13 @@
 		 *
 		 * File content is stored encrypted with randomly generated key.
 		 */
-		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
+		size_t enclen = datalen + ENC_AUTHTAG_SIZE;
 
-		/* prepare aligned data to encrypt */
 		data = kmalloc(enclen, GFP_KERNEL);
 		if (!data)
 			return -ENOMEM;
 
 		memcpy(data, prep->data, datalen);
-		memset(data + datalen, 0x00, enclen - datalen);
 
 		/* generate random key */
 		enckey = kmalloc(ENC_KEY_SIZE, GFP_KERNEL);
@@ -162,13 +171,10 @@
 			ret = -ENOMEM;
 			goto error;
 		}
-
-		ret = big_key_gen_enckey(enckey);
-		if (ret)
-			goto err_enckey;
+		get_random_bytes(enckey, ENC_KEY_SIZE);
 
 		/* encrypt aligned data */
-		ret = big_key_crypt(BIG_KEY_ENC, data, enclen, enckey);
+		ret = big_key_crypt(BIG_KEY_ENC, data, datalen, enckey);
 		if (ret)
 			goto err_enckey;
 
@@ -194,7 +200,7 @@
 		*path = file->f_path;
 		path_get(path);
 		fput(file);
-		kfree(data);
+		kzfree(data);
 	} else {
 		/* Just store the data in a buffer */
 		void *data = kmalloc(datalen, GFP_KERNEL);
@@ -210,9 +216,9 @@
 err_fput:
 	fput(file);
 err_enckey:
-	kfree(enckey);
+	kzfree(enckey);
 error:
-	kfree(data);
+	kzfree(data);
 	return ret;
 }
 
@@ -226,7 +232,7 @@
 
 		path_put(path);
 	}
-	kfree(prep->payload.data[big_key_data]);
+	kzfree(prep->payload.data[big_key_data]);
 }
 
 /*
@@ -239,7 +245,7 @@
 
 	/* clear the quota */
 	key_payload_reserve(key, 0);
-	if (key_is_instantiated(key) &&
+	if (key_is_positive(key) &&
 	    (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
 		vfs_truncate(path, 0);
 }
@@ -258,7 +264,7 @@
 		path->mnt = NULL;
 		path->dentry = NULL;
 	}
-	kfree(key->payload.data[big_key_data]);
+	kzfree(key->payload.data[big_key_data]);
 	key->payload.data[big_key_data] = NULL;
 }
 
@@ -271,7 +277,7 @@
 
 	seq_puts(m, key->description);
 
-	if (key_is_instantiated(key))
+	if (key_is_positive(key))
 		seq_printf(m, ": %zu [%s]",
 			   datalen,
 			   datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");
@@ -294,7 +300,7 @@
 		struct file *file;
 		u8 *data;
 		u8 *enckey = (u8 *)key->payload.data[big_key_data];
-		size_t enclen = ALIGN(datalen, crypto_skcipher_blocksize(big_key_skcipher));
+		size_t enclen = datalen + ENC_AUTHTAG_SIZE;
 
 		data = kmalloc(enclen, GFP_KERNEL);
 		if (!data)
@@ -326,7 +332,7 @@
 err_fput:
 		fput(file);
 error:
-		kfree(data);
+		kzfree(data);
 	} else {
 		ret = datalen;
 		if (copy_to_user(buffer, key->payload.data[big_key_data],
@@ -342,47 +348,31 @@
  */
 static int __init big_key_init(void)
 {
-	struct crypto_skcipher *cipher;
-	struct crypto_rng *rng;
 	int ret;
 
-	rng = crypto_alloc_rng(big_key_rng_name, 0, 0);
-	if (IS_ERR(rng)) {
-		pr_err("Can't alloc rng: %ld\n", PTR_ERR(rng));
-		return PTR_ERR(rng);
-	}
-
-	big_key_rng = rng;
-
-	/* seed RNG */
-	ret = crypto_rng_reset(rng, NULL, crypto_rng_seedsize(rng));
-	if (ret) {
-		pr_err("Can't reset rng: %d\n", ret);
-		goto error_rng;
-	}
-
 	/* init block cipher */
-	cipher = crypto_alloc_skcipher(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
-	if (IS_ERR(cipher)) {
-		ret = PTR_ERR(cipher);
+	big_key_aead = crypto_alloc_aead(big_key_alg_name, 0, CRYPTO_ALG_ASYNC);
+	if (IS_ERR(big_key_aead)) {
+		ret = PTR_ERR(big_key_aead);
 		pr_err("Can't alloc crypto: %d\n", ret);
-		goto error_rng;
+		return ret;
 	}
-
-	big_key_skcipher = cipher;
+	ret = crypto_aead_setauthsize(big_key_aead, ENC_AUTHTAG_SIZE);
+	if (ret < 0) {
+		pr_err("Can't set crypto auth tag len: %d\n", ret);
+		goto free_aead;
+	}
 
 	ret = register_key_type(&key_type_big_key);
 	if (ret < 0) {
 		pr_err("Can't register type: %d\n", ret);
-		goto error_cipher;
+		goto free_aead;
 	}
 
 	return 0;
 
-error_cipher:
-	crypto_free_skcipher(big_key_skcipher);
-error_rng:
-	crypto_free_rng(big_key_rng);
+free_aead:
+	crypto_free_aead(big_key_aead);
 	return ret;
 }
 
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 8d9330a..a871159 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -315,6 +315,13 @@
 
 	down_read(&ukey->sem);
 	upayload = user_key_payload(ukey);
+	if (!upayload) {
+		/* key was revoked before we acquired its semaphore */
+		up_read(&ukey->sem);
+		key_put(ukey);
+		ukey = ERR_PTR(-EKEYREVOKED);
+		goto error;
+	}
 	*master_key = upayload->data;
 	*master_keylen = upayload->datalen;
 error:
@@ -867,7 +874,7 @@
 	size_t datalen = prep->datalen;
 	int ret = 0;
 
-	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+	if (key_is_negative(key))
 		return -ENOKEY;
 	if (datalen <= 0 || datalen > 32767 || !prep->data)
 		return -EINVAL;
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 9cb4fe4..1659094 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -129,15 +129,15 @@
 	while (!list_empty(keys)) {
 		struct key *key =
 			list_entry(keys->next, struct key, graveyard_link);
+		short state = key->state;
+
 		list_del(&key->graveyard_link);
 
 		kdebug("- %u", key->serial);
 		key_check(key);
 
 		/* Throw away the key data if the key is instantiated */
-		if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
-		    !test_bit(KEY_FLAG_NEGATIVE, &key->flags) &&
-		    key->type->destroy)
+		if (state == KEY_IS_POSITIVE && key->type->destroy)
 			key->type->destroy(key);
 
 		security_key_free(key);
@@ -151,7 +151,7 @@
 		}
 
 		atomic_dec(&key->user->nkeys);
-		if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+		if (state != KEY_IS_UNINSTANTIATED)
 			atomic_dec(&key->user->nikeys);
 
 		key_user_put(key->user);
diff --git a/security/keys/internal.h b/security/keys/internal.h
index a705a7d..fb0c650 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -137,7 +137,7 @@
 extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
 extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
 
-extern struct key *find_keyring_by_name(const char *name, bool skip_perm_check);
+extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);
 
 extern int install_user_keyrings(void);
 extern int install_thread_keyring_to_cred(struct cred *);
diff --git a/security/keys/key.c b/security/keys/key.c
index 2f4ce35..7dc5906 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -301,6 +301,8 @@
 		key->flags |= 1 << KEY_FLAG_IN_QUOTA;
 	if (flags & KEY_ALLOC_BUILT_IN)
 		key->flags |= 1 << KEY_FLAG_BUILTIN;
+	if (flags & KEY_ALLOC_UID_KEYRING)
+		key->flags |= 1 << KEY_FLAG_UID_KEYRING;
 
 #ifdef KEY_DEBUGGING
 	key->magic = KEY_DEBUG_MAGIC;
@@ -399,6 +401,18 @@
 EXPORT_SYMBOL(key_payload_reserve);
 
 /*
+ * Change the key state to being instantiated.
+ */
+static void mark_key_instantiated(struct key *key, int reject_error)
+{
+	/* Commit the payload before setting the state; barrier versus
+	 * key_read_state().
+	 */
+	smp_store_release(&key->state,
+			  (reject_error < 0) ? reject_error : KEY_IS_POSITIVE);
+}
+
+/*
  * Instantiate a key and link it into the target keyring atomically.  Must be
  * called with the target keyring's semaphore writelocked.  The target key's
  * semaphore need not be locked as instantiation is serialised by
@@ -421,14 +435,14 @@
 	mutex_lock(&key_construction_mutex);
 
 	/* can't instantiate twice */
-	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+	if (key->state == KEY_IS_UNINSTANTIATED) {
 		/* instantiate the key */
 		ret = key->type->instantiate(key, prep);
 
 		if (ret == 0) {
 			/* mark the key as being instantiated */
 			atomic_inc(&key->user->nikeys);
-			set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+			mark_key_instantiated(key, 0);
 
 			if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
 				awaken = 1;
@@ -570,13 +584,10 @@
 	mutex_lock(&key_construction_mutex);
 
 	/* can't instantiate twice */
-	if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+	if (key->state == KEY_IS_UNINSTANTIATED) {
 		/* mark the key as being negatively instantiated */
 		atomic_inc(&key->user->nikeys);
-		key->reject_error = -error;
-		smp_wmb();
-		set_bit(KEY_FLAG_NEGATIVE, &key->flags);
-		set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
+		mark_key_instantiated(key, -error);
 		now = current_kernel_time();
 		key->expiry = now.tv_sec + timeout;
 		key_schedule_gc(key->expiry + key_gc_delay);
@@ -748,8 +759,8 @@
 
 	ret = key->type->update(key, prep);
 	if (ret == 0)
-		/* updating a negative key instantiates it */
-		clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+		/* Updating a negative key positively instantiates it */
+		mark_key_instantiated(key, 0);
 
 	up_write(&key->sem);
 
@@ -933,6 +944,16 @@
 	 */
 	__key_link_end(keyring, &index_key, edit);
 
+	key = key_ref_to_ptr(key_ref);
+	if (test_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) {
+		ret = wait_for_key_construction(key, true);
+		if (ret < 0) {
+			key_ref_put(key_ref);
+			key_ref = ERR_PTR(ret);
+			goto error_free_prep;
+		}
+	}
+
 	key_ref = __key_update(key_ref, &prep);
 	goto error_free_prep;
 }
@@ -983,8 +1004,8 @@
 
 	ret = key->type->update(key, &prep);
 	if (ret == 0)
-		/* updating a negative key instantiates it */
-		clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
+		/* Updating a negative key positively instantiates it */
+		mark_key_instantiated(key, 0);
 
 	up_write(&key->sem);
 
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ada12c3..797edcf 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -766,6 +766,10 @@
 
 	key = key_ref_to_ptr(key_ref);
 
+	ret = key_read_state(key);
+	if (ret < 0)
+		goto error2; /* Negatively instantiated */
+
 	/* see if we can read it directly */
 	ret = key_permission(key_ref, KEY_NEED_READ);
 	if (ret == 0)
@@ -896,7 +900,7 @@
 		atomic_dec(&key->user->nkeys);
 		atomic_inc(&newowner->nkeys);
 
-		if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
+		if (key->state != KEY_IS_UNINSTANTIATED) {
 			atomic_dec(&key->user->nikeys);
 			atomic_inc(&newowner->nikeys);
 		}
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index c91e4e0..4e9b4d2 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -407,7 +407,7 @@
 	else
 		seq_puts(m, "[anon]");
 
-	if (key_is_instantiated(keyring)) {
+	if (key_is_positive(keyring)) {
 		if (keyring->keys.nr_leaves_on_tree != 0)
 			seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
 		else
@@ -416,7 +416,7 @@
 }
 
 struct keyring_read_iterator_context {
-	size_t			qty;
+	size_t			buflen;
 	size_t			count;
 	key_serial_t __user	*buffer;
 };
@@ -428,9 +428,9 @@
 	int ret;
 
 	kenter("{%s,%d},,{%zu/%zu}",
-	       key->type->name, key->serial, ctx->count, ctx->qty);
+	       key->type->name, key->serial, ctx->count, ctx->buflen);
 
-	if (ctx->count >= ctx->qty)
+	if (ctx->count >= ctx->buflen)
 		return 1;
 
 	ret = put_user(key->serial, ctx->buffer);
@@ -452,38 +452,33 @@
 			 char __user *buffer, size_t buflen)
 {
 	struct keyring_read_iterator_context ctx;
-	unsigned long nr_keys;
-	int ret;
+	long ret;
 
 	kenter("{%d},,%zu", key_serial(keyring), buflen);
 
 	if (buflen & (sizeof(key_serial_t) - 1))
 		return -EINVAL;
 
-	nr_keys = keyring->keys.nr_leaves_on_tree;
-	if (nr_keys == 0)
-		return 0;
-
-	/* Calculate how much data we could return */
-	ctx.qty = nr_keys * sizeof(key_serial_t);
-
-	if (!buffer || !buflen)
-		return ctx.qty;
-
-	if (buflen > ctx.qty)
-		ctx.qty = buflen;
-
-	/* Copy the IDs of the subscribed keys into the buffer */
-	ctx.buffer = (key_serial_t __user *)buffer;
-	ctx.count = 0;
-	ret = assoc_array_iterate(&keyring->keys, keyring_read_iterator, &ctx);
-	if (ret < 0) {
-		kleave(" = %d [iterate]", ret);
-		return ret;
+	/* Copy as many key IDs as fit into the buffer */
+	if (buffer && buflen) {
+		ctx.buffer = (key_serial_t __user *)buffer;
+		ctx.buflen = buflen;
+		ctx.count = 0;
+		ret = assoc_array_iterate(&keyring->keys,
+					  keyring_read_iterator, &ctx);
+		if (ret < 0) {
+			kleave(" = %ld [iterate]", ret);
+			return ret;
+		}
 	}
 
-	kleave(" = %zu [ok]", ctx.count);
-	return ctx.count;
+	/* Return the size of the buffer needed */
+	ret = keyring->keys.nr_leaves_on_tree * sizeof(key_serial_t);
+	if (ret <= buflen)
+		kleave("= %ld [ok]", ret);
+	else
+		kleave("= %ld [buffer too small]", ret);
+	return ret;
 }
 
 /*
@@ -550,7 +545,8 @@
 {
 	struct keyring_search_context *ctx = iterator_data;
 	const struct key *key = keyring_ptr_to_key(object);
-	unsigned long kflags = key->flags;
+	unsigned long kflags = READ_ONCE(key->flags);
+	short state = READ_ONCE(key->state);
 
 	kenter("{%d}", key->serial);
 
@@ -594,9 +590,8 @@
 
 	if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
 		/* we set a different error code if we pass a negative key */
-		if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
-			smp_rmb();
-			ctx->result = ERR_PTR(key->reject_error);
+		if (state < 0) {
+			ctx->result = ERR_PTR(state);
 			kleave(" = %d [neg]", ctx->skipped_ret);
 			goto skipped;
 		}
@@ -989,15 +984,15 @@
 /*
  * Find a keyring with the specified name.
  *
- * All named keyrings in the current user namespace are searched, provided they
- * grant Search permission directly to the caller (unless this check is
- * skipped).  Keyrings whose usage points have reached zero or who have been
- * revoked are skipped.
+ * Only keyrings that have nonzero refcount, are not revoked, and are owned by a
+ * user in the current user namespace are considered.  If @uid_keyring is %true,
+ * the keyring additionally must have been allocated as a user or user session
+ * keyring; otherwise, it must grant Search permission directly to the caller.
  *
  * Returns a pointer to the keyring with the keyring's refcount having being
  * incremented on success.  -ENOKEY is returned if a key could not be found.
  */
-struct key *find_keyring_by_name(const char *name, bool skip_perm_check)
+struct key *find_keyring_by_name(const char *name, bool uid_keyring)
 {
 	struct key *keyring;
 	int bucket;
@@ -1025,10 +1020,15 @@
 			if (strcmp(keyring->description, name) != 0)
 				continue;
 
-			if (!skip_perm_check &&
-			    key_permission(make_key_ref(keyring, 0),
-					   KEY_NEED_SEARCH) < 0)
-				continue;
+			if (uid_keyring) {
+				if (!test_bit(KEY_FLAG_UID_KEYRING,
+					      &keyring->flags))
+					continue;
+			} else {
+				if (key_permission(make_key_ref(keyring, 0),
+						   KEY_NEED_SEARCH) < 0)
+					continue;
+			}
 
 			/* we've got a match but we might end up racing with
 			 * key_cleanup() if the keyring is currently 'dead'
diff --git a/security/keys/proc.c b/security/keys/proc.c
index b9f531c..0361286 100644
--- a/security/keys/proc.c
+++ b/security/keys/proc.c
@@ -182,6 +182,7 @@
 	unsigned long timo;
 	key_ref_t key_ref, skey_ref;
 	char xbuf[16];
+	short state;
 	int rc;
 
 	struct keyring_search_context ctx = {
@@ -240,17 +241,19 @@
 			sprintf(xbuf, "%luw", timo / (60*60*24*7));
 	}
 
+	state = key_read_state(key);
+
 #define showflag(KEY, LETTER, FLAG) \
 	(test_bit(FLAG,	&(KEY)->flags) ? LETTER : '-')
 
 	seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
 		   key->serial,
-		   showflag(key, 'I', KEY_FLAG_INSTANTIATED),
+		   state != KEY_IS_UNINSTANTIATED ? 'I' : '-',
 		   showflag(key, 'R', KEY_FLAG_REVOKED),
 		   showflag(key, 'D', KEY_FLAG_DEAD),
 		   showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
 		   showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
-		   showflag(key, 'N', KEY_FLAG_NEGATIVE),
+		   state < 0 ? 'N' : '-',
 		   showflag(key, 'i', KEY_FLAG_INVALIDATED),
 		   atomic_read(&key->usage),
 		   xbuf,
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index 45536c6..2d35d71 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -76,7 +76,8 @@
 		if (IS_ERR(uid_keyring)) {
 			uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
 						    cred, user_keyring_perm,
-						    KEY_ALLOC_IN_QUOTA,
+						    KEY_ALLOC_UID_KEYRING |
+							KEY_ALLOC_IN_QUOTA,
 						    NULL, NULL);
 			if (IS_ERR(uid_keyring)) {
 				ret = PTR_ERR(uid_keyring);
@@ -93,7 +94,8 @@
 			session_keyring =
 				keyring_alloc(buf, user->uid, INVALID_GID,
 					      cred, user_keyring_perm,
-					      KEY_ALLOC_IN_QUOTA,
+					      KEY_ALLOC_UID_KEYRING |
+						  KEY_ALLOC_IN_QUOTA,
 					      NULL, NULL);
 			if (IS_ERR(session_keyring)) {
 				ret = PTR_ERR(session_keyring);
@@ -727,7 +729,7 @@
 
 	ret = -EIO;
 	if (!(lflags & KEY_LOOKUP_PARTIAL) &&
-	    !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
+	    key_read_state(key) == KEY_IS_UNINSTANTIATED)
 		goto invalid_key;
 
 	/* check the permissions */
diff --git a/security/keys/request_key.c b/security/keys/request_key.c
index 43affcf..5030fcf 100644
--- a/security/keys/request_key.c
+++ b/security/keys/request_key.c
@@ -594,10 +594,9 @@
 			  intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
 	if (ret)
 		return -ERESTARTSYS;
-	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
-		smp_rmb();
-		return key->reject_error;
-	}
+	ret = key_read_state(key);
+	if (ret < 0)
+		return ret;
 	return key_validate(key);
 }
 EXPORT_SYMBOL(wait_for_key_construction);
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 9db8b4a..ba74a0b 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -73,7 +73,7 @@
 
 	seq_puts(m, "key:");
 	seq_puts(m, key->description);
-	if (key_is_instantiated(key))
+	if (key_is_positive(key))
 		seq_printf(m, " pid:%d ci:%zu", rka->pid, rka->callout_len);
 }
 
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 90d6175..4ba2f6b 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -70,7 +70,7 @@
 	}
 
 	ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
-	kfree(sdesc);
+	kzfree(sdesc);
 	return ret;
 }
 
@@ -114,7 +114,7 @@
 	if (!ret)
 		ret = crypto_shash_final(&sdesc->shash, digest);
 out:
-	kfree(sdesc);
+	kzfree(sdesc);
 	return ret;
 }
 
@@ -165,7 +165,7 @@
 				  paramdigest, TPM_NONCE_SIZE, h1,
 				  TPM_NONCE_SIZE, h2, 1, &c, 0, 0);
 out:
-	kfree(sdesc);
+	kzfree(sdesc);
 	return ret;
 }
 
@@ -246,7 +246,7 @@
 	if (memcmp(testhmac, authdata, SHA1_DIGEST_SIZE))
 		ret = -EINVAL;
 out:
-	kfree(sdesc);
+	kzfree(sdesc);
 	return ret;
 }
 
@@ -347,7 +347,7 @@
 	if (memcmp(testhmac2, authdata2, SHA1_DIGEST_SIZE))
 		ret = -EINVAL;
 out:
-	kfree(sdesc);
+	kzfree(sdesc);
 	return ret;
 }
 
@@ -564,7 +564,7 @@
 		*bloblen = storedsize;
 	}
 out:
-	kfree(td);
+	kzfree(td);
 	return ret;
 }
 
@@ -678,7 +678,7 @@
 	if (ret < 0)
 		pr_info("trusted_key: srkseal failed (%d)\n", ret);
 
-	kfree(tb);
+	kzfree(tb);
 	return ret;
 }
 
@@ -703,7 +703,7 @@
 		/* pull migratable flag out of sealed key */
 		p->migratable = p->key[--p->key_len];
 
-	kfree(tb);
+	kzfree(tb);
 	return ret;
 }
 
@@ -1037,12 +1037,12 @@
 	if (!ret && options->pcrlock)
 		ret = pcrlock(options->pcrlock);
 out:
-	kfree(datablob);
-	kfree(options);
+	kzfree(datablob);
+	kzfree(options);
 	if (!ret)
 		rcu_assign_keypointer(key, payload);
 	else
-		kfree(payload);
+		kzfree(payload);
 	return ret;
 }
 
@@ -1051,8 +1051,7 @@
 	struct trusted_key_payload *p;
 
 	p = container_of(rcu, struct trusted_key_payload, rcu);
-	memset(p->key, 0, p->key_len);
-	kfree(p);
+	kzfree(p);
 }
 
 /*
@@ -1067,7 +1066,7 @@
 	char *datablob;
 	int ret = 0;
 
-	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+	if (key_is_negative(key))
 		return -ENOKEY;
 	p = key->payload.data[0];
 	if (!p->migratable)
@@ -1094,13 +1093,13 @@
 	ret = datablob_parse(datablob, new_p, new_o);
 	if (ret != Opt_update) {
 		ret = -EINVAL;
-		kfree(new_p);
+		kzfree(new_p);
 		goto out;
 	}
 
 	if (!new_o->keyhandle) {
 		ret = -EINVAL;
-		kfree(new_p);
+		kzfree(new_p);
 		goto out;
 	}
 
@@ -1114,22 +1113,22 @@
 	ret = key_seal(new_p, new_o);
 	if (ret < 0) {
 		pr_info("trusted_key: key_seal failed (%d)\n", ret);
-		kfree(new_p);
+		kzfree(new_p);
 		goto out;
 	}
 	if (new_o->pcrlock) {
 		ret = pcrlock(new_o->pcrlock);
 		if (ret < 0) {
 			pr_info("trusted_key: pcrlock failed (%d)\n", ret);
-			kfree(new_p);
+			kzfree(new_p);
 			goto out;
 		}
 	}
 	rcu_assign_keypointer(key, new_p);
 	call_rcu(&p->rcu, trusted_rcu_free);
 out:
-	kfree(datablob);
-	kfree(new_o);
+	kzfree(datablob);
+	kzfree(new_o);
 	return ret;
 }
 
@@ -1148,34 +1147,30 @@
 	p = rcu_dereference_key(key);
 	if (!p)
 		return -EINVAL;
-	if (!buffer || buflen <= 0)
-		return 2 * p->blob_len;
-	ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL);
-	if (!ascii_buf)
-		return -ENOMEM;
 
-	bufp = ascii_buf;
-	for (i = 0; i < p->blob_len; i++)
-		bufp = hex_byte_pack(bufp, p->blob[i]);
-	if ((copy_to_user(buffer, ascii_buf, 2 * p->blob_len)) != 0) {
-		kfree(ascii_buf);
-		return -EFAULT;
+	if (buffer && buflen >= 2 * p->blob_len) {
+		ascii_buf = kmalloc(2 * p->blob_len, GFP_KERNEL);
+		if (!ascii_buf)
+			return -ENOMEM;
+
+		bufp = ascii_buf;
+		for (i = 0; i < p->blob_len; i++)
+			bufp = hex_byte_pack(bufp, p->blob[i]);
+		if (copy_to_user(buffer, ascii_buf, 2 * p->blob_len) != 0) {
+			kzfree(ascii_buf);
+			return -EFAULT;
+		}
+		kzfree(ascii_buf);
 	}
-	kfree(ascii_buf);
 	return 2 * p->blob_len;
 }
 
 /*
- * trusted_destroy - before freeing the key, clear the decrypted data
+ * trusted_destroy - clear and free the key's payload
  */
 static void trusted_destroy(struct key *key)
 {
-	struct trusted_key_payload *p = key->payload.data[0];
-
-	if (!p)
-		return;
-	memset(p->key, 0, p->key_len);
-	kfree(key->payload.data[0]);
+	kzfree(key->payload.data[0]);
 }
 
 struct key_type key_type_trusted = {
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index 66b1840..3dc2607 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -106,7 +106,7 @@
 
 	/* attach the new data, displacing the old */
 	key->expiry = prep->expiry;
-	if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
+	if (key_is_positive(key))
 		zap = rcu_dereference_key(key);
 	rcu_assign_keypointer(key, prep->payload.data[0]);
 	prep->payload.data[0] = NULL;
@@ -154,7 +154,7 @@
 void user_describe(const struct key *key, struct seq_file *m)
 {
 	seq_puts(m, key->description);
-	if (key_is_instantiated(key))
+	if (key_is_positive(key))
 		seq_printf(m, ": %u", key->datalen);
 }
 
diff --git a/security/pfe/Kconfig b/security/pfe/Kconfig
new file mode 100644
index 0000000..0cd9e81
--- /dev/null
+++ b/security/pfe/Kconfig
@@ -0,0 +1,28 @@
+menu "Qualcomm Technologies, Inc Per File Encryption security device drivers"
+	depends on ARCH_QCOM
+
+config PFT
+	bool "Per-File-Tagger driver"
+	depends on SECURITY
+	default n
+	help
+		This driver is used for tagging enterprise files.
+		It is part of the Per-File-Encryption (PFE) feature.
+		The driver is tagging files when created by
+		registered application.
+		Tagged files are encrypted using the dm-req-crypt driver.
+
+config PFK
+	bool "Per-File-Key driver"
+	depends on SECURITY
+	depends on SECURITY_SELINUX
+	default n
+	help
+		This driver is used for storing eCryptfs information
+		in file node.
+		This is part of eCryptfs hardware enhanced solution
+		provided by Qualcomm Technologies, Inc.
+		Information is used when file is encrypted later using
+		ICE or dm crypto engine
+
+endmenu
diff --git a/security/pfe/Makefile b/security/pfe/Makefile
new file mode 100644
index 0000000..242a216
--- /dev/null
+++ b/security/pfe/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the MSM specific security device drivers.
+#
+
+ccflags-y += -Isecurity/selinux -Isecurity/selinux/include
+ccflags-y += -Ifs/ext4
+ccflags-y += -Ifs/crypto
+
+obj-$(CONFIG_PFT) += pft.o
+obj-$(CONFIG_PFK) += pfk.o pfk_kc.o pfk_ice.o pfk_ext4.o
diff --git a/security/pfe/pfk.c b/security/pfe/pfk.c
new file mode 100644
index 0000000..615353e
--- /dev/null
+++ b/security/pfe/pfk.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+/*
+ * Per-File-Key (PFK).
+ *
+ * This driver is responsible for overall management of various
+ * Per File Encryption variants that work on top of or as part of different
+ * file systems.
+ *
+ * The driver has the following purpose :
+ * 1) Define priorities between PFE's if more than one is enabled
+ * 2) Extract key information from inode
+ * 3) Load and manage various keys in ICE HW engine
+ * 4) It should be invoked from various layers in FS/BLOCK/STORAGE DRIVER
+ *    that need to take decision on HW encryption management of the data
+ *    Some examples:
+ *	BLOCK LAYER: when it takes decision on whether 2 chunks can be united
+ *	to one encryption / decryption request sent to the HW
+ *
+ *	UFS DRIVER: when it need to configure ICE HW with a particular key slot
+ *	to be used for encryption / decryption
+ *
+ * PFE variants can differ on particular way of storing the cryptographic info
+ * inside inode, actions to be taken upon file operations, etc., but the common
+ * properties are described above
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt)	"pfk [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/bio.h>
+#include <linux/security.h>
+#include <crypto/ice.h>
+
+#include <linux/pfk.h>
+
+#include "pfk_kc.h"
+#include "objsec.h"
+#include "pfk_ice.h"
+#include "pfk_ext4.h"
+#include "pfk_internal.h"
+#include "ext4.h"
+
+static bool pfk_ready;
+
+
+/* might be replaced by a table when more than one cipher is supported */
+#define PFK_SUPPORTED_KEY_SIZE 32
+#define PFK_SUPPORTED_SALT_SIZE 32
+
+/* Various PFE types and function tables to support each one of them */
+enum pfe_type {EXT4_CRYPT_PFE, INVALID_PFE};
+
+typedef int (*pfk_parse_inode_type)(const struct bio *bio,
+	const struct inode *inode,
+	struct pfk_key_info *key_info,
+	enum ice_cryto_algo_mode *algo,
+	bool *is_pfe);
+
+typedef bool (*pfk_allow_merge_bio_type)(const struct bio *bio1,
+	const struct bio *bio2, const struct inode *inode1,
+	const struct inode *inode2);
+
+static const pfk_parse_inode_type pfk_parse_inode_ftable[] = {
+	/* EXT4_CRYPT_PFE */ &pfk_ext4_parse_inode,
+};
+
+static const pfk_allow_merge_bio_type pfk_allow_merge_bio_ftable[] = {
+	/* EXT4_CRYPT_PFE */ &pfk_ext4_allow_merge_bio,
+};
+
+static void __exit pfk_exit(void)
+{
+	pfk_ready = false;
+	pfk_ext4_deinit();
+	pfk_kc_deinit();
+}
+
+static int __init pfk_init(void)
+{
+
+	int ret = 0;
+
+	ret = pfk_ext4_init();
+	if (ret != 0)
+		goto fail;
+
+	ret = pfk_kc_init();
+	if (ret != 0) {
+		pr_err("could init pfk key cache, error %d\n", ret);
+		pfk_ext4_deinit();
+		goto fail;
+	}
+
+	pfk_ready = true;
+	pr_info("Driver initialized successfully\n");
+
+	return 0;
+
+fail:
+	pr_err("Failed to init driver\n");
+	return -ENODEV;
+}
+
+/*
+ * If more than one type is supported simultaneously, this function will also
+ * set the priority between them
+ */
+static enum pfe_type pfk_get_pfe_type(const struct inode *inode)
+{
+	if (!inode)
+		return INVALID_PFE;
+
+	if (pfk_is_ext4_type(inode))
+		return EXT4_CRYPT_PFE;
+
+	return INVALID_PFE;
+}
+
+/**
+ * inode_to_filename() - get the filename from inode pointer.
+ * @inode: inode pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+char *inode_to_filename(const struct inode *inode)
+{
+	struct dentry *dentry = NULL;
+	char *filename = NULL;
+
+	if (hlist_empty(&inode->i_dentry))
+		return "unknown";
+
+	dentry = hlist_entry(inode->i_dentry.first, struct dentry, d_u.d_alias);
+	filename = dentry->d_iname;
+
+	return filename;
+}
+
+/**
+ * pfk_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_is_ready(void)
+{
+	return pfk_ready;
+}
+
+/**
+ * pfk_bio_get_inode() - get the inode from a bio.
+ * @bio: Pointer to BIO structure.
+ *
+ * Walk the bio struct links to get the inode.
+ * Please note, that in general bio may consist of several pages from
+ * several files, but in our case we always assume that all pages come
+ * from the same file, since our logic ensures it. That is why we only
+ * walk through the first page to look for inode.
+ *
+ * Return: pointer to the inode struct if successful, or NULL otherwise.
+ *
+ */
+static struct inode *pfk_bio_get_inode(const struct bio *bio)
+{
+	struct address_space *mapping;
+
+	if (!bio)
+		return NULL;
+	if (!bio->bi_io_vec)
+		return NULL;
+	if (!bio->bi_io_vec->bv_page)
+		return NULL;
+	if (!bio_has_data((struct bio *)bio))
+		return NULL;
+
+	if (PageAnon(bio->bi_io_vec->bv_page)) {
+		struct inode *inode;
+
+		//Using direct-io (O_DIRECT) without page cache
+		inode = dio_bio_get_inode((struct bio *)bio);
+		pr_debug("inode on direct-io, inode = 0x%pK.\n", inode);
+
+		return inode;
+	}
+
+	mapping = page_mapping(bio->bi_io_vec->bv_page);
+	if (!mapping)
+		return NULL;
+
+	if (!mapping->host)
+		return NULL;
+
+	return bio->bi_io_vec->bv_page->mapping->host;
+}
+
+/**
+ * pfk_key_size_to_key_type() - translate key size to key size enum
+ * @key_size: key size in bytes
+ * @key_size_type: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported key size)
+ */
+int pfk_key_size_to_key_type(size_t key_size,
+	enum ice_crpto_key_size *key_size_type)
+{
+	/*
+	 *  currently only 32 bit key size is supported
+	 *  in the future, table with supported key sizes might
+	 *  be introduced
+	 */
+
+	if (key_size != PFK_SUPPORTED_KEY_SIZE) {
+		pr_err("not supported key size %zu\n", key_size);
+		return -EINVAL;
+	}
+
+	if (key_size_type)
+		*key_size_type = ICE_CRYPTO_KEY_SIZE_256;
+
+	return 0;
+}
+
+/*
+ * Retrieves filesystem type from inode's superblock
+ */
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+	const char *fs_type)
+{
+	if (!inode || !fs_type)
+		return false;
+
+	if (!inode->i_sb)
+		return false;
+
+	if (!inode->i_sb->s_type)
+		return false;
+
+	return (strcmp(inode->i_sb->s_type->name, fs_type) == 0);
+}
+
+
+/**
+ * pfk_load_key_start() - loads PFE encryption key to the ICE
+ *			  Can also be invoked from non
+ *			  PFE context, in this case it
+ *			  is not relevant and is_pfe
+ *			  flag is set to false
+ *
+ * @bio: Pointer to the BIO structure
+ * @ice_setting: Pointer to ice setting structure that will be filled with
+ * ice configuration values, including the index to which the key was loaded
+ *  @is_pfe: will be false if inode is not relevant to PFE, in such a case
+ * it should be treated as non PFE by the block layer
+ *
+ * Returns the index where the key is stored in encryption hw and additional
+ * information that will be used later for configuration of the encryption hw.
+ *
+ * Must be followed by pfk_load_key_end when key is no longer used by ice
+ *
+ */
+int pfk_load_key_start(const struct bio *bio,
+		struct ice_crypto_setting *ice_setting, bool *is_pfe,
+		bool async)
+{
+	int ret = 0;
+	struct pfk_key_info key_info = {NULL, NULL, 0, 0};
+	enum ice_cryto_algo_mode algo_mode = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+	enum ice_crpto_key_size key_size_type = 0;
+	u32 key_index = 0;
+	struct inode *inode = NULL;
+	enum pfe_type which_pfe = INVALID_PFE;
+
+	if (!is_pfe) {
+		pr_err("is_pfe is NULL\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * only a few errors below can indicate that
+	 * this function was not invoked within PFE context,
+	 * otherwise we will consider it PFE
+	 */
+	*is_pfe = true;
+
+	if (!pfk_is_ready())
+		return -ENODEV;
+
+	if (!ice_setting) {
+		pr_err("ice setting is NULL\n");
+		return -EINVAL;
+	}
+//pr_err("%s %d\n", __func__, __LINE__);
+	inode = pfk_bio_get_inode(bio);
+	if (!inode) {
+		*is_pfe = false;
+		return -EINVAL;
+	}
+    //pr_err("%s %d\n", __func__, __LINE__);
+	which_pfe = pfk_get_pfe_type(inode);
+	if (which_pfe == INVALID_PFE) {
+		*is_pfe = false;
+		return -EPERM;
+	}
+
+	pr_debug("parsing file %s with PFE %d\n",
+		inode_to_filename(inode), which_pfe);
+//pr_err("%s %d\n", __func__, __LINE__);
+	ret = (*(pfk_parse_inode_ftable[which_pfe]))
+			(bio, inode, &key_info, &algo_mode, is_pfe);
+	if (ret != 0)
+		return ret;
+//pr_err("%s %d\n", __func__, __LINE__);
+	ret = pfk_key_size_to_key_type(key_info.key_size, &key_size_type);
+	if (ret != 0)
+		return ret;
+//pr_err("%s %d\n", __func__, __LINE__);
+	ret = pfk_kc_load_key_start(key_info.key, key_info.key_size,
+			key_info.salt, key_info.salt_size, &key_index, async);
+	if (ret) {
+		if (ret != -EBUSY && ret != -EAGAIN)
+			pr_err("start: could not load key into pfk key cache, error %d\n",
+					ret);
+
+		return ret;
+	}
+
+	ice_setting->key_size = key_size_type;
+	ice_setting->algo_mode = algo_mode;
+	/* hardcoded for now */
+	ice_setting->key_mode = ICE_CRYPTO_USE_LUT_SW_KEY;
+	ice_setting->key_index = key_index;
+
+	pr_debug("loaded key for file %s key_index %d\n",
+		inode_to_filename(inode), key_index);
+
+	return 0;
+}
+
+/**
+ * pfk_load_key_end() - marks the PFE key as no longer used by ICE
+ *			Can also be invoked from non
+ *			PFE context, in this case it is not
+ *			relevant and is_pfe flag is
+ *			set to false
+ *
+ * @bio: Pointer to the BIO structure
+ * @is_pfe: Pointer to is_pfe flag, which will be true if function was invoked
+ *			from PFE context
+ */
+int pfk_load_key_end(const struct bio *bio, bool *is_pfe)
+{
+	int ret = 0;
+	struct pfk_key_info key_info = {0};
+	enum pfe_type which_pfe = INVALID_PFE;
+	struct inode *inode = NULL;
+
+	if (!is_pfe) {
+		pr_err("is_pfe is NULL\n");
+		return -EINVAL;
+	}
+
+	/* only a few errors below can indicate that
+	 * this function was not invoked within PFE context,
+	 * otherwise we will consider it PFE
+	 */
+	*is_pfe = true;
+
+	if (!pfk_is_ready())
+		return -ENODEV;
+
+	inode = pfk_bio_get_inode(bio);
+	if (!inode) {
+		*is_pfe = false;
+		return -EINVAL;
+	}
+
+	which_pfe = pfk_get_pfe_type(inode);
+	if (which_pfe == INVALID_PFE) {
+		*is_pfe = false;
+		return -EPERM;
+	}
+
+	ret = (*(pfk_parse_inode_ftable[which_pfe]))
+			(bio, inode, &key_info, NULL, is_pfe);
+	if (ret != 0)
+		return ret;
+
+	pfk_kc_load_key_end(key_info.key, key_info.key_size,
+		key_info.salt, key_info.salt_size);
+
+	pr_debug("finished using key for file %s\n",
+		inode_to_filename(inode));
+
+	return 0;
+}
+
+/**
+ * pfk_allow_merge_bio() - Check if 2 BIOs can be merged.
+ * @bio1:	Pointer to first BIO structure.
+ * @bio2:	Pointer to second BIO structure.
+ *
+ * Prevent merging of BIOs from encrypted and non-encrypted
+ * files, or files encrypted with different key.
+ * Also prevent non encrypted and encrypted data from the same file
+ * to be merged (ecryptfs header if stored inside file should be non
+ * encrypted)
+ * This API is called by the file system block layer.
+ *
+ * Return: true if the BIOs allowed to be merged, false
+ * otherwise.
+ */
+bool pfk_allow_merge_bio(const struct bio *bio1, const struct bio *bio2)
+{
+	struct inode *inode1 = NULL;
+	struct inode *inode2 = NULL;
+	enum pfe_type which_pfe1 = INVALID_PFE;
+	enum pfe_type which_pfe2 = INVALID_PFE;
+
+	if (!pfk_is_ready())
+		return false;
+
+	if (!bio1 || !bio2)
+		return false;
+
+	if (bio1 == bio2)
+		return true;
+
+	inode1 = pfk_bio_get_inode(bio1);
+	inode2 = pfk_bio_get_inode(bio2);
+
+
+	which_pfe1 = pfk_get_pfe_type(inode1);
+	which_pfe2 = pfk_get_pfe_type(inode2);
+
+	/* nodes with different encryption, do not merge */
+	if (which_pfe1 != which_pfe2)
+		return false;
+
+	/* both nodes do not have encryption, allow merge */
+	if (which_pfe1 == INVALID_PFE)
+		return true;
+
+	return (*(pfk_allow_merge_bio_ftable[which_pfe1]))(bio1, bio2,
+		inode1, inode2);
+}
+/**
+ * Flush key table on storage core reset. During core reset key configuration
+ * is lost in ICE. We need to flash the cache, so that the keys will be
+ * reconfigured again for every subsequent transaction
+ */
+void pfk_clear_on_reset(void)
+{
+	if (!pfk_is_ready())
+		return;
+
+	pfk_kc_clear_on_reset();
+}
+
+module_init(pfk_init);
+module_exit(pfk_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Key driver");
diff --git a/security/pfe/pfk_ext4.c b/security/pfe/pfk_ext4.c
new file mode 100644
index 0000000..7ce70bc
--- /dev/null
+++ b/security/pfe/pfk_ext4.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+/*
+ * Per-File-Key (PFK) - EXT4
+ *
+ * This driver is used for working with EXT4 crypt extension
+ *
+ * The key information  is stored in node by EXT4 when file is first opened
+ * and will be later accessed by Block Device Driver to actually load the key
+ * to encryption hw.
+ *
+ * PFK exposes API's for loading and removing keys from encryption hw
+ * and also API to determine whether 2 adjacent blocks can be agregated by
+ * Block Layer in one request to encryption hw.
+ *
+ */
+
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+#define pr_fmt(fmt)	"pfk_ext4 [%s]: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+
+#include "ext4_ice.h"
+#include "pfk_ext4.h"
+
+static bool pfk_ext4_ready;
+
+/*
+ * pfk_ext4_deinit() - Deinit function, should be invoked by upper PFK layer
+ */
+void pfk_ext4_deinit(void)
+{
+	pfk_ext4_ready = false;
+}
+
+/*
+ * pfk_ecryptfs_init() - Init function, should be invoked by upper PFK layer
+ */
+int __init pfk_ext4_init(void)
+{
+	pfk_ext4_ready = true;
+	pr_info("PFK EXT4 inited successfully\n");
+
+	return 0;
+}
+
+/**
+ * pfk_ecryptfs_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the driver is ready.
+ */
+static inline bool pfk_ext4_is_ready(void)
+{
+	return pfk_ext4_ready;
+}
+
+/**
+ * pfk_ext4_dump_inode() - dumps all interesting info about inode to the screen
+ *
+ *
+ */
+/*
+ * static void pfk_ext4_dump_inode(const struct inode* inode)
+ * {
+ *	struct ext4_crypt_info *ci = ext4_encryption_info((struct inode*)inode);
+ *
+ *	pr_debug("dumping inode with address 0x%p\n", inode);
+ *	pr_debug("S_ISREG is %d\n", S_ISREG(inode->i_mode));
+ *	pr_debug("EXT4_INODE_ENCRYPT flag is %d\n",
+ *		ext4_test_inode_flag((struct inode*)inode, EXT4_INODE_ENCRYPT));
+ *	if (ci) {
+ *		pr_debug("crypt_info address 0x%p\n", ci);
+ *		pr_debug("ci->ci_data_mode %d\n", ci->ci_data_mode);
+ *	} else {
+ *		pr_debug("crypt_info is NULL\n");
+ *	}
+ * }
+ */
+
+/**
+ * pfk_is_ext4_type() - return true if inode belongs to ICE EXT4 PFE
+ * @inode: inode pointer
+ */
+bool pfk_is_ext4_type(const struct inode *inode)
+{
+	if (!pfe_is_inode_filesystem_type(inode, "ext4"))
+		return false;
+
+	return ext4_should_be_processed_by_ice(inode);
+}
+
+/**
+ * pfk_ext4_parse_cipher() - parse cipher from inode to enum
+ * @inode: inode
+ * @algo: pointer to store the output enum (can be null)
+ *
+ * return 0 in case of success, error otherwise (i.e not supported cipher)
+ */
+static int pfk_ext4_parse_cipher(const struct inode *inode,
+	enum ice_cryto_algo_mode *algo)
+{
+	/*
+	 * currently only AES XTS algo is supported
+	 * in the future, table with supported ciphers might
+	 * be introduced
+	 */
+
+	if (!inode)
+		return -EINVAL;
+
+	if (!ext4_is_aes_xts_cipher(inode)) {
+		pr_err("ext4 alghoritm is not supported by pfk\n");
+		return -EINVAL;
+	}
+
+	if (algo)
+		*algo = ICE_CRYPTO_ALGO_MODE_AES_XTS;
+
+	return 0;
+}
+
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+	const struct inode *inode,
+	struct pfk_key_info *key_info,
+	enum ice_cryto_algo_mode *algo,
+	bool *is_pfe)
+{
+	int ret = 0;
+
+	if (!is_pfe)
+		return -EINVAL;
+
+	/*
+	 * only a few errors below can indicate that
+	 * this function was not invoked within PFE context,
+	 * otherwise we will consider it PFE
+	 */
+	*is_pfe = true;
+
+	if (!pfk_ext4_is_ready())
+		return -ENODEV;
+
+	if (!inode)
+		return -EINVAL;
+
+	if (!key_info)
+		return -EINVAL;
+
+	key_info->key = ext4_get_ice_encryption_key(inode);
+	if (!key_info->key) {
+		pr_err("could not parse key from ext4\n");
+		return -EINVAL;
+	}
+
+	key_info->key_size = ext4_get_ice_encryption_key_size(inode);
+	if (!key_info->key_size) {
+		pr_err("could not parse key size from ext4\n");
+		return -EINVAL;
+	}
+
+	key_info->salt = ext4_get_ice_encryption_salt(inode);
+	if (!key_info->salt) {
+		pr_err("could not parse salt from ext4\n");
+		return -EINVAL;
+	}
+
+	key_info->salt_size = ext4_get_ice_encryption_salt_size(inode);
+	if (!key_info->salt_size) {
+		pr_err("could not parse salt size from ext4\n");
+		return -EINVAL;
+	}
+
+	ret = pfk_ext4_parse_cipher(inode, algo);
+	if (ret != 0) {
+		pr_err("not supported cipher\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+	const struct bio *bio2, const struct inode *inode1,
+	const struct inode *inode2)
+{
+	/* if there is no ext4 pfk, don't disallow merging blocks */
+	if (!pfk_ext4_is_ready())
+		return true;
+
+	if (!inode1 || !inode2)
+		return false;
+
+	return ext4_is_ice_encryption_info_equal(inode1, inode2);
+}
+
diff --git a/security/pfe/pfk_ext4.h b/security/pfe/pfk_ext4.h
new file mode 100644
index 0000000..1f33632
--- /dev/null
+++ b/security/pfe/pfk_ext4.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 _PFK_EXT4_H_
+#define _PFK_EXT4_H_
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <crypto/ice.h>
+#include "pfk_internal.h"
+
+bool pfk_is_ext4_type(const struct inode *inode);
+
+int pfk_ext4_parse_inode(const struct bio *bio,
+	const struct inode *inode,
+	struct pfk_key_info *key_info,
+	enum ice_cryto_algo_mode *algo,
+	bool *is_pfe);
+
+bool pfk_ext4_allow_merge_bio(const struct bio *bio1,
+	const struct bio *bio2, const struct inode *inode1,
+	const struct inode *inode2);
+
+int __init pfk_ext4_init(void);
+
+void pfk_ext4_deinit(void);
+
+#endif /* _PFK_EXT4_H_ */
diff --git a/security/pfe/pfk_ice.c b/security/pfe/pfk_ice.c
new file mode 100644
index 0000000..f0bbf9c
--- /dev/null
+++ b/security/pfe/pfk_ice.c
@@ -0,0 +1,188 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 <linux/errno.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/async.h>
+#include <linux/mm.h>
+#include <linux/of.h>
+#include <soc/qcom/scm.h>
+#include <linux/device-mapper.h>
+#include <soc/qcom/qseecomi.h>
+#include <crypto/ice.h>
+#include "pfk_ice.h"
+
+
+/**********************************/
+/** global definitions		 **/
+/**********************************/
+
+#define TZ_ES_SET_ICE_KEY 0x2
+#define TZ_ES_INVALIDATE_ICE_KEY 0x3
+
+/* index 0 and 1 is reserved for FDE */
+#define MIN_ICE_KEY_INDEX 2
+
+#define MAX_ICE_KEY_INDEX 31
+
+
+#define TZ_ES_SET_ICE_KEY_ID \
+	TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, TZ_SVC_ES, TZ_ES_SET_ICE_KEY)
+
+
+#define TZ_ES_INVALIDATE_ICE_KEY_ID \
+		TZ_SYSCALL_CREATE_SMC_ID(TZ_OWNER_SIP, \
+			TZ_SVC_ES, TZ_ES_INVALIDATE_ICE_KEY)
+
+
+#define TZ_ES_SET_ICE_KEY_PARAM_ID \
+	TZ_SYSCALL_CREATE_PARAM_ID_5( \
+		TZ_SYSCALL_PARAM_TYPE_VAL, \
+		TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL, \
+		TZ_SYSCALL_PARAM_TYPE_BUF_RW, TZ_SYSCALL_PARAM_TYPE_VAL)
+
+#define TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID \
+	TZ_SYSCALL_CREATE_PARAM_ID_1( \
+	TZ_SYSCALL_PARAM_TYPE_VAL)
+
+#define ICE_KEY_SIZE 32
+#define ICE_SALT_SIZE 32
+
+static uint8_t ice_key[ICE_KEY_SIZE];
+static uint8_t ice_salt[ICE_KEY_SIZE];
+
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+			char *storage_type)
+{
+	struct scm_desc desc = {0};
+	int ret, ret1;
+	char *tzbuf_key = (char *)ice_key;
+	char *tzbuf_salt = (char *)ice_salt;
+	char *s_type = storage_type;
+
+	uint32_t smc_id = 0;
+	u32 tzbuflen_key = sizeof(ice_key);
+	u32 tzbuflen_salt = sizeof(ice_salt);
+
+	if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) {
+		pr_err("%s Invalid index %d\n", __func__, index);
+		return -EINVAL;
+	}
+	if (!key || !salt) {
+		pr_err("%s Invalid key/salt\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!tzbuf_key || !tzbuf_salt) {
+		pr_err("%s No Memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	if (s_type == NULL) {
+		pr_err("%s Invalid Storage type\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(tzbuf_key, 0, tzbuflen_key);
+	memset(tzbuf_salt, 0, tzbuflen_salt);
+
+	memcpy(ice_key, key, tzbuflen_key);
+	memcpy(ice_salt, salt, tzbuflen_salt);
+
+	dmac_flush_range(tzbuf_key, tzbuf_key + tzbuflen_key);
+	dmac_flush_range(tzbuf_salt, tzbuf_salt + tzbuflen_salt);
+
+	smc_id = TZ_ES_SET_ICE_KEY_ID;
+
+	desc.arginfo = TZ_ES_SET_ICE_KEY_PARAM_ID;
+	desc.args[0] = index;
+	desc.args[1] = virt_to_phys(tzbuf_key);
+	desc.args[2] = tzbuflen_key;
+	desc.args[3] = virt_to_phys(tzbuf_salt);
+	desc.args[4] = tzbuflen_salt;
+
+	ret = qcom_ice_setup_ice_hw((const char *)s_type, true);
+
+	if (ret) {
+		pr_err("%s: could not enable clocks: %d\n", __func__, ret);
+		goto out;
+	}
+
+	ret = scm_call2(smc_id, &desc);
+
+	if (ret) {
+		pr_err("%s: Set Key Error: %d\n", __func__, ret);
+		if (ret == -EBUSY) {
+			if (qcom_ice_setup_ice_hw((const char *)s_type, false))
+				pr_err("%s: clock disable failed\n", __func__);
+			goto out;
+		}
+		/*Try to invalidate the key to keep ICE in proper state*/
+		smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID;
+		desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID;
+		desc.args[0] = index;
+		ret1 = scm_call2(smc_id, &desc);
+		if (ret1)
+			pr_err("%s: Invalidate Key Error: %d\n", __func__,
+					ret1);
+	}
+	ret = qcom_ice_setup_ice_hw((const char *)s_type, false);
+
+out:
+	return ret;
+}
+
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type)
+{
+	struct scm_desc desc = {0};
+	int ret;
+
+	uint32_t smc_id = 0;
+
+	if (index < MIN_ICE_KEY_INDEX || index > MAX_ICE_KEY_INDEX) {
+		pr_err("%s Invalid index %d\n", __func__, index);
+		return -EINVAL;
+	}
+
+	if (storage_type == NULL) {
+		pr_err("%s Invalid Storage type\n", __func__);
+		return -EINVAL;
+	}
+
+	smc_id = TZ_ES_INVALIDATE_ICE_KEY_ID;
+
+	desc.arginfo = TZ_ES_INVALIDATE_ICE_KEY_PARAM_ID;
+	desc.args[0] = index;
+
+	ret = qcom_ice_setup_ice_hw((const char *)storage_type, true);
+
+	if (ret) {
+		pr_err("%s: could not enable clocks: 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	ret = scm_call2(smc_id, &desc);
+
+	if (ret) {
+		pr_err("%s: Error: 0x%x\n", __func__, ret);
+		if (qcom_ice_setup_ice_hw((const char *)storage_type, false))
+			pr_err("%s: could not disable clocks\n", __func__);
+	} else {
+		ret = qcom_ice_setup_ice_hw((const char *)storage_type, false);
+	}
+
+	return ret;
+}
diff --git a/security/pfe/pfk_ice.h b/security/pfe/pfk_ice.h
new file mode 100644
index 0000000..fb7c0d1
--- /dev/null
+++ b/security/pfe/pfk_ice.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef PFK_ICE_H_
+#define PFK_ICE_H_
+
+/*
+ * PFK ICE
+ *
+ * ICE keys configuration through scm calls.
+ *
+ */
+
+#include <linux/types.h>
+
+int pfk_ice_init(void);
+int pfk_ice_deinit(void);
+
+int qti_pfk_ice_set_key(uint32_t index, uint8_t *key, uint8_t *salt,
+			char *storage_type);
+int qti_pfk_ice_invalidate_key(uint32_t index, char *storage_type);
+
+
+#endif /* PFK_ICE_H_ */
diff --git a/security/pfe/pfk_internal.h b/security/pfe/pfk_internal.h
new file mode 100644
index 0000000..86526fa
--- /dev/null
+++ b/security/pfe/pfk_internal.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 _PFK_INTERNAL_H_
+#define _PFK_INTERNAL_H_
+
+#include <linux/types.h>
+#include <crypto/ice.h>
+
+struct pfk_key_info {
+	const unsigned char *key;
+	const unsigned char *salt;
+	size_t key_size;
+	size_t salt_size;
+};
+
+int pfk_key_size_to_key_type(size_t key_size,
+	enum ice_crpto_key_size *key_size_type);
+
+bool pfe_is_inode_filesystem_type(const struct inode *inode,
+	const char *fs_type);
+
+char *inode_to_filename(const struct inode *inode);
+
+#endif /* _PFK_INTERNAL_H_ */
diff --git a/security/pfe/pfk_kc.c b/security/pfe/pfk_kc.c
new file mode 100644
index 0000000..da71f80
--- /dev/null
+++ b/security/pfe/pfk_kc.c
@@ -0,0 +1,905 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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.
+ */
+
+/*
+ * PFK Key Cache
+ *
+ * Key Cache used internally in PFK.
+ * The purpose of the cache is to save access time to QSEE when loading keys.
+ * Currently the cache is the same size as the total number of keys that can
+ * be loaded to ICE. Since this number is relatively small, the algorithms for
+ * cache eviction are simple, linear and based on last usage timestamp, i.e
+ * the node that will be evicted is the one with the oldest timestamp.
+ * Empty entries always have the oldest timestamp.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <crypto/ice.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
+
+#include "pfk_kc.h"
+#include "pfk_ice.h"
+
+
+/** the first available index in ice engine */
+#define PFK_KC_STARTING_INDEX 2
+
+/** currently the only supported key and salt sizes */
+#define PFK_KC_KEY_SIZE 32
+#define PFK_KC_SALT_SIZE 32
+
+/** Table size */
+/* TODO replace by some constant from ice.h */
+#define PFK_KC_TABLE_SIZE ((32) - (PFK_KC_STARTING_INDEX))
+
+/** The maximum key and salt size */
+#define PFK_MAX_KEY_SIZE PFK_KC_KEY_SIZE
+#define PFK_MAX_SALT_SIZE PFK_KC_SALT_SIZE
+#define PFK_UFS "ufs"
+
+static DEFINE_SPINLOCK(kc_lock);
+static unsigned long flags;
+static bool kc_ready;
+static char *s_type = "sdcc";
+
+/**
+ * enum pfk_kc_entry_state - state of the entry inside kc table
+ *
+ * @FREE:		   entry is free
+ * @ACTIVE_ICE_PRELOAD:    entry is actively used by ICE engine
+			   and cannot be used by others. SCM call
+			   to load key to ICE is pending to be performed
+ * @ACTIVE_ICE_LOADED:     entry is actively used by ICE engine and
+			   cannot be used by others. SCM call to load the
+			   key to ICE was successfully executed and key is
+			   now loaded
+ * @INACTIVE_INVALIDATING: entry is being invalidated during file close
+			   and cannot be used by others until invalidation
+			   is complete
+ * @INACTIVE:		   entry's key is already loaded, but is not
+			   currently being used. It can be re-used for
+			   optimization and to avoid SCM call cost or
+			   it can be taken by another key if there are
+			   no FREE entries
+ * @SCM_ERROR:		   error occurred while scm call was performed to
+			   load the key to ICE
+ */
+enum pfk_kc_entry_state {
+	FREE,
+	ACTIVE_ICE_PRELOAD,
+	ACTIVE_ICE_LOADED,
+	INACTIVE_INVALIDATING,
+	INACTIVE,
+	SCM_ERROR
+};
+
+struct kc_entry {
+	 unsigned char key[PFK_MAX_KEY_SIZE];
+	 size_t key_size;
+
+	 unsigned char salt[PFK_MAX_SALT_SIZE];
+	 size_t salt_size;
+
+	 u64 time_stamp;
+	 u32 key_index;
+
+	 struct task_struct *thread_pending;
+
+	 enum pfk_kc_entry_state state;
+
+	 /* ref count for the number of requests in the HW queue for this key */
+	 int loaded_ref_cnt;
+	 int scm_error;
+};
+
+static struct kc_entry kc_table[PFK_KC_TABLE_SIZE];
+
+/**
+ * kc_is_ready() - driver is initialized and ready.
+ *
+ * Return: true if the key cache is ready.
+ */
+static inline bool kc_is_ready(void)
+{
+	return kc_ready;
+}
+
+static inline void kc_spin_lock(void)
+{
+	spin_lock_irqsave(&kc_lock, flags);
+}
+
+static inline void kc_spin_unlock(void)
+{
+	spin_unlock_irqrestore(&kc_lock, flags);
+}
+
+/**
+ * kc_entry_is_available() - checks whether the entry is available
+ *
+ * Return true if it is , false otherwise or if invalid
+ * Should be invoked under spinlock
+ */
+static bool kc_entry_is_available(const struct kc_entry *entry)
+{
+	if (!entry)
+		return false;
+
+	return (entry->state == FREE || entry->state == INACTIVE);
+}
+
+/**
+ * kc_entry_wait_till_available() - waits till entry is available
+ *
+ * Returns 0 in case of success or -ERESTARTSYS if the wait was interrupted
+ * by signal
+ *
+ * Should be invoked under spinlock
+ */
+static int kc_entry_wait_till_available(struct kc_entry *entry)
+{
+	int res = 0;
+
+	while (!kc_entry_is_available(entry)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (signal_pending(current)) {
+			res = -ERESTARTSYS;
+			break;
+		}
+		/* assuming only one thread can try to invalidate
+		 * the same entry
+		 */
+		entry->thread_pending = current;
+		kc_spin_unlock();
+		schedule();
+		kc_spin_lock();
+	}
+	set_current_state(TASK_RUNNING);
+
+	return res;
+}
+
+/**
+ * kc_entry_start_invalidating() - moves entry to state
+ *			           INACTIVE_INVALIDATING
+ *				   If entry is in use, waits till
+ *				   it gets available
+ * @entry: pointer to entry
+ *
+ * Return 0 in case of success, otherwise error
+ * Should be invoked under spinlock
+ */
+static int kc_entry_start_invalidating(struct kc_entry *entry)
+{
+	int res;
+
+	res = kc_entry_wait_till_available(entry);
+	if (res)
+		return res;
+
+	entry->state = INACTIVE_INVALIDATING;
+
+	return 0;
+}
+
+/**
+ * kc_entry_finish_invalidating() - moves entry to state FREE
+ *				    wakes up all the tasks waiting
+ *				    on it
+ *
+ * @entry: pointer to entry
+ *
+ * Return 0 in case of success, otherwise error
+ * Should be invoked under spinlock
+ */
+static void kc_entry_finish_invalidating(struct kc_entry *entry)
+{
+	if (!entry)
+		return;
+
+	if (entry->state != INACTIVE_INVALIDATING)
+		return;
+
+	entry->state = FREE;
+}
+
+/**
+ * kc_min_entry() - compare two entries to find one with minimal time
+ * @a: ptr to the first entry. If NULL the other entry will be returned
+ * @b: pointer to the second entry
+ *
+ * Return the entry which timestamp is the minimal, or b if a is NULL
+ */
+static inline struct kc_entry *kc_min_entry(struct kc_entry *a,
+		struct kc_entry *b)
+{
+	if (!a)
+		return b;
+
+	if (time_before64(b->time_stamp, a->time_stamp))
+		return b;
+
+	return a;
+}
+
+/**
+ * kc_entry_at_index() - return entry at specific index
+ * @index: index of entry to be accessed
+ *
+ * Return entry
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_entry_at_index(int index)
+{
+	return &(kc_table[index]);
+}
+
+/**
+ * kc_find_key_at_index() - find kc entry starting at specific index
+ * @key: key to look for
+ * @key_size: the key size
+ * @salt: salt to look for
+ * @salt_size: the salt size
+ * @sarting_index: index to start search with, if entry found, updated with
+ * index of that entry
+ *
+ * Return entry or NULL in case of error
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_find_key_at_index(const unsigned char *key,
+	size_t key_size, const unsigned char *salt, size_t salt_size,
+	int *starting_index)
+{
+	struct kc_entry *entry = NULL;
+	int i = 0;
+
+	for (i = *starting_index; i < PFK_KC_TABLE_SIZE; i++) {
+		entry = kc_entry_at_index(i);
+
+		if (salt != NULL) {
+			if (entry->salt_size != salt_size)
+				continue;
+
+			if (memcmp(entry->salt, salt, salt_size) != 0)
+				continue;
+		}
+
+		if (entry->key_size != key_size)
+			continue;
+
+		if (memcmp(entry->key, key, key_size) == 0) {
+			*starting_index = i;
+			return entry;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * kc_find_key() - find kc entry
+ * @key: key to look for
+ * @key_size: the key size
+ * @salt: salt to look for
+ * @salt_size: the salt size
+ *
+ * Return entry or NULL in case of error
+ * Should be invoked under spinlock
+ */
+static struct kc_entry *kc_find_key(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size)
+{
+	int index = 0;
+
+	return kc_find_key_at_index(key, key_size, salt, salt_size, &index);
+}
+
+/**
+ * kc_find_oldest_entry_non_locked() - finds the entry with minimal timestamp
+ * that is not locked
+ *
+ * Returns entry with minimal timestamp. Empty entries have timestamp
+ * of 0, therefore they are returned first.
+ * If all the entries are locked, will return NULL
+ * Should be invoked under spin lock
+ */
+static struct kc_entry *kc_find_oldest_entry_non_locked(void)
+{
+	struct kc_entry *curr_min_entry = NULL;
+	struct kc_entry *entry = NULL;
+	int i = 0;
+
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+		entry = kc_entry_at_index(i);
+
+		if (entry->state == FREE)
+			return entry;
+
+		if (entry->state == INACTIVE)
+			curr_min_entry = kc_min_entry(curr_min_entry, entry);
+	}
+
+	return curr_min_entry;
+}
+
+/**
+ * kc_update_timestamp() - updates timestamp of entry to current
+ *
+ * @entry: entry to update
+ *
+ */
+static void kc_update_timestamp(struct kc_entry *entry)
+{
+	if (!entry)
+		return;
+
+	entry->time_stamp = get_jiffies_64();
+}
+
+/**
+ * kc_clear_entry() - clear the key from entry and mark entry not in use
+ *
+ * @entry: pointer to entry
+ *
+ * Should be invoked under spinlock
+ */
+static void kc_clear_entry(struct kc_entry *entry)
+{
+	if (!entry)
+		return;
+
+	memset(entry->key, 0, entry->key_size);
+	memset(entry->salt, 0, entry->salt_size);
+
+	entry->key_size = 0;
+	entry->salt_size = 0;
+
+	entry->time_stamp = 0;
+	entry->scm_error = 0;
+
+	entry->state = FREE;
+
+	entry->loaded_ref_cnt = 0;
+	entry->thread_pending = NULL;
+}
+
+/**
+ * kc_update_entry() - replaces the key in given entry and
+ *			loads the new key to ICE
+ *
+ * @entry: entry to replace key in
+ * @key: key
+ * @key_size: key_size
+ * @salt: salt
+ * @salt_size: salt_size
+ *
+ * The previous key is securely released and wiped, the new one is loaded
+ * to ICE.
+ * Should be invoked under spinlock
+ */
+static int kc_update_entry(struct kc_entry *entry, const unsigned char *key,
+	size_t key_size, const unsigned char *salt, size_t salt_size)
+{
+	int ret;
+
+	kc_clear_entry(entry);
+
+	memcpy(entry->key, key, key_size);
+	entry->key_size = key_size;
+
+	memcpy(entry->salt, salt, salt_size);
+	entry->salt_size = salt_size;
+
+	/* Mark entry as no longer free before releasing the lock */
+	entry->state = ACTIVE_ICE_PRELOAD;
+	kc_spin_unlock();
+
+	ret = qti_pfk_ice_set_key(entry->key_index, entry->key,
+			entry->salt, s_type);
+
+	kc_spin_lock();
+	return ret;
+}
+
+/**
+ * pfk_kc_init() - init function
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_init(void)
+{
+	int i = 0;
+	struct kc_entry *entry = NULL;
+
+	kc_spin_lock();
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+		entry = kc_entry_at_index(i);
+		entry->key_index = PFK_KC_STARTING_INDEX + i;
+	}
+	kc_ready = true;
+	kc_spin_unlock();
+	return 0;
+}
+
+/**
+ * pfk_kc_denit() - deinit function
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_deinit(void)
+{
+	int res = pfk_kc_clear();
+
+	kc_ready = false;
+	return res;
+}
+
+/**
+ * pfk_kc_load_key_start() - retrieve the key from cache or add it if
+ * it's not there and return the ICE hw key index in @key_index.
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the salt
+ * @salt_size: the size of the salt
+ * @key_index: the pointer to key_index where the output will be stored
+ * @async: whether scm calls are allowed in the caller context
+ *
+ * If key is present in cache, than the key_index will be retrieved from cache.
+ * If it is not present, the oldest entry from kc table will be evicted,
+ * the key will be loaded to ICE via QSEE to the index that is the evicted
+ * entry number and stored in cache.
+ * Entry that is going to be used is marked as being used, it will mark
+ * as not being used when ICE finishes using it and pfk_kc_load_key_end
+ * will be invoked.
+ * As QSEE calls can only be done from a non-atomic context, when @async flag
+ * is set to 'false', it specifies that it is ok to make the calls in the
+ * current context. Otherwise, when @async is set, the caller should retry the
+ * call again from a different context, and -EAGAIN error will be returned.
+ *
+ * Return 0 in case of success, error otherwise
+ */
+int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size, u32 *key_index,
+		bool async)
+{
+	int ret = 0;
+	struct kc_entry *entry = NULL;
+	bool entry_exists = false;
+
+	if (!kc_is_ready())
+		return -ENODEV;
+
+	if (!key || !salt || !key_index) {
+		pr_err("%s key/salt/key_index NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (key_size != PFK_KC_KEY_SIZE) {
+		pr_err("unsupported key size %zu\n", key_size);
+		return -EINVAL;
+	}
+
+	if (salt_size != PFK_KC_SALT_SIZE) {
+		pr_err("unsupported salt size %zu\n", salt_size);
+		return -EINVAL;
+	}
+
+	kc_spin_lock();
+
+	entry = kc_find_key(key, key_size, salt, salt_size);
+	if (!entry) {
+		if (async) {
+			pr_debug("%s task will populate entry\n", __func__);
+			kc_spin_unlock();
+			return -EAGAIN;
+		}
+
+		entry = kc_find_oldest_entry_non_locked();
+		if (!entry) {
+			/* could not find a single non locked entry,
+			 * return EBUSY to upper layers so that the
+			 * request will be rescheduled
+			 */
+			kc_spin_unlock();
+			return -EBUSY;
+		}
+	} else {
+		entry_exists = true;
+	}
+
+	pr_debug("entry with index %d is in state %d\n",
+		entry->key_index, entry->state);
+
+	switch (entry->state) {
+	case (INACTIVE):
+		if (entry_exists) {
+			kc_update_timestamp(entry);
+			entry->state = ACTIVE_ICE_LOADED;
+
+			if (!strcmp(s_type, (char *)PFK_UFS)) {
+				if (async)
+					entry->loaded_ref_cnt++;
+			} else {
+				entry->loaded_ref_cnt++;
+			}
+			break;
+		}
+	case (FREE):
+		ret = kc_update_entry(entry, key, key_size, salt, salt_size);
+		if (ret) {
+			entry->state = SCM_ERROR;
+			entry->scm_error = ret;
+			pr_err("%s: key load error (%d)\n", __func__, ret);
+		} else {
+			kc_update_timestamp(entry);
+			entry->state = ACTIVE_ICE_LOADED;
+
+			/*
+			 * In case of UFS only increase ref cnt for async calls,
+			 * sync calls from within work thread do not pass
+			 * requests further to HW
+			 */
+			if (!strcmp(s_type, (char *)PFK_UFS)) {
+				if (async)
+					entry->loaded_ref_cnt++;
+			} else {
+				entry->loaded_ref_cnt++;
+			}
+		}
+		break;
+	case (ACTIVE_ICE_PRELOAD):
+	case (INACTIVE_INVALIDATING):
+		ret = -EAGAIN;
+		break;
+	case (ACTIVE_ICE_LOADED):
+		kc_update_timestamp(entry);
+
+		if (!strcmp(s_type, (char *)PFK_UFS)) {
+			if (async)
+				entry->loaded_ref_cnt++;
+		} else {
+			entry->loaded_ref_cnt++;
+		}
+		break;
+	case(SCM_ERROR):
+		ret = entry->scm_error;
+		kc_clear_entry(entry);
+		entry->state = FREE;
+		break;
+	default:
+		pr_err("invalid state %d for entry with key index %d\n",
+			entry->state, entry->key_index);
+		ret = -EINVAL;
+	}
+
+	*key_index = entry->key_index;
+	kc_spin_unlock();
+
+	return ret;
+}
+
+/**
+ * pfk_kc_load_key_end() - finish the process of key loading that was started
+ *						   by pfk_kc_load_key_start
+ *						   by marking the entry as not
+ *						   being in use
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the salt
+ * @salt_size: the size of the salt
+ *
+ */
+void pfk_kc_load_key_end(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size)
+{
+	struct kc_entry *entry = NULL;
+	struct task_struct *tmp_pending = NULL;
+	int ref_cnt = 0;
+
+	if (!kc_is_ready())
+		return;
+
+	if (!key || !salt)
+		return;
+
+	if (key_size != PFK_KC_KEY_SIZE)
+		return;
+
+	if (salt_size != PFK_KC_SALT_SIZE)
+		return;
+
+	kc_spin_lock();
+
+	entry = kc_find_key(key, key_size, salt, salt_size);
+	if (!entry) {
+		kc_spin_unlock();
+		pr_err("internal error, there should an entry to unlock\n");
+
+		return;
+	}
+	ref_cnt = --entry->loaded_ref_cnt;
+
+	if (ref_cnt < 0)
+		pr_err("internal error, ref count should never be negative\n");
+
+	if (!ref_cnt) {
+		entry->state = INACTIVE;
+		/*
+		 * wake-up invalidation if it's waiting
+		 * for the entry to be released
+		 */
+		if (entry->thread_pending) {
+			tmp_pending = entry->thread_pending;
+			entry->thread_pending = NULL;
+
+			kc_spin_unlock();
+			wake_up_process(tmp_pending);
+			return;
+		}
+	}
+
+	kc_spin_unlock();
+}
+
+/**
+ * pfk_kc_remove_key() - remove the key from cache and from ICE engine
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ * @salt: pointer to the key
+ * @salt_size: the size of the key
+ *
+ * Return 0 in case of success, error otherwise (also in case of non
+ * (existing key)
+ */
+int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size)
+{
+	struct kc_entry *entry = NULL;
+	int res = 0;
+
+	if (!kc_is_ready())
+		return -ENODEV;
+
+	if (!key)
+		return -EINVAL;
+
+	if (!salt)
+		return -EINVAL;
+
+	if (key_size != PFK_KC_KEY_SIZE)
+		return -EINVAL;
+
+	if (salt_size != PFK_KC_SALT_SIZE)
+		return -EINVAL;
+
+	kc_spin_lock();
+
+	entry = kc_find_key(key, key_size, salt, salt_size);
+	if (!entry) {
+		pr_debug("%s: key does not exist\n", __func__);
+		kc_spin_unlock();
+		return -EINVAL;
+	}
+
+	res = kc_entry_start_invalidating(entry);
+	if (res != 0) {
+		kc_spin_unlock();
+		return res;
+	}
+	kc_clear_entry(entry);
+
+	kc_spin_unlock();
+
+	qti_pfk_ice_invalidate_key(entry->key_index, s_type);
+
+	kc_spin_lock();
+	kc_entry_finish_invalidating(entry);
+	kc_spin_unlock();
+
+	return 0;
+}
+
+/**
+ * pfk_kc_remove_key() - remove the key from cache and from ICE engine
+ * when no salt is available. Will only search key part, if there are several,
+ * all will be removed
+ *
+ * @key: pointer to the key
+ * @key_size: the size of the key
+ *
+ * Return 0 in case of success, error otherwise (also for non-existing key)
+ */
+int pfk_kc_remove_key(const unsigned char *key, size_t key_size)
+{
+	struct kc_entry *entry = NULL;
+	int index = 0;
+	int temp_indexes[PFK_KC_TABLE_SIZE] = {0};
+	int temp_indexes_size = 0;
+	int i = 0;
+	int res = 0;
+
+	if (!kc_is_ready())
+		return -ENODEV;
+
+	if (!key)
+		return -EINVAL;
+
+	if (key_size != PFK_KC_KEY_SIZE)
+		return -EINVAL;
+
+	memset(temp_indexes, -1, sizeof(temp_indexes));
+
+	kc_spin_lock();
+
+	entry = kc_find_key_at_index(key, key_size, NULL, 0, &index);
+	if (!entry) {
+		pr_err("%s: key does not exist\n", __func__);
+		kc_spin_unlock();
+		return -EINVAL;
+	}
+
+	res = kc_entry_start_invalidating(entry);
+	if (res != 0) {
+		kc_spin_unlock();
+		return res;
+	}
+
+	temp_indexes[temp_indexes_size++] = index;
+	kc_clear_entry(entry);
+
+	/* let's clean additional entries with the same key if there are any */
+	do {
+		index++;
+		entry = kc_find_key_at_index(key, key_size, NULL, 0, &index);
+		if (!entry)
+			break;
+
+		res = kc_entry_start_invalidating(entry);
+		if (res != 0) {
+			kc_spin_unlock();
+			goto out;
+		}
+
+		temp_indexes[temp_indexes_size++] = index;
+
+		kc_clear_entry(entry);
+
+
+	} while (true);
+
+	kc_spin_unlock();
+
+	temp_indexes_size--;
+	for (i = temp_indexes_size; i >= 0 ; i--)
+		qti_pfk_ice_invalidate_key(
+			kc_entry_at_index(temp_indexes[i])->key_index,
+					s_type);
+
+	/* fall through */
+	res = 0;
+
+out:
+	kc_spin_lock();
+	for (i = temp_indexes_size; i >= 0 ; i--)
+		kc_entry_finish_invalidating(
+				kc_entry_at_index(temp_indexes[i]));
+	kc_spin_unlock();
+
+	return res;
+}
+
+/**
+ * pfk_kc_clear() - clear the table and remove all keys from ICE
+ *
+ * Return 0 on success, error otherwise
+ *
+ */
+int pfk_kc_clear(void)
+{
+	struct kc_entry *entry = NULL;
+	int i = 0;
+	int res = 0;
+
+	if (!kc_is_ready())
+		return -ENODEV;
+
+	kc_spin_lock();
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+		entry = kc_entry_at_index(i);
+		res = kc_entry_start_invalidating(entry);
+		if (res != 0) {
+			kc_spin_unlock();
+			goto out;
+		}
+		kc_clear_entry(entry);
+	}
+	kc_spin_unlock();
+
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++)
+		qti_pfk_ice_invalidate_key(kc_entry_at_index(i)->key_index,
+					s_type);
+
+	/* fall through */
+	res = 0;
+out:
+	kc_spin_lock();
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++)
+		kc_entry_finish_invalidating(kc_entry_at_index(i));
+	kc_spin_unlock();
+
+	return res;
+}
+
+/**
+ * pfk_kc_clear_on_reset() - clear the table and remove all keys from ICE
+ * The assumption is that at this point we don't have any pending transactions
+ * Also, there is no need to clear keys from ICE
+ *
+ * Return 0 on success, error otherwise
+ *
+ */
+void pfk_kc_clear_on_reset(void)
+{
+	struct kc_entry *entry = NULL;
+	int i = 0;
+
+	if (!kc_is_ready())
+		return;
+
+	kc_spin_lock();
+	for (i = 0; i < PFK_KC_TABLE_SIZE; i++) {
+		entry = kc_entry_at_index(i);
+		kc_clear_entry(entry);
+	}
+	kc_spin_unlock();
+}
+
+static int pfk_kc_find_storage_type(char **device)
+{
+	char boot[20] = {'\0'};
+	char *match = (char *)strnstr(saved_command_line,
+				"androidboot.bootdevice=",
+				strlen(saved_command_line));
+	if (match) {
+		memcpy(boot, (match + strlen("androidboot.bootdevice=")),
+			sizeof(boot) - 1);
+		if (strnstr(boot, PFK_UFS, strlen(boot)))
+			*device = PFK_UFS;
+
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int __init pfk_kc_pre_init(void)
+{
+	return pfk_kc_find_storage_type(&s_type);
+}
+
+static void __exit pfk_kc_exit(void)
+{
+	s_type = NULL;
+}
+
+module_init(pfk_kc_pre_init);
+module_exit(pfk_kc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Per-File-Key-KC driver");
diff --git a/security/pfe/pfk_kc.h b/security/pfe/pfk_kc.h
new file mode 100644
index 0000000..dc4ad15
--- /dev/null
+++ b/security/pfe/pfk_kc.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * 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 PFK_KC_H_
+#define PFK_KC_H_
+
+#include <linux/types.h>
+
+int pfk_kc_init(void);
+int pfk_kc_deinit(void);
+int pfk_kc_load_key_start(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size, u32 *key_index,
+		bool async);
+void pfk_kc_load_key_end(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size);
+int pfk_kc_remove_key_with_salt(const unsigned char *key, size_t key_size,
+		const unsigned char *salt, size_t salt_size);
+int pfk_kc_remove_key(const unsigned char *key, size_t key_size);
+int pfk_kc_clear(void);
+void pfk_kc_clear_on_reset(void);
+extern char *saved_command_line;
+
+
+#endif /* PFK_KC_H_ */
diff --git a/security/security.c b/security/security.c
index 6a7b359..e1f9e32 100644
--- a/security/security.c
+++ b/security/security.c
@@ -524,6 +524,14 @@
 }
 EXPORT_SYMBOL_GPL(security_inode_create);
 
+int security_inode_post_create(struct inode *dir, struct dentry *dentry,
+			       umode_t mode)
+{
+	if (unlikely(IS_PRIVATE(dir)))
+		return 0;
+	return call_int_hook(inode_post_create, 0, dir, dentry, mode);
+}
+
 int security_inode_link(struct dentry *old_dentry, struct inode *dir,
 			 struct dentry *new_dentry)
 {
@@ -1668,6 +1676,8 @@
 	.inode_init_security =
 		LIST_HEAD_INIT(security_hook_heads.inode_init_security),
 	.inode_create =	LIST_HEAD_INIT(security_hook_heads.inode_create),
+	.inode_post_create =
+		LIST_HEAD_INIT(security_hook_heads.inode_post_create),
 	.inode_link =	LIST_HEAD_INIT(security_hook_heads.inode_link),
 	.inode_unlink =	LIST_HEAD_INIT(security_hook_heads.inode_unlink),
 	.inode_symlink =
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index c21e135..13011038 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -25,8 +25,9 @@
 #include <linux/in.h>
 #include <linux/spinlock.h>
 #include <net/net_namespace.h>
-#include "flask.h"
-#include "avc.h"
+//#include "flask.h"
+//#include "avc.h"
+#include "security.h"
 
 struct task_security_struct {
 	u32 osid;		/* SID prior to last execve */
@@ -52,6 +53,8 @@
 	u32 sid;		/* SID of this object */
 	u16 sclass;		/* security class of this object */
 	unsigned char initialized;	/* initialization flag */
+	u32 tag;		/* Per-File-Encryption tag */
+	void *pfk_data; /* Per-File-Key data from ecryptfs */
 	struct mutex lock;
 };
 
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 308a286..b8e98c1 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -12,7 +12,6 @@
 #include <linux/dcache.h>
 #include <linux/magic.h>
 #include <linux/types.h>
-#include "flask.h"
 
 #define SECSID_NULL			0x00000000 /* unspecified SID */
 #define SECSID_WILD			0xffffffff /* wildcard SID */
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index b75c31a..530ed9b 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -1486,7 +1486,7 @@
  * @inode: the object
  * @name: attribute name
  * @buffer: where to put the result
- * @alloc: unused
+ * @alloc: duplicate memory
  *
  * Returns the size of the attribute or an error code
  */
@@ -1499,43 +1499,38 @@
 	struct super_block *sbp;
 	struct inode *ip = (struct inode *)inode;
 	struct smack_known *isp;
-	int ilen;
-	int rc = 0;
 
-	if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
+	if (strcmp(name, XATTR_SMACK_SUFFIX) == 0)
 		isp = smk_of_inode(inode);
-		ilen = strlen(isp->smk_known);
-		*buffer = isp->smk_known;
-		return ilen;
+	else {
+		/*
+		 * The rest of the Smack xattrs are only on sockets.
+		 */
+		sbp = ip->i_sb;
+		if (sbp->s_magic != SOCKFS_MAGIC)
+			return -EOPNOTSUPP;
+
+		sock = SOCKET_I(ip);
+		if (sock == NULL || sock->sk == NULL)
+			return -EOPNOTSUPP;
+
+		ssp = sock->sk->sk_security;
+
+		if (strcmp(name, XATTR_SMACK_IPIN) == 0)
+			isp = ssp->smk_in;
+		else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
+			isp = ssp->smk_out;
+		else
+			return -EOPNOTSUPP;
 	}
 
-	/*
-	 * The rest of the Smack xattrs are only on sockets.
-	 */
-	sbp = ip->i_sb;
-	if (sbp->s_magic != SOCKFS_MAGIC)
-		return -EOPNOTSUPP;
-
-	sock = SOCKET_I(ip);
-	if (sock == NULL || sock->sk == NULL)
-		return -EOPNOTSUPP;
-
-	ssp = sock->sk->sk_security;
-
-	if (strcmp(name, XATTR_SMACK_IPIN) == 0)
-		isp = ssp->smk_in;
-	else if (strcmp(name, XATTR_SMACK_IPOUT) == 0)
-		isp = ssp->smk_out;
-	else
-		return -EOPNOTSUPP;
-
-	ilen = strlen(isp->smk_known);
-	if (rc == 0) {
-		*buffer = isp->smk_known;
-		rc = ilen;
+	if (alloc) {
+		*buffer = kstrdup(isp->smk_known, GFP_KERNEL);
+		if (*buffer == NULL)
+			return -ENOMEM;
 	}
 
-	return rc;
+	return strlen(isp->smk_known);
 }
 
 
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index a098656..add1f8d 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -781,7 +781,7 @@
 	if (!retval) {
 		stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
 		wake_up(&stream->runtime->sleep);
-		return retval;
+		goto ret;
 	}
 
 ret:
@@ -981,14 +981,13 @@
 static int snd_compress_dev_register(struct snd_device *device)
 {
 	int ret = -EINVAL;
-	char str[16];
 	struct snd_compr *compr;
 
 	if (snd_BUG_ON(!device || !device->device_data))
 		return -EBADFD;
 	compr = device->device_data;
 
-	pr_debug("reg %s for device %s, direction %d\n", str, compr->name,
+	pr_debug("reg device %s, direction %d\n", compr->name,
 			compr->direction);
 	/* register compressed device */
 	ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS,
diff --git a/sound/core/control.c b/sound/core/control.c
index fb096cb..995cde4 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -1156,7 +1156,7 @@
 		mutex_lock(&ue->card->user_ctl_lock);
 		change = ue->tlv_data_size != size;
 		if (!change)
-			change = memcmp(ue->tlv_data, new_data, size);
+			change = memcmp(ue->tlv_data, new_data, size) != 0;
 		kfree(ue->tlv_data);
 		ue->tlv_data = new_data;
 		ue->tlv_data_size = size;
diff --git a/sound/core/info.c b/sound/core/info.c
index 2ffa3fa..08558ae 100644
--- a/sound/core/info.c
+++ b/sound/core/info.c
@@ -735,8 +735,11 @@
 	INIT_LIST_HEAD(&entry->children);
 	INIT_LIST_HEAD(&entry->list);
 	entry->parent = parent;
-	if (parent)
+	if (parent) {
+		mutex_lock(&parent->access);
 		list_add_tail(&entry->list, &parent->children);
+		mutex_unlock(&parent->access);
+	}
 	return entry;
 }
 
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index a2c2f06..4fc68b1 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -742,6 +742,7 @@
 		}
 		substream->group = &substream->self_group;
 		spin_lock_init(&substream->self_group.lock);
+		spin_lock_init(&substream->runtime_lock);
 		mutex_init(&substream->self_group.mutex);
 		INIT_LIST_HEAD(&substream->self_group.substreams);
 		list_add_tail(&substream->link_list, &substream->self_group.substreams);
@@ -1020,9 +1021,11 @@
 void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime;
+	unsigned long flags = 0;
 
 	if (PCM_RUNTIME_CHECK(substream))
 		return;
+	spin_lock_irqsave(&substream->runtime_lock, flags);
 	runtime = substream->runtime;
 	if (runtime->private_free != NULL)
 		runtime->private_free(runtime);
@@ -1036,6 +1039,7 @@
 	put_pid(substream->pid);
 	substream->pid = NULL;
 	substream->pstr->substream_opened--;
+	spin_unlock_irqrestore(&substream->runtime_lock, flags);
 }
 
 static ssize_t show_pcm_class(struct device *dev,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index 6faddfb..f8d0bd8 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -1857,8 +1857,6 @@
 		      unsigned int cmd, void *arg)
 {
 	switch (cmd) {
-	case SNDRV_PCM_IOCTL1_INFO:
-		return 0;
 	case SNDRV_PCM_IOCTL1_RESET:
 		return snd_pcm_lib_ioctl_reset(substream, arg);
 	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 3d9ff6d..8e5649a 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -197,7 +197,6 @@
 
 int snd_pcm_info(struct snd_pcm_substream *substream, struct snd_pcm_info *info)
 {
-	struct snd_pcm_runtime *runtime;
 	struct snd_pcm *pcm = substream->pcm;
 	struct snd_pcm_str *pstr = substream->pstr;
 
@@ -213,12 +212,7 @@
 	info->subdevices_count = pstr->substream_count;
 	info->subdevices_avail = pstr->substream_count - pstr->substream_opened;
 	strlcpy(info->subname, substream->name, sizeof(info->subname));
-	runtime = substream->runtime;
-	/* AB: FIXME!!! This is definitely nonsense */
-	if (runtime) {
-		info->sync = runtime->sync;
-		substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
-	}
+
 	return 0;
 }
 
diff --git a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
index 20ecd8f..5258ecc 100644
--- a/sound/core/pcm_timer.c
+++ b/sound/core/pcm_timer.c
@@ -65,9 +65,16 @@
 static unsigned long snd_pcm_timer_resolution(struct snd_timer * timer)
 {
 	struct snd_pcm_substream *substream;
+	unsigned long ret = 0, flags = 0;
 	
 	substream = timer->private_data;
-	return substream->runtime ? substream->runtime->timer_resolution : 0;
+	spin_lock_irqsave(&substream->runtime_lock, flags);
+	if (substream->runtime)
+		ret = substream->runtime->timer_resolution;
+	else
+		ret = 0;
+	spin_unlock_irqrestore(&substream->runtime_lock, flags);
+	return ret;
 }
 
 static int snd_pcm_timer_start(struct snd_timer * timer)
diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c
index aaff9ee..b30b213 100644
--- a/sound/core/seq/oss/seq_oss_midi.c
+++ b/sound/core/seq/oss/seq_oss_midi.c
@@ -612,9 +612,7 @@
 	if (!dp->timer->running)
 		len = snd_seq_oss_timer_start(dp->timer);
 	if (ev->type == SNDRV_SEQ_EVENT_SYSEX) {
-		if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) == SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
-			snd_seq_oss_readq_puts(dp->readq, mdev->seq_device,
-					       ev->data.ext.ptr, ev->data.ext.len);
+		snd_seq_oss_readq_sysex(dp->readq, mdev->seq_device, ev);
 	} else {
 		len = snd_midi_event_decode(mdev->coder, msg, sizeof(msg), ev);
 		if (len > 0)
diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
index 046cb586..06b2122 100644
--- a/sound/core/seq/oss/seq_oss_readq.c
+++ b/sound/core/seq/oss/seq_oss_readq.c
@@ -118,6 +118,35 @@
 }
 
 /*
+ * put MIDI sysex bytes; the event buffer may be chained, thus it has
+ * to be expanded via snd_seq_dump_var_event().
+ */
+struct readq_sysex_ctx {
+	struct seq_oss_readq *readq;
+	int dev;
+};
+
+static int readq_dump_sysex(void *ptr, void *buf, int count)
+{
+	struct readq_sysex_ctx *ctx = ptr;
+
+	return snd_seq_oss_readq_puts(ctx->readq, ctx->dev, buf, count);
+}
+
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+			    struct snd_seq_event *ev)
+{
+	struct readq_sysex_ctx ctx = {
+		.readq = q,
+		.dev = dev
+	};
+
+	if ((ev->flags & SNDRV_SEQ_EVENT_LENGTH_MASK) != SNDRV_SEQ_EVENT_LENGTH_VARIABLE)
+		return 0;
+	return snd_seq_dump_var_event(ev, readq_dump_sysex, &ctx);
+}
+
+/*
  * copy an event to input queue:
  * return zero if enqueued
  */
diff --git a/sound/core/seq/oss/seq_oss_readq.h b/sound/core/seq/oss/seq_oss_readq.h
index f1463f1..8d033ca 100644
--- a/sound/core/seq/oss/seq_oss_readq.h
+++ b/sound/core/seq/oss/seq_oss_readq.h
@@ -44,6 +44,8 @@
 void snd_seq_oss_readq_clear(struct seq_oss_readq *readq);
 unsigned int snd_seq_oss_readq_poll(struct seq_oss_readq *readq, struct file *file, poll_table *wait);
 int snd_seq_oss_readq_puts(struct seq_oss_readq *readq, int dev, unsigned char *data, int len);
+int snd_seq_oss_readq_sysex(struct seq_oss_readq *q, int dev,
+			    struct snd_seq_event *ev);
 int snd_seq_oss_readq_put_event(struct seq_oss_readq *readq, union evrec *ev);
 int snd_seq_oss_readq_put_timestamp(struct seq_oss_readq *readq, unsigned long curt, int seq_mode);
 int snd_seq_oss_readq_pick(struct seq_oss_readq *q, union evrec *rec);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index f3b1d7f..45ef591 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -663,7 +663,7 @@
 	if (atomic)
 		read_lock(&grp->list_lock);
 	else
-		down_read(&grp->list_mutex);
+		down_read_nested(&grp->list_mutex, hop);
 	list_for_each_entry(subs, &grp->list_head, src_list) {
 		/* both ports ready? */
 		if (atomic_read(&subs->ref_count) != 2)
@@ -1259,6 +1259,7 @@
 	struct snd_seq_port_info *info = arg;
 	struct snd_seq_client_port *port;
 	struct snd_seq_port_callback *callback;
+	int port_idx;
 
 	/* it is not allowed to create the port for an another client */
 	if (info->addr.client != client->number)
@@ -1269,7 +1270,9 @@
 		return -ENOMEM;
 
 	if (client->type == USER_CLIENT && info->kernel) {
-		snd_seq_delete_port(client, port->addr.port);
+		port_idx = port->addr.port;
+		snd_seq_port_unlock(port);
+		snd_seq_delete_port(client, port_idx);
 		return -EINVAL;
 	}
 	if (client->type == KERNEL_CLIENT) {
@@ -1290,6 +1293,7 @@
 
 	snd_seq_set_port_info(port, info);
 	snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
+	snd_seq_port_unlock(port);
 
 	return 0;
 }
@@ -1502,16 +1506,11 @@
 static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
 {
 	struct snd_seq_queue_info *info = arg;
-	int result;
 	struct snd_seq_queue *q;
 
-	result = snd_seq_queue_alloc(client->number, info->locked, info->flags);
-	if (result < 0)
-		return result;
-
-	q = queueptr(result);
-	if (q == NULL)
-		return -EINVAL;
+	q = snd_seq_queue_alloc(client->number, info->locked, info->flags);
+	if (IS_ERR(q))
+		return PTR_ERR(q);
 
 	info->queue = q->queue;
 	info->locked = q->locked;
@@ -1521,7 +1520,7 @@
 	if (!info->name[0])
 		snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue);
 	strlcpy(q->name, info->name, sizeof(q->name));
-	queuefree(q);
+	snd_use_lock_free(&q->use_lock);
 
 	return 0;
 }
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 12ba833..ba5752e 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -23,8 +23,6 @@
 #include <sound/core.h>
 #include "seq_lock.h"
 
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
 /* wait until all locks are released */
 void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
 {
@@ -42,5 +40,3 @@
 }
 
 EXPORT_SYMBOL(snd_use_lock_sync_helper);
-
-#endif
diff --git a/sound/core/seq/seq_lock.h b/sound/core/seq/seq_lock.h
index 54044bc..ac38031 100644
--- a/sound/core/seq/seq_lock.h
+++ b/sound/core/seq/seq_lock.h
@@ -3,8 +3,6 @@
 
 #include <linux/sched.h>
 
-#if defined(CONFIG_SMP) || defined(CONFIG_SND_DEBUG)
-
 typedef atomic_t snd_use_lock_t;
 
 /* initialize lock */
@@ -20,14 +18,4 @@
 void snd_use_lock_sync_helper(snd_use_lock_t *lock, const char *file, int line);
 #define snd_use_lock_sync(lockp) snd_use_lock_sync_helper(lockp, __BASE_FILE__, __LINE__)
 
-#else /* SMP || CONFIG_SND_DEBUG */
-
-typedef spinlock_t snd_use_lock_t;	/* dummy */
-#define snd_use_lock_init(lockp) /**/
-#define snd_use_lock_use(lockp) /**/
-#define snd_use_lock_free(lockp) /**/
-#define snd_use_lock_sync(lockp) /**/
-
-#endif /* SMP || CONFIG_SND_DEBUG */
-
 #endif /* __SND_SEQ_LOCK_H */
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index fe686ee..f04714d 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -122,7 +122,9 @@
 }
 
 
-/* create a port, port number is returned (-1 on failure) */
+/* create a port, port number is returned (-1 on failure);
+ * the caller needs to unref the port via snd_seq_port_unlock() appropriately
+ */
 struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
 						int port)
 {
@@ -151,6 +153,7 @@
 	snd_use_lock_init(&new_port->use_lock);
 	port_subs_info_init(&new_port->c_src);
 	port_subs_info_init(&new_port->c_dest);
+	snd_use_lock_use(&new_port->use_lock);
 
 	num = port >= 0 ? port : 0;
 	mutex_lock(&client->ports_mutex);
@@ -165,9 +168,9 @@
 	list_add_tail(&new_port->list, &p->list);
 	client->num_ports++;
 	new_port->addr.port = num;	/* store the port number in the port */
+	sprintf(new_port->name, "port-%d", num);
 	write_unlock_irqrestore(&client->ports_lock, flags);
 	mutex_unlock(&client->ports_mutex);
-	sprintf(new_port->name, "port-%d", num);
 
 	return new_port;
 }
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index 450c518..79e0c56 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -184,22 +184,26 @@
 static void queue_use(struct snd_seq_queue *queue, int client, int use);
 
 /* allocate a new queue -
- * return queue index value or negative value for error
+ * return pointer to new queue or ERR_PTR(-errno) for error
+ * The new queue's use_lock is set to 1. It is the caller's responsibility to
+ * call snd_use_lock_free(&q->use_lock).
  */
-int snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int info_flags)
 {
 	struct snd_seq_queue *q;
 
 	q = queue_new(client, locked);
 	if (q == NULL)
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	q->info_flags = info_flags;
 	queue_use(q, client, 1);
+	snd_use_lock_use(&q->use_lock);
 	if (queue_list_add(q) < 0) {
+		snd_use_lock_free(&q->use_lock);
 		queue_delete(q);
-		return -ENOMEM;
+		return ERR_PTR(-ENOMEM);
 	}
-	return q->queue;
+	return q;
 }
 
 /* delete a queue - queue must be owned by the client */
diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
index 30c8111..7190934 100644
--- a/sound/core/seq/seq_queue.h
+++ b/sound/core/seq/seq_queue.h
@@ -71,7 +71,7 @@
 
 
 /* create new queue (constructor) */
-int snd_seq_queue_alloc(int client, int locked, unsigned int flags);
+struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int flags);
 
 /* delete queue (destructor) */
 int snd_seq_queue_delete(int client, int queueid);
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index c82ed3e..2007649 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -77,13 +77,17 @@
  * decode input event and put to read buffer of each opened file
  */
 static int snd_virmidi_dev_receive_event(struct snd_virmidi_dev *rdev,
-					 struct snd_seq_event *ev)
+					 struct snd_seq_event *ev,
+					 bool atomic)
 {
 	struct snd_virmidi *vmidi;
 	unsigned char msg[4];
 	int len;
 
-	read_lock(&rdev->filelist_lock);
+	if (atomic)
+		read_lock(&rdev->filelist_lock);
+	else
+		down_read(&rdev->filelist_sem);
 	list_for_each_entry(vmidi, &rdev->filelist, list) {
 		if (!vmidi->trigger)
 			continue;
@@ -97,7 +101,10 @@
 				snd_rawmidi_receive(vmidi->substream, msg, len);
 		}
 	}
-	read_unlock(&rdev->filelist_lock);
+	if (atomic)
+		read_unlock(&rdev->filelist_lock);
+	else
+		up_read(&rdev->filelist_sem);
 
 	return 0;
 }
@@ -115,7 +122,7 @@
 	struct snd_virmidi_dev *rdev;
 
 	rdev = rmidi->private_data;
-	return snd_virmidi_dev_receive_event(rdev, ev);
+	return snd_virmidi_dev_receive_event(rdev, ev, true);
 }
 #endif  /*  0  */
 
@@ -130,7 +137,7 @@
 	rdev = private_data;
 	if (!(rdev->flags & SNDRV_VIRMIDI_USE))
 		return 0; /* ignored */
-	return snd_virmidi_dev_receive_event(rdev, ev);
+	return snd_virmidi_dev_receive_event(rdev, ev, atomic);
 }
 
 /*
@@ -209,7 +216,6 @@
 	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 	struct snd_rawmidi_runtime *runtime = substream->runtime;
 	struct snd_virmidi *vmidi;
-	unsigned long flags;
 
 	vmidi = kzalloc(sizeof(*vmidi), GFP_KERNEL);
 	if (vmidi == NULL)
@@ -223,9 +229,11 @@
 	vmidi->client = rdev->client;
 	vmidi->port = rdev->port;	
 	runtime->private_data = vmidi;
-	write_lock_irqsave(&rdev->filelist_lock, flags);
+	down_write(&rdev->filelist_sem);
+	write_lock_irq(&rdev->filelist_lock);
 	list_add_tail(&vmidi->list, &rdev->filelist);
-	write_unlock_irqrestore(&rdev->filelist_lock, flags);
+	write_unlock_irq(&rdev->filelist_lock);
+	up_write(&rdev->filelist_sem);
 	vmidi->rdev = rdev;
 	return 0;
 }
@@ -264,9 +272,11 @@
 	struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
 	struct snd_virmidi *vmidi = substream->runtime->private_data;
 
+	down_write(&rdev->filelist_sem);
 	write_lock_irq(&rdev->filelist_lock);
 	list_del(&vmidi->list);
 	write_unlock_irq(&rdev->filelist_lock);
+	up_write(&rdev->filelist_sem);
 	snd_midi_event_free(vmidi->parser);
 	substream->runtime->private_data = NULL;
 	kfree(vmidi);
@@ -520,6 +530,7 @@
 	rdev->rmidi = rmidi;
 	rdev->device = device;
 	rdev->client = -1;
+	init_rwsem(&rdev->filelist_sem);
 	rwlock_init(&rdev->filelist_lock);
 	INIT_LIST_HEAD(&rdev->filelist);
 	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_DISPATCH;
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index 6a437eb..59127b6 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -133,7 +133,8 @@
 #endif /* CONFIG_X86_X32 */
 };
 
-static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
+static long __snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+					  unsigned long arg)
 {
 	void __user *argp = compat_ptr(arg);
 
@@ -153,7 +154,7 @@
 	case SNDRV_TIMER_IOCTL_PAUSE:
 	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
 	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
-		return snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
+		return __snd_timer_user_ioctl(file, cmd, (unsigned long)argp);
 	case SNDRV_TIMER_IOCTL_GPARAMS32:
 		return snd_timer_user_gparams_compat(file, argp);
 	case SNDRV_TIMER_IOCTL_INFO32:
@@ -167,3 +168,15 @@
 	}
 	return -ENOIOCTLCMD;
 }
+
+static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd,
+					unsigned long arg)
+{
+	struct snd_timer_user *tu = file->private_data;
+	long ret;
+
+	mutex_lock(&tu->ioctl_lock);
+	ret = __snd_timer_user_ioctl_compat(file, cmd, arg);
+	mutex_unlock(&tu->ioctl_lock);
+	return ret;
+}
diff --git a/sound/firewire/iso-resources.c b/sound/firewire/iso-resources.c
index f0e4d50..066b5df 100644
--- a/sound/firewire/iso-resources.c
+++ b/sound/firewire/iso-resources.c
@@ -210,9 +210,14 @@
  */
 void fw_iso_resources_free(struct fw_iso_resources *r)
 {
-	struct fw_card *card = fw_parent_device(r->unit)->card;
+	struct fw_card *card;
 	int bandwidth, channel;
 
+	/* Not initialized. */
+	if (r->unit == NULL)
+		return;
+	card = fw_parent_device(r->unit)->card;
+
 	mutex_lock(&r->mutex);
 
 	if (r->allocated) {
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 0f41257..8761877 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -284,6 +284,11 @@
 		dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
 			(cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
 
+		if (cur_cap == -1) {
+			dev_dbg(bus->dev, "Invalid capability reg read\n");
+			break;
+		}
+
 		switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
 		case AZX_ML_CAP_ID:
 			dev_dbg(bus->dev, "Found ML capability\n");
diff --git a/sound/isa/msnd/msnd_midi.c b/sound/isa/msnd/msnd_midi.c
index ffc67fd..58e59cd 100644
--- a/sound/isa/msnd/msnd_midi.c
+++ b/sound/isa/msnd/msnd_midi.c
@@ -120,24 +120,24 @@
 	unsigned long flags;
 	struct snd_msndmidi *mpu = mpuv;
 	void *pwMIDQData = mpu->dev->mappedbase + MIDQ_DATA_BUFF;
+	u16 head, tail, size;
 
 	spin_lock_irqsave(&mpu->input_lock, flags);
-	while (readw(mpu->dev->MIDQ + JQS_wTail) !=
-	       readw(mpu->dev->MIDQ + JQS_wHead)) {
-		u16 wTmp, val;
-		val = readw(pwMIDQData + 2 * readw(mpu->dev->MIDQ + JQS_wHead));
+	head = readw(mpu->dev->MIDQ + JQS_wHead);
+	tail = readw(mpu->dev->MIDQ + JQS_wTail);
+	size = readw(mpu->dev->MIDQ + JQS_wSize);
+	if (head > size || tail > size)
+		goto out;
+	while (head != tail) {
+		unsigned char val = readw(pwMIDQData + 2 * head);
 
-			if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER,
-				     &mpu->mode))
-				snd_rawmidi_receive(mpu->substream_input,
-						    (unsigned char *)&val, 1);
-
-		wTmp = readw(mpu->dev->MIDQ + JQS_wHead) + 1;
-		if (wTmp > readw(mpu->dev->MIDQ + JQS_wSize))
-			writew(0,  mpu->dev->MIDQ + JQS_wHead);
-		else
-			writew(wTmp,  mpu->dev->MIDQ + JQS_wHead);
+		if (test_bit(MSNDMIDI_MODE_BIT_INPUT_TRIGGER, &mpu->mode))
+			snd_rawmidi_receive(mpu->substream_input, &val, 1);
+		if (++head > size)
+			head = 0;
+		writew(head, mpu->dev->MIDQ + JQS_wHead);
 	}
+ out:
 	spin_unlock_irqrestore(&mpu->input_lock, flags);
 }
 EXPORT_SYMBOL(snd_msndmidi_input_read);
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 4c07266..a31ea6c 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -170,23 +170,24 @@
 {
 	struct snd_msnd *chip = dev_id;
 	void *pwDSPQData = chip->mappedbase + DSPQ_DATA_BUFF;
+	u16 head, tail, size;
 
 	/* Send ack to DSP */
 	/* inb(chip->io + HP_RXL); */
 
 	/* Evaluate queued DSP messages */
-	while (readw(chip->DSPQ + JQS_wTail) != readw(chip->DSPQ + JQS_wHead)) {
-		u16 wTmp;
-
-		snd_msnd_eval_dsp_msg(chip,
-			readw(pwDSPQData + 2 * readw(chip->DSPQ + JQS_wHead)));
-
-		wTmp = readw(chip->DSPQ + JQS_wHead) + 1;
-		if (wTmp > readw(chip->DSPQ + JQS_wSize))
-			writew(0, chip->DSPQ + JQS_wHead);
-		else
-			writew(wTmp, chip->DSPQ + JQS_wHead);
+	head = readw(chip->DSPQ + JQS_wHead);
+	tail = readw(chip->DSPQ + JQS_wTail);
+	size = readw(chip->DSPQ + JQS_wSize);
+	if (head > size || tail > size)
+		goto out;
+	while (head != tail) {
+		snd_msnd_eval_dsp_msg(chip, readw(pwDSPQData + 2 * head));
+		if (++head > size)
+			head = 0;
+		writew(head, chip->DSPQ + JQS_wHead);
 	}
+ out:
 	/* Send ack to DSP */
 	inb(chip->io + HP_RXL);
 	return IRQ_HANDLED;
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index e1af24f..c308a4f 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2279,6 +2279,9 @@
 	} else {
 		int src[2], mix[2];
 
+		if (nr_ch < 1)
+			return -EINVAL;
+
 		/* Get SRC and MIXER hardware resources. */
 		for (i = 0; i < nr_ch; i++) {
 			if ((mix[i] =
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 9370717..286f5e3 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1272,11 +1272,11 @@
 
 	chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
 	uinfo->dimen.d[0] = num_busses_out(chip);
 	uinfo->dimen.d[1] = num_busses_in(chip);
-	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
 	return 0;
 }
 
@@ -1344,11 +1344,11 @@
 
 	chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = ECHOGAIN_MAXOUT;
 	uinfo->dimen.d[0] = num_busses_out(chip);
 	uinfo->dimen.d[1] = num_pipes_out(chip);
-	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
 	return 0;
 }
 
@@ -1728,6 +1728,7 @@
 				  struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 96;
 	uinfo->value.integer.min = ECHOGAIN_MINOUT;
 	uinfo->value.integer.max = 0;
 #ifdef ECHOCARD_HAS_VMIXER
@@ -1737,7 +1738,6 @@
 #endif
 	uinfo->dimen.d[1] = 16;	/* 16 channels */
 	uinfo->dimen.d[2] = 2;	/* 0=level, 1=peak */
-	uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1] * uinfo->dimen.d[2];
 	return 0;
 }
 
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 9913be8..e46c561 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1755,7 +1755,7 @@
 			return -1;
 		if (*step_to_check && *step_to_check != step) {
 			codec_err(codec, "Mismatching dB step for vmaster slave (%d!=%d)\n",
--				   *step_to_check, step);
+				   *step_to_check, step);
 			return -1;
 		}
 		*step_to_check = step;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index c15c51b..f2e4e99 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -854,6 +854,7 @@
 	SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC),
 	SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
 	SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
+	SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
 	SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
 	SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
 	SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 775c678..bd65022 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -3685,6 +3685,7 @@
 HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",	patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",	patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI",	patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI",	patch_i915_hsw_hdmi),
 HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI",	patch_generic_hdmi),
 HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",	patch_i915_byt_hdmi),
 HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",	patch_i915_byt_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index bb1aad3..fe1d06d 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -329,6 +329,7 @@
 		break;
 	case 0x10ec0225:
 	case 0x10ec0233:
+	case 0x10ec0236:
 	case 0x10ec0255:
 	case 0x10ec0256:
 	case 0x10ec0282:
@@ -909,6 +910,7 @@
 	{ 0x10ec0275, 0x1028, 0, "ALC3260" },
 	{ 0x10ec0899, 0x1028, 0, "ALC3861" },
 	{ 0x10ec0298, 0x1028, 0, "ALC3266" },
+	{ 0x10ec0236, 0x1028, 0, "ALC3204" },
 	{ 0x10ec0256, 0x1028, 0, "ALC3246" },
 	{ 0x10ec0225, 0x1028, 0, "ALC3253" },
 	{ 0x10ec0295, 0x1028, 0, "ALC3254" },
@@ -2233,6 +2235,7 @@
 	SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3),
 	SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
 	SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
+	SND_PCI_QUIRK(0x104d, 0x9060, "Sony Vaio VPCL14M1R", ALC882_FIXUP_NO_PRIMARY_HP),
 	SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
 	SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
 
@@ -3693,6 +3696,7 @@
 		alc_process_coef_fw(codec, coef0255_1);
 		alc_process_coef_fw(codec, coef0255);
 		break;
+	case 0x10ec0236:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, coef0256);
 		alc_process_coef_fw(codec, coef0255);
@@ -3776,6 +3780,7 @@
 
 
 	switch (codec->core.vendor_id) {
+	case 0x10ec0236:
 	case 0x10ec0255:
 	case 0x10ec0256:
 		alc_write_coef_idx(codec, 0x45, 0xc489);
@@ -3884,6 +3889,7 @@
 	case 0x10ec0295:
 		alc_process_coef_fw(codec, coef0225);
 		break;
+	case 0x10ec0236:
 	case 0x10ec0255:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, coef0255);
@@ -3970,6 +3976,7 @@
 	case 0x10ec0255:
 		alc_process_coef_fw(codec, coef0255);
 		break;
+	case 0x10ec0236:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, coef0256);
 		break;
@@ -4063,6 +4070,7 @@
 	case 0x10ec0255:
 		alc_process_coef_fw(codec, coef0255);
 		break;
+	case 0x10ec0236:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, coef0256);
 		break;
@@ -4130,6 +4138,7 @@
 	};
 
 	switch (codec->core.vendor_id) {
+	case 0x10ec0236:
 	case 0x10ec0255:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, coef0255);
@@ -4334,6 +4343,7 @@
 	case 0x10ec0255:
 		alc_process_coef_fw(codec, alc255fw);
 		break;
+	case 0x10ec0236:
 	case 0x10ec0256:
 		alc_process_coef_fw(codec, alc256fw);
 		break;
@@ -5851,6 +5861,14 @@
 		ALC225_STANDARD_PINS,
 		{0x12, 0xb7a60130},
 		{0x1b, 0x90170110}),
+	SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+		{0x12, 0x90a60140},
+		{0x14, 0x90170110},
+		{0x21, 0x02211020}),
+	SND_HDA_PIN_QUIRK(0x10ec0236, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+		{0x12, 0x90a60140},
+		{0x14, 0x90170150},
+		{0x21, 0x02211020}),
 	SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
 		{0x14, 0x90170110},
 		{0x21, 0x02211020}),
@@ -6225,6 +6243,7 @@
 	case 0x10ec0255:
 		spec->codec_variant = ALC269_TYPE_ALC255;
 		break;
+	case 0x10ec0236:
 	case 0x10ec0256:
 		spec->codec_variant = ALC269_TYPE_ALC256;
 		spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
@@ -7204,6 +7223,7 @@
 	HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+	HDA_CODEC_ENTRY(0x10ec0236, "ALC236", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
 	HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 439aa3f..79dcb1e 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -91,6 +91,27 @@
 	return 0;
 }
 
+static int adau17x1_adc_fixup(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct adau *adau = snd_soc_codec_get_drvdata(codec);
+
+	/*
+	 * If we are capturing, toggle the ADOSR bit in Converter Control 0 to
+	 * avoid losing SNR (workaround from ADI). This must be done after
+	 * the ADC(s) have been enabled. According to the data sheet, it is
+	 * normally illegal to set this bit when the sampling rate is 96 kHz,
+	 * but according to ADI it is acceptable for this workaround.
+	 */
+	regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+		ADAU17X1_CONVERTER0_ADOSR, ADAU17X1_CONVERTER0_ADOSR);
+	regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
+		ADAU17X1_CONVERTER0_ADOSR, 0);
+
+	return 0;
+}
+
 static const char * const adau17x1_mono_stereo_text[] = {
 	"Stereo",
 	"Mono Left Channel (L+R)",
@@ -122,7 +143,8 @@
 	SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
 		&adau17x1_dac_mode_mux),
 
-	SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
+	SND_SOC_DAPM_ADC_E("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0,
+			   adau17x1_adc_fixup, SND_SOC_DAPM_POST_PMU),
 	SND_SOC_DAPM_ADC("Right Decimator", NULL, ADAU17X1_ADC_CONTROL, 1, 0),
 	SND_SOC_DAPM_DAC("Left DAC", NULL, ADAU17X1_DAC_CONTROL0, 0, 0),
 	SND_SOC_DAPM_DAC("Right DAC", NULL, ADAU17X1_DAC_CONTROL0, 1, 0),
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index bf04b7ef..db35003 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -129,5 +129,7 @@
 
 #define ADAU17X1_CONVERTER0_CONVSR_MASK		0x7
 
+#define ADAU17X1_CONVERTER0_ADOSR		BIT(3)
+
 
 #endif
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index f24b7cf..e024800 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -395,14 +395,14 @@
 	"DMIC1", "DMIC2"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
 	RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
 
 static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
 	SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
 	RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
 
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 10c2a564..1ac96ef 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3833,6 +3833,9 @@
 		}
 	}
 
+	regmap_update_bits(rt5645->regmap, RT5645_ADDA_CLK1,
+		RT5645_I2S_PD1_MASK, RT5645_I2S_PD1_2);
+
 	if (rt5645->pdata.jd_invert) {
 		regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
 			RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index db54550..635818f 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -1150,28 +1150,28 @@
 	"L/R", "R/L", "L/L", "R/R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_01_adc_enum,
 	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT01_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_23_adc_enum,
 	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT23_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_45_adc_enum,
 	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT45_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if1_67_adc_enum,
 	RT5659_TDM_CTRL_2, RT5659_DS_ADC_SLOT67_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_dac_enum,
 	RT5659_DIG_INF23_DATA, RT5659_IF2_DAC_SEL_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if2_adc_enum,
 	RT5659_DIG_INF23_DATA, RT5659_IF2_ADC_SEL_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_dac_enum,
 	RT5659_DIG_INF23_DATA, RT5659_IF3_DAC_SEL_SFT, rt5659_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5659_if3_adc_enum,
 	RT5659_DIG_INF23_DATA, RT5659_IF3_ADC_SEL_SFT, rt5659_data_select);
 
 static const struct snd_kcontrol_new rt5659_if1_01_adc_swap_mux =
@@ -1207,31 +1207,31 @@
 	0, 1, 2, 3, 5, 6,
 };
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_da_sto_asrc_enum, RT5659_ASRC_2, RT5659_DA_STO_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_da_monol_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_L_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_da_monor_asrc_enum, RT5659_ASRC_2, RT5659_DA_MONO_R_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_ad_sto1_asrc_enum, RT5659_ASRC_2, RT5659_AD_STO1_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_ad_sto2_asrc_enum, RT5659_ASRC_3, RT5659_AD_STO2_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_ad_monol_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_L_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
-static const SOC_VALUE_ENUM_SINGLE_DECL(
+static SOC_VALUE_ENUM_SINGLE_DECL(
 	rt5659_ad_monor_asrc_enum, RT5659_ASRC_3, RT5659_AD_MONO_R_T_SFT, 0x7,
 	rt5659_asrc_clk_src, rt5659_asrc_clk_map_values);
 
@@ -1930,14 +1930,14 @@
 	"IF1 DAC2", "IF2 DAC", "IF3 DAC", "Mono ADC MIX"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dac_l2_enum, RT5659_DAC_CTRL,
 	RT5659_DAC_L2_SEL_SFT, rt5659_dac2_src);
 
 static const struct snd_kcontrol_new rt5659_dac_l2_mux =
 	SOC_DAPM_ENUM("DAC L2 Source", rt5659_dac_l2_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dac_r2_enum, RT5659_DAC_CTRL,
 	RT5659_DAC_R2_SEL_SFT, rt5659_dac2_src);
 
@@ -1951,7 +1951,7 @@
 	"DAC MIX", "ADC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_sto1_adc1_enum, RT5659_STO1_ADC_MIXER,
 	RT5659_STO1_ADC1_SRC_SFT, rt5659_sto1_adc1_src);
 
@@ -1964,7 +1964,7 @@
 	"ADC1", "ADC2"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_sto1_adc_enum, RT5659_STO1_ADC_MIXER,
 	RT5659_STO1_ADC_SRC_SFT, rt5659_sto1_adc_src);
 
@@ -1977,7 +1977,7 @@
 	"DAC MIX", "DMIC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_sto1_adc2_enum, RT5659_STO1_ADC_MIXER,
 	RT5659_STO1_ADC2_SRC_SFT, rt5659_sto1_adc2_src);
 
@@ -1990,7 +1990,7 @@
 	"DMIC1", "DMIC2"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_sto1_dmic_enum, RT5659_STO1_ADC_MIXER,
 	RT5659_STO1_DMIC_SRC_SFT, rt5659_sto1_dmic_src);
 
@@ -2004,7 +2004,7 @@
 	"Mono DAC MIXL", "DMIC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adc_l2_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_L2_SRC_SFT, rt5659_mono_adc_l2_src);
 
@@ -2018,7 +2018,7 @@
 	"Mono DAC MIXL", "ADC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adc_l1_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_L1_SRC_SFT, rt5659_mono_adc_l1_src);
 
@@ -2031,14 +2031,14 @@
 	"ADC1 L", "ADC1 R", "ADC2 L", "ADC2 R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adc_l_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_L_SRC_SFT, rt5659_mono_adc_src);
 
 static const struct snd_kcontrol_new rt5659_mono_adc_l_mux =
 	SOC_DAPM_ENUM("Mono ADC L Source", rt5659_mono_adc_l_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adcr_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_R_SRC_SFT, rt5659_mono_adc_src);
 
@@ -2051,7 +2051,7 @@
 	"DMIC1 L", "DMIC2 L"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_dmic_l_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_DMIC_L_SRC_SFT, rt5659_mono_dmic_l_src);
 
@@ -2064,7 +2064,7 @@
 	"Mono DAC MIXR", "DMIC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adc_r2_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_R2_SRC_SFT, rt5659_mono_adc_r2_src);
 
@@ -2077,7 +2077,7 @@
 	"Mono DAC MIXR", "ADC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_adc_r1_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_ADC_R1_SRC_SFT, rt5659_mono_adc_r1_src);
 
@@ -2090,7 +2090,7 @@
 	"DMIC1 R", "DMIC2 R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_mono_dmic_r_enum, RT5659_MONO_ADC_MIXER,
 	RT5659_MONO_DMIC_R_SRC_SFT, rt5659_mono_dmic_r_src);
 
@@ -2104,14 +2104,14 @@
 	"IF1 DAC1", "IF2 DAC", "IF3 DAC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dac_r1_enum, RT5659_AD_DA_MIXER,
 	RT5659_DAC1_R_SEL_SFT, rt5659_dac1_src);
 
 static const struct snd_kcontrol_new rt5659_dac_r1_mux =
 	SOC_DAPM_ENUM("DAC R1 Source", rt5659_dac_r1_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dac_l1_enum, RT5659_AD_DA_MIXER,
 	RT5659_DAC1_L_SEL_SFT, rt5659_dac1_src);
 
@@ -2124,14 +2124,14 @@
 	"Stereo DAC Mixer", "Mono DAC Mixer"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dig_dac_mixl_enum, RT5659_DIG_MIXER,
 	RT5659_DAC_MIX_L_SFT, rt5659_dig_dac_mix_src);
 
 static const struct snd_kcontrol_new rt5659_dig_dac_mixl_mux =
 	SOC_DAPM_ENUM("DAC Digital Mixer L Source", rt5659_dig_dac_mixl_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_dig_dac_mixr_enum, RT5659_DIG_MIXER,
 	RT5659_DAC_MIX_R_SFT, rt5659_dig_dac_mix_src);
 
@@ -2144,14 +2144,14 @@
 	"DAC", "Stereo DAC Mixer"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_alg_dac_l1_enum, RT5659_A_DAC_MUX,
 	RT5659_A_DACL1_SFT, rt5659_alg_dac1_src);
 
 static const struct snd_kcontrol_new rt5659_alg_dac_l1_mux =
 	SOC_DAPM_ENUM("Analog DACL1 Source", rt5659_alg_dac_l1_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_alg_dac_r1_enum, RT5659_A_DAC_MUX,
 	RT5659_A_DACR1_SFT, rt5659_alg_dac1_src);
 
@@ -2164,14 +2164,14 @@
 	"Stereo DAC Mixer", "Mono DAC Mixer"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_alg_dac_l2_enum, RT5659_A_DAC_MUX,
 	RT5659_A_DACL2_SFT, rt5659_alg_dac2_src);
 
 static const struct snd_kcontrol_new rt5659_alg_dac_l2_mux =
 	SOC_DAPM_ENUM("Analog DAC L2 Source", rt5659_alg_dac_l2_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_alg_dac_r2_enum, RT5659_A_DAC_MUX,
 	RT5659_A_DACR2_SFT, rt5659_alg_dac2_src);
 
@@ -2184,7 +2184,7 @@
 	"IF_ADC1", "IF_ADC2", "DAC_REF", "IF_ADC3"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_if2_adc_in_enum, RT5659_DIG_INF23_DATA,
 	RT5659_IF2_ADC_IN_SFT, rt5659_if2_adc_in_src);
 
@@ -2197,7 +2197,7 @@
 	"IF_ADC1", "IF_ADC2", "DAC_REF", "Stereo2_ADC_L/R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_if3_adc_in_enum, RT5659_DIG_INF23_DATA,
 	RT5659_IF3_ADC_IN_SFT, rt5659_if3_adc_in_src);
 
@@ -2210,14 +2210,14 @@
 	"Mono DAC", "Stereo DAC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_pdm_l_enum, RT5659_PDM_OUT_CTRL,
 	RT5659_PDM1_L_SFT, rt5659_pdm_src);
 
 static const struct snd_kcontrol_new rt5659_pdm_l_mux =
 	SOC_DAPM_ENUM("PDM L Source", rt5659_pdm_l_enum);
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_pdm_r_enum, RT5659_PDM_OUT_CTRL,
 	RT5659_PDM1_R_SFT, rt5659_pdm_src);
 
@@ -2230,7 +2230,7 @@
 	"IF1_DAC1", "IF1_DAC2", "IF2_DAC", "IF3_DAC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_spdif_enum, RT5659_SPDIF_CTRL,
 	RT5659_SPDIF_SEL_SFT, rt5659_spdif_src);
 
@@ -2250,7 +2250,7 @@
 	"NUL:AD2:DAC:AD1", "NUL:DAC:DAC:AD2", "NUL:DAC:AD2:DAC"
 };
 
-static const SOC_ENUM_SINGLE_DECL(
+static SOC_ENUM_SINGLE_DECL(
 	rt5659_rx_adc_data_enum, RT5659_TDM_CTRL_2,
 	RT5659_ADCDAT_SRC_SFT, rt5659_rx_adc_data_src);
 
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 9f0933c..e396b768 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -526,10 +526,10 @@
 	"L/R", "R/L", "L/L", "R/R"
 };
 
-static const SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
 	RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select);
 
-static const SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,
+static SOC_ENUM_SINGLE_DECL(rt5660_if1_adc_enum,
 	RT5660_DIG_INF1_DATA, RT5660_IF1_ADC_IN_SFT, rt5660_data_select);
 
 static const struct snd_kcontrol_new rt5660_if1_dac_swap_mux =
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b943dde..3bdd819 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -789,7 +789,10 @@
 
 	mutex_lock(&ctl->dsp->pwr_lock);
 
-	memcpy(ctl->cache, p, ctl->len);
+	if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+		ret = -EPERM;
+	else
+		memcpy(ctl->cache, p, ctl->len);
 
 	ctl->set = 1;
 	if (ctl->enabled && ctl->dsp->running)
@@ -816,6 +819,8 @@
 		ctl->set = 1;
 		if (ctl->enabled && ctl->dsp->running)
 			ret = wm_coeff_write_control(ctl, ctl->cache, size);
+		else if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
+			ret = -EPERM;
 	}
 
 	mutex_unlock(&ctl->dsp->pwr_lock);
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index bd19fad..c17f262 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -807,7 +807,6 @@
 static struct platform_driver snd_byt_rt5640_mc_driver = {
 	.driver = {
 		.name = "bytcr_rt5640",
-		.pm = &snd_soc_pm_ops,
 	},
 	.probe = snd_byt_rt5640_mc_probe,
 };
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index eabff3a..ae49f81 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -317,7 +317,6 @@
 static struct platform_driver snd_byt_rt5651_mc_driver = {
 	.driver = {
 		.name = "bytcr_rt5651",
-		.pm = &snd_soc_pm_ops,
 	},
 	.probe = snd_byt_rt5651_mc_probe,
 };
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 05cf809..d7013bd 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -13,7 +13,7 @@
 
 config SND_SOC_MT2701_CS42448
 	tristate "ASoc Audio driver for MT2701 with CS42448 codec"
-	depends on SND_SOC_MT2701
+	depends on SND_SOC_MT2701 && I2C
 	select SND_SOC_CS42XX8_I2C
 	select SND_SOC_BT_SCO
 	help
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index d40bfef..172af54 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -360,6 +360,10 @@
 				snd_soc_dapm_new_control_unlocked(widget->dapm,
 				&template);
 			kfree(name);
+			if (IS_ERR(data->widget)) {
+				ret = PTR_ERR(data->widget);
+				goto err_data;
+			}
 			if (!data->widget) {
 				ret = -ENOMEM;
 				goto err_data;
@@ -394,6 +398,10 @@
 			data->widget = snd_soc_dapm_new_control_unlocked(
 						widget->dapm, &template);
 			kfree(name);
+			if (IS_ERR(data->widget)) {
+				ret = PTR_ERR(data->widget);
+				goto err_data;
+			}
 			if (!data->widget) {
 				ret = -ENOMEM;
 				goto err_data;
@@ -3327,11 +3335,22 @@
 
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 	w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+	/* Do not nag about probe deferrals */
+	if (IS_ERR(w)) {
+		int ret = PTR_ERR(w);
+
+		if (ret != -EPROBE_DEFER)
+			dev_err(dapm->dev,
+				"ASoC: Failed to create DAPM control %s (%d)\n",
+				widget->name, ret);
+		goto out_unlock;
+	}
 	if (!w)
 		dev_err(dapm->dev,
 			"ASoC: Failed to create DAPM control %s\n",
 			widget->name);
 
+out_unlock:
 	mutex_unlock(&dapm->card->dapm_mutex);
 	return w;
 }
@@ -3354,6 +3373,8 @@
 		w->regulator = devm_regulator_get(dapm->dev, w->name);
 		if (IS_ERR(w->regulator)) {
 			ret = PTR_ERR(w->regulator);
+			if (ret == -EPROBE_DEFER)
+				return ERR_PTR(ret);
 			dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
 				w->name, ret);
 			return NULL;
@@ -3372,6 +3393,8 @@
 		w->clk = devm_clk_get(dapm->dev, w->name);
 		if (IS_ERR(w->clk)) {
 			ret = PTR_ERR(w->clk);
+			if (ret == -EPROBE_DEFER)
+				return ERR_PTR(ret);
 			dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
 				w->name, ret);
 			return NULL;
@@ -3490,6 +3513,16 @@
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
 		w = snd_soc_dapm_new_control_unlocked(dapm, widget);
+		if (IS_ERR(w)) {
+			ret = PTR_ERR(w);
+			/* Do not nag about probe deferrals */
+			if (ret == -EPROBE_DEFER)
+				break;
+			dev_err(dapm->dev,
+				"ASoC: Failed to create DAPM control %s (%d)\n",
+				widget->name, ret);
+			break;
+		}
 		if (!w) {
 			dev_err(dapm->dev,
 				"ASoC: Failed to create DAPM control %s\n",
@@ -3766,6 +3799,15 @@
 	dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name);
 
 	w = snd_soc_dapm_new_control_unlocked(&card->dapm, &template);
+	if (IS_ERR(w)) {
+		ret = PTR_ERR(w);
+		/* Do not nag about probe deferrals */
+		if (ret != -EPROBE_DEFER)
+			dev_err(card->dev,
+				"ASoC: Failed to create %s widget (%d)\n",
+				link_name, ret);
+		goto outfree_kcontrol_news;
+	}
 	if (!w) {
 		dev_err(card->dev, "ASoC: Failed to create %s widget\n",
 			link_name);
@@ -3817,6 +3859,16 @@
 			template.name);
 
 		w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+		if (IS_ERR(w)) {
+			int ret = PTR_ERR(w);
+
+			/* Do not nag about probe deferrals */
+			if (ret != -EPROBE_DEFER)
+				dev_err(dapm->dev,
+				"ASoC: Failed to create %s widget (%d)\n",
+				dai->driver->playback.stream_name, ret);
+			return ret;
+		}
 		if (!w) {
 			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
 				dai->driver->playback.stream_name);
@@ -3836,6 +3888,16 @@
 			template.name);
 
 		w = snd_soc_dapm_new_control_unlocked(dapm, &template);
+		if (IS_ERR(w)) {
+			int ret = PTR_ERR(w);
+
+			/* Do not nag about probe deferrals */
+			if (ret != -EPROBE_DEFER)
+				dev_err(dapm->dev,
+				"ASoC: Failed to create %s widget (%d)\n",
+				dai->driver->playback.stream_name, ret);
+			return ret;
+		}
 		if (!w) {
 			dev_err(dapm->dev, "ASoC: Failed to create %s widget\n",
 				dai->driver->capture.stream_name);
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 19d6af8..fc9c9b0 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -379,7 +379,7 @@
 	unsigned int rshift = mc->rshift;
 	int max = mc->max;
 	int min = mc->min;
-	int mask = (1 << (fls(min + max) - 1)) - 1;
+	unsigned int mask = (1 << (fls(min + max) - 1)) - 1;
 	unsigned int val;
 	int ret;
 
@@ -424,7 +424,7 @@
 	unsigned int rshift = mc->rshift;
 	int max = mc->max;
 	int min = mc->min;
-	int mask = (1 << (fls(min + max) - 1)) - 1;
+	unsigned int mask = (1 << (fls(min + max) - 1)) - 1;
 	int err = 0;
 	unsigned int val, val_mask, val2 = 0;
 
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index be6290d..bd8f34a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -212,6 +212,7 @@
 			pr_debug("%s Don't close BE\n", __func__);
 			continue;
 		}
+
 		snd_soc_dapm_stream_event(be, dir, event);
 	}
 
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 6b05047..8a758c9 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -1473,6 +1473,15 @@
 		widget = snd_soc_dapm_new_control(dapm, &template);
 	else
 		widget = snd_soc_dapm_new_control_unlocked(dapm, &template);
+	if (IS_ERR(widget)) {
+		ret = PTR_ERR(widget);
+		/* Do not nag about probe deferrals */
+		if (ret != -EPROBE_DEFER)
+			dev_err(tplg->dev,
+				"ASoC: failed to create widget %s controls (%d)\n",
+				w->name, ret);
+		goto hdr_err;
+	}
 	if (widget == NULL) {
 		dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
 			w->name);
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 88fbb3a..048de15 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -403,14 +403,6 @@
 	.name = "spdif",
 };
 
-static const struct snd_soc_dapm_widget dit_widgets[] = {
-	SND_SOC_DAPM_OUTPUT("spdif-out"),
-};
-
-static const struct snd_soc_dapm_route dit_routes[] = {
-	{ "spdif-out", NULL, "Playback" },
-};
-
 static const struct of_device_id sun4i_spdif_of_match[] = {
 	{ .compatible = "allwinner,sun4i-a10-spdif", },
 	{ .compatible = "allwinner,sun6i-a31-spdif", },
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index b871ba4..4458190 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -469,10 +469,12 @@
 
 	err = snd_usb_caiaq_send_command(cdev, EP1_CMD_GET_DEVICE_INFO, NULL, 0);
 	if (err)
-		return err;
+		goto err_kill_urb;
 
-	if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ))
-		return -ENODEV;
+	if (!wait_event_timeout(cdev->ep1_wait_queue, cdev->spec_received, HZ)) {
+		err = -ENODEV;
+		goto err_kill_urb;
+	}
 
 	usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
 		   cdev->vendor_name, CAIAQ_USB_STR_LEN);
@@ -507,6 +509,10 @@
 
 	setup_card(cdev);
 	return 0;
+
+ err_kill_urb:
+	usb_kill_urb(&cdev->ep1_in_urb);
+	return err;
 }
 
 static int snd_probe(struct usb_interface *intf,
diff --git a/sound/usb/card.c b/sound/usb/card.c
index a87a526..f029f8c 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -287,6 +287,7 @@
 	struct usb_interface_descriptor *altsd;
 	struct usb_interface *usb_iface;
 	int i, protocol;
+	int rest_bytes;
 
 	usb_iface = usb_ifnum_to_if(dev, ctrlif);
 	if (!usb_iface) {
@@ -328,12 +329,31 @@
 			return -EINVAL;
 		}
 
+		rest_bytes = (void *)(host_iface->extra + host_iface->extralen) -
+			control_header;
+
+		/* just to be sure -- this shouldn't hit at all */
+		if (rest_bytes <= 0) {
+			dev_err(&dev->dev, "invalid control header\n");
+			return -EINVAL;
+		}
+
 		h1 = control_header;
+		if (rest_bytes < sizeof(*h1)) {
+			dev_err(&dev->dev, "too short v1 buffer descriptor\n");
+			return -EINVAL;
+		}
+
 		if (!h1->bInCollection) {
 			dev_info(&dev->dev, "skipping empty audio interface (v1)\n");
 			return -EINVAL;
 		}
 
+		if (rest_bytes < h1->bLength) {
+			dev_err(&dev->dev, "invalid buffer length (v1)\n");
+			return -EINVAL;
+		}
+
 		if (h1->bLength < sizeof(*h1) + h1->bInCollection) {
 			dev_err(&dev->dev, "invalid UAC_HEADER (v1)\n");
 			return -EINVAL;
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index ab3c280..58d6249 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -775,9 +775,10 @@
 	return 0;
 
  error:
-	if (line6->disconnect)
-		line6->disconnect(line6);
-	snd_card_free(card);
+	/* we can call disconnect callback here because no close-sync is
+	 * needed yet at this point
+	 */
+	line6_disconnect(interface);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(line6_probe);
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 49cd4a6..5ab9e0c 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -307,6 +307,9 @@
 
 	line6->disconnect = podhd_disconnect;
 
+	init_timer(&pod->startup_timer);
+	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+
 	if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
 		/* create sysfs entries: */
 		err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
@@ -330,8 +333,6 @@
 	}
 
 	/* init device and delay registering */
-	init_timer(&pod->startup_timer);
-	INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
 	podhd_startup(pod);
 	return 0;
 }
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 9e7861a..3501ff9 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -216,7 +216,6 @@
 				    int index, char *buf, int maxlen)
 {
 	int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
-	buf[len] = 0;
 	return len;
 }
 
@@ -554,6 +553,8 @@
 
 	if (size < sizeof(scale))
 		return -ENOMEM;
+	if (cval->min_mute)
+		scale[0] = SNDRV_CTL_TLVT_DB_MINMAX_MUTE;
 	scale[2] = cval->dBmin;
 	scale[3] = cval->dBmax;
 	if (copy_to_user(_tlv, scale, sizeof(scale)))
@@ -2503,6 +2504,9 @@
 
 static void snd_usb_mixer_free(struct usb_mixer_interface *mixer)
 {
+	/* kill pending URBs */
+	snd_usb_mixer_disconnect(mixer);
+
 	kfree(mixer->id_elems);
 	if (mixer->urb) {
 		kfree(mixer->urb->transfer_buffer);
@@ -2936,8 +2940,13 @@
 
 void snd_usb_mixer_disconnect(struct usb_mixer_interface *mixer)
 {
-	usb_kill_urb(mixer->urb);
-	usb_kill_urb(mixer->rc_urb);
+	if (mixer->disconnected)
+		return;
+	if (mixer->urb)
+		usb_kill_urb(mixer->urb);
+	if (mixer->rc_urb)
+		usb_kill_urb(mixer->rc_urb);
+	mixer->disconnected = true;
 }
 
 #ifdef CONFIG_PM
diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h
index 3417ef3..545d99b 100644
--- a/sound/usb/mixer.h
+++ b/sound/usb/mixer.h
@@ -22,6 +22,8 @@
 	struct urb *rc_urb;
 	struct usb_ctrlrequest *rc_setup_packet;
 	u8 rc_buffer[6];
+
+	bool disconnected;
 };
 
 #define MAX_CHANNELS	16	/* max logical channels */
@@ -64,6 +66,7 @@
 	int cached;
 	int cache_val[MAX_CHANNELS];
 	u8 initialized;
+	u8 min_mute;
 	void *private_data;
 };
 
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index 04991b0..5d2fc5f 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1873,6 +1873,12 @@
 		if (unitid == 7 && cval->control == UAC_FU_VOLUME)
 			snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
 		break;
+	/* lowest playback value is muted on C-Media devices */
+	case USB_ID(0x0d8c, 0x000c):
+	case USB_ID(0x0d8c, 0x0014):
+		if (strstr(kctl->id.name, "Playback"))
+			cval->min_mute = 1;
+		break;
 	}
 }
 
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 8279009..dd81574 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -39,6 +39,8 @@
 #define SUBSTREAM_FLAG_DATA_EP_STARTED	0
 #define SUBSTREAM_FLAG_SYNC_EP_STARTED	1
 
+#define MAX_SETALT_TIMEOUT_MS 1000
+
 /* return the estimated delay based on USB frame counters */
 snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
 				    unsigned int rate)
@@ -508,7 +510,8 @@
 
 	/* close the old interface */
 	if (subs->interface >= 0 && subs->interface != fmt->iface) {
-		err = usb_set_interface(subs->dev, subs->interface, 0);
+		err = usb_set_interface_timeout(subs->dev, subs->interface, 0,
+			MAX_SETALT_TIMEOUT_MS);
 		if (err < 0) {
 			dev_err(&dev->dev,
 				"%d:%d: return to setting 0 failed (%d)\n",
@@ -527,7 +530,8 @@
 		if (err < 0)
 			return -EIO;
 
-		err = usb_set_interface(dev, fmt->iface, fmt->altsetting);
+		err = usb_set_interface_timeout(dev, fmt->iface,
+				fmt->altsetting, MAX_SETALT_TIMEOUT_MS);
 		if (err < 0) {
 			dev_err(&dev->dev,
 				"%d:%d: usb_set_interface failed (%d)\n",
@@ -574,7 +578,8 @@
 
 	if (!enable) {
 		if (subs->interface >= 0) {
-			usb_set_interface(subs->dev, subs->interface, 0);
+			usb_set_interface_timeout(subs->dev, subs->interface, 0,
+				MAX_SETALT_TIMEOUT_MS);
 			subs->altset_idx = 0;
 			subs->interface = -1;
 			subs->cur_audiofmt = NULL;
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index eb4b9f7..7613b9e 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1142,6 +1142,7 @@
 	case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
 	case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */
 	case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+	case USB_ID(0x1395, 0x740a): /* Sennheiser DECT */
 	case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
 	case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
 	case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
@@ -1308,10 +1309,13 @@
 	    && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
 		mdelay(20);
 
-	/* Zoom R16/24 needs a tiny delay here, otherwise requests like
-	 * get/set frequency return as failed despite actually succeeding.
+	/* Zoom R16/24, Logitech H650e, Jabra 550a needs a tiny delay here,
+	 * otherwise requests like get/set frequency return as failed despite
+	 * actually succeeding.
 	 */
-	if (chip->usb_id == USB_ID(0x1686, 0x00dd) &&
+	if ((chip->usb_id == USB_ID(0x1686, 0x00dd) ||
+	     chip->usb_id == USB_ID(0x046d, 0x0a46) ||
+	     chip->usb_id == USB_ID(0x0b0e, 0x0349)) &&
 	    (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
 		mdelay(1);
 }
@@ -1348,6 +1352,7 @@
 	case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
 	case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
 	case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
+	case USB_ID(0x2772, 0x0230): /* Pro-Ject Pre Box S2 Digital */
 		if (fp->altsetting == 2)
 			return SNDRV_PCM_FMTBIT_DSD_U32_BE;
 		break;
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index 801508c..0aeabfe 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -26,6 +26,7 @@
 #include <linux/qmi_encdec.h>
 #include <soc/qcom/msm_qmi_interface.h>
 #include <linux/iommu.h>
+#include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
 #include <linux/usb/audio-v3.h>
 
@@ -45,8 +46,7 @@
 /*  event ring iova base address */
 #define IOVA_BASE 0x1000
 
-#define IOVA_DCBA_BASE 0x2000
-#define IOVA_XFER_RING_BASE (IOVA_DCBA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
+#define IOVA_XFER_RING_BASE (IOVA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
 #define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
 #define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
 #define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
@@ -81,10 +81,9 @@
 	/* audio control interface */
 	struct usb_host_interface *ctrl_intf;
 	unsigned int card_num;
+	unsigned int usb_core_id;
 	atomic_t in_use;
 	struct kref kref;
-	unsigned long dcba_iova;
-	size_t dcba_size;
 	wait_queue_head_t disconnect_wq;
 
 	/* interface specific */
@@ -101,9 +100,6 @@
 	struct iommu_domain *domain;
 
 	/* list to keep track of available iova */
-	struct list_head dcba_list;
-	size_t dcba_iova_size;
-	unsigned long curr_dcba_iova;
 	struct list_head xfer_ring_list;
 	size_t xfer_ring_iova_size;
 	unsigned long curr_xfer_ring_iova;
@@ -150,7 +146,6 @@
 
 enum mem_type {
 	MEM_EVENT_RING,
-	MEM_DCBA,
 	MEM_XFER_RING,
 	MEM_XFER_BUF,
 };
@@ -176,6 +171,26 @@
 	USB_QMI_PCM_FORMAT_U32_BE,
 };
 
+static enum usb_audio_device_speed_enum_v01
+get_speed_info(enum usb_device_speed udev_speed)
+{
+	switch (udev_speed) {
+	case USB_SPEED_LOW:
+		return USB_AUDIO_DEVICE_SPEED_LOW_V01;
+	case USB_SPEED_FULL:
+		return USB_AUDIO_DEVICE_SPEED_FULL_V01;
+	case USB_SPEED_HIGH:
+		return USB_AUDIO_DEVICE_SPEED_HIGH_V01;
+	case USB_SPEED_SUPER:
+		return USB_AUDIO_DEVICE_SPEED_SUPER_V01;
+	case USB_SPEED_SUPER_PLUS:
+		return USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01;
+	default:
+		pr_err("%s: udev speed %d\n", __func__, udev_speed);
+		return USB_AUDIO_DEVICE_SPEED_INVALID_V01;
+	}
+}
+
 static unsigned long uaudio_get_iova(unsigned long *curr_iova,
 	size_t *curr_iova_size, struct list_head *head, size_t size)
 {
@@ -275,10 +290,6 @@
 		if (uaudio_qdev->er_phys_addr == pa)
 			map = false;
 		break;
-	case MEM_DCBA:
-		va = uaudio_get_iova(&uaudio_qdev->curr_dcba_iova,
-		&uaudio_qdev->dcba_iova_size, &uaudio_qdev->dcba_list, size);
-		break;
 	case MEM_XFER_RING:
 		va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
 		&uaudio_qdev->xfer_ring_iova_size, &uaudio_qdev->xfer_ring_list,
@@ -363,10 +374,7 @@
 		else
 			unmap = false;
 		break;
-	case MEM_DCBA:
-		uaudio_put_iova(va, size, &uaudio_qdev->dcba_list,
-		&uaudio_qdev->dcba_iova_size);
-		break;
+
 	case MEM_XFER_RING:
 		uaudio_put_iova(va, size, &uaudio_qdev->xfer_ring_list,
 		&uaudio_qdev->xfer_ring_iova_size);
@@ -407,10 +415,12 @@
 	int protocol, card_num, pcm_dev_num;
 	void *hdr_ptr;
 	u8 *xfer_buf;
-	u32 len, mult, remainder, xfer_buf_len;
-	unsigned long va, tr_data_va = 0, tr_sync_va = 0, dcba_va = 0,
-	xfer_buf_va = 0;
-	phys_addr_t xhci_pa, xfer_buf_pa;
+	u32 len, mult, remainder, xfer_buf_len, sg_len, i, total_len = 0;
+	unsigned long va, va_sg, tr_data_va = 0, tr_sync_va = 0;
+	phys_addr_t xhci_pa, xfer_buf_pa, tr_data_pa = 0, tr_sync_pa = 0;
+	dma_addr_t dma;
+	struct sg_table sgt;
+	struct scatterlist *sg;
 
 	iface = usb_ifnum_to_if(subs->dev, subs->interface);
 	if (!iface) {
@@ -524,13 +534,13 @@
 	memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
 	resp->std_as_data_ep_desc_valid = 1;
 
-	xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
-	if (!xhci_pa) {
+	tr_data_pa = usb_get_xfer_ring_phys_addr(subs->dev, ep, &dma);
+	if (!tr_data_pa) {
 		pr_err("%s:failed to get data ep ring dma address\n", __func__);
 		goto err;
 	}
 
-	resp->xhci_mem_info.tr_data.pa = xhci_pa;
+	resp->xhci_mem_info.tr_data.pa = dma;
 
 	if (subs->sync_endpoint) {
 		ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe);
@@ -541,19 +551,26 @@
 		memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
 		resp->std_as_sync_ep_desc_valid = 1;
 
-		xhci_pa = usb_get_xfer_ring_dma_addr(subs->dev, ep);
-		if (!xhci_pa) {
+		tr_sync_pa = usb_get_xfer_ring_phys_addr(subs->dev, ep, &dma);
+		if (!tr_sync_pa) {
 			pr_err("%s:failed to get sync ep ring dma address\n",
 				__func__);
 			goto err;
 		}
-		resp->xhci_mem_info.tr_sync.pa = xhci_pa;
+		resp->xhci_mem_info.tr_sync.pa = dma;
 	}
 
 skip_sync_ep:
 	resp->interrupter_num = uaudio_qdev->intr_num;
 	resp->interrupter_num_valid = 1;
 
+	ret = usb_get_controller_id(subs->dev);
+	if (ret < 0)
+		goto err;
+
+	resp->controller_num =  ret;
+	resp->controller_num_valid = 1;
+
 	/*  map xhci data structures PA memory to iova */
 
 	/* event ring */
@@ -563,8 +580,8 @@
 			ret);
 		goto err;
 	}
-	xhci_pa = usb_get_sec_event_ring_dma_addr(subs->dev,
-			resp->interrupter_num);
+	xhci_pa = usb_get_sec_event_ring_phys_addr(subs->dev,
+			resp->interrupter_num, &dma);
 	if (!xhci_pa) {
 		pr_err("%s: failed to get sec event ring dma address\n",
 		__func__);
@@ -577,37 +594,20 @@
 
 	resp->xhci_mem_info.evt_ring.va = PREPEND_SID_TO_IOVA(va,
 						uaudio_qdev->sid);
-	resp->xhci_mem_info.evt_ring.pa = xhci_pa;
+	resp->xhci_mem_info.evt_ring.pa = dma;
 	resp->xhci_mem_info.evt_ring.size = PAGE_SIZE;
 	uaudio_qdev->er_phys_addr = xhci_pa;
 
-	/* dcba */
-	xhci_pa = usb_get_dcba_dma_addr(subs->dev);
-	if (!xhci_pa) {
-		pr_err("%s:failed to get dcba dma address\n", __func__);
+	resp->speed_info = get_speed_info(subs->dev->speed);
+	if (resp->speed_info == USB_AUDIO_DEVICE_SPEED_INVALID_V01)
 		goto unmap_er;
-	}
 
-	if (!uadev[card_num].dcba_iova) { /* mappped per usb device */
-		va = uaudio_iommu_map(MEM_DCBA, xhci_pa, PAGE_SIZE);
-		if (!va)
-			goto unmap_er;
-
-		uadev[card_num].dcba_iova = va;
-		uadev[card_num].dcba_size = PAGE_SIZE;
-	}
-
-	dcba_va = uadev[card_num].dcba_iova;
-	resp->xhci_mem_info.dcba.va = PREPEND_SID_TO_IOVA(dcba_va,
-						uaudio_qdev->sid);
-	resp->xhci_mem_info.dcba.pa = xhci_pa;
-	resp->xhci_mem_info.dcba.size = PAGE_SIZE;
+	resp->speed_info_valid = 1;
 
 	/* data transfer ring */
-	xhci_pa = resp->xhci_mem_info.tr_data.pa;
-	va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE);
+	va = uaudio_iommu_map(MEM_XFER_RING, tr_data_pa, PAGE_SIZE);
 	if (!va)
-		goto unmap_dcba;
+		goto unmap_er;
 
 	tr_data_va = va;
 	resp->xhci_mem_info.tr_data.va = PREPEND_SID_TO_IOVA(va,
@@ -619,7 +619,7 @@
 		goto skip_sync;
 
 	xhci_pa = resp->xhci_mem_info.tr_sync.pa;
-	va = uaudio_iommu_map(MEM_XFER_RING, xhci_pa, PAGE_SIZE);
+	va = uaudio_iommu_map(MEM_XFER_RING, tr_sync_pa, PAGE_SIZE);
 	if (!va)
 		goto unmap_data;
 
@@ -648,19 +648,33 @@
 	if (!xfer_buf)
 		goto unmap_sync;
 
+	dma_get_sgtable(subs->dev->bus->sysdev, &sgt, xfer_buf, xfer_buf_pa,
+			len);
+
+	va = 0;
+	for_each_sg(sgt.sgl, sg, sgt.nents, i) {
+		sg_len = PAGE_ALIGN(sg->offset + sg->length);
+		va_sg = uaudio_iommu_map(MEM_XFER_BUF,
+			page_to_phys(sg_page(sg)), sg_len);
+		if (!va_sg)
+			goto unmap_xfer_buf;
+
+		if (!va)
+			va = va_sg;
+
+		total_len += sg_len;
+	}
+
 	resp->xhci_mem_info.xfer_buff.pa = xfer_buf_pa;
 	resp->xhci_mem_info.xfer_buff.size = len;
 
-	va = uaudio_iommu_map(MEM_XFER_BUF, xfer_buf_pa, len);
-	if (!va)
-		goto unmap_sync;
-
-	xfer_buf_va = va;
 	resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va,
 						uaudio_qdev->sid);
 
 	resp->xhci_mem_info_valid = 1;
 
+	sg_free_table(&sgt);
+
 	if (!atomic_read(&uadev[card_num].in_use)) {
 		kref_init(&uadev[card_num].kref);
 		init_waitqueue_head(&uadev[card_num].disconnect_wq);
@@ -680,13 +694,14 @@
 	}
 
 	uadev[card_num].card_num = card_num;
+	uadev[card_num].usb_core_id = resp->controller_num;
 
 	/* cache intf specific info to use it for unmap and free xfer buf */
 	uadev[card_num].info[info_idx].data_xfer_ring_va = tr_data_va;
 	uadev[card_num].info[info_idx].data_xfer_ring_size = PAGE_SIZE;
 	uadev[card_num].info[info_idx].sync_xfer_ring_va = tr_sync_va;
 	uadev[card_num].info[info_idx].sync_xfer_ring_size = PAGE_SIZE;
-	uadev[card_num].info[info_idx].xfer_buf_va = xfer_buf_va;
+	uadev[card_num].info[info_idx].xfer_buf_va = va;
 	uadev[card_num].info[info_idx].xfer_buf_pa = xfer_buf_pa;
 	uadev[card_num].info[info_idx].xfer_buf_size = len;
 	uadev[card_num].info[info_idx].xfer_buf = xfer_buf;
@@ -701,14 +716,13 @@
 	return 0;
 
 unmap_xfer_buf:
-	uaudio_iommu_unmap(MEM_XFER_BUF, xfer_buf_va, len);
+	if (va)
+		uaudio_iommu_unmap(MEM_XFER_BUF, va, total_len);
 unmap_sync:
 	usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
 	uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE);
 unmap_data:
 	uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE);
-unmap_dcba:
-	uaudio_iommu_unmap(MEM_DCBA, dcba_va, PAGE_SIZE);
 unmap_er:
 	uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE);
 err:
@@ -754,11 +768,6 @@
 			dev->info[if_idx].intf_num, dev->card_num);
 	}
 
-	/* iommu_unmap dcba iova for a usb device */
-	uaudio_iommu_unmap(MEM_DCBA, dev->dcba_iova, dev->dcba_size);
-
-	dev->dcba_iova = 0;
-	dev->dcba_size = 0;
 	dev->num_intf = 0;
 
 	/* free interface info */
@@ -807,6 +816,8 @@
 		pr_debug("%s: sending qmi indication disconnect\n", __func__);
 		disconnect_ind.dev_event = USB_AUDIO_DEV_DISCONNECT_V01;
 		disconnect_ind.slot_id = dev->udev->slot_id;
+		disconnect_ind.controller_num = dev->usb_core_id;
+		disconnect_ind.controller_num_valid = 1;
 		ret = qmi_send_ind(svc->uaudio_svc_hdl, svc->curr_conn,
 				&uaudio_stream_ind_desc, &disconnect_ind,
 				sizeof(disconnect_ind));
@@ -1228,11 +1239,7 @@
 		goto free_domain;
 	}
 
-	/* initialize dcba, xfer ring and xfer buf iova list */
-	INIT_LIST_HEAD(&uaudio_qdev->dcba_list);
-	uaudio_qdev->curr_dcba_iova = IOVA_DCBA_BASE;
-	uaudio_qdev->dcba_iova_size = SNDRV_CARDS * PAGE_SIZE;
-
+	/* initialize xfer ring and xfer buf iova list */
 	INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
 	uaudio_qdev->curr_xfer_ring_iova = IOVA_XFER_RING_BASE;
 	uaudio_qdev->xfer_ring_iova_size =
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
index fef7505..4fa8445 100644
--- a/sound/usb/usb_audio_qmi_v01.c
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -633,6 +633,46 @@
 					interrupter_num),
 	},
 	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x1C,
+		.offset         = offsetof(
+					struct qmi_uaudio_stream_resp_msg_v01,
+					speed_info_valid),
+	},
+	{
+		.data_type      = QMI_SIGNED_4_BYTE_ENUM,
+		.elem_len       = 1,
+		.elem_size      = sizeof(enum usb_audio_device_speed_enum_v01),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x1C,
+		.offset         = offsetof(
+					struct qmi_uaudio_stream_resp_msg_v01,
+					speed_info),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x1D,
+		.offset         = offsetof(
+					struct qmi_uaudio_stream_resp_msg_v01,
+					controller_num_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x1D,
+		.offset         = offsetof(
+					struct qmi_uaudio_stream_resp_msg_v01,
+					controller_num),
+	},
+	{
 		.data_type      = QMI_EOTI,
 		.is_array       = NO_ARRAY,
 		.is_array       = QMI_COMMON_TLV_TYPE,
@@ -826,6 +866,24 @@
 					   interrupter_num),
 	},
 	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x19,
+		.offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+					   controller_num_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x19,
+		.offset         = offsetof(struct qmi_uaudio_stream_ind_msg_v01,
+					   controller_num),
+	},
+	{
 		.data_type      = QMI_EOTI,
 		.is_array       = NO_ARRAY,
 		.is_array       = QMI_COMMON_TLV_TYPE,
diff --git a/sound/usb/usb_audio_qmi_v01.h b/sound/usb/usb_audio_qmi_v01.h
index 83a966c..addc0ed 100644
--- a/sound/usb/usb_audio_qmi_v01.h
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -77,6 +77,17 @@
 	USB_AUDIO_DEVICE_INDICATION_ENUM_MAX_VAL_V01 = INT_MAX,
 };
 
+enum usb_audio_device_speed_enum_v01 {
+	USB_AUDIO_DEVICE_SPEED_ENUM_MIN_VAL_V01 = INT_MIN,
+	USB_AUDIO_DEVICE_SPEED_INVALID_V01 = 0,
+	USB_AUDIO_DEVICE_SPEED_LOW_V01 = 1,
+	USB_AUDIO_DEVICE_SPEED_FULL_V01 = 2,
+	USB_AUDIO_DEVICE_SPEED_HIGH_V01 = 3,
+	USB_AUDIO_DEVICE_SPEED_SUPER_V01 = 4,
+	USB_AUDIO_DEVICE_SPEED_SUPER_PLUS_V01 = 5,
+	USB_AUDIO_DEVICE_SPEED_ENUM_MAX_VAL_V01 = INT_MAX,
+};
+
 struct qmi_uaudio_stream_req_msg_v01 {
 	uint8_t enable;
 	uint32_t usb_token;
@@ -118,8 +129,12 @@
 	struct apps_mem_info_v01 xhci_mem_info;
 	uint8_t interrupter_num_valid;
 	uint8_t interrupter_num;
+	uint8_t speed_info_valid;
+	enum usb_audio_device_speed_enum_v01 speed_info;
+	uint8_t controller_num_valid;
+	uint8_t controller_num;
 };
-#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 191
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 202
 extern struct elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
 
 struct qmi_uaudio_stream_ind_msg_v01 {
@@ -143,8 +158,10 @@
 	struct apps_mem_info_v01 xhci_mem_info;
 	uint8_t interrupter_num_valid;
 	uint8_t interrupter_num;
+	uint8_t controller_num_valid;
+	uint8_t controller_num;
 };
-#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 177
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
 extern struct elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
 
 #endif
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index bf618e1..e7b934f 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -191,7 +191,8 @@
 	}
 
 	pg = get_order(read_size);
-	sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+	sk->s = (void *) __get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
+					  __GFP_NOWARN, pg);
 	if (!sk->s) {
 		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
 		goto out;
@@ -211,7 +212,8 @@
 	pg = get_order(write_size);
 
 	sk->write_page =
-		(void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO, pg);
+		(void *)__get_free_pages(GFP_KERNEL|__GFP_COMP|__GFP_ZERO|
+					 __GFP_NOWARN, pg);
 	if (!sk->write_page) {
 		snd_printk(KERN_WARNING "couldn't __get_free_pages()\n");
 		usb_stream_free(sk);
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4e778ea..415a9c3 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -309,10 +309,11 @@
 
 	event_attr_init(attr);
 
-	evsel = perf_evsel__new_idx(attr, (*idx)++);
+	evsel = perf_evsel__new_idx(attr, *idx);
 	if (!evsel)
 		return NULL;
 
+	(*idx)++;
 	evsel->cpus     = cpu_map__get(cpus);
 	evsel->own_cpus = cpu_map__get(cpus);
 
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 6c50d9f..6a6f44d 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -163,7 +163,7 @@
 
 	/* A file path -- this is an offline module */
 	if (module && strchr(module, '/'))
-		return machine__findnew_module_map(host_machine, 0, module);
+		return dso__new_map(module);
 
 	if (!module)
 		module = "kernel";
@@ -173,6 +173,7 @@
 		if (strncmp(pos->dso->short_name + 1, module,
 			    pos->dso->short_name_len - 2) == 0 &&
 		    module[pos->dso->short_name_len - 2] == '\0') {
+			map__get(pos);
 			return pos;
 		}
 	}
@@ -188,15 +189,6 @@
 		return kernel_get_module_map(target);
 }
 
-static void put_target_map(struct map *map, bool user)
-{
-	if (map && user) {
-		/* Only the user map needs to be released */
-		map__put(map);
-	}
-}
-
-
 static int convert_exec_to_group(const char *exec, char **result)
 {
 	char *ptr1, *ptr2, *exec_copy;
@@ -412,7 +404,7 @@
 	}
 
 out:
-	put_target_map(map, uprobes);
+	map__put(map);
 	return ret;
 
 }
@@ -2944,7 +2936,7 @@
 	}
 
 out:
-	put_target_map(map, pev->uprobes);
+	map__put(map);
 	free(syms);
 	return ret;
 
@@ -3437,10 +3429,7 @@
 		return ret;
 
 	/* Get a symbol map */
-	if (user)
-		map = dso__new_map(target);
-	else
-		map = kernel_get_module_map(target);
+	map = get_target_map(target, user);
 	if (!map) {
 		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
 		return -EINVAL;
@@ -3472,9 +3461,7 @@
         }
 
 end:
-	if (user) {
-		map__put(map);
-	}
+	map__put(map);
 	exit_probe_symbol_maps();
 
 	return ret;
diff --git a/tools/power/x86/turbostat/turbostat.c b/tools/power/x86/turbostat/turbostat.c
index 3e199b5..9664b1f 100644
--- a/tools/power/x86/turbostat/turbostat.c
+++ b/tools/power/x86/turbostat/turbostat.c
@@ -2003,8 +2003,10 @@
 
 	if (fp == NULL)
 		fp = fopen_or_die("/sys/class/graphics/fb0/device/drm/card0/gt_cur_freq_mhz", "r");
-	else
+	else {
 		rewind(fp);
+		fflush(fp);
+	}
 
 	retval = fscanf(fp, "%d", &gfx_cur_mhz);
 	if (retval != 1)
diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh
index 5c495ad..d8ac9ba 100755
--- a/tools/testing/selftests/firmware/fw_filesystem.sh
+++ b/tools/testing/selftests/firmware/fw_filesystem.sh
@@ -48,18 +48,18 @@
 
 NAME=$(basename "$FW")
 
-if printf '\000' >"$DIR"/trigger_request; then
+if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then
 	echo "$0: empty filename should not succeed" >&2
 	exit 1
 fi
 
-if printf '\000' >"$DIR"/trigger_async_request; then
+if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then
 	echo "$0: empty filename should not succeed (async)" >&2
 	exit 1
 fi
 
 # Request a firmware that doesn't exist, it should fail.
-if echo -n "nope-$NAME" >"$DIR"/trigger_request; then
+if echo -n "nope-$NAME" >"$DIR"/trigger_request 2> /dev/null; then
 	echo "$0: firmware shouldn't have loaded" >&2
 	exit 1
 fi
diff --git a/tools/testing/selftests/firmware/fw_userhelper.sh b/tools/testing/selftests/firmware/fw_userhelper.sh
index b9983f8..01c626a 100755
--- a/tools/testing/selftests/firmware/fw_userhelper.sh
+++ b/tools/testing/selftests/firmware/fw_userhelper.sh
@@ -64,9 +64,33 @@
 echo "ABCD0123" >"$FW"
 NAME=$(basename "$FW")
 
+DEVPATH="$DIR"/"nope-$NAME"/loading
+
 # Test failure when doing nothing (timeout works).
-echo 1 >/sys/class/firmware/timeout
-echo -n "$NAME" >"$DIR"/trigger_request
+echo -n 2 >/sys/class/firmware/timeout
+echo -n "nope-$NAME" >"$DIR"/trigger_request 2>/dev/null &
+
+# Give the kernel some time to load the loading file, must be less
+# than the timeout above.
+sleep 1
+if [ ! -f $DEVPATH ]; then
+	echo "$0: fallback mechanism immediately cancelled"
+	echo ""
+	echo "The file never appeared: $DEVPATH"
+	echo ""
+	echo "This might be a distribution udev rule setup by your distribution"
+	echo "to immediately cancel all fallback requests, this must be"
+	echo "removed before running these tests. To confirm look for"
+	echo "a firmware rule like /lib/udev/rules.d/50-firmware.rules"
+	echo "and see if you have something like this:"
+	echo ""
+	echo "SUBSYSTEM==\"firmware\", ACTION==\"add\", ATTR{loading}=\"-1\""
+	echo ""
+	echo "If you do remove this file or comment out this line before"
+	echo "proceeding with these tests."
+	exit 1
+fi
+
 if diff -q "$FW" /dev/test_firmware >/dev/null ; then
 	echo "$0: firmware was not expected to match" >&2
 	exit 1
diff --git a/tools/testing/selftests/ntb/ntb_test.sh b/tools/testing/selftests/ntb/ntb_test.sh
index a676d3e..b3c48fc 100755
--- a/tools/testing/selftests/ntb/ntb_test.sh
+++ b/tools/testing/selftests/ntb/ntb_test.sh
@@ -305,7 +305,7 @@
 	echo "Running remote perf test $WITH DMA"
 	write_file "" $REMOTE_PERF/run
 	echo -n "  "
-	read_file $LOCAL_PERF/run
+	read_file $REMOTE_PERF/run
 	echo "  Passed"
 
 	_modprobe -r ntb_perf
@@ -326,6 +326,10 @@
 	link_test $LOCAL_TOOL $REMOTE_TOOL
 	link_test $REMOTE_TOOL $LOCAL_TOOL
 
+	#Ensure the link is up on both sides before continuing
+	write_file Y $LOCAL_TOOL/link_event
+	write_file Y $REMOTE_TOOL/link_event
+
 	for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do
 		PT=$(basename $PEER_TRANS)
 		write_file $MW_SIZE $LOCAL_TOOL/$PT
diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c
index 03f1fa4..cbb0564 100644
--- a/tools/testing/selftests/seccomp/seccomp_bpf.c
+++ b/tools/testing/selftests/seccomp/seccomp_bpf.c
@@ -6,10 +6,18 @@
  */
 
 #include <sys/types.h>
-#include <asm/siginfo.h>
-#define __have_siginfo_t 1
-#define __have_sigval_t 1
-#define __have_sigevent_t 1
+
+/*
+ * glibc 2.26 and later have SIGSYS in siginfo_t. Before that,
+ * we need to use the kernel's siginfo.h file and trick glibc
+ * into accepting it.
+ */
+#if !__GLIBC_PREREQ(2, 26)
+# include <asm/siginfo.h>
+# define __have_siginfo_t 1
+# define __have_sigval_t 1
+# define __have_sigevent_t 1
+#endif
 
 #include <errno.h>
 #include <linux/filter.h>
@@ -676,7 +684,7 @@
 	syscall(__NR_getpid);
 }
 
-static struct siginfo TRAP_info;
+static siginfo_t TRAP_info;
 static volatile int TRAP_nr;
 static void TRAP_action(int nr, siginfo_t *info, void *void_context)
 {
diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c
index 5b2b4b3..9b4610c 100644
--- a/tools/testing/selftests/x86/fsgsbase.c
+++ b/tools/testing/selftests/x86/fsgsbase.c
@@ -285,9 +285,12 @@
 	}
 }
 
-static void set_gs_and_switch_to(unsigned long local, unsigned long remote)
+static void set_gs_and_switch_to(unsigned long local,
+				 unsigned short force_sel,
+				 unsigned long remote)
 {
 	unsigned long base;
+	unsigned short sel_pre_sched, sel_post_sched;
 
 	bool hard_zero = false;
 	if (local == HARD_ZERO) {
@@ -297,6 +300,8 @@
 
 	printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n",
 	       local, hard_zero ? " and clear gs" : "", remote);
+	if (force_sel)
+		printf("\tBefore schedule, set selector to 0x%hx\n", force_sel);
 	if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0)
 		err(1, "ARCH_SET_GS");
 	if (hard_zero)
@@ -307,18 +312,35 @@
 		printf("[FAIL]\tGSBASE wasn't set as expected\n");
 	}
 
+	if (force_sel) {
+		asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+		sel_pre_sched = force_sel;
+		local = read_base(GS);
+
+		/*
+		 * Signal delivery seems to mess up weird selectors.  Put it
+		 * back.
+		 */
+		asm volatile ("mov %0, %%gs" : : "rm" (force_sel));
+	} else {
+		asm volatile ("mov %%gs, %0" : "=rm" (sel_pre_sched));
+	}
+
 	remote_base = remote;
 	ftx = 1;
 	syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
 	while (ftx != 0)
 		syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
 
+	asm volatile ("mov %%gs, %0" : "=rm" (sel_post_sched));
 	base = read_base(GS);
-	if (base == local) {
-		printf("[OK]\tGSBASE remained 0x%lx\n", local);
+	if (base == local && sel_pre_sched == sel_post_sched) {
+		printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n",
+		       sel_pre_sched, local);
 	} else {
 		nerrs++;
-		printf("[FAIL]\tGSBASE changed to 0x%lx\n", base);
+		printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n",
+		       sel_pre_sched, local, sel_post_sched, base);
 	}
 }
 
@@ -381,8 +403,15 @@
 
 	for (int local = 0; local < 4; local++) {
 		for (int remote = 0; remote < 4; remote++) {
-			set_gs_and_switch_to(bases_with_hard_zero[local],
-					     bases_with_hard_zero[remote]);
+			for (unsigned short s = 0; s < 5; s++) {
+				unsigned short sel = s;
+				if (s == 4)
+					asm ("mov %%ss, %0" : "=rm" (sel));
+				set_gs_and_switch_to(
+					bases_with_hard_zero[local],
+					sel,
+					bases_with_hard_zero[remote]);
+			}
 		}
 	}