Merge "ARM: dts: msm: Add retention support for qsmmuv500 for sdm670"
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/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/msm.txt b/Documentation/devicetree/bindings/arm/msm/msm.txt
index 765b5e4..327a7d4 100644
--- a/Documentation/devicetree/bindings/arm/msm/msm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/msm.txt
@@ -166,6 +166,8 @@
 - VR device:
   compatible = "qcom,qvr"
 
+- HDK device:
+  compatible = "qcom,hdk"
 
 Boards (SoC type + board variant):
 
@@ -279,9 +281,11 @@
 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"
@@ -321,3 +325,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_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,osm.txt b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
index cc4c3cc..bce983a 100644
--- a/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
+++ b/Documentation/devicetree/bindings/arm/msm/qcom,osm.txt
@@ -9,397 +9,27 @@
 - 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".
 		    Must be specified in the same order as the corresponding
 		    addresses are specified in the reg property.
 
-- vdd-l3-supply
-	Usage:      required
+- l3-devs
+	Usage: optional
 	Value type: <phandle>
-	Definition: phandle of the underlying regulator device that manages
-		    the voltage supply of the L3 cluster.
-
-- vdd-pwrcl-supply
-	Usage:      required
-	Value type: <phandle>
-	Definition: phandle of the underlying regulator device that manages
-		    the voltage supply of the Power cluster.
-
-- vdd-perfcl-supply
-	Usage:      required
-	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
@@ -416,156 +46,12 @@
 		compatible = "qcom,clk-cpu-osm";
 		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>;
-		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";
+			<0x17d45800 0x1400>;
+		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
 
-		/* 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;
+		l3-devs = <&phandle0 &phandle1 &phandle2>;
 
 		clock-names = "xo_ao";
 		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
 		#clock-cells = <1>;
-		#reset-cells = <1>;
 	};
diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc.txt b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
index 538fb6d..78bb87a 100644
--- a/Documentation/devicetree/bindings/clock/qcom,gcc.txt
+++ b/Documentation/devicetree/bindings/clock/qcom,gcc.txt
@@ -18,6 +18,7 @@
 			"qcom,gcc-mdm9615"
 			"qcom,gcc-sdm845"
 			"qcom,gcc-sdm845-v2"
+			"qcom,gcc-sdm845-v2.1"
 			"qcom,gcc-sdm670"
 			"qcom,debugcc-sdm845"
 
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/devfreq/arm-memlat-mon.txt b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
index 67dc991..6f2fac7 100644
--- a/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
+++ b/Documentation/devicetree/bindings/devfreq/arm-memlat-mon.txt
@@ -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/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 2782428..22b4e91 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -331,9 +331,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.
@@ -367,6 +380,14 @@
 				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
@@ -452,8 +473,12 @@
     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>;
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
index 62ba54b..d99b214 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,7 @@
 		    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.
 
 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 d2e635a..2a7e161 100644
--- a/Documentation/devicetree/bindings/iommu/arm,smmu.txt
+++ b/Documentation/devicetree/bindings/iommu/arm,smmu.txt
@@ -104,6 +104,11 @@
 		  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-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/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
index d3098be..6222881 100644
--- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
+++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt
@@ -86,7 +86,11 @@
 	  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/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 85b0fe9..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
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
index d4bf1ce..4b483e5 100644
--- a/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,msm8953-pinctrl.txt
@@ -1,4 +1,4 @@
-Qualcomm MSM8953 TLMM block
+Qualcomm Technologies, Inc. MSM8953 TLMM block
 
 This binding describes the Top Level Mode Multiplexer block found in the
 MSM8953 platform.
diff --git a/Documentation/devicetree/bindings/platform/msm/ipa.txt b/Documentation/devicetree/bindings/platform/msm/ipa.txt
index e821feb..aede546 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"
@@ -122,6 +121,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 d205b0b..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
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-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/regulator/cpr4-apss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
index 29bb2d3..54ab182 100644
--- a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
@@ -414,6 +414,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 +519,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/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/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 45e309c..bf2a91a 100644
--- a/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
+++ b/Documentation/devicetree/bindings/soc/qcom/qcom,msm-eud.txt
@@ -14,6 +14,9 @@
    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
@@ -29,6 +32,7 @@
 		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/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/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
index c3e2cab..7f79f40 100644
--- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
+++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt
@@ -73,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/msm-phy.txt b/Documentation/devicetree/bindings/usb/msm-phy.txt
index d23cb46..6109fad 100644
--- a/Documentation/devicetree/bindings/usb/msm-phy.txt
+++ b/Documentation/devicetree/bindings/usb/msm-phy.txt
@@ -159,7 +159,6 @@
    "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.
-   "eud_base" : EUD device register address space to use EUD pet functionality.
  - 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"
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 3bdc8967..a491bd7 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.
diff --git a/Makefile b/Makefile
index 665104d..dd43551 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 4
 PATCHLEVEL = 9
-SUBLEVEL = 51
+SUBLEVEL = 58
 EXTRAVERSION =
 NAME = Roaring Lionus
 
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/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..63da745 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))
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/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/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..3826bad 100644
--- a/arch/arm/boot/dts/qcom/Makefile
+++ b/arch/arm/boot/dts/qcom/Makefile
@@ -1,5 +1,7 @@
 
-dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb
+dtb-$(CONFIG_ARCH_SDXPOORWILLS) += sdxpoorwills-rumi.dtb \
+	sdxpoorwills-cdp.dtb \
+	sdxpoorwills-mtp.dtb
 
 
 ifeq ($(CONFIG_ARM64),y)
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..e3f154b 100644
--- a/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/pmxpoorwills.dtsi
@@ -51,6 +51,40 @@
 				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>;
+			};
+		};
 	};
 
 	qcom,pmxpoorwills@1 {
@@ -58,5 +92,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/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/arm/boot/dts/qcom/sdxpoorwills-cdp.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
new file mode 100644
index 0000000..8b5eee0
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-cdp.dts
@@ -0,0 +1,34 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+
+#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";
+};
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..0f84228
--- /dev/null
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-mtp.dts
@@ -0,0 +1,34 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+
+
+#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";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
index ac02429..8181fa8 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-pinctrl.dtsi
@@ -31,5 +31,893 @@
 				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;
+				};
+			};
+		};
 	};
 };
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
index b3103cd..3aacd63 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills-rumi.dts
@@ -68,3 +68,7 @@
 &usb3_qmp_phy {
 	status = "disabled";
 };
+
+&qnand_1 {
+	status = "ok";
+};
diff --git a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
index d538efe..7875f19 100644
--- a/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
+++ b/arch/arm/boot/dts/qcom/sdxpoorwills.dtsi
@@ -54,6 +54,10 @@
 		};
 	};
 
+	aliases {
+		qpic_nand1 = &qnand_1;
+	};
+
 	soc: soc { };
 };
 
@@ -194,6 +198,30 @@
 		status = "ok";
 	};
 
+	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 {
 		compatible = "qcom,msm-imem";
 		reg = <0x8600000 0x1000>; /* Address and size of IMEM */
@@ -459,6 +487,8 @@
 };
 
 #include "pmxpoorwills.dtsi"
+#include "sdxpoorwills-blsp.dtsi"
 #include "sdxpoorwills-regulator.dtsi"
 #include "sdxpoorwills-smp2p.dtsi"
 #include "sdxpoorwills-usb.dtsi"
+#include "sdxpoorwills-bus.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/configs/sdxpoorwills-perf_defconfig b/arch/arm/configs/sdxpoorwills-perf_defconfig
index f57d9c3..877406f 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,6 +192,7 @@
 # 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
@@ -200,20 +202,29 @@
 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_THERMAL=y
+CONFIG_THERMAL_QPNP=y
 CONFIG_THERMAL_TSENS=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_SOC=y
@@ -265,7 +276,9 @@
 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_GSI=y
@@ -276,6 +289,7 @@
 CONFIG_IPA_UT=y
 CONFIG_SPS=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
 CONFIG_USB_BAM=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
 CONFIG_MAILBOX=y
@@ -293,7 +307,9 @@
 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 ee823ff..d860595 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,6 +184,7 @@
 # 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
@@ -199,18 +201,26 @@
 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_THERMAL=y
+CONFIG_THERMAL_QPNP=y
 CONFIG_THERMAL_TSENS=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
@@ -263,6 +273,7 @@
 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
@@ -275,6 +286,7 @@
 CONFIG_IPA_UT=y
 CONFIG_SPS=y
 CONFIG_SPS_SUPPORT_NDP_BAM=y
+CONFIG_QPNP_REVID=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
 CONFIG_MAILBOX=y
 CONFIG_QCOM_SCM=y
@@ -292,7 +304,9 @@
 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/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-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/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/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index aefdb52..aec9930 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -17,6 +17,12 @@
 		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 \
@@ -26,6 +32,7 @@
 		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
@@ -44,6 +51,12 @@
 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
@@ -53,6 +66,7 @@
 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
@@ -79,6 +93,8 @@
 		sdm670-cdp-overlay.dtbo \
 		sdm670-mtp-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 \
@@ -104,6 +120,8 @@
 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
@@ -130,6 +148,8 @@
 dtb-$(CONFIG_ARCH_SDM670) += sdm670-rumi.dtb \
 	sdm670-mtp.dtb \
 	sdm670-cdp.dtb \
+	sdm670-qrd.dtb \
+	sdm670-qrd-sku2.dtb \
 	sdm670-pm660a-mtp.dtb \
 	sdm670-pm660a-cdp.dtb \
 	sdm670-external-codec-cdp.dtb \
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-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-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/fg-gen3-batterydata-mlp446579-3800mah.dtsi b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
new file mode 100644
index 0000000..ca43a45
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/fg-gen3-batterydata-mlp446579-3800mah.dtsi
@@ -0,0 +1,81 @@
+/* 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,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 894ee5a..715b59c 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm670.dtsi
@@ -36,11 +36,9 @@
 				<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>;
 		attach-impl-defs =
 				<0x6000 0x2378>,
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 a48fba0..e4fe2e3 100644
--- a/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm-arm-smmu-sdm845.dtsi
@@ -35,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>,
@@ -316,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.
@@ -347,15 +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-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/pm660.dtsi b/arch/arm64/boot/dts/qcom/pm660.dtsi
index 1fdb3f6..d3c3c38 100644
--- a/arch/arm64/boot/dts/qcom/pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660.dtsi
@@ -338,7 +338,7 @@
 				qcom,scale-function = <2>;
 				qcom,hw-settle-time = <2>;
 				qcom,btm-channel-number = <0x80>;
-				qcom,vadc-thermal-node;
+				qcom,thermal-node;
 			};
 
 			chan@4f {
@@ -349,7 +349,7 @@
 				qcom,scale-function = <2>;
 				qcom,hw-settle-time = <2>;
 				qcom,btm-channel-number = <0x88>;
-				qcom,vadc-thermal-node;
+				qcom,thermal-node;
 			};
 		};
 
@@ -371,6 +371,16 @@
 						"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";
+		};
 	};
 
 	pm660_1: qcom,pm660@1 {
diff --git a/arch/arm64/boot/dts/qcom/pm660l.dtsi b/arch/arm64/boot/dts/qcom/pm660l.dtsi
index aaf5d05..bcb5138 100644
--- a/arch/arm64/boot/dts/qcom/pm660l.dtsi
+++ b/arch/arm64/boot/dts/qcom/pm660l.dtsi
@@ -50,24 +50,20 @@
 		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>;
 		};
 	};
 
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/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 1ee6d51..8d8bd63 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>;
@@ -530,7 +530,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/sda845-v2-hdk-overlay.dts b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.dts
new file mode 100644
index 0000000..f836f50
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk-overlay.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/;
+/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 "sda845-v2-hdk.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>;
+};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
index 71b21b9..17f8324 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dts
@@ -10,19 +10,14 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
 
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+/dts-v1/;
 
-#include "cam_hw_mgr_intf.h"
-#include "cam_jpeg_hw_intf.h"
+#include "sda845-v2.dtsi"
+#include "sda845-v2-hdk.dtsi"
 
-enum cam_jpeg_dma_cmd_type {
-	CAM_JPEG_DMA_CMD_CDM_CFG,
-	CAM_JPEG_DMA_CMD_SET_IRQ_CB,
-	CAM_JPEG_DMA_CMD_MAX,
+/ {
+	model = "Qualcomm Technologies, Inc. SDA845 HDK";
+	compatible = "qcom,sda845-hdk", "qcom,sda845", "qcom,hdk";
+	qcom,board-id = <0x01001F 0x00>;
 };
-
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
index 71b21b9..53617dc 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sda845-v2-hdk.dtsi
@@ -10,19 +10,11 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#include "sdm845-qvr.dtsi"
+#include "sdm845-camera-sensor-mtp.dtsi"
 
-#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_dma_cmd_type {
-	CAM_JPEG_DMA_CMD_CDM_CFG,
-	CAM_JPEG_DMA_CMD_SET_IRQ_CB,
-	CAM_JPEG_DMA_CMD_MAX,
+&vendor {
+	qcom,battery-data {
+		#include "fg-gen3-batterydata-mlp356477-2800mah.dtsi"
+	};
 };
-
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi b/arch/arm64/boot/dts/qcom/sdm670-audio.dtsi
index ef92cdd..bbf6683 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>
 
@@ -100,7 +100,7 @@
 	int_codec: sound {
 		status = "okay";
 		compatible = "qcom,sdm670-asoc-snd";
-		qcom,model = "sdm670-snd-card-mtp";
+		qcom,model = "sdm670-mtp-snd-card";
 		qcom,wcn-btfm;
 		qcom,mi2s-audio-intf;
 		qcom,auxpcm-audio-intf;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
index 8749145..6f22264 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-bus.dtsi
@@ -713,10 +713,10 @@
 				&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>;
+				&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>;
 		};
@@ -741,10 +741,11 @@
 				&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>;
+				&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>;
 		};
@@ -1379,6 +1380,15 @@
 			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";
diff --git a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
index fa06779..8e152b0 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-cdp.dtsi
@@ -61,7 +61,7 @@
 };
 
 &qupv3_se6_4uart {
-	status = "disabled";
+	status = "ok";
 };
 
 &sdhc_1 {
@@ -95,15 +95,6 @@
 	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>;
 
-	#address-cells = <0>;
-	interrupt-parent = <&sdhc_2>;
-	interrupts = <0 1 2>;
-	#interrupt-cells = <1>;
-	interrupt-map-mask = <0xffffffff>;
-	interrupt-map = <0 &intc 0 0 204 0
-			1 &intc 0 0 222 0
-			2 &tlmm 96 0>;
-	interrupt-names = "hc_irq", "pwr_irq", "status_irq";
 	cd-gpios = <&tlmm 96 0x1>;
 
 	status = "ok";
@@ -233,7 +224,54 @@
 	qcom,platform-reset-gpio = <&tlmm 75 0>;
 };
 
-&dsi_dual_nt35597_truly_video {
+&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_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	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;
 };
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi b/arch/arm64/boot/dts/qcom/sdm670-coresight.dtsi
index 8b79d8b..d054164 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,6 @@
 					  <&funnel_apss_merg_out_funnel_in2>;
 				};
 			};
-
 		};
 	};
 
@@ -544,6 +751,8 @@
 
 		coresight-name = "coresight-tpdm-center";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -560,6 +769,8 @@
 		reg = <0x6a24000 0x1000>;
 		reg-names = "tpdm-base";
 
+		qcom,msr-fix-req;
+
 		coresight-name = "coresight-tpdm-north";
 
 		clocks = <&clock_aop QDSS_CLK>;
@@ -644,6 +855,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>;
@@ -687,6 +1008,8 @@
 
 		coresight-name = "coresight-tpdm-mm";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -729,13 +1052,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>;
 				};
 			};
 		};
@@ -746,7 +1098,8 @@
 		arm,primecell-periphid = <0x0003b968>;
 		reg = <0x6860000 0x1000>;
 		reg-names = "tpdm-base";
-		status = "disabled";
+
+		qcom,msr-fix-req;
 
 		coresight-name = "coresight-tpdm-turing";
 
@@ -804,6 +1157,8 @@
 
 		coresight-name = "coresight-tpdm-ddr";
 
+		qcom,msr-fix-req;
+
 		clocks = <&clock_aop QDSS_CLK>;
 		clock-names = "apb_pclk";
 
@@ -935,6 +1290,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>;
+				};
+			};
 		};
 	};
 
@@ -1562,9 +1926,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>;
 			};
 		};
 	};
@@ -1583,6 +1947,20 @@
 		};
 	};
 
+	audio_etm0 {
+		compatible = "qcom,coresight-remote-etm";
+
+		coresight-name = "coresight-audio-etm0";
+		qcom,inst-id = <2>;
+
+		port {
+			audio_etm0_out_funnel_in1: endpoint {
+				remote-endpoint =
+					<&funnel_in1_in_audio_etm0>;
+			};
+		};
+	};
+
 	funnel_apss_merg: funnel@7810000 {
 		compatible = "arm,primecell";
 		arm,primecell-periphid = <0x0003b908>;
@@ -1633,6 +2011,22 @@
 					    <&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>;
+				};
+			};
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
index 89dee0c..4783396 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-gpu.dtsi
@@ -79,11 +79,10 @@
 			<&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";
 
 		/* Bus Scale Settings */
 		qcom,gpubw-dev = <&gpubw>;
@@ -275,11 +274,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>;
@@ -289,12 +287,12 @@
 
 			qcom,gmu-pwrlevel@0 {
 				reg = <0>;
-				qcom,gmu-freq = <200000000>;
+				qcom,gmu-freq = <0>;
 			};
 
 			qcom,gmu-pwrlevel@1 {
 				reg = <1>;
-				qcom,gmu-freq = <0>;
+				qcom,gmu-freq = <200000000>;
 			};
 		};
 
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.dtsi b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
index 466062b..016917b 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-mtp.dtsi
@@ -13,6 +13,7 @@
 #include <dt-bindings/gpio/gpio.h>
 #include "sdm670-pmic-overlay.dtsi"
 #include "sdm670-sde-display.dtsi"
+#include "smb1355.dtsi"
 
 &ufsphy_mem {
 	compatible = "qcom,ufs-phy-qmp-v3";
@@ -57,11 +58,11 @@
 };
 
 &qupv3_se10_i2c {
-	status = "disabled";
+	status = "ok";
 };
 
 &qupv3_se6_4uart {
-	status = "disabled";
+	status = "ok";
 };
 
 &sdhc_1 {
@@ -95,15 +96,6 @@
 	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>;
 
-	#address-cells = <0>;
-	interrupt-parent = <&sdhc_2>;
-	interrupts = <0 1 2>;
-	#interrupt-cells = <1>;
-	interrupt-map-mask = <0xffffffff>;
-	interrupt-map = <0 &intc 0 0 204 0
-			1 &intc 0 0 222 0
-			2 &tlmm 96 0>;
-	interrupt-names = "hc_irq", "pwr_irq", "status_irq";
 	cd-gpios = <&tlmm 96 0x1>;
 
 	status = "ok";
@@ -122,6 +114,51 @@
 	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";
@@ -242,7 +279,54 @@
 	qcom,platform-reset-gpio = <&tlmm 75 0>;
 };
 
-&dsi_dual_nt35597_truly_video {
+&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_wled";
+	qcom,mdss-dsi-bl-min-level = <1>;
+	qcom,mdss-dsi-bl-max-level = <4095>;
+	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;
 };
 
diff --git a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
index 177813f..2bf00fb 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pinctrl.dtsi
@@ -1625,6 +1625,20 @@
 };
 
 &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";
@@ -1635,3 +1649,13 @@
 		};
 	};
 };
+
+&pm660_gpios {
+	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-pmic-overlay.dtsi b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
index aa6be24..c39978e 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-pmic-overlay.dtsi
@@ -30,8 +30,7 @@
 
 		qcom,wipower-max-uw = <5000000>;
 
-		/* Enable after the qusb_phy0 device node is added */
-		/* dpdm-supply = <&qusb_phy0>; */
+		dpdm-supply = <&qusb_phy0>;
 
 		qcom,thermal-mitigation
 				= <3000000 2500000 2000000 1500000
diff --git a/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
new file mode 100644
index 0000000..36d485e
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-overlay.dts
@@ -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.
+ */
+
+/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-qrd.dtsi"
+
+/ {
+	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/sdm670-qrd-sku2-overlay.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
new file mode 100644
index 0000000..37eb4cd
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2-overlay.dts
@@ -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.
+ */
+
+/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-qrd.dtsi"
+
+/ {
+	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/sdm670-qrd-sku2.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.dts
new file mode 100644
index 0000000..dada4c6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd-sku2.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-qrd.dtsi"
+
+/ {
+	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/sdm670-qrd.dts b/arch/arm64/boot/dts/qcom/sdm670-qrd.dts
new file mode 100644
index 0000000..c22afa4
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.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-qrd.dtsi"
+
+/ {
+	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..616980c
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm670-qrd.dtsi
@@ -0,0 +1,147 @@
+/* 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-pmic-overlay.dtsi"
+#include "sdm670-audio-overlay.dtsi"
+#include "smb1355.dtsi"
+
+&qupv3_se9_2uart {
+	status = "disabled";
+};
+
+&qupv3_se12_2uart {
+	status = "ok";
+};
+
+&qupv3_se8_spi {
+	status = "disabled";
+};
+
+&qupv3_se3_i2c {
+	status = "disabled";
+};
+
+&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>;
+};
+
+&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";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
index 3e9d967..53213f8 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-display.dtsi
@@ -20,6 +20,11 @@
 #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 <dt-bindings/clock/mdss-10nm-pll-clk.h>
 
 &soc {
@@ -97,6 +102,56 @@
 		};
 	};
 
+	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 = "wqhd-vddio";
+			qcom,supply-min-voltage = <1800000>;
+			qcom,supply-max-voltage = <1950000>;
+			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";
@@ -300,6 +355,120 @@
 		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>;
+		lab-supply = <&lcdb_ldo_vreg>;
+		ibb-supply = <&lcdb_ncp_vreg>;
+	};
+
+	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>;
+	};
+
 	sde_wb: qcom,wb-display@0 {
 		compatible = "qcom,wb-display";
 		cell-index = <0>;
@@ -308,116 +477,17 @@
 
 	ext_disp: qcom,msm-ext-disp {
 		compatible = "qcom,msm-ext-disp";
-		status = "disabled";
 
 		ext_disp_audio_codec: qcom,msm-ext-disp-audio-codec-rx {
 			compatible = "qcom,msm-ext-disp-audio-codec-rx";
 		};
 	};
-
-	sde_dp: qcom,dp_display@0{
-		cell-index = <0>;
-		compatible = "qcom,dp-display";
-		status = "disabled";
-
-		gdsc-supply = <&mdss_core_gdsc>;
-		vdda-1p2-supply = <&pm660_l1>;
-		vdda-0p9-supply = <&pm660l_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 = <&pm660_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 = <675000>;
-
-		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 {
-	status = "disabled";
+	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>;
@@ -427,7 +497,7 @@
 };
 
 &mdss_mdp {
-	connectors = <&sde_rscc &sde_wb>;
+	connectors = <&sde_rscc &sde_wb &sde_dp>;
 };
 
 &dsi_dual_nt35597_truly_video {
@@ -595,3 +665,70 @@
 		};
 	};
 };
+
+&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_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,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>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
index 78e5d94..72e3f5f 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-sde-pll.dtsi
@@ -67,7 +67,6 @@
 
 	mdss_dp_pll: qcom,mdss_dp_pll@88ea000 {
 		compatible = "qcom,mdss_dp_pll_10nm";
-		status = "disabled";
 		label = "MDSS DP PLL";
 		cell-index = <0>;
 		#clock-cells = <1>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
index bb30a20..8a811b7 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-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 {
@@ -52,10 +53,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 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>;
@@ -190,10 +194,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 {
@@ -529,4 +537,85 @@
 		};
 	};
 
+	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 0xa84>,
+			<0x88eaa00 0x200>,
+			<0x88ea200 0x200>,
+			<0x88ea600 0x200>,
+			<0xaf02000 0x1a0>,
+			<0x780000 0x621c>,
+			<0x88ea030 0x10>,
+			<0x88e8000 0x20>,
+			<0x0aee1000 0x034>;
+		reg-names = "dp_ctrl", "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
index e4993d6..1ce8dba 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-thermal.dtsi
@@ -518,7 +518,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu0_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -567,7 +567,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu1_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -616,7 +616,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu2_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -665,7 +665,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu3_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -714,7 +714,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu4_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -763,7 +763,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpu5_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -812,7 +812,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&l3_0_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -861,7 +861,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&l3_1_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -910,7 +910,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpug0_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -959,7 +959,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&cpug1_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1008,7 +1008,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&gpu0_trip_l>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1057,7 +1057,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&gpu1_trip_l>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1106,7 +1106,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&aoss1_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1155,7 +1155,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&dsp_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1204,7 +1204,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&ddr_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1253,7 +1253,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&wlan_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1302,7 +1302,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&hvx_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1351,7 +1351,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&camera_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1400,7 +1400,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&mmss_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
@@ -1449,7 +1449,7 @@
 				cooling-device = <&CPU6 9 9>;
 			};
 			gpu_vdd_cdev {
-				trip = <&aoss0_trip>;
+				trip = <&mdm_trip>;
 				cooling-device = <&msm_gpu 1 1>;
 			};
 			cx_vdd_cdev {
diff --git a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
index a74f9d8..01d4057 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-vidc.dtsi
@@ -139,7 +139,7 @@
 			"bus_clk", "core0_clk", "core0_bus_clk";
 		qcom,clock-configs = <0x1 0x0 0x0 0x1 0x0>;
 		qcom,allowed-clock-rates = <100000000 200000000 320000000
-			364800000>;
+			364700000>;
 
 		/* Buses */
 		bus_cnoc {
diff --git a/arch/arm64/boot/dts/qcom/sdm670.dtsi b/arch/arm64/boot/dts/qcom/sdm670.dtsi
index b527f3d..6e768b18 100644
--- a/arch/arm64/boot/dts/qcom/sdm670.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670.dtsi
@@ -56,7 +56,7 @@
 			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>;
@@ -75,11 +75,11 @@
 			};
 			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>;
@@ -92,7 +92,7 @@
 			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>;
@@ -106,11 +106,11 @@
 			};
 			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>;
@@ -123,7 +123,7 @@
 			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>;
@@ -137,11 +137,11 @@
 			};
 			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>;
@@ -154,7 +154,7 @@
 			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>;
@@ -168,11 +168,11 @@
 			};
 			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>;
@@ -185,7 +185,7 @@
 			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>;
@@ -199,11 +199,11 @@
 			};
 			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>;
@@ -216,7 +216,7 @@
 			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>;
@@ -230,11 +230,11 @@
 			};
 			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>;
@@ -247,7 +247,7 @@
 			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>;
@@ -261,11 +261,11 @@
 			};
 			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>;
@@ -278,7 +278,7 @@
 			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>;
@@ -292,11 +292,11 @@
 			};
 			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>;
@@ -726,6 +726,73 @@
 		qcom,pipe-attr-ee;
 	};
 
+	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-bypass;
+		iommus = <&apps_smmu 0x706 0x3>,
+			 <&apps_smmu 0x716 0x3>;
+	};
+
+	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-bypass;
+		iommus = <&apps_smmu 0x704 0x3>,
+			 <&apps_smmu 0x714 0x3>;
+	};
+
 	qcom,qbt1000 {
 		compatible = "qcom,qbt1000";
 		clock-names = "core", "iface";
@@ -734,6 +801,58 @@
 		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,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 {
@@ -915,11 +1034,18 @@
 		#clock-cells = <1>;
 	};
 
-	clock_cpucc: qcom,cpucc {
-		compatible = "qcom,dummycc";
-		clock-output-names = "cpucc_clocks";
+	clock_cpucc: qcom,cpucc@0x17d41000 {
+		compatible = "qcom,clk-cpu-osm-sdm670";
+		reg = <0x17d41000 0x1400>,
+			<0x17d43000 0x1400>,
+			<0x17d45800 0x1400>;
+		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
+
+		l3-devs = <&l3_cpu0 &l3_cpu6>;
+
+		clock-names = "xo_ao";
+		clocks = <&clock_rpmh RPMH_CXO_CLK_A>;
 		#clock-cells = <1>;
-		#reset-cells = <1>;
 	};
 
 	clock_aop: qcom,aopclk {
@@ -960,7 +1086,7 @@
 		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 {
@@ -968,13 +1094,20 @@
 			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;
@@ -1642,6 +1775,7 @@
 			<0 0>,
 			<0 0>;
 
+		non-removable;
 		qcom,msm-bus,name = "ufshc_mem";
 		qcom,msm-bus,num-cases = <12>;
 		qcom,msm-bus,num-paths = <2>;
@@ -1705,6 +1839,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 */
@@ -1715,6 +1850,9 @@
 
 		/* 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";
 	};
 
@@ -1725,6 +1863,10 @@
 		qcom,client-id = <0x00000001>;
 	};
 
+	qcom,msm_gsi {
+		compatible = "qcom,msm_gsi";
+	};
+
 	qcom,rmnet-ipa {
 		compatible = "qcom,rmnet-ipa3";
 		qcom,rmnet-ipa-ssr;
@@ -1750,7 +1892,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>;
@@ -1867,17 +2008,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>;
 		};
@@ -1929,8 +2073,9 @@
 		qcom,sysmon-id = <0>;
 		qcom,ssctl-instance-id = <0x12>;
 		qcom,override-acc;
+		qcom,signal-aop;
 		qcom,qdsp6v65-1-0;
-		qcom,mss_pdc_offset = <8>;
+		qcom,mss_pdc_offset = <9>;
 		status = "ok";
 		memory-region = <&pil_modem_mem>;
 		qcom,mem-protect-id = <0xF>;
@@ -1944,6 +2089,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>;
@@ -1995,6 +2143,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 */
@@ -2005,6 +2154,9 @@
 
 		/* 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";
 	};
 
@@ -2030,30 +2182,30 @@
 		qcom,msm-bus,num-paths = <2>;
 		qcom,msm-bus,vectors-KBps =
 			/* No vote */
-			<78 512 0 0>, <1 606 0 0>,
+			<150 512 0 0>, <1 606 0 0>,
 			/* 400 KB/s*/
-			<78 512 1046 1600>,
+			<150 512 1046 1600>,
 			<1 606 1600 1600>,
 			/* 20 MB/s */
-			<78 512 52286 80000>,
+			<150 512 52286 80000>,
 			<1 606 80000 80000>,
 			/* 25 MB/s */
-			<78 512 65360 100000>,
+			<150 512 65360 100000>,
 			<1 606 100000 100000>,
 			/* 50 MB/s */
-			<78 512 130718 200000>,
+			<150 512 130718 200000>,
 			<1 606 133320 133320>,
 			/* 100 MB/s */
-			<78 512 130718 200000>,
+			<150 512 130718 200000>,
 			<1 606 150000 150000>,
 			/* 200 MB/s */
-			<78 512 261438 400000>,
+			<150 512 261438 400000>,
 			<1 606 300000 300000>,
 			/* 400 MB/s */
-			<78 512 261438 400000>,
+			<150 512 261438 400000>,
 			<1 606 300000 300000>,
 			/* Max. bandwidth */
-			<78 512 1338562 4096000>,
+			<150 512 1338562 4096000>,
 			<1 606 1338562 4096000>;
 		qcom,bus-bw-vectors-bps = <0 400000 20000000 25000000 50000000
 			100000000 200000000 400000000 4294967295>;
@@ -2067,11 +2219,15 @@
 
 		clocks = <&clock_gcc GCC_SDCC1_AHB_CLK>,
 			<&clock_gcc GCC_SDCC1_APPS_CLK>,
-			<&clock_gcc GCC_SDCC1_ICE_CORE_CLK>;
-		clock-names = "iface_clk", "core_clk", "ice_core_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;
 
 		qcom,scaling-lower-bus-speed-mode = "DDR52";
@@ -2220,12 +2376,36 @@
 			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 {
-		status = "disabled";
 		compatible = "qcom,icnss";
-		reg = <0x18800000 0x800000>;
+		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 */ >,
@@ -2238,6 +2418,10 @@
 			     <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;
 	};
@@ -2268,6 +2452,7 @@
 		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>;
 	};
@@ -2291,7 +2476,7 @@
 			< MHZ_TO_MBPS(1804, 4) >; /* 6881 MB/s */
 	};
 
-	memlat_cpu4: qcom,memlat-cpu4 {
+	memlat_cpu6: qcom,memlat-cpu6 {
 		compatible = "qcom,devbw";
 		governor = "powersave";
 		qcom,src-dst-ports = <1 512>;
@@ -2313,9 +2498,9 @@
 
 	devfreq_memlat_0: qcom,cpu0-memlat-mon {
 		compatible = "qcom,arm-memlat-mon";
-		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
+		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
 		qcom,target-dev = <&memlat_cpu0>;
-		qcom,cachemiss-ev = <0x24>;
+		qcom,cachemiss-ev = <0x2a>;
 		qcom,core-dev-table =
 			<  748800 MHZ_TO_MBPS( 300, 4) >,
 			<  998400 MHZ_TO_MBPS( 451, 4) >,
@@ -2324,11 +2509,11 @@
 			< 1728000 MHZ_TO_MBPS(1017, 4) >;
 	};
 
-	devfreq_memlat_4: qcom,cpu4-memlat-mon {
+	devfreq_memlat_6: qcom,cpu6-memlat-mon {
 		compatible = "qcom,arm-memlat-mon";
-		qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>;
-		qcom,target-dev = <&memlat_cpu4>;
-		qcom,cachemiss-ev = <0x24>;
+		qcom,cpulist = <&CPU6 &CPU7>;
+		qcom,target-dev = <&memlat_cpu6>;
+		qcom,cachemiss-ev = <0x2a>;
 		qcom,core-dev-table =
 			<  787200 MHZ_TO_MBPS( 300, 4) >,
 			< 1113600 MHZ_TO_MBPS( 547, 4) >,
@@ -2344,7 +2529,7 @@
 		governor = "performance";
 	};
 
-	l3_cpu4: qcom,l3-cpu4 {
+	l3_cpu6: qcom,l3-cpu6 {
 		compatible = "devfreq-simple-dev";
 		clock-names = "devfreq_clk";
 		clocks = <&clock_cpucc L3_CLUSTER1_VOTE_CLK>;
@@ -2353,7 +2538,7 @@
 
 	devfreq_l3lat_0: qcom,cpu0-l3lat-mon {
 		compatible = "qcom,arm-memlat-mon";
-		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3>;
+		qcom,cpulist = <&CPU0 &CPU1 &CPU2 &CPU3 &CPU4 &CPU5>;
 		qcom,target-dev = <&l3_cpu0>;
 		qcom,cachemiss-ev = <0x17>;
 		qcom,core-dev-table =
@@ -2365,10 +2550,10 @@
 			< 1728000 1440000000 >;
 	};
 
-	devfreq_l3lat_4: qcom,cpu4-l3lat-mon {
+	devfreq_l3lat_6: qcom,cpu6-l3lat-mon {
 		compatible = "qcom,arm-memlat-mon";
-		qcom,cpulist = <&CPU4 &CPU5 &CPU6 &CPU7>;
-		qcom,target-dev = <&l3_cpu4>;
+		qcom,cpulist = <&CPU6 &CPU7>;
+		qcom,target-dev = <&l3_cpu6>;
 		qcom,cachemiss-ev = <0x17>;
 		qcom,core-dev-table =
 			< 1113600  566400000 >,
@@ -2405,7 +2590,7 @@
 				< 1209600 MHZ_TO_MBPS( 451, 4) >,
 				< 1612000 MHZ_TO_MBPS( 547, 4) >,
 				< 1728000 MHZ_TO_MBPS( 768, 4) >;
-			cpu-to-dev-map-4 =
+			cpu-to-dev-map-6 =
 				< 1113600 MHZ_TO_MBPS( 300, 4) >,
 				< 1344000 MHZ_TO_MBPS( 547, 4) >,
 				< 1728000 MHZ_TO_MBPS( 768, 4) >,
@@ -2414,6 +2599,12 @@
 		};
 	};
 
+	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>;
@@ -2554,8 +2745,28 @@
 	};
 };
 
+&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-thermal.dtsi"
 #include "sdm670-bus.dtsi"
+
+&pm660_div_clk {
+	status = "ok";
+};
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 04418d4..7ca2645 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-cdp.dtsi
@@ -122,12 +122,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 +137,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 +163,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 +204,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 +219,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>;
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 9088fac..aa55698 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-camera-sensor-mtp.dtsi
@@ -122,12 +122,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 +137,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 +163,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 +204,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 +219,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>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-camera.dtsi
index 7c9482c..db57aae 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>,
@@ -62,7 +62,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>,
@@ -97,7 +97,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>,
@@ -358,6 +358,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/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
index 81ce1e5..dffb5e0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-cdp.dtsi
@@ -269,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-gpu.dtsi b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
index 11b6a4d..eac21c8 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-gpu.dtsi
@@ -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;
@@ -81,11 +82,10 @@
 			<&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>;
 
@@ -267,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>,
@@ -277,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 {
@@ -288,7 +290,7 @@
 
 		gfx3d_secure: gfx3d_secure {
 			compatible = "qcom,smmu-kgsl-cb";
-			iommus = <&kgsl_smmu 2>;
+			iommus = <&kgsl_smmu 2>, <&kgsl_smmu 1>;
 		};
 	};
 
@@ -317,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>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
index cc609aa..1a8de22 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-pm660.dtsi
@@ -54,6 +54,18 @@
 	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>;
+};
+
 &sde_dp {
 	status = "disabled";
 	/delete-property/ vdda-1p2-supply;
@@ -230,12 +242,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 {
@@ -325,8 +342,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;
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 1265d2a..9313a75 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-cdp.dtsi
@@ -37,3 +37,7 @@
 &pcie0 {
 	status = "disabled";
 };
+
+&eud {
+	vdda33-supply = <&pm660l_l7>;
+};
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 9d722df..e7ff910 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-interposer-sdm670-mtp.dtsi
@@ -42,3 +42,7 @@
 &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 f0d16ec..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;
 };
@@ -218,9 +239,18 @@
 	qcom,vdd-io-voltage-level = <1808000 2960000>;
 	qcom,vdd-io-current-level = <200 22000>;
 
-	pinctrl-names = "active", "sleep";
+	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>;
 
 	cd-gpios = <&tlmm 126 GPIO_ACTIVE_LOW>;
 
@@ -256,10 +286,12 @@
 
 &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 a0207e5..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 {
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 2ac313d..48040a3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pmic-overlay.dtsi
@@ -37,6 +37,11 @@
 	};
 };
 
+&pmi8998_qnovo {
+	pinctrl-names = "default";
+	pinctrl-0 = <&qnovo_fet_ctrl_default>;
+};
+
 &usb0 {
 	extcon = <&pmi8998_pdphy>, <&pmi8998_pdphy>, <&eud>;
 };
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qupv3.dtsi
index a805e2e..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>;
@@ -442,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>;
@@ -459,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>;
@@ -476,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>;
@@ -493,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>;
@@ -510,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>;
@@ -527,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>;
@@ -544,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>;
@@ -561,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>;
@@ -581,7 +629,7 @@
 			<&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>;
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-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
index 2fac9e8..b1b81d1 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr-overlay.dts
@@ -19,7 +19,9 @@
 #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"
 
 / {
 	model = "Qualcomm Technologies, Inc. SDM845 v2 QVR";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
index d89722f..2d701a5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qvr.dtsi
@@ -14,11 +14,19 @@
 #include "sdm845-pinctrl-overlay.dtsi"
 #include "smb1355.dtsi"
 
+&vendor {
+	qvr_batterydata: qcom,battery-data {
+		qcom,batt-id-range-pct = <15>;
+		#include "fg-gen3-batterydata-mlp446579-3800mah.dtsi"
+	};
+};
+
 &pmi8998_pdphy {
 	vbus-supply = <&smb2_vbus>;
 };
 
 &pmi8998_fg {
+	qcom,battery-data = <&qvr_batterydata>;
 	qcom,fg-bmd-en-delay-ms = <300>;
 };
 
@@ -75,6 +83,10 @@
 	status = "okay";
 };
 
+&qupv3_se9_2uart {
+	status = "ok";
+};
+
 &ufsphy_mem {
 	compatible = "qcom,ufs-phy-qmp-v3";
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index d22c28a..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>;
 		};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
index d607f75..34de0a0 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-rumi.dtsi
@@ -132,14 +132,6 @@
 	};
 };
 
-&apc0_cpr {
-	qcom,cpr-ignore-invalid-fuses;
-};
-
-&apc1_cpr {
-	qcom,cpr-ignore-invalid-fuses;
-};
-
 &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 4337da7..4254fcd 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde-display.dtsi
@@ -25,6 +25,8 @@
 #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 <dt-bindings/clock/mdss-10nm-pll-clk.h>
 
 &soc {
@@ -401,6 +403,54 @@
 		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>;
+	};
+
 	sde_wb: qcom,wb-display@0 {
 		compatible = "qcom,wb-display";
 		cell-index = <0>;
@@ -545,8 +595,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>;
 		};
@@ -660,3 +711,31 @@
 		};
 	};
 };
+
+&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>;
+		};
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
index e7a946c..7c8eab4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-sde.dtsi
@@ -55,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>;
@@ -160,7 +163,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>,
@@ -207,6 +221,9 @@
 
 		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>;
diff --git a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
index 09f4efa..ba397e5 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-usb.dtsi
@@ -78,6 +78,7 @@
 			snps,disable-clk-gating;
 			snps,has-lpm-erratum;
 			snps,hird-threshold = /bits/ 8 <0x10>;
+			snps,usb3_lpm_capable;
 			usb-core-id = <0>;
 		};
 
@@ -387,6 +388,7 @@
 			snps,disable-clk-gating;
 			snps,has-lpm-erratum;
 			snps,hird-threshold = /bits/ 8 <0x10>;
+			snps,usb3_lpm_capable;
 			usb-core-id = <1>;
 		};
 	};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
index 0b6e27e..ca83bed 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2-camera.dtsi
@@ -256,6 +256,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",
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-v2.1-cdp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
new file mode 100644
index 0000000..0825f4b
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-cdp-overlay.dts
@@ -0,0 +1,31 @@
+/* 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 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-v2.1-mtp-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
new file mode 100644
index 0000000..7b3573a
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-mtp-overlay.dts
@@ -0,0 +1,31 @@
+/* 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 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-v2.1-qrd-overlay.dts b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
new file mode 100644
index 0000000..15d79c2
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1-qrd-overlay.dts
@@ -0,0 +1,31 @@
+/* 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 QRD";
+	compatible = "qcom,sdm845-qrd", "qcom,sdm845", "qcom,qrd";
+	qcom,msm-id = <321 0x20001>;
+	qcom,board-id = <11 0>;
+};
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
similarity index 61%
rename from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
rename to arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
index 71b21b9..2902a60 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dts
@@ -10,19 +10,13 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+/dts-v1/;
 
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+#include "sdm845-v2.1.dtsi"
 
-#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,
+/ {
+	model = "Qualcomm Technologies, Inc. SDM845 v2.1 SoC";
+	compatible = "qcom,sdm845";
+	qcom,board-id = <0 0>;
 };
 
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
similarity index 61%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
index 71b21b9..ff8c01a 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.1.dtsi
@@ -10,19 +10,18 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#include "sdm845-v2.dtsi"
+#include "sdm845-v2-camera.dtsi"
 
-#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_dma_cmd_type {
-	CAM_JPEG_DMA_CMD_CDM_CFG,
-	CAM_JPEG_DMA_CMD_SET_IRQ_CB,
-	CAM_JPEG_DMA_CMD_MAX,
+/ {
+	model = "Qualcomm Technologies, Inc. SDM845 V2.1";
+	qcom,msm-id = <321 0x20001>;
 };
 
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
+&spmi_bus {
+	/delete-property/ qcom,enable-ahb-bus-workaround;
+};
+
+&clock_gcc {
+	compatible = "qcom,gcc-sdm845-v2.1", "syscon";
+};
diff --git a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
index b3cb8f5..cfa4517 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-v2.dtsi
@@ -22,674 +22,13 @@
 	/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,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 = <0x3E803E8>;
-
-		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 = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <18 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 =
-					/* Speed bin 0 */
-					<      0        0    12000    12000>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					/* Speed bin 1 */
-					<      0        0    12000    12000>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					/* Speed bin 2 */
-					<      0        0    12000    12000>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>,
-					<(-15000) (-15000)  (-3000)  (-3000)>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<      0        0    12000    10000>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					/* Speed bin 1 */
-					<      0        0    12000    10000>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					/* Speed bin 2 */
-					<      0        0    12000    10000>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>,
-					<(-15000) (-15000)  (-3000)  (-5000)>;
-
-				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>,
-					/* Speed bin 2 */
-					<0 1 1 1 1 1 1 1>;
-				qcom,allow-aging-open-loop-voltage-adjustment =
-					<1>;
-			};
-		};
-
-		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 = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <14 15 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>,
-					/* Speed bin 2 */
-					<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>,
-					/* Speed bin 2 */
-					<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>,
-					/* Speed bin 2 */
-					<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>,
-					/* Speed bin 2 */
-					<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>,
-					/* Speed bin 2 */
-					<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>,
-					/* Speed bin 2 */
-					<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 =
-					/* Speed bin 0 */
-					<   8000    16000    16000    12000>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					/* Speed bin 1 */
-					<   8000    16000    16000    12000>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					/* Speed bin 2 */
-					<   8000    16000    16000    12000>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>,
-					< (-7000)    1000     1000   (-3000)>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-					/* Speed bin 0 */
-					<   6000    14000    16000    12000>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					/* Speed bin 1 */
-					<   6000    14000    16000    12000>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					/* Speed bin 2 */
-					<   6000    14000    16000    12000>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>,
-					< (-9000)  (-1000)    1000   (-3000)>;
-
-				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 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>,
-					/* 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-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 = <0x4880488>;
-
-		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 = <1160000>;
-		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 = <35>;
-
-				qcom,cpr-fuse-corners = <5>;
-				qcom,cpr-fuse-combos = <24>;
-				qcom,cpr-speed-bins = <3>;
-				qcom,cpr-speed-bin-corners = <28 31 33>;
-				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>,
-					/* Speed bin 2 */
-					<33 33 33 33 33 33 33 33>;
-
-				qcom,cpr-corner-fmax-map =
-					/* Speed bin 0 */
-					<7 14 22 27 28>,
-					/* Speed bin 1 */
-					<7 14 22 27 31>,
-					/* Speed bin 2 */
-					<7 14 22 30 33>;
-
-				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 1160000 1160000>,
-					/* 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 1160000 1160000 1160000 1160000
-					1160000>,
-					/* Speed bin 2 */
-					<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 1160000 1160000 1160000 1160000
-					1160000 1160000 1160000>;
-
-				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>,
-					/* 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
-					 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>,
-					/* Speed bin 2 */
-					<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  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>,
-					/* Speed bin 2 */
-					<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 2726400000 2745600000>;
-
-				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>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 < (-7000)  (-7000)  (-7000) (-15000) (-15000)>,
-				 /* Speed bin 1 */
-				 <   8000     8000     8000        0    16000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 /* Speed bin 2 */
-				 <   8000     8000     8000        0    16000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>,
-				 < (-7000)  (-7000)  (-7000) (-15000)    1000>;
-
-				qcom,cpr-closed-loop-voltage-fuse-adjustment =
-				 /* Speed bin 0 */
-				 <   6000     6000     8000        0        0>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 < (-9000)  (-9000)  (-7000) (-15000) (-15000)>,
-				 /* Speed bin 1 */
-				 <   6000     6000     8000        0    16000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 /* Speed bin 2 */
-				 <   6000     6000     8000        0    16000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>,
-				 < (-9000)  (-9000)  (-7000) (-15000)    1000>;
-
-				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 33>;
-				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>;
-			};
-		};
-	};
-
 	gpu_gx_domain_addr: syscon@0x5091508 {
 		compatible = "syscon";
 		reg = <0x5091508 0x4>;
@@ -699,250 +38,23 @@
 		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;
+	};
+
 };
 
 &pil_modem {
 	qcom,mss_pdc_offset = <9>;
 };
 
-/* VDD_APC0 */
-&pm8998_s13 {
-	regulator-min-microvolt = <568000>;
-	regulator-max-microvolt = <1000000>;
-};
-
-/* VDD_APC1 */
-&pm8998_s12 {
-	regulator-min-microvolt = <568000>;
-	regulator-max-microvolt = <1160000>;
-};
-
 &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,l3-speedbin2-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,pwrcl-speedbin2-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,perfcl-speedbin2-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 >,
-		<  2707200000 0x40511e8d 0x00007171 0x2 30 >,
-		<  2764800000 0x40511f90 0x00007373 0x2 31 >,
-		<  2784000000 0x40512091 0x00007474 0x2 32 >,
-		<  2803200000 0x40512192 0x00007575 0x2 33 >;
-
-	qcom,l3-memacc-level-vc-bin0 = <8 13>;
-	qcom,l3-memacc-level-vc-bin1 = <8 13>;
-	qcom,l3-memacc-level-vc-bin2 = <8 13>;
-
-	qcom,pwrcl-memacc-level-vc-bin0 = <12 16>;
-	qcom,pwrcl-memacc-level-vc-bin1 = <12 16>;
-	qcom,pwrcl-memacc-level-vc-bin2 = <12 16>;
-
-	qcom,perfcl-memacc-level-vc-bin0 = <14 22>;
-	qcom,perfcl-memacc-level-vc-bin1 = <14 22>;
-	qcom,perfcl-memacc-level-vc-bin2 = <14 22>;
 };
 
 &pcie1 {
@@ -1162,7 +274,7 @@
 	mincpubw-cpufreq {
 		cpu-to-dev-map-4 =
 			< 1881600 MHZ_TO_MBPS(200, 4) >,
-			< 2400000 MHZ_TO_MBPS(681, 4) >;
+			< 2400000 MHZ_TO_MBPS(1017, 4) >;
 	};
 };
 
@@ -1209,6 +321,8 @@
 &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 {
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 03117b1..86705d6 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))
 
@@ -210,7 +211,7 @@
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_400: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -241,7 +242,7 @@
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_500: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -272,7 +273,7 @@
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_600: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -303,7 +304,7 @@
 				qcom,dump-size = <0x14000>;
 			};
 			L1_TLB_700: l1-tlb {
-				qcom,dump-size = <0x3c000>;
+				qcom,dump-size = <0x3c00>;
 			};
 		};
 
@@ -610,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>;
@@ -1083,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;
@@ -1207,267 +1220,14 @@
 		compatible = "qcom,clk-cpu-osm";
 		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>;
-		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";
+			<0x17d45800 0x1400>;
+		reg-names = "osm_l3_base", "osm_pwrcl_base", "osm_perfcl_base";
 
-		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;
+		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 {
@@ -1573,6 +1333,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>;
@@ -1689,6 +1450,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";
 	};
 
@@ -1727,6 +1494,8 @@
 		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>;
@@ -1800,7 +1569,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>;
@@ -1884,6 +1655,7 @@
 		reg-names = "eud_base";
 		clocks = <&clock_gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
 		clock-names = "cfg_ahb_clk";
+		vdda33-supply = <&pm8998_l24>;
 		status = "ok";
 	};
 
@@ -2797,6 +2569,7 @@
 		reg = <0x0 0x200000>;
 		reg-names = "rmtfs";
 		qcom,client-id = <0x00000001>;
+		qcom,guard-memory;
 	};
 
 	qcom,rmnet-ipa {
@@ -2824,7 +2597,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>;
@@ -2947,17 +2719,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>;
 		};
@@ -3013,6 +2794,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 {
diff --git a/arch/arm64/boot/dts/qcom/smb1355.dtsi b/arch/arm64/boot/dts/qcom/smb1355.dtsi
index bde4d1e..3412b25d 100644
--- a/arch/arm64/boot/dts/qcom/smb1355.dtsi
+++ b/arch/arm64/boot/dts/qcom/smb1355.dtsi
@@ -52,8 +52,10 @@
 
 			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";
 			};
 		};
 	};
@@ -97,8 +99,10 @@
 
 			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";
 			};
 		};
 	};
diff --git a/arch/arm64/configs/sdm670-perf_defconfig b/arch/arm64/configs/sdm670-perf_defconfig
index 901143b..144ba27 100644
--- a/arch/arm64/configs/sdm670-perf_defconfig
+++ b/arch/arm64/configs/sdm670-perf_defconfig
@@ -230,6 +230,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
@@ -282,6 +283,7 @@
 # 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
@@ -439,6 +441,7 @@
 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
@@ -529,7 +532,6 @@
 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
@@ -566,6 +568,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
diff --git a/arch/arm64/configs/sdm670_defconfig b/arch/arm64/configs/sdm670_defconfig
index 1e9c191..a3ddc74 100644
--- a/arch/arm64/configs/sdm670_defconfig
+++ b/arch/arm64/configs/sdm670_defconfig
@@ -287,6 +287,7 @@
 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
@@ -434,6 +435,7 @@
 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
@@ -446,8 +448,6 @@
 CONFIG_EDAC_MM_EDAC=y
 CONFIG_EDAC_KRYO3XX_ARM64=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
@@ -536,7 +536,6 @@
 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
@@ -575,6 +574,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
@@ -607,7 +607,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
diff --git a/arch/arm64/configs/sdm845-perf_defconfig b/arch/arm64/configs/sdm845-perf_defconfig
index 9ee3277..cdfa1eb 100644
--- a/arch/arm64/configs/sdm845-perf_defconfig
+++ b/arch/arm64/configs/sdm845-perf_defconfig
@@ -188,6 +188,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
@@ -229,6 +230,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
@@ -343,7 +345,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
@@ -526,7 +527,6 @@
 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
@@ -563,6 +563,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
diff --git a/arch/arm64/configs/sdm845_defconfig b/arch/arm64/configs/sdm845_defconfig
index f251367..a2a9c12 100644
--- a/arch/arm64/configs/sdm845_defconfig
+++ b/arch/arm64/configs/sdm845_defconfig
@@ -194,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
@@ -300,6 +301,8 @@
 # CONFIG_SERIO_SERPORT is not set
 # CONFIG_VT is not set
 # CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVMEM is not set
+# CONFIG_DEVKMEM is not set
 CONFIG_SERIAL_MSM_GENI=y
 CONFIG_SERIAL_MSM_GENI_CONSOLE=y
 CONFIG_DIAG_CHAR=y
@@ -351,7 +354,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
@@ -544,7 +546,6 @@
 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
@@ -585,6 +586,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
diff --git a/arch/arm64/include/asm/dma-iommu.h b/arch/arm64/include/asm/dma-iommu.h
index 110f750..cfd49b2 100644
--- a/arch/arm64/include/asm/dma-iommu.h
+++ b/arch/arm64/include/asm/dma-iommu.h
@@ -23,6 +23,8 @@
 	void			*bitmap;
 	size_t			bits;
 	dma_addr_t		base;
+	u32			min_iova_align;
+	struct page		*guard_page;
 
 	struct dma_fast_smmu_mapping *fast;
 };
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/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/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index 5229b33..15fef4c 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -37,7 +37,8 @@
 #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;
@@ -1165,15 +1166,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 +1197,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;
+	else
+		guard_len = 0;
+
+	iommu_unmap(mapping->domain, addr + size, guard_len);
+
+	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);
@@ -1942,6 +1977,23 @@
 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;
+	bool min_iova_align = 0;
+
+	iommu_domain_get_attr(mapping->domain,
+			DOMAIN_ATTR_QCOM_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);
@@ -1979,26 +2031,18 @@
 					struct dma_iommu_mapping *mapping)
 {
 	struct iommu_domain *domain = mapping->domain;
-	struct iommu_group *group = dev->iommu_group;
 	dma_addr_t base = mapping->base;
 	u64 size = mapping->bits << PAGE_SHIFT;
 
 	if (iommu_get_dma_cookie(domain))
 		return -EINVAL;
 
-	/* Need to attach to get geometry */
-	if (iommu_attach_group(domain, group))
+	if (iommu_dma_init_domain(domain, base, size, dev))
 		goto out_put_cookie;
 
-	if (iommu_dma_init_domain(domain, base, size, dev))
-		goto out_detach_group;
-
 	mapping->ops = &iommu_dma_ops;
-	iommu_detach_group(domain, group);
 	return 0;
 
-out_detach_group:
-	iommu_detach_group(domain, group);
 out_put_cookie:
 	iommu_put_dma_cookie(domain);
 	return -EINVAL;
@@ -2060,20 +2104,8 @@
 {
 	int err = -EINVAL;
 	int s1_bypass = 0, is_fast = 0, is_upstream = 0;
-	struct iommu_group *group;
 	dma_addr_t iova_end;
 
-	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;
-	}
-
 	if (mapping->init) {
 		kref_get(&mapping->kref);
 		return 0;
@@ -2128,14 +2160,28 @@
 			    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;
 
 	err = arm_iommu_init_mapping(dev, mapping);
-	if (err)
+	if (err) {
+		iommu_detach_group(domain, group);
 		return err;
-
-	err = iommu_attach_group(mapping->domain, dev->iommu_group);
-	if (err)
-		return err;
+	}
 
 	dev->archdata.mapping = mapping;
 	set_dma_ops(dev, mapping->ops);
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 792dac8..b5d88f8 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -537,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/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/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/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..1b50958 100644
--- a/arch/mips/kernel/process.c
+++ b/arch/mips/kernel/process.c
@@ -487,31 +487,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 +553,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/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/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/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/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/ptrace.c b/arch/powerpc/kernel/ptrace.c
index dcbb914..d973708 100644
--- a/arch/powerpc/kernel/ptrace.c
+++ b/arch/powerpc/kernel/ptrace.c
@@ -131,7 +131,7 @@
 	 * in the appropriate thread structures from live.
 	 */
 
-	if (tsk != current)
+	if ((!cpu_has_feature(CPU_FTR_TM)) || (tsk != current))
 		return;
 
 	if (MSR_TM_SUSPENDED(mfmsr())) {
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/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/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/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 d33f245..db74d39 100644
--- a/arch/s390/include/asm/pgtable.h
+++ b/arch/s390/include/asm/pgtable.h
@@ -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/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/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/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index 2deb89e..ca7cb8e 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -1465,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/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/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/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/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 55ffd9d..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);
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/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/block/bio.c b/block/bio.c
index 655c901..07f287b 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -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/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 45af0fe..aaf2f81 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -143,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/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/android/binder.c b/drivers/android/binder.c
index fcf85be..256a1d5 100644
--- a/drivers/android/binder.c
+++ b/drivers/android/binder.c
@@ -1153,6 +1153,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;
@@ -2102,6 +2106,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.
@@ -4183,12 +4207,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);
@@ -4258,15 +4290,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(
diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
index e026894..9057411 100644
--- a/drivers/android/binder_alloc.c
+++ b/drivers/android/binder_alloc.c
@@ -186,12 +186,12 @@
 }
 
 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 binder_lru_page *page;
+	struct vm_area_struct *vma = NULL;
 	struct mm_struct *mm = NULL;
 	bool need_mm = false;
 
@@ -215,17 +215,12 @@
 		}
 	}
 
-	if (!vma && need_mm)
-		mm = get_task_mm(alloc->tsk);
+	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 (!vma && need_mm) {
@@ -442,7 +437,7 @@
 	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);
 
@@ -483,7 +478,7 @@
 err_alloc_buf_struct_failed:
 	binder_update_page_range(alloc, 0,
 				 (void *)PAGE_ALIGN((uintptr_t)buffer->data),
-				 end_page_addr, NULL);
+				 end_page_addr);
 	return ERR_PTR(-ENOMEM);
 }
 
@@ -567,8 +562,7 @@
 				   alloc->pid, buffer->data,
 				   prev->data, next->data);
 		binder_update_page_range(alloc, 0, buffer_start_page(buffer),
-					 buffer_start_page(buffer) + PAGE_SIZE,
-					 NULL);
+					 buffer_start_page(buffer) + PAGE_SIZE);
 	}
 	list_del(&buffer->entry);
 	kfree(buffer);
@@ -605,8 +599,7 @@
 
 	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;
@@ -720,6 +713,8 @@
 	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;
 
@@ -795,6 +790,8 @@
 		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",
@@ -889,7 +886,6 @@
 void binder_alloc_vma_close(struct binder_alloc *alloc)
 {
 	WRITE_ONCE(alloc->vma, NULL);
-	WRITE_ONCE(alloc->vma_vm_mm, NULL);
 }
 
 /**
@@ -926,9 +922,9 @@
 	page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE;
 	vma = alloc->vma;
 	if (vma) {
-		mm = get_task_mm(alloc->tsk);
-		if (!mm)
-			goto err_get_task_mm_failed;
+		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;
 	}
@@ -964,7 +960,7 @@
 
 err_down_write_mmap_sem_failed:
 	mmput_async(mm);
-err_get_task_mm_failed:
+err_mmget:
 err_page_already_freed:
 	mutex_unlock(&alloc->mutex);
 err_get_alloc_mutex_failed:
@@ -1003,7 +999,6 @@
  */
 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);
diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h
index a3a3602..2dd33b6 100644
--- a/drivers/android/binder_alloc.h
+++ b/drivers/android/binder_alloc.h
@@ -100,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;
diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h
index 76e3b9c..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),
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/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/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/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/property.c b/drivers/base/property.c
index 06f6668..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;
 };
@@ -817,6 +818,7 @@
 void device_remove_properties(struct device *dev)
 {
 	struct fwnode_handle *fwnode;
+	struct property_set *pset;
 
 	fwnode = dev_fwnode(dev);
 	if (!fwnode)
@@ -826,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);
 
@@ -863,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/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/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/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/char/adsprpc.c b/drivers/char/adsprpc.c
index 89bf7c6..7de9b79c 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
@@ -61,6 +65,7 @@
 #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)
 
@@ -76,6 +81,9 @@
 #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) \
@@ -176,6 +184,7 @@
 	struct overlap **overps;
 	struct smq_msg msg;
 	uint32_t *crc;
+	unsigned int magic;
 };
 
 struct fastrpc_ctx_lst {
@@ -184,6 +193,7 @@
 };
 
 struct fastrpc_smmu {
+	struct device *dev;
 	struct dma_iommu_mapping *mapping;
 	int cb;
 	int enabled;
@@ -220,7 +230,10 @@
 	int ssrcount;
 	void *handle;
 	int prevssrcount;
+	int issubsystemup;
 	int vmid;
+	int ramdumpenabled;
+	void *remoteheap_ramdump_dev;
 	struct fastrpc_glink_info link;
 };
 
@@ -231,6 +244,7 @@
 	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;
@@ -252,7 +266,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;
@@ -338,7 +352,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)
@@ -362,7 +376,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);
@@ -375,7 +389,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);
@@ -390,31 +404,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;
@@ -422,10 +462,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;
 
@@ -464,51 +522,75 @@
 
 static void fastrpc_mmap_free(struct fastrpc_mmap *map)
 {
+	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->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);
+	} else {
+		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 (map->flags == ADSP_MMAP_HEAP_ADDR ||
+				map->flags == ADSP_MMAP_REMOTE_HEAP_ADDR) {
 
-	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 (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);
 	}
-	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);
 	kfree(map);
 }
 
@@ -516,15 +598,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;
 
@@ -540,47 +624,58 @@
 	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 {
+		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 ||
@@ -590,37 +685,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);
@@ -636,7 +732,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);
@@ -656,21 +752,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));
 	}
@@ -704,7 +800,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;
 
@@ -759,7 +855,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) {
@@ -788,7 +884,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));\
@@ -797,8 +894,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)
@@ -811,7 +908,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;
 
@@ -822,7 +919,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;
 
@@ -836,7 +933,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;
@@ -864,6 +961,7 @@
 	ctx->pid = current->pid;
 	ctx->tgid = fl->tgid;
 	init_completion(&ctx->work);
+	ctx->magic = FASTRPC_CTX_MAGIC;
 
 	spin_lock(&fl->hlock);
 	hlist_add_head(&ctx->hn, &clst->pending);
@@ -899,6 +997,7 @@
 	for (i = 0; i < nbufs; ++i)
 		fastrpc_mmap_free(ctx->maps[i]);
 	fastrpc_buf_free(ctx->buf, 1);
+	ctx->magic = 0;
 	kfree(ctx);
 }
 
@@ -947,11 +1046,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);
@@ -963,7 +1062,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);
@@ -983,7 +1082,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);
@@ -1015,13 +1114,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))
@@ -1089,9 +1188,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;
@@ -1199,7 +1299,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;
@@ -1246,7 +1346,7 @@
 				goto bail;
 		} else {
 			fastrpc_mmap_free(ctx->maps[i]);
-			ctx->maps[i] = 0;
+			ctx->maps[i] = NULL;
 		}
 	}
 	if (inbufs + outbufs + handles) {
@@ -1259,7 +1359,7 @@
 		}
 	}
 	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:
@@ -1350,7 +1450,7 @@
 	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 = fl->tgid;
@@ -1401,7 +1501,7 @@
 				   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;
@@ -1487,8 +1587,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;
@@ -1499,10 +1598,16 @@
 				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)
@@ -1516,8 +1621,8 @@
 		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,
@@ -1547,6 +1652,7 @@
 			if (err)
 				goto bail;
 		}
+
 		inbuf.pageslen = 1;
 		VERIFY(err, !fastrpc_mmap_create(fl, init->memfd, 0,
 				init->mem, init->memlen, mflags, &mem));
@@ -1587,7 +1693,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 = strlen(proc_name)+1;
+		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)));
@@ -1597,8 +1775,14 @@
 		err = -ENOTTY;
 	}
 bail:
-	if (mem && err)
+	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);
+	}
 	if (file)
 		fastrpc_mmap_free(file);
 	return err;
@@ -1614,7 +1798,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;
@@ -1623,8 +1807,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)));
@@ -1670,13 +1854,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;
 }
 
@@ -1704,11 +1961,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);
+		}
+	} while (match);
+bail:
+	if (err && match)
+		fastrpc_mmap_add(match);
 	return err;
 }
 
@@ -1721,7 +2033,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)
@@ -1740,15 +2052,16 @@
 				 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));
@@ -1770,7 +2083,7 @@
 	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);
@@ -1803,6 +2116,7 @@
 		if (err)
 			goto bail;
 		chan->session[0].dev = me->dev;
+		chan->session[0].smmu.dev = me->dev;
 	}
 
 	*session = &chan->session[idx];
@@ -1810,78 +2124,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)
 {
 }
 
-static int fastrpc_search_ctx(uint64_t rctx)
-{
-	struct fastrpc_apps *me = &gfa;
-	struct fastrpc_file *fl;
-	struct hlist_node *n, *m;
-	struct smq_invoke_ctx *ictx = NULL;
-	struct smq_invoke_ctx *ctx;
-	int bfound = 0;
-
-	ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rctx));
-	if (!ctx)
-		return bfound;
-
-	spin_lock(&me->hlock);
-	hlist_for_each_entry_safe(fl, n, &me->drivers, hn) {
-		if (ctx->fl != fl)
-			continue;
-		spin_lock(&fl->hlock);
-		hlist_for_each_entry_safe(ictx, m, &fl->clst.pending, hn) {
-			if (ptr_to_uint64(ictx) == rctx) {
-				bfound = 1;
-				break;
-			}
-		}
-		hlist_for_each_entry_safe(ictx, m, &fl->clst.interrupted, hn) {
-			if (ptr_to_uint64(ictx) == rctx) {
-				bfound = 1;
-				break;
-			}
-		}
-		spin_unlock(&fl->hlock);
-		if (bfound)
-			break;
-	}
-	spin_unlock(&me->hlock);
-	return bfound;
-}
-
-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 bfound = 0;
+	struct smq_invoke_ctx *ctx;
+	int err = 0;
 
-	if (!rsp || (size < sizeof(*rsp)))
+	VERIFY(err, (rsp && size >= sizeof(*rsp)));
+	if (err)
 		goto bail;
 
-	bfound = fastrpc_search_ctx((uint64_t)(rsp->ctx & ~1));
-	if (!bfound) {
-		pr_err("adsprpc: invalid context %pK\n", (void *)rsp->ctx);
+	ctx = (struct smq_invoke_ctx *)(uint64_to_ptr(rsp->ctx & ~1));
+	VERIFY(err, (ctx && ctx->magic == FASTRPC_CTX_MAGIC));
+	if (err)
 		goto bail;
-	}
 
-	rsp->ctx = rsp->ctx & ~1;
-	context_notify_user(uint64_to_ptr(rsp->ctx), rsp->retval);
+	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;
@@ -1902,7 +2181,7 @@
 	case GLINK_REMOTE_DISCONNECTED:
 		if (me->channel[cid].chan) {
 			fastrpc_glink_close(me->channel[cid].chan, cid);
-			me->channel[cid].chan = 0;
+			me->channel[cid].chan = NULL;
 		}
 		break;
 	default:
@@ -1936,7 +2215,7 @@
 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)
@@ -1982,7 +2261,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;
 }
@@ -2088,8 +2367,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;
@@ -2106,9 +2388,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;
@@ -2219,12 +2501,20 @@
 	if (err)
 		goto bail;
 	cid = fl->cid;
+	if (me->channel[cid].ssrcount !=
+				 me->channel[cid].prevssrcount) {
+		if (!me->channel[cid].issubsystemup) {
+			VERIFY(err, 0);
+			if (err)
+				goto bail;
+		}
+	}
 	VERIFY(err, cid >= 0 && cid < NUM_CHANNELS);
 	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;
@@ -2236,7 +2526,7 @@
 			 wait_for_completion_timeout(&me->channel[cid].workport,
 						RPC_TIMEOUT));
 		if (err) {
-			me->channel[cid].chan = 0;
+			me->channel[cid].chan = NULL;
 			goto bail;
 		}
 		kref_init(&me->channel[cid].kref);
@@ -2249,6 +2539,8 @@
 					 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;
 		}
@@ -2263,10 +2555,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,
@@ -2297,7 +2589,7 @@
 	int err = 0;
 	uint32_t cid;
 
-	VERIFY(err, fl != 0);
+	VERIFY(err, fl != NULL);
 	if (err)
 		goto bail;
 	if (fl->cid == -1) {
@@ -2371,8 +2663,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) {
@@ -2398,7 +2690,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,
@@ -2407,20 +2699,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,
@@ -2447,30 +2739,30 @@
 		}
 		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)));
@@ -2478,13 +2770,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;
@@ -2496,11 +2788,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));
@@ -2523,6 +2819,7 @@
 {
 	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);
@@ -2530,14 +2827,24 @@
 	if (code == SUBSYS_BEFORE_SHUTDOWN) {
 		mutex_lock(&me->smd_mutex);
 		ctx->ssrcount++;
+		ctx->issubsystemup = 0;
 		if (ctx->chan) {
 			fastrpc_glink_close(ctx->chan, cid);
-			ctx->chan = 0;
+			ctx->chan = NULL;
 			pr_info("'restart notifier: closed /dev/%s c %d %d'\n",
 				 gcinfo[cid].name, MAJOR(me->dev_no), cid);
 		}
 		mutex_unlock(&me->smd_mutex);
+		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;
@@ -2568,7 +2875,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++) {
@@ -2612,7 +2920,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,
@@ -2699,18 +3007,17 @@
 		if (chan->chan) {
 			kref_put_mutex(&chan->kref,
 				fastrpc_channel_close, &me->smd_mutex);
-			chan->chan = 0;
+			chan->chan = NULL;
 		}
 		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;
 			}
 		}
 	}
@@ -2728,7 +3035,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));
@@ -2763,6 +3070,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 2e8bfc2..43edf71 100644
--- a/drivers/char/adsprpc_shared.h
+++ b/drivers/char/adsprpc_shared.h
@@ -58,6 +58,7 @@
 /* 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)
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..40bfd74 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)
 {
@@ -152,6 +152,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 +208,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 +1062,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 +1076,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 ee76d39..2df62e4 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -1662,6 +1662,7 @@
 	struct diag_msg_mask_t *src_mask = NULL;
 	struct diag_msg_mask_t *dest_mask = NULL;
 	struct diag_ssid_range_t range;
+	int mask_size = 0;
 
 	if (!src || !dest)
 		return -EINVAL;
@@ -1682,8 +1683,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++;
 	}
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 7e3fe90..dabb1f4 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -218,6 +218,7 @@
 	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];
@@ -240,6 +241,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 +268,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 +318,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_core.c b/drivers/char/diag/diagchar_core.c
index 543f0a2..54e6486 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1304,11 +1304,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 +1424,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 +1539,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 +1571,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)
@@ -1999,8 +2001,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;
@@ -2768,10 +2771,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);
@@ -2835,7 +2841,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);
@@ -3010,7 +3018,9 @@
 			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;
@@ -3028,7 +3038,9 @@
 		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,
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index b59f245..fc67c1a 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -240,10 +240,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 +260,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 +320,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 +330,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 +349,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 +411,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 +422,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)
@@ -926,7 +969,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 +980,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,
@@ -956,7 +1000,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 +1022,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;
 	}
 
@@ -988,13 +1032,15 @@
 		reg_item = container_of(temp_entry, struct diag_cmd_reg_t,
 								entry);
 		if (info) {
-			if (MD_PERIPHERAL_MASK(reg_item->proc) &
-				info->peripheral_mask)
+			if ((MD_PERIPHERAL_MASK(reg_item->proc) &
+				info->peripheral_mask) ||
+				(MD_PERIPHERAL_PD_MASK(reg_item->proc) &
+				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 +1056,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 +1075,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 +1086,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 +1097,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 +1124,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 +1133,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 +1142,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 +1153,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 +1173,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 +1186,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 +1205,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 +1225,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 +1308,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 +1581,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);
@@ -1625,15 +1671,14 @@
 	case TYPE_CMD:
 		if (peripheral >= 0 && peripheral < NUM_PERIPHERALS) {
 			diagfwd_write_done(peripheral, type, num);
-		} else if (peripheral == APPS_DATA) {
+		}
+		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:
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_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 710271e..d8c107e 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);
diff --git a/drivers/char/diag/diagfwd_peripheral.c b/drivers/char/diag/diagfwd_peripheral.c
index e00a61d..4d4b660 100644
--- a/drivers/char/diag/diagfwd_peripheral.c
+++ b/drivers/char/diag/diagfwd_peripheral.c
@@ -222,12 +222,22 @@
 	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;
@@ -836,6 +846,7 @@
 	uint8_t peripheral;
 	uint8_t type;
 	struct diagfwd_info *fwd_info = NULL;
+	int transport = 0;
 
 	diag_socket_exit();
 
@@ -857,7 +868,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,
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/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c
index 93a08db..ec4c83e 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,15 @@
 #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 <dt-bindings/clock/qcom,cpucc-sdm845.h>
 
 #include "common.h"
 #include "clk-regmap.h"
-#include "clk-rcg.h"
 #include "clk-voter.h"
 #include "clk-debug.h"
 
@@ -50,454 +44,74 @@
 #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 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))
-
-/* 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,
-};
+#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))
 
 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;
 };
 
-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,
@@ -559,23 +173,6 @@
 	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);
-
-	/* 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);
-
-	return 0;
-}
-
 const struct clk_ops clk_ops_cpu_osm = {
 	.round_rate = clk_osm_round_rate,
 	.list_rate = clk_osm_list_rate,
@@ -608,11 +205,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 +223,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);
@@ -637,7 +235,6 @@
 
 
 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,
@@ -668,14 +265,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],
 };
 
@@ -727,8 +327,33 @@
 	},
 };
 
+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,
+		.ops = &clk_dummy_ops,
+	},
+};
+
+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,
+		.ops = &clk_dummy_ops,
+	},
+};
+
 static struct clk_osm perfcl_clk = {
 	.cluster_num = 2,
+	.max_core_count = 4,
 	.hw.init = &osm_clks_init[2],
 };
 
@@ -781,16 +406,11 @@
 	},
 };
 
-/*
- * 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 +421,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 +442,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);
@@ -894,11 +517,11 @@
 static void
 osm_set_index(struct clk_osm *c, unsigned int index, unsigned int num)
 {
-	clk_osm_write_reg(c, index, DCVS_PERF_STATE_DESIRED_REG(num, is_v2),
-							OSM_BASE);
+	clk_osm_write_reg(c, index,
+			DCVS_PERF_STATE_DESIRED_REG(num, is_sdm845v1));
 
 	/* Make sure the write goes through before proceeding */
-	clk_osm_mb(c, OSM_BASE);
+	clk_osm_mb(c);
 }
 
 static int
@@ -921,7 +544,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 +570,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 +598,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 +622,6 @@
 	}
 
 	policy->driver_data = c;
-
-	clk_osm_enable(&parent->hw);
-	udelay(300);
-
 	return 0;
 
 err:
@@ -1036,704 +655,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 +670,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 +688,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 +702,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 +723,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 +799,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 +829,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,196 +846,6 @@
 	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;
@@ -2123,494 +889,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 +901,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 +917,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 +933,43 @@
 		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;
+	u32 val;
 	int num_clks = ARRAY_SIZE(osm_qcom_clk_hws);
 	struct clk *ext_xo_clk, *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 cpu_cycle_counter_cb cb = {
 		.get_cpu_cycle_counter = clk_osm_get_cpu_cycle_counter,
 	};
@@ -3062,8 +985,12 @@
 		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"))
+		clk_cpu_osm_driver_sdm670_fixup();
 
 	clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
 								GFP_KERNEL);
@@ -3085,282 +1012,34 @@
 		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);
@@ -3369,6 +1048,9 @@
 
 	/* 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,34 +1069,12 @@
 
 	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");
 
 	populate_opp_table(pdev);
 
@@ -3443,6 +1103,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/debugcc-sdm845.c b/drivers/clk/qcom/debugcc-sdm845.c
index be84b46..45bd556 100644
--- a/drivers/clk/qcom/debugcc-sdm845.c
+++ b/drivers/clk/qcom/debugcc-sdm845.c
@@ -247,7 +247,6 @@
 	"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",
@@ -720,8 +719,6 @@
 			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/gcc-sdm845.c b/drivers/clk/qcom/gcc-sdm845.c
index 4142dd5..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
 
@@ -4071,6 +4068,7 @@
 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" },
 	{ }
 };
@@ -4270,7 +4268,8 @@
 	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();
@@ -4288,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))
@@ -4333,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/gpucc-sdm845.c b/drivers/clk/qcom/gpucc-sdm845.c
index 35a23f7..55d14ff 100644
--- a/drivers/clk/qcom/gpucc-sdm845.c
+++ b/drivers/clk/qcom/gpucc-sdm845.c
@@ -348,19 +348,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,
@@ -545,7 +532,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,
diff --git a/drivers/clk/qcom/videocc-sdm845.c b/drivers/clk/qcom/videocc-sdm845.c
index f8fdf3f..79cc5cf 100644
--- a/drivers/clk/qcom/videocc-sdm845.c
+++ b/drivers/clk/qcom/videocc-sdm845.c
@@ -118,7 +118,7 @@
 	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(364800000, 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),
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/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/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/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index fb11acd..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)
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 67f2d0c..630cda2 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;
@@ -1464,7 +1474,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;
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index a458475..b3364b4 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;
 };
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..17b19a6 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)
@@ -318,6 +319,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..e23c36c 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
@@ -472,6 +473,7 @@
 };
 
 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..64deb00 100644
--- a/drivers/crypto/ccp/ccp-ops.c
+++ b/drivers/crypto/ccp/ccp-ops.c
@@ -779,6 +779,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 +804,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 +828,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/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 94b3c17..958fb91 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -903,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;
 }
 
@@ -1066,6 +1067,7 @@
 	handle->sha_ctxt.first_blk = 0;
 
 	kzfree(k_src);
+	qcedev_areq->sha_req.sreq.src = NULL;
 	return err;
 }
 
@@ -1220,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;
@@ -1231,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;
@@ -1241,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/devfreq/arm-memlat-mon.c b/drivers/devfreq/arm-memlat-mon.c
index f4f503e..9943c8c 100644
--- a/drivers/devfreq/arm-memlat-mon.c
+++ b/drivers/devfreq/arm-memlat-mon.c
@@ -36,6 +36,7 @@
 	INST_IDX,
 	CM_IDX,
 	CYC_IDX,
+	STALL_CYC_IDX,
 	NUM_EVENTS
 };
 #define INST_EV		0x08
@@ -105,12 +106,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)
@@ -130,7 +138,10 @@
 
 	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;
+		}
 	}
 }
 
@@ -149,6 +160,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))
@@ -182,6 +194,7 @@
 	struct perf_event *pevent;
 	struct perf_event_attr *attr;
 	int err, i;
+	unsigned int event_id;
 	struct cpu_pmu_stats *cpustats = to_cpustats(cpu_grp, cpu);
 
 	/* Allocate an attribute for event initialization */
@@ -190,7 +203,11 @@
 		return -ENOMEM;
 
 	for (i = 0; i < ARRAY_SIZE(cpustats->events); i++) {
-		attr->config = cpu_grp->event_ids[i];
+		event_id = cpu_grp->event_ids[i];
+		if (!event_id)
+			continue;
+
+		attr->config = event_id;
 		pevent = perf_event_create_kernel_counter(attr, cpu, NULL,
 							  NULL, NULL);
 		if (IS_ERR(pevent))
@@ -348,6 +365,13 @@
 	}
 	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;
+
 	for_each_cpu(cpu, &cpu_grp->cpus)
 		to_devstats(cpu_grp, cpu)->id = cpu;
 
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..a1d9b50 100644
--- a/drivers/devfreq/governor_bw_hwmon.c
+++ b/drivers/devfreq/governor_bw_hwmon.c
@@ -820,11 +820,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 +836,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 +866,7 @@
 		if (ret) {
 			dev_err(df->dev.parent,
 				"Unable to resume HW monitor (%d)\n", ret);
-			return ret;
+			goto out;
 		}
 		break;
 
@@ -874,7 +876,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 +888,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 = {
diff --git a/drivers/devfreq/governor_memlat.c b/drivers/devfreq/governor_memlat.c
index 1a8ef1f..9688502 100644
--- a/drivers/devfreq/governor_memlat.c
+++ b/drivers/devfreq/governor_memlat.c
@@ -35,6 +35,7 @@
 
 struct memlat_node {
 	unsigned int ratio_ceil;
+	unsigned int stall_floor;
 	bool mon_started;
 	bool already_zero;
 	struct list_head list;
@@ -247,9 +248,11 @@
 					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 <= 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;
@@ -275,9 +278,11 @@
 }
 
 gov_attr(ratio_ceil, 1U, 10000U);
+gov_attr(stall_floor, 0U, 100U);
 
 static struct attribute *dev_attr[] = {
 	&dev_attr_ratio_ceil.attr,
+	&dev_attr_stall_floor.attr,
 	&dev_attr_freq_map.attr,
 	NULL,
 };
diff --git a/drivers/devfreq/governor_memlat.h b/drivers/devfreq/governor_memlat.h
index 8c533ee..f2ba534 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 {
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/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/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
index 334278b..7eb0458 100644
--- a/drivers/esoc/esoc-mdm-4x.c
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -482,7 +482,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 +490,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)
@@ -748,6 +757,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 +828,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 +917,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 +978,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.h b/drivers/esoc/esoc.h
index 9fc3192..e6794c2 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
@@ -66,6 +67,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;
@@ -83,11 +85,13 @@
  * 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/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/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/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c
index 82c193e..afe0480 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);
 
@@ -189,9 +184,9 @@
 	if (plane->state->rotation & DRM_ROTATE_MASK)
 		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/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 4e16dff..33778bf 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -1871,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))
@@ -1915,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/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/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_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..afa3d01 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);
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_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/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index 2894e82..b9b996a 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -604,7 +604,7 @@
 static void dp_catalog_ctrl_config_misc(struct dp_catalog_ctrl *ctrl,
 					u32 cc, u32 tb)
 {
-	u32 misc_val;
+	u32 misc_val = cc;
 	struct dp_catalog_private *catalog;
 	void __iomem *base;
 
@@ -616,8 +616,6 @@
 	dp_catalog_get_priv(ctrl);
 	base = catalog->io->ctrl_io.base;
 
-	misc_val = dp_read(base + DP_MISC1_MISC0);
-	misc_val |= cc;
 	misc_val |= (tb << 5);
 	misc_val |= BIT(0); /* Configure clock to synchronous mode */
 
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index ea07d15..7fbc63a 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -88,6 +88,7 @@
 	struct workqueue_struct *hdcp_workqueue;
 	struct delayed_work hdcp_cb_work;
 	struct mutex hdcp_mutex;
+	struct mutex session_lock;
 	int hdcp_status;
 };
 
@@ -500,6 +501,8 @@
 
 	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);
 		return -EINVAL;
 	}
 
@@ -634,6 +637,7 @@
 
 	dp->ctrl->push_idle(dp->ctrl);
 	dp->ctrl->off(dp->ctrl);
+	dp->power_on = false;
 }
 
 static int dp_display_usbpd_disconnect_cb(struct device *dev)
@@ -662,6 +666,8 @@
 
 	rc = dp_display_send_hpd_notification(dp, false);
 
+	mutex_lock(&dp->session_lock);
+
 	/* if cable is disconnected, reset psm_enabled flag */
 	if (!dp->usbpd->alt_mode_cfg_done)
 		dp->link->psm_enabled = false;
@@ -670,6 +676,8 @@
 		dp_display_clean(dp);
 
 	dp_display_host_deinit(dp);
+
+	mutex_unlock(&dp->session_lock);
 end:
 	return rc;
 }
@@ -760,6 +768,7 @@
 	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);
 }
 
@@ -787,6 +796,8 @@
 		goto error;
 	}
 
+	mutex_init(&dp->session_lock);
+
 	dp->parser = dp_parser_get(dp->pdev);
 	if (IS_ERR(dp->parser)) {
 		rc = PTR_ERR(dp->parser);
@@ -890,6 +901,7 @@
 	dp_parser_put(dp->parser);
 error_parser:
 	dp_usbpd_put(dp->usbpd);
+	mutex_destroy(&dp->session_lock);
 error:
 	return rc;
 }
@@ -897,20 +909,20 @@
 static int dp_display_set_mode(struct dp_display *dp_display,
 		struct dp_display_mode *mode)
 {
-	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);
 	dp->panel->pinfo = mode->timing;
 	dp->panel->init_info(dp->panel);
-error:
-	return rc;
+	mutex_unlock(&dp->session_lock);
+
+	return 0;
 }
 
 static int dp_display_prepare(struct dp_display *dp)
@@ -925,37 +937,44 @@
 
 	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 setup, return\n");
-		return 0;
+		goto end;
 	}
 
 	rc = dp->ctrl->on(dp->ctrl);
 	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);
 
+	mutex_lock(&dp->session_lock);
+
+	if (!dp->power_on) {
+		pr_debug("Link not setup, return\n");
+		goto end;
+	}
+
 	if (dp->audio_supported) {
 		dp->audio->bw_code = dp->link->link_params.bw_code;
 		dp->audio->lane_count = dp->link->link_params.lane_count;
@@ -973,23 +992,30 @@
 		queue_delayed_work(dp->hdcp_workqueue,
 				&dp->hdcp_cb_work, HZ / 2);
 	}
+
 end:
-	return rc;
+	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;
 
@@ -1003,33 +1029,38 @@
 		dp->link->psm_config(dp->link, &dp->panel->link_info, true);
 
 	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);
+
+	if (!dp->power_on || !dp->core_initialized) {
+		pr_debug("Link already powered off, return\n");
+		goto end;
+	}
 
 	dp->ctrl->off(dp->ctrl);
 
 	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)
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 254343a..170734f 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -306,7 +306,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;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index 53570f5..eb78e71 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -78,11 +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 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);
 
diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
index c8da99a..f7201ec 100644
--- a/drivers/gpu/drm/msm/dp/dp_parser.c
+++ b/drivers/gpu/drm/msm/dp/dp_parser.c
@@ -240,6 +240,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++) {
diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
index eb787fa..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) {
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index 790ee22..a51c1a7 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -785,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;
@@ -885,7 +889,6 @@
 	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)
@@ -949,20 +952,21 @@
 		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) ?
@@ -971,19 +975,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) ?
@@ -997,6 +1002,9 @@
 	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,
@@ -2483,6 +2491,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))
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 4781299..0e5c0bd 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,7 @@
 #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
 
 /**
  * enum dsi_power_state - defines power states for dsi controller.
@@ -225,6 +228,7 @@
 	struct drm_gem_object *tx_cmd_buf;
 	u32 cmd_buffer_size;
 	u32 cmd_buffer_iova;
+	u32 cmd_len;
 	void *vaddr;
 
 	/* Debug Information */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
index 96ed47e..2f0d25f 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_defs.h
@@ -355,6 +355,7 @@
  * @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.
  */
@@ -373,7 +374,7 @@
 	bool v_sync_polarity;
 
 	u32 refresh_rate;
-
+	u64 clk_rate_hz;
 	bool dsc_enabled;
 	struct msm_display_dsc_info *dsc;
 };
@@ -500,6 +501,7 @@
  * @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
@@ -513,6 +515,7 @@
 	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;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index d71a5f21..8bca4e4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1707,6 +1707,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
@@ -3307,6 +3311,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;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
index af721eb..280c754 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.c
@@ -297,7 +297,7 @@
 
 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;
@@ -318,6 +318,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));
@@ -631,10 +632,17 @@
 	struct dsi_display_ctrl *m_ctrl, *ctrl;
 	int i, rc = 0;
 
-	if (!connector || !connector->state->best_encoder)
+	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;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
index 9700e68..828e65d 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_drm.h
@@ -76,10 +76,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
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index 6718156..956db0e 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -497,6 +497,9 @@
 		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 +686,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);
@@ -2512,8 +2530,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");
 
@@ -2635,6 +2655,15 @@
 				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:
@@ -2725,20 +2754,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);
@@ -3075,6 +3092,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/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index cbcf580..28f2e7c 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -555,6 +555,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) {
@@ -610,8 +615,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 2f9571b..c0f796c 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -57,8 +57,6 @@
 #define MSM_VERSION_MINOR	2
 #define MSM_VERSION_PATCHLEVEL	0
 
-#define TEARDOWN_DEADLOCK_RETRY_MAX 5
-
 static void msm_fb_output_poll_changed(struct drm_device *dev)
 {
 	struct msm_drm_private *priv = NULL;
@@ -939,9 +937,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);
 	}
 }
 
@@ -1930,6 +1928,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 */
@@ -1941,6 +1961,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 e19d1db..8554574 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,6 +114,8 @@
 	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,
@@ -404,6 +406,7 @@
  * @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
  */
@@ -413,6 +416,7 @@
 	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;
 };
@@ -431,6 +435,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
@@ -450,6 +455,7 @@
 
 	uint32_t max_width;
 	uint32_t max_height;
+	uint64_t clk_rate;
 
 	bool is_primary;
 	bool is_te_using_watchdog_timer;
@@ -609,6 +615,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_prop.c b/drivers/gpu/drm/msm/msm_prop.c
index 033be6e..ce84b7a 100644
--- a/drivers/gpu/drm/msm/msm_prop.c
+++ b/drivers/gpu/drm/msm/msm_prop.c
@@ -393,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..92d1865 100644
--- a/drivers/gpu/drm/msm/msm_smmu.c
+++ b/drivers/gpu/drm/msm/msm_smmu.c
@@ -455,6 +455,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 +466,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 e9ffb96..2c5b7ea 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);
@@ -85,6 +89,8 @@
 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; \
@@ -108,11 +114,11 @@
 	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,
@@ -667,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) {
@@ -1214,9 +1220,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);
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.h b/drivers/gpu/drm/msm/sde/sde_color_processing.h
index aff07ef..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,8 @@
 enum sde_memcolor_type {
 	MEMCOLOR_SKIN = 0,
 	MEMCOLOR_SKY,
-	MEMCOLOR_FOLIAGE
+	MEMCOLOR_FOLIAGE,
+	MEMCOLOR_MAX
 };
 
 /*
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.c b/drivers/gpu/drm/msm/sde/sde_connector.c
index d83f476..88dd60da 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.c
+++ b/drivers/gpu/drm/msm/sde/sde_connector.c
@@ -1088,6 +1088,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
diff --git a/drivers/gpu/drm/msm/sde/sde_connector.h b/drivers/gpu/drm/msm/sde/sde_connector.h
index 4018441..005d050 100644
--- a/drivers/gpu/drm/msm/sde/sde_connector.h
+++ b/drivers/gpu/drm/msm/sde/sde_connector.h
@@ -134,11 +134,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
@@ -319,7 +320,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
@@ -335,7 +336,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
@@ -343,7 +344,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
@@ -598,4 +599,15 @@
  */
 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);
+
 #endif /* _SDE_CONNECTOR_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_core_perf.c b/drivers/gpu/drm/msm/sde/sde_core_perf.c
index 9ddca8c..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) {
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 23ca2e9..6f76e20 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1124,7 +1124,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)
@@ -1143,10 +1143,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);
 
@@ -1600,6 +1599,8 @@
 	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
@@ -1609,7 +1610,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);
 	}
 
@@ -1682,6 +1683,11 @@
 
 	SDE_DEBUG("SMMU State:%d, type:%d ops:%x\n", smmu_state->state,
 			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;
 }
 
@@ -1725,6 +1731,9 @@
 		SDE_ERROR("Error:scm_call2, vmid (%lld): ret%d\n",
 				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;
@@ -1750,15 +1759,17 @@
 	}
 
 	if (sde_crtc->scl3_lut_cfg->is_configured) {
-		SDE_DEBUG("lut already configured\n");
+		SDE_DEBUG("%s: lut already configured\n", sde_crtc->name);
 		return 0;
 	}
 
 	lut_data = msm_property_get_blob(&sde_crtc->property_info,
 			&cstate->property_state, &len, lut_idx);
 	if (!lut_data || !len) {
-		SDE_ERROR("lut(%d): no data, len(%zu)\n", lut_idx, len);
-		return -ENODATA;
+		SDE_DEBUG("%s: lut(%d): cleared: %pK, %zu\n", sde_crtc->name,
+				lut_idx, lut_data, len);
+		lut_data = NULL;
+		len = 0;
 	}
 
 	cfg = sde_crtc->scl3_lut_cfg;
@@ -1782,8 +1793,7 @@
 		break;
 	}
 
-	if (cfg->dir_lut && cfg->cir_lut && cfg->sep_lut)
-		cfg->is_configured = true;
+	cfg->is_configured = cfg->dir_lut && cfg->cir_lut && cfg->sep_lut;
 
 	return ret;
 }
@@ -1819,6 +1829,9 @@
 	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 */
@@ -1903,6 +1916,9 @@
 
 error:
 	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;
 }
 
@@ -2530,6 +2546,11 @@
 	}
 
 	count = ds_data.num_dest_scaler;
+	if (!count) {
+		SDE_DEBUG("no ds data available\n");
+		return 0;
+	}
+
 	if (!sde_crtc->num_mixers || count > sde_crtc->num_mixers ||
 		(count && (count != sde_crtc->num_mixers) &&
 		!(ds_data.ds_cfg[0].flags & SDE_DRM_DESTSCALER_PU_ENABLE))) {
@@ -3848,7 +3869,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);
@@ -3893,11 +3915,14 @@
 	/**
 	 * 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;
@@ -3933,6 +3958,22 @@
 	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;
@@ -3940,9 +3981,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);
@@ -4461,6 +4499,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
@@ -4505,7 +4594,7 @@
 		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);
+			0x0, 0, INR_OPEN_MAX, 0, CRTC_PROP_OUTPUT_FENCE);
 
 	msm_property_install_range(&sde_crtc->property_info,
 			"output_fence_offset", 0x0, 0, 1, 0,
@@ -5367,6 +5456,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);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 59bfc47..704095a 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -280,6 +280,7 @@
 	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;
@@ -711,4 +712,14 @@
  */
 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);
+
 #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 7c600ca..7d1e4bf 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -194,7 +194,7 @@
  *				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
@@ -241,7 +241,7 @@
 	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;
@@ -559,6 +559,7 @@
 	}
 
 	hw_res->topology = sde_enc->mode_info.topology;
+	hw_res->is_primary = sde_enc->disp_info.is_primary;
 }
 
 void sde_encoder_destroy(struct drm_encoder *drm_enc)
@@ -697,6 +698,7 @@
 	struct sde_kms *sde_kms;
 	const struct drm_display_mode *mode;
 	struct drm_display_mode *adj_mode;
+	struct sde_connector *sde_conn = NULL;
 	int i = 0;
 	int ret = 0;
 
@@ -713,6 +715,7 @@
 	sde_kms = to_sde_kms(priv->kms);
 	mode = &crtc_state->mode;
 	adj_mode = &crtc_state->adjusted_mode;
+	sde_conn = to_sde_connector(conn_state->connector);
 	SDE_EVT32(DRMID(drm_enc));
 
 	/*
@@ -742,17 +745,43 @@
 		}
 	}
 
-	/* 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 (!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_enc->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 (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 (crtc_state->active)
+			topology = &sde_enc->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;
 		}
 	}
 
@@ -1243,13 +1272,9 @@
 
 static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
 {
-	enum sde_rm_topology_name topology;
-	struct drm_connector *drm_conn;
 	int i, ret = 0;
-	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
-	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC] = {NULL};
-	int pp_count = 0;
-	int dsc_count = 0;
+	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) {
@@ -1258,80 +1283,16 @@
 		return -EINVAL;
 	}
 
-	drm_conn = sde_enc->phys_encs[0]->connector;
-
-	topology = sde_connector_get_topology_name(drm_conn);
-	if (topology == SDE_RM_TOPOLOGY_NONE) {
-		SDE_ERROR_ENC(sde_enc, "topology not set yet\n");
-		return -EINVAL;
-	}
-
-	switch (topology) {
-	case SDE_RM_TOPOLOGY_SINGLEPIPE:
-	case SDE_RM_TOPOLOGY_SINGLEPIPE_DSC:
-		/* single PP */
-		hw_pp[0] = sde_enc->hw_pp[0];
-		hw_dsc[0] = sde_enc->hw_dsc[0];
-		pp_count = 1;
-		dsc_count = 1;
-		break;
-	case SDE_RM_TOPOLOGY_DUALPIPE_DSC:
-	case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC:
-	case SDE_RM_TOPOLOGY_DUALPIPE_DSCMERGE:
-		/* dual dsc */
-		for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
-			hw_dsc[i] = sde_enc->hw_dsc[i];
-			if (hw_dsc[i])
-				dsc_count++;
-		}
-		/* fall through */
-	case SDE_RM_TOPOLOGY_DUALPIPE:
-	case SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE:
-		/* dual pp */
-		for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
-			hw_pp[i] = sde_enc->hw_pp[i];
-			if (hw_pp[i])
-				pp_count++;
-		}
-		break;
-	default:
-		SDE_DEBUG_ENC(sde_enc, "Unexpected topology:%d\n", topology);
-		return -EINVAL;
-	};
-
-	SDE_EVT32(DRMID(&sde_enc->base), topology, pp_count, dsc_count);
-
-	if (pp_count > MAX_CHANNELS_PER_ENC ||
-		dsc_count > MAX_CHANNELS_PER_ENC) {
-		SDE_ERROR_ENC(sde_enc, "Wrong count pp:%d dsc:%d top:%d\n",
-				pp_count, dsc_count, topology);
-		return -EINVAL;
-	}
-
 	/* Disable DSC for all the pp's present in this topology */
-	for (i = 0; i < pp_count; i++) {
+	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[i]) {
-			SDE_ERROR_ENC(sde_enc, "null pp:%d top:%d cnt:%d\n",
-					i, topology, pp_count);
-			return -EINVAL;
-		}
+		if (hw_pp && hw_pp->ops.disable_dsc)
+			hw_pp->ops.disable_dsc(hw_pp);
 
-		if (hw_pp[i]->ops.disable_dsc)
-			hw_pp[i]->ops.disable_dsc(hw_pp[i]);
-	}
-
-	/* Disable DSC HW */
-	for (i = 0; i < dsc_count; i++) {
-
-		if (!hw_dsc[i]) {
-			SDE_ERROR_ENC(sde_enc, "null dsc:%d top:%d cnt:%d\n",
-					i, topology, dsc_count);
-			return -EINVAL;
-		}
-
-		if (hw_dsc[i]->ops.dsc_disable)
-			hw_dsc[i]->ops.dsc_disable(hw_dsc[i]);
+		if (hw_dsc && hw_dsc->ops.dsc_disable)
+			hw_dsc->ops.dsc_disable(hw_dsc);
 	}
 
 	return ret;
@@ -1491,6 +1452,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;
@@ -1710,7 +1709,7 @@
 		else
 			idle_timeout = sde_enc->idle_timeout;
 
-		if (!autorefresh_enabled)
+		if (!autorefresh_enabled && idle_timeout)
 			kthread_queue_delayed_work(
 				&disp_thread->worker,
 				&sde_enc->delayed_off_work,
@@ -1830,6 +1829,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);
@@ -1852,6 +1852,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);
@@ -1959,7 +1960,8 @@
 	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_kms->catalog->max_mixer_width,
+				sde_conn->display);
 		if (ret) {
 			SDE_ERROR_ENC(sde_enc,
 				"invalid topology for the mode\n");
@@ -2031,8 +2033,6 @@
 	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;
 }
 
 static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
@@ -2327,6 +2327,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,
@@ -2441,8 +2442,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;
 	}
 
@@ -2462,7 +2463,7 @@
 	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,
@@ -2502,6 +2503,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;
@@ -2610,6 +2625,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);
 
@@ -2637,8 +2675,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);
@@ -3787,6 +3824,7 @@
 	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);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index baf59b4..9a9ff86 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -40,6 +40,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 +48,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;
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 8813fd2..e5a4da4 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -117,6 +117,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
@@ -131,6 +132,8 @@
  * @is_autorefresh_enabled:	provides the autorefresh current
  *                              enable/disable state.
  * @get_line_count:		Obtain current vertical line count
+ * @wait_dma_trigger:		Returns true if lut dma has to trigger and wait
+ *                              unitl transaction is complete.
  */
 
 struct sde_encoder_phys_ops {
@@ -160,6 +163,7 @@
 	void (*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);
 
@@ -174,6 +178,7 @@
 	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);
+	bool (*wait_dma_trigger)(struct sde_encoder_phys *phys);
 };
 
 /**
@@ -463,6 +468,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.
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 4291098..9976f85 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -177,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,
@@ -1254,6 +1261,7 @@
 	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;
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 6a4348ba..a983b7c 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -591,6 +591,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;
@@ -942,8 +973,10 @@
 	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->wait_dma_trigger = sde_encoder_phys_vid_wait_dma_trigger;
 }
 
 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 12115756..d7084dd 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -458,10 +458,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 +848,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));
@@ -950,6 +958,30 @@
 }
 
 /**
+ * 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);
+}
+
+/**
  * sde_encoder_phys_wb_handle_post_kickoff - post-kickoff processing
  * @phys_enc:	Pointer to physical encoder
  */
@@ -1167,7 +1199,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");
@@ -1175,22 +1207,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);
@@ -1274,6 +1303,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_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 aea2c6b..b8b0967 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -122,6 +122,7 @@
 enum {
 	HW_OFF,
 	HW_LEN,
+	HW_DISP,
 	HW_PROP_MAX,
 };
 
@@ -166,7 +167,10 @@
 	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,
@@ -288,6 +292,7 @@
 	MIXER_LEN,
 	MIXER_PAIR_MASK,
 	MIXER_BLOCKS,
+	MIXER_DISP,
 	MIXER_PROP_MAX,
 };
 
@@ -424,7 +429,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,
@@ -433,6 +445,7 @@
 			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},
 };
@@ -475,6 +488,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[] = {
@@ -488,6 +502,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[] = {
@@ -1292,6 +1308,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);
@@ -1299,6 +1317,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)
@@ -1306,7 +1328,6 @@
 		if (sde_cfg->has_sbuf)
 			set_bit(SDE_CTL_SBUF, &ctl->features);
 	}
-
 end:
 	kfree(prop_value);
 	return rc;
@@ -1326,8 +1347,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, ds_count;
+	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) {
@@ -1349,8 +1371,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)
@@ -1398,8 +1418,16 @@
 	if (rc)
 		goto end;
 
-	for (i = 0, pp_idx = 0, dspp_idx = 0, ds_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;
@@ -1408,7 +1436,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",
@@ -1435,23 +1463,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;
-			mixer->ds = ds_count > 0 ? ds_idx + DS_0 : DS_MAX;
-			pp_count--;
-			dspp_count--;
-			ds_count--;
-			pp_idx++;
-			dspp_idx++;
-			ds_idx++;
-		} else {
-			mixer->pingpong = PINGPONG_MAX;
-			mixer->dspp = DSPP_MAX;
-			mixer->ds = DS_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]) {
@@ -1463,6 +1492,7 @@
 			set_bit(SDE_MIXER_GC, &mixer->features);
 		}
 	}
+	sde_cfg->mixer_count = mixer_count;
 
 end:
 	kfree(prop_value);
@@ -2805,8 +2835,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;
 
@@ -2931,15 +2976,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++) {
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index e60b5ca..d56ad06 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -152,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 {
@@ -159,6 +160,7 @@
 	SDE_MIXER_SOURCESPLIT,
 	SDE_MIXER_GC,
 	SDE_DIM_LAYER,
+	SDE_DISP_PRIMARY_PREF,
 	SDE_MIXER_MAX
 };
 
@@ -217,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
 };
 
@@ -858,8 +862,8 @@
  * @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
  */
@@ -882,8 +886,8 @@
 	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];
 };
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 8e54a2a..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
@@ -14,15 +14,17 @@
 #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
@@ -78,18 +80,44 @@
 #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
@@ -116,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);
@@ -152,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);
@@ -197,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);
@@ -234,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);
@@ -270,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)
@@ -333,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;
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 74018a3..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
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 95b7a6d..88f821d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -378,10 +378,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);
 	}
 }
 
@@ -563,12 +565,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,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 97bc1c1..bad80f0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -214,8 +214,9 @@
 	/**
 	 * 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);
 
 };
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 36e30b7..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 ==
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
index 4878fc6..2b64165 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.h
@@ -52,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
@@ -73,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
@@ -101,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_reg_dma_v1.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
index d7b7625..3326aa2 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,32 @@
 
 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])) {
+				rc = -EINVAL;
+				break;
+			}
+		}
 	}
+	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 +537,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;
 }
@@ -767,24 +803,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;
 	}
 
-	if (!last_cmd_buf->iova) {
-		DRM_DEBUG("iova not set, possible secure session\n");
+	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;
-	reset_reg_dma_buffer_v1(last_cmd_buf);
+	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;
@@ -800,18 +840,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 b59dd16..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,7 +39,14 @@
 #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];
 
@@ -49,9 +56,9 @@
 	[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_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index ecb445d..b48022f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -176,6 +176,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_irq.c b/drivers/gpu/drm/msm/sde/sde_irq.c
index 76f89f4..d6a1d30 100644
--- a/drivers/gpu/drm/msm/sde/sde_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_irq.c
@@ -23,7 +23,6 @@
 
 void sde_irq_update(struct msm_kms *msm_kms, bool enable)
 {
-	int irq_num;
 	struct sde_kms *sde_kms = to_sde_kms(msm_kms);
 
 	if (!msm_kms || !sde_kms) {
@@ -31,16 +30,10 @@
 		return;
 	}
 
-	irq_num = platform_get_irq(sde_kms->dev->platformdev, 0);
-	if (irq_num < 0) {
-		SDE_ERROR("invalid irq number\n");
-		return;
-	}
-
 	if (enable)
-		enable_irq(irq_num);
+		enable_irq(sde_kms->irq_num);
 	else
-		disable_irq(irq_num);
+		disable_irq(sde_kms->irq_num);
 }
 
 irqreturn_t sde_irq(struct msm_kms *kms)
@@ -103,6 +96,15 @@
 	}
 
 	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 */
+	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_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index bf06dfb..c2fe10a 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -433,8 +433,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 +443,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 +483,7 @@
 				}
 			}
 		}
+		SDE_EVT32(DRMID(crtc), SDE_EVTLOG_FUNC_EXIT);
 		SDE_DEBUG("secure operations completed\n");
 	}
 
@@ -1569,15 +1568,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,
@@ -1909,6 +2138,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,
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index aacff78..f047305 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -202,6 +202,7 @@
 
 	struct sde_hw_intr *hw_intr;
 	struct sde_irq irq_obj;
+	int irq_num;	/* mdss irq number */
 
 	struct sde_core_perf perf;
 
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index f4672b8..10e52bf 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -223,6 +223,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 +251,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 +383,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 +402,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 +434,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);
 
@@ -693,7 +704,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);
 }
@@ -895,7 +909,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(
@@ -923,6 +938,7 @@
 	 * 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,
@@ -931,6 +947,9 @@
 		}
 		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)
@@ -949,7 +968,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);
 	}
@@ -2134,6 +2154,7 @@
 	}
 
 	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);
@@ -2438,6 +2459,7 @@
 	 * 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) {
@@ -2841,6 +2863,7 @@
 	}
 
 	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;
@@ -2868,20 +2891,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);
@@ -3001,8 +3080,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;
@@ -3768,6 +3848,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)
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index d6c5876..e1b07c2 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -294,4 +294,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 a52abd9..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;
 }
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 ebcf9e0..21fbcb5 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.c
+++ b/drivers/gpu/drm/msm/sde/sde_rm.c
@@ -419,8 +419,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,
@@ -570,14 +570,16 @@
 	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 ds %d pp %d\n",
-			   lm_cfg->id, lm_cfg->dspp,
-			   lm_cfg->ds, 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) {
@@ -591,28 +593,39 @@
 		}
 	}
 
-	is_valid_dspp = (lm_cfg->dspp != DSPP_MAX) ? true : false;
-	is_valid_ds = (lm_cfg->ds != DS_MAX) ? true : false;
+	/* 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);
+		/**
+		 * 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,
-			(bool)(RM_RQ_DS(reqs)), lm_cfg->ds);
-		return ret;
+		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, (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;
 	}
 
 	/* Already reserved? */
@@ -804,6 +817,7 @@
 static int _sde_rm_reserve_ctls(
 		struct sde_rm *rm,
 		struct sde_rm_rsvp *rsvp,
+		struct sde_rm_requirements *reqs,
 		const struct sde_rm_topology_def *top)
 {
 	struct sde_rm_hw_blk *ctls[MAX_BLOCKS];
@@ -816,21 +830,34 @@
 	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) {
+			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("ctl %d match\n", iter.blk->id);
@@ -1032,11 +1059,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);
 	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);
 	}
 	if (ret) {
 		SDE_ERROR("unable to find appropriate CTL\n");
@@ -1084,10 +1111,6 @@
 		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
@@ -1145,6 +1168,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
@@ -1226,12 +1276,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:
@@ -1247,18 +1291,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) {
@@ -1343,12 +1375,6 @@
 		_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 */
diff --git a/drivers/gpu/drm/msm/sde/sde_rm.h b/drivers/gpu/drm/msm/sde/sde_rm.h
index 1e48855..3b9b82f 100644
--- a/drivers/gpu/drm/msm/sde/sde_rm.h
+++ b/drivers/gpu/drm/msm/sde/sde_rm.h
@@ -197,4 +197,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..2cdc2f3 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.c
@@ -60,6 +60,67 @@
 	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);
+		return rc;
+	}
+
+	status = vbif->ops.get_halt_ctrl(vbif, xin_id);
+	if (status == 0) {
+		SDE_ERROR("halt failed for pipe xin_id %u halt clk_ctrl %u\n",
+			       xin_id, clk_ctrl);
+		SDE_EVT32(xin_id, clk_ctrl, SDE_EVTLOG_ERROR);
+		return -ETIMEDOUT;
+	}
+
+	/* 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 0;
+}
+
 /**
  * _sde_vbif_apply_dynamic_ot_limit - determine OT based on usecase parameters
  * @vbif:	Pointer to hardware vbif driver
diff --git a/drivers/gpu/drm/msm/sde/sde_vbif.h b/drivers/gpu/drm/msm/sde/sde_vbif.h
index a4830a0..30cc416 100644
--- a/drivers/gpu/drm/msm/sde/sde_vbif.h
+++ b/drivers/gpu/drm/msm/sde/sde_vbif.h
@@ -78,6 +78,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..8076e0c 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;
 
@@ -339,7 +378,7 @@
 	 * Add extra connector properties
 	 */
 	msm_property_install_range(&c_conn->property_info, "FB_ID",
-			0x0, 0, ~0, ~0, CONNECTOR_PROP_OUT_FB);
+			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",
diff --git a/drivers/gpu/drm/msm/sde/sde_wb.h b/drivers/gpu/drm/msm/sde/sde_wb.h
index aa57d3e..c3f9e06 100644
--- a/drivers/gpu/drm/msm/sde/sde_wb.h
+++ b/drivers/gpu/drm/msm/sde/sde_wb.h
@@ -190,10 +190,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
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
index dc16ab1..c2ce9f0 100644
--- a/drivers/gpu/drm/msm/sde_dbg.c
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -168,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
@@ -183,6 +184,7 @@
 	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];
@@ -2554,6 +2556,8 @@
 {
 	int i;
 
+	mutex_lock(&sde_dbg_base.mutex);
+
 	sde_evtlog_dump_all(sde_dbg_base.evtlog);
 
 	if (dump_all || !blk_arr || !len) {
@@ -2577,6 +2581,8 @@
 
 	if (do_panic && sde_dbg_base.panic_on_err)
 		panic(name);
+
+	mutex_unlock(&sde_dbg_base.mutex);
 }
 
 /**
@@ -2679,6 +2685,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;
@@ -2698,6 +2707,9 @@
 	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);
 	if (copy_to_user(buff, evtlog_buf, len))
@@ -2759,7 +2771,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);
@@ -2778,6 +2790,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);
@@ -2813,13 +2828,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;
 }
 
@@ -2834,11 +2859,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;
 
@@ -2862,8 +2891,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);
 
@@ -2880,27 +2914,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;
 }
@@ -2915,11 +2964,15 @@
 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];
 
+	if (!file)
+		return -EINVAL;
+
+	dbg = file->private_data;
 	if (!dbg)
 		return -ENODEV;
 
@@ -2939,8 +2992,11 @@
 	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);
 
@@ -2948,6 +3004,8 @@
 
 	_sde_dbg_enable_power(false);
 
+	mutex_unlock(&sde_dbg_base.mutex);
+
 	pr_debug("addr=%zx data=%x\n", off, data);
 
 	return count;
@@ -2963,14 +3021,22 @@
 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;
 
+	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;
@@ -2980,11 +3046,15 @@
 			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;
@@ -3012,16 +3082,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;
 }
@@ -3135,6 +3209,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;
@@ -3187,6 +3262,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_power_handle.h b/drivers/gpu/drm/msm/sde_power_handle.h
index 18777fd..6e00184 100644
--- a/drivers/gpu/drm/msm/sde_power_handle.h
+++ b/drivers/gpu/drm/msm/sde_power_handle.h
@@ -18,7 +18,7 @@
 
 #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_DISABLE_BUS_IB_QUOTA	0
 
 #include <linux/sde_io_util.h>
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/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/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/msm/a6xx_reg.h b/drivers/gpu/msm/a6xx_reg.h
index c2eb9ea..728e897 100644
--- a/drivers/gpu/msm/a6xx_reg.h
+++ b/drivers/gpu/msm/a6xx_reg.h
@@ -1004,6 +1004,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..a56a593 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -325,6 +325,26 @@
 		.core = 6,
 		.major = 3,
 		.minor = 0,
+		.patchid = 0,
+		.features = ADRENO_64BIT | ADRENO_RPMH |
+			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 = 0x0,
+		.gpmu_minor = 0x005,
+		.gpmu_tsens = 0x000C000D,
+		.max_power = 5448,
+	},
+	{
+		.gpurev = ADRENO_REV_A630,
+		.core = 6,
+		.major = 3,
+		.minor = 0,
 		.patchid = ANY_ID,
 		.features = ADRENO_64BIT | ADRENO_RPMH |
 			ADRENO_GPMU | ADRENO_CONTENT_PROTECTION | ADRENO_LM,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 8e5683f..ae5a78d 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -112,6 +112,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 */
@@ -852,13 +857,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;
@@ -874,6 +879,8 @@
 		}
 	}
 
+	KGSL_CORE_ERR("GPU speed_bin:%d mismatch for efused bin:%d\n",
+			adreno_dev->speed_bin, bin);
 	return -ENODEV;
 }
 
@@ -898,6 +905,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,
@@ -1266,6 +1274,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;
@@ -1389,9 +1401,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);
@@ -1405,6 +1418,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;
 	}
 }
 
@@ -1455,6 +1470,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
@@ -1492,6 +1523,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);
 
@@ -1676,22 +1710,6 @@
 	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);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 3118375..d6cba9d 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
@@ -208,6 +210,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 +261,9 @@
  * @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)
  */
 struct adreno_preemption {
 	atomic_t state;
@@ -265,6 +271,9 @@
 	struct timer_list timer;
 	struct work_struct work;
 	bool token_submit;
+	unsigned int preempt_level;
+	bool skipsaverestore;
+	bool usesgmem;
 };
 
 
@@ -483,10 +492,6 @@
 	void *gpuhtw_llc_slice;
 	bool gpuhtw_llc_slice_enable;
 	unsigned int zap_loaded;
-	unsigned int preempt_level;
-	bool usesgmem;
-	bool skipsaverestore;
-
 };
 
 /**
diff --git a/drivers/gpu/msm/adreno_a6xx.c b/drivers/gpu/msm/adreno_a6xx.c
index 1964517..8cbc75e 100644
--- a/drivers/gpu/msm/adreno_a6xx.c
+++ b/drivers/gpu/msm/adreno_a6xx.c
@@ -65,10 +65,10 @@
 	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},
@@ -143,6 +143,72 @@
 	{A6XX_RBBM_CLOCK_CNTL2_RB1, 0x00002222},
 	{A6XX_RBBM_CLOCK_CNTL2_RB2, 0x00002222},
 	{A6XX_RBBM_CLOCK_CNTL2_RB3, 0x00002222},
+	{A6XX_RBBM_CLOCK_CNTL_CCU0, 0x00000000},
+	{A6XX_RBBM_CLOCK_CNTL_CCU1, 0x00000000},
+	{A6XX_RBBM_CLOCK_CNTL_CCU2, 0x00000000},
+	{A6XX_RBBM_CLOCK_CNTL_CCU3, 0x00000000},
+	{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, 0x00022022},
+	{A6XX_RBBM_CLOCK_CNTL2_RAC, 0x00005550},
+	{A6XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+	{A6XX_RBBM_CLOCK_HYST_RAC, 0x00445044},
+	{A6XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222220},
+	{A6XX_RBBM_CLOCK_MODE_GPC, 0x00202222},
+	{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,  0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
+	{A6XX_RBBM_CLOCK_DELAY_SP0, 0x00000081},
+	{A6XX_RBBM_CLOCK_HYST_SP0,  0x0000F3CF},
+	{A6XX_RBBM_CLOCK_CNTL_TP0,  0x22222222},
+	{A6XX_RBBM_CLOCK_CNTL_TP1,  0x22222222},
+	{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},
@@ -153,7 +219,7 @@
 	{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},
@@ -178,7 +244,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 {
@@ -937,42 +1003,42 @@
 	_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);
@@ -1027,11 +1093,11 @@
 /*
  * 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 0x5DC cycles, or 78.1 us. The highest 16 bits of this value are
+ * 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 0x000A05DC
+#define GMU_PWR_COL_HYST 0x000A1680
 
 /*
  * a6xx_gmu_power_config() - Configure and enable GMU's low power mode
diff --git a/drivers/gpu/msm/adreno_a6xx_preempt.c b/drivers/gpu/msm/adreno_a6xx_preempt.c
index ca011e4..1eec381 100644
--- a/drivers/gpu/msm/adreno_a6xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a6xx_preempt.c
@@ -208,10 +208,11 @@
 	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,
diff --git a/drivers/gpu/msm/adreno_a6xx_snapshot.c b/drivers/gpu/msm/adreno_a6xx_snapshot.c
index e865f20..4357518 100644
--- a/drivers/gpu/msm/adreno_a6xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a6xx_snapshot.c
@@ -1481,6 +1481,7 @@
 {
 	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;
@@ -1488,10 +1489,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 */
@@ -1579,9 +1586,6 @@
 	bool sptprac_on;
 	unsigned int i;
 
-	/* Make sure the fence is in ALLOW mode so registers can be read */
-	kgsl_regwrite(device, A6XX_GMU_AO_AHB_FENCE_CTRL, 0);
-
 	/* GMU TCM data dumped through AHB */
 	a6xx_snapshot_gmu(adreno_dev, snapshot);
 
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index cb42a70..902dc0a 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -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);
 
 	/*
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 2615d44..d984c6d 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);
@@ -520,21 +522,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_gmu_gpu_fault(adreno_dev));
+
+		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,
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_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index d248479..1adfeb2 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -86,7 +86,7 @@
  * 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 20
+#define GMU_WAKEUP_DELAY 10
 /* Max amount of tries to wake up the GMU. */
 #define GMU_WAKEUP_RETRY_MAX 60
 
@@ -225,8 +225,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;
@@ -287,9 +288,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;
@@ -325,9 +333,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);
 }
@@ -469,7 +481,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))
@@ -512,14 +525,17 @@
 	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;
+	}
 
 	ringcmds = adreno_ringbuffer_allocspace(rb, total_sizedwords);
 	if (IS_ERR(ringcmds))
@@ -608,23 +624,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
diff --git a/drivers/gpu/msm/adreno_sysfs.c b/drivers/gpu/msm/adreno_sysfs.c
index 704e79e..fcf0417 100644
--- a/drivers/gpu/msm/adreno_sysfs.c
+++ b/drivers/gpu/msm/adreno_sysfs.c
@@ -54,38 +54,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,
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 0a7d165..31868a0 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)
@@ -2763,6 +2761,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
@@ -3461,6 +3463,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,
@@ -3489,10 +3492,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);
@@ -3713,8 +3722,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;
 
@@ -4368,13 +4380,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);
@@ -4675,11 +4687,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
@@ -4725,8 +4732,6 @@
 
 	return 0;
 
-error_free_memstore:
-	kgsl_free_global(device, &device->memstore);
 error_close_mmu:
 	kgsl_mmu_close(device);
 error_pwrctrl_close:
@@ -4754,8 +4759,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_gmu.c b/drivers/gpu/msm/kgsl_gmu.c
index 31d7870..df25c28 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>
@@ -25,6 +26,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
@@ -109,9 +117,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;
 }
 
@@ -524,9 +532,8 @@
 	 * them until we get to the end of the buffer or hit the
 	 * zero padding.
 	 */
-	for (arc->num = 1; arc->num <= len; arc->num++) {
-		if (arc->num == len ||
-				arc->val[arc->num - 1] >= arc->val[arc->num])
+	for (arc->num = 1; arc->num < (len >> 1); arc->num++) {
+		if (arc->val[arc->num - 1] >= arc->val[arc->num])
 			break;
 	}
 
@@ -799,6 +806,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",
@@ -1266,7 +1282,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;
@@ -1285,7 +1301,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);
@@ -1355,6 +1371,31 @@
 	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 */
 int gmu_start(struct kgsl_device *device)
 {
@@ -1389,8 +1430,7 @@
 			goto error_gmu;
 
 		/* Request default DCVS level */
-		kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel);
-
+		gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
 		msm_bus_scale_client_update_request(gmu->pcl, 0);
 		break;
 
@@ -1409,7 +1449,7 @@
 		if (ret)
 			goto error_gmu;
 
-		kgsl_pwrctrl_pwrlevel_change(device, pwr->default_pwrlevel);
+		gmu_change_gpu_pwrlevel(device, pwr->default_pwrlevel);
 		break;
 
 	case KGSL_STATE_RESET:
@@ -1431,7 +1471,7 @@
 				goto error_gmu;
 
 			/* Send DCVS level prior to reset*/
-			kgsl_pwrctrl_pwrlevel_change(device,
+			gmu_change_gpu_pwrlevel(device,
 				pwr->default_pwrlevel);
 		} else {
 			/* GMU fast boot */
@@ -1467,7 +1507,7 @@
 	struct gmu_device *gmu = &device->gmu;
 	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
 	struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
-	bool idle = true;
+	int ret = 0;
 
 	if (!test_bit(GMU_CLK_ON, &gmu->flags))
 		return;
@@ -1475,38 +1515,37 @@
 	/* Wait for the lowest idle level we requested */
 	if (gpudev->wait_for_lowest_idle &&
 			gpudev->wait_for_lowest_idle(adreno_dev))
-		idle = false;
+		goto error;
 
-	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
-	if (!idle || (gpudev->wait_for_gmu_idle &&
-			gpudev->wait_for_gmu_idle(adreno_dev))) {
-		dev_err(&gmu->pdev->dev, "Stopping GMU before it is idle\n");
-		idle = false;
-		set_bit(GMU_FAULT, &gmu->flags);
-	} else {
-		idle = true;
-	}
+	ret = gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_NOTIFY_SLUMBER, 0, 0);
+	if (ret)
+		goto error;
 
-	if (idle) {
-		/* Pending message in all queues are abandoned */
-		hfi_stop(gmu);
-		clear_bit(GMU_HFI_ON, &gmu->flags);
-		gmu_irq_disable(device);
+	if (gpudev->wait_for_gmu_idle &&
+			gpudev->wait_for_gmu_idle(adreno_dev))
+		goto error;
 
-		gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
-		gmu_disable_clks(gmu);
-		gmu_disable_gdsc(gmu);
-	} else {
-		/*
-		 * The power controller will change state to SLUMBER anyway
-		 * Set GMU_FAULT flag to indicate to power contrller
-		 * that hang recovery is needed to power on GPU
-		 */
-		set_bit(GMU_FAULT, &gmu->flags);
-		gmu_snapshot(device);
-	}
+	/* Pending message in all queues are abandoned */
+	hfi_stop(gmu);
+	clear_bit(GMU_HFI_ON, &gmu->flags);
+	gmu_irq_disable(device);
+
+	gpudev->rpmh_gpu_pwrctrl(adreno_dev, GMU_FW_STOP, 0, 0);
+	gmu_disable_clks(gmu);
+	gmu_disable_gdsc(gmu);
 
 	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)
diff --git a/drivers/gpu/msm/kgsl_gmu.h b/drivers/gpu/msm/kgsl_gmu.h
index adabbc2..e0c857f 100644
--- a/drivers/gpu/msm/kgsl_gmu.h
+++ b/drivers/gpu/msm/kgsl_gmu.h
@@ -22,11 +22,13 @@
 
 #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))
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_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 97e5c22..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;
@@ -263,7 +263,7 @@
 		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;
 }
@@ -344,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) {
@@ -370,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 */
@@ -402,14 +398,35 @@
 	 * 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 &&
 		!test_bit(GMU_DCVS_REPLAY, &device->gmu.flags))
@@ -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;
 	}
@@ -2485,9 +2502,6 @@
 static void kgsl_pwrctrl_disable(struct kgsl_device *device)
 {
 	if (kgsl_gmu_isenabled(device)) {
-		struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-
-		pwr->active_pwrlevel = pwr->num_pwrlevels - 1;
 		kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
 		return gmu_stop(device);
 	}
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_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..04ebd03 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;
@@ -804,9 +804,9 @@
 	high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
 
 	if (param->adc_tm_hc) {
-		low_output *= QPNP_VADC_HC_VREF_CODE;
+		low_output *= param->full_scale_code;
 		do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
-		high_output *= QPNP_VADC_HC_VREF_CODE;
+		high_output *= param->full_scale_code;
 		do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
 	} else {
 		rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param,
@@ -869,14 +869,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);
@@ -1068,14 +1068,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),
@@ -1105,13 +1105,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);
@@ -1151,7 +1151,7 @@
 			param->low_thr_temp, &param->low_thr_voltage);
 		if (rc)
 			return rc;
-		param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+		param->low_thr_voltage *= adc_properties->full_scale_code;
 		do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
 
 		rc = qpnp_adc_map_temp_voltage(
@@ -1160,7 +1160,7 @@
 			param->high_thr_temp, &param->high_thr_voltage);
 		if (rc)
 			return rc;
-		param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+		param->high_thr_voltage *= adc_properties->full_scale_code;
 		do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
 	} else {
 		qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
@@ -1241,13 +1241,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);
@@ -1305,13 +1305,13 @@
 	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;
+		low_thr *= param->full_scale_code;
 		do_div(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;
+		high_thr *= param->full_scale_code;
 		do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
 		*high_threshold = high_thr;
 	} else {
@@ -2027,11 +2027,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/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-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-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 2460ba7..793cbb5 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -881,9 +881,17 @@
 		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);
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/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/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/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/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/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 9398143..6512a55 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -2577,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;
 }
 
@@ -3441,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/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_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c
index 4d2a346..8f9aba7 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;
 }
 
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/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/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 c380b7e..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;
 }
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.c b/drivers/iommu/arm-smmu.c
index a0e1f59..333836c 100644
--- a/drivers/iommu/arm-smmu.c
+++ b/drivers/iommu/arm-smmu.c
@@ -101,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
@@ -177,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,
@@ -251,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)
@@ -322,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;
@@ -403,6 +404,7 @@
 	int				regulator_defer;
 };
 
+struct arm_smmu_arch_ops;
 struct arm_smmu_device {
 	struct device			*dev;
 
@@ -534,6 +536,7 @@
 
 	bool				qsmmuv500_errata1_init;
 	bool				qsmmuv500_errata1_client;
+	bool				qsmmuv500_errata2_min_align;
 };
 
 static DEFINE_SPINLOCK(arm_smmu_devices_lock);
@@ -571,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);
 
@@ -638,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)) {
@@ -1181,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);
 	}
 }
@@ -1526,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;
@@ -1774,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.
@@ -1929,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));
 }
@@ -2392,10 +2469,6 @@
 	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;
@@ -2436,7 +2509,6 @@
 		iova = __saved_iova_start;
 	}
 	arm_smmu_secure_domain_unlock(smmu_domain);
-	arm_smmu_domain_power_off(domain, smmu_domain->smmu);
 	return iova - __saved_iova_start;
 }
 
@@ -2686,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;
 }
@@ -2820,6 +2899,10 @@
 			& (1 << DOMAIN_ATTR_CB_STALL_DISABLE));
 		ret = 0;
 		break;
+	case DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN:
+		*((int *)data) = smmu_domain->qsmmuv500_errata2_min_align;
+		ret = 0;
+		break;
 	default:
 		ret = -ENODEV;
 		break;
@@ -2950,11 +3033,22 @@
 				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:
-		if (*((int *)data))
+	{
+		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) {
@@ -3430,6 +3524,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;
+
 	/* Push the button */
 	__arm_smmu_tlb_sync(smmu);
 	writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
@@ -3978,24 +4076,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;
@@ -4320,6 +4400,15 @@
 
 #define TBU_DBG_TIMEOUT_US		30000
 
+#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;
@@ -4352,14 +4441,24 @@
 	u32				halt_count;
 };
 
-static bool arm_smmu_domain_match_smr(struct arm_smmu_domain *smmu_domain,
+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(smmu_domain->dev->iommu_fwspec, i, idx) {
-		smr2 = &smmu_domain->smmu->smrs[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;
@@ -4377,13 +4476,15 @@
 	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_domain_match_smr(smmu_domain, smr)) {
+		if (arm_smmu_fwspec_match_smr(fwspec, smr)) {
 			ret = true;
 			break;
 		}
@@ -4717,6 +4818,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;
@@ -4768,6 +4946,38 @@
 	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;
@@ -4775,6 +4985,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)
@@ -4795,6 +5007,22 @@
 	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;
@@ -4810,6 +5038,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 da4d283..a65214b 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_QCOM_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);
@@ -206,11 +235,19 @@
 		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 iova_len = 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
@@ -231,16 +268,35 @@
 	limit = min_t(dma_addr_t, dma_limit >> shift, iovad->dma_32bit_pfn);
 	iova = alloc_iova_fast(iovad, iova_len, limit);
 
-	return (dma_addr_t)iova << shift;
+	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;
 }
 
-static void iommu_dma_free_iova(struct iommu_dma_cookie *cookie,
+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;
 
-	free_iova_fast(iovad, iova >> shift, size >> shift);
+	if (cookie->min_iova_align)
+		guard_len = ALIGN(size, cookie->min_iova_align) - size;
+	else
+		guard_len = 0;
+	iommu_unmap(domain, iova + size, guard_len);
+
+	free_iova_fast(iovad, iova >> shift, (size + guard_len) >> shift);
 }
 
 static void __iommu_dma_unmap(struct iommu_domain *domain, dma_addr_t dma_addr,
@@ -253,7 +309,7 @@
 	size = iova_align(iovad, size + iova_off);
 
 	WARN_ON(iommu_unmap(domain, dma_addr, size) != size);
-	iommu_dma_free_iova(domain->iova_cookie, dma_addr, size);
+	iommu_dma_free_iova(domain, domain->iova_cookie, dma_addr, size);
 }
 
 static void __iommu_dma_free_pages(struct page **pages, int count)
@@ -418,7 +474,7 @@
 out_free_sg:
 	sg_free_table(&sgt);
 out_free_iova:
-	iommu_dma_free_iova(cookie, iova, size);
+	iommu_dma_free_iova(domain, cookie, iova, size);
 out_free_pages:
 	__iommu_dma_free_pages(pages, count);
 	return NULL;
@@ -464,7 +520,7 @@
 		return DMA_ERROR_CODE;
 
 	if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
-		iommu_dma_free_iova(cookie, iova, size);
+		iommu_dma_free_iova(domain, cookie, iova, size);
 		return DMA_ERROR_CODE;
 	}
 	return iova + iova_off;
@@ -629,7 +685,7 @@
 	return __finalise_sg(dev, sg, nents, iova);
 
 out_free_iova:
-	iommu_dma_free_iova(cookie, iova, iova_len);
+	iommu_dma_free_iova(domain, cookie, iova, iova_len);
 out_restore_sg:
 	__invalidate_sg(sg, nents);
 	return 0;
@@ -716,7 +772,7 @@
 	return msi_page;
 
 out_free_iova:
-	iommu_dma_free_iova(cookie, iova, iovad->granule);
+	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 04a5c09..73090df 100644
--- a/drivers/iommu/dma-mapping-fast.c
+++ b/drivers/iommu/dma-mapping-fast.c
@@ -20,6 +20,9 @@
 #include <linux/slab.h>
 #include <linux/vmalloc.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
 #define FAST_PAGE_SIZE (1UL << FAST_PAGE_SHIFT)
@@ -152,9 +155,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 +203,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 +306,16 @@
 				  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;
+	else
+		guard_len = 0;
+	nbits = (size + guard_len) >> FAST_PAGE_SHIFT;
+
+	iommu_unmap(mapping->domain, iova + size, guard_len);
 
 	/*
 	 * We don't invalidate TLBs on unmap.  We invalidate TLBs on map
@@ -434,7 +464,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,
@@ -855,6 +886,28 @@
 	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_QCOM_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_init_mapping
  * @dev: valid struct device pointer
@@ -867,9 +920,8 @@
 int fast_smmu_init_mapping(struct device *dev,
 			    struct dma_iommu_mapping *mapping)
 {
-	int err, 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;
 
@@ -878,63 +930,37 @@
 		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))
 		return -ENOMEM;
 	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");
-		err = -ENODEV;
-		goto release_mapping;
-	}
-
-	if (iommu_get_domain_for_dev(dev)) {
-		dev_err(dev, "Device already attached to other iommu_domain\n");
-		err = -EINVAL;
-		goto release_mapping;
-	}
-
-	/*
-	 * Need to attach prior to calling DOMAIN_ATTR_PGTBL_INFO and then
-	 * detach to be in the expected state. Its a bit messy.
-	 */
-	if (iommu_attach_group(mapping->domain, group)) {
-		err = -EINVAL;
-		goto release_mapping;
-	}
-
 	if (iommu_domain_get_attr(domain, DOMAIN_ATTR_PGTBL_INFO,
 				  &info)) {
 		dev_err(dev, "Couldn't get page table info\n");
 		err = -EINVAL;
-		goto detach_group;
+		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)) {
 		err = -EINVAL;
-		goto detach_group;
+		goto release_mapping;
 	}
 
 	mapping->fast->notifier.notifier_call = fast_smmu_notify;
 	av8l_register_notify(&mapping->fast->notifier);
 
-	iommu_detach_group(mapping->domain, group);
 	mapping->ops = &fast_smmu_dma_ops;
 	return 0;
 
-detach_group:
-	iommu_detach_group(mapping->domain, group);
 release_mapping:
 	kfree(mapping->fast->bitmap);
 	kfree(mapping->fast);
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 a3594d2..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 */
diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c
index 56eff61b..6d79cfb 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);
 	}
 
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/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 5c9c75c..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)
@@ -125,6 +122,10 @@
 #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)
 #define QPNP_WLED_SYNC_REG(b)		(b + 0x47)
@@ -404,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;
 };
 
@@ -600,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,
@@ -1098,7 +1103,7 @@
 	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;
@@ -1128,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);
@@ -1136,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);
@@ -1174,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);
@@ -1190,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) {
@@ -1203,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;
 	}
 
@@ -1224,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
@@ -1456,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)
@@ -1762,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;
 
@@ -1785,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) {
@@ -2108,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) {
@@ -2237,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) {
@@ -2442,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;
@@ -2450,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/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/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 549b4af..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;
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/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/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/msm/camera/cam_core/cam_context.c b/drivers/media/platform/msm/camera/cam_core/cam_context.c
index 8f625ae..bfa1bdd 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.c
@@ -36,6 +36,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)
 {
@@ -334,7 +354,7 @@
 	}
 
 	memset(ctx, 0, sizeof(*ctx));
-
+	ctx->dev_hdl = -1;
 	INIT_LIST_HEAD(&ctx->list);
 	mutex_init(&ctx->ctx_mutex);
 	spin_lock_init(&ctx->lock);
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..10285cb 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_context.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_context.h
@@ -186,12 +186,22 @@
 };
 
 /**
+ * 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 +212,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 +224,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 +236,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 +248,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 +260,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 +272,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 +284,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 +296,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 +308,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 +320,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);
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..714891e 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
@@ -180,9 +180,9 @@
 	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;
+	ctx->session_hdl = -1;
+	ctx->dev_hdl = -1;
+	ctx->link_hdl = -1;
 
 	while (!list_empty(&ctx->active_req_list)) {
 		req = list_first_entry(&ctx->active_req_list,
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 6e48c6a..11e9290 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.c
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.c
@@ -17,12 +17,6 @@
 #include "cam_node.h"
 #include "cam_trace.h"
 #include "cam_debug_util.h"
-static void  __cam_node_handle_shutdown(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);
-}
 
 static int __cam_node_handle_query_cap(struct cam_node *node,
 	struct cam_query_cap_cmd *query)
@@ -307,6 +301,29 @@
 	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);
+			list_add_tail(&(node->ctx_list[i].list),
+				&node->free_ctx_list);
+		}
+	}
+
+	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)
 {
@@ -456,9 +473,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..02e153d 100644
--- a/drivers/media/platform/msm/camera/cam_core/cam_node.h
+++ b/drivers/media/platform/msm/camera/cam_core/cam_node.h
@@ -73,6 +73,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.
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..8518862 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
@@ -1302,6 +1302,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 +1463,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_intf.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_intf.c
index 3846784..0ba3bb2 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,6 +50,33 @@
 
 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)
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 e3d46df..4b0cc74 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
@@ -372,6 +372,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);
 		}
 	}
 
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..e3639a6 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;
 };
 
 /**
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 918258d..55cb07b 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
@@ -494,8 +494,15 @@
 	},
 	{
 		.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[] = {
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..aa8b266 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
@@ -309,4 +323,17 @@
 	struct cam_hw_version    *camera_version,
 	struct cam_hw_version    *cpas_version);
 
+/**
+ * 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_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/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 b8a5685..d9be53d 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
@@ -112,6 +112,9 @@
 	/* Before triggering reset to HW, clear the reset complete */
 	reinit_completion(&fd_core->reset_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,
@@ -126,9 +129,6 @@
 	if (time_left <= 0)
 		CAM_WARN(CAM_FD, "HW reset timeout time_left=%d", time_left);
 
-	cam_fd_soc_register_write(soc_info, CAM_FD_REG_CORE,
-		hw_static_info->core_regs.control, 0x1);
-
 	CAM_DBG(CAM_FD, "FD Wrapper SW Sync Reset complete");
 
 	return 0;
@@ -424,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) {
@@ -434,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.
@@ -511,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;
 }
@@ -776,6 +777,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) {
@@ -784,18 +788,23 @@
 	}
 
 	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_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) {
@@ -809,9 +818,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;
 }
@@ -824,6 +836,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) {
@@ -841,11 +854,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;
 	}
 
@@ -858,7 +871,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;
 
@@ -903,9 +916,9 @@
 
 	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;
 }
@@ -914,6 +927,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) {
@@ -922,19 +938,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) {
@@ -949,9 +970,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 bdd72af..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   100
+#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_icp/cam_icp_subdev.c b/drivers/media/platform/msm/camera/cam_icp/cam_icp_subdev.c
index 8a0d5f7..905cc97 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
@@ -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;
diff --git a/drivers/media/platform/msm/camera/cam_icp/hfi.c b/drivers/media/platform/msm/camera/cam_icp/hfi.c
index a315268..cdb0cfa 100644
--- a/drivers/media/platform/msm/camera/cam_icp/hfi.c
+++ b/drivers/media/platform/msm/camera/cam_icp/hfi.c
@@ -385,10 +385,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);
 	}
 
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 aad7902..16c02d8 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
@@ -151,6 +151,13 @@
 {
 	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++) {
@@ -202,27 +209,35 @@
 			continue;
 		}
 
-		if (!bubble_state) {
-			CAM_DBG(CAM_ISP, "Sync with success: fd 0x%x",
-				   req_isp->fence_map_out[j].sync_id);
-			if (req_isp->fence_map_out[j].sync_id == -1)
-				__cam_isp_ctx_handle_buf_done_fail_log(req_isp);
-			else
-				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",
-					 rc);
+		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: 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_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
@@ -230,18 +245,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);
@@ -779,7 +807,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;
 
@@ -817,11 +847,22 @@
 
 	if (ctx_isp->active_req_cnt >=  2) {
 		CAM_ERR_RATE_LIMIT(CAM_ISP,
-			"Reject apply request due to congestion(cnt = %d)",
+			"Reject apply request (id %lld) due to congestion(cnt = %d)",
+			req->request_id,
 			ctx_isp->active_req_cnt);
-		__cam_isp_ctx_handle_buf_done_fail_log(req_isp);
-		rc = -EFAULT;
-		goto end;
+		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;
 
@@ -1212,6 +1253,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 */
@@ -1454,9 +1496,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;
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/cam_ife_hw_mgr.c b/drivers/media/platform/msm/camera/cam_isp/isp_hw_mgr/cam_ife_hw_mgr.c
index 6060278..f7b40a4 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
@@ -778,10 +778,11 @@
 	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_ife_hw_mgr_res           *cid_res;
 	struct cam_hw_intf                   *hw_intf;
 	struct cam_csid_hw_reserve_resource_args  csid_acquire;
 
@@ -800,69 +801,64 @@
 	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 resource");
-		goto err;
-	}
-
-	CAM_DBG(CAM_ISP, "acquired csid(%d) left ipp resource 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;
-		csid_acquire.master_idx = csid_res->hw_res[0]->hw_intf->hw_idx;
+	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;
+
 	CAM_DBG(CAM_ISP, "acquire res %d", csid_acquire.res_id);
 
 	return 0;
@@ -909,7 +905,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;
 
@@ -934,6 +931,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);
@@ -944,37 +942,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_IFE_CSID_HW_NUM_MAX) {
-			CAM_ERR(CAM_ISP,
-				"Can not acquire ife csid rdi resource");
-			goto err;
-		}
+			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 = &ife_ctx->res_list_ife_in;
+			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;
+		}
 	}
 
 	return 0;
 err:
-	/* resource resources at entry funciton */
 	return rc;
 }
 
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 5f68f21..8514ab3 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
@@ -513,8 +513,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);
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 dcbea8d..c6d5601 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
@@ -613,7 +613,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);
 
@@ -632,6 +631,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/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 7d6e758..cd92035 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
@@ -627,6 +627,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) {
@@ -1247,6 +1253,11 @@
 		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 */
@@ -2539,8 +2550,8 @@
 	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];
+	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;
 
 	csid_hw = (struct cam_ife_csid_hw *)data;
@@ -2587,8 +2598,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");
@@ -2603,35 +2618,55 @@
 	}
 
 	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);
 	}
 
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 77b830c..8e83cb0 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
@@ -34,7 +34,7 @@
 
 static uint32_t camif_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
 	0x0003FD1F,
-	0x0FFF7EB3,
+	0x0FFF7EBC,
 };
 
 static uint32_t rdi_irq_reg_mask[CAM_IFE_IRQ_REGISTERS_MAX] = {
@@ -507,8 +507,6 @@
 			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);
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 e3a6f7b..5773bbe 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
@@ -222,11 +222,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 */
 		{
@@ -707,8 +708,24 @@
 			.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,
@@ -739,21 +756,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 005d7e0..f7c62a1 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
@@ -25,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";
 
@@ -184,6 +185,8 @@
 
 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];
@@ -279,23 +282,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 = 0x3;
-			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",
@@ -307,25 +324,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",
@@ -394,6 +393,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:
@@ -425,6 +426,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:
@@ -551,6 +553,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:
@@ -611,33 +649,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:
@@ -792,7 +803,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;
@@ -840,10 +851,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;
@@ -2148,13 +2160,30 @@
 	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);
@@ -2162,13 +2191,15 @@
 		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;
@@ -2192,6 +2223,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;
@@ -2267,7 +2307,8 @@
 		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
 			wm_data->hw_regs->buffer_width_cfg,
 			wm_data->width);
-		CAM_DBG(CAM_ISP, "image width 0x%x", wm_data->width);
+		CAM_DBG(CAM_ISP, "WM %d image width 0x%x",
+			wm_data->index, wm_data->width);
 
 		/* For initial configuration program all bus registers */
 		if ((wm_data->stride != io_cfg->planes[i].plane_stride ||
@@ -2276,8 +2317,9 @@
 				wm_data->hw_regs->stride,
 				io_cfg->planes[i].plane_stride);
 			wm_data->stride = io_cfg->planes[i].plane_stride;
+			CAM_DBG(CAM_ISP, "WM %d image stride 0x%x",
+				wm_data->index, wm_data->stride);
 		}
-		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) {
@@ -2285,9 +2327,11 @@
 				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,
+				wm_data->framedrop_pattern);
 		}
-		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) {
@@ -2295,9 +2339,10 @@
 				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,
+				wm_data->framedrop_period);
 		}
-		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) {
@@ -2306,9 +2351,10 @@
 				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,
+				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 != io_cfg->subsample_pattern
 			|| !wm_data->hfr_cfg_done) {
@@ -2317,9 +2363,10 @@
 				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,
+				wm_data->irq_subsample_pattern);
 		}
-		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) {
@@ -2335,9 +2382,9 @@
 					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, wm_data->packer_cfg);
 			}
-			CAM_DBG(CAM_ISP, "packer cfg 0x%x",
-				wm_data->packer_cfg);
 
 			if (wm_data->is_dual) {
 				CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
@@ -2351,8 +2398,9 @@
 					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, wm_data->tile_cfg);
 			}
-			CAM_DBG(CAM_ISP, "tile cfg 0x%x", wm_data->tile_cfg);
 
 			if (wm_data->is_dual) {
 				if ((wm_data->h_init != wm_data->offset) ||
@@ -2375,8 +2423,9 @@
 					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, wm_data->h_init);
 			}
-			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) {
@@ -2384,8 +2433,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, wm_data->v_init);
 			}
-			CAM_DBG(CAM_ISP, "v_init 0x%x", wm_data->v_init);
 
 			if (wm_data->ubwc_meta_stride !=
 				io_cfg->planes[i].meta_stride ||
@@ -2396,9 +2446,10 @@
 					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,
+					wm_data->ubwc_meta_stride);
 			}
-			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 ||
@@ -2408,9 +2459,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, wm_data->ubwc_mode_cfg);
 			}
-			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 ||
@@ -2421,16 +2472,17 @@
 					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,
+					wm_data->ubwc_meta_offset);
 			}
-			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]);
+			CAM_DBG(CAM_ISP, "WM %d ubwc meta addr 0x%llx",
+				wm_data->index, update_buf->image_buf[i]);
 		}
 
 		/* WM Image address */
@@ -2445,12 +2497,16 @@
 			update_buf->image_buf[i] +
 			wm_data->offset);
 
-		CAM_DBG(CAM_ISP, "image address 0x%x", reg_val_pair[j-1]);
+		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, frame_inc);
+
 
 		/* enable the WM */
 		CAM_VFE_ADD_REG_VAL_PAIR(reg_val_pair, j,
@@ -2523,9 +2579,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) {
@@ -2533,9 +2589,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) {
@@ -2544,9 +2600,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) {
@@ -2555,9 +2611,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)
@@ -2783,6 +2839,8 @@
 	}
 	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;
@@ -2808,7 +2866,7 @@
 	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) {
@@ -2826,9 +2884,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;
@@ -2871,7 +2929,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]);
 
@@ -2909,7 +2967,7 @@
 	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,
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 c90d4ce..ed7d5fe 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,
@@ -160,11 +160,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_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 2cd6b04..35c2717 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;
 
@@ -234,6 +243,59 @@
 	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,10 @@
 	uint32_t dev_type;
 	struct cam_jpeg_set_irq_cb irq_cb;
 	struct cam_jpeg_hw_cfg_req *p_cfg_req = NULL;
+	struct cam_hw_done_event_data buf_data;
 	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);
@@ -296,7 +357,8 @@
 
 	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);
@@ -304,7 +366,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);
 
@@ -313,99 +376,123 @@
 	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);
-
-	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;
-
-		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 for backward compat */
+	if (config_args->hw_update_entries[CAM_JPEG_CHBASE].handle) {
+		rc = cam_jpeg_insert_cdm_change_base(config_args,
+			ctx_data, hw_mgr);
 		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);
+			CAM_ERR(CAM_JPEG, "insert change base failed %d", rc);
 			goto end_callcb;
 		}
 	} else {
-		CAM_ERR(CAM_JPEG, "No commands to config");
+		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(ctx_data->cmd_chbase_buf_addr,
+			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;
+		cam_cdm_submit_bls(hw_mgr->cdm_info[dev_type][0].
+			cdm_handle,
+			ctx_data->cdm_cmd_chbase);
+	}
+
+	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++;
+	}
+
+	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;
 	}
 
 	return rc;
@@ -423,6 +510,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;
@@ -535,6 +628,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",
@@ -564,7 +658,17 @@
 			packet->header.op_code & 0xff);
 		return -EINVAL;
 	}
-	if ((packet->num_cmd_buf > 2) || !packet->num_patches ||
+
+	/* if for backward compat */
+	if (packet->kmd_cmd_buf_index != -1) {
+		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,
@@ -611,16 +715,35 @@
 			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;
+	/* if-else for backward compat */
+	if (packet->kmd_cmd_buf_index != -1) {
+		rc = cam_packet_util_get_kmd_buffer(packet, &kmd_buf);
+		if (rc) {
+			CAM_ERR(CAM_JPEG, "get kmd buf failed %d", rc);
+			return rc;
+		}
+	} else {
+		memset(&kmd_buf, 0x0, sizeof(kmd_buf));
 	}
+	/* 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",
@@ -651,6 +774,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,12 +791,6 @@
 		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_data);
@@ -787,20 +909,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,9 +941,12 @@
 	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_data);
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 0a15f71..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,20 +57,19 @@
 		return -EINVAL;
 	}
 
-
 	mutex_lock(&core_info->core_mutex);
 	if (++core_info->ref_count > 1) {
 		mutex_unlock(&core_info->core_mutex);
 		return 0;
 	}
 
-	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;
+	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);
+		&ahb_vote, &axi_vote);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
 		goto cpas_failed;
@@ -91,6 +90,7 @@
 cpas_failed:
 	--core_info->ref_count;
 	mutex_unlock(&core_info->core_mutex);
+
 	return rc;
 }
 
@@ -154,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;
 	}
@@ -164,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 1e0c2e2..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,7 @@
 	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;
 };
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 06ad260..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,20 +66,19 @@
 		return -EINVAL;
 	}
 
-
 	mutex_lock(&core_info->core_mutex);
 	if (++core_info->ref_count > 1) {
 		mutex_unlock(&core_info->core_mutex);
 		return 0;
 	}
 
-	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;
+	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);
+		&ahb_vote, &axi_vote);
 	if (rc) {
 		CAM_ERR(CAM_JPEG, "cpass start failed: %d", rc);
 		goto cpas_failed;
@@ -134,6 +99,7 @@
 cpas_failed:
 	--core_info->ref_count;
 	mutex_unlock(&core_info->core_mutex);
+
 	return rc;
 }
 
@@ -174,7 +140,7 @@
 
 	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)
@@ -207,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,
@@ -229,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);
@@ -238,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",
@@ -285,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);
@@ -325,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;
 }
@@ -342,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;
 	}
@@ -352,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 eb5caef..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,7 @@
 	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;
 };
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 570d9f9..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,
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 9d454e9..88efcb5 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
@@ -115,56 +115,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;
@@ -734,6 +684,70 @@
 	return rc;
 }
 
+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);
+}
+
+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)
 {
 	int rc = 0;
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 8565ad1..a6b097d 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;
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 3687a5a..db34157 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
@@ -406,5 +406,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_dev.c b/drivers/media/platform/msm/camera/cam_req_mgr/cam_req_mgr_dev.c
index e4bc98f..c316dbb 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);
 
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 c3475b6..85db1b1 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
@@ -317,7 +317,7 @@
 		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",
@@ -326,7 +326,7 @@
 		}
 	} 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);
@@ -338,7 +338,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",
@@ -358,7 +358,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",
@@ -401,12 +401,16 @@
 		return -EINVAL;
 	}
 
-	if (config)
+	if (config) {
+		rc = cam_soc_util_request_platform_resource(soc_info,
+			NULL, NULL);
 		rc = cam_soc_util_enable_platform_resource(soc_info, false, 0,
 			false);
-	else
+	} else {
 		rc = cam_soc_util_disable_platform_resource(soc_info, false,
 			false);
+		rc = cam_soc_util_release_platform_resource(soc_info);
+	}
 
 	return rc;
 }
@@ -429,15 +433,6 @@
 	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);
@@ -456,12 +451,6 @@
 		&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 &&
@@ -471,18 +460,43 @@
 		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 (rc < 0)
-			CAM_ERR(CAM_ACTUATOR,
-				"Failed to disable platform resources: %d", rc);
 	}
 
+	rc = cam_actuator_vreg_control(a_ctrl, 0);
+	if (rc < 0)
+		CAM_ERR(CAM_ACTUATOR, "Disable Regulator Failed: %d", rc);
+
 	return rc;
 }
 
+void cam_actuator_shutdown(struct cam_actuator_ctrl_t *a_ctrl)
+{
+	int rc;
+
+	if (a_ctrl->cam_act_state == CAM_ACTUATOR_INIT)
+		return;
+
+	if (a_ctrl->cam_act_state == CAM_ACTUATOR_START) {
+		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");
+		a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
+	}
+
+	if (a_ctrl->cam_act_state == CAM_ACTUATOR_ACQUIRE) {
+		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,
 	void *arg)
 {
@@ -535,7 +549,7 @@
 			rc = -EFAULT;
 			goto release_mutex;
 		}
-
+		a_ctrl->cam_act_state = CAM_ACTUATOR_ACQUIRE;
 	}
 		break;
 	case CAM_RELEASE_DEV: {
@@ -552,6 +566,7 @@
 		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: {
@@ -591,9 +606,13 @@
 			rc = -EINVAL;
 			goto release_mutex;
 		}
+		a_ctrl->cam_act_state = CAM_ACTUATOR_START;
 	}
 		break;
 	case CAM_STOP_DEV: {
+		struct i2c_settings_array *i2c_set = NULL;
+		int i;
+
 		rc = camera_io_release(&a_ctrl->io_master_info);
 		if (rc < 0)
 			CAM_ERR(CAM_ACTUATOR, "Failed in releasing CCI");
@@ -602,17 +621,29 @@
 			CAM_ERR(CAM_ACTUATOR, "Actuator Power down failed");
 			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);
@@ -631,8 +662,6 @@
 		}
 	}
 		break;
-	case CAM_SD_SHUTDOWN:
-		break;
 	default:
 		CAM_ERR(CAM_ACTUATOR, "Invalid Opcode %d", cmd->op_code);
 	}
@@ -642,3 +671,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..f24070e 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
@@ -30,6 +30,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 +53,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 45dbba1..465f5e2 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)
 {
@@ -191,6 +211,9 @@
 		cam_actuator_apply_request;
 
 	v4l2_set_subdevdata(&(a_ctrl->v4l2_dev_str.sd), a_ctrl);
+
+	a_ctrl->cam_act_state = CAM_ACTUATOR_INIT;
+
 	return rc;
 free_mem:
 	kfree(a_ctrl->i2c_data.per_frame);
@@ -307,6 +330,8 @@
 		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);
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 ad42a3d..bd5d50f 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,18 @@
 #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_actator_state {
+	CAM_ACTUATOR_INIT,
+	CAM_ACTUATOR_ACQUIRE,
+	CAM_ACTUATOR_START,
+	CAM_ACTUATOR_RELEASE,
+};
+
 /**
  * struct intf_params
  * @device_hdl: Device Handle
@@ -76,6 +83,9 @@
  * @io_master_info: Information about the communication master
  * @actuator_mutex: Actuator mutex
  * @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
@@ -90,7 +100,9 @@
 	struct camera_io_master io_master_info;
 	struct cam_hw_soc_info soc_info;
 	struct mutex actuator_mutex;
-	enum msm_actuator_state_t act_apply_state;
+	uint32_t id;
+	enum cam_actuator_apply_state_t setting_apply_state;
+	enum cam_actator_state cam_act_state;
 	struct msm_camera_gpio_num_info *gpio_num_info;
 	uint8_t cam_pinctrl_status;
 	struct cam_subdev v4l2_dev_str;
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 57d576a..7cc26c1 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
@@ -346,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)
 {
@@ -480,8 +518,7 @@
 			CAM_ERR(CAM_CSIPHY, "de-voting CPAS: %d", rc);
 			goto release_mutex;
 		}
-		csiphy_dev->csiphy_info.combo_mode = 0;
-		csiphy_dev->csiphy_state = CAM_CSIPHY_STOP;
+		csiphy_dev->csiphy_state = CAM_CSIPHY_ACQUIRE;
 	}
 		break;
 	case CAM_RELEASE_DEV: {
@@ -517,8 +554,6 @@
 
 		csiphy_dev->config_count--;
 		csiphy_dev->acquire_count--;
-		if (csiphy_dev->acquire_count == 0)
-			csiphy_dev->csiphy_state = CAM_CSIPHY_RELEASE;
 
 		if (csiphy_dev->csiphy_info.secure_mode &&
 			(!csiphy_dev->config_count)) {
@@ -528,6 +563,9 @@
 				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: {
@@ -584,8 +622,6 @@
 		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 a136fa7..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)
 {
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 25891c5..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
@@ -68,10 +68,9 @@
 #endif
 
 enum cam_csiphy_state {
+	CAM_CSIPHY_INIT,
 	CAM_CSIPHY_ACQUIRE,
-	CAM_CSIPHY_RELEASE,
 	CAM_CSIPHY_START,
-	CAM_CSIPHY_STOP,
 };
 
 /**
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 a173954..bd9f0fe 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
@@ -307,7 +307,7 @@
 		CAM_ERR(CAM_EEPROM, "failed: eeprom power down rc %d", rc);
 	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);
@@ -668,7 +668,7 @@
 	case CAM_EEPROM_PACKET_OPCODE_INIT:
 		if (e_ctrl->userspace_probe == false) {
 			rc = cam_eeprom_parse_read_memory_map(
-					e_ctrl->pdev->dev.of_node, e_ctrl);
+					e_ctrl->soc_info.dev->of_node, e_ctrl);
 			if (rc < 0) {
 				CAM_ERR(CAM_EEPROM, "Failed: rc : %d", rc);
 				return rc;
@@ -723,7 +723,7 @@
 	}
 	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:
@@ -733,6 +733,35 @@
 	return rc;
 }
 
+void cam_eeprom_shutdown(struct cam_eeprom_ctrl_t *e_ctrl)
+{
+	int rc;
+
+	if (e_ctrl->cam_eeprom_state == CAM_EEPROM_INIT)
+		return;
+
+	if (e_ctrl->cam_eeprom_state == CAM_EEPROM_START) {
+		rc = camera_io_release(&e_ctrl->io_master_info);
+		if (rc < 0)
+			CAM_ERR(CAM_EEPROM, "Failed in releasing CCI");
+		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;
+	}
+
+	e_ctrl->cam_eeprom_state = CAM_EEPROM_INIT;
+}
+
 /**
  * cam_eeprom_driver_cmd - Handle eeprom cmds
  * @e_ctrl:     ctrl structure
@@ -775,6 +804,7 @@
 			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->bridge_intf.device_hdl == -1) {
@@ -792,6 +822,7 @@
 		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 88d7665..d667cf4 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,
@@ -205,6 +225,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:
@@ -404,6 +425,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));
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 e06e22a..fa4a3dd 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,13 @@
 #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_START,
+	CAM_EEPROM_RELEASE,
+};
+
 /**
  * struct cam_eeprom_map_t - eeprom map
  * @data_type       :   Data type
@@ -154,6 +161,7 @@
  * @cci_i2c_master  :   I2C structure
  * @v4l2_dev_str    :   V4L2 device structure
  * @bridge_intf     :   bridge interface params
+ * @cam_eeprom_state:   eeprom_device_state
  * @userspace_probe :   flag indicates userspace or kernel probe
  * @cal_data        :   Calibration data
  * @device_name     :   Device name
@@ -170,6 +178,7 @@
 	struct cam_subdev v4l2_dev_str;
 	struct cam_eeprom_intf_params bridge_intf;
 	enum msm_camera_device_type_t eeprom_device_type;
+	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_flash/cam_flash_core.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_flash/cam_flash_core.c
index b0fe293..8573f00 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
@@ -45,6 +45,7 @@
 			return rc;
 		}
 		flash_ctrl->is_regulator_enabled = false;
+		flash_ctrl->flash_state = CAM_FLASH_STATE_RELEASE;
 	} else {
 		CAM_ERR(CAM_FLASH, "Wrong Flash State : %d",
 			flash_ctrl->flash_state);
@@ -751,6 +752,41 @@
 	return rc;
 }
 
+void cam_flash_shutdown(struct cam_flash_ctrl *fctrl)
+{
+	int rc, i, j;
+
+	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;
+	}
+
+	cam_flash_flush_nrt(fctrl);
+
+	if ((fctrl->flash_state != CAM_FLASH_STATE_RELEASE) &&
+		(fctrl->is_regulator_enabled == true)) {
+		rc = cam_flash_prepare(fctrl, CAM_FLASH_STATE_RELEASE);
+		if (rc)
+			CAM_ERR(CAM_FLASH, "Disable Regulator Failed ret = %d",
+				rc);
+	}
+
+	if (fctrl->bridge_intf.device_hdl != -1) {
+		rc = cam_destroy_device_hdl(fctrl->bridge_intf.
+			device_hdl);
+		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;
+	}
+}
+
 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..f2a782b 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,5 @@
 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);
 #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 d743cf0..57f1f0f 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
@@ -71,6 +71,7 @@
 			goto release_mutex;
 		}
 		break;
+		fctrl->flash_state = CAM_FLASH_STATE_ACQUIRE;
 	}
 	case CAM_RELEASE_DEV: {
 		CAM_DBG(CAM_FLASH, "CAM_RELEASE_DEV");
@@ -91,6 +92,7 @@
 		fctrl->bridge_intf.link_hdl = -1;
 		fctrl->bridge_intf.session_hdl = -1;
 		break;
+		fctrl->flash_state = CAM_FLASH_STATE_RELEASE;
 	}
 	case CAM_QUERY_CAP: {
 		struct cam_flash_query_cap_info flash_cap = {0};
@@ -130,6 +132,7 @@
 			CAM_ERR(CAM_FLASH, "cannot apply settings rc = %d", rc);
 			goto release_mutex;
 		}
+		fctrl->flash_state = CAM_FLASH_STATE_INIT;
 		break;
 	}
 	case CAM_STOP_DEV: {
@@ -143,8 +146,7 @@
 				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 +258,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 +287,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)
 {
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..1583c27 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,6 +49,7 @@
 
 enum cam_flash_state {
 	CAM_FLASH_STATE_INIT,
+	CAM_FLASH_STATE_ACQUIRE,
 	CAM_FLASH_STATE_LOW,
 	CAM_FLASH_STATE_HIGH,
 	CAM_FLASH_STATE_RELEASE,
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
index 0a283ab..2a877fd 100644
--- 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
@@ -77,12 +77,16 @@
 		return -EINVAL;
 	}
 
-	if (config)
+	if (config) {
+		rc = cam_soc_util_request_platform_resource(soc_info,
+			NULL, NULL);
 		rc = cam_soc_util_enable_platform_resource(soc_info, false, 0,
 			false);
-	else
+	} else {
 		rc = cam_soc_util_disable_platform_resource(soc_info, false,
 			false);
+		rc = cam_soc_util_release_platform_resource(soc_info);
+	}
 
 	return rc;
 }
@@ -105,15 +109,6 @@
 	if (soc_info->gpio_data &&
 		gpio_num_info &&
 		gpio_num_info->valid[SENSOR_VAF] == 1) {
-		rc = cam_soc_util_request_platform_resource(&o_ctrl->soc_info,
-			NULL, NULL);
-		rc = cam_soc_util_enable_platform_resource(&o_ctrl->soc_info,
-			false, 0, false);
-		if (rc < 0) {
-			CAM_ERR(CAM_OIS, "Failed in req gpio: %d", rc);
-			return rc;
-		}
-
 		gpio_set_value_cansleep(
 			gpio_num_info->gpio_num[SENSOR_VAF],
 			1);
@@ -132,12 +127,6 @@
 		&o_ctrl->soc_info;
 	struct msm_camera_gpio_num_info *gpio_num_info = NULL;
 
-	rc = cam_ois_vreg_control(o_ctrl, 0);
-	if (rc < 0) {
-		CAM_ERR(CAM_OIS, "Failed %d");
-		return rc;
-	}
-
 	gpio_num_info = o_ctrl->gpio_num_info;
 
 	if (soc_info->gpio_data &&
@@ -147,15 +136,12 @@
 		gpio_set_value_cansleep(
 			gpio_num_info->gpio_num[SENSOR_VAF],
 			GPIOF_OUT_INIT_LOW);
-
-		rc = cam_soc_util_release_platform_resource(&o_ctrl->soc_info);
-		rc |= cam_soc_util_disable_platform_resource(&o_ctrl->soc_info,
-					0, 0);
-		if (rc < 0)
-			CAM_ERR(CAM_OIS,
-				"Failed to disable platform resources: %d", rc);
 	}
 
+	rc = cam_ois_vreg_control(o_ctrl, 0);
+	if (rc < 0)
+		CAM_ERR(CAM_OIS, "Disable regualtor Failed %d", rc);
+
 	return rc;
 }
 
@@ -430,7 +416,7 @@
 		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_pkt_parser(i2c_reg_settings,
+		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",
@@ -442,7 +428,7 @@
 			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_pkt_parser(i2c_reg_settings,
+			rc = cam_sensor_i2c_command_parser(i2c_reg_settings,
 				&cmd_desc[2], 1);
 			if (rc < 0) {
 				CAM_ERR(CAM_OIS,
@@ -458,7 +444,7 @@
 		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_pkt_parser(i2c_reg_settings,
+		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);
@@ -475,6 +461,35 @@
 	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) {
+		rc = camera_io_release(&o_ctrl->io_master_info);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS, "Failed in releasing CCI");
+		rc = cam_ois_power_down(o_ctrl);
+		if (rc < 0)
+			CAM_ERR(CAM_OIS, "OIS Power down failed");
+		o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
+	}
+
+	if (o_ctrl->cam_ois_state == CAM_OIS_ACQUIRE) {
+		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
@@ -513,6 +528,7 @@
 			CAM_ERR(CAM_OIS, "Failed to acquire dev");
 			goto release_mutex;
 		}
+		o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
 		break;
 	case CAM_START_DEV:
 		rc = cam_ois_power_up(o_ctrl);
@@ -548,6 +564,7 @@
 				goto pwr_dwn;
 			}
 		}
+		o_ctrl->cam_ois_state = CAM_OIS_START;
 		break;
 	case CAM_CONFIG_DEV:
 		rc = cam_ois_pkt_parse(o_ctrl, arg);
@@ -570,6 +587,7 @@
 		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:
 		rc = camera_io_release(&o_ctrl->io_master_info);
@@ -580,6 +598,7 @@
 			CAM_ERR(CAM_OIS, "OIS Power down failed");
 			goto release_mutex;
 		}
+		o_ctrl->cam_ois_state = CAM_OIS_ACQUIRE;
 		break;
 	default:
 		CAM_ERR(CAM_OIS, "invalid opcode");
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
index 13da82a..6f81d09 100644
--- 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
@@ -16,5 +16,12 @@
 
 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
index aeab388..2629180 100644
--- 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
@@ -34,6 +34,24 @@
 	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)
 {
@@ -99,7 +117,9 @@
 }
 #endif
 
-static const struct v4l2_subdev_internal_ops cam_ois_internal_ops;
+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,
@@ -183,6 +203,7 @@
 	rc = cam_ois_init_subdev_param(o_ctrl);
 	if (rc)
 		goto octrl_free;
+	o_ctrl->cam_ois_state = CAM_OIS_INIT;
 
 	return rc;
 
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
index 794b65a..e341bb7 100644
--- 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
@@ -31,6 +31,13 @@
 #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,
+	CAM_OIS_RELEASE,
+};
+
 /**
  * struct cam_ois_registered_driver_t - registered driver info
  * @platform_driver      :   flag indicates if platform driver is registered
@@ -95,6 +102,7 @@
  * @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
@@ -115,6 +123,7 @@
 	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;
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 cd96129..ac9235d 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.res_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,32 @@
 
 	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_RESCONFIG: {
+		i2c_reg_settings = &i2c_data->res_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 %
@@ -91,10 +185,14 @@
 		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 {
+		cam_sensor_update_req_mgr(s_ctrl, csl_packet);
+		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,28 +201,11 @@
 	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;
-		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);
-	}
 	return rc;
 }
 
@@ -377,6 +458,41 @@
 	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) {
+		cam_sensor_power_down(s_ctrl);
+		s_ctrl->sensor_state = CAM_SENSOR_ACQUIRE;
+	}
+
+	if (s_ctrl->sensor_state == CAM_SENSOR_ACQUIRE) {
+		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;
@@ -511,8 +627,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, slave_id: %d",
+			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");
@@ -525,6 +645,7 @@
 		 * probed on this slot
 		 */
 		s_ctrl->is_probe_succeed = 1;
+		s_ctrl->sensor_state = CAM_SENSOR_INIT;
 	}
 		break;
 	case CAM_ACQUIRE_DEV: {
@@ -565,6 +686,7 @@
 	}
 		break;
 	case CAM_RELEASE_DEV: {
+		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",
@@ -595,26 +717,29 @@
 		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;
+		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;
+			}
 		}
-		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");
-			rc = -EINVAL;
-			goto release_mutex;
-		}
+		s_ctrl->sensor_state = CAM_SENSOR_START;
 	}
 		break;
 	case CAM_STOP_DEV: {
+		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");
+			}
+		}
 		rc = cam_sensor_power_down(s_ctrl);
 		if (rc < 0) {
 			CAM_ERR(CAM_SENSOR, "Sensor Power Down failed");
@@ -628,10 +753,48 @@
 			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_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,
+				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.res_settings.is_settings_valid &&
+			(s_ctrl->i2c_data.res_settings.request_id == 0)) {
+			rc = cam_sensor_apply_settings(s_ctrl, 0,
+				CAM_SENSOR_PACKET_OPCODE_SENSOR_RESCONFIG);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"cannot apply res settings");
+				goto release_mutex;
+			}
+			rc = delete_request(&s_ctrl->i2c_data.res_settings);
+			if (rc < 0) {
+				CAM_ERR(CAM_SENSOR,
+					"Fail in deleting the res settings");
+				goto release_mutex;
+			}
+			s_ctrl->i2c_data.res_settings.request_id = -1;
+		}
 	}
 		break;
-	case CAM_SD_SHUTDOWN:
-		break;
 	default:
 		CAM_ERR(CAM_SENSOR, "Invalid Opcode: %d", cmd->op_code);
 		rc = -EINVAL;
@@ -687,9 +850,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));
 
@@ -731,8 +894,6 @@
 		}
 	}
 
-	s_ctrl->sensor_state = CAM_SENSOR_POWER_UP;
-
 	return rc;
 }
 
@@ -763,20 +924,39 @@
 	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;
-
 	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_RESCONFIG: {
+			i2c_set = &s_ctrl->i2c_data.res_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) {
@@ -790,12 +970,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;
@@ -836,6 +1011,7 @@
 					del_req_id, rc);
 		}
 	}
+
 	return rc;
 }
 
@@ -855,7 +1031,8 @@
 	}
 	CAM_DBG(CAM_SENSOR, " Req Id: %lld", apply->request_id);
 	trace_cam_apply_req("Sensor", apply->request_id);
-	rc = cam_sensor_apply_settings(s_ctrl, 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..f915a0e 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.res_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.res_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_utils/cam_sensor_cmn_header.h b/drivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils/cam_sensor_cmn_header.h
index 97b4c01..ac1e23b 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
@@ -153,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_RESCONFIG,
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_STREAMOFF,
+	CAM_SENSOR_PACKET_OPCODE_SENSOR_NOP = 127
 };
 
 enum cam_actuator_packet_opcodes {
@@ -279,6 +282,9 @@
 
 struct i2c_data_settings {
 	struct i2c_settings_array init_settings;
+	struct i2c_settings_array res_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 bcdaf6d..85d7b74 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
@@ -250,7 +250,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
@@ -260,7 +260,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;
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_sync/cam_sync.c b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
index 3fe7b00..bfeb7c3 100644
--- a/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
+++ b/drivers/media/platform/msm/camera/cam_sync/cam_sync.c
@@ -986,7 +986,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_utils/cam_debug_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_debug_util.h
index 40dbe00..9745d45 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
@@ -119,7 +119,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_soc_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
index 86c2737..8bd8275 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
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 a54f028..ab3223e 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>
@@ -301,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);
@@ -319,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;
 	}
 
@@ -2970,16 +2966,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)
@@ -3007,12 +2994,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,
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 e2f5465..57a68ed 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
@@ -431,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;
@@ -768,6 +765,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_dev.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_dev.c
index f81cd2f..a90bc4c 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);
 
@@ -1643,6 +1636,7 @@
 		}
 
 		request->req = req;
+		request->sequence_id = req->entries[0].item.sequence_id;
 
 		spin_lock(&ctx->list_lock);
 		list_del_init(&request->list);
@@ -2761,6 +2755,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,
@@ -2785,8 +2791,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,
 };
 
 /*
@@ -3000,6 +3006,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:
@@ -3008,6 +3016,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_r3.c b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
index 3206844..f7b4e6e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
@@ -2572,6 +2572,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,
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 cc1407a..e75f36e 100644
--- a/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
+++ b/drivers/media/platform/msm/sde/rotator/sde_rotator_smmu.c
@@ -517,6 +517,7 @@
 	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) {
@@ -612,6 +613,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/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index cf0413e..89e83b8 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -1877,6 +1877,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/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 266c50e..85b4724 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -1068,6 +1068,135 @@
 		),
 		.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,
+	},
 
 };
 
@@ -2095,6 +2224,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;
@@ -2133,6 +2275,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__);
@@ -2281,6 +2428,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);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index ae2a2c6..907e01f 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -626,6 +626,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);
@@ -835,7 +846,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;
@@ -1159,14 +1171,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);
@@ -1174,6 +1189,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 = {
@@ -1488,6 +1507,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:
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
index f2f6d58..5183ddd 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)
 {
@@ -509,6 +522,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 +565,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);
 
@@ -652,7 +689,9 @@
 
 	operating_rate = operating_rate >> 16;
 
-	if ((curr_operating_rate + ops_left) >= operating_rate) {
+	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);
@@ -796,19 +835,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;
@@ -852,6 +878,10 @@
 		}
 		dcvs->max_threshold = output_buf_req->buffer_count_actual -
 			output_buf_req->buffer_count_min_host + 1;
+		/* Compensate for decode only frames */
+		if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_VP9)
+			dcvs->max_threshold += 2;
+
 		dcvs->min_threshold =
 			msm_vidc_get_extra_buff_count(inst, dcvs->buffer_type);
 	} else {
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index f8c5798..a9dbd34 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);
 
@@ -2330,9 +2328,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);
@@ -2458,6 +2453,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);
@@ -2659,6 +2655,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:
@@ -3336,20 +3333,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;
 }
 
@@ -5302,6 +5290,8 @@
 		return -EINVAL;
 	}
 	hdev = core->device;
+
+	mutex_lock(&core->lock);
 	if (core->state == VIDC_CORE_INIT_DONE) {
 		/*
 		 * In current implementation user-initiated SSR triggers
@@ -5317,7 +5307,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;
 }
@@ -5471,15 +5465,15 @@
 	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);
@@ -5520,7 +5514,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};
@@ -5535,7 +5529,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;
@@ -6490,7 +6484,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_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
index 37645fe..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
@@ -422,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 56524ccd4..1818788 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_platform.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_platform.c
@@ -66,6 +66,7 @@
 	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),
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index e260886..60169e9 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -1029,8 +1029,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;
 }
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 53df90f5..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 {
@@ -1398,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/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/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/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/qseecom.c b/drivers/misc/qseecom.c
index 7567f86..fe5dad7 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;
@@ -4298,6 +4297,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");
@@ -4312,7 +4312,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;
@@ -4353,7 +4353,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) {
@@ -4401,7 +4401,7 @@
 	}
 
 exit_free_img_data:
-	__qseecom_free_img_data(&qseecom.cmnlib_ion_handle);
+	__qseecom_free_img_data(&cmnlib_ion_handle);
 	return ret;
 }
 
@@ -7856,7 +7856,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/mmc/card/block.c b/drivers/mmc/card/block.c
index eb14f0f..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)) {
 			/*
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index a378b8c..fb2ca3d 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -431,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 727b301..edbf682 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -4134,6 +4134,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
@@ -4424,6 +4425,7 @@
 		goto out;
 	}
 	mmc_rescan_try_freq(host, host->f_min);
+	host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
 	mmc_release_host(host);
 
  out:
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/mmc.c b/drivers/mmc/core/mmc.c
index 36295f5..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) {
@@ -1413,6 +1414,23 @@
 	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;
@@ -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;
@@ -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 d591ded..10d55b8 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1348,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 591d6b2..f16a999 100644
--- a/drivers/mmc/host/cmdq_hci.c
+++ b/drivers/mmc/host/cmdq_hci.c
@@ -803,6 +803,7 @@
 	if (cq_host->ops->crypto_cfg) {
 		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;
@@ -1015,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));
 
@@ -1030,6 +1032,7 @@
 		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",
@@ -1054,6 +1057,7 @@
 			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",
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 55d9cf5..32da1fd 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -968,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 {
@@ -1281,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);
 		}
@@ -1398,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)
 {
@@ -1578,6 +1612,27 @@
 		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_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_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_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);
+
 	pdata->pctrl_data = pctrl_data;
 out:
 	return ret;
@@ -1986,6 +2041,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;
@@ -3013,11 +3071,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)) {
@@ -3037,6 +3104,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);
@@ -3058,6 +3128,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);
@@ -3149,6 +3221,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);
@@ -3322,6 +3396,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.
@@ -4468,6 +4552,16 @@
 	}
 	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");
@@ -4479,11 +4573,11 @@
 				dev_err(&pdev->dev, "ICE_CLK rate set failed (%d) for %u\n",
 					ret,
 					msm_host->pdata->ice_clk_max);
-				goto pclk_disable;
+				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->ice_clk_max;
@@ -4494,18 +4588,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);
@@ -4870,6 +4964,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);
diff --git a/drivers/mmc/host/sdhci-msm.h b/drivers/mmc/host/sdhci-msm.h
index 7c0b873..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 {
@@ -153,6 +157,8 @@
 	bool sdr104_wa;
 	u32 ice_clk_max;
 	u32 ice_clk_min;
+	u32 ddr_config;
+	bool rclk_wa;
 };
 
 struct sdhci_msm_bus_vote {
@@ -203,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.c b/drivers/mmc/host/sdhci.c
index 4476e51..566be69 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2491,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;
 	}
@@ -2701,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,
@@ -2714,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)
@@ -2916,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;
@@ -2939,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");
@@ -2997,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 |
@@ -3131,6 +3143,7 @@
 			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;
 			}
@@ -3154,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);
@@ -3242,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;
 }
 
@@ -3270,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) {
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/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/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 3ec573c..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)
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/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/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/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index ba652d8..727122d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -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));
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/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c
index 1806b1f..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);
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/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.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/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/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/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/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/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
index 6de8e0e..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;
 	}
 
@@ -758,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;
@@ -779,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++;
@@ -802,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) {
@@ -830,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/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 663e163..89e3fbf 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -23,9 +23,9 @@
 #include <linux/rtnetlink.h>
 #include <linux/pm_runtime.h>
 
-static bool use_msi = true;
+static bool use_msi;
 module_param(use_msi, bool, 0444);
-MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - true");
+MODULE_PARM_DESC(use_msi, " Use MSI interrupt, default - false");
 
 static bool ftm_mode;
 module_param(ftm_mode, bool, 0444);
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 261a0da..1082f66 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;
 		}
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/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/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/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/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/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/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/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/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/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/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index 13e9a0d..e63f1a0 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>
@@ -747,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);
 }
@@ -760,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);
 }
@@ -789,6 +837,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);
@@ -807,10 +858,158 @@
 	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);
@@ -904,14 +1103,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 988b7ca..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,6 +135,7 @@
 	unsigned ngpios;
 	const struct msm_dir_conn *dir_conn;
 	unsigned int n_dir_conns;
+	unsigned int dir_conn_irq_base;
 };
 
 int msm_pinctrl_probe(struct platform_device *pdev,
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
index f7d551e..e77dcd9 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845-v2.c
@@ -53,6 +53,8 @@
 		.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,			\
@@ -67,6 +69,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)	\
@@ -1689,7 +1692,7 @@
 	{24, 517},
 	{26, 518},
 	{30, 519},
-	{31, 639},
+	{31, 632},
 	{32, 521},
 	{34, 522},
 	{36, 523},
@@ -1697,12 +1700,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},
@@ -1727,7 +1730,7 @@
 	{85, 555},
 	{86, 556},
 	{88, 557},
-	{89, 638},
+	{89, 631},
 	{91, 559},
 	{92, 560},
 	{95, 561},
@@ -1747,6 +1750,7 @@
 	{123, 613},
 	{124, 614},
 	{125, 615},
+	{126, 616},
 	{127, 617},
 	{128, 618},
 	{129, 619},
@@ -1754,6 +1758,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 = {
@@ -1766,6 +1778,7 @@
 	.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)
diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c
index b7e06b6..ee5bd19 100644
--- a/drivers/pinctrl/qcom/pinctrl-sdm845.c
+++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c
@@ -52,6 +52,8 @@
 		.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,			\
@@ -66,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)	\
@@ -117,6 +120,7 @@
 		.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"),
@@ -1648,7 +1652,7 @@
 	[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},
@@ -1659,7 +1663,7 @@
 	{24, 517},
 	{26, 518},
 	{30, 519},
-	{31, 639},
+	{31, 632},
 	{32, 521},
 	{34, 522},
 	{36, 523},
@@ -1667,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},
@@ -1697,7 +1701,7 @@
 	{85, 555},
 	{86, 556},
 	{88, 557},
-	{89, 638},
+	{89, 631},
 	{91, 559},
 	{92, 560},
 	{95, 561},
@@ -1717,6 +1721,7 @@
 	{123, 613},
 	{124, 614},
 	{125, 615},
+	{126, 616},
 	{127, 617},
 	{128, 618},
 	{129, 619},
@@ -1724,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 = {
@@ -1736,6 +1749,7 @@
 	.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)
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c
index 4a9232e..59e1871 100644
--- a/drivers/platform/msm/ipa/ipa_api.c
+++ b/drivers/platform/msm/ipa/ipa_api.c
@@ -3004,6 +3004,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..ff1a77a 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_
@@ -385,6 +386,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/ipa_mhi_client.c b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
index 4d3113f..e18c0d4 100644
--- a/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
+++ b/drivers/platform/msm/ipa/ipa_clients/ipa_mhi_client.c
@@ -185,6 +185,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);
@@ -206,8 +212,7 @@
 				goto fail_memcopy;
 			}
 		}
-		dma_free_coherent(pdev, mem.size, mem.base,
-			mem.phys_base);
+		goto dma_succeed;
 	} else {
 		void *host_ptr;
 
@@ -230,9 +235,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;
 }
 
@@ -2499,6 +2509,7 @@
 	else
 		ipa_mhi_delete_rm_resources();
 
+	ipa_dma_destroy();
 	ipa_mhi_debugfs_destroy();
 	destroy_workqueue(ipa_mhi_client_ctx->wq);
 	kfree(ipa_mhi_client_ctx);
@@ -2714,6 +2725,12 @@
 		goto fail_create_wq;
 	}
 
+	res = ipa_dma_init();
+	if (res) {
+		IPA_MHI_ERR("failed to init ipa dma %d\n", res);
+		goto fail_dma_init;
+	}
+
 	if (ipa_pm_is_used())
 		res = ipa_mhi_register_pm();
 	else
@@ -2737,6 +2754,8 @@
 	return 0;
 
 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_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/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
index 1c47e69..2e87bd2 100644
--- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c
@@ -2490,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 a487bf4..fe8cbc0 100644
--- a/drivers/platform/msm/ipa/ipa_common_i.h
+++ b/drivers/platform/msm/ipa/ipa_common_i.h
@@ -18,6 +18,7 @@
 #include <linux/ipc_logging.h>
 #include <linux/ipa.h>
 #include <linux/ipa_uc_offload.h>
+#include <linux/ipa_wdi3.h>
 
 #define __FILENAME__ \
 	(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
@@ -383,6 +384,16 @@
 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);
 
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_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 9bfdcdc..67b0be6 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h
@@ -1546,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 2608e1d..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 */
@@ -673,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_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..210ddfe 100644
--- a/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v2/ipa_utils.c
@@ -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 2f272d2..8ea1d99 100644
--- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c
@@ -649,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;
@@ -1516,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)))
@@ -1540,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);
@@ -1661,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);
@@ -2693,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);
 
@@ -2982,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);
 
@@ -3016,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);
 
@@ -3059,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 5db2545..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_pm.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 e9df986..9d55032 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -236,8 +236,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 +255,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 +436,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)
@@ -4374,11 +4366,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;
@@ -4430,19 +4417,42 @@
 	if (IS_ERR_OR_NULL(subsystem_get_retval)) {
 		IPAERR("Unable to trigger PIL process for FW loading\n");
 		return -EINVAL;
-	} else {
-		subsystem_put(subsystem_get_retval);
 	}
 
 	IPADBG("PIL FW loading process is complete\n");
 	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 };
 
@@ -4456,6 +4466,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;
@@ -4472,71 +4485,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;
 }
 
@@ -4643,10 +4662,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;
@@ -4684,7 +4707,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;
@@ -5006,7 +5032,7 @@
 	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)
@@ -5017,6 +5043,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.
@@ -5029,7 +5062,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;
 		}
 	}
 
@@ -5049,8 +5082,13 @@
 	return 0;
 
 fail_cdev_add:
-fail_ipa_init_interrupts:
-	if (!ipa3_ctx->use_ipa_pm)
+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:
 	if (!ipa3_ctx->use_ipa_pm)
@@ -5061,6 +5099,7 @@
 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];
@@ -5069,7 +5108,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);
@@ -5450,7 +5488,7 @@
 				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);
 		}
@@ -5489,7 +5527,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)) {
@@ -5497,8 +5538,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)) {
@@ -5506,7 +5550,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,
@@ -5520,6 +5564,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);
@@ -5607,20 +5654,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;
@@ -5630,8 +5684,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;
@@ -5641,6 +5695,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) {
@@ -5707,7 +5764,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)) {
@@ -5716,8 +5775,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)) {
@@ -5726,7 +5786,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,
@@ -5739,6 +5799,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);
@@ -5922,17 +5985,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");
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 1af968e..a859ff7 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);
@@ -1012,13 +1032,15 @@
 
 /* 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;
 
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 		ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1039,6 +1061,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 +1142,21 @@
 	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;
+		}
+	}
 	return result;
 }
 
@@ -1133,7 +1186,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 +1372,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_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 7f30a10..ee312c7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c
@@ -136,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);
 			}
 		}
@@ -3816,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;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
index 0883e6f..5d1bbe7 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_flt.c
@@ -753,6 +753,11 @@
 				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) {
@@ -773,7 +778,8 @@
 	}
 
 	if (rule->rule_id) {
-		if (!(rule->rule_id & ipahal_get_rule_id_hi_bit())) {
+		if ((rule->rule_id < ipahal_get_rule_id_hi_bit()) ||
+		(rule->rule_id >= ((ipahal_get_rule_id_hi_bit()<<1)-1))) {
 			IPAERR("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());
@@ -879,7 +885,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 +933,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 +968,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 +1025,11 @@
 				goto error;
 			}
 		}
+	} else {
+		if (frule->rule.rt_tbl_idx > 0) {
+			IPAERR_RL("invalid RT tbl\n");
+			goto error;
+		}
 	}
 
 	entry->rule = frule->rule;
@@ -1380,7 +1394,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..34624c0 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_hdr.c
@@ -1133,6 +1133,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 +1256,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_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index 3e1d188..cc69eee 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -556,6 +556,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;
@@ -1033,11 +1034,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;
@@ -1098,6 +1094,13 @@
 	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
@@ -1296,7 +1299,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;
@@ -1825,6 +1828,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
@@ -2094,6 +2102,8 @@
 			      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);
 
@@ -2234,4 +2244,5 @@
 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);
 #endif /* _IPA3_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
index 2bd7b79..4ada018 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_intf.c
@@ -225,6 +225,7 @@
 		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_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index fbaa4ae..e3a3821 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 */
@@ -805,6 +868,30 @@
 		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) {
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
index 8bd7d30..8d7b107 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_rt.c
@@ -732,6 +732,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) {
@@ -1081,6 +1082,7 @@
 
 	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,
@@ -1126,7 +1128,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",
@@ -1456,6 +1458,7 @@
 		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) {
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..b8928da 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -672,19 +672,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 +695,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 +1821,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 f717264..fd38a2d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -4497,6 +4497,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;
 }
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/ipahal_fltrt.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
index acc72f0..d6dbc85 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_fltrt.c
@@ -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;
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index f08e1e3..e93210d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -677,6 +677,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));
@@ -1639,8 +1641,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)))
@@ -1665,6 +1667,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);
@@ -1731,6 +1735,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);
@@ -2984,6 +2989,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);
 
@@ -3276,6 +3284,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);
 
@@ -3310,6 +3322,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);
 
@@ -3353,6 +3369,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/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..78cdc50 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;
@@ -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/msm_ext_display.c b/drivers/platform/msm/msm_ext_display.c
index d5fc867..73bf935 100644
--- a/drivers/platform/msm/msm_ext_display.c
+++ b/drivers/platform/msm/msm_ext_display.c
@@ -291,6 +291,13 @@
 	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)
 {
@@ -330,6 +337,7 @@
 
 	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)
 {
diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c
index 707c95e..423c8f1 100644
--- a/drivers/platform/msm/qcom-geni-se.c
+++ b/drivers/platform/msm/qcom-geni-se.c
@@ -1284,10 +1284,9 @@
 	geni_se_dev = dev_get_drvdata(rsc->wrapper_dev);
 	if (unlikely(!geni_se_dev || !geni_se_dev->bus_bw))
 		return;
-	mutex_lock(&geni_se_dev->ab_ib_lock);
 	if (unlikely(list_empty(&rsc->ab_list) || list_empty(&rsc->ib_list))) {
 		GENI_SE_DBG(ipc, false, NULL, "%s: Clocks not on\n", __func__);
-		goto exit_geni_se_dump_dbg_regs;
+		return;
 	}
 	m_cmd0 = geni_read_reg(base, SE_GENI_M_CMD0);
 	m_irq_status = geni_read_reg(base, SE_GENI_M_IRQ_STATUS);
@@ -1315,8 +1314,6 @@
 	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);
-exit_geni_se_dump_dbg_regs:
-	mutex_unlock(&geni_se_dev->ab_ib_lock);
 }
 EXPORT_SYMBOL(geni_se_dump_dbg_regs);
 
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index 8495787..93121df 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -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 */
@@ -822,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;
@@ -836,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;
@@ -1735,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)) {
@@ -2101,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++) {
@@ -2460,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,
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index 8d023a8..c090b2a 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -533,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);
@@ -603,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/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/fg-core.h b/drivers/power/supply/qcom/fg-core.h
index 3ca5def..7c10e63 100644
--- a/drivers/power/supply/qcom/fg-core.h
+++ b/drivers/power/supply/qcom/fg-core.h
@@ -426,6 +426,7 @@
 	int			batt_id_ohms;
 	int			ki_coeff_full_soc;
 	int			charge_status;
+	int			prev_charge_status;
 	int			charge_done;
 	int			charge_type;
 	int			online_status;
diff --git a/drivers/power/supply/qcom/fg-memif.c b/drivers/power/supply/qcom/fg-memif.c
index c1b5adc..279b097 100644
--- a/drivers/power/supply/qcom/fg-memif.c
+++ b/drivers/power/supply/qcom/fg-memif.c
@@ -847,8 +847,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))
@@ -860,24 +860,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 fc60b26..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;
 		}
diff --git a/drivers/power/supply/qcom/qpnp-fg-gen3.c b/drivers/power/supply/qcom/qpnp-fg-gen3.c
index fc34a8c9..2a47442 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;
@@ -1148,7 +1163,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;
@@ -1167,7 +1182,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;
@@ -1249,6 +1264,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;
@@ -1434,7 +1464,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;
@@ -1453,16 +1482,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:
@@ -1482,9 +1508,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;
@@ -1497,8 +1521,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);
 
@@ -1511,6 +1536,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);
@@ -1526,8 +1554,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);
@@ -1545,6 +1577,7 @@
 					 batt_soc_msb);
 				chip->cl.active = false;
 				chip->cl.init_cc_uah = 0;
+				prime_cc = true;
 			}
 		}
 
@@ -1561,10 +1594,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);
 }
@@ -1779,7 +1831,8 @@
 			fg_dbg(chip, FG_STATUS, "Terminated charging @ SOC%d\n",
 				msoc);
 		}
-	} else if (msoc_raw <= recharge_soc && chip->charge_full) {
+	} else if ((msoc_raw <= recharge_soc || !chip->charge_done)
+			&& chip->charge_full) {
 		if (chip->dt.linearize_soc) {
 			chip->delta_soc = FULL_CAPACITY - msoc;
 
@@ -2563,7 +2616,7 @@
 	}
 
 	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",
 		chip->charge_status, chip->charge_type, chip->charge_done);
@@ -3555,6 +3608,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;
@@ -3765,6 +3821,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,
@@ -4585,7 +4642,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
@@ -5000,6 +5057,7 @@
 	chip->debug_mask = &fg_gen3_debug_mask;
 	chip->irqs = fg_irqs;
 	chip->charge_status = -EINVAL;
+	chip->prev_charge_status = -EINVAL;
 	chip->ki_coeff_full_soc = -EINVAL;
 	chip->online_status = -EINVAL;
 	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
diff --git a/drivers/power/supply/qcom/qpnp-fg.c b/drivers/power/supply/qcom/qpnp-fg.c
new file mode 100644
index 0000000..17b9c1d3
--- /dev/null
+++ b/drivers/power/supply/qcom/qpnp-fg.c
@@ -0,0 +1,7028 @@
+/* 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 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;
+
+	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;
+		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;
+
+	/* Is the the log buffer empty */
+	if (log->rpos >= log->wpos) {
+		if (get_log_data(trans) <= 0)
+			return 0;
+	}
+
+	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");
+		return -EFAULT;
+	}
+
+	/* 'ret' is the number of bytes not copied */
+	len -= ret;
+
+	*ppos += len;
+	log->rpos += len;
+	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;
+
+	struct fg_trans *trans = file->private_data;
+	u32 offset = trans->offset;
+
+	/* Make a copy of the user data */
+	char *kbuf = kmalloc(count + 1, GFP_KERNEL);
+
+	if (!kbuf)
+		return -ENOMEM;
+
+	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);
+	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 34514c9..1ab0357 100644
--- a/drivers/power/supply/qcom/qpnp-smb2.c
+++ b/drivers/power/supply/qcom/qpnp-smb2.c
@@ -1664,6 +1664,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:
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 52fcd7f..ddc8701 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -2740,17 +2740,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);
@@ -2838,6 +2834,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)
 {
@@ -3059,6 +3064,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);
@@ -3593,6 +3603,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",
@@ -3822,7 +3839,7 @@
 	 * give opportunity to the other side to be a SRC,
 	 * for tDRPTRY + Tccdebounce time
 	 */
-	msleep(100);
+	msleep(120);
 
 	rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
 	if (rc < 0) {
@@ -4007,6 +4024,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);
@@ -4255,7 +4273,7 @@
 
 	if (chg->cc2_detach_wa_active || chg->typec_en_dis_active ||
 					 chg->try_sink_active) {
-		smblib_dbg(chg, PR_INTERRUPT, "Ignoring since %s active\n",
+		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;
@@ -4780,7 +4798,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,
@@ -4789,7 +4809,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) {
@@ -4801,6 +4821,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);
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/smb1355-charger.c b/drivers/power/supply/qcom/smb1355-charger.c
index 4b42420..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;
 
@@ -270,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 *
  *****************************/
@@ -563,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;
@@ -605,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;
 	}
@@ -619,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;
 }
 
@@ -636,6 +804,10 @@
 		.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)
@@ -748,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);
@@ -760,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);
diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c
index 06ecc7e..c759314 100644
--- a/drivers/power/supply/qcom/step-chg-jeita.c
+++ b/drivers/power/supply/qcom/step-chg-jeita.c
@@ -123,7 +123,7 @@
 		/* TEMP_LOW	TEMP_HIGH	FCC */
 		{0,		100,		600000},
 		{101,		200,		2000000},
-		{201,		450,		3000000},
+		{201,		450,		3450000},
 		{451,		550,		600000},
 	},
 };
@@ -135,7 +135,7 @@
 	.fv_cfg		= {
 		/* TEMP_LOW	TEMP_HIGH	FCC */
 		{0,		100,		4200000},
-		{101,		450,		4400000},
+		{101,		450,		4350000},
 		{451,		550,		4200000},
 	},
 };
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..ec492af 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -4905,6 +4905,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/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
index cfc09ba..66f9f66 100644
--- a/drivers/regulator/cpr4-apss-regulator.c
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -54,6 +54,8 @@
  * @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.
  */
@@ -67,6 +69,7 @@
 	u64	boost_cfg;
 	u64	boost_voltage;
 	u64	misc;
+	u64	aging_init_quot_diff;
 };
 
 /*
@@ -161,6 +164,12 @@
 	{},
 };
 
+static const struct cpr3_fuse_param msm8953_apss_aging_init_quot_diff_param[]
+= {
+	{72, 0, 7},
+	{},
+};
+
 /*
  * The number of possible values for misc fuse is
  * 2^(#bits defined for misc fuse)
@@ -206,6 +215,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
@@ -257,6 +274,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],
@@ -1212,6 +1237,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;
+	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 +1490,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/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index dbe2a08..6b1e480 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -1226,6 +1226,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 +1238,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 +1348,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 +1362,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 +1376,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,
 };
@@ -3003,7 +3012,7 @@
 				struct device_node *of_node)
 {
 	int rc = 0;
-	u32 i, tmp = 0;
+	u32 i = 0, tmp = 0;
 	u8 val, mask;
 
 	/*
@@ -3037,37 +3046,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);
@@ -3723,7 +3743,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/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/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_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..9bd9b9a 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"
@@ -273,25 +273,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/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/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/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 8c4641b..9a34afc 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -318,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) {
@@ -343,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);
@@ -417,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 4bd6fd4..0114e2a 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> */
@@ -146,8 +146,7 @@
 	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 */
@@ -833,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)
 {
@@ -949,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);
@@ -962,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;
 		}
@@ -1031,40 +1064,13 @@
 			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,
 						SZ_SG_REQ_INFO * SG_MAX_QUEUE);
@@ -1172,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;
@@ -2055,7 +2061,6 @@
 	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;
@@ -2068,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))) {
@@ -2086,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;
@@ -2168,7 +2148,7 @@
 
 	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;
@@ -2209,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,
@@ -2615,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;
@@ -2635,9 +2618,7 @@
 		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) {
@@ -2672,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-qcom-debugfs.c b/drivers/scsi/ufs/ufs-qcom-debugfs.c
index 4547a6d..494ecd1 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,12 +111,15 @@
 				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,
 		&buff_pos, ubuf, cnt);
@@ -125,6 +128,7 @@
 			__func__);
 		goto out;
 	}
+	configuration[ret] = '\0';
 
 	comma = strnchr(configuration, TESTBUS_CFG_BUFF_LINE_SIZE, ',');
 	if (!comma || comma == configuration) {
@@ -142,8 +146,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.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 32edd76..0ab656e 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -100,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 */
@@ -390,6 +390,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.c b/drivers/scsi/ufs/ufshcd.c
index 8405537..b98d2ae 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,
 };
@@ -4802,8 +4805,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 &&
@@ -6471,6 +6475,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
@@ -6510,6 +6560,8 @@
 					hba->full_init_linereset = true;
 				}
 			}
+			if (!hba->full_init_linereset)
+				schedule_work(&hba->rls_work);
 		}
 		retval |= IRQ_HANDLED;
 	}
@@ -10450,6 +10502,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 1b21238..1f5c404 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -873,6 +873,7 @@
 	/* Work Queues */
 	struct work_struct eh_work;
 	struct work_struct eeh_work;
+	struct work_struct rls_work;
 
 	/* HBA Errors */
 	u32 errors;
@@ -987,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)
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 3ae9a10..4c9210a 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -2512,7 +2512,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 c94f915..62306bad 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -590,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"
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 4966c04..9a4e010 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -73,7 +73,6 @@
 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
diff --git a/drivers/soc/qcom/dcc_v2.c b/drivers/soc/qcom/dcc_v2.c
index e11efb0..b986328 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)
 
@@ -536,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++) {
 
@@ -1088,14 +1089,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);
 
@@ -1110,18 +1127,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;
@@ -1171,16 +1182,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);
 
@@ -1197,23 +1229,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;
@@ -1418,6 +1442,64 @@
 	dcc_sram_dev_deregister(drvdata);
 }
 
+static void dcc_configure_list(struct dcc_drvdata *drvdata,
+			       struct device_node *np)
+{
+	int ret, i;
+	const __be32 *prop;
+	uint32_t len, entry, val1, val2, apb_bus;
+	uint32_t curr_link_list;
+
+	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;
+
+	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;
+			}
+		}
+
+		if (!ret)
+			dcc_enable(drvdata);
+	}
+}
+
 static int dcc_probe(struct platform_device *pdev)
 {
 	int ret, i;
@@ -1492,6 +1574,8 @@
 	if (ret)
 		goto err;
 
+	dcc_configure_list(drvdata, pdev->dev.of_node);
+
 	return 0;
 err:
 	return ret;
@@ -1527,13 +1611,7 @@
 {
 	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 f7f3317..3c4238c 100644
--- a/drivers/soc/qcom/eud.c
+++ b/drivers/soc/qcom/eud.c
@@ -29,6 +29,9 @@
 #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
@@ -74,6 +77,10 @@
 	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[] = {
@@ -487,12 +494,47 @@
 	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) {
@@ -575,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);
@@ -587,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/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/icnss.c b/drivers/soc/qcom/icnss.c
index 9742f7a..04c611c 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);		\
@@ -2103,7 +2105,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;
@@ -2115,10 +2118,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;
 	}
 
@@ -2226,6 +2234,7 @@
 static int icnss_driver_event_register_driver(void *data)
 {
 	int ret = 0;
+	int probe_cnt = 0;
 
 	if (penv->ops)
 		return -EEXIST;
@@ -2245,11 +2254,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;
 	}
 
@@ -2636,8 +2649,7 @@
 	clear_bit(ICNSS_HOST_TRIGGERED_PDR, &priv->state);
 
 	fw_down_data.crashed = event_data->crashed;
-	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state) &&
-	      !test_bit(ICNSS_PD_RESTART, &priv->state))
+	if (test_bit(ICNSS_FW_READY, &priv->state))
 		icnss_call_driver_uevent(priv, ICNSS_UEVENT_FW_DOWN,
 					 &fw_down_data);
 	icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
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-sdm670.c b/drivers/soc/qcom/llcc-sdm670.c
index 68ad755..494b93b 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, 0x0, 0x8, 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/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_noc_rpmh.c b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
index bdcdf29..282f2ac 100644
--- a/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
+++ b/drivers/soc/qcom/msm_bus/msm_bus_noc_rpmh.c
@@ -33,6 +33,7 @@
 #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))
@@ -107,6 +108,16 @@
 	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)
@@ -159,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)
@@ -355,6 +378,16 @@
 		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)
@@ -395,45 +428,17 @@
 
 	spin_lock_irqsave(&noc_lock, flags);
 
-	if (!memnoc_qos_base) {
-		MSM_BUS_ERR("Memnoc QoS Base address not found!");
+	if (!qm_base) {
+		MSM_BUS_ERR("QM CFG base address not found!");
 		goto noc_throttle_exit;
 	}
 
 	if (enable) {
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 2,
-								0x1000, 0x1B);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 3,
-								0x1000, 0x1B);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 10,
-								0x1000, 0x30);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 11,
-								0x1000, 0x30);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 10,
-								0x1000, 1);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 11,
-								0x1000, 1);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 2,
-								0x1000, 1);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 3,
-								0x1000, 1);
+		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_enable_qos_limiter(memnoc_qos_base, 0x10000, 2,
-								0x1000, 0);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 3,
-								0x1000, 0);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 10,
-								0x1000, 0);
-		noc_enable_qos_limiter(memnoc_qos_base, 0x10000, 11,
-								0x1000, 0);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 2,
-								0x1000, 0);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 3,
-								0x1000, 0);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 10,
-								0x1000, 0);
-		noc_set_qos_limit_bw(memnoc_qos_base, 0x10000, 11,
-								0x1000, 0);
+		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:
diff --git a/drivers/soc/qcom/pil-msa.c b/drivers/soc/qcom/pil-msa.c
index ec3063e..a3eb551 100644
--- a/drivers/soc/qcom/pil-msa.c
+++ b/drivers/soc/qcom/pil-msa.c
@@ -124,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;
@@ -153,9 +146,6 @@
 		writel_relaxed(regval, drv->cxrail_bhs);
 	}
 
-	if (drv->vreg)
-		return regulator_disable(drv->vreg);
-
 	return 0;
 }
 
@@ -456,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);
@@ -472,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,
diff --git a/drivers/soc/qcom/pil-msa.h b/drivers/soc/qcom/pil-msa.h
index 7a6a3cc..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;
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index cf5f7e4..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
@@ -332,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/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/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-geni-qcom.c b/drivers/spi/spi-geni-qcom.c
index 8dc42ac..7aaf08b 100644
--- a/drivers/spi/spi-geni-qcom.c
+++ b/drivers/spi/spi-geni-qcom.c
@@ -716,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;
@@ -1199,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) {
@@ -1293,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/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/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/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/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/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/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/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c
index af119f4..9c819fb 100644
--- a/drivers/tty/serial/msm_geni_serial.c
+++ b/drivers/tty/serial/msm_geni_serial.c
@@ -117,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)
 
@@ -184,6 +184,7 @@
 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 atomic_t uart_line_id = ATOMIC_INIT(0);
 
@@ -241,26 +242,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_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;
@@ -329,6 +344,7 @@
 				__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__,
@@ -362,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);
@@ -384,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))
@@ -404,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,
@@ -456,6 +485,14 @@
 
 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);
 }
@@ -541,6 +578,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
@@ -789,13 +827,16 @@
 	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))
+		pm_runtime_get(uport->dev);
+
 	if (msm_port->xfer_mode == FIFO_MODE) {
 		geni_status = geni_read_reg_nolog(uport->membase,
 						  SE_GENI_STATUS);
@@ -828,6 +869,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)
@@ -855,7 +899,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__);
@@ -907,7 +951,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__);
@@ -916,7 +960,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) {
@@ -938,8 +983,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;
 		}
 	}
 	/*
@@ -948,6 +993,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);
 }
 
@@ -976,9 +1022,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;
@@ -1007,7 +1053,17 @@
 	/* 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,
@@ -1096,6 +1152,15 @@
 	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;
 	}
@@ -1152,8 +1217,7 @@
 		msm_port->xmit_size = xmit_size;
 	}
 exit_handle_tx:
-	if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
-		uart_write_wakeup(uport);
+	uart_write_wakeup(uport);
 	return ret;
 }
 
@@ -1220,8 +1284,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;
 }
 
@@ -1241,7 +1315,7 @@
 	spin_lock_irqsave(&uport->lock, flags);
 	if (uart_console(uport) && uport->suspended)
 		goto exit_geni_serial_isr;
-	if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) {
+	if (!uart_console(uport) && pm_runtime_suspended(uport->dev)) {
 		dev_err(uport->dev, "%s.Device is suspended.\n", __func__);
 		IPC_LOG_MSG(msm_port->ipc_log_misc,
 				"%s.Device is suspended.\n", __func__);
@@ -1511,47 +1585,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;
 }
@@ -1586,7 +1619,6 @@
 			goto exit_startup;
 	}
 
-	msm_geni_serial_start_rx(uport);
 	/*
 	 * Ensure that all the port configuration writes complete
 	 * before returning to the framework.
@@ -1707,11 +1739,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;
@@ -1800,6 +1838,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;
 
 }
@@ -1810,10 +1851,10 @@
 	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)) {
+	if (!uart_console(uport) && device_pending_suspend(uport)) {
 		IPC_LOG_MSG(port->ipc_log_pwr,
 			"%s Device suspended,vote clocks on.\n", __func__);
-		return 1;
+		return 0;
 	}
 
 	if (port->xfer_mode == SE_DMA)
@@ -2069,13 +2110,54 @@
 }
 #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,
@@ -2319,7 +2401,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);
 
@@ -2347,12 +2429,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;
 
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 6a05d5b..792fb3b 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -53,7 +53,6 @@
 #include <linux/sysfs.h>
 #include <linux/stat.h>
 #include <linux/device.h>
-#include <linux/wakelock.h>
 #include <linux/debugfs.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
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-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..6e7bafe 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);
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index c8075eb..860108c 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1577,7 +1577,11 @@
 			totlen += isopkt[u].length;
 		}
 		u *= sizeof(struct usb_iso_packet_descriptor);
-		uurb->buffer_length = totlen;
+		if (totlen <= uurb->buffer_length)
+			uurb->buffer_length = totlen;
+		else
+			WARN_ONCE(1, "uurb->buffer_length is too short %d vs %d",
+				  totlen, uurb->buffer_length);
 		break;
 
 	default:
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3b0cc03..1c9fbcb 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4841,7 +4841,7 @@
 			goto loop;
 
 		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
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/dwc3/core.c b/drivers/usb/dwc3/core.c
index 4b78e02..5ca987a 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -977,41 +977,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;
@@ -1019,21 +992,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 */
@@ -1210,6 +1171,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");
 
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 5af75fd..63d0a3e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -952,6 +952,7 @@
  * @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;
@@ -1147,6 +1148,7 @@
 	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/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index ccdf543..d0fc511 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -3667,9 +3667,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 +3717,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,7 +3785,7 @@
 		}
 
 		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);
@@ -3956,20 +3945,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) {
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index ec9ffc1..1c33051 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -878,10 +878,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..f4f378c 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -950,9 +950,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;
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 02adc93..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,
@@ -2162,6 +2164,8 @@
 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 d6e77a5..885ed26 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1179,11 +1179,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 +1196,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 +1221,7 @@
 		configfs_add_default_group(&d->group, os_desc_group);
 	}
 
-	return 0;
+	return os_desc_group;
 }
 EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
 
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_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_gsi.c b/drivers/usb/gadget/function/f_gsi.c
index c11629d..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;
@@ -2679,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:
@@ -2827,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);
 }
 
@@ -2991,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
@@ -3020,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)
@@ -3058,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;
 }
@@ -3083,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) {
@@ -3090,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_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_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/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 d2cfefa..bb89e24 100644
--- a/drivers/usb/gadget/udc/renesas_usb3.c
+++ b/drivers/usb/gadget/udc/renesas_usb3.c
@@ -879,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;
@@ -900,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);
 
@@ -991,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);
@@ -1568,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 58b9685..ee213c5 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -447,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__);
@@ -1022,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)
@@ -1069,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) {
@@ -1100,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..667e596 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]);
@@ -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 14c0203..588546a 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -313,7 +313,7 @@
 	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))
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index db46db4..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;
@@ -1670,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;
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/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
index e8961b5..aabfb41 100644
--- a/drivers/usb/pd/policy_engine.c
+++ b/drivers/usb/pd/policy_engine.c
@@ -1038,6 +1038,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)
@@ -1211,14 +1221,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);
@@ -1358,6 +1367,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);
@@ -2046,6 +2063,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) {
@@ -2208,8 +2229,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);
@@ -2217,24 +2241,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:
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
index 735774a..6395ca2 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;
 	}
@@ -668,15 +667,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 +723,6 @@
 		false);
 	pdphy->rx_bytes += size + 1;
 done:
-	pdphy->rx_busy = false;
 	return IRQ_HANDLED;
 }
 
@@ -819,7 +808,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 68bd576..6d97dec 100644
--- a/drivers/usb/phy/phy-msm-qusb-v2.c
+++ b/drivers/usb/phy/phy-msm-qusb-v2.c
@@ -63,17 +63,6 @@
 #define LINESTATE_DP			BIT(0)
 #define LINESTATE_DM			BIT(1)
 
-/* eud related registers */
-#define EUD_SW_ATTACH_DET	0x1018
-#define EUD_INT1_EN_MASK	0x0024
-
-/* EUD interrupt mask bits */
-#define EUD_INT_RX		BIT(0)
-#define EUD_INT_TX		BIT(1)
-#define EUD_INT_VBUS		BIT(2)
-#define EUD_INT_CHGR		BIT(3)
-#define EUD_INT_SAFE_MODE	BIT(4)
-
 unsigned int phy_tune1;
 module_param(phy_tune1, uint, 0644);
 MODULE_PARM_DESC(phy_tune1, "QUSB PHY v2 TUNE1");
@@ -92,7 +81,6 @@
 	struct usb_phy		phy;
 	struct mutex		lock;
 	void __iomem		*base;
-	void __iomem		*eud_base;
 	void __iomem		*efuse_reg;
 
 	struct clk		*ref_clk_src;
@@ -507,17 +495,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)
@@ -676,22 +655,6 @@
 			return ret;
 		}
 		qphy->dpdm_enable = true;
-
-		if (qphy->eud_base) {
-			if (qphy->cfg_ahb_clk)
-				clk_prepare_enable(qphy->cfg_ahb_clk);
-			writel_relaxed(BIT(0),
-					qphy->eud_base + EUD_SW_ATTACH_DET);
-			/* to flush above write before next write */
-			wmb();
-
-			writel_relaxed(EUD_INT_VBUS | EUD_INT_CHGR,
-					qphy->eud_base + EUD_INT1_EN_MASK);
-			/* to flush above write before turning off clk */
-			wmb();
-			if (qphy->cfg_ahb_clk)
-				clk_disable_unprepare(qphy->cfg_ahb_clk);
-		}
 	}
 
 	return ret;
@@ -706,16 +669,6 @@
 				__func__, qphy->dpdm_enable);
 
 	if (qphy->dpdm_enable) {
-		if (qphy->eud_base) {
-			if (qphy->cfg_ahb_clk)
-				clk_prepare_enable(qphy->cfg_ahb_clk);
-			writel_relaxed(0, qphy->eud_base + EUD_SW_ATTACH_DET);
-			/* to flush above write before turning off clk */
-			wmb();
-			if (qphy->cfg_ahb_clk)
-				clk_disable_unprepare(qphy->cfg_ahb_clk);
-		}
-
 		ret = qusb_phy_enable_power(qphy, false);
 		if (ret < 0) {
 			dev_dbg(qphy->phy.dev,
@@ -822,17 +775,6 @@
 		}
 	}
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-							"eud_base");
-	if (res) {
-		qphy->eud_base = devm_ioremap(dev, res->start,
-					resource_size(res));
-		if (IS_ERR(qphy->eud_base)) {
-			dev_dbg(dev, "couldn't ioremap eud_base\n");
-			qphy->eud_base = NULL;
-		}
-	}
-
 	/* 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)) {
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/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 470b17b..11ee55e 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -171,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/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 2a99443..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 */
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/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/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/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/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/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/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/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/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 0437e5f..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);
diff --git a/fs/direct-io.c b/fs/direct-io.c
index c60756e..c6220a2 100644
--- a/fs/direct-io.c
+++ b/fs/direct-io.c
@@ -835,7 +835,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/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/file.c b/fs/ext4/file.c
index d17d12e..510e664 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -527,7 +527,7 @@
 	inode_lock(inode);
 
 	isize = i_size_read(inode);
-	if (offset >= isize) {
+	if (offset < 0 || offset >= isize) {
 		inode_unlock(inode);
 		return -ENXIO;
 	}
@@ -590,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..496c9b5 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -2120,15 +2120,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--;
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/super.c b/fs/ext4/super.c
index 5fa9ba1..f72535e 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 */
@@ -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/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/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/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/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/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/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/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/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/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/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/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c
index bce2e26..6c95812 100644
--- a/fs/xfs/xfs_ioctl.c
+++ b/fs/xfs/xfs_ioctl.c
@@ -1085,6 +1085,7 @@
 	int			*join_flags)
 {
 	struct inode		*inode = VFS_I(ip);
+	struct super_block	*sb = inode->i_sb;
 	int			error;
 
 	*join_flags = 0;
@@ -1097,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_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/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/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/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,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 e20175d..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
@@ -252,7 +259,10 @@
 #define	MSM_BUS_MASTER_CAMNOC_SF_UNCOMP 148
 #define	MSM_BUS_MASTER_GIC 149
 #define	MSM_BUS_MASTER_EMMC 150
-#define	MSM_BUS_MASTER_MASTER_LAST 151
+#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
@@ -593,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
diff --git a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h b/include/dt-bindings/soc/qcom,dcc_v2.h
similarity index 60%
copy from drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
copy to include/dt-bindings/soc/qcom,dcc_v2.h
index 71b21b9..fb4ed6d 100644
--- a/drivers/media/platform/msm/camera/cam_jpeg/jpeg_hw/include/cam_dma_hw_intf.h
+++ b/include/dt-bindings/soc/qcom,dcc_v2.h
@@ -10,19 +10,11 @@
  * GNU General Public License for more details.
  */
 
-#ifndef CAM_JPEG_DMA_HW_INTF_H
-#define CAM_JPEG_DMA_HW_INTF_H
+#ifndef __DT_BINDINGS_QCOM_DCC_V2_H
+#define __DT_BINDINGS_QCOM_DCC_V2_H
 
-#include <uapi/media/cam_defs.h>
-#include <media/cam_jpeg.h>
+#define DCC_READ	0
+#define DCC_WRITE	1
+#define DCC_LOOP	2
 
-#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,
-};
-
-#endif /* CAM_JPEG_DMA_HW_INTF_H */
+#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/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/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 6be0299..7a2ae2f 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -128,6 +128,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 cd32a49..d807fa9 100644
--- a/include/linux/cpuset.h
+++ b/include/linux/cpuset.h
@@ -55,7 +55,9 @@
 
 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);
@@ -168,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-mapping-fast.h b/include/linux/dma-mapping-fast.h
index 64ae548..e9dabab 100644
--- a/include/linux/dma-mapping-fast.h
+++ b/include/linux/dma-mapping-fast.h
@@ -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;
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 99eb77a..3d9e6b8 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -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)
 
@@ -143,6 +144,7 @@
 	DOMAIN_ATTR_PAGE_TABLE_FORCE_COHERENT,
 	DOMAIN_ATTR_CB_STALL_DISABLE,
 	DOMAIN_ATTR_UPSTREAM_IOVA_ALLOCATOR,
+	DOMAIN_ATTR_QCOM_MMU500_ERRATA_MIN_ALIGN,
 	DOMAIN_ATTR_MAX,
 };
 
diff --git a/include/linux/ipa.h b/include/linux/ipa.h
index f8e7e8c..02736245 100644
--- a/include/linux/ipa.h
+++ b/include/linux/ipa.h
@@ -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
 
 /*
@@ -1530,6 +1540,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) */
 
 /*
@@ -2276,6 +2301,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/key.h b/include/linux/key.h
index 7229147..6a54472 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -176,6 +176,7 @@
 #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_UID_KEYRING	11	/* 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
@@ -235,6 +236,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);
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/host.h b/include/linux/mmc/host.h
index 1e07ed2..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);
@@ -563,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/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/netdevice.h b/include/linux/netdevice.h
index 266471e..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,
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/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/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/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/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/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..38a02fd 100644
--- a/include/net/netfilter/nf_conntrack.h
+++ b/include/net/netfilter/nf_conntrack.h
@@ -120,6 +120,8 @@
 	/* Extensions */
 	struct nf_ct_ext *ext;
 
+	void *sfe_entry;
+
 	/* Storage reserved for other modules, must be the last member */
 	union nf_conntrack_proto proto;
 };
@@ -314,6 +316,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..af67969 100644
--- a/include/net/netfilter/nf_conntrack_core.h
+++ b/include/net/netfilter/nf_conntrack_core.h
@@ -50,6 +50,7 @@
 			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);
 
 /* 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/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/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/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_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/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/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..b4bcedf 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -1775,16 +1775,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/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/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_pp.h b/include/uapi/drm/msm_drm_pp.h
index 0765527..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
diff --git a/include/uapi/linux/esoc_ctrl.h b/include/uapi/linux/esoc_ctrl.h
index 4201c95..e2bd12b 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)
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/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/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/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 d750568..1df8c41 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -1134,6 +1134,37 @@
 	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/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/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/bpf/verifier.c b/kernel/bpf/verifier.c
index 779c871..372454a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1720,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;
 			}
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/cpu.c b/kernel/cpu.c
index 69dc428..0d10ef5 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -239,6 +239,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 +898,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 +910,8 @@
 		return -EBUSY;
 
 	cpu_hotplug_begin();
+	if (trace_cpuhp_latency_enabled())
+		start_time = sched_clock();
 
 	cpuhp_tasks_frozen = tasks_frozen;
 
@@ -942,6 +950,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 +1024,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 +1076,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;
 }
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index d3a7411..af9159a 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -808,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
@@ -825,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();
 }
 
 /**
@@ -877,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)
 {
@@ -932,7 +930,7 @@
 	rcu_read_unlock();
 
 	if (need_rebuild_sched_domains)
-		rebuild_sched_domains_locked();
+		rebuild_sched_domains_unlocked();
 }
 
 /**
@@ -1282,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;
@@ -1313,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,
@@ -1348,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);
@@ -1616,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;
@@ -1653,6 +1651,7 @@
 	}
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 	return retval;
 }
 
@@ -1663,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;
@@ -1677,6 +1677,7 @@
 	}
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
+	put_online_cpus();
 	return retval;
 }
 
@@ -1715,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;
@@ -1740,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);
@@ -2046,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))
@@ -2062,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)
@@ -2307,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
  *
@@ -2381,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)
@@ -2401,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 f6e81b5..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);
 	}
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/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/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/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/core.c b/kernel/sched/core.c
index d43ad69..01a589c 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. */
@@ -7961,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);
 }
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
index 772d97b..cc5a97c 100644
--- a/kernel/sched/core_ctl.c
+++ b/kernel/sched/core_ctl.c
@@ -22,6 +22,8 @@
 #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
@@ -36,6 +38,7 @@
 	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;
@@ -348,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],
@@ -359,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;
@@ -472,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
@@ -555,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);
@@ -599,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)
@@ -637,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);
 
@@ -667,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;
@@ -680,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);
@@ -688,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();
 
@@ -791,10 +797,18 @@
 			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;
 
@@ -1107,6 +1121,7 @@
 	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);
 
diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c
index 2eb966c..3192612 100644
--- a/kernel/sched/cpufreq_schedutil.c
+++ b/kernel/sched/cpufreq_schedutil.c
@@ -86,6 +86,7 @@
 
 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 ***********************/
 
@@ -162,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;
@@ -313,10 +315,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;
 
@@ -324,15 +331,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);
 		/*
@@ -343,6 +365,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)
@@ -375,7 +398,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;
 		}
@@ -395,6 +418,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;
@@ -513,7 +539,7 @@
 {
 	struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
 
-	return sprintf(buf, "%u\n", tunables->hispeed_load);
+	return scnprintf(buf, PAGE_SIZE, "%u\n", tunables->hispeed_load);
 }
 
 static ssize_t hispeed_load_store(struct gov_attr_set *attr_set,
@@ -533,7 +559,7 @@
 {
 	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,
@@ -565,7 +591,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,
@@ -681,6 +707,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())
@@ -689,6 +740,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;
@@ -743,6 +811,8 @@
 	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",
 				   schedutil_gov.name);
@@ -782,8 +852,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/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 7b459ca..3d933a0 100755
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3223,6 +3223,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) {}
@@ -5563,6 +5593,13 @@
 	return DIV_ROUND_UP(util << SCHED_CAPACITY_SHIFT, capacity);
 }
 
+static inline bool bias_to_waker_cpu_enabled(struct task_struct *wakee,
+		struct task_struct *waker)
+{
+	return task_util(waker) > sched_big_waker_task_load &&
+		task_util(wakee) < sched_small_wakee_task_load;
+}
+
 static inline bool
 bias_to_waker_cpu(struct task_struct *p, int cpu, struct cpumask *rtg_target)
 {
@@ -6917,6 +6954,7 @@
 	struct related_thread_group *grp;
 	cpumask_t search_cpus;
 	int prev_cpu = task_cpu(p);
+	struct task_struct *curr = cpu_rq(cpu)->curr;
 #ifdef CONFIG_SCHED_CORE_ROTATE
 	bool do_rotate = false;
 	bool avoid_prev_cpu = false;
@@ -6943,7 +6981,8 @@
 	if (grp && grp->preferred_cluster)
 		rtg_target = &grp->preferred_cluster->cpus;
 
-	if (sync && bias_to_waker_cpu(p, cpu, rtg_target)) {
+	if (sync && bias_to_waker_cpu_enabled(p, curr) &&
+		bias_to_waker_cpu(p, cpu, rtg_target)) {
 		trace_sched_task_util_bias_to_waker(p, prev_cpu,
 					task_util(p), cpu, cpu, 0, need_idle);
 		return cpu;
@@ -8391,6 +8430,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;
@@ -8398,9 +8439,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/sched.h b/kernel/sched/sched.h
index 7ac731d..b5a271b 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -2349,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) \
diff --git a/kernel/sched/walt.c b/kernel/sched/walt.c
index 316c276..1f5639c 100644
--- a/kernel/sched/walt.c
+++ b/kernel/sched/walt.c
@@ -162,6 +162,13 @@
  */
 __read_mostly unsigned int sysctl_sched_freq_reporting_policy;
 
+
+#define SCHED_BIG_WAKER_TASK_LOAD_PCT 25UL
+#define SCHED_SMALL_WAKEE_TASK_LOAD_PCT 10UL
+
+__read_mostly unsigned int sched_big_waker_task_load;
+__read_mostly unsigned int sched_small_wakee_task_load;
+
 static int __init set_sched_ravg_window(char *str)
 {
 	unsigned int window_size;
@@ -2833,14 +2840,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);
@@ -3116,4 +3120,9 @@
 
 	walt_cpu_util_freq_divisor =
 	    (sched_ravg_window >> SCHED_CAPACITY_SHIFT) * 100;
+
+	sched_big_waker_task_load =
+		(SCHED_BIG_WAKER_TASK_LOAD_PCT << SCHED_CAPACITY_SHIFT) / 100;
+	sched_small_wakee_task_load =
+		(SCHED_SMALL_WAKEE_TASK_LOAD_PCT << SCHED_CAPACITY_SHIFT) / 100;
 }
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/sysctl.c b/kernel/sysctl.c
index 2c4cd17..7eb1d73e 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1292,6 +1292,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/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/timer.c b/kernel/time/timer.c
index 80aa30d..ccf6499 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -245,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);
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 6e432ed..5b8d718 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -2747,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;
 	}
 
@@ -2808,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)
@@ -4379,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);
@@ -5979,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 cddedb5..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
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/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/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_hotplug.c b/mm/memory_hotplug.c
index 6eb61a4..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);
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index af783a6..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
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/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/dev.c b/net/core/dev.c
index bc129b0..7aab8f6 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);
@@ -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,9 @@
 	return 0;
 }
 
+int (*athrs_fast_nat_recv)(struct sk_buff *skb) __rcu __read_mostly;
+EXPORT_SYMBOL(athrs_fast_nat_recv);
+
 static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
 {
 	struct packet_type *ptype, *pt_prev;
@@ -4111,6 +4182,7 @@
 	bool deliver_exact = false;
 	int ret = NET_RX_DROP;
 	__be16 type;
+	int (*fast_recv)(struct sk_buff *skb);
 
 	net_timestamp_check(!netdev_tstamp_prequeue, skb);
 
@@ -4170,6 +4242,14 @@
 			goto out;
 	}
 #endif
+	fast_recv = rcu_dereference(athrs_fast_nat_recv);
+	if (fast_recv) {
+		if (fast_recv(skb)) {
+			ret = NET_RX_SUCCESS;
+			goto out;
+		}
+	}
+
 #ifdef CONFIG_NET_CLS_ACT
 	skb->tc_verd = 0;
 ncls:
@@ -4843,6 +4923,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 +4956,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();
@@ -8028,7 +8113,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;
@@ -8072,6 +8157,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/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..62893eb 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
@@ -1258,9 +1259,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 +3119,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 +3160,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 4d26297..c2339b8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3758,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/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/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/fib_frontend.c b/net/ipv4/fib_frontend.c
index c8409ca0..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);
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..b8597d2
--- /dev/null
+++ b/net/ipv4/netfilter/ipt_NATTYPE.c
@@ -0,0 +1,595 @@
+/* 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 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.
+ */
+static bool nattype_refresh_timer(struct ipt_nattype *nte)
+{
+	if (del_timer(&nte->timeout)) {
+		nte->timeout.expires = jiffies + NATTYPE_TIMEOUT * HZ;
+		add_timer(&nte->timeout);
+		return true;
+	}
+	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);
+	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));
+		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;
+
+			/* netfilter NATTYPE
+			 * Refresh the timer, if we fail, break
+			 * out and forward fail as though we never
+			 * found the entry.
+			 */
+			if (!nattype_refresh_timer(nte))
+				break;
+
+			/* netfilter NATTYPE
+			 * The entry is found and refreshed, the
+			 * entry values should not change so print
+			 * them outside the lock.
+			 */
+			spin_unlock_bh(&nattype_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;
+
+		/* 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(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.
+		 */
+		spin_unlock_bh(&nattype_lock);
+		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);
+	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)
+{
+	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/route.c b/net/ipv4/route.c
index a4faf30..cd632e6 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -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/tcp_output.c b/net/ipv4/tcp_output.c
index a835716..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)
@@ -3374,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/udp.c b/net/ipv4/udp.c
index 7c1f5f6..200c9b6 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -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 6de016f..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))));
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 5da8649..7ba9a6e 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -914,6 +914,7 @@
 			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;
@@ -928,6 +929,7 @@
 					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;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index ae87b9a..48e6e75 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -940,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.
@@ -1301,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_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/udp.c b/net/ipv6/udp.c
index c43ef0c..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);
@@ -1381,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/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..072e80a 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -95,19 +95,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 +155,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 +187,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 +387,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 +404,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)
@@ -1434,6 +1458,9 @@
 			  unsigned long extra_jiffies,
 			  int do_acct)
 {
+	struct nf_conn_acct *acct;
+	u64 pkts;
+
 	NF_CT_ASSERT(skb);
 
 	/* Only update if this is not a fixed timeout */
@@ -1446,8 +1473,27 @@
 
 	ct->timeout = extra_jiffies;
 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_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_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 dde64c4..2916f48 100644
--- a/net/netfilter/nf_nat_core.c
+++ b/net/netfilter/nf_nat_core.c
@@ -892,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/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c
index 1cf2874..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,8 +1608,8 @@
 	}
 
 	if (sk) {
-		MT_DEBUG("qtaguid: %p->sk_proto=%u "
-			 "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state);
+		MT_DEBUG("qtaguid[%d]: %pK->sk_proto=%u->sk_state=%d\n",
+			 par->hooknum, sk, sk->sk_protocol, sk->sk_state);
 	}
 	return sk;
 }
@@ -1606,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)
@@ -1646,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;
@@ -1718,19 +1720,13 @@
 	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) {
+	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=NULL)\n", par->hooknum);
 		res = (info->match ^ info->invert) == 0;
@@ -1741,12 +1737,9 @@
 		goto put_sock_ret_res;
 	}
 	sock_uid = sk->sk_uid;
-	/*
-	 * 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);
+	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:
@@ -1758,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(sk->sk_uid, uid_min) &&
-		     uid_lte(sk->sk_uid, 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);
@@ -1773,16 +1766,18 @@
 		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);
+			 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;
+			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);
+			 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)) {
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/packet/af_packet.c b/net/packet/af_packet.c
index 35ba4b6..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);
@@ -2832,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;
 
@@ -2875,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))) {
@@ -2935,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;
@@ -3063,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) {
@@ -3884,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) {
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/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/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_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/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/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/wireless/db.txt b/net/wireless/db.txt
index cad82a4..f323faf 100644
--- a/net/wireless/db.txt
+++ b/net/wireless/db.txt
@@ -737,7 +737,7 @@
 	(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)
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index adf7d03..3dd7b21 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,
@@ -10233,7 +10241,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 +10491,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;
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..47c6dca 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]);
 }
 
 /*
@@ -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;
 }
 
@@ -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/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..135e1eb 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;
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index ada12c3..1302cb3 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -766,6 +766,11 @@
 
 	key = key_ref_to_ptr(key_ref);
 
+	if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
+		ret = -ENOKEY;
+		goto error2;
+	}
+
 	/* see if we can read it directly */
 	ret = key_permission(key_ref, KEY_NEED_READ);
 	if (ret == 0)
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index c91e4e0..a86d0ae 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -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);
@@ -465,16 +465,12 @@
 		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;
+		return nr_keys * sizeof(key_serial_t);
 
 	/* Copy the IDs of the subscribed keys into the buffer */
 	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) {
@@ -989,15 +985,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 +1021,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/process_keys.c b/security/keys/process_keys.c
index 45536c6..ce45c78 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);
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..99ee618 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -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/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/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 67c4c68..c411483 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -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;
 }
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_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/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/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/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/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/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-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/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 6c3d62f..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;
 }
 
@@ -2505,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);
@@ -2938,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 2b4b067..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 */
diff --git a/sound/usb/usb_audio_qmi_svc.c b/sound/usb/usb_audio_qmi_svc.c
index ebc081f..a7cda4a 100644
--- a/sound/usb/usb_audio_qmi_svc.c
+++ b/sound/usb/usb_audio_qmi_svc.c
@@ -80,6 +80,7 @@
 	/* 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;
 	wait_queue_head_t disconnect_wq;
@@ -181,6 +182,8 @@
 		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;
@@ -690,6 +693,7 @@
 	}
 
 	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;
@@ -811,6 +815,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));
diff --git a/sound/usb/usb_audio_qmi_v01.c b/sound/usb/usb_audio_qmi_v01.c
index a93665c..4fa8445 100644
--- a/sound/usb/usb_audio_qmi_v01.c
+++ b/sound/usb/usb_audio_qmi_v01.c
@@ -866,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 9900764..addc0ed 100644
--- a/sound/usb/usb_audio_qmi_v01.h
+++ b/sound/usb/usb_audio_qmi_v01.h
@@ -84,6 +84,7 @@
 	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,
 };
 
@@ -157,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/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/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)
 {